Skip to content

Keep explicit zero directional capacity hard under relax-constraints - #2345

Merged
Flix6x merged 8 commits into
FlexMeasures:mainfrom
sankalpsthakur:fix-2323-explicit-zero-capacity-hard
Aug 7, 2026
Merged

Keep explicit zero directional capacity hard under relax-constraints#2345
Flix6x merged 8 commits into
FlexMeasures:mainfrom
sankalpsthakur:fix-2323-explicit-zero-capacity-hard

Conversation

@sankalpsthakur

@sankalpsthakur sankalpsthakur commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Description

  • Treat an explicit all-zero production-capacity / consumption-capacity as a physical impossibility, not an economic limit
  • Under relax-constraints (or when breach prices are set), keep the hard derivative bound and do not create soft device capacity FlowCommitments for that direction
  • Non-zero directional capacities may still be softened via breach prices (unchanged; explicit regression)
  • Regression tests: hard bounds + no soft breach commitments for both directions; schedule never produces/consumes when that directional capacity is 0 with a directional breach price set
  • Changelog entry

Context

Fixes #2323

Impact

Severity: physically impossible schedules under default relax-constraints.

Observed (community co-sim, #2323): heat pump with explicit production-capacity: 0 kW scheduled −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_positive on consumption-only sensors.

How to test

pytest \
  flexmeasures/data/models/planning/tests/test_solver.py::test_explicit_zero_directional_capacity_stays_hard_under_relax_constraints \
  flexmeasures/data/models/planning/tests/test_solver.py::test_non_zero_directional_capacity_still_softens_under_breach_price \
  flexmeasures/data/models/planning/tests/test_solver.py::test_explicit_zero_directional_capacity_not_breached_in_schedule \
  flexmeasures/data/models/planning/tests/test_solver.py::test_device_power_capacity_uses_directional_capacity_before_site_fallback \
  flexmeasures/data/models/planning/tests/test_solver.py::test_capacity \
  -v

Related Items


Sign-off

  • I agree to contribute to the project under Apache 2 License.
  • To the best of my knowledge, the proposed patch is not based on code under GPL or other license that is incompatible with FlexMeasures
  • Added changelog item in documentation/changelog.rst

AI/LLM disclosure

  • AI coding tools (including Grok and/or Codex agent-assisted editing) were used to help draft or modify code and this PR description.
  • I reviewed the complete change, understand the reasoning, and ran the reported local tests before submitting.
  • This submission is original work of authorship under the project CLA / contributor terms; AI output was not pasted unreviewed.

sankalpsthakur added a commit to sankalpsthakur/flexmeasures that referenced this pull request Jul 22, 2026
Signed-off-by: Sankalp Thakur <sankalphimself@gmail.com>
@sankalpsthakur
sankalpsthakur force-pushed the fix-2323-explicit-zero-capacity-hard branch 3 times, most recently from 076a8d0 to 8ec7149 Compare July 22, 2026 19:03
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>
@Flix6x

Flix6x commented Aug 5, 2026

Copy link
Copy Markdown
Member

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:

  • physics — the S2 CEM in Explicit zero directional capacity should stay a hard constraint under relax-constraints #2323 derives production-capacity: 0 kW from a heat pump's operation modes; it cannot produce at any price;
  • preference — V2G-Liberty sets a windowed production-capacity: 0 kW during a calendar car reservation, so the charger stands idle once the soc-minima is reached and the user can unplug immediately (DC charging risks an arc if uncoupled while power is flowing). This is in our shipped docs, in documentation/tut/flex-model-v2g.rst.

The two separate on exactly the signal production_capacity_d.max() > 0 tests: physics zeros span the whole scheduling window; preference zeros are windowed. So making the guard elementwise — which an earlier draft of #2395 argued for — would harden V2G-Liberty's reservation windows and break the documented tutorial. Please keep it as it is.

Two small requests before merge:

  1. Drop the soft_breach_names == [] assertion. The FlowCommitments can harmlessly remain: on the hardened slots their quantity is 0 and the hard bound pins flow to 0, so they are unbreachable and cost nothing. Asserting their absence over-specifies an incidental detail.
  2. Add a test asserting that a windowed zero (zero over part of the horizon, non-zero elsewhere) stays soft. That is the V2G-Liberty contract and nothing currently pins it, so the next person to "simplify" this guard would break it silently. Happy to contribute that test as a follow-up if you would rather not expand the PR.

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:

A directional capacity that is zero throughout the scheduling window is read as a physical property of the device and is enforced strictly. A zero covering only part of the window is read as a preference and may be breached at the applicable breach price.

On the PR description's note that this also fixes #2329: it fixes the reported EVSE discharge (that request's production-capacity segment covers the whole PT27H window at 0 W), and it correctly leaves that device's consumption zeros at 17:00–18:00 soft, since those coincide with its soc-minima and are the reservation pattern.

