Merge branch 'update-district-heating-cops' of https://github.com/PyPSA/pypsa-eur into update-district-heating-cops

This commit is contained in:
AmosSchledorn 2024-08-02 17:27:18 +02:00
commit 10e0b30a40
6 changed files with 78 additions and 36 deletions

View File

@ -965,7 +965,7 @@ rule prepare_sector_network:
emissions_scope=config_provider("energy", "emissions"), emissions_scope=config_provider("energy", "emissions"),
RDIR=RDIR, RDIR=RDIR,
heat_pump_sources=config_provider("sector", "heat_pump_sources"), heat_pump_sources=config_provider("sector", "heat_pump_sources"),
heat_systems=config_provider("sector", "heat_systems") heat_systems=config_provider("sector", "heat_systems"),
input: input:
unpack(input_profile_offwind), unpack(input_profile_offwind),
**rules.cluster_gas_network.output, **rules.cluster_gas_network.output,

View File

@ -7,11 +7,15 @@ import numpy as np
import pandas as pd import pandas as pd
import xarray as xr import xarray as xr
from _helpers import set_scenario_config from _helpers import set_scenario_config
import sys; sys.path.append("..")
from scripts.enums.HeatSystemType import HeatSystemType
from CentralHeatingCopApproximator import CentralHeatingCopApproximator from CentralHeatingCopApproximator import CentralHeatingCopApproximator
from DecentralHeatingCopApproximator import DecentralHeatingCopApproximator from DecentralHeatingCopApproximator import DecentralHeatingCopApproximator
from scripts.enums.HeatSystemType import HeatSystemType
import sys
sys.path.append("..")
def get_cop( def get_cop(
heat_system_type: str, heat_system_type: str,
@ -52,7 +56,6 @@ def get_cop(
).approximate_cop() ).approximate_cop()
if __name__ == "__main__": if __name__ == "__main__":
if "snakemake" not in globals(): if "snakemake" not in globals():
from _helpers import mock_snakemake from _helpers import mock_snakemake

View File

@ -1,7 +1,9 @@
# -*- coding: utf-8 -*-
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
from enum import Enum from enum import Enum
class HeatSector(Enum): class HeatSector(Enum):
""" """
Enumeration class representing different heat sectors. Enumeration class representing different heat sectors.
@ -22,4 +24,3 @@ class HeatSector(Enum):
str: The string representation of the heat sector. str: The string representation of the heat sector.
""" """
return self.value return self.value

View File

@ -5,8 +5,9 @@
from enum import Enum from enum import Enum
from .HeatSystemType import HeatSystemType from scripts.enums.HeatSector import HeatSector
from .HeatSector import HeatSector from scripts.enums.HeatSystemType import HeatSystemType
class HeatSystem(Enum): class HeatSystem(Enum):
""" """
@ -99,7 +100,10 @@ class HeatSystem(Enum):
""" """
if self == HeatSystem.URBAN_CENTRAL: if self == HeatSystem.URBAN_CENTRAL:
return HeatSystemType.URBAN_CENTRAL return HeatSystemType.URBAN_CENTRAL
elif self == HeatSystem.RESIDENTIAL_URBAN_DECENTRAL or self == HeatSystem.SERVICES_URBAN_DECENTRAL: elif (
self == HeatSystem.RESIDENTIAL_URBAN_DECENTRAL
or self == HeatSystem.SERVICES_URBAN_DECENTRAL
):
return HeatSystemType.URBAN_DECENTRAL return HeatSystemType.URBAN_DECENTRAL
elif self == HeatSystem.RESIDENTIAL_RURAL or self == HeatSystem.SERVICES_RURAL: elif self == HeatSystem.RESIDENTIAL_RURAL or self == HeatSystem.SERVICES_RURAL:
return HeatSystemType.RURAL return HeatSystemType.RURAL
@ -127,7 +131,7 @@ class HeatSystem(Enum):
): ):
return HeatSector.SERVICES return HeatSector.SERVICES
else: else:
'tot' "tot"
@property @property
def is_rural(self) -> bool: def is_rural(self) -> bool:
@ -154,7 +158,10 @@ class HeatSystem(Enum):
bool bool
True if the heat system is for urban decentralized areas, False otherwise. True if the heat system is for urban decentralized areas, False otherwise.
""" """
if self == HeatSystem.RESIDENTIAL_URBAN_DECENTRAL or self == HeatSystem.SERVICES_URBAN_DECENTRAL: if (
self == HeatSystem.RESIDENTIAL_URBAN_DECENTRAL
or self == HeatSystem.SERVICES_URBAN_DECENTRAL
):
return True return True
else: else:
return False return False
@ -166,12 +173,14 @@ class HeatSystem(Enum):
Returns Returns
------- -------
bool True if the heat system is for urban areas, False otherwise. """ bool True if the heat system is for urban areas, False otherwise.
"""
return not self.is_rural return not self.is_rural
def heat_demand_weighting(self, urban_fraction=None, dist_fraction=None) -> float: def heat_demand_weighting(self, urban_fraction=None, dist_fraction=None) -> float:
""" """
Calculates the heat demand weighting based on urban fraction and distribution fraction. Calculates the heat demand weighting based on urban fraction and
distribution fraction.
Parameters Parameters
---------- ----------
@ -214,5 +223,3 @@ class HeatSystem(Enum):
The name for the heat pump costs. The name for the heat pump costs.
""" """
return f"{self.central_or_decentral} {heat_source}-sourced heat pump" return f"{self.central_or_decentral} {heat_source}-sourced heat pump"

View File

@ -5,6 +5,7 @@
from enum import Enum from enum import Enum
class HeatSystemType(Enum): class HeatSystemType(Enum):
""" """
Enumeration representing different types of heat systems. Enumeration representing different types of heat systems.
@ -32,4 +33,3 @@ class HeatSystemType(Enum):
bool: True if the heat system type is central, False otherwise. bool: True if the heat system type is central, False otherwise.
""" """
return self == HeatSystemType.URBAN_CENTRAL return self == HeatSystemType.URBAN_CENTRAL

View File

@ -22,9 +22,6 @@ from _helpers import (
set_scenario_config, set_scenario_config,
update_config_from_wildcards, update_config_from_wildcards,
) )
from scripts.enums.HeatSystem import HeatSystem
from scripts.enums.HeatSystemType import HeatSystemType
from scripts.enums.HeatSector import HeatSector
from add_electricity import calculate_annuity, sanitize_carriers, sanitize_locations from add_electricity import calculate_annuity, sanitize_carriers, sanitize_locations
from build_energy_totals import ( from build_energy_totals import (
build_co2_totals, build_co2_totals,
@ -40,6 +37,10 @@ from pypsa.geo import haversine_pts
from pypsa.io import import_components_from_dataframe from pypsa.io import import_components_from_dataframe
from scipy.stats import beta from scipy.stats import beta
from scripts.enums.HeatSector import HeatSector
from scripts.enums.HeatSystem import HeatSystem
from scripts.enums.HeatSystemType import HeatSystemType
spatial = SimpleNamespace() spatial = SimpleNamespace()
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -1833,7 +1834,11 @@ def add_heat(n, costs):
solar_thermal = options["solar_cf_correction"] * solar_thermal / 1e3 solar_thermal = options["solar_cf_correction"] * solar_thermal / 1e3
cop = xr.open_dataarray(snakemake.input.cop_profiles) cop = xr.open_dataarray(snakemake.input.cop_profiles)
for heat_system in HeatSystem: #this loops through all heat systems defined in _entities.HeatSystem for (
heat_system
) in (
HeatSystem
): # this loops through all heat systems defined in _entities.HeatSystem
if heat_system == HeatSystem.URBAN_CENTRAL: if heat_system == HeatSystem.URBAN_CENTRAL:
nodes = dist_fraction.index[dist_fraction > 0] nodes = dist_fraction.index[dist_fraction > 0]
@ -1864,17 +1869,23 @@ def add_heat(n, costs):
) )
## Add heat load ## Add heat load
factor = heat_system.heat_demand_weighting(urban_fraction=urban_fraction[nodes], dist_fraction=dist_fraction[nodes]) factor = heat_system.heat_demand_weighting(
urban_fraction=urban_fraction[nodes], dist_fraction=dist_fraction[nodes]
)
if not heat_system == HeatSystem.URBAN_CENTRAL: if not heat_system == HeatSystem.URBAN_CENTRAL:
heat_load = ( heat_load = (
heat_demand[[heat_system.sector.value + " water", heat_system.sector.value + " space"]] heat_demand[
[
heat_system.sector.value + " water",
heat_system.sector.value + " space",
]
]
.T.groupby(level=1) .T.groupby(level=1)
.sum() .sum()
.T[nodes] .T[nodes]
.multiply(factor) .multiply(factor)
) )
if heat_system == HeatSystem.URBAN_CENTRAL: if heat_system == HeatSystem.URBAN_CENTRAL:
heat_load = ( heat_load = (
heat_demand.T.groupby(level=1) heat_demand.T.groupby(level=1)
@ -1895,10 +1906,16 @@ def add_heat(n, costs):
) )
## Add heat pumps ## Add heat pumps
for heat_source in snakemake.params.heat_pump_sources[heat_system.system_type.value]: for heat_source in snakemake.params.heat_pump_sources[
heat_system.system_type.value
]:
costs_name = heat_system.heat_pump_costs_name(heat_source) costs_name = heat_system.heat_pump_costs_name(heat_source)
efficiency = ( efficiency = (
cop.sel(heat_system=heat_system.system_type.value, heat_source=heat_source, name=nodes) cop.sel(
heat_system=heat_system.system_type.value,
heat_source=heat_source,
name=nodes,
)
.to_pandas() .to_pandas()
.reindex(index=n.snapshots) .reindex(index=n.snapshots)
if options["time_dep_hp_cop"] if options["time_dep_hp_cop"]
@ -1951,7 +1968,9 @@ def add_heat(n, costs):
p_nom_extendable=True, p_nom_extendable=True,
) )
tes_time_constant_days = options["tes_tau"][heat_system.central_or_decentral] tes_time_constant_days = options["tes_tau"][
heat_system.central_or_decentral
]
n.madd( n.madd(
"Store", "Store",
@ -1961,8 +1980,12 @@ def add_heat(n, costs):
e_nom_extendable=True, e_nom_extendable=True,
carrier=f"{heat_system} water tanks", carrier=f"{heat_system} water tanks",
standing_loss=1 - np.exp(-1 / 24 / tes_time_constant_days), standing_loss=1 - np.exp(-1 / 24 / tes_time_constant_days),
capital_cost=costs.at[heat_system.central_or_decentral + " water tank storage", "fixed"], capital_cost=costs.at[
lifetime=costs.at[heat_system.central_or_decentral + " water tank storage", "lifetime"], heat_system.central_or_decentral + " water tank storage", "fixed"
],
lifetime=costs.at[
heat_system.central_or_decentral + " water tank storage", "lifetime"
],
) )
if options["resistive_heaters"]: if options["resistive_heaters"]:
@ -2011,10 +2034,14 @@ def add_heat(n, costs):
bus=nodes + f" {heat_system} heat", bus=nodes + f" {heat_system} heat",
carrier=f"{heat_system} solar thermal", carrier=f"{heat_system} solar thermal",
p_nom_extendable=True, p_nom_extendable=True,
capital_cost=costs.at[heat_system.central_or_decentral + " solar thermal", "fixed"] capital_cost=costs.at[
heat_system.central_or_decentral + " solar thermal", "fixed"
]
* overdim_factor, * overdim_factor,
p_max_pu=solar_thermal[nodes], p_max_pu=solar_thermal[nodes],
lifetime=costs.at[heat_system.central_or_decentral + " solar thermal", "lifetime"], lifetime=costs.at[
heat_system.central_or_decentral + " solar thermal", "lifetime"
],
) )
if options["chp"] and heat_system == HeatSystem.URBAN_CENTRAL: if options["chp"] and heat_system == HeatSystem.URBAN_CENTRAL:
@ -2074,7 +2101,11 @@ def add_heat(n, costs):
lifetime=costs.at["central gas CHP", "lifetime"], lifetime=costs.at["central gas CHP", "lifetime"],
) )
if options["chp"] and options["micro_chp"] and heat_system.value != "urban central": if (
options["chp"]
and options["micro_chp"]
and heat_system.value != "urban central"
):
n.madd( n.madd(
"Link", "Link",
nodes + f" {heat_system} micro gas CHP", nodes + f" {heat_system} micro gas CHP",