def admmopf_hessfcn(self, x=None, lmbda=None): if x is None: x = self.pb['x0'] if lmbda is None: # multiplier for power flow constraints lmbda = {'eqnonlin': 3000 * ones(2 * len(self.idx['rbus']['int']))} # lmbda = {'eqnonlin': 3000 * ones(2 * self.region['nb'])} nx = len(x) nb = self.region['nb'] ng = self.region['ng'] rho = self.var['rho'] Ybus = self.region['Ybus'] iv = self.idx['var'] iVa = iv['iVa'] iVm = iv['iVm'] # reconstruct V Va, Vm = x[iVa], x[iVm] V = Vm * exp(1j * Va) # ---- evaluate Hessian of f ---------- Adiff = self.mtx['Adiff'] Asum = self.mtx['Asum'] d2f_dVm2 = d2f_dVa2 = Adiff.T.dot(Adiff) * rho + Asum.T.dot(Asum) * rho data = self.region['gencost'][:, COST] * 2 i = arange(0, ng) d2f_dPgQg2 = sparse((data, (i, i)), (2 * ng, 2 * ng)) blankv = sparse((nb, nb)) d2f_dV2 = vstack( [hstack([d2f_dVa2, blankv]), hstack([blankv, d2f_dVm2])], 'csr') blankvp = sparse((2 * nb, 2 * ng)) d2f = vstack( [hstack([d2f_dV2, blankvp]), hstack([blankvp.T, d2f_dPgQg2])], 'csr') # ---- evaluate Hessian of power flow constraints ------ nlam = len(lmbda["eqnonlin"]) // 2 lamP = zeros(nb) #for non-included pf balances use 0 as multiplier lamQ = zeros(nb) lamP[self.idx['rbus']['int']] = lmbda["eqnonlin"][:nlam] lamQ[self.idx['rbus']['int']] = lmbda["eqnonlin"][nlam:nlam + nlam] # lamP = lmbda["eqnonlin"][:nlam] # lamQ = lmbda["eqnonlin"][nlam:nlam + nlam] Gpaa, Gpav, Gpva, Gpvv = d2Sbus_dV2(Ybus, V, lamP) Gqaa, Gqav, Gqva, Gqvv = d2Sbus_dV2(Ybus, V, lamQ) d2G = vstack([ hstack([ vstack([hstack([Gpaa, Gpav]), hstack([Gpva, Gpvv])]).real + vstack([hstack([Gqaa, Gqav]), hstack([Gqva, Gqvv])]).imag, sparse((2 * nb, 2 * ng)) ]), hstack([sparse((2 * ng, 2 * nb)), sparse((2 * ng, 2 * ng))]) ], 'csr') return d2f + d2G
def opf_hessfcn(x, lmbda, om, Ybus, Yf, Yt, ppopt, il=None, cost_mult=1.0): """Evaluates Hessian of Lagrangian for AC OPF. Hessian evaluation function for AC optimal power flow, suitable for use with L{pips}. Examples:: Lxx = opf_hessfcn(x, lmbda, om, Ybus, Yf, Yt, ppopt) Lxx = opf_hessfcn(x, lmbda, om, Ybus, Yf, Yt, ppopt, il) Lxx = opf_hessfcn(x, lmbda, om, Ybus, Yf, Yt, ppopt, il, cost_mult) @param x: optimization vector @param lmbda: C{eqnonlin} - Lagrange multipliers on power balance equations. C{ineqnonlin} - Kuhn-Tucker multipliers on constrained branch flows. @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}. @param cost_mult: (optional) Scale factor to be applied to the cost (default = 1). @return: Hessian of the Lagrangian. @see: L{opf_costfcn}, L{opf_consfcn} @author: Ray Zimmerman (PSERC Cornell) @author: Carlos E. Murillo-Sanchez (PSERC Cornell & Universidad Autonoma de Manizales) """ ##----- initialize ----- ## unpack data ppc = om.get_ppc() baseMVA, bus, gen, branch, gencost = \ ppc["baseMVA"], ppc["bus"], ppc["gen"], ppc["branch"], ppc["gencost"] cp = om.get_cost_params() N, Cw, H, dd, rh, kk, mm = \ cp["N"], cp["Cw"], cp["H"], cp["dd"], cp["rh"], cp["kk"], cp["mm"] vv, _, _, _ = om.get_idx() ## unpack needed parameters 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 ## reconstruct V Va = x[vv["i1"]["Va"]:vv["iN"]["Va"]] Vm = x[vv["i1"]["Vm"]:vv["iN"]["Vm"]] V = Vm * exp(1j * Va) nxtra = nxyz - 2 * nb pcost = gencost[arange(ng), :] if gencost.shape[0] > ng: qcost = gencost[arange(ng, 2 * ng), :] else: qcost = array([]) ## ----- evaluate d2f ----- d2f_dPg2 = zeros(ng) #sparse((ng, 1)) ## w.r.t. p.u. Pg d2f_dQg2 = zeros(ng) #sparse((ng, 1)) ## w.r.t. p.u. Qg ipolp = find(pcost[:, MODEL] == POLYNOMIAL) d2f_dPg2[ipolp] = \ baseMVA**2 * polycost(pcost[ipolp, :], Pg[ipolp] * baseMVA, 2) if any(qcost): ## Qg is not free ipolq = find(qcost[:, MODEL] == POLYNOMIAL) d2f_dQg2[ipolq] = \ baseMVA**2 * polycost(qcost[ipolq, :], Qg[ipolq] * baseMVA, 2) i = r_[arange(vv["i1"]["Pg"], vv["iN"]["Pg"]), arange(vv["i1"]["Qg"], vv["iN"]["Qg"])] # d2f = sparse((vstack([d2f_dPg2, d2f_dQg2]).toarray().flatten(), # (i, i)), shape=(nxyz, nxyz)) d2f = sparse((r_[d2f_dPg2, d2f_dQg2], (i, i)), (nxyz, nxyz)) ## generalized cost if issparse(N) and N.nnz > 0: nw = N.shape[0] r = N * x - rh ## Nx - rhat iLT = find(r < -kk) ## below dead zone iEQ = find((r == 0) & (kk == 0)) ## dead zone doesn't exist iGT = find(r > kk) ## above dead zone iND = r_[iLT, iEQ, iGT] ## rows that are Not in the Dead region iL = find(dd == 1) ## rows using linear function iQ = find(dd == 2) ## rows using quadratic function LL = sparse((ones(len(iL)), (iL, iL)), (nw, nw)) QQ = sparse((ones(len(iQ)), (iQ, iQ)), (nw, nw)) kbar = sparse((r_[ones(len(iLT)), zeros(len(iEQ)), -ones(len(iGT))], (iND, iND)), (nw, nw)) * kk rr = r + kbar ## apply non-dead zone shift M = sparse((mm[iND], (iND, iND)), (nw, nw)) ## dead zone or scale diagrr = sparse((rr, (arange(nw), arange(nw))), (nw, nw)) ## linear rows multiplied by rr(i), quadratic rows by rr(i)^2 w = M * (LL + QQ * diagrr) * rr HwC = H * w + Cw AA = N.T * M * (LL + 2 * QQ * diagrr) d2f = d2f + AA * H * AA.T + 2 * N.T * M * QQ * \ sparse((HwC, (arange(nw), arange(nw))), (nw, nw)) * N d2f = d2f * cost_mult ##----- evaluate Hessian of power balance constraints ----- nlam = len(lmbda["eqnonlin"]) / 2 lamP = lmbda["eqnonlin"][:nlam] lamQ = lmbda["eqnonlin"][nlam:nlam + nlam] Gpaa, Gpav, Gpva, Gpvv = d2Sbus_dV2(Ybus, V, lamP) Gqaa, Gqav, Gqva, Gqvv = d2Sbus_dV2(Ybus, V, lamQ) d2G = vstack([ hstack([ vstack([hstack([Gpaa, Gpav]), hstack([Gpva, Gpvv])]).real + vstack([hstack([Gqaa, Gqav]), hstack([Gqva, Gqvv])]).imag, sparse((2 * nb, nxtra)) ]), hstack([sparse( (nxtra, 2 * nb)), sparse((nxtra, nxtra))]) ], "csr") ##----- evaluate Hessian of flow constraints ----- nmu = len(lmbda["ineqnonlin"]) / 2 muF = lmbda["ineqnonlin"][:nmu] muT = lmbda["ineqnonlin"][nmu:nmu + nmu] if ppopt['OPF_FLOW_LIM'] == 2: ## current dIf_dVa, dIf_dVm, dIt_dVa, dIt_dVm, If, It = dIbr_dV(branch, Yf, Yt, V) Hfaa, Hfav, Hfva, Hfvv = d2AIbr_dV2(dIf_dVa, dIf_dVm, If, Yf, V, muF) Htaa, Htav, Htva, Htvv = d2AIbr_dV2(dIt_dVa, dIt_dVm, It, Yt, V, muT) else: f = branch[il, F_BUS].astype(int) ## list of "from" buses t = branch[il, T_BUS].astype(int) ## list of "to" buses ## connection matrix for line & from buses Cf = sparse((ones(nl2), (arange(nl2), f)), (nl2, nb)) ## connection matrix for line & to buses Ct = sparse((ones(nl2), (arange(nl2), t)), (nl2, nb)) dSf_dVa, dSf_dVm, dSt_dVa, dSt_dVm, Sf, St = \ dSbr_dV(branch[il,:], Yf, Yt, V) if ppopt['OPF_FLOW_LIM'] == 1: ## real power Hfaa, Hfav, Hfva, Hfvv = d2ASbr_dV2(dSf_dVa.real, dSf_dVm.real, Sf.real, Cf, Yf, V, muF) Htaa, Htav, Htva, Htvv = d2ASbr_dV2(dSt_dVa.real, dSt_dVm.real, St.real, Ct, Yt, V, muT) else: ## apparent power Hfaa, Hfav, Hfva, Hfvv = \ d2ASbr_dV2(dSf_dVa, dSf_dVm, Sf, Cf, Yf, V, muF) Htaa, Htav, Htva, Htvv = \ d2ASbr_dV2(dSt_dVa, dSt_dVm, St, Ct, Yt, V, muT) d2H = vstack([ hstack([ vstack([hstack([Hfaa, Hfav]), hstack([Hfva, Hfvv])]) + vstack([hstack([Htaa, Htav]), hstack([Htva, Htvv])]), sparse((2 * nb, nxtra)) ]), hstack([sparse( (nxtra, 2 * nb)), sparse((nxtra, nxtra))]) ], "csr") ##----- do numerical check using (central) finite differences ----- if 0: nx = len(x) step = 1e-5 num_d2f = sparse((nx, nx)) num_d2G = sparse((nx, nx)) num_d2H = sparse((nx, nx)) for i in range(nx): xp = x xm = x xp[i] = x[i] + step / 2 xm[i] = x[i] - step / 2 # evaluate cost & gradients _, dfp = opf_costfcn(xp, om) _, dfm = opf_costfcn(xm, om) # evaluate constraints & gradients _, _, dHp, dGp = opf_consfcn(xp, om, Ybus, Yf, Yt, ppopt, il) _, _, dHm, dGm = opf_consfcn(xm, om, Ybus, Yf, Yt, ppopt, il) num_d2f[:, i] = cost_mult * (dfp - dfm) / step num_d2G[:, i] = (dGp - dGm) * lmbda["eqnonlin"] / step num_d2H[:, i] = (dHp - dHm) * lmbda["ineqnonlin"] / step d2f_err = max(max(abs(d2f - num_d2f))) d2G_err = max(max(abs(d2G - num_d2G))) d2H_err = max(max(abs(d2H - num_d2H))) if d2f_err > 1e-6: print('Max difference in d2f: %g' % d2f_err) if d2G_err > 1e-5: print('Max difference in d2G: %g' % d2G_err) if d2H_err > 1e-6: print('Max difference in d2H: %g' % d2H_err) return d2f + d2G + d2H
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_hessfcn(x, lmbda, om, Ybus, Yf, Yt, ppopt, il=None, cost_mult=1.0): """Evaluates Hessian of Lagrangian for AC OPF. Hessian evaluation function for AC optimal power flow, suitable for use with L{pips}. Examples:: Lxx = opf_hessfcn(x, lmbda, om, Ybus, Yf, Yt, ppopt) Lxx = opf_hessfcn(x, lmbda, om, Ybus, Yf, Yt, ppopt, il) Lxx = opf_hessfcn(x, lmbda, om, Ybus, Yf, Yt, ppopt, il, cost_mult) @param x: optimization vector @param lmbda: C{eqnonlin} - Lagrange multipliers on power balance equations. C{ineqnonlin} - Kuhn-Tucker multipliers on constrained branch flows. @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}. @param cost_mult: (optional) Scale factor to be applied to the cost (default = 1). @return: Hessian of the Lagrangian. @see: L{opf_costfcn}, L{opf_consfcn} @author: Ray Zimmerman (PSERC Cornell) @author: Carlos E. Murillo-Sanchez (PSERC Cornell & Universidad Autonoma de Manizales) """ ##----- initialize ----- ## unpack data ppc = om.get_ppc() baseMVA, bus, gen, branch, gencost = \ ppc["baseMVA"], ppc["bus"], ppc["gen"], ppc["branch"], ppc["gencost"] cp = om.get_cost_params() N, Cw, H, dd, rh, kk, mm = \ cp["N"], cp["Cw"], cp["H"], cp["dd"], cp["rh"], cp["kk"], cp["mm"] vv, _, _, _ = om.get_idx() ## unpack needed parameters 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 ## reconstruct V Va = x[vv["i1"]["Va"]:vv["iN"]["Va"]] Vm = x[vv["i1"]["Vm"]:vv["iN"]["Vm"]] V = Vm * exp(1j * Va) nxtra = nxyz - 2 * nb pcost = gencost[arange(ng), :] if gencost.shape[0] > ng: qcost = gencost[arange(ng, 2 * ng), :] else: qcost = array([]) ## ----- evaluate d2f ----- d2f_dPg2 = zeros(ng)#sparse((ng, 1)) ## w.r.t. p.u. Pg d2f_dQg2 = zeros(ng)#sparse((ng, 1)) ## w.r.t. p.u. Qg ipolp = find(pcost[:, MODEL] == POLYNOMIAL) d2f_dPg2[ipolp] = \ baseMVA**2 * polycost(pcost[ipolp, :], Pg[ipolp] * baseMVA, 2) if any(qcost): ## Qg is not free ipolq = find(qcost[:, MODEL] == POLYNOMIAL) d2f_dQg2[ipolq] = \ baseMVA**2 * polycost(qcost[ipolq, :], Qg[ipolq] * baseMVA, 2) i = r_[arange(vv["i1"]["Pg"], vv["iN"]["Pg"]), arange(vv["i1"]["Qg"], vv["iN"]["Qg"])] # d2f = sparse((vstack([d2f_dPg2, d2f_dQg2]).toarray().flatten(), # (i, i)), shape=(nxyz, nxyz)) d2f = sparse((r_[d2f_dPg2, d2f_dQg2], (i, i)), (nxyz, nxyz)) ## generalized cost if issparse(N) and N.nnz > 0: nw = N.shape[0] r = N * x - rh ## Nx - rhat iLT = find(r < -kk) ## below dead zone iEQ = find((r == 0) & (kk == 0)) ## dead zone doesn't exist iGT = find(r > kk) ## above dead zone iND = r_[iLT, iEQ, iGT] ## rows that are Not in the Dead region iL = find(dd == 1) ## rows using linear function iQ = find(dd == 2) ## rows using quadratic function LL = sparse((ones(len(iL)), (iL, iL)), (nw, nw)) QQ = sparse((ones(len(iQ)), (iQ, iQ)), (nw, nw)) kbar = sparse((r_[ones(len(iLT)), zeros(len(iEQ)), -ones(len(iGT))], (iND, iND)), (nw, nw)) * kk rr = r + kbar ## apply non-dead zone shift M = sparse((mm[iND], (iND, iND)), (nw, nw)) ## dead zone or scale diagrr = sparse((rr, (arange(nw), arange(nw))), (nw, nw)) ## linear rows multiplied by rr(i), quadratic rows by rr(i)^2 w = M * (LL + QQ * diagrr) * rr HwC = H * w + Cw AA = N.T * M * (LL + 2 * QQ * diagrr) d2f = d2f + AA * H * AA.T + 2 * N.T * M * QQ * \ sparse((HwC, (arange(nw), arange(nw))), (nw, nw)) * N d2f = d2f * cost_mult ##----- evaluate Hessian of power balance constraints ----- nlam = len(lmbda["eqnonlin"]) / 2 lamP = lmbda["eqnonlin"][:nlam] lamQ = lmbda["eqnonlin"][nlam:nlam + nlam] Gpaa, Gpav, Gpva, Gpvv = d2Sbus_dV2(Ybus, V, lamP) Gqaa, Gqav, Gqva, Gqvv = d2Sbus_dV2(Ybus, V, lamQ) d2G = vstack([ hstack([ vstack([hstack([Gpaa, Gpav]), hstack([Gpva, Gpvv])]).real + vstack([hstack([Gqaa, Gqav]), hstack([Gqva, Gqvv])]).imag, sparse((2 * nb, nxtra))]), hstack([ sparse((nxtra, 2 * nb)), sparse((nxtra, nxtra)) ]) ], "csr") ##----- evaluate Hessian of flow constraints ----- nmu = len(lmbda["ineqnonlin"]) / 2 muF = lmbda["ineqnonlin"][:nmu] muT = lmbda["ineqnonlin"][nmu:nmu + nmu] if ppopt['OPF_FLOW_LIM'] == 2: ## current dIf_dVa, dIf_dVm, dIt_dVa, dIt_dVm, If, It = dIbr_dV(Yf, Yt, V) Hfaa, Hfav, Hfva, Hfvv = d2AIbr_dV2(dIf_dVa, dIf_dVm, If, Yf, V, muF) Htaa, Htav, Htva, Htvv = d2AIbr_dV2(dIt_dVa, dIt_dVm, It, Yt, V, muT) else: f = branch[il, F_BUS].astype(int) ## list of "from" buses t = branch[il, T_BUS].astype(int) ## list of "to" buses ## connection matrix for line & from buses Cf = sparse((ones(nl2), (arange(nl2), f)), (nl2, nb)) ## connection matrix for line & to buses Ct = sparse((ones(nl2), (arange(nl2), t)), (nl2, nb)) dSf_dVa, dSf_dVm, dSt_dVa, dSt_dVm, Sf, St = \ dSbr_dV(branch[il,:], Yf, Yt, V) if ppopt['OPF_FLOW_LIM'] == 1: ## real power Hfaa, Hfav, Hfva, Hfvv = d2ASbr_dV2(dSf_dVa.real, dSf_dVm.real, Sf.real, Cf, Yf, V, muF) Htaa, Htav, Htva, Htvv = d2ASbr_dV2(dSt_dVa.real, dSt_dVm.real, St.real, Ct, Yt, V, muT) else: ## apparent power Hfaa, Hfav, Hfva, Hfvv = \ d2ASbr_dV2(dSf_dVa, dSf_dVm, Sf, Cf, Yf, V, muF) Htaa, Htav, Htva, Htvv = \ d2ASbr_dV2(dSt_dVa, dSt_dVm, St, Ct, Yt, V, muT) d2H = vstack([ hstack([ vstack([hstack([Hfaa, Hfav]), hstack([Hfva, Hfvv])]) + vstack([hstack([Htaa, Htav]), hstack([Htva, Htvv])]), sparse((2 * nb, nxtra)) ]), hstack([ sparse((nxtra, 2 * nb)), sparse((nxtra, nxtra)) ]) ], "csr") ##----- do numerical check using (central) finite differences ----- if 0: nx = len(x) step = 1e-5 num_d2f = sparse((nx, nx)) num_d2G = sparse((nx, nx)) num_d2H = sparse((nx, nx)) for i in range(nx): xp = x xm = x xp[i] = x[i] + step / 2 xm[i] = x[i] - step / 2 # evaluate cost & gradients _, dfp = opf_costfcn(xp, om) _, dfm = opf_costfcn(xm, om) # evaluate constraints & gradients _, _, dHp, dGp = opf_consfcn(xp, om, Ybus, Yf, Yt, ppopt, il) _, _, dHm, dGm = opf_consfcn(xm, om, Ybus, Yf, Yt, ppopt, il) num_d2f[:, i] = cost_mult * (dfp - dfm) / step num_d2G[:, i] = (dGp - dGm) * lmbda["eqnonlin"] / step num_d2H[:, i] = (dHp - dHm) * lmbda["ineqnonlin"] / step d2f_err = max(max(abs(d2f - num_d2f))) d2G_err = max(max(abs(d2G - num_d2G))) d2H_err = max(max(abs(d2H - num_d2H))) if d2f_err > 1e-6: print('Max difference in d2f: %g' % d2f_err) if d2G_err > 1e-5: print('Max difference in d2G: %g' % d2G_err) if d2H_err > 1e-6: print('Max difference in d2H: %g' % d2H_err) return d2f + d2G + d2H
def admmopf_hessfcn(self, x=None, lmbda=None, cost_mult=1.0): if x is None: x = self.pb['x0'] if lmbda is None: # multiplier for power flow constraints lmbda = {'eqnonlin': 3000 * ones(2 * len(self.idx['rbus']['int']))} # lmbda = {'eqnonlin': 3000 * ones(2 * self.region['nb'])} nx = len(x) nb = self.region['nb'] nl = self.region['nl'] ng = self.region['ng'] rho = self.var['rho'] Ybus = self.region['Ybus'] iv = self.idx['var'] branch = self.region['branch'] baseMVA = self.region['baseMVA'] bus = self.region['bus'] iVa = iv['iVa'] iVm = iv['iVm'] # reconstruct V Va, Vm = x[iVa], x[iVm] V = Vm * exp(1j * Va) # ---- evaluate Hessian of f ---------- Adiff = self.mtx['Adiff'] Asum = self.mtx['Asum'] d2f_dVm2 = d2f_dVa2 = Adiff.T.dot(Adiff) * rho + Asum.T.dot(Asum) * rho data = self.region['gencost'][:, COST] * 2 i = arange(0, ng) d2f_dPgQg2 = sparse((data, (i, i)), (2 * ng, 2 * ng)) blankv = sparse((nb, nb)) d2f_dV2 = vstack( [hstack([d2f_dVa2, blankv]), hstack([blankv, d2f_dVm2])], 'csr') blankvp = sparse((2 * nb, 2 * ng)) d2f = vstack( [hstack([d2f_dV2, blankvp]), hstack([blankvp.T, d2f_dPgQg2])], 'csr') # ---- evaluate Hessian of power flow constraints ------ nlam = len(lmbda["eqnonlin"]) // 2 lamP = zeros(nb) #for non-included pf balances use 0 as multiplier lamQ = zeros(nb) lamP[self.idx['rbus']['int']] = lmbda["eqnonlin"][:nlam] lamQ[self.idx['rbus']['int']] = lmbda["eqnonlin"][nlam:nlam + nlam] # lamP = lmbda["eqnonlin"][:nlam] # lamQ = lmbda["eqnonlin"][nlam:nlam + nlam] Gpaa, Gpav, Gpva, Gpvv = d2Sbus_dV2(Ybus, V, lamP) Gqaa, Gqav, Gqva, Gqvv = d2Sbus_dV2(Ybus, V, lamQ) d2G = vstack([ hstack([ vstack([hstack([Gpaa, Gpav]), hstack([Gpva, Gpvv])]).real + vstack([hstack([Gqaa, Gqav]), hstack([Gqva, Gqvv])]).imag, sparse((2 * nb, 2 * ng)) ]), hstack([sparse((2 * ng, 2 * nb)), sparse((2 * ng, 2 * ng))]) ], 'csr') ##----- evaluate Hessian of flow constraints ----- """ 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 il is None: il = arange(nl) ## all lines have limits by default nl2 = len(il) ## number of constrained lines nmu = int(len(lmbda["ineqnonlin"]) / 2) muF = lmbda["ineqnonlin"][:nmu] muT = lmbda["ineqnonlin"][nmu:nmu + nmu] f = branch[il, F_BUS].astype(int) ## list of "from" buses t = branch[il, T_BUS].astype(int) ## list of "to" buses ## connection matrix for line & from buses Cf = sparse((ones(nl2), (arange(nl2), f)), (nl2, nb)) ## connection matrix for line & to buses Ct = sparse((ones(nl2), (arange(nl2), t)), (nl2, nb)) dSf_dVa, dSf_dVm, dSt_dVa, dSt_dVm, Sf, St = \ dSbr_dV(branch[il,:], Yf, Yt, V) Hfaa, Hfav, Hfva, Hfvv = d2ASbr_dV2(dSf_dVa.real, dSf_dVm.real, Sf.real, Cf, Yf, V, muF) Htaa, Htav, Htva, Htvv = d2ASbr_dV2(dSt_dVa.real, dSt_dVm.real, St.real, Ct, Yt, V, muT) nxtra = nx - 2 * nb d2H = vstack([ hstack([ vstack([hstack([Hfaa, Hfav]), hstack([Hfva, Hfvv])]) + vstack([hstack([Htaa, Htav]), hstack([Htva, Htvv])]), sparse((2 * nb, nxtra)) ]), hstack([ sparse((nxtra, 2 * nb)), sparse((nxtra, nxtra)) ]) ], "csr") """ return d2f + d2G #+d2H