Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
58d8bb7
feat: relax storage SoC bounds by default
BelhsanHmida Jun 25, 2026
2944dac
feat: remove storage fallback scheduler
BelhsanHmida Jun 25, 2026
09e9998
feat: remove storage fallback policy helper
BelhsanHmida Jun 25, 2026
11eeb37
test: cover SoC relaxation schema defaults
BelhsanHmida Jun 25, 2026
0fd1487
test: use default SoC breach prices
BelhsanHmida Jun 25, 2026
18aa13b
test: expect storage infeasibility without fallback
BelhsanHmida Jun 25, 2026
783fd8b
test: assert storage schedules do not fall back
BelhsanHmida Jun 25, 2026
48114c1
test: update sequential scheduling fallback case
BelhsanHmida Jun 25, 2026
9d90ff9
docs: describe default SoC relaxation metadata
BelhsanHmida Jun 25, 2026
ed23342
docs: update storage scheduling infeasibility guide
BelhsanHmida Jun 25, 2026
6483a52
docs: clarify fallback redirects for custom schedulers
BelhsanHmida Jun 25, 2026
2191013
docs: scope fallback redirect configuration
BelhsanHmida Jun 25, 2026
c08ba18
docs: refresh SoC relaxation OpenAPI text
BelhsanHmida Jun 25, 2026
7135f33
docs: add fallback scheduler changelog entry
BelhsanHmida Jun 26, 2026
429d639
test: allow small unit conversion drift
BelhsanHmida Jun 26, 2026
aedfa12
test: avoid exact quantity string comparisons
BelhsanHmida Jun 26, 2026
3a68fa7
docs: clarify storage infeasibility behavior
BelhsanHmida Jun 28, 2026
79241ee
test: simplify storage fallback assertion comment
BelhsanHmida Jun 28, 2026
0f805b8
docs: regenerate openapi-specs.json
BelhsanHmida Jun 28, 2026
eaa4a7a
fix: preserve explicit zero breach prices
BelhsanHmida Jul 5, 2026
0468c9b
Merge branch 'main' into feat/retire-fallback-scheduler
BelhsanHmida Jul 5, 2026
73465ac
Merge remote-tracking branch 'origin/main' into feat/retire-fallback-…
BelhsanHmida Jul 9, 2026
b0aa05d
Merge branch 'main' into feat/retire-fallback-scheduler
BelhsanHmida Jul 13, 2026
468bdd9
chore: align agent instructions with main
BelhsanHmida Jul 13, 2026
919e28d
docs: fix jobs OpenAPI description indentation
BelhsanHmida Jul 13, 2026
5918dc2
Merge branch 'main' into feat/retire-fallback-scheduler
BelhsanHmida Jul 17, 2026
f354561
fix(schema): let relax-constraints=false keep SoC constraints hard
BelhsanHmida Jul 17, 2026
1bafc44
docs: restore hard-constraints list and describe the SoC relaxation o…
BelhsanHmida Jul 17, 2026
bf2faf3
test: restore config via monkeypatch and fix truncated docstring
BelhsanHmida Jul 17, 2026
b932527
docs+test: fix review follow-ups for relax-soc-constraints default
BelhsanHmida Jul 22, 2026
0aefa2b
Merge branch 'main' into feat/retire-fallback-scheduler
BelhsanHmida Jul 22, 2026
d1e3f27
Merge branch 'main' into feat/retire-fallback-scheduler
BelhsanHmida Aug 5, 2026
42406c9
feat: support a `default` fallback on sensor references
BelhsanHmida Aug 6, 2026
d452706
Merge branch 'main' into feat/retire-fallback-scheduler
BelhsanHmida Aug 6, 2026
ae77cba
Merge branch 'feat/retire-fallback-scheduler' into feat/sensor-refere…
BelhsanHmida Aug 6, 2026
bbf299b
docs: scope the sensor-reference `default` to variable-quantity fields
BelhsanHmida Aug 6, 2026
95d2fac
revert the fallback-scheduler content from this branch
BelhsanHmida Aug 7, 2026
d9bc5ae
Merge branch 'main' into feat/sensor-reference-defaults
BelhsanHmida Aug 7, 2026
ec5fe72
Warn about a zero fallback on a directional capacity
Flix6x Aug 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions documentation/api/change_log.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ v3.0-32 | July XX, 2026
- API endpoints are now rate-limited. A request which exceeds a limit is answered with a ``429 (Too Many Requests)`` status code and a ``Retry-After`` header stating how many seconds to wait. Responses also carry ``X-RateLimit-*`` headers, describing the limit that applied, how much of it is left, and when it resets. A stricter limit applies to ``POST /assets/<id>/schedules/trigger``, ``POST /sensors/<id>/schedules/trigger`` and ``POST /sensors/<id>/forecasts/trigger`` than to other endpoints; the health endpoints are exempt. Per-account overrides are set by assigning the account a plan (a ``Plan`` database row), rather than through an account attribute.
- Introduced the ``inflexible-consumption`` and ``inflexible-production`` flex-context fields, which make explicit how the sign of each inflexible device's power data should be read: positive values denote consumption resp. production. Each entry is a sensor reference (``{"sensor": <id>}``), optionally with source filters (``source-types``, ``exclude-source-types``, ``sources``, ``source-account``). Deprecated the ``inflexible-device-sensors`` field (a list of bare sensor IDs, whose sign convention is read from each sensor's ``consumption_is_positive`` attribute); it remains supported, but cannot be combined with the new fields in one flex-context.
- Added a ``role`` query parameter to ``GET /api/v3_0/accounts`` for filtering accessible organisations by account role.
- Sensor references on variable-quantity flex-model and flex-context fields (such as ``soc-minima``, ``soc-maxima``, the capacity fields and the price fields) may now include a ``default`` fallback quantity, e.g. ``{"sensor": 50, "default": "0 kWh"}``. It fills the time slots for which the referenced sensor holds no value. Note that this fills *every* such slot, so a sensor recording only occasional setpoints becomes densely constrained. The field is not (yet) applied to sensor references on ``inflexible-consumption``/``inflexible-production`` or to forecaster regressors. Take particular care with a fallback of ``0`` on a ``consumption-capacity`` or ``production-capacity``: if the sensor holds no value for the whole scheduling window, the resulting all-zero capacity is read as a physical statement about the device and enforced strictly (see :ref:`the flex-model capacity fields <flex_models_and_schedulers>`), rather than as a limit that may be breached at a price.
- Extended ``GET /api/v3_0/jobs/<uuid>`` with a ``result`` field containing ``unresolved`` and ``resolved`` arrays, each keyed by asset ID. For scheduling jobs, this surfaces soft state-of-charge constraint analysis: ``soc-minima`` and ``soc-maxima`` violations (with a ``violation`` magnitude) or satisfied constraints (with a ``margin`` headroom). Both arrays are empty when no SoC constraints were defined.
- **Field canonicalization** for background job tracking:
* The ``job`` field is now the canonical way to identify background jobs returned by `/sensors/<id>/schedules/trigger`, `/assets/<id>/schedules/trigger`, and `/sensors/<id>/forecasts/trigger` endpoints. If applicable, the triggered response now also returns a ``results-url`` pointing to the sensor-specific results endpoint, alongside the generic ``job-url``.
Expand Down
1 change: 1 addition & 0 deletions documentation/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ New features
* Commodity contexts that omit grid-connection fields (prices and site capacities) now get smart defaults instead of failing or silently leaving the grid unconstrained — for instance, a bare ``{"commodity": "gas"}`` is treated as having no grid connection; see :ref:`commodity_context_defaults` for the full rules [see `PR #2272 <https://www.github.com/FlexMeasures/flexmeasures/pull/2272>`_]
* CLI support for adding/editing account attributes [see `PR #2242 <https://www.github.com/FlexMeasures/flexmeasures/pull/2242>`_]
* Improve chart axis domain for event values not around zero, with a per-sub-chart ``y-axis`` option in ``sensors_to_show`` (default ``zero``, which pads the axis out to include zero) that can be set to ``data`` to fit a sub-chart's y-axis to the values shown, to an explicit ``[min, max]`` domain that the axis will cover at least (expanding to fit data beyond it), or to a strict ``{"min": min, "max": max}`` domain that the axis will never exceed (clamping data beyond it, with a warning when that happens), editable from the graph editor [see `PR #2244 <https://www.github.com/FlexMeasures/flexmeasures/pull/2244>`_]
* Sensor references on variable-quantity flex-model and flex-context fields now accept a ``default`` fallback quantity, e.g. ``{"sensor": 50, "default": "0 kWh"}``, filling the time slots for which the referenced sensor holds no value; also settable from the flex-model UI. Note that a fallback of ``0`` on a directional capacity makes that capacity a hard bound wherever the sensor is silent for the whole scheduling window [see `PR #2405 <https://www.github.com/FlexMeasures/flexmeasures/pull/2405>`_]
* Breaking behaviour change: the built-in storage fallback scheduler has been retired, so an infeasible storage problem now fails with its failure reason instead of silently saving a fallback schedule; ``FLEXMEASURES_FALLBACK_REDIRECT`` is now only relevant for custom schedulers that define a fallback scheduler [see `PR #2252 <https://www.github.com/FlexMeasures/flexmeasures/pull/2252>`_]
* Breaking behaviour change: the specific ``relax-soc-constraints`` and ``relax-site-capacity-constraints`` flags now follow the umbrella ``relax-constraints`` flag (which now defaults to true, see above) unless they are set explicitly, in which case they take precedence for their respective constraints, in either direction (previously, these flags were independent opt-ins defaulting to false); also fixed: explicitly set breach prices (including zero prices) are no longer overwritten with default breach prices when relaxation is enabled, and when only one breach price of the ``soc-minima``/``soc-maxima`` (or site capacity) pair is set explicitly, the other one still gets its default [see `PR #2252 <https://www.github.com/FlexMeasures/flexmeasures/pull/2252>`_]
* Extended ``GET /api/v3_0/jobs/<uuid>`` with a ``result`` field containing ``unresolved`` and ``resolved`` soft state-of-charge constraint analysis (``soc-minima``/``soc-maxima`` violations or satisfied constraints, keyed by asset ID) for scheduling jobs; both arrays are empty when no SoC constraints were defined [see `PR #2072 <https://www.github.com/FlexMeasures/flexmeasures/pull/2072>`_]
Expand Down
43 changes: 43 additions & 0 deletions flexmeasures/data/models/planning/tests/test_utils_fresh_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from flexmeasures.data.schemas.sensors import SensorReference
from flexmeasures.data.models.planning.storage import StorageScheduler
from flexmeasures.data.models.planning.utils import get_series_from_quantity_or_sensor
from flexmeasures.utils.unit_utils import ur


