From f45b9a37ae0abc22c51d367085a1ab9c21e6b455 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Fri, 28 Aug 2020 19:13:18 +0200 Subject: [PATCH] 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