asif-coder commited on
Commit
1776bfe
ยท
verified ยท
1 Parent(s): ac74aa0

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +248 -38
src/streamlit_app.py CHANGED
@@ -1,40 +1,250 @@
1
- import altair as alt
2
- import numpy as np
3
- import pandas as pd
4
  import streamlit as st
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
- """
7
- # Welcome to Streamlit!
8
-
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
-
13
- In the meantime, below is an example of what you can do with just a few lines of code:
14
- """
15
-
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
 
 
 
1
  import streamlit as st
2
+ import pandas as pd
3
+ import numpy as np
4
+ import math
5
+ import matplotlib.pyplot as plt
6
+ import numpy_financial as npf
7
+ from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
8
+ from reportlab.lib.pagesizes import A4
9
+ from reportlab.lib.styles import getSampleStyleSheet
10
+ import io
11
+ import xlsxwriter
12
+
13
+ # ===============================
14
+ # CONFIGURATION
15
+ # ===============================
16
+ SYSTEM_LOSSES = 0.20
17
+ PANEL_COST_PER_WATT = 55
18
+ INSTALLATION_COST_PER_WATT = 35
19
+ LITHIUM_BATTERY_COST_5KWH = 95000
20
+
21
+ CITY_SUNLIGHT = {
22
+ "Karachi": 6.2,
23
+ "Lahore": 5.5,
24
+ "Islamabad": 5.2,
25
+ "Peshawar": 5.6,
26
+ "Quetta": 6.5,
27
+ }
28
+
29
+ APPLIANCES_RESIDENTIAL = {
30
+ "LED Bulb (12W)": 12,
31
+ "Fan (80W)": 80,
32
+ "Refrigerator (200W)": 200,
33
+ "LED TV (150W)": 150,
34
+ "Air Conditioner 1.5 Ton (1500W)": 1500,
35
+ "Washing Machine (500W)": 500,
36
+ "Water Pump (750W)": 750,
37
+ "Laptop (65W)": 65,
38
+ "Iron (1000W)": 1000,
39
+ }
40
+
41
+ APPLIANCES_COMMERCIAL = {
42
+ "CNC Machine (2kW)": 2000,
43
+ "Industrial AC (5kW)": 5000,
44
+ "Lighting System (1kW)": 1000,
45
+ "Water Pump 3HP (2.2kW)": 2200,
46
+ "Server Rack (1.5kW)": 1500,
47
+ }
48
+
49
+ RESIDENTIAL_TARIFF = [
50
+ (100, 22),
51
+ (100, 32),
52
+ (100, 38),
53
+ (100, 42),
54
+ (100, 48),
55
+ (np.inf, 65),
56
+ ]
57
+
58
+ COMMERCIAL_TARIFF = 72 # PKR/unit average
59
+
60
+ # ===============================
61
+ # FUNCTIONS
62
+ # ===============================
63
+
64
+ def calculate_residential_bill(units):
65
+ remaining = units
66
+ bill = 0
67
+ for slab_units, rate in RESIDENTIAL_TARIFF:
68
+ if remaining > slab_units:
69
+ bill += slab_units * rate
70
+ remaining -= slab_units
71
+ else:
72
+ bill += remaining * rate
73
+ break
74
+ return bill
75
+
76
+ def calculate_commercial_bill(units):
77
+ return units * COMMERCIAL_TARIFF
78
+
79
+ def calculate_system(load_watts, hours, sunlight):
80
+ daily_kwh = (load_watts * hours) / 1000
81
+ adjusted_kwh = daily_kwh / (1 - SYSTEM_LOSSES)
82
+ required_kw = adjusted_kwh / sunlight
83
+ return daily_kwh, round(required_kw, 2)
84
+
85
+ def calculate_battery(daily_kwh, backup_hours):
86
+ backup_kwh = (daily_kwh / 24) * backup_hours
87
+ batteries = math.ceil(backup_kwh / 5)
88
+ return batteries
89
+
90
+ def calculate_cost(system_kw, batteries, system_type):
91
+ base_cost = system_kw * 1000 * (PANEL_COST_PER_WATT + INSTALLATION_COST_PER_WATT)
92
+ battery_cost = batteries * LITHIUM_BATTERY_COST_5KWH
93
+ if system_type == "On-Grid":
94
+ return base_cost
95
+ elif system_type == "Off-Grid":
96
+ return base_cost + battery_cost
97
+ else:
98
+ return base_cost * 1.1 + battery_cost
99
+
100
+ def emi_calculator(principal, annual_rate, years):
101
+ r = annual_rate / 100 / 12
102
+ n = years * 12
103
+ emi = principal * r * (1 + r)**n / ((1 + r)**n - 1)
104
+ return round(emi)
105
+
106
+ def financial_projection(total_cost, daily_kwh, mode, years=25, inflation_rate=5, energy_price_increase=7):
107
+ monthly_units = daily_kwh * 30
108
+ cashflows = []
109
+ for year in range(1, years+1):
110
+ if mode == "Homeowner":
111
+ monthly_bill = calculate_residential_bill(monthly_units * ((1 + energy_price_increase/100)**(year-1)))
112
+ else:
113
+ monthly_bill = calculate_commercial_bill(monthly_units * ((1 + energy_price_increase/100)**(year-1)))
114
+ annual_savings = monthly_bill * 12
115
+ cashflows.append(annual_savings)
116
+ npv = npf.npv(inflation_rate/100, [-total_cost]+cashflows)
117
+ irr = npf.irr([-total_cost]+cashflows)
118
+ payback_year = next((i for i, cf in enumerate(np.cumsum(cashflows), 1) if cf >= total_cost), None)
119
+ cumulative_savings = np.cumsum(cashflows)
120
+ return cashflows, round(npv,2), round(irr*100,2), payback_year, cumulative_savings
121
+
122
+ def generate_pdf(report_data):
123
+ file_path = "solar_report.pdf"
124
+ doc = SimpleDocTemplate(file_path, pagesize=A4)
125
+ elements = []
126
+ styles = getSampleStyleSheet()
127
+ elements.append(Paragraph("<b>Pakistan Solar Feasibility Report</b>", styles['Title']))
128
+ elements.append(Spacer(1, 12))
129
+ for key, value in report_data.items():
130
+ elements.append(Paragraph(f"<b>{key}:</b> {value}", styles['Normal']))
131
+ elements.append(Spacer(1, 8))
132
+ doc.build(elements)
133
+ return file_path
134
+
135
+ def generate_excel(report_data):
136
+ output = io.BytesIO()
137
+ workbook = xlsxwriter.Workbook(output)
138
+ worksheet = workbook.add_worksheet("Solar Report")
139
+ bold = workbook.add_format({'bold': True})
140
+ row = 0
141
+ for key, value in report_data.items():
142
+ worksheet.write(row, 0, key, bold)
143
+ worksheet.write(row, 1, str(value))
144
+ row += 1
145
+ workbook.close()
146
+ output.seek(0)
147
+ return output
148
+
149
+ # ===============================
150
+ # STREAMLIT APP
151
+ # ===============================
152
+ st.set_page_config(layout="wide")
153
+ st.title("๐Ÿ‡ต๐Ÿ‡ฐ Pakistan Solar Engineering & Financial Dashboard")
154
+
155
+ audience = st.selectbox("Select Audience", ["Homeowner", "Solar Company", "Industrial Investor"])
156
+ city = st.selectbox("Select City", list(CITY_SUNLIGHT.keys()))
157
+ sunlight = CITY_SUNLIGHT[city]
158
+
159
+ if audience == "Homeowner":
160
+ appliances = st.multiselect("Select Appliances", list(APPLIANCES_RESIDENTIAL.keys()))
161
+ elif audience == "Solar Company":
162
+ appliances = st.multiselect("Select Residential / Commercial Appliances",
163
+ list(APPLIANCES_RESIDENTIAL.keys()) + list(APPLIANCES_COMMERCIAL.keys()))
164
+ else:
165
+ appliances = st.multiselect("Select Industrial Equipment", list(APPLIANCES_COMMERCIAL.keys()))
166
+
167
+ hours = st.slider("Usage Hours per Day", 1, 24, 8)
168
+ system_type = st.radio("System Type", ["On-Grid", "Off-Grid", "Hybrid"])
169
+ backup_hours = st.slider("Battery Backup Hours", 0, 24, 4)
170
+
171
+ if st.button("Calculate Solar System"):
172
+ if not appliances:
173
+ st.error("Please select at least one appliance or equipment")
174
+ else:
175
+ total_load = sum(APPLIANCES_RESIDENTIAL.get(a,0) + APPLIANCES_COMMERCIAL.get(a,0) for a in appliances)
176
+ daily_kwh, system_kw = calculate_system(total_load, hours, sunlight)
177
+ batteries = calculate_battery(daily_kwh, backup_hours)
178
+ total_cost = calculate_cost(system_kw, batteries, system_type)
179
+
180
+ interest = st.slider("Bank Interest Rate (%)", 5, 25, 15)
181
+ years_loan = st.slider("Loan Duration (Years)", 1, 10, 5)
182
+ emi = emi_calculator(total_cost, interest, years_loan)
183
+
184
+ cashflows, npv, irr, payback_year, cumulative_savings = financial_projection(total_cost, daily_kwh, audience)
185
+
186
+ # Display Results
187
+ st.subheader("System Analysis")
188
+ st.write(f"Total Load: {total_load} W")
189
+ st.write(f"Daily Energy Consumption: {round(daily_kwh,2)} kWh")
190
+ st.write(f"Required System Size: {system_kw} kW")
191
+ st.write(f"Battery Units Required (5kWh each): {batteries}")
192
+ st.write(f"Estimated System Cost: PKR {round(total_cost):,}")
193
+ st.write(f"EMI (Monthly): PKR {emi:,}")
194
+ st.write(f"25-Year Projection: NPV = PKR {npv:,}, IRR = {irr}%, Payback Year = {payback_year}")
195
+
196
+ # Dashboard
197
+ st.subheader("๐Ÿ”น Daily Load vs Solar Generation")
198
+ hours_day = np.arange(0,24,1)
199
+ load_profile = np.array([total_load]*24)
200
+ solar_profile = np.array([system_kw*1000/sunlight]*24)
201
+ plt.figure(figsize=(10,4))
202
+ plt.plot(hours_day, load_profile, label="Load (W)")
203
+ plt.plot(hours_day, solar_profile, label="Solar Generation (W)")
204
+ plt.xlabel("Hour of Day")
205
+ plt.ylabel("Power (W)")
206
+ plt.title("Daily Load vs Solar Generation")
207
+ plt.legend()
208
+ st.pyplot(plt)
209
+
210
+ st.subheader("๐Ÿ”น Cumulative Savings Over 25 Years")
211
+ plt.figure(figsize=(10,4))
212
+ plt.plot(range(1,26), cumulative_savings, marker='o')
213
+ plt.axhline(total_cost, color='r', linestyle='--', label="Total System Cost")
214
+ plt.xlabel("Year")
215
+ plt.ylabel("Cumulative Savings (PKR)")
216
+ plt.title("Payback & Savings Curve")
217
+ plt.legend()
218
+ st.pyplot(plt)
219
+
220
+ st.subheader("๐Ÿ”น Yearly Cashflows")
221
+ df_cashflow = pd.DataFrame({"Year": range(1,26), "Annual Savings (PKR)": cashflows})
222
+ st.dataframe(df_cashflow)
223
+
224
+ st.subheader("๐Ÿ”น Carbon Emission Reduction Estimate")
225
+ co2_per_kwh = 0.85
226
+ total_co2_saved = round(daily_kwh * 365 * 25 * co2_per_kwh)
227
+ st.write(f"Estimated CO2 Reduction over 25 years: {total_co2_saved:,} kg (~{total_co2_saved/1000:,} tons)")
228
+
229
+ # PDF & Excel
230
+ report_data = {
231
+ "Audience": audience,
232
+ "City": city,
233
+ "System Type": system_type,
234
+ "Total Load (W)": total_load,
235
+ "Daily Energy (kWh)": round(daily_kwh,2),
236
+ "System Size (kW)": system_kw,
237
+ "Battery Units": batteries,
238
+ "Total Cost (PKR)": round(total_cost),
239
+ "EMI (PKR)": emi,
240
+ "25-Year NPV (PKR)": npv,
241
+ "IRR (%)": irr,
242
+ "Payback Year": payback_year
243
+ }
244
+
245
+ pdf_file = generate_pdf(report_data)
246
+ excel_file = generate_excel(report_data)
247
 
248
+ with open(pdf_file, "rb") as f:
249
+ st.download_button("Download PDF Report", f, file_name="Solar_Report_Pakistan.pdf")
250
+ st.download_button("Download Excel Report", data=excel_file, file_name="Solar_Report_Pakistan.xlsx")