Skip to content

Keep device capacities hard unless relaxation is asked for - #2398

Open
Flix6x wants to merge 2 commits into
mainfrom
fix/no-default-device-capacity-relaxation
Open

Keep device capacities hard unless relaxation is asked for#2398
Flix6x wants to merge 2 commits into
mainfrom
fix/no-default-device-capacity-relaxation

Conversation

@Flix6x

@Flix6x Flix6x commented Aug 5, 2026

Copy link
Copy Markdown
Member

Description

  • Fill in default device breach prices only when relaxation is asked for — through relax-capacity-constraints, or through a relax-constraints that was passed explicitly rather than defaulted
  • Leave the SoC and site capacity constraints following the default, where softening was the point
  • Stop overwriting explicitly given device breach prices with the defaults
  • Docs (relax-constraints field description, API introduction note) + changelog

Context

Fixes the default-path half of #2323, and is step 1 of the plan in #2395.

Since relax-constraints began defaulting to True (#2172, unreleased), every scheduling request gets default device breach prices, which turns a directional device capacity into a soft constraint for everybody. That is wrong wherever the capacity states a physical impossibility rather than an economic preference — in #2323 a heat pump with production-capacity: 0 kW was scheduled to produce −1.764 kW, because breaching the device (100 EUR/kW) was cheaper than breaching the site (10 000 EUR/kW). The LP was right, given what we told it.

Before the default flip, softening only ever happened when the user explicitly set consumption-breach-price / production-breach-price (shipped in v0.26.0, PR #1405). "You asked for a breach price, so we softened your capacity" is a defensible reading of an opt-in. This PR restores that opt-in without taking anything away: an explicit relax-constraints: true still relaxes everything, exactly as it did before.

Impact

Behaviour change, on top of an unreleased behaviour change, so most users never saw the intermediate state.

flex-context device capacities SoC site capacities
(nothing given) hard (was soft) soft soft
relax-constraints: true soft soft soft
relax-capacity-constraints: true soft soft soft
relax-constraints: false hard hard hard

Anyone who wants the current behaviour adds one field. Anyone who was relying on the pre-#2172 opt-in is unaffected.

Second change: explicit device breach prices are no longer overwritten

The guard's own comment says "unless already set explicitly", but A or B and C and D parses as A or (B and C and D), so the "already set" conditions only ever guarded the relax_constraints branch — and set_default_breach_prices assigns unconditionally. Setting relax-capacity-constraints: true together with consumption-breach-price: 7 EUR/kW therefore silently replaced the 7 with the 100 EUR/kW default. It now keeps the caller's price.

The same precedence shape remains in the SoC and site-capacity blocks. Fixing those changes behaviour for soc-minima/soc-maxima and the site capacities too, and the SoC one needs the relax flags to become tri-state (relax_soc_constraints has load_default=False, so "unset" and "explicitly False" are indistinguishable). Left for its own PR; tracked in #2395.

What this does not fix

#2329, which passes 'relax-capacity-constraints': true explicitly. That is an opt-in, so the device breach prices are still filled and its EVSE's production-capacity: 0 W is still soft. Closing that needs a way to tell a physical zero from a preferential one — see #2395, and PR #2345 for the interim rule.

How to test

pytest \
  flexmeasures/data/schemas/tests/test_scheduling.py::test_device_capacity_relaxation_is_opt_in \
  flexmeasures/data/schemas/tests/test_scheduling.py::test_explicit_device_breach_price_is_not_overwritten \
  flexmeasures/data/schemas/tests/ \
  flexmeasures/data/models/planning/tests/ \
  -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

🤖 Generated with Claude Code

https://claude.ai/code/session_014qPe5bv41RDTuBVtpdxjWH

Since relax-constraints started defaulting to True, every scheduling request
got default device breach prices, which turned a directional device capacity
into a soft constraint. That is wrong for the many cases where the capacity
states a physical impossibility rather than an economic preference: a heat pump
with production-capacity 0 was scheduled to produce, because breaching the
device was cheaper than breaching the site.

Restore the opt-in that held before the default flip: fill in default device
breach prices only when relax-capacity-constraints is set, or when
relax-constraints is passed explicitly rather than defaulted. The SoC and site
capacity constraints keep following the default, where softening was the point.

Also stop overwriting explicitly given device breach prices. The guard's own
comment says "unless already set explicitly", but operator precedence tied that
condition to one branch only, and set_default_breach_prices assigns
unconditionally.

This does not fix the case where relaxation was asked for and a capacity of
zero is still meant as a physical statement; see issue #2395.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014qPe5bv41RDTuBVtpdxjWH
Signed-off-by: F.N. Claessen <claessen@seita.nl>
@Flix6x Flix6x added this to the 1.0.0 milestone Aug 5, 2026
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: F.N. Claessen <claessen@seita.nl>
@read-the-docs-community

Copy link
Copy Markdown

Documentation build overview

📚 flexmeasures | 🛠️ Build #33925921 | 📁 Comparing 0cdd62f against latest (65d2a70)

  🔍 Preview build  

5 files changed · ± 5 modified

± Modified

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts flex-context price-defaulting so that device directional capacities (consumption-capacity / production-capacity) remain hard constraints unless the caller explicitly opts into relaxing them, preventing physically impossible schedules caused by default breach pricing.

Changes:

  • Update FlexContextSchema.check_prices to only auto-fill default device breach prices when relax-capacity-constraints is set, or when relax-constraints was explicitly provided (not merely defaulted).
  • Add regression tests covering opt-in device relaxation and ensuring explicitly provided device breach prices aren’t overwritten.
  • Update API/OpenAPI/docs/changelog text to document the revised relaxation semantics.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
flexmeasures/data/schemas/scheduling/init.py Changes defaulting logic so default relax-constraints no longer softens device directional capacities
flexmeasures/data/schemas/tests/test_scheduling.py Adds tests for opt-in device capacity relaxation and non-overwriting of explicit breach prices
flexmeasures/data/schemas/scheduling/metadata.py Updates relax-constraints field description to reflect new semantics
flexmeasures/ui/static/openapi-specs.json Updates OpenAPI description for relax-constraints accordingly
documentation/api/introduction.rst Notes that device directional capacities stay hard unless explicitly relaxed
documentation/changelog.rst Adds changelog entry describing the behavior change and explicit-price preservation
Suppressed comments (4)

flexmeasures/data/schemas/tests/test_scheduling.py:2007

  • This comment wraps mid-phrase (“as it did” → next line), which violates the repo’s rule that comments only break lines after punctuation. Please reflow the comment.
        # An explicitly passed relax-constraints still softens everything, as it did
        # before relax-constraints defaulted to True.

flexmeasures/data/schemas/tests/test_scheduling.py:2029

  • This docstring wraps mid-phrase (“…that cannot” → next line), which violates the repo’s rule that docstrings only break lines after punctuation. Please reflow without mid-phrase breaks.
    A directional capacity can state a physical impossibility (a heat pump that cannot
    produce), so making it breachable at a price has to be asked for, either through
    relax-capacity-constraints or through an explicitly passed relax-constraints.

flexmeasures/data/schemas/tests/test_scheduling.py:2045

  • This docstring wraps mid-phrase (“…keep it” → next line), which violates the repo’s rule that docstrings only break lines after punctuation. Please reflow without mid-phrase breaks.
    ``set_default_breach_prices`` assigns unconditionally, so the guard has to keep it
    from running at all when the caller already priced a breach themselves.

flexmeasures/data/schemas/tests/test_scheduling.py:2056

  • This comment wraps mid-phrase between lines (“…puts” → next line), which violates the repo’s rule that comments only break lines after punctuation. Please reflow it.
    # The opposite direction is left alone too: pricing one direction explicitly puts
    # the caller in charge of both, rather than mixing their price with our default.

Comment on lines +1058 to +1062
# Note that a *defaulted* 'relax-constraints' does not soften device capacities.
# A directional capacity can state a physical impossibility (a heat pump that
# cannot produce), and a default should not make that breachable at a price.
# Softening them therefore stays an opt-in, through 'relax-capacity-constraints'
# or through an explicitly passed 'relax-constraints'.
Comment on lines +2003 to +2004
# Nothing given: relax-constraints defaults to True, which softens the SoC and
# site capacity constraints, but no longer the device directional capacities.
The fallback schedule mechanism activates when the main scheduler encounters an infeasible problem (i.e. when constraints cannot be satisfied).
This is less likely to happen when ``"relax-constraints": true`` is set in the ``flex-context``, as constraint relaxation softens most infeasibility-causing constraints.
The hard constraints that remain even after constraint relaxation are ``soc-min``, ``soc-max``, ``soc-targets`` and ``power-capacity`` in the ``flex-model``, and ``site-power-capacity`` in the ``flex-context``.
The device ``consumption-capacity`` and ``production-capacity`` also stay hard unless you ask for them to be relaxed, by passing ``relax-constraints`` explicitly or by setting ``relax-capacity-constraints``.
* Floor off-clock API datetimes to a non-instantaneous sensor's resolution by default when ingesting sensor data, uploading sensor data, and handling scheduler flex-model timed events; configurable with the ``floor_datetimes_to_resolution`` sensor attribute [see `PR #2146 <https://www.github.com/FlexMeasures/flexmeasures/pull/2146>`_ and `PR #2194 <https://www.github.com/FlexMeasures/flexmeasures/pull/2194>`_]
* In the UI, asset and sensor charts now render with Apache ECharts (canvas) by default, for much faster drawing and interaction on dense time series, while staying visually and functionally equivalent to the previous Vega-Lite charts, which remain available as a fallback via a toggle [see `PR #2234 <https://www.github.com/FlexMeasures/flexmeasures/pull/2234>`_]
* Breaking behaviour change: the top-level flex-context's ``relax-constraints`` field now defaults to ``True`` (matching the default already used within each ``commodities`` entry), so constraint violations are softly penalized by default instead of being hard constraints, unless explicitly set to ``False`` [see `PR #2172 <https://www.github.com/FlexMeasures/flexmeasures/pull/2172>`_]
* That default does not extend to the device ``consumption-capacity`` and ``production-capacity``, which stay hard unless relaxation is asked for, either by passing ``relax-constraints`` explicitly or by setting ``relax-capacity-constraints``. A directional device capacity may state a physical impossibility (a heat pump that cannot produce) rather than an economic preference, and a default should not make that breachable at a price. Explicitly given device breach prices are now also respected instead of being overwritten by the defaults [see `PR #2398 <https://www.github.com/FlexMeasures/flexmeasures/pull/2398>`_]

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider merging with the previous changelog entry, referencing the two PRs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants