diff --git a/CHANGELOG.md b/CHANGELOG.md index 770b6d52..81bbc4b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,9 +62,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ETS forecasts. A median fallback is used when the available history is too short for ETS. ### Changed -- Seed genetic optimization runs with ten exact warm-start copies, twenty locally mutated - warm-start neighbours, and diverse domain-informed battery, direct-marketing, EV, and flexible - appliance schedules to improve early convergence without discarding the previous solution. +- Use a fixed, diverse genetic start population with ten exact warm-start copies, up to fifty + locally mutated warm-start neighbours, up to one hundred randomized domain-informed battery, + direct-marketing, EV, and flexible-appliance schedules, and a guaranteed random remainder. + Retain 150 parents while generating 150 offspring per generation. +- Memoize successful canonical fitness evaluations within one optimization run, including repaired + EV genomes and auxiliary metrics. Log cache hits, misses, key count, and hit rate after each run; + failed evaluations and results from previous runs are never reused. - `max_home_appliances` is now purely an upper bound. No demo appliance is created when no `home_appliances` are configured, and the number is no longer used as an on/off switch. diff --git a/src/akkudoktoreos/optimization/genetic/genetic.py b/src/akkudoktoreos/optimization/genetic/genetic.py index 426691f3..162778e1 100644 --- a/src/akkudoktoreos/optimization/genetic/genetic.py +++ b/src/akkudoktoreos/optimization/genetic/genetic.py @@ -76,6 +76,15 @@ class ApplianceGeneLayout: ) +@dataclass(frozen=True) +class FitnessCacheEntry: + """One canonical, successful fitness evaluation within an optimization run.""" + + genome: tuple[int, ...] + fitness: tuple[float] + extra_data: tuple[float, float, float] + + class GeneticSimulation(PydanticBaseModel): """Device simulation for GENETIC optimization algorithm.""" @@ -537,7 +546,11 @@ class GeneticOptimization(OptimizationBase): """GENETIC algorithm to solve energy optimization.""" WARM_START_COPIES = 10 - WARM_START_MUTATIONS = 20 + WARM_START_MUTATIONS = 50 + EDUCATED_GUESS_TARGET = 100 + MIN_RANDOM_POPULATION_FRACTION = 0.25 + SURVIVOR_COUNT = 150 + OFFSPRING_COUNT = 150 EDUCATED_GUESS_EXPORT_QUANTILES = (0.60, 0.75, 0.90) # Slot-math helpers — single source of truth for the optimization grid. @@ -624,6 +637,14 @@ class GeneticOptimization(OptimizationBase): # Per-run cache for the AC-charge break-even penalty (see evaluate()). self._ac_break_even_best_prices: Optional[list[float]] = None + # Fitness memoization is activated only around optimize(). The cache is + # never shared across runs because forecasts, prices and device state may + # have changed even when the genome is identical. + self._fitness_cache_enabled = False + self._fitness_cache: dict[tuple[int, ...], FitnessCacheEntry] = {} + self._fitness_cache_hits = 0 + self._fitness_cache_misses = 0 + # Appliance genome layout, built once per optimization run in # optimierung_ems(). Empty by default so setup_deap_environment() can be # exercised standalone (e.g. in tests) without appliances. @@ -1247,8 +1268,14 @@ class GeneticOptimization(OptimizationBase): genes.append(min(range(len(gene.allowed_start_slots)), key=opportunity_cost)) return genes - def _educated_guess_individuals(self) -> list[list[int]]: - """Create diverse domain-informed candidates for the initial population.""" + def _educated_guess_individuals( + self, + target_count: int = EDUCATED_GUESS_TARGET, + ) -> list[list[int]]: + """Create a randomized family of domain-informed initial candidates.""" + if target_count <= 0: + return [] + slots = self.total_slots start_slot = self._start_day_slot() len_bat = len(self.bat_possible_charge_values) @@ -1267,6 +1294,7 @@ class GeneticOptimization(OptimizationBase): future_feed_in = feed_in[future] high_import_price = float(np.quantile(future_prices, 0.70)) low_import_price = float(np.quantile(future_prices, 0.25)) + feed_spread = float(np.ptp(future_feed_in)) if future_feed_in.size else 0.0 ev_price = self._heuristic_ev_schedule(prefer_pv=False) ev_pv = self._heuristic_ev_schedule(prefer_pv=True) @@ -1279,56 +1307,127 @@ class GeneticOptimization(OptimizationBase): individual.extend(appliance_genes) return individual - guesses: list[list[int]] = [] + unique: dict[tuple[int, ...], list[int]] = {} + + def add_guess(battery_genes: list[int], ev_genes: list[int]) -> None: + guess = compose(battery_genes, ev_genes) + unique.setdefault(tuple(guess), guess) + + def policy_guess( + *, + import_quantile: float, + export_quantile: Optional[float], + pv_surplus_ratio: float, + allow_ac_arbitrage: bool, + ) -> list[int]: + import_threshold = float(np.quantile(future_prices, import_quantile)) + export_threshold = ( + float(np.quantile(future_feed_in, export_quantile)) + if export_quantile is not None and future_feed_in.size + else float("inf") + ) + low_price_threshold = float( + np.quantile(future_prices, max(0.05, 1.0 - import_quantile)) + ) + battery_genes = [idle_state] * slots + for slot in range(start_slot, slots): + high_feed_in = ( + export_quantile is not None + and self.optimize_battery_grid_export + and feed_spread > 1e-12 + and feed_in[slot] > 0.0 + and feed_in[slot] >= export_threshold + ) + pv_surplus = pv[slot] > load[slot] * pv_surplus_ratio + if high_feed_in: + battery_genes[slot] = export_state + elif self.optimize_dc_charge and pv_surplus: + battery_genes[slot] = dc_allowed_state + elif allow_ac_arbitrage and prices[slot] <= low_price_threshold: + battery_genes[slot] = ac_charge_state + elif prices[slot] >= import_threshold and load[slot] > pv[slot]: + battery_genes[slot] = discharge_state + return battery_genes # Baseline and self-consumption candidates are useful even without # direct marketing and anchor the population with feasible schedules. - guesses.append(compose([idle_state] * slots, ev_price)) - self_consumption = [idle_state] * slots - for slot in range(start_slot, slots): - if self.optimize_dc_charge and pv[slot] > load[slot]: - self_consumption[slot] = dc_allowed_state - elif prices[slot] >= high_import_price and load[slot] > pv[slot]: - self_consumption[slot] = discharge_state - guesses.append(compose(self_consumption, ev_pv)) + add_guess([idle_state] * slots, ev_price) + add_guess( + policy_guess( + import_quantile=0.70, + export_quantile=None, + pv_surplus_ratio=1.0, + allow_ac_arbitrage=False, + ), + ev_pv, + ) # Direct marketing candidates export only in the relatively expensive # feed-in slots. At low tariffs PV is preferentially stored instead. if self.optimize_battery_grid_export and future_feed_in.size: - feed_spread = float(np.ptp(future_feed_in)) for quantile in self.EDUCATED_GUESS_EXPORT_QUANTILES: - export_threshold = float(np.quantile(future_feed_in, quantile)) - direct_marketing = [idle_state] * slots - for slot in range(start_slot, slots): - high_feed_in = ( - feed_spread > 1e-12 - and feed_in[slot] > 0.0 - and feed_in[slot] >= export_threshold - ) - if high_feed_in: - direct_marketing[slot] = export_state - elif self.optimize_dc_charge and pv[slot] > load[slot]: - direct_marketing[slot] = dc_allowed_state - elif prices[slot] >= high_import_price and load[slot] > pv[slot]: - direct_marketing[slot] = discharge_state - guesses.append(compose(direct_marketing, ev_pv)) + add_guess( + policy_guess( + import_quantile=0.70, + export_quantile=quantile, + pv_surplus_ratio=1.0, + allow_ac_arbitrage=False, + ), + ev_pv, + ) inverter = self.simulation.inverter - if inverter is not None and ( + ac_arbitrage_possible = inverter is not None and ( inverter.max_ac_charge_power_w is None or inverter.max_ac_charge_power_w > 0 - ): + ) + if ac_arbitrage_possible: price_arbitrage = [idle_state] * slots for slot in range(start_slot, slots): if prices[slot] <= low_import_price: price_arbitrage[slot] = ac_charge_state elif prices[slot] >= high_import_price: price_arbitrage[slot] = discharge_state - guesses.append(compose(price_arbitrage, ev_price)) + add_guess(price_arbitrage, ev_price) - unique: dict[tuple[int, ...], list[int]] = {} - for guess in guesses: - unique.setdefault(tuple(guess), guess) - return list(unique.values()) + # Randomize policy thresholds rather than merely cloning a handful of + # templates. Every candidate remains policy-safe: a flat/low-information + # feed-in series never acquires export actions through blind mutation. + attempts = max(target_count * 20, 100) + for _ in range(attempts): + export_quantile = ( + random.uniform(0.50, 0.98) # noqa: S311 + if self.optimize_battery_grid_export and feed_spread > 1e-12 + else None + ) + randomized = policy_guess( + import_quantile=random.uniform(0.55, 0.95), # noqa: S311 + export_quantile=export_quantile, + pv_surplus_ratio=random.uniform(0.80, 1.20), # noqa: S311 + allow_ac_arbitrage=ac_arbitrage_possible and random.random() < 0.35, # noqa: S311 + ) + + # Add small policy-safe local variations. These provide diversity + # even when price quantiles collapse to only a few distinct slot + # masks. Export is only ever removed here, never introduced into a + # slot that the tariff policy did not mark as attractive. + future_slots = list(range(start_slot, slots)) + perturbations = random.randint(1, max(2, len(future_slots) // 12)) # noqa: S311 + for slot in random.sample(future_slots, min(perturbations, len(future_slots))): # noqa: S311 + if randomized[slot] != idle_state: + randomized[slot] = idle_state + elif self.optimize_dc_charge and pv[slot] > load[slot]: + randomized[slot] = dc_allowed_state + elif load[slot] > pv[slot] and prices[slot] >= high_import_price: + randomized[slot] = discharge_state + + add_guess( + randomized, + ev_pv if random.random() < 0.5 else ev_price, # noqa: S311 + ) + if len(unique) >= target_count: + break + + return list(unique.values())[:target_count] def _mutated_warm_start_neighbors( self, @@ -1478,6 +1577,49 @@ class GeneticOptimization(OptimizationBase): parameters: GeneticOptimizationParameters, start_hour: int, worst_case: bool, + ) -> tuple[float]: + """Evaluate an individual, using run-local canonical memoization when active.""" + if not self._fitness_cache_enabled: + return self._evaluate_uncached(individual, parameters, start_hour, worst_case) + + original_key = tuple(int(value) for value in individual) + cached = self._fitness_cache.get(original_key) + if cached is not None: + individual[:] = cached.genome + individual.extra_data = cached.extra_data # type: ignore[attr-defined] + self._fitness_cache_hits += 1 + return cached.fitness + + self._fitness_cache_misses += 1 + fitness = self._evaluate_uncached(individual, parameters, start_hour, worst_case) + extra_data = getattr(individual, "extra_data", None) + if extra_data is None: + # Failed evaluations use the sentinel fitness and are intentionally + # not cached: an unexpected transient failure must never become a + # persistent result for the remainder of the run. + return fitness + + canonical_key = tuple(int(value) for value in individual) + extra_value1, extra_value2, extra_value3 = extra_data + entry = FitnessCacheEntry( + genome=canonical_key, + fitness=fitness, + extra_data=( + float(extra_value1), + float(extra_value2), + float(extra_value3), + ), + ) + self._fitness_cache[original_key] = entry + self._fitness_cache[canonical_key] = entry + return fitness + + def _evaluate_uncached( + self, + individual: list[int], + parameters: GeneticOptimizationParameters, + start_hour: int, + worst_case: bool, ) -> tuple[float]: """Evaluate the fitness score of a single individual in the DEAP genetic algorithm. @@ -1519,6 +1661,8 @@ class GeneticOptimization(OptimizationBase): simulation_result = self.evaluate_inner(individual) except Exception: # Return bad fitness score ("FitnessMin") in case of an exception + if hasattr(individual, "extra_data"): + del individual.extra_data # type: ignore[attr-defined] return (100000.0,) gesamtbilanz = simulation_result["Gesamtbilanz_Euro"] * (-1.0 if worst_case else 1.0) @@ -1716,7 +1860,6 @@ class GeneticOptimization(OptimizationBase): individuals = 300 logger.error("Individuals not configured. Using {}.", individuals) - population = self.toolbox.population(n=individuals) hof = tools.HallOfFame(1) stats = tools.Statistics(lambda ind: ind.fitness.values) stats.register("min", np.min) @@ -1725,9 +1868,8 @@ class GeneticOptimization(OptimizationBase): logger.debug("Start optimize: {}", start_solution) - # Insert the start solution into the population if provided and compatible with the - # currently active genome layout. EV optimization adds one gene per prediction slot, - # so a cached solution from a previous run without EV optimization must not be reused. + # Validate the warm start before assigning the fixed population budget. + valid_start_solution: Optional[list[float]] = None if start_solution is not None: n_appliance_genes = self.appliance_layout.n_genes expected_length = ( @@ -1747,38 +1889,87 @@ class GeneticOptimization(OptimizationBase): "appliance layout." ) else: - for _ in range(self.WARM_START_COPIES): - population.insert(0, creator.Individual(start_solution)) - warm_neighbors = self._mutated_warm_start_neighbors( - start_solution, - self.WARM_START_MUTATIONS, - ) - population.extend(creator.Individual(neighbor) for neighbor in warm_neighbors) - logger.info( - "Seeded population with {} exact and {} mutated warm-start solutions.", - self.WARM_START_COPIES, - len(warm_neighbors), - ) + valid_start_solution = start_solution - educated_guesses = self._educated_guess_individuals() - population.extend(creator.Individual(guess) for guess in educated_guesses) + # Keep the configured initial population size fixed. With the default + # 300 individuals this yields 10 exact warm starts, 50 local variants, + # 100 educated guesses and 140 fully random candidates. + minimum_random = max( + int(individuals * self.MIN_RANDOM_POPULATION_FRACTION + 0.999999), + individuals + - ( + self.WARM_START_COPIES + + self.WARM_START_MUTATIONS + + self.EDUCATED_GUESS_TARGET + ), + ) + seed_budget = max(individuals - minimum_random, 0) + seeded: list[list[float]] = [] + + exact_warm_count = 0 + warm_neighbors: list[list[int]] = [] + if valid_start_solution is not None and seed_budget > 0: + exact_warm_count = min(self.WARM_START_COPIES, seed_budget) + seeded.extend([valid_start_solution] * exact_warm_count) + remaining_seed_budget = seed_budget - len(seeded) + warm_neighbors = self._mutated_warm_start_neighbors( + valid_start_solution, + min(self.WARM_START_MUTATIONS, remaining_seed_budget), + ) + seeded.extend(warm_neighbors) + + remaining_seed_budget = seed_budget - len(seeded) + educated_guesses = self._educated_guess_individuals( + min(self.EDUCATED_GUESS_TARGET, remaining_seed_budget) + ) + seeded.extend(educated_guesses) + + random_count = max(individuals - len(seeded), 0) + population = [creator.Individual(seed) for seed in seeded] + population.extend(self.toolbox.population(n=random_count)) logger.info( - "Seeded population with {} educated-guess solutions.", + "Initial population {}: {} exact warm starts, {} warm mutations, " + "{} educated guesses, {} random candidates.", + len(population), + exact_warm_count, + len(warm_neighbors), len(educated_guesses), + random_count, ) - # Run the evolutionary algorithm - pop, log = algorithms.eaMuPlusLambda( - population, - self.toolbox, - mu=100, - lambda_=150, - cxpb=0.6, - mutpb=0.4, - ngen=ngen, - stats=stats, - halloffame=hof, - verbose=self.verbose, + # The memoization scope is exactly one optimizer invocation. Always turn + # it off again, including when DEAP raises, so no later caller can reuse + # results under changed forecasts or device state. + self._fitness_cache.clear() + self._fitness_cache_hits = 0 + self._fitness_cache_misses = 0 + self._fitness_cache_enabled = True + try: + pop, log = algorithms.eaMuPlusLambda( + population, + self.toolbox, + mu=self.SURVIVOR_COUNT, + lambda_=self.OFFSPRING_COUNT, + cxpb=0.6, + mutpb=0.4, + ngen=ngen, + stats=stats, + halloffame=hof, + verbose=self.verbose, + ) + finally: + self._fitness_cache_enabled = False + + cache_lookups = self._fitness_cache_hits + self._fitness_cache_misses + cache_hit_rate = ( + self._fitness_cache_hits / cache_lookups if cache_lookups > 0 else 0.0 + ) + logger.info( + "Fitness cache: {} hits, {} misses, {:.1%} hit rate, {} keys.", + self._fitness_cache_hits, + self._fitness_cache_misses, + cache_hit_rate, + len(self._fitness_cache), ) # Store fitness history @@ -1787,6 +1978,12 @@ class GeneticOptimization(OptimizationBase): "avg": log.select("avg"), # Average fitness for each generation (Y-axis) "max": log.select("max"), # Maximum fitness for each generation (Y-axis) "min": log.select("min"), # Minimum fitness for each generation (Y-axis) + "fitness_cache": { + "hits": self._fitness_cache_hits, + "misses": self._fitness_cache_misses, + "hit_rate": cache_hit_rate, + "keys": len(self._fitness_cache), + }, } member: dict[str, list[float]] = {"bilanz": [], "verluste": [], "nebenbedingung": []} diff --git a/tests/test_genetic_seeding.py b/tests/test_genetic_seeding.py index 172e29c7..cf6460ff 100644 --- a/tests/test_genetic_seeding.py +++ b/tests/test_genetic_seeding.py @@ -52,6 +52,63 @@ def test_ev_repair_is_resimulated_before_fitness_assignment(config_eos: ConfigEO assert individual[opt.total_slots :] == [0] * opt.total_slots +def test_fitness_cache_restores_canonical_ev_genome(config_eos: ConfigEOS): + _configure_hourly_grid(config_eos) + opt = GeneticOptimization(fixed_seed=42) + opt.optimize_ev = True + opt.ev_possible_charge_values = [0.0, 1.0] + opt.setup_deap_environment({"home_appliance": 0}, start_hour=0) + parameters = SimpleNamespace( + ems=SimpleNamespace(preis_euro_pro_wh_akku=0.0), + eauto=None, + ) + result = { + "Gesamtbilanz_Euro": 1.0, + "Gesamt_Verluste": 0.0, + "EAuto_SoC_pro_Stunde": np.full(opt.total_slots, 100.0), + } + first = creator.Individual([0] * opt.total_slots + [1] * opt.total_slots) + duplicate = creator.Individual(first) + opt._fitness_cache_enabled = True + + with patch.object(opt, "evaluate_inner", return_value=result) as evaluate: + first_fitness = opt.evaluate(first, parameters, 0, False) # type: ignore[arg-type] + duplicate_fitness = opt.evaluate(duplicate, parameters, 0, False) # type: ignore[arg-type] + + # The miss evaluates and then re-evaluates the repaired EV plan. The duplicate + # is served directly from the original-key alias and receives the canonical genome. + assert evaluate.call_count == 2 + assert first_fitness == duplicate_fitness + assert duplicate == first + assert duplicate[opt.total_slots :] == [0] * opt.total_slots + assert duplicate.extra_data == first.extra_data + assert opt._fitness_cache_hits == 1 + assert opt._fitness_cache_misses == 1 + + +def test_fitness_cache_never_stores_failed_evaluations(config_eos: ConfigEOS): + _configure_hourly_grid(config_eos) + opt = GeneticOptimization(fixed_seed=42) + opt.optimize_ev = False + opt.setup_deap_environment({"home_appliance": 0}, start_hour=0) + parameters = SimpleNamespace( + ems=SimpleNamespace(preis_euro_pro_wh_akku=0.0), + eauto=None, + ) + first = creator.Individual([0] * opt.total_slots) + duplicate = creator.Individual(first) + opt._fitness_cache_enabled = True + + with patch.object(opt, "evaluate_inner", side_effect=RuntimeError("transient")) as evaluate: + assert opt.evaluate(first, parameters, 0, False) == (100000.0,) # type: ignore[arg-type] + assert opt.evaluate(duplicate, parameters, 0, False) == (100000.0,) # type: ignore[arg-type] + + assert evaluate.call_count == 2 + assert opt._fitness_cache_hits == 0 + assert opt._fitness_cache_misses == 2 + assert opt._fitness_cache == {} + + def test_mutated_warm_start_neighbors_keep_elapsed_slots(config_eos: ConfigEOS): _configure_hourly_grid(config_eos, start_hour=10) opt = GeneticOptimization(fixed_seed=42) @@ -67,6 +124,50 @@ def test_mutated_warm_start_neighbors_keep_elapsed_slots(config_eos: ConfigEOS): assert all(neighbor != start_solution for neighbor in neighbors) +def test_initial_population_uses_fixed_seed_budget_and_150_survivors(config_eos: ConfigEOS): + _configure_hourly_grid(config_eos) + config_eos.optimization.genetic.individuals = 300 + opt = GeneticOptimization(fixed_seed=42) + opt.optimize_ev = False + opt.setup_deap_environment({"home_appliance": 0}, start_hour=0) + start_solution = [5] * opt.total_slots + warm_neighbors = [[6] * opt.total_slots for _ in range(50)] + educated = [[7] * opt.total_slots for _ in range(100)] + captured: dict[str, object] = {} + + def fake_ea(population, toolbox, **kwargs): + captured["population"] = list(population) + captured["mu"] = kwargs["mu"] + captured["lambda"] = kwargs["lambda_"] + for individual in population: + individual.fitness.values = (float(sum(individual)),) + individual.extra_data = (0.0, 0.0, 0.0) + kwargs["halloffame"].update(population) + return population, SimpleNamespace(select=lambda _name: []) + + with ( + patch.object(opt, "_mutated_warm_start_neighbors", return_value=warm_neighbors), + patch.object(opt, "_educated_guess_individuals", return_value=educated), + patch.object( + opt.toolbox, + "population", + side_effect=lambda n: [creator.Individual([9] * opt.total_slots) for _ in range(n)], + ), + patch("akkudoktoreos.optimization.genetic.genetic.algorithms.eaMuPlusLambda", fake_ea), + ): + opt.optimize(start_solution=start_solution, ngen=1) + + population = captured["population"] + first_genes = [individual[0] for individual in population] # type: ignore[union-attr] + assert len(population) == 300 # type: ignore[arg-type] + assert first_genes.count(5) == 10 + assert first_genes.count(6) == 50 + assert first_genes.count(7) == 100 + assert first_genes.count(9) == 140 + assert captured["mu"] == 150 + assert captured["lambda"] == 150 + + def test_educated_guesses_encode_high_price_direct_marketing(config_eos: ConfigEOS): _configure_hourly_grid(config_eos) opt = GeneticOptimization(fixed_seed=42) @@ -86,7 +187,7 @@ def test_educated_guesses_encode_high_price_direct_marketing(config_eos: ConfigE dc_allowed_state = 4 export_state = 5 - assert len(guesses) >= 4 + assert len(guesses) == opt.EDUCATED_GUESS_TARGET assert all(len(guess) == slots for guess in guesses) assert any(guess[0] == dc_allowed_state for guess in guesses) assert any(guess[-1] == export_state for guess in guesses) diff --git a/tests/testdata/optimize_result_1.json b/tests/testdata/optimize_result_1.json index 98e2c3ee..d0995b71 100644 --- a/tests/testdata/optimize_result_1.json +++ b/tests/testdata/optimize_result_1.json @@ -14,13 +14,6 @@ 0.0, 0.0, 0.0, - 1.0, - 1.0, - 0.0, - 1.0, - 1.0, - 0.0, - 1.0, 0.0, 0.0, 0.0, @@ -40,11 +33,18 @@ 0.0, 0.0, 0.0, - 1.0, - 1.0, 0.0, - 1.0, - 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, 0.0, 0.0, 0.0 @@ -110,9 +110,7 @@ 0, 0, 0, - 1, - 1, - 1, + 0, 0, 0, 0, @@ -122,31 +120,33 @@ 0, 0, 1, + 1, + 1, 0, 0, 1, 1, 1, 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, 0, 0, 0, - 1, - 1, - 1, - 1, - 1, 0, - 1, - 1, 0, 0, 0, 1, - 0, - 0, - 0, 1, + 1, + 0, 0 ], "battery_grid_export_allowed": [], @@ -157,7 +157,7 @@ 1063.91, 1320.56, 1132.03, - 1308.5200000000004, + 1163.67, 1176.82, 1216.22, 1103.78, @@ -237,13 +237,11 @@ 0.0, 0.0, 0.0, - 0.0, - 0.21077535122459587, + 0.022582049506752234, + 0.3039575, 0.19320652266312205, 0.1358062627100041, 0.0692592282596561, - 0.023370695807696434, - 0.0019327051509204403, 0.0, 0.0, 0.0, @@ -262,20 +260,22 @@ 0.0, 0.0, 0.0, - 0.18814608875566813, + 0.0, + 0.0, + 0.09514375330616998, 0.15236390731688437, 0.10316291465819699, - 0.029150936607659956, - 0.020928608511559126, - 0.003524558735906156, + 0.05435777788576338, + 0.0, + 0.0, 0.0, 0.0, 0.0 ], - "Gesamt_Verluste": 3290.8745999936527, - "Gesamtbilanz_Euro": 1.251565700139288, - "Gesamteinnahmen_Euro": 1.1316277804018695, - "Gesamtkosten_Euro": 2.3831934805411574, + "Gesamt_Verluste": 3421.6165248449383, + "Gesamtbilanz_Euro": 0.5951993628823129, + "Gesamteinnahmen_Euro": 1.1298399163065491, + "Gesamtkosten_Euro": 1.725039279188862, "Home_appliance_wh_per_hour": [ 0.0, 0.0, @@ -318,17 +318,17 @@ ], "home_appliance_energy_wh": {}, "Kosten_Euro_pro_Stunde": [ - 0.0, - 0.0, - 0.0, + 0.10013413727316416, + 0.09007999454232955, + 0.11436917344552285, 0.07557452231671152, - 0.026623430000000066, + 0.0, 4.55656845588237e-17, 0.001414013162203277, 0.005881449073870462, 0.05258762370598476, - 0.1614775630079859, - 0.2338232746714084, + 0.0, + 0.0, 0.0, 0.26650619799999997, 0.19588158, @@ -336,39 +336,39 @@ 0.0, 0.0, 0.0, - 0.182970359, - 0.162995926, - 0.16677339, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.1306329312971816, + 0.07362195915902499, + 0.060401289430882174, 0.009619897970888898, - 0.0, - 0.0, + 0.07029121023060134, + 4.179128154646605e-17, 2.3325608707865465e-05, 0.0021886029750169123, 0.013012984677295973, + 0.08357424731947552, 0.0, - 0.17784012884918773, - 0.19011028252189552, - 0.293043269, 0.0, + 0.0, + 0.214398479, 0.16484566 ], "Netzbezug_Wh_pro_Stunde": [ - 0.0, - 0.0, - 0.0, + 439.1848126015972, + 407.23324838304495, + 546.436566868241, 402.20607938643707, - 144.85000000000036, + 0.0, 2.2737367544323206e-13, 6.433180901743754, 25.909467285772962, 175.4675465665157, - 505.40708296709204, - 758.9200735845777, + 0.0, + 0.0, 0.0, 912.38, 704.61, @@ -376,25 +376,25 @@ 0.0, 0.0, 0.0, - 556.31, - 488.89, - 506.91, 0.0, 0.0, 0.0, 0.0, 0.0, + 537.58407941227, + 322.9033296448464, + 273.0618871197205, 45.96224544141853, - 0.0, - 0.0, + 374.088399311343, + 2.2737367544323206e-13, 0.11639525303326081, 9.957247384062384, 57.32592368852852, + 278.85968408233407, 0.0, - 556.6201215937018, - 617.0408390843736, - 987.01, 0.0, + 0.0, + 733.99, 592.97 ], "Netzeinspeisung_Wh_pro_Stunde": [ @@ -402,13 +402,11 @@ 0.0, 0.0, 0.0, - 0.0, - 3011.0764460656555, + 322.60070723931767, + 4342.25, 2760.093180901744, 1940.089467285773, 989.4175465665157, - 333.86708296709196, - 27.61007358457772, 0.0, 0.0, 0.0, @@ -427,28 +425,30 @@ 0.0, 0.0, 0.0, - 2687.8012679381163, + 0.0, + 0.0, + 1359.1964758024283, 2176.6272473840627, 1473.7559236885286, - 416.4419515379994, - 298.9801215937018, - 50.35083908437366, + 776.5396840823341, + 0.0, + 0.0, 0.0, 0.0, 0.0 ], "Verluste_Pro_Stunde": [ - 97.85621559422765, - 101.92059640365329, - 114.42806532440363, + 37.96737751219166, + 46.38878980596536, + 39.91398802418894, 51.82392952637247, - 599.9999999999995, - 159.7408264721214, - 0.0, + 543.9059151312817, 0.0, 0.0, 0.0, 0.0, + 108.98319763338179, + 106.80230977350088, 133.7321802766326, 0.0, 0.0, @@ -456,66 +456,66 @@ 118.37045454545455, 94.68272727272722, 83.01681818181817, - 0.0, - 0.0, - 0.0, + 75.86045454545456, + 66.66681818181814, + 69.12409090909085, 109.07302361034766, 116.99491129525349, - 95.61900944932736, - 98.21987178167876, - 123.8591383343284, + 22.312089529472388, + 54.18759955738153, + 86.6234264543665, 165.15986945297027, - 116.9350623689079, + 65.92300791736113, 538.2984000000001, - 119.40181527778998, - 0.0, - 0.0, - 81.2380484620021, + 278.8343903340726, 0.0, 0.0, 0.0, - 100.08954545454549, + 111.78035844493081, + 90.18403329253945, + 134.59227272727276, + 0.0, 0.0 ], "akku_soc_pro_stunde": [ 80.0, - 79.16421888032488, - 78.69989843940196, - 77.45653455559739, - 78.89608815355218, - 95.56275482021886, + 81.05464937533866, + 82.3432268699488, + 83.45194875950959, + 84.8915023574644, 100.0, 100.0, 100.0, 100.0, 100.0, - 100.0, - 95.77874174137638, - 95.77874174137638, - 95.77874174137638, - 93.5560747303571, - 89.81963361465462, - 86.83091116286398, - 84.21043079096314, - 84.21043079096314, - 84.21043079096314, - 84.21043079096314, - 80.76754055001486, - 77.26987043147221, - 75.57566963810356, - 75.69097315408206, - 76.9218097513005, - 81.5095839027719, - 81.73054957561693, - 96.68328290895028, + 98.93741213017658, + 95.76274429012544, + 91.54148603150185, + 91.54148603150185, + 91.54148603150185, + 89.31881902048255, + 85.58237790478007, + 82.59365545298944, + 79.97317508108861, + 77.57859002599218, + 75.47420813893983, + 73.29226082489025, + 69.84937058394196, + 66.35170046539932, + 66.97148073010689, + 68.47669182892304, + 70.8828981193221, + 75.4706722707935, + 77.30186693516465, + 92.254600268498, 100.0, 100.0, 100.0, 100.0, - 100.0, - 100.0, - 100.0, - 96.84060778236915 + 98.60068046043587, + 96.11252124341868, + 91.86402778611841, + 91.86402778611841 ], "Electricity_price": [ 0.000228, @@ -711,51 +711,51 @@ "start_solution": [ 0.0, 0.0, - 1.0, - 2.0, 0.0, - 2.0, - 2.0, - 1.0, - 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 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, 1.0, 1.0, 0.0, - 2.0, - 2.0, - 0.0, - 2.0, - 2.0, - 0.0, - 2.0, - 1.0, - 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, - 0.0, - 0.0, - 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, 1.0, 1.0, 0.0, - 2.0, - 2.0, - 1.0, - 2.0, - 2.0, - 0.0, - 1.0, 0.0 ], "washingstart": null, diff --git a/tests/testdata/optimize_result_1_be.json b/tests/testdata/optimize_result_1_be.json index ada69726..ebc525bd 100644 --- a/tests/testdata/optimize_result_1_be.json +++ b/tests/testdata/optimize_result_1_be.json @@ -110,9 +110,10 @@ 0, 0, 0, + 1, + 1, 0, - 0, - 0, + 1, 0, 0, 0, @@ -123,7 +124,7 @@ 1, 0, 0, - 0, + 1, 1, 1, 1, @@ -142,10 +143,9 @@ 0, 0, 0, - 0, 1, 1, - 0, + 1, 0, 0 ], @@ -237,11 +237,12 @@ 0.0, 0.0, 0.0, - 0.022582049506752234, - 0.3039575, + 0.0, + 0.21367321450420126, 0.19320652266312205, 0.1358062627100041, 0.0692592282596561, + 0.023370695807696434, 0.0, 0.0, 0.0, @@ -261,8 +262,7 @@ 0.0, 0.0, 0.0, - 0.0013652045768039896, - 0.2577971476677123, + 0.11745809857246167, 0.15236390731688437, 0.10316291465819699, 0.05435777788576338, @@ -272,10 +272,10 @@ 0.0, 0.0 ], - "Gesamt_Verluste": 2758.4157389677953, - "Gesamtbilanz_Euro": 1.2690402398027016, - "Gesamteinnahmen_Euro": 1.2938585152448954, - "Gesamtkosten_Euro": 2.562898755047597, + "Gesamt_Verluste": 3638.132237848992, + "Gesamtbilanz_Euro": 0.5580695656866563, + "Gesamteinnahmen_Euro": 1.0626586223779864, + "Gesamtkosten_Euro": 1.6207281880646427, "Home_appliance_wh_per_hour": [ 0.0, 0.0, @@ -318,18 +318,18 @@ ], "home_appliance_energy_wh": {}, "Kosten_Euro_pro_Stunde": [ - 0.10013413727316416, - 0.09007999454232955, + 0.0, + 0.0, 0.11436917344552285, - 0.07557452231671152, + 0.0, 0.0, 4.55656845588237e-17, 0.001414013162203277, 0.005881449073870462, 0.05258762370598476, + 0.1614775630079859, 0.0, 0.0, - 0.29116746986009023, 0.26650619799999997, 0.19588158, 0.0, @@ -340,7 +340,7 @@ 0.0, 0.0, 0.0, - 0.25364873699864443, + 0.0, 0.1306329312971816, 0.07362195915902499, 0.060401289430882174, @@ -353,23 +353,23 @@ 0.08357424731947552, 0.0, 0.0, - 0.293043269, + 0.0, 0.214398479, 0.16484566 ], "Netzbezug_Wh_pro_Stunde": [ - 439.1848126015972, - 407.23324838304495, + 0.0, + 0.0, 546.436566868241, - 402.20607938643707, + 0.0, 0.0, 2.2737367544323206e-13, 6.433180901743754, 25.909467285772962, 175.4675465665157, + 505.40708296709204, 0.0, 0.0, - 980.6920507244535, 912.38, 704.61, 0.0, @@ -380,7 +380,7 @@ 0.0, 0.0, 0.0, - 833.8222781020527, + 0.0, 537.58407941227, 322.9033296448464, 273.0618871197205, @@ -393,7 +393,7 @@ 278.85968408233407, 0.0, 0.0, - 987.01, + 0.0, 733.99, 592.97 ], @@ -402,11 +402,12 @@ 0.0, 0.0, 0.0, - 322.60070723931767, - 4342.25, + 0.0, + 3052.474492917161, 2760.093180901744, 1940.089467285773, 989.4175465665157, + 333.86708296709196, 0.0, 0.0, 0.0, @@ -426,8 +427,7 @@ 0.0, 0.0, 0.0, - 19.502922525771282, - 3682.816395253033, + 1677.9728367494527, 2176.6272473840627, 1473.7559236885286, 776.5396840823341, @@ -438,18 +438,18 @@ 0.0 ], "Verluste_Pro_Stunde": [ - 37.96737751219166, - 46.38878980596536, + 97.85621559422765, + 101.92059640365329, 39.91398802418894, - 51.82392952637247, - 543.9059151312817, + 106.67021307906845, + 582.6179999999995, + 154.77306084994075, 0.0, 0.0, 0.0, 0.0, - 108.98319763338179, 106.80230977350088, - 0.0014460869344160802, + 133.7321802766326, 0.0, 0.0, 70.41409090909087, @@ -460,62 +460,62 @@ 66.66681818181814, 69.12409090909085, 109.07302361034766, - 3.2918733722463145, + 116.99491129525349, 22.312089529472388, 54.18759955738153, 86.6234264543665, 165.15986945297027, 65.92300791736113, - 535.9580492969076, - 0.0, + 538.2984000000001, + 240.58122702042965, 0.0, 0.0, 0.0, 111.78035844493081, 90.18403329253945, - 0.0, + 134.59227272727276, 0.0, 0.0 ], "akku_soc_pro_stunde": [ 80.0, - 81.05464937533866, - 82.3432268699488, - 83.45194875950959, - 84.8915023574644, + 79.16421888032488, + 78.69989843940196, + 79.80862032896276, + 79.51691497639054, + 95.70074830972388, 100.0, 100.0, 100.0, 100.0, 100.0, - 98.93741213017658, - 95.76274429012544, - 95.76278445920696, - 95.76278445920696, - 95.76278445920696, - 93.54011744818767, - 89.80367633248518, - 86.81495388069455, - 84.19447350879372, - 81.79988845369729, - 79.69550656664495, - 77.51355925259536, - 74.07066901164707, - 74.16210993865391, - 74.78189020336148, - 76.28710130217763, - 78.6933075925767, - 83.2810817440481, - 85.11227640841923, - 100.0, + 96.82533215994886, + 92.60407390132525, + 92.60407390132525, + 92.60407390132525, + 90.38140689030597, + 86.64496577460349, + 83.65624332281286, + 81.03576295091202, + 78.64117789581559, + 76.53679600876325, + 74.35484869471367, + 70.91195845376538, + 67.41428833522274, + 68.03406859993031, + 69.53927969874645, + 71.94548598914552, + 76.53326014061692, + 78.36445480498806, + 93.3171881383214, 100.0, 100.0, 100.0, 100.0, 98.60068046043587, 96.11252124341868, - 96.11252124341868, - 96.11252124341868 + 91.86402778611841, + 91.86402778611841 ], "Electricity_price": [ 0.000228, @@ -717,11 +717,12 @@ 0.0, 0.0, 0.0, + 2.0, + 2.0, + 1.0, + 1.0, 0.0, - 0.0, - 0.0, - 0.0, - 0.0, + 1.0, 0.0, 0.0, 0.0, @@ -732,7 +733,7 @@ 1.0, 0.0, 0.0, - 0.0, + 1.0, 1.0, 1.0, 1.0, @@ -751,10 +752,9 @@ 0.0, 0.0, 0.0, - 0.0, 1.0, 1.0, - 0.0, + 1.0, 0.0, 0.0 ], diff --git a/tests/testdata/optimize_result_2.json b/tests/testdata/optimize_result_2.json index 2cf68cfe..86cdf480 100644 --- a/tests/testdata/optimize_result_2.json +++ b/tests/testdata/optimize_result_2.json @@ -14,14 +14,6 @@ 0.0, 0.0, 0.0, - 1.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0, 0.0, 0.0, 0.0, @@ -39,13 +31,21 @@ 0.0, 0.0, 0.0, - 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, 0.0, 0.0 ], @@ -111,43 +111,43 @@ 0, 0, 0, - 1, - 0, - 0, - 0, - 1, - 0, - 1, - 1, - 0, - 1, 0, 0, 0, 0, - 1, + 0, + 0, + 0, + 0, 0, 1, 1, 0, 0, 1, - 0, - 0, 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, 0, 0, 1, 1, - 0, - 1, 1, 0, - 1, - 1, - 0, - 1, - 1 + 0 ], "battery_grid_export_allowed": [], "eautocharge_hours_float": [ @@ -161,14 +161,14 @@ 0.0, 0.0, 0.0, - 1.0, - 0.5, + 0.875, + 0.625, 0.625, 0.375, + 0.875, 0.75, 0.75, 0.375, - 0.875, 0.1, 0.0, 0.0, @@ -202,18 +202,18 @@ ], "result": { "Last_Wh_pro_Stunde": [ - 11541.07, - 6307.91, + 10230.07, + 7618.91, 7875.5599999999995, - 5065.03, - 13622.08822093064, + 7565.03, + 12840.67, 9042.82, - 7649.22, - 12780.779999999999, + 9082.22, + 5036.78, 2177.92, 1178.71, 1050.98, - 5988.547949275546, + 988.56, 912.38, 704.61, 516.37, @@ -231,25 +231,25 @@ 827.01, 1257.98, 1232.67, - 2188.443604746967, + 871.26, 860.88, 1158.03, 1222.72, 1221.04, 949.99, - 1987.01, + 987.01, 733.99, 592.97 ], "EAuto_SoC_pro_Stunde": [ 5.0, - 22.48, + 20.294999999999998, 31.22, 42.144999999999996, 48.699999999999996, - 61.809999999999995, - 74.92, - 81.475, + 63.995000000000005, + 77.105, + 90.215, 96.77, 98.518, 98.518, @@ -313,22 +313,19 @@ 0.0, 0.0, 0.0, - 0.0, - 0.0, + 0.05936927575872234, + 0.05435777788576338, 0.0, 0.0, 0.0, 0.0, 0.0 ], - "Gesamt_Verluste": 9279.931765999105, - "Gesamtbilanz_Euro": 12.822538076242544, - "Gesamteinnahmen_Euro": 0.0, - "Gesamtkosten_Euro": 12.822538076242544, + "Gesamt_Verluste": 6227.580163897914, + "Gesamtbilanz_Euro": 11.420868526053463, + "Gesamteinnahmen_Euro": 0.11372705364448572, + "Gesamtkosten_Euro": 11.534595579697948, "Home_appliance_wh_per_hour": [ - 0.0, - 0.0, - 0.0, 0.0, 0.0, 0.0, @@ -363,13 +360,13 @@ 0.0, 0.0, 0.0, + 0.0, + 0.0, + 0.0, 0.0 ], "home_appliance_energy_wh": { "dishwasher1": [ - 0.0, - 0.0, - 0.0, 0.0, 0.0, 0.0, @@ -404,88 +401,91 @@ 0.0, 0.0, 0.0, + 0.0, + 0.0, + 0.0, 0.0 ] }, "Kosten_Euro_pro_Stunde": [ - 2.41926012, - 0.19137741085396395, + 2.1203521199999997, + 1.4545721927072854, 1.4167558060312964, - 0.7340784901492668, - 1.4723942300000001, - 0.0, - 0.8450811735885355, - 1.2197241405944232, - 0.0, + 1.2032659414129376, + 1.2892826874611185, + 0.770122611209644, + 1.1459236583154115, + 0.4965507655670841, + 0.1912938683161112, 0.1614775630079859, 0.0, - 1.7756638919999999, + 0.0, 0.26650619799999997, 0.19588158, - 0.174739608, - 0.0, - 0.22802125600000003, 0.0, 0.0, - 0.162995926, - 0.16677339, 0.0, - 0.25364873699864443, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, 0.1306329312971816, - 0.0, + 0.07362195915902499, 0.060401289430882174, 0.009619897970888898, - 0.0, - 0.0, - 0.26398692, - 0.0, - 0.0, + 0.07029121023060134, + 4.179128154646605e-17, + 2.3325608707865465e-05, + 0.0021886029750169123, + 0.013012984677295973, 0.08357424731947552, 0.0, 0.0, - 0.589943269, 0.0, - 0.0 + 0.214398479, + 0.16484566 ], "Netzbezug_Wh_pro_Stunde": [ - 10610.789999999999, - 865.1781684175585, + 9299.789999999999, + 6575.823656000386, 6769.01961792306, - 3906.7508789210588, - 8010.85, - 0.0, - 3844.7733102299158, - 5373.234099534904, - 0.0, + 6403.757005923032, + 7014.595688036554, + 3842.9272016449304, + 5213.483431826258, + 2187.4483064629258, + 638.2845122326032, 505.40708296709204, 0.0, - 5980.679999999999, + 0.0, 912.38, 704.61, - 516.37, - 0.0, - 694.34, 0.0, 0.0, - 488.89, - 506.91, 0.0, - 833.8222781020527, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, 537.58407941227, - 0.0, + 322.9033296448464, 273.0618871197205, 45.96224544141853, - 0.0, - 0.0, - 1317.3000000000002, - 0.0, - 0.0, + 374.088399311343, + 2.2737367544323206e-13, + 0.11639525303326081, + 9.957247384062384, + 57.32592368852852, 278.85968408233407, 0.0, 0.0, - 1987.01, 0.0, - 0.0 + 733.99, + 592.97 ], "Netzeinspeisung_Wh_pro_Stunde": [ 0.0, @@ -519,8 +519,8 @@ 0.0, 0.0, 0.0, - 0.0, - 0.0, + 848.1325108388907, + 776.5396840823341, 0.0, 0.0, 0.0, @@ -528,84 +528,84 @@ 0.0 ], "Verluste_Pro_Stunde": [ - 552.0, - 876.0621802101069, + 483.0, + 345.0196387200463, 345.0239541507672, - 207.4093054705271, - 1013.9999999999998, - 976.3367916944278, - 226.85199722758986, - 1084.2496919441885, - 190.65093859054008, + 207.05004071076388, + 506.1294825643864, + 452.3012641973916, + 426.13721181915116, + 227.23539677555112, + 103.61214146791241, 40.06404995605101, 106.80230977350088, - 600.0000000000001, - 0.0, + 133.7321802766326, 0.0, 0.0, + 70.41409090909087, 118.37045454545455, - 0.0, + 94.68272727272722, 83.01681818181817, 75.86045454545456, - 0.0, - 0.0, + 66.66681818181814, + 69.12409090909085, 109.07302361034766, - 3.2918733722463145, + 116.99491129525349, 22.312089529472388, - 98.21987178167876, + 54.18759955738153, 86.6234264543665, 165.15986945297027, - 116.9350623689079, + 65.92300791736113, 538.2984000000001, - 600.0000000000002, - 262.55307614755066, - 184.66788225469543, - 93.18476208988011, + 441.9379674303641, + 261.1952696860876, + 75.0748095419566, + 0.0, 111.78035844493081, 90.18403329253945, - 120.0, - 100.08954545454549, - 80.85954545454547 + 134.59227272727276, + 0.0, + 0.0 ], "akku_soc_pro_stunde": [ 80.0, 80.0, - 61.0623332886646, - 61.06299868174146, - 61.074368278144995, - 77.74103494481164, - 62.263433461120634, - 62.81487782855368, - 43.910197554276095, - 42.50754248385738, - 43.62043276041435, - 40.44576492036321, - 57.11243158702987, - 57.11243158702987, - 57.11243158702987, - 57.11243158702987, - 53.3759904713274, - 53.3759904713274, - 50.75551009942657, - 48.36092504433015, - 48.36092504433015, - 48.36092504433015, - 44.918034803381865, - 45.0094757303887, - 45.629255995096266, - 45.744559511074755, - 48.150765801473824, - 52.73853995294522, - 52.95950562579026, - 67.9122389591236, - 84.57890562579026, - 91.79146973129197, - 96.45723532881205, - 99.04570094241983, - 97.64638140285571, - 95.1582221858385, - 98.49155551917185, - 95.332163301541 + 80.00054552000128, + 80.00121091307815, + 80.0026009328216, + 80.6450865596101, + 81.70901056509321, + 82.04615533784741, + 82.60824969272383, + 83.95303140016584, + 85.06592167672281, + 81.89125383667168, + 77.66999557804807, + 77.66999557804807, + 77.66999557804807, + 75.44732856702878, + 71.71088745132629, + 68.72216499953565, + 66.10168462763482, + 63.7070995725384, + 61.60271768548606, + 59.42077037143647, + 55.97788013048819, + 52.48021001194556, + 53.09999027665313, + 54.60520137546928, + 57.01140766586834, + 61.59918181733974, + 63.43037648171088, + 78.38310981504422, + 90.65916446588767, + 97.91458862383455, + 100.0, + 100.0, + 98.60068046043587, + 96.11252124341868, + 91.86402778611841, + 91.86402778611841 ], "Electricity_price": [ 0.000228, @@ -702,14 +702,14 @@ 0.0, 0.0, 0.0, - 1.0, - 0.5, + 0.875, + 0.625, 0.625, 0.375, + 0.875, 0.75, 0.75, 0.375, - 0.875, 0.1, 0.0, 0.0, @@ -800,107 +800,107 @@ }, "start_solution": [ 0.0, - 1.0, - 2.0, - 1.0, - 0.0, - 2.0, - 0.0, - 2.0, - 0.0, - 2.0, - 0.0, - 1.0, - 0.0, - 0.0, - 2.0, - 1.0, - 0.0, - 1.0, - 1.0, - 0.0, - 1.0, - 2.0, 0.0, 0.0, 0.0, - 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 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, 1.0, 0.0, 0.0, 1.0, - 0.0, - 0.0, 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, 0.0, 0.0, 1.0, 1.0, - 2.0, - 1.0, 1.0, 0.0, - 1.0, - 1.0, - 2.0, - 1.0, - 1.0, - 6.0, - 5.0, - 4.0, + 0.0, 3.0, 6.0, - 5.0, - 5.0, - 6.0, - 1.0, - 0.0, - 6.0, 2.0, - 3.0, - 1.0, - 4.0, 4.0, + 5.0, + 6.0, + 0.0, 1.0, + 2.0, + 0.0, 5.0, 3.0, - 2.0, - 0.0, - 6.0, - 5.0, - 6.0, - 2.0, - 5.0, - 2.0, - 5.0, - 4.0, - 4.0, - 6.0, - 5.0, - 6.0, - 2.0, - 0.0, - 4.0, - 6.0, - 1.0, - 2.0, - 1.0, 3.0, 1.0, - 0.0, + 5.0, 4.0, 4.0, - 5.0, - 5.0, 1.0, - 6.0 + 1.0, + 2.0, + 2.0, + 1.0, + 4.0, + 6.0, + 4.0, + 5.0, + 6.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 3.0 ], - "washingstart": 16, + "washingstart": 13, "appliance_starts": { "dishwasher1": [ - "2025-01-15 16:00:00+01:00" + "2025-01-15 13:00:00+01:00" ] } } \ No newline at end of file diff --git a/tests/testdata/optimize_result_2_be.json b/tests/testdata/optimize_result_2_be.json index ae3105dc..5d232d51 100644 --- a/tests/testdata/optimize_result_2_be.json +++ b/tests/testdata/optimize_result_2_be.json @@ -13,20 +13,6 @@ 0.0, 0.0, 0.0, - 1.0, - 1.0, - 0.0, - 1.0, - 1.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0, - 1.0, 0.0, 0.0, 0.0, @@ -36,7 +22,21 @@ 0.0, 0.0, 0.0, - 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, 0.0, 0.0, 0.0, @@ -110,33 +110,21 @@ 0, 0, 0, - 0, 1, - 0, - 0, - 0, 1, 0, 0, 0, 0, 0, - 1, 0, 0, - 1, - 0, 0, 1, 1, 0, 0, 1, - 0, - 0, - 1, - 0, - 0, 1, 1, 1, @@ -145,8 +133,20 @@ 1, 1, 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, 1, 1, + 0, 0 ], "battery_grid_export_allowed": [], @@ -162,13 +162,13 @@ 0.0, 0.0, 1.0, + 0.5, 0.75, - 0.625, + 0.875, + 0.5, 0.375, - 0.75, - 0.75, + 1.0, 0.375, - 0.8, 0.0, 0.0, 0.0, @@ -203,13 +203,13 @@ "result": { "Last_Wh_pro_Stunde": [ 11541.07, - 8929.91, - 7875.5599999999995, - 10061.61912107894, - 13622.08822093064, - 11542.82, - 12483.786689770084, - 11482.522793459368, + 6307.91, + 9186.56, + 10309.03, + 6407.67, + 5109.82, + 11704.22, + 5036.78, 1129.12, 1178.71, 1050.98, @@ -217,7 +217,7 @@ 912.38, 704.61, 516.37, - 2368.05, + 868.05, 694.34, 608.79, 556.31, @@ -228,9 +228,9 @@ 1056.97, 992.46, 1155.99, - 1189.376775455858, - 1257.98, - 1232.67, + 827.01, + 3757.98, + 3732.67, 871.26, 860.88, 1158.03, @@ -244,42 +244,42 @@ "EAuto_SoC_pro_Stunde": [ 5.0, 22.48, - 35.589999999999996, - 46.515, - 53.06999999999999, - 66.18, - 79.29, - 85.845, - 99.82900000000001, - 99.82900000000001, - 99.82900000000001, - 99.82900000000001, - 99.82900000000001, - 99.82900000000001, - 99.82900000000001, - 99.82900000000001, - 99.82900000000001, - 99.82900000000001, - 99.82900000000001, - 99.82900000000001, - 99.82900000000001, - 99.82900000000001, - 99.82900000000001, - 99.82900000000001, - 99.82900000000001, - 99.82900000000001, - 99.82900000000001, - 99.82900000000001, - 99.82900000000001, - 99.82900000000001, - 99.82900000000001, - 99.82900000000001, - 99.82900000000001, - 99.82900000000001, - 99.82900000000001, - 99.82900000000001, - 99.82900000000001, - 99.82900000000001 + 31.22, + 44.330000000000005, + 59.62499999999999, + 68.365, + 74.92, + 92.4, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955 ], "Einnahmen_Euro_pro_Stunde": [ 0.0, @@ -290,9 +290,6 @@ 0.0, 0.0, 0.0, - 0.05814763393192599, - 0.023370695807696434, - 0.0019327051509204403, 0.0, 0.0, 0.0, @@ -310,22 +307,47 @@ 0.0, 0.0, 0.0, - 0.300304461863129, - 0.2577866264025879, - 0.15146384621553569, - 0.09798107754792196, - 0.029150936607659956, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ], - "Gesamt_Verluste": 8027.369144275042, - "Gesamtbilanz_Euro": 13.979945090881573, - "Gesamteinnahmen_Euro": 0.9201379835273773, - "Gesamtkosten_Euro": 14.90008307440895, + "Gesamt_Verluste": 7430.87215820259, + "Gesamtbilanz_Euro": 9.4881560601163, + "Gesamteinnahmen_Euro": 0.0, + "Gesamtkosten_Euro": 9.4881560601163, "Home_appliance_wh_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, 0.0, 0.0, 0.0, @@ -341,32 +363,32 @@ 0.0, 0.0, 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, 0.0 ], "home_appliance_energy_wh": { "dishwasher1": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, 0.0, 0.0, 0.0, @@ -382,109 +404,87 @@ 0.0, 0.0, 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, 0.0 ] }, "Kosten_Euro_pro_Stunde": [ - 2.41926012, - 0.7712613792641736, - 1.4167558060312964, - 1.672937586, - 1.4723942300000001, - 0.36290611910050846, - 1.907718932, - 1.9280712188270857, + 1.4160601199999998, + 0.19137741085396395, + 1.691123530177008, + 1.7187910298948639, + 0.20791005863613377, + 0.09234842570092357, + 1.709869129414328, + 0.4965507655670841, 0.05258762370598476, 0.1614775630079859, - 0.2338232746714084, + 0.0, 0.0, 0.26650619799999997, 0.19588158, 0.0, - 0.78571899, - 0.22802125600000003, 0.0, 0.0, - 0.162995926, - 0.16677339, 0.0, - 0.25364873699864443, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, 0.1306329312971816, - 0.0, + 0.07362195915902499, 0.060401289430882174, - 0.0854632640738, + 0.009619897970888898, + 0.4410873661898387, 0.0, + 2.3325608707865465e-05, + 0.0021886029750169123, + 0.013012984677295973, 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, + 0.17784012884918773, 0.0, 0.0, + 0.214398479, 0.16484566 ], "Netzbezug_Wh_pro_Stunde": [ - 10610.789999999999, - 3486.7150961309835, - 6769.01961792306, - 8903.34, - 8010.85, - 1810.908777946649, - 8679.34, - 8493.70580981095, + 6210.789999999999, + 865.1781684175585, + 8079.902198647912, + 9147.371101090283, + 1131.1755094457767, + 460.8204875295587, + 7779.204410438253, + 2187.4483064629258, 175.4675465665157, 505.40708296709204, - 758.9200735845777, + 0.0, 0.0, 912.38, 704.61, 0.0, - 2368.05, - 694.34, 0.0, 0.0, - 488.89, - 506.91, 0.0, - 833.8222781020527, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, 537.58407941227, - 0.0, + 322.9033296448464, 273.0618871197205, - 408.3290208972767, + 45.96224544141853, + 2347.45804252176, 0.0, + 0.11639525303326081, + 9.957247384062384, + 57.32592368852852, 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, + 556.6201215937018, 0.0, 0.0, + 733.99, 592.97 ], "Netzeinspeisung_Wh_pro_Stunde": [ @@ -496,9 +496,6 @@ 0.0, 0.0, 0.0, - 830.6804847418, - 333.86708296709196, - 27.61007358457772, 0.0, 0.0, 0.0, @@ -516,11 +513,14 @@ 0.0, 0.0, 0.0, - 4290.063740901843, - 3682.6660914655417, - 2163.76923165051, - 1399.729679256028, - 416.4419515379994, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, 0.0, 0.0, 0.0, @@ -528,84 +528,84 @@ 0.0 ], "Verluste_Pro_Stunde": [ - 552.0, - 1014.0066115357181, - 345.0239541507672, - 806.9999999999999, - 1013.9999999999998, - 1036.459053353598, - 807.0, - 683.6982971773144, - 19.04844741896588, - 0.0, - 0.0, + 1152.0, + 876.0621802101069, + 414.00986383774955, + 483.00373213083384, + 365.07906113349316, + 311.4084585035471, + 557.3837292525905, + 227.23539677555112, + 118.73010558798194, + 40.06404995605101, + 106.80230977350088, 133.7321802766326, 0.0, 0.0, 70.41409090909087, - 180.0, - 0.0, + 118.37045454545455, + 94.68272727272722, 83.01681818181817, 75.86045454545456, - 0.0, - 0.0, + 66.66681818181814, + 69.12409090909085, 109.07302361034766, - 3.2918733722463145, + 116.99491129525349, 22.312089529472388, - 98.21987178167876, + 54.18759955738153, 86.6234264543665, - 208.64388250767325, - 116.9350623689079, - 23.490751091778808, - 0.03390853445803674, - 2.900768349489402, - 16.700320743972142, - 81.2380484620021, - 111.78035844493081, + 165.15986945297027, + 2.727365102611202, + 238.2983999999999, + 441.9379674303641, + 261.1952696860876, + 176.85071084262336, + 131.21108264656203, + 35.877614591244196, 90.18403329253945, 134.59227272727276, - 100.08954545454549, + 0.0, 0.0 ], "akku_soc_pro_stunde": [ 80.0, - 80.0, - 61.06078971437601, - 61.06145510745288, - 77.72812177411956, - 94.39478844078621, - 76.07925709454777, - 92.74592376121446, - 99.47087646058428, - 100.0, - 100.0, - 100.0, - 95.77874174137638, - 95.77874174137638, - 95.77874174137638, - 93.5560747303571, - 98.5560747303571, - 98.5560747303571, - 95.93559435845627, - 93.54100930335983, - 93.54100930335983, - 93.54100930335983, - 90.09811906241156, - 90.1895599894184, - 90.80934025412597, - 90.92464377010445, - 93.33085006050352, - 99.12651346349443, - 99.34747913633947, - 100.0, - 100.0, - 100.0, - 100.0, - 100.0, - 98.60068046043587, - 96.11252124341868, - 91.86402778611841, - 88.70463556848756 + 61.06060606060606, + 42.12293934927065, + 42.12321334476369, + 42.123317015064636, + 44.59773537988389, + 47.49797033831576, + 47.64751837310994, + 48.209612727986354, + 51.507671216541404, + 52.620561493098386, + 49.44589365304724, + 45.224635394423636, + 45.224635394423636, + 45.224635394423636, + 43.00196838340435, + 39.26552726770188, + 36.27680481591124, + 33.656324444010416, + 31.261739388913995, + 29.157357501861657, + 26.97541018781207, + 23.53251994686378, + 20.034849828321157, + 20.654630093028718, + 22.159841191844876, + 24.566047482243945, + 29.15382163371534, + 29.229581775454538, + 35.84898177545453, + 48.12503642629798, + 55.38046058424485, + 60.29298032987328, + 61.68112016833327, + 62.6777205736456, + 60.18956135662841, + 55.94106789932813, + 55.94106789932813 ], "Electricity_price": [ 0.000228, @@ -703,13 +703,13 @@ 0.0, 0.0, 1.0, + 0.5, 0.75, - 0.625, + 0.875, + 0.5, 0.375, - 0.75, - 0.75, + 1.0, 0.375, - 0.8, 0.0, 0.0, 0.0, @@ -795,49 +795,35 @@ "capacity_wh": 60000, "charging_efficiency": 0.95, "max_charge_power_w": 11040, - "soc_wh": 59897.4, + "soc_wh": 59373.0, "initial_soc_percentage": 5 }, "start_solution": [ - 2.0, - 2.0, - 1.0, - 1.0, - 0.0, - 1.0, - 2.0, - 2.0, - 2.0, - 0.0, - 0.0, 1.0, 0.0, 2.0, 2.0, - 1.0, - 2.0, - 2.0, - 0.0, - 0.0, - 0.0, - 1.0, - 0.0, 0.0, 1.0, 2.0, 2.0, 1.0, - 1.0, - 0.0, - 0.0, - 1.0, - 0.0, - 0.0, - 1.0, - 0.0, 2.0, 1.0, 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 1.0, + 0.0, + 0.0, + 1.0, 1.0, 1.0, 1.0, @@ -848,59 +834,73 @@ 1.0, 0.0, 0.0, - 5.0, - 4.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 1.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 2.0, 3.0, 6.0, - 5.0, - 5.0, - 3.0, - 1.0, - 0.0, - 6.0, 4.0, 3.0, 1.0, - 4.0, - 4.0, - 1.0, - 5.0, - 3.0, - 2.0, - 5.0, 6.0, - 5.0, 1.0, - 1.0, - 1.0, - 2.0, - 5.0, - 4.0, - 2.0, - 6.0, - 5.0, 6.0, 2.0, - 0.0, 4.0, - 4.0, - 1.0, + 5.0, 2.0, 1.0, + 6.0, + 1.0, + 3.0, + 4.0, + 3.0, 3.0, 2.0, - 1.0, - 0.0, + 3.0, 0.0, 0.0, + 6.0, 4.0, 5.0, - 5.0 + 6.0, + 1.0, + 2.0, + 4.0, + 3.0, + 3.0, + 0.0, + 6.0, + 5.0, + 4.0, + 1.0, + 6.0, + 1.0, + 2.0, + 4.0, + 1.0, + 6.0, + 1.0, + 4.0, + 27.0 ], - "washingstart": 15, + "washingstart": 37, "appliance_starts": { "dishwasher1": [ - "2025-01-15 15:00:00+01:00" + "2025-01-16 13:00:00+01:00" ] } } \ No newline at end of file