Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Separate basic chemicals into HVC, chlorine, methanol and ammonia #166

Merged
merged 10 commits into from
Sep 28, 2021
18 changes: 16 additions & 2 deletions config.default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ sector:
transport_fuel_cell_efficiency: 0.5
transport_internal_combustion_efficiency: 0.3
shipping_average_efficiency: 0.4 #For conversion of fuel oil to propulsion in 2011
shipping_hydrogen_liquefaction: true # whether to consider liquefaction costs for shipping H2 demands
shipping_hydrogen_liquefaction: false # whether to consider liquefaction costs for shipping H2 demands
shipping_hydrogen_share: # 1 means all hydrogen FC
2020: 0
2025: 0
Expand Down Expand Up @@ -272,9 +272,23 @@ industry:
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
HVC_primary_fraction: 1.0 #fraction of current non-ammonia basic chemicals produced via primary route
HVC_primary_fraction: 1. # fraction of today's HVC produced via primary route
HVC_mechanical_recycling_fraction: 0. # fraction of today's HVC produced via mechanical recycling
HVC_chemical_recycling_fraction: 0. # fraction of today's HVC produced via chemical recycling
HVC_production_today: 52. # MtHVC/a from DECHEMA (2017), Figure 16, page 107; includes ethylene, propylene and BTX
MWh_elec_per_tHVC_mechanical_recycling: 0.547 # from SI of https://doi.org/10.1016/j.resconrec.2020.105010, Table S5, for HDPE, PP, PS, PET. LDPE would be 0.756.
MWh_elec_per_tHVC_chemical_recycling: 6.9 # Material Economics (2019), page 125; based on pyrolysis and electric steam cracking
chlorine_production_today: 9.58 # MtCl/a from DECHEMA (2017), Table 7, page 43
MWh_elec_per_tCl: 3.6 # DECHEMA (2017), Table 6, page 43
MWh_H2_per_tCl: -0.9372 # DECHEMA (2017), page 43; negative since hydrogen produced in chloralkali process
methanol_production_today: 1.5 # MtMeOH/a from DECHEMA (2017), page 62
MWh_elec_per_tMeOH: 0.167 # DECHEMA (2017), Table 14, page 65
MWh_CH4_per_tMeOH: 10.25 # DECHEMA (2017), Table 14, page 65
hotmaps_locate_missing: false
reference_year: 2015
# references:
# DECHEMA (2017): https://dechema.de/dechema_media/Downloads/Positionspapiere/Technology_study_Low_carbon_energy_and_feedstock_for_the_European_chemical_industry-p-20002750.pdf
# Material Economics (2019): https://materialeconomics.com/latest-updates/industrial-transformation-2050

costs:
lifetime: 25 #default lifetime
Expand Down
2 changes: 2 additions & 0 deletions doc/release_notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ Future release
in the new optional rule ``build_biomass_transport_costs``.
Biomass transport can be activated with the setting ``sector: biomass_transport: true``.
* Compatibility with ``xarray`` version 0.19.
* Separate basic chemicals into HVC, chlorine, methanol and ammonia [`#166 <https://github.com/PyPSA/PyPSA-Eur-Sec/pull/166>`_].
* Add option to specify reuse, primary production, and mechanical and chemical recycling fraction of platics [`#166 <https://github.com/PyPSA/PyPSA-Eur-Sec/pull/166>`_].

PyPSA-Eur-Sec 0.5.0 (21st May 2021)
===================================
Expand Down
6 changes: 6 additions & 0 deletions scripts/build_industrial_energy_demand_per_country_today.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ def ammonia_by_fuel(x):
demand['Basic chemicals (without ammonia)'] = demand["Basic chemicals"] - demand["Ammonia"]

demand['Basic chemicals (without ammonia)'].clip(lower=0, inplace=True)

demand.drop(columns='Basic chemicals', inplace=True)

return demand
Expand All @@ -114,6 +115,11 @@ def add_non_eu28_industrial_energy_demand(demand):
fn = snakemake.input.industrial_production_per_country
production = pd.read_csv(fn, index_col=0) / 1e3

#recombine HVC, Chlorine and Methanol to Basic chemicals (without ammonia)
chemicals = ["HVC", "Chlorine", "Methanol"]
production["Basic chemicals (without ammonia)"] = production[chemicals].sum(axis=1)
production.drop(columns=chemicals, inplace=True)

