Merge pull request #278 from PyPSA/methanol

methanol in shipping
This commit is contained in:
Fabian Neumann 2022-12-28 13:51:16 +01:00 committed by GitHub
commit 642b3fa47a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 116 additions and 38 deletions

View File

@ -193,9 +193,15 @@ sector:
agriculture_machinery_electric_share: 0 agriculture_machinery_electric_share: 0
agriculture_machinery_fuel_efficiency: 0.7 # fuel oil per use agriculture_machinery_fuel_efficiency: 0.7 # fuel oil per use
agriculture_machinery_electric_efficiency: 0.3 # electricity per use agriculture_machinery_electric_efficiency: 0.3 # electricity per use
shipping_average_efficiency: 0.4 #For conversion of fuel oil to propulsion in 2011 MWh_MeOH_per_MWh_H2: 0.8787 # in LHV, source: DECHEMA (2017): Low carbon energy and feedstock for the European chemical industry , pg. 64.
MWh_MeOH_per_tCO2: 4.0321 # in LHV, source: DECHEMA (2017): Low carbon energy and feedstock for the European chemical industry , pg. 64.
MWh_MeOH_per_MWh_e: 3.6907 # in LHV, source: DECHEMA (2017): Low carbon energy and feedstock for the European chemical industry , pg. 64.
shipping_hydrogen_liquefaction: false # 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: 0 shipping_hydrogen_share: 0
shipping_methanol_share: 1
shipping_oil_share: 0
shipping_methanol_efficiency: 0.46 # 10-15% higher https://www.iea-amf.org/app/webroot/files/file/Annex%20Reports/AMF_Annex_56.pdf, https://users.ugent.be/~lsileghe/documents/extended_abstract.pdf
shipping_oil_efficiency: 0.40 #For conversion of fuel oil to propulsion in 2011
time_dep_hp_cop: true #time dependent heat pump coefficient of performance time_dep_hp_cop: true #time dependent heat pump coefficient of performance
heat_pump_sink_T: 55. # Celsius, based on DTU / large area radiators; used in build_cop_profiles.py heat_pump_sink_T: 55. # Celsius, based on DTU / large area radiators; used in build_cop_profiles.py
# conservatively high to cover hot water and space heating in poorly-insulated buildings # conservatively high to cover hot water and space heating in poorly-insulated buildings
@ -598,6 +604,9 @@ plotting:
liquid: '#25c49a' liquid: '#25c49a'
kerosene for aviation: '#a1ffe6' kerosene for aviation: '#a1ffe6'
naphtha for industry: '#57ebc4' naphtha for industry: '#57ebc4'
methanolisation: '#83d6d5'
methanol: '#468c8b'
shipping methanol: '#468c8b'
# co2 # co2
CC: '#f29dae' CC: '#f29dae'
CCS: '#f29dae' CCS: '#f29dae'
@ -614,6 +623,7 @@ plotting:
process emissions to atmosphere: '#888888' process emissions to atmosphere: '#888888'
oil emissions: '#aaaaaa' oil emissions: '#aaaaaa'
shipping oil emissions: "#555555" shipping oil emissions: "#555555"
shipping methanol emissions: '#666666'
land transport oil emissions: '#777777' land transport oil emissions: '#777777'
agriculture machinery oil emissions: '#333333' agriculture machinery oil emissions: '#333333'
# other # other

View File

@ -78,6 +78,8 @@ incorporates retrofitting options to hydrogen.
carrier can be nodally resolved or copperplated across Europe. This feature is carrier can be nodally resolved or copperplated across Europe. This feature is
controlled by ``sector: ammonia:``. controlled by ``sector: ammonia:``.
* Add methanol as energy carrier, methanolisation as process, and option for methanol demand in shipping sector.
* Updated `data bundle <https://zenodo.org/record/5824485/files/pypsa-eur-sec-data-bundle.tar.gz>`_ that includes the hydrogan salt cavern storage potentials. * Updated `data bundle <https://zenodo.org/record/5824485/files/pypsa-eur-sec-data-bundle.tar.gz>`_ that includes the hydrogan salt cavern storage potentials.
* Updated and extended documentation in <https://pypsa-eur-sec.readthedocs.io/en/latest/> * Updated and extended documentation in <https://pypsa-eur-sec.readthedocs.io/en/latest/>
@ -89,6 +91,8 @@ incorporates retrofitting options to hydrogen.
* The CO2 sequestration limit implemented as GlobalConstraint (introduced in the previous version) * The CO2 sequestration limit implemented as GlobalConstraint (introduced in the previous version)
caused a failure to read in the shadow prices of other global constraints. caused a failure to read in the shadow prices of other global constraints.
* Correct capital cost of Fischer-Tropsch according to new units in ``technology-data``.
PyPSA-Eur-Sec 0.6.0 (4 October 2021) PyPSA-Eur-Sec 0.6.0 (4 October 2021)
==================================== ====================================

