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:
Fabian Neumann 2021-11-29 09:12:07 +01:00 committed by GitHub
parent 7760c30d3c
commit 3ecb761b57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 118 additions and 19 deletions

View File

@ -209,6 +209,19 @@ else:
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:
input:
usgs="data/myb1-2017-nitro.xls"
@ -357,7 +370,7 @@ rule prepare_sector_network:
costs=CDIR + "costs_{planning_horizons}.csv",
profile_offwind_ac=pypsaeur("resources/profile_offwind-ac.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=pypsaeur("resources/busmap_elec_s{simpl}_{clusters}.csv"),
clustered_pop_layout="resources/pop_layout_elec_s{simpl}_{clusters}.csv",

View File

@ -238,6 +238,10 @@ sector:
co2_network: false
cc_fraction: 0.9 # default fraction of CO2 captured with post-combustion capture
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_fuel_cell_waste_heat: true
electricity_distribution_grid: false

View File

@ -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
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
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 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

1 description file/folder licence source
15 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
16 IRENA existing VRE capacities existing_infrastructure/{solar|onwind|offwind}_capcity_IRENA.csv unknown https://www.irena.org/Statistics/Download-Data
17 USGS ammonia production myb1-2017-nitro.xls unknown https://www.usgs.gov/centers/nmic/nitrogen-statistics-and-information
18 hydrogen salt cavern potentials hydrogen_salt_cavern_potentials.csv 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.1016/j.ijhydene.2019.12.161 https://doi.org/10.20944/preprints201910.0187.v1
19 hotmaps industrial site database Industrial_Database.csv CC BY 4.0 https://gitlab.com/hotmaps/industrial_sites/industrial_sites_Industrial_Database
20 Hotmaps building stock data data_building_stock.csv CC BY 4.0 https://gitlab.com/hotmaps/building-stock
21 U-values Poland u_values_poland.csv unknown https://data.europa.eu/euodp/de/data/dataset/building-stock-observatory

View File

@ -8,6 +8,9 @@ Future release
.. note::
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)
====================================

View 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)

View File

@ -1040,26 +1040,27 @@ def add_storage(n, costs):
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']:
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"]
# assumptions: weight storage potential in a country by population
# TODO: fix with real geographic potentials
# convert TWh to MWh with 1e6
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",
h2_caverns.index + " H2 Store",
bus=h2_caverns.index + " H2",
e_nom_extendable=True,
e_nom_max=h2_pot.values,
e_nom_max=h2_caverns.values,
e_cyclic=True,
carrier="H2 Store",
capital_cost=h2_capital_cost
@ -1067,7 +1068,7 @@ def add_storage(n, costs):
# hydrogen stored overground (where not already underground)
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",
nodes_overground + " H2 Store",