예제 #1
0
def _extract_result_ppci_to_pp(net, ppc, ppci):
    # convert to pandapower indices
    ppc = _copy_results_ppci_to_ppc(ppci, ppc, mode="se")

    # extract results from ppc
    try:
        _add_pf_options(net,
                        tolerance_mva=1e-8,
                        trafo_loading="current",
                        numba=True,
                        ac=True,
                        algorithm='nr',
                        max_iteration="auto")
    except:
        pass
    # writes res_bus.vm_pu / va_degree and res_line
    _extract_results_se(net, ppc)

    # restore backup of previous results
    _rename_results(net)

    # additionally, write bus power demand results (these are not written in _extract_results)
    mapping_table = net["_pd2ppc_lookups"]["bus"]
    net.res_bus_est.index = net.bus.index
    net.res_bus_est.p_mw = get_values(ppc["bus"][:, 2], net.bus.index.values,
                                      mapping_table)
    net.res_bus_est.q_mvar = get_values(ppc["bus"][:, 3], net.bus.index.values,
                                        mapping_table)

    _clean_up(net)
    # delete results which are not correctly calculated
    for k in list(net.keys()):
        if k.startswith("res_") and k.endswith("_est") and \
                k not in ("res_bus_est", "res_line_est", "res_trafo_est", "res_trafo3w_est"):
            del net[k]
    return net
예제 #2
0
def runpp(net,
          algorithm='nr',
          calculate_voltage_angles="auto",
          init="auto",
          max_iteration="auto",
          tolerance_kva=1e-5,
          trafo_model="t",
          trafo_loading="current",
          enforce_q_lims=False,
          check_connectivity=True,
          voltage_depend_loads=True,
          **kwargs):
    """
    Runs a power flow

    INPUT:
        **net** - The pandapower format network

    OPTIONAL:
        **algorithm** (str, "nr") - algorithm that is used to solve the power flow problem.

            The following algorithms are available:

                - "nr" Newton-Raphson (pypower implementation with numba accelerations)
                - "iwamoto_nr" Newton-Raphson with Iwamoto multiplier (maybe slower than NR but more robust)
                - "bfsw" backward/forward sweep (specially suited for radial and weakly-meshed networks)
                - "gs" gauss-seidel (pypower implementation)
                - "fdbx" fast-decoupled (pypower implementation)
                - "fdxb" fast-decoupled (pypower implementation)

        **calculate_voltage_angles** (bool, "auto") - consider voltage angles in loadflow calculation

            If True, voltage angles of ext_grids and transformer shifts are considered in the
            loadflow calculation. Considering the voltage angles is only necessary in meshed
            networks that are usually found in higher voltage levels. calculate_voltage_angles
            in "auto" mode defaults to:

                - True, if the network voltage level is above 70 kV
                - False otherwise

            The network voltage level is defined as the maximum rated voltage of any bus in the network that
            is connected to a line.

        **init** (str, "auto") - initialization method of the loadflow
        pandapower supports four methods for initializing the loadflow:

            - "auto" - init defaults to "dc" if calculate_voltage_angles is True or "flat" otherwise
            - "flat"- flat start with voltage of 1.0pu and angle of 0° at all PQ-buses and 0° for PV buses as initial solution
            - "dc" - initial DC loadflow before the AC loadflow. The results of the DC loadflow are used as initial solution for the AC loadflow.
            - "results" - voltage vector of last loadflow from net.res_bus is used as initial solution. This can be useful to accelerate convergence in iterative loadflows like time series calculations.

        Considering the voltage angles might lead to non-convergence of the power flow in flat start.
        That is why in "auto" mode, init defaults to "dc" if calculate_voltage_angles is True or "flat" otherwise

        **max_iteration** (int, "auto") - maximum number of iterations carried out in the power flow algorithm.

            In "auto" mode, the default value depends on the power flow solver:

                - 10 for "nr"
                - 100 for "bfsw"
                - 1000 for "gs"
                - 30 for "fdbx"
                - 30 for "fdxb"

        **tolerance_kva** (float, 1e-5) - loadflow termination condition referring to P / Q mismatch of node power in kva

        **trafo_model** (str, "t")  - transformer equivalent circuit model
        pandapower provides two equivalent circuit models for the transformer:

            - "t" - transformer is modeled as equivalent with the T-model.
            - "pi" - transformer is modeled as equivalent PI-model. This is not recommended, since it is less exact than the T-model. It is only recommended for valdiation with other software that uses the pi-model.

        **trafo_loading** (str, "current") - mode of calculation for transformer loading

            Transformer loading can be calculated relative to the rated current or the rated power. In both cases the overall transformer loading is defined as the maximum loading on the two sides of the transformer.

            - "current"- transformer loading is given as ratio of current flow and rated current of the transformer. This is the recommended setting, since thermal as well as magnetic effects in the transformer depend on the current.
            - "power" - transformer loading is given as ratio of apparent power flow to the rated apparent power of the transformer.

        **enforce_q_lims** (bool, False) - respect generator reactive power limits

            If True, the reactive power limits in net.gen.max_q_kvar/min_q_kvar are respected in the
            loadflow. This is done by running a second loadflow if reactive power limits are
            violated at any generator, so that the runtime for the loadflow will increase if reactive
            power has to be curtailed.

            Note: enforce_q_lims only works if algorithm="nr"!


        **check_connectivity** (bool, True) - Perform an extra connectivity test after the conversion from pandapower to PYPOWER

            If True, an extra connectivity test based on SciPy Compressed Sparse Graph Routines is perfomed.
            If check finds unsupplied buses, they are set out of service in the ppc

        **voltage_depend_loads** (bool, True) - consideration of voltage-dependent loads. If False, net.load.const_z_percent and net.load.const_i_percent are not considered, i.e. net.load.p_kw and net.load.q_kvar are considered as constant-power loads.


        **KWARGS:

        **numba** (bool, True) - Activation of numba JIT compiler in the newton solver

            If set to True, the numba JIT compiler is used to generate matrices for the powerflow,
            which leads to significant speed improvements.

        **r_switch** (float, 0.0) - resistance of bus-bus-switches. If impedance is zero, buses connected by a closed bus-bus switch are fused to model an ideal bus. Otherwise, they are modelled as branches with resistance r_switch.

        **delta_q** - Reactive power tolerance for option "enforce_q_lims" in kvar - helps convergence in some cases.

        **trafo3w_losses** - defines where open loop losses of three-winding transformers are considered. Valid options are "hv", "mv", "lv" for HV/MV/LV side or "star" for the star point.

        **v_debug** (bool, False) - if True, voltage values in each newton-raphson iteration are logged in the ppc

        **init_vm_pu** (string/float/array/Series, None) - Allows to define initialization specifically for voltage magnitudes. Only works with init == "auto"!

            - "auto": all buses are initialized with the mean value of all voltage controlled elements in the grid
            - "flat" for flat start from 1.0
            - "results": voltage magnitude vector is taken from result table
            - a float with which all voltage magnitudes are initialized
            - an iterable with a voltage magnitude value for each bus (length and order has to match with the buses in net.bus)
            - a pandas Series with a voltage magnitude value for each bus (indexes have to match the indexes in net.bus)

        **init_va_degree** (string/float/array/Series, None) - Allows to define initialization specifically for voltage angles. Only works with init == "auto"!

            - "auto": voltage angles are initialized from DC power flow if angles are calculated or as 0 otherwise
            - "dc": voltage angles are initialized from DC power flow
            - "flat" for flat start from 0
            - "results": voltage angle vector is taken from result table
            - a float with which all voltage angles are initialized
            - an iterable with a voltage angle value for each bus (length and order has to match with the buses in net.bus)
            - a pandas Series with a voltage angle value for each bus (indexes have to match the indexes in net.bus)

        **recycle** (dict, none) - Reuse of internal powerflow variables for time series calculation

            Contains a dict with the following parameters:
            _is_elements: If True in service elements are not filtered again and are taken from the last result in net["_is_elements"]
            ppc: If True the ppc is taken from net["_ppc"] and gets updated instead of reconstructed entirely
            Ybus: If True the admittance matrix (Ybus, Yf, Yt) is taken from ppc["internal"] and not reconstructed

    """

    # if dict 'user_pf_options' is present in net, these options overrule the net.__internal_options
    # except for parameters that are passed by user
    overrule_options = {}
    if "user_pf_options" in net.keys() and len(net.user_pf_options) > 0:
        passed_parameters = _passed_runpp_parameters(locals())
        overrule_options = {
            key: val
            for key, val in net.user_pf_options.items()
            if key not in passed_parameters.keys()
        }

    kwargs.update(overrule_options)

    trafo3w_losses = kwargs.get("trafo3w_losses", "hv")
    v_debug = kwargs.get("v_debug", False)
    delta_q = kwargs.get("delta_q", 0)
    r_switch = kwargs.get("r_switch", 0.0)
    numba = kwargs.get("numba", True)
    init_vm_pu = kwargs.get("init_vm_pu", None)
    init_va_degree = kwargs.get("init_va_degree", None)
    recycle = kwargs.get("recycle", None)
    if "init" in overrule_options:
        init = overrule_options["init"]

    ## check if numba is available and the corresponding flag
    if numba == True:
        numba = _check_if_numba_is_installed(numba)

    if voltage_depend_loads:
        if not (np.any(net["load"]["const_z_percent"].values)
                or np.any(net["load"]["const_i_percent"].values)):
            voltage_depend_loads = False

    if algorithm not in ['nr', 'bfsw', 'iwamoto_nr'
                         ] and voltage_depend_loads == True:
        logger.warning(
            "voltage-dependent loads not supported for {0} power flow algorithm -> "
            "loads will be considered as constant power".format(algorithm))

    ac = True
    mode = "pf"
    copy_constraints_to_ppc = False
    if calculate_voltage_angles == "auto":
        calculate_voltage_angles = False
        is_hv_bus = np.where(net.bus.vn_kv.values > 70)[0]
        if any(is_hv_bus) > 0:
            line_buses = set(net.line[["from_bus", "to_bus"]].values.flatten())
            hv_buses = net.bus.index[is_hv_bus]
            if any(a in line_buses for a in hv_buses):
                calculate_voltage_angles = True

    default_max_iteration = {
        "nr": 10,
        "iwamoto_nr": 10,
        "bfsw": 100,
        "gs": 10000,
        "fdxb": 30,
        "fdbx": 30
    }
    if max_iteration == "auto":
        max_iteration = default_max_iteration[algorithm]

    if init != "auto" and ((init_va_degree != None) or (init_vm_pu != None)):
        raise ValueError(
            "Either define initialization through 'init' or through 'init_vm_pu' and 'init_va_degree'."
        )

    if init == "auto":
        if init_va_degree is None or (isinstance(init_va_degree, str)
                                      and init_va_degree == "auto"):
            init_va_degree = "dc" if calculate_voltage_angles else "flat"
        if init_vm_pu is None or (isinstance(init_vm_pu, str)
                                  and init_vm_pu == "auto"):
            init_vm_pu = (net.ext_grid.vm_pu.sum() + net.gen.vm_pu.sum()) / \
                          (len(net.ext_grid.vm_pu) + len(net.gen.vm_pu))
    elif init == "dc":
        init_vm_pu = "flat"
        init_va_degree = "dc"
    else:
        init_vm_pu = init
        init_va_degree = init

    # init options
    net._options = {}
    _add_ppc_options(net,
                     calculate_voltage_angles=calculate_voltage_angles,
                     trafo_model=trafo_model,
                     check_connectivity=check_connectivity,
                     mode=mode,
                     copy_constraints_to_ppc=copy_constraints_to_ppc,
                     r_switch=r_switch,
                     init_vm_pu=init_vm_pu,
                     init_va_degree=init_va_degree,
                     enforce_q_lims=enforce_q_lims,
                     recycle=recycle,
                     voltage_depend_loads=voltage_depend_loads,
                     delta=delta_q,
                     trafo3w_losses=trafo3w_losses)
    _add_pf_options(net,
                    tolerance_kva=tolerance_kva,
                    trafo_loading=trafo_loading,
                    numba=numba,
                    ac=ac,
                    algorithm=algorithm,
                    max_iteration=max_iteration,
                    v_debug=v_debug)
    net._options.update(overrule_options)
    _check_bus_index_and_print_warning_if_high(net)
    _check_gen_index_and_print_warning_if_high(net)
    _powerflow(net, **kwargs)
