You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Under linopy.options["semantics"] = "v1", add_piecewise_formulation raises
ValueError: NaN found in a user-supplied constant. linopy treats this as ambiguous: ...
for ragged breakpoints — i.e. entities with different numbers of breakpoints, NaN-padded along _breakpoint. That padding is linopy's own ragged representation: _pad_ragged builds it (np.full((len(keys), max_len), np.nan)), and _strip_nan / _has_trailing_nan_only consume it. So the NaN is structural, not a data error.
The formulation code already knows which pieces are padding — _add_incremental computes a piece_mask from bp_valid (piecewise.py:1930-1932) — but the arithmetic that trips the v1 NaN guard runs before that mask is applied:
_incremental_weighted (piecewise.py:1806) evaluates (delta_var * steps).sum(dim=piece_dim) + bp0_term, where steps are consecutive breakpoint differences and so are NaN in the padded slot.
_tangent_lines_impl (piecewise.py:769, called from _add_incremental at :1937) evaluates slopes * _to_linexpr(x) + intercepts with NaN intercepts from the same padding.
Under legacy this is silently tolerated, and linopy's own deprecation notice points straight at the site:
piecewise.py:1806: LinopySemanticsWarning: NaN in the multiplicative factor was silently
treated as 0 by legacy (so the variable was zeroed out at that slot). Under v1 this raises
ValueError.
return (delta_var * steps).sum(dim=piece_dim) + bp0_term
All three formulation paths are affected (lp, incremental, and incremental with active=), so it is not specific to a method or to unit-commitment gating.
This blocks PyPSA's v1 migration (PyPSA/PyPSA#1829): piecewise cost/efficiency curves are naturally ragged across components, and the two remaining v1 test failures there both land in these two functions.
legacy | lp (convex) | OK
legacy | incremental (concave) | OK
legacy | incremental + active | OK
v1 | lp (convex) | ValueError
v1 | incremental (concave) | ValueError
v1 | incremental + active | ValueError
Expected Behavior
All six rows print OK. NaN padding along _breakpoint marks absent pieces, which linopy itself created and already detects via bp_valid / piece_mask — it should not be reported to the user as an ambiguous NaN constant.
Applying the existing piece_mask (or resolving the padded slots) before the arithmetic in _incremental_weighted and _tangent_lines_impl would fix both sites. If instead ragged input is meant to be rejected under v1, a dedicated error naming the ragged entity would be far easier to act on than the generic NaN-constant message, which gives no hint that padding is the cause.
likely the last issue I found for fully integrating v1 into pypsa
Note
The content of this issue was generated with AI assistance.
Version Checks
semanticsoption does not exist there.feat/arithmetic-conventionbranch (34694331).Issue Description
Under
linopy.options["semantics"] = "v1",add_piecewise_formulationraisesfor ragged breakpoints — i.e. entities with different numbers of breakpoints, NaN-padded along
_breakpoint. That padding is linopy's own ragged representation:_pad_raggedbuilds it (np.full((len(keys), max_len), np.nan)), and_strip_nan/_has_trailing_nan_onlyconsume it. So the NaN is structural, not a data error.The formulation code already knows which pieces are padding —
_add_incrementalcomputes apiece_maskfrombp_valid(piecewise.py:1930-1932) — but the arithmetic that trips the v1 NaN guard runs before that mask is applied:_incremental_weighted(piecewise.py:1806) evaluates(delta_var * steps).sum(dim=piece_dim) + bp0_term, wherestepsare consecutive breakpoint differences and so are NaN in the padded slot._tangent_lines_impl(piecewise.py:769, called from_add_incrementalat:1937) evaluatesslopes * _to_linexpr(x) + interceptswith NaNinterceptsfrom the same padding.Under legacy this is silently tolerated, and linopy's own deprecation notice points straight at the site:
All three formulation paths are affected (
lp,incremental, andincrementalwithactive=), so it is not specific to a method or to unit-commitment gating.This blocks PyPSA's v1 migration (PyPSA/PyPSA#1829): piecewise cost/efficiency curves are naturally ragged across components, and the two remaining v1 test failures there both land in these two functions.
Reproducible Example
Output:
Expected Behavior
All six rows print
OK. NaN padding along_breakpointmarks absent pieces, which linopy itself created and already detects viabp_valid/piece_mask— it should not be reported to the user as an ambiguous NaN constant.Applying the existing
piece_mask(or resolving the padded slots) before the arithmetic in_incremental_weightedand_tangent_lines_implwould fix both sites. If instead ragged input is meant to be rejected under v1, a dedicated error naming the ragged entity would be far easier to act on than the generic NaN-constant message, which gives no hint that padding is the cause.Installed Versions
Details