# -*- coding: utf-8 -*-

# Copyright (c) 2016-2026 by University of Kassel and Fraunhofer Institute for Energy Economics
# and Energy System Technology (IEE), Kassel. All rights reserved.

from __future__ import annotations

import logging
from operator import itemgetter
from typing import Iterable, Sequence

from numpy import nan, isnan, any as np_any, bool_, all as np_all, float64
import numpy.typing as npt

from pandapower.auxiliary import pandapowerNet
from pandapower.std_types import load_std_type
from pandapower.pp_types import Int, LineType
from pandapower.create._utils import (
    _add_branch_geodata,
    _add_multiple_branch_geodata,
    _add_to_entries_if_not_nan,
    _check_branch_element,
    _check_multiple_branch_elements,
    _get_index_with_check,
    _get_multiple_index_with_check,
    _set_entries,
    _set_multiple_entries,
    _set_value_if_not_nan,
)

logger = logging.getLogger(__name__)


def create_line(
    net: pandapowerNet,
    from_bus: Int,
    to_bus: Int,
    length_km: float,
    std_type: str,
    name: str | None = None,
    index: Int | None = None,
    geodata: Iterable[tuple[float, float]] | None = None,
    df: float = 1.0,
    parallel: int = 1,
    in_service: bool = True,
    max_loading_percent: float = nan,
    alpha: float = nan,
    temperature_degree_celsius: float = nan,
    **kwargs,
) -> Int:
    """
    Creates a line element in net["line"]
    The line parameters are defined through the standard type library.

    Parameters:
        net: The net within this line should be created
        from_bus: ID of the bus on one side which the line will be connected with
        to_bus: ID of the bus on the other side which the line will be connected with
        length_km: The line length in km
        std_type: Name of a standard line type:
            - Pre-defined in standard_linetypes
            - Customized std_type made using **create_std_type()**
        name: A custom name for this line
        index: Force a specified ID if it is available. If None, the index one higher than the highest already existing
            index is selected.
        geodata: Iterable[Tuple[int, int]|Tuple[float, float]], The geodata of the line. The first element should be the
            coordinates of from_bus and the last should be the coordinates of to_bus. The points in the middle represent
            the bending points of the line
        in_service: True for in_service or False for out of service
        df: derating factor: maximum current of line in relation to nominal current of line (from 0 to 1)
        parallel: number of parallel line systems
        max_loading_percent: maximum current loading (only needed for OPF)
        alpha: temperature coefficient of resistance: R(T) = R(T_0) * (1 + alpha * (T - T_0))
        temperature_degree_celsius: line temperature for which line resistance is adjusted
        
    Keyword arguments:
        tdpf (bool): whether the line is considered in the TDPF calculation
        wind_speed_m_per_s (float): wind speed at the line in m/s (TDPF)
        wind_angle_degree (float): angle of attack between the wind direction and the line (TDPF)
        conductor_outer_diameter_m (float): outer diameter of the line conductor in m (TDPF)
        air_temperature_degree_celsius (float): ambient temperature in °C (TDPF)
        reference_temperature_degree_celsius (float): reference temperature in °C for which r_ohm_per_km for the line is
            specified (TDPF)
        solar_radiation_w_per_sq_m (float): solar radiation on horizontal plane in W/m² (TDPF)
        solar_absorptivity: Albedo factor for absorptivity of the lines (TDPF)
        emissivity (float): Albedo factor for emissivity of the lines (TDPF)
        r_theta_kelvin_per_mw (float): thermal resistance of the line (TDPF, only for simplified method)
        mc_joule_per_m_k (float): specific mass of the conductor multiplied by the specific thermal capacity of the
            material (TDPF, only for thermal inertia consideration with tdpf_delay_s parameter)

    Returns:
        The unique ID of the created line

    Example:
        >>> create_line(net, from_bus=0, to_bus=1, length_km=0.1,  std_type="NAYY 4x50 SE", name="line1")
    """
    # check if bus exist to attach the line to
    _check_branch_element(net, "Line", index, from_bus, to_bus)

    index = _get_index_with_check(net, "line", index)

    tdpf_columns = (
        "wind_speed_m_per_s",
        "wind_angle_degree",
        "conductor_outer_diameter_m",
        "air_temperature_degree_celsius",
        "reference_temperature_degree_celsius",
        "solar_radiation_w_per_sq_m",
        "solar_absorptivity",
        "emissivity",
        "r_theta_kelvin_per_mw",
        "mc_joule_per_m_k",
    )
    tdpf_parameters = {c: kwargs.pop(c) for c in tdpf_columns if c in kwargs}

    entries = {
        "name": name,
        "length_km": length_km,
        "from_bus": from_bus,
        "to_bus": to_bus,
        "in_service": in_service,
        "std_type": std_type,
        "df": df,
        "parallel": parallel,
        **kwargs,
    }

    lineparam = load_std_type(net, std_type, "line")

    entries.update({param: lineparam[param] for param in ["r_ohm_per_km", "x_ohm_per_km", "c_nf_per_km", "max_i_ka"]})
    if "r0_ohm_per_km" in lineparam:
        entries.update({param: lineparam[param] for param in ["r0_ohm_per_km", "x0_ohm_per_km", "c0_nf_per_km"]})

    entries["g_us_per_km"] = lineparam["g_us_per_km"] if "g_us_per_km" in lineparam else 0.0

    if "type" in lineparam:
        entries["type"] = lineparam["type"]

    # if net.line column already has alpha, add it from std_type
    if "alpha" in net.line.columns and "alpha" in lineparam:
        entries["alpha"] = lineparam["alpha"]

    _set_entries(net, "line", index, entries=entries)

    _set_value_if_not_nan(net, index, max_loading_percent, "max_loading_percent", "line")
    _set_value_if_not_nan(net, index, alpha, "alpha", "line")
    _set_value_if_not_nan(net, index, temperature_degree_celsius, "temperature_degree_celsius", "line")
    # add optional columns for TDPF if parameters passed to kwargs:
    _set_value_if_not_nan(net, index, kwargs.get("tdpf"), "tdpf", "line", bool_)
    for column, value in tdpf_parameters.items():
        _set_value_if_not_nan(net, index, value, column, "line", float64)

    _add_branch_geodata(net, geodata, index)

    return index


