added option for hourly egs capacity factors

This commit is contained in:
LukasFrankenQ 2023-11-11 23:15:41 +00:00
parent 11ecbf3fba
commit 6cd46bf261
3 changed files with 73 additions and 9 deletions

View File

@ -511,6 +511,8 @@ sector:
enhanced_geothermal: true enhanced_geothermal: true
enhanced_geothermal_optimism: false # if true, egs costs are reducing towards 2050 according to Aghahosseini et al., (2020) 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_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 # docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#industry
industry: industry:

View File

@ -705,12 +705,20 @@ rule build_transport_demand:
rule build_egs_potentials: rule build_egs_potentials:
params:
snapshots=config["snapshots"],
input: input:
egs_cost="data/egs_costs.json", 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: output:
egs_potentials=RESOURCES + "egs_potentials_s{simpl}_{clusters}.csv", egs_potentials=RESOURCES + "egs_potentials_s{simpl}_{clusters}.csv",
egs_overlap=RESOURCES + "egs_overlap_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 threads: 1
resources: resources:
mem_mb=2000, mem_mb=2000,
@ -790,6 +798,9 @@ rule prepare_sector_network:
cop_air_urban=RESOURCES + "cop_air_urban_elec_s{simpl}_{clusters}.nc", cop_air_urban=RESOURCES + "cop_air_urban_elec_s{simpl}_{clusters}.nc",
egs_potentials=RESOURCES + "egs_potentials_s{simpl}_{clusters}.csv", egs_potentials=RESOURCES + "egs_potentials_s{simpl}_{clusters}.csv",
egs_overlap=RESOURCES + "egs_overlap_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=RESOURCES
+ "solar_thermal_total_elec_s{simpl}_{clusters}.nc" + "solar_thermal_total_elec_s{simpl}_{clusters}.nc"
if config["sector"]["solar_thermal"] if config["sector"]["solar_thermal"]

View File

@ -22,8 +22,10 @@ logger = logging.getLogger(__name__)
import json import json
import geopandas as gpd import numpy as np
import pandas as pd import pandas as pd
import xarray as xr
import geopandas as gpd
from shapely.geometry import Polygon from shapely.geometry import Polygon
@ -84,6 +86,48 @@ def prepare_egs_data(egs_file):
return egs_data 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 __name__ == "__main__":
if "snakemake" not in globals(): if "snakemake" not in globals():
from _helpers import mock_snakemake 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_data = egs_data.loc[egs_data["PowerSust"] > 0].reset_index(drop=True)
egs_shapes = egs_data.geometry egs_regions = egs_data.geometry
network_shapes = ( network_regions = (
gpd.read_file(snakemake.input.shapes) gpd.read_file(snakemake.input.regions)
.set_index("name", drop=True) .set_index("name", drop=True)
.set_crs(epsg=4326) .set_crs(epsg=4326)
) )
overlap_matrix = pd.DataFrame( overlap_matrix = pd.DataFrame(
index=network_shapes.index, index=network_regions.index,
columns=egs_data.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] = ( overlap_matrix.loc[name] = (
egs_shapes.intersection(polygon).area egs_regions.intersection(polygon).area
) / egs_shapes.area ) / egs_regions.area
overlap_matrix.to_csv(snakemake.output["egs_overlap"]) 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"] = egs_data["PowerSust"] / sustainability_factor
egs_data[["p_nom_max", "CAPEX"]].to_csv(snakemake.output["egs_potentials"]) 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"])