Update central heating temperatures based on Euroheat data and AGFW-Hauptbericht (#1264)

* chore: update config.default
Using Euroheat market outlook 2024 and AGFW-Hauptbericht

* feat: extrapolate missing values in central_heating_temperature_profile.run

* update release notes

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

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

* fix: map_temperature_dict_to_onshore regions and correct use of extrapolation function

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

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

* fix: extrapolation_ration in build_central_heating_temperature_profiles.run

* style: remove obsolete time index in min/max fwd, return temperatures

* feat: throw exception if max_fwd_temp < min_fwd_temp or min_fwd_temp < return_temp

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

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

---------

Co-authored-by: Amos Schledorn <a.schledorn@tu-berlin.de>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Amos Schledorn 2024-09-09 16:38:39 +02:00 committed by GitHub
parent 0423366db6
commit 5cf706fe4d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 90 additions and 39 deletions

View File

@ -451,21 +451,18 @@ sector:
district_heating_loss: 0.15
supply_temperature_approximation:
max_forward_temperature:
default: 90
DK: 70
SE: 70
NO: 70
FR: 110
DK: 75
DE: 109
CZ: 130
FI: 115
PL: 130
SE: 102
IT: 90
min_forward_temperature:
default: 68
DK: 54
SE: 54
NO: 54
DE: 82
return_temperature:
default: 50
DK: 40
SE: 40
NO: 40
FI: 40
DE: 58
lower_threshold_ambient_temperature: 0
upper_threshold_ambient_temperature: 10
rolling_window_ambient_temperature: 72

View File

@ -9,8 +9,8 @@ Release Notes
##########################################
.. Upcoming Release
.. ================
* Updated district heating supply temperatures based on `Euroheat's DHC Market Outlook 2024<https://api.euroheat.org/uploads/Market_Outlook_2024_beeecd62d4.pdf>`__ and `AGFW-Hauptbericht 2022 <https://www.agfw.de/securedl/sdl-eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE3MjU2MjI2MTUsImV4cCI6MTcyNTcxMjYxNSwidXNlciI6MCwiZ3JvdXBzIjpbMCwtMV0sImZpbGUiOiJmaWxlYWRtaW4vdXNlcl91cGxvYWQvWmFobGVuX3VuZF9TdGF0aXN0aWtlbi9IYXVwdGJlcmljaHRfMjAyMi9BR0ZXX0hhdXB0YmVyaWNodF8yMDIyLnBkZiIsInBhZ2UiOjQzNn0.Bhma3PKg9uJnC57Ixi2p9STW5-II9VXPTDXS544M208/AGFW_Hauptbericht_2022.pdf>`__. `min_forward_temperature` and `return_temperature` (not given by Euroheat) are extrapolated based on German values.
* Made the overdimensioning factor for heating systems specific for central/decentral heating, defaults to no overdimensionining for central heating and no changes to decentral heating compared to previous version.
* bugfix: The carrier of stores was silently overwritten by their bus_carrier as a side effect when building the co2 constraints

View File

@ -58,6 +58,15 @@ class CentralHeatingTemperatureApproximator:
rolling_window_ambient_temperature : int
Rolling window size for averaging ambient temperature.
"""
if any(max_forward_temperature < min_forward_temperature):
raise ValueError(
"max_forward_temperature must be greater than min_forward_temperature"
)
if any(min_forward_temperature < fixed_return_temperature):
raise ValueError(
"min_forward_temperature must be greater than fixed_return_temperature"
)
self._ambient_temperature = ambient_temperature
self.max_forward_temperature = max_forward_temperature
self.min_forward_temperature = min_forward_temperature

View File

@ -9,8 +9,8 @@ al. 2019, where for ambient temperatures below 0C, the highest possible forward
temperature is assumed and vice versa for temperatures above 10C. Between these
threshold levels, forward temperatures are linearly interpolated.
By default, temperature levels are increased for non-Scandinavian countries.
The default ratios between min. and max. forward temperatures is based on AGFW-Hauptbericht 2022.
By default, `max_forward_temperature` from Euroheat DHC Market Outlook 2024 is used; `min_forward_temperature` and `return_temperature` for Germany is used from AGFW-Hauptbericht 2022.
`min_forward_temperature` and `return_temperature` for other countries are extrapolated based on the ratio between `max_forward_temperature` and `min_forward_temperature` and `return_temperature` for those countries not missing (by default only Germany).
Relevant Settings
-----------------
@ -47,26 +47,68 @@ from central_heating_temperature_approximator import (
)
def extrapolate_missing_supply_temperatures_by_country(
extrapolate_from: dict, extrapolate_to: dict
) -> xr.DataArray:
"""
Extrapolates missing supply temperatures by country.
Parameters:
extrapolate_from (dict): A dictionary containing supply temperatures to extrapolate from. Should contain all countries.
extrapolate_to (dict): A dictionary containing supply temperatures to extrapolate to. Where `country` is present, average ratio between `extrapolate_to[country]` and `extrapolate_from[country]` is applied to all countries for which `country` is not present in `extrapolate_from.keys()` to infer ratio for extrapolation.
Returns:
xr.DataArray: A DataArray containing the extrapolated supply temperatures.
"""
if not all([key in extrapolate_from.keys() for key in extrapolate_to.keys()]):
raise ValueError(
"Not all countries in extrapolate_to are present in extrapolate_from."
)
# average ratio between extrapolate_from and extrapolate_to for those countries that are in both dictionaries
extrapolation_ratio = np.mean(
[extrapolate_to[key] / extrapolate_from[key] for key in extrapolate_to.keys()]
)
# apply extrapolation ratio to all keys missing in extrapolate_to
return {
key: (
extrapolate_to[key]
if key in extrapolate_to.keys()
else extrapolate_from[key] * extrapolation_ratio
)
for key in extrapolate_from.keys()
}
def get_country_from_node_name(node_name: str) -> str:
"""
Extracts the country code from a given node name.
Parameters:
node_name (str): The name of the node.
Returns:
str: The country code extracted from the node name.
"""
return node_name[:2]
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.
Missing values are replaced by the mean of all values.
Parameters:
----------
supply_temperature_by_country : dictionary
Dictionary with temperatures as values and country keys as keys. One key must be named "default"
Dictionary with temperatures as values and country keys as keys.
regions_onshore : pd.Index
Names of onshore regions
snapshots : pd.DatetimeIndex
Time stamps
Returns:
-------
@ -75,20 +117,16 @@ def map_temperature_dict_to_onshore_regions(
"""
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
(
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 np.mean(list(supply_temperature_by_country.values()))
)
for node_name in regions_onshore.values
],
dims=["time", "name"],
coords={"time": snapshots, "name": regions_onshore},
dims=["name"],
coords={"name": regions_onshore},
)
@ -104,28 +142,35 @@ if __name__ == "__main__":
set_scenario_config(snakemake)
max_forward_temperature = snakemake.params.max_forward_temperature_central_heating
min_forward_temperature = extrapolate_missing_supply_temperatures_by_country(
extrapolate_from=max_forward_temperature,
extrapolate_to=snakemake.params.min_forward_temperature_central_heating,
)
return_temperature = extrapolate_missing_supply_temperatures_by_country(
extrapolate_from=max_forward_temperature,
extrapolate_to=snakemake.params.return_temperature_central_heating,
)
# 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)
max_forward_temperature_central_heating_by_node_and_time: xr.DataArray = (
map_temperature_dict_to_onshore_regions(
supply_temperature_by_country=snakemake.params.max_forward_temperature_central_heating,
supply_temperature_by_country=max_forward_temperature,
regions_onshore=regions_onshore,
snapshots=snapshots,
)
)
min_forward_temperature_central_heating_by_node_and_time: xr.DataArray = (
map_temperature_dict_to_onshore_regions(
supply_temperature_by_country=snakemake.params.min_forward_temperature_central_heating,
supply_temperature_by_country=min_forward_temperature,
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,
supply_temperature_by_country=return_temperature,
regions_onshore=regions_onshore,
snapshots=snapshots,
)
)