def create_line_dc(
    net: pandapowerNet,
    from_bus_dc: Int,
    to_bus_dc: Int,
    length_km: float,
    std_type: str,
    name: str | None = None,
    index: Int | None = None,
    geodata: Iterable[tuple[float, float]] | None = None,
    df: float = 1.0,
    parallel: int = 1,
    in_service: bool = True,
    max_loading_percent: float = nan,
    alpha: float = nan,
    temperature_degree_celsius: float = nan,
    **kwargs,
) -> Int:
    """
    Creates a line element in net["line_dc"]
    The line_dc parameters are defined through the standard type library.

    Parameters:
        net: The net within this line should be created
        from_bus_dc: ID of the bus_dc on one side which the line will be connected with
        to_bus_dc: ID of the bus_dc on the other side which the line will be connected with
        length_km: The line length in km
        std_type: Name of a standard line type:
            - Pre-defined in standard_linetypes
            - Customized std_type made using **create_std_type()**
        name: A custom name for this line_dc
        index: Force a specified ID if it is available. If None, the index one higher than the highest already existing
            index is selected.
        geodata: The line geodata of the line_dc. The first row should be the coordinates of bus a and the last should
            be the coordinates of bus b. The points in the middle represent the bending points of the line
        in_service: True for in_service or False for out of service
        df: derating factor, maximum current of line_dc in relation to nominal current of line (from 0 to 1)
        parallel: number of parallel line systems
        max_loading_percent: maximum current loading (only needed for OPF)
        alpha: temperature coefficient of resistance: R(T) = R(T_0) * (1 + alpha * (T - T_0))
        temperature_degree_celsius: line temperature for which line resistance is adjusted
    
    Keyword Arguments:
        tdpf (bool): whether the line is considered in the TDPF calculation
        wind_speed_m_per_s (float): wind speed at the line in m/s (TDPF)
        wind_angle_degree (float): angle of attack between the wind direction and the line (TDPF)
        conductor_outer_diameter_m (float): outer diameter of the line conductor in m (TDPF)
        air_temperature_degree_celsius (float): ambient temperature in °C (TDPF)
        reference_temperature_degree_celsius (float): reference temperature in °C for which r_ohm_per_km for the line_dc
            is specified (TDPF)
        solar_radiation_w_per_sq_m (float): solar radiation on horizontal plane in W/m² (TDPF)
        solar_absorptivity (float): Albedo factor for absorptivity of the lines (TDPF)
        emissivity (float): Albedo factor for emissivity of the lines (TDPF)
        r_theta_kelvin_per_mw (float): thermal resistance of the line (TDPF, only for simplified method)
        mc_joule_per_m_k (float): specific mass of the conductor multiplied by the specific thermal capacity of the
            material (TDPF, only for thermal inertia consideration with tdpf_delay_s parameter)

    Returns:
        The ID of the created dc line

    Example:
        >>> create_line_dc(net, from_bus_dc=0, to_bus_dc=1, length_km=0.1,  std_type="NAYY 4x50 SE", name="line_dc1")
    """

    # check if bus exist to attach the line to
    _check_branch_element(net, "Line_dc", index, from_bus_dc, to_bus_dc, node_name="bus_dc")
    
    index = _get_index_with_check(net, "line_dc", index)

    tdpf_columns = (
        "wind_speed_m_per_s",
        "wind_angle_degree",
        "conductor_outer_diameter_m",
        "air_temperature_degree_celsius",
        "reference_temperature_degree_celsius",
        "solar_radiation_w_per_sq_m",
        "solar_absorptivity",
        "emissivity",
        "r_theta_kelvin_per_mw",
        "mc_joule_per_m_k",
    )
    tdpf_parameters = {c: kwargs.pop(c) for c in tdpf_columns if c in kwargs}

    entries = {
        "name": name,
        "length_km": length_km,
        "from_bus_dc": from_bus_dc,
        "to_bus_dc": to_bus_dc,
        "in_service": in_service,
        "std_type": std_type,
        "df": df,
        "parallel": parallel,
        **kwargs,
    }

    lineparam = load_std_type(net, std_type, "line_dc")

    entries.update({param: lineparam[param] for param in ["r_ohm_per_km", "max_i_ka"]})
    if "r0_ohm_per_km" in lineparam:
        entries.update({param: lineparam[param] for param in ["r0_ohm_per_km"]})

    entries["g_us_per_km"] = lineparam["g_us_per_km"] if "g_us_per_km" in lineparam else 0.0

    if "type" in lineparam:
        entries["type"] = lineparam["type"]

    # if net.line column already has alpha, add it from std_type
    if "alpha" in net.line.columns and "alpha" in lineparam:
        entries["alpha"] = lineparam["alpha"]

    _set_entries(net, "line_dc", index, entries=entries)

    if geodata and hasattr(geodata, "__iter__"):
        geo = [[x, y] for x, y in geodata]
        net.line_dc.at[index, "geo"] = f'{{"coordinates": {geo}, "type": "LineString"}}'

    _set_value_if_not_nan(net, index, max_loading_percent, "max_loading_percent", "line_dc")
    _set_value_if_not_nan(net, index, alpha, "alpha", "line_dc")
    _set_value_if_not_nan(net, index, temperature_degree_celsius, "temperature_degree_celsius", "line_dc")
    # add optional columns for TDPF if parameters passed to kwargs:
    _set_value_if_not_nan(net, index, kwargs.get("tdpf"), "tdpf", "line_dc", bool_)
    for column, value in tdpf_parameters.items():
        _set_value_if_not_nan(net, index, value, column, "line_dc", float64)

    _add_branch_geodata(net, geodata, index, "line_dc")

    return index


