def eval_f(x, user_data=None): """Calculates the objective value. @param x: input vector """ om = user_data['om'] f, _ = opf_costfcn(x, om) return f
def pipsopf_solver(om, ppopt, out_opt=None): """Solves AC optimal power flow using PIPS. Inputs are an OPF model object, a PYPOWER options vector and a dict containing keys (can be empty) for each of the desired optional output fields. outputs are a C{results} dict, C{success} flag and C{raw} output dict. C{results} is a PYPOWER case dict (ppc) with the usual baseMVA, bus branch, gen, gencost fields, along with the following additional fields: - C{order} see 'help ext2int' for details of this field - C{x} final value of optimization variables (internal order) - C{f} final objective function value - C{mu} shadow prices on ... - C{var} - C{l} lower bounds on variables - C{u} upper bounds on variables - C{nln} - C{l} lower bounds on nonlinear constraints - C{u} upper bounds on nonlinear constraints - C{lin} - C{l} lower bounds on linear constraints - C{u} upper bounds on linear constraints C{success} is C{True} if solver converged successfully, C{False} otherwise C{raw} is a raw output dict in form returned by MINOS - xr final value of optimization variables - pimul constraint multipliers - info solver specific termination code - output solver specific output information @see: L{opf}, L{pips} @author: Ray Zimmerman (PSERC Cornell) @author: Carlos E. Murillo-Sanchez (PSERC Cornell & Universidad Autonoma de Manizales) @author: Richard Lincoln """ ##----- initialization ----- ## optional output if out_opt is None: out_opt = {} ## options verbose = ppopt['VERBOSE'] feastol = ppopt['PDIPM_FEASTOL'] gradtol = ppopt['PDIPM_GRADTOL'] comptol = ppopt['PDIPM_COMPTOL'] costtol = ppopt['PDIPM_COSTTOL'] max_it = ppopt['PDIPM_MAX_IT'] max_red = ppopt['SCPDIPM_RED_IT'] step_control = (ppopt['OPF_ALG'] == 565) ## OPF_ALG == 565, PIPS-sc if feastol == 0: feastol = ppopt['OPF_VIOLATION'] opt = { 'feastol': feastol, 'gradtol': gradtol, 'comptol': comptol, 'costtol': costtol, 'max_it': max_it, 'max_red': max_red, 'step_control': step_control, 'cost_mult': 1e-4, 'verbose': verbose } ## unpack data ppc = om.get_ppc() baseMVA, bus, gen, branch, gencost = \ ppc["baseMVA"], ppc["bus"], ppc["gen"], ppc["branch"], ppc["gencost"] vv, _, nn, _ = om.get_idx() ## problem dimensions nb = bus.shape[0] ## number of buses nl = branch.shape[0] ## number of branches ny = om.getN('var', 'y') ## number of piece-wise linear costs ## linear constraints A, l, u = om.linear_constraints() ## bounds on optimization vars _, xmin, xmax = om.getv() ## build admittance matrices Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch) ## try to select an interior initial point ll, uu = xmin.copy(), xmax.copy() ll[xmin == -Inf] = -1e10 ## replace Inf with numerical proxies uu[xmax == Inf] = 1e10 x0 = (ll + uu) / 2 Varefs = bus[bus[:, BUS_TYPE] == REF, VA] * (pi / 180) ## angles set to first reference angle x0[vv["i1"]["Va"]:vv["iN"]["Va"]] = Varefs[0] if ny > 0: ipwl = find(gencost[:, MODEL] == PW_LINEAR) # PQ = r_[gen[:, PMAX], gen[:, QMAX]] # c = totcost(gencost[ipwl, :], PQ[ipwl]) c = gencost.flatten('F')[sub2ind(gencost.shape, ipwl, NCOST+2*gencost[ipwl, NCOST])] ## largest y-value in CCV data x0[vv["i1"]["y"]:vv["iN"]["y"]] = max(c) + 0.1 * abs(max(c)) # x0[vv["i1"]["y"]:vv["iN"]["y"]] = c + 0.1 * abs(c) ## find branches with flow limits il = find((branch[:, RATE_A] != 0) & (branch[:, RATE_A] < 1e10)) nl2 = len(il) ## number of constrained lines ##----- run opf ----- f_fcn = lambda x, return_hessian=False: opf_costfcn(x, om, return_hessian) gh_fcn = lambda x: opf_consfcn(x, om, Ybus, Yf[il, :], Yt[il,:], ppopt, il) hess_fcn = lambda x, lmbda, cost_mult: opf_hessfcn(x, lmbda, om, Ybus, Yf[il, :], Yt[il, :], ppopt, il, cost_mult) solution = pips(f_fcn, x0, A, l, u, xmin, xmax, gh_fcn, hess_fcn, opt) x, f, info, lmbda, output = solution["x"], solution["f"], \ solution["eflag"], solution["lmbda"], solution["output"] success = (info > 0) ## update solution data Va = x[vv["i1"]["Va"]:vv["iN"]["Va"]] Vm = x[vv["i1"]["Vm"]:vv["iN"]["Vm"]] Pg = x[vv["i1"]["Pg"]:vv["iN"]["Pg"]] Qg = x[vv["i1"]["Qg"]:vv["iN"]["Qg"]] V = Vm * exp(1j * Va) ##----- calculate return values ----- ## update voltages & generator outputs bus[:, VA] = Va * 180 / pi bus[:, VM] = Vm gen[:, PG] = Pg * baseMVA gen[:, QG] = Qg * baseMVA gen[:, VG] = Vm[ gen[:, GEN_BUS].astype(int) ] ## compute branch flows Sf = V[ branch[:, F_BUS].astype(int) ] * conj(Yf * V) ## cplx pwr at "from" bus, p["u"]. St = V[ branch[:, T_BUS].astype(int) ] * conj(Yt * V) ## cplx pwr at "to" bus, p["u"]. branch[:, PF] = Sf.real * baseMVA branch[:, QF] = Sf.imag * baseMVA branch[:, PT] = St.real * baseMVA branch[:, QT] = St.imag * baseMVA ## line constraint is actually on square of limit ## so we must fix multipliers muSf = zeros(nl) muSt = zeros(nl) if len(il) > 0: muSf[il] = \ 2 * lmbda["ineqnonlin"][:nl2] * branch[il, RATE_A] / baseMVA muSt[il] = \ 2 * lmbda["ineqnonlin"][nl2:nl2+nl2] * branch[il, RATE_A] / baseMVA ## update Lagrange multipliers bus[:, MU_VMAX] = lmbda["upper"][vv["i1"]["Vm"]:vv["iN"]["Vm"]] bus[:, MU_VMIN] = lmbda["lower"][vv["i1"]["Vm"]:vv["iN"]["Vm"]] gen[:, MU_PMAX] = lmbda["upper"][vv["i1"]["Pg"]:vv["iN"]["Pg"]] / baseMVA gen[:, MU_PMIN] = lmbda["lower"][vv["i1"]["Pg"]:vv["iN"]["Pg"]] / baseMVA gen[:, MU_QMAX] = lmbda["upper"][vv["i1"]["Qg"]:vv["iN"]["Qg"]] / baseMVA gen[:, MU_QMIN] = lmbda["lower"][vv["i1"]["Qg"]:vv["iN"]["Qg"]] / baseMVA bus[:, LAM_P] = \ lmbda["eqnonlin"][nn["i1"]["Pmis"]:nn["iN"]["Pmis"]] / baseMVA bus[:, LAM_Q] = \ lmbda["eqnonlin"][nn["i1"]["Qmis"]:nn["iN"]["Qmis"]] / baseMVA branch[:, MU_SF] = muSf / baseMVA branch[:, MU_ST] = muSt / baseMVA ## package up results nlnN = om.getN('nln') ## extract multipliers for nonlinear constraints kl = find(lmbda["eqnonlin"] < 0) ku = find(lmbda["eqnonlin"] > 0) nl_mu_l = zeros(nlnN) nl_mu_u = r_[zeros(2*nb), muSf, muSt] nl_mu_l[kl] = -lmbda["eqnonlin"][kl] nl_mu_u[ku] = lmbda["eqnonlin"][ku] mu = { 'var': {'l': lmbda["lower"], 'u': lmbda["upper"]}, 'nln': {'l': nl_mu_l, 'u': nl_mu_u}, 'lin': {'l': lmbda["mu_l"], 'u': lmbda["mu_u"]} } results = ppc results["bus"], results["branch"], results["gen"], \ results["om"], results["x"], results["mu"], results["f"] = \ bus, branch, gen, om, x, mu, f pimul = r_[ results["mu"]["nln"]["l"] - results["mu"]["nln"]["u"], results["mu"]["lin"]["l"] - results["mu"]["lin"]["u"], -ones(ny > 0), results["mu"]["var"]["l"] - results["mu"]["var"]["u"], ] raw = {'xr': x, 'pimul': pimul, 'info': info, 'output': output} return results, success, raw
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 eval_grad_f(x, user_data=None): """Calculates gradient for objective function. """ om = user_data['om'] _, df = opf_costfcn(x, om) return df
def ipoptopf_solver(om, ppopt): """Solves AC optimal power flow using IPOPT. Inputs are an OPF model object and a PYPOWER options vector. Outputs are a C{results} dict, C{success} flag and C{raw} output dict. C{results} is a PYPOWER case dict (ppc) with the usual C{baseMVA}, C{bus} C{branch}, C{gen}, C{gencost} fields, along with the following additional fields: - C{order} see 'help ext2int' for details of this field - C{x} final value of optimization variables (internal order) - C{f} final objective function value - C{mu} shadow prices on ... - C{var} - C{l} lower bounds on variables - C{u} upper bounds on variables - C{nln} - C{l} lower bounds on nonlinear constraints - C{u} upper bounds on nonlinear constraints - C{lin} - C{l} lower bounds on linear constraints - C{u} upper bounds on linear constraints C{success} is C{True} if solver converged successfully, C{False} otherwise C{raw} is a raw output dict in form returned by MINOS - C{xr} final value of optimization variables - C{pimul} constraint multipliers - C{info} solver specific termination code - C{output} solver specific output information @see: L{opf}, L{pips} @author: Ray Zimmerman (PSERC Cornell) @author: Carlos E. Murillo-Sanchez (PSERC Cornell & Universidad Autonoma de Manizales) @author: Richard Lincoln """ import pyipopt ## unpack data ppc = om.get_ppc() baseMVA, bus, gen, branch, gencost = \ ppc['baseMVA'], ppc['bus'], ppc['gen'], ppc['branch'], ppc['gencost'] vv, _, nn, _ = om.get_idx() ## problem dimensions nb = shape(bus)[0] ## number of buses ng = shape(gen)[0] ## number of gens nl = shape(branch)[0] ## number of branches ny = om.getN('var', 'y') ## number of piece-wise linear costs ## linear constraints A, l, u = om.linear_constraints() ## bounds on optimization vars _, xmin, xmax = om.getv() ## build admittance matrices Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch) ## try to select an interior initial point ll = xmin.copy() uu = xmax.copy() ll[xmin == -Inf] = -2e19 ## replace Inf with numerical proxies uu[xmax == Inf] = 2e19 x0 = (ll + uu) / 2 Varefs = bus[bus[:, BUS_TYPE] == REF, VA] * (pi / 180) x0[vv['i1']['Va']:vv['iN']['Va']] = Varefs[ 0] ## angles set to first reference angle if ny > 0: ipwl = find(gencost[:, MODEL] == PW_LINEAR) # PQ = r_[gen[:, PMAX], gen[:, QMAX]] # c = totcost(gencost[ipwl, :], PQ[ipwl]) ## largest y-value in CCV data c = gencost.flatten('F')[sub2ind(shape(gencost), ipwl, NCOST + 2 * gencost[ipwl, NCOST])] x0[vv['i1']['y']:vv['iN']['y']] = max(c) + 0.1 * abs(max(c)) # x0[vv['i1']['y']:vv['iN']['y']) = c + 0.1 * abs(c) ## find branches with flow limits il = find((branch[:, RATE_A] != 0) & (branch[:, RATE_A] < 1e10)) nl2 = len(il) ## number of constrained lines ##----- run opf ----- ## build Jacobian and Hessian structure if A is not None and issparse(A): nA = A.shape[0] ## number of original linear constraints else: nA = 0 nx = len(x0) f = branch[:, F_BUS] ## list of "from" buses t = branch[:, T_BUS] ## list of "to" buses Cf = sparse((ones(nl), (arange(nl), f)), (nl, nb)) ## connection matrix for line & from buses Ct = sparse((ones(nl), (arange(nl), t)), (nl, nb)) ## connection matrix for line & to buses Cl = Cf + Ct Cb = Cl.T * Cl + speye(nb, nb) Cl2 = Cl[il, :] Cg = sparse((ones(ng), (gen[:, GEN_BUS], arange(ng))), (nb, ng)) nz = nx - 2 * (nb + ng) nxtra = nx - 2 * nb if nz > 0: Js = vstack([ hstack([Cb, Cb, Cg, sparse( (nb, ng)), sparse((nb, nz))]), hstack([Cb, Cb, sparse( (nb, ng)), Cg, sparse((nb, nz))]), hstack([Cl2, Cl2, sparse((nl2, 2 * ng)), sparse((nl2, nz))]), hstack([Cl2, Cl2, sparse((nl2, 2 * ng)), sparse((nl2, nz))]) ], 'coo') else: Js = vstack([ hstack([Cb, Cb, Cg, sparse((nb, ng))]), hstack([ Cb, Cb, sparse((nb, ng)), Cg, ]), hstack([ Cl2, Cl2, sparse((nl2, 2 * ng)), ]), hstack([ Cl2, Cl2, sparse((nl2, 2 * ng)), ]) ], 'coo') if A is not None and issparse(A): Js = vstack([Js, A], 'coo') f, _, d2f = opf_costfcn(x0, om, True) Hs = tril(d2f + vstack([ hstack([Cb, Cb, sparse((nb, nxtra))]), hstack([Cb, Cb, sparse((nb, nxtra))]), sparse((nxtra, nx)) ]), format='coo') ## set options struct for IPOPT # options = {} # options['ipopt'] = ipopt_options([], ppopt) ## extra data to pass to functions userdata = { 'om': om, 'Ybus': Ybus, 'Yf': Yf[il, :], 'Yt': Yt[il, :], 'ppopt': ppopt, 'il': il, 'A': A, 'nA': nA, 'neqnln': 2 * nb, 'niqnln': 2 * nl2, 'Js': Js, 'Hs': Hs } ## check Jacobian and Hessian structure #xr = rand(x0.shape) #lmbda = rand( 2 * nb + 2 * nl2) #Js1 = eval_jac_g(x, flag, userdata) #(xr, options.auxdata) #Hs1 = eval_h(xr, 1, lmbda, userdata) #i1, j1, s = find(Js) #i2, j2, s = find(Js1) #if (len(i1) != len(i2)) | (norm(i1 - i2) != 0) | (norm(j1 - j2) != 0): # raise ValueError, 'something''s wrong with the Jacobian structure' # #i1, j1, s = find(Hs) #i2, j2, s = find(Hs1) #if (len(i1) != len(i2)) | (norm(i1 - i2) != 0) | (norm(j1 - j2) != 0): # raise ValueError, 'something''s wrong with the Hessian structure' ## define variable and constraint bounds # n is the number of variables n = x0.shape[0] # xl is the lower bound of x as bounded constraints xl = xmin # xu is the upper bound of x as bounded constraints xu = xmax neqnln = 2 * nb niqnln = 2 * nl2 # number of constraints m = neqnln + niqnln + nA # lower bound of constraint gl = r_[zeros(neqnln), -Inf * ones(niqnln), l] # upper bound of constraints gu = r_[zeros(neqnln), zeros(niqnln), u] # number of nonzeros in Jacobi matrix nnzj = Js.nnz # number of non-zeros in Hessian matrix, you can set it to 0 nnzh = Hs.nnz eval_hessian = True if eval_hessian: hessian = lambda x, lagrange, obj_factor, flag, user_data=None: \ eval_h(x, lagrange, obj_factor, flag, userdata) nlp = pyipopt.create(n, xl, xu, m, gl, gu, nnzj, nnzh, eval_f, eval_grad_f, eval_g, eval_jac_g, hessian) else: nnzh = 0 nlp = pyipopt.create(n, xl, xu, m, gl, gu, nnzj, nnzh, eval_f, eval_grad_f, eval_g, eval_jac_g) nlp.int_option('print_level', 5) nlp.num_option('tol', 1.0000e-12) nlp.int_option('max_iter', 250) nlp.num_option('dual_inf_tol', 0.10000) nlp.num_option('constr_viol_tol', 1.0000e-06) nlp.num_option('compl_inf_tol', 1.0000e-05) nlp.num_option('acceptable_tol', 1.0000e-08) nlp.num_option('acceptable_constr_viol_tol', 1.0000e-04) nlp.num_option('acceptable_compl_inf_tol', 0.0010000) nlp.str_option('mu_strategy', 'adaptive') iter = 0 def intermediate_callback(algmod, iter_count, obj_value, inf_pr, inf_du, mu, d_norm, regularization_size, alpha_du, alpha_pr, ls_trials, user_data=None): iter = iter_count return True nlp.set_intermediate_callback(intermediate_callback) ## run the optimization # returns final solution x, upper and lower bound for multiplier, final # objective function obj and the return status of ipopt x, zl, zu, obj, status, zg = nlp.solve(x0, m, userdata) info = { 'x': x, 'zl': zl, 'zu': zu, 'obj': obj, 'status': status, 'lmbda': zg } nlp.close() success = (status == 0) | (status == 1) output = {'iterations': iter} f, _ = opf_costfcn(x, om) ## update solution data Va = x[vv['i1']['Va']:vv['iN']['Va']] Vm = x[vv['i1']['Vm']:vv['iN']['Vm']] Pg = x[vv['i1']['Pg']:vv['iN']['Pg']] Qg = x[vv['i1']['Qg']:vv['iN']['Qg']] V = Vm * exp(1j * Va) ##----- calculate return values ----- ## update voltages & generator outputs bus[:, VA] = Va * 180 / pi bus[:, VM] = Vm gen[:, PG] = Pg * baseMVA gen[:, QG] = Qg * baseMVA gen[:, VG] = Vm[gen[:, GEN_BUS].astype(int)] ## compute branch flows f_br = branch[:, F_BUS].astype(int) t_br = branch[:, T_BUS].astype(int) Sf = V[f_br] * conj(Yf * V) ## cplx pwr at "from" bus, p.u. St = V[t_br] * conj(Yt * V) ## cplx pwr at "to" bus, p.u. branch[:, PF] = Sf.real * baseMVA branch[:, QF] = Sf.imag * baseMVA branch[:, PT] = St.real * baseMVA branch[:, QT] = St.imag * baseMVA ## line constraint is actually on square of limit ## so we must fix multipliers muSf = zeros(nl) muSt = zeros(nl) if len(il) > 0: muSf[il] = 2 * info['lmbda'][2 * nb + arange(nl2)] * branch[il, RATE_A] / baseMVA muSt[il] = 2 * info['lmbda'][2 * nb + nl2 + arange(nl2)] * branch[il, RATE_A] / baseMVA ## update Lagrange multipliers bus[:, MU_VMAX] = info['zu'][vv['i1']['Vm']:vv['iN']['Vm']] bus[:, MU_VMIN] = info['zl'][vv['i1']['Vm']:vv['iN']['Vm']] gen[:, MU_PMAX] = info['zu'][vv['i1']['Pg']:vv['iN']['Pg']] / baseMVA gen[:, MU_PMIN] = info['zl'][vv['i1']['Pg']:vv['iN']['Pg']] / baseMVA gen[:, MU_QMAX] = info['zu'][vv['i1']['Qg']:vv['iN']['Qg']] / baseMVA gen[:, MU_QMIN] = info['zl'][vv['i1']['Qg']:vv['iN']['Qg']] / baseMVA bus[:, LAM_P] = info['lmbda'][nn['i1']['Pmis']:nn['iN']['Pmis']] / baseMVA bus[:, LAM_Q] = info['lmbda'][nn['i1']['Qmis']:nn['iN']['Qmis']] / baseMVA branch[:, MU_SF] = muSf / baseMVA branch[:, MU_ST] = muSt / baseMVA ## package up results nlnN = om.getN('nln') ## extract multipliers for nonlinear constraints kl = find(info['lmbda'][:2 * nb] < 0) ku = find(info['lmbda'][:2 * nb] > 0) nl_mu_l = zeros(nlnN) nl_mu_u = r_[zeros(2 * nb), muSf, muSt] nl_mu_l[kl] = -info['lmbda'][kl] nl_mu_u[ku] = info['lmbda'][ku] ## extract multipliers for linear constraints lam_lin = info['lmbda'][2 * nb + 2 * nl2 + arange(nA)] ## lmbda for linear constraints kl = find(lam_lin < 0) ## lower bound binding ku = find(lam_lin > 0) ## upper bound binding mu_l = zeros(nA) mu_l[kl] = -lam_lin[kl] mu_u = zeros(nA) mu_u[ku] = lam_lin[ku] mu = { 'var': {'l': info['zl'], 'u': info['zu']}, 'nln': {'l': nl_mu_l, 'u': nl_mu_u}, \ 'lin': {'l': mu_l, 'u': mu_u} } results = ppc results['bus'], results['branch'], results['gen'], \ results['om'], results['x'], results['mu'], results['f'] = \ bus, branch, gen, om, x, mu, f pimul = r_[results['mu']['nln']['l'] - results['mu']['nln']['u'], results['mu']['lin']['l'] - results['mu']['lin']['u'], -ones(ny > 0), results['mu']['var']['l'] - results['mu']['var']['u']] raw = {'xr': x, 'pimul': pimul, 'info': info['status'], 'output': output} return results, success, raw
def pipsopf_solver(om, ppopt, out_opt=None): """Solves AC optimal power flow using PIPS. Inputs are an OPF model object, a PYPOWER options vector and a dict containing keys (can be empty) for each of the desired optional output fields. outputs are a C{results} dict, C{success} flag and C{raw} output dict. C{results} is a PYPOWER case dict (ppc) with the usual baseMVA, bus branch, gen, gencost fields, along with the following additional fields: - C{order} see 'help ext2int' for details of this field - C{x} final value of optimization variables (internal order) - C{f} final objective function value - C{mu} shadow prices on ... - C{var} - C{l} lower bounds on variables - C{u} upper bounds on variables - C{nln} - C{l} lower bounds on nonlinear constraints - C{u} upper bounds on nonlinear constraints - C{lin} - C{l} lower bounds on linear constraints - C{u} upper bounds on linear constraints C{success} is C{True} if solver converged successfully, C{False} otherwise C{raw} is a raw output dict in form returned by MINOS - xr final value of optimization variables - pimul constraint multipliers - info solver specific termination code - output solver specific output information @see: L{opf}, L{pips} @author: Ray Zimmerman (PSERC Cornell) @author: Carlos E. Murillo-Sanchez (PSERC Cornell & Universidad Autonoma de Manizales) @author: Richard Lincoln """ ##----- initialization ----- ## optional output if out_opt is None: out_opt = {} ## options verbose = ppopt['VERBOSE'] feastol = ppopt['PDIPM_FEASTOL'] gradtol = ppopt['PDIPM_GRADTOL'] comptol = ppopt['PDIPM_COMPTOL'] costtol = ppopt['PDIPM_COSTTOL'] max_it = ppopt['PDIPM_MAX_IT'] max_red = ppopt['SCPDIPM_RED_IT'] step_control = (ppopt['OPF_ALG'] == 565) ## OPF_ALG == 565, PIPS-sc if feastol == 0: feastol = ppopt['OPF_VIOLATION'] opt = { 'feastol': feastol, 'gradtol': gradtol, 'comptol': comptol, 'costtol': costtol, 'max_it': max_it, 'max_red': max_red, 'step_control': step_control, 'cost_mult': 1e-4, 'verbose': verbose } ## unpack data ppc = om.get_ppc() baseMVA, bus, gen, branch, gencost = \ ppc["baseMVA"], ppc["bus"], ppc["gen"], ppc["branch"], ppc["gencost"] vv, _, nn, _ = om.get_idx() ## problem dimensions nb = bus.shape[0] ## number of buses nl = branch.shape[0] ## number of branches ny = om.getN('var', 'y') ## number of piece-wise linear costs ## linear constraints A, l, u = om.linear_constraints() ## bounds on optimization vars _, xmin, xmax = om.getv() ## build admittance matrices Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch) ## try to select an interior initial point ll, uu = xmin.copy(), xmax.copy() ll[xmin == -Inf] = -1e10 ## replace Inf with numerical proxies uu[xmax == Inf] = 1e10 x0 = (ll + uu) / 2 Varefs = bus[bus[:, BUS_TYPE] == REF, VA] * (pi / 180) ## angles set to first reference angle x0[vv["i1"]["Va"]:vv["iN"]["Va"]] = Varefs[0] if ny > 0: ipwl = find(gencost[:, MODEL] == PW_LINEAR) # PQ = r_[gen[:, PMAX], gen[:, QMAX]] # c = totcost(gencost[ipwl, :], PQ[ipwl]) c = gencost.flatten('F')[sub2ind(gencost.shape, ipwl, NCOST+2*gencost[ipwl, NCOST])] ## largest y-value in CCV data x0[vv["i1"]["y"]:vv["iN"]["y"]] = max(c) + 0.1 * abs(max(c)) # x0[vv["i1"]["y"]:vv["iN"]["y"]] = c + 0.1 * abs(c) ## find branches with flow limits il = find((branch[:, RATE_A] != 0) & (branch[:, RATE_A] < 1e10)) nl2 = len(il) ## number of constrained lines ##----- run opf ----- f_fcn = lambda x, return_hessian=False: opf_costfcn(x, om, return_hessian) gh_fcn = lambda x: opf_consfcn(x, om, Ybus, Yf[il, :], Yt[il,:], ppopt, il) hess_fcn = lambda x, lmbda, cost_mult: opf_hessfcn(x, lmbda, om, Ybus, Yf[il, :], Yt[il, :], ppopt, il, cost_mult) solution = pips(f_fcn, x0, A, l, u, xmin, xmax, gh_fcn, hess_fcn, opt) x, f, info, lmbda, output = solution["x"], solution["f"], \ solution["eflag"], solution["lmbda"], solution["output"] success = (info > 0) ## update solution data Va = x[vv["i1"]["Va"]:vv["iN"]["Va"]] Vm = x[vv["i1"]["Vm"]:vv["iN"]["Vm"]] Pg = x[vv["i1"]["Pg"]:vv["iN"]["Pg"]] Qg = x[vv["i1"]["Qg"]:vv["iN"]["Qg"]] V = Vm * exp(1j * Va) ##----- calculate return values ----- ## update voltages & generator outputs bus[:, VA] = Va * 180 / pi bus[:, VM] = Vm gen[:, PG] = Pg * baseMVA gen[:, QG] = Qg * baseMVA gen[:, VG] = Vm[ gen[:, GEN_BUS].astype(int) ] ## compute branch flows Sf = V[ branch[:, F_BUS].astype(int) ] * conj(Yf * V) ## cplx pwr at "from" bus, p["u"]. St = V[ branch[:, T_BUS].astype(int) ] * conj(Yt * V) ## cplx pwr at "to" bus, p["u"]. branch[:, PF] = Sf.real * baseMVA branch[:, QF] = Sf.imag * baseMVA branch[:, PT] = St.real * baseMVA branch[:, QT] = St.imag * baseMVA ## line constraint is actually on square of limit ## so we must fix multipliers muSf = zeros(nl) muSt = zeros(nl) if len(il) > 0: muSf[il] = \ 2 * lmbda["ineqnonlin"][:nl2] * branch[il, RATE_A] / baseMVA muSt[il] = \ 2 * lmbda["ineqnonlin"][nl2:nl2+nl2] * branch[il, RATE_A] / baseMVA ## update Lagrange multipliers bus[:, MU_VMAX] = lmbda["upper"][vv["i1"]["Vm"]:vv["iN"]["Vm"]] bus[:, MU_VMIN] = lmbda["lower"][vv["i1"]["Vm"]:vv["iN"]["Vm"]] gen[:, MU_PMAX] = lmbda["upper"][vv["i1"]["Pg"]:vv["iN"]["Pg"]] / baseMVA gen[:, MU_PMIN] = lmbda["lower"][vv["i1"]["Pg"]:vv["iN"]["Pg"]] / baseMVA gen[:, MU_QMAX] = lmbda["upper"][vv["i1"]["Qg"]:vv["iN"]["Qg"]] / baseMVA gen[:, MU_QMIN] = lmbda["lower"][vv["i1"]["Qg"]:vv["iN"]["Qg"]] / baseMVA bus[:, LAM_P] = \ lmbda["eqnonlin"][nn["i1"]["Pmis"]:nn["iN"]["Pmis"]] / baseMVA bus[:, LAM_Q] = \ lmbda["eqnonlin"][nn["i1"]["Qmis"]:nn["iN"]["Qmis"]] / baseMVA branch[:, MU_SF] = muSf / baseMVA branch[:, MU_ST] = muSt / baseMVA ## package up results nlnN = om.getN('nln') ## extract multipliers for nonlinear constraints kl = find(lmbda["eqnonlin"] < 0) ku = find(lmbda["eqnonlin"] > 0) nl_mu_l = zeros(nlnN) nl_mu_u = r_[zeros(2*nb), muSf, muSt] nl_mu_l[kl] = -lmbda["eqnonlin"][kl] nl_mu_u[ku] = lmbda["eqnonlin"][ku] mu = { 'var': {'l': lmbda["lower"], 'u': lmbda["upper"]}, 'nln': {'l': nl_mu_l, 'u': nl_mu_u}, 'lin': {'l': lmbda["mu_l"], 'u': lmbda["mu_u"]} } results = ppc results["bus"], results["branch"], results["gen"], \ results["om"], results["x"], results["mu"], results["f"] = \ bus, branch, gen, om, x, mu, f pimul = r_[ results["mu"]["nln"]["l"] - results["mu"]["nln"]["u"], results["mu"]["lin"]["l"] - results["mu"]["lin"]["u"], -ones(ny), results["mu"]["var"]["l"] - results["mu"]["var"]["u"], ] raw = {'xr': x, 'pimul': pimul, 'info': info, 'output': output} return results, success, raw
def ipoptopf_solver(om, ppopt): """Solves AC optimal power flow using IPOPT. Inputs are an OPF model object and a PYPOWER options vector. Outputs are a C{results} dict, C{success} flag and C{raw} output dict. C{results} is a PYPOWER case dict (ppc) with the usual C{baseMVA}, C{bus} C{branch}, C{gen}, C{gencost} fields, along with the following additional fields: - C{order} see 'help ext2int' for details of this field - C{x} final value of optimization variables (internal order) - C{f} final objective function value - C{mu} shadow prices on ... - C{var} - C{l} lower bounds on variables - C{u} upper bounds on variables - C{nln} - C{l} lower bounds on nonlinear constraints - C{u} upper bounds on nonlinear constraints - C{lin} - C{l} lower bounds on linear constraints - C{u} upper bounds on linear constraints C{success} is C{True} if solver converged successfully, C{False} otherwise C{raw} is a raw output dict in form returned by MINOS - C{xr} final value of optimization variables - C{pimul} constraint multipliers - C{info} solver specific termination code - C{output} solver specific output information @see: L{opf}, L{pips} @author: Ray Zimmerman (PSERC Cornell) @author: Carlos E. Murillo-Sanchez (PSERC Cornell & Universidad Autonoma de Manizales) @author: Richard Lincoln """ import pyipopt ## unpack data ppc = om.get_ppc() baseMVA, bus, gen, branch, gencost = \ ppc['baseMVA'], ppc['bus'], ppc['gen'], ppc['branch'], ppc['gencost'] vv, _, nn, _ = om.get_idx() ## problem dimensions nb = shape(bus)[0] ## number of buses ng = shape(gen)[0] ## number of gens nl = shape(branch)[0] ## number of branches ny = om.getN('var', 'y') ## number of piece-wise linear costs ## linear constraints A, l, u = om.linear_constraints() ## bounds on optimization vars _, xmin, xmax = om.getv() ## build admittance matrices Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch) ## try to select an interior initial point ll = xmin.copy(); uu = xmax.copy() ll[xmin == -Inf] = -2e19 ## replace Inf with numerical proxies uu[xmax == Inf] = 2e19 x0 = (ll + uu) / 2 Varefs = bus[bus[:, BUS_TYPE] == REF, VA] * (pi / 180) x0[vv['i1']['Va']:vv['iN']['Va']] = Varefs[0] ## angles set to first reference angle if ny > 0: ipwl = find(gencost[:, MODEL] == PW_LINEAR) # PQ = r_[gen[:, PMAX], gen[:, QMAX]] # c = totcost(gencost[ipwl, :], PQ[ipwl]) ## largest y-value in CCV data c = gencost.flatten('F')[sub2ind(shape(gencost), ipwl, NCOST + 2 * gencost[ipwl, NCOST])] x0[vv['i1']['y']:vv['iN']['y']] = max(c) + 0.1 * abs(max(c)) # x0[vv['i1']['y']:vv['iN']['y']) = c + 0.1 * abs(c) ## find branches with flow limits il = find((branch[:, RATE_A] != 0) & (branch[:, RATE_A] < 1e10)) nl2 = len(il) ## number of constrained lines ##----- run opf ----- ## build Jacobian and Hessian structure if A is not None and issparse(A): nA = A.shape[0] ## number of original linear constraints else: nA = 0 nx = len(x0) f = branch[:, F_BUS] ## list of "from" buses t = branch[:, T_BUS] ## list of "to" buses Cf = sparse((ones(nl), (arange(nl), f)), (nl, nb)) ## connection matrix for line & from buses Ct = sparse((ones(nl), (arange(nl), t)), (nl, nb)) ## connection matrix for line & to buses Cl = Cf + Ct Cb = Cl.T * Cl + speye(nb, nb) Cl2 = Cl[il, :] Cg = sparse((ones(ng), (gen[:, GEN_BUS], arange(ng))), (nb, ng)) nz = nx - 2 * (nb + ng) nxtra = nx - 2 * nb if nz > 0: Js = vstack([ hstack([Cb, Cb, Cg, sparse((nb, ng)), sparse((nb, nz))]), hstack([Cb, Cb, sparse((nb, ng)), Cg, sparse((nb, nz))]), hstack([Cl2, Cl2, sparse((nl2, 2 * ng)), sparse((nl2, nz))]), hstack([Cl2, Cl2, sparse((nl2, 2 * ng)), sparse((nl2, nz))]) ], 'coo') else: Js = vstack([ hstack([Cb, Cb, Cg, sparse((nb, ng))]), hstack([Cb, Cb, sparse((nb, ng)), Cg, ]), hstack([Cl2, Cl2, sparse((nl2, 2 * ng)), ]), hstack([Cl2, Cl2, sparse((nl2, 2 * ng)), ]) ], 'coo') if A is not None and issparse(A): Js = vstack([Js, A], 'coo') f, _, d2f = opf_costfcn(x0, om, True) Hs = tril(d2f + vstack([ hstack([Cb, Cb, sparse((nb, nxtra))]), hstack([Cb, Cb, sparse((nb, nxtra))]), sparse((nxtra, nx)) ]), format='coo') ## set options struct for IPOPT # options = {} # options['ipopt'] = ipopt_options([], ppopt) ## extra data to pass to functions userdata = { 'om': om, 'Ybus': Ybus, 'Yf': Yf[il, :], 'Yt': Yt[il, :], 'ppopt': ppopt, 'il': il, 'A': A, 'nA': nA, 'neqnln': 2 * nb, 'niqnln': 2 * nl2, 'Js': Js, 'Hs': Hs } ## check Jacobian and Hessian structure #xr = rand(x0.shape) #lmbda = rand( 2 * nb + 2 * nl2) #Js1 = eval_jac_g(x, flag, userdata) #(xr, options.auxdata) #Hs1 = eval_h(xr, 1, lmbda, userdata) #i1, j1, s = find(Js) #i2, j2, s = find(Js1) #if (len(i1) != len(i2)) | (norm(i1 - i2) != 0) | (norm(j1 - j2) != 0): # raise ValueError, 'something''s wrong with the Jacobian structure' # #i1, j1, s = find(Hs) #i2, j2, s = find(Hs1) #if (len(i1) != len(i2)) | (norm(i1 - i2) != 0) | (norm(j1 - j2) != 0): # raise ValueError, 'something''s wrong with the Hessian structure' ## define variable and constraint bounds # n is the number of variables n = x0.shape[0] # xl is the lower bound of x as bounded constraints xl = xmin # xu is the upper bound of x as bounded constraints xu = xmax neqnln = 2 * nb niqnln = 2 * nl2 # number of constraints m = neqnln + niqnln + nA # lower bound of constraint gl = r_[zeros(neqnln), -Inf * ones(niqnln), l] # upper bound of constraints gu = r_[zeros(neqnln), zeros(niqnln), u] # number of nonzeros in Jacobi matrix nnzj = Js.nnz # number of non-zeros in Hessian matrix, you can set it to 0 nnzh = Hs.nnz eval_hessian = True if eval_hessian: hessian = lambda x, lagrange, obj_factor, flag, user_data=None: \ eval_h(x, lagrange, obj_factor, flag, userdata) nlp = pyipopt.create(n, xl, xu, m, gl, gu, nnzj, nnzh, eval_f, eval_grad_f, eval_g, eval_jac_g, hessian) else: nnzh = 0 nlp = pyipopt.create(n, xl, xu, m, gl, gu, nnzj, nnzh, eval_f, eval_grad_f, eval_g, eval_jac_g) nlp.int_option('print_level', 5) nlp.num_option('tol', 1.0000e-12) nlp.int_option('max_iter', 250) nlp.num_option('dual_inf_tol', 0.10000) nlp.num_option('constr_viol_tol', 1.0000e-06) nlp.num_option('compl_inf_tol', 1.0000e-05) nlp.num_option('acceptable_tol', 1.0000e-08) nlp.num_option('acceptable_constr_viol_tol', 1.0000e-04) nlp.num_option('acceptable_compl_inf_tol', 0.0010000) nlp.str_option('mu_strategy', 'adaptive') iter = 0 def intermediate_callback(algmod, iter_count, obj_value, inf_pr, inf_du, mu, d_norm, regularization_size, alpha_du, alpha_pr, ls_trials, user_data=None): iter = iter_count return True nlp.set_intermediate_callback(intermediate_callback) ## run the optimization # returns final solution x, upper and lower bound for multiplier, final # objective function obj and the return status of ipopt x, zl, zu, obj, status, zg = nlp.solve(x0, m, userdata) info = {'x': x, 'zl': zl, 'zu': zu, 'obj': obj, 'status': status, 'lmbda': zg} nlp.close() success = (status == 0) | (status == 1) output = {'iterations': iter} f, _ = opf_costfcn(x, om) ## update solution data Va = x[vv['i1']['Va']:vv['iN']['Va']] Vm = x[vv['i1']['Vm']:vv['iN']['Vm']] Pg = x[vv['i1']['Pg']:vv['iN']['Pg']] Qg = x[vv['i1']['Qg']:vv['iN']['Qg']] V = Vm * exp(1j * Va) ##----- calculate return values ----- ## update voltages & generator outputs bus[:, VA] = Va * 180 / pi bus[:, VM] = Vm gen[:, PG] = Pg * baseMVA gen[:, QG] = Qg * baseMVA gen[:, VG] = Vm[gen[:, GEN_BUS].astype(int)] ## compute branch flows f_br = branch[:, F_BUS].astype(int) t_br = branch[:, T_BUS].astype(int) Sf = V[f_br] * conj(Yf * V) ## cplx pwr at "from" bus, p.u. St = V[t_br] * conj(Yt * V) ## cplx pwr at "to" bus, p.u. branch[:, PF] = Sf.real * baseMVA branch[:, QF] = Sf.imag * baseMVA branch[:, PT] = St.real * baseMVA branch[:, QT] = St.imag * baseMVA ## line constraint is actually on square of limit ## so we must fix multipliers muSf = zeros(nl) muSt = zeros(nl) if len(il) > 0: muSf[il] = 2 * info['lmbda'][2 * nb + arange(nl2)] * branch[il, RATE_A] / baseMVA muSt[il] = 2 * info['lmbda'][2 * nb + nl2 + arange(nl2)] * branch[il, RATE_A] / baseMVA ## update Lagrange multipliers bus[:, MU_VMAX] = info['zu'][vv['i1']['Vm']:vv['iN']['Vm']] bus[:, MU_VMIN] = info['zl'][vv['i1']['Vm']:vv['iN']['Vm']] gen[:, MU_PMAX] = info['zu'][vv['i1']['Pg']:vv['iN']['Pg']] / baseMVA gen[:, MU_PMIN] = info['zl'][vv['i1']['Pg']:vv['iN']['Pg']] / baseMVA gen[:, MU_QMAX] = info['zu'][vv['i1']['Qg']:vv['iN']['Qg']] / baseMVA gen[:, MU_QMIN] = info['zl'][vv['i1']['Qg']:vv['iN']['Qg']] / baseMVA bus[:, LAM_P] = info['lmbda'][nn['i1']['Pmis']:nn['iN']['Pmis']] / baseMVA bus[:, LAM_Q] = info['lmbda'][nn['i1']['Qmis']:nn['iN']['Qmis']] / baseMVA branch[:, MU_SF] = muSf / baseMVA branch[:, MU_ST] = muSt / baseMVA ## package up results nlnN = om.getN('nln') ## extract multipliers for nonlinear constraints kl = find(info['lmbda'][:2 * nb] < 0) ku = find(info['lmbda'][:2 * nb] > 0) nl_mu_l = zeros(nlnN) nl_mu_u = r_[zeros(2 * nb), muSf, muSt] nl_mu_l[kl] = -info['lmbda'][kl] nl_mu_u[ku] = info['lmbda'][ku] ## extract multipliers for linear constraints lam_lin = info['lmbda'][2 * nb + 2 * nl2 + arange(nA)] ## lmbda for linear constraints kl = find(lam_lin < 0) ## lower bound binding ku = find(lam_lin > 0) ## upper bound binding mu_l = zeros(nA) mu_l[kl] = -lam_lin[kl] mu_u = zeros(nA) mu_u[ku] = lam_lin[ku] mu = { 'var': {'l': info['zl'], 'u': info['zu']}, 'nln': {'l': nl_mu_l, 'u': nl_mu_u}, \ 'lin': {'l': mu_l, 'u': mu_u} } results = ppc results['bus'], results['branch'], results['gen'], \ results['om'], results['x'], results['mu'], results['f'] = \ bus, branch, gen, om, x, mu, f pimul = r_[ results['mu']['nln']['l'] - results['mu']['nln']['u'], results['mu']['lin']['l'] - results['mu']['lin']['u'], -ones(ny > 0), results['mu']['var']['l'] - results['mu']['var']['u'] ] raw = {'xr': x, 'pimul': pimul, 'info': info['status'], 'output': output} return results, success, raw
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 opf_execute(om, ppopt): """Executes the OPF specified by an OPF model object. C{results} are returned with internal indexing, all equipment in-service, etc. @see: L{opf}, L{opf_setup} @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln """ ##----- setup ----- ## options dc = ppopt['PF_DC'] ## 1 = DC OPF, 0 = AC OPF alg = ppopt['OPF_ALG'] verbose = ppopt['VERBOSE'] ## build user-defined costs om.build_cost_params() ## get indexing vv, ll, nn, _ = om.get_idx() if verbose > 0: v = ppver('all') stdout.write('PYPOWER Version %s, %s' % (v['Version'], v['Date'])) ##----- run DC OPF solver ----- if dc: if verbose > 0: stdout.write(' -- DC Optimal Power Flow\n') results, success, raw = dcopf_solver(om, ppopt) else: ##----- run AC OPF solver ----- if verbose > 0: stdout.write(' -- AC Optimal Power Flow\n') ## if OPF_ALG not set, choose best available option if alg == 0: alg = 560 ## MIPS ## update deprecated algorithm codes to new, generalized formulation equivalents if alg == 100 | alg == 200: ## CONSTR alg = 300 elif alg == 120 | alg == 220: ## dense LP alg = 320 elif alg == 140 | alg == 240: ## sparse (relaxed) LP alg = 340 elif alg == 160 | alg == 260: ## sparse (full) LP alg = 360 ppopt['OPF_ALG_POLY'] = alg ## run specific AC OPF solver if alg == 560 or alg == 565: ## PIPS results, success, raw = pipsopf_solver(om, ppopt) elif alg == 580: ## IPOPT # pragma: no cover try: __import__('pyipopt') results, success, raw = ipoptopf_solver(om, ppopt) except ImportError: raise ImportError('OPF_ALG %d requires IPOPT ' '(see https://projects.coin-or.org/Ipopt/)' % alg) else: stderr.write( 'opf_execute: OPF_ALG %d is not a valid algorithm code\n' % alg) if ('output' not in raw) or ('alg' not in raw['output']): raw['output']['alg'] = alg if success: if not dc: ## copy bus voltages back to gen matrix results['gen'][:, VG] = results['bus'][ results['gen'][:, GEN_BUS].astype(int), VM] ## gen PQ capability curve multipliers if (ll['N']['PQh'] > 0) | (ll['N']['PQl'] > 0): # pragma: no cover mu_PQh = results['mu']['lin']['l'][ ll['i1']['PQh']:ll['iN']['PQh']] - results['mu']['lin'][ 'u'][ll['i1']['PQh']:ll['iN']['PQh']] mu_PQl = results['mu']['lin']['l'][ ll['i1']['PQl']:ll['iN']['PQl']] - results['mu']['lin'][ 'u'][ll['i1']['PQl']:ll['iN']['PQl']] Apqdata = om.userdata('Apqdata') results['gen'] = update_mupq(results['baseMVA'], results['gen'], mu_PQh, mu_PQl, Apqdata) ## compute g, dg, f, df, d2f if requested by RETURN_RAW_DER = 1 if ppopt['RETURN_RAW_DER']: # pragma: no cover ## move from results to raw if using v4.0 of MINOPF or TSPOPF if 'dg' in results: raw = {} raw['dg'] = results['dg'] raw['g'] = results['g'] ## compute g, dg, unless already done by post-v4.0 MINOPF or TSPOPF if 'dg' not in raw: ppc = om.get_ppc() Ybus, Yf, Yt = makeYbus(ppc['baseMVA'], ppc['bus'], ppc['branch']) g, geq, dg, dgeq = opf_consfcn(results['x'], om, Ybus, Yf, Yt, ppopt) raw['g'] = r_[geq, g] raw['dg'] = r_[dgeq.T, dg.T] ## true Jacobian organization ## compute df, d2f _, df, d2f = opf_costfcn(results['x'], om, True) raw['df'] = df raw['d2f'] = d2f ## delete g and dg fieldsfrom results if using v4.0 of MINOPF or TSPOPF if 'dg' in results: del results['dg'] del results['g'] ## angle limit constraint multipliers if ll['N']['ang'] > 0: iang = om.userdata('iang') results['branch'][iang, MU_ANGMIN] = results['mu']['lin']['l'][ ll['i1']['ang']:ll['iN']['ang']] * pi / 180 results['branch'][iang, MU_ANGMAX] = results['mu']['lin']['u'][ ll['i1']['ang']:ll['iN']['ang']] * pi / 180 else: ## assign empty g, dg, f, df, d2f if requested by RETURN_RAW_DER = 1 if not dc and ppopt['RETURN_RAW_DER']: raw['dg'] = array([]) raw['g'] = array([]) raw['df'] = array([]) raw['d2f'] = array([]) ## assign values and limit shadow prices for variables if om.var['order']: results['var'] = {'val': {}, 'mu': {'l': {}, 'u': {}}} for name in om.var['order']: if om.getN('var', name): idx = arange(vv['i1'][name], vv['iN'][name]) results['var']['val'][name] = results['x'][idx] results['var']['mu']['l'][name] = results['mu']['var']['l'][idx] results['var']['mu']['u'][name] = results['mu']['var']['u'][idx] ## assign shadow prices for linear constraints if om.lin['order']: results['lin'] = {'mu': {'l': {}, 'u': {}}} for name in om.lin['order']: if om.getN('lin', name): idx = arange(ll['i1'][name], ll['iN'][name]) results['lin']['mu']['l'][name] = results['mu']['lin']['l'][idx] results['lin']['mu']['u'][name] = results['mu']['lin']['u'][idx] ## assign shadow prices for nonlinear constraints if not dc: if om.nln['order']: results['nln'] = {'mu': {'l': {}, 'u': {}}} for name in om.nln['order']: if om.getN('nln', name): idx = arange(nn['i1'][name], nn['iN'][name]) results['nln']['mu']['l'][name] = results['mu']['nln']['l'][ idx] results['nln']['mu']['u'][name] = results['mu']['nln']['u'][ idx] ## assign values for components of user cost if om.cost['order']: results['cost'] = {} for name in om.cost['order']: if om.getN('cost', name): results['cost'][name] = om.compute_cost(results['x'], name) ## if single-block PWL costs were converted to POLY, insert dummy y into x ## Note: The "y" portion of x will be nonsense, but everything should at ## least be in the expected locations. pwl1 = om.userdata('pwl1') if (len(pwl1) > 0) and (alg != 545) and (alg != 550): ## get indexing vv, _, _, _ = om.get_idx() if dc: nx = vv['iN']['Pg'] else: nx = vv['iN']['Qg'] y = zeros(len(pwl1)) raw['xr'] = r_[raw['xr'][:nx], y, raw['xr'][nx:]] results['x'] = r_[results['x'][:nx], y, results['x'][nx:]] return results, success, raw
def opf_execute(om, ppopt): """Executes the OPF specified by an OPF model object. C{results} are returned with internal indexing, all equipment in-service, etc. @see: L{opf}, L{opf_setup} @author: Ray Zimmerman (PSERC Cornell) """ ##----- setup ----- ## options dc = ppopt['PF_DC'] ## 1 = DC OPF, 0 = AC OPF alg = ppopt['OPF_ALG'] verbose = ppopt['VERBOSE'] ## build user-defined costs om.build_cost_params() ## get indexing vv, ll, nn, _ = om.get_idx() if verbose > 0: v = ppver('all') stdout.write('PYPOWER Version %s, %s' % (v['Version'], v['Date'])) ##----- run DC OPF solver ----- if dc: if verbose > 0: stdout.write(' -- DC Optimal Power Flow\n') results, success, raw = dcopf_solver(om, ppopt) else: ##----- run AC OPF solver ----- if verbose > 0: stdout.write(' -- AC Optimal Power Flow\n') ## if OPF_ALG not set, choose best available option if alg == 0: alg = 560 ## MIPS ## update deprecated algorithm codes to new, generalized formulation equivalents if alg == 100 | alg == 200: ## CONSTR alg = 300 elif alg == 120 | alg == 220: ## dense LP alg = 320 elif alg == 140 | alg == 240: ## sparse (relaxed) LP alg = 340 elif alg == 160 | alg == 260: ## sparse (full) LP alg = 360 ppopt['OPF_ALG_POLY'] = alg ## run specific AC OPF solver if alg == 560 or alg == 565: ## PIPS results, success, raw = pipsopf_solver(om, ppopt) elif alg == 580: ## IPOPT try: __import__('pyipopt') results, success, raw = ipoptopf_solver(om, ppopt) except ImportError: raise ImportError('OPF_ALG %d requires IPOPT ' '(see https://projects.coin-or.org/Ipopt/)' % alg) else: stderr.write('opf_execute: OPF_ALG %d is not a valid algorithm code\n' % alg) if ('output' not in raw) or ('alg' not in raw['output']): raw['output']['alg'] = alg if success: if not dc: ## copy bus voltages back to gen matrix results['gen'][:, VG] = results['bus'][results['gen'][:, GEN_BUS].astype(int), VM] ## gen PQ capability curve multipliers if (ll['N']['PQh'] > 0) | (ll['N']['PQl'] > 0): mu_PQh = results['mu']['lin']['l'][ll['i1']['PQh']:ll['iN']['PQh']] - results['mu']['lin']['u'][ll['i1']['PQh']:ll['iN']['PQh']] mu_PQl = results['mu']['lin']['l'][ll['i1']['PQl']:ll['iN']['PQl']] - results['mu']['lin']['u'][ll['i1']['PQl']:ll['iN']['PQl']] Apqdata = om.userdata('Apqdata') results['gen'] = update_mupq(results['baseMVA'], results['gen'], mu_PQh, mu_PQl, Apqdata) ## compute g, dg, f, df, d2f if requested by RETURN_RAW_DER = 1 if ppopt['RETURN_RAW_DER']: ## move from results to raw if using v4.0 of MINOPF or TSPOPF if 'dg' in results: raw = {} raw['dg'] = results['dg'] raw['g'] = results['g'] ## compute g, dg, unless already done by post-v4.0 MINOPF or TSPOPF if 'dg' not in raw: ppc = om.get_ppc() Ybus, Yf, Yt = makeYbus(ppc['baseMVA'], ppc['bus'], ppc['branch']) g, geq, dg, dgeq = opf_consfcn(results['x'], om, Ybus, Yf, Yt, ppopt) raw['g'] = r_[geq, g] raw['dg'] = r_[dgeq.T, dg.T] ## true Jacobian organization ## compute df, d2f _, df, d2f = opf_costfcn(results['x'], om, True) raw['df'] = df raw['d2f'] = d2f ## delete g and dg fieldsfrom results if using v4.0 of MINOPF or TSPOPF if 'dg' in results: del results['dg'] del results['g'] ## angle limit constraint multipliers if ll['N']['ang'] > 0: iang = om.userdata('iang') results['branch'][iang, MU_ANGMIN] = results['mu']['lin']['l'][ll['i1']['ang']:ll['iN']['ang']] * pi / 180 results['branch'][iang, MU_ANGMAX] = results['mu']['lin']['u'][ll['i1']['ang']:ll['iN']['ang']] * pi / 180 else: ## assign empty g, dg, f, df, d2f if requested by RETURN_RAW_DER = 1 if not dc and ppopt['RETURN_RAW_DER']: raw['dg'] = array([]) raw['g'] = array([]) raw['df'] = array([]) raw['d2f'] = array([]) ## assign values and limit shadow prices for variables if om.var['order']: results['var'] = {'val': {}, 'mu': {'l': {}, 'u': {}}} for name in om.var['order']: if om.getN('var', name): idx = arange(vv['i1'][name], vv['iN'][name]) results['var']['val'][name] = results['x'][idx] results['var']['mu']['l'][name] = results['mu']['var']['l'][idx] results['var']['mu']['u'][name] = results['mu']['var']['u'][idx] ## assign shadow prices for linear constraints if om.lin['order']: results['lin'] = {'mu': {'l': {}, 'u': {}}} for name in om.lin['order']: if om.getN('lin', name): idx = arange(ll['i1'][name], ll['iN'][name]) results['lin']['mu']['l'][name] = results['mu']['lin']['l'][idx] results['lin']['mu']['u'][name] = results['mu']['lin']['u'][idx] ## assign shadow prices for nonlinear constraints if not dc: if om.nln['order']: results['nln'] = {'mu': {'l': {}, 'u': {}}} for name in om.nln['order']: if om.getN('nln', name): idx = arange(nn['i1'][name], nn['iN'][name]) results['nln']['mu']['l'][name] = results['mu']['nln']['l'][idx] results['nln']['mu']['u'][name] = results['mu']['nln']['u'][idx] ## assign values for components of user cost if om.cost['order']: results['cost'] = {} for name in om.cost['order']: if om.getN('cost', name): results['cost'][name] = om.compute_cost(results['x'], name) ## if single-block PWL costs were converted to POLY, insert dummy y into x ## Note: The "y" portion of x will be nonsense, but everything should at ## least be in the expected locations. pwl1 = om.userdata('pwl1') if (len(pwl1) > 0) and (alg != 545) and (alg != 550): ## get indexing vv, _, _, _ = om.get_idx() if dc: nx = vv['iN']['Pg'] else: nx = vv['iN']['Qg'] y = zeros(len(pwl1)) raw['xr'] = r_[raw['xr'][:nx], y, raw['xr'][nx:]] results['x'] = r_[results['x'][:nx], y, results['x'][nx:]] return results, success, raw