Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import pandas as pd | |
| import numpy as np | |
| import math | |
| import matplotlib.pyplot as plt | |
| import numpy_financial as npf | |
| from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer | |
| from reportlab.lib.pagesizes import A4 | |
| from reportlab.lib.styles import getSampleStyleSheet | |
| import io | |
| import xlsxwriter | |
| # =============================== | |
| # PAGE CONFIG | |
| # =============================== | |
| st.set_page_config(layout="wide") | |
| st.title("🇵🇰 Pakistan Solar Engineering & Financial Dashboard") | |
| # =============================== | |
| # CONFIGURATION | |
| # =============================== | |
| SYSTEM_LOSSES = 0.20 | |
| PANEL_COST_PER_WATT = 55 | |
| INSTALLATION_COST_PER_WATT = 35 | |
| LITHIUM_BATTERY_COST_5KWH = 95000 | |
| CITY_SUNLIGHT = { | |
| "Karachi": 6.2, | |
| "Lahore": 5.5, | |
| "Islamabad": 5.2, | |
| "Peshawar": 5.6, | |
| "Quetta": 6.5, | |
| } | |
| # Appliance Database | |
| APPLIANCES_RESIDENTIAL = { | |
| "LED Bulb (12W)": 12, | |
| "Fan (80W)": 80, | |
| "Refrigerator (200W)": 200, | |
| "LED TV (150W)": 150, | |
| "Air Conditioner 1.5 Ton (1500W)": 1500, | |
| "Washing Machine (500W)": 500, | |
| "Water Pump (750W)": 750, | |
| "Laptop (65W)": 65, | |
| "Iron (1000W)": 1000, | |
| } | |
| APPLIANCES_COMMERCIAL = { | |
| "CNC Machine (2kW)": 2000, | |
| "Industrial AC (5kW)": 5000, | |
| "Lighting System (1kW)": 1000, | |
| "Water Pump 3HP (2.2kW)": 2200, | |
| "Server Rack (1.5kW)": 1500, | |
| } | |
| # Demo Load Profiles | |
| DEMO_SELECTIONS = { | |
| "Homeowner": ["LED Bulb (12W)", "Fan (80W)", "Refrigerator (200W)", "LED TV (150W)"], | |
| "Solar Company": ["LED Bulb (12W)", "Fan (80W)", "CNC Machine (2kW)", "Industrial AC (5kW)"], | |
| "Industrial Investor": ["CNC Machine (2kW)", "Industrial AC (5kW)", "Water Pump 3HP (2.2kW)", "Server Rack (1.5kW)"] | |
| } | |
| # Tariffs | |
| RESIDENTIAL_TARIFF = [ | |
| (100, 22), | |
| (100, 32), | |
| (100, 38), | |
| (100, 42), | |
| (100, 48), | |
| (np.inf, 65), | |
| ] | |
| COMMERCIAL_TARIFF = 72 | |
| # =============================== | |
| # CALCULATION FUNCTIONS | |
| # =============================== | |
| def calculate_residential_bill(units): | |
| remaining = units | |
| bill = 0 | |
| for slab_units, rate in RESIDENTIAL_TARIFF: | |
| if remaining > slab_units: | |
| bill += slab_units * rate | |
| remaining -= slab_units | |
| else: | |
| bill += remaining * rate | |
| break | |
| return bill | |
| def calculate_commercial_bill(units): | |
| return units * COMMERCIAL_TARIFF | |
| def calculate_system(load_watts, hours, sunlight): | |
| daily_kwh = (load_watts * hours) / 1000 | |
| adjusted_kwh = daily_kwh / (1 - SYSTEM_LOSSES) | |
| required_kw = adjusted_kwh / sunlight | |
| return daily_kwh, round(required_kw, 2) | |
| def calculate_battery(daily_kwh, backup_hours): | |
| backup_kwh = (daily_kwh / 24) * backup_hours | |
| return math.ceil(backup_kwh / 5) | |
| def calculate_cost(system_kw, batteries, system_type): | |
| base_cost = system_kw * 1000 * (PANEL_COST_PER_WATT + INSTALLATION_COST_PER_WATT) | |
| battery_cost = batteries * LITHIUM_BATTERY_COST_5KWH | |
| if system_type == "On-Grid": | |
| return base_cost | |
| elif system_type == "Off-Grid": | |
| return base_cost + battery_cost | |
| else: | |
| return base_cost * 1.1 + battery_cost | |
| def emi_calculator(principal, annual_rate, years): | |
| r = annual_rate / 100 / 12 | |
| n = years * 12 | |
| return round(principal * r * (1 + r)**n / ((1 + r)**n - 1)) | |
| def financial_projection(total_cost, daily_kwh, mode, years=25): | |
| monthly_units = daily_kwh * 30 | |
| cashflows = [] | |
| for year in range(1, years + 1): | |
| price_increase = (1 + 0.07) ** (year - 1) | |
| if mode == "Homeowner": | |
| monthly_bill = calculate_residential_bill(monthly_units * price_increase) | |
| else: | |
| monthly_bill = calculate_commercial_bill(monthly_units * price_increase) | |
| cashflows.append(monthly_bill * 12) | |
| npv = npf.npv(0.05, [-total_cost] + cashflows) | |
| irr = npf.irr([-total_cost] + cashflows) | |
| payback_year = next((i for i, cf in enumerate(np.cumsum(cashflows), 1) if cf >= total_cost), None) | |
| return cashflows, round(npv, 2), round(irr * 100, 2), payback_year, np.cumsum(cashflows) | |
| # =============================== | |
| # PDF REPORT | |
| # =============================== | |
| def generate_pdf(report_data): | |
| file_path = "solar_report.pdf" | |
| doc = SimpleDocTemplate(file_path, pagesize=A4) | |
| elements = [] | |
| styles = getSampleStyleSheet() | |
| elements.append(Paragraph("Pakistan Solar Feasibility Report", styles['Title'])) | |
| elements.append(Spacer(1, 12)) | |
| for k, v in report_data.items(): | |
| elements.append(Paragraph(f"{k}: {v}", styles['Normal'])) | |
| elements.append(Spacer(1, 6)) | |
| doc.build(elements) | |
| return file_path | |
| # =============================== | |
| # STREAMLIT UI | |
| # =============================== | |
| audience = st.selectbox("Select Audience", ["Homeowner", "Solar Company", "Industrial Investor"]) | |
| city = st.selectbox("Select City", list(CITY_SUNLIGHT.keys())) | |
| sunlight = CITY_SUNLIGHT[city] | |
| # Demo Settings Buttons | |
| if st.button("🎯 Load Demo Appliances"): | |
| if audience in DEMO_SELECTIONS: | |
| st.session_state["demo_appliances"] = DEMO_SELECTIONS[audience] | |
| st.success("Demo appliances loaded!") | |
| if "demo_appliances" in st.session_state: | |
| default_selection = st.session_state["demo_appliances"] | |
| else: | |
| default_selection = [] | |
| # Appliance Selection | |
| if audience == "Homeowner": | |
| appliances = st.multiselect("Select Appliances", list(APPLIANCES_RESIDENTIAL.keys()), | |
| default=default_selection) | |
| elif audience == "Solar Company": | |
| appliances = st.multiselect("Select Appliances", | |
| list(APPLIANCES_RESIDENTIAL.keys()) + list(APPLIANCES_COMMERCIAL.keys()), | |
| default=default_selection) | |
| else: | |
| appliances = st.multiselect("Select Industrial Equipment", | |
| list(APPLIANCES_COMMERCIAL.keys()), | |
| default=default_selection) | |
| # Demo Settings Button | |
| if st.button("🎯 Load Demo City & Settings"): | |
| city = "Karachi" | |
| sunlight = CITY_SUNLIGHT[city] | |
| hours = 8 | |
| backup_hours = 4 | |
| st.success("Demo settings loaded!") | |
| hours = st.slider("Usage Hours per Day", 1, 24, 8) | |
| system_type = st.radio("System Type", ["On-Grid", "Off-Grid", "Hybrid"]) | |
| backup_hours = st.slider("Battery Backup Hours", 0, 24, 4) | |
| # Calculation Trigger | |
| if st.button("⚡ Calculate Solar System"): | |
| if not appliances: | |
| st.error("Please select appliances") | |
| else: | |
| total_load = sum(APPLIANCES_RESIDENTIAL.get(a, 0) + APPLIANCES_COMMERCIAL.get(a, 0) for a in appliances) | |
| daily_kwh, system_kw = calculate_system(total_load, hours, sunlight) | |
| batteries = calculate_battery(daily_kwh, backup_hours) | |
| total_cost = calculate_cost(system_kw, batteries, system_type) | |
| interest = st.slider("Bank Interest Rate (%)", 5, 25, 15) | |
| years_loan = st.slider("Loan Duration (Years)", 1, 10, 5) | |
| emi = emi_calculator(total_cost, interest, years_loan) | |
| cashflows, npv, irr, payback_year, cumulative_savings = financial_projection(total_cost, daily_kwh, audience) | |
| # Results | |
| st.subheader("System Results") | |
| st.write(f"Total Load: {total_load} W") | |
| st.write(f"Daily Energy: {round(daily_kwh,2)} kWh") | |
| st.write(f"System Size: {system_kw} kW") | |
| st.write(f"Battery Units: {batteries}") | |
| st.write(f"Total Cost: PKR {round(total_cost):,}") | |
| st.write(f"EMI: PKR {emi:,}") | |
| st.write(f"NPV: PKR {npv:,}") | |
| st.write(f"IRR: {irr}%") | |
| st.write(f"Payback Year: {payback_year}") | |
| # Charts | |
| st.subheader("Savings Growth") | |
| plt.figure(figsize=(10,4)) | |
| plt.plot(range(1,26), cumulative_savings) | |
| plt.axhline(total_cost, linestyle="--") | |
| st.pyplot(plt) | |
| # PDF + Excel | |
| report_data = { | |
| "Audience": audience, | |
| "City": city, | |
| "System Size (kW)": system_kw, | |
| "Total Cost": total_cost, | |
| "IRR": irr, | |
| "NPV": npv, | |
| "Payback Year": payback_year | |
| } | |
| pdf_file = generate_pdf(report_data) | |
| with open(pdf_file, "rb") as f: | |
| st.download_button("Download PDF Report", f, file_name="Solar_Report.pdf") |