def create_lines(
    net: pandapowerNet,
    from_buses: Sequence,
    to_buses: Sequence,
    length_km: float | Iterable[float],
    std_type: str | Iterable[str],
    name: Iterable[str] | None = None,
    index: Int | Iterable[Int] | None = None,
    geodata: Iterable[Iterable[tuple[float, float]]] | None = None,
    df: float | Iterable[float] = 1.0,
    parallel: int | Iterable[int] = 1,
    in_service: bool | Iterable[bool] = True,
    max_loading_percent: float | Iterable[float] = nan,
    **kwargs,
) -> npt.NDArray[Int]:
    """
    Convenience function for creating many lines at once. Parameters 'from_buses' and 'to_buses'
    must be arrays of equal length. Other parameters may be either arrays of the same length or
    single or values. In any case the line parameters are defined through a single standard
    type, so all lines have the same standard type.


    Parameters:
        net: The net within this line should be created
        from_buses: ID of the bus on one side which the line will be connected with
        to_buses: ID of the bus on the other side which the line will be connected with
        length_km: The line length in km
        std_type: The line type of the lines.
        name: A custom name for this line
        index: Force a specified ID if it is available. If None, the index one higher than the highest already existing
            index is selected.
        geodata: The geodata of the line. The first element should be the coordinates of from_bus and the last should be
            the coordinates of to_bus. The points in the middle represent the bending points of the line
        in_service: True for in_service or False for out of service
        df: derating factor: maximum current of line in relation to nominal current of line (from 0 to 1)
        parallel: number of parallel line systems
        max_loading_percent: maximum current loading (only needed for OPF)
        
    Keyword Arguments:
        alpha (float): temperature coefficient of resistance: R(T) = R(T_0) * (1 + alpha * (T - T_0))
        temperature_degree_celsius (float): line temperature for which line resistance is adjusted
        tdpf (bool): whether the line is considered in the TDPF calculation
        wind_speed_m_per_s (float): wind speed at the line in m/s (TDPF)
        wind_angle_degree (float): angle of attack between the wind direction and the line (TDPF)
        conductor_outer_diameter_m (float): outer diameter of the line conductor in m (TDPF)
        air_temperature_degree_celsius (float): ambient temperature in °C (TDPF)
        reference_temperature_degree_celsius (float): reference temperature in °C for which r_ohm_per_km for the line is
            specified (TDPF)
        solar_radiation_w_per_sq_m (float): solar radiation on horizontal plane in W/m² (TDPF)
        solar_absorptivity (float): Albedo factor for absorptivity of the lines (TDPF)
        emissivity (float): Albedo factor for emissivity of the lines (TDPF)
        r_theta_kelvin_per_mw (float): thermal resistance of the line (TDPF, only for simplified method)
        mc_joule_per_m_k (float): specific mass of the conductor multiplied by the specific thermal capacity of the
            material (TDPF, only for thermal inertia consideration with tdpf_delay_s parameter)

        Returns:
            The unique ID of the created lines

        Example:
            >>> create_lines(
            >>>   net, from_buses=[0,1], to_buses=[2,3], length_km=0.1, std_type="NAYY 4x50 SE", name=["line1", "line2"]
            >>> )
    """
    _check_multiple_branch_elements(net, from_buses, to_buses, "Lines")

    index = _get_multiple_index_with_check(net, "line", index, len(from_buses))

    entries = {
        "from_bus": from_buses,
        "to_bus": to_buses,
        "length_km": length_km,
        "std_type": std_type,
        "name": name,
        "df": df,
        "parallel": parallel,
        "in_service": in_service,
        **kwargs,
    }

    # add std type data
    if isinstance(std_type, str):
        lineparam = load_std_type(net, std_type, "line")
        entries["r_ohm_per_km"] = lineparam["r_ohm_per_km"]
        entries["x_ohm_per_km"] = lineparam["x_ohm_per_km"]
        entries["c_nf_per_km"] = lineparam["c_nf_per_km"]
        entries["max_i_ka"] = lineparam["max_i_ka"]
        entries["g_us_per_km"] = lineparam["g_us_per_km"] if "g_us_per_km" in lineparam else 0.0
        if "type" in lineparam:
            entries["type"] = lineparam["type"]
    else:
        lineparam = list(map(load_std_type, [net] * len(index), std_type, ["line"] * len(index)))
        entries["r_ohm_per_km"] = list(map(itemgetter("r_ohm_per_km"), lineparam))
        entries["x_ohm_per_km"] = list(map(itemgetter("x_ohm_per_km"), lineparam))
        entries["c_nf_per_km"] = list(map(itemgetter("c_nf_per_km"), lineparam))
        entries["max_i_ka"] = list(map(itemgetter("max_i_ka"), lineparam))
        entries["g_us_per_km"] = [line_param_dict.get("g_us_per_km", 0) for line_param_dict in lineparam]
        entries["type"] = [line_param_dict.get("type", None) for line_param_dict in lineparam]

    _add_to_entries_if_not_nan(net, "line", entries, index, "max_loading_percent", max_loading_percent)

    # add optional columns for TDPF if parameters passed to kwargs:
    _add_to_entries_if_not_nan(net, "line", entries, index, "tdpf", kwargs.get("tdpf"), bool_)
    tdpf_columns = (
        "wind_speed_m_per_s",
        "wind_angle_degree",
        "conductor_outer_diameter_m",
        "air_temperature_degree_celsius",
        "reference_temperature_degree_celsius",
        "solar_radiation_w_per_sq_m",
        "solar_absorptivity",
        "emissivity",
        "r_theta_kelvin_per_mw",
        "mc_joule_per_m_k",
    )
    tdpf_parameters = {c: kwargs.pop(c) for c in tdpf_columns if c in kwargs}
    for column, value in tdpf_parameters.items():
        _add_to_entries_if_not_nan(net, "line", entries, index, column, value, float64)

    _set_multiple_entries(net, "line", index, entries=entries)
    if "geo" in net.bus.columns:
        net.line.loc[net.line.geo == "", "geo"] = None  # overwrite

    _add_multiple_branch_geodata(net, geodata, index)

    return index


