Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
166 commits
Select commit Hold shift + click to select a range
9dd779a
docs: new section describing commitments
Flix6x Dec 4, 2025
13e9308
docs: rewrite the overview in commitments section
Flix6x Dec 4, 2025
1179198
docs: keep ems variables, but explain them as representing the site c…
Flix6x Dec 4, 2025
1b10360
docs: cross-reference the commitments section and the linear problem …
Flix6x Dec 4, 2025
0d25b25
Merge remote-tracking branch 'refs/remotes/origin/main' into docs/com…
Flix6x Dec 8, 2025
6f6e52b
fix: cross-references
Flix6x Dec 8, 2025
3599379
docs: add commitments section to index
Flix6x Dec 8, 2025
ce1533d
Merge remote-tracking branch 'refs/remotes/origin/main' into docs/com…
Flix6x Jan 23, 2026
84fbd5a
feat: add a util function for printing out commitments in a tabulated…
Ahmad-Wahid Jan 23, 2026
90177bf
refactor: move pretty printing method to class
Flix6x Jan 23, 2026
9f34676
feat: Commitment supports device groups
Flix6x Jan 23, 2026
43107c8
feat: start testing device grouping
Flix6x Jan 23, 2026
b90d7f0
dev: test multi-feed
Flix6x Jan 23, 2026
4179981
update the ids of devices to be integers
Ahmad-Wahid Jan 26, 2026
8b108ed
feat: function that group commitment quantities
Ahmad-Wahid Jan 26, 2026
485349e
add commitments for multi group
Ahmad-Wahid Jan 26, 2026
81bb9ec
fix: get unique list of devices for a frame column
Ahmad-Wahid Jan 27, 2026
334e4d3
fix: create util functions that extract devices for a list of values …
Ahmad-Wahid Jan 27, 2026
f738d9f
fix: create a series for a list of grouped devices
Ahmad-Wahid Jan 27, 2026
620c6dc
drop outdated comments
Ahmad-Wahid Jan 31, 2026
40eb747
use commitment costs and add asserts for electricity and gas
Ahmad-Wahid Jan 31, 2026
708d869
and an assert for commodity costs
Ahmad-Wahid Jan 31, 2026
ce307f8
add an extra assert on costs
Ahmad-Wahid Jan 31, 2026
5be555b
support commodity based EMS flow commitments and grouped devices
Ahmad-Wahid Feb 3, 2026
6f2d745
Add commodity field and support multi-device commitments
Ahmad-Wahid Feb 3, 2026
1d63893
test shared buffer and multi-comodity commitments
Ahmad-Wahid Feb 3, 2026
8c9b1b5
remove hard check for commodity to make backward compatible
Ahmad-Wahid Feb 3, 2026
a8f3b89
update the function to support backward compatibility
Ahmad-Wahid Feb 3, 2026
be09c4d
feat: add commodity field to the flexmodel and DBstorage-flex-model s…
Ahmad-Wahid Feb 10, 2026
07822eb
fix: use devices as index rather than time series
Flix6x Feb 10, 2026
92eabc7
fix: exclude gas-power devices from electricity commitments
Flix6x Feb 10, 2026
ee33fa2
Merge remote-tracking branch 'origin/main' into feat/switching-betwee…
Flix6x Feb 10, 2026
f9df705
Merge branch 'feat/switching-between-gas-and-electricity' into feat/m…
Flix6x Feb 10, 2026
325bfe8
feat: add gas-price field to the Flex-context schema
Ahmad-Wahid Feb 10, 2026
9261629
apply black
Ahmad-Wahid Feb 10, 2026
9dd5802
feat: add a test case for two flexible devices with commodity
Ahmad-Wahid Feb 10, 2026
b22c6d7
use expected datatypes
Ahmad-Wahid Feb 16, 2026
c88af5c
feat: split commitments per commodity
Ahmad-Wahid Feb 16, 2026
c2341f1
feat: split commitments per commodity
Ahmad-Wahid Feb 16, 2026
88d4be0
Merge remote-tracking branch 'origin/feat/multi-commodity' into feat/…
Ahmad-Wahid Feb 16, 2026
be05a19
Revert "use expected datatypes"
Ahmad-Wahid Feb 16, 2026
f4ffd8a
feat: add a test case for different commodities
Ahmad-Wahid Feb 16, 2026
c43d2ad
fix: do not produce gas
Flix6x Feb 20, 2026
0aa5b2b
feat: create a flow commitment for prefering to charge sooner devices
Ahmad-Wahid Feb 26, 2026
a48c6ba
add soc constraints for boiler
Ahmad-Wahid Feb 26, 2026
c58965f
add some assert statments
Ahmad-Wahid Feb 26, 2026
515f34a
update and add new assertions with clear explanation
Ahmad-Wahid Mar 4, 2026
4d77344
update the docstring
Ahmad-Wahid Mar 4, 2026
2becd02
refactor: move tiny-price-slope decleration out of the for loop
Ahmad-Wahid Mar 6, 2026
d9d4f94
Revert "refactor: move tiny-price-slope decleration out of the for loop"
Ahmad-Wahid Mar 6, 2026
2a22fae
refactor: move tiny-price-slope decleration out of the for loop
Ahmad-Wahid Mar 6, 2026
f38d6d8
fix: add data_key attr
Ahmad-Wahid Mar 6, 2026
007e514
add missing commodity description and it's field in ui flexmodel schema
Ahmad-Wahid Mar 6, 2026
0bff8bc
fix: add missing gas-price field in UI Flexcontext schema
Ahmad-Wahid Mar 6, 2026
82f807e
fix: wrong timezone; the test relied on the preference to charge soon…
Flix6x Mar 13, 2026
128550f
feat: move preference to charge sooner and discharge later into a Sto…
Flix6x Mar 13, 2026
130b9dd
fix: test case no longer relies on arbitrage opportunity coming from …
Flix6x Mar 13, 2026
1eab828
feat: check for optimal schedule
Flix6x Mar 13, 2026
b9bc4a9
feat: prefer a full storage earlier over later
Flix6x Mar 13, 2026
57df5c3
docs: update commitment name and inline comments
Flix6x Mar 13, 2026
ce71637
docs: touch up test explanation
Flix6x Mar 14, 2026
f6183df
fix: update test case given preference for a full battery
Flix6x Mar 14, 2026
ed471a8
delete: clean up comment
Flix6x Mar 14, 2026
7611fb9
feat: model the preference to curtail later within the same StockComm…
Flix6x Mar 14, 2026
bf16e63
fix: reduce tiny price slope
Flix6x Mar 14, 2026
06c30dc
docs: delete duplicate changelog entry
Flix6x Mar 16, 2026
0125e28
Merge remote-tracking branch 'origin/main' into feat/full-soc-preference
Flix6x Mar 18, 2026
d99089b
docs: fix broken link
Flix6x Mar 18, 2026
cf01f1d
Revert "fix: reduce tiny price slope"
Flix6x Mar 18, 2026
bdbdead
fix: soc unit conversion
Flix6x Mar 18, 2026
f987706
fix: adapt test to check for 1 hour of free energy at 15-min scheduli…
Flix6x Mar 18, 2026
7acbc99
Merge remote-tracking branch 'origin/feat/full-soc-preference' into f…
Flix6x Mar 19, 2026
12cf595
Merge remote-tracking branch 'origin/main' into feat/switching-betwee…
Flix6x Mar 19, 2026
c09371a
Merge branch 'feat/switching-between-gas-and-electricity' into feat/m…
Flix6x Mar 19, 2026
534179a
style: black
Flix6x Mar 19, 2026
19dc4dc
Merge remote-tracking branch 'origin/feat/switching-between-gas-and-e…
Ahmad-Wahid Mar 19, 2026
e4c43ea
fix: check curtailment preference per distinct device
Flix6x Mar 20, 2026
b3138c3
fix: set tight tolerance for HiGHS solver
Flix6x Mar 20, 2026
8eff839
refactor: merge if-blocks
Flix6x Mar 20, 2026
b0074d4
fix: update test_two_flexible_assets_with_commodity
Flix6x Mar 20, 2026
ae23ef6
Merge remote-tracking branch 'origin/feat/multi-commodity' into feat/…
Ahmad-Wahid Mar 20, 2026
05aed7e
fix: check curtailment preference per distinct device
Flix6x Mar 20, 2026
e55f638
fix: set tight tolerance for HiGHS solver
Flix6x Mar 20, 2026
764712f
refactor: merge if-blocks
Flix6x Mar 20, 2026
9070eae
fix: use iloc
Flix6x Mar 23, 2026
857e9c1
fix: diminish tiny price slope by number of planning steps
Flix6x Mar 23, 2026
54c9c36
refactor: always diminish tiny price slope by number of planning step…
Flix6x Mar 23, 2026
158c7a0
chore: increment StorageScheduler version
Flix6x Mar 23, 2026
3df2142
Merge remote-tracking branch 'origin/feat/full-soc-preference' into f…
Flix6x Mar 23, 2026
decf794
Merge remote-tracking branch 'origin/main' into feat/multi-commodity
Flix6x Mar 23, 2026
261ed3a
Merge branch 'feat/full-soc-preference' into feat/switching-between-g…
Flix6x Mar 23, 2026
a16ec74
Merge branch 'feat/switching-between-gas-and-electricity' into feat/m…
Flix6x Mar 23, 2026
021543c
Merge remote-tracking branch 'origin/feat/multi-commodity' into feat/…
Ahmad-Wahid Mar 23, 2026
66e9a8b
Merge remote-tracking branch 'origin/main' into feat/switching-betwee…
Flix6x Mar 23, 2026
eda0ec7
Merge branch 'feat/switching-between-gas-and-electricity' into feat/m…
Flix6x Mar 23, 2026
b092aa1
fix: use approximation to compare battery and heat pump costs
Ahmad-Wahid Mar 23, 2026
b20465e
update the assert statements with prefered to charge battery sooner t…
Ahmad-Wahid Mar 23, 2026
031a6e8
fix: update the expected ev and battery costs
Ahmad-Wahid Mar 24, 2026
de4981e
fix: add device id to get costs for the given device
Ahmad-Wahid Mar 24, 2026
1ec0b2c
Merge remote-tracking branch 'origin/main' into feat/switching-betwee…
Flix6x Apr 24, 2026
49df3a6
feat: rewrite test to not rely on multi-commodity
Flix6x Apr 24, 2026
a518437
feat: rewrite test to prove that only when both HPs share a single co…
Flix6x Apr 24, 2026
4258636
fix: do not coerce device_group into a time series
Flix6x Apr 24, 2026
b4f8b2b
docs: changelog entry
Flix6x Apr 24, 2026
6e3a5a4
docs: pretty_print is not specifically for FlowCommitments
Flix6x Apr 24, 2026
7eb1319
docs: add (back) inline dev notes
Flix6x Apr 24, 2026
a298dc7
feat: strengthen asserts
Flix6x Apr 24, 2026
7f15c13
feat: add check for exact electricity costs expected
Flix6x Apr 24, 2026
ffd0d86
fix: sum over electricity costs; and move to StockCommitment to model…
Flix6x Apr 24, 2026
a33790a
fix: replace vague assert with explicit asserts
Flix6x Apr 24, 2026
6b6a474
docs: lose confusing comment
Flix6x Apr 24, 2026
70e1af6
fix: backwards compatibility in case no device is specified;
Flix6x Apr 24, 2026
792d2df
fix: when device is present but device_group is absent, fall back to …
Flix6x Apr 25, 2026
a9174a5
dev: update expectations of how costs are shared between EV and battery
Flix6x Apr 26, 2026
864a98f
fix commodity-level commitments by grouping devices and aligning devi…
Ahmad-Wahid Apr 30, 2026
c885b04
update the test cases for net commodity consumption and production
Ahmad-Wahid Apr 30, 2026
55075d5
fix: merge conflicts
Ahmad-Wahid Apr 30, 2026
7deff15
Merge remote-tracking branch 'origin/main' into feat/switching-betwee…
Flix6x Jun 3, 2026
3e20ce0
Merge remote-tracking branch 'origin/feat/switching-between-gas-and-e…
Flix6x Jun 3, 2026
afba390
Merge remote-tracking branch 'origin/main' into feat/multi-commodity
Flix6x Jun 3, 2026
7e60e2d
fix: update the test cases according device level costs
Ahmad-Wahid Jun 4, 2026
6c82e1c
docs/scheduling: add COMMODITY and GAS_PRICE metadata field documenta…
Ahmad-Wahid Jun 4, 2026
581ff05
Feat: Support shared storage (multi‑feed stock) (#2001)
Ahmad-Wahid Jul 7, 2026
0c84fb9
Merge remote-tracking branch 'origin/main' into feat/multi-commodity
Flix6x Jul 7, 2026
75392b9
docs: changelog entry
Flix6x Jul 7, 2026
b3e12bd
fix(tests): unique public asset names in commitment tests to satisfy …
Flix6x Jul 7, 2026
107ab37
fix: consolidate currency validation across commodity flex-contexts
Flix6x Jul 7, 2026
4d7eef5
feat!: default top-level relax-constraints to True
Flix6x Jul 7, 2026
cec885b
fix: support per-commodity inflexible-device-sensors and all-gas port…
Flix6x Jul 7, 2026
95965a9
fix: default device commodity to electricity and match commitment com…
Flix6x Jul 7, 2026
c2e4f4e
fix: fix dead cross-commodity currency check in list-form flex-context
Flix6x Jul 7, 2026
6e890a6
fix: restrict aggregate-power to electricity devices
Flix6x Jul 7, 2026
b976b53
fix: reject duplicate commodities in flex-context commodities list
Flix6x Jul 7, 2026
1381b14
feat: reject non-electricity semantics in the single-dict flex-context
Flix6x Jul 7, 2026
777bc08
fix: raise ValidationError for malformed flex-context input
Flix6x Jul 7, 2026
8210c68
refactor: drop dead 'types' comment blocks in UI_FLEX_CONTEXT_SCHEMA
Flix6x Jul 7, 2026
04dfdd0
test: add schema-level regression tests for hardening fixes
Flix6x Jul 7, 2026
5ec17ae
test: add scheduler-level regression tests for gas-only and per-commo…
Flix6x Jul 7, 2026
65f0fe7
docs: changelog entry for multi-commodity hardening fixes
Flix6x Jul 7, 2026
d39a6a2
fix: count only device models, not stock entries, when enumerating de…
Flix6x Jul 7, 2026
39e860c
Merge branch 'feat/multi-commodity' into fix/multi-commodity-hardening
Flix6x Jul 7, 2026
a00ab9a
fix: declare commodity field on FlexContextSchema
Flix6x Jul 7, 2026
5d19054
fix: tolerate commodities list combined with top-level flex-context f…
Flix6x Jul 7, 2026
f4a8262
refactor: use units_are_convertible for scheduler-side currency check…
Flix6x Jul 7, 2026
3c544f2
test: strengthen per-commodity inflexible sensor test; cover electric…
Flix6x Jul 7, 2026
2724920
fix: keep deprecated price sensors working and fix breach-price error…
Flix6x Jul 8, 2026
3c7bf96
fix: skip commodity device groups without devices in _prepare
Flix6x Jul 8, 2026
97b8b5f
test: pin relax-constraints False in tests asserting hard-capacity se…
Flix6x Jul 8, 2026
7e22cff
test: exclude the commodity field from the UI flex-context schema guard
Flix6x Jul 8, 2026
9592a8e
test: fix sign convention of inflexible gas load beliefs
Flix6x Jul 8, 2026
e78a0d3
Add commodity field to CommitmentSchema
Flix6x Jul 8, 2026
dbe922f
Add smarter defaults for commodity contexts
Flix6x Jul 8, 2026
5fa8b90
Fix spurious cross-currency error for price-free commodity contexts
Flix6x Jul 8, 2026
d074831
Warn when a commitment's commodity matches no devices
Flix6x Jul 8, 2026
02713b2
Note commodity-context defaults behaviour change in changelog
Flix6x Jul 8, 2026
6937bee
docs: reorder and consolidate changelog entries per self-review
Flix6x Jul 8, 2026
50b1b1d
docs: restore intentional 'types' notes on UI_FLEX_CONTEXT_SCHEMA agg…
Flix6x Jul 8, 2026
1c5809e
Point CommitmentSchema.commodity docs to the commodities list
Flix6x Jul 8, 2026
ff29d8f
Merge branch 'fix/multi-commodity-hardening' into feat/commodity-cont…
Flix6x Jul 8, 2026
eddefbd
Merge remote-tracking branch 'origin/main' into feat/multi-commodity
Flix6x Jul 8, 2026
022e5c0
fix: resolve CI failures from merge conflict between #1946 and #2072 …
Copilot Jul 8, 2026
ab2f01a
Merge remote-tracking branch 'origin/feat/multi-commodity' into fix/m…
Flix6x Jul 8, 2026
121ce32
Merge remote-tracking branch 'origin/fix/multi-commodity-hardening' i…
Flix6x Jul 8, 2026
5e891e2
Merge remote-tracking branch 'origin/main' into feat/commodity-contex…
Flix6x Jul 8, 2026
49abd52
feat(ci): suggest subagent default models (Claude can override)
Flix6x Jul 8, 2026
cb9505d
docs: move explanation to proper docs section
Flix6x Jul 8, 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 .claude/agents/api-backward-compatibility-specialist.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
name: api-backward-compatibility-specialist
description: Protects users and integrators by ensuring API changes are backwards compatible, properly versioned, and well-documented
model: sonnet
---

# Agent: API & Backward Compatibility Specialist
Expand Down
1 change: 1 addition & 0 deletions .claude/agents/architecture-domain-specialist.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
name: architecture-domain-specialist
description: Guards domain model, invariants, and architecture to maintain model clarity and prevent erosion of core principles
model: opus
---

# Agent: Architecture & Domain Specialist
Expand Down
1 change: 1 addition & 0 deletions .claude/agents/coordinator.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
name: coordinator
description: Meta-agent that manages agent lifecycle, enforces structural standards, and maintains coherence across the agent system
model: sonnet
---

# Agent: Coordinator
Expand Down
1 change: 1 addition & 0 deletions .claude/agents/data-time-semantics-specialist.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
name: data-time-semantics-specialist
description: Prevents subtle bugs in time handling, units, and data semantics with focus on timezone-aware operations and unit conversions
model: sonnet
---

# Agent: Data & Time Semantics Specialist
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
name: documentation-developer-experience-specialist
description: Ensures excellent documentation, clear error messages, and smooth developer workflows to keep FlexMeasures accessible
model: sonnet
---

# Agent: Documentation & Developer Experience Specialist
Expand Down
1 change: 1 addition & 0 deletions .claude/agents/performance-scalability-specialist.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
name: performance-scalability-specialist
description: Identifies performance bottlenecks, inefficient algorithms, and scalability issues to keep FlexMeasures fast under load
model: sonnet
---

# Agent: Performance & Scalability Specialist
Expand Down
1 change: 1 addition & 0 deletions .claude/agents/test-specialist.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
name: test-specialist
description: Focuses on test coverage, quality, and testing best practices without modifying production code
model: sonnet
---

# Agent: Test Specialist
Expand Down
1 change: 1 addition & 0 deletions .claude/agents/tooling-ci-specialist.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
name: tooling-ci-specialist
description: Reviews GitHub Actions workflows, pre-commit hooks, and CI/CD pipelines to ensure automation reliability
model: sonnet
---

# Agent: Tooling & CI Specialist
Expand Down
1 change: 1 addition & 0 deletions .claude/agents/ui-specialist.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
name: ui-specialist
description: Guards UI consistency, permission patterns, JavaScript interaction patterns, and template quality in the FlexMeasures web interface
model: sonnet
---

# Agent: UI Specialist
Expand Down
2 changes: 1 addition & 1 deletion documentation/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ New features
* Sensor references in flex-model and flex-context support various ways of filtering by source [see `PR #2209 <https://www.github.com/FlexMeasures/flexmeasures/pull/2209>`_]
* Let storage scheduling infer missing ``power-capacity`` from directional device capacities before falling back to site capacity, and default the missing opposite capacity to zero when only a non-zero ``consumption-capacity`` or ``production-capacity`` is configured [see `PR #2222 <https://www.github.com/FlexMeasures/flexmeasures/pull/2222>`_]
* Support multiple feeders to a shared storage [see `PR #2001 <https://www.github.com/FlexMeasures/flexmeasures/pull/2001>`_ ]
* The flex-context can now define multiple commodities, each specifying their own prices and grid capacities [see `PR #1946 <https://www.github.com/FlexMeasures/flexmeasures/pull/1946>`_, `PR #2172 <https://www.github.com/FlexMeasures/flexmeasures/pull/2172>`_, `PR #2235 <https://www.github.com/FlexMeasures/flexmeasures/pull/2235>`_ and `PR #2271 <https://www.github.com/FlexMeasures/flexmeasures/pull/2271>`_]
* The flex-context can now define multiple commodities, each specifying their own prices and grid capacities [see `PR #1946 <https://www.github.com/FlexMeasures/flexmeasures/pull/1946>`_, `PR #2172 <https://www.github.com/FlexMeasures/flexmeasures/pull/2172>`_, `PR #2235 <https://www.github.com/FlexMeasures/flexmeasures/pull/2235>`_, `PR #2271 <https://www.github.com/FlexMeasures/flexmeasures/pull/2271>`_ and `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>`_]
* 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
26 changes: 26 additions & 0 deletions documentation/features/scheduling.rst
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,32 @@ And if the asset belongs to a larger system (a hierarchy of assets), the schedul
The flexible device can still have its own power limit defined in its flex-model.


.. _commodity_context_defaults:

Smart defaults for commodity-context grid connections
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

For multi-commodity scheduling problems, each entry of the top-level ``commodities`` list is itself a flex-context (a "commodity context") describing the grid connection for that commodity.
A commodity context that leaves out some or all of its grid-connection fields (``consumption-price``, ``production-price``, ``site-consumption-capacity``, ``site-production-capacity`` and ``site-power-capacity``) gets sensible defaults for the missing fields, rather than failing or silently leaving the grid unconstrained.

As a rule of thumb, a price given for a direction (consumption or production) implies a grid connection in that direction, with an unlimited capacity unless a capacity is also given; a capacity given for a direction (without a price) implies a 0 price in that direction; and anything not implied by a given field defaults to "no connection" (0 capacity, as a soft constraint).
The exception is ``site-power-capacity`` given on its own, which sets a *hard* (symmetric) capacity limit instead.

This leads to the following defaults, depending on which fields are explicitly given:

- **Nothing given** (e.g. just ``{"commodity": "gas"}``): both ``site-consumption-capacity`` and ``site-production-capacity`` default to 0, as soft constraints (a breach is possible, but penalized). ``site-power-capacity`` stays unlimited.
- **Only** ``consumption-price``: ``site-power-capacity`` and ``site-consumption-capacity`` stay unlimited; ``site-production-capacity`` defaults to 0 (soft).
- **Only** ``production-price``: the mirror image, for production.
- **Only** ``site-consumption-capacity``: ``site-power-capacity`` stays unlimited; ``consumption-price`` defaults to 0; ``site-production-capacity`` (and, transitively, ``production-price``) default to 0.
- **Only** ``site-production-capacity``: the mirror image, for production.
- **Only** ``site-power-capacity``: a *hard* constraint at that capacity, with ``site-consumption-capacity`` and ``site-production-capacity`` both set equal to it, and ``consumption-price``/``production-price`` defaulting to 0.

For any combination of explicitly given fields, these rules apply per direction (consumption/production) independently, filling in only the fields not already determined by a given field.
As a safety net, ``consumption-price`` still defaults to 0 if it remains unset after applying the rules above, since the scheduler requires a resolvable consumption price.

.. note:: Setting ``relax-constraints`` to ``False`` on a commodity context that ends up with a smart-defaulted 0 hard capacity can make the schedule infeasible; FlexMeasures logs a warning in that case.


.. _flex_models_and_schedulers:

The flex-models & corresponding schedulers
Expand Down
35 changes: 34 additions & 1 deletion flexmeasures/data/models/planning/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
CommodityFlexContextSchema,
FlexContextSchema,
MultiSensorFlexModelSchema,
SharedSchema,
)
from flexmeasures.data.schemas.sensors import SensorReference, VariableQuantityField
from flexmeasures.data.services.scheduling_result import SchedulingJobResult
Expand Down Expand Up @@ -1351,6 +1352,7 @@ def convert_to_commitments(
start, end, timing_kwargs["resolution"]
)
commitment_commodity = commitment_spec.get("commodity", "electricity")
bound_device_count = 0
for d, flex_model_d in enumerate(flex_model):
device_commodity = flex_model_d.get("commodity", "electricity")
if device_commodity != commitment_commodity:
Expand All @@ -1361,6 +1363,15 @@ def convert_to_commitments(
**commitment_spec,
)
commitments.append(commitment)
bound_device_count += 1
if bound_device_count == 0:
current_app.logger.warning(
f"Commitment '{commitment_spec.get('name')}' has commodity"
f" '{commitment_commodity}', which matches none of the devices"
" in the flex-model. This commitment will not bind any device"
" (check for a typo in the commitment's `commodity` field, or in"
" a device's `commodity` field in the flex-model)."
)

