Ejemplo n.º 1
0
def _runpf_pypower(ppci, options, **kwargs):
    """
    This is a modified version* of runpf() to run the algorithms gausspf and fdpf from PYPOWER.
    See PYPOWER documentation for more information.

    * mainly the verbose functions and ext2int() int2ext() were deleted
    """

    ##-----  run the power flow  -----
    t0 = perf_counter()
    # ToDo: Options should be extracted in every subfunction not here...
    init_va_degree, ac, numba, recycle, ppopt = _get_options(options, **kwargs)

    if ac:  # AC formulation
        if init_va_degree == "dc":
            ppci = _run_dc_pf(ppci)
            success = True

        ppci, success, bus, gen, branch, it = _ac_runpf(
            ppci, ppopt, numba, recycle)
    else:  ## DC formulation
        ppci = _run_dc_pf(ppci)
        success = True

    et = perf_counter() - t0
    ppci = _store_results_from_pf_in_ppci(ppci, bus, gen, branch, success, it,
                                          et)
    return ppci, success
Ejemplo n.º 2
0
def _run_newton_raphson_pf(ppci, options):
    """
    Runs a Newton-Raphson power flow.

    INPUT
    ppci (dict) - the "internal" ppc (without out ot service elements and sorted elements)
    options(dict) - options for the power flow

    """
    t0 = perf_counter()
    # we cannot run DC pf before running newton with distributed slack because the slacks come pre-solved after the DC pf
    if isinstance(options["init_va_degree"],
                  str) and options["init_va_degree"] == "dc":
        if options['distributed_slack']:
            pg_copy = ppci['gen'][:, PG].copy()
            pd_copy = ppci['bus'][:, PD].copy()
            ppci = _run_dc_pf(ppci)
            ppci['gen'][:, PG] = pg_copy
            ppci['bus'][:, PD] = pd_copy
        else:
            ppci = _run_dc_pf(ppci)
    if options["enforce_q_lims"]:
        ppci, success, iterations, bus, gen, branch = _run_ac_pf_with_qlims_enforced(
            ppci, options)
    else:
        ppci, success, iterations = _run_ac_pf_without_qlims_enforced(
            ppci, options)
        # update data matrices with solution store in ppci
        bus, gen, branch = ppci_to_pfsoln(ppci, options)
    et = perf_counter() - t0
    ppci = _store_results_from_pf_in_ppci(ppci, bus, gen, branch, success,
                                          iterations, et)
    return ppci
Ejemplo n.º 3
0
def _run_newton_raphson_pf(ppci, options):
    """
    Runs a Newton-Raphson power flow.

    INPUT
    ppci (dict) - the "internal" ppc (without out ot service elements and sorted elements)
    options(dict) - options for the power flow

    """
    t0 = time()
    if isinstance(options["init_va_degree"],
                  str) and options["init_va_degree"] == "dc":
        ppci = _run_dc_pf(ppci)
    if options["enforce_q_lims"]:
        ppci, success, iterations, bus, gen, branch = _run_ac_pf_with_qlims_enforced(
            ppci, options)
    else:
        ppci, success, iterations = _run_ac_pf_without_qlims_enforced(
            ppci, options)
        # update data matrices with solution store in ppci
        bus, gen, branch = ppci_to_pfsoln(ppci, options)
    et = time() - t0
    ppci = _store_results_from_pf_in_ppci(ppci, bus, gen, branch, success,
                                          iterations, et)
    return ppci
Ejemplo n.º 4
0
def _run_dc_pf(ppci):
    t0 = time()
    baseMVA, bus, gen, branch, ref, pv, pq, on, gbus, _, refgen = _get_pf_variables_from_ppci(
        ppci)

    ## initial state
    Va0 = bus[:, VA] * (pi / 180.)

    ## build B matrices and phase shift injections
    B, Bf, Pbusinj, Pfinj = makeBdc(bus, branch)

    ## updates Bbus matrix
    ppci['internal']['Bbus'] = B

    ## compute complex bus power injections [generation - load]
    ## adjusted for phase shifters and real shunts
    Pbus = makeSbus(baseMVA, bus, gen) - Pbusinj - bus[:, GS] / baseMVA

    ## "run" the power flow
    Va = dcpf(B, Pbus, Va0, ref, pv, pq)

    ## update data matrices with solution
    branch[:, [QF, QT]] = zeros((branch.shape[0], 2))
    branch[:, PF] = (Bf * Va + Pfinj) * baseMVA
    branch[:, PT] = -branch[:, PF]
    bus[:, VA] = Va * (180. / pi)
    ## update Pg for slack generators
    ## (note: other gens at ref bus are accounted for in Pbus)
    ##      Pg = Pinj + Pload + Gs
    ##      newPg = oldPg + newPinj - oldPinj

    ## ext_grid (refgen) buses
    refgenbus = gen[refgen, GEN_BUS].astype(int)
    ## number of ext_grids (refgen) at those buses
    ext_grids_bus = bincount(refgenbus)
    gen[refgen,
        PG] = real(gen[refgen, PG] + (B[refgenbus, :] * Va - Pbus[refgenbus]) *
                   baseMVA / ext_grids_bus[refgenbus])

    # store results from DC powerflow for AC powerflow
    et = time() - t0
    success = True
    iterations = 1
    ppci = _store_results_from_pf_in_ppci(ppci, bus, gen, branch, success,
                                          iterations, et)
    return ppci
Ejemplo n.º 5
0
def _run_newton_raphson_pf(ppci, options):
    """Runs a newton raphson power flow.
    """

    ##-----  run the power flow  -----

    t0 = time()
    if options["init_va_degree"] == "dc":
        ppci = _run_dc_pf(ppci)
    if options["enforce_q_lims"]:
        ppci, success, iterations, bus, gen, branch = _run_ac_pf_with_qlims_enforced(
            ppci, options)
    else:
        ppci, success, iterations, bus, gen, branch = _run_ac_pf_without_qlims_enforced(
            ppci, options)
    et = time() - t0
    ppci = _store_results_from_pf_in_ppci(ppci, bus, gen, branch, success,
                                          iterations, et)
    return ppci
Ejemplo n.º 6
0
def _run_fast_decoupled_pf(ppci, options):
    """
    This is a modified version of _run_newton_raphson to run fast decoupled
    algorithms.
    """

    t0 = time()

    if options["init_va_degree"] == "dc":
        ppci = _run_dc_pf(ppci)
    if options["enforce_q_lims"]:
        ppci, success, iterations, bus, gen, branch = \
            _run_ac_pf_with_qlims_enforced(ppci, options)
    else:
        ppci, success, iterations = \
            _run_ac_pf_without_qlims_enforced(ppci, options)
        # update data matrices with the solution stores in ppci
        bus, gen, branch = ppci_to_pfsoln(ppci, options)
    et = time() - t0
    ppci = _store_results_from_pf_in_ppci(ppci, bus, gen, branch, success,
                                          iterations, et)
    return ppci    
Ejemplo n.º 7
0
def _run_dc_pf(ppci):
    t0 = time()
    baseMVA, bus, gen, branch, ref, pv, pq, on, gbus, _, _ = _get_pf_variables_from_ppci(ppci)

    ## initial state
    Va0 = bus[:, VA] * (pi / 180.)

    ## build B matrices and phase shift injections
    B, Bf, Pbusinj, Pfinj = makeBdc(bus, branch)

    ## compute complex bus power injections [generation - load]
    ## adjusted for phase shifters and real shunts
    Pbus = makeSbus(baseMVA, bus, gen) - Pbusinj - bus[:, GS] / baseMVA

    ## "run" the power flow
    Va = dcpf(B, Pbus, Va0, ref, pv, pq)

    ## update data matrices with solution
    branch[:, [QF, QT]] = zeros((branch.shape[0], 2))
    branch[:, PF] = (Bf * Va + Pfinj) * baseMVA
    branch[:, PT] = -branch[:, PF]
    bus[:, VA] = Va * (180. / pi)
    ## update Pg for slack generator (1st gen at ref bus)
    ## (note: other gens at ref bus are accounted for in Pbus)
    ##      Pg = Pinj + Pload + Gs
    ##      newPg = oldPg + newPinj - oldPinj

    refgen = zeros(len(ref), dtype=int)
    for k in range(len(ref)):
        temp = find(gbus == ref[k])
        refgen[k] = on[temp[0]]
    gen[refgen, PG] = real(gen[refgen, PG] + (B[ref, :] * Va - Pbus[ref]) * baseMVA)

    # store results from DC powerflow for AC powerflow
    et = time() - t0
    success = True
    iterations = 1
    ppci = _store_results_from_pf_in_ppci(ppci, bus, gen, branch, success, iterations, et)
    return ppci
