From de56f17747c69600e7de2e3b0c7b7add3ce0dc61 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 7 May 2026 18:59:35 +0200 Subject: [PATCH 1/9] docs: restructure upcoming release notes and fold in missing PRs Group the upcoming version block into Features / Performance / Bug Fixes / Breaking Changes / Documentation sections so the headline (piecewise) leads, and add the entries for #589, #595, #601, #614, #619, #635, #656, #671, #672, #674. Tighten the piecewise block to its final state. Co-Authored-By: Claude Opus 4.7 (1M context) --- doc/release_notes.rst | 75 ++++++++++++++++++++++++++++++------------- 1 file changed, 52 insertions(+), 23 deletions(-) diff --git a/doc/release_notes.rst b/doc/release_notes.rst index 88180844..34ca7c5f 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -4,31 +4,60 @@ Release Notes Upcoming Version ---------------- -* Add ``Model.copy()`` (default deep copy) with ``deep`` and ``include_solution`` options; support Python ``copy.copy`` and ``copy.deepcopy`` protocols via ``__copy__`` and ``__deepcopy__``. -* Harmonize coordinate alignment for operations with subset/superset objects: - - Multiplication and division fill missing coords with 0 (variable doesn't participate) - - Addition and subtraction of constants fill missing coords with 0 (identity element) and pin result to LHS coords - - Comparison operators (``==``, ``<=``, ``>=``) fill missing RHS coords with NaN (no constraint created) - - Fixes crash on ``subset + var`` / ``subset + expr`` reverse addition - - Fixes superset DataArrays expanding result coords beyond the variable's coordinate space -* Add ``add_piecewise_formulation()`` for piecewise linear constraints with SOS2, incremental, and disjunctive formulations: ``m.add_piecewise_formulation((power, x_pts), (fuel, y_pts))``. Supports N-variable linking (e.g. CHP with fuel/power/heat) and per-entity breakpoints. ``method="auto"`` picks the cheapest correct formulation automatically. The API is newly added and emits an :class:`linopy.EvolvingAPIWarning` to signal that details may be refined in minor releases — the current restrictions on per-tuple sign (at most one bounded tuple, N≥3 must be all equality) are the most likely candidates to relax as use cases come in. Feedback and use cases at https://github.com/PyPSA/linopy/issues shape what stabilises. Silence with ``warnings.filterwarnings("ignore", category=linopy.EvolvingAPIWarning)``. -* Add one-sided piecewise bounds via a per-tuple sign on ``add_piecewise_formulation``: append ``"<="`` or ``">="`` as a third tuple element — e.g. ``(fuel, y_pts, "<=")`` — to mark that expression as bounded by the curve while the others remain pinned. At most one tuple may carry a non-equality sign; with 3 or more tuples all signs must be ``"=="``. On convex/concave curves with a matching sign, ``method="auto"`` dispatches to a pure-LP chord formulation (``method="lp"``) with no auxiliary variables and automatic domain bounds on the input. Mismatched curvature+sign is detected and falls back to SOS2/incremental with an explanatory info log. -* Add unit-commitment gating via the ``active`` parameter on ``add_piecewise_formulation``: a binary variable that, when zero, forces all auxiliary variables (and thus the linked expressions) to zero. Works with the SOS2, incremental, and disjunctive methods. -* Surface formulation metadata on the returned ``PiecewiseFormulation``: ``.method`` (resolved method name) and ``.convexity`` (``"convex"`` / ``"concave"`` / ``"linear"`` / ``"mixed"`` when well-defined). Both persist across netCDF round-trip. -* Add ``tangent_lines()`` as a low-level helper that returns per-piece chord expressions as a ``LinearExpression`` — no variables created. Most users should prefer ``add_piecewise_formulation`` with a bounded tuple ``(y, y_pts, "<=")``, which builds on this helper and adds domain bounds and curvature validation. -* Add ``linopy.breakpoints()`` (lists/Series/DataFrame/DataArray/dict) and ``linopy.segments()`` (disjunctive operating regions) as breakpoint-construction helpers. -* Add ``linopy.Slopes`` for specifying a piecewise curve by marginal costs / per-piece slopes instead of absolute y-values — ``(fuel, Slopes([1.2, 1.4, 1.7], y0=0))`` borrows the x grid from a sibling tuple in ``add_piecewise_formulation``. -* Add the `sphinx-copybutton` to the documentation -* Add SOS1 and SOS2 reformulations for solvers not supporting them. -* Add semi-continous variables for solvers that support them -* Add ``OetcSettings.from_env()`` classmethod to create OETC settings from environment variables (``OETC_EMAIL``, ``OETC_PASSWORD``, ``OETC_NAME``, ``OETC_AUTH_URL``, ``OETC_ORCHESTRATOR_URL``, ``OETC_CPU_CORES``, ``OETC_DISK_SPACE_GB``, ``OETC_DELETE_WORKER_ON_ERROR``). -* Forward ``solver_name`` and ``**solver_options`` from ``Model.solve()`` to OETC handler. Call-level options override settings-level defaults. -* Improve handling of CPLEX solver quality attributes to ensure metrics such are extracted correctly when available. -* Enable quadratic problems with SCIP on windows. -* Add ``format_labels()`` on ``Constraints``/``Variables`` and ``format_infeasibilities()`` on ``Model`` that return strings instead of printing to stdout, allowing usage with logging, storage, or custom output handling. Deprecate ``print_labels()`` and ``print_infeasibilities()``. +**Features** + +*Piecewise linear constraints (new)* + +* Add ``add_piecewise_formulation()`` for piecewise linear constraints with SOS2, incremental, and disjunctive formulations: ``m.add_piecewise_formulation((power, x_pts), (fuel, y_pts))``. Supports N-variable linking (e.g. CHP with fuel/power/heat) and per-entity breakpoints. ``method="auto"`` picks the cheapest correct formulation automatically. The API emits an :class:`linopy.EvolvingAPIWarning` to signal that details may be refined in minor releases — feedback at https://github.com/PyPSA/linopy/issues shapes what stabilises. Silence with ``warnings.filterwarnings("ignore", category=linopy.EvolvingAPIWarning)``. +* One-sided bounds via a per-tuple sign: append ``"<="`` or ``">="`` as a third tuple element (e.g. ``(fuel, y_pts, "<=")``) to mark that expression as bounded by the curve while the others remain pinned. On convex/concave curves with a matching sign, ``method="auto"`` dispatches to a pure-LP chord formulation with no auxiliary variables and automatic domain bounds; mismatched curvature falls back to SOS2/incremental with an explanatory log. +* Unit-commitment gating via the ``active`` parameter: a binary variable that, when zero, forces all auxiliary variables (and thus the linked expressions) to zero. Works with the SOS2, incremental, and disjunctive methods. +* Returned ``PiecewiseFormulation`` surfaces ``.method`` and ``.convexity`` (``"convex"`` / ``"concave"`` / ``"linear"`` / ``"mixed"``); both persist across netCDF round-trip. +* Construction helpers: ``linopy.breakpoints()`` (lists/Series/DataFrame/DataArray/dict) with a ``slopes_align`` keyword for the marginal-cost convention; ``linopy.segments()`` for disjunctive operating regions; ``linopy.Slopes`` for specifying a curve by per-piece slopes — ``(fuel, Slopes([1.2, 1.4, 1.7], y0=0))`` borrows the x grid from a sibling tuple; and ``tangent_lines()`` as a low-level chord-expression helper. + +*Variable utilities* + * Add ``fix()``, ``unfix()``, and ``fixed`` to ``Variable`` and ``Variables`` for fixing variables to values via equality constraints. Supports automatic rounding for integer/binary variables. * Add ``relax()``, ``unrelax()``, and ``relaxed`` to ``Variable`` and ``Variables`` for LP relaxation of integer/binary variables. Supports partial relaxation via filtered views (e.g. ``m.variables.integers.relax()``). Semi-continuous variables raise ``NotImplementedError``. -* Fix ``as_dataarray`` treating multi-index level names as extra dimensions when broadcasting a scalar against ``xarray.Coordinates``. +* Add support for semi-continuous variables on solvers that support them. +* Add SOS1 and SOS2 reformulations for solvers without native SOS support. ``Model.solve()`` accepts ``reformulate_sos="auto"`` to apply the reformulation only when the chosen solver lacks SOS support and pass through otherwise. + +*Model and expression utilities* + +* Add ``Model.copy()`` (default deep copy) with ``deep`` and ``include_solution`` options; supports Python's ``copy.copy`` and ``copy.deepcopy`` protocols via ``__copy__`` and ``__deepcopy__``. +* Add ``__weakref__`` to ``Model.__slots__`` so ``weakref.ref(model)`` and ``WeakKeyDictionary`` keyed by ``Model`` work — enables third-party accessor-style extensions without subclassing. +* Add ``format_labels()`` on ``Constraints``/``Variables`` and ``format_infeasibilities()`` on ``Model`` that return strings instead of printing to stdout, allowing usage with logging, storage, or custom output handling. Deprecate ``print_labels()`` and ``print_infeasibilities()``. +* Harmonize coordinate alignment for operations between objects with subset/superset coordinates: + + - Multiplication and division fill missing coords with 0 (variable doesn't participate). + - Addition and subtraction of constants fill missing coords with 0 (identity element) and pin the result to LHS coords. + - Comparison operators (``==``, ``<=``, ``>=``) fill missing RHS coords with NaN (no constraint created). + - Fixes the crash on ``subset + var`` / ``subset + expr`` reverse addition and superset DataArrays expanding result coords beyond the variable's coordinate space. + +*Solver integration* + +* Forward ``solver_name`` and ``**solver_options`` from ``Model.solve()`` to the OETC handler. Call-level options override settings-level defaults. +* Add ``OetcSettings.from_env()`` classmethod to create OETC settings from environment variables (``OETC_EMAIL``, ``OETC_PASSWORD``, ``OETC_NAME``, ``OETC_AUTH_URL``, ``OETC_ORCHESTRATOR_URL``, ``OETC_CPU_CORES``, ``OETC_DISK_SPACE_GB``, ``OETC_DELETE_WORKER_ON_ERROR``). +* Enable quadratic problems with SCIP on Windows. +* Improve handling of CPLEX solver quality attributes; safely skip attributes that are not always available (e.g. ``max_dual_infeasibility`` when a barrier solution has no crossover). + +**Performance** + +* Speed up solution unpacking in ``Model.solve()`` by replacing pandas ``Series.loc`` with a direct numpy array lookup. + +**Bug Fixes** + +* Raise a clear ``ValueError`` from ``Model.solve()`` when no objective has been set, instead of writing a malformed LP file. The message points to ``m.add_objective(...)`` (and ``m.add_objective(0 * x)`` for pure-feasibility checks). +* Fix ``add_variables`` silently ignoring ``coords`` when ``lower`` / ``upper`` are passed as DataArrays. +* Fix ``as_dataarray`` treating MultiIndex level names as extra dimensions when broadcasting a scalar against ``xarray.Coordinates``. +* Fix ``Model.to_netcdf`` failing on the scipy netCDF backend with a ``KeyError`` on MultiIndex level names; the names are now serialized as a JSON-encoded string. Files written by older linopy versions remain readable. + +**Breaking Changes** + +* ``google-cloud-storage`` and ``requests`` are now optional dependencies. Install with the ``oetc`` extra (``pip install linopy[oetc]``) to keep the previous behaviour. + +**Documentation** + +* Add ``sphinx-copybutton`` to the documentation. Version 0.6.7 From 326728c50d093638b46d818fb303bb6cbd23e8b6 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 7 May 2026 19:03:16 +0200 Subject: [PATCH 2/9] docs: tighten upcoming changelog and drop internal-only entries Trim verbose phrasing in the piecewise / variables / model / solvers sections, fold subset-superset sub-bullets into one paragraph, and drop two entries that aren't user-facing for a release notes audience: sphinx-copybutton (doc tooling) and Model.__weakref__ (only relevant to extension authors). Co-Authored-By: Claude Opus 4.7 (1M context) --- doc/release_notes.rst | 60 ++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 35 deletions(-) diff --git a/doc/release_notes.rst b/doc/release_notes.rst index 34ca7c5f..3a82c623 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -8,56 +8,46 @@ Upcoming Version *Piecewise linear constraints (new)* -* Add ``add_piecewise_formulation()`` for piecewise linear constraints with SOS2, incremental, and disjunctive formulations: ``m.add_piecewise_formulation((power, x_pts), (fuel, y_pts))``. Supports N-variable linking (e.g. CHP with fuel/power/heat) and per-entity breakpoints. ``method="auto"`` picks the cheapest correct formulation automatically. The API emits an :class:`linopy.EvolvingAPIWarning` to signal that details may be refined in minor releases — feedback at https://github.com/PyPSA/linopy/issues shapes what stabilises. Silence with ``warnings.filterwarnings("ignore", category=linopy.EvolvingAPIWarning)``. -* One-sided bounds via a per-tuple sign: append ``"<="`` or ``">="`` as a third tuple element (e.g. ``(fuel, y_pts, "<=")``) to mark that expression as bounded by the curve while the others remain pinned. On convex/concave curves with a matching sign, ``method="auto"`` dispatches to a pure-LP chord formulation with no auxiliary variables and automatic domain bounds; mismatched curvature falls back to SOS2/incremental with an explanatory log. -* Unit-commitment gating via the ``active`` parameter: a binary variable that, when zero, forces all auxiliary variables (and thus the linked expressions) to zero. Works with the SOS2, incremental, and disjunctive methods. -* Returned ``PiecewiseFormulation`` surfaces ``.method`` and ``.convexity`` (``"convex"`` / ``"concave"`` / ``"linear"`` / ``"mixed"``); both persist across netCDF round-trip. -* Construction helpers: ``linopy.breakpoints()`` (lists/Series/DataFrame/DataArray/dict) with a ``slopes_align`` keyword for the marginal-cost convention; ``linopy.segments()`` for disjunctive operating regions; ``linopy.Slopes`` for specifying a curve by per-piece slopes — ``(fuel, Slopes([1.2, 1.4, 1.7], y0=0))`` borrows the x grid from a sibling tuple; and ``tangent_lines()`` as a low-level chord-expression helper. +* ``Model.add_piecewise_formulation()`` for piecewise linear constraints with SOS2, incremental, and disjunctive formulations: ``m.add_piecewise_formulation((power, x_pts), (fuel, y_pts))``. Supports N-variable linking (e.g. CHP with fuel/power/heat), per-entity breakpoints, and ``method="auto"`` to pick the cheapest correct formulation. The API is marked with :class:`linopy.EvolvingAPIWarning` while it stabilises. +* One-sided bounds via a per-tuple sign — append ``"<="`` or ``">="`` (e.g. ``(fuel, y_pts, "<=")``). On matching convex/concave curves, ``method="auto"`` dispatches to a pure-LP chord formulation with no auxiliary variables. +* Unit-commitment gating via the ``active`` parameter: a binary variable that, when zero, forces all auxiliary variables to zero. +* ``PiecewiseFormulation`` exposes ``.method`` and ``.convexity``; both persist across netCDF round-trip. +* Construction helpers: ``linopy.breakpoints()`` (with a ``slopes_align`` keyword for the marginal-cost convention), ``linopy.segments()`` for disjunctive operating regions, ``linopy.Slopes`` for specifying a curve by per-piece slopes, and ``tangent_lines()`` as a low-level chord helper. -*Variable utilities* +*Variables* -* Add ``fix()``, ``unfix()``, and ``fixed`` to ``Variable`` and ``Variables`` for fixing variables to values via equality constraints. Supports automatic rounding for integer/binary variables. -* Add ``relax()``, ``unrelax()``, and ``relaxed`` to ``Variable`` and ``Variables`` for LP relaxation of integer/binary variables. Supports partial relaxation via filtered views (e.g. ``m.variables.integers.relax()``). Semi-continuous variables raise ``NotImplementedError``. -* Add support for semi-continuous variables on solvers that support them. -* Add SOS1 and SOS2 reformulations for solvers without native SOS support. ``Model.solve()`` accepts ``reformulate_sos="auto"`` to apply the reformulation only when the chosen solver lacks SOS support and pass through otherwise. +* ``fix()`` / ``unfix()`` / ``fixed`` for fixing variables to values via equality constraints (with auto-rounding for integer/binary variables). +* ``relax()`` / ``unrelax()`` / ``relaxed`` for LP relaxation of integer/binary variables; supports partial relaxation via filtered views (e.g. ``m.variables.integers.relax()``). +* Semi-continuous variables on solvers that support them. +* SOS1 / SOS2 reformulations for solvers without native SOS support. ``Model.solve(reformulate_sos="auto")`` applies the reformulation only when the chosen solver lacks SOS. -*Model and expression utilities* +*Model* -* Add ``Model.copy()`` (default deep copy) with ``deep`` and ``include_solution`` options; supports Python's ``copy.copy`` and ``copy.deepcopy`` protocols via ``__copy__`` and ``__deepcopy__``. -* Add ``__weakref__`` to ``Model.__slots__`` so ``weakref.ref(model)`` and ``WeakKeyDictionary`` keyed by ``Model`` work — enables third-party accessor-style extensions without subclassing. -* Add ``format_labels()`` on ``Constraints``/``Variables`` and ``format_infeasibilities()`` on ``Model`` that return strings instead of printing to stdout, allowing usage with logging, storage, or custom output handling. Deprecate ``print_labels()`` and ``print_infeasibilities()``. -* Harmonize coordinate alignment for operations between objects with subset/superset coordinates: +* ``Model.copy()`` (default deep) with ``deep`` and ``include_solution`` options; supports ``copy.copy`` / ``copy.deepcopy``. +* ``format_labels()`` (on ``Constraints``/``Variables``) and ``format_infeasibilities()`` (on ``Model``) return strings instead of printing. Deprecates ``print_labels()`` / ``print_infeasibilities()``. +* Harmonized coordinate alignment between subset/superset operands: multiplication/division fill missing coords with 0; constant ± fills with 0 and pins to LHS coords; comparisons fill missing RHS coords with NaN. Fixes ``subset + var`` reverse-addition and superset DataArrays expanding result coords. - - Multiplication and division fill missing coords with 0 (variable doesn't participate). - - Addition and subtraction of constants fill missing coords with 0 (identity element) and pin the result to LHS coords. - - Comparison operators (``==``, ``<=``, ``>=``) fill missing RHS coords with NaN (no constraint created). - - Fixes the crash on ``subset + var`` / ``subset + expr`` reverse addition and superset DataArrays expanding result coords beyond the variable's coordinate space. +*Solvers* -*Solver integration* - -* Forward ``solver_name`` and ``**solver_options`` from ``Model.solve()`` to the OETC handler. Call-level options override settings-level defaults. -* Add ``OetcSettings.from_env()`` classmethod to create OETC settings from environment variables (``OETC_EMAIL``, ``OETC_PASSWORD``, ``OETC_NAME``, ``OETC_AUTH_URL``, ``OETC_ORCHESTRATOR_URL``, ``OETC_CPU_CORES``, ``OETC_DISK_SPACE_GB``, ``OETC_DELETE_WORKER_ON_ERROR``). -* Enable quadratic problems with SCIP on Windows. -* Improve handling of CPLEX solver quality attributes; safely skip attributes that are not always available (e.g. ``max_dual_infeasibility`` when a barrier solution has no crossover). +* ``Model.solve()`` forwards ``solver_name`` and ``**solver_options`` to the OETC handler (call-level overrides settings-level defaults). +* ``OetcSettings.from_env()`` reads OETC settings from ``OETC_*`` environment variables. +* SCIP supports quadratic problems on Windows. +* CPLEX safely skips quality attributes that aren't always available (e.g. ``max_dual_infeasibility`` without crossover). **Performance** -* Speed up solution unpacking in ``Model.solve()`` by replacing pandas ``Series.loc`` with a direct numpy array lookup. +* Faster solution unpacking in ``Model.solve()`` via direct numpy indexing instead of pandas ``Series.loc``. **Bug Fixes** -* Raise a clear ``ValueError`` from ``Model.solve()`` when no objective has been set, instead of writing a malformed LP file. The message points to ``m.add_objective(...)`` (and ``m.add_objective(0 * x)`` for pure-feasibility checks). -* Fix ``add_variables`` silently ignoring ``coords`` when ``lower`` / ``upper`` are passed as DataArrays. -* Fix ``as_dataarray`` treating MultiIndex level names as extra dimensions when broadcasting a scalar against ``xarray.Coordinates``. -* Fix ``Model.to_netcdf`` failing on the scipy netCDF backend with a ``KeyError`` on MultiIndex level names; the names are now serialized as a JSON-encoded string. Files written by older linopy versions remain readable. +* ``Model.solve()`` raises a clear ``ValueError`` when no objective is set, instead of writing a malformed LP file. +* ``add_variables`` no longer ignores ``coords`` when ``lower``/``upper`` are DataArrays. +* ``as_dataarray`` no longer treats MultiIndex level names as extra dimensions when broadcasting a scalar against ``xarray.Coordinates``. +* ``Model.to_netcdf`` now works on the scipy netCDF backend (MultiIndex level names serialized as JSON; old files remain readable). **Breaking Changes** -* ``google-cloud-storage`` and ``requests`` are now optional dependencies. Install with the ``oetc`` extra (``pip install linopy[oetc]``) to keep the previous behaviour. - -**Documentation** - -* Add ``sphinx-copybutton`` to the documentation. +* ``google-cloud-storage`` and ``requests`` are now optional. Install ``linopy[oetc]`` to keep the previous behaviour. Version 0.6.7 From 2fe721c092d28d12776a998df92976aa1a9e6d4e Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 7 May 2026 19:04:49 +0200 Subject: [PATCH 3/9] docs: move align convention from breakpoints() to Slopes in changelog #673 removed the slopes-mode (and slopes_align kwarg) from breakpoints(); the align kwarg now lives on the Slopes class. Co-Authored-By: Claude Opus 4.7 (1M context) --- doc/release_notes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/release_notes.rst b/doc/release_notes.rst index 3a82c623..ec199a6e 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -12,7 +12,7 @@ Upcoming Version * One-sided bounds via a per-tuple sign — append ``"<="`` or ``">="`` (e.g. ``(fuel, y_pts, "<=")``). On matching convex/concave curves, ``method="auto"`` dispatches to a pure-LP chord formulation with no auxiliary variables. * Unit-commitment gating via the ``active`` parameter: a binary variable that, when zero, forces all auxiliary variables to zero. * ``PiecewiseFormulation`` exposes ``.method`` and ``.convexity``; both persist across netCDF round-trip. -* Construction helpers: ``linopy.breakpoints()`` (with a ``slopes_align`` keyword for the marginal-cost convention), ``linopy.segments()`` for disjunctive operating regions, ``linopy.Slopes`` for specifying a curve by per-piece slopes, and ``tangent_lines()`` as a low-level chord helper. +* Construction helpers: ``linopy.breakpoints()`` for points-based breakpoints, ``linopy.segments()`` for disjunctive operating regions, ``linopy.Slopes`` for specifying a curve by per-piece slopes (with ``align="pieces"``/``"leading"`` for the marginal-cost convention), and ``tangent_lines()`` as a low-level chord helper. *Variables* From 531cf1d5f8fcaa2498a0fabaf08aeb78df8558b9 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 7 May 2026 19:05:24 +0200 Subject: [PATCH 4/9] docs: move SOS reformulation bullet from Variables to Model SOS reformulation is a model-rewrite/solve-pipeline concern, not a variable attribute. Co-Authored-By: Claude Opus 4.7 (1M context) --- doc/release_notes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/release_notes.rst b/doc/release_notes.rst index ec199a6e..e65d3da0 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -19,11 +19,11 @@ Upcoming Version * ``fix()`` / ``unfix()`` / ``fixed`` for fixing variables to values via equality constraints (with auto-rounding for integer/binary variables). * ``relax()`` / ``unrelax()`` / ``relaxed`` for LP relaxation of integer/binary variables; supports partial relaxation via filtered views (e.g. ``m.variables.integers.relax()``). * Semi-continuous variables on solvers that support them. -* SOS1 / SOS2 reformulations for solvers without native SOS support. ``Model.solve(reformulate_sos="auto")`` applies the reformulation only when the chosen solver lacks SOS. *Model* * ``Model.copy()`` (default deep) with ``deep`` and ``include_solution`` options; supports ``copy.copy`` / ``copy.deepcopy``. +* SOS1 / SOS2 reformulations for solvers without native SOS support. ``Model.solve(reformulate_sos="auto")`` applies the reformulation only when the chosen solver lacks SOS. * ``format_labels()`` (on ``Constraints``/``Variables``) and ``format_infeasibilities()`` (on ``Model``) return strings instead of printing. Deprecates ``print_labels()`` / ``print_infeasibilities()``. * Harmonized coordinate alignment between subset/superset operands: multiplication/division fill missing coords with 0; constant ± fills with 0 and pins to LHS coords; comparisons fill missing RHS coords with NaN. Fixes ``subset + var`` reverse-addition and superset DataArrays expanding result coords. From 2e3f27a336b59195c008105cb4eac4d3db204f74 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 7 May 2026 19:08:48 +0200 Subject: [PATCH 5/9] docs: split coord alignment into Expressions, move CPLEX to Bug Fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New *Expressions* subsection holds the subset/superset coord harmonization, which was misfiled under *Model*. - CPLEX quality-attribute handling is a fix for crashes on missing attributes, not a new feature — moved to **Bug Fixes**. Co-Authored-By: Claude Opus 4.7 (1M context) --- doc/release_notes.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/release_notes.rst b/doc/release_notes.rst index e65d3da0..8616257e 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -25,6 +25,9 @@ Upcoming Version * ``Model.copy()`` (default deep) with ``deep`` and ``include_solution`` options; supports ``copy.copy`` / ``copy.deepcopy``. * SOS1 / SOS2 reformulations for solvers without native SOS support. ``Model.solve(reformulate_sos="auto")`` applies the reformulation only when the chosen solver lacks SOS. * ``format_labels()`` (on ``Constraints``/``Variables``) and ``format_infeasibilities()`` (on ``Model``) return strings instead of printing. Deprecates ``print_labels()`` / ``print_infeasibilities()``. + +*Expressions* + * Harmonized coordinate alignment between subset/superset operands: multiplication/division fill missing coords with 0; constant ± fills with 0 and pins to LHS coords; comparisons fill missing RHS coords with NaN. Fixes ``subset + var`` reverse-addition and superset DataArrays expanding result coords. *Solvers* @@ -32,7 +35,6 @@ Upcoming Version * ``Model.solve()`` forwards ``solver_name`` and ``**solver_options`` to the OETC handler (call-level overrides settings-level defaults). * ``OetcSettings.from_env()`` reads OETC settings from ``OETC_*`` environment variables. * SCIP supports quadratic problems on Windows. -* CPLEX safely skips quality attributes that aren't always available (e.g. ``max_dual_infeasibility`` without crossover). **Performance** @@ -44,6 +46,7 @@ Upcoming Version * ``add_variables`` no longer ignores ``coords`` when ``lower``/``upper`` are DataArrays. * ``as_dataarray`` no longer treats MultiIndex level names as extra dimensions when broadcasting a scalar against ``xarray.Coordinates``. * ``Model.to_netcdf`` now works on the scipy netCDF backend (MultiIndex level names serialized as JSON; old files remain readable). +* CPLEX no longer errors on quality attributes that aren't always available (e.g. ``max_dual_infeasibility`` when a barrier solution has no crossover). **Breaking Changes** From f232c46f431bd3dc28cbe5e0fefffde97d4eb0b8 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 7 May 2026 19:11:30 +0200 Subject: [PATCH 6/9] docs: fold as_dataarray MultiIndex fix into add_variables bullet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #659 fixes a regression introduced by #614 in the same release cycle — no end user ever saw the broken state, so a standalone bullet overstates the change. Net behavior is captured by extending the add_variables bullet to mention MultiIndex coords. Co-Authored-By: Claude Opus 4.7 (1M context) --- doc/release_notes.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/release_notes.rst b/doc/release_notes.rst index 8616257e..df3e039e 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -43,8 +43,7 @@ Upcoming Version **Bug Fixes** * ``Model.solve()`` raises a clear ``ValueError`` when no objective is set, instead of writing a malformed LP file. -* ``add_variables`` no longer ignores ``coords`` when ``lower``/``upper`` are DataArrays. -* ``as_dataarray`` no longer treats MultiIndex level names as extra dimensions when broadcasting a scalar against ``xarray.Coordinates``. +* ``add_variables`` no longer ignores ``coords`` when ``lower``/``upper`` are DataArrays, and now handles MultiIndex coords correctly when broadcasting scalar bounds. * ``Model.to_netcdf`` now works on the scipy netCDF backend (MultiIndex level names serialized as JSON; old files remain readable). * CPLEX no longer errors on quality attributes that aren't always available (e.g. ``max_dual_infeasibility`` when a barrier solution has no crossover). From ce7c6c825051aebebe888c991ed3e4126e22892a Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 7 May 2026 19:41:51 +0200 Subject: [PATCH 7/9] docs: tighter pass on upcoming changelog Drop implementation details that belong in API docs (numpy-vs-pandas note, JSON encoding for netCDF, "with no auxiliary variables" piecewise detail), merge the two OETC bullets, and trim "Add X. Supports Y." wrappers across most lines. Co-Authored-By: Claude Opus 4.7 (1M context) --- doc/release_notes.rst | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/doc/release_notes.rst b/doc/release_notes.rst index df3e039e..702b1966 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -8,44 +8,43 @@ Upcoming Version *Piecewise linear constraints (new)* -* ``Model.add_piecewise_formulation()`` for piecewise linear constraints with SOS2, incremental, and disjunctive formulations: ``m.add_piecewise_formulation((power, x_pts), (fuel, y_pts))``. Supports N-variable linking (e.g. CHP with fuel/power/heat), per-entity breakpoints, and ``method="auto"`` to pick the cheapest correct formulation. The API is marked with :class:`linopy.EvolvingAPIWarning` while it stabilises. -* One-sided bounds via a per-tuple sign — append ``"<="`` or ``">="`` (e.g. ``(fuel, y_pts, "<=")``). On matching convex/concave curves, ``method="auto"`` dispatches to a pure-LP chord formulation with no auxiliary variables. -* Unit-commitment gating via the ``active`` parameter: a binary variable that, when zero, forces all auxiliary variables to zero. -* ``PiecewiseFormulation`` exposes ``.method`` and ``.convexity``; both persist across netCDF round-trip. -* Construction helpers: ``linopy.breakpoints()`` for points-based breakpoints, ``linopy.segments()`` for disjunctive operating regions, ``linopy.Slopes`` for specifying a curve by per-piece slopes (with ``align="pieces"``/``"leading"`` for the marginal-cost convention), and ``tangent_lines()`` as a low-level chord helper. +* ``Model.add_piecewise_formulation((power, x_pts), (fuel, y_pts))`` adds piecewise constraints with SOS2, incremental, disjunctive, or pure-LP formulations (``method="auto"``). Supports N-variable linking (e.g. CHP) and per-entity breakpoints; emits :class:`linopy.EvolvingAPIWarning` while the API stabilises. +* One-sided bounds: append ``"<="`` / ``">="`` to a tuple, e.g. ``(fuel, y_pts, "<=")``. On matching convex/concave curves ``method="auto"`` uses a pure-LP chord formulation. +* Unit-commitment gating via ``active``: a binary that zeros all auxiliaries when off. +* ``PiecewiseFormulation`` exposes ``.method`` / ``.convexity`` (persisted across netCDF round-trip). +* Construction helpers: ``linopy.breakpoints()``, ``linopy.segments()``, ``linopy.Slopes`` (per-piece slopes, ``align="pieces"|"leading"``), ``tangent_lines()``. *Variables* -* ``fix()`` / ``unfix()`` / ``fixed`` for fixing variables to values via equality constraints (with auto-rounding for integer/binary variables). -* ``relax()`` / ``unrelax()`` / ``relaxed`` for LP relaxation of integer/binary variables; supports partial relaxation via filtered views (e.g. ``m.variables.integers.relax()``). +* ``fix()`` / ``unfix()`` / ``fixed`` for fixing variables to values via equality constraints (rounds integers/binaries). +* ``relax()`` / ``unrelax()`` / ``relaxed`` for LP relaxation; supports partial relaxation (e.g. ``m.variables.integers.relax()``). * Semi-continuous variables on solvers that support them. *Model* -* ``Model.copy()`` (default deep) with ``deep`` and ``include_solution`` options; supports ``copy.copy`` / ``copy.deepcopy``. -* SOS1 / SOS2 reformulations for solvers without native SOS support. ``Model.solve(reformulate_sos="auto")`` applies the reformulation only when the chosen solver lacks SOS. -* ``format_labels()`` (on ``Constraints``/``Variables``) and ``format_infeasibilities()`` (on ``Model``) return strings instead of printing. Deprecates ``print_labels()`` / ``print_infeasibilities()``. +* ``Model.copy()`` (default deep) with ``deep`` / ``include_solution`` options; works with ``copy.copy`` / ``copy.deepcopy``. +* SOS1 / SOS2 reformulations for solvers without native SOS; ``Model.solve(reformulate_sos="auto")`` applies them only when needed. +* ``format_labels()`` / ``format_infeasibilities()`` return strings instead of printing; deprecates the ``print_*`` siblings. *Expressions* -* Harmonized coordinate alignment between subset/superset operands: multiplication/division fill missing coords with 0; constant ± fills with 0 and pins to LHS coords; comparisons fill missing RHS coords with NaN. Fixes ``subset + var`` reverse-addition and superset DataArrays expanding result coords. +* Coordinate alignment between subset/superset operands: ``*`` / ``/`` fill with 0, constant ``±`` fills with 0 and pins to LHS coords, comparisons fill RHS with NaN. Fixes ``subset + var`` reverse-addition and result coords expanding past the variable's space. *Solvers* -* ``Model.solve()`` forwards ``solver_name`` and ``**solver_options`` to the OETC handler (call-level overrides settings-level defaults). -* ``OetcSettings.from_env()`` reads OETC settings from ``OETC_*`` environment variables. +* OETC: ``Model.solve()`` forwards ``solver_name`` / ``**solver_options`` to the handler; ``OetcSettings.from_env()`` reads ``OETC_*``. * SCIP supports quadratic problems on Windows. **Performance** -* Faster solution unpacking in ``Model.solve()`` via direct numpy indexing instead of pandas ``Series.loc``. +* Faster solution unpacking in ``Model.solve()``. **Bug Fixes** -* ``Model.solve()`` raises a clear ``ValueError`` when no objective is set, instead of writing a malformed LP file. -* ``add_variables`` no longer ignores ``coords`` when ``lower``/``upper`` are DataArrays, and now handles MultiIndex coords correctly when broadcasting scalar bounds. -* ``Model.to_netcdf`` now works on the scipy netCDF backend (MultiIndex level names serialized as JSON; old files remain readable). -* CPLEX no longer errors on quality attributes that aren't always available (e.g. ``max_dual_infeasibility`` when a barrier solution has no crossover). +* ``Model.solve()`` raises a clear ``ValueError`` when no objective is set. +* ``add_variables`` no longer ignores ``coords`` when ``lower`` / ``upper`` are DataArrays, and handles MultiIndex coords correctly with scalar bounds. +* ``Model.to_netcdf`` works on the scipy netCDF backend (old files remain readable). +* CPLEX no longer errors on missing quality attributes (e.g. ``max_dual_infeasibility`` without crossover). **Breaking Changes** From 2fa20ae794e94a71adb1815e4df616790fad023e Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 7 May 2026 19:45:39 +0200 Subject: [PATCH 8/9] docs: rephrase active gating bullet to avoid output-zeroing implication Previous wording ("zeros all auxiliaries when off") was true at the auxiliary level but glossed over the bounded-tuple case where the output is not automatically pinned to 0. Drop the implication and defer the detail to the docstring. Co-Authored-By: Claude Opus 4.7 (1M context) --- doc/release_notes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/release_notes.rst b/doc/release_notes.rst index 702b1966..ff2d0924 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -10,7 +10,7 @@ Upcoming Version * ``Model.add_piecewise_formulation((power, x_pts), (fuel, y_pts))`` adds piecewise constraints with SOS2, incremental, disjunctive, or pure-LP formulations (``method="auto"``). Supports N-variable linking (e.g. CHP) and per-entity breakpoints; emits :class:`linopy.EvolvingAPIWarning` while the API stabilises. * One-sided bounds: append ``"<="`` / ``">="`` to a tuple, e.g. ``(fuel, y_pts, "<=")``. On matching convex/concave curves ``method="auto"`` uses a pure-LP chord formulation. -* Unit-commitment gating via ``active``: a binary that zeros all auxiliaries when off. +* Unit-commitment gating via ``active``: when zero, deactivates the piecewise relation. * ``PiecewiseFormulation`` exposes ``.method`` / ``.convexity`` (persisted across netCDF round-trip). * Construction helpers: ``linopy.breakpoints()``, ``linopy.segments()``, ``linopy.Slopes`` (per-piece slopes, ``align="pieces"|"leading"``), ``tangent_lines()``. From e39270d12da7e1ea8e02462f783f4a82816661eb Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 7 May 2026 20:02:18 +0200 Subject: [PATCH 9/9] docs: drop option-name detail from upcoming changelog Trim references to specific kwargs/attributes the reader doesn't need in the high-level summary: method="auto" parens, align="pieces|leading", deep / include_solution, reformulate_sos="auto", solver_name / **solver_options, max_dual_infeasibility example, and the operator-by-operator coord-alignment breakdown. Co-Authored-By: Claude Opus 4.7 (1M context) --- doc/release_notes.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/release_notes.rst b/doc/release_notes.rst index ff2d0924..81141551 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -8,11 +8,11 @@ Upcoming Version *Piecewise linear constraints (new)* -* ``Model.add_piecewise_formulation((power, x_pts), (fuel, y_pts))`` adds piecewise constraints with SOS2, incremental, disjunctive, or pure-LP formulations (``method="auto"``). Supports N-variable linking (e.g. CHP) and per-entity breakpoints; emits :class:`linopy.EvolvingAPIWarning` while the API stabilises. -* One-sided bounds: append ``"<="`` / ``">="`` to a tuple, e.g. ``(fuel, y_pts, "<=")``. On matching convex/concave curves ``method="auto"`` uses a pure-LP chord formulation. +* ``Model.add_piecewise_formulation((power, x_pts), (fuel, y_pts))`` adds piecewise constraints with SOS2, incremental, disjunctive, or pure-LP formulations and automatic method dispatch. Supports N-variable linking (e.g. CHP) and per-entity breakpoints; emits :class:`linopy.EvolvingAPIWarning` while the API stabilises. +* One-sided bounds: append ``"<="`` / ``">="`` to a tuple, e.g. ``(fuel, y_pts, "<=")``. On matching convex/concave curves this dispatches to a pure-LP chord formulation. * Unit-commitment gating via ``active``: when zero, deactivates the piecewise relation. * ``PiecewiseFormulation`` exposes ``.method`` / ``.convexity`` (persisted across netCDF round-trip). -* Construction helpers: ``linopy.breakpoints()``, ``linopy.segments()``, ``linopy.Slopes`` (per-piece slopes, ``align="pieces"|"leading"``), ``tangent_lines()``. +* Construction helpers: ``linopy.breakpoints()``, ``linopy.segments()``, ``linopy.Slopes`` for per-piece slopes, and ``tangent_lines()``. *Variables* @@ -22,17 +22,17 @@ Upcoming Version *Model* -* ``Model.copy()`` (default deep) with ``deep`` / ``include_solution`` options; works with ``copy.copy`` / ``copy.deepcopy``. -* SOS1 / SOS2 reformulations for solvers without native SOS; ``Model.solve(reformulate_sos="auto")`` applies them only when needed. +* ``Model.copy()`` for a deep copy of a model, optionally including the solution; supports the ``copy`` protocol. +* SOS1 / SOS2 reformulations for solvers without native SOS, applied automatically by ``Model.solve()`` when needed. * ``format_labels()`` / ``format_infeasibilities()`` return strings instead of printing; deprecates the ``print_*`` siblings. *Expressions* -* Coordinate alignment between subset/superset operands: ``*`` / ``/`` fill with 0, constant ``±`` fills with 0 and pins to LHS coords, comparisons fill RHS with NaN. Fixes ``subset + var`` reverse-addition and result coords expanding past the variable's space. +* Coordinate alignment between subset/superset operands: missing coords fill with 0 in arithmetic and NaN in comparisons. Fixes ``subset + var`` reverse-addition and result coords expanding past the variable's space. *Solvers* -* OETC: ``Model.solve()`` forwards ``solver_name`` / ``**solver_options`` to the handler; ``OetcSettings.from_env()`` reads ``OETC_*``. +* OETC: ``Model.solve()`` forwards solver options to the handler; ``OetcSettings.from_env()`` reads ``OETC_*``. * SCIP supports quadratic problems on Windows. **Performance** @@ -44,7 +44,7 @@ Upcoming Version * ``Model.solve()`` raises a clear ``ValueError`` when no objective is set. * ``add_variables`` no longer ignores ``coords`` when ``lower`` / ``upper`` are DataArrays, and handles MultiIndex coords correctly with scalar bounds. * ``Model.to_netcdf`` works on the scipy netCDF backend (old files remain readable). -* CPLEX no longer errors on missing quality attributes (e.g. ``max_dual_infeasibility`` without crossover). +* CPLEX no longer errors on quality attributes that aren't always available. **Breaking Changes**