Skip to content

Advisory: integration branch for the multi-commodity + commitments PR family - #2301

Closed
Flix6x wants to merge 302 commits into
mainfrom
dev/multi-commodity-integration
Closed

Advisory: integration branch for the multi-commodity + commitments PR family#2301
Flix6x wants to merge 302 commits into
mainfrom
dev/multi-commodity-integration

Conversation

@Flix6x

@Flix6x Flix6x commented Jul 12, 2026

Copy link
Copy Markdown
Member

Purpose — do not merge

This was a mock-main integration branch combining the open PRs of the multi-commodity/commitments family, opened to advise on merge order and cross-PR interactions.

Its advice has been carried out. Seven of the eight PRs it tracked are merged, and the branch itself is stale (last updated 2026-07-20). It is kept only as a record of the interactions found, which are noted below with how each was resolved.

Status of the family

PR Status
#2218 — coupling primitive (CHP) merged 2026-08-03
#2289 — balance groups (internal commodity nodes) open, now based on main, green
#2272 — commodity-context smart defaults merged 2026-07-17
#2295 — sensor-scoped commitments merged 2026-08-01
#2281#2284#2287 — UI stack merged 2026-07-17 / 07-24 / 07-24
#2285 — namespace user commitment names closed, not merged

The recommended merge order was followed, with #2289 the only piece left.

How each interaction turned out

a) #2272 × #2289#2272's smart defaults fill in zero prices for bare commodity contexts, defeating #2289's internal-node detection. This was the real one. #2272 landed first, so #2289 carries the fix: fill_grid_connection_defaults records an is_internal_node flag, and _prepare treats a commodity as internal only when the user gave no grid-connection signal at all — neither prices nor capacity fields. A commodity given a capacity but no price still carries smart-defaulted zero prices, and still declares a grid connection, so it is not an internal node.

b) #2285 × #2287 — moot. #2285 was closed without merging, so there is no custom: prefix and no test to adapt.

c) #2289 base — resolved; the balance rule sums plain ems_power, and this is part of #2289.

d) Squash-merging stacked branches conflicts spuriously — confirmed in practice, and the advice here was superseded by something better.

When #2218 was squash-merged, #2289 conflicted across 11 files, because main held #2218's content as one commit with no shared history while #2289 carried its original commits. The recommendation above (rebase each branch on main after its base merges) works, but rewrites a branch that already has review history and needs a force-push.

The approach actually used, which needs neither:

  1. Restore the base branch from its deleted tip.
  2. Merge into it the state of main from just before the squash-merge commit.
  3. Merge the base into the stacked branch, so it holds the full unsquashed lineage.
  4. Merge origin/main into the stacked branch with -X ours.

Step 4 is safe by construction: every conflict is then between two representations of the same content, so taking the stacked branch's side is correct rather than a judgement call. Verify by measuring the stacked branch's unique content before starting (git diff <old stack base> <tip> --stat) and confirming the post-merge git diff origin/main --stat matches — it did exactly, at 13 files / +405 −16.

What has changed underneath the family since

Worth knowing for anyone reading this branch as a guide, as it predates all of the following:

Not included

🤖 Generated with Claude Code

Ahmad-Wahid and others added 30 commits March 4, 2026 23:30
…multi-feed-stock

# Conflicts:
#	flexmeasures/data/models/planning/tests/test_commitments.py
Signed-off-by: Ahmad-Wahid <ahmedwahid16101@gmail.com>
Signed-off-by: Ahmad-Wahid <ahmedwahid16101@gmail.com>
Signed-off-by: Ahmad-Wahid <ahmedwahid16101@gmail.com>
Signed-off-by: Ahmad-Wahid <ahmedwahid16101@gmail.com>
Signed-off-by: Ahmad-Wahid <ahmedwahid16101@gmail.com>
Signed-off-by: Ahmad-Wahid <ahmedwahid16101@gmail.com>
Signed-off-by: Ahmad-Wahid <ahmedwahid16101@gmail.com>
Signed-off-by: Ahmad-Wahid <ahmedwahid16101@gmail.com>
Signed-off-by: Ahmad-Wahid <ahmedwahid16101@gmail.com>
Introduce stock_groups mapping to link multiple devices to a shared SOC.

Aggregate stock delta across devices sharing the same battery.

Update stock change calculation to use combined device flows.

Add device-to-group and group-to-devices lookup for efficient shared stock computation.

Signed-off-by: Ahmad-Wahid <ahmedwahid16101@gmail.com>
…er and discharge later, rather than on the EPEX price transition, as the inline test documentation advertised

