add regionalised hydrogen salt cavern storage potentials (#191)
* add regionalised hydrogen salt cavern storage potentials * fix reading in salt-cavern potentials and typo in imports * by default disable nearshore and offshore salt cavern potentials Co-authored-by: lisazeyen <lisa.zeyen@web.de>
This commit is contained in:
parent
7760c30d3c
commit
3ecb761b57
15
Snakefile
15
Snakefile
@ -209,6 +209,19 @@ else:
|
|||||||
build_biomass_transport_costs_output = {}
|
build_biomass_transport_costs_output = {}
|
||||||
|
|
||||||
|
|
||||||
|
rule build_salt_cavern_potentials:
|
||||||
|
input:
|
||||||
|
salt_caverns="data/h2_salt_caverns_GWh_per_sqkm.geojson",
|
||||||
|
regions_onshore=pypsaeur("resources/regions_onshore_elec_s{simpl}_{clusters}.geojson"),
|
||||||
|
regions_offshore=pypsaeur("resources/regions_offshore_elec_s{simpl}_{clusters}.geojson"),
|
||||||
|
output:
|
||||||
|
h2_cavern_potential="resources/salt_cavern_potentials_s{simpl}_{clusters}.csv"
|
||||||
|
threads: 1
|
||||||
|
resources: mem_mb=2000
|
||||||
|
benchmark: "benchmarks/build_salt_cavern_potentials_s{simpl}_{clusters}"
|
||||||
|
script: "scripts/build_salt_cavern_potentials.py"
|
||||||
|
|
||||||
|
|
||||||
rule build_ammonia_production:
|
rule build_ammonia_production:
|
||||||
input:
|
input:
|
||||||
usgs="data/myb1-2017-nitro.xls"
|
usgs="data/myb1-2017-nitro.xls"
|
||||||
@ -357,7 +370,7 @@ rule prepare_sector_network:
|
|||||||
costs=CDIR + "costs_{planning_horizons}.csv",
|
costs=CDIR + "costs_{planning_horizons}.csv",
|
||||||
profile_offwind_ac=pypsaeur("resources/profile_offwind-ac.nc"),
|
profile_offwind_ac=pypsaeur("resources/profile_offwind-ac.nc"),
|
||||||
profile_offwind_dc=pypsaeur("resources/profile_offwind-dc.nc"),
|
profile_offwind_dc=pypsaeur("resources/profile_offwind-dc.nc"),
|
||||||
h2_cavern="data/hydrogen_salt_cavern_potentials.csv",
|
h2_cavern="resources/salt_cavern_potentials_s{simpl}_{clusters}.csv",
|
||||||
busmap_s=pypsaeur("resources/busmap_elec_s{simpl}.csv"),
|
busmap_s=pypsaeur("resources/busmap_elec_s{simpl}.csv"),
|
||||||
busmap=pypsaeur("resources/busmap_elec_s{simpl}_{clusters}.csv"),
|
busmap=pypsaeur("resources/busmap_elec_s{simpl}_{clusters}.csv"),
|
||||||
clustered_pop_layout="resources/pop_layout_elec_s{simpl}_{clusters}.csv",
|
clustered_pop_layout="resources/pop_layout_elec_s{simpl}_{clusters}.csv",
|
||||||
|
@ -238,6 +238,10 @@ sector:
|
|||||||
co2_network: false
|
co2_network: false
|
||||||
cc_fraction: 0.9 # default fraction of CO2 captured with post-combustion capture
|
cc_fraction: 0.9 # default fraction of CO2 captured with post-combustion capture
|
||||||
hydrogen_underground_storage: true
|
hydrogen_underground_storage: true
|
||||||
|
hydrogen_underground_storage_locations:
|
||||||
|
- onshore # more than 50 km from sea
|
||||||
|
# - nearshore # within 50 km of sea
|
||||||
|
# - offshore
|
||||||
use_fischer_tropsch_waste_heat: true
|
use_fischer_tropsch_waste_heat: true
|
||||||
use_fuel_cell_waste_heat: true
|
use_fuel_cell_waste_heat: true
|
||||||
electricity_distribution_grid: false
|
electricity_distribution_grid: false
|
||||||
|
@ -15,7 +15,7 @@ co2 budgets,co2_budget.csv,CC BY 4.0,https://arxiv.org/abs/2004.11009
|
|||||||
existing heating potentials,existing_infrastructure/existing_heating_raw.csv,unknown,https://ec.europa.eu/energy/studies/mapping-and-analyses-current-and-future-2020-2030-heatingcooling-fuel-deployment_en?redir=1
|
existing heating potentials,existing_infrastructure/existing_heating_raw.csv,unknown,https://ec.europa.eu/energy/studies/mapping-and-analyses-current-and-future-2020-2030-heatingcooling-fuel-deployment_en?redir=1
|
||||||
IRENA existing VRE capacities,existing_infrastructure/{solar|onwind|offwind}_capcity_IRENA.csv,unknown,https://www.irena.org/Statistics/Download-Data
|
IRENA existing VRE capacities,existing_infrastructure/{solar|onwind|offwind}_capcity_IRENA.csv,unknown,https://www.irena.org/Statistics/Download-Data
|
||||||
USGS ammonia production,myb1-2017-nitro.xls,unknown,https://www.usgs.gov/centers/nmic/nitrogen-statistics-and-information
|
USGS ammonia production,myb1-2017-nitro.xls,unknown,https://www.usgs.gov/centers/nmic/nitrogen-statistics-and-information
|
||||||
hydrogen salt cavern potentials,hydrogen_salt_cavern_potentials.csv,CC BY 4.0,https://doi.org/10.1016/j.ijhydene.2019.12.161
|
hydrogen salt cavern potentials,h2_salt_caverns_GWh_per_sqkm.geojson,CC BY 4.0,https://doi.org/10.1016/j.ijhydene.2019.12.161 https://doi.org/10.20944/preprints201910.0187.v1
|
||||||
hotmaps industrial site database,Industrial_Database.csv,CC BY 4.0,https://gitlab.com/hotmaps/industrial_sites/industrial_sites_Industrial_Database
|
hotmaps industrial site database,Industrial_Database.csv,CC BY 4.0,https://gitlab.com/hotmaps/industrial_sites/industrial_sites_Industrial_Database
|
||||||
Hotmaps building stock data,data_building_stock.csv,CC BY 4.0,https://gitlab.com/hotmaps/building-stock
|
Hotmaps building stock data,data_building_stock.csv,CC BY 4.0,https://gitlab.com/hotmaps/building-stock
|
||||||
U-values Poland,u_values_poland.csv,unknown,https://data.europa.eu/euodp/de/data/dataset/building-stock-observatory
|
U-values Poland,u_values_poland.csv,unknown,https://data.europa.eu/euodp/de/data/dataset/building-stock-observatory
|
||||||
|
|
@ -8,6 +8,9 @@ Future release
|
|||||||
.. note::
|
.. note::
|
||||||
This unreleased version currently may require the master branches of PyPSA, PyPSA-Eur, and the technology-data repository.
|
This unreleased version currently may require the master branches of PyPSA, PyPSA-Eur, and the technology-data repository.
|
||||||
|
|
||||||
|
* Add regionalised hydrogen salt cavern storage potentials from `Technical Potential of Salt Caverns for Hydrogen Storage in Europe <https://doi.org/10.20944/preprints201910.0187.v1>`_.
|
||||||
|
|
||||||
|
|
||||||
PyPSA-Eur-Sec 0.6.0 (4 October 2021)
|
PyPSA-Eur-Sec 0.6.0 (4 October 2021)
|
||||||
====================================
|
====================================
|
||||||
|
|
||||||
|
78
scripts/build_salt_cavern_potentials.py
Normal file
78
scripts/build_salt_cavern_potentials.py
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
"""
|
||||||
|
Build salt cavern potentials for hydrogen storage.
|
||||||
|
|
||||||
|
Technical Potential of Salt Caverns for Hydrogen Storage in Europe
|
||||||
|
CC-BY 4.0
|
||||||
|
https://doi.org/10.20944/preprints201910.0187.v1
|
||||||
|
https://doi.org/10.1016/j.ijhydene.2019.12.161
|
||||||
|
|
||||||
|
Figure 6. Distribution of potential salt cavern sites across Europe with their corresponding
|
||||||
|
energy densities (cavern storage potential divided by the volume).
|
||||||
|
|
||||||
|
Figure 7. Total cavern storage potential in European countries
|
||||||
|
classified as onshore, offshore and within 50 km of shore.
|
||||||
|
|
||||||
|
The regional distribution is taken from the map (Figure 6) and scaled to the
|
||||||
|
capacities from the bar chart split by nearshore (<50km from sea),
|
||||||
|
onshore (>50km from sea), offshore (Figure 7).
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
import geopandas as gpd
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
|
||||||
|
def concat_gdf(gdf_list, crs='EPSG:4326'):
|
||||||
|
"""Concatenate multiple geopandas dataframes with common coordinate reference system (crs)."""
|
||||||
|
return gpd.GeoDataFrame(pd.concat(gdf_list), crs=crs)
|
||||||
|
|
||||||
|
|
||||||
|
def load_bus_regions(onshore_path, offshore_path):
|
||||||
|
"""Load pypsa-eur on- and offshore regions and concat."""
|
||||||
|
|
||||||
|
bus_regions_offshore = gpd.read_file(offshore_path)
|
||||||
|
bus_regions_onshore = gpd.read_file(onshore_path)
|
||||||
|
bus_regions = concat_gdf([bus_regions_offshore, bus_regions_onshore])
|
||||||
|
bus_regions = bus_regions.dissolve(by='name', aggfunc='sum')
|
||||||
|
|
||||||
|
return bus_regions
|
||||||
|
|
||||||
|
|
||||||
|
def area(gdf):
|
||||||
|
"""Returns area of GeoDataFrame geometries in square kilometers."""
|
||||||
|
return gdf.to_crs(epsg=3035).area.div(1e6)
|
||||||
|
|
||||||
|
|
||||||
|
def salt_cavern_potential_by_region(caverns, regions):
|
||||||
|
|
||||||
|
# calculate area of caverns shapes
|
||||||
|
caverns["area_caverns"] = area(caverns)
|
||||||
|
|
||||||
|
overlay = gpd.overlay(regions.reset_index(), caverns, keep_geom_type=True)
|
||||||
|
|
||||||
|
# calculate share of cavern area inside region
|
||||||
|
overlay["share"] = area(overlay) / overlay["area_caverns"]
|
||||||
|
|
||||||
|
overlay["e_nom"] = overlay.eval("capacity_per_area * share * area_caverns / 1000") # TWh
|
||||||
|
|
||||||
|
caverns_regions = overlay.groupby(['name', "storage_type"]).e_nom.sum().unstack("storage_type")
|
||||||
|
|
||||||
|
return caverns_regions
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
if 'snakemake' not in globals():
|
||||||
|
from helper import mock_snakemake
|
||||||
|
snakemake = mock_snakemake('build_salt_cavern_potentials', simpl='', clusters='37')
|
||||||
|
|
||||||
|
|
||||||
|
fn_onshore = snakemake.input.regions_onshore
|
||||||
|
fn_offshore = snakemake.input.regions_offshore
|
||||||
|
|
||||||
|
regions = load_bus_regions(fn_onshore, fn_offshore)
|
||||||
|
|
||||||
|
caverns = gpd.read_file(snakemake.input.salt_caverns) # GWh/sqkm
|
||||||
|
|
||||||
|
caverns_regions = salt_cavern_potential_by_region(caverns, regions)
|
||||||
|
|
||||||
|
caverns_regions.to_csv(snakemake.output.h2_cavern_potential)
|
@ -1040,26 +1040,27 @@ def add_storage(n, costs):
|
|||||||
lifetime=costs.at['fuel cell', 'lifetime']
|
lifetime=costs.at['fuel cell', 'lifetime']
|
||||||
)
|
)
|
||||||
|
|
||||||
cavern_nodes = pd.DataFrame()
|
cavern_types = snakemake.config["sector"]["hydrogen_underground_storage_locations"]
|
||||||
|
h2_caverns = pd.read_csv(snakemake.input.h2_cavern, index_col=0)[cavern_types].sum(axis=1)
|
||||||
|
|
||||||
|
# only use sites with at least 2 TWh potential
|
||||||
|
h2_caverns = h2_caverns[h2_caverns > 2]
|
||||||
|
|
||||||
|
# convert TWh to MWh
|
||||||
|
h2_caverns = h2_caverns * 1e6
|
||||||
|
|
||||||
|
# clip at 1000 TWh for one location
|
||||||
|
h2_caverns.clip(upper=1e9, inplace=True)
|
||||||
|
|
||||||
if options['hydrogen_underground_storage']:
|
if options['hydrogen_underground_storage']:
|
||||||
h2_salt_cavern_potential = pd.read_csv(snakemake.input.h2_cavern, index_col=0, squeeze=True)
|
|
||||||
h2_cavern_ct = h2_salt_cavern_potential[~h2_salt_cavern_potential.isna()]
|
|
||||||
cavern_nodes = pop_layout[pop_layout.ct.isin(h2_cavern_ct.index)]
|
|
||||||
|
|
||||||
h2_capital_cost = costs.at["hydrogen storage underground", "fixed"]
|
h2_capital_cost = costs.at["hydrogen storage underground", "fixed"]
|
||||||
|
|
||||||
# assumptions: weight storage potential in a country by population
|
n.madd("Store",
|
||||||
# TODO: fix with real geographic potentials
|
h2_caverns.index + " H2 Store",
|
||||||
# convert TWh to MWh with 1e6
|
bus=h2_caverns.index + " H2",
|
||||||
h2_pot = h2_cavern_ct.loc[cavern_nodes.ct]
|
|
||||||
h2_pot.index = cavern_nodes.index
|
|
||||||
h2_pot = h2_pot * cavern_nodes.fraction * 1e6
|
|
||||||
|
|
||||||
n.madd("Store",
|
|
||||||
cavern_nodes.index + " H2 Store",
|
|
||||||
bus=cavern_nodes.index + " H2",
|
|
||||||
e_nom_extendable=True,
|
e_nom_extendable=True,
|
||||||
e_nom_max=h2_pot.values,
|
e_nom_max=h2_caverns.values,
|
||||||
e_cyclic=True,
|
e_cyclic=True,
|
||||||
carrier="H2 Store",
|
carrier="H2 Store",
|
||||||
capital_cost=h2_capital_cost
|
capital_cost=h2_capital_cost
|
||||||
@ -1067,7 +1068,7 @@ def add_storage(n, costs):
|
|||||||
|
|
||||||
# hydrogen stored overground (where not already underground)
|
# hydrogen stored overground (where not already underground)
|
||||||
h2_capital_cost = costs.at["hydrogen storage tank incl. compressor", "fixed"]
|
h2_capital_cost = costs.at["hydrogen storage tank incl. compressor", "fixed"]
|
||||||
nodes_overground = cavern_nodes.index.symmetric_difference(nodes)
|
nodes_overground = h2_caverns.index.symmetric_difference(nodes)
|
||||||
|
|
||||||
n.madd("Store",
|
n.madd("Store",
|
||||||
nodes_overground + " H2 Store",
|
nodes_overground + " H2 Store",
|
||||||
|
Loading…
Reference in New Issue
Block a user