mirror of
https://github.com/Akkudoktor-EOS/EOS.git
synced 2026-08-31 12:46:38 +00:00
feat(optimization): schedule any number of flexible consumers
Replace the single hourly "dishwasher" home appliance with a list of flexible consumers (home_appliances). Each consumer defines its load either as an explicit power profile (energy-preservingly resampled onto the optimization slot grid, incl. 15-min and non-integer interval ratios) or the flat consumption_wh/duration_h fallback, and runs ONCE or DAILY within its time windows and the optimization horizon. - ConsumerScheduleMode + shared load-definition validation (XOR of profile/fallback, reject negative/NaN/inf, unique device_id) - ApplianceGeneLayout: variable appliance gene block (index into allowed_start_slots), ONCE/DAILY calendar-day based, no snapping - per-device output: result.home_appliance_energy_wh, appliance_starts (absolute local times), per-device solution columns and DDBC RUN/OFF instructions on state transitions only - deprecate dishwasher/washingstart/Home_appliance_wh_per_hour with backward-compatible mapping and explicit conflict rejection - max_home_appliances is now an upper bound only; no demo appliance and no on/off behaviour - docs, openapi.json, CHANGELOG and optimize_result_2* fixtures updated; new tests/test_homeappliance.py covers the mandatory test matrix Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
c59bf1b486
commit
67cf6f7d8a
@@ -221,7 +221,7 @@ def test_hourly_start_solution_is_expanded_to_slots(config_eos: ConfigEOS):
|
||||
opt.optimize_ev = False
|
||||
hourly = list(range(48))
|
||||
|
||||
migrated = opt._start_solution_for_slot_grid(hourly, has_appliance=False)
|
||||
migrated = opt._start_solution_for_slot_grid(hourly)
|
||||
|
||||
assert len(migrated) == 192
|
||||
assert migrated[:8] == [0, 0, 0, 0, 1, 1, 1, 1]
|
||||
@@ -242,8 +242,8 @@ def test_quarter_hour_mutation_probability_preserves_hourly_rate(config_eos: Con
|
||||
assert opt.toolbox.mutate_charge_discharge.keywords["indpb"] == pytest.approx(0.05)
|
||||
|
||||
|
||||
def test_sub_hourly_home_appliance_is_rejected(config_eos: ConfigEOS):
|
||||
"""An hourly appliance model must not silently run on slot indices."""
|
||||
def test_sub_hourly_home_appliance_is_scheduled(config_eos: ConfigEOS):
|
||||
"""A home appliance is scheduled on the 15-min slot grid and delivers its energy."""
|
||||
config_eos.merge_settings_from_dict(
|
||||
{
|
||||
"prediction": {"hours": 48},
|
||||
@@ -252,18 +252,28 @@ def test_sub_hourly_home_appliance_is_rejected(config_eos: ConfigEOS):
|
||||
)
|
||||
parameters = load_hourly_parameters().model_copy(
|
||||
update={
|
||||
"dishwasher": HomeApplianceParameters(
|
||||
device_id="dishwasher", consumption_wh=1200, duration_h=2
|
||||
)
|
||||
"home_appliances": [
|
||||
HomeApplianceParameters(
|
||||
device_id="dishwasher1", consumption_wh=1200, duration_h=2
|
||||
)
|
||||
]
|
||||
},
|
||||
deep=True,
|
||||
)
|
||||
ems_eos.set_start_datetime(to_datetime().set(hour=10, minute=0))
|
||||
CacheEnergyManagementStore().clear()
|
||||
|
||||
with pytest.raises(ValueError, match="Home-appliance scheduling"):
|
||||
GeneticOptimization(fixed_seed=42).optimierung_ems(
|
||||
parameters=parameters, start_hour=10, ngen=1
|
||||
)
|
||||
genetic_solution = GeneticOptimization(fixed_seed=42).optimierung_ems(
|
||||
parameters=parameters, start_hour=10, ngen=3
|
||||
)
|
||||
|
||||
# The appliance runs exactly once and delivers its full energy on the 15-min grid.
|
||||
energy = genetic_solution.result.home_appliance_energy_wh["dishwasher1"]
|
||||
assert sum(energy) == pytest.approx(1200.0)
|
||||
# The run occupies 2 h = 8 quarter-hour slots at 1200/2 = 600 W -> 150 Wh/slot.
|
||||
assert max(energy) == pytest.approx(150.0)
|
||||
# A single start time is reported as an absolute datetime.
|
||||
assert len(genetic_solution.appliance_starts["dishwasher1"]) == 1
|
||||
|
||||
|
||||
def test_optimize_15min_slot_grid(config_eos: ConfigEOS):
|
||||
|
||||
Reference in New Issue
Block a user