diff --git a/config.default.yaml b/config.default.yaml index d2bf6159..f952f5b7 100755 --- a/config.default.yaml +++ b/config.default.yaml @@ -58,10 +58,20 @@ electricity: conventional_carriers: [nuclear, oil, OCGT, CCGT, coal, lignite, geothermal, biomass] renewable_capacities_from_OPSD: [] # onwind, offwind, solar - # estimate_renewable_capacities_from_capacity_stats: - # # Wind is the Fueltype in ppm.data.Capacity_stats, onwind, offwind-{ac,dc} the carrier in PyPSA-Eur - # Wind: [onwind, offwind-ac, offwind-dc] - # Solar: [solar] + estimate_renewable_capacities: + # Renewable capacities are based on existing capacities reported by IRENA + + # Reference year, any of 2000 to 2020 + year: 2020 + # Artificially limit maximum capacities to factor * (IRENA capacities), + # i.e. 110% of 's capacities => expansion_limit: 1.1 + # false: Use estimated renewable potentials determine by the workflow + expansion_limit: false + technology_mapping: + # Wind is the Fueltype in ppm.data.Capacity_stats, onwind, offwind-{ac,dc} the carrier in PyPSA-Eur + Offshore: [offwind-ac, offwind-dc] + Onshore: [onwind] + PV: [solar] atlite: nprocesses: 4 diff --git a/doc/release_notes.rst b/doc/release_notes.rst index 3f131dc0..9b012bf8 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -7,6 +7,26 @@ Release Notes ########################################## +Energy Security Release (April 2022) +==================================== + +**New Features and Changes** + +* Added existing renewable capacities for all countries based on IRENA statistics (IRENASTAT) using new ``powerplantmatching`` version: + * Configuration of reference year for capacities can be configured (default: ``2020``) + * Uniform expansion limit of renewable build-up based on existing capacities can be configured using ``expansion_limit`` option + (default: ``false``; limited to determined renewable potentials) + * Distribution of country-level capacities proportional to maximum annual energy yield for each bus region + * This functionality was previously using OPSD data. + * The corresponding ``config`` entries changed, cf. ``config.default.yaml``: + * old: ``estimate_renewable_capacities_from_capacity_stats`` + * new: ``estimate_renewable_capacities`` + + +**Bugs and Compatibility** + +* ``powerplantmatching>=0.5.1`` is now required for ``IRENASTATS``. + Synchronisation Release - Ukraine and Moldova (17th March 2022) =============================================================== diff --git a/envs/environment.yaml b/envs/environment.yaml index 3c69b77b..d02b0018 100644 --- a/envs/environment.yaml +++ b/envs/environment.yaml @@ -24,7 +24,7 @@ dependencies: - yaml - pytables - lxml - - powerplantmatching>=0.4.8 + - powerplantmatching>=0.5.1 - numpy - pandas - geopandas diff --git a/scripts/add_electricity.py b/scripts/add_electricity.py index ad932cd8..f468bab3 100755 --- a/scripts/add_electricity.py +++ b/scripts/add_electricity.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: : 2017-2020 The PyPSA-Eur Authors +# SPDX-FileCopyrightText: : 2017-2022 The PyPSA-Eur Authors # # SPDX-License-Identifier: MIT @@ -491,29 +491,29 @@ def attach_OPSD_renewables(n, techs): -def estimate_renewable_capacities(n, tech_map): +def estimate_renewable_capacities(n, config): - if len(tech_map) == 0: return - - capacities = (pm.data.Capacity_stats().powerplant.convert_country_to_alpha2() - [lambda df: df.Energy_Source_Level_2] - .set_index(['Fueltype', 'Country']).sort_index()) - - countries = n.buses.country.unique() + if not config["electricity"]["estimate_renewable_capacities"]: return + + year = config["electricity"]["estimate_renewable_capacities"]["year"] + tech_map = config["electricity"]["estimate_renewable_capacities"]["technology_mapping"] + tech_keys = list(tech_map.keys()) + countries = config["countries"] + expansion_limit = config["electricity"]["estimate_renewable_capacities"]["expansion_limit"] if len(countries) == 0: return + if len(tech_map) == 0: return - logger.info('heuristics applied to distribute renewable capacities [MW] \n{}' - .format(capacities.query('Fueltype in @tech_map.keys() and Capacity >= 0.1') - .groupby('Country').agg({'Capacity': 'sum'}))) + capacities = pm.data.IRENASTAT().powerplant.convert_country_to_alpha2() + capacities = capacities.query("Year == @year and Technology in @tech_keys and Country in @countries") + capacities = capacities.groupby(["Technology", "Country"]).Capacity.sum() - for ppm_fueltype, techs in tech_map.items(): - tech_capacities = capacities.loc[ppm_fueltype, 'Capacity']\ - .reindex(countries, fill_value=0.) - #tech_i = n.generators.query('carrier in @techs').index - tech_i = (n.generators.query('carrier in @techs') - [n.generators.query('carrier in @techs') - .bus.map(n.buses.country).isin(countries)].index) + logger.info(f"Heuristics applied to distribute renewable capacities [MW] " + f"{capacities.groupby('Country').sum()}") + + for ppm_technology, techs in tech_map.items(): + tech_capacities = capacities.loc[ppm_technology].reindex(countries, fill_value=0.) + tech_i = n.generators.query('carrier in @techs').index n.generators.loc[tech_i, 'p_nom'] = ( (n.generators_t.p_max_pu[tech_i].mean() * n.generators.loc[tech_i, 'p_nom_max']) # maximal yearly generation @@ -522,6 +522,11 @@ def estimate_renewable_capacities(n, tech_map): .where(lambda s: s>0.1, 0.)) # only capacities above 100kW n.generators.loc[tech_i, 'p_nom_min'] = n.generators.loc[tech_i, 'p_nom'] + if expansion_limit: + assert np.isscalar(expansion_limit) + logger.info(f"Reducing capacity expansion limit to {expansion_limit*100:.2f}% of installed capacity.") + n.generators.loc[tech_i, 'p_nom_max'] = float(expansion_limit) * n.generators.loc[tech_i, 'p_nom_min'] + def add_nice_carrier_names(n, config): carrier_i = n.carriers.index @@ -565,11 +570,9 @@ if __name__ == "__main__": carriers = snakemake.config['electricity']['extendable_carriers']['Generator'] attach_extendable_generators(n, costs, ppl, carriers) - tech_map = snakemake.config['electricity'].get('estimate_renewable_capacities_from_capacity_stats', {}) - estimate_renewable_capacities(n, tech_map) + estimate_renewable_capacities(n, snakemake.config) techs = snakemake.config['electricity'].get('renewable_capacities_from_OPSD', []) attach_OPSD_renewables(n, techs) - update_p_nom_max(n) add_nice_carrier_names(n, snakemake.config)