charagu-eric commited on
Commit
c857c28
·
1 Parent(s): 4e9f3c5
Files changed (2) hide show
  1. app.py +158 -560
  2. requirements.txt +1 -0
app.py CHANGED
@@ -1,88 +1,49 @@
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": 100,
28
- "batteries": 50,
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,12 +63,12 @@ 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,517 +101,154 @@ def create_consumption_breakdown(
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()
 
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
  # 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
  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()
requirements.txt CHANGED
@@ -6,3 +6,4 @@ matplotlib
6
  numpy
7
  transformers
8
  torch
 
 
6
  numpy
7
  transformers
8
  torch
9
+ gradio