Skip to content

Commit 21e97dc

Browse files
FBumannclaude
andcommitted
fix: align Variable.fix() value to the variable's coordinates
fix() converted the value with as_dataarray().broadcast_like(self.labels), which aligns only by dimension name and so worked solely for the default `dim_0`. On a named dimension, a positional value (list/array) gained a spurious `dim_0` and broadcast across the real dimension instead of onto it, silently building a wrong fix constraint (one fixing every entry to every value). Use broadcast_to_coords against the variable's own coords — the same coords- aware alignment add_variables uses for lower/upper: scalars broadcast, positional inputs land on the right dimension, named pandas/xarray inputs align by coordinate value, and a mismatch raises an error naming the variable. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 7eb8c9a commit 21e97dc

3 files changed

Lines changed: 19 additions & 2 deletions

File tree

doc/release_notes.rst

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

77
* Add documentation about `LinearExpression.where` with `drop=True`. Add `BaseExpression.variable_names` property.
88
* Add ``BaseExpression.has_terms`` property: boolean array, true at slots with at least one live term (`#741 <https://github.com/PyPSA/linopy/issues/741>`_).
9+
* ``Variable.fix(value)`` now places ``value`` correctly on variables with named dimensions; previously array values could be misaligned.
910

1011
**Features**
1112

linopy/variables.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
from xarray.core.utils import Frozen
3232

3333
import linopy.expressions as expressions
34-
from linopy.alignment import as_dataarray, broadcast_to_coords
34+
from linopy.alignment import broadcast_to_coords
3535
from linopy.common import (
3636
LabelPositionIndex,
3737
LocIndexer,
@@ -1368,7 +1368,9 @@ def fix(
13681368
)
13691369
raise ValueError(msg) from None
13701370

1371-
value = as_dataarray(value).broadcast_like(self.labels)
1371+
value = broadcast_to_coords(
1372+
value, self.coords, label=f"fix() for variable '{self.name}'"
1373+
)
13721374

13731375
if self.attrs.get("integer") or self.attrs.get("binary"):
13741376
value = value.round(0)

test/test_fix_relax.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,3 +482,17 @@ def test_unrelax_after_roundtrip(
482482
m2.variables["z"].unrelax()
483483
assert m2.variables["z"].attrs["binary"]
484484
assert "z" not in m2._relaxed_registry
485+
486+
487+
def test_fix_aligns_positional_value_to_named_dimension() -> None:
488+
# fix() delegates alignment to the (separately tested) broadcast_to_coords;
489+
# this only guards that it passes the variable's own coords, so a positional
490+
# value lands on the named dimension instead of gaining a spurious dim_0.
491+
m = Model()
492+
m.add_variables(
493+
lower=-5, upper=5, coords=[pd.Index([2020, 2030, 2040], name="time")], name="t"
494+
)
495+
m.variables["t"].fix([1.0, 2.0, 3.0])
496+
con = m.constraints[f"{FIX_CONSTRAINT_PREFIX}t"]
497+
assert con.rhs.dims == ("time",)
498+
np.testing.assert_array_almost_equal(con.rhs.values, [1.0, 2.0, 3.0])

0 commit comments

Comments
 (0)