streamline code for year-dependent technologies (turbines/panels)
This commit is contained in:
parent
bb4eb123e5
commit
a834ff222a
@ -164,14 +164,11 @@ atlite:
|
|||||||
|
|
||||||
# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#renewable
|
# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#renewable
|
||||||
renewable:
|
renewable:
|
||||||
year: 2020
|
|
||||||
onwind:
|
onwind:
|
||||||
cutout: europe-2013-era5
|
cutout: europe-2013-era5
|
||||||
resource:
|
resource:
|
||||||
method: wind
|
method: wind
|
||||||
turbine:
|
turbine: Vestas_V112_3MW
|
||||||
2020: Vestas_V112_3MW
|
|
||||||
2030: NREL_ReferenceTurbine_2020ATB_5.5MW
|
|
||||||
add_cutout_windspeed: true
|
add_cutout_windspeed: true
|
||||||
capacity_per_sqkm: 3
|
capacity_per_sqkm: 3
|
||||||
# correction_factor: 0.93
|
# correction_factor: 0.93
|
||||||
@ -190,9 +187,7 @@ renewable:
|
|||||||
cutout: europe-2013-era5
|
cutout: europe-2013-era5
|
||||||
resource:
|
resource:
|
||||||
method: wind
|
method: wind
|
||||||
turbine:
|
turbine: NREL_ReferenceTurbine_5MW_offshore
|
||||||
2020: NREL_ReferenceTurbine_5MW_offshore.yaml
|
|
||||||
2030: NREL_ReferenceTurbine_2020ATB_15MW_offshore
|
|
||||||
add_cutout_windspeed: true
|
add_cutout_windspeed: true
|
||||||
capacity_per_sqkm: 2
|
capacity_per_sqkm: 2
|
||||||
correction_factor: 0.8855
|
correction_factor: 0.8855
|
||||||
@ -208,10 +203,7 @@ renewable:
|
|||||||
cutout: europe-2013-era5
|
cutout: europe-2013-era5
|
||||||
resource:
|
resource:
|
||||||
method: wind
|
method: wind
|
||||||
turbine:
|
turbine: Vestas_V164_7MW_offshore
|
||||||
2020: Vestas_V164_7MW_offshore
|
|
||||||
2025: NREL_ReferenceTurbine_2020ATB_15MW_offshore
|
|
||||||
2030: NREL_ReferenceTurbine_2020ATB_18MW_offshore
|
|
||||||
add_cutout_windspeed: true
|
add_cutout_windspeed: true
|
||||||
capacity_per_sqkm: 2
|
capacity_per_sqkm: 2
|
||||||
correction_factor: 0.8855
|
correction_factor: 0.8855
|
||||||
@ -227,8 +219,7 @@ renewable:
|
|||||||
cutout: europe-2013-sarah
|
cutout: europe-2013-sarah
|
||||||
resource:
|
resource:
|
||||||
method: pv
|
method: pv
|
||||||
panel:
|
panel: CSi
|
||||||
2020: CSi
|
|
||||||
orientation:
|
orientation:
|
||||||
slope: 35.
|
slope: 35.
|
||||||
azimuth: 180.
|
azimuth: 180.
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
cutout,--,"Should be a folder listed in the configuration ``atlite: cutouts:`` (e.g. 'europe-2013-era5') or reference an existing folder in the directory ``cutouts``. Source module must be ERA5.","Specifies the directory where the relevant weather data ist stored."
|
cutout,--,"Should be a folder listed in the configuration ``atlite: cutouts:`` (e.g. 'europe-2013-era5') or reference an existing folder in the directory ``cutouts``. Source module must be ERA5.","Specifies the directory where the relevant weather data ist stored."
|
||||||
resource,,,
|
resource,,,
|
||||||
-- method,--,"Must be 'wind'","A superordinate technology type."
|
-- method,--,"Must be 'wind'","A superordinate technology type."
|
||||||
-- turbine,--,"One of turbine types included in `atlite <https://github.com/PyPSA/atlite/tree/master/atlite/resources/windturbine>`_","Specifies the turbine type and its characteristic power curve."
|
-- turbine,--,"One of turbine types included in `atlite <https://github.com/PyPSA/atlite/tree/master/atlite/resources/windturbine>`_. Can be a string or a dictionary with years as keys which denote the year another turbine model becomes available.","Specifies the turbine type and its characteristic power curve."
|
||||||
capacity_per_sqkm,:math:`MW/km^2`,float,"Allowable density of wind turbine placement."
|
capacity_per_sqkm,:math:`MW/km^2`,float,"Allowable density of wind turbine placement."
|
||||||
correction_factor,--,float,"Correction factor for capacity factor time series."
|
correction_factor,--,float,"Correction factor for capacity factor time series."
|
||||||
excluder_resolution,m,float,"Resolution on which to perform geographical elibility analysis."
|
excluder_resolution,m,float,"Resolution on which to perform geographical elibility analysis."
|
||||||
|
|
@ -2,7 +2,7 @@
|
|||||||
cutout,--,"Should be a folder listed in the configuration ``atlite: cutouts:`` (e.g. 'europe-2013-era5') or reference an existing folder in the directory ``cutouts``. Source module must be ERA5.","Specifies the directory where the relevant weather data ist stored."
|
cutout,--,"Should be a folder listed in the configuration ``atlite: cutouts:`` (e.g. 'europe-2013-era5') or reference an existing folder in the directory ``cutouts``. Source module must be ERA5.","Specifies the directory where the relevant weather data ist stored."
|
||||||
resource,,,
|
resource,,,
|
||||||
-- method,--,"Must be 'wind'","A superordinate technology type."
|
-- method,--,"Must be 'wind'","A superordinate technology type."
|
||||||
-- turbine,--,"One of turbine types included in `atlite <https://github.com/PyPSA/atlite/tree/master/atlite/resources/windturbine>`__","Specifies the turbine type and its characteristic power curve."
|
-- turbine,--,"One of turbine types included in `atlite <https://github.com/PyPSA/atlite/tree/master/atlite/resources/windturbine>`_. Can be a string or a dictionary with years as keys which denote the year another turbine model becomes available.","Specifies the turbine type and its characteristic power curve."
|
||||||
capacity_per_sqkm,:math:`MW/km^2`,float,"Allowable density of wind turbine placement."
|
capacity_per_sqkm,:math:`MW/km^2`,float,"Allowable density of wind turbine placement."
|
||||||
correction_factor,--,float,"Correction factor for capacity factor time series."
|
correction_factor,--,float,"Correction factor for capacity factor time series."
|
||||||
excluder_resolution,m,float,"Resolution on which to perform geographical elibility analysis."
|
excluder_resolution,m,float,"Resolution on which to perform geographical elibility analysis."
|
||||||
|
|
@ -2,7 +2,7 @@
|
|||||||
cutout,--,"Should be a folder listed in the configuration ``atlite: cutouts:`` (e.g. 'europe-2013-era5') or reference an existing folder in the directory ``cutouts``. Source module must be ERA5.","Specifies the directory where the relevant weather data ist stored."
|
cutout,--,"Should be a folder listed in the configuration ``atlite: cutouts:`` (e.g. 'europe-2013-era5') or reference an existing folder in the directory ``cutouts``. Source module must be ERA5.","Specifies the directory where the relevant weather data ist stored."
|
||||||
resource,,,
|
resource,,,
|
||||||
-- method,--,"Must be 'wind'","A superordinate technology type."
|
-- method,--,"Must be 'wind'","A superordinate technology type."
|
||||||
-- turbine,--,"One of turbine types included in `atlite <https://github.com/PyPSA/atlite/tree/master/atlite/resources/windturbine>`__","Specifies the turbine type and its characteristic power curve."
|
-- turbine,--,"One of turbine types included in `atlite <https://github.com/PyPSA/atlite/tree/master/atlite/resources/windturbine>`_. Can be a string or a dictionary with years as keys which denote the year another turbine model becomes available.","Specifies the turbine type and its characteristic power curve."
|
||||||
capacity_per_sqkm,:math:`MW/km^2`,float,"Allowable density of wind turbine placement."
|
capacity_per_sqkm,:math:`MW/km^2`,float,"Allowable density of wind turbine placement."
|
||||||
corine,,,
|
corine,,,
|
||||||
-- grid_codes,--,"Any subset of the `CORINE Land Cover code list <http://www.eea.europa.eu/data-and-maps/data/corine-land-cover-2006-raster-1/corine-land-cover-classes-and/clc_legend.csv/at_download/file>`_","Specifies areas according to CORINE Land Cover codes which are generally eligible for wind turbine placement."
|
-- grid_codes,--,"Any subset of the `CORINE Land Cover code list <http://www.eea.europa.eu/data-and-maps/data/corine-land-cover-2006-raster-1/corine-land-cover-classes-and/clc_legend.csv/at_download/file>`_","Specifies areas according to CORINE Land Cover codes which are generally eligible for wind turbine placement."
|
||||||
|
|
@ -2,7 +2,7 @@
|
|||||||
cutout,--,"Should be a folder listed in the configuration ``atlite: cutouts:`` (e.g. 'europe-2013-era5') or reference an existing folder in the directory ``cutouts``. Source module can be ERA5 or SARAH-2.","Specifies the directory where the relevant weather data ist stored that is specified at ``atlite/cutouts`` configuration. Both ``sarah`` and ``era5`` work."
|
cutout,--,"Should be a folder listed in the configuration ``atlite: cutouts:`` (e.g. 'europe-2013-era5') or reference an existing folder in the directory ``cutouts``. Source module can be ERA5 or SARAH-2.","Specifies the directory where the relevant weather data ist stored that is specified at ``atlite/cutouts`` configuration. Both ``sarah`` and ``era5`` work."
|
||||||
resource,,,
|
resource,,,
|
||||||
-- method,--,"Must be 'pv'","A superordinate technology type."
|
-- method,--,"Must be 'pv'","A superordinate technology type."
|
||||||
-- panel,--,"One of {'Csi', 'CdTe', 'KANENA'} as defined in `atlite <https://github.com/PyPSA/atlite/tree/master/atlite/resources/solarpanel>`__","Specifies the solar panel technology and its characteristic attributes."
|
-- panel,--,"One of {'Csi', 'CdTe', 'KANENA'} as defined in `atlite <https://github.com/PyPSA/atlite/tree/master/atlite/resources/solarpanel>`_ . Can be a string or a dictionary with years as keys which denote the year another turbine model becomes available.","Specifies the solar panel technology and its characteristic attributes."
|
||||||
-- orientation,,,
|
-- orientation,,,
|
||||||
-- -- slope,°,"Realistically any angle in [0., 90.]","Specifies the tilt angle (or slope) of the solar panel. A slope of zero corresponds to the face of the panel aiming directly overhead. A positive tilt angle steers the panel towards the equator."
|
-- -- slope,°,"Realistically any angle in [0., 90.]","Specifies the tilt angle (or slope) of the solar panel. A slope of zero corresponds to the face of the panel aiming directly overhead. A positive tilt angle steers the panel towards the equator."
|
||||||
-- -- azimuth,°,"Any angle in [0., 360.]","Specifies the `azimuth <https://en.wikipedia.org/wiki/Azimuth>`_ orientation of the solar panel. South corresponds to 180.°."
|
-- -- azimuth,°,"Any angle in [0., 360.]","Specifies the `azimuth <https://en.wikipedia.org/wiki/Azimuth>`_ orientation of the solar panel. South corresponds to 180.°."
|
||||||
|
|
@ -71,6 +71,12 @@ Upcoming Release
|
|||||||
Energiewende (2021)
|
Energiewende (2021)
|
||||||
<https://static.agora-energiewende.de/fileadmin/Projekte/2021/2021_02_EU_CEAP/A-EW_254_Mobilising-circular-economy_study_WEB.pdf>`_.
|
<https://static.agora-energiewende.de/fileadmin/Projekte/2021/2021_02_EU_CEAP/A-EW_254_Mobilising-circular-economy_study_WEB.pdf>`_.
|
||||||
|
|
||||||
|
* Added option to specify turbine and solar panel models for specific years as a
|
||||||
|
dictionary (e.g. ``renewable: onwind: resource: turbine:``). The years will be
|
||||||
|
interpreted as years from when the the corresponding turbine model substitutes
|
||||||
|
the previous model for new installations. This will only have an effect on
|
||||||
|
workflows with foresight "myopic" and still needs to be added foresight option
|
||||||
|
"perfect".
|
||||||
|
|
||||||
PyPSA-Eur 0.9.0 (5th January 2024)
|
PyPSA-Eur 0.9.0 (5th January 2024)
|
||||||
==================================
|
==================================
|
||||||
|
@ -261,7 +261,6 @@ rule build_renewable_profiles:
|
|||||||
params:
|
params:
|
||||||
snapshots={k: config["snapshots"][k] for k in ["start", "end", "inclusive"]},
|
snapshots={k: config["snapshots"][k] for k in ["start", "end", "inclusive"]},
|
||||||
renewable=config["renewable"],
|
renewable=config["renewable"],
|
||||||
foresight=config["foresight"],
|
|
||||||
input:
|
input:
|
||||||
**opt,
|
**opt,
|
||||||
base_network=RESOURCES + "networks/base.nc",
|
base_network=RESOURCES + "networks/base.nc",
|
||||||
|
@ -85,10 +85,13 @@ rule add_brownfield:
|
|||||||
H2_retrofit=config["sector"]["H2_retrofit"],
|
H2_retrofit=config["sector"]["H2_retrofit"],
|
||||||
H2_retrofit_capacity_per_CH4=config["sector"]["H2_retrofit_capacity_per_CH4"],
|
H2_retrofit_capacity_per_CH4=config["sector"]["H2_retrofit_capacity_per_CH4"],
|
||||||
threshold_capacity=config["existing_capacities"]["threshold_capacity"],
|
threshold_capacity=config["existing_capacities"]["threshold_capacity"],
|
||||||
|
snapshots={k: config["snapshots"][k] for k in ["start", "end", "inclusive"]},
|
||||||
|
carriers=config["electricity"]["renewable_carriers"],
|
||||||
input:
|
input:
|
||||||
**{
|
**{
|
||||||
f"profile_{tech}": RESOURCES + f"profile_{tech}.nc"
|
f"profile_{tech}": RESOURCES + f"profile_{tech}.nc"
|
||||||
for tech in config["electricity"]["renewable_carriers"]
|
for tech in config["electricity"]["renewable_carriers"]
|
||||||
|
if tech != "hydro"
|
||||||
},
|
},
|
||||||
simplify_busmap=RESOURCES + "busmap_elec_s{simpl}.csv",
|
simplify_busmap=RESOURCES + "busmap_elec_s{simpl}.csv",
|
||||||
cluster_busmap=RESOURCES + "busmap_elec_s{simpl}_{clusters}.csv",
|
cluster_busmap=RESOURCES + "busmap_elec_s{simpl}_{clusters}.csv",
|
||||||
|
@ -145,78 +145,53 @@ def disable_grid_expansion_if_LV_limit_hit(n):
|
|||||||
n.global_constraints.drop("lv_limit", inplace=True)
|
n.global_constraints.drop("lv_limit", inplace=True)
|
||||||
|
|
||||||
|
|
||||||
def adjust_renewable_profiles(n, input_profiles, config, year):
|
def adjust_renewable_profiles(n, input_profiles, params, year):
|
||||||
"""
|
"""
|
||||||
Adjusts renewable profiles according to the renewable technology specified.
|
Adjusts renewable profiles according to the renewable technology specified,
|
||||||
|
using the latest year below or equal to the selected year.
|
||||||
If the planning horizon is not available, the closest year is used
|
|
||||||
instead.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# spatial clustering
|
||||||
cluster_busmap = pd.read_csv(snakemake.input.cluster_busmap, index_col=0).squeeze()
|
cluster_busmap = pd.read_csv(snakemake.input.cluster_busmap, index_col=0).squeeze()
|
||||||
simplify_busmap = pd.read_csv(
|
simplify_busmap = pd.read_csv(
|
||||||
snakemake.input.simplify_busmap, index_col=0
|
snakemake.input.simplify_busmap, index_col=0
|
||||||
).squeeze()
|
).squeeze()
|
||||||
clustermaps = simplify_busmap.map(cluster_busmap)
|
clustermaps = simplify_busmap.map(cluster_busmap)
|
||||||
clustermaps.index = clustermaps.index.astype(str)
|
clustermaps.index = clustermaps.index.astype(str)
|
||||||
dr = pd.date_range(**config["snapshots"], freq="H")
|
|
||||||
|
# temporal clustering
|
||||||
|
dr = pd.date_range(**params["snapshots"], freq="h")
|
||||||
snapshotmaps = (
|
snapshotmaps = (
|
||||||
pd.Series(dr, index=dr).where(lambda x: x.isin(n.snapshots), pd.NA).ffill()
|
pd.Series(dr, index=dr).where(lambda x: x.isin(n.snapshots), pd.NA).ffill()
|
||||||
)
|
)
|
||||||
|
|
||||||
for carrier in config["electricity"]["renewable_carriers"]:
|
for carrier in params["carriers"]:
|
||||||
if carrier == "hydro":
|
|
||||||
continue
|
|
||||||
|
|
||||||
clustermaps.index = clustermaps.index.astype(str)
|
|
||||||
dr = pd.date_range(**config["snapshots"], freq="H")
|
|
||||||
snapshotmaps = (
|
|
||||||
pd.Series(dr, index=dr).where(lambda x: x.isin(n.snapshots), pd.NA).ffill()
|
|
||||||
)
|
|
||||||
for carrier in config["electricity"]["renewable_carriers"]:
|
|
||||||
if carrier == "hydro":
|
if carrier == "hydro":
|
||||||
continue
|
continue
|
||||||
with xr.open_dataset(getattr(input_profiles, "profile_" + carrier)) as ds:
|
with xr.open_dataset(getattr(input_profiles, "profile_" + carrier)) as ds:
|
||||||
if ds.indexes["bus"].empty or "year" not in ds.indexes:
|
if ds.indexes["bus"].empty or "year" not in ds.indexes:
|
||||||
continue
|
continue
|
||||||
if year in ds.indexes["year"]:
|
|
||||||
p_max_pu = (
|
closest_year = max(
|
||||||
ds["year_profiles"]
|
(y for y in ds.year.values if y <= year), default=min(ds.year.values)
|
||||||
.sel(year=year)
|
|
||||||
.transpose("time", "bus")
|
|
||||||
.to_pandas()
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
available_previous_years = [
|
|
||||||
available_year
|
|
||||||
for available_year in ds.indexes["year"]
|
|
||||||
if available_year < year
|
|
||||||
]
|
|
||||||
available_following_years = [
|
|
||||||
available_year
|
|
||||||
for available_year in ds.indexes["year"]
|
|
||||||
if available_year > year
|
|
||||||
]
|
|
||||||
if available_previous_years:
|
|
||||||
closest_year = max(available_previous_years)
|
|
||||||
if available_following_years:
|
|
||||||
closest_year = min(available_following_years)
|
|
||||||
logging.warning(
|
|
||||||
f"Planning horizon {year} not in {carrier} profiles. Using closest year {closest_year} instead."
|
|
||||||
)
|
)
|
||||||
|
|
||||||
p_max_pu = (
|
p_max_pu = (
|
||||||
ds["year_profiles"]
|
ds["profile"]
|
||||||
.sel(year=closest_year)
|
.sel(year=closest_year)
|
||||||
.transpose("time", "bus")
|
.transpose("time", "bus")
|
||||||
.to_pandas()
|
.to_pandas()
|
||||||
)
|
)
|
||||||
|
|
||||||
# spatial clustering
|
# spatial clustering
|
||||||
weight = ds["weight"].to_pandas()
|
weight = ds["weight"].sel(year=closest_year).to_pandas()
|
||||||
weight = weight.groupby(clustermaps).transform(normed_or_uniform)
|
weight = weight.groupby(clustermaps).transform(normed_or_uniform)
|
||||||
p_max_pu = (p_max_pu * weight).T.groupby(clustermaps).sum().T
|
p_max_pu = (p_max_pu * weight).T.groupby(clustermaps).sum().T
|
||||||
p_max_pu.columns = p_max_pu.columns + f" {carrier}"
|
p_max_pu.columns = p_max_pu.columns + f" {carrier}"
|
||||||
|
|
||||||
# temporal_clustering
|
# temporal_clustering
|
||||||
p_max_pu = p_max_pu.groupby(snapshotmaps).mean()
|
p_max_pu = p_max_pu.groupby(snapshotmaps).mean()
|
||||||
|
|
||||||
# replace renewable time series
|
# replace renewable time series
|
||||||
n.generators_t.p_max_pu.loc[:, p_max_pu.columns] = p_max_pu
|
n.generators_t.p_max_pu.loc[:, p_max_pu.columns] = p_max_pu
|
||||||
|
|
||||||
@ -245,7 +220,7 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
n = pypsa.Network(snakemake.input.network)
|
n = pypsa.Network(snakemake.input.network)
|
||||||
|
|
||||||
adjust_renewable_profiles(n, snakemake.input, snakemake.config, year)
|
adjust_renewable_profiles(n, snakemake.input, snakemake.params, year)
|
||||||
|
|
||||||
add_build_year_to_new_assets(n, year)
|
add_build_year_to_new_assets(n, year)
|
||||||
|
|
||||||
|
@ -374,6 +374,10 @@ def attach_wind_and_solar(
|
|||||||
if ds.indexes["bus"].empty:
|
if ds.indexes["bus"].empty:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# if-statement for compatibility with old profiles
|
||||||
|
if "year" in ds.indexes:
|
||||||
|
ds = ds.sel(year=ds.year.min(), drop=True)
|
||||||
|
|
||||||
supcar = car.split("-", 2)[0]
|
supcar = car.split("-", 2)[0]
|
||||||
if supcar == "offwind":
|
if supcar == "offwind":
|
||||||
underwater_fraction = ds["underwater_fraction"].to_pandas()
|
underwater_fraction = ds["underwater_fraction"].to_pandas()
|
||||||
|
@ -200,24 +200,20 @@ if __name__ == "__main__":
|
|||||||
if "snakemake" not in globals():
|
if "snakemake" not in globals():
|
||||||
from _helpers import mock_snakemake
|
from _helpers import mock_snakemake
|
||||||
|
|
||||||
snakemake = mock_snakemake("build_renewable_profiles", technology="onwind")
|
snakemake = mock_snakemake("build_renewable_profiles", technology="offwind-dc")
|
||||||
configure_logging(snakemake)
|
configure_logging(snakemake)
|
||||||
|
|
||||||
nprocesses = int(snakemake.threads)
|
nprocesses = int(snakemake.threads)
|
||||||
noprogress = snakemake.config["run"].get("disable_progressbar", True)
|
noprogress = snakemake.config["run"].get("disable_progressbar", True)
|
||||||
noprogress = noprogress or not snakemake.config["atlite"]["show_progress"]
|
noprogress = noprogress or not snakemake.config["atlite"]["show_progress"]
|
||||||
year = snakemake.params.renewable["year"]
|
|
||||||
foresight = snakemake.params.foresight
|
|
||||||
params = snakemake.params.renewable[snakemake.wildcards.technology]
|
params = snakemake.params.renewable[snakemake.wildcards.technology]
|
||||||
resource = params["resource"] # pv panel params / wind turbine params
|
resource = params["resource"] # pv panel params / wind turbine params
|
||||||
|
|
||||||
year_dependent_techs = {
|
tech = next(t for t in ["panel", "turbine"] if t in resource)
|
||||||
k: resource.get(k)
|
models = resource[tech]
|
||||||
for k in ["panel", "turbine"]
|
if not isinstance(models, dict):
|
||||||
if isinstance(resource.get(k), dict)
|
models = {0: models}
|
||||||
}
|
resource[tech] = models[next(iter(models))]
|
||||||
for key, techs in year_dependent_techs.items():
|
|
||||||
resource[key] = resource[key][year]
|
|
||||||
|
|
||||||
correction_factor = params.get("correction_factor", 1.0)
|
correction_factor = params.get("correction_factor", 1.0)
|
||||||
capacity_per_sqkm = params["capacity_per_sqkm"]
|
capacity_per_sqkm = params["capacity_per_sqkm"]
|
||||||
@ -334,10 +330,18 @@ if __name__ == "__main__":
|
|||||||
duration = time.time() - start
|
duration = time.time() - start
|
||||||
logger.info(f"Completed average capacity factor calculation ({duration:2.2f}s)")
|
logger.info(f"Completed average capacity factor calculation ({duration:2.2f}s)")
|
||||||
|
|
||||||
logger.info("Calculate weighted capacity factor time series...")
|
profiles = []
|
||||||
|
capacities = []
|
||||||
|
for year, model in models.items():
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
f"Calculate weighted capacity factor time series for model {model}..."
|
||||||
|
)
|
||||||
start = time.time()
|
start = time.time()
|
||||||
|
|
||||||
profile, capacities = func(
|
resource[tech] = model
|
||||||
|
|
||||||
|
profile, capacity = func(
|
||||||
matrix=availability.stack(spatial=["y", "x"]),
|
matrix=availability.stack(spatial=["y", "x"]),
|
||||||
layout=layout,
|
layout=layout,
|
||||||
index=buses,
|
index=buses,
|
||||||
@ -346,34 +350,21 @@ if __name__ == "__main__":
|
|||||||
**resource,
|
**resource,
|
||||||
)
|
)
|
||||||
|
|
||||||
if year_dependent_techs and foresight != "overnight":
|
dim = {"year": [year]}
|
||||||
for key, techs in year_dependent_techs.items():
|
profile = profile.expand_dims(dim)
|
||||||
year_profiles = list()
|
capacity = capacity.expand_dims(dim)
|
||||||
tech_profiles = dict()
|
|
||||||
tech_profiles[resource[key]] = profile
|
profiles.append(profile.rename("profile"))
|
||||||
for year, tech in techs.items():
|
capacities.append(capacity.rename("weight"))
|
||||||
resource[key] = tech
|
|
||||||
if tech not in tech_profiles:
|
|
||||||
tech_profiles[tech] = func(
|
|
||||||
matrix=availability.stack(spatial=["y", "x"]),
|
|
||||||
layout=layout,
|
|
||||||
index=buses,
|
|
||||||
per_unit=True,
|
|
||||||
return_capacity=False,
|
|
||||||
**resource,
|
|
||||||
)
|
|
||||||
year_profile = tech_profiles[tech]
|
|
||||||
year_profile = year_profile.expand_dims({"year": [year]}).rename(
|
|
||||||
"year_profiles"
|
|
||||||
)
|
|
||||||
year_profiles.append(year_profile)
|
|
||||||
year_profiles = xr.merge(year_profiles)
|
|
||||||
|
|
||||||
duration = time.time() - start
|
duration = time.time() - start
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Completed weighted capacity factor time series calculation ({duration:2.2f}s)"
|
f"Completed weighted capacity factor time series calculation for model {model} ({duration:2.2f}s)"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
profiles = xr.merge(profiles)
|
||||||
|
capacities = xr.merge(capacities)
|
||||||
|
|
||||||
logger.info("Calculating maximal capacity per bus")
|
logger.info("Calculating maximal capacity per bus")
|
||||||
p_nom_max = capacity_per_sqkm * availability @ area
|
p_nom_max = capacity_per_sqkm * availability @ area
|
||||||
|
|
||||||
@ -399,17 +390,14 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
ds = xr.merge(
|
ds = xr.merge(
|
||||||
[
|
[
|
||||||
(correction_factor * profile).rename("profile"),
|
correction_factor * profiles,
|
||||||
capacities.rename("weight"),
|
capacities,
|
||||||
p_nom_max.rename("p_nom_max"),
|
p_nom_max.rename("p_nom_max"),
|
||||||
potential.rename("potential"),
|
potential.rename("potential"),
|
||||||
average_distance.rename("average_distance"),
|
average_distance.rename("average_distance"),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
if year_dependent_techs:
|
|
||||||
ds = xr.merge([ds, year_profiles * correction_factor])
|
|
||||||
|
|
||||||
if snakemake.wildcards.technology.startswith("offwind"):
|
if snakemake.wildcards.technology.startswith("offwind"):
|
||||||
logger.info("Calculate underwater fraction of connections.")
|
logger.info("Calculate underwater fraction of connections.")
|
||||||
offshore_shape = gpd.read_file(snakemake.input["offshore_shapes"]).unary_union
|
offshore_shape = gpd.read_file(snakemake.input["offshore_shapes"]).unary_union
|
||||||
@ -425,7 +413,7 @@ if __name__ == "__main__":
|
|||||||
# select only buses with some capacity and minimal capacity factor
|
# select only buses with some capacity and minimal capacity factor
|
||||||
ds = ds.sel(
|
ds = ds.sel(
|
||||||
bus=(
|
bus=(
|
||||||
(ds["profile"].mean("time") > params.get("min_p_max_pu", 0.0))
|
(ds["profile"].mean("time").max("year") > params.get("min_p_max_pu", 0.0))
|
||||||
& (ds["p_nom_max"] > params.get("min_p_nom_max", 0.0))
|
& (ds["p_nom_max"] > params.get("min_p_nom_max", 0.0))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -433,9 +421,6 @@ if __name__ == "__main__":
|
|||||||
if "clip_p_max_pu" in params:
|
if "clip_p_max_pu" in params:
|
||||||
min_p_max_pu = params["clip_p_max_pu"]
|
min_p_max_pu = params["clip_p_max_pu"]
|
||||||
ds["profile"] = ds["profile"].where(ds["profile"] >= min_p_max_pu, 0)
|
ds["profile"] = ds["profile"].where(ds["profile"] >= min_p_max_pu, 0)
|
||||||
ds["year_profiles"] = ds["year_profiles"].where(
|
|
||||||
ds["year_profiles"] >= min_p_max_pu, 0
|
|
||||||
)
|
|
||||||
|
|
||||||
ds.to_netcdf(snakemake.output.profile)
|
ds.to_netcdf(snakemake.output.profile)
|
||||||
|
|
||||||
|
@ -421,6 +421,11 @@ def update_wind_solar_costs(n, costs):
|
|||||||
tech = "offwind-" + connection
|
tech = "offwind-" + connection
|
||||||
profile = snakemake.input["profile_offwind_" + connection]
|
profile = snakemake.input["profile_offwind_" + connection]
|
||||||
with xr.open_dataset(profile) as ds:
|
with xr.open_dataset(profile) as ds:
|
||||||
|
|
||||||
|
# if-statement for compatibility with old profiles
|
||||||
|
if "year" in ds.indexes:
|
||||||
|
ds = ds.sel(year=ds.year.min(), drop=True)
|
||||||
|
|
||||||
underwater_fraction = ds["underwater_fraction"].to_pandas()
|
underwater_fraction = ds["underwater_fraction"].to_pandas()
|
||||||
connection_cost = (
|
connection_cost = (
|
||||||
snakemake.params.length_factor
|
snakemake.params.length_factor
|
||||||
|
Loading…
Reference in New Issue
Block a user