added option for hourly egs capacity factors
This commit is contained in:
parent
11ecbf3fba
commit
6cd46bf261
@ -511,6 +511,8 @@ sector:
|
||||
enhanced_geothermal: true
|
||||
enhanced_geothermal_optimism: false # if true, egs costs are reducing towards 2050 according to Aghahosseini et al., (2020)
|
||||
enhanced_geothermal_performant: true # if true, adds only the cheapest patch of EGS potential to each region
|
||||
enhanced_geothermal_flexible: true # if true, adds a storage unit simulating flexible operation of EGS, see Ricks et al. 2023
|
||||
enhanced_geothermal_var_cf: true # if true, adds time-dependent capacity factor to EGS, see Ricks et al. 2023
|
||||
|
||||
# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#industry
|
||||
industry:
|
||||
|
@ -705,12 +705,20 @@ rule build_transport_demand:
|
||||
|
||||
|
||||
rule build_egs_potentials:
|
||||
params:
|
||||
snapshots=config["snapshots"],
|
||||
input:
|
||||
egs_cost="data/egs_costs.json",
|
||||
shapes=RESOURCES + "regions_onshore_elec_s{simpl}_{clusters}.geojson",
|
||||
regions=RESOURCES + "regions_onshore_elec_s{simpl}_{clusters}.geojson",
|
||||
air_temperature=RESOURCES + "temp_air_total_elec_s{simpl}_{clusters}.nc"
|
||||
if config["sector"]["enhanced_geothermal_var_cf"]
|
||||
else [],
|
||||
output:
|
||||
egs_potentials=RESOURCES + "egs_potentials_s{simpl}_{clusters}.csv",
|
||||
egs_overlap=RESOURCES + "egs_overlap_s{simpl}_{clusters}.csv",
|
||||
egs_capacity_factors=RESOURCES + "egs_capacity_factors_s{simpl}_{clusters}.csv",
|
||||
if config["sector"]["enhanced_geothermal_var_cf"]
|
||||
else [],
|
||||
threads: 1
|
||||
resources:
|
||||
mem_mb=2000,
|
||||
@ -790,6 +798,9 @@ rule prepare_sector_network:
|
||||
cop_air_urban=RESOURCES + "cop_air_urban_elec_s{simpl}_{clusters}.nc",
|
||||
egs_potentials=RESOURCES + "egs_potentials_s{simpl}_{clusters}.csv",
|
||||
egs_overlap=RESOURCES + "egs_overlap_s{simpl}_{clusters}.csv",
|
||||
egs_capacity_factors=RESOURCES + "egs_capacity_factors_s{simpl}_{clusters}.csv",
|
||||
if config["sector"]["enhanced_geothermal_var_cf"]
|
||||
else [],
|
||||
solar_thermal_total=RESOURCES
|
||||
+ "solar_thermal_total_elec_s{simpl}_{clusters}.nc"
|
||||
if config["sector"]["solar_thermal"]
|
||||
|
@ -22,8 +22,10 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
import json
|
||||
|
||||
import geopandas as gpd
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import xarray as xr
|
||||
import geopandas as gpd
|
||||
from shapely.geometry import Polygon
|
||||
|
||||
|
||||
@ -84,6 +86,48 @@ def prepare_egs_data(egs_file):
|
||||
return egs_data
|
||||
|
||||
|
||||
def get_capacity_factors(
|
||||
network_regions_file,
|
||||
air_temperatures_file
|
||||
):
|
||||
"""
|
||||
Performance of EGS is higher for lower temperatures, due to more efficient air cooling
|
||||
Data from Ricks et al.: The Role of Flexible Geothermal Power in Decarbonized Elec Systems
|
||||
"""
|
||||
|
||||
delta_T = [-15, -10, -5, 0, 5, 10, 15, 20]
|
||||
cf = [1.17, 1.13, 1.07, 1, 0.925, 0.84, 0.75, 0.65]
|
||||
|
||||
x = np.linspace(-15, 20, 200)
|
||||
y = np.interp(x, delta_T, cf)
|
||||
|
||||
upper_x = np.linspace(20, 25, 50)
|
||||
m_upper = (y[-1] - y[-2]) / (x[-1] - x[-2])
|
||||
upper_y = upper_x * m_upper - x[-1] * m_upper + y[-1]
|
||||
|
||||
lower_x = np.linspace(-20, -15, 50)
|
||||
m_lower = (y[1] - y[0]) / (x[1] - x[0])
|
||||
lower_y = lower_x * m_lower - x[0] * m_lower + y[0]
|
||||
|
||||
x = np.hstack((lower_x, x, upper_x))
|
||||
y = np.hstack((lower_y, y, upper_y))
|
||||
|
||||
network_regions = gpd.read_file(network_regions_file).set_crs(epsg=4326)
|
||||
index = network_regions["name"]
|
||||
|
||||
air_temp = xr.open_dataset(air_temperatures_file)
|
||||
|
||||
snapshots = pd.date_range(freq="h", **snakemake.params.snapshots)
|
||||
capacity_factors = pd.DataFrame(index=snapshots)
|
||||
|
||||
for bus in index:
|
||||
temp = air_temp.sel(name=bus).to_dataframe()["temperature"]
|
||||
capacity_factors[bus] = np.interp((temp - temp.mean()).values, x, y)
|
||||
|
||||
return capacity_factors
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if "snakemake" not in globals():
|
||||
from _helpers import mock_snakemake
|
||||
@ -117,23 +161,23 @@ if __name__ == "__main__":
|
||||
)
|
||||
|
||||
egs_data = egs_data.loc[egs_data["PowerSust"] > 0].reset_index(drop=True)
|
||||
egs_shapes = egs_data.geometry
|
||||
egs_regions = egs_data.geometry
|
||||
|
||||
network_shapes = (
|
||||
gpd.read_file(snakemake.input.shapes)
|
||||
network_regions = (
|
||||
gpd.read_file(snakemake.input.regions)
|
||||
.set_index("name", drop=True)
|
||||
.set_crs(epsg=4326)
|
||||
)
|
||||
|
||||
overlap_matrix = pd.DataFrame(
|
||||
index=network_shapes.index,
|
||||
index=network_regions.index,
|
||||
columns=egs_data.index,
|
||||
)
|
||||
|
||||
for name, polygon in network_shapes.geometry.items():
|
||||
for name, polygon in network_regions.geometry.items():
|
||||
overlap_matrix.loc[name] = (
|
||||
egs_shapes.intersection(polygon).area
|
||||
) / egs_shapes.area
|
||||
egs_regions.intersection(polygon).area
|
||||
) / egs_regions.area
|
||||
|
||||
overlap_matrix.to_csv(snakemake.output["egs_overlap"])
|
||||
|
||||
@ -141,3 +185,10 @@ if __name__ == "__main__":
|
||||
egs_data["p_nom_max"] = egs_data["PowerSust"] / sustainability_factor
|
||||
|
||||
egs_data[["p_nom_max", "CAPEX"]].to_csv(snakemake.output["egs_potentials"])
|
||||
|
||||
capacity_factors = get_capacity_factors(
|
||||
snakemake.input["regions"],
|
||||
snakemake.input["air_temperature"],
|
||||
)
|
||||
|
||||
capacity_factors.to_csv(snakemake.output["egs_capacity_factors"])
|
Loading…
Reference in New Issue
Block a user