diff --git a/modules/class_akku.py b/modules/class_akku.py index bad4856..d777235 100644 --- a/modules/class_akku.py +++ b/modules/class_akku.py @@ -1,18 +1,19 @@ import numpy as np class PVAkku: - def __init__(self, kapazitaet_wh): + def __init__(self, kapazitaet_wh, hours): # Kapazität des Akkus in Wh self.kapazitaet_wh = kapazitaet_wh # Initialer Ladezustand des Akkus in Wh self.soc_wh = 0 - self.discharge_array = np.full(24, 1) + self.hours = hours + self.discharge_array = np.full(self.hours, 1) def reset(self): self.soc_wh = 0 - self.discharge_array = np.full(24, 1) + self.discharge_array = np.full(self.hours, 1) def set_discharge_per_hour(self, discharge_array): - assert(len(discharge_array) == 24) + assert(len(discharge_array) == self.hours) self.discharge_array = discharge_array def ladezustand_in_prozent(self): diff --git a/modules/class_ems.py b/modules/class_ems.py index ee74ee8..b3a10de 100644 --- a/modules/class_ems.py +++ b/modules/class_ems.py @@ -1,35 +1,6 @@ -# class EnergieManagementSystem: - # def __init__(self, akku, lastkurve_wh, pv_prognose_wh): - # self.akku = akku - # self.lastkurve_wh = lastkurve_wh - # self.pv_prognose_wh = pv_prognose_wh +from datetime import datetime +from pprint import pprint - # def simuliere(self): - # eigenverbrauch_wh = 0 - # netzeinspeisung_wh = 0 - # netzbezug_wh = 0 - - # for stunde in range(len(self.lastkurve_wh)): - # verbrauch = self.lastkurve_wh[stunde] - # erzeugung = self.pv_prognose_wh[stunde] - - # if erzeugung > verbrauch: - # überschuss = erzeugung - verbrauch - # eigenverbrauch_wh += verbrauch - # geladene_energie = min(überschuss, self.akku.kapazitaet_wh - self.akku.soc_wh) - # self.akku.energie_laden(geladene_energie) - # netzeinspeisung_wh += überschuss - geladene_energie - # else: - # eigenverbrauch_wh += erzeugung - # benötigte_energie = verbrauch - erzeugung - # aus_akku = self.akku.energie_abgeben(benötigte_energie) - # netzbezug_wh += benötigte_energie - aus_akku - - # return { - # 'Eigenverbrauch_Wh': eigenverbrauch_wh, - # 'Netzeinspeisung_Wh': netzeinspeisung_wh, - # 'Netzbezug_Wh': netzbezug_wh - # } class EnergieManagementSystem: @@ -39,12 +10,30 @@ class EnergieManagementSystem: self.pv_prognose_wh = pv_prognose_wh self.strompreis_cent_pro_wh = strompreis_cent_pro_wh # Strompreis in Cent pro Wh self.einspeiseverguetung_cent_pro_wh = einspeiseverguetung_cent_pro_wh # Einspeisevergütung in Cent pro Wh + + # print("\n\nLastprognose:",self.lastkurve_wh.shape) + # print("PV Prognose:",self.pv_prognose_wh.shape) + # print("Preis Prognose:",self.strompreis_cent_pro_wh.shape) + + def set_akku_discharge_hours(self, ds): self.akku.set_discharge_per_hour(ds) def reset(self): self.akku.reset() - + + def simuliere_ab_jetzt(self): + jetzt = datetime.now() + start_stunde = jetzt.hour + # Berechne die Anzahl der Stunden bis zum gleichen Zeitpunkt am nächsten Tag + stunden_bis_ende_tag = 24 - start_stunde + # Füge diese Stunden zum nächsten Tag hinzu + gesamt_stunden = stunden_bis_ende_tag + 24 + + # Beginne die Simulation ab der aktuellen Stunde und führe sie für die berechnete Dauer aus + return self.simuliere(start_stunde) + + def simuliere(self, start_stunde): eigenverbrauch_wh_pro_stunde = [] netzeinspeisung_wh_pro_stunde = [] @@ -53,19 +42,17 @@ class EnergieManagementSystem: einnahmen_euro_pro_stunde = [] akku_soc_pro_stunde = [] - ende = len(self.lastkurve_wh) # Berechnet das Ende basierend auf der Länge der Lastkurve + ende = min( len(self.lastkurve_wh),len(self.pv_prognose_wh), len(self.strompreis_cent_pro_wh)) + #print(ende) + # Berechnet das Ende basierend auf der Länge der Lastkurve for stunde in range(start_stunde, ende): + # Anpassung, um sicherzustellen, dass Indizes korrekt sind verbrauch = self.lastkurve_wh[stunde] erzeugung = self.pv_prognose_wh[stunde] strompreis = self.strompreis_cent_pro_wh[stunde] if stunde < len(self.strompreis_cent_pro_wh) else self.strompreis_cent_pro_wh[-1] - - - # for stunde in range(len(self.lastkurve_wh)): - # verbrauch = self.lastkurve_wh[stunde] - # erzeugung = self.pv_prognose_wh[stunde] - # strompreis = self.strompreis_cent_pro_wh[stunde] - + #print(verbrauch," ",erzeugung," ",strompreis) + stündlicher_netzbezug_wh = 0 stündliche_kosten_euro = 0 stündliche_einnahmen_euro = 0 @@ -92,8 +79,16 @@ class EnergieManagementSystem: # Berechnung der Gesamtbilanzen gesamtkosten_euro = sum(kosten_euro_pro_stunde) - sum(einnahmen_euro_pro_stunde) + expected_length = ende - start_stunde + array_names = ['Eigenverbrauch_Wh_pro_Stunde', 'Netzeinspeisung_Wh_pro_Stunde', 'Netzbezug_Wh_pro_Stunde', 'Kosten_Euro_pro_Stunde', 'akku_soc_pro_stunde', 'Einnahmen_Euro_pro_Stunde'] + all_arrays = [eigenverbrauch_wh_pro_stunde, netzeinspeisung_wh_pro_stunde, netzbezug_wh_pro_stunde, kosten_euro_pro_stunde, akku_soc_pro_stunde, einnahmen_euro_pro_stunde] - return { + inconsistent_arrays = [name for name, arr in zip(array_names, all_arrays) if len(arr) != expected_length] + + if inconsistent_arrays: + raise ValueError(f"Inkonsistente Längen bei den Arrays: {', '.join(inconsistent_arrays)}. Erwartete Länge: {expected_length}, gefunden: {[len(all_arrays[array_names.index(name)]) for name in inconsistent_arrays]}") + + out = { 'Eigenverbrauch_Wh_pro_Stunde': eigenverbrauch_wh_pro_stunde, 'Netzeinspeisung_Wh_pro_Stunde': netzeinspeisung_wh_pro_stunde, 'Netzbezug_Wh_pro_Stunde': netzbezug_wh_pro_stunde, @@ -105,42 +100,5 @@ class EnergieManagementSystem: 'Gesamtkosten_Euro': sum(kosten_euro_pro_stunde) } - - # def simuliere(self): - # eigenverbrauch_wh = 0 - # netzeinspeisung_wh = 0 - # netzbezug_wh = 0 - # kosten_euro = 0 - # einnahmen_euro = 0 - - # for stunde in range(len(self.lastkurve_wh)): - # verbrauch = self.lastkurve_wh[stunde] - # erzeugung = self.pv_prognose_wh[stunde] - # strompreis = self.strompreis_cent_pro_wh[stunde] - - # if erzeugung > verbrauch: - # überschuss = erzeugung - verbrauch - # eigenverbrauch_wh += verbrauch - # geladene_energie = min(überschuss, self.akku.kapazitaet_wh - self.akku.soc_wh) - # self.akku.energie_laden(geladene_energie) - # netzeinspeisung_wh += überschuss - geladene_energie - # einnahmen_euro += (überschuss - geladene_energie) * self.einspeiseverguetung_cent_pro_wh[stunde] / 100 - # else: - # eigenverbrauch_wh += erzeugung - # benötigte_energie = verbrauch - erzeugung - # aus_akku = self.akku.energie_abgeben(benötigte_energie) - # netzbezug_wh += benötigte_energie - aus_akku - # print(strompreis) - - # kosten_euro += (benötigte_energie - aus_akku) * strompreis / 100 - - # gesamtkosten_euro = kosten_euro - einnahmen_euro - - # return { - # 'Eigenverbrauch_Wh': eigenverbrauch_wh, - # 'Netzeinspeisung_Wh': netzeinspeisung_wh, - # 'Netzbezug_Wh': netzbezug_wh, - # 'Kosten_Euro': kosten_euro, - # 'Einnahmen_Euro': einnahmen_euro, - # 'Gesamtkosten_Euro': gesamtkosten_euro - # } \ No newline at end of file + + return out diff --git a/modules/class_load.py b/modules/class_load.py index c025e48..5b34f1c 100644 --- a/modules/class_load.py +++ b/modules/class_load.py @@ -12,22 +12,7 @@ class LoadForecast: self.data_year_energy = None self.year_energy = year_energy self.load_data() - - # def get_prices_for_date(self, query_date): - # query_date = datetime.strptime(query_date, '%Y-%m-%d').date() - # prices_for_date = [price for price in self.price_data if price.starts_at.date() == query_date] - # return prices_for_date - - # def get_price_for_datetime(self, query_datetime): - # query_datetime = datetime.strptime(query_datetime, '%Y-%m-%d %H').replace(minute=0, second=0, microsecond=0) - # query_datetime = query_datetime.replace(tzinfo=timezone(timedelta(hours=1))) - - # for price in self.price_data: - # #print(price.starts_at.replace(minute=0, second=0, microsecond=0) , " ", query_datetime, " == ",price.starts_at.replace(minute=0, second=0, microsecond=0) == query_datetime) - # if price.starts_at.replace(minute=0, second=0, microsecond=0) == query_datetime: - # return price - # return None def get_daily_stats(self, date_str): """ Gibt den 24-Stunden-Verlauf mit Erwartungswert und Standardabweichung für ein gegebenes Datum zurück. @@ -66,6 +51,27 @@ class LoadForecast: return hourly_stats + def get_stats_for_date_range(self, start_date_str, end_date_str): + """ + Gibt die Erwartungswerte und Standardabweichungen für einen Zeitraum zurück. + + :param start_date_str: Startdatum als String im Format "YYYY-MM-DD" + :param end_date_str: Enddatum als String im Format "YYYY-MM-DD" + :return: Ein Array mit den aggregierten Daten für den Zeitraum + """ + start_date = datetime.strptime(start_date_str, "%Y-%m-%d") + end_date = datetime.strptime(end_date_str, "%Y-%m-%d") + + start_day_of_year = start_date.timetuple().tm_yday + end_day_of_year = end_date.timetuple().tm_yday + + # Beachten, dass bei Schaltjahren der Tag des Jahres angepasst werden muss + stats_for_range = self.data_year_energy[start_day_of_year-1:end_day_of_year] # -1 da die Indizierung bei 0 beginnt + + # Hier kannst du entscheiden, wie du die Daten über den Zeitraum aggregieren möchtest + # Zum Beispiel könntest du Mittelwerte, Summen oder andere Statistiken über diesen Zeitraum berechnen + return stats_for_range + def load_data(self): diff --git a/modules/class_pv_forecast.py b/modules/class_pv_forecast.py index 7f957eb..60a1c68 100644 --- a/modules/class_pv_forecast.py +++ b/modules/class_pv_forecast.py @@ -117,6 +117,35 @@ class PVForecast: return np.array(daily_forecast) + def get_pv_forecast_for_date_range(self, start_date_str, end_date_str): + start_date = datetime.strptime(start_date_str, "%Y-%m-%d").date() + end_date = datetime.strptime(end_date_str, "%Y-%m-%d").date() + date_range_forecast = [] + + for data in self.forecast_data: + data_date = datetime.strptime(data.get_date_time(), "%Y-%m-%dT%H:%M:%S.%f%z").date() + if start_date <= data_date <= end_date: + date_range_forecast.append(data) + + ac_power_forecast = np.array([data.get_ac_power() for data in date_range_forecast]) + + return ac_power_forecast + + def get_temperature_for_date_range(self, start_date_str, end_date_str): + start_date = datetime.strptime(start_date_str, "%Y-%m-%d").date() + end_date = datetime.strptime(end_date_str, "%Y-%m-%d").date() + date_range_forecast = [] + + for data in self.forecast_data: + data_date = datetime.strptime(data.get_date_time(), "%Y-%m-%dT%H:%M:%S.%f%z").date() + if start_date <= data_date <= end_date: + date_range_forecast.append(data) + + forecast_data = date_range_forecast + temperature_forecast = [data.get_temperature() for data in forecast_data] + return np.array(temperature_forecast) + + diff --git a/modules/class_strompreis.py b/modules/class_strompreis.py index fd04a9e..c7beba9 100644 --- a/modules/class_strompreis.py +++ b/modules/class_strompreis.py @@ -43,7 +43,23 @@ class HourlyElectricityPriceForecast: def get_price_for_date(self, date_str): """Gibt alle Preise für das spezifizierte Datum zurück.""" date_prices = [entry["marketpriceEurocentPerKWh"] for entry in self.prices if date_str in entry['start']] - return date_prices + return np.array(date_prices)/(1000.0*100.0) + + def get_price_for_daterange(self, start_date_str, end_date_str): + """Gibt alle Preise zwischen dem Start- und Enddatum zurück.""" + start_date = datetime.strptime(start_date_str, "%Y-%m-%d") + end_date = datetime.strptime(end_date_str, "%Y-%m-%d") + price_list = [] + + while start_date <= end_date: + date_str = start_date.strftime("%Y-%m-%d") + daily_prices = self.get_price_for_date(date_str) + if daily_prices.size > 0: + price_list.extend(daily_prices) + start_date += timedelta(days=1) + + return np.array(price_list) + diff --git a/modules/visualize.py b/modules/visualize.py index d18726a..4dcf274 100644 --- a/modules/visualize.py +++ b/modules/visualize.py @@ -3,7 +3,7 @@ import matplotlib.pyplot as plt def visualisiere_ergebnisse(last,leistung_haushalt,leistung_wp, pv_forecast, strompreise, ergebnisse): - stunden = np.arange(1, 25) # 1 bis 24 Stunden + stunden = np.arange(1, len(last)+1) # 1 bis 24 Stunden # Last und PV-Erzeugung plt.figure(figsize=(14, 10)) @@ -20,8 +20,9 @@ def visualisiere_ergebnisse(last,leistung_haushalt,leistung_wp, pv_forecast, str plt.grid(True) # Strompreise + stundenp = np.arange(1, len(strompreise)+1) plt.subplot(3, 1, 2) - plt.plot(stunden, strompreise, label='Strompreis (€/Wh)', color='purple', marker='s') + plt.plot(stundenp, strompreise, label='Strompreis (€/Wh)', color='purple', marker='s') plt.title('Strompreise') plt.xlabel('Stunde des Tages') plt.ylabel('Preis (€/Wh)') @@ -30,15 +31,15 @@ def visualisiere_ergebnisse(last,leistung_haushalt,leistung_wp, pv_forecast, str plt.figure(figsize=(18, 12)) - + stunden = np.arange(1, len(ergebnisse['Eigenverbrauch_Wh_pro_Stunde'])+1) # Eigenverbrauch, Netzeinspeisung und Netzbezug plt.subplot(3, 2, 1) plt.plot(stunden, ergebnisse['Eigenverbrauch_Wh_pro_Stunde'], label='Eigenverbrauch (Wh)', marker='o') plt.plot(stunden, ergebnisse['Netzeinspeisung_Wh_pro_Stunde'], label='Netzeinspeisung (Wh)', marker='x') plt.plot(stunden, ergebnisse['akku_soc_pro_stunde'], label='Akku (%)', marker='x') plt.plot(stunden, ergebnisse['Netzbezug_Wh_pro_Stunde'], label='Netzbezug (Wh)', marker='^') - plt.plot(stunden, pv_forecast, label='PV-Erzeugung (Wh)', marker='x') - plt.plot(stunden, last, label='Last (Wh)', marker='o') + #plt.plot(stunden, pv_forecast, label='PV-Erzeugung (Wh)', marker='x') + #plt.plot(stunden, last, label='Last (Wh)', marker='o') plt.title('Energiefluss pro Stunde') plt.xlabel('Stunde') diff --git a/test.py b/test.py index 3596d72..33597ae 100644 --- a/test.py +++ b/test.py @@ -16,44 +16,42 @@ import random import os -date = "2024-02-26" -akku_size = 1000 # Wh -year_energy = 2000*1000 #Wh -einspeiseverguetung_cent_pro_wh = np.full(24, 7/1000.0) +prediction_hours = 48 +date = (datetime.now().date() + timedelta(days=1)).strftime("%Y-%m-%d") +date_now = datetime.now().strftime("%Y-%m-%d") -max_heizleistung = 5000 # 5 kW Heizleistung +akku_size = 30000 # Wh +year_energy = 2000*1000 #Wh +einspeiseverguetung_cent_pro_wh = np.full(prediction_hours, 7/(1000.0*100.0)) # € / Wh + +max_heizleistung = 1000 # 5 kW Heizleistung wp = Waermepumpe(max_heizleistung) -akku = PVAkku(akku_size) -discharge_array = np.full(24,1) +akku = PVAkku(akku_size,prediction_hours) +discharge_array = np.full(prediction_hours,1) # Load Forecast ############### lf = LoadForecast(filepath=r'load_profiles.npz', year_energy=year_energy) -leistung_haushalt = lf.get_daily_stats(date)[0,...] # Datum anpassen -pprint(leistung_haushalt.shape) +#leistung_haushalt = lf.get_daily_stats(date)[0,...] # Datum anpassen +leistung_haushalt = lf.get_stats_for_date_range(date_now,date)[0,...].flatten() + # PV Forecast ############### #PVforecast = PVForecast(filepath=os.path.join(r'test_data', r'pvprognose.json')) PVforecast = PVForecast(url="https://api.akkudoktor.net/forecast?lat=52.52&lon=13.405&power=5400&azimuth=-10&tilt=7&powerInvertor=2500&horizont=20,40,30,30&power=4800&azimuth=-90&tilt=7&powerInvertor=2500&horizont=20,40,45,50&power=1480&azimuth=-90&tilt=70&powerInvertor=1120&horizont=60,45,30,70&power=1600&azimuth=5&tilt=60&powerInvertor=1200&horizont=60,45,30,70&past_days=5&cellCoEff=-0.36&inverterEfficiency=0.8&albedo=0.25&timezone=Europe%2FBerlin&hourly=relativehumidity_2m%2Cwindspeed_10m") -pv_forecast = PVforecast.get_forecast_for_date(date) -temperature_forecast = PVforecast.get_temperature_forecast_for_date(date) -pprint(pv_forecast.shape) +pv_forecast = PVforecast.get_pv_forecast_for_date_range(date_now,date) #get_forecast_for_date(date) + +temperature_forecast = PVforecast.get_temperature_for_date_range(date_now,date) # Strompreise ############### filepath = os.path.join (r'test_data', r'strompreise_akkudokAPI.json') # Pfad zur JSON-Datei anpassen #price_forecast = HourlyElectricityPriceForecast(source=filepath) -price_forecast = HourlyElectricityPriceForecast(source="https://api.akkudoktor.net/prices?start="+date+"&end="+date+"") - - - - -specific_date_prices = price_forecast.get_price_for_date(date) -pprint(f"Preise für {date}: {specific_date_prices}") - +price_forecast = HourlyElectricityPriceForecast(source="https://api.akkudoktor.net/prices?start="+date_now+"&end="+date+"") +specific_date_prices = price_forecast.get_price_for_daterange(date_now,date) # WP leistung_wp = wp.simulate_24h(temperature_forecast) @@ -64,9 +62,10 @@ load = leistung_haushalt + leistung_wp # EMS / Stromzähler Bilanz ems = EnergieManagementSystem(akku, load, pv_forecast, specific_date_prices, einspeiseverguetung_cent_pro_wh) -o = ems.simuliere() +o = ems.simuliere_ab_jetzt() +pprint(o) pprint(o["Gesamtbilanz_Euro"]) - +#sys.exit() # Optimierung @@ -77,7 +76,7 @@ def evaluate(individual): #akku.set_discharge_per_hour(individual) ems.reset() ems.set_akku_discharge_hours(individual) - o = ems.simuliere() + o = ems.simuliere_ab_jetzt() gesamtbilanz = o["Gesamtbilanz_Euro"] #print(individual, " ",gesamtbilanz) return (gesamtbilanz,) @@ -88,7 +87,7 @@ creator.create("Individual", list, fitness=creator.FitnessMin) toolbox = base.Toolbox() toolbox.register("attr_bool", random.randint, 0, 1) -toolbox.register("individual", tools.initRepeat, creator.Individual, toolbox.attr_bool, 24) +toolbox.register("individual", tools.initRepeat, creator.Individual, toolbox.attr_bool, prediction_hours) toolbox.register("population", tools.initRepeat, list, toolbox.individual) toolbox.register("evaluate", evaluate) @@ -114,7 +113,7 @@ best_solution = optimize() print("Beste Lösung:", best_solution) ems.set_akku_discharge_hours(best_solution) -o = ems.simuliere() +o = ems.simuliere_ab_jetzt() pprint(o["Gesamtbilanz_Euro"])