replace build_cop_profiles and build_heat_source_utilisation_profiles…#2184
Draft
amos-schledorn wants to merge 1 commit into
Draft
replace build_cop_profiles and build_heat_source_utilisation_profiles…#2184amos-schledorn wants to merge 1 commit into
amos-schledorn wants to merge 1 commit into
Conversation
… with a single rule and compute heat_pump_coolling for COP approximation iteratively when enabled
cpschau
approved these changes
Jun 3, 2026
Contributor
cpschau
left a comment
There was a problem hiding this comment.
Looks great! New structure with all scripts consolidated in build_heat_source_profiles is way cleaner.
Comment on lines
+489
to
+496
| # Write the full per-node, per-timestep Picard trace only when logging is on. | ||
| if cooling_iteration_log: | ||
| iterations_path = ( | ||
| Path(snakemake.output.cop_profiles).parent | ||
| / f"heat_pump_cooling_iterations_base_s_{snakemake.wildcards.clusters}_{snakemake.wildcards.planning_horizons}.csv" | ||
| ) | ||
| pd.concat(cooling_iteration_log, ignore_index=True).to_csv( | ||
| iterations_path, index=False |
Contributor
There was a problem hiding this comment.
Would be the first log-type resource afaik. Maybe the normal log of the snakemake rule suffices.
Contributor
There was a problem hiding this comment.
Just realized that the additional log resource is a config option, so nvm.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Consistent heat-pump cooling for COP and heat-source utilisation profiles
For preheating heat sources (PTES and geothermal) the temperature drop the heat pump imposes on its source is not a free parameter — the heat pump's energy balance fixes it — yet that same drop feeds back into the COP correlation that determines the balance. This PR replaces the previous constant-cooling assumption with an iterative solve that finds the cooling and the COP consistently, and merges the former
build_cop_profilesandbuild_heat_source_utilisation_profilesrules into a singlebuild_heat_source_profilesrule that produces both.Background
District-heating COPs come from the Jensen et al. (2018) correlation in
CentralHeatingCopApproximator. Among its inputs are the source inlet and source outlet temperatures — that is, how far the heat pump cools the stream it draws heat from. The gap between them, call it ΔT_cool, was set by a single config number,heat_source_cooling(6 K), for every source and every hour.That is fine for an ambient source like air, where the evaporator ΔT is essentially a design choice. It is not fine for a source that is first used to preheat the return flow and then lifted to the network forward temperature. Under the plant layout we assume for those sources — a single storage stream, a preheater followed by the evaporator, equal mass flows on both sides — the heat pump's energy balance ties the source-side cooling directly to the lift and the COP:
because the heat pulled from the source is the delivered heat minus the electrical work, so
Q_source / Q_sink = (COP - 1) / COP. The catch is that the COP on the right-hand side is itself a function of ΔT_cool: the cooling sets the source outlet temperature that goes into the correlation. So ΔT_cool and the COP are bound by one physical relation, and you cannot pin ΔT_cool to a constant and read the COP off the correlation at that constant and expect the two to agree.The current implemementation does exactly that. The reported COP and the cooling implied by its own energy balance are therefore inconsistent — the cooling is over-specified, once by the config constant and once (implicitly) by the heat pump's energy balance.
The same ΔT_cool also appears in the preheater utilisation profile, in the denominator that splits source heat between direct preheating and the heat-pump cold side. Whatever value we settle on has to be used in both the COP and the utilisation profile, which is the second reason for merging the
build_cop_profilesandbuild_heat_source_utilisation_profilesrules.Code changes
build_heat_source_profilesproduces the three profiles the sector network already consumes:cop_profilesheat_source_direct_utilisation_profilesheat_source_preheater_utilisation_profilesFor each
(heat system, source, node, snapshot)it derives the source and sink inlet temperatures from the existing logic and then decides the cooling:compute_heat_pump_coolingstarts from the previousheat_source_cooling, evaluates the COP resulting from that initial value, recomputes the cooling from(COP - 1) / COP * (T_forward - T_source), and repeats until the cooling stops changing.heat_source_coolingvalue, exactly as before.The solved cooling is then reused for both the COP profile and the preheater-utilisation profile.
build_cop_profilesandbuild_heat_source_utilisation_profilesalways run on the same inputs (forward/return temperatures and the source temperature profiles), and with this change both depend on the same solved cooling. Keeping them apart would mean either computing heat pump cooling and COP profiles twice or shuttling them between rules.This feature merges them and keeps the existing output file names, so nothing downstream needs to change:
prepare_sector_network,solve_myopic/solve_perfectandplot_cop_profilesstill resolvecop_profilesand the two utilisation profiles by path. The only workflow change is thatheat_sourcesis now passed as the full{system: [sources]}dict (asbuild_cop_profileshad it) instead of just the urban-central list that the old utilisation rule used.New config settings
All live under
sector: district_heating:.heat_source_cooling(existing, default6): unchanged for non-preheating sources. It now also serves as the starting guess for the iterative solve.heat_pump_cooling_iterative(new, bool, defaulttrue): turns the iterative solve on. Set it tofalseto fall back to flat cooling for all sources. That path reproduces pre-this-feature results exactly.log_heat_pump_cooling_iterations(new, bool, defaultfalse): a debugging aid. When enabled, the full per-node, per-timestep, per-iteration trace (forward and source temperature, cooling, COP) is written toheat_pump_cooling_iterations_<...>.csvalongsidecop_profiles. Might be removed in final version.The two new settings are registered in the config-validation model (
_DistrictHeatingConfiginscripts/lib/validation/config/sector.py) and inconfig/config.default.yaml.File changes
New
scripts/build_heat_source_profiles/run.py- the merged rule andcompute_heat_pump_cooling.scripts/build_heat_source_profiles/{base,central_heating,decentral_heating}_cop_approximator.py- the COP approximators moved into the package so it is self-contained. They are identical to the old ones.Removed
scripts/build_cop_profiles/andscripts/build_heat_source_utilisation_profiles.py.Changed
rules/build_sector.smk- the two rules replaced bybuild_heat_source_profiles, which passes the fullheat_sourcesdict and the two new params.config/config.default.yamlandscripts/lib/validation/config/sector.py- the two new settings.doc/sector.rst- the autodoc entry renamed frombuild_cop_profilestobuild_heat_source_profiles.scripts/definitions/heat_source.py- theSee Alsodocstring now points at the new rule.scripts/build_central_heating_temperature_profiles/run.py- fixed a straymock_snakemake("build_cop_profiles")left over from a copy-paste; it now names its own rule (and would otherwise have referenced the deleted rule).Checklist
Required:
doc/release_notes.rst.If applicable:
scripts/lib/validation.doc/*.rstfiles.