From 2605460a99ee2dd07b20e0b3e02089460e5851d8 Mon Sep 17 00:00:00 2001 From: Bla Bla Date: Wed, 8 May 2024 09:58:41 +0200 Subject: [PATCH] =?UTF-8?q?48=20Stunden=20Predcition=20&=20Optimierung=20E?= =?UTF-8?q?in=20paar=20Zeitfunktionen=20korrigiert=20(24h=20/=2048h)=20Str?= =?UTF-8?q?ompreis=20Cache=20st=C3=BCndlich=20leeren=20Strompreis=20bei=20?= =?UTF-8?q?nur=2024h=20Daten,=20wird=20verdoppelt=20(Prognose=20fehlt=20no?= =?UTF-8?q?ch)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +++ flask_server.py | 4 +-- modules/class_ems.py | 27 +------------------- modules/class_load.py | 9 ++++++- modules/class_optimize.py | 31 ++++++++++++++++------- modules/class_strompreis.py | 50 +++++++++++++++++++++++++++++++------ 6 files changed, 79 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index ae3d2a6..ec720f0 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,10 @@ Dieses Projekt bietet eine umfassende Lösung zur Simulation und Optimierung ein - `Simulation:` Lastverteilung 1h Werte -> Minuten (Tabelle) - `Dynamische Lasten:` z.B. eine Spülmaschine, welche gesteuert werdeb jabb, - `Simulation:` AC Chargen möglich +- `Optimierung:` E-Auto Akku voll = in der 0/1 Liste keine Möglichkeit mehr auf 1 (aktuell ist der Optimierung das egalm ändert ja nichts) Optimierungsparameter reduzieren +- `Backend:` Visual Cleaner (z.B. E-Auto Akku = 100%, dann sollte die Lademöglichkeit auf 0 stehen. Zumindest bei der Ausgabe sollte das "sauber" sein) +- `Backend:` Cache regelmäßig leeren können (API) + ## Installation diff --git a/flask_server.py b/flask_server.py index ff2c3d6..ddd40da 100644 --- a/flask_server.py +++ b/flask_server.py @@ -27,7 +27,7 @@ import os app = Flask(__name__) -opt_class = optimization_problem(prediction_hours=24, strafe=10) +opt_class = optimization_problem(prediction_hours=48, strafe=10) soc_predictor = BatterySocPredictor.load_model('battery_model.pkl') @@ -69,7 +69,7 @@ def flask_optimize(): return jsonify({"error": f"Fehlender Parameter: {p}"}), 400 # Simulation durchführen - ergebnis = opt_class.optimierung_ems(parameter=parameter, start_hour=datetime.now().hour) + ergebnis = opt_class.optimierung_ems(parameter=parameter, start_hour=datetime.now().hour) # , startdate = datetime.now().date() - timedelta(days = 1) return jsonify(ergebnis) diff --git a/modules/class_ems.py b/modules/class_ems.py index c948dae..8b9d29d 100644 --- a/modules/class_ems.py +++ b/modules/class_ems.py @@ -98,32 +98,7 @@ class EnergieManagementSystem: verluste_wh_pro_stunde[-1] += verluste eigenverbrauch_wh_pro_stunde.append(eigenverbrauch) - - - # Mehr erzeugt als verbraucht - # if erzeugung > verbrauch: - # überschuss = erzeugung - verbrauch - # #geladene_energie = min(überschuss, self.akku.kapazitaet_wh - self.akku.soc_wh) - # geladene_energie, verluste_laden_akku = self.akku.energie_laden(überschuss, stunde) - # verluste_wh_pro_stunde[-1] += verluste_laden_akku - # #print("verluste_laden_akku:",verluste_laden_akku) - # netzeinspeisung_wh_pro_stunde.append(überschuss - geladene_energie-verluste_laden_akku) - # eigenverbrauch_wh_pro_stunde.append(verbrauch) - # stündliche_einnahmen_euro = (überschuss - geladene_energie-verluste_laden_akku) * self.einspeiseverguetung_euro_pro_wh[stunde] - # #print(überschuss," ", geladene_energie," ",verluste_laden_akku) - # netzbezug_wh_pro_stunde.append(0.0) - # # Noch Netzbezug nötig - # else: - # netzeinspeisung_wh_pro_stunde.append(0.0) - # benötigte_energie = verbrauch - erzeugung - # aus_akku, akku_entladeverluste = self.akku.energie_abgeben(benötigte_energie, stunde) - # verluste_wh_pro_stunde[-1] += akku_entladeverluste - # #print("akku_entladeverluste:",akku_entladeverluste) - - # stündlicher_netzbezug_wh = benötigte_energie - aus_akku - # netzbezug_wh_pro_stunde.append(stündlicher_netzbezug_wh) - # eigenverbrauch_wh_pro_stunde.append(erzeugung+aus_akku) - # stündliche_kosten_euro = stündlicher_netzbezug_wh * strompreis + if self.eauto: eauto_soc_pro_stunde.append(eauto_soc) diff --git a/modules/class_load.py b/modules/class_load.py index 941c04f..a1f4d16 100644 --- a/modules/class_load.py +++ b/modules/class_load.py @@ -67,12 +67,19 @@ class LoadForecast: # Beachten, dass bei Schaltjahren der Tag des Jahres angepasst werden muss stats_for_range = self.data_year_energy[start_day_of_year:end_day_of_year] # -1 da die Indizierung bei 0 beginnt + # print(start_day_of_year,"-",end_day_of_year) + # print(stats_for_range.shape) + stats_for_range =stats_for_range.swapaxes(1, 0) + + stats_for_range = stats_for_range.reshape(stats_for_range.shape[0],-1) + # print(stats_for_range.shape) + # print(stats_for_range) # 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[0] + return stats_for_range diff --git a/modules/class_optimize.py b/modules/class_optimize.py index fb4206f..dd875bf 100644 --- a/modules/class_optimize.py +++ b/modules/class_optimize.py @@ -70,7 +70,7 @@ class optimization_problem: self.toolbox.register("individual", create_individual)#tools.initCycle, creator.Individual, (self.toolbox.attr_bool,self.toolbox.attr_bool), n=self.prediction_hours+1) self.toolbox.register("population", tools.initRepeat, list, self.toolbox.individual) self.toolbox.register("mate", tools.cxTwoPoint) - self.toolbox.register("mutate", tools.mutFlipBit, indpb=0.05) + self.toolbox.register("mutate", tools.mutFlipBit, indpb=0.1) self.toolbox.register("select", tools.selTournament, tournsize=3) def evaluate_inner(self,individual, ems,start_hour): @@ -131,7 +131,7 @@ class optimization_problem: # Genetischer Algorithmus def optimize(self,start_solution=None): - population = self.toolbox.population(n=1000) + population = self.toolbox.population(n=400) hof = tools.HallOfFame(1) stats = tools.Statistics(lambda ind: ind.fitness.values) @@ -145,7 +145,7 @@ class optimization_problem: population.insert(0, creator.Individual(start_solution)) #algorithms.eaMuPlusLambda(population, self.toolbox, 100, 200, cxpb=0.2, mutpb=0.2, ngen=500, stats=stats, halloffame=hof, verbose=True) - algorithms.eaSimple(population, self.toolbox, cxpb=0.2, mutpb=0.2, ngen=200, stats=stats, halloffame=hof, verbose=True) + algorithms.eaSimple(population, self.toolbox, cxpb=0.1, mutpb=0.1, ngen=400, stats=stats, halloffame=hof, verbose=True) member = {"bilanz":[],"verluste":[],"nebenbedingung":[]} for ind in population: @@ -159,14 +159,19 @@ class optimization_problem: return hof[0], member - def optimierung_ems(self,parameter=None, start_hour=None,worst_case=False): + def optimierung_ems(self,parameter=None, start_hour=None,worst_case=False, startdate=None): ############ # Parameter ############ - date = (datetime.now().date() + timedelta(hours = self.prediction_hours)).strftime("%Y-%m-%d") - date_now = datetime.now().strftime("%Y-%m-%d") + if startdate == None: + date = (datetime.now().date() + timedelta(hours = self.prediction_hours)).strftime("%Y-%m-%d") + date_now = datetime.now().strftime("%Y-%m-%d") + else: + date = (startdate + timedelta(hours = self.prediction_hours)).strftime("%Y-%m-%d") + date_now = startdate.strftime("%Y-%m-%d") + #print("Start_date:",date_now) akku_size = parameter['pv_akku_cap'] # Wh year_energy = parameter['year_energy'] #2000*1000 #Wh @@ -187,7 +192,7 @@ class optimization_problem: eauto.set_charge_per_hour(laden_moeglich) min_soc_eauto = parameter['eauto_min_soc'] start_params = parameter['start_solution'] - gesamtlast = Gesamtlast() + gesamtlast = Gesamtlast(prediction_hours=self.prediction_hours) ############### # spuelmaschine @@ -205,7 +210,12 @@ class optimization_problem: ############### 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() + + leistung_haushalt = lf.get_stats_for_date_range(date_now,date)[0] # Nur Erwartungswert! + + + + gesamtlast.hinzufuegen("Haushalt", leistung_haushalt) ############### @@ -229,8 +239,11 @@ class optimization_problem: ############### 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_now+"&end="+date+"") + price_forecast = HourlyElectricityPriceForecast(source="https://api.akkudoktor.net/prices?start="+date_now+"&end="+date+"", prediction_hours=self.prediction_hours) specific_date_prices = price_forecast.get_price_for_daterange(date_now,date) + #print(price_forecast) + print(specific_date_prices) + #print("https://api.akkudoktor.net/prices?start="+date_now+"&end="+date) ############### # WP diff --git a/modules/class_strompreis.py b/modules/class_strompreis.py index 182cc68..c5e8d2d 100644 --- a/modules/class_strompreis.py +++ b/modules/class_strompreis.py @@ -14,20 +14,34 @@ utc_time = utc_time.replace(tzinfo=pytz.utc) local_time = utc_time.astimezone(pytz.timezone('Europe/Berlin')) print(local_time) +def repeat_to_shape(array, target_shape): + # Prüfen , ob das Array in die Zielgröße passt + if len(target_shape) != array.ndim: + raise ValueError("Array and target shape must have the same number of dimensions") + + # die Anzahl der Wiederholungen pro Dimension + repeats = tuple(target_shape[i] // array.shape[i] for i in range(array.ndim)) + + # np.tile, um das Array zu erweitern + expanded_array = np.tile(array, repeats) + return expanded_array + class HourlyElectricityPriceForecast: - def __init__(self, source, cache_dir='cache', abgaben=0.00019): + def __init__(self, source, cache_dir='cache', abgaben=0.000, prediction_hours=24): #228 self.cache_dir = cache_dir if not os.path.exists(self.cache_dir): os.makedirs(self.cache_dir) + self.cache_time_file = os.path.join(self.cache_dir, 'cache_timestamp.txt') self.prices = self.load_data(source) self.abgaben = abgaben + self.prediction_hours = prediction_hours def load_data(self, source): + cache_filename = self.get_cache_filename(source) if source.startswith('http'): - cache_filename = self.get_cache_filename(source) - if os.path.exists(cache_filename): + if os.path.exists(cache_filename) and not self.is_cache_expired(): print("Lade Daten aus dem Cache...") with open(cache_filename, 'r') as file: data = json.load(file) @@ -38,6 +52,7 @@ class HourlyElectricityPriceForecast: data = response.json() with open(cache_filename, 'w') as file: json.dump(data, file) + self.update_cache_timestamp() else: raise Exception(f"Fehler beim Abrufen der Daten: {response.status_code}") else: @@ -49,6 +64,18 @@ class HourlyElectricityPriceForecast: hash_object = hashlib.sha256(url.encode()) hex_dig = hash_object.hexdigest() return os.path.join(self.cache_dir, f"cache_{hex_dig}.json") + + def is_cache_expired(self): + if not os.path.exists(self.cache_time_file): + return True + with open(self.cache_time_file, 'r') as file: + timestamp_str = file.read() + last_cache_time = datetime.strptime(timestamp_str, '%Y-%m-%d %H:%M:%S') + return datetime.now() - last_cache_time > timedelta(hours=1) + + def update_cache_timestamp(self): + with open(self.cache_time_file, 'w') as file: + file.write(datetime.now().strftime('%Y-%m-%d %H:%M:%S')) @@ -68,13 +95,17 @@ class HourlyElectricityPriceForecast: # Extrahieren aller Preise für das spezifizierte Datum date_prices = [entry["marketpriceEurocentPerKWh"]+self.abgaben for entry in self.prices if date_str in entry['end']] + print("getPRice:",len(date_prices)) # Hinzufügen des letzten Preises des vorherigen Tages am Anfang der Liste - date_prices.insert(0, last_price_of_previous_day) + if len(date_prices) == 23: + date_prices.insert(0, last_price_of_previous_day) return np.array(date_prices)/(1000.0*100.0) + self.abgaben def get_price_for_daterange(self, start_date_str, end_date_str): + print(start_date_str) + print(end_date_str) """Gibt alle Preise zwischen dem Start- und Enddatum zurück.""" start_date_utc = datetime.strptime(start_date_str, "%Y-%m-%d").replace(tzinfo=pytz.utc) end_date_utc = datetime.strptime(end_date_str, "%Y-%m-%d").replace(tzinfo=pytz.utc) @@ -84,13 +115,16 @@ class HourlyElectricityPriceForecast: price_list = [] - while start_date <= end_date: + while start_date < end_date: date_str = start_date.strftime("%Y-%m-%d") daily_prices = self.get_price_for_date(date_str) - #print(len(self.get_price_for_date(date_str))) + print(date_str," ",daily_prices) + print(len(self.get_price_for_date(date_str))) + if daily_prices.size ==24: price_list.extend(daily_prices) start_date += timedelta(days=1) - return np.array(price_list) - + price_list = repeat_to_shape(np.array(price_list),(self.prediction_hours,)) + + return price_list