View File

@ -29,7 +29,7 @@ def rename_techs_tyndp(tech):
return "gas-to-power/heat" return "gas-to-power/heat"
elif "solar" in tech: elif "solar" in tech:
return "solar" return "solar"
elif tech == "Fischer-Tropsch": elif tech in ["Fischer-Tropsch", "methanolisation"]:
return "power-to-liquid" return "power-to-liquid"
elif "offshore wind" in tech: elif "offshore wind" in tech:
return "offshore wind" return "offshore wind"

View File

@ -106,6 +106,16 @@ def define_spatial(nodes, options):
spatial.ammonia.df = pd.DataFrame(vars(spatial.ammonia), index=nodes) spatial.ammonia.df = pd.DataFrame(vars(spatial.ammonia), index=nodes)
# hydrogen
spatial.h2 = SimpleNamespace()
spatial.h2.nodes = nodes + " H2"
spatial.h2.locations = nodes
# methanol
spatial.methanol = SimpleNamespace()
spatial.methanol.nodes = ["EU methanol"]
spatial.methanol.locations = ["EU"]
# oil # oil
spatial.oil = SimpleNamespace() spatial.oil = SimpleNamespace()
spatial.oil.nodes = ["EU oil"] spatial.oil.nodes = ["EU oil"]
@ -2130,64 +2140,118 @@ def add_industry(n, costs):
p_set=industrial_demand.loc[nodes, "hydrogen"] / 8760 p_set=industrial_demand.loc[nodes, "hydrogen"] / 8760
) )
if options["shipping_hydrogen_liquefaction"]: shipping_hydrogen_share = get(options['shipping_hydrogen_share'], investment_year)
shipping_methanol_share = get(options['shipping_methanol_share'], investment_year)
shipping_oil_share = get(options['shipping_oil_share'], investment_year)
n.madd("Bus", total_share = shipping_hydrogen_share + shipping_methanol_share + shipping_oil_share
nodes, if total_share != 1:
suffix=" H2 liquid", logger.warning(f"Total shipping shares sum up to {total_share*100}%, corresponding to increased or decreased demand assumptions.")
carrier="H2 liquid",
location=nodes,
unit="MWh_LHV"
)
n.madd("Link", domestic_navigation = pop_weighted_energy_totals.loc[nodes, "total domestic navigation"].squeeze()
nodes + " H2 liquefaction", international_navigation = pd.read_csv(snakemake.input.shipping_demand, index_col=0).squeeze()
bus0=nodes + " H2",
bus1=nodes + " H2 liquid",
carrier="H2 liquefaction",
efficiency=costs.at["H2 liquefaction", 'efficiency'],
capital_cost=costs.at["H2 liquefaction", 'fixed'],
p_nom_extendable=True,
lifetime=costs.at['H2 liquefaction', 'lifetime']
)
shipping_bus = nodes + " H2 liquid"
else:
shipping_bus = nodes + " H2"
domestic_navigation = pop_weighted_energy_totals.loc[nodes, "total domestic navigation"]
international_navigation = pd.read_csv(snakemake.input.shipping_demand, index_col=0)
all_navigation = domestic_navigation + international_navigation all_navigation = domestic_navigation + international_navigation
p_set = all_navigation * 1e6 / 8760
if shipping_hydrogen_share > 0: if shipping_hydrogen_share:
efficiency = options['shipping_average_efficiency'] / costs.at["fuel cell", "efficiency"] efficiency = options['shipping_average_efficiency'] / costs.at["fuel cell", "efficiency"]
shipping_hydrogen_share = get(options['shipping_hydrogen_share'], investment_year) shipping_hydrogen_share = get(options['shipping_hydrogen_share'], investment_year)
p_set = shipping_hydrogen_share * all_navigation * 1e6 * efficiency / 8760
if options["shipping_hydrogen_liquefaction"]:
n.madd("Bus",
nodes,
suffix=" H2 liquid",
carrier="H2 liquid",
location=nodes,
unit="MWh_LHV"
)
n.madd("Link",
nodes + " H2 liquefaction",
bus0=nodes + " H2",
bus1=nodes + " H2 liquid",
carrier="H2 liquefaction",
efficiency=costs.at["H2 liquefaction", 'efficiency'],
capital_cost=costs.at["H2 liquefaction", 'fixed'],
p_nom_extendable=True,
lifetime=costs.at['H2 liquefaction', 'lifetime']
)
shipping_bus = nodes + " H2 liquid"
else:
shipping_bus = nodes + " H2"
efficiency = options['shipping_oil_efficiency'] / costs.at["fuel cell", "efficiency"]
p_set_hydrogen = shipping_hydrogen_share * p_set * efficiency
n.madd("Load", n.madd("Load",
nodes, nodes,
suffix=" H2 for shipping", suffix=" H2 for shipping",
bus=shipping_bus, bus=shipping_bus,
carrier="H2 for shipping", carrier="H2 for shipping",
p_set=p_set p_set=p_set_hydrogen
) )
if shipping_hydrogen_share < 1: if shipping_methanol_share:
shipping_oil_share = 1 - shipping_hydrogen_share n.madd("Bus",
spatial.methanol.nodes,
carrier="methanol",
location=spatial.methanol.locations,
unit="MWh_LHV"
)
p_set = shipping_oil_share * all_navigation * 1e6 / 8760. n.madd("Link",
spatial.h2.locations + " methanolisation",
bus0=spatial.h2.nodes,
bus1=spatial.methanol.nodes,
bus2=nodes,
bus3=spatial.co2.nodes,
carrier="methanolisation",
p_nom_extendable=True,
capital_cost=costs.at["methanolisation", 'fixed'] * options["MWh_MeOH_per_MWh_H2"], # EUR/MW_H2/a
lifetime=costs.at["methanolisation", 'lifetime'],
efficiency=options["MWh_MeOH_per_MWh_H2"],
efficiency2=- options["MWh_MeOH_per_MWh_H2"] / options["MWh_MeOH_per_MWh_e"],
efficiency3=- options["MWh_MeOH_per_MWh_H2"] / options["MWh_MeOH_per_tCO2"],
)
efficiency = options["shipping_oil_efficiency"] / options["shipping_methanol_efficiency"]
p_set_methanol = shipping_methanol_share * p_set.sum() * efficiency
n.madd("Load", n.madd("Load",
nodes, spatial.methanol.nodes,
suffix=" shipping methanol",
bus=spatial.methanol.nodes,
carrier="shipping methanol",
p_set=p_set_methanol,
)
# CO2 intensity methanol based on stoichiometric calculation with 22.7 GJ/t methanol (32 g/mol), CO2 (44 g/mol), 277.78 MWh/TJ = 0.218 t/MWh
co2 = p_set_methanol / options["MWh_MeOH_per_tCO2"]
n.add("Load",
"shipping methanol emissions",
bus="co2 atmosphere",
carrier="shipping methanol emissions",
p_set=-co2,
)
if shipping_oil_share:
p_set_oil = shipping_oil_share * p_set.sum()
n.madd("Load",
spatial.oil.nodes,
suffix=" shipping oil", suffix=" shipping oil",
bus=spatial.oil.nodes, bus=spatial.oil.nodes,
carrier="shipping oil", carrier="shipping oil",
p_set=p_set p_set=p_set_oil
) )
co2 = shipping_oil_share * all_navigation.sum() * 1e6 / 8760 * costs.at["oil", "CO2 intensity"] co2 = p_set_oil * costs.at["oil", "CO2 intensity"]
n.add("Load", n.add("Load",
"shipping oil emissions", "shipping oil emissions",
@ -2251,7 +2315,7 @@ def add_industry(n, costs):
bus2=spatial.co2.nodes, bus2=spatial.co2.nodes,
carrier="Fischer-Tropsch", carrier="Fischer-Tropsch",
efficiency=costs.at["Fischer-Tropsch", 'efficiency'], efficiency=costs.at["Fischer-Tropsch", 'efficiency'],
capital_cost=costs.at["Fischer-Tropsch", 'fixed'], capital_cost=costs.at["Fischer-Tropsch", 'fixed'] * costs.at["Fischer-Tropsch", 'efficiency'], # EUR/MW_H2/a
efficiency2=-costs.at["oil", 'CO2 intensity'] * costs.at["Fischer-Tropsch", 'efficiency'], efficiency2=-costs.at["oil", 'CO2 intensity'] * costs.at["Fischer-Tropsch", 'efficiency'],
p_nom_extendable=True, p_nom_extendable=True,
lifetime=costs.at['Fischer-Tropsch', 'lifetime'] lifetime=costs.at['Fischer-Tropsch', 'lifetime']