Refactor: update heat source handling, introduce PyPS-onic pre-heating for geothermal and PTES#1893
Refactor: update heat source handling, introduce PyPS-onic pre-heating for geothermal and PTES#1893amos-schledorn wants to merge 79 commits intomasterfrom
Conversation
…True" This reverts commit ba26588.
|
Thanks for the review, @cpschau. I have
|
cpschau
left a comment
There was a problem hiding this comment.
Logic for sink inlet T must be changed, and e_nom_min for PTES must be removed.
| nodes, | ||
| suffix=f" {heat_system} water pits discharger", | ||
| bus0=nodes + f" {heat_system} water pits", | ||
| bus1=HeatSource.PTES.resource_bus(nodes, heat_system), |
There was a problem hiding this comment.
I assume this is the "urban central ptes heat" bus, that is added just before in L 3166? Some comments would be helpful to understand what is going on. A compromise has to be found between keeping prepare_sector_network lean but also understandable.
There was a problem hiding this comment.
But isn't this way more readable than a string? You can just hit f12/hover to get a full description or highlight where else it's being used.
There was a problem hiding this comment.
Sure, but maybe a comment above would be helpful to understand that the bus components initialized are these exact resource buses.
There was a problem hiding this comment.
Gotcha. Didn't see that the bus creation uses an explicit string. It now does and I've also added examples to the HeatSource.resource_bus method.
| return_temp = return_temperature.sel(name=regions) | ||
|
|
||
| # Broadcast return_temperature to match forward_temperature dimensions | ||
| return_temp_broadcast = return_temp.broadcast_like(forward_temp) |
There was a problem hiding this comment.
Do forward and return T ever arrive with different temporal indices?
There was a problem hiding this comment.
return_temp should be constant and not time-indexed, thus the broadcast.
scripts/build_cop_profiles/run.py
Outdated
| temperature. When preheating is not used (source <= return), the heat pump | ||
| receives water at return temperature and heats it to forward temperature. | ||
|
|
||
| When source temperature > return temperature, preheater is used: preheater raises return flow, heat pump inlet is at forward temp. |
There was a problem hiding this comment.
This is wrong. The sink inlet temperature should never be at forward T level but either at source T or return T level. The sink outlet T should be equal to the forward level after liftinf the temeprature.
Instead it should be:
return xr.where(
source_temperature > central_heating_return_temperature,
source_temperature,
central_heating_return_temperature,
)
There was a problem hiding this comment.
Good catch! I think it's actually supposed to be
return xr.where(
source_temperature > central_heating_return_temperature,
central_heating_return_temperature,
source_temperature,
)
(which is what I implemented)
There was a problem hiding this comment.
You're right of course, brain-farted a little - fixed.
…ntation and implementation
|
Thanks for looking into that - I know this is a lot of code!
Review giude:
|

