def _run_ac_pf_without_qlims_enforced(ppci, options): if options["numba"]: makeYbus = makeYbus_numba pfsoln = pfsoln_numba else: makeYbus = makeYbus_pypower pfsoln = pfsoln_pypower baseMVA, bus, gen, branch, ref, pv, pq, _, _, V0 = _get_pf_variables_from_ppci( ppci) ppci, Ybus, Yf, Yt = _get_Y_bus(ppci, options, makeYbus, baseMVA, bus, branch) ## compute complex bus power injections [generation - load] Sbus = makeSbus(baseMVA, bus, gen) ## run the newton power flow V, success, _, ppci["internal"]["J"] = newtonpf(Ybus, Sbus, V0, pv, pq, ppci, options) ## update data matrices with solution bus, gen, branch = pfsoln(baseMVA, bus, gen, branch, Ybus, Yf, Yt, V, ref) return ppci, success, bus, gen, branch
def _run_ac_pf_without_qlims_enforced(ppci, recycle, makeYbus, ppopt): baseMVA, bus, gen, branch, ref, pv, pq, _, gbus, V0, ref_gens = _get_pf_variables_from_ppci(ppci) ppci, Ybus, Yf, Yt = _get_Y_bus(ppci, recycle, makeYbus, baseMVA, bus, branch) ## compute complex bus power injections [generation - load] Sbus = makeSbus(baseMVA, bus, gen) ## run the power flow V, success, it = _call_power_flow_function(baseMVA, bus, branch, Ybus, Sbus, V0, ref, pv, pq, ppopt) ## update data matrices with solution bus, gen, branch = pfsoln(baseMVA, bus, gen, branch, Ybus, Yf, Yt, V, ref, ref_gens) return ppci, success, bus, gen, branch, it
def _run_dc_pf(ppci): t0 = time() baseMVA, bus, gen, branch, ref, pv, pq, on, gbus, _ = _get_pf_variables_from_ppci( ppci) ppci["bus"][:, VM] = 1.0 ## 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[:, VM] = ones(bus.shape[0]) 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 ppci = _store_results_from_pf_in_ppci(ppci, bus, gen, branch) ppci["et"] = time() - t0 ppci["success"] = True return ppci
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) ## 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
def _run_ac_pf_without_qlims_enforced(ppci, options): if options["numba"]: try: makeYbus = makeYbus_numba except: makeYbus = makeYbus_pypower else: makeYbus = makeYbus_pypower baseMVA, bus, gen, branch, ref, pv, pq, _, gbus, V0 = _get_pf_variables_from_ppci( ppci) ppci, Ybus, Yf, Yt = _get_Y_bus(ppci, options, makeYbus, baseMVA, bus, branch) ## compute complex bus power injections [generation - load] Sbus = makeSbus(baseMVA, bus, gen) ## compute complex bus current injections from constant current loads Ibus = _get_ibus(ppci) ## run the newton power flow V, success, _ = newtonpf(Ybus, Sbus, V0, pv, pq, options, Ibus=Ibus) ## update data matrices with solution bus, gen, branch = pfsoln(baseMVA, bus, gen, branch, Ybus, Yf, Yt, V, ref, pv, pq, Ibus=Ibus) return ppci, success, bus, gen, branch
def newtonpf(Ybus, Sbus, V0, pv, pq, ppci, options): """Solves the power flow using a full Newton's method. Solves for bus voltages given the full system admittance matrix (for all buses), the complex bus power injection vector (for all buses), the initial vector of complex bus voltages, and column vectors with the lists of bus indices for the swing bus, PV buses, and PQ buses, respectively. The bus voltage vector contains the set point for generator (including ref bus) buses, and the reference angle of the swing bus, as well as an initial guess for remaining magnitudes and angles. @see: L{runpf} @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln Modified by University of Kassel (Florian Schaefer) to use numba """ ## options tol = options['tolerance_kva'] * 1e-3 max_it = options["max_iteration"] numba = options["numba"] iwamoto = options["algorithm"] == "iwamoto_nr" voltage_depend_loads = options["voltage_depend_loads"] v_debug = options["v_debug"] baseMVA = ppci['baseMVA'] bus = ppci['bus'] gen = ppci['gen'] ## initialize i = 0 V = V0 Va = angle(V) Vm = abs(V) dVa, dVm = None, None if iwamoto: dVm, dVa = zeros_like(Vm), zeros_like(Va) if v_debug: Vm_it = Vm.copy() Va_it = Va.copy() else: Vm_it = None Va_it = None ## set up indexing for updating V pvpq = r_[pv, pq] # generate lookup pvpq -> index pvpq (used in createJ) pvpq_lookup = zeros(max(Ybus.indices) + 1, dtype=int) pvpq_lookup[pvpq] = arange(len(pvpq)) # get jacobian function createJ = get_fastest_jacobian_function(pvpq, pq, numba) npv = len(pv) npq = len(pq) j1 = 0 j2 = npv ## j1:j2 - V angle of pv buses j3 = j2 j4 = j2 + npq ## j3:j4 - V angle of pq buses j5 = j4 j6 = j4 + npq ## j5:j6 - V mag of pq buses ## evaluate F(x0) F = _evaluate_Fx(Ybus, V, Sbus, pv, pq) converged = _check_for_convergence(F, tol) Ybus = Ybus.tocsr() J = None ## do Newton iterations while (not converged and i < max_it): ## update iteration counter i = i + 1 J = create_jacobian_matrix(Ybus, V, pvpq, pq, createJ, pvpq_lookup, npv, npq, numba) dx = -1 * spsolve(J, F) ## update voltage if npv and not iwamoto: Va[pv] = Va[pv] + dx[j1:j2] if npq and not iwamoto: Va[pq] = Va[pq] + dx[j3:j4] Vm[pq] = Vm[pq] + dx[j5:j6] # iwamoto multiplier to increase convergence if iwamoto: Vm, Va = _iwamoto_step(Ybus, J, F, dx, pvpq, pq, createJ, pvpq_lookup, npv, npq, numba, dVa, dVm, Vm, Va, pv, j1, j2, j3, j4, j5, j6) V = Vm * exp(1j * Va) Vm = abs(V) ## update Vm and Va again in case Va = angle(V) ## we wrapped around with a negative Vm if v_debug: Vm_it = column_stack((Vm_it, Vm)) Va_it = column_stack((Va_it, Va)) if voltage_depend_loads: Sbus = makeSbus(baseMVA, bus, gen, vm=Vm) F = _evaluate_Fx(Ybus, V, Sbus, pv, pq) converged = _check_for_convergence(F, tol) return V, converged, i, J, Vm_it, Va_it
def _run_bfswpf(ppci, options, **kwargs): """ SPARSE version of distribution power flow solution according to [1] :References: [1] Jen-Hao Teng, "A Direct Approach for Distribution System Load Flow Solutions", IEEE Transactions on Power Delivery, vol. 18, no. 3, pp. 882-887, July 2003. :param ppci: matpower-style case data :param options: pf options :return: results (pypower style), success (flag about PF convergence) """ time_start = time() # starting pf calculation timing baseMVA, bus, gen, branch, ref, pv, pq, _, gbus, V0, ref_gens = _get_pf_variables_from_ppci( ppci) enforce_q_lims, tolerance_kva, max_iteration, calculate_voltage_angles, numba = _get_options( options) numba, makeYbus = _import_numba_extensions_if_flag_is_true(numba) nobus = bus.shape[0] nobranch = branch.shape[0] # generate Sbus Sbus = makeSbus(baseMVA, bus, gen) # generate results for original bus ordering # Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch) ppci, Ybus, Yf, Yt = _get_Y_bus(ppci, options, makeYbus, baseMVA, bus, branch) # creating network graph from list of branches bus_from = branch[:, F_BUS].real.astype(int) bus_to = branch[:, T_BUS].real.astype(int) G = csr_matrix((np.ones(nobranch), (bus_from, bus_to)), shape=(nobus, nobus)) # create spanning trees using breadth-first-search # TODO add efficiency warning if a network is heavy-meshed G_trees = [] for refbus in ref: G_trees.append(csgraph.breadth_first_tree(G, refbus, directed=False)) # depth-first-search bus ordering and generating Direct Load Flow matrix DLF = BCBV * BIBC ppci, DLF, buses_ordered_bfs_nets = _get_bibc_bcbv( ppci, options, bus, branch, G) # if there are trafos with phase-shift calculate Ybus without phase-shift for bfswpf any_trafo_shift = (branch[:, SHIFT] != 0).any() if any_trafo_shift: branch_noshift = branch.copy() branch_noshift[:, SHIFT] = 0 Ybus_noshift, Yf_noshift, _ = makeYbus(baseMVA, bus, branch_noshift) else: Ybus_noshift = Ybus.copy() # #----- run the power flow ----- V_final, success = _bfswpf(DLF, bus, gen, branch, baseMVA, Ybus_noshift, Sbus, V0, ref, pv, pq, buses_ordered_bfs_nets, options, **kwargs) # if phase-shifting trafos are present adjust final state vector angles accordingly if calculate_voltage_angles and any_trafo_shift: brch_shift_mask = branch[:, SHIFT] != 0 trafos_shift = dict( list( zip( list( zip(branch[brch_shift_mask, F_BUS].real.astype(int), branch[brch_shift_mask, T_BUS].real.astype(int))), branch[brch_shift_mask, SHIFT].real))) for trafo_ind, shift_degree in iteritems(trafos_shift): neti = 0 # if multiple reference nodes, find in which network trafo is located if len(ref) > 0: for refbusi in range(len(ref)): if trafo_ind[0] in buses_ordered_bfs_nets[refbusi]: neti = refbusi break G_tree = G_trees[neti] buses_ordered_bfs = buses_ordered_bfs_nets[neti] if (np.argwhere(buses_ordered_bfs == trafo_ind[0]) < np.argwhere(buses_ordered_bfs == trafo_ind[1])): lv_bus = trafo_ind[1] shift_degree *= -1 else: lv_bus = trafo_ind[0] buses_shifted_from_root = csgraph.breadth_first_order( G_tree, lv_bus, directed=True, return_predecessors=False) V_final[buses_shifted_from_root] *= np.exp(1j * np.pi / 180 * shift_degree) # #----- output results to ppc ------ ppci["et"] = time() - time_start # pf time end bus, gen, branch = pfsoln(baseMVA, bus, gen, branch, Ybus, Yf, Yt, V_final, ref, ref_gens) # bus, gen, branch = pfsoln_bfsw(baseMVA, bus, gen, branch, V_final, ref, pv, pq, BIBC, ysh_f,ysh_t,Iinj, Sbus) ppci["success"] = success ppci["bus"], ppci["gen"], ppci["branch"] = bus, gen, branch return ppci, success
def _bfswpf(DLF, bus, gen, branch, baseMVA, Ybus, Sbus, V0, ref, pv, pq, buses_ordered_bfs_nets, options, **kwargs): """ distribution power flow solution according to [1] :param DLF: direct-Load-Flow matrix which relates bus current injections to voltage drops from the root bus :param bus: buses martix :param gen: generators matrix :param branch: branches matrix :param baseMVA: :param Ybus: bus admittance matrix :param Sbus: vector of power injections :param V0: initial voltage state vector :param ref: reference bus index :param pv: PV buses indices :param pq: PQ buses indices :param buses_ordered_bfs_nets: buses ordered according to breadth-first search :return: power flow result """ enforce_q_lims = options["enforce_q_lims"] tolerance_kva = options["tolerance_kva"] max_iteration = options["max_iteration"] voltage_depend_loads = options["voltage_depend_loads"] # setting options tolerance_mva = tolerance_kva * 1e-3 max_it = max_iteration # maximum iterations verbose = kwargs["VERBOSE"] # verbose is set in run._runpppf() # # tolerance for the inner loop for PV nodes if 'tolerance_kva_pv' in kwargs: tol_mva_inner = kwargs['tolerance_kva_pv'] * 1e-3 else: tol_mva_inner = 1.e-2 if 'max_iter_pv' in kwargs: max_iter_pv = kwargs['max_iter_pv'] else: max_iter_pv = 20 nobus = bus.shape[0] ngen = gen.shape[0] mask_root = ~(bus[:, BUS_TYPE] == 3) # mask for eliminating root bus norefs = len(ref) Ysh = _makeYsh_bfsw(bus, branch, baseMVA) # detect generators on PV buses which have status ON gen_pv = np.in1d(gen[:, GEN_BUS], pv) & (gen[:, GEN_STATUS] > 0) qg_lim = np.zeros( ngen, dtype=bool) # initialize generators which violated Q limits Iinj = np.conj(Sbus / V0) - Ysh * V0 # Initial current injections # initiate reference voltage vector V_ref = np.ones(nobus, dtype=complex) for neti, buses_ordered_bfs in enumerate(buses_ordered_bfs_nets): V_ref[buses_ordered_bfs] *= V0[ref[neti]] V = V0.copy() n_iter = 0 converged = 0 if verbose: print(' -- AC Power Flow (Backward/Forward sweep)\n') while not converged and n_iter < max_it: n_iter_inner = 0 n_iter += 1 deltaV = DLF * Iinj[mask_root] V[mask_root] = V_ref[mask_root] + deltaV # ## # inner loop for considering PV buses # TODO improve PV buses inner loop inner_loop_converged = False while not inner_loop_converged and len(pv) > 0: pvi = pv - norefs # internal PV buses indices, assuming reference node is always 0 Vmis = (np.abs(gen[gen_pv, VG]))**2 - (np.abs(V[pv]))**2 # TODO improve getting values from sparse DLF matrix - DLF[pvi, pvi] is unefficient dQ = (Vmis / (2 * DLF[pvi, pvi].A1.imag)).flatten() gen[gen_pv, QG] += dQ if enforce_q_lims: # check Q violation limits ## find gens with violated Q constraints qg_max_lim = (gen[:, QG] > gen[:, QMAX]) & gen_pv qg_min_lim = (gen[:, QG] < gen[:, QMIN]) & gen_pv if qg_min_lim.any(): gen[qg_min_lim, QG] = gen[qg_min_lim, QMIN] bus[gen[qg_min_lim, GEN_BUS].astype(int), BUS_TYPE] = 1 # convert to PQ bus if qg_max_lim.any(): gen[qg_max_lim, QG] = gen[qg_max_lim, QMAX] bus[gen[qg_max_lim, GEN_BUS].astype(int), BUS_TYPE] = 1 # convert to PQ bus # TODO: correct: once all the PV buses are converted to PQ buses, conversion back to PV is not possible qg_lim_new = qg_min_lim | qg_max_lim if qg_lim_new.any(): pq2pv = (qg_lim != qg_lim_new) & qg_lim # convert PQ to PV bus if pq2pv.any(): bus[gen[qg_max_lim, GEN_BUS].astype(int), BUS_TYPE] = 2 # convert to PV bus qg_lim = qg_lim_new.copy() ref, pv, pq = bustypes(bus, gen) # avoid calling makeSbus, update only Sbus for pv nodes Sbus = (makeSbus(baseMVA, bus, gen, vm=abs(V)) if voltage_depend_loads else makeSbus(baseMVA, bus, gen)) Iinj = np.conj(Sbus / V) - Ysh * V deltaV = DLF * Iinj[mask_root] V[mask_root] = V_ref[mask_root] + deltaV if n_iter_inner > max_iter_pv: raise LoadflowNotConverged( " FBSW Power Flow did not converge - inner iterations for PV nodes " "reached maximum value of {0}!".format(max_iter_pv)) n_iter_inner += 1 if np.all(np.abs(dQ) < tol_mva_inner ): # inner loop termination criterion inner_loop_converged = True # testing termination criterion - if voltage_depend_loads: Sbus = makeSbus(baseMVA, bus, gen, vm=abs(V)) F = _evaluate_Fx(Ybus, V, Sbus, pv, pq) # check tolerance converged = _check_for_convergence(F, tolerance_mva) if converged and verbose: print("\nFwd-back sweep power flow converged in " "{0} iterations.\n".format(n_iter)) # updating injected currents Iinj = np.conj(Sbus / V) - Ysh * V return V, converged
def newtonpf(Ybus, Sbus, V0, pv, pq, ppci, options): """Solves the power flow using a full Newton's method. Solves for bus voltages given the full system admittance matrix (for all buses), the complex bus power injection vector (for all buses), the initial vector of complex bus voltages, and column vectors with the lists of bus indices for the swing bus, PV buses, and PQ buses, respectively. The bus voltage vector contains the set point for generator (including ref bus) buses, and the reference angle of the swing bus, as well as an initial guess for remaining magnitudes and angles. @see: L{runpf} @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln Modified by University of Kassel (Florian Schaefer) to use numba """ ## options tol = options['tolerance_kva'] * 1e-3 max_it = options["max_iteration"] numba = options["numba"] voltage_depend_loads = options["voltage_depend_loads"] baseMVA = ppci['baseMVA'] bus = ppci['bus'] gen = ppci['gen'] ## initialize i = 0 V = V0 Va = angle(V) Vm = abs(V) ## set up indexing for updating V pvpq = r_[pv, pq] # generate lookup pvpq -> index pvpq (used in createJ) pvpq_lookup = zeros(max(Ybus.indices) + 1, dtype=int) pvpq_lookup[pvpq] = arange(len(pvpq)) # import "numba enhanced" functions if numba: # check if pvpq is the same as pq. In this case a faster version of create_J can be used if len(pvpq) == len(pq): createJ = create_J2 else: createJ = create_J npv = len(pv) npq = len(pq) j1 = 0 j2 = npv ## j1:j2 - V angle of pv buses j3 = j2 j4 = j2 + npq ## j3:j4 - V angle of pq buses j5 = j4 j6 = j4 + npq ## j5:j6 - V mag of pq buses ## evaluate F(x0) F = _evaluate_Fx(Ybus, V, Sbus, pv, pq) converged = _check_for_convergence(F, tol) Ybus = Ybus.tocsr() J = None ## do Newton iterations while (not converged and i < max_it): ## update iteration counter i = i + 1 # use numba if activated if numba: J = _create_J_with_numba(Ybus, V, pvpq, pq, createJ, pvpq_lookup, npv, npq) else: J = _create_J_without_numba(Ybus, V, pvpq, pq) dx = -1 * spsolve(J, F) ## update voltage if npv: Va[pv] = Va[pv] + dx[j1:j2] if npq: Va[pq] = Va[pq] + dx[j3:j4] Vm[pq] = Vm[pq] + dx[j5:j6] V = Vm * exp(1j * Va) Vm = abs(V) ## update Vm and Va again in case Va = angle(V) ## we wrapped around with a negative Vm if voltage_depend_loads: Sbus = makeSbus(baseMVA, bus, gen, vm=Vm) F = _evaluate_Fx(Ybus, V, Sbus, pv, pq) converged = _check_for_convergence(F, tol) return V, converged, i, J