def create_lines_dc(
    net: pandapowerNet,
    from_buses_dc: Sequence,
    to_buses_dc: Sequence,
    length_km: float | Iterable[float],
    std_type: str | Sequence[str],
    name: Iterable[str] | None = None,
    index: Int | Iterable[Int] | None = None,
    geodata: Iterable[Iterable[tuple[float, float]]] | None = None,
    df: float | Iterable[float] = 1.0,
    parallel: int | Iterable[int] = 1,
    in_service: bool | Iterable[bool] = True,
    max_loading_percent: float | Iterable[float] = nan,
    **kwargs,
) -> npt.NDArray[Int]:
    """
    Convenience function for creating many dc lines at once. Parameters 'from_buses_dc' and 'to_buses_dc'
    must be arrays of equal length. Other parameters may be either arrays of the same length or
    single or values. In any case the dc line parameters are defined through a single standard
    type, so all lines have the same standard type.


    Parameters:
        net: The net within this dc line should be created
        from_buses_dc: ID of the dc buses on one side which the dc lines will be connected with
        to_buses_dc: ID of the dc buses on the other side which the dc lines will be connected with
        length_km: The dc line length in km
        std_type: The dc line type of the dc lines.
        name: A custom name for these dc lines
        index: Force a specified ID if it is available. If None, the index one higher than the highest already existing
            index is selected.
        geodata: The linegeodata of the dc line. The first row should be the coordinates of dc bus a and the last should
            be the coordinates of dc bus b. The points in the middle represent the bending points of the dc line
        in_service: True for in_service or False for out of service
        df: derating factor: maximum current of line in relation to nominal current of line (from 0 to 1)
        parallel: number of parallel line systems
        max_loading_percent: maximum current loading (only needed for OPF)
        
    Keyword Arguments:
        alpha (float): temperature coefficient of resistance: R(T) = R(T_0) * (1 + alpha * (T - T_0))
        temperature_degree_celsius (float): line temperature for which line resistance is adjusted
        tdpf (bool): whether the line is considered in the TDPF calculation
        wind_speed_m_per_s (float): wind speed at the line in m/s (TDPF)
        wind_angle_degree (float): angle of attack between the wind direction and the line (TDPF)
        conductor_outer_diameter_m (float): outer diameter of the line conductor in m (TDPF)
        air_temperature_degree_celsius (float): ambient temperature in °C (TDPF)
        reference_temperature_degree_celsius (float): reference temperature in °C for which r_ohm_per_km for the line is
            specified (TDPF)
        solar_radiation_w_per_sq_m (float): solar radiation on horizontal plane in W/m² (TDPF)
        solar_absorptivity (float): Albedo factor for absorptivity of the lines (TDPF)
        emissivity (float): Albedo factor for emissivity of the lines (TDPF)
        r_theta_kelvin_per_mw (float): thermal resistance of the line (TDPF, only for simplified method)
        mc_joule_per_m_k (float): specific mass of the conductor multiplied by the specific thermal capacity of the
            material (TDPF, only for thermal inertia consideration with tdpf_delay_s parameter)
    
    Returns:
        The unique ID of the created dc lines

    Example:
        >>> create_lines_dc(net, from_buses_dc=[0,1], to_buses_dc=[2,3], length_km=0.1,
        >>>   std_type="Not specified yet", name=["line_dc1","line_dc2"]
        >>> )
    """
    _check_multiple_branch_elements(
        net, from_buses_dc, to_buses_dc, "Lines_dc", node_name="bus_dc", plural="(all dc buses)"
    )

    index = _get_multiple_index_with_check(net, "line_dc", index, len(from_buses_dc))

    entries = {
        "from_bus_dc": from_buses_dc,
        "to_bus_dc": to_buses_dc,
        "length_km": length_km,
        "std_type": std_type,
        "name": name,
        "df": df,
        "parallel": parallel,
        "in_service": in_service,
        **kwargs,
    }

    # add std type data
    if isinstance(std_type, str):
        lineparam = load_std_type(net, std_type, "line_dc")
        entries["r_ohm_per_km"] = lineparam["r_ohm_per_km"]
        entries["max_i_ka"] = lineparam["max_i_ka"]
        entries["g_us_per_km"] = lineparam["g_us_per_km"] if "g_us_per_km" in lineparam else 0.0
        if "type" in lineparam:
            entries["type"] = lineparam["type"]
    else:
        lineparam = list(map(load_std_type, [net] * len(std_type), std_type, ["line_dc"] * len(std_type)))
        entries["r_ohm_per_km"] = list(map(itemgetter("r_ohm_per_km"), lineparam))
        entries["max_i_ka"] = list(map(itemgetter("max_i_ka"), lineparam))
        entries["g_us_per_km"] = [line_param_dict.get("g_us_per_km", 0) for line_param_dict in lineparam]
        entries["type"] = [line_param_dict.get("type", None) for line_param_dict in lineparam]

    _add_to_entries_if_not_nan(net, "line_dc", entries, index, "max_loading_percent", max_loading_percent)

    # add optional columns for TDPF if parameters passed to kwargs:
    _add_to_entries_if_not_nan(net, "line_dc", entries, index, "tdpf", kwargs.get("tdpf"), bool_)
    tdpf_columns = (
        "wind_speed_m_per_s",
        "wind_angle_degree",
        "conductor_outer_diameter_m",
        "air_temperature_degree_celsius",
        "reference_temperature_degree_celsius",
        "solar_radiation_w_per_sq_m",
        "solar_absorptivity",
        "emissivity",
        "r_theta_kelvin_per_mw",
        "mc_joule_per_m_k",
    )
    tdpf_parameters = {c: kwargs.pop(c) for c in tdpf_columns if c in kwargs}
    for column, value in tdpf_parameters.items():
        _add_to_entries_if_not_nan(net, "line_dc", entries, index, column, value, float64)

    _set_multiple_entries(net, "line_dc", index, entries=entries)

    _add_multiple_branch_geodata(net, geodata, index, "line_dc")

    return index


def create_line_from_parameters(
    net: pandapowerNet,
    from_bus: Int,
    to_bus: Int,
    length_km: float,
    r_ohm_per_km: float,
    x_ohm_per_km: float,
    c_nf_per_km: float,
    max_i_ka: float,
    name: str | None = None,
    index: Int | None = None,
    type: LineType | None = None,
    geodata: Iterable[tuple[float, float]] | None = None,
    in_service: bool = True,
    df: float = 1.0,
    parallel: int = 1,
    g_us_per_km: float = 0.0,
    max_loading_percent: float = nan,
    alpha: float = nan,
    temperature_degree_celsius: float = nan,
    r0_ohm_per_km: float = nan,
    x0_ohm_per_km: float = nan,
    c0_nf_per_km: float = nan,
    g0_us_per_km: float = 0,
    endtemp_degree: float = nan,
    **kwargs,
) -> Int:
    """
    Creates a line element in net["line"] from line parameters.

    Parameters:
        net: The net within this line should be created
        from_bus: ID of the bus on one side which the line will be connected with
        to_bus: ID of the bus on the other side which the line will be connected with
        length_km: The line length in km
        r_ohm_per_km: line resistance in ohm per km
        x_ohm_per_km: line reactance in ohm per km
        c_nf_per_km: line capacitance (line-to-earth) in nano Farad per km
        r0_ohm_per_km: zero sequence line resistance in ohm per km
        x0_ohm_per_km: zero sequence line reactance in ohm per km
        c0_nf_per_km: zero sequence line capacitance in nano Farad per km
        max_i_ka: maximum thermal current in kilo Ampere
        name: A custom name for this line
        index: Force a specified ID if it is available. If None, the index one higher than the highest already existing
            index is selected.
        in_service: True for in_service or False for out of service
        type: type of line ("ol" for overhead line or "cs" for cable system)
        df: derating factor: maximum current of line in relation to nominal current of line (from 0 to 1)
        g_us_per_km: dielectric conductance in micro Siemens per km
        g0_us_per_km: zero sequence dielectric conductance in micro Siemens per km
        parallel: number of parallel line systems
        geodata: The geodata of the line. The first row should be the coordinates of bus a and the last should be the
            coordinates of bus b. The points in the middle represent the bending points of the line
        max_loading_percent: maximum current loading (only needed for OPF)
        alpha: temperature coefficient of resistance: R(T) = R(T_0) * (1 + alpha * (T - T_0)))
        temperature_degree_celsius: line temperature for which line resistance is adjusted

    Keyword Arguments:
        tdpf: whether the line is considered in the TDPF calculation
        wind_speed_m_per_s: wind speed at the line in m/s (TDPF)
        wind_angle_degree: angle of attack between the wind direction and the line (TDPF)
        conductor_outer_diameter_m: outer diameter of the line conductor in m (TDPF)
        air_temperature_degree_celsius: ambient temperature in °C (TDPF)
        reference_temperature_degree_celsius: reference temperature in °C for which r_ohm_per_km for the line is
            specified (TDPF)
        solar_radiation_w_per_sq_m: solar radiation on horizontal plane in W/m² (TDPF)
        solar_absorptivity: Albedo factor for absorptivity of the lines (TDPF)
        emissivity: Albedo factor for emissivity of the lines (TDPF)
        r_theta_kelvin_per_mw: thermal resistance of the line (TDPF, only for simplified method)
        mc_joule_per_m_k: specific mass of the conductor multiplied by the specific thermal capacity of the material
            (TDPF, only for thermal inertia consideration with tdpf_delay_s parameter)
        
    Returns:
        The unique ID of the created line

    Example:
        >>> create_line_from_parameters(net, from_bus=0, to_bus=1, length_km=0.1,
        >>>   r_ohm_per_km=.01, x_ohm_per_km=0.05, c_nf_per_km=10, max_i_ka=0.4, name="line1"
        >>> )
    """

    # check if bus exist to attach the line to
    _check_branch_element(net, "Line", index, from_bus, to_bus)

    index = _get_index_with_check(net, "line", index)

    tdpf_columns = (
        "wind_speed_m_per_s",
        "wind_angle_degree",
        "conductor_outer_diameter_m",
        "air_temperature_degree_celsius",
        "reference_temperature_degree_celsius",
        "solar_radiation_w_per_sq_m",
        "solar_absorptivity",
        "emissivity",
        "r_theta_kelvin_per_mw",
        "mc_joule_per_m_k",
    )
    tdpf_parameters = {c: kwargs.pop(c) for c in tdpf_columns if c in kwargs}

    entries = {
        "name": name,
        "length_km": length_km,
        "from_bus": from_bus,
        "to_bus": to_bus,
        "in_service": in_service,
        "std_type": None,
        "df": df,
        "r_ohm_per_km": r_ohm_per_km,
        "x_ohm_per_km": x_ohm_per_km,
        "c_nf_per_km": c_nf_per_km,
        "max_i_ka": max_i_ka,
        "parallel": parallel,
        "type": type,
        "g_us_per_km": g_us_per_km,
        **kwargs,
    }
    _set_entries(net, "line", index, entries=entries)

    nan_0_values = [isnan(r0_ohm_per_km), isnan(x0_ohm_per_km), isnan(c0_nf_per_km)]
    if not np_any(nan_0_values):
        _set_value_if_not_nan(net, index, r0_ohm_per_km, "r0_ohm_per_km", "line")
        _set_value_if_not_nan(net, index, x0_ohm_per_km, "x0_ohm_per_km", "line")
        _set_value_if_not_nan(net, index, c0_nf_per_km, "c0_nf_per_km", "line")
        _set_value_if_not_nan(net, index, g0_us_per_km, "g0_us_per_km", "line", default_val=0.0)
    elif not np_all(nan_0_values):
        logger.warning(
            "Zero sequence values are given for only some parameters. Please specify "
            "them for all parameters, otherwise they are not set!"
        )

    _set_value_if_not_nan(net, index, max_loading_percent, "max_loading_percent", "line")
    _set_value_if_not_nan(net, index, alpha, "alpha", "line")
    _set_value_if_not_nan(net, index, temperature_degree_celsius, "temperature_degree_celsius", "line")
    _set_value_if_not_nan(net, index, endtemp_degree, "endtemp_degree", "line")

    # add optional columns for TDPF if parameters passed to kwargs:
    _set_value_if_not_nan(net, index, kwargs.get("tdpf"), "tdpf", "line", bool_)
    for column, value in tdpf_parameters.items():
        _set_value_if_not_nan(net, index, value, column, "line", float64)

    _add_branch_geodata(net, geodata, index)
    return index


