charagu-eric commited on
Commit
d1cd9d0
·
1 Parent(s): f2a4aec
Files changed (1) hide show
  1. app.py +174 -236
app.py CHANGED
@@ -9,264 +9,202 @@ from typing import Dict
9
  ONE_BR_UNITS = 23
10
  TWO_BR_UNITS = 45
11
  SOLAR_PANEL_RATING = 625 # W
12
- SOLAR_PANEL_COST = 13000 # KSH per panel
13
  BATTERY_CAPACITY = 200 # Ah
14
  BATTERY_VOLTAGE = 12 # V
15
- BATTERY_COST = 39000 # KSH per battery
16
- SYSTEM_LOSSES = 0.20 # 20% system losses
17
- GRID_COST_PER_KWH = 28.44 # KSH
18
- FEED_IN_TARIFF = 12 # KSH per kWh sold back to grid
19
-
20
- # Consumption scenarios (kWh/month)
21
- ONE_BR_CONSUMPTION = 250
22
- TWO_BR_CONSUMPTION = 400
23
- COMMON_AREA_CURRENT = 1.974 # kWh
24
- COMMON_AREA_INCREASED = 5.904 # kWh
25
 
 
 
 
 
26
 
27
  def initialize_session_state():
28
  """Initialize session state variables"""
29
- if "solar_panels" not in st.session_state:
30
- st.session_state.solar_panels = 100
31
- if "batteries" not in st.session_state:
32
- st.session_state.batteries = 50
33
-
 
 
 
 
 
34
 
35
- def calculate_consumption(
36
- one_br_occupancy: float, two_br_occupancy: float, common_area: float
37
- ) -> float:
38
- """Calculate total monthly consumption based on occupancy rates"""
39
  return (
40
- one_br_occupancy * ONE_BR_UNITS * ONE_BR_CONSUMPTION
41
- + two_br_occupancy * TWO_BR_UNITS * TWO_BR_CONSUMPTION
42
- + common_area * 30 # Convert daily to monthly
43
- )
44
-
45
-
46
- def solar_production(panel_count: int, sun_hours: float = 5) -> float:
47
- """Calculate daily solar production considering losses"""
48
- daily_production = (
49
- panel_count * SOLAR_PANEL_RATING * sun_hours * (1 - SYSTEM_LOSSES) / 1000
50
- ) # kWh
51
- return daily_production * 30 # Monthly production
52
-
53
 
54
- def battery_storage(battery_count: int) -> float:
55
- """Calculate usable battery storage considering losses"""
56
- total_capacity = battery_count * BATTERY_CAPACITY * BATTERY_VOLTAGE / 1000 # kWh
57
- return total_capacity * (1 - SYSTEM_LOSSES)
58
-
59
-
60
- def financial_analysis(
61
- consumption: float, production: float, storage: float
62
- ) -> Dict[str, float]:
63
- """Calculate financial metrics with detailed energy flows"""
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  solar_used = min(production, consumption)
65
- excess_solar = max(production - consumption, 0)
66
- grid_purchased = max(consumption - solar_used, 0)
67
-
68
- # Battery operations
69
- battery_stored = min(excess_solar, storage)
70
- battery_used = min(grid_purchased, storage)
71
-
72
- final_grid = max(grid_purchased - battery_used, 0)
73
- final_excess = max(excess_solar - battery_stored, 0)
74
-
75
- grid_cost = final_grid * GRID_COST_PER_KWH
76
- feed_in_income = final_excess * FEED_IN_TARIFF
77
- savings = (consumption * GRID_COST_PER_KWH) - grid_cost + feed_in_income
78
-
79
  return {
80
- "consumption": consumption,
81
- "production": production,
82
- "solar_used": solar_used,
83
- "excess_solar": final_excess,
84
- "grid_purchased": final_grid,
85
- "battery_utilization": (
86
- (battery_used + battery_stored) / storage if storage > 0 else 0
87
- ),
88
- "solar_coverage": solar_used / consumption if consumption > 0 else 0,
89
- "monthly_savings": savings,
90
- "extra_grid_cost_saved": (3.93 * 30 * GRID_COST_PER_KWH)
91
- - (final_grid * GRID_COST_PER_KWH),
92
- "profit_margin": (feed_in_income + savings)
93
- / (consumption * GRID_COST_PER_KWH)
94
- * 100,
95
  }
