diff --git a/Snakefile b/Snakefile index 6e484aae..31370603 100644 --- a/Snakefile +++ b/Snakefile @@ -8,7 +8,7 @@ wildcard_constraints: clusters="[0-9]+m?", sectors="[+a-zA-Z0-9]+", opts="[-+a-zA-Z0-9]*", - sector_opts="[-+a-zA-Z0-9\.]*" + sector_opts="[-+a-zA-Z0-9\.\s]*" diff --git a/config.default.yaml b/config.default.yaml index 6af565d9..c0143d01 100644 --- a/config.default.yaml +++ b/config.default.yaml @@ -15,14 +15,15 @@ scenario: lv: [1.0,1.5] # allowed transmission line volume expansion, can be any float >= 1.0 (today) or "opt" clusters: [45,50] # number of nodes in Europe, any integer between 37 (1 node per country-zone) and several hundred opts: [''] # only relevant for PyPSA-Eur - sector_opts: [Co2L0-3H-T-H-B-I-solar3-dist1] # this is where the main scenario settings are + sector_opts: [Co2L0-3H-T-H-B-I-solar+p3-dist1] # this is where the main scenario settings are # to really understand the options here, look in scripts/prepare_sector_network.py # Co2Lx specifies the CO2 target in x% of the 1990 values; default will give default (5%); # Co2L0p25 will give 25% CO2 emissions; Co2Lm0p05 will give 5% negative emissions # xH is the temporal resolution; 3H is 3-hourly, i.e. one snapshot every 3 hours # single letters are sectors: T for land transport, H for building heating, # B for biomass supply, I for industry, shipping and aviation - # solarx or onwindx changes the available installable potential by factor x + # solar+c0.5 reduces the capital cost of solar to 50\% of reference value + # solar+p3 multiplies the available installable potential by factor 3 # dist{n} includes distribution grids with investment cost of n times cost in data/costs.csv # for myopic/perfect foresight cb states the carbon budget in GtCO2 (cumulative # emissions throughout the transition path in the timeframe determined by the diff --git a/doc/release_notes.rst b/doc/release_notes.rst index 66594614..8076d778 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -5,6 +5,7 @@ Release Notes Future release =================== *For the myopic option, a carbon budget and a type of decay (exponential or beta) can be selected in the config file to distribute the budget across the planning_horizons. +*Added an option to alter the capital cost or maximum capacity of carriers by a factor via ``carrier+factor`` in the ``{opts}`` wildcard. This can be useful for exploring uncertain cost parameters. Example: ``solar+c0.5`` reduces the capital cost of solar to 50\% of original values. Similarly ``solar+p3`` multiplies the p_nom_max by 3. PyPSA-Eur-Sec 0.4.0 (11th December 2020) ========================================= diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index a2c2795e..fd8321c4 100644 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -1812,13 +1812,6 @@ def add_waste_heat(network): network.links.loc[urban_central + " H2 Fuel Cell","bus2"] = urban_central + " urban central heat" network.links.loc[urban_central + " H2 Fuel Cell","efficiency2"] = 0.95 - network.links.loc[urban_central + " H2 Fuel Cell","efficiency"] - -def restrict_technology_potential(n,tech,limit): - print("restricting potentials (p_nom_max) for {} to {} of technical potential".format(tech,limit)) - gens = n.generators.index[n.generators.carrier.str.contains(tech)] - #beware if limit is 0 and p_nom_max is np.inf, 0*np.inf is nan - n.generators.loc[gens,"p_nom_max"] *=limit - def decentral(n): n.lines.drop(n.lines.index,inplace=True) n.links.drop(n.links.index[n.links.carrier.isin(["DC","B2B"])],inplace=True) @@ -1862,7 +1855,7 @@ if __name__ == "__main__": snakemake = MockSnakemake( wildcards=dict(network='elec', simpl='', clusters='37', lv='1.0', opts='', planning_horizons='2020', - sector_opts='120H-T-H-B-I-solar3-dist1-cb48be3'), + sector_opts='120H-T-H-B-I-onwind+p3-dist1-cb48be3'), input=dict( network='../pypsa-eur/networks/{network}_s{simpl}_{clusters}_ec_lv{lv}_{opts}.nc', energy_totals_name='resources/energy_totals.csv', co2_totals_name='resources/co2_totals.csv', @@ -2028,15 +2021,7 @@ if __name__ == "__main__": print("adding CO2 budget limit as per unit of 1990 levels of",limit) add_co2limit(n, Nyears, limit) - for o in opts: - for tech in ["solar","onwind","offwind"]: - if tech in o: - limit = o[o.find(tech)+len(tech):] - limit = float(limit.replace("p",".").replace("m","-")) - print("changing potential for",tech,"by factor",limit) - restrict_technology_potential(n,tech,limit) - if o[:10] == 'linemaxext': maxext = float(o[10:])*1e3 print("limiting new HVAC and HVDC extensions to",maxext,"MW") @@ -2047,6 +2032,31 @@ if __name__ == "__main__": if snakemake.config["sector"]['electricity_distribution_grid']: insert_electricity_distribution_grid(n) + + for o in opts: + if "+" in o: + oo = o.split("+") + carrier_list=np.hstack((n.generators.carrier.unique(), n.links.carrier.unique(), + n.stores.carrier.unique(), n.storage_units.carrier.unique())) + suptechs = map(lambda c: c.split("-", 2)[0], carrier_list) + if oo[0].startswith(tuple(suptechs)): + carrier = oo[0] + attr_lookup = {"p": "p_nom_max", "c": "capital_cost"} + attr = attr_lookup[oo[1][0]] + factor = float(oo[1][1:]) + #beware if factor is 0 and p_nom_max is np.inf, 0*np.inf is nan + if carrier == "AC": # lines do not have carrier + n.lines[attr] *= factor + else: + comps = {"Generator", "Link", "StorageUnit"} if attr=='p_nom_max' else {"Generator", "Link", "StorageUnit", "Store"} + for c in n.iterate_components(comps): + if carrier=='solar': + sel = c.df.carrier.str.contains(carrier) & ~c.df.carrier.str.contains("solar rooftop") + else: + sel = c.df.carrier.str.contains(carrier) + c.df.loc[sel,attr] *= factor + print("changing", attr ,"for",carrier,"by factor",factor) + if snakemake.config["sector"]['gas_distribution_grid']: insert_gas_distribution_costs(n) if snakemake.config["sector"]['electricity_grid_connection']: