Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@

* chore: Drop the `grpcio < 1.78` pin and add a temporary `python < 3.14` pin ([#2244](https://github.com/PyPSA/pypsa-eur/pull/2244)). The upper bound on python will be lifted once [#2245](https://github.com/PyPSA/pypsa-eur/issues/2245) is resolved.

* refactor: Refactor some input definitions to use conditional input files ([#2248](https://github.com/PyPSA/pypsa-eur/pull/2248)).

## PyPSA-Eur v2026.02.0 (18th February 2026)

**Features**
Expand Down
16 changes: 15 additions & 1 deletion rules/build_electricity.smk
Original file line number Diff line number Diff line change
Expand Up @@ -804,7 +804,21 @@ rule add_electricity:
f"costs_{config_provider('costs', 'year')(w)}_processed.csv"
),
regions=resources("regions_onshore_base_s_{clusters}.geojson"),
powerplants=resources("powerplants_s_{clusters}.csv"),
powerplants=branch(
lambda w: config_provider("electricity", "conventional_carriers")(w)
or config_provider("electricity", "extendable_carriers", "Generator")(w)
or (
config_provider(
"electricity", "estimate_renewable_capacities", "enable"
)(w)
and config_provider(
"electricity",
"estimate_renewable_capacities",
"from_powerplantmatching",
)(w)
),
resources("powerplants_s_{clusters}.csv"),
),
hydro_capacities=ancient("data/hydro_capacities.csv"),
unit_commitment="data/unit_commitment.csv",
fuel_price=lambda w: (
Expand Down
47 changes: 40 additions & 7 deletions rules/build_sector.smk
Original file line number Diff line number Diff line change
Expand Up @@ -819,7 +819,9 @@ rule build_swiss_energy_balances:
rule build_energy_totals:
input:
nuts3_shapes=resources("nuts3_shapes.geojson"),
co2=rules.retrieve_ghg_emissions.output["csv"],
co2=branch(
config_provider("co2_budget"), rules.retrieve_ghg_emissions.output["csv"]
),
swiss=resources("switzerland_energy_balances.csv"),
swiss_transport=f"{BFS_ROAD_VEHICLE_STOCK_DATASET['folder']}/vehicle_stock.csv",
idees=rules.retrieve_jrc_idees.output["directory"],
Expand Down Expand Up @@ -1562,12 +1564,33 @@ def input_heat_source_power(w):
}


def input_gas_network(w):
inputs = {}

gas_network = config_provider("sector", "gas_network")(w)
h2_retrofit = config_provider("sector", "H2_retrofit")(w)
imports_enabled = config_provider("sector", "imports", "enable")(w)
import_carriers = config_provider("sector", "imports", "carriers")(w)

if gas_network or h2_retrofit:
inputs["clustered_gas_network"] = resources("gas_network_base_s_{clusters}.csv")

gas_imports = imports_enabled and "gas" in import_carriers
h2_imports = imports_enabled and "H2" in import_carriers

if gas_network or gas_imports or h2_imports:
inputs["gas_input_nodes_simplified"] = resources(
"gas_input_locations_s_{clusters}_simplified.csv"
)

return inputs


rule prepare_sector_network:
input:
unpack(input_profile_offwind),
unpack(input_heat_source_power),
**rules.cluster_gas_network.output,
**rules.build_gas_input_locations.output,
unpack(input_gas_network),
snapshot_weightings=resources(
"snapshot_weightings_base_s_{clusters}_elec_{opts}_{sector_opts}.csv"
),
Expand Down Expand Up @@ -1599,7 +1622,10 @@ rule prepare_sector_network:
pop_weighted_energy_totals=resources(
"pop_weighted_energy_totals_s_{clusters}.csv"
),
pop_weighted_heat_totals=resources("pop_weighted_heat_totals_s_{clusters}.csv"),
pop_weighted_heat_totals=branch(
config_provider("sector", "heating"),
resources("pop_weighted_heat_totals_s_{clusters}.csv"),
),
shipping_demand=resources("shipping_demand_s_{clusters}.csv"),
transport_demand=resources("transport_demand_s_{clusters}.csv"),
transport_data=resources("transport_data_s_{clusters}.csv"),
Expand All @@ -1608,8 +1634,12 @@ rule prepare_sector_network:
heat_dsm_profile=resources(
"residential_heat_dsm_profile_total_base_s_{clusters}.csv"
),
co2_totals_name=resources("co2_totals.csv"),
co2=rules.retrieve_ghg_emissions.output["csv"],
co2_totals_name=branch(
config_provider("co2_budget"), resources("co2_totals.csv")
),
co2=branch(
config_provider("co2_budget"), rules.retrieve_ghg_emissions.output["csv"]
),
biomass_potentials=resources(
"biomass_potentials_s_{clusters}_{planning_horizons}.csv"
),
Expand All @@ -1618,7 +1648,10 @@ rule prepare_sector_network:
if config_provider("foresight")(w) == "overnight"
else resources("costs_{planning_horizons}_processed.csv")
),
h2_cavern=resources("salt_cavern_potentials_s_{clusters}.csv"),
h2_cavern=branch(
config_provider("sector", "h2_topology_tyndp"),
otherwise=resources("salt_cavern_potentials_s_{clusters}.csv"),
),
busmap_s=resources("busmap_base_s.csv"),
busmap=resources("busmap_base_s_{clusters}.csv"),
clustered_pop_layout=resources("pop_layout_base_s_{clusters}.csv"),
Expand Down
4 changes: 3 additions & 1 deletion rules/postprocess.smk
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,9 @@ rule plot_summary:
energy=RESULTS + "csvs/energy.csv",
balances=RESULTS + "csvs/energy_balance.csv",
eurostat=resources("eurostat_energy_balances.csv"),
co2=rules.retrieve_ghg_emissions.output["csv"],
co2=branch(
config_provider("co2_budget"), rules.retrieve_ghg_emissions.output["csv"]
),
output:
costs=RESULTS + "graphs/costs.pdf",
energy=RESULTS + "graphs/energy.pdf",
Expand Down
6 changes: 5 additions & 1 deletion rules/solve_myopic.smk
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ rule add_existing_baseyear:
network=resources(
"networks/base_s_{clusters}_{opts}_{sector_opts}_{planning_horizons}.nc"
),
powerplants=resources("powerplants_s_{clusters}.csv"),
powerplants=branch(
lambda w: config_provider("electricity", "conventional_carriers")(w)
or config_provider("electricity", "extendable_carriers", "Generator")(w),
resources("powerplants_s_{clusters}.csv"),
),
costs=lambda w: resources(
f"costs_{config_provider('scenario', 'planning_horizons',0)(w)}_processed.csv"
),
Expand Down
6 changes: 5 additions & 1 deletion rules/solve_perfect.smk
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ rule add_existing_baseyear:
network=resources(
"networks/base_s_{clusters}_{opts}_{sector_opts}_{planning_horizons}.nc"
),
powerplants=resources("powerplants_s_{clusters}.csv"),
powerplants=branch(
lambda w: config_provider("electricity", "conventional_carriers")(w)
or config_provider("electricity", "extendable_carriers", "Generator")(w),
resources("powerplants_s_{clusters}.csv"),
),
busmap_s=resources("busmap_base_s.csv"),
busmap=resources("busmap_base_s_{clusters}.csv"),
clustered_pop_layout=resources("pop_layout_base_s_{clusters}.csv"),
Expand Down
26 changes: 19 additions & 7 deletions scripts/add_electricity.py
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,9 @@ def attach_conventional_generators(
fuel_price : pd.DataFrame, optional
DataFrame containing fuel price data, by default None.
"""
if ppl.empty:
return

carriers = list(
set(conventional_carriers)
| set(extendable_carriers["Generator"]) - set(renewable_carriers)
Expand Down Expand Up @@ -700,6 +703,9 @@ def attach_existing_batteries(
ppl: pd.DataFrame,
) -> None:
"""Attach existing battery storage units from the power plant dataset."""
if ppl.empty:
return

batt = ppl.query('carrier == "battery"')
if batt.empty:
return
Expand Down Expand Up @@ -760,6 +766,9 @@ def attach_hydro(
**params :
Additional parameters for hydro units.
"""
if ppl.empty:
return

add_missing_carriers(n, carriers)
add_co2_emissions(n, costs, carriers)

Expand Down Expand Up @@ -1215,13 +1224,16 @@ def attach_stores(

costs = load_costs(snakemake.input.costs)

ppl = load_and_aggregate_powerplants(
snakemake.input.powerplants,
costs,
params.consider_efficiency_classes,
params.aggregation_strategies,
params.exclude_carriers,
)
if ppl_fn := snakemake.input.powerplants:
ppl = load_and_aggregate_powerplants(
ppl_fn,
costs,
params.consider_efficiency_classes,
params.aggregation_strategies,
params.exclude_carriers,
)
else:
ppl = pd.DataFrame()

attach_load(
n,
Expand Down
3 changes: 3 additions & 0 deletions scripts/add_existing_baseyear.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,9 @@ def add_power_capacities_installed_before_baseyear(
"""
logger.debug(f"Adding power capacities installed before {baseyear}")

if not powerplants_file:
return

df_agg = pd.read_csv(powerplants_file, index_col=0)

rename_fuel = {
Expand Down
17 changes: 10 additions & 7 deletions scripts/build_energy_totals.py
Original file line number Diff line number Diff line change
Expand Up @@ -1331,13 +1331,16 @@ def build_heating_efficiencies(
)
district_heat_share.to_csv(snakemake.output.district_heat_share)

base_year_emissions = params["base_emissions_year"]
emissions_scope = snakemake.params.energy["emissions"]
eea_co2 = build_eea_co2(snakemake.input.co2, base_year_emissions, emissions_scope)
eurostat_co2 = build_eurostat_co2(eurostat, base_year_emissions)

co2 = build_co2_totals(countries, eea_co2, eurostat_co2)
co2.to_csv(snakemake.output.co2_name)
if co2_fn := snakemake.input.co2:
base_year_emissions = params["base_emissions_year"]
emissions_scope = snakemake.params.energy["emissions"]
eea_co2 = build_eea_co2(co2_fn, base_year_emissions, emissions_scope)
eurostat_co2 = build_eurostat_co2(eurostat, base_year_emissions)

co2 = build_co2_totals(countries, eea_co2, eurostat_co2)
co2.to_csv(snakemake.output.co2_name)
else:
pd.DataFrame().to_csv(snakemake.output.co2_name)

transport = build_transport_data(countries, population, idees)
transport.to_csv(snakemake.output.transport_name)
Expand Down
19 changes: 10 additions & 9 deletions scripts/prepare_sector_network.py
Original file line number Diff line number Diff line change
Expand Up @@ -6314,13 +6314,14 @@ def add_import_options(
pop_weighted_energy_totals = (
pd.read_csv(snakemake.input.pop_weighted_energy_totals, index_col=0) * nyears
)
pop_weighted_heat_totals = (
pd.read_csv(snakemake.input.pop_weighted_heat_totals, index_col=0) * nyears
)
pop_weighted_energy_totals.update(pop_weighted_heat_totals)
if options["heating"]:
pop_weighted_heat_totals = (
pd.read_csv(snakemake.input.pop_weighted_heat_totals, index_col=0) * nyears
)
pop_weighted_energy_totals.update(pop_weighted_heat_totals)

fn = snakemake.input.gas_input_nodes_simplified
gas_input_nodes = pd.read_csv(fn, index_col=0)
fn = snakemake.input.get("gas_input_nodes_simplified")
gas_input_nodes = pd.read_csv(fn, index_col=0) if fn else None

carriers_to_keep = snakemake.params.pypsa_eur
profiles = {
Expand Down Expand Up @@ -6388,7 +6389,7 @@ def add_import_options(
pop_layout=pop_layout,
h2_cavern_file=snakemake.input.h2_cavern,
cavern_types=snakemake.params.sector["hydrogen_underground_storage_locations"],
clustered_gas_network_file=snakemake.input.clustered_gas_network,
clustered_gas_network_file=snakemake.input.get("clustered_gas_network"),
gas_input_nodes=gas_input_nodes,
spatial=spatial,
options=options,
Expand Down Expand Up @@ -6584,8 +6585,8 @@ def add_import_options(
add_co2limit(
n,
options,
snakemake.input.co2_totals_name,
snakemake.params.countries,
snakemake.input.get("co2_totals_name"),
snakemake.params.get("countries"),
nyears,
limit,
)
Expand Down