예제 #3
0
def rundcpp(net,
            trafo_model="t",
            trafo_loading="current",
            recycle=None,
            check_connectivity=True,
            r_switch=0.0,
            trafo3w_losses="hv",
            **kwargs):
    """
    Runs PANDAPOWER DC Flow

    INPUT:
        **net** - The pandapower format network

    OPTIONAL:
        **trafo_model** (str, "t")  - transformer equivalent circuit model
        pandapower provides two equivalent circuit models for the transformer:

            - "t" - transformer is modeled as equivalent with the T-model. This is consistent with PowerFactory and is also more accurate than the PI-model. We recommend using this transformer model.
            - "pi" - transformer is modeled as equivalent PI-model. This is consistent with Sincal, but the method is questionable since the transformer is physically T-shaped. We therefore recommend the use of the T-model.

        **trafo_loading** (str, "current") - mode of calculation for transformer loading

            Transformer loading can be calculated relative to the rated current or the rated power. In both cases the overall transformer loading is defined as the maximum loading on the two sides of the transformer.

            - "current"- transformer loading is given as ratio of current flow and rated current of the transformer. This is the recommended setting, since thermal as well as magnetic effects in the transformer depend on the current.
            - "power" - transformer loading is given as ratio of apparent power flow to the rated apparent power of the transformer.

        **recycle** (dict, none) - Reuse of internal powerflow variables for time series calculation

            Contains a dict with the following parameters:
            _is_elements: If True in service elements are not filtered again and are taken from the last result in net["_is_elements"]
            ppc: If True the ppc (PYPOWER case file) is taken from net["_ppc"] and gets updated instead of reconstructed entirely
            Ybus: If True the admittance matrix (Ybus, Yf, Yt) is taken from ppc["internal"] and not reconstructed

        **check_connectivity** (bool, False) - Perform an extra connectivity test after the conversion from pandapower to PYPOWER

            If true, an extra connectivity test based on SciPy Compressed Sparse Graph Routines is perfomed.
            If check finds unsupplied buses, they are put out of service in the PYPOWER matrix

        **r_switch** (float, 0.0) - resistance of bus-bus-switches. If impedance is zero, buses connected by a closed bus-bus switch are fused to model an ideal bus. Otherwise, they are modelled as branches with resistance r_switch

        ****kwargs** - options to use for PYPOWER.runpf
    """
    ac = False
    numba = True
    mode = "pf"
    init = 'flat'

    numba = _check_if_numba_is_installed(numba)

    # the following parameters have no effect if ac = False
    calculate_voltage_angles = True
    copy_constraints_to_ppc = False
    enforce_q_lims = False
    algorithm = None
    max_iteration = None
    tolerance_kva = None

    # net.__internal_options = {}
    net._options = {}
    _add_ppc_options(net,
                     calculate_voltage_angles=calculate_voltage_angles,
                     trafo_model=trafo_model,
                     check_connectivity=check_connectivity,
                     mode=mode,
                     copy_constraints_to_ppc=copy_constraints_to_ppc,
                     r_switch=r_switch,
                     init_vm_pu=init,
                     init_va_degree=init,
                     enforce_q_lims=enforce_q_lims,
                     recycle=recycle,
                     voltage_depend_loads=False,
                     delta=0,
                     trafo3w_losses=trafo3w_losses)
    _add_pf_options(net,
                    tolerance_kva=tolerance_kva,
                    trafo_loading=trafo_loading,
                    numba=numba,
                    ac=ac,
                    algorithm=algorithm,
                    max_iteration=max_iteration)
    _check_bus_index_and_print_warning_if_high(net)
    _check_gen_index_and_print_warning_if_high(net)
    _powerflow(net, **kwargs)
