Skip to content
Draft
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
1 change: 1 addition & 0 deletions config/config.default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,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
Expand Down
10 changes: 10 additions & 0 deletions config/schema.default.json
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,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'.",
Expand Down Expand Up @@ -9768,6 +9773,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'.",
Expand Down
2 changes: 2 additions & 0 deletions doc/release_notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Upcoming Release

* Add configuration schema updater that allows changes to be made in soft-forks without touching base PyPSA-Eur files (#2014).

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

* Rewrite mapping of power plant sites to model regions / buses. Previously, power plants were mapped to the nearest bus in the same country.
Expand Down
1 change: 1 addition & 0 deletions rules/build_electricity.smk
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,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"),
input:
unpack(input_profile_tech),
unpack(input_class_regions),
Expand Down
61 changes: 60 additions & 1 deletion scripts/add_electricity.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -270,6 +324,7 @@ def load_and_aggregate_powerplants(
consider_efficiency_classes: bool = False,
aggregation_strategies: dict = None,
exclude_carriers: list = None,
estimate_efficiencies: bool = False,
) -> pd.DataFrame:
if not aggregation_strategies:
aggregation_strategies = {}
Expand Down Expand Up @@ -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"] = (
Expand Down Expand Up @@ -1154,6 +1212,7 @@ def attach_stores(
params.consider_efficiency_classes,
params.aggregation_strategies,
params.exclude_carriers,
params.estimate_efficiencies,
)

attach_load(
Expand Down
4 changes: 4 additions & 0 deletions scripts/lib/validation/config/conventional.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'.",
Expand Down