def create_line_dc_from_parameters(
    net: pandapowerNet,
    from_bus_dc: Int,
    to_bus_dc: Int,
    length_km: float,
    r_ohm_per_km: float,
    max_i_ka: float,
    name: str | None = None,
    index: Int | None = None,
    type: LineType | None = None,
    geodata: Iterable[tuple[float, float]] | None = None,
    in_service: bool = True,
    df: float = 1.0,
    parallel: int = 1,
    max_loading_percent: float = nan,
    alpha: float = nan,
    temperature_degree_celsius: float = nan,
    g_us_per_km: float = 0.0,
    **kwargs,
) -> Int:
    """
    Creates a dc line element in net["line_dc"] from dc line parameters.

    Parameters:
        net: The net within this dc line should be created
        from_bus_dc: ID of the dc bus on one side which the dc line will be connected with
        to_bus_dc: ID of the dc bus on the other side which the dc line will be connected with
        length_km: The dc line length in km
        r_ohm_per_km: dc line resistance in ohm per km
        max_i_ka: maximum thermal current in kilo Ampere
        name: A custom name for this line
        index: Force a specified ID if it is available. If None, the index one higher than the highest already existing
            index is selected.
        in_service: True for in_service or False for out of service
        type: type of dc line ("ol" for overhead dc line or "cs" for cable system)
        df: derating factor: maximum current of dc line in relation to nominal current of line (from 0 to 1)
        g_us_per_km: dielectric conductance in micro Siemens per km
        g0_us_per_km: zero sequence dielectric conductance in micro Siemens per km
        parallel: number of parallel line systems
        geodata: The linegeodata of the dc line. The first row should be the coordinates of dc bus a and the last should
            be the coordinates of dc bus b. The points in the middle represent the bending points of the line
        max_loading_percent: maximum current loading (only needed for OPF)
        alpha: temperature coefficient of resistance: R(T) = R(T_0) * (1 + alpha * (T - T_0)))
        temperature_degree_celsius: line temperature for which line resistance is adjusted

    Keyword Arguments:
        tdpf: whether the line is considered in the TDPF calculation
        wind_speed_m_per_s: wind speed at the line in m/s (TDPF)
        wind_angle_degree: angle of attack between the wind direction and the line (TDPF)
        conductor_outer_diameter_m: outer diameter of the line conductor in m (TDPF)
        air_temperature_degree_celsius: ambient temperature in °C (TDPF)
        reference_temperature_degree_celsius: reference temperature in °C for which r_ohm_per_km for the line is
            specified (TDPF)
        solar_radiation_w_per_sq_m: solar radiation on horizontal plane in W/m² (TDPF)
        solar_absorptivity: Albedo factor for absorptivity of the lines (TDPF)
        emissivity: Albedo factor for emissivity of the lines (TDPF)
        r_theta_kelvin_per_mw: thermal resistance of the line (TDPF, only for simplified method)
        mc_joule_per_m_k: specific mass of the conductor multiplied by the specific thermal capacity of the material
            (TDPF, only for thermal inertia consideration with tdpf_delay_s parameter)

    Returns:
        The ID of the created line

    Example:
        >>> create_line_dc_from_parameters(
        >>>   net, from_bus_dc=0, to_bus_dc=1, length_km=0.1, r_ohm_per_km=.01, max_i_ka=0.4, name="line_dc1"
        >>> )
    """

    # check if bus exist to attach the dc line to
    _check_branch_element(net, "Line_dc", index, from_bus_dc, to_bus_dc, node_name="bus_dc")

    index = _get_index_with_check(net, "line_dc", index)

    tdpf_columns = (
        "wind_speed_m_per_s",
        "wind_angle_degree",
        "conductor_outer_diameter_m",
        "air_temperature_degree_celsius",
        "reference_temperature_degree_celsius",
        "solar_radiation_w_per_sq_m",
        "solar_absorptivity",
        "emissivity",
        "r_theta_kelvin_per_mw",
        "mc_joule_per_m_k",
    )
    tdpf_parameters = {c: kwargs.pop(c) for c in tdpf_columns if c in kwargs}

    entries = {
        "name": name,
        "length_km": length_km,
        "from_bus_dc": from_bus_dc,
        "to_bus_dc": to_bus_dc,
        "in_service": in_service,
        "std_type": None,
        "df": df,
        "r_ohm_per_km": r_ohm_per_km,
        "max_i_ka": max_i_ka,
        "parallel": parallel,
        "type": type,
        "g_us_per_km": g_us_per_km,
        **kwargs,
    }
    _set_entries(net, "line_dc", index, entries=entries)

    if geodata and hasattr(geodata, "__iter__"):
        geo = [[x, y] for x, y in geodata]
        net.line_dc.at[index, "geo"] = f'{{"coordinates": {geo}, "type": "LineString"}}'

    _set_value_if_not_nan(net, index, max_loading_percent, "max_loading_percent", "line_dc")
    _set_value_if_not_nan(net, index, alpha, "alpha", "line_dc")
    _set_value_if_not_nan(net, index, temperature_degree_celsius, "temperature_degree_celsius", "line_dc")

    # add optional columns for TDPF if parameters passed to kwargs:
    _set_value_if_not_nan(net, index, kwargs.get("tdpf"), "tdpf", "line_dc", bool_)
    for column, value in tdpf_parameters.items():
        _set_value_if_not_nan(net, index, value, column, "line_dc", float64)

    _add_branch_geodata(net, geodata, index, "line_dc")

    return index