예제 #4
0
def runpp(net, algorithm='nr', calculate_voltage_angles="auto", init="auto", max_iteration="auto",
          tolerance_kva=1e-5, trafo_model="t", trafo_loading="current", enforce_q_lims=False,
          numba=True, recycle=None, check_connectivity=True, r_switch=0.0, voltage_depend_loads=True,
          delta_q=0, **kwargs):
    """
    Runs PANDAPOWER AC Flow

    INPUT:
        **net** - The pandapower format network

    OPTIONAL:
        **algorithm** (str, "nr") - algorithm that is used to solve the power flow problem.

            The following algorithms are available:

                - "nr" newton-raphson (pypower implementation with numba accelerations)
                - "bfsw" backward/forward sweep (specially suited for radial and weakly-meshed networks)
                - "gs" gauss-seidel (pypower implementation)
                - "fdbx" (pypower implementation)
                - "fdxb"(pypower implementation)

        **calculate_voltage_angles** (bool, "auto") - consider voltage angles in loadflow calculation

            If True, voltage angles of ext_grids and transformer shifts are considered in the
            loadflow calculation. Considering the voltage angles is only necessary in meshed
            networks that are usually found in higher networks. Thats why calculate_voltage_angles
            in "auto" mode defaults to:

                - True, if the network voltage level is above 70 kV
                - False otherwise

            The network voltage level is defined as the maximum rated voltage in the network that
            is connected to a line.

        **init** (str, "auto") - initialization method of the loadflow
        pandapower supports four methods for initializing the loadflow:

            - "auto" - init defaults to "dc" if calculate_voltage_angles is True or "flat" otherwise
            - "flat"- flat start with voltage of 1.0pu and angle of 0° at all PQ-buses and 0° for PV buses as initial solution
            - "dc" - initial DC loadflow before the AC loadflow. The results of the DC loadflow are used as initial solution for the AC loadflow.
            - "results" - voltage vector of last loadflow from net.res_bus is used as initial solution. This can be useful to accelerate convergence in iterative loadflows like time series calculations.

        Considering the voltage angles might lead to non-convergence of the power flow in flat start.
        That is why in "auto" mode, init defaults to "dc" if calculate_voltage_angles is True or "flat" otherwise

        **max_iteration** (int, "auto") - maximum number of iterations carried out in the power flow algorithm.

            In "auto" mode, the default value depends on the power flow solver:

                - 10 for "nr"
                - 100 for "bfsw"
                - 1000 for "gs"
                - 30 for "fdbx"
                - 30 for "fdxb"

        **tolerance_kva** (float, 1e-5) - loadflow termination condition referring to P / Q mismatch of node power in kva

        **trafo_model** (str, "t")  - transformer equivalent circuit model
        pandapower provides two equivalent circuit models for the transformer:

            - "t" - transformer is modeled as equivalent with the T-model.
            - "pi" - transformer is modeled as equivalent PI-model. This is not recommended, since it is less exact than the T-model. It is only recommended for valdiation with other software that uses the pi-model.

        **trafo_loading** (str, "current") - mode of calculation for transformer loading

            Transformer loading can be calculated relative to the rated current or the rated power. In both cases the overall transformer loading is defined as the maximum loading on the two sides of the transformer.

            - "current"- transformer loading is given as ratio of current flow and rated current of the transformer. This is the recommended setting, since thermal as well as magnetic effects in the transformer depend on the current.
            - "power" - transformer loading is given as ratio of apparent power flow to the rated apparent power of the transformer.

        **enforce_q_lims** (bool, False) - respect generator reactive power limits

            If True, the reactive power limits in net.gen.max_q_kvar/min_q_kvar are respected in the
            loadflow. This is done by running a second loadflow if reactive power limits are
            violated at any generator, so that the runtime for the loadflow will increase if reactive
            power has to be curtailed.

            Note: enforce_q_lims only works if algorithm="nr"!

        **numba** (bool, True) - Activation of numba JIT compiler in the newton solver

            If set to True, the numba JIT compiler is used to generate matrices for the powerflow,
            which leads to significant speed improvements.

        **recycle** (dict, none) - Reuse of internal powerflow variables for time series calculation

            Contains a dict with the following parameters:
            _is_elements: If True in service elements are not filtered again and are taken from the last result in net["_is_elements"]
            ppc: If True the ppc is taken from net["_ppc"] and gets updated instead of reconstructed entirely
            Ybus: If True the admittance matrix (Ybus, Yf, Yt) is taken from ppc["internal"] and not reconstructed

        **check_connectivity** (bool, True) - Perform an extra connectivity test after the conversion from pandapower to PYPOWER

            If True, an extra connectivity test based on SciPy Compressed Sparse Graph Routines is perfomed.
            If check finds unsupplied buses, they are set out of service in the ppc

        **r_switch** (float, 0.0) - resistance of bus-bus-switches. If impedance is zero, buses connected by a closed bus-bus switch are fused to model an ideal bus. Otherwise, they are modelled as branches with resistance r_switch.

        **voltage_depend_loads** (bool, True) - consideration of voltage-dependent loads. If False, net.load.const_z_percent and net.load.const_i_percent are not considered, i.e. net.load.p_kw and net.load.q_kvar are considered as constant-power loads.

        **delta_q** - Reactive power tolerance for option "enforce_q_lims" in kvar - helps convergence in some cases.

        ****kwargs** - options to use for PYPOWER.runpf
    """

    # if dict 'user_pf_options' is present in net, these options overrule the net.__internal_options
    # except for parameters that are passed by user
    overrule_options = {}
    if "user_pf_options" in net.keys() and len(net.user_pf_options) > 0:
        passed_parameters = _passed_runpp_parameters(locals())
        overrule_options = {key: val for key, val in net.user_pf_options.items()
                            if key not in passed_parameters.keys()}

        ## check if numba is available and the corresponding flag
    if numba:
        numba = _check_if_numba_is_installed(numba)

    if voltage_depend_loads:
        if not (np.any(net["load"]["const_z_percent"].values) or
                    np.any(net["load"]["const_i_percent"].values)):
            voltage_depend_loads = False

    if algorithm not in ['nr', 'bfsw'] and voltage_depend_loads == True:
        logger.warning("voltage-dependent loads not supported for {0} power flow algorithm -> "
                       "loads will be considered as constant power".format(algorithm))

    ac = True
    mode = "pf"
    copy_constraints_to_ppc = False
    if calculate_voltage_angles == "auto":
        calculate_voltage_angles = False
        hv_buses = np.where(net.bus.vn_kv.values > 70)[0]
        if len(hv_buses) > 0:
            line_buses = net.line[["from_bus", "to_bus"]].values.flatten()
            if len(set(net.bus.index[hv_buses]) & set(line_buses)) > 0:
                calculate_voltage_angles = True
    if init == "auto":
        init = "dc" if calculate_voltage_angles else "flat"
    if init == "results" and len(net.res_bus) == 0:
        init = "auto"
    default_max_iteration = {"nr": 10, "bfsw": 100, "gs": 10000, "fdxb": 30, "fdbx": 30}
    if max_iteration == "auto":
        max_iteration = default_max_iteration[algorithm]

    # init options
    # net.__internal_options = {}
    net._options = {}
    _add_ppc_options(net, calculate_voltage_angles=calculate_voltage_angles,
                     trafo_model=trafo_model, check_connectivity=check_connectivity,
                     mode=mode, copy_constraints_to_ppc=copy_constraints_to_ppc,
                     r_switch=r_switch, init=init, enforce_q_lims=enforce_q_lims,
                     recycle=recycle, voltage_depend_loads=voltage_depend_loads, delta=delta_q)
    _add_pf_options(net, tolerance_kva=tolerance_kva, trafo_loading=trafo_loading,
                    numba=numba, ac=ac, algorithm=algorithm, max_iteration=max_iteration)
    # net.__internal_options.update(overrule_options)
    net._options.update(overrule_options)
    _powerflow(net, **kwargs)
