diff --git a/CHANGELOG.md b/CHANGELOG.md index 81bbc4b9..d4aadfd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -80,6 +80,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). are deprecated in favour of `appliance_starts` and `result.home_appliance_energy_wh`. ### Fixed +- Allow the direct-marketing optimizer to select a true battery self-consumption state with DC + charging and local-load discharge enabled in the same slot. Existing warm-start state numbers + remain compatible, and educated guesses now use the combined state for PV/load overlap instead + of unnecessarily bypassing PV while serving loads such as EV charging. +- Account for EV charging losses in the AC load seen by the inverter and grid, so fitness and + energy costs use the charger's raw input rather than only the energy stored in the EV battery. +- Treat fitness memoization as disabled for lightweight optimizer instances constructed without + the normal initializer, preserving isolated penalty evaluation and test callers. - Re-simulate genetic candidates after removing EV charging genes from slots that begin at full SoC, keeping the repaired genome and its assigned fitness consistent. - FeedInTariffEnergyCharts no longer aborts the whole prediction/optimization when the diff --git a/docs/akkudoktoreos/optimpost.md b/docs/akkudoktoreos/optimpost.md index 948cb019..093a2c1f 100644 --- a/docs/akkudoktoreos/optimpost.md +++ b/docs/akkudoktoreos/optimpost.md @@ -357,6 +357,11 @@ smaller values (e.g. `0.0`) disable the penalty entirely. - `discharge_allowed`: Battery discharge permission for local self-consumption/load coverage (0 or 1) - `battery_grid_export_allowed`: Battery discharge permission for grid export/direct marketing (0 or 1) +With direct marketing enabled, `dc_charge = 1` and `discharge_allowed = 1` may occur together. This +is the normal self-consumption mode: within a coarse optimization slot, the battery may cover +probabilistic load gaps and store PV surplus from different sub-intervals. A discharge-only state +remains available when deliberately bypassing PV charging is economically preferable. + 0 (no charge) 1 (charge with full load) diff --git a/src/akkudoktoreos/optimization/genetic/genetic.py b/src/akkudoktoreos/optimization/genetic/genetic.py index 162778e1..c5c23372 100644 --- a/src/akkudoktoreos/optimization/genetic/genetic.py +++ b/src/akkudoktoreos/optimization/genetic/genetic.py @@ -85,6 +85,17 @@ class FitnessCacheEntry: extra_data: tuple[float, float, float] +@dataclass(frozen=True) +class BatteryStateLayout: + """Indices of optional battery states appended to the legacy state ranges.""" + + total_states: int + dc_not_allowed_state: Optional[int] = None + dc_allowed_state: Optional[int] = None + grid_export_state: Optional[int] = None + self_consumption_state: Optional[int] = None + + class GeneticSimulation(PydanticBaseModel): """Device simulation for GENETIC optimization algorithm.""" @@ -411,10 +422,12 @@ class GeneticSimulation(PydanticBaseModel): if ev_fast: soc_ev_per_hour[hour_idx] = ev_fast.current_soc_percentage() # save begin state if ev_charge_hours_fast[hour] > 0: - loaded_energy_ev, verluste_eauto = ev_fast.charge_energy( + stored_energy_ev, verluste_eauto = ev_fast.charge_energy( wh=None, hour=hour, charge_factor=ev_charge_hours_fast[hour] ) - consumption += loaded_energy_ev + # The inverter/grid must supply the EV charger's raw input, + # not only the energy stored after charging losses. + consumption += stored_energy_ev + verluste_eauto losses_wh_per_hour[hour_idx] += verluste_eauto # Save battery SOC before inverter processing = true begin-of-interval state. @@ -663,6 +676,40 @@ class GeneticOptimization(OptimizationBase): except Exception: return False + def _battery_state_layout(self) -> BatteryStateLayout: + """Build optional state indices without renumbering legacy warm starts. + + The pre-existing order is retained exactly: base charge/discharge ranges, + two optional DC states, then optional grid export. SELF_CONSUMPTION is + appended last so an old export gene never changes its meaning. + """ + next_state = 3 * len(self.bat_possible_charge_values) + dc_not_allowed_state: Optional[int] = None + dc_allowed_state: Optional[int] = None + grid_export_state: Optional[int] = None + self_consumption_state: Optional[int] = None + + if self.optimize_dc_charge: + dc_not_allowed_state = next_state + dc_allowed_state = next_state + 1 + next_state += 2 + + if self.optimize_battery_grid_export: + grid_export_state = next_state + next_state += 1 + + if self.optimize_dc_charge: + self_consumption_state = next_state + next_state += 1 + + return BatteryStateLayout( + total_states=next_state, + dc_not_allowed_state=dc_not_allowed_state, + dc_allowed_state=dc_allowed_state, + grid_export_state=grid_export_state, + self_consumption_state=self_consumption_state, + ) + def _appliance_horizon_end_slot(self) -> int: """Exclusive upper slot bound for appliance runs (end of horizon). @@ -966,9 +1013,8 @@ class GeneticOptimization(OptimizationBase): # AC Charge: 2*len_bat .. 3*len_bat - 1 (maps to bat_possible_charge_values) # DC optional: 3*len_bat (not allowed), 3*len_bat + 1 (allowed) # Grid export: next state, if direct marketing/export optimization is enabled - - # Idle states - idle_mask = (discharge_hours_bin_np >= 0) & (discharge_hours_bin_np < len_bat) + # Self-consumption: final state, with DC charging and local discharge enabled + state_layout = self._battery_state_layout() # Discharge states discharge_mask = (discharge_hours_bin_np >= len_bat) & ( @@ -980,24 +1026,28 @@ class GeneticOptimization(OptimizationBase): ac_indices = (discharge_hours_bin_np[ac_mask] - 2 * len_bat).astype(int) # DC states (if enabled) - if self.optimize_dc_charge: - dc_not_allowed_state = 3 * len_bat - dc_allowed_state = 3 * len_bat + 1 - dc_charge = np.where(discharge_hours_bin_np == dc_allowed_state, 1, 0) + if state_layout.dc_allowed_state is not None: + dc_mask = discharge_hours_bin_np == state_layout.dc_allowed_state + if state_layout.self_consumption_state is not None: + dc_mask |= discharge_hours_bin_np == state_layout.self_consumption_state + dc_charge = np.where(dc_mask, 1, 0) else: dc_charge = np.ones_like(discharge_hours_bin_np, dtype=float) # Generate the result arrays discharge = np.zeros_like(discharge_hours_bin_np, dtype=int) discharge[discharge_mask] = 1 # Set Discharge states to 1 + if state_layout.self_consumption_state is not None: + discharge[discharge_hours_bin_np == state_layout.self_consumption_state] = 1 ac_charge = np.zeros_like(discharge_hours_bin_np, dtype=float) ac_charge[ac_mask] = [self.bat_possible_charge_values[i] for i in ac_indices] battery_grid_export = np.zeros_like(discharge_hours_bin_np, dtype=int) - if self.optimize_battery_grid_export: - grid_export_state = 3 * len_bat + (2 if self.optimize_dc_charge else 0) - battery_grid_export = np.where(discharge_hours_bin_np == grid_export_state, 1, 0) + if state_layout.grid_export_state is not None: + battery_grid_export = np.where( + discharge_hours_bin_np == state_layout.grid_export_state, 1, 0 + ) # Idle is just 0, already default. @@ -1005,14 +1055,7 @@ class GeneticOptimization(OptimizationBase): def mutate(self, individual: list[int]) -> tuple[list[int]]: """Custom mutation function for the individual.""" - # Calculate the number of states using battery charge levels - len_bat = len(self.bat_possible_charge_values) - if self.optimize_dc_charge: - total_states = 3 * len_bat + 2 - else: - total_states = 3 * len_bat - if self.optimize_battery_grid_export: - total_states += 1 + total_states = self._battery_state_layout().total_states # 1. Mutating the charge_discharge part charge_discharge_part = individual[: self.total_slots] @@ -1146,14 +1189,15 @@ class GeneticOptimization(OptimizationBase): must re-simulate after a change so the individual's genome, simulation state and assigned fitness always describe the same schedule. """ - if not self.optimize_ev or not self.ev_possible_charge_values: + ev_possible_charge_values = getattr(self, "ev_possible_charge_values", None) + if not self.optimize_ev or not ev_possible_charge_values: return False zero_charge_index = min( - range(len(self.ev_possible_charge_values)), - key=lambda index: abs(self.ev_possible_charge_values[index]), + range(len(ev_possible_charge_values)), + key=lambda index: abs(ev_possible_charge_values[index]), ) - if abs(self.ev_possible_charge_values[zero_charge_index]) > 1e-12: + if abs(ev_possible_charge_values[zero_charge_index]) > 1e-12: return False _, ev_charge_indices, _ = self.split_individual(individual) @@ -1172,7 +1216,7 @@ class GeneticOptimization(OptimizationBase): charge_index = int(ev_charge_indices[slot]) if ( ev_soc[offset] >= 100.0 - 1e-9 - and self.ev_possible_charge_values[charge_index] > 0.0 + and ev_possible_charge_values[charge_index] > 0.0 ): ev_charge_indices[slot] = zero_charge_index changed = True @@ -1279,11 +1323,13 @@ class GeneticOptimization(OptimizationBase): slots = self.total_slots start_slot = self._start_day_slot() len_bat = len(self.bat_possible_charge_values) + state_layout = self._battery_state_layout() idle_state = 0 discharge_state = len_bat ac_charge_state = 3 * len_bat - 1 - dc_allowed_state = 3 * len_bat + 1 - export_state = 3 * len_bat + (2 if self.optimize_dc_charge else 0) + dc_allowed_state = state_layout.dc_allowed_state + export_state = state_layout.grid_export_state + self_consumption_state = state_layout.self_consumption_state prices = np.asarray(self.simulation.elect_price_hourly, dtype=float) feed_in = np.asarray(self.simulation.elect_revenue_per_hour_arr, dtype=float) @@ -1333,15 +1379,20 @@ class GeneticOptimization(OptimizationBase): for slot in range(start_slot, slots): high_feed_in = ( export_quantile is not None - and self.optimize_battery_grid_export + and export_state is not None 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: + if high_feed_in and export_state is not None: battery_genes[slot] = export_state - elif self.optimize_dc_charge and pv_surplus: + elif self_consumption_state is not None and pv[slot] > 0.0 and load[slot] > 0.0: + # The probabilistic inverter model can see a residual load + # and a PV surplus within the same coarse slot. Normal + # self-consumption must therefore allow both directions. + battery_genes[slot] = self_consumption_state + elif dc_allowed_state is not None 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 @@ -1415,7 +1466,9 @@ class GeneticOptimization(OptimizationBase): 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]: + elif self_consumption_state is not None and pv[slot] > 0.0 and load[slot] > 0.0: + randomized[slot] = self_consumption_state + elif dc_allowed_state is not None 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 @@ -1480,12 +1533,8 @@ class GeneticOptimization(OptimizationBase): # AC-Charge: len_bat states (maps to bat_possible_charge_values) # With DC: + 2 additional states # With battery grid export: + 1 additional state - if self.optimize_dc_charge: - total_states = 3 * len_bat + 2 - else: - total_states = 3 * len_bat - if self.optimize_battery_grid_export: - total_states += 1 + # With DC: + 1 final SELF_CONSUMPTION state + total_states = self._battery_state_layout().total_states # State space: 0 .. (total_states - 1) self.toolbox.register("attr_discharge_state", random.randint, 0, total_states - 1) @@ -1579,7 +1628,10 @@ class GeneticOptimization(OptimizationBase): worst_case: bool, ) -> tuple[float]: """Evaluate an individual, using run-local canonical memoization when active.""" - if not self._fitness_cache_enabled: + # Some lightweight callers construct the optimizer without __init__ + # (for example isolated penalty evaluations). Memoization is opt-in, so + # a missing flag must behave exactly like a disabled cache. + if not getattr(self, "_fitness_cache_enabled", False): return self._evaluate_uncached(individual, parameters, start_hour, worst_case) original_key = tuple(int(value) for value in individual) diff --git a/tests/test_genetic_seeding.py b/tests/test_genetic_seeding.py index cf6460ff..bae0bf4c 100644 --- a/tests/test_genetic_seeding.py +++ b/tests/test_genetic_seeding.py @@ -187,9 +187,11 @@ def test_educated_guesses_encode_high_price_direct_marketing(config_eos: ConfigE dc_allowed_state = 4 export_state = 5 + self_consumption_state = 6 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(dc_allowed_state in guess or self_consumption_state in guess for guess in guesses) + assert any(self_consumption_state in guess for guess in guesses) assert any(guess[-1] == export_state for guess in guesses) diff --git a/tests/test_geneticsimulation.py b/tests/test_geneticsimulation.py index 51387d99..ee6da449 100644 --- a/tests/test_geneticsimulation.py +++ b/tests/test_geneticsimulation.py @@ -338,7 +338,7 @@ def test_simulation(genetic_simulation): # Verify the total balance assert ( - abs(result["Gesamtbilanz_Euro"] - 7.025236588371921) < 1e-5 + abs(result["Gesamtbilanz_Euro"] - 7.224316588371922) < 1e-5 ), "Total balance should reflect the shared per-slot battery power limit." # Check total revenue and total costs @@ -346,7 +346,7 @@ def test_simulation(genetic_simulation): abs(result["Gesamteinnahmen_Euro"] - 2.3247787887715) < 1e-5 ), "Total revenue should respect the shared per-slot battery power limit." assert ( - abs(result["Gesamtkosten_Euro"] - 9.350015377143421) < 1e-5 + abs(result["Gesamtkosten_Euro"] - 9.549095377143422) < 1e-5 ), "Total costs should respect the shared per-slot battery power limit." # Check the losses @@ -379,6 +379,47 @@ def test_simulation(genetic_simulation): print("All tests passed successfully.") +def test_ev_charging_uses_raw_input_energy_for_load_and_grid(config_eos): + config_eos.merge_settings_from_dict( + {"prediction": {"hours": 1}, "optimization": {"horizon_hours": 1}} + ) + ev = Battery( + ElectricVehicleParameters( + device_id="ev1", + capacity_wh=1000, + charging_efficiency=0.8, + max_charge_power_w=100, + initial_soc_percentage=0, + min_soc_percentage=0, + ), + prediction_hours=1, + ) + inverter = Inverter(InverterParameters(device_id="inverter1", max_power_wh=1000.0)) + simulation = GeneticSimulation() + simulation.prepare( + GeneticEnergyManagementParameters( + pv_prognose_wh=[0.0], + strompreis_euro_pro_wh=[0.001], + einspeiseverguetung_euro_pro_wh=[0.0], + preis_euro_pro_wh_akku=0.0, + gesamtlast=[0.0], + ), + optimization_hours=1, + prediction_hours=1, + inverter=inverter, + ev=ev, + ) + simulation.ev_charge_hours = np.array([1.0]) + + result = simulation.simulate(start_hour=0) + + assert result["Last_Wh_pro_Stunde"][0] == pytest.approx(100.0) + assert result["Netzbezug_Wh_pro_Stunde"][0] == pytest.approx(100.0) + assert result["Kosten_Euro_pro_Stunde"][0] == pytest.approx(0.1) + assert result["Verluste_Pro_Stunde"][0] == pytest.approx(20.0) + assert ev.current_soc_percentage() == pytest.approx(8.0) + + def test_direct_marketing_curtails_negative_feed_in(config_eos, monkeypatch): config_eos.merge_settings_from_dict( {"prediction": {"hours": 2}, "optimization": {"horizon_hours": 2}} diff --git a/tests/test_geneticsolution.py b/tests/test_geneticsolution.py index 66e74056..f7cd9a93 100644 --- a/tests/test_geneticsolution.py +++ b/tests/test_geneticsolution.py @@ -54,3 +54,23 @@ def test_decode_charge_discharge_has_separate_battery_grid_export_state(): assert dc_charge.tolist() == [0] assert discharge.tolist() == [0] assert battery_grid_export.tolist() == [1] + + +def test_decode_charge_discharge_has_self_consumption_state_after_legacy_export(): + optimization = GeneticOptimization() + optimization.bat_possible_charge_values = [1.0] + optimization.optimize_dc_charge = True + optimization.optimize_battery_grid_export = True + + layout = optimization._battery_state_layout() + ac_charge, dc_charge, discharge, battery_grid_export = ( + optimization.decode_charge_discharge(np.array([6])) + ) + + assert layout.total_states == 7 + assert layout.grid_export_state == 5 + assert layout.self_consumption_state == 6 + assert ac_charge.tolist() == [0.0] + assert dc_charge.tolist() == [1] + assert discharge.tolist() == [1] + assert battery_grid_export.tolist() == [0] diff --git a/tests/testdata/new_optimize_15min.pdf b/tests/testdata/new_optimize_15min.pdf index 5cbf4937..3fb15774 100644 Binary files a/tests/testdata/new_optimize_15min.pdf and b/tests/testdata/new_optimize_15min.pdf differ diff --git a/tests/testdata/optimize_result_2.json b/tests/testdata/optimize_result_2.json index 86cdf480..5c417682 100644 --- a/tests/testdata/optimize_result_2.json +++ b/tests/testdata/optimize_result_2.json @@ -202,15 +202,15 @@ ], "result": { "Last_Wh_pro_Stunde": [ - 10230.07, - 7618.91, - 7875.5599999999995, - 7565.03, - 12840.67, - 9042.82, - 9082.22, - 5036.78, - 2177.92, + 10713.07, + 7963.91, + 8220.56, + 7772.03, + 13323.67, + 9456.82, + 9496.22, + 5243.78, + 2233.12, 1178.71, 1050.98, 988.56, @@ -313,7 +313,7 @@ 0.0, 0.0, 0.0, - 0.05936927575872234, + 0.053661230306193436, 0.05435777788576338, 0.0, 0.0, @@ -322,9 +322,9 @@ 0.0 ], "Gesamt_Verluste": 6227.580163897914, - "Gesamtbilanz_Euro": 11.420868526053463, - "Gesamteinnahmen_Euro": 0.11372705364448572, - "Gesamtkosten_Euro": 11.534595579697948, + "Gesamtbilanz_Euro": 12.033027623527284, + "Gesamteinnahmen_Euro": 0.10801900819195681, + "Gesamtkosten_Euro": 12.14104663171924, "Home_appliance_wh_per_hour": [ 0.0, 0.0, @@ -408,15 +408,15 @@ ] }, "Kosten_Euro_pro_Stunde": [ - 2.1203521199999997, - 1.4545721927072854, - 1.4167558060312964, - 1.2032659414129376, - 1.2892826874611185, - 0.770122611209644, - 1.1459236583154115, - 0.4965507655670841, - 0.1912938683161112, + 2.23047612, + 1.5308798733801507, + 1.4889564723943407, + 1.242149738556099, + 1.3742255912165289, + 0.8479283355019763, + 1.2336553365360492, + 0.5407163922683618, + 0.20558284318867343, 0.1614775630079859, 0.0, 0.0, @@ -448,15 +448,15 @@ 0.16484566 ], "Netzbezug_Wh_pro_Stunde": [ - 9299.789999999999, - 6575.823656000386, - 6769.01961792306, - 6403.757005923032, - 7014.595688036554, - 3842.9272016449304, - 5213.483431826258, - 2187.4483064629258, - 638.2845122326032, + 9782.789999999999, + 6920.795087613701, + 7113.9821901306295, + 6610.695787951565, + 7476.74423948057, + 4231.179318872138, + 5612.626644840988, + 2382.0105386271443, + 685.962106068313, 505.40708296709204, 0.0, 0.0, @@ -519,7 +519,7 @@ 0.0, 0.0, 0.0, - 848.1325108388907, + 766.589004374192, 776.5396840823341, 0.0, 0.0, @@ -529,14 +529,14 @@ ], "Verluste_Pro_Stunde": [ 483.0, - 345.0196387200463, - 345.0239541507672, - 207.05004071076388, - 506.1294825643864, - 452.3012641973916, - 426.13721181915116, - 227.23539677555112, - 103.61214146791241, + 345.0162105136441, + 345.0194628156755, + 207.04269455418785, + 503.6273087376684, + 449.2115182646565, + 424.3543973809186, + 225.74286463525735, + 102.70945272819762, 40.06404995605101, 106.80230977350088, 133.7321802766326, @@ -559,7 +559,7 @@ 538.2984000000001, 441.9379674303641, 261.1952696860876, - 75.0748095419566, + 84.86003031772043, 0.0, 111.78035844493081, 90.18403329253945, @@ -570,36 +570,36 @@ "akku_soc_pro_stunde": [ 80.0, 80.0, - 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, + 80.00045029204567, + 80.00099092581443, + 80.00217688565297, + 80.57515768392155, + 81.55325541349534, + 81.8408775629653, + 82.36151269172245, + 83.68121971195016, + 84.79410998850713, + 81.619442148456, + 77.39818388983241, + 77.39818388983241, + 77.39818388983241, + 75.17551687881311, + 71.43907576311062, + 68.45035331131999, + 65.82987293941916, + 63.43528788432273, + 61.33090599727039, + 59.14895868322081, + 55.706068442272525, + 52.208398323729895, + 52.82817858843747, + 54.33338968725362, + 56.73959597765268, + 61.327370129124084, + 63.158564793495216, + 78.11129812682856, + 90.387352777672, + 97.64277693561888, 100.0, 100.0, 98.60068046043587, diff --git a/tests/testdata/optimize_result_2_be.json b/tests/testdata/optimize_result_2_be.json index 5d232d51..89eb4c7b 100644 --- a/tests/testdata/optimize_result_2_be.json +++ b/tests/testdata/optimize_result_2_be.json @@ -202,14 +202,14 @@ ], "result": { "Last_Wh_pro_Stunde": [ - 11541.07, - 6307.91, - 9186.56, - 10309.03, - 6407.67, - 5109.82, - 11704.22, - 5036.78, + 12093.07, + 6583.91, + 9600.56, + 10792.03, + 6683.67, + 5316.82, + 12256.22, + 5243.78, 1129.12, 1178.71, 1050.98, @@ -321,10 +321,10 @@ 0.0, 0.0 ], - "Gesamt_Verluste": 7430.87215820259, - "Gesamtbilanz_Euro": 9.4881560601163, + "Gesamt_Verluste": 7415.050669156861, + "Gesamtbilanz_Euro": 10.086958235191952, "Gesamteinnahmen_Euro": 0.0, - "Gesamtkosten_Euro": 9.4881560601163, + "Gesamtkosten_Euro": 10.086958235191952, "Home_appliance_wh_per_hour": [ 0.0, 0.0, @@ -408,14 +408,14 @@ ] }, "Kosten_Euro_pro_Stunde": [ - 1.4160601199999998, - 0.19137741085396395, - 1.691123530177008, - 1.7187910298948639, - 0.20791005863613377, - 0.09234842570092357, - 1.709869129414328, - 0.4965507655670841, + 1.5419161199999998, + 0.2524105556335792, + 1.7777665549410084, + 1.809540886, + 0.24971825220475874, + 0.12061259291950001, + 1.83015129135275, + 0.5407163922683618, 0.05258762370598476, 0.1614775630079859, 0.0, @@ -448,14 +448,14 @@ 0.16484566 ], "Netzbezug_Wh_pro_Stunde": [ - 6210.789999999999, - 865.1781684175585, - 8079.902198647912, - 9147.371101090283, - 1131.1755094457767, - 460.8204875295587, - 7779.204410438253, - 2187.4483064629258, + 6762.789999999999, + 1141.0965444556023, + 8493.867916583891, + 9630.34, + 1358.64119806724, + 601.8592461052895, + 8326.43899614536, + 2382.0105386271443, 175.4675465665157, 505.40708296709204, 0.0, @@ -529,13 +529,13 @@ ], "Verluste_Pro_Stunde": [ 1152.0, - 876.0621802101069, - 414.00986383774955, - 483.00373213083384, - 365.07906113349316, - 311.4084585035471, - 557.3837292525905, - 227.23539677555112, + 876.0523853346722, + 414.00574999006693, + 483.0, + 359.25494376806876, + 303.4931095326348, + 556.8118795374434, + 225.74286463525735, 118.73010558798194, 40.06404995605101, 106.80230977350088, @@ -570,42 +570,42 @@ "akku_soc_pro_stunde": [ 80.0, 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 + 42.12266726939745, + 42.122826991343764, + 42.122826991343764, + 44.435464318234565, + 47.11582847191886, + 47.249491792403404, + 47.770126921160546, + 51.0681854097156, + 52.181075686272585, + 49.006407846221435, + 44.78514958759783, + 44.78514958759783, + 44.78514958759783, + 42.562482576578546, + 38.82604146087607, + 35.837319009085434, + 33.2168386371846, + 30.822253582088187, + 28.717871695035846, + 26.53592438098626, + 23.09303414003797, + 19.595364021495346, + 20.215144286202914, + 21.72035538501907, + 24.126561675418138, + 28.71433582688953, + 28.79009596862873, + 35.40949596862873, + 47.68555061947217, + 54.94097477741905, + 59.85349452304748, + 61.241634361507465, + 62.2382347668198, + 59.7500755498026, + 55.50158209250233, + 55.50158209250233 ], "Electricity_price": [ 0.000228,