From 133bfc67276f14a442e6bdcbca32b52d6ffb2807 Mon Sep 17 00:00:00 2001 From: Bla Bla Date: Sun, 3 Mar 2024 18:32:47 +0100 Subject: [PATCH] - Akku mit Verlusten + Bug in der test.py Gesamtlast kein update --- modules/class_akku.py | 35 +++++++++++------- modules/class_eauto.py | 7 +++- modules/class_ems.py | 2 ++ modules/class_load.py | 8 +++-- modules/visualize.py | 52 +++++++++++++++++---------- test.py | 80 +++++++++++++++++++++++++++++------------- 6 files changed, 126 insertions(+), 58 deletions(-) diff --git a/modules/class_akku.py b/modules/class_akku.py index d777235..d801a52 100644 --- a/modules/class_akku.py +++ b/modules/class_akku.py @@ -1,12 +1,15 @@ import numpy as np class PVAkku: - def __init__(self, kapazitaet_wh, hours): + def __init__(self, kapazitaet_wh, hours, lade_effizienz=0.9, entlade_effizienz=0.9): # Kapazität des Akkus in Wh self.kapazitaet_wh = kapazitaet_wh # Initialer Ladezustand des Akkus in Wh self.soc_wh = 0 self.hours = hours self.discharge_array = np.full(self.hours, 1) + # Lade- und Entladeeffizienz + self.lade_effizienz = lade_effizienz + self.entlade_effizienz = entlade_effizienz def reset(self): self.soc_wh = 0 @@ -22,19 +25,27 @@ class PVAkku: def energie_abgeben(self, wh, hour): if self.discharge_array[hour] == 0: return 0.0 - if self.soc_wh >= wh: - self.soc_wh -= wh - return wh - else: - abgegebene_energie = self.soc_wh - self.soc_wh = 0 - return abgegebene_energie + soc_tmp = self.soc_wh + # Berechnung der tatsächlichen Entlademenge unter Berücksichtigung der Entladeeffizienz + effektive_entlademenge = wh / self.entlade_effizienz + # Aktualisierung des Ladezustands ohne negativ zu werden + self.soc_wh = max(self.soc_wh - effektive_entlademenge, 0) + return soc_tmp-self.soc_wh + + # if self.soc_wh >= wh: + # self.soc_wh -= wh + # return wh + # else: + # abgegebene_energie = self.soc_wh + # self.soc_wh = 0 + # return abgegebene_energie def energie_laden(self, wh): - if self.soc_wh + wh <= self.kapazitaet_wh: - self.soc_wh += wh - else: - self.soc_wh = self.kapazitaet_wh + # Berechnung der tatsächlichen Lademenge unter Berücksichtigung der Ladeeffizienz + effektive_lademenge = wh * self.lade_effizienz + # Aktualisierung des Ladezustands ohne die Kapazität zu überschreiten + self.soc_wh = min(self.soc_wh + effektive_lademenge, self.kapazitaet_wh) + diff --git a/modules/class_eauto.py b/modules/class_eauto.py index a572dcc..a1e04af 100644 --- a/modules/class_eauto.py +++ b/modules/class_eauto.py @@ -6,6 +6,7 @@ from pprint import pprint class EAuto: def __init__(self, soc=None, capacity = None, power_charge = None, load_allowed = None): self.soc = soc + self.init_soc = soc self.akku_kapazitaet = capacity self.ladegeschwindigkeit = power_charge self.laden_moeglich = None @@ -14,9 +15,13 @@ class EAuto: self.laden_moeglich = load_allowed self.berechne_ladevorgang() + def reset(self): + self.soc = self.init_soc + self.stuendlicher_soc = [] + def set_laden_moeglich(self, laden_moeglich): self.laden_moeglich = laden_moeglich - self.stuendlicher_soc = [self.soc] # Beginnt mit dem aktuellen SoC + self.stuendlicher_soc = [] # Beginnt mit dem aktuellen SoC self.stuendliche_last = [] # Zurücksetzen der stündlichen Last def berechne_ladevorgang(self): diff --git a/modules/class_ems.py b/modules/class_ems.py index d03e6c0..8cd9f00 100644 --- a/modules/class_ems.py +++ b/modules/class_ems.py @@ -10,6 +10,8 @@ class EnergieManagementSystem: 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 + def set_gesamtlast(self,load): + self.lastkurve_wh = load def set_akku_discharge_hours(self, ds): self.akku.set_discharge_per_hour(ds) diff --git a/modules/class_load.py b/modules/class_load.py index 8c982a0..941c04f 100644 --- a/modules/class_load.py +++ b/modules/class_load.py @@ -66,11 +66,13 @@ class LoadForecast: 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 - + stats_for_range = self.data_year_energy[start_day_of_year:end_day_of_year] # -1 da die Indizierung bei 0 beginnt + # print() + # print(stats_for_range) + # print(start_day_of_year, " ",end_day_of_year) # 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 + return stats_for_range[0] diff --git a/modules/visualize.py b/modules/visualize.py index 03490e6..a1d4a27 100644 --- a/modules/visualize.py +++ b/modules/visualize.py @@ -1,19 +1,13 @@ import numpy as np -import matplotlib.pyplot as plt -import numpy as np -import matplotlib.pyplot as plt from modules.class_load_container import Gesamtlast # Stellen Sie sicher, dass dies dem tatsächlichen Importpfad entspricht +import matplotlib.pyplot as plt - - -def visualisiere_ergebnisse(gesamtlast,leistung_haushalt,leistung_wp, pv_forecast, strompreise, ergebnisse): - - +def visualisiere_ergebnisse(gesamtlast,leistung_haushalt,leistung_wp, pv_forecast, strompreise, ergebnisse, soc_eauto, discharge_hours, laden_moeglich): # Last und PV-Erzeugung plt.figure(figsize=(14, 10)) - plt.subplot(3, 1, 1) + plt.subplot(3, 2, 1) stunden = np.arange(1, len(next(iter(gesamtlast.lasten.values()))) + 1) @@ -31,9 +25,13 @@ def visualisiere_ergebnisse(gesamtlast,leistung_haushalt,leistung_wp, pv_forecas plt.grid(True) plt.legend() + + + + # Strompreise stundenp = np.arange(1, len(strompreise)+1) - plt.subplot(3, 1, 2) + plt.subplot(3, 2, 2) plt.plot(stundenp, strompreise, label='Strompreis (€/Wh)', color='purple', marker='s') plt.title('Strompreise') plt.xlabel('Stunde des Tages') @@ -41,26 +39,44 @@ def visualisiere_ergebnisse(gesamtlast,leistung_haushalt,leistung_wp, pv_forecas plt.legend() plt.grid(True) + print(pv_forecast.shape) + print(len(ergebnisse['Eigenverbrauch_Wh_pro_Stunde'])) - 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.subplot(3, 2, 3) 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, 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') plt.ylabel('Energie (Wh)') plt.legend() - plt.grid(True) + + plt.subplot(3, 2, 4) + plt.plot(stunden, ergebnisse['akku_soc_pro_stunde'], label='Akku (%)', marker='x') + plt.plot(stunden, soc_eauto, label='Eauto Akku (%)', marker='x') + plt.legend(loc='upper left') + ax1 = plt.subplot(3, 2, 5) + for hour, value in enumerate(discharge_hours): + if value == 1: + ax1.axvspan(hour, hour+1, color='red', alpha=0.3, label='Entlademöglichkeit' if hour == 0 else "") + for hour, value in enumerate(laden_moeglich): + if value == 1: + ax1.axvspan(hour, hour+1, color='green', alpha=0.3, label='Lademöglichkeit' if hour == 0 else "") + ax1.legend(loc='upper left') + + + + + plt.grid(True) + plt.figure(figsize=(14, 10)) # Kosten und Einnahmen pro Stunde - plt.subplot(3, 2, 2) + plt.subplot(1, 2, 1) plt.plot(stunden, ergebnisse['Kosten_Euro_pro_Stunde'], label='Kosten (Euro)', marker='o', color='red') plt.plot(stunden, ergebnisse['Einnahmen_Euro_pro_Stunde'], label='Einnahmen (Euro)', marker='x', color='green') plt.title('Finanzielle Bilanz pro Stunde') @@ -70,7 +86,7 @@ def visualisiere_ergebnisse(gesamtlast,leistung_haushalt,leistung_wp, pv_forecas plt.grid(True) # Zusammenfassende Finanzen - plt.subplot(3, 2, 3) + plt.subplot(1, 2, 2) gesamtkosten = ergebnisse['Gesamtkosten_Euro'] gesamteinnahmen = ergebnisse['Gesamteinnahmen_Euro'] gesamtbilanz = ergebnisse['Gesamtbilanz_Euro'] diff --git a/test.py b/test.py index 1197448..c28f560 100644 --- a/test.py +++ b/test.py @@ -20,7 +20,7 @@ import os -prediction_hours = 48 +prediction_hours = 24 date = (datetime.now().date() + timedelta(hours = prediction_hours)).strftime("%Y-%m-%d") date_now = datetime.now().strftime("%Y-%m-%d") @@ -32,12 +32,13 @@ max_heizleistung = 1000 # 5 kW Heizleistung wp = Waermepumpe(max_heizleistung,prediction_hours) akku = PVAkku(akku_size,prediction_hours) -discharge_array = np.full(prediction_hours,1) - -laden_moeglich = np.full(prediction_hours,1) -eauto = EAuto(soc=60, capacity = 60000, power_charge = 7000, load_allowed = laden_moeglich) - +discharge_array = np.full(prediction_hours,1) #np.array([1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0]) # +laden_moeglich = np.full(prediction_hours,1) # np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0]) +#np.full(prediction_hours,1) +eauto = EAuto(soc=10, capacity = 60000, power_charge = 7000, load_allowed = laden_moeglich) +min_soc_eauto = 20 +hohe_strafe = 10.0 @@ -52,12 +53,14 @@ gesamtlast = Gesamtlast() lf = LoadForecast(filepath=r'load_profiles.npz', year_energy=year_energy) #leistung_haushalt = lf.get_daily_stats(date)[0,...] # Datum anpassen leistung_haushalt = lf.get_stats_for_date_range(date_now,date)[0,...].flatten() +# print(date_now," ",date) +# print(leistung_haushalt.shape) gesamtlast.hinzufuegen("Haushalt", leistung_haushalt) # PV Forecast ############### #PVforecast = PVForecast(filepath=os.path.join(r'test_data', r'pvprognose.json')) -PVforecast = PVForecast(url="https://api.akkudoktor.net/forecast?lat=50.8588&lon=7.3747&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") +PVforecast = PVForecast(prediction_hours = prediction_hours, url="https://api.akkudoktor.net/forecast?lat=50.8588&lon=7.3747&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_pv_forecast_for_date_range(date_now,date) #get_forecast_for_date(date) temperature_forecast = PVforecast.get_temperature_for_date_range(date_now,date) @@ -79,19 +82,20 @@ gesamtlast.hinzufuegen("Heatpump", leistung_wp) # EAuto ###################### leistung_eauto = eauto.get_stuendliche_last() +soc_eauto = eauto.get_stuendlicher_soc() gesamtlast.hinzufuegen("eauto", leistung_eauto) -print(gesamtlast.gesamtlast_berechnen()) +# print(gesamtlast.gesamtlast_berechnen()) # EMS / Stromzähler Bilanz ems = EnergieManagementSystem(akku, gesamtlast.gesamtlast_berechnen(), pv_forecast, specific_date_prices, einspeiseverguetung_cent_pro_wh) o = ems.simuliere(0)#ems.simuliere_ab_jetzt() -pprint(o) -pprint(o["Gesamtbilanz_Euro"]) +#pprint(o) +#pprint(o["Gesamtbilanz_Euro"]) -visualisiere_ergebnisse(gesamtlast,leistung_haushalt,leistung_wp, pv_forecast, specific_date_prices, o) +#visualisiere_ergebnisse(gesamtlast,leistung_haushalt,leistung_wp, pv_forecast, specific_date_prices, o, soc_eauto) #sys.exit() @@ -100,23 +104,40 @@ visualisiere_ergebnisse(gesamtlast,leistung_haushalt,leistung_wp, pv_forecast, s # Fitness-Funktion (muss Ihre EnergieManagementSystem-Logik integrieren) def evaluate(individual): - # Hier müssen Sie Ihre Logik einbauen, um die Gesamtbilanz zu berechnen - # basierend auf dem gegebenen `individual` (discharge_array) - #akku.set_discharge_per_hour(individual) ems.reset() - ems.set_akku_discharge_hours(individual) - o = ems.simuliere_ab_jetzt() + eauto.reset() + ems.set_akku_discharge_hours(individual[:prediction_hours]) + + eauto.set_laden_moeglich(individual[prediction_hours:]) + eauto.berechne_ladevorgang() + leistung_eauto = eauto.get_stuendliche_last() + gesamtlast.hinzufuegen("eauto", leistung_eauto) + + ems.set_gesamtlast(gesamtlast.gesamtlast_berechnen()) + + o = ems.simuliere(0) gesamtbilanz = o["Gesamtbilanz_Euro"] - #print(individual, " ",gesamtbilanz) + + # Überprüfung, ob der Mindest-SoC erreicht wird + final_soc = eauto.get_stuendlicher_soc()[-1] # Nimmt den SoC am Ende des Optimierungszeitraums + strafe = 0.0 + #if final_soc < min_soc_eauto: + # Fügt eine Strafe hinzu, wenn der Mindest-SoC nicht erreicht wird + strafe = max(0,(min_soc_eauto - final_soc) * hohe_strafe ) # `hohe_strafe` ist ein vorher festgelegter Strafwert + gesamtbilanz += strafe + #if strafe > 0.0: + # print(min_soc_eauto," - ",final_soc,"*10 = ",strafe) + return (gesamtbilanz,) + # Werkzeug-Setup creator.create("FitnessMin", base.Fitness, weights=(-1.0,)) 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, prediction_hours) +toolbox.register("individual", tools.initRepeat, creator.Individual, toolbox.attr_bool, prediction_hours*2) toolbox.register("population", tools.initRepeat, list, toolbox.individual) toolbox.register("evaluate", evaluate) @@ -126,7 +147,7 @@ toolbox.register("select", tools.selTournament, tournsize=3) # Genetischer Algorithmus def optimize(): - population = toolbox.population(n=100) + population = toolbox.population(n=500) hof = tools.HallOfFame(1) stats = tools.Statistics(lambda ind: ind.fitness.values) @@ -134,20 +155,31 @@ def optimize(): stats.register("min", np.min) stats.register("max", np.max) - algorithms.eaSimple(population, toolbox, cxpb=0.5, mutpb=0.2, ngen=100, + algorithms.eaSimple(population, toolbox, cxpb=0.4, mutpb=0.3, ngen=100, stats=stats, halloffame=hof, verbose=True) return hof[0] best_solution = optimize() print("Beste Lösung:", best_solution) -ems.set_akku_discharge_hours(best_solution) -o = ems.simuliere_ab_jetzt() -pprint(o["Gesamtbilanz_Euro"]) +#ems.set_akku_discharge_hours(best_solution) +ems.reset() +eauto.reset() +ems.set_akku_discharge_hours(best_solution[:prediction_hours]) +eauto.set_laden_moeglich(best_solution[prediction_hours:]) +eauto.berechne_ladevorgang() +leistung_eauto = eauto.get_stuendliche_last() +gesamtlast.hinzufuegen("eauto", leistung_eauto) +ems.set_gesamtlast(gesamtlast.gesamtlast_berechnen()) +o = ems.simuliere(0) +soc_eauto = eauto.get_stuendlicher_soc() +print(soc_eauto) +pprint(o) +pprint(eauto.get_stuendlicher_soc()) -visualisiere_ergebnisse(load,leistung_haushalt,leistung_wp, pv_forecast, specific_date_prices, o) +visualisiere_ergebnisse(gesamtlast,leistung_haushalt,leistung_wp, pv_forecast, specific_date_prices, o,soc_eauto,best_solution[:prediction_hours],best_solution[prediction_hours:] ) # for data in forecast.get_forecast_data():