Skip to content

Define intermediate power constraints via DB flex-models on the asset tree - #2277

Merged
Flix6x merged 1 commit into
fix/intermediate-power-constraintsfrom
feat/db-tree-group-constraints
Jul 10, 2026
Merged

Define intermediate power constraints via DB flex-models on the asset tree#2277
Flix6x merged 1 commit into
fix/intermediate-power-constraintsfrom
feat/db-tree-group-constraints

Conversation

@Flix6x

@Flix6x Flix6x commented Jul 10, 2026

Copy link
Copy Markdown
Member

Description

Follow-up to #2276, making intermediate power constraints fully definable via DB-stored flex-models on an asset tree — trigger the parent asset with an empty flex-model and everything comes from the tree.

Note: based on fix/intermediate-power-constraints; the diff will settle once that branch's signed-off rebase is pushed.

What's new

  • group: {"asset": <id>}: the group field now also accepts an asset reference. DB flex-models are asset-keyed (get_flex_model() never emits sensor), so this is the form an asset tree naturally produces. Sensor references keep working.
  • Group output via output sensors: asset-referenced group entries have no power sensor; instead they may define consumption and/or production output sensors and the group's aggregate power is saved there, following the standard conventions (consumption only → full profile consumption-positive; production only → full profile production-positive on save; both → clip-split into positive parts on each). Sensor-referenced groups still save to the group power sensor and may also define output sensors.
  • UI: the asset flex-model editor recommends the parent asset for a child's group field (one-click "Use parent asset as group"), and shows hints in both the parent's and children's editors when the parent's flex-model defines a power-capacity.
  • Tutorial: new toy example (site → hybrid inverter 2.5 kW → battery + PV) driven entirely by DB-stored flex-models, cross-linked from the scheduling feature docs.

Bug fixes uncovered

  • AssetTriggerSchema.check_flex_model_sensors raised KeyError on any asset-only flex-model entry.
  • The freeze_server_now test fixture leaked its server_now monkeypatch into all subsequently run tests in the same process (its teardown restored nothing), causing order-dependent duplicate-key failures whenever scheduling jobs later saved beliefs with identical frozen belief times. Fixed at the source in conftest.py.

Tests

  • 8 new solver tests (asset-ref groups, output-sensor saving in all three modes, nested mixed-kind groups, validation) plus a pure-DB-tree end-to-end test (test_pure_db_tree_group_constraint) that stores all flex-models as asset attributes and schedules with flex_model=[] through the real collect_flex_config path.
  • UI tests for the new hints; schema tests for the asset variant.
  • Full serial regression (planning + schemas + UI + API schedule suites): 537 passed, 3 xfailed. Pre-commit clean; docs build clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01TAad46Ayg86DHpL6nY54sX

… asset tree

Extend the storage flex-model's group field to also accept {"asset": <id>},
so a group can be identified by an asset-keyed flex-model entry - the form
that DB-stored flex-models naturally produce. This makes intermediate power
constraints fully definable via an asset tree with stored flex-models, with
an empty flex-model in the scheduling trigger.

- Asset-referenced group entries define no power sensor of their own; the
  group's scheduled aggregate power is saved via the entry's consumption
  and/or production output sensors, following the usual output-sensor
  conventions (full profile on a single sensor; clip-split when both given).
- Sensor-referenced groups keep saving the aggregate to the group power
  sensor, and may now also define output sensors.
- The UI asset flex-model editor recommends the parent asset when editing a
  child's group field (one-click suggestion), and hints in both the parent's
  and children's editors when the parent's flex-model defines power-capacity.
- New tutorial: toy example for intermediate power constraints, driven
  entirely by DB-stored flex-models.

Bug fixes uncovered along the way:
- AssetTriggerSchema.check_flex_model_sensors raised KeyError on asset-only
  flex-model entries.
- The freeze_server_now test fixture leaked its server_now monkeypatch into
  all subsequently run tests in the same process, causing order-dependent
  duplicate-key failures when scheduling jobs saved beliefs with identical
  (frozen) belief times.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TAad46Ayg86DHpL6nY54sX
Signed-off-by: F.N. Claessen <claessen@seita.nl>
@read-the-docs-community

Copy link
Copy Markdown

@Flix6x Flix6x self-assigned this Jul 10, 2026

@Flix6x Flix6x left a comment

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.

Merging this so we can review the whole feature rather than in parts, on #2276.