def test_get_series_from_sensor_reference_source_filter_integration(fresh_db):
Expand Down Expand Up @@ -88,6 +89,48 @@ def test_get_series_from_sensor_reference_source_filter_integration(fresh_db):
assert result_forecaster.iloc[0] == pytest.approx(200.0)


def test_get_series_from_sensor_reference_default_fills_missing_values(fresh_db):
"""A SensorReference default fills query slots with no matching sensor belief."""
query_window = (
pd.Timestamp("2025-06-01 08:00:00+02:00"),
pd.Timestamp("2025-06-01 08:30:00+02:00"),
)
source = DataSource(name="test-default-source", type="scheduler")
fresh_db.session.add(source)
asset_type = GenericAssetType(name="test-asset-type-default")
fresh_db.session.add(asset_type)
asset = GenericAsset(name="test-asset-default", generic_asset_type=asset_type)
fresh_db.session.add(asset)
sensor = Sensor(
name="test-sensor-default",
generic_asset=asset,
event_resolution=timedelta(minutes=15),
unit="MW",
)
fresh_db.session.add(sensor)
fresh_db.session.flush()
fresh_db.session.add(
TimedBelief(
event_start=query_window[0],
belief_horizon=timedelta(0),
event_value=0.1,
source=source,
sensor=sensor,
)
)
fresh_db.session.commit()

result = get_series_from_quantity_or_sensor(
variable_quantity=SensorReference(sensor=sensor, default=ur.Quantity("1 MW")),
query_window=query_window,
resolution=sensor.event_resolution,
unit="kW",
as_instantaneous_events=False,
)

assert list(result) == pytest.approx([100.0, 1000.0])


def test_get_series_from_sensor_reference_sources_filter_integration(fresh_db):
"""A :class:`SensorReference` with ``sources`` returns only beliefs from the specified source.

Expand Down
8 changes: 8 additions & 0 deletions flexmeasures/data/models/planning/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,14 @@ def get_series_from_quantity_or_sensor(
time_series = convert_units(
time_series, variable_quantity.unit, unit, resolution
)
if variable_quantity.default is not None:
default_value = convert_units(
variable_quantity.default.magnitude,
str(variable_quantity.default.units),
unit,
resolution,
)
time_series = time_series.fillna(default_value)
elif isinstance(variable_quantity, Sensor):
bdf: tb.BeliefsDataFrame = TimedBelief.search(
variable_quantity,
Expand Down
Loading
Loading