diff --git a/config.default.yaml b/config.default.yaml index 4078ee01..6875c4ef 100644 --- a/config.default.yaml +++ b/config.default.yaml @@ -193,9 +193,15 @@ sector: agriculture_machinery_electric_share: 0 agriculture_machinery_fuel_efficiency: 0.7 # fuel oil 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_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 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 @@ -598,6 +604,9 @@ plotting: liquid: '#25c49a' kerosene for aviation: '#a1ffe6' naphtha for industry: '#57ebc4' + methanolisation: '#83d6d5' + methanol: '#468c8b' + shipping methanol: '#468c8b' # co2 CC: '#f29dae' CCS: '#f29dae' @@ -614,6 +623,7 @@ plotting: process emissions to atmosphere: '#888888' oil emissions: '#aaaaaa' shipping oil emissions: "#555555" + shipping methanol emissions: '#666666' land transport oil emissions: '#777777' agriculture machinery oil emissions: '#333333' # other diff --git a/doc/release_notes.rst b/doc/release_notes.rst index be66a9ba..918f885c 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -78,6 +78,8 @@ incorporates retrofitting options to hydrogen. carrier can be nodally resolved or copperplated across Europe. This feature is controlled by ``sector: ammonia:``. +* Add methanol as energy carrier, methanolisation as process, and option for methanol demand in shipping sector. + * Updated `data bundle `_ that includes the hydrogan salt cavern storage potentials. * Updated and extended documentation in @@ -89,6 +91,8 @@ incorporates retrofitting options to hydrogen. * 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. +* Correct capital cost of Fischer-Tropsch according to new units in ``technology-data``. + PyPSA-Eur-Sec 0.6.0 (4 October 2021) ==================================== diff --git a/scripts/plot_network.py b/scripts/plot_network.py index 11cfc84e..caedb335 100644 --- a/scripts/plot_network.py +++ b/scripts/plot_network.py @@ -29,7 +29,7 @@ def rename_techs_tyndp(tech): return "gas-to-power/heat" elif "solar" in tech: return "solar" - elif tech == "Fischer-Tropsch": + elif tech in ["Fischer-Tropsch", "methanolisation"]: return "power-to-liquid" elif "offshore wind" in tech: return "offshore wind" diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index 011eb97e..b40c2084 100644 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -106,6 +106,16 @@ def define_spatial(nodes, options): 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 spatial.oil = SimpleNamespace() spatial.oil.nodes = ["EU oil"] @@ -2130,64 +2140,118 @@ def add_industry(n, costs): 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", - nodes, - suffix=" H2 liquid", - carrier="H2 liquid", - location=nodes, - unit="MWh_LHV" - ) + total_share = shipping_hydrogen_share + shipping_methanol_share + shipping_oil_share + if total_share != 1: + logger.warning(f"Total shipping shares sum up to {total_share*100}%, corresponding to increased or decreased demand assumptions.") - 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" - - domestic_navigation = pop_weighted_energy_totals.loc[nodes, "total domestic navigation"] - international_navigation = pd.read_csv(snakemake.input.shipping_demand, index_col=0) + domestic_navigation = pop_weighted_energy_totals.loc[nodes, "total domestic navigation"].squeeze() + international_navigation = pd.read_csv(snakemake.input.shipping_demand, index_col=0).squeeze() 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"] 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", nodes, suffix=" H2 for shipping", bus=shipping_bus, 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", - 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", bus=spatial.oil.nodes, 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", "shipping oil emissions", @@ -2251,7 +2315,7 @@ def add_industry(n, costs): bus2=spatial.co2.nodes, carrier="Fischer-Tropsch", 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'], p_nom_extendable=True, lifetime=costs.at['Fischer-Tropsch', 'lifetime']