예제 #5
0
    def estimate(self,
                 v_start=None,
                 delta_start=None,
                 calculate_voltage_angles=True):
        """
        The function estimate is the main function of the module. It takes up to three input
        arguments: v_start, delta_start and calculate_voltage_angles. The first two are the initial
        state variables for the estimation process. Usually they can be initialized in a
        "flat-start" condition: All voltages being 1.0 pu and all voltage angles being 0 degrees.
        In this case, the parameters can be left at their default values (None). If the estimation
        is applied continuously, using the results from the last estimation as the starting
        condition for the current estimation can decrease the  amount of iterations needed to
        estimate the current state. The third parameter defines whether all voltage angles are
        calculated absolutely, including phase shifts from transformers. If only the relative
        differences between buses are required, this parameter can be set to False. Returned is a
        boolean value, which is true after a successful estimation and false otherwise.
        The resulting complex voltage will be written into the pandapower network. The result
        fields are found res_bus_est of the pandapower network.

        INPUT:
            **net** - The net within this line should be created

            **v_start** (np.array, shape=(1,), optional) - Vector with initial values for all
            voltage magnitudes in p.u. (sorted by bus index)

            **delta_start** (np.array, shape=(1,), optional) - Vector with initial values for all
            voltage angles in degrees (sorted by bus index)

        OPTIONAL:
            **calculate_voltage_angles** - (bool) - Take into account absolute voltage angles and
            phase shifts in transformers Default is True.

        OUTPUT:
            **successful** (boolean) - True if the estimation process was successful

        Optional estimation variables:
            The bus power injections can be accessed with *se.s_node_powers* and the estimated
            values corresponding to the (noisy) measurement values with *se.hx*. (*hx* denotes h(x))

        EXAMPLE:
            success = estimate(np.array([1.0, 1.0, 1.0]), np.array([0.0, 0.0, 0.0]))

        """
        if self.net is None:
            raise UserWarning("Component was not initialized with a network.")
        t0 = time()
        # add initial values for V and delta
        # node voltages
        # V<delta
        if v_start is None:
            v_start = np.ones(self.net.bus.shape[0])
        if delta_start is None:
            delta_start = np.zeros(self.net.bus.shape[0])

        # initialize result tables if not existent
        _copy_power_flow_results(self.net)

        # initialize ppc
        ppc, ppci = _init_ppc(self.net, v_start, delta_start,
                              calculate_voltage_angles)

        # add measurements to ppci structure
        ppci = _add_measurements_to_ppc(self.net, ppci, self.s_ref)

        # calculate relevant vectors from ppci measurements
        z, self.pp_meas_indices, r_cov = _build_measurement_vectors(ppci)

        # number of nodes
        n_active = len(np.where(ppci["bus"][:, 1] != 4)[0])
        slack_buses = np.where(ppci["bus"][:, 1] == 3)[0]

        # Check if observability criterion is fulfilled and the state estimation is possible
        if len(z) < 2 * n_active - 1:
            self.logger.error("System is not observable (cancelling)")
            self.logger.error(
                "Measurements available: %d. Measurements required: %d" %
                (len(z), 2 * n_active - 1))
            return False

        # set the starting values for all active buses
        v_m = ppci["bus"][:, 7]
        delta = ppci["bus"][:, 8] * np.pi / 180  # convert to rad
        delta_masked = np.ma.array(delta, mask=False)
        delta_masked.mask[slack_buses] = True
        non_slack_buses = np.arange(len(delta))[~delta_masked.mask]

        # matrix calculation object
        sem = wls_matrix_ops(ppci, slack_buses, non_slack_buses, self.s_ref)

        # state vector
        E = np.concatenate((delta_masked.compressed(), v_m))

        # invert covariance matrix
        r_inv = csr_matrix(np.linalg.inv(np.diagflat(r_cov)**2))

        current_error = 100.
        cur_it = 0
        G_m, r, H, h_x = None, None, None, None

        while current_error > self.tolerance and cur_it < self.max_iterations:
            self.logger.debug(" Starting iteration %d" % (1 + cur_it))
            try:
                # create h(x) for the current iteration
                h_x = sem.create_hx(v_m, delta)

                # residual r
                r = csr_matrix(z - h_x).T

                # jacobian matrix H
                H = csr_matrix(sem.create_jacobian(v_m, delta))

                # gain matrix G_m
                # G_m = H^t * R^-1 * H
                G_m = H.T * (r_inv * H)

                # state vector difference d_E
                # d_E = G_m^-1 * (H' * R^-1 * r)
                d_E = spsolve(G_m, H.T * (r_inv * r))
                E += d_E

                # update V/delta
                delta[non_slack_buses] = E[:len(non_slack_buses)]
                v_m = np.squeeze(E[len(non_slack_buses):])

                # prepare next iteration
                cur_it += 1
                current_error = np.max(np.abs(d_E))
                self.logger.debug("Current error: %.7f" % current_error)

            except np.linalg.linalg.LinAlgError:
                self.logger.error(
                    "A problem appeared while using the linear algebra methods."
                    "Check and change the measurement set.")
                return False

        # print output for results
        if current_error <= self.tolerance:
            successful = True
            self.logger.debug(
                "WLS State Estimation successful (%d iterations)" % cur_it)
        else:
            successful = False
            self.logger.debug(
                "WLS State Estimation not successful (%d/%d iterations)" %
                (cur_it, self.max_iterations))

        # store results for all elements
        # calculate bus power injections
        v_cpx = v_m * np.exp(1j * delta)
        bus_powers_conj = np.zeros(len(v_cpx), dtype=np.complex128)
        for i in range(len(v_cpx)):
            bus_powers_conj[i] = np.dot(sem.Y_bus[i, :], v_cpx) * np.conjugate(
                v_cpx[i])

        ppci["bus"][:, 2] = bus_powers_conj.real  # saved in per unit
        ppci["bus"][:, 3] = -bus_powers_conj.imag  # saved in per unit
        ppci["bus"][:, 7] = v_m
        ppci["bus"][:, 8] = delta * 180 / np.pi  # convert to degree

        # calculate line results (in ppc_i)
        s_ref, bus, gen, branch = _get_pf_variables_from_ppci(ppci)[0:4]
        out = np.flatnonzero(branch[:,
                                    BR_STATUS] == 0)  # out-of-service branches
        br = np.flatnonzero(branch[:, BR_STATUS]).astype(
            int)  # in-service branches
        # complex power at "from" bus
        Sf = v_cpx[np.real(branch[br, F_BUS]).astype(int)] * np.conj(
            sem.Yf[br, :] * v_cpx) * s_ref
        # complex power injected at "to" bus
        St = v_cpx[np.real(branch[br, T_BUS]).astype(int)] * np.conj(
            sem.Yt[br, :] * v_cpx) * s_ref
        branch[np.ix_(br, [PF, QF, PT, QT])] = np.c_[Sf.real, Sf.imag, St.real,
                                                     St.imag]
        branch[np.ix_(out, [PF, QF, PT, QT])] = np.zeros((len(out), 4))
        et = time() - t0
        ppci = _store_results_from_pf_in_ppci(ppci, bus, gen, branch,
                                              successful, cur_it, et)

        # convert to pandapower indices
        ppc = _copy_results_ppci_to_ppc(ppci, ppc, mode="se")

        # extract results from ppc
        _add_pf_options(self.net,
                        tolerance_kva=1e-5,
                        trafo_loading="current",
                        numba=True,
                        ac=True,
                        algorithm='nr',
                        max_iteration="auto")
        # writes res_bus.vm_pu / va_degree and res_line
        _extract_results_se(self.net, ppc)

        # restore backup of previous results
        _rename_results(self.net)

        # additionally, write bus power injection results (these are not written in _extract_results)
        mapping_table = self.net["_pd2ppc_lookups"]["bus"]
        self.net.res_bus_est.p_kw = -get_values(
            ppc["bus"][:, 2], self.net.bus.index.values,
            mapping_table) * self.s_ref / 1e3
        self.net.res_bus_est.q_kvar = -get_values(
            ppc["bus"][:, 3], self.net.bus.index.values,
            mapping_table) * self.s_ref / 1e3

        # store variables required for chi^2 and r_N_max test:
        self.R_inv = r_inv.toarray()
        self.Gm = G_m.toarray()
        self.r = r.toarray()
        self.H = H.toarray()
        self.Ht = self.H.T
        self.hx = h_x
        self.V = v_m
        self.delta = delta

        # delete results which are not correctly calculated
        for k in list(self.net.keys()):
            if k.startswith("res_") and k.endswith("_est") and \
                    k not in ("res_bus_est", "res_line_est", "res_trafo_est", "res_trafo3w_est"):
                del self.net[k]

        return successful
