Skip to content

Representation of German district heating systems as subnodes #58

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 797 commits into
base: main
Choose a base branch
from

Conversation

cpschau
Copy link
Contributor

@cpschau cpschau commented Mar 20, 2025

Closes

This PR transfers and extends the changes made in the deprecated PRs in pypsa-ariadne and former submodule pypsa-eur:
PyPSA/pypsa-ariadne#147
PyPSA/pypsa-ariadne#299
PyPSA/pypsa-eur#1230

Description

We introduce subnodes to represent the n largest district heating networks in Germany, as identified from the open Fernwärmeatlas dataset (available upon request at Fernwärmeatlas under CC BY 4.0 license). Largest means the amount of annual district heating feed-in. The number of these largest networks that are modeled explicitly can be determined using the configuration parameter sector:district_heating:subnodes:nlargest. The feature must be enabled by setting sector:district_heating:subnodes:enable. to True. Otherwise, the feature is deactivated, and the default pypsa-de workflow is executed.

The district heating subnodes are attached to the superordinated standard onshore regions, which continue to represent the remaining smaller district heating systems within their respective regions. All essential district heating components, that are attached to the mother node, are mirrored in the subnodes. Exceptions are waste heat from P2X and electrolysis (heat generating links) and DAC (heat-consuming link).

District heating areas

The Fernwärmeatlas does not provide any georeferences for the district heating systems it comprises. Therefore, district heating areas are derived based on the lower administrative units (LAU) corresponding to the cities and if the parameter sector:district_heating:subnodes:census_areas:enable is set to true the raster dataset of the German census 2022. Ba activating the feaure, the census data is processed to synthesize a cleaned and smoothed set of district heating zones that were validated by eyesight against a couple of the largest German district heating systems.

Validation:
image

In the default configuration, raster tiles from the census with more than 1% district heating are extracted and united into cohesive district heating zones. This threshold value can be chosen differently using the parameter sector:district_heating:subnodes:census_areas:min_district_heating_share. It then intersects these zones with the LAU regions applying an overlay and refines the resulting shapes by filtering small areas and applying iterative buffering and merging. The parameters (min_area [m^2] & buffer_factor [% of square root of area]) of this processing step can be set using sector:district_heating:subnodes:census_areas:processing.

The generated district heating areas serve as system-specific geographic reference to yield a more accurate assignment of heat supply potentials (originally implemented in PyPSA/pypsa-eur#1359 and PyPSA/pypsa-eur#1516) and calculation of eligible land e.g. for roll-out of pit storage.

Buses

The cities are represented by their own specific urban central heat buses. These subnodal district heating buses are always referenced in the subnodal components instead of the superordinated urban central heat bus of the mother node.

Loads

The annual feed-in data of the district heating systems, as recorded in the dataset, is used to assign the heat loads to the subnodes, with a corresponding reduction in the load of the mother nodes. These loads fall into the categories "urban central heat" and "low-temperature heat for industry". The ratio between those two loads in the mother node is preserved in the subnode. Also, the ratio between multiple subnodal loads that are attached to the same cluster is preserved. The load of the mother node is an upper bound to the attached subnodal nodes. In the case, that the sum of subnodal loads exceeds the cluster load, the subnodal loads from Fernwärmeatlas are reduced.

Generators

The same generator technologies as in the mother node are available. Currently, they comprise heat vents for district heating curtailment and the geothermal generators, which provide the extraction of geothermal heat, which is bounded by the location-specific potential. The upstream potential calculation receives an adjusted data input, to

  1. calculate the subnodal potentials that are located within their specific district heating areas
  2. restrict the geothermal potentials of the mother nodes to the regions of the remaining district heating regions that are part of the Fernwärmeatlas but not modeled explicitly.

Stores

The same store and storage unit options as in the mother nodes are available in the subnodes. Currently, only tank and pit thermal energy storage are represented as store components. In the case of pit thermal energy storage, the limit on the maximum installable capacity is pre-calculated in prepare_district_heating_subnodes and set in add_district_heating_subnodes if the limit is activated with sector:district_heating:subnodes:limit_ptes_potential:enable. Settting sector:district_heating:subnodes:limit_ptes_potential:limit_mother_nodes as true, the storage potential of the mother nodes is also limited to the sum of potentials corresponding to the remaining smaller district heating systems contained within the Fernwärmeatlas data.

PTES potential calculation

The land eligibility is based on OSM land cover dataset (10m resolution), the NATURA 2000 protected areas, and the dataset indicating the local groundwater depth by Fan et al (DOI: 10.1126/science.12298). Eligible land

  • is classified as arable land (21 is grid code), pastures (23), shrub/herbaceous (32), or open spaces with little vegetation (33). Other OSM grid codes can be passed with the config parameter sector:district_heating:subnodes:limit_ptes_potential:osm_landcover_codes.
  • is located outside of NATURA 2000 protected areas
  • must have a minimum distance to groundwater of 10 m, which can be changed using the parameter sector:district_heating:subnodes:limit_ptes_potential:max_groundwater_depth.
  • areas must have minimum size of 10000 m², which can be set to a different value in the config using sector:district_heating:subnodes:limit_ptes_potential:min_area

The total resulting area returned by the atlite ExclusionContainer is divided by 10000m^2 (also min_area parameter) and multiplied by a default capacity value configurable with sector:district_heating:subnodes:limit_ptes_potential:default_capacity. The parameter is set to 4500 MWh per default to calculate the e_nom_max. These default values are common parameters found for large pit thermal energy storage in the DEA catalogue (4500 MWh at 70000 m³) and the literature like the review from Xiang et al. (Dronninglund [8100 m² at 60000m³, Hoje-Taastrup 11098 m² at 70000m³).

Links

Except from electrolysis, P2H, and DAC technologies, all links affecting the district heating at the mother nodes are also represented at the subnodes. While the urban central heat buses are adjusted to reference the specific subnodal urban central heat bus, other buses remain identical compared to the mother node. Therefore,

  • electricity generated in CHP plants flows into the AC buses of mother node
  • electricity consumed by heat pumps and power-to-heat technologies is drawn from AC bus of mother node
  • hydrogen consumed by hydrogen-fueled CHP plants is drawn from hydrogen bus of mother node
  • $CO_2$ captured in CHP plants with CC unit flows into $CO_2$ mus of mother node
  • etc.

Heat pumps

The geolocations of the subnodes are used to calculate the location-specific heat pump efficiencies (modified onshore regions are passed to rules build_temperature_profiles, build_central_heating_temperature_profiles, build_cop_profiles).

Existing CHP plants

When a brownfield approach is followed, the existing CHP capacities from the MaStR database are allocated to the subnodes based on their geolocation. For mapping, the census-based district heating areas are used. Through a modification in the add_existing_baseyear script ( formerly PyPSA/pypsa-eur#1230), a CHP plant is either added to a subnode or remains in the mother node.

Overview on changes

New data sources

/data/fernwaermeatlas

  • /cities_geolocations.geojson contains coordinates of cities contained in
  • /fernwaermeatlas.xlsx, which is an open dataset licensed under CC 4.0. Here it can be explored and requested.

Zensusatlas

  • is open and can be explored here

OSM land cover

  • OSM land cover can be explored here. It is published under CC BY 4.0 and can be downloaded here.

Groundwater table depth

  • provided by: Fan Y, Miguez-Macho G, Jobbágy EG, Jackson RB, Otero-Casal C (2017): Hydrologic regulation of plant rooting depth, Proceedings of the National Academy of Sciences 114 (40), 10572-10577, doi: 10.1073/pnas.1712381114

New scripts

prepare_district_heating_subnodes:

  • data cleaning of Fernwärmeatlas, assignment of geoinformation and onshore regions in prepare_subnodes()
  • synthesis of city-specific district heating areas using raster data of census in refine_dh_areas_from_census_data()
  • calculate PTES potential based on land eligibility that takes into account OSM land cover, NATURA protected areas, and groundwater depth in add_ptes_limit()
  • create extended (for the calculation of city-specific temperature profiles, supply temperatures, COPs) and restricted versions of onshore regions (for calculation of geothermal potentials) in extend_onshore_regions()

add_district_heating_subnodes:

  • addition of network components as described above (buses, loads, stores, storage units, generators, (multi-)links in master function add_subnodes() and subfunctions add_buses(), add_loads(), add_stores(), add_storage_units(), add_generators(), and add_links().
  • extend index of existing_heating_distribution dataframe by subnodes in extend_heating_distribution. At the moment only zero-capacities are filled to ensure a smooth workflow, as there is no data available on existing capacities in German district heating systems.

Further code adjustments

  • addition of function assign_subnode() in build_existing_chp_de maps CHP plants from MaStR to mother nodes and subnodes.
  • adjustments in prepare_sector_network to drop subnodes from temperature xarray and include TES (Add PTES and introduce PTES/TES energy-to-power ratios pypsa-eur#1546)
  • fill missing geothermal potentials in UK with zero values (part of fix: Handle missing geothermal potential data in non-EU 27 pypsa-eur#1617)
  • initialize empty dictionaries for config parameters in solving to allow deactivation of constraints using the config
  • fix overriding of heat pump efficiencies to account for changes in district heating supply temperature in add_brownfield
  • filter out CHP plants with expired lifetime in add_existing_baseyear
  • assure correct bus mapping in CHP multilinks (electricity and fuel of mother node, urban central heat of subnode) in add_existing_baseyear

Exemplary district heating balances from myopic run with 7H-27c resolution

image

Checklist

  • Workflow with target rule ariadne_all completes without errors
  • The logic of export_ariadne_variables has been adapted to the changes
  • One or several figures that validate the changes in the PR have been posted as a comment
  • A brief description of the changes has been added to Changelog.md
  • The latest main has been merged into the PR
  • The config has a new prefix of the format YYYYMMDDdescriptive_title

lindnemi and others added 30 commits July 4, 2024 14:36
Adding more variables to the exporter
Industry Steam from Biomass/Hydrogen/Electricity
Preparation for public model Pypsa-DE
Copy link
Contributor

@amos-schledorn amos-schledorn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very impressive!
I roughly went through prepare_district_heating_subnodes and add_district_heating_subnodes. I'd suggest doing another iteration once we've sorted the diff to the master and changes to existing code is clear.

Comment on lines 55 to 64
# If head is boolean set it to 40 for default behavior
if isinstance(head, bool):
head = 40

# Keep only n largest district heating networks according to head parameter
subnodes_head = subnodes.sort_values(
by="Wärmeeinspeisung in GWh/a", ascending=False
).head(head)
subnodes_head.to_file(snakemake.output.district_heating_subnodes, driver="GeoJSON")

subnodes_rest = subnodes[~subnodes.index.isin(subnodes_head.index)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be more organic to handle this in prepare_district_heating_subnodes?
If not: let's avoid snakemake (or any) globals in local name spaces and pass the filename as an argument.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found it more practical to also keep the smaller systems (not explicitly modeled as nlargest) within the output of prepare_district_heating_subnodes, as they can be optionally used to constrain storage potentials in mother node.

@cpschau cpschau force-pushed the refine_dh_areas branch from 98f0a4e to 9b66fd6 Compare May 15, 2025 13:13
@cpschau cpschau force-pushed the refine_dh_areas branch from 9b66fd6 to 98d094e Compare May 15, 2025 13:22
@cpschau cpschau requested a review from amos-schledorn May 16, 2025 10:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants