charagu-eric commited on
Commit
c43a444
·
1 Parent(s): d4e79d5
Files changed (2) hide show
  1. README.md +3 -3
  2. app.py +560 -158
README.md CHANGED
@@ -3,10 +3,10 @@ title: Solar Savings
3
  emoji: ⚡
4
  colorFrom: yellow
5
  colorTo: red
6
- sdk: gradio
7
- sdk_version: 5.29.0
8
  app_file: app.py
9
  pinned: false
10
  ---
11
 
12
- Check out the configuration reference at <https://huggingface.co/docs/hub/spaces-config-reference>
 
3
  emoji: ⚡
4
  colorFrom: yellow
5
  colorTo: red
6
+ sdk: streamlit
7
+ sdk_version: 1.44.1
8
  app_file: app.py
9
  pinned: false
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py CHANGED
@@ -1,49 +1,88 @@
1
- import gradio as gr
 
2
  import pandas as pd
3
  import matplotlib.pyplot as plt
4
  import seaborn as sns
 
5
  from typing import Dict
 
6
 
7
- # Constants (replace with your actual values)
8
- FEED_IN_TARIFF = 12.0 # Example value in cents/kWh
9
- SOLAR_PANEL_RATING = 625 # Watts
10
  ONE_BR_UNITS = 23
11
  TWO_BR_UNITS = 45
12
-
13
-
14
- def initialize_state():
15
- return {
16
- "solar_panels": 20,
17
- "batteries": 5,
18
- "panel_price": 25000,
19
- "battery_price": 50000,
20
- "grid_price": 24.0,
 
 
 
 
 
 
 
 
 
 
 
21
  }
 
 
 
22
 
23
 
24
- state = initialize_state()
25
-
26
-
27
- def battery_storage(num_batteries):
28
- return num_batteries * 2.4 # kWh per battery
 
29
 
30
 
31
- def solar_production(num_panels):
32
- return num_panels * SOLAR_PANEL_RATING / 1000 * 5 # 5 sun hours per day
33
 
34
 
35
- def calculate_lighting_consumption(occupancy_1br, occupancy_2br):
36
- return (occupancy_1br * ONE_BR_UNITS * 0.5) + (occupancy_2br * TWO_BR_UNITS * 0.8)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
 
38
 
39
- def calculate_appliance_consumption(occupancy_1br, occupancy_2br):
40
- return (occupancy_1br * ONE_BR_UNITS * 2.0) + (occupancy_2br * TWO_BR_UNITS * 3.0)
 
 
 
 
41
 
42
 
43
- def total_consumption(occupancy_1br, occupancy_2br, common_area):
44
- lighting = calculate_lighting_consumption(occupancy_1br, occupancy_2br)
45
- appliances = calculate_appliance_consumption(occupancy_1br, occupancy_2br)
46
- return lighting + appliances + common_area
47
 
48
 
49
  def financial_analysis(
@@ -63,12 +102,12 @@ def financial_analysis(
63
  # Battery can offset some grid purchases
64
  grid_offset = min(grid_purchased, storage)
65
  grid_purchased -= grid_offset
66
-
67
- monthly_savings = consumption * state["grid_price"] / 100
68
 
69
  total_investment = (
70
- state["solar_panels"] * state["panel_price"]
71
- + state["batteries"] * state["battery_price"]
72
  )
73
 
74
  # Avoid division by zero
@@ -101,154 +140,517 @@ def create_consumption_breakdown(
101
  return pd.DataFrame.from_dict(breakdown, orient="index", columns=["kWh"])
102
 
103
 
104
- def update_state(solar_panels, batteries, panel_price, battery_price, grid_price):
105
- state["solar_panels"] = solar_panels
106
- state["batteries"] = batteries
107
- state["panel_price"] = panel_price
108
- state["battery_price"] = battery_price
109
- state["grid_price"] = grid_price
110
- return state
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
 
 
 
 
 
 
 
 
 
 
112
 
113
- def analyze_scenario(occupancy_1br, occupancy_2br, common_area):
114
- consumption = total_consumption(occupancy_1br, occupancy_2br, common_area)
115
- production = solar_production(state["solar_panels"])
116
- storage = battery_storage(state["batteries"])
117
 
118
- financials = financial_analysis(consumption, common_area, production, storage)
119
- breakdown_df = create_consumption_breakdown(
120
- occupancy_1br, occupancy_2br, common_area
121
- )
122
 
123
- # Create plots
124
- fig1, ax1 = plt.subplots(figsize=(10, 5))
125
- energy_sources = ["Solar", "Grid"]
126
- values = [financials["solar_contribution"], financials["grid_dependency"]]
127
- ax1.bar(energy_sources, values, color=["#4CAF50", "#F44336"])
128
- ax1.set_ylabel("Percentage (%)")
129
- ax1.set_title("Energy Contribution")
130
-
131
- fig2, ax2 = plt.subplots(figsize=(8, 8))
132
- breakdown_df["Percentage"] = (
133
- breakdown_df["kWh"] / breakdown_df["kWh"].sum() * 100
134
- ).round(1)
135
- breakdown_df.plot.pie(
136
- y="kWh",
137
- labels=breakdown_df.index,
138
- autopct="%1.1f%%",
139
- colors=["#FF9800", "#2196F3", "#4CAF50"],
140
- ax=ax2,
141
- )
142
- ax2.set_ylabel("")
143
 
144
- return (
145
- f"Total Consumption: {consumption:.1f} kWh/day\n"
146
- f"Solar Production: {production:.1f} kWh/day\n"
147
- f"Solar Contribution: {financials['solar_contribution']:.1f}%\n"
148
- f"Monthly Savings: {financials['monthly_savings']:,.0f} Ksh\n"
149
- f"Payback Period: {financials['payback_period']:.1f} years",
150
- fig1,
151
- fig2,
152
- breakdown_df,
153
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
 
156
- with gr.Blocks(title="Solar Analysis Suite", theme=gr.themes.Soft()) as demo:
157
- gr.Markdown("# 🌞 Advanced Solar Performance Analyzer")
158
- gr.Markdown(
159
- "Optimize your apartment complex solar installation with data-driven insights"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
  )
161
 
162
- with gr.Row():
163
- with gr.Column(scale=1):
164
- gr.Markdown("## System Configuration")
 
 
 
 
 
 
 
165
 
166
- with gr.Tab("Hardware"):
167
- solar_panels = gr.Slider(
168
- 1, 300, value=20, step=5, label="Number of Solar Panels"
169
- )
170
- batteries = gr.Slider(
171
- 0, 150, value=5, step=5, label="Number of Batteries"
172
- )
173
 
174
- with gr.Tab("Pricing"):
175
- panel_price = gr.Slider(
176
- 1000, 50000, value=25000, step=500, label="Panel Price (Ksh)"
177
- )
178
- battery_price = gr.Slider(
179
- 5000, 100000, value=50000, step=1000, label="Battery Price (Ksh)"
180
- )
181
- grid_price = gr.Slider(
182
- 10.0, 50.0, value=24.0, step=0.1, label="Grid Price (Ksh/kWh)"
183
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
 
185
- update_btn = gr.Button("Update System Configuration")
 
 
 
186
 
187
- gr.Markdown("### System Totals")
188
- total_panel = gr.Textbox(
189
- label="Total Panel Capacity (kW)", interactive=False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
  )
191
- total_battery = gr.Textbox(
192
- label="Total Battery Storage (kWh)", interactive=False
 
 
 
 
 
 
 
193
  )
194
- total_investment = gr.Textbox(
195
- label="Total Investment (Ksh)", interactive=False
 
 
196
  )
197
 
198
- with gr.Column(scale=2):
199
- gr.Markdown("## Scenario Analysis")
 
 
 
 
 
 
 
 
 
 
 
 
200
 
201
- with gr.Tab("Input Parameters"):
202
- occupancy_1br = gr.Slider(
203
- 0.0, 1.0, value=0.5, step=0.25, label="1BR Occupancy Rate"
204
- )
205
- occupancy_2br = gr.Slider(
206
- 0.0, 1.0, value=0.5, step=0.25, label="2BR Occupancy Rate"
207
- )
208
- common_area = gr.Number(
209
- value=23.544, label="Common Area Consumption (kWh/day)"
210
- )
211
- analyze_btn = gr.Button("Analyze Scenario")
212
-
213
- with gr.Tab("Results"):
214
- results_text = gr.Textbox(label="Analysis Results", lines=5)
215
- energy_plot = gr.Plot(label="Energy Contribution")
216
- breakdown_plot = gr.Plot(label="Consumption Breakdown")
217
- breakdown_table = gr.Dataframe(label="Detailed Breakdown")
218
-
219
- # Update system totals when configuration changes
220
- def update_totals(solar_panels, batteries, panel_price, battery_price):
221
- panel_kw = solar_panels * SOLAR_PANEL_RATING / 1000
222
- battery_kwh = battery_storage(batteries)
223
- investment = solar_panels * panel_price + batteries * battery_price
224
- return (
225
- f"{panel_kw:.1f} kW",
226
- f"{battery_kwh:.1f} kWh",
227
- f"{investment:,.0f} Ksh",
228
  )
229
 
230
- update_btn.click(
231
- update_state,
232
- inputs=[solar_panels, batteries, panel_price, battery_price, grid_price],
233
- outputs=None,
234
- ).then(
235
- update_totals,
236
- inputs=[solar_panels, batteries, panel_price, battery_price],
237
- outputs=[total_panel, total_battery, total_investment],
238
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
239
 
240
- analyze_btn.click(
241
- analyze_scenario,
242
- inputs=[occupancy_1br, occupancy_2br, common_area],
243
- outputs=[results_text, energy_plot, breakdown_plot, breakdown_table],
244
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245
 
246
- # Initialize with default values
247
- demo.load(
248
- update_totals,
249
- inputs=[solar_panels, batteries, panel_price, battery_price],
250
- outputs=[total_panel, total_battery, total_investment],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
251
  )
252
 
 
253
  if __name__ == "__main__":
254
- demo.launch()
 
1
+ # Constants
2
+ import numpy as np
3
  import pandas as pd
4
  import matplotlib.pyplot as plt
5
  import seaborn as sns
6
+ import streamlit as st
7
  from typing import Dict
8
+ from utils.llm import summary_generation
9
 
 
 
 
10
  ONE_BR_UNITS = 23
11
  TWO_BR_UNITS = 45
12
+ SOLAR_PANEL_RATING = 625 # W
13
+ BATTERY_CAPACITY = 200 # Ah
14
+ BATTERY_VOLTAGE = 96 # V
15
+ SYSTEM_LOSSES = 0.20
16
+ FEED_IN_TARIFF = 12
17
+
18
+ # Lighting specifications
19
+ LIGHTS_1BR = 5
20
+ LIGHTS_2BR = 12
21
+ LIGHT_POWER = 6 # Watts per light
22
+
23
+
24
+ def initialize_session_state():
25
+ """Initialize session state variables"""
26
+ defaults = {
27
+ "solar_panels": 25,
28
+ "batteries": 10,
29
+ "panel_price": 13000,
30
+ "battery_price": 39000,
31
+ "grid_price": 28.44,
32
  }
33
+ for key, value in defaults.items():
34
+ if key not in st.session_state:
35
+ st.session_state[key] = value
36
 
37
 
38
+ def calculate_lighting_consumption(occupancy_1br: float, occupancy_2br: float) -> float:
39
+ """Calculate daily lighting consumption"""
40
+ return (
41
+ (occupancy_1br * ONE_BR_UNITS * LIGHTS_1BR * LIGHT_POWER / 1000)
42
+ + (occupancy_2br * TWO_BR_UNITS * LIGHTS_2BR * LIGHT_POWER / 1000)
43
+ ) * 6 # 6 hours per day
44
 
45
 
46
+ # assuming that 1br average usage is 250wh and for a 2br is 400wh
 
47
 
48
 
49
+ def calculate_appliance_consumption(
50
+ occupancy_1br: float, occupancy_2br: float
51
+ ) -> float:
52
+ """Calculate daily appliance consumption by subtracting the lighting usage from the average total consumption for each house type"""
53
+ return (
54
+ occupancy_1br
55
+ * ONE_BR_UNITS
56
+ * (3 - (LIGHTS_1BR * LIGHT_POWER * 6 / 1000))
57
+ # + (500 * 24) # Fridge
58
+ ) + (
59
+ occupancy_2br
60
+ * TWO_BR_UNITS
61
+ * (4 - (LIGHTS_2BR * LIGHT_POWER * 6 / 1000))
62
+ # + (500 * 24) # Fridge
63
+ ) # Daily kWh
64
+
65
+
66
+ def total_consumption(
67
+ occupancy_1br: float, occupancy_2br: float, common_area: float
68
+ ) -> float:
69
+ """Calculate total monthly consumption"""
70
+ lighting = calculate_lighting_consumption(occupancy_1br, occupancy_2br)
71
+ appliances = calculate_appliance_consumption(occupancy_1br, occupancy_2br)
72
+ return (lighting + appliances + common_area) * 30 # Monthly kWh
73
 
74
 
75
+ def solar_production(panels: int) -> float:
76
+ """Monthly solar production with losses"""
77
+ daily_production = (
78
+ panels * SOLAR_PANEL_RATING * 6.5 * (1 - SYSTEM_LOSSES) / 1000
79
+ ) # 6.5 sun hours
80
+ return daily_production * 30 # Monthly kWh
81
 
82
 
83
+ def battery_storage(batteries: int) -> float:
84
+ """Usable battery capacity"""
85
+ return batteries * BATTERY_CAPACITY * BATTERY_VOLTAGE * 0.8 / 1000 # kWh
 
86
 
87
 
88
  def financial_analysis(
 
102
  # Battery can offset some grid purchases
103
  grid_offset = min(grid_purchased, storage)
104
  grid_purchased -= grid_offset
105
+ # Money paid to owner if client used this instead o
106
+ monthly_savings = consumption * st.session_state.grid_price / 100
107
 
108
  total_investment = (
109
+ st.session_state.solar_panels * st.session_state.panel_price
110
+ + st.session_state.batteries * st.session_state.battery_price
111
  )
112
 
113
  # Avoid division by zero
 
140
  return pd.DataFrame.from_dict(breakdown, orient="index", columns=["kWh"])
141
 
142
 
143
+ # Streamlit Interface
144
+ def main():
145
+ st.set_page_config(page_title="Solar Analysis Suite", page_icon="🌞", layout="wide")
146
+ initialize_session_state()
147
+
148
+ # Custom CSS
149
+ st.markdown(
150
+ """
151
+ <style>
152
+ .main .block-container {padding-top: 2rem;}
153
+ h1, h2, h3 {color: #1E88E5;}
154
+ .stExpander {border-radius: 8px; border: 1px solid #1E88E5;}
155
+ .stTabs [data-baseweb="tab-list"] {gap: 10px;}
156
+ .stTabs [data-baseweb="tab"] {
157
+ height: 50px;
158
+ white-space: pre-wrap;
159
+ background-color: #F0F2F6;
160
+ border-radius: 4px 4px 0px 0px;
161
+ gap: 1px;
162
+ padding-top: 10px;
163
+ padding-bottom: 10px;
164
+ }
165
+ .stTabs [aria-selected="true"] {
166
+ background-color: #1E88E5;
167
+ color: white;
168
+ }
169
+ </style>
170
+ """,
171
+ unsafe_allow_html=True,
172
+ )
173
 
174
+ # Header with logo
175
+ col1, col2 = st.columns([1, 4])
176
+ with col1:
177
+ st.image("https://img.icons8.com/fluency/96/000000/sun.png", width=100)
178
+ with col2:
179
+ st.title("🌞 Advanced Solar Performance Analyzer")
180
+ st.markdown(
181
+ "Optimize your apartment complex solar installation with data-driven insights"
182
+ )
183
 
184
+ # Sidebar for system configuration
185
+ with st.sidebar:
186
+ st.header("System Configuration")
 
187
 
188
+ # Add a nice header image
189
+ st.image("https://img.icons8.com/color/96/000000/solar-panel.png", width=80)
 
 
190
 
191
+ # Create tabs for different settings
192
+ tab1, tab2 = st.tabs(["Hardware", "Pricing"])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
 
194
+ with tab1:
195
+ st.number_input(
196
+ "Number of Solar Panels",
197
+ 1,
198
+ 300,
199
+ step=5,
200
+ key="solar_panels",
201
+ help="Each panel rated at 625W",
202
+ )
203
+ st.number_input(
204
+ "Number of Batteries",
205
+ 0,
206
+ 150,
207
+ step=5,
208
+ key="batteries",
209
+ help="Each battery has 200Ah capacity at 12V",
210
+ )
211
+
212
+ with tab2:
213
+ st.number_input(
214
+ "Panel Price (Ksh)",
215
+ 1000,
216
+ 50000,
217
+ step=500,
218
+ key="panel_price",
219
+ help="Cost per solar panel",
220
+ )
221
+ st.number_input(
222
+ "Battery Price (Ksh)",
223
+ 5000,
224
+ 100000,
225
+ step=1000,
226
+ key="battery_price",
227
+ help="Cost per battery unit",
228
+ )
229
+ st.number_input(
230
+ "Grid Price (Ksh/kWh)",
231
+ 10.0,
232
+ 50.0,
233
+ step=0.1,
234
+ key="grid_price",
235
+ help="Current electricity price from the grid",
236
+ )
237
 
238
+ st.markdown("---")
239
+ st.markdown(
240
+ """
241
+ 📊 **System Totals**
242
+ - **Total Panel Capacity**: {0:.1f} kW
243
+ - **Total Battery Storage**: {1:.1f} kWh
244
+ - **Total Investment**: ksh. {2:,.0f}
245
+ """.format(
246
+ st.session_state.solar_panels * SOLAR_PANEL_RATING / 1000,
247
+ battery_storage(st.session_state.batteries),
248
+ st.session_state.solar_panels * st.session_state.panel_price
249
+ + st.session_state.batteries * st.session_state.battery_price,
250
+ )
251
+ )
252
 
253
+ # Main content
254
+ # Create scenarios with varying occupancy levels
255
+ scenarios = {}
256
+
257
+ # Common area consumption remains constant
258
+ common_area_consumption = 23.544 # kWh per day
259
+
260
+ # Generate scenarios with different occupancy combinations
261
+ occupancy_levels = [0.0, 0.25, 0.50, 0.75, 1.0]
262
+
263
+ # Create scenarios for 1BR fixed, varying 2BR
264
+ for br1_level in occupancy_levels:
265
+ for br2_level in occupancy_levels:
266
+ scenario_name = f"1BR: {int(br1_level*100)}%, 2BR: {int(br2_level*100)}%"
267
+ scenarios[scenario_name] = {
268
+ "1br": br1_level,
269
+ "2br": br2_level,
270
+ "common": common_area_consumption,
271
+ }
272
+
273
+ # Analysis tabs
274
+ st.markdown("---")
275
+ tab1, tab2, tab3 = st.tabs(
276
+ ["📊 Energy Analysis", "💰 Financial Metrics", "🔍 Detailed Breakdown"]
277
  )
278
 
279
+ # Prepare analysis data for all scenarios
280
+ analysis_data = []
281
+ for name, params in scenarios.items():
282
+ consumption = total_consumption(params["1br"], params["2br"], params["common"])
283
+ production = solar_production(st.session_state.solar_panels)
284
+ storage = battery_storage(st.session_state.batteries)
285
+ financials = financial_analysis(
286
+ consumption, common_area_consumption, production, storage
287
+ )
288
+ analysis_data.append({"Scenario": name, **financials})
289
 
290
+ df = pd.DataFrame(analysis_data)
 
 
 
 
 
 
291
 
292
+ # Tab 1: Energy Analysis
293
+ with tab1:
294
+ st.header("Energy Flow Analysis")
295
+
296
+ # Allow filtering by 1BR occupancy
297
+ one_br_filter = st.selectbox(
298
+ "Filter by 1BR Occupancy",
299
+ ["All"] + [f"{int(level*100)}%" for level in occupancy_levels],
300
+ help="Filter scenarios by 1BR occupancy level",
301
+ )
302
+
303
+ # Filter the dataframe based on selection
304
+ filtered_df = df
305
+ if one_br_filter != "All":
306
+ occupancy_value = int(one_br_filter.replace("%", ""))
307
+ filtered_df = df[df["Scenario"].str.contains(f"1BR: {occupancy_value}%")]
308
+
309
+ # Chart 1: Energy Balance
310
+ st.subheader("Energy Balance by Scenario")
311
+
312
+ energy_fig = plt.figure(figsize=(12, 7))
313
+ ax = energy_fig.add_subplot(111)
314
+
315
+ # Create data for stacked bar chart
316
+ chart_data = filtered_df.copy()
317
+ chart_data["grid_energy"] = chart_data["grid_purchased"]
318
+ chart_data["solar_energy"] = (
319
+ chart_data["consumption"] - chart_data["grid_purchased"]
320
+ )
321
+
322
+ # Create normalized stacked bar chart
323
+ chart_data = chart_data.set_index("Scenario")
324
+ energy_proportions = (
325
+ chart_data[["solar_energy", "grid_energy"]].div(
326
+ chart_data["consumption"], axis=0
327
+ )
328
+ * 100
329
+ )
330
+ energy_proportions = energy_proportions.reset_index()
331
+
332
+ # Reshape for seaborn
333
+ energy_melt = pd.melt(
334
+ energy_proportions,
335
+ id_vars=["Scenario"],
336
+ value_vars=["solar_energy", "grid_energy"],
337
+ var_name="Energy Source",
338
+ value_name="Percentage",
339
+ )
340
 
341
+ # Rename for better labels
342
+ energy_melt["Energy Source"] = energy_melt["Energy Source"].replace(
343
+ {"solar_energy": "Solar Generated", "grid_energy": "Grid Purchased"}
344
+ )
345
 
346
+ # Plot with seaborn
347
+ sns.set_theme(style="whitegrid")
348
+ sns.barplot(
349
+ data=energy_melt,
350
+ x="Scenario",
351
+ y="Percentage",
352
+ hue="Energy Source",
353
+ palette=["#4CAF50", "#F44336"],
354
+ ax=ax,
355
+ )
356
+ ax.set_ylabel("Energy Contribution (%)")
357
+ ax.set_title("Energy Source Distribution by Occupancy Scenario")
358
+ plt.xticks(rotation=45, ha="right")
359
+ plt.tight_layout()
360
+ st.pyplot(energy_fig)
361
+
362
+ # Detailed metrics
363
+ col1, col2, col3 = st.columns(3)
364
+ with col1:
365
+ st.metric(
366
+ "Avg. Solar Contribution",
367
+ f"{filtered_df['solar_contribution'].mean():.1f}%",
368
+ (
369
+ f"{filtered_df['solar_contribution'].mean() - 50:.1f}%"
370
+ if filtered_df["solar_contribution"].mean() > 50
371
+ else f"{filtered_df['solar_contribution'].mean() - 50:.1f}%"
372
+ ),
373
  )
374
+ with col2:
375
+ st.metric(
376
+ "Avg. Grid Dependency",
377
+ f"{filtered_df['grid_dependency'].mean():.1f}%",
378
+ (
379
+ f"{50 - filtered_df['grid_dependency'].mean():.1f}%"
380
+ if filtered_df["grid_dependency"].mean() < 50
381
+ else f"{50 - filtered_df['grid_dependency'].mean():.1f}%"
382
+ ),
383
  )
384
+ with col3:
385
+ st.metric(
386
+ "Production/Consumption Ratio",
387
+ f"{(filtered_df['production'].mean() / filtered_df['consumption'].mean() * 100):.1f}%",
388
  )
389
 
390
+ with st.expander("🔍 Energy Flow Interpretation"):
391
+ st.markdown(
392
+ """
393
+ **Understanding the Chart:**
394
+ - **Solar Contribution**: Percentage of total energy needs met directly by solar production
395
+ - **Grid Dependency**: Remaining energy required from the grid
396
+ - The ideal scenario shows high solar contribution with minimal grid dependency
397
+
398
+ **Key Factors Affecting Energy Balance:**
399
+ 1. **Occupancy Levels**: Higher occupancy means higher consumption, which may exceed solar capacity
400
+ 2. **Solar System Size**: More panels increase production and reduce grid dependency
401
+ 3. **Battery Storage**: Helps utilize excess daytime production for nighttime use
402
+ """
403
+ )
404
 
405
+ # Tab 2: Financial Metrics
406
+ with tab2:
407
+ st.header("Financial Performance Analysis")
408
+
409
+ # Allow filtering by 2BR occupancy
410
+ two_br_filter = st.selectbox(
411
+ "Filter by 2BR Occupancy",
412
+ ["All"] + [f"{int(level*100)}%" for level in occupancy_levels],
413
+ help="Filter scenarios by 2BR occupancy level",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
414
  )
415
 
416
+ # Filter the dataframe based on selection
417
+ filtered_fin_df = df
418
+ if two_br_filter != "All":
419
+ occupancy_value = int(two_br_filter.replace("%", ""))
420
+ filtered_fin_df = df[
421
+ df["Scenario"].str.contains(f"2BR: {occupancy_value}%")
422
+ ]
423
+
424
+ # Monthly Savings Chart
425
+ st.subheader("Monthly Cost Savings")
426
+
427
+ # Fix large values
428
+ filtered_fin_df["monthly_savings_fixed"] = filtered_fin_df[
429
+ "monthly_savings"
430
+ ].clip(0, 100000)
431
+
432
+ fig1, ax1 = plt.subplots(figsize=(12, 6))
433
+ sns.barplot(
434
+ data=filtered_fin_df,
435
+ x="Scenario",
436
+ y="monthly_savings_fixed",
437
+ palette="viridis",
438
+ ax=ax1,
439
+ )
440
+ ax1.set_title("Monthly Cost Savings by Scenario")
441
+ ax1.set_ylabel("Ksh")
442
+ plt.xticks(rotation=45, ha="right")
443
+ plt.tight_layout()
444
+ st.pyplot(fig1)
445
+
446
+ # Payback Period Chart
447
+ st.subheader("System Payback Period")
448
+
449
+ # Fix large values
450
+ filtered_fin_df["payback_period_fixed"] = filtered_fin_df[
451
+ "payback_period"
452
+ ].clip(0, 30)
453
+
454
+ fig2, ax2 = plt.subplots(figsize=(12, 6))
455
+ sns.barplot(
456
+ data=filtered_fin_df,
457
+ x="Scenario",
458
+ y="payback_period_fixed",
459
+ palette="rocket_r",
460
+ ax=ax2,
461
+ )
462
+ ax2.set_title("Investment Payback Period by Scenario")
463
+ ax2.set_ylabel("Years")
464
+ plt.xticks(rotation=45, ha="right")
465
+ plt.tight_layout()
466
+ st.pyplot(fig2)
467
+
468
+ # Financial summary metrics
469
+ col1, col2, col3 = st.columns(3)
470
+ with col1:
471
+ avg_savings = filtered_fin_df["monthly_savings"].mean()
472
+ st.metric(
473
+ "Avg. Monthly Savings",
474
+ f"{avg_savings:,.0f} Ksh",
475
+ (
476
+ f"{avg_savings - df['monthly_savings'].mean():,.0f} Ksh"
477
+ if avg_savings > df["monthly_savings"].mean()
478
+ else f"{avg_savings - df['monthly_savings'].mean():,.0f} Ksh"
479
+ ),
480
+ )
481
+ with col2:
482
+ min_payback = filtered_fin_df["payback_period"].min()
483
+ st.metric(
484
+ "Best Payback Period",
485
+ f"{min_payback:.1f} years",
486
+ help="Shortest time to recover investment",
487
+ )
488
+ with col3:
489
+ total_investment = (
490
+ st.session_state.solar_panels * st.session_state.panel_price
491
+ + st.session_state.batteries * st.session_state.battery_price
492
+ )
493
+ annual_roi = (
494
+ (avg_savings * 12 / total_investment) * 100
495
+ if total_investment > 0
496
+ else 0
497
+ )
498
+ st.metric(
499
+ "Annual ROI", f"{annual_roi:.1f}%", help="Annual Return on Investment"
500
+ )
501
 
502
+ with st.expander("💵 Financial Analysis Details"):
503
+ st.markdown(
504
+ f"""
505
+ **Investment Details:**
506
+ - Total Solar Panel Investment: {st.session_state.solar_panels:,} panels × {st.session_state.panel_price:,} Ksh = {st.session_state.solar_panels * st.session_state.panel_price:,} Ksh
507
+ - Total Battery Investment: {st.session_state.batteries:,} batteries × {st.session_state.battery_price:,} Ksh = {st.session_state.batteries * st.session_state.battery_price:,} Ksh
508
+ - Total System Cost: {total_investment:,} Ksh
509
+
510
+ **Savings Calculation:**
511
+ - Grid Price: {st.session_state.grid_price} Ksh/kWh
512
+ - Monthly Savings =(Total Consumption - Common Area) × Grid Price
513
+ - Payback Period = Total Investment / Annual Savings
514
+
515
+ **Filtered Scenario Data:**
516
+ """
517
+ )
518
+ st.dataframe(
519
+ filtered_fin_df[
520
+ [
521
+ "Scenario",
522
+ "consumption",
523
+ "production",
524
+ "monthly_savings",
525
+ "payback_period",
526
+ ]
527
+ ].sort_values("monthly_savings", ascending=False),
528
+ hide_index=True,
529
+ )
530
+ # Button to trigger analysis
531
+ if st.button("🔍 Analyze Financial Data with LLM"):
532
+ with st.spinner("Generating insights with AI..."):
533
+ analysis = summary_generation(filtered_fin_df)
534
+ st.success("Analysis Complete!")
535
+ st.write(analysis) # Display the results
536
+
537
+ # Tab 3: Detailed Breakdown
538
+ with tab3:
539
+ st.header("Consumption Breakdown Analysis")
540
+
541
+ # Select specific scenario for detailed analysis
542
+ scenario_select = st.selectbox(
543
+ "Select Specific Scenario", list(scenarios.keys())
544
+ )
545
+ selected_params = scenarios[scenario_select]
546
+
547
+ # Create consumption breakdown
548
+ breakdown_df = create_consumption_breakdown(
549
+ selected_params["1br"], selected_params["2br"], selected_params["common"]
550
+ )
551
+
552
+ total_kwh = breakdown_df["kWh"].sum()
553
 
554
+ # Add percentage column
555
+ breakdown_df["Percentage"] = (breakdown_df["kWh"] / total_kwh * 100).round(1)
556
+
557
+ col1, col2 = st.columns([2, 3])
558
+
559
+ with col1:
560
+ st.subheader("Energy Composition")
561
+
562
+ # Create a more attractive pie chart
563
+ fig3 = plt.figure(figsize=(8, 8))
564
+ ax3 = fig3.add_subplot(111)
565
+
566
+ colors = ["#FF9800", "#2196F3", "#4CAF50"]
567
+ explode = (0.1, 0, 0)
568
+
569
+ wedges, texts, autotexts = ax3.pie(
570
+ breakdown_df["kWh"],
571
+ labels=breakdown_df.index,
572
+ autopct="%1.1f%%",
573
+ explode=explode,
574
+ colors=colors,
575
+ shadow=True,
576
+ startangle=90,
577
+ textprops={"fontsize": 12},
578
+ )
579
+
580
+ # Equal aspect ratio ensures that pie is drawn as a circle
581
+ ax3.axis("equal")
582
+ plt.tight_layout()
583
+ st.pyplot(fig3)
584
+
585
+ # Show total consumption
586
+ st.metric(
587
+ "Total Monthly Consumption",
588
+ f"{total_kwh:.1f} kWh",
589
+ help="Sum of all consumption components",
590
+ )
591
+
592
+ with col2:
593
+ st.subheader("Detailed Component Analysis")
594
+
595
+ # Show breakdown as a horizontal bar chart
596
+ fig4 = plt.figure(figsize=(10, 5))
597
+ ax4 = fig4.add_subplot(111)
598
+
599
+ # Sort by consumption
600
+ sorted_df = breakdown_df.sort_values("kWh", ascending=True)
601
+
602
+ # Create horizontal bar chart
603
+ bars = sns.barplot(
604
+ y=sorted_df.index, x="kWh", data=sorted_df, palette=colors[::-1], ax=ax4
605
+ )
606
+
607
+ # Add data labels
608
+ for i, v in enumerate(sorted_df["kWh"]):
609
+ ax4.text(
610
+ v + 5,
611
+ i,
612
+ f"{v:.1f} kWh ({sorted_df['Percentage'].iloc[i]}%)",
613
+ va="center",
614
+ )
615
+
616
+ ax4.set_title(f"Energy Consumption Breakdown - {scenario_select}")
617
+ ax4.set_xlabel("Monthly Consumption (kWh)")
618
+ ax4.set_ylabel("")
619
+ plt.tight_layout()
620
+ st.pyplot(fig4)
621
+
622
+ # Add scenario details
623
+ st.markdown(
624
+ f"""
625
+ **Scenario Details:**
626
+ - 1BR Units Occupancy: {selected_params['1br']*100:.0f}% ({selected_params['1br']*ONE_BR_UNITS:.0f} units)
627
+ - 2BR Units Occupancy: {selected_params['2br']*100:.0f}% ({selected_params['2br']*TWO_BR_UNITS:.0f} units)
628
+ - Common Areas Consumption: {selected_params['common']*30:.1f} kWh/month
629
+ """
630
+ )
631
+
632
+ # Insight box
633
+ st.info(
634
+ f"""
635
+ **Key Insights for {scenario_select}:**
636
+ - Lighting contributes {breakdown_df.loc['Lighting', 'Percentage']:.1f}% of total consumption
637
+ - Common areas account for {breakdown_df.loc['Common Areas', 'Percentage']:.1f}% of the total
638
+ - {'2BR units dominate consumption at ' + str(selected_params['2br']*100) + '% occupancy' if selected_params['2br'] > selected_params['1br'] else '1BR units are the primary consumers at ' + str(selected_params['1br']*100) + '% occupancy'}
639
+ - Total potential solar offset: {min(solar_production(st.session_state.solar_panels)/total_kwh*100, 100):.1f}%
640
+ """
641
+ )
642
+
643
+ # Footer
644
+ st.markdown("---")
645
+ st.markdown(
646
+ """
647
+ <div style="text-align: center; color: #666;">
648
+ <p>Solar Analysis Suite v1.0 | Developed with ❤️ for sustainable energy solutions</p>
649
+ </div>
650
+ """,
651
+ unsafe_allow_html=True,
652
  )
653
 
654
+
655
  if __name__ == "__main__":
656
+ main()