Merge pull request #1180 from PyPSA/country-specific-dh-forward-temperatures
Add option of country-specific district heating supply temperatures
This commit is contained in:
commit
31c8907370
@ -448,8 +448,19 @@ sector:
|
||||
2045: 0.8
|
||||
2050: 1.0
|
||||
district_heating_loss: 0.15
|
||||
forward_temperature: 90 #C
|
||||
return_temperature: 50 #C
|
||||
# check these numbers!
|
||||
forward_temperature:
|
||||
default: 90
|
||||
DK: 70
|
||||
SE: 70
|
||||
NO: 70
|
||||
FI: 70
|
||||
return_temperature:
|
||||
default: 50
|
||||
DK: 40
|
||||
SE: 40
|
||||
NO: 40
|
||||
FI: 40
|
||||
heat_source_cooling: 6 #K
|
||||
heat_pump_cop_approximation:
|
||||
refrigerant: ammonia
|
||||
|
@ -9,8 +9,8 @@ district_heating,--,,`prepare_sector_network.py <https://github.com/PyPSA/pypsa-
|
||||
-- potential,--,float,maximum fraction of urban demand which can be supplied by district heating
|
||||
-- progress,--,Dictionary with planning horizons as keys., 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,Share increase in district heat demand in urban central due to heat losses
|
||||
-- forward_temperature,°C,float,Forward temperature in district heating
|
||||
-- return_temperature,°C,float,Return temperature in district heating. Must be lower than forward temperature
|
||||
-- forward_temperature,°C,Dictionary with country codes as keys. One key must be 'default'.,Forward temperature in district heating
|
||||
-- return_temperature,°C,Dictionary with country codes as keys. One key must be 'default'.,Return temperature in district heating. Must be lower than forward temperature
|
||||
-- heat_source_cooling,K,float,Cooling of heat source for heat pumps
|
||||
-- heat_pump_cop_approximation,,,
|
||||
-- -- refrigerant,--,"{ammonia, isobutane}",Heat pump refrigerant assumed for COP approximation
|
||||
|
|
@ -20,6 +20,8 @@ Upcoming Release
|
||||
|
||||
* Update JRC-IDEES-2015 to `JRC-IDEES-2021 <https://publications.jrc.ec.europa.eu/repository/handle/JRC137809>`__. The reference year is changed from 2015 to 2019.
|
||||
|
||||
* Added option to use country-specific district heating forward and return temperatures. Defaults to lower temperatures in Scandinavia.
|
||||
|
||||
* Added unsustainable biomass potentials for solid, gaseous, and liquid biomass. The potentials can be phased-out and/or
|
||||
substituted by the phase-in of sustainable biomass types using the config parameters
|
||||
``biomass: share_unsustainable_use_retained`` and ``biomass: share_sustainable_potential_available``.
|
||||
|
@ -233,9 +233,11 @@ rule build_cop_profiles:
|
||||
"sector", "district_heating", "heat_pump_cop_approximation"
|
||||
),
|
||||
heat_pump_sources=config_provider("sector", "heat_pump_sources"),
|
||||
snapshots=config_provider("snapshots"),
|
||||
input:
|
||||
temp_soil_total=resources("temp_soil_total_elec_s{simpl}_{clusters}.nc"),
|
||||
temp_air_total=resources("temp_air_total_elec_s{simpl}_{clusters}.nc"),
|
||||
regions_onshore=resources("regions_onshore_elec_s{simpl}_{clusters}.geojson"),
|
||||
output:
|
||||
cop_profiles=resources("cop_profiles_elec_s{simpl}_{clusters}.nc"),
|
||||
resources:
|
||||
|
@ -22,12 +22,11 @@ from _helpers import (
|
||||
update_config_from_wildcards,
|
||||
)
|
||||
from add_electricity import sanitize_carriers
|
||||
from definitions.heat_sector import HeatSector
|
||||
from definitions.heat_system import HeatSystem
|
||||
from definitions.heat_system_type import HeatSystemType
|
||||
from prepare_sector_network import cluster_heat_buses, define_spatial, prepare_costs
|
||||
|
||||
from scripts.definitions.heat_sector import HeatSector
|
||||
from scripts.definitions.heat_system import HeatSystem
|
||||
from scripts.definitions.heat_system_type import HeatSystemType
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
cc = coco.CountryConverter()
|
||||
idx = pd.IndexSlice
|
||||
|
@ -2,9 +2,47 @@
|
||||
# SPDX-FileCopyrightText: : 2020-2024 The PyPSA-Eur Authors
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
"""
|
||||
Approximate heat pump coefficient-of-performance (COP) profiles for different
|
||||
heat sources and systems.
|
||||
|
||||
For central heating, this is based on Jensen et al. (2018) (c.f. `CentralHeatingCopApproximator <CentralHeatingCopApproximator.py>`_) and for decentral heating, the approximation is based on Staffell et al. (2012) (c.f. `DecentralHeatingCopApproximator <DecentralHeatingCopApproximator.py>`_).
|
||||
|
||||
Relevant Settings
|
||||
-----------------
|
||||
|
||||
.. code:: yaml
|
||||
sector:
|
||||
heat_pump_sink_T_decentral_heating:
|
||||
district_heating:
|
||||
forward_temperature:
|
||||
return_temperature:
|
||||
heat_source_cooling:
|
||||
heat_pump_cop_approximation:
|
||||
refrigerant:
|
||||
heat_exchanger_pinch_point_temperature_difference
|
||||
isentropic_compressor_efficiency:
|
||||
heat_loss:
|
||||
heat_pump_sources:
|
||||
urban central:
|
||||
urban decentral:
|
||||
rural:
|
||||
snapshots:
|
||||
|
||||
Inputs
|
||||
------
|
||||
- `resources/<run_name>/regions_onshore.geojson`: Onshore regions
|
||||
- `resources/<run_name>/temp_soil_total`: Ground temperature
|
||||
- `resources/<run_name>/temp_air_total`: Air temperature
|
||||
|
||||
Outputs
|
||||
-------
|
||||
- `resources/<run_name>/cop_profiles.nc`: Heat pump coefficient-of-performance (COP) profiles
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
import geopandas as gpd
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import xarray as xr
|
||||
@ -14,13 +52,54 @@ from DecentralHeatingCopApproximator import DecentralHeatingCopApproximator
|
||||
|
||||
from scripts.definitions.heat_system_type import HeatSystemType
|
||||
|
||||
sys.path.append("..")
|
||||
|
||||
def map_temperature_dict_to_onshore_regions(
|
||||
supply_temperature_by_country: dict,
|
||||
regions_onshore: pd.Index,
|
||||
snapshots: pd.DatetimeIndex,
|
||||
) -> xr.DataArray:
|
||||
"""
|
||||
Map dictionary of temperatures to onshore regions.
|
||||
|
||||
Parameters:
|
||||
----------
|
||||
supply_temperature_by_country : dictionary
|
||||
Dictionary with temperatures as values and country keys as keys. One key must be named "default"
|
||||
regions_onshore : pd.Index
|
||||
Names of onshore regions
|
||||
snapshots : pd.DatetimeIndex
|
||||
Time stamps
|
||||
|
||||
Returns:
|
||||
-------
|
||||
xr.DataArray
|
||||
The dictionary values mapped to onshore regions with onshore regions as coordinates.
|
||||
"""
|
||||
return xr.DataArray(
|
||||
[
|
||||
[
|
||||
(
|
||||
supply_temperature_by_country[get_country_from_node_name(node_name)]
|
||||
if get_country_from_node_name(node_name)
|
||||
in supply_temperature_by_country.keys()
|
||||
else supply_temperature_by_country["default"]
|
||||
)
|
||||
for node_name in regions_onshore.values
|
||||
]
|
||||
# pass both nodes and snapshots as dimensions to preserve correct data structure
|
||||
for _ in snapshots
|
||||
],
|
||||
dims=["time", "name"],
|
||||
coords={"time": snapshots, "name": regions_onshore},
|
||||
)
|
||||
|
||||
|
||||
def get_cop(
|
||||
heat_system_type: str,
|
||||
heat_source: str,
|
||||
source_inlet_temperature_celsius: xr.DataArray,
|
||||
forward_temperature_by_node_and_time: xr.DataArray = None,
|
||||
return_temperature_by_node_and_time: xr.DataArray = None,
|
||||
) -> xr.DataArray:
|
||||
"""
|
||||
Calculate the coefficient of performance (COP) for a heating system.
|
||||
@ -41,8 +120,8 @@ def get_cop(
|
||||
"""
|
||||
if HeatSystemType(heat_system_type).is_central:
|
||||
return CentralHeatingCopApproximator(
|
||||
forward_temperature_celsius=snakemake.params.forward_temperature_central_heating,
|
||||
return_temperature_celsius=snakemake.params.return_temperature_central_heating,
|
||||
forward_temperature_celsius=forward_temperature_by_node_and_time,
|
||||
return_temperature_celsius=return_temperature_by_node_and_time,
|
||||
source_inlet_temperature_celsius=source_inlet_temperature_celsius,
|
||||
source_outlet_temperature_celsius=source_inlet_temperature_celsius
|
||||
- snakemake.params.heat_source_cooling_central_heating,
|
||||
@ -56,6 +135,10 @@ def get_cop(
|
||||
).approximate_cop()
|
||||
|
||||
|
||||
def get_country_from_node_name(node_name: str) -> str:
|
||||
return node_name[:2]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if "snakemake" not in globals():
|
||||
from _helpers import mock_snakemake
|
||||
@ -68,6 +151,23 @@ if __name__ == "__main__":
|
||||
|
||||
set_scenario_config(snakemake)
|
||||
|
||||
# map forward and return temperatures specified on country-level to onshore regions
|
||||
regions_onshore = gpd.read_file(snakemake.input.regions_onshore)["name"]
|
||||
snapshots = pd.date_range(freq="h", **snakemake.params.snapshots)
|
||||
forward_temperature_central_heating_by_node_and_time: xr.DataArray = (
|
||||
map_temperature_dict_to_onshore_regions(
|
||||
supply_temperature_by_country=snakemake.params.forward_temperature_central_heating,
|
||||
regions_onshore=regions_onshore,
|
||||
snapshots=snapshots,
|
||||
)
|
||||
)
|
||||
return_temperature_central_heating_by_node_and_time: xr.DataArray = (
|
||||
map_temperature_dict_to_onshore_regions(
|
||||
supply_temperature_by_country=snakemake.params.return_temperature_central_heating,
|
||||
regions_onshore=regions_onshore,
|
||||
snapshots=snapshots,
|
||||
)
|
||||
)
|
||||
cop_all_system_types = []
|
||||
for heat_system_type, heat_sources in snakemake.params.heat_pump_sources.items():
|
||||
cop_this_system_type = []
|
||||
@ -79,6 +179,8 @@ if __name__ == "__main__":
|
||||
heat_system_type=heat_system_type,
|
||||
heat_source=heat_source,
|
||||
source_inlet_temperature_celsius=source_inlet_temperature_celsius,
|
||||
forward_temperature_by_node_and_time=forward_temperature_central_heating_by_node_and_time,
|
||||
return_temperature_by_node_and_time=return_temperature_central_heating_by_node_and_time,
|
||||
)
|
||||
cop_this_system_type.append(cop_da)
|
||||
cop_all_system_types.append(
|
||||
|
@ -30,6 +30,9 @@ from build_energy_totals import (
|
||||
build_eurostat_co2,
|
||||
)
|
||||
from build_transport_demand import transport_degree_factor
|
||||
from definitions.heat_sector import HeatSector
|
||||
from definitions.heat_system import HeatSystem
|
||||
from definitions.heat_system_type import HeatSystemType
|
||||
from networkx.algorithms import complement
|
||||
from networkx.algorithms.connectivity.edge_augmentation import k_edge_augmentation
|
||||
from prepare_network import maybe_adjust_costs_and_potentials
|
||||
@ -37,10 +40,6 @@ from pypsa.geo import haversine_pts
|
||||
from pypsa.io import import_components_from_dataframe
|
||||
from scipy.stats import beta
|
||||
|
||||
from scripts.definitions.heat_sector import HeatSector
|
||||
from scripts.definitions.heat_system import HeatSystem
|
||||
from scripts.definitions.heat_system_type import HeatSystemType
|
||||
|
||||
spatial = SimpleNamespace()
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -2831,8 +2830,11 @@ def add_biomass(n, costs):
|
||||
)
|
||||
|
||||
if options["bioH2"]:
|
||||
name = (pd.Index(spatial.biomass.nodes) + " "
|
||||
+ pd.Index(spatial.h2.nodes.str.replace(" H2", "")))
|
||||
name = (
|
||||
pd.Index(spatial.biomass.nodes)
|
||||
+ " "
|
||||
+ pd.Index(spatial.h2.nodes.str.replace(" H2", ""))
|
||||
)
|
||||
n.madd(
|
||||
"Link",
|
||||
name,
|
||||
@ -2842,16 +2844,22 @@ def add_biomass(n, costs):
|
||||
bus2=spatial.co2.nodes,
|
||||
bus3="co2 atmosphere",
|
||||
carrier="solid biomass to hydrogen",
|
||||
efficiency=costs.at['solid biomass to hydrogen', 'efficiency'],
|
||||
efficiency2=costs.at['solid biomass', 'CO2 intensity'] * options["cc_fraction"],
|
||||
efficiency3=-costs.at['solid biomass', 'CO2 intensity'] * options["cc_fraction"],
|
||||
efficiency=costs.at["solid biomass to hydrogen", "efficiency"],
|
||||
efficiency2=costs.at["solid biomass", "CO2 intensity"]
|
||||
* options["cc_fraction"],
|
||||
efficiency3=-costs.at["solid biomass", "CO2 intensity"]
|
||||
* options["cc_fraction"],
|
||||
p_nom_extendable=True,
|
||||
capital_cost=costs.at['solid biomass to hydrogen', 'fixed'] * costs.at['solid biomass to hydrogen', 'efficiency']
|
||||
+ costs.at['biomass CHP capture', 'fixed'] * costs.at['solid biomass', 'CO2 intensity'],
|
||||
overnight_cost=costs.at['solid biomass to hydrogen', 'investment'] * costs.at['solid biomass to hydrogen', 'efficiency']
|
||||
+ costs.at['biomass CHP capture', 'investment'] * costs.at['solid biomass', 'CO2 intensity'],
|
||||
marginal_cost=0.,
|
||||
)
|
||||
capital_cost=costs.at["solid biomass to hydrogen", "fixed"]
|
||||
* costs.at["solid biomass to hydrogen", "efficiency"]
|
||||
+ costs.at["biomass CHP capture", "fixed"]
|
||||
* costs.at["solid biomass", "CO2 intensity"],
|
||||
overnight_cost=costs.at["solid biomass to hydrogen", "investment"]
|
||||
* costs.at["solid biomass to hydrogen", "efficiency"]
|
||||
+ costs.at["biomass CHP capture", "investment"]
|
||||
* costs.at["solid biomass", "CO2 intensity"],
|
||||
marginal_cost=0.0,
|
||||
)
|
||||
|
||||
|
||||
def add_industry(n, costs):
|
||||
|
Loading…
Reference in New Issue
Block a user