def create_lines_from_parameters(
    net: pandapowerNet,
    from_buses: Sequence,
    to_buses: Sequence,
    length_km: float | Iterable[float],
    r_ohm_per_km: float | Iterable[float],
    x_ohm_per_km: float | Iterable[float],
    c_nf_per_km: float | Iterable[float],
    max_i_ka: float | Iterable[float],
    name: Iterable[str] | None = None,
    index: Int | Iterable[Int] | None = None,
    type: LineType | Iterable[str] | None = None,
    geodata: Iterable[Iterable[tuple[float, float]]] | None = None,
    in_service: bool | Iterable[bool] = True,
    df: float | Iterable[float] = 1.0,
    parallel: int | Iterable[int] = 1,
    g_us_per_km: float | Iterable[float] = 0.0,
    max_loading_percent: float | Iterable[float] = nan,
    alpha: float = nan,
    temperature_degree_celsius: float = nan,
    r0_ohm_per_km: float | Iterable[float] = nan,
    x0_ohm_per_km: float | Iterable[float] = nan,
    c0_nf_per_km: float | Iterable[float] = nan,
    g0_us_per_km: float | Iterable[float] = nan,
    **kwargs,
) -> npt.NDArray[Int]:
    """
    Convenience function for creating many lines at once. Parameters 'from_buses' and 'to_buses'
    must be arrays of equal length. Other parameters may be either arrays of the same length or
    single or values.

    Parameters:
        net: The net within this line should be created
        from_buses: ID of the buses on one side which the lines will be connected with
        to_buses: ID of the buses on the other side which the lines will be connected with
        length_km: The line length in km
        r_ohm_per_km: line resistance in ohm per km
        x_ohm_per_km: line reactance in ohm per km
        c_nf_per_km: line capacitance in nano Farad per kma
        r0_ohm_per_km: zero sequence line resistance in ohm per km
        x0_ohm_per_km: zero sequence line reactance in ohm per km
        c0_nf_per_km: zero sequence line capacitance in nano Farad per km
        max_i_ka: maximum thermal current in kilo Ampere
        name: A custom name for this line
        index: Force a specified ID if it is available. If None, the index one higher than the highest already existing
            index is selected.
        in_service: True for in_service or False for out of service
        type: type of line ("ol" for overhead line or "cs" for cable system)
        df: derating factor: maximum current of line in relation to nominal current of line (from 0 to 1)
        g_us_per_km: dielectric conductance in micro Siemens per km
        g0_us_per_km: zero sequence dielectric conductance in micro Siemens per km
        parallel: number of parallel line systems
        geodata: The geodata of the line. The first row should be the coordinates of bus a and the last should be the
            coordinates of bus b. The points in the middle represent the bending points of the line
        max_loading_percent: maximum current loading (only needed for OPF)
        alpha: temperature coefficient of resistance: R(T) = R(T_0) * (1 + alpha * (T - T_0)))
        temperature_degree_celsius: line temperature for which line resistance is adjusted

    Keyword Arguments:
        tdpf (bool): whether the line is considered in the TDPF calculation
        wind_speed_m_per_s (float): wind speed at the line in m/s (TDPF)
        wind_angle_degree (float): angle of attack between the wind direction and the line (TDPF)
        conductor_outer_diameter_m (float): outer diameter of the line conductor in m (TDPF)
        air_temperature_degree_celsius (float): ambient temperature in °C (TDPF)
        reference_temperature_degree_celsius (float): reference temperature in °C for which r_ohm_per_km for the line is
            specified (TDPF)
        solar_radiation_w_per_sq_m (float): solar radiation on horizontal plane in W/m² (TDPF)
        solar_absorptivity (float): Albedo factor for absorptivity of the lines (TDPF)
        emissivity (float): Albedo factor for emissivity of the lines (TDPF)
        r_theta_kelvin_per_mw (float): thermal resistance of the line (TDPF, only for simplified method)
        mc_joule_per_m_k (float): specific mass of the conductor multiplied by the specific thermal capacity of the
            material (TDPF, only for thermal inertia consideration with tdpf_delay_s parameter)
        
    Returns:
        The ID of the created lines

    Example:
        >>> create_lines_from_parameters(
        >>>   net, from_buses=[0,1], to_buses=[2,3], length_km=0.1, r_ohm_per_km=.01, x_ohm_per_km=0.05, c_nf_per_km=10,
        >>>   max_i_ka=0.4, name=["line1","line2"]
        >>> )
    """
    _check_multiple_branch_elements(net, from_buses, to_buses, "Lines")

    index = _get_multiple_index_with_check(net, "line", index, len(from_buses))

    entries = {
        "from_bus": from_buses,
        "to_bus": to_buses,
        "length_km": length_km,
        "type": type,
        "r_ohm_per_km": r_ohm_per_km,
        "x_ohm_per_km": x_ohm_per_km,
        "c_nf_per_km": c_nf_per_km,
        "max_i_ka": max_i_ka,
        "g_us_per_km": g_us_per_km,
        "name": name,
        "df": df,
        "parallel": parallel,
        "in_service": in_service,
        **kwargs,
    }

    _add_to_entries_if_not_nan(net, "line", entries, index, "max_loading_percent", max_loading_percent)
    _add_to_entries_if_not_nan(net, "line", entries, index, "r0_ohm_per_km", r0_ohm_per_km)
    _add_to_entries_if_not_nan(net, "line", entries, index, "x0_ohm_per_km", x0_ohm_per_km)
    _add_to_entries_if_not_nan(net, "line", entries, index, "c0_nf_per_km", c0_nf_per_km)
    _add_to_entries_if_not_nan(net, "line", entries, index, "g0_us_per_km", g0_us_per_km)
    _add_to_entries_if_not_nan(net, "line", entries, index, "temperature_degree_celsius", temperature_degree_celsius)
    _add_to_entries_if_not_nan(net, "line", entries, index, "alpha", alpha)

    # add optional columns for TDPF if parameters passed to kwargs:
    _add_to_entries_if_not_nan(net, "line", entries, index, "tdpf", kwargs.get("tdpf"), bool_)
    tdpf_columns = (
        "wind_speed_m_per_s",
        "wind_angle_degree",
        "conductor_outer_diameter_m",
        "air_temperature_degree_celsius",
        "reference_temperature_degree_celsius",
        "solar_radiation_w_per_sq_m",
        "solar_absorptivity",
        "emissivity",
        "r_theta_kelvin_per_mw",
        "mc_joule_per_m_k",
    )
    tdpf_parameters = {c: kwargs.pop(c) for c in tdpf_columns if c in kwargs}
    for column, value in tdpf_parameters.items():
        _add_to_entries_if_not_nan(net, "line", entries, index, column, value, float64)

    _set_multiple_entries(net, "line", index, entries=entries)

    _add_multiple_branch_geodata(net, geodata, index)

    return index


