Overview
A whole energy-system model in one offline page
Every number below was produced by solving a PyPSA/HiGHS linear program in this repository — capacity expansion, multi-duration storage, CO₂ cap-and-trade duality, ramp/reserve adequacy, meshed DC-OPF and a mean-CVaR stochastic program. Move a control and the page re-reads the solved data; nothing is typed by hand and nothing is fetched from the internet.
Cost surface: discount rate × VRE CapEx
Exact LP solves on a 5×5 grid. Click a cell to load that case into the dispatch view.
Module map
Each card is a solved model; select one to explore.
Cross-module invariants
Residuals recomputed from the solved primal/dual pairs at build time; tolerance is the numerical acceptance band used by the test suite.
| Module | Identity | Residual | Tolerance | Status |
|---|
Dispatch, cost stack and the price of capital
A representative day is solved to least cost with extendable solar, wind and gas. The KKT dual of the power balance is the nodal price; its load-weighted mean equals the system LCOE to machine precision. Drag the financial levers to watch the portfolio re-optimise.
Hourly dispatch —
Controls
Cost stack across the surface
Annualised capital versus fuel and variable O&M at the current case.
Hourly prices
Nodal shadow price λt; scarcity hours price gas at its marginal cost plus capacity scarcity.
Batteries, hydrogen and the length of a dark lull
A 168-hour week is solved with a 4-hour Li-ion battery (95% one-way efficiency, cyclic SoC) and a decoupled power-to-hydrogen chain (68% electrolyser, 50% fuel cell, cavern store). The crossover between power-dense and energy-dense storage is a function of how long the wind and sun disappear.
LCOE versus lull duration
Dunkelflaute length
Adequacy regimes (168 h, annualised)
| Regime | Solar MW | Wind MW | BESS MWh | H₂ MWhe | LCOE |
|---|
Chronological dispatch in the lull
Hybrid portfolio over 168 hours; hover to read hour-level detail.
What a tonne of CO₂ costs when the cap binds
Relaxing the annual CO₂ cap with its KKT multiplier reproduces the Pigouvian carbon tax exactly: at the dual price the tax optimum equals the cap optimum plus the value of the allowance. The marginal abatement cost curve is the derivative of the frontier, and its right-hand limit at zero emissions is the minimum net-zero price.
Pareto frontier: emissions versus resource cost
Solid: resource LCOE (left axis). Dashed: the dual CO₂ shadow price (right axis) as the cap tightens.
Abatement target
Marginal abatement cost curve
Step marginal cost of each abatement increment, and the average cost of the whole programme.
Fuel-switching stages
Installed capacity by carrier at the selected cap.
The duck curve is a flexibility problem
Intertemporal ramp envelopes, minimum stable generation and co-optimised upward reserves are added to the capacity problem. Reserve co-optimisation is paid for in the energy market, and a binding downward ramp can push a nodal price below zero even when every fuel cost is non-negative.
Net load and price shape (48 h)
Regime metrics
Reserve requirement vs. price
Reserve requirement αload·Lt + αvre·VREt, and the reserve shadow price.
Regime comparison
| Regime | LCOE | Peak LMP | Reserve price | BESS MWh |
|---|
Loop flows, congestion rent and Braess's paradox
Four zones, five 380 kV corridors and two independent Kirchhoff voltage loops. Passive flows follow the PTDF matrix, zonal prices decompose into a reference price minus congestion terms, and the ISO merchandising surplus recovers exactly the cost of the grid it co-optimises.
Network state hour 0
A zero nodal price means the marginal unit at that zone is non-binding (curtailed VRE sets the price). Drag the snapshot slider to watch congestion appear.
Regime
Congestion rent versus corridor capacity
PTDF matrix
Sensitivity of each corridor flow to a 1 MW injection at a zone (withdrawal at the East Metro slack).
| Corridor |
|---|
Hedging the tail instead of the average
Eight weather-and-demand regimes are solved jointly. The objective is (1−β)·E[Z] + β·CVaR95%(Z) through its Rockafellar–Uryasev epigraph, in one 1,357-variable LP with explicit sparse matrices. The dual risk kernel q* shows exactly which futures the hedge is buying, and where it hits the π/(1−α) bound.
Efficient frontier: expected cost versus tail cost
Risk aversion β
Dual risk kernel q*
Bars: optimal scenario weights q* at the selected β (left axis). Circles: the prior π. Dashed ticks: the theoretical ceiling π/(1−α) that q* reaches at β = 1 (right axis).
Stochastic bounds
Wait-and-see ≤ recourse ≤ expected-value plan evaluated across futures.
How to reproduce every pixel
The page is a build artifact: workbench/data/etm_workbench_data.json holds solved values, and energy_model/workbench.py renders them into this single HTML file. A CI job re-renders the committed page from the committed data and fails on any drift, so the figure you are reading cannot silently diverge from the repository.
Surrogate validation
Exact LP solves at 16 node positions never used for interpolation.
Build provenance
Reproduce locally
python -m venv .venv .venv/bin/pip install --no-build-isolation -e ".[dev]" .venv/bin/etm-workbench build # re-solve + re-render .venv/bin/etm-workbench render # template only .venv/bin/etm-workbench check # verify no drift
Test inventory
| Test module | Tests |
|---|
Scope & honest limits
- Inputs are deterministic synthetic profiles and illustrative annualised costs in an unspecified currency; they are not metered SCADA data or investment quotes.
- Module 7's CVaR panel is an independent explicit-LP re-derivation on its own declared 8-regime set. It is not a numerical replica of the Module 6 run, which remains the reference implementation and CLI.
- The cost surface is exactly solved at 25 nodes and bilinearly interpolated between them; the reported holdout error bounds the interpolation, it does not remove it.
- Single-node copper-plate dispatch in M1–M4 and lossless DC power flow in M5; no AC power flow, unit commitment or integer investment.
Licence MIT · cite via CITATION.cff. Built as the practical implementation of the Politecnico di Milano EQF-7 microcredential “Modelling the Energy Transition: the revolution of open-source software”.