Ejemplo n.º 8
0
    def runpp(self, net, max_iteration=10, need_reset=True, **kwargs):
        net_orig = copy.deepcopy(net)
        pp.runpp(net_orig)
        V_orig = net_orig._ppc["internal"]["V"]

        # ---------- pp.run.runpp() -----------------
        t0_start = time()

        t0_options = time()
        passed_parameters = _passed_runpp_parameters(locals())
        _init_runpp_options(net,
                            algorithm="nr",
                            calculate_voltage_angles="auto",
                            init="auto",
                            max_iteration=max_iteration,
                            tolerance_mva=1e-8,
                            trafo_model="t",
                            trafo_loading="current",
                            enforce_q_lims=False,
                            check_connectivity=False,
                            voltage_depend_loads=True,
                            consider_line_temperature=False,
                            passed_parameters=passed_parameters,
                            numba=True,
                            **kwargs)
        _check_bus_index_and_print_warning_if_high(net)
        _check_gen_index_and_print_warning_if_high(net)
        et_options = time() - t0_options

        # ---------- pp.powerflow._powerflow() -----------------
        """
        Gets called by runpp or rundcpp with different arguments.
        """
        # get infos from options
        t0_early_init = time()
        init_results = net["_options"]["init_results"]
        ac = net["_options"]["ac"]
        algorithm = net["_options"]["algorithm"]

        net["converged"] = False
        net["OPF_converged"] = False
        _add_auxiliary_elements(net)

        if not ac or init_results:
            verify_results(net)
        else:
            reset_results(net, all_empty=False)

        # TODO remove this when zip loads are integrated for all PF algorithms
        if algorithm not in ['nr', 'bfsw']:
            net["_options"]["voltage_depend_loads"] = False

        _add_auxiliary_elements(net)
        # convert pandapower net to ppc
        ppc, self.ppci = _pd2ppc(net)

        # pdb.set_trace()
        # store variables
        net["_ppc"] = ppc

        if not "VERBOSE" in kwargs:
            kwargs["VERBOSE"] = 0

        # ----- run the powerflow -----
        options = net["_options"]
        et_early_init = time() - t0_early_init

        # ---------- pp.powerflow._run_pf_algorithm() ----------------
        # ---------- pp.pf.run_newton_raphson_pf.run_newton_raphson_pf() ----------------
        t0 = time()
        t0_init = t0
        et_init_dc = 0.
        if need_reset:
            if isinstance(options["init_va_degree"],
                          str) and options["init_va_degree"] == "dc":
                self.ppci = _run_dc_pf(self.ppci)
                et_init_dc = time() - t0
            if options["enforce_q_lims"]:
                raise NotImplementedError("enforce_q_lims not yet implemented")

            t0_init = time()
            # ---------- pp.pf.run_newton_raphson_pf._run_ac_pf_without_qlims_enforced ----------
            # ppci, success, iterations = _run_ac_pf_without_qlims_enforced(ppci, options)
            makeYbus, pfsoln = _get_numba_functions(self.ppci, options)
            self.baseMVA, self.bus, self.gen, self.branch, self.ref, self.pv, self.pq, _, _, V0, self.ref_gens = _get_pf_variables_from_ppci(
                self.ppci)
            self.ppci, self.Ybus, self.Yf, self.Yt = _get_Y_bus(
                self.ppci, options, makeYbus, self.baseMVA, self.bus,
                self.branch)

            # TODO i have a problem here for the order of the bus / id of bus
            tmp_bus_ind = np.argsort(net.bus.index)
            model = DataModel()
            # model.set_sn_mva(net.sn_mva)
            # model.set_f_hz(net.f_hz)

            # TODO set that elsewhere
            self.converter.set_sn_mva(net.sn_mva)
            self.converter.set_f_hz(net.f_hz)

            # init_but should be called first among all the rest
            model.init_bus(net.bus.iloc[tmp_bus_ind]["vn_kv"].values,
                           net.line.shape[0], net.trafo.shape[0])

            # init the shunts
            line_r, line_x, line_h = self.converter.get_line_param(
                net.line["r_ohm_per_km"].values * net.line["length_km"].values,
                net.line["x_ohm_per_km"].values * net.line["length_km"].values,
                net.line["c_nf_per_km"].values * net.line["length_km"].values,
                net.line["g_us_per_km"].values * net.line["length_km"].values,
                net.bus.loc[net.line["from_bus"]]["vn_kv"],
                net.bus.loc[net.line["to_bus"]]["vn_kv"])
            model.init_powerlines(line_r, line_x, line_h,
                                  net.line["from_bus"].values,
                                  net.line["to_bus"].values)

            # init the shunts
            model.init_shunt(net.shunt["p_mw"].values,
                             net.shunt["q_mvar"].values,
                             net.shunt["bus"].values)
            # init trafo
            if net.trafo.shape[0]:
                trafo_r, trafo_x, trafo_b = self.converter.get_trafo_param(
                    net.trafo["vn_hv_kv"].values, net.trafo["vn_lv_kv"].values,
                    net.trafo["vk_percent"].values,
                    net.trafo["vkr_percent"].values,
                    net.trafo["sn_mva"].values, net.trafo["pfe_kw"].values,
                    net.trafo["i0_percent"].values,
                    net.bus.loc[net.trafo["lv_bus"]]["vn_kv"])

                # trafo_branch = ppc["branch"][net.line.shape[0]:, :]

                tap_step_pct = net.trafo["tap_step_percent"].values
                tap_step_pct[~np.isfinite(tap_step_pct)] = 0.

                tap_pos = net.trafo["tap_pos"].values
                tap_pos[~np.isfinite(tap_pos)] = 0.

                is_tap_hv_side = net.trafo["tap_side"].values == "hv"
                is_tap_hv_side[~np.isfinite(tap_pos)] = True
                model.init_trafo(trafo_r, trafo_x, trafo_b, tap_step_pct,
                                 tap_pos, is_tap_hv_side,
                                 net.trafo["hv_bus"].values,
                                 net.trafo["lv_bus"].values)

            model.init_loads(net.load["p_mw"].values,
                             net.load["q_mvar"].values, net.load["bus"].values)
            model.init_generators(net.gen["p_mw"].values,
                                  net.gen["vm_pu"].values,
                                  net.gen["bus"].values)
            # TODO better way here!
            model.add_slackbus(net.ext_grid["bus"].values)

            # model.init_Ybus()
            # Ybus = model.get_Ybus()

            # be careful, the order is not the same between this and pandapower, you need to change it
            # Ybus_proper_oder = Ybus[np.array([net.bus.index]).T, np.array([net.bus.index])]
            # self.Ybus_proper_oder = self.Ybus
        else:
            pass
            # TODO update self.ppci with new values of generation - load such that  Sbus is properly udpated

        # compute complex bus power injections [generation - load]
        Sbus = _get_Sbus(self.ppci, False)

        # Sbus_me = model.get_Sbus()
        # pdb.set_trace()
        # Sbus_me_r = np.real(Sbus_me)
        # Va0 = np.full(net.bus.shape[0], fill_value=net["_options"]["init_vm_pu"], dtype=np.complex_)
        # Va0[net.ext_grid["bus"].values] = net.ext_grid["vm_pu"].values * np.exp(1j * net.ext_grid["va_degree"].values / 360. * 2 * np.pi)
        #dctheta = model.dc_pf(Sbus_me_r, Va0)
        # self.dctheta = V0[tmp_bus_ind]

        # self.dcYbus = self.ppci["internal"]['Bbus'][np.array([tmp_bus_ind]).T, np.array([tmp_bus_ind])]
        # tmpdc = np.abs(dcYbus - self.dcYbus)
        # pv_me = model.get_pv()
        # pq_me = model.get_pq()
        # pdb.set_trace()

        # run the newton power  flow
        # ------------------- pp.pypower.newtonpf ---------------------
        max_it = options["max_iteration"]
        tol = options['tolerance_mva']
        self.Ybus = sparse.csc_matrix(self.Ybus)
        et_init = time() - t0_init

        t0__ = time()
        if need_reset:
            # reset the solver
            self.solver.reset()
            self.V = 1.0 * copy.deepcopy(V0)
        else:
            # reuse previous voltages
            pass
        self.solver.solve(self.Ybus, self.V, Sbus, self.pv, self.pq, max_it,
                          tol)
        et__ = time() - t0__

        t0_ = time()
        Va = self.solver.get_Va()
        Vm = self.solver.get_Vm()
        self.V = Vm * np.exp(1j * Va)
        J = self.solver.get_J()
        success = self.solver.converged()
        iterations = self.solver.get_nb_iter()
        # timer_Fx_, timer_solve_, timer_initialize_, timer_check_, timer_dSbus_, timer_fillJ_, timer_total_nr_
        timers = self.solver.get_timers()
        et_ = time() - t0_
        # ---------------------- pp.pypower.newtonpf ---------------------

        self.ppci = _store_internal(
            self.ppci, {
                "J": J,
                "Vm_it": None,
                "Va_it": None,
                "bus": self.bus,
                "gen": self.gen,
                "branch": self.branch,
                "baseMVA": self.baseMVA,
                "V": self.V,
                "pv": self.pv,
                "pq": self.pq,
                "ref": self.ref,
                "Sbus": Sbus,
                "ref_gens": self.ref_gens,
                "Ybus": self.Ybus,
                "Yf": self.Yf,
                "Yt": self.Yt,
                "timers": timers,
                "time_get_res": et_,
                "time_solve": et__,
                "time_init": et_init,
                "time_init_dc": et_init_dc,
                "time_early_init": et_early_init,
                "time_options": et_options
            })
        t0_ppci_to_pfsoln = time()
        # update data matrices with solution store in ppci
        # ---------- pp.pf.run_newton_raphson_pf._run_ac_pf_without_qlims_enforced ----------
        self.bus, self.gen, self.branch = ppci_to_pfsoln(self.ppci, options)
        te_ppci_to_pfsoln = time() - t0_ppci_to_pfsoln
        # these are the values from pypower / matlab
        t0_store_res = time()
        et = t0_store_res - t0
        result = _store_results_from_pf_in_ppci(self.ppci, self.bus, self.gen,
                                                self.branch, success,
                                                iterations, et)
        t0_to_net = time()
        et_store_res = t0_to_net - t0_store_res
        # ---------- pp.pf.run_newton_raphson_pf.run_newton_raphson_pf() ----------------
        # ---------- pp.powerflow._run_pf_algorithm() ----------------

        # read the results (=ppci with results) to net
        _ppci_to_net(result, net)
        et_to_net = time() - t0_to_net
        # ---------- pp.powerflow._powerflow() ----------------
        # ---------- pp.run.runpp() -----------------

        # added
        et_start = time() - t0_start
        self.ppci = _store_internal(
            self.ppci, {
                "time_store_res": et_store_res,
                "time_to_net": et_to_net,
                "time_all": et_start,
                "time_ppci_to_pfsoln": te_ppci_to_pfsoln
            })

        has_conv = model.compute_newton(V0[tmp_bus_ind], max_it, tol)
        # check the results
        results_solver = np.max(np.abs(V_orig - self.V))

        Ybus = model.get_Ybus()
        Ybus_proper_oder = Ybus
        self.Ybus_proper_oder = self.Ybus[np.array([tmp_bus_ind]).T,
                                          np.array([tmp_bus_ind])]

        tmp = np.abs(Ybus_proper_oder - self.Ybus_proper_oder)  # > 1e-7
        por, qor, vor, aor = model.get_lineor_res()
        pex, qex, vex, aex = model.get_lineex_res()
        load_p, load_q, load_v = model.get_loads_res()

        np.max(np.abs(por - net_orig.res_line["p_from_mw"]))
        np.max(np.abs(qor - net_orig.res_line["q_from_mvar"]))
        a_or_pp = np.sqrt(net.res_line["p_from_mw"].values**2 +
                          net.res_line["q_from_mvar"].values**2)
        a_or_pp /= np.sqrt(3) * net.bus.loc[net.line["from_bus"].values][
            "vn_kv"].values * net.res_line["vm_from_pu"].values
        np.max(np.abs(a_or_pp - aor))
        np.max(np.abs(a_or_pp - net.res_line["i_from_ka"]))
        np.max(np.abs(a_or_pp - net.res_line["i_from_ka"]))

        Va_me2 = model.get_Va()
        Vm_me2 = model.get_Vm()
        res_vm = np.abs(Vm_me2 - Vm[tmp_bus_ind])
        res_va = np.abs(Va_me2 - Va[tmp_bus_ind])

        # check that if i start the solver on the data
        Sbus_me = model.get_Sbus()
        pv_me = model.get_pv()
        pq_me = model.get_pq()

        np.all(sorted(pv_me) == sorted(net.gen["bus"]))
        np.all(sorted(pq_me) == sorted(tmp_bus_ind[self.pq]))

        plv, qlv, vlv, alv = model.get_trafolv_res()
        phv, qhv, vhv, ahv = model.get_trafohv_res()
        res_trafo = np.abs(plv - net_orig.res_trafo["p_lv_mw"].values)

        res_trafo_q = np.abs(qlv - net_orig.res_trafo["q_lv_mvar"].values)
        # self.solver.reset()
        # self.solver.solve(Ybus, V0, Sbus, pv_me, pq_me, max_it, tol)
        # Va2 = self.solver.get_Va()
        # Vm2 = self.solver.get_Vm()

        pdb.set_trace()
Ejemplo n.º 9
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
Ejemplo n.º 10
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