Skip to content

feat: unit-committed affine coupling groups (on/off + min-load + no-load base)#2336

Open
Flix6x wants to merge 1 commit into
feat/chpfrom
feat/unit-committed-coupling
Open

feat: unit-committed affine coupling groups (on/off + min-load + no-load base)#2336
Flix6x wants to merge 1 commit into
feat/chpfrom
feat/unit-committed-coupling

Conversation

@Flix6x

@Flix6x Flix6x commented Jul 22, 2026

Copy link
Copy Markdown
Member

Extends the coupling primitive (#2218) with unit commitment: a coupling group can now have an on/off binary, a minimum level when on, and a per-port no-load base gated by that binary.

What it does

When a coupling group is unit-committed, the solver introduces a per-timestep binary u_g[j] ∈ {0,1} and a marginal level alpha_g[j] ≥ 0, with:

  • min_g * u_g[j] <= alpha_g[j] <= max_g * u_g[j]alpha is 0 when off, and at least the minimum level when on;
  • for every port d in the group: P[d,j] == coeff_d * alpha_g[j] + base_d * u_g[j],

where coeff_d is the existing signed coupling coefficient and base_d is the new signed per-port no-load base. This makes the affine cogeneration/CHP model exact, including no-load fuel: in an unprofitable step the whole unit idles (all ports exactly 0, no no-load fuel burned), and when running it stays at or above the minimum level.

Purely proportional (non-UC) coupling is unchanged: the problem stays an LP unless some group is unit-committed (a group becomes unit-committed only when coupling-min is set or a non-zero coupling-base is given). Minimum up/down times are out of scope.

Schema

  • coupling-base (per port): a signed power quantity whose direction follows the port's flow direction (like coupling-coefficient); default 0.
  • coupling-min (on the group's reference port, the port with |coefficient| = 1): a power quantity that sets the group minimum and enables unit commitment. max_g comes from the reference port's power-capacity.

Both fields are documented in metadata.py, added to the storage flex-model schemas, and reflected in openapi-specs.json. They are carried through DeviceInventory (coupling_uc, coupling_bases) and storage.py to device_scheduler.

Tests

  • A factory-style, hand-computed 1-step unit-committed cogeneration dispatch (driving electrical output, affine gas/heat with no-load bases): verifies full idle in an unprofitable step, running at max (≥ Pmin) in a profitable step, and the exact objective.
  • Plumbing tests that a unit-committed flex-model resolves to the expected signed bases and (min, max) bounds in MW, and that a group without min/base stays purely proportional.
  • The existing proportional coupling tests still pass unchanged.

Stacked on #2218.

🤖 Generated with Claude Code

…oad base)

Extend the coupling primitive (#2218) so a converter (e.g. a cogeneration
unit) can be modelled with an on/off binary, a minimum level when on, and a
per-port no-load base gated by that binary.

When a coupling group is unit-committed, the solver adds a per-timestep binary
u_g[j] and a marginal level alpha_g[j] >= 0 with
  min_g * u_g <= alpha_g <= max_g * u_g
and, for every port d,
  P[d,j] == coeff_d * alpha_g[j] + base_d * u_g[j].
This turns the LP into a MILP only when a group is unit-committed; purely
proportional coupling is unchanged.

Schema: add a per-port `coupling-base` (signed power quantity, sign follows the
port's flow direction; default 0) and a `coupling-min` power quantity on the
group's reference port (|coeff| == 1). Presence of `coupling-min` (or any
non-zero `coupling-base`) marks the group unit-committed; max_g comes from the
reference port's `power-capacity`. Both are carried through DeviceInventory
(coupling_uc, coupling_bases) and storage.py to device_scheduler.

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>
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.

1 participant