Skip to content

Commit 8de5277

Browse files
FBumannclaude
andcommitted
refac: store names in PiecewiseFormulation, add IO persistence
PiecewiseFormulation now stores variable/constraint names as strings with a model reference. Properties return live Views on access. This makes serialization trivial — persist as JSON in netcdf attrs, reconstruct on load. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 09f63a8 commit 8de5277

File tree

3 files changed

+49
-11
lines changed

3 files changed

+49
-11
lines changed

linopy/io.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,6 +1147,11 @@ def with_prefix(ds: xr.Dataset, prefix: str) -> xr.Dataset:
11471147
ds = ds.assign_attrs(scalars)
11481148
if m._relaxed_registry:
11491149
ds.attrs["_relaxed_registry"] = json.dumps(m._relaxed_registry)
1150+
if m._piecewise_formulations:
1151+
ds.attrs["_piecewise_formulations"] = json.dumps(
1152+
{name: {"method": pwl.method, "variables": pwl.variable_names, "constraints": pwl.constraint_names}
1153+
for name, pwl in m._piecewise_formulations.items()}
1154+
)
11501155
ds.attrs = non_bool_dict(ds.attrs)
11511156

11521157
for k in ds:
@@ -1244,6 +1249,18 @@ def get_prefix(ds: xr.Dataset, prefix: str) -> xr.Dataset:
12441249
if "_relaxed_registry" in ds.attrs:
12451250
m._relaxed_registry = json.loads(ds.attrs["_relaxed_registry"])
12461251

1252+
if "_piecewise_formulations" in ds.attrs:
1253+
from linopy.piecewise import PiecewiseFormulation
1254+
1255+
for name, d in json.loads(ds.attrs["_piecewise_formulations"]).items():
1256+
m._piecewise_formulations[name] = PiecewiseFormulation(
1257+
name=name,
1258+
method=d["method"],
1259+
variable_names=d["variables"],
1260+
constraint_names=d["constraints"],
1261+
model=m,
1262+
)
1263+
12471264
return m
12481265

12491266

linopy/model.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -533,8 +533,8 @@ def _piecewise_names(self) -> set[str]:
533533
"""Return all variable/constraint names belonging to piecewise formulations."""
534534
names: set[str] = set()
535535
for pwl in self._piecewise_formulations.values():
536-
names.update(pwl.variables)
537-
names.update(pwl.constraints)
536+
names.update(pwl.variable_names)
537+
names.update(pwl.constraint_names)
538538
return names
539539

540540
def __getitem__(self, key: str) -> Variable:

linopy/piecewise.py

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from __future__ import annotations
99

1010
from collections.abc import Sequence
11-
from dataclasses import dataclass
1211
from numbers import Real
1312
from typing import TYPE_CHECKING, Literal, TypeAlias
1413

@@ -62,19 +61,40 @@
6261
# ---------------------------------------------------------------------------
6362

6463

65-
@dataclass(repr=False)
6664
class PiecewiseFormulation:
6765
"""
6866
Result of ``add_piecewise_formulation``.
6967
7068
Groups all auxiliary variables and constraints created by a single
71-
piecewise formulation.
69+
piecewise formulation. Stores only names internally; ``variables``
70+
and ``constraints`` properties return live views from the model.
7271
"""
7372

74-
name: str
75-
method: str
76-
variables: Variables
77-
constraints: Constraints
73+
__slots__ = ("name", "method", "variable_names", "constraint_names", "_model")
74+
75+
def __init__(
76+
self,
77+
name: str,
78+
method: str,
79+
variable_names: list[str],
80+
constraint_names: list[str],
81+
model: Model,
82+
) -> None:
83+
self.name = name
84+
self.method = method
85+
self.variable_names = variable_names
86+
self.constraint_names = constraint_names
87+
self._model = model
88+
89+
@property
90+
def variables(self) -> Variables:
91+
"""View of the auxiliary variables in this formulation."""
92+
return self._model.variables[self.variable_names]
93+
94+
@property
95+
def constraints(self) -> Constraints:
96+
"""View of the auxiliary constraints in this formulation."""
97+
return self._model.constraints[self.constraint_names]
7898

7999
def __repr__(self) -> str:
80100
# Collect user-facing dims with sizes (skip internal _ prefixed dims)
@@ -769,8 +789,9 @@ def add_piecewise_formulation(
769789
result = PiecewiseFormulation(
770790
name=name,
771791
method=resolved_method,
772-
variables=model.variables[new_vars],
773-
constraints=model.constraints[new_cons],
792+
variable_names=new_vars,
793+
constraint_names=new_cons,
794+
model=model,
774795
)
775796
model._piecewise_formulations[name] = result
776797
return result

0 commit comments

Comments
 (0)