Signed-off-by: F.N. Claessen <claessen@seita.nl>
…ckCommitment to prefer being full

Signed-off-by: F.N. Claessen <claessen@seita.nl>
…artificial price slope

Signed-off-by: F.N. Claessen <claessen@seita.nl>
Signed-off-by: F.N. Claessen <claessen@seita.nl>
Signed-off-by: F.N. Claessen <claessen@seita.nl>
Signed-off-by: F.N. Claessen <claessen@seita.nl>
Signed-off-by: F.N. Claessen <claessen@seita.nl>
Signed-off-by: F.N. Claessen <claessen@seita.nl>
Signed-off-by: F.N. Claessen <claessen@seita.nl>
…itment, using a tiny price slope to prefer a fuller SoC sooner rather than later, by lowering penalties later

Signed-off-by: F.N. Claessen <claessen@seita.nl>
Signed-off-by: F.N. Claessen <claessen@seita.nl>
Signed-off-by: F.N. Claessen <claessen@seita.nl>
Signed-off-by: F.N. Claessen <claessen@seita.nl>
This reverts commit bf16e63.

Signed-off-by: F.N. Claessen <claessen@seita.nl>
Signed-off-by: F.N. Claessen <claessen@seita.nl>
…ng resolution

Signed-off-by: F.N. Claessen <claessen@seita.nl>
…eat/multi-commodity

# Conflicts:
#	documentation/concepts/commitments.rst
#	flexmeasures/data/models/planning/__init__.py
#	flexmeasures/data/models/planning/storage.py
Flix6x and others added 25 commits July 8, 2026 13:32
Flag explicitly that partially-specified commodity contexts used to
leave capacities unlimited and hard-error on a missing consumption
price, whereas they now get 0-capacity soft constraints and 0 prices;
and that price-free contexts no longer trip a spurious cross-currency
error against a differently-currencied portfolio.

Signed-off-by: F.N. Claessen <claessen@seita.nl>
Move the relax-constraints breaking-change entry to the top of its
section, and fold the PR #2271 hardening-fix summary into the existing
multi-commodity feature entry (appending the PR reference) instead of
listing it as a separate bugfix bullet.

Signed-off-by: F.N. Claessen <claessen@seita.nl>
…regate fields

Per self-review: these commented-out notes on aggregate-consumption
and aggregate-production are intentional placeholders for planned
future UI support, not dead code to remove.

Signed-off-by: F.N. Claessen <claessen@seita.nl>
The field's comment previously read like it documented a supported way
to bind a commitment to a commodity. Clarify that it's internal
bookkeeping, and that the documented way to associate a commitment
with a commodity is to place it under the relevant entry of the
multi-commodity flex-context's commodities list.

Signed-off-by: F.N. Claessen <claessen@seita.nl>
…ext-defaults

# Conflicts:
#	documentation/changelog.rst
Signed-off-by: F.N. Claessen <claessen@seita.nl>

# Conflicts:
#	flexmeasures/data/models/planning/storage.py
#	flexmeasures/data/services/scheduling.py
…2274)

Context:
- PR #2072 added scheduling_result output to StorageScheduler.compute()
  when return_multiple=True, and added _compute_unresolved_targets.
- In single-sensor mode (self.sensor is set), flex_model entries lack a
  "sensor" key, so _compute_unresolved_targets was skipping every device
  and returning empty unresolved/resolved lists.

Changes:
- storage.py: fall back to self.sensor in _compute_unresolved_targets
  when the flex_model entry has no "sensor" key (single-sensor mode)
- test_commitments.py: update schedule-count assertions (+1 for the new
  scheduling_result entry added by PR #2072)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Flix6x <30658763+Flix6x@users.noreply.github.com>
…nto feat/commodity-context-defaults

# Conflicts:
#	documentation/changelog.rst
…t-defaults

chore: update openapi-specs.json

Signed-off-by: F.N. Claessen <claessen@seita.nl>
Signed-off-by: F.N. Claessen <claessen@seita.nl>
Signed-off-by: F.N. Claessen <claessen@seita.nl>
…nstraint analysis #2072, CI/agents #2275)

Conflict resolution policy:
- Schema layer: main's design wins wholesale (commodities list nesting,
  commodity default 'electricity', uniqueness + shared-currency validators).
- Engine: main's #1946 multi-commodity device_scheduler plus this branch's
  coupling_groups primitive (coupling_alpha variables and per-device
  flow-coupling constraints), keeping the branch's group_to_devices mapping
  which supports overlapping stock-group membership.
