GridCoin / app.py
rishabh5752's picture
Create app.py
5f9bd22 verified
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)