Spaces:
Sleeping
Sleeping
File size: 5,205 Bytes
1e4a033 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
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.") |