Repository navigation
Conversation
…lipping it S2 lets a resource manager expect any usage, negative included, and says nothing about what happens when the storage cannot take it. FlexMeasures plans a forecast usage as forced, so a gain at a full storage, or a draw at an empty one the actuator cannot keep up with, left no feasible plan. The handler clipped negative usage to zero to avoid the first case. With a usage_correction_sensor_id and FlexMeasures v1.1.0 or newer, the handler now asks for a schedule with a correction instead: a second device on the storage's stock, on a commodity of its own, which may only move each step's expected usage towards zero. Its price is twice a bound on what a unit of stock can be worth to the scheduler, computed from a coherent set of prices the request carries: band breach prices of 10x, and a site capacity breach price of 100x, the highest absolute market price over the window. So it is used only where nothing physical keeps the plan within the hard bounds. A negative usage is then recorded and planned as stated. Where a correction cannot be priced or shared (no prices, a state-of-charge sensor with a resolution), the forecast is planned without its negative values, as before. The site configurator and the example server give each site a "usage correction" sensor. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CcpawsJs74cSwFLXkz7ZwW Signed-off-by: F.N. Claessen <claessen@seita.nl>
- The stock was planned against every forecast on the usage sensor, while the correction's limits and the translation back came from the latest one only, so an older forecast's gain had no correction. The stock now gets its usage inline, from the same series. Before any forecast has arrived since the handler started, the plan goes without usage, since the sensor may hold negative usage recorded as stated. - A request with a commodity list made FlexMeasures ignore the site's stored electricity flex context: a contracted capacity or a peak price was lost. Those fields are carried into the electricity entry, and stored per-MW prices (peaks, commitments) go into the bound; one that is not a fixed price per MW leaves the plan without a correction. - A stored relax-capacity-constraints would make the correction's limits soft; device capacities are now kept hard in the correction path. - Efficiencies above one (a heat pump's COP, a storage that gains) no longer lower the bound. - The correction is opt-in for sites: FRBCSiteConfigurator(correct_usage=) and the example server's S2_CORRECT_USAGE, since it also replaces FlexMeasures' default breach prices with market-relative ones. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CcpawsJs74cSwFLXkz7ZwW Signed-off-by: F.N. Claessen <claessen@seita.nl>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CcpawsJs74cSwFLXkz7ZwW Signed-off-by: F.N. Claessen <claessen@seita.nl>
Coverage Report for CI Build 42648128287Warning No base build found for commit Coverage: 96.188%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsRequires a base build to compare against. How to fix this → Coverage Stats
💛 - Coveralls |
…s with The co-simulation's controller steers each home with peak prices it stores on the site as sensor references. The bound accepted only fixed prices per MW, so every plan fell back to planning without a correction, in the very setting the correction is for. A stored price on a sensor is now bounded by its highest absolute value over the window, read in the market's currency per MW, as the market prices are. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CcpawsJs74cSwFLXkz7ZwW Signed-off-by: F.N. Claessen <claessen@seita.nl>
The first co-simulation run with the correction on had every request refused with 422: the site's stored flex context holds aggregate-power, which FlexMeasures accepts only at the top level, and the request copied it into the electricity entry. Only the fields FlexMeasures' commodity flex-context schema lists are copied now; the others stay where FlexMeasures merges them in itself. Checked against FlexMeasures v1.1.0's API: the request this handler builds, against a stored context shaped like a co-simulation's (sensor peak prices and capacities, inflexible devices, aggregate-power), is accepted (202), and its job removes exactly the gain the hard top forces. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CcpawsJs74cSwFLXkz7ZwW Signed-off-by: F.N. Claessen <claessen@seita.nl>
Pricing the usage correction above a breach of the site's capacity forced the request to set its own, lower site breach price: at FlexMeasures' default, the correction price would exceed what HiGHS solves reliably. In a steered co-simulation, a site breach price low enough to keep the correction solvable made the houses ignore their steering capacity, and one high enough to steer made every house's schedule infeasible. The correction is now priced from the market and the fill-level band only, and the site's breach prices are left to the asset or FlexMeasures. A correction can then be cheaper than a site breach, but stays dearer than a breach of the band. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CcpawsJs74cSwFLXkz7ZwW Signed-off-by: F.N. Claessen <claessen@seita.nl>
…ed request FlexMeasures fills in its default site breach price (10000 per kW) only for a flex context without commodities. In a commodity entry, a site capacity without a breach price stays hard. A corrected request names its commodities, so a steered site capacity became hard, and a house whose inflexible load exceeded it had no feasible plan. The electricity entry now states that default where the site's capacity may be breached, unless the asset stores a breach price of its own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CcpawsJs74cSwFLXkz7ZwW Signed-off-by: F.N. Claessen <claessen@seita.nl>
…cted request FlexMeasures merges the asset's stored flex context in at the top level of a request, and counts inflexible devices from there as well as from the electricity entry, to which the request also carries them. So each was counted twice: a house's planned aggregate power came out at twice its inflexible load, and a community planner saw a breach that was never realized (FlexMeasures/flexmeasures#2848). The request now names the same inflexible-device fields empty at the top level, which keeps FlexMeasures from merging the stored ones in there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CcpawsJs74cSwFLXkz7ZwW Signed-off-by: F.N. Claessen <claessen@seita.nl>
S2 allows a negative expected usage, a gain. It was recorded as zero, which hid what the resource manager stated, to keep plans feasible without a usage correction. Recording and planning are separate questions: a schedule request now carries the latest forecast, without its negative values where no correction plans them, so the usage sensor can hold what was stated. A FlexMeasures server before v0.23.0 takes a usage only as a sensor, so there it is still recorded as zero. Until a forecast arrives, a handler plans without usage, since the sensor may hold negative usage recorded before it started. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CcpawsJs74cSwFLXkz7ZwW Signed-off-by: F.N. Claessen <claessen@seita.nl>
|
Co-simulation results for this PR: two simulated homes with heat pumps, one day, steered as a community under a 2 kW limit. Each run uses deterministic solver settings.
Comfort was kept in every run, and no run fell back to its last plan. What this shows:
Next: find why the community steering converges to signals that leave the homes' plans over the limit when the correction is on. Until then, the correction (opt-in) should not be switched on for steered communities. |
Stacked on #279 (base
dev/s2-frbc-complete), as asked in the review of #271. Merge #279 first.Why
S2 lets a resource manager expect any usage, negative included, and says nothing about what happens when the storage cannot take it. FlexMeasures plans a forecast usage as forced. So a gain at a full storage, or a draw at an empty one that the actuator cannot keep up with, leaves no feasible plan, and the storage gets no instruction. Until now the handler clipped negative usage to zero, which hid the first case and not the second.
What
With a
usage_correction_sensor_idand FlexMeasures v1.1.0 or newer,FRBCSimpleasks for a schedule with a correction of the usage forecast:A second device on the storage's stock (same state-of-charge sensor), on a commodity of its own, so it never touches the site's electricity. No instruction is ever sent for it.
Per step, it may only move the expected usage towards zero: give back up to the expected usage, or take away up to the expected gain. It works the same on both sides, since S2's fill levels do not say which bound is an overflow.
It is priced as a last resort: at twice an upper bound on what one MWh of stock can be worth to the scheduler. The request carries a coherent set of prices to make that bound finite and numerically sound:
soc_breach_price_factor);Device capacities are kept hard, so the correction cannot move a usage past zero.
A negative expected usage is then planned as stated. It is also recorded as stated without a correction (since 905254e, from a review comment on docs(changelog): say each S2 change plainly #283): there, the schedule request carries the forecast without its negative values. Only a FlexMeasures server before v0.23.0, which takes a usage only as a sensor, still gets it recorded as zero. The usage is sent inline, from the same forecast as the correction's limits and the translation back into instructions.
Where a correction cannot be planned (prices unreadable, a state-of-charge sensor with a resolution, a stored price that cannot be bounded, a stored flex context with commodities), the forecast is planned without its negative values, as before, with a warning saying why.
It is opt-in:
FRBCSiteConfigurator(correct_usage=True), orS2_CORRECT_USAGE=1for the example server. That is because it replaces FlexMeasures' default band breach price (1000 EUR/kWh) with a market-relative one, which is the deployment's call. Handlers without the new sensor behave exactly as before.How the price was chosen
Probes against FlexMeasures v1.1.0 measured, per scenario, the correction price above which the scheduler stops using a correction it does not need. That threshold is what a MWh of stock is worth to the scheduler. Each matched one term of the bound:
With FlexMeasures' default breach prices, a dominant price was about 1e8 EUR/MWh, where HiGHS already returned over-corrected plans. With the coherent set, over a 24-hour window with prices from −120 to 450 EUR/MWh, the correction price is about 1.8e6 EUR/MWh:
Priced at the band's breach price instead, the correction replaced band breaches (9.24 vs 3.12 kWh) and corrected where nothing needed correcting.
Why the site's capacity breach is left out. Pricing the correction above a site breach needs the request to set a site breach price the solver can live with. In the co-simulation (below), a site breach price of 100× the market price kept the correction solvable but let the houses ignore their steering capacity, and 1000× pushed the correction price to 1.3e7 EUR/MWh, where HiGHS declared every house schedule infeasible (replays put the cliff between 0.7e7 and 1.3e7 for this house). So a correction can now be cheaper than a site breach, while it stays dearer than a band breach, which the plan still uses first.
The asset's stored inflexible devices are carried into the electricity entry, and named empty at the top level, because FlexMeasures merges the stored context in there too and would count them twice (FlexMeasures/flexmeasures#2848).
FlexMeasures fills in its default site breach price only for a flex context without commodities; in a commodity entry, a site capacity without one stays hard. The request therefore states that default itself (FlexMeasures/flexmeasures#2846).
Evidence
relax-capacity-constraintswould soften the limits; and that efficiencies above one lowered the bound. All are fixed in the second commit, which also makes the correction opt-in.Co-simulation
One steered day, two houses, the community capacity policy, deterministic solver settings. Every run below had 0 degree-hours outside the comfort band.
The correction is opt-in. Until the cause is found, it should not be switched on for steered communities.
🤖 Generated with Claude Code
https://claude.ai/code/session_01CcpawsJs74cSwFLXkz7ZwW