rishabh5752 commited on
Commit
d12c425
Β·
verified Β·
1 Parent(s): 1e4a033

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +144 -38
src/streamlit_app.py CHANGED
@@ -1,40 +1,146 @@
1
- import altair as alt
2
- import numpy as np
3
- import pandas as pd
4
  import streamlit as st
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
- """
7
- # Welcome to Streamlit!
8
-
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
-
13
- In the meantime, below is an example of what you can do with just a few lines of code:
14
- """
15
-
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
1
+ import os
2
+ import math
 
3
  import streamlit as st
4
+ import pandas as pd
5
+ from pathlib import Path
6
+ from dotenv import load_dotenv
7
+
8
+ load_dotenv()
9
+
10
+ st.set_page_config(page_title="GridCoin Calculator", layout="wide", page_icon="⚑")
11
+
12
+
13
+ st.markdown("""
14
+ <style>
15
+ .stNumberInput, .stSelectbox, .stSlider {
16
+ margin-bottom: -1rem;
17
+ }
18
+ div[data-testid="stVerticalBlock"] > div {
19
+ gap: 0.5rem;
20
+ }
21
+ .stSubheader {
22
+ margin-top: 0.5rem;
23
+ margin-bottom: 0.5rem;
24
+ }
25
+ </style>
26
+ """, unsafe_allow_html=True)
27
+
28
+ st.title("GridCoin β€” DER vs Non-DER Dashboard")
29
+
30
+
31
+ left_col, right_col = st.columns([1, 1])
32
+
33
+
34
+ with left_col:
35
+ st.subheader("βš™οΈ System Configuration")
36
+
37
+ c1, c2 = st.columns(2)
38
+ with c1:
39
+ owner = st.selectbox("Household type", ["DER Homeowner", "Non-DER Homeowner"])
40
+ pv_kw = st.number_input("Solar size (kW)", 0.0, 50.0, 7.0, step=0.5)
41
+ batt_kwh = st.number_input("Battery (kWh)", 0.0, 40.0, 0.0, step=0.5)
42
+ with c2:
43
+ annual_load = st.number_input("Annual use (kWh)", 500, 30000, 12000)
44
+ retail_rate = st.number_input("Retail rate ($/kWh)", 0.01, 1.0, 0.13, format="%.3f")
45
+ upfront = st.number_input("System cost ($)", 0, 100000, 25000)
46
+
47
+ st.markdown("---")
48
+ st.subheader("πŸ’° Policy & GridCoin Settings")
49
+
50
+ c3, c4 = st.columns(2)
51
+ with c3:
52
+ exp_actual = st.number_input("Export credit ($/kWh)", 0.0, 1.0, 0.075, format="%.3f")
53
+ exp_evc = st.number_input("EVC Bonus ($/kWh)", 0.0, 1.0, 0.123, format="%.3f")
54
+ grid_fee = st.number_input("Grid fee ($/kW-mo)", 0.0, 20.0, 4.0)
55
+ with c4:
56
+ peak_start = st.number_input("Peak start (hr)", 0, 23, 17)
57
+ peak_end = st.number_input("Peak end (hr)", 0, 23, 21)
58
+ coin_value = st.number_input("GridCoin value ($)", 0.0, 1.0, 0.05, format="%.3f")
59
+
60
+ st.markdown("---")
61
+ st.subheader("πŸ”‹ GridCoin Calculator")
62
+ c5, c6 = st.columns(2)
63
+ with c5:
64
+ peak_fraction = st.slider("Peak export share", 0.0, 1.0, 0.30, 0.05)
65
+ with c6:
66
+ dr_kwh = st.number_input("DR savings (kWh)", 0.0, 5000.0, 0.0)
67
+
68
+
69
+ pv_yield = pv_kw * 1250
70
+ self_use_ratio = 0.65
71
+ self_used = pv_yield * self_use_ratio
72
+ exports = max(0, pv_yield - self_used - batt_kwh * 200)
73
+
74
+ peak_exports = exports * peak_fraction
75
+ coins_peak = peak_exports * 1.0
76
+ coins_dr = dr_kwh * 0.5
77
+ coins_total = coins_peak + coins_dr
78
+ coin_dollars = coins_total * coin_value
79
+
80
+ bill_nonder = annual_load * retail_rate
81
+ imports_der = max(0, annual_load - self_used)
82
+ exp_credit = exports * exp_actual
83
+ fee_der = grid_fee * pv_kw * 12
84
+ bill_der = imports_der * retail_rate + fee_der - exp_credit - coin_dollars
85
+
86
+ savings = max(0, bill_nonder - bill_der)
87
+ payback = upfront / savings if savings > 0 else math.inf
88
+
89
+
90
+ with right_col:
91
+ st.subheader("πŸ“Š Results Dashboard")
92
+
93
+
94
+ m1, m2 = st.columns(2)
95
+ m1.metric("GridCoins Earned", f"{coins_total:,.1f}")
96
+ m2.metric("GridCoin Value", f"${coin_dollars:,.2f}")
97
+
98
+ st.markdown("---")
99
+
100
+
101
+ st.markdown("### πŸ’΅ Annual Bill Comparison")
102
+ b1, b2, b3 = st.columns(3)
103
+ b1.metric("Non-DER Bill", f"${bill_nonder:,.0f}")
104
+ b2.metric("DER Bill", f"${bill_der:,.0f}", delta=f"${bill_der-bill_nonder:,.0f}")
105
+ b3.metric("Payback Period", f"{payback:.1f} yrs" if payback != math.inf else "N/A")
106
+
107
+ st.markdown("---")
108
+
109
+
110
+ st.markdown("### πŸ€– AI Analysis")
111
+
112
+ if os.getenv("OPENAI_API_KEY"):
113
+ try:
114
+ import openai
115
+ openai.api_key = os.getenv("OPENAI_API_KEY")
116
+
117
+ prompt = (
118
+ f"Write a clear, well-formatted summary in 2-3 complete sentences with proper spacing:\n\n"
119
+ f"A {owner} with a {pv_kw} kW solar system and {batt_kwh} kWh battery "
120
+ f"earns {coins_total:.1f} GridCoins valued at ${coin_dollars:.2f} annually. "
121
+ f"Their annual electricity bill is ${bill_der:,.0f} (DER) compared to ${bill_nonder:,.0f} (Non-DER), "
122
+ f"with a payback period of {payback:.1f} years.\n\n"
123
+ "Explain the financial benefits clearly and concisely. Use proper sentence structure with spaces between words."
124
+ )
125
+
126
+ with st.spinner("Generating..."):
127
+ resp = openai.ChatCompletion.create(
128
+ model="gpt-4o",
129
+ messages=[
130
+ {"role": "system", "content": "You are a helpful energy policy assistant. Provide clear, concise summaries."},
131
+ {"role": "user", "content": prompt}
132
+ ],
133
+ max_tokens=150,
134
+ temperature=0.7
135
+ )
136
+ summary = resp.choices[0].message.content.strip()
137
 
138
+ st.markdown(f"""
139
+ <div style="background-color: #e8f4f8; padding: 1rem; border-radius: 0.5rem; border-left: 4px solid #0068c9;">
140
+ <p style="margin: 0; line-height: 1.6; color: #0c0c0c;">{summary}</p>
141
+ </div>
142
+ """, unsafe_allow_html=True)
143
+ except Exception as e:
144
+ st.warning(f"AI summary unavailable: {e}")
145
+ else:
146
+ st.caption("Set OPENAI_API_KEY to enable AI summaries.")