From 2807ec797897663ecd2d8b56f5fdb7d00781e639 Mon Sep 17 00:00:00 2001 From: Andreas Date: Fri, 11 Oct 2024 10:47:29 +0200 Subject: [PATCH] EV possible currents -> Work now (new Mutate Function) -> Some Penalties are removed (deprecated) AC Charge -> Parameter in Optimization -1,0,1 -> Missing integration in class_ems --- single_test_optimization.py | 18 +++++-- src/akkudoktoreos/class_akku.py | 2 +- src/akkudoktoreos/class_ems.py | 1 + src/akkudoktoreos/class_optimize.py | 77 ++++++++++++++++++++--------- 4 files changed, 72 insertions(+), 26 deletions(-) diff --git a/single_test_optimization.py b/single_test_optimization.py index 679d0a9..775ac74 100644 --- a/single_test_optimization.py +++ b/single_test_optimization.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import json +import time # Import necessary modules from the project from akkudoktoreos.class_optimize import optimization_problem @@ -241,7 +242,7 @@ parameter = { # Electricity price forecast (48 hours) "strompreis_euro_pro_wh": strompreis_euro_pro_wh, # Minimum SOC for electric car - "eauto_min_soc": 80, + "eauto_min_soc": 20, # Electric car battery capacity (Wh) "eauto_cap": 60000, # Charging efficiency of the electric car @@ -262,6 +263,9 @@ parameter = { "min_soc_prozent": 15, } +# Startzeit nehmen +start_time = time.time() + # Initialize the optimization problem opt_class = optimization_problem( prediction_hours=48, strafe=10, optimization_hours=24, verbose=True, fixed_seed=42 @@ -270,8 +274,16 @@ opt_class = optimization_problem( # Perform the optimisation based on the provided parameters and start hour ergebnis = opt_class.optimierung_ems(parameter=parameter, start_hour=start_hour) +# Endzeit nehmen +end_time = time.time() + +# Berechnete Zeit ausgeben +elapsed_time = end_time - start_time +print(f"Elapsed time: {elapsed_time:.4f} seconds") + + # Print or visualize the result # pprint(ergebnis) -json_data = json.dumps(ergebnis) -print(json_data) +#json_data = json.dumps(ergebnis) +#print(json_data) diff --git a/src/akkudoktoreos/class_akku.py b/src/akkudoktoreos/class_akku.py index 70d3513..5ec19fb 100644 --- a/src/akkudoktoreos/class_akku.py +++ b/src/akkudoktoreos/class_akku.py @@ -84,7 +84,7 @@ class PVAkku: return (self.soc_wh / self.kapazitaet_wh) * 100 def energie_abgeben(self, wh, hour): - if self.discharge_array[hour] == 0: + if self.discharge_array[hour] == 0 and self.discharge_array[hour] == -1: return 0.0, 0.0 # No energy discharge and no losses # Calculate the maximum energy that can be discharged considering min_soc and efficiency diff --git a/src/akkudoktoreos/class_ems.py b/src/akkudoktoreos/class_ems.py index f01f488..229bc9d 100644 --- a/src/akkudoktoreos/class_ems.py +++ b/src/akkudoktoreos/class_ems.py @@ -28,6 +28,7 @@ class EnergieManagementSystem: self.akku.set_discharge_per_hour(ds) def set_eauto_charge_hours(self, ds: List[int]) -> None: + self.eauto.set_charge_per_hour(ds) def set_haushaltsgeraet_start(self, ds: List[int], global_start_hour: int = 0) -> None: diff --git a/src/akkudoktoreos/class_optimize.py b/src/akkudoktoreos/class_optimize.py index 64715cc..120f72f 100644 --- a/src/akkudoktoreos/class_optimize.py +++ b/src/akkudoktoreos/class_optimize.py @@ -39,7 +39,7 @@ class optimization_problem: ) -> Tuple[List[int], List[float], Optional[int]]: """ Split the individual solution into its components: - 1. Discharge hours (binary), + 1. Discharge hours (-1 (Charge),0 (Nothing),1 (Discharge)), 2. Electric vehicle charge hours (float), 3. Dishwasher start time (integer if applicable). """ @@ -69,8 +69,8 @@ class optimization_problem: # Initialize toolbox with attributes and operations self.toolbox = base.Toolbox() - self.toolbox.register("attr_bool", random.randint, 0, 1) - self.toolbox.register("attr_float", random.uniform, 0, 1) + self.toolbox.register("attr_discharge_state", random.randint, -1, 1) + self.toolbox.register("attr_ev_charge_index", random.randint, 0, len(moegliche_ladestroeme_in_prozent) - 1) self.toolbox.register("attr_int", random.randint, start_hour, 23) # Register individual creation method based on household appliance parameter @@ -78,8 +78,8 @@ class optimization_problem: self.toolbox.register( "individual", lambda: creator.Individual( - [self.toolbox.attr_bool() for _ in range(self.prediction_hours)] - + [self.toolbox.attr_float() for _ in range(self.prediction_hours)] + [self.toolbox.attr_discharge_state() for _ in range(self.prediction_hours)] + + [self.toolbox.attr_ev_charge_index() for _ in range(self.prediction_hours)] + [self.toolbox.attr_int()] ), ) @@ -87,15 +87,48 @@ class optimization_problem: self.toolbox.register( "individual", lambda: creator.Individual( - [self.toolbox.attr_bool() for _ in range(self.prediction_hours)] - + [self.toolbox.attr_float() for _ in range(self.prediction_hours)] + [self.toolbox.attr_discharge_state() for _ in range(self.prediction_hours)] + + [self.toolbox.attr_ev_charge_index() for _ in range(self.prediction_hours)] ), ) # Register population, mating, mutation, and selection functions self.toolbox.register("population", tools.initRepeat, list, self.toolbox.individual) self.toolbox.register("mate", tools.cxTwoPoint) - self.toolbox.register("mutate", tools.mutFlipBit, indpb=0.1) + #self.toolbox.register("mutate", tools.mutFlipBit, indpb=0.1) + # Register separate mutation functions for each type of value: + # - Discharge state mutation (-1, 0, 1) + self.toolbox.register("mutate_discharge", tools.mutUniformInt, low=0, up=1, indpb=0.1) + # - Float mutation for EV charging values + self.toolbox.register("mutate_ev_charge_index", tools.mutUniformInt, low=0, up=len(moegliche_ladestroeme_in_prozent) - 1, indpb=0.1) + # - Start hour mutation for household devices + self.toolbox.register("mutate_hour", tools.mutUniformInt, low=start_hour, up=23, indpb=0.3) + + # Custom mutation function that applies type-specific mutations + def mutate(individual): + # Mutate the discharge state genes (-1, 0, 1) + individual[:self.prediction_hours], = self.toolbox.mutate_discharge( + individual[:self.prediction_hours] + ) + + # Mutate the EV charging indices + ev_charge_part = individual[self.prediction_hours : self.prediction_hours * 2] + ev_charge_part_mutated, = self.toolbox.mutate_ev_charge_index(ev_charge_part) + individual[self.prediction_hours : self.prediction_hours * 2] = ev_charge_part_mutated + + # Mutate the appliance start hour if present + if len(individual) > self.prediction_hours * 2: + appliance_part = [individual[-1]] + appliance_part_mutated, = self.toolbox.mutate_hour(appliance_part) + individual[-1] = appliance_part_mutated[0] + + return (individual,) + + + # Register custom mutation function + self.toolbox.register("mutate", mutate) + + self.toolbox.register("select", tools.selTournament, tournsize=3) def evaluate_inner( @@ -106,16 +139,22 @@ class optimization_problem: using the provided individual solution. """ ems.reset() - discharge_hours_bin, eautocharge_hours_float, spuelstart_int = self.split_individual( + discharge_hours_bin, eautocharge_hours_index, spuelstart_int = self.split_individual( individual ) if self.opti_param.get("haushaltsgeraete", 0) > 0: ems.set_haushaltsgeraet_start(spuelstart_int, global_start_hour=start_hour) ems.set_akku_discharge_hours(discharge_hours_bin) - eautocharge_hours_float[self.prediction_hours - self.fixed_eauto_hours :] = [ - 0.0 + eautocharge_hours_index[self.prediction_hours - self.fixed_eauto_hours :] = [ + 0 ] * self.fixed_eauto_hours + + eautocharge_hours_float = [ + moegliche_ladestroeme_in_prozent[i] for i in eautocharge_hours_index + ] + + ems.set_eauto_charge_hours(eautocharge_hours_float) return ems.simuliere(start_hour) @@ -134,16 +173,17 @@ class optimization_problem: o = self.evaluate_inner(individual, ems, start_hour) except Exception as e: return (100000.0,) # Return a high penalty in case of an exception - + gesamtbilanz = o["Gesamtbilanz_Euro"] * (-1.0 if worst_case else 1.0) + discharge_hours_bin, eautocharge_hours_float, _ = self.split_individual(individual) max_ladeleistung = np.max(moegliche_ladestroeme_in_prozent) - # Penalty for not discharging + # Small Penalty for not discharging gesamtbilanz += sum( 0.01 for i in range(self.prediction_hours) if discharge_hours_bin[i] == 0.0 ) - + # Penalty for charging the electric vehicle during restricted hours gesamtbilanz += sum( self.strafe @@ -151,13 +191,6 @@ class optimization_problem: if eautocharge_hours_float[i] != 0.0 ) - # Penalty for exceeding maximum charge power - gesamtbilanz += sum( - self.strafe * 10 - for ladeleistung in eautocharge_hours_float - if ladeleistung > max_ladeleistung - ) - # Penalty for not meeting the minimum SOC (State of Charge) requirement if parameter["eauto_min_soc"] - ems.eauto.ladezustand_in_prozent() <= 0.0: gesamtbilanz += sum( @@ -205,7 +238,7 @@ class optimization_problem: self.toolbox, mu=100, lambda_=200, - cxpb=0.5, + cxpb=0.7, mutpb=0.3, ngen=ngen, stats=stats,