# -*- coding: utf-8 -*-
# SPDX-FileCopyrightText: : 2017-2020 The PyPSA-Eur Authors
#
# SPDX-License-Identifier: MIT

# coding: utf-8
"""
Adds dynamic line rating timeseries to the base network.

Relevant Settings
-----------------

.. code:: yaml

    lines:
        cutout:
        line_rating:


.. seealso::
    Documentation of the configuration file ``config.yaml`
Inputs
------

- ``data/cutouts``:
- ``networks/base.nc``: confer :ref:`base`

Outputs
-------

- ``resources/line_rating.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 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 shapely.geometry import LineString as Line
from shapely.geometry import Point


def calculate_resistance(T, R_ref, T_ref=293, alpha=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, cutout):
    """
    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.
    """
    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)
    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",
            network="elec",
            simpl="",
            clusters="5",
            ll="v1.0",
            opts="Co2L-4H",
        )
    configure_logging(snakemake)
    set_scenario_config(snakemake)

    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)
    da.to_netcdf(snakemake.output[0])