-
Notifications
You must be signed in to change notification settings - Fork 267
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
Introduce PTES and TES constraints #1546
base: master
Are you sure you want to change the base?
Conversation
for more information, see https://pre-commit.ci
…nto ltes_stores # Bitte geben Sie eine Commit-Beschreibung ein, um zu erklären, warum dieser # Merge erforderlich ist, insbesondere wenn es einen aktualisierten # Upstream-Branch mit einem Thema-Branch zusammenführt. # # Zeilen, die mit '#' beginnen, werden ignoriert, # und eine leere Beschreibung bricht den Commit ab.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great! Just a few small comments. When the CI runs through, I'll test this locally.
for more information, see https://pre-commit.ci
…nto ltes_stores
@amos-schledorn thanks for the review! I've implemented your suggestions. Moreover, i added error handling to ensure that the TES constraints are only applied when intended. |
Hi @fneum and @amos-schledorn, Currently, the CI is failing when running:
Reason for CI Failure: With this PR, the efficiency of both the central/decentral chargers and dischargers has been set to 1.0 according to the DEA catalogue. As a result, they can no longer act as heat vents, which prevents energy from being released. Additionally, chargers are now directly tied to the store size via the energy-to-power ratio, meaning they cannot be used independently. Possible Solution: Is there a specific reason why heat vents are currently only allowed for urban central heating? Or are there any concerns against extending this option to all systems? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR Overview
This PR implements new PTES and TES constraints to ensure a fixed energy-to-power ratio and equal sizing for TES charger/discharger pairs in the network, replacing the previous implementation incompatible with the HiGHS solver. Key changes include:
- Updating the prepare_sector_network.py to dynamically reference cost parameters for water tank and water pit components.
- Adding two new TES constraint functions (energy-to-power ratio and charger ratio) in solve_network.py.
- Updating the configuration file to include new color mappings for water pits in visualizations.
Reviewed Changes
File | Description |
---|---|
scripts/prepare_sector_network.py | Updates to water tanks/pits component cost/efficiency and related linking. |
scripts/solve_network.py | New TES constraint functions added and integrated into network setup. |
config/config.default.yaml | New plotting color options added for water pits. |
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
scripts/solve_network.py:886
- The TES charger ratio constraint assumes a one-to-one ordering between charger and discharger indices. Ensure that these indices are explicitly paired to enforce that each TES unit’s charger and discharger are correctly matched.
lhs = ( n.model["Link-p_nom"].loc[indices_charger_p_nom_extendable] - n.model["Link-p_nom"].loc[indices_discharger_p_nom_extendable] * eff_discharger )
for more information, see https://pre-commit.ci
…price-water-tank-chargers
…er ratio constraints and energy power constraints
…nto ltes_stores
…ilt with correct baseyear
for more information, see https://pre-commit.ci
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR Overview
This pull request introduces PTES and TES constraints to the network optimization scripts by enforcing an energy‐to‐power ratio for thermal energy stores and equal sizing between the charger and discharger linked to TES units. Key changes include:
- Adding new constraints and variables in prepare_sector_network.py to support water tanks and water pits.
- Implementing TES energy-to-power ratio and TES charger ratio constraints in solve_network.py with linopy for expression merging.
- Updating configuration settings in config.default.yaml to include cost and plotting parameters for water pits.
Reviewed Changes
File | Description |
---|---|
scripts/prepare_sector_network.py | Adds PTES-related constraints and extends the modeling of water tanks and water pits. |
scripts/solve_network.py | Introduces TES energy-to-power ratio and charger ratio constraints and a new linopy import. |
config/config.default.yaml | Updates cost and plotting settings to support new water pits parameters and associated components. |
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
scripts/prepare_sector_network.py:2732
- [nitpick] There is an inconsistency in how storage keys are referenced between water tanks and water pits. Consider constructing the key for water pit storage using heat_system.central_or_decentral to maintain consistency, if applicable.
lifetime=costs.at["central water pit storage", "lifetime"],
if not tes.replace("-", " ").split(" ")[:5] == charger.split(" ")[:5]: | ||
# e.g. "DE0 0 urban central water tanks charger" -> ["DE0", "0", "urban", "central", "water"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The string-based matching of charger and TES names using split and slice may be brittle. Consider refactoring this logic to use a more robust pairing mechanism (e.g. a helper function that parses and compares structured components) and explicitly sort the indices to ensure correct pairing.
if not tes.replace("-", " ").split(" ")[:5] == charger.split(" ")[:5]: | |
# e.g. "DE0 0 urban central water tanks charger" -> ["DE0", "0", "urban", "central", "water"] | |
if parse_name_components(tes) != parse_name_components(charger): | |
# e.g. "DE0 0 urban central water tanks charger" -> ("DE0", "0", "urban", "central", "water") |
Copilot is powered by AI, so mistakes are possible. Review output carefully before use.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Valid comment, but possible issues are addressed by throwing an error when respective string matching fails.
for charger, discharger in zip( | ||
indices_charger_p_nom_extendable, indices_discharger_p_nom_extendable | ||
): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The reliance on splitting strings to match charger and discharger links may lead to unexpected mismatches. It is advisable to standardize and explicitly sort both indices or use a dedicated matching function to ensure consistent pairing.
for charger, discharger in zip( | |
indices_charger_p_nom_extendable, indices_discharger_p_nom_extendable | |
): | |
def standardize_and_sort_indices(indices): | |
return sorted(indices, key=lambda x: " ".join(x.split(" ")[:5])) | |
sorted_charger_indices = standardize_and_sort_indices(indices_charger_p_nom_extendable) | |
sorted_discharger_indices = standardize_and_sort_indices(indices_discharger_p_nom_extendable) | |
for charger, discharger in zip(sorted_charger_indices, sorted_discharger_indices): |
Copilot is powered by AI, so mistakes are possible. Review output carefully before use.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Valid comment, but possible issues are addressed by throwing an error when respective string matching fails.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
) | ||
|
||
n.links.loc[ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was not aware of the practice to store custom attributes in network files. Is this OK @fneum? Alternatively, I would suggest to read out energy-to-power ratio from costs file in solve_network
.
lifetime=costs.at["central water pit storage", "lifetime"], | ||
) | ||
|
||
n.links.loc[ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here.
"No valid extendable charger links or stores found for TES energy to power constraints." | ||
) | ||
|
||
energy_to_power_ratio_values = n.links.loc[ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here the parameter would have to be accessed using the costs file as an additional data input to the rule.
) | ||
|
||
eff_discharger = n.links.efficiency[indices_discharger_p_nom_extendable].values | ||
lhs = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why only the discharger efficiency is considered here?
n.links.index.str.lower() | ||
.str.contains("pits charger|tanks charger", case=False, na=False) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why lower()
and contains(case=False)
have to be used?
n.buses.index.str.lower() | ||
.str.contains( | ||
r"urban central heat|urban decentral heat|rural heat", | ||
case=False, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why lower()
and contains(case=False)
have to be used?
Changes proposed in this Pull Request
This pull request introduces the following adjustments to the
prepare_sector_network
andsolve_network
scripts, replacing PR #1444 (which did not work with the HiGHS solver):The following config data has been used:
Constraints
The etpr ratio constraint and the constraint enforcing equal sizing of the charger and discharger are working as intended, as shown in the figure below. In particular, the size of the store divided by 150 equals the size of both the charger and the discharger.
The annual charging and discharging process for an exemplary PTES store is illustrated in the figure below:
System Costs
System costs have increased slightly with the newly introduced constraints. This is logical given the larger capacities required when the charger and discharger can no longer be chosen independently.
Checklist
envs/environment.yaml
.config/config.default.yaml
.doc/configtables/*.csv
.doc/data_sources.rst
.doc/release_notes.rst
is added.