Skip to content

Commit 2d47d4a

Browse files
fix(variables): Variable.where works with solution attached (#790)
* fix(variables): Variable.where works with solution attached Variable.where passed a fill dict covering only labels/lower/upper to Dataset.where, which requires an exact match against the dataset's data variables. After solve() or fix() the variable carries extra columns (solution, stashed bounds), so where() raised a ValueError. Augment the fill dict to cover all present data variables, defaulting extras to NaN. * fix(variables): annotate where fill dict for mypy
1 parent d810de7 commit 2d47d4a

3 files changed

Lines changed: 13 additions & 0 deletions

File tree

doc/release_notes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Upcoming Version
3030

3131
* LP file export now honors bounds tightened below ``[0, 1]`` on a binary variable via the ``.lower``/``.upper`` setters after creation (e.g. ``upper = 0``). Previously such bounds were written only by ``io_api="direct"`` and dropped by ``io_api="lp"``. (https://github.com/PyPSA/linopy/issues/776)
3232
* Freezing an empty constraint group (e.g. an empty ``isel`` slice) no longer raises ``ValueError: cannot reshape array of size 0``. ``Model(freeze_constraints=True)`` and ``Constraint.freeze()`` now round-trip zero-row constraints losslessly.
33+
* ``Variable.where`` no longer raises ``ValueError: exact match required for all data variable names`` once a solution is attached (after ``Model.solve``) or the variable is fixed. The fill value now covers auxiliary data variables (``solution``, stashed bounds) instead of only ``labels``/``lower``/``upper``.
3334

3435
Version 0.8.0
3536
-------------

linopy/variables.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1223,6 +1223,9 @@ def where(
12231223
raise ValueError(
12241224
f"other must be a Variable, ScalarVariable, dict or Dataset, got {type(other)}"
12251225
)
1226+
if isinstance(_other, dict):
1227+
fill: dict[str, float] = {str(k): np.nan for k in self.data}
1228+
_other = {**fill, **_other}
12261229
return self.__class__(
12271230
self.data.where(cond, _other, **kwargs), self.model, self.name
12281231
)

test/test_variable.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,15 @@ def test_variable_where(x: linopy.Variable) -> None:
276276
x.where([True] * 4 + [False] * 6, 0) # type: ignore
277277

278278

279+
def test_variable_where_with_solution(x: linopy.Variable) -> None:
280+
x.solution = xr.DataArray(np.arange(10.0), coords=x.labels.coords)
281+
cond = [True] * 4 + [False] * 6
282+
filtered = x.where(cond)
283+
assert filtered.labels[9] == x._fill_value["labels"]
284+
assert filtered.data["solution"][0] == 0.0
285+
assert np.isnan(filtered.data["solution"][9])
286+
287+
279288
def test_variable_shift(x: linopy.Variable) -> None:
280289
x = x.shift(first=3)
281290
assert isinstance(x, linopy.variables.Variable)

0 commit comments

Comments
 (0)