Created
December 10, 2024 17:51
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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