pass heat source/system type to prepare_sector_network and add_existing_baseyear

This commit is contained in:
AmosSchledorn 2024-07-29 18:29:27 +02:00
parent 0edf566e3f
commit 29479c50d0
7 changed files with 176 additions and 196 deletions

View File

@ -417,9 +417,9 @@ sector:
isentropic_compressor_efficiency: 0.8 isentropic_compressor_efficiency: 0.8
heat_loss: 0.0 heat_loss: 0.0
heat_pump_sources: heat_pump_sources:
central_heating: central:
- air - air
decentral_heating: decentral:
- air - air
- ground - ground
cluster_heat_buses: true cluster_heat_buses: true

View File

@ -214,6 +214,13 @@ rule build_temperature_profiles:
script: script:
"../scripts/build_temperature_profiles.py" "../scripts/build_temperature_profiles.py"
# def output_cop(wildcards):
# return {
# f"cop_{source}_{sink}": resources(
# "cop_" + source + "_" + sink + "_" + "elec_s{simpl}_{clusters}.nc"
# )
# for sink, source in config["sector"]["heat_pump_sources"].items()
# }
rule build_cop_profiles: rule build_cop_profiles:
params: params:
@ -237,21 +244,7 @@ rule build_cop_profiles:
temp_soil_total=resources("temp_soil_total_elec_s{simpl}_{clusters}.nc"), temp_soil_total=resources("temp_soil_total_elec_s{simpl}_{clusters}.nc"),
temp_air_total=resources("temp_air_total_elec_s{simpl}_{clusters}.nc"), temp_air_total=resources("temp_air_total_elec_s{simpl}_{clusters}.nc"),
output: output:
**{f"cop_{source}_{sink}": resources( cop_profiles=resources("cop_profiles_elec_s{simpl}_{clusters}.nc"),
"cop_" + source + "_" + {sink} + "_" + "elec_s{simpl}_{clusters}.nc"
) for sink, source in config_provider("sector", "heat_pump_sources").items()},
# cop_air_decentral_heating=resources(
# "cop_air_decentral_heating_elec_s{simpl}_{clusters}.nc"
# ),
# cop_soil_decentral_heating=resources(
# "cop_soil_decentral_heating_elec_s{simpl}_{clusters}.nc"
# ),
# cop_air_central_heating=resources(
# "cop_air_central_heating_elec_s{simpl}_{clusters}.nc"
# ),
# cop_soil_central_heating=resources(
# "cop_soil_central_heating_elec_s{simpl}_{clusters}.nc"
# ),
resources: resources:
mem_mb=20000, mem_mb=20000,
log: log:
@ -969,6 +962,7 @@ rule prepare_sector_network:
adjustments=config_provider("adjustments", "sector"), adjustments=config_provider("adjustments", "sector"),
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"),
input: input:
unpack(input_profile_offwind), unpack(input_profile_offwind),
**rules.cluster_gas_network.output, **rules.cluster_gas_network.output,
@ -1045,18 +1039,7 @@ rule prepare_sector_network:
), ),
temp_soil_total=resources("temp_soil_total_elec_s{simpl}_{clusters}.nc"), temp_soil_total=resources("temp_soil_total_elec_s{simpl}_{clusters}.nc"),
temp_air_total=resources("temp_air_total_elec_s{simpl}_{clusters}.nc"), temp_air_total=resources("temp_air_total_elec_s{simpl}_{clusters}.nc"),
cop_soil_decentral_heating=resources( cop_profiles=resources("cop_profiles_elec_s{simpl}_{clusters}.nc"),
"cop_soil_decentral_heating_elec_s{simpl}_{clusters}.nc"
),
cop_air_decentral_heating=resources(
"cop_air_decentral_heating_elec_s{simpl}_{clusters}.nc"
),
cop_air_central_heating=resources(
"cop_air_central_heating_elec_s{simpl}_{clusters}.nc"
),
cop_soil_central_heating=resources(
"cop_soil_central_heating_elec_s{simpl}_{clusters}.nc"
),
solar_thermal_total=lambda w: ( solar_thermal_total=lambda w: (
resources("solar_thermal_total_elec_s{simpl}_{clusters}.nc") resources("solar_thermal_total_elec_s{simpl}_{clusters}.nc")
if config_provider("sector", "solar_thermal")(w) if config_provider("sector", "solar_thermal")(w)

View File

@ -9,6 +9,7 @@ rule add_existing_baseyear:
sector=config_provider("sector"), sector=config_provider("sector"),
existing_capacities=config_provider("existing_capacities"), existing_capacities=config_provider("existing_capacities"),
costs=config_provider("costs"), costs=config_provider("costs"),
heat_pump_sources=config_provider("sector", "heat_pump_sources"),
input: input:
network=RESULTS network=RESULTS
+ "prenetworks/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.nc", + "prenetworks/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.nc",
@ -21,18 +22,7 @@ rule add_existing_baseyear:
config_provider("scenario", "planning_horizons", 0)(w) config_provider("scenario", "planning_horizons", 0)(w)
) )
), ),
cop_soil_decentral_heating=resources( cop_profiles=resources("cop_profiles_elec_s{simpl}_{clusters}.nc"),
"cop_soil_decentral_heating_elec_s{simpl}_{clusters}.nc"
),
cop_air_decentral_heating=resources(
"cop_air_decentral_heating_elec_s{simpl}_{clusters}.nc"
),
cop_air_central_heating=resources(
"cop_air_central_heating_elec_s{simpl}_{clusters}.nc"
),
cop_soil_central_heating=resources(
"cop_soil_central_heating_elec_s{simpl}_{clusters}.nc"
),
existing_heating_distribution=resources( existing_heating_distribution=resources(
"existing_heating_distribution_elec_s{simpl}_{clusters}_{planning_horizons}.csv" "existing_heating_distribution_elec_s{simpl}_{clusters}_{planning_horizons}.csv"
), ),

View File

@ -442,27 +442,27 @@ def add_heating_capacities_installed_before_baseyear(
""" """
logger.debug(f"Adding heating capacities installed before {baseyear}") logger.debug(f"Adding heating capacities installed before {baseyear}")
for name in existing_heating.columns.get_level_values(0).unique(): for heat_system in existing_heating.columns.get_level_values(0).unique():
name_type = "central" if name == "urban central" else "decentral" system_type = "central" if heat_system == "urban central" else "decentral"
nodes = pd.Index(n.buses.location[n.buses.index.str.contains(f"{name} heat")]) nodes = pd.Index(n.buses.location[n.buses.index.str.contains(f"{heat_system} heat")])
if (name_type != "central") and options["electricity_distribution_grid"]: if (system_type != "central") and options["electricity_distribution_grid"]:
nodes_elec = nodes + " low voltage" nodes_elec = nodes + " low voltage"
else: else:
nodes_elec = nodes nodes_elec = nodes
heat_pump_type = "air" if "urban" in name else "ground"
# Add heat pumps # Add heat pumps
costs_name = f"decentral {heat_pump_type}-sourced heat pump" heat_source = snakemake.params.heat_pump_sources[system_type]
costs_name = f"{system_type} {heat_source}-sourced heat pump"
efficiency = ( efficiency = (
cop[f"{heat_pump_type} {name_type}"][nodes] cop.sel(heat_system=system_type, heat_source=heat_source, name=nodes).to_pandas().reindex(index=n.snapshots)
if time_dep_hp_cop if options["time_dep_hp_cop"]
else costs.at[costs_name, "efficiency"] else costs.at[costs_name, "efficiency"]
) )
too_large_grouping_years = [gy for gy in grouping_years if gy >= int(baseyear)] too_large_grouping_years = [gy for gy in grouping_years if gy >= int(baseyear)]
if too_large_grouping_years: if too_large_grouping_years:
logger.warning( logger.warning(
@ -491,14 +491,14 @@ def add_heating_capacities_installed_before_baseyear(
n.madd( n.madd(
"Link", "Link",
nodes, nodes,
suffix=f" {name} {heat_pump_type} heat pump-{grouping_year}", suffix=f" {heat_system} {heat_source} heat pump-{grouping_year}",
bus0=nodes_elec, bus0=nodes_elec,
bus1=nodes + " " + name + " heat", bus1=nodes + " " + heat_system + " heat",
carrier=f"{name} {heat_pump_type} heat pump", carrier=f"{heat_system} {heat_source} heat pump",
efficiency=efficiency, efficiency=efficiency,
capital_cost=costs.at[costs_name, "efficiency"] capital_cost=costs.at[costs_name, "efficiency"]
* costs.at[costs_name, "fixed"], * costs.at[costs_name, "fixed"],
p_nom=existing_heating.loc[nodes, (name, f"{heat_pump_type} heat pump")] p_nom=existing_heating.loc[nodes, (heat_system, f"{heat_source} heat pump")]
* ratio * ratio
/ costs.at[costs_name, "efficiency"], / costs.at[costs_name, "efficiency"],
build_year=int(grouping_year), build_year=int(grouping_year),
@ -509,66 +509,66 @@ def add_heating_capacities_installed_before_baseyear(
n.madd( n.madd(
"Link", "Link",
nodes, nodes,
suffix=f" {name} resistive heater-{grouping_year}", suffix=f" {heat_system} resistive heater-{grouping_year}",
bus0=nodes_elec, bus0=nodes_elec,
bus1=nodes + " " + name + " heat", bus1=nodes + " " + heat_system + " heat",
carrier=name + " resistive heater", carrier=heat_system + " resistive heater",
efficiency=costs.at[f"{name_type} resistive heater", "efficiency"], efficiency=costs.at[f"{system_type} resistive heater", "efficiency"],
capital_cost=( capital_cost=(
costs.at[f"{name_type} resistive heater", "efficiency"] costs.at[f"{system_type} resistive heater", "efficiency"]
* costs.at[f"{name_type} resistive heater", "fixed"] * costs.at[f"{system_type} resistive heater", "fixed"]
), ),
p_nom=( p_nom=(
existing_heating.loc[nodes, (name, "resistive heater")] existing_heating.loc[nodes, (heat_system, "resistive heater")]
* ratio * ratio
/ costs.at[f"{name_type} resistive heater", "efficiency"] / costs.at[f"{system_type} resistive heater", "efficiency"]
), ),
build_year=int(grouping_year), build_year=int(grouping_year),
lifetime=costs.at[f"{name_type} resistive heater", "lifetime"], lifetime=costs.at[f"{system_type} resistive heater", "lifetime"],
) )
n.madd( n.madd(
"Link", "Link",
nodes, nodes,
suffix=f" {name} gas boiler-{grouping_year}", suffix=f" {heat_system} gas boiler-{grouping_year}",
bus0="EU gas" if "EU gas" in spatial.gas.nodes else nodes + " gas", bus0="EU gas" if "EU gas" in spatial.gas.nodes else nodes + " gas",
bus1=nodes + " " + name + " heat", bus1=nodes + " " + heat_system + " heat",
bus2="co2 atmosphere", bus2="co2 atmosphere",
carrier=name + " gas boiler", carrier=heat_system + " gas boiler",
efficiency=costs.at[f"{name_type} gas boiler", "efficiency"], efficiency=costs.at[f"{system_type} gas boiler", "efficiency"],
efficiency2=costs.at["gas", "CO2 intensity"], efficiency2=costs.at["gas", "CO2 intensity"],
capital_cost=( capital_cost=(
costs.at[f"{name_type} gas boiler", "efficiency"] costs.at[f"{system_type} gas boiler", "efficiency"]
* costs.at[f"{name_type} gas boiler", "fixed"] * costs.at[f"{system_type} gas boiler", "fixed"]
), ),
p_nom=( p_nom=(
existing_heating.loc[nodes, (name, "gas boiler")] existing_heating.loc[nodes, (heat_system, "gas boiler")]
* ratio * ratio
/ costs.at[f"{name_type} gas boiler", "efficiency"] / costs.at[f"{system_type} gas boiler", "efficiency"]
), ),
build_year=int(grouping_year), build_year=int(grouping_year),
lifetime=costs.at[f"{name_type} gas boiler", "lifetime"], lifetime=costs.at[f"{system_type} gas boiler", "lifetime"],
) )
n.madd( n.madd(
"Link", "Link",
nodes, nodes,
suffix=f" {name} oil boiler-{grouping_year}", suffix=f" {heat_system} oil boiler-{grouping_year}",
bus0=spatial.oil.nodes, bus0=spatial.oil.nodes,
bus1=nodes + " " + name + " heat", bus1=nodes + " " + heat_system + " heat",
bus2="co2 atmosphere", bus2="co2 atmosphere",
carrier=name + " oil boiler", carrier=heat_system + " oil boiler",
efficiency=costs.at["decentral oil boiler", "efficiency"], efficiency=costs.at["decentral oil boiler", "efficiency"],
efficiency2=costs.at["oil", "CO2 intensity"], efficiency2=costs.at["oil", "CO2 intensity"],
capital_cost=costs.at["decentral oil boiler", "efficiency"] capital_cost=costs.at["decentral oil boiler", "efficiency"]
* costs.at["decentral oil boiler", "fixed"], * costs.at["decentral oil boiler", "fixed"],
p_nom=( p_nom=(
existing_heating.loc[nodes, (name, "oil boiler")] existing_heating.loc[nodes, (heat_system, "oil boiler")]
* ratio * ratio
/ costs.at["decentral oil boiler", "efficiency"] / costs.at["decentral oil boiler", "efficiency"]
), ),
build_year=int(grouping_year), build_year=int(grouping_year),
lifetime=costs.at[f"{name_type} gas boiler", "lifetime"], lifetime=costs.at[f"{system_type} gas boiler", "lifetime"],
) )
# delete links with p_nom=nan corresponding to extra nodes in country # delete links with p_nom=nan corresponding to extra nodes in country

View File

@ -39,12 +39,12 @@ class DecentralHeatingCopApproximator(BaseCopApproximator):
return_temperature_celsius : Union[xr.DataArray, np.array] return_temperature_celsius : Union[xr.DataArray, np.array]
The return temperature in Celsius. The return temperature in Celsius.
source: str source: str
The source of the heat pump. Must be either 'air' or 'soil' The source of the heat pump. Must be either 'air' or 'ground'
""" """
self.delta_t = forward_temperature_celsius - source_inlet_temperature_celsius self.delta_t = forward_temperature_celsius - source_inlet_temperature_celsius
if source_type not in ["air", "soil"]: if source_type not in ["air", "ground"]:
raise ValueError("'source' must be one of ['air', 'soil']") raise ValueError("'source' must be one of ['air', 'ground']")
else: else:
self.source_type = source_type self.source_type = source_type
@ -57,7 +57,7 @@ class DecentralHeatingCopApproximator(BaseCopApproximator):
""" """
if self.source_type == "air": if self.source_type == "air":
return self._approximate_cop_air_source() return self._approximate_cop_air_source()
elif self.source_type == "soil": elif self.source_type == "ground":
return self._approximate_cop_ground_source() return self._approximate_cop_ground_source()
def _approximate_cop_air_source(self) -> Union[xr.DataArray, np.array]: def _approximate_cop_air_source(self) -> Union[xr.DataArray, np.array]:

View File

@ -4,11 +4,39 @@
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
import numpy as np import numpy as np
import pandas as pd
import xarray as xr import xarray as xr
from _helpers import set_scenario_config from _helpers import set_scenario_config
from CentralHeatingCopApproximator import CentralHeatingCopApproximator from CentralHeatingCopApproximator import CentralHeatingCopApproximator
from DecentralHeatingCopApproximator import DecentralHeatingCopApproximator from DecentralHeatingCopApproximator import DecentralHeatingCopApproximator
def get_cop(
heat_system_type: str,
heat_source: str,
source_inlet_temperature_celsius: xr.DataArray,
) -> xr.DataArray:
if heat_system_type == "decentral":
return DecentralHeatingCopApproximator(
forward_temperature_celsius=snakemake.params.heat_pump_sink_T_decentral_heating,
source_inlet_temperature_celsius=source_inlet_temperature_celsius,
source_type=heat_source,
).approximate_cop()
elif heat_system_type == "central":
return CentralHeatingCopApproximator(
forward_temperature_celsius=snakemake.params.forward_temperature_central_heating,
return_temperature_celsius=snakemake.params.return_temperature_central_heating,
source_inlet_temperature_celsius=source_inlet_temperature_celsius,
source_outlet_temperature_celsius=source_inlet_temperature_celsius
- snakemake.params.heat_source_cooling_central_heating,
).approximate_cop()
else:
raise ValueError(
f"Invalid heat system type '{heat_system_type}'. Must be one of ['decentral', 'central']"
)
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
@ -21,30 +49,28 @@ if __name__ == "__main__":
set_scenario_config(snakemake) set_scenario_config(snakemake)
for source_type in ["air", "soil"]: cop_all_system_types = []
# source inlet temperature (air/soil) is based on weather data for heat_system_type, heat_sources in snakemake.params.heat_pump_sources.items():
source_inlet_temperature_celsius = xr.open_dataarray( cop_this_system_type = []
snakemake.input[f"temp_{source_type}_total"] for heat_source in heat_sources:
source_inlet_temperature_celsius = xr.open_dataarray(
snakemake.input[f"temp_{heat_source.replace('ground', 'soil')}_total"]
)
cop_da = get_cop(
heat_system_type=heat_system_type,
heat_source=heat_source,
source_inlet_temperature_celsius=source_inlet_temperature_celsius,
)
cop_this_system_type.append(cop_da)
cop_all_system_types.append(
xr.concat(
cop_this_system_type, dim=pd.Index(heat_sources, name="heat_source")
)
) )
# Approximate COP for decentral (individual) heating cop_dataarray = xr.concat(
cop_individual_heating = DecentralHeatingCopApproximator( cop_all_system_types,
forward_temperature_celsius=snakemake.params.heat_pump_sink_T_decentral_heating, dim=pd.Index(snakemake.params.heat_pump_sources.keys(), name="heat_system"),
source_inlet_temperature_celsius=source_inlet_temperature_celsius, )
source_type=source_type,
).approximate_cop()
cop_individual_heating.to_netcdf(
snakemake.output[f"cop_{source_type}_decentral_heating"]
)
# Approximate COP for central (district) heating cop_dataarray.to_netcdf(snakemake.output.cop_profiles)
cop_central_heating = CentralHeatingCopApproximator(
forward_temperature_celsius=snakemake.params.forward_temperature_central_heating,
return_temperature_celsius=snakemake.params.return_temperature_central_heating,
source_inlet_temperature_celsius=source_inlet_temperature_celsius,
source_outlet_temperature_celsius=source_inlet_temperature_celsius
- snakemake.params.heat_source_cooling_central_heating,
).approximate_cop()
cop_central_heating.to_netcdf(
snakemake.output[f"cop_{source_type}_central_heating"]
)

View File

@ -1825,23 +1825,6 @@ def add_heat(n, costs):
"urban central", "urban central",
] ]
cop = {
"air decentral": xr.open_dataarray(snakemake.input.cop_air_decentral_heating)
.to_pandas()
.reindex(index=n.snapshots),
"ground decentral": xr.open_dataarray(
snakemake.input.cop_soil_decentral_heating
)
.to_pandas()
.reindex(index=n.snapshots),
"air central": xr.open_dataarray(snakemake.input.cop_air_central_heating)
.to_pandas()
.reindex(index=n.snapshots),
"ground central": xr.open_dataarray(snakemake.input.cop_soil_central_heating)
.to_pandas()
.reindex(index=n.snapshots),
}
if options["solar_thermal"]: if options["solar_thermal"]:
solar_thermal = ( solar_thermal = (
xr.open_dataarray(snakemake.input.solar_thermal_total) xr.open_dataarray(snakemake.input.solar_thermal_total)
@ -1851,31 +1834,32 @@ def add_heat(n, costs):
# 1e3 converts from W/m^2 to MW/(1000m^2) = kW/m^2 # 1e3 converts from W/m^2 to MW/(1000m^2) = kW/m^2
solar_thermal = options["solar_cf_correction"] * solar_thermal / 1e3 solar_thermal = options["solar_cf_correction"] * solar_thermal / 1e3
for name in heat_systems: cop = xr.open_dataarray(snakemake.input.cop_profiles)
name_type = "central" if name == "urban central" else "decentral" for heat_system in heat_systems:
system_type = "central" if heat_system == "urban central" else "decentral"
if name == "urban central": if heat_system == "urban central":
nodes = dist_fraction.index[dist_fraction > 0] nodes = dist_fraction.index[dist_fraction > 0]
else: else:
nodes = pop_layout.index nodes = pop_layout.index
n.add("Carrier", name + " heat") n.add("Carrier", heat_system + " heat")
n.madd( n.madd(
"Bus", "Bus",
nodes + f" {name} heat", nodes + f" {heat_system} heat",
location=nodes, location=nodes,
carrier=name + " heat", carrier=heat_system + " heat",
unit="MWh_th", unit="MWh_th",
) )
if name == "urban central" and options.get("central_heat_vent"): if heat_system == "urban central" and options.get("central_heat_vent"):
n.madd( n.madd(
"Generator", "Generator",
nodes + f" {name} heat vent", nodes + f" {heat_system} heat vent",
bus=nodes + f" {name} heat", bus=nodes + f" {heat_system} heat",
location=nodes, location=nodes,
carrier=name + " heat vent", carrier=heat_system + " heat vent",
p_nom_extendable=True, p_nom_extendable=True,
p_max_pu=0, p_max_pu=0,
p_min_pu=-1, p_min_pu=-1,
@ -1886,18 +1870,18 @@ def add_heat(n, costs):
for sector in sectors: for sector in sectors:
# heat demand weighting # heat demand weighting
if "rural" in name: if "rural" in heat_system:
factor = 1 - urban_fraction[nodes] factor = 1 - urban_fraction[nodes]
elif "urban central" in name: elif "urban central" in heat_system:
factor = dist_fraction[nodes] factor = dist_fraction[nodes]
elif "urban decentral" in name: elif "urban decentral" in heat_system:
factor = urban_fraction[nodes] - dist_fraction[nodes] factor = urban_fraction[nodes] - dist_fraction[nodes]
else: else:
raise NotImplementedError( raise NotImplementedError(
f" {name} not in " f"heat systems: {heat_systems}" f" {heat_system} not in " f"heat systems: {heat_systems}"
) )
if sector in name: if sector in heat_system:
heat_load = ( heat_load = (
heat_demand[[sector + " water", sector + " space"]] heat_demand[[sector + " water", sector + " space"]]
.T.groupby(level=1) .T.groupby(level=1)
@ -1906,7 +1890,7 @@ def add_heat(n, costs):
.multiply(factor) .multiply(factor)
) )
if name == "urban central": if heat_system == "urban central":
heat_load = ( heat_load = (
heat_demand.T.groupby(level=1) heat_demand.T.groupby(level=1)
.sum() .sum()
@ -1919,20 +1903,17 @@ def add_heat(n, costs):
n.madd( n.madd(
"Load", "Load",
nodes, nodes,
suffix=f" {name} heat", suffix=f" {heat_system} heat",
bus=nodes + f" {name} heat", bus=nodes + f" {heat_system} heat",
carrier=name + " heat", carrier=heat_system + " heat",
p_set=heat_load, p_set=heat_load,
) )
## Add heat pumps ## Add heat pumps
for heat_source in snakemake.params.heat_pump_sources[system_type]:
heat_pump_types = ["air"] if "urban" in name else ["ground", "air"] costs_name = f"{system_type} {heat_source}-sourced heat pump"
for heat_pump_type in heat_pump_types:
costs_name = f"{name_type} {heat_pump_type}-sourced heat pump"
efficiency = ( efficiency = (
cop[f"{heat_pump_type} {name_type}"][nodes] cop.sel(heat_system=system_type, heat_source=heat_source, name=nodes).to_pandas().reindex(index=n.snapshots)
if options["time_dep_hp_cop"] if options["time_dep_hp_cop"]
else costs.at[costs_name, "efficiency"] else costs.at[costs_name, "efficiency"]
) )
@ -1940,10 +1921,10 @@ def add_heat(n, costs):
n.madd( n.madd(
"Link", "Link",
nodes, nodes,
suffix=f" {name} {heat_pump_type} heat pump", suffix=f" {heat_system} {heat_source} heat pump",
bus0=nodes, bus0=nodes,
bus1=nodes + f" {name} heat", bus1=nodes + f" {heat_system} heat",
carrier=f"{name} {heat_pump_type} heat pump", carrier=f"{heat_system} {heat_source} heat pump",
efficiency=efficiency, efficiency=efficiency,
capital_cost=costs.at[costs_name, "efficiency"] capital_cost=costs.at[costs_name, "efficiency"]
* costs.at[costs_name, "fixed"] * costs.at[costs_name, "fixed"]
@ -1953,59 +1934,59 @@ def add_heat(n, costs):
) )
if options["tes"]: if options["tes"]:
n.add("Carrier", name + " water tanks") n.add("Carrier", heat_system + " water tanks")
n.madd( n.madd(
"Bus", "Bus",
nodes + f" {name} water tanks", nodes + f" {heat_system} water tanks",
location=nodes, location=nodes,
carrier=name + " water tanks", carrier=heat_system + " water tanks",
unit="MWh_th", unit="MWh_th",
) )
n.madd( n.madd(
"Link", "Link",
nodes + f" {name} water tanks charger", nodes + f" {heat_system} water tanks charger",
bus0=nodes + f" {name} heat", bus0=nodes + f" {heat_system} heat",
bus1=nodes + f" {name} water tanks", bus1=nodes + f" {heat_system} water tanks",
efficiency=costs.at["water tank charger", "efficiency"], efficiency=costs.at["water tank charger", "efficiency"],
carrier=name + " water tanks charger", carrier=heat_system + " water tanks charger",
p_nom_extendable=True, p_nom_extendable=True,
) )
n.madd( n.madd(
"Link", "Link",
nodes + f" {name} water tanks discharger", nodes + f" {heat_system} water tanks discharger",
bus0=nodes + f" {name} water tanks", bus0=nodes + f" {heat_system} water tanks",
bus1=nodes + f" {name} heat", bus1=nodes + f" {heat_system} heat",
carrier=name + " water tanks discharger", carrier=heat_system + " water tanks discharger",
efficiency=costs.at["water tank discharger", "efficiency"], efficiency=costs.at["water tank discharger", "efficiency"],
p_nom_extendable=True, p_nom_extendable=True,
) )
tes_time_constant_days = options["tes_tau"][name_type] tes_time_constant_days = options["tes_tau"][system_type]
n.madd( n.madd(
"Store", "Store",
nodes + f" {name} water tanks", nodes + f" {heat_system} water tanks",
bus=nodes + f" {name} water tanks", bus=nodes + f" {heat_system} water tanks",
e_cyclic=True, e_cyclic=True,
e_nom_extendable=True, e_nom_extendable=True,
carrier=name + " water tanks", carrier=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[name_type + " water tank storage", "fixed"], capital_cost=costs.at[system_type + " water tank storage", "fixed"],
lifetime=costs.at[name_type + " water tank storage", "lifetime"], lifetime=costs.at[system_type + " water tank storage", "lifetime"],
) )
if options["resistive_heaters"]: if options["resistive_heaters"]:
key = f"{name_type} resistive heater" key = f"{system_type} resistive heater"
n.madd( n.madd(
"Link", "Link",
nodes + f" {name} resistive heater", nodes + f" {heat_system} resistive heater",
bus0=nodes, bus0=nodes,
bus1=nodes + f" {name} heat", bus1=nodes + f" {heat_system} heat",
carrier=name + " resistive heater", carrier=heat_system + " resistive heater",
efficiency=costs.at[key, "efficiency"], efficiency=costs.at[key, "efficiency"],
capital_cost=costs.at[key, "efficiency"] capital_cost=costs.at[key, "efficiency"]
* costs.at[key, "fixed"] * costs.at[key, "fixed"]
@ -2015,16 +1996,16 @@ def add_heat(n, costs):
) )
if options["boilers"]: if options["boilers"]:
key = f"{name_type} gas boiler" key = f"{system_type} gas boiler"
n.madd( n.madd(
"Link", "Link",
nodes + f" {name} gas boiler", nodes + f" {heat_system} gas boiler",
p_nom_extendable=True, p_nom_extendable=True,
bus0=spatial.gas.df.loc[nodes, "nodes"].values, bus0=spatial.gas.df.loc[nodes, "nodes"].values,
bus1=nodes + f" {name} heat", bus1=nodes + f" {heat_system} heat",
bus2="co2 atmosphere", bus2="co2 atmosphere",
carrier=name + " gas boiler", carrier=heat_system + " gas boiler",
efficiency=costs.at[key, "efficiency"], efficiency=costs.at[key, "efficiency"],
efficiency2=costs.at["gas", "CO2 intensity"], efficiency2=costs.at["gas", "CO2 intensity"],
capital_cost=costs.at[key, "efficiency"] capital_cost=costs.at[key, "efficiency"]
@ -2034,22 +2015,22 @@ def add_heat(n, costs):
) )
if options["solar_thermal"]: if options["solar_thermal"]:
n.add("Carrier", name + " solar thermal") n.add("Carrier", heat_system + " solar thermal")
n.madd( n.madd(
"Generator", "Generator",
nodes, nodes,
suffix=f" {name} solar thermal collector", suffix=f" {heat_system} solar thermal collector",
bus=nodes + f" {name} heat", bus=nodes + f" {heat_system} heat",
carrier=name + " solar thermal", carrier=heat_system + " solar thermal",
p_nom_extendable=True, p_nom_extendable=True,
capital_cost=costs.at[name_type + " solar thermal", "fixed"] capital_cost=costs.at[system_type + " solar thermal", "fixed"]
* overdim_factor, * overdim_factor,
p_max_pu=solar_thermal[nodes], p_max_pu=solar_thermal[nodes],
lifetime=costs.at[name_type + " solar thermal", "lifetime"], lifetime=costs.at[system_type + " solar thermal", "lifetime"],
) )
if options["chp"] and name == "urban central": if options["chp"] and heat_system == "urban central":
# add gas CHP; biomass CHP is added in biomass section # add gas CHP; biomass CHP is added in biomass section
n.madd( n.madd(
"Link", "Link",
@ -2106,16 +2087,16 @@ 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 name != "urban central": if options["chp"] and options["micro_chp"] and heat_system != "urban central":
n.madd( n.madd(
"Link", "Link",
nodes + f" {name} micro gas CHP", nodes + f" {heat_system} micro gas CHP",
p_nom_extendable=True, p_nom_extendable=True,
bus0=spatial.gas.df.loc[nodes, "nodes"].values, bus0=spatial.gas.df.loc[nodes, "nodes"].values,
bus1=nodes, bus1=nodes,
bus2=nodes + f" {name} heat", bus2=nodes + f" {heat_system} heat",
bus3="co2 atmosphere", bus3="co2 atmosphere",
carrier=name + " micro gas CHP", carrier=heat_system + " micro gas CHP",
efficiency=costs.at["micro CHP", "efficiency"], efficiency=costs.at["micro CHP", "efficiency"],
efficiency2=costs.at["micro CHP", "efficiency-heat"], efficiency2=costs.at["micro CHP", "efficiency-heat"],
efficiency3=costs.at["gas", "CO2 intensity"], efficiency3=costs.at["gas", "CO2 intensity"],
@ -2150,27 +2131,27 @@ def add_heat(n, costs):
heat_demand["services space"] + heat_demand["residential space"] heat_demand["services space"] + heat_demand["residential space"]
) / heat_demand.T.groupby(level=[1]).sum().T ) / heat_demand.T.groupby(level=[1]).sum().T
for name in n.loads[ for heat_system in n.loads[
n.loads.carrier.isin([x + " heat" for x in heat_systems]) n.loads.carrier.isin([x + " heat" for x in heat_systems])
].index: ].index:
node = n.buses.loc[name, "location"] node = n.buses.loc[heat_system, "location"]
ct = pop_layout.loc[node, "ct"] ct = pop_layout.loc[node, "ct"]
# weighting 'f' depending on the size of the population at the node # weighting 'f' depending on the size of the population at the node
if "urban central" in name: if "urban central" in heat_system:
f = dist_fraction[node] f = dist_fraction[node]
elif "urban decentral" in name: elif "urban decentral" in heat_system:
f = urban_fraction[node] - dist_fraction[node] f = urban_fraction[node] - dist_fraction[node]
else: else:
f = 1 - urban_fraction[node] f = 1 - urban_fraction[node]
if f == 0: if f == 0:
continue continue
# get sector name ("residential"/"services"/or both "tot" for urban central) # get sector name ("residential"/"services"/or both "tot" for urban central)
if "urban central" in name: if "urban central" in heat_system:
sec = "tot" sec = "tot"
if "residential" in name: if "residential" in heat_system:
sec = "residential" sec = "residential"
if "services" in name: if "services" in heat_system:
sec = "services" sec = "services"
# get floor aread at node and region (urban/rural) in m^2 # get floor aread at node and region (urban/rural) in m^2
@ -2178,7 +2159,7 @@ def add_heat(n, costs):
pop_layout.loc[node].fraction * floor_area.loc[ct, "value"] * 10**6 pop_layout.loc[node].fraction * floor_area.loc[ct, "value"] * 10**6
).loc[sec] * f ).loc[sec] * f
# total heat demand at node [MWh] # total heat demand at node [MWh]
demand = n.loads_t.p_set[name] demand = n.loads_t.p_set[heat_system]
# space heat demand at node [MWh] # space heat demand at node [MWh]
space_heat_demand = demand * w_space[sec][node] space_heat_demand = demand * w_space[sec][node]
@ -2219,12 +2200,12 @@ def add_heat(n, costs):
# add for each retrofitting strength a generator with heat generation profile following the profile of the heat demand # add for each retrofitting strength a generator with heat generation profile following the profile of the heat demand
for strength in strengths: for strength in strengths:
node_name = " ".join(name.split(" ")[2::]) node_name = " ".join(heat_system.split(" ")[2::])
n.madd( n.madd(
"Generator", "Generator",
[node], [node],
suffix=" retrofitting " + strength + " " + node_name, suffix=" retrofitting " + strength + " " + node_name,
bus=name, bus=heat_system,
carrier="retrofitting", carrier="retrofitting",
p_nom_extendable=True, p_nom_extendable=True,
p_nom_max=dE_diff[strength] p_nom_max=dE_diff[strength]