Add a steam boiler pool - #1429
Conversation
8cda58a to
ba9e618
Compare
cwasicki
left a comment
There was a problem hiding this comment.
Any advice what to review here? On a first glimpse this looks like a lot of boiler-plate code.
| Metric.AC_REACTIVE_POWER_PHASE_3: lambda msg: msg.reactive_power_per_phase[2], | ||
| } | ||
|
|
||
| _STEAM_BOILER_DATA_METHODS: dict[ |
There was a problem hiding this comment.
Why do we need to repeat these for every device?
There was a problem hiding this comment.
"historical reasons". This comes from the old days where the API have one message per component for the data. Now it just needs modernization. Hopefully soon™️.
There was a problem hiding this comment.
It would be a lot of work to do it differently, which is not worth it, because this stuff will all be rewritten in rust in the next several months.
The big things to check are:
I'll need to do some testing as well. |
cwasicki
left a comment
There was a problem hiding this comment.
LGTM, the PR description mentions a wrong default though.
There was a problem hiding this comment.
Oh, damn. I sill need to remove all this cr*p... 🤦
There was a problem hiding this comment.
Maybe you can forget about this for now. There's a big cleanup coming soon anyway, with the moving of the algorithmic parts to be behind the microgrid API.
| Metric.AC_REACTIVE_POWER_PHASE_3: lambda msg: msg.reactive_power_per_phase[2], | ||
| } | ||
|
|
||
| _STEAM_BOILER_DATA_METHODS: dict[ |
There was a problem hiding this comment.
"historical reasons". This comes from the old days where the API have one message per component for the data. Now it just needs modernization. Hopefully soon™️.
llucax
left a comment
There was a problem hiding this comment.
🤖 rAIview (AI review here!)
The overall structure is consistent with the existing component pools. I found three concrete control-path issues around failure feedback, stale bounds when all boilers become unavailable, and reversed reactive-power documentation. I also left two questions about whether the deliberately simple allocation and exclusion-bound behavior should be accepted and documented for this initial version.
| reactive_power: float = 0.0 | ||
| """The total reactive 3-phase AC power, in Volt-Ampere Reactive (VAr). | ||
|
|
||
| * Positive power means capacitive (current leading w.r.t. voltage). | ||
| * Negative power means inductive (current lagging w.r.t. voltage). | ||
| """ | ||
|
|
||
| reactive_power_per_phase: PhaseTuple = (0.0, 0.0, 0.0) | ||
| """The per-phase AC reactive power, in Volt-Ampere Reactive (VAr). | ||
|
|
||
| The provided values are for phase 1, 2, and 3 respectively. | ||
|
|
||
| * Positive power means capacitive (current leading w.r.t. voltage). | ||
| * Negative power means inductive (current lagging w.r.t. voltage). |
There was a problem hiding this comment.
🟡⬇️ Correct the reactive-power sign convention
These descriptions reverse the Microgrid API convention. The API defines negative reactive power as capacitive, with current leading voltage, and positive reactive power as inductive, with current lagging voltage. Both the total and per-phase docstrings currently say the opposite.
API reference: microgrid.proto:183-187.
Impact
Users can interpret measured reactive power with the wrong physical direction and build control or reporting logic around the reversed convention.
Suggested fix (⬇️ effort)
Change both docstrings to say that negative is capacitive and positive is inductive.
🟡 Medium Severity: Probably best to fix before merging because this documents a public data type with the opposite convention from the API.
⬇️ Low Effort: This requires changing four documentation lines.
There was a problem hiding this comment.
I guess we're contradicting the proto everywhere. in the old_component_data.py file, not just here.
Besides, both the proto and the SDK docs are completely wrong about the meaning of reactive power signs. So both need to be fixed, but separate project I guess.
There was a problem hiding this comment.
Are there issues for this? I seems nasty to document stuff that is not true.
Steam boilers are controllable electrical loads. Their active power and its inclusion/exclusion bounds, together with the per-phase power, current, voltage and frequency, are reported through the corresponding AC_* metrics. SteamBoilerData derives directly from ComponentData and declares its own fields and from_samples/to_samples handling, rather than reusing another category's type, so steam boilers are not coupled to the CHP data model. Signed-off-by: Sahas Subramanian <sahas.subramanian@proton.me>
Adds the timeseries.steam_boiler_pool package: the SteamBoilerPool, its reference store and report types, and a SteamBoilerSystemBoundsTracker that aggregates the active-power bounds of the working boilers, mirroring the PV pool. Signed-off-by: Sahas Subramanian <sahas.subramanian@proton.me>
The data sourcing actor handles the steam boiler component category so the power formula can resample per-boiler power. Signed-off-by: Sahas Subramanian <sahas.subramanian@proton.me>
The SteamBoilerStatusTracker reports per-boiler health so that broken boilers can be excluded from power distribution, mirroring the PV inverter status tracker. Signed-off-by: Sahas Subramanian <sahas.subramanian@proton.me>
The SteamBoilerManager splits the target power equally across the working boilers. Each allocation stays inside the boiler's inclusion bounds: a share below a boiler's minimum operating power is raised to the minimum when the remaining budget covers it, and the boiler is kept off otherwise. Like the PV manager, the measured draw of unreachable boilers is subtracted from the target, so the working boilers don't make the site overshoot it. Signed-off-by: Sahas Subramanian <sahas.subramanian@proton.me>
Wires the steam boiler pool into the data pipeline factory, using the plain matryoshka power-manager algorithm with DefaultPower.ZERO, so that unmanaged boilers are switched off and fall back to gas heating, and documents the new pool in the microgrid module docs. Signed-off-by: Sahas Subramanian <sahas.subramanian@proton.me>
Adds SteamBoilerDataWrapper, steam boiler streaming in the mock microgrid, resampling in the mock resampler, and graph-generator support. Signed-off-by: Sahas Subramanian <sahas.subramanian@proton.me>
Tests for the steam boiler pool power formula (including its meter fallback) and the power control methods. Signed-off-by: Sahas Subramanian <sahas.subramanian@proton.me>
Tests for the SteamBoilerManager's distribution (minimum-power and unreachable-power handling, formula delegation, shutdown) and the status tracker. Signed-off-by: Sahas Subramanian <sahas.subramanian@proton.me>
Signed-off-by: Sahas Subramanian <sahas.subramanian@proton.me>
ba9e618 to
b7d7402
Compare
llucax
left a comment
There was a problem hiding this comment.
OK, approving, assuming there were no code changes except for resolving any conflicts or adapting the necessary bits to the fixes for exhaustive match.
|
Yes, it was only a rebase. |
Steam boilers are controllable electrical loads the SDK could not monitor or
manage. This adds a steam boiler pool with power readings and power control,
following the structure of the EV charger and PV pools.
Changes
microgrid.new_steam_boiler_pool()returning aSteamBoilerPoolwithpower,power_status, and a consumption-onlypropose_power(negative power is rejected).SteamBoilerDatacomponent data type and data sourcing support.DefaultPower.ZEROwhen no actor is proposing.SteamBoilerManagersplits the target power equally across the workingboilers, keeping every allocation inside the boiler's inclusion bounds: a
share below a boiler's minimum operating power is raised to the minimum, or
the boiler is kept off. Like the PV manager, it subtracts the measured draw
of unreachable boilers from the target.
SteamBoilerStatusTrackerfor per-boiler health, mirroring the PV tracker.Worth a look
boiler can't be afforded instead of topping up earlier boilers
(
test_unaffordable_minimum_strands_excesspins this).same behavior as the PV and EV managers; a follow-up could make all three
send an
Errorresult the way the battery manager does.