eu28_production = production.loc[eu28].sum()
eu28_energy = demand.groupby(level=1).sum()
eu28_averages = eu28_energy / eu28_production
Expand Down
18 changes: 12 additions & 6 deletions scripts/build_industrial_production_per_country.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,8 @@ def industry_production(countries):
return demand


def add_ammonia_demand_separately(demand):
"""Include ammonia demand separately and remove ammonia from basic chemicals."""
def separate_basic_chemicals(demand):
"""Separate basic chemicals into ammonia, chlorine, methanol and HVC."""

ammonia = pd.read_csv(snakemake.input.ammonia_production, index_col=0)

Expand All @@ -189,7 +189,7 @@ def add_ammonia_demand_separately(demand):

print("Following countries have no ammonia demand:", missing)

demand.insert(2, "Ammonia", 0.)
demand["Ammonia"] = 0.

demand.loc[there, "Ammonia"] = ammonia.loc[there, str(year)]

Expand All @@ -198,9 +198,13 @@ def add_ammonia_demand_separately(demand):
# EE, HR and LT got negative demand through subtraction - poor data
demand['Basic chemicals'].clip(lower=0., inplace=True)

to_rename = {"Basic chemicals": "Basic chemicals (without ammonia)"}
demand.rename(columns=to_rename, inplace=True)
# assume HVC, methanol, chlorine production proportional to non-ammonia basic chemicals
distribution_key = demand["Basic chemicals"] / demand["Basic chemicals"].sum()
demand["HVC"] = config["HVC_production_today"] * 1e3 * distribution_key
demand["Chlorine"] = config["chlorine_production_today"] * 1e3 * distribution_key
demand["Methanol"] = config["methanol_production_today"] * 1e3 * distribution_key

demand.drop(columns=["Basic chemicals"], inplace=True)

if __name__ == '__main__':
if 'snakemake' not in globals():
Expand All @@ -211,12 +215,14 @@ def add_ammonia_demand_separately(demand):

year = snakemake.config['industry']['reference_year']

config = snakemake.config["industry"]

jrc_dir = snakemake.input.jrc
eurostat_dir = snakemake.input.eurostat

demand = industry_production(countries)

add_ammonia_demand_separately(demand)
separate_basic_chemicals(demand)

fn = snakemake.output.industrial_production_per_country
demand.to_csv(fn, float_format='%.2f')
7 changes: 5 additions & 2 deletions scripts/build_industrial_production_per_country_tomorrow.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,14 @@

al_primary_fraction = get(config["Al_primary_fraction"], investment_year)
fraction_persistent_primary = al_primary_fraction * total_aluminium.sum() / production[key_pri].sum()

production[key_pri] = fraction_persistent_primary * production[key_pri]
production[key_sec] = total_aluminium - production[key_pri]

production["Basic chemicals (without ammonia)"] *= config['HVC_primary_fraction']
production["HVC (mechanical recycling)"] = get(config["HVC_mechanical_recycling_fraction"], investment_year) * production["HVC"]
production["HVC (chemical recycling)"] = get(config["HVC_chemical_recycling_fraction"], investment_year) * production["HVC"]

production["HVC"] *= get(config['HVC_primary_fraction'], investment_year)

fn = snakemake.output.industrial_production_per_country_tomorrow
production.to_csv(fn, float_format='%.2f')
10 changes: 7 additions & 3 deletions scripts/build_industrial_production_per_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@
'Integrated steelworks': 'Iron and steel',
'DRI + Electric arc': 'Iron and steel',
'Ammonia': 'Chemical industry',
'Basic chemicals (without ammonia)': 'Chemical industry',
'HVC': 'Chemical industry',
'HVC (mechanical recycling)': 'Chemical industry',
'HVC (chemical recycling)': 'Chemical industry',
'Methanol': 'Chemical industry',
'Chlorine': 'Chemical industry',
'Other chemicals': 'Chemical industry',
'Pharmaceutical products etc.': 'Chemical industry',
'Cement': 'Cement',
Expand Down Expand Up @@ -40,12 +44,12 @@ def build_nodal_industrial_production():

countries = keys.country.unique()
sectors = industrial_production.columns

for country, sector in product(countries, sectors):

buses = keys.index[keys.country == country]
mapping = sector_mapping.get(sector, "population")

key = keys.loc[buses, mapping]
nodal_production.loc[buses, sector] = industrial_production.at[country, sector] * key

Expand Down
62 changes: 46 additions & 16 deletions scripts/build_industry_sector_ratios.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ def chemicals_industry():

df = pd.DataFrame(index=index)

# Basid chemicals
# Basic chemicals

sector = "Basic chemicals"

Expand Down Expand Up @@ -374,52 +374,82 @@ def chemicals_industry():
# putting in ammonia demand for H2 and electricity separately

s_emi = idees["emi"][3:57]
s_out = idees["out"][8:9]
assert s_emi.index[0] == sector
assert sector in str(s_out.index)

ammonia = pd.read_csv(snakemake.input.ammonia_production, index_col=0)

# ktNH3/a
ammonia_total = ammonia.loc[ammonia.index.intersection(eu28), str(year)].sum()

s_out -= ammonia_total
# convert from MtHVC/a to ktHVC/a
s_out = config["HVC_production_today"] * 1e3

# tCO2/t material
df.loc["process emission", sector] += (
s_emi["Process emissions"]
- config["petrochemical_process_emissions"] * 1e3
- config["NH3_process_emissions"] * 1e3
) / s_out.values
) / s_out

# emissions originating from feedstock, could be non-fossil origin
# tCO2/t material
df.loc["process emission from feedstock", sector] += (
config["petrochemical_process_emissions"] * 1e3
) / s_out.values
) / s_out

# convert from ktoe/a to GWh/a
sources = ["elec", "biomass", "methane", "hydrogen", "heat", "naphtha"]
df.loc[sources, sector] *= toe_to_MWh

# subtract ammonia energy demand (in ktNH3/a)
ammonia = pd.read_csv(snakemake.input.ammonia_production, index_col=0)
ammonia_total = ammonia.loc[ammonia.index.intersection(eu28), str(year)].sum()
df.loc["methane", sector] -= ammonia_total * config["MWh_CH4_per_tNH3_SMR"]
df.loc["elec", sector] -= ammonia_total * config["MWh_elec_per_tNH3_SMR"]

# subtract chlorine demand
chlorine_total = config["chlorine_production_today"]
df.loc["hydrogen", sector] -= chlorine_total * config["MWh_H2_per_tCl"]
df.loc["elec", sector] -= chlorine_total * config["MWh_elec_per_tCl"]

# subtract methanol demand
methanol_total = config["methanol_production_today"]
df.loc["methane", sector] -= methanol_total * config["MWh_CH4_per_tMeOH"]
df.loc["elec", sector] -= methanol_total * config["MWh_elec_per_tMeOH"]

# MWh/t material
df.loc[sources, sector] = df.loc[sources, sector] / s_out.values
df.loc[sources, sector] = df.loc[sources, sector] / s_out

to_rename = {sector: f"{sector} (without ammonia)"}
df.rename(columns=to_rename, inplace=True)
df.rename(columns={sector: "HVC"}, inplace=True)

# Ammonia
# HVC mechanical recycling

sector = "Ammonia"
sector = "HVC (mechanical recycling)"
df[sector] = 0.0
df.loc["elec", sector] = config["MWh_elec_per_tHVC_mechanical_recycling"]

# HVC chemical recycling

sector = "HVC (chemical recycling)"
df[sector] = 0.0
df.loc["elec", sector] = config["MWh_elec_per_tHVC_chemical_recycling"]

# Ammonia

sector = "Ammonia"
df[sector] = 0.0
df.loc["hydrogen", sector] = config["MWh_H2_per_tNH3_electrolysis"]
df.loc["elec", sector] = config["MWh_elec_per_tNH3_electrolysis"]

# Chlorine

sector = "Chlorine"
df[sector] = 0.0
df.loc["hydrogen", sector] = config["MWh_H2_per_tCl"]
df.loc["elec", sector] = config["MWh_elec_per_tCl"]

# Methanol

sector = "Methanol"
df[sector] = 0.0
df.loc["methane", sector] = config["MWh_CH4_per_tMeOH"]
df.loc["elec", sector] = config["MWh_elec_per_tMeOH"]

# Other chemicals

sector = "Other chemicals"
Expand Down