Merge branch 'master' into wildcard-opts-config

This commit is contained in:
Fabian Neumann 2024-01-03 13:08:21 +01:00 committed by GitHub
commit 83ecaac4de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
77 changed files with 4198 additions and 767 deletions

View File

@ -83,6 +83,7 @@ jobs:
snakemake -call solve_elec_networks --configfile config/test/config.electricity.yaml --rerun-triggers=mtime
snakemake -call all --configfile config/test/config.overnight.yaml --rerun-triggers=mtime
snakemake -call all --configfile config/test/config.myopic.yaml --rerun-triggers=mtime
snakemake -call all --configfile config/test/config.perfect.yaml --rerun-triggers=mtime
- name: Upload artifacts
uses: actions/upload-artifact@v3

2
.gitignore vendored
View File

@ -8,6 +8,7 @@ __pycache__
*dconf
gurobi.log
.vscode
*.orig
/bak
/resources
@ -45,6 +46,7 @@ data/costs_*.csv
dask-worker-space/
publications.jrc.ec.europa.eu/
d1gam3xoknrgr2.cloudfront.net/
*.org

View File

@ -5,7 +5,7 @@ exclude: "^LICENSES"
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
rev: v4.5.0
hooks:
- id: check-merge-conflict
- id: end-of-file-fixer
@ -17,7 +17,7 @@ repos:
# Sort package imports alphabetically
- repo: https://github.com/PyCQA/isort
rev: 5.12.0
rev: 5.13.2
hooks:
- id: isort
args: ["--profile", "black", "--filter-files"]
@ -30,10 +30,10 @@ repos:
# Find common spelling mistakes in comments and docstrings
- repo: https://github.com/codespell-project/codespell
rev: v2.2.5
rev: v2.2.6
hooks:
- id: codespell
args: ['--ignore-regex="(\b[A-Z]+\b)"', '--ignore-words-list=fom,appartment,bage,ore,setis,tabacco,berfore'] # Ignore capital case words, e.g. country codes
args: ['--ignore-regex="(\b[A-Z]+\b)"', '--ignore-words-list=fom,appartment,bage,ore,setis,tabacco,berfore,vor'] # Ignore capital case words, e.g. country codes
types_or: [python, rst, markdown]
files: ^(scripts|doc)/
@ -45,13 +45,13 @@ repos:
args: ["--in-place", "--make-summary-multi-line", "--pre-summary-newline"]
- repo: https://github.com/keewis/blackdoc
rev: v0.3.8
rev: v0.3.9
hooks:
- id: blackdoc
# Formatting with "black" coding style
- repo: https://github.com/psf/black
rev: 23.9.1
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 23.12.1
hooks:
# Format Python files
- id: black
@ -67,14 +67,14 @@ repos:
# Do YAML formatting (before the linter checks it for misses)
- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks
rev: v2.10.0
rev: v2.12.0
hooks:
- id: pretty-format-yaml
args: [--autofix, --indent, "2", --preserve-quotes]
# Format Snakemake rule / workflow files
- repo: https://github.com/snakemake/snakefmt
rev: v0.8.4
rev: v0.8.5
hooks:
- id: snakefmt

View File

@ -14,4 +14,3 @@ build:
python:
install:
- requirements: doc/requirements.txt
system_packages: false

View File

@ -61,9 +61,9 @@ The dataset consists of:
- A grid model based on a modified [GridKit](https://github.com/bdw/GridKit)
extraction of the [ENTSO-E Transmission System
Map](https://www.entsoe.eu/data/map/). The grid model contains 6763 lines
Map](https://www.entsoe.eu/data/map/). The grid model contains 7072 lines
(alternating current lines at and above 220kV voltage level and all high
voltage direct current lines) and 3642 substations.
voltage direct current lines) and 3803 substations.
- The open power plant database
[powerplantmatching](https://github.com/FRESNA/powerplantmatching).
- Electrical demand time series from the
@ -103,6 +103,6 @@ We strongly welcome anyone interested in contributing to this project. If you ha
# Licence
The code in PyPSA-Eur is released as free software under the
[MIT License](https://opensource.org/licenses/MIT), see `LICENSE.txt`.
[MIT License](https://opensource.org/licenses/MIT), see [`doc/licenses.rst`](doc/licenses.rst).
However, different licenses and terms of use may apply to the various
input data.

View File

@ -66,6 +66,11 @@ if config["foresight"] == "myopic":
include: "rules/solve_myopic.smk"
if config["foresight"] == "perfect":
include: "rules/solve_perfect.smk"
rule all:
input:
RESULTS + "graphs/costs.pdf",

View File

@ -71,6 +71,7 @@ enable:
retrieve_sector_databundle: true
retrieve_cost_data: true
build_cutout: false
retrieve_irena: false
retrieve_cutout: true
build_natura_raster: false
retrieve_natura_raster: true
@ -90,6 +91,7 @@ co2_budget:
# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#electricity
electricity:
voltages: [220., 300., 380.]
voltages: [220., 300., 380., 500., 750.]
gaslimit_enable: false
gaslimit: false
co2limit_enable: false
@ -144,14 +146,14 @@ atlite:
# module: era5
europe-2013-era5:
module: era5 # in priority order
x: [-12., 35.]
x: [-12., 42.]
y: [33., 72]
dx: 0.3
dy: 0.3
time: ['2013', '2013']
europe-2013-sarah:
module: [sarah, era5] # in priority order
x: [-12., 45.]
x: [-12., 42.]
y: [33., 65]
dx: 0.2
dy: 0.2
@ -167,6 +169,7 @@ renewable:
resource:
method: wind
turbine: Vestas_V112_3MW
add_cutout_windspeed: true
capacity_per_sqkm: 3
# correction_factor: 0.93
corine:
@ -181,7 +184,8 @@ renewable:
cutout: europe-2013-era5
resource:
method: wind
turbine: NREL_ReferenceTurbine_5MW_offshore
turbine: NREL_ReferenceTurbine_2020ATB_5.5MW
add_cutout_windspeed: true
capacity_per_sqkm: 2
correction_factor: 0.8855
corine: [44, 255]
@ -196,7 +200,8 @@ renewable:
cutout: europe-2013-era5
resource:
method: wind
turbine: NREL_ReferenceTurbine_5MW_offshore
turbine: NREL_ReferenceTurbine_2020ATB_5.5MW
add_cutout_windspeed: true
capacity_per_sqkm: 2
correction_factor: 0.8855
corine: [44, 255]
@ -244,10 +249,13 @@ lines:
220.: "Al/St 240/40 2-bundle 220.0"
300.: "Al/St 240/40 3-bundle 300.0"
380.: "Al/St 240/40 4-bundle 380.0"
500.: "Al/St 240/40 4-bundle 380.0"
750.: "Al/St 560/50 4-bundle 750.0"
s_max_pu: 0.7
s_nom_max: .inf
max_extension: .inf
length_factor: 1.25
reconnect_crimea: true
under_construction: 'zero' # 'zero': set capacity to zero, 'remove': remove, 'keep': with full capacity
dynamic_line_rating:
activate: false
@ -458,10 +466,12 @@ sector:
coal_cc: false
dac: true
co2_vent: false
central_heat_vent: false
allam_cycle: false
hydrogen_fuel_cell: true
hydrogen_turbine: false
SMR: true
SMR_cc: true
regional_co2_sequestration_potential:
enable: false
attribute: 'conservative estimate Mt'
@ -471,6 +481,7 @@ sector:
years_of_storage: 25
co2_sequestration_potential: 200
co2_sequestration_cost: 10
co2_sequestration_lifetime: 50
co2_spatial: false
co2network: false
cc_fraction: 0.9
@ -480,11 +491,15 @@ sector:
- nearshore # within 50 km of sea
# - offshore
ammonia: false
min_part_load_fischer_tropsch: 0.9
min_part_load_methanolisation: 0.5
min_part_load_fischer_tropsch: 0.7
min_part_load_methanolisation: 0.3
min_part_load_methanation: 0.3
use_fischer_tropsch_waste_heat: true
use_haber_bosch_waste_heat: true
use_methanolisation_waste_heat: true
use_methanation_waste_heat: true
use_fuel_cell_waste_heat: true
use_electrolysis_waste_heat: false
use_electrolysis_waste_heat: true
electricity_distribution_grid: true
electricity_distribution_grid_cost_factor: 1.0
electricity_grid_connection: true
@ -497,10 +512,25 @@ sector:
gas_distribution_grid_cost_factor: 1.0
biomass_spatial: false
biomass_transport: false
biogas_upgrading_cc: false
conventional_generation:
OCGT: gas
biomass_to_liquid: false
biosng: false
limit_max_growth:
enable: false
# allowing 30% larger than max historic growth
factor: 1.3
max_growth: # unit GW
onwind: 16 # onshore max grow so far 16 GW in Europe https://www.iea.org/reports/renewables-2020/wind
solar: 28 # solar max grow so far 28 GW in Europe https://www.iea.org/reports/renewables-2020/solar-pv
offwind-ac: 35 # offshore max grow so far 3.5 GW in Europe https://windeurope.org/about-wind/statistics/offshore/european-offshore-wind-industry-key-trends-statistics-2019/
offwind-dc: 35
max_relative_growth:
onwind: 3
solar: 3
offwind-ac: 3
offwind-dc: 3
# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#industry
industry:
@ -533,8 +563,8 @@ industry:
MWh_NH3_per_tNH3: 5.166
MWh_CH4_per_tNH3_SMR: 10.8
MWh_elec_per_tNH3_SMR: 0.7
MWh_H2_per_tNH3_electrolysis: 6.5
MWh_elec_per_tNH3_electrolysis: 1.17
MWh_H2_per_tNH3_electrolysis: 5.93
MWh_elec_per_tNH3_electrolysis: 0.2473
MWh_NH3_per_MWh_H2_cracker: 1.46 # https://github.com/euronion/trace/blob/44a5ff8401762edbef80eff9cfe5a47c8d3c8be4/data/efficiencies.csv
NH3_process_emissions: 24.5
petrochemical_process_emissions: 25.5
@ -553,11 +583,13 @@ industry:
hotmaps_locate_missing: false
reference_year: 2015
# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#costs
costs:
year: 2030
version: v0.6.0
rooftop_share: 0.14 # based on the potentials, assuming (0.1 kW/m2 and 10 m2/person)
social_discountrate: 0.02
fill_values:
FOM: 0
VOM: 0
@ -587,6 +619,7 @@ costs:
# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#clustering
clustering:
focus_weights: false
simplify_network:
to_substations: false
algorithm: kmeans # choose from: [hac, kmeans]
@ -718,6 +751,7 @@ plotting:
H2: "Hydrogen Storage"
lines: "Transmission Lines"
ror: "Run of River"
load: "Load Shedding"
ac: "AC"
dc: "DC"
@ -768,6 +802,7 @@ plotting:
fossil gas: '#e05b09'
natural gas: '#e05b09'
biogas to gas: '#e36311'
biogas to gas CC: '#e51245'
CCGT: '#a85522'
CCGT marginal: '#a85522'
allam: '#B98F76'
@ -779,6 +814,7 @@ plotting:
gas pipeline new: '#a87c62'
# oil
oil: '#c9c9c9'
imported oil: '#a3a3a3'
oil boiler: '#adadad'
residential rural oil boiler: '#a9a9a9'
services rural oil boiler: '#a5a5a5'
@ -797,6 +833,7 @@ plotting:
Coal: '#545454'
coal: '#545454'
Coal marginal: '#545454'
coal for industry: '#343434'
solid: '#545454'
Lignite: '#826837'
lignite: '#826837'
@ -867,12 +904,14 @@ plotting:
# heat demand
Heat load: '#cc1f1f'
heat: '#cc1f1f'
heat vent: '#aa3344'
heat demand: '#cc1f1f'
rural heat: '#ff5c5c'
residential rural heat: '#ff7c7c'
services rural heat: '#ff9c9c'
central heat: '#cc1f1f'
urban central heat: '#d15959'
urban central heat vent: '#a74747'
decentral heat: '#750606'
residential urban decentral heat: '#a33c3c'
services urban decentral heat: '#cc1f1f'
@ -910,6 +949,7 @@ plotting:
H2 for shipping: "#ebaee0"
H2: '#bf13a0'
hydrogen: '#bf13a0'
retrofitted H2 boiler: '#e5a0d9'
SMR: '#870c71'
SMR CC: '#4f1745'
H2 liquefaction: '#d647bd'
@ -980,3 +1020,4 @@ plotting:
DC: "#8a1caf"
DC-DC: "#8a1caf"
DC link: "#8a1caf"
load: "#dd2e23"

View File

@ -0,0 +1,43 @@
# SPDX-FileCopyrightText: 2017-2023 The PyPSA-Eur Authors
#
# SPDX-License-Identifier: CC0-1.0
run:
name: "entsoe-all"
disable_progressbar: true
shared_resources: false
shared_cutouts: true
scenario:
simpl:
- ''
ll:
- vopt
clusters:
- 39
- 128
- 256
opts:
- ''
sector_opts:
- ''
planning_horizons:
- ''
# TODO add Turkey (TR)
countries: ['AL', 'AT', 'BA', 'BE', 'BG', 'CH', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GB', 'GR', 'HR', 'HU', 'IE', 'IT', 'LT', 'LU', 'LV', 'ME', 'MD', 'MK', 'NL', 'NO', 'PL', 'PT', 'RO', 'RS', 'SE', 'SI', 'SK', 'UA']
electricity:
custom_powerplants: true
co2limit: 9.59e+7
co2base: 1.918e+9
lines:
reconnect_crimea: true
enable:
retrieve: true
retrieve_databundle: true
retrieve_sector_databundle: false
retrieve_cost_data: true
retrieve_cutout: true

View File

@ -0,0 +1,43 @@
# SPDX-FileCopyrightText: : 2017-2023 The PyPSA-Eur Authors
#
# SPDX-License-Identifier: CC0-1.0
run:
name: "perfect"
# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#foresight
foresight: perfect
# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#scenario
# Wildcard docs in https://pypsa-eur.readthedocs.io/en/latest/wildcards.html
scenario:
simpl:
- ''
ll:
- v1.0
clusters:
- 37
opts:
- ''
sector_opts:
- 1p5-4380H-T-H-B-I-A-solar+p3-dist1
- 1p7-4380H-T-H-B-I-A-solar+p3-dist1
- 2p0-4380H-T-H-B-I-A-solar+p3-dist1
planning_horizons:
- 2020
- 2030
- 2040
- 2050
# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#co2-budget
co2_budget:
# update of IPCC 6th AR compared to the 1.5SR. (discussed here: https://twitter.com/JoeriRogelj/status/1424743828339167233)
1p5: 34.2 # 25.7 # Budget in Gt CO2 for 1.5 for Europe, global 420 Gt, assuming per capita share
1p6: 43.259666 # 35 # Budget in Gt CO2 for 1.6 for Europe, global 580 Gt
1p7: 51.4 # 45 # Budget in Gt CO2 for 1.7 for Europe, global 800 Gt
2p0: 69.778 # 73.9 # Budget in Gt CO2 for 2 for Europe, global 1170 Gt
sector:
min_part_load_fischer_tropsch: 0
min_part_load_methanolisation: 0

View File

@ -30,6 +30,9 @@ snapshots:
start: "2013-03-01"
end: "2013-03-08"
sector:
central_heat_vent: true
electricity:
co2limit: 100.e+6

View File

@ -0,0 +1,92 @@
# SPDX-FileCopyrightText: : 2017-2023 The PyPSA-Eur Authors
#
# SPDX-License-Identifier: CC0-1.0
tutorial: true
run:
name: "test-sector-perfect"
disable_progressbar: true
shared_resources: true
shared_cutouts: true
foresight: perfect
scenario:
ll:
- v1.0
clusters:
- 5
sector_opts:
- 8760H-T-H-B-I-A-solar+p3-dist1
planning_horizons:
- 2030
- 2040
- 2050
countries: ['BE']
snapshots:
start: "2013-03-01"
end: "2013-03-08"
electricity:
co2limit: 100.e+6
extendable_carriers:
Generator: [OCGT]
StorageUnit: [battery]
Store: [H2]
Link: [H2 pipeline]
renewable_carriers: [solar, onwind, offwind-ac, offwind-dc]
sector:
min_part_load_fischer_tropsch: 0
min_part_load_methanolisation: 0
atlite:
default_cutout: be-03-2013-era5
cutouts:
be-03-2013-era5:
module: era5
x: [4., 15.]
y: [46., 56.]
time: ["2013-03-01", "2013-03-08"]
renewable:
onwind:
cutout: be-03-2013-era5
offwind-ac:
cutout: be-03-2013-era5
max_depth: false
offwind-dc:
cutout: be-03-2013-era5
max_depth: false
solar:
cutout: be-03-2013-era5
industry:
St_primary_fraction:
2020: 0.8
2030: 0.6
2040: 0.5
2050: 0.4
solving:
solver:
name: glpk
options: glpk-default
mem: 4000
plotting:
map:
boundaries:
eu_node_location:
x: -5.5
y: 46.
costs_max: 1000
costs_threshold: 0.0000001
energy_max:
energy_min:
energy_threshold: 0.000001

View File

@ -0,0 +1,151 @@
name,GDP_PPP,country
3140,632728.0438507323,MD
3139,806541.9318093687,MD
3142,1392454.6690911907,MD
3152,897871.2903553953,MD
3246,645554.8588933202,MD
7049,1150156.4449477682,MD
1924,162285.16792916053,UA
1970,751970.6071848695,UA
2974,368873.75840156944,UA
2977,294847.85539198935,UA
2979,197988.13680768458,UA
2980,301371.2491126519,UA
3031,56925.21878805953,UA
3032,139395.18279351242,UA
3033,145377.8061037629,UA
3035,52282.83655208812,UA
3036,497950.25890516065,UA
3037,1183293.1987702171,UA
3038,255005.98207636533,UA
3039,224711.50098325178,UA
3040,342959.943226467,UA
3044,69119.31486955672,UA
3045,246273.65986119965,UA
3047,146742.08407299497,UA
3049,107265.7028733467,UA
3050,1126147.985259493,UA
3051,69833.56303043803,UA
3052,67230.88206577855,UA
3053,27019.224685201345,UA
3054,260571.47337292184,UA
3055,88760.94152915622,UA
3056,101368.26196568517,UA
3058,55752.92329667119,UA
3059,89024.37880630122,UA
3062,358411.291265149,UA
3064,75081.64142862396,UA
3065,158101.42949135564,UA
3066,83763.89576442329,UA
3068,173474.51218344545,UA
3069,60327.01572375589,UA
3070,18073.687271955278,UA
3071,249069.43314695224,UA
3072,220707.35700825177,UA
3073,61342.30137462664,UA
3074,254235.98867635374,UA
3077,769558.9832370486,UA
3078,132674.2315809836,UA
3079,1388517.1478032232,UA
3080,1861003.8718246964,UA
3082,140123.73854745473,UA
3083,834887.5595419679,UA
3084,1910795.5590558557,UA
3086,93828.36549170096,UA
3088,347197.65113392205,UA
3089,3754718.141734592,UA
3090,521912.69768585655,UA
3093,232818.05269714879,UA
3095,435376.20361377904,UA
3099,345596.5288937008,UA
3100,175689.10947424968,UA
3105,538438.9311459162,UA
3107,88096.86032871014,UA
3108,79847.68447063807,UA
3109,348504.73449373,UA
3144,71657.0165675802,UA
3146,80342.05037424155,UA
3158,74465.12922576343,UA
3164,3102112.2672631275,UA
3165,65215.04081671433,UA
3166,413924.2225725632,UA
3167,135060.0056434935,UA
3168,54980.442979330146,UA
3170,29584.879122227037,UA
3171,142780.68163047134,UA
3172,40436.63814695243,UA
3173,1253342.1790126422,UA
3174,173842.03139155387,UA
3176,65699.76352408895,UA
3177,143591.75419817626,UA
3178,56434.04525832523,UA
3179,389996.1670051216,UA
3180,138452.84503524794,UA
3181,67402.59500436619,UA
3184,51204.293695376415,UA
3185,46867.82356528432,UA
3186,103892.35612417295,UA
3187,193668.91476930346,UA
3189,54584.176457692694,UA
3190,219077.64942830536,UA
3197,88516.52699983507,UA
3198,298166.8272673622,UA
3199,61334.952541812374,UA
3229,175692.61136747137,UA
3230,106722.62773321665,UA
3236,61542.06264321315,UA
3241,83752.90489164277,UA
4301,48419.52825967164,UA
4305,147759.74280349456,UA
4306,53156.905740992224,UA
4315,218025.78516351627,UA
4317,155240.40554731718,UA
4318,1342144.2459407183,UA
4319,91669.1449633853,UA
4321,85852.49282415409,UA
4347,67938.7698430624,UA
4357,20064.979012172935,UA
4360,47840.51245168512,UA
4361,55580.924388032574,UA
4362,165753.82588729708,UA
4363,46390.2448142152,UA
4365,96265.47592938849,UA
4366,272003.25510057947,UA
4367,80878.50229245829,UA
4370,330072.35444044066,UA
4371,7707066.181975477,UA
4373,2019766.7891575783,UA
4374,985354.331818515,UA
4377,230805.08833664874,UA
4382,125670.67125287943,UA
4383,46914.065511740075,UA
4384,48020.804310510954,UA
4385,55612.34707641123,UA
4387,74558.3475791577,UA
4388,245243.33449409154,UA
4389,95696.56767732685,UA
4391,251085.7523045193,UA
4401,66375.82996856027,UA
4403,111954.41038437477,UA
4405,46911.68560148837,UA
4408,150782.51691456966,UA
4409,112776.7399582134,UA
4410,153076.56860965435,UA
4412,192629.31238456024,UA
4413,181295.3120834606,UA
4414,995694.9413199169,UA
4416,157640.7868989174,UA
4418,77580.20674809469,UA
4420,122320.99275223716,UA
4424,184891.10924920067,UA
4425,84486.75974340564,UA
4431,50485.84380961137,UA
4435,231040.45446464577,UA
4436,81222.18707585508,UA
4438,114819.76472988473,UA
4439,76839.1052178896,UA
4440,135337.0313562152,UA
4441,49159.485269198034,UA
7031,42001.73757065917,UA
7059,159790.48382874,UA
7063,39599.10564971086,UA
1 name GDP_PPP country
2 3140 632728.0438507323 MD
3 3139 806541.9318093687 MD
4 3142 1392454.6690911907 MD
5 3152 897871.2903553953 MD
6 3246 645554.8588933202 MD
7 7049 1150156.4449477682 MD
8 1924 162285.16792916053 UA
9 1970 751970.6071848695 UA
10 2974 368873.75840156944 UA
11 2977 294847.85539198935 UA
12 2979 197988.13680768458 UA
13 2980 301371.2491126519 UA
14 3031 56925.21878805953 UA
15 3032 139395.18279351242 UA
16 3033 145377.8061037629 UA
17 3035 52282.83655208812 UA
18 3036 497950.25890516065 UA
19 3037 1183293.1987702171 UA
20 3038 255005.98207636533 UA
21 3039 224711.50098325178 UA
22 3040 342959.943226467 UA
23 3044 69119.31486955672 UA
24 3045 246273.65986119965 UA
25 3047 146742.08407299497 UA
26 3049 107265.7028733467 UA
27 3050 1126147.985259493 UA
28 3051 69833.56303043803 UA
29 3052 67230.88206577855 UA
30 3053 27019.224685201345 UA
31 3054 260571.47337292184 UA
32 3055 88760.94152915622 UA
33 3056 101368.26196568517 UA
34 3058 55752.92329667119 UA
35 3059 89024.37880630122 UA
36 3062 358411.291265149 UA
37 3064 75081.64142862396 UA
38 3065 158101.42949135564 UA
39 3066 83763.89576442329 UA
40 3068 173474.51218344545 UA
41 3069 60327.01572375589 UA
42 3070 18073.687271955278 UA
43 3071 249069.43314695224 UA
44 3072 220707.35700825177 UA
45 3073 61342.30137462664 UA
46 3074 254235.98867635374 UA
47 3077 769558.9832370486 UA
48 3078 132674.2315809836 UA
49 3079 1388517.1478032232 UA
50 3080 1861003.8718246964 UA
51 3082 140123.73854745473 UA
52 3083 834887.5595419679 UA
53 3084 1910795.5590558557 UA
54 3086 93828.36549170096 UA
55 3088 347197.65113392205 UA
56 3089 3754718.141734592 UA
57 3090 521912.69768585655 UA
58 3093 232818.05269714879 UA
59 3095 435376.20361377904 UA
60 3099 345596.5288937008 UA
61 3100 175689.10947424968 UA
62 3105 538438.9311459162 UA
63 3107 88096.86032871014 UA
64 3108 79847.68447063807 UA
65 3109 348504.73449373 UA
66 3144 71657.0165675802 UA
67 3146 80342.05037424155 UA
68 3158 74465.12922576343 UA
69 3164 3102112.2672631275 UA
70 3165 65215.04081671433 UA
71 3166 413924.2225725632 UA
72 3167 135060.0056434935 UA
73 3168 54980.442979330146 UA
74 3170 29584.879122227037 UA
75 3171 142780.68163047134 UA
76 3172 40436.63814695243 UA
77 3173 1253342.1790126422 UA
78 3174 173842.03139155387 UA
79 3176 65699.76352408895 UA
80 3177 143591.75419817626 UA
81 3178 56434.04525832523 UA
82 3179 389996.1670051216 UA
83 3180 138452.84503524794 UA
84 3181 67402.59500436619 UA
85 3184 51204.293695376415 UA
86 3185 46867.82356528432 UA
87 3186 103892.35612417295 UA
88 3187 193668.91476930346 UA
89 3189 54584.176457692694 UA
90 3190 219077.64942830536 UA
91 3197 88516.52699983507 UA
92 3198 298166.8272673622 UA
93 3199 61334.952541812374 UA
94 3229 175692.61136747137 UA
95 3230 106722.62773321665 UA
96 3236 61542.06264321315 UA
97 3241 83752.90489164277 UA
98 4301 48419.52825967164 UA
99 4305 147759.74280349456 UA
100 4306 53156.905740992224 UA
101 4315 218025.78516351627 UA
102 4317 155240.40554731718 UA
103 4318 1342144.2459407183 UA
104 4319 91669.1449633853 UA
105 4321 85852.49282415409 UA
106 4347 67938.7698430624 UA
107 4357 20064.979012172935 UA
108 4360 47840.51245168512 UA
109 4361 55580.924388032574 UA
110 4362 165753.82588729708 UA
111 4363 46390.2448142152 UA
112 4365 96265.47592938849 UA
113 4366 272003.25510057947 UA
114 4367 80878.50229245829 UA
115 4370 330072.35444044066 UA
116 4371 7707066.181975477 UA
117 4373 2019766.7891575783 UA
118 4374 985354.331818515 UA
119 4377 230805.08833664874 UA
120 4382 125670.67125287943 UA
121 4383 46914.065511740075 UA
122 4384 48020.804310510954 UA
123 4385 55612.34707641123 UA
124 4387 74558.3475791577 UA
125 4388 245243.33449409154 UA
126 4389 95696.56767732685 UA
127 4391 251085.7523045193 UA
128 4401 66375.82996856027 UA
129 4403 111954.41038437477 UA
130 4405 46911.68560148837 UA
131 4408 150782.51691456966 UA
132 4409 112776.7399582134 UA
133 4410 153076.56860965435 UA
134 4412 192629.31238456024 UA
135 4413 181295.3120834606 UA
136 4414 995694.9413199169 UA
137 4416 157640.7868989174 UA
138 4418 77580.20674809469 UA
139 4420 122320.99275223716 UA
140 4424 184891.10924920067 UA
141 4425 84486.75974340564 UA
142 4431 50485.84380961137 UA
143 4435 231040.45446464577 UA
144 4436 81222.18707585508 UA
145 4438 114819.76472988473 UA
146 4439 76839.1052178896 UA
147 4440 135337.0313562152 UA
148 4441 49159.485269198034 UA
149 7031 42001.73757065917 UA
150 7059 159790.48382874 UA
151 7063 39599.10564971086 UA

View File

@ -1 +1,37 @@
Name,Fueltype,Technology,Set,Country,Capacity,Efficiency,Duration,Volume_Mm3,DamHeight_m,YearCommissioned,Retrofit,lat,lon,projectID,YearDecommissioning
,Name,Fueltype,Technology,Set,Country,Capacity,Efficiency,Duration,Volume_Mm3,DamHeight_m,StorageCapacity_MWh,DateIn,DateRetrofit,DateMothball,DateOut,lat,lon,EIC,projectID
1266,Khmelnitskiy,Nuclear,,PP,UA,1901.8916595755832,,0.0,0.0,0.0,0.0,1988.0,2005.0,,,50.3023,26.6466,[nan],"{'GEO': ['GEO3842'], 'GPD': ['WRI1005111'], 'CARMA': ['CARMA22000']}"
1268,Kaniv,Hydro,Reservoir,PP,UA,452.1656050955414,,0.0,0.0,0.0,0.0,1972.0,2003.0,,,49.76653,31.47165,[nan],"{'GEO': ['GEO43017'], 'GPD': ['WRI1005122'], 'CARMA': ['CARMA21140']}"
1269,Kahovska kakhovka,Hydro,Reservoir,PP,UA,352.45222929936307,,0.0,0.0,0.0,0.0,1955.0,1956.0,,,46.77858,33.36965,[nan],"{'GEO': ['GEO43018'], 'GPD': ['WRI1005118'], 'CARMA': ['CARMA20855']}"
1347,Kharkiv,Natural Gas,Steam Turbine,CHP,UA,494.94274967602314,,0.0,0.0,0.0,0.0,1979.0,1980.0,,,49.9719,36107,[nan],"{'GEO': ['GEO43027'], 'GPD': ['WRI1005126'], 'CARMA': ['CARMA21972']}"
1348,Kremenchuk,Hydro,Reservoir,PP,UA,617.0382165605096,,0.0,0.0,0.0,0.0,1959.0,1960.0,,,49.07759,33.2505,[nan],"{'GEO': ['GEO43019'], 'GPD': ['WRI1005121'], 'CARMA': ['CARMA23072']}"
1377,Krivorozhskaya,Hard Coal,Steam Turbine,PP,UA,2600.0164509342876,,0.0,0.0,0.0,0.0,1965.0,1992.0,,,47.5432,33.6583,[nan],"{'GEO': ['GEO42989'], 'GPD': ['WRI1005100'], 'CARMA': ['CARMA23176']}"
1407,Zmiyevskaya zmiivskaya,Hard Coal,Steam Turbine,PP,UA,2028.3816283884514,,0.0,0.0,0.0,0.0,1960.0,2005.0,,,49.5852,36.5231,[nan],"{'GEO': ['GEO42999'], 'GPD': ['WRI1005103'], 'CARMA': ['CARMA51042']}"
1408,Pridneprovskaya,Hard Coal,Steam Turbine,CHP,UA,1627.3152609570984,,0.0,0.0,0.0,0.0,1959.0,1966.0,,,48.4051,35.1131,[nan],"{'GEO': ['GEO42990'], 'GPD': ['WRI1005102'], 'CARMA': ['CARMA35874']}"
1409,Kurakhovskaya,Hard Coal,Steam Turbine,PP,UA,1371.0015824607397,,0.0,0.0,0.0,0.0,1972.0,2003.0,,,47.9944,37.24022,[nan],"{'GEO': ['GEO42994'], 'GPD': ['WRI1005104'], 'CARMA': ['CARMA23339']}"
1410,Dobrotvorsky,Hard Coal,Steam Turbine,PP,UA,553.1949895604868,,0.0,0.0,0.0,0.0,1960.0,1964.0,,,50.2133,24375,[nan],"{'GEO': ['GEO42992'], 'GPD': ['WRI1005096'], 'CARMA': ['CARMA10971']}"
1422,Zuyevskaya,Hard Coal,Steam Turbine,PP,UA,1147.87960333801,,0.0,0.0,0.0,0.0,1982.0,2007.0,,,48.0331,38.28615,[nan],"{'GEO': ['GEO42995'], 'GPD': ['WRI1005106'], 'CARMA': ['CARMA51083']}"
1423,Zaporozhye,Nuclear,,PP,UA,5705.67497872675,,0.0,0.0,0.0,0.0,1985.0,1996.0,,,47.5119,34.5863,[nan],"{'GEO': ['GEO6207'], 'GPD': ['WRI1005114'], 'CARMA': ['CARMA50875']}"
1424,Trypilska,Hard Coal,Steam Turbine,PP,UA,1659.5849686814602,,0.0,0.0,0.0,0.0,1969.0,1972.0,,,50.1344,30.7468,[nan],"{'GEO': ['GEO43000'], 'GPD': ['WRI1005099'], 'CARMA': ['CARMA46410']}"
1425,Tashlyk,Hydro,Pumped Storage,Store,UA,285.55968954109585,,0.0,0.0,0.0,0.0,2006.0,2007.0,,,47.7968,31.1811,[nan],"{'GEO': ['GEO43025'], 'GPD': ['WRI1005117'], 'CARMA': ['CARMA44696']}"
1426,Starobeshivska,Hard Coal,Steam Turbine,PP,UA,1636.5351774497733,,0.0,0.0,0.0,0.0,1961.0,1967.0,,,47.7997,38.00612,[nan],"{'GEO': ['GEO43003'], 'GPD': ['WRI1005105'], 'CARMA': ['CARMA43083']}"
1427,South,Nuclear,,PP,UA,2852.837489363375,,0.0,0.0,0.0,0.0,1983.0,1989.0,,,47812,31.22,[nan],"{'GEO': ['GEO5475'], 'GPD': ['WRI1005113'], 'CARMA': ['CARMA42555']}"
1428,Rovno rivne,Nuclear,,PP,UA,2695.931427448389,,0.0,0.0,0.0,0.0,1981.0,2006.0,,,51.3245,25.89744,[nan],"{'GEO': ['GEO5174'], 'GPD': ['WRI1005112'], 'CARMA': ['CARMA38114']}"
1429,Ladyzhinska,Hard Coal,Steam Turbine,PP,UA,1659.5849686814602,,0.0,0.0,0.0,0.0,1970.0,1971.0,,,48706,29.2202,[nan],"{'GEO': ['GEO42993'], 'GPD': ['WRI1005098'], 'CARMA': ['CARMA24024']}"
1430,Kiev,Hydro,Pumped Storage,PP,UA,635.8694635681177,,0.0,0.0,0.0,0.0,1964.0,1972.0,,,50.5998,30501,"[nan, nan]","{'GEO': ['GEO43024', 'GEO43023'], 'GPD': ['WRI1005123', 'WRI1005124'], 'CARMA': ['CARMA23516', 'CARMA23517']}"
2450,Cet chisinau,Natural Gas,,PP,MD,306.0,,0.0,0.0,0.0,0.0,,,,,47.027550000000005,28.8801,"[nan, nan]","{'GPD': ['WRI1002985', 'WRI1002984'], 'CARMA': ['CARMA8450', 'CARMA8451']}"
2460,Hydropower che costesti,Hydro,,PP,MD,16.0,,0.0,0.0,0.0,0.0,1978.0,,,,47.8381,27.2246,[nan],"{'GPD': ['WRI1002987'], 'CARMA': ['CARMA9496']}"
2465,Moldavskaya gres,Hard Coal,,PP,MD,2520.0,,0.0,0.0,0.0,0.0,,,,,46.6292,29.9407,[nan],"{'GPD': ['WRI1002989'], 'CARMA': ['CARMA28979']}"
2466,Hydropower dubasari,Hydro,,PP,MD,48.0,,0.0,0.0,0.0,0.0,,,,,47.2778,29123,[nan],"{'GPD': ['WRI1002988'], 'CARMA': ['CARMA11384']}"
2676,Cet nord balti,Natural Gas,,PP,MD,24.0,,0.0,0.0,0.0,0.0,,,,,47.7492,27.8938,[nan],"{'GPD': ['WRI1002986'], 'CARMA': ['CARMA3071']}"
2699,Dniprodzerzhynsk,Hydro,Reservoir,PP,UA,360.3503184713376,,0.0,0.0,0.0,0.0,1963.0,1964.0,,,48.5485,34.541015,[nan],"{'GEO': ['GEO43020'], 'GPD': ['WRI1005119']}"
2707,Burshtynska tes,Hard Coal,Steam Turbine,PP,UA,2212.779958241947,,0.0,0.0,0.0,0.0,1965.0,1984.0,,,49.21038,24.66654,[nan],"{'GEO': ['GEO42991'], 'GPD': ['WRI1005097']}"
2708,Danipro dnieper,Hydro,Reservoir,PP,UA,1484.8407643312103,,0.0,0.0,0.0,0.0,1932.0,1947.0,,,47.86944,35.08611,[nan],"{'GEO': ['GEO43016'], 'GPD': ['WRI1005120']}"
2709,Dniester,Hydro,Pumped Storage,Store,UA,612.7241020616891,,0.0,0.0,0.0,0.0,2009.0,2011.0,,,48.51361,27.47333,[nan],"{'GEO': ['GEO43022'], 'GPD': ['WRI1005116', 'WRI1005115']}"
2710,Kiev,Natural Gas,Steam Turbine,CHP,UA,458.2803237740955,,0.0,0.0,0.0,0.0,1982.0,1984.0,,,50532,30.6625,[nan],"{'GEO': ['GEO42998'], 'GPD': ['WRI1005125']}"
2712,Luganskaya,Hard Coal,Steam Turbine,PP,UA,1060.2903966575996,,0.0,0.0,0.0,0.0,1962.0,1969.0,,,48.74781,39.2624,[nan],"{'GEO': ['GEO42996'], 'GPD': ['WRI1005110']}"
2713,Slavyanskaya,Hard Coal,Steam Turbine,PP,UA,737.5933194139823,,0.0,0.0,0.0,0.0,1971.0,1971.0,,,48872,37.76567,[nan],"{'GEO': ['GEO43002'], 'GPD': ['WRI1005109']}"
2714,Vuhlehirska uglegorskaya,Hard Coal,Steam Turbine,PP,UA,3319.1699373629203,,0.0,0.0,0.0,0.0,1972.0,1977.0,,,48.4633,38.20328,[nan],"{'GEO': ['GEO43001'], 'GPD': ['WRI1005107']}"
2715,Zaporiska,Hard Coal,Steam Turbine,PP,UA,3319.1699373629203,,0.0,0.0,0.0,0.0,1972.0,1977.0,,,47.5089,34.6253,[nan],"{'GEO': ['GEO42988'], 'GPD': ['WRI1005101']}"
3678,Mironovskaya,Hard Coal,,PP,UA,815.0,,0.0,0.0,0.0,0.0,,,,,48.3407,38.4049,[nan],"{'GPD': ['WRI1005108'], 'CARMA': ['CARMA28679']}"
3679,Kramatorskaya,Hard Coal,,PP,UA,120.0,,0.0,0.0,0.0,0.0,1974.0,,,,48.7477,37.5723,[nan],"{'GPD': ['WRI1075856'], 'CARMA': ['CARMA54560']}"
3680,Chernihiv,Hard Coal,,PP,UA,200.0,,0.0,0.0,0.0,0.0,1968.0,,,,51455,31.2602,[nan],"{'GPD': ['WRI1075853'], 'CARMA': ['CARMA8190']}"

1 Name Fueltype Technology Set Country Capacity Efficiency Duration Volume_Mm3 YearCommissioned DamHeight_m Retrofit StorageCapacity_MWh DateIn DateRetrofit DateMothball YearDecommissioning DateOut lat lon EIC projectID
2 1266 Khmelnitskiy Nuclear PP UA 1901.8916595755832 0.0 0.0 0.0 0.0 1988.0 2005.0 50.3023 26.6466 [nan] {'GEO': ['GEO3842'], 'GPD': ['WRI1005111'], 'CARMA': ['CARMA22000']}
3 1268 Kaniv Hydro Reservoir PP UA 452.1656050955414 0.0 0.0 0.0 0.0 1972.0 2003.0 49.76653 31.47165 [nan] {'GEO': ['GEO43017'], 'GPD': ['WRI1005122'], 'CARMA': ['CARMA21140']}
4 1269 Kahovska kakhovka Hydro Reservoir PP UA 352.45222929936307 0.0 0.0 0.0 0.0 1955.0 1956.0 46.77858 33.36965 [nan] {'GEO': ['GEO43018'], 'GPD': ['WRI1005118'], 'CARMA': ['CARMA20855']}
5 1347 Kharkiv Natural Gas Steam Turbine CHP UA 494.94274967602314 0.0 0.0 0.0 0.0 1979.0 1980.0 49.9719 36107 [nan] {'GEO': ['GEO43027'], 'GPD': ['WRI1005126'], 'CARMA': ['CARMA21972']}
6 1348 Kremenchuk Hydro Reservoir PP UA 617.0382165605096 0.0 0.0 0.0 0.0 1959.0 1960.0 49.07759 33.2505 [nan] {'GEO': ['GEO43019'], 'GPD': ['WRI1005121'], 'CARMA': ['CARMA23072']}
7 1377 Krivorozhskaya Hard Coal Steam Turbine PP UA 2600.0164509342876 0.0 0.0 0.0 0.0 1965.0 1992.0 47.5432 33.6583 [nan] {'GEO': ['GEO42989'], 'GPD': ['WRI1005100'], 'CARMA': ['CARMA23176']}
8 1407 Zmiyevskaya zmiivskaya Hard Coal Steam Turbine PP UA 2028.3816283884514 0.0 0.0 0.0 0.0 1960.0 2005.0 49.5852 36.5231 [nan] {'GEO': ['GEO42999'], 'GPD': ['WRI1005103'], 'CARMA': ['CARMA51042']}
9 1408 Pridneprovskaya Hard Coal Steam Turbine CHP UA 1627.3152609570984 0.0 0.0 0.0 0.0 1959.0 1966.0 48.4051 35.1131 [nan] {'GEO': ['GEO42990'], 'GPD': ['WRI1005102'], 'CARMA': ['CARMA35874']}
10 1409 Kurakhovskaya Hard Coal Steam Turbine PP UA 1371.0015824607397 0.0 0.0 0.0 0.0 1972.0 2003.0 47.9944 37.24022 [nan] {'GEO': ['GEO42994'], 'GPD': ['WRI1005104'], 'CARMA': ['CARMA23339']}
11 1410 Dobrotvorsky Hard Coal Steam Turbine PP UA 553.1949895604868 0.0 0.0 0.0 0.0 1960.0 1964.0 50.2133 24375 [nan] {'GEO': ['GEO42992'], 'GPD': ['WRI1005096'], 'CARMA': ['CARMA10971']}
12 1422 Zuyevskaya Hard Coal Steam Turbine PP UA 1147.87960333801 0.0 0.0 0.0 0.0 1982.0 2007.0 48.0331 38.28615 [nan] {'GEO': ['GEO42995'], 'GPD': ['WRI1005106'], 'CARMA': ['CARMA51083']}
13 1423 Zaporozhye Nuclear PP UA 5705.67497872675 0.0 0.0 0.0 0.0 1985.0 1996.0 47.5119 34.5863 [nan] {'GEO': ['GEO6207'], 'GPD': ['WRI1005114'], 'CARMA': ['CARMA50875']}
14 1424 Trypilska Hard Coal Steam Turbine PP UA 1659.5849686814602 0.0 0.0 0.0 0.0 1969.0 1972.0 50.1344 30.7468 [nan] {'GEO': ['GEO43000'], 'GPD': ['WRI1005099'], 'CARMA': ['CARMA46410']}
15 1425 Tashlyk Hydro Pumped Storage Store UA 285.55968954109585 0.0 0.0 0.0 0.0 2006.0 2007.0 47.7968 31.1811 [nan] {'GEO': ['GEO43025'], 'GPD': ['WRI1005117'], 'CARMA': ['CARMA44696']}
16 1426 Starobeshivska Hard Coal Steam Turbine PP UA 1636.5351774497733 0.0 0.0 0.0 0.0 1961.0 1967.0 47.7997 38.00612 [nan] {'GEO': ['GEO43003'], 'GPD': ['WRI1005105'], 'CARMA': ['CARMA43083']}
17 1427 South Nuclear PP UA 2852.837489363375 0.0 0.0 0.0 0.0 1983.0 1989.0 47812 31.22 [nan] {'GEO': ['GEO5475'], 'GPD': ['WRI1005113'], 'CARMA': ['CARMA42555']}
18 1428 Rovno rivne Nuclear PP UA 2695.931427448389 0.0 0.0 0.0 0.0 1981.0 2006.0 51.3245 25.89744 [nan] {'GEO': ['GEO5174'], 'GPD': ['WRI1005112'], 'CARMA': ['CARMA38114']}
19 1429 Ladyzhinska Hard Coal Steam Turbine PP UA 1659.5849686814602 0.0 0.0 0.0 0.0 1970.0 1971.0 48706 29.2202 [nan] {'GEO': ['GEO42993'], 'GPD': ['WRI1005098'], 'CARMA': ['CARMA24024']}
20 1430 Kiev Hydro Pumped Storage PP UA 635.8694635681177 0.0 0.0 0.0 0.0 1964.0 1972.0 50.5998 30501 [nan, nan] {'GEO': ['GEO43024', 'GEO43023'], 'GPD': ['WRI1005123', 'WRI1005124'], 'CARMA': ['CARMA23516', 'CARMA23517']}
21 2450 Cet chisinau Natural Gas PP MD 306.0 0.0 0.0 0.0 0.0 47.027550000000005 28.8801 [nan, nan] {'GPD': ['WRI1002985', 'WRI1002984'], 'CARMA': ['CARMA8450', 'CARMA8451']}
22 2460 Hydropower che costesti Hydro PP MD 16.0 0.0 0.0 0.0 0.0 1978.0 47.8381 27.2246 [nan] {'GPD': ['WRI1002987'], 'CARMA': ['CARMA9496']}
23 2465 Moldavskaya gres Hard Coal PP MD 2520.0 0.0 0.0 0.0 0.0 46.6292 29.9407 [nan] {'GPD': ['WRI1002989'], 'CARMA': ['CARMA28979']}
24 2466 Hydropower dubasari Hydro PP MD 48.0 0.0 0.0 0.0 0.0 47.2778 29123 [nan] {'GPD': ['WRI1002988'], 'CARMA': ['CARMA11384']}
25 2676 Cet nord balti Natural Gas PP MD 24.0 0.0 0.0 0.0 0.0 47.7492 27.8938 [nan] {'GPD': ['WRI1002986'], 'CARMA': ['CARMA3071']}
26 2699 Dniprodzerzhynsk Hydro Reservoir PP UA 360.3503184713376 0.0 0.0 0.0 0.0 1963.0 1964.0 48.5485 34.541015 [nan] {'GEO': ['GEO43020'], 'GPD': ['WRI1005119']}
27 2707 Burshtynska tes Hard Coal Steam Turbine PP UA 2212.779958241947 0.0 0.0 0.0 0.0 1965.0 1984.0 49.21038 24.66654 [nan] {'GEO': ['GEO42991'], 'GPD': ['WRI1005097']}
28 2708 Danipro dnieper Hydro Reservoir PP UA 1484.8407643312103 0.0 0.0 0.0 0.0 1932.0 1947.0 47.86944 35.08611 [nan] {'GEO': ['GEO43016'], 'GPD': ['WRI1005120']}
29 2709 Dniester Hydro Pumped Storage Store UA 612.7241020616891 0.0 0.0 0.0 0.0 2009.0 2011.0 48.51361 27.47333 [nan] {'GEO': ['GEO43022'], 'GPD': ['WRI1005116', 'WRI1005115']}
30 2710 Kiev Natural Gas Steam Turbine CHP UA 458.2803237740955 0.0 0.0 0.0 0.0 1982.0 1984.0 50532 30.6625 [nan] {'GEO': ['GEO42998'], 'GPD': ['WRI1005125']}
31 2712 Luganskaya Hard Coal Steam Turbine PP UA 1060.2903966575996 0.0 0.0 0.0 0.0 1962.0 1969.0 48.74781 39.2624 [nan] {'GEO': ['GEO42996'], 'GPD': ['WRI1005110']}
32 2713 Slavyanskaya Hard Coal Steam Turbine PP UA 737.5933194139823 0.0 0.0 0.0 0.0 1971.0 1971.0 48872 37.76567 [nan] {'GEO': ['GEO43002'], 'GPD': ['WRI1005109']}
33 2714 Vuhlehirska uglegorskaya Hard Coal Steam Turbine PP UA 3319.1699373629203 0.0 0.0 0.0 0.0 1972.0 1977.0 48.4633 38.20328 [nan] {'GEO': ['GEO43001'], 'GPD': ['WRI1005107']}
34 2715 Zaporiska Hard Coal Steam Turbine PP UA 3319.1699373629203 0.0 0.0 0.0 0.0 1972.0 1977.0 47.5089 34.6253 [nan] {'GEO': ['GEO42988'], 'GPD': ['WRI1005101']}
35 3678 Mironovskaya Hard Coal PP UA 815.0 0.0 0.0 0.0 0.0 48.3407 38.4049 [nan] {'GPD': ['WRI1005108'], 'CARMA': ['CARMA28679']}
36 3679 Kramatorskaya Hard Coal PP UA 120.0 0.0 0.0 0.0 0.0 1974.0 48.7477 37.5723 [nan] {'GPD': ['WRI1075856'], 'CARMA': ['CARMA54560']}
37 3680 Chernihiv Hard Coal PP UA 200.0 0.0 0.0 0.0 0.0 1968.0 51455 31.2602 [nan] {'GPD': ['WRI1075853'], 'CARMA': ['CARMA8190']}

View File

@ -1,50 +1,53 @@
https://www.eia.gov/international/data/world/electricity/electricity-generation?pd=2&p=000000000000000000000000000000g&u=1&f=A&v=mapbubble&a=-&i=none&vo=value&t=R&g=000000000000002&l=73-1028i008017kg6368g80a4k000e0ag00gg0004g8g0ho00g000400008&s=315532800000&e=1577836800000&ev=false&
Report generated on: 03-28-2022 11:20:48
"API","","1980","1981","1982","1983","1984","1985","1986","1987","1988","1989","1990","1991","1992","1993","1994","1995","1996","1997","1998","1999","2000","2001","2002","2003","2004","2005","2006","2007","2008","2009","2010","2011","2012","2013","2014","2015","2016","2017","2018","2019","2020"
"","hydroelectricity net generation (billion kWh)","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
"INTL.33-12-EURO-BKWH.A"," Europe","458.018","464.155","459.881","473.685","481.241","476.739","459.535","491.085","534.517","465.365","474.466","475.47","509.041","526.448","531.815","543.743","529.114164","543.845616","562.441501","569.308453","591.206662","587.371195","541.542535","506.19703","544.536443","545.176179","537.335934","540.934407","567.557921","564.244482","619.96477","543.05273","600.46622","631.86431","619.59229","615.53013","629.98906","562.59258","619.31106","610.62616","670.925"
"INTL.33-12-ALB-BKWH.A"," Albania","2.919","3.018","3.093","3.167","3.241","3.315","3.365","3.979","3.713","3.846","2.82","3.483","3.187","3.281","3.733","4.162","5.669","4.978","4.872","5.231","4.548","3.519","3.477","5.117","5.411","5.319","4.951","2.76","3.759","5.201","7.49133","4.09068","4.67775","6.88941","4.67676","5.83605","7.70418","4.47975","8.46648","5.15394","5.281"
"INTL.33-12-AUT-BKWH.A"," Austria","28.501","30.008","29.893","29.577","28.384","30.288","30.496","25.401","35.151","34.641","31.179","31.112","34.483","36.336","35.349","36.696","33.874","35.744","36.792","40.292","41.418","40.05","39.825","32.883","36.394","36.31","35.48","36.732","37.969","40.487","36.466","32.511","41.862","40.138","39.001","35.255","37.954","36.462","35.73","40.43655","45.344"
"INTL.33-12-BEL-BKWH.A"," Belgium","0.274","0.377","0.325","0.331","0.348","0.282","0.339","0.425","0.354","0.3","0.263","0.226","0.338","0.252","0.342","0.335","0.237","0.30195","0.38511","0.338","0.455","0.437","0.356","0.245","0.314","0.285","0.355","0.385","0.406","0.325","0.298","0.193","0.353","0.376","0.289","0.314","0.367","0.268","0.311","0.108","1.29"
"INTL.33-12-BIH-BKWH.A"," Bosnia and Herzegovina","--","--","--","--","--","--","--","--","--","--","--","--","3.374","2.343","3.424","3.607","5.104","4.608","4.511","5.477","5.043","5.129","5.215","4.456","5.919","5.938","5.798","3.961","4.818","6.177","7.946","4.343","4.173","7.164","5.876","5.495","5.585","3.7521","6.35382","6.02019","6.1"
"INTL.33-12-BGR-BKWH.A"," Bulgaria","3.674","3.58","3.018","3.318","3.226","2.214","2.302","2.512","2.569","2.662","1.859","2.417","2.042","1.923","1.453","2.291","2.89","2.726","3.066","2.725","2.646","1.72","2.172","2.999","3.136","4.294","4.196","2.845","2.796","3.435","4.98168","2.84328","3.14622","3.99564","4.55598","5.59845","3.8412","2.79972","5.09553","3.34917","3.37"
"INTL.33-12-HRV-BKWH.A"," Croatia","--","--","--","--","--","--","--","--","--","--","--","--","4.298","4.302","4.881","5.212","7.156","5.234","5.403","6.524","5.794","6.482","5.311","4.827","6.888","6.27","5.94","4.194","5.164","6.663","9.035","4.983","4.789","8.536","8.917","6.327","6.784","5.255","7.62399","5.87268","3.4"
"INTL.33-12-CYP-BKWH.A"," Cyprus","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"
"INTL.33-12-CZE-BKWH.A"," Czech Republic","--","--","--","--","--","--","--","--","--","--","--","--","--","1.355","1.445","1.982","1.949","1.68201","1.382","1.664","1.7404","2.033","2.467","1.369","1.999","2.356","2.525","2.068","2.004","2.405","2.775","1.95","2.107","2.704","1.909","1.779","1.983","1.852","1.615","1.98792","3.4"
"INTL.33-12-DNK-BKWH.A"," Denmark","0.03","0.031","0.028","0.036","0.028","0.027","0.029","0.029","0.032","0.027","0.027","0.026","0.028","0.027","0.033","0.03","0.019","0.019","0.02673","0.031","0.03","0.028","0.032","0.021","0.027","0.023","0.023","0.028","0.026","0.019","0.021","0.017","0.017","0.013","0.015","0.018","0.019","0.018","0.015","0.01584","0.02"
"INTL.33-12-EST-BKWH.A"," Estonia","--","--","--","--","--","--","--","--","--","--","--","--","0.001","0.001","0.003","0.002","0.002","0.003","0.004","0.004","0.005","0.007","0.006","0.013","0.022","0.022","0.014","0.021","0.028","0.032","0.027","0.03","0.042","0.026","0.027","0.027","0.035","0.026","0.015","0.01881","0.04"
"INTL.33-12-FRO-BKWH.A"," Faroe Islands","0.049","0.049","0.049","0.049","0.049","0.049","0.049","0.049","0.062","0.071","0.074","0.074","0.083","0.073","0.075","0.075","0.069564","0.075066","0.076501","0.069453","0.075262","0.075195","0.095535","0.08483","0.093443","0.097986","0.099934","0.103407","0.094921","0.091482","0.06676","0.092","0.099","0.091","0.121","0.132","0.105","0.11","0.107","0.102","0.11"
"INTL.33-12-FIN-BKWH.A"," Finland","10.115","13.518","12.958","13.445","13.115","12.211","12.266","13.658","13.229","12.9","10.75","13.065","14.956","13.341","11.669","12.796","11.742","12.11958","14.9","12.652","14.513","13.073","10.668","9.495","14.919","13.646","11.379","14.035","16.941","12.559","12.743","12.278","16.667","12.672","13.24","16.584","15.634","14.61","13.137","12.31461","15.56"
"INTL.33-12-CSK-BKWH.A"," Former Czechoslovakia","4.8","4.2","3.7","3.9","3.2","4.3","4","4.853","4.355","4.229","3.919","3.119","3.602","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--"
"INTL.33-12-SCG-BKWH.A"," Former Serbia and Montenegro","--","--","--","--","--","--","--","--","--","--","--","--","11.23","10.395","11.016","12.071","14.266","12.636","12.763","13.243","11.88","12.326","11.633","9.752","11.01","11.912","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--"
"INTL.33-12-YUG-BKWH.A"," Former Yugoslavia","27.868","25.044","23.295","21.623","25.645","24.363","27.474","25.98","25.612","23.256","19.601","18.929","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--"
"INTL.33-12-FRA-BKWH.A"," France","68.253","70.358","68.6","67.515","64.01","60.248","60.953","68.623","73.952","45.744","52.796","56.277","68.313","64.3","78.057","72.196","64.43","63.151","61.479","71.832","66.466","73.888","59.992","58.567","59.276","50.965","55.741","57.029","63.017","56.428","61.945","45.184","59.099","71.042","62.993","54.876","60.094","49.389","64.485","56.98242","64.84"
"INTL.33-12-DEU-BKWH.A"," Germany","--","--","--","--","--","--","--","--","--","--","--","14.742","17.223","17.699","19.731","21.562","21.737","17.18343","17.044","19.451","21.515","22.506","22.893","19.071","20.866","19.442","19.808","20.957","20.239","18.841","20.678","17.323","21.331","22.66","19.31","18.664","20.214","19.985","17.815","19.86039","24.75"
"INTL.33-12-DDR-BKWH.A"," Germany, East","1.658","1.718","1.748","1.683","1.748","1.758","1.767","1.726","1.719","1.551","1.389","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--"
"INTL.33-12-DEUW-BKWH.A"," Germany, West","17.125","17.889","17.694","16.713","16.434","15.354","16.526","18.36","18.128","16.482","15.769","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--"
"INTL.33-12-GIB-BKWH.A"," Gibraltar","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"
"INTL.33-12-GRC-BKWH.A"," Greece","3.396","3.398","3.551","2.331","2.852","2.792","3.222","2.768","2.354","1.888","1.751","3.068","2.181","2.26","2.573","3.494","4.305","3.84318","3.68","4.546","3.656","2.076","2.772","4.718","4.625","4.967","5.806","2.565","3.279","5.32","7.431","3.998","4.387","6.337","4.464","5.782","5.543","3.962","5.035","3.9798","3.43"
"INTL.33-12-HUN-BKWH.A"," Hungary","0.111","0.166","0.158","0.153","0.179","0.153","0.152","0.167","0.167","0.156","0.176","0.192","0.156","0.164","0.159","0.161","0.205","0.21384","0.15345","0.179","0.176","0.184","0.192","0.169","0.203","0.2","0.184","0.208","0.211","0.226","0.184","0.216","0.206","0.208","0.294","0.227","0.253","0.214","0.216","0.21681","0.24"
"INTL.33-12-ISL-BKWH.A"," Iceland","3.053","3.085","3.407","3.588","3.738","3.667","3.846","3.918","4.169","4.217","4.162","4.162","4.267","4.421","4.47","4.635","4.724","5.15493","5.565","5.987","6.292","6.512","6.907","7.017","7.063","6.949","7.22","8.31","12.303","12.156","12.51","12.382","12.214","12.747","12.554","13.541","13.092","13.892","13.679","13.32441","12.46"
"INTL.33-12-IRL-BKWH.A"," Ireland","0.833","0.855","0.792","0.776","0.68","0.824","0.91","0.673","0.862","0.684","0.69","0.738","0.809","0.757","0.911","0.706","0.715","0.67122","0.907","0.838","0.838","0.59","0.903","0.592","0.624","0.625","0.717","0.66","0.959","0.893","0.593","0.699","0.795","0.593","0.701","0.798","0.674","0.685","0.687","0.87813","1.21"
"INTL.33-12-ITA-BKWH.A"," Italy","44.997","42.782","41.216","40.96","41.923","40.616","40.626","39.05","40.205","33.647","31.31","41.817","41.778","41.011","44.212","37.404","41.617","41.18697","40.808","44.911","43.763","46.343","39.125","33.303","41.915","35.706","36.624","32.488","41.207","48.647","50.506","45.36477","41.45625","52.24626","57.95955","45.08163","42.00768","35.83701","48.29913","45.31824","47.72"
"INTL.33-12-XKS-BKWH.A"," Kosovo","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","0.075","0.119","0.154","0.104","0.095","0.142","0.149","0.139","0.243","0.177","0.27027","0.2079","0.26"
"INTL.33-12-LVA-BKWH.A"," Latvia","--","--","--","--","--","--","--","--","--","--","--","--","2.498","2.846","3.272","2.908","1.841","2.922","2.99","2.729","2.791","2.805","2.438","2.243","3.078","3.293","2.671","2.706","3.078","3.422","3.488","2.857","3.677","2.838","1.953","1.841","2.523","4.356","2.417","2.08692","2.59"
"INTL.33-12-LTU-BKWH.A"," Lithuania","--","--","--","--","--","--","--","--","--","--","--","--","0.308","0.389","0.447","0.369","0.323","0.291","0.413","0.409","0.336","0.322","0.35","0.323","0.417","0.446193","0.393","0.417","0.398","0.42","0.535","0.475","0.419","0.516","0.395","0.346","0.45","0.597","0.427","0.34254","1.06"
"INTL.33-12-LUX-BKWH.A"," Luxembourg","0.086","0.095","0.084","0.083","0.088","0.071","0.084","0.101","0.097","0.072","0.07","0.083","0.069","0.066","0.117","0.087","0.059","0.082","0.114","0.084","0.119","0.117","0.098","0.078","0.103","0.093","0.11","0.116","0.131","0.105","0.104","0.061","0.095","0.114","0.104","0.095","0.111","0.082","0.089","0.10593","1.09"
"INTL.33-12-MLT-BKWH.A"," Malta","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"
"INTL.33-12-MNE-BKWH.A"," Montenegro","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","1.733","1.271","1.524","2.05","2.723","1.192","1.462","2.479","1.734","1.476","1.825","1.014","2.09187","1.78","1.8"
"INTL.33-12-NLD-BKWH.A"," Netherlands","0","0","0","0","0","0.003","0.003","0.001","0.002","0.037","0.119","0.079","0.119","0.091","0.1","0.087","0.079","0.09108","0.111","0.089","0.141","0.116","0.109","0.071","0.094","0.087","0.105","0.106","0.101","0.097","0.105","0.057","0.104","0.114","0.112","0.093","0.1","0.061","0.072","0.07326","0.05"
"INTL.33-12-MKD-BKWH.A"," North Macedonia","--","--","--","--","--","--","--","--","--","--","--","--","0.817","0.517","0.696","0.793","0.842","0.891","1.072","1.375","1.158","0.62","0.749","1.36","1.467","1.477","1.634","1","0.832","1.257","2.407","1.419","1.031","1.568","1.195","1.846","1.878","1.099","1.773","1.15236","1.24"
"INTL.33-12-NOR-BKWH.A"," Norway","82.717","91.876","91.507","104.704","104.895","101.464","95.321","102.341","107.919","117.369","119.933","109.032","115.505","118.024","110.398","120.315","102.823","108.677","114.546","120.237","140.4","119.258","128.078","104.425","107.693","134.331","118.175","132.319","137.654","124.03","116.257","119.78","141.189","127.551","134.844","136.662","142.244","141.651","138.202","123.66288","141.69"
"INTL.33-12-POL-BKWH.A"," Poland","2.326","2.116","1.528","1.658","1.394","1.833","1.534","1.644","1.775","1.593","1.403","1.411","1.492","1.473","1.716","1.868","1.912","1.941","2.286","2.133","2.085","2.302","2.256","1.654","2.06","2.179","2.022","2.328","2.13","2.351","2.9","2.313","2.02","2.421","2.165","1.814","2.117","2.552","1.949","1.93842","2.93"
"INTL.33-12-PRT-BKWH.A"," Portugal","7.873","4.934","6.82","7.897","9.609","10.512","8.364","9.005","12.037","5.72","9.065","8.952","4.599","8.453","10.551","8.26","14.613","12.97395","12.853","7.213","11.21","13.894","7.722","15.566","9.77","4.684","10.892","9.991","6.73","8.201","15.954","11.423","5.589","13.652","15.471","8.615","15.608","5.79","12.316","8.6526","13.96"
"INTL.33-12-ROU-BKWH.A"," Romania","12.506","12.605","11.731","9.934","11.208","11.772","10.688","11.084","13.479","12.497","10.87","14.107","11.583","12.64","12.916","16.526","15.597","17.334","18.69","18.107","14.63","14.774","15.886","13.126","16.348","20.005","18.172","15.806","17.023","15.379","19.684","14.581","11.945","14.807","18.618","16.467","17.848","14.349","17.48736","15.65289","15.53"
"INTL.33-12-SRB-BKWH.A"," Serbia","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","10.855","9.937","9.468","10.436","11.772","8.58","9.193","10.101","10.893","9.979","10.684","9.061","10.53261","10.07028","9.66"
"INTL.33-12-SVK-BKWH.A"," Slovakia","--","--","--","--","--","--","--","--","--","--","--","--","--","3.432","4.311","4.831","4.185","4.023","4.224","4.429","4.569","4.878","5.215","3.4452","4.059","4.592","4.355","4.406","4","4.324","5.184","3.211","3.687","4.329","3.762","3.701","4.302","4.321","3.506","4.27383","4.67"
"INTL.33-12-SVN-BKWH.A"," Slovenia","--","--","--","--","--","--","--","--","--","--","--","--","3.379","2.974","3.348","3.187","3.616","3.046","3.4","3.684","3.771","3.741","3.265","2.916","4.033","3.426","3.555","3.233","3.978","4.666","4.452","3.506","3.841","4.562","6.011","3.75","4.443","3.814","4.643","4.43421","5.24"
"INTL.33-12-ESP-BKWH.A"," Spain","29.16","21.64","25.99","26.696","31.088","30.895","26.105","27.016","34.76","19.046","25.16","27.01","18.731","24.133","27.898","22.881","39.404","34.43","33.665","22.634","29.274","40.617","22.691","40.643","31.359","18.209","25.699","27.036","23.13","26.147","41.576","30.07","20.192","36.45","38.815","27.656","35.77","18.007","33.743","24.23025","33.34"
"INTL.33-12-SWE-BKWH.A"," Sweden","58.133","59.006","54.369","62.801","67.106","70.095","60.134","70.95","69.016","70.911","71.778","62.603","73.588","73.905","58.508","67.421","51.2226","68.365","74.25","70.974","77.798","78.269","65.696","53.005","59.522","72.075","61.106","65.497","68.378","65.193","66.279","66.047","78.333","60.81","63.227","74.734","61.645","64.651","61.79","64.46583","71.6"
"INTL.33-12-CHE-BKWH.A"," Switzerland","32.481","35.13","35.974","35.069","29.871","31.731","32.576","34.328","35.437","29.477","29.497","31.756","32.373","35.416","38.678","34.817","28.458","33.70257","33.136","39.604","36.466","40.895","34.862","34.471","33.411","30.914","30.649","34.898","35.676","35.366","35.704","32.069","38.218","38.08","37.659","37.879","34.281","33.754","34.637","37.6596","40.62"
"INTL.33-12-TUR-BKWH.A"," Turkey","11.159","12.308","13.81","11.13","13.19","11.822","11.637","18.314","28.447","17.61","22.917","22.456","26.302","33.611","30.28","35.186","40.07","39.41784","41.80671","34.33","30.57","23.77","33.346","34.977","45.623","39.165","43.802","35.492","32.937","35.598","51.423","51.155","56.669","58.225","39.75","65.856","66.686","57.824","59.49","87.99714","77.39"
"INTL.33-12-GBR-BKWH.A"," United Kingdom","3.921","4.369","4.543","4.548","3.992","4.08","4.767","4.13","4.915","4.732","5.119","4.534","5.329","4.237","5.043","4.79","3.359","4.127","5.067","5.283","5.035","4.015","4.74","3.195","4.795","4.873","4.547","5.026","5.094","5.178","3.566","5.655","5.286","4.667","5.832","6.246","5.342","5.836","5.189","5.89941","7.64"
https://www.eia.gov/international/data/world/electricity/electricity-generation?pd=2&p=000000000000000000000000000000g&u=1&f=A&v=mapbubble&a=-&i=none&vo=value&t=R&g=000000000000002&l=73-1028i008017kg6368g80a4k000e0ag00gg0004g8g0ho00g000400008&l=72-00000000000000000000000000080000000000000000000g&s=315532800000&e=1609459200000&ev=false&,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
Report generated on: 01-06-2023 21:17:46,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
API,,1980,1981,1982,1983,1984,1985,1986,1987,1988,1989,1990,1991,1992,1993,1994,1995,1996,1997,1998,1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021
,hydroelectricity net generation (billion kWh),,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
INTL.33-12-EURO-BKWH.A, Europe,"458,018","464,155","459,881","473,685","481,241","476,739","459,535","491,085","534,517","465,365","474,466","475,47","509,041","526,448","531,815","543,743","529,114164","543,845616","562,491501","566,861453","588,644662","584,806195","539,051405","503,7067","542,112443","542,974669","535,006084","538,449707","565,143111","561,761402","617,547148","540,926277","598,055253","629,44709","617,111295","613,079848","627,720566217","560,362524","616,5081462","606,5997419","644,1106599","628,1390143"
INTL.33-12-ALB-BKWH.A, Albania,"2,919","3,018","3,093","3,167","3,241","3,315","3,365","3,979","3,713","3,846","2,82","3,483","3,187","3,281","3,733","4,162","5,669","4,978","4,872","5,231","4,548","3,519","3,477","5,117","5,411","5,319","4,951","2,76","3,759","5,201","7,49133","4,09068","4,67775","6,88941","4,67676","5,83605","7,70418","4,47975","8,46648","5,15394","5,281","8,891943"
INTL.33-12-AUT-BKWH.A, Austria,"28,501","30,008","29,893","29,577","28,384","30,288","30,496","25,401","35,151","34,641","31,179","31,112","34,483","36,336","35,349","36,696","33,874","35,744","36,792","40,292","41,418","40,05","39,825","32,883","36,394","36,31","35,48","36,732","37,969","40,487","36,466","32,511","41,862","40,138","39,001","35,255","37,954","36,462","35,73","40,43655","41,9356096","38,75133"
INTL.33-12-BEL-BKWH.A, Belgium,"0,274","0,377","0,325","0,331","0,348","0,282","0,339","0,425","0,354","0,3","0,263","0,226","0,338","0,252","0,342","0,335","0,237","0,30195","0,38511","0,338","0,455","0,437","0,356","0,245","0,314","0,285","0,355","0,385","0,406","0,325","0,298","0,193","0,353","0,376","0,289","0,314","0,367","0,268","0,3135","0,302","0,2669","0,3933"
INTL.33-12-BIH-BKWH.A, Bosnia and Herzegovina,--,--,--,--,--,--,--,--,--,--,--,--,"3,374","2,343","3,424","3,607","5,104","4,608","4,511","5,477","5,043","5,129","5,215","4,456","5,919","5,938","5,798","3,961","4,818","6,177","7,946","4,343","4,173","7,164","5,876","5,495","5,585","3,7521","6,35382","6,02019","4,58","6,722"
INTL.33-12-BGR-BKWH.A, Bulgaria,"3,674","3,58","3,018","3,318","3,226","2,214","2,302","2,512","2,569","2,662","1,859","2,417","2,042","1,923","1,453","2,291","2,89","2,726","3,066","2,725","2,646","1,72","2,172","2,999","3,136","4,294","4,196","2,845","2,796","3,435","4,98168","2,84328","3,14622","3,99564","4,55598","5,59845","3,8412","2,79972","5,09553","2,929499","2,820398","4,819205"
INTL.33-12-HRV-BKWH.A, Croatia,--,--,--,--,--,--,--,--,--,--,--,--,"4,298","4,302","4,881","5,212","7,156","5,234","5,403","6,524","5,794","6,482","5,311","4,827","6,888","6,27","5,94","4,194","5,164","6,663","9,035","4,983","4,789","8,536","8,917","6,327","6,784","5,255","7,62399","5,87268","5,6624","7,1277"
INTL.33-12-CYP-BKWH.A, Cyprus,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
INTL.33-12-CZE-BKWH.A, Czechia,--,--,--,--,--,--,--,--,--,--,--,--,--,"1,355","1,445","1,982","1,949","1,68201","1,382","1,664","1,7404","2,033","2,467","1,369","1,999","2,356","2,525","2,068","2,004","2,405","2,775","1,95","2,107","2,704","1,909","1,779","1,983","1,852","1,615","1,98792","2,143884","2,40852"
INTL.33-12-DNK-BKWH.A, Denmark,"0,03","0,031","0,028","0,036","0,028","0,027","0,029","0,029","0,032","0,027","0,027","0,026","0,028","0,027","0,033","0,03","0,019","0,019","0,02673","0,031","0,03","0,028","0,032","0,021","0,027","0,023","0,023","0,028","0,026","0,019","0,021","0,017","0,017","0,013","0,015","0,01803","0,01927","0,017871","0,0148621","0,0172171","0,017064","0,016295"
INTL.33-12-EST-BKWH.A, Estonia,--,--,--,--,--,--,--,--,--,--,--,--,"0,001","0,001","0,003","0,002","0,002","0,003","0,004","0,004","0,005","0,007","0,006","0,013","0,022","0,022","0,014","0,021","0,028","0,032","0,027","0,029999","0,042","0,026","0,027","0,027","0,035","0,025999","0,0150003","0,0189999","0,03","0,0248"
INTL.33-12-FRO-BKWH.A, Faroe Islands,"0,049","0,049","0,049","0,049","0,049","0,049","0,049","0,049","0,062","0,071","0,074","0,074","0,083","0,073","0,075","0,075","0,069564","0,075066","0,076501","0,069453","0,075262","0,075195","0,095535","0,08483","0,093443","0,097986","0,099934","0,103407","0,094921","0,091482","0,06676","0,092","0,099","0,091","0,121","0,132","0,105","0,11","0,107","0,102","0,11","0,11"
INTL.33-12-FIN-BKWH.A, Finland,"10,115","13,518","12,958","13,445","13,115","12,211","12,266","13,658","13,229","12,9","10,75","13,065","14,956","13,341","11,669","12,796","11,742","12,11958","14,9","12,652","14,513","13,073","10,668","9,495","14,919","13,646","11,379","14,035","16,941","12,559","12,743","12,278001","16,666998","12,672","13,240001","16,583999","15,634127","14,609473","13,1369998","12,2454823","15,883","15,766"
INTL.33-12-CSK-BKWH.A, Former Czechoslovakia,"4,8","4,2","3,7","3,9","3,2","4,3",4,"4,853","4,355","4,229","3,919","3,119","3,602",--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--
INTL.33-12-SCG-BKWH.A, Former Serbia and Montenegro,--,--,--,--,--,--,--,--,--,--,--,--,"11,23","10,395","11,016","12,071","14,266","12,636","12,763","13,243","11,88","12,326","11,633","9,752","11,01","11,912",--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--
INTL.33-12-YUG-BKWH.A, Former Yugoslavia,"27,868","25,044","23,295","21,623","25,645","24,363","27,474","25,98","25,612","23,256","19,601","18,929",--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--
INTL.33-12-FRA-BKWH.A, France,"68,253","70,358","68,6","67,515","64,01","60,248","60,953","68,623","73,952","45,744","52,796","56,277","68,313","64,3","78,057","72,196","64,43","63,151","61,479","71,832","66,466","73,888","59,992","58,567","59,276","50,965","55,741","57,029","63,017","56,428","61,945","45,184","59,099","71,042","62,993","54,876","60,094","49,389","64,485","56,913891","62,06191","58,856657"
INTL.33-12-DEU-BKWH.A, Germany,--,--,--,--,--,--,--,--,--,--,--,"14,742","17,223","17,699","19,731","21,562","21,737","17,18343","17,044","19,451","21,515","22,506","22,893","19,071","20,866","19,442","19,808","20,957","20,239","18,841","20,678","17,323","21,331","22,66","19,31","18,664","20,214","19,985","17,694","19,731","18,322","19,252"
INTL.33-12-DDR-BKWH.A," Germany, East","1,658","1,718","1,748","1,683","1,748","1,758","1,767","1,726","1,719","1,551","1,389",--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--
INTL.33-12-DEUW-BKWH.A," Germany, West","17,125","17,889","17,694","16,713","16,434","15,354","16,526","18,36","18,128","16,482","15,769",--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--
INTL.33-12-GIB-BKWH.A, Gibraltar,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
INTL.33-12-GRC-BKWH.A, Greece,"3,396","3,398","3,551","2,331","2,852","2,792","3,222","2,768","2,354","1,888","1,751","3,068","2,181","2,26","2,573","3,494","4,305","3,84318","3,68","4,546","3,656","2,076","2,772","4,718","4,625","4,967","5,806","2,565","3,279","5,32","7,431","3,998","4,387","6,337","4,464","5,782","5,543","3,962","5,035","3,9798","3,343687","5,909225"
INTL.33-12-HUN-BKWH.A, Hungary,"0,111","0,166","0,158","0,153","0,179","0,153","0,152","0,167","0,167","0,156","0,176","0,192","0,156","0,164","0,159","0,161","0,205","0,21384","0,15345","0,179","0,176","0,184","0,192","0,169","0,203","0,2","0,184","0,208","0,211","0,226","0,184","0,215999","0,205999","0,207999","0,294001","0,226719","0,253308","0,213999","0,216","0,2129999","0,238","0,202379"
INTL.33-12-ISL-BKWH.A, Iceland,"3,053","3,085","3,407","3,588","3,738","3,667","3,846","3,918","4,169","4,217","4,162","4,162","4,267","4,421","4,47","4,635","4,724","5,15493","5,565","5,987","6,292","6,512","6,907","7,017","7,063","6,949","7,22","8,31","12,303","12,156","12,509999","12,381999","12,213999","12,747001","12,554","13,541","13,091609","13,891929","13,679377","13,32911","12,9196201","13,5746171"
INTL.33-12-IRL-BKWH.A, Ireland,"0,833","0,855","0,792","0,776","0,68","0,824","0,91","0,673","0,862","0,684","0,69","0,738","0,809","0,757","0,911","0,706","0,715","0,67122","0,907","0,838","0,838","0,59","0,903","0,592","0,624","0,625","0,717","0,66","0,959","0,893","0,593","0,699","0,795","0,593","0,701","0,798","0,674","0,685","0,687","0,87813","0,932656","0,750122"
INTL.33-12-ITA-BKWH.A, Italy,"44,997","42,782","41,216","40,96","41,923","40,616","40,626","39,05","40,205","33,647","31,31","41,817","41,778","41,011","44,212","37,404","41,617","41,18697","40,808","44,911","43,763","46,343","39,125","33,303","41,915","35,706","36,624","32,488","41,207","48,647","50,506","45,36477","41,45625","52,24626","57,95955","45,08163","42,00768","35,83701","48,29913","45,31824","47,551784","44,739"
INTL.33-12-XKS-BKWH.A, Kosovo,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,"0,075","0,119","0,154","0,104","0,095","0,142","0,149","0,139","0,243","0,177","0,27027","0,2079","0,262826","0,300635"
INTL.33-12-LVA-BKWH.A, Latvia,--,--,--,--,--,--,--,--,--,--,--,--,"2,498","2,846","3,272","2,908","1,841","2,922","2,99","2,729","2,791","2,805","2,438","2,243","3,078","3,293","2,671","2,706","3,078","3,422","3,487998","2,8568","3,677","2,838","1,953","1,841","2,522819","4,355513","2,4170639","2,0958919","2,5840101","2,6889293"
INTL.33-12-LTU-BKWH.A, Lithuania,--,--,--,--,--,--,--,--,--,--,--,--,"0,308","0,389","0,447","0,369","0,323","0,291","0,413","0,409","0,336","0,322","0,35","0,323","0,417","0,446193","0,393","0,417","0,398","0,42","0,535","0,475","0,419","0,516","0,395","0,346","0,45","0,597","0,427","0,34254","0,3006","0,3837"
INTL.33-12-LUX-BKWH.A, Luxembourg,"0,086","0,095","0,084","0,083","0,088","0,071","0,084","0,101","0,097","0,072","0,07","0,083","0,069","0,066","0,117","0,087","0,059","0,082","0,114","0,084","0,119","0,117","0,098","0,078","0,103","0,093","0,11","0,116","0,131","0,105","0,104","0,061","0,095","0,114","0,104","0,095","0,111","0,082","0,089","0,10593","0,091602","0,1068"
INTL.33-12-MLT-BKWH.A, Malta,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
INTL.33-12-MNE-BKWH.A, Montenegro,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,"1,733","1,271","1,524","2,05","2,723","1,192","1,462","2,479","1,734","1,476","1,825","1,014","1,693443","1,262781","0,867637","1,212652"
INTL.33-12-NLD-BKWH.A, Netherlands,0,0,0,0,0,"0,003","0,003","0,001","0,002","0,037","0,119","0,079","0,119","0,091","0,1","0,087","0,079","0,09108","0,111","0,089","0,141","0,116","0,109","0,071","0,094","0,087","0,105","0,106","0,101","0,097","0,105","0,057","0,104389","0,11431","0,112202","0,0927","0,100078","0,060759","0,0723481","0,074182","0,0462851","0,0838927"
INTL.33-12-MKD-BKWH.A, North Macedonia,--,--,--,--,--,--,--,--,--,--,--,--,"0,817","0,517","0,696","0,793","0,842","0,891","1,072","1,375","1,158","0,62","0,749","1,36","1,467","1,477","1,634",1,"0,832","1,257","2,407","1,419","1,031","1,568","1,195","1,846","1,878","1,099","1,773","1,15236","1,277144","1,451623"
INTL.33-12-NOR-BKWH.A, Norway,"82,717","91,876","91,507","104,704","104,895","101,464","95,321","102,341","107,919","117,369","119,933","109,032","115,505","118,024","110,398","120,315","102,823","108,677","114,546","120,237","140,4","119,258","128,078","104,425","107,693","134,331","118,175","132,319","137,654","124,03","116,257","119,78","141,189","127,551","134,844","136,662","142,244","141,651","138,202","123,66288","141,69",144
INTL.33-12-POL-BKWH.A, Poland,"2,326","2,116","1,528","1,658","1,394","1,833","1,534","1,644","1,775","1,593","1,403","1,411","1,492","1,473","1,716","1,868","1,912","1,941","2,286","2,133","2,085","2,302","2,256","1,654","2,06","2,179","2,022","2,328","2,13","2,351","2,9","2,313","2,02","2,421","2,165","1,814","2,117","2,552","1,949","1,93842","2,118337","2,339192"
INTL.33-12-PRT-BKWH.A, Portugal,"7,873","4,934","6,82","7,897","9,609","10,512","8,364","9,005","12,037","5,72","9,065","8,952","4,599","8,453","10,551","8,26","14,613","12,97395","12,853","7,213","11,21","13,894","7,722","15,566","9,77","4,684","10,892","9,991","6,73","8,201","15,954","11,423","5,589","13,652","15,471","8,615","15,608","5,79","12,316","8,6526","12,082581","11,846464"
INTL.33-12-ROU-BKWH.A, Romania,"12,506","12,605","11,731","9,934","11,208","11,772","10,688","11,084","13,479","12,497","10,87","14,107","11,583","12,64","12,916","16,526","15,597","17,334","18,69","18,107","14,63","14,774","15,886","13,126","16,348","20,005","18,172","15,806","17,023","15,379","19,684","14,581","11,945","14,807","18,618","16,467","17,848","14,349","17,48736","15,580622","15,381243","17,376933"
INTL.33-12-SRB-BKWH.A, Serbia,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,--,"10,855","9,937","9,468","10,436","11,772","8,58","9,193","10,101","10,893","9,979","10,684","9,061","10,53261","9,457175","9,034496","11,284232"
INTL.33-12-SVK-BKWH.A, Slovakia,--,--,--,--,--,--,--,--,--,--,--,--,--,"3,432","4,311","4,831","4,185","4,023","4,224","4,429","4,569","4,878","5,215","3,4452","4,059","4,592","4,355","4,406",4,"4,324","5,184","3,211","3,687","4,329","3,762","3,701","4,302","4,321","3,506","4,27383","4,517","4,17"
INTL.33-12-SVN-BKWH.A, Slovenia,--,--,--,--,--,--,--,--,--,--,--,--,"3,379","2,974","3,348","3,187","3,616","3,046","3,4","3,684","3,771","3,741","3,265","2,916","4,033","3,426","3,555","3,233","3,978","4,666","4,452","3,506","3,841","4,562","6,011","3,75","4,443","3,814","4,643","4,43421","4,93406","4,711944"
INTL.33-12-ESP-BKWH.A, Spain,"29,16","21,64","25,99","26,696","31,088","30,895","26,105","27,016","34,76","19,046","25,16","27,01","18,731","24,133","27,898","22,881","39,404","34,43","33,665","22,634","29,274","40,617","22,691","40,643","31,359","18,209","25,699","27,036","23,13","26,147","41,576","30,07","20,192","36,45","38,815","27,656","35,77","18,007","33,743","24,23025","30,507","29,626"
INTL.33-12-SWE-BKWH.A, Sweden,"58,133","59,006","54,369","62,801","67,106","70,095","60,134","70,95","69,016","70,911","71,778","62,603","73,588","73,905","58,508","67,421","51,2226","68,365","74,25","70,974","77,798","78,269","65,696","53,005","59,522","72,075","61,106","65,497","68,378","65,193","66,279","66,047","78,333","60,81","63,227","74,734","61,645","64,651","61,79","64,46583","71,6","71,086"
INTL.33-12-CHE-BKWH.A, Switzerland,"32,481","35,13","35,974","35,069","29,871","31,731","32,576","34,328","35,437","29,477","29,497","31,756","32,373","35,416","38,678","34,817","28,458","33,70257","33,136","37,104","33,854","38,29","32,323","31,948","30,938","28,664","28,273","32,362","33,214","32,833","33,261","29,906","35,783","35,628","35,122","35,378","31,984","31,47968","32,095881","35,156989","37,867647","36,964485"
INTL.33-12-TUR-BKWH.A, Turkey,"11,159","12,308","13,81","11,13","13,19","11,822","11,637","18,314","28,447","17,61","22,917","22,456","26,302","33,611","30,28","35,186","40,07","39,41784","41,80671","34,33","30,57","23,77","33,346","34,977","45,623","39,165","43,802","35,492","32,937","35,598","51,423001","51,154999","56,668998","58,225","39,750001","65,856","66,685883","57,823851","59,490211","88,2094218","78,094369","55,1755392"
INTL.33-12-GBR-BKWH.A, United Kingdom,"3,921","4,369","4,543","4,548","3,992","4,08","4,767","4,13","4,915","4,732","5,119","4,534","5,329","4,237","5,043","4,79","3,359","4,127","5,117","5,336","5,085","4,055","4,78787","3,22767","4,844","4,92149","4,59315","5,0773","5,14119","5,22792","3,59138","5,69175","5,30965","4,70147","5,8878","6,29727","5,370412217","5,88187","5,44327","5,84628","6,75391","5,0149"
, Eurasia,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
INTL.33-12-MDA-BKWH.A, Moldova,--,--,--,--,--,--,--,--,--,--,--,--,"0,255","0,371","0,275","0,321","0,362","0,378","0,387","0,363","0,392","0,359","0,348","0,358","0,35","0,359","0,365","0,354","0,385","0,354","0,403","0,348","0,266","0,311","0,317","0,265","0,228","0,282","0,27324","0,29799","0,276","0,316"
INTL.33-12-UKR-BKWH.A, Ukraine,--,--,--,--,--,--,--,--,--,--,--,--,"7,725","10,929","11,997","9,853","8,546","9,757","15,756","14,177","11,161","11,912","9,531","9,146","11,635","12,239","12,757","10,042","11,397","11,817","13,02","10,837","10,374","13,663","8,393","5,343","7,594","8,856","10,32372","6,5083","7,5638","10,3326"

Can't render this file because it has a wrong number of fields in line 3.

View File

@ -1,34 +1,34 @@
Country/area,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018
Albania,,,,,,,,,,,,,,,,,,,
Austria,,,,,,,,,,,,,,,,,,,
Belgium,,,,,,,,,,31.5,196.5,196.5,381,707.7,707.7,712,712.2,877.2,1185.9
Bosnia Herzg,,,,,,,,,,,,,,,,,,,
Bulgaria,,,,,,,,,,,,,,,,,,,
Croatia,,,,,,,,,,,,,,,,,,,
Czechia,,,,,,,,,,,,,,,,,,,
Denmark,50,50,214,423.4,423.4,423.4,423.4,423.4,423.4,660.9,867.9,871.5,921.9,1271.1,1271.1,1271.1,1271.1,1263.8,1700.8
Estonia,,,,,,,,,,,,,,,,,,,
Finland,,,,,,,,,24,24,26.3,26.3,26.3,26.3,26.3,32,32,72.7,72.7
France,,,,,,,,,,,,,,,,,,2,2
Germany,,,,,,,,,,35,80,188,268,508,994,3283,4132,5406,6396
Greece,,,,,,,,,,,,,,,,,,,
Hungary,,,,,,,,,,,,,,,,,,,
Ireland,,,,,25.2,25.2,25.2,25.2,25.2,25.2,25.2,25.2,25.2,25.2,25.2,25.2,25.2,25.2,25.2
Italy,,,,,,,,,,,,,,,,,,,
Latvia,,,,,,,,,,,,,,,,,,,
Lithuania,,,,,,,,,,,,,,,,,,,
Luxembourg,,,,,,,,,,,,,,,,,,,
Montenegro,,,,,,,,,,,,,,,,,,,
Netherlands,,,,,,,108,108,228,228,228,228,228,228,228,357,957,957,957
North Macedonia,,,,,,,,,,,,,,,,,,,
Norway,,,,,,,,,,2.3,2.3,2.3,2.3,2.3,2.3,2.3,2.3,2.3,2.3
Poland,,,,,,,,,,,,,,,,,,,
Portugal,,,,,,,,,,,,1.9,2,2,2,2,,,
Romania,,,,,,,,,,,,,,,,,,,
Serbia,,,,,,,,,,,,,,,,,,,
Slovakia,,,,,,,,,,,,,,,,,,,
Slovenia,,,,,,,,,,,,,,,,,,,
Spain,,,,,,,,,,,,,,5,5,5,5,5,5
Sweden,13,22,22,22,22,22,22,131,133,163,163,163,163,212,213,213,203,203,203
Switzerland,,,,,,,,,,,,,,,,,,,
UK,3.8,3.8,3.8,63.8,123.8,213.8,303.8,393.8,596.2,951.2,1341.5,1838.3,2995.5,3696,4501.3,5093.4,5293.4,6987.9,8216.5
Country/area,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021,2022
Albania,,,,,,,,,,,,,,,,,,,,,,,
Austria,,,,,,,,,,,,,,,,,,,,,,,
Belgium,,,,,,,,,,31.5,196.5,196.5,381.0,707.7,707.7,712.0,712.2,877.2,1185.9,1555.5,2261.8,2261.8,2261.8
Bosnia Herzg,,,,,,,,,,,,,,,,,,,,,,,
Bulgaria,,,,,,,,,,,,,,,,,,,,,,,
Croatia,,,,,,,,,,,,,,,,,,,,,,,
Czechia,,,,,,,,,,,,,,,,,,,,,,,
Denmark,49.95,49.95,213.95,423.35,423.35,423.35,423.35,423.35,423.35,660.85,867.85,871.45,921.85,1271.05,1271.05,1271.05,1271.05,1263.8,1700.8,1700.8,1700.8,2305.6,2305.6
Estonia,,,,,,,,,,,,,,,,,,,,,,,
Finland,,,,,,,,,24.0,24.0,26.3,26.3,26.3,26.3,26.3,32.0,32.0,72.7,72.7,73.0,73.0,73.0,73.0
France,,,,,,,,,,,,,,,,,,2.0,2.0,2.0,2.0,2.0,482.0
Germany,,,,,,,,,,35.0,80.0,188.0,268.0,508.0,994.0,3283.0,4132.0,5406.0,6393.0,7555.0,7787.0,7787.0,8129.0
Greece,,,,,,,,,,,,,,,,,,,,,,,
Hungary,,,,,,,,,,,,,,,,,,,,,,,
Ireland,,,,,25.2,25.2,25.2,25.2,25.2,25.2,25.2,25.2,25.2,25.2,25.2,25.2,25.2,25.2,25.2,25.2,25.2,25.2,25.2
Italy,,,,,,,,,,,,,,,,,,,,,,,30.0
Latvia,,,,,,,,,,,,,,,,,,,,,,,
Lithuania,,,,,,,,,,,,,,,,,,,,,,,
Luxembourg,,,,,,,,,,,,,,,,,,,,,,,
Montenegro,,,,,,,,,,,,,,,,,,,,,,,
Netherlands,,,,,,,108.0,108.0,228.0,228.0,228.0,228.0,228.0,228.0,228.0,357.0,957.0,957.0,957.0,957.0,2459.5,2459.5,2571.0
North Macedonia,,,,,,,,,,,,,,,,,,,,,,,
Norway,,,,,,,,,,2.3,2.3,2.3,2.3,2.3,2.3,2.3,2.3,2.3,2.3,2.3,2.3,6.3,66.3
Poland,,,,,,,,,,,,,,,,,,,,,,,
Portugal,,,,,,,,,,,,1.86,2.0,2.0,2.0,2.0,,,,,25.0,25.0,25.0
Romania,,,,,,,,,,,,,,,,,,,,,,,
Serbia,,,,,,,,,,,,,,,,,,,,,,,
Slovakia,,,,,,,,,,,,,,,,,,,,,,,
Slovenia,,,,,,,,,,,,,,,,,,,,,,,
Spain,,,,,,,,,,,,,,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0
Sweden,13.0,22.0,22.0,22.0,22.0,22.0,22.0,131.0,133.0,163.0,163.0,163.0,163.0,212.0,213.0,213.0,203.0,203.0,203.0,203.0,203.0,193.0,193.0
Switzerland,,,,,,,,,,,,,,,,,,,,,,,
UK,4.0,4.0,4.0,64.0,124.0,214.0,304.0,394.0,596.2,951.0,1341.0,1838.0,2995.0,3696.0,4501.0,5093.0,5293.0,6988.0,8181.0,9888.0,10383.0,11255.0,13928.0

1 Country/area 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022
2 Albania
3 Austria
4 Belgium 31.5 196.5 196.5 381 381.0 707.7 707.7 712 712.0 712.2 877.2 1185.9 1555.5 2261.8 2261.8 2261.8
5 Bosnia Herzg
6 Bulgaria
7 Croatia
8 Czechia
9 Denmark 50 49.95 50 49.95 214 213.95 423.4 423.35 423.4 423.35 423.4 423.35 423.4 423.35 423.4 423.35 423.4 423.35 660.9 660.85 867.9 867.85 871.5 871.45 921.9 921.85 1271.1 1271.05 1271.1 1271.05 1271.1 1271.05 1271.1 1271.05 1263.8 1700.8 1700.8 1700.8 2305.6 2305.6
10 Estonia
11 Finland 24 24.0 24 24.0 26.3 26.3 26.3 26.3 26.3 32 32.0 32 32.0 72.7 72.7 73.0 73.0 73.0 73.0
12 France 2 2.0 2 2.0 2.0 2.0 2.0 482.0
13 Germany 35 35.0 80 80.0 188 188.0 268 268.0 508 508.0 994 994.0 3283 3283.0 4132 4132.0 5406 5406.0 6396 6393.0 7555.0 7787.0 7787.0 8129.0
14 Greece
15 Hungary
16 Ireland 25.2 25.2 25.2 25.2 25.2 25.2 25.2 25.2 25.2 25.2 25.2 25.2 25.2 25.2 25.2 25.2 25.2 25.2 25.2
17 Italy 30.0
18 Latvia
19 Lithuania
20 Luxembourg
21 Montenegro
22 Netherlands 108 108.0 108 108.0 228 228.0 228 228.0 228 228.0 228 228.0 228 228.0 228 228.0 228 228.0 357 357.0 957 957.0 957 957.0 957 957.0 957.0 2459.5 2459.5 2571.0
23 North Macedonia
24 Norway 2.3 2.3 2.3 2.3 2.3 2.3 2.3 2.3 2.3 2.3 2.3 2.3 6.3 66.3
25 Poland
26 Portugal 1.9 1.86 2 2.0 2 2.0 2 2.0 2 2.0 25.0 25.0 25.0
27 Romania
28 Serbia
29 Slovakia
30 Slovenia
31 Spain 5 5.0 5 5.0 5 5.0 5 5.0 5 5.0 5 5.0 5.0 5.0 5.0 5.0
32 Sweden 13 13.0 22 22.0 22 22.0 22 22.0 22 22.0 22 22.0 22 22.0 131 131.0 133 133.0 163 163.0 163 163.0 163 163.0 163 163.0 212 212.0 213 213.0 213 213.0 203 203.0 203 203.0 203 203.0 203.0 203.0 193.0 193.0
33 Switzerland
34 UK 3.8 4.0 3.8 4.0 3.8 4.0 63.8 64.0 123.8 124.0 213.8 214.0 303.8 304.0 393.8 394.0 596.2 951.2 951.0 1341.5 1341.0 1838.3 1838.0 2995.5 2995.0 3696 3696.0 4501.3 4501.0 5093.4 5093.0 5293.4 5293.0 6987.9 6988.0 8216.5 8181.0 9888.0 10383.0 11255.0 13928.0

View File

@ -1,34 +1,34 @@
Country/area,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018
Albania,,,,,,,,,,,,,,,,,,,
Austria,50,67,109,322,581,825.2,968.3,991.2,992,1001,1015.8,1106,1337.2,1674.5,2110.3,2488.7,2730,2886.7,3132.7
Belgium,14,26,31,67,96,167,212,276,324,576.5,715.5,872.5,989,1072.3,1236.3,1464,1657.8,1919.3,2074.8
Bosnia Herzg,,,,,,,,,,,,0.3,0.3,0.3,0.3,0.3,0.3,0.3,50.9
Bulgaria,,,,,1,8,27,30,114,333,488,541,677,683,699,699,699,698.4,698.9
Croatia,,,,,6,6,17,17,17,70,79,130,180,254,339,418,483,576.1,586.3
Czechia,2,,6.4,10.6,16.5,22,43.5,113.8,150,193,213,213,258,262,278,281,282,308.2,316.2
Denmark,2340.1,2447.2,2680.6,2696.6,2700.4,2704.5,2712.3,2700.9,2739.5,2821.2,2934,3080.5,3240.1,3547.9,3615.4,3805.9,3974.5,4225.8,4419.8
Estonia,,,1,3,7,31,31,50,77,104,108,180,266,248,275,300,310,311.8,310
Finland,38,39,43,52,82,82,86,110,119,123,170.7,172.7,230.7,420.7,600.7,973,1533,1971.3,1968.3
France,38,66,138,218,358,690,1412,2223,3403,4582,5912,6758,7607.5,8156,9201.4,10298.2,11566.6,13497.4,14898.1
Germany,6095,8754,12001,14381,16419,18248,20474,22116,22794,25697,26823,28524,30711,32969,37620,41297,45303,50174,52447
Greece,226,270,287,371,470,491,749,846,1022,1171,1298,1640,1753,1809,1978,2091,2370,2624,2877.5
Hungary,,1,1,3,3,17,33,61,134,203,293,331,325,329,329,329,329,329,329
Ireland,116.5,122.9,134.8,210.3,311.2,468.1,651.3,715.3,917.1,1226.1,1365.2,1559.4,1679.2,1983,2258.1,2426,2760.8,3292.8,3650.9
Italy,363,664,780,874,1127,1635,1902,2702,3525,4879,5794,6918,8102,8542,8683,9137,9384,9736.6,10230.2
Latvia,2,2,22,26,26,26,26,26,28,29,30,36,59,65.9,68.9,68.2,69.9,77.1,78.2
Lithuania,,,,,1,1,31,47,54,98,133,202,275,279,288,436,509,518,533
Luxembourg,14,13.9,13.9,20.5,34.9,34.9,34.9,34.9,42.9,42.9,43.7,44.5,58.3,58.3,58.3,63.8,119.7,119.7,122.9
Montenegro,,,,,,,,,,,,,,,,,,72,118
Netherlands,447,486,672,905,1075,1224,1453,1641,1921,1994,2009,2088,2205,2485,2637,3034,3300,3245,3436
North Macedonia,,,,,,,,,,,,,,,37,37,37,37,37
Norway,13,13,97,97,152,265,284,348,395,420.7,422.7,509.7,702.7,815.7,856.7,864.7,880.7,1204.7,1708
Poland,4,19,32,35,40,121,172,306,526,709,1108,1800,2564,3429,3836,4886,5747,5759.4,5766.1
Portugal,83,125,190,268,553,1064,1681,2201,2857,3326,3796,4254.4,4409.6,4607.9,4854.6,4934.8,5124.1,5124.1,5172.4
Romania,,,,,,1,1,3,5,15,389,988,1822,2773,3244,3130,3025,3029.8,3032.3
Serbia,,,,,,,,,,,,,0.5,0.5,0.5,10.4,17,25,25
Slovakia,,,,3,3,5,5,5,5,3,3,3,3,5,3,3,3,4,3
Slovenia,,,,,,,,,,,,,,4,4,5,5,5,5.2
Spain,2206,3397,4891,5945,8317,9918,11722,14820,16555,19176,20693,21529,22789,22953,22920,22938,22985,23119.5,23400.1
Sweden,196,273,335,395,453,500,563,692,956,1312,1854,2601,3443,3982,4875,5606,6232,6408,7097
Switzerland,3,5,5,5,9,12,12,12,14,18,42,46,49,60,60,60,75,75,75
UK,408.2,489.2,530.2,678.2,809.2,1351.2,1651.2,2083.2,2849.8,3470.8,4079.8,4758,6035,7586.3,8572.7,9212.2,10832.3,12596.9,13553.9
Country/area,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021,2022
Albania,,,,,,,,,,,,,,,,,,,,,,,
Austria,50.0,67.0,109.0,322.0,581.0,825.22,968.27,991.16,991.97,1000.99,1015.83,1105.97,1337.15,1674.54,2110.28,2488.73,2730.0,2886.7,3132.71,3224.12,3225.98,3407.81,3735.81
Belgium,14.0,26.0,31.0,67.0,96.0,167.0,212.0,276.0,324.0,576.5,715.5,872.5,985.9,1061.3,1225.0,1469.3,1621.6,1902.2,2119.0,2308.0,2410.9,2686.6,2989.6
Bosnia Herzg,,,,,,,,,,,,0.3,0.3,0.3,0.3,0.3,0.3,0.3,51.0,87.0,87.0,135.0,135.0
Bulgaria,,,,,1.0,8.0,27.0,30.0,114.0,333.0,488.0,541.0,677.0,683.0,699.0,699.0,699.0,698.39,698.92,703.12,702.8,704.38,704.38
Croatia,,,,,6.0,6.0,17.0,17.0,17.0,70.0,79.0,130.0,180.0,254.0,339.0,418.0,483.0,576.1,586.3,646.3,801.3,986.9,1042.9
Czechia,2.0,,6.4,10.6,16.5,22.0,43.5,113.8,150.0,193.0,213.0,213.0,258.0,262.0,278.0,281.0,282.0,308.21,316.2,339.41,339.42,339.41,339.41
Denmark,2340.07,2447.2,2680.58,2696.57,2700.36,2704.49,2712.35,2700.86,2739.52,2821.24,2933.98,3080.53,3240.09,3547.87,3615.35,3805.92,3974.09,4225.15,4421.86,4409.74,4566.23,4715.24,4782.24
Estonia,,,1.0,3.0,7.0,31.0,31.0,50.0,77.0,104.0,108.0,180.0,266.0,248.0,275.0,300.0,310.0,311.8,310.0,316.0,317.0,315.0,315.0
Finland,38.0,39.0,43.0,52.0,82.0,82.0,86.0,110.0,119.0,123.0,170.7,172.7,230.7,420.7,600.7,973.0,1533.0,1971.3,1968.3,2211.0,2513.0,3184.0,5541.0
France,38.0,66.0,138.0,218.0,358.0,690.0,1412.0,2223.0,3403.0,4582.0,5912.0,6758.02,7607.5,8155.96,9201.42,10298.18,11566.56,13497.35,14898.14,16424.85,17512.0,18737.98,20637.98
Germany,6095.0,8754.0,12001.0,14381.0,16419.0,18248.0,20474.0,22116.0,22794.0,25697.0,26823.0,28524.0,30711.0,32969.0,37620.0,41297.0,45303.0,50174.0,52328.0,53187.0,54414.0,56046.0,58165.0
Greece,226.0,270.0,287.0,371.0,470.0,491.0,749.0,846.0,1022.0,1171.0,1298.0,1640.0,1753.0,1809.0,1978.0,2091.0,2370.0,2624.0,2877.5,3589.0,4119.25,4649.13,4879.13
Hungary,,1.0,1.0,3.0,3.0,17.0,33.0,61.0,134.0,203.0,293.0,331.0,325.0,329.0,329.0,329.0,329.0,329.0,329.0,323.0,323.0,324.0,324.0
Ireland,116.5,122.9,134.8,210.3,311.2,468.1,651.3,715.3,917.1,1226.1,1365.2,1559.4,1679.15,1898.1,2258.05,2425.95,2776.45,3293.95,3648.65,4101.25,4281.5,4313.84,4593.84
Italy,363.0,664.0,780.0,874.0,1127.0,1635.0,1902.0,2702.0,3525.0,4879.0,5794.0,6918.0,8102.0,8542.0,8683.0,9137.0,9384.0,9736.58,10230.25,10679.46,10870.62,11253.73,11749.73
Latvia,2.0,2.0,22.0,26.0,26.0,26.0,26.0,26.0,28.0,29.0,30.0,36.0,59.0,65.89,68.92,68.17,69.91,77.11,78.17,78.07,78.07,77.13,136.13
Lithuania,,,,,1.0,1.0,31.0,47.0,54.0,98.0,133.0,202.0,275.0,279.0,288.0,436.0,509.0,518.0,533.0,534.0,540.0,671.0,814.0
Luxembourg,14.0,13.9,13.9,20.5,34.9,34.9,34.9,34.9,42.92,42.93,43.73,44.53,58.33,58.33,58.34,63.79,119.69,119.69,122.89,135.79,152.74,136.44,165.44
Montenegro,,,,,,,,,,,,,,,,,,72.0,72.0,118.0,118.0,118.0,118.0
Netherlands,447.0,486.0,672.0,905.0,1075.0,1224.0,1453.0,1641.0,1921.0,1994.0,2009.0,2088.0,2205.0,2485.0,2637.0,3033.84,3300.12,3245.0,3436.11,3527.16,4188.38,5309.87,6176.0
North Macedonia,,,,,,,,,,,,,,,37.0,37.0,37.0,37.0,37.0,37.0,37.0,37.0,37.0
Norway,13.0,13.0,97.0,97.0,152.0,265.0,284.0,348.0,395.0,420.7,422.7,509.7,702.7,815.7,856.7,864.7,880.7,1204.7,1707.7,2911.7,4027.7,5042.7,5067.7
Poland,4.0,19.0,32.0,35.0,40.0,121.0,172.0,306.0,526.0,709.0,1108.0,1800.0,2564.0,3429.0,3836.0,4886.0,5747.0,5759.36,5766.08,5837.76,6298.25,6967.34,7987.34
Portugal,83.0,125.0,190.0,268.0,553.0,1064.0,1681.0,2201.0,2857.0,3326.0,3796.0,4254.35,4409.55,4607.95,4854.56,4934.84,5124.1,5124.1,5172.36,5222.75,5097.26,5402.33,5430.33
Romania,,,,,,1.0,1.0,3.0,5.0,15.0,389.0,988.0,1822.0,2773.0,3244.0,3130.0,3025.0,3029.8,3032.26,3037.52,3012.53,3014.96,3014.96
Serbia,,,,,,,,,,,,,0.5,0.5,0.5,10.4,17.0,25.0,227.0,398.0,398.0,398.0,398.0
Slovakia,,,,3.0,3.0,5.0,5.0,5.0,5.0,3.0,3.0,3.0,3.0,5.0,3.0,3.0,3.0,4.0,3.0,4.0,4.0,4.0,4.0
Slovenia,,,,,,,,,,,,,2.0,2.0,3.0,3.0,3.0,3.3,3.3,3.3,3.3,3.33,3.33
Spain,2206.0,3397.0,4891.0,5945.0,8317.0,9918.0,11722.0,14820.0,16555.0,19176.0,20693.0,21529.0,22789.0,22953.0,22920.0,22938.0,22985.0,23119.48,23400.06,25585.08,26814.19,27902.65,29302.84
Sweden,196.0,273.0,335.0,395.0,453.0,500.0,563.0,692.0,956.0,1312.0,1854.0,2601.0,3443.0,3982.0,4875.0,5606.0,6232.0,6408.0,7097.0,8478.0,9773.0,11923.0,14364.0
Switzerland,3.0,5.0,5.0,5.0,9.0,12.0,12.0,12.0,14.0,18.0,42.0,46.0,49.0,60.0,60.0,60.0,75.0,75.0,75.0,75.0,87.0,87.0,87.0
UK,431.0,490.0,531.0,678.0,809.0,1351.0,1651.0,2083.0,2849.8,3468.0,4080.0,4758.0,6035.0,7586.0,8573.0,9212.0,10833.0,12597.0,13425.0,13999.0,14075.0,14492.0,14832.0

1 Country/area 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022
2 Albania
3 Austria 50 50.0 67 67.0 109 109.0 322 322.0 581 581.0 825.2 825.22 968.3 968.27 991.2 991.16 992 991.97 1001 1000.99 1015.8 1015.83 1106 1105.97 1337.2 1337.15 1674.5 1674.54 2110.3 2110.28 2488.7 2488.73 2730 2730.0 2886.7 3132.7 3132.71 3224.12 3225.98 3407.81 3735.81
4 Belgium 14 14.0 26 26.0 31 31.0 67 67.0 96 96.0 167 167.0 212 212.0 276 276.0 324 324.0 576.5 715.5 872.5 989 985.9 1072.3 1061.3 1236.3 1225.0 1464 1469.3 1657.8 1621.6 1919.3 1902.2 2074.8 2119.0 2308.0 2410.9 2686.6 2989.6
5 Bosnia Herzg 0.3 0.3 0.3 0.3 0.3 0.3 0.3 50.9 51.0 87.0 87.0 135.0 135.0
6 Bulgaria 1 1.0 8 8.0 27 27.0 30 30.0 114 114.0 333 333.0 488 488.0 541 541.0 677 677.0 683 683.0 699 699.0 699 699.0 699 699.0 698.4 698.39 698.9 698.92 703.12 702.8 704.38 704.38
7 Croatia 6 6.0 6 6.0 17 17.0 17 17.0 17 17.0 70 70.0 79 79.0 130 130.0 180 180.0 254 254.0 339 339.0 418 418.0 483 483.0 576.1 586.3 646.3 801.3 986.9 1042.9
8 Czechia 2 2.0 6.4 10.6 16.5 22 22.0 43.5 113.8 150 150.0 193 193.0 213 213.0 213 213.0 258 258.0 262 262.0 278 278.0 281 281.0 282 282.0 308.2 308.21 316.2 339.41 339.42 339.41 339.41
9 Denmark 2340.1 2340.07 2447.2 2680.6 2680.58 2696.6 2696.57 2700.4 2700.36 2704.5 2704.49 2712.3 2712.35 2700.9 2700.86 2739.5 2739.52 2821.2 2821.24 2934 2933.98 3080.5 3080.53 3240.1 3240.09 3547.9 3547.87 3615.4 3615.35 3805.9 3805.92 3974.5 3974.09 4225.8 4225.15 4419.8 4421.86 4409.74 4566.23 4715.24 4782.24
10 Estonia 1 1.0 3 3.0 7 7.0 31 31.0 31 31.0 50 50.0 77 77.0 104 104.0 108 108.0 180 180.0 266 266.0 248 248.0 275 275.0 300 300.0 310 310.0 311.8 310 310.0 316.0 317.0 315.0 315.0
11 Finland 38 38.0 39 39.0 43 43.0 52 52.0 82 82.0 82 82.0 86 86.0 110 110.0 119 119.0 123 123.0 170.7 172.7 230.7 420.7 600.7 973 973.0 1533 1533.0 1971.3 1968.3 2211.0 2513.0 3184.0 5541.0
12 France 38 38.0 66 66.0 138 138.0 218 218.0 358 358.0 690 690.0 1412 1412.0 2223 2223.0 3403 3403.0 4582 4582.0 5912 5912.0 6758 6758.02 7607.5 8156 8155.96 9201.4 9201.42 10298.2 10298.18 11566.6 11566.56 13497.4 13497.35 14898.1 14898.14 16424.85 17512.0 18737.98 20637.98
13 Germany 6095 6095.0 8754 8754.0 12001 12001.0 14381 14381.0 16419 16419.0 18248 18248.0 20474 20474.0 22116 22116.0 22794 22794.0 25697 25697.0 26823 26823.0 28524 28524.0 30711 30711.0 32969 32969.0 37620 37620.0 41297 41297.0 45303 45303.0 50174 50174.0 52447 52328.0 53187.0 54414.0 56046.0 58165.0
14 Greece 226 226.0 270 270.0 287 287.0 371 371.0 470 470.0 491 491.0 749 749.0 846 846.0 1022 1022.0 1171 1171.0 1298 1298.0 1640 1640.0 1753 1753.0 1809 1809.0 1978 1978.0 2091 2091.0 2370 2370.0 2624 2624.0 2877.5 3589.0 4119.25 4649.13 4879.13
15 Hungary 1 1.0 1 1.0 3 3.0 3 3.0 17 17.0 33 33.0 61 61.0 134 134.0 203 203.0 293 293.0 331 331.0 325 325.0 329 329.0 329 329.0 329 329.0 329 329.0 329 329.0 329 329.0 323.0 323.0 324.0 324.0
16 Ireland 116.5 122.9 134.8 210.3 311.2 468.1 651.3 715.3 917.1 1226.1 1365.2 1559.4 1679.2 1679.15 1983 1898.1 2258.1 2258.05 2426 2425.95 2760.8 2776.45 3292.8 3293.95 3650.9 3648.65 4101.25 4281.5 4313.84 4593.84
17 Italy 363 363.0 664 664.0 780 780.0 874 874.0 1127 1127.0 1635 1635.0 1902 1902.0 2702 2702.0 3525 3525.0 4879 4879.0 5794 5794.0 6918 6918.0 8102 8102.0 8542 8542.0 8683 8683.0 9137 9137.0 9384 9384.0 9736.6 9736.58 10230.2 10230.25 10679.46 10870.62 11253.73 11749.73
18 Latvia 2 2.0 2 2.0 22 22.0 26 26.0 26 26.0 26 26.0 26 26.0 26 26.0 28 28.0 29 29.0 30 30.0 36 36.0 59 59.0 65.9 65.89 68.9 68.92 68.2 68.17 69.9 69.91 77.1 77.11 78.2 78.17 78.07 78.07 77.13 136.13
19 Lithuania 1 1.0 1 1.0 31 31.0 47 47.0 54 54.0 98 98.0 133 133.0 202 202.0 275 275.0 279 279.0 288 288.0 436 436.0 509 509.0 518 518.0 533 533.0 534.0 540.0 671.0 814.0
20 Luxembourg 14 14.0 13.9 13.9 20.5 34.9 34.9 34.9 34.9 42.9 42.92 42.9 42.93 43.7 43.73 44.5 44.53 58.3 58.33 58.3 58.33 58.3 58.34 63.8 63.79 119.7 119.69 119.7 119.69 122.9 122.89 135.79 152.74 136.44 165.44
21 Montenegro 72 72.0 118 72.0 118.0 118.0 118.0 118.0
22 Netherlands 447 447.0 486 486.0 672 672.0 905 905.0 1075 1075.0 1224 1224.0 1453 1453.0 1641 1641.0 1921 1921.0 1994 1994.0 2009 2009.0 2088 2088.0 2205 2205.0 2485 2485.0 2637 2637.0 3034 3033.84 3300 3300.12 3245 3245.0 3436 3436.11 3527.16 4188.38 5309.87 6176.0
23 North Macedonia 37 37.0 37 37.0 37 37.0 37 37.0 37 37.0 37.0 37.0 37.0 37.0
24 Norway 13 13.0 13 13.0 97 97.0 97 97.0 152 152.0 265 265.0 284 284.0 348 348.0 395 395.0 420.7 422.7 509.7 702.7 815.7 856.7 864.7 880.7 1204.7 1708 1707.7 2911.7 4027.7 5042.7 5067.7
25 Poland 4 4.0 19 19.0 32 32.0 35 35.0 40 40.0 121 121.0 172 172.0 306 306.0 526 526.0 709 709.0 1108 1108.0 1800 1800.0 2564 2564.0 3429 3429.0 3836 3836.0 4886 4886.0 5747 5747.0 5759.4 5759.36 5766.1 5766.08 5837.76 6298.25 6967.34 7987.34
26 Portugal 83 83.0 125 125.0 190 190.0 268 268.0 553 553.0 1064 1064.0 1681 1681.0 2201 2201.0 2857 2857.0 3326 3326.0 3796 3796.0 4254.4 4254.35 4409.6 4409.55 4607.9 4607.95 4854.6 4854.56 4934.8 4934.84 5124.1 5124.1 5172.4 5172.36 5222.75 5097.26 5402.33 5430.33
27 Romania 1 1.0 1 1.0 3 3.0 5 5.0 15 15.0 389 389.0 988 988.0 1822 1822.0 2773 2773.0 3244 3244.0 3130 3130.0 3025 3025.0 3029.8 3032.3 3032.26 3037.52 3012.53 3014.96 3014.96
28 Serbia 0.5 0.5 0.5 10.4 17 17.0 25 25.0 25 227.0 398.0 398.0 398.0 398.0
29 Slovakia 3 3.0 3 3.0 5 5.0 5 5.0 5 5.0 5 5.0 3 3.0 3 3.0 3 3.0 3 3.0 5 5.0 3 3.0 3 3.0 3 3.0 4 4.0 3 3.0 4.0 4.0 4.0 4.0
30 Slovenia 2.0 4 2.0 4 3.0 5 3.0 5 3.0 5 3.3 5.2 3.3 3.3 3.3 3.33 3.33
31 Spain 2206 2206.0 3397 3397.0 4891 4891.0 5945 5945.0 8317 8317.0 9918 9918.0 11722 11722.0 14820 14820.0 16555 16555.0 19176 19176.0 20693 20693.0 21529 21529.0 22789 22789.0 22953 22953.0 22920 22920.0 22938 22938.0 22985 22985.0 23119.5 23119.48 23400.1 23400.06 25585.08 26814.19 27902.65 29302.84
32 Sweden 196 196.0 273 273.0 335 335.0 395 395.0 453 453.0 500 500.0 563 563.0 692 692.0 956 956.0 1312 1312.0 1854 1854.0 2601 2601.0 3443 3443.0 3982 3982.0 4875 4875.0 5606 5606.0 6232 6232.0 6408 6408.0 7097 7097.0 8478.0 9773.0 11923.0 14364.0
33 Switzerland 3 3.0 5 5.0 5 5.0 5 5.0 9 9.0 12 12.0 12 12.0 12 12.0 14 14.0 18 18.0 42 42.0 46 46.0 49 49.0 60 60.0 60 60.0 60 60.0 75 75.0 75 75.0 75 75.0 75.0 87.0 87.0 87.0
34 UK 408.2 431.0 489.2 490.0 530.2 531.0 678.2 678.0 809.2 809.0 1351.2 1351.0 1651.2 1651.0 2083.2 2083.0 2849.8 3470.8 3468.0 4079.8 4080.0 4758 4758.0 6035 6035.0 7586.3 7586.0 8572.7 8573.0 9212.2 9212.0 10832.3 10833.0 12596.9 12597.0 13553.9 13425.0 13999.0 14075.0 14492.0 14832.0

View File

@ -1,34 +1,34 @@
Country/area,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018
Albania,,0.1,0.2,0.2,0.2,0.2,0.2,0.2,0.2,0.3,0.4,0.6,0.7,0.8,0.9,1.1,1,1,1
Austria,5,7,9,23,27,21,22.4,24.2,30.1,48.9,88.8,174.1,337.5,626,785.2,937.1,1096,1269,1437.6
Belgium,,,1,1,1,2,2,20,62,386,1007,1979,2647,2902,3015.2,3131.7,3327,3616.2,3986.5
Bosnia Herzg,,,,0.1,0.2,0.3,0.3,0.3,0.3,0.3,0.3,0.3,0.3,1.3,7.2,8.2,14.1,16,18.2
Bulgaria,,,,,,,,0,0.1,2,25,154,1013,1020,1026,1029,1028,1035.6,1032.7
Croatia,,,,,,,,,,0.3,0.3,0.3,4,19,33,47.8,55.8,60,67.7
Czechia,0.1,0.1,0.2,0.3,0.4,0.6,0.8,4,39.5,464.6,1727,1913,2022,2063.5,2067.4,2074.9,2067.9,2069.5,2075.1
Denmark,1,1,2,2,2,3,3,3,3,5,7,17,402,571,607,782.1,851,906.4,998
Estonia,,,,,,,,,,0.1,0.1,0.2,0.4,1.5,3.3,6.5,10,15,31.9
Finland,2,3,3,3,4,4,5,5,6,6,7,7,8,9,11,17,39,82,140
France,7,7,8,9,11,13,15,26,80,277,1044,3003.6,4358.8,5277.3,6034.4,7137.5,7702.1,8610.4,9617
Germany,114,195,260,435,1105,2056,2899,4170,6120,10564,18004,25914,34075,36708,37898,39222,40677,42291,45179
Greece,,1,1,1,1,1,5,9,12,46,202,612,1536,2579,2596,2604,2604,2605.5,2651.6
Hungary,,,,,,,,0.4,1,1,2,4,12,35,89,172,235,344,726
Ireland,,,,,,,,,,0.6,0.7,0.8,0.9,1,1.6,2.4,5.9,15.7,24.2
Italy,19,20,22,26,31,34,45,110,483,1264,3592,13131,16785,18185,18594,18901,19283,19682.3,20107.6
Latvia,,,,,,,,,,,,,0.2,0.2,0.2,0.2,0.7,0.7,2
Lithuania,,,,,,,,,0.1,0.1,0.1,0.3,7,68,69,69,70,73.8,82
Luxembourg,,0.2,1.6,14.2,23.6,23.6,23.7,23.9,24.6,26.4,29.5,40.7,74.7,95,109.9,116.3,121.9,128.1,130.6
Montenegro,,,,,,,0,0.2,0.4,0.4,0.6,0.8,0.9,1.1,2.1,2.7,3.1,3.4,3.4
Netherlands,13,21,26,46,50,51,53,54,59,69,90,149,369,746,1048,1515,2049,2903,4522
North Macedonia,,,,,,,,,,,0,2,4,7,15,17,16.7,16.7,20.6
Norway,6,6,6,7,7,7,8,8,8.3,8.7,9.1,9.5,10,11,13,15,26.7,44.9,68.4
Poland,,,,,,,,,,,,1.1,1.3,2.4,27.2,107.8,187.2,287.1,562
Portugal,1,1,1,2,2,2,3,24,59,115,134,172,238,296,415,447,512.8,579.2,667.4
Romania,,,,,,,,,0.1,0.1,0.1,1,41,761,1293,1326,1372,1374.1,1385.8
Serbia,,,,,,0.1,0.2,0.4,0.9,1.2,1.3,1.5,3.1,4.7,6,9,11,10,10
Slovakia,,,,,,,,,,,19,496,513,533,533,533,533,528,472
Slovenia,,,0,0,0,0,0.2,0.6,1,4,12,57,142,187,223,238,233,246.8,221.3
Spain,10,13,17,22,33,52,130,494,3384,3423,3873,4283,4569,4690,4697,4704,4713,4723,4763.5
Sweden,3,3,3,4,4,4,5,6,8,9,11,12,24,43,60,104,153,402,492
Switzerland,16,18,20,22,24,28,30,37,49,79,125,223,437,756,1061,1394,1664,1906,2171
UK,2,3,4,6,8,11,14,18,23,27,95,1000,1753,2937,5528,9601.2,11930.5,12781.8,13118.3
Country/area,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021,2022
Albania,,0.1,0.2,0.2,0.2,0.2,0.2,0.2,0.2,0.3,0.4,0.56,0.68,0.76,0.87,1.05,1.0,1.0,1.0,14.0,21.0,23.0,28.6
Austria,5.0,7.0,9.0,23.0,27.0,18.49,19.61,21.42,27.0,45.56,85.27,169.88,333.09,620.78,779.76,931.56,1089.53,1262.01,1447.94,1694.4,2034.74,2773.91,3538.91
Belgium,,,1.0,1.0,1.0,2.0,2.0,20.0,62.0,386.0,1006.6,1978.6,2646.6,2901.6,3015.0,3131.6,3328.8,3620.6,4000.0,4636.6,5572.8,6012.4,6898.4
Bosnia Herzg,,,,0.1,0.2,0.3,0.3,0.3,0.3,0.3,0.3,0.3,0.35,1.34,7.17,8.17,14.12,16.0,18.15,22.35,34.89,56.51,107.47
Bulgaria,,,,,,,,0.03,0.1,2.0,25.0,154.0,921.99,1038.54,1028.92,1027.89,1029.89,1030.7,1033.06,1044.39,1100.21,1274.71,1948.36
Croatia,,,,,,,,,,0.3,0.3,0.3,4.0,19.0,33.0,47.8,55.8,60.0,67.7,84.8,108.5,138.3,182.3
Czechia,0.1,0.1,0.2,0.3,0.4,0.59,0.84,3.96,39.5,464.6,1727.0,1913.0,2022.0,2063.5,2067.4,2074.9,2067.9,2075.44,2081.05,2110.67,2171.96,2246.09,2627.09
Denmark,1.0,1.0,2.0,2.0,2.0,3.0,3.0,3.0,3.0,5.0,7.0,17.0,402.0,571.0,607.0,782.11,850.95,906.35,998.0,1080.0,1304.29,1704.04,3122.04
Estonia,,,,,,,,,,0.1,0.1,0.2,0.38,1.5,3.34,6.5,10.0,15.0,31.9,120.6,207.67,394.77,534.77
Finland,2.0,3.0,3.0,3.0,4.0,4.0,5.0,5.0,6.0,6.0,7.0,7.0,8.0,9.0,11.0,17.0,39.0,82.0,140.0,222.0,318.0,425.0,590.6
France,7.0,7.0,8.0,9.0,11.0,13.0,15.0,26.0,80.0,277.0,1044.0,3003.57,4358.75,5277.29,6034.42,7137.52,7702.08,8610.44,9638.88,10738.39,11812.2,14436.97,17036.97
Germany,114.0,195.0,260.0,435.0,1105.0,2056.0,2899.0,4170.0,6120.0,10564.0,18004.0,25914.0,34075.0,36708.0,37898.0,39222.0,40677.0,42291.0,45156.0,48912.0,53669.0,59371.0,66662.0
Greece,,1.0,1.0,1.0,1.0,1.0,5.0,9.0,12.0,46.0,202.0,612.0,1536.0,2579.0,2596.0,2604.0,2604.0,2605.53,2651.57,2833.79,3287.72,4277.42,5557.42
Hungary,,,,,,,,0.4,1.0,1.0,2.0,4.0,12.0,35.0,89.0,172.0,235.0,344.0,728.0,1400.0,2131.0,2968.0,2988.0
Ireland,,,,,,,,,,,,,,,,,,,,,,,
Italy,19.0,20.0,22.0,26.0,31.0,34.0,45.0,110.0,483.0,1264.0,3592.0,13131.0,16785.0,18185.0,18594.0,18901.0,19283.0,19682.29,20107.59,20865.28,21650.04,22594.26,25076.56
Latvia,,,,,,,,,,,,,,,,,0.69,0.69,1.96,3.3,5.1,7.16,56.16
Lithuania,,,,,,,,,0.1,0.1,0.1,0.3,7.0,68.0,69.0,69.0,70.0,70.08,72.0,73.0,80.0,84.0,397.0
Luxembourg,,0.16,1.59,14.17,23.56,23.58,23.7,23.93,24.56,26.36,29.45,40.67,74.65,95.02,109.93,116.27,121.9,128.1,130.62,159.74,186.64,277.16,319.16
Montenegro,,,,,,,,,,,,,,,,,,,,,2.57,2.57,22.2
Netherlands,13.0,21.0,26.0,46.0,50.0,51.0,53.0,54.0,59.0,69.0,90.0,149.0,287.0,650.0,1007.0,1526.26,2135.02,2910.89,4608.0,7226.0,11108.43,14910.69,18848.69
North Macedonia,,,,,,,,,,,,2.0,4.0,7.0,15.0,17.0,16.7,16.7,16.7,16.71,84.93,84.93,84.93
Norway,6.0,6.0,6.0,7.0,7.0,7.0,8.0,8.0,8.3,8.7,9.1,9.5,10.0,11.0,13.0,15.0,26.7,44.9,53.11,102.53,141.53,186.53,302.53
Poland,,,,,,,,,,,,1.11,1.3,2.39,27.15,107.78,187.25,287.09,561.98,1539.26,3954.96,7415.52,11166.52
Portugal,1.0,1.0,1.0,2.0,2.0,2.0,3.0,24.0,59.0,115.0,134.0,169.6,235.6,293.6,412.6,441.75,493.05,539.42,617.85,832.74,1010.07,1474.78,2364.78
Romania,,,,,,,,,0.1,0.1,0.1,1.0,41.0,761.0,1293.0,1326.0,1372.0,1374.13,1385.82,1397.71,1382.54,1393.92,1413.92
Serbia,,,,,,0.1,0.2,0.4,0.9,1.2,1.3,1.5,3.1,4.7,6.0,9.0,11.0,10.0,11.0,11.0,11.5,11.94,11.94
Slovakia,,,,,,,,,,,19.0,496.0,513.0,533.0,533.0,533.0,533.0,528.0,472.0,590.0,535.0,537.0,537.0
Slovenia,1.0,1.0,,,,0.05,0.19,0.59,1.0,4.0,12.0,57.0,142.0,187.0,223.0,238.0,233.0,246.8,246.8,277.88,369.78,461.16,632.16
Spain,1.0,3.0,6.0,10.0,19.0,37.0,113.0,476.0,3365.0,3403.0,3851.0,4260.0,4545.0,4665.0,4672.0,4677.0,4687.0,4696.0,4730.7,8772.02,10100.42,13678.4,18176.73
Sweden,3.0,3.0,3.0,4.0,4.0,4.0,5.0,6.0,8.0,9.0,11.0,12.0,24.0,43.0,60.0,104.0,153.0,231.0,411.0,698.0,1090.0,1587.0,2587.0
Switzerland,16.0,18.0,20.0,22.0,24.0,28.0,30.0,37.0,49.0,79.0,125.0,223.0,437.0,756.0,1061.0,1394.0,1664.0,1906.0,2173.0,2498.0,2973.0,3655.0,4339.92
UK,2.0,3.0,4.0,6.0,8.0,11.0,14.0,18.0,23.0,27.0,95.0,1000.0,1753.0,2937.0,5528.0,9601.0,11914.0,12760.0,13059.0,13345.0,13579.0,13965.0,14660.0

1 Country/area 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022
2 Albania 0.1 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.3 0.4 0.6 0.56 0.7 0.68 0.8 0.76 0.9 0.87 1.1 1.05 1 1.0 1 1.0 1 1.0 14.0 21.0 23.0 28.6
3 Austria 5 5.0 7 7.0 9 9.0 23 23.0 27 27.0 21 18.49 22.4 19.61 24.2 21.42 30.1 27.0 48.9 45.56 88.8 85.27 174.1 169.88 337.5 333.09 626 620.78 785.2 779.76 937.1 931.56 1096 1089.53 1269 1262.01 1437.6 1447.94 1694.4 2034.74 2773.91 3538.91
4 Belgium 1 1.0 1 1.0 1 1.0 2 2.0 2 2.0 20 20.0 62 62.0 386 386.0 1007 1006.6 1979 1978.6 2647 2646.6 2902 2901.6 3015.2 3015.0 3131.7 3131.6 3327 3328.8 3616.2 3620.6 3986.5 4000.0 4636.6 5572.8 6012.4 6898.4
5 Bosnia Herzg 0.1 0.2 0.3 0.3 0.3 0.3 0.3 0.3 0.3 0.3 0.35 1.3 1.34 7.2 7.17 8.2 8.17 14.1 14.12 16 16.0 18.2 18.15 22.35 34.89 56.51 107.47
6 Bulgaria 0 0.03 0.1 2 2.0 25 25.0 154 154.0 1013 921.99 1020 1038.54 1026 1028.92 1029 1027.89 1028 1029.89 1035.6 1030.7 1032.7 1033.06 1044.39 1100.21 1274.71 1948.36
7 Croatia 0.3 0.3 0.3 4 4.0 19 19.0 33 33.0 47.8 55.8 60 60.0 67.7 84.8 108.5 138.3 182.3
8 Czechia 0.1 0.1 0.2 0.3 0.4 0.6 0.59 0.8 0.84 4 3.96 39.5 464.6 1727 1727.0 1913 1913.0 2022 2022.0 2063.5 2067.4 2074.9 2067.9 2069.5 2075.44 2075.1 2081.05 2110.67 2171.96 2246.09 2627.09
9 Denmark 1 1.0 1 1.0 2 2.0 2 2.0 2 2.0 3 3.0 3 3.0 3 3.0 3 3.0 5 5.0 7 7.0 17 17.0 402 402.0 571 571.0 607 607.0 782.1 782.11 851 850.95 906.4 906.35 998 998.0 1080.0 1304.29 1704.04 3122.04
10 Estonia 0.1 0.1 0.2 0.4 0.38 1.5 3.3 3.34 6.5 10 10.0 15 15.0 31.9 120.6 207.67 394.77 534.77
11 Finland 2 2.0 3 3.0 3 3.0 3 3.0 4 4.0 4 4.0 5 5.0 5 5.0 6 6.0 6 6.0 7 7.0 7 7.0 8 8.0 9 9.0 11 11.0 17 17.0 39 39.0 82 82.0 140 140.0 222.0 318.0 425.0 590.6
12 France 7 7.0 7 7.0 8 8.0 9 9.0 11 11.0 13 13.0 15 15.0 26 26.0 80 80.0 277 277.0 1044 1044.0 3003.6 3003.57 4358.8 4358.75 5277.3 5277.29 6034.4 6034.42 7137.5 7137.52 7702.1 7702.08 8610.4 8610.44 9617 9638.88 10738.39 11812.2 14436.97 17036.97
13 Germany 114 114.0 195 195.0 260 260.0 435 435.0 1105 1105.0 2056 2056.0 2899 2899.0 4170 4170.0 6120 6120.0 10564 10564.0 18004 18004.0 25914 25914.0 34075 34075.0 36708 36708.0 37898 37898.0 39222 39222.0 40677 40677.0 42291 42291.0 45179 45156.0 48912.0 53669.0 59371.0 66662.0
14 Greece 1 1.0 1 1.0 1 1.0 1 1.0 1 1.0 5 5.0 9 9.0 12 12.0 46 46.0 202 202.0 612 612.0 1536 1536.0 2579 2579.0 2596 2596.0 2604 2604.0 2604 2604.0 2605.5 2605.53 2651.6 2651.57 2833.79 3287.72 4277.42 5557.42
15 Hungary 0.4 1 1.0 1 1.0 2 2.0 4 4.0 12 12.0 35 35.0 89 89.0 172 172.0 235 235.0 344 344.0 726 728.0 1400.0 2131.0 2968.0 2988.0
16 Ireland 0.6 0.7 0.8 0.9 1 1.6 2.4 5.9 15.7 24.2
17 Italy 19 19.0 20 20.0 22 22.0 26 26.0 31 31.0 34 34.0 45 45.0 110 110.0 483 483.0 1264 1264.0 3592 3592.0 13131 13131.0 16785 16785.0 18185 18185.0 18594 18594.0 18901 18901.0 19283 19283.0 19682.3 19682.29 20107.6 20107.59 20865.28 21650.04 22594.26 25076.56
18 Latvia 0.2 0.2 0.2 0.2 0.7 0.69 0.7 0.69 2 1.96 3.3 5.1 7.16 56.16
19 Lithuania 0.1 0.1 0.1 0.3 7 7.0 68 68.0 69 69.0 69 69.0 70 70.0 73.8 70.08 82 72.0 73.0 80.0 84.0 397.0
20 Luxembourg 0.2 0.16 1.6 1.59 14.2 14.17 23.6 23.56 23.6 23.58 23.7 23.9 23.93 24.6 24.56 26.4 26.36 29.5 29.45 40.7 40.67 74.7 74.65 95 95.02 109.9 109.93 116.3 116.27 121.9 128.1 130.6 130.62 159.74 186.64 277.16 319.16
21 Montenegro 0 0.2 0.4 0.4 0.6 0.8 0.9 1.1 2.1 2.7 3.1 3.4 3.4 2.57 2.57 22.2
22 Netherlands 13 13.0 21 21.0 26 26.0 46 46.0 50 50.0 51 51.0 53 53.0 54 54.0 59 59.0 69 69.0 90 90.0 149 149.0 369 287.0 746 650.0 1048 1007.0 1515 1526.26 2049 2135.02 2903 2910.89 4522 4608.0 7226.0 11108.43 14910.69 18848.69
23 North Macedonia 0 2 2.0 4 4.0 7 7.0 15 15.0 17 17.0 16.7 16.7 20.6 16.7 16.71 84.93 84.93 84.93
24 Norway 6 6.0 6 6.0 6 6.0 7 7.0 7 7.0 7 7.0 8 8.0 8 8.0 8.3 8.7 9.1 9.5 10 10.0 11 11.0 13 13.0 15 15.0 26.7 44.9 68.4 53.11 102.53 141.53 186.53 302.53
25 Poland 1.1 1.11 1.3 2.4 2.39 27.2 27.15 107.8 107.78 187.2 187.25 287.1 287.09 562 561.98 1539.26 3954.96 7415.52 11166.52
26 Portugal 1 1.0 1 1.0 1 1.0 2 2.0 2 2.0 2 2.0 3 3.0 24 24.0 59 59.0 115 115.0 134 134.0 172 169.6 238 235.6 296 293.6 415 412.6 447 441.75 512.8 493.05 579.2 539.42 667.4 617.85 832.74 1010.07 1474.78 2364.78
27 Romania 0.1 0.1 0.1 1 1.0 41 41.0 761 761.0 1293 1293.0 1326 1326.0 1372 1372.0 1374.1 1374.13 1385.8 1385.82 1397.71 1382.54 1393.92 1413.92
28 Serbia 0.1 0.2 0.4 0.9 1.2 1.3 1.5 3.1 4.7 6 6.0 9 9.0 11 11.0 10 10.0 10 11.0 11.0 11.5 11.94 11.94
29 Slovakia 19 19.0 496 496.0 513 513.0 533 533.0 533 533.0 533 533.0 533 533.0 528 528.0 472 472.0 590.0 535.0 537.0 537.0
30 Slovenia 1.0 1.0 0 0 0 0 0.05 0.2 0.19 0.6 0.59 1 1.0 4 4.0 12 12.0 57 57.0 142 142.0 187 187.0 223 223.0 238 238.0 233 233.0 246.8 221.3 246.8 277.88 369.78 461.16 632.16
31 Spain 10 1.0 13 3.0 17 6.0 22 10.0 33 19.0 52 37.0 130 113.0 494 476.0 3384 3365.0 3423 3403.0 3873 3851.0 4283 4260.0 4569 4545.0 4690 4665.0 4697 4672.0 4704 4677.0 4713 4687.0 4723 4696.0 4763.5 4730.7 8772.02 10100.42 13678.4 18176.73
32 Sweden 3 3.0 3 3.0 3 3.0 4 4.0 4 4.0 4 4.0 5 5.0 6 6.0 8 8.0 9 9.0 11 11.0 12 12.0 24 24.0 43 43.0 60 60.0 104 104.0 153 153.0 402 231.0 492 411.0 698.0 1090.0 1587.0 2587.0
33 Switzerland 16 16.0 18 18.0 20 20.0 22 22.0 24 24.0 28 28.0 30 30.0 37 37.0 49 49.0 79 79.0 125 125.0 223 223.0 437 437.0 756 756.0 1061 1061.0 1394 1394.0 1664 1664.0 1906 1906.0 2171 2173.0 2498.0 2973.0 3655.0 4339.92
34 UK 2 2.0 3 3.0 4 4.0 6 6.0 8 8.0 11 11.0 14 14.0 18 18.0 23 23.0 27 27.0 95 95.0 1000 1000.0 1753 1753.0 2937 2937.0 5528 5528.0 9601.2 9601.0 11930.5 11914.0 12781.8 12760.0 13118.3 13059.0 13345.0 13579.0 13965.0 14660.0

View File

@ -1,4 +1,5 @@
,Unit,Values,Description
focus_weights,,,Optionally specify the focus weights for the clustering of countries. For instance: `DE: 0.8` will distribute 80% of all nodes to Germany and 20% to the rest of the countries.
simplify_network,,,
-- to_substations,bool,"{'true','false'}","Aggregates all nodes without power injection (positive or negative, i.e. demand or generation) to electrically closest ones"
-- algorithm,str,"One of {kmeans, hac, modularity}",

1 Unit Values Description
2 focus_weights Optionally specify the focus weights for the clustering of countries. For instance: `DE: 0.8` will distribute 80% of all nodes to Germany and 20% to the rest of the countries.
3 simplify_network
4 -- to_substations bool {'true','false'} Aggregates all nodes without power injection (positive or negative, i.e. demand or generation) to electrically closest ones
5 -- algorithm str One of {‘kmeans’, ‘hac’, ‘modularity‘}

View File

@ -5,6 +5,7 @@ retrieve_databundle,bool,"{true, false}","Switch to retrieve databundle from zen
retrieve_sector_databundle,bool,"{true, false}","Switch to retrieve sector databundle from zenodo via the rule :mod:`retrieve_sector_databundle` or whether to keep a custom databundle located in the corresponding folder."
retrieve_cost_data,bool,"{true, false}","Switch to retrieve technology cost data from `technology-data repository <https://github.com/PyPSA/technology-data>`_."
build_cutout,bool,"{true, false}","Switch to enable the building of cutouts via the rule :mod:`build_cutout`."
retrieve_irena,bool,"{true, false}",Switch to enable the retrieval of ``existing_capacities`` from IRENASTAT with :mod:`retrieve_irena`.
retrieve_cutout,bool,"{true, false}","Switch to enable the retrieval of cutouts from zenodo with :mod:`retrieve_cutout`."
build_natura_raster,bool,"{true, false}","Switch to enable the creation of the raster ``natura.tiff`` via the rule :mod:`build_natura_raster`."
retrieve_natura_raster,bool,"{true, false}","Switch to enable the retrieval of ``natura.tiff`` from zenodo with :mod:`retrieve_natura_raster`."

1 Unit Values Description
5 retrieve_sector_databundle bool {true, false} Switch to retrieve sector databundle from zenodo via the rule :mod:`retrieve_sector_databundle` or whether to keep a custom databundle located in the corresponding folder.
6 retrieve_cost_data bool {true, false} Switch to retrieve technology cost data from `technology-data repository <https://github.com/PyPSA/technology-data>`_.
7 build_cutout bool {true, false} Switch to enable the building of cutouts via the rule :mod:`build_cutout`.
8 retrieve_irena bool {true, false} Switch to enable the retrieval of ``existing_capacities`` from IRENASTAT with :mod:`retrieve_irena`.
9 retrieve_cutout bool {true, false} Switch to enable the retrieval of cutouts from zenodo with :mod:`retrieve_cutout`.
10 build_natura_raster bool {true, false} Switch to enable the creation of the raster ``natura.tiff`` via the rule :mod:`build_natura_raster`.
11 retrieve_natura_raster bool {true, false} Switch to enable the retrieval of ``natura.tiff`` from zenodo with :mod:`retrieve_natura_raster`.

View File

@ -5,7 +5,7 @@
"naturalearth/*",,,,,http://www.naturalearthdata.com/about/terms-of-use/
"NUTS_2013 _60M_SH/*","x","x",,"x",https://ec.europa.eu/eurostat/web/gisco/geodata/reference-data/administrative-units-statistical-units
"cantons.csv","x",,"x",,https://en.wikipedia.org/wiki/Data_codes_for_Switzerland
"EIA_hydro_generation _2000_2014.csv","x",,,,https://www.eia.gov/about/copyrights_reuse.php
"eia_hydro_annual_generation.csv","x",,,,https://www.eia.gov/about/copyrights_reuse.php
"GEBCO_2014_2D.nc","x",,,,https://www.gebco.net/data_and_products/gridded_bathymetry_data/documents/gebco_2014_historic.pdf
"hydro_capacities.csv","x",,,,
"je-e-21.03.02.xls","x","x",,,https://www.bfs.admin.ch/bfs/en/home/fso/swiss-federal-statistical-office/terms-of-use.html

1 Files BY NC SA Mark Changes Detail
5 naturalearth/* http://www.naturalearthdata.com/about/terms-of-use/
6 NUTS_2013 _60M_SH/* x x x https://ec.europa.eu/eurostat/web/gisco/geodata/reference-data/administrative-units-statistical-units
7 cantons.csv x x https://en.wikipedia.org/wiki/Data_codes_for_Switzerland
8 EIA_hydro_generation _2000_2014.csv eia_hydro_annual_generation.csv x https://www.eia.gov/about/copyrights_reuse.php
9 GEBCO_2014_2D.nc x https://www.gebco.net/data_and_products/gridded_bathymetry_data/documents/gebco_2014_historic.pdf
10 hydro_capacities.csv x
11 je-e-21.03.02.xls x x https://www.bfs.admin.ch/bfs/en/home/fso/swiss-federal-statistical-office/terms-of-use.html

View File

@ -5,6 +5,7 @@ s_nom_max,MW,"float","Global upper limit for the maximum capacity of each extend
max_extension,MW,"float","Upper limit for the extended capacity of each extendable line."
length_factor,--,float,"Correction factor to account for the fact that buses are *not* connected by lines through air-line distance."
under_construction,--,"One of {'zero': set capacity to zero, 'remove': remove completely, 'keep': keep with full capacity}","Specifies how to handle lines which are currently under construction."
reconnect_crimea,--,"true or false","Whether to reconnect Crimea to the Ukrainian grid"
dynamic_line_rating,,,
-- activate,bool,"true or false","Whether to take dynamic line rating into account"
-- cutout,--,"Should be a folder listed in the configuration ``atlite: cutouts:`` (e.g. 'europe-2013-era5') or reference an existing folder in the directory ``cutouts``. Source module must be ERA5.","Specifies the directory where the relevant weather data ist stored."

1 Unit Values Description
5 max_extension MW float Upper limit for the extended capacity of each extendable line.
6 length_factor -- float Correction factor to account for the fact that buses are *not* connected by lines through air-line distance.
7 under_construction -- One of {'zero': set capacity to zero, 'remove': remove completely, 'keep': keep with full capacity} Specifies how to handle lines which are currently under construction.
8 reconnect_crimea -- true or false Whether to reconnect Crimea to the Ukrainian grid
9 dynamic_line_rating
10 -- activate bool true or false Whether to take dynamic line rating into account
11 -- cutout -- Should be a folder listed in the configuration ``atlite: cutouts:`` (e.g. 'europe-2013-era5') or reference an existing folder in the directory ``cutouts``. Source module must be ERA5. Specifies the directory where the relevant weather data ist stored.

View File

@ -79,6 +79,7 @@ allam_cycle,--,"{true, false}",Add option to include `Allam cycle gas power plan
hydrogen_fuel_cell,--,"{true, false}",Add option to include hydrogen fuel cell for re-electrification. Assuming OCGT technology costs
hydrogen_turbine,--,"{true, false}",Add option to include hydrogen turbine for re-electrification. Assuming OCGT technology costs
SMR,--,"{true, false}",Add option for transforming natural gas into hydrogen and CO2 using Steam Methane Reforming (SMR)
SMR CC,--,"{true, false}",Add option for transforming natural gas into hydrogen and CO2 using Steam Methane Reforming (SMR) and Carbon Capture (CC)
regional_co2 _sequestration_potential,,,
-- enable,--,"{true, false}",Add option for regionally-resolved geological carbon dioxide sequestration potentials based on `CO2StoP <https://setis.ec.europa.eu/european-co2-storage-database_en>`_.
-- attribute,--,string,Name of the attribute for the sequestration potential
@ -117,6 +118,7 @@ gas_distribution_grid _cost_factor,,,Multiplier for the investment cost of the g
,,,
biomass_spatial,--,"{true, false}",Add option for resolving biomass demand regionally
biomass_transport,--,"{true, false}",Add option for transporting solid biomass between nodes
biogas_upgrading_cc,--,"{true, false}",Add option to capture CO2 from biomass upgrading
conventional_generation,,,Add a more detailed description of conventional carriers. Any power generation requires the consumption of fuel from nodes representing that fuel.
biomass_to_liquid,--,"{true, false}",Add option for transforming solid biomass into liquid fuel with the same properties as oil
biosng,--,"{true, false}",Add option for transforming solid biomass into synthesis gas with the same properties as natural gas

1 Unit Values Description
79 hydrogen_fuel_cell -- {true, false} Add option to include hydrogen fuel cell for re-electrification. Assuming OCGT technology costs
80 hydrogen_turbine -- {true, false} Add option to include hydrogen turbine for re-electrification. Assuming OCGT technology costs
81 SMR -- {true, false} Add option for transforming natural gas into hydrogen and CO2 using Steam Methane Reforming (SMR)
82 SMR CC -- {true, false} Add option for transforming natural gas into hydrogen and CO2 using Steam Methane Reforming (SMR) and Carbon Capture (CC)
83 regional_co2 _sequestration_potential
84 -- enable -- {true, false} Add option for regionally-resolved geological carbon dioxide sequestration potentials based on `CO2StoP <https://setis.ec.europa.eu/european-co2-storage-database_en>`_.
85 -- attribute -- string Name of the attribute for the sequestration potential
118
119 biomass_spatial -- {true, false} Add option for resolving biomass demand regionally
120 biomass_transport -- {true, false} Add option for transporting solid biomass between nodes
121 biogas_upgrading_cc -- {true, false} Add option to capture CO2 from biomass upgrading
122 conventional_generation Add a more detailed description of conventional carriers. Any power generation requires the consumption of fuel from nodes representing that fuel.
123 biomass_to_liquid -- {true, false} Add option for transforming solid biomass into liquid fuel with the same properties as oil
124 biosng -- {true, false} Add option for transforming solid biomass into synthesis gas with the same properties as natural gas

View File

@ -41,10 +41,10 @@ Perfect foresight scenarios
.. warning::
Perfect foresight is currently under development and not yet implemented.
Perfect foresight is currently implemented as a first test version.
For running perfect foresight scenarios, in future versions you will be able to
set in the ``config/config.yaml``:
For running perfect foresight scenarios, you can adjust the
``config/config.perfect.yaml``:
.. code:: yaml

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 789 KiB

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@ -116,7 +116,7 @@ of the individual parts.
topics we are working on. Please feel free to help or make suggestions.
This project is currently maintained by the `Department of Digital
Transformation in Energy Systems <https:/www.ensys.tu-berlin.de>`_ at the
Transformation in Energy Systems <https://www.tu.berlin/en/ensys>`_ at the
`Technische Universität Berlin <https://www.tu.berlin>`_. Previous versions were
developed within the `IAI <http://www.iai.kit.edu>`_ at the `Karlsruhe Institute
of Technology (KIT) <http://www.kit.edu/english/index.php>`_ which was funded by
@ -185,7 +185,7 @@ For sector-coupling studies: ::
pages = "1--25"
year = "2023",
eprint = "2207.05816",
doi = "10.1016/j.joule.2022.04.016",
doi = "10.1016/j.joule.2023.06.016",
}
For sector-coupling studies with pathway optimisation: ::

View File

@ -10,6 +10,9 @@ Release Notes
Upcoming Release
================
* Pin ``snakemake`` version to below 8.0.0, as the new version is not yet
supported by ``pypsa-eur``.
* Updated Global Energy Monitor LNG terminal data to March 2023 version.
* For industry distribution, use EPRTR as fallback if ETS data is not available.
@ -20,6 +23,8 @@ Upcoming Release
* Files extracted from sector-coupled data bundle have been moved from ``data/`` to ``data/sector-bundle``.
* New feature multi-decade optimisation with perfect foresight.
* It is now possible to specify years for biomass potentials which do not exist
in the JRC-ENSPRESO database, e.g. 2037. These are linearly interpolated.
@ -27,10 +32,59 @@ Upcoming Release
* Rule ``purge`` now initiates a dialog to confirm if purge is desired.
* Rule ``retrieve_irena`` get updated values for renewables capacities.
* Rule ``retrieve_wdpa`` updated to not only check for current and previous, but also potentially next months dataset availability.
* Split configuration to enable SMR and SMR CC.
* Bugfix: The unit of the capital cost of Haber-Bosch plants was corrected.
* The configuration setting for country focus weights when clustering the
network has been moved from ``focus_weights:`` to ``clustering:
focus_weights:``. Backwards compatibility to old config files is maintained.
* Extend options for waste usage from Haber-Bosch, methanolisation and methanation.
* Use electrolysis waste heat by default.
* Add new ``sector_opts`` wildcard option "nowasteheat" to disable all waste heat usage.
* Set minimum part loads for PtX processes to 30% for methanolisation and methanation, and to 70% for Fischer-Tropsch synthesis.
* Add VOM as marginal cost to PtX processes.
* Add pelletizing costs for biomass boilers.
* The ``mock_snakemake`` function can now be used with a Snakefile from a different directory using the new ``root_dir`` argument.
* Switch to using hydrogen and electricity inputs for Haber-Bosch from https://github.com/PyPSA/technology-data.
* Add option to capture CO2 contained in biogas when upgrading (``sector: biogas_to_gas_cc``).
* Merged option to extend geographical scope to Ukraine and Moldova. These
countries are excluded by default and is currently constrained to power-sector
only parts of the workflow. A special config file
`config/config.entsoe-all.yaml` was added as an example to run the workflow
with all ENTSO-E member countries (including observer members like Ukraine and
Moldova). Moldova can currently only be included in conjunction with Ukraine
due to the absence of demand data. The Crimean power system is manually
reconnected to the main Ukrainian grid with the configuration option
`reconnect_crimea`.
* Validate downloads from Zenodo using MD5 checksums. This identifies corrupted
or incomplete downloads.
* Add locations, capacities and costs of existing gas storage using Global
Energy Monitor's `Europe Gas Tracker
<https://globalenergymonitor.org/projects/europe-gas-tracker>`_.
**Bugs and Compatibility**
* A bug preventing custom powerplants specified in ``data/custom_powerplants.csv`` was fixed. (https://github.com/PyPSA/pypsa-eur/pull/732)
* Fix nodal fraction in ``add_existing_year`` when using distributed generators
* Fix typo in buses definition for oil boilers in ``add_industry`` in ``prepare_sector_network``
PyPSA-Eur 0.8.1 (27th July 2023)
================================
@ -155,6 +209,8 @@ PyPSA-Eur 0.8.1 (27th July 2023)
(https://github.com/PyPSA/pypsa-eur/pull/672)
* Addressed deprecation warnings for ``pandas=2.0``. ``pandas=2.0`` is now minimum requirement.
PyPSA-Eur 0.8.0 (18th March 2023)
=================================

View File

@ -22,11 +22,11 @@ Rule ``retrieve_databundle``
Rule ``retrieve_cutout``
============================
.. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.3517949.svg
:target: https://doi.org/10.5281/zenodo.3517949
.. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.6382570.svg
:target: https://doi.org/10.5281/zenodo.6382570
Cutouts are spatio-temporal subsets of the European weather data from the `ECMWF ERA5 <https://software.ecmwf.int/wiki/display/CKB/ERA5+data+documentation>`_ reanalysis dataset and the `CMSAF SARAH-2 <https://wui.cmsaf.eu/safira/action/viewDoiDetails?acronym=SARAH_V002>`_ solar surface radiation dataset for the year 2013.
They have been prepared by and are for use with the `atlite <https://github.com/PyPSA/atlite>`_ tool. You can either generate them yourself using the ``build_cutouts`` rule or retrieve them directly from `zenodo <https://doi.org/10.5281/zenodo.3517949>`__ through the rule ``retrieve_cutout``.
They have been prepared by and are for use with the `atlite <https://github.com/PyPSA/atlite>`_ tool. You can either generate them yourself using the ``build_cutouts`` rule or retrieve them directly from `zenodo <https://doi.org/10.5281/zenodo.6382570>`__ through the rule ``retrieve_cutout``.
The :ref:`tutorial` uses a smaller cutout than required for the full model (30 MB), which is also automatically downloaded.
.. note::
@ -91,7 +91,7 @@ None.
**Outputs**
- ``data/load_raw.csv``
- ``resources/load_raw.csv``
Rule ``retrieve_cost_data``
@ -118,6 +118,11 @@ This rule downloads techno-economic assumptions from the `technology-data reposi
- ``resources/costs.csv``
Rule ``retrieve_irena``
================================
.. automodule:: retrieve_irena
Rule ``retrieve_ship_raster``
================================

View File

@ -25,7 +25,7 @@ full model, which allows the user to explore most of its functionalities on a
local machine. The tutorial will cover examples on how to configure and
customise the PyPSA-Eur model and run the ``snakemake`` workflow step by step
from network creation to the solved network. The configuration for the tutorial
is located at ``test/config.electricity.yaml``. It includes parts deviating from
is located at ``config/test/config.electricity.yaml``. It includes parts deviating from
the default config file ``config/config.default.yaml``. To run the tutorial with this
configuration, execute
@ -96,7 +96,7 @@ open-source solver GLPK.
:start-at: solver:
:end-before: plotting:
Note, that ``test/config.electricity.yaml`` only includes changes relative to
Note, that ``config/test/config.electricity.yaml`` only includes changes relative to
the default configuration. There are many more configuration options, which are
documented at :ref:`config`.
@ -133,89 +133,82 @@ This triggers a workflow of multiple preceding jobs that depend on each rule's i
graph[bgcolor=white, margin=0];
node[shape=box, style=rounded, fontname=sans, fontsize=10, penwidth=2];
edge[penwidth=2, color=grey];
0[label = "solve_network", color = "0.21 0.6 0.85", style="rounded"];
1[label = "prepare_network\nll: copt\nopts: Co2L-24H", color = "0.02 0.6 0.85", style="rounded"];
2[label = "add_extra_components", color = "0.37 0.6 0.85", style="rounded"];
3[label = "cluster_network\nclusters: 6", color = "0.39 0.6 0.85", style="rounded"];
4[label = "simplify_network\nsimpl: ", color = "0.11 0.6 0.85", style="rounded"];
5[label = "add_electricity", color = "0.23 0.6 0.85", style="rounded"];
6[label = "build_renewable_profiles\ntechnology: onwind", color = "0.57 0.6 0.85", style="rounded"];
7[label = "base_network", color = "0.09 0.6 0.85", style="rounded"];
8[label = "build_shapes", color = "0.41 0.6 0.85", style="rounded"];
9[label = "retrieve_databundle", color = "0.28 0.6 0.85", style="rounded"];
10[label = "retrieve_natura_raster", color = "0.62 0.6 0.85", style="rounded"];
11[label = "build_bus_regions", color = "0.53 0.6 0.85", style="rounded"];
12[label = "retrieve_cutout\ncutout: europe-2013-era5", color = "0.05 0.6 0.85", style="rounded,dashed"];
13[label = "build_renewable_profiles\ntechnology: offwind-ac", color = "0.57 0.6 0.85", style="rounded"];
14[label = "build_ship_raster", color = "0.64 0.6 0.85", style="rounded"];
15[label = "retrieve_ship_raster", color = "0.07 0.6 0.85", style="rounded,dashed"];
16[label = "retrieve_cutout\ncutout: europe-2013-sarah", color = "0.05 0.6 0.85", style="rounded,dashed"];
17[label = "build_renewable_profiles\ntechnology: offwind-dc", color = "0.57 0.6 0.85", style="rounded"];
18[label = "build_renewable_profiles\ntechnology: solar", color = "0.57 0.6 0.85", style="rounded"];
19[label = "build_hydro_profile", color = "0.44 0.6 0.85", style="rounded"];
20[label = "retrieve_cost_data", color = "0.30 0.6 0.85", style="rounded"];
21[label = "build_powerplants", color = "0.16 0.6 0.85", style="rounded"];
22[label = "build_electricity_demand", color = "0.00 0.6 0.85", style="rounded"];
23[label = "retrieve_electricity_demand", color = "0.34 0.6 0.85", style="rounded,dashed"];
0[label = "solve_network", color = "0.33 0.6 0.85", style="rounded"];
1[label = "prepare_network\nll: copt\nopts: Co2L-24H", color = "0.03 0.6 0.85", style="rounded"];
2[label = "add_extra_components", color = "0.45 0.6 0.85", style="rounded"];
3[label = "cluster_network\nclusters: 6", color = "0.46 0.6 0.85", style="rounded"];
4[label = "simplify_network\nsimpl: ", color = "0.52 0.6 0.85", style="rounded"];
5[label = "add_electricity", color = "0.55 0.6 0.85", style="rounded"];
6[label = "build_renewable_profiles\ntechnology: solar", color = "0.15 0.6 0.85", style="rounded"];
7[label = "base_network", color = "0.37 0.6 0.85", style="rounded,dashed"];
8[label = "build_shapes", color = "0.07 0.6 0.85", style="rounded,dashed"];
9[label = "retrieve_databundle", color = "0.60 0.6 0.85", style="rounded"];
10[label = "retrieve_natura_raster", color = "0.42 0.6 0.85", style="rounded"];
11[label = "build_bus_regions", color = "0.09 0.6 0.85", style="rounded,dashed"];
12[label = "build_renewable_profiles\ntechnology: onwind", color = "0.15 0.6 0.85", style="rounded"];
13[label = "build_renewable_profiles\ntechnology: offwind-ac", color = "0.15 0.6 0.85", style="rounded"];
14[label = "build_ship_raster", color = "0.02 0.6 0.85", style="rounded"];
15[label = "retrieve_ship_raster", color = "0.40 0.6 0.85", style="rounded"];
16[label = "build_renewable_profiles\ntechnology: offwind-dc", color = "0.15 0.6 0.85", style="rounded"];
17[label = "build_line_rating", color = "0.32 0.6 0.85", style="rounded"];
18[label = "retrieve_cost_data\nyear: 2030", color = "0.50 0.6 0.85", style="rounded"];
19[label = "build_powerplants", color = "0.64 0.6 0.85", style="rounded,dashed"];
20[label = "build_electricity_demand", color = "0.13 0.6 0.85", style="rounded,dashed"];
21[label = "retrieve_electricity_demand", color = "0.31 0.6 0.85", style="rounded"];
22[label = "copy_config", color = "0.23 0.6 0.85", style="rounded"];
1 -> 0
22 -> 0
2 -> 1
20 -> 1
18 -> 1
3 -> 2
20 -> 2
18 -> 2
4 -> 3
20 -> 3
18 -> 3
5 -> 4
20 -> 4
18 -> 4
11 -> 4
6 -> 5
12 -> 5
13 -> 5
16 -> 5
7 -> 5
17 -> 5
18 -> 5
19 -> 5
7 -> 5
20 -> 5
11 -> 5
21 -> 5
19 -> 5
9 -> 5
22 -> 5
20 -> 5
8 -> 5
7 -> 6
9 -> 6
10 -> 6
8 -> 6
11 -> 6
12 -> 6
8 -> 7
9 -> 8
8 -> 11
7 -> 11
7 -> 12
9 -> 12
10 -> 12
8 -> 12
11 -> 12
7 -> 13
9 -> 13
10 -> 13
14 -> 13
8 -> 13
11 -> 13
12 -> 13
15 -> 14
12 -> 14
16 -> 14
7 -> 16
9 -> 16
10 -> 16
14 -> 16
8 -> 16
11 -> 16
7 -> 17
9 -> 17
10 -> 17
14 -> 17
8 -> 17
11 -> 17
12 -> 17
7 -> 18
9 -> 18
10 -> 18
8 -> 18
11 -> 18
16 -> 18
8 -> 19
12 -> 19
7 -> 21
23 -> 22
7 -> 19
21 -> 20
}
|

View File

@ -11,6 +11,8 @@ dependencies:
- pip
- atlite>=0.2.9
- pypsa>=0.26.1
- linopy
- dask
# Dependencies of the workflow itself
@ -18,23 +20,24 @@ dependencies:
- openpyxl!=3.1.1
- pycountry
- seaborn
- snakemake-minimal>=7.7.0
# snakemake 8 introduced a number of breaking changes which the workflow has yet to be made compatible with
- snakemake-minimal>=7.7.0,<8.0.0
- memory_profiler
- yaml
- pytables
- lxml
- powerplantmatching>=0.5.5
- numpy
- pandas>=1.4
- pandas>=2.1
- geopandas>=0.11.0
- xarray
- xarray>=2023.11.0
- rioxarray
- netcdf4
- networkx
- scipy
- shapely>=2.0
- pyomo
- matplotlib<3.6
- matplotlib
- proj
- fiona
- country_converter
@ -44,6 +47,7 @@ dependencies:
- tabula-py
- pyxlsb
- graphviz
- ipopt
# Keep in conda environment when calling ipython
- ipython
@ -55,5 +59,4 @@ dependencies:
- pip:
- tsam>=1.1.0
- pypsa>=0.25.1
- tsam>=2.3.1

View File

@ -24,7 +24,7 @@ rule build_electricity_demand:
countries=config["countries"],
load=config["load"],
input:
ancient("data/load_raw.csv"),
ancient(RESOURCES + "load_raw.csv"),
output:
RESOURCES + "load.csv",
log:
@ -206,11 +206,62 @@ rule build_ship_raster:
"../scripts/build_ship_raster.py"
rule determine_availability_matrix_MD_UA:
input:
copernicus="data/Copernicus_LC100_global_v3.0.1_2019-nrt_Discrete-Classification-map_EPSG-4326.tif",
wdpa="data/WDPA.gpkg",
wdpa_marine="data/WDPA_WDOECM_marine.gpkg",
gebco=lambda w: (
"data/bundle/GEBCO_2014_2D.nc"
if "max_depth" in config["renewable"][w.technology].keys()
else []
),
ship_density=lambda w: (
RESOURCES + "shipdensity_raster.tif"
if "ship_threshold" in config["renewable"][w.technology].keys()
else []
),
country_shapes=RESOURCES + "country_shapes.geojson",
offshore_shapes=RESOURCES + "offshore_shapes.geojson",
regions=lambda w: (
RESOURCES + "regions_onshore.geojson"
if w.technology in ("onwind", "solar")
else RESOURCES + "regions_offshore.geojson"
),
cutout=lambda w: "cutouts/"
+ CDIR
+ config["renewable"][w.technology]["cutout"]
+ ".nc",
output:
availability_matrix=RESOURCES + "availability_matrix_MD-UA_{technology}.nc",
availability_map=RESOURCES + "availability_matrix_MD-UA_{technology}.png",
log:
LOGS + "determine_availability_matrix_MD_UA_{technology}.log",
threads: ATLITE_NPROCESSES
resources:
mem_mb=ATLITE_NPROCESSES * 5000,
conda:
"../envs/environment.yaml"
script:
"../scripts/determine_availability_matrix_MD_UA.py"
# Optional input when having Ukraine (UA) or Moldova (MD) in the countries list
if {"UA", "MD"}.intersection(set(config["countries"])):
opt = {
"availability_matrix_MD_UA": RESOURCES
+ "availability_matrix_MD-UA_{technology}.nc"
}
else:
opt = {}
rule build_renewable_profiles:
params:
snapshots={k: config["snapshots"][k] for k in ["start", "end", "inclusive"]},
renewable=config["renewable"],
input:
**opt,
base_network=RESOURCES + "networks/base.nc",
corine=ancient("data/bundle/corine/g250_clc06_V18_5.tif"),
natura=lambda w: (
@ -227,7 +278,7 @@ rule build_renewable_profiles:
),
ship_density=lambda w: (
RESOURCES + "shipdensity_raster.tif"
if "ship_threshold" in config["renewable"][w.technology].keys()
if config["renewable"][w.technology].get("ship_threshold", False)
else []
),
country_shapes=RESOURCES + "country_shapes.geojson",
@ -358,6 +409,7 @@ rule add_electricity:
else [],
load=RESOURCES + "load.csv",
nuts3_shapes=RESOURCES + "nuts3_shapes.geojson",
ua_md_gdp="data/GDP_PPP_30arcsec_v3_mapped_default.csv",
output:
RESOURCES + "networks/elec.nc",
log:
@ -377,7 +429,9 @@ rule simplify_network:
params:
simplify_network=config["clustering"]["simplify_network"],
aggregation_strategies=config["clustering"].get("aggregation_strategies", {}),
focus_weights=config.get("focus_weights", None),
focus_weights=config["clustering"].get(
"focus_weights", config.get("focus_weights")
),
renewable_carriers=config["electricity"]["renewable_carriers"],
max_hours=config["electricity"]["max_hours"],
length_factor=config["lines"]["length_factor"],
@ -412,7 +466,9 @@ rule cluster_network:
cluster_network=config["clustering"]["cluster_network"],
aggregation_strategies=config["clustering"].get("aggregation_strategies", {}),
custom_busmap=config["enable"].get("custom_busmap", False),
focus_weights=config.get("focus_weights", None),
focus_weights=config["clustering"].get(
"focus_weights", config.get("focus_weights")
),
renewable_carriers=config["electricity"]["renewable_carriers"],
conventional_carriers=config["electricity"].get("conventional_carriers", []),
max_hours=config["electricity"]["max_hours"],

View File

@ -85,12 +85,12 @@ if config["sector"]["gas_network"] or config["sector"]["H2_retrofit"]:
rule build_gas_input_locations:
input:
lng=HTTP.remote(
gem=HTTP.remote(
"https://globalenergymonitor.org/wp-content/uploads/2023/07/Europe-Gas-Tracker-2023-03-v3.xlsx",
keep_local=True,
),
entry="data/gas_network/scigrid-gas/data/IGGIELGN_BorderPoints.geojson",
production="data/gas_network/scigrid-gas/data/IGGIELGN_Productions.geojson",
storage="data/gas_network/scigrid-gas/data/IGGIELGN_Storages.geojson",
regions_onshore=RESOURCES
+ "regions_onshore_elec_s{simpl}_{clusters}.geojson",
regions_offshore=RESOURCES
@ -269,7 +269,7 @@ rule build_biomass_potentials:
biomass=config["biomass"],
input:
enspreso_biomass=HTTP.remote(
"https://cidportal.jrc.ec.europa.eu/ftp/jrc-opendata/ENSPRESO/ENSPRESO_BIOMASS.xlsx",
"https://zenodo.org/records/10356004/files/ENSPRESO_BIOMASS.xlsx",
keep_local=True,
),
nuts2="data/bundle-sector/nuts/NUTS_RG_10M_2013_4326_LEVL_2.geojson", # https://gisco-services.ec.europa.eu/distribution/v2/nuts/download/#nuts21
@ -609,7 +609,7 @@ if config["sector"]["retrofitting"]["retro_endogen"]:
countries=config["countries"],
input:
building_stock="data/retro/data_building_stock.csv",
data_tabula="data/retro/tabula-calculator-calcsetbuilding.csv",
data_tabula="data/bundle-sector/retro/tabula-calculator-calcsetbuilding.csv",
air_temperature=RESOURCES + "temp_air_total_elec_s{simpl}_{clusters}.nc",
u_values_PL="data/retro/u_values_poland.csv",
tax_w="data/retro/electricity_taxes_eu.csv",

View File

@ -60,6 +60,15 @@ rule solve_sector_networks:
),
rule solve_sector_networks_perfect:
input:
expand(
RESULTS
+ "postnetworks/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_brownfield_all_years.nc",
**config["scenario"]
),
rule plot_networks:
input:
expand(

View File

@ -2,6 +2,11 @@
#
# SPDX-License-Identifier: MIT
import os, sys
sys.path.insert(0, os.path.abspath("scripts"))
from _helpers import validate_checksum
def memory(w):
factor = 3.0

View File

@ -5,9 +5,10 @@
localrules:
copy_config,
copy_conda_env,
if config["foresight"] != "perfect":
rule plot_network:
params:
foresight=config["foresight"],
@ -35,6 +36,35 @@ rule plot_network:
"../scripts/plot_network.py"
if config["foresight"] == "perfect":
rule plot_network:
params:
foresight=config["foresight"],
plotting=config["plotting"],
input:
network=RESULTS
+ "postnetworks/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_brownfield_all_years.nc",
regions=RESOURCES + "regions_onshore_elec_s{simpl}_{clusters}.geojson",
output:
**{
f"map_{year}": RESULTS
+ "maps/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}-costs-all_"
+ f"{year}.pdf"
for year in config["scenario"]["planning_horizons"]
},
threads: 2
resources:
mem_mb=10000,
benchmark:
BENCHMARKS
+"postnetworks/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_brownfield_all_years_benchmark"
conda:
"../envs/environment.yaml"
script:
"../scripts/plot_network.py"
rule copy_config:
params:
RDIR=RDIR,

View File

@ -2,6 +2,9 @@
#
# SPDX-License-Identifier: MIT
import requests
from datetime import datetime, timedelta
if config["enable"].get("retrieve", "auto") == "auto":
config["enable"]["retrieve"] = has_internet_access()
@ -39,6 +42,24 @@ if config["enable"]["retrieve"] and config["enable"].get("retrieve_databundle",
"../scripts/retrieve_databundle.py"
if config["enable"].get("retrieve_irena"):
rule retrieve_irena:
output:
offwind="data/existing_infrastructure/offwind_capacity_IRENA.csv",
onwind="data/existing_infrastructure/onwind_capacity_IRENA.csv",
solar="data/existing_infrastructure/solar_capacity_IRENA.csv",
log:
LOGS + "retrieve_irena.log",
resources:
mem_mb=1000,
retries: 2
conda:
"../envs/environment.yaml"
script:
"../scripts/retrieve_irena.py"
if config["enable"]["retrieve"] and config["enable"].get("retrieve_cutout", True):
rule retrieve_cutout:
@ -56,6 +77,7 @@ if config["enable"]["retrieve"] and config["enable"].get("retrieve_cutout", True
retries: 2
run:
move(input[0], output[0])
validate_checksum(output[0], input[0])
if config["enable"]["retrieve"] and config["enable"].get("retrieve_cost_data", True):
@ -92,7 +114,7 @@ if config["enable"]["retrieve"] and config["enable"].get(
static=True,
),
output:
protected(RESOURCES + "natura.tiff"),
RESOURCES + "natura.tiff",
log:
LOGS + "retrieve_natura_raster.log",
resources:
@ -100,6 +122,7 @@ if config["enable"]["retrieve"] and config["enable"].get(
retries: 2
run:
move(input[0], output[0])
validate_checksum(output[0], input[0])
if config["enable"]["retrieve"] and config["enable"].get(
@ -146,6 +169,7 @@ if config["enable"]["retrieve"] and (
"IGGIELGN_LNGs.geojson",
"IGGIELGN_BorderPoints.geojson",
"IGGIELGN_Productions.geojson",
"IGGIELGN_Storages.geojson",
"IGGIELGN_PipeSegments.geojson",
]
@ -177,7 +201,7 @@ if config["enable"]["retrieve"]:
static=True,
),
output:
"data/load_raw.csv",
RESOURCES + "load_raw.csv",
log:
LOGS + "retrieve_electricity_demand.log",
resources:
@ -205,6 +229,106 @@ if config["enable"]["retrieve"]:
retries: 2
run:
move(input[0], output[0])
validate_checksum(output[0], input[0])
if config["enable"]["retrieve"]:
# Downloading Copernicus Global Land Cover for land cover and land use:
# Website: https://land.copernicus.eu/global/products/lc
rule download_copernicus_land_cover:
input:
HTTP.remote(
"zenodo.org/record/3939050/files/PROBAV_LC100_global_v3.0.1_2019-nrt_Discrete-Classification-map_EPSG-4326.tif",
static=True,
),
output:
"data/Copernicus_LC100_global_v3.0.1_2019-nrt_Discrete-Classification-map_EPSG-4326.tif",
run:
move(input[0], output[0])
validate_checksum(output[0], input[0])
if config["enable"]["retrieve"]:
# Some logic to find the correct file URL
# Sometimes files are released delayed or ahead of schedule, check which file is currently available
def check_file_exists(url):
response = requests.head(url)
return response.status_code == 200
# Basic pattern where WDPA files can be found
url_pattern = (
"https://d1gam3xoknrgr2.cloudfront.net/current/WDPA_{bYYYY}_Public_shp.zip"
)
# 3-letter month + 4 digit year for current/previous/next month to test
current_monthyear = datetime.now().strftime("%b%Y")
prev_monthyear = (datetime.now() - timedelta(30)).strftime("%b%Y")
next_monthyear = (datetime.now() + timedelta(30)).strftime("%b%Y")
# Test prioritised: current month -> previous -> next
for bYYYY in [current_monthyear, prev_monthyear, next_monthyear]:
if check_file_exists(url := url_pattern.format(bYYYY=bYYYY)):
break
else:
# If None of the three URLs are working
url = False
assert (
url
), f"No WDPA files found at {url_pattern} for bY='{current_monthyear}, {prev_monthyear}, or {next_monthyear}'"
# Downloading protected area database from WDPA
# extract the main zip and then merge the contained 3 zipped shapefiles
# Website: https://www.protectedplanet.net/en/thematic-areas/wdpa
rule download_wdpa:
input:
HTTP.remote(
url,
static=True,
keep_local=True,
),
params:
zip="data/WDPA_shp.zip",
folder=directory("data/WDPA"),
output:
gpkg=protected("data/WDPA.gpkg"),
run:
shell("cp {input} {params.zip}")
shell("unzip -o {params.zip} -d {params.folder}")
for i in range(3):
# vsizip is special driver for directly working with zipped shapefiles in ogr2ogr
layer_path = (
f"/vsizip/{params.folder}/WDPA_{bYYYY}_Public_shp_{i}.zip"
)
print(f"Adding layer {i+1} of 3 to combined output file.")
shell("ogr2ogr -f gpkg -update -append {output.gpkg} {layer_path}")
rule download_wdpa_marine:
# Downloading Marine protected area database from WDPA
# extract the main zip and then merge the contained 3 zipped shapefiles
# Website: https://www.protectedplanet.net/en/thematic-areas/marine-protected-areas
input:
HTTP.remote(
f"d1gam3xoknrgr2.cloudfront.net/current/WDPA_WDOECM_{bYYYY}_Public_marine_shp.zip",
static=True,
keep_local=True,
),
params:
zip="data/WDPA_WDOECM_marine.zip",
folder=directory("data/WDPA_WDOECM_marine"),
output:
gpkg=protected("data/WDPA_WDOECM_marine.gpkg"),
run:
shell("cp {input} {params.zip}")
shell("unzip -o {params.zip} -d {params.folder}")
for i in range(3):
# vsizip is special driver for directly working with zipped shapefiles in ogr2ogr
layer_path = f"/vsizip/{params.folder}/WDPA_WDOECM_{bYYYY}_Public_marine_shp_{i}.zip"
print(f"Adding layer {i+1} of 3 to combined output file.")
shell("ogr2ogr -f gpkg -update -append {output.gpkg} {layer_path}")
if config["enable"]["retrieve"]:

194
rules/solve_perfect.smk Normal file
View File

@ -0,0 +1,194 @@
# SPDX-FileCopyrightText: : 2023 The PyPSA-Eur Authors
#
# SPDX-License-Identifier: MIT
rule add_existing_baseyear:
params:
baseyear=config["scenario"]["planning_horizons"][0],
sector=config["sector"],
existing_capacities=config["existing_capacities"],
costs=config["costs"],
input:
network=RESULTS
+ "prenetworks/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.nc",
powerplants=RESOURCES + "powerplants.csv",
busmap_s=RESOURCES + "busmap_elec_s{simpl}.csv",
busmap=RESOURCES + "busmap_elec_s{simpl}_{clusters}.csv",
clustered_pop_layout=RESOURCES + "pop_layout_elec_s{simpl}_{clusters}.csv",
costs="data/costs_{}.csv".format(config["scenario"]["planning_horizons"][0]),
cop_soil_total=RESOURCES + "cop_soil_total_elec_s{simpl}_{clusters}.nc",
cop_air_total=RESOURCES + "cop_air_total_elec_s{simpl}_{clusters}.nc",
existing_heating="data/existing_infrastructure/existing_heating_raw.csv",
existing_solar="data/existing_infrastructure/solar_capacity_IRENA.csv",
existing_onwind="data/existing_infrastructure/onwind_capacity_IRENA.csv",
existing_offwind="data/existing_infrastructure/offwind_capacity_IRENA.csv",
output:
RESULTS
+ "prenetworks-brownfield/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.nc",
wildcard_constraints:
planning_horizons=config["scenario"]["planning_horizons"][0], #only applies to baseyear
threads: 1
resources:
mem_mb=2000,
log:
LOGS
+ "add_existing_baseyear_elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.log",
benchmark:
(
BENCHMARKS
+ "add_existing_baseyear/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}"
)
conda:
"../envs/environment.yaml"
script:
"../scripts/add_existing_baseyear.py"
rule add_brownfield:
params:
H2_retrofit=config["sector"]["H2_retrofit"],
H2_retrofit_capacity_per_CH4=config["sector"]["H2_retrofit_capacity_per_CH4"],
threshold_capacity=config["existing_capacities"]["threshold_capacity"],
input:
network=RESULTS
+ "prenetworks/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.nc",
network_p=solved_previous_horizon, #solved network at previous time step
costs="data/costs_{planning_horizons}.csv",
cop_soil_total=RESOURCES + "cop_soil_total_elec_s{simpl}_{clusters}.nc",
cop_air_total=RESOURCES + "cop_air_total_elec_s{simpl}_{clusters}.nc",
output:
RESULTS
+ "prenetworks-brownfield/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.nc",
threads: 4
resources:
mem_mb=10000,
log:
LOGS
+ "add_brownfield_elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.log",
benchmark:
(
BENCHMARKS
+ "add_brownfield/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}"
)
conda:
"../envs/environment.yaml"
script:
"../scripts/add_brownfield.py"
rule prepare_perfect_foresight:
input:
**{
f"network_{year}": RESULTS
+ "prenetworks/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_"
+ f"{year}.nc"
for year in config["scenario"]["planning_horizons"][1:]
},
brownfield_network=lambda w: (
RESULTS
+ "prenetworks-brownfield/"
+ "elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_"
+ "{}.nc".format(str(config["scenario"]["planning_horizons"][0]))
),
output:
RESULTS
+ "prenetworks-brownfield/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_brownfield_all_years.nc",
threads: 2
resources:
mem_mb=10000,
log:
LOGS
+ "prepare_perfect_foresight{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}.log",
benchmark:
(
BENCHMARKS
+ "prepare_perfect_foresight{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}"
)
conda:
"../envs/environment.yaml"
script:
"../scripts/prepare_perfect_foresight.py"
rule solve_sector_network_perfect:
params:
solving=config["solving"],
foresight=config["foresight"],
sector=config["sector"],
planning_horizons=config["scenario"]["planning_horizons"],
co2_sequestration_potential=config["sector"].get(
"co2_sequestration_potential", 200
),
input:
network=RESULTS
+ "prenetworks-brownfield/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_brownfield_all_years.nc",
costs="data/costs_2030.csv",
config=RESULTS + "config.yaml",
output:
RESULTS
+ "postnetworks/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_brownfield_all_years.nc",
threads: 4
resources:
mem_mb=config["solving"]["mem"],
shadow:
"shallow"
log:
solver=RESULTS
+ "logs/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_brownfield_all_years_solver.log",
python=RESULTS
+ "logs/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_brownfield_all_years_python.log",
memory=RESULTS
+ "logs/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_brownfield_all_years_memory.log",
benchmark:
(
BENCHMARKS
+ "solve_sector_network/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_brownfield_all_years}"
)
conda:
"../envs/environment.yaml"
script:
"../scripts/solve_network.py"
rule make_summary_perfect:
input:
**{
f"networks_{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}": RESULTS
+ f"postnetworks/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_brownfield_all_years.nc"
for simpl in config["scenario"]["simpl"]
for clusters in config["scenario"]["clusters"]
for opts in config["scenario"]["opts"]
for sector_opts in config["scenario"]["sector_opts"]
for ll in config["scenario"]["ll"]
},
costs="data/costs_2020.csv",
output:
nodal_costs=RESULTS + "csvs/nodal_costs.csv",
nodal_capacities=RESULTS + "csvs/nodal_capacities.csv",
nodal_cfs=RESULTS + "csvs/nodal_cfs.csv",
cfs=RESULTS + "csvs/cfs.csv",
costs=RESULTS + "csvs/costs.csv",
capacities=RESULTS + "csvs/capacities.csv",
curtailment=RESULTS + "csvs/curtailment.csv",
energy=RESULTS + "csvs/energy.csv",
supply=RESULTS + "csvs/supply.csv",
supply_energy=RESULTS + "csvs/supply_energy.csv",
prices=RESULTS + "csvs/prices.csv",
weighted_prices=RESULTS + "csvs/weighted_prices.csv",
market_values=RESULTS + "csvs/market_values.csv",
price_statistics=RESULTS + "csvs/price_statistics.csv",
metrics=RESULTS + "csvs/metrics.csv",
co2_emissions=RESULTS + "csvs/co2_emissions.csv",
threads: 2
resources:
mem_mb=10000,
log:
LOGS + "make_summary_perfect.log",
benchmark:
(BENCHMARKS + "make_summary_perfect")
conda:
"../envs/environment.yaml"
script:
"../scripts/make_summary_perfect.py"
ruleorder: add_existing_baseyear > add_brownfield

256
scripts/_benchmark.py Normal file
View File

@ -0,0 +1,256 @@
# -*- coding: utf-8 -*-
# SPDX-FileCopyrightText: : 2020-2023 The PyPSA-Eur Authors
#
# SPDX-License-Identifier: MIT
"""
"""
from __future__ import absolute_import, print_function
import logging
import os
import sys
import time
logger = logging.getLogger(__name__)
# TODO: provide alternative when multiprocessing is not available
try:
from multiprocessing import Pipe, Process
except ImportError:
from multiprocessing.dummy import Process, Pipe
from memory_profiler import _get_memory, choose_backend
# The memory logging facilities have been adapted from memory_profiler
class MemTimer(Process):
"""
Write memory consumption over a time interval to file until signaled to
stop on the pipe.
"""
def __init__(
self, monitor_pid, interval, pipe, filename, max_usage, backend, *args, **kw
):
self.monitor_pid = monitor_pid
self.interval = interval
self.pipe = pipe
self.filename = filename
self.max_usage = max_usage
self.backend = backend
self.timestamps = kw.pop("timestamps", True)
self.include_children = kw.pop("include_children", True)
super(MemTimer, self).__init__(*args, **kw)
def run(self):
# get baseline memory usage
cur_mem = _get_memory(
self.monitor_pid,
self.backend,
timestamps=self.timestamps,
include_children=self.include_children,
)
n_measurements = 1
mem_usage = cur_mem if self.max_usage else [cur_mem]
if self.filename is not None:
stream = open(self.filename, "w")
stream.write("MEM {0:.6f} {1:.4f}\n".format(*cur_mem))
stream.flush()
else:
stream = None
self.pipe.send(0) # we're ready
stop = False
while True:
cur_mem = _get_memory(
self.monitor_pid,
self.backend,
timestamps=self.timestamps,
include_children=self.include_children,
)
if stream is not None:
stream.write("MEM {0:.6f} {1:.4f}\n".format(*cur_mem))
stream.flush()
n_measurements += 1
if not self.max_usage:
mem_usage.append(cur_mem)
else:
mem_usage = max(cur_mem, mem_usage)
if stop:
break
stop = self.pipe.poll(self.interval)
# do one more iteration
if stream is not None:
stream.close()
self.pipe.send(mem_usage)
self.pipe.send(n_measurements)
class memory_logger(object):
"""
Context manager for taking and reporting memory measurements at fixed
intervals from a separate process, for the duration of a context.
Parameters
----------
filename : None|str
Name of the text file to log memory measurements, if None no log is
created (defaults to None)
interval : float
Interval between measurements (defaults to 1.)
max_usage : bool
If True, only store and report the maximum value (defaults to True)
timestamps : bool
Whether to record tuples of memory usage and timestamps; if logging to
a file timestamps are always kept (defaults to True)
include_children : bool
Whether the memory of subprocesses is to be included (default: True)
Arguments
---------
n_measurements : int
Number of measurements that have been taken
mem_usage : (float, float)|[(float, float)]
All memory measurements and timestamps (if timestamps was True) or only
the maximum memory usage and its timestamp
Note
----
The arguments are only set after all the measurements, i.e. outside of the
with statement.
Example
-------
with memory_logger(filename="memory.log", max_usage=True) as mem:
# Do a lot of long running memory intensive stuff
hard_memory_bound_stuff()
max_mem, timestamp = mem.mem_usage
"""
def __init__(
self,
filename=None,
interval=1.0,
max_usage=True,
timestamps=True,
include_children=True,
):
if filename is not None:
timestamps = True
self.filename = filename
self.interval = interval
self.max_usage = max_usage
self.timestamps = timestamps
self.include_children = include_children
def __enter__(self):
backend = choose_backend()
self.child_conn, self.parent_conn = Pipe() # this will store MemTimer's results
self.p = MemTimer(
os.getpid(),
self.interval,
self.child_conn,
self.filename,
backend=backend,
timestamps=self.timestamps,
max_usage=self.max_usage,
include_children=self.include_children,
)
self.p.start()
self.parent_conn.recv() # wait until memory logging in subprocess is ready
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type is None:
self.parent_conn.send(0) # finish timing
self.mem_usage = self.parent_conn.recv()
self.n_measurements = self.parent_conn.recv()
else:
self.p.terminate()
return False
class timer(object):
level = 0
opened = False
def __init__(self, name="", verbose=True):
self.name = name
self.verbose = verbose
def __enter__(self):
if self.verbose:
if self.opened:
sys.stdout.write("\n")
if len(self.name) > 0:
sys.stdout.write((".. " * self.level) + self.name + ": ")
sys.stdout.flush()
self.__class__.opened = True
self.__class__.level += 1
self.start = time.time()
return self
def print_usec(self, usec):
if usec < 1000:
print("%.1f usec" % usec)
else:
msec = usec / 1000
if msec < 1000:
print("%.1f msec" % msec)
else:
sec = msec / 1000
print("%.1f sec" % sec)
def __exit__(self, exc_type, exc_val, exc_tb):
if not self.opened and self.verbose:
sys.stdout.write(".. " * self.level)
if exc_type is None:
stop = time.time()
self.usec = usec = (stop - self.start) * 1e6
if self.verbose:
self.print_usec(usec)
elif self.verbose:
print("failed")
sys.stdout.flush()
self.__class__.level -= 1
if self.verbose:
self.__class__.opened = False
return False
class optional(object):
def __init__(self, variable, contextman):
self.variable = variable
self.contextman = contextman
def __enter__(self):
if self.variable:
return self.contextman.__enter__()
def __exit__(self, exc_type, exc_val, exc_tb):
if self.variable:
return self.contextman.__exit__(exc_type, exc_val, exc_tb)
return False

View File

@ -4,6 +4,7 @@
# SPDX-License-Identifier: MIT
import contextlib
import hashlib
import logging
import os
import re
@ -12,6 +13,7 @@ from pathlib import Path
import pandas as pd
import pytz
import requests
import yaml
from pypsa.components import component_attrs, components
from pypsa.descriptors import Dict
@ -221,7 +223,7 @@ def progress_retrieve(url, file, disable=False):
urllib.request.urlretrieve(url, file, reporthook=update_to)
def mock_snakemake(rulename, configfiles=[], **wildcards):
def mock_snakemake(rulename, root_dir=None, configfiles=[], **wildcards):
"""
This function is expected to be executed from the 'scripts'-directory of '
the snakemake project. It returns a snakemake.script.Snakemake object,
@ -233,6 +235,8 @@ def mock_snakemake(rulename, configfiles=[], **wildcards):
----------
rulename: str
name of the rule for which the snakemake object should be generated
root_dir: str/path-like
path to the root directory of the snakemake project
configfiles: list, str
list of configfiles to be used to update the config
**wildcards:
@ -247,7 +251,10 @@ def mock_snakemake(rulename, configfiles=[], **wildcards):
from snakemake.script import Snakemake
script_dir = Path(__file__).parent.resolve()
if root_dir is None:
root_dir = script_dir.parent
else:
root_dir = Path(root_dir).resolve()
user_in_script_dir = Path.cwd().resolve() == script_dir
if user_in_script_dir:
@ -333,10 +340,7 @@ def generate_periodic_profiles(dt_index, nodes, weekly_profile, localize=None):
def parse(l):
if len(l) == 1:
return yaml.safe_load(l[0])
else:
return {l.pop(0): parse(l)}
return yaml.safe_load(l[0]) if len(l) == 1 else {l.pop(0): parse(l)}
def update_config_with_sector_opts(config, sector_opts):
@ -346,3 +350,63 @@ def update_config_with_sector_opts(config, sector_opts):
if o.startswith("CF+"):
l = o.split("+")[1:]
update_config(config, parse(l))
def get_checksum_from_zenodo(file_url):
parts = file_url.split("/")
record_id = parts[parts.index("record") + 1]
filename = parts[-1]
response = requests.get(f"https://zenodo.org/api/records/{record_id}", timeout=30)
response.raise_for_status()
data = response.json()
for file in data["files"]:
if file["key"] == filename:
return file["checksum"]
return None
def validate_checksum(file_path, zenodo_url=None, checksum=None):
"""
Validate file checksum against provided or Zenodo-retrieved checksum.
Calculates the hash of a file using 64KB chunks. Compares it against a
given checksum or one from a Zenodo URL.
Parameters
----------
file_path : str
Path to the file for checksum validation.
zenodo_url : str, optional
URL of the file on Zenodo to fetch the checksum.
checksum : str, optional
Checksum (format 'hash_type:checksum_value') for validation.
Raises
------
AssertionError
If the checksum does not match, or if neither `checksum` nor `zenodo_url` is provided.
Examples
--------
>>> validate_checksum("/path/to/file", checksum="md5:abc123...")
>>> validate_checksum(
... "/path/to/file",
... zenodo_url="https://zenodo.org/record/12345/files/example.txt",
... )
If the checksum is invalid, an AssertionError will be raised.
"""
assert checksum or zenodo_url, "Either checksum or zenodo_url must be provided"
if zenodo_url:
checksum = get_checksum_from_zenodo(zenodo_url)
hash_type, checksum = checksum.split(":")
hasher = hashlib.new(hash_type)
with open(file_path, "rb") as f:
for chunk in iter(lambda: f.read(65536), b""): # 64kb chunks
hasher.update(chunk)
calculated_checksum = hasher.hexdigest()
assert (
calculated_checksum == checksum
), "Checksum is invalid. This may be due to an incomplete download. Delete the file and re-execute the rule."

View File

@ -41,12 +41,9 @@ def add_brownfield(n, n_p, year):
# remove assets if their optimized nominal capacity is lower than a threshold
# since CHP heat Link is proportional to CHP electric Link, make sure threshold is compatible
chp_heat = c.df.index[
(
c.df[attr + "_nom_extendable"]
& c.df.index.str.contains("urban central")
(c.df[f"{attr}_nom_extendable"] & c.df.index.str.contains("urban central"))
& c.df.index.str.contains("CHP")
& c.df.index.str.contains("heat")
)
]
threshold = snakemake.params.threshold_capacity
@ -60,21 +57,20 @@ def add_brownfield(n, n_p, year):
)
n_p.mremove(
c.name,
chp_heat[c.df.loc[chp_heat, attr + "_nom_opt"] < threshold_chp_heat],
chp_heat[c.df.loc[chp_heat, f"{attr}_nom_opt"] < threshold_chp_heat],
)
n_p.mremove(
c.name,
c.df.index[
c.df[attr + "_nom_extendable"]
& ~c.df.index.isin(chp_heat)
& (c.df[attr + "_nom_opt"] < threshold)
(c.df[f"{attr}_nom_extendable"] & ~c.df.index.isin(chp_heat))
& (c.df[f"{attr}_nom_opt"] < threshold)
],
)
# copy over assets but fix their capacity
c.df[attr + "_nom"] = c.df[attr + "_nom_opt"]
c.df[attr + "_nom_extendable"] = False
c.df[f"{attr}_nom"] = c.df[f"{attr}_nom_opt"]
c.df[f"{attr}_nom_extendable"] = False
n.import_components_from_dataframe(c.df, c.name)
@ -124,7 +120,6 @@ def add_brownfield(n, n_p, year):
n.links.loc[new_pipes, "p_nom_min"] = 0.0
# %%
if __name__ == "__main__":
if "snakemake" not in globals():
from _helpers import mock_snakemake

View File

@ -84,6 +84,7 @@ It further adds extendable ``generators`` with **zero** capacity for
import logging
from itertools import product
from typing import Dict, List
import geopandas as gpd
import numpy as np
@ -255,6 +256,7 @@ def load_powerplants(ppl_fn):
"bioenergy": "biomass",
"ccgt, thermal": "CCGT",
"hard coal": "coal",
"natural gas": "OCGT",
}
return (
pd.read_csv(ppl_fn, index_col=0, dtype={"bus": "str"})
@ -279,11 +281,13 @@ def shapes_to_shapes(orig, dest):
return transfer
def attach_load(n, regions, load, nuts3_shapes, countries, scaling=1.0):
def attach_load(n, regions, load, nuts3_shapes, ua_md_gdp, countries, scaling=1.0):
substation_lv_i = n.buses.index[n.buses["substation_lv"]]
regions = gpd.read_file(regions).set_index("name").reindex(substation_lv_i)
opsd_load = pd.read_csv(load, index_col=0, parse_dates=True).filter(items=countries)
ua_md_gdp = pd.read_csv(ua_md_gdp, dtype={"name": "str"}).set_index("name")
logger.info(f"Load data scaled with scalling factor {scaling}.")
opsd_load *= scaling
@ -291,9 +295,9 @@ def attach_load(n, regions, load, nuts3_shapes, countries, scaling=1.0):
def upsample(cntry, group):
l = opsd_load[cntry]
if len(group) == 1:
return pd.DataFrame({group.index[0]: l})
else:
nuts3_cntry = nuts3.loc[nuts3.country == cntry]
transfer = shapes_to_shapes(group, nuts3_cntry.geometry).T.tocsr()
gdp_n = pd.Series(
@ -306,6 +310,9 @@ def attach_load(n, regions, load, nuts3_shapes, countries, scaling=1.0):
# relative factors 0.6 and 0.4 have been determined from a linear
# regression on the country to continent load data
factors = normed(0.6 * normed(gdp_n) + 0.4 * normed(pop_n))
if cntry in ["UA", "MD"]:
# overwrite factor because nuts3 provides no data for UA+MD
factors = normed(ua_md_gdp.loc[group.index, "GDP_PPP"].squeeze())
return pd.DataFrame(
factors.values * l.values[:, np.newaxis],
index=l.index,
@ -406,6 +413,7 @@ def attach_wind_and_solar(
capital_cost=capital_cost,
efficiency=costs.at[supcar, "efficiency"],
p_max_pu=ds["profile"].transpose("time", "bus").to_pandas(),
lifetime=costs.at[supcar, "lifetime"],
)
@ -434,7 +442,7 @@ def attach_conventional_generators(
ppl = (
ppl.query("carrier in @carriers")
.join(costs, on="carrier", rsuffix="_r")
.rename(index=lambda s: "C" + str(s))
.rename(index=lambda s: f"C{str(s)}")
)
ppl["efficiency"] = ppl.efficiency.fillna(ppl.efficiency_r)
@ -511,7 +519,7 @@ def attach_hydro(n, costs, ppl, profile_hydro, hydro_capacities, carriers, **par
ppl = (
ppl.query('carrier == "hydro"')
.reset_index(drop=True)
.rename(index=lambda s: str(s) + " hydro")
.rename(index=lambda s: f"{str(s)} hydro")
)
ror = ppl.query('technology == "Run-Of-River"')
phs = ppl.query('technology == "Pumped Storage"')
@ -608,16 +616,13 @@ def attach_hydro(n, costs, ppl, profile_hydro, hydro_capacities, carriers, **par
)
if not missing_countries.empty:
logger.warning(
"Assuming max_hours=6 for hydro reservoirs in the countries: {}".format(
", ".join(missing_countries)
)
f'Assuming max_hours=6 for hydro reservoirs in the countries: {", ".join(missing_countries)}'
)
hydro_max_hours = hydro.max_hours.where(
hydro.max_hours > 0, hydro.country.map(max_hours_country)
).fillna(6)
flatten_dispatch = params.get("flatten_dispatch", False)
if flatten_dispatch:
if flatten_dispatch := params.get("flatten_dispatch", False):
buffer = params.get("flatten_dispatch_buffer", 0.2)
average_capacity_factor = inflow_t[hydro.index].mean() / hydro["p_nom"]
p_max_pu = (average_capacity_factor + buffer).clip(upper=1)
@ -713,7 +718,17 @@ def attach_extendable_generators(n, costs, ppl, carriers):
)
def attach_OPSD_renewables(n, tech_map):
def attach_OPSD_renewables(n: pypsa.Network, tech_map: Dict[str, List[str]]) -> None:
"""
Attach renewable capacities from the OPSD dataset to the network.
Args:
- n: The PyPSA network to attach the capacities to.
- tech_map: A dictionary mapping fuel types to carrier names.
Returns:
- None
"""
tech_string = ", ".join(sum(tech_map.values(), []))
logger.info(f"Using OPSD renewable capacities for carriers {tech_string}.")
@ -738,7 +753,26 @@ def attach_OPSD_renewables(n, tech_map):
n.generators.p_nom_min.update(gens.bus.map(caps).dropna())
def estimate_renewable_capacities(n, year, tech_map, expansion_limit, countries):
def estimate_renewable_capacities(
n: pypsa.Network, year: int, tech_map: dict, expansion_limit: bool, countries: list
) -> None:
"""
Estimate a different between renewable capacities in the network and
reported country totals from IRENASTAT dataset. Distribute the difference
with a heuristic.
Heuristic: n.generators_t.p_max_pu.mean() * n.generators.p_nom_max
Args:
- n: The PyPSA network.
- year: The year of optimisation.
- tech_map: A dictionary mapping fuel types to carrier names.
- expansion_limit: Boolean value from config file
- countries: A list of country codes to estimate capacities for.
Returns:
- None
"""
if not len(countries) or not len(tech_map):
return
@ -755,7 +789,10 @@ def estimate_renewable_capacities(n, year, tech_map, expansion_limit, countries)
for ppm_technology, techs in tech_map.items():
tech_i = n.generators.query("carrier in @techs").index
if ppm_technology in capacities.index.get_level_values("Technology"):
stats = capacities.loc[ppm_technology].reindex(countries, fill_value=0.0)
else:
stats = pd.Series(0.0, index=countries)
country = n.generators.bus[tech_i].map(n.buses.country)
existent = n.generators.p_nom[tech_i].groupby(country).sum()
missing = stats - existent
@ -828,6 +865,7 @@ if __name__ == "__main__":
snakemake.input.regions,
snakemake.input.load,
snakemake.input.nuts3_shapes,
snakemake.input.ua_md_gdp,
params.countries,
params.scaling_factor,
)

View File

@ -45,7 +45,7 @@ def add_build_year_to_new_assets(n, baseyear):
# add -baseyear to name
rename = pd.Series(c.df.index, c.df.index)
rename[assets] += "-" + str(baseyear)
rename[assets] += f"-{str(baseyear)}"
c.df.rename(index=rename, inplace=True)
# rename time-dependent
@ -88,7 +88,9 @@ def add_existing_renewables(df_agg):
]
cfs = n.generators_t.p_max_pu[gens].mean()
cfs_key = cfs / cfs.sum()
nodal_fraction.loc[n.generators.loc[gens, "bus"]] = cfs_key.values
nodal_fraction.loc[n.generators.loc[gens, "bus"]] = cfs_key.groupby(
n.generators.loc[gens, "bus"]
).sum()
nodal_df = df.loc[n.buses.loc[elec_buses, "country"]]
nodal_df.index = elec_buses
@ -252,7 +254,7 @@ def add_power_capacities_installed_before_baseyear(n, grouping_years, costs, bas
if "m" in snakemake.wildcards.clusters:
for ind in new_capacity.index:
# existing capacities are split evenly among regions in every country
inv_ind = [i for i in inv_busmap[ind]]
inv_ind = list(inv_busmap[ind])
# for offshore the splitting only includes coastal regions
inv_ind = [
@ -305,6 +307,18 @@ def add_power_capacities_installed_before_baseyear(n, grouping_years, costs, bas
if "EU" not in vars(spatial)[carrier[generator]].locations:
bus0 = bus0.intersection(capacity.index + " gas")
# check for missing bus
missing_bus = pd.Index(bus0).difference(n.buses.index)
if not missing_bus.empty:
logger.info(f"add buses {bus0}")
n.madd(
"Bus",
bus0,
carrier=generator,
location=vars(spatial)[carrier[generator]].locations,
unit="MWh_el",
)
already_build = n.links.index.intersection(asset_i)
new_build = asset_i.difference(n.links.index)
lifetime_assets = lifetime.loc[grouping_year, generator].dropna()
@ -533,13 +547,17 @@ def add_heating_capacities_installed_before_baseyear(
bus0=nodes[name],
bus1=nodes[name] + " " + name + " heat",
carrier=name + " resistive heater",
efficiency=costs.at[name_type + " resistive heater", "efficiency"],
capital_cost=costs.at[name_type + " resistive heater", "efficiency"]
* costs.at[name_type + " resistive heater", "fixed"],
p_nom=0.5
efficiency=costs.at[f"{name_type} resistive heater", "efficiency"],
capital_cost=(
costs.at[f"{name_type} resistive heater", "efficiency"]
* costs.at[f"{name_type} resistive heater", "fixed"]
),
p_nom=(
0.5
* nodal_df[f"{heat_type} resistive heater"][nodes[name]]
* ratio
/ costs.at[name_type + " resistive heater", "efficiency"],
/ costs.at[f"{name_type} resistive heater", "efficiency"]
),
build_year=int(grouping_year),
lifetime=costs.at[costs_name, "lifetime"],
)
@ -552,16 +570,20 @@ def add_heating_capacities_installed_before_baseyear(
bus1=nodes[name] + " " + name + " heat",
bus2="co2 atmosphere",
carrier=name + " gas boiler",
efficiency=costs.at[name_type + " gas boiler", "efficiency"],
efficiency=costs.at[f"{name_type} gas boiler", "efficiency"],
efficiency2=costs.at["gas", "CO2 intensity"],
capital_cost=costs.at[name_type + " gas boiler", "efficiency"]
* costs.at[name_type + " gas boiler", "fixed"],
p_nom=0.5
capital_cost=(
costs.at[f"{name_type} gas boiler", "efficiency"]
* costs.at[f"{name_type} gas boiler", "fixed"]
),
p_nom=(
0.5
* nodal_df[f"{heat_type} gas boiler"][nodes[name]]
* ratio
/ costs.at[name_type + " gas boiler", "efficiency"],
/ costs.at[f"{name_type} gas boiler", "efficiency"]
),
build_year=int(grouping_year),
lifetime=costs.at[name_type + " gas boiler", "lifetime"],
lifetime=costs.at[f"{name_type} gas boiler", "lifetime"],
)
n.madd(
@ -581,7 +603,7 @@ def add_heating_capacities_installed_before_baseyear(
* ratio
/ costs.at["decentral oil boiler", "efficiency"],
build_year=int(grouping_year),
lifetime=costs.at[name_type + " gas boiler", "lifetime"],
lifetime=costs.at[f"{name_type} gas boiler", "lifetime"],
)
# delete links with p_nom=nan corresponding to extra nodes in country
@ -605,21 +627,24 @@ def add_heating_capacities_installed_before_baseyear(
],
)
# drop assets which are at the end of their lifetime
links_i = n.links[(n.links.build_year + n.links.lifetime <= baseyear)].index
n.mremove("Link", links_i)
# %%
if __name__ == "__main__":
if "snakemake" not in globals():
from _helpers import mock_snakemake
snakemake = mock_snakemake(
"add_existing_baseyear",
configfiles="config/test/config.myopic.yaml",
# configfiles="config/test/config.myopic.yaml",
simpl="",
clusters="5",
ll="v1.5",
clusters="37",
ll="v1.0",
opts="",
sector_opts="24H-T-H-B-I-A-solar+p3-dist1",
planning_horizons=2030,
sector_opts="1p7-4380H-T-H-B-I-A-solar+p3-dist1",
planning_horizons=2020,
)
logging.basicConfig(level=snakemake.config["logging"]["level"])

View File

@ -151,9 +151,7 @@ def _load_buses_from_eg(eg_buses, europe_shape, config_elec):
buses.v_nom.isin(config_elec["voltages"]) | buses.v_nom.isnull()
)
logger.info(
"Removing buses with voltages {}".format(
pd.Index(buses.v_nom.unique()).dropna().difference(config_elec["voltages"])
)
f'Removing buses with voltages {pd.Index(buses.v_nom.unique()).dropna().difference(config_elec["voltages"])}'
)
return pd.DataFrame(buses.loc[buses_in_europe_b & buses_with_v_nom_to_keep_b])
@ -368,6 +366,25 @@ def _apply_parameter_corrections(n, parameter_corrections):
df.loc[inds, attr] = r[inds].astype(df[attr].dtype)
def _reconnect_crimea(lines):
logger.info("Reconnecting Crimea to the Ukrainian grid.")
lines_to_crimea = pd.DataFrame(
{
"bus0": ["3065", "3181", "3181"],
"bus1": ["3057", "3055", "3057"],
"v_nom": [300, 300, 300],
"num_parallel": [1, 1, 1],
"length": [140, 120, 140],
"carrier": ["AC", "AC", "AC"],
"underground": [False, False, False],
"under_construction": [False, False, False],
},
index=["Melitopol", "Liubymivka left", "Luibymivka right"],
)
return pd.concat([lines, lines_to_crimea])
def _set_electrical_parameters_lines(lines, config):
v_noms = config["electricity"]["voltages"]
linetypes = config["lines"]["types"]
@ -452,19 +469,15 @@ def _remove_dangling_branches(branches, buses):
)
def _remove_unconnected_components(network):
def _remove_unconnected_components(network, threshold=6):
_, labels = csgraph.connected_components(network.adjacency_matrix(), directed=False)
component = pd.Series(labels, index=network.buses.index)
component_sizes = component.value_counts()
components_to_remove = component_sizes.iloc[1:]
components_to_remove = component_sizes.loc[component_sizes < threshold]
logger.info(
"Removing {} unconnected network components with less than {} buses. In total {} buses.".format(
len(components_to_remove),
components_to_remove.max(),
components_to_remove.sum(),
)
f"Removing {len(components_to_remove)} unconnected network components with less than {components_to_remove.max()} buses. In total {components_to_remove.sum()} buses."
)
return network[component == component_sizes.index[0]]
@ -547,7 +560,7 @@ def _set_countries_and_substations(n, config, country_shapes, offshore_shapes):
~buses["under_construction"]
)
c_nan_b = buses.country.isnull()
c_nan_b = buses.country == "na"
if c_nan_b.sum() > 0:
c_tag = _get_country(buses.loc[c_nan_b])
c_tag.loc[~c_tag.isin(countries)] = np.nan
@ -705,6 +718,9 @@ def base_network(
lines = _load_lines_from_eg(buses, eg_lines)
transformers = _load_transformers_from_eg(buses, eg_transformers)
if config["lines"].get("reconnect_crimea", True) and "UA" in config["countries"]:
lines = _reconnect_crimea(lines)
lines = _set_electrical_parameters_lines(lines, config)
transformers = _set_electrical_parameters_transformers(transformers, config)
links = _set_electrical_parameters_links(links, config, links_p_nom)

View File

@ -134,7 +134,7 @@ def disaggregate_nuts0(bio):
# get population in nuts2
pop_nuts2 = pop.loc[pop.index.str.len() == 4]
by_country = pop_nuts2.total.groupby(pop_nuts2.ct).sum()
pop_nuts2["fraction"] = pop_nuts2.total / pop_nuts2.ct.map(by_country)
pop_nuts2.loc[:, "fraction"] = pop_nuts2.total / pop_nuts2.ct.map(by_country)
# distribute nuts0 data to nuts2 by population
bio_nodal = bio.loc[pop_nuts2.ct]
@ -263,7 +263,7 @@ if __name__ == "__main__":
df.to_csv(snakemake.output.biomass_potentials_all)
grouper = {v: k for k, vv in params["classes"].items() for v in vv}
df = df.groupby(grouper, axis=1).sum()
df = df.T.groupby(grouper).sum().T
df *= 1e6 # TWh/a to MWh/a
df.index.name = "MWh/a"

View File

@ -31,7 +31,7 @@ Relevant Settings
Inputs
------
- ``data/load_raw.csv``:
- ``resources/load_raw.csv``:
Outputs
-------
@ -81,7 +81,7 @@ def load_timeseries(fn, years, countries, powerstatistics=True):
return s[: -len(pattern)]
return (
pd.read_csv(fn, index_col=0, parse_dates=[0])
pd.read_csv(fn, index_col=0, parse_dates=[0], date_format="%Y-%m-%dT%H:%M:%SZ")
.tz_localize(None)
.filter(like=pattern)
.rename(columns=rename)
@ -155,7 +155,7 @@ def copy_timeslice(load, cntry, start, stop, delta, fn_load=None):
].values
def manual_adjustment(load, fn_load, powerstatistics):
def manual_adjustment(load, fn_load, powerstatistics, countries):
"""
Adjust gaps manual for load data from OPSD time-series package.
@ -278,6 +278,14 @@ def manual_adjustment(load, fn_load, powerstatistics):
load, "LU", "2019-02-05 20:00", "2019-02-06 19:00", Delta(weeks=-1)
)
if "UA" in countries:
copy_timeslice(
load, "UA", "2013-01-25 14:00", "2013-01-28 21:00", Delta(weeks=1)
)
copy_timeslice(
load, "UA", "2013-10-28 03:00", "2013-10-28 20:00", Delta(weeks=1)
)
return load
@ -298,8 +306,22 @@ if __name__ == "__main__":
load = load_timeseries(snakemake.input[0], years, countries, powerstatistics)
if "UA" in countries:
# attach load of UA (best data only for entsoe transparency)
load_ua = load_timeseries(snakemake.input[0], "2018", ["UA"], False)
snapshot_year = str(snapshots.year.unique().item())
time_diff = pd.Timestamp("2018") - pd.Timestamp(snapshot_year)
load_ua.index -= (
time_diff # hack indices (currently, UA is manually set to 2018)
)
load["UA"] = load_ua
# attach load of MD (no time-series available, use 2020-totals and distribute according to UA):
# https://www.iea.org/data-and-statistics/data-browser/?country=MOLDOVA&fuel=Energy%20consumption&indicator=TotElecCons
if "MD" in countries:
load["MD"] = 6.2e6 * (load_ua / load_ua.sum())
if snakemake.params.load["manual_adjustments"]:
load = manual_adjustment(load, snakemake.input[0], powerstatistics)
load = manual_adjustment(load, snakemake.input[0], powerstatistics, countries)
if load.empty:
logger.warning("Build electricity demand time series is empty.")

View File

@ -172,8 +172,6 @@ def build_swiss(year):
def idees_per_country(ct, year, base_dir):
ct_totals = {}
ct_idees = idees_rename.get(ct, ct)
fn_residential = f"{base_dir}/JRC-IDEES-2015_Residential_{ct_idees}.xlsx"
fn_tertiary = f"{base_dir}/JRC-IDEES-2015_Tertiary_{ct_idees}.xlsx"
@ -183,20 +181,20 @@ def idees_per_country(ct, year, base_dir):
df = pd.read_excel(fn_residential, "RES_hh_fec", index_col=0)[year]
ct_totals["total residential space"] = df["Space heating"]
rows = ["Advanced electric heating", "Conventional electric heating"]
ct_totals["electricity residential space"] = df[rows].sum()
ct_totals = {
"total residential space": df["Space heating"],
"electricity residential space": df[rows].sum(),
}
ct_totals["total residential water"] = df.at["Water heating"]
assert df.index[23] == "Electricity"
ct_totals["electricity residential water"] = df[23]
ct_totals["electricity residential water"] = df.iloc[23]
ct_totals["total residential cooking"] = df["Cooking"]
assert df.index[30] == "Electricity"
ct_totals["electricity residential cooking"] = df[30]
ct_totals["electricity residential cooking"] = df.iloc[30]
df = pd.read_excel(fn_residential, "RES_summary", index_col=0)[year]
@ -204,13 +202,13 @@ def idees_per_country(ct, year, base_dir):
ct_totals["total residential"] = df[row]
assert df.index[47] == "Electricity"
ct_totals["electricity residential"] = df[47]
ct_totals["electricity residential"] = df.iloc[47]
assert df.index[46] == "Derived heat"
ct_totals["derived heat residential"] = df[46]
ct_totals["derived heat residential"] = df.iloc[46]
assert df.index[50] == "Thermal uses"
ct_totals["thermal uses residential"] = df[50]
ct_totals["thermal uses residential"] = df.iloc[50]
# services
@ -224,12 +222,12 @@ def idees_per_country(ct, year, base_dir):
ct_totals["total services water"] = df["Hot water"]
assert df.index[24] == "Electricity"
ct_totals["electricity services water"] = df[24]
ct_totals["electricity services water"] = df.iloc[24]
ct_totals["total services cooking"] = df["Catering"]
assert df.index[31] == "Electricity"
ct_totals["electricity services cooking"] = df[31]
ct_totals["electricity services cooking"] = df.iloc[31]
df = pd.read_excel(fn_tertiary, "SER_summary", index_col=0)[year]
@ -237,13 +235,13 @@ def idees_per_country(ct, year, base_dir):
ct_totals["total services"] = df[row]
assert df.index[50] == "Electricity"
ct_totals["electricity services"] = df[50]
ct_totals["electricity services"] = df.iloc[50]
assert df.index[49] == "Derived heat"
ct_totals["derived heat services"] = df[49]
ct_totals["derived heat services"] = df.iloc[49]
assert df.index[53] == "Thermal uses"
ct_totals["thermal uses services"] = df[53]
ct_totals["thermal uses services"] = df.iloc[53]
# agriculture, forestry and fishing
@ -284,28 +282,28 @@ def idees_per_country(ct, year, base_dir):
ct_totals["total two-wheel"] = df["Powered 2-wheelers (Gasoline)"]
assert df.index[19] == "Passenger cars"
ct_totals["total passenger cars"] = df[19]
ct_totals["total passenger cars"] = df.iloc[19]
assert df.index[30] == "Battery electric vehicles"
ct_totals["electricity passenger cars"] = df[30]
ct_totals["electricity passenger cars"] = df.iloc[30]
assert df.index[31] == "Motor coaches, buses and trolley buses"
ct_totals["total other road passenger"] = df[31]
ct_totals["total other road passenger"] = df.iloc[31]
assert df.index[39] == "Battery electric vehicles"
ct_totals["electricity other road passenger"] = df[39]
ct_totals["electricity other road passenger"] = df.iloc[39]
assert df.index[41] == "Light duty vehicles"
ct_totals["total light duty road freight"] = df[41]
ct_totals["total light duty road freight"] = df.iloc[41]
assert df.index[49] == "Battery electric vehicles"
ct_totals["electricity light duty road freight"] = df[49]
ct_totals["electricity light duty road freight"] = df.iloc[49]
row = "Heavy duty vehicles (Diesel oil incl. biofuels)"
ct_totals["total heavy duty road freight"] = df[row]
assert df.index[61] == "Passenger cars"
ct_totals["passenger car efficiency"] = df[61]
ct_totals["passenger car efficiency"] = df.iloc[61]
df = pd.read_excel(fn_transport, "TrRail_ene", index_col=0)[year]
@ -314,39 +312,39 @@ def idees_per_country(ct, year, base_dir):
ct_totals["electricity rail"] = df["Electricity"]
assert df.index[15] == "Passenger transport"
ct_totals["total rail passenger"] = df[15]
ct_totals["total rail passenger"] = df.iloc[15]
assert df.index[16] == "Metro and tram, urban light rail"
assert df.index[19] == "Electric"
assert df.index[20] == "High speed passenger trains"
ct_totals["electricity rail passenger"] = df[[16, 19, 20]].sum()
ct_totals["electricity rail passenger"] = df.iloc[[16, 19, 20]].sum()
assert df.index[21] == "Freight transport"
ct_totals["total rail freight"] = df[21]
ct_totals["total rail freight"] = df.iloc[21]
assert df.index[23] == "Electric"
ct_totals["electricity rail freight"] = df[23]
ct_totals["electricity rail freight"] = df.iloc[23]
df = pd.read_excel(fn_transport, "TrAvia_ene", index_col=0)[year]
assert df.index[6] == "Passenger transport"
ct_totals["total aviation passenger"] = df[6]
ct_totals["total aviation passenger"] = df.iloc[6]
assert df.index[10] == "Freight transport"
ct_totals["total aviation freight"] = df[10]
ct_totals["total aviation freight"] = df.iloc[10]
assert df.index[7] == "Domestic"
ct_totals["total domestic aviation passenger"] = df[7]
ct_totals["total domestic aviation passenger"] = df.iloc[7]
assert df.index[8] == "International - Intra-EU"
assert df.index[9] == "International - Extra-EU"
ct_totals["total international aviation passenger"] = df[[8, 9]].sum()
ct_totals["total international aviation passenger"] = df.iloc[[8, 9]].sum()
assert df.index[11] == "Domestic and International - Intra-EU"
ct_totals["total domestic aviation freight"] = df[11]
ct_totals["total domestic aviation freight"] = df.iloc[11]
assert df.index[12] == "International - Extra-EU"
ct_totals["total international aviation freight"] = df[12]
ct_totals["total international aviation freight"] = df.iloc[12]
ct_totals["total domestic aviation"] = (
ct_totals["total domestic aviation freight"]
@ -366,7 +364,7 @@ def idees_per_country(ct, year, base_dir):
df = pd.read_excel(fn_transport, "TrRoad_act", index_col=0)[year]
assert df.index[85] == "Passenger cars"
ct_totals["passenger cars"] = df[85]
ct_totals["passenger cars"] = df.iloc[85]
return pd.Series(ct_totals, name=ct)

View File

@ -23,11 +23,10 @@ def read_scigrid_gas(fn):
return df
def build_gem_lng_data(lng_fn):
df = pd.read_excel(lng_fn[0], sheet_name="LNG terminals - data")
def build_gem_lng_data(fn):
df = pd.read_excel(fn[0], sheet_name="LNG terminals - data")
df = df.set_index("ComboID")
remove_status = ["Cancelled"]
remove_country = ["Cyprus", "Turkey"]
remove_terminal = ["Puerto de la Luz LNG Terminal", "Gran Canaria LNG Terminal"]
@ -42,9 +41,50 @@ def build_gem_lng_data(lng_fn):
return gpd.GeoDataFrame(df, geometry=geometry, crs="EPSG:4326")
def build_gas_input_locations(lng_fn, entry_fn, prod_fn, countries):
def build_gem_prod_data(fn):
df = pd.read_excel(fn[0], sheet_name="Gas extraction - main")
df = df.set_index("GEM Unit ID")
remove_country = ["Cyprus", "Türkiye"]
remove_fuel_type = ["oil"]
df = df.query(
"Status != 'shut in' \
& 'Fuel type' != 'oil' \
& Country != @remove_country \
& ~Latitude.isna() \
& ~Longitude.isna()"
).copy()
p = pd.read_excel(fn[0], sheet_name="Gas extraction - production")
p = p.set_index("GEM Unit ID")
p = p[p["Fuel description"] == "gas"]
capacities = pd.DataFrame(index=df.index)
for key in ["production", "production design capacity", "reserves"]:
cap = (
p.loc[p["Production/reserves"] == key, "Quantity (converted)"]
.groupby("GEM Unit ID")
.sum()
.reindex(df.index)
)
# assume capacity such that 3% of reserves can be extracted per year (25% quantile)
annualization_factor = 0.03 if key == "reserves" else 1.0
capacities[key] = cap * annualization_factor
df["mcm_per_year"] = (
capacities["production"]
.combine_first(capacities["production design capacity"])
.combine_first(capacities["reserves"])
)
geometry = gpd.points_from_xy(df["Longitude"], df["Latitude"])
return gpd.GeoDataFrame(df, geometry=geometry, crs="EPSG:4326")
def build_gas_input_locations(gem_fn, entry_fn, sto_fn, countries):
# LNG terminals
lng = build_gem_lng_data(lng_fn)
lng = build_gem_lng_data(gem_fn)
# Entry points from outside the model scope
entry = read_scigrid_gas(entry_fn)
@ -55,25 +95,30 @@ def build_gas_input_locations(lng_fn, entry_fn, prod_fn, countries):
| (entry.from_country == "NO") # malformed datapoint # entries from NO to GB
]
sto = read_scigrid_gas(sto_fn)
remove_country = ["RU", "UA", "TR", "BY"]
sto = sto.query("country_code != @remove_country")
# production sites inside the model scope
prod = read_scigrid_gas(prod_fn)
prod = prod.loc[
(prod.geometry.y > 35) & (prod.geometry.x < 30) & (prod.country_code != "DE")
]
prod = build_gem_prod_data(gem_fn)
mcm_per_day_to_mw = 437.5 # MCM/day to MWh/h
mcm_per_year_to_mw = 1.199 # MCM/year to MWh/h
mtpa_to_mw = 1649.224 # mtpa to MWh/h
lng["p_nom"] = lng["CapacityInMtpa"] * mtpa_to_mw
entry["p_nom"] = entry["max_cap_from_to_M_m3_per_d"] * mcm_per_day_to_mw
prod["p_nom"] = prod["max_supply_M_m3_per_d"] * mcm_per_day_to_mw
mcm_to_gwh = 11.36 # MCM to GWh
lng["capacity"] = lng["CapacityInMtpa"] * mtpa_to_mw
entry["capacity"] = entry["max_cap_from_to_M_m3_per_d"] * mcm_per_day_to_mw
prod["capacity"] = prod["mcm_per_year"] * mcm_per_year_to_mw
sto["capacity"] = sto["max_cushionGas_M_m3"] * mcm_to_gwh
lng["type"] = "lng"
entry["type"] = "pipeline"
prod["type"] = "production"
sto["type"] = "storage"
sel = ["geometry", "p_nom", "type"]
sel = ["geometry", "capacity", "type"]
return pd.concat([prod[sel], entry[sel], lng[sel]], ignore_index=True)
return pd.concat([prod[sel], entry[sel], lng[sel], sto[sel]], ignore_index=True)
if __name__ == "__main__":
@ -83,7 +128,7 @@ if __name__ == "__main__":
snakemake = mock_snakemake(
"build_gas_input_locations",
simpl="",
clusters="37",
clusters="128",
)
logging.basicConfig(level=snakemake.config["logging"]["level"])
@ -104,9 +149,9 @@ if __name__ == "__main__":
countries = regions.index.str[:2].unique().str.replace("GB", "UK")
gas_input_locations = build_gas_input_locations(
snakemake.input.lng,
snakemake.input.gem,
snakemake.input.entry,
snakemake.input.production,
snakemake.input.storage,
countries,
)
@ -116,9 +161,13 @@ if __name__ == "__main__":
gas_input_nodes.to_file(snakemake.output.gas_input_nodes, driver="GeoJSON")
ensure_columns = ["lng", "pipeline", "production", "storage"]
gas_input_nodes_s = (
gas_input_nodes.groupby(["bus", "type"])["p_nom"].sum().unstack()
gas_input_nodes.groupby(["bus", "type"])["capacity"]
.sum()
.unstack()
.reindex(columns=ensure_columns)
)
gas_input_nodes_s.columns.name = "p_nom"
gas_input_nodes_s.columns.name = "capacity"
gas_input_nodes_s.to_csv(snakemake.output.gas_input_nodes_simplified)

View File

@ -29,25 +29,25 @@ def diameter_to_capacity(pipe_diameter_mm):
Based on p.15 of
https://gasforclimate2050.eu/wp-content/uploads/2020/07/2020_European-Hydrogen-Backbone_Report.pdf
"""
# slopes definitions
m0 = (1500 - 0) / (500 - 0)
m1 = (5000 - 1500) / (600 - 500)
m2 = (11250 - 5000) / (900 - 600)
m3 = (21700 - 11250) / (1200 - 900)
# intercept
a0 = 0
a1 = -16000
a2 = -7500
a3 = -20100
if pipe_diameter_mm < 500:
# slopes definitions
m0 = (1500 - 0) / (500 - 0)
# intercept
a0 = 0
return a0 + m0 * pipe_diameter_mm
elif pipe_diameter_mm < 600:
return a1 + m1 * pipe_diameter_mm
elif pipe_diameter_mm < 900:
return a2 + m2 * pipe_diameter_mm
else:
m3 = (21700 - 11250) / (1200 - 900)
a3 = -20100
return a3 + m3 * pipe_diameter_mm

View File

@ -26,7 +26,7 @@ Relevant Settings
Inputs
------
- ``data/bundle/EIA_hydro_generation_2000_2014.csv``: Hydroelectricity net generation per country and year (`EIA <https://www.eia.gov/beta/international/data/browser/#/?pa=000000000000000000000000000000g&c=1028i008006gg6168g80a4k000e0ag00gg0004g800ho00g8&ct=0&ug=8&tl_id=2-A&vs=INTL.33-12-ALB-BKWH.A&cy=2014&vo=0&v=H&start=2000&end=2016>`_)
- ``data/bundle/eia_hydro_annual_generation.csv``: Hydroelectricity net generation per country and year (`EIA <https://www.eia.gov/beta/international/data/browser/#/?pa=000000000000000000000000000000g&c=1028i008006gg6168g80a4k000e0ag00gg0004g800ho00g8&ct=0&ug=8&tl_id=2-A&vs=INTL.33-12-ALB-BKWH.A&cy=2014&vo=0&v=H&start=2000&end=2016>`_)
.. image:: img/hydrogeneration.png
:scale: 33 %
@ -72,12 +72,14 @@ cc = coco.CountryConverter()
def get_eia_annual_hydro_generation(fn, countries):
# in billion kWh/a = TWh/a
df = pd.read_csv(fn, skiprows=2, index_col=1, na_values=[" ", "--"]).iloc[1:, 1:]
df = pd.read_csv(
fn, skiprows=2, index_col=1, na_values=[" ", "--"], decimal=","
).iloc[1:, 1:]
df.index = df.index.str.strip()
former_countries = {
"Former Czechoslovakia": dict(
countries=["Czech Republic", "Slovakia"], start=1980, end=1992
countries=["Czechia", "Slovakia"], start=1980, end=1992
),
"Former Serbia and Montenegro": dict(
countries=["Serbia", "Montenegro"], start=1992, end=2005

View File

@ -154,7 +154,7 @@ if __name__ == "__main__":
snakemake = mock_snakemake(
"build_industrial_distribution_key",
simpl="",
clusters=48,
clusters=128,
)
logging.basicConfig(level=snakemake.config["logging"]["level"])

View File

@ -167,9 +167,7 @@ def industrial_energy_demand(countries, year):
with mp.Pool(processes=nprocesses) as pool:
demand_l = list(tqdm(pool.imap(func, countries), **tqdm_kwargs))
demand = pd.concat(demand_l, keys=countries)
return demand
return pd.concat(demand_l, keys=countries)
if __name__ == "__main__":

View File

@ -41,7 +41,7 @@ The following heat gains and losses are considered:
- heat gain through resistive losses
- heat gain through solar radiation
- heat loss through radiation of the trasnmission line
- heat loss through radiation of the transmission line
- heat loss through forced convection with wind
- heat loss through natural convection
@ -83,8 +83,7 @@ def calculate_resistance(T, R_ref, T_ref=293, alpha=0.00403):
-------
Resistance of at given temperature.
"""
R = R_ref * (1 + alpha * (T - T_ref))
return R
return R_ref * (1 + alpha * (T - T_ref))
def calculate_line_rating(n, cutout):
@ -120,18 +119,17 @@ def calculate_line_rating(n, cutout):
.apply(lambda x: int(re.findall(r"(\d+)-bundle", x)[0]))
)
# Set default number of bundles per line
relevant_lines["n_bundle"].fillna(1, inplace=True)
relevant_lines["n_bundle"] = relevant_lines["n_bundle"].fillna(1)
R *= relevant_lines["n_bundle"]
R = calculate_resistance(T=353, R_ref=R)
Imax = cutout.line_rating(shapes, R, D=0.0218, Ts=353, epsilon=0.8, alpha=0.8)
line_factor = relevant_lines.eval("v_nom * n_bundle * num_parallel") / 1e3 # in mW
da = xr.DataArray(
return xr.DataArray(
data=np.sqrt(3) * Imax * line_factor.values.reshape(-1, 1),
attrs=dict(
description="Maximal possible power in MW for given line considering line rating"
),
)
return da
if __name__ == "__main__":

View File

@ -146,8 +146,7 @@ if __name__ == "__main__":
ppl, snakemake.input.custom_powerplants, custom_ppl_query
)
countries_wo_ppl = set(countries) - set(ppl.Country.unique())
if countries_wo_ppl:
if countries_wo_ppl := set(countries) - set(ppl.Country.unique()):
logging.warning(f"No powerplants known in: {', '.join(countries_wo_ppl)}")
substations = n.buses.query("substation_lv")

View File

@ -252,7 +252,7 @@ if __name__ == "__main__":
snakemake.input.corine, codes=codes, buffer=buffer, crs=3035
)
if "ship_threshold" in params:
if params.get("ship_threshold"):
shipping_threshold = (
params["ship_threshold"] * 8760 * 6
) # approximation because 6 years of data which is hourly collected
@ -288,6 +288,14 @@ if __name__ == "__main__":
else:
availability = cutout.availabilitymatrix(regions, excluder, **kwargs)
# For Moldova and Ukraine: Overwrite parts not covered by Corine with
# externally determined available areas
if "availability_matrix_MD_UA" in snakemake.input.keys():
availability_MDUA = xr.open_dataarray(
snakemake.input["availability_matrix_MD_UA"]
)
availability.loc[availability_MDUA.coords] = availability_MDUA
area = cutout.grid.to_crs(3035).area / 1e6
area = xr.DataArray(
area.values.reshape(cutout.shape), [cutout.coords["y"], cutout.coords["x"]]

View File

@ -102,7 +102,7 @@ solar_energy_transmittance = (
)
# solar global radiation [kWh/(m^2a)]
solar_global_radiation = pd.Series(
[246, 401, 246, 148],
[271, 392, 271, 160],
index=["east", "south", "west", "north"],
name="solar_global_radiation [kWh/(m^2a)]",
)
@ -164,6 +164,12 @@ def prepare_building_stock_data():
},
inplace=True,
)
building_data["feature"].replace(
{
"Construction features (U-value)": "Construction features (U-values)",
},
inplace=True,
)
building_data.country_code = building_data.country_code.str.upper()
building_data["subsector"].replace(
@ -198,12 +204,14 @@ def prepare_building_stock_data():
}
)
building_data["country_code"] = building_data["country"].map(country_iso_dic)
# heated floor area ----------------------------------------------------------
area = building_data[
(building_data.type == "Heated area [Mm²]")
& (building_data.subsector != "Total")
]
area_tot = area.groupby(["country", "sector"]).sum()
area_tot = area[["country", "sector", "value"]].groupby(["country", "sector"]).sum()
area = pd.concat(
[
area,
@ -223,7 +231,7 @@ def prepare_building_stock_data():
usecols=[0, 1, 2, 3],
encoding="ISO-8859-1",
)
area_tot = area_tot.append(area_missing.unstack(level=-1).dropna().stack())
area_tot = pd.concat([area_tot, area_missing.unstack(level=-1).dropna().stack()])
area_tot = area_tot.loc[~area_tot.index.duplicated(keep="last")]
# for still missing countries calculate floor area by population size
@ -246,7 +254,7 @@ def prepare_building_stock_data():
averaged_data.index = index
averaged_data["estimated"] = 1
if ct not in area_tot.index.levels[0]:
area_tot = area_tot.append(averaged_data, sort=True)
area_tot = pd.concat([area_tot, averaged_data], sort=True)
else:
area_tot.loc[averaged_data.index] = averaged_data
@ -272,7 +280,7 @@ def prepare_building_stock_data():
][x["bage"]].iloc[0],
axis=1,
)
data_PL_final = data_PL_final.append(data_PL)
data_PL_final = pd.concat([data_PL_final, data_PL])
u_values = pd.concat([u_values, data_PL_final]).reset_index(drop=True)
@ -609,12 +617,11 @@ def calculate_costs(u_values, l, cost_retro, window_assumptions):
/ x.A_C_Ref
if x.name[3] != "Window"
else (
window_cost(x["new_U_{}".format(l)], cost_retro, window_assumptions)
* x.A_element
(window_cost(x[f"new_U_{l}"], cost_retro, window_assumptions) * x.A_element)
/ x.A_C_Ref
)
if x.value > window_limit(float(l), window_assumptions)
else 0
),
else 0,
axis=1,
)
@ -739,12 +746,12 @@ def calculate_heat_losses(u_values, data_tabula, l_strength, temperature_factor)
# (1) by transmission
# calculate new U values of building elements due to additional insulation
for l in l_strength:
u_values["new_U_{}".format(l)] = calculate_new_u(
u_values[f"new_U_{l}"] = calculate_new_u(
u_values, l, l_weight, window_assumptions
)
# surface area of building components [m^2]
area_element = (
data_tabula[["A_{}".format(e) for e in u_values.index.levels[3]]]
data_tabula[[f"A_{e}" for e in u_values.index.levels[3]]]
.rename(columns=lambda x: x[2:])
.stack()
.unstack(-2)
@ -756,7 +763,7 @@ def calculate_heat_losses(u_values, data_tabula, l_strength, temperature_factor)
# heat transfer H_tr_e [W/m^2K] through building element
# U_e * A_e / A_C_Ref
columns = ["value"] + ["new_U_{}".format(l) for l in l_strength]
columns = ["value"] + [f"new_U_{l}" for l in l_strength]
heat_transfer = pd.concat(
[u_values[columns].mul(u_values.A_element, axis=0), u_values.A_element], axis=1
)
@ -829,9 +836,9 @@ def calculate_heat_losses(u_values, data_tabula, l_strength, temperature_factor)
F_red_temp = map_to_lstrength(l_strength, F_red_temp)
Q_ht = (
heat_transfer_perm2.groupby(level=1, axis=1)
heat_transfer_perm2.T.groupby(level=1)
.sum()
.mul(F_red_temp.droplevel(0, axis=1))
.T.mul(F_red_temp.droplevel(0, axis=1))
.mul(temperature_factor.reindex(heat_transfer_perm2.index, level=0), axis=0)
)
@ -871,14 +878,11 @@ def calculate_gain_utilisation_factor(heat_transfer_perm2, Q_ht, Q_gain):
Calculates gain utilisation factor nu.
"""
# time constant of the building tau [h] = c_m [Wh/(m^2K)] * 1 /(H_tr_e+H_tb*H_ve) [m^2 K /W]
tau = c_m / heat_transfer_perm2.groupby(level=1, axis=1).sum()
tau = c_m / heat_transfer_perm2.T.groupby(axis=1).sum().T
alpha = alpha_H_0 + (tau / tau_H_0)
# heat balance ratio
gamma = (1 / Q_ht).mul(Q_gain.sum(axis=1), axis=0)
# gain utilisation factor
nu = (1 - gamma**alpha) / (1 - gamma ** (alpha + 1))
return nu
return (1 - gamma**alpha) / (1 - gamma ** (alpha + 1))
def calculate_space_heat_savings(
@ -947,7 +951,8 @@ def sample_dE_costs_area(
.rename(index=rename_sectors, level=2)
.reset_index()
)
.rename(columns={"country": "country_code"})
# if uncommented, leads to the second `country_code` column
# .rename(columns={"country": "country_code"})
.set_index(["country_code", "subsector", "bage"])
)
@ -960,13 +965,14 @@ def sample_dE_costs_area(
)
# map missing countries
for ct in countries.difference(cost_dE.index.levels[0]):
for ct in set(countries).difference(cost_dE.index.levels[0]):
averaged_data = (
cost_dE.reindex(index=map_for_missings[ct], level=0)
.mean(level=1)
.groupby(level=1)
.mean()
.set_index(pd.MultiIndex.from_product([[ct], cost_dE.index.levels[1]]))
)
cost_dE = cost_dE.append(averaged_data)
cost_dE = pd.concat([cost_dE, averaged_data])
# weights costs after construction index
if construction_index:
@ -983,24 +989,23 @@ def sample_dE_costs_area(
# drop not considered countries
cost_dE = cost_dE.reindex(countries, level=0)
# get share of residential and service floor area
sec_w = area_tot.value / area_tot.value.groupby(level=0).sum()
sec_w = area_tot.div(area_tot.groupby(level=0).transform("sum"))
# get the total cost-energy-savings weight by sector area
tot = (
cost_dE.mul(sec_w, axis=0)
.groupby(level="country_code")
# sec_w has columns "estimated" and "value"
cost_dE.mul(sec_w.value, axis=0)
# for some reasons names of the levels were lost somewhere
# .groupby(level="country_code")
.groupby(level=0)
.sum()
.set_index(
pd.MultiIndex.from_product(
[cost_dE.index.unique(level="country_code"), ["tot"]]
.set_index(pd.MultiIndex.from_product([cost_dE.index.unique(level=0), ["tot"]]))
)
)
)
cost_dE = cost_dE.append(tot).unstack().stack()
cost_dE = pd.concat([cost_dE, tot]).unstack().stack()
summed_area = pd.DataFrame(area_tot.groupby("country").sum()).set_index(
pd.MultiIndex.from_product([area_tot.index.unique(level="country"), ["tot"]])
summed_area = pd.DataFrame(area_tot.groupby(level=0).sum()).set_index(
pd.MultiIndex.from_product([area_tot.index.unique(level=0), ["tot"]])
)
area_tot = area_tot.append(summed_area).unstack().stack()
area_tot = pd.concat([area_tot, summed_area]).unstack().stack()
cost_per_saving = cost_dE["cost"] / (
1 - cost_dE["dE"]

View File

@ -66,11 +66,7 @@ def salt_cavern_potential_by_region(caverns, regions):
"capacity_per_area * share * area_caverns / 1000"
) # TWh
caverns_regions = (
overlay.groupby(["name", "storage_type"]).e_nom.sum().unstack("storage_type")
)
return caverns_regions
return overlay.groupby(["name", "storage_type"]).e_nom.sum().unstack("storage_type")
if __name__ == "__main__":

View File

@ -119,7 +119,7 @@ def countries(naturalearth, country_list):
fieldnames = (
df[x].where(lambda s: s != "-99") for x in ("ISO_A2", "WB_A2", "ADM0_A3")
)
df["name"] = reduce(lambda x, y: x.fillna(y), fieldnames, next(fieldnames)).str[0:2]
df["name"] = reduce(lambda x, y: x.fillna(y), fieldnames, next(fieldnames)).str[:2]
df = df.loc[
df.name.isin(country_list) & ((df["scalerank"] == 0) | (df["scalerank"] == 5))
@ -174,8 +174,8 @@ def nuts3(country_shapes, nuts3, nuts3pop, nuts3gdp, ch_cantons, ch_popgdp):
pd.MultiIndex.from_tuples(pop.pop("unit,geo\\time").str.split(","))
)
.loc["THS"]
.applymap(lambda x: pd.to_numeric(x, errors="coerce"))
.fillna(method="bfill", axis=1)
.map(lambda x: pd.to_numeric(x, errors="coerce"))
.bfill(axis=1)
)["2014"]
gdp = pd.read_table(nuts3gdp, na_values=[":"], delimiter=" ?\t", engine="python")
@ -184,8 +184,8 @@ def nuts3(country_shapes, nuts3, nuts3pop, nuts3gdp, ch_cantons, ch_popgdp):
pd.MultiIndex.from_tuples(gdp.pop("unit,geo\\time").str.split(","))
)
.loc["EUR_HAB"]
.applymap(lambda x: pd.to_numeric(x, errors="coerce"))
.fillna(method="bfill", axis=1)
.map(lambda x: pd.to_numeric(x, errors="coerce"))
.bfill(axis=1)
)["2014"]
cantons = pd.read_csv(ch_cantons)

View File

@ -64,7 +64,7 @@ if __name__ == "__main__":
with zipfile.ZipFile(snakemake.input.ship_density) as zip_f:
zip_f.extract("shipdensity_global.tif")
with rioxarray.open_rasterio("shipdensity_global.tif") as ship_density:
ship_density = ship_density.drop(["band"]).sel(
ship_density = ship_density.drop_vars(["band"]).sel(
x=slice(min(xs), max(Xs)), y=slice(max(Ys), min(ys))
)
ship_density.rio.to_raster(snakemake.output[0])

View File

@ -81,14 +81,12 @@ def build_transport_demand(traffic_fn, airtemp_fn, nodes, nodal_transport_data):
- pop_weighted_energy_totals["electricity rail"]
)
transport = (
return (
(transport_shape.multiply(energy_totals_transport) * 1e6 * nyears)
.divide(efficiency_gain * ice_correction)
.multiply(1 + dd_EV)
)
return transport
def transport_degree_factor(
temperature,
@ -132,14 +130,12 @@ def bev_availability_profile(fn, snapshots, nodes, options):
traffic.mean() - traffic.min()
)
avail_profile = generate_periodic_profiles(
return generate_periodic_profiles(
dt_index=snapshots,
nodes=nodes,
weekly_profile=avail.values,
)
return avail_profile
def bev_dsm_profile(snapshots, nodes, options):
dsm_week = np.zeros((24 * 7,))
@ -148,14 +144,12 @@ def bev_dsm_profile(snapshots, nodes, options):
"bev_dsm_restriction_value"
]
dsm_profile = generate_periodic_profiles(
return generate_periodic_profiles(
dt_index=snapshots,
nodes=nodes,
weekly_profile=dsm_week,
)
return dsm_profile
if __name__ == "__main__":
if "snakemake" not in globals():

View File

@ -16,7 +16,6 @@ Relevant Settings
clustering:
cluster_network:
aggregation_strategies:
focus_weights:
solving:
@ -237,7 +236,7 @@ def distribute_clusters(n, n_clusters, focus_weights=None, solver_name="cbc"):
n_clusters >= len(N) and n_clusters <= N.sum()
), f"Number of clusters must be {len(N)} <= n_clusters <= {N.sum()} for this selection of countries."
if focus_weights is not None:
if isinstance(focus_weights, dict):
total_focus = sum(list(focus_weights.values()))
assert (
@ -271,7 +270,7 @@ def distribute_clusters(n, n_clusters, focus_weights=None, solver_name="cbc"):
)
opt = po.SolverFactory(solver_name)
if not opt.has_capability("quadratic_objective"):
if solver_name == "appsi_highs" or not opt.has_capability("quadratic_objective"):
logger.warning(
f"The configured solver `{solver_name}` does not support quadratic objectives. Falling back to `ipopt`."
)
@ -322,9 +321,9 @@ def busmap_for_n_clusters(
neighbor_bus = n.lines.query(
"bus0 == @disconnected_bus or bus1 == @disconnected_bus"
).iloc[0][["bus0", "bus1"]]
new_country = list(
set(n.buses.loc[neighbor_bus].country) - set([country])
)[0]
new_country = list(set(n.buses.loc[neighbor_bus].country) - {country})[
0
]
logger.info(
f"overwriting country `{country}` of bus `{disconnected_bus}` "
@ -466,9 +465,13 @@ if __name__ == "__main__":
params = snakemake.params
solver_name = snakemake.config["solving"]["solver"]["name"]
solver_name = "appsi_highs" if solver_name == "highs" else solver_name
n = pypsa.Network(snakemake.input.network)
# remove integer outputs for compatibility with PyPSA v0.26.0
n.generators.drop("n_mod", axis=1, inplace=True, errors="ignore")
exclude_carriers = params.cluster_network["exclude_carriers"]
aggregate_carriers = set(n.generators.carrier) - set(exclude_carriers)
conventional_carriers = set(params.conventional_carriers)

View File

@ -0,0 +1,156 @@
# -*- coding: utf-8 -*-
# SPDX-FileCopyrightText: : 2017-2023 The PyPSA-Eur Authors
#
# SPDX-License-Identifier: MIT
import functools
import logging
import time
import atlite
import fiona
import geopandas as gpd
import matplotlib.pyplot as plt
import numpy as np
from _helpers import configure_logging
from atlite.gis import shape_availability
from rasterio.plot import show
logger = logging.getLogger(__name__)
def get_wdpa_layer_name(wdpa_fn, layer_substring):
"""
Get layername from file "wdpa_fn" whose name contains "layer_substring".
"""
l = fiona.listlayers(wdpa_fn)
return [_ for _ in l if layer_substring in _][0]
if __name__ == "__main__":
if "snakemake" not in globals():
from _helpers import mock_snakemake
snakemake = mock_snakemake(
"determine_availability_matrix_MD_UA", technology="solar"
)
configure_logging(snakemake)
nprocesses = None # snakemake.config["atlite"].get("nprocesses")
noprogress = not snakemake.config["atlite"].get("show_progress", True)
config = snakemake.config["renewable"][snakemake.wildcards.technology]
cutout = atlite.Cutout(snakemake.input.cutout)
regions = (
gpd.read_file(snakemake.input.regions).set_index("name").rename_axis("bus")
)
buses = regions.index
excluder = atlite.ExclusionContainer(crs=3035, res=100)
corine = config.get("corine", {})
if "grid_codes" in corine:
# Land cover codes to emulate CORINE results
if snakemake.wildcards.technology == "solar":
codes = [20, 30, 40, 50, 60, 90, 100]
elif snakemake.wildcards.technology == "onwind":
codes = [20, 30, 40, 60, 100]
elif snakemake.wildcards.technology == "offwind-ac":
codes = [80, 200]
elif snakemake.wildcards.technology == "offwind-dc":
codes = [80, 200]
else:
assert False, "technology not supported"
excluder.add_raster(
snakemake.input.copernicus, codes=codes, invert=True, crs="EPSG:4326"
)
if "distance" in corine and corine.get("distance", 0.0) > 0.0:
# Land cover codes to emulate CORINE results
if snakemake.wildcards.technology == "onwind":
codes = [50]
else:
assert False, "technology not supported"
buffer = corine["distance"]
excluder.add_raster(
snakemake.input.copernicus, codes=codes, buffer=buffer, crs="EPSG:4326"
)
if config["natura"]:
wdpa_fn = (
snakemake.input.wdpa_marine
if "offwind" in snakemake.wildcards.technology
else snakemake.input.wdpa
)
layer = get_wdpa_layer_name(wdpa_fn, "polygons")
wdpa = gpd.read_file(
wdpa_fn,
bbox=regions.geometry,
layer=layer,
).to_crs(3035)
if not wdpa.empty:
excluder.add_geometry(wdpa.geometry)
layer = get_wdpa_layer_name(wdpa_fn, "points")
wdpa_pts = gpd.read_file(
wdpa_fn,
bbox=regions.geometry,
layer=layer,
).to_crs(3035)
wdpa_pts = wdpa_pts[wdpa_pts["REP_AREA"] > 1]
wdpa_pts["buffer_radius"] = np.sqrt(wdpa_pts["REP_AREA"] / np.pi) * 1000
wdpa_pts = wdpa_pts.set_geometry(
wdpa_pts["geometry"].buffer(wdpa_pts["buffer_radius"])
)
if not wdpa_pts.empty:
excluder.add_geometry(wdpa_pts.geometry)
if "max_depth" in config:
# lambda not supported for atlite + multiprocessing
# use named function np.greater with partially frozen argument instead
# and exclude areas where: -max_depth > grid cell depth
func = functools.partial(np.greater, -config["max_depth"])
excluder.add_raster(snakemake.input.gebco, codes=func, crs=4236, nodata=-1000)
if "min_shore_distance" in config:
buffer = config["min_shore_distance"]
excluder.add_geometry(snakemake.input.country_shapes, buffer=buffer)
if "max_shore_distance" in config:
buffer = config["max_shore_distance"]
excluder.add_geometry(
snakemake.input.country_shapes, buffer=buffer, invert=True
)
if "ship_threshold" in config:
shipping_threshold = config["ship_threshold"] * 8760 * 6
func = functools.partial(np.less, shipping_threshold)
excluder.add_raster(
snakemake.input.ship_density, codes=func, crs=4326, allow_no_overlap=True
)
kwargs = dict(nprocesses=nprocesses, disable_progressbar=noprogress)
if noprogress:
logger.info("Calculate landuse availabilities...")
start = time.time()
availability = cutout.availabilitymatrix(regions, excluder, **kwargs)
duration = time.time() - start
logger.info(f"Completed availability calculation ({duration:2.2f}s)")
else:
availability = cutout.availabilitymatrix(regions, excluder, **kwargs)
regions_geometry = regions.to_crs(3035).geometry
band, transform = shape_availability(regions_geometry, excluder)
fig, ax = plt.subplots(figsize=(4, 8))
gpd.GeoSeries(regions_geometry.unary_union).plot(ax=ax, color="none")
show(band, transform=transform, cmap="Greens", ax=ax)
plt.axis("off")
plt.savefig(snakemake.output.availability_map, bbox_inches="tight", dpi=500)
# Limit results only to buses for UA and MD
buses = regions.loc[regions["country"].isin(["UA", "MD"])].index.values
availability = availability.sel(bus=buses)
# Save and plot for verification
availability.to_netcdf(snakemake.output.availability_matrix)

View File

@ -33,10 +33,7 @@ def assign_locations(n):
ifind = pd.Series(c.df.index.str.find(" ", start=4), c.df.index)
for i in ifind.unique():
names = ifind.index[ifind == i]
if i == -1:
c.df.loc[names, "location"] = ""
else:
c.df.loc[names, "location"] = names.str[:i]
c.df.loc[names, "location"] = "" if i == -1 else names.str[:i]
def calculate_nodal_cfs(n, label, nodal_cfs):
@ -397,7 +394,7 @@ def calculate_supply_energy(n, label, supply_energy):
for c in n.iterate_components(n.branch_components):
for end in [col[3:] for col in c.df.columns if col[:3] == "bus"]:
items = c.df.index[c.df["bus" + str(end)].map(bus_map).fillna(False)]
items = c.df.index[c.df[f"bus{str(end)}"].map(bus_map).fillna(False)]
if len(items) == 0:
continue
@ -449,6 +446,10 @@ def calculate_metrics(n, label, metrics):
if "CO2Limit" in n.global_constraints.index:
metrics.at["co2_shadow", label] = n.global_constraints.at["CO2Limit", "mu"]
if "co2_sequestration_limit" in n.global_constraints.index:
metrics.at["co2_storage_shadow", label] = n.global_constraints.at[
"co2_sequestration_limit", "mu"
]
return metrics
@ -493,7 +494,7 @@ def calculate_weighted_prices(n, label, weighted_prices):
"H2": ["Sabatier", "H2 Fuel Cell"],
}
for carrier in link_loads:
for carrier, value in link_loads.items():
if carrier == "electricity":
suffix = ""
elif carrier[:5] == "space":
@ -515,14 +516,14 @@ def calculate_weighted_prices(n, label, weighted_prices):
else:
load = n.loads_t.p_set[buses]
for tech in link_loads[carrier]:
for tech in value:
names = n.links.index[n.links.index.to_series().str[-len(tech) :] == tech]
if names.empty:
continue
if not names.empty:
load += (
n.links_t.p0[names].groupby(n.links.loc[names, "bus0"], axis=1).sum()
n.links_t.p0[names]
.groupby(n.links.loc[names, "bus0"], axis=1)
.sum()
)
# Add H2 Store when charging
@ -650,11 +651,7 @@ def make_summaries(networks_dict):
networks_dict.keys(), names=["cluster", "ll", "opt", "planning_horizon"]
)
df = {}
for output in outputs:
df[output] = pd.DataFrame(columns=columns, dtype=float)
df = {output: pd.DataFrame(columns=columns, dtype=float) for output in outputs}
for label, filename in networks_dict.items():
logger.info(f"Make summary for scenario {label}, using {filename}")

View File

@ -0,0 +1,755 @@
# -*- coding: utf-8 -*-
# SPDX-FileCopyrightText: : 2020-2023 The PyPSA-Eur Authors
#
# SPDX-License-Identifier: MIT
"""
Create summary CSV files for all scenario runs with perfect foresight including
costs, capacities, capacity factors, curtailment, energy balances, prices and
other metrics.
"""
import numpy as np
import pandas as pd
import pypsa
from make_summary import (
assign_carriers,
assign_locations,
calculate_cfs,
calculate_nodal_cfs,
calculate_nodal_costs,
)
from prepare_sector_network import prepare_costs
from pypsa.descriptors import get_active_assets, nominal_attrs
from six import iteritems
idx = pd.IndexSlice
opt_name = {"Store": "e", "Line": "s", "Transformer": "s"}
def reindex_columns(df, cols):
investments = cols.levels[3]
if len(cols.names) != len(df.columns.levels):
df = pd.concat([df] * len(investments), axis=1)
df.columns = cols
df = df.reindex(cols, axis=1)
return df
def calculate_costs(n, label, costs):
investments = n.investment_periods
cols = pd.MultiIndex.from_product(
[
costs.columns.levels[0],
costs.columns.levels[1],
costs.columns.levels[2],
investments,
],
names=costs.columns.names[:3] + ["year"],
)
costs = reindex_columns(costs, cols)
for c in n.iterate_components(
n.branch_components | n.controllable_one_port_components ^ {"Load"}
):
capital_costs = c.df.capital_cost * c.df[opt_name.get(c.name, "p") + "_nom_opt"]
active = pd.concat(
[
get_active_assets(n, c.name, inv_p).rename(inv_p)
for inv_p in investments
],
axis=1,
).astype(int)
capital_costs = active.mul(capital_costs, axis=0)
discount = (
n.investment_period_weightings["objective"]
/ n.investment_period_weightings["years"]
)
capital_costs_grouped = capital_costs.groupby(c.df.carrier).sum().mul(discount)
capital_costs_grouped = pd.concat([capital_costs_grouped], keys=["capital"])
capital_costs_grouped = pd.concat([capital_costs_grouped], keys=[c.list_name])
costs = costs.reindex(capital_costs_grouped.index.union(costs.index))
costs.loc[capital_costs_grouped.index, label] = capital_costs_grouped.values
if c.name == "Link":
p = (
c.pnl.p0.multiply(n.snapshot_weightings.generators, axis=0)
.groupby(level=0)
.sum()
)
elif c.name == "Line":
continue
elif c.name == "StorageUnit":
p_all = c.pnl.p.multiply(n.snapshot_weightings.stores, axis=0)
p_all[p_all < 0.0] = 0.0
p = p_all.groupby(level=0).sum()
else:
p = (
round(c.pnl.p, ndigits=2)
.multiply(n.snapshot_weightings.generators, axis=0)
.groupby(level=0)
.sum()
)
# correct sequestration cost
if c.name == "Store":
items = c.df.index[
(c.df.carrier == "co2 stored") & (c.df.marginal_cost <= -100.0)
]
c.df.loc[items, "marginal_cost"] = -20.0
marginal_costs = p.mul(c.df.marginal_cost).T
# marginal_costs = active.mul(marginal_costs, axis=0)
marginal_costs_grouped = (
marginal_costs.groupby(c.df.carrier).sum().mul(discount)
)
marginal_costs_grouped = pd.concat([marginal_costs_grouped], keys=["marginal"])
marginal_costs_grouped = pd.concat([marginal_costs_grouped], keys=[c.list_name])
costs = costs.reindex(marginal_costs_grouped.index.union(costs.index))
costs.loc[marginal_costs_grouped.index, label] = marginal_costs_grouped.values
# add back in all hydro
# costs.loc[("storage_units","capital","hydro"),label] = (0.01)*2e6*n.storage_units.loc[n.storage_units.group=="hydro","p_nom"].sum()
# costs.loc[("storage_units","capital","PHS"),label] = (0.01)*2e6*n.storage_units.loc[n.storage_units.group=="PHS","p_nom"].sum()
# costs.loc[("generators","capital","ror"),label] = (0.02)*3e6*n.generators.loc[n.generators.group=="ror","p_nom"].sum()
return costs
def calculate_cumulative_cost():
planning_horizons = snakemake.config["scenario"]["planning_horizons"]
cumulative_cost = pd.DataFrame(
index=df["costs"].sum().index,
columns=pd.Series(data=np.arange(0, 0.1, 0.01), name="social discount rate"),
)
# discount cost and express them in money value of planning_horizons[0]
for r in cumulative_cost.columns:
cumulative_cost[r] = [
df["costs"].sum()[index] / ((1 + r) ** (index[-1] - planning_horizons[0]))
for index in cumulative_cost.index
]
# integrate cost throughout the transition path
for r in cumulative_cost.columns:
for cluster in cumulative_cost.index.get_level_values(level=0).unique():
for lv in cumulative_cost.index.get_level_values(level=1).unique():
for sector_opts in cumulative_cost.index.get_level_values(
level=2
).unique():
cumulative_cost.loc[
(cluster, lv, sector_opts, "cumulative cost"), r
] = np.trapz(
cumulative_cost.loc[
idx[cluster, lv, sector_opts, planning_horizons], r
].values,
x=planning_horizons,
)
return cumulative_cost
def calculate_nodal_capacities(n, label, nodal_capacities):
# Beware this also has extraneous locations for country (e.g. biomass) or continent-wide (e.g. fossil gas/oil) stuff
for c in n.iterate_components(
n.branch_components | n.controllable_one_port_components ^ {"Load"}
):
nodal_capacities_c = c.df.groupby(["location", "carrier"])[
opt_name.get(c.name, "p") + "_nom_opt"
].sum()
index = pd.MultiIndex.from_tuples(
[(c.list_name,) + t for t in nodal_capacities_c.index.to_list()]
)
nodal_capacities = nodal_capacities.reindex(index.union(nodal_capacities.index))
nodal_capacities.loc[index, label] = nodal_capacities_c.values
return nodal_capacities
def calculate_capacities(n, label, capacities):
investments = n.investment_periods
cols = pd.MultiIndex.from_product(
[
capacities.columns.levels[0],
capacities.columns.levels[1],
capacities.columns.levels[2],
investments,
],
names=capacities.columns.names[:3] + ["year"],
)
capacities = reindex_columns(capacities, cols)
for c in n.iterate_components(
n.branch_components | n.controllable_one_port_components ^ {"Load"}
):
active = pd.concat(
[
get_active_assets(n, c.name, inv_p).rename(inv_p)
for inv_p in investments
],
axis=1,
).astype(int)
caps = c.df[opt_name.get(c.name, "p") + "_nom_opt"]
caps = active.mul(caps, axis=0)
capacities_grouped = (
caps.groupby(c.df.carrier).sum().drop("load", errors="ignore")
)
capacities_grouped = pd.concat([capacities_grouped], keys=[c.list_name])
capacities = capacities.reindex(
capacities_grouped.index.union(capacities.index)
)
capacities.loc[capacities_grouped.index, label] = capacities_grouped.values
return capacities
def calculate_curtailment(n, label, curtailment):
avail = (
n.generators_t.p_max_pu.multiply(n.generators.p_nom_opt)
.sum()
.groupby(n.generators.carrier)
.sum()
)
used = n.generators_t.p.sum().groupby(n.generators.carrier).sum()
curtailment[label] = (((avail - used) / avail) * 100).round(3)
return curtailment
def calculate_energy(n, label, energy):
investments = n.investment_periods
cols = pd.MultiIndex.from_product(
[
energy.columns.levels[0],
energy.columns.levels[1],
energy.columns.levels[2],
investments,
],
names=energy.columns.names[:3] + ["year"],
)
energy = reindex_columns(energy, cols)
for c in n.iterate_components(n.one_port_components | n.branch_components):
if c.name in n.one_port_components:
c_energies = (
c.pnl.p.multiply(n.snapshot_weightings.generators, axis=0)
.groupby(level=0)
.sum()
.multiply(c.df.sign)
.groupby(c.df.carrier, axis=1)
.sum()
)
else:
c_energies = pd.DataFrame(
0.0, columns=c.df.carrier.unique(), index=n.investment_periods
)
for port in [col[3:] for col in c.df.columns if col[:3] == "bus"]:
totals = (
c.pnl["p" + port]
.multiply(n.snapshot_weightings.generators, axis=0)
.groupby(level=0)
.sum()
)
# remove values where bus is missing (bug in nomopyomo)
no_bus = c.df.index[c.df["bus" + port] == ""]
totals[no_bus] = float(
n.component_attrs[c.name].loc["p" + port, "default"]
)
c_energies -= totals.groupby(c.df.carrier, axis=1).sum()
c_energies = pd.concat([c_energies.T], keys=[c.list_name])
energy = energy.reindex(c_energies.index.union(energy.index))
energy.loc[c_energies.index, label] = c_energies.values
return energy
def calculate_supply(n, label, supply):
"""
Calculate the max dispatch of each component at the buses aggregated by
carrier.
"""
bus_carriers = n.buses.carrier.unique()
for i in bus_carriers:
bus_map = n.buses.carrier == i
bus_map.at[""] = False
for c in n.iterate_components(n.one_port_components):
items = c.df.index[c.df.bus.map(bus_map).fillna(False)]
if len(items) == 0:
continue
s = (
c.pnl.p[items]
.max()
.multiply(c.df.loc[items, "sign"])
.groupby(c.df.loc[items, "carrier"])
.sum()
)
s = pd.concat([s], keys=[c.list_name])
s = pd.concat([s], keys=[i])
supply = supply.reindex(s.index.union(supply.index))
supply.loc[s.index, label] = s
for c in n.iterate_components(n.branch_components):
for end in [col[3:] for col in c.df.columns if col[:3] == "bus"]:
items = c.df.index[c.df["bus" + end].map(bus_map).fillna(False)]
if len(items) == 0:
continue
# lots of sign compensation for direction and to do maximums
s = (-1) ** (1 - int(end)) * (
(-1) ** int(end) * c.pnl["p" + end][items]
).max().groupby(c.df.loc[items, "carrier"]).sum()
s.index = s.index + end
s = pd.concat([s], keys=[c.list_name])
s = pd.concat([s], keys=[i])
supply = supply.reindex(s.index.union(supply.index))
supply.loc[s.index, label] = s
return supply
def calculate_supply_energy(n, label, supply_energy):
"""
Calculate the total energy supply/consuption of each component at the buses
aggregated by carrier.
"""
investments = n.investment_periods
cols = pd.MultiIndex.from_product(
[
supply_energy.columns.levels[0],
supply_energy.columns.levels[1],
supply_energy.columns.levels[2],
investments,
],
names=supply_energy.columns.names[:3] + ["year"],
)
supply_energy = reindex_columns(supply_energy, cols)
bus_carriers = n.buses.carrier.unique()
for i in bus_carriers:
bus_map = n.buses.carrier == i
bus_map.at[""] = False
for c in n.iterate_components(n.one_port_components):
items = c.df.index[c.df.bus.map(bus_map).fillna(False)]
if len(items) == 0:
continue
if c.name == "Generator":
weightings = n.snapshot_weightings.generators
else:
weightings = n.snapshot_weightings.stores
if i in ["oil", "co2", "H2"]:
if c.name == "Load":
c.df.loc[items, "carrier"] = [
load.split("-202")[0] for load in items
]
if i == "oil" and c.name == "Generator":
c.df.loc[items, "carrier"] = "imported oil"
s = (
c.pnl.p[items]
.multiply(weightings, axis=0)
.groupby(level=0)
.sum()
.multiply(c.df.loc[items, "sign"])
.groupby(c.df.loc[items, "carrier"], axis=1)
.sum()
.T
)
s = pd.concat([s], keys=[c.list_name])
s = pd.concat([s], keys=[i])
supply_energy = supply_energy.reindex(
s.index.union(supply_energy.index, sort=False)
)
supply_energy.loc[s.index, label] = s.values
for c in n.iterate_components(n.branch_components):
for end in [col[3:] for col in c.df.columns if col[:3] == "bus"]:
items = c.df.index[c.df[f"bus{str(end)}"].map(bus_map).fillna(False)]
if len(items) == 0:
continue
s = (
(-1)
* c.pnl["p" + end]
.reindex(items, axis=1)
.multiply(n.snapshot_weightings.objective, axis=0)
.groupby(level=0)
.sum()
.groupby(c.df.loc[items, "carrier"], axis=1)
.sum()
).T
s.index = s.index + end
s = pd.concat([s], keys=[c.list_name])
s = pd.concat([s], keys=[i])
supply_energy = supply_energy.reindex(
s.index.union(supply_energy.index, sort=False)
)
supply_energy.loc[s.index, label] = s.values
return supply_energy
def calculate_metrics(n, label, metrics):
metrics = metrics.reindex(
pd.Index(
[
"line_volume",
"line_volume_limit",
"line_volume_AC",
"line_volume_DC",
"line_volume_shadow",
"co2_shadow",
]
).union(metrics.index)
)
metrics.at["line_volume_DC", label] = (n.links.length * n.links.p_nom_opt)[
n.links.carrier == "DC"
].sum()
metrics.at["line_volume_AC", label] = (n.lines.length * n.lines.s_nom_opt).sum()
metrics.at["line_volume", label] = metrics.loc[
["line_volume_AC", "line_volume_DC"], label
].sum()
if hasattr(n, "line_volume_limit"):
metrics.at["line_volume_limit", label] = n.line_volume_limit
metrics.at["line_volume_shadow", label] = n.line_volume_limit_dual
if "CO2Limit" in n.global_constraints.index:
metrics.at["co2_shadow", label] = n.global_constraints.at["CO2Limit", "mu"]
return metrics
def calculate_prices(n, label, prices):
prices = prices.reindex(prices.index.union(n.buses.carrier.unique()))
# WARNING: this is time-averaged, see weighted_prices for load-weighted average
prices[label] = n.buses_t.marginal_price.mean().groupby(n.buses.carrier).mean()
return prices
def calculate_weighted_prices(n, label, weighted_prices):
# Warning: doesn't include storage units as loads
weighted_prices = weighted_prices.reindex(
pd.Index(
[
"electricity",
"heat",
"space heat",
"urban heat",
"space urban heat",
"gas",
"H2",
]
)
)
link_loads = {
"electricity": [
"heat pump",
"resistive heater",
"battery charger",
"H2 Electrolysis",
],
"heat": ["water tanks charger"],
"urban heat": ["water tanks charger"],
"space heat": [],
"space urban heat": [],
"gas": ["OCGT", "gas boiler", "CHP electric", "CHP heat"],
"H2": ["Sabatier", "H2 Fuel Cell"],
}
for carrier, value in link_loads.items():
if carrier == "electricity":
suffix = ""
elif carrier[:5] == "space":
suffix = carrier[5:]
else:
suffix = " " + carrier
buses = n.buses.index[n.buses.index.str[2:] == suffix]
if buses.empty:
continue
load = (
pd.DataFrame(index=n.snapshots, columns=buses, data=0.0)
if carrier in ["H2", "gas"]
else n.loads_t.p_set.reindex(buses, axis=1)
)
for tech in value:
names = n.links.index[n.links.index.to_series().str[-len(tech) :] == tech]
if names.empty:
continue
load += (
n.links_t.p0[names].groupby(n.links.loc[names, "bus0"], axis=1).sum()
)
# Add H2 Store when charging
# if carrier == "H2":
# stores = n.stores_t.p[buses+ " Store"].groupby(n.stores.loc[buses+ " Store","bus"],axis=1).sum(axis=1)
# stores[stores > 0.] = 0.
# load += -stores
weighted_prices.loc[carrier, label] = (
load * n.buses_t.marginal_price[buses]
).sum().sum() / load.sum().sum()
if carrier[:5] == "space":
print(load * n.buses_t.marginal_price[buses])
return weighted_prices
def calculate_market_values(n, label, market_values):
# Warning: doesn't include storage units
carrier = "AC"
buses = n.buses.index[n.buses.carrier == carrier]
## First do market value of generators ##
generators = n.generators.index[n.buses.loc[n.generators.bus, "carrier"] == carrier]
techs = n.generators.loc[generators, "carrier"].value_counts().index
market_values = market_values.reindex(market_values.index.union(techs))
for tech in techs:
gens = generators[n.generators.loc[generators, "carrier"] == tech]
dispatch = (
n.generators_t.p[gens]
.groupby(n.generators.loc[gens, "bus"], axis=1)
.sum()
.reindex(columns=buses, fill_value=0.0)
)
revenue = dispatch * n.buses_t.marginal_price[buses]
market_values.at[tech, label] = revenue.sum().sum() / dispatch.sum().sum()
## Now do market value of links ##
for i in ["0", "1"]:
all_links = n.links.index[n.buses.loc[n.links["bus" + i], "carrier"] == carrier]
techs = n.links.loc[all_links, "carrier"].value_counts().index
market_values = market_values.reindex(market_values.index.union(techs))
for tech in techs:
links = all_links[n.links.loc[all_links, "carrier"] == tech]
dispatch = (
n.links_t["p" + i][links]
.groupby(n.links.loc[links, "bus" + i], axis=1)
.sum()
.reindex(columns=buses, fill_value=0.0)
)
revenue = dispatch * n.buses_t.marginal_price[buses]
market_values.at[tech, label] = revenue.sum().sum() / dispatch.sum().sum()
return market_values
def calculate_price_statistics(n, label, price_statistics):
price_statistics = price_statistics.reindex(
price_statistics.index.union(
pd.Index(["zero_hours", "mean", "standard_deviation"])
)
)
buses = n.buses.index[n.buses.carrier == "AC"]
threshold = 0.1 # higher than phoney marginal_cost of wind/solar
df = pd.DataFrame(data=0.0, columns=buses, index=n.snapshots)
df[n.buses_t.marginal_price[buses] < threshold] = 1.0
price_statistics.at["zero_hours", label] = df.sum().sum() / (
df.shape[0] * df.shape[1]
)
price_statistics.at["mean", label] = n.buses_t.marginal_price[buses].mean().mean()
price_statistics.at["standard_deviation", label] = (
n.buses_t.marginal_price[buses].std().std()
)
return price_statistics
def calculate_co2_emissions(n, label, df):
carattr = "co2_emissions"
emissions = n.carriers.query(f"{carattr} != 0")[carattr]
if emissions.empty:
return
weightings = n.snapshot_weightings.generators.mul(
n.investment_period_weightings["years"]
.reindex(n.snapshots)
.fillna(method="bfill")
.fillna(1.0),
axis=0,
)
# generators
gens = n.generators.query("carrier in @emissions.index")
if not gens.empty:
em_pu = gens.carrier.map(emissions) / gens.efficiency
em_pu = (
weightings["generators"].to_frame("weightings")
@ em_pu.to_frame("weightings").T
)
emitted = n.generators_t.p[gens.index].mul(em_pu)
emitted_grouped = (
emitted.groupby(level=0).sum().groupby(n.generators.carrier, axis=1).sum().T
)
df = df.reindex(emitted_grouped.index.union(df.index))
df.loc[emitted_grouped.index, label] = emitted_grouped.values
if any(n.stores.carrier == "co2"):
co2_i = n.stores[n.stores.carrier == "co2"].index
df[label] = n.stores_t.e.groupby(level=0).last()[co2_i].iloc[:, 0]
return df
outputs = [
"nodal_costs",
"nodal_capacities",
"nodal_cfs",
"cfs",
"costs",
"capacities",
"curtailment",
"energy",
"supply",
"supply_energy",
"prices",
"weighted_prices",
"price_statistics",
"market_values",
"metrics",
"co2_emissions",
]
def make_summaries(networks_dict):
columns = pd.MultiIndex.from_tuples(
networks_dict.keys(), names=["cluster", "lv", "opt"]
)
df = {}
for output in outputs:
df[output] = pd.DataFrame(columns=columns, dtype=float)
for label, filename in iteritems(networks_dict):
print(label, filename)
try:
n = pypsa.Network(filename)
except OSError:
print(label, " not solved yet.")
continue
# del networks_dict[label]
if not hasattr(n, "objective"):
print(label, " not solved correctly. Check log if infeasible or unbounded.")
continue
assign_carriers(n)
assign_locations(n)
for output in outputs:
df[output] = globals()["calculate_" + output](n, label, df[output])
return df
def to_csv(df):
for key in df:
df[key] = df[key].apply(lambda x: pd.to_numeric(x))
df[key].to_csv(snakemake.output[key])
if __name__ == "__main__":
# Detect running outside of snakemake and mock snakemake for testing
if "snakemake" not in globals():
from _helpers import mock_snakemake
snakemake = mock_snakemake("make_summary_perfect")
run = snakemake.config["run"]["name"]
if run != "":
run += "/"
networks_dict = {
(clusters, lv, opts + sector_opts): "results/"
+ run
+ f"postnetworks/elec_s{simpl}_{clusters}_l{lv}_{opts}_{sector_opts}_brownfield_all_years.nc"
for simpl in snakemake.config["scenario"]["simpl"]
for clusters in snakemake.config["scenario"]["clusters"]
for opts in snakemake.config["scenario"]["opts"]
for sector_opts in snakemake.config["scenario"]["sector_opts"]
for lv in snakemake.config["scenario"]["ll"]
}
print(networks_dict)
nyears = 1
costs_db = prepare_costs(
snakemake.input.costs,
snakemake.config["costs"],
nyears,
)
df = make_summaries(networks_dict)
df["metrics"].loc["total costs"] = df["costs"].sum().groupby(level=[0, 1, 2]).sum()
to_csv(df)

View File

@ -24,7 +24,7 @@ from make_summary import assign_carriers
from plot_summary import preferred_order, rename_techs
from pypsa.plot import add_legend_circles, add_legend_lines, add_legend_patches
plt.style.use(["ggplot", "matplotlibrc"])
plt.style.use(["ggplot"])
def rename_techs_tyndp(tech):
@ -145,12 +145,12 @@ def plot_map(
ac_color = "rosybrown"
dc_color = "darkseagreen"
title = "added grid"
if snakemake.wildcards["ll"] == "v1.0":
# should be zero
line_widths = n.lines.s_nom_opt - n.lines.s_nom
link_widths = n.links.p_nom_opt - n.links.p_nom
title = "added grid"
if transmission:
line_widths = n.lines.s_nom_opt
link_widths = n.links.p_nom_opt
@ -160,8 +160,6 @@ def plot_map(
else:
line_widths = n.lines.s_nom_opt - n.lines.s_nom_min
link_widths = n.links.p_nom_opt - n.links.p_nom_min
title = "added grid"
if transmission:
line_widths = n.lines.s_nom_opt
link_widths = n.links.p_nom_opt
@ -262,12 +260,7 @@ def group_pipes(df, drop_direction=False):
lambda x: f"H2 pipeline {x.bus0.replace(' H2', '')} -> {x.bus1.replace(' H2', '')}",
axis=1,
)
# group pipe lines connecting the same buses and rename them for plotting
pipe_capacity = df.groupby(level=0).agg(
{"p_nom_opt": sum, "bus0": "first", "bus1": "first"}
)
return pipe_capacity
return df.groupby(level=0).agg({"p_nom_opt": sum, "bus0": "first", "bus1": "first"})
def plot_h2_map(network, regions):
@ -766,11 +759,13 @@ def plot_series(network, carrier="AC", name="test"):
supply = pd.concat(
(
supply,
(-1)
* c.pnl["p" + str(i)]
.loc[:, c.df.index[c.df["bus" + str(i)].isin(buses)]]
(
-1
* c.pnl[f"p{str(i)}"]
.loc[:, c.df.index[c.df[f"bus{str(i)}"].isin(buses)]]
.groupby(c.df.carrier, axis=1)
.sum(),
.sum()
),
),
axis=1,
)
@ -913,6 +908,158 @@ def plot_series(network, carrier="AC", name="test"):
)
def plot_map_perfect(
network,
components=["Link", "Store", "StorageUnit", "Generator"],
bus_size_factor=1.7e10,
):
n = network.copy()
assign_location(n)
# Drop non-electric buses so they don't clutter the plot
n.buses.drop(n.buses.index[n.buses.carrier != "AC"], inplace=True)
# investment periods
investments = n.snapshots.levels[0]
costs = {}
for comp in components:
df_c = n.df(comp)
if df_c.empty:
continue
df_c["nice_group"] = df_c.carrier.map(rename_techs_tyndp)
attr = "e_nom_opt" if comp == "Store" else "p_nom_opt"
active = pd.concat(
[n.get_active_assets(comp, inv_p).rename(inv_p) for inv_p in investments],
axis=1,
).astype(int)
capital_cost = n.df(comp)[attr] * n.df(comp).capital_cost
capital_cost_t = (
(active.mul(capital_cost, axis=0))
.groupby([n.df(comp).location, n.df(comp).nice_group])
.sum()
)
capital_cost_t.drop("load", level=1, inplace=True, errors="ignore")
costs[comp] = capital_cost_t
costs = pd.concat(costs).groupby(level=[1, 2]).sum()
costs.drop(costs[costs.sum(axis=1) == 0].index, inplace=True)
new_columns = preferred_order.intersection(costs.index.levels[1]).append(
costs.index.levels[1].difference(preferred_order)
)
costs = costs.reindex(new_columns, level=1)
for item in new_columns:
if item not in snakemake.config["plotting"]["tech_colors"]:
print(
"Warning!",
item,
"not in config/plotting/tech_colors, assign random color",
)
snakemake.config["plotting"]["tech_colors"] = "pink"
n.links.drop(
n.links.index[(n.links.carrier != "DC") & (n.links.carrier != "B2B")],
inplace=True,
)
# drop non-bus
to_drop = costs.index.levels[0].symmetric_difference(n.buses.index)
if len(to_drop) != 0:
print("dropping non-buses", to_drop)
costs.drop(to_drop, level=0, inplace=True, axis=0, errors="ignore")
# make sure they are removed from index
costs.index = pd.MultiIndex.from_tuples(costs.index.values)
# PDF has minimum width, so set these to zero
line_lower_threshold = 500.0
line_upper_threshold = 1e4
linewidth_factor = 2e3
ac_color = "gray"
dc_color = "m"
line_widths = n.lines.s_nom_opt
link_widths = n.links.p_nom_opt
linewidth_factor = 2e3
line_lower_threshold = 0.0
title = "Today's transmission"
line_widths[line_widths < line_lower_threshold] = 0.0
link_widths[link_widths < line_lower_threshold] = 0.0
line_widths[line_widths > line_upper_threshold] = line_upper_threshold
link_widths[link_widths > line_upper_threshold] = line_upper_threshold
for year in costs.columns:
fig, ax = plt.subplots(subplot_kw={"projection": ccrs.PlateCarree()})
fig.set_size_inches(7, 6)
fig.suptitle(year)
n.plot(
bus_sizes=costs[year] / bus_size_factor,
bus_colors=snakemake.config["plotting"]["tech_colors"],
line_colors=ac_color,
link_colors=dc_color,
line_widths=line_widths / linewidth_factor,
link_widths=link_widths / linewidth_factor,
ax=ax,
**map_opts,
)
sizes = [20, 10, 5]
labels = [f"{s} bEUR/a" for s in sizes]
sizes = [s / bus_size_factor * 1e9 for s in sizes]
legend_kw = dict(
loc="upper left",
bbox_to_anchor=(0.01, 1.06),
labelspacing=0.8,
frameon=False,
handletextpad=0,
title="system cost",
)
add_legend_circles(
ax,
sizes,
labels,
srid=n.srid,
patch_kw=dict(facecolor="lightgrey"),
legend_kw=legend_kw,
)
sizes = [10, 5]
labels = [f"{s} GW" for s in sizes]
scale = 1e3 / linewidth_factor
sizes = [s * scale for s in sizes]
legend_kw = dict(
loc="upper left",
bbox_to_anchor=(0.27, 1.06),
frameon=False,
labelspacing=0.8,
handletextpad=1,
title=title,
)
add_legend_lines(
ax, sizes, labels, patch_kw=dict(color="lightgrey"), legend_kw=legend_kw
)
legend_kw = dict(
bbox_to_anchor=(1.52, 1.04),
frameon=False,
)
fig.savefig(
snakemake.output[f"map_{year}"], transparent=True, bbox_inches="tight"
)
if __name__ == "__main__":
if "snakemake" not in globals():
from _helpers import mock_snakemake
@ -921,10 +1068,9 @@ if __name__ == "__main__":
"plot_network",
simpl="",
opts="",
clusters="5",
ll="v1.5",
sector_opts="CO2L0-1H-T-H-B-I-A-solar+p3-dist1",
planning_horizons="2030",
clusters="37",
ll="v1.0",
sector_opts="4380H-T-H-B-I-A-solar+p3-dist1",
)
logging.basicConfig(level=snakemake.config["logging"]["level"])
@ -938,6 +1084,13 @@ if __name__ == "__main__":
if map_opts["boundaries"] is None:
map_opts["boundaries"] = regions.total_bounds[[0, 2, 1, 3]] + [-1, 1, -1, 1]
if snakemake.params["foresight"] == "perfect":
plot_map_perfect(
n,
components=["Link", "Store", "StorageUnit", "Generator"],
bus_size_factor=2e10,
)
else:
plot_map(
n,
components=["generators", "links", "stores", "storage_units"],

View File

@ -33,8 +33,6 @@ if __name__ == "__main__":
lambda s: s != "", "lightgrey"
)
# %%
def rename_index(ds):
specific = ds.index.map(lambda x: f"{x[1]}\n({x[0]})")
generic = ds.index.get_level_values("carrier")

View File

@ -49,6 +49,10 @@ def rename_techs(label):
# "H2 Fuel Cell": "hydrogen storage",
# "H2 pipeline": "hydrogen storage",
"battery": "battery storage",
"H2 for industry": "H2 for industry",
"land transport fuel cell": "land transport fuel cell",
"land transport oil": "land transport oil",
"oil shipping": "shipping oil",
# "CC": "CC"
}
@ -157,11 +161,11 @@ def plot_costs():
df.index.difference(preferred_order)
)
new_columns = df.sum().sort_values().index
# new_columns = df.sum().sort_values().index
fig, ax = plt.subplots(figsize=(12, 8))
df.loc[new_index, new_columns].T.plot(
df.loc[new_index].T.plot(
kind="bar",
ax=ax,
stacked=True,
@ -213,17 +217,22 @@ def plot_energy():
logger.info(f"Total energy of {round(df.sum()[0])} TWh/a")
if df.empty:
fig, ax = plt.subplots(figsize=(12, 8))
fig.savefig(snakemake.output.energy, bbox_inches="tight")
return
new_index = preferred_order.intersection(df.index).append(
df.index.difference(preferred_order)
)
new_columns = df.columns.sort_values()
# new_columns = df.columns.sort_values()
fig, ax = plt.subplots(figsize=(12, 8))
logger.debug(df.loc[new_index, new_columns])
logger.debug(df.loc[new_index])
df.loc[new_index, new_columns].T.plot(
df.loc[new_index].T.plot(
kind="bar",
ax=ax,
stacked=True,
@ -267,8 +276,6 @@ def plot_balances():
i for i in balances_df.index.levels[0] if i not in co2_carriers
]
fig, ax = plt.subplots(figsize=(12, 8))
for k, v in balances.items():
df = balances_df.loc[v]
df = df.groupby(df.index.get_level_values(2)).sum()
@ -279,7 +286,7 @@ def plot_balances():
# remove trailing link ports
df.index = [
i[:-1]
if ((i not in ["co2", "NH3"]) and (i[-1:] in ["0", "1", "2", "3"]))
if ((i not in ["co2", "NH3", "H2"]) and (i[-1:] in ["0", "1", "2", "3"]))
else i
for i in df.index
]
@ -290,11 +297,7 @@ def plot_balances():
df.abs().max(axis=1) < snakemake.params.plotting["energy_threshold"] / 10
]
if v[0] in co2_carriers:
units = "MtCO2/a"
else:
units = "TWh/a"
units = "MtCO2/a" if v[0] in co2_carriers else "TWh/a"
logger.debug(
f"Dropping technology energy balance smaller than {snakemake.params['plotting']['energy_threshold']/10} {units}"
)
@ -313,6 +316,8 @@ def plot_balances():
new_columns = df.columns.sort_values()
fig, ax = plt.subplots(figsize=(12, 8))
df.loc[new_index, new_columns].T.plot(
kind="bar",
ax=ax,
@ -345,8 +350,6 @@ def plot_balances():
fig.savefig(snakemake.output.balances[:-10] + k + ".pdf", bbox_inches="tight")
plt.cla()
def historical_emissions(countries):
"""
@ -354,8 +357,7 @@ def historical_emissions(countries):
"""
# https://www.eea.europa.eu/data-and-maps/data/national-emissions-reported-to-the-unfccc-and-to-the-eu-greenhouse-gas-monitoring-mechanism-16
# downloaded 201228 (modified by EEA last on 201221)
fn = "data/bundle-sector/eea/UNFCCC_v23.csv"
df = pd.read_csv(fn, encoding="latin-1")
df = pd.read_csv(snakemake.input.co2, encoding="latin-1", low_memory=False)
df.loc[df["Year"] == "1985-1987", "Year"] = 1986
df["Year"] = df["Year"].astype(int)
df = df.set_index(
@ -379,18 +381,21 @@ def historical_emissions(countries):
e["waste management"] = "5 - Waste management"
e["other"] = "6 - Other Sector"
e["indirect"] = "ind_CO2 - Indirect CO2"
e["total wL"] = "Total (with LULUCF)"
e["total woL"] = "Total (without LULUCF)"
e["other LULUCF"] = "4.H - Other LULUCF"
pol = ["CO2"] # ["All greenhouse gases - (CO2 equivalent)"]
if "GB" in countries:
countries.remove("GB")
countries.append("UK")
# remove countries which are not included in eea historical emission dataset
countries_to_remove = {"AL", "BA", "ME", "MK", "RS"}
countries = list(set(countries) - countries_to_remove)
year = np.arange(1990, 2018).tolist()
year = df.index.levels[0][df.index.levels[0] >= 1990]
missing = pd.Index(countries).difference(df.index.levels[2])
if not missing.empty:
logger.warning(
f"The following countries are missing and not considered when plotting historic CO2 emissions: {missing}"
)
countries = pd.Index(df.index.levels[2]).intersection(countries)
idx = pd.IndexSlice
co2_totals = (
@ -447,24 +452,17 @@ def plot_carbon_budget_distribution(input_eurostat):
sns.set()
sns.set_style("ticks")
plt.style.use("seaborn-ticks")
plt.rcParams["xtick.direction"] = "in"
plt.rcParams["ytick.direction"] = "in"
plt.rcParams["xtick.labelsize"] = 20
plt.rcParams["ytick.labelsize"] = 20
plt.figure(figsize=(10, 7))
gs1 = gridspec.GridSpec(1, 1)
ax1 = plt.subplot(gs1[0, 0])
ax1.set_ylabel("CO$_2$ emissions (Gt per year)", fontsize=22)
ax1.set_ylim([0, 5])
ax1.set_xlim([1990, snakemake.params.planning_horizons[-1] + 1])
path_cb = "results/" + snakemake.params.RDIR + "csvs/"
countries = snakemake.params.countries
emissions_scope = snakemake.params.emissions_scope
report_year = snakemake.params.eurostat_report_year
input_co2 = snakemake.input.co2
# historic emissions
countries = snakemake.params.countries
e_1990 = co2_emissions_year(
countries,
input_eurostat,
@ -474,15 +472,37 @@ def plot_carbon_budget_distribution(input_eurostat):
input_co2,
year=1990,
)
CO2_CAP = pd.read_csv(path_cb + "carbon_budget_distribution.csv", index_col=0)
ax1.plot(e_1990 * CO2_CAP[o], linewidth=3, color="dodgerblue", label=None)
emissions = historical_emissions(countries)
# add other years https://sdi.eea.europa.eu/data/0569441f-2853-4664-a7cd-db969ef54de0
emissions.loc[2019] = 2.971372
emissions.loc[2020] = 2.691958
emissions.loc[2021] = 2.869355
if snakemake.config["foresight"] == "myopic":
path_cb = "results/" + snakemake.params.RDIR + "/csvs/"
co2_cap = pd.read_csv(path_cb + "carbon_budget_distribution.csv", index_col=0)[
["cb"]
]
co2_cap *= e_1990
else:
supply_energy = pd.read_csv(
snakemake.input.balances, index_col=[0, 1, 2], header=[0, 1, 2, 3]
)
co2_cap = (
supply_energy.loc["co2"].droplevel(0).drop("co2").sum().unstack().T / 1e9
)
co2_cap.rename(index=lambda x: int(x), inplace=True)
plt.figure(figsize=(10, 7))
gs1 = gridspec.GridSpec(1, 1)
ax1 = plt.subplot(gs1[0, 0])
ax1.set_ylabel("CO$_2$ emissions \n [Gt per year]", fontsize=22)
# ax1.set_ylim([0, 5])
ax1.set_xlim([1990, snakemake.params.planning_horizons[-1] + 1])
ax1.plot(emissions, color="black", linewidth=3, label=None)
# plot committed and uder-discussion targets
# plot committed and under-discussion targets
# (notice that historical emissions include all countries in the
# network, but targets refer to EU)
ax1.plot(
@ -499,7 +519,7 @@ def plot_carbon_budget_distribution(input_eurostat):
[0.45 * emissions[1990]],
marker="*",
markersize=12,
markerfacecolor="white",
markerfacecolor="black",
markeredgecolor="black",
)
@ -523,21 +543,7 @@ def plot_carbon_budget_distribution(input_eurostat):
ax1.plot(
[2050],
[0.01 * emissions[1990]],
marker="*",
markersize=12,
markerfacecolor="white",
linewidth=0,
markeredgecolor="black",
label="EU under-discussion target",
zorder=10,
clip_on=False,
)
ax1.plot(
[2050],
[0.125 * emissions[1990]],
"ro",
[0.0 * emissions[1990]],
marker="*",
markersize=12,
markerfacecolor="black",
@ -545,12 +551,16 @@ def plot_carbon_budget_distribution(input_eurostat):
label="EU committed target",
)
for col in co2_cap.columns:
ax1.plot(co2_cap[col], linewidth=3, label=col)
ax1.legend(
fancybox=True, fontsize=18, loc=(0.01, 0.01), facecolor="white", frameon=True
)
path_cb_plot = "results/" + snakemake.params.RDIR + "graphs/"
plt.savefig(path_cb_plot + "carbon_budget_plot.pdf", dpi=300)
plt.grid(axis="y")
path = snakemake.output.balances.split("balances")[0] + "carbon_budget.pdf"
plt.savefig(path, bbox_inches="tight")
if __name__ == "__main__":
@ -571,6 +581,5 @@ if __name__ == "__main__":
for sector_opts in snakemake.params.sector_opts:
opts = sector_opts.split("-")
for o in opts:
if "cb" in o:
if any("cb" in o for o in opts) or snakemake.config["foresight"] == "perfect":
plot_carbon_budget_distribution(snakemake.input.eurostat)

View File

@ -84,13 +84,9 @@ def cross_border_time_series(countries, data):
df_neg.plot.area(
ax=ax[axis], stacked=True, linewidth=0.0, color=color, ylim=[-1, 1]
)
if (axis % 2) == 0:
title = "Historic"
else:
title = "Optimized"
title = "Historic" if (axis % 2) == 0 else "Optimized"
ax[axis].set_title(
title + " Import / Export for " + cc.convert(country, to="name_short")
f"{title} Import / Export for " + cc.convert(country, to="name_short")
)
# Custom legend elements
@ -137,16 +133,12 @@ def cross_border_bar(countries, data):
df_country = sort_one_country(country, df)
df_neg, df_pos = df_country.clip(upper=0), df_country.clip(lower=0)
if (order % 2) == 0:
title = "Historic"
else:
title = "Optimized"
title = "Historic" if (order % 2) == 0 else "Optimized"
df_positive_new = pd.DataFrame(data=df_pos.sum()).T.rename(
{0: title + " " + cc.convert(country, to="name_short")}
{0: f"{title} " + cc.convert(country, to="name_short")}
)
df_negative_new = pd.DataFrame(data=df_neg.sum()).T.rename(
{0: title + " " + cc.convert(country, to="name_short")}
{0: f"{title} " + cc.convert(country, to="name_short")}
)
df_positive = pd.concat([df_positive_new, df_positive])

View File

@ -274,7 +274,6 @@ def set_line_nom_max(
n.links.p_nom_max.clip(upper=p_nom_max_set, inplace=True)
# %%
if __name__ == "__main__":
if "snakemake" not in globals():
from _helpers import mock_snakemake

View File

@ -0,0 +1,548 @@
# -*- coding: utf-8 -*-
# SPDX-FileCopyrightText: : 2020-2023 The PyPSA-Eur Authors
#
# SPDX-License-Identifier: MIT
"""
Concats pypsa networks of single investment periods to one network.
"""
import logging
import re
import numpy as np
import pandas as pd
import pypsa
from _helpers import update_config_with_sector_opts
from add_existing_baseyear import add_build_year_to_new_assets
from pypsa.descriptors import expand_series
from pypsa.io import import_components_from_dataframe
from six import iterkeys
logger = logging.getLogger(__name__)
# helper functions ---------------------------------------------------
def get_missing(df, n, c):
"""
Get in network n missing assets of df for component c.
Input:
df: pandas DataFrame, static values of pypsa components
n : pypsa Network to which new assets should be added
c : string, pypsa component.list_name (e.g. "generators")
Return:
pd.DataFrame with static values of missing assets
"""
df_final = getattr(n, c)
missing_i = df.index.difference(df_final.index)
return df.loc[missing_i]
def get_social_discount(t, r=0.01):
"""
Calculate for a given time t and social discount rate r [per unit] the
social discount.
"""
return 1 / (1 + r) ** t
def get_investment_weighting(time_weighting, r=0.01):
"""
Define cost weighting.
Returns cost weightings depending on the the time_weighting
(pd.Series) and the social discountrate r
"""
end = time_weighting.cumsum()
start = time_weighting.cumsum().shift().fillna(0)
return pd.concat([start, end], axis=1).apply(
lambda x: sum(get_social_discount(t, r) for t in range(int(x[0]), int(x[1]))),
axis=1,
)
def add_year_to_constraints(n, baseyear):
"""
Add investment period to global constraints and rename index.
Parameters
----------
n : pypsa.Network
baseyear : int
year in which optimized assets are built
"""
for c in n.iterate_components(["GlobalConstraint"]):
c.df["investment_period"] = baseyear
c.df.rename(index=lambda x: x + "-" + str(baseyear), inplace=True)
def hvdc_transport_model(n):
"""
Convert AC lines to DC links for multi-decade optimisation with line
expansion.
Losses of DC links are assumed to be 3% per 1000km
"""
logger.info("Convert AC lines to DC links to perform multi-decade optimisation.")
n.madd(
"Link",
n.lines.index,
bus0=n.lines.bus0,
bus1=n.lines.bus1,
p_nom_extendable=True,
p_nom=n.lines.s_nom,
p_nom_min=n.lines.s_nom,
p_min_pu=-1,
efficiency=1 - 0.03 * n.lines.length / 1000,
marginal_cost=0,
carrier="DC",
length=n.lines.length,
capital_cost=n.lines.capital_cost,
)
# Remove AC lines
logger.info("Removing AC lines")
lines_rm = n.lines.index
n.mremove("Line", lines_rm)
# Set efficiency of all DC links to include losses depending on length
n.links.loc[n.links.carrier == "DC", "efficiency"] = (
1 - 0.03 * n.links.loc[n.links.carrier == "DC", "length"] / 1000
)
def adjust_electricity_grid(n, year, years):
"""
Add carrier to lines. Replace AC lines with DC links in case of line
expansion. Add lifetime to DC links in case of line expansion.
Parameters
----------
n : pypsa.Network
year : int
year in which optimized assets are built
years: list
investment periods
"""
n.lines["carrier"] = "AC"
links_i = n.links[n.links.carrier == "DC"].index
if n.lines.s_nom_extendable.any() or n.links.loc[links_i, "p_nom_extendable"].any():
hvdc_transport_model(n)
links_i = n.links[n.links.carrier == "DC"].index
n.links.loc[links_i, "lifetime"] = 100
if year != years[0]:
n.links.loc[links_i, "p_nom_min"] = 0
n.links.loc[links_i, "p_nom"] = 0
# --------------------------------------------------------------------
def concat_networks(years):
"""
Concat given pypsa networks and adds build_year.
Return:
n : pypsa.Network for the whole planning horizon
"""
# input paths of sector coupling networks
network_paths = [snakemake.input.brownfield_network] + [
snakemake.input[f"network_{year}"] for year in years[1:]
]
# final concatenated network
n = pypsa.Network()
# iterate over single year networks and concat to perfect foresight network
for i, network_path in enumerate(network_paths):
year = years[i]
network = pypsa.Network(network_path)
adjust_electricity_grid(network, year, years)
add_build_year_to_new_assets(network, year)
# static ----------------------------------
# (1) add buses and carriers
for component in network.iterate_components(["Bus", "Carrier"]):
df_year = component.df
# get missing assets
missing = get_missing(df_year, n, component.list_name)
import_components_from_dataframe(n, missing, component.name)
# (2) add generators, links, stores and loads
for component in network.iterate_components(
["Generator", "Link", "Store", "Load", "Line", "StorageUnit"]
):
df_year = component.df.copy()
missing = get_missing(df_year, n, component.list_name)
import_components_from_dataframe(n, missing, component.name)
# time variant --------------------------------------------------
network_sns = pd.MultiIndex.from_product([[year], network.snapshots])
snapshots = n.snapshots.drop("now", errors="ignore").union(network_sns)
n.set_snapshots(snapshots)
for component in network.iterate_components():
pnl = getattr(n, component.list_name + "_t")
for k in iterkeys(component.pnl):
pnl_year = component.pnl[k].copy().reindex(snapshots, level=1)
if pnl_year.empty and ~(component.name == "Load" and k == "p_set"):
continue
if component.name == "Load":
static_load = network.loads.loc[network.loads.p_set != 0]
static_load_t = expand_series(static_load.p_set, network_sns).T
pnl_year = pd.concat(
[pnl_year.reindex(network_sns), static_load_t], axis=1
)
columns = (pnl[k].columns.union(pnl_year.columns)).unique()
pnl[k] = pnl[k].reindex(columns=columns)
pnl[k].loc[pnl_year.index, pnl_year.columns] = pnl_year
else:
# this is to avoid adding multiple times assets with
# infinite lifetime as ror
cols = pnl_year.columns.difference(pnl[k].columns)
pnl[k] = pd.concat([pnl[k], pnl_year[cols]], axis=1)
n.snapshot_weightings.loc[year, :] = network.snapshot_weightings.values
# (3) global constraints
for component in network.iterate_components(["GlobalConstraint"]):
add_year_to_constraints(network, year)
import_components_from_dataframe(n, component.df, component.name)
# set investment periods
n.investment_periods = n.snapshots.levels[0]
# weighting of the investment period -> assuming last period same weighting as the period before
time_w = n.investment_periods.to_series().diff().shift(-1).fillna(method="ffill")
n.investment_period_weightings["years"] = time_w
# set objective weightings
objective_w = get_investment_weighting(
n.investment_period_weightings["years"], social_discountrate
)
n.investment_period_weightings["objective"] = objective_w
# all former static loads are now time-dependent -> set static = 0
n.loads["p_set"] = 0
n.loads_t.p_set.fillna(0, inplace=True)
return n
def adjust_stores(n):
"""
Make sure that stores still behave cyclic over one year and not whole
modelling horizon.
"""
# cyclic constraint
cyclic_i = n.stores[n.stores.e_cyclic].index
n.stores.loc[cyclic_i, "e_cyclic_per_period"] = True
n.stores.loc[cyclic_i, "e_cyclic"] = False
# non cyclic store assumptions
non_cyclic_store = ["co2", "co2 stored", "solid biomass", "biogas", "Li ion"]
co2_i = n.stores[n.stores.carrier.isin(non_cyclic_store)].index
n.stores.loc[co2_i, "e_cyclic_per_period"] = False
n.stores.loc[co2_i, "e_cyclic"] = False
# e_initial at beginning of each investment period
e_initial_store = ["solid biomass", "biogas"]
co2_i = n.stores[n.stores.carrier.isin(e_initial_store)].index
n.stores.loc[co2_i, "e_initial_per_period"] = True
# n.stores.loc[co2_i, "e_initial"] *= 10
# n.stores.loc[co2_i, "e_nom"] *= 10
e_initial_store = ["co2 stored"]
co2_i = n.stores[n.stores.carrier.isin(e_initial_store)].index
n.stores.loc[co2_i, "e_initial_per_period"] = True
return n
def set_phase_out(n, carrier, ct, phase_out_year):
"""
Set planned phase outs for given carrier,country (ct) and planned year of
phase out (phase_out_year).
"""
df = n.links[(n.links.carrier.isin(carrier)) & (n.links.bus1.str[:2] == ct)]
# assets which are going to be phased out before end of their lifetime
assets_i = df[df[["build_year", "lifetime"]].sum(axis=1) > phase_out_year].index
build_year = n.links.loc[assets_i, "build_year"]
# adjust lifetime
n.links.loc[assets_i, "lifetime"] = (phase_out_year - build_year).astype(float)
def set_all_phase_outs(n):
# TODO move this to a csv or to the config
planned = [
(["nuclear"], "DE", 2022),
(["nuclear"], "BE", 2025),
(["nuclear"], "ES", 2027),
(["coal", "lignite"], "DE", 2030),
(["coal", "lignite"], "ES", 2027),
(["coal", "lignite"], "FR", 2022),
(["coal", "lignite"], "GB", 2024),
(["coal", "lignite"], "IT", 2025),
(["coal", "lignite"], "DK", 2030),
(["coal", "lignite"], "FI", 2030),
(["coal", "lignite"], "HU", 2030),
(["coal", "lignite"], "SK", 2030),
(["coal", "lignite"], "GR", 2030),
(["coal", "lignite"], "IE", 2030),
(["coal", "lignite"], "NL", 2030),
(["coal", "lignite"], "RS", 2030),
]
for carrier, ct, phase_out_year in planned:
set_phase_out(n, carrier, ct, phase_out_year)
# remove assets which are already phased out
remove_i = n.links[n.links[["build_year", "lifetime"]].sum(axis=1) < years[0]].index
n.mremove("Link", remove_i)
def set_carbon_constraints(n, opts):
"""
Add global constraints for carbon emissions.
"""
budget = None
for o in opts:
# other budgets
m = re.match(r"^\d+p\d$", o, re.IGNORECASE)
if m is not None:
budget = snakemake.config["co2_budget"][m.group(0)] * 1e9
if budget != None:
logger.info(f"add carbon budget of {budget}")
n.add(
"GlobalConstraint",
"Budget",
type="Co2Budget",
carrier_attribute="co2_emissions",
sense="<=",
constant=budget,
investment_period=n.investment_periods[-1],
)
# drop other CO2 limits
drop_i = n.global_constraints[n.global_constraints.type == "co2_limit"].index
n.mremove("GlobalConstraint", drop_i)
n.add(
"GlobalConstraint",
"carbon_neutral",
type="co2_limit",
carrier_attribute="co2_emissions",
sense="<=",
constant=0,
investment_period=n.investment_periods[-1],
)
# set minimum CO2 emission constraint to avoid too fast reduction
if "co2min" in opts:
emissions_1990 = 4.53693
emissions_2019 = 3.344096
target_2030 = 0.45 * emissions_1990
annual_reduction = (emissions_2019 - target_2030) / 11
first_year = n.snapshots.levels[0][0]
time_weightings = n.investment_period_weightings.loc[first_year, "years"]
co2min = emissions_2019 - ((first_year - 2019) * annual_reduction)
logger.info(f"add minimum emissions for {first_year} of {co2min} t CO2/a")
n.add(
"GlobalConstraint",
f"Co2Min-{first_year}",
type="Co2min",
carrier_attribute="co2_emissions",
sense=">=",
investment_period=first_year,
constant=co2min * 1e9 * time_weightings,
)
return n
def adjust_lvlimit(n):
"""
Convert global constraints for single investment period to one uniform if
all attributes stay the same.
"""
c = "GlobalConstraint"
cols = ["carrier_attribute", "sense", "constant", "type"]
glc_type = "transmission_volume_expansion_limit"
if (n.df(c)[n.df(c).type == glc_type][cols].nunique() == 1).all():
glc = n.df(c)[n.df(c).type == glc_type][cols].iloc[[0]]
glc.index = pd.Index(["lv_limit"])
remove_i = n.df(c)[n.df(c).type == glc_type].index
n.mremove(c, remove_i)
import_components_from_dataframe(n, glc, c)
return n
def adjust_CO2_glc(n):
c = "GlobalConstraint"
glc_name = "CO2Limit"
glc_type = "primary_energy"
mask = (n.df(c).index.str.contains(glc_name)) & (n.df(c).type == glc_type)
n.df(c).loc[mask, "type"] = "co2_limit"
return n
def add_H2_boilers(n):
"""
Gas boilers can be retrofitted to run with H2.
Add H2 boilers for heating for all existing gas boilers.
"""
c = "Link"
logger.info("Add H2 boilers.")
# existing gas boilers
mask = n.links.carrier.str.contains("gas boiler") & ~n.links.p_nom_extendable
gas_i = n.links[mask].index
df = n.links.loc[gas_i]
# adjust bus 0
df["bus0"] = df.bus1.map(n.buses.location) + " H2"
# rename carrier and index
df["carrier"] = df.carrier.apply(
lambda x: x.replace("gas boiler", "retrofitted H2 boiler")
)
df.rename(
index=lambda x: x.replace("gas boiler", "retrofitted H2 boiler"), inplace=True
)
# todo, costs for retrofitting
df["capital_costs"] = 100
# set existing capacity to zero
df["p_nom"] = 0
df["p_nom_extendable"] = True
# add H2 boilers to network
import_components_from_dataframe(n, df, c)
def apply_time_segmentation_perfect(
n, segments, solver_name="cbc", overwrite_time_dependent=True
):
"""
Aggregating time series to segments with different lengths.
Input:
n: pypsa Network
segments: (int) number of segments in which the typical period should be
subdivided
solver_name: (str) name of solver
overwrite_time_dependent: (bool) overwrite time dependent data of pypsa network
with typical time series created by tsam
"""
try:
import tsam.timeseriesaggregation as tsam
except:
raise ModuleNotFoundError(
"Optional dependency 'tsam' not found." "Install via 'pip install tsam'"
)
# get all time-dependent data
columns = pd.MultiIndex.from_tuples([], names=["component", "key", "asset"])
raw = pd.DataFrame(index=n.snapshots, columns=columns)
for c in n.iterate_components():
for attr, pnl in c.pnl.items():
# exclude e_min_pu which is used for SOC of EVs in the morning
if not pnl.empty and attr != "e_min_pu":
df = pnl.copy()
df.columns = pd.MultiIndex.from_product([[c.name], [attr], df.columns])
raw = pd.concat([raw, df], axis=1)
raw = raw.dropna(axis=1)
sn_weightings = {}
for year in raw.index.levels[0]:
logger.info(f"Find representative snapshots for {year}.")
raw_t = raw.loc[year]
# normalise all time-dependent data
annual_max = raw_t.max().replace(0, 1)
raw_t = raw_t.div(annual_max, level=0)
# get representative segments
agg = tsam.TimeSeriesAggregation(
raw_t,
hoursPerPeriod=len(raw_t),
noTypicalPeriods=1,
noSegments=int(segments),
segmentation=True,
solver=solver_name,
)
segmented = agg.createTypicalPeriods()
weightings = segmented.index.get_level_values("Segment Duration")
offsets = np.insert(np.cumsum(weightings[:-1]), 0, 0)
timesteps = [raw_t.index[0] + pd.Timedelta(f"{offset}h") for offset in offsets]
snapshots = pd.DatetimeIndex(timesteps)
sn_weightings[year] = pd.Series(
weightings, index=snapshots, name="weightings", dtype="float64"
)
sn_weightings = pd.concat(sn_weightings)
n.set_snapshots(sn_weightings.index)
n.snapshot_weightings = n.snapshot_weightings.mul(sn_weightings, axis=0)
return n
def set_temporal_aggregation_SEG(n, opts, solver_name):
"""
Aggregate network temporally with tsam.
"""
for o in opts:
# segments with package tsam
m = re.match(r"^(\d+)seg$", o, re.IGNORECASE)
if m is not None:
segments = int(m[1])
logger.info(f"Use temporal segmentation with {segments} segments")
n = apply_time_segmentation_perfect(n, segments, solver_name=solver_name)
break
return n
if __name__ == "__main__":
if "snakemake" not in globals():
from _helpers import mock_snakemake
snakemake = mock_snakemake(
"prepare_perfect_foresight",
simpl="",
opts="",
clusters="37",
ll="v1.5",
sector_opts="1p7-4380H-T-H-B-I-A-solar+p3-dist1",
)
update_config_with_sector_opts(snakemake.config, snakemake.wildcards.sector_opts)
# parameters -----------------------------------------------------------
years = snakemake.config["scenario"]["planning_horizons"]
opts = snakemake.wildcards.sector_opts.split("-")
social_discountrate = snakemake.config["costs"]["social_discountrate"]
for o in opts:
if "sdr" in o:
social_discountrate = float(o.replace("sdr", "")) / 100
logger.info(
f"Concat networks of investment period {years} with social discount rate of {social_discountrate * 100}%"
)
# concat prenetworks of planning horizon to single network ------------
n = concat_networks(years)
# temporal aggregate
opts = snakemake.wildcards.sector_opts.split("-")
solver_name = snakemake.config["solving"]["solver"]["name"]
n = set_temporal_aggregation_SEG(n, opts, solver_name)
# adjust global constraints lv limit if the same for all years
n = adjust_lvlimit(n)
# adjust global constraints CO2 limit
n = adjust_CO2_glc(n)
# adjust stores to multi period investment
n = adjust_stores(n)
# set phase outs
set_all_phase_outs(n)
# add H2 boiler
add_H2_boilers(n)
# set carbon constraints
opts = snakemake.wildcards.sector_opts.split("-")
n = set_carbon_constraints(n, opts)
# export network
n.export_to_netcdf(snakemake.output[0])

View File

@ -95,12 +95,14 @@ def define_spatial(nodes, options):
spatial.gas.industry = nodes + " gas for industry"
spatial.gas.industry_cc = nodes + " gas for industry CC"
spatial.gas.biogas_to_gas = nodes + " biogas to gas"
spatial.gas.biogas_to_gas_cc = nodes + "biogas to gas CC"
else:
spatial.gas.nodes = ["EU gas"]
spatial.gas.locations = ["EU"]
spatial.gas.biogas = ["EU biogas"]
spatial.gas.industry = ["gas for industry"]
spatial.gas.biogas_to_gas = ["EU biogas to gas"]
spatial.gas.biogas_to_gas_cc = ["EU biogas to gas CC"]
if options.get("co2_spatial", options["co2network"]):
spatial.gas.industry_cc = nodes + " gas for industry CC"
else:
@ -184,10 +186,7 @@ def get(item, investment_year=None):
"""
Check whether item depends on investment year.
"""
if isinstance(item, dict):
return item[investment_year]
else:
return item
return item[investment_year] if isinstance(item, dict) else item
def co2_emissions_year(
@ -220,7 +219,7 @@ def co2_emissions_year(
# TODO: move to own rule with sector-opts wildcard?
def build_carbon_budget(o, input_eurostat, fn, emissions_scope, report_year, input_co2):
def build_carbon_budget(o, input_eurostat, fn, emissions_scope, report_year):
"""
Distribute carbon budget following beta or exponential transition path.
"""
@ -413,11 +412,9 @@ def update_wind_solar_costs(n, costs):
# e.g. clusters == 37m means that VRE generators are left
# at clustering of simplified network, but that they are
# connected to 37-node network
if snakemake.wildcards.clusters[-1:] == "m":
genmap = busmap_s
else:
genmap = clustermaps
genmap = (
busmap_s if snakemake.wildcards.clusters[-1:] == "m" else clustermaps
)
connection_cost = (connection_cost * weight).groupby(
genmap
).sum() / weight.groupby(genmap).sum()
@ -457,10 +454,11 @@ def add_carrier_buses(n, carrier, nodes=None):
n.add("Carrier", carrier)
unit = "MWh_LHV" if carrier == "gas" else "MWh_th"
# preliminary value for non-gas carriers to avoid zeros
capital_cost = costs.at["gas storage", "fixed"] if carrier == "gas" else 0.02
n.madd("Bus", nodes, location=location, carrier=carrier, unit=unit)
# capital cost could be corrected to e.g. 0.2 EUR/kWh * annuity and O&M
n.madd(
"Store",
nodes + " Store",
@ -468,8 +466,7 @@ def add_carrier_buses(n, carrier, nodes=None):
e_nom_extendable=True,
e_cyclic=True,
carrier=carrier,
capital_cost=0.2
* costs.at[carrier, "discount rate"], # preliminary value to avoid zeros
capital_cost=capital_cost,
)
n.madd(
@ -505,8 +502,7 @@ def remove_non_electric_buses(n):
"""
Remove buses from pypsa-eur with carriers which are not AC buses.
"""
to_drop = list(n.buses.query("carrier not in ['AC', 'DC']").carrier.unique())
if to_drop:
if to_drop := list(n.buses.query("carrier not in ['AC', 'DC']").carrier.unique()):
logger.info(f"Drop buses from PyPSA-Eur with carrier: {to_drop}")
n.buses = n.buses[n.buses.carrier.isin(["AC", "DC"])]
@ -577,6 +573,7 @@ def add_co2_tracking(n, options):
capital_cost=options["co2_sequestration_cost"],
carrier="co2 stored",
bus=spatial.co2.nodes,
lifetime=options["co2_sequestration_lifetime"],
)
n.add("Carrier", "co2 stored")
@ -810,14 +807,13 @@ def add_ammonia(n, costs):
bus2=nodes + " H2",
p_nom_extendable=True,
carrier="Haber-Bosch",
efficiency=1
/ (
cf_industry["MWh_elec_per_tNH3_electrolysis"]
/ cf_industry["MWh_NH3_per_tNH3"]
), # output: MW_NH3 per MW_elec
efficiency2=-cf_industry["MWh_H2_per_tNH3_electrolysis"]
/ cf_industry["MWh_elec_per_tNH3_electrolysis"], # input: MW_H2 per MW_elec
capital_cost=costs.at["Haber-Bosch", "fixed"],
efficiency=1 / costs.at["Haber-Bosch", "electricity-input"],
efficiency2=-costs.at["Haber-Bosch", "hydrogen-input"]
/ costs.at["Haber-Bosch", "electricity-input"],
capital_cost=costs.at["Haber-Bosch", "fixed"]
/ costs.at["Haber-Bosch", "electricity-input"],
marginal_cost=costs.at["Haber-Bosch", "VOM"]
/ costs.at["Haber-Bosch", "electricity-input"],
lifetime=costs.at["Haber-Bosch", "lifetime"],
)
@ -1028,7 +1024,7 @@ def insert_gas_distribution_costs(n, costs):
f"Inserting gas distribution grid with investment cost factor of {f_costs}"
)
capital_cost = costs.loc["electricity distribution grid"]["fixed"] * f_costs
capital_cost = costs.at["electricity distribution grid", "fixed"] * f_costs
# gas boilers
gas_b = n.links.index[
@ -1105,6 +1101,7 @@ def add_storage_and_grids(n, costs):
efficiency=costs.at["OCGT", "efficiency"],
capital_cost=costs.at["OCGT", "fixed"]
* costs.at["OCGT", "efficiency"], # NB: fixed cost is per MWel
marginal_cost=costs.at["OCGT", "VOM"],
lifetime=costs.at["OCGT", "lifetime"],
)
@ -1165,7 +1162,7 @@ def add_storage_and_grids(n, costs):
if options["gas_network"]:
logger.info(
"Add natural gas infrastructure, incl. LNG terminals, production and entry-points."
"Add natural gas infrastructure, incl. LNG terminals, production, storage and entry-points."
)
if options["H2_retrofit"]:
@ -1210,10 +1207,25 @@ def add_storage_and_grids(n, costs):
remove_i = n.generators[gas_i & internal_i].index
n.generators.drop(remove_i, inplace=True)
p_nom = gas_input_nodes.sum(axis=1).rename(lambda x: x + " gas")
input_types = ["lng", "pipeline", "production"]
p_nom = gas_input_nodes[input_types].sum(axis=1).rename(lambda x: x + " gas")
n.generators.loc[gas_i, "p_nom_extendable"] = False
n.generators.loc[gas_i, "p_nom"] = p_nom
# add existing gas storage capacity
gas_i = n.stores.carrier == "gas"
e_nom = (
gas_input_nodes["storage"]
.rename(lambda x: x + " gas Store")
.reindex(n.stores.index)
.fillna(0.0)
* 1e3
) # MWh_LHV
e_nom.clip(
upper=e_nom.quantile(0.98), inplace=True
) # limit extremely large storage
n.stores.loc[gas_i, "e_nom_min"] = e_nom
# add candidates for new gas pipelines to achieve full connectivity
G = nx.Graph()
@ -1231,11 +1243,9 @@ def add_storage_and_grids(n, costs):
# apply k_edge_augmentation weighted by length of complement edges
k_edge = options.get("gas_network_connectivity_upgrade", 3)
augmentation = list(
if augmentation := list(
k_edge_augmentation(G, k_edge, avail=complement_edges.values)
)
if augmentation:
):
new_gas_pipes = pd.DataFrame(augmentation, columns=["bus0", "bus1"])
new_gas_pipes["length"] = new_gas_pipes.apply(haversine, axis=1)
@ -1350,6 +1360,7 @@ def add_storage_and_grids(n, costs):
bus2=spatial.co2.nodes,
p_nom_extendable=True,
carrier="Sabatier",
p_min_pu=options.get("min_part_load_methanation", 0),
efficiency=costs.at["methanation", "efficiency"],
efficiency2=-costs.at["methanation", "efficiency"]
* costs.at["gas", "CO2 intensity"],
@ -1399,7 +1410,7 @@ def add_storage_and_grids(n, costs):
lifetime=costs.at["coal", "lifetime"],
)
if options["SMR"]:
if options["SMR_cc"]:
n.madd(
"Link",
spatial.nodes,
@ -1417,6 +1428,7 @@ def add_storage_and_grids(n, costs):
lifetime=costs.at["SMR CC", "lifetime"],
)
if options["SMR"]:
n.madd(
"Link",
nodes + " SMR",
@ -1562,14 +1574,7 @@ def add_land_transport(n, costs):
)
if ice_share > 0:
if "oil" not in n.buses.carrier.unique():
n.madd(
"Bus",
spatial.oil.nodes,
location=spatial.oil.locations,
carrier="oil",
unit="MWh_LHV",
)
add_carrier_buses(n, "oil")
ice_efficiency = options["transport_internal_combustion_efficiency"]
@ -1643,7 +1648,7 @@ def build_heat_demand(n):
electric_nodes = n.loads.index[n.loads.carrier == "electricity"]
n.loads_t.p_set[electric_nodes] = (
n.loads_t.p_set[electric_nodes]
- electric_heat_supply.groupby(level=1, axis=1).sum()[electric_nodes]
- electric_heat_supply.T.groupby(level=1).sum().T[electric_nodes]
)
return heat_demand
@ -1706,6 +1711,19 @@ def add_heat(n, costs):
unit="MWh_th",
)
if name == "urban central" and options.get("central_heat_vent"):
n.madd(
"Generator",
nodes[name] + f" {name} heat vent",
bus=nodes[name] + f" {name} heat",
location=nodes[name],
carrier=name + " heat vent",
p_nom_extendable=True,
p_max_pu=0,
p_min_pu=-1,
unit="MWh_th",
)
## Add heat load
for sector in sectors:
@ -1724,15 +1742,17 @@ def add_heat(n, costs):
if sector in name:
heat_load = (
heat_demand[[sector + " water", sector + " space"]]
.groupby(level=1, axis=1)
.sum()[nodes[name]]
.T.groupby(level=1)
.sum()
.T[nodes[name]]
.multiply(factor)
)
if name == "urban central":
heat_load = (
heat_demand.groupby(level=1, axis=1)
.sum()[nodes[name]]
heat_demand.T.groupby(level=1)
.sum()
.T[nodes[name]]
.multiply(
factor * (1 + options["district_heating"]["district_heating_loss"])
)
@ -1960,7 +1980,7 @@ def add_heat(n, costs):
# demand 'dE' [per unit of original heat demand] for each country and
# different retrofitting strengths [additional insulation thickness in m]
retro_data = pd.read_csv(
snakemake.input.retro_cost_energy,
snakemake.input.retro_cost,
index_col=[0, 1],
skipinitialspace=True,
header=[0, 1],
@ -2004,7 +2024,11 @@ def add_heat(n, costs):
space_heat_demand = demand * w_space[sec][node]
# normed time profile of space heat demand 'space_pu' (values between 0-1),
# p_max_pu/p_min_pu of retrofitting generators
space_pu = (space_heat_demand / space_heat_demand.max()).to_frame(name=node)
space_pu = (
(space_heat_demand / space_heat_demand.max())
.to_frame(name=node)
.fillna(0)
)
# minimum heat demand 'dE' after retrofitting in units of original heat demand (values between 0-1)
dE = retro_data.loc[(ct, sec), ("dE")]
@ -2016,6 +2040,9 @@ def add_heat(n, costs):
* floor_area_node
/ ((1 - dE) * space_heat_demand.max())
)
if space_heat_demand.max() == 0:
capital_cost = capital_cost.apply(lambda b: 0 if b == np.inf else b)
# number of possible retrofitting measures 'strengths' (set in list at config.yaml 'l_strength')
# given in additional insulation thickness [m]
# for each measure, a retrofitting generator is added at the node
@ -2159,12 +2186,42 @@ def add_biomass(n, costs):
bus1=spatial.gas.nodes,
bus2="co2 atmosphere",
carrier="biogas to gas",
capital_cost=costs.loc["biogas upgrading", "fixed"],
marginal_cost=costs.loc["biogas upgrading", "VOM"],
capital_cost=costs.at["biogas", "fixed"]
+ costs.at["biogas upgrading", "fixed"],
marginal_cost=costs.at["biogas upgrading", "VOM"],
efficiency=costs.at["biogas", "efficiency"],
efficiency2=-costs.at["gas", "CO2 intensity"],
p_nom_extendable=True,
)
if options.get("biogas_upgrading_cc"):
# Assuming for costs that the CO2 from upgrading is pure, such as in amine scrubbing. I.e., with and without CC is
# equivalent. Adding biomass CHP capture because biogas is often small-scale and decentral so further
# from e.g. CO2 grid or buyers. This is a proxy for the added cost for e.g. a raw biogas pipeline to a central upgrading facility
n.madd(
"Link",
spatial.gas.biogas_to_gas_cc,
bus0=spatial.gas.biogas,
bus1=spatial.gas.nodes,
bus2="co2 stored",
bus3="co2 atmosphere",
carrier="biogas to gas CC",
capital_cost=costs.at["biogas CC", "fixed"]
+ costs.at["biogas upgrading", "fixed"]
+ costs.at["biomass CHP capture", "fixed"]
* costs.at["biogas CC", "CO2 stored"],
marginal_cost=costs.at["biogas CC", "VOM"]
+ costs.at["biogas upgrading", "VOM"],
efficiency=costs.at["biogas CC", "efficiency"],
efficiency2=costs.at["biogas CC", "CO2 stored"]
* costs.at["biogas CC", "capture rate"],
efficiency3=-costs.at["gas", "CO2 intensity"]
- costs.at["biogas CC", "CO2 stored"]
* costs.at["biogas CC", "capture rate"],
p_nom_extendable=True,
)
if options["biomass_transport"]:
# add biomass transport
transport_costs = pd.read_csv(
@ -2290,6 +2347,7 @@ def add_biomass(n, costs):
efficiency=costs.at["biomass boiler", "efficiency"],
capital_cost=costs.at["biomass boiler", "efficiency"]
* costs.at["biomass boiler", "fixed"],
marginal_cost=costs.at["biomass boiler", "pelletizing cost"],
lifetime=costs.at["biomass boiler", "lifetime"],
)
@ -2309,7 +2367,7 @@ def add_biomass(n, costs):
+ costs.at["BtL", "CO2 stored"],
p_nom_extendable=True,
capital_cost=costs.at["BtL", "fixed"],
marginal_cost=costs.at["BtL", "efficiency"] * costs.loc["BtL", "VOM"],
marginal_cost=costs.at["BtL", "efficiency"] * costs.at["BtL", "VOM"],
)
# TODO: Update with energy penalty
@ -2330,7 +2388,7 @@ def add_biomass(n, costs):
p_nom_extendable=True,
capital_cost=costs.at["BtL", "fixed"]
+ costs.at["biomass CHP capture", "fixed"] * costs.at["BtL", "CO2 stored"],
marginal_cost=costs.at["BtL", "efficiency"] * costs.loc["BtL", "VOM"],
marginal_cost=costs.at["BtL", "efficiency"] * costs.at["BtL", "VOM"],
)
# BioSNG from solid biomass
@ -2349,7 +2407,7 @@ def add_biomass(n, costs):
+ costs.at["BioSNG", "CO2 stored"],
p_nom_extendable=True,
capital_cost=costs.at["BioSNG", "fixed"],
marginal_cost=costs.at["BioSNG", "efficiency"] * costs.loc["BioSNG", "VOM"],
marginal_cost=costs.at["BioSNG", "efficiency"] * costs.at["BioSNG", "VOM"],
)
# TODO: Update with energy penalty for CC
@ -2373,7 +2431,7 @@ def add_biomass(n, costs):
capital_cost=costs.at["BioSNG", "fixed"]
+ costs.at["biomass CHP capture", "fixed"]
* costs.at["BioSNG", "CO2 stored"],
marginal_cost=costs.at["BioSNG", "efficiency"] * costs.loc["BioSNG", "VOM"],
marginal_cost=costs.at["BioSNG", "efficiency"] * costs.at["BioSNG", "VOM"],
)
@ -2606,6 +2664,8 @@ def add_industry(n, costs):
p_min_pu=options.get("min_part_load_methanolisation", 0),
capital_cost=costs.at["methanolisation", "fixed"]
* options["MWh_MeOH_per_MWh_H2"], # EUR/MW_H2/a
marginal_cost=options["MWh_MeOH_per_MWh_H2"]
* costs.at["methanolisation", "VOM"],
lifetime=costs.at["methanolisation", "lifetime"],
efficiency=options["MWh_MeOH_per_MWh_H2"],
efficiency2=-options["MWh_MeOH_per_MWh_H2"] / options["MWh_MeOH_per_MWh_e"],
@ -2723,6 +2783,8 @@ def add_industry(n, costs):
efficiency=costs.at["Fischer-Tropsch", "efficiency"],
capital_cost=costs.at["Fischer-Tropsch", "fixed"]
* costs.at["Fischer-Tropsch", "efficiency"], # EUR/MW_H2/a
marginal_cost=costs.at["Fischer-Tropsch", "efficiency"]
* costs.at["Fischer-Tropsch", "VOM"],
efficiency2=-costs.at["oil", "CO2 intensity"]
* costs.at["Fischer-Tropsch", "efficiency"],
p_nom_extendable=True,
@ -2893,6 +2955,30 @@ def add_industry(n, costs):
p_set=p_set,
)
primary_steel = get(
snakemake.config["industry"]["St_primary_fraction"], investment_year
)
dri_steel = get(snakemake.config["industry"]["DRI_fraction"], investment_year)
bof_steel = primary_steel - dri_steel
if bof_steel > 0:
add_carrier_buses(n, "coal")
mwh_coal_per_mwh_coke = 1.366 # from eurostat energy balance
p_set = (
industrial_demand["coal"].sum()
+ mwh_coal_per_mwh_coke * industrial_demand["coke"].sum()
) / nhours
n.madd(
"Load",
spatial.coal.nodes,
suffix=" for industry",
bus=spatial.coal.nodes,
carrier="coal for industry",
p_set=p_set,
)
def add_waste_heat(n):
# TODO options?
@ -2904,8 +2990,13 @@ def add_waste_heat(n):
if not urban_central.empty:
urban_central = urban_central.str[: -len(" urban central heat")]
link_carriers = n.links.carrier.unique()
# TODO what is the 0.95 and should it be a config option?
if options["use_fischer_tropsch_waste_heat"]:
if (
options["use_fischer_tropsch_waste_heat"]
and "Fischer-Tropsch" in link_carriers
):
n.links.loc[urban_central + " Fischer-Tropsch", "bus3"] = (
urban_central + " urban central heat"
)
@ -2913,8 +3004,48 @@ def add_waste_heat(n):
0.95 - n.links.loc[urban_central + " Fischer-Tropsch", "efficiency"]
)
if options["use_methanation_waste_heat"] and "Sabatier" in link_carriers:
n.links.loc[urban_central + " Sabatier", "bus3"] = (
urban_central + " urban central heat"
)
n.links.loc[urban_central + " Sabatier", "efficiency3"] = (
0.95 - n.links.loc[urban_central + " Sabatier", "efficiency"]
)
# DEA quotes 15% of total input (11% of which are high-value heat)
if options["use_haber_bosch_waste_heat"] and "Haber-Bosch" in link_carriers:
n.links.loc[urban_central + " Haber-Bosch", "bus3"] = (
urban_central + " urban central heat"
)
total_energy_input = (
cf_industry["MWh_H2_per_tNH3_electrolysis"]
+ cf_industry["MWh_elec_per_tNH3_electrolysis"]
) / cf_industry["MWh_NH3_per_tNH3"]
electricity_input = (
cf_industry["MWh_elec_per_tNH3_electrolysis"]
/ cf_industry["MWh_NH3_per_tNH3"]
)
n.links.loc[urban_central + " Haber-Bosch", "efficiency3"] = (
0.15 * total_energy_input / electricity_input
)
if (
options["use_methanolisation_waste_heat"]
and "methanolisation" in link_carriers
):
n.links.loc[urban_central + " methanolisation", "bus4"] = (
urban_central + " urban central heat"
)
n.links.loc[urban_central + " methanolisation", "efficiency4"] = (
costs.at["methanolisation", "heat-output"]
/ costs.at["methanolisation", "hydrogen-input"]
)
# TODO integrate usable waste heat efficiency into technology-data from DEA
if options.get("use_electrolysis_waste_heat", False):
if (
options.get("use_electrolysis_waste_heat", False)
and "H2 Electrolysis" in link_carriers
):
n.links.loc[urban_central + " H2 Electrolysis", "bus2"] = (
urban_central + " urban central heat"
)
@ -2922,7 +3053,7 @@ def add_waste_heat(n):
0.84 - n.links.loc[urban_central + " H2 Electrolysis", "efficiency"]
)
if options["use_fuel_cell_waste_heat"]:
if options["use_fuel_cell_waste_heat"] and "H2 Fuel Cell" in link_carriers:
n.links.loc[urban_central + " H2 Fuel Cell", "bus2"] = (
urban_central + " urban central heat"
)
@ -3325,7 +3456,7 @@ if __name__ == "__main__":
spatial = define_spatial(pop_layout.index, options)
if snakemake.params.foresight == "myopic":
if snakemake.params.foresight in ["myopic", "perfect"]:
add_lifetime_wind_solar(n, costs)
conventional = snakemake.params.conventional_carriers
@ -3357,6 +3488,15 @@ if __name__ == "__main__":
if "nodistrict" in opts:
options["district_heating"]["progress"] = 0.0
if "nowasteheat" in opts:
logger.info("Disabling waste heat.")
options["use_fischer_tropsch_waste_heat"] = False
options["use_methanolisation_waste_heat"] = False
options["use_haber_bosch_waste_heat"] = False
options["use_methanation_waste_heat"] = False
options["use_fuel_cell_waste_heat"] = False
options["use_electrolysis_waste_heat"] = False
if "T" in opts:
add_land_transport(n, costs)
@ -3402,7 +3542,7 @@ if __name__ == "__main__":
if "cb" not in o:
continue
limit_type = "carbon budget"
fn = "results/" + snakemake.params.RDIR + "csvs/carbon_budget_distribution.csv"
fn = "results/" + snakemake.params.RDIR + "/csvs/carbon_budget_distribution.csv"
if not os.path.exists(fn):
emissions_scope = snakemake.params.emissions_scope
report_year = snakemake.params.eurostat_report_year
@ -3446,7 +3586,7 @@ if __name__ == "__main__":
if options["electricity_grid_connection"]:
add_electricity_grid_connection(n, costs)
first_year_myopic = (snakemake.params.foresight == "myopic") and (
first_year_myopic = (snakemake.params.foresight in ["myopic", "perfect"]) and (
snakemake.params.planning_horizons[0] == investment_year
)

View File

@ -36,7 +36,7 @@ import logging
import tarfile
from pathlib import Path
from _helpers import configure_logging, progress_retrieve
from _helpers import configure_logging, progress_retrieve, validate_checksum
logger = logging.getLogger(__name__)
@ -65,6 +65,8 @@ if __name__ == "__main__":
disable_progress = snakemake.config["run"].get("disable_progressbar", False)
progress_retrieve(url, tarball_fn, disable=disable_progress)
validate_checksum(tarball_fn, url)
logger.info("Extracting databundle.")
tarfile.open(tarball_fn).extractall(to_fn)

View File

@ -11,7 +11,7 @@ import logging
import zipfile
from pathlib import Path
from _helpers import progress_retrieve
from _helpers import progress_retrieve, validate_checksum
logger = logging.getLogger(__name__)
@ -35,6 +35,8 @@ if __name__ == "__main__":
disable_progress = snakemake.config["run"].get("disable_progressbar", False)
progress_retrieve(url, zip_fn, disable=disable_progress)
validate_checksum(zip_fn, url)
logger.info("Extracting databundle.")
zipfile.ZipFile(zip_fn).extractall(to_fn)

107
scripts/retrieve_irena.py Normal file
View File

@ -0,0 +1,107 @@
# -*- coding: utf-8 -*-
# Copyright 2023 Thomas Gilon (Climact)
# SPDX-FileCopyrightText: : 2017-2023 The PyPSA-Eur Authors
#
# SPDX-License-Identifier: MIT
"""
This rule downloads the existing capacities from `IRENASTAT <https://www.irena.org/Data/Downloads/IRENASTAT>`_ and extracts it in the ``data/existing_capacities`` sub-directory.
**Relevant Settings**
.. code:: yaml
enable:
retrieve_irena:
.. seealso::
Documentation of the configuration file ``config.yaml`` at
:ref:`enable_cf`
**Outputs**
- ``data/existing_capacities``: existing capacities for offwind, onwind and solar
"""
import logging
import pandas as pd
from _helpers import configure_logging
logger = logging.getLogger(__name__)
REGIONS = [
"Albania",
"Austria",
"Belgium",
"Bosnia and Herzegovina",
"Bulgaria",
"Croatia",
"Czechia",
"Denmark",
"Estonia",
"Finland",
"France",
"Germany",
"Greece",
"Hungary",
"Ireland",
"Italy",
"Latvia",
"Lithuania",
"Luxembourg",
"Montenegro",
# "Netherlands",
"Netherlands (Kingdom of the)",
"North Macedonia",
"Norway",
"Poland",
"Portugal",
"Romania",
"Serbia",
"Slovakia",
"Slovenia",
"Spain",
"Sweden",
"Switzerland",
# "United Kingdom",
"United Kingdom of Great Britain and Northern Ireland (the)",
]
REGIONS_DICT = {
"Bosnia and Herzegovina": "Bosnia Herzg",
"Netherlands (Kingdom of the)": "Netherlands",
"United Kingdom of Great Britain and Northern Ireland (the)": "UK",
}
if __name__ == "__main__":
if "snakemake" not in globals():
from _helpers import mock_snakemake
snakemake = mock_snakemake("retrieve_irena")
configure_logging(snakemake)
irena_raw = pd.read_csv(
"https://pxweb.irena.org:443/sq/99e64b12-fe03-4a7b-92ea-a22cc3713b92",
skiprows=2,
index_col=[0, 1, 3],
encoding="latin-1",
)
var = "Installed electricity capacity (MW)"
irena = irena_raw[var].unstack(level=2).reset_index(level=1).replace(0, "")
irena = irena[irena.index.isin(REGIONS)]
irena.rename(index=REGIONS_DICT, inplace=True)
df_offwind = irena[irena.Technology.str.contains("Offshore")].drop(
columns=["Technology"]
)
df_onwind = irena[irena.Technology.str.contains("Onshore")].drop(
columns=["Technology"]
)
df_pv = irena[irena.Technology.str.contains("Solar")].drop(columns=["Technology"])
df_offwind.to_csv(snakemake.output["offwind"])
df_onwind.to_csv(snakemake.output["onwind"])
df_pv.to_csv(snakemake.output["solar"])

View File

@ -13,7 +13,7 @@ logger = logging.getLogger(__name__)
import tarfile
from pathlib import Path
from _helpers import configure_logging, progress_retrieve
from _helpers import configure_logging, progress_retrieve, validate_checksum
if __name__ == "__main__":
if "snakemake" not in globals():
@ -34,6 +34,8 @@ if __name__ == "__main__":
disable_progress = snakemake.config["run"].get("disable_progressbar", False)
progress_retrieve(url, tarball_fn, disable=disable_progress)
validate_checksum(tarball_fn, url)
logger.info("Extracting databundle.")
tarfile.open(tarball_fn).extractall(to_fn)

View File

@ -152,11 +152,8 @@ def _prepare_connection_costs_per_link(n, costs, renewable_carriers, length_fact
if n.links.empty:
return {}
connection_costs_per_link = {}
for tech in renewable_carriers:
if tech.startswith("offwind"):
connection_costs_per_link[tech] = (
return {
tech: (
n.links.length
* length_factor
* (
@ -166,8 +163,9 @@ def _prepare_connection_costs_per_link(n, costs, renewable_carriers, length_fact
* costs.at[tech + "-connection-underground", "capital_cost"]
)
)
return connection_costs_per_link
for tech in renewable_carriers
if tech.startswith("offwind")
}
def _compute_connection_costs_to_bus(
@ -538,6 +536,9 @@ if __name__ == "__main__":
n = pypsa.Network(snakemake.input.network)
Nyears = n.snapshot_weightings.objective.sum() / 8760
# remove integer outputs for compatibility with PyPSA v0.26.0
n.generators.drop("n_mod", axis=1, inplace=True, errors="ignore")
n, trafo_map = simplify_network_to_380(n)
technology_costs = load_costs(

View File

@ -33,7 +33,9 @@ import numpy as np
import pandas as pd
import pypsa
import xarray as xr
from _benchmark import memory_logger
from _helpers import configure_logging, get_opt, update_config_with_sector_opts
from pypsa.descriptors import get_activity_mask
logger = logging.getLogger(__name__)
pypsa.pf.logger.setLevel(logging.WARNING)
@ -47,6 +49,69 @@ def add_land_use_constraint(n, planning_horizons, config):
_add_land_use_constraint(n)
def add_land_use_constraint_perfect(n):
"""
Add global constraints for tech capacity limit.
"""
logger.info("Add land-use constraint for perfect foresight")
def compress_series(s):
def process_group(group):
if group.nunique() == 1:
return pd.Series(group.iloc[0], index=[None])
else:
return group
return s.groupby(level=[0, 1]).apply(process_group)
def new_index_name(t):
# Convert all elements to string and filter out None values
parts = [str(x) for x in t if x is not None]
# Join with space, but use a dash for the last item if not None
return " ".join(parts[:2]) + (f"-{parts[-1]}" if len(parts) > 2 else "")
def check_p_min_p_max(p_nom_max):
p_nom_min = n.generators[ext_i].groupby(grouper).sum().p_nom_min
p_nom_min = p_nom_min.reindex(p_nom_max.index)
check = (
p_nom_min.groupby(level=[0, 1]).sum()
> p_nom_max.groupby(level=[0, 1]).min()
)
if check.sum():
logger.warning(
f"summed p_min_pu values at node larger than technical potential {check[check].index}"
)
grouper = [n.generators.carrier, n.generators.bus, n.generators.build_year]
ext_i = n.generators.p_nom_extendable
# get technical limit per node and investment period
p_nom_max = n.generators[ext_i].groupby(grouper).min().p_nom_max
# drop carriers without tech limit
p_nom_max = p_nom_max[~p_nom_max.isin([np.inf, np.nan])]
# carrier
carriers = p_nom_max.index.get_level_values(0).unique()
gen_i = n.generators[(n.generators.carrier.isin(carriers)) & (ext_i)].index
n.generators.loc[gen_i, "p_nom_min"] = 0
# check minimum capacities
check_p_min_p_max(p_nom_max)
# drop multi entries in case p_nom_max stays constant in different periods
# p_nom_max = compress_series(p_nom_max)
# adjust name to fit syntax of nominal constraint per bus
df = p_nom_max.reset_index()
df["name"] = df.apply(
lambda row: f"nom_max_{row['carrier']}"
+ (f"_{row['build_year']}" if row["build_year"] is not None else ""),
axis=1,
)
for name in df.name.unique():
df_carrier = df[df.name == name]
bus = df_carrier.bus
n.buses.loc[bus, name] = df_carrier.p_nom_max.values
return n
def _add_land_use_constraint(n):
# warning: this will miss existing offwind which is not classed AC-DC and has carrier 'offwind'
@ -82,19 +147,13 @@ def _add_land_use_constraint(n):
def _add_land_use_constraint_m(n, planning_horizons, config):
# if generators clustering is lower than network clustering, land_use accounting is at generators clusters
planning_horizons = param["planning_horizons"]
grouping_years = config["existing_capacities"]["grouping_years"]
current_horizon = snakemake.wildcards.planning_horizons
for carrier in ["solar", "onwind", "offwind-ac", "offwind-dc"]:
existing = n.generators.loc[n.generators.carrier == carrier, "p_nom"]
ind = list(
set(
[
i.split(sep=" ")[0] + " " + i.split(sep=" ")[1]
for i in existing.index
]
)
{i.split(sep=" ")[0] + " " + i.split(sep=" ")[1] for i in existing.index}
)
previous_years = [
@ -116,7 +175,7 @@ def _add_land_use_constraint_m(n, planning_horizons, config):
n.generators.p_nom_max.clip(lower=0, inplace=True)
def add_co2_sequestration_limit(n, limit=200):
def add_co2_sequestration_limit(n, config, limit=200):
"""
Add a global constraint on the amount of Mt CO2 that can be sequestered.
"""
@ -130,16 +189,146 @@ def add_co2_sequestration_limit(n, limit=200):
limit = float(o[o.find("seq") + 3 :]) * 1e6
break
n.add(
if not n.investment_periods.empty:
periods = n.investment_periods
names = pd.Index([f"co2_sequestration_limit-{period}" for period in periods])
else:
periods = [np.nan]
names = pd.Index(["co2_sequestration_limit"])
n.madd(
"GlobalConstraint",
"co2_sequestration_limit",
names,
sense="<=",
constant=limit,
type="primary_energy",
carrier_attribute="co2_absorptions",
investment_period=periods,
)
def add_carbon_constraint(n, snapshots):
glcs = n.global_constraints.query('type == "co2_limit"')
if glcs.empty:
return
for name, glc in glcs.iterrows():
carattr = glc.carrier_attribute
emissions = n.carriers.query(f"{carattr} != 0")[carattr]
if emissions.empty:
continue
# stores
n.stores["carrier"] = n.stores.bus.map(n.buses.carrier)
stores = n.stores.query("carrier in @emissions.index and not e_cyclic")
if not stores.empty:
last = n.snapshot_weightings.reset_index().groupby("period").last()
last_i = last.set_index([last.index, last.timestep]).index
final_e = n.model["Store-e"].loc[last_i, stores.index]
time_valid = int(glc.loc["investment_period"])
time_i = pd.IndexSlice[time_valid, :]
lhs = final_e.loc[time_i, :] - final_e.shift(snapshot=1).loc[time_i, :]
rhs = glc.constant
n.model.add_constraints(lhs <= rhs, name=f"GlobalConstraint-{name}")
def add_carbon_budget_constraint(n, snapshots):
glcs = n.global_constraints.query('type == "Co2Budget"')
if glcs.empty:
return
for name, glc in glcs.iterrows():
carattr = glc.carrier_attribute
emissions = n.carriers.query(f"{carattr} != 0")[carattr]
if emissions.empty:
continue
# stores
n.stores["carrier"] = n.stores.bus.map(n.buses.carrier)
stores = n.stores.query("carrier in @emissions.index and not e_cyclic")
if not stores.empty:
last = n.snapshot_weightings.reset_index().groupby("period").last()
last_i = last.set_index([last.index, last.timestep]).index
final_e = n.model["Store-e"].loc[last_i, stores.index]
time_valid = int(glc.loc["investment_period"])
time_i = pd.IndexSlice[time_valid, :]
weighting = n.investment_period_weightings.loc[time_valid, "years"]
lhs = final_e.loc[time_i, :] * weighting
rhs = glc.constant
n.model.add_constraints(lhs <= rhs, name=f"GlobalConstraint-{name}")
def add_max_growth(n, config):
"""
Add maximum growth rates for different carriers.
"""
opts = snakemake.params["sector"]["limit_max_growth"]
# take maximum yearly difference between investment periods since historic growth is per year
factor = n.investment_period_weightings.years.max() * opts["factor"]
for carrier in opts["max_growth"].keys():
max_per_period = opts["max_growth"][carrier] * factor
logger.info(
f"set maximum growth rate per investment period of {carrier} to {max_per_period} GW."
)
n.carriers.loc[carrier, "max_growth"] = max_per_period * 1e3
for carrier in opts["max_relative_growth"].keys():
max_r_per_period = opts["max_relative_growth"][carrier]
logger.info(
f"set maximum relative growth per investment period of {carrier} to {max_r_per_period}."
)
n.carriers.loc[carrier, "max_relative_growth"] = max_r_per_period
return n
def add_retrofit_gas_boiler_constraint(n, snapshots):
"""
Allow retrofitting of existing gas boilers to H2 boilers.
"""
c = "Link"
logger.info("Add constraint for retrofitting gas boilers to H2 boilers.")
# existing gas boilers
mask = n.links.carrier.str.contains("gas boiler") & ~n.links.p_nom_extendable
gas_i = n.links[mask].index
mask = n.links.carrier.str.contains("retrofitted H2 boiler")
h2_i = n.links[mask].index
n.links.loc[gas_i, "p_nom_extendable"] = True
p_nom = n.links.loc[gas_i, "p_nom"]
n.links.loc[gas_i, "p_nom"] = 0
# heat profile
cols = n.loads_t.p_set.columns[
n.loads_t.p_set.columns.str.contains("heat")
& ~n.loads_t.p_set.columns.str.contains("industry")
& ~n.loads_t.p_set.columns.str.contains("agriculture")
]
profile = n.loads_t.p_set[cols].div(
n.loads_t.p_set[cols].groupby(level=0).max(), level=0
)
# to deal if max value is zero
profile.fillna(0, inplace=True)
profile.rename(columns=n.loads.bus.to_dict(), inplace=True)
profile = profile.reindex(columns=n.links.loc[gas_i, "bus1"])
profile.columns = gas_i
rhs = profile.mul(p_nom)
dispatch = n.model["Link-p"]
active = get_activity_mask(n, c, snapshots, gas_i)
rhs = rhs[active]
p_gas = dispatch.sel(Link=gas_i)
p_h2 = dispatch.sel(Link=h2_i)
lhs = p_gas + p_h2
n.model.add_constraints(lhs == rhs, name="gas_retrofit")
def prepare_network(
n,
solve_opts=None,
@ -156,13 +345,12 @@ def prepare_network(
):
df.where(df > solve_opts["clip_p_max_pu"], other=0.0, inplace=True)
load_shedding = solve_opts.get("load_shedding")
if load_shedding:
if load_shedding := solve_opts.get("load_shedding"):
# intersect between macroeconomic and surveybased willingness to pay
# http://journal.frontiersin.org/article/10.3389/fenrg.2015.00055/full
# TODO: retrieve color and nice name from config
n.add("Carrier", "load", color="#dd2e23", nice_name="Load shedding")
buses_i = n.buses.query("carrier == 'AC'").index
buses_i = n.buses.index
if not np.isscalar(load_shedding):
# TODO: do not scale via sign attribute (use Eur/MWh instead of Eur/kWh)
load_shedding = 1e2 # Eur/kWh
@ -200,9 +388,14 @@ def prepare_network(
if foresight == "myopic":
add_land_use_constraint(n, planning_horizons, config)
if foresight == "perfect":
n = add_land_use_constraint_perfect(n)
if snakemake.params["sector"]["limit_max_growth"]["enable"]:
n = add_max_growth(n, config)
if n.stores.carrier.eq("co2 stored").any():
limit = co2_sequestration_potential
add_co2_sequestration_limit(n, limit=limit)
add_co2_sequestration_limit(n, config, limit=limit)
return n
@ -606,12 +799,17 @@ def extra_functionality(n, snapshots):
add_battery_constraints(n)
add_pipe_retrofit_constraint(n)
if n._multi_invest:
add_carbon_constraint(n, snapshots)
add_carbon_budget_constraint(n, snapshots)
add_retrofit_gas_boiler_constraint(n, snapshots)
def solve_network(n, config, solving, opts="", **kwargs):
set_of_options = solving["solver"]["options"]
cf_solving = solving["options"]
kwargs["multi_investment_periods"] = config["foresight"] == "perfect"
kwargs["solver_options"] = (
solving["solver_options"][set_of_options] if set_of_options else {}
)
@ -663,13 +861,14 @@ if __name__ == "__main__":
from _helpers import mock_snakemake
snakemake = mock_snakemake(
"solve_network",
"solve_sector_network_perfect",
configfiles="../config/test/config.perfect.yaml",
simpl="",
opts="Ept",
clusters="37",
ll="v1.0",
sector_opts="",
planning_horizons="2020",
opts="",
clusters="5",
ll="v1.5",
sector_opts="8760H-T-H-B-I-A-solar+p3-dist1",
planning_horizons="2030",
)
configure_logging(snakemake)
if "sector_opts" in snakemake.wildcards.keys():
@ -696,6 +895,9 @@ if __name__ == "__main__":
co2_sequestration_potential=snakemake.params["co2_sequestration_potential"],
)
with memory_logger(
filename=getattr(snakemake.log, "memory", None), interval=30.0
) as mem:
n = solve_network(
n,
config=snakemake.config,
@ -704,5 +906,7 @@ if __name__ == "__main__":
log_fn=snakemake.log.solver,
)
logger.info(f"Maximum memory usage: {mem.mem_usage}")
n.meta = dict(snakemake.config, **dict(wildcards=dict(snakemake.wildcards)))
n.export_to_netcdf(snakemake.output[0])

View File

@ -7,6 +7,7 @@ Solves linear optimal dispatch in hourly resolution using the capacities of
previous capacity expansion in rule :mod:`solve_network`.
"""
import logging
import numpy as np
@ -35,7 +36,7 @@ if __name__ == "__main__":
configure_logging(snakemake)
update_config_with_sector_opts(snakemake.config, snakemake.wildcards.sector_opts)
opts = (snakemake.wildcards.opts + "-" + snakemake.wildcards.sector_opts).split("-")
opts = f"{snakemake.wildcards.opts}-{snakemake.wildcards.sector_opts}".split("-")
opts = [o for o in opts if o != ""]
solve_opts = snakemake.params.options