96
 
 
 
 
 
 
 
 
 
97
 
98
- def plot_comparison(data: pd.DataFrame, metric: str, title: str):
99
- """Create comparison plots using Seaborn"""
100
- plt.figure(figsize=(10, 6))
101
- ax = sns.barplot(data=data, x="Scenario", y=metric, hue="Common Area Usage")
102
-
103
- # Add value annotations
104
- for p in ax.patches:
105
- ax.annotate(
106
- f"{p.get_height():.1f}",
107
- (p.get_x() + p.get_width() / 2.0, p.get_height()),
108
- ha="center",
109
- va="center",
110
- xytext=(0, 10),
111
- textcoords="offset points",
112
- )
113
-
114
- plt.title(title)
115
- plt.xticks(rotation=45)
116
- plt.tight_layout()
117
- st.pyplot(plt)
118
-
119
-
120
  def main():
121
- st.set_page_config(
122
- page_title="Solar Profitability Analyzer", page_icon="📊", layout="wide"
123
- )
124
  initialize_session_state()
125
-
126
- st.title("📊 Solar Profitability Analysis")
127
- st.markdown(
128
- """
129
- Comparing current vs increased common area usage (1.974kWh vs 5.904kWh daily)
130
- Grid cost: Ksh 28.44/kWh | Extra 3.93kWh would cost Ksh {:.2f} monthly
131
- """.format(
132
- 3.93 * 30 * GRID_COST_PER_KWH
133
- )
134
- )
135
-
136
- # System configuration
137
  with st.sidebar:
138
  st.header("System Configuration")
139
- st.session_state.solar_panels = st.number_input(
140
- "Solar Panels", 0, 1000, st.session_state.solar_panels
141
- )
142
- st.session_state.batteries = st.number_input(
143
- "Batteries", 0, 500, st.session_state.batteries
144
- )
145
-
146
- st.markdown(
147
- f"""
148
- **System Details:**
149
- - Panel: {SOLAR_PANEL_RATING}W @ Ksh{SOLAR_PANEL_COST:,}
150
- - Battery: {BATTERY_CAPACITY}Ah @ Ksh{BATTERY_COST:,}
151
- - Losses: {SYSTEM_LOSSES*100:.0f}%
152
- """
153
- )
154
-
155
- # Occupancy scenarios
156
  scenarios = {
157
- "1BR:0%, 2BR:100%": {"1br": 0.0, "2br": 1.0},
158
- "1BR:25%, 2BR:100%": {"1br": 0.25, "2br": 1.0},
159
- "1BR:50%, 2BR:100%": {"1br": 0.5, "2br": 1.0},
160
  }
161
-
162
- # Common area scenarios
163
- common_areas = {
164
- "Current (1.974kWh)": COMMON_AREA_CURRENT,
165
- "Increased (5.904kWh)": COMMON_AREA_INCREASED,
166
- }
167
-
168
- # Calculate all combinations
169
- results = []
170
- for scenario_name, occupancy in scenarios.items():
171
- for area_name, area_usage in common_areas.items():
172
- consumption = calculate_consumption(
173
- occupancy["1br"], occupancy["2br"], area_usage
174
- )
175
-
176
- production = solar_production(st.session_state.solar_panels)
177
- storage = battery_storage(st.session_state.batteries)
178
-
179
- financials = financial_analysis(consumption, production, storage)
180
-
181
- results.append(
182
- {
183
- "Scenario": scenario_name,
184
- "Common Area Usage": area_name,
185
- **financials,
186
- }
187
- )
188
-
189
- df = pd.DataFrame(results)
190
-
191
- # Key metrics comparison
192
- st.subheader("Performance Comparison")
193
- col1, col2, col3 = st.columns(3)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
  with col1:
195
- plot_comparison(df, "monthly_savings", "Monthly Savings (Ksh)")
 
 
 
 
196
  with col2:
197
- plot_comparison(df, "extra_grid_cost_saved", "Extra Grid Cost Saved (Ksh)")
198
- with col3:
199
- plot_comparison(df, "profit_margin", "Profit Margin (%)")
200
-
201
- # Energy flow visualization
202
- st.subheader("Energy Flow Analysis")
203
- flow_df = df.melt(
204
- id_vars=["Scenario", "Common Area Usage"],
205
- value_vars=["solar_used", "grid_purchased", "excess_solar"],
206
- var_name="Flow",
207
- value_name="kWh",
208
- )
209
-
210
- plt.figure(figsize=(12, 6))
211
- sns.barplot(
212
- data=flow_df,
213
- x="Scenario",
214
- y="kWh",
215
- hue="Flow",
216
- palette="viridis",
217
- )
218
- plt.title("Energy Flow Composition")
219
- plt.ylabel("Monthly Energy (kWh)")
220
- plt.xticks(rotation=45)
221
- st.pyplot(plt)
222
-
223
- # Detailed data table
224
- st.subheader("Detailed Financial Analysis")
225
- display_df = df[
226
- [
227
- "Scenario",
228
- "Common Area Usage",
229
- "consumption",
230
- "production",
231
- "solar_coverage",
232
- "grid_purchased",
233
- "monthly_savings",
234
- "extra_grid_cost_saved",
235
- "profit_margin",
236
- ]
237
- ].copy()
238
-
239
- display_df.columns = [
240
- "Scenario",
241
- "Common Area",
242
- "Consumption (kWh)",
243
- "Production (kWh)",
244
- "Solar Coverage (%)",
245
- "Grid Purchased (kWh)",
246
- "Savings (Ksh)",
247
- "Extra Cost Saved (Ksh)",
248
- "Profit Margin (%)",
249
- ]
250
-
251
- # Format numeric columns
252
- for col in display_df.columns[2:]:
253
- display_df[col] = display_df[col].apply(lambda x: f"{x:,.1f}")
254
-
255
- st.dataframe(display_df, hide_index=True)
256
-
257
- # Savings potential highlight
258
- max_saving = df["extra_grid_cost_saved"].max()
259
- best_case = df[df["extra_grid_cost_saved"] == max_saving].iloc[0]
260
-
261
- st.success(
262
- f"""
263
- **Maximum Extra Cost Savings Potential:**
264
- Ksh {max_saving:,.2f}/month in scenario:
265
- {best_case['Scenario']} with {best_case['Common Area Usage']}
266
- (Current profit margin: {best_case['profit_margin']:.1f}%)
267
- """
268
- )
269
-
270
 
271
  if __name__ == "__main__":
272
  main()
 
9
  ONE_BR_UNITS = 23
10
  TWO_BR_UNITS = 45
11
  SOLAR_PANEL_RATING = 625 # W
 
12
  BATTERY_CAPACITY = 200 # Ah
13
  BATTERY_VOLTAGE = 12 # V
14
+ SYSTEM_LOSSES = 0.20
15
+ FEED_IN_TARIFF = 12
 
 
 
 
 
 
 
 
16
 
17
+ # Lighting specifications
18
+ LIGHTS_1BR = 5
19
+ LIGHTS_2BR = 8
20
+ LIGHT_POWER = 6 # Watts per light
21
 
22
  def initialize_session_state():
23
  """Initialize session state variables"""
24
+ defaults = {
25
+ 'solar_panels': 100,
26
+ 'batteries': 50,
27
+ 'panel_price': 13000,
28
+ 'battery_price': 39000,
29
+ 'grid_price': 28.44
30
+ }
31
+ for key, value in defaults.items():
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
+ ) * 24 # Daily kWh
 
 
 
 
 
 
 
 
 
 
41
 
