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

Introduce PTES and TES constraints #1546

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

Conversation

TomKae00
Copy link
Contributor

Changes proposed in this Pull Request

This pull request introduces the following adjustments to the prepare_sector_network and solve_network scripts, replacing PR #1444 (which did not work with the HiGHS solver):

  • Added PTES.
  • Introduced a constraint to enforce the energy-to-power ratio (etpr).
  • Implemented a constraint to ensure that the charger and discharger of the thermal energy stores are sized equally.

The following config data has been used:

scenario:
  clusters:
    - 6

planning_horizons:
  - 2030

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

clustering:
  temporal:
    resolution_sector: 12h

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.

charger_discharger_profile_comparison

The annual charging and discharging process for an exemplary PTES store is illustrated in the figure below:

charger_discharger_size_comparison

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.

total_cost_comparison

Checklist

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

Copy link
Contributor

@amos-schledorn amos-schledorn left a 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.

def add_TES_etpr_constraints(n):
"""
Add a constraint for each TES storage unit enforcing:
Store-e_nom - etpr * Link-p_nom == 0
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's add a more extensive docstring and type hinting for the input here.

"""
Add constraints ensuring that for each TES unit, the charger and discharger are sized the same.
Link-p_nom(charger) - efficiency * Link-p_nom(discharger) == 0
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's add a more extensive docstring and type hinting for the input here.

)
tes_store_bool = n.stores.index.str.contains("water tanks|water pits")

chargers_ext = n.links[tes_charger_bool].query("p_nom_extendable").index
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we use more descriptive variable names? (It's okay if they get long)
e.g. index_chargers_p_nom_extendable or similar

linear_expr_list, dim="Store-ext, Link-ext", cls=type(linear_expr_list[0])
)

n.model.add_constraints(merged_expr == 0, name="TES_etpr")
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 simplify the two functions to something like this:

# Extract indices of extendable TES chargers and storage units
charger_indices = n.links.index[n.links.index.str.contains("water tanks charger|water pits charger") & n.links.p_nom_extendable]
store_indices = n.stores.index[n.stores.index.str.contains("water tanks|water pits") & n.stores.e_nom_extendable]

# Extract energy-to-power ratio (ETPR) values
e2p_ratio = n.links.loc[charger_indices, "etpr"]

# Add constraints directly to the PyPSA model using vectorized operations
n.model.add_constraints(
    n.model["Store-e_nom"].loc[store_indices] >= e2p_ratio.values * n.model["Link-p_nom"].loc[charger_indices],
    name="TES_etpr"
)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The challenge is that vectorizing these operations leads to unintended broadcasting. Since we have two different variables, p_nom and e_nom, vectorized multiplication would result in a 24x24 matrix if there are 24 chargers and 24 stores. This mismatch means the constraint wouldn’t correctly associate each TES store with its corresponding charger.

@TomKae00
Copy link
Contributor Author

@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.

@TomKae00
Copy link
Contributor Author

Hi @fneum and @amos-schledorn,

Currently, the CI is failing when running:

snakemake make_summary_perfect --configfile config/test/config.perfect.yaml

Reason for CI Failure:
In the master, the water tank charger and discharger are included in costs.csv with an efficiency of 0.9. Because of this, they can be used as heat vents, allowing energy to be released from the system.

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:
Since energy can no longer be released through the chargers/dischargers, the system costs are now higher. A potential solution could be to allow heat vents to be built for all heat systems, as it is done for urban central heating systems in prepare_sector_network.py line 2437.

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?

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