diff --git a/Snakefile b/Snakefile index f003bd5e..1a572562 100644 --- a/Snakefile +++ b/Snakefile @@ -25,17 +25,12 @@ rule all: rule solve_all_networks: input: - expand(config['results_dir'] + config['run'] + "/postnetworks/elec_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{co2_budget_name}_{planning_horizons}.nc", + expand(config['results_dir'] + config['run'] + "/postnetworks/elec_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{planning_horizons}.nc", **config['scenario']) -rule test_script: - input: - expand("resources/heat_demand_urban_elec_s_{clusters}.nc", - **config['scenario']) - rule prepare_sector_networks: input: - expand(config['results_dir'] + config['run'] + "/prenetworks/elec_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{co2_budget_name}_{planning_horizons}.nc", + expand(config['results_dir'] + config['run'] + "/prenetworks/elec_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{planning_horizons}.nc", **config['scenario']) @@ -295,7 +290,6 @@ rule prepare_sector_network: heat_profile="data/heat_load_profile_BDEW.csv", costs=config['costs_dir'] + "costs_{planning_horizons}.csv", h2_cavern = "data/hydrogen_salt_cavern_potentials.csv", - co2_budget="data/co2_budget.csv", profile_offwind_ac=pypsaeur("resources/profile_offwind-ac.nc"), profile_offwind_dc=pypsaeur("resources/profile_offwind-dc.nc"), busmap_s=pypsaeur("resources/busmap_{network}_s{simpl}.csv"), @@ -321,20 +315,20 @@ rule prepare_sector_network: solar_thermal_total="resources/solar_thermal_total_{network}_s{simpl}_{clusters}.nc", solar_thermal_urban="resources/solar_thermal_urban_{network}_s{simpl}_{clusters}.nc", solar_thermal_rural="resources/solar_thermal_rural_{network}_s{simpl}_{clusters}.nc" - output: config['results_dir'] + config['run'] + '/prenetworks/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{co2_budget_name}_{planning_horizons}.nc' + output: config['results_dir'] + config['run'] + '/prenetworks/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{planning_horizons}.nc' threads: 1 resources: mem_mb=2000 - benchmark: config['results_dir'] + config['run'] + "/benchmarks/prepare_network/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{co2_budget_name}_{planning_horizons}" + benchmark: config['results_dir'] + config['run'] + "/benchmarks/prepare_network/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{planning_horizons}" script: "scripts/prepare_sector_network.py" rule plot_network: input: - network=config['results_dir'] + config['run'] + "/postnetworks/elec_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{co2_budget_name}_{planning_horizons}.nc" + network=config['results_dir'] + config['run'] + "/postnetworks/elec_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{planning_horizons}.nc" output: - map=config['results_dir'] + config['run'] + "/maps/elec_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}-costs-all_{co2_budget_name}_{planning_horizons}.pdf", - today=config['results_dir'] + config['run'] + "/maps/elec_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{co2_budget_name}_{planning_horizons}-today.pdf" + map=config['results_dir'] + config['run'] + "/maps/elec_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}-costs-all_{planning_horizons}.pdf", + today=config['results_dir'] + config['run'] + "/maps/elec_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{planning_horizons}-today.pdf" threads: 2 resources: mem_mb=10000 script: "scripts/plot_network.py" @@ -351,10 +345,10 @@ rule copy_config: rule make_summary: input: - networks=expand(config['results_dir'] + config['run'] + "/postnetworks/elec_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{co2_budget_name}_{planning_horizons}.nc", + networks=expand(config['results_dir'] + config['run'] + "/postnetworks/elec_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{planning_horizons}.nc", **config['scenario']), costs=config['costs_dir'] + "costs_{}.csv".format(config['scenario']['planning_horizons'][0]), - plots=expand(config['results_dir'] + config['run'] + "/maps/elec_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}-costs-all_{co2_budget_name}_{planning_horizons}.pdf", + plots=expand(config['results_dir'] + config['run'] + "/maps/elec_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}-costs-all_{planning_horizons}.pdf", **config['scenario']) #heat_demand_name='data/heating/daily_heat_demand.h5' output: @@ -397,16 +391,16 @@ if config["foresight"] == "overnight": rule solve_network: input: - network=config['results_dir'] + config['run'] + "/prenetworks/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{co2_budget_name}_{planning_horizons}.nc", + network=config['results_dir'] + config['run'] + "/prenetworks/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{planning_horizons}.nc", costs=config['costs_dir'] + "costs_{planning_horizons}.csv", config=config['summary_dir'] + '/' + config['run'] + '/configs/config.yaml' - output: config['results_dir'] + config['run'] + "/postnetworks/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{co2_budget_name}_{planning_horizons}.nc" + output: config['results_dir'] + config['run'] + "/postnetworks/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{planning_horizons}.nc" shadow: "shallow" log: - solver=config['results_dir'] + config['run'] + "/logs/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{co2_budget_name}_{planning_horizons}_solver.log", - python=config['results_dir'] + config['run'] + "/logs/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{co2_budget_name}_{planning_horizons}_python.log", - memory=config['results_dir'] + config['run'] + "/logs/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{co2_budget_name}_{planning_horizons}_memory.log" - benchmark: config['results_dir'] + config['run'] + "/benchmarks/solve_network/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{co2_budget_name}_{planning_horizons}" + solver=config['results_dir'] + config['run'] + "/logs/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{planning_horizons}_solver.log", + python=config['results_dir'] + config['run'] + "/logs/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{planning_horizons}_python.log", + memory=config['results_dir'] + config['run'] + "/logs/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{planning_horizons}_memory.log" + benchmark: config['results_dir'] + config['run'] + "/benchmarks/solve_network/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{planning_horizons}" threads: 4 resources: mem_mb=config['solving']['mem'] # group: "solve" # with group, threads is ignored https://bitbucket.org/snakemake/snakemake/issues/971/group-job-description-does-not-contain @@ -417,7 +411,7 @@ if config["foresight"] == "myopic": rule add_existing_baseyear: input: - network=config['results_dir'] + config['run'] + '/prenetworks/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{co2_budget_name}_{planning_horizons}.nc', + network=config['results_dir'] + config['run'] + '/prenetworks/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{planning_horizons}.nc', powerplants=pypsaeur('resources/powerplants.csv'), busmap_s=pypsaeur("resources/busmap_{network}_s{simpl}.csv"), busmap=pypsaeur("resources/busmap_{network}_s{simpl}_{clusters}.csv"), @@ -425,7 +419,7 @@ if config["foresight"] == "myopic": costs=config['costs_dir'] + "costs_{}.csv".format(config['scenario']['planning_horizons'][0]), cop_soil_total="resources/cop_soil_total_{network}_s{simpl}_{clusters}.nc", cop_air_total="resources/cop_air_total_{network}_s{simpl}_{clusters}.nc" - output: config['results_dir'] + config['run'] + '/prenetworks-brownfield/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{co2_budget_name}_{planning_horizons}.nc' + output: config['results_dir'] + config['run'] + '/prenetworks-brownfield/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{planning_horizons}.nc' wildcard_constraints: planning_horizons=config['scenario']['planning_horizons'][0] #only applies to baseyear threads: 1 @@ -434,36 +428,36 @@ if config["foresight"] == "myopic": def process_input(wildcards): i = config["scenario"]["planning_horizons"].index(int(wildcards.planning_horizons)) - return config['results_dir'] + config['run'] + "/postnetworks/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{co2_budget_name}_" + str(config["scenario"]["planning_horizons"][i-1]) + ".nc" + return config['results_dir'] + config['run'] + "/postnetworks/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_" + str(config["scenario"]["planning_horizons"][i-1]) + ".nc" rule add_brownfield: input: - network=config['results_dir'] + config['run'] + '/prenetworks/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{co2_budget_name}_{planning_horizons}.nc', + network=config['results_dir'] + config['run'] + '/prenetworks/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{planning_horizons}.nc', network_p=process_input, #solved network at previous time step costs=config['costs_dir'] + "costs_{planning_horizons}.csv", cop_soil_total="resources/cop_soil_total_{network}_s{simpl}_{clusters}.nc", cop_air_total="resources/cop_air_total_{network}_s{simpl}_{clusters}.nc" - output: config['results_dir'] + config['run'] + "/prenetworks-brownfield/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{co2_budget_name}_{planning_horizons}.nc" + output: config['results_dir'] + config['run'] + "/prenetworks-brownfield/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{planning_horizons}.nc" threads: 4 - resources: mem_mb=2000 + resources: mem_mb=10000 script: "scripts/add_brownfield.py" ruleorder: add_existing_baseyear > add_brownfield rule solve_network_myopic: input: - network=config['results_dir'] + config['run'] + "/prenetworks-brownfield/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{co2_budget_name}_{planning_horizons}.nc", + network=config['results_dir'] + config['run'] + "/prenetworks-brownfield/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{planning_horizons}.nc", costs=config['costs_dir'] + "costs_{planning_horizons}.csv", config=config['summary_dir'] + '/' + config['run'] + '/configs/config.yaml' - output: config['results_dir'] + config['run'] + "/postnetworks/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{co2_budget_name}_{planning_horizons}.nc" + output: config['results_dir'] + config['run'] + "/postnetworks/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{planning_horizons}.nc" shadow: "shallow" log: - solver=config['results_dir'] + config['run'] + "/logs/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{co2_budget_name}_{planning_horizons}_solver.log", - python=config['results_dir'] + config['run'] + "/logs/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{co2_budget_name}_{planning_horizons}_python.log", - memory=config['results_dir'] + config['run'] + "/logs/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{co2_budget_name}_{planning_horizons}_memory.log" - benchmark: config['results_dir'] + config['run'] + "/benchmarks/solve_network/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{co2_budget_name}_{planning_horizons}" + solver=config['results_dir'] + config['run'] + "/logs/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{planning_horizons}_solver.log", + python=config['results_dir'] + config['run'] + "/logs/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{planning_horizons}_python.log", + memory=config['results_dir'] + config['run'] + "/logs/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{planning_horizons}_memory.log" + benchmark: config['results_dir'] + config['run'] + "/benchmarks/solve_network/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{planning_horizons}" threads: 4 resources: mem_mb=config['solving']['mem'] script: "scripts/solve_network.py" diff --git a/config.default.yaml b/config.default.yaml index 7aa23901..cd3ceca9 100644 --- a/config.default.yaml +++ b/config.default.yaml @@ -25,7 +25,11 @@ scenario: # solarx or onwindx changes the available installable potential by factor x # dist{n} includes distribution grids with investment cost of n times cost in data/costs.csv planning_horizons : [2030] #investment years for myopic and perfect; or costs year for overnight - co2_budget_name: ['go'] #gives shape of CO2 budgets over planning horizon + +#CO2 budget as a fraction of 1990 emission +#this is over-ridden if CO2Lx is set in sector_opts +co2_budget: + 2030: 0 # snapshots are originally set in PyPSA-Eur/config.yaml but used again by PyPSA-Eur-Sec snapshots: @@ -166,7 +170,7 @@ solving: industry: 'St_primary_fraction' : 0.3 # fraction of steel produced via primary route (DRI + EAF) versus secondary route (EAF); today fraction is 0.6 - 'H2_DRI' : 1.7 #H2 consumption in Direct Reduced Iron (DRI), MWh_H2,LHV/ton_Steel from Vogl et al (2018) doi:10.1016/j.jclepro.2018.08.279 + 'H2_DRI' : 1.7 #H2 consumption in Direct Reduced Iron (DRI), MWh_H2,LHV/ton_Steel from 51kgH2/tSt in Vogl et al (2018) doi:10.1016/j.jclepro.2018.08.279 'elec_DRI' : 0.322 #electricity consumption in Direct Reduced Iron (DRI) shaft, MWh/tSt HYBRIT brochure https://ssabwebsitecdn.azureedge.net/-/media/hybrit/files/hybrit_brochure.pdf 'Al_primary_fraction' : 0.2 # fraction of aluminium produced via the primary route versus scrap; today fraction is 0.4 'MWh_CH4_per_tNH3_SMR' : 10.8 # 2012's demand from https://ec.europa.eu/docsroom/documents/4165/attachments/1/translations/en/renditions/pdf diff --git a/config.myopic.yaml b/config.myopic.yaml index c7f5ac1e..a9a1279e 100644 --- a/config.myopic.yaml +++ b/config.myopic.yaml @@ -25,7 +25,19 @@ scenario: # solarx or onwindx changes the available installable potential by factor x # dist{n} includes distribution grids with investment cost of n times cost in data/costs.csv planning_horizons : [2020, 2030, 2040, 2050] #investment years for myopic and perfect; or costs year for overnight - co2_budget_name: ['go'] #gives shape of CO2 budgets over planning horizon + + +#CO2 budget as a fraction of 1990 emission +#this is over-ridden if CO2Lx is set in sector_opts +co2_budget: + 2020: 0.7011648746 + 2025: 0.5241935484 + 2030: 0.2970430108 + 2035: 0.1500896057 + 2040: 0.0712365591 + 2045: 0.0322580645 + 2050: 0 + # snapshots are originally set in PyPSA-Eur/config.yaml but used again by PyPSA-Eur-Sec snapshots: @@ -166,7 +178,7 @@ solving: industry: 'St_primary_fraction' : 0.3 # fraction of steel produced via primary route (DRI + EAF) versus secondary route (EAF); today fraction is 0.6 - 'H2_DRI' : 1.7 #H2 consumption in Direct Reduced Iron (DRI), MWh_H2,LHV/ton_Steel from Vogl et al (2018) doi:10.1016/j.jclepro.2018.08.279 + 'H2_DRI' : 1.7 #H2 consumption in Direct Reduced Iron (DRI), MWh_H2,LHV/ton_Steel from 51kgH2/tSt in Vogl et al (2018) doi:10.1016/j.jclepro.2018.08.279 'elec_DRI' : 0.322 #electricity consumption in Direct Reduced Iron (DRI) shaft, MWh/tSt HYBRIT brochure https://ssabwebsitecdn.azureedge.net/-/media/hybrit/files/hybrit_brochure.pdf 'Al_primary_fraction' : 0.2 # fraction of aluminium produced via the primary route versus scrap; today fraction is 0.4 'MWh_CH4_per_tNH3_SMR' : 10.8 # 2012's demand from https://ec.europa.eu/docsroom/documents/4165/attachments/1/translations/en/renditions/pdf diff --git a/data/co2_budget.csv b/data/co2_budget.csv deleted file mode 100644 index 8e66a993..00000000 --- a/data/co2_budget.csv +++ /dev/null @@ -1,8 +0,0 @@ -,go,wait -2020,0.7011648746,0.7011648746 -2025,0.5241935484,0.6285842294 -2030,0.2970430108,0.3503584229 -2035,0.1500896057,0.0725806452 -2040,0.0712365591,0 -2045,0.0322580645,0 -2050,0,0 diff --git a/scripts/make_summary.py b/scripts/make_summary.py index e3e937a0..3ff79934 100644 --- a/scripts/make_summary.py +++ b/scripts/make_summary.py @@ -522,7 +522,7 @@ outputs = ["nodal_costs", def make_summaries(networks_dict): - columns = pd.MultiIndex.from_tuples(networks_dict.keys(),names=["cluster","lv","opt", "co2_budget_name","planning_horizon"]) + columns = pd.MultiIndex.from_tuples(networks_dict.keys(),names=["cluster","lv","opt","planning_horizon"]) df = {} @@ -571,21 +571,19 @@ if __name__ == "__main__": for item in outputs: snakemake.output[item] = snakemake.config['summary_dir'] + '/{name}/csvs/{item}.csv'.format(name=snakemake.config['run'],item=item) - networks_dict = {(cluster,lv,opt+sector_opt, co2_budget_name, planning_horizon) : - snakemake.config['results_dir'] + snakemake.config['run'] + '/postnetworks/elec_s{simpl}_{cluster}_lv{lv}_{opt}_{sector_opt}_{co2_budget_name}_{planning_horizon}.nc'\ + networks_dict = {(cluster, lv, opt+sector_opt, planning_horizon) : + snakemake.config['results_dir'] + snakemake.config['run'] + '/postnetworks/elec_s{simpl}_{cluster}_lv{lv}_{opt}_{sector_opt}_{planning_horizon}.nc'\ .format(simpl=simpl, cluster=cluster, opt=opt, lv=lv, sector_opt=sector_opt, - co2_budget_name=co2_budget_name, planning_horizon=planning_horizon)\ for simpl in snakemake.config['scenario']['simpl'] \ for cluster in snakemake.config['scenario']['clusters'] \ for opt in snakemake.config['scenario']['opts'] \ for sector_opt in snakemake.config['scenario']['sector_opts'] \ for lv in snakemake.config['scenario']['lv'] \ - for co2_budget_name in snakemake.config['scenario']['co2_budget_name'] \ for planning_horizon in snakemake.config['scenario']['planning_horizons']} print(networks_dict) diff --git a/scripts/plot_summary.py b/scripts/plot_summary.py index 79064690..9ecea108 100644 --- a/scripts/plot_summary.py +++ b/scripts/plot_summary.py @@ -256,7 +256,8 @@ if __name__ == "__main__": snakemake.input["balances"] = snakemake.config['summary_dir'] + '/test/csvs/supply_energy.csv' snakemake.output["balances"] = snakemake.config['summary_dir'] + '/test/graphs/balances-energy.csv' - n_header = 5 + n_header = 4 + plot_costs() plot_energy() diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index f6e8a7be..8e825292 100644 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -1747,11 +1747,9 @@ if __name__ == "__main__": snakemake = MockSnakemake( wildcards=dict(network='elec', simpl='', clusters='37', lv='1.0', opts='', planning_horizons='2020', - co2_budget_name='go', sector_opts='Co2L0-168H-T-H-B-I-solar3-dist1'), input=dict(network='pypsa-eur/networks/{network}_s{simpl}_{clusters}_ec_lv{lv}_{opts}.nc', timezone_mappings='pypsa-eur-sec/data/timezone_mappings.csv', - co2_budget='pypsa-eur-sec/data/co2_budget.csv', clustered_pop_layout='pypsa-eur-sec/resources/pop_layout_{network}_s{simpl}_{clusters}.csv', costs='technology-data/outputs/costs_{planning_horizons}.csv', profile_offwind_ac='pypsa-eur/resources/profile_offwind-ac.nc', @@ -1868,22 +1866,22 @@ if __name__ == "__main__": else: logger.info("No resampling") - - if snakemake.config["foresight"] == 'myopic': - co2_limits=pd.read_csv(snakemake.input.co2_budget, index_col=0) - year=snakemake.wildcards.planning_horizons[-4:] - limit=co2_limits.loc[int(year),snakemake.config["scenario"]["co2_budget_name"]] - add_co2limit(n, Nyears, limit) + #process CO2 limit + if type(snakemake.config["co2_budget"]) == dict: + year=int(snakemake.wildcards.planning_horizons[-4:]) + limit=snakemake.config["co2_budget"][year] else: - for o in opts: - if "Co2L" in o: - limit = o[o.find("Co2L")+4:] - print(o,limit) - if limit == "": - limit = snakemake.config['co2_reduction'] - else: - limit = float(limit.replace("p",".").replace("m","-")) - add_co2limit(n, Nyears, limit) + limit=snakemake.config["co2_budget"] + print("CO2 limit set to",limit) + + for o in opts: + if "Co2L" in o: + limit = o[o.find("Co2L")+4:] + limit = float(limit.replace("p",".").replace("m","-")) + print("overriding CO2 limit with scenario limit",limit) + + print("adding CO2 budget limit as per unit of 1990 levels of",limit) + add_co2limit(n, Nyears, limit) for o in opts: diff --git a/scripts/solve_network.py b/scripts/solve_network.py index a7078433..51086038 100644 --- a/scripts/solve_network.py +++ b/scripts/solve_network.py @@ -351,7 +351,7 @@ def solve_network(n, config=None, solver_log=None, opts=None): # fn = os.path.basename(snakemake.output[0]) # n.export_to_netcdf('/home/vres/data/jonas/playground/pypsa-eur/' + fn) - status, termination_condition = run_lopf(n, fix_ext_lines=True) + status, termination_condition = run_lopf(n, allow_warning_status=True, fix_ext_lines=True) # Drop zero lines from network # zero_lines_i = n.lines.index[(n.lines.s_nom_opt == 0.) & n.lines.s_nom_extendable]