Skip to content

feat: resistive_heaters and air_sourced_hp for ptes temperature boosting #1711

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

Open
wants to merge 15 commits into
base: master
Choose a base branch
from

Conversation

TomKae00
Copy link
Contributor

@TomKae00 TomKae00 commented Jun 5, 2025

Changes proposed in this Pull Request

This feature introduces resistive heaters and air-sourced heat pumps as temperature boosting technologies for PTES. The boosting methodology is based on a temperature_boost_ratio, which calculates the additional temperature lift required between the store’s current top temperature and the target forward temperature. This is done relative to the lift already achieved within the store, using the return temperature of the district heating system as a reference.


1. Boosting-ratio derivation

Let

  • Ṽ = volumetric flow rate (constant),
  • ρ = fluid density (constant),
  • cₚ = specific heat capacity (constant),
  • T_return = district heating return temperature,
  • T_max,store = temperature in the PTES,
  • T_forward = forward temperature.

We define two heat-transfer rates:

  1. Energy supplied by the PTES (source)
    Q_source = Ṽ·ρ·cₚ·(T_max,store − T_return)

  2. Additional energy required (boost)
    Q_boost = Ṽ·ρ·cₚ·(T_forward − T_max,store)

The total heating demand is
Q_total = Q_source + Q_boost

Define the boosting ratio α as the lift-ratio between what the pump must add versus what the PTES already delivers:
α = (T_forward − T_max,store) / (T_max,store − T_return) = Q_boost / Q_source

In other words,
Q_boost = α·Q_source

Proceeding from Q_boost = P_el·η, one finds
Q_source = (P_el·η) / α
Q_total = Q_source + Q_boost = (P_el·η)/α + P_el·η = P_el·η·(1/α + 1)

Thus:

  1. PTES output per unit electricity
    Q_source / P_el = η / α

  2. Total output per unit electricity
    Q_total / P_el = η·(1/α + 1)


2. COP calculation adjustment for heat pumps

Previously, we calculated the heat-pump COP using the district heating forward/return temperatures. To accurately model air-sourced ptes heat pumps, the COP is now based on the pump’s sink inlet and outlet temperatures. This change ensures consistency whenever an external heat source (e.g., ambient air) is used for boosting.


3. Passing heat-pump parameters correctly

Prior, heat-pump parameters (set in config) were not used and default value instead. Now, all provided parameters are properly passed through the config and used.


4. Improved handling in logarithmic_mean

The function logarithmic_mean(t_hot, t_cold) failed (division-by-zero) when t_hot = t_cold. It now returns t_hot in that case (L’Hôpital’s rule), instead of throwing an error. This specifically handles instances where the PTES temperature equals the forward temperature.


5. @amos-schledorn discussion about heat-source flags

Because the air-sourced heat pump is now used in two contexts—

  1. Standard heating: raising district-heating return temperature up to forward temperature, and
  2. Boosting: raising PTES outlet temperature up to forward temperature—
    both cases would have “heat source = Air”, but this would break the logic we have at the moment. Therefore I still used “heat source = ptes” to manually distinguish the two scenarios. It might be cleaner to introduce a separate identifier for “boosting heat source.” Do you have a quick suggestion on how best to differentiate these two cases in the codebase?

Testing

scenario:
  clusters:
    - 8

planning_horizons:
  - 2030

foresight: overnight
countries: ['DE', 'PL']

clustering:
  temporal:
    resolution_sector: 12h

Results

  1. System-cost comparison
  • With supplemental heating:
    System cost = 141.071 trillion
  • With supplemental heating disabled:
    System cost = 141.054 trillion
  1. supplemental heating

Ran the analysis for DE and PL, both of which require relatively high forward temperatures (over 90°C). Since the PTES maximum storage temperature is 90 °C, supplemental boosting is necessary whenever the forward temperature exceeds that limit.

  1. Preferred boosting technology

In both countries, resistive heaters were chosen over air-sourced heat pumps for boosting. The main reasons are:

  • The air heat pump’s COP at high outlet temperatures is relatively low.
  • Resistive heaters have much lower capital costs.
  • The full-load hours for each booster technology are limited, favoring resistive heaters.
  1. Interpretation of the plots
  • The blue line shows electricity consumption for boosting (either by resistive heater or heat pump).
  • The combined heat output is Q_total = Q_store + P_el·η
  • The boosting_ratio determines how much extra energy (blue area) must be added beyond what the PTES alone can supply to achieve the forward temperature

With supplemental heating and booster technologies for PL 01 (resistive heaters)

overview6plots_resitive_PL0_1

With supplemental heating and booster technologies for PL 01 (HP)

overview6plots_hp_PL0_1

With supplemental heating and booster technologies for DE 01 (resistive heaters)

overview6plots_resitive_DE0_1

With supplemental heating and booster technologies for DE 01 (HP)

overview6plots_hp_DE0_1

Checklist

  • I tested my contribution locally and it works as intended.
  • Code and workflow changes are sufficiently documented.
  • Changed dependencies are added to envs/environment.yaml.
  • Changes in configuration options are added in config/config.default.yaml.
  • Changes in configuration options are documented in doc/configtables/*.csv.
  • Sources of newly added data are documented in doc/data_sources.rst.
  • A release note doc/release_notes.rst is added.

Copy link
Contributor

@cpschau cpschau left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @TomKae00 ,
first of all: Kudos to the nice mathematical description and the overall documentation of the PR. I left a couple of comments, most of them questions to better understand what is happening or stylistic remarks. Some of them might be worth discussing together with @amos-schledorn.

"central water pit discharger",
"efficiency",
]
* ptes_supplemental_heating_required,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't we just save the variable ptes_supplemental_heating_required in this else-clause?

]
):
raise ValueError(
"'booster_heat_pump' is true, but 'enable' is false in 'supplemental_heating'."
"Supplemental heating: 'booster_technologies' contains 'heat_pump', but 'enable' is false."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this is a matter of taste: As it is, we incentivize the user to change both enable and boosting_technologies. Personally, I find it practical being able to switch a feature on/off by just setting the enable parameter in the config file, especially with a cascaded config setup. Compromise would be a warning instead of an error.

"booster_technologies"
]
):
raise ValueError(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above

@@ -153,7 +170,7 @@ def approximate_cop(self) -> Union[xr.DataArray, np.array]:
Union[xr.DataArray, np.array]: The calculated COP values.
"""
return xr.where(
self.t_source_in_kelvin >= self.t_sink_out_kelvin,
(self.delta_t_lift < self.min_delta_t_lift),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means, that for the default min_delta_t_lift of 10 K, PTES could not be discharged with heat pumps to the district heating system if the forward flow temperature is between 90 and 100 °C, right?

  1. Is this realistic?
  2. This would suppress the capacity factors of booster heat pumps further down and give another reason why there not favored for boosting applications given the higher CAPEX compared to resistive heaters.

Comment on lines +3322 to +3324
and "heat_pump"
in options["district_heating"]["ptes"]["supplemental_heating"][
"booster_technologies"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do the booster heat pumps stand in an if-else-relation with normal heat pumps (see else-clause below in L.3355 and following)? We want the latter ones to be available independent of the boosting feature.

n.add(
"Link",
nodes,
suffix=f" {heat_system} ptes resistive heater",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe change the "ptes" part to "pits" or even "water pits" to be more consistent, avoid abbreviations and allow a more convenient filtering of links.

Comment on lines +474 to +480
ptes resistive heater: '#a0e78d'
residential rural resistive heater: '#bef5b5'
residential urban decentral resistive heater: '#b2f1a9'
services rural resistive heater: '#a5ed9d'
services urban decentral resistive heater: '#98e991'
urban central resistive heater: '#8cdf85'
urban central ptes resistive heater: '#71c96b'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need both entries?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants