Skip to content

Conversation

@virio-andreyana
Copy link
Collaborator

@virio-andreyana virio-andreyana commented Jan 9, 2026

Closes #7
Previously attempted in OET #58 (Better to upstream to OS)

Changes proposed in this Pull Request

This is a feature that were first implemented in the form-energy-storage storage that has many applications in future projects (procurement-metastudy, google-go)

It aims to make any storage technologies implementable both in the forms of stores and links, or as storage units.

  • With it, novel storage technologies such as li-ion, vanadium, lfp, lair, pair, iron-air can be included.

  • The original design made many changes such as the options for multiple max_hours for one technology. I think that is too detailed and have been omitted.

  • The original design rename the term batteries to li-ion for more clarity, but this would lead to backward incompatibility, so we just let both terms be valid.

  • It works for both in add_electricity and prepare_sector_network.

  • Technology mapping, nice names, color is to be added.

  • I tested my contribution locally and it works as intended.

  • Code and workflow changes are sufficiently documented.

  • Changed dependencies are added to pixi.toml (using pixi add <dependency-name>).

  • Changes in configuration options are added in config/config.default.yaml.

  • Changes in configuration options are documented in doc/configtables/*.csv.

  • For new data sources or versions, these instructions have been followed.

  • A release note doc/release_notes.rst is added.

virio-andreyana and others added 2 commits January 9, 2026 09:22
Co-authored-by: Gianvito Colucci <gianvi.colucci@gmail.com>
Co-authored-by: Gianvito Colucci <gianvi.colucci@gmail.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request adds support for multiple new battery storage technologies (LFP, vanadium, liquid-air, compressed-air, and iron-air) to PyPSA-EUR, extending beyond the original battery and H2 storage options. The implementation allows these technologies to be configured as either StorageUnits or as Store-Link combinations.

Key changes:

  • Introduces a STORE_LOOKUP dictionary mapping storage technology names to their component specifications (store, bicharger, charger, discharger)
  • Refactors storage cost calculations to support multiple technologies with varying configurations
  • Updates both electricity-only and sector-coupled network preparation to support new storage types

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
scripts/add_electricity.py Adds STORE_LOOKUP dictionary and refactors attach_storageunits/attach_stores functions to support multiple storage technologies with generic implementation
scripts/process_cost_data.py Updates cost calculation logic to iterate over STORE_LOOKUP entries and handle different charger/discharger configurations
scripts/prepare_sector_network.py Renames add_storage_and_grids to add_h2_gas_infrastructure, removes battery-specific code, and integrates generic storage attachment functions
rules/build_sector.smk Adds electricity parameter provider to make configuration available in sector network preparation
config/config.default.yaml Adds max_hours configuration for new storage technologies and updates extendable_carriers comments
doc/configtables/electricity.csv Updates StorageUnit and Store documentation to list new supported technologies
config/plotting.default.yaml Adds nice names and color mappings for new storage technologies
doc/release_notes.rst Documents the new feature

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 1081 to 1104
n.add(
"Link",
b_buses_i + " charger",
bus_names,
suffix=f" {charge_name}",
bus0=buses_i,
bus1=b_buses_i,
carrier="battery charger",
# the efficiencies are "round trip efficiencies"
efficiency=costs.at["battery inverter", "efficiency"] ** 0.5,
capital_cost=costs.at["battery inverter", "capital_cost"],
bus1=bus_names,
carrier=f"{carrier} {charge_name}",
efficiency=costs.at[lookup_charge, "efficiency"] ** roundtrip_correction,
capital_cost=costs.at[lookup_charge, "capital_cost"],
p_nom_extendable=True,
marginal_cost=costs.at["battery inverter", "marginal_cost"],
lifetime=costs.at[lookup_charge, "lifetime"],
)

n.add(
"Link",
b_buses_i + " discharger",
bus0=b_buses_i,
bus_names,
suffix=f" {discharge_name}",
bus0=bus_names,
bus1=buses_i,
carrier="battery discharger",
efficiency=costs.at["battery inverter", "efficiency"] ** 0.5,
carrier=f"{carrier} {discharge_name}",
efficiency=costs.at[lookup_discharge, "efficiency"] ** roundtrip_correction,
p_nom_extendable=True,
marginal_cost=costs.at["battery inverter", "marginal_cost"],
lifetime=costs.at[lookup_discharge, "lifetime"],
)
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

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

The carrier names for the charger and discharger links (e.g., "battery charger", "battery discharger") are not being registered with n.add("Carrier", ...). While line 1042 registers the main carrier (e.g., "battery"), the derived carrier names used in lines 1087 and 1100 should also be registered. This could cause issues with carrier-based filtering or plotting. Consider adding these carriers to the network before creating the links.

Copilot uses AI. Check for mistakes.
Comment on lines +20 to +21
-- StorageUnit,--,"Any subset of {'battery','H2','li-ion','vanadium','lfp','lair','pair','iron-air'}",Adds extendable storage units (battery and/or hydrogen) at every node/bus after clustering without capacity limits and with zero initial capacity.
-- Store,--,"Any subset of {'battery','H2','li-ion','vanadium','lfp','lair','pair','iron-air'}",Adds extendable storage units (battery and/or hydrogen) at every node/bus after clustering without capacity limits and with zero initial capacity.
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

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

The description still refers only to "battery and/or hydrogen" but should be updated to reflect all the new storage technologies that are now supported (li-ion, vanadium, lfp, lair, pair, iron-air). Consider updating to: "Adds extendable storage units at every node/bus after clustering without capacity limits and with zero initial capacity. Supported technologies include battery, H2, li-ion, vanadium, lfp, lair, pair, and iron-air."

Suggested change
-- StorageUnit,--,"Any subset of {'battery','H2','li-ion','vanadium','lfp','lair','pair','iron-air'}",Adds extendable storage units (battery and/or hydrogen) at every node/bus after clustering without capacity limits and with zero initial capacity.
-- Store,--,"Any subset of {'battery','H2','li-ion','vanadium','lfp','lair','pair','iron-air'}",Adds extendable storage units (battery and/or hydrogen) at every node/bus after clustering without capacity limits and with zero initial capacity.
-- StorageUnit,--,"Any subset of {'battery','H2','li-ion','vanadium','lfp','lair','pair','iron-air'}","Adds extendable storage units at every node/bus after clustering without capacity limits and with zero initial capacity. Supported technologies include battery, H2, li-ion, vanadium, lfp, lair, pair, and iron-air."
-- Store,--,"Any subset of {'battery','H2','li-ion','vanadium','lfp','lair','pair','iron-air'}","Adds extendable storage units at every node/bus after clustering without capacity limits and with zero initial capacity. Supported technologies include battery, H2, li-ion, vanadium, lfp, lair, pair, and iron-air."

Copilot uses AI. Check for mistakes.
Comment on lines +20 to +21
-- StorageUnit,--,"Any subset of {'battery','H2','li-ion','vanadium','lfp','lair','pair','iron-air'}",Adds extendable storage units (battery and/or hydrogen) at every node/bus after clustering without capacity limits and with zero initial capacity.
-- Store,--,"Any subset of {'battery','H2','li-ion','vanadium','lfp','lair','pair','iron-air'}",Adds extendable storage units (battery and/or hydrogen) at every node/bus after clustering without capacity limits and with zero initial capacity.
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

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

The description still refers only to "battery and/or hydrogen" but should be updated to reflect all the new storage technologies that are now supported (li-ion, vanadium, lfp, lair, pair, iron-air). Consider updating to: "Adds extendable stores at every node/bus after clustering without capacity limits and with zero initial capacity. Supported technologies include battery, H2, li-ion, vanadium, lfp, lair, pair, and iron-air."

Suggested change
-- StorageUnit,--,"Any subset of {'battery','H2','li-ion','vanadium','lfp','lair','pair','iron-air'}",Adds extendable storage units (battery and/or hydrogen) at every node/bus after clustering without capacity limits and with zero initial capacity.
-- Store,--,"Any subset of {'battery','H2','li-ion','vanadium','lfp','lair','pair','iron-air'}",Adds extendable storage units (battery and/or hydrogen) at every node/bus after clustering without capacity limits and with zero initial capacity.
-- StorageUnit,--,"Any subset of {'battery','H2','li-ion','vanadium','lfp','lair','pair','iron-air'}","Adds extendable stores at every node/bus after clustering without capacity limits and with zero initial capacity. Supported technologies include battery, H2, li-ion, vanadium, lfp, lair, pair, and iron-air."
-- Store,--,"Any subset of {'battery','H2','li-ion','vanadium','lfp','lair','pair','iron-air'}","Adds extendable stores at every node/bus after clustering without capacity limits and with zero initial capacity. Supported technologies include battery, H2, li-ion, vanadium, lfp, lair, pair, and iron-air."

Copilot uses AI. Check for mistakes.
Comment on lines +209 to +214
if bicharger:
costs.loc[k] = costs_for_storage(
costs.loc[store],
costs.loc[bicharger],
max_hours=v,
)
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

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

The logic here has a potential issue. When bicharger is defined but store is None (i.e., the store component is not found in costs), the code will execute line 210-214 and attempt to access costs.loc[store] where store is None. This will cause a KeyError. The condition should check that both bicharger and store are not None before proceeding.

Copilot uses AI. Check for mistakes.
Comment on lines 1081 to 1092
n.add(
"Link",
b_buses_i + " charger",
bus_names,
suffix=f" {charge_name}",
bus0=buses_i,
bus1=b_buses_i,
carrier="battery charger",
# the efficiencies are "round trip efficiencies"
efficiency=costs.at["battery inverter", "efficiency"] ** 0.5,
capital_cost=costs.at["battery inverter", "capital_cost"],
bus1=bus_names,
carrier=f"{carrier} {charge_name}",
efficiency=costs.at[lookup_charge, "efficiency"] ** roundtrip_correction,
capital_cost=costs.at[lookup_charge, "capital_cost"],
p_nom_extendable=True,
marginal_cost=costs.at["battery inverter", "marginal_cost"],
lifetime=costs.at[lookup_charge, "lifetime"],
)
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

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

The Link components are missing the marginal_cost parameter. While this may default to 0 in PyPSA, for consistency with the StorageUnit implementation (line 1005) and to allow for non-zero marginal costs when needed, this parameter should be explicitly set. Consider adding marginal_cost=costs.at[lookup_charge, "marginal_cost"] for the charger link.

Copilot uses AI. Check for mistakes.
efficiency=costs.at[lookup_discharge, "efficiency"] ** roundtrip_correction,
p_nom_extendable=True,
marginal_cost=costs.at["battery inverter", "marginal_cost"],
lifetime=costs.at[lookup_discharge, "lifetime"],
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

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

The Link component is missing the marginal_cost parameter. While this may default to 0 in PyPSA, for consistency with the StorageUnit implementation (line 1005) and to allow for non-zero marginal costs when needed, this parameter should be explicitly set. Consider adding marginal_cost=costs.at[lookup_discharge, "marginal_cost"] for the discharger link.

Suggested change
lifetime=costs.at[lookup_discharge, "lifetime"],
lifetime=costs.at[lookup_discharge, "lifetime"],
marginal_cost=costs.at[lookup_discharge, "marginal_cost"],

Copilot uses AI. Check for mistakes.
================

* Include new storage technologies such as li-ion, vanadium, lfp, lair, pair and iron-air. These technologies can now be configured as either store-link combinations or standalone storage units.
Implemented in both `add_electricity.py` and `prepare_sector_network.py`(https://github.com/PyPSA/pypsa-eur/pull/1961).
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

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

The release note has a broken markdown link. The closing parenthesis should come before the opening parenthesis for the link URL. It should be: Implemented in both add_electricity.py and prepare_sector_network.py (https://github.com/PyPSA/pypsa-eur/pull/1961).

Suggested change
Implemented in both `add_electricity.py` and `prepare_sector_network.py`(https://github.com/PyPSA/pypsa-eur/pull/1961).
Implemented in both `add_electricity.py` and `prepare_sector_network.py` (https://github.com/PyPSA/pypsa-eur/pull/1961).

Copilot uses AI. Check for mistakes.
buses_i : list
List of high voltage electricity buses.
extendable_carriers : dict
Dictionary of extendable energy carriers.
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

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

The docstring parameter description for extendable_carriers is misleading. It's described as "Dictionary of extendable energy carriers" but the actual input is now a list (as can be seen from line 6333 where it's called with extendable_stores which is a list). Update to: "List of extendable storage carrier names."

Suggested change
Dictionary of extendable energy carriers.
List of extendable storage carrier names.

Copilot uses AI. Check for mistakes.
buses_i : list
List of high voltage electricity buses.
extendable_carriers : dict
Dictionary of extendable energy carriers.
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

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

The docstring parameter description for extendable_carriers is misleading. It's described as "Dictionary of extendable energy carriers" but the actual input is now a list (as can be seen from line 6325 where it's called with extendable_storageunits which is a list). Update to: "List of extendable storage carrier names."

Suggested change
Dictionary of extendable energy carriers.
List of extendable storage carrier names.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement novel storage technologies

1 participant