Spaces:
Runtime error
Runtime error
| import os | |
| import math | |
| import gradio as gr | |
| import pandas as pd | |
| from pathlib import Path | |
| from dotenv import load_dotenv | |
| load_dotenv() | |
| def calculate_gridcoin( | |
| owner, | |
| pv_kw, | |
| batt_kwh, | |
| annual_load, | |
| retail_rate, | |
| upfront, | |
| exp_actual, | |
| exp_evc, | |
| grid_fee, | |
| peak_start, | |
| peak_end, | |
| coin_value, | |
| peak_fraction, | |
| dr_kwh | |
| ): | |
| """Calculate GridCoin metrics and generate results.""" | |
| # Core calculations | |
| pv_yield = pv_kw * 1250 | |
| self_use_ratio = 0.65 | |
| self_used = pv_yield * self_use_ratio | |
| exports = max(0, pv_yield - self_used - batt_kwh * 200) | |
| peak_exports = exports * peak_fraction | |
| coins_peak = peak_exports * 1.0 | |
| coins_dr = dr_kwh * 0.5 | |
| coins_total = coins_peak + coins_dr | |
| coin_dollars = coins_total * coin_value | |
| bill_nonder = annual_load * retail_rate | |
| imports_der = max(0, annual_load - self_used) | |
| exp_credit = exports * exp_actual | |
| fee_der = grid_fee * pv_kw * 12 | |
| bill_der = imports_der * retail_rate + fee_der - exp_credit - coin_dollars | |
| savings = max(0, bill_nonder - bill_der) | |
| payback = upfront / savings if savings > 0 else math.inf | |
| # Format results | |
| gridcoins_earned = f"{coins_total:,.1f}" | |
| gridcoin_value = f"${coin_dollars:,.2f}" | |
| non_der_bill = f"${bill_nonder:,.0f}" | |
| der_bill = f"${bill_der:,.0f}" | |
| bill_savings = f"${bill_der - bill_nonder:,.0f}" | |
| payback_period = f"{payback:.1f} yrs" if payback != math.inf else "N/A" | |
| # AI Analysis | |
| ai_summary = "" | |
| if os.getenv("OPENAI_API_KEY"): | |
| try: | |
| import openai | |
| openai.api_key = os.getenv("OPENAI_API_KEY") | |
| prompt = ( | |
| f"Write a clear, well-formatted summary in 2-3 complete sentences with proper spacing:\n\n" | |
| f"A {owner} with a {pv_kw} kW solar system and {batt_kwh} kWh battery " | |
| f"earns {coins_total:.1f} GridCoins valued at ${coin_dollars:.2f} annually. " | |
| f"Their annual electricity bill is ${bill_der:,.0f} (DER) compared to ${bill_nonder:,.0f} (Non-DER), " | |
| f"with a payback period of {payback:.1f} years.\n\n" | |
| "Explain the financial benefits clearly and concisely. Use proper sentence structure with spaces between words." | |
| ) | |
| resp = openai.ChatCompletion.create( | |
| model="gpt-4o", | |
| messages=[ | |
| {"role": "system", "content": "You are a helpful energy policy assistant. Provide clear, concise summaries."}, | |
| {"role": "user", "content": prompt} | |
| ], | |
| max_tokens=150, | |
| temperature=0.7 | |
| ) | |
| ai_summary = resp.choices[0].message.content.strip() | |
| except Exception as e: | |
| ai_summary = f"AI summary unavailable: {e}" | |
| else: | |
| ai_summary = "Set OPENAI_API_KEY to enable AI summaries." | |
| return ( | |
| gridcoins_earned, | |
| gridcoin_value, | |
| non_der_bill, | |
| der_bill, | |
| bill_savings, | |
| payback_period, | |
| ai_summary | |
| ) | |
| # Custom CSS for better styling | |
| custom_css = """ | |
| .gradio-container { | |
| font-family: 'Inter', sans-serif; | |
| } | |
| .output-text { | |
| font-size: 1.2rem; | |
| font-weight: 600; | |
| } | |
| """ | |
| # Create Gradio interface | |
| with gr.Blocks(css=custom_css, title="GridCoin Calculator", theme=gr.themes.Soft()) as demo: | |
| gr.Markdown("# β‘ GridCoin β DER vs Non-DER Dashboard") | |
| with gr.Row(): | |
| # Left column - Inputs | |
| with gr.Column(scale=1): | |
| gr.Markdown("### βοΈ System Configuration") | |
| with gr.Row(): | |
| with gr.Column(): | |
| owner = gr.Dropdown( | |
| choices=["DER Homeowner", "Non-DER Homeowner"], | |
| value="DER Homeowner", | |
| label="Household type" | |
| ) | |
| pv_kw = gr.Number( | |
| value=7.0, | |
| minimum=0.0, | |
| maximum=50.0, | |
| step=0.5, | |
| label="Solar size (kW)" | |
| ) | |
| batt_kwh = gr.Number( | |
| value=0.0, | |
| minimum=0.0, | |
| maximum=40.0, | |
| step=0.5, | |
| label="Battery (kWh)" | |
| ) | |
| with gr.Column(): | |
| annual_load = gr.Number( | |
| value=12000, | |
| minimum=500, | |
| maximum=30000, | |
| label="Annual use (kWh)" | |
| ) | |
| retail_rate = gr.Number( | |
| value=0.13, | |
| minimum=0.01, | |
| maximum=1.0, | |
| step=0.001, | |
| label="Retail rate ($/kWh)" | |
| ) | |
| upfront = gr.Number( | |
| value=25000, | |
| minimum=0, | |
| maximum=100000, | |
| label="System cost ($)" | |
| ) | |
| gr.Markdown("---") | |
| gr.Markdown("### π° Policy & GridCoin Settings") | |
| with gr.Row(): | |
| with gr.Column(): | |
| exp_actual = gr.Number( | |
| value=0.075, | |
| minimum=0.0, | |
| maximum=1.0, | |
| step=0.001, | |
| label="Export credit ($/kWh)" | |
| ) | |
| exp_evc = gr.Number( | |
| value=0.123, | |
| minimum=0.0, | |
| maximum=1.0, | |
| step=0.001, | |
| label="EVC Bonus ($/kWh)" | |
| ) | |
| grid_fee = gr.Number( | |
| value=4.0, | |
| minimum=0.0, | |
| maximum=20.0, | |
| label="Grid fee ($/kW-mo)" | |
| ) | |
| with gr.Column(): | |
| peak_start = gr.Number( | |
| value=17, | |
| minimum=0, | |
| maximum=23, | |
| label="Peak start (hr)" | |
| ) | |
| peak_end = gr.Number( | |
| value=21, | |
| minimum=0, | |
| maximum=23, | |
| label="Peak end (hr)" | |
| ) | |
| coin_value = gr.Number( | |
| value=0.05, | |
| minimum=0.0, | |
| maximum=1.0, | |
| step=0.001, | |
| label="GridCoin value ($)" | |
| ) | |
| gr.Markdown("---") | |
| gr.Markdown("### π GridCoin Calculator") | |
| with gr.Row(): | |
| with gr.Column(): | |
| peak_fraction = gr.Slider( | |
| minimum=0.0, | |
| maximum=1.0, | |
| value=0.30, | |
| step=0.05, | |
| label="Peak export share" | |
| ) | |
| with gr.Column(): | |
| dr_kwh = gr.Number( | |
| value=0.0, | |
| minimum=0.0, | |
| maximum=5000.0, | |
| label="DR savings (kWh)" | |
| ) | |
| calculate_btn = gr.Button("Calculate Results", variant="primary", size="lg") | |
| # Right column - Outputs | |
| with gr.Column(scale=1): | |
| gr.Markdown("### π Results Dashboard") | |
| with gr.Row(): | |
| gridcoins_earned = gr.Textbox(label="GridCoins Earned", interactive=False) | |
| gridcoin_value = gr.Textbox(label="GridCoin Value", interactive=False) | |
| gr.Markdown("---") | |
| gr.Markdown("### π΅ Annual Bill Comparison") | |
| with gr.Row(): | |
| non_der_bill = gr.Textbox(label="Non-DER Bill", interactive=False) | |
| der_bill = gr.Textbox(label="DER Bill", interactive=False) | |
| bill_savings = gr.Textbox(label="Savings", interactive=False) | |
| payback_period = gr.Textbox(label="Payback Period", interactive=False) | |
| gr.Markdown("---") | |
| gr.Markdown("### π€ AI Analysis") | |
| ai_summary = gr.Textbox( | |
| label="", | |
| lines=5, | |
| interactive=False, | |
| show_label=False | |
| ) | |
| # Connect the calculate button to the function | |
| calculate_btn.click( | |
| fn=calculate_gridcoin, | |
| inputs=[ | |
| owner, pv_kw, batt_kwh, annual_load, retail_rate, upfront, | |
| exp_actual, exp_evc, grid_fee, peak_start, peak_end, coin_value, | |
| peak_fraction, dr_kwh | |
| ], | |
| outputs=[ | |
| gridcoins_earned, gridcoin_value, non_der_bill, der_bill, | |
| bill_savings, payback_period, ai_summary | |
| ] | |
| ) | |
| # Auto-calculate on load | |
| demo.load( | |
| fn=calculate_gridcoin, | |
| inputs=[ | |
| owner, pv_kw, batt_kwh, annual_load, retail_rate, upfront, | |
| exp_actual, exp_evc, grid_fee, peak_start, peak_end, coin_value, | |
| peak_fraction, dr_kwh | |
| ], | |
| outputs=[ | |
| gridcoins_earned, gridcoin_value, non_der_bill, der_bill, | |
| bill_savings, payback_period, ai_summary | |
| ] | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch(share=False, server_name="127.0.0.1", server_port=7860) | |