pypsa-eur/scripts/build_existing_heating_distribution.py
Fabian Neumann 013b705ee4
Clustering: build renewable profiles and add all assets after clustering (#1201)
* Cluster first: build renewable profiles and add all assets after clustering

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* correction: pass landfall_lengths through functions

* assign landfall_lenghts correctly

* remove parameter add_land_use_constraint

* fix network_dict

* calculate distance to shoreline, remove underwater_fraction

* adjust simplification parameter to exclude Crete from offshore wind connections

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* remove unused geth2015 hydro capacities

* removing remaining traces of {simpl} wildcard

* add release notes and update workflow graphics

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: lisazeyen <lisa.zeyen@web.de>
2024-09-13 15:37:01 +02:00

163 lines
6.0 KiB
Python

# -*- coding: utf-8 -*-
# SPDX-FileCopyrightText: : 2020-2024 The PyPSA-Eur Authors
#
# SPDX-License-Identifier: MIT
"""
Builds table of existing heat generation capacities for initial planning
horizon.
Existing heat generation capacities are distributed to nodes based on population.
Within the nodes, the capacities are distributed to sectors (residential and services) based on sectoral consumption and urban/rural based population distribution.
Inputs:
-------
- Existing heating generators: `data/existing_heating_raw.csv` per country
- Population layout: `resources/{run_name}/pop_layout_s<simpl>_<clusters>.csv`. Output of `scripts/build_clustered_population_layout.py`
- Population layout with energy demands: `resources/<run_name>/pop_weighted_energy_totals_s<simpl>_<clusters>.csv`
- District heating share: `resources/<run_name>/district_heat_share_base_s<simpl>_<clusters>_<planning_horizons>.csv`
Outputs:
--------
- Existing heat generation capacities distributed to nodes: `resources/{run_name}/existing_heating_distribution_base_s_{clusters}_{planning_horizons}.csv`
Relevant settings:
------------------
.. code:: yaml
scenario:
planning_horizons
sector:
existing_capacities:
Notes:
------
- Data for Albania, Montenegro and Macedonia is not included in input database and assumed 0.
- Coal and oil boilers are assimilated to oil boilers.
- All ground-source heat pumps are assumed in rural areas and all air-source heat pumps are assumed to be in urban areas.
References:
-----------
- "Mapping and analyses of the current and future (2020 - 2030) heating/cooling fuel deployment (fossil/renewables)" (https://energy.ec.europa.eu/publications/mapping-and-analyses-current-and-future-2020-2030-heatingcooling-fuel-deployment-fossilrenewables-1_en)
"""
import country_converter as coco
import numpy as np
import pandas as pd
from _helpers import set_scenario_config
cc = coco.CountryConverter()
def build_existing_heating():
# retrieve existing heating capacities
# Add existing heating capacities, data comes from the study
# "Mapping and analyses of the current and future (2020 - 2030)
# heating/cooling fuel deployment (fossil/renewables) "
# https://energy.ec.europa.eu/publications/mapping-and-analyses-current-and-future-2020-2030-heatingcooling-fuel-deployment-fossilrenewables-1_en
# file: "WP2_DataAnnex_1_BuildingTechs_ForPublication_201603.xls" -> "existing_heating_raw.csv".
# data is for buildings only (i.e. NOT district heating) and represents the year 2012
# TODO start from original file
existing_heating = pd.read_csv(
snakemake.input.existing_heating, index_col=0, header=0
)
# data for Albania, Montenegro and Macedonia not included in database
existing_heating.loc["Albania"] = np.nan
existing_heating.loc["Montenegro"] = np.nan
existing_heating.loc["Macedonia"] = np.nan
existing_heating.fillna(0.0, inplace=True)
# convert GW to MW
existing_heating *= 1e3
existing_heating.index = cc.convert(existing_heating.index, to="iso2")
# coal and oil boilers are assimilated to oil boilers
existing_heating["oil boiler"] = (
existing_heating["oil boiler"] + existing_heating["coal boiler"]
)
existing_heating.drop(["coal boiler"], axis=1, inplace=True)
# distribute technologies to nodes by population
pop_layout = pd.read_csv(snakemake.input.clustered_pop_layout, index_col=0)
nodal_heating = existing_heating.loc[pop_layout.ct]
nodal_heating.index = pop_layout.index
nodal_heating = nodal_heating.multiply(pop_layout.fraction, axis=0)
district_heat_info = pd.read_csv(snakemake.input.district_heat_share, index_col=0)
urban_fraction = district_heat_info["urban fraction"]
energy_layout = pd.read_csv(
snakemake.input.clustered_pop_energy_layout, index_col=0
)
uses = ["space", "water"]
sectors = ["residential", "services"]
nodal_sectoral_totals = pd.DataFrame(dtype=float)
for sector in sectors:
nodal_sectoral_totals[sector] = energy_layout[
[f"total {sector} {use}" for use in uses]
].sum(axis=1)
nodal_sectoral_fraction = nodal_sectoral_totals.div(
nodal_sectoral_totals.sum(axis=1), axis=0
)
nodal_heat_name_fraction = pd.DataFrame(index=district_heat_info.index, dtype=float)
nodal_heat_name_fraction["urban central"] = 0.0
for sector in sectors:
nodal_heat_name_fraction[f"{sector} rural"] = nodal_sectoral_fraction[
sector
] * (1 - urban_fraction)
nodal_heat_name_fraction[f"{sector} urban decentral"] = (
nodal_sectoral_fraction[sector] * urban_fraction
)
nodal_heat_name_tech = pd.concat(
{
name: nodal_heating.multiply(nodal_heat_name_fraction[name], axis=0)
for name in nodal_heat_name_fraction.columns
},
axis=1,
names=["heat name", "technology"],
)
# move all ground HPs to rural, all air to urban
for sector in sectors:
nodal_heat_name_tech[(f"{sector} rural", "ground heat pump")] += (
nodal_heat_name_tech[("urban central", "ground heat pump")]
* nodal_sectoral_fraction[sector]
+ nodal_heat_name_tech[(f"{sector} urban decentral", "ground heat pump")]
)
nodal_heat_name_tech[(f"{sector} urban decentral", "ground heat pump")] = 0.0
nodal_heat_name_tech[
(f"{sector} urban decentral", "air heat pump")
] += nodal_heat_name_tech[(f"{sector} rural", "air heat pump")]
nodal_heat_name_tech[(f"{sector} rural", "air heat pump")] = 0.0
nodal_heat_name_tech[("urban central", "ground heat pump")] = 0.0
nodal_heat_name_tech.to_csv(snakemake.output.existing_heating_distribution)
if __name__ == "__main__":
if "snakemake" not in globals():
from _helpers import mock_snakemake
snakemake = mock_snakemake(
"build_existing_heating_distribution",
clusters=48,
planning_horizons=2050,
)
set_scenario_config(snakemake)
build_existing_heating()