Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 12 commits into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 10 additions & 13 deletions config/config.default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion doc/release_notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,15 @@ def __init__(
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
Expand Down
95 changes: 70 additions & 25 deletions scripts/build_central_heating_temperature_profiles/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
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
-----------------
Expand Down Expand Up @@ -47,26 +47,68 @@
)


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:
-------
Expand All @@ -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},
)


Expand All @@ -104,28 +142,35 @@ def map_temperature_dict_to_onshore_regions(

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,
)
)

Expand Down
Loading