42
+ def calculate_appliance_consumption(occupancy_1br: float, occupancy_2br: float) -> float:
43
+ """Calculate daily appliance consumption"""
44
+ return (
45
+ (occupancy_1br * ONE_BR_UNITS * (250 - (LIGHTS_1BR * LIGHT_POWER * 24 / 1000))) +
46
+ (occupancy_2br * TWO_BR_UNITS * (400 - (LIGHTS_2BR * LIGHT_POWER * 24 / 1000)))
47
+ ) # Daily kWh
48
+
49
+ def total_consumption(occupancy_1br: float, occupancy_2br: float, common_area: float) -> float:
50
+ """Calculate total monthly consumption"""
51
+ lighting = calculate_lighting_consumption(occupancy_1br, occupancy_2br)
52
+ appliances = calculate_appliance_consumption(occupancy_1br, occupancy_2br)
53
+ return (lighting + appliances + common_area) * 30 # Monthly kWh
54
+
55
+ def solar_production(panels: int) -> float:
56
+ """Monthly solar production with losses"""
57
+ return panels * SOLAR_PANEL_RATING * 5 * 0.8 * 30 / 1000 # 5 sun hours
58
+
59
+ def battery_storage(batteries: int) -> float:
60
+ """Usable battery capacity"""
61
+ return batteries * BATTERY_CAPACITY * BATTERY_VOLTAGE * 0.8 / 1000
62
+
63
+ def financial_analysis(consumption: float, production: float, storage: float) -> Dict:
64
+ """Detailed financial calculations"""
65
  solar_used = min(production, consumption)
66
+ grid_purchased = max(consumption - solar_used - storage, 0)
67
+
 
 
 
 
 
 
 
 
 
 
 
 
68
  return {
69
+ 'solar_contribution': solar_used / consumption * 100,
70
+ 'grid_dependency': grid_purchased / consumption * 100,
71
+ 'monthly_savings': (consumption * st.session_state.grid_price) -
72
+ (grid_purchased * st.session_state.grid_price),
73
+ 'payback_period': (st.session_state.solar_panels * st.session_state.panel_price +
74
+ st.session_state.batteries * st.session_state.battery_price) /
75
+ ((consumption - grid_purchased) * st.session_state.grid_price * 12)
 
 
 
 
 
 
 
 
76
  }
77
 
78
+ def create_consumption_breakdown(occupancy_1br: float, occupancy_2br: float, common_area: float):
79
+ """Create detailed consumption breakdown"""
80
+ breakdown = {
81
+ 'Lighting': calculate_lighting_consumption(occupancy_1br, occupancy_2br) * 30,
82
+ 'Appliances': calculate_appliance_consumption(occupancy_1br, occupancy_2br) * 30,
83
+ 'Common Areas': common_area * 30
84
+ }
85
+ return pd.DataFrame.from_dict(breakdown, orient='index', columns=['kWh'])
86
 
87
+ # Streamlit Interface
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  def main():
89
+ st.set_page_config("Solar Analysis Suite", "🌞", "wide")
 
 
90
  initialize_session_state()
91
+
92
+ st.title("🌞 Advanced Solar Performance Analyzer")
93
+
 
 
 
 
 
 
 
 
 
94
  with st.sidebar:
95
  st.header("System Configuration")
96
+ st.number_input("Solar Panels", 1, 1000, key='solar_panels')
97
+ st.number_input("Batteries", 0, 500, key='batteries')
98
+ st.number_input("Panel Price (Ksh)", 1000, 50000, key='panel_price')
99
+ st.number_input("Battery Price (Ksh)", 5000, 100000, key='battery_price')
100
+ st.number_input("Grid Price (Ksh/kWh)", 10.0, 50.0, key='grid_price')
101
+
 
 
 
 
 
 
 
 
 
 
 
102
  scenarios = {
103
+ "Low Occupancy": {"1br": 0.0, "2br": 1.0, "common": 5.904},
104
+ "Medium Occupancy": {"1br": 0.25, "2br": 1.0, "common": 5.904},
105
+ "High Occupancy": {"1br": 0.5, "2br": 1.0, "common": 5.904}
106
  }
