From 37f36047ca778582814ea80a7890b665d32b4585 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Wed, 26 Aug 2020 12:06:01 +0200 Subject: [PATCH 01/19] Industry demand: Also record material production per industry --- Snakefile | 5 +-- scripts/build_industrial_demand.py | 2 +- .../build_industrial_demand_per_country.py | 33 ++++++++++++++----- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/Snakefile b/Snakefile index 54b8c241..5009b872 100644 --- a/Snakefile +++ b/Snakefile @@ -159,7 +159,8 @@ rule build_industrial_demand_per_country: input: industry_sector_ratios="resources/industry_sector_ratios.csv" output: - industrial_demand_per_country="resources/industrial_demand_per_country.csv" + industrial_demand_per_country="resources/industrial_demand_per_country.csv", + industrial_energy_demand_per_country="resources/industrial_energy_demand_per_country.csv" threads: 1 resources: mem_mb=1000 script: 'scripts/build_industrial_demand_per_country.py' @@ -168,7 +169,7 @@ rule build_industrial_demand_per_country: rule build_industrial_demand: input: clustered_pop_layout="resources/pop_layout_{network}_s{simpl}_{clusters}.csv", - industrial_demand_per_country="resources/industrial_demand_per_country.csv" + industrial_demand_per_country="resources/industrial_energy_demand_per_country.csv" output: industrial_demand="resources/industrial_demand_{network}_s{simpl}_{clusters}.csv" threads: 1 diff --git a/scripts/build_industrial_demand.py b/scripts/build_industrial_demand.py index fce7b6ad..3bc56254 100644 --- a/scripts/build_industrial_demand.py +++ b/scripts/build_industrial_demand.py @@ -7,7 +7,7 @@ def build_industrial_demand(): pop_layout = pd.read_csv(snakemake.input.clustered_pop_layout,index_col=0) pop_layout["ct"] = pop_layout.index.str[:2] ct_total = pop_layout.total.groupby(pop_layout["ct"]).sum() - pop_layout["ct_total"] = pop_layout["ct"].map(ct_total.get) + pop_layout["ct_total"] = pop_layout["ct"].map(ct_total) pop_layout["fraction"] = pop_layout["total"]/pop_layout["ct_total"] industrial_demand_per_country = pd.read_csv(snakemake.input.industrial_demand_per_country,index_col=0) diff --git a/scripts/build_industrial_demand_per_country.py b/scripts/build_industrial_demand_per_country.py index 7d7d0ef3..ee4f817d 100644 --- a/scripts/build_industrial_demand_per_country.py +++ b/scripts/build_industrial_demand_per_country.py @@ -16,7 +16,7 @@ tj_to_ktoe = 0.0238845 ktoe_to_twh = 0.01163 # import EU ratios df as csv -df=pd.read_csv('resources/industry_sector_ratios.csv', sep=';', index_col=0) +df=pd.read_csv(snakemake.input.industry_sector_ratios, sep=';', index_col=0) @@ -36,9 +36,6 @@ sub_sheet_name_dict = { 'Iron and steel':'ISI', index = ['elec','biomass','methane','hydrogen','heat','naphtha','process emission','process emission from feedstock'] -countries_df = pd.DataFrame(columns=index) #data frame final energy consumption per country and source - - non_EU = ['NO', 'CH', 'ME', 'MK', 'RS', 'BA', 'AL'] rename = {"GR" : "EL", @@ -49,7 +46,7 @@ eu28 = ['FR', 'DE', 'GB', 'IT', 'ES', 'PL', 'SE', 'NL', 'BE', 'FI', 'CZ', 'HU', 'IE', 'SK', 'LT', 'HR', 'LU', 'SI'] + ['CY','MT'] -countries = non_EU + [rename.get(eu,eu) for eu in eu28[:-2]] +countries = non_EU + [rename.get(eu,eu) for eu in eu28] sectors = ['Iron and steel','Chemicals Industry','Non-metallic mineral products', @@ -69,6 +66,20 @@ sect2sub = {'Iron and steel':['Electric arc','Integrated steelworks'], 'Wood and wood products' :['Wood and wood products'], 'Other Industrial Sectors':['Other Industrial Sectors']} +subsectors = [ss for s in sectors for ss in sect2sub[s]] + +#final energy consumption per country and industry (TWh/a) +countries_df = pd.DataFrame(index=countries, + columns=index, + dtype=float) + +#material demand per country and industry (kton/a) +countries_demand = pd.DataFrame(index=countries, + columns=subsectors, + dtype=float) + + + out_dic ={'Electric arc': 'Electric arc', 'Integrated steelworks': 'Integrated steelworks', 'Basic chemicals': 'Basic chemicals (kt ethylene eq.)', @@ -153,8 +164,9 @@ dic_Switzerland ={'Iron and steel': 7889., dic_sec_position={} for country in countries: - countries_df.loc[country] = 0 - print (country) + countries_df.loc[country] = 0. + countries_demand.loc[country] = 0. + print(country) for sector in sectors: if country in non_EU: if country == 'CH': @@ -181,7 +193,7 @@ for country in countries: for subsector in sect2sub[sector]: output = ratio_country_EU28*s_out[out_dic[subsector]] - + countries_demand.loc[country,subsector] = output for ind in index: countries_df.loc[country, ind] += float(output*df.loc[ind, subsector]) # kton * MWh = GWh (# kton * tCO2 = ktCO2) @@ -194,6 +206,7 @@ for country in countries: for subsector in sect2sub[sector]: output = s_out[out_dic[subsector]] + countries_demand.loc[country,subsector] = output for ind in index: countries_df.loc[country, ind] += output*df.loc[ind, subsector] #kton * MWh = GWh (# kton * tCO2 = ktCO2) @@ -234,5 +247,7 @@ rename_sectors = {'elec':'electricity', countries_df.rename(columns=rename_sectors,inplace=True) -countries_df.to_csv('resources/industrial_demand_per_country.csv', +countries_df.to_csv(snakemake.output.industrial_energy_demand_per_country, float_format='%.2f') +countries_demand.to_csv(snakemake.output.industrial_demand_per_country, + float_format='%.2f') From 851142fe0f3608bc0d438cfa06c88e6ab9168386 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Wed, 26 Aug 2020 13:12:16 +0200 Subject: [PATCH 02/19] Separate script for industrial production per ct from energy demand --- Snakefile | 18 +++- ...ld_industrial_energy_demand_per_country.py | 84 +++++++++++++++ ...uild_industrial_production_per_country.py} | 100 ++++-------------- scripts/build_industry_sector_ratios.py | 1 + 4 files changed, 117 insertions(+), 86 deletions(-) create mode 100644 scripts/build_industrial_energy_demand_per_country.py rename scripts/{build_industrial_demand_per_country.py => build_industrial_production_per_country.py} (72%) diff --git a/Snakefile b/Snakefile index 5009b872..b921bf11 100644 --- a/Snakefile +++ b/Snakefile @@ -155,15 +155,23 @@ rule build_industry_sector_ratios: script: 'scripts/build_industry_sector_ratios.py' -rule build_industrial_demand_per_country: - input: - industry_sector_ratios="resources/industry_sector_ratios.csv" +rule build_industrial_production_per_country: + output: + industrial_production_per_country="resources/industrial_production_per_country.csv" + threads: 1 + resources: mem_mb=1000 + script: 'scripts/build_industrial_production_per_country.py' + + +rule build_industrial_energy_demand_per_country: + input: + industry_sector_ratios="resources/industry_sector_ratios.csv", + industrial_production_per_country="resources/industrial_production_per_country.csv" output: - industrial_demand_per_country="resources/industrial_demand_per_country.csv", industrial_energy_demand_per_country="resources/industrial_energy_demand_per_country.csv" threads: 1 resources: mem_mb=1000 - script: 'scripts/build_industrial_demand_per_country.py' + script: 'scripts/build_industrial_energy_demand_per_country.py' rule build_industrial_demand: diff --git a/scripts/build_industrial_energy_demand_per_country.py b/scripts/build_industrial_energy_demand_per_country.py new file mode 100644 index 00000000..750d4eda --- /dev/null +++ b/scripts/build_industrial_energy_demand_per_country.py @@ -0,0 +1,84 @@ + +import pandas as pd +import numpy as np + + +tj_to_ktoe = 0.0238845 +ktoe_to_twh = 0.01163 + +eb_base_dir = "data/eurostat-energy_balances-may_2018_edition" +jrc_base_dir = "data/jrc-idees-2015" + +# import EU ratios df as csv +industry_sector_ratios=pd.read_csv(snakemake.input.industry_sector_ratios, sep=';', index_col=0) + +#material demand per country and industry (kton/a) +countries_production = pd.read_csv(snakemake.input.industrial_production_per_country, index_col=0) + +#Annual energy consumption in Switzerland by sector in 2015 (in TJ) +#From: Energieverbrauch in der Industrie und im Dienstleistungssektor, Der Bundesrat +#http://www.bfe.admin.ch/themen/00526/00541/00543/index.html?lang=de&dossier_id=00775 + +dic_Switzerland ={'Iron and steel': 7889., + 'Chemicals Industry': 26871., + 'Non-metallic mineral products': 15513.+3820., + 'Pulp, paper and printing': 12004., + 'Food, beverages and tobacco': 17728., + 'Non Ferrous Metals': 3037., + 'Transport Equipment': 14993., + 'Machinery Equipment': 4724., + 'Textiles and leather': 1742., + 'Wood and wood products': 0., + 'Other Industrial Sectors': 10825., + 'current electricity': 53760.} + + +eb_names={'NO':'Norway', 'AL':'Albania', 'BA':'Bosnia and Herzegovina', + 'MK':'FYR of Macedonia', 'GE':'Georgia', 'IS':'Iceland', + 'KO':'Kosovo', 'MD':'Moldova', 'ME':'Montenegro', 'RS':'Serbia', + 'UA':'Ukraine', 'TR':'Turkey', } + +jrc_names = {"GR" : "EL", + "GB" : "UK"} + +#final energy consumption per country and industry (TWh/a) +countries_df = countries_production.dot(industry_sector_ratios.T) +countries_df*= 0.001 #GWh -> TWh (ktCO2 -> MtCO2) + + + +non_EU = ['NO', 'CH', 'ME', 'MK', 'RS', 'BA', 'AL'] + + +# save current electricity consumption +for country in countries_df.index: + if country in non_EU: + if country == 'CH': + countries_df.loc[country, 'current electricity']=dic_Switzerland['current electricity']*tj_to_ktoe*ktoe_to_twh + else: + excel_balances = pd.read_excel('{}/{}.XLSX'.format(eb_base_dir,eb_names[country]), + sheet_name='2016', index_col=1,header=0, skiprows=1 ,squeeze=True) + + countries_df.loc[country, 'current electricity'] = excel_balances.loc['Industry', 'Electricity']*ktoe_to_twh + + else: + + excel_out = pd.read_excel('{}/JRC-IDEES-2015_Industry_{}.xlsx'.format(jrc_base_dir,jrc_names.get(country,country)), + sheet_name='Ind_Summary',index_col=0,header=0,squeeze=True) # the summary sheet + + s_out = excel_out.iloc[27:48,-1] + countries_df.loc[country, 'current electricity'] = s_out['Electricity']*ktoe_to_twh + print(countries_df.loc[country, 'current electricity']) + + + +rename_sectors = {'elec':'electricity', + 'biomass':'solid biomass', + 'heat':'low-temperature heat'} + +countries_df.rename(columns=rename_sectors,inplace=True) + +countries_df.index.name = "TWh/a (MtCO2/a)" + +countries_df.to_csv(snakemake.output.industrial_energy_demand_per_country, + float_format='%.2f') diff --git a/scripts/build_industrial_demand_per_country.py b/scripts/build_industrial_production_per_country.py similarity index 72% rename from scripts/build_industrial_demand_per_country.py rename to scripts/build_industrial_production_per_country.py index ee4f817d..4c4de875 100644 --- a/scripts/build_industrial_demand_per_country.py +++ b/scripts/build_industrial_production_per_country.py @@ -1,27 +1,14 @@ - -#%matplotlib inline import pandas as pd import numpy as np +tj_to_ktoe = 0.0238845 +ktoe_to_twh = 0.01163 jrc_base_dir = "data/jrc-idees-2015" eb_base_dir = "data/eurostat-energy_balances-may_2018_edition" - - -tj_to_ktoe = 0.0238845 - -ktoe_to_twh = 0.01163 - -# import EU ratios df as csv -df=pd.read_csv(snakemake.input.industry_sector_ratios, sep=';', index_col=0) - - - - - sub_sheet_name_dict = { 'Iron and steel':'ISI', 'Chemicals Industry':'CHI', 'Non-metallic mineral products': 'NMM', @@ -38,15 +25,15 @@ index = ['elec','biomass','methane','hydrogen','heat','naphtha','process emissio non_EU = ['NO', 'CH', 'ME', 'MK', 'RS', 'BA', 'AL'] -rename = {"GR" : "EL", - "GB" : "UK"} +jrc_names = {"GR" : "EL", + "GB" : "UK"} -eu28 = ['FR', 'DE', 'GB', 'IT', 'ES', 'PL', 'SE', 'NL', 'BE', 'FI', 'CZ', - 'DK', 'PT', 'RO', 'AT', 'BG', 'EE', 'GR', 'LV', - 'HU', 'IE', 'SK', 'LT', 'HR', 'LU', 'SI'] + ['CY','MT'] +eu28 = ['FR', 'DE', 'GB', 'IT', 'ES', 'PL', 'SE', 'NL', 'BE', 'FI', + 'DK', 'PT', 'RO', 'AT', 'BG', 'EE', 'GR', 'LV', 'CZ', + 'HU', 'IE', 'SK', 'LT', 'HR', 'LU', 'SI', 'CY', 'MT'] -countries = non_EU + [rename.get(eu,eu) for eu in eu28] +countries = non_EU + eu28 sectors = ['Iron and steel','Chemicals Industry','Non-metallic mineral products', @@ -68,18 +55,12 @@ sect2sub = {'Iron and steel':['Electric arc','Integrated steelworks'], subsectors = [ss for s in sectors for ss in sect2sub[s]] -#final energy consumption per country and industry (TWh/a) -countries_df = pd.DataFrame(index=countries, - columns=index, - dtype=float) - #material demand per country and industry (kton/a) countries_demand = pd.DataFrame(index=countries, columns=subsectors, dtype=float) - out_dic ={'Electric arc': 'Electric arc', 'Integrated steelworks': 'Integrated steelworks', 'Basic chemicals': 'Basic chemicals (kt ethylene eq.)', @@ -128,10 +109,11 @@ dic_sec_summary = {'Iron and steel': 'Iron and steel', 'Other Industrial Sectors': ' Other Industrial Sectors'} #countries=['CH'] -dic_countries={'NO':'Norway', 'AL':'Albania', 'BA':'Bosnia and Herzegovina', - 'MK':'FYR of Macedonia', 'GE':'Georgia', 'IS':'Iceland', - 'KO':'Kosovo', 'MD':'Moldova', 'ME':'Montenegro', 'RS':'Serbia', - 'UA':'Ukraine', 'TR':'Turkey', } +eb_names={'NO':'Norway', 'AL':'Albania', 'BA':'Bosnia and Herzegovina', + 'MK':'FYR of Macedonia', 'GE':'Georgia', 'IS':'Iceland', + 'KO':'Kosovo', 'MD':'Moldova', 'ME':'Montenegro', 'RS':'Serbia', + 'UA':'Ukraine', 'TR':'Turkey', } + dic_sec ={'Iron and steel':'Iron & steel industry', 'Chemicals Industry': 'Chemical and Petrochemical industry', 'Non-metallic mineral products': 'Non-ferrous metal industry', @@ -164,7 +146,6 @@ dic_Switzerland ={'Iron and steel': 7889., dic_sec_position={} for country in countries: - countries_df.loc[country] = 0. countries_demand.loc[country] = 0. print(country) for sector in sectors: @@ -174,7 +155,7 @@ for country in countries: else: # estimate physical output #energy consumption in the sector and country - excel_balances = pd.read_excel('{}/{}.XLSX'.format(eb_base_dir,dic_countries[country]), + excel_balances = pd.read_excel('{}/{}.XLSX'.format(eb_base_dir,eb_names[country]), sheet_name='2016', index_col=2,header=0, skiprows=1 ,squeeze=True) e_country = excel_balances.loc[dic_sec[sector], 'Total all products'] @@ -192,62 +173,19 @@ for country in countries: s_out = excel_out.iloc[loc_dic[sector][0]:loc_dic[sector][1],-1] for subsector in sect2sub[sector]: - output = ratio_country_EU28*s_out[out_dic[subsector]] - countries_demand.loc[country,subsector] = output - for ind in index: - countries_df.loc[country, ind] += float(output*df.loc[ind, subsector]) # kton * MWh = GWh (# kton * tCO2 = ktCO2) + countries_demand.loc[country,subsector] = ratio_country_EU28*s_out[out_dic[subsector]] else: # read the input sheets - excel_out = pd.read_excel('{}/JRC-IDEES-2015_Industry_{}.xlsx'.format(jrc_base_dir,country), sheet_name=sub_sheet_name_dict[sector],index_col=0,header=0,squeeze=True) # the summary sheet + excel_out = pd.read_excel('{}/JRC-IDEES-2015_Industry_{}.xlsx'.format(jrc_base_dir,jrc_names.get(country,country)), sheet_name=sub_sheet_name_dict[sector],index_col=0,header=0,squeeze=True) # the summary sheet s_out = excel_out.iloc[loc_dic[sector][0]:loc_dic[sector][1],-1] for subsector in sect2sub[sector]: - output = s_out[out_dic[subsector]] - countries_demand.loc[country,subsector] = output - for ind in index: - countries_df.loc[country, ind] += output*df.loc[ind, subsector] #kton * MWh = GWh (# kton * tCO2 = ktCO2) + countries_demand.loc[country,subsector] = s_out[out_dic[subsector]] -countries_df*= 0.001 #GWh -> TWh (ktCO2 -> MtCO2) +countries_demand.index.name = "kton/a" -# save current electricity consumption -for country in countries: - if country in non_EU: - if country == 'CH': - countries_df.loc[country, 'current electricity']=dic_Switzerland['current electricity']*tj_to_ktoe*ktoe_to_twh - else: - excel_balances = pd.read_excel('{}/{}.XLSX'.format(eb_base_dir,dic_countries[country]), - sheet_name='2016', index_col=1,header=0, skiprows=1 ,squeeze=True) - - countries_df.loc[country, 'current electricity'] = excel_balances.loc['Industry', 'Electricity']*ktoe_to_twh - - else: - - excel_out = pd.read_excel('{}/JRC-IDEES-2015_Industry_{}.xlsx'.format(jrc_base_dir,country), - sheet_name='Ind_Summary',index_col=0,header=0,squeeze=True) # the summary sheet - - s_out = excel_out.iloc[27:48,-1] - countries_df.loc[country, 'current electricity'] = s_out['Electricity']*ktoe_to_twh - print(countries_df.loc[country, 'current electricity']) - - - -# save df as csv -for ind in index: - countries_df[ind]=countries_df[ind].astype('float') -countries_df = countries_df.round(3) - -countries_df.rename(index={value : key for key,value in rename.items()},inplace=True) - -rename_sectors = {'elec':'electricity', - 'biomass':'solid biomass', - 'heat':'low-temperature heat'} - -countries_df.rename(columns=rename_sectors,inplace=True) - -countries_df.to_csv(snakemake.output.industrial_energy_demand_per_country, - float_format='%.2f') -countries_demand.to_csv(snakemake.output.industrial_demand_per_country, +countries_demand.to_csv(snakemake.output.industrial_production_per_country, float_format='%.2f') diff --git a/scripts/build_industry_sector_ratios.py b/scripts/build_industry_sector_ratios.py index 5d3acb9c..b608a91e 100644 --- a/scripts/build_industry_sector_ratios.py +++ b/scripts/build_industry_sector_ratios.py @@ -1372,4 +1372,5 @@ sources=['elec','biomass', 'methane', 'hydrogen', 'heat','naphtha'] df.loc[sources,sector] = df.loc[sources,sector]*conv_factor/s_out['Physical output (index)'] # unit MWh/t material +df.index.name = "MWh/tMaterial" df.to_csv('resources/industry_sector_ratios.csv', sep=';') From b761281b3d4849ba3e5f371dbf171746fdfa816c Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Wed, 26 Aug 2020 16:13:01 +0200 Subject: [PATCH 03/19] Move changes to primary/secondary routes for Al/St to new script This was handled before in industry_sector_ratios.csv which was confusing. Now industry_sector_ratios.csv represents the genuine energy consumption per tonne of material for each industrial route (MWh/tMaterial). An new file is created with ktMaterial/a in industrial_production_per_country_tomorrow.csv which contains changes to the fraction of primary/secondary routes compared to today's production in industrial_production_per_country.csv. This is less confusing I think. --- Snakefile | 12 +++++++++- config.default.yaml | 6 ++--- config.myopic.yaml | 6 ++--- ...ld_industrial_energy_demand_per_country.py | 2 -- ...ustrial_production_per_country_tomorrow.py | 22 +++++++++++++++++++ scripts/build_industry_sector_ratios.py | 22 ++++++++----------- 6 files changed, 48 insertions(+), 22 deletions(-) create mode 100644 scripts/build_industrial_production_per_country_tomorrow.py diff --git a/Snakefile b/Snakefile index b921bf11..7558a5ac 100644 --- a/Snakefile +++ b/Snakefile @@ -163,10 +163,20 @@ rule build_industrial_production_per_country: script: 'scripts/build_industrial_production_per_country.py' +rule build_industrial_production_per_country_tomorrow: + input: + industrial_production_per_country="resources/industrial_production_per_country.csv" + output: + industrial_production_per_country_tomorrow="resources/industrial_production_per_country_tomorrow.csv" + threads: 1 + resources: mem_mb=1000 + script: 'scripts/build_industrial_production_per_country_tomorrow.py' + + rule build_industrial_energy_demand_per_country: input: industry_sector_ratios="resources/industry_sector_ratios.csv", - industrial_production_per_country="resources/industrial_production_per_country.csv" + industrial_production_per_country="resources/industrial_production_per_country_tomorrow.csv" output: industrial_energy_demand_per_country="resources/industrial_energy_demand_per_country.csv" threads: 1 diff --git a/config.default.yaml b/config.default.yaml index 575d6c78..55380f63 100644 --- a/config.default.yaml +++ b/config.default.yaml @@ -158,9 +158,9 @@ solving: mem: 30000 #memory in MB; 20 GB enough for 50+B+I+H2; 100 GB for 181+B+I+H2 industry: - 'DRI_ratio' : 0.5 #ratio of today's blast-furnace steel (60% primary route, 40% secondary) to future assumption (30% primary, 70% secondary), transformed into DRI + electric arc - 'H2_DRI' : 1.7 #H2 consumption in Direct Reduced Iron (DRI), MWh_H2/ton_Steel from Vogl et al (2018) doi:10.1016/j.jclepro.2018.08.279 - 'Al_to_scrap' : 0.5 # ratio of primary-route Aluminum transformed into scrap (today 40% to future 20% primary route) + '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 + 'Al_primary_fraction' : 0.2 # fraction of aluminium produced via the primary route versus scrap; today fraction is 0.4 'H2_for_NH3' : 85000 # H2 in GWh/a for 17 MtNH3/a transformed from SMR to electrolyzed-H2, following Lechtenböhmer(2016) 'NH3_process_emissions' : 24.5 # in MtCO2/a from SMR for H2 production for NH3 from UNFCCC for 2015 for EU28 'petrochemical_process_emissions' : 25.5 # in MtCO2/a for petrochemical and other from UNFCCC for 2015 for EU28 diff --git a/config.myopic.yaml b/config.myopic.yaml index 48f6cb62..24213b44 100644 --- a/config.myopic.yaml +++ b/config.myopic.yaml @@ -158,9 +158,9 @@ solving: mem: 30000 #memory in MB; 20 GB enough for 50+B+I+H2; 100 GB for 181+B+I+H2 industry: - 'DRI_ratio' : 0.5 #ratio of today's blast-furnace steel (60% primary route, 40% secondary) to future assumption (30% primary, 70% secondary), transformed into DRI + electric arc - 'H2_DRI' : 1.7 #H2 consumption in Direct Reduced Iron (DRI), MWh_H2/ton_Steel from Vogl et al (2018) doi:10.1016/j.jclepro.2018.08.279 - 'Al_to_scrap' : 0.5 # ratio of primary-route Aluminum transformed into scrap (today 40% to future 20% primary route) + '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 + 'Al_primary_fraction' : 0.2 # fraction of aluminium produced via the primary route versus scrap; today fraction is 0.4 'H2_for_NH3' : 85000 # H2 in GWh/a for 17 MtNH3/a transformed from SMR to electrolyzed-H2, following Lechtenböhmer(2016) 'NH3_process_emissions' : 24.5 # in MtCO2/a from SMR for H2 production for NH3 from UNFCCC for 2015 for EU28 'petrochemical_process_emissions' : 25.5 # in MtCO2/a for petrochemical and other from UNFCCC for 2015 for EU28 diff --git a/scripts/build_industrial_energy_demand_per_country.py b/scripts/build_industrial_energy_demand_per_country.py index 750d4eda..73036a99 100644 --- a/scripts/build_industrial_energy_demand_per_country.py +++ b/scripts/build_industrial_energy_demand_per_country.py @@ -68,8 +68,6 @@ for country in countries_df.index: s_out = excel_out.iloc[27:48,-1] countries_df.loc[country, 'current electricity'] = s_out['Electricity']*ktoe_to_twh - print(countries_df.loc[country, 'current electricity']) - rename_sectors = {'elec':'electricity', diff --git a/scripts/build_industrial_production_per_country_tomorrow.py b/scripts/build_industrial_production_per_country_tomorrow.py new file mode 100644 index 00000000..c1d5d5da --- /dev/null +++ b/scripts/build_industrial_production_per_country_tomorrow.py @@ -0,0 +1,22 @@ + +import pandas as pd + +industrial_production = pd.read_csv(snakemake.input.industrial_production_per_country, + index_col=0) + +total_steel = industrial_production[["Integrated steelworks","Electric arc"]].sum(axis=1) + +industrial_production.insert(2, "DRI + Electric arc", + snakemake.config["industry"]["St_primary_fraction"]*total_steel) +industrial_production["Electric arc"] = (1 - snakemake.config["industry"]["St_primary_fraction"])*total_steel +industrial_production["Integrated steelworks"] = 0. + + +total_aluminium = industrial_production[["Aluminium - primary production","Aluminium - secondary production"]].sum(axis=1) + +industrial_production["Aluminium - primary production"] = snakemake.config["industry"]["Al_primary_fraction"]*total_aluminium +industrial_production["Aluminium - secondary production"] = (1 - snakemake.config["industry"]["Al_primary_fraction"])*total_aluminium + + +industrial_production.to_csv(snakemake.output.industrial_production_per_country_tomorrow, + float_format='%.2f') diff --git a/scripts/build_industry_sector_ratios.py b/scripts/build_industry_sector_ratios.py index b608a91e..3c567a38 100644 --- a/scripts/build_industry_sector_ratios.py +++ b/scripts/build_industry_sector_ratios.py @@ -152,19 +152,18 @@ df.loc[['elec','heat','methane'],sector] = df.loc[['elec','heat','methane'],sect -## Integrated steelworks is converted to Electric arc -# -#> Electric arc uses scrap metal and Direct Reduced Iron -# -#> We assume that when substituting Integrated Steelworks by Electric arc furnaces. -#> 50% of Integrated steelworks is substituted by scrap metal + electric furnaces -#> 50% of Integrated steelworks is substituted by Direct Reduce Iron (with Hydrogen) + electric furnaces +## Integrated steelworks is not used in future +## TODO Include integrated steelworks + CCS -df['Integrated steelworks']=df['Electric arc'] +df['Integrated steelworks']= 0. + + +## For primary route: DRI with H2 + EAF + +df['DRI + Electric arc'] = df['Electric arc'] # adding the Hydrogen necessary for the Direct Reduction of Iron. consumption 1.7 MWh H2 /ton steel -#(0.5 because only half of the steel requires DRI, the rest is scrap metal) -df.loc['hydrogen', 'Integrated steelworks'] =snakemake.config["industry"]["H2_DRI"] * snakemake.config["industry"]["DRI_ratio"] +df.loc['hydrogen', 'DRI + Electric arc'] = snakemake.config["industry"]["H2_DRI"] ## Chemicals Industry @@ -1052,9 +1051,6 @@ sources=['elec','biomass', 'methane', 'hydrogen', 'heat','naphtha'] df.loc[sources,sector] = df.loc[sources,sector]*conv_factor/s_out['Aluminium - secondary production'] # unit MWh/t material # 1 ktoe = 11630 MWh -# primary route is divided into 50% remains as today and 50% is transformed into secondary route -df.loc[sources,'Aluminium - primary production'] = (1-snakemake.config["industry"]["Al_to_scrap"])*df.loc[sources,'Aluminium - primary production'] + snakemake.config["industry"]["Al_to_scrap"]*df.loc[sources,'Aluminium - secondary production'] -df.loc['process emission','Aluminium - primary production'] = (1-snakemake.config["industry"]["Al_to_scrap"])*df.loc['process emission','Aluminium - primary production'] ### Other non-ferrous metals From f45b9a37ae0abc22c51d367085a1ab9c21e6b455 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Fri, 28 Aug 2020 19:13:18 +0200 Subject: [PATCH 04/19] Separate ammonia from other "Basic chemicals" This allows us to control the substitution of natural gas for hydrogen in NH3 production. Remaining basic chemicals are olefins, BTX and chlorine. For 2015 NH3 production, we use the USGS data source. --- .gitignore | 2 + Snakefile | 13 ++++ config.default.yaml | 5 +- config.myopic.yaml | 5 +- scripts/build_ammonia_production.py | 45 +++++++++++++ ...build_industrial_production_per_country.py | 33 ++++++++- scripts/build_industry_sector_ratios.py | 67 ++++++++++++++----- 7 files changed, 147 insertions(+), 23 deletions(-) create mode 100644 scripts/build_ammonia_production.py diff --git a/.gitignore b/.gitignore index ed3291f6..b41436e7 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,5 @@ gurobi.log config.yaml doc/_build + +*.xls \ No newline at end of file diff --git a/Snakefile b/Snakefile index 7558a5ac..f31ef836 100644 --- a/Snakefile +++ b/Snakefile @@ -146,8 +146,19 @@ rule build_biomass_potentials: resources: mem_mb=1000 script: 'scripts/build_biomass_potentials.py' +rule build_ammonia_production: + input: + usgs="data/myb1-2017-nitro.xls" + output: + ammonia_production="resources/ammonia_production.csv" + threads: 1 + resources: mem_mb=1000 + script: 'scripts/build_ammonia_production.py' + rule build_industry_sector_ratios: + input: + ammonia_production="resources/ammonia_production.csv" output: industry_sector_ratios="resources/industry_sector_ratios.csv" threads: 1 @@ -156,6 +167,8 @@ rule build_industry_sector_ratios: rule build_industrial_production_per_country: + input: + ammonia_production="resources/ammonia_production.csv" output: industrial_production_per_country="resources/industrial_production_per_country.csv" threads: 1 diff --git a/config.default.yaml b/config.default.yaml index 55380f63..0c111485 100644 --- a/config.default.yaml +++ b/config.default.yaml @@ -161,7 +161,10 @@ 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 'Al_primary_fraction' : 0.2 # fraction of aluminium produced via the primary route versus scrap; today fraction is 0.4 - 'H2_for_NH3' : 85000 # H2 in GWh/a for 17 MtNH3/a transformed from SMR to electrolyzed-H2, following Lechtenböhmer(2016) + 'MWh_CH4_per_tNH3_SMR' : 10.8 # 2012's demand from https://ec.europa.eu/docsroom/documents/4165/attachments/1/translations/en/renditions/pdf + 'MWh_elec_per_tNH3_SMR' : 0.7 # same source, assuming 94-6% split methane-elec of total energy demand 11.5 MWh/tNH3 + 'MWh_H2_per_tNH3_electrolysis' : 6.5 # from https://doi.org/10.1016/j.joule.2018.04.017, around 0.197 tH2/tHN3 (>3/17 since some H2 lost and used for energy) + 'MWh_elec_per_tNH3_electrolysis' : 1.17 # from https://doi.org/10.1016/j.joule.2018.04.017 Table 13 (air separation and HB) 'NH3_process_emissions' : 24.5 # in MtCO2/a from SMR for H2 production for NH3 from UNFCCC for 2015 for EU28 'petrochemical_process_emissions' : 25.5 # in MtCO2/a for petrochemical and other from UNFCCC for 2015 for EU28 diff --git a/config.myopic.yaml b/config.myopic.yaml index 24213b44..00f7b062 100644 --- a/config.myopic.yaml +++ b/config.myopic.yaml @@ -161,7 +161,10 @@ 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 'Al_primary_fraction' : 0.2 # fraction of aluminium produced via the primary route versus scrap; today fraction is 0.4 - 'H2_for_NH3' : 85000 # H2 in GWh/a for 17 MtNH3/a transformed from SMR to electrolyzed-H2, following Lechtenböhmer(2016) + 'MWh_CH4_per_tNH3_SMR' : 10.8 # 2012's demand from https://ec.europa.eu/docsroom/documents/4165/attachments/1/translations/en/renditions/pdf + 'MWh_elec_per_tNH3_SMR' : 0.7 # same source, assuming 94-6% split methane-elec of total energy demand 11.5 MWh/tNH3 + 'MWh_H2_per_tNH3_electrolysis' : 6.5 # from https://doi.org/10.1016/j.joule.2018.04.017, around 0.197 tH2/tHN3 (>3/17 since some H2 lost and used for energy) + 'MWh_elec_per_tNH3_electrolysis' : 1.17 # from https://doi.org/10.1016/j.joule.2018.04.017 Table 13 (air separation and HB) 'NH3_process_emissions' : 24.5 # in MtCO2/a from SMR for H2 production for NH3 from UNFCCC for 2015 for EU28 'petrochemical_process_emissions' : 25.5 # in MtCO2/a for petrochemical and other from UNFCCC for 2015 for EU28 diff --git a/scripts/build_ammonia_production.py b/scripts/build_ammonia_production.py new file mode 100644 index 00000000..9f2c2690 --- /dev/null +++ b/scripts/build_ammonia_production.py @@ -0,0 +1,45 @@ + + +import pandas as pd + +ammonia = pd.read_excel(snakemake.input.usgs, + sheet_name="T12", + skiprows=5, + header=0, + index_col=0, + skipfooter=19) + +rename = {"Austriae" : "AT", + "Bulgaria" : "BG", + "Belgiume" : "BE", + "Croatia" : "HR", + "Czechia" : "CZ", + "Estonia" : "EE", + "Finland" : "FI", + "France" : "FR", + "Germany" : "DE", + "Greece" : "GR", + "Hungarye" : "HU", + "Italye" : "IT", + "Lithuania" : "LT", + "Netherlands" : "NL", + "Norwaye" : "NO", + "Poland" : "PL", + "Romania" : "RO", + "Serbia" : "RS", + "Slovakia" : "SK", + "Spain" : "ES", + "Switzerland" : "CH", + "United Kingdom" : "GB", +} + +ammonia = ammonia.rename(rename) + +ammonia = ammonia.loc[rename.values(),[str(i) for i in range(2013,2018)]].astype(float) + +#convert from ktonN to ktonNH3 +ammonia = ammonia*17/14 + +ammonia.index.name = "ktonNH3/a" + +ammonia.to_csv(snakemake.output.ammonia_production) diff --git a/scripts/build_industrial_production_per_country.py b/scripts/build_industrial_production_per_country.py index 4c4de875..c8fa6910 100644 --- a/scripts/build_industrial_production_per_country.py +++ b/scripts/build_industrial_production_per_country.py @@ -9,6 +9,10 @@ ktoe_to_twh = 0.01163 jrc_base_dir = "data/jrc-idees-2015" eb_base_dir = "data/eurostat-energy_balances-may_2018_edition" +# year for which data is retrieved +raw_year = 2015 +year = raw_year-2016 + sub_sheet_name_dict = { 'Iron and steel':'ISI', 'Chemicals Industry':'CHI', 'Non-metallic mineral products': 'NMM', @@ -162,7 +166,7 @@ for country in countries: #energy consumption in the sector and EU28 excel_sum_out = pd.read_excel('{}/JRC-IDEES-2015_Industry_EU28.xlsx'.format(jrc_base_dir), sheet_name='Ind_Summary', index_col=0,header=0,squeeze=True) # the summary sheet - s_sum_out = excel_sum_out.iloc[49:76,-1] + s_sum_out = excel_sum_out.iloc[49:76,year] e_EU28 = s_sum_out[dic_sec_summary[sector]] ratio_country_EU28=e_country/e_EU28 @@ -170,7 +174,7 @@ for country in countries: excel_out = pd.read_excel('{}/JRC-IDEES-2015_Industry_EU28.xlsx'.format(jrc_base_dir), sheet_name=sub_sheet_name_dict[sector],index_col=0,header=0,squeeze=True) # the summary sheet - s_out = excel_out.iloc[loc_dic[sector][0]:loc_dic[sector][1],-1] + s_out = excel_out.iloc[loc_dic[sector][0]:loc_dic[sector][1],year] for subsector in sect2sub[sector]: countries_demand.loc[country,subsector] = ratio_country_EU28*s_out[out_dic[subsector]] @@ -180,11 +184,34 @@ for country in countries: # read the input sheets excel_out = pd.read_excel('{}/JRC-IDEES-2015_Industry_{}.xlsx'.format(jrc_base_dir,jrc_names.get(country,country)), sheet_name=sub_sheet_name_dict[sector],index_col=0,header=0,squeeze=True) # the summary sheet - s_out = excel_out.iloc[loc_dic[sector][0]:loc_dic[sector][1],-1] + s_out = excel_out.iloc[loc_dic[sector][0]:loc_dic[sector][1],year] for subsector in sect2sub[sector]: countries_demand.loc[country,subsector] = s_out[out_dic[subsector]] + +#include ammonia demand separately and remove ammonia from basic chemicals + +ammonia = pd.read_csv(snakemake.input.ammonia_production, + index_col=0) + +there = ammonia.index.intersection(countries_demand.index) +missing = countries_demand.index^there + +print("Following countries have no ammonia demand:", missing) + +countries_demand.insert(2,"Ammonia",0.) + +countries_demand.loc[there,"Ammonia"] = ammonia.loc[there, str(raw_year)] + +countries_demand["Basic chemicals"] -= countries_demand["Ammonia"] + +#EE, HR and LT got negative demand through subtraction - poor data +countries_demand.loc[countries_demand["Basic chemicals"] < 0.,"Basic chemicals"] = 0. + +countries_demand.rename(columns={"Basic chemicals" : "Basic chemicals (without ammonia)"}, + inplace=True) + countries_demand.index.name = "kton/a" countries_demand.to_csv(snakemake.output.industrial_production_per_country, diff --git a/scripts/build_industry_sector_ratios.py b/scripts/build_industry_sector_ratios.py index 3c567a38..5b7d4ba3 100644 --- a/scripts/build_industry_sector_ratios.py +++ b/scripts/build_industry_sector_ratios.py @@ -5,11 +5,11 @@ import numpy as np base_dir = "data/jrc-idees-2015" -# year for wich data is retrieved -year = 2015 -year = year-2016 +# year for which data is retrieved +raw_year = 2015 +year = raw_year-2016 -conv_factor=11.630 #ktoe/kton -> MWh/ton +conv_factor=11.630 #GWh/ktoe OR MWh/toe country = 'EU28' @@ -185,6 +185,8 @@ excel_emi = pd.read_excel('{}/JRC-IDEES-2015_Industry_{}.xlsx'.format(base_dir,c ### Basic chemicals +## Ammonia is separate afterwards + sector = 'Basic chemicals' df[sector] = 0 @@ -203,9 +205,8 @@ df.loc['heat',sector] += s_fec['Low enthalpy heat'] #### Chemicals: Feedstock (energy used as raw material) #> There are Solids, Refinery gas, LPG, Diesel oil, Residual fuel oil, Other liquids, Naphtha, Natural gas for feedstock. # -#> Naphta represents 47%, methane 17%. LPG (18%) solids, refinery gas, diesel oil, residual fuel oils and other liquids are asimilated to Napthta -# -#> Following Lechtenbohmer 2016, the 85 TWh/year of methane for the ammonia industry are substited by hydrogen. +#> Naphta represents 47%, methane 17%. LPG (18%) solids, refinery gas, diesel oil, residual fuel oils and other liquids are asimilated to Naphtha + subsector = 'Chemicals: Feedstock (energy used as raw material)' @@ -218,10 +219,7 @@ assert s_fec.index[0] == subsector df.loc['naphtha',sector] += s_fec['Naphtha'] # natural gas -# 85 TWh/year of methane for the ammonia industry are substituted by hydrogen -df.loc['methane',sector] += s_fec['Natural gas'] - snakemake.config["industry"]["H2_for_NH3"]/conv_factor -df.loc['hydrogen',sector] += snakemake.config["industry"]["H2_for_NH3"]/conv_factor -# 1 ktoe = 11630 MWh +df.loc['methane',sector] += s_fec['Natural gas'] # LPG and other feedstock materials are assimilated to naphtha since they will be produced trough Fischer-Tropsh process df.loc['naphtha',sector] += (s_fec['Solids'] + s_fec['Refinery gas'] + s_fec['LPG'] + s_fec['Diesel oil'] @@ -244,8 +242,10 @@ assert s_fec.index[0] == subsector # efficiency of biomass eff_bio = s_ued['Biomass']/s_fec['Biomass'] -# replace all fec by biomass -df.loc['biomass',sector] += s_ued[subsector]/eff_bio +# replace all non-methane fec by biomass +df.loc['biomass',sector] += (s_ued[subsector]-s_ued['Natural gas (incl. biogas)'])/eff_bio + +df.loc['methane',sector] += s_fec['Natural gas (incl. biogas)'] #### Chemicals: Furnaces #> assume fully electrified @@ -297,10 +297,25 @@ s_emi = excel_emi.iloc[3:57,year] assert s_emi.index[0] == sector + +## Correct everything by subtracting 2015's ammonia demand and putting in ammonia demand for H2 and electricity separately + s_out = excel_out.iloc[8:9,year] assert sector in str(s_out.index) +ammonia = pd.read_csv(snakemake.input.ammonia_production, + index_col=0) + +eu28 = ['FR', 'DE', 'GB', 'IT', 'ES', 'PL', 'SE', 'NL', 'BE', 'FI', + 'DK', 'PT', 'RO', 'AT', 'BG', 'EE', 'GR', 'LV', 'CZ', + 'HU', 'IE', 'SK', 'LT', 'HR', 'LU', 'SI', 'CY', 'MT'] + +#ktNH3/a +total_ammonia = ammonia.loc[ammonia.index.intersection(eu28),str(raw_year)].sum() + +s_out -= total_ammonia + df.loc['process emission',sector] += (s_emi['Process emissions'] - snakemake.config["industry"]['petrochemical_process_emissions']*1e3 - snakemake.config["industry"]['NH3_process_emissions']*1e3)/s_out.values # unit tCO2/t material #these are emissions originating from feedstock, i.e. could be non-fossil origin @@ -310,8 +325,24 @@ df.loc['process emission from feedstock',sector] += (snakemake.config["industry" # final energy consumption per t sources=['elec','biomass', 'methane', 'hydrogen', 'heat','naphtha'] -df.loc[sources,sector] = df.loc[sources,sector]*conv_factor/s_out.values# unit MWh/t material -# 1 ktoe = 11630 MWh +#convert from ktoe/a to GWh/a +df.loc[sources,sector] *= conv_factor + +df.loc['methane',sector] -= total_ammonia*snakemake.config['industry']['MWh_CH4_per_tNH3_SMR'] +df.loc['elec',sector] -= total_ammonia*snakemake.config['industry']['MWh_elec_per_tNH3_SMR'] + +df.loc[sources,sector] = df.loc[sources,sector]/s_out.values # unit MWh/t material + +df.rename(columns={sector : sector + " (without ammonia)"}, + inplace=True) + +sector = 'Ammonia' + +df[sector] = 0. + +df.loc['hydrogen',sector] = snakemake.config['industry']['MWh_H2_per_tNH3_electrolysis'] +df.loc['elec',sector] = snakemake.config['industry']['MWh_elec_per_tNH3_electrolysis'] + ### Other chemicals @@ -404,7 +435,7 @@ df.loc['process emission',sector] += s_emi['Process emissions']/s_out.values # u # final energy consumption per t sources=['elec','biomass', 'methane', 'hydrogen', 'heat','naphtha'] -df.loc[sources,sector] = df.loc[sources,sector]*11.630/s_out.values # unit MWh/t material +df.loc[sources,sector] = df.loc[sources,sector]*conv_factor/s_out.values # unit MWh/t material # 1 ktoe = 11630 MWh ### Pharmaceutical products etc. @@ -494,7 +525,7 @@ df.loc['process emission',sector] += 0 # unit tCO2/t material # final energy consumption per t sources=['elec','biomass', 'methane', 'hydrogen', 'heat', 'naphtha'] -df.loc[sources,sector] = df.loc[sources,sector]*11.630/s_out.values # unit MWh/t material +df.loc[sources,sector] = df.loc[sources,sector]*conv_factor/s_out.values # unit MWh/t material # 1 ktoe = 11630 MWh ## Non-metallic mineral products @@ -634,7 +665,7 @@ df.loc['process emission',sector] += s_emi['Process emissions']/s_out.values # u # final energy consumption per t sources=['elec','biomass', 'methane', 'hydrogen', 'heat','naphtha'] -df.loc[sources,sector] = df.loc[sources,sector]*11.630/s_out.values # unit MWh/t material +df.loc[sources,sector] = df.loc[sources,sector]*conv_factor/s_out.values # unit MWh/t material # 1 ktoe = 11630 MWh ### Glass production From f3dcda9179bd4aecfc87239ccc3582defc982c7a Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Mon, 7 Sep 2020 11:04:50 +0200 Subject: [PATCH 05/19] Include integrated steelworks in industry_sector_ratio Also switch it from sep=";" to "," --- ...ld_industrial_energy_demand_per_country.py | 3 +- scripts/build_industry_sector_ratios.py | 126 ++++++++++++++++-- 2 files changed, 115 insertions(+), 14 deletions(-) diff --git a/scripts/build_industrial_energy_demand_per_country.py b/scripts/build_industrial_energy_demand_per_country.py index 73036a99..6ee67f4f 100644 --- a/scripts/build_industrial_energy_demand_per_country.py +++ b/scripts/build_industrial_energy_demand_per_country.py @@ -10,7 +10,8 @@ eb_base_dir = "data/eurostat-energy_balances-may_2018_edition" jrc_base_dir = "data/jrc-idees-2015" # import EU ratios df as csv -industry_sector_ratios=pd.read_csv(snakemake.input.industry_sector_ratios, sep=';', index_col=0) +industry_sector_ratios=pd.read_csv(snakemake.input.industry_sector_ratios, + index_col=0) #material demand per country and industry (kton/a) countries_production = pd.read_csv(snakemake.input.industrial_production_per_country, index_col=0) diff --git a/scripts/build_industry_sector_ratios.py b/scripts/build_industry_sector_ratios.py index 5b7d4ba3..c8a1747d 100644 --- a/scripts/build_industry_sector_ratios.py +++ b/scripts/build_industry_sector_ratios.py @@ -26,7 +26,7 @@ sub_sheet_name_dict = { 'Iron and steel':'ISI', 'Wood and wood products': 'WWP', 'Other Industrial Sectors': 'OIS'} -index = ['elec','biomass','methane','hydrogen','heat','naphtha','process emission','process emission from feedstock'] +index = ['elec','coal','coke','biomass','methane','hydrogen','heat','naphtha','process emission','process emission from feedstock'] df = pd.DataFrame(index=index) @@ -58,7 +58,7 @@ excel_emi = pd.read_excel('{}/JRC-IDEES-2015_Industry_{}.xlsx'.format(base_dir,c sector = 'Electric arc' -df[sector] = 0 +df[sector] = 0. # read the corresponding lines s_fec = excel_fec.iloc[51:57,year] @@ -150,15 +150,7 @@ df.loc['process emission',sector] = s_emi['Process emissions']/s_out[sector] # u # final energy consumption per t df.loc[['elec','heat','methane'],sector] = df.loc[['elec','heat','methane'],sector]*conv_factor/s_out[sector] # unit MWh/t material - - -## Integrated steelworks is not used in future -## TODO Include integrated steelworks + CCS - -df['Integrated steelworks']= 0. - - -## For primary route: DRI with H2 + EAF +### For primary route: DRI with H2 + EAF df['DRI + Electric arc'] = df['Electric arc'] @@ -166,6 +158,114 @@ df['DRI + Electric arc'] = df['Electric arc'] df.loc['hydrogen', 'DRI + Electric arc'] = snakemake.config["industry"]["H2_DRI"] +### Integrated steelworks (could be used in combination with CCS) +### Assume existing fuels are kept, except for furnaces, refining, rolling, finishing +### Ignore 'derived gases' since these are top gases from furnaces + +sector = 'Integrated steelworks' + +df['Integrated steelworks']= 0. + +# read the corresponding lines +s_fec = excel_fec.iloc[3:9,year] + +assert s_fec.index[0] == sector + +# Lighting, Air compressors, Motor drives, Fans and pumps +df.loc['elec',sector] += s_fec[['Lighting','Air compressors','Motor drives','Fans and pumps']].sum() + +# Low enthalpy heat +df.loc['heat',sector] += s_fec['Low enthalpy heat'] + + +#### Steel: Sinter/Pellet making + +subsector = 'Steel: Sinter/Pellet making' + +# read the corresponding lines +s_fec = excel_fec.iloc[13:19,year] + +s_ued = excel_ued.iloc[13:19,year] + +assert s_fec.index[0] == subsector + +df.loc['elec',sector] += s_fec['Electricity'] +df.loc['methane',sector] += s_fec['Natural gas (incl. biogas)'] +df.loc['methane',sector] += s_fec['Residual fuel oil'] +df.loc['coal',sector] += s_fec['Solids'] + + +#### Steel: Blast / Basic Oxygen Furnace + +subsector = 'Steel: Blast /Basic oxygen furnace' + +# read the corresponding lines +s_fec = excel_fec.iloc[19:25,year] + +s_ued = excel_ued.iloc[19:25,year] + +assert s_fec.index[0] == subsector + +df.loc['methane',sector] += s_fec['Natural gas (incl. biogas)'] +df.loc['methane',sector] += s_fec['Residual fuel oil'] +df.loc['coal',sector] += s_fec['Solids'] +df.loc['coke',sector] += s_fec['Coke'] + + +#### Steel: Furnaces, Refining and Rolling +#> assume fully electrified +# +#> other processes are scaled by the used energy + +subsector = 'Steel: Furnaces, Refining and Rolling' + +# read the corresponding lines +s_fec = excel_fec.iloc[25:32,year] + +s_ued = excel_ued.iloc[25:32,year] + +assert s_fec.index[0] == subsector + +# this process can be electrified +eff = s_ued['Steel: Furnaces, Refining and Rolling - Electric']/s_fec['Steel: Furnaces, Refining and Rolling - Electric'] + +df.loc['elec',sector] += s_ued[subsector]/eff + +#### Steel: Products finishing +#> assume fully electrified + +subsector = 'Steel: Products finishing' + +# read the corresponding lines +s_fec = excel_fec.iloc[32:49,year] + +s_ued = excel_ued.iloc[32:49,year] + +assert s_fec.index[0] == subsector + +# this process can be electrified +eff = s_ued['Steel: Products finishing - Electric']/s_fec['Steel: Products finishing - Electric'] + +df.loc['elec',sector] += s_ued[subsector]/eff + + +#### Process emissions (per physical output) + +s_emi = excel_emi.iloc[3:50,year] + +assert s_emi.index[0] == sector + +s_out = excel_out.iloc[6:7,year] + +assert sector in str(s_out.index) + +df.loc['process emission',sector] = s_emi['Process emissions']/s_out[sector] # unit tCO2/t material + +# final energy consumption per t +df.loc[['elec','heat','methane','coke','coal'],sector] = df.loc[['elec','heat','methane','coke','coal'],sector]*conv_factor/s_out[sector] # unit MWh/t material + + + ## Chemicals Industry sector = 'Chemicals Industry' @@ -185,7 +285,7 @@ excel_emi = pd.read_excel('{}/JRC-IDEES-2015_Industry_{}.xlsx'.format(base_dir,c ### Basic chemicals -## Ammonia is separate afterwards +## Ammonia is separated afterwards sector = 'Basic chemicals' @@ -1400,4 +1500,4 @@ df.loc[sources,sector] = df.loc[sources,sector]*conv_factor/s_out['Physical outp df.index.name = "MWh/tMaterial" -df.to_csv('resources/industry_sector_ratios.csv', sep=';') +df.to_csv('resources/industry_sector_ratios.csv') From aa3ffb9ac25708fb33474c5e2c381d4923d9bb4b Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Mon, 7 Sep 2020 14:49:20 +0200 Subject: [PATCH 06/19] Remove biomass from chemicals, cement; increase in PPA, FBT Remove non-existing biomass from chemicals and cement, since these need higher temperatures than achievable with residues and waste. Increase biomass in pulp and paper (since already used extensively here and T < 500), and replace methane with biomass in food, beverages and tobacco, since temperatures needed are low (T < 500). --- scripts/build_industry_sector_ratios.py | 81 +++++++++++++++---------- 1 file changed, 48 insertions(+), 33 deletions(-) diff --git a/scripts/build_industry_sector_ratios.py b/scripts/build_industry_sector_ratios.py index c8a1747d..f283d222 100644 --- a/scripts/build_industry_sector_ratios.py +++ b/scripts/build_industry_sector_ratios.py @@ -326,9 +326,9 @@ df.loc['naphtha',sector] += (s_fec['Solids'] + s_fec['Refinery gas'] + s_fec['LP + s_fec['Residual fuel oil'] + s_fec['Other liquids']) #### Chemicals: Steam processing -#> All the final energy consumption in the Stem processing is converted to biomass. +#> All the final energy consumption in the Steam processing is converted to methane, since we need >1000 C temperatures here. # -#> The current efficiency of biomass is assumed in the conversion. +#> The current efficiency of methane is assumed in the conversion. subsector = 'Chemicals: Steam processing' @@ -339,13 +339,11 @@ s_ued = excel_ued.iloc[22:33,year] assert s_fec.index[0] == subsector -# efficiency of biomass -eff_bio = s_ued['Biomass']/s_fec['Biomass'] +# efficiency of natural gas +eff_ch4 = s_ued['Natural gas (incl. biogas)']/s_fec['Natural gas (incl. biogas)'] -# replace all non-methane fec by biomass -df.loc['biomass',sector] += (s_ued[subsector]-s_ued['Natural gas (incl. biogas)'])/eff_bio - -df.loc['methane',sector] += s_fec['Natural gas (incl. biogas)'] +# replace all fec by methane +df.loc['methane',sector] += s_ued[subsector]/eff_ch4 #### Chemicals: Furnaces #> assume fully electrified @@ -657,7 +655,7 @@ excel_emi = pd.read_excel('{}/JRC-IDEES-2015_Industry_{}.xlsx'.format(base_dir,c # #> Temperatures above 1400C are required for procesing limestone and sand into clinker. # -#> Everything (except current electricity and heat consumption) is transformed into biomass +#> Everything (except current electricity and heat consumption and existing biomass) is transformed into methane for high T. sector = 'Cement' @@ -676,10 +674,11 @@ df.loc['elec',sector] += s_fec[['Lighting','Air compressors','Motor drives','Fan # Low enthalpy heat df.loc['heat',sector] += s_fec['Low enthalpy heat'] -# Efficiency changes due to biomass -eff_bio=s_ued['Biomass']/s_fec['Biomass'] +# pre-processing: keep existing elec and biomass, rest to methane +df.loc['elec', sector] += s_fec['Cement: Grinding, milling of raw material'] +df.loc['biomass', sector] += s_fec['Biomass'] +df.loc['methane', sector] += s_fec['Cement: Pre-heating and pre-calcination'] - s_fec['Biomass'] -df.loc['biomass', sector] += s_ued[['Cement: Grinding, milling of raw material', 'Cement: Pre-heating and pre-calcination']].sum()/eff_bio #### Cement: Clinker production (kilns) @@ -692,10 +691,10 @@ s_ued = excel_ued.iloc[34:43,year] assert s_fec.index[0] == subsector -# Efficiency changes due to biomass -eff_bio=s_ued['Biomass']/s_fec['Biomass'] +df.loc['biomass', sector] += s_fec['Biomass'] +df.loc['methane', sector] += s_fec['Cement: Clinker production (kilns)'] - s_fec['Biomass'] +df.loc['elec', sector] += s_fec['Cement: Grinding, packaging'] -df.loc['biomass', sector] += s_ued[['Cement: Clinker production (kilns)', 'Cement: Grinding, packaging']].sum()/eff_bio #### Process-emission came from the calcination of limestone to chemically reactive calcium oxide (lime). #> Calcium carbonate -> lime + CO2 @@ -837,7 +836,7 @@ excel_out = pd.read_excel('{}/JRC-IDEES-2015_Industry_{}.xlsx'.format(base_dir,c # #> Includes three subcategories: (a) Wood preparation, grinding; (b) Pulping; (c) Cleaning. # -#> (b) Pulping is electrified. The efficiency is calculated from the pulping process that is already electric. +#> (b) Pulping is either biomass or electric; left like this (dominated by biomass). # #> (a) Wood preparation, grinding and (c) Cleaning represent only 10% their current energy consumption is assumed to be electrified without any change in efficiency @@ -859,14 +858,11 @@ df.loc['elec', sector] += s_fec[['Lighting','Air compressors','Motor drives','Fa df.loc['heat', sector] += s_fec['Low enthalpy heat'] # Industry-specific -df.loc['elec', sector] += s_fec[['Pulp: Wood preparation, grinding', 'Pulp: Cleaning']].sum() +df.loc['elec', sector] += s_fec[['Pulp: Wood preparation, grinding', 'Pulp: Cleaning', 'Pulp: Pulping electric']].sum() -# Efficiency changes due to electrification -eff_elec=s_ued['Pulp: Pulping electric']/s_fec['Pulp: Pulping electric'] -df.loc['elec', sector] += s_ued['Pulp: Pulping thermal']/eff_elec - -# add electricity from process that is already electrified -df.loc['elec', sector] += s_fec['Pulp: Pulping electric'] +# Efficiency changes due to biomass +eff_bio=s_ued['Biomass']/s_fec['Biomass'] +df.loc['biomass', sector] += s_ued['Pulp: Pulping thermal']/eff_bio s_out = excel_out.iloc[8:9,year] @@ -881,7 +877,7 @@ df.loc[sources,sector] = df.loc[sources,sector]*conv_factor/s_out['Pulp producti # #> Includes three subcategories: (a) Stock preparation; (b) Paper machine; (c) Product finishing. # -#> (b) Paper machine and (c) Product finishing are electrified. The efficiency is calculated from the pulping process that is already electric. +#> (b) Paper machine and (c) Product finishing are left electric and thermal is moved to biomass. The efficiency is calculated from the pulping process that is already biomass. # #> (a) Stock preparation represents only 7% and its current energy consumption is assumed to be electrified without any change in efficiency. @@ -905,16 +901,35 @@ df.loc['heat', sector] += s_fec['Low enthalpy heat'] # Industry-specific df.loc['elec', sector] += s_fec['Paper: Stock preparation'] -# Efficiency changes due to electrification -eff_elec=s_ued['Paper: Paper machine - Electricity']/s_fec['Paper: Paper machine - Electricity'] -df.loc['elec', sector] += s_ued['Paper: Paper machine - Steam use']/eff_elec - -eff_elec=s_ued['Paper: Product finishing - Electricity']/s_fec['Paper: Product finishing - Electricity'] -df.loc['elec', sector] += s_ued['Paper: Product finishing - Steam use']/eff_elec - # add electricity from process that is already electrified df.loc['elec', sector] += s_fec['Paper: Paper machine - Electricity'] +# add electricity from process that is already electrified +df.loc['elec', sector] += s_fec['Paper: Product finishing - Electricity'] + + +s_fec = excel_fec.iloc[53:64,year] + +s_ued = excel_ued.iloc[53:64,year] + +assert s_fec.index[0] == 'Paper: Paper machine - Steam use' + +# Efficiency changes due to biomass +eff_bio=s_ued['Biomass']/s_fec['Biomass'] +df.loc['biomass', sector] += s_ued['Paper: Paper machine - Steam use']/eff_bio + + +s_fec = excel_fec.iloc[66:77,year] + +s_ued = excel_ued.iloc[66:77,year] + +assert s_fec.index[0] == 'Paper: Product finishing - Steam use' + +# Efficiency changes due to biomass +eff_bio=s_ued['Biomass']/s_fec['Biomass'] +df.loc['biomass', sector] += s_ued['Paper: Product finishing - Steam use']/eff_bio + + # read the corresponding lines s_out = excel_out.iloc[9:10,year] @@ -1008,8 +1023,8 @@ df.loc['elec', sector] += s_ued['Food: Drying']/eff_elec eff_elec=s_ued['Food: Electric cooling']/s_fec['Food: Electric cooling'] df.loc['elec', sector] += s_ued['Food: Process cooling and refrigeration']/eff_elec -# Steam processing is electrified without change in efficiency -df.loc['elec', sector] += s_fec['Food: Steam processing'] +# Steam processing goes all to biomass without change in efficiency +df.loc['biomass', sector] += s_fec['Food: Steam processing'] # add electricity from process that is already electrified df.loc['elec', sector] += s_fec['Food: Electric machinery'] From 916aa5e0d95fc7075d54b85735e3243372d48239 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Mon, 7 Sep 2020 16:48:06 +0200 Subject: [PATCH 07/19] Generate today's industry demand per ct and per sector Only covers EU28 at the moment. Uses the energy balances sheets from JRC-IDEES. --- Snakefile | 7 ++ ...ustrial_energy_demand_per_country_today.py | 107 ++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 scripts/build_industrial_energy_demand_per_country_today.py diff --git a/Snakefile b/Snakefile index f31ef836..0b153428 100644 --- a/Snakefile +++ b/Snakefile @@ -185,6 +185,13 @@ rule build_industrial_production_per_country_tomorrow: resources: mem_mb=1000 script: 'scripts/build_industrial_production_per_country_tomorrow.py' +rule build_industrial_energy_demand_per_country_today: + output: + industrial_energy_demand_per_country_today="resources/industrial_energy_demand_per_country_today.csv" + threads: 1 + resources: mem_mb=1000 + script: 'scripts/build_industrial_energy_demand_per_country_today.py' + rule build_industrial_energy_demand_per_country: input: diff --git a/scripts/build_industrial_energy_demand_per_country_today.py b/scripts/build_industrial_energy_demand_per_country_today.py new file mode 100644 index 00000000..91b39576 --- /dev/null +++ b/scripts/build_industrial_energy_demand_per_country_today.py @@ -0,0 +1,107 @@ + +import pandas as pd + +# sub-sectors as used in PyPSA-Eur-Sec and listed in JRC-IDEES industry sheets +sub_sectors = {'Iron and steel' : ['Integrated steelworks','Electric arc'], + 'Non-ferrous metals' : ['Alumina production','Aluminium - primary production','Aluminium - secondary production','Other non-ferrous metals'], + 'Chemicals' : ['Basic chemicals', 'Other chemicals', 'Pharmaceutical products etc.', 'Basic chemicals feedstock'], + 'Non-metalic mineral' : ['Cement','Ceramics & other NMM','Glass production'], + 'Printing' : ['Pulp production','Paper production','Printing and media reproduction'], + 'Food' : ['Food, beverages and tobacco'], + 'Transport equipment' : ['Transport Equipment'], + 'Machinery equipment' : ['Machinery Equipment'], + 'Textiles and leather' : ['Textiles and leather'], + 'Wood and wood products' : ['Wood and wood products'], + 'Other Industrial Sectors' : ['Other Industrial Sectors'], +} + + +# name in JRC-IDEES Energy Balances +eb_sheet_name = {'Integrated steelworks' : 'cisb', + 'Electric arc' : 'cise', + 'Alumina production' : 'cnfa', + 'Aluminium - primary production' : 'cnfp', + 'Aluminium - secondary production' : 'cnfs', + 'Other non-ferrous metals' : 'cnfo', + 'Basic chemicals' : 'cbch', + 'Other chemicals' : 'coch', + 'Pharmaceutical products etc.' : 'cpha', + 'Basic chemicals feedstock' : 'cpch', + 'Cement' : 'ccem', + 'Ceramics & other NMM' : 'ccer', + 'Glass production' : 'cgla', + 'Pulp production' : 'cpul', + 'Paper production' : 'cpap', + 'Printing and media reproduction' : 'cprp', + 'Food, beverages and tobacco' : 'cfbt', + 'Transport Equipment' : 'ctre', + 'Machinery Equipment' : 'cmae', + 'Textiles and leather' : 'ctel', + 'Wood and wood products' : 'cwwp', + 'Mining and quarrying' : 'cmiq', + 'Construction' : 'ccon', + 'Non-specified': 'cnsi', +} + + + +fuels = {'all' : ['All Products'], + 'solid' : ['Solid Fuels'], + 'liquid' : ['Total petroleum products (without biofuels)'], + 'gas' : ['Gases'], + 'heat' : ['Nuclear heat','Derived heat'], + 'biomass' : ['Biomass and Renewable wastes'], + 'waste' : ['Wastes (non-renewable)'], + 'electricity' : ['Electricity'], +} + +ktoe_to_twh = 0.011630 + +eu28 = ['FR', 'DE', 'GB', 'IT', 'ES', 'PL', 'SE', 'NL', 'BE', 'FI', + 'DK', 'PT', 'RO', 'AT', 'BG', 'EE', 'GR', 'LV', 'CZ', + 'HU', 'IE', 'SK', 'LT', 'HR', 'LU', 'SI', 'CY', 'MT'] + +jrc_names = {"GR" : "EL", + "GB" : "UK"} + +year = 2015 +summaries = {} + +#for some reason the Energy Balances list Other Industrial Sectors separately +ois_subs = ['Mining and quarrying','Construction','Non-specified'] + + + +for ct in eu28: + print(ct) + filename = 'data/jrc-idees-2015/JRC-IDEES-2015_EnergyBalance_{}.xlsx'.format(jrc_names.get(ct,ct)) + + summary = pd.DataFrame(index=list(fuels.keys()) + ['other']) + + for sector in sub_sectors: + if sector == 'Other Industrial Sectors': + subs = ois_subs + else: + subs = sub_sectors[sector] + + for sub in subs: + df = pd.read_excel(filename, + sheet_name=eb_sheet_name[sub], + index_col=0) + + s = df[year].astype(float) + + for fuel in fuels: + summary.at[fuel,sub] = s[fuels[fuel]].sum() + summary.at['other',sub] = summary.at['all',sub] - summary.loc[summary.index^['all','other'],sub].sum() + + summary['Other Industrial Sectors'] = summary[ois_subs].sum(axis=1) + summary.drop(columns=ois_subs,inplace=True) + + summaries[ct] = summary*ktoe_to_twh + +final_summary = pd.concat(summaries,axis=1) + +final_summary.index.name = 'TWh/a' + +final_summary.to_csv(snakemake.output.industrial_energy_demand_per_country_today) From 04629b5113025c7300e01e48bd98c286d62df85e Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Mon, 7 Sep 2020 17:23:31 +0200 Subject: [PATCH 08/19] For DRI+EAF route for primary steel, add electricity for DRI shaft --- config.default.yaml | 1 + config.myopic.yaml | 1 + scripts/build_industry_sector_ratios.py | 2 ++ 3 files changed, 4 insertions(+) diff --git a/config.default.yaml b/config.default.yaml index 0c111485..135d7c25 100644 --- a/config.default.yaml +++ b/config.default.yaml @@ -160,6 +160,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 + '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 'MWh_elec_per_tNH3_SMR' : 0.7 # same source, assuming 94-6% split methane-elec of total energy demand 11.5 MWh/tNH3 diff --git a/config.myopic.yaml b/config.myopic.yaml index 00f7b062..435f4eb6 100644 --- a/config.myopic.yaml +++ b/config.myopic.yaml @@ -160,6 +160,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 + '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 'MWh_elec_per_tNH3_SMR' : 0.7 # same source, assuming 94-6% split methane-elec of total energy demand 11.5 MWh/tNH3 diff --git a/scripts/build_industry_sector_ratios.py b/scripts/build_industry_sector_ratios.py index f283d222..810b242a 100644 --- a/scripts/build_industry_sector_ratios.py +++ b/scripts/build_industry_sector_ratios.py @@ -156,6 +156,8 @@ df['DRI + Electric arc'] = df['Electric arc'] # adding the Hydrogen necessary for the Direct Reduction of Iron. consumption 1.7 MWh H2 /ton steel df.loc['hydrogen', 'DRI + Electric arc'] = snakemake.config["industry"]["H2_DRI"] +# add electricity consumption in DRI shaft (0.322 MWh/tSl) +df.loc['elec', 'DRI + Electric arc'] += snakemake.config["industry"]["elec_DRI"] ### Integrated steelworks (could be used in combination with CCS) From 783e026ebfde8669d40675eab18872b243f523e8 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Mon, 7 Sep 2020 18:41:07 +0200 Subject: [PATCH 09/19] Separate ammonia from basic chemicals in today's industry energy --- Snakefile | 2 ++ ...ustrial_energy_demand_per_country_today.py | 21 ++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/Snakefile b/Snakefile index 0b153428..07868043 100644 --- a/Snakefile +++ b/Snakefile @@ -186,6 +186,8 @@ rule build_industrial_production_per_country_tomorrow: script: 'scripts/build_industrial_production_per_country_tomorrow.py' rule build_industrial_energy_demand_per_country_today: + input: + ammonia_production="resources/ammonia_production.csv" output: industrial_energy_demand_per_country_today="resources/industrial_energy_demand_per_country_today.csv" threads: 1 diff --git a/scripts/build_industrial_energy_demand_per_country_today.py b/scripts/build_industrial_energy_demand_per_country_today.py index 91b39576..7e91636f 100644 --- a/scripts/build_industrial_energy_demand_per_country_today.py +++ b/scripts/build_industrial_energy_demand_per_country_today.py @@ -71,6 +71,11 @@ summaries = {} ois_subs = ['Mining and quarrying','Construction','Non-specified'] +#MtNH3/a +ammonia = pd.read_csv(snakemake.input.ammonia_production, + index_col=0)/1e3 + + for ct in eu28: print(ct) @@ -98,7 +103,21 @@ for ct in eu28: summary['Other Industrial Sectors'] = summary[ois_subs].sum(axis=1) summary.drop(columns=ois_subs,inplace=True) - summaries[ct] = summary*ktoe_to_twh + summary.drop(index=['all'],inplace=True) + + summary *= ktoe_to_twh + + summary['Basic chemicals'] += summary['Basic chemicals feedstock'] + summary.drop(columns=['Basic chemicals feedstock'], inplace=True) + + summary['Ammonia'] = 0. + summary.at['gas','Ammonia'] = snakemake.config['industry']['MWh_CH4_per_tNH3_SMR']*ammonia[str(year)].get(ct,0.) + summary.at['electricity','Ammonia'] = snakemake.config['industry']['MWh_elec_per_tNH3_SMR']*ammonia[str(year)].get(ct,0.) + summary['Basic chemicals (without ammonia)'] = summary['Basic chemicals'] - summary['Ammonia'] + summary.loc[summary['Basic chemicals (without ammonia)'] < 0, 'Basic chemicals (without ammonia)'] = 0. + summary.drop(columns=['Basic chemicals'], inplace=True) + + summaries[ct] = summary final_summary = pd.concat(summaries,axis=1) From c58f18667d387b0d8991609d9b2822830790a1ac Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Mon, 7 Sep 2020 19:12:47 +0200 Subject: [PATCH 10/19] Add non-EU28 energy demand per country and sector for today Use the industrial production per sector and multiply with EU28 averages for energy per sector. --- Snakefile | 3 ++- ...d_industrial_energy_demand_per_country_today.py | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Snakefile b/Snakefile index 07868043..2f709e07 100644 --- a/Snakefile +++ b/Snakefile @@ -187,7 +187,8 @@ rule build_industrial_production_per_country_tomorrow: rule build_industrial_energy_demand_per_country_today: input: - ammonia_production="resources/ammonia_production.csv" + ammonia_production="resources/ammonia_production.csv", + industrial_production_per_country="resources/industrial_production_per_country.csv" output: industrial_energy_demand_per_country_today="resources/industrial_energy_demand_per_country_today.csv" threads: 1 diff --git a/scripts/build_industrial_energy_demand_per_country_today.py b/scripts/build_industrial_energy_demand_per_country_today.py index 7e91636f..7593477b 100644 --- a/scripts/build_industrial_energy_demand_per_country_today.py +++ b/scripts/build_industrial_energy_demand_per_country_today.py @@ -121,6 +121,20 @@ for ct in eu28: final_summary = pd.concat(summaries,axis=1) +# add in the non-EU28 based on their output (which is derived from their energy too) +# output in MtMaterial/a +output = pd.read_csv(snakemake.input.industrial_production_per_country, + index_col=0)/1e3 + +eu28_averages = final_summary.groupby(level=1,axis=1).sum().divide(output.loc[eu28].sum(),axis=1) + +non_eu28 = output.index^eu28 + +for ct in non_eu28: + print(ct) + final_summary = pd.concat((final_summary,pd.concat({ct : eu28_averages.multiply(output.loc[ct],axis=1)},axis=1)),axis=1) + + final_summary.index.name = 'TWh/a' final_summary.to_csv(snakemake.output.industrial_energy_demand_per_country_today) From 06a3b029d8b89f7841c375f1a207c8cfa5408759 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Mon, 7 Sep 2020 19:15:40 +0200 Subject: [PATCH 11/19] When changing prim/secondary production, take account of existing Don't fix uniform ratios e.g. of 0.3:0.7 primary:secondary for steel and aluminium, but convert the necessary amount of existing primary in each country so that the overall ratio applies at European level. This stops sudden swings from primary to secondary in countries dominated by primary production. --- ...ld_industrial_production_per_country_tomorrow.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/scripts/build_industrial_production_per_country_tomorrow.py b/scripts/build_industrial_production_per_country_tomorrow.py index c1d5d5da..1bfc40f2 100644 --- a/scripts/build_industrial_production_per_country_tomorrow.py +++ b/scripts/build_industrial_production_per_country_tomorrow.py @@ -6,16 +6,21 @@ industrial_production = pd.read_csv(snakemake.input.industrial_production_per_co total_steel = industrial_production[["Integrated steelworks","Electric arc"]].sum(axis=1) +fraction_primary_stays_primary = snakemake.config["industry"]["St_primary_fraction"]*total_steel.sum()/industrial_production["Integrated steelworks"].sum() + industrial_production.insert(2, "DRI + Electric arc", - snakemake.config["industry"]["St_primary_fraction"]*total_steel) -industrial_production["Electric arc"] = (1 - snakemake.config["industry"]["St_primary_fraction"])*total_steel + fraction_primary_stays_primary*industrial_production["Integrated steelworks"]) + +industrial_production["Electric arc"] = total_steel - industrial_production["DRI + Electric arc"] industrial_production["Integrated steelworks"] = 0. total_aluminium = industrial_production[["Aluminium - primary production","Aluminium - secondary production"]].sum(axis=1) -industrial_production["Aluminium - primary production"] = snakemake.config["industry"]["Al_primary_fraction"]*total_aluminium -industrial_production["Aluminium - secondary production"] = (1 - snakemake.config["industry"]["Al_primary_fraction"])*total_aluminium +fraction_primary_stays_primary = snakemake.config["industry"]["Al_primary_fraction"]*total_aluminium.sum()/industrial_production["Aluminium - primary production"].sum() + +industrial_production["Aluminium - primary production"] = fraction_primary_stays_primary*industrial_production["Aluminium - primary production"] +industrial_production["Aluminium - secondary production"] = total_aluminium - industrial_production["Aluminium - primary production"] industrial_production.to_csv(snakemake.output.industrial_production_per_country_tomorrow, From 82d7c8719e0f2217be53e26562a1dcf1b1878868 Mon Sep 17 00:00:00 2001 From: Leon <5868911+leonsn@users.noreply.github.com> Date: Thu, 17 Sep 2020 16:37:47 +0200 Subject: [PATCH 12/19] Update installation notes: PyPSA-Eur datafiles are part of the snakemake flow --- doc/installation.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/doc/installation.rst b/doc/installation.rst index c99e69bc..a7f6850f 100644 --- a/doc/installation.rst +++ b/doc/installation.rst @@ -18,7 +18,12 @@ its dependencies. Clone the repository: projects % git clone git@github.com:PyPSA/pypsa-eur.git -then download and unpack all the PyPSA-Eur data files. +then download and unpack all the PyPSA-Eur data files by running the following snakemake rule: + +.. code:: bash + + projects/pypsa-eur % conda activate pypsa-eur + projects/pypsa-eur % snakemake -j 1 retrieve_databundle Clone technology-data repository From ac4100922aa95399066c0bbec2b855cdb46901a8 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Mon, 21 Sep 2020 12:06:20 +0200 Subject: [PATCH 13/19] Include also industry new electricity load at low voltage bus For consistency with the existing industry electricity load already there. This only applies when the electricity distribution grid is inserted. --- scripts/prepare_sector_network.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index bdddd2cf..eef8206b 100644 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -655,7 +655,8 @@ def insert_electricity_distribution_grid(network): lifetime=costs.at['electricity distribution grid','lifetime'], capital_cost=costs.at['electricity distribution grid','fixed']*snakemake.config["sector"]['electricity_distribution_grid_cost_factor']) - loads = network.loads.index[network.loads.carrier=="electricity"] + #this catches regular electricity load and "industry new electricity" + loads = network.loads.index[network.loads.carrier.str.contains("electricity")] network.loads.loc[loads,"bus"] += " low voltage" bevs = network.links.index[network.links.carrier == "BEV charger"] From 1652cdc09eec704f7a18210dc69bff381f3ec682 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Mon, 21 Sep 2020 13:06:58 +0200 Subject: [PATCH 14/19] Move all intermediately-generated files from data to resources Now data only contains external input data. Now resources only contains intermediary files. --- Snakefile | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Snakefile b/Snakefile index 2f709e07..17f79dff 100644 --- a/Snakefile +++ b/Snakefile @@ -130,9 +130,9 @@ rule build_energy_totals: input: nuts3_shapes=pypsaeur('resources/nuts3_shapes.geojson') output: - energy_name='data/energy_totals.csv', - co2_name='data/co2_totals.csv', - transport_name='data/transport_data.csv' + energy_name='resources/energy_totals.csv', + co2_name='resources/co2_totals.csv', + transport_name='resources/transport_data.csv' threads: 1 resources: mem_mb=10000 script: 'scripts/build_energy_totals.py' @@ -141,7 +141,7 @@ rule build_biomass_potentials: input: jrc_potentials="data/biomass/JRC Biomass Potentials.xlsx" output: - biomass_potentials='data/biomass_potentials.csv' + biomass_potentials='resources/biomass_potentials.csv' threads: 1 resources: mem_mb=1000 script: 'scripts/build_biomass_potentials.py' @@ -221,10 +221,10 @@ rule build_industrial_demand: rule prepare_sector_network: input: network=pypsaeur('networks/{network}_s{simpl}_{clusters}_ec_lv{lv}_{opts}.nc'), - energy_totals_name='data/energy_totals.csv', - co2_totals_name='data/co2_totals.csv', - transport_name='data/transport_data.csv', - biomass_potentials='data/biomass_potentials.csv', + energy_totals_name='resources/energy_totals.csv', + co2_totals_name='resources/co2_totals.csv', + transport_name='resources/transport_data.csv', + biomass_potentials='resources/biomass_potentials.csv', timezone_mappings='data/timezone_mappings.csv', heat_profile="data/heat_load_profile_BDEW.csv", costs=config['costs_dir'] + "costs_{planning_horizons}.csv", From d727a981a2d38153e88d9aab3a1091445dff06b6 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Mon, 21 Sep 2020 13:12:03 +0200 Subject: [PATCH 15/19] Update data bundle to include USGS ammonia production by country --- doc/installation.rst | 4 ++-- doc/release_notes.rst | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/doc/installation.rst b/doc/installation.rst index c99e69bc..2d969b39 100644 --- a/doc/installation.rst +++ b/doc/installation.rst @@ -65,8 +65,8 @@ To download and extract it on the command line: .. code:: bash - projects/pypsa-eur-sec/data % wget "https://nworbmot.org/pypsa-eur-sec-data-bundle-190719.tar.gz" - projects/pypsa-eur-sec/data % tar xvzf pypsa-eur-sec-data-bundle-190719.tar.gz + projects/pypsa-eur-sec/data % wget "https://nworbmot.org/pypsa-eur-sec-data-bundle-200921.tar.gz" + projects/pypsa-eur-sec/data % tar xvzf pypsa-eur-sec-data-bundle-200921.tar.gz Set up the default configuration ================================ diff --git a/doc/release_notes.rst b/doc/release_notes.rst index 4c8fdf28..d02f79e0 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -74,3 +74,9 @@ Release Process * Make a `GitHub release `_, which automatically triggers archiving by `zenodo `_. * Send announcement on the `PyPSA mailing list `_. + +To make a new release of the data bundle, do: + +.. code:: bash + + data % tar pczf pypsa-eur-sec-data-bundle-date.tar.gz eea switzerland-sfoe biomass eurostat-energy_balances-* jrc-idees-2015 emobility urban_percent.csv timezone_mappings.csv heat_load_profile_DK_AdamJensen.csv WindWaveWEC_GLTB.xlsx myb1-2017-nitro.xls From 44ee205820f937923e6aac1b2d85b27d81d7a42b Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Mon, 21 Sep 2020 17:04:45 +0200 Subject: [PATCH 16/19] Fix recalculation of offshore connection costs for "m" clusters I.e. when the generators are clustered to the "simplified" network resolution, but the grid is clustered further, e.g. by using the clusters = 37m "m" option. --- scripts/prepare_sector_network.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index eef8206b..f635b78b 100644 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -73,6 +73,7 @@ def update_wind_solar_costs(n,costs): #map simplified network -> clustered network busmap = pd.read_hdf(snakemake.input.clustermaps, key="/busmap") + #map initial network -> clustered network clustermaps = busmap_s.map(busmap) #code adapted from pypsa-eur/scripts/add_electricity.py @@ -90,7 +91,16 @@ def update_wind_solar_costs(n,costs): #convert to aggregated clusters with weighting weight = ds['weight'].to_pandas() - connection_cost = (connection_cost*weight).groupby(clustermaps).sum()/weight.groupby(clustermaps).sum() + + #e.g. clusters == 37m means that VRE generators are left + #at clustering of simplified network, but that they are + #connected to 37-node network + if snakemake.wildcards.clusters[-1:] == "m": + genmap = busmap_s + else: + genmap = clustermaps + + connection_cost = (connection_cost*weight).groupby(genmap).sum()/weight.groupby(genmap).sum() capital_cost = (costs.at['offwind', 'fixed'] + costs.at[tech + '-station', 'fixed'] + From 791a58fc6bb4797c2e1184418f764cb6bf30ad6a Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Mon, 21 Sep 2020 18:35:45 +0200 Subject: [PATCH 17/19] Make selection of biomass classes (solid, biogas) more explicit List classes in config.yaml, rather than integer selection in build_biomass_potentials.py. Also output potentials for all years and scenarios for analysis. --- Snakefile | 1 + config.default.yaml | 4 ++++ config.myopic.yaml | 4 ++++ scripts/build_biomass_potentials.py | 27 +++++++++++++-------------- 4 files changed, 22 insertions(+), 14 deletions(-) diff --git a/Snakefile b/Snakefile index 17f79dff..45cc654d 100644 --- a/Snakefile +++ b/Snakefile @@ -141,6 +141,7 @@ rule build_biomass_potentials: input: jrc_potentials="data/biomass/JRC Biomass Potentials.xlsx" output: + biomass_potentials_all='resources/biomass_potentials_all.csv', biomass_potentials='resources/biomass_potentials.csv' threads: 1 resources: mem_mb=1000 diff --git a/config.default.yaml b/config.default.yaml index 135d7c25..292e3baf 100644 --- a/config.default.yaml +++ b/config.default.yaml @@ -48,6 +48,10 @@ electricity: biomass: year: 2030 scenario: "Med" + classes: + solid biomass: ['Primary agricultural residues', 'Forestry energy residue', 'Secondary forestry residues', 'Secondary Forestry residues – sawdust', 'Forestry residues from landscape care biomass', 'Municipal waste'] + not included: ['Bioethanol sugar beet biomass', 'Rapeseeds for biodiesel', 'sunflower and soya for Biodiesel', 'Starchy crops biomass', 'Grassy crops biomass', 'Willow biomass', 'Poplar biomass potential', 'Roundwood fuelwood', 'Roundwood Chips & Pellets'] + biogas: ['Manure biomass potential', 'Sludge biomass'] # only relevant for foresight = myopic or perfect existing_capacities: diff --git a/config.myopic.yaml b/config.myopic.yaml index 435f4eb6..0a4f21bb 100644 --- a/config.myopic.yaml +++ b/config.myopic.yaml @@ -48,6 +48,10 @@ electricity: biomass: year: 2030 scenario: "Med" + classes: + solid biomass: ['Primary agricultural residues', 'Forestry energy residue', 'Secondary forestry residues', 'Secondary Forestry residues – sawdust', 'Forestry residues from landscape care biomass', 'Municipal waste'] + not included: ['Bioethanol sugar beet biomass', 'Rapeseeds for biodiesel', 'sunflower and soya for Biodiesel', 'Starchy crops biomass', 'Grassy crops biomass', 'Willow biomass', 'Poplar biomass potential', 'Roundwood fuelwood', 'Roundwood Chips & Pellets'] + biogas: ['Manure biomass potential', 'Sludge biomass'] # only relevant for foresight = myopic or perfect existing_capacities: diff --git a/scripts/build_biomass_potentials.py b/scripts/build_biomass_potentials.py index db478778..29e09713 100644 --- a/scripts/build_biomass_potentials.py +++ b/scripts/build_biomass_potentials.py @@ -19,29 +19,28 @@ def build_biomass_potentials(): for i in range(36): df_dict[df.iloc[i*16,1]] = df.iloc[1+i*16:(i+1)*16].astype(float) - df_new = pd.concat(df_dict) + #convert from PJ to MWh + df_new = pd.concat(df_dict).rename({"UK" : "GB", "BH" : "BA"})/3.6*1e6 + df_new.index.name = "MWh/a" + df_new.to_csv(snakemake.output.biomass_potentials_all) # solid biomass includes: Primary agricultural residues (MINBIOAGRW1), - # Forestry energy residue (MINBIOFRSF1), + # Forestry energy residue (MINBIOFRSF1), # Secondary forestry residues (MINBIOWOOW1), # Secondary Forestry residues – sawdust (MINBIOWOO1a)', - # Forestry residues from landscape care biomass (MINBIOFRSF1a), + # Forestry residues from landscape care biomass (MINBIOFRSF1a), # Municipal waste (MINBIOMUN1)', - + # biogas includes : Manure biomass potential (MINBIOGAS1), # Sludge biomass (MINBIOSLU1) - - us_type = pd.Series(index=df_new.columns) - us_type.iloc[0:7] = "not included" - us_type.iloc[7:8] = "biogas" - us_type.iloc[8:9] = "solid biomass" - us_type.iloc[9:11] = "not included" - us_type.iloc[11:16] = "solid biomass" - us_type.iloc[16:17] = "biogas" + us_type = pd.Series("", df_new.columns) - #convert from PJ to MWh - biomass_potentials = df_new.loc[idx[:,snakemake.config['biomass']['year'],snakemake.config['biomass']['scenario']],:].groupby(us_type,axis=1).sum().groupby(level=0).sum().rename({"UK" : "GB", "BH" : "BA"})/3.6*1e6 + for k,v in snakemake.config['biomass']['classes'].items(): + us_type.loc[v] = k + + biomass_potentials = df_new.swaplevel(0,2).loc[snakemake.config['biomass']['scenario'],snakemake.config['biomass']['year']].groupby(us_type,axis=1).sum() + biomass_potentials.index.name = "MWh/a" biomass_potentials.to_csv(snakemake.output.biomass_potentials) From 0ebad1a2e99b5476402a5aa1a6d7b52d258ab7fa Mon Sep 17 00:00:00 2001 From: Leon <5868911+leonsn@users.noreply.github.com> Date: Mon, 21 Sep 2020 20:07:48 +0200 Subject: [PATCH 18/19] Remove the reminder to activate the conda environment --- doc/installation.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/installation.rst b/doc/installation.rst index a7f6850f..0e8cb736 100644 --- a/doc/installation.rst +++ b/doc/installation.rst @@ -22,7 +22,6 @@ then download and unpack all the PyPSA-Eur data files by running the following s .. code:: bash - projects/pypsa-eur % conda activate pypsa-eur projects/pypsa-eur % snakemake -j 1 retrieve_databundle From 7fe7dc381f3ca490d9eb6dcc731c94c41045135b Mon Sep 17 00:00:00 2001 From: lisazeyen Date: Tue, 22 Sep 2020 09:32:24 +0200 Subject: [PATCH 19/19] read config.yaml for testing with yaml.safe_load() --- scripts/add_brownfield.py | 4 ++-- scripts/add_existing_baseyear.py | 4 ++-- scripts/build_biomass_potentials.py | 4 ++-- scripts/build_industrial_demand.py | 4 ++-- scripts/make_summary.py | 4 ++-- scripts/plot_summary.py | 4 ++-- scripts/prepare_sector_network.py | 4 ++-- scripts/solve_network.py | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/scripts/add_brownfield.py b/scripts/add_brownfield.py index 43d03fbe..a0b8d97b 100644 --- a/scripts/add_brownfield.py +++ b/scripts/add_brownfield.py @@ -98,8 +98,8 @@ if __name__ == "__main__": 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: - snakemake.config = yaml.load(f) + with open('config.yaml', encoding='utf8') as f: + snakemake.config = yaml.safe_load(f) print(snakemake.input.network_p) logging.basicConfig(level=snakemake.config['logging_level']) diff --git a/scripts/add_existing_baseyear.py b/scripts/add_existing_baseyear.py index 727037a1..5010e8e7 100644 --- a/scripts/add_existing_baseyear.py +++ b/scripts/add_existing_baseyear.py @@ -423,8 +423,8 @@ if __name__ == "__main__": 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: - snakemake.config = yaml.load(f) + with open('config.yaml', encoding='utf8') as f: + snakemake.config = yaml.safe_load(f) logging.basicConfig(level=snakemake.config['logging_level']) diff --git a/scripts/build_biomass_potentials.py b/scripts/build_biomass_potentials.py index db478778..fc400360 100644 --- a/scripts/build_biomass_potentials.py +++ b/scripts/build_biomass_potentials.py @@ -58,7 +58,7 @@ if __name__ == "__main__": snakemake.input['jrc_potentials'] = "data/biomass/JRC Biomass Potentials.xlsx" snakemake.output = Dict() snakemake.output['biomass_potentials'] = 'data/biomass_potentials.csv' - with open('config.yaml') as f: - snakemake.config = yaml.load(f) + with open('config.yaml', encoding='utf8') as f: + snakemake.config = yaml.safe_load(f) build_biomass_potentials() diff --git a/scripts/build_industrial_demand.py b/scripts/build_industrial_demand.py index fce7b6ad..47efef45 100644 --- a/scripts/build_industrial_demand.py +++ b/scripts/build_industrial_demand.py @@ -33,7 +33,7 @@ if __name__ == "__main__": snakemake.input['industrial_demand_per_country']="resources/industrial_demand_per_country.csv" snakemake.output = Dict() snakemake.output['industrial_demand'] = "resources/industrial_demand_elec_s_128.csv" - with open('config.yaml') as f: - snakemake.config = yaml.load(f) + with open('config.yaml', encoding='utf8') as f: + snakemake.config = yaml.safe_load(f) build_industrial_demand() diff --git a/scripts/make_summary.py b/scripts/make_summary.py index d24064c2..2ef0285d 100644 --- a/scripts/make_summary.py +++ b/scripts/make_summary.py @@ -565,8 +565,8 @@ if __name__ == "__main__": from vresutils import Dict import yaml snakemake = Dict() - with open('config.yaml') as f: - snakemake.config = yaml.load(f) + with open('config.yaml', encoding='utf8') as f: + snakemake.config = yaml.safe_load(f) #overwrite some options snakemake.config["run"] = "test" diff --git a/scripts/plot_summary.py b/scripts/plot_summary.py index caba5f18..79064690 100644 --- a/scripts/plot_summary.py +++ b/scripts/plot_summary.py @@ -245,8 +245,8 @@ if __name__ == "__main__": from vresutils import Dict import yaml snakemake = Dict() - with open('config.yaml') as f: - snakemake.config = yaml.load(f) + with open('config.yaml', encoding='utf8') as f: + snakemake.config = yaml.safe_load(f) snakemake.input = Dict() snakemake.output = Dict() diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index bdddd2cf..dc8fcba6 100644 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -1700,8 +1700,8 @@ if __name__ == "__main__": output=['pypsa-eur-sec/results/test/prenetworks/{network}_s{simpl}_{clusters}_lv{lv}__{sector_opts}_{co2_budget_name}_{planning_horizons}.nc'] ) import yaml - with open('config.yaml') as f: - snakemake.config = yaml.load(f) + with open('config.yaml', encoding='utf8') as f: + snakemake.config = yaml.safe_load(f) logging.basicConfig(level=snakemake.config['logging_level']) diff --git a/scripts/solve_network.py b/scripts/solve_network.py index 0e2eb17c..a7078433 100644 --- a/scripts/solve_network.py +++ b/scripts/solve_network.py @@ -378,8 +378,8 @@ if __name__ == "__main__": python="logs/{network}_s{simpl}_{clusters}_lv{lv}_{sector_opts}_{co2_budget_name}_{planning_horizons}_python-test.log") ) import yaml - with open('config.yaml') as f: - snakemake.config = yaml.load(f) + with open('config.yaml', encoding='utf8') as f: + snakemake.config = yaml.safe_load(f) tmpdir = snakemake.config['solving'].get('tmpdir') if tmpdir is not None: patch_pyomo_tmpdir(tmpdir)