charagu-eric commited on
Commit
14c8553
·
1 Parent(s): 3ba1f30
Files changed (1) hide show
  1. app.py +441 -142
app.py CHANGED
@@ -19,7 +19,6 @@ LIGHTS_1BR = 5
19
  LIGHTS_2BR = 8
20
  LIGHT_POWER = 6 # Watts per light
21
 
22
-
23
  def initialize_session_state():
24
  """Initialize session state variables"""
25
  defaults = {
@@ -33,26 +32,23 @@ def initialize_session_state():
33
  if key not in st.session_state:
34
  st.session_state[key] = value
35
 
36
-
37
  def calculate_lighting_consumption(occupancy_1br: float, occupancy_2br: float) -> float:
38
  """Calculate daily lighting consumption"""
39
  return (
40
  (occupancy_1br * ONE_BR_UNITS * LIGHTS_1BR * LIGHT_POWER / 1000)
41
  + (occupancy_2br * TWO_BR_UNITS * LIGHTS_2BR * LIGHT_POWER / 1000)
42
- ) * 24 # Daily kWh
43
-
44
 
45
  def calculate_appliance_consumption(
46
  occupancy_1br: float, occupancy_2br: float
47
  ) -> float:
48
  """Calculate daily appliance consumption"""
49
  return (
50
- occupancy_1br * ONE_BR_UNITS * (250 - (LIGHTS_1BR * LIGHT_POWER * 24 / 1000))
51
  ) + (
52
- occupancy_2br * TWO_BR_UNITS * (400 - (LIGHTS_2BR * LIGHT_POWER * 24 / 1000))
53
  ) # Daily kWh
54
 
55
-
56
  def total_consumption(
57
  occupancy_1br: float, occupancy_2br: float, common_area: float
58
  ) -> float:
@@ -61,180 +57,483 @@ def total_consumption(
61
  appliances = calculate_appliance_consumption(occupancy_1br, occupancy_2br)
62
  return (lighting + appliances + common_area) * 30 # Monthly kWh
63
 
64
-
65
  def solar_production(panels: int) -> float:
66
  """Monthly solar production with losses"""
67
- return panels * SOLAR_PANEL_RATING * 5 * 0.8 * 30 / 1000 # 5 sun hours
68
-
69
 
70
  def battery_storage(batteries: int) -> float:
71
  """Usable battery capacity"""
72
- return batteries * BATTERY_CAPACITY * BATTERY_VOLTAGE * 0.8 / 1000
73
-
74
 
75
  def financial_analysis(consumption: float, production: float, storage: float) -> Dict:
76
  """Detailed financial calculations"""
77
  solar_used = min(production, consumption)
78
- grid_purchased = max(consumption - solar_used - storage, 0)
79
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  return {
81
- "solar_contribution": solar_used / consumption * 100,
82
- "grid_dependency": grid_purchased / consumption * 100,
83
- "monthly_savings": (consumption * st.session_state.grid_price)
84
- - (grid_purchased * st.session_state.grid_price),
85
- "payback_period": (
86
- st.session_state.solar_panels * st.session_state.panel_price
87
- + st.session_state.batteries * st.session_state.battery_price
88
- )
89
- / ((consumption - grid_purchased) * st.session_state.grid_price * 12),
90
  }
91
 
92
-
93
  def create_consumption_breakdown(
94
  occupancy_1br: float, occupancy_2br: float, common_area: float
95
  ):
96
  """Create detailed consumption breakdown"""
97
  breakdown = {
98
  "Lighting": calculate_lighting_consumption(occupancy_1br, occupancy_2br) * 30,
99
- "Appliances": calculate_appliance_consumption(occupancy_1br, occupancy_2br)
100
- * 30,
101
  "Common Areas": common_area * 30,
102
  }
103
  return pd.DataFrame.from_dict(breakdown, orient="index", columns=["kWh"])
104
 
105
-
106
  # Streamlit Interface
107
  def main():
108
- st.set_page_config("Solar Analysis Suite", "🌞", "wide")
109
  initialize_session_state()
110
-
111
- st.title("🌞 Advanced Solar Performance Analyzer")
112
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  with st.sidebar:
114
  st.header("System Configuration")
115
- st.number_input("Solar Panels", 1, 1000, key="solar_panels")
116
- st.number_input("Batteries", 0, 500, key="batteries")
117
- st.number_input("Panel Price (Ksh)", 1000, 50000, key="panel_price")
118
- st.number_input("Battery Price (Ksh)", 5000, 100000, key="battery_price")
119
- st.number_input("Grid Price (Ksh/kWh)", 10.0, 50.0, key="grid_price")
120
-
121
- scenarios = {
122
- "Low Occupancy": {"1br": 0.0, "2br": 1.0, "common": 5.904},
123
- "Medium Occupancy": {"1br": 0.25, "2br": 1.0, "common": 5.904},
124
- "High Occupancy": {"1br": 0.5, "2br": 1.0, "common": 5.904},
125
- }
126
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  analysis_data = []
128
  for name, params in scenarios.items():
129
  consumption = total_consumption(params["1br"], params["2br"], params["common"])
130
  production = solar_production(st.session_state.solar_panels)
131
  storage = battery_storage(st.session_state.batteries)
132
  financials = financial_analysis(consumption, production, storage)
133
-
134
- analysis_data.append(
135
- {
136
- "Scenario": name,
137
- "Consumption": consumption,
138
- "Production": production,
139
- **financials,
140
- }
141
- )
142
-
143
  df = pd.DataFrame(analysis_data)
144
-
145
- # Energy Flow Analysis
146
- st.header("Energy Flow Composition")
147
- fig, ax = plt.subplots(figsize=(10, 6))
148
- sns.barplot(
149
- data=data,
150
- x="Scenario",
151
- y="value",
152
-
153
- hue="variable",
154
- ax=ax,
155
  )
156
- )
157
- ax.set_ylabel("Percentage (%)")
158
- ax.set_title("Energy Contribution Breakdown")
159
- st.pyplot(fig)
160
-
161
- with st.expander("🔍 Energy Flow Interpretation"):
162
- st.markdown(
163
- """
164
- **Understanding the Chart:**
165
- - **Solar Contribution**: Percentage of total energy needs met by solar production
166
- - **Grid Dependency**: Remaining energy required from grid
167
- - Ideal scenario shows high solar contribution with minimal grid dependency
168
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
  )
170
-
171
- # Financial Analysis
172
- st.header("Financial Performance Metrics")
173
-
174
- # Metric 1: Monthly Savings
175
- fig1, ax1 = plt.subplots(figsize=(10, 4))
176
- sns.barplot(data=df, x="Scenario", y="monthly_savings", ax=ax1)
177
- ax1.set_title("Monthly Cost Savings")
178
- ax1.set_ylabel("Ksh")
179
- st.pyplot(fig1)
180
-
181
- with st.expander("💵 Savings Analysis"):
182
- st.markdown(
183
- f"""
184
- **Key Observations:**
185
- - Savings calculated as: `(Total Consumption × Grid Price) - (Grid Purchased × Grid Price)`
186
- - Current Grid Price: Ksh {st.session_state.grid_price}/kWh
187
- - Maximum potential savings limited by solar production capacity
188
- """
189
  )
190
- st.table(df[["Scenario", "Consumption", "Production", "monthly_savings"]])
191
-
192
- # Metric 2: Payback Period
193
- fig2, ax2 = plt.subplots(figsize=(10, 4))
194
- sns.barplot(data=df, x="Scenario", y="payback_period", ax=ax2)
195
- ax2.set_title("System Payback Period")
196
- ax2.set_ylabel("Years")
197
- st.pyplot(fig2)
198
-
199
- with st.expander("⏳ Payback Explanation"):
200
- st.markdown(
201
- f"""
202
- **Calculation Methodology:**
203
- - Total Investment: (Panels × {st.session_state.panel_price:,}Ksh) + (Batteries × {st.session_state.battery_price:,}Ksh)
204
- - Annual Savings: Monthly Savings × 12
205
- - Payback Period = Total Investment / Annual Savings
206
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
  )
208
- st.table(df[["Scenario", "payback_period"]])
209
-
210
- # Consumption Breakdown
211
- st.header("Detailed Consumption Analysis")
212
- scenario_select = st.selectbox("Select Scenario", list(scenarios.keys()))
213
-
214
- selected_params = scenarios[scenario_select]
215
- breakdown_df = create_consumption_breakdown(
216
- selected_params["1br"], selected_params["2br"], selected_params["common"]
217
- )
218
-
219
- col1, col2 = st.columns(2)
220
- with col1:
221
- st.subheader("Energy Composition")
222
- fig3, ax3 = plt.subplots()
223
- breakdown_df.plot.pie(y="kWh", ax=ax3, autopct="%1.1f%%")
224
- st.pyplot(fig3)
225
-
226
- with col2:
227
- st.subheader("Component Breakdown")
228
- st.table(breakdown_df)
229
-
230
- analysis_text = f"""
231
- **Key Insights for {scenario_select}:**
232
- - Lighting contributes {breakdown_df.loc['Lighting', 'kWh']/breakdown_df.sum().values[0]*100:.1f}% of total consumption
233
- - Common areas account for {breakdown_df.loc['Common Areas', 'kWh']:.0f}kWh monthly
234
- - 2BR units dominate appliance usage at {selected_params['2br']*100}% occupancy
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
235
  """
236
- st.markdown(analysis_text)
237
-
 
 
 
 
238
 
239
  if __name__ == "__main__":
240
  main()
 
19
  LIGHTS_2BR = 8
20
  LIGHT_POWER = 6 # Watts per light
21
 
 
22
  def initialize_session_state():
23
  """Initialize session state variables"""
24
  defaults = {
 
32
  if key not in st.session_state:
33
  st.session_state[key] = value
34
 
 
35
  def calculate_lighting_consumption(occupancy_1br: float, occupancy_2br: float) -> float:
36
  """Calculate daily lighting consumption"""
37
  return (
38
  (occupancy_1br * ONE_BR_UNITS * LIGHTS_1BR * LIGHT_POWER / 1000)
39
  + (occupancy_2br * TWO_BR_UNITS * LIGHTS_2BR * LIGHT_POWER / 1000)
40
+ ) * 6 # 6 hours per day
 
41
 
42
  def calculate_appliance_consumption(
43
  occupancy_1br: float, occupancy_2br: float
44
  ) -> float:
45
  """Calculate daily appliance consumption"""
46
  return (
47
+ occupancy_1br * ONE_BR_UNITS * (250 - (LIGHTS_1BR * LIGHT_POWER * 6 / 1000))
48
  ) + (
49
+ occupancy_2br * TWO_BR_UNITS * (400 - (LIGHTS_2BR * LIGHT_POWER * 6 / 1000))
50
  ) # Daily kWh
51
 
 
52
  def total_consumption(
53
  occupancy_1br: float, occupancy_2br: float, common_area: float
54
  ) -> float:
 
57
  appliances = calculate_appliance_consumption(occupancy_1br, occupancy_2br)
58
  return (lighting + appliances + common_area) * 30 # Monthly kWh
59
 
 
60
  def solar_production(panels: int) -> float:
61
  """Monthly solar production with losses"""
62
+ daily_production = panels * SOLAR_PANEL_RATING * 5 * (1 - SYSTEM_LOSSES) / 1000 # 5 sun hours
63
+ return daily_production * 30 # Monthly kWh
64
 
65
  def battery_storage(batteries: int) -> float:
66
  """Usable battery capacity"""
67
+ return batteries * BATTERY_CAPACITY * BATTERY_VOLTAGE * 0.8 / 1000 # kWh
 
68
 
69
  def financial_analysis(consumption: float, production: float, storage: float) -> Dict:
70
  """Detailed financial calculations"""
71
  solar_used = min(production, consumption)
72
+ surplus = max(0, production - consumption)
73
+ feed_in_revenue = surplus * FEED_IN_TARIFF / 100 # Convert to Ksh from cents/kWh
74
+
75
+ # Account for battery storage
76
+ grid_purchased = max(0, consumption - solar_used)
77
+ if storage > 0:
78
+ # Battery can offset some grid purchases
79
+ grid_offset = min(grid_purchased, storage)
80
+ grid_purchased -= grid_offset
81
+
82
+ monthly_savings = (consumption * st.session_state.grid_price / 100) - (grid_purchased * st.session_state.grid_price / 100) + feed_in_revenue
83
+
84
+ total_investment = (
85
+ st.session_state.solar_panels * st.session_state.panel_price
86
+ + st.session_state.batteries * st.session_state.battery_price
87
+ )
88
+
89
+ # Avoid division by zero
90
+ if monthly_savings > 0:
91
+ payback_years = total_investment / (monthly_savings * 12)
92
+ else:
93
+ payback_years = float('inf')
94
+
95
  return {
96
+ "consumption": consumption,
97
+ "production": production,
98
+ "solar_contribution": min(100, (solar_used / max(1, consumption)) * 100),
99
+ "grid_dependency": (grid_purchased / max(1, consumption)) * 100,
100
+ "monthly_savings": monthly_savings,
101
+ "payback_period": payback_years,
102
+ "grid_purchased": grid_purchased,
 
 
103
  }
104
 
 
105
  def create_consumption_breakdown(
106
  occupancy_1br: float, occupancy_2br: float, common_area: float
107
  ):
108
  """Create detailed consumption breakdown"""
109
  breakdown = {
110
  "Lighting": calculate_lighting_consumption(occupancy_1br, occupancy_2br) * 30,
111
+ "Appliances": calculate_appliance_consumption(occupancy_1br, occupancy_2br) * 30,
 
112
  "Common Areas": common_area * 30,
113
  }
114
  return pd.DataFrame.from_dict(breakdown, orient="index", columns=["kWh"])
115
 
 
116
  # Streamlit Interface
117
  def main():
118
+ st.set_page_config(page_title="Solar Analysis Suite", page_icon="🌞", layout="wide")
119
  initialize_session_state()
120
+
121
+ # Custom CSS
122
+ st.markdown("""
123
+ <style>
124
+ .main .block-container {padding-top: 2rem;}
125
+ h1, h2, h3 {color: #1E88E5;}
126
+ .stExpander {border-radius: 8px; border: 1px solid #1E88E5;}
127
+ .stTabs [data-baseweb="tab-list"] {gap: 10px;}
128
+ .stTabs [data-baseweb="tab"] {
129
+ height: 50px;
130
+ white-space: pre-wrap;
131
+ background-color: #F0F2F6;
132
+ border-radius: 4px 4px 0px 0px;
133
+ gap: 1px;
134
+ padding-top: 10px;
135
+ padding-bottom: 10px;
136
+ }
137
+ .stTabs [aria-selected="true"] {
138
+ background-color: #1E88E5;
139
+ color: white;
140
+ }
141
+ </style>
142
+ """, unsafe_allow_html=True)
143
+
144
+ # Header with logo
145
+ col1, col2 = st.columns([1, 4])
146
+ with col1:
147
+ st.image("https://img.icons8.com/fluency/96/000000/sun.png", width=100)
148
+ with col2:
149
+ st.title("🌞 Advanced Solar Performance Analyzer")
150
+ st.markdown("Optimize your apartment complex solar installation with data-driven insights")
151
+
152
+ # Sidebar for system configuration
153
  with st.sidebar:
154
  st.header("System Configuration")
155
+
156
+ # Add a nice header image
157
+ st.image("https://img.icons8.com/color/96/000000/solar-panel.png", width=80)
158
+
159
+ # Create tabs for different settings
160
+ tab1, tab2 = st.tabs(["Hardware", "Pricing"])
161
+
162
+ with tab1:
163
+ st.slider("Number of Solar Panels", 1, 300, key="solar_panels", help="Each panel rated at 625W")
164
+ st.slider("Number of Batteries", 0, 150, key="batteries", help="Each battery has 200Ah capacity at 12V")
165
+
166
+ with tab2:
167
+ st.number_input("Panel Price (Ksh)", 1000, 50000, step=500, key="panel_price",
168
+ help="Cost per solar panel")
169
+ st.number_input("Battery Price (Ksh)", 5000, 100000, step=1000, key="battery_price",
170
+ help="Cost per battery unit")
171
+ st.number_input("Grid Price (Ksh/kWh)", 10.0, 50.0, step=0.1, key="grid_price",
172
+ help="Current electricity price from the grid")
173
+
174
+ st.markdown("---")
175
+ st.markdown("""
176
+ 📊 **System Totals**
177
+ - **Total Panel Capacity**: {0:.1f} kW
178
+ - **Total Battery Storage**: {1:.1f} kWh
179
+ - **Total Investment**: {2:,.0f} Ksh
180
+ """.format(
181
+ st.session_state.solar_panels * SOLAR_PANEL_RATING / 1000,
182
+ battery_storage(st.session_state.batteries),
183
+ st.session_state.solar_panels * st.session_state.panel_price +
184
+ st.session_state.batteries * st.session_state.battery_price
185
+ ))
186
+
187
+ # Main content
188
+ # Create scenarios with varying occupancy levels
189
+ scenarios = {}
190
+
191
+ # Common area consumption remains constant
192
+ common_area_consumption = 5.904 # kWh per day
193
+
194
+ # Generate scenarios with different occupancy combinations
195
+ occupancy_levels = [0.0, 0.25, 0.50, 0.75, 1.0]
196
+
197
+ # Create scenarios for 1BR fixed, varying 2BR
198
+ for br1_level in occupancy_levels:
199
+ for br2_level in occupancy_levels:
200
+ scenario_name = f"1BR: {int(br1_level*100)}%, 2BR: {int(br2_level*100)}%"
201
+ scenarios[scenario_name] = {
202
+ "1br": br1_level,
203
+ "2br": br2_level,
204
+ "common": common_area_consumption
205
+ }
206
+
207
+ # Analysis tabs
208
+ st.markdown("---")
209
+ tab1, tab2, tab3 = st.tabs(["📊 Energy Analysis", "💰 Financial Metrics", "🔍 Detailed Breakdown"])
210
+
211
+ # Prepare analysis data for all scenarios
212
  analysis_data = []
213
  for name, params in scenarios.items():
214
  consumption = total_consumption(params["1br"], params["2br"], params["common"])
215
  production = solar_production(st.session_state.solar_panels)
216
  storage = battery_storage(st.session_state.batteries)
217
  financials = financial_analysis(consumption, production, storage)
218
+ analysis_data.append({
219
+ "Scenario": name,
220
+ **financials
221
+ })
222
+
 
 
 
 
 
223
  df = pd.DataFrame(analysis_data)
224
+
225
+ # Tab 1: Energy Analysis
226
+ with tab1:
227
+ st.header("Energy Flow Analysis")
228
+
229
+ # Allow filtering by 1BR occupancy
230
+ one_br_filter = st.selectbox(
231
+ "Filter by 1BR Occupancy",
232
+ ["All"] + [f"{int(level*100)}%" for level in occupancy_levels],
233
+ help="Filter scenarios by 1BR occupancy level"
 
234
  )
235
+
236
+ # Filter the dataframe based on selection
237
+ filtered_df = df
238
+ if one_br_filter != "All":
239
+ occupancy_value = int(one_br_filter.replace("%", ""))
240
+ filtered_df = df[df["Scenario"].str.contains(f"1BR: {occupancy_value}%")]
241
+
242
+ # Chart 1: Energy Balance
243
+ st.subheader("Energy Balance by Scenario")
244
+
245
+ energy_fig = plt.figure(figsize=(12, 7))
246
+ ax = energy_fig.add_subplot(111)
247
+
248
+ # Create data for stacked bar chart
249
+ chart_data = filtered_df.copy()
250
+ chart_data["grid_energy"] = chart_data["grid_purchased"]
251
+ chart_data["solar_energy"] = chart_data["consumption"] - chart_data["grid_purchased"]
252
+
253
+ # Create normalized stacked bar chart
254
+ chart_data = chart_data.set_index("Scenario")
255
+ energy_proportions = chart_data[["solar_energy", "grid_energy"]].div(chart_data["consumption"], axis=0) * 100
256
+ energy_proportions = energy_proportions.reset_index()
257
+
258
+ # Reshape for seaborn
259
+ energy_melt = pd.melt(
260
+ energy_proportions,
261
+ id_vars=["Scenario"],
262
+ value_vars=["solar_energy", "grid_energy"],
263
+ var_name="Energy Source",
264
+ value_name="Percentage"
265
  )
266
+
267
+ # Rename for better labels
268
+ energy_melt["Energy Source"] = energy_melt["Energy Source"].replace({
269
+ "solar_energy": "Solar Generated",
270
+ "grid_energy": "Grid Purchased"
271
+ })
272
+
273
+ # Plot with seaborn
274
+ sns.set_theme(style="whitegrid")
275
+ sns.barplot(
276
+ data=energy_melt,
277
+ x="Scenario",
278
+ y="Percentage",
279
+ hue="Energy Source",
280
+ palette=["#4CAF50", "#F44336"],
281
+ ax=ax
 
 
 
282
  )
283
+ ax.set_ylabel("Energy Contribution (%)")
284
+ ax.set_title("Energy Source Distribution by Occupancy Scenario")
285
+ plt.xticks(rotation=45, ha="right")
286
+ plt.tight_layout()
287
+ st.pyplot(energy_fig)
288
+
289
+ # Detailed metrics
290
+ col1, col2, col3 = st.columns(3)
291
+ with col1:
292
+ st.metric(
293
+ "Avg. Solar Contribution",
294
+ f"{filtered_df['solar_contribution'].mean():.1f}%",
295
+ f"{filtered_df['solar_contribution'].mean() - 50:.1f}%" if filtered_df['solar_contribution'].mean() > 50 else f"{filtered_df['solar_contribution'].mean() - 50:.1f}%"
296
+ )
297
+ with col2:
298
+ st.metric(
299
+ "Avg. Grid Dependency",
300
+ f"{filtered_df['grid_dependency'].mean():.1f}%",
301
+ f"{50 - filtered_df['grid_dependency'].mean():.1f}%" if filtered_df['grid_dependency'].mean() < 50 else f"{50 - filtered_df['grid_dependency'].mean():.1f}%"
302
+ )
303
+ with col3:
304
+ st.metric(
305
+ "Production/Consumption Ratio",
306
+ f"{(filtered_df['production'].mean() / filtered_df['consumption'].mean() * 100):.1f}%"
307
+ )
308
+
309
+ with st.expander("🔍 Energy Flow Interpretation"):
310
+ st.markdown(
311
+ """
312
+ **Understanding the Chart:**
313
+ - **Solar Contribution**: Percentage of total energy needs met directly by solar production
314
+ - **Grid Dependency**: Remaining energy required from the grid
315
+ - The ideal scenario shows high solar contribution with minimal grid dependency
316
+
317
+ **Key Factors Affecting Energy Balance:**
318
+ 1. **Occupancy Levels**: Higher occupancy means higher consumption, which may exceed solar capacity
319
+ 2. **Solar System Size**: More panels increase production and reduce grid dependency
320
+ 3. **Battery Storage**: Helps utilize excess daytime production for nighttime use
321
+ """
322
+ )
323
+
324
+ # Tab 2: Financial Metrics
325
+ with tab2:
326
+ st.header("Financial Performance Analysis")
327
+
328
+ # Allow filtering by 2BR occupancy
329
+ two_br_filter = st.selectbox(
330
+ "Filter by 2BR Occupancy",
331
+ ["All"] + [f"{int(level*100)}%" for level in occupancy_levels],
332
+ help="Filter scenarios by 2BR occupancy level"
333
  )
334
+
335
+ # Filter the dataframe based on selection
336
+ filtered_fin_df = df
337
+ if two_br_filter != "All":
338
+ occupancy_value = int(two_br_filter.replace("%", ""))
339
+ filtered_fin_df = df[df["Scenario"].str.contains(f"2BR: {occupancy_value}%")]
340
+
341
+ # Monthly Savings Chart
342
+ st.subheader("Monthly Cost Savings")
343
+
344
+ # Fix large values
345
+ filtered_fin_df['monthly_savings_fixed'] = filtered_fin_df['monthly_savings'].clip(0, 100000)
346
+
347
+ fig1, ax1 = plt.subplots(figsize=(12, 6))
348
+ sns.barplot(
349
+ data=filtered_fin_df,
350
+ x="Scenario",
351
+ y="monthly_savings_fixed",
352
+ palette="viridis",
353
+ ax=ax1
354
+ )
355
+ ax1.set_title("Monthly Cost Savings by Scenario")
356
+ ax1.set_ylabel("Ksh")
357
+ plt.xticks(rotation=45, ha="right")
358
+ plt.tight_layout()
359
+ st.pyplot(fig1)
360
+
361
+ # Payback Period Chart
362
+ st.subheader("System Payback Period")
363
+
364
+ # Fix large values
365
+ filtered_fin_df['payback_period_fixed'] = filtered_fin_df['payback_period'].clip(0, 30)
366
+
367
+ fig2, ax2 = plt.subplots(figsize=(12, 6))
368
+ sns.barplot(
369
+ data=filtered_fin_df,
370
+ x="Scenario",
371
+ y="payback_period_fixed",
372
+ palette="rocket_r",
373
+ ax=ax2
374
+ )
375
+ ax2.set_title("Investment Payback Period by Scenario")
376
+ ax2.set_ylabel("Years")
377
+ plt.xticks(rotation=45, ha="right")
378
+ plt.tight_layout()
379
+ st.pyplot(fig2)
380
+
381
+ # Financial summary metrics
382
+ col1, col2, col3 = st.columns(3)
383
+ with col1:
384
+ avg_savings = filtered_fin_df['monthly_savings'].mean()
385
+ st.metric(
386
+ "Avg. Monthly Savings",
387
+ f"{avg_savings:,.0f} Ksh",
388
+ f"{avg_savings - df['monthly_savings'].mean():,.0f} Ksh" if avg_savings > df['monthly_savings'].mean() else f"{avg_savings - df['monthly_savings'].mean():,.0f} Ksh"
389
+ )
390
+ with col2:
391
+ min_payback = filtered_fin_df['payback_period'].min()
392
+ st.metric(
393
+ "Best Payback Period",
394
+ f"{min_payback:.1f} years",
395
+ help="Shortest time to recover investment"
396
+ )
397
+ with col3:
398
+ total_investment = (st.session_state.solar_panels * st.session_state.panel_price +
399
+ st.session_state.batteries * st.session_state.battery_price)
400
+ annual_roi = (avg_savings * 12 / total_investment) * 100 if total_investment > 0 else 0
401
+ st.metric(
402
+ "Annual ROI",
403
+ f"{annual_roi:.1f}%",
404
+ help="Annual Return on Investment"
405
+ )
406
+
407
+ with st.expander("💵 Financial Analysis Details"):
408
+ st.markdown(
409
+ f"""
410
+ **Investment Details:**
411
+ - 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
412
+ - Total Battery Investment: {st.session_state.batteries:,} batteries × {st.session_state.battery_price:,} Ksh = {st.session_state.batteries * st.session_state.battery_price:,} Ksh
413
+ - Total System Cost: {total_investment:,} Ksh
414
+
415
+ **Savings Calculation:**
416
+ - Grid Price: {st.session_state.grid_price} Ksh/kWh
417
+ - Monthly Savings = (Total Consumption × Grid Price) - (Grid Purchased × Grid Price)
418
+ - Payback Period = Total Investment / Annual Savings
419
+
420
+ **Filtered Scenario Data:**
421
+ """
422
+ )
423
+ st.dataframe(
424
+ filtered_fin_df[["Scenario", "consumption", "production", "monthly_savings", "payback_period"]].sort_values("monthly_savings", ascending=False),
425
+ hide_index=True
426
+ )
427
+
428
+ # Tab 3: Detailed Breakdown
429
+ with tab3:
430
+ st.header("Consumption Breakdown Analysis")
431
+
432
+ # Select specific scenario for detailed analysis
433
+ scenario_select = st.selectbox("Select Specific Scenario", list(scenarios.keys()))
434
+ selected_params = scenarios[scenario_select]
435
+
436
+ # Create consumption breakdown
437
+ breakdown_df = create_consumption_breakdown(
438
+ selected_params["1br"], selected_params["2br"], selected_params["common"]
439
+ )
440
+
441
+ total_kwh = breakdown_df["kWh"].sum()
442
+
443
+ # Add percentage column
444
+ breakdown_df["Percentage"] = (breakdown_df["kWh"] / total_kwh * 100).round(1)
445
+
446
+ col1, col2 = st.columns([2, 3])
447
+
448
+ with col1:
449
+ st.subheader("Energy Composition")
450
+
451
+ # Create a more attractive pie chart
452
+ fig3 = plt.figure(figsize=(8, 8))
453
+ ax3 = fig3.add_subplot(111)
454
+
455
+ colors = ['#FF9800', '#2196F3', '#4CAF50']
456
+ explode = (0.1, 0, 0)
457
+
458
+ wedges, texts, autotexts = ax3.pie(
459
+ breakdown_df["kWh"],
460
+ labels=breakdown_df.index,
461
+ autopct='%1.1f%%',
462
+ explode=explode,
463
+ colors=colors,
464
+ shadow=True,
465
+ startangle=90,
466
+ textprops={'fontsize': 12}
467
+ )
468
+
469
+ # Equal aspect ratio ensures that pie is drawn as a circle
470
+ ax3.axis('equal')
471
+ plt.tight_layout()
472
+ st.pyplot(fig3)
473
+
474
+ # Show total consumption
475
+ st.metric(
476
+ "Total Monthly Consumption",
477
+ f"{total_kwh:.1f} kWh",
478
+ help="Sum of all consumption components"
479
+ )
480
+
481
+ with col2:
482
+ st.subheader("Detailed Component Analysis")
483
+
484
+ # Show breakdown as a horizontal bar chart
485
+ fig4 = plt.figure(figsize=(10, 5))
486
+ ax4 = fig4.add_subplot(111)
487
+
488
+ # Sort by consumption
489
+ sorted_df = breakdown_df.sort_values("kWh", ascending=True)
490
+
491
+ # Create horizontal bar chart
492
+ bars = sns.barplot(
493
+ y=sorted_df.index,
494
+ x="kWh",
495
+ data=sorted_df,
496
+ palette=colors[::-1],
497
+ ax=ax4
498
+ )
499
+
500
+ # Add data labels
501
+ for i, v in enumerate(sorted_df["kWh"]):
502
+ ax4.text(v + 5, i, f"{v:.1f} kWh ({sorted_df['Percentage'].iloc[i]}%)", va='center')
503
+
504
+ ax4.set_title(f"Energy Consumption Breakdown - {scenario_select}")
505
+ ax4.set_xlabel("Monthly Consumption (kWh)")
506
+ ax4.set_ylabel("")
507
+ plt.tight_layout()
508
+ st.pyplot(fig4)
509
+
510
+ # Add scenario details
511
+ st.markdown(f"""
512
+ **Scenario Details:**
513
+ - 1BR Units Occupancy: {selected_params['1br']*100:.0f}% ({selected_params['1br']*ONE_BR_UNITS:.0f} units)
514
+ - 2BR Units Occupancy: {selected_params['2br']*100:.0f}% ({selected_params['2br']*TWO_BR_UNITS:.0f} units)
515
+ - Common Areas Consumption: {selected_params['common']*30:.1f} kWh/month
516
+ """)
517
+
518
+ # Insight box
519
+ st.info(f"""
520
+ **Key Insights for {scenario_select}:**
521
+ - Lighting contributes {breakdown_df.loc['Lighting', 'Percentage']:.1f}% of total consumption
522
+ - Common areas account for {breakdown_df.loc['Common Areas', 'Percentage']:.1f}% of the total
523
+ - {'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'}
524
+ - Total potential solar offset: {min(solar_production(st.session_state.solar_panels)/total_kwh*100, 100):.1f}%
525
+ """)
526
+
527
+ # Footer
528
+ st.markdown("---")
529
+ st.markdown(
530
  """
531
+ <div style="text-align: center; color: #666;">
532
+ <p>Solar Analysis Suite v1.0 | Developed with ❤️ for sustainable energy solutions</p>
533
+ </div>
534
+ """,
535
+ unsafe_allow_html=True
536
+ )
537
 
538
  if __name__ == "__main__":
539
  main()