예제 #6
0
    def estimate(self,
                 v_start='flat',
                 delta_start='flat',
                 calculate_voltage_angles=True,
                 zero_injection=None,
                 fuse_buses_with_bb_switch='all'):
        """
        The function estimate is the main function of the module. It takes up to three input
        arguments: v_start, delta_start and calculate_voltage_angles. The first two are the initial
        state variables for the estimation process. Usually they can be initialized in a
        "flat-start" condition: All voltages being 1.0 pu and all voltage angles being 0 degrees.
        In this case, the parameters can be left at their default values (None). If the estimation
        is applied continuously, using the results from the last estimation as the starting
        condition for the current estimation can decrease the  amount of iterations needed to
        estimate the current state. The third parameter defines whether all voltage angles are
        calculated absolutely, including phase shifts from transformers. If only the relative
        differences between buses are required, this parameter can be set to False. Returned is a
        boolean value, which is true after a successful estimation and false otherwise.
        The resulting complex voltage will be written into the pandapower network. The result
        fields are found res_bus_est of the pandapower network.

        INPUT:
            **net** - The net within this line should be created

            **v_start** (np.array, shape=(1,), optional) - Vector with initial values for all
            voltage magnitudes in p.u. (sorted by bus index)

            **delta_start** (np.array, shape=(1,), optional) - Vector with initial values for all
            voltage angles in degrees (sorted by bus index)

        OPTIONAL:
            **calculate_voltage_angles** - (bool) - Take into account absolute voltage angles and
            phase shifts in transformers Default is True
            
            **zero_injection** - (str, iterable, None) - Defines which buses are zero injection bus or the method
            to identify zero injection bus, with 'wls_estimator' virtual measurements will be added, with 
            'wls_estimator with zero constraints' the buses will be handled as constraints
            "auto": all bus without p,q measurement, without p, q value (load, sgen...) and aux buses will be
                identified as zero injection bus  
            "aux_bus": only aux bus will be identified as zero injection bus
            None: no bus will be identified as zero injection bus
            iterable: the iterable should contain index of the zero injection bus and also aux bus will be identified
                as zero-injection bus
    
            **fuse_buses_with_bb_switch** - (str, iterable, None) - Defines how buses with closed bb switches should 
            be handled, if fuse buses will only fused to one for calculation, if not fuse, an auxiliary bus and 
            auxiliary line will be automatically added to the network to make the buses with different p,q injection
            measurements identifieble
            "all": all buses with bb-switches will be fused, the same as the default behaviour in load flow
            None: buses with bb-switches and individual p,q measurements will be reconfigurated
                by auxiliary elements
            iterable: the iterable should contain the index of buses to be fused, the behaviour is contigous e.g.
                if one of the bus among the buses connected through bb switch is given, then all of them will still
                be fused
        OUTPUT:
            **successful** (boolean) - True if the estimation process was successful

        Optional estimation variables:
            The bus power injections can be accessed with *se.s_node_powers* and the estimated
            values corresponding to the (noisy) measurement values with *se.hx*. (*hx* denotes h(x))

        EXAMPLE:
            success = estimate(np.array([1.0, 1.0, 1.0]), np.array([0.0, 0.0, 0.0]))

        """
        if self.net is None:
            raise UserWarning("Component was not initialized with a network.")
        t0 = time()

        # change the configuration of the pp net to avoid auto fusing of buses connected
        # through bb switch with elements on each bus if this feature enabled
        bus_to_be_fused = None
        if fuse_buses_with_bb_switch != 'all' and not self.net.switch.empty:
            if isinstance(fuse_buses_with_bb_switch, str):
                raise UserWarning(
                    "fuse_buses_with_bb_switch parameter is not correctly initialized"
                )
            elif hasattr(fuse_buses_with_bb_switch, '__iter__'):
                bus_to_be_fused = fuse_buses_with_bb_switch
            _add_aux_elements_for_bb_switch(self.net, bus_to_be_fused)

        # add initial values for V and delta
        # node voltages
        # V<delta
        if v_start is None:
            v_start = "flat"
        if delta_start is None:
            delta_start = "flat"

        # initialize result tables if not existent
        _copy_power_flow_results(self.net)

        # initialize ppc
        ppc, ppci = _init_ppc(self.net, v_start, delta_start,
                              calculate_voltage_angles)

        # add measurements to ppci structure
        ppci = _add_measurements_to_ppc(self.net, ppci, zero_injection)

        # Finished converting pandapower network to ppci
        # Estimate voltage magnitude and angle with the given estimator
        delta, v_m = self.estimator.estimate(ppci)

        # store results for all elements
        # calculate bus power injections
        v_cpx = v_m * np.exp(1j * delta)
        bus_powers_conj = np.zeros(len(v_cpx), dtype=np.complex128)
        for i in range(len(v_cpx)):
            bus_powers_conj[i] = np.dot(ppci['internal']['Y_bus'][i, :],
                                        v_cpx) * np.conjugate(v_cpx[i])

        ppci["bus"][:, 2] = bus_powers_conj.real  # saved in per unit
        ppci["bus"][:, 3] = -bus_powers_conj.imag  # saved in per unit
        ppci["bus"][:, 7] = v_m
        ppci["bus"][:, 8] = delta * 180 / np.pi  # convert to degree

        # calculate line results (in ppc_i)
        s_ref, bus, gen, branch = _get_pf_variables_from_ppci(ppci)[0:4]
        out = np.flatnonzero(branch[:,
                                    BR_STATUS] == 0)  # out-of-service branches
        br = np.flatnonzero(branch[:, BR_STATUS]).astype(
            int)  # in-service branches
        # complex power at "from" bus
        Sf = v_cpx[np.real(branch[br, F_BUS]).astype(int)] * np.conj(
            ppci['internal']['Yf'][br, :] * v_cpx) * s_ref
        # complex power injected at "to" bus
        St = v_cpx[np.real(branch[br, T_BUS]).astype(int)] * np.conj(
            ppci['internal']['Yt'][br, :] * v_cpx) * s_ref
        branch[np.ix_(br, [PF, QF, PT, QT])] = np.c_[Sf.real, Sf.imag, St.real,
                                                     St.imag]
        branch[np.ix_(out, [PF, QF, PT, QT])] = np.zeros((len(out), 4))
        et = time() - t0
        ppci = _store_results_from_pf_in_ppci(ppci, bus, gen, branch,
                                              self.estimator.successful,
                                              self.estimator.iterations, et)

        # convert to pandapower indices
        ppc = _copy_results_ppci_to_ppc(ppci, ppc, mode="se")

        # extract results from ppc
        _add_pf_options(self.net,
                        tolerance_mva=1e-8,
                        trafo_loading="current",
                        numba=True,
                        ac=True,
                        algorithm='nr',
                        max_iteration="auto")
        # writes res_bus.vm_pu / va_degree and res_line
        _extract_results_se(self.net, ppc)

        # restore backup of previous results
        _rename_results(self.net)

        # additionally, write bus power injection results (these are not written in _extract_results)
        mapping_table = self.net["_pd2ppc_lookups"]["bus"]
        self.net.res_bus_est.p_mw = -get_values(
            ppc["bus"][:, 2], self.net.bus.index.values, mapping_table)
        self.net.res_bus_est.q_mvar = -get_values(
            ppc["bus"][:, 3], self.net.bus.index.values, mapping_table)
        _clean_up(self.net)
        # clear the aux elements and calculation results created for the substitution of bb switches
        if fuse_buses_with_bb_switch != 'all' and not self.net.switch.empty:
            _drop_aux_elements_for_bb_switch(self.net)

        # delete results which are not correctly calculated
        for k in list(self.net.keys()):
            if k.startswith("res_") and k.endswith("_est") and \
                    k not in ("res_bus_est", "res_line_est", "res_trafo_est", "res_trafo3w_est"):
                del self.net[k]

        return self.estimator.successful