@Flix6x
Flix6x merged commit 82a6cb7 into fix/intermediate-power-constraints Jul 10, 2026
13 checks passed
@Flix6x
Flix6x deleted the feat/db-tree-group-constraints branch July 10, 2026 10:22
Flix6x added a commit that referenced this pull request Jul 18, 2026
…el group field) (#2276)

* feat: support intermediate power constraints on groups of devices

Add a new `group` field to the storage flex-model, letting device entries
reference a power sensor that represents a group of devices (e.g. a hybrid
inverter shared by a battery and PV). The group sensor's own flex-model entry
constrains the group's aggregate power:

- power-capacity: hard constraint (both directions)
- consumption-capacity / production-capacity: soft constraints, enforced via
  breach commitments with default breach prices (10000 <currency>/kW)

The group's scheduled aggregate power is saved to the group sensor. Nested
groups are supported (cycles rejected); groups require multi-device
flex-models and members must share a commodity.

Implemented on top of the existing device-group solver machinery
(ems_constraint_groups from the multi-commodity work, and grouped
FlowCommitments from PR #1934); no changes to the optimizer itself.

Also fixes a latent device-index misalignment in MetaStorageScheduler._prepare,
where per-device lists were built from the full flex-model list (including
stock-only entries) instead of the filtered device models.

Closes #2092

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TAad46Ayg86DHpL6nY54sX

* docs: cite PR #2276 in changelog entry

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TAad46Ayg86DHpL6nY54sX

* feat: support intermediate power constraints on groups of devices

Add a new `group` field to the storage flex-model, letting device entries
reference a power sensor that represents a group of devices (e.g. a hybrid
inverter shared by a battery and PV). The group sensor's own flex-model entry
constrains the group's aggregate power:

- power-capacity: hard constraint (both directions)
- consumption-capacity / production-capacity: soft constraints, enforced via
  breach commitments with default breach prices (10000 <currency>/kW)

The group's scheduled aggregate power is saved to the group sensor. Nested
groups are supported (cycles rejected); groups require multi-device
flex-models and members must share a commodity.

Implemented on top of the existing device-group solver machinery
(ems_constraint_groups from the multi-commodity work, and grouped
FlowCommitments from PR #1934); no changes to the optimizer itself.

Also fixes a latent device-index misalignment in MetaStorageScheduler._prepare,
where per-device lists were built from the full flex-model list (including
stock-only entries) instead of the filtered device models.

Closes #2092

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TAad46Ayg86DHpL6nY54sX
Signed-off-by: F.N. Claessen <claessen@seita.nl>

* docs: cite PR #2276 in changelog entry

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TAad46Ayg86DHpL6nY54sX
Signed-off-by: F.N. Claessen <claessen@seita.nl>

* fix: add group field to UI flex-model schema

The UI-DB flex-model schema parity test requires every DBStorageFlexModelSchema
field to have UI support.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TAad46Ayg86DHpL6nY54sX
Signed-off-by: F.N. Claessen <claessen@seita.nl>

* feat: define intermediate power constraints via DB flex-models on the asset tree (#2277)

Extend the storage flex-model's group field to also accept {"asset": <id>},
so a group can be identified by an asset-keyed flex-model entry - the form
that DB-stored flex-models naturally produce. This makes intermediate power
constraints fully definable via an asset tree with stored flex-models, with
an empty flex-model in the scheduling trigger.

- Asset-referenced group entries define no power sensor of their own; the
  group's scheduled aggregate power is saved via the entry's consumption
  and/or production output sensors, following the usual output-sensor
  conventions (full profile on a single sensor; clip-split when both given).
- Sensor-referenced groups keep saving the aggregate to the group power
  sensor, and may now also define output sensors.
- The UI asset flex-model editor recommends the parent asset when editing a
  child's group field (one-click suggestion), and hints in both the parent's
  and children's editors when the parent's flex-model defines power-capacity.
- New tutorial: toy example for intermediate power constraints, driven
  entirely by DB-stored flex-models.

Bug fixes uncovered along the way:
- AssetTriggerSchema.check_flex_model_sensors raised KeyError on asset-only
  flex-model entries.
- The freeze_server_now test fixture leaked its server_now monkeypatch into
  all subsequently run tests in the same process, causing order-dependent
  duplicate-key failures when scheduling jobs saved beliefs with identical
  (frozen) belief times.


Claude-Session: https://claude.ai/code/session_01TAad46Ayg86DHpL6nY54sX

Signed-off-by: F.N. Claessen <claessen@seita.nl>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

* docs: keep the group-constraints tutorial audience-appropriate

Remove references to code modules, tests and GitHub issues from the tutorial;
tutorials address FlexMeasures users, not developers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TAad46Ayg86DHpL6nY54sX
Signed-off-by: F.N. Claessen <claessen@seita.nl>

* fix: address review on the group field (XSS, error path, schema base)

- asset_properties.html: HTML-escape the parent asset name before it is
  inserted into innerHTML in the group hint (it is user-controlled -> XSS).
- storage.py: attach the "group must reference a power sensor" ValidationError
  to the `group` field, not `sensor`.
- GroupReferenceSchema now inherits from SharedSensorReferenceSchema instead of
  SensorReferenceSchema, so it accepts only `sensor`/`asset` (a group is a
  device-group identifier, not a belief-query reference) and no longer exposes
  the source-* filter fields; regenerate openapi-specs.json accordingly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016MLCUiSdXDqDBmg8GbYp1B
Signed-off-by: F.N. Claessen <claessen@seita.nl>

* docs: the model computes group schedules, it does not save them

Reword _add_group_schedules' docstring: the scheduling model assembles and
returns each group's aggregate power schedule; persistence is the data
service's responsibility.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016MLCUiSdXDqDBmg8GbYp1B
Signed-off-by: F.N. Claessen <claessen@seita.nl>

---------

Signed-off-by: F.N. Claessen <claessen@seita.nl>
Signed-off-by: F.N. Claessen <felix@seita.nl>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@nhoening nhoening added this to the 1.0.0 milestone Jul 27, 2026
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.

2 participants