Keep explicit zero directional capacity hard under relax-constraints - #2345
Conversation
Signed-off-by: Sankalp Thakur <sankalphimself@gmail.com>
Documentation build overview
77 files changed ·
|
076a8d0 to
8ec7149
Compare
With relax-constraints defaulting to True, an explicit production-capacity: 0 (or consumption-capacity: 0) was turned into a soft FlowCommitment whenever breach prices were present. The LP could then schedule power in a physically impossible direction (e.g. produce from a heat pump). Treat an all-zero directional capacity as a physical impossibility: keep the hard derivative bound and skip soft breach commitments. Non-zero (economic) capacity limits may still be relaxed. Fixes FlexMeasures#2323 Signed-off-by: Sankalp Thakur <sankalphimself@gmail.com>
Companion regression for FlexMeasures#2323: only explicit zero stays hard; economic non-zero production/consumption capacities must still create soft breach commitments. Signed-off-by: Sankalp Thakur <sankalphimself@gmail.com>
Parametrize the end-to-end schedule regression so both production and consumption explicit zeros stay hard under directional breach prices. Signed-off-by: Sankalp Thakur <sankalphimself@gmail.com>
…ression Signed-off-by: Sankalp Thakur <sankalphimself@gmail.com>
8a366ad to
2c536cf
Compare
|
Context for this PR from #2395, which works out the semantics behind it: this PR should land as written — the horizon-global guard is not a rough edge, it is doing the classification the design needs. A fixed zero directional capacity carries two incompatible meanings in the wild:
The two separate on exactly the signal Two small requests before merge:
Also worth a line in the changelog and the flex-model docs stating the rule explicitly, since it is currently emergent rather than written down:
On the PR description's note that this also fixes #2329: it fixes the reported EVSE discharge (that request's |
The rule this PR implements is that a directional capacity of zero states a physical property of the device when it holds for the whole scheduling window, and a preference when it covers only part of it. That rule was emergent: nothing stated it, and nothing stopped a later reader from "fixing" the horizon-wide test into a per-timestep one, which would harden preferences. So pin the preference half with a test, and state the rule where users meet the fields. V2G-Liberty relies on the preference half to keep a charger idle during a calendar car reservation, so that the user can unplug without waiting for the socket to unlock. Also drop the assertion that no soft breach commitments are constructed. It over-specifies: alongside the hard bound those commitments have quantity 0, so they cannot be deviated from and cost nothing. What the device obeys is the bound, which the same test already asserts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: F.N. Claessen <claessen@seita.nl>
|
I have pushed the two changes I asked for above, plus a merge of 1. Merged 2. Dropped the 3. Added The rule this PR implements is: a directional capacity of zero states a physical property of the device when it holds for the whole scheduling window, and a preference when it covers only part of it. Your horizon-wide That matters because a real client depends on the preference half. V2G-Liberty punches I checked the new test has teeth: mutating the guard to a per-timestep variant ( 4. Wrote the rule down, on both For context on where this fits: #2395 covers the wider question of why one field carries two meanings, and what it would take to stop guessing. |
The rule applies to both directional capacity fields and is longer than either description, so repeating it inline crowded out what the fields are for. Both now cite it as a footnote, next to the overlap-resolution one they already share. Also break the new docstring and comments after punctuation, per the repo's convention. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: F.N. Claessen <claessen@seita.nl>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: F.N. Claessen <claessen@seita.nl>
Scheduler behaviour changed since v0.33.1 for identical inputs, through this PR (explicit relax-flag opt-outs are honoured, explicit breach prices are no longer overwritten, no more fallback schedules) as well as PR #2398 and PR #2345 (device capacity and zero-capacity relaxation semantics), so data sources should distinguish v9 schedules from v8 ones. Signed-off-by: F.N. Claessen <claessen@seita.nl>
…ics-hardening The previous wording asserted a one-directional softening trend, while #2398 and #2345 hardened physical statements at the same time. Reframe: preferences (SoC minima/maxima, site capacities) are soft by default, and contradictory physics could never yield an executable schedule, so failing with the reason beats a fallback schedule that ignored the contradiction. Signed-off-by: F.N. Claessen <claessen@seita.nl>
* feat: relax storage SoC bounds by default Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * feat: remove storage fallback scheduler Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * feat: remove storage fallback policy helper Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * test: cover SoC relaxation schema defaults Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * test: use default SoC breach prices Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * test: expect storage infeasibility without fallback Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * test: assert storage schedules do not fall back Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * test: update sequential scheduling fallback case Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * docs: describe default SoC relaxation metadata Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * docs: update storage scheduling infeasibility guide Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * docs: clarify fallback redirects for custom schedulers Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * docs: scope fallback redirect configuration Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * docs: refresh SoC relaxation OpenAPI text Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * docs: add fallback scheduler changelog entry Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * test: allow small unit conversion drift Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * test: avoid exact quantity string comparisons Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * docs: clarify storage infeasibility behavior Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * test: simplify storage fallback assertion comment Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * docs: regenerate openapi-specs.json Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * fix: preserve explicit zero breach prices Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * chore: align agent instructions with main Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * docs: fix jobs OpenAPI description indentation Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * fix(schema): let relax-constraints=false keep SoC constraints hard Both relax-soc-constraints and relax-constraints default to True, so the former's default swallowed an explicit opt-out through the umbrella flag. Now an explicit relax-soc-constraints wins and otherwise the umbrella relax-constraints decides, per the semantics discussed in PR #2267: setting either flag to False keeps SoC minima/maxima hard. Also document why DBFlexContextSchema turns the relaxation defaults off. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: restore hard-constraints list and describe the SoC relaxation opt-out The capacity bounds (power-capacity and site-power-capacity) still remain hard constraints after relaxation; mention them again alongside soc-min, soc-max and soc-targets, and explain that setting either relax-soc-constraints or relax-constraints to false keeps SoC minima/maxima hard. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: restore config via monkeypatch and fix truncated docstring Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs+test: fix review follow-ups for relax-soc-constraints default - Correct RELAX_CONSTRAINTS metadata text: it now defaults to True, not False. - Give DBFlexContextSchema's relax flags their own description, since a stored flex-context defaults to False, unlike the True scheduling-time default applied after merging with the request. - Align the hard-constraints list in scheduling.rst with introduction.rst (both now mention power-capacity / site-power-capacity). - Add the missing v3.0-32 API changelog entry for the relax-soc-constraints default flip and the retired fallback scheduler's effect on GET schedule. - Drop now-tautological assertions checking for "StorageFallbackScheduler" in belief sources, since that class no longer exists in the codebase. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs: describe what this PR actually changes about SoC relaxation SoC constraints are already soft by default on main: relax-soc-constraints defaults to False there, but the umbrella relax-constraints defaults to True and check_prices fills the default breach prices on either flag. So "SoC constraints are now relaxed by default" reads as a behaviour change that already shipped, and invites users to brace for breaching schedules they are already getting. What this PR changes is the opt-out: an explicit relax-soc-constraints: false is now honoured, where main silently overrode it with the relax-constraints default. Lead with the fallback retirement, which is the actual headline. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * Let an explicit relax flag take precedence over the umbrella relax-constraints, for site capacities too An unset relax-soc-constraints or relax-site-capacity-constraints now loads as None, meaning the umbrella relax-constraints flag decides; an explicitly set specific flag wins in either direction. This extends the fix for 'the field had no effect on its own' from SoC constraints to site capacity constraints, via a shared relaxation_asked_for helper (also used by the commodity-context zero-capacity defaulting). Signed-off-by: F.N. Claessen <claessen@seita.nl> * Fill default breach prices per field for the SoC and site capacity pairs Setting one breach price of a relaxed-by-default pair explicitly no longer silently hardens its twin; the twin still gets the default breach price. The device capacity pair keeps its pair-as-unit semantics (an explicit opt-in puts the caller in charge of both directions), as established in PR #2398. Also, an explicitly set breach price (including a zero price, which is falsy as a quantity) is never overwritten anymore. Signed-off-by: F.N. Claessen <claessen@seita.nl> * Replace the DB schema's relax-flag overrides with a single fill switch A stored asset flex-context is only validated (never scheduled directly), so DBFlexContextSchema now simply skips filling default breach prices, instead of overriding the relax-flag defaults with misleading per-field descriptions. The relax flags keep uniform defaults and documentation across both schemas; scheduling-time defaults are applied after the stored flex-context is merged with the request's flex-context. Signed-off-by: F.N. Claessen <claessen@seita.nl> * chore: increment StorageScheduler version Scheduler behaviour changed since v0.33.1 for identical inputs, through this PR (explicit relax-flag opt-outs are honoured, explicit breach prices are no longer overwritten, no more fallback schedules) as well as PR #2398 and PR #2345 (device capacity and zero-capacity relaxation semantics), so data sources should distinguish v9 schedules from v8 ones. Signed-off-by: F.N. Claessen <claessen@seita.nl> * docs: cover the full relaxation-semantics change and name the removed plugin symbols Adds an upgrade warning for the retired fallback scheduler (naming the removed importable symbols), splits the changelog entry into the fallback retirement and the relaxation-semantics fixes, and extends the API changelog with the site-capacity opt-out and the never-overwrite/per-field breach price behaviour. Signed-off-by: F.N. Claessen <claessen@seita.nl> * docs: describe the relaxation change relative to the last release In v0.33.1 all four relax flags defaulted to False and the specific flags were independent opt-ins, so 'these flags had no effect on their own' only ever described unreleased main (after the umbrella default flipped to True). Reframe both changelog entries accordingly and split the API entry in two, so the fallback retirement stands alone. The never-overwrite fix does describe released behaviour and stays. Signed-off-by: F.N. Claessen <claessen@seita.nl> * Cover the fallback machinery for custom schedulers at the API level Salvages the retired built-in fallback scheduler's API tests, substituting a custom scheduler (via the sensor's custom-scheduler attribute) that defines a fallback scheduler: clients get a 303 redirect to the fallback schedule under FLEXMEASURES_FALLBACK_REDIRECT = True, or the fallback schedule directly under the default False. Also cushions the changelog warning (the fallback is expected to be needed much less, now that SoC constraints are relaxed by default) and fixes a stale fallback comment in the highspy tests. Signed-off-by: F.N. Claessen <claessen@seita.nl> * docs: shorten the relax-flag field descriptions The precedence rule now lives in full on relax-constraints only; the specific flags carry a two-sentence echo, the device-capacity rationale moves to relax-capacity-constraints, and soc-minima/soc-maxima point at relax-soc-constraints with a shared soft_by_default footnote in the docs. Signed-off-by: F.N. Claessen <claessen@seita.nl> * docs: frame the fallback retirement as preference-softening plus physics-hardening The previous wording asserted a one-directional softening trend, while #2398 and #2345 hardened physical statements at the same time. Reframe: preferences (SoC minima/maxima, site capacities) are soft by default, and contradictory physics could never yield an executable schedule, so failing with the reason beats a fallback schedule that ignored the contradiction. Signed-off-by: F.N. Claessen <claessen@seita.nl> * Validate power bounds for every device, and stop a stock-less device from disabling validation A device without a stock set the shared skip_validation flag, which silently disabled constraint validation for all subsequent devices in the same flex-model; stock-constraint validation is now tracked per device. Also add validate_power_constraints, run for every device (stock-less included): derivative min <= derivative max, and derivative equals within those bounds, reported through the existing per-time-step violations message, so a contradictory hard power bound fails usefully instead of as a bare solver infeasibility. This matters more now that no fallback schedule cushions such failures. Signed-off-by: F.N. Claessen <claessen@seita.nl> --------- Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> Signed-off-by: F.N. Claessen <claessen@seita.nl> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: F.N. Claessen <claessen@seita.nl>
Since #2345, a consumption-capacity or production-capacity that is zero throughout the scheduling window is read as a physical statement about the device and enforced strictly. A default fills every slot the sensor leaves empty, so a fallback of 0 on one of those fields turns a silent sensor into a hard bound, which is not what "use this value when the sensor has nothing to say" sounds like it does. Regenerating the specs here also restores their version to 1.0.0. The generator takes it from the installed FlexMeasures, so the 0.33.2 in the previous revision records a stale environment rather than an intended change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: F.N. Claessen <claessen@seita.nl>
* feat: relax storage SoC bounds by default Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * feat: remove storage fallback scheduler Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * feat: remove storage fallback policy helper Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * test: cover SoC relaxation schema defaults Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * test: use default SoC breach prices Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * test: expect storage infeasibility without fallback Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * test: assert storage schedules do not fall back Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * test: update sequential scheduling fallback case Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * docs: describe default SoC relaxation metadata Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * docs: update storage scheduling infeasibility guide Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * docs: clarify fallback redirects for custom schedulers Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * docs: scope fallback redirect configuration Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * docs: refresh SoC relaxation OpenAPI text Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * docs: add fallback scheduler changelog entry Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * test: allow small unit conversion drift Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * test: avoid exact quantity string comparisons Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * docs: clarify storage infeasibility behavior Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * test: simplify storage fallback assertion comment Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * docs: regenerate openapi-specs.json Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * fix: preserve explicit zero breach prices Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * chore: align agent instructions with main Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * docs: fix jobs OpenAPI description indentation Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * fix(schema): let relax-constraints=false keep SoC constraints hard Both relax-soc-constraints and relax-constraints default to True, so the former's default swallowed an explicit opt-out through the umbrella flag. Now an explicit relax-soc-constraints wins and otherwise the umbrella relax-constraints decides, per the semantics discussed in PR #2267: setting either flag to False keeps SoC minima/maxima hard. Also document why DBFlexContextSchema turns the relaxation defaults off. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: restore hard-constraints list and describe the SoC relaxation opt-out The capacity bounds (power-capacity and site-power-capacity) still remain hard constraints after relaxation; mention them again alongside soc-min, soc-max and soc-targets, and explain that setting either relax-soc-constraints or relax-constraints to false keeps SoC minima/maxima hard. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: restore config via monkeypatch and fix truncated docstring Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs+test: fix review follow-ups for relax-soc-constraints default - Correct RELAX_CONSTRAINTS metadata text: it now defaults to True, not False. - Give DBFlexContextSchema's relax flags their own description, since a stored flex-context defaults to False, unlike the True scheduling-time default applied after merging with the request. - Align the hard-constraints list in scheduling.rst with introduction.rst (both now mention power-capacity / site-power-capacity). - Add the missing v3.0-32 API changelog entry for the relax-soc-constraints default flip and the retired fallback scheduler's effect on GET schedule. - Drop now-tautological assertions checking for "StorageFallbackScheduler" in belief sources, since that class no longer exists in the codebase. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * feat: support a `default` fallback on sensor references A sensor reference on any flex-model or flex-context field may now carry a `default` quantity, e.g. {"sensor": 50, "default": "0 kWh"}. It fills the time slots for which the referenced sensor holds no value, and is settable from the flex-model UI. This is the uncontroversial half of #2267, split out on Felix's suggestion so it can land while the SoC constraint hardness question is settled across the whole flex-model (see #2395). The canonical soc-min/soc-max work stays on feat/dynamic-soc-bounds-defaults. Note that a default fills *every* slot the sensor leaves empty, so a sensor recording only occasional setpoints becomes densely constrained; the field documentation says so. Also omit `default` from serialized sensor references when it is unset, rather than emitting `default: None`, which is not valid input on the way back in and broke the forecaster config round-trips. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * docs: scope the sensor-reference `default` to variable-quantity fields `default` is declared on the shared SensorReferenceSchema, so it is accepted on every sensor reference in the API — but it is only applied in get_series_from_quantity_or_sensor. Sensor references resolved by get_power_values (inflexible devices) and by the forecasting pipelines (regressors) ignore it, silently. Rather than claim more than the code does, say where the field takes effect. Applying it in those two paths is worth a follow-up. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * revert the fallback-scheduler content from this branch This branch was carved out of #2267, which was stacked on feat/retire-fallback-scheduler, so #2252's commits travelled along with it. Now that this PR targets main, drop that content here: the tree of this commit equals main plus the sensor-reference `default` work alone. No behaviour of this PR changes; #2252 lands on its own. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> * Warn about a zero fallback on a directional capacity Since #2345, a consumption-capacity or production-capacity that is zero throughout the scheduling window is read as a physical statement about the device and enforced strictly. A default fills every slot the sensor leaves empty, so a fallback of 0 on one of those fields turns a silent sensor into a hard bound, which is not what "use this value when the sensor has nothing to say" sounds like it does. Regenerating the specs here also restores their version to 1.0.0. The generator takes it from the installed FlexMeasures, so the 0.33.2 in the previous revision records a stale environment rather than an intended change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: F.N. Claessen <claessen@seita.nl> --------- Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com> Signed-off-by: Mohamed Belhsan Hmida <149331360+BelhsanHmida@users.noreply.github.com> Signed-off-by: F.N. Claessen <claessen@seita.nl> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: F.N. Claessen <claessen@seita.nl>
Description
production-capacity/consumption-capacityas a physical impossibility, not an economic limitrelax-constraints(or when breach prices are set), keep the hard derivative bound and do not create soft device capacityFlowCommitments for that directionContext
Fixes #2323
Impact
Severity: physically impossible schedules under default
relax-constraints.Observed (community co-sim, #2323): heat pump with explicit
production-capacity: 0 kWscheduled −1.764 kW production when site capacity was tight and site breach prices (10 000 EUR/kW) dwarfed device breach prices (100 EUR/kW). The LP rationally breached a soft zero-capacity commitment.Root cause: explicit zero was turned into a soft
FlowCommitment; hard bound stayed at symmetric-power-capacity. Breach prices soften limits, not impossibilities (#2272 spirit).Workaround before this fix: set
is_strictly_non_positiveon consumption-only sensors.How to test
Related Items
production-capacity: 0 W— same zero-capacity softening class; this PR fixes that constraint path (multi-asset interaction there may still need separate follow-up)Sign-off
documentation/changelog.rstAI/LLM disclosure