- storage.py: main's _prepare/_build_soc_schedule structure wins, with the
  branch's coupling-group construction re-added; main's strict price-presence
  validation is restored (the branch had it commented out).
- Tests: kept both sides' distinct tests (branch: CHP coupling + factory
  dispatch; main: gas-only flex-model, per-commodity inflexible sensors,
  directional-capacity fallback).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016MLCUiSdXDqDBmg8GbYp1B
The sign of a coupling coefficient is inferred from directional capacities,
and a device with both directions open (or blocked) was silently treated as
an input. Reject such flex-models with a validation error instead.

Also promote the coupling field descriptions to MetaData constants and
document both fields in the storage flex-model table.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016MLCUiSdXDqDBmg8GbYp1B
Adds a balance_groups argument to device_scheduler: each group lists the
devices of an internal commodity node (e.g. a heat or steam network without
a grid connection) whose stock-side flows must sum to zero at every time
step. This replaces the reference-device min=max=0 stock-group workaround
used by the factory scenario, which is now tested in both modes.

The StorageScheduler derives balance groups from the flex-config: a
non-electricity commodity without energy prices becomes an internal node
(previously this raised 'Missing consumption price'). Together with
coupling groups (one flex-model entry per converter port), this makes the
factory scenario (CHP + gas boiler + e-heater meeting a fixed steam demand)
schedulable end-to-end through StorageScheduler.compute().

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

# Conflicts:
#	.claude/agents/api-backward-compatibility-specialist.md
#	.claude/agents/architecture-domain-specialist.md
#	.claude/agents/coordinator.md
#	.claude/agents/data-time-semantics-specialist.md
#	.claude/agents/documentation-developer-experience-specialist.md
#	.claude/agents/performance-scalability-specialist.md
#	.claude/agents/test-specialist.md
#	.claude/agents/tooling-ci-specialist.md
#	.claude/agents/ui-specialist.md
- Give this PR its own concise changelog entry (pointing at the new
  commodity_context_defaults docs section) instead of only tagging the
  multi-commodity entry.
- Reword the smart-defaults docs per review: prefer 'zero' over '0',
  clarify that zero prices refer to the usage (energy) prices, adopt the
  suggested 'Then, ...' phrasing in the per-case bullets, and state that
  giving all capacity fields is valid (directional capacities soft within
  the hard site-power-capacity limit).
- Drop the redundant test-name prefix from a test docstring.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016MLCUiSdXDqDBmg8GbYp1B
…l combo branch

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

The smart defaults (#2272) fill a zero consumption-price into price-free
commodity contexts, which defeated the priceless-commodity internal-node
detection (#2279). Record durably on the context whether any price field
was user-given (prices_are_defaulted), and treat a context whose prices
were all defaulted as an internal node.

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

A device can sit in both a commodity balance group (via its commodity) and
a shared-stock group (via its state-of-charge sensor), e.g. a steamer that
discharges a heat buffer to produce steam. Its derivative efficiencies and
stock delta (e.g. the buffer's soc-usage losses assigned to it) describe
the stock-side conversion and must not leak into the commodity balance:
what crosses the node is the device's power flow (ems_power).

Found while running a realistic factory scenario, where the heat buffer's
soc-usage drain was distorting the steam balance.

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

A flex-context commitment gains an optional 'sensors' field: instead of
binding each device of the matching commodity separately, the commitment
binds the aggregate flow of the devices whose power sensors are listed,
as one grouped commitment (device_group machinery). Useful to commit a
band on a subset of devices, e.g. an aFRR upward-regulation band on a
site's e-heaters (aggregate consumption >= band, deviation penalized).

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

Flix6x commented Jul 17, 2026

Copy link
Copy Markdown
Member Author

Consolidation status update (main has since absorbed the solver rewrite #2282/#2321/#2322/#2325 and merged #2272 + #2281):

Done — rebased onto current main, tested, pushed (fast-forward, no history rewrite):

Remaining: #2295 (scoped commitments), UI #2284#2287, #2285, then a refresh of this integration branch onto current main. The two fixes above are the ones flagged in the interaction notes — now landed on their respective PR branches.

🤖 update via Claude Code

@Flix6x Flix6x self-assigned this Jul 20, 2026
@Flix6x

Flix6x commented Aug 4, 2026

Copy link
Copy Markdown
Member Author

Closing this advisory now that only #2289 needs to land.

@Flix6x Flix6x closed this Aug 4, 2026
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.

3 participants