def d2Ibr_dV2(Ybr, V, lam): """Computes 2nd derivatives of complex branch current w.r.t. voltage. Returns 4 matrices containing the partial derivatives w.r.t. voltage angle and magnitude of the product of a vector LAM with the 1st partial derivatives of the complex branch currents. Takes sparse branch admittance matrix C{Ybr}, voltage vector C{V} and C{nl x 1} vector of multipliers C{lam}. Output matrices are sparse. For more details on the derivations behind the derivative code used in PYPOWER information, see: [TN2] R. D. Zimmerman, I{"AC Power Flows, Generalized OPF Costs and their Derivatives using Complex Matrix Notation"}, MATPOWER Technical Note 2, February 2010. U{http://www.pserc.cornell.edu/matpower/TN2-OPF-Derivatives.pdf} @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln """ nb = len(V) ib = arange(nb) diaginvVm = sparse((ones(nb) / abs(V), (ib, ib))) Haa = sparse((-(Ybr.T * lam) * V, (ib, ib))) Hva = -1j * Haa * diaginvVm Hav = Hva.copy() Hvv = sparse((nb, nb)) return Haa, Hav, Hva, Hvv
def dSbus_dV(Ybus, V): """Computes partial derivatives of power injection w.r.t. voltage. Returns two matrices containing partial derivatives of the complex bus power injections w.r.t voltage magnitude and voltage angle respectively (for all buses). If C{Ybus} is a sparse matrix, the return values will be also. The following explains the expressions used to form the matrices:: S = diag(V) * conj(Ibus) = diag(conj(Ibus)) * V Partials of V & Ibus w.r.t. voltage magnitudes:: dV/dVm = diag(V / abs(V)) dI/dVm = Ybus * dV/dVm = Ybus * diag(V / abs(V)) Partials of V & Ibus w.r.t. voltage angles:: dV/dVa = j * diag(V) dI/dVa = Ybus * dV/dVa = Ybus * j * diag(V) Partials of S w.r.t. voltage magnitudes:: dS/dVm = diag(V) * conj(dI/dVm) + diag(conj(Ibus)) * dV/dVm = diag(V) * conj(Ybus * diag(V / abs(V))) + conj(diag(Ibus)) * diag(V / abs(V)) Partials of S w.r.t. voltage angles:: dS/dVa = diag(V) * conj(dI/dVa) + diag(conj(Ibus)) * dV/dVa = diag(V) * conj(Ybus * j * diag(V)) + conj(diag(Ibus)) * j * diag(V) = -j * diag(V) * conj(Ybus * diag(V)) + conj(diag(Ibus)) * j * diag(V) = j * diag(V) * conj(diag(Ibus) - Ybus * diag(V)) For more details on the derivations behind the derivative code used in PYPOWER information, see: [TN2] R. D. Zimmerman, "AC Power Flows, Generalized OPF Costs and their Derivatives using Complex Matrix Notation", MATPOWER Technical Note 2, February 2010. U{http://www.pserc.cornell.edu/matpower/TN2-OPF-Derivatives.pdf} @author: Ray Zimmerman (PSERC Cornell) """ ib = range(len(V)) if issparse(Ybus): Ibus = Ybus * V diagV = sparse((V, (ib, ib))) diagIbus = sparse((Ibus, (ib, ib))) diagVnorm = sparse((V / abs(V), (ib, ib))) else: Ibus = Ybus * asmatrix(V).T diagV = asmatrix(diag(V)) diagIbus = asmatrix(diag( asarray(Ibus).flatten() )) diagVnorm = asmatrix(diag(V / abs(V))) dS_dVm = diagV * conj(Ybus * diagVnorm) + conj(diagIbus) * diagVnorm dS_dVa = 1j * diagV * conj(diagIbus - Ybus * diagV) return dS_dVm, dS_dVa
def qp4d(h=False, pl=0, mi=50): """Constrained 4-d quadratic program from http://www.jmu.edu/docs/sasdoc/sashtml/iml/chap8/sect12.htm """ H = sparse([[1003.1, 4.3, 6.3, 5.9], [4.3, 2.2, 2.1, 3.9], [6.3, 2.1, 3.5, 4.8], [5.9, 3.9, 4.8, 10.0]]) c = zeros(4) A = sparse([[ 1, 1, 1, 1], [0.17, 0.11, 0.10, 0.18]]) l = array([1, 0.10]) u = array([1, Inf]) xmin = zeros(4) x0 = array([1, 0, 0, 1], float) x, zl, zu, obj, status, zg = pyipopt_qp(H, c, A, l, u, x0, xmin, None, hessian=h, print_level=pl, max_iter=mi) assert status == 0 # success assert_array_almost_equal(x, array([0, 2.8, 0.2, 0]) / 3) assert_almost_equal(obj, 3.29 / 3) assert_array_almost_equal(zl, [2.24, 0, 0, 1.7667], 4) assert_array_almost_equal(zu, zeros(x.shape), 13) # assert_array_almost_equal(zg[:, 0], array([6.58, 0]) / 3, 6) # assert_array_almost_equal(zg[:, 1], [0, 0], 13) print '4-d QP - success'
def hess6(x, lam, cost_mult=1): mu = lam['ineqnonlin'] Lxx = cost_mult * \ sparse([[ 0, -1, 0], [-1, 0, -1], [ 0, -1, 0]], dtype=float) + \ sparse([[2 * dot([1, 1], mu), 0, 0], [0, 2 * dot([-1, 1], mu), 0], [0, 0, 2 * dot([1, 1], mu)]], dtype=float) return Lxx
def dIbr_dV(branch, Yf, Yt, V): """Computes partial derivatives of branch currents w.r.t. voltage. Returns four matrices containing partial derivatives of the complex branch currents at "from" and "to" ends of each branch w.r.t voltage magnitude and voltage angle respectively (for all buses). If C{Yf} is a sparse matrix, the partial derivative matrices will be as well. Optionally returns vectors containing the currents themselves. The following explains the expressions used to form the matrices:: If = Yf * V Partials of V, Vf & If w.r.t. voltage angles:: dV/dVa = j * diag(V) dVf/dVa = sparse(range(nl), f, j*V(f)) = j * sparse(range(nl), f, V(f)) dIf/dVa = Yf * dV/dVa = Yf * j * diag(V) Partials of V, Vf & If w.r.t. voltage magnitudes:: dV/dVm = diag(V / abs(V)) dVf/dVm = sparse(range(nl), f, V(f) / abs(V(f)) dIf/dVm = Yf * dV/dVm = Yf * diag(V / abs(V)) Derivations for "to" bus are similar. @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln """ i = range(len(V)) Vnorm = V / abs(V) if issparse(Yf): diagV = sparse((V, (i, i))) diagVnorm = sparse((Vnorm, (i, i))) else: diagV = asmatrix( diag(V) ) diagVnorm = asmatrix( diag(Vnorm) ) dIf_dVa = Yf * 1j * diagV dIf_dVm = Yf * diagVnorm dIt_dVa = Yt * 1j * diagV dIt_dVm = Yt * diagVnorm # Compute currents. if issparse(Yf): If = Yf * V It = Yt * V else: If = asarray( Yf * asmatrix(V).T ).flatten() It = asarray( Yt * asmatrix(V).T ).flatten() return dIf_dVa, dIf_dVm, dIt_dVa, dIt_dVm, If, It
def lp3d(h=False, pl=0, mi=50): """3-d LP from linprog documentation. """ c = array([-5, -4, -6], float) A = sparse([[1, -1, 1], [3, 2, 4], [3, 2, 0]], dtype=float) l = None u = array([20, 42, 30], float) xmin = array([0, 0, 0], float) x0 = None x, zl, zu, obj, status, zg = pyipopt_lp(c, A, l, u, x0, xmin, None, hessian=h, print_level=pl, max_iter=mi) assert status == 0 # success assert_array_almost_equal(x, [0, 15, 3]) assert_almost_equal(obj, -78.0, 5) assert_array_almost_equal(zl, [1, 0, 0], 9) assert_array_almost_equal(zu, zeros(x.shape), 13) # assert_array_almost_equal(zg[:, 0], [0, 0, 0], 13) # assert_array_almost_equal(zg[:, 1], [0, 1.5, 0.5], 9) print '3-d LP - success'
def makeSbus(baseMVA, bus, gen): """Builds the vector of complex bus power injections. Returns the vector of complex bus power injections, that is, generation minus load. Power is expressed in per unit. @see: L{makeYbus} @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln """ ## generator info on = find(gen[:, GEN_STATUS] > 0) ## which generators are on? gbus = gen[on, GEN_BUS] ## what buses are they at? ## form net complex bus power injection vector nb = bus.shape[0] ngon = on.shape[0] ## connection matrix, element i, j is 1 if gen on(j) at bus i is ON Cg = sparse((ones(ngon), (gbus, range(ngon))), (nb, ngon)) ## power injected by gens plus power injected by loads converted to p.u. Sbus = ( Cg * (gen[on, PG] + 1j * gen[on, QG]) - (bus[:, PD] + 1j * bus[:, QD]) ) / baseMVA return Sbus
def makeLODF(branch, PTDF): """Builds the line outage distribution factor matrix. Returns the DC line outage distribution factor matrix for a given PTDF. The matrix is C{nbr x nbr}, where C{nbr} is the number of branches. Example:: H = makePTDF(baseMVA, bus, branch) LODF = makeLODF(branch, H) @see: L{makePTDF} @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln """ nl, nb = PTDF.shape f = branch[:, F_BUS] t = branch[:, T_BUS] Cft = sparse((r_[ones(nl), -ones(nl)], (r_[f, t], r_[arange(nl), arange(nl)])), (nb, nl)) H = PTDF * Cft h = diag(H, 0) LODF = H / (ones((nl, nl)) - ones((nl, 1)) * h.T) LODF = LODF - diag(diag(LODF)) - eye(nl, nl) return LODF
def bustypes(bus, gen): """Builds index lists of each type of bus (C{REF}, C{PV}, C{PQ}). Generators with "out-of-service" status are treated as L{PQ} buses with zero generation (regardless of C{Pg}/C{Qg} values in gen). Expects C{bus} and C{gen} have been converted to use internal consecutive bus numbering. @param bus: bus data @param gen: generator data @return: index lists of each bus type @author: Ray Zimmerman (PSERC Cornell) """ # get generator status nb = bus.shape[0] ng = gen.shape[0] # gen connection matrix, element i, j is 1 if, generator j at bus i is ON Cg = sparse((gen[:, GEN_STATUS] > 0, (gen[:, GEN_BUS], range(ng))), (nb, ng)) # number of generators at each bus that are ON bus_gen_status = (Cg * ones(ng, int)).astype(bool) # form index lists for slack, PV, and PQ buses ref = find((bus[:, BUS_TYPE] == REF) & bus_gen_status) # ref bus index pv = find((bus[:, BUS_TYPE] == PV) & bus_gen_status) # PV bus indices pq = find((bus[:, BUS_TYPE] == PQ) | ~bus_gen_status) # PQ bus indices # pick a new reference bus if for some reason there is none (may have been # shut down) if len(ref) == 0: ref = pv[0] # use the first PV bus pv = pv[1:] # take it off PV list return ref, pv, pq
def bustypes(bus, gen, Sbus): """ Builds index lists of each type of bus (C{REF}, C{PV}, C{PQ}). Generators with "out-of-service" status are treated as L{PQ} buses with zero generation (regardless of C{Pg}/C{Qg} values in gen). Expects C{bus} and C{gen} have been converted to use internal consecutive bus numbering. @param bus: bus data @param gen: generator data @return: index lists of each bus type @author: Ray Zimmerman (PSERC Cornell) """ # flag to indicate that it is impossible to solve the grid the_grid_is_disabled = False # get generator status nb = bus.shape[0] ng = gen.shape[0] # gen connection matrix, element i, j is 1 if, generator j at bus i is ON Cg = sparse((gen[:, GEN_STATUS] > 0, (gen[:, GEN_BUS], range(ng))), (nb, ng)) # number of generators at each bus that are ON bus_gen_status = (Cg * ones(ng, int)).astype(bool) # form index lists for slack, PV, and PQ buses ref = find((bus[:, BUS_TYPE] == REF) & bus_gen_status) # ref bus index pv = find((bus[:, BUS_TYPE] == PV) & bus_gen_status) # PV bus indices pq = find((bus[:, BUS_TYPE] == PQ) | ~bus_gen_status) # PQ bus indices # pick a new reference bus if for some reason there is none (may have been # shut down) if len(ref) == 0: if len(pv) > 0: ref = [pv[0]] # use the first PV bus pv = pv[1:] # take it off PV list else: # look for positive power injections to take the largest as the slack positive_power_injections = Sbus.real[where(Sbus.real > 0)[0]] if len(positive_power_injections) > 0: idx = where(Sbus.real == max(positive_power_injections))[0] if len(idx) == 1: ref = idx i = where(pq == idx[0])[0][0] pq = delete(pq, i) else: warn('It was not possible to find a slack bus') the_grid_is_disabled = True else: warn('It was not possible to find a slack bus') the_grid_is_disabled = True # create the types array types = zeros(nb) types[ref] = 3 types[pv] = 2 types[pq] = 1 return ref, pv, pq, types, the_grid_is_disabled
def userfcn_reserves_formulation(om, *args): """This is the 'formulation' stage userfcn callback that defines the user costs and constraints for fixed reserves. It expects to find a 'reserves' field in the ppc stored in om, as described above. By the time it is passed to this callback, ppc['reserves'] should have two additional fields: - C{igr} C{1 x ngr}, indices of generators available for reserves - C{rgens} C{1 x ng}, 1 if gen avaiable for reserves, 0 otherwise It is also assumed that if cost or qty were C{ngr x 1}, they have been expanded to C{ng x 1} and that everything has been converted to internal indexing, i.e. all gens are on-line (by the 'ext2int' callback). The optional args are not currently used. """ ## initialize some things ppc = om.get_ppc() r = ppc['reserves'] igr = r['igr'] ## indices of gens available to provide reserves ngr = len(igr) ## number of gens available to provide reserves ng = ppc['gen'].shape[0] ## number of on-line gens (+ disp loads) ## variable bounds Rmin = zeros(ngr) ## bound below by 0 Rmax = Inf * ones(ngr) ## bound above by ... k = find(ppc['gen'][igr, RAMP_10]) Rmax[k] = ppc['gen'][igr[k], RAMP_10] ## ... ramp rate and ... if 'qty' in r: k = find(r['qty'][igr] < Rmax) Rmax[k] = r['qty'][igr[k]] ## ... stated max reserve qty Rmax = Rmax / ppc['baseMVA'] ## constraints I = speye(ngr, ngr, format='csr') ## identity matrix Ar = hstack([sparse((ones(ngr), (arange(ngr), igr)), (ngr, ng)), I], 'csr') ur = ppc['gen'][igr, PMAX] / ppc['baseMVA'] lreq = r['req'] / ppc['baseMVA'] ## cost Cw = r['cost'][igr] * ppc['baseMVA'] ## per unit cost coefficients ## add them to the model om.add_vars('R', ngr, [], Rmin, Rmax) om.add_constraints('Pg_plus_R', Ar, [], ur, ['Pg', 'R']) om.add_constraints('Rreq', sparse( r['zones'][:, igr] ), lreq, [], ['R']) om.add_costs('Rcost', {'N': I, 'Cw': Cw}, ['R']) return om
def gh5(x): h = dot([[-1.0, -1.0], [ 1.0, 1.0]], x**2) + [1, -2] dh = 2 * sparse([[-x[0], x[0]], [-x[1], x[1]]]) g = array([]) dg = None return h, g, dh, dg
def f5(x, return_hessian=False): c = -array([1.0, 1.0]) f = dot(c, x) df = c if not return_hessian: return f, df d2f = sparse((2, 2)) return f, df, d2f
def gh6(x): h = dot([[1.0, -1.0, 1.0], [1.0, 1.0, 1.0]], x**2) + [-2.0, -10.0] dh = 2 * sparse([[ x[0], x[0]], [-x[1], x[1]], [ x[2], x[2]]], dtype=float) g = array([]) dg = None return h, g, dh, dg
def make_sparse(a, b, w, n, m): s = sparse((n,m), dtype=scipy.complex128) for i, value in enumerate(a): try: s[int(a[i]), int(b[i])] = w[i][0] except: s[int(a[i]), int(b[i])] = w[i] return s
def f6(x, return_hessian=False): f = -x[0] * x[1] - x[1] * x[2] df = -array([x[1], x[0] + x[2], x[1]]) if not return_hessian: return f, df d2f = -sparse([[0, 1, 0], [1, 0, 1], [0, 1, 0]], dtype=float) return f, df, d2f
def pre_process(n_bus, Yseries, Vset, pq, pv, vd): """ Make the Helm System matrix @param n_bus: Number of buses of the circuit @param Yseries: Circuit admittance matrix of the series elements @param Vset: Vector of voltages of those nodes where the voltage is controlled (AKA Slack and PV buses) @param pq: list of PQ node indices @param pv: list of PV node indices @param vd: list of Slack node indices @return: """ npq = len(pq) npv = len(pv) nvd = len(vd) npqpv = npq + npv # now to have efficient arrays of coefficients, we create the array maps map_pqpv = zeros(n_bus, dtype=np.int) map_w = zeros(n_bus, dtype=np.int) map_pqpv[pq] = array(range(npq)) map_pqpv[pv] = array(range(npv)) map_w[r_[pq, pv]] = array(range(npqpv)) # build the expanded system matrix Ysys = zeros((2*n_bus, 2*n_bus)) # Yseries is a CRC sparse matrix, I pass it to coordinates to be able to vectorize the m = Yseries.tocoo() a = m.row b = m.col Ysys[2 * a, 2 * b] = m.data.real Ysys[2 * a, 2 * b + 1] = -m.data.imag Ysys[2 * a + 1, 2 * b] = m.data.imag Ysys[2 * a + 1, 2 * b + 1] = m.data.real # set pv columns Ysys[:, 2 * pv] = zeros((2 * n_bus, npv)) Ysys[pv * 2 + 1, pv * 2] = ones(npv) # set vd elements Ysys[vd * 2, :] = zeros((nvd, 2 * n_bus)) Ysys[vd * 2 + 1, :] = zeros((nvd, 2 * n_bus)) Ysys[vd * 2, vd * 2] = ones(nvd) Ysys[vd * 2 + 1, vd * 2 + 1] = ones(nvd) # build the PV matrix Ysys_pv = zeros((2 * n_bus, npv)) for a, b in product(r_[pq, pv], pv): kk = map_pqpv[b] Ysys_pv[2 * a, kk] = Yseries[a, b].real Ysys_pv[2 * a + 1, kk] = Yseries[a, b].imag # compute the voltage squared array Vset2 = Vset * Vset return sparse(Ysys), Ysys_pv, Vset2, map_pqpv, map_w, npq, npv, nvd
def hess7(x, lam, sigma=1): lmbda = asscalar( lam['eqnonlin'] ) mu = asscalar( lam['ineqnonlin'] ) _, _, d2f = f7(x, True) Lxx = sigma * d2f + lmbda * 2 * speye(4, 4) - \ mu * sparse([ [ 0.0, x[2] * x[3], x[2] * x[3], x[1] * x[2]], [x[2] * x[2], 0.0, x[0] * x[3], x[0] * x[2]], [x[1] * x[3], x[0] * x[3], 0.0, x[0] * x[1]], [x[1] * x[2], x[0] * x[2], x[0] * x[1], 0.0] ]) return Lxx
def f3(x, return_hessian=False): H = sparse([ [ 5, -2, -1], [-2, 4, 3], [-1, 3, 5] ], dtype=float) c = array([2, -35, -47], float) f = 0.5 * dot(x * H, x) + dot(c, x) + 5 df = H * x + c if not return_hessian: return f, df d2f = H return f, df, d2f
def f4(x, return_hessian=False): H = sparse([ [1003.1, 4.3, 6.3, 5.9], [ 4.3, 2.2, 2.1, 3.9], [ 6.3, 2.1, 3.5, 4.8], [ 5.9, 3.9, 4.8, 10.0] ]) c = zeros(4) f = 0.5 * dot(x * H, x) + dot(c, x) df = H * x + c if not return_hessian: return f, df d2f = H return f, df, d2f
def d2AIbr_dV2(dIbr_dVa, dIbr_dVm, Ibr, Ybr, V, lam): """Computes 2nd derivatives of |complex current|**2 w.r.t. V. Returns 4 matrices containing the partial derivatives w.r.t. voltage angle and magnitude of the product of a vector C{lam} with the 1st partial derivatives of the square of the magnitude of the branch currents. Takes sparse first derivative matrices of complex flow, complex flow vector, sparse branch admittance matrix C{Ybr}, voltage vector C{V} and C{nl x 1} vector of multipliers C{lam}. Output matrices are sparse. For more details on the derivations behind the derivative code used in PYPOWER information, see: [TN2] R. D. Zimmerman, I{"AC Power Flows, Generalized OPF Costs and their Derivatives using Complex Matrix Notation"}, MATPOWER Technical Note 2, February 2010. U{http://www.pserc.cornell.edu/matpower/TN2-OPF-Derivatives.pdf} @see: L{dIbr_dV}. @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln """ # define il = range(len(lam)) diaglam = sparse((lam, (il, il))) diagIbr_conj = sparse((Ibr.conj(), (il, il))) Iaa, Iav, Iva, Ivv = d2Ibr_dV2(Ybr, V, diagIbr_conj * lam) Haa = 2 * ( Iaa + dIbr_dVa.T * diaglam * dIbr_dVa.conj() ).real Hva = 2 * ( Iva + dIbr_dVm.T * diaglam * dIbr_dVa.conj() ).real Hav = 2 * ( Iav + dIbr_dVa.T * diaglam * dIbr_dVm.conj() ).real Hvv = 2 * ( Ivv + dIbr_dVm.T * diaglam * dIbr_dVm.conj() ).real return Haa, Hav, Hva, Hvv
def f7(x, return_hessian=False): f = x[0] * x[3] * sum(x[:3]) + x[2] df = array([ x[0] * x[3] + x[3] * sum(x[:3]), x[0] * x[3], x[0] * x[3] + 1, x[0] * sum(x[:3]) ]) if not return_hessian: return f, df d2f = sparse([ [2 * x[3], x[3], x[3], 2 * x[0] + x[1] + x[2]], [ x[3], 0.0, 0.0, x[0]], [ x[3], 0.0, 0.0, x[0]], [2 * x[0] + x[1] + x[2], x[0], x[0], 0.0] ]) return f, df, d2f
def f2(x, return_hessian=False): a = 100 f = a * (x[1] - x[0]**2)**2 + (1 - x[0])**2 df = array([ 4 * a * (x[0]**3 - x[0] * x[1]) + 2 * x[0] - 2, 2 * a * (x[1] - x[0]**2) ]) if not return_hessian: return f, df d2f = 4 * a * sparse([ [3 * x[0]**2 - x[1] + 1. / (2 * a), -x[0]], [ -x[0], 0.5] ]) return f, df, d2f
def pyipopt_lp(c, A, gl, gu, x0, xl=None, xu=None, hessian=False, **kw_args): """IPOPT based quadratic program solver. Solves linear programs of the form: min c'x x subject to: gl <= Ax <= gu xl <= x <= xu """ n = c.shape[0] H = sparse((n, n)) return pyipopt_qp(H, c, A, gl, gu, x0, xl, xu, hessian, **kw_args)
def makeAang(baseMVA, branch, nb, ppopt): """Construct constraints for branch angle difference limits. Constructs the parameters for the following linear constraint limiting the voltage angle differences across branches, where C{Va} is the vector of bus voltage angles. C{nb} is the number of buses:: lang <= Aang * Va <= uang C{iang} is the vector of indices of branches with angle difference limits. @author: Ray Zimmerman (PSERC Cornell) @author: Carlos E. Murillo-Sanchez (PSERC Cornell & Universidad Autonoma de Manizales) @author: Richard Lincoln """ ## options ignore_ang_lim = ppopt['OPF_IGNORE_ANG_LIM'] if ignore_ang_lim: Aang = zeros((0, nb)) lang = array([]) uang = array([]) iang = array([]) else: iang = find(((branch[:, ANGMIN] != 0) & (branch[:, ANGMIN] > -360)) | ((branch[:, ANGMAX] != 0) & (branch[:, ANGMAX] < 360))) iangl = find(branch[iang, ANGMIN]) iangh = find(branch[iang, ANGMAX]) nang = len(iang) if nang > 0: ii = r_[arange(nang), arange(nang)] jj = r_[branch[iang, F_BUS], branch[iang, T_BUS]] Aang = sparse((r_[ones(nang), -ones(nang)], (ii, jj)), (nang, nb)) uang = Inf * ones(nang) lang = -uang lang[iangl] = branch[iang[iangl], ANGMIN] * pi / 180 uang[iangh] = branch[iang[iangh], ANGMAX] * pi / 180 else: Aang = zeros((0, nb)) lang = array([]) uang = array([]) return Aang, lang, uang, iang
def userfcn_iflims_formulation(om, *args): """This is the 'formulation' stage userfcn callback that defines the user costs and constraints for interface flow limits. It expects to find an 'if' field in the ppc stored in om, as described above. The optional args are not currently used. """ ## initialize some things ppc = om.get_ppc() baseMVA, bus, branch = ppc['baseMVA'], ppc['bus'], ppc['branch'] ifmap = ppc['if']['map'] iflims = ppc['if']['lims'] ## form B matrices for DC model _, Bf, _, Pfinj = makeBdc(baseMVA, bus, branch) n = Bf.shape[1] ## dim of theta ## form constraints ifidx = unique(iflims[:, 0]) ## interface number list nifs = len(ifidx) ## number of interfaces Aif = lil_matrix((nifs, n)) lif = zeros(nifs) uif = zeros(nifs) for k in range(nifs): ## extract branch indices br = ifmap[ifmap[:, 0] == ifidx[k], 1] if len(br) == 0: stderr.write('userfcn_iflims_formulation: interface %d has no in-service branches\n' % k) d = sign(br) br = abs(br) Ak = sparse((1, n)) ## Ak = sum( d(i) * Bf(i, :) ) bk = 0 ## bk = sum( d(i) * Pfinj(i) ) for i in range(len(br)): Ak = Ak + d[i] * Bf[br[i], :] bk = bk + d[i] * Pfinj[br[i]] Aif[k, :] = Ak lif[k] = iflims[k, 1] / baseMVA - bk uif[k] = iflims[k, 2] / baseMVA - bk ## add interface constraint om.add_constraints('iflims', Aif, lif, uif, ['Va']) ## nifs return om
def userfcn_dcline_formulation(om, args): """This is the 'formulation' stage userfcn callback that defines the user constraints for the dummy generators representing DC lines. It expects to find a 'dcline' field in the ppc stored in om, as described above. By the time it is passed to this callback, MPC.dcline should contain only in-service lines and the from and two bus columns should be converted to internal indexing. The optional args are not currently used. If Pf, Pt and Ploss are the flow at the "from" end, flow at the "to" end and loss respectively, and L0 and L1 are the linear loss coefficients, the the relationships between them is given by: Pf - Ploss = Pt Ploss = L0 + L1 * Pf If Pgf and Pgt represent the injections of the dummy generators representing the DC line injections into the network, then Pgf = -Pf and Pgt = Pt, and we can combine all of the above to get the following constraint on Pgf ang Pgt: -Pgf - (L0 - L1 * Pgf) = Pgt which can be written: -L0 <= (1 - L1) * Pgf + Pgt <= -L0 """ ## define named indices into data matrices c = idx_dcline.c ## initialize some things ppc = om.get_ppc() dc = ppc['dcline'] ndc = dc.shape[0] ## number of in-service DC lines ng = ppc['gen'].shape[0] - 2 * ndc ## number of original gens/disp loads ## constraints #nL0 = -dc[:, c['LOSS0']] / ppc.baseMVA nL0 = -dc[:, c['LOSS0']] / ppc['baseMVA'] L1 = dc[:, c['LOSS1']] Adc = hstack([sparse((ndc, ng)), spdiags(1-L1, 0, ndc, ndc), speye(ndc, ndc)], format="csr") #Adc = c_[zeros(ndc, ng), spdiags(1-L1, 0, ndc, ndc), speye(ndc, ndc)] #print("Adc", Adc.todense()) ## add them to the model om = om.add_constraints('dcline', Adc, nL0, nL0, ['Pg']) return om
def d2Sbus_dV2(Ybus, V, lam): """Computes 2nd derivatives of power injection w.r.t. voltage. Returns 4 matrices containing the partial derivatives w.r.t. voltage angle and magnitude of the product of a vector C{lam} with the 1st partial derivatives of the complex bus power injections. Takes sparse bus admittance matrix C{Ybus}, voltage vector C{V} and C{nb x 1} vector of multipliers C{lam}. Output matrices are sparse. For more details on the derivations behind the derivative code used in PYPOWER information, see: [TN2] R. D. Zimmerman, I{"AC Power Flows, Generalized OPF Costs and their Derivatives using Complex Matrix Notation"}, MATPOWER Technical Note 2, February 2010. U{http://www.pserc.cornell.edu/matpower/TN2-OPF-Derivatives.pdf} @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln """ nb = len(V) ib = arange(nb) Ibus = Ybus * V diaglam = sparse((lam, (ib, ib))) diagV = sparse((V, (ib, ib))) A = sparse((lam * V, (ib, ib))) B = Ybus * diagV C = A * conj(B) D = Ybus.H * diagV E = diagV.conj() * (D * diaglam - sparse((D * lam, (ib, ib)))) F = C - A * sparse((conj(Ibus), (ib, ib))) G = sparse((ones(nb) / abs(V), (ib, ib))) Gaa = E + F Gva = 1j * G * (E - F) Gav = Gva.T Gvv = G * (C + C.T) * G return Gaa, Gav, Gva, Gvv
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 t_opf_pips(quiet=False): """Tests for PIPS-based AC optimal power flow. @author: Ray Zimmerman (PSERC Cornell) """ num_tests = 101 t_begin(num_tests, quiet) tdir = dirname(__file__) casefile = join(tdir, 't_case9_opf') verbose = 0#not quiet t0 = 'PIPS : ' ppopt = ppoption(OPF_VIOLATION=1e-6, PDIPM_GRADTOL=1e-8, PDIPM_COMPTOL=1e-8, PDIPM_COSTTOL=1e-9) ppopt = ppoption(ppopt, OUT_ALL=0, VERBOSE=verbose, OPF_ALG=560) ## set up indices ib_data = r_[arange(BUS_AREA + 1), arange(BASE_KV, VMIN + 1)] ib_voltage = arange(VM, VA + 1) ib_lam = arange(LAM_P, LAM_Q + 1) ib_mu = arange(MU_VMAX, MU_VMIN + 1) ig_data = r_[[GEN_BUS, QMAX, QMIN], arange(MBASE, APF + 1)] ig_disp = array([PG, QG, VG]) ig_mu = arange(MU_PMAX, MU_QMIN + 1) ibr_data = arange(ANGMAX + 1) ibr_flow = arange(PF, QT + 1) ibr_mu = array([MU_SF, MU_ST]) ibr_angmu = array([MU_ANGMIN, MU_ANGMAX]) ## get solved AC power flow case from MAT-file soln9_opf = loadmat(join(tdir, 'soln9_opf.mat'), struct_as_record=True) ## defines bus_soln, gen_soln, branch_soln, f_soln bus_soln = soln9_opf['bus_soln'] gen_soln = soln9_opf['gen_soln'] branch_soln = soln9_opf['branch_soln'] f_soln = soln9_opf['f_soln'][0] ## run OPF t = t0 r = runopf(casefile, ppopt) bus, gen, branch, f, success = \ r['bus'], r['gen'], r['branch'], r['f'], r['success'] t_ok(success, [t, 'success']) t_is(f, f_soln, 3, [t, 'f']) t_is( bus[:, ib_data ], bus_soln[:, ib_data ], 10, [t, 'bus data']) t_is( bus[:, ib_voltage], bus_soln[:, ib_voltage], 3, [t, 'bus voltage']) t_is( bus[:, ib_lam ], bus_soln[:, ib_lam ], 3, [t, 'bus lambda']) t_is( bus[:, ib_mu ], bus_soln[:, ib_mu ], 2, [t, 'bus mu']) t_is( gen[:, ig_data ], gen_soln[:, ig_data ], 10, [t, 'gen data']) t_is( gen[:, ig_disp ], gen_soln[:, ig_disp ], 3, [t, 'gen dispatch']) t_is( gen[:, ig_mu ], gen_soln[:, ig_mu ], 3, [t, 'gen mu']) t_is(branch[:, ibr_data ], branch_soln[:, ibr_data ], 10, [t, 'branch data']) t_is(branch[:, ibr_flow ], branch_soln[:, ibr_flow ], 3, [t, 'branch flow']) t_is(branch[:, ibr_mu ], branch_soln[:, ibr_mu ], 2, [t, 'branch mu']) ## run with automatic conversion of single-block pwl to linear costs t = ''.join([t0, '(single-block PWL) : ']) ppc = loadcase(casefile) ppc['gencost'][2, NCOST] = 2 r = runopf(ppc, ppopt) bus, gen, branch, f, success = \ r['bus'], r['gen'], r['branch'], r['f'], r['success'] t_ok(success, [t, 'success']) t_is(f, f_soln, 3, [t, 'f']) t_is( bus[:, ib_data ], bus_soln[:, ib_data ], 10, [t, 'bus data']) t_is( bus[:, ib_voltage], bus_soln[:, ib_voltage], 3, [t, 'bus voltage']) t_is( bus[:, ib_lam ], bus_soln[:, ib_lam ], 3, [t, 'bus lambda']) t_is( bus[:, ib_mu ], bus_soln[:, ib_mu ], 2, [t, 'bus mu']) t_is( gen[:, ig_data ], gen_soln[:, ig_data ], 10, [t, 'gen data']) t_is( gen[:, ig_disp ], gen_soln[:, ig_disp ], 3, [t, 'gen dispatch']) t_is( gen[:, ig_mu ], gen_soln[:, ig_mu ], 3, [t, 'gen mu']) t_is(branch[:, ibr_data ], branch_soln[:, ibr_data ], 10, [t, 'branch data']) t_is(branch[:, ibr_flow ], branch_soln[:, ibr_flow ], 3, [t, 'branch flow']) t_is(branch[:, ibr_mu ], branch_soln[:, ibr_mu ], 2, [t, 'branch mu']) xr = r_[r['var']['val']['Va'], r['var']['val']['Vm'], r['var']['val']['Pg'], r['var']['val']['Qg'], 0, r['var']['val']['y']] t_is(r['x'], xr, 8, [t, 'check on raw x returned from OPF']) ## get solved AC power flow case from MAT-file soln9_opf_Plim = loadmat(join(tdir, 'soln9_opf_Plim.mat'), struct_as_record=True) ## defines bus_soln, gen_soln, branch_soln, f_soln bus_soln = soln9_opf_Plim['bus_soln'] gen_soln = soln9_opf_Plim['gen_soln'] branch_soln = soln9_opf_Plim['branch_soln'] f_soln = soln9_opf_Plim['f_soln'][0] ## run OPF with active power line limits t = ''.join([t0, '(P line lim) : ']) ppopt1 = ppoption(ppopt, OPF_FLOW_LIM=1) r = runopf(casefile, ppopt1) bus, gen, branch, f, success = \ r['bus'], r['gen'], r['branch'], r['f'], r['success'] t_ok(success, [t, 'success']) t_is(f, f_soln, 3, [t, 'f']) t_is( bus[:, ib_data ], bus_soln[:, ib_data ], 10, [t, 'bus data']) t_is( bus[:, ib_voltage], bus_soln[:, ib_voltage], 3, [t, 'bus voltage']) t_is( bus[:, ib_lam ], bus_soln[:, ib_lam ], 3, [t, 'bus lambda']) t_is( bus[:, ib_mu ], bus_soln[:, ib_mu ], 2, [t, 'bus mu']) t_is( gen[:, ig_data ], gen_soln[:, ig_data ], 10, [t, 'gen data']) t_is( gen[:, ig_disp ], gen_soln[:, ig_disp ], 3, [t, 'gen dispatch']) t_is( gen[:, ig_mu ], gen_soln[:, ig_mu ], 3, [t, 'gen mu']) t_is(branch[:, ibr_data ], branch_soln[:, ibr_data ], 10, [t, 'branch data']) t_is(branch[:, ibr_flow ], branch_soln[:, ibr_flow ], 3, [t, 'branch flow']) t_is(branch[:, ibr_mu ], branch_soln[:, ibr_mu ], 2, [t, 'branch mu']) ##----- test OPF with quadratic gen costs moved to generalized costs ----- ppc = loadcase(casefile) ppc['gencost'] = array([ [2, 1500, 0, 3, 0.11, 5, 0], [2, 2000, 0, 3, 0.085, 1.2, 0], [2, 3000, 0, 3, 0.1225, 1, 0] ]) r = runopf(ppc, ppopt) bus_soln, gen_soln, branch_soln, f_soln, success = \ r['bus'], r['gen'], r['branch'], r['f'], r['success'] branch_soln = branch_soln[:, :MU_ST + 1] A = None l = array([]) u = array([]) nb = ppc['bus'].shape[0] # number of buses ng = ppc['gen'].shape[0] # number of gens thbas = 0; thend = thbas + nb vbas = thend; vend = vbas + nb pgbas = vend; pgend = pgbas + ng # qgbas = pgend; qgend = qgbas + ng nxyz = 2 * nb + 2 * ng N = sparse((ppc['baseMVA'] * ones(ng), (arange(ng), arange(pgbas, pgend))), (ng, nxyz)) fparm = ones((ng, 1)) * array([[1, 0, 0, 1]]) ix = argsort(ppc['gen'][:, 0]) H = 2 * spdiags(ppc['gencost'][ix, 4], 0, ng, ng, 'csr') Cw = ppc['gencost'][ix, 5] ppc['gencost'][:, 4:7] = 0 ## run OPF with quadratic gen costs moved to generalized costs t = ''.join([t0, 'w/quadratic generalized gen cost : ']) r = opf(ppc, A, l, u, ppopt, N, fparm, H, Cw) f, bus, gen, branch, success = \ r['f'], r['bus'], r['gen'], r['branch'], r['success'] t_ok(success, [t, 'success']) t_is(f, f_soln, 3, [t, 'f']) t_is( bus[:, ib_data ], bus_soln[:, ib_data ], 10, [t, 'bus data']) t_is( bus[:, ib_voltage], bus_soln[:, ib_voltage], 3, [t, 'bus voltage']) t_is( bus[:, ib_lam ], bus_soln[:, ib_lam ], 3, [t, 'bus lambda']) t_is( bus[:, ib_mu ], bus_soln[:, ib_mu ], 2, [t, 'bus mu']) t_is( gen[:, ig_data ], gen_soln[:, ig_data ], 10, [t, 'gen data']) t_is( gen[:, ig_disp ], gen_soln[:, ig_disp ], 3, [t, 'gen dispatch']) t_is( gen[:, ig_mu ], gen_soln[:, ig_mu ], 3, [t, 'gen mu']) t_is(branch[:, ibr_data ], branch_soln[:, ibr_data ], 10, [t, 'branch data']) t_is(branch[:, ibr_flow ], branch_soln[:, ibr_flow ], 3, [t, 'branch flow']) t_is(branch[:, ibr_mu ], branch_soln[:, ibr_mu ], 2, [t, 'branch mu']) t_is(r['cost']['usr'], f, 12, [t, 'user cost']) ##----- run OPF with extra linear user constraints & costs ----- ## single new z variable constrained to be greater than or equal to ## deviation from 1 pu voltage at bus 1, linear cost on this z ## get solved AC power flow case from MAT-file soln9_opf_extras1 = loadmat(join(tdir, 'soln9_opf_extras1.mat'), struct_as_record=True) ## defines bus_soln, gen_soln, branch_soln, f_soln bus_soln = soln9_opf_extras1['bus_soln'] gen_soln = soln9_opf_extras1['gen_soln'] branch_soln = soln9_opf_extras1['branch_soln'] f_soln = soln9_opf_extras1['f_soln'][0] row = [0, 0, 1, 1] col = [9, 24, 9, 24] A = sparse(([-1, 1, 1, 1], (row, col)), (2, 25)) u = array([Inf, Inf]) l = array([-1, 1]) N = sparse(([1], ([0], [24])), (1, 25)) ## new z variable only fparm = array([[1, 0, 0, 1]]) ## w = r = z H = sparse((1, 1)) ## no quadratic term Cw = array([100.0]) t = ''.join([t0, 'w/extra constraints & costs 1 : ']) r = opf(casefile, A, l, u, ppopt, N, fparm, H, Cw) f, bus, gen, branch, success = \ r['f'], r['bus'], r['gen'], r['branch'], r['success'] t_ok(success, [t, 'success']) t_is(f, f_soln, 3, [t, 'f']) t_is( bus[:, ib_data ], bus_soln[:, ib_data ], 10, [t, 'bus data']) t_is( bus[:, ib_voltage], bus_soln[:, ib_voltage], 3, [t, 'bus voltage']) t_is( bus[:, ib_lam ], bus_soln[:, ib_lam ], 3, [t, 'bus lambda']) t_is( bus[:, ib_mu ], bus_soln[:, ib_mu ], 2, [t, 'bus mu']) t_is( gen[:, ig_data ], gen_soln[:, ig_data ], 10, [t, 'gen data']) t_is( gen[:, ig_disp ], gen_soln[:, ig_disp ], 3, [t, 'gen dispatch']) t_is( gen[:, ig_mu ], gen_soln[:, ig_mu ], 3, [t, 'gen mu']) t_is(branch[:, ibr_data ], branch_soln[:, ibr_data ], 10, [t, 'branch data']) t_is(branch[:, ibr_flow ], branch_soln[:, ibr_flow ], 3, [t, 'branch flow']) t_is(branch[:, ibr_mu ], branch_soln[:, ibr_mu ], 2, [t, 'branch mu']) t_is(r['var']['val']['z'], 0.025419, 6, [t, 'user variable']) t_is(r['cost']['usr'], 2.5419, 4, [t, 'user cost']) ##----- test OPF with capability curves ----- ppc = loadcase(join(tdir, 't_case9_opfv2')) ## remove angle diff limits ppc['branch'][0, ANGMAX] = 360 ppc['branch'][8, ANGMIN] = -360 ## get solved AC power flow case from MAT-file soln9_opf_PQcap = loadmat(join(tdir, 'soln9_opf_PQcap.mat'), struct_as_record=True) ## defines bus_soln, gen_soln, branch_soln, f_soln bus_soln = soln9_opf_PQcap['bus_soln'] gen_soln = soln9_opf_PQcap['gen_soln'] branch_soln = soln9_opf_PQcap['branch_soln'] f_soln = soln9_opf_PQcap['f_soln'][0] ## run OPF with capability curves t = ''.join([t0, 'w/capability curves : ']) r = runopf(ppc, ppopt) bus, gen, branch, f, success = \ r['bus'], r['gen'], r['branch'], r['f'], r['success'] t_ok(success, [t, 'success']) t_is(f, f_soln, 3, [t, 'f']) t_is( bus[:, ib_data ], bus_soln[:, ib_data ], 10, [t, 'bus data']) t_is( bus[:, ib_voltage], bus_soln[:, ib_voltage], 3, [t, 'bus voltage']) t_is( bus[:, ib_lam ], bus_soln[:, ib_lam ], 3, [t, 'bus lambda']) t_is( bus[:, ib_mu ], bus_soln[:, ib_mu ], 2, [t, 'bus mu']) t_is( gen[:, ig_data ], gen_soln[:, ig_data ], 10, [t, 'gen data']) t_is( gen[:, ig_disp ], gen_soln[:, ig_disp ], 3, [t, 'gen dispatch']) t_is( gen[:, ig_mu ], gen_soln[:, ig_mu ], 3, [t, 'gen mu']) t_is(branch[:, ibr_data ], branch_soln[:, ibr_data ], 10, [t, 'branch data']) t_is(branch[:, ibr_flow ], branch_soln[:, ibr_flow ], 3, [t, 'branch flow']) t_is(branch[:, ibr_mu ], branch_soln[:, ibr_mu ], 2, [t, 'branch mu']) ##----- test OPF with angle difference limits ----- ppc = loadcase(join(tdir, 't_case9_opfv2')) ## remove capability curves ppc['gen'][ix_(arange(1, 3), [PC1, PC2, QC1MIN, QC1MAX, QC2MIN, QC2MAX])] = zeros((2, 6)) ## get solved AC power flow case from MAT-file soln9_opf_ang = loadmat(join(tdir, 'soln9_opf_ang.mat'), struct_as_record=True) ## defines bus_soln, gen_soln, branch_soln, f_soln bus_soln = soln9_opf_ang['bus_soln'] gen_soln = soln9_opf_ang['gen_soln'] branch_soln = soln9_opf_ang['branch_soln'] f_soln = soln9_opf_ang['f_soln'][0] ## run OPF with angle difference limits t = ''.join([t0, 'w/angle difference limits : ']) r = runopf(ppc, ppopt) bus, gen, branch, f, success = \ r['bus'], r['gen'], r['branch'], r['f'], r['success'] t_ok(success, [t, 'success']) t_is(f, f_soln, 3, [t, 'f']) t_is( bus[:, ib_data ], bus_soln[:, ib_data ], 10, [t, 'bus data']) t_is( bus[:, ib_voltage], bus_soln[:, ib_voltage], 3, [t, 'bus voltage']) t_is( bus[:, ib_lam ], bus_soln[:, ib_lam ], 3, [t, 'bus lambda']) t_is( bus[:, ib_mu ], bus_soln[:, ib_mu ], 1, [t, 'bus mu']) t_is( gen[:, ig_data ], gen_soln[:, ig_data ], 10, [t, 'gen data']) t_is( gen[:, ig_disp ], gen_soln[:, ig_disp ], 3, [t, 'gen dispatch']) t_is( gen[:, ig_mu ], gen_soln[:, ig_mu ], 3, [t, 'gen mu']) t_is(branch[:, ibr_data ], branch_soln[:, ibr_data ], 10, [t, 'branch data']) t_is(branch[:, ibr_flow ], branch_soln[:, ibr_flow ], 3, [t, 'branch flow']) t_is(branch[:, ibr_mu ], branch_soln[:, ibr_mu ], 2, [t, 'branch mu']) t_is(branch[:, ibr_angmu ], branch_soln[:, ibr_angmu ], 2, [t, 'branch angle mu']) ##----- test OPF with ignored angle difference limits ----- ## get solved AC power flow case from MAT-file soln9_opf = loadmat(join(tdir, 'soln9_opf.mat'), struct_as_record=True) ## defines bus_soln, gen_soln, branch_soln, f_soln bus_soln = soln9_opf['bus_soln'] gen_soln = soln9_opf['gen_soln'] branch_soln = soln9_opf['branch_soln'] f_soln = soln9_opf['f_soln'][0] ## run OPF with ignored angle difference limits t = ''.join([t0, 'w/ignored angle difference limits : ']) ppopt1 = ppoption(ppopt, OPF_IGNORE_ANG_LIM=1) r = runopf(ppc, ppopt1) bus, gen, branch, f, success = \ r['bus'], r['gen'], r['branch'], r['f'], r['success'] ## ang limits are not in this solution data, so let's remove them branch[0, ANGMAX] = 360 branch[8, ANGMIN] = -360 t_ok(success, [t, 'success']) t_is(f, f_soln, 3, [t, 'f']) t_is( bus[:, ib_data ], bus_soln[:, ib_data ], 10, [t, 'bus data']) t_is( bus[:, ib_voltage], bus_soln[:, ib_voltage], 3, [t, 'bus voltage']) t_is( bus[:, ib_lam ], bus_soln[:, ib_lam ], 3, [t, 'bus lambda']) t_is( bus[:, ib_mu ], bus_soln[:, ib_mu ], 2, [t, 'bus mu']) t_is( gen[:, ig_data ], gen_soln[:, ig_data ], 10, [t, 'gen data']) t_is( gen[:, ig_disp ], gen_soln[:, ig_disp ], 3, [t, 'gen dispatch']) t_is( gen[:, ig_mu ], gen_soln[:, ig_mu ], 3, [t, 'gen mu']) t_is(branch[:, ibr_data ], branch_soln[:, ibr_data ], 10, [t, 'branch data']) t_is(branch[:, ibr_flow ], branch_soln[:, ibr_flow ], 3, [t, 'branch flow']) t_is(branch[:, ibr_mu ], branch_soln[:, ibr_mu ], 2, [t, 'branch mu']) t_end()
def run_inversion(home, project_name, run_name, source_name, model_name, GF_list, G_from_file, G_name, reg_spatial, nsources, solver, forceMT, mt, weight=False, Ltype=0): ''' Assemble G and d, determine smoothing and run the inversion ''' from mudpy import inverse as inv from mudpy.forward import get_mu_and_area from numpy import zeros, dot, array, squeeze, expand_dims, empty, tile, eye, ones, arange, load from numpy.linalg import lstsq from scipy.sparse import csr_matrix as sparse from scipy.optimize import nnls from datetime import datetime import gc t1 = datetime.now() #Get data vector d = inv.getdata(home, project_name, GF_list, decimate=None, bandpass=None) #Get GFs G = getG(home, project_name, source_name, model_name, GF_list, G_from_file, G_name, forceMT) #Get data weights if weight == True: print 'Applying data weights' w = inv.get_data_weights(home, project_name, GF_list, d, None) W = empty(G.shape) W = tile(w, (G.shape[1], 1)).T WG = empty(G.shape) WG = W * G wd = w * d.squeeze() wd = expand_dims(wd, axis=1) #Clear up extraneous variables W = None w = None #Define inversion quantities x = WG.transpose().dot(wd) print 'Computing G\'G' K = (WG.T).dot(WG) #And cleanup W = None WG = None wd = None else: #Define inversion quantities if no weightd x = G.transpose().dot(d) print 'Computing G\'G' K = (G.T).dot(G) #Cleanup #G=None #Get regularization matrices (set to 0 matrix if not needed) if forceMT == False: Ls = eye(nsources * 6) else: Ls = eye(nsources) print 'Nsources: ' + str(nsources) Ninversion = len(reg_spatial) #Make L's sparse Ls = sparse(Ls) #Get regularization tranposes for ABIC LsLs = Ls.transpose().dot(Ls) #inflate Ls = Ls.todense() LsLs = LsLs.todense() #off we go dt = datetime.now() - t1 print 'Preprocessing wall time was ' + str(dt) print '\n--- RUNNING INVERSIONS ---\n' ttotal = datetime.now() kout = 0 for ks in range(len(reg_spatial)): t1 = datetime.now() lambda_spatial = reg_spatial[ks] print 'Running inversion ' + str(kout + 1) + ' of ' + str( Ninversion) + ' at regularization levels: ls =' + repr( lambda_spatial) Kinv = K + (lambda_spatial**2) * LsLs if solver == 'lstsq': sol, res, rank, s = lstsq(Kinv, x) elif solver == 'nnls': sol, res = nnls(Kinv, squeeze(x.T)) #Compute synthetics ds = dot(G, sol) #Get stats L2, Lmodel = inv.get_stats(Kinv, sol, x) VR = inv.get_VR(home, project_name, GF_list, sol, d, ds, None) #Get moment M0total, M0, Mw = get_moment(home, project_name, source_name, sol, forceMT) #Write log write_log(home, project_name, run_name, kout, lambda_spatial, L2, Lmodel, VR, M0total, Mw, model_name, source_name, G_name, GF_list) #Write output to file inv.write_synthetics(home, project_name, run_name, GF_list, G, sol, ds, kout, None) write_model(home, project_name, run_name, source_name, model_name, sol, kout, forceMT, mt) kout += 1 dt1 = datetime.now() - t1 dt2 = datetime.now() - ttotal print '... inversion wall time was ' + str( dt1) + ', total wall time elapsed is ' + str(dt2)
def makeBdc(baseMVA, bus, branch): """Builds the B matrices and phase shift injections for DC power flow. Returns the B matrices and phase shift injection vectors needed for a DC power flow. The bus real power injections are related to bus voltage angles by:: P = Bbus * Va + PBusinj The real power flows at the from end the lines are related to the bus voltage angles by:: Pf = Bf * Va + Pfinj Does appropriate conversions to p.u. @see: L{dcpf} @author: Carlos E. Murillo-Sanchez (PSERC Cornell & Universidad Autonoma de Manizales) @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln """ ## constants nb = bus.shape[0] ## number of buses nl = branch.shape[0] ## number of lines ## check that bus numbers are equal to indices to bus (one set of bus nums) if any(bus[:, BUS_I] != range(nb)): stderr.write('makeBdc: buses must be numbered consecutively in ' 'bus matrix\n') ## for each branch, compute the elements of the branch B matrix and the phase ## shift "quiescent" injections, where ## ## | Pf | | Bff Bft | | Vaf | | Pfinj | ## | | = | | * | | + | | ## | Pt | | Btf Btt | | Vat | | Ptinj | ## stat = branch[:, BR_STATUS] ## ones at in-service branches b = stat / branch[:, BR_X] ## series susceptance tap = ones(nl) ## default tap ratio = 1 i = find(branch[:, TAP]) ## indices of non-zero tap ratios tap[i] = branch[i, TAP] ## assign non-zero tap ratios b = b / tap ## build connection matrix Cft = Cf - Ct for line and from - to buses f = branch[:, F_BUS] ## list of "from" buses t = branch[:, T_BUS] ## list of "to" buses i = r_[range(nl), range(nl)] ## double set of row indices ## connection matrix Cft = sparse((r_[ones(nl), -ones(nl)], (i, r_[f, t])), (nl, nb)) ## build Bf such that Bf * Va is the vector of real branch powers injected ## at each branch's "from" bus Bf = sparse((r_[b, -b], (i, r_[f, t])))## = spdiags(b, 0, nl, nl) * Cft ## build Bbus Bbus = Cft.T * Bf ## build phase shift injection vectors Pfinj = b * (-branch[:, SHIFT] * pi / 180) ## injected at the from bus ... # Ptinj = -Pfinj ## and extracted at the to bus Pbusinj = Cft.T * Pfinj ## Pbusinj = Cf * Pfinj + Ct * Ptinj return Bbus, Bf, Pbusinj, Pfinj
def newtonpf(Ybus, Sbus, V0, pv, pq, options, numba): """Solves the power flow using a full Newton's method. Solves for bus voltages given the full system admittance matrix (for all buses), the complex bus power injection vector (for all buses), the initial vector of complex bus voltages, and column vectors with the lists of bus indices for the swing bus, PV buses, and PQ buses, respectively. The bus voltage vector contains the set point for generator (including ref bus) buses, and the reference angle of the swing bus, as well as an initial guess for remaining magnitudes and angles. C{ppopt} is a PYPOWER options vector which can be used to set the termination tolerance, maximum number of iterations, and output options (see L{ppoption} for details). Uses default options if this parameter is not given. Returns the final complex voltages, a flag which indicates whether it converged or not, and the number of iterations performed. @see: L{runpf} @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln Modified by University of Kassel (Florian Schaefer) to use numba """ ## default arguments global dSbus_dV_calc, dSbus_dV_calc ## options tol = options['tolerance_kva'] * 1e-3 max_it = options["max_iteration"] ## initialize i = 0 V = V0 Va = angle(V) Vm = abs(V) ## set up indexing for updating V pvpq = r_[pv, pq] # generate lookup pvpq -> index pvpq (used in createJ) pvpq_lookup = zeros(pvpq[-1]+1, dtype=int) pvpq_lookup[pvpq] = arange(len(pvpq)) # if numba is available import "numba enhanced" functions if numba: from pandapower.pypower_extensions.create_J import create_J, create_J2 from pandapower.pypower_extensions.dSbus_dV import dSbus_dV_calc from pandapower.pypower_extensions.dSbus_dV import dSbus_dV # check if pvpq is the same as pq. In this case a faster version of create_J can be used if len(pvpq) == len(pq): createJ = create_J2 else: createJ = create_J else: # numba == False -> Import pypower standard from pypower.dSbus_dV import dSbus_dV npv = len(pv) npq = len(pq) j1 = 0; j2 = npv ## j1:j2 - V angle of pv buses j3 = j2; j4 = j2 + npq ## j3:j4 - V angle of pq buses j5 = j4; j6 = j4 + npq ## j5:j6 - V mag of pq buses ## evaluate F(x0) mis = V * conj(Ybus * V) - Sbus F = r_[ mis[pv].real, mis[pq].real, mis[pq].imag ] # check for convergence converged = _check_for_convergence(F, tol) Ybus = Ybus.tocsr() ## do Newton iterations while (not converged and i < max_it): ## update iteration counter i = i + 1 # use numba if activated if numba: #create Jacobian from fast calc of dS_dV dVm_x, dVa_x = dSbus_dV_calc(Ybus.data, Ybus.indptr, Ybus.indices, V, V/abs(V)) # data in J, space preallocated is bigger than acutal Jx -> will be reduced later on Jx = empty(len(dVm_x) * 4, dtype=float64) # row pointer, dimension = pvpq.shape[0] + pq.shape[0] + 1 Jp = zeros(pvpq.shape[0] + pq.shape[0] + 1, dtype=int64) # indices, same with the preallocated space (see Jx) Jj = empty(len(dVm_x) * 4, dtype=int64) # fill Jx, Jj and Jp createJ(dVm_x, dVa_x, Ybus.indptr, Ybus.indices, pvpq_lookup, pvpq, pq, Jx, Jj, Jp) # resize before generating the scipy sparse matrix Jx.resize(Jp[-1], refcheck=False) Jj.resize(Jp[-1], refcheck=False) # generate scipy sparse matrix dimJ = npv+npq+npq J = sparse((Jx, Jj, Jp), shape=(dimJ, dimJ)) else: # create Jacobian with standard pypower implementation. dS_dVm, dS_dVa = dSbus_dV(Ybus, V) ## evaluate Jacobian J11 = dS_dVa[array([pvpq]).T, pvpq].real J12 = dS_dVm[array([pvpq]).T, pq].real J21 = dS_dVa[array([pq]).T, pvpq].imag J22 = dS_dVm[array([pq]).T, pq].imag J = vstack([ hstack([J11, J12]), hstack([J21, J22]) ], format="csr") ## compute update step dx = -1 * spsolve(J, F) ## update voltage if npv: Va[pv] = Va[pv] + dx[j1:j2] if npq: Va[pq] = Va[pq] + dx[j3:j4] Vm[pq] = Vm[pq] + dx[j5:j6] V = Vm * exp(1j * Va) Vm = abs(V) ## update Vm and Va again in case Va = angle(V) ## we wrapped around with a negative Vm ## evalute F(x) mis = V * conj(Ybus * V) - Sbus F = r_[ mis[pv].real, mis[pq].real, mis[pq].imag ] ## check for convergence converged = _check_for_convergence(F, tol) return V, converged, i
def optimal_power_flow(*args): casedata = args[0] # Target power flow modelling mpc = loadcase(casedata) # Import the power flow modelling ## convert to internal indexing mpc = ext2int(mpc) baseMVA, bus, gen, branch, gencost = mpc["baseMVA"], mpc["bus"], mpc[ "gen"], mpc["branch"], mpc["gencost"] # nb = shape(mpc['bus'])[0] ## number of buses nl = shape(mpc['branch'])[0] ## number of branches ng = shape(mpc['gen'])[0] ## number of dispatchable injections ## Formualte the stat = branch[:, BR_STATUS] ## ones at in-service branches b = stat / branch[:, BR_X] ## series susceptance tap = ones(nl) ## default tap ratio = 1 i = find(branch[:, TAP]) ## indices of non-zero tap ratios tap[i] = branch[i, TAP] ## assign non-zero tap ratios ## build connection matrix Cft = Cf - Ct for line and from - to buses f = branch[:, F_BUS] ## list of "from" buses t = branch[:, T_BUS] ## list of "to" buses i = r_[range(nl), range(nl)] ## double set of row indices ## connection matrix Cft = sparse((r_[ones(nl), -ones(nl)], (i, r_[f, t])), (nl, nb)) ## build Bf such that Bf * Va is the vector of real branch powers injected ## at each branch's "from" bus Bf = sparse((r_[b, -b], (i, r_[f, t])), shape=(nl, nb)) ## = spdiags(b, 0, nl, nl) * Cft ## build Bbus Bbus = Cft.T * Bf # The distribution factor Distribution_factor = sparse(Bf * inv(Bbus)) Cg = sparse( (ones(ng), (gen[:, GEN_BUS], arange(ng))), (nb, ng) ) # Sparse index generation method is different from the way of matlab Cd = sparse((ones(nb), (bus[:, BUS_I], arange(nb))), (nb, nb)) # Sparse index load Pd = sum(bus[:, PD]) # Total power demand # Formulate the problem lb = gen[:, PMIN] ub = gen[:, PMAX] Aeq = sparse(ones(ng)) beq = [Pd] Aineq = Distribution_factor * Cg Aineq = vstack([Aineq, -Aineq]) bineq = concatenate( (branch[:, RATE_A] + Distribution_factor * Cd * bus[:, PD], branch[:, RATE_A] - Distribution_factor * Cd * bus[:, PD])) c = gencost[:, 5] Q = 2 * diag(gencost[:, 4]) (Pg, obj) = miqp_gurobi(c=c, Q=Q, Aeq=Aeq, beq=beq, A=Aineq, b=bineq, xmin=lb, xmax=ub) obj = obj + sum(gencost[:, 6]) return Pg, obj
def dSbus_dV(Ybus, V): """ Computes partial derivatives of power injection w.r.t. voltage. Returns two matrices containing partial derivatives of the complex bus power injections w.r.t voltage magnitude and voltage angle respectively (for all buses). If C{Ybus} is a sparse matrix, the return values will be also. The following explains the expressions used to form the matrices:: S = diag(V) * conj(Ibus) = diag(conj(Ibus)) * V Partials of V & Ibus w.r.t. voltage magnitudes:: dV/dVm = diag(V / abs(V)) dI/dVm = Ybus * dV/dVm = Ybus * diag(V / abs(V)) Partials of V & Ibus w.r.t. voltage angles:: dV/dVa = j * diag(V) dI/dVa = Ybus * dV/dVa = Ybus * j * diag(V) Partials of S w.r.t. voltage magnitudes:: dS/dVm = diag(V) * conj(dI/dVm) + diag(conj(Ibus)) * dV/dVm = diag(V) * conj(Ybus * diag(V / abs(V))) + conj(diag(Ibus)) * diag(V / abs(V)) Partials of S w.r.t. voltage angles:: dS/dVa = diag(V) * conj(dI/dVa) + diag(conj(Ibus)) * dV/dVa = diag(V) * conj(Ybus * j * diag(V)) + conj(diag(Ibus)) * j * diag(V) = -j * diag(V) * conj(Ybus * diag(V)) + conj(diag(Ibus)) * j * diag(V) = j * diag(V) * conj(diag(Ibus) - Ybus * diag(V)) For more details on the derivations behind the derivative code used in PYPOWER information, see: [TN2] R. D. Zimmerman, "AC Power Flows, Generalized OPF Costs and their Derivatives using Complex Matrix Notation", MATPOWER Technical Note 2, February 2010. U{http://www.pserc.cornell.edu/matpower/TN2-OPF-Derivatives.pdf} @author: Ray Zimmerman (PSERC Cornell) """ ib = range(len(V)) if issparse(Ybus): Ibus = Ybus * V diagV = sparse((V, (ib, ib))) diagIbus = sparse((Ibus, (ib, ib))) diagVnorm = sparse((V / abs(V), (ib, ib))) else: Ibus = Ybus * asmatrix(V).T diagV = asmatrix(diag(V)) diagIbus = asmatrix(diag(asarray(Ibus).flatten())) diagVnorm = asmatrix(diag(V / abs(V))) dS_dVm = diagV * conj(Ybus * diagVnorm) + conj(diagIbus) * diagVnorm dS_dVa = 1j * diagV * conj(diagIbus - Ybus * diagV) return dS_dVm, dS_dVa
def makeAy(baseMVA, ng, gencost, pgbas, qgbas, ybas): """Make the A matrix and RHS for the CCV formulation. Constructs the parameters for linear "basin constraints" on C{Pg}, C{Qg} and C{Y} used by the CCV cost formulation, expressed as:: Ay * x <= by where C{x} is the vector of optimization variables. The starting index within the C{x} vector for the active, reactive sources and the C{y} variables should be provided in arguments C{pgbas}, C{qgbas}, C{ybas}. The number of generators is C{ng}. Assumptions: All generators are in-service. Filter any generators that are offline from the C{gencost} matrix before calling L{makeAy}. Efficiency depends on C{Qg} variables being after C{Pg} variables, and the C{y} variables must be the last variables within the vector C{x} for the dimensions of the resulting C{Ay} to be conformable with C{x}. @author: Carlos E. Murillo-Sanchez (PSERC Cornell & Universidad Autonoma de Manizales) """ ## find all pwl cost rows in gencost, either real or reactive iycost = find(gencost[:, MODEL] == PW_LINEAR) ## this is the number of extra "y" variables needed to model those costs ny = iycost.shape[0] if ny == 0: Ay = zeros((0, ybas + ny - 1)) ## TODO: Check size (- 1) by = array([]) return Ay, by ## if p(i),p(i+1),c(i),c(i+1) define one of the cost segments, then ## the corresponding constraint on Pg (or Qg) and Y is ## c(i+1) - c(i) ## Y >= c(i) + m * (Pg - p(i)), m = --------------- ## p(i+1) - p(i) ## ## this becomes m * Pg - Y <= m*p(i) - c(i) ## Form A matrix. Use two different loops, one for the PG/Qg coefs, ## then another for the y coefs so that everything is filled in the ## same order as the compressed column sparse format used by matlab ## this should be the quickest. m = sum(gencost[iycost, NCOST].astype(int)) ## total number of cost points Ay = sparse((m - ny, ybas + ny - 1)) by = array([]) ## First fill the Pg or Qg coefficients (since their columns come first) ## and the rhs k = 0 for i in iycost: ns = gencost[i, NCOST].astype(int) ## # of cost points segments = ns-1 p = gencost[i, COST:COST + 2 * ns - 1:2] / baseMVA c = gencost[i, COST + 1:COST + 2 * ns:2] m = diff(c) / diff(p) ## slopes for Pg (or Qg) if any(diff(p) == 0): print('makeAy: bad x axis data in row ##i of gencost matrix' % i) b = m * p[:ns - 1] - c[:ns - 1] ## and rhs by = r_[by, b] if i > ng: sidx = qgbas + (i - ng) - 1 ## this was for a q cost else: sidx = pgbas + i - 1 ## this was for a p cost ## FIXME: Bug in SciPy 0.7.2 prevents setting with a sequence # Ay[k:k + ns - 1, sidx] = m for ii, kk in enumerate(range(k, k + ns - 1)): Ay[kk, sidx] = m[ii] k = k + ns - 1 ## Now fill the y columns with -1's k = 0 j = 0 for i in iycost: ns = gencost[i, NCOST].astype(int) ## FIXME: Bug in SciPy 0.7.2 prevents setting with a sequence # Ay[k:k + ns - 1, ybas + j - 1] = -ones(ns - 1) for kk in range(k, k + ns - 1): Ay[kk, ybas + j - 1] = -1 k = k + ns - 1 j = j + 1 return Ay.tocsr(), by
def makeApq(baseMVA, gen): """Construct linear constraints for generator capability curves. Constructs the parameters for the following linear constraints implementing trapezoidal generator capability curves, where C{Pg} and C{Qg} are the real and reactive generator injections:: Apqh * [Pg, Qg] <= ubpqh Apql * [Pg, Qg] <= ubpql C{data} constains additional information as shown below. Example:: Apqh, ubpqh, Apql, ubpql, data = makeApq(baseMVA, gen) data['h'] [Qc1max-Qc2max, Pc2-Pc1] data['l'] [Qc2min-Qc1min, Pc1-Pc2] data['ipqh'] indices of gens with general PQ cap curves (upper) data['ipql'] indices of gens with general PQ cap curves (lower) @author: Ray Zimmerman (PSERC Cornell) @author: Carlos E. Murillo-Sanchez (PSERC Cornell & Universidad Autonoma de Manizales) """ data = {} ## data dimensions ng = gen.shape[0] ## number of dispatchable injections ## which generators require additional linear constraints ## (in addition to simple box constraints) on (Pg,Qg) to correctly ## model their PQ capability curves ipqh = find(hasPQcap(gen, 'U')) ipql = find(hasPQcap(gen, 'L')) npqh = ipqh.shape[0] ## number of general PQ capability curves (upper) npql = ipql.shape[0] ## number of general PQ capability curves (lower) ## make Apqh if there is a need to add general PQ capability curves ## use normalized coefficient rows so multipliers have right scaling ## in $$/pu if npqh > 0: data["h"] = c_[gen[ipqh, QC1MAX] - gen[ipqh, QC2MAX], gen[ipqh, PC2] - gen[ipqh, PC1]] ubpqh = data["h"][:, 0] * gen[ipqh, PC1] + \ data["h"][:, 1] * gen[ipqh, QC1MAX] for i in range(npqh): tmp = linalg.norm(data["h"][i, :]) data["h"][i, :] = data["h"][i, :] / tmp ubpqh[i] = ubpqh[i] / tmp Apqh = sparse((data["h"].flatten('F'), (r_[arange(npqh), arange(npqh)], r_[ipqh, ipqh + ng])), (npqh, 2 * ng)) ubpqh = ubpqh / baseMVA else: data["h"] = array([]) Apqh = zeros((0, 2 * ng)) ubpqh = array([]) ## similarly Apql if npql > 0: data["l"] = c_[gen[ipql, QC2MIN] - gen[ipql, QC1MIN], gen[ipql, PC1] - gen[ipql, PC2]] ubpql = data["l"][:, 0] * gen[ipql, PC1] + \ data["l"][:, 1] * gen[ipql, QC1MIN] for i in range(npql): tmp = linalg.norm(data["l"][i, :]) data["l"][i, :] = data["l"][i, :] / tmp ubpql[i] = ubpql[i] / tmp Apql = sparse((data["l"].flatten('F'), (r_[arange(npql), arange(npql)], r_[ipql, ipql + ng])), (npql, 2 * ng)) ubpql = ubpql / baseMVA else: data["l"] = array([]) Apql = zeros((0, 2 * ng)) ubpql = array([]) data["ipql"] = ipql data["ipqh"] = ipqh return Apqh, ubpqh, Apql, ubpql, data
def ips(f_fcn, x0=None, Aeq=None, beq=None, A=None, b=None, xmin=None, xmax=None, gh_fcn=None, hess_fcn=None, opt=None): """Primal-dual interior point method for NLP (nonlinear programming). Minimize a function F(X) beginning from a starting point M{x0}, subject to optional linear and nonlinear constraints and variable bounds:: min f(x) x subject to:: g(x) = 0 (nonlinear equalities) h(x) <= 0 (nonlinear inequalities) A*x == beq (linear constraints, equality) A*x <= b (linear constraints, inequality) xmin <= x <= xmax (variable bounds) Ported by Richard Lincoln from the MATLAB Interior Point Solver (MIPS) (v1.9) by Ray Zimmerman. MIPS is distributed as part of the MATPOWER project, developed at the Power System Engineering Research Center (PSERC) (PSERC), Cornell. See U{http://www.pserc.cornell.edu/matpower/} for more info. MIPS was ported by Ray Zimmerman from C code written by H. Wang for his PhD dissertation: - "On the Computation and Application of Multi-period Security-Constrained Optimal Power Flow for Real-time Electricity Market Operations", Cornell University, May 2007. See also: - H. Wang, C. E. Murillo-Sanchez, R. D. Zimmerman, R. J. Thomas, "On Computational Issues of Market-Based Optimal Power Flow", IEEE Transactions on Power Systems, Vol. 22, No. 3, Aug. 2007, pp. 1185-1193. All parameters are optional except C{f_fcn} and C{x0}. @param f_fcn: Function that evaluates the objective function, its gradients and Hessian for a given value of M{x}. If there are nonlinear constraints, the Hessian information is provided by the 'hess_fcn' argument and is not required here. @type f_fcn: callable @param x0: Starting value of optimization vector M{x}. @type x0: array @param Aeq: Optional equality linear constraints. @type Aeq: csr_matrix @param beq: Optional equality linear constraints. @type beq: array @param A: Optional linear constraints. @type A: csr_matrix @param b: Optional linear constraints. Default values are M{Inf}. @type b: array @param xmin: Optional lower bounds on the M{x} variables, defaults are M{-Inf}. @type xmin: array @param xmax: Optional upper bounds on the M{x} variables, defaults are M{Inf}. @type xmax: array @param gh_fcn: Function that evaluates the optional nonlinear constraints and their gradients for a given value of M{x}. @type gh_fcn: callable @param hess_fcn: Handle to function that computes the Hessian of the Lagrangian for given values of M{x}, M{lambda} and M{mu}, where M{lambda} and M{mu} are the multipliers on the equality and inequality constraints, M{g} and M{h}, respectively. @type hess_fcn: callable @param opt: optional options dictionary with the following keys, all of which are also optional (default values shown in parentheses) - C{verbose} (False) - Controls level of progress output displayed - C{feastol} (1e-6) - termination tolerance for feasibility condition - C{gradtol} (1e-6) - termination tolerance for gradient condition - C{comptol} (1e-6) - termination tolerance for complementarity condition - C{costtol} (1e-6) - termination tolerance for cost condition - C{max_it} (150) - maximum number of iterations - C{step_control} (False) - set to True to enable step-size control - C{max_red} (20) - maximum number of step-size reductions if step-control is on - C{cost_mult} (1.0) - cost multiplier used to scale the objective function for improved conditioning. Note: This value is also passed as the 3rd argument to the Hessian evaluation function so that it can appropriately scale the objective function term in the Hessian of the Lagrangian. @type opt: dict @rtype: dict @return: The solution dictionary has the following keys: - C{x} - solution vector - C{f} - final objective function value - C{converged} - exit status - True = first order optimality conditions satisfied - False = maximum number of iterations reached - None = numerically failed - C{output} - output dictionary with keys: - C{iterations} - number of iterations performed - C{hist} - list of arrays with trajectories of the following: feascond, gradcond, compcond, costcond, gamma, stepsize, obj, alphap, alphad - C{message} - exit message - C{lmbda} - dictionary containing the Langrange and Kuhn-Tucker multipliers on the constraints, with keys: - C{eqnonlin} - nonlinear equality constraints - C{ineqnonlin} - nonlinear inequality constraints - C{mu} - linear equal constraints - C{lower} - lower bound on optimization variables - C{upper} - upper bound on optimization variables """ if isinstance(f_fcn, dict): ## problem dict, the input can also be dict p = f_fcn f_fcn = p['f_fcn'] x0 = p['x0'] if 'opt' in p: opt = p['opt'] if 'hess_fcn' in p: hess_fcn = p['hess_fcn'] if 'gh_fcn' in p: gh_fcn = p['gh_fcn'] if 'xmax' in p: xmax = p['xmax'] if 'xmin' in p: xmin = p['xmin'] if 'Aeq' in p: Aeq = p['Aeq'] if 'beq' in p: beq = p['beq'] if 'A' in p: Aeq = p['A'] if 'b' in p: beq = p['b'] nx = x0.shape[0] # number of variables neq = Aeq.shape[ 0] if Aeq is not None else 0 # number of equal linear constraints nineq = A.shape[ 0] if A is not None else 0 # number of inequal linear constraints # default argument values if beq is None or len(beq) == 0: beq = -Inf * ones(neq) if b is None or len(b) == 0: b = Inf * ones(nineq) if xmin is None or len(xmin) == 0: xmin = -Inf * ones(x0.shape[0]) if xmax is None or len(xmax) == 0: xmax = Inf * ones(x0.shape[0]) if gh_fcn is None: nonlinear = False gn = array([]) hn = array([]) else: nonlinear = True if opt is None: opt = {} # options if "feastol" not in opt: opt["feastol"] = 1e-06 if "gradtol" not in opt: opt["gradtol"] = 1e-06 if "comptol" not in opt: opt["comptol"] = 1e-06 if "costtol" not in opt: opt["costtol"] = 1e-06 if "max_it" not in opt: opt["max_it"] = 150 if "max_red" not in opt: opt["max_red"] = 20 if "step_control" not in opt: opt["step_control"] = False if "cost_mult" not in opt: opt["cost_mult"] = 1 if "verbose" not in opt: opt["verbose"] = 2 # initialize history hist = [] # constants xi = 0.99995 sigma = 0.1 z0 = 1 alpha_min = 1e-8 rho_min = 0.95 rho_max = 1.05 mu_threshold = 1e-5 # initialize i = 0 # iteration counter converged = False # flag eflag = False # exit flag # add var limits to linear constraints eyex = eye(nx, nx, format="csr") AA = vstack([-eyex, eyex], "csr") if A is None else vstack( [-eyex, eyex, A], "csr") bb = r_[-xmin, xmax, b] ib = find( bb <= 1e10) # Bounded constrain, does not need to take into consideration if len(ib): idxs = [(1, ib)] Ai = vstack([sig * AA[idx, :] for sig, idx in idxs if len(idx)], 'csr') else: Ai = None bi = bb[ib] # Parameters input for the linear constraints Ae = Aeq be = beq # evaluate cost f(x0) and constraints g(x0), h(x0) x = x0 f, df = f_fcn(x) # cost f = f * opt["cost_mult"] df = df * opt["cost_mult"] if nonlinear: hn, gn, dhn, dgn = gh_fcn(x) # nonlinear constraints h = hn if Ai is None else r_[hn, Ai * x - bi] # inequality constraints g = gn if Ae is None else r_[gn, Ae * x - be] # equality constraints if (dhn is None) and (Ai is None): dh = None elif dhn is None: dh = Ai.T elif Ai is None: dh = dhn else: dh = hstack([dhn, Ai.T]) if (dgn is None) and (Ae is None): dg = None elif dgn is None: dg = Ae.T elif Ae is None: dg = dgn else: dg = hstack([dgn, Ae.T]) else: h = -bi if Ai is None else Ai * x - bi # inequality constraints g = -be if Ae is None else Ae * x - be # equality constraints dh = None if Ai is None else Ai.T # 1st derivative of inequalities dg = None if Ae is None else Ae.T # 1st derivative of equalities # some dimensions neq = g.shape[0] # number of equality constraints niq = h.shape[0] # number of inequality constraints neqnln = gn.shape[0] # number of nonlinear equality constraints niqnln = hn.shape[0] # number of nonlinear inequality constraints # initialize gamma, lam, mu, z, e gamma = 1 # barrier coefficient lam = zeros(neq) z = z0 * ones(niq) mu = z0 * ones(niq) k = find(h < -z0) z[k] = -h[k] k = find((gamma / z) > z0) mu[k] = gamma / z[k] e = ones(niq) # check tolerance f0 = f if opt["step_control"]: L = f + dot(lam, g) + dot(mu, h + z) - gamma * sum(log(z)) Lx = df.copy() Lx = Lx + dg * lam if dg is not None else Lx Lx = Lx + dh * mu if dh is not None else Lx maxh = zeros(1) if len(h) == 0 else max(h) gnorm = norm(g, Inf) if len(g) else 0.0 lam_norm = norm(lam, Inf) if len(lam) else 0.0 mu_norm = norm(mu, Inf) if len(mu) else 0.0 znorm = norm(z, Inf) if len(z) else 0.0 feascond = \ max([gnorm, maxh]) / (1 + max([norm(x, Inf), znorm])) gradcond = \ norm(Lx, Inf) / (1 + max([lam_norm, mu_norm])) compcond = dot(z, mu) / (1 + norm(x, Inf)) costcond = absolute(f - f0) / (1 + absolute(f0)) # save history hist.append({ 'feascond': feascond, 'gradcond': gradcond, 'compcond': compcond, 'costcond': costcond, 'gamma': gamma, 'stepsize': 0, 'obj': f / opt["cost_mult"], 'alphap': 0, 'alphad': 0 }) if opt["verbose"]: s = '-sc' if opt["step_control"] else '' if opt['verbose'] > 1: print(" it objective step size feascond gradcond " "compcond costcond ") print("---- ------------ --------- ------------ ------------ " "------------ ------------") print("%3d %12.8g %10s %12g %12g %12g %12g" % (i, (f / opt["cost_mult"]), "", feascond, gradcond, compcond, costcond)) if feascond < opt["feastol"] and gradcond < opt["gradtol"] and \ compcond < opt["comptol"] and costcond < opt["costtol"]: converged = True if opt["verbose"]: print("Converged!") # do Newton iterations while (not converged) and (i < opt["max_it"]): # update iteration counter i += 1 # compute update step lmbda = { "eqnonlin": lam[range(neqnln)], "ineqnonlin": mu[range(niqnln)] } if nonlinear: if hess_fcn is None: print("pips: Hessian evaluation via finite differences " "not yet implemented.\nPlease provide " "your own hessian evaluation function.") Lxx = hess_fcn(x, lmbda, opt["cost_mult"]) else: _, _, d2f = f_fcn(x, True) # cost Lxx = d2f * opt["cost_mult"] rz = range(len(z)) zinvdiag = sparse((1.0 / z, (rz, rz))) if len(z) else None rmu = range(len(mu)) mudiag = sparse((mu, (rmu, rmu))) if len(mu) else None dh_zinv = None if dh is None else dh * zinvdiag M = Lxx if dh is None else Lxx + dh_zinv * mudiag * dh.T N = Lx if dh is None else Lx + dh_zinv * (mudiag * h + gamma * e) Ab = sparse(M) if dg is None else vstack( [hstack([M, dg]), hstack([dg.T, sparse((neq, neq))])]) bb = r_[-N, -g] dxdlam = spsolve(Ab.tocsr(), bb) if any(isnan(dxdlam)): if opt["verbose"]: print('\nNumerically Failed\n') eflag = -1 break dx = dxdlam[:nx] dlam = dxdlam[nx:nx + neq] dz = -h - z if dh is None else -h - z - dh.T * dx dmu = -mu if dh is None else -mu + zinvdiag * (gamma * e - mudiag * dz) # optional step-size control sc = False if opt["step_control"]: x1 = x + dx # evaluate cost, constraints, derivatives at x1 f1, df1 = f_fcn(x1) # cost f1 = f1 * opt["cost_mult"] df1 = df1 * opt["cost_mult"] if nonlinear: hn1, gn1, dhn1, dgn1 = gh_fcn(x1) # nonlinear constraints h1 = hn1 if Ai is None else r_[hn1, Ai * x1 - bi] # ieq constraints g1 = gn1 if Ae is None else r_[gn1, Ae * x1 - be] # eq constraints # 1st der of ieq if (dhn1 is None) and (Ai is None): dh1 = None elif dhn1 is None: dh1 = Ai.T elif Ai is None: dh1 = dhn1 else: dh1 = hstack([dhn1, Ai.T]) # 1st der of eqs if (dgn1 is None) and (Ae is None): dg1 = None elif dgn is None: dg1 = Ae.T elif Ae is None: dg1 = dgn1 else: dg1 = hstack([dgn1, Ae.T]) else: h1 = -bi if Ai is None else Ai * x1 - bi # inequality constraints g1 = -be if Ae is None else Ae * x1 - be # equality constraints dh1 = dh ## 1st derivative of inequalities dg1 = dg ## 1st derivative of equalities # check tolerance Lx1 = df1 Lx1 = Lx1 + dg1 * lam if dg1 is not None else Lx1 Lx1 = Lx1 + dh1 * mu if dh1 is not None else Lx1 maxh1 = zeros(1) if len(h1) == 0 else max(h1) g1norm = norm(g1, Inf) if len(g1) else 0.0 lam1_norm = norm(lam, Inf) if len(lam) else 0.0 mu1_norm = norm(mu, Inf) if len(mu) else 0.0 z1norm = norm(z, Inf) if len(z) else 0.0 feascond1 = max([g1norm, maxh1]) / \ (1 + max([norm(x1, Inf), z1norm])) gradcond1 = norm(Lx1, Inf) / (1 + max([lam1_norm, mu1_norm])) if (feascond1 > feascond) and (gradcond1 > gradcond): sc = True if sc: alpha = 1.0 for j in range(opt["max_red"]): dx1 = alpha * dx x1 = x + dx1 f1, _ = f_fcn(x1) # cost f1 = f1 * opt["cost_mult"] if nonlinear: hn1, gn1, _, _ = gh_fcn(x1) # nonlinear constraints h1 = hn1 if Ai is None else r_[ hn1, Ai * x1 - bi] # inequality constraints g1 = gn1 if Ae is None else r_[gn1, Ae * x1 - be] # equality constraints else: h1 = -bi if Ai is None else Ai * x1 - bi # inequality constraints g1 = -be if Ae is None else Ae * x1 - be # equality constraints L1 = f1 + dot(lam, g1) + dot(mu, h1 + z) - gamma * sum(log(z)) if opt["verbose"] > 2: print(" %3d %10.5f" % (-j, norm(dx1))) rho = (L1 - L) / (dot(Lx, dx1) + 0.5 * dot(dx1, Lxx * dx1)) if (rho > rho_min) and (rho < rho_max): break else: alpha = alpha / 2.0 dx = alpha * dx dz = alpha * dz dlam = alpha * dlam dmu = alpha * dmu # do the update k = find(dz < 0.0) alphap = min([xi * min(z[k] / -dz[k]), 1]) if len(k) else 1.0 k = find(dmu < 0.0) alphad = min([xi * min(mu[k] / -dmu[k]), 1]) if len(k) else 1.0 x = x + alphap * dx z = z + alphap * dz lam = lam + alphad * dlam mu = mu + alphad * dmu if niq > 0: gamma = sigma * dot(z, mu) / niq # evaluate cost, constraints, derivatives f, df = f_fcn(x) # cost f = f * opt["cost_mult"] df = df * opt["cost_mult"] if nonlinear: hn, gn, dhn, dgn = gh_fcn(x) # nln constraints # g = gn if Ai is None else r_[gn, Ai * x - bi] # ieq constraints # h = hn if Ae is None else r_[hn, Ae * x - be] # eq constraints h = hn if Ai is None else r_[hn, Ai * x - bi] # ieq constr g = gn if Ae is None else r_[gn, Ae * x - be] # eq constr if (dhn is None) and (Ai is None): dh = None elif dhn is None: dh = Ai.T elif Ai is None: dh = dhn else: dh = hstack([dhn, Ai.T]) if (dgn is None) and (Ae is None): dg = None elif dgn is None: dg = Ae.T elif Ae is None: dg = dgn else: dg = hstack([dgn, Ae.T]) else: h = -bi if Ai is None else Ai * x - bi # inequality constraints g = -be if Ae is None else Ae * x - be # equality constraints # 1st derivatives are constant, still dh = Ai.T, dg = Ae.T Lx = df Lx = Lx + dg * lam if dg is not None else Lx Lx = Lx + dh * mu if dh is not None else Lx if len(h) == 0: maxh = zeros(1) else: maxh = max(h) gnorm = norm(g, Inf) if len(g) else 0.0 lam_norm = norm(lam, Inf) if len(lam) else 0.0 mu_norm = norm(mu, Inf) if len(mu) else 0.0 znorm = norm(z, Inf) if len(z) else 0.0 feascond = \ max([gnorm, maxh]) / (1 + max([norm(x, Inf), znorm])) gradcond = \ norm(Lx, Inf) / (1 + max([lam_norm, mu_norm])) compcond = dot(z, mu) / (1 + norm(x, Inf)) costcond = float(absolute(f - f0) / (1 + absolute(f0))) hist.append({ 'feascond': feascond, 'gradcond': gradcond, 'compcond': compcond, 'costcond': costcond, 'gamma': gamma, 'stepsize': norm(dx), 'obj': f / opt["cost_mult"], 'alphap': alphap, 'alphad': alphad }) if opt["verbose"] > 1: print("%3d %12.8g %10.5g %12g %12g %12g %12g" % (i, (f / opt["cost_mult"]), norm(dx), feascond, gradcond, compcond, costcond)) if feascond < opt["feastol"] and gradcond < opt["gradtol"] and \ compcond < opt["comptol"] and costcond < opt["costtol"]: converged = True if opt["verbose"]: print("Converged!") else: if any(isnan(x)) or (alphap < alpha_min) or \ (alphad < alpha_min) or (gamma < EPS) or (gamma > 1.0 / EPS): if opt["verbose"]: print("Numerically failed.") eflag = -1 break f0 = f if opt["step_control"]: L = f + dot(lam, g) + dot(mu, (h + z)) - gamma * sum(log(z)) if opt["verbose"]: if not converged: print("Did not converge in %d iterations." % i) # package results if eflag != -1: eflag = converged if eflag == 0: message = 'Did not converge' elif eflag == 1: message = 'Converged' elif eflag == -1: message = 'Numerically failed' else: raise ("Type error") output = {"iterations": i, "hist": hist, "message": message} # zero out multipliers on non-binding constraints mu[find((h < -opt["feastol"]) & (mu < mu_threshold))] = 0.0 # un-scale cost and prices f = f / opt["cost_mult"] lam = lam / opt["cost_mult"] mu = mu / opt["cost_mult"] # re-package multipliers into struct lam_lin = lam[neqnln:neq] # lambda for equal linear constraints mu_lin = mu[niqnln:niq] # mu for inequal linear constraints lmbda = { 'lam': lam_lin, 'mu': mu_lin[nx + nx:nx + nx + nineq], 'lower': mu_lin[:nx], 'upper': mu_lin[nx:nx + nx] } if niqnln > 0: lmbda['ineqnonlin'] = mu[:niqnln] if neqnln > 0: lmbda['eqnonlin'] = lam[:neqnln] # lmbda = {"eqnonlin": lam[:neqnln], 'ineqnonlin': mu[:niqnln], # "mu_l": mu_l[nx:], "mu_u": mu_u[nx:], # "lower": mu_l[:nx], "upper": mu_u[:nx]} solution = { "x": x, "f": f, "eflag": converged, "output": output, "lmbda": lmbda } return solution
def t_makePTDF(quiet=False): """Tests for C{makePTDF}. @author: Ray Zimmerman (PSERC Cornell) """ ntests = 24 t_begin(ntests, quiet) tdir = dirname(__file__) casefile = join(tdir, 't_case9_opf') verbose = 0 #not quiet ## load case ppopt = ppoption(VERBOSE=verbose, OUT_ALL=0) r = rundcopf(casefile, ppopt) baseMVA, bus, gen, branch = r['baseMVA'], r['bus'], r['gen'], r['branch'] _, bus, gen, branch = ext2int1(bus, gen, branch) nb = bus.shape[0] nbr = branch.shape[0] ng = gen.shape[0] ## compute injections and flows Cg = sparse((ones(ng), (gen[:, GEN_BUS], arange(ng))), (nb, ng)) Pg = Cg * gen[:, PG] Pd = bus[:, PD] P = Pg - Pd ig = find(P > 0) il = find(P <= 0) F = branch[:, PF] ## create corresponding slack distribution matrices e1 = zeros((nb, 1)) e1[0] = 1 e4 = zeros((nb, 1)) e4[3] = 1 D1 = eye(nb, nb) - dot(e1, ones((1, nb))) D4 = eye(nb, nb) - dot(e4, ones((1, nb))) Deq = eye(nb, nb) - ones((nb, 1)) / nb * ones((1, nb)) Dg = eye(nb) - matrix(Pd / sum(Pd)).T * ones(nb) Dd = eye(nb) - matrix(Pg / sum(Pg)).T * ones(nb) ## create some PTDF matrices H1 = makePTDF(baseMVA, bus, branch, 0) H4 = makePTDF(baseMVA, bus, branch, 3) Heq = makePTDF(baseMVA, bus, branch, ones(nb)) Hg = makePTDF(baseMVA, bus, branch, Pd) Hd = makePTDF(baseMVA, bus, branch, Pg) ## matrices get properly transformed by slack dist matrices t_is(H1, dot(H1, D1), 8, 'H1 == H1 * D1') t_is(H4, dot(H1, D4), 8, 'H4 == H1 * D4') t_is(Heq, dot(H1, Deq), 8, 'Heq == H1 * Deq') t_is(Hg, dot(H1, Dg), 8, 'Hg == H1 * Dg') t_is(Hd, dot(H1, Dd), 8, 'Hd == H1 * Dd') t_is(H1, dot(Heq, D1), 8, 'H1 == Heq * D1') t_is(H4, dot(Heq, D4), 8, 'H4 == Heq * D4') t_is(Heq, dot(Heq, Deq), 8, 'Heq == Heq * Deq') t_is(Hg, dot(Heq, Dg), 8, 'Hg == Heq * Dg') t_is(Hd, dot(Heq, Dd), 8, 'Hd == Heq * Dd') t_is(H1, dot(Hg, D1), 8, 'H1 == Hg * D1') t_is(H4, dot(Hg, D4), 8, 'H4 == Hg * D4') t_is(Heq, dot(Hg, Deq), 8, 'Heq == Hg * Deq') t_is(Hg, dot(Hg, Dg), 8, 'Hg == Hg * Dg') t_is(Hd, dot(Hg, Dd), 8, 'Hd == Hg * Dd') ## PTDFs can reconstruct flows t_is(F, dot(H1, P), 3, 'Flow == H1 * P') t_is(F, dot(H4, P), 3, 'Flow == H4 * P') t_is(F, dot(Heq, P), 3, 'Flow == Heq * P') t_is(F, dot(Hg, P), 3, 'Flow == Hg * P') t_is(F, dot(Hd, P), 3, 'Flow == Hd * P') ## other t_is(F, dot(Hg, Pg), 3, 'Flow == Hg * Pg') t_is(F, dot(Hd, (-Pd)), 3, 'Flow == Hd * (-Pd)') t_is(zeros(nbr), dot(Hg, (-Pd)), 3, 'zeros == Hg * (-Pd)') t_is(zeros(nbr), dot(Hd, Pg), 3, 'zeros == Hd * Pg') t_end()
def t_opf_dc_pips(quiet=False): """Tests for DC optimal power flow using PIPS solver. @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln """ num_tests = 23 t_begin(num_tests, quiet) tdir = dirname(__file__) casefile = join(tdir, 't_case9_opf') verbose = 0 #not quiet t0 = 'DC OPF (PIPS): ' ppopt = ppoption(VERBOSE=verbose, OUT_ALL=0, OPF_ALG_DC=200) ## run DC OPF ## set up indices ib_data = r_[arange(BUS_AREA + 1), arange(BASE_KV, VMIN + 1)] ib_voltage = arange(VM, VA + 1) ib_lam = arange(LAM_P, LAM_Q + 1) ib_mu = arange(MU_VMAX, MU_VMIN + 1) ig_data = r_[[GEN_BUS, QMAX, QMIN], arange(MBASE, APF + 1)] ig_disp = array([PG, QG, VG]) ig_mu = arange(MU_PMAX, MU_QMIN + 1) ibr_data = arange(ANGMAX + 1) ibr_flow = arange(PF, QT + 1) ibr_mu = array([MU_SF, MU_ST]) #ibr_angmu = array([MU_ANGMIN, MU_ANGMAX]) ## get solved DC power flow case from MAT-file soln9_dcopf = loadmat(join(tdir, 'soln9_dcopf.mat'), struct_as_record=True) ## defines bus_soln, gen_soln, branch_soln, f_soln bus_soln = soln9_dcopf['bus_soln'] gen_soln = soln9_dcopf['gen_soln'] branch_soln = soln9_dcopf['branch_soln'] f_soln = soln9_dcopf['f_soln'][0] ## run OPF t = t0 r = rundcopf(casefile, ppopt) bus, gen, branch, f, success = \ r['bus'], r['gen'], r['branch'], r['f'], r['success'] t_ok(success, [t, 'success']) t_is(f, f_soln, 3, [t, 'f']) t_is(bus[:, ib_data], bus_soln[:, ib_data], 10, [t, 'bus data']) t_is(bus[:, ib_voltage], bus_soln[:, ib_voltage], 3, [t, 'bus voltage']) t_is(bus[:, ib_lam], bus_soln[:, ib_lam], 3, [t, 'bus lambda']) t_is(bus[:, ib_mu], bus_soln[:, ib_mu], 2, [t, 'bus mu']) t_is(gen[:, ig_data], gen_soln[:, ig_data], 10, [t, 'gen data']) t_is(gen[:, ig_disp], gen_soln[:, ig_disp], 3, [t, 'gen dispatch']) t_is(gen[:, ig_mu], gen_soln[:, ig_mu], 3, [t, 'gen mu']) t_is(branch[:, ibr_data], branch_soln[:, ibr_data], 10, [t, 'branch data']) t_is(branch[:, ibr_flow], branch_soln[:, ibr_flow], 3, [t, 'branch flow']) t_is(branch[:, ibr_mu], branch_soln[:, ibr_mu], 2, [t, 'branch mu']) ##----- run OPF with extra linear user constraints & costs ----- ## two new z variables ## 0 <= z1, P2 - P1 <= z1 ## 0 <= z2, P2 - P3 <= z2 ## with A and N sized for DC opf ppc = loadcase(casefile) row = [0, 0, 0, 1, 1, 1] col = [9, 10, 12, 10, 11, 13] ppc['A'] = sparse(([-1, 1, -1, 1, -1, -1], (row, col)), (2, 14)) ppc['u'] = array([0, 0]) ppc['l'] = array([-Inf, -Inf]) ppc['zl'] = array([0, 0]) ppc['N'] = sparse(([1, 1], ([0, 1], [12, 13])), (2, 14)) ## new z variables only ppc['fparm'] = ones((2, 1)) * array([[1, 0, 0, 1]]) ## w = r = z ppc['H'] = sparse((2, 2)) ## no quadratic term ppc['Cw'] = array([1000, 1]) t = ''.join([t0, 'w/extra constraints & costs 1 : ']) r = rundcopf(ppc, ppopt) t_ok(r['success'], [t, 'success']) t_is(r['gen'][0, PG], 116.15974, 4, [t, 'Pg1 = 116.15974']) t_is(r['gen'][1, PG], 116.15974, 4, [t, 'Pg2 = 116.15974']) t_is(r['var']['val']['z'], [0, 0.3348], 4, [t, 'user vars']) t_is(r['cost']['usr'], 0.3348, 3, [t, 'user costs']) ## with A and N sized for AC opf ppc = loadcase(casefile) row = [0, 0, 0, 1, 1, 1] col = [18, 19, 24, 19, 20, 25] ppc['A'] = sparse(([-1, 1, -1, 1, -1, -1], (row, col)), (2, 26)) ppc['u'] = array([0, 0]) ppc['l'] = array([-Inf, -Inf]) ppc['zl'] = array([0, 0]) ppc['N'] = sparse(([1, 1], ([0, 1], [24, 25])), (2, 26)) ## new z variables only ppc['fparm'] = ones((2, 1)) * array([[1, 0, 0, 1]]) ## w = r = z ppc['H'] = sparse((2, 2)) ## no quadratic term ppc['Cw'] = array([1000, 1]) t = ''.join([t0, 'w/extra constraints & costs 2 : ']) r = rundcopf(ppc, ppopt) t_ok(r['success'], [t, 'success']) t_is(r['gen'][0, PG], 116.15974, 4, [t, 'Pg1 = 116.15974']) t_is(r['gen'][1, PG], 116.15974, 4, [t, 'Pg2 = 116.15974']) t_is(r['var']['val']['z'], [0, 0.3348], 4, [t, 'user vars']) t_is(r['cost']['usr'], 0.3348, 3, [t, 'user costs']) t = ''.join([t0, 'infeasible : ']) ## with A and N sized for DC opf ppc = loadcase(casefile) ppc['A'] = sparse(([1, 1], ([0, 0], [9, 10])), (1, 14)) ## Pg1 + Pg2 ppc['u'] = array([Inf]) ppc['l'] = array([600]) r = rundcopf(ppc, ppopt) t_ok(not r['success'], [t, 'no success']) t_end()
def dcopf_solver(om, ppopt, out_opt=None): """Solves a DC optimal power flow. Inputs are an OPF model object, a PYPOWER options dict and a dict containing fields (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{lin} - C{l} lower bounds on linear constraints - C{u} upper bounds on linear constraints - C{g} (optional) constraint values - C{dg} (optional) constraint 1st derivatives - C{df} (optional) obj fun 1st derivatives (not yet implemented) - C{d2f} (optional) obj fun 2nd derivatives (not yet implemented) 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{qps_pypower} @author: Ray Zimmerman (PSERC Cornell) @author: Carlos E. Murillo-Sanchez (PSERC Cornell & Universidad Autonoma de Manizales) @author: Richard Lincoln """ if out_opt is None: out_opt = {} ## options verbose = ppopt['VERBOSE'] alg = ppopt['OPF_ALG_DC'] if alg == 0: if have_fcn('cplex'): ## use CPLEX by default, if available alg = 500 # MOSEK is currently not supported # elif have_fcn('mosek'): ## if not, then MOSEK, if available # alg = 600 elif have_fcn('gurobi'): ## if not, then Gurobi, if available # Error in Gurobi pypower solver -> Issue with pypower 5.1.4. Gurobi won't work. Using alg 200 instead # Reason for failure: In qps_gurobi of pypower len(H) raises Error: # TypeError: sparse matrix length is ambiguous; use getnnz() or shape[0] # Todo: Fix Gurobi and activate 700 again. ATM: Fallback on 200 # alg = 700 alg = 200 UserWarning("Gurobi not working with pypower 5.1.4") else: ## otherwise PIPS alg = 200 ## 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, H, Cw = cp["N"], cp["H"], cp["Cw"] fparm = array(c_[cp["dd"], cp["rh"], cp["kk"], cp["mm"]]) Bf = om.userdata('Bf') Pfinj = om.userdata('Pfinj') vv, ll, _, _ = om.get_idx() ## problem dimensions ipol = find(gencost[:, MODEL] == POLYNOMIAL) ## polynomial costs ipwl = find(gencost[:, MODEL] == PW_LINEAR) ## piece-wise linear costs nb = bus.shape[0] ## number of buses nl = branch.shape[0] ## number of branches nw = N.shape[0] ## number of general cost vars, w ny = om.getN('var', 'y') ## number of piece-wise linear costs nxyz = om.getN('var') ## total number of control vars of all types ## linear constraints & variable bounds A, l, u = om.linear_constraints() x0, xmin, xmax = om.getv() ## set up objective function of the form: f = 1/2 * X'*HH*X + CC'*X ## where X = [x;y;z]. First set up as quadratic function of w, ## f = 1/2 * w'*HHw*w + CCw'*w, where w = diag(M) * (N*X - Rhat). We ## will be building on the (optionally present) user supplied parameters. ## piece-wise linear costs any_pwl = int(ny > 0) if any_pwl: # Sum of y vars. Npwl = sparse((ones(ny), (zeros(ny), arange(vv["i1"]["y"], vv["iN"]["y"]))), (1, nxyz)) Hpwl = sparse((1, 1)) Cpwl = array([1]) fparm_pwl = array([[1, 0, 0, 1]]) else: Npwl = None#zeros((0, nxyz)) Hpwl = None#array([]) Cpwl = array([]) fparm_pwl = zeros((0, 4)) ## quadratic costs npol = len(ipol) if any(len(gencost[ipol, NCOST] > 3)) and sum(gencost[find(gencost[ipol, NCOST] > 3)][:][NCOST+1:]): stderr.write('DC opf cannot handle polynomial costs with higher ' 'than quadratic order.\n') iqdr = find(gencost[ipol, NCOST] == 3) ilin = find(gencost[ipol, NCOST] == 2) polycf = zeros((npol, 3)) ## quadratic coeffs for Pg if len(iqdr) > 0: polycf[iqdr, :] = gencost[ipol[iqdr], COST:COST + 3] if npol: polycf[ilin, 1:3] = gencost[ipol[ilin], COST:COST + 2] polycf = dot(polycf, diag([ baseMVA**2, baseMVA, 1])) ## convert to p.u. if npol: Npol = sparse((ones(npol), (arange(npol), vv["i1"]["Pg"] + ipol)), (npol, nxyz)) # Pg vars Hpol = sparse((2 * polycf[:, 0], (arange(npol), arange(npol))), (npol, npol)) else: Npol = None Hpol = None Cpol = polycf[:, 1] fparm_pol = ones((npol, 1)) * array([[1, 0, 0, 1]]) ## combine with user costs NN = vstack([n for n in [Npwl, Npol, N] if n is not None and n.shape[0] > 0], "csr") # FIXME: Zero dimension sparse matrices. if (Hpwl is not None) and any_pwl and (npol + nw): Hpwl = hstack([Hpwl, sparse((any_pwl, npol + nw))]) if Hpol is not None: if any_pwl and npol: Hpol = hstack([sparse((npol, any_pwl)), Hpol]) if npol and nw: Hpol = hstack([Hpol, sparse((npol, nw))]) if (H is not None) and nw and (any_pwl + npol): H = hstack([sparse((nw, any_pwl + npol)), H]) HHw = vstack([h for h in [Hpwl, Hpol, H] if h is not None and h.shape[0] > 0], "csr") CCw = r_[Cpwl, Cpol, Cw] ffparm = r_[fparm_pwl, fparm_pol, fparm] ## transform quadratic coefficients for w into coefficients for X nnw = any_pwl + npol + nw M = sparse((ffparm[:, 3], (range(nnw), range(nnw)))) MR = M * ffparm[:, 1] HMR = HHw * MR MN = M * NN HH = MN.T * HHw * MN CC = MN.T * (CCw - HMR) C0 = 0.5 * dot(MR, HMR) + sum(polycf[:, 2]) # Constant term of cost. ## set up input for QP solver opt = {'alg': alg, 'verbose': verbose} if (alg == 200) or (alg == 250): ## try to select an interior initial point Varefs = bus[bus[:, BUS_TYPE] == REF, VA] * (pi / 180.0) lb, ub = xmin.copy(), xmax.copy() lb[xmin == -Inf] = -1e10 ## replace Inf with numerical proxies ub[xmax == Inf] = 1e10 x0 = (lb + ub) / 2; # angles set to first reference angle x0[vv["i1"]["Va"]:vv["iN"]["Va"]] = Varefs[0] if ny > 0: ipwl = find(gencost[:, MODEL] == PW_LINEAR) # largest y-value in CCV data c = gencost.flatten('F')[sub2ind(gencost.shape, ipwl, NCOST + 2 * gencost[ipwl, NCOST])] x0[vv["i1"]["y"]:vv["iN"]["y"]] = max(c) + 0.1 * abs(max(c)) ## set up options 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'] if feastol == 0: feastol = ppopt['OPF_VIOLATION'] ## = OPF_VIOLATION by default opt["pips_opt"] = { 'feastol': feastol, 'gradtol': gradtol, 'comptol': comptol, 'costtol': costtol, 'max_it': max_it, 'max_red': max_red, 'cost_mult': 1 } # elif alg == 400: # opt['ipopt_opt'] = ipopt_options([], ppopt) # elif alg == 500: # opt['cplex_opt'] = cplex_options([], ppopt) # elif alg == 600: # opt['mosek_opt'] = mosek_options([], ppopt) # elif alg == 700: # ppopt['GRB_OPT'] = 0 # ppopt['GRB_METHOD'] = "automatic" # ppopt['GRB_TIMELIMIT'] = Inf # ppopt['GRB_THREADS'] = 0 # opt['GRB_OPT'] = gurobi_options(None, ppopt) # else: # raise ValueError("Unrecognised solver [%d]." % alg) ##----- run opf ----- x, f, info, output, lmbda = \ qps_pypower(HH, CC, A, l, u, xmin, xmax, x0, opt) success = (info == 1) ##----- calculate return values ----- if not any(isnan(x)): ## update solution data Va = x[vv["i1"]["Va"]:vv["iN"]["Va"]] Pg = x[vv["i1"]["Pg"]:vv["iN"]["Pg"]] f = f + C0 ## update voltages & generator outputs bus[:, VA] = Va * 180 / pi gen[:, PG] = Pg * baseMVA ## compute branch flows branch[:, [QF, QT]] = zeros((nl, 2)) branch[:, PF] = (Bf * Va + Pfinj) * baseMVA branch[:, PT] = -branch[:, PF] ## package up results mu_l = lmbda["mu_l"] mu_u = lmbda["mu_u"] muLB = lmbda["lower"] muUB = lmbda["upper"] ## update Lagrange multipliers il = find((branch[:, RATE_A] != 0) & (branch[:, RATE_A] < 1e10)) bus[:, [LAM_P, LAM_Q, MU_VMIN, MU_VMAX]] = zeros((nb, 4)) gen[:, [MU_PMIN, MU_PMAX, MU_QMIN, MU_QMAX]] = zeros((gen.shape[0], 4)) branch[:, [MU_SF, MU_ST]] = zeros((nl, 2)) bus[:, LAM_P] = (mu_u[ll["i1"]["Pmis"]:ll["iN"]["Pmis"]] - mu_l[ll["i1"]["Pmis"]:ll["iN"]["Pmis"]]) / baseMVA branch[il, MU_SF] = mu_u[ll["i1"]["Pf"]:ll["iN"]["Pf"]] / baseMVA branch[il, MU_ST] = mu_u[ll["i1"]["Pt"]:ll["iN"]["Pt"]] / baseMVA gen[:, MU_PMIN] = muLB[vv["i1"]["Pg"]:vv["iN"]["Pg"]] / baseMVA gen[:, MU_PMAX] = muUB[vv["i1"]["Pg"]:vv["iN"]["Pg"]] / baseMVA pimul = r_[ mu_l - mu_u, -ones((ny)), ## dummy entry corresponding to linear cost row in A muLB - muUB ] mu = { 'var': {'l': muLB, 'u': muUB}, 'lin': {'l': mu_l, 'u': mu_u} } results = deepcopy(ppc) results["bus"], results["branch"], results["gen"], \ results["om"], results["x"], results["mu"], results["f"] = \ bus, branch, gen, om, x, mu, f raw = {'xr': x, 'pimul': pimul, 'info': info, 'output': output} return results, success, raw
def gh7(x): g = array( [sum(x**2) - 40.0] ) h = array( [ -prod(x) + 25.0] ) dg = sparse( 2 * x ).T dh = sparse( -prod(x) / x ).T return h, g, dh, dg
def makeAvl(baseMVA, gen): """Construct linear constraints for constant power factor var loads. Constructs parameters for the following linear constraint enforcing a constant power factor constraint for dispatchable loads:: lvl <= Avl * [Pg, Qg] <= uvl C{ivl} is the vector of indices of generators representing variable loads. @author: Ray Zimmerman (PSERC Cornell) @author: Carlos E. Murillo-Sanchez (PSERC Cornell & Universidad Autonoma de Manizales) """ ## data dimensions ng = gen.shape[0] ## number of dispatchable injections Pg = gen[:, PG] / baseMVA Qg = gen[:, QG] / baseMVA Pmin = gen[:, PMIN] / baseMVA Qmin = gen[:, QMIN] / baseMVA Qmax = gen[:, QMAX] / baseMVA # Find out if any of these "generators" are actually dispatchable loads. # (see 'help isload' for details on what constitutes a dispatchable load) # Dispatchable loads are modeled as generators with an added constant # power factor constraint. The power factor is derived from the original # value of Pmin and either Qmin (for inductive loads) or Qmax (for # capacitive loads). If both Qmin and Qmax are zero, this implies a unity # power factor without the need for an additional constraint. ivl = find(isload(gen) & ((Qmin != 0) | (Qmax != 0))) nvl = ivl.shape[0] ## number of dispatchable loads ## at least one of the Q limits must be zero (corresponding to Pmax == 0) if any((Qmin[ivl] != 0) & (Qmax[ivl] != 0)): stderr.write('makeAvl: either Qmin or Qmax must be equal to zero for ' 'each dispatchable load.\n') # Initial values of PG and QG must be consistent with specified power # factor This is to prevent a user from unknowingly using a case file which # would have defined a different power factor constraint under a previous # version which used PG and QG to define the power factor. Qlim = (Qmin[ivl] == 0) * Qmax[ivl] + (Qmax[ivl] == 0) * Qmin[ivl] if any(abs(Qg[ivl] - Pg[ivl] * Qlim / Pmin[ivl]) > 1e-6): stderr.write('makeAvl: For a dispatchable load, PG and QG must be ' 'consistent with the power factor defined by PMIN and ' 'the Q limits.\n') # make Avl, lvl, uvl, for lvl <= Avl * [Pg Qg] <= uvl if nvl > 0: xx = Pmin[ivl] yy = Qlim pftheta = arctan2(yy, xx) pc = sin(pftheta) qc = -cos(pftheta) ii = r_[arange(nvl), arange(nvl)] jj = r_[ivl, ivl + ng] Avl = sparse((r_[pc, qc], (ii, jj)), (nvl, 2 * ng)) lvl = zeros(nvl) uvl = lvl else: Avl = zeros((0, 2 * ng)) lvl = array([]) uvl = array([]) return Avl, lvl, uvl, ivl
def makeBdc(bus, branch): """Builds the B matrices and phase shift injections for DC power flow. Returns the B matrices and phase shift injection vectors needed for a DC power flow. The bus real power injections are related to bus voltage angles by:: P = Bbus * Va + PBusinj The real power flows at the from end the lines are related to the bus voltage angles by:: Pf = Bf * Va + Pfinj Does appropriate conversions to p.u. @see: L{dcpf} @author: Carlos E. Murillo-Sanchez (PSERC Cornell & Universidad Autonoma de Manizales) @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln """ ## constants nb = bus.shape[0] ## number of buses nl = branch.shape[0] ## number of lines ## check that bus numbers are equal to indices to bus (one set of bus nums) if any(bus[:, BUS_I] != list(range(nb))): logger.error('makeBdc: buses must be numbered consecutively in ' 'bus matrix\n') ## for each branch, compute the elements of the branch B matrix and the phase ## shift "quiescent" injections, where ## ## | Pf | | Bff Bft | | Vaf | | Pfinj | ## | | = | | * | | + | | ## | Pt | | Btf Btt | | Vat | | Ptinj | ## stat = branch[:, BR_STATUS] ## ones at in-service branches b = stat / branch[:, BR_X] ## series susceptance tap = ones(nl) ## default tap ratio = 1 i = find(real(branch[:, TAP])) ## indices of non-zero tap ratios tap[i] = real(branch[i, TAP]) ## assign non-zero tap ratios b = b / tap ## build connection matrix Cft = Cf - Ct for line and from - to buses f = real(branch[:, F_BUS]).astype(int) ## list of "from" buses t = real(branch[:, T_BUS]).astype(int) ## list of "to" buses i = r_[range(nl), range(nl)] ## double set of row indices ## connection matrix Cft = sparse((r_[ones(nl), -ones(nl)], (i, r_[f, t])), (nl, nb)) ## build Bf such that Bf * Va is the vector of real branch powers injected ## at each branch's "from" bus Bf = sparse((r_[b, -b], (i, r_[f, t]))) ## = spdiags(b, 0, nl, nl) * Cft ## build Bbus Bbus = Cft.T * Bf ## build phase shift injection vectors Pfinj = b * (-branch[:, SHIFT] * pi / 180. ) ## injected at the from bus ... # Ptinj = -Pfinj ## and extracted at the to bus Pbusinj = Cft.T * Pfinj ## Pbusinj = Cf * Pfinj + Ct * Ptinj return Bbus, Bf, Pbusinj, Pfinj
def LevenbergMarquardtPF(Ybus, Sbus, V0, Ibus, pv, pq, tol, max_it=50): """ Solves the power flow problem by the Levenberg-Marquardt power flow algorithm. It is usually better than Newton-Raphson, but it takes an order of magnitude more time to converge. Args: Ybus: Admittance matrix Sbus: Array of nodal power injections V0: Array of nodal voltages (initial solution) Ibus: Array of nodal current injections pv: Array with the indices of the PV buses pq: Array with the indices of the PQ buses tol: Tolerance max_it: Maximum number of iterations Returns: Voltage solution, converged?, error, calculated power injections @Author: Santiago Peñate Vera """ start = time.time() # initialize V = V0 Va = angle(V) Vm = np.abs(V) dVa = zeros_like(Va) dVm = zeros_like(Vm) # set up indexing for updating V pvpq = r_[pv, pq] npv = len(pv) npq = len(pq) # j1:j2 - V angle of pv buses j1 = 0 j2 = npv # j3:j4 - V angle of pq buses j3 = j2 j4 = j2 + npq # j5:j6 - V mag of pq buses j5 = j4 j6 = j4 + npq update_jacobian = True converged = False iter_ = 0 nu = 2.0 lbmda = 0 f_prev = 1e9 # very large number nn = 2 * npq + npv ii = np.linspace(0, nn - 1, nn) Idn = sparse((np.ones(nn), (ii, ii)), shape=(nn, nn)) # csr_matrix identity while not converged and iter_ < max_it: # evaluate Jacobian if update_jacobian: H = Jacobian(Ybus, V, Ibus, pq, pvpq) # evaluate the solution error F(x0) Scalc = V * conj(Ybus * V - Ibus) mis = Scalc - Sbus # compute the mismatch dz = r_[mis[pv].real, mis[pq].real, mis[pq].imag] # mismatch in the Jacobian order # system matrix # H1 = H^t H1 = H.transpose().tocsr() # H2 = H1·H H2 = H1.dot(H) # set first value of lmbda if iter_ == 0: lbmda = 1e-3 * H2.diagonal().max() # compute system matrix A = H^T·H - lambda·I A = H2 + lbmda * Idn # right hand side # H^t·dz rhs = H1.dot(dz) # Solve the increment dx = spsolve(A, rhs) # objective function to minimize f = 0.5 * dz.dot(dz) # decision function val = dx.dot(lbmda * dx + rhs) if val > 0.0: rho = (f_prev - f) / (0.5 * val) else: rho = -1.0 # lambda update if rho >= 0: update_jacobian = True lbmda *= max([1.0 / 3.0, 1 - (2 * rho - 1)**3]) nu = 2.0 # reassign the solution vector if npv: dVa[pv] = dx[j1:j2] if npq: dVa[pq] = dx[j3:j4] dVm[pq] = dx[j5:j6] Vm -= dVm Va -= dVa # update Vm and Va again in case we wrapped around with a negative Vm V = Vm * exp(1j * Va) Vm = np.abs(V) Va = angle(V) else: update_jacobian = False lbmda *= nu nu *= 2.0 # check convergence # normF = np.linalg.norm(dx, np.Inf) normF = np.linalg.norm(Sbus - V * conj(Ybus.dot(V)), np.Inf) converged = normF < tol f_prev = f # update iteration counter iter_ += 1 end = time.time() elapsed = end - start return V, converged, normF, Scalc, iter_, elapsed
def qps_mosek(H, c=None, A=None, l=None, u=None, xmin=None, xmax=None, x0=None, opt=None): """Quadratic Program Solver based on MOSEK. A wrapper function providing a PYPOWER standardized interface for using MOSEKOPT to solve the following QP (quadratic programming) problem:: min 1/2 x'*H*x + c'*x x subject to:: l <= A*x <= u (linear constraints) xmin <= x <= xmax (variable bounds) Inputs (all optional except C{H}, C{C}, C{A} and C{L}): - C{H} : matrix (possibly sparse) of quadratic cost coefficients - C{C} : vector of linear cost coefficients - C{A, l, u} : define the optional linear constraints. Default values for the elements of L and U are -Inf and Inf, respectively. - xmin, xmax : optional lower and upper bounds on the C{x} variables, defaults are -Inf and Inf, respectively. - C{x0} : optional starting value of optimization vector C{x} - C{opt} : optional options structure with the following fields, all of which are also optional (default values shown in parentheses) - C{verbose} (0) - controls level of progress output displayed - 0 = no progress output - 1 = some progress output - 2 = verbose progress output - C{max_it} (0) - maximum number of iterations allowed - 0 = use algorithm default - C{mosek_opt} - options struct for MOSEK, values in C{verbose} and C{max_it} override these options - C{problem} : The inputs can alternatively be supplied in a single C{problem} struct with fields corresponding to the input arguments described above: C{H, c, A, l, u, xmin, xmax, x0, opt} Outputs: - C{x} : solution vector - C{f} : final objective function value - C{exitflag} : exit flag - 1 = success - 0 = terminated at maximum number of iterations - -1 = primal or dual infeasible < 0 = the negative of the MOSEK return code - C{output} : output dict with the following fields: - C{r} - MOSEK return code - C{res} - MOSEK result dict - C{lmbda} : dict containing the Langrange and Kuhn-Tucker multipliers on the constraints, with fields: - C{mu_l} - lower (left-hand) limit on linear constraints - C{mu_u} - upper (right-hand) limit on linear constraints - C{lower} - lower bound on optimization variables - C{upper} - upper bound on optimization variables @author: Ray Zimmerman (PSERC Cornell) """ ##----- input argument handling ----- ## gather inputs if isinstance(H, dict): ## problem struct p = H else: ## individual args p = {'H': H, 'c': c, 'A': A, 'l': l, 'u': u} if xmin is not None: p['xmin'] = xmin if xmax is not None: p['xmax'] = xmax if x0 is not None: p['x0'] = x0 if opt is not None: p['opt'] = opt ## define nx, set default values for H and c if 'H' not in p or len(p['H']) or not any(any(p['H'])): if ('A' not in p) | len(p['A']) == 0 & \ ('xmin' not in p) | len(p['xmin']) == 0 & \ ('xmax' not in p) | len(p['xmax']) == 0: stderr.write( 'qps_mosek: LP problem must include constraints or variable bounds\n' ) else: if 'A' in p & len(p['A']) > 0: nx = shape(p['A'])[1] elif 'xmin' in p & len(p['xmin']) > 0: nx = len(p['xmin']) else: # if isfield(p, 'xmax') && ~isempty(p.xmax) nx = len(p['xmax']) p['H'] = sparse((nx, nx)) qp = 0 else: nx = shape(p['H'])[0] qp = 1 if 'c' not in p | len(p['c']) == 0: p['c'] = zeros(nx) if 'x0' not in p | len(p['x0']) == 0: p['x0'] = zeros(nx) ## default options if 'opt' not in p: p['opt'] = [] if 'verbose' in p['opt']: verbose = p['opt']['verbose'] else: verbose = 0 if 'max_it' in p['opt']: max_it = p['opt']['max_it'] else: max_it = 0 if 'mosek_opt' in p['opt']: mosek_opt = mosek_options(p['opt']['mosek_opt']) else: mosek_opt = mosek_options() if max_it: mosek_opt['MSK_IPAR_INTPNT_MAX_ITERATIONS'] = max_it if qp: mosek_opt['MSK_IPAR_OPTIMIZER'] = 0 ## default solver only for QP ## set up problem struct for MOSEK prob = {} prob['c'] = p['c'] if qp: prob['qosubi'], prob['qosubj'], prob['qoval'] = find( tril(sparse(p['H']))) if 'A' in p & len(p['A']) > 0: prob['a'] = sparse(p['A']) if 'l' in p & len(p['A']) > 0: prob['blc'] = p['l'] if 'u' in p & len(p['A']) > 0: prob['buc'] = p['u'] if 'xmin' in p & len(p['xmin']) > 0: prob['blx'] = p['xmin'] if 'xmax' in p & len(p['xmax']) > 0: prob['bux'] = p['xmax'] ## A is not allowed to be empty if 'a' not in prob | len(prob['a']) == 0: unconstrained = True prob['a'] = sparse((1, (1, 1)), (1, nx)) prob.blc = -Inf prob.buc = Inf else: unconstrained = False ##----- run optimization ----- if verbose: methods = [ 'default', 'interior point', '<default>', '<default>', 'primal simplex', 'dual simplex', 'primal dual simplex', 'automatic simplex', '<default>', '<default>', 'concurrent' ] if len(H) == 0 or not any(any(H)): lpqp = 'LP' else: lpqp = 'QP' # (this code is also in mpver.m) # MOSEK Version 6.0.0.93 (Build date: 2010-10-26 13:03:27) # MOSEK Version 6.0.0.106 (Build date: 2011-3-17 10:46:54) # pat = 'Version (\.*\d)+.*Build date: (\d\d\d\d-\d\d-\d\d)'; pat = 'Version (\.*\d)+.*Build date: (\d+-\d+-\d+)' s, e, tE, m, t = re.compile(eval('mosekopt'), pat) if len(t) == 0: vn = '<unknown>' else: vn = t[0][0] print(('MOSEK Version %s -- %s %s solver\n' % (vn, methods[mosek_opt['MSK_IPAR_OPTIMIZER'] + 1], lpqp))) cmd = 'minimize echo(%d)' % verbose r, res = mosekopt(cmd, prob, mosek_opt) ##----- repackage results ----- if 'sol' in res: if 'bas' in res['sol']: sol = res['sol.bas'] else: sol = res['sol.itr'] x = sol['xx'] else: sol = array([]) x = array([]) ##----- process return codes ----- if 'symbcon' in res: sc = res['symbcon'] else: r2, res2 = mosekopt('symbcon echo(0)') sc = res2['symbcon'] eflag = -r msg = '' if r == sc.MSK_RES_OK: if len(sol) > 0: # if sol['solsta'] == sc.MSK_SOL_STA_OPTIMAL: if sol['solsta'] == 'OPTIMAL': msg = 'The solution is optimal.' eflag = 1 else: eflag = -1 # if sol['prosta'] == sc['MSK_PRO_STA_PRIM_INFEAS']: if sol['prosta'] == 'PRIMAL_INFEASIBLE': msg = 'The problem is primal infeasible.' # elif sol['prosta'] == sc['MSK_PRO_STA_DUAL_INFEAS']: elif sol['prosta'] == 'DUAL_INFEASIBLE': msg = 'The problem is dual infeasible.' else: msg = sol['solsta'] elif r == sc['MSK_RES_TRM_MAX_ITERATIONS']: eflag = 0 msg = 'The optimizer terminated at the maximum number of iterations.' else: if 'rmsg' in res and 'rcodestr' in res: msg = '%s : %s' % (res['rcodestr'], res['rmsg']) else: msg = 'MOSEK return code = %d' % r ## always alert user if license is expired if (verbose or r == 1001) and len(msg) < 0: stdout.write('%s\n' % msg) ##----- repackage results ----- if r == 0: f = p['c'].T * x if len(p['H']) > 0: f = 0.5 * x.T * p['H'] * x + f else: f = array([]) output = {} output['r'] = r output['res'] = res if 'sol' in res: lmbda = {} lmbda['lower'] = sol['slx'] lmbda['upper'] = sol['sux'] lmbda['mu_l'] = sol['slc'] lmbda['mu_u'] = sol['suc'] if unconstrained: lmbda['mu_l'] = array([]) lmbda['mu_u'] = array([]) else: lmbda = array([]) return x, f, eflag, output, lmbda
def dSbr_dV(branch, Yf, Yt, V): """Computes partial derivatives of power flows w.r.t. voltage. returns four matrices containing partial derivatives of the complex branch power flows at "from" and "to" ends of each branch w.r.t voltage magnitude and voltage angle respectively (for all buses). If C{Yf} is a sparse matrix, the partial derivative matrices will be as well. Optionally returns vectors containing the power flows themselves. The following explains the expressions used to form the matrices:: If = Yf * V; Sf = diag(Vf) * conj(If) = diag(conj(If)) * Vf Partials of V, Vf & If w.r.t. voltage angles:: dV/dVa = j * diag(V) dVf/dVa = sparse(range(nl), f, j*V(f)) = j * sparse(range(nl), f, V(f)) dIf/dVa = Yf * dV/dVa = Yf * j * diag(V) Partials of V, Vf & If w.r.t. voltage magnitudes:: dV/dVm = diag(V / abs(V)) dVf/dVm = sparse(range(nl), f, V(f) / abs(V(f)) dIf/dVm = Yf * dV/dVm = Yf * diag(V / abs(V)) Partials of Sf w.r.t. voltage angles:: dSf/dVa = diag(Vf) * conj(dIf/dVa) + diag(conj(If)) * dVf/dVa = diag(Vf) * conj(Yf * j * diag(V)) + conj(diag(If)) * j * sparse(range(nl), f, V(f)) = -j * diag(Vf) * conj(Yf * diag(V)) + j * conj(diag(If)) * sparse(range(nl), f, V(f)) = j * (conj(diag(If)) * sparse(range(nl), f, V(f)) - diag(Vf) * conj(Yf * diag(V))) Partials of Sf w.r.t. voltage magnitudes:: dSf/dVm = diag(Vf) * conj(dIf/dVm) + diag(conj(If)) * dVf/dVm = diag(Vf) * conj(Yf * diag(V / abs(V))) + conj(diag(If)) * sparse(range(nl), f, V(f)/abs(V(f))) Derivations for "to" bus are similar. For more details on the derivations behind the derivative code used in PYPOWER information, see: [TN2] R. D. Zimmerman, "AC Power Flows, Generalized OPF Costs and their Derivatives using Complex Matrix Notation", MATPOWER Technical Note 2, February 2010. U{http://www.pserc.cornell.edu/matpower/TN2-OPF-Derivatives.pdf} @author: Ray Zimmerman (PSERC Cornell) """ ## define 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) il = arange(nl) ib = arange(nb) Vnorm = V / abs(V) if issparse(Yf): ## compute currents If = Yf * V It = Yt * V diagVf = sparse((V[f], (il, il))) diagIf = sparse((If, (il, il))) diagVt = sparse((V[t], (il, il))) diagIt = sparse((It, (il, il))) diagV = sparse((V, (ib, ib))) diagVnorm = sparse((Vnorm, (ib, ib))) shape = (nl, nb) # Partial derivative of S w.r.t voltage phase angle. dSf_dVa = 1j * (conj(diagIf) * sparse( (V[f], (il, f)), shape) - diagVf * conj(Yf * diagV)) dSt_dVa = 1j * (conj(diagIt) * sparse( (V[t], (il, t)), shape) - diagVt * conj(Yt * diagV)) # Partial derivative of S w.r.t. voltage amplitude. dSf_dVm = diagVf * conj(Yf * diagVnorm) + conj(diagIf) * \ sparse((Vnorm[f], (il, f)), shape) dSt_dVm = diagVt * conj(Yt * diagVnorm) + conj(diagIt) * \ sparse((Vnorm[t], (il, t)), shape) else: ## dense version ## compute currents If = asarray(Yf * asmatrix(V).T).flatten() It = asarray(Yt * asmatrix(V).T).flatten() diagVf = asmatrix(diag(V[f])) diagIf = asmatrix(diag(If)) diagVt = asmatrix(diag(V[t])) diagIt = asmatrix(diag(It)) diagV = asmatrix(diag(V)) diagVnorm = asmatrix(diag(Vnorm)) temp1 = asmatrix(zeros((nl, nb), complex)) temp2 = asmatrix(zeros((nl, nb), complex)) temp3 = asmatrix(zeros((nl, nb), complex)) temp4 = asmatrix(zeros((nl, nb), complex)) for i in range(nl): fi, ti = f[i], t[i] temp1[i, fi] = V[fi].item() temp2[i, fi] = Vnorm[fi].item() temp3[i, ti] = V[ti].item() temp4[i, ti] = Vnorm[ti].item() dSf_dVa = 1j * (conj(diagIf) * temp1 - diagVf * conj(Yf * diagV)) dSf_dVm = diagVf * conj(Yf * diagVnorm) + conj(diagIf) * temp2 dSt_dVa = 1j * (conj(diagIt) * temp3 - diagVt * conj(Yt * diagV)) dSt_dVm = diagVt * conj(Yt * diagVnorm) + conj(diagIt) * temp4 # Compute power flow vectors. Sf = V[f] * conj(If) St = V[t] * conj(It) return dSf_dVa, dSf_dVm, dSt_dVa, dSt_dVm, Sf, St
def run(mpc): """ Gurobi based optimal power flow modelling and solution :param mpc: The input case of optimal power flow :return: obtained solution """ # Data format from pypower.idx_brch import F_BUS, T_BUS, BR_R, BR_X, TAP, SHIFT, BR_STATUS, RATE_A from pypower.idx_cost import MODEL, NCOST, PW_LINEAR, COST, POLYNOMIAL from pypower.idx_bus import BUS_TYPE, REF, VA, VM, PD, GS, VMAX, VMIN, BUS_I, QD from pypower.idx_gen import GEN_BUS, VG, PG, QG, PMAX, PMIN, QMAX, QMIN from pypower.ext2int import ext2int mpc = ext2int(mpc) baseMVA, bus, gen, branch, gencost = mpc["baseMVA"], mpc["bus"], mpc["gen"], mpc["branch"], mpc["gencost"] nb = shape(mpc['bus'])[0] # number of buses nl = shape(mpc['branch'])[0] # number of branches ng = shape(mpc['gen'])[0] # number of dispatchable injections for i in range(nl): # branch indexing exchange if branch[i, F_BUS] > branch[i, T_BUS]: temp = branch[i, F_BUS] branch[i, F_BUS] = branch[i, T_BUS] branch[i, T_BUS] = temp f = branch[:, F_BUS] ## list of "from" buses t = branch[:, T_BUS] ## list of "to" buses area = ancestor_children_generation(f, t, range(nb)) # Connection matrix Cg = sparse((ones(ng), (gen[:, GEN_BUS], range(ng))), (nb, ng)) Branch_R = branch[:, BR_R] Branch_X = branch[:, BR_X] # Obtain the boundary information Slmax = branch[:, RATE_A] / baseMVA Pij_l = -Slmax Qij_l = -Slmax Iij_l = zeros(nl) Vm_l = turn_to_power(bus[:, VMIN], 2) Pg_l = gen[:, PMIN] / baseMVA Qg_l = gen[:, QMIN] / baseMVA Pi_l = -bus[:, PD] / baseMVA + Cg * Pg_l Qi_l = -bus[:, QD] / baseMVA + Cg * Qg_l Pij_u = Slmax Qij_u = Slmax Iij_u = Slmax Vm_u = turn_to_power(bus[:, VMAX], 2) Pg_u = 2 * gen[:, PMAX] / baseMVA Qg_u = 2 * gen[:, QMAX] / baseMVA Pi_u = -bus[:, PD] / baseMVA + Cg * Pg_u Qi_u = -bus[:, QD] / baseMVA + Cg * Qg_u # model = Model("OPF") # Define the decision variables, compact set Pij = {} Qij = {} Iij = {} Vi = {} Pg = {} Qg = {} Pi = {} Qi = {} for i in range(nl): Pij[i] = model.addVar(lb=Pij_l[i], ub=Pij_u[i], vtype=GRB.CONTINUOUS, name="Pij{0}".format(i)) Qij[i] = model.addVar(lb=Qij_l[i], ub=Qij_u[i], vtype=GRB.CONTINUOUS, name="Qij{0}".format(i)) Iij[i] = model.addVar(lb=Iij_l[i], ub=Iij_u[i], vtype=GRB.CONTINUOUS, name="Iij{0}".format(i)) for i in range(nb): Vi[i] = model.addVar(lb=Vm_l[i], ub=Vm_u[i], vtype=GRB.CONTINUOUS, name="V{0}".format(i)) for i in range(ng): Pg[i] = model.addVar(lb=Pg_l[i], ub=Pg_u[i], vtype=GRB.CONTINUOUS, name="Pg{0}".format(i)) Qg[i] = model.addVar(lb=Qg_l[i], ub=Qg_u[i], vtype=GRB.CONTINUOUS, name="Qg{0}".format(i)) for i in range(nb): Pi[i] = model.addVar(lb=Pi_l[i], ub=Pi_u[i], vtype=GRB.CONTINUOUS, name="Pi{0}".format(i)) Qi[i] = model.addVar(lb=Qi_l[i], ub=Qi_u[i], vtype=GRB.CONTINUOUS, name="Qi{0}".format(i)) # For each area, before decomposition # Add system level constraints for i in range(nb): # If the bus is the root bus, only the children information is required. if len(area[i]["Ai"]) == 0: print(i) expr = 0 for j in range(len(area[i]["Cbranch"][0])): expr += Pij[area[i]["Cbranch"][0][j]] model.addConstr(lhs=expr - Pi[i], sense=GRB.EQUAL, rhs=0) expr = 0 for j in range(len(area[i]["Cbranch"][0])): expr += Qij[area[i]["Cbranch"][0][j]] model.addConstr(lhs=expr - Qi[i], sense=GRB.EQUAL, rhs=0) elif len(area[i]["Cbranch"]) == 0: # This bus is the lead node model.addConstr(lhs=Pij[area[i]["Abranch"][0][0]] - Iij[area[i]["Abranch"][0][0]] * Branch_R[area[i]["Abranch"][0][0]] + Pi[i],sense=GRB.EQUAL, rhs=0) model.addConstr(lhs=Qij[area[i]["Abranch"][0][0]] - Iij[area[i]["Abranch"][0][0]] * Branch_X[area[i]["Abranch"][0][0]] + Qi[i],sense=GRB.EQUAL, rhs=0) model.addConstr(lhs=Vi[int(area[i]["Ai"][0])] - Vi[i] - 2 * Branch_R[area[i]["Abranch"][0][0]] * Pij[area[i]["Abranch"][0][0]] - 2 * Branch_X[area[i]["Abranch"][0][0]] * Qij[area[i]["Abranch"][0][0]] + Iij[area[i]["Abranch"][0][0]] * (Branch_R[area[i]["Abranch"][0][0]] ** 2 + Branch_X[area[i]["Abranch"][0][0]] ** 2),sense=GRB.EQUAL, rhs=0) model.addConstr( Pij[area[i]["Abranch"][0][0]] * Pij[area[i]["Abranch"][0][0]] + Qij[area[i]["Abranch"][0][0]] * Qij[ area[i]["Abranch"][0][0]] <= Vi[int(area[i]["Ai"][0])] * Iij[area[i]["Abranch"][0][0]]) else: expr = 0 for j in range(len(area[i]["Cbranch"][0])): expr += Pij[area[i]["Cbranch"][0][j]] model.addConstr( lhs= Pij[area[i]["Abranch"][0][0]] - Iij[area[i]["Abranch"][0][0]] * Branch_R[area[i]["Abranch"][0][0]] + Pi[i] - expr,sense=GRB.EQUAL, rhs=0) expr = 0 for j in range(len(area[i]["Cbranch"][0])): expr += Qij[area[i]["Cbranch"][0][j]] model.addConstr(Qij[area[i]["Abranch"][0][0]] - Iij[area[i]["Abranch"][0][0]] * Branch_X[area[i]["Abranch"][0][0]] + Qi[i] - expr,sense=GRB.EQUAL, rhs=0) model.addConstr(lhs=Vi[int(area[i]["Ai"][0])] - Vi[i] - 2 * Branch_R[area[i]["Abranch"][0][0]] * Pij[area[i]["Abranch"][0][0]] - 2 * Branch_X[area[i]["Abranch"][0][0]] * Qij[area[i]["Abranch"][0][0]] + Iij[area[i]["Abranch"][0][0]] * (Branch_R[area[i]["Abranch"][0][0]] ** 2 + Branch_X[area[i]["Abranch"][0][0]] ** 2),sense=GRB.EQUAL, rhs=0) model.addConstr( Pij[area[i]["Abranch"][0][0]] * Pij[area[i]["Abranch"][0][0]] + Qij[area[i]["Abranch"][0][0]] * Qij[area[i]["Abranch"][0][0]] <= Vi[int(area[i]["Ai"][0])] * Iij[area[i]["Abranch"][0][0]]) obj = 0 for i in range(ng): model.addConstr(lhs=Pg[i] - Pi[int(gen[i, GEN_BUS])] ,sense=GRB.EQUAL, rhs= bus[int(gen[i, GEN_BUS]), PD] / baseMVA) model.addConstr(lhs= Qg[i] - Qi[int(gen[i, GEN_BUS])], sense=GRB.EQUAL, rhs= bus[int(gen[i, GEN_BUS]), QD] / baseMVA) obj += gencost[i, 4] * Pg[i] * Pg[i] * baseMVA * baseMVA + gencost[i, 5] * Pg[i] * baseMVA + gencost[i, 6] model.setObjective(obj) model.Params.OutputFlag = 0 model.Params.LogToConsole = 0 model.Params.DisplayInterval = 1 model.optimize() Pij = [] Qij = [] Iij = [] Vi = [] Pg = [] Qg = [] Pi = [] Qi = [] for i in range(nl): Pij.append(model.getVarByName("Pij{0}".format(i)).X) Qij.append(model.getVarByName("Qij{0}".format(i)).X) Iij.append(model.getVarByName("Iij{0}".format(i)).X) for i in range(nb): Vi.append(model.getVarByName("V{0}".format(i)).X) Pi.append(model.getVarByName("Pi{0}".format(i)).X) Qi.append(model.getVarByName("Qi{0}".format(i)).X) for i in range(ng): Pg.append(model.getVarByName("Pg{0}".format(i)).X) Qg.append(model.getVarByName("Qg{0}".format(i)).X) obj = obj.getValue() primal_residual = [] for i in range(nl): primal_residual.append(Pij[i] * Pij[i] + Qij[i] * Qij[i] - Iij[i] * Vi[int(f[i])]) return obj, primal_residual
def distort_image(self, image, fov=20, out_size=256, inv=0, type=1, series=0): # Arguments: # image = array_like # Numpy array containing an image, containing a link to an image, or is empty. # Leaving it empty will call up a GUI to manually select a file. # fov = integer # Field of view coverage with distance in visual angle. When # decompressing an image, it is advised that the value is set to the fov of the original image. # range=[1, 100] # out_size = integer # Determines the size of the output image in pixels. The value is # the size of the output image on one axis. Output image is always a square image. When # decompressing an image, it is advised that the value is set to the size of the original image. # inv = integer # Set to 1 to get a decompression of a distorted input image. Set to 0 to have compression. # range[0, 1] # type = integer # Set to 0 for photoreceptor (cones) based distortion, set to 1 for Ganglion cell based # distortion # series = integer # Set to 1 to store the sparse transformation matrix which can be used for a series of # transformations. # Notes: # Main method for image distortion. This can be used for both forward compression (normal image to Ganglion # compressed image, type_d = 0), or decompressing (Ganglion/Photoreceptor compressed to normal image, type_ # d = 1) an image. Decompression is intended to be used to create a normalized visualization of a # Ganglion/Photocell-compressed images. These normalized representations can be used as an indication of # information loss. As the original image is in pixel-space, and the model assumes an input that is described in # visual angle eccentricity, there is a need to describe the image coverage on the visual field in degrees of # visual angle. Therefore, the model needs the user to input how much of the field of view is covered by the # image (fov) in degrees of visual angle. The range should be between 1 and 100 degrees of visual angle. # The method works on the radial distance of each pixel which will be remapped according to the amount of cells # involved in processing the image. A constant distance between the cells is assumed.For remapping, we use an # inverse mapping approach in which each pixel in the new image is determined by taking it from the original # image. This method requires a inverted integrated cell density function. if (self.W is None) or (series == 0): try: [ self.inR, self.inC, self.inD ] = image.shape # Determine dimensions of the selected image (pixel space) except: #image = np.repeat(image[:, :, np.newaxis], 3, axis=2) [ self.inR, self.inC ] = image.shape # Determine dimensions of the selected image (pixel space) self.inD = None self.inS = max( self.inR, self.inC) # Determine the largest dimension of the image # Parameter e represents the radius of visual field coverage (visual angle). e = fov / 2 # Determine the radius of the in-and output image. in_radius = self.inS / 2 out_radius = out_size / 2 # Calculate the number of Ganglion/Photo cells covering the radius of the image, given the field of view # coverage of the input image. We run the degrees of visual angle (eccentricity) through the integrated # Ganglion/Photo cell density function which gives us the total amount of cells involved with processing the # image along the radius given the covered visual field. if type == 0: n_cells = self.cones_i(e) if type == 1: n_cells = self.fi(e) # Inv determines decompression. If set to 1, it is assumed that the image is compressed already, and # should be normalized. If set to 0, it is assumed the image needs to be distorted. if inv == 0: # How many degrees are modeled within a pixel of the image? This can be determined by dividing the # visual angle of eccentricity by half the image dimension. This will be used to adjust the new radial # distances for each pixel (expressed in degrees) to pixel distances. deg_per_pix = e / in_radius # The n_cells variable represents the amount of cells along the radius of the covered field of view # (visual angle eccentricity). Therefore, the total amount of cell involved along the diameter of the # image will be from -n_cells to n_cells. The image pixels are expressed in number of total cells # involved in processing the image up to each individual pixel. t = np.linspace(-n_cells, n_cells, num=out_size) elif inv == 1: # When going from distorted image to normalized image, we have to take the inverse of the inverse, thus # the regular integrated function. The new radial distances for each pixel will thus be given in the # number of retinal cells. Therefore, this has to be converted to number of pixels. This is done by # calculating the number of cells per pixels. cell_per_pix = n_cells / in_radius # The image is expressed in visual angles. t = np.linspace(-e, e, num=out_size) x, y = np.meshgrid(t, t) x = np.reshape(x, out_size**2) y = np.reshape(y, out_size**2) # For every pixel, calculate its angle, and radius. ang = np.angle(x + y * 1j) rad = np.abs(x + y * 1j) if inv == 0: # Calculate a mask that covers all pixel beyond the modeled fov coverage, for better visualization # (optional) msk = (rad <= n_cells) # Calculate the new location of each pixel. Inverse mapping: For each pixel in the new image, calculate # from which pixel in the original image it gets it values. if type == 0: new_r = self.cones_ii(rad) / deg_per_pix if type == 1: new_r = self.fii(rad) / deg_per_pix # Use angle and new radial values to determine the x and y coordinates. x_n = np.multiply(np.cos(ang), new_r) + self.inS / 2 y_n = np.multiply(np.sin(ang), new_r) + self.inS / 2 elif inv == 1: # Calculate a mask that covers all pixel beyond the modeled fov coverage, for better visualization # (optional) msk = (rad <= fov) # Calculate the new location of each pixel. Inverse mapping: For each pixel in the new image, calculate # from which pixel in the original image it gets it values. if type == 0: new_r = self.cones_i(rad) / cell_per_pix if type == 1: new_r = self.fi(rad) / cell_per_pix # Use angle and new radial values to determine the x and y coordinates. x_n = np.multiply(np.cos(ang), new_r) + in_radius y_n = np.multiply(np.sin(ang), new_r) + in_radius # The method used for image conversion. A sparse matrix that maps every pixel in the old image, to each # pixel in the new image via inverse mapping, is used. # Build a spare matrix for image conversion. W = sparse((out_size**2, self.inS**2), dtype=np.float) # Sometimes division by 0 might happen. This line makes sure the user won't see a warning when this happens. np.seterr(divide='ignore', invalid='ignore') for i in range(out_size**2): # Pixel indices will almost always not be a perfect integer value. Therefore, the value of the new pixel # is value is determined by taking the average of all pixels involved. E.g. a value of 4.3 is converted # to the indices 4, and 5. The RGB values are weighted accordingly (0.7 for index 4, and 0.3 for index # 5). Additionally, boundary checking is used. Values can never be smaller than 0, or larger than the # maximum index of the image. x = np.minimum( np.maximum([math.floor(y_n[i]), math.ceil(y_n[i])], 0), self.inS - 1) y = np.minimum( np.maximum([math.floor(x_n[i]), math.ceil(x_n[i])], 0), self.inS - 1) c, idx = np.unique([x[0] * self.inS + y, x[1] * self.inS + y], return_index=True) dist = np.reshape( np.array([np.abs(x - x_n[i]), np.abs(y - y_n[i])]), 4) W[i, c] = dist[idx] / sum(dist[idx]) if series == 1: self.W = W self.msk = msk else: msk = [] # Vectorize the image if self.inD: image = np.reshape(image, (self.inS**2, self.inD)) else: self.inD = 0 image = np.reshape(image, self.inS**2) # Sparse matrix multiplication with the original input image to build the new image. if series == 1: W = self.W msk = self.msk if self.inD: output = np.reshape( W.dot(image), (out_size, out_size, self.inD)).astype(np.uint8) else: output = np.reshape(W.dot(image), (out_size, out_size)) return output, msk
def t_auction_pips(quiet=False): """Tests for code in auction.py, using PIPS solver. @author: Ray Zimmerman (PSERC Cornell) """ n_tests = 183 t_begin(n_tests, quiet) try: from pypower.extras.smartmarket import runmkt except ImportError: t_skip(n_tests, 'smartmarket code not available') return ppopt = ppoption ppopt['OPF_VIOLATION'] = 1e-7 ppopt['PDIPM_GRADTOL'] = 1e-6 ppopt['PDIPM_COMPTOL'] = 1e-7 ppopt['PDIPM_COSTTOL'] = 5e-9 ppopt['OPF_ALG'] = 560 ppopt['OUT_ALL_LIM'] = 1 ppopt['OUT_BRANCH'] = 0 ppopt['OUT_SYS_SUM'] = 0 ppopt['OUT_ALL'] = 0 ppopt['VERBOSE'] = 0 q = array([ [12, 24, 24], [12, 24, 24], [12, 24, 24], [12, 24, 24], [12, 24, 24], [12, 24, 24], [10, 10, 10], [10, 10, 10], [10, 10, 10], ]) ##----- one offer block marginal @ $50 ----- p = array([[20, 50, 60], [20, 40, 70], [20, 42, 80], [20, 44, 90], [20, 46, 75], [20, 48, 60], [100, 70, 60], [100, 50, 20], [100, 60, 50]]) t = 'one marginal offer @ $50, auction_type = 5' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1150, 100, [], [], mpopt) cq5 = cq.copy() cp5 = cp.copy() i2e = bus.bus_i e2i = sparse((max(i2e), 1)) e2i[i2e] = range(bus.size()) G = find(isload(gen) == False) ## real generators L = find(isload(gen)) ## dispatchable loads Gbus = e2i[gen.gen_bus[G]] Lbus = e2i[gen.gen_bus[L]] Qfudge = zeros(p.shape) Qfudge[L, :] = \ diag(gen.Qg[L] / gen.Pg[L] * bus.lam_Q[Lbus]) * ones(p[L :].shape) t_is(cq[G[0], 1:3], [23.32, 0], 2, t) t_is(cp[G[0], :], 50, 4, t) t_is(cq[L[1], 0:2], [10, 0], 2, t) t_is(cp[L[1], :], 54.0312, 4, t) t_is(cp[G, 0], bus.lam_P[Gbus], 8, [t, ' : gen prices']) t_is(cp[L, 0], bus.lam_P[Lbus] + Qfudge[L, 0], 8, [t, ' : load prices']) lao_X = p(G[0], 1) / bus.lam_P[Gbus[0], LAM_P] fro_X = p(G(5), 2) / bus.lam_P[Gbus[5], LAM_P] lab_X = p(L(2), 1) / (bus.lam_P[Lbus[2]] + Qfudge[L[2], 0]) frb_X = p(L(1), 1) / (bus.lam_P[Lbus[1]] + Qfudge[L[1], 0]) t_is(lao_X, 1, 4, 'lao_X') t_is(fro_X, 1.1324, 4, 'fro_X') t_is(lab_X, 1.0787, 4, 'lab_X') t_is(frb_X, 0.9254, 4, 'frb_X') t = 'one marginal offer @ $50, auction_type = 1' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1110, 100, [], [], mpopt) cp1 = cp.copy() t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp, cp5, 6, [t, ' : prices']) t = 'one marginal offer @ $50, auction_type = 2' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1120, 100, [], [], mpopt) cp2 = cp.copy() t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp[G, :], cp5[G, :] * fro_X, 8, [t, ' : gen prices']) t_is(cp[L[0:1], :], cp5[L[0:1], :] * fro_X, 8, [t, ' : load 1,2 prices']) t_is(cp[L[2], :], 60, 5, [t, ' : load 3 price']) ## clipped by accepted bid t = 'one marginal offer @ $50, auction_type = 3' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1130, 100, [], [], mpopt) cp3 = cp.copy() t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp, cp5 * lab_X, 8, [t, ' : prices']) t = 'one marginal offer @ $50, auction_type = 4' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1140, 100, [], [], mpopt) t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp[G[0], :], p[G[0], 1], 8, [t, ' : gen 1 price']) t_is(cp[G[1:6], :], cp5[G[1:6], :] * frb_X, 8, [t, ' : gen 2-6 prices']) t_is(cp[L, :], cp5[L, :] * frb_X, 8, [t, ' : load prices']) t = 'one marginal offer @ $50, auction_type = 6' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1160, 100, [], [], mpopt) t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp, cp3, 8, [t, ' : prices']) p2 = p.copy() p2[L, :] = array([[100, 100, 100], [100, 0, 0], [100, 100, 0]]) MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1160, 100, [], [], mpopt) t_is(cq, cq5, 5, [t, ' : quantities']) t_is(cp[G, :], cp5[G, :] * fro_X, 4, [t, ' : gen prices']) t_is(cp[L, :], cp5[L, :] * fro_X, 4, [t, ' : load prices']) ## load 3 not clipped as in FRO t = 'one marginal offer @ $50, auction_type = 7' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1170, 100, [], [], mpopt) t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp, cp5 * (lao_X + lab_X) / 2, 8, [t, ' : prices']) t_is(cp, (cp1 + cp3) / 2, 8, [t, ' : prices']) t = 'one marginal offer @ $50, auction_type = 8' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1180, 100, [], [], mpopt) t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp[G, :], cp1[G, :], 8, [t, ' : gen prices']) t_is(cp[L, :], cp3[L, :], 8, [t, ' : load prices']) t = 'one marginal offer @ $50, auction_type = 0' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1100, 100, [], [], mpopt) t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp, p, 8, [t, ' : prices']) ##----- one bid block marginal @ $55 ----- p[L[1], 1] = 55 t = 'one marginal bid @ $55, auction_type = 5' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1150, 100, [], [], mpopt) cq5 = cq.copy() cp5 = cp.copy() Qfudge = zeros(p.shape) Qfudge[L, :] = diag(gen.Qg[L] / gen.Pg[L] * bus.lam_Q[Lbus]) * ones( p[L, :].shape) t_is(cq[G[0], 1:3], [24, 0], 2, t) t_is(cp[G[0], :], 50.016, 3, t) t_is(cq[L[1], 0:2], [10, 0.63], 2, t) t_is(cp[L[1], :], 55, 4, t) t_is(cp[G, 0], bus.lam_P[Gbus], 8, [t, ' : gen prices']) t_is(cp[L, 0], bus.lam_P[Lbus] + Qfudge[L, 0], 8, [t, ' : load prices']) lao_X = p[G[0], 1] / bus.lam_P[Gbus[0]] fro_X = p[G[5], 2] / bus.lam_P[Gbus[5]] lab_X = p[L[1], 1] / (bus.lam_P[Lbus[1]] + Qfudge[L[1], 0]) frb_X = p[L[2], 2] / (bus.lam_P[Lbus[2]] + Qfudge[L[2], 0]) t_is(lao_X, 0.9997, 4, 'lao_X') t_is(fro_X, 1.1111, 4, 'fro_X') t_is(lab_X, 1, 4, 'lab_X') t_is(frb_X, 0.8960, 4, 'frb_X') t = 'one marginal bid @ $55, auction_type = 1' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1110, 100, [], [], mpopt) cp1 = cp.copy() t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp, cp5 * lao_X, 8, [t, ' : prices']) t = 'one marginal bid @ $55, auction_type = 2' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1120, 100, [], [], mpopt) cp2 = cp.copy() t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp[G, :], cp5[G, :] * fro_X, 8, [t, ' : gen prices']) t_is(cp[L[0], :], cp5[L[0], :] * fro_X, 8, [t, ' : load 1 price']) t_is(cp[L[1], :], 55, 5, [t, ' : load 2 price']) t_is(cp[L[2], :], 60, 5, [t, ' : load 3 price']) t = 'one marginal bid @ $55, auction_type = 3' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1130, 100, [], [], mpopt) cp3 = cp.copy() t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp, cp5, 7, [t, ' : prices']) t = 'one marginal bid @ $55, auction_type = 4' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1140, 100, [], [], mpopt) cp4 = cp.copy() t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp[G[0], :], 50, 5, [t, ' : gen 1 price']) t_is(cp[G[1:6], :], cp5[G[1:6], :] * frb_X, 8, [t, ' : gen 2-6 prices']) t_is(cp[L, :], cp5[L, :] * frb_X, 8, [t, ' : load prices']) t = 'one marginal bid @ $55, auction_type = 6' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1160, 100, [], [], mpopt) t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp, cp1, 8, [t, ' : prices']) p2 = p.copy() p2[G, :] = array([[0, 0, 100], [0, 0, 100], [0, 0, 100], [0, 0, 100], [0, 0, 100], [0, 0, 100]]) MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1160, 100, [], [], mpopt) t_is(cq, cq5, 3, [t, ' : quantities']) t_is(cp[G, :], cp5[G, :] * frb_X, 3, [t, ' : gen prices']) ## gen 1, not clipped this time t_is(cp[L, :], cp4[L, :], 3, [t, ' : load prices']) t = 'one marginal bid @ $55, auction_type = 7' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1170, 100, [], [], mpopt) t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp, cp5 * (lao_X + lab_X) / 2, 8, [t, ' : prices']) t_is(cp, (cp1 + cp3) / 2, 8, [t, ' : prices']) t = 'one marginal bid @ $55, auction_type = 8' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1180, 100, [], [], mpopt) t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp[G, :], cp1[G, :], 8, [t, ' : gen prices']) t_is(cp[L, :], cp3[L, :], 8, [t, ' : load prices']) t = 'one marginal bid @ $55, auction_type = 0' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1100, 100, [], [], mpopt) t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp, p, 8, [t, ' : prices']) ##----- one bid block marginal @ $54.50 and one offer block marginal @ $50 ----- p[L[1], 1] = 54.5 t = 'marginal offer @ $50, bid @ $54.50, auction_type = 5' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1150, 100, [], [], mpopt) cq5 = cq.copy() cp5 = cp.copy() Qfudge = zeros(p.shape) Qfudge[L, :] = diag(gen.Qg[L] / gen.Pg[L] * bus.lam_Q[Lbus]) * ones( p[L, :].shape) t_is(cq[G[0], 1:3], [23.74, 0], 2, t) t_is(cp[G[0], :], 50, 4, t) t_is(cq[L[1], 0:2], [10, 0.39], 2, t) t_is(cp[L[1], :], 54.5, 4, t) t_is(cp[G, 0], bus.lam_P[Gbus], 8, [t, ' : gen prices']) t_is(cp[L, 0], bus.lam_P[Lbus] + Qfudge[L, 0], 8, [t, ' : load prices']) lao_X = p[G[0], 1] / bus.lam_P[Gbus[0]] fro_X = p[G[5], 2] / bus.lam_P[Gbus[5]] lab_X = p[L[1], 1] / (bus.lam_P[Lbus[1]] + Qfudge[L[1], 0]) frb_X = p[L[2], 2] / (bus.lam_P[Lbus[2]] + Qfudge[L[2], 0]) t_is(lao_X, 1, 4, 'lao_X') t_is(fro_X, 1.1221, 4, 'fro_X') t_is(lab_X, 1, 4, 'lab_X') t_is(frb_X, 0.8976, 4, 'frb_X') t = 'marginal offer @ $50, bid @ $54.50, auction_type = 1' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1110, 100, [], [], mpopt) cp1 = cp.copy() t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp, cp5, 4, [t, ' : prices']) t = 'marginal offer @ $50, bid @ $54.50, auction_type = 2' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1120, 100, [], [], mpopt) cp2 = cp.copy() t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp[G, :], cp5[G, :] * fro_X, 5, [t, ' : gen prices']) t_is(cp[L[0], :], cp5[L[0], :] * fro_X, 5, [t, ' : load 1 price']) t_is(cp[L[1], :], 54.5, 5, [t, ' : load 2 price']) t_is(cp[L[2], :], 60, 5, [t, ' : load 3 price']) t = 'marginal offer @ $50, bid @ $54.50, auction_type = 3' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1130, 100, [], [], mpopt) cp3 = cp.copy() t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp, cp5, 6, [t, ' : prices']) t = 'marginal offer @ $50, bid @ $54.50, auction_type = 4' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1140, 100, [], [], mpopt) cp4 = cp.copy() t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp[G[0], :], 50, 5, [t, ' : gen 1 price']) t_is(cp[G[1:5], :], cp5[G[1:5], :] * frb_X, 8, [t, ' : gen 2-5 prices']) t_is(cp[G[5], :], 48, 5, [t, ' : gen 6 price']) t_is(cp[L, :], cp5[L, :] * frb_X, 8, [t, ' : load prices']) t = 'marginal offer @ $50, bid @ $54.50, auction_type = 6' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1160, 100, [], [], mpopt) t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp, cp5, 4, [t, ' : prices']) t = 'marginal offer @ $50, bid @ $54.50, auction_type = 7' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1170, 100, [], [], mpopt) t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp, cp5, 4, [t, ' : prices']) t = 'marginal offer @ $50, bid @ $54.50, auction_type = 8' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1180, 100, [], [], mpopt) t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp, cp5, 4, [t, ' : prices']) t = 'marginal offer @ $50, bid @ $54.50, auction_type = 0' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1100, 100, [], [], mpopt) t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp, p, 8, [t, ' : prices']) ##----- gen 1 at Pmin, load 3 block 2 marginal @ $60 ----- t = 'gen 1 @ Pmin, marginal bid @ $60, auction_type = 5' p[L[1], 1] = 50 ## undo previous change p2 = p.copy() p2[G[0], 1:3] = [65, 65] MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1150, 100, [], [], mpopt) Qfudge = zeros(p.shape) Qfudge[L, :] = diag(gen.Qg[L] / gen.Pg[L] * bus.lam_Q[Lbus]) * ones( p[L, :].shape) t_is(cp[G[0], :], 65, 4, [t, ' : gen 1 price']) t_is(cp[G[1], :], 54.2974, 4, [t, ' : gen 2 price']) cq5 = cq.copy() cp5 = cp.copy() cp_lam = cp5.copt() cp_lam[0, :] = bus.lam_P[Gbus[0]] ## unclipped lao_X = p2[G[5], 1] / bus.lam_P[Gbus[5]] fro_X = p2[G[5], 2] / bus.lam_P[Gbus[5]] lab_X = p2[L[2], 1] / (bus.lam_P[Lbus[2]] + Qfudge[L[2], 0]) frb_X = p2[L[1], 1] / (bus.lam_P[Lbus[1]] + Qfudge[L[1], 0]) t_is(lao_X, 0.8389, 4, 'lao_X') t_is(fro_X, 1.0487, 4, 'fro_X') t_is(lab_X, 1, 4, 'lab_X') t_is(frb_X, 0.8569, 4, 'frb_X') t = 'gen 1 @ Pmin, marginal bid @ $60, auction_type = 1' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1110, 100, [], [], mpopt) cp1 = cp.copy() t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp[G[0], :], 65, 8, [t, ' : gen 1 price']) t_is(cp[G[1:6], :], cp_lam[G[1:6], :] * lao_X, 8, [t, ' : gen 2-6 prices']) t_is(cp[L, :], cp_lam[L, :] * lao_X, 8, [t, ' : load prices']) t = 'gen 1 @ Pmin, marginal bid @ $60, auction_type = 2' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1120, 100, [], [], mpopt) t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp[G[0], :], 65, 8, [t, ' : gen 1 price']) t_is(cp[G[1:6], :], cp_lam[G[1:6], :] * fro_X, 8, [t, ' : gen 2-6 prices']) t_is(cp[L[0:2], :], cp_lam[L[0:2], :] * fro_X, 8, [t, ' : load 1-2 prices']) t_is(cp[L[2], :], 60, 8, [t, ' : load 3 price']) t = 'gen 1 @ Pmin, marginal bid @ $60, auction_type = 3' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1130, 100, [], [], mpopt) cp3 = cp.copy() t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp[G[0], :], 65, 8, [t, ' : gen 1 price']) t_is(cp[G[1:6], :], cp_lam[G[1:6], :], 6, [t, ' : gen 2-6 prices']) t_is(cp[L, :], cp_lam[L, :], 6, [t, ' : load prices']) t = 'gen 1 @ Pmin, marginal bid @ $60, auction_type = 4' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1140, 100, [], [], mpopt) cp4 = cp.copy() t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp[G[0], :], 65, 5, [t, ' : gen 1 price']) t_is(cp[G[1:6], :], cp5[G[1:6], :] * frb_X, 8, [t, ' : gen 2-6 prices']) t_is(cp[L, :], cp5[L, :] * frb_X, 8, [t, ' : load prices']) t = 'gen 1 @ Pmin, marginal bid @ $60, auction_type = 6' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1160, 100, [], [], mpopt) t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp, cp4, 8, [t, ' : prices']) t = 'gen 1 @ Pmin, marginal bid @ $60, auction_type = 7' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1170, 100, [], [], mpopt) t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp[G[0], :], 65, 4, [t, ' : gen 1 price']) t_is(cp[G[1:6], :], cp_lam[G[1:6], :] * (lao_X + lab_X) / 2, 8, [t, ' : gen 2-6 prices']) t_is(cp[L, :], cp_lam[L, :] * (lao_X + lab_X) / 2, 8, [t, ' : load prices']) t_is(cp, (cp1 + cp3) / 2, 8, [t, ' : prices']) t = 'gen 1 @ Pmin, marginal bid @ $60, auction_type = 8' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1180, 100, [], [], mpopt) t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp[G, :], cp1[G, :], 8, [t, ' : prices']) t_is(cp[L, :], cp3[L, :], 8, [t, ' : prices']) t = 'gen 1 @ Pmin, marginal bid @ $60, auction_type = 0' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1100, 100, [], [], mpopt) t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp, p2, 8, [t, ' : prices']) ##----- gen 1 at Pmin, gen 6 block 3 marginal @ $60 ----- t = 'gen 1 @ Pmin, marginal offer @ $60, auction_type = 5' p2[L, :] = array([[100, 100, 100], [100, 0, 0], [100, 100, 0]]) MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1150, 100, [], [], mpopt) Qfudge = zeros(p.shape) Qfudge[L, :] = diag(gen.Qg[L] / gen.Pg[L] * bus.lam_Q[Lbus]) * ones( p[L, :].shape) t_is(cp[G[0], :], 65, 4, [t, ' : gen 1 price']) t_is(cp[G[1], :], 57.1612, 4, [t, ' : gen 2 price']) cq5 = cq.copy() cp5 = cp.copy() cp_lam = cp5.copy() cp_lam[0, :] = bus.lamP[Gbus[0]] ## unclipped lao_X = p2[G[5], 2] / bus.lam_P[Gbus[5]] fro_X = p2[G[0], 2] / bus.lam_P[Gbus[0]] lab_X = p2[L[2], 1] / (bus.lam_P[Lbus[2]] + Qfudge[L[2], 0]) frb_X = p2[L[1], 1] / (bus.lam_P[Lbus[1]] + Qfudge[L[1], 0]) t_is(lao_X, 1, 4, 'lao_X') t_is(fro_X, 1.1425, 4, 'fro_X') t_is(lab_X, 1.5813, 4, 'lab_X') t_is(frb_X, 0, 4, 'frb_X') t = 'gen 1 @ Pmin, marginal offer @ $60, auction_type = 1' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1110, 100, [], [], mpopt) cp1 = cp.copy() t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp, cp5, 6, [t, ' : prices']) t = 'gen 1 @ Pmin, marginal offer @ $60, auction_type = 2' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1120, 100, [], [], mpopt) t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp, cp_lam * fro_X, 8, [t, ' : prices']) t = 'gen 1 @ Pmin, marginal offer @ $60, auction_type = 3' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1130, 100, [], [], mpopt) cp3 = cp.copy() t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp, cp_lam * lab_X, 8, [t, ' : prices']) t = 'gen 1 @ Pmin, marginal offer @ $60, auction_type = 4' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1140, 100, [], [], mpopt) t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp[G, 0], [654042444660], 4, [t, ' : gen prices']) t_is(cp[L, :], cp_lam[L, :] * frb_X, 8, [t, ' : prices']) t = 'gen 1 @ Pmin, marginal offer @ $60, auction_type = 6' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1160, 100, [], [], mpopt) t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp, cp_lam * fro_X, 8, [t, ' : prices']) t = 'gen 1 @ Pmin, marginal offer @ $60, auction_type = 7' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1170, 100, [], [], mpopt) t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp, cp_lam * (lao_X + lab_X) / 2, 8, [t, ' : prices']) t_is(cp, (cp_lam + cp3) / 2, 7, [t, ' : prices']) t = 'gen 1 @ Pmin, marginal offer @ $60, auction_type = 8' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1180, 100, [], [], mpopt) t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp[G, :], cp5[G, :], 7, [t, ' : prices']) t_is(cp[L, :], cp3[L, :], 8, [t, ' : prices']) t = 'gen 1 @ Pmin, marginal offer @ $60, auction_type = 0' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1100, 100, [], [], mpopt) t_is(cq, cq5, 8, [t, ' : quantities']) t_is(cp, p2, 8, [t, ' : prices']) ##----- gen 2 decommitted, one offer block marginal @ $60 ----- p[G[1], :] = p[G[1], :] + 100 t = 'price of decommited gen, auction_type = 5' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1150, 200, [], [], mpopt) cp5 = cp.copy() Qfudge = zeros(p.shape) Qfudge[L, :] = diag(gen.Qg[L] / gen.Pg[L] * bus.lam_Q[Lbus]) * ones( p[L, :].shape) t_is(sum(cq[1, :]), 0, 8, t) t_is(cp[1, 0], 59.194, 3, t) # Xo = p[0:6, :] / (diag(bus.lam_P[Gbus]) * ones(p[G, :].shape)) # ao = (cq[0:6, :] != 0) # ro = (cq[0:6, :] == 0) # Xb = p[6:9, :] / (diag(bus.lam_P[Lbus] + gen.Qg[L] / gen.Pg[L] * bus.lam_Q[Lbus]) * ones(p[L, :].shape)) # ab = (cq[6:9, :] != 0) # rb = (cq[6:9, :] == 0) # aXo = ao * Xo # rXo = ro * Xo # aXb = ab * Xb # rXb = rb * Xb lao_X = p[G[5], 2] / bus.lam_P[Gbus[5]] fro_X = p[G[0], 2] / bus.lam_P[Gbus[0]] lab_X = p[L[0], 1] / (bus.lam_P[Lbus[0]] + Qfudge[L[0], 0]) frb_X = p[L[0], 2] / (bus.lam_P[Lbus[0]] + Qfudge[L[0], 0]) t_is(lao_X, 1, 4, 'lao_X') t_is(fro_X, 1.0212, 4, 'fro_X') t_is(lab_X, 1.1649, 4, 'lab_X') t_is(frb_X, 0.9985, 4, 'frb_X') t = 'price of decommited gen, auction_type = 1' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1110, 200, [], [], mpopt) t_is(cp[1, 0], 59.194, 3, t) t = 'price of decommited gen, auction_type = 2' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1120, 200, [], [], mpopt) t_is(cp[1, 0], cp5[1, 0] * fro_X, 3, t) t = 'price of decommited gen, auction_type = 3' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1130, 200, [], [], mpopt) t_is(cp[1, 0], cp5[1, 0] * lab_X, 3, t) t = 'price of decommited gen, auction_type = 4' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1140, 200, [], [], mpopt) t_is(cp[1, 0], cp5[1, 0] * frb_X, 3, t) t = 'price of decommited gen, auction_type = 6' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1160, 200, [], [], mpopt) t_is(cp[1, 0], cp5[1, 0] * fro_X, 3, t) t = 'price of decommited gen, auction_type = 7' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1170, 200, [], [], mpopt) t_is(cp[1, 0], cp5[1, 0] * (lao_X + lab_X) / 2, 3, t) t = 'price of decommited gen, auction_type = 0' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1100, 200, [], [], mpopt) t_is(cp[1, 0], 120, 3, t) t = 'single block, marginal offer @ $50, auction_type = 5' q = array([[60], [36], [36], [36], [36], [36], [30], [10], [20]]) p = array([[50], [40], [42], [44], [46], [48], [100], [100], [100]]) MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1150, 100, [], [], mpopt) t_is(cq[G[0]], 35.32, 2, t) t_is(cq[G[1:6]], q[G[1:6]], 8, [t, ' : gen qtys']) t_is(cp[G[0]], 50, 4, t) t_is(cq[L], q[L], 8, [t, ' : load qtys']) t_is(cp[L[1], :], 54.03, 2, t) t_is(cp[G], bus.lam_P[Gbus], 8, [t, ' : gen prices']) Qfudge = zeros(p.shape) Qfudge[L, :] = diag(gen.Qg[L] / gen.Pg[L] * bus.lam_Q[Lbus]) * ones( p[L, :].shape) t_is(cp[L], bus.lam_P[Lbus] + Qfudge[L, 0], 8, [t, ' : load prices']) t_end()
def pips(f_fcn, x0=None, A=None, l=None, u=None, xmin=None, xmax=None, gh_fcn=None, hess_fcn=None, opt=None): """Primal-dual interior point method for NLP (nonlinear programming). Minimize a function F(X) beginning from a starting point M{x0}, subject to optional linear and nonlinear constraints and variable bounds:: min f(x) x subject to:: g(x) = 0 (nonlinear equalities) h(x) <= 0 (nonlinear inequalities) l <= A*x <= u (linear constraints) xmin <= x <= xmax (variable bounds) Note: The calling syntax is almost identical to that of FMINCON from MathWorks' Optimization Toolbox. The main difference is that the linear constraints are specified with C{A}, C{L}, C{U} instead of C{A}, C{B}, C{Aeq}, C{Beq}. The functions for evaluating the objective function, constraints and Hessian are identical. Example from U{http://en.wikipedia.org/wiki/Nonlinear_programming}: >>> from numpy import array, r_, float64, dot >>> from scipy.sparse import csr_matrix >>> def f2(x): ... f = -x[0] * x[1] - x[1] * x[2] ... df = -r_[x[1], x[0] + x[2], x[1]] ... # actually not used since 'hess_fcn' is provided ... d2f = -array([[0, 1, 0], [1, 0, 1], [0, 1, 0]], float64) ... return f, df, d2f >>> def gh2(x): ... h = dot(array([[1, -1, 1], ... [1, 1, 1]]), x**2) + array([-2.0, -10.0]) ... dh = 2 * csr_matrix(array([[ x[0], x[0]], ... [-x[1], x[1]], ... [ x[2], x[2]]])) ... g = array([]) ... dg = None ... return h, g, dh, dg >>> def hess2(x, lam, cost_mult=1): ... mu = lam["ineqnonlin"] ... a = r_[dot(2 * array([1, 1]), mu), -1, 0] ... b = r_[-1, dot(2 * array([-1, 1]), mu),-1] ... c = r_[0, -1, dot(2 * array([1, 1]), mu)] ... Lxx = csr_matrix(array([a, b, c])) ... return Lxx >>> x0 = array([1, 1, 0], float64) >>> solution = pips(f2, x0, gh_fcn=gh2, hess_fcn=hess2) >>> round(solution["f"], 11) == -7.07106725919 True >>> solution["output"]["iterations"] 8 Ported by Richard Lincoln from the MATLAB Interior Point Solver (MIPS) (v1.9) by Ray Zimmerman. MIPS is distributed as part of the MATPOWER project, developed at the Power System Engineering Research Center (PSERC) (PSERC), Cornell. See U{http://www.pserc.cornell.edu/matpower/} for more info. MIPS was ported by Ray Zimmerman from C code written by H. Wang for his PhD dissertation: - "On the Computation and Application of Multi-period Security-Constrained Optimal Power Flow for Real-time Electricity Market Operations", Cornell University, May 2007. See also: - H. Wang, C. E. Murillo-Sanchez, R. D. Zimmerman, R. J. Thomas, "On Computational Issues of Market-Based Optimal Power Flow", IEEE Transactions on Power Systems, Vol. 22, No. 3, Aug. 2007, pp. 1185-1193. All parameters are optional except C{f_fcn} and C{x0}. @param f_fcn: Function that evaluates the objective function, its gradients and Hessian for a given value of M{x}. If there are nonlinear constraints, the Hessian information is provided by the 'hess_fcn' argument and is not required here. @type f_fcn: callable @param x0: Starting value of optimization vector M{x}. @type x0: array @param A: Optional linear constraints. @type A: csr_matrix @param l: Optional linear constraints. Default values are M{-Inf}. @type l: array @param u: Optional linear constraints. Default values are M{Inf}. @type u: array @param xmin: Optional lower bounds on the M{x} variables, defaults are M{-Inf}. @type xmin: array @param xmax: Optional upper bounds on the M{x} variables, defaults are M{Inf}. @type xmax: array @param gh_fcn: Function that evaluates the optional nonlinear constraints and their gradients for a given value of M{x}. @type gh_fcn: callable @param hess_fcn: Handle to function that computes the Hessian of the Lagrangian for given values of M{x}, M{lambda} and M{mu}, where M{lambda} and M{mu} are the multipliers on the equality and inequality constraints, M{g} and M{h}, respectively. @type hess_fcn: callable @param opt: optional options dictionary with the following keys, all of which are also optional (default values shown in parentheses) - C{verbose} (False) - Controls level of progress output displayed - C{feastol} (1e-6) - termination tolerance for feasibility condition - C{gradtol} (1e-6) - termination tolerance for gradient condition - C{comptol} (1e-6) - termination tolerance for complementarity condition - C{costtol} (1e-6) - termination tolerance for cost condition - C{max_it} (150) - maximum number of iterations - C{step_control} (False) - set to True to enable step-size control - C{max_red} (20) - maximum number of step-size reductions if step-control is on - C{cost_mult} (1.0) - cost multiplier used to scale the objective function for improved conditioning. Note: This value is also passed as the 3rd argument to the Hessian evaluation function so that it can appropriately scale the objective function term in the Hessian of the Lagrangian. @type opt: dict @rtype: dict @return: The solution dictionary has the following keys: - C{x} - solution vector - C{f} - final objective function value - C{converged} - exit status - True = first order optimality conditions satisfied - False = maximum number of iterations reached - None = numerically failed - C{output} - output dictionary with keys: - C{iterations} - number of iterations performed - C{hist} - list of arrays with trajectories of the following: feascond, gradcond, compcond, costcond, gamma, stepsize, obj, alphap, alphad - C{message} - exit message - C{lmbda} - dictionary containing the Langrange and Kuhn-Tucker multipliers on the constraints, with keys: - C{eqnonlin} - nonlinear equality constraints - C{ineqnonlin} - nonlinear inequality constraints - C{mu_l} - lower (left-hand) limit on linear constraints - C{mu_u} - upper (right-hand) limit on linear constraints - C{lower} - lower bound on optimization variables - C{upper} - upper bound on optimization variables @see: U{http://www.pserc.cornell.edu/matpower/} @author: Ray Zimmerman (PSERC Cornell) """ if isinstance(f_fcn, dict): ## problem dict p = f_fcn f_fcn = p['f_fcn'] x0 = p['x0'] if 'opt' in p: opt = p['opt'] if 'hess_fcn' in p: hess_fcn = p['hess_fcn'] if 'gh_fcn' in p: gh_fcn = p['gh_fcn'] if 'xmax' in p: xmax = p['xmax'] if 'xmin' in p: xmin = p['xmin'] if 'u' in p: u = p['u'] if 'l' in p: l = p['l'] if 'A' in p: A = p['A'] nx = x0.shape[0] # number of variables nA = A.shape[0] if A is not None else 0 # number of original linear constr # default argument values if l is None or len(l) == 0: l = -Inf * ones(nA) if u is None or len(u) == 0: u = Inf * ones(nA) if xmin is None or len(xmin) == 0: xmin = -Inf * ones(x0.shape[0]) if xmax is None or len(xmax) == 0: xmax = Inf * ones(x0.shape[0]) if gh_fcn is None: nonlinear = False gn = array([]) hn = array([]) else: nonlinear = True if opt is None: opt = {} # options if "feastol" not in opt: opt["feastol"] = 1e-06 if "gradtol" not in opt: opt["gradtol"] = 1e-06 if "comptol" not in opt: opt["comptol"] = 1e-06 if "costtol" not in opt: opt["costtol"] = 1e-06 if "max_it" not in opt: opt["max_it"] = 150 if "max_red" not in opt: opt["max_red"] = 20 if "step_control" not in opt: opt["step_control"] = False if "cost_mult" not in opt: opt["cost_mult"] = 1 if "verbose" not in opt: opt["verbose"] = 0 # initialize history hist = [] # constants xi = 0.99995 sigma = 0.1 z0 = 1 alpha_min = 1e-8 rho_min = 0.95 rho_max = 1.05 mu_threshold = 1e-5 # initialize i = 0 # iteration counter converged = False # flag eflag = False # exit flag # add var limits to linear constraints eyex = eye(nx, nx, format="csr") AA = eyex if A is None else vstack([eyex, A], "csr") ll = r_[xmin, l] uu = r_[xmax, u] # split up linear constraints ieq = find(absolute(uu - ll) <= EPS) igt = find((uu >= 1e10) & (ll > -1e10)) ilt = find((ll <= -1e10) & (uu < 1e10)) ibx = find((absolute(uu - ll) > EPS) & (uu < 1e10) & (ll > -1e10)) # zero-sized sparse matrices unsupported Ae = AA[ieq, :] if len(ieq) else None if len(ilt) or len(igt) or len(ibx): idxs = [(1, ilt), (-1, igt), (1, ibx), (-1, ibx)] Ai = vstack([sig * AA[idx, :] for sig, idx in idxs if len(idx)], 'csr') else: Ai = None be = uu[ieq] bi = r_[uu[ilt], -ll[igt], uu[ibx], -ll[ibx]] # evaluate cost f(x0) and constraints g(x0), h(x0) x = x0 f, df = f_fcn(x) # cost f = f * opt["cost_mult"] df = df * opt["cost_mult"] if nonlinear: hn, gn, dhn, dgn = gh_fcn(x) # nonlinear constraints h = hn if Ai is None else r_[hn, Ai * x - bi] # inequality constraints g = gn if Ae is None else r_[gn, Ae * x - be] # equality constraints if (dhn is None) and (Ai is None): dh = None elif dhn is None: dh = Ai.T elif Ai is None: dh = dhn else: dh = hstack([dhn, Ai.T]) if (dgn is None) and (Ae is None): dg = None elif dgn is None: dg = Ae.T elif Ae is None: dg = dgn else: dg = hstack([dgn, Ae.T]) else: h = -bi if Ai is None else Ai * x - bi # inequality constraints g = -be if Ae is None else Ae * x - be # equality constraints dh = None if Ai is None else Ai.T # 1st derivative of inequalities dg = None if Ae is None else Ae.T # 1st derivative of equalities # some dimensions neq = g.shape[0] # number of equality constraints niq = h.shape[0] # number of inequality constraints neqnln = gn.shape[0] # number of nonlinear equality constraints niqnln = hn.shape[0] # number of nonlinear inequality constraints nlt = len(ilt) # number of upper bounded linear inequalities ngt = len(igt) # number of lower bounded linear inequalities nbx = len(ibx) # number of doubly bounded linear inequalities # initialize gamma, lam, mu, z, e gamma = 1 # barrier coefficient lam = zeros(neq) z = z0 * ones(niq) mu = z0 * ones(niq) k = find(h < -z0) z[k] = -h[k] k = find((gamma / z) > z0) mu[k] = gamma / z[k] e = ones(niq) # check tolerance f0 = f if opt["step_control"]: L = f + dot(lam, g) + dot(mu, h + z) - gamma * sum(log(z)) Lx = df.copy() Lx = Lx + dg * lam if dg is not None else Lx Lx = Lx + dh * mu if dh is not None else Lx maxh = zeros(1) if len(h) == 0 else max(h) gnorm = norm(g, Inf) if len(g) else 0.0 lam_norm = norm(lam, Inf) if len(lam) else 0.0 mu_norm = norm(mu, Inf) if len(mu) else 0.0 znorm = norm(z, Inf) if len(z) else 0.0 feascond = \ max([gnorm, maxh]) / (1 + max([norm(x, Inf), znorm])) gradcond = \ norm(Lx, Inf) / (1 + max([lam_norm, mu_norm])) compcond = dot(z, mu) / (1 + norm(x, Inf)) costcond = absolute(f - f0) / (1 + absolute(f0)) # save history hist.append({ 'feascond': feascond, 'gradcond': gradcond, 'compcond': compcond, 'costcond': costcond, 'gamma': gamma, 'stepsize': 0, 'obj': f / opt["cost_mult"], 'alphap': 0, 'alphad': 0 }) if opt["verbose"]: s = '-sc' if opt["step_control"] else '' v = pipsver('all') print(('Python Interior Point Solver - PIPS%s, Version %s, %s' % (s, v['Version'], v['Date']))) if opt['verbose'] > 1: print(" it objective step size feascond gradcond " "compcond costcond ") print("---- ------------ --------- ------------ ------------ " "------------ ------------") print(("%3d %12.8g %10s %12g %12g %12g %12g" % (i, (f / opt["cost_mult"]), "", feascond, gradcond, compcond, costcond))) if feascond < opt["feastol"] and gradcond < opt["gradtol"] and \ compcond < opt["comptol"] and costcond < opt["costtol"]: converged = True if opt["verbose"]: print("Converged!") # do Newton iterations while (not converged) and (i < opt["max_it"]): # update iteration counter i += 1 # compute update step lmbda = { "eqnonlin": lam[list(range(neqnln))], "ineqnonlin": mu[list(range(niqnln))] } if nonlinear: if hess_fcn is None: print("pips: Hessian evaluation via finite differences " "not yet implemented.\nPlease provide " "your own hessian evaluation function.") Lxx = hess_fcn(x, lmbda, opt["cost_mult"]) else: _, _, d2f = f_fcn(x, True) # cost Lxx = d2f * opt["cost_mult"] rz = list(range(len(z))) zinvdiag = sparse((1.0 / z, (rz, rz))) if len(z) else None rmu = list(range(len(mu))) mudiag = sparse((mu, (rmu, rmu))) if len(mu) else None dh_zinv = None if dh is None else dh * zinvdiag M = Lxx if dh is None else Lxx + dh_zinv * mudiag * dh.T N = Lx if dh is None else Lx + dh_zinv * (mudiag * h + gamma * e) Ab = sparse(M) if dg is None else vstack( [hstack([M, dg]), hstack([dg.T, sparse((neq, neq))])]) bb = r_[-N, -g] dxdlam = spsolve(Ab.tocsr(), bb) if any(isnan(dxdlam)): if opt["verbose"]: print('\nNumerically Failed\n') eflag = -1 break dx = dxdlam[:nx] dlam = dxdlam[nx:nx + neq] dz = -h - z if dh is None else -h - z - dh.T * dx dmu = -mu if dh is None else -mu + zinvdiag * (gamma * e - mudiag * dz) # optional step-size control sc = False if opt["step_control"]: x1 = x + dx # evaluate cost, constraints, derivatives at x1 f1, df1 = f_fcn(x1) # cost f1 = f1 * opt["cost_mult"] df1 = df1 * opt["cost_mult"] if nonlinear: hn1, gn1, dhn1, dgn1 = gh_fcn(x1) # nonlinear constraints h1 = hn1 if Ai is None else r_[hn1, Ai * x1 - bi] # ieq constraints g1 = gn1 if Ae is None else r_[gn1, Ae * x1 - be] # eq constraints # 1st der of ieq if (dhn1 is None) and (Ai is None): dh1 = None elif dhn1 is None: dh1 = Ai.T elif Ai is None: dh1 = dhn1 else: dh1 = hstack([dhn1, Ai.T]) # 1st der of eqs if (dgn1 is None) and (Ae is None): dg1 = None elif dgn is None: dg1 = Ae.T elif Ae is None: dg1 = dgn1 else: dg1 = hstack([dgn1, Ae.T]) else: h1 = -bi if Ai is None else Ai * x1 - bi # inequality constraints g1 = -be if Ae is None else Ae * x1 - be # equality constraints dh1 = dh ## 1st derivative of inequalities dg1 = dg ## 1st derivative of equalities # check tolerance Lx1 = df1 Lx1 = Lx1 + dg1 * lam if dg1 is not None else Lx1 Lx1 = Lx1 + dh1 * mu if dh1 is not None else Lx1 maxh1 = zeros(1) if len(h1) == 0 else max(h1) g1norm = norm(g1, Inf) if len(g1) else 0.0 lam1_norm = norm(lam, Inf) if len(lam) else 0.0 mu1_norm = norm(mu, Inf) if len(mu) else 0.0 z1norm = norm(z, Inf) if len(z) else 0.0 feascond1 = max([ g1norm, maxh1 ]) / \ (1 + max([ norm(x1, Inf), z1norm ])) gradcond1 = norm(Lx1, Inf) / (1 + max([lam1_norm, mu1_norm])) if (feascond1 > feascond) and (gradcond1 > gradcond): sc = True if sc: alpha = 1.0 for j in range(opt["max_red"]): dx1 = alpha * dx x1 = x + dx1 f1, _ = f_fcn(x1) # cost f1 = f1 * opt["cost_mult"] if nonlinear: hn1, gn1, _, _ = gh_fcn(x1) # nonlinear constraints h1 = hn1 if Ai is None else r_[ hn1, Ai * x1 - bi] # inequality constraints g1 = gn1 if Ae is None else r_[gn1, Ae * x1 - be] # equality constraints else: h1 = -bi if Ai is None else Ai * x1 - bi # inequality constraints g1 = -be if Ae is None else Ae * x1 - be # equality constraints L1 = f1 + dot(lam, g1) + dot(mu, h1 + z) - gamma * sum(log(z)) if opt["verbose"] > 2: print((" %3d %10.5f" % (-j, norm(dx1)))) rho = (L1 - L) / (dot(Lx, dx1) + 0.5 * dot(dx1, Lxx * dx1)) if (rho > rho_min) and (rho < rho_max): break else: alpha = alpha / 2.0 dx = alpha * dx dz = alpha * dz dlam = alpha * dlam dmu = alpha * dmu # do the update k = find(dz < 0.0) alphap = min([xi * min(z[k] / -dz[k]), 1]) if len(k) else 1.0 k = find(dmu < 0.0) alphad = min([xi * min(mu[k] / -dmu[k]), 1]) if len(k) else 1.0 x = x + alphap * dx z = z + alphap * dz lam = lam + alphad * dlam mu = mu + alphad * dmu if niq > 0: gamma = sigma * dot(z, mu) / niq # evaluate cost, constraints, derivatives f, df = f_fcn(x) # cost f = f * opt["cost_mult"] df = df * opt["cost_mult"] if nonlinear: hn, gn, dhn, dgn = gh_fcn(x) # nln constraints # g = gn if Ai is None else r_[gn, Ai * x - bi] # ieq constraints # h = hn if Ae is None else r_[hn, Ae * x - be] # eq constraints h = hn if Ai is None else r_[hn, Ai * x - bi] # ieq constr g = gn if Ae is None else r_[gn, Ae * x - be] # eq constr if (dhn is None) and (Ai is None): dh = None elif dhn is None: dh = Ai.T elif Ai is None: dh = dhn else: dh = hstack([dhn, Ai.T]) if (dgn is None) and (Ae is None): dg = None elif dgn is None: dg = Ae.T elif Ae is None: dg = dgn else: dg = hstack([dgn, Ae.T]) else: h = -bi if Ai is None else Ai * x - bi # inequality constraints g = -be if Ae is None else Ae * x - be # equality constraints # 1st derivatives are constant, still dh = Ai.T, dg = Ae.T Lx = df Lx = Lx + dg * lam if dg is not None else Lx Lx = Lx + dh * mu if dh is not None else Lx if len(h) == 0: maxh = zeros(1) else: maxh = max(h) gnorm = norm(g, Inf) if len(g) else 0.0 lam_norm = norm(lam, Inf) if len(lam) else 0.0 mu_norm = norm(mu, Inf) if len(mu) else 0.0 znorm = norm(z, Inf) if len(z) else 0.0 feascond = \ max([gnorm, maxh]) / (1 + max([norm(x, Inf), znorm])) gradcond = \ norm(Lx, Inf) / (1 + max([lam_norm, mu_norm])) compcond = dot(z, mu) / (1 + norm(x, Inf)) costcond = float(absolute(f - f0) / (1 + absolute(f0))) hist.append({ 'feascond': feascond, 'gradcond': gradcond, 'compcond': compcond, 'costcond': costcond, 'gamma': gamma, 'stepsize': norm(dx), 'obj': f / opt["cost_mult"], 'alphap': alphap, 'alphad': alphad }) if opt["verbose"] > 1: print(("%3d %12.8g %10.5g %12g %12g %12g %12g" % (i, (f / opt["cost_mult"]), norm(dx), feascond, gradcond, compcond, costcond))) if feascond < opt["feastol"] and gradcond < opt["gradtol"] and \ compcond < opt["comptol"] and costcond < opt["costtol"]: converged = True if opt["verbose"]: print("Converged!") else: if any(isnan(x)) or (alphap < alpha_min) or \ (alphad < alpha_min) or (gamma < EPS) or (gamma > 1.0 / EPS): if opt["verbose"]: print("Numerically failed.") eflag = -1 break f0 = f if opt["step_control"]: L = f + dot(lam, g) + dot(mu, (h + z)) - gamma * sum(log(z)) if opt["verbose"]: if not converged: print(("Did not converge in %d iterations." % i)) # package results if eflag != -1: eflag = converged if eflag == 0: message = 'Did not converge' elif eflag == 1: message = 'Converged' elif eflag == -1: message = 'Numerically failed' else: raise output = {"iterations": i, "hist": hist, "message": message} # zero out multipliers on non-binding constraints mu[find((h < -opt["feastol"]) & (mu < mu_threshold))] = 0.0 # un-scale cost and prices f = f / opt["cost_mult"] lam = lam / opt["cost_mult"] mu = mu / opt["cost_mult"] # re-package multipliers into struct lam_lin = lam[neqnln:neq] # lambda for linear constraints mu_lin = mu[niqnln:niq] # mu for linear constraints kl = find(lam_lin < 0.0) # lower bound binding ku = find(lam_lin > 0.0) # upper bound binding mu_l = zeros(nx + nA) mu_l[ieq[kl]] = -lam_lin[kl] mu_l[igt] = mu_lin[nlt:nlt + ngt] mu_l[ibx] = mu_lin[nlt + ngt + nbx:nlt + ngt + nbx + nbx] mu_u = zeros(nx + nA) mu_u[ieq[ku]] = lam_lin[ku] mu_u[ilt] = mu_lin[:nlt] mu_u[ibx] = mu_lin[nlt + ngt:nlt + ngt + nbx] lmbda = { 'mu_l': mu_l[nx:], 'mu_u': mu_u[nx:], 'lower': mu_l[:nx], 'upper': mu_u[:nx] } if niqnln > 0: lmbda['ineqnonlin'] = mu[:niqnln] if neqnln > 0: lmbda['eqnonlin'] = lam[:neqnln] # lmbda = {"eqnonlin": lam[:neqnln], 'ineqnonlin': mu[:niqnln], # "mu_l": mu_l[nx:], "mu_u": mu_u[nx:], # "lower": mu_l[:nx], "upper": mu_u[:nx]} solution = { "x": x, "f": f, "eflag": converged, "output": output, "lmbda": lmbda } return solution
def run_inversion(home, project_name, run_name, fault_name, model_name, GF_list, G_from_file, G_name, epicenter, rupture_speed, num_windows, reg_spatial, reg_temporal, nfaults, beta, decimate, bandpass, solver, bounds, weight=False, Ltype=2, target_moment=None, data_vector=None, onset_file=None): ''' Assemble G and d, determine smoothing and run the inversion ''' from mudpy import inverse as inv from mudpy.forward import get_mu_and_area from numpy import zeros, dot, array, squeeze, expand_dims, empty, tile, eye, ones, arange, load, size, genfromtxt from numpy import where, sort, r_ from numpy.linalg import lstsq from scipy.sparse import csr_matrix as sparse from scipy.optimize import nnls from datetime import datetime import gc from matplotlib import path t1 = datetime.now() #Get data vector if data_vector == None: d = inv.getdata(home, project_name, GF_list, decimate, bandpass=bandpass) else: d = load(data_vector) #Get GFs G = inv.getG(home, project_name, fault_name, model_name, GF_list, G_from_file, G_name, epicenter, rupture_speed, num_windows, decimate, bandpass, onset_file=onset_file) print(G.shape) gc.collect() #Get data weights if weight == True: print('Applying data weights') w = inv.get_data_weights(home, project_name, GF_list, d, decimate) W = empty(G.shape) W = tile(w, (G.shape[1], 1)).T WG = empty(G.shape) WG = W * G wd = w * d.squeeze() wd = expand_dims(wd, axis=1) #Clear up extraneous variables W = None w = None #Define inversion quantities x = WG.transpose().dot(wd) print('Computing G\'G') K = (WG.T).dot(WG) else: #Define inversion quantities if no weightd x = G.transpose().dot(d) print('Computing G\'G') K = (G.T).dot(G) #Get regularization matrices (set to 0 matrix if not needed) static = False #Is it jsut a static inversion? if size(reg_spatial) > 1: if Ltype == 2: #Laplacian smoothing Ls = inv.getLs(home, project_name, fault_name, nfaults, num_windows, bounds) elif Ltype == 0: #Tikhonov smoothing N = nfaults[0] * nfaults[ 1] * num_windows * 2 #Get total no. of model parameters Ls = eye(N) elif Ltype == 3: #moment regularization N = nfaults[0] * nfaults[ 1] * num_windows * 2 #Get total no. of model parameters Ls = ones((1, N)) #Add rigidity and subfault area mu, area = get_mu_and_area(home, project_name, fault_name, model_name) istrike = arange(0, N, 2) Ls[0, istrike] = area * mu idip = arange(1, N, 2) Ls[0, idip] = area * mu #Modify inversion quantities x = x + Ls.T.dot(target_moment) else: print('ERROR: Unrecognized regularization type requested') return Ninversion = len(reg_spatial) else: Ls = zeros(K.shape) reg_spatial = array([0.]) Ninversion = 1 if size(reg_temporal) > 1: Lt = inv.getLt(home, project_name, fault_name, num_windows) Ninversion = len(reg_temporal) * Ninversion else: Lt = zeros(K.shape) reg_temporal = array([0.]) static = True #Make L's sparse Ls = sparse(Ls) Lt = sparse(Lt) #Get regularization tranposes for ABIC LsLs = Ls.transpose().dot(Ls) LtLt = Lt.transpose().dot(Lt) #inflate Ls = Ls.todense() Lt = Lt.todense() LsLs = LsLs.todense() LtLt = LtLt.todense() #off we go dt = datetime.now() - t1 print('Preprocessing wall time was ' + str(dt)) print('\n--- RUNNING INVERSIONS ---\n') ttotal = datetime.now() kout = 0 for kt in range(len(reg_temporal)): for ks in range(len(reg_spatial)): t1 = datetime.now() lambda_spatial = reg_spatial[ks] lambda_temporal = reg_temporal[kt] print('Running inversion ' + str(kout + 1) + ' of ' + str(Ninversion) + ' at regularization levels: ls =' + repr(lambda_spatial) + ' , lt = ' + repr(lambda_temporal)) if static == True: #Only statics inversion no Lt matrix Kinv = K + (lambda_spatial**2) * LsLs Lt = eye(len(K)) LtLt = Lt.T.dot(Lt) else: #Mixed inversion Kinv = K + (lambda_spatial**2) * LsLs + (lambda_temporal** 2) * LtLt if solver.lower() == 'lstsq': sol, res, rank, s = lstsq(Kinv, x) elif solver.lower() == 'nnls': x = squeeze(x.T) try: sol, res = nnls(Kinv, x) except: print('+++ WARNING: No solution found, writting zeros.') sol = zeros(G.shape[1]) x = expand_dims(x, axis=1) sol = expand_dims(sol, axis=1) else: print('ERROR: Unrecognized solver \'' + solver + '\'') #Force faults outside a polygon to be zero print('WARNING: Using fault polygon to force solutions to zero') #load faulkt fault = genfromtxt(home + project_name + '/data/model_info/' + fault_name) polygon = genfromtxt( '/Users/dmelgarm/Oaxaca2020/etc/zero_fault.txt') polygon = path.Path(polygon) i = where(polygon.contains_points(fault[:, 1:3]) == False)[0] i = sort(r_[i * 2, i * 2 + 1]) N = nfaults[0] * 2 i = r_[i, i + N, i + 2 * N, i + 3 * N] sol[i] = 0 #Compute synthetics ds = dot(G, sol) #Get stats L2, Lmodel = inv.get_stats(Kinv, sol, x) VR, L2data = inv.get_VR(home, project_name, GF_list, sol, d, ds, decimate, WG, wd) #VR=inv.get_VR(WG,sol,wd) #ABIC=inv.get_ABIC(WG,K,sol,wd,lambda_spatial,lambda_temporal,Ls,LsLs,Lt,LtLt) ABIC = inv.get_ABIC(G, K, sol, d, lambda_spatial, lambda_temporal, Ls, LsLs, Lt, LtLt) #Get moment Mo, Mw = inv.get_moment(home, project_name, fault_name, model_name, sol) #If a rotational offset was applied then reverse it for output to file if beta != 0: sol = inv.rot2ds(sol, beta) #Write log inv.write_log(home, project_name, run_name, kout, rupture_speed, num_windows, lambda_spatial, lambda_temporal, beta, L2, Lmodel, VR, ABIC, Mo, Mw, model_name, fault_name, G_name, GF_list, solver, L2data) #Write output to file inv.write_synthetics(home, project_name, run_name, GF_list, G, sol, ds, kout, decimate) inv.write_model(home, project_name, run_name, fault_name, model_name, rupture_speed, num_windows, epicenter, sol, kout, onset_file=onset_file) kout += 1 dt1 = datetime.now() - t1 dt2 = datetime.now() - ttotal print('... inversion wall time was ' + str(dt1) + ', total wall time elapsed is ' + str(dt2))
def qps_cplex(H, c, A, l, u, xmin, xmax, x0, opt): """Quadratic Program Solver based on CPLEX. A wrapper function providing a PYPOWER standardized interface for using C{cplexqp} or C{cplexlp} to solve the following QP (quadratic programming) problem:: min 1/2 X'*H*x + c'*x x subject to:: l <= A*x <= u (linear constraints) xmin <= x <= xmax (variable bounds) Inputs (all optional except C{H}, C{c}, C{A} and C{l}): - C{H} : matrix (possibly sparse) of quadratic cost coefficients - C{c} : vector of linear cost coefficients - C{A, l, u} : define the optional linear constraints. Default values for the elements of L and U are -Inf and Inf, respectively. - C{xmin, xmax} : optional lower and upper bounds on the C{x} variables, defaults are -Inf and Inf, respectively. - C{x0} : optional starting value of optimization vector C{x} - C{opt} : optional options structure with the following fields, all of which are also optional (default values shown in parentheses) - C{verbose} (0) - controls level of progress output displayed - 0 = no progress output - 1 = some progress output - 2 = verbose progress output - C{cplex_opt} - options dict for CPLEX, value in verbose overrides these options - C{problem} : The inputs can alternatively be supplied in a single C{problem} dict with fields corresponding to the input arguments described above: C{H, c, A, l, u, xmin, xmax, x0, opt} Outputs: - C{x} : solution vector - C{f} : final objective function value - C{exitflag} : CPLEXQP/CPLEXLP exit flag (see C{cplexqp} and C{cplexlp} documentation for details) - C{output} : CPLEXQP/CPLEXLP output dict (see C{cplexqp} and C{cplexlp} documentation for details) - C{lmbda} : dict containing the Langrange and Kuhn-Tucker multipliers on the constraints, with fields: - mu_l - lower (left-hand) limit on linear constraints - mu_u - upper (right-hand) limit on linear constraints - lower - lower bound on optimization variables - upper - upper bound on optimization variables @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln """ ##----- input argument handling ----- ## gather inputs if isinstance(H, dict): ## problem struct p = H if 'opt' in p: opt = p['opt'] if 'x0' in p: x0 = p['x0'] if 'xmax' in p: xmax = p['xmax'] if 'xmin' in p: xmin = p['xmin'] if 'u' in p: u = p['u'] if 'l' in p: l = p['l'] if 'A' in p: A = p['A'] if 'c' in p: c = p['c'] if 'H' in p: H = p['H'] else: ## individual args assert H is not None assert c is not None assert A is not None assert l is not None if opt is None: opt = {} # if x0 is None: # x0 = array([]) # if xmax is None: # xmax = array([]) # if xmin is None: # xmin = array([]) ## define nx, set default values for missing optional inputs if len(H) == 0 or not any(any(H)): if len(A) == 0 and len(xmin) == 0 and len(xmax) == 0: stderr.write( 'qps_cplex: LP problem must include constraints or variable bounds\n' ) else: if len(A) > 0: nx = shape(A)[1] elif len(xmin) > 0: nx = len(xmin) else: # if len(xmax) > 0 nx = len(xmax) else: nx = shape(H)[0] if len(c) == 0: c = zeros(nx) if len(A) > 0 and (len(l) == 0 or all(l == -Inf)) and \ (len(u) == 0 or all(u == Inf)): A = None ## no limits => no linear constraints nA = shape(A)[0] ## number of original linear constraints if len(u) == 0: ## By default, linear inequalities are ... u = Inf * ones(nA) ## ... unbounded above and ... if len(l) == 0: l = -Inf * ones(nA) ## ... unbounded below. if len(xmin) == 0: ## By default, optimization variables are ... xmin = -Inf * ones(nx) ## ... unbounded below and ... if len(xmax) == 0: xmax = Inf * ones(nx) ## ... unbounded above. if len(x0) == 0: x0 = zeros(nx) ## default options if 'verbose' in opt: verbose = opt['verbose'] else: verbose = 0 #if 'max_it' in opt: # max_it = opt['max_it'] #else: # max_it = 0 ## split up linear constraints ieq = find(abs(u - l) <= EPS) ## equality igt = find(u >= 1e10 & l > -1e10) ## greater than, unbounded above ilt = find(l <= -1e10 & u < 1e10) ## less than, unbounded below ibx = find((abs(u - l) > EPS) & (u < 1e10) & (l > -1e10)) Ae = A[ieq, :] be = u[ieq] Ai = r_[A[ilt, :], -A[igt, :], A[ibx, :] - A[ibx, :]] bi = r_[u[ilt], -l[igt], u[ibx], -l[ibx]] ## grab some dimensions nlt = len(ilt) ## number of upper bounded linear inequalities ngt = len(igt) ## number of lower bounded linear inequalities nbx = len(ibx) ## number of doubly bounded linear inequalities ## set up options struct for CPLEX if 'cplex_opt' in opt: cplex_opt = cplex_options(opt['cplex_opt']) else: cplex_opt = cplex_options cplex = Cplex('null') vstr = cplex.getVersion s, e, tE, m, t = re.compile(vstr, '(\d+\.\d+)\.') vnum = int(t[0][0]) vrb = max([0, verbose - 1]) cplex_opt['barrier']['display'] = vrb cplex_opt['conflict']['display'] = vrb cplex_opt['mip']['display'] = vrb cplex_opt['sifting']['display'] = vrb cplex_opt['simplex']['display'] = vrb cplex_opt['tune']['display'] = vrb if vrb and (vnum > 12.2): cplex_opt['diagnostics'] = 'on' #if max_it: # cplex_opt. ## not sure what to set here if len(Ai) == 0 and len(Ae) == 0: unconstrained = 1 Ae = sparse((1, nx)) be = 0 else: unconstrained = 0 ## call the solver if verbose: methods = [ 'default', 'primal simplex', 'dual simplex', 'network simplex', 'barrier', 'sifting', 'concurrent' ] if len(H) == 0 or not any(any(H)): if verbose: stdout.write('CPLEX Version %s -- %s LP solver\n' % (vstr, methods[cplex_opt['lpmethod'] + 1])) x, f, eflag, output, lam = \ cplexlp(c, Ai, bi, Ae, be, xmin, xmax, x0, cplex_opt) else: if verbose: stdout.write('CPLEX Version %s -- %s QP solver\n' % (vstr, methods[cplex_opt['qpmethod'] + 1])) ## ensure H is numerically symmetric if H != H.T: H = (H + H.T) / 2 x, f, eflag, output, lam = \ cplexqp(H, c, Ai, bi, Ae, be, xmin, xmax, x0, cplex_opt) ## check for empty results (in case optimization failed) if len(x) == 0: x = NaN * zeros(nx) if len(f) == 0: f = NaN if len(lam) == 0: lam['ineqlin'] = NaN * zeros(len(bi)) lam['eqlin'] = NaN * zeros(len(be)) lam['lower'] = NaN * zeros(nx) lam['upper'] = NaN * zeros(nx) mu_l = NaN * zeros(nA) mu_u = NaN * zeros(nA) else: mu_l = zeros(nA) mu_u = zeros(nA) if unconstrained: lam['eqlin'] = array([]) ## negate prices depending on version if vnum < 12.3: lam['eqlin'] = -lam['eqlin'] lam['ineqlin'] = -lam['ineqlin'] ## repackage lambdas kl = find(lam.eqlin < 0) ## lower bound binding ku = find(lam.eqlin > 0) ## upper bound binding mu_l[ieq[kl]] = -lam['eqlin'][kl] mu_l[igt] = lam['ineqlin'][nlt + arange(ngt)] mu_l[ibx] = lam['ineqlin'][nlt + ngt + nbx + arange(nbx)] mu_u[ieq[ku]] = lam['eqlin'][ku] mu_u[ilt] = lam['ineqlin'][:nlt] mu_u[ibx] = lam['ineqlin'][nlt + ngt + arange(nbx)] lmbda = { 'mu_l': mu_l, 'mu_u': mu_u, 'lower': lam.lower, 'upper': lam.upper } return x, f, eflag, output, lmbda
def total_load(bus, gen=None, load_zone=None, which_type=None): """Returns vector of total load in each load zone. @param bus: standard C{bus} matrix with C{nb} rows, where the fixed active and reactive loads are specified in columns C{PD} and C{QD} @param gen: (optional) standard C{gen} matrix with C{ng} rows, where the dispatchable loads are specified by columns C{PG}, C{QG}, C{PMIN}, C{QMIN} and C{QMAX} (in rows for which C{isload(GEN)} returns C{True}). If C{gen} is empty, it assumes there are no dispatchable loads. @param load_zone: (optional) C{nb} element vector where the value of each element is either zero or the index of the load zone to which the corresponding bus belongs. If C{load_zone(b) = k} then the loads at bus C{b} will added to the values of C{Pd[k]} and C{Qd[k]}. If C{load_zone} is empty, the default is defined as the areas specified in the C{bus} matrix, i.e. C{load_zone = bus[:, BUS_AREA]} and load will have dimension C{= max(bus[:, BUS_AREA])}. If C{load_zone = 'all'}, the result is a scalar with the total system load. @param which_type: (default is 'BOTH' if C{gen} is provided, else 'FIXED') - 'FIXED' : sum only fixed loads - 'DISPATCHABLE' : sum only dispatchable loads - 'BOTH' : sum both fixed and dispatchable loads @see: L{scale_load} @author: Ray Zimmerman (PSERC Cornell) """ nb = bus.shape[0] ## number of buses if gen is None: gen = array([]) if load_zone is None: load_zone = array([], int) ## fill out and check which_type if len(gen) == 0: which_type = 'FIXED' if (which_type == None) and (len(gen) > 0): which_type = 'BOTH' ## 'FIXED', 'DISPATCHABLE' or 'BOTH' if (which_type[0] != 'F') and (which_type[0] != 'D') and (which_type[0] != 'B'): stderr.write( "total_load: which_type should be 'FIXED, 'DISPATCHABLE or 'BOTH'\n" ) want_Q = True want_fixed = (which_type[0] == 'B') | (which_type[0] == 'F') want_disp = (which_type[0] == 'B') | (which_type[0] == 'D') ## initialize load_zone if isinstance(load_zone, str) and (load_zone == 'all'): load_zone = ones(nb, int) ## make a single zone of all buses elif len(load_zone) == 0: load_zone = bus[:, BUS_AREA].astype( int) ## use areas defined in bus data as zones nz = max(load_zone) ## number of load zones ## fixed load at each bus, & initialize dispatchable if want_fixed: Pdf = bus[:, PD] ## real power if want_Q: Qdf = bus[:, QD] ## reactive power else: Pdf = zeros(nb) ## real power if want_Q: Qdf = zeros(nb) ## reactive power ## dispatchable load at each bus if want_disp: ## need dispatchable ng = gen.shape[0] is_ld = isload(gen) & (gen[:, GEN_STATUS] > 0) ld = find(is_ld) ## create map of external bus numbers to bus indices i2e = bus[:, BUS_I].astype(int) e2i = zeros(max(i2e) + 1) e2i[i2e] = arange(nb) gbus = gen[:, GEN_BUS].astype(int) Cld = sparse((is_ld, (e2i[gbus], arange(ng))), (nb, ng)) Pdd = -Cld * gen[:, PMIN] ## real power if want_Q: Q = zeros(ng) Q[ld] = (gen[ld, QMIN] == 0) * gen[ld, QMAX] + \ (gen[ld, QMAX] == 0) * gen[ld, QMIN] Qdd = -Cld * Q ## reactive power else: Pdd = zeros(nb) if want_Q: Qdd = zeros(nb) ## compute load sums Pd = zeros(nz) if want_Q: Qd = zeros(nz) for k in range(1, nz + 1): idx = find(load_zone == k) Pd[k - 1] = sum(Pdf[idx]) + sum(Pdd[idx]) if want_Q: Qd[k - 1] = sum(Qdf[idx]) + sum(Qdd[idx]) return Pd, Qd
def t_runmarket(quiet=False): """Tests for code in C{runmkt}, C{smartmkt} and C{auction}. @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln """ n_tests = 20 t_begin(n_tests, quiet) try: from pypower.extras.smartmarket import runmarket except ImportError: t_skip(n_tests, 'smartmarket code not available') t_end; return ppc = loadcase('t_auction_case') ppopt = ppoption(OPF_ALG=560, OUT_ALL_LIM=1, OUT_BRANCH=0, OUT_SYS_SUM=0) ppopt = ppoption(ppopt, OUT_ALL=0, VERBOSE=1) #ppopt = ppoption(ppopt, OUT_GEN=1, OUT_BRANCH=0, OUT_SYS_SUM=0) offers = {'P': {}, 'Q': {}} bids = {'P': {}, 'Q': {}} offers['P']['qty'] = array([ [12, 24, 24], [12, 24, 24], [12, 24, 24], [12, 24, 24], [12, 24, 24], [12, 24, 24] ]) offers['P']['prc'] = array([ [20, 50, 60], [20, 40, 70], [20, 42, 80], [20, 44, 90], [20, 46, 75], [20, 48, 60] ]) bids['P']['qty'] = array([ [10, 10, 10], [10, 10, 10], [10, 10, 10] ]) bids['P']['prc'] = array([ [100, 70, 60], # [100, 64.3, 20], # [100, 30.64545, 0], [100, 50, 20], [100, 60, 50] ]) offers['Q']['qty'] = [ 60, 60, 60, 60, 60, 60, 0, 0, 0 ] offers['Q']['prc'] = [ 0, 0, 0, 0, 0, 3, 0, 0, 0 ] bids.Q['qty'] = [ 15, 15, 15, 15, 15, 15, 15, 12, 7.5 ] # bids.Q['prc'] = [ 0, 0, 0, 0, 0, 0, 0, 83.9056, 0 ] bids.Q['prc'] = [ 0, 0, 0, 0, 0, 0, 0, 20, 0 ] t = 'marginal Q offer, marginal PQ bid, auction_type = 5' mkt = {'auction_type': 5, 't': [], 'u0': [], 'lim': []} r, co, cb, _, _, _, _ = runmarket(ppc, offers, bids, mkt, ppopt) co5 = co.copy() cb5 = cb.copy() # [ co['P']['qty'] co['P']['prc'] ] # [ cb['P']['qty'] cb['P']['prc'] ] # [ co['Q']['qty'] co['Q']['prc'] ] # [ cb['Q']['qty'] cb['Q']['prc'] ] i2e = r['bus'][:, BUS_I] e2i = sparse((max(i2e), 1)) e2i[i2e] = range(r['bus'].size) G = find( isload(r['gen']) == 0 ) ## real generators L = find( isload(r['gen']) ) ## dispatchable loads Gbus = e2i[r['gen'][G, GEN_BUS]] Lbus = e2i[r['gen'][L, GEN_BUS]] t_is( co['P']['qty'], ones((6, 1)) * [12, 24, 0], 2, [t, ' : gen P quantities'] ) t_is( co['P']['prc'][0, :], 50.1578, 3, [t, ' : gen 1 P prices'] ) t_is( cb['P']['qty'], [[10, 10, 10], [10, 0.196, 0], [10, 10, 0]], 2, [t, ' : load P quantities'] ) t_is( cb['P']['prc'][1, :], 56.9853, 4, [t, ' : load 2 P price'] ) t_is( co['P']['prc'][:, 0], r['bus'][Gbus, LAM_P], 8, [t, ' : gen P prices'] ) t_is( cb['P']['prc'][:, 0], r['bus'][Lbus, LAM_P], 8, [t, ' : load P prices'] ) t_is( co['Q']['qty'], [4.2722, 11.3723, 14.1472, 22.8939, 36.7886, 12.3375, 0, 0, 0], 2, [t, ' : Q offer quantities'] ) t_is( co['Q']['prc'], [0, 0, 0, 0, 0, 3, 0.4861, 2.5367, 1.3763], 4, [t, ' : Q offer prices'] ) t_is( cb['Q']['qty'], [0, 0, 0, 0, 0, 0, 15, 4.0785, 5], 2, [t, ' : Q bid quantities'] ) t_is( cb['Q']['prc'], [0, 0, 0, 0, 0, 3, 0.4861, 2.5367, 1.3763], 4, [t, ' : Q bid prices'] ) t_is( co['Q']['prc'], r['bus'][[Gbus, Lbus], LAM_Q], 8, [t, ' : Q offer prices'] ) t_is( cb['Q']['prc'], co['Q']['prc'], 8, [t, ' : Q bid prices'] ) t = 'marginal Q offer, marginal PQ bid, auction_type = 0' mkt['auction_type'] = 0 r, co, cb, _, _, _, _ = runmarket(ppc, offers, bids, mkt, ppopt) t_is( co['P']['qty'], co5['P']['qty'], 8, [t, ' : gen P quantities'] ) t_is( cb['P']['qty'], cb5['P']['qty'], 8, [t, ' : load P quantities'] ) t_is( co['P']['prc'], offers['P']['prc'], 8, [t, ' : gen P prices'] ) t_is( cb['P']['prc'], bids['P']['prc'], 8, [t, ' : load P prices'] ) t_is( co['Q']['qty'], co5['Q']['qty'], 8, [t, ' : gen Q quantities'] ) t_is( cb['Q']['qty'], cb5['Q']['qty'], 8, [t, ' : load Q quantities'] ) t_is( co['Q']['prc'], offers['Q']['prc'], 8, [t, ' : gen Q prices'] ) t_is( cb['Q']['prc'], bids['Q']['prc'], 8, [t, ' : load Q prices'] ) t_end
def run(mpc): """ Gurobi based optimal power flow modelling and solution :param mpc: The input case of optimal power flow :return: obtained solution """ # Data format from pypower.idx_brch import F_BUS, T_BUS, BR_R, BR_X, TAP, SHIFT, BR_STATUS, RATE_A from pypower.idx_cost import MODEL, NCOST, PW_LINEAR, COST, POLYNOMIAL from pypower.idx_bus import BUS_TYPE, REF, VA, VM, PD, GS, VMAX, VMIN, BUS_I, QD from pypower.idx_gen import GEN_BUS, VG, PG, QG, PMAX, PMIN, QMAX, QMIN from pypower.ext2int import ext2int mpc = ext2int(mpc) baseMVA, bus, gen, branch, gencost = mpc["baseMVA"], mpc["bus"], mpc[ "gen"], mpc["branch"], mpc["gencost"] nb = shape(mpc['bus'])[0] # number of buses nl = shape(mpc['branch'])[0] # number of branches ng = shape(mpc['gen'])[0] # number of dispatchable injections f = branch[:, F_BUS] ## list of "from" buses t = branch[:, T_BUS] ## list of "to" buses i = range(nl) ## double set of row indices # Connection matrix Cf = sparse((ones(nl), (i, f)), (nl, nb)) Ct = sparse((ones(nl), (i, t)), (nl, nb)) Cg = sparse((ones(ng), (gen[:, GEN_BUS], range(ng))), (nb, ng)) Branch_R = branch[:, BR_R] Branch_X = branch[:, BR_X] Cf = Cf.T Ct = Ct.T # Obtain the boundary information Slmax = branch[:, RATE_A] / baseMVA Pij_l = -Slmax Qij_l = -Slmax Iij_l = zeros(nl) Vm_l = turn_to_power(bus[:, VMIN], 2) Pg_l = gen[:, PMIN] / baseMVA Qg_l = gen[:, QMIN] / baseMVA Pij_u = Slmax Qij_u = Slmax Iij_u = Slmax Vm_u = turn_to_power(bus[:, VMAX], 2) Pg_u = 2 * gen[:, PMAX] / baseMVA Qg_u = 2 * gen[:, QMAX] / baseMVA lx = concatenate([Pij_l, Qij_l, Iij_l, Vm_l, Pg_l, Qg_l]) ux = concatenate([Pij_u, Qij_u, Iij_u, Vm_u, Pg_u, Qg_u]) model = Model("OPF") # Define the decision variables x = {} nx = 3 * nl + nb + 2 * ng for i in range(nx): x[i] = model.addVar(lb=lx[i], ub=ux[i], vtype=GRB.CONTINUOUS) # Add system level constraints Aeq_p = hstack([ Ct - Cf, zeros((nb, nl)), -diag(Ct * Branch_R) * Ct, zeros((nb, nb)), Cg, zeros((nb, ng)) ]) beq_p = bus[:, PD] / baseMVA # Add constraints for each sub system Aeq_q = hstack([ zeros((nb, nl)), Ct - Cf, -diag(Ct * Branch_X) * Ct, zeros((nb, nb)), zeros((nb, ng)), Cg ]) beq_q = bus[:, QD] / baseMVA Aeq_KVL = hstack([ -2 * diags(Branch_R), -2 * diags(Branch_X), diags(turn_to_power(Branch_R, 2)) + diags(turn_to_power(Branch_X, 2)), Cf.T - Ct.T, zeros((nl, 2 * ng)) ]) beq_KVL = zeros(nl) Aeq = vstack([Aeq_p, Aeq_q, Aeq_KVL]) Aeq = Aeq.todense() beq = concatenate([beq_p, beq_q, beq_KVL]) neq = len(beq) for i in range(neq): expr = 0 for j in range(nx): expr += x[j] * Aeq[i, j] model.addConstr(lhs=expr, sense=GRB.EQUAL, rhs=beq[i]) for i in range(nl): model.addConstr(x[i] * x[i] + x[i + nl] * x[i + nl] <= x[i + 2 * nl] * x[f[i] + 3 * nl], name='"rc{0}"'.format(i)) obj = 0 for i in range(ng): obj += gencost[i, 4] * x[i + 3 * nl + nb] * x[ i + 3 * nl + nb] * baseMVA * baseMVA + gencost[i, 5] * x[ i + 3 * nl + nb] * baseMVA + gencost[i, 6] model.setObjective(obj) model.Params.OutputFlag = 0 model.Params.LogToConsole = 0 model.Params.DisplayInterval = 1 model.optimize() xx = [] for v in model.getVars(): xx.append(v.x) obj = obj.getValue() Pij = xx[0:nl] Qij = xx[nl + 0:2 * nl] Iij = xx[2 * nl:3 * nl] Vi = xx[3 * nl:3 * nl + nb] Pg = xx[3 * nl + nb:3 * nl + nb + ng] Qg = xx[3 * nl + nb + ng:3 * nl + nb + 2 * ng] # for i in range(nl): # branch indexing exchange # if branch[i, F_BUS] > branch[i, T_BUS]: # temp = branch[i, F_BUS] # branch[i, F_BUS] = branch[i, T_BUS] # branch[i, T_BUS] = temp f = branch[:, F_BUS] ## list of "from" buses t = branch[:, T_BUS] ## list of "to" buses # i = range(nl) ## double set of row indices area = ancestor_children_generation(f, t, range(nb)) Pi = Cg * Pg - bus[:, PD] / baseMVA Qi = Cg * Qg - bus[:, QD] / baseMVA for i in range(nb): # If the bus is the root bus, only the children information is required. if len(area[i]["Ai"]) == 0: print(i) expr = 0 for j in range(len(area[i]["Cbranch"][0])): expr += Pij[area[i]["Cbranch"][0][j]] print(expr - Pi[i]) expr = 0 for j in range(len(area[i]["Cbranch"][0])): expr += Qij[area[i]["Cbranch"][0][j]] print(expr - Qi[i]) elif len(area[i]["Cbranch"]) == 0: # This bus is the lead node print(i) print(Pij[area[i]["Abranch"][0][0]] - Iij[area[i]["Abranch"][0][0]] * Branch_R[area[i]["Abranch"][0][0]] + Pi[i]) print(Qij[area[i]["Abranch"][0][0]] - Iij[area[i]["Abranch"][0][0]] * Branch_X[area[i]["Abranch"][0][0]] + Qi[i]) print(Vi[int(area[i]["Ai"][0])] - Vi[i] - 2 * Branch_R[area[i]["Abranch"][0][0]] * Pij[area[i]["Abranch"][0][0]] - 2 * Branch_X[area[i]["Abranch"][0][0]] * Qij[area[i]["Abranch"][0][0]] + Iij[area[i]["Abranch"][0][0]] * (Branch_R[area[i]["Abranch"][0][0]]**2 + Branch_X[area[i]["Abranch"][0][0]]**2)) print( Pij[area[i]["Abranch"][0][0]] * Pij[area[i]["Abranch"][0][0]] + Qij[area[i]["Abranch"][0][0]] * Qij[area[i]["Abranch"][0][0]] <= Vi[int(area[i]["Ai"][0])] * Iij[area[i]["Abranch"][0][0]]) else: print(i) expr = 0 for j in range(len(area[i]["Cbranch"][0])): expr += Pij[area[i]["Cbranch"][0][j]] print(Pij[area[i]["Abranch"][0][0]] - Iij[area[i]["Abranch"][0][0]] * Branch_R[area[i]["Abranch"][0][0]] + Pi[i] - expr) expr = 0 for j in range(len(area[i]["Cbranch"][0])): expr += Qij[area[i]["Cbranch"][0][j]] print(Qij[area[i]["Abranch"][0][0]] - Iij[area[i]["Abranch"][0][0]] * Branch_X[area[i]["Abranch"][0][0]] + Qi[i] - expr) print(Vi[int(area[i]["Ai"][0])] - Vi[i] - 2 * Branch_R[area[i]["Abranch"][0][0]] * Pij[area[i]["Abranch"][0][0]] - 2 * Branch_X[area[i]["Abranch"][0][0]] * Qij[area[i]["Abranch"][0][0]] + Iij[area[i]["Abranch"][0][0]] * (Branch_R[area[i]["Abranch"][0][0]]**2 + Branch_X[area[i]["Abranch"][0][0]]**2)) print( Pij[area[i]["Abranch"][0][0]] * Pij[area[i]["Abranch"][0][0]] + Qij[area[i]["Abranch"][0][0]] * Qij[area[i]["Abranch"][0][0]] <= Vi[int(area[i]["Ai"][0])] * Iij[area[i]["Abranch"][0][0]]) obj = 0 for i in range(ng): print(Pg[i] - Pi[int(gen[i, GEN_BUS])] - bus[int(gen[i, GEN_BUS]), PD] / baseMVA) print(Qg[i] - Qi[int(gen[i, GEN_BUS])] - bus[int(gen[i, GEN_BUS]), QD] / baseMVA) print(int(gen[i, GEN_BUS])) obj += gencost[i, 4] * Pg[i] * Pg[i] * baseMVA * baseMVA + gencost[ i, 5] * Pg[i] * baseMVA + gencost[i, 6] # Connection matrix Cg = sparse((ones(ng), (gen[:, GEN_BUS], range(ng))), (nb, ng)) Branch_R = branch[:, BR_R] Branch_X = branch[:, BR_X] # Obtain the boundary information Slmax = branch[:, RATE_A] / baseMVA Pij_l = -Slmax Qij_l = -Slmax Iij_l = zeros(nl) Vm_l = turn_to_power(bus[:, VMIN], 2) Pg_l = gen[:, PMIN] / baseMVA Qg_l = gen[:, QMIN] / baseMVA Pi_l = -bus[:, PD] / baseMVA + Cg * Pg_l / baseMVA Qi_l = -bus[:, QD] / baseMVA + Cg * Qg_l / baseMVA Pij_u = Slmax Qij_u = Slmax Iij_u = Slmax Vm_u = turn_to_power(bus[:, VMAX], 2) Pg_u = 2 * gen[:, PMAX] / baseMVA Qg_u = 2 * gen[:, QMAX] / baseMVA Pi_u = -bus[:, PD] / baseMVA + Cg * Pg_u # Boundary error Qi_u = -bus[:, QD] / baseMVA + Cg * Qg_u # Boundary error model = Model("OPF") # Define the decision variables, compact set Pij = {} Qij = {} Iij = {} Vi = {} Pg = {} Qg = {} Pi = {} Qi = {} for i in range(nl): Pij[i] = model.addVar(lb=Pij_l[i], ub=Pij_u[i], vtype=GRB.CONTINUOUS, name="Pij{0}".format(i)) Qij[i] = model.addVar(lb=Qij_l[i], ub=Qij_u[i], vtype=GRB.CONTINUOUS, name="Qij{0}".format(i)) Iij[i] = model.addVar(lb=Iij_l[i], ub=Iij_u[i], vtype=GRB.CONTINUOUS, name="Iij{0}".format(i)) for i in range(nb): Vi[i] = model.addVar(lb=Vm_l[i], ub=Vm_u[i], vtype=GRB.CONTINUOUS, name="V{0}".format(i)) for i in range(ng): Pg[i] = model.addVar(lb=Pg_l[i], ub=Pg_u[i], vtype=GRB.CONTINUOUS, name="Pg{0}".format(i)) Qg[i] = model.addVar(lb=Qg_l[i], ub=Qg_u[i], vtype=GRB.CONTINUOUS, name="Qg{0}".format(i)) for i in range(nb): Pi[i] = model.addVar(lb=Pi_l[i], ub=Pi_u[i], vtype=GRB.CONTINUOUS, name="Pi{0}".format(i)) Qi[i] = model.addVar(lb=Qi_l[i], ub=Qi_u[i], vtype=GRB.CONTINUOUS, name="Qi{0}".format(i)) # For each area, before decomposition # Add system level constraints for i in range(nb): # If the bus is the root bus, only the children information is required. if len(area[i]["Ai"]) == 0: expr = 0 for j in range(len(area[i]["Cbranch"][0])): expr += Pij[area[i]["Cbranch"][0][j]] model.addConstr(lhs=expr - Pi[i], sense=GRB.EQUAL, rhs=0) expr = 0 for j in range(len(area[i]["Cbranch"][0])): expr += Qij[area[i]["Cbranch"][0][j]] model.addConstr(lhs=expr - Qi[i], sense=GRB.EQUAL, rhs=0) elif len(area[i]["Cbranch"]) == 0: # This bus is the lead node model.addConstr(lhs=Pij[area[i]["Abranch"][0][0]] - Iij[area[i]["Abranch"][0][0]] * Branch_R[area[i]["Abranch"][0][0]] + Pi[i], sense=GRB.EQUAL, rhs=0) model.addConstr(lhs=Qij[area[i]["Abranch"][0][0]] - Iij[area[i]["Abranch"][0][0]] * Branch_X[area[i]["Abranch"][0][0]] + Qi[i], sense=GRB.EQUAL, rhs=0) model.addConstr(lhs=Vi[int(area[i]["Ai"][0])] - Vi[i] - 2 * Branch_R[area[i]["Abranch"][0][0]] * Pij[area[i]["Abranch"][0][0]] - 2 * Branch_X[area[i]["Abranch"][0][0]] * Qij[area[i]["Abranch"][0][0]] + Iij[area[i]["Abranch"][0][0]] * (Branch_R[area[i]["Abranch"][0][0]]**2 + Branch_X[area[i]["Abranch"][0][0]]**2), sense=GRB.EQUAL, rhs=0) model.addConstr( Pij[area[i]["Abranch"][0][0]] * Pij[area[i]["Abranch"][0][0]] + Qij[area[i]["Abranch"][0][0]] * Qij[area[i]["Abranch"][0][0]] <= Vi[area[i]["Ai"][0]] * Iij[area[i]["Abranch"][0][0]], name="rc{0}".format(i)) else: expr = 0 for j in range(len(area[i]["Cbranch"][0])): expr += Pij[area[i]["Cbranch"][0][j]] model.addConstr(lhs=Pij[area[i]["Abranch"][0][0]] - Iij[area[i]["Abranch"][0][0]] * Branch_R[area[i]["Abranch"][0][0]] + Pi[i] - expr, sense=GRB.EQUAL, rhs=0) expr = 0 for j in range(len(area[i]["Cbranch"][0])): expr += Qij[area[i]["Cbranch"][0][j]] model.addConstr(lhs=Qij[area[i]["Abranch"][0][0]] - Iij[area[i]["Abranch"][0][0]] * Branch_X[area[i]["Abranch"][0][0]] + Qi[i] - expr, sense=GRB.EQUAL, rhs=0) model.addConstr(lhs=Vi[int(area[i]["Ai"][0])] - Vi[i] - 2 * Branch_R[area[i]["Abranch"][0][0]] * Pij[area[i]["Abranch"][0][0]] - 2 * Branch_X[area[i]["Abranch"][0][0]] * Qij[area[i]["Abranch"][0][0]] + Iij[area[i]["Abranch"][0][0]] * (Branch_R[area[i]["Abranch"][0][0]]**2 + Branch_X[area[i]["Abranch"][0][0]]**2), sense=GRB.EQUAL, rhs=0) model.addConstr( Pij[area[i]["Abranch"][0][0]] * Pij[area[i]["Abranch"][0][0]] + Qij[area[i]["Abranch"][0][0]] * Qij[area[i]["Abranch"][0][0]] <= Vi[area[i]["Ai"][0]] * Iij[area[i]["Abranch"][0][0]], name="rc{0}".format(i)) obj = 0 for i in range(ng): model.addConstr(lhs=Pg[i] - Pi[int(gen[i, GEN_BUS])], sense=GRB.EQUAL, rhs=bus[int(gen[i, GEN_BUS]), PD] / baseMVA) model.addConstr(lhs=Qg[i] - Qi[int(gen[i, GEN_BUS])], sense=GRB.EQUAL, rhs=bus[int(gen[i, GEN_BUS]), QD] / baseMVA) obj += gencost[i, 4] * Pg[i] * Pg[i] * baseMVA * baseMVA + gencost[ i, 5] * Pg[i] * baseMVA + gencost[i, 6] model.setObjective(obj) model.Params.OutputFlag = 0 model.Params.LogToConsole = 0 model.Params.DisplayInterval = 1 model.optimize() Pij = [] Qij = [] Iij = [] Vi = [] Pg = [] Qg = [] Pi = [] Qi = [] for i in range(nl): Pij.append(model.getVarByName("Pij{0}".format(i)).X) Qij.append(model.getVarByName("Qij{0}".format(i)).X) Iij.append(model.getVarByName("Iij{0}".format(i)).X) for i in range(nb): Vi.append(model.getVarByName("V{0}".format(i)).X) Pi.append(model.getVarByName("Pi{0}".format(i)).X) Qi.append(model.getVarByName("Qi{0}".format(i)).X) for i in range(ng): Pg.append(model.getVarByName("Pg{0}".format(i)).X) Qg.append(model.getVarByName("Qg{0}".format(i)).X) obj = obj.getValue() primal_residual = [] for i in range(nl): primal_residual.append(Pij[i] * Pij[i] + Qij[i] * Qij[i] - Iij[i] * Vi[int(f[i])]) return obj, primal_residual
def t_qps_pypower(quiet=False): """Tests of C{qps_pypower} QP solvers. @author: Ray Zimmerman (PSERC Cornell) """ algs = [200, 250, 400, 500, 600, 700] names = ['PIPS', 'sc-PIPS', 'IPOPT', 'CPLEX', 'MOSEK', 'Gurobi'] check = [None, None, 'ipopt', 'cplex', 'mosek', 'gurobipy'] n = 36 t_begin(n * len(algs), quiet) for k in range(len(algs)): if check[k] is not None and not have_fcn(check[k]): t_skip(n, '%s not installed' % names[k]) else: opt = {'verbose': 0, 'alg': algs[k]} if names[k] == 'PIPS' or names[k] == 'sc-PIPS': opt['pips_opt'] = {} opt['pips_opt']['comptol'] = 1e-8 if names[k] == 'CPLEX': # alg = 0 ## default uses barrier method with NaN bug in lower lim multipliers alg = 2 ## use dual simplex ppopt = ppoption(CPLEX_LPMETHOD = alg, CPLEX_QPMETHOD = min([4, alg])) opt['cplex_opt'] = cplex_options([], ppopt) if names[k] == 'MOSEK': # alg = 5 ## use dual simplex ppopt = ppoption() # ppopt = ppoption(ppopt, MOSEK_LP_ALG = alg) ppopt = ppoption(ppopt, MOSEK_GAP_TOL=1e-9) opt['mosek_opt'] = mosek_options([], ppopt) t = '%s - 3-d LP : ' % names[k] ## example from 'doc linprog' c = array([-5, -4, -6], float) A = sparse([[1, -1, 1], [3, 2, 4], [3, 2, 0]], dtype=float) l = None u = array([20, 42, 30], float) xmin = array([0, 0, 0], float) x0 = None x, f, s, _, lam = qps_pypower(None, c, A, l, u, xmin, None, None, opt) t_is(s, 1, 12, [t, 'success']) t_is(x, [0, 15, 3], 6, [t, 'x']) t_is(f, -78, 6, [t, 'f']) t_is(lam['mu_l'], [0, 0, 0], 13, [t, 'lam.mu_l']) t_is(lam['mu_u'], [0, 1.5, 0.5], 9, [t, 'lam.mu_u']) t_is(lam['lower'], [1, 0, 0], 9, [t, 'lam.lower']) t_is(lam['upper'], zeros(shape(x)), 13, [t, 'lam.upper']) t = '%s - unconstrained 3-d quadratic : ' % names[k] ## from http://www.akiti.ca/QuadProgEx0Constr.html H = sparse([ [ 5, -2, -1], [-2, 4, 3], [-1, 3, 5] ], dtype=float) c = array([2, -35, -47], float) x0 = array([0, 0, 0], float) x, f, s, _, lam = qps_pypower(H, c, opt=opt) t_is(s, 1, 12, [t, 'success']) t_is(x, [3, 5, 7], 8, [t, 'x']) t_is(f, -249, 13, [t, 'f']) t_ok(len(lam['mu_l']) == 0, [t, 'lam.mu_l']) t_ok(len(lam['mu_u']) == 0, [t, 'lam.mu_u']) t_is(lam['lower'], zeros(shape(x)), 13, [t, 'lam.lower']) t_is(lam['upper'], zeros(shape(x)), 13, [t, 'lam.upper']) t = '%s - constrained 2-d QP : ' % names[k] ## example from 'doc quadprog' H = sparse([[ 1, -1], [-1, 2]], dtype=float) c = array([-2, -6], float) A = sparse([[ 1, 1], [-1, 2], [ 2, 1]], dtype=float) l = None u = array([2, 2, 3], float) xmin = array([0, 0]) x0 = None x, f, s, _, lam = qps_pypower(H, c, A, l, u, xmin, None, x0, opt) t_is(s, 1, 12, [t, 'success']) t_is(x, array([2., 4.]) / 3, 7, [t, 'x']) t_is(f, -74. / 9, 6, [t, 'f']) t_is(lam['mu_l'], [0., 0., 0.], 13, [t, 'lam.mu_l']) t_is(lam['mu_u'], array([28., 4., 0.]) / 9, 7, [t, 'lam.mu_u']) t_is(lam['lower'], zeros(shape(x)), 8, [t, 'lam.lower']) t_is(lam['upper'], zeros(shape(x)), 13, [t, 'lam.upper']) t = '%s - constrained 4-d QP : ' % names[k] ## from http://www.jmu.edu/docs/sasdoc/sashtml/iml/chap8/sect12.htm H = sparse([[1003.1, 4.3, 6.3, 5.9], [4.3, 2.2, 2.1, 3.9], [6.3, 2.1, 3.5, 4.8], [5.9, 3.9, 4.8, 10.0]]) c = zeros(4) A = sparse([[ 1, 1, 1, 1], [0.17, 0.11, 0.10, 0.18]]) l = array([1, 0.10]) u = array([1, Inf]) xmin = zeros(4) x0 = array([1, 0, 0, 1], float) x, f, s, _, lam = qps_pypower(H, c, A, l, u, xmin, None, x0, opt) t_is(s, 1, 12, [t, 'success']) t_is(x, array([0, 2.8, 0.2, 0]) / 3, 5, [t, 'x']) t_is(f, 3.29 / 3, 6, [t, 'f']) t_is(lam['mu_l'], array([6.58, 0]) / 3, 6, [t, 'lam.mu_l']) t_is(lam['mu_u'], [0, 0], 13, [t, 'lam.mu_u']) t_is(lam['lower'], [2.24, 0, 0, 1.7667], 4, [t, 'lam.lower']) t_is(lam['upper'], zeros(shape(x)), 13, [t, 'lam.upper']) t = '%s - (dict) constrained 4-d QP : ' % names[k] p = {'H': H, 'A': A, 'l': l, 'u': u, 'xmin': xmin, 'x0': x0, 'opt': opt} x, f, s, _, lam = qps_pypower(p) t_is(s, 1, 12, [t, 'success']) t_is(x, array([0, 2.8, 0.2, 0]) / 3, 5, [t, 'x']) t_is(f, 3.29 / 3, 6, [t, 'f']) t_is(lam['mu_l'], array([6.58, 0]) / 3, 6, [t, 'lam.mu_l']) t_is(lam['mu_u'], [0, 0], 13, [t, 'lam.mu_u']) t_is(lam['lower'], [2.24, 0, 0, 1.7667], 4, [t, 'lam.lower']) t_is(lam['upper'], zeros(shape(x)), 13, [t, 'lam.upper']) t = '%s - infeasible LP : ' % names[k] p = {'A': sparse([1, 1]), 'c': array([1, 1]), 'u': array([-1]), 'xmin': array([0, 0]), 'opt': opt} x, f, s, _, lam = qps_pypower(p) t_ok(s <= 0, [t, 'no success']) t_end()
def t_opf_dc_gurobi(quiet=False): """Tests for DC optimal power flow using Gurobi solver. """ algs = [0, 1, 2, 3, 4] num_tests = 23 * len(algs) t_begin(num_tests, quiet) tdir = dirname(__file__) casefile = join(tdir, 't_case9_opf') if quiet: verbose = False else: verbose = False ppopt = ppoption('OUT_ALL', 0, 'VERBOSE', verbose); ppopt = ppoption(ppopt, 'OPF_ALG_DC', 700); ## run DC OPF if have_fcn('gurobipy'): for k in range(len(algs)): ppopt = ppoption(ppopt, 'GRB_METHOD', algs[k]) methods = [ 'automatic', 'primal simplex', 'dual simplex', 'barrier', 'concurrent', 'deterministic concurrent', ] t0 = 'DC OPF (Gurobi %s): ' % methods[k] ## set up indices ib_data = r_[arange(BUS_AREA + 1), arange(BASE_KV, VMIN + 1)] ib_voltage = arange(VM, VA + 1) ib_lam = arange(LAM_P, LAM_Q + 1) ib_mu = arange(MU_VMAX, MU_VMIN + 1) ig_data = r_[[GEN_BUS, QMAX, QMIN], arange(MBASE, APF + 1)] ig_disp = array([PG, QG, VG]) ig_mu = arange(MU_PMAX, MU_QMIN + 1) ibr_data = arange(ANGMAX + 1) ibr_flow = arange(PF, QT + 1) ibr_mu = array([MU_SF, MU_ST]) #ibr_angmu = array([MU_ANGMIN, MU_ANGMAX]) ## get solved DC power flow case from MAT-file ## defines bus_soln, gen_soln, branch_soln, f_soln soln9_dcopf = loadmat(join(tdir, 'soln9_dcopf.mat'), struct_as_record=True) bus_soln, gen_soln, branch_soln, f_soln = \ soln9_dcopf['bus_soln'], soln9_dcopf['gen_soln'], \ soln9_dcopf['branch_soln'], soln9_dcopf['f_soln'] ## run OPF t = t0 r = rundcopf(casefile, ppopt) bus, gen, branch, f, success = \ r['bus'], r['gen'], r['branch'], r['f'], r['success'] t_ok(success, [t, 'success']) t_is(f, f_soln, 3, [t, 'f']) t_is( bus[:, ib_data ], bus_soln[:, ib_data ], 10, [t, 'bus data']) t_is( bus[:, ib_voltage], bus_soln[:, ib_voltage], 3, [t, 'bus voltage']) t_is( bus[:, ib_lam ], bus_soln[:, ib_lam ], 3, [t, 'bus lambda']) t_is( bus[:, ib_mu ], bus_soln[:, ib_mu ], 2, [t, 'bus mu']) t_is( gen[:, ig_data ], gen_soln[:, ig_data ], 10, [t, 'gen data']) t_is( gen[:, ig_disp ], gen_soln[:, ig_disp ], 3, [t, 'gen dispatch']) t_is( gen[:, ig_mu ], gen_soln[:, ig_mu ], 3, [t, 'gen mu']) t_is(branch[:, ibr_data ], branch_soln[:, ibr_data ], 10, [t, 'branch data']) t_is(branch[:, ibr_flow ], branch_soln[:, ibr_flow ], 3, [t, 'branch flow']) t_is(branch[:, ibr_mu ], branch_soln[:, ibr_mu ], 2, [t, 'branch mu']) ##----- run OPF with extra linear user constraints & costs ----- ## two new z variables ## 0 <= z1, P2 - P1 <= z1 ## 0 <= z2, P2 - P3 <= z2 ## with A and N sized for DC opf ppc = loadcase(casefile) row = [0, 0, 0, 1, 1, 1] col = [9, 10, 12, 10, 11, 13] ppc['A'] = sparse(([-1, 1, -1, 1, -1, -1], (row, col)), (2, 14)) ppc['u'] = array([0, 0]) ppc['l'] = array([-Inf, -Inf]) ppc['zl'] = array([0, 0]) ppc['N'] = sparse(([1, 1], ([0, 1], [12, 13])), (2, 14)) ## new z variables only ppc['fparm'] = ones((2, 1)) * array([[1, 0, 0, 1]]) ## w = r = z ppc['H'] = sparse((2, 2)) ## no quadratic term ppc['Cw'] = array([1000, 1]) t = ''.join([t0, 'w/extra constraints & costs 1 : ']) r = rundcopf(ppc, ppopt) t_ok(r['success'], [t, 'success']) t_is(r['gen'][0, PG], 116.15974, 4, [t, 'Pg1 = 116.15974']) t_is(r['gen'][1, PG], 116.15974, 4, [t, 'Pg2 = 116.15974']) t_is(r['var']['val']['z'], [0, 0.3348], 4, [t, 'user vars']) t_is(r['cost']['usr'], 0.3348, 3, [t, 'user costs']) ## with A and N sized for AC opf ppc = loadcase(casefile) row = [0, 0, 0, 1, 1, 1] col = [18, 19, 24, 19, 20, 25] ppc['A'] = sparse(([-1, 1, -1, 1, -1, -1], (row, col)), (2, 26)) ppc['u'] = array([0, 0]) ppc['l'] = array([-Inf, -Inf]) ppc['zl'] = array([0, 0]) ppc['N'] = sparse(([1, 1], ([0, 1], [24, 25])), (2, 26)) ## new z variables only ppc['fparm'] = ones((2, 1)) * array([[1, 0, 0, 1]]) ## w = r = z ppc['H'] = sparse((2, 2)) ## no quadratic term ppc['Cw'] = array([1000, 1]) t = ''.join([t0, 'w/extra constraints & costs 2 : ']) r = rundcopf(ppc, ppopt) t_ok(r['success'], [t, 'success']) t_is(r['gen'][0, PG], 116.15974, 4, [t, 'Pg1 = 116.15974']) t_is(r['gen'][1, PG], 116.15974, 4, [t, 'Pg2 = 116.15974']) t_is(r['var']['val']['z'], [0, 0.3348], 4, [t, 'user vars']) t_is(r['cost']['usr'], 0.3348, 3, [t, 'user costs']) t = ''.join([t0, 'infeasible : ']) ## with A and N sized for DC opf ppc = loadcase(casefile) ppc['A'] = sparse(([1, 1], ([0, 0], [9, 10])), (1, 14)) ## Pg1 + Pg2 ppc['u'] = array([Inf]) ppc['l'] = array([600]) r = rundcopf(ppc, ppopt) t_ok(not r['success'], [t, 'no success']) else: t_skip(num_tests, 'Gurobi not available') t_end()
def pre_process(n_bus, Yseries, Vset, pq, pv, vd): """ Make the Helm System matrix @param n_bus: Number of buses of the circuit @param Yseries: Circuit admittance matrix of the series elements @param Vset: Vector of voltages of those nodes where the voltage is controlled (AKA Slack and PV buses) @param S: Vector of power injections at all the nodes @param pq: list of PQ node indices @param pv: list of PV node indices @param vd: list of Slack node indices @return: """ """ Reduction of the circuit magnitudes. Args: n_bus: Yseries: slack_indices: Array of indices of the slack nodes Vset: S: Output: Yred: Reduced admittance matrix (Without the rows and columns belonging to slack buses) I: Matrix of currents (In practice only one slack bus is selected, hence it is a vector) injected by the slack buses Sred: Array of power injections of the buses that are not of type slack types_red: Array of types of the buses that are not of type slack non_slack_indices: Array of indices of the buses that are not of type slack """ # now to have efficient arrays of coefficients map_idx = zeros(n_bus, dtype=np.int) map_w = zeros(n_bus, dtype=np.int) npq = 0 npv = 0 npqpv = 0 for i in pq: map_idx[i] = npq map_w[i] = npqpv npq += 1 npqpv += 1 for i in pv: map_idx[i] = npv map_w[i] = npqpv npv += 1 npqpv += 1 # build the expanded system matrix Ysys = zeros((2 * n_bus, 2 * n_bus)) for a, b in product(range(n_bus), range(n_bus)): Ysys[2 * a, 2 * b] = Yseries[a, b].real Ysys[2 * a, 2 * b + 1] = -Yseries[a, b].imag Ysys[2 * a + 1, 2 * b] = Yseries[a, b].imag Ysys[2 * a + 1, 2 * b + 1] = Yseries[a, b].real # set pv column for a in pv: b = a Ysys[:, 2 * b] = zeros(2 * n_bus) # Ysys[a*2, b*2+1] = 0 Ysys[a * 2 + 1, b * 2] = 1 # set vd elements for a in vd: Ysys[a * 2, :] = zeros(2 * n_bus) Ysys[a * 2 + 1, :] = zeros(2 * n_bus) Ysys[a * 2, a * 2] = 1 Ysys[a * 2 + 1, a * 2 + 1] = 1 # print('Ysys\n', Ysys) # build the PV matrix Ypv = zeros((2 * n_bus, npv)) for a, b in product(r_[pq, pv], pv): kk = map_idx[b] Ypv[2 * a, kk] = Yseries[a, b].real Ypv[2 * a + 1, kk] = Yseries[a, b].imag # print('Ypv\n', Ypv) Vset2 = Vset * Vset return sparse(Ysys), Ypv, Vset2, map_idx, map_w, npq, npv
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) @author: Richard Lincoln Modified by University of Kassel (Friederike Meier): Bugfix in line 173 """ ##----- 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) #TypeError: dIbr_dV() missing 1 required positional argument: 'V' >> branch was missing 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