@Flix6x Flix6x added this to the 1.0.0 milestone Aug 5, 2026
Flix6x and others added 2 commits August 7, 2026 14:13
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>
@Flix6x

Flix6x commented Aug 7, 2026

Copy link
Copy Markdown
Member

I have pushed the two changes I asked for above, plus a merge of main, so this does not sit waiting on you — thanks for bearing with the back-and-forth on the semantics.

1. Merged main. It now contains #2398, which stops relax-constraints from softening device capacities at all. That narrows this PR's audience — the default path no longer creates the situation — but it does not replace it: #2329 opts in with 'relax-capacity-constraints': true explicitly, so its EVSE's production-capacity: 0 W is still soft without this PR.

2. Dropped the soft_breach_names == [] assertion. It over-specifies. Next to the hard bound those commitments carry quantity 0, so the pinned flow cannot deviate from them: they are unbreachable and free. What the device obeys is the bound, which the same test already asserts. I left a comment in place of the assertion saying so.

3. Added test_windowed_zero_directional_capacity_stays_soft. This is the important one, and it pins the half of the rule that had no coverage.

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 production_capacity_d.max() > 0 is what distinguishes them, and that is correct — a heat pump never produces, whereas a reservation is a window.

That matters because a real client depends on the preference half. V2G-Liberty punches value: 0 windows into an otherwise non-zero capacity for each calendar car reservation, so the charger stands idle once the SoC target is met and the driver can unplug without waiting for the socket to unlock (DC charging risks an arc if uncoupled under flow). Hardening those windows would turn that preference into an infeasibility whenever a soc-minimum needs the device to act during one. The V2G tutorial documents the same shape.

I checked the new test has teeth: mutating the guard to a per-timestep variant (not (production_capacity_d == 0).any()) makes it fail with derivative min of [-2, -0], i.e. the zero hour hardened. With the guard as you wrote it, it passes.

4. Wrote the rule down, on both consumption-capacity and production-capacity in metadata.py, and rewrote the changelog entry to state both halves. It was emergent behaviour before — nothing said it, and nothing stopped a later reader from "simplifying" the horizon-wide check into a per-timestep one.

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.

@Flix6x Flix6x self-assigned this Aug 7, 2026
Flix6x and others added 2 commits August 7, 2026 14:38
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>
@Flix6x
Flix6x merged commit 3327ad4 into FlexMeasures:main Aug 7, 2026
8 of 9 checks passed
Flix6x added a commit that referenced this pull request Aug 7, 2026
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>
@Flix6x Flix6x mentioned this pull request Aug 7, 2026
7 tasks
Flix6x added a commit that referenced this pull request Aug 7, 2026
…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>
Flix6x added a commit that referenced this pull request Aug 7, 2026
* 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>
Flix6x added a commit that referenced this pull request Aug 7, 2026
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>
BelhsanHmida added a commit that referenced this pull request Aug 7, 2026
* 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Explicit zero directional capacity should stay a hard constraint under relax-constraints

2 participants