Skip to content

feat: Improve numerical conditioning#23

Merged
koen-vg merged 3 commits into
Sustainable-Solutions-Lab:mainfrom
koen-vg:improve-numerical-conditioning
Jul 3, 2026
Merged

feat: Improve numerical conditioning#23
koen-vg merged 3 commits into
Sustainable-Solutions-Lab:mainfrom
koen-vg:improve-numerical-conditioning

Conversation

@koen-vg

@koen-vg koen-vg commented Jul 3, 2026

Copy link
Copy Markdown
Member

Improve numerical conditioning, reformulate L1 deviation costs for better presolve, and update PyPSA to include model building performance fix.

koen-vg added 3 commits July 3, 2026 10:52
… clipping)

Remove Gurobi's "large matrix coefficient range" warning on the food-system
model by tightening two sources of extreme coefficients:

- Denominate the CH4 and N2O emission buses in kilotonnes instead of tonnes,
  so their flow coefficients (rice/enteric methane, fertilizer N2O) sit within
  a few orders of the MtCO2 bus. Updates the source-link writers, the GWP
  aggregation links, the analysis converters, and the carrier unit labels.
- Add a `numerics` config block and a build-time clip pass
  (clip_negligible_coefficients) that zeroes physically-negligible
  coefficients: sub-hectare land areas (the source of ~1e-21 bounds/RHS),
  trace irrigation water, near-zero spared-land carbon fluxes, and
  rounding-level cost corrections. The former `land.filtering` thresholds are
  folded into `numerics` so all build-time conditioning knobs live together.

Central A/B (barrier + crossover): matrix range [6e-08, 1e+05] -> [5e-06,
3e+04], warning gone, solver work units 24.9 -> 22.3 (~10%); objective
unchanged to 1.5e-4. Emission totals are identical. Calibration provenance
stamps are refreshed (artefacts are inert to the change: calibration solves
run GHG-off and the pruning thresholds keep their values).
The L1 stability penalties (crop, grassland, animal feed, diet) encoded
|deviation| with an auxiliary abs_dev variable and two inequality rows
per link (abs_dev >= +dev, abs_dev >= -dev). Gurobi's presolve cannot
fold that pair, so on a full-resolution health solve the stability
machinery accounted for 150k of 368k presolved rows.

Encode instead dev_pos - dev_neg == deviation with dev_pos, dev_neg >= 0
and price their sum: one equality row per link, same optimum for any
positive L1 cost. The land-conversion penalty (zero baseline,
non-negative flows, |p - 0| = p) is priced directly on the link flows
with no auxiliary variables at all.

On the central health-on config this cuts presolved rows 368k -> 212k
and total solver work 41 -> 28 work units (-33%); objectives agree to
within the 0.1% MIP gap and calibrated L1 costs remain valid. The
objective-breakdown bookkeeping in core.py reads dev_pos + dev_neg
(and link flows for land conversion) in place of abs_dev.
Picks up the balance-arg grouping fix in _iter_balance_args: grouping on
a two-column delay/cyclic frame instead of a deep copy of the full wide
static frame per output port. create_model on the full-resolution model
drops from ~13.4s to ~8.7s; the constructed model is verified
element-for-element identical.
@koen-vg koen-vg merged commit 335faee into Sustainable-Solutions-Lab:main Jul 3, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant