2023-03-06 08:27:45 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
2024-02-19 15:21:48 +00:00
|
|
|
# SPDX-FileCopyrightText: : 2020-2024 The PyPSA-Eur Authors
|
2023-03-06 17:49:23 +00:00
|
|
|
#
|
|
|
|
# SPDX-License-Identifier: MIT
|
2021-11-29 08:12:07 +00:00
|
|
|
"""
|
|
|
|
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
|
2024-02-12 10:53:20 +00:00
|
|
|
from _helpers import set_scenario_config
|
2021-11-29 08:12:07 +00:00
|
|
|
|
|
|
|
|
|
|
|
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
|
2023-03-06 08:27:45 +00:00
|
|
|
|
2023-10-08 09:20:36 +00:00
|
|
|
return overlay.groupby(["name", "storage_type"]).e_nom.sum().unstack("storage_type")
|
2021-11-29 08:12:07 +00:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
if "snakemake" not in globals():
|
2023-03-06 18:09:45 +00:00
|
|
|
from _helpers import mock_snakemake
|
2021-11-29 08:12:07 +00:00
|
|
|
|
2024-09-13 13:37:01 +00:00
|
|
|
snakemake = mock_snakemake("build_salt_cavern_potentials", clusters="37")
|
2021-11-29 08:12:07 +00:00
|
|
|
|
2024-02-12 10:53:20 +00:00
|
|
|
set_scenario_config(snakemake)
|
|
|
|
|
2021-11-29 08:12:07 +00:00
|
|
|
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)
|