pypsa-eur/scripts/build_line_rating.py
Fabian Neumann 013b705ee4
Clustering: build renewable profiles and add all assets after clustering (#1201)
* Cluster first: build renewable profiles and add all assets after clustering

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* correction: pass landfall_lengths through functions

* assign landfall_lenghts correctly

* remove parameter add_land_use_constraint

* fix network_dict

* calculate distance to shoreline, remove underwater_fraction

* adjust simplification parameter to exclude Crete from offshore wind connections

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* remove unused geth2015 hydro capacities

* removing remaining traces of {simpl} wildcard

* add release notes and update workflow graphics

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: lisazeyen <lisa.zeyen@web.de>
2024-09-13 15:37:01 +02:00

180 lines
5.2 KiB
Python
Executable File

# -*- coding: utf-8 -*-
# SPDX-FileCopyrightText: : 2017-2020 The PyPSA-Eur Authors
#
# SPDX-License-Identifier: MIT
# coding: utf-8
"""
Calculates dynamic line rating time series from base network.
Relevant Settings
-----------------
.. code:: yaml
lines:
cutout:
dynamic_line_rating:
.. seealso::
Documentation of the configuration file ``config.yaml`
Inputs
------
- ``data/cutouts``:
- ``networks/base.nc``: confer :ref:`base`
Outputs
-------
- ``resources/dlr.nc``
Description
-----------
The rule :mod:`build_line_rating` calculates the line rating for transmission lines.
The line rating provides the maximal capacity of a transmission line considering the heat exchange with the environment.
The following heat gains and losses are considered:
- heat gain through resistive losses
- heat gain through solar radiation
- heat loss through radiation of the transmission line
- heat loss through forced convection with wind
- heat loss through natural convection
With a heat balance considering the maximum temperature threshold of the transmission line,
the maximal possible capacity factor "s_max_pu" for each transmission line at each time step is calculated.
"""
import logging
import re
import atlite
import geopandas as gpd
import numpy as np
import pypsa
import xarray as xr
from _helpers import configure_logging, get_snapshots, set_scenario_config
from dask.distributed import Client
from shapely.geometry import LineString as Line
from shapely.geometry import Point
logger = logging.getLogger(__name__)
def calculate_resistance(T, R_ref, T_ref: float | int = 293, alpha: float = 0.00403):
"""
Calculates the resistance at other temperatures than the reference
temperature.
Parameters
----------
T : Temperature at which resistance is calculated in [°C] or [K]
R_ref : Resistance at reference temperature in [Ohm] or [Ohm/Per Length Unit]
T_ref : Reference temperature in [°C] or [K]
alpha: Temperature coefficient in [1/K]
Defaults are:
* T_ref : 20 °C
* alpha : 0.00403 1/K
Returns
-------
Resistance of at given temperature.
"""
return R_ref * (1 + alpha * (T - T_ref))
def calculate_line_rating(
n: pypsa.Network,
cutout: atlite.Cutout,
show_progress: bool = True,
dask_kwargs: dict = None,
) -> xr.DataArray:
"""
Calculates the maximal allowed power flow in each line for each time step
considering the maximal temperature.
Parameters
----------
n : pypsa.Network object containing information on grid
Returns
-------
xarray DataArray object with maximal power.
"""
if dask_kwargs is None:
dask_kwargs = {}
logger.info("Calculating dynamic line rating.")
relevant_lines = n.lines[~n.lines["underground"]].copy()
buses = relevant_lines[["bus0", "bus1"]].values
x = n.buses.x
y = n.buses.y
shapes = [Line([Point(x[b0], y[b0]), Point(x[b1], y[b1])]) for (b0, b1) in buses]
shapes = gpd.GeoSeries(shapes, index=relevant_lines.index)
if relevant_lines.r_pu.eq(0).all():
# Overwrite standard line resistance with line resistance obtained from line type
r_per_length = n.line_types["r_per_length"]
R = (
relevant_lines.join(r_per_length, on=["type"])["r_per_length"] / 1000
) # in meters
# If line type with bundles is given retrieve number of conductors per bundle
relevant_lines["n_bundle"] = (
relevant_lines["type"]
.where(relevant_lines["type"].str.contains("bundle"))
.dropna()
.apply(lambda x: int(re.findall(r"(\d+)-bundle", x)[0]))
)
# Set default number of bundles per line
relevant_lines["n_bundle"] = relevant_lines["n_bundle"].fillna(1)
R *= relevant_lines["n_bundle"]
R = calculate_resistance(T=353, R_ref=R)
Imax = cutout.line_rating(
shapes,
R,
D=0.0218,
Ts=353,
epsilon=0.8,
alpha=0.8,
show_progress=show_progress,
dask_kwargs=dask_kwargs,
)
line_factor = relevant_lines.eval("v_nom * n_bundle * num_parallel") / 1e3 # in mW
return xr.DataArray(
data=np.sqrt(3) * Imax * line_factor.values.reshape(-1, 1),
attrs=dict(
description="Maximal possible power in MW for given line considering line rating"
),
)
if __name__ == "__main__":
if "snakemake" not in globals():
from _helpers import mock_snakemake
snakemake = mock_snakemake("build_line_rating")
configure_logging(snakemake)
set_scenario_config(snakemake)
nprocesses = int(snakemake.threads)
show_progress = not snakemake.config["run"].get("disable_progressbar", True)
show_progress = show_progress and snakemake.config["atlite"]["show_progress"]
if nprocesses > 1:
client = Client(n_workers=nprocesses, threads_per_worker=1)
else:
client = None
dask_kwargs = {"scheduler": client}
n = pypsa.Network(snakemake.input.base_network)
time = get_snapshots(snakemake.params.snapshots, snakemake.params.drop_leap_day)
cutout = atlite.Cutout(snakemake.input.cutout).sel(time=time)
da = calculate_line_rating(n, cutout, show_progress, dask_kwargs)
da.to_netcdf(snakemake.output[0])