Skip to content

Instantly share code, notes, and snippets.

@chriszero
Created December 10, 2024 17:51
Show Gist options
  • Select an option

  • Save chriszero/8864d1994c1eea1a8d8c9755378a7f01 to your computer and use it in GitHub Desktop.

Select an option

Save chriszero/8864d1994c1eea1a8d8c9755378a7f01 to your computer and use it in GitHub Desktop.
Simuliert Kosten der Netzentgelte nach enwg14a, Verbräuche Stündlich als csv als Export von Zeitreihen aus InfluxDB
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
import matplotlib.pyplot as plt
import streamlit as st
from demandlib import bdew
# Streamlit Web App starten
st.title("Netzentgelte Kosten-Simulator")
st.write("""
Laden Sie Ihre Verbrauchsdaten hoch, um die Einsparungen bei Netzentgelten zu berechnen.
Sie können den Gesamtverbrauch hochladen (obligatorisch) und optional den Wallbox-Verbrauch. Alternativ können Sie Ihren jährlichen Stromverbrauch angeben.
**Format der CSV-Datei:**
- Die Datei muss eine Spalte mit Zeitstempeln (`time`) enthalten.
- Der Gesamtverbrauch muss in der Spalte `kWh.mean_value` angegeben sein.
- Der Wallbox-Verbrauch (optional) muss in der Spalte `chargeTotalImport.mean_value` angegeben sein.
Die Verbrauchswerte sollen kumuliert sein.
""")
# Datei Upload oder Verbrauchseingabe
uploaded_file_gesamtverbrauch = st.file_uploader("Gesamtverbrauch CSV-Datei hochladen (optional)", type="csv")
uploaded_file_wallbox = st.file_uploader("Wallbox Verbrauch CSV-Datei hochladen (optional)", type="csv")
manual_verbrauch = st.number_input("Alternativ: Jährlicher Stromverbrauch in kWh (anstatt CSV hochzuladen)", min_value=0, value=0)
if uploaded_file_gesamtverbrauch is not None or manual_verbrauch > 0:
if uploaded_file_gesamtverbrauch is not None:
# Daten aus CSV-Dateien einlesen
df_gesamtverbrauch = pd.read_csv(uploaded_file_gesamtverbrauch)
df_wallbox = pd.read_csv(uploaded_file_wallbox) if uploaded_file_wallbox is not None else pd.DataFrame()
# Zeitstempel konvertieren
df_gesamtverbrauch['timestamp'] = pd.to_datetime(df_gesamtverbrauch['time'], utc=True)
if not df_wallbox.empty:
df_wallbox['timestamp'] = pd.to_datetime(df_wallbox['time'], utc=True)
# Daten zusammenführen
if not df_wallbox.empty:
df = pd.merge(df_gesamtverbrauch, df_wallbox, on='timestamp', how='left', suffixes=('_gesamt', '_wallbox'))
else:
df = df_gesamtverbrauch.copy()
df['chargeTotalImport.mean_value'] = 0
# Differenzbildung der kumulierten Verbrauchswerte
df['verbrauch_kwh'] = df['kWh.mean_value'].diff().fillna(0)
df['verbrauch_e_auto_kwh'] = df['chargeTotalImport.mean_value'].diff().fillna(0)
# Negative Differenzen (Rücksetzung des kumulierten Wertes) verhindern
df['verbrauch_kwh'] = df['verbrauch_kwh'].clip(lower=0)
df['verbrauch_e_auto_kwh'] = df['verbrauch_e_auto_kwh'].clip(lower=0)
# Korrekte Behandlung des Verbrauchs ohne Wallbox
df['verbrauch_ohne_wallbox_kwh'] = df['verbrauch_kwh'] - df['verbrauch_e_auto_kwh']
df['verbrauch_ohne_wallbox_kwh'] = df['verbrauch_ohne_wallbox_kwh'].clip(lower=0)
else:
# Verwendung des manuell eingegebenen jährlichen Stromverbrauchs mit Lastprofil aus demandlib
st.write("Verwendung des Standardlastprofils zur Verteilung des jährlichen Stromverbrauchs.")
verbrauch_gesamt = manual_verbrauch
# Standardlastprofil mit demandlib erzeugen
ann_el_demand_per_sector = {"household": verbrauch_gesamt}
demand = bdew.ElecSlp2014()
df_slp = demand.get_profile(ann_el_demand_per_sector, datetime(2024, 1, 1), datetime(2024, 12, 31))
df_slp = df_slp.reset_index()
df_slp.rename(columns={0: 'verbrauch_kwh', 'index': 'timestamp'}, inplace=True)
df = df_slp.copy()
df['verbrauch_e_auto_kwh'] = 0 # Kein spezifischer Wallboxverbrauch
df['verbrauch_ohne_wallbox_kwh'] = df['verbrauch_kwh']
# Option, um den Wallbox-Verbrauch zu 100% in den Niedertarif zu schieben
wallbox_in_niedertarif = st.checkbox("Wallbox-Verbrauch zu 100% in den Niedertarif verschieben", value=True)
# Tarifzeiten definieren (vom Benutzer anpassbar)
hochtarif_start = st.number_input("Hochtarif Start (Stunde, 24h Format)", value=17, min_value=0, max_value=23)
hochtarif_end = st.number_input("Hochtarif Ende (Stunde, 24h Format)", value=22, min_value=0, max_value=23)
niedertarif_start = st.number_input("Niedertarif Start (Stunde, 24h Format)", value=10, min_value=0, max_value=23)
niedertarif_end = st.number_input("Niedertarif Ende (Stunde, 24h Format)", value=14, min_value=0, max_value=23)
# Tarifpreise in ct/kWh (vom Benutzer anpassbar)
hochtarif_preis = st.number_input("Hochtarif Preis (ct/kWh)", value=17.09, min_value=0.0)
niedertarif_preis = st.number_input("Niedertarif Preis (ct/kWh)", value=4.63, min_value=0.0)
standard_preis = st.number_input("Standardtarif Preis (ct/kWh)", value=11.58, min_value=0.0)
def get_tarifpreis(hour):
if niedertarif_start <= hour < niedertarif_end:
return niedertarif_preis
elif hochtarif_start <= hour < hochtarif_end:
return hochtarif_preis
else:
return standard_preis
# Verbrauch des E-Autos immer im Niedertarif berechnen, wenn Option ausgewählt
if wallbox_in_niedertarif:
df['kosten_e_auto_ct'] = df['verbrauch_e_auto_kwh'] * niedertarif_preis
else:
df['tarif_wallbox'] = df['timestamp'].dt.hour.apply(lambda x: get_tarifpreis(x))
df['kosten_e_auto_ct'] = df['verbrauch_e_auto_kwh'] * df['tarif_wallbox']
kosten_e_auto_ct = df['kosten_e_auto_ct'].sum()
st.write(f"**Kosten für das Laden des E-Autos:** {kosten_e_auto_ct / 100:.2f} €")
# Berechnung der Grundlastkosten (Nicht verschiebbarer Verbrauch)
df['tarif'] = df['timestamp'].dt.hour.apply(lambda x: get_tarifpreis(x))
df['kosten_grundlast_ct'] = df['verbrauch_ohne_wallbox_kwh'] * df['tarif']
kosten_grundlast_ct = df['kosten_grundlast_ct'].sum()
st.write(f"**Kosten für die Grundlast (nach tatsächlicher Tarifzeit):** {kosten_grundlast_ct / 100:.2f} €")
# Gesamtkosten berechnen
gesamtkosten_ct = kosten_e_auto_ct + kosten_grundlast_ct
st.write(f"**Gesamtkosten für den Zeitraum:** {gesamtkosten_ct / 100:.2f} €")
# Vergleich zu permanentem Standardtarif
kosten_standardtarif_ct = df['verbrauch_kwh'].sum() * standard_preis
st.write(f"**Gesamtkosten bei permanentem Standardtarif:** {kosten_standardtarif_ct / 100:.2f} €")
# Kostendifferenz berechnen
kostendifferenz_ct = kosten_standardtarif_ct - gesamtkosten_ct
st.write(f"**Kostendifferenz im Vergleich zum Standardtarif:** {kostendifferenz_ct / 100:.2f} €")
# Modul 1 Fixe Reduzierung
modul1_reduzierung_eur = 166.84
st.write(f"**Modul 1 Ersparnis:** {modul1_reduzierung_eur:.2f} €")
# Modul 3 basiert auf den drei Tarifzonen (Hochtarif, Niedertarif, Standardtarif)
modul3_ersparnis = (kosten_standardtarif_ct - gesamtkosten_ct) / 100
st.write(f"**Modul 3 Ersparnis:** {modul3_ersparnis:.2f} €")
# Gesamtersparnis durch Modul 1 und Modul 3
gesamtersparnis = modul1_reduzierung_eur + modul3_ersparnis
st.write(f"**Gesamtersparnis durch Modul 1 und Modul 3:** {gesamtersparnis:.2f} €")
# Verbräuche grafisch darstellen
st.write("### Verbrauch über den Zeitraum")
st.line_chart(df.set_index('timestamp')[['verbrauch_kwh', 'verbrauch_e_auto_kwh', 'verbrauch_ohne_wallbox_kwh']])
# Verbrauch nach Tarifzonen aggregieren
verbrauch_hochtarif = df.loc[(df['timestamp'].dt.hour >= hochtarif_start) & (df['timestamp'].dt.hour < hochtarif_end), 'verbrauch_ohne_wallbox_kwh'].sum()
verbrauch_niedertarif = df.loc[(df['timestamp'].dt.hour >= niedertarif_start) & (df['timestamp'].dt.hour < niedertarif_end), 'verbrauch_ohne_wallbox_kwh'].sum()
verbrauch_standardtarif = df['verbrauch_ohne_wallbox_kwh'].sum() - verbrauch_hochtarif - verbrauch_niedertarif
verbrauch_wallbox = df['verbrauch_e_auto_kwh'].sum()
# Da der Wallbox-Verbrauch immer im Niedertarif stattfindet, wird dieser entsprechend zugeordnet
wallbox_niedertarif = verbrauch_wallbox
# Sicherstellen, dass keine negativen Werte für das Kreisdiagramm vorhanden sind
verbrauch_hochtarif = max(verbrauch_hochtarif, 0)
verbrauch_niedertarif = max(verbrauch_niedertarif, 0)
verbrauch_standardtarif = max(verbrauch_standardtarif, 0)
wallbox_niedertarif = max(wallbox_niedertarif, 0)
st.write(f"**Verbrauch im Hochtarif:** {verbrauch_hochtarif:.2f} kWh")
st.write(f"**Verbrauch im Niedertarif (inkl. Wallbox):** {verbrauch_niedertarif + wallbox_niedertarif:.2f} kWh")
st.write(f"**Verbrauch im Standardtarif:** {verbrauch_standardtarif:.2f} kWh")
if verbrauch_wallbox > 0:
st.write(f"**Verbrauch der Wallbox gesamt (im Niedertarif):** {wallbox_niedertarif:.2f} kWh")
# Darstellung der Verteilung des Verbrauchs nach Tarifzonen und Wallbox
labels = ['Hochtarif', 'Niedertarif (inkl. Wallbox)', 'Standardtarif']
values = [verbrauch_hochtarif, verbrauch_niedertarif + wallbox_niedertarif, verbrauch_standardtarif]
# Entferne negative oder Nullwerte für das Kreisdiagramm
labels, values = zip(*[(label, value) for label, value in zip(labels, values) if value > 0])
fig, ax = plt.subplots()
ax.pie(values, labels=labels, autopct=lambda p: f'{p:.1f}% ({p * sum(values) / 100:.1f} kWh)', startangle=140, colors=['red', 'yellow', 'blue'])
ax.set_title('Verteilung des Verbrauchs nach Tarifzonen (inkl. Wallbox)')
st.pyplot(fig)
time chargeTotalImport.mean_value
2024-05-26T00:00:00.000+02:00
2024-05-26T01:00:00.000+02:00
2024-05-26T02:00:00.000+02:00
2024-05-26T03:00:00.000+02:00
2024-05-26T04:00:00.000+02:00
2024-05-26T05:00:00.000+02:00
2024-05-26T06:00:00.000+02:00
2024-05-26T07:00:00.000+02:00
2024-05-26T08:00:00.000+02:00
2024-05-26T09:00:00.000+02:00
2024-05-26T10:00:00.000+02:00
2024-05-26T11:00:00.000+02:00
2024-05-26T12:00:00.000+02:00 3199.7303522222232
2024-05-26T13:00:00.000+02:00 3203.8351522685202
2024-05-26T14:00:00.000+02:00 3215.058834990338
2024-05-26T15:00:00.000+02:00 3221.2124556680515
2024-05-26T16:00:00.000+02:00 3221.4932630000053
2024-05-26T17:00:00.000+02:00 3221.4950875000004
2024-05-26T18:00:00.000+02:00
2024-05-26T19:00:00.000+02:00
2024-05-26T20:00:00.000+02:00 3221.495087500002
2024-05-26T21:00:00.000+02:00 3221.4950875000054
2024-05-26T22:00:00.000+02:00 3221.4950875000054
2024-05-26T23:00:00.000+02:00 3221.4950875000054
2024-05-27T00:00:00.000+02:00 3221.4950875000054
2024-05-27T01:00:00.000+02:00 3221.4950875000054
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment