GridCoin / app.py
rishabh5752's picture
Create app.py
5f9bd22 verified
Raw
History Blame Contribute Delete
9.84 kB
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)