Category: test brittleness / solution degeneracy — NOT a semantics issue (reproduces identically under legacy AND v1)
Symptom
On linopy 0.8, these fail with a numeric AssertionError on charge_state values (objective and all flow rates are correct):
tests/test_math/test_clustering.py::TestClusteringExact::test_storage_cyclic_charge_discharge_pattern
tests/test_clustering/test_expansion_regression.py::...::test_expanded_storage
Example: cs_c0 expected [50,50,0,50,0], got [100,100,50,100,50] — a uniform +50 offset / the two clusters' levels swapped.
Root cause
In cluster_mode='cyclic', StorageModel._add_initial_final_constraints intentionally skips the initial/final constraints, so the absolute SOC level of each representative cluster is unpinned — only the per-cluster delta is fixed (by the cyclic start==end constraint and the [0, capacity] bounds). The LP is therefore degenerate in the SOC level: multiple equally-optimal vertices exist. A different linopy build orders the model differently, so HiGHS returns a different (equally valid) vertex. Verified: objective == 100.0 and grid/charge/discharge flow rates match exactly; only the non-unique SOC level differs.
Why this is filed here
It was surfaced by the linopy 0.8 bump during the v1-compat work, but it is not a v1-semantics fault and not a linopy correctness bug — it reproduces identically under "legacy" and "v1". Nothing to file upstream.
Proposed fix (test-side)
Assert the objective and flow rates (which are unique), and/or assert the SOC delta rather than the absolute level; or pin the level in the fixture (e.g. an anchor constraint) if a unique trajectory is intended.
Severity
Numerically-different result vs. the hard-coded expectation, but semantics-independent and physically equivalent (same cost & dispatch).
Context: #729.
Category: test brittleness / solution degeneracy — NOT a semantics issue (reproduces identically under legacy AND v1)
Symptom
On linopy 0.8, these fail with a numeric
AssertionErroroncharge_statevalues (objective and all flow rates are correct):tests/test_math/test_clustering.py::TestClusteringExact::test_storage_cyclic_charge_discharge_patterntests/test_clustering/test_expansion_regression.py::...::test_expanded_storageExample:
cs_c0expected[50,50,0,50,0], got[100,100,50,100,50]— a uniform +50 offset / the two clusters' levels swapped.Root cause
In
cluster_mode='cyclic',StorageModel._add_initial_final_constraintsintentionally skips the initial/final constraints, so the absolute SOC level of each representative cluster is unpinned — only the per-cluster delta is fixed (by the cyclic start==end constraint and the[0, capacity]bounds). The LP is therefore degenerate in the SOC level: multiple equally-optimal vertices exist. A different linopy build orders the model differently, so HiGHS returns a different (equally valid) vertex. Verified:objective == 100.0and grid/charge/discharge flow rates match exactly; only the non-unique SOC level differs.Why this is filed here
It was surfaced by the linopy 0.8 bump during the v1-compat work, but it is not a v1-semantics fault and not a linopy correctness bug — it reproduces identically under
"legacy"and"v1". Nothing to file upstream.Proposed fix (test-side)
Assert the objective and flow rates (which are unique), and/or assert the SOC delta rather than the absolute level; or pin the level in the fixture (e.g. an anchor constraint) if a unique trajectory is intended.
Severity
Numerically-different result vs. the hard-coded expectation, but semantics-independent and physically equivalent (same cost & dispatch).
Context: #729.