Skip to content

fix: support linopy 0.9.0 - #758

Merged
FBumann merged 2 commits into
mainfrom
fix/linopy-0.9-compat
Jul 27, 2026
Merged

fix: support linopy 0.9.0#758
FBumann merged 2 commits into
mainfrom
fix/linopy-0.9-compat

Conversation

@FBumann

@FBumann FBumann commented Jul 27, 2026

Copy link
Copy Markdown
Member

Makes flixopt build and solve on linopy 0.9.0, and widens the pin to >=0.5.1,<0.10.

What broke

linopy 0.9 deprecates assignment to Constraint.lhs in favour of Constraint.update(lhs=...). flixopt escalates DeprecationWarning from its own package to an error (error::DeprecationWarning:flixopt), so the accumulator in ShareAllocationModel took down every effect-bearing model at build time — 668 of 1261 tests failed, all from this single root cause.

The fix

modeling._set_constraint_lhs() dispatches on whichever API the installed linopy provides:

  • linopy >= 0.8 → Constraint.update(lhs=...) (the .lhs setter is deprecated there)
  • linopy < 0.8 → the .lhs setter (update() does not exist, and the setter is not deprecated)

All six in-place LHS mutations route through it: the share accumulators in features.py, the bus imbalance term in elements.py, and transmission absolute losses in components.py. Term order is unchanged on every version, so the existing white-box constraint tests still hold.

Two tests were pinning degenerate optima

Both flipped under 0.9 because its internal reordering makes HiGHS land on a different vertex of the same optimal face. The objective and all flow rates are identical — only the arbitrary part of the solution moved. This is the degeneracy already diagnosed in #733, not a semantics change.

  • test_storage_cyclic_charge_discharge_pattern — the two clusters carry identical data, so which one gets which absolute SOC offset is arbitrary (any level in [50, 100] is optimal). Now asserts the SOC deltas and the cyclic wrap, which are uniquely determined.
  • test_expanded_storage — gas price and boiler efficiency are flat, so storage earns nothing and every cycling depth (including none) is optimal. The old nansum(charge_state) > 0 passed on linopy 0.7 only on ~1e-5 numerical noise.

Verification

Full suite minus slow, 1261 tests:

linopy result
0.9.0 1259 passed, 2 skipped
0.8.0 1259 passed, 2 skipped
0.7.0 1259 passed, 2 skipped
0.6.7 1259 passed, 2 skipped
0.5.1 linopy↔xarray incompat only (see below)

0.5.1 fails solely through the known incompatibility between old linopy and xarray >= 2026 — TypeError: Passing a Dataset as data_vars raised inside linopy/expressions.py, before any flixopt code runs. Paired with xarray 2025.7.1 it passes, so the >=0.5.1 lower bound remains honest.

Not run: slow tests, notebooks, examples.

Relationship to other PRs

Pre-existing issue noticed, not addressed here

The expanded clustered charge_state can far exceed capacity (241 against a cap of 50 under gurobi), and the clustered charge_state goes negative. Both behave identically on linopy 0.7.0, so they predate this work — plausibly the same root as #735 (initial_charge_state ignored in cyclic cluster_mode).

🤖 Generated with Claude Code

FBumann added 2 commits July 27, 2026 16:09
…te()

linopy 0.8 added Constraint.update() and 0.9 deprecates assignment to
Constraint.lhs. flixopt escalates DeprecationWarning from its own package to
an error, so every effect-bearing model failed to build on linopy 0.9.

Add modeling._set_constraint_lhs(), which dispatches to Constraint.update()
where it exists and to the .lhs setter on linopy < 0.8, and route the six
in-place LHS mutations (share accumulators, bus imbalance, transmission
absolute losses) through it. Term order is unchanged on every version.

Widen the linopy pin to >=0.5.1,<0.10.
Both tests pinned one arbitrary vertex of a degenerate optimal face, so they
flipped when linopy 0.9 changed internal ordering and HiGHS landed on the
mirrored optimum. The objective and all flow rates were unchanged.

test_storage_cyclic_charge_discharge_pattern: the two clusters carry identical
data, so which one gets which absolute SOC offset is arbitrary (any level in
[50, 100] is optimal). Assert the SOC deltas and the cyclic wrap instead.

test_expanded_storage: gas price and boiler efficiency are flat, so storage
earns nothing and every cycling depth -- including none -- is optimal. The
old `nansum(charge_state) > 0` passed on linopy 0.7 only on ~1e-5 noise.
@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@FBumann, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 49 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1f2c6662-23cf-43c0-9f4b-059374e5456f

📥 Commits

Reviewing files that changed from the base of the PR and between f280bda and 7164d44.

📒 Files selected for processing (7)
  • flixopt/components.py
  • flixopt/elements.py
  • flixopt/features.py
  • flixopt/modeling.py
  • pyproject.toml
  • tests/test_clustering/test_expansion_regression.py
  • tests/test_math/test_clustering.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/linopy-0.9-compat

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@FBumann
FBumann merged commit 051480b into main Jul 27, 2026
11 checks passed
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