diff --git a/config/config.default.yaml b/config/config.default.yaml index cd940e1a55..c02535e009 100644 --- a/config/config.default.yaml +++ b/config/config.default.yaml @@ -421,6 +421,7 @@ renewable: # docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#conventional conventional: + estimate_efficiencies: false unit_commitment: false dynamic_fuel_price: false fuel_price_rolling_window: 6 diff --git a/config/schema.default.json b/config/schema.default.json index bed19a77ee..e4b50c3a6f 100644 --- a/config/schema.default.json +++ b/config/schema.default.json @@ -657,6 +657,11 @@ "additionalProperties": true, "description": "Configuration for `conventional` settings.", "properties": { + "estimate_efficiencies": { + "default": false, + "description": "Estimate missing plant-level efficiencies from a carrier- and age-dependent linear heuristic.", + "type": "boolean" + }, "unit_commitment": { "default": false, "description": "Allow the overwrite of ramp_limit_up, ramp_limit_start_up, ramp_limit_shut_down, p_min_pu, min_up_time, min_down_time, and start_up_cost of conventional generators. Refer to the CSV file 'unit_commitment.csv'.", @@ -10041,6 +10046,11 @@ "additionalProperties": true, "description": "Configuration for `conventional` settings.", "properties": { + "estimate_efficiencies": { + "default": false, + "description": "Estimate missing plant-level efficiencies from a carrier- and age-dependent linear heuristic.", + "type": "boolean" + }, "unit_commitment": { "default": false, "description": "Allow the overwrite of ramp_limit_up, ramp_limit_start_up, ramp_limit_shut_down, p_min_pu, min_up_time, min_down_time, and start_up_cost of conventional generators. Refer to the CSV file 'unit_commitment.csv'.", diff --git a/doc/release_notes.rst b/doc/release_notes.rst index d2b877bc60..6d61283ca8 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -11,6 +11,10 @@ Release Notes * Fix: ``atlite.plot_availability_matrix`` config option for :mod:`determine_availability_matrix` and :mod:`determine_availability_matrix_MD_UA` scripts, changed their output and behaviour to align consistently (https://github.com/PyPSA/pypsa-eur/pull/2173). +* New ``conventional: estimate_efficiencies`` option (default ``false``) to fill missing plant-level efficiencies using a carrier- and age-dependent heuristic. + +* Adjust ``powerplants_filter`` to include power plants operational in 2025. + * Fix: Activate losses for `H2 pipeline retrofitted` links by default, to ensure consistency with `H2 pipeline` links. * Fix: Re-introduce capital costs for non-bicharging discharge links in ``add_electricity.py``, e.g. fuel cells. diff --git a/rules/build_electricity.smk b/rules/build_electricity.smk index 15583d6785..87fee767b8 100755 --- a/rules/build_electricity.smk +++ b/rules/build_electricity.smk @@ -839,6 +839,7 @@ rule add_electricity: ), aggregation_strategies=config_provider("clustering", "aggregation_strategies"), exclude_carriers=config_provider("clustering", "exclude_carriers"), + estimate_efficiencies=config_provider("conventional", "estimate_efficiencies"), message: "Adding electricity to network with {wildcards.clusters} clusters" script: diff --git a/scripts/add_electricity.py b/scripts/add_electricity.py index a5828c6799..c1468edccd 100755 --- a/scripts/add_electricity.py +++ b/scripts/add_electricity.py @@ -254,6 +254,60 @@ def sanitize_locations(n): ) +def estimate_efficiency(df: pd.DataFrame, reference_year: int = 2025) -> pd.Series: + carrier = df.carrier + year = df.dateretrofit.combine_first(df.datein) + + offset = carrier.map( + { + "lignite": 0.25, + "coal": 0.28, + "CCGT": 0.40, + "OCGT": 0.28, + "oil": 0.28, + "nuclear": 0.33, + } + ) + slope = carrier.map( + { + "lignite": 0.003, + "coal": 0.003, + "CCGT": 0.004, + "OCGT": 0.003, + "oil": 0.002, + "nuclear": 0.0, + } + ) + year0 = carrier.map( + { + "lignite": 1960, + "coal": 1960, + "CCGT": 1980, + "OCGT": 1970, + "oil": 1960, + "nuclear": 1960, + } + ) + cap = carrier.map( + { + "lignite": 0.42, + "coal": 0.44, + "CCGT": 0.60, + "OCGT": 0.41, + "oil": 0.38, + "nuclear": 0.33, + } + ) + + # heuristic linear efficiency estimation + eta = (offset + slope * (year - year0)).clip(lower=offset, upper=cap) + + # degradation of efficiency + eta *= 1 - (reference_year - year - 10).clip(lower=0) * 0.001 + + return eta + + def add_co2_emissions(n, costs, carriers): """ Add CO2 emissions to the network's carriers attribute. @@ -270,6 +324,7 @@ def load_and_aggregate_powerplants( consider_efficiency_classes: bool | list[float] = False, aggregation_strategies: dict = None, exclude_carriers: list = None, + estimate_efficiencies: bool = False, ) -> pd.DataFrame: if not aggregation_strategies: aggregation_strategies = {} @@ -313,7 +368,10 @@ def load_and_aggregate_powerplants( ] ppl = ppl.join(costs[cost_columns], on="carrier", rsuffix="_r") - ppl["efficiency"] = ppl.efficiency.combine_first(ppl.efficiency_r) + efficiency = ppl.efficiency + if estimate_efficiencies: + efficiency = efficiency.combine_first(estimate_efficiency(ppl)) + ppl["efficiency"] = efficiency.combine_first(ppl.efficiency_r) ppl["lifetime"] = (ppl.dateout - ppl.datein).fillna(np.inf) ppl["build_year"] = ppl.datein.fillna(0).astype(int) ppl["marginal_cost"] = ( @@ -1220,6 +1278,7 @@ def attach_stores( params.consider_efficiency_classes, params.aggregation_strategies, params.exclude_carriers, + params.estimate_efficiencies, ) attach_load( diff --git a/scripts/lib/validation/config/conventional.py b/scripts/lib/validation/config/conventional.py index 7094aa8a94..321c3e7bb5 100644 --- a/scripts/lib/validation/config/conventional.py +++ b/scripts/lib/validation/config/conventional.py @@ -18,6 +18,10 @@ class ConventionalConfig(ConfigModel): model_config = ConfigDict(extra="allow") + estimate_efficiencies: bool = Field( + False, + description="Estimate missing plant-level efficiencies from a carrier- and age-dependent linear heuristic.", + ) unit_commitment: bool = Field( False, description="Allow the overwrite of ramp_limit_up, ramp_limit_start_up, ramp_limit_shut_down, p_min_pu, min_up_time, min_down_time, and start_up_cost of conventional generators. Refer to the CSV file 'unit_commitment.csv'.",