예제 #7
0
def runpp_3ph(net,
              calculate_voltage_angles=True,
              init="auto",
              max_iteration="auto",
              tolerance_mva=1e-8,
              trafo_model='t',
              trafo_loading="current",
              enforce_q_lims=False,
              numba=True,
              recycle=None,
              check_connectivity=True,
              switch_rx_ratio=2.0,
              delta_q=0,
              v_debug=False,
              **kwargs):
    """
 runpp_3ph: Performs Unbalanced/Asymmetric/Three Phase Load flow

    INPUT:
        **net** - The pandapower format network

    OPTIONAL:
        **algorithm** (str, "nr") - algorithm that is used to solve the power
        flow problem.

            The following algorithms are available:

                - "nr" Newton-Raphson (pypower implementation with numba accelerations)

                Used only for positive sequence network

                Zero and Negative sequence networks use Current Injection method

                Vnew = Y.inv * Ispecified ( from s_abc/v_abc old)

                Icalculated = Y * Vnew


        **calculate_voltage_angles** (bool, "auto") - consider voltage angles
        in loadflow calculation

            If True, voltage angles of ext_grids and transformer shifts are
            considered in the loadflow calculation. Considering the voltage
            angles is only necessary in meshed networks that are usually
            found in higher voltage levels. calculate_voltage_angles
            in "auto" mode defaults to:

                - True, if the network voltage level is above 70 kV
                - False otherwise

            The network voltage level is defined as the maximum rated voltage
            of any bus in the network that is connected to a line.


        **max_iteration** (int, "auto") - maximum number of iterations carried
        out in the power flow algorithm.

            In "auto" mode, the default value depends on the power flow solver:

                - 10 for "nr"

            For three phase calculations, its extended to 3 * max_iteration

        **tolerance_mva** (float, 1e-8) - loadflow termination condition
        referring to P / Q mismatch of node power in MVA

        **trafo_model**

            - transformer equivalent models
            - "t" - transformer is modeled as equivalent with the T-model.
            - "pi" - This is not recommended, since it is less exact than the T-model. So, for three phase load flow, its not implemented


        **trafo_loading** (str, "current") - mode of calculation for
        transformer loading

            Transformer loading can be calculated relative to the rated
            current or the rated power. In both cases the overall transformer
            loading is defined as the maximum loading on the two sides of
            the transformer.

            - "current"- transformer loading is given as ratio of current
                        flow and rated current of the transformer. This is the recommended
                        setting, since thermal as well as magnetic effects in the
                        transformer depend on the current.
            - "power" - transformer loading is given as ratio of apparent
                        power flow to the rated apparent power of the transformer.

        **enforce_q_lims** (bool, False) (Not tested with 3 Phase load flow)
        - respect generator reactive power limits

            If True, the reactive power limits in net.gen.max_q_mvar/min_q_mvar
            are respected in the loadflow. This is done by running a second
            loadflow if reactive power limits are violated at any generator,
            so that the runtime for the loadflow will increase if reactive
            power has to be curtailed.

            Note: enforce_q_lims only works if algorithm="nr"!


        **check_connectivity** (bool, True) - Perform an extra connectivity
        test after the conversion from pandapower to PYPOWER.

            If True, an extra connectivity test based on SciPy Compressed
            Sparse Graph Routines is perfomed. If check finds unsupplied buses,
            they are set out of service in the ppc

        **voltage_depend_loads** (bool, True)
        (Not tested with 3 Phase load flow) - consideration of voltage-dependent loads.

            If False, ``net.load.const_z_percent`` and ``net.load.const_i_percent``
            are not considered, i.e. ``net.load.p_mw`` and ``net.load.q_mvar``
            are considered as constant-power loads.

        **consider_line_temperature** (bool, False) (Not tested with 3 Phase
        load flow) - adjustment of line impedance based on provided line temperature.

            If True, ``net.line`` must contain a column ``temperature_degree_celsius``.
            The temperature dependency coefficient alpha must be provided in
            the ``net.line.alpha`` column, otherwise the default value of 0.004 is used.


        **KWARGS**:

        **numba** (bool, True) - Activation of numba JIT compiler in the
        newton solver

            If set to True, the numba JIT compiler is used to generate
            matrices for the powerflow, which leads to significant speed
            improvements.

        **switch_rx_ratio** (float, 2)

        (Not tested with 3 Phase load flow)  - rx_ratio of bus-bus-switches.
        If impedance is zero, buses connected by a closed bus-bus switch
        are fused to model an ideal bus. Otherwise, they are modelled
        as branches with resistance defined as z_ohm column in switch
        table and this parameter

        **delta_q**

        (Not tested with 3 Phase load flow) - Reactive power tolerance for option "enforce_q_lims"
        in kvar - helps convergence in some cases.

        **trafo3w_losses**

        (Not tested with 3 Phase load flow) - defines where open loop losses of three-winding
        transformers are considered. Valid options are "hv", "mv", "lv"
        for HV/MV/LV side or "star" for the star point.

        **v_debug** (bool, False) (Not tested with 3 Phase load flow) - if True,
        voltage values in each newton-raphson iteration are logged in the ppc.

        **init_vm_pu** (string/float/array/Series, None) (Not tested with 3
        Phase load flow) - Allows to define initialization specifically for
        voltage magnitudes. Only works with ``init == "auto"``!

            - "auto": all buses are initialized with the mean value of all
              voltage controlled elements in the grid
            - "flat" for flat start from 1.0
            - "results": voltage magnitude vector is taken from result table
            - a float with which all voltage magnitudes are initialized
            - an iterable with a voltage magnitude value for each bus
              (length and order has to match with the buses in net.bus)
            - a pandas Series with a voltage magnitude value for each bus
              (indexes have to match the indexes in net.bus)

         **init_va_degree** (string/float/array/Series, None) (Not tested with
         3 Phase load flow) - Allows to define initialization specifically for voltage angles.
         Only works with ``init == "auto"``!

            - "auto": voltage angles are initialized from DC power flow
              if angles are calculated or as 0 otherwise
            - "dc": voltage angles are initialized from DC power flow
            - "flat" for flat start from 0
            - "results": voltage angle vector is taken from result table
            - a float with which all voltage angles are initialized
            - an iterable with a voltage angle value for each bus (length
              and order has to match with the buses in net.bus)
            - a pandas Series with a voltage angle value for each bus (indexes
              have to match the indexes in net.bus)

        **recycle** (dict, none) - Reuse of internal powerflow variables for
        time series calculation.

            Contains a dict with the following parameters:
            bus_pq: If True PQ values of buses are updated

            gen: If True Sbus and the gen table in the ppc are recalculated

            Ybus: If True the admittance matrix (Ybus, Yf, Yt) is taken from

            ppc["internal"] and not reconstructed

        **neglect_open_switch_branches** (bool, False)

        (Not tested with 3 Phase load flow) - If True no auxiliary
        buses are created for branches when switches are opened at the branch.
        Instead branches are set out of service

    SEE ALSO:
         pp.add_zero_impedance_parameters(net):
         To add zero sequence parameters into network from the standard type

    EXAMPLES:
        Use this module like this:

        .. code-block:: python

            from pandapower.pf.runpp_3ph import runpp_3ph
            runpp_3ph(net)

    NOTES:
        - Three phase load flow uses Sequence Frame for power flow solution.
        - Three phase system is modelled with earth return.
        - PH-E load type is called as wye since Neutral and Earth are considered same
        - This solver has proved successful only for Earthed transformers (i.e Dyn,Yyn,YNyn & Yzn vector groups)
    """
    # =============================================================================
    # pandapower settings
    # =============================================================================
    overrule_options = {}
    if "user_pf_options" in net.keys() and len(net.user_pf_options) > 0:
        passed_parameters = _passed_runpp_parameters(locals())
        overrule_options = {
            key: val
            for key, val in net.user_pf_options.items()
            if key not in passed_parameters.keys()
        }
    if numba:
        numba = _check_if_numba_is_installed(numba)

    ac = True
    mode = "pf_3ph"  # TODO: Make valid modes (pf, pf_3ph, se, etc.) available in seperate file (similar to idx_bus.py)
    #    v_debug = kwargs.get("v_debug", False)
    copy_constraints_to_ppc = False
    if trafo_model == 'pi':
        raise Not_implemented("Three phase Power Flow doesnot support pi model\
                                because of lack of accuracy")


