diff --git a/Snakefile b/Snakefile old mode 100755 new mode 100644 index 2120230a..53f5dc58 --- a/Snakefile +++ b/Snakefile @@ -15,13 +15,13 @@ subworkflow pypsaeur: workdir: "../pypsa-eur" snakefile: "../pypsa-eur/Snakefile" configfile: "../pypsa-eur/config.yaml" - + rule all: - input: + input: config['summary_dir'] + '/' + config['run'] + '/graphs/costs.pdf' - + rule solve_all_elec_networks: input: expand(config['results_dir'] + config['run'] + "/postnetworks/elec_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}.nc", @@ -174,7 +174,7 @@ rule build_industrial_demand: threads: 1 resources: mem_mb=1000 script: 'scripts/build_industrial_demand.py' - + rule prepare_sector_network: input: @@ -185,7 +185,7 @@ rule prepare_sector_network: biomass_potentials='data/biomass_potentials.csv', timezone_mappings='data/timezone_mappings.csv', heat_profile="data/heat_load_profile_BDEW.csv", - costs="data/costs/costs_{planning_horizons}.csv", #"data/costs.csv" + costs=config['costs_dir'] + "costs_{planning_horizons}.csv", #"data/costs.csv" co2_budget="data/co2_budget.csv", clustered_pop_layout="resources/pop_layout_{network}_s{simpl}_{clusters}.csv", industrial_demand="resources/industrial_demand_{network}_s{simpl}_{clusters}.csv", @@ -265,7 +265,7 @@ rule make_summary: resources: mem_mb=10000 script: 'scripts/make_summary.py' - + rule plot_summary: input: @@ -282,13 +282,13 @@ rule plot_summary: 'scripts/plot_summary.py' 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", - costs="data/costs/costs_{planning_horizons}.csv", - - output: config['results_dir'] + config['run'] + "/postnetworks/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{co2_budget_name}_{planning_horizons}.nc" + costs=config['costs_dir'] + "costs_{planning_horizons}.csv", + + output: config['results_dir'] + config['run'] + "/postnetworks/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{co2_budget_name}_{planning_horizons}.nc" shadow: "shallow" log: solver="logs/" + config['run'] + "/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{co2_budget_name}_{planning_horizons}_solver.log", @@ -297,51 +297,51 @@ if config["foresight"] == "overnight": benchmark: "benchmarks/solve_network/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{co2_budget_name}_{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 + # group: "solve" # with group, threads is ignored https://bitbucket.org/snakemake/snakemake/issues/971/group-job-description-does-not-contain script: "scripts/solve_network.py" - - -if config["foresight"] == "myopic": + + +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', - costs="data/costs/costs_{}.csv".format(config['scenario']['planning_horizons'][0]), - cop_soil_total="resources/cop_soil_total_{network}_s{simpl}_{clusters}.nc", + 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}_{co2_budget_name}_{planning_horizons}.nc' wildcard_constraints: planning_horizons=config['scenario']['planning_horizons'][0] #only applies to baseyear threads: 1 - resources: mem_mb=2000 + resources: mem_mb=2000 script: "scripts/add_existing_baseyear.py" - + def process_input(wildcards): i = config["scenario"]["planning_horizons"].index(wildcards.planning_horizons) - return config['results_dir'] + config['run'] + "/postnetworks/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{co2_budget_name}_" + config["scenario"]["planning_horizons"][i-1] + ".nc" - - + return config['results_dir'] + config['run'] + "/postnetworks/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{co2_budget_name}_" + 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_p=process_input, #solved network at previous time step - costs="data/costs/costs_{planning_horizons}.csv", - cop_soil_total="resources/cop_soil_total_{network}_s{simpl}_{clusters}.nc", + 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}_{co2_budget_name}_{planning_horizons}.nc" threads: 4 - resources: mem_mb=2000 + resources: mem_mb=2000 script: "scripts/add_brownfield.py" - ruleorder: add_existing_baseyear > add_brownfield - + 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", - costs="data/costs/costs_{planning_horizons}.csv", - - output: config['results_dir'] + config['run'] + "/postnetworks/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{co2_budget_name}_{planning_horizons}.nc" + costs=config['costs_dir'] + "costs_{planning_horizons}.csv", + + output: config['results_dir'] + config['run'] + "/postnetworks/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{co2_budget_name}_{planning_horizons}.nc" shadow: "shallow" log: solver="logs/" + config['run'] + "/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{co2_budget_name}_{planning_horizons}_solver.log", @@ -351,7 +351,3 @@ if config["foresight"] == "myopic": threads: 4 resources: mem_mb=config['solving']['mem'] script: "scripts/solve_network.py" - - - - diff --git a/config.default.yaml b/config.default.yaml old mode 100755 new mode 100644 index 6d070675..b0c7b064 --- a/config.default.yaml +++ b/config.default.yaml @@ -2,6 +2,7 @@ logging_level: INFO results_dir: 'results/' summary_dir: results +costs_dir: '../technology_data/outputs/' run: 'your-run-name' # use this to keep track of runs with different settings foresight: 'myopic' #options are overnight, myopic, perfect @@ -21,8 +22,8 @@ scenario: # B for biomass supply, I for industry, shipping and aviation # 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'] #timesteps for myopic and perfect - co2_budget_name: ['go'] + planning_horizons : ['2020', '2030', '2040', '2050'] #timesteps for myopic and perfect + co2_budget_name: ['go'] # snapshots are originally set in PyPSA-Eur/config.yaml but used again by PyPSA-Eur-Sec snapshots: @@ -49,7 +50,7 @@ biomass: existing_capacities: grouping_years: ['1980', '1985', '1990', '1995', '2000','2005','2010','2015', '2019'] threshold_capacity: 10 - + sector: 'central' : True 'central_fraction' : 0.6 diff --git a/scripts/add_brownfield.py b/scripts/add_brownfield.py index 55195592..87ea6925 100755 --- a/scripts/add_brownfield.py +++ b/scripts/add_brownfield.py @@ -41,16 +41,16 @@ override_component_attrs["Generator"].loc["build_year"] = ["integer","year",np.n override_component_attrs["Generator"].loc["lifetime"] = ["float","years",np.nan,"build year","Input (optional)"] override_component_attrs["Store"].loc["build_year"] = ["integer","year",np.nan,"build year","Input (optional)"] override_component_attrs["Store"].loc["lifetime"] = ["float","years",np.nan,"build year","Input (optional)"] - + def add_brownfield(n, n_p, year): print("adding brownfield") - + #first, remove generators, links and stores that track CO2 or global EU values - n_p.mremove("Generator", [index for index in n_p.generators.index.to_list() if 'ror' in index]) + n_p.mremove("Generator", [index for index in n_p.generators.index.to_list() if 'ror' in index]) n_p.mremove("Generator", ['EU fossil gas', 'fossil oil'] ) n_p.mremove("Store", ['co2 atmosphere', 'co2 stored', 'EU gas Store'] ) n_p.mremove("Link", ['co2 vent'] ) - + if "H" in opts: n_p.mremove("Link", [index for index in n_p.links.index.to_list() if 'water tanks charger' in index]) n_p.mremove("Link", [index for index in n_p.links.index.to_list() if 'water tanks discharger' in index]) @@ -64,31 +64,31 @@ def add_brownfield(n, n_p, year): n_p.mremove("Store", [index for index in n_p.stores.index.to_list() if 'battery storage' in index]) n_p.mremove("Link", [index for index in n_p.links.index.to_list() if 'BEV charger' in index]) n_p.mremove("Link", [index for index in n_p.links.index.to_list() if 'V2G' in index]) - + previous_timestep=snakemake.config['scenario']['planning_horizons'][snakemake.config['scenario']['planning_horizons'].index(year)-1] previous_timesteps=snakemake.config['scenario']['planning_horizons'][0:snakemake.config['scenario']['planning_horizons'].index(year)] grouping_years=snakemake.config['existing_capacities']['grouping_years'] - - + + ### GENERATORS ### # generators whose build_year + lifetime < year are removed - n_p.mremove("Generator", [index for index in n_p.generators.index.to_list() + n_p.mremove("Generator", [index for index in n_p.generators.index.to_list() if (n_p.generators.loc[index, 'build_year']+n_p.generators.loc[index, 'lifetime'] < int(year))]) - + # remove generators if their optimized nominal capacity is lower than a threshold - n_p.mremove("Generator", [index for index in n_p.generators.index.to_list() + n_p.mremove("Generator", [index for index in n_p.generators.index.to_list() if (n_p.generators.loc[index, 'p_nom_opt'] < snakemake.config['existing_capacities']['threshold_capacity'])]) - + # generators whose capacity was optimized in the previous year are renamed and build year is added n_p.generators.index=np.where(n_p.generators.index.str[-4:].isin(previous_timesteps+grouping_years)==False, - n_p.generators.index + '-' + previous_timestep, + n_p.generators.index + '-' + previous_timestep, n_p.generators.index) n_p.generators.loc[[index for index in n_p.generators.index.to_list() if previous_timestep in index], 'build_year']=int(previous_timestep) - - #add generators from previous step - n.madd("Generator", + + #add generators from previous step + n.madd("Generator", n_p.generators.index, bus=n_p.generators.bus, carrier=n_p.generators.carrier, @@ -99,24 +99,24 @@ def add_brownfield(n, n_p, year): p_max_pu=n_p.generators_t.p_max_pu, build_year=n_p.generators.build_year, lifetime=n_p.generators.lifetime) - + ### STORES ### # stores whose installationYear + lifetime < year are removed - n_p.mremove("Store", [index for index in n_p.stores.index.to_list() - if (n_p.stores.loc[index, 'build_year']+n_p.stores.loc[index, 'lifetime'] < int(year))]) - + n_p.mremove("Store", [index for index in n_p.stores.index.to_list() + if (n_p.stores.loc[index, 'build_year']+n_p.stores.loc[index, 'lifetime'] < int(year))]) + # remove stores if their optimized nominal capacity is lower than a threshold - n_p.mremove("Store", [index for index in n_p.stores.index.to_list() + n_p.mremove("Store", [index for index in n_p.stores.index.to_list() if (n_p.stores.loc[index, 'e_nom_opt'] < snakemake.config['existing_capacities']['threshold_capacity'])]) - + # stores whose capacity was optimized in the previous year are renamed and the build year is added n_p.stores.index=np.where(n_p.stores.index.str[-4:].isin(previous_timesteps+grouping_years)==False, - n_p.stores.index + '-' + previous_timestep, + n_p.stores.index + '-' + previous_timestep, n_p.stores.index) n_p.stores.loc[[index for index in n_p.stores.index.to_list() if previous_timestep in index], 'build_year']=int(previous_timestep) #add stores from previous steps - n.madd("Store", + n.madd("Store", n_p.stores.index, bus=n_p.stores.bus, carrier=n_p.stores.carrier, @@ -125,27 +125,27 @@ def add_brownfield(n, n_p, year): capital_cost=n_p.stores.capital_cost, build_year=n_p.stores.build_year, lifetime=n_p.stores.lifetime) - - ### LINKS ### - # TODO: add_chp_constraint() in solve_network needs to be adjusted + + ### LINKS ### + # TODO: add_chp_constraint() in solve_network needs to be adjusted n_p.mremove("Link", [index for index in n_p.links.index.to_list() if 'CHP' in index]) - + # links whose installationYear + lifetime < year are removed - n_p.mremove("Link", [index for index in n_p.links.index.to_list() - if (n_p.links.loc[index, 'build_year']+n_p.links.loc[index, 'lifetime'] < int(year))]) - + n_p.mremove("Link", [index for index in n_p.links.index.to_list() + if (n_p.links.loc[index, 'build_year']+n_p.links.loc[index, 'lifetime'] < int(year))]) + # delete links if their optimized nominal capacity is lower than a threshold - n_p.mremove("Link", [index for index in n_p.links.index.to_list() + n_p.mremove("Link", [index for index in n_p.links.index.to_list() if (n_p.links.loc[index, 'p_nom_opt'] < snakemake.config['existing_capacities']['threshold_capacity'])]) - + # links whose capacity was optimized in the previous year are renamed and the build year is added n_p.links.index=np.where(n_p.links.index.str[-4:].isin(previous_timesteps+grouping_years)==False, - n_p.links.index + '-' + previous_timestep, + n_p.links.index + '-' + previous_timestep, n_p.links.index) n_p.links.loc[[index for index in n_p.links.index.to_list() if previous_timestep in index], 'build_year']=int(previous_timestep) #add links from previous steps - n.madd("Link", + n.madd("Link", n_p.links.index, bus0=n_p.links.bus0, bus1=n_p.links.bus1, @@ -158,23 +158,23 @@ def add_brownfield(n, n_p, year): efficiency2=n_p.links.efficiency2, build_year=n_p.links.build_year, lifetime=n_p.links.lifetime) - - + + if __name__ == "__main__": # Detect running outside of snakemake and mock snakemake for testing if 'snakemake' not in globals(): from vresutils.snakemake import MockSnakemake snakemake = MockSnakemake( - wildcards=dict(network='elec', simpl='', clusters='37', lv='1.0', + wildcards=dict(network='elec', simpl='', clusters='37', lv='1.0', sector_opts='Co2L0-168H-T-H-B-I-solar3-dist1', co2_budget_name='go', planning_horizons='2030'), - input=dict(network='pypsa-eur-sec/results/test/prenetworks/{network}_s{simpl}_{clusters}_lv{lv}__{sector_opts}_{co2_budget_name}_{planning_horizons}.nc', - network_p='pypsa-eur-sec/results/test/postnetworks/{network}_s{simpl}_{clusters}_lv{lv}__{sector_opts}_{co2_budget_name}_2020.nc', + input=dict(network='pypsa-eur-sec/results/test/prenetworks/{network}_s{simpl}_{clusters}_lv{lv}__{sector_opts}_{co2_budget_name}_{planning_horizons}.nc', + network_p='pypsa-eur-sec/results/test/postnetworks/{network}_s{simpl}_{clusters}_lv{lv}__{sector_opts}_{co2_budget_name}_2020.nc', costs='pypsa-eur-sec/data/costs/costs_{planning_horizons}.csv', cop_air_total="pypsa-eur-sec/resources/cop_air_total_{network}_s{simpl}_{clusters}.nc", - cop_soil_total="pypsa-eur-sec/resources/cop_soil_total_{network}_s{simpl}_{clusters}.nc"), - output=['pypsa-eur-sec/results/test/prenetworks_brownfied/{network}_s{simpl}_{clusters}_lv{lv}__{sector_opts}_{planning_horizons}.nc'] + cop_soil_total="pypsa-eur-sec/resources/cop_soil_total_{network}_s{simpl}_{clusters}.nc"), + output=['pypsa-eur-sec/results/test/prenetworks_brownfield/{network}_s{simpl}_{clusters}_lv{lv}__{sector_opts}_{planning_horizons}.nc'] ) import yaml with open('config.yaml') as f: @@ -185,28 +185,25 @@ if __name__ == "__main__": options = snakemake.config["sector"] opts = snakemake.wildcards.sector_opts.split('-') - + year=snakemake.wildcards.planning_horizons n = pypsa.Network(snakemake.input.network, override_component_attrs=override_component_attrs) - + n_p = pypsa.Network(snakemake.input.network_p, override_component_attrs=override_component_attrs) #%% add_brownfield(n, n_p, year) - + Nyears = n.snapshot_weightings.sum()/8760. - + costs = prepare_costs(snakemake.input.costs, snakemake.config['costs']['USD2013_to_EUR2013'], snakemake.config['costs']['discountrate'], Nyears) baseyear = snakemake.config['scenario']["planning_horizons"][0] - - + + n.export_to_netcdf(snakemake.output[0]) - - -