107
+
108
+ analysis_data = []
109
+ for name, params in scenarios.items():
110
+ consumption = total_consumption(
111
+ params["1br"], params["2br"], params["common"]
112
+ )
113
+ production = solar_production(st.session_state.solar_panels)
114
+ storage = battery_storage(st.session_state.batteries)
115
+ financials = financial_analysis(consumption, production, storage)
116
+
117
+ analysis_data.append({
118
+ "Scenario": name,
119
+ "Consumption": consumption,
120
+ "Production": production,
121
+ **financials
122
+ })
123
+
124
+ df = pd.DataFrame(analysis_data)
125
+
126
+ # Energy Flow Analysis
127
+ st.header("Energy Flow Composition")
128
+ fig, ax = plt.subplots(figsize=(10, 6))
129
+ sns.barplot(df.melt(id_vars=['Scenario'],
130
+ value_vars=['solar_contribution', 'grid_dependency'],
131
+ x='Scenario', y='value', hue='variable', ax=ax)
132
+ ax.set_ylabel("Percentage (%)")
133
+ ax.set_title("Energy Contribution Breakdown")
134
+ st.pyplot(fig)
135
+
136
+ with st.expander("🔍 Energy Flow Interpretation"):
137
+ st.markdown("""
138
+ **Understanding the Chart:**
139
+ - **Solar Contribution**: Percentage of total energy needs met by solar production
140
+ - **Grid Dependency**: Remaining energy required from grid
141
+ - Ideal scenario shows high solar contribution with minimal grid dependency
142
+ """)
143
+
144
+ # Financial Analysis
145
+ st.header("Financial Performance Metrics")
146
+
147
+ # Metric 1: Monthly Savings
148
+ fig1, ax1 = plt.subplots(figsize=(10, 4))
149
+ sns.barplot(data=df, x='Scenario', y='monthly_savings', ax=ax1)
150
+ ax1.set_title("Monthly Cost Savings")
151
+ ax1.set_ylabel("Ksh")
152
+ st.pyplot(fig1)
153
+
154
+ with st.expander("💵 Savings Analysis"):
155
+ st.markdown(f"""
156
+ **Key Observations:**
157
+ - Savings calculated as: `(Total Consumption × Grid Price) - (Grid Purchased × Grid Price)`
158
+ - Current Grid Price: Ksh {st.session_state.grid_price}/kWh
159
+ - Maximum potential savings limited by solar production capacity
160
+ """)
161
+ st.table(df[['Scenario', 'Consumption', 'Production', 'monthly_savings']])
162
+
163
+ # Metric 2: Payback Period
164
+ fig2, ax2 = plt.subplots(figsize=(10, 4))
165
+ sns.barplot(data=df, x='Scenario', y='payback_period', ax=ax2)
166
+ ax2.set_title("System Payback Period")
167
+ ax2.set_ylabel("Years")
168
+ st.pyplot(fig2)
169
+
170
+ with st.expander("⏳ Payback Explanation"):
171
+ st.markdown(f"""
172
+ **Calculation Methodology:**
173
+ - Total Investment: (Panels × {st.session_state.panel_price:,}Ksh) + (Batteries × {st.session_state.battery_price:,}Ksh)
174
+ - Annual Savings: Monthly Savings × 12
175
+ - Payback Period = Total Investment / Annual Savings
176
+ """)
177
+ st.table(df[['Scenario', 'payback_period']])
178
+
179
+ # Consumption Breakdown
180
+ st.header("Detailed Consumption Analysis")
181
+ scenario_select = st.selectbox("Select Scenario", list(scenarios.keys()))
182
+
183
+ selected_params = scenarios[scenario_select]
184
+ breakdown_df = create_consumption_breakdown(
185
+ selected_params["1br"],
186
+ selected_params["2br"],
187
+ selected_params["common"]
188
+ )
189
+
190
+ col1, col2 = st.columns(2)
191
  with col1:
192
+ st.subheader("Energy Composition")
193
+ fig3, ax3 = plt.subplots()
194
+ breakdown_df.plot.pie(y='kWh', ax=ax3, autopct='%1.1f%%')
195
+ st.pyplot(fig3)
196
+
197
  with col2:
198
+ st.subheader("Component Breakdown")
199
+ st.table(breakdown_df)
200
+
201
+ analysis_text = f"""
202
+ **Key Insights for {scenario_select}:**
203
+ - Lighting contributes {breakdown_df.loc['Lighting', 'kWh']/breakdown_df.sum().values[0]*100:.1f}% of total consumption
204
+ - Common areas account for {breakdown_df.loc['Common Areas', 'kWh']:.0f}kWh monthly
205
+ - 2BR units dominate appliance usage at {selected_params['2br']*100}% occupancy
206
+ """
207
+ st.markdown(analysis_text)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
208
 
209
  if __name__ == "__main__":
210
  main()