From 5c3fcb642c306d4c58aa499186e0c0eb613a1862 Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Tue, 5 Feb 2019 23:00:35 +0100 Subject: [PATCH] add_electricity: Add heuristic for estimating renewable capacities from country totals Split per-country capacity totals reported in entsoe SO&AF 2016 in proportion to yearly generation potential at each bus, i.e. p_nom_max * mean(p_max_pu) --- config.yaml | 5 +++ scripts/add_electricity.py | 39 ++++++++++++++++++-- scripts/build_powerplants.py | 71 ++++++++++++++++++------------------ 3 files changed, 77 insertions(+), 38 deletions(-) diff --git a/config.yaml b/config.yaml index fd4f7884..a724df72 100644 --- a/config.yaml +++ b/config.yaml @@ -36,6 +36,11 @@ electricity: battery: 6 H2: 168 + # 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] + conventional_carriers: [] # nuclear, oil, OCGT, CCGT, coal, lignite, geothermal, biomass] atlite: diff --git a/scripts/add_electricity.py b/scripts/add_electricity.py index 5037ef07..1fa37ef3 100644 --- a/scripts/add_electricity.py +++ b/scripts/add_electricity.py @@ -18,6 +18,13 @@ from vresutils import transfer as vtransfer import pypsa +try: + import powerplantmatching as ppm + from build_powerplants import country_alpha_2 + + has_ppm = True +except ImportError: + has_ppm = False def normed(s): return s/s.sum() @@ -396,6 +403,29 @@ def attach_storage(n, costs): # marginal_cost=options['marginal_cost_storage'], # p_nom_extendable=True) +def estimate_renewable_capacities(n, tech_map=None): + if tech_map is None: + tech_map = snakemake.config['electricity'].get('estimate_renewable_capacities_from_capacity_stats', {}) + + if len(tech_map) == 0: return + + assert has_ppm, "The estimation of renewable capacities needs the powerplantmatching package" + + capacities = ppm.data.Capacity_stats() + capacities['alpha_2'] = capacities['Country'].map(country_alpha_2) + capacities = capacities.loc[capacities.Energy_Source_Level_2].set_index(['Fueltype', 'alpha_2']).sort_index() + + countries = n.buses.country.unique() + + for ppm_fueltype, techs in tech_map.items(): + tech_capacities = capacities.loc[ppm_fueltype, 'Capacity'].reindex(countries, fill_value=0.) + tech_b = n.generators.carrier.isin(techs) + n.generators.loc[tech_b, 'p_nom'] = ( + (n.generators_t.p_max_pu.mean().loc[tech_b] * n.generators.loc[tech_b, 'p_nom_max']) # maximal yearly generation + .groupby(n.generators.bus.map(n.buses.country)) # for each country + .transform(lambda s: normed(s) * tech_capacities.at[s.name]) + .where(lambda s: s>0.1, 0.) # only capacities above 100kW + ) def add_co2limit(n, Nyears=1.): n.add("GlobalConstraint", "CO2Limit", @@ -418,11 +448,12 @@ if __name__ == "__main__": snakemake = MockSnakemake(output=['networks/elec.nc']) snakemake.input = snakemake.expand( Dict(base_network='networks/base.nc', - tech_costs='data/costs/costs.csv', + tech_costs='data/costs.csv', regions="resources/regions_onshore.geojson", powerplants="resources/powerplants.csv", - hydro_capacities='data/hydro_capacities.csv', - opsd_load='data/time_series_60min_singleindex_filtered.csv', + hydro_capacities='data/bundle/hydro_capacities.csv', + opsd_load='data/bundle/time_series_60min_singleindex_filtered.csv', + nuts3_shapes='resources/nuts3_shapes.geojson', **{'profile_' + t: "resources/profile_" + t + ".nc" for t in snakemake.config['renewable']}) ) @@ -445,4 +476,6 @@ if __name__ == "__main__": attach_extendable_generators(n, costs, ppl) attach_storage(n, costs) + estimate_renewable_capacities(n) + n.export_to_netcdf(snakemake.output[0]) diff --git a/scripts/build_powerplants.py b/scripts/build_powerplants.py index 4e76a408..bf5a8a1a 100644 --- a/scripts/build_powerplants.py +++ b/scripts/build_powerplants.py @@ -18,50 +18,51 @@ def country_alpha_2(name): cntry = pyc.countries.get(official_name=name) return cntry.alpha_2 -if 'snakemake' not in globals(): - from vresutils.snakemake import MockSnakemake, Dict +if __name__ == "__main__": + if 'snakemake' not in globals(): + from vresutils.snakemake import MockSnakemake, Dict - snakemake = MockSnakemake( - input=Dict(base_network='networks/base.nc'), - output=['resources/powerplants.csv'] - ) + snakemake = MockSnakemake( + input=Dict(base_network='networks/base.nc'), + output=['resources/powerplants.csv'] + ) -logging.basicConfig(level=snakemake.config['logging_level']) + logging.basicConfig(level=snakemake.config['logging_level']) -n = pypsa.Network(snakemake.input.base_network) + n = pypsa.Network(snakemake.input.base_network) -ppl = (ppm.collection.matched_data() - [lambda df : ~df.Fueltype.isin(('Solar', 'Wind'))] - .pipe(ppm.cleaning.clean_technology) - .assign(Fueltype=lambda df: ( - df.Fueltype.where(df.Fueltype != 'Natural Gas', - df.Technology.replace('Steam Turbine', 'OCGT').fillna('OCGT')))) - .pipe(ppm.utils.fill_geoposition)) + ppl = (ppm.collection.matched_data() + [lambda df : ~df.Fueltype.isin(('Solar', 'Wind'))] + .pipe(ppm.cleaning.clean_technology) + .assign(Fueltype=lambda df: ( + df.Fueltype.where(df.Fueltype != 'Natural Gas', + df.Technology.replace('Steam Turbine', 'OCGT').fillna('OCGT')))) + .pipe(ppm.utils.fill_geoposition)) -# ppl.loc[(ppl.Fueltype == 'Other') & ppl.Technology.str.contains('CCGT'), 'Fueltype'] = 'CCGT' -# ppl.loc[(ppl.Fueltype == 'Other') & ppl.Technology.str.contains('Steam Turbine'), 'Fueltype'] = 'CCGT' + # ppl.loc[(ppl.Fueltype == 'Other') & ppl.Technology.str.contains('CCGT'), 'Fueltype'] = 'CCGT' + # ppl.loc[(ppl.Fueltype == 'Other') & ppl.Technology.str.contains('Steam Turbine'), 'Fueltype'] = 'CCGT' -ppl = ppl.loc[ppl.lon.notnull() & ppl.lat.notnull()] + ppl = ppl.loc[ppl.lon.notnull() & ppl.lat.notnull()] -ppl_country = ppl.Country.map(country_alpha_2) -countries = n.buses.country.unique() -cntries_without_ppl = [] + ppl_country = ppl.Country.map(country_alpha_2) + countries = n.buses.country.unique() + cntries_without_ppl = [] -for cntry in countries: - substation_lv_i = n.buses.index[n.buses['substation_lv'] & (n.buses.country == cntry)] - ppl_b = ppl_country == cntry - if not ppl_b.any(): - cntries_without_ppl.append(cntry) - continue + for cntry in countries: + substation_lv_i = n.buses.index[n.buses['substation_lv'] & (n.buses.country == cntry)] + ppl_b = ppl_country == cntry + if not ppl_b.any(): + cntries_without_ppl.append(cntry) + continue - kdtree = KDTree(n.buses.loc[substation_lv_i, ['x','y']].values) - ppl.loc[ppl_b, 'bus'] = substation_lv_i[kdtree.query(ppl.loc[ppl_b, ['lon','lat']].values)[1]] + kdtree = KDTree(n.buses.loc[substation_lv_i, ['x','y']].values) + ppl.loc[ppl_b, 'bus'] = substation_lv_i[kdtree.query(ppl.loc[ppl_b, ['lon','lat']].values)[1]] -if cntries_without_ppl: - logging.warning("No powerplants known in: {}".format(", ".join(cntries_without_ppl))) + if cntries_without_ppl: + logging.warning("No powerplants known in: {}".format(", ".join(cntries_without_ppl))) -bus_null_b = ppl["bus"].isnull() -if bus_null_b.any(): - logging.warning("Couldn't find close bus for {} powerplants".format(bus_null_b.sum())) + bus_null_b = ppl["bus"].isnull() + if bus_null_b.any(): + logging.warning("Couldn't find close bus for {} powerplants".format(bus_null_b.sum())) -ppl.to_csv(snakemake.output[0]) + ppl.to_csv(snakemake.output[0])