Supersedes #1765 .
This feature implements some refactoring of how Pit Thermal Energy Storage (PTES) and heat sources are handled.
PTES boosting is now possible without custom constraints. Instead, energy balancing is handled more PyPS-onicly (yes, that's a word now!) through a few extra busses and links.
Geothermal boosting is introduced similarly with a preheater to better approximate real-world behaviour.
This results in an overall cleaner/more generic integration of heat sources.
1. New
HeatSourceEnum Class (scripts/definitions/heat_source.py)A new enumeration class has been added to categorize heat sources:
Key properties for each source:
requires_bus: Whether a resource bus is needed (True for limited sources)requires_generator: Whether a generator component is needed (geothermal, river_water)has_constant_temperature: Whether temperature is time-invariant (geothermal)The class also provides methods for:
2.
build_ptes_operationsRuleNew configuration parameters:
Outputs:
temp_ptes_top_profiles: PTES top layer temperature profileptes_e_max_pu_profiles: Normalized storage capacity (accounts for temperature-dependent capacity)ptes_boost_per_discharge_profiles: Ratio of boost energy to discharge energy when T_forward > T_top. Used iffsector:district_heating:ptes:discharge_resistive_boosting: true3.
build_heat_source_utilisation_profilesRuleRenamed from
build_heat_source_direct_utilisation_profiles. Calculates when heat sources can be used and in what mode:Outputs:
heat_source_direct_utilisation_profiles: Binary (1.0 when T_source ≥ T_forward for direct supply) - this already existsheat_source_preheater_utilisation_profiles: Efficiency when T_return < T_source < T_forward (preheating mode) - this is new4. Changes in
prepare_sector_network:add_heat: new bus structurea) For exhaustible heat sources (currently geothermal, river-water and PTES with HP boosting but more to come in #1944)
For PTES, the generator is replaced with the
urban central water pits dischargerlink.b) For Inexhaustible Sources (air / ground / sea_water)
No buses created — these sources don't need resource tracking, thus
heat_source.requires_busreturnsFalseheat_source.get_heat_pump_input_bus()returns"", which is alsobus2for heat pumps.Links created:
c) PTES with Resistive Boosting
When
ptes:discharge_resistive_boosting: true, PTES uses resistive heaters instead of heat pumps for the temperature lift. The structure bypasses the cascading utilisation/preheater/heat-pump chain.Key calculation (in
build_ptes_operations):water pits dischargerlink still points to the PTES resource busurban central ptes heatheat_source.requires_heat_pump()returnsFalse.With resistive boosting, the ratio α determines how much of the output comes from PTES discharge vs electric heating:
5. Further onfiguration Changes
heat_pump_sources→heat_sourcestes→ttes(tank thermal energy storage)limited_heat_sources,direct_utilisation_heat_sources,temperature_limited_storesOpen issues
Checklist
envs/environment.yaml.config/config.default.yaml.doc/configtables/*.csv.doc/data_sources.rst.doc/release_notes.rstis added.Testing
Config
Results
Parameters
(look reasonable)

Example energy balance (Jan 3rd,
ptes_90-10_dynamic_capacity_hpBoostingDE0 1 urban central ptes heatbusPTES discharge: 369 MWh
DE0 1 urban central ptes medium-temperature heatbusDischarge is fully absorbed by pre-heater, since no direct utilisation allowed
DE0 1 urban central ptes return-temperature heatbus231 MWh is used as heat pump input
DE0 1 urban central heatbusRemaining 138 MWh are used by preheater to heat return flow
Heat pump output is 282 MWh > 231 MWh due to electricity input
Entire year,
ptes_90-10_dynamic_capacity_hpBoosting,DE0 1Discharging over the entire year follows parameters shown above, with direct utilisation mostly in the summer.
There are some periods where both direct utilisation and pre-heating are happening due to the
25hresolution.Entire year,
ptes_90-10_dynamic_capacity_hpBoosting,DK1 0Direct utilisation is possible throughout the entire year due to lower forward temperatures.
Entire year,
ptes_90-10_dynamic_capacity_rhBoosting_geothermal,DE0 0Here, the pre-heating ratio for geothermal seems a little high but matches the logic, as
(T_source - T_return) / (T_source - T_return + 6K) = 0.45.EDIT
I've also updated
build_geothermal_heat_source_potentialsby introducing scaling of Manz et al.'s data.Manz et al. assume a difference of 15K. This rule now scales the potentials based our actual temperature delta:
a) If source_temperature > forward_temperature (direct utilisation):
scale_factor = (source_temperature - return_temperature) / 15 K
b) If forward_temperature >= source_temperature > return_temperature (preheating):
scale_factor = (source_temperature - return_temperature + heat_source_cooling) / 15 K
c) If source_temperature <= return_temperature (heat pump only):
scale_factor = heat_source_cooling / 15 K
EDIT 2
The example results are using a buggy scaling of
p_max_pufor geothermal heating. I've fixed that bug resulting in the model not building goethermal heating anymore. I've checked that it can (by lowering CAPEX) and pre-heating ratios are still valid.(just a note that when executing these configs, geothermal has to be forced in to get comparable results, so I'm leaving the "faulty" results here)
EDIT 3
Following a suggestion of @fneum's, I've compared a scenario without PTES & geothermal heating, i.e. those technologies that should be affected by the feature, to the master:
On the master, I have removed the section in
prepare_sector_networkthat builds PTES.-> The objective value is constant across both feature and master to the 5th digit:
feature=1.16952e+11 master=1.16952e+11 (Δ=57755.6, +0.000%)I've also compared runs with default settings in the feature and master (still DE/DK-25h) with similar results (different colours though).
District heating balance in master, default settings:

and feature, default settings:

Energy balance in master:

and feature:
