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

Hydrogen Gas Turbines and Retrofitting of Gas Turbines #151

Merged
merged 34 commits into from
Aug 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
46e28aa
first stab at enabling H2 and H2 retrofit gas plants
toniseibold Aug 2, 2024
c715e9a
forcing gas turbines from one year on
toniseibold Aug 2, 2024
ce3d39d
fixing efficiency of hydrogen gas turbines
toniseibold Aug 2, 2024
eaf0ffb
bug fixing params
toniseibold Aug 4, 2024
53c2400
dropping gas plants after forcing them to retrofit
toniseibold Aug 5, 2024
8bf64fa
Merge branch 'main' into retrofit_gas_plants
toniseibold Aug 6, 2024
0f8a0e7
make unravelling of oil bus consistent with new refineries
nworbmot Aug 2, 2024
23f5c51
update exporter
lindnemi Aug 2, 2024
4052c2f
ci: add validator
lkstrp Aug 2, 2024
26b3b4c
select images and add repo owner path
lkstrp Aug 5, 2024
fae4517
Update PULL_REQUEST_TEMPLATE.md
lindnemi Aug 6, 2024
0dcefde
Update Changelog.md
lindnemi Aug 6, 2024
7effbe0
update submodule
lindnemi Aug 6, 2024
651cb36
Update .pre-commit-config.yaml
lkstrp Aug 6, 2024
b2cb305
remove reuse check
lkstrp Aug 6, 2024
05633cf
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 6, 2024
b09ca68
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 6, 2024
1221164
Using config_provider for additional functionality exclusively (#154)
toniseibold Aug 6, 2024
f5c6e6f
Create .git-blame-ignore-revs (#158)
lkstrp Aug 6, 2024
61dac77
minor changes for running single node network (#160)
JulianGeis Aug 7, 2024
1cc0d65
Fix hydrogen import boundary condition (#159)
toniseibold Aug 7, 2024
42af166
first stab at enabling H2 and H2 retrofit gas plants
toniseibold Aug 2, 2024
8b49d8f
forcing gas turbines from one year on
toniseibold Aug 2, 2024
be7c89c
fix merge bugs
toniseibold Aug 7, 2024
462ba9e
change exporter to report hydrogen plants
toniseibold Aug 7, 2024
0ccb216
Merge branch 'main' into retrofit_gas_plants
toniseibold Aug 8, 2024
aa68b31
Merge branch 'main' into retrofit_gas_plants
toniseibold Aug 8, 2024
80cf0c2
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 8, 2024
0f337a3
change name of retrofitted H2
lindnemi Aug 8, 2024
f5ca661
updating exporter
lindnemi Aug 8, 2024
6d7116c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 8, 2024
558a451
fix bus0 auf H2 plants
lindnemi Aug 9, 2024
7a81f6b
finish rename of h2 retrofit
lindnemi Aug 9, 2024
67b8ffd
default efficiency = 1
lindnemi Aug 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#run
run:
prefix: 20240807unravelCH4coarse
prefix: 20240809H2_plants_retrofit
name:
# - CurrentPolicies
- KN2045_Bal_v4
Expand Down Expand Up @@ -479,6 +479,12 @@ plotting:
load: "#111100"
H2 pipeline (Kernnetz): '#6b3161'
renewable oil: '#c9c9c9'
urban central H2 retrofit CHP: '#ff0000'
H2 retrofit OCGT: '#ff0000'
H2 retrofit CCGT: '#ff0000'
H2 OCGT: '#ff0000'
H2 CCGT: '#ff0000'
urban central H2 CHP: '#ff0000'
renewable gas: '#e05b09'
countries:
- all
Expand All @@ -501,6 +507,12 @@ electricity:
custom_file: resources/german_chp.csv
estimate_renewable_capacities:
year: 2019
H2_plants_DE:
enable: true
start: 2030 # should be < force
force: 2035
cost_factor: 0.15 # repurposing cost of OCGT gas to H2 in % investment cost in EUR/MW source: Christidis et al (2023) - H2-Ready-Gaskraftwerke, Table 3 https://reiner-lemoine-institut.de/wp-content/uploads/2023/11/RLI-Studie-H2-ready_DE.pdf
efficiency_loss: 0.05

pypsa_eur:
Bus:
Expand Down
1 change: 1 addition & 0 deletions workflow/Snakefile
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ rule modify_prenetwork:
transmission_costs=config_provider("costs", "transmission"),
biomass_must_run=config_provider("must_run_biomass"),
clustering=config_provider("clustering", "temporal", "resolution_sector"),
H2_plants=config_provider("electricity", "H2_plants_DE"),
land_transport_electric_share=config_provider(
"sector", "land_transport_electric_share"
),
Expand Down
51 changes: 45 additions & 6 deletions workflow/scripts/export_ariadne_variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,17 @@ def _get_capacities(n, region, cap_func, cap_string="Capacity|", costs=None):
# Q: What about retrofitted gas power plants? -> Lisa
var[cap_string + "Electricity|Hydrogen"] = var[
cap_string + "Electricity|Hydrogen|FC"
] = capacities_electricity.get("H2 Fuel Cell", 0)
] = capacities_electricity.reindex(
[
"H2 Fuel Cell",
"H2 OCGT",
"H2 CCGT",
"urban central H2 CHP",
"H2 retrofit OCGT",
"H2 retrofit CCGT",
"urban central H2 retrofit CHP",
]
).sum()

var[cap_string + "Electricity|Nuclear"] = capacities_electricity.get("nuclear", 0)

Expand Down Expand Up @@ -796,6 +806,10 @@ def _get_capacities(n, region, cap_func, cap_string="Capacity|", costs=None):
}
).sum()

var[cap_string + "Heat|Hydrogen"] = capacities_central_heat.reindex(
["urban central H2 CHP", "urban central H2 retrofit CHP"]
).sum()

# !!! Missing in the Ariadne database

var[cap_string + "Heat|Gas"] = (
Expand Down Expand Up @@ -1362,9 +1376,17 @@ def get_secondary_energy(n, region, _industry_demand):
+ var["Secondary Energy|Electricity|Wind"]
)

var["Secondary Energy|Electricity|Hydrogen"] = electricity_supply.get(
"H2 Fuel Cell", 0
)
var["Secondary Energy|Electricity|Hydrogen"] = electricity_supply.reindex(
[
"H2 Fuel Cell",
"H2 OCGT",
"H2 CCGT",
"urban central H2 CHP",
"H2 retrofit OCGT",
"H2 retrofit CCGT",
"urban central H2 retrofit CHP",
]
).sum()
# ! Add H2 Turbines if they get implemented

var["Secondary Energy|Electricity|Waste"] = electricity_supply.filter(
Expand Down Expand Up @@ -1470,6 +1492,9 @@ def get_secondary_energy(n, region, _industry_demand):
# var["Secondary Energy|Heat|Nuclear"] = \
# var["Secondary Energy|Heat|Other"] = \
# ! Not implemented
var["Secondary Energy|Heat|Hydrogen"] = heat_supply.filter(
like="urban central H2"
).sum()

var["Secondary Energy|Heat|Oil"] = heat_supply.filter(
like="urban central oil"
Expand Down Expand Up @@ -1508,6 +1533,7 @@ def get_secondary_energy(n, region, _industry_demand):
+ var["Secondary Energy|Heat|Other"]
+ var["Secondary Energy|Heat|Coal"]
+ var["Secondary Energy|Heat|Waste"]
+ var["Secondary Energy|Heat|Hydrogen"]
)
assert isclose(
var["Secondary Energy|Heat"],
Expand Down Expand Up @@ -1671,10 +1697,23 @@ def get_secondary_energy(n, region, _industry_demand):
.multiply(MWh2PJ)
)

var["Secondary Energy Input|Hydrogen|Electricity"] = hydrogen_withdrawal.get(
"H2 Fuel Cell", 0
H2_CHP_E_usage, H2_CHP_H_usage = get_CHP_E_and_H_usage(n, "H2", region)

var["Secondary Energy Input|Hydrogen|Electricity"] = (
hydrogen_withdrawal.reindex(
[
"H2 Fuel Cell",
"H2 OCGT",
"H2 CCGT",
"H2 retrofit OCGT",
"H2 retrofit CCGT",
]
).sum()
+ H2_CHP_E_usage
)

var["Secondary Energy Input|Hydrogen|Heat"] = H2_CHP_H_usage

var["Secondary Energy Input|Hydrogen|Gases"] = hydrogen_withdrawal.get(
"Sabatier", 0
)
Expand Down
115 changes: 115 additions & 0 deletions workflow/scripts/modify_prenetwork.py
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,111 @@ def aladin_mobility_demand(n):
n.stores.loc[dsm_i].e_nom *= pd.Series(factor.values, index=dsm_i)


def add_hydrogen_turbines(n):
"""
This adds links that instead of a gas turbine use a hydrogen turbine.

It is assumed that the efficiency stays the same. This function is
only applied to German nodes.
"""
logger.info("Adding hydrogen turbine technologies for Germany.")

gas_carrier = ["OCGT", "CCGT"]
for carrier in gas_carrier:
gas_plants = n.links[
(n.links.carrier == carrier)
& (n.links.index.str[:2] == "DE")
& (n.links.p_nom_extendable)
].index
if gas_plants.empty:
continue
h2_plants = n.links.loc[gas_plants].copy()
h2_plants.carrier = h2_plants.carrier.str.replace(carrier, "H2 " + carrier)
h2_plants.index = h2_plants.index.str.replace(carrier, "H2 " + carrier)
h2_plants.bus0 = h2_plants.bus1 + " H2"
h2_plants.bus2 = ""
h2_plants.efficiency2 = 1
# add the new links
n.import_components_from_dataframe(h2_plants, "Link")

# special handling of CHPs
gas_plants = n.links[
(n.links.carrier == "urban central gas CHP")
& (n.links.index.str[:2] == "DE")
& (n.links.p_nom_extendable)
].index
h2_plants = n.links.loc[gas_plants].copy()
h2_plants.carrier = h2_plants.carrier.str.replace("gas", "H2")
h2_plants.index = h2_plants.index.str.replace("gas", "H2")
h2_plants.bus0 = h2_plants.bus1 + " H2"
h2_plants.bus3 = ""
h2_plants.efficiency3 = 1
n.import_components_from_dataframe(h2_plants, "Link")


def force_retrofit(n, params):
"""
This function forces the retrofit of gas turbines from params["force"] on.

Extendable gas links are deleted.
"""

logger.info("Forcing retrofit of gas turbines to hydrogen turbines.")

gas_carrier = ["OCGT", "CCGT", "urban central gas CHP"]
# deleting extendable gas turbine plants
to_drop = n.links[
(n.links.carrier.isin(gas_carrier))
& (n.links.p_nom_extendable)
& (n.links.index.str[:2] == "DE")
].index
n.links.drop(to_drop, inplace=True)

# forcing retrofit
for carrier in ["OCGT", "CCGT"]:
gas_plants = n.links[
(n.links.carrier == carrier)
& (n.links.index.str[:2] == "DE")
& (n.links.build_year >= params["start"])
].index
if gas_plants.empty:
continue

h2_plants = n.links.loc[gas_plants].copy()
h2_plants.carrier = h2_plants.carrier.str.replace(
carrier, "H2 retrofit " + carrier
)
h2_plants.index = h2_plants.index.str.replace(carrier, "H2 retrofit " + carrier)
h2_plants.bus0 = h2_plants.bus1 + " H2"
h2_plants.bus2 = ""
h2_plants.efficiency -= params["efficiency_loss"]
h2_plants.efficiency2 = 1 # default value
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's a bit strange that the default value is 0 for H2 plants, but 1 for H2 retro plants

h2_plants.capital_cost *= 1 + params["cost_factor"]
# add the new links
n.import_components_from_dataframe(h2_plants, "Link")
n.links.drop(gas_plants, inplace=True)

# special handling of CHPs
gas_plants = n.links[
(n.links.carrier == "urban central gas CHP")
& (n.links.index.str[:2] == "DE")
& (n.links.build_year >= params["start"])
].index
if gas_plants.empty:
return

h2_plants = n.links.loc[gas_plants].copy()
h2_plants.carrier = h2_plants.carrier.str.replace("gas", "H2 retrofit")
h2_plants.index = h2_plants.index.str.replace("gas", "H2 retrofit")
h2_plants.bus0 = h2_plants.bus1 + " H2"
h2_plants.bus3 = ""
h2_plants.efficiency -= params["efficiency_loss"]
h2_plants.efficiency3 = 1 # default value
h2_plants.capital_cost *= 1 + params["cost_factor"]
n.import_components_from_dataframe(h2_plants, "Link")
n.links.drop(gas_plants, inplace=True)


if __name__ == "__main__":
if "snakemake" not in globals():
import os
Expand Down Expand Up @@ -759,4 +864,14 @@ def aladin_mobility_demand(n):
snakemake.params.biomass_must_run["regions"],
)

if snakemake.params.H2_plants["enable"]:
if snakemake.params.H2_plants["start"] <= int(
snakemake.wildcards.planning_horizons
):
add_hydrogen_turbines(n)
if snakemake.params.H2_plants["force"] <= int(
snakemake.wildcards.planning_horizons
):
force_retrofit(n, snakemake.params.H2_plants)

n.export_to_netcdf(snakemake.output.network)