rishabh5752's picture
Upload app.py
1e4a033 verified
import os
import math
import streamlit as st
import pandas as pd
from pathlib import Path
from dotenv import load_dotenv
load_dotenv()
st.set_page_config(page_title="GridCoin Calculator", layout="wide", page_icon="⚑")
st.markdown("""
<style>
.stNumberInput, .stSelectbox, .stSlider {
margin-bottom: -1rem;
}
div[data-testid="stVerticalBlock"] > div {
gap: 0.5rem;
}
.stSubheader {
margin-top: 0.5rem;
margin-bottom: 0.5rem;
}
</style>
""", unsafe_allow_html=True)
st.title("GridCoin β€” DER vs Non-DER Dashboard")
left_col, right_col = st.columns([1, 1])
with left_col:
st.subheader("βš™οΈ System Configuration")
c1, c2 = st.columns(2)
with c1:
owner = st.selectbox("Household type", ["DER Homeowner", "Non-DER Homeowner"])
pv_kw = st.number_input("Solar size (kW)", 0.0, 50.0, 7.0, step=0.5)
batt_kwh = st.number_input("Battery (kWh)", 0.0, 40.0, 0.0, step=0.5)
with c2:
annual_load = st.number_input("Annual use (kWh)", 500, 30000, 12000)
retail_rate = st.number_input("Retail rate ($/kWh)", 0.01, 1.0, 0.13, format="%.3f")
upfront = st.number_input("System cost ($)", 0, 100000, 25000)
st.markdown("---")
st.subheader("πŸ’° Policy & GridCoin Settings")
c3, c4 = st.columns(2)
with c3:
exp_actual = st.number_input("Export credit ($/kWh)", 0.0, 1.0, 0.075, format="%.3f")
exp_evc = st.number_input("EVC Bonus ($/kWh)", 0.0, 1.0, 0.123, format="%.3f")
grid_fee = st.number_input("Grid fee ($/kW-mo)", 0.0, 20.0, 4.0)
with c4:
peak_start = st.number_input("Peak start (hr)", 0, 23, 17)
peak_end = st.number_input("Peak end (hr)", 0, 23, 21)
coin_value = st.number_input("GridCoin value ($)", 0.0, 1.0, 0.05, format="%.3f")
st.markdown("---")
st.subheader("πŸ”‹ GridCoin Calculator")
c5, c6 = st.columns(2)
with c5:
peak_fraction = st.slider("Peak export share", 0.0, 1.0, 0.30, 0.05)
with c6:
dr_kwh = st.number_input("DR savings (kWh)", 0.0, 5000.0, 0.0)
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
with right_col:
st.subheader("πŸ“Š Results Dashboard")
m1, m2 = st.columns(2)
m1.metric("GridCoins Earned", f"{coins_total:,.1f}")
m2.metric("GridCoin Value", f"${coin_dollars:,.2f}")
st.markdown("---")
st.markdown("### πŸ’΅ Annual Bill Comparison")
b1, b2, b3 = st.columns(3)
b1.metric("Non-DER Bill", f"${bill_nonder:,.0f}")
b2.metric("DER Bill", f"${bill_der:,.0f}", delta=f"${bill_der-bill_nonder:,.0f}")
b3.metric("Payback Period", f"{payback:.1f} yrs" if payback != math.inf else "N/A")
st.markdown("---")
st.markdown("### πŸ€– AI Analysis")
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."
)
with st.spinner("Generating..."):
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
)
summary = resp.choices[0].message.content.strip()
st.markdown(f"""
<div style="background-color: #e8f4f8; padding: 1rem; border-radius: 0.5rem; border-left: 4px solid #0068c9;">
<p style="margin: 0; line-height: 1.6; color: #0c0c0c;">{summary}</p>
</div>
""", unsafe_allow_html=True)
except Exception as e:
st.warning(f"AI summary unavailable: {e}")
else:
st.caption("Set OPENAI_API_KEY to enable AI summaries.")