#    if calculate_voltage_angles == "auto":
#        calculate_voltage_angles = False
#        hv_buses = np.where(net.bus.vn_kv.values > 70)[0]  # Todo: Where does that number come from?
#        if len(hv_buses) > 0:
#            line_buses = net.line[["from_bus", "to_bus"]].values.flatten()
#            if len(set(net.bus.index[hv_buses]) & set(line_buses)) > 0:
# scipy spsolve options in NR power flow
    use_umfpack = kwargs.get("use_umfpack", True)
    permc_spec = kwargs.get("permc_spec", None)
    calculate_voltage_angles = True
    if init == "results" and net.res_bus_3ph.empty:
        init = "auto"
    if init == "auto":
        init = "dc" if calculate_voltage_angles else "flat"
    default_max_iteration = {
        "nr": 10,
        "bfsw": 10,
        "gs": 10000,
        "fdxb": 30,
        "fdbx": 30
    }
    if max_iteration == "auto":
        max_iteration = default_max_iteration["nr"]

    neglect_open_switch_branches = kwargs.get("neglect_open_switch_branches",
                                              False)
    only_v_results = kwargs.get("only_v_results", False)
    net._options = {}
    _add_ppc_options(net, calculate_voltage_angles=calculate_voltage_angles,
                     trafo_model=trafo_model, check_connectivity=check_connectivity,
                     mode=mode, switch_rx_ratio=switch_rx_ratio,
                     init_vm_pu=init, init_va_degree=init,
                     enforce_q_lims=enforce_q_lims, recycle=None,
                     voltage_depend_loads=False, delta=delta_q,\
                     neglect_open_switch_branches=neglect_open_switch_branches
                     )
    _add_pf_options(net, tolerance_mva=tolerance_mva, trafo_loading=trafo_loading,
                    numba=numba, ac=ac, algorithm="nr", max_iteration=max_iteration,\
                    only_v_results=only_v_results,v_debug=v_debug, use_umfpack=use_umfpack,
                    permc_spec=permc_spec)
    net._options.update(overrule_options)
    _check_bus_index_and_print_warning_if_high(net)
    _check_gen_index_and_print_warning_if_high(net)
    # =========================================================================
    # pd2ppc conversion
    # =========================================================================
    _, ppci1 = _pd2ppc_recycle(net, 1, recycle=recycle)

    _, ppci2 = _pd2ppc_recycle(net, 2, recycle=recycle)
    gs_eg, bs_eg = _add_ext_grid_sc_impedance(net, ppci2)

    _, ppci0 = _pd2ppc_recycle(net, 0, recycle=recycle)

    _, bus0, gen0, branch0, _, _, _ = _get_pf_variables_from_ppci(ppci0)
    base_mva, bus1, gen1, branch1, sl_bus, _, pq_bus = _get_pf_variables_from_ppci(
        ppci1)
    _, bus2, gen2, branch2, _, _, _ = _get_pf_variables_from_ppci(ppci2)

    # initialize the results after the conversion to ppc is done, otherwise init=results does not work
    init_results(net, "pf_3ph")

    # =============================================================================
    #     P Q values aggragated and summed up for each bus to make s_abc matrix
    #     s_abc for wye connections ; s_abc_delta for delta connection
    # =============================================================================
    s_abc_delta, s_abc = _load_mapping(net, ppci1)
    # =========================================================================
    # Construct Sequence Frame Bus admittance matrices Ybus
    # =========================================================================

    ppci0, ppci1, ppci2, y_0_pu, y_1_pu, y_2_pu, y_0_f, y_1_f, _,\
        y_0_t, y_1_t, _ = _get_y_bus(ppci0, ppci1, ppci2, recycle)
    # =========================================================================
    # Initial voltage values
    # =========================================================================
    nb = ppci1["bus"].shape[0]

    # make sure flat start is always respected, even with other voltage data in recycled ppc
    if init == "flat":
        v_012_it = np.zeros((3, nb), dtype=np.complex128)
        v_012_it[1, :] = 1.0
    else:
        v_012_it = np.concatenate([
            np.array(ppc["bus"][:, VM] *
                     np.exp(1j * np.deg2rad(ppc["bus"][:, VA]))).reshape(
                         (1, nb)) for ppc in (ppci0, ppci1, ppci2)
        ],
                                  axis=0).astype(np.complex128)

    # For Delta transformation:
    # Voltage changed from line-earth to line-line using V_T
    # s_abc/v_abc will now give line-line currents. This is converted to line-earth
    # current using I-T
    v_del_xfmn = np.array([[1, -1, 0], [0, 1, -1], [-1, 0, 1]])
    i_del_xfmn = np.array([[1, 0, -1], [-1, 1, 0], [0, -1, 1]])
    v_abc_it = sequence_to_phase(v_012_it)

    # =========================================================================
    #             Iteration using Power mismatch criterion
    # =========================================================================
    outer_tolerance_mva = 3e-8
    count = 0
    s_mismatch = np.array([[True], [True]], dtype=bool)
    t0 = perf_counter()
    while (s_mismatch >
           outer_tolerance_mva).any() and count < 30 * max_iteration:
        # =====================================================================
        #     Voltages and Current transformation for PQ and Slack bus
        # =====================================================================
        s_abc_pu = -np.divide(s_abc, ppci1["baseMVA"])
        s_abc_delta_pu = -np.divide(s_abc_delta, ppci1["baseMVA"])

        i_abc_it_wye = (np.divide(s_abc_pu, v_abc_it)).conjugate()
        i_abc_it_delta = np.matmul(i_del_xfmn, (np.divide(
            s_abc_delta_pu, np.matmul(v_del_xfmn, v_abc_it))).conjugate())

        # For buses with both delta and wye loads we need to sum of their currents
        # to sum up the currents
        i_abc_it = i_abc_it_wye + i_abc_it_delta
        i012_it = phase_to_sequence(i_abc_it)
        v1_for_s1 = v_012_it[1, :]
        i1_for_s1 = -i012_it[1, :]
        v0_pu_it = X012_to_X0(v_012_it)
        v2_pu_it = X012_to_X2(v_012_it)
        i0_pu_it = X012_to_X0(i012_it)
        i2_pu_it = X012_to_X2(i012_it)
        s1 = np.multiply(v1_for_s1, i1_for_s1.conjugate())
        # =============================================================================
        # Current used to find S1 Positive sequence power
        # =============================================================================

        ppci1["bus"][pq_bus, PD] = np.real(s1[pq_bus]) * ppci1["baseMVA"]
        ppci1["bus"][pq_bus, QD] = np.imag(s1[pq_bus]) * ppci1["baseMVA"]
        # =============================================================================
        # Conduct Positive sequence power flow
        # =============================================================================
        _run_newton_raphson_pf(ppci1, net._options)
        # =============================================================================
        # Conduct Negative and Zero sequence power flow
        # =============================================================================
        v0_pu_it = V_from_I(y_0_pu, i0_pu_it)
        v2_pu_it = V_from_I(y_2_pu, i2_pu_it)
        # =============================================================================
        #    Evaluate Positive Sequence Power Mismatch
        # =============================================================================
        i1_from_v_it = I1_from_V012(v_012_it, y_1_pu).flatten()
        s_from_voltage = S_from_VI_elementwise(v1_for_s1, i1_from_v_it)
        v1_pu_it = V1_from_ppc(ppci1)

        v_012_new = combine_X012(v0_pu_it, v1_pu_it, v2_pu_it)

        s_mismatch = np.abs(
            np.abs(s1[pq_bus]) - np.abs(s_from_voltage[pq_bus]))
        v_012_it = v_012_new
        v_abc_it = sequence_to_phase(v_012_it)
        count += 1
    et = perf_counter() - t0
    success = (count < 30 * max_iteration)
    for ppc in [ppci0, ppci1, ppci2]:
        ppc["et"] = et
        ppc["success"] = success
    # TODO: Add reference to paper to explain the following steps
    # This is required since the ext_grid power results are not correct if its
    # not done
    ref, pv, pq = bustypes(ppci0["bus"], ppci0["gen"])
    ref_gens = ppci0["internal"]["ref_gens"]
    ppci0["bus"][ref, GS] -= gs_eg
    ppci0["bus"][ref, BS] -= bs_eg
    y_0_pu, y_0_f, y_0_t = makeYbus(ppci0["baseMVA"], ppci0["bus"],
                                    ppci0["branch"])
    # revert the change, otherwise repeated calculation with recycled elements will fail
    ppci0["bus"][ref, GS] += gs_eg
    ppci0["bus"][ref, BS] += bs_eg
    # Bus, Branch, and Gen  power values
    bus0, gen0, branch0 = pfsoln(base_mva, bus0, gen0, branch0, y_0_pu, y_0_f,
                                 y_0_t, v_012_it[0, :].flatten(), sl_bus,
                                 ref_gens)
    bus1, gen1, branch1 = pfsoln(base_mva, bus1, gen1, branch1, y_1_pu, y_1_f,
                                 y_1_t, v_012_it[1, :].flatten(), sl_bus,
                                 ref_gens)
    bus2, gen2, branch2 = pfsoln(base_mva, bus2, gen2, branch2, y_1_pu, y_1_f,
                                 y_1_t, v_012_it[2, :].flatten(), sl_bus,
                                 ref_gens)
    ppci0 = _store_results_from_pf_in_ppci(ppci0, bus0, gen0, branch0)
    ppci1 = _store_results_from_pf_in_ppci(ppci1, bus1, gen1, branch1)
    ppci2 = _store_results_from_pf_in_ppci(ppci2, bus2, gen2, branch2)
    i_012_res = _current_from_voltage_results(y_0_pu, y_1_pu, v_012_it)
    s_012_res = S_from_VI_elementwise(v_012_it, i_012_res) * ppci1["baseMVA"]
    eg_is_mask = net["_is_elements"]['ext_grid']
    ext_grid_lookup = net["_pd2ppc_lookups"]["ext_grid"]
    eg_is_idx = net["ext_grid"].index.values[eg_is_mask]
    eg_idx_ppc = ext_grid_lookup[eg_is_idx]
    """ # 2 ext_grids Fix: Instead of the generator index, bus indices of the generators are used"""
    eg_bus_idx_ppc = np.real(ppci1["gen"][eg_idx_ppc, GEN_BUS]).astype(int)

    ppci0["gen"][eg_idx_ppc, PG] = s_012_res[0, eg_bus_idx_ppc].real
    ppci1["gen"][eg_idx_ppc, PG] = s_012_res[1, eg_bus_idx_ppc].real
    ppci2["gen"][eg_idx_ppc, PG] = s_012_res[2, eg_bus_idx_ppc].real
    ppci0["gen"][eg_idx_ppc, QG] = s_012_res[0, eg_bus_idx_ppc].imag
    ppci1["gen"][eg_idx_ppc, QG] = s_012_res[1, eg_bus_idx_ppc].imag
    ppci2["gen"][eg_idx_ppc, QG] = s_012_res[2, eg_bus_idx_ppc].imag

    ppc0 = net["_ppc0"]
    ppc1 = net["_ppc1"]
    ppc2 = net["_ppc2"]

    # ppci doesn't contain out of service elements, but ppc does -> copy results accordingly
    ppc0 = _copy_results_ppci_to_ppc(ppci0, ppc0, mode=mode)
    ppc1 = _copy_results_ppci_to_ppc(ppci1, ppc1, mode=mode)
    ppc2 = _copy_results_ppci_to_ppc(ppci2, ppc2, mode=mode)

    _extract_results_3ph(net, ppc0, ppc1, ppc2)

    #    Raise error if PF was not successful. If DC -> success is always 1

    if not ppci0["success"]:
        net["converged"] = False
        _clean_up(net, res=False)
        raise LoadflowNotConverged("Power Flow {0} did not converge after\
                                {1} iterations!".format("nr", count))
    else:
        net["converged"] = True

    _clean_up(net)