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:
Andreas
2026-07-15 14:19:46 +02:00
co-authored by Claude Opus 4.8
parent c59bf1b486
commit 67cf6f7d8a
21 changed files with 2505 additions and 1094 deletions
+5 -4
View File
@@ -257,13 +257,14 @@ The GENETIC algorithm supports 4 devices:
- **electric_vehicle**: An electric vehicle, basically the battery of an electric vehicle. The
The electrical vehicle is optional.
- **battery**: A battery that can be charged by the inverter. The battery is mandatory.
- **home_appliance**: A home appliance, like a washing machine or a dish washer. The home
appliance is optional.
- **home_appliance**: A flexible consumer, like a washing machine or a dish washer. Any number of
home appliances can be configured; each is scheduled independently. Home appliances are optional.
:::{admonition} Warning
:class: warning
The GENETIC algorithm can only use the first inverter, electrical vehicle, battery, home appliance
that is configured, even if more devices are configured.
The GENETIC algorithm can only use the first inverter, electrical vehicle and battery that is
configured, even if more devices are configured. Home appliances are the exception: all configured
home appliances are scheduled.
:::
#### Inverter simulation configuration
+9 -6
View File
@@ -93,12 +93,15 @@ to `DISABLED` in the configuration.
"initial_soc_percentage": 54,
"min_soc_percentage": 0
},
"dishwasher": {
"device_id": "dishwasher1",
"consumption_wh": 2000,
"duration_h": 3,
"time_windows": null
},
"home_appliances": [
{
"device_id": "dishwasher1",
"consumption_wh": 2000,
"duration_h": 3,
"schedule_mode": "ONCE",
"time_windows": null
}
],
"temperature_forecast": [
18.3, 17.8, 16.9, 16.2, 15.6, 15.1, 14.6, 14.2, 14.3, 14.8, 15.7, 16.7, 17.4,
18.0, 18.6, 19.2, 19.1, 18.7, 18.5, 17.7, 16.2, 14.6, 13.6, 13.0, 12.6, 12.2,
+33 -9
View File
@@ -193,28 +193,52 @@ indicates no power transfer. Intermediate values scale the power proportionally.
## Home Appliance
The optimization algorithm supports one start of the home appliance within the optimization
horizon.
The optimization algorithm schedules any number of flexible consumers (home appliances). Each
consumer has a unique `device_id` and is scheduled independently. The `schedule_mode` selects how
often a consumer runs:
- `ONCE` — a single run somewhere within the optimization horizon ("fire and forget").
- `DAILY` — one run per local calendar day that still has a feasible complete run.
### Home Appliance Simulation
Each consumer's load is described by the energy of a single complete run, resampled onto the
optimization slot grid (hourly or 15-minute). Multiple consumers and multiple daily runs may
overlap; their energy adds up.
### Home Appliance Configuration
Home appliance to run within the optimization horizon.
A consumer's load is defined **either** by an explicit power profile or by the flat
`consumption_wh` + `duration_h` fallback (exactly one of the two).
Two consumers, one defined by the flat fallback (runs once), one by an explicit 15-minute power
profile that runs once per day:
```json
[
{
"device_id": "dishwasher1",
"consumption_wh": 2000,
"duration_h": 3
"duration_h": 3,
"schedule_mode": "ONCE"
},
{
"device_id": "washingmachine1",
"load_profile_power_w": [200, 2000, 1800, 100],
"load_profile_interval_seconds": 900,
"schedule_mode": "DAILY"
}
]
```
Home appliance to run within a time window of 5 hours starting at 8:00 every day and another time
window of 3 hours starting at 15:00 every day. See
[Time Window Sequence Configuration](configtimewindow-page) for more information.
- `load_profile_power_w`: non-negative power values in watts describing one complete run. Each value
covers `load_profile_interval_seconds` (default: the configured optimization interval). The profile
is resampled energy-preservingly onto the optimization slot grid.
- `consumption_wh` / `duration_h`: flat fallback used when no `load_profile_power_w` is given.
A consumer may be restricted to run within a time window of 5 hours starting at 8:00 every day and
another time window of 3 hours starting at 15:00 every day. The complete run must fit inside a
single window. See [Time Window Sequence Configuration](configtimewindow-page) for more information.
```json
[
@@ -240,8 +264,8 @@ window of 3 hours starting at 15:00 every day. See
:::{admonition} Note
:class: note
The optimization algorithm always restricts to one start within the optimization horizon per
energy management run.
A `ONCE` consumer without any valid start (given its time windows and the horizon) is rejected. For
`DAILY`, a calendar day without a feasible run simply gets no run for that day.
:::
### Home Appliance Instructions