diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 58a592b18..95f105b86 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -63,7 +63,7 @@ jobs: token: ${{ steps.generate-token.outputs.token }} - name: Setup Pixi - uses: prefix-dev/setup-pixi@v0.9.3 + uses: prefix-dev/setup-pixi@v0.9.4 with: pixi-version: v0.59.0 cache: true diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 32cd6363c..b557a3d69 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -1,4 +1,4 @@ -name: Test workflows +name: Test on: push: @@ -17,8 +17,30 @@ concurrency: cancel-in-progress: true jobs: - run-tests: - name: OS + unit-tests: + name: Unit + runs-on: ubuntu-latest + + defaults: + run: + shell: bash -l {0} + + steps: + - uses: actions/checkout@v6 + + - name: Setup Pixi + uses: prefix-dev/setup-pixi@v0.9.4 + with: + pixi-version: v0.59.0 + cache: true + cache-write: ${{ github.event_name == 'push' && github.ref_name == 'master' }} + + - name: Run unit tests + run: | + pixi run unit-tests + + integration-tests: + name: Integration runs-on: ${{ matrix.os }}-latest strategy: fail-fast: false @@ -43,7 +65,6 @@ jobs: - 'data/**' - 'Snakefile' - 'config/**' - - 'test/**' - 'pixi.toml' - 'pixi.lock' - '.github/workflows/test.yaml' @@ -58,31 +79,41 @@ jobs: sudo docker builder prune -a --force echo "Final disk space" df -h + - name: Skip - no source changes if: steps.filter.outputs.src != 'true' && github.event_name != 'schedule' run: echo "Skipping tests because no source code changes detected" - name: Setup Pixi if: steps.filter.outputs.src == 'true' || github.event_name == 'schedule' - uses: prefix-dev/setup-pixi@v0.9.3 + uses: prefix-dev/setup-pixi@v0.9.4 with: pixi-version: v0.59.0 cache: true - # Do not cache in branches - cache-write: ${{ github.event_name == 'push' && github.ref_name == 'main' }} + cache-write: ${{ github.event_name == 'push' && github.ref_name == 'master' }} - name: Setup cache keys if: steps.filter.outputs.src == 'true' || github.event_name == 'schedule' run: | - echo "WEEK=$(date +'%Y%U')" >> $GITHUB_ENV # data and cutouts + echo "WEEK=$(date +'%Y%U')" >> $GITHUB_ENV # data + echo "MONTH=$(date +'%Y%m')" >> $GITHUB_ENV # cutouts + echo "VERSIONS_HASH=${{ hashFiles('data/versions.csv') }}" >> $GITHUB_ENV - uses: actions/cache@v5 if: steps.filter.outputs.src == 'true' || github.event_name == 'schedule' with: - path: | - data - cutouts - key: data-cutouts-${{ env.WEEK }} + path: data + key: data-${{ env.WEEK }}-${{ env.VERSIONS_HASH }} + + - uses: actions/cache@v5 + if: steps.filter.outputs.src == 'true' || github.event_name == 'schedule' + with: + path: cutouts + key: cutouts-${{ env.MONTH }} + + - name: Restore git-tracked files in data/ + if: steps.filter.outputs.src == 'true' || github.event_name == 'schedule' + run: git checkout HEAD -- data/ - name: Run pylint check on scripts if: steps.filter.outputs.src == 'true' || github.event_name == 'schedule' @@ -98,11 +129,6 @@ jobs: run: | pixi run integration-tests - - name: Run unit tests - if: steps.filter.outputs.src == 'true' || github.event_name == 'schedule' - run: | - pixi run unit-tests - - name: Upload artifacts if: always() uses: actions/upload-artifact@v6 diff --git a/.github/workflows/update-lockfile.yaml b/.github/workflows/update-lockfile.yaml index 31d264773..56342220b 100644 --- a/.github/workflows/update-lockfile.yaml +++ b/.github/workflows/update-lockfile.yaml @@ -19,7 +19,7 @@ jobs: - uses: actions/checkout@v6 - name: Setup Pixi - uses: prefix-dev/setup-pixi@v0.9.3 + uses: prefix-dev/setup-pixi@v0.9.4 with: pixi-version: v0.59.0 diff --git a/.gitignore b/.gitignore index fbbf13ac2..731b37d56 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ gurobi.log /cutouts /tmp doc/_build +_build /scripts/old /scripts/create_scenarios.py @@ -65,6 +66,8 @@ d1gam3xoknrgr2.cloudfront.net/ *~ +*.egg-info + *.pyc *.xlsx diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b0bc9781f..5e3e5f35e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,8 +1,7 @@ # SPDX-FileCopyrightText: Contributors to PyPSA-Eur # # SPDX-License-Identifier: CC0-1.0 - -exclude: "^LICENSES" +exclude: "^LICENSES|^config/schema\\.json$|^config/config\\.default\\.yaml$" ci: autoupdate_schedule: quarterly diff --git a/Snakefile b/Snakefile index 68eb57bbd..26fbe41ea 100644 --- a/Snakefile +++ b/Snakefile @@ -7,8 +7,11 @@ import yaml import sys from os.path import normpath, exists, join from shutil import copyfile, move, rmtree, unpack_archive +from dotenv import load_dotenv from snakemake.utils import min_version +load_dotenv() + min_version("8.11") from scripts._helpers import ( @@ -17,6 +20,7 @@ from scripts._helpers import ( get_shadow, path_provider, ) +from scripts.lib.validation.config import validate_config configfile: "config/config.default.yaml" @@ -24,6 +28,8 @@ configfile: "config/plotting.default.yaml" configfile: "config/config.de.yaml" +validate_config(config) + run = config["run"] scenarios = get_scenarios(run) RDIR = get_rdir(run) diff --git a/config/config.de.yaml b/config/config.de.yaml index f06ad6e69..14c2af903 100644 --- a/config/config.de.yaml +++ b/config/config.de.yaml @@ -417,6 +417,7 @@ solving: runtime: 12h mem_mb: 70000 #30000 is OK for 22 nodes, 365H; 140000 for 22 nodes 3H; 400000 for 44 nodes 3H options: + custom_extra_functionality: "../scripts/pypsa-de/additional_functionality.py" assign_all_duals: true load_shedding: false skip_iterations: true # settings for post-discretization: false diff --git a/config/config.default.yaml b/config/config.default.yaml index 9ae9f3ece..8dec48b30 100644 --- a/config/config.default.yaml +++ b/config/config.default.yaml @@ -1,15 +1,19 @@ -# SPDX-FileCopyrightText: Contributors to PyPSA-Eur -# -# SPDX-License-Identifier: CC0-1.0 +%YAML 1.1 +--- +# yaml-language-server: $schema=./schema.json -# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#top-level-configuration +# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#version version: v2025.07.0 + +# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#tutorial tutorial: false +# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#logging logging: level: INFO format: "%(levelname)s:%(name)s:%(message)s" +# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#remote remote: ssh: "" path: "" @@ -31,14 +35,13 @@ run: foresight: overnight # 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: clusters: - 50 opts: - - '' + - "" sector_opts: - - '' + - "" planning_horizons: - 2050 @@ -83,103 +86,124 @@ countries: snapshots: start: "2013-01-01" end: "2014-01-01" - inclusive: 'left' + inclusive: left # docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#enable enable: drop_leap_day: true -# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#co2-budget +# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#co2_budget co2_budget: - 2020: 0.720 # average emissions of 2019 to 2021 relative to 1990, CO2 excl LULUCF, EEA data, European Environment Agency. (2023a). Annual European Union greenhouse gas inventory 1990–2021 and inventory report 2023 - CRF Table. https://unfccc.int/documents/627830 - 2025: 0.648 # With additional measures (WAM) projection, CO2 excl LULUCF, European Environment Agency. (2023e). Member States’ greenhouse gas (GHG) emission projections 2023. https://www.eea.europa.eu/en/datahub/datahubitem-view/4b8d94a4-aed7-4e67-a54c-0623a50f48e8 - 2030: 0.450 # 55% reduction by 2030 (Ff55) - 2035: 0.250 - 2040: 0.100 # 90% by 2040 - 2045: 0.050 - 2050: 0.000 # climate-neutral by 2050 + 2020: 0.72 + 2025: 0.648 + 2030: 0.45 + 2035: 0.25 + 2040: 0.1 + 2045: 0.05 + 2050: 0.0 # docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#electricity electricity: - voltages: [220., 300., 330., 380., 400., 500., 750.] + voltages: + - 220.0 + - 300.0 + - 330.0 + - 380.0 + - 400.0 + - 500.0 + - 750.0 base_network: osm gaslimit_enable: false gaslimit: false co2limit_enable: false - co2limit: 7.75e+7 - co2base: 1.487e+9 - + co2limit: 77500000.0 + co2base: 1487000000.0 operational_reserve: activate: false epsilon_load: 0.02 epsilon_vres: 0.02 contingency: 4000 - max_hours: battery: 6 H2: 168 - extendable_carriers: - Generator: [solar, solar-hsat, onwind, offwind-ac, offwind-dc, offwind-float, OCGT, CCGT] - StorageUnit: [] # battery, H2 - Store: [battery, H2] - Link: [] # H2 pipeline - - powerplants_filter: (DateOut >= 2024 or DateOut != DateOut) and not (Country == 'Germany' and Fueltype == 'Nuclear') + Generator: + - solar + - "solar-hsat" + - onwind + - "offwind-ac" + - "offwind-dc" + - "offwind-float" + - OCGT + - CCGT + StorageUnit: [] + Store: + - battery + - H2 + Link: [] + powerplants_filter: "(DateOut >= 2024 or DateOut != DateOut) and not (Country == 'Germany' and Fueltype == 'Nuclear')" custom_powerplants: false everywhere_powerplants: [] - - conventional_carriers: [nuclear, oil, OCGT, CCGT, coal, lignite, geothermal, biomass] - renewable_carriers: [solar, solar-hsat, onwind, offwind-ac, offwind-dc, offwind-float, hydro] - + conventional_carriers: + - nuclear + - oil + - OCGT + - CCGT + - coal + - lignite + - geothermal + - biomass + renewable_carriers: + - solar + - "solar-hsat" + - onwind + - "offwind-ac" + - "offwind-dc" + - "offwind-float" + - hydro estimate_renewable_capacities: enable: true from_gem: true year: 2020 expansion_limit: false technology_mapping: - Offshore: offwind-ac + Offshore: "offwind-ac" Onshore: onwind PV: solar - autarky: enable: false by_country: false - transmission_limit: vopt # docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#atlite atlite: - default_cutout: europe-2013-sarah3-era5 + default_cutout: "europe-2013-sarah3-era5" nprocesses: 16 show_progress: false cutouts: - # use 'base' to determine geographical bounds and time span from config - # base: - # module: era5 - europe-2013-sarah3-era5: - module: [sarah, era5] # in priority order - x: [-12., 42.] - y: [33., 72.] - dx: 0.3 - dy: 0.3 - time: ['2013', '2013'] - # prepare_kwargs: - # features: [] - # sarah_dir: "" - europe-1940-2024-era5: + "europe-1940-2024-era5": module: era5 - x: [-12., 42.] - y: [33., 72.] + x: + - -12.0 + - 42.0 + 'y': + - 33.0 + - 72.0 dx: 0.3 dy: 0.3 - time: ['1940', '2024'] + time: + - '2013' + - '2013' chunks: time: 500 prepare_kwargs: - features: ['temperature', 'height', 'runoff'] + features: + - temperature + - height + - runoff + sarah_dir: monthly_requests: true - tmpdir: "./cutouts_tmp/" + tmpdir: ./cutouts_tmp/ # docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#renewable renewable: @@ -192,19 +216,42 @@ renewable: add_cutout_windspeed: true resource_classes: 1 capacity_per_sqkm: 3 - # correction_factor: 0.93 + correction_factor: 1.0 corine: - grid_codes: [12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 31, 32] - distance: 1000 - distance_grid_codes: [1, 2, 3, 4, 5, 6] + grid_codes: + - 12 + - 13 + - 14 + - 15 + - 16 + - 17 + - 18 + - 19 + - 20 + - 21 + - 22 + - 23 + - 24 + - 25 + - 26 + - 27 + - 28 + - 29 + - 31 + - 32 + distance: 1000.0 + distance_grid_codes: + - 1 + - 2 + - 3 + - 4 + - 5 + - 6 luisa: false - # grid_codes: [1111, 1121, 1122, 1123, 1130, 1210, 1221, 1222, 1230, 1241, 1242] - # distance: 1000 - # distance_grid_codes: [1111, 1121, 1122, 1123, 1130, 1210, 1221, 1222, 1230, 1241, 1242] natura: true excluder_resolution: 100 - clip_p_max_pu: 1.e-2 - offwind-ac: + clip_p_max_pu: 0.01 + "offwind-ac": cutout: default resource: method: wind @@ -214,16 +261,20 @@ renewable: resource_classes: 1 capacity_per_sqkm: 2 correction_factor: 0.8855 - corine: [44, 255] - luisa: false # [0, 5230] + corine: + - 44 + - 255 + luisa: false natura: true ship_threshold: 400 - max_depth: 60 - max_shore_distance: 30000 + max_depth: 60.0 + min_depth: + max_shore_distance: 30000.0 + min_shore_distance: excluder_resolution: 200 - clip_p_max_pu: 1.e-2 - landfall_length: 20 - offwind-dc: + clip_p_max_pu: 0.01 + landfall_length: 20.0 + "offwind-dc": cutout: default resource: method: wind @@ -233,16 +284,20 @@ renewable: resource_classes: 1 capacity_per_sqkm: 2 correction_factor: 0.8855 - corine: [44, 255] - luisa: false # [0, 5230] + corine: + - 44 + - 255 + luisa: false natura: true ship_threshold: 400 - max_depth: 60 - min_shore_distance: 30000 + max_depth: 60.0 + min_depth: + max_shore_distance: + min_shore_distance: 30000.0 excluder_resolution: 200 - clip_p_max_pu: 1.e-2 - landfall_length: 30 - offwind-float: + clip_p_max_pu: 0.01 + landfall_length: 30.0 + "offwind-float": cutout: default resource: method: wind @@ -250,57 +305,109 @@ renewable: smooth: false add_cutout_windspeed: true resource_classes: 1 - # ScholzPhd Tab 4.3.1: 10MW/km^2 capacity_per_sqkm: 2 correction_factor: 0.8855 - # proxy for wake losses - # from 10.1016/j.energy.2018.08.153 - # until done more rigorously in #153 - corine: [44, 255] + corine: + - 44 + - 255 + luisa: false natura: true ship_threshold: 400 + max_depth: 1000.0 + min_depth: 60.0 + max_shore_distance: + min_shore_distance: excluder_resolution: 200 - min_depth: 60 - max_depth: 1000 - clip_p_max_pu: 1.e-2 - landfall_length: 40 + clip_p_max_pu: 0.01 + landfall_length: 40.0 solar: cutout: default resource: method: pv panel: CSi orientation: - slope: 35. - azimuth: 180. + slope: 35.0 + azimuth: 180.0 + tracking: resource_classes: 1 capacity_per_sqkm: 5.1 - # correction_factor: 0.854337 - corine: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 26, 31, 32] - luisa: false # [1111, 1121, 1122, 1123, 1130, 1210, 1221, 1222, 1230, 1241, 1242, 1310, 1320, 1330, 1410, 1421, 1422, 2110, 2120, 2130, 2210, 2220, 2230, 2310, 2410, 2420, 3210, 3320, 3330] + correction_factor: 1.0 + corine: + - 1 + - 2 + - 3 + - 4 + - 5 + - 6 + - 7 + - 8 + - 9 + - 10 + - 11 + - 12 + - 13 + - 14 + - 15 + - 16 + - 17 + - 18 + - 19 + - 20 + - 26 + - 31 + - 32 + luisa: false natura: true excluder_resolution: 100 - clip_p_max_pu: 1.e-2 - solar-hsat: + clip_p_max_pu: 0.01 + "solar-hsat": cutout: default resource: method: pv panel: CSi orientation: - slope: 35. - azimuth: 180. + slope: 35.0 + azimuth: 180.0 tracking: horizontal resource_classes: 1 - capacity_per_sqkm: 4.43 # 15% higher land usage acc. to NREL - corine: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 26, 31, 32] - luisa: false # [1111, 1121, 1122, 1123, 1130, 1210, 1221, 1222, 1230, 1241, 1242, 1310, 1320, 1330, 1410, 1421, 1422, 2110, 2120, 2130, 2210, 2220, 2230, 2310, 2410, 2420, 3210, 3320, 3330] + capacity_per_sqkm: 4.43 + correction_factor: 1.0 + corine: + - 1 + - 2 + - 3 + - 4 + - 5 + - 6 + - 7 + - 8 + - 9 + - 10 + - 11 + - 12 + - 13 + - 14 + - 15 + - 16 + - 17 + - 18 + - 19 + - 20 + - 26 + - 31 + - 32 + luisa: false natura: true excluder_resolution: 100 - clip_p_max_pu: 1.e-2 + clip_p_max_pu: 0.01 hydro: cutout: default - carriers: [ror, PHS, hydro] + carriers: + - ror + - PHS + - hydro PHS_max_hours: 6 - hydro_max_hours: energy_capacity_totals_by_country # one of energy_capacity_totals_by_country, estimate_by_large_installations or a float + hydro_max_hours: energy_capacity_totals_by_country flatten_dispatch: false flatten_dispatch_buffer: 0.2 clip_min_inflow: 1.0 @@ -313,30 +420,30 @@ conventional: unit_commitment: false dynamic_fuel_price: false nuclear: - p_max_pu: data/nuclear_p_max_pu.csv # float of file name + p_max_pu: data/nuclear_p_max_pu.csv # docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#lines lines: types: - 63.: 94-AL1/15-ST1A 20.0 - 66.: 94-AL1/15-ST1A 20.0 - 90.: 184-AL1/30-ST1A 110.0 - 110.: 184-AL1/30-ST1A 110.0 - 132.: 243-AL1/39-ST1A 110.0 - 150.: 243-AL1/39-ST1A 110.0 - 220.: Al/St 240/40 2-bundle 220.0 - 300.: Al/St 240/40 3-bundle 300.0 - 330.: Al/St 240/40 3-bundle 300.0 - 380.: Al/St 240/40 4-bundle 380.0 - 400.: 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 + 63.0: "94-AL1/15-ST1A 20.0" + 66.0: "94-AL1/15-ST1A 20.0" + 90.0: "184-AL1/30-ST1A 110.0" + 110.0: "184-AL1/30-ST1A 110.0" + 132.0: "243-AL1/39-ST1A 110.0" + 150.0: "243-AL1/39-ST1A 110.0" + 220.0: "Al/St 240/40 2-bundle 220.0" + 300.0: "Al/St 240/40 3-bundle 300.0" + 330.0: "Al/St 240/40 3-bundle 300.0" + 380.0: "Al/St 240/40 4-bundle 380.0" + 400.0: "Al/St 240/40 4-bundle 380.0" + 500.0: "Al/St 240/40 4-bundle 380.0" + 750.0: "Al/St 560/50 4-bundle 750.0" s_max_pu: 0.7 s_nom_max: .inf - max_extension: 20000 #MW + max_extension: 20000 length_factor: 1.25 reconnect_crimea: true - under_construction: keep # 'zero': set capacity to zero, 'remove': remove, 'keep': with full capacity for lines in grid extract + under_construction: keep dynamic_line_rating: activate: false cutout: default @@ -349,9 +456,9 @@ links: p_max_pu: 1.0 p_min_pu: -1.0 p_nom_max: .inf - max_extension: 30000 #MW + max_extension: 30000 length_factor: 1.25 - under_construction: keep # 'zero': set capacity to zero, 'remove': remove, 'keep': with full capacity for lines in grid extract + under_construction: keep # docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#transmission_projects transmission_projects: @@ -367,17 +474,15 @@ transmission_projects: - under_construction - in_permitting - confirmed - #- planned_not_yet_permitted - #- under_consideration - new_link_capacity: zero #keep or zero + new_link_capacity: zero # docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#transformers transformers: x: 0.1 - s_nom: 2000. - type: '' + s_nom: 2000.0 + type: "" -# docs-load in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#load +# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#load load: fill_gaps: enable: true @@ -391,12 +496,7 @@ load: gdp: 0.6 population: 0.4 -# docs -# TODO: PyPSA-Eur merge issue in prepare_sector_network.py -# regulate what components with which carriers are kept from PyPSA-Eur; -# some technologies are removed because they are implemented differently -# (e.g. battery or H2 storage) or have different year-dependent costs -# in PyPSA-Eur-Sec +# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#pypsa_eur pypsa_eur: Bus: - AC @@ -404,10 +504,10 @@ pypsa_eur: - DC Generator: - onwind - - offwind-ac - - offwind-dc - - offwind-float - - solar-hsat + - "offwind-ac" + - "offwind-dc" + - "offwind-float" + - "solar-hsat" - solar - ror - nuclear @@ -430,19 +530,19 @@ biomass: solid biomass: - Agricultural waste - Fuelwood residues - - Secondary Forestry residues - woodchips + - "Secondary Forestry residues - woodchips" - Sawdust - Residues from landscape care not included: - Sugar from sugar beet - Rape seed - - "Sunflower, soya seed " + - 'Sunflower, soya seed ' - Bioethanol barley, wheat, grain maize, oats, other cereals and rye - Miscanthus, switchgrass, RCG - Willow - Poplar - FuelwoodRW - - C&P_RW + - "C&P_RW" biogas: - Manure solid, liquid - Sludge @@ -465,19 +565,44 @@ biomass: 2045: 1 2050: 1 - -# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#solar-thermal +# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#solar_thermal solar_thermal: - clearsky_model: simple # should be "simple" or "enhanced"? + clearsky_model: simple orientation: - slope: 45. - azimuth: 180. + slope: 45.0 + azimuth: 180.0 cutout: default -# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#existing-capacities +# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#existing_capacities existing_capacities: - grouping_years_power: [1920, 1950, 1955, 1960, 1965, 1970, 1975, 1980, 1985, 1990, 1995, 2000, 2005, 2010, 2015, 2020, 2025] - grouping_years_heat: [1980, 1985, 1990, 1995, 2000, 2005, 2010, 2015, 2019] # heat grouping years >= baseyear will be ignored + grouping_years_power: + - 1920 + - 1950 + - 1955 + - 1960 + - 1965 + - 1970 + - 1975 + - 1980 + - 1985 + - 1990 + - 1995 + - 2000 + - 2005 + - 2010 + - 2015 + - 2020 + - 2025 + grouping_years_heat: + - 1980 + - 1985 + - 1990 + - 1995 + - 2000 + - 2005 + - 2010 + - 2015 + - 2019 threshold_capacity: 10 default_heating_lifetime: 20 conventional_carriers: @@ -543,13 +668,13 @@ sector: recovery_factor: 0.6 marginal_cost_charger: 0.035 ignore_missing_regions: false - heat_source_cooling: 6 #K + heat_source_cooling: 6 heat_pump_cop_approximation: refrigerant: ammonia - heat_exchanger_pinch_point_temperature_difference: 5 #K + heat_exchanger_pinch_point_temperature_difference: 5 isentropic_compressor_efficiency: 0.8 heat_loss: 0.0 - min_delta_t_lift: 10 #K + min_delta_t_lift: 10 limited_heat_sources: geothermal: constant_temperature_celsius: 65 @@ -592,8 +717,8 @@ sector: heat_demand_cutout: default bev_dsm_restriction_value: 0.8 bev_dsm_restriction_time: 7 - transport_heating_deadband_upper: 20. - transport_heating_deadband_lower: 15. + transport_heating_deadband_upper: 20.0 + transport_heating_deadband_lower: 15.0 ICE_lower_degree_factor: 0.375 ICE_upper_degree_factor: 1.6 EV_lower_degree_factor: 0.98 @@ -630,9 +755,9 @@ sector: 2040: 0.3 2045: 0.15 2050: 0 - transport_electric_efficiency: 53.19 # 1 MWh_el = 53.19*100 km - transport_fuel_cell_efficiency: 30.003 # 1 MWh_H2 = 30.003*100 km - transport_ice_efficiency: 16.0712 # 1 MWh_oil = 16.0712 * 100 km + transport_electric_efficiency: 53.19 + transport_fuel_cell_efficiency: 30.003 + transport_ice_efficiency: 16.0712 agriculture_machinery_electric_share: 0.5 agriculture_machinery_oil_share: 0.5 agriculture_machinery_fuel_efficiency: 0.7 @@ -666,16 +791,16 @@ sector: 2045: 0.2 2050: 0 shipping_methanol_efficiency: 0.46 - shipping_oil_efficiency: 0.40 - aviation_demand_factor: 1. - land_transport_demand_factor: 1. - HVC_demand_factor: 1. + shipping_oil_efficiency: 0.4 + aviation_demand_factor: 1.0 + land_transport_demand_factor: 1.0 + HVC_demand_factor: 1.0 time_dep_hp_cop: true - heat_pump_sink_T_individual_heating: 55. + heat_pump_sink_T_individual_heating: 55.0 reduce_space_heat_exogenously: true reduce_space_heat_exogenously_factor: - 2020: 0.10 # this results in a space heat demand reduction of 10% - 2025: 0.09 # first heat demand increases compared to 2020 because of larger floor area per capita + 2020: 0.1 + 2025: 0.09 2030: 0.09 2035: 0.11 2040: 0.16 @@ -694,16 +819,16 @@ sector: oil_boilers: false biomass_boiler: true overdimension_heat_generators: - decentral: 1.1 #to cover demand peaks bigger than data + decentral: 1.1 central: 1.0 chp: enable: true fuel: - - solid biomass # For solid biomass, CHP with and without CC are added - - gas # For all other fuels the same techno economic data from gas CHP is taken - micro_chp: false # Only gas is used for micro_chp + - solid biomass + - gas + micro_chp: false solar_thermal: true - solar_cf_correction: 0.788457 # = >>> 1/1.2683 + solar_cf_correction: 0.788457 methanation: true coal_cc: false dac: true @@ -747,9 +872,8 @@ sector: cc_fraction: 0.9 hydrogen_underground_storage: true hydrogen_underground_storage_locations: - - onshore # more than 50 km from sea - - nearshore # within 50 km of sea - # - offshore + - onshore + - nearshore methanol: regional_methanol_demand: false methanol_reforming: false @@ -775,6 +899,7 @@ sector: use_electrolysis_waste_heat: 0.25 electricity_transmission_grid: true electricity_distribution_grid: true + electricity_distribution_grid_cost_factor: 1.0 electricity_grid_connection: true transmission_efficiency: enable: @@ -786,10 +911,10 @@ sector: efficiency_static: 0.98 efficiency_per_1000km: 0.977 H2 pipeline: - efficiency_per_1000km: 1 # 0.982 + efficiency_per_1000km: 1 compression_per_1000km: 0.018 gas pipeline: - efficiency_per_1000km: 1 #0.977 + efficiency_per_1000km: 1 compression_per_1000km: 0.01 electricity distribution grid: efficiency_static: 0.97 @@ -817,18 +942,17 @@ sector: municipal_solid_waste: 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_growth: + onwind: 16 + solar: 28 + "offwind-ac": 35 + "offwind-dc": 35 max_relative_growth: onwind: 3 solar: 3 - offwind-ac: 3 - offwind-dc: 3 + "offwind-ac": 3 + "offwind-dc": 3 enhanced_geothermal: enable: false flexible: true @@ -838,13 +962,13 @@ sector: sustainability_factor: 0.0025 solid_biomass_import: enable: false - price: 54 #EUR/MWh - max_amount: 1390 # TWh - upstream_emissions_factor: .1 #share of solid biomass CO2 emissions at full combustion + price: 54 + max_amount: 1390 + upstream_emissions_factor: 0.1 imports: enable: false limit: .inf - limit_sense: <= + limit_sense: "<=" price: H2: 74 NH3: 97 @@ -885,13 +1009,9 @@ industry: MWh_elec_per_tNH3_SMR: 0.7 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 + MWh_NH3_per_MWh_H2_cracker: 1.46 NH3_process_emissions: 24.5 petrochemical_process_emissions: 25.5 - #HVC primary/recycling based on values used in Neumann et al https://doi.org/10.1016/j.joule.2023.06.016, linearly interpolated between 2020 and 2050 - #2020 recycling rates based on Agora https://static.agora-energiewende.de/fileadmin/Projekte/2021/2021_02_EU_CEAP/A-EW_254_Mobilising-circular-economy_study_WEB.pdf - #fractions refer to the total primary HVC production in 2020 - #assumes 6.7 Mtplastics produced from recycling in 2020 steam_biomass_fraction: 1.0 steam_hydrogen_fraction: 0 steam_electricity_fraction: 0 @@ -910,7 +1030,7 @@ industry: 2035: 0.21 2040: 0.24 2045: 0.27 - 2050: 0.30 + 2050: 0.3 HVC_chemical_recycling_fraction: 2020: 0.0 2025: 0.0 @@ -918,8 +1038,8 @@ industry: 2035: 0.08 2040: 0.12 2045: 0.16 - 2050: 0.20 - HVC_environment_sequestration_fraction: 0. + 2050: 0.2 + HVC_environment_sequestration_fraction: 0.0 waste_to_energy: false waste_to_energy_cc: false sector_ratios_fraction_future: @@ -930,8 +1050,8 @@ industry: 2040: 0.7 2045: 0.85 2050: 1.0 - basic_chemicals_without_NH3_production_today: 69. #Mt/a, = 86 Mtethylene-equiv - 17 MtNH3 - HVC_production_today: 52. + basic_chemicals_without_NH3_production_today: 69.0 + HVC_production_today: 52.0 MWh_elec_per_tHVC_mechanical_recycling: 0.547 MWh_elec_per_tHVC_chemical_recycling: 6.9 chlorine_production_today: 9.58 @@ -945,7 +1065,6 @@ industry: reference_year: 2019 oil_refining_emissions: 0.013 gas_compression_losses: 0.04 - # docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#costs costs: year: 2050 @@ -957,14 +1076,16 @@ costs: fuel: 0 investment: 0 lifetime: 25 - "CO2 intensity": 0 - "discount rate": 0.07 - "standing losses": 0 + CO2 intensity: 0 + discount rate: 0.07 + standing losses: 0 custom_cost_fn: data/custom_costs.csv overwrites: {} + capital_cost: {} + marginal_cost: {} emission_prices: enable: false - co2: 0. + co2: 0.0 co2_monthly_prices: false # docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#clustering @@ -972,6 +1093,7 @@ clustering: mode: busmap administrative: level: 1 + countries: {} focus_weights: false copperplate_regions: [] build_bidding_zones: @@ -979,6 +1101,7 @@ clustering: aggregate_to_tyndp: false simplify_network: to_substations: false + exclude_carriers: [] remove_stubs: true remove_stubs_across_borders: false cluster_network: @@ -1000,7 +1123,7 @@ clustering: overnight_cost: mean one_ports: overnight_cost: mean - + buses: {} temporal: resolution_elec: false resolution_sector: false @@ -1015,6 +1138,141 @@ adjustments: capital_cost: 1.0 absolute: false +# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#solving +solving: + options: + clip_p_max_pu: 0.01 + load_shedding: false + curtailment_mode: false + noisy_costs: true + skip_iterations: true + rolling_horizon: false + seed: 123 + custom_extra_functionality: ../data/custom_extra_functionality.py + io_api: + track_iterations: false + min_iterations: 2 + max_iterations: 3 + transmission_losses: 2 + linearized_unit_commitment: true + horizon: 365 + post_discretization: + enable: false + line_unit_size: 1700 + line_threshold: 0.3 + link_unit_size: + DC: 2000 + H2 pipeline: 1200 + gas pipeline: 1500 + link_threshold: + DC: 0.3 + H2 pipeline: 0.3 + gas pipeline: 0.3 + fractional_last_unit_size: false + keep_files: false + model_kwargs: + solver_dir: "" + agg_p_nom_limits: + agg_offwind: false + agg_solar: false + include_existing: false + file: data/agg_p_nom_minmax.csv + constraints: + CCL: false + EQ: false + BAU: false + SAFE: false + solver: + name: gurobi + options: "gurobi-default" + solver_options: + "highs-default": + threads: 1 + solver: ipm + run_crossover: 'off' + small_matrix_value: 1.0e-06 + large_matrix_value: 1000000000.0 + primal_feasibility_tolerance: 1.0e-05 + dual_feasibility_tolerance: 1.0e-05 + ipm_optimality_tolerance: 0.0001 + parallel: 'on' + random_seed: 123 + "highs-simplex": + solver: simplex + parallel: 'on' + primal_feasibility_tolerance: 1.0e-05 + dual_feasibility_tolerance: 1.0e-05 + random_seed: 123 + "gurobi-default": + threads: 32 + method: 2 + crossover: 0 + BarConvTol: 1.0e-05 + Seed: 123 + AggFill: 0 + PreDual: 0 + GURO_PAR_BARDENSETHRESH: 200 + "gurobi-numeric-focus": + NumericFocus: 3 + method: 2 + crossover: 0 + BarHomogeneous: 1 + BarConvTol: 1.0e-05 + FeasibilityTol: 0.0001 + OptimalityTol: 0.0001 + ObjScale: -0.5 + threads: 8 + Seed: 123 + "gurobi-fallback": + crossover: 0 + method: 2 + BarHomogeneous: 1 + BarConvTol: 1.0e-05 + FeasibilityTol: 1.0e-05 + OptimalityTol: 1.0e-05 + Seed: 123 + threads: 8 + "cplex-default": + threads: 4 + lpmethod: 4 + solutiontype: 2 + barrier.convergetol: 1.0e-05 + feasopt.tolerance: 1.0e-06 + "copt-default": + Threads: 8 + LpMethod: 2 + Crossover: 0 + RelGap: 1.0e-06 + Dualize: 0 + "copt-gpu": + LpMethod: 6 + GPUMode: 1 + PDLPTol: 1.0e-05 + Crossover: 0 + "xpress-default": + threads: 8 + lpflags: 4 + crossover: 0 + bargaptarget: 1.0e-05 + baralg: 2 + "xpress-gpu": + lpflags: 4 + crossover: 0 + baralg: 4 + barhggpu: 1 + barhgreltol: 1.0e-05 + "cbc-default": {} + "glpk-default": {} + check_objective: + enable: false + expected_value: + atol: 1000000 + rtol: 0.01 + oetc: + mem_mb: 128000 + memory_logging_frequency: 5 + runtime: 48h + # docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#data data: hotmaps_industrial_sites: @@ -1065,6 +1323,9 @@ data: scigrid_gas: source: primary version: latest + seawater_temperature: + source: archive + version: latest synthetic_electricity_demand: source: primary version: latest @@ -1155,146 +1416,19 @@ data: jrc_ardeco: source: archive version: latest + bidding_zones_electricitymaps: + source: archive + version: latest + bidding_zones_entsoepy: + source: archive + version: latest # docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#overpass_api overpass_api: - url: https://overpass-api.de/api/interpreter + url: "https://overpass-api.de/api/interpreter" max_tries: 5 timeout: 600 user_agent: - project_name: PyPSA-Eur - email: contact@pypsa.org - website: https://github.com/PyPSA/pypsa-eur - -secrets: - corine: '' #Add API key here if primary source is used for retrieving corine dataset after registering in https://land.copernicus.eu/user/login - -# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#solving -solving: - options: - clip_p_max_pu: 1.e-2 - load_shedding: false - curtailment_mode: false - noisy_costs: true - skip_iterations: true - rolling_horizon: false - seed: 123 - custom_extra_functionality: "../scripts/pypsa-de/additional_functionality.py" - # io_api: "direct" # Increases performance but only supported for the highs and gurobi solvers - # options that go into the optimize function - track_iterations: false - min_iterations: 2 - max_iterations: 3 - transmission_losses: 2 - linearized_unit_commitment: true - horizon: 365 - post_discretization: - enable: false - line_unit_size: 1700 - line_threshold: 0.3 - link_unit_size: - DC: 2000 - H2 pipeline: 1200 - gas pipeline: 1500 - link_threshold: - DC: 0.3 - H2 pipeline: 0.3 - gas pipeline: 0.3 - fractional_last_unit_size: false - keep_files: false - model_kwargs: - solver_dir: "" - - agg_p_nom_limits: - agg_offwind: false - agg_solar: false - include_existing: false - file: data/agg_p_nom_minmax.csv - - constraints: - CCL: false - EQ: false - BAU: false - SAFE: false - - solver: - name: gurobi - options: gurobi-default - - solver_options: - highs-default: - # refer to https://ergo-code.github.io/HiGHS/dev/options/definitions/ - threads: 1 - solver: "ipm" - run_crossover: "off" - small_matrix_value: 1e-6 - large_matrix_value: 1e9 - primal_feasibility_tolerance: 1e-5 - dual_feasibility_tolerance: 1e-5 - ipm_optimality_tolerance: 1e-4 - parallel: "on" - random_seed: 123 - highs-simplex: - solver: "simplex" - parallel: "on" - primal_feasibility_tolerance: 1e-5 - dual_feasibility_tolerance: 1e-5 - random_seed: 123 - gurobi-default: - threads: 32 - method: 2 # barrier - crossover: 0 - BarConvTol: 1.e-5 - Seed: 123 - AggFill: 0 - PreDual: 0 - GURO_PAR_BARDENSETHRESH: 200 - gurobi-numeric-focus: - NumericFocus: 3 # Favour numeric stability over speed - method: 2 # barrier - crossover: 0 # do not use crossover - BarHomogeneous: 1 # Use homogeneous barrier if standard does not converge - BarConvTol: 1.e-5 - FeasibilityTol: 1.e-4 - OptimalityTol: 1.e-4 - ObjScale: -0.5 - threads: 8 - Seed: 123 - gurobi-fallback: # Use gurobi defaults - crossover: 0 - method: 2 # barrier - BarHomogeneous: 1 # Use homogeneous barrier if standard does not converge - BarConvTol: 1.e-5 - FeasibilityTol: 1.e-5 - OptimalityTol: 1.e-5 - Seed: 123 - threads: 8 - cplex-default: - threads: 4 - lpmethod: 4 # barrier - solutiontype: 2 # non basic solution, ie no crossover - barrier.convergetol: 1.e-5 - feasopt.tolerance: 1.e-6 - copt-default: - Threads: 8 - LpMethod: 2 - Crossover: 0 - RelGap: 1.e-6 - Dualize: 0 - copt-gpu: - LpMethod: 6 - GPUMode: 1 - PDLPTol: 1.e-5 - Crossover: 0 - cbc-default: {} # Used in CI - glpk-default: {} # Used in CI - - check_objective: - enable: false - expected_value: None - atol: 1_000_000 - rtol: 0.01 - - mem_mb: 128000 - memory_logging_frequency: 5 # in seconds - runtime: 48h #runtime in humanfriendly style https://humanfriendly.readthedocs.io/en/latest/ + project_name: "PyPSA-Eur" + email: "contact@pypsa.org" + website: "https://github.com/PyPSA/pypsa-eur" diff --git a/config/examples/config.distribution-grid-experimental.yaml b/config/examples/config.distribution-grid-experimental.yaml index abdb51407..de36db569 100644 --- a/config/examples/config.distribution-grid-experimental.yaml +++ b/config/examples/config.distribution-grid-experimental.yaml @@ -1,3 +1,4 @@ +# yaml-language-server: $schema=../schema.json # SPDX-FileCopyrightText: Contributors to PyPSA-Eur # # SPDX-License-Identifier: CC0-1.0 diff --git a/config/examples/config.entsoe-all.yaml b/config/examples/config.entsoe-all.yaml index 64a831a82..b1d0dde90 100644 --- a/config/examples/config.entsoe-all.yaml +++ b/config/examples/config.entsoe-all.yaml @@ -1,3 +1,4 @@ +# yaml-language-server: $schema=../schema.json # SPDX-FileCopyrightText: Contributors to PyPSA-Eur # # SPDX-License-Identifier: CC0-1.0 diff --git a/config/examples/config.iterative.yaml b/config/examples/config.iterative.yaml index 2836f7c5e..24c0b6ea3 100644 --- a/config/examples/config.iterative.yaml +++ b/config/examples/config.iterative.yaml @@ -1,3 +1,4 @@ +# yaml-language-server: $schema=../schema.json # SPDX-FileCopyrightText: Contributors to PyPSA-Eur # # SPDX-License-Identifier: CC0-1.0 diff --git a/config/examples/config.perfect.yaml b/config/examples/config.perfect.yaml index dd0b497ba..07af9a019 100644 --- a/config/examples/config.perfect.yaml +++ b/config/examples/config.perfect.yaml @@ -1,3 +1,4 @@ +# yaml-language-server: $schema=../schema.json # SPDX-FileCopyrightText: Contributors to PyPSA-Eur # # SPDX-License-Identifier: CC0-1.0 diff --git a/config/examples/config.validation.yaml b/config/examples/config.validation.yaml index f73bb4dbf..ccd03c77f 100644 --- a/config/examples/config.validation.yaml +++ b/config/examples/config.validation.yaml @@ -1,3 +1,4 @@ +# yaml-language-server: $schema=../schema.json # SPDX-FileCopyrightText: Contributors to PyPSA-Eur # # SPDX-License-Identifier: CC0-1.0 diff --git a/config/plotting.default.yaml b/config/plotting.default.yaml index 456dbe8f5..f3ac0d873 100644 --- a/config/plotting.default.yaml +++ b/config/plotting.default.yaml @@ -1,3 +1,4 @@ +# yaml-language-server: $schema=./schema.json # SPDX-FileCopyrightText: : 2017-2024 The PyPSA-Eur Authors # # SPDX-License-Identifier: CC0-1.0 diff --git a/config/schema.json b/config/schema.json new file mode 100644 index 000000000..f8de41cf8 --- /dev/null +++ b/config/schema.json @@ -0,0 +1,12963 @@ +{ + "$defs": { + "AdjustmentsConfig": { + "description": "Configuration for top-level adjustments key.", + "properties": { + "electricity": { + "anyOf": [ + { + "type": "boolean" + }, + { + "description": "Configuration for adjustment settings (factor/absolute)", + "properties": { + "factor": { + "anyOf": [ + { + "type": "boolean" + }, + { + "additionalProperties": { + "additionalProperties": { + "additionalProperties": { + "anyOf": [ + { + "type": "number" + }, + { + "additionalProperties": { + "type": "number" + }, + "type": "object" + } + ] + }, + "type": "object" + }, + "type": "object" + }, + "type": "object" + } + ], + "default": false, + "description": "Multiply original value with given factor" + }, + "absolute": { + "anyOf": [ + { + "type": "boolean" + }, + { + "additionalProperties": { + "additionalProperties": { + "additionalProperties": { + "anyOf": [ + { + "type": "number" + }, + { + "additionalProperties": { + "type": "number" + }, + "type": "object" + } + ] + }, + "type": "object" + }, + "type": "object" + }, + "type": "object" + } + ], + "default": false, + "description": "Set attribute to absolute value. Can be also a dictionary with planning horizons as keys." + } + } + } + ], + "default": false, + "description": "Parameter adjustments applied in `prepare_network`." + }, + "sector": { + "anyOf": [ + { + "type": "boolean" + }, + { + "description": "Configuration for adjustment settings (factor/absolute)", + "properties": { + "factor": { + "anyOf": [ + { + "type": "boolean" + }, + { + "additionalProperties": { + "additionalProperties": { + "additionalProperties": { + "anyOf": [ + { + "type": "number" + }, + { + "additionalProperties": { + "type": "number" + }, + "type": "object" + } + ] + }, + "type": "object" + }, + "type": "object" + }, + "type": "object" + } + ], + "default": false, + "description": "Multiply original value with given factor" + }, + "absolute": { + "anyOf": [ + { + "type": "boolean" + }, + { + "additionalProperties": { + "additionalProperties": { + "additionalProperties": { + "anyOf": [ + { + "type": "number" + }, + { + "additionalProperties": { + "type": "number" + }, + "type": "object" + } + ] + }, + "type": "object" + }, + "type": "object" + }, + "type": "object" + } + ], + "default": false, + "description": "Set attribute to absolute value. Can be also a dictionary with planning horizons as keys." + } + } + } + ], + "description": "Parameter adjustments applied in `prepare_sector_network`." + } + } + }, + "AtliteConfig": { + "description": "Configuration for `atlite` settings.", + "properties": { + "default_cutout": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": "europe-2013-sarah3-era5", + "description": "Defines a default cutout. Can refer to a single cutout or a list of cutouts." + }, + "nprocesses": { + "default": 16, + "description": "Number of parallel processes in cutout preparation.", + "type": "integer" + }, + "show_progress": { + "default": false, + "description": "Whether progressbar for atlite conversion processes should be shown. False saves time.", + "type": "boolean" + }, + "cutouts": { + "additionalProperties": { + "description": "Configuration for a single cutout in `atlite.cutouts`.", + "properties": { + "module": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Source of the reanalysis weather dataset (e.g. `ERA5 `_ or `SARAH-3 `_).", + "markdownDescription": "Source of the reanalysis weather dataset (e.g. [ERA5 ](https://www.ecmwf.int/en/forecasts/datasets/reanalysis-datasets/era5) or [SARAH-3 ](https://wui.cmsaf.eu/safira/action/viewDoiDetails?acronym=SARAH_V002))." + }, + "x": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Range of longitudes [\u00b0] to download weather data for. Float interval within [-180, 180]. If not defined, it defaults to the spatial bounds of all bus shapes." + }, + "y": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Range of latitudes [\u00b0] to download weather data for. Float interval within [-90, 90]. If not defined, it defaults to the spatial bounds of all bus shapes." + }, + "dx": { + "anyOf": [ + { + "exclusiveMinimum": 0.25, + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Grid resolution [\u00b0] for longitude. Must be larger than 0.25\u00b0." + }, + "dy": { + "anyOf": [ + { + "exclusiveMinimum": 0.25, + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Grid resolution [\u00b0] for latitude. Must be larger than 0.25\u00b0." + }, + "time": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Time span to download weather data for. If not defined, it defaults to the time interval spanned by the snapshots." + }, + "chunks": { + "anyOf": [ + { + "description": "Configuration for `atlite.cutouts.{name}.chunks` settings.", + "properties": { + "time": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Chunk size for time dimension when preparing cutout." + } + } + }, + { + "type": "null" + } + ], + "default": null, + "description": "Chunking configuration for cutout preparation." + }, + "prepare_kwargs": { + "anyOf": [ + { + "description": "Configuration for `atlite.cutouts.{name}.prepare_kwargs` settings.", + "properties": { + "features": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "When freshly building a cutout, retrieve data only for those features. If not defined, it defaults to all available features." + }, + "sarah_dir": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Path to the location where SARAH-2 or SARAH-3 data is stored; SARAH data requires a manual separate download, see the `atlite documentation `_ for details. Required for building cutouts with SARAH, not required for ERA5 cutouts.", + "markdownDescription": "Path to the location where SARAH-2 or SARAH-3 data is stored; SARAH data requires a manual separate download, see the [atlite documentation ](https://atlite.readthedocs.io) for details. Required for building cutouts with SARAH, not required for ERA5 cutouts." + }, + "monthly_requests": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Whether to use monthly requests for ERA5 data when building the cutout. Helpful to avoid running into request limits with large cutouts." + }, + "tmpdir": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Path to a temporary directory where intermediate files are stored when building the cutout. Helpful when building large cutouts." + } + } + }, + { + "type": "null" + } + ], + "default": null, + "description": "Dictionary of keyword arguments passed to ``atlite.Cutout.prepare()`` when building the cutout." + } + } + }, + "description": "Named cutout configurations.", + "type": "object" + } + } + }, + "BiomassConfig": { + "description": "Configuration for `biomass` settings.", + "properties": { + "year": { + "default": 2030, + "description": "Year for which to retrieve biomass potential according to the assumptions of the `JRC ENSPRESO `_.", + "markdownDescription": "Year for which to retrieve biomass potential according to the assumptions of the [JRC ENSPRESO ](https://data.jrc.ec.europa.eu/dataset/74ed5a04-7d74-4807-9eab-b94774309d9f).", + "maximum": 2050, + "minimum": 2010, + "type": "integer" + }, + "scenario": { + "default": "ENS_Med", + "description": "Scenario for which to retrieve biomass potential. The scenario definition can be seen in `ENSPRESO_BIOMASS `_.", + "markdownDescription": "Scenario for which to retrieve biomass potential. The scenario definition can be seen in [ENSPRESO_BIOMASS ](https://cidportal.jrc.ec.europa.eu/ftp/jrc-opendata/ENSPRESO/ENSPRESO_BIOMASS.xlsx).", + "enum": [ + "ENS_Low", + "ENS_Med", + "ENS_High" + ], + "type": "string" + }, + "classes": { + "description": "Configuration for `biomass.classes` settings.", + "properties": { + "solid biomass": { + "description": "The comodity that are included as solid biomass.", + "items": { + "type": "string" + }, + "type": "array" + }, + "not included": { + "description": "The comodity that are not included as a biomass potential.", + "items": { + "type": "string" + }, + "type": "array" + }, + "biogas": { + "description": "The comodity that are included as biogas.", + "items": { + "type": "string" + }, + "type": "array" + }, + "municipal solid waste": { + "description": "The commodities that are included as municipal solid waste.", + "items": { + "type": "string" + }, + "type": "array" + } + } + }, + "share_unsustainable_use_retained": { + "additionalProperties": { + "type": "number" + }, + "description": "Share of unsustainable biomass use retained using primary production of Eurostat data as reference.", + "type": "object" + }, + "share_sustainable_potential_available": { + "additionalProperties": { + "type": "number" + }, + "description": "Share determines phase-in of ENSPRESO biomass potentials.", + "type": "object" + } + } + }, + "ClusteringConfig": { + "description": "Configuration for `clustering` settings.", + "properties": { + "mode": { + "default": "busmap", + "description": "'busmap': Default. 'custom_busmap': Enable the use of custom busmaps in rule `cluster_network`. If activated the rule looks for provided busmaps at ``data/busmaps/base_s_{clusters}_{base_network}.csv`` which should have the same format as ``resources/busmap_base_s_{clusters}.csv``, i.e. the index should contain the buses of ``networks/base_s.nc``. {base_network} is the name of the selected base_network in electricity, e.g. ``gridkit``, ``osm-prebuilt``, or ``osm-raw``. 'administrative': Clusters and indexes the network based on the administrative regions of the countries based on ``nuts3_shapes.geojson`` (level: 1, 2, 3, bz). To activate this, additionally set the ``clusters`` wildcard in ``scenario`` to 'adm'. 'custom_busshapes': Enable the use of custom shapes in rule `cluster_network`. If activated the rule looks for provided busshapes at ``data/busshapes/base_s_{clusters}_{base_network}.geojson``.", + "enum": [ + "busmap", + "custom_busmap", + "administrative", + "custom_busshapes" + ], + "type": "string" + }, + "administrative": { + "description": "Configuration for `clustering.administrative` settings.", + "properties": { + "level": { + "default": 1, + "description": "Level of administrative regions to cluster the network. 0: Country level, 1: NUTS1 level, 2: NUTS2 level, 3: NUTS3 level, 'bz': Bidding zones. Only applies when mode is set to `administrative`. Note that non-NUTS countries 'BA', 'MD', 'UA', and 'XK' can only be clustered to level 0 and 1.", + "enum": [ + 0, + 1, + 2, + 3, + "bz" + ] + }, + "countries": { + "additionalProperties": { + "type": "integer" + }, + "description": "Optionally include dictionary of individual country codes and their individual NUTS levels. Overwrites country-specific `level`. For example: `{'DE': 1, 'FR': 2}`. Only applies when mode is set to `administrative`.", + "type": "object" + } + } + }, + "focus_weights": { + "anyOf": [ + { + "type": "boolean" + }, + { + "additionalProperties": { + "type": "number" + }, + "type": "object" + } + ], + "default": false, + "description": "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. Only applies when mode is set to `busmap`." + }, + "copperplate_regions": { + "description": "Optionally specify the regions to copperplate as a list of groups. Each group is a list of region codes that will be connected with infinite capacity lines.", + "items": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": "array" + }, + "build_bidding_zones": { + "description": "Configuration for `clustering.build_bidding_zones` settings.", + "properties": { + "remove_islands": { + "default": false, + "description": "Exclude from the shape file the Balearic Islands, Bornholm, the Canary Islands, the Orkney Islands, the Shetland Islands, the Azores Islands and Madeira.", + "type": "boolean" + }, + "aggregate_to_tyndp": { + "default": false, + "description": "Adjust the shape file to the TYNDP topology. Aggregate the Southern Norwegian bidding zones and extract Crete as a separate zone from the Greek shape.", + "type": "boolean" + } + } + }, + "simplify_network": { + "description": "Configuration for `clustering.simplify_network` settings.", + "properties": { + "to_substations": { + "default": false, + "description": "Aggregates all nodes without power injection (positive or negative, i.e. demand or generation) to electrically closest ones.", + "type": "boolean" + }, + "exclude_carriers": { + "description": "List of carriers which will not be aggregated. If empty, all carriers will be aggregated.", + "items": { + "type": "string" + }, + "type": "array" + }, + "remove_stubs": { + "default": true, + "description": "Controls whether radial parts of the network should be recursively aggregated. Defaults to true.", + "type": "boolean" + }, + "remove_stubs_across_borders": { + "default": false, + "description": "Controls whether radial parts of the network should be recursively aggregated across borders. Defaults to true.", + "type": "boolean" + } + } + }, + "cluster_network": { + "description": "Configuration for `clustering.cluster_network` settings.", + "properties": { + "algorithm": { + "default": "kmeans", + "description": "Clustering algorithm to use.", + "enum": [ + "kmeans", + "hac" + ], + "type": "string" + }, + "hac_features": { + "description": "List of meteorological variables contained in the weather data cutout that should be considered for hierarchical clustering.", + "items": { + "type": "string" + }, + "type": "array" + } + } + }, + "exclude_carriers": { + "description": "List of carriers which will not be aggregated. If empty, all carriers will be aggregated.", + "items": { + "type": "string" + }, + "type": "array" + }, + "consider_efficiency_classes": { + "default": false, + "description": "Aggregated each carriers into the top 10-quantile (high), the bottom 90-quantile (low), and everything in between (medium).", + "type": "boolean" + }, + "aggregation_strategies": { + "description": "Configuration for `clustering.aggregation_strategies` settings.", + "properties": { + "generators": { + "additionalProperties": { + "type": "string" + }, + "description": "Aggregates the component according to the given strategy. For example, if sum, then all values within each cluster are summed to represent the new generator.", + "type": "object" + }, + "buses": { + "additionalProperties": { + "type": "string" + }, + "description": "Aggregates the component according to the given strategy. For example, if sum, then all values within each cluster are summed to represent the new bus.", + "type": "object" + } + } + }, + "temporal": { + "description": "Configuration for `clustering.temporal` settings.", + "properties": { + "resolution_elec": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "string" + } + ], + "default": false, + "description": "Resample the time-resolution by averaging over every `n` snapshots in `prepare_network`. **Warning:** This option should currently only be used with electricity-only networks, not for sector-coupled networks." + }, + "resolution_sector": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "string" + } + ], + "default": false, + "description": "Resample the time-resolution by averaging over every `n` snapshots in `prepare_sector_network`." + } + } + } + } + }, + "Co2BudgetConfig": { + "additionalProperties": { + "type": "number" + }, + "description": "Configuration for `co2_budget` settings.", + "type": "object" + }, + "ConventionalConfig": { + "additionalProperties": true, + "description": "Configuration for `conventional` settings.", + "properties": { + "unit_commitment": { + "default": false, + "description": "Allow the overwrite of ramp_limit_up, ramp_limit_start_up, ramp_limit_shut_down, p_min_pu, min_up_time, min_down_time, and start_up_cost of conventional generators. Refer to the CSV file 'unit_commitment.csv'.", + "type": "boolean" + }, + "dynamic_fuel_price": { + "default": false, + "description": "Consider the monthly fluctuating fuel prices for each conventional generator. Refer to the CSV file 'data/validation/monthly_fuel_price.csv'.", + "type": "boolean" + }, + "nuclear": { + "additionalProperties": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + } + ] + }, + "description": "For any carrier/technology overwrite attributes as listed below.", + "type": "object" + } + } + }, + "CostsConfig": { + "description": "Configuration for `costs` settings.", + "properties": { + "year": { + "default": 2050, + "description": "Year for which to retrieve cost assumptions of `data/costs/primary//costs_.csv`.", + "type": "integer" + }, + "social_discountrate": { + "default": 0.02, + "description": "Social discount rate to compare costs in different investment periods. 0.02 corresponds to a social discount rate of 2%.", + "type": "number" + }, + "fill_values": { + "description": "Configuration for `costs.fill_values` settings.", + "properties": { + "FOM": { + "default": 0, + "description": "Default fixed operation and maintenance cost.", + "type": "number" + }, + "VOM": { + "default": 0, + "description": "Default variable operation and maintenance cost.", + "type": "number" + }, + "efficiency": { + "default": 1, + "description": "Default efficiency.", + "type": "number" + }, + "fuel": { + "default": 0, + "description": "Default fuel cost.", + "type": "number" + }, + "investment": { + "default": 0, + "description": "Default investment cost.", + "type": "number" + }, + "lifetime": { + "default": 25, + "description": "Default lifetime in years.", + "type": "integer" + }, + "CO2 intensity": { + "default": 0, + "description": "Default CO2 intensity.", + "type": "number" + }, + "discount rate": { + "default": 0.07, + "description": "Default discount rate.", + "type": "number" + }, + "standing losses": { + "default": 0, + "description": "Default standing losses.", + "type": "number" + } + } + }, + "custom_cost_fn": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": "data/custom_costs.csv", + "description": "Path to the custom costs file. None if it should not be used. Default `data/custom_costs.csv` contains minor adjustments for stabilising the optimisation results." + }, + "overwrites": { + "additionalProperties": { + "additionalProperties": { + "type": "number" + }, + "type": "object" + }, + "description": "For the given parameters and technologies, assumptions about their parameter are overwritten the corresponding value of the technology.", + "type": "object" + }, + "capital_cost": { + "additionalProperties": { + "type": "number" + }, + "description": "For the given technologies, assumptions about their capital investment costs are set to the corresponding value. Optional; overwrites cost assumptions from `resources/costs.csv`.", + "type": "object" + }, + "marginal_cost": { + "additionalProperties": { + "type": "number" + }, + "description": "For the given technologies, assumptions about their marginal operating costs are set to the corresponding value. Optional; overwrites cost assumptions from `resources/costs.csv`.", + "type": "object" + }, + "emission_prices": { + "description": "Configuration for `costs.emission_prices` settings.", + "properties": { + "enable": { + "default": false, + "description": "Add cost for a carbon-dioxide price configured in `costs: emission_prices: co2` to `marginal_cost` of generators. Config setting can also be enabled with the keyword `Ep` in the `{opts}` wildcard for electricity-only runs.", + "type": "boolean" + }, + "co2": { + "anyOf": [ + { + "type": "number" + }, + { + "additionalProperties": { + "type": "number" + }, + "type": "object" + } + ], + "default": 0.0, + "description": "Exogenous price of carbon-dioxide. In electricity-only runs it is added to the marginal costs of fossil-fuelled generators according to their carbon intensity, while for sector networks it applies to emissions ending up in CO2 atmosphere." + }, + "co2_monthly_prices": { + "default": false, + "description": "Add monthly cost for a carbon-dioxide price based on historical values built by the rule `build_monthly_prices`.", + "type": "boolean" + } + } + } + } + }, + "CountriesConfig": { + "default": [ + "AL", + "AT", + "BA", + "BE", + "BG", + "CH", + "CZ", + "DE", + "DK", + "EE", + "ES", + "FI", + "FR", + "GB", + "GR", + "HR", + "HU", + "IE", + "IT", + "LT", + "LU", + "LV", + "ME", + "MK", + "NL", + "NO", + "PL", + "PT", + "RO", + "RS", + "SE", + "SI", + "SK", + "XK" + ], + "description": "Configuration for `countries` settings.", + "items": { + "type": "string" + }, + "type": "array" + }, + "DataConfig": { + "description": "Configuration for `data` settings.", + "properties": { + "hotmaps_industrial_sites": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "enspreso_biomass": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "osm": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "worldbank_urban_population": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "gem_europe_gas_tracker": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "co2stop": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "nitrogen_statistics": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "eu_nuts2013": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "eu_nuts2021": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "eurostat_balances": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "eurostat_household_balances": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "wdpa": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "wdpa_marine": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "luisa_land_cover": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "jrc_idees": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "scigrid_gas": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "seawater_temperature": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "synthetic_electricity_demand": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "copernicus_land_cover": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "ship_raster": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "eez": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "nuts3_population": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "gdp_per_capita": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "population_count": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "ghg_emissions": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "gebco": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "attributed_ports": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "corine": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "emobility": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "h2_salt_caverns": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "lau_regions": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "aquifer_data": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "osm_boundaries": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "gem_gspt": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "tyndp": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "powerplants": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "costs": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "country_runoff": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "country_hdd": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "natura": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "bfs_road_vehicle_stock": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "bfs_gdp_and_population": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "mobility_profiles": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "cutout": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "dh_areas": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "geothermal_heat_utilisation_potentials": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "jrc_ardeco": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "bidding_zones_electricitymaps": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "bidding_zones_entsoepy": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + } + } + }, + "ElectricityConfig": { + "description": "Configuration for `electricity` settings.", + "properties": { + "voltages": { + "description": "Voltage levels to consider.", + "items": { + "type": "number" + }, + "type": "array" + }, + "base_network": { + "default": "osm", + "description": "Specify the underlying base network, i.e. GridKit (based on ENTSO-E web map extract), OpenStreetMap (OSM), or TYNDP.", + "enum": [ + "entsoegridkit", + "osm", + "tyndp" + ], + "type": "string" + }, + "gaslimit_enable": { + "default": false, + "description": "Add an overall absolute gas limit configured in `electricity: gaslimit`.", + "type": "boolean" + }, + "gaslimit": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "boolean" + } + ], + "default": false, + "description": "Global gas usage limit." + }, + "co2limit_enable": { + "default": false, + "description": "Add an overall absolute carbon-dioxide emissions limit configured in `electricity: co2limit` in `prepare_network`. **Warning:** This option should currently only be used with electricity-only networks, not for sector-coupled networks.", + "type": "boolean" + }, + "co2limit": { + "default": 77500000.0, + "description": "Cap on total annual system carbon dioxide emissions.", + "type": "number" + }, + "co2base": { + "default": 1487000000.0, + "description": "Reference value of total annual system carbon dioxide emissions if relative emission reduction target is specified in `{opts}` wildcard.", + "type": "number" + }, + "operational_reserve": { + "description": "Configuration for `electricity.operational_reserve` settings.", + "properties": { + "activate": { + "default": false, + "description": "Whether to take operational reserve requirements into account during optimisation.", + "type": "boolean" + }, + "epsilon_load": { + "default": 0.02, + "description": "share of total load.", + "type": "number" + }, + "epsilon_vres": { + "default": 0.02, + "description": "share of total renewable supply.", + "type": "number" + }, + "contingency": { + "default": 4000, + "description": "Fixed reserve capacity (MW).", + "type": "number" + } + } + }, + "max_hours": { + "description": "Configuration for `electricity.max_hours` settings.", + "properties": { + "battery": { + "default": 6, + "description": "Maximum state of charge capacity of the battery in terms of hours at full output capacity `p_nom`. Cf. `PyPSA documentation `_.", + "markdownDescription": "Maximum state of charge capacity of the battery in terms of hours at full output capacity `p_nom`. Cf. [PyPSA documentation ](https://pypsa.readthedocs.io/en/latest/components.html#storage-unit).", + "type": "number" + }, + "H2": { + "default": 168, + "description": "Maximum state of charge capacity of the hydrogen storage in terms of hours at full output capacity `p_nom`. Cf. `PyPSA documentation `_.", + "markdownDescription": "Maximum state of charge capacity of the hydrogen storage in terms of hours at full output capacity `p_nom`. Cf. [PyPSA documentation ](https://pypsa.readthedocs.io/en/latest/components.html#storage-unit).", + "type": "number" + } + } + }, + "extendable_carriers": { + "description": "Configuration for `electricity.extendable_carriers` settings.", + "properties": { + "Generator": { + "description": "Defines existing or non-existing conventional and renewable power plants to be extendable during the optimization. Conventional generators can only be built/expanded where already existent today. If a listed conventional carrier is not included in the `conventional_carriers` list, the lower limit of the capacity expansion is set to 0.", + "items": { + "type": "string" + }, + "type": "array" + }, + "StorageUnit": { + "description": "Adds extendable storage units (battery and/or hydrogen) at every node/bus after clustering without capacity limits and with zero initial capacity.", + "items": { + "type": "string" + }, + "type": "array" + }, + "Store": { + "description": "Adds extendable storage units (battery and/or hydrogen) at every node/bus after clustering without capacity limits and with zero initial capacity.", + "items": { + "type": "string" + }, + "type": "array" + }, + "Link": { + "description": "Adds extendable links (H2 pipelines only) at every connection where there are lines or HVDC links without capacity limits and with zero initial capacity. Hydrogen pipelines require hydrogen storage to be modelled as `Store`.", + "items": { + "type": "string" + }, + "type": "array" + } + } + }, + "powerplants_filter": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "boolean" + } + ], + "default": "(DateOut >= 2024 or DateOut != DateOut) and not (Country == 'Germany' and Fueltype == 'Nuclear')", + "description": "Filter query for the default powerplant database." + }, + "custom_powerplants": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "boolean" + } + ], + "default": false, + "description": "Filter query for the custom powerplant database." + }, + "everywhere_powerplants": { + "description": "List of conventional power plants to add to every node in the model with zero initial capacity. To be used in combination with `extendable_carriers` to allow for building conventional powerplants irrespective of existing locations.", + "items": { + "type": "string" + }, + "type": "array" + }, + "conventional_carriers": { + "description": "List of conventional power plants to include in the model from `resources/powerplants_s_{clusters}.csv`. If an included carrier is also listed in `extendable_carriers`, the capacity is taken as a lower bound.", + "items": { + "type": "string" + }, + "type": "array" + }, + "renewable_carriers": { + "description": "List of renewable generators to include in the model.", + "items": { + "type": "string" + }, + "type": "array" + }, + "estimate_renewable_capacities": { + "description": "Configuration for `electricity.estimate_renewable_capacities` settings.", + "properties": { + "enable": { + "default": true, + "description": "Activate routine to estimate renewable capacities in rule `add_electricity`. This option should not be used in combination with pathway planning `foresight: myopic` or `foresight: perfect` as renewable capacities are added differently in `add_existing_baseyear`.", + "type": "boolean" + }, + "from_gem": { + "default": true, + "description": "Add renewable capacities from `Global Energy Monitor's Global Solar Power Tracker `_ and `Global Energy Monitor's Global Wind Power Tracker `_.", + "markdownDescription": "Add renewable capacities from [Global Energy Monitor's Global Solar Power Tracker ](https://globalenergymonitor.org/projects/global-solar-power-tracker/) and [Global Energy Monitor's Global Wind Power Tracker ](https://globalenergymonitor.org/projects/global-wind-power-tracker/).", + "type": "boolean" + }, + "year": { + "default": 2020, + "description": "Renewable capacities are based on existing capacities reported by IRENA (IRENASTAT) for the specified year.", + "type": "integer" + }, + "expansion_limit": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "boolean" + } + ], + "default": false, + "description": "Artificially limit maximum IRENA capacities to a factor. For example, an `expansion_limit: 1.1` means 110% of capacities. If false are chosen, the estimated renewable potentials determine by the workflow are used." + }, + "technology_mapping": { + "description": "Configuration for `electricity.estimate_renewable_capacities.technology_mapping` settings.", + "properties": { + "Offshore": { + "default": "offwind-ac", + "description": "PyPSA-Eur carrier that is considered for existing offshore wind technology (IRENA, GEM).", + "type": "string" + }, + "Onshore": { + "default": "onwind", + "description": "PyPSA-Eur carrier that is considered for existing onshore wind capacities (IRENA, GEM).", + "type": "string" + }, + "PV": { + "default": "solar", + "description": "PyPSA-Eur carrier that is considered for existing solar PV capacities (IRENA, GEM).", + "type": "string" + } + } + } + } + }, + "autarky": { + "description": "Configuration for `electricity.autarky` settings.", + "properties": { + "enable": { + "default": false, + "description": "Require each node to be autarkic by removing all lines and links.", + "type": "boolean" + }, + "by_country": { + "default": false, + "description": "Require each country to be autarkic by removing all cross-border lines and links. `electricity: autarky` must be enabled.", + "type": "boolean" + } + } + }, + "transmission_limit": { + "default": "vopt", + "description": "Limit on transmission expansion. The first part can be `v` (for setting a limit on line volume) or `c` (for setting a limit on line cost). The second part can be `opt` or a float bigger than one (e.g. 1.25). If `opt` is chosen line expansion is optimised according to its capital cost (where the choice `v` only considers overhead costs for HVDC transmission lines, while `c` uses more accurate costs distinguishing between overhead and underwater sections and including inverter pairs). The setting `v1.25` will limit the total volume of line expansion to 25% of currently installed capacities weighted by individual line lengths. The setting `c1.25` will allow to build a transmission network that costs no more than 25 % more than the current system.", + "type": "string" + } + } + }, + "EnableConfig": { + "description": "Configuration for `enable` settings.", + "properties": { + "drop_leap_day": { + "default": true, + "description": "Switch to drop February 29 from all time-dependent data in leap years.", + "type": "boolean" + } + } + }, + "EnergyConfig": { + "description": "Configuration for `energy` settings.", + "properties": { + "energy_totals_year": { + "default": 2019, + "description": "The year for the sector energy use. The year must be available in the Eurostat report.", + "type": "integer" + }, + "base_emissions_year": { + "default": 1990, + "description": "The base year for the sector emissions. See `European Environment Agency (EEA) `_.", + "markdownDescription": "The base year for the sector emissions. See [European Environment Agency (EEA) ](https://www.eea.europa.eu/data-and-maps/data/national-emissions-reported-to-the-unfccc-and-to-the-eu-greenhouse-gas-monitoring-mechanism-16).", + "type": "integer" + }, + "emissions": { + "default": "CO2", + "description": "Specify which sectoral emissions are taken into account. Data derived from EEA. Currently only CO2 is implemented.", + "type": "string" + } + } + }, + "ExistingCapacitiesConfig": { + "description": "Configuration for `existing_capacities` settings.", + "properties": { + "grouping_years_power": { + "description": "Intervals to group existing capacities for power.", + "items": { + "type": "integer" + }, + "type": "array" + }, + "grouping_years_heat": { + "description": "Intervals to group existing capacities for heat.", + "items": { + "type": "integer" + }, + "type": "array" + }, + "threshold_capacity": { + "default": 10, + "description": "Capacities (MW) of generators and links below threshold are removed during add_existing_capacities.", + "type": "number" + }, + "default_heating_lifetime": { + "default": 20, + "description": "Default lifetime for heating technologies (years).", + "type": "integer" + }, + "conventional_carriers": { + "description": "List of conventional power plants to include in the sectoral network.", + "items": { + "type": "string" + }, + "type": "array" + } + } + }, + "ForesightConfig": { + "default": "overnight", + "description": "Configuration for `foresight` settings.", + "enum": [ + "overnight", + "myopic", + "perfect" + ], + "type": "string" + }, + "IndustryConfig": { + "description": "Configuration for `industry` settings.", + "properties": { + "St_primary_fraction": { + "additionalProperties": { + "type": "number" + }, + "description": "The fraction of steel produced via primary route versus secondary route (scrap+EAF). Current fraction is 0.6.", + "type": "object" + }, + "DRI_fraction": { + "additionalProperties": { + "type": "number" + }, + "description": "The fraction of the primary route DRI + EAF.", + "type": "object" + }, + "H2_DRI": { + "default": 1.7, + "description": "The hydrogen consumption in Direct Reduced Iron (DRI) Mwh_H2 LHV/ton_Steel from 51kgH2/tSt in `Vogl et al (2018) `_.", + "markdownDescription": "The hydrogen consumption in Direct Reduced Iron (DRI) Mwh_H2 LHV/ton_Steel from 51kgH2/tSt in [Vogl et al (2018) ](https://doi.org/10.1016/j.jclepro.2018.08.279).", + "type": "number" + }, + "elec_DRI": { + "default": 0.322, + "description": "The electricity consumed in Direct Reduced Iron (DRI) shaft. From `HYBRIT brochure `_.", + "markdownDescription": "The electricity consumed in Direct Reduced Iron (DRI) shaft. From [HYBRIT brochure ](https://ssabwebsitecdn.azureedge.net/-/media/hybrit/files/hybrit_brochure.pdf).", + "type": "number" + }, + "Al_primary_fraction": { + "additionalProperties": { + "type": "number" + }, + "description": "The fraction of aluminium produced via the primary route versus scrap. Current fraction is 0.4.", + "type": "object" + }, + "MWh_NH3_per_tNH3": { + "default": 5.166, + "description": "The energy amount per ton of ammonia (LHV).", + "type": "number" + }, + "MWh_CH4_per_tNH3_SMR": { + "default": 10.8, + "description": "The energy amount of methane needed to produce a ton of ammonia using steam methane reforming (SMR). Value derived from 2012's demand from `Center for European Policy Studies (2008) `_.", + "markdownDescription": "The energy amount of methane needed to produce a ton of ammonia using steam methane reforming (SMR). Value derived from 2012's demand from [Center for European Policy Studies (2008) ](https://ec.europa.eu/docsroom/documents/4165/attachments/1/translations/en/renditions/pdf).", + "type": "number" + }, + "MWh_elec_per_tNH3_SMR": { + "default": 0.7, + "description": "The energy amount of electricity needed to produce a ton of ammonia using steam methane reforming (SMR). same source, assuming 94-6% split methane-elec of total energy demand 11.5 MWh/tNH3.", + "type": "number" + }, + "MWh_H2_per_tNH3_electrolysis": { + "default": 5.93, + "description": "The energy amount of hydrogen needed to produce a ton of ammonia using Haber\u2013Bosch process. From `Wang et al (2018) `_, Base value assumed around 0.197 tH2/tHN3 (>3/17 since some H2 lost and used for energy).", + "markdownDescription": "The energy amount of hydrogen needed to produce a ton of ammonia using Haber\u2013Bosch process. From [Wang et al (2018) ](https://doi.org/10.1016/j.joule.2018.04.017), Base value assumed around 0.197 tH2/tHN3 (>3/17 since some H2 lost and used for energy).", + "type": "number" + }, + "MWh_elec_per_tNH3_electrolysis": { + "default": 0.2473, + "description": "The energy amount of electricity needed to produce a ton of ammonia using Haber\u2013Bosch process. From `Wang et al (2018) `_, Table 13 (air separation and HB).", + "markdownDescription": "The energy amount of electricity needed to produce a ton of ammonia using Haber\u2013Bosch process. From [Wang et al (2018) ](https://doi.org/10.1016/j.joule.2018.04.017), Table 13 (air separation and HB).", + "type": "number" + }, + "MWh_NH3_per_MWh_H2_cracker": { + "default": 1.46, + "description": "The energy amount of amonia needed to produce an energy amount hydrogen using ammonia cracker.", + "type": "number" + }, + "NH3_process_emissions": { + "default": 24.5, + "description": "The emission of ammonia production from steam methane reforming (SMR). From UNFCCC for 2015 for EU28.", + "type": "number" + }, + "petrochemical_process_emissions": { + "default": 25.5, + "description": "The emission of petrochemical production. From UNFCCC for 2015 for EU28.", + "type": "number" + }, + "HVC_primary_fraction": { + "additionalProperties": { + "type": "number" + }, + "description": "The fraction of high value chemicals (HVC) produced via primary route.", + "type": "object" + }, + "HVC_mechanical_recycling_fraction": { + "additionalProperties": { + "type": "number" + }, + "description": "The fraction of high value chemicals (HVC) produced using mechanical recycling.", + "type": "object" + }, + "HVC_chemical_recycling_fraction": { + "additionalProperties": { + "type": "number" + }, + "description": "The fraction of high value chemicals (HVC) produced using chemical recycling.", + "type": "object" + }, + "HVC_environment_sequestration_fraction": { + "default": 0.0, + "description": "The fraction of high value chemicals (HVC) put into landfill resulting in additional carbon sequestration. The default value is 0.", + "type": "number" + }, + "waste_to_energy": { + "default": false, + "description": "Switch to enable expansion of waste to energy CHPs for conversion of plastics. Default is false.", + "type": "boolean" + }, + "waste_to_energy_cc": { + "default": false, + "description": "Switch to enable expansion of waste to energy CHPs for conversion of plastics with carbon capture. Default is false.", + "type": "boolean" + }, + "sector_ratios_fraction_future": { + "additionalProperties": { + "type": "number" + }, + "description": "The fraction of total progress in fuel and process switching achieved in the industry sector.", + "type": "object" + }, + "basic_chemicals_without_NH3_production_today": { + "default": 69.0, + "description": "The amount of basic chemicals produced without ammonia (= 86 Mtethylene-equiv - 17 MtNH3).", + "type": "number" + }, + "HVC_production_today": { + "default": 52.0, + "description": "The amount of high value chemicals (HVC) produced. This includes ethylene, propylene and BTX. From `DECHEMA (2017) `_, Figure 16, page 107.", + "markdownDescription": "The amount of high value chemicals (HVC) produced. This includes ethylene, propylene and BTX. From [DECHEMA (2017) ](https://dechema.de/dechema_media/Downloads/Positionspapiere/Technology_study_Low_carbon_energy_and_feedstock_for_the_European_chemical_industry-p-20002750.pdf), Figure 16, page 107.", + "type": "number" + }, + "MWh_elec_per_tHVC_mechanical_recycling": { + "default": 0.547, + "description": "The energy amount of electricity needed to produce a ton of high value chemical (HVC) using mechanical recycling. From SI of `Meys et al (2020) `_, Table S5, for HDPE, PP, PS, PET. LDPE would be 0.756.", + "markdownDescription": "The energy amount of electricity needed to produce a ton of high value chemical (HVC) using mechanical recycling. From SI of [Meys et al (2020) ](https://doi.org/10.1016/j.resconrec.2020.105010), Table S5, for HDPE, PP, PS, PET. LDPE would be 0.756.", + "type": "number" + }, + "MWh_elec_per_tHVC_chemical_recycling": { + "default": 6.9, + "description": "The energy amount of electricity needed to produce a ton of high value chemical (HVC) using chemical recycling. The default value is based on pyrolysis and electric steam cracking. From `Material Economics (2019) `_, page 125.", + "markdownDescription": "The energy amount of electricity needed to produce a ton of high value chemical (HVC) using chemical recycling. The default value is based on pyrolysis and electric steam cracking. From [Material Economics (2019) ](https://materialeconomics.com/latest-updates/industrial-transformation-2050), page 125.", + "type": "number" + }, + "chlorine_production_today": { + "default": 9.58, + "description": "The amount of chlorine produced. From `DECHEMA (2017) `_, Table 7, page 43.", + "markdownDescription": "The amount of chlorine produced. From [DECHEMA (2017) ](https://dechema.de/dechema_media/Downloads/Positionspapiere/Technology_study_Low_carbon_energy_and_feedstock_for_the_European_chemical_industry-p-20002750.pdf), Table 7, page 43.", + "type": "number" + }, + "MWh_elec_per_tCl": { + "default": 3.6, + "description": "The energy amount of electricity needed to produce a ton of chlorine. From `DECHEMA (2017) `_, Table 6 page 43.", + "markdownDescription": "The energy amount of electricity needed to produce a ton of chlorine. From [DECHEMA (2017) ](https://dechema.de/dechema_media/Downloads/Positionspapiere/Technology_study_Low_carbon_energy_and_feedstock_for_the_European_chemical_industry-p-20002750.pdf), Table 6 page 43.", + "type": "number" + }, + "MWh_H2_per_tCl": { + "default": -0.9372, + "description": "The energy amount of hydrogen needed to produce a ton of chlorine. The value is negative since hydrogen produced in chloralkali process. From `DECHEMA (2017) `_, page 43.", + "markdownDescription": "The energy amount of hydrogen needed to produce a ton of chlorine. The value is negative since hydrogen produced in chloralkali process. From [DECHEMA (2017) ](https://dechema.de/dechema_media/Downloads/Positionspapiere/Technology_study_Low_carbon_energy_and_feedstock_for_the_European_chemical_industry-p-20002750.pdf), page 43.", + "type": "number" + }, + "methanol_production_today": { + "default": 1.5, + "description": "The amount of methanol produced. From `DECHEMA (2017) `_, page 62.", + "markdownDescription": "The amount of methanol produced. From [DECHEMA (2017) ](https://dechema.de/dechema_media/Downloads/Positionspapiere/Technology_study_Low_carbon_energy_and_feedstock_for_the_European_chemical_industry-p-20002750.pdf), page 62.", + "type": "number" + }, + "MWh_elec_per_tMeOH": { + "default": 0.167, + "description": "The energy amount of electricity needed to produce a ton of methanol. From `DECHEMA (2017) `_, Table 14, page 65.", + "markdownDescription": "The energy amount of electricity needed to produce a ton of methanol. From [DECHEMA (2017) ](https://dechema.de/dechema_media/Downloads/Positionspapiere/Technology_study_Low_carbon_energy_and_feedstock_for_the_European_chemical_industry-p-20002750.pdf), Table 14, page 65.", + "type": "number" + }, + "MWh_CH4_per_tMeOH": { + "default": 10.25, + "description": "The energy amount of methane needed to produce a ton of methanol. From `DECHEMA (2017) `_, Table 14, page 65.", + "markdownDescription": "The energy amount of methane needed to produce a ton of methanol. From [DECHEMA (2017) ](https://dechema.de/dechema_media/Downloads/Positionspapiere/Technology_study_Low_carbon_energy_and_feedstock_for_the_European_chemical_industry-p-20002750.pdf), Table 14, page 65.", + "type": "number" + }, + "MWh_MeOH_per_tMeOH": { + "default": 5.528, + "description": "The energy amount per ton of methanol (LHV). From `DECHEMA (2017) `_, page 74.", + "markdownDescription": "The energy amount per ton of methanol (LHV). From [DECHEMA (2017) ](https://dechema.de/dechema_media/Downloads/Positionspapiere/Technology_study_Low_carbon_energy_and_feedstock_for_the_European_chemical_industry-p-20002750.pdf), page 74.", + "type": "number" + }, + "hotmaps_locate_missing": { + "default": false, + "description": "Locate industrial sites without valid locations based on city and countries.", + "type": "boolean" + }, + "reference_year": { + "default": 2019, + "description": "The year used as the baseline for industrial energy demand and production. Data extracted from `JRC-IDEES 2015 `_.", + "markdownDescription": "The year used as the baseline for industrial energy demand and production. Data extracted from [JRC-IDEES 2015 ](https://data.jrc.ec.europa.eu/dataset/jrc-10110-10001).", + "type": "integer" + }, + "oil_refining_emissions": { + "default": 0.013, + "description": "The emissions from oil fuel processing (e.g. oil in petrochemical refinieries). The default value of 0.013 tCO2/MWh is based on DE statistics for 2019; the EU value is very similar.", + "type": "number" + } + } + }, + "LinesConfig": { + "description": "Configuration for `lines` settings.", + "properties": { + "types": { + "additionalProperties": { + "type": "string" + }, + "description": "Specifies line types to assume for the different voltage levels of the ENTSO-E grid extraction. Should normally handle voltage levels 220, 300, and 380 kV.", + "type": "object" + }, + "s_max_pu": { + "default": 0.7, + "description": "Correction factor for line capacities (`s_nom`) to approximate N-1 security and reserve capacity for reactive power flows.", + "type": "number" + }, + "s_nom_max": { + "default": null, + "description": "Global upper limit for the maximum capacity of each extendable line (MW).", + "type": "number" + }, + "max_extension": { + "default": 20000, + "description": "Upper limit for the extended capacity of each extendable line (MW).", + "type": "number" + }, + "length_factor": { + "default": 1.25, + "description": "Correction factor to account for the fact that buses are *not* connected by lines through air-line distance.", + "type": "number" + }, + "reconnect_crimea": { + "default": true, + "description": "Whether to reconnect Crimea to the Ukrainian grid.", + "type": "boolean" + }, + "under_construction": { + "default": "keep", + "description": "Specifies how to handle lines which are currently under construction.", + "enum": [ + "zero", + "remove", + "keep" + ], + "type": "string" + }, + "dynamic_line_rating": { + "description": "Configuration for `lines.dynamic_line_rating` settings.", + "properties": { + "activate": { + "default": false, + "description": "Whether to take dynamic line rating into account.", + "type": "boolean" + }, + "cutout": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": "default", + "description": "Specifies the weather data cutout file(s) to use." + }, + "correction_factor": { + "default": 0.95, + "description": "Factor to compensate for overestimation of wind speeds in hourly averaged wind data.", + "type": "number" + }, + "max_voltage_difference": { + "anyOf": [ + { + "type": "number" + }, + { + "const": false, + "type": "boolean" + } + ], + "default": false, + "description": "Maximum voltage angle difference in degrees or 'false' to disable." + }, + "max_line_rating": { + "anyOf": [ + { + "type": "number" + }, + { + "const": false, + "type": "boolean" + } + ], + "default": false, + "description": "Maximum line rating relative to nominal capacity without DLR, e.g. 1.3 or 'false' to disable." + } + } + } + } + }, + "LinksConfig": { + "description": "Configuration for `links` settings.", + "properties": { + "p_max_pu": { + "default": 1.0, + "description": "Correction factor for link capacities `p_nom`.", + "type": "number" + }, + "p_min_pu": { + "default": -1.0, + "description": "Correction factor for link capacities `p_nom`.", + "type": "number" + }, + "p_nom_max": { + "default": null, + "description": "Global upper limit for the maximum capacity of each extendable DC link (MW).", + "type": "number" + }, + "max_extension": { + "default": 30000, + "description": "Upper limit for the extended capacity of each extendable DC link (MW).", + "type": "number" + }, + "length_factor": { + "default": 1.25, + "description": "Correction factor to account for the fact that buses are *not* connected by links through air-line distance.", + "type": "number" + }, + "under_construction": { + "default": "keep", + "description": "Specifies how to handle lines which are currently under construction.", + "enum": [ + "zero", + "remove", + "keep" + ], + "type": "string" + } + } + }, + "LoadConfig": { + "description": "Configuration for `load` settings.", + "properties": { + "fill_gaps": { + "description": "Configuration for `load.fill_gaps` settings.", + "properties": { + "enable": { + "default": true, + "description": "Whether to fill gaps using interpolation for small gaps and time shift for large gaps.", + "type": "boolean" + }, + "interpolate_limit": { + "default": 3, + "description": "Maximum gap size (consecutive nans) which interpolated linearly.", + "type": "integer" + }, + "time_shift_for_large_gaps": { + "default": "1w", + "description": "Periods which are used for copying time-slices in order to fill large gaps of nans. Have to be valid `pandas` period strings.", + "type": "string" + } + } + }, + "manual_adjustments": { + "default": true, + "description": "Whether to adjust the load data manually according to the function in `manual_adjustment`.", + "type": "boolean" + }, + "scaling_factor": { + "default": 1.0, + "description": "Global correction factor for the load time series.", + "type": "number" + }, + "fixed_year": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "boolean" + } + ], + "default": false, + "description": "To specify a fixed year for the load time series that deviates from the snapshots' year." + }, + "supplement_synthetic": { + "default": true, + "description": "Whether to supplement missing data for selected time period should be supplemented by synthetic data from `Zenodo `_.", + "markdownDescription": "Whether to supplement missing data for selected time period should be supplemented by synthetic data from [Zenodo ](https://zenodo.org/records/10820928).", + "type": "boolean" + }, + "distribution_key": { + "description": "Configuration for `load.distribution_key` settings.", + "properties": { + "gdp": { + "default": 0.6, + "description": "Weighting factor for the GDP data in the distribution key.", + "type": "number" + }, + "population": { + "default": 0.4, + "description": "Weighting factor for the population data in the distribution key.", + "type": "number" + } + } + } + } + }, + "LoggingConfig": { + "description": "Configuration for top level `logging` settings.", + "properties": { + "level": { + "default": "INFO", + "description": "Restrict console outputs to all infos, warning or errors only", + "enum": [ + "DEBUG", + "INFO", + "WARNING", + "ERROR", + "CRITICAL" + ], + "type": "string" + }, + "format": { + "default": "%(levelname)s:%(name)s:%(message)s", + "description": "Custom format for log messages. See `LogRecord `_ attributes.", + "markdownDescription": "Custom format for log messages. See [LogRecord ](https://docs.python.org/3/library/logging.html#logging.LogRecord) attributes.", + "type": "string" + } + } + }, + "OverpassApiConfig": { + "description": "Configuration for `overpass_api` settings.", + "properties": { + "url": { + "default": "https://overpass-api.de/api/interpreter", + "description": "Overpass API endpoint URL. See `Overpass API Wiki `_ for available public instances.", + "markdownDescription": "Overpass API endpoint URL. See [Overpass API Wiki ](https://wiki.openstreetmap.org/wiki/Overpass_API#Public_Overpass_API_instances) for available public instances.", + "type": "string" + }, + "max_tries": { + "default": 5, + "description": "Maximum retry attempts for Overpass API requests. Please be respectful to the Overpass API fair use policy of the individual instances.", + "type": "integer" + }, + "timeout": { + "default": 600, + "description": "Timeout in seconds for Overpass API requests.", + "type": "integer" + }, + "user_agent": { + "description": "Configuration for `overpass_api.user_agent` settings.", + "properties": { + "project_name": { + "default": "PyPSA-Eur", + "description": "Project name used to identify the user agent of the Overpass API requests.", + "type": "string" + }, + "email": { + "default": "contact@pypsa.org", + "description": "Contact email address for the project using the Overpass API.", + "type": "string" + }, + "website": { + "default": "https://github.com/PyPSA/pypsa-eur", + "description": "Website URL for the project using the Overpass API.", + "type": "string" + } + } + } + } + }, + "PypsaEurConfig": { + "description": "Configuration for `pypsa_eur` settings.", + "properties": { + "Bus": { + "description": "Bus carriers to keep from PyPSA-Eur.", + "items": { + "type": "string" + }, + "type": "array" + }, + "Link": { + "description": "Link carriers to keep from PyPSA-Eur.", + "items": { + "type": "string" + }, + "type": "array" + }, + "Generator": { + "description": "Generator carriers to keep from PyPSA-Eur.", + "items": { + "type": "string" + }, + "type": "array" + }, + "StorageUnit": { + "description": "StorageUnit carriers to keep from PyPSA-Eur.", + "items": { + "type": "string" + }, + "type": "array" + }, + "Store": { + "description": "Store carriers to keep from PyPSA-Eur.", + "items": { + "type": "string" + }, + "type": "array" + } + } + }, + "RemoteConfig": { + "description": "Configuration for top level `remote` settings.", + "properties": { + "ssh": { + "default": "", + "description": "Optionally specify the SSH of a remote cluster to be synchronized.", + "type": "string" + }, + "path": { + "default": "", + "description": "Optionally specify the file path within the remote cluster to be synchronized.", + "type": "string" + } + } + }, + "RenewableConfig": { + "description": "Configuration for `renewable` settings.", + "properties": { + "onwind": { + "description": "Configuration for onshore wind.", + "properties": { + "cutout": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": "default", + "description": "Specifies the weather data cutout file(s) to use." + }, + "resource": { + "description": "Configuration for wind resource settings.", + "properties": { + "method": { + "default": "wind", + "description": "A superordinate technology type.", + "type": "string" + }, + "turbine": { + "anyOf": [ + { + "type": "string" + }, + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + } + ], + "description": "Specifies the turbine type and its characteristic power curve. Can be a string or a dictionary with years as keys which denote the year another turbine model becomes available." + }, + "smooth": { + "default": false, + "description": "Switch to apply a gaussian kernel density smoothing to the power curve.", + "type": "boolean" + }, + "add_cutout_windspeed": { + "default": true, + "description": "Whether to add cutout windspeed data.", + "type": "boolean" + } + }, + "required": [ + "turbine" + ] + }, + "resource_classes": { + "default": 1, + "description": "Number of resource classes per clustered region.", + "type": "integer" + }, + "capacity_per_sqkm": { + "default": 3, + "description": "Allowable density of wind turbine placement.", + "type": "number" + }, + "correction_factor": { + "default": 1.0, + "description": "Correction factor for capacity factor time series.", + "type": "number" + }, + "corine": { + "description": "Configuration for CORINE land cover settings.", + "properties": { + "grid_codes": { + "description": "Specifies areas according to CORINE Land Cover codes which are generally eligible for wind turbine placement.", + "items": { + "type": "integer" + }, + "type": "array" + }, + "distance": { + "default": 1000, + "description": "Distance in meters to keep from areas specified in `distance_grid_codes`.", + "type": "number" + }, + "distance_grid_codes": { + "description": "Specifies areas according to CORINE Land Cover codes to which wind turbines must maintain a distance specified in the setting `distance`.", + "items": { + "type": "integer" + }, + "type": "array" + } + }, + "required": [ + "grid_codes" + ] + }, + "luisa": { + "anyOf": [ + { + "type": "boolean" + }, + { + "additionalProperties": true, + "type": "object" + } + ], + "default": false, + "description": "LUISA land cover configuration." + }, + "natura": { + "default": true, + "description": "Switch to exclude `Natura 2000 `_ natural protection areas. Area is excluded if `true`.", + "markdownDescription": "Switch to exclude [Natura 2000 ](https://en.wikipedia.org/wiki/Natura_2000) natural protection areas. Area is excluded if `true`.", + "type": "boolean" + }, + "excluder_resolution": { + "default": 100, + "description": "Resolution in meters on which to perform geographical eligibility analysis.", + "type": "number" + }, + "clip_p_max_pu": { + "default": 0.01, + "description": "To avoid too small values in the renewables` per-unit availability time series values below this threshold are set to zero.", + "type": "number" + } + } + }, + "offwind-ac": { + "description": "Configuration for offshore wind.", + "properties": { + "cutout": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": "default", + "description": "Specifies the weather data cutout file(s) to use." + }, + "resource": { + "description": "Configuration for wind resource settings.", + "properties": { + "method": { + "default": "wind", + "description": "A superordinate technology type.", + "type": "string" + }, + "turbine": { + "anyOf": [ + { + "type": "string" + }, + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + } + ], + "description": "Specifies the turbine type and its characteristic power curve. Can be a string or a dictionary with years as keys which denote the year another turbine model becomes available." + }, + "smooth": { + "default": false, + "description": "Switch to apply a gaussian kernel density smoothing to the power curve.", + "type": "boolean" + }, + "add_cutout_windspeed": { + "default": true, + "description": "Whether to add cutout windspeed data.", + "type": "boolean" + } + }, + "required": [ + "turbine" + ] + }, + "resource_classes": { + "default": 1, + "description": "Number of resource classes per clustered region.", + "type": "integer" + }, + "capacity_per_sqkm": { + "default": 2, + "description": "Allowable density of wind turbine placement.", + "type": "number" + }, + "correction_factor": { + "default": 0.8855, + "description": "Correction factor for capacity factor time series.", + "type": "number" + }, + "corine": { + "description": "Specifies areas according to CORINE Land Cover codes which are generally eligible for AC-connected offshore wind turbine placement.", + "items": { + "type": "integer" + }, + "type": "array" + }, + "luisa": { + "anyOf": [ + { + "type": "boolean" + }, + { + "items": { + "type": "integer" + }, + "type": "array" + } + ], + "default": false, + "description": "Specifies areas according to the LUISA Base Map codes which are generally eligible for AC-connected offshore wind turbine placement." + }, + "natura": { + "default": true, + "description": "Switch to exclude `Natura 2000 `_ natural protection areas. Area is excluded if `true`.", + "markdownDescription": "Switch to exclude [Natura 2000 ](https://en.wikipedia.org/wiki/Natura_2000) natural protection areas. Area is excluded if `true`.", + "type": "boolean" + }, + "ship_threshold": { + "default": 400, + "description": "Ship density threshold from which areas are excluded.", + "type": "number" + }, + "max_depth": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Maximum sea water depth in meters at which wind turbines can be built. Maritime areas with deeper waters are excluded in the process of calculating the AC-connected offshore wind potential." + }, + "min_depth": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Minimum water depth in meters." + }, + "max_shore_distance": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Maximum distance to the shore in meters above which wind turbines cannot be built. Such areas are excluded in the process of calculating the AC-connected offshore wind potential." + }, + "min_shore_distance": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Minimum distance to the shore in meters below which wind turbines cannot be built. Such areas close to the shore are excluded in the process of calculating the AC-connected offshore wind potential." + }, + "excluder_resolution": { + "default": 200, + "description": "Resolution in meters on which to perform geographical eligibility analysis.", + "type": "number" + }, + "clip_p_max_pu": { + "default": 0.01, + "description": "To avoid too small values in the renewables` per-unit availability time series values below this threshold are set to zero.", + "type": "number" + }, + "landfall_length": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "default": 20, + "description": "Fixed length of the cable connection that is onshorelandfall in km. If 'centroid', the length is calculated as the distance to centroid of the onshore bus." + } + } + }, + "offwind-dc": { + "description": "Configuration for offshore wind.", + "properties": { + "cutout": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": "default", + "description": "Specifies the weather data cutout file(s) to use." + }, + "resource": { + "description": "Configuration for wind resource settings.", + "properties": { + "method": { + "default": "wind", + "description": "A superordinate technology type.", + "type": "string" + }, + "turbine": { + "anyOf": [ + { + "type": "string" + }, + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + } + ], + "description": "Specifies the turbine type and its characteristic power curve. Can be a string or a dictionary with years as keys which denote the year another turbine model becomes available." + }, + "smooth": { + "default": false, + "description": "Switch to apply a gaussian kernel density smoothing to the power curve.", + "type": "boolean" + }, + "add_cutout_windspeed": { + "default": true, + "description": "Whether to add cutout windspeed data.", + "type": "boolean" + } + }, + "required": [ + "turbine" + ] + }, + "resource_classes": { + "default": 1, + "description": "Number of resource classes per clustered region.", + "type": "integer" + }, + "capacity_per_sqkm": { + "default": 2, + "description": "Allowable density of wind turbine placement.", + "type": "number" + }, + "correction_factor": { + "default": 0.8855, + "description": "Correction factor for capacity factor time series.", + "type": "number" + }, + "corine": { + "description": "Specifies areas according to CORINE Land Cover codes which are generally eligible for AC-connected offshore wind turbine placement.", + "items": { + "type": "integer" + }, + "type": "array" + }, + "luisa": { + "anyOf": [ + { + "type": "boolean" + }, + { + "items": { + "type": "integer" + }, + "type": "array" + } + ], + "default": false, + "description": "Specifies areas according to the LUISA Base Map codes which are generally eligible for AC-connected offshore wind turbine placement." + }, + "natura": { + "default": true, + "description": "Switch to exclude `Natura 2000 `_ natural protection areas. Area is excluded if `true`.", + "markdownDescription": "Switch to exclude [Natura 2000 ](https://en.wikipedia.org/wiki/Natura_2000) natural protection areas. Area is excluded if `true`.", + "type": "boolean" + }, + "ship_threshold": { + "default": 400, + "description": "Ship density threshold from which areas are excluded.", + "type": "number" + }, + "max_depth": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Maximum sea water depth in meters at which wind turbines can be built. Maritime areas with deeper waters are excluded in the process of calculating the AC-connected offshore wind potential." + }, + "min_depth": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Minimum water depth in meters." + }, + "max_shore_distance": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Maximum distance to the shore in meters above which wind turbines cannot be built. Such areas are excluded in the process of calculating the AC-connected offshore wind potential." + }, + "min_shore_distance": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Minimum distance to the shore in meters below which wind turbines cannot be built. Such areas close to the shore are excluded in the process of calculating the AC-connected offshore wind potential." + }, + "excluder_resolution": { + "default": 200, + "description": "Resolution in meters on which to perform geographical eligibility analysis.", + "type": "number" + }, + "clip_p_max_pu": { + "default": 0.01, + "description": "To avoid too small values in the renewables` per-unit availability time series values below this threshold are set to zero.", + "type": "number" + }, + "landfall_length": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "default": 20, + "description": "Fixed length of the cable connection that is onshorelandfall in km. If 'centroid', the length is calculated as the distance to centroid of the onshore bus." + } + } + }, + "offwind-float": { + "description": "Configuration for offshore wind.", + "properties": { + "cutout": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": "default", + "description": "Specifies the weather data cutout file(s) to use." + }, + "resource": { + "description": "Configuration for wind resource settings.", + "properties": { + "method": { + "default": "wind", + "description": "A superordinate technology type.", + "type": "string" + }, + "turbine": { + "anyOf": [ + { + "type": "string" + }, + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + } + ], + "description": "Specifies the turbine type and its characteristic power curve. Can be a string or a dictionary with years as keys which denote the year another turbine model becomes available." + }, + "smooth": { + "default": false, + "description": "Switch to apply a gaussian kernel density smoothing to the power curve.", + "type": "boolean" + }, + "add_cutout_windspeed": { + "default": true, + "description": "Whether to add cutout windspeed data.", + "type": "boolean" + } + }, + "required": [ + "turbine" + ] + }, + "resource_classes": { + "default": 1, + "description": "Number of resource classes per clustered region.", + "type": "integer" + }, + "capacity_per_sqkm": { + "default": 2, + "description": "Allowable density of wind turbine placement.", + "type": "number" + }, + "correction_factor": { + "default": 0.8855, + "description": "Correction factor for capacity factor time series.", + "type": "number" + }, + "corine": { + "description": "Specifies areas according to CORINE Land Cover codes which are generally eligible for AC-connected offshore wind turbine placement.", + "items": { + "type": "integer" + }, + "type": "array" + }, + "luisa": { + "anyOf": [ + { + "type": "boolean" + }, + { + "items": { + "type": "integer" + }, + "type": "array" + } + ], + "default": false, + "description": "Specifies areas according to the LUISA Base Map codes which are generally eligible for AC-connected offshore wind turbine placement." + }, + "natura": { + "default": true, + "description": "Switch to exclude `Natura 2000 `_ natural protection areas. Area is excluded if `true`.", + "markdownDescription": "Switch to exclude [Natura 2000 ](https://en.wikipedia.org/wiki/Natura_2000) natural protection areas. Area is excluded if `true`.", + "type": "boolean" + }, + "ship_threshold": { + "default": 400, + "description": "Ship density threshold from which areas are excluded.", + "type": "number" + }, + "max_depth": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Maximum sea water depth in meters at which wind turbines can be built. Maritime areas with deeper waters are excluded in the process of calculating the AC-connected offshore wind potential." + }, + "min_depth": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Minimum water depth in meters." + }, + "max_shore_distance": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Maximum distance to the shore in meters above which wind turbines cannot be built. Such areas are excluded in the process of calculating the AC-connected offshore wind potential." + }, + "min_shore_distance": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Minimum distance to the shore in meters below which wind turbines cannot be built. Such areas close to the shore are excluded in the process of calculating the AC-connected offshore wind potential." + }, + "excluder_resolution": { + "default": 200, + "description": "Resolution in meters on which to perform geographical eligibility analysis.", + "type": "number" + }, + "clip_p_max_pu": { + "default": 0.01, + "description": "To avoid too small values in the renewables` per-unit availability time series values below this threshold are set to zero.", + "type": "number" + }, + "landfall_length": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "default": 20, + "description": "Fixed length of the cable connection that is onshorelandfall in km. If 'centroid', the length is calculated as the distance to centroid of the onshore bus." + } + } + }, + "solar": { + "description": "Configuration for solar PV.", + "properties": { + "cutout": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": "default", + "description": "Specifies the weather data cutout file(s) to use." + }, + "resource": { + "description": "Configuration for solar resource settings.", + "properties": { + "method": { + "default": "pv", + "description": "A superordinate technology type.", + "type": "string" + }, + "panel": { + "anyOf": [ + { + "type": "string" + }, + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + } + ], + "default": "CSi", + "description": "Specifies the solar panel technology and its characteristic attributes. Can be a string or a dictionary with years as keys which denote the year another panel model becomes available." + }, + "orientation": { + "additionalProperties": { + "type": "number" + }, + "description": "Panel orientation with slope and azimuth.", + "type": "object" + }, + "tracking": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Tracking type (e.g., 'horizontal')." + } + } + }, + "resource_classes": { + "default": 1, + "description": "Number of resource classes per clustered region.", + "type": "integer" + }, + "capacity_per_sqkm": { + "default": 5.1, + "description": "Allowable density of solar panel placement.", + "type": "number" + }, + "correction_factor": { + "default": 1.0, + "description": "A correction factor for the capacity factor (availability) time series.", + "type": "number" + }, + "corine": { + "description": "Specifies areas according to CORINE Land Cover codes which are generally eligible for solar panel placement.", + "items": { + "type": "integer" + }, + "type": "array" + }, + "luisa": { + "anyOf": [ + { + "type": "boolean" + }, + { + "items": { + "type": "integer" + }, + "type": "array" + } + ], + "default": false, + "description": "Specifies areas according to the LUISA Base Map codes which are generally eligible for solar panel placement." + }, + "natura": { + "default": true, + "description": "Switch to exclude `Natura 2000 `_ natural protection areas. Area is excluded if `true`.", + "markdownDescription": "Switch to exclude [Natura 2000 ](https://en.wikipedia.org/wiki/Natura_2000) natural protection areas. Area is excluded if `true`.", + "type": "boolean" + }, + "excluder_resolution": { + "default": 100, + "description": "Resolution in meters on which to perform geographical eligibility analysis.", + "type": "number" + }, + "clip_p_max_pu": { + "default": 0.01, + "description": "To avoid too small values in the renewables` per-unit availability time series values below this threshold are set to zero.", + "type": "number" + } + } + }, + "solar-hsat": { + "description": "Configuration for solar PV.", + "properties": { + "cutout": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": "default", + "description": "Specifies the weather data cutout file(s) to use." + }, + "resource": { + "description": "Configuration for solar resource settings.", + "properties": { + "method": { + "default": "pv", + "description": "A superordinate technology type.", + "type": "string" + }, + "panel": { + "anyOf": [ + { + "type": "string" + }, + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + } + ], + "default": "CSi", + "description": "Specifies the solar panel technology and its characteristic attributes. Can be a string or a dictionary with years as keys which denote the year another panel model becomes available." + }, + "orientation": { + "additionalProperties": { + "type": "number" + }, + "description": "Panel orientation with slope and azimuth.", + "type": "object" + }, + "tracking": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Tracking type (e.g., 'horizontal')." + } + } + }, + "resource_classes": { + "default": 1, + "description": "Number of resource classes per clustered region.", + "type": "integer" + }, + "capacity_per_sqkm": { + "default": 5.1, + "description": "Allowable density of solar panel placement.", + "type": "number" + }, + "correction_factor": { + "default": 1.0, + "description": "A correction factor for the capacity factor (availability) time series.", + "type": "number" + }, + "corine": { + "description": "Specifies areas according to CORINE Land Cover codes which are generally eligible for solar panel placement.", + "items": { + "type": "integer" + }, + "type": "array" + }, + "luisa": { + "anyOf": [ + { + "type": "boolean" + }, + { + "items": { + "type": "integer" + }, + "type": "array" + } + ], + "default": false, + "description": "Specifies areas according to the LUISA Base Map codes which are generally eligible for solar panel placement." + }, + "natura": { + "default": true, + "description": "Switch to exclude `Natura 2000 `_ natural protection areas. Area is excluded if `true`.", + "markdownDescription": "Switch to exclude [Natura 2000 ](https://en.wikipedia.org/wiki/Natura_2000) natural protection areas. Area is excluded if `true`.", + "type": "boolean" + }, + "excluder_resolution": { + "default": 100, + "description": "Resolution in meters on which to perform geographical eligibility analysis.", + "type": "number" + }, + "clip_p_max_pu": { + "default": 0.01, + "description": "To avoid too small values in the renewables` per-unit availability time series values below this threshold are set to zero.", + "type": "number" + } + } + }, + "hydro": { + "description": "Configuration for hydropower.", + "properties": { + "cutout": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": "default", + "description": "Specifies the weather data cutout file(s) to use." + }, + "carriers": { + "description": "Specifies the types of hydro power plants to build per-unit availability time series for. 'ror' stands for run-of-river plants, 'PHS' represents pumped-hydro storage, and 'hydro' stands for hydroelectric dams.", + "items": { + "type": "string" + }, + "type": "array" + }, + "PHS_max_hours": { + "default": 6, + "description": "Maximum state of charge capacity of the pumped-hydro storage (PHS) in terms of hours at full output capacity `p_nom`. Cf. `PyPSA documentation `_.", + "markdownDescription": "Maximum state of charge capacity of the pumped-hydro storage (PHS) in terms of hours at full output capacity `p_nom`. Cf. [PyPSA documentation ](https://pypsa.readthedocs.io/en/latest/components.html#storage-unit).", + "type": "number" + }, + "hydro_max_hours": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "default": "energy_capacity_totals_by_country", + "description": "Maximum state of charge capacity of the pumped-hydro storage (PHS) in terms of hours at full output capacity `p_nom` or heuristically determined. Cf. `PyPSA documentation `_.", + "markdownDescription": "Maximum state of charge capacity of the pumped-hydro storage (PHS) in terms of hours at full output capacity `p_nom` or heuristically determined. Cf. [PyPSA documentation ](https://pypsa.readthedocs.io/en/latest/components.html#storage-unit)." + }, + "flatten_dispatch": { + "default": false, + "description": "Consider an upper limit for the hydro dispatch. The limit is given by the average capacity factor plus the buffer given in `flatten_dispatch_buffer`.", + "type": "boolean" + }, + "flatten_dispatch_buffer": { + "default": 0.2, + "description": "If `flatten_dispatch` is true, specify the value added above the average capacity factor.", + "type": "number" + }, + "clip_min_inflow": { + "default": 1.0, + "description": "To avoid too small values in the inflow time series, values below this threshold (MW) are set to zero.", + "type": "number" + }, + "eia_norm_year": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "integer" + } + ], + "default": false, + "description": "To specify a specific year by which hydro inflow is normed that deviates from the snapshots' year." + }, + "eia_correct_by_capacity": { + "default": false, + "description": "Correct EIA annual hydro generation data by installed capacity.", + "type": "boolean" + }, + "eia_approximate_missing": { + "default": false, + "description": "Approximate hydro generation data for years not included in EIA dataset through a regression based on annual runoff.", + "type": "boolean" + } + } + } + } + }, + "RunConfig": { + "description": "Configuration for top level `run` settings.", + "properties": { + "prefix": { + "default": "", + "description": "Prefix for the run name which is used as a top-layer directory name in the results and resources folders.", + "type": "string" + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": "", + "description": "Specify a name for your run. Results will be stored under this name. If ``scenario: enable:`` is set to ``true``, the name must contain a subset of scenario names defined in ``scenario: file:``. If the name is 'all', all defined scenarios will be run.", + "examples": [ + "my-pypsa-eur-run" + ] + }, + "scenarios": { + "description": "Configuration for `run.scenarios` level.", + "properties": { + "enable": { + "default": false, + "description": "Switch to select whether workflow should generate scenarios based on ``file``.", + "type": "boolean" + }, + "file": { + "default": "config/scenarios.yaml", + "description": "Path to the scenario yaml file. The scenario file contains config overrides for each scenario. In order to be taken account, ``run: scenarios`` has to be set to ``true`` and ``run: name`` has to be a subset of top level keys given in the scenario file. In order to automatically create a `scenario.yaml` file based on a combination of settings, alter and use the ``config/create_scenarios.py`` script in the ``config`` directory.", + "examples": [ + "config/scenarios.yaml" + ], + "type": "string" + } + } + }, + "disable_progressbar": { + "default": false, + "description": "Switch to select whether progressbar should be disabled.", + "type": "boolean" + }, + "shared_resources": { + "description": "Configuration for `run.shared_resources` level.", + "properties": { + "policy": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "string" + } + ], + "default": false, + "description": "Boolean switch to select whether resources should be shared across runs. If a string is passed, this is used as a subdirectory name for shared resources. If set to 'base', only resources before creating the elec.nc file are shared.", + "examples": [ + false, + "base" + ] + }, + "exclude": { + "description": "For the case shared_resources=base, specify additional files that should not be shared across runs.", + "items": { + "type": "string" + }, + "type": "array" + } + } + }, + "use_shadow_directory": { + "default": false, + "description": "Set to ``true`` (default) if snakemake shadow directories (``shallow``) should be used. Set to ``false`` if problems occur.", + "examples": [ + true + ], + "type": "boolean" + } + } + }, + "ScenarioConfig": { + "description": "Configuration for top level `scenario` settings.", + "properties": { + "clusters": { + "description": "List of ``{clusters}`` wildcards to run. Use 'adm' for administrative clustering mode, 'all' for all nodes.", + "items": { + "anyOf": [ + { + "type": "integer" + }, + { + "enum": [ + "adm", + "all" + ], + "type": "string" + } + ] + }, + "type": "array" + }, + "opts": { + "description": "List of ``{opts}`` wildcards to run.", + "items": { + "type": "string" + }, + "type": "array" + }, + "sector_opts": { + "description": "List of ``{sector_opts}`` wildcards to run.", + "items": { + "type": "string" + }, + "type": "array" + }, + "planning_horizons": { + "description": "List of ``{planning_horizon}`` wildcards to run.", + "items": { + "type": "integer" + }, + "type": "array" + } + } + }, + "SectorConfig": { + "description": "Configuration for `sector` settings.", + "properties": { + "transport": { + "default": true, + "description": "Flag to include transport sector.", + "type": "boolean" + }, + "heating": { + "default": true, + "description": "Flag to include heating sector.", + "type": "boolean" + }, + "biomass": { + "default": true, + "description": "Flag to include biomass sector.", + "type": "boolean" + }, + "industry": { + "default": true, + "description": "Flag to include industry sector.", + "type": "boolean" + }, + "shipping": { + "default": true, + "description": "Flag to include shipping sector.", + "type": "boolean" + }, + "aviation": { + "default": true, + "description": "Flag to include aviation sector.", + "type": "boolean" + }, + "agriculture": { + "default": true, + "description": "Flag to include agriculture sector.", + "type": "boolean" + }, + "fossil_fuels": { + "default": true, + "description": "Flag to include imports of fossil fuels.", + "type": "boolean" + }, + "district_heating": { + "description": "Configuration for `sector.district_heating` settings.", + "properties": { + "potential": { + "anyOf": [ + { + "type": "number" + }, + { + "additionalProperties": { + "type": "number" + }, + "type": "object" + } + ], + "default": 0.6, + "description": "Maximum fraction of urban demand which can be supplied by district heating. If given as dictionary, specify one value per country modeled or provide a default value with key `default` to fill values for all unspecified countries." + }, + "progress": { + "additionalProperties": { + "type": "number" + }, + "description": "Increase of today's district heating demand to potential maximum district heating share. Progress = 0 means today's district heating share. Progress = 1 means maximum fraction of urban demand is supplied by district heating.", + "type": "object" + }, + "district_heating_loss": { + "default": 0.15, + "description": "Share increase in district heat demand in urban central due to heat losses.", + "type": "number" + }, + "supply_temperature_approximation": { + "additionalProperties": true, + "description": "Supply temperature approximation settings.", + "type": "object" + }, + "ptes": { + "additionalProperties": true, + "description": "Pit thermal energy storage settings.", + "type": "object" + }, + "ates": { + "additionalProperties": true, + "description": "Aquifer thermal energy storage settings.", + "type": "object" + }, + "heat_source_cooling": { + "default": 6, + "description": "Cooling of heat source for heat pumps.", + "type": "number" + }, + "heat_pump_cop_approximation": { + "additionalProperties": true, + "description": "Heat pump COP approximation settings.", + "type": "object" + }, + "limited_heat_sources": { + "additionalProperties": true, + "description": "Dictionary with names of limited heat sources (not air). Must be `river_water` / `geothermal` or another heat source in `Manz et al. 2024 `_.", + "markdownDescription": "Dictionary with names of limited heat sources (not air). Must be `river_water` / `geothermal` or another heat source in [Manz et al. 2024 ](https://www.sciencedirect.com/science/article/pii/S0960148124001769).", + "type": "object" + }, + "direct_utilisation_heat_sources": { + "description": "List of heat sources for direct heat utilisation in district heating. Must be in the keys of `heat_utilisation_potentials` (e.g. `geothermal`).", + "items": { + "type": "string" + }, + "type": "array" + }, + "temperature_limited_stores": { + "description": "List of names for stores used as limited heat sources.", + "items": { + "type": "string" + }, + "type": "array" + }, + "dh_areas": { + "additionalProperties": true, + "description": "District heating areas settings.", + "type": "object" + } + } + }, + "heat_pump_sources": { + "additionalProperties": { + "items": { + "type": "string" + }, + "type": "array" + }, + "description": "Heat pump sources by area.", + "type": "object" + }, + "residential_heat": { + "description": "Configuration for `sector.residential_heat` settings.", + "properties": { + "dsm": { + "description": "Configuration for `sector.residential_heat.dsm` settings.", + "properties": { + "enable": { + "default": false, + "description": "Enable residential heat demand-side management that allows heating systems to provide flexibility by shifting demand within configurable time periods. Models building thermal mass as energy storage.", + "type": "boolean" + }, + "direction": { + "description": "'overheat-undercool' means both pre-heating and delayed heating are allowed. 'overheat' allows only pre-heating where buildings are heated up above target temperature and then allowed to cool down, while 'undercool' allows only delayed heating where buildings can cool below target temperature and then be heated up again.", + "items": { + "type": "string" + }, + "type": "array" + }, + "restriction_value": { + "additionalProperties": { + "type": "number" + }, + "description": "Maximum state of charge (as fraction) for heat flexibility storage representing available thermal buffer capacity in buildings. Set to 0 for no flexibility or to 1.0 to assume that the entire heating demand can contribute to flexibility.", + "type": "object" + }, + "restriction_time": { + "description": "Checkpoint hours (0-23) at which heat flexibility storage must return to baseline state of charge, i.e. the residence surplus or missing heat be balanced. Time is the local time for each country and bus. Default: [10, 22] creates 12-hour periods with checkpoints at 10am and 10pm.", + "items": { + "type": "integer" + }, + "type": "array" + } + } + } + } + }, + "cluster_heat_buses": { + "default": true, + "description": "Cluster residential and service heat buses in `prepare_sector_network.py `_ to one to save memory.", + "markdownDescription": "Cluster residential and service heat buses in [prepare_sector_network.py ](https://github.com/PyPSA/pypsa-eur-sec/blob/master/scripts/prepare_sector_network.py) to one to save memory.", + "type": "boolean" + }, + "heat_demand_cutout": { + "default": "default", + "description": "Heat demand cutout.", + "type": "string" + }, + "bev_dsm_restriction_value": { + "default": 0.8, + "description": "Adds a lower state of charge (SOC) limit for battery electric vehicles (BEV) to manage its own energy demand (DSM). Located in `build_transport_demand.py `_. Set to 0 for no restriction on BEV DSM.", + "markdownDescription": "Adds a lower state of charge (SOC) limit for battery electric vehicles (BEV) to manage its own energy demand (DSM). Located in [build_transport_demand.py ](https://github.com/PyPSA/pypsa-eur-sec/blob/master/scripts/build_transport_demand.py). Set to 0 for no restriction on BEV DSM.", + "type": "number" + }, + "bev_dsm_restriction_time": { + "default": 7, + "description": "Time at which SOC of BEV has to be dsm_restriction_value.", + "type": "number" + }, + "transport_heating_deadband_upper": { + "default": 20.0, + "description": "The maximum temperature in the vehicle. At higher temperatures, the energy required for cooling in the vehicle increases.", + "type": "number" + }, + "transport_heating_deadband_lower": { + "default": 15.0, + "description": "The minimum temperature in the vehicle. At lower temperatures, the energy required for heating in the vehicle increases.", + "type": "number" + }, + "ICE_lower_degree_factor": { + "default": 0.375, + "description": "Share increase in energy demand in internal combustion engine (ICE) for each degree difference between the cold environment and the minimum temperature.", + "type": "number" + }, + "ICE_upper_degree_factor": { + "default": 1.6, + "description": "Share increase in energy demand in internal combustion engine (ICE) for each degree difference between the hot environment and the maximum temperature.", + "type": "number" + }, + "EV_lower_degree_factor": { + "default": 0.98, + "description": "Share increase in energy demand in electric vehicles (EV) for each degree difference between the cold environment and the minimum temperature.", + "type": "number" + }, + "EV_upper_degree_factor": { + "default": 0.63, + "description": "Share increase in energy demand in electric vehicles (EV) for each degree difference between the hot environment and the maximum temperature.", + "type": "number" + }, + "bev_dsm": { + "default": true, + "description": "Add the option for battery electric vehicles (BEV) to participate in demand-side management (DSM).", + "type": "boolean" + }, + "bev_dsm_availability": { + "default": 0.5, + "description": "The share for battery electric vehicles (BEV) that are able to do demand side management (DSM).", + "type": "number" + }, + "bev_energy": { + "default": 0.05, + "description": "The average size of battery electric vehicles (BEV) in MWh.", + "type": "number" + }, + "bev_charge_efficiency": { + "default": 0.9, + "description": "Battery electric vehicles (BEV) charge and discharge efficiency.", + "type": "number" + }, + "bev_charge_rate": { + "default": 0.011, + "description": "The power consumption for one electric vehicle (EV) in MWh. Value derived from 3-phase charger with 11 kW.", + "type": "number" + }, + "bev_avail_max": { + "default": 0.95, + "description": "The maximum share plugged-in availability for passenger electric vehicles.", + "type": "number" + }, + "bev_avail_mean": { + "default": 0.8, + "description": "The average share plugged-in availability for passenger electric vehicles.", + "type": "number" + }, + "v2g": { + "default": true, + "description": "Allows feed-in to grid from EV battery. This is only enabled if BEV demand-side management is enabled, and the share of vehicles participating is V2G is given by `bev_dsm_availability`.", + "type": "boolean" + }, + "land_transport_fuel_cell_share": { + "additionalProperties": { + "type": "number" + }, + "description": "The share of vehicles that uses fuel cells in a given year.", + "type": "object" + }, + "land_transport_electric_share": { + "additionalProperties": { + "type": "number" + }, + "description": "The share of vehicles that uses electric vehicles (EV) in a given year.", + "type": "object" + }, + "land_transport_ice_share": { + "additionalProperties": { + "type": "number" + }, + "description": "The share of vehicles that uses internal combustion engines (ICE) in a given year. What is not EV or FCEV is oil-fuelled ICE.", + "type": "object" + }, + "transport_electric_efficiency": { + "default": 53.19, + "description": "The conversion efficiencies of electric vehicles in transport.", + "type": "number" + }, + "transport_fuel_cell_efficiency": { + "default": 30.003, + "description": "The H2 conversion efficiencies of fuel cells in transport.", + "type": "number" + }, + "transport_ice_efficiency": { + "default": 16.0712, + "description": "The oil conversion efficiencies of internal combustion engine (ICE) in transport.", + "type": "number" + }, + "agriculture_machinery_electric_share": { + "default": 0.5, + "description": "The share for agricultural machinery that uses electricity.", + "type": "number" + }, + "agriculture_machinery_oil_share": { + "default": 0.5, + "description": "The share for agricultural machinery that uses oil.", + "type": "number" + }, + "agriculture_machinery_fuel_efficiency": { + "default": 0.7, + "description": "The efficiency of electric-powered machinery in the conversion of electricity to meet agricultural needs.", + "type": "number" + }, + "agriculture_machinery_electric_efficiency": { + "default": 0.3, + "description": "The efficiency of oil-powered machinery in the conversion of oil to meet agricultural needs.", + "type": "number" + }, + "MWh_MeOH_per_MWh_H2": { + "default": 0.8787, + "description": "The energy amount of the produced methanol per energy amount of hydrogen. From `DECHEMA (2017) `_, page 64.", + "markdownDescription": "The energy amount of the produced methanol per energy amount of hydrogen. From [DECHEMA (2017) ](https://dechema.de/dechema_media/Downloads/Positionspapiere/Technology_study_Low_carbon_energy_and_feedstock_for_the_European_chemical_industry-p-20002750.pdf), page 64.", + "type": "number" + }, + "MWh_MeOH_per_tCO2": { + "default": 4.0321, + "description": "The energy amount of the produced methanol per ton of CO2. From `DECHEMA (2017) `_, page 66.", + "markdownDescription": "The energy amount of the produced methanol per ton of CO2. From [DECHEMA (2017) ](https://dechema.de/dechema_media/Downloads/Positionspapiere/Technology_study_Low_carbon_energy_and_feedstock_for_the_European_chemical_industry-p-20002750.pdf), page 66.", + "type": "number" + }, + "MWh_MeOH_per_MWh_e": { + "default": 3.6907, + "description": "The energy amount of the produced methanol per energy amount of electricity. From `DECHEMA (2017) `_, page 64.", + "markdownDescription": "The energy amount of the produced methanol per energy amount of electricity. From [DECHEMA (2017) ](https://dechema.de/dechema_media/Downloads/Positionspapiere/Technology_study_Low_carbon_energy_and_feedstock_for_the_European_chemical_industry-p-20002750.pdf), page 64.", + "type": "number" + }, + "shipping_hydrogen_liquefaction": { + "default": false, + "description": "Whether to include liquefaction costs for hydrogen demand in shipping.", + "type": "boolean" + }, + "shipping_hydrogen_share": { + "additionalProperties": { + "type": "number" + }, + "description": "The share of ships powered by hydrogen in a given year.", + "type": "object" + }, + "shipping_methanol_share": { + "additionalProperties": { + "type": "number" + }, + "description": "The share of ships powered by methanol in a given year.", + "type": "object" + }, + "shipping_oil_share": { + "additionalProperties": { + "type": "number" + }, + "description": "The share of ships powered by oil in a given year.", + "type": "object" + }, + "shipping_methanol_efficiency": { + "default": 0.46, + "description": "The efficiency of methanol-powered ships in the conversion of methanol to meet shipping needs (propulsion). The efficiency increase from oil can be 10-15% higher according to the `IEA `_.", + "markdownDescription": "The efficiency of methanol-powered ships in the conversion of methanol to meet shipping needs (propulsion). The efficiency increase from oil can be 10-15% higher according to the [IEA ](https://www.iea-amf.org/app/webroot/files/file/Annex%20Reports/AMF_Annex_56.pdf).", + "type": "number" + }, + "shipping_oil_efficiency": { + "default": 0.4, + "description": "The efficiency of oil-powered ships in the conversion of oil to meet shipping needs (propulsion). Base value derived from 2011.", + "type": "number" + }, + "aviation_demand_factor": { + "default": 1.0, + "description": "The proportion of demand for aviation compared to today's consumption.", + "type": "number" + }, + "HVC_demand_factor": { + "default": 1.0, + "description": "The proportion of demand for high-value chemicals compared to today's consumption.", + "type": "number" + }, + "time_dep_hp_cop": { + "default": true, + "description": "Consider the time dependent coefficient of performance (COP) of the heat pump.", + "type": "boolean" + }, + "heat_pump_sink_T_individual_heating": { + "default": 55.0, + "description": "The temperature heat sink used in heat pumps based on DTU / large area radiators. The value is conservatively high to cover hot water and space heating in poorly-insulated buildings.", + "type": "number" + }, + "reduce_space_heat_exogenously": { + "default": true, + "description": "Influence on space heating demand by a certain factor (applied before losses in district heating).", + "type": "boolean" + }, + "reduce_space_heat_exogenously_factor": { + "additionalProperties": { + "type": "number" + }, + "description": "A positive factor can mean renovation or demolition of a building. If the factor is negative, it can mean an increase in floor area, increased thermal comfort, population growth. The default factors are determined by the `Eurocalc Homes and buildings decarbonization scenario `_.", + "markdownDescription": "A positive factor can mean renovation or demolition of a building. If the factor is negative, it can mean an increase in floor area, increased thermal comfort, population growth. The default factors are determined by the [Eurocalc Homes and buildings decarbonization scenario ](http://tool.european-calculator.eu/app/buildings/building-types-area/?levers=1ddd4444421213bdbbbddd44444ffffff11f411111221111211l212221).", + "type": "object" + }, + "retrofitting": { + "description": "Configuration for `sector.retrofitting` settings.", + "properties": { + "retro_endogen": { + "default": false, + "description": "Add retrofitting as an endogenous system which co-optimise space heat savings.", + "type": "boolean" + }, + "cost_factor": { + "default": 1.0, + "description": "Weight costs for building renovation.", + "type": "number" + }, + "interest_rate": { + "default": 0.04, + "description": "The interest rate for investment in building components.", + "type": "number" + }, + "annualise_cost": { + "default": true, + "description": "Annualise the investment costs of retrofitting.", + "type": "boolean" + }, + "tax_weighting": { + "default": false, + "description": "Weight the costs of retrofitting depending on taxes in countries.", + "type": "boolean" + }, + "construction_index": { + "default": true, + "description": "Weight the costs of retrofitting depending on labour/material costs per country.", + "type": "boolean" + } + } + }, + "tes": { + "default": true, + "description": "Add option for storing thermal energy in large water pits associated with district heating systems and individual thermal energy storage (TES).", + "type": "boolean" + }, + "boilers": { + "default": true, + "description": "Add option for transforming gas into heat using gas boilers.", + "type": "boolean" + }, + "resistive_heaters": { + "default": true, + "description": "Add option for transforming electricity into heat using resistive heaters (independently from gas boilers).", + "type": "boolean" + }, + "oil_boilers": { + "default": false, + "description": "Add option for transforming oil into heat using boilers.", + "type": "boolean" + }, + "biomass_boiler": { + "default": true, + "description": "Add option for transforming biomass into heat using boilers.", + "type": "boolean" + }, + "overdimension_heat_generators": { + "additionalProperties": { + "type": "number" + }, + "description": "Add option for overdimensioning heating systems by a certain factor. This allows them to cover heat demand peaks e.g. 10% higher than those in the data with a setting of 1.1.", + "type": "object" + }, + "chp": { + "description": "Configuration for `sector.chp` settings.", + "properties": { + "enable": { + "default": true, + "description": "Add option for using Combined Heat and Power (CHP).", + "type": "boolean" + }, + "fuel": { + "description": "Possible options are all fuels which have an existing bus and their CO2 intensity is given in the technology data. Currently possible are \"gas\", \"oil\", \"methanol\", \"lignite\", \"coal\" as well as \"solid biomass\". For all fuels except solid biomass, the techno-economic data from gas CHP is used. For the special case of solid biomass fuel, both CHP plants with and without carbon capture are added.", + "items": { + "type": "string" + }, + "type": "array" + }, + "micro_chp": { + "default": false, + "description": "Add option for using gas-fired Combined Heat and Power (CHP) for decentral areas.", + "type": "boolean" + } + } + }, + "solar_thermal": { + "default": true, + "description": "Add option for using solar thermal to generate heat.", + "type": "boolean" + }, + "solar_cf_correction": { + "default": 0.788457, + "description": "The correction factor for the value provided by the solar thermal profile calculations.", + "type": "number" + }, + "methanation": { + "default": true, + "description": "Add option for transforming hydrogen and CO2 into methane using methanation.", + "type": "boolean" + }, + "coal_cc": { + "default": false, + "description": "Add option for coal CHPs with carbon capture.", + "type": "boolean" + }, + "dac": { + "default": true, + "description": "Add option for Direct Air Capture (DAC).", + "type": "boolean" + }, + "co2_vent": { + "default": false, + "description": "Add option for vent out CO2 from storages to the atmosphere.", + "type": "boolean" + }, + "heat_vent": { + "additionalProperties": { + "type": "boolean" + }, + "description": "Heat venting by area.", + "type": "object" + }, + "marginal_cost_heat_vent": { + "default": 0.02, + "description": "The marginal cost of heat-venting in all heating systems.", + "type": "number" + }, + "allam_cycle_gas": { + "default": false, + "description": "Add option to include `Allam cycle gas power plants `_.", + "markdownDescription": "Add option to include [Allam cycle gas power plants ](https://en.wikipedia.org/wiki/Allam_power_cycle).", + "type": "boolean" + }, + "hydrogen_fuel_cell": { + "default": true, + "description": "Add option to include hydrogen fuel cell for re-electrification. Assuming OCGT technology costs.", + "type": "boolean" + }, + "hydrogen_turbine": { + "default": true, + "description": "Add option to include hydrogen turbine for re-electrification. Assuming OCGT technology costs.", + "type": "boolean" + }, + "SMR": { + "default": true, + "description": "Add option for transforming natural gas into hydrogen and CO2 using Steam Methane Reforming (SMR).", + "type": "boolean" + }, + "SMR_cc": { + "default": true, + "description": "Add option for transforming natural gas into hydrogen and CO2 using Steam Methane Reforming (SMR) and Carbon Capture (CC).", + "type": "boolean" + }, + "regional_oil_demand": { + "default": true, + "description": "Spatially resolve oil demand. Set to true if regional CO2 constraints needed.", + "type": "boolean" + }, + "regional_coal_demand": { + "default": false, + "description": "Regional coal demand.", + "type": "boolean" + }, + "regional_co2_sequestration_potential": { + "additionalProperties": true, + "description": "Add option for regionally-resolved geological carbon dioxide sequestration potentials based on `CO2StoP `_.", + "markdownDescription": "Add option for regionally-resolved geological carbon dioxide sequestration potentials based on [CO2StoP ](https://setis.ec.europa.eu/european-co2-storage-database_en).", + "type": "object" + }, + "co2_sequestration_potential": { + "additionalProperties": { + "type": "number" + }, + "description": "The potential of sequestering CO2 in Europe per year and investment period.", + "type": "object" + }, + "co2_sequestration_cost": { + "default": 30, + "description": "The cost of sequestering a ton of CO2 (currency/tCO2).", + "type": "number" + }, + "co2_sequestration_lifetime": { + "default": 50, + "description": "The lifetime of a CO2 sequestration site (years).", + "type": "integer" + }, + "co2_spatial": { + "default": true, + "description": "Add option to spatially resolve carrier representing stored carbon dioxide. This allows for more detailed modelling of CCUTS, e.g. regarding the capturing of industrial process emissions, usage as feedstock for electrofuels, transport of carbon dioxide, and geological sequestration sites.", + "type": "boolean" + }, + "co2_network": { + "default": true, + "description": "Add option for planning a new carbon dioxide transmission network.", + "type": "boolean" + }, + "co2_network_cost_factor": { + "default": 1, + "description": "The cost factor for the capital cost of the carbon dioxide transmission network.", + "type": "number" + }, + "cc_fraction": { + "default": 0.9, + "description": "The default fraction of CO2 captured with post-combustion capture.", + "type": "number" + }, + "hydrogen_underground_storage": { + "default": true, + "description": "Add options for storing hydrogen underground. Storage potential depends regionally.", + "type": "boolean" + }, + "hydrogen_underground_storage_locations": { + "description": "The location where hydrogen underground storage can be located. Onshore, nearshore, offshore means it must be located more than 50 km away from the sea, within 50 km of the sea, or within the sea itself respectively.", + "items": { + "type": "string" + }, + "type": "array" + }, + "methanol": { + "description": "Configuration for `sector.methanol` settings.", + "properties": { + "regional_methanol_demand": { + "default": false, + "description": "Spatially resolve methanol demand. Set to true if regional CO2 constraints needed.", + "type": "boolean" + }, + "methanol_reforming": { + "default": false, + "description": "Add methanol reforming.", + "type": "boolean" + }, + "methanol_reforming_cc": { + "default": false, + "description": "Add methanol reforming with carbon capture.", + "type": "boolean" + }, + "methanol_to_kerosene": { + "default": false, + "description": "Add methanol to kerosene.", + "type": "boolean" + }, + "methanol_to_power": { + "additionalProperties": { + "type": "boolean" + }, + "description": "Add different methanol to power technologies.", + "type": "object" + }, + "biomass_to_methanol": { + "default": true, + "description": "Add biomass to methanol.", + "type": "boolean" + }, + "biomass_to_methanol_cc": { + "default": false, + "description": "Add biomass to methanol with carbon capture.", + "type": "boolean" + } + } + }, + "ammonia": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "string" + } + ], + "default": true, + "description": "Add ammonia as a carrier. It can be either true (copperplated NH3), false (no NH3 carrier) or \"regional\" (regionalised NH3 without network)." + }, + "min_part_load_electrolysis": { + "default": 0, + "description": "The minimum unit dispatch (`p_min_pu`) for electrolysis.", + "type": "number" + }, + "min_part_load_fischer_tropsch": { + "default": 0.5, + "description": "The minimum unit dispatch (`p_min_pu`) for the Fischer-Tropsch process.", + "type": "number" + }, + "min_part_load_methanolisation": { + "default": 0.3, + "description": "The minimum unit dispatch (`p_min_pu`) for the methanolisation process.", + "type": "number" + }, + "min_part_load_methanation": { + "default": 0.3, + "description": "Minimum part load methanation.", + "type": "number" + }, + "use_fischer_tropsch_waste_heat": { + "default": 0.25, + "description": "Add option for using waste heat of Fischer Tropsch in district heating networks.", + "type": "number" + }, + "use_haber_bosch_waste_heat": { + "default": 0.25, + "description": "Use Haber-Bosch waste heat.", + "type": "number" + }, + "use_methanolisation_waste_heat": { + "default": 0.25, + "description": "Use methanolisation waste heat.", + "type": "number" + }, + "use_methanation_waste_heat": { + "default": 0.25, + "description": "Use methanation waste heat.", + "type": "number" + }, + "use_fuel_cell_waste_heat": { + "default": 1, + "description": "Add option for using waste heat of fuel cells in district heating networks.", + "type": "number" + }, + "use_electrolysis_waste_heat": { + "default": 0.25, + "description": "Add option for using waste heat of electrolysis in district heating networks.", + "type": "number" + }, + "electricity_transmission_grid": { + "default": true, + "description": "Switch for enabling/disabling the electricity transmission grid.", + "type": "boolean" + }, + "electricity_distribution_grid": { + "default": true, + "description": "Add a simplified representation of the exchange capacity between transmission and distribution grid level through a link.", + "type": "boolean" + }, + "electricity_distribution_grid_cost_factor": { + "default": 1.0, + "description": "Multiplies the investment cost of the electricity distribution grid.", + "type": "number" + }, + "electricity_grid_connection": { + "default": true, + "description": "Add the cost of electricity grid connection for onshore wind and solar.", + "type": "boolean" + }, + "transmission_efficiency": { + "description": "Configuration for `sector.transmission_efficiency` settings.", + "properties": { + "enable": { + "description": "Switch to select the carriers for which transmission efficiency is to be added. Carriers not listed assume lossless transmission.", + "items": { + "type": "string" + }, + "type": "array" + }, + "DC": { + "additionalProperties": { + "type": "number" + }, + "description": "DC transmission efficiency.", + "type": "object" + }, + "H2 pipeline": { + "additionalProperties": { + "type": "number" + }, + "description": "H2 pipeline transmission efficiency.", + "type": "object" + }, + "gas pipeline": { + "additionalProperties": { + "type": "number" + }, + "description": "Gas pipeline transmission efficiency.", + "type": "object" + }, + "electricity distribution grid": { + "additionalProperties": { + "type": "number" + }, + "description": "Electricity distribution grid efficiency.", + "type": "object" + } + } + }, + "H2_network": { + "default": true, + "description": "Add option for new hydrogen pipelines.", + "type": "boolean" + }, + "gas_network": { + "default": true, + "description": "Add existing natural gas infrastructure, incl. LNG terminals, production and entry-points. The existing gas network is added with a lossless transport model. A length-weighted `k-edge augmentation algorithm `_ can be run to add new candidate gas pipelines such that all regions of the model can be connected to the gas network. When activated, all the gas demands are regionally disaggregated as well.", + "markdownDescription": "Add existing natural gas infrastructure, incl. LNG terminals, production and entry-points. The existing gas network is added with a lossless transport model. A length-weighted [k-edge augmentation algorithm ](https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.connectivity.edge_augmentation.k_edge_augmentation.html#networkx.algorithms.connectivity.edge_augmentation.k_edge_augmentation) can be run to add new candidate gas pipelines such that all regions of the model can be connected to the gas network. When activated, all the gas demands are regionally disaggregated as well.", + "type": "boolean" + }, + "H2_retrofit": { + "default": false, + "description": "Add option for retrofiting existing pipelines to transport hydrogen.", + "type": "boolean" + }, + "H2_retrofit_capacity_per_CH4": { + "default": 0.6, + "description": "The ratio for H2 capacity per original CH4 capacity of retrofitted pipelines. The `European Hydrogen Backbone (April, 2020) p.15 `_ 60% of original natural gas capacity could be used in cost-optimal case as H2 capacity.", + "markdownDescription": "The ratio for H2 capacity per original CH4 capacity of retrofitted pipelines. The [European Hydrogen Backbone (April, 2020) p.15 ](https://gasforclimate2050.eu/wp-content/uploads/2020/07/2020_European-Hydrogen-Backbone_Report.pdf) 60% of original natural gas capacity could be used in cost-optimal case as H2 capacity.", + "type": "number" + }, + "gas_network_connectivity_upgrade": { + "default": 1, + "description": "The number of desired edge connectivity (k) in the length-weighted `k-edge augmentation algorithm `_ used for the gas network.", + "markdownDescription": "The number of desired edge connectivity (k) in the length-weighted [k-edge augmentation algorithm ](https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.connectivity.edge_augmentation.k_edge_augmentation.html#networkx.algorithms.connectivity.edge_augmentation.k_edge_augmentation) used for the gas network.", + "type": "number" + }, + "gas_distribution_grid": { + "default": true, + "description": "Add a gas distribution grid.", + "type": "boolean" + }, + "gas_distribution_grid_cost_factor": { + "default": 1.0, + "description": "Multiplier for the investment cost of the gas distribution grid.", + "type": "number" + }, + "biomass_spatial": { + "default": true, + "description": "Add option for resolving biomass demand regionally.", + "type": "boolean" + }, + "biomass_transport": { + "default": false, + "description": "Add option for transporting solid biomass between nodes.", + "type": "boolean" + }, + "biogas_upgrading": { + "default": true, + "description": "Biogas upgrading.", + "type": "boolean" + }, + "biogas_upgrading_cc": { + "default": false, + "description": "Add option to capture CO2 from biomass upgrading.", + "type": "boolean" + }, + "conventional_generation": { + "additionalProperties": { + "type": "string" + }, + "description": "Add a more detailed description of conventional carriers. Any power generation requires the consumption of fuel from nodes representing that fuel.", + "type": "object" + }, + "biomass_to_liquid": { + "default": true, + "description": "Add option for transforming solid biomass into liquid fuel with the same properties as oil.", + "type": "boolean" + }, + "biomass_to_liquid_cc": { + "default": false, + "description": "Add option for transforming solid biomass into liquid fuel with the same properties as oil with carbon capture.", + "type": "boolean" + }, + "electrobiofuels": { + "default": true, + "description": "Electrobiofuels.", + "type": "boolean" + }, + "biosng": { + "default": false, + "description": "Add option for transforming solid biomass into synthesis gas with the same properties as natural gas.", + "type": "boolean" + }, + "biosng_cc": { + "default": false, + "description": "Add option for transforming solid biomass into synthesis gas with the same properties as natural gas with carbon capture.", + "type": "boolean" + }, + "bioH2": { + "default": false, + "description": "Add option for transforming solid biomass into hydrogen with carbon capture.", + "type": "boolean" + }, + "municipal_solid_waste": { + "default": false, + "description": "Add option for municipal solid waste.", + "type": "boolean" + }, + "limit_max_growth": { + "description": "Configuration for `sector.limit_max_growth` settings.", + "properties": { + "enable": { + "default": false, + "description": "Add option to limit the maximum growth of a carrier.", + "type": "boolean" + }, + "factor": { + "default": 1.3, + "description": "The maximum growth factor of a carrier (e.g. 1.3 allows 30% larger than max historic growth).", + "type": "number" + }, + "max_growth": { + "additionalProperties": { + "type": "number" + }, + "description": "The historic maximum growth of a carrier.", + "type": "object" + }, + "max_relative_growth": { + "additionalProperties": { + "type": "number" + }, + "description": "The historic maximum relative growth of a carrier.", + "type": "object" + } + } + }, + "enhanced_geothermal": { + "description": "Configuration for `sector.enhanced_geothermal` settings.", + "properties": { + "enable": { + "default": false, + "description": "Add option to include Enhanced Geothermal Systems.", + "type": "boolean" + }, + "flexible": { + "default": true, + "description": "Add option for flexible operation (see Ricks et al. 2024).", + "type": "boolean" + }, + "max_hours": { + "default": 240, + "description": "The maximum hours the reservoir can be charged under flexible operation.", + "type": "integer" + }, + "max_boost": { + "default": 0.25, + "description": "The maximum boost in power output under flexible operation.", + "type": "number" + }, + "var_cf": { + "default": true, + "description": "Add option for variable capacity factor (see Ricks et al. 2024).", + "type": "boolean" + }, + "sustainability_factor": { + "default": 0.0025, + "description": "Share of sourced heat that is replenished by the earth's core (see details in `build_egs_potentials.py `_).", + "markdownDescription": "Share of sourced heat that is replenished by the earth's core (see details in [build_egs_potentials.py ](https://github.com/PyPSA/pypsa-eur-sec/blob/master/scripts/build_egs_potentials.py)).", + "type": "number" + } + } + }, + "solid_biomass_import": { + "description": "Configuration for `sector.solid_biomass_import` settings.", + "properties": { + "enable": { + "default": false, + "description": "Add option to include solid biomass imports.", + "type": "boolean" + }, + "price": { + "default": 54, + "description": "Price for importing solid biomass (currency/MWh).", + "type": "number" + }, + "max_amount": { + "default": 1390, + "description": "Maximum solid biomass import potential (TWh).", + "type": "number" + }, + "upstream_emissions_factor": { + "default": 0.1, + "description": "Upstream emissions of solid biomass imports.", + "type": "number" + } + } + }, + "imports": { + "description": "Configuration for `sector.imports` settings.", + "properties": { + "enable": { + "default": false, + "description": "Add option to include renewable energy imports.", + "type": "boolean" + }, + "limit": { + "default": null, + "description": "Maximum allowed renewable energy imports (TWh).", + "type": "number" + }, + "limit_sense": { + "default": "<=", + "description": "Sense of the limit.", + "type": "string" + }, + "price": { + "additionalProperties": { + "type": "number" + }, + "description": "Price for importing renewable energy of carrier.", + "type": "object" + } + } + } + } + }, + "SnapshotsConfig": { + "description": "Configuration for `snapshots` settings.", + "properties": { + "start": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": "2013-01-01", + "description": "Left bound of date range." + }, + "end": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": "2014-01-01", + "description": "Right bound of date range." + }, + "inclusive": { + "anyOf": [ + { + "enum": [ + "left", + "right", + "both" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": "left", + "description": "Make the time interval closed to the `left`, `right`, or both sides `both` or neither side `None`." + } + } + }, + "SolarThermalConfig": { + "description": "Configuration for `solar_thermal` settings.", + "properties": { + "clearsky_model": { + "default": "simple", + "description": "Type of clearsky model for diffuse irradiation.", + "enum": [ + "simple", + "enhanced" + ], + "type": "string" + }, + "orientation": { + "description": "Configuration for `solar_thermal.orientation` settings.", + "properties": { + "slope": { + "default": 45.0, + "description": "The angle between the ground and the panels.", + "type": "number" + }, + "azimuth": { + "default": 180.0, + "description": "The angle between the North and the sun with panels on the local horizon.", + "type": "number" + } + } + }, + "cutout": { + "default": "default", + "description": "Name of the cutout to use for solar thermal calculations.", + "type": "string" + } + } + }, + "SolvingConfig": { + "description": "Configuration for `solving` settings.", + "properties": { + "options": { + "description": "Configuration for `solving.options` settings.", + "properties": { + "clip_p_max_pu": { + "default": 0.01, + "description": "To avoid too small values in the renewables` per-unit availability time series values below this threshold are set to zero.", + "type": "number" + }, + "load_shedding": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "number" + } + ], + "default": false, + "description": "Add generators with very high marginal cost to simulate load shedding and avoid problem infeasibilities. If load shedding is a float, it denotes the marginal cost in EUR/kWh." + }, + "curtailment_mode": { + "default": false, + "description": "Fixes the dispatch profiles of generators with time-varying p_max_pu by setting `p_min_pu = p_max_pu` and adds an auxiliary curtailment generator (with negative sign to absorb excess power) at every AC bus. This can speed up the solving process as the curtailment decision is aggregated into a single generator per region. Defaults to `false`.", + "type": "boolean" + }, + "noisy_costs": { + "default": true, + "description": "Add random noise to marginal cost of generators by :math:`\\mathcal{U}(0.009,0,011)` and capital cost of lines and links by :math:`\\mathcal{U}(0.09,0,11)`.", + "type": "boolean" + }, + "skip_iterations": { + "default": true, + "description": "Skip iterating, do not update impedances of branches. Defaults to true.", + "type": "boolean" + }, + "rolling_horizon": { + "default": false, + "description": "Switch for rule `solve_operations_network` whether to optimize the network in a rolling horizon manner, where the snapshot range is split into slices of size `horizon` which are solved consecutively. This setting has currently no effect on sector-coupled networks.", + "type": "boolean" + }, + "seed": { + "default": 123, + "description": "Random seed for increased deterministic behaviour.", + "type": "integer" + }, + "custom_extra_functionality": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": "../data/custom_extra_functionality.py", + "description": "Path to a Python file with custom extra functionality code to be injected into the solving rules of the workflow relative to `rules` directory." + }, + "io_api": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Passed to linopy and determines the API used to communicate with the solver. With the `'lp'` and `'mps'` options linopy passes a file to the solver; with the `'direct'` option (only supported for HIGHS and Gurobi) linopy uses an in-memory python API resulting in better performance." + }, + "track_iterations": { + "default": false, + "description": "Flag whether to store the intermediate branch capacities and objective function values are recorded for each iteration in `network.lines['s_nom_opt_X']` (where `X` labels the iteration)", + "type": "boolean" + }, + "min_iterations": { + "default": 2, + "description": "Minimum number of solving iterations in between which resistance and reactence (`x/r`) are updated for branches according to `s_nom_opt` of the previous run.", + "type": "integer" + }, + "max_iterations": { + "default": 3, + "description": "Maximum number of solving iterations in between which resistance and reactence (`x/r`) are updated for branches according to `s_nom_opt` of the previous run.", + "type": "integer" + }, + "transmission_losses": { + "default": 2, + "description": "Add piecewise linear approximation of transmission losses based on n tangents. Defaults to 0, which means losses are ignored.", + "type": "integer" + }, + "linearized_unit_commitment": { + "default": true, + "description": "Whether to optimise using the linearized unit commitment formulation.", + "type": "boolean" + }, + "horizon": { + "default": 365, + "description": "Number of snapshots to consider in each iteration. Defaults to 100.", + "type": "integer" + }, + "post_discretization": { + "description": "Configuration for `solving.options.post_discretization` settings.", + "properties": { + "enable": { + "default": false, + "description": "Switch to enable post-discretization of the network. Disabled by default.", + "type": "boolean" + }, + "line_unit_size": { + "default": 1700, + "description": "Discrete unit size of lines in MW.", + "type": "number" + }, + "line_threshold": { + "default": 0.3, + "description": "The threshold relative to the discrete line unit size beyond which to round up to the next unit.", + "type": "number" + }, + "link_unit_size": { + "additionalProperties": { + "type": "number" + }, + "description": "Discrete unit size of links in MW by carrier (given in dictionary style).", + "type": "object" + }, + "link_threshold": { + "additionalProperties": { + "type": "number" + }, + "description": "The threshold relative to the discrete link unit size beyond which to round up to the next unit by carrier (given in dictionary style).", + "type": "object" + }, + "fractional_last_unit_size": { + "default": false, + "description": "When true, links and lines can be built up to p_nom_max. When false, they can only be built up to a multiple of the unit size.", + "type": "boolean" + } + } + }, + "keep_files": { + "default": false, + "description": "Whether to keep LPs and MPS files after solving.", + "type": "boolean" + }, + "model_kwargs": { + "description": "Configuration for `solving.options.model_kwargs` settings.", + "properties": { + "solver_dir": { + "default": "", + "description": "Absolute path to the directory where linopy saves files.", + "type": "string" + } + } + } + } + }, + "agg_p_nom_limits": { + "description": "Configuration for `solving.agg_p_nom_limits` settings.", + "properties": { + "agg_offwind": { + "default": false, + "description": "Aggregate together all the types of offwind when writing the constraint (`offwind-all` as a carrier in the `.csv` file). Default is false.", + "type": "boolean" + }, + "agg_solar": { + "default": false, + "description": "Aggregate together all the types of electric solar when writing the constraint (`solar-all` as a carrier in the `.csv` file). Default is false.", + "type": "boolean" + }, + "include_existing": { + "default": false, + "description": "Take existing capacities into account when writing the constraint. Default is false.", + "type": "boolean" + }, + "file": { + "default": "data/agg_p_nom_minmax.csv", + "description": "Reference to `.csv` file specifying per carrier generator nominal capacity constraints for individual countries and planning horizons. Defaults to `data/agg_p_nom_minmax.csv`.", + "type": "string" + } + } + }, + "constraints": { + "description": "Configuration for `solving.constraints` settings.", + "properties": { + "CCL": { + "default": false, + "description": "Add minimum and maximum levels of generator nominal capacity per carrier for individual countries. These can be specified in the file linked at `electricity: agg_p_nom_limits` in the configuration. File defaults to `data/agg_p_nom_minmax.csv`. Does not work with a time resolution resampling.", + "type": "boolean" + }, + "EQ": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "string" + } + ], + "default": false, + "description": "Require each country or node to on average produce a minimal share of its total consumption itself. Example: `EQ0.5c` demands each country to produce on average at least 50% of its consumption; `EQ0.5` demands each node to produce on average at least 50% of its consumption." + }, + "BAU": { + "default": false, + "description": "Add a per-`carrier` minimal overall capacity; i.e. at least `40GW` of `OCGT` in Europe; configured in `electricity: BAU_mincapacities`", + "type": "boolean" + }, + "SAFE": { + "default": false, + "description": "Add a capacity reserve margin of a certain fraction above the peak demand to which renewable generators and storage do *not* contribute. Ignores network.", + "type": "boolean" + } + } + }, + "solver": { + "description": "Configuration for `solving.solver` settings.", + "properties": { + "name": { + "default": "gurobi", + "description": "Solver to use for optimisation problems in the workflow; e.g. clustering and linear optimal power flow.", + "type": "string" + }, + "options": { + "default": "gurobi-default", + "description": "Link to specific parameter settings.", + "type": "string" + } + } + }, + "solver_options": { + "additionalProperties": { + "additionalProperties": true, + "type": "object" + }, + "description": "Dictionaries with solver-specific parameter settings.", + "type": "object" + }, + "check_objective": { + "description": "Configuration for `solving.check_objective` settings.", + "properties": { + "enable": { + "default": false, + "description": "Enable objective value checking.", + "type": "boolean" + }, + "expected_value": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Expected objective value." + }, + "atol": { + "default": 1000000, + "description": "Absolute tolerance.", + "type": "number" + }, + "rtol": { + "default": 0.01, + "description": "Relative tolerance.", + "type": "number" + } + } + }, + "oetc": { + "anyOf": [ + { + "description": "Configuration for `solving.oetc` settings (Open Energy Transition Computing cluster support).", + "properties": { + "name": { + "default": "pypsa-eur", + "description": "Name identifier for the OETC job.", + "type": "string" + }, + "authentication_server_url": { + "default": "", + "description": "URL of the OETC authentication server for job submission.", + "type": "string" + }, + "orchestrator_server_url": { + "default": "", + "description": "URL of the OETC orchestrator server for job management.", + "type": "string" + }, + "cpu_cores": { + "default": 8, + "description": "Number of CPU cores to request for the OETC job. (includes RAM amount at the moment with a factor of 8)", + "type": "integer" + }, + "disk_space_gb": { + "default": 50, + "description": "Amount of disk space in gigabytes to request for the OETC job.", + "type": "integer" + }, + "delete_worker_on_error": { + "default": true, + "description": "Whether to delete the worker instance when an error occurs during job execution.", + "type": "boolean" + } + } + }, + { + "type": "null" + } + ], + "default": null, + "description": "Configuration options for Open Energy Transition Computing (OETC) cluster support." + }, + "mem_mb": { + "default": 128000, + "description": "Estimated maximum memory requirement for solving networks (MB).", + "type": "integer" + }, + "memory_logging_frequency": { + "default": 5, + "description": "Interval in seconds at which memory usage is logged.", + "type": "integer" + }, + "runtime": { + "default": "48h", + "description": "Runtime in humanfriendly style.", + "type": "string" + } + } + }, + "TransformersConfig": { + "description": "Configuration for `transformers` settings.", + "properties": { + "x": { + "default": 0.1, + "description": "Series reactance in per unit (p.u.), using `s_nom` as base power of the transformer. Overwritten if `type` is specified.", + "type": "number" + }, + "s_nom": { + "default": 2000.0, + "description": "Limit of apparent power which can pass through branch (MVA). Overwritten if `type` is specified.", + "type": "number" + }, + "type": { + "default": "", + "description": "Specifies transformer types to assume for the transformers of the ENTSO-E grid extraction.", + "type": "string" + } + } + }, + "TransmissionProjectsConfig": { + "description": "Configuration for `transmission_projects` settings.", + "properties": { + "enable": { + "default": true, + "description": "Whether to integrate this transmission projects or not.", + "type": "boolean" + }, + "include": { + "description": "Configuration for `transmission_projects.include` settings.", + "properties": { + "tyndp2020": { + "default": true, + "description": "Whether to integrate the TYNDP 2020 dataset.", + "type": "boolean" + }, + "nep": { + "default": true, + "description": "Whether to integrate the German network development plan dataset.", + "type": "boolean" + }, + "manual": { + "default": true, + "description": "Whether to integrate the manually added transmission projects. They are taken from the previously existing links_tyndp.csv file.", + "type": "boolean" + } + } + }, + "skip": { + "description": "Type of lines to skip from all transmission projects. Possible values are: `upgraded_lines`, `upgraded_links`, `new_lines`, `new_links`.", + "items": { + "type": "string" + }, + "type": "array" + }, + "status": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "additionalProperties": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": "object" + } + ], + "description": "Status to include into the model as list or as dict with name of project and status to include. Possible values for status are `under_construction`, `in_permitting`, `confirmed`, `planned_not_yet_permitted`, `under_consideration`." + }, + "new_link_capacity": { + "default": "zero", + "description": "Whether to set the new link capacity to the provided capacity or set it to zero.", + "enum": [ + "zero", + "keep" + ], + "type": "string" + } + } + }, + "_AdjustmentConfig": { + "description": "Configuration for adjustment settings (factor/absolute)", + "properties": { + "factor": { + "anyOf": [ + { + "type": "boolean" + }, + { + "additionalProperties": { + "additionalProperties": { + "additionalProperties": { + "anyOf": [ + { + "type": "number" + }, + { + "additionalProperties": { + "type": "number" + }, + "type": "object" + } + ] + }, + "type": "object" + }, + "type": "object" + }, + "type": "object" + } + ], + "default": false, + "description": "Multiply original value with given factor" + }, + "absolute": { + "anyOf": [ + { + "type": "boolean" + }, + { + "additionalProperties": { + "additionalProperties": { + "additionalProperties": { + "anyOf": [ + { + "type": "number" + }, + { + "additionalProperties": { + "type": "number" + }, + "type": "object" + } + ] + }, + "type": "object" + }, + "type": "object" + }, + "type": "object" + } + ], + "default": false, + "description": "Set attribute to absolute value. Can be also a dictionary with planning horizons as keys." + } + } + }, + "_AdministrativeConfig": { + "description": "Configuration for `clustering.administrative` settings.", + "properties": { + "level": { + "default": 1, + "description": "Level of administrative regions to cluster the network. 0: Country level, 1: NUTS1 level, 2: NUTS2 level, 3: NUTS3 level, 'bz': Bidding zones. Only applies when mode is set to `administrative`. Note that non-NUTS countries 'BA', 'MD', 'UA', and 'XK' can only be clustered to level 0 and 1.", + "enum": [ + 0, + 1, + 2, + 3, + "bz" + ] + }, + "countries": { + "additionalProperties": { + "type": "integer" + }, + "description": "Optionally include dictionary of individual country codes and their individual NUTS levels. Overwrites country-specific `level`. For example: `{'DE': 1, 'FR': 2}`. Only applies when mode is set to `administrative`.", + "type": "object" + } + } + }, + "_AggPNomLimitsConfig": { + "description": "Configuration for `solving.agg_p_nom_limits` settings.", + "properties": { + "agg_offwind": { + "default": false, + "description": "Aggregate together all the types of offwind when writing the constraint (`offwind-all` as a carrier in the `.csv` file). Default is false.", + "type": "boolean" + }, + "agg_solar": { + "default": false, + "description": "Aggregate together all the types of electric solar when writing the constraint (`solar-all` as a carrier in the `.csv` file). Default is false.", + "type": "boolean" + }, + "include_existing": { + "default": false, + "description": "Take existing capacities into account when writing the constraint. Default is false.", + "type": "boolean" + }, + "file": { + "default": "data/agg_p_nom_minmax.csv", + "description": "Reference to `.csv` file specifying per carrier generator nominal capacity constraints for individual countries and planning horizons. Defaults to `data/agg_p_nom_minmax.csv`.", + "type": "string" + } + } + }, + "_AggregationStrategiesConfig": { + "description": "Configuration for `clustering.aggregation_strategies` settings.", + "properties": { + "generators": { + "additionalProperties": { + "type": "string" + }, + "description": "Aggregates the component according to the given strategy. For example, if sum, then all values within each cluster are summed to represent the new generator.", + "type": "object" + }, + "buses": { + "additionalProperties": { + "type": "string" + }, + "description": "Aggregates the component according to the given strategy. For example, if sum, then all values within each cluster are summed to represent the new bus.", + "type": "object" + } + } + }, + "_AutarkyConfig": { + "description": "Configuration for `electricity.autarky` settings.", + "properties": { + "enable": { + "default": false, + "description": "Require each node to be autarkic by removing all lines and links.", + "type": "boolean" + }, + "by_country": { + "default": false, + "description": "Require each country to be autarkic by removing all cross-border lines and links. `electricity: autarky` must be enabled.", + "type": "boolean" + } + } + }, + "_BiomassClassesConfig": { + "description": "Configuration for `biomass.classes` settings.", + "properties": { + "solid biomass": { + "description": "The comodity that are included as solid biomass.", + "items": { + "type": "string" + }, + "type": "array" + }, + "not included": { + "description": "The comodity that are not included as a biomass potential.", + "items": { + "type": "string" + }, + "type": "array" + }, + "biogas": { + "description": "The comodity that are included as biogas.", + "items": { + "type": "string" + }, + "type": "array" + }, + "municipal solid waste": { + "description": "The commodities that are included as municipal solid waste.", + "items": { + "type": "string" + }, + "type": "array" + } + } + }, + "_BuildBiddingZonesConfig": { + "description": "Configuration for `clustering.build_bidding_zones` settings.", + "properties": { + "remove_islands": { + "default": false, + "description": "Exclude from the shape file the Balearic Islands, Bornholm, the Canary Islands, the Orkney Islands, the Shetland Islands, the Azores Islands and Madeira.", + "type": "boolean" + }, + "aggregate_to_tyndp": { + "default": false, + "description": "Adjust the shape file to the TYNDP topology. Aggregate the Southern Norwegian bidding zones and extract Crete as a separate zone from the Greek shape.", + "type": "boolean" + } + } + }, + "_CHPConfig": { + "description": "Configuration for `sector.chp` settings.", + "properties": { + "enable": { + "default": true, + "description": "Add option for using Combined Heat and Power (CHP).", + "type": "boolean" + }, + "fuel": { + "description": "Possible options are all fuels which have an existing bus and their CO2 intensity is given in the technology data. Currently possible are \"gas\", \"oil\", \"methanol\", \"lignite\", \"coal\" as well as \"solid biomass\". For all fuels except solid biomass, the techno-economic data from gas CHP is used. For the special case of solid biomass fuel, both CHP plants with and without carbon capture are added.", + "items": { + "type": "string" + }, + "type": "array" + }, + "micro_chp": { + "default": false, + "description": "Add option for using gas-fired Combined Heat and Power (CHP) for decentral areas.", + "type": "boolean" + } + } + }, + "_CheckObjectiveConfig": { + "description": "Configuration for `solving.check_objective` settings.", + "properties": { + "enable": { + "default": false, + "description": "Enable objective value checking.", + "type": "boolean" + }, + "expected_value": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Expected objective value." + }, + "atol": { + "default": 1000000, + "description": "Absolute tolerance.", + "type": "number" + }, + "rtol": { + "default": 0.01, + "description": "Relative tolerance.", + "type": "number" + } + } + }, + "_ChunksConfig": { + "description": "Configuration for `atlite.cutouts.{name}.chunks` settings.", + "properties": { + "time": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Chunk size for time dimension when preparing cutout." + } + } + }, + "_ClusterNetworkConfig": { + "description": "Configuration for `clustering.cluster_network` settings.", + "properties": { + "algorithm": { + "default": "kmeans", + "description": "Clustering algorithm to use.", + "enum": [ + "kmeans", + "hac" + ], + "type": "string" + }, + "hac_features": { + "description": "List of meteorological variables contained in the weather data cutout that should be considered for hierarchical clustering.", + "items": { + "type": "string" + }, + "type": "array" + } + } + }, + "_ConstraintsConfig": { + "description": "Configuration for `solving.constraints` settings.", + "properties": { + "CCL": { + "default": false, + "description": "Add minimum and maximum levels of generator nominal capacity per carrier for individual countries. These can be specified in the file linked at `electricity: agg_p_nom_limits` in the configuration. File defaults to `data/agg_p_nom_minmax.csv`. Does not work with a time resolution resampling.", + "type": "boolean" + }, + "EQ": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "string" + } + ], + "default": false, + "description": "Require each country or node to on average produce a minimal share of its total consumption itself. Example: `EQ0.5c` demands each country to produce on average at least 50% of its consumption; `EQ0.5` demands each node to produce on average at least 50% of its consumption." + }, + "BAU": { + "default": false, + "description": "Add a per-`carrier` minimal overall capacity; i.e. at least `40GW` of `OCGT` in Europe; configured in `electricity: BAU_mincapacities`", + "type": "boolean" + }, + "SAFE": { + "default": false, + "description": "Add a capacity reserve margin of a certain fraction above the peak demand to which renewable generators and storage do *not* contribute. Ignores network.", + "type": "boolean" + } + } + }, + "_CorineConfig": { + "description": "Configuration for CORINE land cover settings.", + "properties": { + "grid_codes": { + "description": "Specifies areas according to CORINE Land Cover codes which are generally eligible for wind turbine placement.", + "items": { + "type": "integer" + }, + "type": "array" + }, + "distance": { + "default": 1000, + "description": "Distance in meters to keep from areas specified in `distance_grid_codes`.", + "type": "number" + }, + "distance_grid_codes": { + "description": "Specifies areas according to CORINE Land Cover codes to which wind turbines must maintain a distance specified in the setting `distance`.", + "items": { + "type": "integer" + }, + "type": "array" + } + }, + "required": [ + "grid_codes" + ] + }, + "_CutoutConfig": { + "description": "Configuration for a single cutout in `atlite.cutouts`.", + "properties": { + "module": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Source of the reanalysis weather dataset (e.g. `ERA5 `_ or `SARAH-3 `_).", + "markdownDescription": "Source of the reanalysis weather dataset (e.g. [ERA5 ](https://www.ecmwf.int/en/forecasts/datasets/reanalysis-datasets/era5) or [SARAH-3 ](https://wui.cmsaf.eu/safira/action/viewDoiDetails?acronym=SARAH_V002))." + }, + "x": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Range of longitudes [\u00b0] to download weather data for. Float interval within [-180, 180]. If not defined, it defaults to the spatial bounds of all bus shapes." + }, + "y": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Range of latitudes [\u00b0] to download weather data for. Float interval within [-90, 90]. If not defined, it defaults to the spatial bounds of all bus shapes." + }, + "dx": { + "anyOf": [ + { + "exclusiveMinimum": 0.25, + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Grid resolution [\u00b0] for longitude. Must be larger than 0.25\u00b0." + }, + "dy": { + "anyOf": [ + { + "exclusiveMinimum": 0.25, + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Grid resolution [\u00b0] for latitude. Must be larger than 0.25\u00b0." + }, + "time": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Time span to download weather data for. If not defined, it defaults to the time interval spanned by the snapshots." + }, + "chunks": { + "anyOf": [ + { + "description": "Configuration for `atlite.cutouts.{name}.chunks` settings.", + "properties": { + "time": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Chunk size for time dimension when preparing cutout." + } + } + }, + { + "type": "null" + } + ], + "default": null, + "description": "Chunking configuration for cutout preparation." + }, + "prepare_kwargs": { + "anyOf": [ + { + "description": "Configuration for `atlite.cutouts.{name}.prepare_kwargs` settings.", + "properties": { + "features": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "When freshly building a cutout, retrieve data only for those features. If not defined, it defaults to all available features." + }, + "sarah_dir": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Path to the location where SARAH-2 or SARAH-3 data is stored; SARAH data requires a manual separate download, see the `atlite documentation `_ for details. Required for building cutouts with SARAH, not required for ERA5 cutouts.", + "markdownDescription": "Path to the location where SARAH-2 or SARAH-3 data is stored; SARAH data requires a manual separate download, see the [atlite documentation ](https://atlite.readthedocs.io) for details. Required for building cutouts with SARAH, not required for ERA5 cutouts." + }, + "monthly_requests": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Whether to use monthly requests for ERA5 data when building the cutout. Helpful to avoid running into request limits with large cutouts." + }, + "tmpdir": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Path to a temporary directory where intermediate files are stored when building the cutout. Helpful when building large cutouts." + } + } + }, + { + "type": "null" + } + ], + "default": null, + "description": "Dictionary of keyword arguments passed to ``atlite.Cutout.prepare()`` when building the cutout." + } + } + }, + "_DataSourceConfig": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "_DistributionKeyConfig": { + "description": "Configuration for `load.distribution_key` settings.", + "properties": { + "gdp": { + "default": 0.6, + "description": "Weighting factor for the GDP data in the distribution key.", + "type": "number" + }, + "population": { + "default": 0.4, + "description": "Weighting factor for the population data in the distribution key.", + "type": "number" + } + } + }, + "_DistrictHeatingConfig": { + "description": "Configuration for `sector.district_heating` settings.", + "properties": { + "potential": { + "anyOf": [ + { + "type": "number" + }, + { + "additionalProperties": { + "type": "number" + }, + "type": "object" + } + ], + "default": 0.6, + "description": "Maximum fraction of urban demand which can be supplied by district heating. If given as dictionary, specify one value per country modeled or provide a default value with key `default` to fill values for all unspecified countries." + }, + "progress": { + "additionalProperties": { + "type": "number" + }, + "description": "Increase of today's district heating demand to potential maximum district heating share. Progress = 0 means today's district heating share. Progress = 1 means maximum fraction of urban demand is supplied by district heating.", + "type": "object" + }, + "district_heating_loss": { + "default": 0.15, + "description": "Share increase in district heat demand in urban central due to heat losses.", + "type": "number" + }, + "supply_temperature_approximation": { + "additionalProperties": true, + "description": "Supply temperature approximation settings.", + "type": "object" + }, + "ptes": { + "additionalProperties": true, + "description": "Pit thermal energy storage settings.", + "type": "object" + }, + "ates": { + "additionalProperties": true, + "description": "Aquifer thermal energy storage settings.", + "type": "object" + }, + "heat_source_cooling": { + "default": 6, + "description": "Cooling of heat source for heat pumps.", + "type": "number" + }, + "heat_pump_cop_approximation": { + "additionalProperties": true, + "description": "Heat pump COP approximation settings.", + "type": "object" + }, + "limited_heat_sources": { + "additionalProperties": true, + "description": "Dictionary with names of limited heat sources (not air). Must be `river_water` / `geothermal` or another heat source in `Manz et al. 2024 `_.", + "markdownDescription": "Dictionary with names of limited heat sources (not air). Must be `river_water` / `geothermal` or another heat source in [Manz et al. 2024 ](https://www.sciencedirect.com/science/article/pii/S0960148124001769).", + "type": "object" + }, + "direct_utilisation_heat_sources": { + "description": "List of heat sources for direct heat utilisation in district heating. Must be in the keys of `heat_utilisation_potentials` (e.g. `geothermal`).", + "items": { + "type": "string" + }, + "type": "array" + }, + "temperature_limited_stores": { + "description": "List of names for stores used as limited heat sources.", + "items": { + "type": "string" + }, + "type": "array" + }, + "dh_areas": { + "additionalProperties": true, + "description": "District heating areas settings.", + "type": "object" + } + } + }, + "_DynamicLineRatingConfig": { + "description": "Configuration for `lines.dynamic_line_rating` settings.", + "properties": { + "activate": { + "default": false, + "description": "Whether to take dynamic line rating into account.", + "type": "boolean" + }, + "cutout": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": "default", + "description": "Specifies the weather data cutout file(s) to use." + }, + "correction_factor": { + "default": 0.95, + "description": "Factor to compensate for overestimation of wind speeds in hourly averaged wind data.", + "type": "number" + }, + "max_voltage_difference": { + "anyOf": [ + { + "type": "number" + }, + { + "const": false, + "type": "boolean" + } + ], + "default": false, + "description": "Maximum voltage angle difference in degrees or 'false' to disable." + }, + "max_line_rating": { + "anyOf": [ + { + "type": "number" + }, + { + "const": false, + "type": "boolean" + } + ], + "default": false, + "description": "Maximum line rating relative to nominal capacity without DLR, e.g. 1.3 or 'false' to disable." + } + } + }, + "_EmissionPricesConfig": { + "description": "Configuration for `costs.emission_prices` settings.", + "properties": { + "enable": { + "default": false, + "description": "Add cost for a carbon-dioxide price configured in `costs: emission_prices: co2` to `marginal_cost` of generators. Config setting can also be enabled with the keyword `Ep` in the `{opts}` wildcard for electricity-only runs.", + "type": "boolean" + }, + "co2": { + "anyOf": [ + { + "type": "number" + }, + { + "additionalProperties": { + "type": "number" + }, + "type": "object" + } + ], + "default": 0.0, + "description": "Exogenous price of carbon-dioxide. In electricity-only runs it is added to the marginal costs of fossil-fuelled generators according to their carbon intensity, while for sector networks it applies to emissions ending up in CO2 atmosphere." + }, + "co2_monthly_prices": { + "default": false, + "description": "Add monthly cost for a carbon-dioxide price based on historical values built by the rule `build_monthly_prices`.", + "type": "boolean" + } + } + }, + "_EnhancedGeothermalConfig": { + "description": "Configuration for `sector.enhanced_geothermal` settings.", + "properties": { + "enable": { + "default": false, + "description": "Add option to include Enhanced Geothermal Systems.", + "type": "boolean" + }, + "flexible": { + "default": true, + "description": "Add option for flexible operation (see Ricks et al. 2024).", + "type": "boolean" + }, + "max_hours": { + "default": 240, + "description": "The maximum hours the reservoir can be charged under flexible operation.", + "type": "integer" + }, + "max_boost": { + "default": 0.25, + "description": "The maximum boost in power output under flexible operation.", + "type": "number" + }, + "var_cf": { + "default": true, + "description": "Add option for variable capacity factor (see Ricks et al. 2024).", + "type": "boolean" + }, + "sustainability_factor": { + "default": 0.0025, + "description": "Share of sourced heat that is replenished by the earth's core (see details in `build_egs_potentials.py `_).", + "markdownDescription": "Share of sourced heat that is replenished by the earth's core (see details in [build_egs_potentials.py ](https://github.com/PyPSA/pypsa-eur-sec/blob/master/scripts/build_egs_potentials.py)).", + "type": "number" + } + } + }, + "_EstimateRenewableCapacitiesConfig": { + "description": "Configuration for `electricity.estimate_renewable_capacities` settings.", + "properties": { + "enable": { + "default": true, + "description": "Activate routine to estimate renewable capacities in rule `add_electricity`. This option should not be used in combination with pathway planning `foresight: myopic` or `foresight: perfect` as renewable capacities are added differently in `add_existing_baseyear`.", + "type": "boolean" + }, + "from_gem": { + "default": true, + "description": "Add renewable capacities from `Global Energy Monitor's Global Solar Power Tracker `_ and `Global Energy Monitor's Global Wind Power Tracker `_.", + "markdownDescription": "Add renewable capacities from [Global Energy Monitor's Global Solar Power Tracker ](https://globalenergymonitor.org/projects/global-solar-power-tracker/) and [Global Energy Monitor's Global Wind Power Tracker ](https://globalenergymonitor.org/projects/global-wind-power-tracker/).", + "type": "boolean" + }, + "year": { + "default": 2020, + "description": "Renewable capacities are based on existing capacities reported by IRENA (IRENASTAT) for the specified year.", + "type": "integer" + }, + "expansion_limit": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "boolean" + } + ], + "default": false, + "description": "Artificially limit maximum IRENA capacities to a factor. For example, an `expansion_limit: 1.1` means 110% of capacities. If false are chosen, the estimated renewable potentials determine by the workflow are used." + }, + "technology_mapping": { + "description": "Configuration for `electricity.estimate_renewable_capacities.technology_mapping` settings.", + "properties": { + "Offshore": { + "default": "offwind-ac", + "description": "PyPSA-Eur carrier that is considered for existing offshore wind technology (IRENA, GEM).", + "type": "string" + }, + "Onshore": { + "default": "onwind", + "description": "PyPSA-Eur carrier that is considered for existing onshore wind capacities (IRENA, GEM).", + "type": "string" + }, + "PV": { + "default": "solar", + "description": "PyPSA-Eur carrier that is considered for existing solar PV capacities (IRENA, GEM).", + "type": "string" + } + } + } + } + }, + "_ExtendableCarriersConfig": { + "description": "Configuration for `electricity.extendable_carriers` settings.", + "properties": { + "Generator": { + "description": "Defines existing or non-existing conventional and renewable power plants to be extendable during the optimization. Conventional generators can only be built/expanded where already existent today. If a listed conventional carrier is not included in the `conventional_carriers` list, the lower limit of the capacity expansion is set to 0.", + "items": { + "type": "string" + }, + "type": "array" + }, + "StorageUnit": { + "description": "Adds extendable storage units (battery and/or hydrogen) at every node/bus after clustering without capacity limits and with zero initial capacity.", + "items": { + "type": "string" + }, + "type": "array" + }, + "Store": { + "description": "Adds extendable storage units (battery and/or hydrogen) at every node/bus after clustering without capacity limits and with zero initial capacity.", + "items": { + "type": "string" + }, + "type": "array" + }, + "Link": { + "description": "Adds extendable links (H2 pipelines only) at every connection where there are lines or HVDC links without capacity limits and with zero initial capacity. Hydrogen pipelines require hydrogen storage to be modelled as `Store`.", + "items": { + "type": "string" + }, + "type": "array" + } + } + }, + "_FillGapsConfig": { + "description": "Configuration for `load.fill_gaps` settings.", + "properties": { + "enable": { + "default": true, + "description": "Whether to fill gaps using interpolation for small gaps and time shift for large gaps.", + "type": "boolean" + }, + "interpolate_limit": { + "default": 3, + "description": "Maximum gap size (consecutive nans) which interpolated linearly.", + "type": "integer" + }, + "time_shift_for_large_gaps": { + "default": "1w", + "description": "Periods which are used for copying time-slices in order to fill large gaps of nans. Have to be valid `pandas` period strings.", + "type": "string" + } + } + }, + "_FillValuesConfig": { + "description": "Configuration for `costs.fill_values` settings.", + "properties": { + "FOM": { + "default": 0, + "description": "Default fixed operation and maintenance cost.", + "type": "number" + }, + "VOM": { + "default": 0, + "description": "Default variable operation and maintenance cost.", + "type": "number" + }, + "efficiency": { + "default": 1, + "description": "Default efficiency.", + "type": "number" + }, + "fuel": { + "default": 0, + "description": "Default fuel cost.", + "type": "number" + }, + "investment": { + "default": 0, + "description": "Default investment cost.", + "type": "number" + }, + "lifetime": { + "default": 25, + "description": "Default lifetime in years.", + "type": "integer" + }, + "CO2 intensity": { + "default": 0, + "description": "Default CO2 intensity.", + "type": "number" + }, + "discount rate": { + "default": 0.07, + "description": "Default discount rate.", + "type": "number" + }, + "standing losses": { + "default": 0, + "description": "Default standing losses.", + "type": "number" + } + } + }, + "_HydroConfig": { + "description": "Configuration for hydropower.", + "properties": { + "cutout": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": "default", + "description": "Specifies the weather data cutout file(s) to use." + }, + "carriers": { + "description": "Specifies the types of hydro power plants to build per-unit availability time series for. 'ror' stands for run-of-river plants, 'PHS' represents pumped-hydro storage, and 'hydro' stands for hydroelectric dams.", + "items": { + "type": "string" + }, + "type": "array" + }, + "PHS_max_hours": { + "default": 6, + "description": "Maximum state of charge capacity of the pumped-hydro storage (PHS) in terms of hours at full output capacity `p_nom`. Cf. `PyPSA documentation `_.", + "markdownDescription": "Maximum state of charge capacity of the pumped-hydro storage (PHS) in terms of hours at full output capacity `p_nom`. Cf. [PyPSA documentation ](https://pypsa.readthedocs.io/en/latest/components.html#storage-unit).", + "type": "number" + }, + "hydro_max_hours": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "default": "energy_capacity_totals_by_country", + "description": "Maximum state of charge capacity of the pumped-hydro storage (PHS) in terms of hours at full output capacity `p_nom` or heuristically determined. Cf. `PyPSA documentation `_.", + "markdownDescription": "Maximum state of charge capacity of the pumped-hydro storage (PHS) in terms of hours at full output capacity `p_nom` or heuristically determined. Cf. [PyPSA documentation ](https://pypsa.readthedocs.io/en/latest/components.html#storage-unit)." + }, + "flatten_dispatch": { + "default": false, + "description": "Consider an upper limit for the hydro dispatch. The limit is given by the average capacity factor plus the buffer given in `flatten_dispatch_buffer`.", + "type": "boolean" + }, + "flatten_dispatch_buffer": { + "default": 0.2, + "description": "If `flatten_dispatch` is true, specify the value added above the average capacity factor.", + "type": "number" + }, + "clip_min_inflow": { + "default": 1.0, + "description": "To avoid too small values in the inflow time series, values below this threshold (MW) are set to zero.", + "type": "number" + }, + "eia_norm_year": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "integer" + } + ], + "default": false, + "description": "To specify a specific year by which hydro inflow is normed that deviates from the snapshots' year." + }, + "eia_correct_by_capacity": { + "default": false, + "description": "Correct EIA annual hydro generation data by installed capacity.", + "type": "boolean" + }, + "eia_approximate_missing": { + "default": false, + "description": "Approximate hydro generation data for years not included in EIA dataset through a regression based on annual runoff.", + "type": "boolean" + } + } + }, + "_ImportsConfig": { + "description": "Configuration for `sector.imports` settings.", + "properties": { + "enable": { + "default": false, + "description": "Add option to include renewable energy imports.", + "type": "boolean" + }, + "limit": { + "default": null, + "description": "Maximum allowed renewable energy imports (TWh).", + "type": "number" + }, + "limit_sense": { + "default": "<=", + "description": "Sense of the limit.", + "type": "string" + }, + "price": { + "additionalProperties": { + "type": "number" + }, + "description": "Price for importing renewable energy of carrier.", + "type": "object" + } + } + }, + "_IncludeConfig": { + "description": "Configuration for `transmission_projects.include` settings.", + "properties": { + "tyndp2020": { + "default": true, + "description": "Whether to integrate the TYNDP 2020 dataset.", + "type": "boolean" + }, + "nep": { + "default": true, + "description": "Whether to integrate the German network development plan dataset.", + "type": "boolean" + }, + "manual": { + "default": true, + "description": "Whether to integrate the manually added transmission projects. They are taken from the previously existing links_tyndp.csv file.", + "type": "boolean" + } + } + }, + "_LimitMaxGrowthConfig": { + "description": "Configuration for `sector.limit_max_growth` settings.", + "properties": { + "enable": { + "default": false, + "description": "Add option to limit the maximum growth of a carrier.", + "type": "boolean" + }, + "factor": { + "default": 1.3, + "description": "The maximum growth factor of a carrier (e.g. 1.3 allows 30% larger than max historic growth).", + "type": "number" + }, + "max_growth": { + "additionalProperties": { + "type": "number" + }, + "description": "The historic maximum growth of a carrier.", + "type": "object" + }, + "max_relative_growth": { + "additionalProperties": { + "type": "number" + }, + "description": "The historic maximum relative growth of a carrier.", + "type": "object" + } + } + }, + "_MaxHoursConfig": { + "description": "Configuration for `electricity.max_hours` settings.", + "properties": { + "battery": { + "default": 6, + "description": "Maximum state of charge capacity of the battery in terms of hours at full output capacity `p_nom`. Cf. `PyPSA documentation `_.", + "markdownDescription": "Maximum state of charge capacity of the battery in terms of hours at full output capacity `p_nom`. Cf. [PyPSA documentation ](https://pypsa.readthedocs.io/en/latest/components.html#storage-unit).", + "type": "number" + }, + "H2": { + "default": 168, + "description": "Maximum state of charge capacity of the hydrogen storage in terms of hours at full output capacity `p_nom`. Cf. `PyPSA documentation `_.", + "markdownDescription": "Maximum state of charge capacity of the hydrogen storage in terms of hours at full output capacity `p_nom`. Cf. [PyPSA documentation ](https://pypsa.readthedocs.io/en/latest/components.html#storage-unit).", + "type": "number" + } + } + }, + "_MethanolConfig": { + "description": "Configuration for `sector.methanol` settings.", + "properties": { + "regional_methanol_demand": { + "default": false, + "description": "Spatially resolve methanol demand. Set to true if regional CO2 constraints needed.", + "type": "boolean" + }, + "methanol_reforming": { + "default": false, + "description": "Add methanol reforming.", + "type": "boolean" + }, + "methanol_reforming_cc": { + "default": false, + "description": "Add methanol reforming with carbon capture.", + "type": "boolean" + }, + "methanol_to_kerosene": { + "default": false, + "description": "Add methanol to kerosene.", + "type": "boolean" + }, + "methanol_to_power": { + "additionalProperties": { + "type": "boolean" + }, + "description": "Add different methanol to power technologies.", + "type": "object" + }, + "biomass_to_methanol": { + "default": true, + "description": "Add biomass to methanol.", + "type": "boolean" + }, + "biomass_to_methanol_cc": { + "default": false, + "description": "Add biomass to methanol with carbon capture.", + "type": "boolean" + } + } + }, + "_ModelKwargsConfig": { + "description": "Configuration for `solving.options.model_kwargs` settings.", + "properties": { + "solver_dir": { + "default": "", + "description": "Absolute path to the directory where linopy saves files.", + "type": "string" + } + } + }, + "_OETCConfig": { + "description": "Configuration for `solving.oetc` settings (Open Energy Transition Computing cluster support).", + "properties": { + "name": { + "default": "pypsa-eur", + "description": "Name identifier for the OETC job.", + "type": "string" + }, + "authentication_server_url": { + "default": "", + "description": "URL of the OETC authentication server for job submission.", + "type": "string" + }, + "orchestrator_server_url": { + "default": "", + "description": "URL of the OETC orchestrator server for job management.", + "type": "string" + }, + "cpu_cores": { + "default": 8, + "description": "Number of CPU cores to request for the OETC job. (includes RAM amount at the moment with a factor of 8)", + "type": "integer" + }, + "disk_space_gb": { + "default": 50, + "description": "Amount of disk space in gigabytes to request for the OETC job.", + "type": "integer" + }, + "delete_worker_on_error": { + "default": true, + "description": "Whether to delete the worker instance when an error occurs during job execution.", + "type": "boolean" + } + } + }, + "_OffwindConfig": { + "description": "Configuration for offshore wind.", + "properties": { + "cutout": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": "default", + "description": "Specifies the weather data cutout file(s) to use." + }, + "resource": { + "description": "Configuration for wind resource settings.", + "properties": { + "method": { + "default": "wind", + "description": "A superordinate technology type.", + "type": "string" + }, + "turbine": { + "anyOf": [ + { + "type": "string" + }, + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + } + ], + "description": "Specifies the turbine type and its characteristic power curve. Can be a string or a dictionary with years as keys which denote the year another turbine model becomes available." + }, + "smooth": { + "default": false, + "description": "Switch to apply a gaussian kernel density smoothing to the power curve.", + "type": "boolean" + }, + "add_cutout_windspeed": { + "default": true, + "description": "Whether to add cutout windspeed data.", + "type": "boolean" + } + }, + "required": [ + "turbine" + ] + }, + "resource_classes": { + "default": 1, + "description": "Number of resource classes per clustered region.", + "type": "integer" + }, + "capacity_per_sqkm": { + "default": 2, + "description": "Allowable density of wind turbine placement.", + "type": "number" + }, + "correction_factor": { + "default": 0.8855, + "description": "Correction factor for capacity factor time series.", + "type": "number" + }, + "corine": { + "description": "Specifies areas according to CORINE Land Cover codes which are generally eligible for AC-connected offshore wind turbine placement.", + "items": { + "type": "integer" + }, + "type": "array" + }, + "luisa": { + "anyOf": [ + { + "type": "boolean" + }, + { + "items": { + "type": "integer" + }, + "type": "array" + } + ], + "default": false, + "description": "Specifies areas according to the LUISA Base Map codes which are generally eligible for AC-connected offshore wind turbine placement." + }, + "natura": { + "default": true, + "description": "Switch to exclude `Natura 2000 `_ natural protection areas. Area is excluded if `true`.", + "markdownDescription": "Switch to exclude [Natura 2000 ](https://en.wikipedia.org/wiki/Natura_2000) natural protection areas. Area is excluded if `true`.", + "type": "boolean" + }, + "ship_threshold": { + "default": 400, + "description": "Ship density threshold from which areas are excluded.", + "type": "number" + }, + "max_depth": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Maximum sea water depth in meters at which wind turbines can be built. Maritime areas with deeper waters are excluded in the process of calculating the AC-connected offshore wind potential." + }, + "min_depth": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Minimum water depth in meters." + }, + "max_shore_distance": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Maximum distance to the shore in meters above which wind turbines cannot be built. Such areas are excluded in the process of calculating the AC-connected offshore wind potential." + }, + "min_shore_distance": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Minimum distance to the shore in meters below which wind turbines cannot be built. Such areas close to the shore are excluded in the process of calculating the AC-connected offshore wind potential." + }, + "excluder_resolution": { + "default": 200, + "description": "Resolution in meters on which to perform geographical eligibility analysis.", + "type": "number" + }, + "clip_p_max_pu": { + "default": 0.01, + "description": "To avoid too small values in the renewables` per-unit availability time series values below this threshold are set to zero.", + "type": "number" + }, + "landfall_length": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "default": 20, + "description": "Fixed length of the cable connection that is onshorelandfall in km. If 'centroid', the length is calculated as the distance to centroid of the onshore bus." + } + } + }, + "_OnwindConfig": { + "description": "Configuration for onshore wind.", + "properties": { + "cutout": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": "default", + "description": "Specifies the weather data cutout file(s) to use." + }, + "resource": { + "description": "Configuration for wind resource settings.", + "properties": { + "method": { + "default": "wind", + "description": "A superordinate technology type.", + "type": "string" + }, + "turbine": { + "anyOf": [ + { + "type": "string" + }, + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + } + ], + "description": "Specifies the turbine type and its characteristic power curve. Can be a string or a dictionary with years as keys which denote the year another turbine model becomes available." + }, + "smooth": { + "default": false, + "description": "Switch to apply a gaussian kernel density smoothing to the power curve.", + "type": "boolean" + }, + "add_cutout_windspeed": { + "default": true, + "description": "Whether to add cutout windspeed data.", + "type": "boolean" + } + }, + "required": [ + "turbine" + ] + }, + "resource_classes": { + "default": 1, + "description": "Number of resource classes per clustered region.", + "type": "integer" + }, + "capacity_per_sqkm": { + "default": 3, + "description": "Allowable density of wind turbine placement.", + "type": "number" + }, + "correction_factor": { + "default": 1.0, + "description": "Correction factor for capacity factor time series.", + "type": "number" + }, + "corine": { + "description": "Configuration for CORINE land cover settings.", + "properties": { + "grid_codes": { + "description": "Specifies areas according to CORINE Land Cover codes which are generally eligible for wind turbine placement.", + "items": { + "type": "integer" + }, + "type": "array" + }, + "distance": { + "default": 1000, + "description": "Distance in meters to keep from areas specified in `distance_grid_codes`.", + "type": "number" + }, + "distance_grid_codes": { + "description": "Specifies areas according to CORINE Land Cover codes to which wind turbines must maintain a distance specified in the setting `distance`.", + "items": { + "type": "integer" + }, + "type": "array" + } + }, + "required": [ + "grid_codes" + ] + }, + "luisa": { + "anyOf": [ + { + "type": "boolean" + }, + { + "additionalProperties": true, + "type": "object" + } + ], + "default": false, + "description": "LUISA land cover configuration." + }, + "natura": { + "default": true, + "description": "Switch to exclude `Natura 2000 `_ natural protection areas. Area is excluded if `true`.", + "markdownDescription": "Switch to exclude [Natura 2000 ](https://en.wikipedia.org/wiki/Natura_2000) natural protection areas. Area is excluded if `true`.", + "type": "boolean" + }, + "excluder_resolution": { + "default": 100, + "description": "Resolution in meters on which to perform geographical eligibility analysis.", + "type": "number" + }, + "clip_p_max_pu": { + "default": 0.01, + "description": "To avoid too small values in the renewables` per-unit availability time series values below this threshold are set to zero.", + "type": "number" + } + } + }, + "_OperationalReserveConfig": { + "description": "Configuration for `electricity.operational_reserve` settings.", + "properties": { + "activate": { + "default": false, + "description": "Whether to take operational reserve requirements into account during optimisation.", + "type": "boolean" + }, + "epsilon_load": { + "default": 0.02, + "description": "share of total load.", + "type": "number" + }, + "epsilon_vres": { + "default": 0.02, + "description": "share of total renewable supply.", + "type": "number" + }, + "contingency": { + "default": 4000, + "description": "Fixed reserve capacity (MW).", + "type": "number" + } + } + }, + "_OrientationConfig": { + "description": "Configuration for `solar_thermal.orientation` settings.", + "properties": { + "slope": { + "default": 45.0, + "description": "The angle between the ground and the panels.", + "type": "number" + }, + "azimuth": { + "default": 180.0, + "description": "The angle between the North and the sun with panels on the local horizon.", + "type": "number" + } + } + }, + "_PostDiscretizationConfig": { + "description": "Configuration for `solving.options.post_discretization` settings.", + "properties": { + "enable": { + "default": false, + "description": "Switch to enable post-discretization of the network. Disabled by default.", + "type": "boolean" + }, + "line_unit_size": { + "default": 1700, + "description": "Discrete unit size of lines in MW.", + "type": "number" + }, + "line_threshold": { + "default": 0.3, + "description": "The threshold relative to the discrete line unit size beyond which to round up to the next unit.", + "type": "number" + }, + "link_unit_size": { + "additionalProperties": { + "type": "number" + }, + "description": "Discrete unit size of links in MW by carrier (given in dictionary style).", + "type": "object" + }, + "link_threshold": { + "additionalProperties": { + "type": "number" + }, + "description": "The threshold relative to the discrete link unit size beyond which to round up to the next unit by carrier (given in dictionary style).", + "type": "object" + }, + "fractional_last_unit_size": { + "default": false, + "description": "When true, links and lines can be built up to p_nom_max. When false, they can only be built up to a multiple of the unit size.", + "type": "boolean" + } + } + }, + "_PrepareKwargsConfig": { + "description": "Configuration for `atlite.cutouts.{name}.prepare_kwargs` settings.", + "properties": { + "features": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "When freshly building a cutout, retrieve data only for those features. If not defined, it defaults to all available features." + }, + "sarah_dir": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Path to the location where SARAH-2 or SARAH-3 data is stored; SARAH data requires a manual separate download, see the `atlite documentation `_ for details. Required for building cutouts with SARAH, not required for ERA5 cutouts.", + "markdownDescription": "Path to the location where SARAH-2 or SARAH-3 data is stored; SARAH data requires a manual separate download, see the [atlite documentation ](https://atlite.readthedocs.io) for details. Required for building cutouts with SARAH, not required for ERA5 cutouts." + }, + "monthly_requests": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Whether to use monthly requests for ERA5 data when building the cutout. Helpful to avoid running into request limits with large cutouts." + }, + "tmpdir": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Path to a temporary directory where intermediate files are stored when building the cutout. Helpful when building large cutouts." + } + } + }, + "_ResidentialHeatConfig": { + "description": "Configuration for `sector.residential_heat` settings.", + "properties": { + "dsm": { + "description": "Configuration for `sector.residential_heat.dsm` settings.", + "properties": { + "enable": { + "default": false, + "description": "Enable residential heat demand-side management that allows heating systems to provide flexibility by shifting demand within configurable time periods. Models building thermal mass as energy storage.", + "type": "boolean" + }, + "direction": { + "description": "'overheat-undercool' means both pre-heating and delayed heating are allowed. 'overheat' allows only pre-heating where buildings are heated up above target temperature and then allowed to cool down, while 'undercool' allows only delayed heating where buildings can cool below target temperature and then be heated up again.", + "items": { + "type": "string" + }, + "type": "array" + }, + "restriction_value": { + "additionalProperties": { + "type": "number" + }, + "description": "Maximum state of charge (as fraction) for heat flexibility storage representing available thermal buffer capacity in buildings. Set to 0 for no flexibility or to 1.0 to assume that the entire heating demand can contribute to flexibility.", + "type": "object" + }, + "restriction_time": { + "description": "Checkpoint hours (0-23) at which heat flexibility storage must return to baseline state of charge, i.e. the residence surplus or missing heat be balanced. Time is the local time for each country and bus. Default: [10, 22] creates 12-hour periods with checkpoints at 10am and 10pm.", + "items": { + "type": "integer" + }, + "type": "array" + } + } + } + } + }, + "_ResidentialHeatDsmConfig": { + "description": "Configuration for `sector.residential_heat.dsm` settings.", + "properties": { + "enable": { + "default": false, + "description": "Enable residential heat demand-side management that allows heating systems to provide flexibility by shifting demand within configurable time periods. Models building thermal mass as energy storage.", + "type": "boolean" + }, + "direction": { + "description": "'overheat-undercool' means both pre-heating and delayed heating are allowed. 'overheat' allows only pre-heating where buildings are heated up above target temperature and then allowed to cool down, while 'undercool' allows only delayed heating where buildings can cool below target temperature and then be heated up again.", + "items": { + "type": "string" + }, + "type": "array" + }, + "restriction_value": { + "additionalProperties": { + "type": "number" + }, + "description": "Maximum state of charge (as fraction) for heat flexibility storage representing available thermal buffer capacity in buildings. Set to 0 for no flexibility or to 1.0 to assume that the entire heating demand can contribute to flexibility.", + "type": "object" + }, + "restriction_time": { + "description": "Checkpoint hours (0-23) at which heat flexibility storage must return to baseline state of charge, i.e. the residence surplus or missing heat be balanced. Time is the local time for each country and bus. Default: [10, 22] creates 12-hour periods with checkpoints at 10am and 10pm.", + "items": { + "type": "integer" + }, + "type": "array" + } + } + }, + "_RetrofittingConfig": { + "description": "Configuration for `sector.retrofitting` settings.", + "properties": { + "retro_endogen": { + "default": false, + "description": "Add retrofitting as an endogenous system which co-optimise space heat savings.", + "type": "boolean" + }, + "cost_factor": { + "default": 1.0, + "description": "Weight costs for building renovation.", + "type": "number" + }, + "interest_rate": { + "default": 0.04, + "description": "The interest rate for investment in building components.", + "type": "number" + }, + "annualise_cost": { + "default": true, + "description": "Annualise the investment costs of retrofitting.", + "type": "boolean" + }, + "tax_weighting": { + "default": false, + "description": "Weight the costs of retrofitting depending on taxes in countries.", + "type": "boolean" + }, + "construction_index": { + "default": true, + "description": "Weight the costs of retrofitting depending on labour/material costs per country.", + "type": "boolean" + } + } + }, + "_ScenariosConfig": { + "description": "Configuration for `run.scenarios` level.", + "properties": { + "enable": { + "default": false, + "description": "Switch to select whether workflow should generate scenarios based on ``file``.", + "type": "boolean" + }, + "file": { + "default": "config/scenarios.yaml", + "description": "Path to the scenario yaml file. The scenario file contains config overrides for each scenario. In order to be taken account, ``run: scenarios`` has to be set to ``true`` and ``run: name`` has to be a subset of top level keys given in the scenario file. In order to automatically create a `scenario.yaml` file based on a combination of settings, alter and use the ``config/create_scenarios.py`` script in the ``config`` directory.", + "examples": [ + "config/scenarios.yaml" + ], + "type": "string" + } + } + }, + "_SharedResourcesConfig": { + "description": "Configuration for `run.shared_resources` level.", + "properties": { + "policy": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "string" + } + ], + "default": false, + "description": "Boolean switch to select whether resources should be shared across runs. If a string is passed, this is used as a subdirectory name for shared resources. If set to 'base', only resources before creating the elec.nc file are shared.", + "examples": [ + false, + "base" + ] + }, + "exclude": { + "description": "For the case shared_resources=base, specify additional files that should not be shared across runs.", + "items": { + "type": "string" + }, + "type": "array" + } + } + }, + "_SimplifyNetworkConfig": { + "description": "Configuration for `clustering.simplify_network` settings.", + "properties": { + "to_substations": { + "default": false, + "description": "Aggregates all nodes without power injection (positive or negative, i.e. demand or generation) to electrically closest ones.", + "type": "boolean" + }, + "exclude_carriers": { + "description": "List of carriers which will not be aggregated. If empty, all carriers will be aggregated.", + "items": { + "type": "string" + }, + "type": "array" + }, + "remove_stubs": { + "default": true, + "description": "Controls whether radial parts of the network should be recursively aggregated. Defaults to true.", + "type": "boolean" + }, + "remove_stubs_across_borders": { + "default": false, + "description": "Controls whether radial parts of the network should be recursively aggregated across borders. Defaults to true.", + "type": "boolean" + } + } + }, + "_SolarConfig": { + "description": "Configuration for solar PV.", + "properties": { + "cutout": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": "default", + "description": "Specifies the weather data cutout file(s) to use." + }, + "resource": { + "description": "Configuration for solar resource settings.", + "properties": { + "method": { + "default": "pv", + "description": "A superordinate technology type.", + "type": "string" + }, + "panel": { + "anyOf": [ + { + "type": "string" + }, + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + } + ], + "default": "CSi", + "description": "Specifies the solar panel technology and its characteristic attributes. Can be a string or a dictionary with years as keys which denote the year another panel model becomes available." + }, + "orientation": { + "additionalProperties": { + "type": "number" + }, + "description": "Panel orientation with slope and azimuth.", + "type": "object" + }, + "tracking": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Tracking type (e.g., 'horizontal')." + } + } + }, + "resource_classes": { + "default": 1, + "description": "Number of resource classes per clustered region.", + "type": "integer" + }, + "capacity_per_sqkm": { + "default": 5.1, + "description": "Allowable density of solar panel placement.", + "type": "number" + }, + "correction_factor": { + "default": 1.0, + "description": "A correction factor for the capacity factor (availability) time series.", + "type": "number" + }, + "corine": { + "description": "Specifies areas according to CORINE Land Cover codes which are generally eligible for solar panel placement.", + "items": { + "type": "integer" + }, + "type": "array" + }, + "luisa": { + "anyOf": [ + { + "type": "boolean" + }, + { + "items": { + "type": "integer" + }, + "type": "array" + } + ], + "default": false, + "description": "Specifies areas according to the LUISA Base Map codes which are generally eligible for solar panel placement." + }, + "natura": { + "default": true, + "description": "Switch to exclude `Natura 2000 `_ natural protection areas. Area is excluded if `true`.", + "markdownDescription": "Switch to exclude [Natura 2000 ](https://en.wikipedia.org/wiki/Natura_2000) natural protection areas. Area is excluded if `true`.", + "type": "boolean" + }, + "excluder_resolution": { + "default": 100, + "description": "Resolution in meters on which to perform geographical eligibility analysis.", + "type": "number" + }, + "clip_p_max_pu": { + "default": 0.01, + "description": "To avoid too small values in the renewables` per-unit availability time series values below this threshold are set to zero.", + "type": "number" + } + } + }, + "_SolarResourceConfig": { + "description": "Configuration for solar resource settings.", + "properties": { + "method": { + "default": "pv", + "description": "A superordinate technology type.", + "type": "string" + }, + "panel": { + "anyOf": [ + { + "type": "string" + }, + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + } + ], + "default": "CSi", + "description": "Specifies the solar panel technology and its characteristic attributes. Can be a string or a dictionary with years as keys which denote the year another panel model becomes available." + }, + "orientation": { + "additionalProperties": { + "type": "number" + }, + "description": "Panel orientation with slope and azimuth.", + "type": "object" + }, + "tracking": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Tracking type (e.g., 'horizontal')." + } + } + }, + "_SolidBiomassImportConfig": { + "description": "Configuration for `sector.solid_biomass_import` settings.", + "properties": { + "enable": { + "default": false, + "description": "Add option to include solid biomass imports.", + "type": "boolean" + }, + "price": { + "default": 54, + "description": "Price for importing solid biomass (currency/MWh).", + "type": "number" + }, + "max_amount": { + "default": 1390, + "description": "Maximum solid biomass import potential (TWh).", + "type": "number" + }, + "upstream_emissions_factor": { + "default": 0.1, + "description": "Upstream emissions of solid biomass imports.", + "type": "number" + } + } + }, + "_SolverConfig": { + "description": "Configuration for `solving.solver` settings.", + "properties": { + "name": { + "default": "gurobi", + "description": "Solver to use for optimisation problems in the workflow; e.g. clustering and linear optimal power flow.", + "type": "string" + }, + "options": { + "default": "gurobi-default", + "description": "Link to specific parameter settings.", + "type": "string" + } + } + }, + "_SolvingOptionsConfig": { + "description": "Configuration for `solving.options` settings.", + "properties": { + "clip_p_max_pu": { + "default": 0.01, + "description": "To avoid too small values in the renewables` per-unit availability time series values below this threshold are set to zero.", + "type": "number" + }, + "load_shedding": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "number" + } + ], + "default": false, + "description": "Add generators with very high marginal cost to simulate load shedding and avoid problem infeasibilities. If load shedding is a float, it denotes the marginal cost in EUR/kWh." + }, + "curtailment_mode": { + "default": false, + "description": "Fixes the dispatch profiles of generators with time-varying p_max_pu by setting `p_min_pu = p_max_pu` and adds an auxiliary curtailment generator (with negative sign to absorb excess power) at every AC bus. This can speed up the solving process as the curtailment decision is aggregated into a single generator per region. Defaults to `false`.", + "type": "boolean" + }, + "noisy_costs": { + "default": true, + "description": "Add random noise to marginal cost of generators by :math:`\\mathcal{U}(0.009,0,011)` and capital cost of lines and links by :math:`\\mathcal{U}(0.09,0,11)`.", + "type": "boolean" + }, + "skip_iterations": { + "default": true, + "description": "Skip iterating, do not update impedances of branches. Defaults to true.", + "type": "boolean" + }, + "rolling_horizon": { + "default": false, + "description": "Switch for rule `solve_operations_network` whether to optimize the network in a rolling horizon manner, where the snapshot range is split into slices of size `horizon` which are solved consecutively. This setting has currently no effect on sector-coupled networks.", + "type": "boolean" + }, + "seed": { + "default": 123, + "description": "Random seed for increased deterministic behaviour.", + "type": "integer" + }, + "custom_extra_functionality": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": "../data/custom_extra_functionality.py", + "description": "Path to a Python file with custom extra functionality code to be injected into the solving rules of the workflow relative to `rules` directory." + }, + "io_api": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Passed to linopy and determines the API used to communicate with the solver. With the `'lp'` and `'mps'` options linopy passes a file to the solver; with the `'direct'` option (only supported for HIGHS and Gurobi) linopy uses an in-memory python API resulting in better performance." + }, + "track_iterations": { + "default": false, + "description": "Flag whether to store the intermediate branch capacities and objective function values are recorded for each iteration in `network.lines['s_nom_opt_X']` (where `X` labels the iteration)", + "type": "boolean" + }, + "min_iterations": { + "default": 2, + "description": "Minimum number of solving iterations in between which resistance and reactence (`x/r`) are updated for branches according to `s_nom_opt` of the previous run.", + "type": "integer" + }, + "max_iterations": { + "default": 3, + "description": "Maximum number of solving iterations in between which resistance and reactence (`x/r`) are updated for branches according to `s_nom_opt` of the previous run.", + "type": "integer" + }, + "transmission_losses": { + "default": 2, + "description": "Add piecewise linear approximation of transmission losses based on n tangents. Defaults to 0, which means losses are ignored.", + "type": "integer" + }, + "linearized_unit_commitment": { + "default": true, + "description": "Whether to optimise using the linearized unit commitment formulation.", + "type": "boolean" + }, + "horizon": { + "default": 365, + "description": "Number of snapshots to consider in each iteration. Defaults to 100.", + "type": "integer" + }, + "post_discretization": { + "description": "Configuration for `solving.options.post_discretization` settings.", + "properties": { + "enable": { + "default": false, + "description": "Switch to enable post-discretization of the network. Disabled by default.", + "type": "boolean" + }, + "line_unit_size": { + "default": 1700, + "description": "Discrete unit size of lines in MW.", + "type": "number" + }, + "line_threshold": { + "default": 0.3, + "description": "The threshold relative to the discrete line unit size beyond which to round up to the next unit.", + "type": "number" + }, + "link_unit_size": { + "additionalProperties": { + "type": "number" + }, + "description": "Discrete unit size of links in MW by carrier (given in dictionary style).", + "type": "object" + }, + "link_threshold": { + "additionalProperties": { + "type": "number" + }, + "description": "The threshold relative to the discrete link unit size beyond which to round up to the next unit by carrier (given in dictionary style).", + "type": "object" + }, + "fractional_last_unit_size": { + "default": false, + "description": "When true, links and lines can be built up to p_nom_max. When false, they can only be built up to a multiple of the unit size.", + "type": "boolean" + } + } + }, + "keep_files": { + "default": false, + "description": "Whether to keep LPs and MPS files after solving.", + "type": "boolean" + }, + "model_kwargs": { + "description": "Configuration for `solving.options.model_kwargs` settings.", + "properties": { + "solver_dir": { + "default": "", + "description": "Absolute path to the directory where linopy saves files.", + "type": "string" + } + } + } + } + }, + "_TechnologyMappingConfig": { + "description": "Configuration for `electricity.estimate_renewable_capacities.technology_mapping` settings.", + "properties": { + "Offshore": { + "default": "offwind-ac", + "description": "PyPSA-Eur carrier that is considered for existing offshore wind technology (IRENA, GEM).", + "type": "string" + }, + "Onshore": { + "default": "onwind", + "description": "PyPSA-Eur carrier that is considered for existing onshore wind capacities (IRENA, GEM).", + "type": "string" + }, + "PV": { + "default": "solar", + "description": "PyPSA-Eur carrier that is considered for existing solar PV capacities (IRENA, GEM).", + "type": "string" + } + } + }, + "_TemporalConfig": { + "description": "Configuration for `clustering.temporal` settings.", + "properties": { + "resolution_elec": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "string" + } + ], + "default": false, + "description": "Resample the time-resolution by averaging over every `n` snapshots in `prepare_network`. **Warning:** This option should currently only be used with electricity-only networks, not for sector-coupled networks." + }, + "resolution_sector": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "string" + } + ], + "default": false, + "description": "Resample the time-resolution by averaging over every `n` snapshots in `prepare_sector_network`." + } + } + }, + "_TransmissionEfficiencyConfig": { + "description": "Configuration for `sector.transmission_efficiency` settings.", + "properties": { + "enable": { + "description": "Switch to select the carriers for which transmission efficiency is to be added. Carriers not listed assume lossless transmission.", + "items": { + "type": "string" + }, + "type": "array" + }, + "DC": { + "additionalProperties": { + "type": "number" + }, + "description": "DC transmission efficiency.", + "type": "object" + }, + "H2 pipeline": { + "additionalProperties": { + "type": "number" + }, + "description": "H2 pipeline transmission efficiency.", + "type": "object" + }, + "gas pipeline": { + "additionalProperties": { + "type": "number" + }, + "description": "Gas pipeline transmission efficiency.", + "type": "object" + }, + "electricity distribution grid": { + "additionalProperties": { + "type": "number" + }, + "description": "Electricity distribution grid efficiency.", + "type": "object" + } + } + }, + "_UserAgentConfig": { + "description": "Configuration for `overpass_api.user_agent` settings.", + "properties": { + "project_name": { + "default": "PyPSA-Eur", + "description": "Project name used to identify the user agent of the Overpass API requests.", + "type": "string" + }, + "email": { + "default": "contact@pypsa.org", + "description": "Contact email address for the project using the Overpass API.", + "type": "string" + }, + "website": { + "default": "https://github.com/PyPSA/pypsa-eur", + "description": "Website URL for the project using the Overpass API.", + "type": "string" + } + } + }, + "_WindResourceConfig": { + "description": "Configuration for wind resource settings.", + "properties": { + "method": { + "default": "wind", + "description": "A superordinate technology type.", + "type": "string" + }, + "turbine": { + "anyOf": [ + { + "type": "string" + }, + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + } + ], + "description": "Specifies the turbine type and its characteristic power curve. Can be a string or a dictionary with years as keys which denote the year another turbine model becomes available." + }, + "smooth": { + "default": false, + "description": "Switch to apply a gaussian kernel density smoothing to the power curve.", + "type": "boolean" + }, + "add_cutout_windspeed": { + "default": true, + "description": "Whether to add cutout windspeed data.", + "type": "boolean" + } + }, + "required": [ + "turbine" + ] + } + }, + "additionalProperties": true, + "description": "Combined configuration schema for PyPSA-EUR.", + "properties": { + "version": { + "default": "v2025.07.0", + "description": "Version of PyPSA-Eur. Descriptive only.", + "type": "string" + }, + "tutorial": { + "default": false, + "description": "Switch to retrieve the tutorial data set instead of the full data set.", + "type": "boolean" + }, + "logging": { + "description": "Configuration for top level `logging` settings.", + "properties": { + "level": { + "default": "INFO", + "description": "Restrict console outputs to all infos, warning or errors only", + "enum": [ + "DEBUG", + "INFO", + "WARNING", + "ERROR", + "CRITICAL" + ], + "type": "string" + }, + "format": { + "default": "%(levelname)s:%(name)s:%(message)s", + "description": "Custom format for log messages. See `LogRecord `_ attributes.", + "markdownDescription": "Custom format for log messages. See [LogRecord ](https://docs.python.org/3/library/logging.html#logging.LogRecord) attributes.", + "type": "string" + } + } + }, + "remote": { + "description": "Configuration for top level `remote` settings.", + "properties": { + "ssh": { + "default": "", + "description": "Optionally specify the SSH of a remote cluster to be synchronized.", + "type": "string" + }, + "path": { + "default": "", + "description": "Optionally specify the file path within the remote cluster to be synchronized.", + "type": "string" + } + } + }, + "run": { + "description": "Configuration for top level `run` settings.", + "properties": { + "prefix": { + "default": "", + "description": "Prefix for the run name which is used as a top-layer directory name in the results and resources folders.", + "type": "string" + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": "", + "description": "Specify a name for your run. Results will be stored under this name. If ``scenario: enable:`` is set to ``true``, the name must contain a subset of scenario names defined in ``scenario: file:``. If the name is 'all', all defined scenarios will be run.", + "examples": [ + "my-pypsa-eur-run" + ] + }, + "scenarios": { + "description": "Configuration for `run.scenarios` level.", + "properties": { + "enable": { + "default": false, + "description": "Switch to select whether workflow should generate scenarios based on ``file``.", + "type": "boolean" + }, + "file": { + "default": "config/scenarios.yaml", + "description": "Path to the scenario yaml file. The scenario file contains config overrides for each scenario. In order to be taken account, ``run: scenarios`` has to be set to ``true`` and ``run: name`` has to be a subset of top level keys given in the scenario file. In order to automatically create a `scenario.yaml` file based on a combination of settings, alter and use the ``config/create_scenarios.py`` script in the ``config`` directory.", + "examples": [ + "config/scenarios.yaml" + ], + "type": "string" + } + } + }, + "disable_progressbar": { + "default": false, + "description": "Switch to select whether progressbar should be disabled.", + "type": "boolean" + }, + "shared_resources": { + "description": "Configuration for `run.shared_resources` level.", + "properties": { + "policy": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "string" + } + ], + "default": false, + "description": "Boolean switch to select whether resources should be shared across runs. If a string is passed, this is used as a subdirectory name for shared resources. If set to 'base', only resources before creating the elec.nc file are shared.", + "examples": [ + false, + "base" + ] + }, + "exclude": { + "description": "For the case shared_resources=base, specify additional files that should not be shared across runs.", + "items": { + "type": "string" + }, + "type": "array" + } + } + }, + "use_shadow_directory": { + "default": false, + "description": "Set to ``true`` (default) if snakemake shadow directories (``shallow``) should be used. Set to ``false`` if problems occur.", + "examples": [ + true + ], + "type": "boolean" + } + } + }, + "foresight": { + "default": "overnight", + "description": "Configuration for `foresight` settings.", + "enum": [ + "overnight", + "myopic", + "perfect" + ], + "type": "string" + }, + "scenario": { + "description": "Configuration for top level `scenario` settings.", + "properties": { + "clusters": { + "description": "List of ``{clusters}`` wildcards to run. Use 'adm' for administrative clustering mode, 'all' for all nodes.", + "items": { + "anyOf": [ + { + "type": "integer" + }, + { + "enum": [ + "adm", + "all" + ], + "type": "string" + } + ] + }, + "type": "array" + }, + "opts": { + "description": "List of ``{opts}`` wildcards to run.", + "items": { + "type": "string" + }, + "type": "array" + }, + "sector_opts": { + "description": "List of ``{sector_opts}`` wildcards to run.", + "items": { + "type": "string" + }, + "type": "array" + }, + "planning_horizons": { + "description": "List of ``{planning_horizon}`` wildcards to run.", + "items": { + "type": "integer" + }, + "type": "array" + } + } + }, + "countries": { + "default": [ + "AL", + "AT", + "BA", + "BE", + "BG", + "CH", + "CZ", + "DE", + "DK", + "EE", + "ES", + "FI", + "FR", + "GB", + "GR", + "HR", + "HU", + "IE", + "IT", + "LT", + "LU", + "LV", + "ME", + "MK", + "NL", + "NO", + "PL", + "PT", + "RO", + "RS", + "SE", + "SI", + "SK", + "XK" + ], + "description": "Configuration for `countries` settings.", + "items": { + "type": "string" + }, + "type": "array" + }, + "snapshots": { + "description": "Configuration for `snapshots` settings.", + "properties": { + "start": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": "2013-01-01", + "description": "Left bound of date range." + }, + "end": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": "2014-01-01", + "description": "Right bound of date range." + }, + "inclusive": { + "anyOf": [ + { + "enum": [ + "left", + "right", + "both" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": "left", + "description": "Make the time interval closed to the `left`, `right`, or both sides `both` or neither side `None`." + } + } + }, + "enable": { + "description": "Configuration for `enable` settings.", + "properties": { + "drop_leap_day": { + "default": true, + "description": "Switch to drop February 29 from all time-dependent data in leap years.", + "type": "boolean" + } + } + }, + "co2_budget": { + "additionalProperties": { + "type": "number" + }, + "description": "Configuration for `co2_budget` settings.", + "type": "object" + }, + "electricity": { + "description": "Configuration for `electricity` settings.", + "properties": { + "voltages": { + "description": "Voltage levels to consider.", + "items": { + "type": "number" + }, + "type": "array" + }, + "base_network": { + "default": "osm", + "description": "Specify the underlying base network, i.e. GridKit (based on ENTSO-E web map extract), OpenStreetMap (OSM), or TYNDP.", + "enum": [ + "entsoegridkit", + "osm", + "tyndp" + ], + "type": "string" + }, + "gaslimit_enable": { + "default": false, + "description": "Add an overall absolute gas limit configured in `electricity: gaslimit`.", + "type": "boolean" + }, + "gaslimit": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "boolean" + } + ], + "default": false, + "description": "Global gas usage limit." + }, + "co2limit_enable": { + "default": false, + "description": "Add an overall absolute carbon-dioxide emissions limit configured in `electricity: co2limit` in `prepare_network`. **Warning:** This option should currently only be used with electricity-only networks, not for sector-coupled networks.", + "type": "boolean" + }, + "co2limit": { + "default": 77500000.0, + "description": "Cap on total annual system carbon dioxide emissions.", + "type": "number" + }, + "co2base": { + "default": 1487000000.0, + "description": "Reference value of total annual system carbon dioxide emissions if relative emission reduction target is specified in `{opts}` wildcard.", + "type": "number" + }, + "operational_reserve": { + "description": "Configuration for `electricity.operational_reserve` settings.", + "properties": { + "activate": { + "default": false, + "description": "Whether to take operational reserve requirements into account during optimisation.", + "type": "boolean" + }, + "epsilon_load": { + "default": 0.02, + "description": "share of total load.", + "type": "number" + }, + "epsilon_vres": { + "default": 0.02, + "description": "share of total renewable supply.", + "type": "number" + }, + "contingency": { + "default": 4000, + "description": "Fixed reserve capacity (MW).", + "type": "number" + } + } + }, + "max_hours": { + "description": "Configuration for `electricity.max_hours` settings.", + "properties": { + "battery": { + "default": 6, + "description": "Maximum state of charge capacity of the battery in terms of hours at full output capacity `p_nom`. Cf. `PyPSA documentation `_.", + "markdownDescription": "Maximum state of charge capacity of the battery in terms of hours at full output capacity `p_nom`. Cf. [PyPSA documentation ](https://pypsa.readthedocs.io/en/latest/components.html#storage-unit).", + "type": "number" + }, + "H2": { + "default": 168, + "description": "Maximum state of charge capacity of the hydrogen storage in terms of hours at full output capacity `p_nom`. Cf. `PyPSA documentation `_.", + "markdownDescription": "Maximum state of charge capacity of the hydrogen storage in terms of hours at full output capacity `p_nom`. Cf. [PyPSA documentation ](https://pypsa.readthedocs.io/en/latest/components.html#storage-unit).", + "type": "number" + } + } + }, + "extendable_carriers": { + "description": "Configuration for `electricity.extendable_carriers` settings.", + "properties": { + "Generator": { + "description": "Defines existing or non-existing conventional and renewable power plants to be extendable during the optimization. Conventional generators can only be built/expanded where already existent today. If a listed conventional carrier is not included in the `conventional_carriers` list, the lower limit of the capacity expansion is set to 0.", + "items": { + "type": "string" + }, + "type": "array" + }, + "StorageUnit": { + "description": "Adds extendable storage units (battery and/or hydrogen) at every node/bus after clustering without capacity limits and with zero initial capacity.", + "items": { + "type": "string" + }, + "type": "array" + }, + "Store": { + "description": "Adds extendable storage units (battery and/or hydrogen) at every node/bus after clustering without capacity limits and with zero initial capacity.", + "items": { + "type": "string" + }, + "type": "array" + }, + "Link": { + "description": "Adds extendable links (H2 pipelines only) at every connection where there are lines or HVDC links without capacity limits and with zero initial capacity. Hydrogen pipelines require hydrogen storage to be modelled as `Store`.", + "items": { + "type": "string" + }, + "type": "array" + } + } + }, + "powerplants_filter": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "boolean" + } + ], + "default": "(DateOut >= 2024 or DateOut != DateOut) and not (Country == 'Germany' and Fueltype == 'Nuclear')", + "description": "Filter query for the default powerplant database." + }, + "custom_powerplants": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "boolean" + } + ], + "default": false, + "description": "Filter query for the custom powerplant database." + }, + "everywhere_powerplants": { + "description": "List of conventional power plants to add to every node in the model with zero initial capacity. To be used in combination with `extendable_carriers` to allow for building conventional powerplants irrespective of existing locations.", + "items": { + "type": "string" + }, + "type": "array" + }, + "conventional_carriers": { + "description": "List of conventional power plants to include in the model from `resources/powerplants_s_{clusters}.csv`. If an included carrier is also listed in `extendable_carriers`, the capacity is taken as a lower bound.", + "items": { + "type": "string" + }, + "type": "array" + }, + "renewable_carriers": { + "description": "List of renewable generators to include in the model.", + "items": { + "type": "string" + }, + "type": "array" + }, + "estimate_renewable_capacities": { + "description": "Configuration for `electricity.estimate_renewable_capacities` settings.", + "properties": { + "enable": { + "default": true, + "description": "Activate routine to estimate renewable capacities in rule `add_electricity`. This option should not be used in combination with pathway planning `foresight: myopic` or `foresight: perfect` as renewable capacities are added differently in `add_existing_baseyear`.", + "type": "boolean" + }, + "from_gem": { + "default": true, + "description": "Add renewable capacities from `Global Energy Monitor's Global Solar Power Tracker `_ and `Global Energy Monitor's Global Wind Power Tracker `_.", + "markdownDescription": "Add renewable capacities from [Global Energy Monitor's Global Solar Power Tracker ](https://globalenergymonitor.org/projects/global-solar-power-tracker/) and [Global Energy Monitor's Global Wind Power Tracker ](https://globalenergymonitor.org/projects/global-wind-power-tracker/).", + "type": "boolean" + }, + "year": { + "default": 2020, + "description": "Renewable capacities are based on existing capacities reported by IRENA (IRENASTAT) for the specified year.", + "type": "integer" + }, + "expansion_limit": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "boolean" + } + ], + "default": false, + "description": "Artificially limit maximum IRENA capacities to a factor. For example, an `expansion_limit: 1.1` means 110% of capacities. If false are chosen, the estimated renewable potentials determine by the workflow are used." + }, + "technology_mapping": { + "description": "Configuration for `electricity.estimate_renewable_capacities.technology_mapping` settings.", + "properties": { + "Offshore": { + "default": "offwind-ac", + "description": "PyPSA-Eur carrier that is considered for existing offshore wind technology (IRENA, GEM).", + "type": "string" + }, + "Onshore": { + "default": "onwind", + "description": "PyPSA-Eur carrier that is considered for existing onshore wind capacities (IRENA, GEM).", + "type": "string" + }, + "PV": { + "default": "solar", + "description": "PyPSA-Eur carrier that is considered for existing solar PV capacities (IRENA, GEM).", + "type": "string" + } + } + } + } + }, + "autarky": { + "description": "Configuration for `electricity.autarky` settings.", + "properties": { + "enable": { + "default": false, + "description": "Require each node to be autarkic by removing all lines and links.", + "type": "boolean" + }, + "by_country": { + "default": false, + "description": "Require each country to be autarkic by removing all cross-border lines and links. `electricity: autarky` must be enabled.", + "type": "boolean" + } + } + }, + "transmission_limit": { + "default": "vopt", + "description": "Limit on transmission expansion. The first part can be `v` (for setting a limit on line volume) or `c` (for setting a limit on line cost). The second part can be `opt` or a float bigger than one (e.g. 1.25). If `opt` is chosen line expansion is optimised according to its capital cost (where the choice `v` only considers overhead costs for HVDC transmission lines, while `c` uses more accurate costs distinguishing between overhead and underwater sections and including inverter pairs). The setting `v1.25` will limit the total volume of line expansion to 25% of currently installed capacities weighted by individual line lengths. The setting `c1.25` will allow to build a transmission network that costs no more than 25 % more than the current system.", + "type": "string" + } + } + }, + "atlite": { + "description": "Configuration for `atlite` settings.", + "properties": { + "default_cutout": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": "europe-2013-sarah3-era5", + "description": "Defines a default cutout. Can refer to a single cutout or a list of cutouts." + }, + "nprocesses": { + "default": 16, + "description": "Number of parallel processes in cutout preparation.", + "type": "integer" + }, + "show_progress": { + "default": false, + "description": "Whether progressbar for atlite conversion processes should be shown. False saves time.", + "type": "boolean" + }, + "cutouts": { + "additionalProperties": { + "description": "Configuration for a single cutout in `atlite.cutouts`.", + "properties": { + "module": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Source of the reanalysis weather dataset (e.g. `ERA5 `_ or `SARAH-3 `_).", + "markdownDescription": "Source of the reanalysis weather dataset (e.g. [ERA5 ](https://www.ecmwf.int/en/forecasts/datasets/reanalysis-datasets/era5) or [SARAH-3 ](https://wui.cmsaf.eu/safira/action/viewDoiDetails?acronym=SARAH_V002))." + }, + "x": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Range of longitudes [\u00b0] to download weather data for. Float interval within [-180, 180]. If not defined, it defaults to the spatial bounds of all bus shapes." + }, + "y": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Range of latitudes [\u00b0] to download weather data for. Float interval within [-90, 90]. If not defined, it defaults to the spatial bounds of all bus shapes." + }, + "dx": { + "anyOf": [ + { + "exclusiveMinimum": 0.25, + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Grid resolution [\u00b0] for longitude. Must be larger than 0.25\u00b0." + }, + "dy": { + "anyOf": [ + { + "exclusiveMinimum": 0.25, + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Grid resolution [\u00b0] for latitude. Must be larger than 0.25\u00b0." + }, + "time": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Time span to download weather data for. If not defined, it defaults to the time interval spanned by the snapshots." + }, + "chunks": { + "anyOf": [ + { + "description": "Configuration for `atlite.cutouts.{name}.chunks` settings.", + "properties": { + "time": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Chunk size for time dimension when preparing cutout." + } + } + }, + { + "type": "null" + } + ], + "default": null, + "description": "Chunking configuration for cutout preparation." + }, + "prepare_kwargs": { + "anyOf": [ + { + "description": "Configuration for `atlite.cutouts.{name}.prepare_kwargs` settings.", + "properties": { + "features": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "When freshly building a cutout, retrieve data only for those features. If not defined, it defaults to all available features." + }, + "sarah_dir": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Path to the location where SARAH-2 or SARAH-3 data is stored; SARAH data requires a manual separate download, see the `atlite documentation `_ for details. Required for building cutouts with SARAH, not required for ERA5 cutouts.", + "markdownDescription": "Path to the location where SARAH-2 or SARAH-3 data is stored; SARAH data requires a manual separate download, see the [atlite documentation ](https://atlite.readthedocs.io) for details. Required for building cutouts with SARAH, not required for ERA5 cutouts." + }, + "monthly_requests": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Whether to use monthly requests for ERA5 data when building the cutout. Helpful to avoid running into request limits with large cutouts." + }, + "tmpdir": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Path to a temporary directory where intermediate files are stored when building the cutout. Helpful when building large cutouts." + } + } + }, + { + "type": "null" + } + ], + "default": null, + "description": "Dictionary of keyword arguments passed to ``atlite.Cutout.prepare()`` when building the cutout." + } + } + }, + "description": "Named cutout configurations.", + "type": "object" + } + } + }, + "renewable": { + "description": "Configuration for `renewable` settings.", + "properties": { + "onwind": { + "description": "Configuration for onshore wind.", + "properties": { + "cutout": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": "default", + "description": "Specifies the weather data cutout file(s) to use." + }, + "resource": { + "description": "Configuration for wind resource settings.", + "properties": { + "method": { + "default": "wind", + "description": "A superordinate technology type.", + "type": "string" + }, + "turbine": { + "anyOf": [ + { + "type": "string" + }, + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + } + ], + "description": "Specifies the turbine type and its characteristic power curve. Can be a string or a dictionary with years as keys which denote the year another turbine model becomes available." + }, + "smooth": { + "default": false, + "description": "Switch to apply a gaussian kernel density smoothing to the power curve.", + "type": "boolean" + }, + "add_cutout_windspeed": { + "default": true, + "description": "Whether to add cutout windspeed data.", + "type": "boolean" + } + }, + "required": [ + "turbine" + ] + }, + "resource_classes": { + "default": 1, + "description": "Number of resource classes per clustered region.", + "type": "integer" + }, + "capacity_per_sqkm": { + "default": 3, + "description": "Allowable density of wind turbine placement.", + "type": "number" + }, + "correction_factor": { + "default": 1.0, + "description": "Correction factor for capacity factor time series.", + "type": "number" + }, + "corine": { + "description": "Configuration for CORINE land cover settings.", + "properties": { + "grid_codes": { + "description": "Specifies areas according to CORINE Land Cover codes which are generally eligible for wind turbine placement.", + "items": { + "type": "integer" + }, + "type": "array" + }, + "distance": { + "default": 1000, + "description": "Distance in meters to keep from areas specified in `distance_grid_codes`.", + "type": "number" + }, + "distance_grid_codes": { + "description": "Specifies areas according to CORINE Land Cover codes to which wind turbines must maintain a distance specified in the setting `distance`.", + "items": { + "type": "integer" + }, + "type": "array" + } + }, + "required": [ + "grid_codes" + ] + }, + "luisa": { + "anyOf": [ + { + "type": "boolean" + }, + { + "additionalProperties": true, + "type": "object" + } + ], + "default": false, + "description": "LUISA land cover configuration." + }, + "natura": { + "default": true, + "description": "Switch to exclude `Natura 2000 `_ natural protection areas. Area is excluded if `true`.", + "markdownDescription": "Switch to exclude [Natura 2000 ](https://en.wikipedia.org/wiki/Natura_2000) natural protection areas. Area is excluded if `true`.", + "type": "boolean" + }, + "excluder_resolution": { + "default": 100, + "description": "Resolution in meters on which to perform geographical eligibility analysis.", + "type": "number" + }, + "clip_p_max_pu": { + "default": 0.01, + "description": "To avoid too small values in the renewables` per-unit availability time series values below this threshold are set to zero.", + "type": "number" + } + } + }, + "offwind-ac": { + "description": "Configuration for offshore wind.", + "properties": { + "cutout": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": "default", + "description": "Specifies the weather data cutout file(s) to use." + }, + "resource": { + "description": "Configuration for wind resource settings.", + "properties": { + "method": { + "default": "wind", + "description": "A superordinate technology type.", + "type": "string" + }, + "turbine": { + "anyOf": [ + { + "type": "string" + }, + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + } + ], + "description": "Specifies the turbine type and its characteristic power curve. Can be a string or a dictionary with years as keys which denote the year another turbine model becomes available." + }, + "smooth": { + "default": false, + "description": "Switch to apply a gaussian kernel density smoothing to the power curve.", + "type": "boolean" + }, + "add_cutout_windspeed": { + "default": true, + "description": "Whether to add cutout windspeed data.", + "type": "boolean" + } + }, + "required": [ + "turbine" + ] + }, + "resource_classes": { + "default": 1, + "description": "Number of resource classes per clustered region.", + "type": "integer" + }, + "capacity_per_sqkm": { + "default": 2, + "description": "Allowable density of wind turbine placement.", + "type": "number" + }, + "correction_factor": { + "default": 0.8855, + "description": "Correction factor for capacity factor time series.", + "type": "number" + }, + "corine": { + "description": "Specifies areas according to CORINE Land Cover codes which are generally eligible for AC-connected offshore wind turbine placement.", + "items": { + "type": "integer" + }, + "type": "array" + }, + "luisa": { + "anyOf": [ + { + "type": "boolean" + }, + { + "items": { + "type": "integer" + }, + "type": "array" + } + ], + "default": false, + "description": "Specifies areas according to the LUISA Base Map codes which are generally eligible for AC-connected offshore wind turbine placement." + }, + "natura": { + "default": true, + "description": "Switch to exclude `Natura 2000 `_ natural protection areas. Area is excluded if `true`.", + "markdownDescription": "Switch to exclude [Natura 2000 ](https://en.wikipedia.org/wiki/Natura_2000) natural protection areas. Area is excluded if `true`.", + "type": "boolean" + }, + "ship_threshold": { + "default": 400, + "description": "Ship density threshold from which areas are excluded.", + "type": "number" + }, + "max_depth": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Maximum sea water depth in meters at which wind turbines can be built. Maritime areas with deeper waters are excluded in the process of calculating the AC-connected offshore wind potential." + }, + "min_depth": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Minimum water depth in meters." + }, + "max_shore_distance": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Maximum distance to the shore in meters above which wind turbines cannot be built. Such areas are excluded in the process of calculating the AC-connected offshore wind potential." + }, + "min_shore_distance": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Minimum distance to the shore in meters below which wind turbines cannot be built. Such areas close to the shore are excluded in the process of calculating the AC-connected offshore wind potential." + }, + "excluder_resolution": { + "default": 200, + "description": "Resolution in meters on which to perform geographical eligibility analysis.", + "type": "number" + }, + "clip_p_max_pu": { + "default": 0.01, + "description": "To avoid too small values in the renewables` per-unit availability time series values below this threshold are set to zero.", + "type": "number" + }, + "landfall_length": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "default": 20, + "description": "Fixed length of the cable connection that is onshorelandfall in km. If 'centroid', the length is calculated as the distance to centroid of the onshore bus." + } + } + }, + "offwind-dc": { + "description": "Configuration for offshore wind.", + "properties": { + "cutout": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": "default", + "description": "Specifies the weather data cutout file(s) to use." + }, + "resource": { + "description": "Configuration for wind resource settings.", + "properties": { + "method": { + "default": "wind", + "description": "A superordinate technology type.", + "type": "string" + }, + "turbine": { + "anyOf": [ + { + "type": "string" + }, + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + } + ], + "description": "Specifies the turbine type and its characteristic power curve. Can be a string or a dictionary with years as keys which denote the year another turbine model becomes available." + }, + "smooth": { + "default": false, + "description": "Switch to apply a gaussian kernel density smoothing to the power curve.", + "type": "boolean" + }, + "add_cutout_windspeed": { + "default": true, + "description": "Whether to add cutout windspeed data.", + "type": "boolean" + } + }, + "required": [ + "turbine" + ] + }, + "resource_classes": { + "default": 1, + "description": "Number of resource classes per clustered region.", + "type": "integer" + }, + "capacity_per_sqkm": { + "default": 2, + "description": "Allowable density of wind turbine placement.", + "type": "number" + }, + "correction_factor": { + "default": 0.8855, + "description": "Correction factor for capacity factor time series.", + "type": "number" + }, + "corine": { + "description": "Specifies areas according to CORINE Land Cover codes which are generally eligible for AC-connected offshore wind turbine placement.", + "items": { + "type": "integer" + }, + "type": "array" + }, + "luisa": { + "anyOf": [ + { + "type": "boolean" + }, + { + "items": { + "type": "integer" + }, + "type": "array" + } + ], + "default": false, + "description": "Specifies areas according to the LUISA Base Map codes which are generally eligible for AC-connected offshore wind turbine placement." + }, + "natura": { + "default": true, + "description": "Switch to exclude `Natura 2000 `_ natural protection areas. Area is excluded if `true`.", + "markdownDescription": "Switch to exclude [Natura 2000 ](https://en.wikipedia.org/wiki/Natura_2000) natural protection areas. Area is excluded if `true`.", + "type": "boolean" + }, + "ship_threshold": { + "default": 400, + "description": "Ship density threshold from which areas are excluded.", + "type": "number" + }, + "max_depth": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Maximum sea water depth in meters at which wind turbines can be built. Maritime areas with deeper waters are excluded in the process of calculating the AC-connected offshore wind potential." + }, + "min_depth": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Minimum water depth in meters." + }, + "max_shore_distance": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Maximum distance to the shore in meters above which wind turbines cannot be built. Such areas are excluded in the process of calculating the AC-connected offshore wind potential." + }, + "min_shore_distance": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Minimum distance to the shore in meters below which wind turbines cannot be built. Such areas close to the shore are excluded in the process of calculating the AC-connected offshore wind potential." + }, + "excluder_resolution": { + "default": 200, + "description": "Resolution in meters on which to perform geographical eligibility analysis.", + "type": "number" + }, + "clip_p_max_pu": { + "default": 0.01, + "description": "To avoid too small values in the renewables` per-unit availability time series values below this threshold are set to zero.", + "type": "number" + }, + "landfall_length": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "default": 20, + "description": "Fixed length of the cable connection that is onshorelandfall in km. If 'centroid', the length is calculated as the distance to centroid of the onshore bus." + } + } + }, + "offwind-float": { + "description": "Configuration for offshore wind.", + "properties": { + "cutout": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": "default", + "description": "Specifies the weather data cutout file(s) to use." + }, + "resource": { + "description": "Configuration for wind resource settings.", + "properties": { + "method": { + "default": "wind", + "description": "A superordinate technology type.", + "type": "string" + }, + "turbine": { + "anyOf": [ + { + "type": "string" + }, + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + } + ], + "description": "Specifies the turbine type and its characteristic power curve. Can be a string or a dictionary with years as keys which denote the year another turbine model becomes available." + }, + "smooth": { + "default": false, + "description": "Switch to apply a gaussian kernel density smoothing to the power curve.", + "type": "boolean" + }, + "add_cutout_windspeed": { + "default": true, + "description": "Whether to add cutout windspeed data.", + "type": "boolean" + } + }, + "required": [ + "turbine" + ] + }, + "resource_classes": { + "default": 1, + "description": "Number of resource classes per clustered region.", + "type": "integer" + }, + "capacity_per_sqkm": { + "default": 2, + "description": "Allowable density of wind turbine placement.", + "type": "number" + }, + "correction_factor": { + "default": 0.8855, + "description": "Correction factor for capacity factor time series.", + "type": "number" + }, + "corine": { + "description": "Specifies areas according to CORINE Land Cover codes which are generally eligible for AC-connected offshore wind turbine placement.", + "items": { + "type": "integer" + }, + "type": "array" + }, + "luisa": { + "anyOf": [ + { + "type": "boolean" + }, + { + "items": { + "type": "integer" + }, + "type": "array" + } + ], + "default": false, + "description": "Specifies areas according to the LUISA Base Map codes which are generally eligible for AC-connected offshore wind turbine placement." + }, + "natura": { + "default": true, + "description": "Switch to exclude `Natura 2000 `_ natural protection areas. Area is excluded if `true`.", + "markdownDescription": "Switch to exclude [Natura 2000 ](https://en.wikipedia.org/wiki/Natura_2000) natural protection areas. Area is excluded if `true`.", + "type": "boolean" + }, + "ship_threshold": { + "default": 400, + "description": "Ship density threshold from which areas are excluded.", + "type": "number" + }, + "max_depth": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Maximum sea water depth in meters at which wind turbines can be built. Maritime areas with deeper waters are excluded in the process of calculating the AC-connected offshore wind potential." + }, + "min_depth": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Minimum water depth in meters." + }, + "max_shore_distance": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Maximum distance to the shore in meters above which wind turbines cannot be built. Such areas are excluded in the process of calculating the AC-connected offshore wind potential." + }, + "min_shore_distance": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Minimum distance to the shore in meters below which wind turbines cannot be built. Such areas close to the shore are excluded in the process of calculating the AC-connected offshore wind potential." + }, + "excluder_resolution": { + "default": 200, + "description": "Resolution in meters on which to perform geographical eligibility analysis.", + "type": "number" + }, + "clip_p_max_pu": { + "default": 0.01, + "description": "To avoid too small values in the renewables` per-unit availability time series values below this threshold are set to zero.", + "type": "number" + }, + "landfall_length": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "default": 20, + "description": "Fixed length of the cable connection that is onshorelandfall in km. If 'centroid', the length is calculated as the distance to centroid of the onshore bus." + } + } + }, + "solar": { + "description": "Configuration for solar PV.", + "properties": { + "cutout": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": "default", + "description": "Specifies the weather data cutout file(s) to use." + }, + "resource": { + "description": "Configuration for solar resource settings.", + "properties": { + "method": { + "default": "pv", + "description": "A superordinate technology type.", + "type": "string" + }, + "panel": { + "anyOf": [ + { + "type": "string" + }, + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + } + ], + "default": "CSi", + "description": "Specifies the solar panel technology and its characteristic attributes. Can be a string or a dictionary with years as keys which denote the year another panel model becomes available." + }, + "orientation": { + "additionalProperties": { + "type": "number" + }, + "description": "Panel orientation with slope and azimuth.", + "type": "object" + }, + "tracking": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Tracking type (e.g., 'horizontal')." + } + } + }, + "resource_classes": { + "default": 1, + "description": "Number of resource classes per clustered region.", + "type": "integer" + }, + "capacity_per_sqkm": { + "default": 5.1, + "description": "Allowable density of solar panel placement.", + "type": "number" + }, + "correction_factor": { + "default": 1.0, + "description": "A correction factor for the capacity factor (availability) time series.", + "type": "number" + }, + "corine": { + "description": "Specifies areas according to CORINE Land Cover codes which are generally eligible for solar panel placement.", + "items": { + "type": "integer" + }, + "type": "array" + }, + "luisa": { + "anyOf": [ + { + "type": "boolean" + }, + { + "items": { + "type": "integer" + }, + "type": "array" + } + ], + "default": false, + "description": "Specifies areas according to the LUISA Base Map codes which are generally eligible for solar panel placement." + }, + "natura": { + "default": true, + "description": "Switch to exclude `Natura 2000 `_ natural protection areas. Area is excluded if `true`.", + "markdownDescription": "Switch to exclude [Natura 2000 ](https://en.wikipedia.org/wiki/Natura_2000) natural protection areas. Area is excluded if `true`.", + "type": "boolean" + }, + "excluder_resolution": { + "default": 100, + "description": "Resolution in meters on which to perform geographical eligibility analysis.", + "type": "number" + }, + "clip_p_max_pu": { + "default": 0.01, + "description": "To avoid too small values in the renewables` per-unit availability time series values below this threshold are set to zero.", + "type": "number" + } + } + }, + "solar-hsat": { + "description": "Configuration for solar PV.", + "properties": { + "cutout": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": "default", + "description": "Specifies the weather data cutout file(s) to use." + }, + "resource": { + "description": "Configuration for solar resource settings.", + "properties": { + "method": { + "default": "pv", + "description": "A superordinate technology type.", + "type": "string" + }, + "panel": { + "anyOf": [ + { + "type": "string" + }, + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + } + ], + "default": "CSi", + "description": "Specifies the solar panel technology and its characteristic attributes. Can be a string or a dictionary with years as keys which denote the year another panel model becomes available." + }, + "orientation": { + "additionalProperties": { + "type": "number" + }, + "description": "Panel orientation with slope and azimuth.", + "type": "object" + }, + "tracking": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Tracking type (e.g., 'horizontal')." + } + } + }, + "resource_classes": { + "default": 1, + "description": "Number of resource classes per clustered region.", + "type": "integer" + }, + "capacity_per_sqkm": { + "default": 5.1, + "description": "Allowable density of solar panel placement.", + "type": "number" + }, + "correction_factor": { + "default": 1.0, + "description": "A correction factor for the capacity factor (availability) time series.", + "type": "number" + }, + "corine": { + "description": "Specifies areas according to CORINE Land Cover codes which are generally eligible for solar panel placement.", + "items": { + "type": "integer" + }, + "type": "array" + }, + "luisa": { + "anyOf": [ + { + "type": "boolean" + }, + { + "items": { + "type": "integer" + }, + "type": "array" + } + ], + "default": false, + "description": "Specifies areas according to the LUISA Base Map codes which are generally eligible for solar panel placement." + }, + "natura": { + "default": true, + "description": "Switch to exclude `Natura 2000 `_ natural protection areas. Area is excluded if `true`.", + "markdownDescription": "Switch to exclude [Natura 2000 ](https://en.wikipedia.org/wiki/Natura_2000) natural protection areas. Area is excluded if `true`.", + "type": "boolean" + }, + "excluder_resolution": { + "default": 100, + "description": "Resolution in meters on which to perform geographical eligibility analysis.", + "type": "number" + }, + "clip_p_max_pu": { + "default": 0.01, + "description": "To avoid too small values in the renewables` per-unit availability time series values below this threshold are set to zero.", + "type": "number" + } + } + }, + "hydro": { + "description": "Configuration for hydropower.", + "properties": { + "cutout": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": "default", + "description": "Specifies the weather data cutout file(s) to use." + }, + "carriers": { + "description": "Specifies the types of hydro power plants to build per-unit availability time series for. 'ror' stands for run-of-river plants, 'PHS' represents pumped-hydro storage, and 'hydro' stands for hydroelectric dams.", + "items": { + "type": "string" + }, + "type": "array" + }, + "PHS_max_hours": { + "default": 6, + "description": "Maximum state of charge capacity of the pumped-hydro storage (PHS) in terms of hours at full output capacity `p_nom`. Cf. `PyPSA documentation `_.", + "markdownDescription": "Maximum state of charge capacity of the pumped-hydro storage (PHS) in terms of hours at full output capacity `p_nom`. Cf. [PyPSA documentation ](https://pypsa.readthedocs.io/en/latest/components.html#storage-unit).", + "type": "number" + }, + "hydro_max_hours": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "default": "energy_capacity_totals_by_country", + "description": "Maximum state of charge capacity of the pumped-hydro storage (PHS) in terms of hours at full output capacity `p_nom` or heuristically determined. Cf. `PyPSA documentation `_.", + "markdownDescription": "Maximum state of charge capacity of the pumped-hydro storage (PHS) in terms of hours at full output capacity `p_nom` or heuristically determined. Cf. [PyPSA documentation ](https://pypsa.readthedocs.io/en/latest/components.html#storage-unit)." + }, + "flatten_dispatch": { + "default": false, + "description": "Consider an upper limit for the hydro dispatch. The limit is given by the average capacity factor plus the buffer given in `flatten_dispatch_buffer`.", + "type": "boolean" + }, + "flatten_dispatch_buffer": { + "default": 0.2, + "description": "If `flatten_dispatch` is true, specify the value added above the average capacity factor.", + "type": "number" + }, + "clip_min_inflow": { + "default": 1.0, + "description": "To avoid too small values in the inflow time series, values below this threshold (MW) are set to zero.", + "type": "number" + }, + "eia_norm_year": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "integer" + } + ], + "default": false, + "description": "To specify a specific year by which hydro inflow is normed that deviates from the snapshots' year." + }, + "eia_correct_by_capacity": { + "default": false, + "description": "Correct EIA annual hydro generation data by installed capacity.", + "type": "boolean" + }, + "eia_approximate_missing": { + "default": false, + "description": "Approximate hydro generation data for years not included in EIA dataset through a regression based on annual runoff.", + "type": "boolean" + } + } + } + } + }, + "conventional": { + "additionalProperties": true, + "description": "Configuration for `conventional` settings.", + "properties": { + "unit_commitment": { + "default": false, + "description": "Allow the overwrite of ramp_limit_up, ramp_limit_start_up, ramp_limit_shut_down, p_min_pu, min_up_time, min_down_time, and start_up_cost of conventional generators. Refer to the CSV file 'unit_commitment.csv'.", + "type": "boolean" + }, + "dynamic_fuel_price": { + "default": false, + "description": "Consider the monthly fluctuating fuel prices for each conventional generator. Refer to the CSV file 'data/validation/monthly_fuel_price.csv'.", + "type": "boolean" + }, + "nuclear": { + "additionalProperties": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + } + ] + }, + "description": "For any carrier/technology overwrite attributes as listed below.", + "type": "object" + } + } + }, + "lines": { + "description": "Configuration for `lines` settings.", + "properties": { + "types": { + "additionalProperties": { + "type": "string" + }, + "description": "Specifies line types to assume for the different voltage levels of the ENTSO-E grid extraction. Should normally handle voltage levels 220, 300, and 380 kV.", + "type": "object" + }, + "s_max_pu": { + "default": 0.7, + "description": "Correction factor for line capacities (`s_nom`) to approximate N-1 security and reserve capacity for reactive power flows.", + "type": "number" + }, + "s_nom_max": { + "default": null, + "description": "Global upper limit for the maximum capacity of each extendable line (MW).", + "type": "number" + }, + "max_extension": { + "default": 20000, + "description": "Upper limit for the extended capacity of each extendable line (MW).", + "type": "number" + }, + "length_factor": { + "default": 1.25, + "description": "Correction factor to account for the fact that buses are *not* connected by lines through air-line distance.", + "type": "number" + }, + "reconnect_crimea": { + "default": true, + "description": "Whether to reconnect Crimea to the Ukrainian grid.", + "type": "boolean" + }, + "under_construction": { + "default": "keep", + "description": "Specifies how to handle lines which are currently under construction.", + "enum": [ + "zero", + "remove", + "keep" + ], + "type": "string" + }, + "dynamic_line_rating": { + "description": "Configuration for `lines.dynamic_line_rating` settings.", + "properties": { + "activate": { + "default": false, + "description": "Whether to take dynamic line rating into account.", + "type": "boolean" + }, + "cutout": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": "default", + "description": "Specifies the weather data cutout file(s) to use." + }, + "correction_factor": { + "default": 0.95, + "description": "Factor to compensate for overestimation of wind speeds in hourly averaged wind data.", + "type": "number" + }, + "max_voltage_difference": { + "anyOf": [ + { + "type": "number" + }, + { + "const": false, + "type": "boolean" + } + ], + "default": false, + "description": "Maximum voltage angle difference in degrees or 'false' to disable." + }, + "max_line_rating": { + "anyOf": [ + { + "type": "number" + }, + { + "const": false, + "type": "boolean" + } + ], + "default": false, + "description": "Maximum line rating relative to nominal capacity without DLR, e.g. 1.3 or 'false' to disable." + } + } + } + } + }, + "links": { + "description": "Configuration for `links` settings.", + "properties": { + "p_max_pu": { + "default": 1.0, + "description": "Correction factor for link capacities `p_nom`.", + "type": "number" + }, + "p_min_pu": { + "default": -1.0, + "description": "Correction factor for link capacities `p_nom`.", + "type": "number" + }, + "p_nom_max": { + "default": null, + "description": "Global upper limit for the maximum capacity of each extendable DC link (MW).", + "type": "number" + }, + "max_extension": { + "default": 30000, + "description": "Upper limit for the extended capacity of each extendable DC link (MW).", + "type": "number" + }, + "length_factor": { + "default": 1.25, + "description": "Correction factor to account for the fact that buses are *not* connected by links through air-line distance.", + "type": "number" + }, + "under_construction": { + "default": "keep", + "description": "Specifies how to handle lines which are currently under construction.", + "enum": [ + "zero", + "remove", + "keep" + ], + "type": "string" + } + } + }, + "transmission_projects": { + "description": "Configuration for `transmission_projects` settings.", + "properties": { + "enable": { + "default": true, + "description": "Whether to integrate this transmission projects or not.", + "type": "boolean" + }, + "include": { + "description": "Configuration for `transmission_projects.include` settings.", + "properties": { + "tyndp2020": { + "default": true, + "description": "Whether to integrate the TYNDP 2020 dataset.", + "type": "boolean" + }, + "nep": { + "default": true, + "description": "Whether to integrate the German network development plan dataset.", + "type": "boolean" + }, + "manual": { + "default": true, + "description": "Whether to integrate the manually added transmission projects. They are taken from the previously existing links_tyndp.csv file.", + "type": "boolean" + } + } + }, + "skip": { + "description": "Type of lines to skip from all transmission projects. Possible values are: `upgraded_lines`, `upgraded_links`, `new_lines`, `new_links`.", + "items": { + "type": "string" + }, + "type": "array" + }, + "status": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "additionalProperties": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": "object" + } + ], + "description": "Status to include into the model as list or as dict with name of project and status to include. Possible values for status are `under_construction`, `in_permitting`, `confirmed`, `planned_not_yet_permitted`, `under_consideration`." + }, + "new_link_capacity": { + "default": "zero", + "description": "Whether to set the new link capacity to the provided capacity or set it to zero.", + "enum": [ + "zero", + "keep" + ], + "type": "string" + } + } + }, + "transformers": { + "description": "Configuration for `transformers` settings.", + "properties": { + "x": { + "default": 0.1, + "description": "Series reactance in per unit (p.u.), using `s_nom` as base power of the transformer. Overwritten if `type` is specified.", + "type": "number" + }, + "s_nom": { + "default": 2000.0, + "description": "Limit of apparent power which can pass through branch (MVA). Overwritten if `type` is specified.", + "type": "number" + }, + "type": { + "default": "", + "description": "Specifies transformer types to assume for the transformers of the ENTSO-E grid extraction.", + "type": "string" + } + } + }, + "load": { + "description": "Configuration for `load` settings.", + "properties": { + "fill_gaps": { + "description": "Configuration for `load.fill_gaps` settings.", + "properties": { + "enable": { + "default": true, + "description": "Whether to fill gaps using interpolation for small gaps and time shift for large gaps.", + "type": "boolean" + }, + "interpolate_limit": { + "default": 3, + "description": "Maximum gap size (consecutive nans) which interpolated linearly.", + "type": "integer" + }, + "time_shift_for_large_gaps": { + "default": "1w", + "description": "Periods which are used for copying time-slices in order to fill large gaps of nans. Have to be valid `pandas` period strings.", + "type": "string" + } + } + }, + "manual_adjustments": { + "default": true, + "description": "Whether to adjust the load data manually according to the function in `manual_adjustment`.", + "type": "boolean" + }, + "scaling_factor": { + "default": 1.0, + "description": "Global correction factor for the load time series.", + "type": "number" + }, + "fixed_year": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "boolean" + } + ], + "default": false, + "description": "To specify a fixed year for the load time series that deviates from the snapshots' year." + }, + "supplement_synthetic": { + "default": true, + "description": "Whether to supplement missing data for selected time period should be supplemented by synthetic data from `Zenodo `_.", + "markdownDescription": "Whether to supplement missing data for selected time period should be supplemented by synthetic data from [Zenodo ](https://zenodo.org/records/10820928).", + "type": "boolean" + }, + "distribution_key": { + "description": "Configuration for `load.distribution_key` settings.", + "properties": { + "gdp": { + "default": 0.6, + "description": "Weighting factor for the GDP data in the distribution key.", + "type": "number" + }, + "population": { + "default": 0.4, + "description": "Weighting factor for the population data in the distribution key.", + "type": "number" + } + } + } + } + }, + "pypsa_eur": { + "description": "Configuration for `pypsa_eur` settings.", + "properties": { + "Bus": { + "description": "Bus carriers to keep from PyPSA-Eur.", + "items": { + "type": "string" + }, + "type": "array" + }, + "Link": { + "description": "Link carriers to keep from PyPSA-Eur.", + "items": { + "type": "string" + }, + "type": "array" + }, + "Generator": { + "description": "Generator carriers to keep from PyPSA-Eur.", + "items": { + "type": "string" + }, + "type": "array" + }, + "StorageUnit": { + "description": "StorageUnit carriers to keep from PyPSA-Eur.", + "items": { + "type": "string" + }, + "type": "array" + }, + "Store": { + "description": "Store carriers to keep from PyPSA-Eur.", + "items": { + "type": "string" + }, + "type": "array" + } + } + }, + "energy": { + "description": "Configuration for `energy` settings.", + "properties": { + "energy_totals_year": { + "default": 2019, + "description": "The year for the sector energy use. The year must be available in the Eurostat report.", + "type": "integer" + }, + "base_emissions_year": { + "default": 1990, + "description": "The base year for the sector emissions. See `European Environment Agency (EEA) `_.", + "markdownDescription": "The base year for the sector emissions. See [European Environment Agency (EEA) ](https://www.eea.europa.eu/data-and-maps/data/national-emissions-reported-to-the-unfccc-and-to-the-eu-greenhouse-gas-monitoring-mechanism-16).", + "type": "integer" + }, + "emissions": { + "default": "CO2", + "description": "Specify which sectoral emissions are taken into account. Data derived from EEA. Currently only CO2 is implemented.", + "type": "string" + } + } + }, + "biomass": { + "description": "Configuration for `biomass` settings.", + "properties": { + "year": { + "default": 2030, + "description": "Year for which to retrieve biomass potential according to the assumptions of the `JRC ENSPRESO `_.", + "markdownDescription": "Year for which to retrieve biomass potential according to the assumptions of the [JRC ENSPRESO ](https://data.jrc.ec.europa.eu/dataset/74ed5a04-7d74-4807-9eab-b94774309d9f).", + "maximum": 2050, + "minimum": 2010, + "type": "integer" + }, + "scenario": { + "default": "ENS_Med", + "description": "Scenario for which to retrieve biomass potential. The scenario definition can be seen in `ENSPRESO_BIOMASS `_.", + "markdownDescription": "Scenario for which to retrieve biomass potential. The scenario definition can be seen in [ENSPRESO_BIOMASS ](https://cidportal.jrc.ec.europa.eu/ftp/jrc-opendata/ENSPRESO/ENSPRESO_BIOMASS.xlsx).", + "enum": [ + "ENS_Low", + "ENS_Med", + "ENS_High" + ], + "type": "string" + }, + "classes": { + "description": "Configuration for `biomass.classes` settings.", + "properties": { + "solid biomass": { + "description": "The comodity that are included as solid biomass.", + "items": { + "type": "string" + }, + "type": "array" + }, + "not included": { + "description": "The comodity that are not included as a biomass potential.", + "items": { + "type": "string" + }, + "type": "array" + }, + "biogas": { + "description": "The comodity that are included as biogas.", + "items": { + "type": "string" + }, + "type": "array" + }, + "municipal solid waste": { + "description": "The commodities that are included as municipal solid waste.", + "items": { + "type": "string" + }, + "type": "array" + } + } + }, + "share_unsustainable_use_retained": { + "additionalProperties": { + "type": "number" + }, + "description": "Share of unsustainable biomass use retained using primary production of Eurostat data as reference.", + "type": "object" + }, + "share_sustainable_potential_available": { + "additionalProperties": { + "type": "number" + }, + "description": "Share determines phase-in of ENSPRESO biomass potentials.", + "type": "object" + } + } + }, + "solar_thermal": { + "description": "Configuration for `solar_thermal` settings.", + "properties": { + "clearsky_model": { + "default": "simple", + "description": "Type of clearsky model for diffuse irradiation.", + "enum": [ + "simple", + "enhanced" + ], + "type": "string" + }, + "orientation": { + "description": "Configuration for `solar_thermal.orientation` settings.", + "properties": { + "slope": { + "default": 45.0, + "description": "The angle between the ground and the panels.", + "type": "number" + }, + "azimuth": { + "default": 180.0, + "description": "The angle between the North and the sun with panels on the local horizon.", + "type": "number" + } + } + }, + "cutout": { + "default": "default", + "description": "Name of the cutout to use for solar thermal calculations.", + "type": "string" + } + } + }, + "existing_capacities": { + "description": "Configuration for `existing_capacities` settings.", + "properties": { + "grouping_years_power": { + "description": "Intervals to group existing capacities for power.", + "items": { + "type": "integer" + }, + "type": "array" + }, + "grouping_years_heat": { + "description": "Intervals to group existing capacities for heat.", + "items": { + "type": "integer" + }, + "type": "array" + }, + "threshold_capacity": { + "default": 10, + "description": "Capacities (MW) of generators and links below threshold are removed during add_existing_capacities.", + "type": "number" + }, + "default_heating_lifetime": { + "default": 20, + "description": "Default lifetime for heating technologies (years).", + "type": "integer" + }, + "conventional_carriers": { + "description": "List of conventional power plants to include in the sectoral network.", + "items": { + "type": "string" + }, + "type": "array" + } + } + }, + "sector": { + "description": "Configuration for `sector` settings.", + "properties": { + "transport": { + "default": true, + "description": "Flag to include transport sector.", + "type": "boolean" + }, + "heating": { + "default": true, + "description": "Flag to include heating sector.", + "type": "boolean" + }, + "biomass": { + "default": true, + "description": "Flag to include biomass sector.", + "type": "boolean" + }, + "industry": { + "default": true, + "description": "Flag to include industry sector.", + "type": "boolean" + }, + "shipping": { + "default": true, + "description": "Flag to include shipping sector.", + "type": "boolean" + }, + "aviation": { + "default": true, + "description": "Flag to include aviation sector.", + "type": "boolean" + }, + "agriculture": { + "default": true, + "description": "Flag to include agriculture sector.", + "type": "boolean" + }, + "fossil_fuels": { + "default": true, + "description": "Flag to include imports of fossil fuels.", + "type": "boolean" + }, + "district_heating": { + "description": "Configuration for `sector.district_heating` settings.", + "properties": { + "potential": { + "anyOf": [ + { + "type": "number" + }, + { + "additionalProperties": { + "type": "number" + }, + "type": "object" + } + ], + "default": 0.6, + "description": "Maximum fraction of urban demand which can be supplied by district heating. If given as dictionary, specify one value per country modeled or provide a default value with key `default` to fill values for all unspecified countries." + }, + "progress": { + "additionalProperties": { + "type": "number" + }, + "description": "Increase of today's district heating demand to potential maximum district heating share. Progress = 0 means today's district heating share. Progress = 1 means maximum fraction of urban demand is supplied by district heating.", + "type": "object" + }, + "district_heating_loss": { + "default": 0.15, + "description": "Share increase in district heat demand in urban central due to heat losses.", + "type": "number" + }, + "supply_temperature_approximation": { + "additionalProperties": true, + "description": "Supply temperature approximation settings.", + "type": "object" + }, + "ptes": { + "additionalProperties": true, + "description": "Pit thermal energy storage settings.", + "type": "object" + }, + "ates": { + "additionalProperties": true, + "description": "Aquifer thermal energy storage settings.", + "type": "object" + }, + "heat_source_cooling": { + "default": 6, + "description": "Cooling of heat source for heat pumps.", + "type": "number" + }, + "heat_pump_cop_approximation": { + "additionalProperties": true, + "description": "Heat pump COP approximation settings.", + "type": "object" + }, + "limited_heat_sources": { + "additionalProperties": true, + "description": "Dictionary with names of limited heat sources (not air). Must be `river_water` / `geothermal` or another heat source in `Manz et al. 2024 `_.", + "markdownDescription": "Dictionary with names of limited heat sources (not air). Must be `river_water` / `geothermal` or another heat source in [Manz et al. 2024 ](https://www.sciencedirect.com/science/article/pii/S0960148124001769).", + "type": "object" + }, + "direct_utilisation_heat_sources": { + "description": "List of heat sources for direct heat utilisation in district heating. Must be in the keys of `heat_utilisation_potentials` (e.g. `geothermal`).", + "items": { + "type": "string" + }, + "type": "array" + }, + "temperature_limited_stores": { + "description": "List of names for stores used as limited heat sources.", + "items": { + "type": "string" + }, + "type": "array" + }, + "dh_areas": { + "additionalProperties": true, + "description": "District heating areas settings.", + "type": "object" + } + } + }, + "heat_pump_sources": { + "additionalProperties": { + "items": { + "type": "string" + }, + "type": "array" + }, + "description": "Heat pump sources by area.", + "type": "object" + }, + "residential_heat": { + "description": "Configuration for `sector.residential_heat` settings.", + "properties": { + "dsm": { + "description": "Configuration for `sector.residential_heat.dsm` settings.", + "properties": { + "enable": { + "default": false, + "description": "Enable residential heat demand-side management that allows heating systems to provide flexibility by shifting demand within configurable time periods. Models building thermal mass as energy storage.", + "type": "boolean" + }, + "direction": { + "description": "'overheat-undercool' means both pre-heating and delayed heating are allowed. 'overheat' allows only pre-heating where buildings are heated up above target temperature and then allowed to cool down, while 'undercool' allows only delayed heating where buildings can cool below target temperature and then be heated up again.", + "items": { + "type": "string" + }, + "type": "array" + }, + "restriction_value": { + "additionalProperties": { + "type": "number" + }, + "description": "Maximum state of charge (as fraction) for heat flexibility storage representing available thermal buffer capacity in buildings. Set to 0 for no flexibility or to 1.0 to assume that the entire heating demand can contribute to flexibility.", + "type": "object" + }, + "restriction_time": { + "description": "Checkpoint hours (0-23) at which heat flexibility storage must return to baseline state of charge, i.e. the residence surplus or missing heat be balanced. Time is the local time for each country and bus. Default: [10, 22] creates 12-hour periods with checkpoints at 10am and 10pm.", + "items": { + "type": "integer" + }, + "type": "array" + } + } + } + } + }, + "cluster_heat_buses": { + "default": true, + "description": "Cluster residential and service heat buses in `prepare_sector_network.py `_ to one to save memory.", + "markdownDescription": "Cluster residential and service heat buses in [prepare_sector_network.py ](https://github.com/PyPSA/pypsa-eur-sec/blob/master/scripts/prepare_sector_network.py) to one to save memory.", + "type": "boolean" + }, + "heat_demand_cutout": { + "default": "default", + "description": "Heat demand cutout.", + "type": "string" + }, + "bev_dsm_restriction_value": { + "default": 0.8, + "description": "Adds a lower state of charge (SOC) limit for battery electric vehicles (BEV) to manage its own energy demand (DSM). Located in `build_transport_demand.py `_. Set to 0 for no restriction on BEV DSM.", + "markdownDescription": "Adds a lower state of charge (SOC) limit for battery electric vehicles (BEV) to manage its own energy demand (DSM). Located in [build_transport_demand.py ](https://github.com/PyPSA/pypsa-eur-sec/blob/master/scripts/build_transport_demand.py). Set to 0 for no restriction on BEV DSM.", + "type": "number" + }, + "bev_dsm_restriction_time": { + "default": 7, + "description": "Time at which SOC of BEV has to be dsm_restriction_value.", + "type": "number" + }, + "transport_heating_deadband_upper": { + "default": 20.0, + "description": "The maximum temperature in the vehicle. At higher temperatures, the energy required for cooling in the vehicle increases.", + "type": "number" + }, + "transport_heating_deadband_lower": { + "default": 15.0, + "description": "The minimum temperature in the vehicle. At lower temperatures, the energy required for heating in the vehicle increases.", + "type": "number" + }, + "ICE_lower_degree_factor": { + "default": 0.375, + "description": "Share increase in energy demand in internal combustion engine (ICE) for each degree difference between the cold environment and the minimum temperature.", + "type": "number" + }, + "ICE_upper_degree_factor": { + "default": 1.6, + "description": "Share increase in energy demand in internal combustion engine (ICE) for each degree difference between the hot environment and the maximum temperature.", + "type": "number" + }, + "EV_lower_degree_factor": { + "default": 0.98, + "description": "Share increase in energy demand in electric vehicles (EV) for each degree difference between the cold environment and the minimum temperature.", + "type": "number" + }, + "EV_upper_degree_factor": { + "default": 0.63, + "description": "Share increase in energy demand in electric vehicles (EV) for each degree difference between the hot environment and the maximum temperature.", + "type": "number" + }, + "bev_dsm": { + "default": true, + "description": "Add the option for battery electric vehicles (BEV) to participate in demand-side management (DSM).", + "type": "boolean" + }, + "bev_dsm_availability": { + "default": 0.5, + "description": "The share for battery electric vehicles (BEV) that are able to do demand side management (DSM).", + "type": "number" + }, + "bev_energy": { + "default": 0.05, + "description": "The average size of battery electric vehicles (BEV) in MWh.", + "type": "number" + }, + "bev_charge_efficiency": { + "default": 0.9, + "description": "Battery electric vehicles (BEV) charge and discharge efficiency.", + "type": "number" + }, + "bev_charge_rate": { + "default": 0.011, + "description": "The power consumption for one electric vehicle (EV) in MWh. Value derived from 3-phase charger with 11 kW.", + "type": "number" + }, + "bev_avail_max": { + "default": 0.95, + "description": "The maximum share plugged-in availability for passenger electric vehicles.", + "type": "number" + }, + "bev_avail_mean": { + "default": 0.8, + "description": "The average share plugged-in availability for passenger electric vehicles.", + "type": "number" + }, + "v2g": { + "default": true, + "description": "Allows feed-in to grid from EV battery. This is only enabled if BEV demand-side management is enabled, and the share of vehicles participating is V2G is given by `bev_dsm_availability`.", + "type": "boolean" + }, + "land_transport_fuel_cell_share": { + "additionalProperties": { + "type": "number" + }, + "description": "The share of vehicles that uses fuel cells in a given year.", + "type": "object" + }, + "land_transport_electric_share": { + "additionalProperties": { + "type": "number" + }, + "description": "The share of vehicles that uses electric vehicles (EV) in a given year.", + "type": "object" + }, + "land_transport_ice_share": { + "additionalProperties": { + "type": "number" + }, + "description": "The share of vehicles that uses internal combustion engines (ICE) in a given year. What is not EV or FCEV is oil-fuelled ICE.", + "type": "object" + }, + "transport_electric_efficiency": { + "default": 53.19, + "description": "The conversion efficiencies of electric vehicles in transport.", + "type": "number" + }, + "transport_fuel_cell_efficiency": { + "default": 30.003, + "description": "The H2 conversion efficiencies of fuel cells in transport.", + "type": "number" + }, + "transport_ice_efficiency": { + "default": 16.0712, + "description": "The oil conversion efficiencies of internal combustion engine (ICE) in transport.", + "type": "number" + }, + "agriculture_machinery_electric_share": { + "default": 0.5, + "description": "The share for agricultural machinery that uses electricity.", + "type": "number" + }, + "agriculture_machinery_oil_share": { + "default": 0.5, + "description": "The share for agricultural machinery that uses oil.", + "type": "number" + }, + "agriculture_machinery_fuel_efficiency": { + "default": 0.7, + "description": "The efficiency of electric-powered machinery in the conversion of electricity to meet agricultural needs.", + "type": "number" + }, + "agriculture_machinery_electric_efficiency": { + "default": 0.3, + "description": "The efficiency of oil-powered machinery in the conversion of oil to meet agricultural needs.", + "type": "number" + }, + "MWh_MeOH_per_MWh_H2": { + "default": 0.8787, + "description": "The energy amount of the produced methanol per energy amount of hydrogen. From `DECHEMA (2017) `_, page 64.", + "markdownDescription": "The energy amount of the produced methanol per energy amount of hydrogen. From [DECHEMA (2017) ](https://dechema.de/dechema_media/Downloads/Positionspapiere/Technology_study_Low_carbon_energy_and_feedstock_for_the_European_chemical_industry-p-20002750.pdf), page 64.", + "type": "number" + }, + "MWh_MeOH_per_tCO2": { + "default": 4.0321, + "description": "The energy amount of the produced methanol per ton of CO2. From `DECHEMA (2017) `_, page 66.", + "markdownDescription": "The energy amount of the produced methanol per ton of CO2. From [DECHEMA (2017) ](https://dechema.de/dechema_media/Downloads/Positionspapiere/Technology_study_Low_carbon_energy_and_feedstock_for_the_European_chemical_industry-p-20002750.pdf), page 66.", + "type": "number" + }, + "MWh_MeOH_per_MWh_e": { + "default": 3.6907, + "description": "The energy amount of the produced methanol per energy amount of electricity. From `DECHEMA (2017) `_, page 64.", + "markdownDescription": "The energy amount of the produced methanol per energy amount of electricity. From [DECHEMA (2017) ](https://dechema.de/dechema_media/Downloads/Positionspapiere/Technology_study_Low_carbon_energy_and_feedstock_for_the_European_chemical_industry-p-20002750.pdf), page 64.", + "type": "number" + }, + "shipping_hydrogen_liquefaction": { + "default": false, + "description": "Whether to include liquefaction costs for hydrogen demand in shipping.", + "type": "boolean" + }, + "shipping_hydrogen_share": { + "additionalProperties": { + "type": "number" + }, + "description": "The share of ships powered by hydrogen in a given year.", + "type": "object" + }, + "shipping_methanol_share": { + "additionalProperties": { + "type": "number" + }, + "description": "The share of ships powered by methanol in a given year.", + "type": "object" + }, + "shipping_oil_share": { + "additionalProperties": { + "type": "number" + }, + "description": "The share of ships powered by oil in a given year.", + "type": "object" + }, + "shipping_methanol_efficiency": { + "default": 0.46, + "description": "The efficiency of methanol-powered ships in the conversion of methanol to meet shipping needs (propulsion). The efficiency increase from oil can be 10-15% higher according to the `IEA `_.", + "markdownDescription": "The efficiency of methanol-powered ships in the conversion of methanol to meet shipping needs (propulsion). The efficiency increase from oil can be 10-15% higher according to the [IEA ](https://www.iea-amf.org/app/webroot/files/file/Annex%20Reports/AMF_Annex_56.pdf).", + "type": "number" + }, + "shipping_oil_efficiency": { + "default": 0.4, + "description": "The efficiency of oil-powered ships in the conversion of oil to meet shipping needs (propulsion). Base value derived from 2011.", + "type": "number" + }, + "aviation_demand_factor": { + "default": 1.0, + "description": "The proportion of demand for aviation compared to today's consumption.", + "type": "number" + }, + "HVC_demand_factor": { + "default": 1.0, + "description": "The proportion of demand for high-value chemicals compared to today's consumption.", + "type": "number" + }, + "time_dep_hp_cop": { + "default": true, + "description": "Consider the time dependent coefficient of performance (COP) of the heat pump.", + "type": "boolean" + }, + "heat_pump_sink_T_individual_heating": { + "default": 55.0, + "description": "The temperature heat sink used in heat pumps based on DTU / large area radiators. The value is conservatively high to cover hot water and space heating in poorly-insulated buildings.", + "type": "number" + }, + "reduce_space_heat_exogenously": { + "default": true, + "description": "Influence on space heating demand by a certain factor (applied before losses in district heating).", + "type": "boolean" + }, + "reduce_space_heat_exogenously_factor": { + "additionalProperties": { + "type": "number" + }, + "description": "A positive factor can mean renovation or demolition of a building. If the factor is negative, it can mean an increase in floor area, increased thermal comfort, population growth. The default factors are determined by the `Eurocalc Homes and buildings decarbonization scenario `_.", + "markdownDescription": "A positive factor can mean renovation or demolition of a building. If the factor is negative, it can mean an increase in floor area, increased thermal comfort, population growth. The default factors are determined by the [Eurocalc Homes and buildings decarbonization scenario ](http://tool.european-calculator.eu/app/buildings/building-types-area/?levers=1ddd4444421213bdbbbddd44444ffffff11f411111221111211l212221).", + "type": "object" + }, + "retrofitting": { + "description": "Configuration for `sector.retrofitting` settings.", + "properties": { + "retro_endogen": { + "default": false, + "description": "Add retrofitting as an endogenous system which co-optimise space heat savings.", + "type": "boolean" + }, + "cost_factor": { + "default": 1.0, + "description": "Weight costs for building renovation.", + "type": "number" + }, + "interest_rate": { + "default": 0.04, + "description": "The interest rate for investment in building components.", + "type": "number" + }, + "annualise_cost": { + "default": true, + "description": "Annualise the investment costs of retrofitting.", + "type": "boolean" + }, + "tax_weighting": { + "default": false, + "description": "Weight the costs of retrofitting depending on taxes in countries.", + "type": "boolean" + }, + "construction_index": { + "default": true, + "description": "Weight the costs of retrofitting depending on labour/material costs per country.", + "type": "boolean" + } + } + }, + "tes": { + "default": true, + "description": "Add option for storing thermal energy in large water pits associated with district heating systems and individual thermal energy storage (TES).", + "type": "boolean" + }, + "boilers": { + "default": true, + "description": "Add option for transforming gas into heat using gas boilers.", + "type": "boolean" + }, + "resistive_heaters": { + "default": true, + "description": "Add option for transforming electricity into heat using resistive heaters (independently from gas boilers).", + "type": "boolean" + }, + "oil_boilers": { + "default": false, + "description": "Add option for transforming oil into heat using boilers.", + "type": "boolean" + }, + "biomass_boiler": { + "default": true, + "description": "Add option for transforming biomass into heat using boilers.", + "type": "boolean" + }, + "overdimension_heat_generators": { + "additionalProperties": { + "type": "number" + }, + "description": "Add option for overdimensioning heating systems by a certain factor. This allows them to cover heat demand peaks e.g. 10% higher than those in the data with a setting of 1.1.", + "type": "object" + }, + "chp": { + "description": "Configuration for `sector.chp` settings.", + "properties": { + "enable": { + "default": true, + "description": "Add option for using Combined Heat and Power (CHP).", + "type": "boolean" + }, + "fuel": { + "description": "Possible options are all fuels which have an existing bus and their CO2 intensity is given in the technology data. Currently possible are \"gas\", \"oil\", \"methanol\", \"lignite\", \"coal\" as well as \"solid biomass\". For all fuels except solid biomass, the techno-economic data from gas CHP is used. For the special case of solid biomass fuel, both CHP plants with and without carbon capture are added.", + "items": { + "type": "string" + }, + "type": "array" + }, + "micro_chp": { + "default": false, + "description": "Add option for using gas-fired Combined Heat and Power (CHP) for decentral areas.", + "type": "boolean" + } + } + }, + "solar_thermal": { + "default": true, + "description": "Add option for using solar thermal to generate heat.", + "type": "boolean" + }, + "solar_cf_correction": { + "default": 0.788457, + "description": "The correction factor for the value provided by the solar thermal profile calculations.", + "type": "number" + }, + "methanation": { + "default": true, + "description": "Add option for transforming hydrogen and CO2 into methane using methanation.", + "type": "boolean" + }, + "coal_cc": { + "default": false, + "description": "Add option for coal CHPs with carbon capture.", + "type": "boolean" + }, + "dac": { + "default": true, + "description": "Add option for Direct Air Capture (DAC).", + "type": "boolean" + }, + "co2_vent": { + "default": false, + "description": "Add option for vent out CO2 from storages to the atmosphere.", + "type": "boolean" + }, + "heat_vent": { + "additionalProperties": { + "type": "boolean" + }, + "description": "Heat venting by area.", + "type": "object" + }, + "marginal_cost_heat_vent": { + "default": 0.02, + "description": "The marginal cost of heat-venting in all heating systems.", + "type": "number" + }, + "allam_cycle_gas": { + "default": false, + "description": "Add option to include `Allam cycle gas power plants `_.", + "markdownDescription": "Add option to include [Allam cycle gas power plants ](https://en.wikipedia.org/wiki/Allam_power_cycle).", + "type": "boolean" + }, + "hydrogen_fuel_cell": { + "default": true, + "description": "Add option to include hydrogen fuel cell for re-electrification. Assuming OCGT technology costs.", + "type": "boolean" + }, + "hydrogen_turbine": { + "default": true, + "description": "Add option to include hydrogen turbine for re-electrification. Assuming OCGT technology costs.", + "type": "boolean" + }, + "SMR": { + "default": true, + "description": "Add option for transforming natural gas into hydrogen and CO2 using Steam Methane Reforming (SMR).", + "type": "boolean" + }, + "SMR_cc": { + "default": true, + "description": "Add option for transforming natural gas into hydrogen and CO2 using Steam Methane Reforming (SMR) and Carbon Capture (CC).", + "type": "boolean" + }, + "regional_oil_demand": { + "default": true, + "description": "Spatially resolve oil demand. Set to true if regional CO2 constraints needed.", + "type": "boolean" + }, + "regional_coal_demand": { + "default": false, + "description": "Regional coal demand.", + "type": "boolean" + }, + "regional_co2_sequestration_potential": { + "additionalProperties": true, + "description": "Add option for regionally-resolved geological carbon dioxide sequestration potentials based on `CO2StoP `_.", + "markdownDescription": "Add option for regionally-resolved geological carbon dioxide sequestration potentials based on [CO2StoP ](https://setis.ec.europa.eu/european-co2-storage-database_en).", + "type": "object" + }, + "co2_sequestration_potential": { + "additionalProperties": { + "type": "number" + }, + "description": "The potential of sequestering CO2 in Europe per year and investment period.", + "type": "object" + }, + "co2_sequestration_cost": { + "default": 30, + "description": "The cost of sequestering a ton of CO2 (currency/tCO2).", + "type": "number" + }, + "co2_sequestration_lifetime": { + "default": 50, + "description": "The lifetime of a CO2 sequestration site (years).", + "type": "integer" + }, + "co2_spatial": { + "default": true, + "description": "Add option to spatially resolve carrier representing stored carbon dioxide. This allows for more detailed modelling of CCUTS, e.g. regarding the capturing of industrial process emissions, usage as feedstock for electrofuels, transport of carbon dioxide, and geological sequestration sites.", + "type": "boolean" + }, + "co2_network": { + "default": true, + "description": "Add option for planning a new carbon dioxide transmission network.", + "type": "boolean" + }, + "co2_network_cost_factor": { + "default": 1, + "description": "The cost factor for the capital cost of the carbon dioxide transmission network.", + "type": "number" + }, + "cc_fraction": { + "default": 0.9, + "description": "The default fraction of CO2 captured with post-combustion capture.", + "type": "number" + }, + "hydrogen_underground_storage": { + "default": true, + "description": "Add options for storing hydrogen underground. Storage potential depends regionally.", + "type": "boolean" + }, + "hydrogen_underground_storage_locations": { + "description": "The location where hydrogen underground storage can be located. Onshore, nearshore, offshore means it must be located more than 50 km away from the sea, within 50 km of the sea, or within the sea itself respectively.", + "items": { + "type": "string" + }, + "type": "array" + }, + "methanol": { + "description": "Configuration for `sector.methanol` settings.", + "properties": { + "regional_methanol_demand": { + "default": false, + "description": "Spatially resolve methanol demand. Set to true if regional CO2 constraints needed.", + "type": "boolean" + }, + "methanol_reforming": { + "default": false, + "description": "Add methanol reforming.", + "type": "boolean" + }, + "methanol_reforming_cc": { + "default": false, + "description": "Add methanol reforming with carbon capture.", + "type": "boolean" + }, + "methanol_to_kerosene": { + "default": false, + "description": "Add methanol to kerosene.", + "type": "boolean" + }, + "methanol_to_power": { + "additionalProperties": { + "type": "boolean" + }, + "description": "Add different methanol to power technologies.", + "type": "object" + }, + "biomass_to_methanol": { + "default": true, + "description": "Add biomass to methanol.", + "type": "boolean" + }, + "biomass_to_methanol_cc": { + "default": false, + "description": "Add biomass to methanol with carbon capture.", + "type": "boolean" + } + } + }, + "ammonia": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "string" + } + ], + "default": true, + "description": "Add ammonia as a carrier. It can be either true (copperplated NH3), false (no NH3 carrier) or \"regional\" (regionalised NH3 without network)." + }, + "min_part_load_electrolysis": { + "default": 0, + "description": "The minimum unit dispatch (`p_min_pu`) for electrolysis.", + "type": "number" + }, + "min_part_load_fischer_tropsch": { + "default": 0.5, + "description": "The minimum unit dispatch (`p_min_pu`) for the Fischer-Tropsch process.", + "type": "number" + }, + "min_part_load_methanolisation": { + "default": 0.3, + "description": "The minimum unit dispatch (`p_min_pu`) for the methanolisation process.", + "type": "number" + }, + "min_part_load_methanation": { + "default": 0.3, + "description": "Minimum part load methanation.", + "type": "number" + }, + "use_fischer_tropsch_waste_heat": { + "default": 0.25, + "description": "Add option for using waste heat of Fischer Tropsch in district heating networks.", + "type": "number" + }, + "use_haber_bosch_waste_heat": { + "default": 0.25, + "description": "Use Haber-Bosch waste heat.", + "type": "number" + }, + "use_methanolisation_waste_heat": { + "default": 0.25, + "description": "Use methanolisation waste heat.", + "type": "number" + }, + "use_methanation_waste_heat": { + "default": 0.25, + "description": "Use methanation waste heat.", + "type": "number" + }, + "use_fuel_cell_waste_heat": { + "default": 1, + "description": "Add option for using waste heat of fuel cells in district heating networks.", + "type": "number" + }, + "use_electrolysis_waste_heat": { + "default": 0.25, + "description": "Add option for using waste heat of electrolysis in district heating networks.", + "type": "number" + }, + "electricity_transmission_grid": { + "default": true, + "description": "Switch for enabling/disabling the electricity transmission grid.", + "type": "boolean" + }, + "electricity_distribution_grid": { + "default": true, + "description": "Add a simplified representation of the exchange capacity between transmission and distribution grid level through a link.", + "type": "boolean" + }, + "electricity_distribution_grid_cost_factor": { + "default": 1.0, + "description": "Multiplies the investment cost of the electricity distribution grid.", + "type": "number" + }, + "electricity_grid_connection": { + "default": true, + "description": "Add the cost of electricity grid connection for onshore wind and solar.", + "type": "boolean" + }, + "transmission_efficiency": { + "description": "Configuration for `sector.transmission_efficiency` settings.", + "properties": { + "enable": { + "description": "Switch to select the carriers for which transmission efficiency is to be added. Carriers not listed assume lossless transmission.", + "items": { + "type": "string" + }, + "type": "array" + }, + "DC": { + "additionalProperties": { + "type": "number" + }, + "description": "DC transmission efficiency.", + "type": "object" + }, + "H2 pipeline": { + "additionalProperties": { + "type": "number" + }, + "description": "H2 pipeline transmission efficiency.", + "type": "object" + }, + "gas pipeline": { + "additionalProperties": { + "type": "number" + }, + "description": "Gas pipeline transmission efficiency.", + "type": "object" + }, + "electricity distribution grid": { + "additionalProperties": { + "type": "number" + }, + "description": "Electricity distribution grid efficiency.", + "type": "object" + } + } + }, + "H2_network": { + "default": true, + "description": "Add option for new hydrogen pipelines.", + "type": "boolean" + }, + "gas_network": { + "default": true, + "description": "Add existing natural gas infrastructure, incl. LNG terminals, production and entry-points. The existing gas network is added with a lossless transport model. A length-weighted `k-edge augmentation algorithm `_ can be run to add new candidate gas pipelines such that all regions of the model can be connected to the gas network. When activated, all the gas demands are regionally disaggregated as well.", + "markdownDescription": "Add existing natural gas infrastructure, incl. LNG terminals, production and entry-points. The existing gas network is added with a lossless transport model. A length-weighted [k-edge augmentation algorithm ](https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.connectivity.edge_augmentation.k_edge_augmentation.html#networkx.algorithms.connectivity.edge_augmentation.k_edge_augmentation) can be run to add new candidate gas pipelines such that all regions of the model can be connected to the gas network. When activated, all the gas demands are regionally disaggregated as well.", + "type": "boolean" + }, + "H2_retrofit": { + "default": false, + "description": "Add option for retrofiting existing pipelines to transport hydrogen.", + "type": "boolean" + }, + "H2_retrofit_capacity_per_CH4": { + "default": 0.6, + "description": "The ratio for H2 capacity per original CH4 capacity of retrofitted pipelines. The `European Hydrogen Backbone (April, 2020) p.15 `_ 60% of original natural gas capacity could be used in cost-optimal case as H2 capacity.", + "markdownDescription": "The ratio for H2 capacity per original CH4 capacity of retrofitted pipelines. The [European Hydrogen Backbone (April, 2020) p.15 ](https://gasforclimate2050.eu/wp-content/uploads/2020/07/2020_European-Hydrogen-Backbone_Report.pdf) 60% of original natural gas capacity could be used in cost-optimal case as H2 capacity.", + "type": "number" + }, + "gas_network_connectivity_upgrade": { + "default": 1, + "description": "The number of desired edge connectivity (k) in the length-weighted `k-edge augmentation algorithm `_ used for the gas network.", + "markdownDescription": "The number of desired edge connectivity (k) in the length-weighted [k-edge augmentation algorithm ](https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.connectivity.edge_augmentation.k_edge_augmentation.html#networkx.algorithms.connectivity.edge_augmentation.k_edge_augmentation) used for the gas network.", + "type": "number" + }, + "gas_distribution_grid": { + "default": true, + "description": "Add a gas distribution grid.", + "type": "boolean" + }, + "gas_distribution_grid_cost_factor": { + "default": 1.0, + "description": "Multiplier for the investment cost of the gas distribution grid.", + "type": "number" + }, + "biomass_spatial": { + "default": true, + "description": "Add option for resolving biomass demand regionally.", + "type": "boolean" + }, + "biomass_transport": { + "default": false, + "description": "Add option for transporting solid biomass between nodes.", + "type": "boolean" + }, + "biogas_upgrading": { + "default": true, + "description": "Biogas upgrading.", + "type": "boolean" + }, + "biogas_upgrading_cc": { + "default": false, + "description": "Add option to capture CO2 from biomass upgrading.", + "type": "boolean" + }, + "conventional_generation": { + "additionalProperties": { + "type": "string" + }, + "description": "Add a more detailed description of conventional carriers. Any power generation requires the consumption of fuel from nodes representing that fuel.", + "type": "object" + }, + "biomass_to_liquid": { + "default": true, + "description": "Add option for transforming solid biomass into liquid fuel with the same properties as oil.", + "type": "boolean" + }, + "biomass_to_liquid_cc": { + "default": false, + "description": "Add option for transforming solid biomass into liquid fuel with the same properties as oil with carbon capture.", + "type": "boolean" + }, + "electrobiofuels": { + "default": true, + "description": "Electrobiofuels.", + "type": "boolean" + }, + "biosng": { + "default": false, + "description": "Add option for transforming solid biomass into synthesis gas with the same properties as natural gas.", + "type": "boolean" + }, + "biosng_cc": { + "default": false, + "description": "Add option for transforming solid biomass into synthesis gas with the same properties as natural gas with carbon capture.", + "type": "boolean" + }, + "bioH2": { + "default": false, + "description": "Add option for transforming solid biomass into hydrogen with carbon capture.", + "type": "boolean" + }, + "municipal_solid_waste": { + "default": false, + "description": "Add option for municipal solid waste.", + "type": "boolean" + }, + "limit_max_growth": { + "description": "Configuration for `sector.limit_max_growth` settings.", + "properties": { + "enable": { + "default": false, + "description": "Add option to limit the maximum growth of a carrier.", + "type": "boolean" + }, + "factor": { + "default": 1.3, + "description": "The maximum growth factor of a carrier (e.g. 1.3 allows 30% larger than max historic growth).", + "type": "number" + }, + "max_growth": { + "additionalProperties": { + "type": "number" + }, + "description": "The historic maximum growth of a carrier.", + "type": "object" + }, + "max_relative_growth": { + "additionalProperties": { + "type": "number" + }, + "description": "The historic maximum relative growth of a carrier.", + "type": "object" + } + } + }, + "enhanced_geothermal": { + "description": "Configuration for `sector.enhanced_geothermal` settings.", + "properties": { + "enable": { + "default": false, + "description": "Add option to include Enhanced Geothermal Systems.", + "type": "boolean" + }, + "flexible": { + "default": true, + "description": "Add option for flexible operation (see Ricks et al. 2024).", + "type": "boolean" + }, + "max_hours": { + "default": 240, + "description": "The maximum hours the reservoir can be charged under flexible operation.", + "type": "integer" + }, + "max_boost": { + "default": 0.25, + "description": "The maximum boost in power output under flexible operation.", + "type": "number" + }, + "var_cf": { + "default": true, + "description": "Add option for variable capacity factor (see Ricks et al. 2024).", + "type": "boolean" + }, + "sustainability_factor": { + "default": 0.0025, + "description": "Share of sourced heat that is replenished by the earth's core (see details in `build_egs_potentials.py `_).", + "markdownDescription": "Share of sourced heat that is replenished by the earth's core (see details in [build_egs_potentials.py ](https://github.com/PyPSA/pypsa-eur-sec/blob/master/scripts/build_egs_potentials.py)).", + "type": "number" + } + } + }, + "solid_biomass_import": { + "description": "Configuration for `sector.solid_biomass_import` settings.", + "properties": { + "enable": { + "default": false, + "description": "Add option to include solid biomass imports.", + "type": "boolean" + }, + "price": { + "default": 54, + "description": "Price for importing solid biomass (currency/MWh).", + "type": "number" + }, + "max_amount": { + "default": 1390, + "description": "Maximum solid biomass import potential (TWh).", + "type": "number" + }, + "upstream_emissions_factor": { + "default": 0.1, + "description": "Upstream emissions of solid biomass imports.", + "type": "number" + } + } + }, + "imports": { + "description": "Configuration for `sector.imports` settings.", + "properties": { + "enable": { + "default": false, + "description": "Add option to include renewable energy imports.", + "type": "boolean" + }, + "limit": { + "default": null, + "description": "Maximum allowed renewable energy imports (TWh).", + "type": "number" + }, + "limit_sense": { + "default": "<=", + "description": "Sense of the limit.", + "type": "string" + }, + "price": { + "additionalProperties": { + "type": "number" + }, + "description": "Price for importing renewable energy of carrier.", + "type": "object" + } + } + } + } + }, + "industry": { + "description": "Configuration for `industry` settings.", + "properties": { + "St_primary_fraction": { + "additionalProperties": { + "type": "number" + }, + "description": "The fraction of steel produced via primary route versus secondary route (scrap+EAF). Current fraction is 0.6.", + "type": "object" + }, + "DRI_fraction": { + "additionalProperties": { + "type": "number" + }, + "description": "The fraction of the primary route DRI + EAF.", + "type": "object" + }, + "H2_DRI": { + "default": 1.7, + "description": "The hydrogen consumption in Direct Reduced Iron (DRI) Mwh_H2 LHV/ton_Steel from 51kgH2/tSt in `Vogl et al (2018) `_.", + "markdownDescription": "The hydrogen consumption in Direct Reduced Iron (DRI) Mwh_H2 LHV/ton_Steel from 51kgH2/tSt in [Vogl et al (2018) ](https://doi.org/10.1016/j.jclepro.2018.08.279).", + "type": "number" + }, + "elec_DRI": { + "default": 0.322, + "description": "The electricity consumed in Direct Reduced Iron (DRI) shaft. From `HYBRIT brochure `_.", + "markdownDescription": "The electricity consumed in Direct Reduced Iron (DRI) shaft. From [HYBRIT brochure ](https://ssabwebsitecdn.azureedge.net/-/media/hybrit/files/hybrit_brochure.pdf).", + "type": "number" + }, + "Al_primary_fraction": { + "additionalProperties": { + "type": "number" + }, + "description": "The fraction of aluminium produced via the primary route versus scrap. Current fraction is 0.4.", + "type": "object" + }, + "MWh_NH3_per_tNH3": { + "default": 5.166, + "description": "The energy amount per ton of ammonia (LHV).", + "type": "number" + }, + "MWh_CH4_per_tNH3_SMR": { + "default": 10.8, + "description": "The energy amount of methane needed to produce a ton of ammonia using steam methane reforming (SMR). Value derived from 2012's demand from `Center for European Policy Studies (2008) `_.", + "markdownDescription": "The energy amount of methane needed to produce a ton of ammonia using steam methane reforming (SMR). Value derived from 2012's demand from [Center for European Policy Studies (2008) ](https://ec.europa.eu/docsroom/documents/4165/attachments/1/translations/en/renditions/pdf).", + "type": "number" + }, + "MWh_elec_per_tNH3_SMR": { + "default": 0.7, + "description": "The energy amount of electricity needed to produce a ton of ammonia using steam methane reforming (SMR). same source, assuming 94-6% split methane-elec of total energy demand 11.5 MWh/tNH3.", + "type": "number" + }, + "MWh_H2_per_tNH3_electrolysis": { + "default": 5.93, + "description": "The energy amount of hydrogen needed to produce a ton of ammonia using Haber\u2013Bosch process. From `Wang et al (2018) `_, Base value assumed around 0.197 tH2/tHN3 (>3/17 since some H2 lost and used for energy).", + "markdownDescription": "The energy amount of hydrogen needed to produce a ton of ammonia using Haber\u2013Bosch process. From [Wang et al (2018) ](https://doi.org/10.1016/j.joule.2018.04.017), Base value assumed around 0.197 tH2/tHN3 (>3/17 since some H2 lost and used for energy).", + "type": "number" + }, + "MWh_elec_per_tNH3_electrolysis": { + "default": 0.2473, + "description": "The energy amount of electricity needed to produce a ton of ammonia using Haber\u2013Bosch process. From `Wang et al (2018) `_, Table 13 (air separation and HB).", + "markdownDescription": "The energy amount of electricity needed to produce a ton of ammonia using Haber\u2013Bosch process. From [Wang et al (2018) ](https://doi.org/10.1016/j.joule.2018.04.017), Table 13 (air separation and HB).", + "type": "number" + }, + "MWh_NH3_per_MWh_H2_cracker": { + "default": 1.46, + "description": "The energy amount of amonia needed to produce an energy amount hydrogen using ammonia cracker.", + "type": "number" + }, + "NH3_process_emissions": { + "default": 24.5, + "description": "The emission of ammonia production from steam methane reforming (SMR). From UNFCCC for 2015 for EU28.", + "type": "number" + }, + "petrochemical_process_emissions": { + "default": 25.5, + "description": "The emission of petrochemical production. From UNFCCC for 2015 for EU28.", + "type": "number" + }, + "HVC_primary_fraction": { + "additionalProperties": { + "type": "number" + }, + "description": "The fraction of high value chemicals (HVC) produced via primary route.", + "type": "object" + }, + "HVC_mechanical_recycling_fraction": { + "additionalProperties": { + "type": "number" + }, + "description": "The fraction of high value chemicals (HVC) produced using mechanical recycling.", + "type": "object" + }, + "HVC_chemical_recycling_fraction": { + "additionalProperties": { + "type": "number" + }, + "description": "The fraction of high value chemicals (HVC) produced using chemical recycling.", + "type": "object" + }, + "HVC_environment_sequestration_fraction": { + "default": 0.0, + "description": "The fraction of high value chemicals (HVC) put into landfill resulting in additional carbon sequestration. The default value is 0.", + "type": "number" + }, + "waste_to_energy": { + "default": false, + "description": "Switch to enable expansion of waste to energy CHPs for conversion of plastics. Default is false.", + "type": "boolean" + }, + "waste_to_energy_cc": { + "default": false, + "description": "Switch to enable expansion of waste to energy CHPs for conversion of plastics with carbon capture. Default is false.", + "type": "boolean" + }, + "sector_ratios_fraction_future": { + "additionalProperties": { + "type": "number" + }, + "description": "The fraction of total progress in fuel and process switching achieved in the industry sector.", + "type": "object" + }, + "basic_chemicals_without_NH3_production_today": { + "default": 69.0, + "description": "The amount of basic chemicals produced without ammonia (= 86 Mtethylene-equiv - 17 MtNH3).", + "type": "number" + }, + "HVC_production_today": { + "default": 52.0, + "description": "The amount of high value chemicals (HVC) produced. This includes ethylene, propylene and BTX. From `DECHEMA (2017) `_, Figure 16, page 107.", + "markdownDescription": "The amount of high value chemicals (HVC) produced. This includes ethylene, propylene and BTX. From [DECHEMA (2017) ](https://dechema.de/dechema_media/Downloads/Positionspapiere/Technology_study_Low_carbon_energy_and_feedstock_for_the_European_chemical_industry-p-20002750.pdf), Figure 16, page 107.", + "type": "number" + }, + "MWh_elec_per_tHVC_mechanical_recycling": { + "default": 0.547, + "description": "The energy amount of electricity needed to produce a ton of high value chemical (HVC) using mechanical recycling. From SI of `Meys et al (2020) `_, Table S5, for HDPE, PP, PS, PET. LDPE would be 0.756.", + "markdownDescription": "The energy amount of electricity needed to produce a ton of high value chemical (HVC) using mechanical recycling. From SI of [Meys et al (2020) ](https://doi.org/10.1016/j.resconrec.2020.105010), Table S5, for HDPE, PP, PS, PET. LDPE would be 0.756.", + "type": "number" + }, + "MWh_elec_per_tHVC_chemical_recycling": { + "default": 6.9, + "description": "The energy amount of electricity needed to produce a ton of high value chemical (HVC) using chemical recycling. The default value is based on pyrolysis and electric steam cracking. From `Material Economics (2019) `_, page 125.", + "markdownDescription": "The energy amount of electricity needed to produce a ton of high value chemical (HVC) using chemical recycling. The default value is based on pyrolysis and electric steam cracking. From [Material Economics (2019) ](https://materialeconomics.com/latest-updates/industrial-transformation-2050), page 125.", + "type": "number" + }, + "chlorine_production_today": { + "default": 9.58, + "description": "The amount of chlorine produced. From `DECHEMA (2017) `_, Table 7, page 43.", + "markdownDescription": "The amount of chlorine produced. From [DECHEMA (2017) ](https://dechema.de/dechema_media/Downloads/Positionspapiere/Technology_study_Low_carbon_energy_and_feedstock_for_the_European_chemical_industry-p-20002750.pdf), Table 7, page 43.", + "type": "number" + }, + "MWh_elec_per_tCl": { + "default": 3.6, + "description": "The energy amount of electricity needed to produce a ton of chlorine. From `DECHEMA (2017) `_, Table 6 page 43.", + "markdownDescription": "The energy amount of electricity needed to produce a ton of chlorine. From [DECHEMA (2017) ](https://dechema.de/dechema_media/Downloads/Positionspapiere/Technology_study_Low_carbon_energy_and_feedstock_for_the_European_chemical_industry-p-20002750.pdf), Table 6 page 43.", + "type": "number" + }, + "MWh_H2_per_tCl": { + "default": -0.9372, + "description": "The energy amount of hydrogen needed to produce a ton of chlorine. The value is negative since hydrogen produced in chloralkali process. From `DECHEMA (2017) `_, page 43.", + "markdownDescription": "The energy amount of hydrogen needed to produce a ton of chlorine. The value is negative since hydrogen produced in chloralkali process. From [DECHEMA (2017) ](https://dechema.de/dechema_media/Downloads/Positionspapiere/Technology_study_Low_carbon_energy_and_feedstock_for_the_European_chemical_industry-p-20002750.pdf), page 43.", + "type": "number" + }, + "methanol_production_today": { + "default": 1.5, + "description": "The amount of methanol produced. From `DECHEMA (2017) `_, page 62.", + "markdownDescription": "The amount of methanol produced. From [DECHEMA (2017) ](https://dechema.de/dechema_media/Downloads/Positionspapiere/Technology_study_Low_carbon_energy_and_feedstock_for_the_European_chemical_industry-p-20002750.pdf), page 62.", + "type": "number" + }, + "MWh_elec_per_tMeOH": { + "default": 0.167, + "description": "The energy amount of electricity needed to produce a ton of methanol. From `DECHEMA (2017) `_, Table 14, page 65.", + "markdownDescription": "The energy amount of electricity needed to produce a ton of methanol. From [DECHEMA (2017) ](https://dechema.de/dechema_media/Downloads/Positionspapiere/Technology_study_Low_carbon_energy_and_feedstock_for_the_European_chemical_industry-p-20002750.pdf), Table 14, page 65.", + "type": "number" + }, + "MWh_CH4_per_tMeOH": { + "default": 10.25, + "description": "The energy amount of methane needed to produce a ton of methanol. From `DECHEMA (2017) `_, Table 14, page 65.", + "markdownDescription": "The energy amount of methane needed to produce a ton of methanol. From [DECHEMA (2017) ](https://dechema.de/dechema_media/Downloads/Positionspapiere/Technology_study_Low_carbon_energy_and_feedstock_for_the_European_chemical_industry-p-20002750.pdf), Table 14, page 65.", + "type": "number" + }, + "MWh_MeOH_per_tMeOH": { + "default": 5.528, + "description": "The energy amount per ton of methanol (LHV). From `DECHEMA (2017) `_, page 74.", + "markdownDescription": "The energy amount per ton of methanol (LHV). From [DECHEMA (2017) ](https://dechema.de/dechema_media/Downloads/Positionspapiere/Technology_study_Low_carbon_energy_and_feedstock_for_the_European_chemical_industry-p-20002750.pdf), page 74.", + "type": "number" + }, + "hotmaps_locate_missing": { + "default": false, + "description": "Locate industrial sites without valid locations based on city and countries.", + "type": "boolean" + }, + "reference_year": { + "default": 2019, + "description": "The year used as the baseline for industrial energy demand and production. Data extracted from `JRC-IDEES 2015 `_.", + "markdownDescription": "The year used as the baseline for industrial energy demand and production. Data extracted from [JRC-IDEES 2015 ](https://data.jrc.ec.europa.eu/dataset/jrc-10110-10001).", + "type": "integer" + }, + "oil_refining_emissions": { + "default": 0.013, + "description": "The emissions from oil fuel processing (e.g. oil in petrochemical refinieries). The default value of 0.013 tCO2/MWh is based on DE statistics for 2019; the EU value is very similar.", + "type": "number" + } + } + }, + "costs": { + "description": "Configuration for `costs` settings.", + "properties": { + "year": { + "default": 2050, + "description": "Year for which to retrieve cost assumptions of `data/costs/primary//costs_.csv`.", + "type": "integer" + }, + "social_discountrate": { + "default": 0.02, + "description": "Social discount rate to compare costs in different investment periods. 0.02 corresponds to a social discount rate of 2%.", + "type": "number" + }, + "fill_values": { + "description": "Configuration for `costs.fill_values` settings.", + "properties": { + "FOM": { + "default": 0, + "description": "Default fixed operation and maintenance cost.", + "type": "number" + }, + "VOM": { + "default": 0, + "description": "Default variable operation and maintenance cost.", + "type": "number" + }, + "efficiency": { + "default": 1, + "description": "Default efficiency.", + "type": "number" + }, + "fuel": { + "default": 0, + "description": "Default fuel cost.", + "type": "number" + }, + "investment": { + "default": 0, + "description": "Default investment cost.", + "type": "number" + }, + "lifetime": { + "default": 25, + "description": "Default lifetime in years.", + "type": "integer" + }, + "CO2 intensity": { + "default": 0, + "description": "Default CO2 intensity.", + "type": "number" + }, + "discount rate": { + "default": 0.07, + "description": "Default discount rate.", + "type": "number" + }, + "standing losses": { + "default": 0, + "description": "Default standing losses.", + "type": "number" + } + } + }, + "custom_cost_fn": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": "data/custom_costs.csv", + "description": "Path to the custom costs file. None if it should not be used. Default `data/custom_costs.csv` contains minor adjustments for stabilising the optimisation results." + }, + "overwrites": { + "additionalProperties": { + "additionalProperties": { + "type": "number" + }, + "type": "object" + }, + "description": "For the given parameters and technologies, assumptions about their parameter are overwritten the corresponding value of the technology.", + "type": "object" + }, + "capital_cost": { + "additionalProperties": { + "type": "number" + }, + "description": "For the given technologies, assumptions about their capital investment costs are set to the corresponding value. Optional; overwrites cost assumptions from `resources/costs.csv`.", + "type": "object" + }, + "marginal_cost": { + "additionalProperties": { + "type": "number" + }, + "description": "For the given technologies, assumptions about their marginal operating costs are set to the corresponding value. Optional; overwrites cost assumptions from `resources/costs.csv`.", + "type": "object" + }, + "emission_prices": { + "description": "Configuration for `costs.emission_prices` settings.", + "properties": { + "enable": { + "default": false, + "description": "Add cost for a carbon-dioxide price configured in `costs: emission_prices: co2` to `marginal_cost` of generators. Config setting can also be enabled with the keyword `Ep` in the `{opts}` wildcard for electricity-only runs.", + "type": "boolean" + }, + "co2": { + "anyOf": [ + { + "type": "number" + }, + { + "additionalProperties": { + "type": "number" + }, + "type": "object" + } + ], + "default": 0.0, + "description": "Exogenous price of carbon-dioxide. In electricity-only runs it is added to the marginal costs of fossil-fuelled generators according to their carbon intensity, while for sector networks it applies to emissions ending up in CO2 atmosphere." + }, + "co2_monthly_prices": { + "default": false, + "description": "Add monthly cost for a carbon-dioxide price based on historical values built by the rule `build_monthly_prices`.", + "type": "boolean" + } + } + } + } + }, + "clustering": { + "description": "Configuration for `clustering` settings.", + "properties": { + "mode": { + "default": "busmap", + "description": "'busmap': Default. 'custom_busmap': Enable the use of custom busmaps in rule `cluster_network`. If activated the rule looks for provided busmaps at ``data/busmaps/base_s_{clusters}_{base_network}.csv`` which should have the same format as ``resources/busmap_base_s_{clusters}.csv``, i.e. the index should contain the buses of ``networks/base_s.nc``. {base_network} is the name of the selected base_network in electricity, e.g. ``gridkit``, ``osm-prebuilt``, or ``osm-raw``. 'administrative': Clusters and indexes the network based on the administrative regions of the countries based on ``nuts3_shapes.geojson`` (level: 1, 2, 3, bz). To activate this, additionally set the ``clusters`` wildcard in ``scenario`` to 'adm'. 'custom_busshapes': Enable the use of custom shapes in rule `cluster_network`. If activated the rule looks for provided busshapes at ``data/busshapes/base_s_{clusters}_{base_network}.geojson``.", + "enum": [ + "busmap", + "custom_busmap", + "administrative", + "custom_busshapes" + ], + "type": "string" + }, + "administrative": { + "description": "Configuration for `clustering.administrative` settings.", + "properties": { + "level": { + "default": 1, + "description": "Level of administrative regions to cluster the network. 0: Country level, 1: NUTS1 level, 2: NUTS2 level, 3: NUTS3 level, 'bz': Bidding zones. Only applies when mode is set to `administrative`. Note that non-NUTS countries 'BA', 'MD', 'UA', and 'XK' can only be clustered to level 0 and 1.", + "enum": [ + 0, + 1, + 2, + 3, + "bz" + ] + }, + "countries": { + "additionalProperties": { + "type": "integer" + }, + "description": "Optionally include dictionary of individual country codes and their individual NUTS levels. Overwrites country-specific `level`. For example: `{'DE': 1, 'FR': 2}`. Only applies when mode is set to `administrative`.", + "type": "object" + } + } + }, + "focus_weights": { + "anyOf": [ + { + "type": "boolean" + }, + { + "additionalProperties": { + "type": "number" + }, + "type": "object" + } + ], + "default": false, + "description": "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. Only applies when mode is set to `busmap`." + }, + "copperplate_regions": { + "description": "Optionally specify the regions to copperplate as a list of groups. Each group is a list of region codes that will be connected with infinite capacity lines.", + "items": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": "array" + }, + "build_bidding_zones": { + "description": "Configuration for `clustering.build_bidding_zones` settings.", + "properties": { + "remove_islands": { + "default": false, + "description": "Exclude from the shape file the Balearic Islands, Bornholm, the Canary Islands, the Orkney Islands, the Shetland Islands, the Azores Islands and Madeira.", + "type": "boolean" + }, + "aggregate_to_tyndp": { + "default": false, + "description": "Adjust the shape file to the TYNDP topology. Aggregate the Southern Norwegian bidding zones and extract Crete as a separate zone from the Greek shape.", + "type": "boolean" + } + } + }, + "simplify_network": { + "description": "Configuration for `clustering.simplify_network` settings.", + "properties": { + "to_substations": { + "default": false, + "description": "Aggregates all nodes without power injection (positive or negative, i.e. demand or generation) to electrically closest ones.", + "type": "boolean" + }, + "exclude_carriers": { + "description": "List of carriers which will not be aggregated. If empty, all carriers will be aggregated.", + "items": { + "type": "string" + }, + "type": "array" + }, + "remove_stubs": { + "default": true, + "description": "Controls whether radial parts of the network should be recursively aggregated. Defaults to true.", + "type": "boolean" + }, + "remove_stubs_across_borders": { + "default": false, + "description": "Controls whether radial parts of the network should be recursively aggregated across borders. Defaults to true.", + "type": "boolean" + } + } + }, + "cluster_network": { + "description": "Configuration for `clustering.cluster_network` settings.", + "properties": { + "algorithm": { + "default": "kmeans", + "description": "Clustering algorithm to use.", + "enum": [ + "kmeans", + "hac" + ], + "type": "string" + }, + "hac_features": { + "description": "List of meteorological variables contained in the weather data cutout that should be considered for hierarchical clustering.", + "items": { + "type": "string" + }, + "type": "array" + } + } + }, + "exclude_carriers": { + "description": "List of carriers which will not be aggregated. If empty, all carriers will be aggregated.", + "items": { + "type": "string" + }, + "type": "array" + }, + "consider_efficiency_classes": { + "default": false, + "description": "Aggregated each carriers into the top 10-quantile (high), the bottom 90-quantile (low), and everything in between (medium).", + "type": "boolean" + }, + "aggregation_strategies": { + "description": "Configuration for `clustering.aggregation_strategies` settings.", + "properties": { + "generators": { + "additionalProperties": { + "type": "string" + }, + "description": "Aggregates the component according to the given strategy. For example, if sum, then all values within each cluster are summed to represent the new generator.", + "type": "object" + }, + "buses": { + "additionalProperties": { + "type": "string" + }, + "description": "Aggregates the component according to the given strategy. For example, if sum, then all values within each cluster are summed to represent the new bus.", + "type": "object" + } + } + }, + "temporal": { + "description": "Configuration for `clustering.temporal` settings.", + "properties": { + "resolution_elec": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "string" + } + ], + "default": false, + "description": "Resample the time-resolution by averaging over every `n` snapshots in `prepare_network`. **Warning:** This option should currently only be used with electricity-only networks, not for sector-coupled networks." + }, + "resolution_sector": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "string" + } + ], + "default": false, + "description": "Resample the time-resolution by averaging over every `n` snapshots in `prepare_sector_network`." + } + } + } + } + }, + "adjustments": { + "description": "Configuration for top-level adjustments key.", + "properties": { + "electricity": { + "anyOf": [ + { + "type": "boolean" + }, + { + "description": "Configuration for adjustment settings (factor/absolute)", + "properties": { + "factor": { + "anyOf": [ + { + "type": "boolean" + }, + { + "additionalProperties": { + "additionalProperties": { + "additionalProperties": { + "anyOf": [ + { + "type": "number" + }, + { + "additionalProperties": { + "type": "number" + }, + "type": "object" + } + ] + }, + "type": "object" + }, + "type": "object" + }, + "type": "object" + } + ], + "default": false, + "description": "Multiply original value with given factor" + }, + "absolute": { + "anyOf": [ + { + "type": "boolean" + }, + { + "additionalProperties": { + "additionalProperties": { + "additionalProperties": { + "anyOf": [ + { + "type": "number" + }, + { + "additionalProperties": { + "type": "number" + }, + "type": "object" + } + ] + }, + "type": "object" + }, + "type": "object" + }, + "type": "object" + } + ], + "default": false, + "description": "Set attribute to absolute value. Can be also a dictionary with planning horizons as keys." + } + } + } + ], + "default": false, + "description": "Parameter adjustments applied in `prepare_network`." + }, + "sector": { + "anyOf": [ + { + "type": "boolean" + }, + { + "description": "Configuration for adjustment settings (factor/absolute)", + "properties": { + "factor": { + "anyOf": [ + { + "type": "boolean" + }, + { + "additionalProperties": { + "additionalProperties": { + "additionalProperties": { + "anyOf": [ + { + "type": "number" + }, + { + "additionalProperties": { + "type": "number" + }, + "type": "object" + } + ] + }, + "type": "object" + }, + "type": "object" + }, + "type": "object" + } + ], + "default": false, + "description": "Multiply original value with given factor" + }, + "absolute": { + "anyOf": [ + { + "type": "boolean" + }, + { + "additionalProperties": { + "additionalProperties": { + "additionalProperties": { + "anyOf": [ + { + "type": "number" + }, + { + "additionalProperties": { + "type": "number" + }, + "type": "object" + } + ] + }, + "type": "object" + }, + "type": "object" + }, + "type": "object" + } + ], + "default": false, + "description": "Set attribute to absolute value. Can be also a dictionary with planning horizons as keys." + } + } + } + ], + "description": "Parameter adjustments applied in `prepare_sector_network`." + } + } + }, + "solving": { + "description": "Configuration for `solving` settings.", + "properties": { + "options": { + "description": "Configuration for `solving.options` settings.", + "properties": { + "clip_p_max_pu": { + "default": 0.01, + "description": "To avoid too small values in the renewables` per-unit availability time series values below this threshold are set to zero.", + "type": "number" + }, + "load_shedding": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "number" + } + ], + "default": false, + "description": "Add generators with very high marginal cost to simulate load shedding and avoid problem infeasibilities. If load shedding is a float, it denotes the marginal cost in EUR/kWh." + }, + "curtailment_mode": { + "default": false, + "description": "Fixes the dispatch profiles of generators with time-varying p_max_pu by setting `p_min_pu = p_max_pu` and adds an auxiliary curtailment generator (with negative sign to absorb excess power) at every AC bus. This can speed up the solving process as the curtailment decision is aggregated into a single generator per region. Defaults to `false`.", + "type": "boolean" + }, + "noisy_costs": { + "default": true, + "description": "Add random noise to marginal cost of generators by :math:`\\mathcal{U}(0.009,0,011)` and capital cost of lines and links by :math:`\\mathcal{U}(0.09,0,11)`.", + "type": "boolean" + }, + "skip_iterations": { + "default": true, + "description": "Skip iterating, do not update impedances of branches. Defaults to true.", + "type": "boolean" + }, + "rolling_horizon": { + "default": false, + "description": "Switch for rule `solve_operations_network` whether to optimize the network in a rolling horizon manner, where the snapshot range is split into slices of size `horizon` which are solved consecutively. This setting has currently no effect on sector-coupled networks.", + "type": "boolean" + }, + "seed": { + "default": 123, + "description": "Random seed for increased deterministic behaviour.", + "type": "integer" + }, + "custom_extra_functionality": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": "../data/custom_extra_functionality.py", + "description": "Path to a Python file with custom extra functionality code to be injected into the solving rules of the workflow relative to `rules` directory." + }, + "io_api": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Passed to linopy and determines the API used to communicate with the solver. With the `'lp'` and `'mps'` options linopy passes a file to the solver; with the `'direct'` option (only supported for HIGHS and Gurobi) linopy uses an in-memory python API resulting in better performance." + }, + "track_iterations": { + "default": false, + "description": "Flag whether to store the intermediate branch capacities and objective function values are recorded for each iteration in `network.lines['s_nom_opt_X']` (where `X` labels the iteration)", + "type": "boolean" + }, + "min_iterations": { + "default": 2, + "description": "Minimum number of solving iterations in between which resistance and reactence (`x/r`) are updated for branches according to `s_nom_opt` of the previous run.", + "type": "integer" + }, + "max_iterations": { + "default": 3, + "description": "Maximum number of solving iterations in between which resistance and reactence (`x/r`) are updated for branches according to `s_nom_opt` of the previous run.", + "type": "integer" + }, + "transmission_losses": { + "default": 2, + "description": "Add piecewise linear approximation of transmission losses based on n tangents. Defaults to 0, which means losses are ignored.", + "type": "integer" + }, + "linearized_unit_commitment": { + "default": true, + "description": "Whether to optimise using the linearized unit commitment formulation.", + "type": "boolean" + }, + "horizon": { + "default": 365, + "description": "Number of snapshots to consider in each iteration. Defaults to 100.", + "type": "integer" + }, + "post_discretization": { + "description": "Configuration for `solving.options.post_discretization` settings.", + "properties": { + "enable": { + "default": false, + "description": "Switch to enable post-discretization of the network. Disabled by default.", + "type": "boolean" + }, + "line_unit_size": { + "default": 1700, + "description": "Discrete unit size of lines in MW.", + "type": "number" + }, + "line_threshold": { + "default": 0.3, + "description": "The threshold relative to the discrete line unit size beyond which to round up to the next unit.", + "type": "number" + }, + "link_unit_size": { + "additionalProperties": { + "type": "number" + }, + "description": "Discrete unit size of links in MW by carrier (given in dictionary style).", + "type": "object" + }, + "link_threshold": { + "additionalProperties": { + "type": "number" + }, + "description": "The threshold relative to the discrete link unit size beyond which to round up to the next unit by carrier (given in dictionary style).", + "type": "object" + }, + "fractional_last_unit_size": { + "default": false, + "description": "When true, links and lines can be built up to p_nom_max. When false, they can only be built up to a multiple of the unit size.", + "type": "boolean" + } + } + }, + "keep_files": { + "default": false, + "description": "Whether to keep LPs and MPS files after solving.", + "type": "boolean" + }, + "model_kwargs": { + "description": "Configuration for `solving.options.model_kwargs` settings.", + "properties": { + "solver_dir": { + "default": "", + "description": "Absolute path to the directory where linopy saves files.", + "type": "string" + } + } + } + } + }, + "agg_p_nom_limits": { + "description": "Configuration for `solving.agg_p_nom_limits` settings.", + "properties": { + "agg_offwind": { + "default": false, + "description": "Aggregate together all the types of offwind when writing the constraint (`offwind-all` as a carrier in the `.csv` file). Default is false.", + "type": "boolean" + }, + "agg_solar": { + "default": false, + "description": "Aggregate together all the types of electric solar when writing the constraint (`solar-all` as a carrier in the `.csv` file). Default is false.", + "type": "boolean" + }, + "include_existing": { + "default": false, + "description": "Take existing capacities into account when writing the constraint. Default is false.", + "type": "boolean" + }, + "file": { + "default": "data/agg_p_nom_minmax.csv", + "description": "Reference to `.csv` file specifying per carrier generator nominal capacity constraints for individual countries and planning horizons. Defaults to `data/agg_p_nom_minmax.csv`.", + "type": "string" + } + } + }, + "constraints": { + "description": "Configuration for `solving.constraints` settings.", + "properties": { + "CCL": { + "default": false, + "description": "Add minimum and maximum levels of generator nominal capacity per carrier for individual countries. These can be specified in the file linked at `electricity: agg_p_nom_limits` in the configuration. File defaults to `data/agg_p_nom_minmax.csv`. Does not work with a time resolution resampling.", + "type": "boolean" + }, + "EQ": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "string" + } + ], + "default": false, + "description": "Require each country or node to on average produce a minimal share of its total consumption itself. Example: `EQ0.5c` demands each country to produce on average at least 50% of its consumption; `EQ0.5` demands each node to produce on average at least 50% of its consumption." + }, + "BAU": { + "default": false, + "description": "Add a per-`carrier` minimal overall capacity; i.e. at least `40GW` of `OCGT` in Europe; configured in `electricity: BAU_mincapacities`", + "type": "boolean" + }, + "SAFE": { + "default": false, + "description": "Add a capacity reserve margin of a certain fraction above the peak demand to which renewable generators and storage do *not* contribute. Ignores network.", + "type": "boolean" + } + } + }, + "solver": { + "description": "Configuration for `solving.solver` settings.", + "properties": { + "name": { + "default": "gurobi", + "description": "Solver to use for optimisation problems in the workflow; e.g. clustering and linear optimal power flow.", + "type": "string" + }, + "options": { + "default": "gurobi-default", + "description": "Link to specific parameter settings.", + "type": "string" + } + } + }, + "solver_options": { + "additionalProperties": { + "additionalProperties": true, + "type": "object" + }, + "description": "Dictionaries with solver-specific parameter settings.", + "type": "object" + }, + "check_objective": { + "description": "Configuration for `solving.check_objective` settings.", + "properties": { + "enable": { + "default": false, + "description": "Enable objective value checking.", + "type": "boolean" + }, + "expected_value": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Expected objective value." + }, + "atol": { + "default": 1000000, + "description": "Absolute tolerance.", + "type": "number" + }, + "rtol": { + "default": 0.01, + "description": "Relative tolerance.", + "type": "number" + } + } + }, + "oetc": { + "anyOf": [ + { + "description": "Configuration for `solving.oetc` settings (Open Energy Transition Computing cluster support).", + "properties": { + "name": { + "default": "pypsa-eur", + "description": "Name identifier for the OETC job.", + "type": "string" + }, + "authentication_server_url": { + "default": "", + "description": "URL of the OETC authentication server for job submission.", + "type": "string" + }, + "orchestrator_server_url": { + "default": "", + "description": "URL of the OETC orchestrator server for job management.", + "type": "string" + }, + "cpu_cores": { + "default": 8, + "description": "Number of CPU cores to request for the OETC job. (includes RAM amount at the moment with a factor of 8)", + "type": "integer" + }, + "disk_space_gb": { + "default": 50, + "description": "Amount of disk space in gigabytes to request for the OETC job.", + "type": "integer" + }, + "delete_worker_on_error": { + "default": true, + "description": "Whether to delete the worker instance when an error occurs during job execution.", + "type": "boolean" + } + } + }, + { + "type": "null" + } + ], + "default": null, + "description": "Configuration options for Open Energy Transition Computing (OETC) cluster support." + }, + "mem_mb": { + "default": 128000, + "description": "Estimated maximum memory requirement for solving networks (MB).", + "type": "integer" + }, + "memory_logging_frequency": { + "default": 5, + "description": "Interval in seconds at which memory usage is logged.", + "type": "integer" + }, + "runtime": { + "default": "48h", + "description": "Runtime in humanfriendly style.", + "type": "string" + } + } + }, + "data": { + "description": "Configuration for `data` settings.", + "properties": { + "hotmaps_industrial_sites": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "enspreso_biomass": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "osm": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "worldbank_urban_population": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "gem_europe_gas_tracker": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "co2stop": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "nitrogen_statistics": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "eu_nuts2013": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "eu_nuts2021": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "eurostat_balances": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "eurostat_household_balances": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "wdpa": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "wdpa_marine": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "luisa_land_cover": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "jrc_idees": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "scigrid_gas": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "seawater_temperature": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "synthetic_electricity_demand": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "copernicus_land_cover": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "ship_raster": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "eez": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "nuts3_population": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "gdp_per_capita": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "population_count": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "ghg_emissions": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "gebco": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "attributed_ports": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "corine": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "emobility": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "h2_salt_caverns": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "lau_regions": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "aquifer_data": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "osm_boundaries": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "gem_gspt": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "tyndp": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "powerplants": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "costs": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "country_runoff": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "country_hdd": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "natura": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "bfs_road_vehicle_stock": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "bfs_gdp_and_population": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "mobility_profiles": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "cutout": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "dh_areas": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "geothermal_heat_utilisation_potentials": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "jrc_ardeco": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "bidding_zones_electricitymaps": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + }, + "bidding_zones_entsoepy": { + "description": "Configuration for a single data source.", + "properties": { + "source": { + "default": "archive", + "description": "Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + "enum": [ + "archive", + "primary", + "build" + ], + "type": "string" + }, + "version": { + "default": "latest", + "description": "Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + "type": "string" + } + } + } + } + }, + "overpass_api": { + "description": "Configuration for `overpass_api` settings.", + "properties": { + "url": { + "default": "https://overpass-api.de/api/interpreter", + "description": "Overpass API endpoint URL. See `Overpass API Wiki `_ for available public instances.", + "markdownDescription": "Overpass API endpoint URL. See [Overpass API Wiki ](https://wiki.openstreetmap.org/wiki/Overpass_API#Public_Overpass_API_instances) for available public instances.", + "type": "string" + }, + "max_tries": { + "default": 5, + "description": "Maximum retry attempts for Overpass API requests. Please be respectful to the Overpass API fair use policy of the individual instances.", + "type": "integer" + }, + "timeout": { + "default": 600, + "description": "Timeout in seconds for Overpass API requests.", + "type": "integer" + }, + "user_agent": { + "description": "Configuration for `overpass_api.user_agent` settings.", + "properties": { + "project_name": { + "default": "PyPSA-Eur", + "description": "Project name used to identify the user agent of the Overpass API requests.", + "type": "string" + }, + "email": { + "default": "contact@pypsa.org", + "description": "Contact email address for the project using the Overpass API.", + "type": "string" + }, + "website": { + "default": "https://github.com/PyPSA/pypsa-eur", + "description": "Website URL for the project using the Overpass API.", + "type": "string" + } + } + } + } + } + }, + "title": "PyPSA-Eur Configuration", + "type": "object" +} diff --git a/config/test/config.clusters.yaml b/config/test/config.clusters.yaml index f7552bb44..7f61f2208 100644 --- a/config/test/config.clusters.yaml +++ b/config/test/config.clusters.yaml @@ -1,3 +1,4 @@ +# yaml-language-server: $schema=../schema.json # SPDX-FileCopyrightText: Contributors to PyPSA-Eur # # SPDX-License-Identifier: CC0-1.0 diff --git a/config/test/config.electricity.yaml b/config/test/config.electricity.yaml index 687cfec68..fba70ed84 100644 --- a/config/test/config.electricity.yaml +++ b/config/test/config.electricity.yaml @@ -1,3 +1,4 @@ +# yaml-language-server: $schema=../schema.json # SPDX-FileCopyrightText: Contributors to PyPSA-Eur # # SPDX-License-Identifier: CC0-1.0 diff --git a/config/test/config.myopic.yaml b/config/test/config.myopic.yaml index 2748f2bc1..6da578027 100644 --- a/config/test/config.myopic.yaml +++ b/config/test/config.myopic.yaml @@ -1,3 +1,4 @@ +# yaml-language-server: $schema=../schema.json # SPDX-FileCopyrightText: Contributors to PyPSA-Eur # # SPDX-License-Identifier: CC0-1.0 diff --git a/config/test/config.overnight.yaml b/config/test/config.overnight.yaml index 624f1be7b..e0508de4b 100644 --- a/config/test/config.overnight.yaml +++ b/config/test/config.overnight.yaml @@ -1,3 +1,4 @@ +# yaml-language-server: $schema=../schema.json # SPDX-FileCopyrightText: Contributors to PyPSA-Eur # # SPDX-License-Identifier: CC0-1.0 diff --git a/config/test/config.perfect.yaml b/config/test/config.perfect.yaml index 6941212c8..efd5cbbf1 100644 --- a/config/test/config.perfect.yaml +++ b/config/test/config.perfect.yaml @@ -1,3 +1,4 @@ +# yaml-language-server: $schema=../schema.json # SPDX-FileCopyrightText: Contributors to PyPSA-Eur # # SPDX-License-Identifier: CC0-1.0 diff --git a/config/test/config.scenarios.yaml b/config/test/config.scenarios.yaml index 12e3464b7..1315ffcc1 100644 --- a/config/test/config.scenarios.yaml +++ b/config/test/config.scenarios.yaml @@ -1,3 +1,4 @@ +# yaml-language-server: $schema=../schema.json # SPDX-FileCopyrightText: Contributors to PyPSA-Eur # # SPDX-License-Identifier: CC0-1.0 diff --git a/config/test/config.tyndp.yaml b/config/test/config.tyndp.yaml index c0ec7f96c..33b224105 100644 --- a/config/test/config.tyndp.yaml +++ b/config/test/config.tyndp.yaml @@ -1,3 +1,4 @@ +# yaml-language-server: $schema=../schema.json # SPDX-FileCopyrightText: Contributors to PyPSA-Eur # # SPDX-License-Identifier: CC0-1.0 diff --git a/config/test/config.validator.yaml b/config/test/config.validator.yaml index 9dc7cccb5..4caa222a6 100644 --- a/config/test/config.validator.yaml +++ b/config/test/config.validator.yaml @@ -1,3 +1,4 @@ +# yaml-language-server: $schema=../schema.json # SPDX-FileCopyrightText: Contributors to PyPSA-Eur # # SPDX-License-Identifier: CC0-1.0 diff --git a/data/versions.csv b/data/versions.csv index 44fb30249..fc8b1c327 100644 --- a/data/versions.csv +++ b/data/versions.csv @@ -1,106 +1,135 @@ -dataset,source,version,tags,url,note -co2stop,archive,26 AUGUST 2020,"['latest', 'supported']",https://zenodo.org/records/15837736/files/co2jrc_openformats.zip, -co2stop,primary,26 AUGUST 2020,"['latest', 'supported']",https://setis.ec.europa.eu/document/download/786a884f-0b33-4789-b744-28004b16bd1a_en?filename=co2jrc_openformats.zip, -gem_europe_gas_tracker,archive,May 2024,"['latest', 'supported']",https://zenodo.org/records/16893480/files/Europe-Gas-Tracker-2024-05.xlsx, -gem_europe_gas_tracker,primary,May 2024,"['latest', 'supported']",https://globalenergymonitor.org/wp-content/uploads/2024/05/Europe-Gas-Tracker-2024-05.xlsx, -gem_gspt,archive,April 2024 V1,"['latest', 'supported']",https://zenodo.org/records/16893375/files/Global-Steel-Plant-Tracker-April-2024-Standard-Copy-V1.xlsx, -gem_gspt,primary,March-2025 V1,['not-supported'],"https://globalenergymonitor.org/wp-content/uploads/2025/03/Plant-level-data-Global-Iron-and-Steel-Tracker-March-2025-V1.xlsx""", -enspreso_biomass,archive,2019-06-20,"['latest', 'supported']",https://zenodo.org/records/10356004/files/ENSPRESO_BIOMASS.xlsx, -enspreso_biomass,primary,unknown,"['latest', 'supported']",https://jeodpp.jrc.ec.europa.eu/ftp/jrc-opendata/ENSPRESO/ENSPRESO_BIOMASS.xlsx,"2019 version was changed in 2023 with negligible differences, but since the version is not frozen we label it as 'unknown'." -hotmaps_industrial_sites,archive,Version 0.2.0,['not-supported'],https://zenodo.org/records/4687147/files/industrial_sites_Industrial_Database-1.0.zip, -hotmaps_industrial_sites,archive,Version 1.1,"['latest', 'supported']",https://zenodo.org/records/15834781/files/Industrial_Database.csv, -hotmaps_industrial_sites,primary,Version 1.0,['not-supported'],https://gitlab.com/hotmaps/industrial_sites/industrial_sites_Industrial_Database/-/raw/1.0/data/Industrial_Database.csv,Required renaming of country names to work with PyPSA-Eur. This was fixed in the latest version. -hotmaps_industrial_sites,primary,unknown,"['latest', 'supported']",https://gitlab.com/hotmaps/industrial_sites/industrial_sites_Industrial_Database/-/raw/master/data/Industrial_Database.csv, -nitrogen_statistics,archive,2022,"['latest', 'supported']",https://zenodo.org/records/15838121/files/nitro-ert.xlsx, -nitrogen_statistics,primary,2022,['supported'],https://d9-wret.s3.us-west-2.amazonaws.com/assets/palladium/production/s3fs-public/media/files/myb1-2022-nitro-ert.xlsx, -nitrogen_statistics,primary,2023,"['latest', 'not-tested']",https://d9-wret.s3.us-west-2.amazonaws.com/assets/palladium/production/s3fs-public/media/files/myb1-2023-nitro-ERT.xlsx, -eu_nuts2013,archive,2015-12-03,"['latest', 'supported']",https://zenodo.org/records/15846347/files/ref-nuts-2013-03m.geojson.zip, -eu_nuts2013,primary,2015-12-03,"['latest', 'supported']",https://gisco-services.ec.europa.eu/distribution/v2/nuts/download/ref-nuts-2013-03m.geojson.zip, -eu_nuts2021,archive,2021-01-01,"['latest', 'supported']",https://zenodo.org/records/15846440/files/ref-nuts-2021-01m.geojson.zip, -eu_nuts2021,primary,2021-01-01,"['latest', 'supported']",https://gisco-services.ec.europa.eu/distribution/v2/nuts/download/ref-nuts-2021-01m.geojson.zip, -eurostat_balances,archive,2023-04,"['latest', 'supported']",https://zenodo.org/records/15849144/files/balances.zip, -eurostat_balances,primary,2023-04,"['latest', 'supported', 'broken link']",https://ec.europa.eu/eurostat/documents/38154/4956218/Balances-April2023.zip,"The link is broken, use the archived versions instead." -eurostat_household_balances,archive,2025-07-09,"['latest', 'supported']",https://zenodo.org/records/15849673/files/nrg_d_hhq.csv, -eurostat_household_balances,primary,unknown,"['latest', 'supported']",https://ec.europa.eu/eurostat/databrowser-backend/api/extraction/1.0/LIVE/false/sdmx/csv/nrg_d_hhq__custom_11480365?startPeriod=2013&endPeriod=2022,"URL limits the period to 2013-2022, but the data is updated regularly." -osm,archive,0.1,"['deprecated', 'not-supported']",https://zenodo.org/records/12799202, -osm,archive,0.2,"['deprecated', 'not-supported']",https://zenodo.org/records/13342577, -osm,archive,0.3,"['deprecated', 'not-supported']",https://zenodo.org/records/13358976, -osm,archive,0.4,"['deprecated', 'not-supported']",https://zenodo.org/records/13759222, -osm,archive,0.5,"['deprecated', 'not-supported']",https://zenodo.org/records/13981528, -osm,archive,0.6,"['latest', 'supported']",https://zenodo.org/records/14144752, -osm,build,unknown,"['latest', 'supported']",,Latest dataset built using OSM data. No single URL available to access the data. See the related scripts and documentation for details. -wdpa,archive,Jul2025,"['latest', 'supported']",https://web.archive.org/web/20250715071823if_/https://d1gam3xoknrgr2.cloudfront.net/current/WDPA_Jul2025_Public_shp.zip,"We are legally not allowed to redistribute this dataset, luckily the web archive is keeping copies of it." -wdpa,primary,unknown,"['latest', 'supported']",https://d1gam3xoknrgr2.cloudfront.net/current/WDPA_{bYYYY}_Public_shp.zip,"WDPA changes its URL every month, the URL here is used as a template and {bYYYY} is replaced inside the retrieve.smk." -wdpa_marine,archive,Jul2025,"['latest', 'supported']",https://web.archive.org/web/20250715084308if_/https://d1gam3xoknrgr2.cloudfront.net/current/WDPA_WDOECM_Jul2025_Public_marine_shp.zip,"We are legally not allowed to redistribute this dataset, luckily the web archive is keeping copies of it." -wdpa_marine,primary,unknown,"['latest', 'supported']",https://d1gam3xoknrgr2.cloudfront.net/current/WDPA_WDOECM_{bYYYY}_Public_marine_shp.zip,"WDPA maritime changes its URL every month, the URL here is used as a template and {bYYYY} is replaced inside the retrieve.smk." -worldbank_urban_population,archive,2025-08-14,"['latest', 'supported']",https://zenodo.org/records/16875854/files/API_SP.URB.TOTL.IN.ZS_DS2_en_csv_v2_22447.zip, -worldbank_urban_population,primary,unknown,"['latest', 'might-work']",https://api.worldbank.org/v2/en/indicator/SP.URB.TOTL.IN.ZS?downloadformat=csv,"This is the original World Bank API link, which is sometimes updated; it is not guaranteed to work with the current codebase and data changes without notice." -luisa_land_cover,primary,unknown,"['latest', 'supported']",https://jeodpp.jrc.ec.europa.eu/ftp/jrc-opendata/LUISA/EUROPE/Basemaps/LandUse/2018/LATEST/LUISA_basemap_020321_50m.tif, -luisa_land_cover,archive,2021-03-02,"['latest', 'supported']",https://zenodo.org/records/15879466/files/LUISA_basemap_020321_50m.tif, -jrc_idees,primary,unknown,"['latest', 'supported']",https://jeodpp.jrc.ec.europa.eu/ftp/jrc-opendata/JRC-IDEES/JRC-IDEES-2021_v1/JRC-IDEES-2021.zip, -jrc_idees,archive,2024-05-20,"['latest', 'supported']",https://zenodo.org/records/15895830/files/JRC-IDEES-2021.zip, -scigrid_gas,primary,1.1.2,"['latest', 'supported']",https://zenodo.org/records/4767098/files/IGGIELGN.zip,the primary is already from Zenodo published by original authors -scigrid_gas,archive,1.1.1,"['deprecated', 'not-supported']",https://zenodo.org/records/4751038/files/IGGIELGN.zip, -scigrid_gas,archive,1.1.0,"['deprecated', 'not-supported']",https://zenodo.org/records/4642569/files/IGGIELGN.zip, -synthetic_electricity_demand,primary,v2,"['latest', 'supported']",https://zenodo.org/records/10820928/files/demand_hourly.csv,the primary is already from Zenodo published by original authors -synthetic_electricity_demand,archive,0.1.0,"['deprecated', 'might-work']",https://zenodo.org/records/7070438/files/demand_hourly.csv, -copernicus_land_cover,primary,v3.0.1,"['latest', 'supported']",https://zenodo.org/records/3939050/files/PROBAV_LC100_global_v3.0.1_2019-nrt_Discrete-Classification-map_EPSG-4326.tif,"The primary is already from Zenodo, documentation in https://zenodo.org/records/4723921" -copernicus_land_cover,archive,v2.0.2,"['deprecated', 'might-work']",https://zenodo.org/records/3939038/files/PROBAV_LC100_global_v3.0.1_2015-base_Discrete-Classification-map_EPSG-4326.tif, -ship_raster,primary,v5,"['latest', 'supported']",https://datacatalogfiles.worldbank.org/ddh-published/0037580/5/DR0045406/shipdensity_global.zip, -ship_raster,archive,v5,"['latest', 'supported']",https://zenodo.org/records/16894236/files/shipdensity_global.zip, -eez,primary,v12_20231025,"['latest', 'supported']",https://www.marineregions.org/download_file.php,API request used -eez,archive,v12_20231025,"['latest', 'supported']",https://zenodo.org/records/16355917/files/World_EEZ_v12_20231025_LR.zip, -nuts3_population,primary,13-03-2025,"['latest', 'supported']",https://ec.europa.eu/eurostat/api/dissemination/sdmx/2.1/data/nama_10r_3popgdp?format=TSV&compressed=true, -nuts3_population,archive,13-03-2025,"['latest', 'supported']",https://zenodo.org/records/16551424/files/nama_10r_3popgdp.tsv.gz,earlier part of zenodo bundle -gdp_per_capita,archive,2018-02-06,"['latest', 'supported']",https://zenodo.org/records/16556029/files/GDP_per_capita_PPP_1990_2015_v2.nc,"Primary link is a direct download, earlier part of zenodo bundle" -ghg_emissions,primary,v23,"['latest', 'supported']",https://web.archive.org/web/20200622130401if_/https://www.eea.europa.eu/data-and-maps/data/national-emissions-reported-to-the-unfccc-and-to-the-eu-greenhouse-gas-monitoring-mechanism-16/national-greenhouse-gas-inventories-ipcc-common-reporting-format-sector-classification/ascii-delimited-zip-2/at_download/file, -ghg_emissions,archive,v23,"['latest', 'supported']",https://zenodo.org/records/16561661/files/UNFCCC_v23.csv,earlier part of databundle -population_count,primary,2018-11-01,"['latest', 'supported']",https://data.worldpop.org/GIS/Population/Global_2000_2020/2019/0_Mosaicked/ppp_2019_1km_Aggregated.tif, -population_count,archive,2018-11-01,"['latest', 'supported']",https://zenodo.org/records/16558833/files/ppp_2019_1km_Aggregated.tif,earlier part of zenodo bundle -gebco,primary,2014,"['latest', 'supported']",https://www.bodc.ac.uk/data/open_download/gebco/GEBCO_30SEC/zip/,The dataset will be cut for the required regions after download -gebco,archive,2014,"['latest', 'supported']",https://zenodo.org/records/16810417/files/GEBCO_2014_2D.nc,Earlier part of databundle -attributed_ports,primary,2020-07-10,"['latest', 'supported']",https://datacatalogfiles.worldbank.org/ddh-published/0038118/1/DR0046414/attributed_ports.geojson, -attributed_ports,archive,2020-07-10,"['latest', 'supported']",https://zenodo.org/records/16810901/files/attributed_ports.json,Moved from github repo `data/` folder -corine,archive,v18_5,"['latest', 'supported']",https://zenodo.org/records/16899113/files/corine.zip, -corine,primary,unknown,"['latest', 'supported']",,Need to register with CLMS API and create an access token. The download URL is dynamic -emobility,archive,28-08-2016,"['latest', 'supported']",https://zenodo.org/records/16899168/files/emobility.zip, -h2_salt_caverns,archive,16-10-2019,"['latest', 'supported']",https://zenodo.org/records/16899309/files/h2_salt_caverns_GWh_per_sqkm.geojson, -lau_regions,primary,2019,"['latest', 'supported']",https://gisco-services.ec.europa.eu/distribution/v2/lau/download/ref-lau-2019-01m.geojson.zip, -aquifer_data,primary,v1.2,"['latest', 'supported']",https://download.bgr.de/bgr/grundwasser/IHME1500/v12/shp/IHME1500_v12.zip, -aquifer_data,archive,v1.2,"['latest', 'supported']",https://zenodo.org/records/16946750/files/IHME1500_v12.zip, -lau_regions,archive,2019,"['latest', 'supported']",https://zenodo.org/records/16947002/files/ref-lau-2019-01m.geojson.zip, -osm_boundaries,primary,unknown,"['latest', 'supported']",,Overpass API is used to fetch the latest available data -osm_boundaries,archive,unknown,"['latest', 'supported']",https://zenodo.org/records/16992755/files/osm_boundaries.zip, -tyndp,primary,2024,"['latest', 'supported']",https://2024-data.entsos-tyndp-scenarios.eu/files/scenarios-inputs, -tyndp,archive,2024,"['latest', 'supported']",https://zenodo.org/records/16759672/files, -powerplants,primary,0.7.1,"['latest', 'supported']",https://raw.githubusercontent.com/PyPSA/powerplantmatching/refs/tags/v0.7.1/powerplants.csv,"Part of the `powerplantmatching` package and versioned on GitHub, i.e. no dedicated 'archive' entry." -powerplants,primary,0.7.0,['supported'],https://raw.githubusercontent.com/PyPSA/powerplantmatching/refs/tags/v0.7.0/powerplants.csv,"Part of the `powerplantmatching` package and versioned on GitHub, i.e. no dedicated 'archive' entry." -costs,primary,v0.13.3,"['latest', 'supported']",https://raw.githubusercontent.com/PyPSA/technology-data/v0.13.3/outputs,"Part of the `technologydata` repository and versioned on GitHub, i.e. no dedicated 'archive' entry." -costs,primary,v0.13.2,['partially-supported'],https://raw.githubusercontent.com/PyPSA/technology-data/v0.13.2/outputs,"Part of the `technologydata` repository and versioned on GitHub, i.e. no dedicated 'archive' entry." -country_runoff,archive,2025-08-13,"['latest', 'supported']",https://zenodo.org/records/16849356/files/era5-runoff-per-country.csv, -country_runoff,build,unknown,"['latest', 'supported']",,Latest dataset built using ERA5 runoff data. This takes a very long time to build. -country_hdd,archive,2025-08-13,"['latest', 'supported']",https://zenodo.org/records/16849356/files/era5-HDD-per-country.csv, -country_hdd,build,unknown,"['latest', 'supported']",,Latest dataset built using ERA5 runoff data. This takes a very long time to build. -natura,archive,2025-08-15,"['latest', 'supported']",https://zenodo.org/records/16881818/files/natura.tiff, -natura,archive,v0.8.2,['supported'],https://zenodo.org/records/16874772/files/natura.tiff, -natura,build,unknown,"['latest', 'supported']",https://sdi.eea.europa.eu/datashare/s/tWpGXaWdWGYcqsL/download, -bfs_road_vehicle_stock,archive,2024-03-13,"['latest', 'supported']",https://raw.githubusercontent.com/PyPSA/pypsa-eur/5b5d308bf70f15dd0b107d8a19c121093dcdd5bd/data/gr-e-11.03.02.01.01-cc.csv, -bfs_road_vehicle_stock,primary,unknown,"['latest', 'supported']",https://datawrapper.dwcdn.net/31f3521eddfa82ada1a436983c31caf5/1/dataset.csv,This dataset is not versioned and is updated regularly. The link points to the latest version. More information on the latest update on this website: https://www.bfs.admin.ch/bfs/de/home/statistiken/kataloge-datenbanken.assetdetail.33827666.html -bfs_gdp_and_population,archive,2019-03-28,"['latest', 'supported']",https://web.archive.org/web/20250818151254if_/https://dam-api.bfs.admin.ch/hub/api/dam/assets/7786557/master, -bfs_gdp_and_population,primary,2019-03-28,"['latest', 'supported']",https://dam-api.bfs.admin.ch/hub/api/dam/assets/7786557/master, -mobility_profiles,build,unknown,"['latest', 'supported']",https://www.bast.de/videos/{year}_{street_type}_S.zip,"Latest dataset built using BASt vehicle monitoring data. The URL is a template that is filled based on the config file, see the documentation for details." -mobility_profiles,archive,2025-08-27,"['latest', 'supported']",https://zenodo.org/records/16965042/files,New version recreating the dataset from the data bundle. -mobility_profiles,archive,2025-08-13,['supported'],https://zenodo.org/records/16964996/files,Data from the original PyPSA-Eur data bundle. -cutout,archive,v0.8,"['latest', 'supported']",https://zenodo.org/records/15349674,Pre-build cutouts for PyPSA-Eur -cutout,build,unknown,"['latest', 'supported']",,Build latest cutouts using ERA5/SARAH3 data. -dh_areas,primary,341.5,"['latest', 'supported']",https://fordatis.fraunhofer.de/bitstream/fordatis/341.5/2/dh_areas.gpkg, -dh_areas,archive,341.5,"['latest', 'supported']",https://zenodo.org/records/17207640/files/dh_areas.gpkg, -geothermal_heat_utilisation_potentials,primary,341.5,"['latest', 'supported']",https://fordatis.fraunhofer.de/bitstream/fordatis/341.5/11/Results_DH_Matching_Cluster.xlsx, -geothermal_heat_utilisation_potentials,archive,341.5,"['latest', 'supported']",https://zenodo.org/records/17207640/files/Results_DH_Matching_Cluster.xlsx, -jrc_ardeco,primary,2021,"['latest', 'supported']",https://territorial.ec.europa.eu/ardeco-api-v2/rest/export/, -jrc_ardeco,archive,2021,"['latest', 'supported']",https://zenodo.org/records/17249457/files, -ariadne_database,primary,v1.0,"['latest', 'supported']",https://ariadne2.apps.ece.iiasa.ac.at/explorer,Public facing database of the second Ariadne Szenarienbericht -ariadne_database,archive,v1.0,"['latest', 'supported']",https://zenodo.org/records/15174592/files/250505_Ariadne2_Data_v1.0.xlsx, -open_mastr,primary,2023-08-08,"['latest', 'supported']",https://zenodo.org/records/8225106/files/bnetza_open_mastr_2023-08-08_B.zip, -egon,primary,2018-2020?,"['latest', 'supported']",https://api.opendata.ffe.de/demandregio/demandregio_spatial,root url is extended in the retrieve rule -ariadne_template,primary,2025-11-20,"['latest', 'supported']",https://github.com/iiasa/ariadne-intern-workflow/blob/main/attachments/2025-11-20_template_Ariadne.xlsx, \ No newline at end of file +dataset,version,source,tags,added,note,url +ariadne_database,v1.0,primary,latest supported,2026-01-15,Public facing database of the second Ariadne Szenarienbericht,https://ariadne2.apps.ece.iiasa.ac.at/explorer +ariadne_database,v1.0,archive,latest supported,2026-01-15,,https://zenodo.org/records/15174592/files/250505_Ariadne2_Data_v1.0.xlsx +ariadne_template,2025-11-20,primary,latest supported,2026-01-15,,https://github.com/iiasa/ariadne-intern-workflow/blob/main/attachments/2025-11-20_template_Ariadne.xlsx +egon,2018-2020?,primary,latest supported,2026-01-15,root url is extended in the retrieve rule,https://api.opendata.ffe.de/demandregio/demandregio_spatial +open_mastr,2023-08-08,primary,latest supported,2026-01-15,,https://zenodo.org/records/8225106/files/bnetza_open_mastr_2023-08-08_B.zip +aquifer_data,v1.2,primary,latest supported,2025-12-02,,https://download.bgr.de/bgr/grundwasser/IHME1500/v12/shp/IHME1500_v12.zip +aquifer_data,v1.2,archive,latest supported,2026-01-13,,https://data.pypsa.org/workflows/eur/aquifer_data/v1.2/IHME1500_v12.zip +attributed_ports,2020-07-10,primary,latest supported,2025-12-02,,https://datacatalogfiles.worldbank.org/ddh-published/0038118/1/DR0046414/attributed_ports.geojson +attributed_ports,2020-07-10,archive,latest supported,2026-01-13,Moved from github repo `data/` folder,https://data.pypsa.org/workflows/eur/attributed_ports/2020-07-10/attributed_ports.json +bfs_gdp_and_population,2019-03-28,primary,latest supported,2025-12-02,,https://dam-api.bfs.admin.ch/hub/api/dam/assets/7786557/master +bfs_gdp_and_population,2019-03-28,archive,latest supported,2025-12-02,,https://web.archive.org/web/20250818151254if_/https%3A//dam-api.bfs.admin.ch/hub/api/dam/assets/7786557/master +bfs_road_vehicle_stock,unknown,primary,latest supported,2025-12-02,This dataset is not versioned and is updated regularly. The link points to the latest version. More information on the latest update on this website: https://www.bfs.admin.ch/bfs/de/home/statistiken/kataloge-datenbanken.assetdetail.33827666.html,https://datawrapper.dwcdn.net/31f3521eddfa82ada1a436983c31caf5/1/dataset.csv +bfs_road_vehicle_stock,2024-03-13,archive,latest supported,2025-12-02,,https://raw.githubusercontent.com/PyPSA/pypsa-eur/5b5d308bf70f15dd0b107d8a19c121093dcdd5bd/data/gr-e-11.03.02.01.01-cc.csv +bidding_zones_electricitymaps,v1.238.0,primary,latest supported,2026-01-22,,https://raw.githubusercontent.com/electricitymaps/electricitymaps-contrib/refs/tags/v1.238.0/web/geo/world.geojson +bidding_zones_electricitymaps,v1.238.0,archive,latest supported,2026-01-22,,https://data.pypsa.org/workflows/eur/bidding_zones_electricitymaps/v1.238.0/world.geojson +bidding_zones_entsoepy,v0.6.18,primary,latest supported,2026-01-22,,https://raw.githubusercontent.com/EnergieID/entsoe-py/refs/tags/V0.6.18/entsoe/geo/geojson +bidding_zones_entsoepy,v0.6.18,archive,latest supported,2026-01-22,,https://data.pypsa.org/workflows/eur/bidding_zones_entsoepy/v0.6.18 +co2stop,26-august-2020,primary,latest supported,2025-12-02,,https://setis.ec.europa.eu/document/download/786a884f-0b33-4789-b744-28004b16bd1a_en?filename=co2jrc_openformats.zip +co2stop,26-august-2020,archive,latest supported,2026-01-13,,https://data.pypsa.org/workflows/eur/co2stop/26-august-2020/co2jrc_openformats.zip +copernicus_land_cover,v3.0.1,primary,latest supported,2025-12-02,"The primary is already from Zenodo, documentation in https://zenodo.org/records/4723921",https://zenodo.org/records/3939050/files/PROBAV_LC100_global_v3.0.1_2019-nrt_Discrete-Classification-map_EPSG-4326.tif +copernicus_land_cover,v3.0.1,archive,latest supported,2026-01-20,"The primary is already from Zenodo, documentation in https://zenodo.org/records/4723921",https://data.pypsa.org/workflows/eur/copernicus_land_cover/v3.0.1 +copernicus_land_cover,v2.0.2,archive,deprecated supported,2026-01-13,,https://data.pypsa.org/workflows/eur/copernicus_land_cover/v2.0.2/PROBAV_LC100_global_v3.0.1_2015-base_Discrete-Classification-map_EPSG-4326.tif +corine,v18_5,archive,latest supported,2026-01-13,,https://data.pypsa.org/workflows/eur/corine/v18_5/corine.zip +corine,unknown,primary,latest supported,2025-12-02,Need to register with CLMS API and create an access token. The download URL is dynamic, +costs,v0.13.4,primary,latest supported,2025-12-02,"Part of the `technologydata` repository and versioned on GitHub, i.e. no dedicated 'archive' entry.",https://raw.githubusercontent.com/PyPSA/technology-data/refs/tags/v0.13.4/outputs +costs,v0.13.4,archive,supported,2026-01-21,"Part of the `technologydata` repository and versioned on GitHub, i.e. no dedicated 'archive' entry.",https://data.pypsa.org/workflows/eur/costs/v0.13.4 +costs,v0.13.3,primary,supported,2025-12-02,"Part of the `technologydata` repository and versioned on GitHub, i.e. no dedicated 'archive' entry.",https://raw.githubusercontent.com/PyPSA/technology-data/refs/tags/v0.13.3/outputs +costs,v0.13.3,archive,supported,2026-01-20,"Part of the `technologydata` repository and versioned on GitHub, i.e. no dedicated 'archive' entry.",https://data.pypsa.org/workflows/eur/costs/v0.13.3 +costs,v0.13.2,primary,supported,2025-12-02,"Part of the `technologydata` repository and versioned on GitHub, i.e. no dedicated 'archive' entry.",https://raw.githubusercontent.com/PyPSA/technology-data/refs/tags/v0.13.2/outputs +costs,v0.13.2,archive,supported,2026-01-20,"Part of the `technologydata` repository and versioned on GitHub, i.e. no dedicated 'archive' entry.",https://data.pypsa.org/workflows/eur/costs/v0.13.2 +country_hdd,unknown,build,latest supported,2025-12-02,Latest dataset built using ERA5 runoff data. This takes a very long time to build., +country_hdd,2025-08-13,primary,latest supported,2025-12-02,,https://zenodo.org/records/16849356/files/era5-HDD-per-country.csv +country_hdd,2025-08-13,archive,latest supported,2026-01-13,,https://data.pypsa.org/workflows/eur/country_hdd/2025-08-13/era5-HDD-per-country.csv +country_runoff,unknown,build,latest supported,2025-12-02,Latest dataset built using ERA5 runoff data. This takes a very long time to build., +country_runoff,2025-08-13,primary,latest supported,2025-12-02,,https://zenodo.org/records/16849356/files/era5-runoff-per-country.csv +country_runoff,2025-08-13,archive,latest supported,2026-01-13,,https://data.pypsa.org/workflows/eur/country_runoff/2025-08-13/era5-runoff-per-country.csv +cutout,v1.0,archive,latest supported,2026-01-13,Pre-build cutouts for PyPSA-Eur,https://data.pypsa.org/workflows/cutout/v1.0 +cutout,unknown,build,latest supported,2025-12-02,Build latest cutouts using ERA5/SARAH3 data., +dh_areas,341.5,primary,latest supported,2025-12-02,,https://fordatis.fraunhofer.de/bitstream/fordatis/341.5/2/dh_areas.gpkg +dh_areas,341.5,archive,latest supported,2026-01-13,,https://data.pypsa.org/workflows/eur/dh_areas/341.5/dh_areas.gpkg +eez,v12_20231025,primary,latest supported,2025-12-02,API request used,https://www.marineregions.org/download_file.php +eez,v12_20231025,archive,latest supported,2026-01-13,,https://data.pypsa.org/workflows/eur/eez/v12_20231025/World_EEZ_v12_20231025_LR.zip +emobility,28-08-2016,primary,latest supported,2025-12-02,,https://zenodo.org/records/16899168/files/emobility.zip +emobility,28-08-2016,archive,latest supported,2026-01-13,,https://data.pypsa.org/workflows/eur/emobility/28-08-2016/emobility.zip +enspreso_biomass,unknown,primary,latest supported,2025-12-02,"2019 version was changed in 2023 with negligible differences, but since the version is not frozen we label it as 'unknown'.",https://jeodpp.jrc.ec.europa.eu/ftp/jrc-opendata/ENSPRESO/ENSPRESO_BIOMASS.xlsx +enspreso_biomass,2019-06-20,archive,latest supported,2026-01-13,,https://data.pypsa.org/workflows/eur/enspreso_biomass/2019-06-20/ENSPRESO_BIOMASS.xlsx +eu_nuts2013,2015-12-03,primary,latest supported,2025-12-02, ,https://gisco-services.ec.europa.eu/distribution/v2/nuts/download/ref-nuts-2013-03m.geojson.zip +eu_nuts2013,2015-12-03,archive,latest supported,2026-01-13,,https://data.pypsa.org/workflows/eur/eu_nuts2013/2015-12-03/ref-nuts-2013-03m.geojson.zip +eu_nuts2021,2021-01-01,primary,latest supported,2025-12-02,,https://gisco-services.ec.europa.eu/distribution/v2/nuts/download/ref-nuts-2021-01m.geojson.zip +eu_nuts2021,2021-01-01,archive,latest supported,2026-01-13,,https://data.pypsa.org/workflows/eur/eu_nuts2021/2021-01-01/ref-nuts-2021-01m.geojson.zip +eurostat_balances,2023-04,primary,latest supported broken-link,2025-12-02,"The link is broken, use the archived versions instead.",https://ec.europa.eu/eurostat/documents/38154/4956218/Balances-April2023.zip +eurostat_balances,2023-04,archive,latest supported,2026-01-13,,https://data.pypsa.org/workflows/eur/eurostat_balances/2023-04/balances.zip +eurostat_household_balances,unknown,primary,latest supported,2025-12-02,"URL limits the period to 2013-2022, but the data is updated regularly.",https://ec.europa.eu/eurostat/databrowser-backend/api/extraction/1.0/LIVE/false/sdmx/csv/nrg_d_hhq__custom_11480365?startPeriod=2013&endPeriod=2022 +eurostat_household_balances,2025-07-09,archive,latest supported,2026-01-13,,https://data.pypsa.org/workflows/eur/eurostat_household_balances/2025-07-09/nrg_d_hhq.csv +gdp_per_capita,2018-02-06,primary,latest supported,2025-12-02,"Primary link is a direct download, earlier part of zenodo bundle",https://zenodo.org/records/16556029/files/GDP_per_capita_PPP_1990_2015_v2.nc +gdp_per_capita,2018-02-06,archive,latest supported,2026-01-13,"Primary link is a direct download, earlier part of zenodo bundle",https://data.pypsa.org/workflows/eur/gdp_per_capita/2018-02-06/GDP_per_capita_PPP_1990_2015_v2.nc +gebco,2014,primary,latest supported,2025-12-02,The dataset will be cut for the required regions after download,https://www.bodc.ac.uk/data/open_download/gebco/GEBCO_30SEC/zip/ +gebco,2014,archive,latest supported,2026-01-13,Earlier part of databundle,https://data.pypsa.org/workflows/eur/gebco/2014/GEBCO_2014_2D.nc +gem_europe_gas_tracker,may-2024,primary,latest supported,2025-12-02,,https://globalenergymonitor.org/wp-content/uploads/2024/05/Europe-Gas-Tracker-2024-05.xlsx +gem_europe_gas_tracker,may-2024,archive,latest supported,2026-01-13,,https://data.pypsa.org/workflows/eur/gem_europe_gas_tracker/may-2024/Europe-Gas-Tracker-2024-05.xlsx +gem_gspt,march-2025-v1,primary,not-supported,2025-12-02,,https://globalenergymonitor.org/wp-content/uploads/2025/03/Plant-level-data-Global-Iron-and-Steel-Tracker-March-2025-V1.xlsx +gem_gspt,april-2024-v1,archive,latest supported,2026-01-13,,https://data.pypsa.org/workflows/eur/gem_gspt/april-2024-v1/Global-Steel-Plant-Tracker-April-2024-Standard-Copy-V1.xlsx +geothermal_heat_utilisation_potentials,341.5,primary,latest supported,2025-12-02,,https://fordatis.fraunhofer.de/bitstream/fordatis/341.5/11/Results_DH_Matching_Cluster.xlsx +geothermal_heat_utilisation_potentials,341.5,archive,latest supported,2026-01-13,,https://data.pypsa.org/workflows/eur/geothermal_heat_utilisation_potentials/341.5/Results_DH_Matching_Cluster.xlsx +ghg_emissions,v23,primary,latest supported,2025-12-02,,https://web.archive.org/web/20200622130401if_/https%3A//www.eea.europa.eu/data-and-maps/data/national-emissions-reported-to-the-unfccc-and-to-the-eu-greenhouse-gas-monitoring-mechanism-16/national-greenhouse-gas-inventories-ipcc-common-reporting-format-sector-classification/ascii-delimited-zip-2/at_download/file +ghg_emissions,v23,archive,latest supported,2026-01-13,earlier part of databundle,https://data.pypsa.org/workflows/eur/ghg_emissions/v23/UNFCCC_v23.csv +h2_salt_caverns,16-10-2019,primary,latest supported,2025-12-02,,https://zenodo.org/records/16899309/files/h2_salt_caverns_GWh_per_sqkm.geojson +h2_salt_caverns,16-10-2019,archive,latest supported,2026-01-13,,https://data.pypsa.org/workflows/eur/h2_salt_caverns/16-10-2019/h2_salt_caverns_GWh_per_sqkm.geojson +hotmaps_industrial_sites,version-1.1,archive,latest supported,2026-01-13,,https://data.pypsa.org/workflows/eur/hotmaps_industrial_sites/version-1.1/Industrial_Database.csv +hotmaps_industrial_sites,version-1.0,primary,not-supported,2025-12-02,Required renaming of country names to work with PyPSA-Eur. This was fixed in the latest version.,https://gitlab.com/hotmaps/industrial_sites/industrial_sites_Industrial_Database/-/raw/1.0/data/Industrial_Database.csv +hotmaps_industrial_sites,unknown,primary,latest supported,2025-12-02,,https://gitlab.com/hotmaps/industrial_sites/industrial_sites_Industrial_Database/-/raw/master/data/Industrial_Database.csv +jrc_ardeco,2021,primary,latest supported,2025-12-02,,https://territorial.ec.europa.eu/ardeco-api-v2/rest/export/ +jrc_ardeco,2021,archive,latest supported,2026-01-13,,https://data.pypsa.org/workflows/eur/jrc_ardeco/2021 +jrc_idees,unknown,primary,latest supported,2025-12-02,,https://jeodpp.jrc.ec.europa.eu/ftp/jrc-opendata/JRC-IDEES/JRC-IDEES-2021_v1/JRC-IDEES-2021.zip +jrc_idees,2024-05-20,archive,latest supported,2026-01-13,,https://data.pypsa.org/workflows/eur/jrc_idees/2024-05-20/JRC-IDEES-2021.zip +lau_regions,2019,primary,latest supported,2025-12-02,,https://gisco-services.ec.europa.eu/distribution/v2/lau/download/ref-lau-2019-01m.geojson.zip +lau_regions,2019,archive,latest supported,2026-01-13,,https://data.pypsa.org/workflows/eur/lau_regions/2019/ref-lau-2019-01m.geojson.zip +luisa_land_cover,unknown,primary,latest supported,2025-12-02,,https://jeodpp.jrc.ec.europa.eu/ftp/jrc-opendata/LUISA/EUROPE/Basemaps/LandUse/2018/LATEST/LUISA_basemap_020321_50m.tif +luisa_land_cover,2021-03-02,archive,latest supported,2026-01-13,,https://data.pypsa.org/workflows/eur/luisa_land_cover/2021-03-02/LUISA_basemap_020321_50m.tif +mobility_profiles,unknown,build,latest supported,2025-12-02,"Latest dataset built using BASt vehicle monitoring data. The URL is a template that is filled based on the config file, see the documentation for details.",https://www.bast.de/videos/{year}_{street_type}_S.zip +mobility_profiles,2025-08-27,primary,latest supported,2025-12-02,New version recreating the dataset from the data bundle.,https://zenodo.org/records/16965042/files +mobility_profiles,2025-08-27,archive,latest supported,2026-01-13,New version recreating the dataset from the data bundle.,https://data.pypsa.org/workflows/eur/mobility_profiles/2025-08-27 +mobility_profiles,2025-08-13,primary,supported,2025-12-02,Data from the original PyPSA-Eur data bundle.,https://zenodo.org/records/16964996/files +mobility_profiles,2025-08-13,archive,supported,2026-01-13,Data from the original PyPSA-Eur data bundle.,https://data.pypsa.org/workflows/eur/mobility_profiles/2025-08-13 +natura,v0.8.2,primary,supported,2025-12-02,,https://zenodo.org/records/16874772/files/natura.tiff +natura,v0.8.2,archive,supported,2026-01-13,,https://data.pypsa.org/workflows/eur/natura/v0.8.2/natura.tiff +natura,unknown,build,latest supported,2025-12-02,,https://sdi.eea.europa.eu/datashare/s/tWpGXaWdWGYcqsL/download +natura,2025-08-15,primary,latest supported,2025-12-02,,https://zenodo.org/records/16881818/files/natura.tiff +natura,2025-08-15,archive,latest supported,2026-01-13,,https://data.pypsa.org/workflows/eur/natura/2025-08-15/natura.tiff +nitrogen_statistics,2023,primary,not-tested,2025-12-02,,https://d9-wret.s3.us-west-2.amazonaws.com/assets/palladium/production/s3fs-public/media/files/myb1-2023-nitro-ERT.xlsx +nitrogen_statistics,2022,primary,latest supported,2025-12-02,,https://d9-wret.s3.us-west-2.amazonaws.com/assets/palladium/production/s3fs-public/media/files/myb1-2022-nitro-ert.xlsx +nitrogen_statistics,2022,archive,latest supported,2026-01-13,,https://data.pypsa.org/workflows/eur/nitrogen_statistics/2022/nitro-ert.xlsx +nuts3_population,13-03-2025,primary,latest supported,2025-12-02,,https://ec.europa.eu/eurostat/api/dissemination/sdmx/2.1/data/nama_10r_3popgdp?format=TSV&compressed=true +nuts3_population,13-03-2025,archive,latest supported,2026-01-13,earlier part of zenodo bundle,https://data.pypsa.org/workflows/eur/nuts3_population/13-03-2025/nama_10r_3popgdp.tsv.gz +osm,unknown,build,latest supported,2025-12-02,Latest dataset built using OSM data. No single URL available to access the data. See the related scripts and documentation for details., +osm,0.6,primary,latest supported,2026-01-13,,https://zenodo.org/records/14144752/files +osm,0.6,archive,latest supported,2026-01-13,,https://data.pypsa.org/workflows/eur/osm/0.6 +osm,0.5,primary,deprecated not-supported,2026-01-13,,https://zenodo.org/records/13981528/files +osm,0.5,archive,deprecated not-supported,2026-01-13,,https://data.pypsa.org/workflows/eur/osm/0.5 +osm,0.4,primary,deprecated not-supported,2026-01-13,,https://zenodo.org/records/13759222/files +osm,0.4,archive,deprecated not-supported,2026-01-13,,https://data.pypsa.org/workflows/eur/osm/0.4 +osm,0.3,primary,deprecated not-supported,2026-01-13,,https://zenodo.org/records/13358976/files +osm,0.3,archive,deprecated not-supported,2026-01-13,,https://data.pypsa.org/workflows/eur/osm/0.3 +osm,0.2,primary,deprecated not-supported,2026-01-13,,https://zenodo.org/records/13342577/files +osm,0.2,archive,deprecated not-supported,2026-01-13,,https://data.pypsa.org/workflows/eur/osm/0.2 +osm,0.1,primary,deprecated not-supported,2026-01-13,,https://zenodo.org/records/12799202/files +osm,0.1,archive,deprecated not-supported,2026-01-13,,https://data.pypsa.org/workflows/eur/osm/0.1 +osm_boundaries,unknown,primary,latest supported,2025-12-02,Overpass API is used to fetch the latest available data, +osm_boundaries,unknown,archive,latest supported,2026-01-13,,https://data.pypsa.org/workflows/eur/osm_boundaries/unknown/osm_boundaries.zip +population_count,2018-11-01,primary,latest supported,2025-12-02,,https://data.worldpop.org/GIS/Population/Global_2000_2020/2019/0_Mosaicked/ppp_2019_1km_Aggregated.tif +population_count,2018-11-01,archive,latest supported,2026-01-13,earlier part of zenodo bundle,https://data.pypsa.org/workflows/eur/population_count/2018-11-01/ppp_2019_1km_Aggregated.tif +powerplants,0.7.1,primary,latest supported,2025-12-02,"Part of the `powerplantmatching` package and versioned on GitHub, i.e. no dedicated 'archive' entry.",https://raw.githubusercontent.com/PyPSA/powerplantmatching/refs/tags/v0.7.1/powerplants.csv +powerplants,0.7.1,archive,latest supported,2026-01-20,"Part of the `powerplantmatching` package and versioned on GitHub, i.e. no dedicated 'archive' entry.",https://data.pypsa.org/workflows/eur/powerplants/0.7.1 +powerplants,0.7.0,primary,supported,2025-12-02,"Part of the `powerplantmatching` package and versioned on GitHub, i.e. no dedicated 'archive' entry.",https://raw.githubusercontent.com/PyPSA/powerplantmatching/refs/tags/v0.7.0/powerplants.csv +powerplants,0.7.0,archive,supported,2026-01-20,"Part of the `powerplantmatching` package and versioned on GitHub, i.e. no dedicated 'archive' entry.",https://data.pypsa.org/workflows/eur/powerplants/0.7.0 +scigrid_gas,1.1.2,primary,latest supported,2025-12-02,the primary is already from Zenodo published by original authors,https://zenodo.org/records/4767098/files/IGGIELGN.zip +scigrid_gas,1.1.2,archive,latest supported,2026-01-20,the primary is already from Zenodo published by original authors,https://data.pypsa.org/workflows/eur/scigrid_gas/1.1.2 +scigrid_gas,1.1.1,archive,deprecated not-supported,2026-01-13,,https://data.pypsa.org/workflows/eur/scigrid_gas/1.1.1/IGGIELGN.zip +scigrid_gas,1.1.0,archive,deprecated not-supported,2026-01-13,,https://data.pypsa.org/workflows/eur/scigrid_gas/1.1.0/IGGIELGN.zip +seawater_temperature,v1.0,primary,latest supported,2026-01-21,Test cutout seawater temperature,https://zenodo.org/records/15828866/files/seawater_temperature.nc +seawater_temperature,v1.0,archive,latest supported,2026-01-21,Test cutout seawater temperature,https://data.pypsa.org/workflows/eur/seawater_temperature/v1.0/seawater_temperature.nc +ship_raster,v5,primary,latest supported,2025-12-02,,https://datacatalogfiles.worldbank.org/ddh-published/0037580/5/DR0045406/shipdensity_global.zip +ship_raster,v5,archive,latest supported,2026-01-13,,https://data.pypsa.org/workflows/eur/ship_raster/v5/shipdensity_global.zip +synthetic_electricity_demand,v2,primary,latest supported,2025-12-02,the primary is already from Zenodo published by original authors,https://zenodo.org/records/10820928/files/demand_hourly.csv +synthetic_electricity_demand,v2,archive,latest supported,2026-01-20,the primary is already from Zenodo published by original authors,https://data.pypsa.org/workflows/eur/synthetic_electricity_demand/v2 +synthetic_electricity_demand,0.1.0,archive,deprecated might-work,2026-01-13,,https://data.pypsa.org/workflows/eur/synthetic_electricity_demand/0.1.0/demand_hourly.csv +tyndp,2024,primary,latest supported,2025-12-02,,https://2024-data.entsos-tyndp-scenarios.eu/files/scenarios-inputs +tyndp,2024,archive,latest supported,2026-01-13,,https://data.pypsa.org/workflows/eur/tyndp/2024 +wdpa,unknown,primary,latest supported,2025-12-02,"WDPA changes its URL every month, the URL here is used as a template and {bYYYY} is replaced inside the retrieve.smk.",https://d1gam3xoknrgr2.cloudfront.net/current/WDPA_{bYYYY}_Public_shp.zip +wdpa,2025-07,archive,latest supported,2025-12-02,"We are legally not allowed to redistribute this dataset, luckily the web archive is keeping copies of it.",https://web.archive.org/web/20250715071823if_/https%3A//d1gam3xoknrgr2.cloudfront.net/current/WDPA_Jul2025_Public_shp.zip +wdpa_marine,unknown,primary,latest supported,2025-12-02,"WDPA maritime changes its URL every month, the URL here is used as a template and {bYYYY} is replaced inside the retrieve.smk.",https://d1gam3xoknrgr2.cloudfront.net/current/WDPA_WDOECM_{bYYYY}_Public_marine_shp.zip +wdpa_marine,2025-07,archive,latest supported,2025-12-02,"We are legally not allowed to redistribute this dataset, luckily the web archive is keeping copies of it.",https://web.archive.org/web/20250715084308if_/https%3A//d1gam3xoknrgr2.cloudfront.net/current/WDPA_WDOECM_Jul2025_Public_marine_shp.zip +worldbank_urban_population,unknown,primary,latest might-work,2025-12-02,"This is the original World Bank API link, which is sometimes updated; it is not guaranteed to work with the current codebase and data changes without notice.",https://api.worldbank.org/v2/en/indicator/SP.URB.TOTL.IN.ZS?downloadformat=csv +worldbank_urban_population,2025-08-14,archive,latest supported,2026-01-13,,https://data.pypsa.org/workflows/eur/worldbank_urban_population/2025-08-14/API_SP.URB.TOTL.IN.ZS_DS2_en_csv_v2_22447.zip diff --git a/doc/configtables/data.csv b/doc/configtables/data.csv deleted file mode 100644 index 2f0b07567..000000000 --- a/doc/configtables/data.csv +++ /dev/null @@ -1,139 +0,0 @@ - ,Unit,Values,Description -hotmaps_industrial_sites,,, --- source,str,"{archive, primary}","Source of the Hotmaps industrial sites data." --- version,str,,"Version of the Hotmaps industrial sites data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -enspreso_biomass,,, --- source,str,"{archive, primary}","Source of the Enspreso biomass data." --- version,str,,"Version of the Enspreso biomass data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -osm,,, --- source,str,"{archive, build}","Source of the OSM data. 'archive' retrieves pre-built OSM data, 'build' uses the latest OSM data to build the network." --- version,str,,"Version of the OSM data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -worldbank_urban_population,,, --- source,str,"{archive, primary}","Source of the World Bank urban population data." --- version,str,,"Version of the World Bank urban population data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -gem_europe_gas_tracker,,, --- source,str,"{archive, primary}","Source of the GEM Europe Gas Tracker data." --- version,str,,"Version of the GEM Europe Gas Tracker data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -co2stop,,, --- source,str,"{archive, primary}","Source of the CO2Stop data." --- version,str,,"Version of the CO2Stop data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -nitrogen_statistics,,, --- source,str,"{archive, primary}","Source of the Nitrogen Statistics data." --- version,str,,"Version of the Nitrogen Statistics data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -eu_nuts2013,,, --- source,str,"{archive, primary}","Source of the EU NUTS 2013 data." --- version,str,,"Version of the EU NUTS 2013 data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -eu_nuts2021,,, --- source,str,"{archive, primary}","Source of the EU NUTS 2021 data." --- version,str,,"Version of the EU NUTS 2021 data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -eurostat_balances,,, --- source,str,"{archive, primary}","Source of the Eurostat balances data." --- version,str,,"Version of the Eurostat balances data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -eurostat_household_balances,,, --- source,str,"{archive, primary}","Source of the Eurostat household balances data." --- version,str,,"Version of the Eurostat household balances data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -wdpa,,, --- source,str,"{archive, primary}","Source of the WDPA data." --- version,str,,"Version of the WDPA data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -wdpa_marine,,, --- source,str,"{archive, primary}","Source of the WDPA Marine data." --- version,str,,"Version of the WDPA Marine data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -luisa_land_cover,,, --- source,str,"{archive, primary}","Source of the LUISA land cover data." --- version,str,,"Version of the LUISA land cover data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -jrc_idees,,, --- source,str,"{archive, primary}","Source of the JRC IDEES data." --- version,str,,"Version of the JRC IDEES data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -scigrid_gas,,, --- source,str,"{primary, archive}","Source of the SciGRID Gas data." --- version,str,,"Version of the SciGRID Gas data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -synthetic_electricity_demand,,, --- source,str,"{primary, archive}","Source of the Synthetic Electricity Demand data." --- version,str,,"Version of the Synthetic Electricity Demand data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -copernicus_land_cover,,, --- source,str,"{primary, archive}","Source of the Copernicus Land Cover data." --- version,str,,"Version of the Copernicus Land Cover data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -ship_raster,,, --- source,str,"{archive, primary}","Source of the Ship Raster data." --- version,str,,"Version of the Ship Raster data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -eez,,, --- source,str,"{archive, primary}","Source of the EEZ data." --- version,str,,"Version of the EEZ data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -nuts3_population,,, --- source,str,"{archive, primary}","Source of the NUTS3 population data." --- version,str,,"Version of the NUTS3 population data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -gdp_per_capita,,, --- source,str,"{archive, primary}","Source of the GDP per capita data." --- version,str,,"Version of the GDP per capita data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -population_count,,, --- source,str,"{archive, primary}","Source of the population count data." --- version,str,,"Version of the population count data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -ghg_emissions,,, --- source,str,"{archive, primary}","Source of the GHG emissions data." --- version,str,,"Version of the GHG emissions data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -gebco,,, --- source,str,"{archive, primary}","Source of the GEBCO data." --- version,str,,"Version of the GEBCO data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -attributed_ports,,, --- source,str,"{archive, primary}","Source of the Attributed Ports data." --- version,str,,"Version of the Attributed Ports data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -corine,,, --- source,str,"{archive, primary}","Source of the CORINE data." --- version,str,,"Version of the CORINE data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -emobility,,, --- source,str,"{archive, primary}","Source of the Emobility data." --- version,str,,"Version of the Emobility data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -h2_salt_caverns,,, --- source,str,"{archive, primary}","Source of the H2 Salt Caverns data." --- version,str,,"Version of the H2 Salt Caverns data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -lau_regions,,, --- source,str,"{archive, primary}","Source of the LAU Regions data." --- version,str,,"Version of the LAU Regions data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -aquifer_data,,, --- source,str,"{archive, primary}","Source of the Aquifer data." --- version,str,,"Version of the Aquifer data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -osm_boundaries,,, --- source,str,"{archive, build}","Source of the OSM Boundaries data." --- version,str,,"Version of the OSM Boundaries data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -gem_gspt,,, --- source,str,"{archive, primary}","Source of the Global Steel Plant Tracker data." --- version,str,,"Version of the Global Steel Plant Tracker data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -tyndp,,, --- source,str,"{archive, primary}","Source of the TYNDP data." --- version,str,,"Version of the TYNDP data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -powerplants,,, --- source,str,"{primary, archive}","Source of the Powerplants data." --- version,str,,"Version of the Powerplants data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -costs,,, --- source,str,"{primary, archive}","Source of the Costs data." --- version,str,,"Version of the Costs data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -country_runoff,,, --- source,str,"{archive, build}","Source of the Country Runoff data." --- version,str,,"Version of the Country Runoff data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -country_hdd,,, --- source,str,"{archive, build}","Source of the Country HDD data." --- version,str,,"Version of the Country HDD data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -natura,,, --- source,str,"{archive, build}","Source of the Natura data." --- version,str,,"Version of the Natura data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -bfs_road_vehicle_stock,,, --- source,str,"{archive, primary}","Source of the BFS Road Vehicle Stock data." --- version,str,,"Version of the BFS Road Vehicle Stock data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -bfs_gdp_and_population,,, --- source,str,"{archive, primary}","Source of the BFS GDP and Population data." --- version,str,,"Version of the BFS GDP and Population data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -mobility_profiles,,, --- source,str,"{archive, build}","Source of the Mobility Profiles data." --- version,str,,"Version of the Mobility Profiles data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -cutout,,, --- source,str,"{archive, build}","Source of the Cutout data." --- version,str,,"Version of the Cutout data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source." -dh_areas,,, --- source,str,"{archive, primary}","Source of the District Heating Areas data." --- version,str,,"Version of the District Heating Areas data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source."" -geothermal_heat_utilisation_potentials,,, --- source,str,"{archive, primary}","Source of the Geothermal Heat Utilisation Potentials data." --- version,str,,"Version of the Geothermal Heat Utilisation Potentials" -jrc_ardeco,,, --- source,str,"{archive, primary}","Source of annual regional database for EU regions" --- version,str,,"Version of the JRC ARDECO dataset" \ No newline at end of file diff --git a/doc/data_inventory.csv b/doc/data_inventory.csv index 133ce39d6..1516aa82b 100644 --- a/doc/data_inventory.csv +++ b/doc/data_inventory.csv @@ -41,4 +41,6 @@ "mobility_profiles","German Vehicle Activity Profiles","Vehicle activity profiles for different vehicle types and road types in Germany, based on monitoring data from the Federal Highway Research Institute (BASt). These profiles provide insights into travel behavior and patterns, which can be used for transport modeling and analysis.","Federal Highway Research Institute (BASt)","https://www.bast.de/DE/Themen/Digitales/HF_1/Massnahmen/verkehrszaehlung/Stundenwerte.html?nn=414410","CC-BY-4.0" "dh_areas","","Shapes of district heating areas","ISI Fraunhofer-Institut für System- und Innovationsforschung","https://fordatis.fraunhofer.de/handle/fordatis/341.5","CC-BY-4.0" "geothermal_heat_utilisation_potentials","","Potentials for Geothermal heat utilisation","ISI Fraunhofer-Institut für System- und Innovationsforschung","https://fordatis.fraunhofer.de/handle/fordatis/341.5","CC-BY-4.0" -"jrc_ardeco","Annual Regional Database of the European Commission's Directorate General for Regional and Urban Policy","The database contains a set of long time-series variables and indicators for EU regions, as well as for regions in some EFTA and candidate countries, at various statistical scales (NUTS1, NUTS2, NUTS3, metro regions).","European Commission","https://territorial.ec.europa.eu/ardeco","similar to CC-BY" \ No newline at end of file +"jrc_ardeco","Annual Regional Database of the European Commission's Directorate General for Regional and Urban Policy","The database contains a set of long time-series variables and indicators for EU regions, as well as for regions in some EFTA and candidate countries, at various statistical scales (NUTS1, NUTS2, NUTS3, metro regions).","European Commission","https://territorial.ec.europa.eu/ardeco","similar to CC-BY" +"bidding_zones_electricitymaps","Electricity Maps Bidding Zones","Geospatial data defining bidding zones for electricity markets in Europe","Electricity Maps","https://github.com/electricitymaps/electricitymaps-contrib","AGPL-3.0" +"bidding_zones_entsoepy","ENTSOE-PY Bidding Zones","Geospatial data defining bidding zones for electricity markets in Europe","EnergieID","https://github.com/EnergieID/entsoe-py","MIT" \ No newline at end of file diff --git a/doc/validation_dev.rst b/doc/validation_dev.rst new file mode 100644 index 000000000..dae7063fb --- /dev/null +++ b/doc/validation_dev.rst @@ -0,0 +1,170 @@ +.. SPDX-FileCopyrightText: Contributors to PyPSA-Eur +.. +.. SPDX-License-Identifier: CC-BY-4.0 + +########################################## +Validation +########################################## + +PyPSA-Eur uses `Pydantic `_ models for validation. +This system provides type checking, default values, and documentation in a single place. + +Configuration +============= + +The configuration validation system consists of: + +- **Pydantic models** in ``scripts/lib/validation/config/`` that define all options and validates the snakemake config. +- **Auto-generated files**: ``config/config.default.yaml`` and ``config/schema.json``. + +Adding a New Config Option +-------------------------- + +To add a new option to an existing config section, edit the corresponding module in +``scripts/lib/validation/config/``. Each field uses Pydantic's ``Field()`` function. + +For example, the ``logging`` section in ``scripts/lib/validation/config/__init__.py``: + +.. code-block:: python + + from typing import Literal + from pydantic import Field + from scripts.lib.validation.config._base import ConfigModel + + class LoggingConfig(ConfigModel): + """Configuration for top level `logging` settings.""" + + # ... existing fields ... + + # An option with default 0.5, float type and between 0 and 1. If anything else is passed, + # the validation will fail + new_option: float = Field( + 0.5, # default value + description="Threshold for the new feature.", # shown in docs and IDE + ge=0, # greater than or equal + le=1, # less than or equal + examples=[0.3, 0.7], # example values for docs + ) + +For more Field parameters, see the `Pydantic Field documentation `__. + +Adding a New Config Section +--------------------------- + +To add a nested config section, define a helper class and add it to an existing config. +For example, adding a ``file`` section to ``LoggingConfig``: + +.. code-block:: python + + class _LoggingFileConfig(ConfigModel): + """Configuration for logging to file.""" + + enabled: bool = Field(False, description="Enable file logging.") + path: str = Field("logs/pypsa.log", description="Log file path.") + format: str | None = Field(None, description="Custom log format for that file.") + + + class LoggingConfig(ConfigModel): + """Configuration for top level `logging` settings.""" + + # ... existing fields ... + + file: _LoggingFileConfig = Field( + default_factory=_LoggingFileConfig, + description="File logging configuration.", + ) + + +There is one python module for each top level configuration. Helper classes for nested +keys usee underscore prefix (e.g., ``_LoggingFileConfig``) by convention. + +Regenerating Config Files +------------------------- + +Snakemake will only read from the ``config/config.default.yaml``, which needs to be generated +after making changes to the Pydantic model. To regenerate the default config and JSON +schema: + +.. code-block:: console + + $ pixi run generate-config + +This updates ``config/config.default.yaml`` and ``config/schema.json``. +For example, the two examples above would now generate: + +.. code-block:: yaml + + logging: + level: INFO + format: "%(levelname)s:%(name)s:%(message)s" + new_option: 0.5 + file: + enabled: false + path: logs/pypsa.log + format: null + +Always commit these regenerated files alongside your model changes. + +Extending for Soft-Forks +------------------------ + +If you maintain a soft-fork of PyPSA-Eur with custom config options, you have two approaches: + +**Allow extra fields**: The ``ConfigSchema`` uses ``extra="allow"`` by default, so +unrecognized config keys won't cause validation errors. Your custom options will pass +through without type checking. Only if you changed existing config settings, you will +need to adjust the schema. But you will lose the sync of Pydantic model and defaults +YAML, which is currently enforced via an upstream CI job. + +**Extend the schema**: It is better to add full validation of your additional +configuration, which means you will need to update the Pydantic model as explained above. + +Custom Validators +----------------- + +For validation logic beyond simple type checks and constraints, Pydantic provides +``field_validator`` (for single fields) and ``model_validator`` (for cross-field validation). + +**Field Validator**: Validate a single field's value. For example, ensuring the log level +is uppercase: + +.. code-block:: python + + from pydantic import Field, field_validator + from scripts.lib.validation.config._base import ConfigModel + + class LoggingConfig(ConfigModel): + """Configuration for top level `logging` settings.""" + + level: str = Field("INFO", description="Logging level.") + + @field_validator("level") + @classmethod + def validate_level(cls, v): + if v.upper() != v: + raise ValueError("Logging level must be uppercase (e.g., 'INFO', 'DEBUG').") + return v + +**Model Validator**: Validate relationships between multiple fields. For example, +ensuring the file path is set when file logging is enabled: + +.. code-block:: python + + from pydantic import Field, model_validator + from scripts.lib.validation.config._base import ConfigModel + + class LoggingConfig(ConfigModel): + """Configuration for top level `logging` settings.""" + + file_enabled: bool = Field(False, description="Enable file logging.") + file_path: str | None = Field(None, description="Log file path.") + + @model_validator + def check_file_path_required(self): + if self.file_enabled and not self.file_path: + raise ValueError("file_path is required when file_enabled is True.") + return self + +Again, find more information in the Pydantic documentation on +`Field Validators `_ +and `Model Validators `_. diff --git a/envs/default_linux-64.pin.txt b/envs/default_linux-64.pin.txt index 3d533f86a..159d1f658 100644 --- a/envs/default_linux-64.pin.txt +++ b/envs/default_linux-64.pin.txt @@ -25,7 +25,7 @@ https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.3-hecca717_0.conda# https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda#4a13eeac0b5c8e5b8ab496e6c4ddd829 https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_105.conda#3ec0aa5037d39b06554109a01e6fb0c6 https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda#51a19bba1b8ebfb60df25cde030b7ebc -https://conda.anaconda.org/conda-forge/linux-64/python-3.12.12-hd63d673_1_cpython.conda#5c00c8cea14ee8d02941cab9121dce41 +https://conda.anaconda.org/conda-forge/linux-64/python-3.13.11-hc97d973_100_cp313.conda#0cbb0010f1d8ecb64a428a8d4214609e https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda#962b9857ee8e7018c22f2776ffa0b2d7 https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_1.conda#9efbfdc37242619130ea42b1cc4ed861 https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda#9d64911b31d57ca443e9f1e36b04385f @@ -40,21 +40,24 @@ https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.1-py312h33ff503_0.cond https://conda.anaconda.org/conda-forge/linux-64/scipy-1.17.0-py312h54fa4ab_0.conda#9faccce05511d05f22001ecc2dfe78de https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda#4de79c071274a53dcaf2a8c749d1499e https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda#615de2a4d97af50c350e5cf160149e77 -https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.8.0-np2py312h3226591_1.conda#38decbeae260892040709cafc0514162 +https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.8.0-np2py313h16d504d_1.conda#d43a148434f123b3e060780d84a05ddc https://conda.anaconda.org/conda-forge/noarch/ply-3.11-pyhd8ed1ab_3.conda#fd5062942bfa1b0bd5e0d2a4397b099e -https://conda.anaconda.org/conda-forge/linux-64/pyomo-6.9.5-py312h1289d80_0.conda#14653b1832d3fe7f51942e60ff2a5b00 +https://conda.anaconda.org/conda-forge/linux-64/pyomo-6.9.5-py313h7033f15_0.conda#920a5ce9078a275fc0313ee3717fd268 https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda#bc8e3267d44011051f2eb14d22fb0960 https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2025.3-pyhd8ed1ab_0.conda#7ead57407430ba33f681738905278d03 https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda#3339e3b65d58accf4ca4fb8748ab16b3 https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8 -https://conda.anaconda.org/conda-forge/linux-64/pandas-2.3.3-py312hf79963d_1.conda#e597b3e812d9613f659b7d87ad252d18 +https://conda.anaconda.org/conda-forge/linux-64/pandas-2.3.3-py313h08cd8bf_2.conda#8a69ea71fdd37bfe42a28f0967dbb75a https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda#a2c1eeadae7a309daed9d62c96012a2b -https://conda.anaconda.org/conda-forge/linux-64/highspy-1.12.0-np2py312h0f77346_0.conda#209aecf319ad78f8ff9426571373844d -https://conda.anaconda.org/conda-forge/noarch/tsam-2.3.9-pyhd8ed1ab_0.conda#2b661fd7a718757b2e91bbcac81deb48 +https://conda.anaconda.org/conda-forge/linux-64/highspy-1.12.0-np2py313h73dcb5b_0.conda#95abab2403527c4199e6daa94671d46a +https://conda.anaconda.org/conda-forge/noarch/tsam-2.3.1-pyhd8ed1ab_0.conda#ed5f5e0cbc50f05631813b0d48021de1 https://conda.anaconda.org/conda-forge/noarch/tenacity-9.1.2-pyhd8ed1ab_0.conda#5d99943f2ae3cc69e1ada12ce9d4d701 +https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda#12c566707c80111f9799308d9e265aef +https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py313hf46b229_1.conda#d0616e7935acab407d1543b28c446f6f +https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py313h54dd161_1.conda#710d4663806d0f72b2fb414e936223b5 https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda#0caa1af407ecff61170c9437a808404d https://conda.anaconda.org/conda-forge/noarch/tqdm-loggable-0.2-pyhd8ed1ab_0.conda#bdb8608d3b834159b1b684dc6df3ac44 -https://conda.anaconda.org/conda-forge/linux-64/wrapt-1.17.3-py312h4c3975b_1.conda#8af3faf88325836e46c6cb79828e058c +https://conda.anaconda.org/conda-forge/linux-64/wrapt-1.17.3-py313h07c4f96_1.conda#c2662497e9a9ff2153753682f53989c9 https://conda.anaconda.org/conda-forge/noarch/throttler-1.2.2-pyhd8ed1ab_0.conda#6fc48bef3b400c82abaee323a9d4e290 https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda#58335b26c38bf4a20f399384c33cbcf9 https://conda.anaconda.org/conda-forge/noarch/configargparse-1.7.1-pyhe01879c_0.conda#18dfeef40f049992f4b46b06e6f3b497 @@ -74,9 +77,7 @@ https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1a https://conda.anaconda.org/conda-forge/noarch/certifi-2026.1.4-pyhd8ed1ab_0.conda#eacc711330cd46939f66cd401ff9c44b https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhcf101f3_1.conda#c65df89a0b2e321045a9e01d1337b182 https://conda.anaconda.org/conda-forge/noarch/pyjwt-2.10.1-pyhd8ed1ab_0.conda#84c5c40ea7c5bbc6243556e5daed20e7 -https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda#12c566707c80111f9799308d9e265aef -https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py312h460c074_1.conda#648ee28dcd4e07a1940a17da62eccd40 -https://conda.anaconda.org/conda-forge/linux-64/cryptography-46.0.3-py312ha4b625e_1.conda#a42f7c8a15d53cdb6738ece5bd745d13 +https://conda.anaconda.org/conda-forge/linux-64/cryptography-46.0.3-py313heb322e3_1.conda#4e6278c519f2766ea707361f81b33364 https://conda.anaconda.org/conda-forge/noarch/blinker-1.9.0-pyhff2d567_0.conda#42834439227a4551b939beeeb8a4b085 https://conda.anaconda.org/conda-forge/noarch/oauthlib-3.3.1-pyhd8ed1ab_0.conda#d4f3f31ee39db3efecb96c0728d4bdbf https://conda.anaconda.org/conda-forge/noarch/requests-oauthlib-1.4.0-pyhd8ed1ab_0.conda#a55b220de8970208f583e38639cfbecc @@ -88,15 +89,15 @@ https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0. https://conda.anaconda.org/conda-forge/noarch/anyio-4.12.1-pyhcf101f3_0.conda#11a2b8c732d215d977998ccd69a9d5e8 https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda#4f14640d58e2cc0aa0819d9d8ba125bb https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda#d6989ead454181f4f9bc987d3dc4e285 -https://conda.anaconda.org/bioconda/noarch/snakemake-storage-plugin-cached-http-0.1.0-pyhdfd78af_0.conda#17232431f65ce347f972f0fd95d2e97a +https://conda.anaconda.org/bioconda/noarch/snakemake-storage-plugin-cached-http-0.2.1-pyhdfd78af_0.conda#f50f82ac51409fd0dc75a20a9f6930ea https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda#a77f85f77be52ff59391544bfe73390a -https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py312h8a5da7c_0.conda#fba10c2007c8b06f77c5a23ce3a635ad +https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py313h3dea7bd_0.conda#4794ea0adaebd9f844414e594b142cb2 https://conda.anaconda.org/conda-forge/noarch/dpath-2.2.0-pyha770c72_1.conda#7b2af124684a994217e62c641bca2e48 https://conda.anaconda.org/conda-forge/noarch/yte-1.9.4-pyhd8ed1ab_0.conda#89d5edf5d52d3bc1ed4d7d3feef508ba https://conda.anaconda.org/conda-forge/noarch/tabulate-0.9.0-pyhcf101f3_3.conda#de98449f11d48d4b52eefb354e2bfe35 https://conda.anaconda.org/bioconda/noarch/snakemake-interface-scheduler-plugins-2.0.2-pyhd4c3c12_0.conda#1500fccf5e46c7f91d14925449ff3632 https://conda.anaconda.org/bioconda/noarch/snakemake-interface-report-plugins-1.3.0-pyhd4c3c12_0.conda#e6fd8cfb23b294da699e395dbc968d11 -https://conda.anaconda.org/bioconda/noarch/snakemake-interface-logger-plugins-2.0.0-pyhd4c3c12_0.conda#98f75f2ca3a222992e2230d7afc54bb8 +https://conda.anaconda.org/bioconda/noarch/snakemake-interface-logger-plugins-1.2.4-pyhdfd78af_0.tar.bz2#cbb15afc697a71cc9a0e9bfd75ae59cc https://conda.anaconda.org/bioconda/noarch/snakemake-interface-executor-plugins-9.3.9-pyhdfd78af_0.tar.bz2#e75b9c422bcc3c9b52679dedb84f3b71 https://conda.anaconda.org/conda-forge/noarch/smart_open-7.5.0-pyhcf101f3_0.conda#9d1659c8332e9822e347e115e6bb4d0c https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.11.0-5_h6ae95b6_openblas.conda#e487a0e38d89da76410cb92a5db39ec5 @@ -113,15 +114,15 @@ https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.1-py312h5253ce2_0.con https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda#019a7385be9af33791c989871317e1ed https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.21.2-pyhe01879c_0.conda#23029aae904a2ba587daba708208012f https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.9.1-pyhc90fa1f_0.conda#b38fe4e78ee75def7e599843ef4c1ab0 -https://conda.anaconda.org/conda-forge/linux-64/rpds-py-0.30.0-py312h868fb18_0.conda#3ffc5a3572db8751c2f15bacf6a0e937 +https://conda.anaconda.org/conda-forge/linux-64/rpds-py-0.30.0-py313h843e2db_0.conda#779e3307a0299518713765b83a36f4b1 https://conda.anaconda.org/conda-forge/noarch/attrs-25.4.0-pyhcf101f3_1.conda#537296d57ea995666c68c821b00e360b https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda#870293df500ca7e18bedefa5838a22ab https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda#439cd0f567d697b20a8f45cb70a1005a https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_0.conda#ada41c863af263cc4c5fcbaff7c3e4dc https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_1.conda#bbe1963f1e47f594070ffe87cdf612ea -https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py312h8a5da7c_0.conda#f775a43412f7f3d7ed218113ad233869 +https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py313h3dea7bd_0.conda#c14389156310b8ed3520d84f854be1ee https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda#04558c96691bed63104678757beb4f8d -https://conda.anaconda.org/conda-forge/linux-64/immutables-0.21-py312h4c3975b_2.conda#4cf92a9dd8712cdde044fb56be498bd4 +https://conda.anaconda.org/conda-forge/linux-64/immutables-0.21-py313h07c4f96_2.conda#68ad0cf3b5c557b70e06e901f7dd3d6a https://conda.anaconda.org/conda-forge/noarch/humanfriendly-10.0-pyh707e725_8.conda#7fe569c10905402ed47024fc481bb371 https://conda.anaconda.org/conda-forge/noarch/smmap-5.0.2-pyhd8ed1ab_0.conda#87f47a78808baf2fa1ea9c315a1e48f1 https://conda.anaconda.org/conda-forge/noarch/gitdb-4.0.12-pyhd8ed1ab_0.conda#7c14f3706e099f8fcd47af2d494616cc @@ -143,6 +144,7 @@ https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h0aef613_1.conda#9344155d33912347b37f0ae6c410a835 https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.1-h9d88235_1.conda#cd5a90476766d53e901500df9215e927 https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda#eecce068c7e4eddeb169591baac20ac4 +https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_16.conda#5a68259fac2da8f2ee6f7bfe49c9eb8b https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda#172bf1cd1ff8629f2b1179945ed45055 https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda#920bb03579f15389b9e512095ad995b7 https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.67.0-had1ee68_0.conda#b499ce4b026493a13774bcf0f4c33849 @@ -189,16 +191,17 @@ https://conda.anaconda.org/conda-forge/linux-64/rasterio-1.5.0-py312hcedc861_0.c https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.7.2-py312h9b6a7d9_2.conda#573b9a879a3a42990f9c51d7376dce6b https://conda.anaconda.org/conda-forge/noarch/rioxarray-0.20.0-pyhd8ed1ab_1.conda#e7e37bf890147fa5d7892812a6dd3888 https://conda.anaconda.org/conda-forge/noarch/pyxlsb-1.0.10-pyhd8ed1ab_0.tar.bz2#0c14e44bc93a99cdc11398311c3c0dcf +https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.2.1-pyhcf101f3_0.conda#083725d6cd3dc007f06d04bcf1e613a2 https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.12.1-default_hafda6a7_1003.conda#4fe840c6d6b3719b4231ed89d389bb17 https://conda.anaconda.org/conda-forge/linux-64/tbb-2022.3.0-h8d10470_1.conda#e3259be3341da4bc06c5b7a78c8bf1bd https://conda.anaconda.org/conda-forge/linux-64/gmp-6.3.0-hac33072_2.conda#c94a5994ef49749880a8139cf9afcbe1 https://conda.anaconda.org/conda-forge/linux-64/mpfr-4.2.1-h90cbb55_3.conda#2eeb50cab6652538eee8fc0bc3340c81 https://conda.anaconda.org/conda-forge/linux-64/mumps-include-5.7.3-h82cca05_10.conda#d6c7d8811686ed912ed4317831dd8c44 https://conda.anaconda.org/conda-forge/linux-64/metis-5.1.0-hd0bcaf9_1007.conda#28eb714416de4eb83e2cbc47e99a1b45 -https://conda.anaconda.org/conda-forge/linux-64/xz-tools-5.8.1-hb9d3cd8_2.conda#1bad2995c8f1c8075c6c331bf96e46fb -https://conda.anaconda.org/conda-forge/linux-64/xz-gpl-tools-5.8.1-hbcc6ac9_2.conda#bf627c16aa26231720af037a2709ab09 -https://conda.anaconda.org/conda-forge/linux-64/liblzma-devel-5.8.1-hb9d3cd8_2.conda#f61edadbb301530bd65a32646bd81552 -https://conda.anaconda.org/conda-forge/linux-64/xz-5.8.1-hbcc6ac9_2.conda#68eae977d7d1196d32b636a026dc015d +https://conda.anaconda.org/conda-forge/linux-64/xz-tools-5.8.2-hb03c661_0.conda#dfd6129671f782988d665354e7aa269d +https://conda.anaconda.org/conda-forge/linux-64/xz-gpl-tools-5.8.2-ha02ee65_0.conda#a159fe1e8200dd67fa88ddea9169d25a +https://conda.anaconda.org/conda-forge/linux-64/liblzma-devel-5.8.2-hb03c661_0.conda#de60549ba9d8921dff3afa4b179e2a4b +https://conda.anaconda.org/conda-forge/linux-64/xz-5.8.2-ha02ee65_0.conda#d34b831f6d6a9b014eb7cf65f6329bba https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-15.2.0-h69a702a_16.conda#e5eb2ddedabd0063e442f230755d2062 https://conda.anaconda.org/conda-forge/linux-64/libscotch-7.0.4-h2fe6a88_5.conda#dd1e1c54432494476d66c679014c675c https://conda.anaconda.org/conda-forge/linux-64/mumps-seq-5.7.3-h27a6a8b_0.conda#d524b41c7757ea147337039fa4158fbb @@ -207,11 +210,11 @@ https://conda.anaconda.org/conda-forge/linux-64/ampl-asl-1.0.0-h5888daf_2.conda# https://conda.anaconda.org/conda-forge/linux-64/ipopt-3.14.19-h0804adb_0.conda#0a2ef4d1be0625a06d74b08829d4ef1f https://conda.anaconda.org/conda-forge/linux-64/cppad-20250000.2-h5888daf_0.conda#dc7dcf7e7f81c82a6254a25b9600fe78 https://conda.anaconda.org/conda-forge/linux-64/scip-9.2.4-hd8b5c82_2.conda#b9d1dc838aee1ded7b34cbc65e6a260c -https://conda.anaconda.org/conda-forge/linux-64/pyscipopt-5.6.0-py312h1289d80_1.conda#35befeaba0fb8867f562d570252f92f0 +https://conda.anaconda.org/conda-forge/linux-64/pyscipopt-5.6.0-py313h7033f15_1.conda#d2e8b402c691b8dd1233b1f7fb53c9ad https://conda.anaconda.org/conda-forge/noarch/validators-0.35.0-pyhd8ed1ab_0.conda#3449ef730c7d483adde81993994092b9 -https://conda.anaconda.org/conda-forge/linux-64/shapely-2.1.2-py312h383787d_2.conda#69e400d3deca12ee7afd4b73a5596905 +https://conda.anaconda.org/conda-forge/linux-64/shapely-2.1.2-py313had47c43_2.conda#6e550dd748e9ac9b2925411684e076a1 https://conda.anaconda.org/conda-forge/noarch/patsy-1.0.2-pyhcf101f3_0.conda#8678577a52161cc4e1c93fcc18e8a646 -https://conda.anaconda.org/conda-forge/linux-64/statsmodels-0.14.6-py312h4f23490_0.conda#423b8676bd6eed60e97097b33f13ea3f +https://conda.anaconda.org/conda-forge/linux-64/statsmodels-0.14.6-py313h29aa505_0.conda#13a3e9edeef521461cb8d47fa855e353 https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda#353823361b1d27eb3960efb076dfcaf6 https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.2-hceb46e0_1.conda#40feea2979654ed579f1cda7c63ccb94 https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda#11b3379b191f63139e29c0d19dee24cd @@ -225,22 +228,21 @@ https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.18-h0c24ade_0.conda#6f2e https://conda.anaconda.org/conda-forge/linux-64/pillow-12.1.0-py312h50c33e8_0.conda#923b06ad75b7acc888fa20a22dc397cd https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.9-py312h0a2e395_2.conda#3a3004fddd39e3bb1a631b08d7045156 https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.1-ha770c72_0.conda#4afc585cd97ba8a23809406cd8a9eda8 -https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.0-py312h4c3975b_1.conda#a0b8efbe73c90f810a171a6c746be087 https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda#37293a85a0f4f77bbd9cf7aaefc62609 https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.2.0-hb03c661_1.conda#af39b9a8711d4a8d437b52c1d78eb6a1 https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-hed03a55_1.conda#8ccf913aaba749a5496c17629d859ed1 -https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.61.1-py312h8a5da7c_0.conda#3bf8fb959dc598c67dac0430b4aff57a +https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.61.1-py313h3dea7bd_0.conda#c0f36dfbb130da4f6ce2df31f6b25ea8 https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda#4c2a8fef270f6c69591889b93f9f55c1 -https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py312hd9148b4_3.conda#86cf7a7d861b79d38e3f0e5097e4965b -https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.8-py312he3d6523_0.conda#b8dc157bbbb69c1407478feede8b7b42 +https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py313hc8edb43_4.conda#33639459bc29437315d4bff9ed5bc7a7 +https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.8-py313h683a580_0.conda#ffe67570e1a9192d2f4c189b27f75f89 https://conda.anaconda.org/conda-forge/noarch/seaborn-base-0.13.2-pyhd8ed1ab_3.conda#fd96da444e81f9e6fcaac38590f3dd42 https://conda.anaconda.org/conda-forge/noarch/seaborn-0.13.2-hd8ed1ab_3.conda#62afb877ca2c2b4b6f9ecb37320085b6 https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda#edd329d7d3a4ab45dcf905899a7a6115 https://conda.anaconda.org/conda-forge/noarch/py-cpuinfo-9.0.0-pyhd8ed1ab_1.conda#46830ee16925d5ed250850503b5dc3a8 https://conda.anaconda.org/conda-forge/noarch/nomkl-1.0-h5ca1d4c_0.tar.bz2#9a66894dfd07c4510beb6b3f9672ccc0 -https://conda.anaconda.org/conda-forge/linux-64/numexpr-2.14.1-py312h88efc94_101.conda#f31fa7178c477ce82dfb47273582de15 -https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.4-h3f801dc_0.conda#01ba04e414e47f95c03d6ddd81fd37be -https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.6-nompi_h1b119a7_104.conda#0857f4d157820dcd5625f61fdfefb780 +https://conda.anaconda.org/conda-forge/linux-64/numexpr-2.14.1-py313h24ae7f9_101.conda#b7e46fb2704458afc67fb95773528967 +https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.5-h088129d_0.conda#86f7414544ae606282352fa1e116b41f +https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.6-nompi_h1b119a7_105.conda#d58cd79121dd51128f2a5dab44edf1ea https://conda.anaconda.org/conda-forge/linux-64/c-blosc2-2.22.0-hc31b594_1.conda#52019609422a72ec80c32bbc16a889d8 https://conda.anaconda.org/conda-forge/linux-64/pytables-3.10.2-py312hefc0c3f_10.conda#e5bb2b09278f18b76ace60e809d8057c https://conda.anaconda.org/conda-forge/noarch/pydeck-0.9.1-pyhd8ed1ab_0.conda#4b13d1d2d5cba37be9fa3c0922bbf995 @@ -259,8 +261,8 @@ https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_ https://conda.anaconda.org/conda-forge/linux-64/polars-runtime-32-1.37.1-py310hffdcd12_0.conda#732a536c6ce768f096f5340121e10cc5 https://conda.anaconda.org/conda-forge/noarch/polars-1.37.1-pyh6a1acc5_0.conda#1894d4373da653406c91e20ef89f05c8 https://conda.anaconda.org/conda-forge/linux-64/libabseil-20250512.1-cxx17_hba17884_0.conda#83b160d4da3e1e847bf044997621ed63 -https://conda.anaconda.org/conda-forge/linux-64/protobuf-6.31.1-py312hb8af0ac_2.conda#2aaf8d6c729beb30d1b41964e7fb2cd6 -https://conda.anaconda.org/conda-forge/noarch/legacy-cgi-2.6.4-pyhcf101f3_0.conda#b22fe9a3d53bc833659823e48f879db9 +https://conda.anaconda.org/conda-forge/linux-64/protobuf-6.31.1-py313h8c92afc_2.conda#afed78c744cc34474140bb4415937fde +https://conda.anaconda.org/conda-forge/noarch/legacy-cgi-2.6.4-pyh742d864_0.conda#9d2a54ab80a5cc8138b8adb1e146ac20 https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2#c965a5aa0d5c1c37ffc62dff36e28400 https://conda.anaconda.org/conda-forge/linux-64/google-crc32c-1.8.0-py312h03f33d3_0.conda#78cba474481131a39da50cd3f1ce4dac https://conda.anaconda.org/conda-forge/noarch/google-resumable-media-2.8.0-pyhd8ed1ab_0.conda#ba7f04ba62be69f9c9fef0c4487c210b @@ -268,10 +270,10 @@ https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h7b12aa8_0. https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h5301d42_0.conda#0227d04521bc3d28c7995c7e1f99a721 https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-6.31.1-h49aed37_4.conda#07479fc04ba3ddd5d9f760ef1635cfa7 https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.73.1-h3288cfb_1.conda#ff63bb12ac31c176ff257e3289f20770 -https://conda.anaconda.org/conda-forge/linux-64/grpcio-1.73.1-py312h6f3464c_1.conda#dca50c100d8d67882ada32756810372f +https://conda.anaconda.org/conda-forge/linux-64/grpcio-1.73.1-py313h2b3948d_1.conda#1180380822ba420e7d953bb6f1e3666d https://conda.anaconda.org/conda-forge/noarch/googleapis-common-protos-1.72.0-pyhd8ed1ab_0.conda#003094932fb90de018f77a273b8a509b https://conda.anaconda.org/conda-forge/noarch/grpcio-status-1.73.1-pyhd8ed1ab_0.conda#5a2944f868149ad5a2e6588be8eed838 -https://conda.anaconda.org/conda-forge/noarch/pyasn1-0.6.1-pyhd8ed1ab_2.conda#09bb17ed307ad6ab2fd78d32372fdd4e +https://conda.anaconda.org/conda-forge/noarch/pyasn1-0.6.2-pyhd8ed1ab_0.conda#c203d401759f448f9e792974e055bcdc https://conda.anaconda.org/conda-forge/noarch/rsa-4.9.1-pyhd8ed1ab_0.conda#58958bb50f986ac0c46f73b6e290d5fe https://conda.anaconda.org/conda-forge/noarch/pyu2f-0.1.5-pyhd8ed1ab_1.conda#644bd4ca9f68ef536b902685d773d697 https://conda.anaconda.org/conda-forge/noarch/pyopenssl-25.3.0-pyhd8ed1ab_0.conda#ddf01a1d87103a152f725c7aeabffa29 @@ -327,19 +329,19 @@ https://conda.anaconda.org/conda-forge/noarch/xlrd-2.0.2-pyhd8ed1ab_0.conda#91f5 https://conda.anaconda.org/conda-forge/noarch/unidecode-1.4.0-pyhcf101f3_1.conda#53cb4b14ab0841e104e2bd11ee64b840 https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223 https://conda.anaconda.org/conda-forge/noarch/et_xmlfile-2.0.0-pyhd8ed1ab_1.conda#71bf9646cbfabf3022c8da4b6b4da737 -https://conda.anaconda.org/conda-forge/linux-64/openpyxl-3.1.5-py312h7f6eeab_2.conda#868d486c51b475998e3b5ea814591ccc +https://conda.anaconda.org/conda-forge/linux-64/openpyxl-3.1.5-py313ha4be090_3.conda#993d27015ca7aa1de3f4a471a9b5309e https://conda.anaconda.org/conda-forge/noarch/geographiclib-2.1-pyhd8ed1ab_0.conda#43dd16b113cc7b244d923b630026ff4f https://conda.anaconda.org/conda-forge/noarch/geopy-2.4.1-pyhd8ed1ab_2.conda#40182a8d62a61d147ec7d3e4c5c36ac2 https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8.1-pyhd8ed1ab_0.conda#7de28c27fe620a4f7dbfaea137c6232b https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.14.3-pyha770c72_0.conda#5267bef8efea4127aacd1f4e1f149b6e -https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.7.8-pyhd8ed1ab_0.conda#3181cf53cd50513a1a7c00aae2f08e7a +https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.7.9-pyhd8ed1ab_0.conda#6e2b8dfbfd1b4e91f49fd13218db08d7 https://conda.anaconda.org/conda-forge/noarch/country_converter-1.3.2-pyhd8ed1ab_0.conda#193a9e54636d8d70781a3e56370f5502 https://conda.anaconda.org/conda-forge/noarch/powerplantmatching-0.8.0-pyhd8ed1ab_0.conda#3c806a133fb9e59dca249c5a00c2ab3e https://conda.anaconda.org/conda-forge/noarch/memory_profiler-0.61.0-pyhcf101f3_1.conda#e1bccffd88819e75729412799824e270 https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.3-py312h4c3975b_0.conda#e03a4bf52d2170d64c816b2a52972097 https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.12-h4f16b4b_0.conda#db038ce880f100acc74dba10302b5630 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.6-hb9d3cd8_0.conda#febbab7d15033c913d53c7a2c102309d -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.6-hb9d3cd8_0.conda#5efa5fa6243a622445fdfd72aee15efa +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.7-hb03c661_0.conda#665d152b9c6e78da404086088077c844 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-hb03c661_0.conda#ba231da7fccf9ea1e768caf5c7099b84 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.2-hb9d3cd8_0.conda#17dcc85db3c7886650b8908b183d6876 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda#7bbe9a0cc0df0ac5f5a8ad6d6a11af2f @@ -360,6 +362,7 @@ https://conda.anaconda.org/conda-forge/linux-64/wayland-1.24.0-hd6090a7_1.conda# https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.46-hb03c661_0.conda#71ae752a748962161b4740eaff510258 https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.1-hca5e8e5_0.conda#2bca1fbb221d9c3c8e3a155784bbc2e9 https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.328.1-h5279c79_0.conda#372a62464d47d9e966b630ffae3abe73 +https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda#5aa797f8787fe7a17d1b0821485b5adc https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.8-hb9d3cd8_0.conda#7c7927b404672409d9917d49bff5f2d6 https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hd9c7081_0.conda#cae723309a49399d2949362f4ab5c9e4 https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-he970967_0.conda#2e5bf4f1da39c0b32778561c3c4e5878 @@ -420,7 +423,7 @@ https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.5.1-pyhcf101f3_0.conda# https://conda.anaconda.org/conda-forge/noarch/bleach-6.3.0-pyhcf101f3_0.conda#b1a27250d70881943cca0dd6b4ba0956 https://conda.anaconda.org/conda-forge/noarch/bleach-with-css-6.3.0-h5f6438b_0.conda#08a03378bc5293c6f97637323802f480 https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.6-pyhcf101f3_1.conda#cfc86ccc3b1de35d36ccaae4c50391f5 -https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_1.conda#2d983ff1b82a1ccb6f2e9d8784bdd6bd +https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.4-pyhcf101f3_0.conda#7b8bace4943e0dc345fc45938826f2b8 https://conda.anaconda.org/conda-forge/noarch/rfc3986-validator-0.1.1-pyh9f0ad1d_0.tar.bz2#912a71cc01012ee38e6b90ddd561e36f https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_1.conda#36de09a8d3e5d5e6f4ee63af49e59706 https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda#a61bf9ec79426938ff785eb69dbb1960 @@ -436,7 +439,7 @@ https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.t https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_1.conda#d3549fd50d450b6d9e7dddff25dd2110 https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.26.0-hcf101f3_0.conda#8368d58342d0825f0843dc6acdd0c483 https://conda.anaconda.org/conda-forge/noarch/jupyter_events-0.12.0-pyh29332c3_0.conda#f56000b36f09ab7533877e695e4e8cb0 -https://conda.anaconda.org/conda-forge/linux-64/argon2-cffi-bindings-25.1.0-py312h4c3975b_2.conda#1567f06d717246abab170736af8bad1b +https://conda.anaconda.org/conda-forge/linux-64/argon2-cffi-bindings-25.1.0-py313h07c4f96_2.conda#27bbec9f2f3a15d32b60ec5734f5b41c https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-25.1.0-pyhd8ed1ab_0.conda#8ac12aff0860280ee0cff7fa2cf63f3b https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.17.0-pyhcf101f3_0.conda#d79a87dcfa726bcea8e61275feed6f83 https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda#e7f89ea5f7ea9401642758ff50a2d9c1 @@ -472,15 +475,15 @@ https://conda.anaconda.org/conda-forge/noarch/jupyterlab_widgets-1.1.11-pyhd8ed1 https://conda.anaconda.org/conda-forge/noarch/ipython_genutils-0.2.0-pyhd8ed1ab_2.conda#2f0ba4bc12af346bc6c99bdc377e8944 https://conda.anaconda.org/conda-forge/noarch/ipywidgets-7.8.5-pyhd8ed1ab_0.conda#47672c493015ab57d5fcde9531ab18ef https://conda.anaconda.org/conda-forge/noarch/jupyter-1.1.1-pyhd8ed1ab_1.conda#9453512288d20847de4356327d0e1282 -https://conda.anaconda.org/conda-forge/linux-64/jpype1-1.6.0-py312hd9148b4_1.conda#e49867483039df96221d655dc0347728 -https://conda.anaconda.org/gurobi/linux-64/gurobi-13.0.0-py312_0.conda#ef0ccf2535c1ad7699b19ec5831b7c4e +https://conda.anaconda.org/conda-forge/linux-64/jpype1-1.6.0-py313h7037e92_1.conda#ac457be3d18517c036312536cf825e26 +https://conda.anaconda.org/gurobi/linux-64/gurobi-13.0.1-py313_0.conda#f0578fa832a660f5b01ec07f87fadab1 https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-hb03c661_0.conda#f9f81ea472684d75b9dd8d0b328cf655 https://conda.anaconda.org/conda-forge/linux-64/pango-1.56.4-hadf4263_0.conda#79f71230c069a287efe3a8614069ddf1 https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.44.4-h2b0a6b4_0.conda#c379d67c686fb83475c1a6ed41cc41ff https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.60.0-h61e6d4b_0.conda#91e6d4d684e237fba31b9815c4b40edf https://conda.anaconda.org/conda-forge/linux-64/libgd-2.3.3-h5fbf134_12.conda#88c1c66987cd52a712eea89c27104be6 https://conda.anaconda.org/conda-forge/linux-64/gts-0.7.6-h977cf35_4.conda#4d8df0b0db060d33c9a702ada998a8fe -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxinerama-1.1.5-h5888daf_1.conda#5e2eb9bf77394fc2e5918beefec9f9ab +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxinerama-1.1.6-hecca717_0.conda#93f5d4b5c17c8540479ad65f206fea51 https://conda.anaconda.org/conda-forge/linux-64/hicolor-icon-theme-0.17-ha770c72_2.tar.bz2#bbf6f174dcd3254e19a2f5d2295ce808 https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.86.3-hf516916_0.conda#fd6acbf37b40cbe919450fa58309fbe1 https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-hb03c661_0.conda#aa8d21be4b461ce612d8f5fb791decae @@ -493,14 +496,14 @@ https://conda.anaconda.org/conda-forge/linux-64/at-spi2-core-2.40.3-h0630a04_0.t https://conda.anaconda.org/conda-forge/linux-64/at-spi2-atk-2.38.0-h0630a04_3.tar.bz2#6b889f174df1e0f816276ae69281af4d https://conda.anaconda.org/conda-forge/linux-64/gtk3-3.24.43-h993cebd_6.conda#f9f33c65b20e6a61f21714785e3613ec https://conda.anaconda.org/conda-forge/noarch/adwaita-icon-theme-49.0-unix_0.conda#b3f0179590f3c0637b7eb5309898f79e -https://conda.anaconda.org/conda-forge/linux-64/graphviz-14.1.0-h8b86629_0.conda#39dcf8bb370df27fd81dbe41d4cb605e +https://conda.anaconda.org/conda-forge/linux-64/graphviz-14.1.2-h8b86629_0.conda#341fc61cfe8efa5c72d24db56c776f44 https://conda.anaconda.org/conda-forge/linux-64/glpk-5.0-h445213a_0.tar.bz2#efc4b0c33bdf47312ad5a8a0587fa653 https://conda.anaconda.org/conda-forge/noarch/geojson-3.2.0-pyhd8ed1ab_0.conda#9f9840fb1c2e009fb0009a2f9461e64a https://conda.anaconda.org/conda-forge/linux-64/fiona-1.10.1-py312h053e1f3_6.conda#a68cae58a81a937a6edcb3e4e6f0bbe7 https://conda.anaconda.org/conda-forge/noarch/descartes-1.1.0-pyhd8ed1ab_5.conda#4a25cae637029c5589135903aa15b3b6 -https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.1.2-py312hd9148b4_1.conda#2e489969e38f0b428c39492619b5e6e5 -https://conda.anaconda.org/conda-forge/noarch/deprecated-1.3.1-pyhd8ed1ab_0.conda#bf74a83f7a0f2a21b5d709997402cac4 -https://conda.anaconda.org/conda-forge/linux-64/numcodecs-0.16.5-py312hf79963d_0.conda#86a969eeb489119374ec1d2e863777e6 +https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.1.2-py313h7037e92_1.conda#cd1cfde0ea3bca6c805c73ffa988b12a +https://conda.anaconda.org/conda-forge/noarch/deprecated-1.3.1-pyhd8ed1ab_1.conda#5498feb783ab29db6ca8845f68fa0f03 +https://conda.anaconda.org/conda-forge/linux-64/numcodecs-0.16.5-py313h08cd8bf_0.conda#0f394ef25fb81d1dec8ff4fa716f00bd https://conda.anaconda.org/conda-forge/noarch/donfig-0.8.1.post1-pyhd8ed1ab_1.conda#c56a7fa5597ad78b62e1f5d21f7f8b8f https://conda.anaconda.org/conda-forge/noarch/zarr-3.1.5-pyhcf101f3_0.conda#c1844a94b2be61bb03bbb71574a0abfc https://conda.anaconda.org/conda-forge/noarch/semver-3.0.4-pyhcf101f3_1.conda#8e7be844ccb9706a999a337e056606ab @@ -526,9 +529,9 @@ https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.39.0-h https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-h5888daf_1005.conda#d411fc29e338efb48c5fd4576d71d881 https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hbabe93e_0.conda#ff862eebdfeb2fd048ae9dc92510baca https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.16.1-h3a458e0_0.conda#1d4e0d37da5f3c22ecd44033f673feba -https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.11.0-h3d7a050_1.conda#89985ba2a3742f34be6aafd6a8f3af8c -https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.15.0-h2a74896_1.conda#ffd553ff98ce5d74d3d89ac269153149 -https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.13.0-hf38f1be_1.conda#f10b9303c7239fbce3580a60a92bcf97 +https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.12.0-h3d7a050_0.conda#e6f12de3a9b016cea81a87db04d85ff3 +https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.16.0-h75daedc_0.conda#e88f8e816ae46c12cbe912c8f4d9d3bc +https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.14.0-hd454692_0.conda#55986e49b7aafe9aa09d7f4c70a56a18 https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.13.2-h3a5f585_1.conda#4e921d9c85e6559c60215497978b3cdb https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.12.6-hb03c661_0.conda#e36ad70a7e0b48f091ed6902f04c23b8 https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.4-h8b1a151_4.conda#c7e3e08b7b1b285524ab9d74162ce40b @@ -544,41 +547,41 @@ https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.13.3-hc63082f_11.co https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.5.7-h28f887f_1.conda#7b8e3f846353b75db163ad93248e5f9d https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.35.4-h8824e59_0.conda#113b9d9913280474c0868b0e290c0326 https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.606-h20b40b1_10.conda#937d1d4c233adc6eeb2ac3d6e9a73e53 -https://conda.anaconda.org/conda-forge/linux-64/libarrow-22.0.0-hb6ed5f4_6_cpu.conda#fbaa3742ccca0f7096216c0832137b72 -https://conda.anaconda.org/conda-forge/linux-64/libarrow-compute-22.0.0-h8c2c5c3_6_cpu.conda#d2cd924b5f451a7c258001cb1c14155d -https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-22.0.0-py312hc195796_0_cpu.conda#7fe5934d9aa025b4e5c8708718c4dafb +https://conda.anaconda.org/conda-forge/linux-64/libarrow-23.0.0-h2c50142_0_cpu.conda#ef47efe8884347ab96f0d26399e83229 +https://conda.anaconda.org/conda-forge/linux-64/libarrow-compute-23.0.0-h8c2c5c3_0_cpu.conda#fa2c484e95ba37950f926bd797c51dc4 +https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-23.0.0-py313he109ebe_0_cpu.conda#9120bf253ebbdb0015069b9a25cf4d36 https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda#a1cfcc585f0c42bf8d5546bb1dfb668d https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.22.0-h454ac66_1.conda#8ed82d90e6b1686f5e98f8b7825a15ef -https://conda.anaconda.org/conda-forge/linux-64/libparquet-22.0.0-h7376487_6_cpu.conda#83fd8f55f38ac972947c9eca12dc4657 -https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-22.0.0-h635bf11_6_cpu.conda#5a8f878ca313083960ab819a009848b3 -https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-22.0.0-h635bf11_6_cpu.conda#579bdb829ab093d048e49a289d3c9883 -https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-22.0.0-h3f74fd7_6_cpu.conda#cfc7d2c5a81eb6de3100661a69de5f3d -https://conda.anaconda.org/conda-forge/linux-64/pyarrow-22.0.0-py312h7900ff3_0.conda#f135d6fe1a8065e6a59cab7512237524 +https://conda.anaconda.org/conda-forge/linux-64/libparquet-23.0.0-h7376487_0_cpu.conda#be2161a27537cb288a5634daf768af00 +https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-23.0.0-h635bf11_0_cpu.conda#0e1d44a4759116c17c77cdead68bb2d6 +https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-23.0.0-h635bf11_0_cpu.conda#a373b33a7a1c9f57ef6273e886e91fe1 +https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-23.0.0-h3f74fd7_0_cpu.conda#618c4d7d323f9b3ec4fdb0b3a5e5df1d +https://conda.anaconda.org/conda-forge/linux-64/pyarrow-23.0.0-py313h78bf25f_0.conda#a6e89cb214f318db9548b791ba27f862 https://conda.anaconda.org/conda-forge/noarch/arcosparse-0.4.2-pyhd8ed1ab_0.conda#9a005ba5f540619a1343587b4ee3d95e -https://conda.anaconda.org/conda-forge/noarch/copernicusmarine-2.2.5-pyhd8ed1ab_0.conda#e6f85f3cd0c5aff4ef0e07e80f49fa39 +https://conda.anaconda.org/conda-forge/noarch/copernicusmarine-2.3.0-pyhd8ed1ab_0.conda#9e18b048c69d2d72bc69d120a435d731 https://conda.anaconda.org/conda-forge/noarch/pyshp-3.0.3-pyhd8ed1ab_0.conda#c138c7aaa6a10b5762dcd92247864aff -https://conda.anaconda.org/conda-forge/linux-64/cartopy-0.25.0-py312hf79963d_1.conda#6c913a686cb4060cbd7639a36fa144f0 +https://conda.anaconda.org/conda-forge/linux-64/cartopy-0.25.0-py313h08cd8bf_1.conda#a0d8dc5c90850d9f1a79f69c98aef0ff https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda#d7585b6550ad04c8c5e21097ada2888e https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda#9614359868482abba1bd15ce465e3c42 https://conda.anaconda.org/conda-forge/noarch/pytest-9.0.2-pyhcf101f3_0.conda#2b694bad8a50dc2f712f5368de866480 https://conda.anaconda.org/conda-forge/noarch/python-utils-3.9.1-pyhff2d567_1.conda#24ed1dc544b101075fa7462be5c3a5c5 https://conda.anaconda.org/conda-forge/noarch/progressbar2-4.5.0-pyhd8ed1ab_1.conda#e557abf678a0bf100fe7cf9d2b4f4a72 -https://conda.anaconda.org/conda-forge/linux-64/lz4-4.4.5-py312h3d67a73_1.conda#a669145a2c834895bdf3fcba1f1e5b9c +https://conda.anaconda.org/conda-forge/linux-64/lz4-4.4.5-py313h28739b2_1.conda#e69ad33075938ba81e43311da86b809c https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda#e52c2ef711ccf31bb7f70ca87d144b9e https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda#f88bb644823094f436792f80fba3207e https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda#0401a17ae845fa72c7210e206ec5647d -https://conda.anaconda.org/conda-forge/linux-64/cytoolz-1.1.0-py312h4c3975b_1.conda#693cda60b9223f55d0836c885621611b -https://conda.anaconda.org/conda-forge/noarch/distributed-2025.12.0-pyhcf101f3_1.conda#613cea9275c4773d0b53c879838ac0ad -https://conda.anaconda.org/conda-forge/noarch/bokeh-3.8.1-pyhd8ed1ab_0.conda#f301f72474b91f1f83d42bcc7d81ce09 -https://conda.anaconda.org/conda-forge/noarch/dask-2025.12.0-pyhcf101f3_0.conda#94d36804598479f9eafa9c973902280e +https://conda.anaconda.org/conda-forge/linux-64/cytoolz-1.1.0-py313h07c4f96_1.conda#bcca9afd203fe05d9582249ac12762da +https://conda.anaconda.org/conda-forge/noarch/distributed-2026.1.1-pyhcf101f3_1.conda#c15e359a982395be86a7576a91f9c5f5 +https://conda.anaconda.org/conda-forge/noarch/bokeh-3.8.2-pyhd8ed1ab_0.conda#0b830ba4947de6d60dd9d96827a1cacb +https://conda.anaconda.org/conda-forge/noarch/dask-2026.1.1-pyhcf101f3_0.conda#a86541105aa7920d2147d48bf370dc08 https://conda.anaconda.org/conda-forge/noarch/findlibs-0.1.2-pyhd8ed1ab_0.conda#fa9e9ec7bf26619a8edd3e11155f15d6 https://conda.anaconda.org/conda-forge/linux-64/libglu-9.0.3-h5888daf_1.conda#8422fcc9e5e172c91e99aef703b3ce65 https://conda.anaconda.org/conda-forge/linux-64/freeglut-3.2.2-ha6d2627_3.conda#84ec3f5b46f3076be49f2cf3f1cfbf02 https://conda.anaconda.org/conda-forge/linux-64/jasper-4.2.8-he3c4edf_0.conda#a04073db11c2c86c555fb088acc8f8c1 -https://conda.anaconda.org/conda-forge/linux-64/eccodes-2.44.0-h83bc92c_0.conda#2d37fd4ccfd98453a02a278e4112da39 -https://conda.anaconda.org/conda-forge/linux-64/python-eccodes-2.44.0-py312h4f23490_1.conda#eea306a68c483e1305381130b35a09ff +https://conda.anaconda.org/conda-forge/linux-64/eccodes-2.45.0-h83bc92c_0.conda#a981ecc85b1ff6307b8f09cf66b77083 +https://conda.anaconda.org/conda-forge/linux-64/python-eccodes-2.45.0-np2py313hc18bace_1.conda#17ae2a605a45add6050a04962f847b21 https://conda.anaconda.org/conda-forge/noarch/cfgrib-0.9.15.1-pyhd8ed1ab_0.conda#0f12f8436a2a238e255d49ea3f8aefe2 https://conda.anaconda.org/conda-forge/noarch/multiurl-0.3.7-pyhd8ed1ab_0.conda#e585c71c2ed48e4eee1663d627ddcd47 -https://conda.anaconda.org/conda-forge/noarch/ecmwf-datastores-client-0.4.1-pyhd8ed1ab_0.conda#ea90ece1da754ca0c5d6766eb59908c2 +https://conda.anaconda.org/conda-forge/noarch/ecmwf-datastores-client-0.4.2-pyhd8ed1ab_0.conda#d957f10f516dcdeb9e382c91d771df12 https://conda.anaconda.org/conda-forge/noarch/cdsapi-0.7.7-pyhd8ed1ab_0.conda#1f878573c1ee2798c052bee1f5a94f50 https://conda.anaconda.org/conda-forge/noarch/atlite-0.4.1-pyhd8ed1ab_1.conda#81f981df273cd627927372680aa9dd31 diff --git a/envs/default_osx-64.pin.txt b/envs/default_osx-64.pin.txt index ce2d9017b..295faf065 100644 --- a/envs/default_osx-64.pin.txt +++ b/envs/default_osx-64.pin.txt @@ -45,6 +45,10 @@ https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda# https://conda.anaconda.org/conda-forge/osx-64/highspy-1.12.0-np2py313h77a8fbf_0.conda#ece793b4a6623b379969ac2277b7824f https://conda.anaconda.org/conda-forge/noarch/tsam-2.3.1-pyhd8ed1ab_0.conda#ed5f5e0cbc50f05631813b0d48021de1 https://conda.anaconda.org/conda-forge/noarch/tenacity-9.1.2-pyhd8ed1ab_0.conda#5d99943f2ae3cc69e1ada12ce9d4d701 +https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h3eecb57_6.conda#727109b184d680772e3122f40136d5ca +https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda#12c566707c80111f9799308d9e265aef +https://conda.anaconda.org/conda-forge/osx-64/cffi-2.0.0-py313hf57695f_1.conda#b10f64f2e725afc9bf2d9b30eff6d0ea +https://conda.anaconda.org/conda-forge/osx-64/zstandard-0.25.0-py313hcb05632_1.conda#da657125cfc67fe18e4499cf88dbe512 https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda#0caa1af407ecff61170c9437a808404d https://conda.anaconda.org/conda-forge/noarch/tqdm-loggable-0.2-pyhd8ed1ab_0.conda#bdb8608d3b834159b1b684dc6df3ac44 https://conda.anaconda.org/conda-forge/osx-64/wrapt-1.17.3-py313h585f44e_1.conda#765dc9b39fc2d62e1351c3a26e316607 @@ -82,7 +86,7 @@ https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0. https://conda.anaconda.org/conda-forge/noarch/anyio-4.12.1-pyhcf101f3_0.conda#11a2b8c732d215d977998ccd69a9d5e8 https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda#4f14640d58e2cc0aa0819d9d8ba125bb https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda#d6989ead454181f4f9bc987d3dc4e285 -https://conda.anaconda.org/bioconda/noarch/snakemake-storage-plugin-cached-http-0.1.0-pyhdfd78af_0.conda#17232431f65ce347f972f0fd95d2e97a +https://conda.anaconda.org/bioconda/noarch/snakemake-storage-plugin-cached-http-0.2.1-pyhdfd78af_0.conda#f50f82ac51409fd0dc75a20a9f6930ea https://conda.anaconda.org/conda-forge/osx-64/yaml-0.2.5-h4132b18_3.conda#a645bb90997d3fc2aea0adf6517059bd https://conda.anaconda.org/conda-forge/osx-64/pyyaml-6.0.3-py313h0f4d31d_0.conda#e0c9e257970870212c449106964a5ace https://conda.anaconda.org/conda-forge/noarch/dpath-2.2.0-pyha770c72_1.conda#7b2af124684a994217e62c641bca2e48 @@ -90,7 +94,7 @@ https://conda.anaconda.org/conda-forge/noarch/yte-1.9.4-pyhd8ed1ab_0.conda#89d5e https://conda.anaconda.org/conda-forge/noarch/tabulate-0.9.0-pyhcf101f3_3.conda#de98449f11d48d4b52eefb354e2bfe35 https://conda.anaconda.org/bioconda/noarch/snakemake-interface-scheduler-plugins-2.0.2-pyhd4c3c12_0.conda#1500fccf5e46c7f91d14925449ff3632 https://conda.anaconda.org/bioconda/noarch/snakemake-interface-report-plugins-1.3.0-pyhd4c3c12_0.conda#e6fd8cfb23b294da699e395dbc968d11 -https://conda.anaconda.org/bioconda/noarch/snakemake-interface-logger-plugins-2.0.0-pyhd4c3c12_0.conda#98f75f2ca3a222992e2230d7afc54bb8 +https://conda.anaconda.org/bioconda/noarch/snakemake-interface-logger-plugins-1.2.4-pyhdfd78af_0.tar.bz2#cbb15afc697a71cc9a0e9bfd75ae59cc https://conda.anaconda.org/bioconda/noarch/snakemake-interface-executor-plugins-9.3.9-pyhdfd78af_0.tar.bz2#e75b9c422bcc3c9b52679dedb84f3b71 https://conda.anaconda.org/conda-forge/noarch/smart_open-7.5.0-pyhcf101f3_0.conda#9d1659c8332e9822e347e115e6bb4d0c https://conda.anaconda.org/conda-forge/osx-64/liblapacke-3.11.0-5_h94b3770_openblas.conda#475b9378f397064c05a8c5ed9eecedef @@ -255,7 +259,7 @@ https://conda.anaconda.org/conda-forge/osx-64/libgrpc-1.73.1-h451496d_1.conda#d6 https://conda.anaconda.org/conda-forge/osx-64/grpcio-1.73.1-py313ha22d4e2_1.conda#91520cdcae125868e76f99cfb665773c https://conda.anaconda.org/conda-forge/noarch/googleapis-common-protos-1.72.0-pyhd8ed1ab_0.conda#003094932fb90de018f77a273b8a509b https://conda.anaconda.org/conda-forge/noarch/grpcio-status-1.73.1-pyhd8ed1ab_0.conda#5a2944f868149ad5a2e6588be8eed838 -https://conda.anaconda.org/conda-forge/noarch/pyasn1-0.6.1-pyhd8ed1ab_2.conda#09bb17ed307ad6ab2fd78d32372fdd4e +https://conda.anaconda.org/conda-forge/noarch/pyasn1-0.6.2-pyhd8ed1ab_0.conda#c203d401759f448f9e792974e055bcdc https://conda.anaconda.org/conda-forge/noarch/rsa-4.9.1-pyhd8ed1ab_0.conda#58958bb50f986ac0c46f73b6e290d5fe https://conda.anaconda.org/conda-forge/noarch/pyu2f-0.1.5-pyhd8ed1ab_1.conda#644bd4ca9f68ef536b902685d773d697 https://conda.anaconda.org/conda-forge/noarch/pyopenssl-25.3.0-pyhd8ed1ab_0.conda#ddf01a1d87103a152f725c7aeabffa29 @@ -316,7 +320,7 @@ https://conda.anaconda.org/conda-forge/noarch/geographiclib-2.1-pyhd8ed1ab_0.con https://conda.anaconda.org/conda-forge/noarch/geopy-2.4.1-pyhd8ed1ab_2.conda#40182a8d62a61d147ec7d3e4c5c36ac2 https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8.1-pyhd8ed1ab_0.conda#7de28c27fe620a4f7dbfaea137c6232b https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.14.3-pyha770c72_0.conda#5267bef8efea4127aacd1f4e1f149b6e -https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.7.8-pyhd8ed1ab_0.conda#3181cf53cd50513a1a7c00aae2f08e7a +https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.7.9-pyhd8ed1ab_0.conda#6e2b8dfbfd1b4e91f49fd13218db08d7 https://conda.anaconda.org/conda-forge/noarch/country_converter-1.3.2-pyhd8ed1ab_0.conda#193a9e54636d8d70781a3e56370f5502 https://conda.anaconda.org/conda-forge/noarch/powerplantmatching-0.8.0-pyhd8ed1ab_0.conda#3c806a133fb9e59dca249c5a00c2ab3e https://conda.anaconda.org/conda-forge/noarch/memory_profiler-0.61.0-pyhcf101f3_1.conda#e1bccffd88819e75729412799824e270 @@ -351,7 +355,7 @@ https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.5.1-pyhcf101f3_0.conda# https://conda.anaconda.org/conda-forge/noarch/bleach-6.3.0-pyhcf101f3_0.conda#b1a27250d70881943cca0dd6b4ba0956 https://conda.anaconda.org/conda-forge/noarch/bleach-with-css-6.3.0-h5f6438b_0.conda#08a03378bc5293c6f97637323802f480 https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.6-pyhcf101f3_1.conda#cfc86ccc3b1de35d36ccaae4c50391f5 -https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_1.conda#2d983ff1b82a1ccb6f2e9d8784bdd6bd +https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.4-pyhcf101f3_0.conda#7b8bace4943e0dc345fc45938826f2b8 https://conda.anaconda.org/conda-forge/noarch/rfc3986-validator-0.1.1-pyh9f0ad1d_0.tar.bz2#912a71cc01012ee38e6b90ddd561e36f https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_1.conda#36de09a8d3e5d5e6f4ee63af49e59706 https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda#a61bf9ec79426938ff785eb69dbb1960 @@ -431,7 +435,7 @@ https://conda.anaconda.org/conda-forge/osx-64/epoxy-1.5.10-h8616949_2.conda#efe7 https://conda.anaconda.org/conda-forge/osx-64/atk-1.0-2.38.0-h4bec284_2.conda#d9684247c943d492d9aac8687bc5db77 https://conda.anaconda.org/conda-forge/osx-64/gtk3-3.24.43-h5e629aa_6.conda#dbd0346e44fcbda7fe4f6eaf42597ef9 https://conda.anaconda.org/conda-forge/noarch/adwaita-icon-theme-49.0-unix_0.conda#b3f0179590f3c0637b7eb5309898f79e -https://conda.anaconda.org/conda-forge/osx-64/graphviz-14.1.0-had0cc5b_0.conda#2b817259cccac25ca7190fe3a48d54d4 +https://conda.anaconda.org/conda-forge/osx-64/graphviz-14.1.2-h44fc223_0.conda#4c1c78d65d867d032c07303cf38117ba https://conda.anaconda.org/conda-forge/osx-64/glpk-5.0-h3cb5acd_0.tar.bz2#323537f09c8044f0352a8af30a6fc650 https://conda.anaconda.org/conda-forge/noarch/geojson-3.2.0-pyhd8ed1ab_0.conda#9f9840fb1c2e009fb0009a2f9461e64a https://conda.anaconda.org/conda-forge/osx-64/fiona-1.10.1-py313ha55c4c1_6.conda#b7268b3d9fcfd219f88e8db709a0e4d8 @@ -455,18 +459,18 @@ https://conda.anaconda.org/conda-forge/noarch/s3transfer-0.16.0-pyhd8ed1ab_0.con https://conda.anaconda.org/conda-forge/noarch/boto3-1.42.26-pyhd8ed1ab_0.conda#5225da63f2304a4e3a58c6f10497c0ff https://conda.anaconda.org/conda-forge/osx-64/libutf8proc-2.11.2-h7983711_0.conda#e630b1baa02a5eeb0ef351c6125865c4 https://conda.anaconda.org/conda-forge/osx-64/prometheus-cpp-1.3.0-h7802330_0.conda#f36107fa2557e63421a46676371c4226 -https://conda.anaconda.org/conda-forge/osx-64/nlohmann_json-3.12.0-h53ec75d_1.conda#5e9bee5fa11d91e1621e477c3cb9b9ba +https://conda.anaconda.org/conda-forge/osx-64/nlohmann_json-3.12.0-h06076ce_1.conda#97d7a1cda5546cb0bbdefa3777cb9897 https://conda.anaconda.org/conda-forge/osx-64/libopentelemetry-cpp-headers-1.21.0-h694c41f_1.conda#62636543478d53b28c1fc5efce346622 https://conda.anaconda.org/conda-forge/osx-64/libopentelemetry-cpp-1.21.0-h7d3f41d_1.conda#952dd64cff4a72cadf5e81572a7a81c8 -https://conda.anaconda.org/conda-forge/osx-64/orc-2.2.1-hd1b02dc_0.conda#b4646b6ddcbcb3b10e9879900c66ed48 +https://conda.anaconda.org/conda-forge/osx-64/orc-2.2.2-h3073fbf_0.conda#7323bc020618321c05afaf23f78460c0 https://conda.anaconda.org/conda-forge/osx-64/libgoogle-cloud-2.39.0-hed66dea_0.conda#06564befaabd2760dfa742e47074bad2 https://conda.anaconda.org/conda-forge/osx-64/libgoogle-cloud-storage-2.39.0-h8ac052b_0.conda#7600fb1377c8eb5a161e4a2520933daa https://conda.anaconda.org/conda-forge/osx-64/gflags-2.2.2-hac325c4_1005.conda#a26de8814083a6971f14f9c8c3cb36c2 https://conda.anaconda.org/conda-forge/osx-64/glog-0.7.1-h2790a97_0.conda#06cf91665775b0da395229cd4331b27d https://conda.anaconda.org/conda-forge/osx-64/azure-core-cpp-1.16.1-he2a98a9_0.conda#9f39c22aad61e76bfb73bb7d4114efac -https://conda.anaconda.org/conda-forge/osx-64/azure-storage-common-cpp-12.11.0-h56a711b_1.conda#278ccb9a3616d4342731130287c3ba79 -https://conda.anaconda.org/conda-forge/osx-64/azure-storage-blobs-cpp-12.15.0-h388f2e7_1.conda#6b5f36e610295f4f859dd9cf680bbf7d -https://conda.anaconda.org/conda-forge/osx-64/azure-storage-files-datalake-cpp-12.13.0-h1984e67_1.conda#ef5701f2da108d432e7872d58e8ac64e +https://conda.anaconda.org/conda-forge/osx-64/azure-storage-common-cpp-12.12.0-h2a5eb39_0.conda#53d1b2dc90315c3b8e4ecc86966ab7bd +https://conda.anaconda.org/conda-forge/osx-64/azure-storage-blobs-cpp-12.16.0-ha4e89a6_0.conda#5f76a3745c0eb7021845161c9a1bfee3 +https://conda.anaconda.org/conda-forge/osx-64/azure-storage-files-datalake-cpp-12.14.0-h7f37a48_0.conda#30ca75c03ba3166f44852b33f07f077c https://conda.anaconda.org/conda-forge/osx-64/azure-identity-cpp-1.13.2-h0e8e1c8_1.conda#32eb613f88ae1530ca78481bdce41cdd https://conda.anaconda.org/conda-forge/osx-64/aws-c-common-0.12.6-h8616949_0.conda#c7f2d588a6d50d170b343f3ae0b72e62 https://conda.anaconda.org/conda-forge/osx-64/aws-c-sdkutils-0.2.4-h901532c_4.conda#b384fb05730f549a55cdb13c484861eb @@ -492,7 +496,7 @@ https://conda.anaconda.org/conda-forge/osx-64/libarrow-dataset-22.0.0-h2db2d7d_6 https://conda.anaconda.org/conda-forge/osx-64/libarrow-substrait-22.0.0-h4653b8a_6_cpu.conda#0420b6cb0c11dfaf0dbd607cd808cf9c https://conda.anaconda.org/conda-forge/osx-64/pyarrow-22.0.0-py313habf4b1d_0.conda#f5e7a81f8f1b2073bc4c149365a8f1d4 https://conda.anaconda.org/conda-forge/noarch/arcosparse-0.4.2-pyhd8ed1ab_0.conda#9a005ba5f540619a1343587b4ee3d95e -https://conda.anaconda.org/conda-forge/noarch/copernicusmarine-2.2.5-pyhd8ed1ab_0.conda#e6f85f3cd0c5aff4ef0e07e80f49fa39 +https://conda.anaconda.org/conda-forge/noarch/copernicusmarine-2.3.0-pyhd8ed1ab_0.conda#9e18b048c69d2d72bc69d120a435d731 https://conda.anaconda.org/conda-forge/noarch/pyshp-3.0.3-pyhd8ed1ab_0.conda#c138c7aaa6a10b5762dcd92247864aff https://conda.anaconda.org/conda-forge/osx-64/cartopy-0.25.0-py313h2f264a9_1.conda#6d810702a3cccf099574172e96807159 https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda#d7585b6550ad04c8c5e21097ada2888e @@ -514,6 +518,6 @@ https://conda.anaconda.org/conda-forge/osx-64/eccodes-2.44.0-h163e534_0.conda#29 https://conda.anaconda.org/conda-forge/osx-64/python-eccodes-2.44.0-py313h0f4b8c3_1.conda#62f2e1e44e0fd85d2034de228cdf3fb3 https://conda.anaconda.org/conda-forge/noarch/cfgrib-0.9.15.1-pyhd8ed1ab_0.conda#0f12f8436a2a238e255d49ea3f8aefe2 https://conda.anaconda.org/conda-forge/noarch/multiurl-0.3.7-pyhd8ed1ab_0.conda#e585c71c2ed48e4eee1663d627ddcd47 -https://conda.anaconda.org/conda-forge/noarch/ecmwf-datastores-client-0.4.1-pyhd8ed1ab_0.conda#ea90ece1da754ca0c5d6766eb59908c2 +https://conda.anaconda.org/conda-forge/noarch/ecmwf-datastores-client-0.4.2-pyhd8ed1ab_0.conda#d957f10f516dcdeb9e382c91d771df12 https://conda.anaconda.org/conda-forge/noarch/cdsapi-0.7.7-pyhd8ed1ab_0.conda#1f878573c1ee2798c052bee1f5a94f50 https://conda.anaconda.org/conda-forge/noarch/atlite-0.4.1-pyhd8ed1ab_1.conda#81f981df273cd627927372680aa9dd31 diff --git a/envs/default_osx-arm64.pin.txt b/envs/default_osx-arm64.pin.txt index f0b622b50..220dbd4a2 100644 --- a/envs/default_osx-arm64.pin.txt +++ b/envs/default_osx-arm64.pin.txt @@ -25,7 +25,7 @@ https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.co https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_16.conda#8b216bac0de7a9d60f3ddeba2515545c https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_16.conda#265a9d03461da24884ecc8eb58396d57 https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_16.conda#11e09edf0dde4c288508501fe621bab4 -https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.30-openmp_ha158390_3.conda#a18a7f471c517062ee71b843ef95eb8a +https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.30-openmp_ha158390_4.conda#a6f6d3a31bb29e48d37ce65de54e2df0 https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-5_h51639a9_openblas.conda#bcc025e2bbaf8a92982d20863fe1fb69 https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-5_hd9741b5_openblas.conda#ca9d752201b7fa1225bca036ee300f2b https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-21.1.8-hf598326_0.conda#780f0251b757564e062187044232c2b7 @@ -46,6 +46,10 @@ https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda# https://conda.anaconda.org/conda-forge/osx-arm64/highspy-1.12.0-np2py313h9ce8dcc_0.conda#db9abb138afc8f175a5f7d6149074882 https://conda.anaconda.org/conda-forge/noarch/tsam-2.3.1-pyhd8ed1ab_0.conda#ed5f5e0cbc50f05631813b0d48021de1 https://conda.anaconda.org/conda-forge/noarch/tenacity-9.1.2-pyhd8ed1ab_0.conda#5d99943f2ae3cc69e1ada12ce9d4d701 +https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda#ab136e4c34e97f34fb621d2592a393d8 +https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda#12c566707c80111f9799308d9e265aef +https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.0.0-py313h224173a_1.conda#050374657d1c7a4f2ea443c0d0cbd9a0 +https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.25.0-py313h9734d34_1.conda#7ac13a947d4d9f57859993c06faf887b https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda#0caa1af407ecff61170c9437a808404d https://conda.anaconda.org/conda-forge/noarch/tqdm-loggable-0.2-pyhd8ed1ab_0.conda#bdb8608d3b834159b1b684dc6df3ac44 https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-1.17.3-py313hcdf3177_1.conda#cd6b5084444b0b4ed22dde20355d4c4b @@ -83,7 +87,7 @@ https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0. https://conda.anaconda.org/conda-forge/noarch/anyio-4.12.1-pyhcf101f3_0.conda#11a2b8c732d215d977998ccd69a9d5e8 https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda#4f14640d58e2cc0aa0819d9d8ba125bb https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda#d6989ead454181f4f9bc987d3dc4e285 -https://conda.anaconda.org/bioconda/noarch/snakemake-storage-plugin-cached-http-0.1.0-pyhdfd78af_0.conda#17232431f65ce347f972f0fd95d2e97a +https://conda.anaconda.org/bioconda/noarch/snakemake-storage-plugin-cached-http-0.2.1-pyhdfd78af_0.conda#f50f82ac51409fd0dc75a20a9f6930ea https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h925e9cb_3.conda#78a0fe9e9c50d2c381e8ee47e3ea437d https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py313h7d74516_0.conda#0e8e3235217b4483a7461b63dca5826b https://conda.anaconda.org/conda-forge/noarch/dpath-2.2.0-pyha770c72_1.conda#7b2af124684a994217e62c641bca2e48 @@ -91,7 +95,7 @@ https://conda.anaconda.org/conda-forge/noarch/yte-1.9.4-pyhd8ed1ab_0.conda#89d5e https://conda.anaconda.org/conda-forge/noarch/tabulate-0.9.0-pyhcf101f3_3.conda#de98449f11d48d4b52eefb354e2bfe35 https://conda.anaconda.org/bioconda/noarch/snakemake-interface-scheduler-plugins-2.0.2-pyhd4c3c12_0.conda#1500fccf5e46c7f91d14925449ff3632 https://conda.anaconda.org/bioconda/noarch/snakemake-interface-report-plugins-1.3.0-pyhd4c3c12_0.conda#e6fd8cfb23b294da699e395dbc968d11 -https://conda.anaconda.org/bioconda/noarch/snakemake-interface-logger-plugins-2.0.0-pyhd4c3c12_0.conda#98f75f2ca3a222992e2230d7afc54bb8 +https://conda.anaconda.org/bioconda/noarch/snakemake-interface-logger-plugins-1.2.4-pyhdfd78af_0.tar.bz2#cbb15afc697a71cc9a0e9bfd75ae59cc https://conda.anaconda.org/bioconda/noarch/snakemake-interface-executor-plugins-9.3.9-pyhdfd78af_0.tar.bz2#e75b9c422bcc3c9b52679dedb84f3b71 https://conda.anaconda.org/conda-forge/noarch/smart_open-7.5.0-pyhcf101f3_0.conda#9d1659c8332e9822e347e115e6bb4d0c https://conda.anaconda.org/conda-forge/osx-arm64/liblapacke-3.11.0-5_h1b118fd_openblas.conda#f77f540d134d9edec0dbf69dba56a4ad @@ -255,7 +259,7 @@ https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.73.1-h3063b79_1.conda https://conda.anaconda.org/conda-forge/osx-arm64/grpcio-1.73.1-py313hb057f1c_1.conda#eae3667f33e9e2a296b775547b42f506 https://conda.anaconda.org/conda-forge/noarch/googleapis-common-protos-1.72.0-pyhd8ed1ab_0.conda#003094932fb90de018f77a273b8a509b https://conda.anaconda.org/conda-forge/noarch/grpcio-status-1.73.1-pyhd8ed1ab_0.conda#5a2944f868149ad5a2e6588be8eed838 -https://conda.anaconda.org/conda-forge/noarch/pyasn1-0.6.1-pyhd8ed1ab_2.conda#09bb17ed307ad6ab2fd78d32372fdd4e +https://conda.anaconda.org/conda-forge/noarch/pyasn1-0.6.2-pyhd8ed1ab_0.conda#c203d401759f448f9e792974e055bcdc https://conda.anaconda.org/conda-forge/noarch/rsa-4.9.1-pyhd8ed1ab_0.conda#58958bb50f986ac0c46f73b6e290d5fe https://conda.anaconda.org/conda-forge/noarch/pyu2f-0.1.5-pyhd8ed1ab_1.conda#644bd4ca9f68ef536b902685d773d697 https://conda.anaconda.org/conda-forge/noarch/pyopenssl-25.3.0-pyhd8ed1ab_0.conda#ddf01a1d87103a152f725c7aeabffa29 @@ -316,7 +320,7 @@ https://conda.anaconda.org/conda-forge/noarch/geographiclib-2.1-pyhd8ed1ab_0.con https://conda.anaconda.org/conda-forge/noarch/geopy-2.4.1-pyhd8ed1ab_2.conda#40182a8d62a61d147ec7d3e4c5c36ac2 https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8.1-pyhd8ed1ab_0.conda#7de28c27fe620a4f7dbfaea137c6232b https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.14.3-pyha770c72_0.conda#5267bef8efea4127aacd1f4e1f149b6e -https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.7.8-pyhd8ed1ab_0.conda#3181cf53cd50513a1a7c00aae2f08e7a +https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.7.9-pyhd8ed1ab_0.conda#6e2b8dfbfd1b4e91f49fd13218db08d7 https://conda.anaconda.org/conda-forge/noarch/country_converter-1.3.2-pyhd8ed1ab_0.conda#193a9e54636d8d70781a3e56370f5502 https://conda.anaconda.org/conda-forge/noarch/powerplantmatching-0.8.0-pyhd8ed1ab_0.conda#3c806a133fb9e59dca249c5a00c2ab3e https://conda.anaconda.org/conda-forge/noarch/memory_profiler-0.61.0-pyhcf101f3_1.conda#e1bccffd88819e75729412799824e270 @@ -351,7 +355,7 @@ https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.5.1-pyhcf101f3_0.conda# https://conda.anaconda.org/conda-forge/noarch/bleach-6.3.0-pyhcf101f3_0.conda#b1a27250d70881943cca0dd6b4ba0956 https://conda.anaconda.org/conda-forge/noarch/bleach-with-css-6.3.0-h5f6438b_0.conda#08a03378bc5293c6f97637323802f480 https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.6-pyhcf101f3_1.conda#cfc86ccc3b1de35d36ccaae4c50391f5 -https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_1.conda#2d983ff1b82a1ccb6f2e9d8784bdd6bd +https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.4-pyhcf101f3_0.conda#7b8bace4943e0dc345fc45938826f2b8 https://conda.anaconda.org/conda-forge/noarch/rfc3986-validator-0.1.1-pyh9f0ad1d_0.tar.bz2#912a71cc01012ee38e6b90ddd561e36f https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_1.conda#36de09a8d3e5d5e6f4ee63af49e59706 https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda#a61bf9ec79426938ff785eb69dbb1960 @@ -431,7 +435,7 @@ https://conda.anaconda.org/conda-forge/osx-arm64/epoxy-1.5.10-hc919400_2.conda#3 https://conda.anaconda.org/conda-forge/osx-arm64/atk-1.0-2.38.0-hd03087b_2.conda#57301986d02d30d6805fdce6c99074ee https://conda.anaconda.org/conda-forge/osx-arm64/gtk3-3.24.43-h5febe37_6.conda#a99f96906158ebae5e3c0904bcd45145 https://conda.anaconda.org/conda-forge/noarch/adwaita-icon-theme-49.0-unix_0.conda#b3f0179590f3c0637b7eb5309898f79e -https://conda.anaconda.org/conda-forge/osx-arm64/graphviz-14.1.0-ha8f0fc4_0.conda#1463b9b703d3fc6eba63587c69611e91 +https://conda.anaconda.org/conda-forge/osx-arm64/graphviz-14.1.2-hec8c438_0.conda#1f3d859de3ca2bcaa845e92e87d73660 https://conda.anaconda.org/conda-forge/osx-arm64/glpk-5.0-h6d7a090_0.tar.bz2#02b868940101a06a6365c109ab1a94fe https://conda.anaconda.org/conda-forge/noarch/geojson-3.2.0-pyhd8ed1ab_0.conda#9f9840fb1c2e009fb0009a2f9461e64a https://conda.anaconda.org/conda-forge/osx-arm64/fiona-1.10.1-py313h7df67bf_6.conda#dc81b108af52deb655ea85f9b745f7e2 @@ -455,18 +459,18 @@ https://conda.anaconda.org/conda-forge/noarch/s3transfer-0.16.0-pyhd8ed1ab_0.con https://conda.anaconda.org/conda-forge/noarch/boto3-1.42.26-pyhd8ed1ab_0.conda#5225da63f2304a4e3a58c6f10497c0ff https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.11.2-hd2415e0_0.conda#1ae98806b064c48f184d7c6e0ac506b6 https://conda.anaconda.org/conda-forge/osx-arm64/prometheus-cpp-1.3.0-h0967b3e_0.conda#7172339b49c94275ba42fec3eaeda34f -https://conda.anaconda.org/conda-forge/osx-arm64/nlohmann_json-3.12.0-h248ca61_1.conda#3ba9d0c21af2150cb92b2ab8bdad3090 +https://conda.anaconda.org/conda-forge/osx-arm64/nlohmann_json-3.12.0-h784d473_1.conda#755cfa6c08ed7b7acbee20ccbf15a47c https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-headers-1.21.0-hce30654_1.conda#c7df4b2d612208f3a27486c113b6aefc https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-1.21.0-he15edb5_1.conda#cbcea547d6d831863ab0a4e164099062 -https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.2.1-h4fd0076_0.conda#b5dea50c77ab3cc18df48bdc9994ac44 +https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.2.2-hac85105_0.conda#1c52effb297c8287cc79c383428e43c4 https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-2.39.0-head0a95_0.conda#ad7272a081abe0966d0297691154eda5 https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-2.39.0-hfa3a374_0.conda#147a468b9b6c3ced1fccd69b864ae289 https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.2.2-hf9b8971_1005.conda#57a511a5905caa37540eb914dfcbf1fb https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-heb240a5_0.conda#fef68d0a95aa5b84b5c1a4f6f3bf40e1 https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.16.1-h88fedcc_0.conda#fbe485a39b05090c0b5f8bb4febcd343 -https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.11.0-h7e4aa5d_1.conda#ac9113ea0b7ed5ecf452503f82bf2956 -https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.15.0-h10d327b_1.conda#443b74cf38c6b0f4b675c0517879ce69 -https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.13.0-hb288d13_1.conda#595091ae43974e5059d6eabf0a6a7aa5 +https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.12.0-ha416c23_0.conda#327799f2eb655ddf596b3e0ba2658979 +https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.16.0-h6507aac_0.conda#ebcb072935c1595c39e2c62f0d3e50cc +https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.14.0-hcfc4f22_0.conda#a49804e4c4ad182a8de7b251d77f3b0c https://conda.anaconda.org/conda-forge/osx-arm64/azure-identity-cpp-1.13.2-h853621b_1.conda#fac63edc393d7035ab23fbccdeda34f4 https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.12.6-hc919400_0.conda#b759f02a7fa946ea9fd9fb035422c848 https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.4-h16f91aa_4.conda#658a8236f3f1ebecaaa937b5ccd5d730 @@ -492,7 +496,7 @@ https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-22.0.0-hc31799 https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-22.0.0-h144af7f_6_cpu.conda#58a5b39bc7d23fa938affe1bfc43c241 https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-22.0.0-py313h39782a4_0.conda#602f2d43efb0dda27ed3b1c86b4cdb75 https://conda.anaconda.org/conda-forge/noarch/arcosparse-0.4.2-pyhd8ed1ab_0.conda#9a005ba5f540619a1343587b4ee3d95e -https://conda.anaconda.org/conda-forge/noarch/copernicusmarine-2.2.5-pyhd8ed1ab_0.conda#e6f85f3cd0c5aff4ef0e07e80f49fa39 +https://conda.anaconda.org/conda-forge/noarch/copernicusmarine-2.3.0-pyhd8ed1ab_0.conda#9e18b048c69d2d72bc69d120a435d731 https://conda.anaconda.org/conda-forge/noarch/pyshp-3.0.3-pyhd8ed1ab_0.conda#c138c7aaa6a10b5762dcd92247864aff https://conda.anaconda.org/conda-forge/osx-arm64/cartopy-0.25.0-py313h7d16b84_1.conda#65859d540753d1a0acb05029eb6cf492 https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda#d7585b6550ad04c8c5e21097ada2888e @@ -514,6 +518,6 @@ https://conda.anaconda.org/conda-forge/osx-arm64/eccodes-2.44.0-h6f4dcf9_0.conda https://conda.anaconda.org/conda-forge/osx-arm64/python-eccodes-2.44.0-py313hc577518_1.conda#77b8497affc46fc25bf0a3f5f2d77e5e https://conda.anaconda.org/conda-forge/noarch/cfgrib-0.9.15.1-pyhd8ed1ab_0.conda#0f12f8436a2a238e255d49ea3f8aefe2 https://conda.anaconda.org/conda-forge/noarch/multiurl-0.3.7-pyhd8ed1ab_0.conda#e585c71c2ed48e4eee1663d627ddcd47 -https://conda.anaconda.org/conda-forge/noarch/ecmwf-datastores-client-0.4.1-pyhd8ed1ab_0.conda#ea90ece1da754ca0c5d6766eb59908c2 +https://conda.anaconda.org/conda-forge/noarch/ecmwf-datastores-client-0.4.2-pyhd8ed1ab_0.conda#d957f10f516dcdeb9e382c91d771df12 https://conda.anaconda.org/conda-forge/noarch/cdsapi-0.7.7-pyhd8ed1ab_0.conda#1f878573c1ee2798c052bee1f5a94f50 https://conda.anaconda.org/conda-forge/noarch/atlite-0.4.1-pyhd8ed1ab_1.conda#81f981df273cd627927372680aa9dd31 diff --git a/envs/default_win-64.pin.txt b/envs/default_win-64.pin.txt index a284d679e..2bb331679 100644 --- a/envs/default_win-64.pin.txt +++ b/envs/default_win-64.pin.txt @@ -40,6 +40,10 @@ https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda# https://conda.anaconda.org/conda-forge/win-64/highspy-1.12.0-np2py313h776c0ec_0.conda#285e57df4d9f89d593a534fe528327b8 https://conda.anaconda.org/conda-forge/noarch/tsam-2.3.1-pyhd8ed1ab_0.conda#ed5f5e0cbc50f05631813b0d48021de1 https://conda.anaconda.org/conda-forge/noarch/tenacity-9.1.2-pyhd8ed1ab_0.conda#5d99943f2ae3cc69e1ada12ce9d4d701 +https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda#053b84beec00b71ea8ff7a4f84b55207 +https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda#12c566707c80111f9799308d9e265aef +https://conda.anaconda.org/conda-forge/win-64/cffi-2.0.0-py313h5ea7bf4_1.conda#55b44664f66a2caf584d72196aa98af9 +https://conda.anaconda.org/conda-forge/win-64/zstandard-0.25.0-py313h5fd188c_1.conda#46f6f9bb324a58a9b081bbc56ade37f2 https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda#0caa1af407ecff61170c9437a808404d https://conda.anaconda.org/conda-forge/noarch/tqdm-loggable-0.2-pyhd8ed1ab_0.conda#bdb8608d3b834159b1b684dc6df3ac44 https://conda.anaconda.org/conda-forge/win-64/wrapt-1.17.3-py313h5ea7bf4_1.conda#3e199c8db04833fe628867462aeaca24 @@ -78,7 +82,7 @@ https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0. https://conda.anaconda.org/conda-forge/noarch/anyio-4.12.1-pyhcf101f3_0.conda#11a2b8c732d215d977998ccd69a9d5e8 https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda#4f14640d58e2cc0aa0819d9d8ba125bb https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda#d6989ead454181f4f9bc987d3dc4e285 -https://conda.anaconda.org/bioconda/noarch/snakemake-storage-plugin-cached-http-0.1.0-pyhdfd78af_0.conda#17232431f65ce347f972f0fd95d2e97a +https://conda.anaconda.org/bioconda/noarch/snakemake-storage-plugin-cached-http-0.2.1-pyhdfd78af_0.conda#f50f82ac51409fd0dc75a20a9f6930ea https://conda.anaconda.org/conda-forge/win-64/yaml-0.2.5-h6a83c73_3.conda#433699cba6602098ae8957a323da2664 https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py313hd650c13_0.conda#c067122d76f8dcbe0848822942ba07be https://conda.anaconda.org/conda-forge/noarch/dpath-2.2.0-pyha770c72_1.conda#7b2af124684a994217e62c641bca2e48 @@ -86,7 +90,7 @@ https://conda.anaconda.org/conda-forge/noarch/yte-1.9.4-pyhd8ed1ab_0.conda#89d5e https://conda.anaconda.org/conda-forge/noarch/tabulate-0.9.0-pyhcf101f3_3.conda#de98449f11d48d4b52eefb354e2bfe35 https://conda.anaconda.org/bioconda/noarch/snakemake-interface-scheduler-plugins-2.0.2-pyhd4c3c12_0.conda#1500fccf5e46c7f91d14925449ff3632 https://conda.anaconda.org/bioconda/noarch/snakemake-interface-report-plugins-1.3.0-pyhd4c3c12_0.conda#e6fd8cfb23b294da699e395dbc968d11 -https://conda.anaconda.org/bioconda/noarch/snakemake-interface-logger-plugins-2.0.0-pyhd4c3c12_0.conda#98f75f2ca3a222992e2230d7afc54bb8 +https://conda.anaconda.org/bioconda/noarch/snakemake-interface-logger-plugins-1.2.4-pyhdfd78af_0.tar.bz2#cbb15afc697a71cc9a0e9bfd75ae59cc https://conda.anaconda.org/bioconda/noarch/snakemake-interface-executor-plugins-9.3.9-pyhdfd78af_0.tar.bz2#e75b9c422bcc3c9b52679dedb84f3b71 https://conda.anaconda.org/conda-forge/noarch/smart_open-7.5.0-pyhcf101f3_0.conda#9d1659c8332e9822e347e115e6bb4d0c https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda#64571d1dd6cdcfa25d0664a5950fdaa2 @@ -178,6 +182,7 @@ https://conda.anaconda.org/conda-forge/win-64/rasterio-1.4.4-py313hfe0960c_1.con https://conda.anaconda.org/conda-forge/win-64/pyproj-3.7.2-py313h24787ba_2.conda#b0093312a3b115bd033e74aa92bea3a1 https://conda.anaconda.org/conda-forge/noarch/rioxarray-0.20.0-pyhd8ed1ab_1.conda#e7e37bf890147fa5d7892812a6dd3888 https://conda.anaconda.org/conda-forge/noarch/pyxlsb-1.0.10-pyhd8ed1ab_0.tar.bz2#0c14e44bc93a99cdc11398311c3c0dcf +https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.2.1-pyhcf101f3_0.conda#083725d6cd3dc007f06d04bcf1e613a2 https://conda.anaconda.org/conda-forge/win-64/libgomp-15.2.0-h8ee18e1_16.conda#ab8189163748f95d4cb18ea1952943c3 https://conda.anaconda.org/conda-forge/win-64/_openmp_mutex-4.5-2_gnu.conda#37e16618af5c4851a3f3d66dd0e11141 https://conda.anaconda.org/conda-forge/win-64/libgcc-15.2.0-h8ee18e1_16.conda#1edb8bd8e093ebd31558008e9cb23b47 @@ -254,7 +259,7 @@ https://conda.anaconda.org/conda-forge/win-64/libgrpc-1.73.1-h317e13b_1.conda#85 https://conda.anaconda.org/conda-forge/win-64/grpcio-1.73.1-py313h3c83859_1.conda#1c1c68305b8e4594f993846576318c46 https://conda.anaconda.org/conda-forge/noarch/googleapis-common-protos-1.72.0-pyhd8ed1ab_0.conda#003094932fb90de018f77a273b8a509b https://conda.anaconda.org/conda-forge/noarch/grpcio-status-1.73.1-pyhd8ed1ab_0.conda#5a2944f868149ad5a2e6588be8eed838 -https://conda.anaconda.org/conda-forge/noarch/pyasn1-0.6.1-pyhd8ed1ab_2.conda#09bb17ed307ad6ab2fd78d32372fdd4e +https://conda.anaconda.org/conda-forge/noarch/pyasn1-0.6.2-pyhd8ed1ab_0.conda#c203d401759f448f9e792974e055bcdc https://conda.anaconda.org/conda-forge/noarch/rsa-4.9.1-pyhd8ed1ab_0.conda#58958bb50f986ac0c46f73b6e290d5fe https://conda.anaconda.org/conda-forge/noarch/pyu2f-0.1.5-pyhd8ed1ab_1.conda#644bd4ca9f68ef536b902685d773d697 https://conda.anaconda.org/conda-forge/noarch/pyopenssl-25.3.0-pyhd8ed1ab_0.conda#ddf01a1d87103a152f725c7aeabffa29 @@ -315,7 +320,7 @@ https://conda.anaconda.org/conda-forge/noarch/geographiclib-2.1-pyhd8ed1ab_0.con https://conda.anaconda.org/conda-forge/noarch/geopy-2.4.1-pyhd8ed1ab_2.conda#40182a8d62a61d147ec7d3e4c5c36ac2 https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8.1-pyhd8ed1ab_0.conda#7de28c27fe620a4f7dbfaea137c6232b https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.14.3-pyha770c72_0.conda#5267bef8efea4127aacd1f4e1f149b6e -https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.7.8-pyhd8ed1ab_0.conda#3181cf53cd50513a1a7c00aae2f08e7a +https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.7.9-pyhd8ed1ab_0.conda#6e2b8dfbfd1b4e91f49fd13218db08d7 https://conda.anaconda.org/conda-forge/noarch/country_converter-1.3.2-pyhd8ed1ab_0.conda#193a9e54636d8d70781a3e56370f5502 https://conda.anaconda.org/conda-forge/noarch/powerplantmatching-0.8.0-pyhd8ed1ab_0.conda#3c806a133fb9e59dca249c5a00c2ab3e https://conda.anaconda.org/conda-forge/noarch/memory_profiler-0.61.0-pyhcf101f3_1.conda#e1bccffd88819e75729412799824e270 @@ -367,7 +372,7 @@ https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.5.1-pyhcf101f3_0.conda# https://conda.anaconda.org/conda-forge/noarch/bleach-6.3.0-pyhcf101f3_0.conda#b1a27250d70881943cca0dd6b4ba0956 https://conda.anaconda.org/conda-forge/noarch/bleach-with-css-6.3.0-h5f6438b_0.conda#08a03378bc5293c6f97637323802f480 https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.6-pyhcf101f3_1.conda#cfc86ccc3b1de35d36ccaae4c50391f5 -https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_1.conda#2d983ff1b82a1ccb6f2e9d8784bdd6bd +https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.4-pyhcf101f3_0.conda#7b8bace4943e0dc345fc45938826f2b8 https://conda.anaconda.org/conda-forge/noarch/rfc3986-validator-0.1.1-pyh9f0ad1d_0.tar.bz2#912a71cc01012ee38e6b90ddd561e36f https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_1.conda#36de09a8d3e5d5e6f4ee63af49e59706 https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda#a61bf9ec79426938ff785eb69dbb1960 @@ -481,7 +486,7 @@ https://conda.anaconda.org/conda-forge/win-64/libarrow-dataset-22.0.0-h7d8d6a5_6 https://conda.anaconda.org/conda-forge/win-64/libarrow-substrait-22.0.0-hf865cc0_6_cpu.conda#01d0606bf4202d358a71545759223202 https://conda.anaconda.org/conda-forge/win-64/pyarrow-22.0.0-py313hfa70ccb_0.conda#dc9d22fa905cbb90914b29dc9791985d https://conda.anaconda.org/conda-forge/noarch/arcosparse-0.4.2-pyhd8ed1ab_0.conda#9a005ba5f540619a1343587b4ee3d95e -https://conda.anaconda.org/conda-forge/noarch/copernicusmarine-2.2.5-pyhd8ed1ab_0.conda#e6f85f3cd0c5aff4ef0e07e80f49fa39 +https://conda.anaconda.org/conda-forge/noarch/copernicusmarine-2.3.0-pyhd8ed1ab_0.conda#9e18b048c69d2d72bc69d120a435d731 https://conda.anaconda.org/conda-forge/noarch/pyshp-3.0.3-pyhd8ed1ab_0.conda#c138c7aaa6a10b5762dcd92247864aff https://conda.anaconda.org/conda-forge/win-64/cartopy-0.25.0-py313hc90dcd4_1.conda#a3e17bc9d5a5e82c0c0fbea5ced9a5ff https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda#d7585b6550ad04c8c5e21097ada2888e @@ -504,6 +509,6 @@ https://conda.anaconda.org/conda-forge/win-64/eccodes-2.44.0-h2bffdaa_0.conda#c0 https://conda.anaconda.org/conda-forge/win-64/python-eccodes-2.44.0-py313h0591002_1.conda#0de0cc9bdbefa9af557c42e26792a6ca https://conda.anaconda.org/conda-forge/noarch/cfgrib-0.9.15.1-pyhd8ed1ab_0.conda#0f12f8436a2a238e255d49ea3f8aefe2 https://conda.anaconda.org/conda-forge/noarch/multiurl-0.3.7-pyhd8ed1ab_0.conda#e585c71c2ed48e4eee1663d627ddcd47 -https://conda.anaconda.org/conda-forge/noarch/ecmwf-datastores-client-0.4.1-pyhd8ed1ab_0.conda#ea90ece1da754ca0c5d6766eb59908c2 +https://conda.anaconda.org/conda-forge/noarch/ecmwf-datastores-client-0.4.2-pyhd8ed1ab_0.conda#d957f10f516dcdeb9e382c91d771df12 https://conda.anaconda.org/conda-forge/noarch/cdsapi-0.7.7-pyhd8ed1ab_0.conda#1f878573c1ee2798c052bee1f5a94f50 https://conda.anaconda.org/conda-forge/noarch/atlite-0.4.1-pyhd8ed1ab_1.conda#81f981df273cd627927372680aa9dd31 diff --git a/envs/environment.yaml b/envs/environment.yaml index 7cde1e9b6..ccbadd863 100644 --- a/envs/environment.yaml +++ b/envs/environment.yaml @@ -35,17 +35,19 @@ dependencies: - jpype1 >=1.6.0 - jupyter >=1.1.1 - libgdal-netcdf >=3.10.3 -- linopy >=0.4.4 +- linopy >=0.6.1 - lxml >=6.0.2 - matplotlib >=3.10.7 - memory_profiler >=0.61.0 -- netcdf4 >=1.7.2 +- netcdf4 >=1.7.2,!=1.7.4 - networkx >=3.5 - numpy >=1.26.4 - openpyxl >=3.1.5 - pandas >=2.1 - plotly >=6.4.0 - powerplantmatching >=0.5.15 +- pydantic >=2 +- python-dotenv >=1.0 - pre-commit >=4.3.0 - proj >=9.6.2 - pylint >=4.0.2 @@ -59,11 +61,11 @@ dependencies: - rasterio >=1.4.3 - rioxarray >=0.20.0 - ruff >=0.14.3 +- ruamel.yaml >=0.18.0 - scipy >=1.16.3 - seaborn >=0.13.2 - shapely >=2.0 - snakemake-executor-plugin-cluster-generic >=1.0.9 -- snakemake-executor-plugin-slurm >=1.9.2 - snakemake-minimal >=9 - snakemake-storage-plugin-http >=0.3 - tenacity >=9.1.2 @@ -72,5 +74,8 @@ dependencies: - xarray >=2024.3.0,<2025.7.0 - xlrd >=2.0.2 - yaml >=0.2.5 -- snakemake-storage-plugin-cached-http >=0.1.0 - +- snakemake-storage-plugin-cached-http >=0.2.1 +- pandera >=0.28.1 +- natsort >=8.4.0 +- snakemake-logger-plugin-pypsa >=0.1.0 +- snakemake-executor-plugin-slurm >=1.9.2 diff --git a/pixi.toml b/pixi.toml index d90f7237a..19607dfc8 100644 --- a/pixi.toml +++ b/pixi.toml @@ -34,6 +34,8 @@ update-dags = """ snakemake --rulegraph -F | sed -n "/digraph/,/}/p" | dot -Tpng -o doc/img/workflow.png """ +generate-config = "python -c 'from scripts.lib.validation.config import generate_config_defaults, generate_config_schema; generate_config_defaults(); generate_config_schema()'" + [dependencies] # Add additional packages only to pypsa-de specific section # All other packages should be identical to pypsa-eur (in best case the same, otherwise @@ -64,17 +66,19 @@ ipython = ">=9.7.0" jpype1 = ">=1.6.0" jupyter = ">=1.1.1" libgdal-netcdf = ">=3.10.3" -linopy = ">=0.4.4" +linopy = ">=0.6.1" lxml = ">=6.0.2" matplotlib = ">=3.10.7" memory_profiler = ">=0.61.0" -netcdf4 = ">=1.7.2" +netcdf4 = ">=1.7.2,!=1.7.4" networkx = ">=3.5" numpy = ">=1.26.4" openpyxl = ">=3.1.5" pandas = ">=2.1" plotly = ">=6.4.0" powerplantmatching = ">=0.5.15" +pydantic = ">=2" +python-dotenv = ">=1.0" pre-commit = ">=4.3.0" proj = ">=9.6.2" pylint = ">=4.0.2" @@ -88,11 +92,11 @@ pyxlsb = ">=1.0.10" rasterio = ">=1.4.3" rioxarray = ">=0.20.0" ruff = ">=0.14.3" +"ruamel.yaml" = ">=0.18.0" scipy = ">=1.16.3" seaborn = ">=0.13.2" shapely = ">=2.0" snakemake-executor-plugin-cluster-generic = ">=1.0.9" -snakemake-executor-plugin-slurm = ">=1.9.2" snakemake-minimal = ">=9" snakemake-storage-plugin-http = ">=0.3" tenacity = ">=9.1.2" @@ -101,37 +105,45 @@ tsam = ">=2.3.1" xarray = ">=2024.3.0,<2025.7.0" xlrd = ">=2.0.2" yaml = ">=0.2.5" -snakemake-storage-plugin-cached-http = ">=0.1.0" +snakemake-storage-plugin-cached-http = ">=0.2.1" +pandera = ">=0.28.1" +natsort = ">=8.4.0" +snakemake-logger-plugin-pypsa = ">=0.1.0" + +[target.unix.dependencies] +snakemake-executor-plugin-slurm = ">=1.9.2" [feature.doc.tasks.build-docs] args = ["dir", {"arg" = "output", "default" = "html"}] cmd = "dot -c && sphinx-build -T -b {{ output }} doc {{ dir }}/{{ output }} " [feature.doc.dependencies] -atlite = ">=0.2.9" -cartopy = ">=0.25.0" -dask = ">=2025.10.0" -descartes = ">=1.1.0" -fiona = ">=1.10.1" -graphviz = ">=13.1.2" -matplotlib = ">3.5.1" -memory_profiler = ">=0.61.0" -myst-parser = ">=4.0.1" -powerplantmatching = ">=0.5.5" -pydot = ">=4.0.1,<5" -pypsa = ">=0.35.2" -pytables = ">=3.10.1" -python = ">=3.10.19" -pyyaml = ">=6.0.3" -requests = ">=2.32.5" -scikit-learn = ">=1.7.2" -seaborn = ">=0.13.2" -sphinx = ">=8.1.3" -sphinx-book-theme = ">=1.1.4" -sphinxcontrib-bibtex = ">=2.6.5" -tabula-py = ">=2.7.0" -tenacity = ">=9.1.2" -tsam = ">=2.3.1" +atlite = "==0.4.1" +cartopy = "==0.25.0" +dask = "==2025.11.0" +descartes = "==1.1.0" +fiona = "==1.10.1" +graphviz = "==13.1.2" +matplotlib = "==3.10.7" +memory_profiler = "==0.61.0" +myst-parser = "==4.0.1" +powerplantmatching = "==0.7.1" +pydot = "==4.0.1" +pypsa = "==1.0.3" +pytables = "==3.10.2" +python = "==3.12.12" +pyyaml = "==6.0.3" +requests = "==2.32.5" +scikit-learn = "==1.7.2" +seaborn = "==0.13.2" +sphinx = "==8.2.3" +sphinx-book-theme = "==1.1.4" +sphinx-design = "==0.6.1" +sphinx-jsonschema = "==1.19.2" +sphinxcontrib-bibtex = "==2.6.5" +tabula-py = "==2.7.0" +tenacity = "==9.1.2" +tsam = "==2.3.9" [feature.test.tasks] diff --git a/profiles/default/config.yaml b/profiles/default/config.yaml new file mode 100644 index 000000000..83e5b1c52 --- /dev/null +++ b/profiles/default/config.yaml @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +# See https://snakemake.readthedocs.io/en/stable/executing/cli.html#profiles + +logger: pypsa diff --git a/rules/build_electricity.smk b/rules/build_electricity.smk index 058c8c9b2..202272aa2 100755 --- a/rules/build_electricity.smk +++ b/rules/build_electricity.smk @@ -4,6 +4,8 @@ rule build_electricity_demand: + message: + "Building electricity demand time series" params: snapshots=config_provider("snapshots"), drop_leap_day=config_provider("enable", "drop_leap_day"), @@ -29,6 +31,8 @@ rule build_electricity_demand: rule build_powerplants: + message: + "Building powerplant list for {wildcards.clusters} clusters" params: powerplants_filter=config_provider("electricity", "powerplants_filter"), custom_powerplants=config_provider("electricity", "custom_powerplants"), @@ -70,6 +74,8 @@ def input_base_network(w): rule base_network: + message: + "Building base network" params: countries=config_provider("countries"), snapshots=config_provider("snapshots"), @@ -102,6 +108,8 @@ rule base_network: rule build_osm_boundaries: + message: + "Building OSM boundaries for {wildcards.country}" input: json=f"{OSM_BOUNDARIES_DATASET['folder']}/{{country}}_adm1.json", eez=ancient(rules.retrieve_eez.output["gpkg"]), @@ -117,6 +125,8 @@ rule build_osm_boundaries: rule build_bidding_zones: + message: + "Building bidding zones" params: countries=config_provider("countries"), remove_islands=config_provider( @@ -126,8 +136,8 @@ rule build_bidding_zones: "clustering", "build_bidding_zones", "aggregate_to_tyndp" ), input: - bidding_zones_entsoepy="data/busshapes/bidding_zones_entsoepy.geojson", - bidding_zones_electricitymaps="data/busshapes/bidding_zones_electricitymaps.geojson", + bidding_zones_entsoepy=f"{BIDDING_ZONES_ENTSOEPY_DATASET['folder']}/bidding_zones_entsoepy.geojson", + bidding_zones_electricitymaps=f"{BIDDING_ZONES_ELECTRICITYMAPS_DATASET['folder']}/bidding_zones_electricitymaps.geojson", output: file=resources("bidding_zones.geojson"), log: @@ -140,6 +150,8 @@ rule build_bidding_zones: rule build_shapes: + message: + "Building geographical shapes" params: config_provider("clustering", "mode"), countries=config_provider("countries"), @@ -178,6 +190,8 @@ rule build_shapes: if CUTOUT_DATASET["source"] in ["build"]: rule build_cutout: + message: + "Building cutout data for {wildcards.cutout}" params: cutouts=config_provider("atlite", "cutouts"), output: @@ -194,6 +208,8 @@ if CUTOUT_DATASET["source"] in ["build"]: rule build_ship_raster: + message: + "Building ship density raster" input: ship_density=rules.retrieve_ship_raster.output["zip_file"], cutout=lambda w: input_cutout(w), @@ -210,6 +226,8 @@ rule build_ship_raster: rule determine_availability_matrix_MD_UA: + message: + "Determining availability matrix for {wildcards.clusters} clusters and {wildcards.technology} technology" params: renewable=config_provider("renewable"), input: @@ -264,13 +282,15 @@ def input_ua_md_availability_matrix(w): rule determine_availability_matrix: + message: + "Determining availability matrix for {wildcards.clusters} clusters and {wildcards.technology} technology" params: renewable=config_provider("renewable"), input: unpack(input_ua_md_availability_matrix), corine=ancient(rules.retrieve_corine.output["tif_file"]), natura=lambda w: ( - f"{NATURA_DATASET["folder"]}/natura.tiff" + f"{NATURA_DATASET['folder']}/natura.tiff" if config_provider("renewable", w.technology, "natura")(w) else [] ), @@ -314,6 +334,8 @@ rule determine_availability_matrix: rule build_renewable_profiles: + message: + "Building renewable profiles for {wildcards.clusters} clusters and {wildcards.technology} technology" params: snapshots=config_provider("snapshots"), drop_leap_day=config_provider("enable", "drop_leap_day"), @@ -347,6 +369,8 @@ rule build_renewable_profiles: rule build_monthly_prices: + message: + "Building monthly fuel and CO2 prices" input: co2_price_raw="data/validation/emission-spot-primary-market-auction-report-2019-data.xls", fuel_price_raw="data/validation/energy-price-trends-xlsx-5619002.xlsx", @@ -387,6 +411,8 @@ if COUNTRY_RUNOFF_DATASET["source"] == "build": rule build_hydro_profile: + message: + "Building hydropower profile" params: hydro=config_provider("renewable", "hydro"), countries=config_provider("countries"), @@ -396,7 +422,7 @@ rule build_hydro_profile: country_shapes=resources("country_shapes.geojson"), eia_hydro_generation="data/eia_hydro_annual_generation.csv", eia_hydro_capacity="data/eia_hydro_annual_capacity.csv", - era5_runoff=f"{COUNTRY_RUNOFF_DATASET["folder"]}/era5-runoff-per-country.csv", + era5_runoff=f"{COUNTRY_RUNOFF_DATASET['folder']}/era5-runoff-per-country.csv", cutout=lambda w: input_cutout( w, config_provider("renewable", "hydro", "cutout")(w) ), @@ -413,6 +439,8 @@ rule build_hydro_profile: rule build_line_rating: + message: + "Building dynamic line ratings" params: snapshots=config_provider("snapshots"), drop_leap_day=config_provider("enable", "drop_leap_day"), @@ -435,6 +463,8 @@ rule build_line_rating: rule build_transmission_projects: + message: + "Building transmission projects" params: transmission_projects=config_provider("transmission_projects"), line_factor=config_provider("lines", "length_factor"), @@ -468,6 +498,8 @@ rule build_transmission_projects: rule add_transmission_projects_and_dlr: + message: + "Adding transmission projects and dynamic line ratings" params: transmission_projects=config_provider("transmission_projects"), dlr=config_provider("lines", "dynamic_line_rating"), @@ -514,6 +546,8 @@ def input_class_regions(w): rule build_electricity_demand_base: + message: + "Building electricity demand time series for base network" params: distribution_key=config_provider("load", "distribution_key"), input: @@ -534,6 +568,8 @@ rule build_electricity_demand_base: rule build_hac_features: + message: + "Aggregate all rastered cutout data to base regions Voronoi cells." params: snapshots=config_provider("snapshots"), drop_leap_day=config_provider("enable", "drop_leap_day"), @@ -576,6 +612,8 @@ rule process_cost_data: rule simplify_network: + message: + "Simplifying network" params: countries=config_provider("countries"), mode=config_provider("clustering", "mode"), @@ -631,6 +669,8 @@ def input_custom_busmap(w): rule cluster_network: + message: + "Clustering network to {wildcards.clusters} clusters" params: countries=config_provider("countries"), mode=config_provider("clustering", "mode"), @@ -709,6 +749,8 @@ def input_conventional(w): rule add_electricity: + message: + "Adding electricity to network with {wildcards.clusters} clusters" params: line_length_factor=config_provider("lines", "length_factor"), link_length_factor=config_provider("links", "length_factor"), @@ -758,6 +800,8 @@ rule add_electricity: rule prepare_network: + message: + "Preparing network for model with {wildcards.clusters} clusters and options {wildcards.opts}" params: time_resolution=config_provider("clustering", "temporal", "resolution_elec"), links=config_provider("links"), @@ -797,6 +841,8 @@ if ( ): rule clean_osm_data: + message: + "Cleaning raw OSM data for countries: " + ", ".join(config["countries"]) input: cables_way=expand( f"{OSM_DATASET['folder']}/{{country}}/cables_way.json", @@ -837,6 +883,8 @@ if ( "../scripts/clean_osm_data.py" rule build_osm_network: + message: + "Building OSM network" params: countries=config_provider("countries"), voltages=config_provider("electricity", "voltages"), @@ -875,6 +923,8 @@ if ( if config["electricity"]["base_network"] == "tyndp": rule build_tyndp_network: + message: + "Building TYNDP network" params: countries=config_provider("countries"), input: diff --git a/rules/build_sector.smk b/rules/build_sector.smk index 2c32d2586..e7da73182 100755 --- a/rules/build_sector.smk +++ b/rules/build_sector.smk @@ -4,6 +4,8 @@ rule build_population_layouts: + message: + "Building population layout data (total, urban, rural) from NUTS3 shapes and World Bank statistics" input: nuts3_shapes=resources("nuts3_shapes.geojson"), urban_percent=rules.retrieve_worldbank_urban_population.output["csv"], @@ -24,6 +26,8 @@ rule build_population_layouts: rule build_clustered_population_layouts: + message: + "Clustering population layouts for {wildcards.clusters} clusters" input: pop_layout_total=resources("pop_layout_total.nc"), pop_layout_urban=resources("pop_layout_urban.nc"), @@ -43,6 +47,8 @@ rule build_clustered_population_layouts: rule build_clustered_solar_rooftop_potentials: + message: + "Building solar rooftop potentials for {wildcards.clusters} clusters" input: pop_layout=resources("pop_layout_total.nc"), class_regions=resources("regions_by_class_{clusters}_solar.geojson"), @@ -60,6 +66,8 @@ rule build_clustered_solar_rooftop_potentials: rule build_simplified_population_layouts: + message: + "Building simplified population layouts for base scenario" input: pop_layout_total=resources("pop_layout_total.nc"), pop_layout_urban=resources("pop_layout_urban.nc"), @@ -79,6 +87,8 @@ rule build_simplified_population_layouts: rule build_gas_network: + message: + "Building cleaned gas network from SciGRID-Gas data" input: gas_network=rules.retrieve_gas_infrastructure_data.output["gas_network"], output: @@ -94,6 +104,8 @@ rule build_gas_network: rule build_gas_input_locations: + message: + "Building gas input locations for {wildcards.clusters} clusters" input: gem="data/gem/Europe-Gas-Tracker-2024-05.xlsx", entry=rules.retrieve_gas_infrastructure_data.output["entry"], @@ -116,6 +128,8 @@ rule build_gas_input_locations: rule cluster_gas_network: + message: + "Clustering gas network for {wildcards.clusters} clusters" input: cleaned_gas_network=resources("gas_network.csv"), regions_onshore=resources("regions_onshore_base_s_{clusters}.geojson"), @@ -133,6 +147,8 @@ rule cluster_gas_network: rule build_daily_heat_demand: + message: + "Building daily heat demand profiles for {wildcards.clusters} clusters" params: snapshots=config_provider("snapshots"), drop_leap_day=config_provider("enable", "drop_leap_day"), @@ -156,6 +172,8 @@ rule build_daily_heat_demand: rule build_hourly_heat_demand: + message: + "Building hourly heat demand profiles from daily demand for {wildcards.clusters} clusters" params: snapshots=config_provider("snapshots"), drop_leap_day=config_provider("enable", "drop_leap_day"), @@ -180,6 +198,8 @@ rule build_hourly_heat_demand: rule build_temperature_profiles: + message: + "Building temperature profiles for {wildcards.clusters} clusters" params: snapshots=config_provider("snapshots"), drop_leap_day=config_provider("enable", "drop_leap_day"), @@ -208,6 +228,8 @@ rule build_temperature_profiles: rule build_central_heating_temperature_profiles: + message: + "Building central heating temperature profiles for {wildcards.clusters} clusters and {wildcards.planning_horizons} planning horizon" params: max_forward_temperature_central_heating_baseyear=config_provider( "sector", @@ -304,6 +326,8 @@ rule build_dh_areas: rule build_geothermal_heat_potential: + message: + "Building geothermal heat potential estimates for {wildcards.clusters} clusters" params: drop_leap_day=config_provider("enable", "drop_leap_day"), countries=config_provider("countries"), @@ -346,6 +370,8 @@ rule build_geothermal_heat_potential: rule build_ates_potentials: + message: + "Building aquifer thermal energy storage (ATES) potentials for {wildcards.clusters} clusters and {wildcards.planning_horizons} planning horizon" params: max_top_temperature=config_provider( "sector", @@ -624,6 +650,8 @@ rule build_sea_heat_potential: rule build_cop_profiles: + message: + "Building coefficient of performance (COP) profiles for {wildcards.clusters} clusters and {wildcards.planning_horizons} planning horizon" params: heat_pump_sink_T_decentral_heating=config_provider( "sector", "heat_pump_sink_T_individual_heating" @@ -665,6 +693,8 @@ rule build_cop_profiles: rule build_ptes_operations: + message: + "Building thermal energy storage operations profiles for {wildcards.clusters} clusters and {wildcards.planning_horizons} planning horizon" params: max_ptes_top_temperature=config_provider( "sector", @@ -708,6 +738,8 @@ rule build_ptes_operations: rule build_direct_heat_source_utilisation_profiles: + message: + "Building direct heat source utilization profiles for industrial applications for {wildcards.clusters} clusters and {wildcards.planning_horizons} planning horizon" params: direct_utilisation_heat_sources=config_provider( "sector", "district_heating", "direct_utilisation_heat_sources" @@ -739,6 +771,8 @@ rule build_direct_heat_source_utilisation_profiles: rule build_solar_thermal_profiles: + message: + "Building solar thermal generation profiles for {wildcards.clusters} clusters" params: snapshots=config_provider("snapshots"), drop_leap_day=config_provider("enable", "drop_leap_day"), @@ -761,6 +795,8 @@ rule build_solar_thermal_profiles: rule build_energy_totals: + message: + "Building energy totals" params: countries=config_provider("countries"), energy=config_provider("energy"), @@ -802,7 +838,7 @@ if (COUNTRY_HDD_DATASET := dataset_version("country_hdd"))["source"] in ["build" cutouts=["cutouts/europe-1940-2024-era5.nc"], country_shapes=resources("country_shapes.geojson"), output: - era5_hdd=f"{COUNTRY_HDD_DATASET["folder"]}/era5-HDD-per-country.csv", + era5_hdd=f"{COUNTRY_HDD_DATASET['folder']}/era5-HDD-per-country.csv", log: logs("build_country_hdd.log"), benchmark: @@ -814,8 +850,10 @@ if (COUNTRY_HDD_DATASET := dataset_version("country_hdd"))["source"] in ["build" rule build_heat_totals: + message: + "Building heat totals" input: - hdd=f"{COUNTRY_HDD_DATASET["folder"]}/era5-HDD-per-country.csv", + hdd=f"{COUNTRY_HDD_DATASET['folder']}/era5-HDD-per-country.csv", energy_totals=resources("energy_totals.csv"), output: heat_totals=resources("heat_totals.csv"), @@ -831,6 +869,8 @@ rule build_heat_totals: rule build_biomass_potentials: + message: + "Building biomass potential estimates for {wildcards.clusters} clusters and {wildcards.planning_horizons} planning horizon" params: biomass=config_provider("biomass"), input: @@ -861,6 +901,8 @@ rule build_biomass_potentials: rule build_biomass_transport_costs: + message: + "Building biomass transport cost" input: sc1="data/biomass_transport_costs_supplychain1.csv", sc2="data/biomass_transport_costs_supplychain2.csv", @@ -878,6 +920,8 @@ rule build_biomass_transport_costs: rule build_co2_sequestration_potentials: + message: + "Building CO2 sequestration potentials" input: storage_table=rules.retrieve_co2stop.output["storage_table"], storage_map=rules.retrieve_co2stop.output["storage_map"], @@ -899,6 +943,8 @@ rule build_co2_sequestration_potentials: rule build_clustered_co2_sequestration_potentials: + message: + "Clustering CO2 sequestration potentials for {wildcards.clusters} clusters" params: sequestration_potential=config_provider( "sector", "regional_co2_sequestration_potential" @@ -923,6 +969,8 @@ rule build_clustered_co2_sequestration_potentials: rule build_salt_cavern_potentials: + message: + "Building salt cavern potential for hydrogen storage for {wildcards.clusters} clusters" input: salt_caverns=rules.retrieve_h2_salt_caverns.output["geojson"], regions_onshore=resources("regions_onshore_base_s_{clusters}.geojson"), @@ -941,6 +989,8 @@ rule build_salt_cavern_potentials: rule build_ammonia_production: + message: + "Building ammonia production capacity and location data" input: usgs=rules.retrieve_nitrogen_statistics.output["xlsx"], output: @@ -957,6 +1007,8 @@ rule build_ammonia_production: rule build_industry_sector_ratios: + message: + "Building industry sector energy demand ratios" params: industry=config_provider("industry"), ammonia=config_provider("sector", "ammonia", default=False), @@ -977,6 +1029,8 @@ rule build_industry_sector_ratios: rule build_industry_sector_ratios_intermediate: + message: + "Building intermediate industry sector ratios for {wildcards.planning_horizons} planning horizon" params: industry=config_provider("industry"), input: @@ -1003,6 +1057,8 @@ rule build_industry_sector_ratios_intermediate: rule build_industrial_production_per_country: + message: + "Building industrial production statistics per country" params: industry=config_provider("industry"), countries=config_provider("countries"), @@ -1027,6 +1083,8 @@ rule build_industrial_production_per_country: rule build_industrial_production_per_country_tomorrow: + message: + "Building future industrial production projections for {wildcards.planning_horizons} planning horizon" params: industry=config_provider("industry"), input: @@ -1053,6 +1111,8 @@ rule build_industrial_production_per_country_tomorrow: rule build_industrial_distribution_key: + message: + "Building industrial activity distribution mapping key for {wildcards.clusters} clusters" params: hotmaps_locate_missing=config_provider( "industry", "hotmaps_locate_missing", default=False @@ -1082,6 +1142,8 @@ rule build_industrial_distribution_key: rule build_industrial_production_per_node: + message: + "Distributing industrial production to network nodes for {wildcards.clusters} clusters and {wildcards.planning_horizons} planning horizon" input: industrial_distribution_key=resources( "industrial_distribution_key_base_s_{clusters}.csv" @@ -1109,6 +1171,8 @@ rule build_industrial_production_per_node: rule build_industrial_energy_demand_per_node: + message: + "Building industrial energy demand per network node for {wildcards.clusters} clusters and {wildcards.planning_horizons} planning horizon" input: industry_sector_ratios=resources( "industry_sector_ratios_{planning_horizons}.csv" @@ -1141,6 +1205,8 @@ rule build_industrial_energy_demand_per_node: rule build_industrial_energy_demand_per_country_today: + message: + "Building current industrial energy demand by country" params: countries=config_provider("countries"), industry=config_provider("industry"), @@ -1167,6 +1233,8 @@ rule build_industrial_energy_demand_per_country_today: rule build_industrial_energy_demand_per_node_today: + message: + "Building current industrial energy demand per network node for {wildcards.clusters} clusters" input: industrial_distribution_key=resources( "industrial_distribution_key_base_s_{clusters}.csv" @@ -1190,6 +1258,8 @@ rule build_industrial_energy_demand_per_node_today: rule build_retro_cost: + message: + "Building retrofitting cost estimates for building efficiency improvements for {wildcards.clusters} clusters" params: retrofitting=config_provider("sector", "retrofitting"), countries=config_provider("countries"), @@ -1218,6 +1288,8 @@ rule build_retro_cost: rule build_population_weighted_energy_totals: + message: + "Building population-weighted energy demand totals for {wildcards.clusters} clusters" params: snapshots=config_provider("snapshots"), drop_leap_day=config_provider("enable", "drop_leap_day"), @@ -1238,6 +1310,8 @@ rule build_population_weighted_energy_totals: rule build_shipping_demand: + message: + "Building shipping fuel demand projections for {wildcards.clusters} clusters" input: ports=rules.retrieve_attributed_ports.output["json"], scope=resources("europe_shape.geojson"), @@ -1289,6 +1363,8 @@ if MOBILITY_PROFILES_DATASET["source"] in ["build"]: rule build_transport_demand: + message: + "Building transport energy demand profiles for {wildcards.clusters} clusters" params: snapshots=config_provider("snapshots"), drop_leap_day=config_provider("enable", "drop_leap_day"), @@ -1301,8 +1377,8 @@ rule build_transport_demand: "pop_weighted_energy_totals_s_{clusters}.csv" ), transport_data=resources("transport_data.csv"), - traffic_data_KFZ=f"{MOBILITY_PROFILES_DATASET["folder"]}/kfz.csv", - traffic_data_Pkw=f"{MOBILITY_PROFILES_DATASET["folder"]}/pkw.csv", + traffic_data_KFZ=f"{MOBILITY_PROFILES_DATASET['folder']}/kfz.csv", + traffic_data_Pkw=f"{MOBILITY_PROFILES_DATASET['folder']}/pkw.csv", temp_air_total=resources("temp_air_total_base_s_{clusters}.nc"), output: transport_demand=resources("transport_demand_s_{clusters}.csv"), @@ -1321,6 +1397,8 @@ rule build_transport_demand: rule build_district_heat_share: + message: + "Building district heating penetration share data for {wildcards.clusters} clusters and {wildcards.planning_horizons} planning horizon" params: sector=config_provider("sector"), energy_totals_year=config_provider("energy", "energy_totals_year"), @@ -1343,6 +1421,8 @@ rule build_district_heat_share: rule build_existing_heating_distribution: + message: + "Building existing heating technology distribution data for {wildcards.clusters} clusters and {wildcards.planning_horizons} planning horizon" params: baseyear=config_provider("scenario", "planning_horizons", 0), sector=config_provider("sector"), @@ -1378,6 +1458,8 @@ rule build_existing_heating_distribution: rule time_aggregation: + message: + "Performing time series aggregation for temporal resolution reduction for {wildcards.clusters} clusters and {wildcards.opts} electric options and {wildcards.sector_opts} sector options" params: time_resolution=config_provider("clustering", "temporal"), drop_leap_day=config_provider("enable", "drop_leap_day"), @@ -1418,6 +1500,8 @@ def input_profile_offwind(w): rule build_egs_potentials: + message: + "Building enhanced geothermal system (EGS) potential estimates for {wildcards.clusters} clusters" params: snapshots=config_provider("snapshots"), drop_leap_day=config_provider("enable", "drop_leap_day"), @@ -1463,6 +1547,8 @@ def input_heat_source_power(w): rule prepare_sector_network: + message: + "Preparing integrated sector-coupled energy network for {wildcards.clusters} clusters, {wildcards.planning_horizons} planning horizon, {wildcards.opts} electric options and {wildcards.sector_opts} sector options" params: time_resolution=config_provider("clustering", "temporal", "resolution_sector"), co2_budget=config_provider("co2_budget"), @@ -1546,7 +1632,7 @@ rule prepare_sector_network: "biomass_potentials_s_{clusters}_{planning_horizons}.csv" ), costs=lambda w: ( - resources(f"costs_{config_provider("costs", "year")(w)}_processed.csv") + resources(f"costs_{config_provider('costs', 'year')(w)}_processed.csv") if config_provider("foresight")(w) == "overnight" else resources("costs_{planning_horizons}_processed.csv") ), diff --git a/rules/collect.smk b/rules/collect.smk index ee0f90f8a..dd805620c 100644 --- a/rules/collect.smk +++ b/rules/collect.smk @@ -31,6 +31,8 @@ rule process_costs: rule cluster_networks: + message: + "Collecting clustered network files" input: expand( resources("networks/base_s_{clusters}.nc"), @@ -40,6 +42,8 @@ rule cluster_networks: rule prepare_elec_networks: + message: + "Collecting prepared electricity network files" input: expand( resources("networks/base_s_{clusters}_elec_{opts}.nc"), @@ -49,6 +53,8 @@ rule prepare_elec_networks: rule prepare_sector_networks: + message: + "Collecting prepared sector-coupled network files" input: expand( resources( @@ -60,6 +66,8 @@ rule prepare_sector_networks: rule solve_elec_networks: + message: + "Collecting solved electricity network files" input: expand( RESULTS + "networks/base_s_{clusters}_elec_{opts}.nc", @@ -69,6 +77,8 @@ rule solve_elec_networks: rule solve_sector_networks: + message: + "Collecting solved sector-coupled network files" input: expand( RESULTS @@ -79,6 +89,8 @@ rule solve_sector_networks: rule solve_sector_networks_perfect: + message: + "Collecting solved sector-coupled network files with perfect foresight" input: expand( RESULTS @@ -105,6 +117,8 @@ def balance_map_paths(kind, w): rule plot_balance_maps: + message: + "Plotting energy balance maps" input: static=lambda w: balance_map_paths("static", w), interactive=lambda w: balance_map_paths("interactive", w), @@ -120,7 +134,9 @@ rule plot_balance_maps_interactive: lambda w: balance_map_paths("interactive", w), -rule plot_statistics: +rule plot_power_networks_clustered: + message: + "Plotting clustered power network topology" input: [ expand( diff --git a/rules/common.smk b/rules/common.smk index 49d494792..137980f89 100644 --- a/rules/common.smk +++ b/rules/common.smk @@ -90,10 +90,8 @@ def load_data_versions(file_path): comment="#", ) - # Turn 'tags' column from string representation of list to individual columns - data_versions["tags"] = data_versions["tags"].apply( - lambda x: json.loads(x.replace("'", '"')) - ) + # Turn space-separated tags into individual columns + data_versions["tags"] = data_versions["tags"].str.split() exploded = data_versions.explode("tags") dummies = pd.get_dummies(exploded["tags"], dtype=bool) tags_matrix = dummies.groupby(dummies.index).max() diff --git a/rules/postprocess.smk b/rules/postprocess.smk index 7fc483481..ca1c434b5 100644 --- a/rules/postprocess.smk +++ b/rules/postprocess.smk @@ -6,6 +6,8 @@ if config["foresight"] != "perfect": rule plot_base_network: + message: + "Plotting base power network" params: plotting=config_provider("plotting"), input: @@ -22,6 +24,8 @@ if config["foresight"] != "perfect": "../scripts/plot_base_network.py" rule plot_power_network_clustered: + message: + "Plotting clustered power network for {wildcards.clusters} clusters" params: plotting=config_provider("plotting"), input: @@ -38,6 +42,8 @@ if config["foresight"] != "perfect": "../scripts/plot_power_network_clustered.py" rule plot_power_network: + message: + "Plotting power network for {wildcards.clusters} clusters, {wildcards.opts} electric options, {wildcards.sector_opts} sector options and {wildcards.planning_horizons} planning horizons" params: plotting=config_provider("plotting"), transmission_limit=config_provider("electricity", "transmission_limit"), @@ -63,6 +69,8 @@ if config["foresight"] != "perfect": "../scripts/plot_power_network.py" rule plot_hydrogen_network: + message: + "Plotting hydrogen network for {wildcards.clusters} clusters, {wildcards.opts} electric options, {wildcards.sector_opts} sector options and {wildcards.planning_horizons} planning horizons" params: plotting=config_provider("plotting"), foresight=config_provider("foresight"), @@ -88,6 +96,8 @@ if config["foresight"] != "perfect": "../scripts/plot_hydrogen_network.py" rule plot_gas_network: + message: + "Plotting methane network for {wildcards.clusters} clusters, {wildcards.opts} electric options, {wildcards.sector_opts} sector options and {wildcards.planning_horizons} planning horizon" params: plotting=config_provider("plotting"), input: @@ -112,6 +122,8 @@ if config["foresight"] != "perfect": "../scripts/plot_gas_network.py" rule plot_balance_map: + message: + "Plotting balance map for {wildcards.clusters} clusters, {wildcards.opts} electric options, {wildcards.sector_opts} sector options, {wildcards.planning_horizons} planning horizons and {wildcards.carrier} carrier" params: plotting=config_provider("plotting"), settings=lambda w: config_provider("plotting", "balance_map", w.carrier), @@ -217,6 +229,8 @@ if config["foresight"] == "perfect": } rule plot_power_network_perfect: + message: + "Plotting power network with perfect foresight for {wildcards.clusters} clusters, {wildcards.opts} electric options and {wildcards.sector_opts} sector options" params: plotting=config_provider("plotting"), input: @@ -233,6 +247,8 @@ if config["foresight"] == "perfect": rule make_summary: + message: + "Creating optimization results summary statistics" input: network=RESULTS + "networks/base_s_{clusters}_{opts}_{sector_opts}_{planning_horizons}.nc", @@ -281,6 +297,8 @@ rule make_summary: rule make_global_summary: + message: + "Creating global summary of optimization results for all scenarios" params: scenario=config_provider("scenario"), RDIR=RDIR, @@ -396,6 +414,8 @@ rule make_global_summary: rule make_cumulative_costs: + message: + "Calculating cumulative costs over time horizon" params: scenario=config_provider("scenario"), input: @@ -414,6 +434,8 @@ rule make_cumulative_costs: rule plot_summary: + message: + "Plotting summary statistics and results" params: countries=config_provider("countries"), planning_horizons=config_provider("scenario", "planning_horizons"), @@ -443,6 +465,8 @@ rule plot_summary: rule plot_balance_timeseries: + message: + "Plotting energy balance time series for {wildcards.clusters} clusters, {wildcards.opts} electric options, {wildcards.sector_opts} sector options and {wildcards.planning_horizons} planning horizons" params: plotting=config_provider("plotting"), snapshots=config_provider("snapshots"), @@ -470,6 +494,8 @@ rule plot_balance_timeseries: rule plot_heatmap_timeseries: + message: + "Plotting heatmap time series visualization for {wildcards.clusters} clusters, {wildcards.opts} electric options, {wildcards.sector_opts} sector options and {wildcards.planning_horizons} planning horizons" params: plotting=config_provider("plotting"), snapshots=config_provider("snapshots"), @@ -605,6 +631,8 @@ rule plot_statistics_comparison: rule plot_base_statistics: + message: + "Plotting base scenario statistics for {wildcards.clusters} clusters and {wildcards.opts} electric options" params: plotting=config_provider("plotting"), barplots=STATISTICS_BARPLOTS, diff --git a/rules/retrieve.smk b/rules/retrieve.smk index 110a3f0b6..c5c244f2c 100755 --- a/rules/retrieve.smk +++ b/rules/retrieve.smk @@ -28,6 +28,8 @@ if (EUROSTAT_BALANCES_DATASET := dataset_version("eurostat_balances"))["source"] ]: rule retrieve_eurostat_balances: + message: + "Retrieving Eurostat balances data" input: zip_file=storage(EUROSTAT_BALANCES_DATASET["url"]), output: @@ -48,6 +50,8 @@ if ( ]: rule retrieve_eurostat_household_balances: + message: + "Retrieving Eurostat household balances data" input: csv=storage(EUROSTAT_HOUSEHOLD_BALANCES_DATASET["url"]), output: @@ -62,6 +66,8 @@ if (NUTS3_POPULATION_DATASET := dataset_version("nuts3_population"))["source"] i ]: rule retrieve_nuts3_population: + message: + "Retrieving NUTS3 population data" input: gz=storage(NUTS3_POPULATION_DATASET["url"]), output: @@ -74,6 +80,8 @@ if (NUTS3_POPULATION_DATASET := dataset_version("nuts3_population"))["source"] i if (CORINE_DATASET := dataset_version("corine"))["source"] in ["archive"]: rule retrieve_corine: + message: + "Retrieving Corine land cover data" input: zip_file=storage(CORINE_DATASET["url"]), output: @@ -90,8 +98,10 @@ if (CORINE_DATASET := dataset_version("corine"))["source"] in ["archive"]: elif (CORINE_DATASET := dataset_version("corine"))["source"] in ["primary"]: rule retrieve_corine: + message: + "Retrieving Corine land cover data" params: - apikey=os.environ.get("CORINE_API_TOKEN", config["secrets"]["corine"]), + apikey=os.environ.get("CORINE_API_TOKEN", ""), output: zip=f"{CORINE_DATASET['folder']}/corine.zip", tif_file=f"{CORINE_DATASET['folder']}/corine.tif", @@ -109,6 +119,8 @@ if (H2_SALT_CAVERNS_DATASET := dataset_version("h2_salt_caverns"))["source"] in ]: rule retrieve_h2_salt_caverns: + message: + "Retrieving H2 salt caverns data" input: geojson=storage(H2_SALT_CAVERNS_DATASET["url"]), output: @@ -123,6 +135,8 @@ if (GDP_PER_CAPITA_DATASET := dataset_version("gdp_per_capita"))["source"] in [ ]: rule retrieve_gdp_per_capita: + message: + "Retrieving GDP per capita data" input: gdp=storage(GDP_PER_CAPITA_DATASET["url"]), output: @@ -138,6 +152,8 @@ if (POPULATION_COUNT_DATASET := dataset_version("population_count"))["source"] i ]: rule retrieve_population_count: + message: + "Retrieving population count data" input: tif=storage(POPULATION_COUNT_DATASET["url"]), output: @@ -163,6 +179,8 @@ if (GHG_EMISSIONS_DATASET := dataset_version("ghg_emissions"))["source"] in [ ]: rule retrieve_ghg_emissions: + message: + "Retrieving GHG emissions data" input: ghg=storage(GHG_EMISSIONS_DATASET["url"]), output: @@ -190,6 +208,8 @@ if (GHG_EMISSIONS_DATASET := dataset_version("ghg_emissions"))["source"] in [ if (GEBCO_DATASET := dataset_version("gebco"))["source"] in ["archive", "primary"]: rule retrieve_gebco: + message: + "Retrieving GEBCO bathymetry data" input: storage(GEBCO_DATASET["url"]), output: @@ -223,6 +243,8 @@ if (ATTRIBUTED_PORTS_DATASET := dataset_version("attributed_ports"))["source"] i ]: rule retrieve_attributed_ports: + message: + "Retrieving attributed ports data" input: json=storage(ATTRIBUTED_PORTS_DATASET["url"]), output: @@ -238,6 +260,8 @@ if (JRC_IDEES_DATASET := dataset_version("jrc_idees"))["source"] in [ ]: rule retrieve_jrc_idees: + message: + "Retrieving JRC IDEES data" input: zip_file=storage(JRC_IDEES_DATASET["url"]), output: @@ -255,6 +279,8 @@ if (EU_NUTS2013_DATASET := dataset_version("eu_nuts2013"))["source"] in [ ]: rule retrieve_eu_nuts_2013: + message: + "Retrieving EU NUTS 2013 data" input: shapes=storage(EU_NUTS2013_DATASET["url"]), output: @@ -275,6 +301,8 @@ if (EU_NUTS2021_DATASET := dataset_version("eu_nuts2021"))["source"] in [ ]: rule retrieve_eu_nuts_2021: + message: + "Retrieving EU NUTS 2021 data" input: shapes=storage(EU_NUTS2021_DATASET["url"]), output: @@ -291,17 +319,62 @@ if (EU_NUTS2021_DATASET := dataset_version("eu_nuts2021"))["source"] in [ unpack_archive(output["zip_file"], Path(output.shapes_level_3).parent) -rule retrieve_bidding_zones: - output: - file_entsoepy="data/busshapes/bidding_zones_entsoepy.geojson", - file_electricitymaps="data/busshapes/bidding_zones_electricitymaps.geojson", - log: - "logs/retrieve_bidding_zones.log", - resources: - mem_mb=1000, - retries: 2 - script: - "../scripts/retrieve_bidding_zones.py" +if ( + BIDDING_ZONES_ELECTRICITYMAPS_DATASET := dataset_version( + "bidding_zones_electricitymaps" + ) +)["source"] in ["primary", "archive"]: + + rule retrieve_bidding_zones_electricitymaps: + input: + geojson=storage(BIDDING_ZONES_ELECTRICITYMAPS_DATASET["url"]), + output: + geojson=f"{BIDDING_ZONES_ELECTRICITYMAPS_DATASET['folder']}/bidding_zones_electricitymaps.geojson", + log: + "logs/retrieve_bidding_zones_electricitymaps.log", + resources: + mem_mb=1000, + retries: 2 + run: + copy2(input["geojson"], output["geojson"]) + + +if (BIDDING_ZONES_ENTSOEPY_DATASET := dataset_version("bidding_zones_entsoepy"))[ + "source" +] in ["primary", "archive"]: + + rule retrieve_bidding_zones_entsoepy: + output: + geojson=f"{BIDDING_ZONES_ENTSOEPY_DATASET['folder']}/bidding_zones_entsoepy.geojson", + log: + "logs/retrieve_bidding_zones_entsoepy.log", + resources: + mem_mb=1000, + retries: 2 + run: + import entsoe + import geopandas as gpd + from urllib.error import HTTPError, URLError + + logger.info("Downloading entsoe-py zones...") + gdfs: list[gpd.GeoDataFrame] = [] + url = f"{BIDDING_ZONES_ENTSOEPY_DATASET['url']}" + for area in entsoe.Area: + name = area.name + try: + file_url = f"{url}/{name}.geojson" + gdfs.append(gpd.read_file(file_url)) + except HTTPError as e: + logger.debug(f"Area file not available for {name}: {e}") + continue + except (URLError, TimeoutError) as e: + raise Exception(f"Network error retrieving {name}: {e}") + shapes = pd.concat(gdfs, ignore_index=True) # type: ignore + + logger.info("Downloading entsoe-py zones... Done") + + shapes.to_file(output.geojson) + if (CUTOUT_DATASET := dataset_version("cutout"))["source"] in [ @@ -309,8 +382,10 @@ if (CUTOUT_DATASET := dataset_version("cutout"))["source"] in [ ]: rule retrieve_cutout: + message: + "Retrieving cutout data for {wildcards.cutout}" input: - storage(CUTOUT_DATASET["url"] + "/files/{cutout}.nc"), + storage(CUTOUT_DATASET["url"] + "/{cutout}.nc"), output: CUTOUT_DATASET["folder"] + "/{cutout}.nc", log: @@ -327,10 +402,12 @@ if (COUNTRY_RUNOFF_DATASET := dataset_version("country_runoff"))["source"] in [ ]: rule retrieve_country_runoff: + message: + "Retrieving country runoff data" input: storage(COUNTRY_RUNOFF_DATASET["url"]), output: - era5_runoff=f"{COUNTRY_RUNOFF_DATASET["folder"]}/era5-runoff-per-country.csv", + era5_runoff=f"{COUNTRY_RUNOFF_DATASET['folder']}/era5-runoff-per-country.csv", run: copy2(input[0], output[0]) @@ -338,10 +415,12 @@ if (COUNTRY_RUNOFF_DATASET := dataset_version("country_runoff"))["source"] in [ if (COUNTRY_HDD_DATASET := dataset_version("country_hdd"))["source"] in ["archive"]: rule retrieve_country_hdd: + message: + "Retrieving country heating degree days data" input: storage(COUNTRY_HDD_DATASET["url"]), output: - era5_runoff=f"{COUNTRY_HDD_DATASET["folder"]}/era5-HDD-per-country.csv", + era5_runoff=f"{COUNTRY_HDD_DATASET['folder']}/era5-HDD-per-country.csv", run: copy2(input[0], output[0]) @@ -351,6 +430,8 @@ if (COSTS_DATASET := dataset_version("costs"))["source"] in [ ]: rule retrieve_cost_data: + message: + "Retrieving cost data for {wildcards.planning_horizons}" input: costs=storage(COSTS_DATASET["url"] + "/costs_{planning_horizons}.csv"), output: @@ -364,6 +445,8 @@ if (POWERPLANTS_DATASET := dataset_version("powerplants"))["source"] in [ ]: rule retrieve_powerplants: + message: + "Retrieving powerplants data" input: powerplants=storage(POWERPLANTS_DATASET["url"]), output: @@ -378,6 +461,8 @@ if (SCIGRID_GAS_DATASET := dataset_version("scigrid_gas"))["source"] in [ ]: rule retrieve_gas_infrastructure_data: + message: + "Retrieving SciGRID gas infrastructure data" input: zip_file=storage(SCIGRID_GAS_DATASET["url"]), output: @@ -392,6 +477,8 @@ if (SCIGRID_GAS_DATASET := dataset_version("scigrid_gas"))["source"] in [ rule retrieve_electricity_demand: + message: + "Retrieving electricity demand data" params: versions=["2019-06-05", "2020-10-06"], output: @@ -415,6 +502,8 @@ if ( ]: rule retrieve_synthetic_electricity_demand: + message: + "Retrieving synthetic electricity demand data" input: csv=storage(SYNTHETIC_ELECTRICITY_DEMAND_DATASET["url"]), output: @@ -430,6 +519,8 @@ if (SHIP_RASTER_DATASET := dataset_version("ship_raster"))["source"] in [ ]: rule retrieve_ship_raster: + message: + "Retrieving shipping raster data" input: zip_file=storage(SHIP_RASTER_DATASET["url"]), output: @@ -449,6 +540,8 @@ if (ENSPRESO_BIOMASS_DATASET := dataset_version("enspreso_biomass"))["source"] i ]: rule retrieve_enspreso_biomass: + message: + "Retrieving ENSPRESO biomass data" input: xlsx=storage(ENSPRESO_BIOMASS_DATASET["url"]), output: @@ -466,6 +559,8 @@ if (HOTMAPS_INDUSTRIAL_SITES := dataset_version("hotmaps_industrial_sites"))[ ]: rule retrieve_hotmaps_industrial_sites: + message: + "Retrieving Hotmaps industrial sites" input: csv=storage(HOTMAPS_INDUSTRIAL_SITES["url"]), output: @@ -483,6 +578,8 @@ if (NITROGEN_STATISTICS_DATASET := dataset_version("nitrogen_statistics"))[ ]: rule retrieve_nitrogen_statistics: + message: + "Retrieving nitrogen statistics data" input: xlsx=storage(NITROGEN_STATISTICS_DATASET["url"]), output: @@ -499,6 +596,8 @@ if (COPERNICUS_LAND_COVER_DATASET := dataset_version("copernicus_land_cover"))[ # Downloading Copernicus Global Land Cover for land cover and land use: # Website: https://land.copernicus.eu/global/products/lc rule download_copernicus_land_cover: + message: + "Retrieving Copernicus land cover data" input: tif=storage(COPERNICUS_LAND_COVER_DATASET["url"]), output: @@ -515,6 +614,8 @@ if (LUISA_LAND_COVER_DATASET := dataset_version("luisa_land_cover"))["source"] i # Downloading LUISA Base Map for land cover and land use: # Website: https://ec.europa.eu/jrc/en/luisa rule retrieve_luisa_land_cover: + message: + "Retrieving LUISA land cover data" input: tif=storage(LUISA_LAND_COVER_DATASET["url"]), output: @@ -526,6 +627,8 @@ if (LUISA_LAND_COVER_DATASET := dataset_version("luisa_land_cover"))["source"] i if (EEZ_DATASET := dataset_version("eez"))["source"] in ["primary"]: rule retrieve_eez: + message: + "Retrieving EEZ data" output: zip_file=f"{EEZ_DATASET['folder']}/World_EEZ_{EEZ_DATASET['version']}_LR.zip", gpkg=f"{EEZ_DATASET['folder']}/World_EEZ_{EEZ_DATASET['version']}_LR/eez_{EEZ_DATASET['version'].split('_')[0]}_lowres.gpkg", @@ -558,6 +661,8 @@ if (EEZ_DATASET := dataset_version("eez"))["source"] in ["primary"]: elif (EEZ_DATASET := dataset_version("eez"))["source"] in ["archive"]: rule retrieve_eez: + message: + "Retrieving EEZ data" input: zip_file=storage( EEZ_DATASET["url"], @@ -577,6 +682,8 @@ if (WB_URB_POP_DATASET := dataset_version("worldbank_urban_population"))["source ]: rule retrieve_worldbank_urban_population: + message: + "Retrieving World Bank urban population data" input: zip=storage(WB_URB_POP_DATASET["url"]), output: @@ -603,6 +710,8 @@ if (CO2STOP_DATASET := dataset_version("co2stop"))["source"] in [ ]: rule retrieve_co2stop: + message: + "Retrieving CO2STOP data" input: zip_file=storage(CO2STOP_DATASET["url"]), output: @@ -628,6 +737,8 @@ if (GEM_EUROPE_GAS_TRACKER_DATASET := dataset_version("gem_europe_gas_tracker")) ]: rule retrieve_gem_europe_gas_tracker: + message: + "Retrieving GEM Europe Gas Tracker data" input: xlsx=storage(GEM_EUROPE_GAS_TRACKER_DATASET["url"]), output: @@ -642,6 +753,8 @@ if (GEM_GSPT_DATASET := dataset_version("gem_gspt"))["source"] in [ ]: rule retrieve_gem_steel_plant_tracker: + message: + "Retrieving GEM Global Steel Plant Tracker data" input: xlsx=storage(GEM_GSPT_DATASET["url"]), output: @@ -658,6 +771,8 @@ if (BFS_ROAD_VEHICLE_STOCK_DATASET := dataset_version("bfs_road_vehicle_stock")) ]: rule retrieve_bfs_road_vehicle_stock: + message: + "Retrieving BFS road vehicle stock data" input: csv=storage(BFS_ROAD_VEHICLE_STOCK_DATASET["url"]), output: @@ -674,6 +789,8 @@ if (BFS_GDP_AND_POPULATION_DATASET := dataset_version("bfs_gdp_and_population")) ]: rule retrieve_bfs_gdp_and_population: + message: + "Retrieving BFS GDP and population data" input: xlsx=storage(BFS_GDP_AND_POPULATION_DATASET["url"]), output: @@ -729,6 +846,8 @@ if (WDPA_DATASET := dataset_version("wdpa"))["source"] in [ # extract the main zip and then merge the contained 3 zipped shapefiles # Website: https://www.protectedplanet.net/en/thematic-areas/wdpa rule retrieve_wdpa: + message: + "Downloading protected area database from WDPA" input: zip_file=storage(get_wdpa_url(WDPA_DATASET)), output: @@ -739,6 +858,12 @@ if (WDPA_DATASET := dataset_version("wdpa"))["source"] in [ copy2(input["zip_file"], output["zip_file"]) unpack_archive(output["zip_file"], output_folder) + # Extract {bYYYY} from the input file / URL + bYYYY = re.search( + r"WDPA_(\w{3}\d{4})_Public_shp.zip", + input["zip_file"], + ).group(1) + for i in range(3): # vsizip is special driver for directly working with zipped shapefiles in ogr2ogr layer_path = ( @@ -758,6 +883,8 @@ if (WDPA_MARINE_DATASET := dataset_version("wdpa_marine"))["source"] in [ # 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 + message: + "Downloading Marine protected area database from WDPA" input: zip_file=storage(get_wdpa_url(WDPA_MARINE_DATASET)), output: @@ -768,6 +895,12 @@ if (WDPA_MARINE_DATASET := dataset_version("wdpa_marine"))["source"] in [ copy2(input["zip_file"], output["zip_file"]) unpack_archive(output["zip_file"], output_folder) + # Extract {bYYYY} from the input file / URL + bYYYY = re.search( + r"WDPA_WDOECM_(\w{3}\d{4})_Public_marine_shp.zip", + input["zip_file"], + ).group(1) + for i in range(3): # vsizip is special driver for directly working with zipped shapefiles in ogr2ogr layer_path = f"/vsizip/{output_folder}/WDPA_WDOECM_{bYYYY}_Public_marine_shp_{i}.zip" @@ -779,6 +912,8 @@ if (WDPA_MARINE_DATASET := dataset_version("wdpa_marine"))["source"] in [ # Versioning not implemented as the dataset is used only for validation # License - (c) EEX AG, all rights reserved. Personal copy for non-commercial use permitted rule retrieve_monthly_co2_prices: + message: + "Retrieving monthly CO2 prices data for validation" input: storage( "https://public.eex-group.com/eex/eua-auction-report/emission-spot-primary-market-auction-report-2019-data.xls", @@ -797,6 +932,8 @@ rule retrieve_monthly_co2_prices: # Versioning not implemented as the dataset is used only for validation # License - custom; no restrictions on use and redistribution, attribution required rule retrieve_monthly_fuel_prices: + message: + "Retrieving monthly fuel prices data for validation" output: "data/validation/energy-price-trends-xlsx-5619002.xlsx", log: @@ -811,6 +948,8 @@ rule retrieve_monthly_fuel_prices: if (TYDNP_DATASET := dataset_version("tyndp"))["source"] in ["primary", "archive"]: rule retrieve_tyndp: + message: + "Retrieving TYNDP network topology data" input: line_data=storage(TYDNP_DATASET["url"] + "/Line-data.zip"), nodes=storage(TYDNP_DATASET["url"] + "/Nodes.zip"), @@ -849,9 +988,11 @@ if OSM_DATASET["source"] in ["archive"]: ] rule retrieve_osm_archive: + message: + "Retrieving OSM archive data" input: **{ - file: storage(f"{OSM_DATASET['url']}/files/{file}") + file: storage(f"{OSM_DATASET['url']}/{file}") for file in OSM_ARCHIVE_FILES }, output: @@ -877,6 +1018,8 @@ elif OSM_DATASET["source"] == "build": ] rule retrieve_osm_data_raw: + message: + "Retrieving OSM raw data for {wildcards.country}" params: overpass_api=config_provider("overpass_api"), output: @@ -904,10 +1047,12 @@ elif OSM_DATASET["source"] == "build": if (NATURA_DATASET := dataset_version("natura"))["source"] in ["archive"]: rule retrieve_natura: + message: + "Retrieving Natura 2000 raster data" input: storage(NATURA_DATASET["url"]), output: - f"{NATURA_DATASET["folder"]}/natura.tiff", + f"{NATURA_DATASET['folder']}/natura.tiff", log: "logs/retrieve_natura.log", run: @@ -916,13 +1061,15 @@ if (NATURA_DATASET := dataset_version("natura"))["source"] in ["archive"]: elif NATURA_DATASET["source"] == "build": rule build_natura_raster: + message: + "Building Natura 2000 raster data" input: online=storage(NATURA_DATASET["url"]), cutout=lambda w: input_cutout(w), output: - zip=f"{NATURA_DATASET["folder"]}/raw/natura.zip", - raw=directory(f"{NATURA_DATASET["folder"]}/raw"), - raster=f"{NATURA_DATASET["folder"]}/natura.tiff", + zip=f"{NATURA_DATASET['folder']}/raw/natura.zip", + raw=directory(f"{NATURA_DATASET['folder']}/raw"), + raster=f"{NATURA_DATASET['folder']}/natura.tiff", resources: mem_mb=5000, log: @@ -936,8 +1083,10 @@ if (OSM_BOUNDARIES_DATASET := dataset_version("osm_boundaries"))["source"] in [ ]: rule retrieve_osm_boundaries: + message: + "Retrieving OSM admin boundaries for {wildcards.country}" output: - json=f"{OSM_BOUNDARIES_DATASET["folder"]}/{country}_adm1.json", + json=f"{OSM_BOUNDARIES_DATASET['folder']}/{country}_adm1.json", log: "logs/retrieve_osm_boundaries_{country}_adm1.log", threads: 1 @@ -949,6 +1098,8 @@ elif (OSM_BOUNDARIES_DATASET := dataset_version("osm_boundaries"))["source"] in ]: rule retrieve_osm_boundaries: + message: + "Retrieving OSM admin boundaries data" input: storage( f"{OSM_BOUNDARIES_DATASET['url']}", @@ -972,6 +1123,8 @@ if ( )["source"] in ["primary", "archive"]: rule retrieve_geothermal_heat_utilisation_potentials: + message: + "Retrieving geothermal heat utilisation potentials" input: isi_heat_potentials=storage( GEOTHERMAL_HEAT_UTILISATION_POTENTIALS_DATASET["url"] @@ -992,6 +1145,8 @@ if (LAU_REGIONS_DATASET := dataset_version("lau_regions"))["source"] in [ ]: rule retrieve_lau_regions: + message: + "Retrieving Local Administrative Units and Administation Unit regions" input: lau_regions=storage(LAU_REGIONS_DATASET["url"]), output: @@ -1004,8 +1159,11 @@ if (LAU_REGIONS_DATASET := dataset_version("lau_regions"))["source"] in [ copy2(input["lau_regions"], output["zip"]) rule retrieve_seawater_temperature: + message: + "Retrieving seawater temperature data for {wildcards.year}" params: default_cutout=config_provider("atlite", "default_cutout"), + test_data_url=dataset_version("seawater_temperature")["url"], output: seawater_temperature="data/seawater_temperature_{year}.nc", log: @@ -1016,6 +1174,8 @@ if (LAU_REGIONS_DATASET := dataset_version("lau_regions"))["source"] in [ "../scripts/retrieve_seawater_temperature.py" rule retrieve_hera_data_test_cutout: + message: + "Retrieving HERA test cutout data" input: hera_data_url=storage( f"https://zenodo.org/records/15828866/files/hera_be_2013-03-01_to_2013-03-08.zip" @@ -1034,6 +1194,8 @@ if (LAU_REGIONS_DATASET := dataset_version("lau_regions"))["source"] in [ unpack_archive(input[0], params.folder) rule retrieve_hera_data: + message: + "Retrieving HERA data for {wildcards.year}" input: river_discharge=storage( "https://jeodpp.jrc.ec.europa.eu/ftp/jrc-opendata/CEMS-EFAS/HERA/VER1-0/Data/NetCDF/river_discharge/dis.HERA{year}.nc" @@ -1061,6 +1223,8 @@ if (JRC_ARDECO_DATASET := dataset_version("jrc_ardeco"))["source"] in [ ]: rule retrieve_jrc_ardeco: + message: + "Retrieving JRC ARDECO data" input: ardeco_gdp=storage( f"{JRC_ARDECO_DATASET['url']}/SUVGDP?versions=2021&unit=EUR&format=csv-table" @@ -1079,6 +1243,8 @@ if (JRC_ARDECO_DATASET := dataset_version("jrc_ardeco"))["source"] in [ elif (JRC_ARDECO_DATASET := dataset_version("jrc_ardeco"))["source"] in ["archive"]: rule retrieve_jrc_ardeco: + message: + "Retrieving JRC ARDECO data" input: ardeco_gdp=storage( f"{JRC_ARDECO_DATASET['url']}/ARDECO-SUVGDP.2021.table.csv" @@ -1101,6 +1267,8 @@ if (AQUIFER_DATA_DATASET := dataset_version("aquifer_data"))["source"] in [ ]: rule retrieve_aquifer_data_bgr: + message: + "Retrieving BGR aquifer data" input: zip_file=storage(AQUIFER_DATA_DATASET["url"]), output: @@ -1131,6 +1299,8 @@ if (DH_AREAS_DATASET := dataset_version("dh_areas"))["source"] in [ ]: rule retrieve_dh_areas: + message: + "Retrieving District Heating areas" input: dh_areas=storage(DH_AREAS_DATASET["url"]), output: @@ -1146,12 +1316,14 @@ if (MOBILITY_PROFILES_DATASET := dataset_version("mobility_profiles"))["source"] ]: rule retrieve_mobility_profiles: + message: + "Retrieving mobility profiles data" input: kfz=storage(MOBILITY_PROFILES_DATASET["url"] + "/kfz.csv"), pkw=storage(MOBILITY_PROFILES_DATASET["url"] + "/pkw.csv"), output: - kfz=f"{MOBILITY_PROFILES_DATASET["folder"]}/kfz.csv", - pkw=f"{MOBILITY_PROFILES_DATASET["folder"]}/pkw.csv", + kfz=f"{MOBILITY_PROFILES_DATASET['folder']}/kfz.csv", + pkw=f"{MOBILITY_PROFILES_DATASET['folder']}/pkw.csv", threads: 1 resources: mem_mb=1000, diff --git a/rules/solve_electricity.smk b/rules/solve_electricity.smk index e94db1e90..61bc3f8a2 100644 --- a/rules/solve_electricity.smk +++ b/rules/solve_electricity.smk @@ -4,6 +4,8 @@ rule solve_network: + message: + "Solving electricity network optimization for {wildcards.clusters} clusters and {wildcards.opts} electric options" params: solving=config_provider("solving"), foresight=config_provider("foresight"), @@ -35,6 +37,8 @@ rule solve_network: rule solve_operations_network: + message: + "Solving electricity network operations optimization for {wildcards.clusters} clusters and {wildcards.opts} electric options" params: options=config_provider("solving", "options"), solving=config_provider("solving"), diff --git a/rules/solve_myopic.smk b/rules/solve_myopic.smk index 0a3823f5d..10f1b005d 100644 --- a/rules/solve_myopic.smk +++ b/rules/solve_myopic.smk @@ -4,6 +4,8 @@ rule add_existing_baseyear: + message: + "Adding existing infrastructure for base year for {wildcards.clusters} clusters, {wildcards.planning_horizons} planning horizons, {wildcards.opts} electric options and {wildcards.sector_opts} sector options" params: baseyear=config_provider("scenario", "planning_horizons", 0), sector=config_provider("sector"), @@ -27,7 +29,7 @@ rule add_existing_baseyear: ), powerplants=resources("powerplants_s_{clusters}.csv"), costs=lambda w: resources( - f"costs_{config_provider("scenario", "planning_horizons",0)(w)}_processed.csv" + f"costs_{config_provider('scenario', 'planning_horizons',0)(w)}_processed.csv" ), cop_profiles=resources("cop_profiles_base_s_{clusters}_{planning_horizons}.nc"), existing_heating_distribution=lambda w: ( @@ -74,6 +76,8 @@ def input_profile_tech_brownfield(w): rule add_brownfield: + message: + "Adding brownfield constraints for existing infrastructure for {wildcards.clusters} clusters, {wildcards.planning_horizons} planning horizons, {wildcards.opts} electric options and {wildcards.sector_opts} sector options" params: H2_retrofit=config_provider("sector", "H2_retrofit"), H2_retrofit_capacity_per_CH4=config_provider( @@ -125,6 +129,8 @@ ruleorder: add_existing_baseyear > add_brownfield rule solve_sector_network_myopic: + message: + "Solving sector-coupled network with myopic foresight for {wildcards.clusters} clusters, {wildcards.planning_horizons} planning horizons, {wildcards.opts} electric options and {wildcards.sector_opts} sector options" params: solving=config_provider("solving"), foresight=config_provider("foresight"), diff --git a/rules/solve_overnight.smk b/rules/solve_overnight.smk index 8a4830e0c..056ad6b3f 100644 --- a/rules/solve_overnight.smk +++ b/rules/solve_overnight.smk @@ -4,6 +4,8 @@ rule solve_sector_network: + message: + "Solving sector-coupled network with overnight investment optimization for {wildcards.clusters} clusters, {wildcards.planning_horizons} planning horizons, {wildcards.opts} electric options and {wildcards.sector_opts} sector options" params: solving=config_provider("solving"), foresight=config_provider("foresight"), diff --git a/rules/solve_perfect.smk b/rules/solve_perfect.smk index f0b7202aa..56fed98cb 100644 --- a/rules/solve_perfect.smk +++ b/rules/solve_perfect.smk @@ -2,6 +2,8 @@ # # SPDX-License-Identifier: MIT rule add_existing_baseyear: + message: + "Adding existing infrastructure for base year for {wildcards.clusters} clusters, {wildcards.planning_horizons} planning horizons, {wildcards.opts} electric options and {wildcards.sector_opts} sector options" params: baseyear=config_provider("scenario", "planning_horizons", 0), sector=config_provider("sector"), @@ -19,7 +21,7 @@ rule add_existing_baseyear: busmap=resources("busmap_base_s_{clusters}.csv"), clustered_pop_layout=resources("pop_layout_base_s_{clusters}.csv"), costs=lambda w: resources( - f"costs_{config_provider("scenario", "planning_horizons",0)(w)}_processed.csv" + f"costs_{config_provider('scenario', 'planning_horizons',0)(w)}_processed.csv" ), cop_profiles=resources("cop_profiles_base_s_{clusters}_{planning_horizons}.nc"), existing_heating_distribution=resources( @@ -59,6 +61,8 @@ def input_network_year(w): rule prepare_perfect_foresight: + message: + "Preparing data for perfect foresight optimization for {wildcards.clusters} clusters, {wildcards.opts} electric options and {wildcards.sector_opts} sector options" params: costs=config_provider("costs"), time_resolution=config_provider("clustering", "temporal", "sector"), @@ -86,6 +90,8 @@ rule prepare_perfect_foresight: rule solve_sector_network_perfect: + message: + "Solving sector-coupled network with perfect foresight for {wildcards.clusters} clusters, {wildcards.opts} electric options and {wildcards.sector_opts} sector options" params: solving=config_provider("solving"), foresight=config_provider("foresight"), @@ -137,6 +143,8 @@ def input_networks_make_summary_perfect(w): rule make_summary_perfect: + message: + "Creating summary for perfect foresight optimization results" input: unpack(input_networks_make_summary_perfect), costs=resources("costs_2020_processed.csv"), diff --git a/scripts/_helpers.py b/scripts/_helpers.py index 61cfd7b08..400b692b1 100644 --- a/scripts/_helpers.py +++ b/scripts/_helpers.py @@ -515,13 +515,17 @@ def mock_snakemake( import os import snakemake as sm + from packaging import version from pypsa.definitions.structures import Dict + from snakemake import __version__ as sm_version from snakemake.api import Workflow from snakemake.common import SNAKEFILE_CHOICES + from snakemake.logging import LoggerManager from snakemake.script import Snakemake from snakemake.settings.types import ( ConfigSettings, DAGSettings, + OutputSettings, ResourceSettings, StorageSettings, WorkflowSettings, @@ -562,15 +566,25 @@ def mock_snakemake( workflow_settings = WorkflowSettings() storage_settings = StorageSettings() dag_settings = DAGSettings(rerun_triggers=[]) - workflow = Workflow( - config_settings, - resource_settings, - workflow_settings, - storage_settings, - dag_settings, + + workflow_kwargs = dict( + config_settings=config_settings, + resource_settings=resource_settings, + workflow_settings=workflow_settings, + storage_settings=storage_settings, + dag_settings=dag_settings, storage_provider_settings=dict(), overwrite_workdir=workdir, ) + + # Snakemake version-dependent logger handling + if version.parse(sm_version) >= version.parse("9.14.6"): + output_settings = OutputSettings() + workflow_kwargs["logger_manager"] = LoggerManager( + logger=logger, settings=output_settings + ) + + workflow = Workflow(**workflow_kwargs) workflow.include(snakefile) if configfiles: diff --git a/scripts/build_osm_network.py b/scripts/build_osm_network.py index bb8f58c2c..a41594d89 100644 --- a/scripts/build_osm_network.py +++ b/scripts/build_osm_network.py @@ -12,7 +12,7 @@ import pandas as pd import pypsa from pyproj import Transformer -from shapely import prepare +from shapely import get_point, prepare from shapely.algorithms.polylabel import polylabel from shapely.geometry import LineString, MultiLineString, Point from shapely.ops import linemerge, split @@ -148,18 +148,23 @@ def _add_line_endings(buses, lines, add=0, name="line-end"): ------- - pd.DataFrame: DataFrame containing the virtual bus endpoints with columns 'bus_id', 'voltage', 'geometry', and 'contains'. """ - endpoints0 = lines[["voltage", "geometry"]].copy() - endpoints0["geometry"] = endpoints0["geometry"].apply(lambda x: x.boundary.geoms[0]) - - endpoints1 = lines[["voltage", "geometry"]].copy() - endpoints1["geometry"] = endpoints1["geometry"].apply(lambda x: x.boundary.geoms[1]) - + line_data = lines[["voltage", "geometry", "line_id"]] + line_geoms = line_data["geometry"].apply(_remove_loops_from_multiline) + endpoints0 = line_data.assign( + geometry=get_point(line_geoms.geometry, 0), endpoint=0 + ) + endpoints1 = line_data.assign( + geometry=get_point(line_geoms.geometry, -1), endpoint=1 + ) endpoints = pd.concat([endpoints0, endpoints1], ignore_index=True) endpoints.drop_duplicates(subset=["geometry", "voltage"], inplace=True) endpoints.reset_index(drop=True, inplace=True) - endpoints["bus_id"] = endpoints.index + add + 1 - endpoints["bus_id"] = "virtual" + "-" + endpoints["bus_id"].astype(str) + # Create deterministic ID from line_id and endpoint + endpoints["bus_id"] = ( + "virtual_" + endpoints["line_id"] + "_" + endpoints["endpoint"].astype(str) + ) + endpoints.drop(columns=["line_id", "endpoint"], inplace=True) endpoints["contains"] = name @@ -300,7 +305,7 @@ def _create_merge_mapping(lines, buses, buses_polygon, geo_crs=GEO_CRS): - Identifies shared buses to remove using networkx. - Creates a network graph of lines to be merged using networkx - Identifies connected components in the graph and merges lines within each component. - - Note that only lines that unambigruosly can be merged are considered. + - Note that only lines that unambiguously can be merged are considered. Parameters ---------- @@ -651,10 +656,9 @@ def _create_station_seeds( buses_to_rename = buses_to_rename.sort_values( by=["country", "lat", "lon"], ascending=[True, False, True] ) - buses_to_rename["bus_id"] = buses_to_rename.groupby("country").cumcount() + 1 - buses_to_rename["bus_id"] = buses_to_rename["country"] + buses_to_rename[ - "bus_id" - ].astype(str) + buses_to_rename["bus_id"] = buses_to_rename[ + "country" + ] + buses_to_rename.index.str.replace("virtual", "") # Dict to rename virtual buses dict_rename = buses_to_rename["bus_id"].to_dict() @@ -729,7 +733,7 @@ def _merge_buses_to_stations( voltages = sorted( g_value["voltage"].unique(), reverse=True ) # Sort voltags in descending order - + not_virtual = ~g_value.bus_id.str.startswith("virtual_") if len(voltages) > 1: poi_x, poi_y = geo_to_dist.transform( g_value["poi"].values[0].x, g_value["poi"].values[0].y @@ -745,10 +749,11 @@ def _merge_buses_to_stations( poi_offset = Point(dist_to_geo.transform(poi_x_offset, poi_y_offset)) - # Update bus_name - g_value.loc[g_value["voltage"] == v, "bus_id"] = ( - g_name + "-" + str(int(v / 1000)) - ) + # Update bus_name if not virtual (in which case the voltage suffix is already present) + g_value.loc[ + (g_value["voltage"] == v) & not_virtual, + "bus_id", + ] = g_name + "-" + str(int(v / 1000)) # Update geometry g_value.loc[g_value["voltage"] == v, "geometry"] = poi_offset @@ -757,7 +762,9 @@ def _merge_buses_to_stations( buses_all.loc[g_value.index, "geometry"] = g_value["geometry"] else: v = voltages[0] - buses_all.loc[g_value.index, "bus_id"] = g_name + "-" + str(int(v / 1000)) + buses_all.loc[g_value.loc[not_virtual].index, "bus_id"] = ( + g_name + "-" + str(int(v / 1000)) + ) buses_all.loc[g_value.index, "geometry"] = g_value["poi"] return buses_all @@ -889,8 +896,9 @@ def _map_endpoints_to_buses( for coord in range(2): # Obtain endpoints endpoints = lines_all[["voltage", "geometry"]].copy() - endpoints["geometry"] = endpoints["geometry"].apply( - lambda x: x.boundary.geoms[coord] + # -1 * coord returns 0 for coord=0 and -1 for coord=1 + endpoints["geometry"] = get_point( + endpoints.geometry.apply(_remove_loops_from_multiline), -1 * coord ) if sjoin == "intersects": endpoints = gpd.sjoin( @@ -1541,7 +1549,7 @@ def build_network( buses_polygon.drop(columns=["voltage"], inplace=True) # Lines - lines = gpd.read_file(inputs["lines"]) + lines = gpd.read_file(inputs["lines"]).drop("contains", axis=1) lines = _merge_identical_lines(lines) # Floor voltages to 3 decimal places (e.g., 66600 becomes 66000, 220000 stays 220000) diff --git a/scripts/determine_availability_matrix.py b/scripts/determine_availability_matrix.py index b89de3f25..3b3262c5d 100644 --- a/scripts/determine_availability_matrix.py +++ b/scripts/determine_availability_matrix.py @@ -145,11 +145,11 @@ snakemake.input.gebco, codes=func, crs=4326, nodata=-1000, invert=True ) - if "min_shore_distance" in params: + if params.get("min_shore_distance") is not None: buffer = params["min_shore_distance"] excluder.add_geometry(snakemake.input.country_shapes, buffer=buffer) - if "max_shore_distance" in params: + if params.get("max_shore_distance") is not None: buffer = params["max_shore_distance"] excluder.add_geometry( snakemake.input.country_shapes, buffer=buffer, invert=True diff --git a/scripts/lib/validation/config/__init__.py b/scripts/lib/validation/config/__init__.py new file mode 100644 index 000000000..a458925ae --- /dev/null +++ b/scripts/lib/validation/config/__init__.py @@ -0,0 +1,398 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +""" +Config validation for PyPSA-EUR. + +The schema is exported to both `config/config.default.yaml` and `config/schema.json`. +The json schema is also contributed to the schemastore.org and matches +`**/pypsa-eur*/config/*.yaml` to get IDE support without additional configuration. +""" + +import re +from typing import Literal + +from pydantic import BaseModel, ConfigDict, Field, ValidationError, model_validator +from ruamel.yaml import YAML + +from scripts.lib.validation.config._base import ConfigModel +from scripts.lib.validation.config.adjustments import AdjustmentsConfig +from scripts.lib.validation.config.atlite import AtliteConfig +from scripts.lib.validation.config.biomass import BiomassConfig +from scripts.lib.validation.config.clustering import ClusteringConfig +from scripts.lib.validation.config.co2_budget import Co2BudgetConfig +from scripts.lib.validation.config.conventional import ConventionalConfig +from scripts.lib.validation.config.costs import CostsConfig +from scripts.lib.validation.config.countries import CountriesConfig +from scripts.lib.validation.config.data import DataConfig +from scripts.lib.validation.config.electricity import ElectricityConfig +from scripts.lib.validation.config.enable import EnableConfig +from scripts.lib.validation.config.energy import EnergyConfig +from scripts.lib.validation.config.existing_capacities import ExistingCapacitiesConfig +from scripts.lib.validation.config.foresight import ForesightConfig +from scripts.lib.validation.config.industry import IndustryConfig +from scripts.lib.validation.config.lines import LinesConfig +from scripts.lib.validation.config.links import LinksConfig +from scripts.lib.validation.config.load import LoadConfig +from scripts.lib.validation.config.overpass_api import OverpassApiConfig +from scripts.lib.validation.config.pypsa_eur import PypsaEurConfig +from scripts.lib.validation.config.renewable import RenewableConfig +from scripts.lib.validation.config.run import RunConfig +from scripts.lib.validation.config.scenario import ScenarioConfig +from scripts.lib.validation.config.sector import SectorConfig +from scripts.lib.validation.config.snapshots import SnapshotsConfig +from scripts.lib.validation.config.solar_thermal import SolarThermalConfig +from scripts.lib.validation.config.solving import SolvingConfig +from scripts.lib.validation.config.transformers import TransformersConfig +from scripts.lib.validation.config.transmission_projects import ( + TransmissionProjectsConfig, +) + + +class LoggingConfig(ConfigModel): + """Configuration for top level `logging` settings.""" + + level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] = Field( + "INFO", + description="Restrict console outputs to all infos, warning or errors only", + ) + format: str = Field( + "%(levelname)s:%(name)s:%(message)s", + description="Custom format for log messages. See `LogRecord `_ attributes.", + ) + + +class RemoteConfig(ConfigModel): + """Configuration for top level `remote` settings.""" + + ssh: str = Field( + "", + description="Optionally specify the SSH of a remote cluster to be synchronized.", + ) + path: str = Field( + "", + description="Optionally specify the file path within the remote cluster to be synchronized.", + ) + + +class ConfigSchema(BaseModel): + """ + Combined configuration schema for PyPSA-EUR. + """ + + # TODO Change to extra='forbid' once schema covers all config options + # For soft-forks it is recommended to either extend the schema for full config + # coverage or allow extra fields with extra='allow' + model_config = ConfigDict(extra="allow", title="PyPSA-Eur Configuration") + + # Top-level fields (from TopLevelConfig) + version: str = Field( + "v2025.07.0", description="Version of PyPSA-Eur. Descriptive only." + ) + tutorial: bool = Field( + False, + description="Switch to retrieve the tutorial data set instead of the full data set.", + ) + logging: LoggingConfig = Field( + default_factory=LoggingConfig, + description="Logging configuration for the workflow", + ) + remote: RemoteConfig = Field( + default_factory=RemoteConfig, + description="Configuration for remote workflow execution", + ) + + run: RunConfig = Field( + default_factory=RunConfig, + description="Run configuration for PyPSA-EUR workflow execution.", + ) + foresight: ForesightConfig = Field( + default_factory=ForesightConfig, + description="Foresight mode for the optimization. See Foresight Options for detailed explanations.", + ) + scenario: ScenarioConfig = Field( + default_factory=ScenarioConfig, + description="Scenario configuration defining wildcards for the workflow.", + ) + countries: CountriesConfig = Field( + default_factory=CountriesConfig, + description="European countries defined by their Two-letter country codes (ISO 3166-1) which should be included in the energy system model.", + ) + snapshots: SnapshotsConfig = Field( + default_factory=SnapshotsConfig, + description="Configuration for the time period snapshots.", + ) + enable: EnableConfig = Field( + default_factory=EnableConfig, + description="Flags to enable/disable workflow features.", + ) + co2_budget: Co2BudgetConfig = Field( + default_factory=Co2BudgetConfig, + description="CO2 budget as fraction of 1990 emissions per planning horizon year.", + ) + electricity: ElectricityConfig = Field( + default_factory=ElectricityConfig, + description="Electricity sector configuration.", + ) + atlite: AtliteConfig = Field( + default_factory=AtliteConfig, + description="Atlite cutout configuration for weather data.", + ) + renewable: RenewableConfig = Field( + default_factory=RenewableConfig, + description="Renewable energy technologies configuration.", + ) + conventional: ConventionalConfig = Field( + default_factory=ConventionalConfig, + description="Conventional power plants configuration.", + ) + lines: LinesConfig = Field( + default_factory=LinesConfig, + description="Transmission lines configuration.", + ) + links: LinksConfig = Field( + default_factory=LinksConfig, + description="HVDC links configuration.", + ) + transmission_projects: TransmissionProjectsConfig = Field( + default_factory=TransmissionProjectsConfig, + description="Transmission projects configuration.", + ) + transformers: TransformersConfig = Field( + default_factory=TransformersConfig, + description="Transformers configuration.", + ) + load: LoadConfig = Field( + default_factory=LoadConfig, + description="Electrical load configuration.", + ) + pypsa_eur: PypsaEurConfig = Field( + default_factory=PypsaEurConfig, + description="PyPSA-Eur component filtering configuration.", + ) + energy: EnergyConfig = Field( + default_factory=EnergyConfig, + description="Energy totals configuration.", + ) + biomass: BiomassConfig = Field( + default_factory=BiomassConfig, + description="Biomass configuration.", + ) + solar_thermal: SolarThermalConfig = Field( + default_factory=SolarThermalConfig, + description="Solar thermal configuration.", + ) + existing_capacities: ExistingCapacitiesConfig = Field( + default_factory=ExistingCapacitiesConfig, + description="Existing capacities grouping configuration.", + ) + sector: SectorConfig = Field( + default_factory=SectorConfig, + description="Sector coupling configuration.", + ) + industry: IndustryConfig = Field( + default_factory=IndustryConfig, + description="Industry sector configuration.", + ) + costs: CostsConfig = Field( + default_factory=CostsConfig, + description="Cost assumptions configuration.", + ) + clustering: ClusteringConfig = Field( + default_factory=ClusteringConfig, + description="Network clustering configuration.", + ) + adjustments: AdjustmentsConfig = Field( + default_factory=AdjustmentsConfig, + description="Network adjustments configuration.", + ) + solving: SolvingConfig = Field( + default_factory=SolvingConfig, + description="Solver and optimization configuration.", + ) + data: DataConfig = Field( + default_factory=DataConfig, + description="Data source configuration.", + ) + overpass_api: OverpassApiConfig = Field( + default_factory=OverpassApiConfig, + description="Overpass API configuration for OSM data retrieval.", + ) + + @model_validator(mode="before") + @classmethod + def check_no_secrets_section(cls, data): + """Prevent secrets from being stored in config.""" + if isinstance(data, dict) and "secrets" in data: + raise ValueError( + "The 'secrets:' section is no longer supported in config to avoid " + "leaking credentials. Use environment variables instead (e.g., " + "CORINE_API_TOKEN). You can set these in a .env file in the project root." + ) + return data + + +def validate_config(config: dict) -> ConfigSchema: + """Validate config dict against schema.""" + return ConfigSchema(**config) + + +def generate_config_defaults(path: str = "config/config.default.yaml") -> dict: + """Generate config defaults YAML file and return the defaults dict.""" + from ruamel.yaml.comments import CommentedMap + + def convert_to_field_name(key: str) -> str: + """Convert dash-case to snake_case for field lookup.""" + return key.replace("-", "_") + + # by_alias is needed to export dash-case instead of snake_case (which are some set aliases) + # the goal should be to use snake_case consistently + defaults = ConfigSchema().model_dump(by_alias=True) + + # Create YAML instance with custom settings + yaml_writer = YAML() + yaml_writer.version = (1, 1) # Make sure to quote boolean-looking strings + yaml_writer.default_flow_style = False + yaml_writer.width = 4096 # Avoid line wrapping + yaml_writer.indent(mapping=2, sequence=2, offset=0) + + # Custom string representer for controlling quote style + def str_representer(dumper, data): + """Use block style for multiline, quotes for special chars, plain otherwise.""" + TAG = "tag:yaml.org,2002:str" + if "\n" in data: + return dumper.represent_scalar(TAG, data, style="|") + if data == "" or any(c in data for c in ":{}[]&*#?|-<>=!%@"): + return dumper.represent_scalar(TAG, data, style='"') + return dumper.represent_scalar(TAG, data, style="") + + yaml_writer.representer.add_representer(str, str_representer) + + # Create a CommentedMap to add comments + data = CommentedMap() + + # Add yaml-language-server comment at the very top (before first key) + data.yaml_set_start_comment("yaml-language-server: $schema=./schema.json") + + for key, value in defaults.items(): + data[key] = value + + field_name = convert_to_field_name(key) + docs_url = f"https://pypsa-eur.readthedocs.io/en/latest/configuration.html#{field_name}" + data.yaml_set_comment_before_after_key(key, before=f"\ndocs in {docs_url}") + + # Write to file + with open(path, "w") as f: + yaml_writer.dump(data, f) + + return defaults + + +def generate_config_schema(path: str = "config/schema.json") -> dict: + """Generate JSON schema file and return the schema dict.""" + import json + import math + + def resolve_refs(obj: dict, defs: dict) -> dict: + """Resolve nested schema references to show them nicely in the documentation.""" + if isinstance(obj, dict): + if "$ref" in obj: + ref_path = obj["$ref"] # "#/$defs/RunConfig + ref_name = ref_path.split("/")[-1] + if ref_name in defs: + resolved = resolve_refs(defs[ref_name].copy(), defs) + # Keep description from the reference + if "description" in obj and "description" not in resolved: + resolved["description"] = obj["description"] + return resolved + return {k: resolve_refs(v, defs) for k, v in obj.items()} + elif isinstance(obj, list): + return [resolve_refs(item, defs) for item in obj] + return obj + + def sanitize_for_json(obj): + """Replace infinity values with None for valid JSON.""" + if isinstance(obj, dict): + return {k: sanitize_for_json(v) for k, v in obj.items()} + elif isinstance(obj, list): + return [sanitize_for_json(v) for v in obj] + elif isinstance(obj, float) and math.isinf(obj): + return None + return obj + + def remove_nested_titles(obj, is_root=True): + """Remove nested titles (e.g. model class names).""" + if isinstance(obj, dict): + result = {} + for k, v in obj.items(): + if k == "title" and not is_root: + continue + result[k] = remove_nested_titles(v, is_root=False) + return result + elif isinstance(obj, list): + return [remove_nested_titles(item, is_root=False) for item in obj] + return obj + + def remove_object_type(obj, is_root=True): + """Remove 'type: object' from nested objects (redundant when properties exist).""" + if isinstance(obj, dict): + result = {} + for k, v in obj.items(): + if ( + k == "type" + and v == "object" + and not is_root + and "properties" in obj + ): + continue + result[k] = remove_object_type(v, is_root=False) + return result + elif isinstance(obj, list): + return [remove_object_type(item, is_root=False) for item in obj] + return obj + + def convert_rst_to_markdown(obj): + """Convert RST-style links in 'description' to Markdown in 'markdownDescription'.""" + + def rst_to_md(text): + """Convert RST link format `Link Text `_ to Markdown [Link Text](URL).""" + # Pattern matches: `Link Text `_ + pattern = r"`([^<>`]+)\s*<([^>]+)>`_" + return re.sub(pattern, r"[\1](\2)", text) + + if isinstance(obj, dict): + result = {} + for k, v in obj.items(): + if k == "description" and isinstance(v, str) and "`" in v and "<" in v: + result[k] = v + md_text = rst_to_md(v) + if md_text != v: + result["markdownDescription"] = md_text + else: + result[k] = convert_rst_to_markdown(v) + return result + elif isinstance(obj, list): + return [convert_rst_to_markdown(item) for item in obj] + return obj + + schema = ConfigSchema.model_json_schema() + defs = schema.get("$defs", {}) + schema = resolve_refs(schema, defs) + schema = sanitize_for_json(schema) + schema = remove_nested_titles(schema) + schema = remove_object_type(schema) + schema = convert_rst_to_markdown(schema) + with open(path, "w") as f: + json.dump(schema, f, indent=2) + f.write("\n") + return schema + + +__all__ = [ + "ConfigSchema", + "validate_config", + "generate_config_defaults", + "generate_config_schema", + "ValidationError", +] diff --git a/scripts/lib/validation/config/_base.py b/scripts/lib/validation/config/_base.py new file mode 100644 index 000000000..e9606d689 --- /dev/null +++ b/scripts/lib/validation/config/_base.py @@ -0,0 +1,38 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +"""Base classes for config validation models.""" + +from collections.abc import Iterator +from typing import Any + +from pydantic import BaseModel + + +class ConfigModel(BaseModel): + """Base model for all config classes with dict-like access for Snakemake compatibility.""" + + def __getitem__(self, key: str) -> Any: + """Enable: config['key'].""" + return getattr(self, key) + + def __contains__(self, key: str) -> bool: + """Enable: 'key' in config.""" + return hasattr(self, key) + + def get(self, key: str, default: Any = None) -> Any: + """Enable: config.get('key', default).""" + return getattr(self, key, default) + + def keys(self) -> Iterator[str]: + """Enable: config.keys().""" + return iter(self.model_fields.keys()) + + def values(self) -> Iterator[Any]: + """Enable: config.values().""" + return (getattr(self, k) for k in self.model_fields.keys()) + + def items(self) -> Iterator[tuple[str, Any]]: + """Enable: config.items().""" + return ((k, getattr(self, k)) for k in self.model_fields.keys()) diff --git a/scripts/lib/validation/config/adjustments.py b/scripts/lib/validation/config/adjustments.py new file mode 100644 index 000000000..321a4375a --- /dev/null +++ b/scripts/lib/validation/config/adjustments.py @@ -0,0 +1,48 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +""" +Adjustments configuration. + +See docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#adjustments +""" + +from pydantic import BaseModel, Field + +from scripts.lib.validation.config._base import ConfigModel + + +class _AdjustmentConfig(ConfigModel): + """Configuration for adjustment settings (factor/absolute)""" + + factor: bool | dict[str, dict[str, dict[str, float | dict[int, float]]]] = Field( + False, + description="Multiply original value with given factor", + ) + absolute: bool | dict[str, dict[str, dict[str, float | dict[int, float]]]] = Field( + False, + description="Set attribute to absolute value. Can be also a dictionary with planning horizons as keys.", + ) + + +class AdjustmentsConfig(BaseModel): + """Configuration for top-level adjustments key.""" + + electricity: bool | _AdjustmentConfig = Field( + False, + description="Parameter adjustments applied in `prepare_network`.", + ) + sector: bool | _AdjustmentConfig = Field( + default_factory=lambda: _AdjustmentConfig( + factor={ + "Link": { + "electricity distribution grid": { + "capital_cost": 1.0, + } + } + }, + absolute=False, + ), + description="Parameter adjustments applied in `prepare_sector_network`.", + ) diff --git a/scripts/lib/validation/config/atlite.py b/scripts/lib/validation/config/atlite.py new file mode 100644 index 000000000..cce1a0428 --- /dev/null +++ b/scripts/lib/validation/config/atlite.py @@ -0,0 +1,142 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +""" +Atlite configuration. + +See docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#atlite +""" + +from pydantic import BaseModel, Field, field_validator + +from scripts.lib.validation.config._base import ConfigModel + + +class _PrepareKwargsConfig(ConfigModel): + """Configuration for `atlite.cutouts.{name}.prepare_kwargs` settings.""" + + features: str | list[str] | None = Field( + None, + description="When freshly building a cutout, retrieve data only for those features. If not defined, it defaults to all available features.", + ) + sarah_dir: str | None = Field( + None, + description="Path to the location where SARAH-2 or SARAH-3 data is stored; SARAH data requires a manual separate download, see the `atlite documentation `_ for details. Required for building cutouts with SARAH, not required for ERA5 cutouts.", + ) + monthly_requests: bool | None = Field( + None, + description="Whether to use monthly requests for ERA5 data when building the cutout. Helpful to avoid running into request limits with large cutouts.", + ) + tmpdir: str | None = Field( + None, + description="Path to a temporary directory where intermediate files are stored when building the cutout. Helpful when building large cutouts.", + ) + + +class _ChunksConfig(ConfigModel): + """Configuration for `atlite.cutouts.{name}.chunks` settings.""" + + time: int | None = Field( + None, + description="Chunk size for time dimension when preparing cutout.", + ) + + +class _CutoutConfig(ConfigModel): + """Configuration for a single cutout in `atlite.cutouts`.""" + + module: str | list[str] | None = Field( + None, + description="Source of the reanalysis weather dataset (e.g. `ERA5 `_ or `SARAH-3 `_).", + ) + x: list[float] | None = Field( + None, + description="Range of longitudes [°] to download weather data for. Float interval within [-180, 180]. If not defined, it defaults to the spatial bounds of all bus shapes.", + ) + y: list[float] | None = Field( + None, + description="Range of latitudes [°] to download weather data for. Float interval within [-90, 90]. If not defined, it defaults to the spatial bounds of all bus shapes.", + ) + dx: float | None = Field( + None, + gt=0.25, + description="Grid resolution [°] for longitude. Must be larger than 0.25°.", + ) + dy: float | None = Field( + None, + gt=0.25, + description="Grid resolution [°] for latitude. Must be larger than 0.25°.", + ) + time: list[str] | None = Field( + None, + description="Time span to download weather data for. If not defined, it defaults to the time interval spanned by the snapshots.", + ) + chunks: _ChunksConfig | None = Field( + None, + description="Chunking configuration for cutout preparation.", + ) + prepare_kwargs: _PrepareKwargsConfig | None = Field( + None, + description="Dictionary of keyword arguments passed to ``atlite.Cutout.prepare()`` when building the cutout.", + ) + + @field_validator("x") + @classmethod + def validate_longitude(cls, v): + if v is not None: + if len(v) != 2: + raise ValueError("x must be a list of two floats [min, max]") + if not all(-180 <= val <= 180 for val in v): + raise ValueError("Longitude values must be within [-180, 180]") + if v[0] >= v[1]: + raise ValueError("x[0] must be less than x[1]") + return v + + @field_validator("y") + @classmethod + def validate_latitude(cls, v): + if v is not None: + if len(v) != 2: + raise ValueError("y must be a list of two floats [min, max]") + if not all(-90 <= val <= 90 for val in v): + raise ValueError("Latitude values must be within [-90, 90]") + if v[0] >= v[1]: + raise ValueError("y[0] must be less than y[1]") + return v + + +class AtliteConfig(BaseModel): + """Configuration for `atlite` settings.""" + + default_cutout: str | list[str] = Field( + "europe-2013-sarah3-era5", + description="Defines a default cutout. Can refer to a single cutout or a list of cutouts.", + ) + nprocesses: int = Field( + 16, + description="Number of parallel processes in cutout preparation.", + ) + show_progress: bool = Field( + False, + description="Whether progressbar for atlite conversion processes should be shown. False saves time.", + ) + cutouts: dict[str, _CutoutConfig] = Field( + default_factory=lambda: { + "europe-1940-2024-era5": _CutoutConfig( + module="era5", + x=[-12.0, 42.0], + y=[33.0, 72.0], + dx=0.3, + dy=0.3, + time=["2013", "2013"], + chunks=_ChunksConfig(time=500), + prepare_kwargs=_PrepareKwargsConfig( + features=["temperature", "height", "runoff"], + monthly_requests=True, + tmpdir="./cutouts_tmp/", + ), + ), + }, + description="Named cutout configurations.", + ) diff --git a/scripts/lib/validation/config/biomass.py b/scripts/lib/validation/config/biomass.py new file mode 100644 index 000000000..2d02af18d --- /dev/null +++ b/scripts/lib/validation/config/biomass.py @@ -0,0 +1,105 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +""" +Biomass configuration. + +See docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#biomass +""" + +from typing import Literal + +from pydantic import BaseModel, ConfigDict, Field + +from scripts.lib.validation.config._base import ConfigModel + + +class _BiomassClassesConfig(ConfigModel): + """Configuration for `biomass.classes` settings.""" + + solid_biomass: list[str] = Field( + default_factory=lambda: [ + "Agricultural waste", + "Fuelwood residues", + "Secondary Forestry residues - woodchips", + "Sawdust", + "Residues from landscape care", + ], + alias="solid biomass", + description="The comodity that are included as solid biomass.", + ) + not_included: list[str] = Field( + default_factory=lambda: [ + "Sugar from sugar beet", + "Rape seed", + "Sunflower, soya seed ", + "Bioethanol barley, wheat, grain maize, oats, other cereals and rye", + "Miscanthus, switchgrass, RCG", + "Willow", + "Poplar", + "FuelwoodRW", + "C&P_RW", + ], + alias="not included", + description="The comodity that are not included as a biomass potential.", + ) + biogas: list[str] = Field( + default_factory=lambda: [ + "Manure solid, liquid", + "Sludge", + ], + description="The comodity that are included as biogas.", + ) + municipal_solid_waste: list[str] = Field( + default_factory=lambda: [ + "Municipal waste", + ], + alias="municipal solid waste", + description="The commodities that are included as municipal solid waste.", + ) + + model_config = ConfigDict(populate_by_name=True) + + +class BiomassConfig(BaseModel): + """Configuration for `biomass` settings.""" + + year: int = Field( + 2030, + ge=2010, + le=2050, + description="Year for which to retrieve biomass potential according to the assumptions of the `JRC ENSPRESO `_.", + ) + scenario: Literal["ENS_Low", "ENS_Med", "ENS_High"] = Field( + "ENS_Med", + description="Scenario for which to retrieve biomass potential. The scenario definition can be seen in `ENSPRESO_BIOMASS `_.", + ) + classes: _BiomassClassesConfig = Field( + default_factory=_BiomassClassesConfig, + description="Classification of biomass commodities.", + ) + share_unsustainable_use_retained: dict[int, float] = Field( + default_factory=lambda: { + 2020: 1, + 2025: 1, + 2030: 0.66, + 2035: 0.33, + 2040: 0, + 2045: 0, + 2050: 0, + }, + description="Share of unsustainable biomass use retained using primary production of Eurostat data as reference.", + ) + share_sustainable_potential_available: dict[int, float] = Field( + default_factory=lambda: { + 2020: 0, + 2025: 0, + 2030: 0.33, + 2035: 0.66, + 2040: 1, + 2045: 1, + 2050: 1, + }, + description="Share determines phase-in of ENSPRESO biomass potentials.", + ) diff --git a/scripts/lib/validation/config/clustering.py b/scripts/lib/validation/config/clustering.py new file mode 100644 index 000000000..b367a4679 --- /dev/null +++ b/scripts/lib/validation/config/clustering.py @@ -0,0 +1,156 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +""" +Clustering configuration. + +See docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#clustering +""" + +from typing import Literal + +from pydantic import BaseModel, Field + +from scripts.lib.validation.config._base import ConfigModel + + +class _AdministrativeConfig(ConfigModel): + """Configuration for `clustering.administrative` settings.""" + + level: Literal[0, 1, 2, 3, "bz"] = Field( + 1, + description="Level of administrative regions to cluster the network. 0: Country level, 1: NUTS1 level, 2: NUTS2 level, 3: NUTS3 level, 'bz': Bidding zones. Only applies when mode is set to `administrative`. Note that non-NUTS countries 'BA', 'MD', 'UA', and 'XK' can only be clustered to level 0 and 1.", + ) + countries: dict[str, int] = Field( + default_factory=dict, + description="Optionally include dictionary of individual country codes and their individual NUTS levels. Overwrites country-specific `level`. For example: `{'DE': 1, 'FR': 2}`. Only applies when mode is set to `administrative`.", + ) + + +class _BuildBiddingZonesConfig(BaseModel): + """Configuration for `clustering.build_bidding_zones` settings.""" + + remove_islands: bool = Field( + False, + description="Exclude from the shape file the Balearic Islands, Bornholm, the Canary Islands, the Orkney Islands, the Shetland Islands, the Azores Islands and Madeira.", + ) + aggregate_to_tyndp: bool = Field( + False, + description="Adjust the shape file to the TYNDP topology. Aggregate the Southern Norwegian bidding zones and extract Crete as a separate zone from the Greek shape.", + ) + + +class _SimplifyNetworkConfig(BaseModel): + """Configuration for `clustering.simplify_network` settings.""" + + to_substations: bool = Field( + False, + description="Aggregates all nodes without power injection (positive or negative, i.e. demand or generation) to electrically closest ones.", + ) + exclude_carriers: list[str] = Field( + default_factory=list, + description="List of carriers which will not be aggregated. If empty, all carriers will be aggregated.", + ) + remove_stubs: bool = Field( + True, + description="Controls whether radial parts of the network should be recursively aggregated. Defaults to true.", + ) + remove_stubs_across_borders: bool = Field( + False, + description="Controls whether radial parts of the network should be recursively aggregated across borders. Defaults to true.", + ) + + +class _ClusterNetworkConfig(BaseModel): + """Configuration for `clustering.cluster_network` settings.""" + + algorithm: Literal["kmeans", "hac"] = Field( + "kmeans", + description="Clustering algorithm to use.", + ) + hac_features: list[str] = Field( + default_factory=lambda: ["wnd100m", "influx_direct"], + description="List of meteorological variables contained in the weather data cutout that should be considered for hierarchical clustering.", + ) + + +class _AggregationStrategiesConfig(BaseModel): + """Configuration for `clustering.aggregation_strategies` settings.""" + + generators: dict[str, str] = Field( + default_factory=lambda: { + "committable": "any", + "ramp_limit_up": "max", + "ramp_limit_down": "max", + }, + description="Aggregates the component according to the given strategy. For example, if sum, then all values within each cluster are summed to represent the new generator.", + ) + buses: dict[str, str] = Field( + default_factory=dict, + description="Aggregates the component according to the given strategy. For example, if sum, then all values within each cluster are summed to represent the new bus.", + ) + + +class _TemporalConfig(BaseModel): + """Configuration for `clustering.temporal` settings.""" + + resolution_elec: bool | str = Field( + False, + description="Resample the time-resolution by averaging over every `n` snapshots in `prepare_network`. **Warning:** This option should currently only be used with electricity-only networks, not for sector-coupled networks.", + ) + resolution_sector: bool | str = Field( + False, + description="Resample the time-resolution by averaging over every `n` snapshots in `prepare_sector_network`.", + ) + + +class ClusteringConfig(BaseModel): + """Configuration for `clustering` settings.""" + + mode: Literal["busmap", "custom_busmap", "administrative", "custom_busshapes"] = ( + Field( + "busmap", + description="'busmap': Default. 'custom_busmap': Enable the use of custom busmaps in rule `cluster_network`. If activated the rule looks for provided busmaps at ``data/busmaps/base_s_{clusters}_{base_network}.csv`` which should have the same format as ``resources/busmap_base_s_{clusters}.csv``, i.e. the index should contain the buses of ``networks/base_s.nc``. {base_network} is the name of the selected base_network in electricity, e.g. ``gridkit``, ``osm-prebuilt``, or ``osm-raw``. 'administrative': Clusters and indexes the network based on the administrative regions of the countries based on ``nuts3_shapes.geojson`` (level: 1, 2, 3, bz). To activate this, additionally set the ``clusters`` wildcard in ``scenario`` to 'adm'. 'custom_busshapes': Enable the use of custom shapes in rule `cluster_network`. If activated the rule looks for provided busshapes at ``data/busshapes/base_s_{clusters}_{base_network}.geojson``.", + ) + ) + administrative: _AdministrativeConfig = Field( + default_factory=_AdministrativeConfig, + description="Administrative clustering settings.", + ) + focus_weights: bool | dict[str, float] = Field( + False, + description="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. Only applies when mode is set to `busmap`.", + ) + copperplate_regions: list[list[str]] = Field( + default_factory=list, + description="Optionally specify the regions to copperplate as a list of groups. Each group is a list of region codes that will be connected with infinite capacity lines.", + ) + build_bidding_zones: _BuildBiddingZonesConfig = Field( + default_factory=_BuildBiddingZonesConfig, + description="Build bidding zones configuration.", + ) + simplify_network: _SimplifyNetworkConfig = Field( + default_factory=_SimplifyNetworkConfig, + description="Network simplification settings.", + ) + cluster_network: _ClusterNetworkConfig = Field( + default_factory=_ClusterNetworkConfig, + description="Network clustering algorithm settings.", + ) + exclude_carriers: list[str] = Field( + default_factory=list, + description="List of carriers which will not be aggregated. If empty, all carriers will be aggregated.", + ) + consider_efficiency_classes: bool = Field( + False, + description="Aggregated each carriers into the top 10-quantile (high), the bottom 90-quantile (low), and everything in between (medium).", + ) + aggregation_strategies: _AggregationStrategiesConfig = Field( + default_factory=_AggregationStrategiesConfig, + description="Aggregation strategies for different components.", + ) + temporal: _TemporalConfig = Field( + default_factory=_TemporalConfig, + description="Options for temporal resolution.", + ) diff --git a/scripts/lib/validation/config/co2_budget.py b/scripts/lib/validation/config/co2_budget.py new file mode 100644 index 000000000..63922685c --- /dev/null +++ b/scripts/lib/validation/config/co2_budget.py @@ -0,0 +1,32 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +""" +CO2 budget configuration. + +See docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#co2-budget +""" + +from pydantic import Field, RootModel + + +class Co2BudgetConfig(RootModel[dict[int, float]]): + """Configuration for `co2_budget` settings.""" + + root: dict[int, float] = Field( + default_factory=lambda: { + # CO2 budget values as fraction of 1990 emissions per planning horizon year. + 2020: 0.720, # Average emissions of 2019-2021 relative to 1990, CO2 excl LULUCF + 2025: 0.648, # WAM projection from EEA Member States' GHG projections 2023 + 2030: 0.450, # 55% reduction by 2030 (Fit for 55) + 2035: 0.250, # Interpolated + 2040: 0.100, # 90% reduction target by 2040 + 2045: 0.050, # Interpolated + 2050: 0.000, # Climate-neutral by 2050 + # Sources: + # - EEA Annual European Union GHG inventory 1990-2021 (https://unfccc.int/documents/627830) + # - EEA Member States' GHG projections 2023 (https://www.eea.europa.eu/en/datahub/datahubitem-view/4b8d94a4-aed7-4e67-a54c-0623a50f48e8) + }, + description="CO2 budget as a fraction of 1990 emissions. Overwritten if `Co2Lx` or `cb` are set in `{sector_opts}` wildcard.", + ) diff --git a/scripts/lib/validation/config/conventional.py b/scripts/lib/validation/config/conventional.py new file mode 100644 index 000000000..eccc2654a --- /dev/null +++ b/scripts/lib/validation/config/conventional.py @@ -0,0 +1,32 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +""" +Conventional generators configuration. + +See docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#conventional +""" + +from pydantic import ConfigDict, Field + +from scripts.lib.validation.config._base import ConfigModel + + +class ConventionalConfig(ConfigModel): + """Configuration for `conventional` settings.""" + + model_config = ConfigDict(extra="allow") + + unit_commitment: bool = Field( + False, + description="Allow the overwrite of ramp_limit_up, ramp_limit_start_up, ramp_limit_shut_down, p_min_pu, min_up_time, min_down_time, and start_up_cost of conventional generators. Refer to the CSV file 'unit_commitment.csv'.", + ) + dynamic_fuel_price: bool = Field( + False, + description="Consider the monthly fluctuating fuel prices for each conventional generator. Refer to the CSV file 'data/validation/monthly_fuel_price.csv'.", + ) + nuclear: dict[str, str | float] = Field( + default_factory=lambda: {"p_max_pu": "data/nuclear_p_max_pu.csv"}, + description="For any carrier/technology overwrite attributes as listed below.", + ) diff --git a/scripts/lib/validation/config/costs.py b/scripts/lib/validation/config/costs.py new file mode 100644 index 000000000..afc1d289d --- /dev/null +++ b/scripts/lib/validation/config/costs.py @@ -0,0 +1,91 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +""" +Costs configuration. + +See docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#costs +""" + +from pydantic import BaseModel, ConfigDict, Field + +from scripts.lib.validation.config._base import ConfigModel + + +class _EmissionPricesConfig(ConfigModel): + """Configuration for `costs.emission_prices` settings.""" + + enable: bool = Field( + False, + description="Add cost for a carbon-dioxide price configured in `costs: emission_prices: co2` to `marginal_cost` of generators. Config setting can also be enabled with the keyword `Ep` in the `{opts}` wildcard for electricity-only runs.", + ) + co2: float | dict[str, float] = Field( + 0.0, + description="Exogenous price of carbon-dioxide. In electricity-only runs it is added to the marginal costs of fossil-fuelled generators according to their carbon intensity, while for sector networks it applies to emissions ending up in CO2 atmosphere.", + ) + co2_monthly_prices: bool = Field( + False, + description="Add monthly cost for a carbon-dioxide price based on historical values built by the rule `build_monthly_prices`.", + ) + + +class _FillValuesConfig(BaseModel): + """Configuration for `costs.fill_values` settings.""" + + FOM: float = Field(0, description="Default fixed operation and maintenance cost.") + VOM: float = Field( + 0, description="Default variable operation and maintenance cost." + ) + efficiency: float = Field(1, description="Default efficiency.") + fuel: float = Field(0, description="Default fuel cost.") + investment: float = Field(0, description="Default investment cost.") + lifetime: int = Field(25, description="Default lifetime in years.") + CO2_intensity: float = Field( + 0, alias="CO2 intensity", description="Default CO2 intensity." + ) + discount_rate: float = Field( + 0.07, alias="discount rate", description="Default discount rate." + ) + standing_losses: float = Field( + 0, alias="standing losses", description="Default standing losses." + ) + + model_config = ConfigDict(populate_by_name=True) + + +class CostsConfig(BaseModel): + """Configuration for `costs` settings.""" + + year: int = Field( + 2050, + description="Year for which to retrieve cost assumptions of `data/costs/primary//costs_.csv`.", + ) + social_discountrate: float = Field( + 0.02, + description="Social discount rate to compare costs in different investment periods. 0.02 corresponds to a social discount rate of 2%.", + ) + fill_values: _FillValuesConfig = Field( + default_factory=_FillValuesConfig, + description="Default values if not specified for a technology in `resources/costs.csv`.", + ) + custom_cost_fn: str | None = Field( + "data/custom_costs.csv", + description="Path to the custom costs file. None if it should not be used. Default `data/custom_costs.csv` contains minor adjustments for stabilising the optimisation results.", + ) + overwrites: dict[str, dict[str, float]] = Field( + default_factory=dict, + description="For the given parameters and technologies, assumptions about their parameter are overwritten the corresponding value of the technology.", + ) + capital_cost: dict[str, float] = Field( + default_factory=dict, + description="For the given technologies, assumptions about their capital investment costs are set to the corresponding value. Optional; overwrites cost assumptions from `resources/costs.csv`.", + ) + marginal_cost: dict[str, float] = Field( + default_factory=dict, + description="For the given technologies, assumptions about their marginal operating costs are set to the corresponding value. Optional; overwrites cost assumptions from `resources/costs.csv`.", + ) + emission_prices: _EmissionPricesConfig = Field( + default_factory=_EmissionPricesConfig, + description="Specify exogenous prices for emission types listed in `network.carriers` to marginal costs.", + ) diff --git a/scripts/lib/validation/config/countries.py b/scripts/lib/validation/config/countries.py new file mode 100644 index 000000000..2e7fc046d --- /dev/null +++ b/scripts/lib/validation/config/countries.py @@ -0,0 +1,55 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +""" +Countries configuration. + +See docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#countries +""" + +from pydantic import Field, RootModel + + +class CountriesConfig(RootModel[list[str]]): + """Configuration for `countries` settings.""" + + root: list[str] = Field( + default=[ + "AL", + "AT", + "BA", + "BE", + "BG", + "CH", + "CZ", + "DE", + "DK", + "EE", + "ES", + "FI", + "FR", + "GB", + "GR", + "HR", + "HU", + "IE", + "IT", + "LT", + "LU", + "LV", + "ME", + "MK", + "NL", + "NO", + "PL", + "PT", + "RO", + "RS", + "SE", + "SI", + "SK", + "XK", + ], + description="European countries defined by their `Two-letter country codes (ISO 3166-1) `_ which should be included in the energy system model.", + ) diff --git a/scripts/lib/validation/config/data.py b/scripts/lib/validation/config/data.py new file mode 100644 index 000000000..1744febef --- /dev/null +++ b/scripts/lib/validation/config/data.py @@ -0,0 +1,229 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +""" +Data source configuration. + +See docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#data +""" + +from typing import Literal + +from pydantic import BaseModel, Field + +from scripts.lib.validation.config._base import ConfigModel + + +class _DataSourceConfig(ConfigModel): + """Configuration for a single data source.""" + + source: Literal["archive", "primary", "build"] = Field( + "archive", + description="Source of the data. 'archive' retrieves pre-built data, 'primary' retrieves from primary source.", + ) + version: str = Field( + "latest", + description="Version of the data to use. Uses the specific 'version' for the selected 'source' or the dataset tagged 'latest' for this source.", + ) + + +class DataConfig(BaseModel): + """Configuration for `data` settings.""" + + hotmaps_industrial_sites: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="Hotmaps industrial sites data source configuration.", + ) + enspreso_biomass: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="Enspreso biomass data source configuration.", + ) + osm: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="OSM data source configuration.", + ) + worldbank_urban_population: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="World Bank urban population data source configuration.", + ) + gem_europe_gas_tracker: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="GEM Europe Gas Tracker data source configuration.", + ) + co2stop: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="CO2Stop data source configuration.", + ) + nitrogen_statistics: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="Nitrogen statistics data source configuration.", + ) + eu_nuts2013: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="EU NUTS 2013 data source configuration.", + ) + eu_nuts2021: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="EU NUTS 2021 data source configuration.", + ) + eurostat_balances: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="Eurostat balances data source configuration.", + ) + eurostat_household_balances: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="Eurostat household balances data source configuration.", + ) + wdpa: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="WDPA data source configuration.", + ) + wdpa_marine: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="WDPA Marine data source configuration.", + ) + luisa_land_cover: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="LUISA land cover data source configuration.", + ) + jrc_idees: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="JRC IDEES data source configuration.", + ) + scigrid_gas: _DataSourceConfig = Field( + default_factory=lambda: _DataSourceConfig(source="primary"), + description="SciGRID Gas data source configuration.", + ) + seawater_temperature: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="Seawater temperature data source configuration.", + ) + synthetic_electricity_demand: _DataSourceConfig = Field( + default_factory=lambda: _DataSourceConfig(source="primary"), + description="Synthetic electricity demand data source configuration.", + ) + copernicus_land_cover: _DataSourceConfig = Field( + default_factory=lambda: _DataSourceConfig(source="primary"), + description="Copernicus land cover data source configuration.", + ) + ship_raster: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="Ship raster data source configuration.", + ) + eez: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="EEZ data source configuration.", + ) + nuts3_population: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="NUTS3 population data source configuration.", + ) + gdp_per_capita: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="GDP per capita data source configuration.", + ) + population_count: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="Population count data source configuration.", + ) + ghg_emissions: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="GHG emissions data source configuration.", + ) + gebco: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="GEBCO data source configuration.", + ) + attributed_ports: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="Attributed ports data source configuration.", + ) + corine: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="CORINE data source configuration.", + ) + emobility: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="E-mobility data source configuration.", + ) + h2_salt_caverns: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="H2 salt caverns data source configuration.", + ) + lau_regions: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="LAU regions data source configuration.", + ) + aquifer_data: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="Aquifer data source configuration.", + ) + osm_boundaries: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="OSM boundaries data source configuration.", + ) + gem_gspt: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="GEM GSPT data source configuration.", + ) + tyndp: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="TYNDP data source configuration.", + ) + powerplants: _DataSourceConfig = Field( + default_factory=lambda: _DataSourceConfig(source="primary"), + description="Powerplants data source configuration.", + ) + costs: _DataSourceConfig = Field( + default_factory=lambda: _DataSourceConfig(source="primary"), + description="Costs data source configuration.", + ) + country_runoff: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="Country runoff data source configuration.", + ) + country_hdd: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="Country HDD data source configuration.", + ) + natura: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="Natura data source configuration.", + ) + bfs_road_vehicle_stock: _DataSourceConfig = Field( + default_factory=lambda: _DataSourceConfig(source="primary"), + description="BFS road vehicle stock data source configuration.", + ) + bfs_gdp_and_population: _DataSourceConfig = Field( + default_factory=lambda: _DataSourceConfig(source="primary"), + description="BFS GDP and population data source configuration.", + ) + mobility_profiles: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="Mobility profiles data source configuration.", + ) + cutout: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="Cutout data source configuration.", + ) + dh_areas: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="District heating areas data source configuration.", + ) + geothermal_heat_utilisation_potentials: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="Geothermal heat utilisation potentials data source configuration.", + ) + jrc_ardeco: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="JRC ARDECO data source configuration.", + ) + bidding_zones_electricitymaps: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="Electricitymaps bidding zones data source configuration.", + ) + bidding_zones_entsoepy: _DataSourceConfig = Field( + default_factory=_DataSourceConfig, + description="Entsoepy bidding zones data source configuration.", + ) diff --git a/scripts/lib/validation/config/electricity.py b/scripts/lib/validation/config/electricity.py new file mode 100644 index 000000000..87c6f7d7f --- /dev/null +++ b/scripts/lib/validation/config/electricity.py @@ -0,0 +1,230 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +""" +Electricity configuration. + +See docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#electricity +""" + +from typing import Literal + +from pydantic import BaseModel, ConfigDict, Field + +from scripts.lib.validation.config._base import ConfigModel + + +class _OperationalReserveConfig(ConfigModel): + """Configuration for `electricity.operational_reserve` settings.""" + + activate: bool = Field( + False, + description="Whether to take operational reserve requirements into account during optimisation.", + ) + epsilon_load: float = Field( + 0.02, + description="share of total load.", + ) + epsilon_vres: float = Field( + 0.02, + description="share of total renewable supply.", + ) + contingency: float = Field( + 4000, + description="Fixed reserve capacity (MW).", + ) + + +class _MaxHoursConfig(BaseModel): + """Configuration for `electricity.max_hours` settings.""" + + battery: float = Field( + 6, + description="Maximum state of charge capacity of the battery in terms of hours at full output capacity `p_nom`. Cf. `PyPSA documentation `_.", + ) + H2: float = Field( + 168, + description="Maximum state of charge capacity of the hydrogen storage in terms of hours at full output capacity `p_nom`. Cf. `PyPSA documentation `_.", + ) + + +class _ExtendableCarriersConfig(BaseModel): + """Configuration for `electricity.extendable_carriers` settings.""" + + Generator: list[str] = Field( + default_factory=lambda: [ + "solar", + "solar-hsat", + "onwind", + "offwind-ac", + "offwind-dc", + "offwind-float", + "OCGT", + "CCGT", + ], + description="Defines existing or non-existing conventional and renewable power plants to be extendable during the optimization. Conventional generators can only be built/expanded where already existent today. If a listed conventional carrier is not included in the `conventional_carriers` list, the lower limit of the capacity expansion is set to 0.", + ) + StorageUnit: list[str] = Field( + default_factory=list, + description="Adds extendable storage units (battery and/or hydrogen) at every node/bus after clustering without capacity limits and with zero initial capacity.", + ) + Store: list[str] = Field( + default_factory=lambda: ["battery", "H2"], + description="Adds extendable storage units (battery and/or hydrogen) at every node/bus after clustering without capacity limits and with zero initial capacity.", + ) + Link: list[str] = Field( + default_factory=list, + description="Adds extendable links (H2 pipelines only) at every connection where there are lines or HVDC links without capacity limits and with zero initial capacity. Hydrogen pipelines require hydrogen storage to be modelled as `Store`.", + ) + + +class _TechnologyMappingConfig(BaseModel): + """Configuration for `electricity.estimate_renewable_capacities.technology_mapping` settings.""" + + Offshore: str = Field( + "offwind-ac", + description="PyPSA-Eur carrier that is considered for existing offshore wind technology (IRENA, GEM).", + ) + Onshore: str = Field( + "onwind", + description="PyPSA-Eur carrier that is considered for existing onshore wind capacities (IRENA, GEM).", + ) + PV: str = Field( + "solar", + description="PyPSA-Eur carrier that is considered for existing solar PV capacities (IRENA, GEM).", + ) + + +class _EstimateRenewableCapacitiesConfig(BaseModel): + """Configuration for `electricity.estimate_renewable_capacities` settings.""" + + enable: bool = Field( + True, + description="Activate routine to estimate renewable capacities in rule `add_electricity`. This option should not be used in combination with pathway planning `foresight: myopic` or `foresight: perfect` as renewable capacities are added differently in `add_existing_baseyear`.", + ) + from_gem: bool = Field( + True, + description="Add renewable capacities from `Global Energy Monitor's Global Solar Power Tracker `_ and `Global Energy Monitor's Global Wind Power Tracker `_.", + ) + year: int = Field( + 2020, + description="Renewable capacities are based on existing capacities reported by IRENA (IRENASTAT) for the specified year.", + ) + expansion_limit: float | bool = Field( + False, + description="Artificially limit maximum IRENA capacities to a factor. For example, an `expansion_limit: 1.1` means 110% of capacities. If false are chosen, the estimated renewable potentials determine by the workflow are used.", + ) + technology_mapping: _TechnologyMappingConfig = Field( + default_factory=_TechnologyMappingConfig, + description="Mapping between PyPSA-Eur and powerplantmatching technology names.", + ) + + +class _AutarkyConfig(BaseModel): + """Configuration for `electricity.autarky` settings.""" + + enable: bool = Field( + False, + description="Require each node to be autarkic by removing all lines and links.", + ) + by_country: bool = Field( + False, + description="Require each country to be autarkic by removing all cross-border lines and links. `electricity: autarky` must be enabled.", + ) + + +class ElectricityConfig(BaseModel): + """Configuration for `electricity` settings.""" + + voltages: list[float] = Field( + default_factory=lambda: [220.0, 300.0, 330.0, 380.0, 400.0, 500.0, 750.0], + description="Voltage levels to consider.", + ) + base_network: Literal["entsoegridkit", "osm", "tyndp"] = Field( + "osm", + description="Specify the underlying base network, i.e. GridKit (based on ENTSO-E web map extract), OpenStreetMap (OSM), or TYNDP.", + ) + gaslimit_enable: bool = Field( + False, + description="Add an overall absolute gas limit configured in `electricity: gaslimit`.", + ) + gaslimit: float | bool = Field( + False, + description="Global gas usage limit.", + ) + co2limit_enable: bool = Field( + False, + description="Add an overall absolute carbon-dioxide emissions limit configured in `electricity: co2limit` in `prepare_network`. **Warning:** This option should currently only be used with electricity-only networks, not for sector-coupled networks.", + ) + co2limit: float = Field( + 7.75e7, + description="Cap on total annual system carbon dioxide emissions.", + ) + co2base: float = Field( + 1.487e9, + description="Reference value of total annual system carbon dioxide emissions if relative emission reduction target is specified in `{opts}` wildcard.", + ) + operational_reserve: _OperationalReserveConfig = Field( + default_factory=_OperationalReserveConfig, + description="Settings for reserve requirements following `GenX `_.", + ) + max_hours: _MaxHoursConfig = Field( + default_factory=_MaxHoursConfig, + description="Maximum storage hours configuration.", + ) + extendable_carriers: _ExtendableCarriersConfig = Field( + default_factory=_ExtendableCarriersConfig, + description="Defines which carriers are extendable during optimization.", + ) + powerplants_filter: str | bool = Field( + "(DateOut >= 2024 or DateOut != DateOut) and not (Country == 'Germany' and Fueltype == 'Nuclear')", + description="Filter query for the default powerplant database.", + ) + custom_powerplants: str | bool = Field( + False, + description="Filter query for the custom powerplant database.", + ) + everywhere_powerplants: list[str] = Field( + default_factory=list, + description="List of conventional power plants to add to every node in the model with zero initial capacity. To be used in combination with `extendable_carriers` to allow for building conventional powerplants irrespective of existing locations.", + ) + conventional_carriers: list[str] = Field( + default_factory=lambda: [ + "nuclear", + "oil", + "OCGT", + "CCGT", + "coal", + "lignite", + "geothermal", + "biomass", + ], + description="List of conventional power plants to include in the model from `resources/powerplants_s_{clusters}.csv`. If an included carrier is also listed in `extendable_carriers`, the capacity is taken as a lower bound.", + ) + renewable_carriers: list[str] = Field( + default_factory=lambda: [ + "solar", + "solar-hsat", + "onwind", + "offwind-ac", + "offwind-dc", + "offwind-float", + "hydro", + ], + description="List of renewable generators to include in the model.", + ) + estimate_renewable_capacities: _EstimateRenewableCapacitiesConfig = Field( + default_factory=_EstimateRenewableCapacitiesConfig, + description="Configuration for estimating renewable capacities.", + ) + autarky: _AutarkyConfig = Field( + default_factory=_AutarkyConfig, + description="Autarky configuration.", + ) + transmission_limit: str = Field( + "vopt", + description="Limit on transmission expansion. The first part can be `v` (for setting a limit on line volume) or `c` (for setting a limit on line cost). The second part can be `opt` or a float bigger than one (e.g. 1.25). If `opt` is chosen line expansion is optimised according to its capital cost (where the choice `v` only considers overhead costs for HVDC transmission lines, while `c` uses more accurate costs distinguishing between overhead and underwater sections and including inverter pairs). The setting `v1.25` will limit the total volume of line expansion to 25% of currently installed capacities weighted by individual line lengths. The setting `c1.25` will allow to build a transmission network that costs no more than 25 % more than the current system.", + ) + + model_config = ConfigDict(populate_by_name=True) diff --git a/scripts/lib/validation/config/enable.py b/scripts/lib/validation/config/enable.py new file mode 100644 index 000000000..74b529854 --- /dev/null +++ b/scripts/lib/validation/config/enable.py @@ -0,0 +1,22 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +""" +Enable configuration. + +See docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#enable +""" + +from pydantic import Field + +from scripts.lib.validation.config._base import ConfigModel + + +class EnableConfig(ConfigModel): + """Configuration for `enable` settings.""" + + drop_leap_day: bool = Field( + True, + description="Switch to drop February 29 from all time-dependent data in leap years.", + ) diff --git a/scripts/lib/validation/config/energy.py b/scripts/lib/validation/config/energy.py new file mode 100644 index 000000000..29898c600 --- /dev/null +++ b/scripts/lib/validation/config/energy.py @@ -0,0 +1,30 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +""" +Energy configuration. + +See docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#energy +""" + +from pydantic import Field + +from scripts.lib.validation.config._base import ConfigModel + + +class EnergyConfig(ConfigModel): + """Configuration for `energy` settings.""" + + energy_totals_year: int = Field( + 2019, + description="The year for the sector energy use. The year must be available in the Eurostat report.", + ) + base_emissions_year: int = Field( + 1990, + description="The base year for the sector emissions. See `European Environment Agency (EEA) `_.", + ) + emissions: str = Field( + "CO2", + description="Specify which sectoral emissions are taken into account. Data derived from EEA. Currently only CO2 is implemented.", + ) diff --git a/scripts/lib/validation/config/existing_capacities.py b/scripts/lib/validation/config/existing_capacities.py new file mode 100644 index 000000000..105760211 --- /dev/null +++ b/scripts/lib/validation/config/existing_capacities.py @@ -0,0 +1,66 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +""" +Existing capacities configuration. + +See docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#existing-capacities +""" + +from pydantic import Field + +from scripts.lib.validation.config._base import ConfigModel + + +class ExistingCapacitiesConfig(ConfigModel): + """Configuration for `existing_capacities` settings.""" + + grouping_years_power: list[int] = Field( + default_factory=lambda: [ + 1920, + 1950, + 1955, + 1960, + 1965, + 1970, + 1975, + 1980, + 1985, + 1990, + 1995, + 2000, + 2005, + 2010, + 2015, + 2020, + 2025, + ], + description="Intervals to group existing capacities for power.", + ) + grouping_years_heat: list[int] = Field( + default_factory=lambda: [ + 1980, + 1985, + 1990, + 1995, + 2000, + 2005, + 2010, + 2015, + 2019, + ], + description="Intervals to group existing capacities for heat.", + ) + threshold_capacity: float = Field( + 10, + description="Capacities (MW) of generators and links below threshold are removed during add_existing_capacities.", + ) + default_heating_lifetime: int = Field( + 20, + description="Default lifetime for heating technologies (years).", + ) + conventional_carriers: list[str] = Field( + default_factory=lambda: ["lignite", "coal", "oil", "uranium"], + description="List of conventional power plants to include in the sectoral network.", + ) diff --git a/scripts/lib/validation/config/foresight.py b/scripts/lib/validation/config/foresight.py new file mode 100644 index 000000000..86d92c813 --- /dev/null +++ b/scripts/lib/validation/config/foresight.py @@ -0,0 +1,22 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +""" +Foresight configuration. + +See docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#foresight +""" + +from typing import Literal + +from pydantic import Field, RootModel + + +class ForesightConfig(RootModel[Literal["overnight", "myopic", "perfect"]]): + """Configuration for `foresight` settings.""" + + root: Literal["overnight", "myopic", "perfect"] = Field( + "overnight", + description="See Foresight Options for detail explanations.", + ) diff --git a/scripts/lib/validation/config/industry.py b/scripts/lib/validation/config/industry.py new file mode 100644 index 000000000..9af8d343d --- /dev/null +++ b/scripts/lib/validation/config/industry.py @@ -0,0 +1,210 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +""" +Industry configuration. + +See docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#industry +""" + +from pydantic import Field + +from scripts.lib.validation.config._base import ConfigModel + + +class IndustryConfig(ConfigModel): + """Configuration for `industry` settings.""" + + St_primary_fraction: dict[int, float] = Field( + default_factory=lambda: { + 2020: 0.6, + 2025: 0.55, + 2030: 0.5, + 2035: 0.45, + 2040: 0.4, + 2045: 0.35, + 2050: 0.3, + }, + description="The fraction of steel produced via primary route versus secondary route (scrap+EAF). Current fraction is 0.6.", + ) + DRI_fraction: dict[int, float] = Field( + default_factory=lambda: { + 2020: 0, + 2025: 0, + 2030: 0.05, + 2035: 0.2, + 2040: 0.4, + 2045: 0.7, + 2050: 1, + }, + description="The fraction of the primary route DRI + EAF.", + ) + H2_DRI: float = Field( + 1.7, + description="The hydrogen consumption in Direct Reduced Iron (DRI) Mwh_H2 LHV/ton_Steel from 51kgH2/tSt in `Vogl et al (2018) `_.", + ) + elec_DRI: float = Field( + 0.322, + description="The electricity consumed in Direct Reduced Iron (DRI) shaft. From `HYBRIT brochure `_.", + ) + Al_primary_fraction: dict[int, float] = Field( + default_factory=lambda: { + 2020: 0.4, + 2025: 0.375, + 2030: 0.35, + 2035: 0.325, + 2040: 0.3, + 2045: 0.25, + 2050: 0.2, + }, + description="The fraction of aluminium produced via the primary route versus scrap. Current fraction is 0.4.", + ) + MWh_NH3_per_tNH3: float = Field( + 5.166, + description="The energy amount per ton of ammonia (LHV).", + ) + MWh_CH4_per_tNH3_SMR: float = Field( + 10.8, + description="The energy amount of methane needed to produce a ton of ammonia using steam methane reforming (SMR). Value derived from 2012's demand from `Center for European Policy Studies (2008) `_.", + ) + MWh_elec_per_tNH3_SMR: float = Field( + 0.7, + description="The energy amount of electricity needed to produce a ton of ammonia using steam methane reforming (SMR). same source, assuming 94-6% split methane-elec of total energy demand 11.5 MWh/tNH3.", + ) + MWh_H2_per_tNH3_electrolysis: float = Field( + 5.93, + description="The energy amount of hydrogen needed to produce a ton of ammonia using Haber–Bosch process. From `Wang et al (2018) `_, Base value assumed around 0.197 tH2/tHN3 (>3/17 since some H2 lost and used for energy).", + ) + MWh_elec_per_tNH3_electrolysis: float = Field( + 0.2473, + description="The energy amount of electricity needed to produce a ton of ammonia using Haber–Bosch process. From `Wang et al (2018) `_, Table 13 (air separation and HB).", + ) + MWh_NH3_per_MWh_H2_cracker: float = Field( + 1.46, + description="The energy amount of amonia needed to produce an energy amount hydrogen using ammonia cracker.", + ) + NH3_process_emissions: float = Field( + 24.5, + description="The emission of ammonia production from steam methane reforming (SMR). From UNFCCC for 2015 for EU28.", + ) + petrochemical_process_emissions: float = Field( + 25.5, + description="The emission of petrochemical production. From UNFCCC for 2015 for EU28.", + ) + HVC_primary_fraction: dict[int, float] = Field( + default_factory=lambda: { + 2020: 0.88, + 2025: 0.85, + 2030: 0.78, + 2035: 0.7, + 2040: 0.6, + 2045: 0.5, + 2050: 0.4, + }, + description="The fraction of high value chemicals (HVC) produced via primary route.", + ) + HVC_mechanical_recycling_fraction: dict[int, float] = Field( + default_factory=lambda: { + 2020: 0.12, + 2025: 0.15, + 2030: 0.18, + 2035: 0.21, + 2040: 0.24, + 2045: 0.27, + 2050: 0.30, + }, + description="The fraction of high value chemicals (HVC) produced using mechanical recycling.", + ) + HVC_chemical_recycling_fraction: dict[int, float] = Field( + default_factory=lambda: { + 2020: 0.0, + 2025: 0.0, + 2030: 0.04, + 2035: 0.08, + 2040: 0.12, + 2045: 0.16, + 2050: 0.20, + }, + description="The fraction of high value chemicals (HVC) produced using chemical recycling.", + ) + HVC_environment_sequestration_fraction: float = Field( + 0.0, + description="The fraction of high value chemicals (HVC) put into landfill resulting in additional carbon sequestration. The default value is 0.", + ) + waste_to_energy: bool = Field( + False, + description="Switch to enable expansion of waste to energy CHPs for conversion of plastics. Default is false.", + ) + waste_to_energy_cc: bool = Field( + False, + description="Switch to enable expansion of waste to energy CHPs for conversion of plastics with carbon capture. Default is false.", + ) + sector_ratios_fraction_future: dict[int, float] = Field( + default_factory=lambda: { + 2020: 0.0, + 2025: 0.05, + 2030: 0.2, + 2035: 0.45, + 2040: 0.7, + 2045: 0.85, + 2050: 1.0, + }, + description="The fraction of total progress in fuel and process switching achieved in the industry sector.", + ) + basic_chemicals_without_NH3_production_today: float = Field( + 69.0, + description="The amount of basic chemicals produced without ammonia (= 86 Mtethylene-equiv - 17 MtNH3).", + ) + HVC_production_today: float = Field( + 52.0, + description="The amount of high value chemicals (HVC) produced. This includes ethylene, propylene and BTX. From `DECHEMA (2017) `_, Figure 16, page 107.", + ) + MWh_elec_per_tHVC_mechanical_recycling: float = Field( + 0.547, + description="The energy amount of electricity needed to produce a ton of high value chemical (HVC) using mechanical recycling. From SI of `Meys et al (2020) `_, Table S5, for HDPE, PP, PS, PET. LDPE would be 0.756.", + ) + MWh_elec_per_tHVC_chemical_recycling: float = Field( + 6.9, + description="The energy amount of electricity needed to produce a ton of high value chemical (HVC) using chemical recycling. The default value is based on pyrolysis and electric steam cracking. From `Material Economics (2019) `_, page 125.", + ) + chlorine_production_today: float = Field( + 9.58, + description="The amount of chlorine produced. From `DECHEMA (2017) `_, Table 7, page 43.", + ) + MWh_elec_per_tCl: float = Field( + 3.6, + description="The energy amount of electricity needed to produce a ton of chlorine. From `DECHEMA (2017) `_, Table 6 page 43.", + ) + MWh_H2_per_tCl: float = Field( + -0.9372, + description="The energy amount of hydrogen needed to produce a ton of chlorine. The value is negative since hydrogen produced in chloralkali process. From `DECHEMA (2017) `_, page 43.", + ) + methanol_production_today: float = Field( + 1.5, + description="The amount of methanol produced. From `DECHEMA (2017) `_, page 62.", + ) + MWh_elec_per_tMeOH: float = Field( + 0.167, + description="The energy amount of electricity needed to produce a ton of methanol. From `DECHEMA (2017) `_, Table 14, page 65.", + ) + MWh_CH4_per_tMeOH: float = Field( + 10.25, + description="The energy amount of methane needed to produce a ton of methanol. From `DECHEMA (2017) `_, Table 14, page 65.", + ) + MWh_MeOH_per_tMeOH: float = Field( + 5.528, + description="The energy amount per ton of methanol (LHV). From `DECHEMA (2017) `_, page 74.", + ) + hotmaps_locate_missing: bool = Field( + False, + description="Locate industrial sites without valid locations based on city and countries.", + ) + reference_year: int = Field( + 2019, + description="The year used as the baseline for industrial energy demand and production. Data extracted from `JRC-IDEES 2015 `_.", + ) + oil_refining_emissions: float = Field( + 0.013, + description="The emissions from oil fuel processing (e.g. oil in petrochemical refinieries). The default value of 0.013 tCO2/MWh is based on DE statistics for 2019; the EU value is very similar.", + ) diff --git a/scripts/lib/validation/config/lines.py b/scripts/lib/validation/config/lines.py new file mode 100644 index 000000000..7ef71cc85 --- /dev/null +++ b/scripts/lib/validation/config/lines.py @@ -0,0 +1,91 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +""" +Lines configuration. + +See docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#lines +""" + +from typing import Literal + +from pydantic import BaseModel, Field + +from scripts.lib.validation.config._base import ConfigModel + + +class _DynamicLineRatingConfig(ConfigModel): + """Configuration for `lines.dynamic_line_rating` settings.""" + + activate: bool = Field( + False, + description="Whether to take dynamic line rating into account.", + ) + cutout: str | list[str] = Field( + "default", + description="Specifies the weather data cutout file(s) to use.", + ) + correction_factor: float = Field( + 0.95, + description="Factor to compensate for overestimation of wind speeds in hourly averaged wind data.", + ) + max_voltage_difference: float | Literal[False] = Field( + False, + description="Maximum voltage angle difference in degrees or 'false' to disable.", + ) + max_line_rating: float | Literal[False] = Field( + False, + description="Maximum line rating relative to nominal capacity without DLR, e.g. 1.3 or 'false' to disable.", + ) + + +class LinesConfig(BaseModel): + """Configuration for `lines` settings.""" + + types: dict[float, str] = Field( + default_factory=lambda: { + 63.0: "94-AL1/15-ST1A 20.0", + 66.0: "94-AL1/15-ST1A 20.0", + 90.0: "184-AL1/30-ST1A 110.0", + 110.0: "184-AL1/30-ST1A 110.0", + 132.0: "243-AL1/39-ST1A 110.0", + 150.0: "243-AL1/39-ST1A 110.0", + 220.0: "Al/St 240/40 2-bundle 220.0", + 300.0: "Al/St 240/40 3-bundle 300.0", + 330.0: "Al/St 240/40 3-bundle 300.0", + 380.0: "Al/St 240/40 4-bundle 380.0", + 400.0: "Al/St 240/40 4-bundle 380.0", + 500.0: "Al/St 240/40 4-bundle 380.0", + 750.0: "Al/St 560/50 4-bundle 750.0", + }, + description="Specifies line types to assume for the different voltage levels of the ENTSO-E grid extraction. Should normally handle voltage levels 220, 300, and 380 kV.", + ) + s_max_pu: float = Field( + 0.7, + description="Correction factor for line capacities (`s_nom`) to approximate N-1 security and reserve capacity for reactive power flows.", + ) + s_nom_max: float = Field( + float("inf"), + description="Global upper limit for the maximum capacity of each extendable line (MW).", + ) + max_extension: float = Field( + 20000, + description="Upper limit for the extended capacity of each extendable line (MW).", + ) + length_factor: float = Field( + 1.25, + description="Correction factor to account for the fact that buses are *not* connected by lines through air-line distance.", + ) + reconnect_crimea: bool = Field( + True, + description="Whether to reconnect Crimea to the Ukrainian grid.", + ) + under_construction: Literal["zero", "remove", "keep"] = Field( + "keep", + description="Specifies how to handle lines which are currently under construction.", + ) + dynamic_line_rating: _DynamicLineRatingConfig = Field( + default_factory=_DynamicLineRatingConfig, + description="Configuration for dynamic line rating.", + ) diff --git a/scripts/lib/validation/config/links.py b/scripts/lib/validation/config/links.py new file mode 100644 index 000000000..a994c20af --- /dev/null +++ b/scripts/lib/validation/config/links.py @@ -0,0 +1,44 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +""" +Links configuration. + +See docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#links +""" + +from typing import Literal + +from pydantic import Field + +from scripts.lib.validation.config._base import ConfigModel + + +class LinksConfig(ConfigModel): + """Configuration for `links` settings.""" + + p_max_pu: float = Field( + 1.0, + description="Correction factor for link capacities `p_nom`.", + ) + p_min_pu: float = Field( + -1.0, + description="Correction factor for link capacities `p_nom`.", + ) + p_nom_max: float = Field( + float("inf"), + description="Global upper limit for the maximum capacity of each extendable DC link (MW).", + ) + max_extension: float = Field( + 30000, + description="Upper limit for the extended capacity of each extendable DC link (MW).", + ) + length_factor: float = Field( + 1.25, + description="Correction factor to account for the fact that buses are *not* connected by links through air-line distance.", + ) + under_construction: Literal["zero", "remove", "keep"] = Field( + "keep", + description="Specifies how to handle lines which are currently under construction.", + ) diff --git a/scripts/lib/validation/config/load.py b/scripts/lib/validation/config/load.py new file mode 100644 index 000000000..e23152b10 --- /dev/null +++ b/scripts/lib/validation/config/load.py @@ -0,0 +1,72 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +""" +Load configuration. + +See docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#load +""" + +from pydantic import BaseModel, Field + +from scripts.lib.validation.config._base import ConfigModel + + +class _FillGapsConfig(ConfigModel): + """Configuration for `load.fill_gaps` settings.""" + + enable: bool = Field( + True, + description="Whether to fill gaps using interpolation for small gaps and time shift for large gaps.", + ) + interpolate_limit: int = Field( + 3, + description="Maximum gap size (consecutive nans) which interpolated linearly.", + ) + time_shift_for_large_gaps: str = Field( + "1w", + description="Periods which are used for copying time-slices in order to fill large gaps of nans. Have to be valid `pandas` period strings.", + ) + + +class _DistributionKeyConfig(BaseModel): + """Configuration for `load.distribution_key` settings.""" + + gdp: float = Field( + 0.6, + description="Weighting factor for the GDP data in the distribution key.", + ) + population: float = Field( + 0.4, + description="Weighting factor for the population data in the distribution key.", + ) + + +class LoadConfig(BaseModel): + """Configuration for `load` settings.""" + + fill_gaps: _FillGapsConfig = Field( + default_factory=_FillGapsConfig, + description="Gaps filling strategy used.", + ) + manual_adjustments: bool = Field( + True, + description="Whether to adjust the load data manually according to the function in `manual_adjustment`.", + ) + scaling_factor: float = Field( + 1.0, + description="Global correction factor for the load time series.", + ) + fixed_year: int | bool = Field( + False, + description="To specify a fixed year for the load time series that deviates from the snapshots' year.", + ) + supplement_synthetic: bool = Field( + True, + description="Whether to supplement missing data for selected time period should be supplemented by synthetic data from `Zenodo `_.", + ) + distribution_key: _DistributionKeyConfig = Field( + default_factory=_DistributionKeyConfig, + description="Distribution key for spatially disaggregating the per-country electricity demand data.", + ) diff --git a/scripts/lib/validation/config/overpass_api.py b/scripts/lib/validation/config/overpass_api.py new file mode 100644 index 000000000..5ab095fc2 --- /dev/null +++ b/scripts/lib/validation/config/overpass_api.py @@ -0,0 +1,49 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +""" +Overpass API configuration. +""" + +from pydantic import BaseModel, Field + +from scripts.lib.validation.config._base import ConfigModel + + +class _UserAgentConfig(ConfigModel): + """Configuration for `overpass_api.user_agent` settings.""" + + project_name: str = Field( + "PyPSA-Eur", + description="Project name used to identify the user agent of the Overpass API requests.", + ) + email: str = Field( + "contact@pypsa.org", + description="Contact email address for the project using the Overpass API.", + ) + website: str = Field( + "https://github.com/PyPSA/pypsa-eur", + description="Website URL for the project using the Overpass API.", + ) + + +class OverpassApiConfig(BaseModel): + """Configuration for `overpass_api` settings.""" + + url: str = Field( + "https://overpass-api.de/api/interpreter", + description="Overpass API endpoint URL. See `Overpass API Wiki `_ for available public instances.", + ) + max_tries: int = Field( + 5, + description="Maximum retry attempts for Overpass API requests. Please be respectful to the Overpass API fair use policy of the individual instances.", + ) + timeout: int = Field( + 600, + description="Timeout in seconds for Overpass API requests.", + ) + user_agent: _UserAgentConfig = Field( + default_factory=_UserAgentConfig, + description="Please provide your own user agent details when using the Overpass API,so the instance operators can contact you if needed.", + ) diff --git a/scripts/lib/validation/config/pypsa_eur.py b/scripts/lib/validation/config/pypsa_eur.py new file mode 100644 index 000000000..2ffbf0b57 --- /dev/null +++ b/scripts/lib/validation/config/pypsa_eur.py @@ -0,0 +1,49 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +""" +PyPSA-Eur component configuration. + +Regulates what components with which carriers are kept from PyPSA-Eur. +Some technologies are removed because they are implemented differently +(e.g. battery or H2 storage) or have different year-dependent costs. +""" + +from pydantic import Field + +from scripts.lib.validation.config._base import ConfigModel + + +class PypsaEurConfig(ConfigModel): + """Configuration for `pypsa_eur` settings.""" + + Bus: list[str] = Field( + default_factory=lambda: ["AC"], + description="Bus carriers to keep from PyPSA-Eur.", + ) + Link: list[str] = Field( + default_factory=lambda: ["DC"], + description="Link carriers to keep from PyPSA-Eur.", + ) + Generator: list[str] = Field( + default_factory=lambda: [ + "onwind", + "offwind-ac", + "offwind-dc", + "offwind-float", + "solar-hsat", + "solar", + "ror", + "nuclear", + ], + description="Generator carriers to keep from PyPSA-Eur.", + ) + StorageUnit: list[str] = Field( + default_factory=lambda: ["PHS", "hydro"], + description="StorageUnit carriers to keep from PyPSA-Eur.", + ) + Store: list[str] = Field( + default_factory=list, + description="Store carriers to keep from PyPSA-Eur.", + ) diff --git a/scripts/lib/validation/config/renewable.py b/scripts/lib/validation/config/renewable.py new file mode 100644 index 000000000..85cbf124d --- /dev/null +++ b/scripts/lib/validation/config/renewable.py @@ -0,0 +1,357 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +""" +Renewable energy configuration. + +See docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#renewable +""" + +from pydantic import BaseModel, ConfigDict, Field + +from scripts.lib.validation.config._base import ConfigModel + + +class _WindResourceConfig(ConfigModel): + """Configuration for wind resource settings.""" + + method: str = Field("wind", description="A superordinate technology type.") + turbine: str | dict[int, str] = Field( + ..., + description="Specifies the turbine type and its characteristic power curve. Can be a string or a dictionary with years as keys which denote the year another turbine model becomes available.", + ) + smooth: bool = Field( + False, + description="Switch to apply a gaussian kernel density smoothing to the power curve.", + ) + add_cutout_windspeed: bool = Field( + True, + description="Whether to add cutout windspeed data.", + ) + + +class _SolarResourceConfig(BaseModel): + """Configuration for solar resource settings.""" + + method: str = Field("pv", description="A superordinate technology type.") + panel: str | dict[int, str] = Field( + "CSi", + description="Specifies the solar panel technology and its characteristic attributes. Can be a string or a dictionary with years as keys which denote the year another panel model becomes available.", + ) + orientation: dict[str, float] = Field( + default_factory=lambda: {"slope": 35.0, "azimuth": 180.0}, + description="Panel orientation with slope and azimuth.", + ) + tracking: str | None = Field( + None, + description="Tracking type (e.g., 'horizontal').", + ) + + +class _CorineConfig(BaseModel): + """Configuration for CORINE land cover settings.""" + + grid_codes: list[int] = Field( + ..., + description="Specifies areas according to CORINE Land Cover codes which are generally eligible for wind turbine placement.", + ) + distance: float = Field( + 1000, + description="Distance in meters to keep from areas specified in `distance_grid_codes`.", + ) + distance_grid_codes: list[int] = Field( + default_factory=list, + description="Specifies areas according to CORINE Land Cover codes to which wind turbines must maintain a distance specified in the setting `distance`.", + ) + + +class _OnwindConfig(BaseModel): + """Configuration for onshore wind.""" + + cutout: str | list[str] = Field( + "default", description="Specifies the weather data cutout file(s) to use." + ) + resource: _WindResourceConfig = Field( + default_factory=lambda: _WindResourceConfig(turbine="Vestas_V112_3MW"), + description="Wind resource configuration.", + ) + resource_classes: int = Field( + 1, description="Number of resource classes per clustered region." + ) + capacity_per_sqkm: float = Field( + 3, description="Allowable density of wind turbine placement." + ) + correction_factor: float = Field( + 1.0, description="Correction factor for capacity factor time series." + ) + corine: _CorineConfig = Field( + default_factory=lambda: _CorineConfig( + grid_codes=[ + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 31, + 32, + ], + distance=1000, + distance_grid_codes=[1, 2, 3, 4, 5, 6], + ), + description="CORINE land cover configuration.", + ) + luisa: bool | dict = Field(False, description="LUISA land cover configuration.") + natura: bool = Field( + True, + description="Switch to exclude `Natura 2000 `_ natural protection areas. Area is excluded if `true`.", + ) + excluder_resolution: float = Field( + 100, + description="Resolution in meters on which to perform geographical eligibility analysis.", + ) + clip_p_max_pu: float = Field( + 0.01, + description="To avoid too small values in the renewables` per-unit availability time series values below this threshold are set to zero.", + ) + + +class _OffwindConfig(BaseModel): + """Configuration for offshore wind.""" + + cutout: str | list[str] = Field( + "default", description="Specifies the weather data cutout file(s) to use." + ) + resource: _WindResourceConfig = Field( + default_factory=lambda: _WindResourceConfig( + turbine="NREL_ReferenceTurbine_2020ATB_5.5MW" + ), + description="Wind resource configuration.", + ) + resource_classes: int = Field( + 1, description="Number of resource classes per clustered region." + ) + capacity_per_sqkm: float = Field( + 2, description="Allowable density of wind turbine placement." + ) + correction_factor: float = Field( + 0.8855, description="Correction factor for capacity factor time series." + ) + corine: list[int] = Field( + default_factory=lambda: [44, 255], + description="Specifies areas according to CORINE Land Cover codes which are generally eligible for AC-connected offshore wind turbine placement.", + ) + luisa: bool | list[int] = Field( + False, + description="Specifies areas according to the LUISA Base Map codes which are generally eligible for AC-connected offshore wind turbine placement.", + ) + natura: bool = Field( + True, + description="Switch to exclude `Natura 2000 `_ natural protection areas. Area is excluded if `true`.", + ) + ship_threshold: float = Field( + 400, description="Ship density threshold from which areas are excluded." + ) + max_depth: float | None = Field( + None, + description="Maximum sea water depth in meters at which wind turbines can be built. Maritime areas with deeper waters are excluded in the process of calculating the AC-connected offshore wind potential.", + ) + min_depth: float | None = Field(None, description="Minimum water depth in meters.") + max_shore_distance: float | None = Field( + None, + description="Maximum distance to the shore in meters above which wind turbines cannot be built. Such areas are excluded in the process of calculating the AC-connected offshore wind potential.", + ) + min_shore_distance: float | None = Field( + None, + description="Minimum distance to the shore in meters below which wind turbines cannot be built. Such areas close to the shore are excluded in the process of calculating the AC-connected offshore wind potential.", + ) + excluder_resolution: float = Field( + 200, + description="Resolution in meters on which to perform geographical eligibility analysis.", + ) + clip_p_max_pu: float = Field( + 0.01, + description="To avoid too small values in the renewables` per-unit availability time series values below this threshold are set to zero.", + ) + landfall_length: float | str = Field( + 20, + description="Fixed length of the cable connection that is onshorelandfall in km. If 'centroid', the length is calculated as the distance to centroid of the onshore bus.", + ) + + +class _SolarConfig(BaseModel): + """Configuration for solar PV.""" + + cutout: str | list[str] = Field( + "default", description="Specifies the weather data cutout file(s) to use." + ) + resource: _SolarResourceConfig = Field( + default_factory=_SolarResourceConfig, + description="Solar resource configuration.", + ) + resource_classes: int = Field( + 1, description="Number of resource classes per clustered region." + ) + capacity_per_sqkm: float = Field( + 5.1, description="Allowable density of solar panel placement." + ) + correction_factor: float = Field( + 1.0, + description="A correction factor for the capacity factor (availability) time series.", + ) + corine: list[int] = Field( + default_factory=lambda: [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 26, + 31, + 32, + ], + description="Specifies areas according to CORINE Land Cover codes which are generally eligible for solar panel placement.", + ) + luisa: bool | list[int] = Field( + False, + description="Specifies areas according to the LUISA Base Map codes which are generally eligible for solar panel placement.", + ) + natura: bool = Field( + True, + description="Switch to exclude `Natura 2000 `_ natural protection areas. Area is excluded if `true`.", + ) + excluder_resolution: float = Field( + 100, + description="Resolution in meters on which to perform geographical eligibility analysis.", + ) + clip_p_max_pu: float = Field( + 0.01, + description="To avoid too small values in the renewables` per-unit availability time series values below this threshold are set to zero.", + ) + + +class _HydroConfig(BaseModel): + """Configuration for hydropower.""" + + cutout: str | list[str] = Field( + "default", description="Specifies the weather data cutout file(s) to use." + ) + carriers: list[str] = Field( + default_factory=lambda: ["ror", "PHS", "hydro"], + description="Specifies the types of hydro power plants to build per-unit availability time series for. 'ror' stands for run-of-river plants, 'PHS' represents pumped-hydro storage, and 'hydro' stands for hydroelectric dams.", + ) + PHS_max_hours: float = Field( + 6, + description="Maximum state of charge capacity of the pumped-hydro storage (PHS) in terms of hours at full output capacity `p_nom`. Cf. `PyPSA documentation `_.", + ) + hydro_max_hours: str | float = Field( + "energy_capacity_totals_by_country", + description="Maximum state of charge capacity of the pumped-hydro storage (PHS) in terms of hours at full output capacity `p_nom` or heuristically determined. Cf. `PyPSA documentation `_.", + ) + flatten_dispatch: bool = Field( + False, + description="Consider an upper limit for the hydro dispatch. The limit is given by the average capacity factor plus the buffer given in `flatten_dispatch_buffer`.", + ) + flatten_dispatch_buffer: float = Field( + 0.2, + description="If `flatten_dispatch` is true, specify the value added above the average capacity factor.", + ) + clip_min_inflow: float = Field( + 1.0, + description="To avoid too small values in the inflow time series, values below this threshold (MW) are set to zero.", + ) + eia_norm_year: bool | int = Field( + False, + description="To specify a specific year by which hydro inflow is normed that deviates from the snapshots' year.", + ) + eia_correct_by_capacity: bool = Field( + False, + description="Correct EIA annual hydro generation data by installed capacity.", + ) + eia_approximate_missing: bool = Field( + False, + description="Approximate hydro generation data for years not included in EIA dataset through a regression based on annual runoff.", + ) + + +class RenewableConfig(BaseModel): + """Configuration for `renewable` settings.""" + + onwind: _OnwindConfig = Field( + default_factory=_OnwindConfig, + description="Onshore wind configuration.", + ) + offwind_ac: _OffwindConfig = Field( + default_factory=lambda: _OffwindConfig( + max_depth=60, + max_shore_distance=30000, + landfall_length=20, + ), + alias="offwind-ac", + description="Offshore wind AC configuration.", + ) + offwind_dc: _OffwindConfig = Field( + default_factory=lambda: _OffwindConfig( + max_depth=60, + min_shore_distance=30000, + landfall_length=30, + ), + alias="offwind-dc", + description="Offshore wind DC configuration.", + ) + offwind_float: _OffwindConfig = Field( + default_factory=lambda: _OffwindConfig( + resource=_WindResourceConfig(turbine="NREL_ReferenceTurbine_5MW_offshore"), + min_depth=60, + max_depth=1000, + landfall_length=40, + ), + alias="offwind-float", + description="Floating offshore wind configuration.", + ) + solar: _SolarConfig = Field( + default_factory=_SolarConfig, + description="Solar PV configuration.", + ) + solar_hsat: _SolarConfig = Field( + default_factory=lambda: _SolarConfig( + resource=_SolarResourceConfig(tracking="horizontal"), + capacity_per_sqkm=4.43, + ), + alias="solar-hsat", + description="Solar PV with horizontal single-axis tracking configuration.", + ) + hydro: _HydroConfig = Field( + default_factory=_HydroConfig, + description="Hydropower configuration.", + ) + + model_config = ConfigDict(populate_by_name=True) diff --git a/scripts/lib/validation/config/run.py b/scripts/lib/validation/config/run.py new file mode 100644 index 000000000..f0731c3b3 --- /dev/null +++ b/scripts/lib/validation/config/run.py @@ -0,0 +1,75 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +""" +Run configuration block. + +See # docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#run +""" + +from pydantic import Field + +from scripts.lib.validation.config._base import ConfigModel + + +class _ScenariosConfig(ConfigModel): + """Configuration for `run.scenarios` level.""" + + enable: bool = Field( + False, + description="Switch to select whether workflow should generate scenarios based on ``file``.", + ) + file: str = Field( + "config/scenarios.yaml", + description="Path to the scenario yaml file. The scenario file contains config overrides for each scenario. In order to be taken account, ``run: scenarios`` has to be set to ``true`` and ``run: name`` has to be a subset of top level keys given in the scenario file. In order to automatically create a `scenario.yaml` file based on a combination of settings, alter and use the ``config/create_scenarios.py`` script in the ``config`` directory.", + examples=["config/scenarios.yaml"], + ) + + +class _SharedResourcesConfig(ConfigModel): + """Configuration for `run.shared_resources` level.""" + + policy: bool | str = Field( + False, + description="Boolean switch to select whether resources should be shared across runs. If a string is passed, this is used as a subdirectory name for shared resources. If set to 'base', only resources before creating the elec.nc file are shared.", + examples=[False, "base"], + ) + exclude: list[str] = Field( + default_factory=list, + description="For the case shared_resources=base, specify additional files that should not be shared across runs.", + ) + + +class RunConfig(ConfigModel): + """Configuration for top level `run` settings.""" + + prefix: str = Field( + "", + description="Prefix for the run name which is used as a top-layer directory name in the results and resources folders.", + ) + name: str | list[str] = Field( + "", + description="Specify a name for your run. Results will be stored under this name. If ``scenario: enable:`` is set to ``true``, the name must contain a subset of scenario names defined in ``scenario: file:``. If the name is 'all', all defined scenarios will be run.", + examples=["my-pypsa-eur-run"], + ) + + scenarios: _ScenariosConfig = Field( + default_factory=_ScenariosConfig, + description="Configuration for running multiple scenarios", + ) + + disable_progressbar: bool = Field( + False, description="Switch to select whether progressbar should be disabled." + ) + + shared_resources: _SharedResourcesConfig = Field( + default_factory=_SharedResourcesConfig, + description="Shared resources configuration for parallel execution", + ) + + use_shadow_directory: bool = Field( + False, + description="Set to ``true`` (default) if snakemake shadow directories (``shallow``) should be used. Set to ``false`` if problems occur.", + examples=[True], + ) diff --git a/scripts/lib/validation/config/scenario.py b/scripts/lib/validation/config/scenario.py new file mode 100644 index 000000000..bccaf6093 --- /dev/null +++ b/scripts/lib/validation/config/scenario.py @@ -0,0 +1,37 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +""" +Scenario configuration block. + +See docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#scenario +Wildcard docs in https://pypsa-eur.readthedocs.io/en/latest/wildcards.html +""" + +from typing import Literal + +from pydantic import Field + +from scripts.lib.validation.config._base import ConfigModel + + +class ScenarioConfig(ConfigModel): + """Configuration for top level `scenario` settings.""" + + clusters: list[int | Literal["adm", "all"]] = Field( + default_factory=lambda: [50], + description="List of ``{clusters}`` wildcards to run. Use 'adm' for administrative clustering mode, 'all' for all nodes.", + ) + opts: list[str] = Field( + default_factory=lambda: [""], + description="List of ``{opts}`` wildcards to run.", + ) + sector_opts: list[str] = Field( + default_factory=lambda: [""], + description="List of ``{sector_opts}`` wildcards to run.", + ) + planning_horizons: list[int] = Field( + default_factory=lambda: [2050], + description="List of ``{planning_horizon}`` wildcards to run.", + ) diff --git a/scripts/lib/validation/config/sector.py b/scripts/lib/validation/config/sector.py new file mode 100644 index 000000000..1c554073b --- /dev/null +++ b/scripts/lib/validation/config/sector.py @@ -0,0 +1,925 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +""" +Sector configuration. + +See docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#sector +""" + +from typing import Any + +from pydantic import BaseModel, ConfigDict, Field + +from scripts.lib.validation.config._base import ConfigModel + + +class _DistrictHeatingConfig(ConfigModel): + """Configuration for `sector.district_heating` settings.""" + + potential: float | dict[str, float] = Field( + 0.6, + description="Maximum fraction of urban demand which can be supplied by district heating. If given as dictionary, specify one value per country modeled or provide a default value with key `default` to fill values for all unspecified countries.", + ) + progress: dict[int, float] = Field( + default_factory=lambda: { + 2020: 0.0, + 2025: 0.1, + 2030: 0.25, + 2035: 0.4, + 2040: 0.55, + 2045: 0.75, + 2050: 1.0, + }, + description="Increase of today's district heating demand to potential maximum district heating share. Progress = 0 means today's district heating share. Progress = 1 means maximum fraction of urban demand is supplied by district heating.", + ) + district_heating_loss: float = Field( + 0.15, + description="Share increase in district heat demand in urban central due to heat losses.", + ) + supply_temperature_approximation: dict[str, Any] = Field( + default_factory=lambda: { + "max_forward_temperature_baseyear": { + "FR": 110, + "DK": 75, + "DE": 109, + "CZ": 130, + "FI": 115, + "PL": 130, + "SE": 102, + "IT": 90, + }, + "min_forward_temperature_baseyear": {"DE": 82}, + "return_temperature_baseyear": {"DE": 58}, + "lower_threshold_ambient_temperature": 0, + "upper_threshold_ambient_temperature": 10, + "rolling_window_ambient_temperature": 72, + "relative_annual_temperature_reduction": 0.01, + }, + description="Supply temperature approximation settings.", + ) + ptes: dict[str, Any] = Field( + default_factory=lambda: { + "dynamic_capacity": False, + "supplemental_heating": {"enable": False, "booster_heat_pump": False}, + "max_top_temperature": 90, + "min_bottom_temperature": 35, + }, + description="Pit thermal energy storage settings.", + ) + ates: dict[str, Any] = Field( + default_factory=lambda: { + "enable": False, + "suitable_aquifer_types": ["Highly productive porous aquifers"], + "aquifer_volumetric_heat_capacity": 2600, + "fraction_of_aquifer_area_available": 0.2, + "effective_screen_length": 20, + "capex_as_fraction_of_geothermal_heat_source": 0.75, + "recovery_factor": 0.6, + "marginal_cost_charger": 0.035, + "ignore_missing_regions": False, + }, + description="Aquifer thermal energy storage settings.", + ) + heat_source_cooling: float = Field( + 6, description="Cooling of heat source for heat pumps." + ) + heat_pump_cop_approximation: dict[str, Any] = Field( + default_factory=lambda: { + "refrigerant": "ammonia", + "heat_exchanger_pinch_point_temperature_difference": 5, + "isentropic_compressor_efficiency": 0.8, + "heat_loss": 0.0, + "min_delta_t_lift": 10, + }, + description="Heat pump COP approximation settings.", + ) + limited_heat_sources: dict[str, Any] = Field( + default_factory=lambda: { + "geothermal": { + "constant_temperature_celsius": 65, + "ignore_missing_regions": False, + }, + "river_water": {"constant_temperature_celsius": False}, + }, + description="Dictionary with names of limited heat sources (not air). Must be `river_water` / `geothermal` or another heat source in `Manz et al. 2024 `_.", + ) + direct_utilisation_heat_sources: list[str] = Field( + default_factory=lambda: ["geothermal"], + description="List of heat sources for direct heat utilisation in district heating. Must be in the keys of `heat_utilisation_potentials` (e.g. `geothermal`).", + ) + temperature_limited_stores: list[str] = Field( + default_factory=lambda: ["ptes"], + description="List of names for stores used as limited heat sources.", + ) + dh_areas: dict[str, Any] = Field( + default_factory=lambda: {"buffer": 1000, "handle_missing_countries": "fill"}, + description="District heating areas settings.", + ) + + +class _ResidentialHeatDsmConfig(BaseModel): + """Configuration for `sector.residential_heat.dsm` settings.""" + + enable: bool = Field( + False, + description="Enable residential heat demand-side management that allows heating systems to provide flexibility by shifting demand within configurable time periods. Models building thermal mass as energy storage.", + ) + direction: list[str] = Field( + default_factory=lambda: ["overheat", "undercool"], + description="'overheat-undercool' means both pre-heating and delayed heating are allowed. 'overheat' allows only pre-heating where buildings are heated up above target temperature and then allowed to cool down, while 'undercool' allows only delayed heating where buildings can cool below target temperature and then be heated up again.", + ) + restriction_value: dict[int, float] = Field( + default_factory=lambda: { + 2020: 0.06, + 2025: 0.16, + 2030: 0.27, + 2035: 0.36, + 2040: 0.38, + 2045: 0.39, + 2050: 0.4, + }, + description="Maximum state of charge (as fraction) for heat flexibility storage representing available thermal buffer capacity in buildings. Set to 0 for no flexibility or to 1.0 to assume that the entire heating demand can contribute to flexibility.", + ) + restriction_time: list[int] = Field( + default_factory=lambda: [10, 22], + description="Checkpoint hours (0-23) at which heat flexibility storage must return to baseline state of charge, i.e. the residence surplus or missing heat be balanced. Time is the local time for each country and bus. Default: [10, 22] creates 12-hour periods with checkpoints at 10am and 10pm.", + ) + + +class _ResidentialHeatConfig(BaseModel): + """Configuration for `sector.residential_heat` settings.""" + + dsm: _ResidentialHeatDsmConfig = Field( + default_factory=_ResidentialHeatDsmConfig, + description="Configuration options for residential heat demand-side management (DSM). See `smartEn DSM study `_ (Appendix A) for methodology.", + ) + + +class _RetrofittingConfig(BaseModel): + """Configuration for `sector.retrofitting` settings.""" + + retro_endogen: bool = Field( + False, + description="Add retrofitting as an endogenous system which co-optimise space heat savings.", + ) + cost_factor: float = Field(1.0, description="Weight costs for building renovation.") + interest_rate: float = Field( + 0.04, description="The interest rate for investment in building components." + ) + annualise_cost: bool = Field( + True, description="Annualise the investment costs of retrofitting." + ) + tax_weighting: bool = Field( + False, + description="Weight the costs of retrofitting depending on taxes in countries.", + ) + construction_index: bool = Field( + True, + description="Weight the costs of retrofitting depending on labour/material costs per country.", + ) + + +class _CHPConfig(BaseModel): + """Configuration for `sector.chp` settings.""" + + enable: bool = Field( + True, description="Add option for using Combined Heat and Power (CHP)." + ) + fuel: list[str] = Field( + default_factory=lambda: ["solid biomass", "gas"], + description='Possible options are all fuels which have an existing bus and their CO2 intensity is given in the technology data. Currently possible are "gas", "oil", "methanol", "lignite", "coal" as well as "solid biomass". For all fuels except solid biomass, the techno-economic data from gas CHP is used. For the special case of solid biomass fuel, both CHP plants with and without carbon capture are added.', + ) + micro_chp: bool = Field( + False, + description="Add option for using gas-fired Combined Heat and Power (CHP) for decentral areas.", + ) + + +class _MethanolConfig(BaseModel): + """Configuration for `sector.methanol` settings.""" + + regional_methanol_demand: bool = Field( + False, + description="Spatially resolve methanol demand. Set to true if regional CO2 constraints needed.", + ) + methanol_reforming: bool = Field(False, description="Add methanol reforming.") + methanol_reforming_cc: bool = Field( + False, description="Add methanol reforming with carbon capture." + ) + methanol_to_kerosene: bool = Field(False, description="Add methanol to kerosene.") + methanol_to_power: dict[str, bool] = Field( + default_factory=lambda: { + "ccgt": False, + "ccgt_cc": False, + "ocgt": True, + "allam": False, + }, + description="Add different methanol to power technologies.", + ) + biomass_to_methanol: bool = Field(True, description="Add biomass to methanol.") + biomass_to_methanol_cc: bool = Field( + False, description="Add biomass to methanol with carbon capture." + ) + + +class _TransmissionEfficiencyConfig(BaseModel): + """Configuration for `sector.transmission_efficiency` settings.""" + + enable: list[str] = Field( + default_factory=lambda: [ + "DC", + "H2 pipeline", + "gas pipeline", + "electricity distribution grid", + ], + description="Switch to select the carriers for which transmission efficiency is to be added. Carriers not listed assume lossless transmission.", + ) + DC: dict[str, float] = Field( + default_factory=lambda: { + "efficiency_static": 0.98, + "efficiency_per_1000km": 0.977, + }, + description="DC transmission efficiency.", + ) + H2_pipeline: dict[str, float] = Field( + default_factory=lambda: { + "efficiency_per_1000km": 1, + "compression_per_1000km": 0.018, + }, + alias="H2 pipeline", + description="H2 pipeline transmission efficiency.", + ) + gas_pipeline: dict[str, float] = Field( + default_factory=lambda: { + "efficiency_per_1000km": 1, + "compression_per_1000km": 0.01, + }, + alias="gas pipeline", + description="Gas pipeline transmission efficiency.", + ) + electricity_distribution_grid: dict[str, float] = Field( + default_factory=lambda: {"efficiency_static": 0.97}, + alias="electricity distribution grid", + description="Electricity distribution grid efficiency.", + ) + + model_config = ConfigDict(populate_by_name=True) + + +class _LimitMaxGrowthConfig(BaseModel): + """Configuration for `sector.limit_max_growth` settings.""" + + enable: bool = Field( + False, description="Add option to limit the maximum growth of a carrier." + ) + factor: float = Field( + 1.3, + description="The maximum growth factor of a carrier (e.g. 1.3 allows 30% larger than max historic growth).", + ) + max_growth: dict[str, float] = Field( + default_factory=lambda: { + "onwind": 16, + "solar": 28, + "offwind-ac": 35, + "offwind-dc": 35, + }, + description="The historic maximum growth of a carrier.", + ) + max_relative_growth: dict[str, float] = Field( + default_factory=lambda: { + "onwind": 3, + "solar": 3, + "offwind-ac": 3, + "offwind-dc": 3, + }, + description="The historic maximum relative growth of a carrier.", + ) + + +class _EnhancedGeothermalConfig(BaseModel): + """Configuration for `sector.enhanced_geothermal` settings.""" + + enable: bool = Field( + False, description="Add option to include Enhanced Geothermal Systems." + ) + flexible: bool = Field( + True, description="Add option for flexible operation (see Ricks et al. 2024)." + ) + max_hours: int = Field( + 240, + description="The maximum hours the reservoir can be charged under flexible operation.", + ) + max_boost: float = Field( + 0.25, description="The maximum boost in power output under flexible operation." + ) + var_cf: bool = Field( + True, + description="Add option for variable capacity factor (see Ricks et al. 2024).", + ) + sustainability_factor: float = Field( + 0.0025, + description="Share of sourced heat that is replenished by the earth's core (see details in `build_egs_potentials.py `_).", + ) + + +class _SolidBiomassImportConfig(BaseModel): + """Configuration for `sector.solid_biomass_import` settings.""" + + enable: bool = Field( + False, description="Add option to include solid biomass imports." + ) + price: float = Field( + 54, description="Price for importing solid biomass (currency/MWh)." + ) + max_amount: float = Field( + 1390, description="Maximum solid biomass import potential (TWh)." + ) + upstream_emissions_factor: float = Field( + 0.1, description="Upstream emissions of solid biomass imports." + ) + + +class _ImportsConfig(BaseModel): + """Configuration for `sector.imports` settings.""" + + enable: bool = Field( + False, description="Add option to include renewable energy imports." + ) + limit: float = Field( + float("inf"), description="Maximum allowed renewable energy imports (TWh)." + ) + limit_sense: str = Field("<=", description="Sense of the limit.") + price: dict[str, float] = Field( + default_factory=lambda: { + "H2": 74, + "NH3": 97, + "methanol": 121, + "gas": 122, + "oil": 125, + }, + description="Price for importing renewable energy of carrier.", + ) + + +class SectorConfig(BaseModel): + """Configuration for `sector` settings.""" + + transport: bool = Field(True, description="Flag to include transport sector.") + heating: bool = Field(True, description="Flag to include heating sector.") + biomass: bool = Field(True, description="Flag to include biomass sector.") + industry: bool = Field(True, description="Flag to include industry sector.") + shipping: bool = Field(True, description="Flag to include shipping sector.") + aviation: bool = Field(True, description="Flag to include aviation sector.") + agriculture: bool = Field(True, description="Flag to include agriculture sector.") + fossil_fuels: bool = Field( + True, description="Flag to include imports of fossil fuels." + ) + + district_heating: _DistrictHeatingConfig = Field( + default_factory=_DistrictHeatingConfig, + description="District heating configuration.", + ) + + heat_pump_sources: dict[str, list[str]] = Field( + default_factory=lambda: { + "urban central": ["air"], + "urban decentral": ["air"], + "rural": ["air", "ground"], + }, + description="Heat pump sources by area.", + ) + + residential_heat: _ResidentialHeatConfig = Field( + default_factory=_ResidentialHeatConfig, + description="Residential heat configuration.", + ) + + cluster_heat_buses: bool = Field( + True, + description="Cluster residential and service heat buses in `prepare_sector_network.py `_ to one to save memory.", + ) + heat_demand_cutout: str = Field("default", description="Heat demand cutout.") + + # Transport settings + bev_dsm_restriction_value: float = Field( + 0.8, + description="Adds a lower state of charge (SOC) limit for battery electric vehicles (BEV) to manage its own energy demand (DSM). Located in `build_transport_demand.py `_. Set to 0 for no restriction on BEV DSM.", + ) + bev_dsm_restriction_time: float = Field( + 7, description="Time at which SOC of BEV has to be dsm_restriction_value." + ) + transport_heating_deadband_upper: float = Field( + 20.0, + description="The maximum temperature in the vehicle. At higher temperatures, the energy required for cooling in the vehicle increases.", + ) + transport_heating_deadband_lower: float = Field( + 15.0, + description="The minimum temperature in the vehicle. At lower temperatures, the energy required for heating in the vehicle increases.", + ) + ICE_lower_degree_factor: float = Field( + 0.375, + description="Share increase in energy demand in internal combustion engine (ICE) for each degree difference between the cold environment and the minimum temperature.", + ) + ICE_upper_degree_factor: float = Field( + 1.6, + description="Share increase in energy demand in internal combustion engine (ICE) for each degree difference between the hot environment and the maximum temperature.", + ) + EV_lower_degree_factor: float = Field( + 0.98, + description="Share increase in energy demand in electric vehicles (EV) for each degree difference between the cold environment and the minimum temperature.", + ) + EV_upper_degree_factor: float = Field( + 0.63, + description="Share increase in energy demand in electric vehicles (EV) for each degree difference between the hot environment and the maximum temperature.", + ) + bev_dsm: bool = Field( + True, + description="Add the option for battery electric vehicles (BEV) to participate in demand-side management (DSM).", + ) + bev_dsm_availability: float = Field( + 0.5, + description="The share for battery electric vehicles (BEV) that are able to do demand side management (DSM).", + ) + bev_energy: float = Field( + 0.05, description="The average size of battery electric vehicles (BEV) in MWh." + ) + bev_charge_efficiency: float = Field( + 0.9, + description="Battery electric vehicles (BEV) charge and discharge efficiency.", + ) + bev_charge_rate: float = Field( + 0.011, + description="The power consumption for one electric vehicle (EV) in MWh. Value derived from 3-phase charger with 11 kW.", + ) + bev_avail_max: float = Field( + 0.95, + description="The maximum share plugged-in availability for passenger electric vehicles.", + ) + bev_avail_mean: float = Field( + 0.8, + description="The average share plugged-in availability for passenger electric vehicles.", + ) + v2g: bool = Field( + True, + description="Allows feed-in to grid from EV battery. This is only enabled if BEV demand-side management is enabled, and the share of vehicles participating is V2G is given by `bev_dsm_availability`.", + ) + + land_transport_fuel_cell_share: dict[int, float] = Field( + default_factory=lambda: { + 2020: 0, + 2025: 0, + 2030: 0, + 2035: 0, + 2040: 0, + 2045: 0, + 2050: 0, + }, + description="The share of vehicles that uses fuel cells in a given year.", + ) + land_transport_electric_share: dict[int, float] = Field( + default_factory=lambda: { + 2020: 0, + 2025: 0.05, + 2030: 0.2, + 2035: 0.45, + 2040: 0.7, + 2045: 0.85, + 2050: 1, + }, + description="The share of vehicles that uses electric vehicles (EV) in a given year.", + ) + land_transport_ice_share: dict[int, float] = Field( + default_factory=lambda: { + 2020: 1, + 2025: 0.95, + 2030: 0.8, + 2035: 0.55, + 2040: 0.3, + 2045: 0.15, + 2050: 0, + }, + description="The share of vehicles that uses internal combustion engines (ICE) in a given year. What is not EV or FCEV is oil-fuelled ICE.", + ) + + transport_electric_efficiency: float = Field( + 53.19, + description="The conversion efficiencies of electric vehicles in transport.", + ) + transport_fuel_cell_efficiency: float = Field( + 30.003, description="The H2 conversion efficiencies of fuel cells in transport." + ) + transport_ice_efficiency: float = Field( + 16.0712, + description="The oil conversion efficiencies of internal combustion engine (ICE) in transport.", + ) + + agriculture_machinery_electric_share: float = Field( + 0.5, description="The share for agricultural machinery that uses electricity." + ) + agriculture_machinery_oil_share: float = Field( + 0.5, description="The share for agricultural machinery that uses oil." + ) + agriculture_machinery_fuel_efficiency: float = Field( + 0.7, + description="The efficiency of electric-powered machinery in the conversion of electricity to meet agricultural needs.", + ) + agriculture_machinery_electric_efficiency: float = Field( + 0.3, + description="The efficiency of oil-powered machinery in the conversion of oil to meet agricultural needs.", + ) + + MWh_MeOH_per_MWh_H2: float = Field( + 0.8787, + description="The energy amount of the produced methanol per energy amount of hydrogen. From `DECHEMA (2017) `_, page 64.", + ) + MWh_MeOH_per_tCO2: float = Field( + 4.0321, + description="The energy amount of the produced methanol per ton of CO2. From `DECHEMA (2017) `_, page 66.", + ) + MWh_MeOH_per_MWh_e: float = Field( + 3.6907, + description="The energy amount of the produced methanol per energy amount of electricity. From `DECHEMA (2017) `_, page 64.", + ) + + shipping_hydrogen_liquefaction: bool = Field( + False, + description="Whether to include liquefaction costs for hydrogen demand in shipping.", + ) + shipping_hydrogen_share: dict[int, float] = Field( + default_factory=lambda: { + 2020: 0, + 2025: 0, + 2030: 0, + 2035: 0, + 2040: 0, + 2045: 0, + 2050: 0, + }, + description="The share of ships powered by hydrogen in a given year.", + ) + shipping_methanol_share: dict[int, float] = Field( + default_factory=lambda: { + 2020: 0, + 2025: 0, + 2030: 0.15, + 2035: 0.35, + 2040: 0.55, + 2045: 0.8, + 2050: 1, + }, + description="The share of ships powered by methanol in a given year.", + ) + shipping_oil_share: dict[int, float] = Field( + default_factory=lambda: { + 2020: 1, + 2025: 1, + 2030: 0.85, + 2035: 0.65, + 2040: 0.45, + 2045: 0.2, + 2050: 0, + }, + description="The share of ships powered by oil in a given year.", + ) + shipping_methanol_efficiency: float = Field( + 0.46, + description="The efficiency of methanol-powered ships in the conversion of methanol to meet shipping needs (propulsion). The efficiency increase from oil can be 10-15% higher according to the `IEA `_.", + ) + shipping_oil_efficiency: float = Field( + 0.40, + description="The efficiency of oil-powered ships in the conversion of oil to meet shipping needs (propulsion). Base value derived from 2011.", + ) + + aviation_demand_factor: float = Field( + 1.0, + description="The proportion of demand for aviation compared to today's consumption.", + ) + HVC_demand_factor: float = Field( + 1.0, + description="The proportion of demand for high-value chemicals compared to today's consumption.", + ) + + time_dep_hp_cop: bool = Field( + True, + description="Consider the time dependent coefficient of performance (COP) of the heat pump.", + ) + heat_pump_sink_T_individual_heating: float = Field( + 55.0, + description="The temperature heat sink used in heat pumps based on DTU / large area radiators. The value is conservatively high to cover hot water and space heating in poorly-insulated buildings.", + ) + + reduce_space_heat_exogenously: bool = Field( + True, + description="Influence on space heating demand by a certain factor (applied before losses in district heating).", + ) + reduce_space_heat_exogenously_factor: dict[int, float] = Field( + default_factory=lambda: { + 2020: 0.10, + 2025: 0.09, + 2030: 0.09, + 2035: 0.11, + 2040: 0.16, + 2045: 0.21, + 2050: 0.29, + }, + description="A positive factor can mean renovation or demolition of a building. If the factor is negative, it can mean an increase in floor area, increased thermal comfort, population growth. The default factors are determined by the `Eurocalc Homes and buildings decarbonization scenario `_.", + ) + + retrofitting: _RetrofittingConfig = Field( + default_factory=_RetrofittingConfig, description="Retrofitting configuration." + ) + + tes: bool = Field( + True, + description="Add option for storing thermal energy in large water pits associated with district heating systems and individual thermal energy storage (TES).", + ) + boilers: bool = Field( + True, description="Add option for transforming gas into heat using gas boilers." + ) + resistive_heaters: bool = Field( + True, + description="Add option for transforming electricity into heat using resistive heaters (independently from gas boilers).", + ) + oil_boilers: bool = Field( + False, description="Add option for transforming oil into heat using boilers." + ) + biomass_boiler: bool = Field( + True, description="Add option for transforming biomass into heat using boilers." + ) + overdimension_heat_generators: dict[str, float] = Field( + default_factory=lambda: {"decentral": 1.1, "central": 1.0}, + description="Add option for overdimensioning heating systems by a certain factor. This allows them to cover heat demand peaks e.g. 10% higher than those in the data with a setting of 1.1.", + ) + + chp: _CHPConfig = Field( + default_factory=_CHPConfig, description="CHP configuration." + ) + solar_thermal: bool = Field( + True, description="Add option for using solar thermal to generate heat." + ) + solar_cf_correction: float = Field( + 0.788457, + description="The correction factor for the value provided by the solar thermal profile calculations.", + ) + + methanation: bool = Field( + True, + description="Add option for transforming hydrogen and CO2 into methane using methanation.", + ) + coal_cc: bool = Field( + False, description="Add option for coal CHPs with carbon capture." + ) + dac: bool = Field(True, description="Add option for Direct Air Capture (DAC).") + co2_vent: bool = Field( + False, + description="Add option for vent out CO2 from storages to the atmosphere.", + ) + heat_vent: dict[str, bool] = Field( + default_factory=lambda: { + "urban central": True, + "urban decentral": True, + "rural": True, + }, + description="Heat venting by area.", + ) + marginal_cost_heat_vent: float = Field( + 0.02, description="The marginal cost of heat-venting in all heating systems." + ) + + allam_cycle_gas: bool = Field( + False, + description="Add option to include `Allam cycle gas power plants `_.", + ) + hydrogen_fuel_cell: bool = Field( + True, + description="Add option to include hydrogen fuel cell for re-electrification. Assuming OCGT technology costs.", + ) + hydrogen_turbine: bool = Field( + True, + description="Add option to include hydrogen turbine for re-electrification. Assuming OCGT technology costs.", + ) + SMR: bool = Field( + True, + description="Add option for transforming natural gas into hydrogen and CO2 using Steam Methane Reforming (SMR).", + ) + SMR_cc: bool = Field( + True, + description="Add option for transforming natural gas into hydrogen and CO2 using Steam Methane Reforming (SMR) and Carbon Capture (CC).", + ) + + regional_oil_demand: bool = Field( + True, + description="Spatially resolve oil demand. Set to true if regional CO2 constraints needed.", + ) + regional_coal_demand: bool = Field(False, description="Regional coal demand.") + + regional_co2_sequestration_potential: dict[str, Any] = Field( + default_factory=lambda: { + "enable": True, + "attribute": [ + "conservative estimate Mt", + "conservative estimate GAS Mt", + "conservative estimate OIL Mt", + "conservative estimate aquifer Mt", + ], + "include_onshore": False, + "min_size": 3, + "max_size": 25, + "years_of_storage": 25, + }, + description="Add option for regionally-resolved geological carbon dioxide sequestration potentials based on `CO2StoP `_.", + ) + co2_sequestration_potential: dict[int, float] = Field( + default_factory=lambda: { + 2020: 0, + 2025: 0, + 2030: 40, + 2035: 100, + 2040: 180, + 2045: 250, + 2050: 250, + }, + description="The potential of sequestering CO2 in Europe per year and investment period.", + ) + co2_sequestration_cost: float = Field( + 30, description="The cost of sequestering a ton of CO2 (currency/tCO2)." + ) + co2_sequestration_lifetime: int = Field( + 50, description="The lifetime of a CO2 sequestration site (years)." + ) + co2_spatial: bool = Field( + True, + description="Add option to spatially resolve carrier representing stored carbon dioxide. This allows for more detailed modelling of CCUTS, e.g. regarding the capturing of industrial process emissions, usage as feedstock for electrofuels, transport of carbon dioxide, and geological sequestration sites.", + ) + co2_network: bool = Field( + True, + description="Add option for planning a new carbon dioxide transmission network.", + ) + co2_network_cost_factor: float = Field( + 1, + description="The cost factor for the capital cost of the carbon dioxide transmission network.", + ) + cc_fraction: float = Field( + 0.9, + description="The default fraction of CO2 captured with post-combustion capture.", + ) + + hydrogen_underground_storage: bool = Field( + True, + description="Add options for storing hydrogen underground. Storage potential depends regionally.", + ) + hydrogen_underground_storage_locations: list[str] = Field( + default_factory=lambda: ["onshore", "nearshore"], + description="The location where hydrogen underground storage can be located. Onshore, nearshore, offshore means it must be located more than 50 km away from the sea, within 50 km of the sea, or within the sea itself respectively.", + ) + + methanol: _MethanolConfig = Field( + default_factory=_MethanolConfig, description="Methanol configuration." + ) + + ammonia: bool | str = Field( + True, + description='Add ammonia as a carrier. It can be either true (copperplated NH3), false (no NH3 carrier) or "regional" (regionalised NH3 without network).', + ) + min_part_load_electrolysis: float = Field( + 0, description="The minimum unit dispatch (`p_min_pu`) for electrolysis." + ) + min_part_load_fischer_tropsch: float = Field( + 0.5, + description="The minimum unit dispatch (`p_min_pu`) for the Fischer-Tropsch process.", + ) + min_part_load_methanolisation: float = Field( + 0.3, + description="The minimum unit dispatch (`p_min_pu`) for the methanolisation process.", + ) + min_part_load_methanation: float = Field( + 0.3, description="Minimum part load methanation." + ) + + use_fischer_tropsch_waste_heat: float = Field( + 0.25, + description="Add option for using waste heat of Fischer Tropsch in district heating networks.", + ) + use_haber_bosch_waste_heat: float = Field( + 0.25, description="Use Haber-Bosch waste heat." + ) + use_methanolisation_waste_heat: float = Field( + 0.25, description="Use methanolisation waste heat." + ) + use_methanation_waste_heat: float = Field( + 0.25, description="Use methanation waste heat." + ) + use_fuel_cell_waste_heat: float = Field( + 1, + description="Add option for using waste heat of fuel cells in district heating networks.", + ) + use_electrolysis_waste_heat: float = Field( + 0.25, + description="Add option for using waste heat of electrolysis in district heating networks.", + ) + + electricity_transmission_grid: bool = Field( + True, + description="Switch for enabling/disabling the electricity transmission grid.", + ) + electricity_distribution_grid: bool = Field( + True, + description="Add a simplified representation of the exchange capacity between transmission and distribution grid level through a link.", + ) + electricity_distribution_grid_cost_factor: float = Field( + 1.0, + description="Multiplies the investment cost of the electricity distribution grid.", + ) + electricity_grid_connection: bool = Field( + True, + description="Add the cost of electricity grid connection for onshore wind and solar.", + ) + + transmission_efficiency: _TransmissionEfficiencyConfig = Field( + default_factory=_TransmissionEfficiencyConfig, + description="Transmission efficiency configuration.", + ) + + H2_network: bool = Field(True, description="Add option for new hydrogen pipelines.") + gas_network: bool = Field( + True, + description="Add existing natural gas infrastructure, incl. LNG terminals, production and entry-points. The existing gas network is added with a lossless transport model. A length-weighted `k-edge augmentation algorithm `_ can be run to add new candidate gas pipelines such that all regions of the model can be connected to the gas network. When activated, all the gas demands are regionally disaggregated as well.", + ) + H2_retrofit: bool = Field( + False, + description="Add option for retrofiting existing pipelines to transport hydrogen.", + ) + H2_retrofit_capacity_per_CH4: float = Field( + 0.6, + description="The ratio for H2 capacity per original CH4 capacity of retrofitted pipelines. The `European Hydrogen Backbone (April, 2020) p.15 `_ 60% of original natural gas capacity could be used in cost-optimal case as H2 capacity.", + ) + gas_network_connectivity_upgrade: float = Field( + 1, + description="The number of desired edge connectivity (k) in the length-weighted `k-edge augmentation algorithm `_ used for the gas network.", + ) + gas_distribution_grid: bool = Field( + True, description="Add a gas distribution grid." + ) + gas_distribution_grid_cost_factor: float = Field( + 1.0, + description="Multiplier for the investment cost of the gas distribution grid.", + ) + + biomass_spatial: bool = Field( + True, description="Add option for resolving biomass demand regionally." + ) + biomass_transport: bool = Field( + False, description="Add option for transporting solid biomass between nodes." + ) + biogas_upgrading: bool = Field(True, description="Biogas upgrading.") + biogas_upgrading_cc: bool = Field( + False, description="Add option to capture CO2 from biomass upgrading." + ) + + conventional_generation: dict[str, str] = Field( + default_factory=lambda: {"OCGT": "gas", "CCGT": "gas"}, + description="Add a more detailed description of conventional carriers. Any power generation requires the consumption of fuel from nodes representing that fuel.", + ) + + biomass_to_liquid: bool = Field( + True, + description="Add option for transforming solid biomass into liquid fuel with the same properties as oil.", + ) + biomass_to_liquid_cc: bool = Field( + False, + description="Add option for transforming solid biomass into liquid fuel with the same properties as oil with carbon capture.", + ) + electrobiofuels: bool = Field(True, description="Electrobiofuels.") + biosng: bool = Field( + False, + description="Add option for transforming solid biomass into synthesis gas with the same properties as natural gas.", + ) + biosng_cc: bool = Field( + False, + description="Add option for transforming solid biomass into synthesis gas with the same properties as natural gas with carbon capture.", + ) + bioH2: bool = Field( + False, + description="Add option for transforming solid biomass into hydrogen with carbon capture.", + ) + municipal_solid_waste: bool = Field( + False, description="Add option for municipal solid waste." + ) + + limit_max_growth: _LimitMaxGrowthConfig = Field( + default_factory=_LimitMaxGrowthConfig, + description="Limit max growth configuration.", + ) + enhanced_geothermal: _EnhancedGeothermalConfig = Field( + default_factory=_EnhancedGeothermalConfig, + description="Enhanced geothermal configuration.", + ) + solid_biomass_import: _SolidBiomassImportConfig = Field( + default_factory=_SolidBiomassImportConfig, + description="Solid biomass import configuration.", + ) + imports: _ImportsConfig = Field( + default_factory=_ImportsConfig, description="Imports configuration." + ) diff --git a/scripts/lib/validation/config/snapshots.py b/scripts/lib/validation/config/snapshots.py new file mode 100644 index 000000000..0932b2f36 --- /dev/null +++ b/scripts/lib/validation/config/snapshots.py @@ -0,0 +1,32 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +""" +Snapshots configuration. + +See docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#snapshots +""" + +from typing import Literal + +from pydantic import Field + +from scripts.lib.validation.config._base import ConfigModel + + +class SnapshotsConfig(ConfigModel): + """Configuration for `snapshots` settings.""" + + start: str | list[str] = Field( + "2013-01-01", + description="Left bound of date range.", + ) + end: str | list[str] = Field( + "2014-01-01", + description="Right bound of date range.", + ) + inclusive: Literal["left", "right", "both"] | None = Field( + "left", + description="Make the time interval closed to the `left`, `right`, or both sides `both` or neither side `None`.", + ) diff --git a/scripts/lib/validation/config/solar_thermal.py b/scripts/lib/validation/config/solar_thermal.py new file mode 100644 index 000000000..fdb9026f4 --- /dev/null +++ b/scripts/lib/validation/config/solar_thermal.py @@ -0,0 +1,45 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +""" +Solar thermal configuration. + +See docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#solar-thermal +""" + +from typing import Literal + +from pydantic import BaseModel, Field + +from scripts.lib.validation.config._base import ConfigModel + + +class _OrientationConfig(ConfigModel): + """Configuration for `solar_thermal.orientation` settings.""" + + slope: float = Field( + 45.0, + description="The angle between the ground and the panels.", + ) + azimuth: float = Field( + 180.0, + description="The angle between the North and the sun with panels on the local horizon.", + ) + + +class SolarThermalConfig(BaseModel): + """Configuration for `solar_thermal` settings.""" + + clearsky_model: Literal["simple", "enhanced"] = Field( + "simple", + description="Type of clearsky model for diffuse irradiation.", + ) + orientation: _OrientationConfig = Field( + default_factory=_OrientationConfig, + description="Panel orientation with slope and azimuth.", + ) + cutout: str = Field( + "default", + description="Name of the cutout to use for solar thermal calculations.", + ) diff --git a/scripts/lib/validation/config/solving.py b/scripts/lib/validation/config/solving.py new file mode 100644 index 000000000..f44ba56fa --- /dev/null +++ b/scripts/lib/validation/config/solving.py @@ -0,0 +1,367 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +""" +Solving configuration. + +See docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#solving +""" + +from typing import Any + +from pydantic import BaseModel, Field, field_validator + +from scripts.lib.validation.config._base import ConfigModel + + +class _PostDiscretizationConfig(ConfigModel): + """Configuration for `solving.options.post_discretization` settings.""" + + enable: bool = Field( + False, + description="Switch to enable post-discretization of the network. Disabled by default.", + ) + line_unit_size: float = Field( + 1700, description="Discrete unit size of lines in MW." + ) + line_threshold: float = Field( + 0.3, + description="The threshold relative to the discrete line unit size beyond which to round up to the next unit.", + ) + link_unit_size: dict[str, float] = Field( + default_factory=lambda: {"DC": 2000, "H2 pipeline": 1200, "gas pipeline": 1500}, + description="Discrete unit size of links in MW by carrier (given in dictionary style).", + ) + link_threshold: dict[str, float] = Field( + default_factory=lambda: {"DC": 0.3, "H2 pipeline": 0.3, "gas pipeline": 0.3}, + description="The threshold relative to the discrete link unit size beyond which to round up to the next unit by carrier (given in dictionary style).", + ) + fractional_last_unit_size: bool = Field( + False, + description="When true, links and lines can be built up to p_nom_max. When false, they can only be built up to a multiple of the unit size.", + ) + + +class _ModelKwargsConfig(BaseModel): + """Configuration for `solving.options.model_kwargs` settings.""" + + solver_dir: str = Field( + "", description="Absolute path to the directory where linopy saves files." + ) + + +class _SolvingOptionsConfig(BaseModel): + """Configuration for `solving.options` settings.""" + + clip_p_max_pu: float = Field( + 0.01, + description="To avoid too small values in the renewables` per-unit availability time series values below this threshold are set to zero.", + ) + load_shedding: bool | float = Field( + False, + description="Add generators with very high marginal cost to simulate load shedding and avoid problem infeasibilities. If load shedding is a float, it denotes the marginal cost in EUR/kWh.", + ) + curtailment_mode: bool = Field( + False, + description="Fixes the dispatch profiles of generators with time-varying p_max_pu by setting `p_min_pu = p_max_pu` and adds an auxiliary curtailment generator (with negative sign to absorb excess power) at every AC bus. This can speed up the solving process as the curtailment decision is aggregated into a single generator per region. Defaults to `false`.", + ) + noisy_costs: bool = Field( + True, + description="Add random noise to marginal cost of generators by :math:`\\mathcal{U}(0.009,0,011)` and capital cost of lines and links by :math:`\\mathcal{U}(0.09,0,11)`.", + ) + skip_iterations: bool = Field( + True, + description="Skip iterating, do not update impedances of branches. Defaults to true.", + ) + rolling_horizon: bool = Field( + False, + description="Switch for rule `solve_operations_network` whether to optimize the network in a rolling horizon manner, where the snapshot range is split into slices of size `horizon` which are solved consecutively. This setting has currently no effect on sector-coupled networks.", + ) + seed: int = Field( + 123, description="Random seed for increased deterministic behaviour." + ) + custom_extra_functionality: str | None = Field( + "../data/custom_extra_functionality.py", + description="Path to a Python file with custom extra functionality code to be injected into the solving rules of the workflow relative to `rules` directory.", + ) + io_api: str | None = Field( + None, + description="Passed to linopy and determines the API used to communicate with the solver. With the `'lp'` and `'mps'` options linopy passes a file to the solver; with the `'direct'` option (only supported for HIGHS and Gurobi) linopy uses an in-memory python API resulting in better performance.", + ) + track_iterations: bool = Field( + False, + description="Flag whether to store the intermediate branch capacities and objective function values are recorded for each iteration in `network.lines['s_nom_opt_X']` (where `X` labels the iteration)", + ) + min_iterations: int = Field( + 2, + description="Minimum number of solving iterations in between which resistance and reactence (`x/r`) are updated for branches according to `s_nom_opt` of the previous run.", + ) + max_iterations: int = Field( + 3, + description="Maximum number of solving iterations in between which resistance and reactence (`x/r`) are updated for branches according to `s_nom_opt` of the previous run.", + ) + transmission_losses: int = Field( + 2, + description="Add piecewise linear approximation of transmission losses based on n tangents. Defaults to 0, which means losses are ignored.", + ) + linearized_unit_commitment: bool = Field( + True, + description="Whether to optimise using the linearized unit commitment formulation.", + ) + horizon: int = Field( + 365, + description="Number of snapshots to consider in each iteration. Defaults to 100.", + ) + post_discretization: _PostDiscretizationConfig = Field( + default_factory=_PostDiscretizationConfig, + description="Post-discretization settings.", + ) + keep_files: bool = Field( + False, description="Whether to keep LPs and MPS files after solving." + ) + model_kwargs: _ModelKwargsConfig = Field( + default_factory=_ModelKwargsConfig, description="Model kwargs for linopy." + ) + + +class _AggPNomLimitsConfig(BaseModel): + """Configuration for `solving.agg_p_nom_limits` settings.""" + + agg_offwind: bool = Field( + False, + description="Aggregate together all the types of offwind when writing the constraint (`offwind-all` as a carrier in the `.csv` file). Default is false.", + ) + agg_solar: bool = Field( + False, + description="Aggregate together all the types of electric solar when writing the constraint (`solar-all` as a carrier in the `.csv` file). Default is false.", + ) + include_existing: bool = Field( + False, + description="Take existing capacities into account when writing the constraint. Default is false.", + ) + file: str = Field( + "data/agg_p_nom_minmax.csv", + description="Reference to `.csv` file specifying per carrier generator nominal capacity constraints for individual countries and planning horizons. Defaults to `data/agg_p_nom_minmax.csv`.", + ) + + +class _ConstraintsConfig(BaseModel): + """Configuration for `solving.constraints` settings.""" + + CCL: bool = Field( + False, + description="Add minimum and maximum levels of generator nominal capacity per carrier for individual countries. These can be specified in the file linked at `electricity: agg_p_nom_limits` in the configuration. File defaults to `data/agg_p_nom_minmax.csv`. Does not work with a time resolution resampling.", + ) + EQ: bool | str = Field( + False, + description="Require each country or node to on average produce a minimal share of its total consumption itself. Example: `EQ0.5c` demands each country to produce on average at least 50% of its consumption; `EQ0.5` demands each node to produce on average at least 50% of its consumption.", + ) + BAU: bool = Field( + False, + description="Add a per-`carrier` minimal overall capacity; i.e. at least `40GW` of `OCGT` in Europe; configured in `electricity: BAU_mincapacities`", + ) + SAFE: bool = Field( + False, + description="Add a capacity reserve margin of a certain fraction above the peak demand to which renewable generators and storage do *not* contribute. Ignores network.", + ) + + +class _SolverConfig(BaseModel): + """Configuration for `solving.solver` settings.""" + + name: str = Field( + "gurobi", + description="Solver to use for optimisation problems in the workflow; e.g. clustering and linear optimal power flow.", + ) + options: str = Field( + "gurobi-default", description="Link to specific parameter settings." + ) + + +class _CheckObjectiveConfig(BaseModel): + """Configuration for `solving.check_objective` settings.""" + + enable: bool = Field(False, description="Enable objective value checking.") + expected_value: float | None = Field(None, description="Expected objective value.") + atol: float = Field(1_000_000, description="Absolute tolerance.") + rtol: float = Field(0.01, description="Relative tolerance.") + + @field_validator("expected_value", mode="before") + @classmethod + def parse_none_string(cls, v): + if v == "None" or v is None: + return None + return v + + +class _OETCConfig(BaseModel): + """Configuration for `solving.oetc` settings (Open Energy Transition Computing cluster support).""" + + name: str = Field( + "pypsa-eur", + description="Name identifier for the OETC job.", + ) + authentication_server_url: str = Field( + "", + description="URL of the OETC authentication server for job submission.", + ) + orchestrator_server_url: str = Field( + "", + description="URL of the OETC orchestrator server for job management.", + ) + cpu_cores: int = Field( + 8, + description="Number of CPU cores to request for the OETC job. (includes RAM amount at the moment with a factor of 8)", + ) + disk_space_gb: int = Field( + 50, + description="Amount of disk space in gigabytes to request for the OETC job.", + ) + delete_worker_on_error: bool = Field( + True, + description="Whether to delete the worker instance when an error occurs during job execution.", + ) + + +class SolvingConfig(BaseModel): + """Configuration for `solving` settings.""" + + options: _SolvingOptionsConfig = Field( + default_factory=_SolvingOptionsConfig, description="Solving options." + ) + agg_p_nom_limits: _AggPNomLimitsConfig = Field( + default_factory=_AggPNomLimitsConfig, + description="Aggregate p_nom limits configuration.", + ) + constraints: _ConstraintsConfig = Field( + default_factory=_ConstraintsConfig, description="Constraints configuration." + ) + solver: _SolverConfig = Field( + default_factory=_SolverConfig, description="Solver configuration." + ) + solver_options: dict[str, dict[str, Any]] = Field( + default_factory=lambda: { + "highs-default": { + "threads": 1, + "solver": "ipm", + "run_crossover": "off", + "small_matrix_value": 1e-6, + "large_matrix_value": 1e9, + "primal_feasibility_tolerance": 1e-5, + "dual_feasibility_tolerance": 1e-5, + "ipm_optimality_tolerance": 1e-4, + "parallel": "on", + "random_seed": 123, + }, + "highs-simplex": { + "solver": "simplex", + "parallel": "on", + "primal_feasibility_tolerance": 1e-5, + "dual_feasibility_tolerance": 1e-5, + "random_seed": 123, + }, + "gurobi-default": { + "threads": 32, + "method": 2, + "crossover": 0, + "BarConvTol": 1e-5, + "Seed": 123, + "AggFill": 0, + "PreDual": 0, + "GURO_PAR_BARDENSETHRESH": 200, + }, + "gurobi-numeric-focus": { + "NumericFocus": 3, + "method": 2, + "crossover": 0, + "BarHomogeneous": 1, + "BarConvTol": 1e-5, + "FeasibilityTol": 1e-4, + "OptimalityTol": 1e-4, + "ObjScale": -0.5, + "threads": 8, + "Seed": 123, + }, + "gurobi-fallback": { + "crossover": 0, + "method": 2, + "BarHomogeneous": 1, + "BarConvTol": 1e-5, + "FeasibilityTol": 1e-5, + "OptimalityTol": 1e-5, + "Seed": 123, + "threads": 8, + }, + "cplex-default": { + "threads": 4, + "lpmethod": 4, + "solutiontype": 2, + "barrier.convergetol": 1e-5, + "feasopt.tolerance": 1e-6, + }, + "copt-default": { + "Threads": 8, + "LpMethod": 2, + "Crossover": 0, + "RelGap": 1e-6, + "Dualize": 0, + }, + "copt-gpu": { + "LpMethod": 6, + "GPUMode": 1, + "PDLPTol": 1e-5, + "Crossover": 0, + }, + "xpress-default": { + "threads": 8, + "lpflags": 4, + "crossover": 0, + "bargaptarget": 1e-5, + "baralg": 2, + }, + "xpress-gpu": { + "lpflags": 4, + "crossover": 0, + "baralg": 4, + "barhggpu": 1, + "barhgreltol": 1e-5, + }, + "cbc-default": {}, + "glpk-default": {}, + }, + description="Dictionaries with solver-specific parameter settings.", + ) + + @field_validator("solver_options") + @classmethod + def check_no_gurobi_credentials(cls, v): + """Prevent Gurobi license credentials from being stored in config.""" + forbidden_keys = {"WLSACCESSID", "WLSSECRET", "LICENSEID"} + for solver_name, options in v.items(): + if "env" in options: + found = forbidden_keys & set(options["env"].keys()) + if found: + raise ValueError( + f"Gurobi license credentials ({', '.join(found)}) must not be set in config to avoid leaking secrets. " + "Use a license file instead or check the PyPSA options documentation on how to pass solver_options via environment variables, " + 'e.g. PYPSA_PARAMS__OPTIMIZE__SOLVER_OPTIONS={"env": {"WLSACCESSID": "...", "WLSSECRET": "...", "LICENSEID": 1234}}' + ) + return v + + check_objective: _CheckObjectiveConfig = Field( + default_factory=_CheckObjectiveConfig, + description="Objective checking configuration.", + ) + oetc: _OETCConfig | None = Field( + None, + description="Configuration options for Open Energy Transition Computing (OETC) cluster support.", + ) + mem_mb: int = Field( + 128000, + description="Estimated maximum memory requirement for solving networks (MB).", + ) + memory_logging_frequency: int = Field( + 5, description="Interval in seconds at which memory usage is logged." + ) + runtime: str = Field("48h", description="Runtime in humanfriendly style.") diff --git a/scripts/lib/validation/config/transformers.py b/scripts/lib/validation/config/transformers.py new file mode 100644 index 000000000..207981250 --- /dev/null +++ b/scripts/lib/validation/config/transformers.py @@ -0,0 +1,30 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +""" +Transformers configuration. + +See docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#transformers +""" + +from pydantic import Field + +from scripts.lib.validation.config._base import ConfigModel + + +class TransformersConfig(ConfigModel): + """Configuration for `transformers` settings.""" + + x: float = Field( + 0.1, + description="Series reactance in per unit (p.u.), using `s_nom` as base power of the transformer. Overwritten if `type` is specified.", + ) + s_nom: float = Field( + 2000.0, + description="Limit of apparent power which can pass through branch (MVA). Overwritten if `type` is specified.", + ) + type: str = Field( + "", + description="Specifies transformer types to assume for the transformers of the ENTSO-E grid extraction.", + ) diff --git a/scripts/lib/validation/config/transmission_projects.py b/scripts/lib/validation/config/transmission_projects.py new file mode 100644 index 000000000..e48a790a9 --- /dev/null +++ b/scripts/lib/validation/config/transmission_projects.py @@ -0,0 +1,57 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +""" +Transmission projects configuration. + +See docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#transmission-projects +""" + +from typing import Literal + +from pydantic import BaseModel, Field + +from scripts.lib.validation.config._base import ConfigModel + + +class _IncludeConfig(ConfigModel): + """Configuration for `transmission_projects.include` settings.""" + + tyndp2020: bool = Field( + True, + description="Whether to integrate the TYNDP 2020 dataset.", + ) + nep: bool = Field( + True, + description="Whether to integrate the German network development plan dataset.", + ) + manual: bool = Field( + True, + description="Whether to integrate the manually added transmission projects. They are taken from the previously existing links_tyndp.csv file.", + ) + + +class TransmissionProjectsConfig(BaseModel): + """Configuration for `transmission_projects` settings.""" + + enable: bool = Field( + True, + description="Whether to integrate this transmission projects or not.", + ) + include: _IncludeConfig = Field( + default_factory=_IncludeConfig, + description="Name of the transmission projects. They should be unique and have to be provided in the `data/transmission_projects` folder.", + ) + skip: list[str] = Field( + default_factory=lambda: ["upgraded_lines", "upgraded_links"], + description="Type of lines to skip from all transmission projects. Possible values are: `upgraded_lines`, `upgraded_links`, `new_lines`, `new_links`.", + ) + status: list[str] | dict[str, list[str]] = Field( + default_factory=lambda: ["under_construction", "in_permitting", "confirmed"], + description="Status to include into the model as list or as dict with name of project and status to include. Possible values for status are `under_construction`, `in_permitting`, `confirmed`, `planned_not_yet_permitted`, `under_consideration`.", + ) + new_link_capacity: Literal["zero", "keep"] = Field( + "zero", + description="Whether to set the new link capacity to the provided capacity or set it to zero.", + ) diff --git a/scripts/prepare_network.py b/scripts/prepare_network.py index 50f149eb3..9555a6e24 100755 --- a/scripts/prepare_network.py +++ b/scripts/prepare_network.py @@ -4,7 +4,7 @@ """ -Prepare PyPSA network for solving according to :ref:`opts` and :ref:`ll`, such +Prepare PyPSA network for solving according to :ref:`opts`, such as. - adding an annual **limit** of carbon-dioxide emissions, diff --git a/scripts/prepare_osm_network_release.py b/scripts/prepare_osm_network_release.py index 2bb896a15..a3562d594 100644 --- a/scripts/prepare_osm_network_release.py +++ b/scripts/prepare_osm_network_release.py @@ -7,6 +7,7 @@ import folium import geopandas as gpd import numpy as np +import pandas as pd import pypsa from shapely.wkt import loads @@ -78,7 +79,9 @@ ] -def export_clean_csv(df, columns, output_file): +def export_clean_csv( + df: pd.DataFrame, columns: list[str], output_file: str, rename_idx: str +): """ Export a cleaned DataFrame to a CSV file. @@ -86,26 +89,19 @@ def export_clean_csv(df, columns, output_file): df (pandas.DataFrame): The DataFrame to be exported. columns (list): A list of column names to include in the exported CSV file. output_file (str): The path to the output CSV file. + rename_idx (str): The name to use for renaming the index column. Returns: None """ - columns = [col for col in columns if col in df.columns] rename_dict = { - "Bus": "bus_id", - "Line": "line_id", - "Link": "link_id", - "Transformer": "transformer_id", "v_nom": "voltage", "num_parallel": "circuits", } - if "converter_id" in columns: - rename_dict["Link"] = "converter_id" - - df.reset_index().rename(columns=rename_dict).loc[:, columns].replace( - {True: "t", False: "f"} - ).to_csv(output_file, index=False, quotechar="'") + df.rename_axis(index=rename_idx).reset_index().rename(columns=rename_dict).loc[ + :, columns + ].replace({True: "t", False: "f"}).to_csv(output_file, index=False, quotechar="'") return None @@ -131,7 +127,6 @@ def create_geometries(network, is_converter, crs=GEO_CRS): """ buses_cols = [ - "Bus", "v_nom", "dc", "symbol", @@ -139,14 +134,13 @@ def create_geometries(network, is_converter, crs=GEO_CRS): "tags", "geometry", ] - buses = network.buses.reset_index()[ + buses = network.buses[ [c for c in buses_cols if c in network.buses.columns] - ] + ].reset_index() buses["geometry"] = buses.geometry.apply(lambda x: loads(x)) buses = gpd.GeoDataFrame(buses, geometry="geometry", crs=crs) lines_cols = [ - "Line", "bus0", "bus1", "v_nom", @@ -163,15 +157,14 @@ def create_geometries(network, is_converter, crs=GEO_CRS): "tags", "geometry", ] - lines = network.lines.reset_index()[ + lines = network.lines[ [c for c in lines_cols if c in network.lines.columns] - ] + ].reset_index() # Create shapely linestring from geometry column lines["geometry"] = lines.geometry.apply(lambda x: loads(x)) lines = gpd.GeoDataFrame(lines, geometry="geometry", crs=crs) links_cols = [ - "Link", "bus0", "bus1", "v_nom", @@ -184,16 +177,15 @@ def create_geometries(network, is_converter, crs=GEO_CRS): ] links = ( network.links[~is_converter] - .reset_index() .rename(columns={"voltage": "v_nom"})[ [c for c in links_cols if c in network.links.columns] ] + .reset_index() ) links["geometry"] = links.geometry.apply(lambda x: loads(x)) links = gpd.GeoDataFrame(links, geometry="geometry", crs=crs) converters_cols = [ - "Link", "bus0", "bus1", "v_nom", @@ -202,16 +194,15 @@ def create_geometries(network, is_converter, crs=GEO_CRS): ] converters = ( network.links[is_converter] - .reset_index() .rename(columns={"voltage": "v_nom"})[ [c for c in converters_cols if c in network.links.columns] ] + .reset_index() ) converters["geometry"] = converters.geometry.apply(lambda x: loads(x)) converters = gpd.GeoDataFrame(converters, geometry="geometry", crs=crs) transformers_cols = [ - "Transformer", "bus0", "bus1", "voltage_bus0", @@ -219,9 +210,9 @@ def create_geometries(network, is_converter, crs=GEO_CRS): "s_nom", "geometry", ] - transformers = network.transformers.reset_index()[ + transformers = network.transformers[ [c for c in transformers_cols if c in network.transformers.columns] - ] + ].reset_index() transformers["geometry"] = transformers.geometry.apply(lambda x: loads(x)) transformers = gpd.GeoDataFrame(transformers, geometry="geometry", crs=crs) @@ -287,19 +278,21 @@ def create_geometries(network, is_converter, crs=GEO_CRS): # Export to clean csv for release logger.info(f"Exporting {len(network.buses)} buses to %s", snakemake.output.buses) - export_clean_csv(network.buses, BUSES_COLUMNS, snakemake.output.buses) + export_clean_csv(network.buses, BUSES_COLUMNS, snakemake.output.buses, "bus_id") logger.info( f"Exporting {len(network.transformers)} transformers to %s", snakemake.output.transformers, ) export_clean_csv( - network.transformers, TRANSFORMERS_COLUMNS, snakemake.output.transformers + network.transformers, + TRANSFORMERS_COLUMNS, + snakemake.output.transformers, + "transformer_id", ) logger.info(f"Exporting {len(network.lines)} lines to %s", snakemake.output.lines) - export_clean_csv(network.lines, LINES_COLUMNS, snakemake.output.lines) - + export_clean_csv(network.lines, LINES_COLUMNS, snakemake.output.lines, "line_id") # Boolean that specifies if link element is a converter is_converter = network.links.index.str.startswith("conv") == True @@ -308,7 +301,7 @@ def create_geometries(network, is_converter, crs=GEO_CRS): snakemake.output.links, ) export_clean_csv( - network.links[~is_converter], LINKS_COLUMNS, snakemake.output.links + network.links[~is_converter], LINKS_COLUMNS, snakemake.output.links, "link_id" ) logger.info( @@ -316,7 +309,10 @@ def create_geometries(network, is_converter, crs=GEO_CRS): snakemake.output.converters, ) export_clean_csv( - network.links[is_converter], CONVERTERS_COLUMNS, snakemake.output.converters + network.links[is_converter], + CONVERTERS_COLUMNS, + snakemake.output.converters, + "converter_id", ) ## Create interactive map diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index 3378e2db7..146845f5e 100755 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -2083,6 +2083,16 @@ def add_storage_and_grids( logger.info( "Add natural gas infrastructure, incl. LNG terminals, production, storage and entry-points." ) + + add_carrier_buses( + n=n, + carrier="gas", + costs=costs, + spatial=spatial, + options=options, + cf_industry=None, + ) + gas_pipes = pd.read_csv(clustered_gas_network_file, index_col=0) if options["H2_retrofit"]: @@ -4817,6 +4827,17 @@ def add_industry( - Process emission handling """ logger.info("Add industrial demand") + + # Ensure the gas carrier bus exists before adding any gas-for-industry links. + add_carrier_buses( + n=n, + carrier="gas", + costs=costs, + spatial=spatial, + options=options, + cf_industry=None, + ) + # add oil buses for shipping, aviation and naptha for industry add_carrier_buses( n, @@ -4842,6 +4863,12 @@ def add_industry( # 1e6 to convert TWh to MWh industrial_demand = pd.read_csv(industrial_demand_file, index_col=0) * 1e6 * nyears + if not options["biomass"]: + raise ValueError( + "Industry demand includes solid biomass, but `sector.biomass` is disabled. " + "Enable `sector: {biomass: true}` in config." + ) + n.add( "Bus", spatial.biomass.industry, diff --git a/scripts/retrieve_bidding_zones.py b/scripts/retrieve_bidding_zones.py deleted file mode 100644 index f4798fb70..000000000 --- a/scripts/retrieve_bidding_zones.py +++ /dev/null @@ -1,73 +0,0 @@ -# SPDX-FileCopyrightText: Contributors to PyPSA-Eur -# -# SPDX-License-Identifier: MIT -""" -Retrieves bidding zone shape files from two sources. `electricitymaps-contrib` provides shape files for all the zones on a global level. `entsoe-py` provides country level shape files which are concatenated into one file. The `electricitymaps-contrib` data is preferred, but the Italian bidding zones from `entsoe-py` are more accurate. - -Outputs -------- - -- ``data/busshapes/bidding_zones_electricitymaps.geojson``: -- ``data/busshapes/bidding_zones_entsoepy.geojson``: -""" - -import logging -from urllib.error import HTTPError - -import entsoe -import geopandas as gpd -import pandas as pd - -logger = logging.getLogger(__name__) - - -def load_bidding_zones_from_entsoepy() -> gpd.GeoDataFrame: - """ - Load bidding zone geometries from entsoe-py GeoJSON files with disk caching. - - Returns: - GeoDataFrame: Contains geometries for all available bidding zones - """ - # If not in cache or cache disabled, load from source - logger.info("Downloading entsoe-py zones...") - gdfs: list[gpd.GeoDataFrame] = [] - for area in entsoe.Area: - name = area.name - try: - url = f"https://raw.githubusercontent.com/EnergieID/entsoe-py/c03c604af36ef92e8ef6ee89dc57c56ca5e1dbac/entsoe/geo/geojson/{name}.geojson" - gdfs.append(gpd.read_file(url)) - except HTTPError: - continue - - shapes = pd.concat(gdfs, ignore_index=True) # type: ignore - - logger.info("Downloading entsoe-py zones... Done") - - return shapes - - -def load_bidding_zones_from_electricitymaps() -> gpd.GeoDataFrame: - """ - Load bidding zone geometries from electricitymaps-contrib repository. - - Returns: - GeoDataFrame: Contains geometries for all available bidding zones - """ - logger.info("Downloading electricitymaps-contrib zones...") - url = "https://raw.githubusercontent.com/electricitymaps/electricitymaps-contrib/v1.238.0/web/geo/world.geojson" - df = gpd.read_file(url) - logger.info("Downloading electricitymaps-contrib zones... Done") - return df - - -if __name__ == "__main__": - if "snakemake" not in globals(): - from _helpers import mock_snakemake - - snakemake = mock_snakemake("retrieve_bidding_zones") - - bidding_zones = load_bidding_zones_from_entsoepy() - bidding_zones.to_file(snakemake.output.file_entsoepy) - - bidding_zones = load_bidding_zones_from_electricitymaps() - bidding_zones.to_file(snakemake.output.file_electricitymaps) diff --git a/scripts/retrieve_corine_dataset_primary.py b/scripts/retrieve_corine_dataset_primary.py index 9d9e4bf17..0205410ea 100644 --- a/scripts/retrieve_corine_dataset_primary.py +++ b/scripts/retrieve_corine_dataset_primary.py @@ -6,9 +6,9 @@ Usage Instructions: 1. Login using EU login at https://land.copernicus.eu/user/login and create an API key - 2. Copy API key into the config.default.yaml -> (save from portal) - # secrets: - # corine: '' + 2. Set the environment variable CORINE_API_TOKEN: + - Option 1: For Linux `export CORINE_API_TOKEN=` and for Windows `set CORINE_API_TOKEN=`. Note that this is only temporary and you will need to either make this variable permanent or set it each time you restart your command line. + - Option 2: Add a file `.env` to your local repository root, e.g. `.../PyPSA-Eur/.env`. Do not add this file to `git`!. Then add the following line: `CORINE_API_TOKEN=`. """ import json @@ -28,9 +28,7 @@ def load_access_token(apikey): # Login using EU login at https://land.copernicus.eu/user/login and create an API key - # Copy API key into the config.default.yaml -> (save from portal) - # secrets: - # corine: '' + # Set CORINE_API_TOKEN environment variable or add to .env file try: service_key = json.loads(apikey) private_key = service_key["private_key"].encode("utf-8") @@ -75,6 +73,12 @@ def load_access_token(apikey): set_scenario_config(snakemake) apikey = snakemake.params["apikey"] + if not apikey: + raise ValueError( + "Environment variable CORINE_API_TOKEN is not set.\n" + "To download CORINE data from the primary source you need to provide a valid API key. " + "See retrieve_corine_dataset_primary.py for details." + ) output_zip_file = snakemake.output["zip"] tif_file = snakemake.output["tif_file"] access_token = load_access_token(apikey) diff --git a/scripts/retrieve_seawater_temperature.py b/scripts/retrieve_seawater_temperature.py index c6f63df33..84646fd15 100644 --- a/scripts/retrieve_seawater_temperature.py +++ b/scripts/retrieve_seawater_temperature.py @@ -68,7 +68,7 @@ if snakemake.params.default_cutout == "be-03-2013-era5": logger.info("Retrieving test-cutout seawater temperature data.") - url = "https://zenodo.org/records/15828866/files/seawater_temperature.nc" + url = snakemake.params.test_data_url response = requests.get(url, stream=True) response.raise_for_status() diff --git a/test/conftest.py b/test/conftest.py index 51914d905..1bb133b1f 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -5,15 +5,12 @@ import pathlib import zipfile from functools import reduce -from shutil import unpack_archive from urllib.request import urlretrieve -from uuid import uuid4 import geopandas as gpd import pandas as pd import pypsa import pytest -import requests import yaml @@ -137,33 +134,19 @@ def download_natural_earth(tmpdir): pathlib.Path(natural_earth_shape_file_path).unlink(missing_ok=True) -@pytest.fixture(scope="function") -def download_eez(tmpdir): - name = str(uuid4())[:8] - org = str(uuid4())[:8] - zipped_filename = "World_EEZ_v12_20231025_LR.zip" - response = requests.post( - "https://www.marineregions.org/download_file.php", - params={"name": zipped_filename}, - data={ - "name": name, - "organisation": org, - "email": f"{name}@{org}.org", - "country": "Germany", - "user_category": "academia", - "purpose_category": "Research", - "agree": "1", - }, - ) - zipped_filename_path = pathlib.Path(tmpdir, zipped_filename) - with open(zipped_filename_path, "wb") as f: - f.write(response.content) - unpack_archive(zipped_filename_path, tmpdir) - output_path = pathlib.Path( - tmpdir, "World_EEZ_v12_20231025_LR", "eez_v12_lowres.gpkg" - ) - yield output_path - pathlib.Path(output_path).unlink(missing_ok=True) +# Disable because of unreliable data download +# @pytest.fixture(scope="function") +# def download_eez(tmpdir): +# url = "https://data.pypsa.org/workflows/eur/eez/v12_20231025/World_EEZ_v12_20231025_LR.zip" +# zipped_filename = "World_EEZ_v12_20231025_LR.zip" +# zipped_filename_path = pathlib.Path(tmpdir, zipped_filename) +# urlretrieve(url, zipped_filename_path) +# unpack_archive(zipped_filename_path, tmpdir) +# output_path = pathlib.Path( +# tmpdir, "World_EEZ_v12_20231025_LR", "eez_v12_lowres.gpkg" +# ) +# yield output_path +# pathlib.Path(output_path).unlink(missing_ok=True) @pytest.fixture(scope="function") diff --git a/test/test_build_shapes.py b/test/test_build_shapes.py index 8660d0011..0c75127e7 100644 --- a/test/test_build_shapes.py +++ b/test/test_build_shapes.py @@ -15,7 +15,7 @@ sys.path.append("./scripts") -from build_shapes import _simplify_polys, eez +from build_shapes import _simplify_polys path_cwd = pathlib.Path.cwd() @@ -59,15 +59,16 @@ def test_simplify_polys(tolerance, expected_tuple, italy_shape): assert all([x == y for x, y in zip(output_tuple, expected_tuple)]) -@pytest.mark.parametrize( - "country_list", - [["DE"], ["IT"]], -) -def test_eez(config, country_list, download_eez): - """ - Verify what is returned by eez. - """ - eez_path = download_eez - offshore_shapes_gdf = eez(eez_path, country_list) - assert offshore_shapes_gdf.shape == (1, 1) - assert offshore_shapes_gdf.index == country_list[0] +# Disable because of unreliable download_eez +# @pytest.mark.parametrize( +# "country_list", +# [["DE"], ["IT"]], +# ) +# def test_eez(config, country_list, download_eez): +# """ +# Verify what is returned by eez. +# """ +# eez_path = download_eez +# offshore_shapes_gdf = eez(eez_path, country_list) +# assert offshore_shapes_gdf.shape == (1, 1) +# assert offshore_shapes_gdf.index == country_list[0] diff --git a/test/test_config_schema.py b/test/test_config_schema.py new file mode 100644 index 000000000..0fd1b4334 --- /dev/null +++ b/test/test_config_schema.py @@ -0,0 +1,72 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +"""Tests for config schema synchronization.""" + +import difflib +import tempfile +from pathlib import Path + +import pytest + +from scripts.lib.validation.config import ( + generate_config_defaults, + generate_config_schema, +) + + +def _check_file_in_sync(existing_path: Path, generate_func, file_type: str): + """Check if a file is in sync with the generated content.""" + assert existing_path.exists(), f"{existing_path} does not exist" + + existing_content = existing_path.read_text() + + with tempfile.NamedTemporaryFile( + mode="w", suffix=f".{file_type}", delete=False + ) as f: + temp_path = f.name + + try: + generate_func(temp_path) + generated_content = Path(temp_path).read_text() + finally: + Path(temp_path).unlink(missing_ok=True) + + if existing_content != generated_content: + diff = difflib.unified_diff( + existing_content.splitlines(keepends=True), + generated_content.splitlines(keepends=True), + fromfile=f"{existing_path} (existing)", + tofile=f"{existing_path} (generated)", + ) + diff_str = "".join(diff) + print(f"\n{'=' * 80}") + print(f"DIFF: {existing_path}") + print("=" * 80) + print(diff_str) + print("=" * 80) + pytest.fail( + f"{existing_path} is out of sync with the Pydantic schema. " + "Run 'pixi run generate-config' to update. See diff above." + ) + + +def test_config_default_yaml_in_sync(): + """Test that config/config.default.yaml is in sync with Pydantic schema.""" + + _check_file_in_sync( + Path("config/config.default.yaml"), + generate_config_defaults, + "yaml", + ) + + +def test_config_schema_json_in_sync(): + """Test that config/schema.json is in sync with Pydantic schema.""" + + _check_file_in_sync( + Path("config/schema.json"), + generate_config_schema, + "json", + ) diff --git a/test/test_data_versions_layer.py b/test/test_data_versions_layer.py new file mode 100644 index 000000000..982e5c90d --- /dev/null +++ b/test/test_data_versions_layer.py @@ -0,0 +1,130 @@ +# SPDX-FileCopyrightText: Contributors to PyPSA-Eur +# +# SPDX-License-Identifier: MIT + +from datetime import date +from pathlib import Path + +import pandas as pd +import pandera.pandas as pa +from natsort import natsort_keygen +from pandera.pandas import Check, Column + +VERSIONS_CSV = Path(__file__).parent.parent / "data" / "versions.csv" +VALID_SOURCES = ["primary", "archive", "build"] # Order defines sort priority + +VALID_TAGS = { + "latest", + "supported", + "not-supported", + "deprecated", + "might-work", + "not-tested", + "broken-link", +} + +not_empty = [Check.str_length(min_value=1), Check.str_matches(r"\S")] +valid_tags = Check(lambda s: all(t in VALID_TAGS for t in s.split()), element_wise=True) +url_safe = Check.str_matches( + r"^[a-z0-9_\-\.]+$", + error="Version must be URL-safe (only alphanumeric, hyphen, underscore, dot)", +) + + +def sort_versions(df: pd.DataFrame) -> pd.DataFrame: + """Sort by dataset (asc), version (desc, natural), source (as predefined).""" + natsort_key = natsort_keygen(key=str.casefold) + df = df.copy() + df["source"] = pd.Categorical(df["source"], categories=VALID_SOURCES, ordered=True) + df = df.sort_values( + by=["dataset", "version", "source"], + key=lambda c: c.map(natsort_key) if c.name != "source" else c, + ascending=[True, False, True], + ).reset_index(drop=True) + df["source"] = df["source"].astype(str) + return df + + +is_sorted = Check(lambda df: df.equals(sort_versions(df)), error="Data must be sorted") +archive_has_url = Check( + lambda df: df.loc[df["source"] == "archive", "url"].str.len().gt(0).all(), + error="Archive entries must have a URL", +) +one_latest_per_dataset_source = Check( + lambda df: df[df["tags"].str.contains("latest")] + .groupby(["dataset", "source"]) + .size() + .eq(1) + .all(), + error="Exactly one 'latest' tag required per dataset/source combination", +) +latest_same_version_across_sources = Check( + lambda df: df[(df["tags"].str.contains("latest")) & (df["version"] != "unknown")] + .groupby("dataset")["version"] + .nunique() + .le(1) + .all(), + error="All 'latest' entries for a dataset must have the same version across sources (excluding 'unknown')", +) +VersionsSchema = pa.DataFrameSchema( + { + "dataset": Column(str, not_empty, nullable=False), + "version": Column(str, not_empty + [url_safe], nullable=False), + "source": Column(str, Check.isin(VALID_SOURCES)), + "tags": Column(str, valid_tags, nullable=False), + "added": Column( + str, + Check.str_matches(r"^\d{4}-\d{2}-\d{2}$"), + nullable=False, + coerce=True, + default=date.today().isoformat(), + ), + "note": Column(str, nullable=True), + "url": Column( + str, + Check.str_matches( + r'^(https?://[^:\s<>"|*]*)?$', + error='URL must start with http(s):// and not contain colons (use %3A), spaces, or Windows-invalid characters (<>"|*).', + ), + nullable=True, + ), + }, + checks=[ + is_sorted, + archive_has_url, + one_latest_per_dataset_source, + latest_same_version_across_sources, + ], + coerce=True, + strict=True, + ordered=True, + unique=["dataset", "version", "source"], +) + + +def load_versions() -> pd.DataFrame: + return pd.read_csv(VERSIONS_CSV, dtype=str, keep_default_na=False) + + +def validate_versions(fix: bool = False) -> pd.DataFrame: + df = load_versions() + if fix: + df = sort_versions(df) + try: + df = VersionsSchema.validate(df, lazy=True) + except pa.errors.SchemaErrors as e: + msg = f"{e.message}\n\nTry 'pixi run python test/test_data_versions_layer.py' to auto-fix (sorting, defaults, etc.)." + e.message = msg + raise + if fix: + df.to_csv(VERSIONS_CSV, index=False) + return df + + +def test_versions_csv(): + validate_versions() + + +if __name__ == "__main__": + validate_versions(fix=True) + print("versions.csv validated and fixed")