diff --git a/src/akkudoktoreos/class_ems.py b/src/akkudoktoreos/class_ems.py index 94c33a2..e682e18 100644 --- a/src/akkudoktoreos/class_ems.py +++ b/src/akkudoktoreos/class_ems.py @@ -95,21 +95,13 @@ class EnergieManagementSystem: haushaltsgeraet_wh_pro_stunde[stunde_since_now] = ha_load # E-Auto handling - if self.eauto: + if self.eauto and self.ev_charge_hours[stunde]>0: geladene_menge_eauto, verluste_eauto = self.eauto.energie_laden(None, stunde, relative_power=self.ev_charge_hours[stunde]) - # if self.ev_charge_hours[stunde] > 0.0: - # print(self.ev_charge_hours[stunde], " ", geladene_menge_eauto," ", self.eauto.ladezustand_in_prozent()) verbrauch += geladene_menge_eauto verluste_wh_pro_stunde[stunde_since_now] += verluste_eauto + + if self.eauto: eauto_soc_pro_stunde[stunde_since_now] = self.eauto.ladezustand_in_prozent() - - # AC PV Battery Charge - if self.ac_charge_hours[stunde] > 0.0: - self.akku.set_charge_allowed_for_hour(self.ac_charge_hours[stunde],stunde) - geladene_menge, verluste_wh = self.akku.energie_laden(None,stunde,relative_power=self.ac_charge_hours[stunde]) - verbrauch += geladene_menge - verluste_wh_pro_stunde[stunde_since_now] += verluste_wh - # Process inverter logic erzeugung = self.pv_prognose_wh[stunde] self.akku.set_charge_allowed_for_hour(self.dc_charge_hours[stunde],stunde) @@ -117,7 +109,14 @@ class EnergieManagementSystem: self.wechselrichter.energie_verarbeiten(erzeugung, verbrauch, stunde) ) - + # AC PV Battery Charge + if self.ac_charge_hours[stunde] > 0.0: + self.akku.set_charge_allowed_for_hour(1,stunde) + geladene_menge, verluste_wh = self.akku.energie_laden(None,stunde,relative_power=self.ac_charge_hours[stunde]) + #print(stunde, " ", geladene_menge, " ",self.ac_charge_hours[stunde]," ",self.akku.ladezustand_in_prozent()) + verbrauch += geladene_menge + netzbezug +=geladene_menge + verluste_wh_pro_stunde[stunde_since_now] += verluste_wh netzeinspeisung_wh_pro_stunde[stunde_since_now] = netzeinspeisung netzbezug_wh_pro_stunde[stunde_since_now] = netzbezug diff --git a/src/akkudoktoreos/class_optimize.py b/src/akkudoktoreos/class_optimize.py index 80b610d..c330507 100644 --- a/src/akkudoktoreos/class_optimize.py +++ b/src/akkudoktoreos/class_optimize.py @@ -31,6 +31,7 @@ class optimization_problem: self.verbose = verbose self.fix_seed = fixed_seed self.optimize_ev = True + self.optimize_dc_charge = False # Set a fixed seed for random operations if provided if fixed_seed is not None: @@ -63,7 +64,12 @@ class optimization_problem: ac_charge = ac_charge / 5.0 # Normalize AC charge to range between 0 and 1 # Create dc_charge array: 7 = Not allowed (mapped to 0), 8 = Allowed (mapped to 1) - dc_charge = np.where(discharge_hours_bin == 8, 1, 0) + # Create dc_charge array: Only if DC charge optimization is enabled + if self.optimize_dc_charge: + dc_charge = np.where(discharge_hours_bin == 8, 1, 0) + else: + dc_charge = np.ones_like(discharge_hours_bin) # Set DC charge to 0 if optimization is disabled + # Create discharge array: Only consider value 1 (Discharge), set the rest to 0 (binary output) discharge = np.where(discharge_hours_bin == 1, 1, 0) @@ -95,7 +101,10 @@ class optimization_problem: charge_discharge_mutated, = self.toolbox.mutate_charge_discharge(charge_discharge_part) # Ensure that no invalid states are introduced during mutation (valid values: 0-8) - charge_discharge_mutated = np.clip(charge_discharge_mutated, 0, 8) + if self.optimize_dc_charge: + charge_discharge_mutated = np.clip(charge_discharge_mutated, 0, 8) + else: + charge_discharge_mutated = np.clip(charge_discharge_mutated, 0, 6) # Use split_charge_discharge to split the mutated array into AC charge, DC charge, and discharge components #ac_charge, dc_charge, discharge = self.split_charge_discharge(charge_discharge_mutated) @@ -190,7 +199,11 @@ class optimization_problem: # Initialize toolbox with attributes and operations self.toolbox = base.Toolbox() - self.toolbox.register("attr_discharge_state", random.randint, 0,11) + if self.optimize_dc_charge: + self.toolbox.register("attr_discharge_state", random.randint, 0,8) + else: + self.toolbox.register("attr_discharge_state", random.randint, 0,6) + if self.optimize_ev: self.toolbox.register("attr_ev_charge_index", random.randint, 0, len(possible_ev_charge_currents) - 1) self.toolbox.register("attr_int", random.randint, start_hour, 23) @@ -205,7 +218,10 @@ class optimization_problem: #self.toolbox.register("mutate", tools.mutFlipBit, indpb=0.1) # Register separate mutation functions for each type of value: # - Discharge state mutation (-5, 0, 1) - self.toolbox.register("mutate_charge_discharge", tools.mutUniformInt, low=0, up=8, indpb=0.1) + if self.optimize_dc_charge: + self.toolbox.register("mutate_charge_discharge", tools.mutUniformInt, low=0, up=8, indpb=0.1) + else: + self.toolbox.register("mutate_charge_discharge", tools.mutUniformInt, low=0, up=6, indpb=0.1) # - Float mutation for EV charging values self.toolbox.register("mutate_ev_charge_index", tools.mutUniformInt, low=0, up=len(possible_ev_charge_currents) - 1, indpb=0.1) # - Start hour mutation for household devices @@ -234,7 +250,9 @@ class optimization_problem: ems.set_akku_discharge_hours(discharge) - ems.set_akku_dc_charge_hours(dc) + # Set DC charge hours only if DC optimization is enabled + if self.optimize_dc_charge: + ems.set_akku_dc_charge_hours(dc) ems.set_akku_ac_charge_hours(ac) if self.optimize_ev: @@ -242,8 +260,8 @@ class optimization_problem: possible_ev_charge_currents[i] for i in eautocharge_hours_index ] ems.set_ev_charge_hours(eautocharge_hours_float) - #else: - # ems.set_ev_charge_hours(np.full(self.prediction_hours, 0 )) + else: + ems.set_ev_charge_hours(np.full(self.prediction_hours, 0 )) return ems.simuliere(start_hour) def evaluate( @@ -284,8 +302,11 @@ class optimization_problem: ) # Adjust total balance with battery value and penalties for unmet SOC + restwert_akku = ems.akku.aktueller_energieinhalt() * parameter["preis_euro_pro_wh_akku"] + #print(ems.akku.aktueller_energieinhalt()," * ", parameter["preis_euro_pro_wh_akku"] , " ", restwert_akku, " ", gesamtbilanz) gesamtbilanz += -restwert_akku + #print(gesamtbilanz) if self.optimize_ev: gesamtbilanz += max( 0, @@ -318,8 +339,8 @@ class optimization_problem: self.toolbox, mu=100, lambda_=150, - cxpb=0.7, - mutpb=0.3, + cxpb=0.6, + mutpb=0.4, ngen=ngen, stats=stats, halloffame=hof, @@ -412,8 +433,8 @@ class optimization_problem: discharge_hours_bin, eautocharge_hours_float, spuelstart_int = self.split_individual( start_solution ) - - + if self.optimize_ev: + eautocharge_hours_float = [possible_ev_charge_currents[i] for i in eautocharge_hours_float] ac_charge, dc_charge, discharge = self.decode_charge_discharge(discharge_hours_bin) # Visualize the results diff --git a/src/akkudoktoreos/class_soc_calc.py b/src/akkudoktoreos/class_soc_calc.py index 7410a5b..6436073 100644 --- a/src/akkudoktoreos/class_soc_calc.py +++ b/src/akkudoktoreos/class_soc_calc.py @@ -5,7 +5,6 @@ import matplotlib.pyplot as plt import numpy as np import pandas as pd - class BatteryDataProcessor: def __init__( self, @@ -235,7 +234,7 @@ class BatteryDataProcessor: marker="o", label="100% SoC Points", ) - # plt.scatter(last_points_0_df['timestamp'], last_points_0_df['battery_voltage'], color='red', marker='x', label='0% SoC Points') + plt.scatter(last_points_0_df['timestamp'], last_points_0_df['battery_voltage'], color='red', marker='x', label='0% SoC Points') plt.xlabel("Timestamp") plt.ylabel("Voltage (V)") plt.legend() @@ -255,7 +254,7 @@ class BatteryDataProcessor: marker="o", label="100% SoC Points", ) - # plt.scatter(last_points_0_df['timestamp'], last_points_0_df['battery_current'], color='red', marker='x', label='0% SoC Points') + plt.scatter(last_points_0_df['timestamp'], last_points_0_df['battery_current'], color='red', marker='x', label='0% SoC Points') plt.xlabel("Timestamp") plt.ylabel("Current (A)") plt.legend() @@ -281,17 +280,23 @@ class BatteryDataProcessor: if __name__ == "__main__": # MariaDB Verbindungsdetails - config = {} + + config = { + 'user': 'soc', + 'password': 'Rayoflight123!', + 'host': '192.168.1.135', + 'database': 'sensor' + } # Parameter festlegen voltage_high_threshold = 55.4 # 100% SoC - voltage_low_threshold = 46.5 # 0% SoC + voltage_low_threshold = 48 # 0% SoC current_low_threshold = 2 # Niedriger Strom für beide Zustände gap = 30 # Zeitlücke in Minuten zum Gruppieren von Maxima/Minima - bat_capacity = 33 * 1000 / 48 + bat_capacity = 0.8*33 * 1000 / 48 # Zeitpunkt X definieren - zeitpunkt_x = (datetime.now() - timedelta(weeks=100)).strftime("%Y-%m-%d %H:%M:%S") + zeitpunkt_x = (datetime.now() - timedelta(weeks=4)).strftime("%Y-%m-%d %H:%M:%S") # BatteryDataProcessor instanziieren und verwenden processor = BatteryDataProcessor( @@ -310,7 +315,7 @@ if __name__ == "__main__": last_points_100_df, last_points_0_df ) # soh_df = processor.calculate_soh(integration_results) - processor.update_database_with_soc(soc_df) + #processor.update_database_with_soc(soc_df) processor.plot_data(last_points_100_df, last_points_0_df, soc_df) diff --git a/src/akkudoktoreos/class_strompreis.py b/src/akkudoktoreos/class_strompreis.py index 7f0b1a8..727fbe3 100644 --- a/src/akkudoktoreos/class_strompreis.py +++ b/src/akkudoktoreos/class_strompreis.py @@ -22,18 +22,20 @@ def repeat_to_shape(array, target_shape): class HourlyElectricityPriceForecast: - def __init__(self, source, cache_dir="cache", charges=0.000228, prediction_hours=24): # 228 + def __init__(self, source, cache_dir="cache", charges=0.000228, prediction_hours=24, cache=True): # 228 self.cache_dir = cache_dir + self.cache=cache os.makedirs(self.cache_dir, exist_ok=True) self.cache_time_file = os.path.join(self.cache_dir, "cache_timestamp.txt") self.prices = self.load_data(source) self.charges = charges self.prediction_hours = prediction_hours + def load_data(self, source): cache_filename = self.get_cache_filename(source) if source.startswith("http"): - if os.path.exists(cache_filename) and not self.is_cache_expired(): + if os.path.exists(cache_filename) and not self.is_cache_expired() and self.cache==True: print("Loading data from cache...") with open(cache_filename, "r") as file: json_data = json.load(file) diff --git a/src/akkudoktoreosserver/flask_server.py b/src/akkudoktoreosserver/flask_server.py index 570ad82..d8bd433 100755 --- a/src/akkudoktoreosserver/flask_server.py +++ b/src/akkudoktoreosserver/flask_server.py @@ -62,6 +62,7 @@ def flask_strompreis(): price_forecast = HourlyElectricityPriceForecast( source=f"https://api.akkudoktor.net/prices?start={date_now}&end={date}", prediction_hours=prediction_hours, + cache=False ) specific_date_prices = price_forecast.get_price_for_daterange( date_now, date