def create_lines_dc_from_parameters(
    net: pandapowerNet,
    from_buses_dc: Sequence,
    to_buses_dc: Sequence,
    length_km: float | Iterable[float],
    r_ohm_per_km: float | Iterable[float],
    max_i_ka: float | Iterable[float],
    name: Iterable[str] | None = None,
    index: Int | Iterable[Int] | None = None,
    type: LineType | Iterable[str] | None = None,
    geodata: Iterable[Iterable[tuple[float, float]]] | None = None,
    in_service: bool | Iterable[bool] = True,
    df: float | Iterable[float] = 1.0,
    parallel: int | Iterable[int] = 1,
    g_us_per_km: float | Iterable[float] = 0.0,
    max_loading_percent: float | Iterable[float] = nan,
    alpha: float = nan,
    temperature_degree_celsius: float = nan,
    **kwargs,
) -> npt.NDArray[Int]:
    """
    Convenience function for creating many dc lines at once. Parameters 'from_buses_dc' and 'to_buses_dc'
        must be arrays of equal length. Other parameters may be either arrays of the same length or
        single or values.

    Parameters:
        net: The net within this dc lines should be created
        from_buses_dc: ID of the dc buses on one side which the dc lines will be connected with
        to_buses_dc: ID of the dc buses on the other side which the dc lines will be connected with
        length_km: The dc line length in km
        r_ohm_per_km: dc line resistance in ohm per km
        max_i_ka: maximum thermal current in kilo Ampere
        name: A custom name for this dc line
        index: Force a specified ID if it is available. If None, the index one higher than the highest already existing
            index is selected.
        in_service: True for in_service or False for out of service
        type: type of dc line ("ol" for overhead dc line or "cs" for cable system)
        df: derating factor: maximum current of line in relation to nominal current of line (from 0 to 1)
        g_us_per_km: dielectric conductance in micro Siemens per km
        parallel: number of parallel line systems
        geodata: The line geodata of the dc lines. The first row should be the coordinates of dc bus a and the last
            should be the coordinates of dc bus b. The points in the middle represent the bending points of the line
        max_loading_percent: maximum current loading (only needed for OPF)
        alpha: temperature coefficient of resistance: R(T) = R(T_0) * (1 + alpha * (T - T_0)))
        temperature_degree_celsius: line temperature for which line resistance is adjusted
        
    Keyword Arguments:
        tdpf (bool): whether the line is considered in the TDPF calculation
        wind_speed_m_per_s (float): wind speed at the line in m/s (TDPF)
        wind_angle_degree (float): angle of attack between the wind direction and the line (TDPF)
        conductor_outer_diameter_m (float): outer diameter of the line conductor in m (TDPF)
        air_temperature_degree_celsius (float): ambient temperature in °C (TDPF)
        reference_temperature_degree_celsius (float): reference temperature in °C for which r_ohm_per_km for the line is
            specified (TDPF)
        solar_radiation_w_per_sq_m (float): solar radiation on horizontal plane in W/m² (TDPF)
        solar_absorptivity (float): Albedo factor for absorptivity of the lines (TDPF)
        emissivity (float): Albedo factor for emissivity of the lines (TDPF)
        r_theta_kelvin_per_mw (float): thermal resistance of the line (TDPF, only for simplified method)
        mc_joule_per_m_k (float): specific mass of the conductor multiplied by the specific thermal capacity of the
            material (TDPF, only for thermal inertia consideration with tdpf_delay_s parameter)
        
    Return:
        List of IDs of the created dc lines

    Example:
        >>> create_lines_dc_from_parameters(net, from_buses_dc=[0,1], to_buses_dc=[2,3], length_km=0.1,
        >>>   r_ohm_per_km=.01, max_i_ka=0.4, name=["line_dc1","line_dc2"]
        >>> )
    """
    _check_multiple_branch_elements(
        net, from_buses_dc, to_buses_dc, "Lines_dc", node_name="bus_dc", plural="(all dc buses)"
    )

    index = _get_multiple_index_with_check(net, "line", index, len(from_buses_dc))

    entries = {
        "from_bus_dc": from_buses_dc,
        "to_bus_dc": to_buses_dc,
        "length_km": length_km,
        "type": type,
        "r_ohm_per_km": r_ohm_per_km,
        "max_i_ka": max_i_ka,
        "g_us_per_km": g_us_per_km,
        "name": name,
        "df": df,
        "parallel": parallel,
        "in_service": in_service,
        **kwargs,
    }

    _add_to_entries_if_not_nan(net, "line_dc", entries, index, "max_loading_percent", max_loading_percent)
    _add_to_entries_if_not_nan(net, "line_dc", entries, index, "temperature_degree_celsius", temperature_degree_celsius)
    _add_to_entries_if_not_nan(net, "line_dc", entries, index, "alpha", alpha)

    # add optional columns for TDPF if parameters passed to kwargs:
    _add_to_entries_if_not_nan(net, "line_dc", entries, index, "tdpf", kwargs.get("tdpf"), bool_)
    tdpf_columns = (
        "wind_speed_m_per_s",
        "wind_angle_degree",
        "conductor_outer_diameter_m",
        "air_temperature_degree_celsius",
        "reference_temperature_degree_celsius",
        "solar_radiation_w_per_sq_m",
        "solar_absorptivity",
        "emissivity",
        "r_theta_kelvin_per_mw",
        "mc_joule_per_m_k",
    )
    tdpf_parameters = {c: kwargs.pop(c) for c in tdpf_columns if c in kwargs}
    for column, value in tdpf_parameters.items():
        _add_to_entries_if_not_nan(net, "line_dc", entries, index, column, value, float64)

    _set_multiple_entries(net, "line_dc", index, entries=entries)

    _add_multiple_branch_geodata(net, geodata, index, "line_dc")

    return index


def create_dcline(
    net: pandapowerNet,
    from_bus: Int,
    to_bus: Int,
    p_mw: float,
    loss_percent: float,
    loss_mw: float,
    vm_from_pu: float,
    vm_to_pu: float,
    index: Int | None = None,
    name: str | None = None,
    max_p_mw: float = nan,
    min_q_from_mvar: float = nan,
    min_q_to_mvar: float = nan,
    max_q_from_mvar: float = nan,
    max_q_to_mvar: float = nan,
    in_service: bool = True,
    **kwargs,
) -> Int:
    """
    Creates a dc line.

    Parameters:
        from_bus: ID of the bus on one side which the line will be connected with
        to_bus: ID of the bus on the other side which the line will be connected with
        p_mw: Active power transmitted from 'from_bus' to 'to_bus'
        loss_percent: Relative transmission loss in percent of active power transmission
        loss_mw: Total transmission loss in MW
        vm_from_pu: Voltage set point at from bus
        vm_to_pu: Voltage set point at to bus
        index: Force a specified ID if it is available. If None, the index one higher than the highest already existing
            index is selected.
        name: A custom name for this dc line
        in_service: True for in_service or False for out of service
        max_p_mw: Maximum active power flow. Only respected for OPF
        min_q_from_mvar: Minimum reactive power at from bus. Necessary for OPF
        min_q_to_mvar: Minimum reactive power at to bus. Necessary for OPF
        max_q_from_mvar: Maximum reactive power at from bus. Necessary for OPF
        max_q_to_mvar: Maximum reactive power at to bus. Necessary for OPF
        
    Return:
        ID of the created element

    Example:
        >>> create_dcline(
        >>>     net, from_bus=0, to_bus=1, p_mw=1e4, loss_percent=1.2, loss_mw=25, vm_from_pu=1.01, vm_to_pu=1.02
        >>> )
    """
    index = _get_index_with_check(net, "dcline", index)

    _check_branch_element(net, "DCLine", index, from_bus, to_bus)

    entries = {
        "name": name,
        "from_bus": from_bus,
        "to_bus": to_bus,
        "p_mw": p_mw,
        "loss_percent": loss_percent,
        "loss_mw": loss_mw,
        "vm_from_pu": vm_from_pu,
        "vm_to_pu": vm_to_pu,
        "max_p_mw": max_p_mw,
        "min_q_from_mvar": min_q_from_mvar,
        "max_q_from_mvar": max_q_from_mvar,
        "max_q_to_mvar": max_q_to_mvar,
        "min_q_to_mvar": min_q_to_mvar,
        "in_service": in_service,
        **kwargs,
    }
    _set_entries(net, "dcline", index, entries=entries)

    return index
