What the open model is given
DNA-PyPSA
The study verified its main model, cGrid, independently with PyPSA, an open-source framework for optimising power systems. The PyPSA model built for the study is open source, for transparency and for work beyond the report.
These two explorers show that model’s own files: what it is given, and what it returns. Every number links to the file and line it comes from.
- Zones
- 17 bidding zones. Capacity is optimised in DK1 and DK2 only; the other 15 zones have fixed fleets.
- Weather
- One weather year, 1991, in 8,760 hourly steps (timestamped 2050).
- Method
- Capacity expansion, then dispatch adjusted step by step until prices and capacities settle (“iterate” mode).
- Licence
- MIT: free to use, change and share.
f0f1761 (2026-07-02) · the repository opens to the public with the study · §3.1.2 PyPSA – open source (p.24)What Denmark can build
The generators in DK1 and DK2, with the costs the model weighs them by. For each expandable technology the model chooses the capacity, up to the upper limit; the others are fixed. Everything outside Denmark is fixed too.
| Technology | Zone | Capacity MW | Upper limit MW | Capital cost €/kW/yr | Fixed O&M €/kW/yr | Variable cost €/MWh | Mean availability | Source |
|---|---|---|---|---|---|---|---|---|
| Nuclearnuclear_new_DK1 | DK1 | chosen by the model | 2,000 | 486.5 | 100.5 | -2.1 | 90.0 % | generators.csv, line 55 ↗ |
| Nuclearnuclear_new_DK2 | DK2 | chosen by the model | 2,000 | 486.5 | 100.5 | -2.1 | 90.0 % | generators.csv, line 56 ↗ |
| Biomassbiomass_DK1 | DK1 | 255 | fixed | 0.0 | 0.0 | 38.2 | 74.8 % | generators.csv, line 59 ↗ |
| Biomassbiomass_DK2 | DK2 | 446 | fixed | 0.0 | 0.0 | 38.2 | 66.3 % | generators.csv, line 60 ↗ |
| Wastewaste_DK1 | DK1 | 55 | fixed | 0.0 | 0.0 | 0.0 | 100.0 % | generators.csv, line 57 ↗ |
| Wastewaste_DK2 | DK2 | 41 | fixed | 0.0 | 0.0 | 0.0 | 100.0 % | generators.csv, line 58 ↗ |
| Wind offshore nearshorewind_offshore_new_DK1 | DK1 | chosen by the model | 4,400 | 182.3 | 30.0 | 3.0 | 52.1 % | generators.csv, line 61 ↗ |
| Wind offshore nearshorewind_offshore_new_DK2 | DK2 | chosen by the model | 6,700 | 182.3 | 30.0 | 3.0 | 46.6 % | generators.csv, line 63 ↗ |
| Wind offshorewind_offshore_farshore_new_DK1 | DK1 | chosen by the model | 23,000 | 227.8 | 30.0 | 3.0 | 52.1 % | generators.csv, line 62 ↗ |
| Wind offshorewind_offshore_farshore_new_DK2 | DK2 | chosen by the model | 1,600 | 227.8 | 30.0 | 3.0 | 46.6 % | generators.csv, line 64 ↗ |
| Wind onshorewind_onshore_new_DK1 | DK1 | 6,514 | fixed | 109.7 | 18.8 | 2.2 | 38.2 % | generators.csv, line 65 ↗ |
| Wind onshorewind_onshore_new_DK2 | DK2 | 609 | fixed | 109.7 | 18.8 | 2.2 | 35.9 % | generators.csv, line 66 ↗ |
| Solar utilitysolar_new_DK1 | DK1 | chosen by the model | 50,000 | 30.9 | 13.2 | 0.0 | 11.4 % | generators.csv, line 69 ↗ |
| Solar utilitysolar_new_DK2 | DK2 | chosen by the model | 50,000 | 30.9 | 13.2 | 0.0 | 11.4 % | generators.csv, line 70 ↗ |
| Solar residentialsolar_existing_DK1 | DK1 | 4,959 | fixed | 0.0 | 0.0 | 0.0 | 11.4 % | generators.csv, line 67 ↗ |
| Solar residentialsolar_existing_DK2 | DK2 | 2,652 | fixed | 0.0 | 0.0 | 0.0 | 11.4 % | generators.csv, line 68 ↗ |
As stored in the network files. For expandable technologies the file also holds a p_nom, which the capacity-expansion step does not use: it chooses the capacity itself. Capital cost is the annualised fixed cost the expansion step weighs (capital_cost); fixed O&M (fom_cost) and variable cost (marginal_cost) as given. For dispatchable plant the variable cost rises with output (marginal_cost_quadratic), a fleet of units of varying cost. Mean availability is the average of the hourly availability profile; wind and solar must produce exactly what the weather allows. Model names are DNA-PyPSA’s; technology names follow the report.
Weather and demand, hour by hour
Wind, sun and temperature-driven demand all come from one weather year, 1991, chosen because its price volatility and capture rates are close to the long-run average of the 33 years in the dataset. Demand peaks and troughs therefore line up with the wind and sun the way they did that year.
Flexibility in Danish demand
Not all demand is fixed. Flexible demand has a buffer: it can buy electricity ahead, in cheaper hours, and hold it until it is needed, but it cannot postpone what it needs. Price-sensitive demand is only served while the price is below what it is willing to pay, and that willingness falls the more of it is served.
| Demand | Zone | Max draw MW | Buffer MWh | Charge on all its energy €/MWh | Source |
|---|---|---|---|---|---|
| Residential & commercial | DK1 | 121 | 483 | 100 | links.csv, line 32 ↗ stores.csv, line 24 ↗ |
| Residential & commercial | DK2 | 88 | 353 | 100 | links.csv, line 33 ↗ stores.csv, line 25 ↗ |
| Electric vehicles | DK1 | 499 | 11,963 | 100 | links.csv, line 34 ↗ stores.csv, line 26 ↗ |
| Electric vehicles | DK2 | 368 | 8,822 | 100 | links.csv, line 35 ↗ stores.csv, line 27 ↗ |
| Industry | DK1 | 173 | 691 | 100 | links.csv, line 36 ↗ stores.csv, line 28 ↗ |
| Industry | DK2 | 101 | 404 | 100 | links.csv, line 37 ↗ stores.csv, line 29 ↗ |
| Electrolysers | DK1 | 1,593 | 458,745 | 50 | links.csv, line 38 ↗ stores.csv, line 30 ↗ |
| Electrolysers | DK2 | 234 | 67,283 | 50 | links.csv, line 39 ↗ stores.csv, line 31 ↗ |
| Hot water | DK1 | 158 | 1,899 | 50 | links.csv, line 40 ↗ stores.csv, line 32 ↗ |
| Space heating | DK1 | 888 | 3,551 | 50 | links.csv, line 41 ↗ stores.csv, line 33 ↗ |
| Hot water | DK2 | 136 | 1,628 | 50 | links.csv, line 42 ↗ stores.csv, line 34 ↗ |
| Space heating | DK2 | 764 | 3,055 | 50 | links.csv, line 43 ↗ stores.csv, line 35 ↗ |
Max draw: the most the load can take from the grid in an hour, to use and to fill its buffer. Buffer: how much it can hold; it starts empty, so demand can be brought forward but not postponed. The charge applies to every MWh the load draws, so it comes to the same total whether or not demand is moved.
| Demand | Zone | Up to MW | Pays up to €/MWh | At most TWh | Source |
|---|---|---|---|---|---|
| Residential & commercial | DK1 | 229 | 1,000 | 1.46 | generators.csv, line 155 ↗ generators-p_max_pu.csv ↗ |
| Residential & commercial | DK2 | 168 | 1,000 | 0.86 | generators.csv, line 156 ↗ generators-p_max_pu.csv ↗ |
| Industry | DK1 | 207 | 1,000 | 1.82 | generators.csv, line 157 ↗ generators-p_max_pu.csv ↗ |
| Industry | DK2 | 121 | 1,000 | 1.06 | generators.csv, line 158 ↗ generators-p_max_pu.csv ↗ |
| Electrolysers | DK2 | 3 | 200 | 0.03 | generators.csv, line 159 ↗ generators-p_max_pu.csv ↗ |
| Hot water | DK1 | 16 | 1,000 | 0.09 | generators.csv, line 160 ↗ generators-p_max_pu.csv ↗ |
| Space heating | DK1 | 89 | 1,000 | 0.31 | generators.csv, line 161 ↗ generators-p_max_pu.csv ↗ |
| Hot water | DK2 | 14 | 1,000 | 0.08 | generators.csv, line 162 ↗ generators-p_max_pu.csv ↗ |
| Space heating | DK2 | 76 | 1,000 | 0.27 | generators.csv, line 163 ↗ generators-p_max_pu.csv ↗ |
Pays up to: what the first MW is willing to pay; the willingness falls in a straight line as more is served (the quadratic cost term), to about 100 €/MWh at full size for the 1,000 €/MWh rows. At most: the most it can consume over the year, from its hourly limits.
| Zone | Cost €/kWh | Round-trip | Lifetime yr | Discount rate | Source |
|---|---|---|---|---|---|
| DK1 | 191 | 81 % | 15 | 8 % | stores.csv, line 54 ↗ |
| DK2 | 191 | 81 % | 15 | 8 % | stores.csv, line 55 ↗ |
Overnight cost of storage; 90 % each way. The network as published carries no battery capacity in any scenario.
Connections to the neighbours
Transmission between zones is fixed, at capacities based on ENTSO-E plans; the model does not expand it. Each border is two one-way links that lose 5 % of what they carry.
| Border | Out of the Danish zone MW | Into the Danish zone MW | Efficiency | Sources |
|---|---|---|---|---|
| DK1 – DE Germany | 3,500 | 3,500 | 95 % | links.csv, line 132 ↗ links.csv, line 133 ↗ |
| DK1 – NO125 Norway South/West | 1,730 | 1,730 | 95 % | links.csv, line 125 ↗ links.csv, line 124 ↗ |
| DK1 – GB Great Britain | 1,400 | 1,400 | 95 % | links.csv, line 154 ↗ links.csv, line 155 ↗ |
| DK1 – SE3 Sweden South-Central | 715 | 715 | 95 % | links.csv, line 123 ↗ links.csv, line 122 ↗ |
| DK1 – NL Netherlands | 700 | 700 | 95 % | links.csv, line 148 ↗ links.csv, line 149 ↗ |
| DK1 – DK2 Denmark East | 600 | 600 | 95 % | links.csv, line 126 ↗ links.csv, line 127 ↗ |
| DK2 – SE4 Sweden South | 1,700 | 1,700 | 95 % | links.csv, line 121 ↗ links.csv, line 120 ↗ |
| DK2 – DE Germany | 1,000 | 1,000 | 95 % | links.csv, line 130 ↗ links.csv, line 131 ↗ |
The system around Denmark
The other 15 zones have fixed fleets, set by a separate optimisation of the European power system with the Balanced scenario as reference. They give Denmark realistic prices and trade at its borders without being optimised alongside it, since national energy policies are in practice made independently.
Excludes hydropower, modelled through reservoirs and inflows rather than as a fleet of generators. generators.csv ↗
How the run is set up, and how to repeat it
| Mode | iterate capacity expansion, then iterative dispatch and capacity adjustment |
| Pre-solve | island |
| Price ceiling in Denmark | 3,000 €/MWh |
| Price ceiling in importing zones | 600 €/MWh |
| Solver used | gurobi HiGHS, which is free, also works |
reference_scenario.json ↗ · network files ↗ · hourly profiles ↗ · run modes ↗
# get the model and its inputs git clone https://github.com/QuantifiedCarbon/DNA-PyPSA.git cd DNA-PyPSA && pip install . # solve the Balanced scenario python scripts/run_full_scenarios.py --scenario reference
Results are written as CSV and netCDF and can be compared with the published ones in publication_results/reference ↗.