return commitments

Expand Down Expand Up @@ -1408,9 +1419,17 @@ def _deserialize_flex_context(self):
commodity_flex_context
)

# Ensure all flex-contexts share the same currency unit
# Ensure all flex-contexts share the same currency unit. Contexts with
# no user-given price fields at all (shared_currency_unit_is_default)
# only carry a fallback "EUR" currency, which isn't a real constraint,
# so they're skipped here and instead backfilled below, once a real
# portfolio currency is known.
shared_currency_unit = None
default_currency_contexts = []
for commodity_flex_context in self.flex_context:
if commodity_flex_context.get("shared_currency_unit_is_default"):
default_currency_contexts.append(commodity_flex_context)
continue
context_currency_unit = commodity_flex_context["shared_currency_unit"]
if shared_currency_unit is None:
shared_currency_unit = context_currency_unit
Expand All @@ -1421,6 +1440,20 @@ def _deserialize_flex_context(self):
f"All prices in the flex-context must share the same currency unit (in this case: '{shared_currency_unit}')."
)

# Let price-free contexts inherit the portfolio's actual currency,
# where determinable (i.e. when at least one other context set one).
if shared_currency_unit is not None:
for commodity_flex_context in default_currency_contexts:
SharedSchema._rebase_default_context_currency(
commodity_flex_context, shared_currency_unit
)
elif default_currency_contexts:
# No context anywhere gave an explicit price: fall back to the
# (shared) default currency already stamped on each of them.
shared_currency_unit = default_currency_contexts[0][
"shared_currency_unit"
]

# Nest the flex-contexts per commodity under the commodity_contexts field
self.flex_context = dict(
commodity_contexts=self.flex_context,
Expand Down
61 changes: 61 additions & 0 deletions flexmeasures/data/models/planning/tests/test_commitments.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from flexmeasures.data.models.planning.linear_optimization import device_scheduler
from flexmeasures.data.models.generic_assets import GenericAsset, GenericAssetType
from flexmeasures.data.utils import save_to_db
from flexmeasures.utils.unit_utils import ur


def test_multi_feed_device_scheduler_shared_buffer():
Expand Down Expand Up @@ -1782,3 +1783,63 @@ def test_electricity_device_indices_exclude_other_commodities():
assert mapping["electricity"] == [0, 2, 3, 4]
assert mapping["gas"] == [1, 5]
assert scheduler._electricity_device_indices() == [0, 2, 3, 4]


def test_commitment_commodity_does_not_bind_other_commodity_devices():
"""test_commitment_commodity_does_not_bind_other_commodity_devices: a commitment
listed under the flex-context's `commitments` should only bind devices of its own
`commodity` (defaulting to "electricity", like devices do). A gas commitment
should therefore not create a FlowCommitment against an electricity device, and
vice versa.

This is a DB-free, unit-level test of StorageScheduler.convert_to_commitments.
"""
scheduler = object.__new__(StorageScheduler)
scheduler.flex_context = {
"shared_currency_unit": "EUR",
"commitments": [
{
"name": "gas commitment",
"commodity": "gas",
"baseline": ur.Quantity("1 MW"),
},
{
# No `commodity` given: defaults to "electricity", like devices do.
"name": "electricity commitment",
"baseline": ur.Quantity("2 MW"),
},
],
}
# Flexible devices: 0 = electricity, 1 = gas.
flex_model = [
{"commodity": "electricity"},
{"commodity": "gas"},
]

start = pd.Timestamp("2024-01-01T00:00:00+01:00")
end = pd.Timestamp("2024-01-01T03:00:00+01:00")
resolution = pd.Timedelta("1h")

commitments = scheduler.convert_to_commitments(
flex_model=flex_model,
query_window=(start, end),
resolution=resolution,
beliefs_before=None,
)

assert len(commitments) == 2

gas_commitment = next(c for c in commitments if c.name == "gas commitment")
electricity_commitment = next(
c for c in commitments if c.name == "electricity commitment"
)

# The gas commitment binds only the gas device (index 1), not the electricity
# device (index 0).
assert (gas_commitment.device == 1).all()
assert set(gas_commitment.device_group.unique()) == {"gas"}

# The electricity commitment (commodity defaulting to "electricity") binds only
# the electricity device (index 0), not the gas device (index 1).
assert (electricity_commitment.device == 0).all()
assert set(electricity_commitment.device_group.unique()) == {"electricity"}
Loading
Loading