Drop files here
or click to upload
import pandas as pd
import matplotlib.pyplot as plt
import math
import streamlit as st
from io import StringIO
# ==============================================
# Streamlit Dashboard for Thermal Storage Model
# ==============================================
st.title("Thermal Storage Simulation Dashboard")
st.sidebar.header("⚙️ Configuration")
# File uploader for CSV
uploaded_file = st.sidebar.file_uploader("Upload Input CSV", type="csv")
# Option to generate sample data
if st.sidebar.button("Generate Sample Input Data"):
import numpy as np
n = 8760
hours = range(n)
load = np.random.uniform(100, 400, n)
cop = np.random.uniform(2.5, 4.0, n)
temp = np.random.uniform(-5, 25, n)
df_sample = pd.DataFrame({"Load_kW": load, "COP": cop, "Outdoor_Temp_C": temp})
df_sample.to_csv("sample_input.csv", index=False)
uploaded_file = "sample_input.csv"
st.sidebar.success("Sample input generated and loaded.")
if uploaded_file is not None:
if isinstance(uploaded_file, str):
load_df = pd.read_csv(uploaded_file)
else:
load_df = pd.read_csv(uploaded_file)
st.subheader("📊 Input Data Preview")
st.write(load_df.head())
# Sidebar parameters
STORAGE_VOLUME_L = st.sidebar.number_input("Storage Volume (L)", 100, 100000, 1000)
STORAGE_DT = st.sidebar.number_input("ΔT for Storage (K)", 1, 50, 10)
PLANT_CAPACITY_KW = st.sidebar.number_input("Plant Capacity (kW)", 10, 2000, 500)
OFFPEAK_THRESHOLD = st.sidebar.slider("Off-peak Threshold (% of avg peak)", 0.1, 1.0, 0.3)
AVGPEAK_TOPFRACTION = st.sidebar.slider("Top fraction for avg peak load", 0.01, 0.2, 0.05)
START_FULL = st.sidebar.checkbox("Start with full storage", True)
# Physical constants
WATER_CP = 4.186
RHO_WATER = 1.0
# Pre-calc Storage Capacity (kWh)
storage_capacity_kWh = STORAGE_VOLUME_L * RHO_WATER * WATER_CP * STORAGE_DT / 3600.0
soc = storage_capacity_kWh if START_FULL else 0.0
# Compute average-peak load baseline
n = len(load_df)
top_count = max(1, int(math.ceil(n * AVGPEAK_TOPFRACTION)))
avg_peak_load = load_df["Load_kW"].nlargest(top_count).mean()
offpeak_cutoff = OFFPEAK_THRESHOLD * avg_peak_load
rows = []
for idx, row in load_df.iterrows():
load = float(row['Load_kW'])
cop_in = float(row['COP'])
tout = float(row['Outdoor_Temp_C'])
plant_to_load = 0.0
storage_out = 0.0
storage_in = 0.0
discharge_possible = min(load, soc)
storage_out = discharge_possible
soc -= discharge_possible
load_remaining = load - discharge_possible
if load_remaining > 0:
plant_to_load = min(load_remaining, PLANT_CAPACITY_KW)
load_remaining -= plant_to_load
spare_capacity = max(0.0, PLANT_CAPACITY_KW - plant_to_load)
is_offpeak = load < offpeak_cutoff
if spare_capacity > 0 and is_offpeak:
charge_possible = min(spare_capacity, (storage_capacity_kWh - soc))
soc += charge_possible
storage_in = charge_possible
plant_thermal_kW = plant_to_load + storage_in
cop_result = max(0.1, cop_in)
plant_elec_kW = plant_thermal_kW / max(0.1, cop_result)
rows.append({
"Load_kW": load,
"COP_Input": cop_in,
"Outdoor_Temp_C": tout,
"Plant_Load_kW": plant_to_load,
"Storage_Discharge_kW": storage_out,
"Storage_Charge_kW": storage_in,
"Plant_Thermal_kW": plant_thermal_kW,
"Plant_Elec_kW": plant_elec_kW,
"COP_Resulting": cop_result,
"SOC_kWh": soc,
"Unserved_Load_kW": max(0.0, load_remaining),
"Is_Offpeak": bool(is_offpeak)
})
out_df = pd.DataFrame(rows)
st.subheader("📈 Simulation Results Preview")
st.write(out_df.head())
# Download buttons
csv = out_df.to_csv(index=False).encode('utf-8')
st.download_button("📥 Download Full Results CSV", data=csv, file_name="thermal_storage_results.csv", mime="text/csv")
else:
st.info("Please upload an input CSV or generate sample data from the sidebar.")
Hi! I can help you with any questions about Streamlit and Python. What would you like to know?