def t_hessian(quiet=False): """Numerical tests of 2nd derivative code. @author: Ray Zimmerman (PSERC Cornell) """ t_begin(44, quiet) ## run powerflow to get solved case ppopt = ppoption(VERBOSE=0, OUT_ALL=0) results, _ = runpf(case30(), ppopt) baseMVA, bus, gen, branch = \ results['baseMVA'], results['bus'], results['gen'], results['branch'] ## switch to internal bus numbering and build admittance matrices _, bus, gen, branch = ext2int1(bus, gen, branch) Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch) Vm = bus[:, VM] Va = bus[:, VA] * (pi / 180) V = Vm * exp(1j * Va) f = branch[:, F_BUS] ## list of "from" buses t = branch[:, T_BUS] ## list of "to" buses nl = len(f) nb = len(V) Cf = sparse((ones(nl), (range(nl), f)), (nl, nb)) ## connection matrix for line & from buses Ct = sparse((ones(nl), (range(nl), t)), (nl, nb)) ## connection matrix for line & to buses pert = 1e-8 ##----- check d2Sbus_dV2 code ----- t = ' - d2Sbus_dV2 (complex power injections)' lam = 10 * random.rand(nb) num_Haa = zeros((nb, nb), complex) num_Hav = zeros((nb, nb), complex) num_Hva = zeros((nb, nb), complex) num_Hvv = zeros((nb, nb), complex) dSbus_dVm, dSbus_dVa = dSbus_dV(Ybus, V) Haa, Hav, Hva, Hvv = d2Sbus_dV2(Ybus, V, lam) for i in range(nb): Vap = V.copy() Vap[i] = Vm[i] * exp(1j * (Va[i] + pert)) dSbus_dVm_ap, dSbus_dVa_ap = dSbus_dV(Ybus, Vap) num_Haa[:, i] = (dSbus_dVa_ap - dSbus_dVa).T * lam / pert num_Hva[:, i] = (dSbus_dVm_ap - dSbus_dVm).T * lam / pert Vmp = V.copy() Vmp[i] = (Vm[i] + pert) * exp(1j * Va[i]) dSbus_dVm_mp, dSbus_dVa_mp = dSbus_dV(Ybus, Vmp) num_Hav[:, i] = (dSbus_dVa_mp - dSbus_dVa).T * lam / pert num_Hvv[:, i] = (dSbus_dVm_mp - dSbus_dVm).T * lam / pert t_is(Haa.todense(), num_Haa, 4, ['Haa', t]) t_is(Hav.todense(), num_Hav, 4, ['Hav', t]) t_is(Hva.todense(), num_Hva, 4, ['Hva', t]) t_is(Hvv.todense(), num_Hvv, 4, ['Hvv', t]) ##----- check d2Sbr_dV2 code ----- t = ' - d2Sbr_dV2 (complex power flows)' lam = 10 * random.rand(nl) # lam = [1 zeros(nl-1, 1)] num_Gfaa = zeros((nb, nb), complex) num_Gfav = zeros((nb, nb), complex) num_Gfva = zeros((nb, nb), complex) num_Gfvv = zeros((nb, nb), complex) num_Gtaa = zeros((nb, nb), complex) num_Gtav = zeros((nb, nb), complex) num_Gtva = zeros((nb, nb), complex) num_Gtvv = zeros((nb, nb), complex) dSf_dVa, dSf_dVm, dSt_dVa, dSt_dVm, _, _ = dSbr_dV(branch, Yf, Yt, V) Gfaa, Gfav, Gfva, Gfvv = d2Sbr_dV2(Cf, Yf, V, lam) Gtaa, Gtav, Gtva, Gtvv = d2Sbr_dV2(Ct, Yt, V, lam) for i in range(nb): Vap = V.copy() Vap[i] = Vm[i] * exp(1j * (Va[i] + pert)) dSf_dVa_ap, dSf_dVm_ap, dSt_dVa_ap, dSt_dVm_ap, Sf_ap, St_ap = \ dSbr_dV(branch, Yf, Yt, Vap) num_Gfaa[:, i] = (dSf_dVa_ap - dSf_dVa).T * lam / pert num_Gfva[:, i] = (dSf_dVm_ap - dSf_dVm).T * lam / pert num_Gtaa[:, i] = (dSt_dVa_ap - dSt_dVa).T * lam / pert num_Gtva[:, i] = (dSt_dVm_ap - dSt_dVm).T * lam / pert Vmp = V.copy() Vmp[i] = (Vm[i] + pert) * exp(1j * Va[i]) dSf_dVa_mp, dSf_dVm_mp, dSt_dVa_mp, dSt_dVm_mp, Sf_mp, St_mp = \ dSbr_dV(branch, Yf, Yt, Vmp) num_Gfav[:, i] = (dSf_dVa_mp - dSf_dVa).T * lam / pert num_Gfvv[:, i] = (dSf_dVm_mp - dSf_dVm).T * lam / pert num_Gtav[:, i] = (dSt_dVa_mp - dSt_dVa).T * lam / pert num_Gtvv[:, i] = (dSt_dVm_mp - dSt_dVm).T * lam / pert t_is(Gfaa.todense(), num_Gfaa, 4, ['Gfaa', t]) t_is(Gfav.todense(), num_Gfav, 4, ['Gfav', t]) t_is(Gfva.todense(), num_Gfva, 4, ['Gfva', t]) t_is(Gfvv.todense(), num_Gfvv, 4, ['Gfvv', t]) t_is(Gtaa.todense(), num_Gtaa, 4, ['Gtaa', t]) t_is(Gtav.todense(), num_Gtav, 4, ['Gtav', t]) t_is(Gtva.todense(), num_Gtva, 4, ['Gtva', t]) t_is(Gtvv.todense(), num_Gtvv, 4, ['Gtvv', t]) ##----- check d2Ibr_dV2 code ----- t = ' - d2Ibr_dV2 (complex currents)' lam = 10 * random.rand(nl) # lam = [1, zeros(nl-1)] num_Gfaa = zeros((nb, nb), complex) num_Gfav = zeros((nb, nb), complex) num_Gfva = zeros((nb, nb), complex) num_Gfvv = zeros((nb, nb), complex) num_Gtaa = zeros((nb, nb), complex) num_Gtav = zeros((nb, nb), complex) num_Gtva = zeros((nb, nb), complex) num_Gtvv = zeros((nb, nb), complex) dIf_dVa, dIf_dVm, dIt_dVa, dIt_dVm, _, _ = dIbr_dV(branch, Yf, Yt, V) Gfaa, Gfav, Gfva, Gfvv = d2Ibr_dV2(Yf, V, lam) Gtaa, Gtav, Gtva, Gtvv = d2Ibr_dV2(Yt, V, lam) for i in range(nb): Vap = V.copy() Vap[i] = Vm[i] * exp(1j * (Va[i] + pert)) dIf_dVa_ap, dIf_dVm_ap, dIt_dVa_ap, dIt_dVm_ap, If_ap, It_ap = \ dIbr_dV(branch, Yf, Yt, Vap) num_Gfaa[:, i] = (dIf_dVa_ap - dIf_dVa).T * lam / pert num_Gfva[:, i] = (dIf_dVm_ap - dIf_dVm).T * lam / pert num_Gtaa[:, i] = (dIt_dVa_ap - dIt_dVa).T * lam / pert num_Gtva[:, i] = (dIt_dVm_ap - dIt_dVm).T * lam / pert Vmp = V.copy() Vmp[i] = (Vm[i] + pert) * exp(1j * Va[i]) dIf_dVa_mp, dIf_dVm_mp, dIt_dVa_mp, dIt_dVm_mp, If_mp, It_mp = \ dIbr_dV(branch, Yf, Yt, Vmp) num_Gfav[:, i] = (dIf_dVa_mp - dIf_dVa).T * lam / pert num_Gfvv[:, i] = (dIf_dVm_mp - dIf_dVm).T * lam / pert num_Gtav[:, i] = (dIt_dVa_mp - dIt_dVa).T * lam / pert num_Gtvv[:, i] = (dIt_dVm_mp - dIt_dVm).T * lam / pert t_is(Gfaa.todense(), num_Gfaa, 4, ['Gfaa', t]) t_is(Gfav.todense(), num_Gfav, 4, ['Gfav', t]) t_is(Gfva.todense(), num_Gfva, 4, ['Gfva', t]) t_is(Gfvv.todense(), num_Gfvv, 4, ['Gfvv', t]) t_is(Gtaa.todense(), num_Gtaa, 4, ['Gtaa', t]) t_is(Gtav.todense(), num_Gtav, 4, ['Gtav', t]) t_is(Gtva.todense(), num_Gtva, 4, ['Gtva', t]) t_is(Gtvv.todense(), num_Gtvv, 4, ['Gtvv', t]) ##----- check d2ASbr_dV2 code ----- t = ' - d2ASbr_dV2 (squared apparent power flows)' lam = 10 * random.rand(nl) # lam = [1 zeros(nl-1, 1)] num_Gfaa = zeros((nb, nb), complex) num_Gfav = zeros((nb, nb), complex) num_Gfva = zeros((nb, nb), complex) num_Gfvv = zeros((nb, nb), complex) num_Gtaa = zeros((nb, nb), complex) num_Gtav = zeros((nb, nb), complex) num_Gtva = zeros((nb, nb), complex) num_Gtvv = zeros((nb, nb), complex) dSf_dVa, dSf_dVm, dSt_dVa, dSt_dVm, Sf, St = dSbr_dV(branch, Yf, Yt, V) dAf_dVa, dAf_dVm, dAt_dVa, dAt_dVm = \ dAbr_dV(dSf_dVa, dSf_dVm, dSt_dVa, dSt_dVm, Sf, St) Gfaa, Gfav, Gfva, Gfvv = d2ASbr_dV2(dSf_dVa, dSf_dVm, Sf, Cf, Yf, V, lam) Gtaa, Gtav, Gtva, Gtvv = d2ASbr_dV2(dSt_dVa, dSt_dVm, St, Ct, Yt, V, lam) for i in range(nb): Vap = V.copy() Vap[i] = Vm[i] * exp(1j * (Va[i] + pert)) dSf_dVa_ap, dSf_dVm_ap, dSt_dVa_ap, dSt_dVm_ap, Sf_ap, St_ap = \ dSbr_dV(branch, Yf, Yt, Vap) dAf_dVa_ap, dAf_dVm_ap, dAt_dVa_ap, dAt_dVm_ap = \ dAbr_dV(dSf_dVa_ap, dSf_dVm_ap, dSt_dVa_ap, dSt_dVm_ap, Sf_ap, St_ap) num_Gfaa[:, i] = (dAf_dVa_ap - dAf_dVa).T * lam / pert num_Gfva[:, i] = (dAf_dVm_ap - dAf_dVm).T * lam / pert num_Gtaa[:, i] = (dAt_dVa_ap - dAt_dVa).T * lam / pert num_Gtva[:, i] = (dAt_dVm_ap - dAt_dVm).T * lam / pert Vmp = V.copy() Vmp[i] = (Vm[i] + pert) * exp(1j * Va[i]) dSf_dVa_mp, dSf_dVm_mp, dSt_dVa_mp, dSt_dVm_mp, Sf_mp, St_mp = \ dSbr_dV(branch, Yf, Yt, Vmp) dAf_dVa_mp, dAf_dVm_mp, dAt_dVa_mp, dAt_dVm_mp = \ dAbr_dV(dSf_dVa_mp, dSf_dVm_mp, dSt_dVa_mp, dSt_dVm_mp, Sf_mp, St_mp) num_Gfav[:, i] = (dAf_dVa_mp - dAf_dVa).T * lam / pert num_Gfvv[:, i] = (dAf_dVm_mp - dAf_dVm).T * lam / pert num_Gtav[:, i] = (dAt_dVa_mp - dAt_dVa).T * lam / pert num_Gtvv[:, i] = (dAt_dVm_mp - dAt_dVm).T * lam / pert t_is(Gfaa.todense(), num_Gfaa, 2, ['Gfaa', t]) t_is(Gfav.todense(), num_Gfav, 2, ['Gfav', t]) t_is(Gfva.todense(), num_Gfva, 2, ['Gfva', t]) t_is(Gfvv.todense(), num_Gfvv, 2, ['Gfvv', t]) t_is(Gtaa.todense(), num_Gtaa, 2, ['Gtaa', t]) t_is(Gtav.todense(), num_Gtav, 2, ['Gtav', t]) t_is(Gtva.todense(), num_Gtva, 2, ['Gtva', t]) t_is(Gtvv.todense(), num_Gtvv, 2, ['Gtvv', t]) ##----- check d2ASbr_dV2 code ----- t = ' - d2ASbr_dV2 (squared real power flows)' lam = 10 * random.rand(nl) # lam = [1 zeros(nl-1, 1)] num_Gfaa = zeros((nb, nb), complex) num_Gfav = zeros((nb, nb), complex) num_Gfva = zeros((nb, nb), complex) num_Gfvv = zeros((nb, nb), complex) num_Gtaa = zeros((nb, nb), complex) num_Gtav = zeros((nb, nb), complex) num_Gtva = zeros((nb, nb), complex) num_Gtvv = zeros((nb, nb), complex) dSf_dVa, dSf_dVm, dSt_dVa, dSt_dVm, Sf, St = dSbr_dV(branch, Yf, Yt, V) dAf_dVa, dAf_dVm, dAt_dVa, dAt_dVm = \ dAbr_dV(dSf_dVa.real, dSf_dVm.real, dSt_dVa.real, dSt_dVm.real, Sf.real, St.real) Gfaa, Gfav, Gfva, Gfvv = d2ASbr_dV2(dSf_dVa.real, dSf_dVm.real, Sf.real, Cf, Yf, V, lam) Gtaa, Gtav, Gtva, Gtvv = d2ASbr_dV2(dSt_dVa.real, dSt_dVm.real, St.real, Ct, Yt, V, lam) for i in range(nb): Vap = V.copy() Vap[i] = Vm[i] * exp(1j * (Va[i] + pert)) dSf_dVa_ap, dSf_dVm_ap, dSt_dVa_ap, dSt_dVm_ap, Sf_ap, St_ap = \ dSbr_dV(branch, Yf, Yt, Vap) dAf_dVa_ap, dAf_dVm_ap, dAt_dVa_ap, dAt_dVm_ap = \ dAbr_dV(dSf_dVa_ap.real, dSf_dVm_ap.real, dSt_dVa_ap.real, dSt_dVm_ap.real, Sf_ap.real, St_ap.real) num_Gfaa[:, i] = (dAf_dVa_ap - dAf_dVa).T * lam / pert num_Gfva[:, i] = (dAf_dVm_ap - dAf_dVm).T * lam / pert num_Gtaa[:, i] = (dAt_dVa_ap - dAt_dVa).T * lam / pert num_Gtva[:, i] = (dAt_dVm_ap - dAt_dVm).T * lam / pert Vmp = V.copy() Vmp[i] = (Vm[i] + pert) * exp(1j * Va[i]) dSf_dVa_mp, dSf_dVm_mp, dSt_dVa_mp, dSt_dVm_mp, Sf_mp, St_mp = \ dSbr_dV(branch, Yf, Yt, Vmp) dAf_dVa_mp, dAf_dVm_mp, dAt_dVa_mp, dAt_dVm_mp = \ dAbr_dV(dSf_dVa_mp.real, dSf_dVm_mp.real, dSt_dVa_mp.real, dSt_dVm_mp.real, Sf_mp.real, St_mp.real) num_Gfav[:, i] = (dAf_dVa_mp - dAf_dVa).T * lam / pert num_Gfvv[:, i] = (dAf_dVm_mp - dAf_dVm).T * lam / pert num_Gtav[:, i] = (dAt_dVa_mp - dAt_dVa).T * lam / pert num_Gtvv[:, i] = (dAt_dVm_mp - dAt_dVm).T * lam / pert t_is(Gfaa.todense(), num_Gfaa, 2, ['Gfaa', t]) t_is(Gfav.todense(), num_Gfav, 2, ['Gfav', t]) t_is(Gfva.todense(), num_Gfva, 2, ['Gfva', t]) t_is(Gfvv.todense(), num_Gfvv, 2, ['Gfvv', t]) t_is(Gtaa.todense(), num_Gtaa, 2, ['Gtaa', t]) t_is(Gtav.todense(), num_Gtav, 2, ['Gtav', t]) t_is(Gtva.todense(), num_Gtva, 2, ['Gtva', t]) t_is(Gtvv.todense(), num_Gtvv, 2, ['Gtvv', t]) ##----- check d2AIbr_dV2 code ----- t = ' - d2AIbr_dV2 (squared current magnitudes)' lam = 10 * random.rand(nl) # lam = [1 zeros(nl-1, 1)] num_Gfaa = zeros((nb, nb), complex) num_Gfav = zeros((nb, nb), complex) num_Gfva = zeros((nb, nb), complex) num_Gfvv = zeros((nb, nb), complex) num_Gtaa = zeros((nb, nb), complex) num_Gtav = zeros((nb, nb), complex) num_Gtva = zeros((nb, nb), complex) num_Gtvv = zeros((nb, nb), complex) dIf_dVa, dIf_dVm, dIt_dVa, dIt_dVm, If, It = dIbr_dV(branch, Yf, Yt, V) dAf_dVa, dAf_dVm, dAt_dVa, dAt_dVm = \ dAbr_dV(dIf_dVa, dIf_dVm, dIt_dVa, dIt_dVm, If, It) Gfaa, Gfav, Gfva, Gfvv = d2AIbr_dV2(dIf_dVa, dIf_dVm, If, Yf, V, lam) Gtaa, Gtav, Gtva, Gtvv = d2AIbr_dV2(dIt_dVa, dIt_dVm, It, Yt, V, lam) for i in range(nb): Vap = V.copy() Vap[i] = Vm[i] * exp(1j * (Va[i] + pert)) dIf_dVa_ap, dIf_dVm_ap, dIt_dVa_ap, dIt_dVm_ap, If_ap, It_ap = \ dIbr_dV(branch, Yf, Yt, Vap) dAf_dVa_ap, dAf_dVm_ap, dAt_dVa_ap, dAt_dVm_ap = \ dAbr_dV(dIf_dVa_ap, dIf_dVm_ap, dIt_dVa_ap, dIt_dVm_ap, If_ap, It_ap) num_Gfaa[:, i] = (dAf_dVa_ap - dAf_dVa).T * lam / pert num_Gfva[:, i] = (dAf_dVm_ap - dAf_dVm).T * lam / pert num_Gtaa[:, i] = (dAt_dVa_ap - dAt_dVa).T * lam / pert num_Gtva[:, i] = (dAt_dVm_ap - dAt_dVm).T * lam / pert Vmp = V.copy() Vmp[i] = (Vm[i] + pert) * exp(1j * Va[i]) dIf_dVa_mp, dIf_dVm_mp, dIt_dVa_mp, dIt_dVm_mp, If_mp, It_mp = \ dIbr_dV(branch, Yf, Yt, Vmp) dAf_dVa_mp, dAf_dVm_mp, dAt_dVa_mp, dAt_dVm_mp = \ dAbr_dV(dIf_dVa_mp, dIf_dVm_mp, dIt_dVa_mp, dIt_dVm_mp, If_mp, It_mp) num_Gfav[:, i] = (dAf_dVa_mp - dAf_dVa).T * lam / pert num_Gfvv[:, i] = (dAf_dVm_mp - dAf_dVm).T * lam / pert num_Gtav[:, i] = (dAt_dVa_mp - dAt_dVa).T * lam / pert num_Gtvv[:, i] = (dAt_dVm_mp - dAt_dVm).T * lam / pert t_is(Gfaa.todense(), num_Gfaa, 3, ['Gfaa', t]) t_is(Gfav.todense(), num_Gfav, 3, ['Gfav', t]) t_is(Gfva.todense(), num_Gfva, 3, ['Gfva', t]) t_is(Gfvv.todense(), num_Gfvv, 2, ['Gfvv', t]) t_is(Gtaa.todense(), num_Gtaa, 3, ['Gtaa', t]) t_is(Gtav.todense(), num_Gtav, 3, ['Gtav', t]) t_is(Gtva.todense(), num_Gtva, 3, ['Gtva', t]) t_is(Gtvv.todense(), num_Gtvv, 2, ['Gtvv', t]) t_end()
def opf_consfcn(x, om, Ybus, Yf, Yt, ppopt, il=None, *args): """Evaluates nonlinear constraints and their Jacobian for OPF. Constraint evaluation function for AC optimal power flow, suitable for use with L{pips}. Computes constraint vectors and their gradients. @param x: optimization vector @param om: OPF model object @param Ybus: bus admittance matrix @param Yf: admittance matrix for "from" end of constrained branches @param Yt: admittance matrix for "to" end of constrained branches @param ppopt: PYPOWER options vector @param il: (optional) vector of branch indices corresponding to branches with flow limits (all others are assumed to be unconstrained). The default is C{range(nl)} (all branches). C{Yf} and C{Yt} contain only the rows corresponding to C{il}. @return: C{h} - vector of inequality constraint values (flow limits) limit^2 - flow^2, where the flow can be apparent power real power or current, depending on value of C{OPF_FLOW_LIM} in C{ppopt} (only for constrained lines). C{g} - vector of equality constraint values (power balances). C{dh} - (optional) inequality constraint gradients, column j is gradient of h(j). C{dg} - (optional) equality constraint gradients. @see: L{opf_costfcn}, L{opf_hessfcn} @author: Carlos E. Murillo-Sanchez (PSERC Cornell & Universidad Autonoma de Manizales) @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln """ ##----- initialize ----- ## unpack data ppc = om.get_ppc() baseMVA, bus, gen, branch = \ ppc["baseMVA"], ppc["bus"], ppc["gen"], ppc["branch"] vv, _, _, _ = om.get_idx() ## problem dimensions nb = bus.shape[0] ## number of buses nl = branch.shape[0] ## number of branches ng = gen.shape[0] ## number of dispatchable injections nxyz = len(x) ## total number of control vars of all types ## set default constrained lines if il is None: il = arange(nl) ## all lines have limits by default nl2 = len(il) ## number of constrained lines ## grab Pg & Qg Pg = x[vv["i1"]["Pg"]:vv["iN"]["Pg"]] ## active generation in p.u. Qg = x[vv["i1"]["Qg"]:vv["iN"]["Qg"]] ## reactive generation in p.u. ## put Pg & Qg back in gen gen[:, PG] = Pg * baseMVA ## active generation in MW gen[:, QG] = Qg * baseMVA ## reactive generation in MVAr ## rebuild Sbus Sbus = makeSbus(baseMVA, bus, gen) ## net injected power in p.u. ## ----- evaluate constraints ----- ## reconstruct V Va = x[vv["i1"]["Va"]:vv["iN"]["Va"]] Vm = x[vv["i1"]["Vm"]:vv["iN"]["Vm"]] V = Vm * exp(1j * Va) ## evaluate power flow equations mis = V * conj(Ybus * V) - Sbus ##----- evaluate constraint function values ----- ## first, the equality constraints (power flow) g = r_[ mis.real, ## active power mismatch for all buses mis.imag ] ## reactive power mismatch for all buses ## then, the inequality constraints (branch flow limits) if nl2 > 0: flow_max = (branch[il, RATE_A] / baseMVA)**2 flow_max[flow_max == 0] = Inf if ppopt['OPF_FLOW_LIM'] == 2: ## current magnitude limit, |I| If = Yf * V It = Yt * V h = r_[ If * conj(If) - flow_max, ## branch I limits (from bus) It * conj(It) - flow_max ].real ## branch I limits (to bus) else: ## compute branch power flows ## complex power injected at "from" bus (p.u.) Sf = V[ branch[il, F_BUS].astype(int) ] * conj(Yf * V) ## complex power injected at "to" bus (p.u.) St = V[ branch[il, T_BUS].astype(int) ] * conj(Yt * V) if ppopt['OPF_FLOW_LIM'] == 1: ## active power limit, P (Pan Wei) h = r_[ Sf.real**2 - flow_max, ## branch P limits (from bus) St.real**2 - flow_max ] ## branch P limits (to bus) else: ## apparent power limit, |S| h = r_[ Sf * conj(Sf) - flow_max, ## branch S limits (from bus) St * conj(St) - flow_max ].real ## branch S limits (to bus) else: h = zeros((0,1)) ##----- evaluate partials of constraints ----- ## index ranges iVa = arange(vv["i1"]["Va"], vv["iN"]["Va"]) iVm = arange(vv["i1"]["Vm"], vv["iN"]["Vm"]) iPg = arange(vv["i1"]["Pg"], vv["iN"]["Pg"]) iQg = arange(vv["i1"]["Qg"], vv["iN"]["Qg"]) iVaVmPgQg = r_[iVa, iVm, iPg, iQg].T ## compute partials of injected bus powers dSbus_dVm, dSbus_dVa = dSbus_dV(Ybus, V) ## w.r.t. V ## Pbus w.r.t. Pg, Qbus w.r.t. Qg neg_Cg = sparse((-ones(ng), (gen[:, GEN_BUS], range(ng))), (nb, ng)) ## construct Jacobian of equality constraints (power flow) and transpose it dg = lil_matrix((2 * nb, nxyz)) blank = sparse((nb, ng)) dg[:, iVaVmPgQg] = vstack([ ## P mismatch w.r.t Va, Vm, Pg, Qg hstack([dSbus_dVa.real, dSbus_dVm.real, neg_Cg, blank]), ## Q mismatch w.r.t Va, Vm, Pg, Qg hstack([dSbus_dVa.imag, dSbus_dVm.imag, blank, neg_Cg]) ], "csr") dg = dg.T if nl2 > 0: ## compute partials of Flows w.r.t. V if ppopt['OPF_FLOW_LIM'] == 2: ## current dFf_dVa, dFf_dVm, dFt_dVa, dFt_dVm, Ff, Ft = \ dIbr_dV(branch[il, :], Yf, Yt, V) else: ## power dFf_dVa, dFf_dVm, dFt_dVa, dFt_dVm, Ff, Ft = \ dSbr_dV(branch[il, :], Yf, Yt, V) if ppopt['OPF_FLOW_LIM'] == 1: ## real part of flow (active power) dFf_dVa = dFf_dVa.real dFf_dVm = dFf_dVm.real dFt_dVa = dFt_dVa.real dFt_dVm = dFt_dVm.real Ff = Ff.real Ft = Ft.real ## squared magnitude of flow (of complex power or current, or real power) df_dVa, df_dVm, dt_dVa, dt_dVm = \ dAbr_dV(dFf_dVa, dFf_dVm, dFt_dVa, dFt_dVm, Ff, Ft) ## construct Jacobian of inequality constraints (branch limits) ## and transpose it. dh = lil_matrix((2 * nl2, nxyz)) dh[:, r_[iVa, iVm].T] = vstack([ hstack([df_dVa, df_dVm]), ## "from" flow limit hstack([dt_dVa, dt_dVm]) ## "to" flow limit ], "csr") dh = dh.T else: dh = None return h, g, dh, dg
def t_jacobian(quiet=False): """Numerical tests of partial derivative code. @author: Ray Zimmerman (PSERC Cornell) """ t_begin(28, quiet) ## run powerflow to get solved case ppopt = ppoption(VERBOSE=0, OUT_ALL=0) ppc = loadcase(case30()) results, _ = runpf(ppc, ppopt) baseMVA, bus, gen, branch = \ results['baseMVA'], results['bus'], results['gen'], results['branch'] ## switch to internal bus numbering and build admittance matrices _, bus, gen, branch = ext2int1(bus, gen, branch) Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch) Ybus_full = Ybus.todense() Yf_full = Yf.todense() Yt_full = Yt.todense() Vm = bus[:, VM] Va = bus[:, VA] * (pi / 180) V = Vm * exp(1j * Va) f = branch[:, F_BUS].astype(int) ## list of "from" buses t = branch[:, T_BUS].astype(int) ## list of "to" buses #nl = len(f) nb = len(V) pert = 1e-8 Vm = array([Vm]).T # column array Va = array([Va]).T # column array Vc = array([V]).T # column array ##----- check dSbus_dV code ----- ## full matrices dSbus_dVm_full, dSbus_dVa_full = dSbus_dV(Ybus_full, V) ## sparse matrices dSbus_dVm, dSbus_dVa = dSbus_dV(Ybus, V) dSbus_dVm_sp = dSbus_dVm.todense() dSbus_dVa_sp = dSbus_dVa.todense() ## compute numerically to compare Vmp = (Vm * ones((1, nb)) + pert*eye(nb)) * (exp(1j * Va) * ones((1, nb))) Vap = (Vm * ones((1, nb))) * (exp(1j * (Va*ones((1, nb)) + pert*eye(nb)))) num_dSbus_dVm = (Vmp * conj(Ybus * Vmp) - Vc * ones((1, nb)) * conj(Ybus * Vc * ones((1, nb)))) / pert num_dSbus_dVa = (Vap * conj(Ybus * Vap) - Vc * ones((1, nb)) * conj(Ybus * Vc * ones((1, nb)))) / pert t_is(dSbus_dVm_sp, num_dSbus_dVm, 5, 'dSbus_dVm (sparse)') t_is(dSbus_dVa_sp, num_dSbus_dVa, 5, 'dSbus_dVa (sparse)') t_is(dSbus_dVm_full, num_dSbus_dVm, 5, 'dSbus_dVm (full)') t_is(dSbus_dVa_full, num_dSbus_dVa, 5, 'dSbus_dVa (full)') ##----- check dSbr_dV code ----- ## full matrices dSf_dVa_full, dSf_dVm_full, dSt_dVa_full, dSt_dVm_full, _, _ = \ dSbr_dV(branch, Yf_full, Yt_full, V) ## sparse matrices dSf_dVa, dSf_dVm, dSt_dVa, dSt_dVm, Sf, St = dSbr_dV(branch, Yf, Yt, V) dSf_dVa_sp = dSf_dVa.todense() dSf_dVm_sp = dSf_dVm.todense() dSt_dVa_sp = dSt_dVa.todense() dSt_dVm_sp = dSt_dVm.todense() ## compute numerically to compare Vmpf = Vmp[f, :] Vapf = Vap[f, :] Vmpt = Vmp[t, :] Vapt = Vap[t, :] Sf2 = (Vc[f] * ones((1, nb))) * conj(Yf * Vc * ones((1, nb))) St2 = (Vc[t] * ones((1, nb))) * conj(Yt * Vc * ones((1, nb))) Smpf = Vmpf * conj(Yf * Vmp) Sapf = Vapf * conj(Yf * Vap) Smpt = Vmpt * conj(Yt * Vmp) Sapt = Vapt * conj(Yt * Vap) num_dSf_dVm = (Smpf - Sf2) / pert num_dSf_dVa = (Sapf - Sf2) / pert num_dSt_dVm = (Smpt - St2) / pert num_dSt_dVa = (Sapt - St2) / pert t_is(dSf_dVm_sp, num_dSf_dVm, 5, 'dSf_dVm (sparse)') t_is(dSf_dVa_sp, num_dSf_dVa, 5, 'dSf_dVa (sparse)') t_is(dSt_dVm_sp, num_dSt_dVm, 5, 'dSt_dVm (sparse)') t_is(dSt_dVa_sp, num_dSt_dVa, 5, 'dSt_dVa (sparse)') t_is(dSf_dVm_full, num_dSf_dVm, 5, 'dSf_dVm (full)') t_is(dSf_dVa_full, num_dSf_dVa, 5, 'dSf_dVa (full)') t_is(dSt_dVm_full, num_dSt_dVm, 5, 'dSt_dVm (full)') t_is(dSt_dVa_full, num_dSt_dVa, 5, 'dSt_dVa (full)') ##----- check dAbr_dV code ----- ## full matrices dAf_dVa_full, dAf_dVm_full, dAt_dVa_full, dAt_dVm_full = \ dAbr_dV(dSf_dVa_full, dSf_dVm_full, dSt_dVa_full, dSt_dVm_full, Sf, St) ## sparse matrices dAf_dVa, dAf_dVm, dAt_dVa, dAt_dVm = \ dAbr_dV(dSf_dVa, dSf_dVm, dSt_dVa, dSt_dVm, Sf, St) dAf_dVa_sp = dAf_dVa.todense() dAf_dVm_sp = dAf_dVm.todense() dAt_dVa_sp = dAt_dVa.todense() dAt_dVm_sp = dAt_dVm.todense() ## compute numerically to compare num_dAf_dVm = (abs(Smpf)**2 - abs(Sf2)**2) / pert num_dAf_dVa = (abs(Sapf)**2 - abs(Sf2)**2) / pert num_dAt_dVm = (abs(Smpt)**2 - abs(St2)**2) / pert num_dAt_dVa = (abs(Sapt)**2 - abs(St2)**2) / pert t_is(dAf_dVm_sp, num_dAf_dVm, 4, 'dAf_dVm (sparse)') t_is(dAf_dVa_sp, num_dAf_dVa, 4, 'dAf_dVa (sparse)') t_is(dAt_dVm_sp, num_dAt_dVm, 4, 'dAt_dVm (sparse)') t_is(dAt_dVa_sp, num_dAt_dVa, 4, 'dAt_dVa (sparse)') t_is(dAf_dVm_full, num_dAf_dVm, 4, 'dAf_dVm (full)') t_is(dAf_dVa_full, num_dAf_dVa, 4, 'dAf_dVa (full)') t_is(dAt_dVm_full, num_dAt_dVm, 4, 'dAt_dVm (full)') t_is(dAt_dVa_full, num_dAt_dVa, 4, 'dAt_dVa (full)') ##----- check dIbr_dV code ----- ## full matrices dIf_dVa_full, dIf_dVm_full, dIt_dVa_full, dIt_dVm_full, _, _ = \ dIbr_dV(branch, Yf_full, Yt_full, V) ## sparse matrices dIf_dVa, dIf_dVm, dIt_dVa, dIt_dVm, _, _ = dIbr_dV(branch, Yf, Yt, V) dIf_dVa_sp = dIf_dVa.todense() dIf_dVm_sp = dIf_dVm.todense() dIt_dVa_sp = dIt_dVa.todense() dIt_dVm_sp = dIt_dVm.todense() ## compute numerically to compare num_dIf_dVm = (Yf * Vmp - Yf * Vc * ones((1, nb))) / pert num_dIf_dVa = (Yf * Vap - Yf * Vc * ones((1, nb))) / pert num_dIt_dVm = (Yt * Vmp - Yt * Vc * ones((1, nb))) / pert num_dIt_dVa = (Yt * Vap - Yt * Vc * ones((1, nb))) / pert t_is(dIf_dVm_sp, num_dIf_dVm, 5, 'dIf_dVm (sparse)') t_is(dIf_dVa_sp, num_dIf_dVa, 5, 'dIf_dVa (sparse)') t_is(dIt_dVm_sp, num_dIt_dVm, 5, 'dIt_dVm (sparse)') t_is(dIt_dVa_sp, num_dIt_dVa, 5, 'dIt_dVa (sparse)') t_is(dIf_dVm_full, num_dIf_dVm, 5, 'dIf_dVm (full)') t_is(dIf_dVa_full, num_dIf_dVa, 5, 'dIf_dVa (full)') t_is(dIt_dVm_full, num_dIt_dVm, 5, 'dIt_dVm (full)') t_is(dIt_dVa_full, num_dIt_dVa, 5, 'dIt_dVa (full)') t_end()
def opf_consfcn(x, om, Ybus, Yf, Yt, ppopt, il=None, *args): """Evaluates nonlinear constraints and their Jacobian for OPF. Constraint evaluation function for AC optimal power flow, suitable for use with L{pips}. Computes constraint vectors and their gradients. @param x: optimization vector @param om: OPF model object @param Ybus: bus admittance matrix @param Yf: admittance matrix for "from" end of constrained branches @param Yt: admittance matrix for "to" end of constrained branches @param ppopt: PYPOWER options vector @param il: (optional) vector of branch indices corresponding to branches with flow limits (all others are assumed to be unconstrained). The default is C{range(nl)} (all branches). C{Yf} and C{Yt} contain only the rows corresponding to C{il}. @return: C{h} - vector of inequality constraint values (flow limits) limit^2 - flow^2, where the flow can be apparent power real power or current, depending on value of C{OPF_FLOW_LIM} in C{ppopt} (only for constrained lines). C{g} - vector of equality constraint values (power balances). C{dh} - (optional) inequality constraint gradients, column j is gradient of h(j). C{dg} - (optional) equality constraint gradients. @see: L{opf_costfcn}, L{opf_hessfcn} @author: Carlos E. Murillo-Sanchez (PSERC Cornell & Universidad Autonoma de Manizales) @author: Ray Zimmerman (PSERC Cornell) """ ##----- initialize ----- ## unpack data ppc = om.get_ppc() baseMVA, bus, gen, branch = \ ppc["baseMVA"], ppc["bus"], ppc["gen"], ppc["branch"] vv, _, _, _ = om.get_idx() ## problem dimensions nb = bus.shape[0] ## number of buses nl = branch.shape[0] ## number of branches ng = gen.shape[0] ## number of dispatchable injections nxyz = len(x) ## total number of control vars of all types ## set default constrained lines if il is None: il = arange(nl) ## all lines have limits by default nl2 = len(il) ## number of constrained lines ## grab Pg & Qg Pg = x[vv["i1"]["Pg"]:vv["iN"]["Pg"]] ## active generation in p.u. Qg = x[vv["i1"]["Qg"]:vv["iN"]["Qg"]] ## reactive generation in p.u. ## put Pg & Qg back in gen gen[:, PG] = Pg * baseMVA ## active generation in MW gen[:, QG] = Qg * baseMVA ## reactive generation in MVAr ## rebuild Sbus Sbus = makeSbus(baseMVA, bus, gen) ## net injected power in p.u. ## ----- evaluate constraints ----- ## reconstruct V Va = x[vv["i1"]["Va"]:vv["iN"]["Va"]] Vm = x[vv["i1"]["Vm"]:vv["iN"]["Vm"]] V = Vm * exp(1j * Va) ## evaluate power flow equations mis = V * conj(Ybus * V) - Sbus ##----- evaluate constraint function values ----- ## first, the equality constraints (power flow) g = r_[mis.real, ## active power mismatch for all buses mis.imag] ## reactive power mismatch for all buses ## then, the inequality constraints (branch flow limits) if nl2 > 0: flow_max = (branch[il, RATE_A] / baseMVA)**2 flow_max[flow_max == 0] = Inf if ppopt['OPF_FLOW_LIM'] == 2: ## current magnitude limit, |I| If = Yf * V It = Yt * V h = r_[If * conj(If) - flow_max, ## branch I limits (from bus) It * conj(It) - flow_max].real ## branch I limits (to bus) else: ## compute branch power flows ## complex power injected at "from" bus (p.u.) Sf = V[branch[il, F_BUS].astype(int)] * conj(Yf * V) ## complex power injected at "to" bus (p.u.) St = V[branch[il, T_BUS].astype(int)] * conj(Yt * V) if ppopt['OPF_FLOW_LIM'] == 1: ## active power limit, P (Pan Wei) h = r_[Sf.real**2 - flow_max, ## branch P limits (from bus) St.real**2 - flow_max] ## branch P limits (to bus) else: ## apparent power limit, |S| h = r_[Sf * conj(Sf) - flow_max, ## branch S limits (from bus) St * conj(St) - flow_max].real ## branch S limits (to bus) else: h = zeros((0, 1)) ##----- evaluate partials of constraints ----- ## index ranges iVa = arange(vv["i1"]["Va"], vv["iN"]["Va"]) iVm = arange(vv["i1"]["Vm"], vv["iN"]["Vm"]) iPg = arange(vv["i1"]["Pg"], vv["iN"]["Pg"]) iQg = arange(vv["i1"]["Qg"], vv["iN"]["Qg"]) iVaVmPgQg = r_[iVa, iVm, iPg, iQg].T ## compute partials of injected bus powers dSbus_dVm, dSbus_dVa = dSbus_dV(Ybus, V) ## w.r.t. V ## Pbus w.r.t. Pg, Qbus w.r.t. Qg neg_Cg = sparse((-ones(ng), (gen[:, GEN_BUS], list(range(ng)))), (nb, ng)) ## construct Jacobian of equality constraints (power flow) and transpose it dg = lil_matrix((2 * nb, nxyz)) blank = sparse((nb, ng)) dg[:, iVaVmPgQg] = vstack( [ ## P mismatch w.r.t Va, Vm, Pg, Qg hstack([dSbus_dVa.real, dSbus_dVm.real, neg_Cg, blank]), ## Q mismatch w.r.t Va, Vm, Pg, Qg hstack([dSbus_dVa.imag, dSbus_dVm.imag, blank, neg_Cg]) ], "csr") dg = dg.T if nl2 > 0: ## compute partials of Flows w.r.t. V if ppopt['OPF_FLOW_LIM'] == 2: ## current dFf_dVa, dFf_dVm, dFt_dVa, dFt_dVm, Ff, Ft = \ dIbr_dV(branch[il, :], Yf, Yt, V) else: ## power dFf_dVa, dFf_dVm, dFt_dVa, dFt_dVm, Ff, Ft = \ dSbr_dV(branch[il, :], Yf, Yt, V) if ppopt['OPF_FLOW_LIM'] == 1: ## real part of flow (active power) dFf_dVa = dFf_dVa.real dFf_dVm = dFf_dVm.real dFt_dVa = dFt_dVa.real dFt_dVm = dFt_dVm.real Ff = Ff.real Ft = Ft.real ## squared magnitude of flow (of complex power or current, or real power) df_dVa, df_dVm, dt_dVa, dt_dVm = \ dAbr_dV(dFf_dVa, dFf_dVm, dFt_dVa, dFt_dVm, Ff, Ft) ## construct Jacobian of inequality constraints (branch limits) ## and transpose it. dh = lil_matrix((2 * nl2, nxyz)) dh[:, r_[iVa, iVm].T] = vstack( [ hstack([df_dVa, df_dVm]), ## "from" flow limit hstack([dt_dVa, dt_dVm]) ## "to" flow limit ], "csr") dh = dh.T else: dh = None return h, g, dh, dg
def admmopf_consfcn(self, x=None): if x is None: x = self.pb['x0'] nx = len(x) nb = self.region['nb'] ng = self.region['ng'] iv = self.idx['var'] bus = self.region['bus'] gen = self.region['gen'] branch = self.region['branch'] Ybus = self.region['Ybus'] Yf = self.region['Yf'] Yt = self.region['Yt'] baseMVA = self.region['baseMVA'] ridx = self.idx['rbus']['int'] # idx ranges iVa = iv['iVa'] iVm = iv['iVm'] iPg = iv['iPg'] iQg = iv['iQg'] # grab Pg and Qg gen[:, PG] = x[iPg] gen[:, QG] = x[iQg] # rebuid Sbus Sbus = makeSbus(1, bus, gen) # reconstruct V Va, Vm = x[iVa], x[iVm] V = Vm * exp(1j * Va) # evaluate power flow equations mis = V * conj(Ybus * V) - Sbus g = r_[mis.real, mis.imag] row = ridx + [i + nb for i in ridx] g = g[row] il = find((branch[:, RATE_A] != 0) & (branch[:, RATE_A] < 1e10)) nl2 = len(il) Ybus2, Yf2, Yt2 = makeYbus(baseMVA, bus, branch) Yf = Yf2[il, :] Yt = Yt2[il, :] if nl2 > 0: flow_max = (branch[il, RATE_A] / baseMVA)**2 flow_max[flow_max == 0] = Inf ## compute branch power flows ## complex power injected at "from" bus (p.u.) Sf = V[branch[il, F_BUS].astype(int)] * conj(Yf * V) ## complex power injected at "to" bus (p.u.) St = V[branch[il, T_BUS].astype(int)] * conj(Yt * V) h = r_[Sf.real**2 - flow_max, ## branch P limits (from bus) St.real**2 - flow_max] ## branch P limits (to bus) else: h = array([]) # ---- evaluate constraint gradients ------- # compute partials of injected bus powers dSbus_dVm, dSbus_dVa = dSbus_dV(Ybus, V) # w.r.t. V neg_Cg = sparse((-ones(ng), (gen[:, GEN_BUS], range(ng))), (nb, ng)) # construct Jacobian of equality constraints (power flow) and transpose it dg = lil_matrix((2 * nb, nx)) blank = sparse((nb, ng)) dg = vstack([ \ #P mismatch w.r.t Va, Vm, Pg, Qg hstack([dSbus_dVa.real, dSbus_dVm.real, neg_Cg, blank]), # Q mismatch w.r.t Va, Vm, Pg, Qg hstack([dSbus_dVa.imag, dSbus_dVm.imag, blank, neg_Cg]) ], "csr") dg = dg[row, :] dg = dg.T if nl2 > 0: ## compute partials of Flows w.r.t. V ## power dFf_dVa, dFf_dVm, dFt_dVa, dFt_dVm, Ff, Ft = \ dSbr_dV(branch[il, :], Yf, Yt, V) dFf_dVa = dFf_dVa.real dFf_dVm = dFf_dVm.real dFt_dVa = dFt_dVa.real dFt_dVm = dFt_dVm.real Ff = Ff.real Ft = Ft.real ## squared magnitude of flow (of complex power or current, or real power) df_dVa, df_dVm, dt_dVa, dt_dVm = \ dAbr_dV(dFf_dVa, dFf_dVm, dFt_dVa, dFt_dVm, Ff, Ft) ## construct Jacobian of inequality constraints (branch limits) ## and transpose it. dh = lil_matrix((2 * nl2, nx)) dh[:, r_[iVa, iVm].T] = vstack( [ hstack([df_dVa, df_dVm]), ## "from" flow limit hstack([dt_dVa, dt_dVm]) ## "to" flow limit ], "csr") dh = dh.T else: dh = None h = array([]) dh = None return h, g, dh, dg