def makeB(baseMVA, bus, branch, alg): """Builds the FDPF matrices, B prime and B double prime. Returns the two matrices B prime and B double prime used in the fast decoupled power flow. Does appropriate conversions to p.u. C{alg} is the value of the C{PF_ALG} option specifying the power flow algorithm. @see: L{fdpf} @author: Ray Zimmerman (PSERC Cornell) """ ## constants nb = bus.shape[0] ## number of buses nl = branch.shape[0] ## number of lines ##----- form Bp (B prime) ----- temp_branch = copy(branch) ## modify a copy of branch temp_bus = copy(bus) ## modify a copy of bus temp_bus[:, BS] = zeros(nb) ## zero out shunts at buses temp_branch[:, BR_B] = zeros(nl) ## zero out line charging shunts temp_branch[:, TAP] = ones(nl) ## cancel out taps if alg == 2: ## if XB method temp_branch[:, BR_R] = zeros(nl) ## zero out line resistance Bp = -1 * makeYbus(baseMVA, temp_bus, temp_branch)[0].imag ##----- form Bpp (B double prime) ----- temp_branch = copy(branch) ## modify a copy of branch temp_branch[:, SHIFT] = zeros(nl) ## zero out phase shifters if alg == 3: ## if BX method temp_branch[:, BR_R] = zeros(nl) ## zero out line resistance Bpp = -1 * makeYbus(baseMVA, bus, temp_branch)[0].imag return Bp, Bpp
def makeB(baseMVA, bus, branch, alg): """Builds the FDPF matrices, B prime and B double prime. Returns the two matrices B prime and B double prime used in the fast decoupled power flow. Does appropriate conversions to p.u. C{alg} is the value of the C{PF_ALG} option specifying the power flow algorithm. @see: L{fdpf} @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln """ ## constants nb = bus.shape[0] ## number of buses nl = branch.shape[0] ## number of lines ##----- form Bp (B prime) ----- temp_branch = copy(branch) ## modify a copy of branch temp_bus = copy(bus) ## modify a copy of bus temp_bus[:, BS] = zeros(nb) ## zero out shunts at buses temp_branch[:, BR_B] = zeros(nl) ## zero out line charging shunts temp_branch[:, TAP] = ones(nl) ## cancel out taps if alg == 2: ## if XB method temp_branch[:, BR_R] = zeros(nl) ## zero out line resistance Bp = -1 * makeYbus(baseMVA, temp_bus, temp_branch)[0].imag ##----- form Bpp (B double prime) ----- temp_branch = copy(branch) ## modify a copy of branch temp_branch[:, SHIFT] = zeros(nl) ## zero out phase shifters if alg == 3: ## if BX method temp_branch[:, BR_R] = zeros(nl) ## zero out line resistance Bpp = -1 * makeYbus(baseMVA, bus, temp_branch)[0].imag return Bp, Bpp
def makeYbus(baseMVA,bus,branch,do_separate_Yslack=False): from pypower.makeYbus import makeYbus if do_separate_Yslack: Y = makeYbus(baseMVA,bus,branch)[0].toarray() slack_ind = (bus[:,BUS_TYPE]==3).nonzero()[0][0] return separate_Yslack(Y,slack_ind) else: return makeYbus(baseMVA,bus,branch)[0]
def AugYbus(baseMVA, bus, branch, xd_tr, gbus, P, Q, U0): """ Constructs augmented bus admittance matrix Ybus. @param baseMVA: power base @param bus: bus data @param branch: branch data @param xd_tr: d component of transient reactance @param gbus: generator buses @param P: load active power @param Q: load reactive power @param U0: steady-state bus voltages @return: factorised augmented bus admittance matrix @see: U{http://www.esat.kuleuven.be/electa/teaching/matdyn/} """ # Calculate bus admittance matrix Ybus, _, _ = makeYbus(baseMVA, bus, branch) # Calculate equivalent load admittance yload = (P - 1j * Q) / (abs(U0)**2) # Calculate equivalent generator admittance ygen = zeros(Ybus.shape[0]) ygen[gbus] = 1 / (1j * xd_tr) # Add equivalent load and generator admittance to Ybus matrix for i in range(Ybus.shape[0]): Ybus[i, i] = Ybus[i, i] + ygen[i] + yload[i] # Factorise return splu(Ybus)
def network_observability(topology, meas_idx, V): from pypower.api import makeYbus from pypower.bustypes import bustypes from pypower.dSbus_dV import dSbus_dV from pypower.dSbr_dV import dSbr_dV # build admittances Ybus,Yfrom,Yto = makeYbus(topology["baseMVA"],topology["bus"],topology["branch"]) Ybus, Yfrom, Yto = Ybus.toarray(), Yfrom.toarray(), Yto.toarray() Nk = Ybus.shape[0] # get non-reference buses ref,pv,pq = bustypes(topology["bus"],topology["gen"]) nonref = np.r_[pv,pq] gbus = topology["gen"][:,GEN_BUS].astype(int) # calculate partial derivative dSbus_dVm, dSbus_dVa = dSbus_dV(Ybus,V) dSf_dVa, dSf_dVm, dSt_dVa, dSt_dVm, Sf, St = dSbr_dV(topology["branch"],Yfrom,Yto,V) # create Jacobian matrix ## submatrices related to line flow dPf_Va = np.real(dSf_dVa)[meas_idx["Pf"],:][:,nonref] dPf_Vm = np.real(dSf_dVm)[meas_idx["Pf"],:][:,nonref] dPt_Va = np.real(dSt_dVa)[meas_idx["Pt"],:][:,nonref] dPt_Vm = np.real(dSt_dVm)[meas_idx["Pt"],:][:,nonref] dQf_Va = np.imag(dSf_dVa)[meas_idx["Qf"],:][:,nonref] dQf_Vm = np.imag(dSf_dVm)[meas_idx["Qf"],:][:,nonref] dQt_Va = np.imag(dSt_dVa)[meas_idx["Qt"],:][:,nonref] dQt_Vm = np.imag(dSt_dVm)[meas_idx["Qt"],:][:,nonref] ## submatrix related to generator output dPg_Va = np.real(dSbus_dVa)[gbus,:][meas_idx["Pg"],:][:,nonref] dPg_Vm = np.real(dSbus_dVm)[gbus,:][meas_idx["Pg"],:][:,nonref] dQg_Va = np.imag(dSbus_dVa)[gbus,:][meas_idx["Qg"],:][:,nonref] dQg_Vm = np.imag(dSbus_dVm)[gbus,:][meas_idx["Qg"],:][:,nonref] ## submatrix related to bus injection dPk_Va = np.real(dSbus_dVa)[meas_idx["Pk"],:][:,nonref] dPk_Vm = np.real(dSbus_dVm)[meas_idx["Pk"],:][:,nonref] dQk_Va = np.imag(dSbus_dVa)[meas_idx["Qk"],:][:,nonref] dQk_Vm = np.imag(dSbus_dVm)[meas_idx["Qk"],:][:,nonref] ## submatrix related to voltage angle dVa_Va = np.eye(Nk)[meas_idx["Va"],:][:,nonref] dVa_Vm = np.zeros((Nk,Nk))[meas_idx["Va"],:][:,nonref] ## submatrix related to voltage magnitude dVm_Va = np.zeros((Nk,Nk))[meas_idx["Vm"],:][:,nonref] dVm_Vm = np.eye(Nk)[meas_idx["Vm"],:][:,nonref] H = np.r_[np.c_[dPf_Va, dPf_Vm],\ np.c_[dPt_Va, dPt_Vm],\ np.c_[dPg_Va, dPg_Vm],\ np.c_[dQf_Va, dQf_Vm],\ np.c_[dQt_Va, dQt_Vm],\ np.c_[dQg_Va, dQg_Vm],\ np.c_[dPk_Va, dPk_Vm],\ np.c_[dQk_Va, dQk_Vm],\ np.c_[dVa_Va, dVa_Vm],\ np.c_[dVm_Va, dVm_Vm]] return isobservable(H,pv,pq)
def voltage2power(V,topology): """Calculation of nodal and line power from complex nodal voltages Returns dictionary of corresponding powers and a dictionary with the partial derivatives :param V: numpy array of complex nodal voltages :param topology: dict in pypower casefile format :returns: dict of calculated power values, dict of jacobians if V is one-dimensional """ from pypower.idx_brch import F_BUS,T_BUS from pypower.idx_gen import GEN_BUS from pypower.makeYbus import makeYbus from pypower.idx_bus import PD,QD from pypower.dSbus_dV import dSbus_dV from pypower.dSbr_dV import dSbr_dV assert(str(V.dtype)[:7]=="complex") list_f = topology["branch"][:,F_BUS].tolist() list_t = topology["branch"][:,T_BUS].tolist() Ybus,Yfrom,Yto = makeYbus(topology["baseMVA"],topology["bus"],topology["branch"]) Ybus, Yfrom, Yto = Ybus.toarray(), Yfrom.toarray(), Yto.toarray() powers = {} if len(V.shape)==1: powers["Sf"] = V[list_f]*np.conj(np.dot(Yfrom,V)) powers["St"] = V[list_t]*np.conj(np.dot(Yto,V)) gbus = topology["gen"][:,GEN_BUS].astype(int) Sgbus= V[gbus]*np.conj(np.dot(Ybus[gbus,:],V)) powers["Sg"] = ( Sgbus*topology["baseMVA"] + topology["bus"][gbus,PD] + 1j*topology["bus"][gbus,QD] ) / topology["baseMVA"] powers["Sk"] = V*np.conj(np.dot(Ybus,V)) else: powers = dict([(name,[]) for name in ["Sf","St","Sg","Sk"]]) for k in range(V.shape[0]): powers["Sf"].append(V[k,list_f]*np.conj(np.dot(Yfrom,V[k,:]))) powers["St"].append(V[k,list_t]*np.conj(np.dot(Yto,V[k,:]))) gbus = topology["gen"][:,GEN_BUS].astype(int) Sgbus= V[k,gbus]*np.conj(np.dot(Ybus[gbus,:],V[k,:])) powers["Sg"].append(( Sgbus*topology["baseMVA"] + topology["bus"][gbus,PD] + 1j*topology["bus"][gbus,QD] ) / topology["baseMVA"]) powers["Sk"].append(V[k,:]*np.conj(np.dot(Ybus,V[k,:]))) for key in powers.keys(): powers[key] = np.asarray(powers[key]) # calculate partial derivative jacobians = dict([]) if len(V.shape)==1: jacobians["dSbus_dVm"], jacobians["dSbus_dVa"] = dSbus_dV(Ybus,V) jacobians["dSf_dVa"], jacobians["dSf_dVm"], jacobians["dSt_dVa"], \ jacobians["dSt_dVm"], jacobians["Sf"], jacobians["St"] = dSbr_dV(topology["branch"],Yfrom,Yto,V) return powers, jacobians
def create_y(self): from_to = np.concatenate((self.ppc["branch"][:, 0].real, self.ppc["branch"][:, 1].real))\ .astype(int) to_from = from_to[::-1] with warnings.catch_warnings(): warnings.simplefilter("ignore") Ybus, _, _ = makeYbus(self.s_ref, self.ppc["bus"], self.ppc["branch"]) # create relevant matrices self.Y_bus = Ybus.toarray() self.G = self.Y_bus.real self.B = self.Y_bus.imag n = len(self.ppc["bus"]) self.G_series = -self.G np.fill_diagonal(self.G_series, 0.) self.B_series = -self.B np.fill_diagonal(self.B_series, 0.) # In case that's becoming relevant later, G_shunt will not be removed self.G_shunt = np.zeros_like(self.G) self.B_shunt = np.zeros((n, n)) self.B_shunt[from_to, to_from] = np.tile(0.5 * self.ppc["branch"][:, 4].real, 2)
def ipoptopf_solver(om, ppopt): """Solves AC optimal power flow using IPOPT. Inputs are an OPF model object and a PYPOWER options vector. Outputs are a C{results} dict, C{success} flag and C{raw} output dict. C{results} is a PYPOWER case dict (ppc) with the usual C{baseMVA}, C{bus} C{branch}, C{gen}, C{gencost} fields, along with the following additional fields: - C{order} see 'help ext2int' for details of this field - C{x} final value of optimization variables (internal order) - C{f} final objective function value - C{mu} shadow prices on ... - C{var} - C{l} lower bounds on variables - C{u} upper bounds on variables - C{nln} - C{l} lower bounds on nonlinear constraints - C{u} upper bounds on nonlinear constraints - C{lin} - C{l} lower bounds on linear constraints - C{u} upper bounds on linear constraints C{success} is C{True} if solver converged successfully, C{False} otherwise C{raw} is a raw output dict in form returned by MINOS - C{xr} final value of optimization variables - C{pimul} constraint multipliers - C{info} solver specific termination code - C{output} solver specific output information @see: L{opf}, L{pips} @author: Ray Zimmerman (PSERC Cornell) @author: Carlos E. Murillo-Sanchez (PSERC Cornell & Universidad Autonoma de Manizales) @author: Richard Lincoln """ import pyipopt ## unpack data ppc = om.get_ppc() baseMVA, bus, gen, branch, gencost = \ ppc['baseMVA'], ppc['bus'], ppc['gen'], ppc['branch'], ppc['gencost'] vv, _, nn, _ = om.get_idx() ## problem dimensions nb = shape(bus)[0] ## number of buses ng = shape(gen)[0] ## number of gens nl = shape(branch)[0] ## number of branches ny = om.getN('var', 'y') ## number of piece-wise linear costs ## linear constraints A, l, u = om.linear_constraints() ## bounds on optimization vars _, xmin, xmax = om.getv() ## build admittance matrices Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch) ## try to select an interior initial point ll = xmin.copy(); uu = xmax.copy() ll[xmin == -Inf] = -2e19 ## replace Inf with numerical proxies uu[xmax == Inf] = 2e19 x0 = (ll + uu) / 2 Varefs = bus[bus[:, BUS_TYPE] == REF, VA] * (pi / 180) x0[vv['i1']['Va']:vv['iN']['Va']] = Varefs[0] ## angles set to first reference angle if ny > 0: ipwl = find(gencost[:, MODEL] == PW_LINEAR) # PQ = r_[gen[:, PMAX], gen[:, QMAX]] # c = totcost(gencost[ipwl, :], PQ[ipwl]) ## largest y-value in CCV data c = gencost.flatten('F')[sub2ind(shape(gencost), ipwl, NCOST + 2 * gencost[ipwl, NCOST])] x0[vv['i1']['y']:vv['iN']['y']] = max(c) + 0.1 * abs(max(c)) # x0[vv['i1']['y']:vv['iN']['y']) = c + 0.1 * abs(c) ## find branches with flow limits il = find((branch[:, RATE_A] != 0) & (branch[:, RATE_A] < 1e10)) nl2 = len(il) ## number of constrained lines ##----- run opf ----- ## build Jacobian and Hessian structure if A is not None and issparse(A): nA = A.shape[0] ## number of original linear constraints else: nA = 0 nx = len(x0) f = branch[:, F_BUS] ## list of "from" buses t = branch[:, T_BUS] ## list of "to" buses Cf = sparse((ones(nl), (arange(nl), f)), (nl, nb)) ## connection matrix for line & from buses Ct = sparse((ones(nl), (arange(nl), t)), (nl, nb)) ## connection matrix for line & to buses Cl = Cf + Ct Cb = Cl.T * Cl + speye(nb, nb) Cl2 = Cl[il, :] Cg = sparse((ones(ng), (gen[:, GEN_BUS], arange(ng))), (nb, ng)) nz = nx - 2 * (nb + ng) nxtra = nx - 2 * nb if nz > 0: Js = vstack([ hstack([Cb, Cb, Cg, sparse((nb, ng)), sparse((nb, nz))]), hstack([Cb, Cb, sparse((nb, ng)), Cg, sparse((nb, nz))]), hstack([Cl2, Cl2, sparse((nl2, 2 * ng)), sparse((nl2, nz))]), hstack([Cl2, Cl2, sparse((nl2, 2 * ng)), sparse((nl2, nz))]) ], 'coo') else: Js = vstack([ hstack([Cb, Cb, Cg, sparse((nb, ng))]), hstack([Cb, Cb, sparse((nb, ng)), Cg, ]), hstack([Cl2, Cl2, sparse((nl2, 2 * ng)), ]), hstack([Cl2, Cl2, sparse((nl2, 2 * ng)), ]) ], 'coo') if A is not None and issparse(A): Js = vstack([Js, A], 'coo') f, _, d2f = opf_costfcn(x0, om, True) Hs = tril(d2f + vstack([ hstack([Cb, Cb, sparse((nb, nxtra))]), hstack([Cb, Cb, sparse((nb, nxtra))]), sparse((nxtra, nx)) ]), format='coo') ## set options struct for IPOPT # options = {} # options['ipopt'] = ipopt_options([], ppopt) ## extra data to pass to functions userdata = { 'om': om, 'Ybus': Ybus, 'Yf': Yf[il, :], 'Yt': Yt[il, :], 'ppopt': ppopt, 'il': il, 'A': A, 'nA': nA, 'neqnln': 2 * nb, 'niqnln': 2 * nl2, 'Js': Js, 'Hs': Hs } ## check Jacobian and Hessian structure #xr = rand(x0.shape) #lmbda = rand( 2 * nb + 2 * nl2) #Js1 = eval_jac_g(x, flag, userdata) #(xr, options.auxdata) #Hs1 = eval_h(xr, 1, lmbda, userdata) #i1, j1, s = find(Js) #i2, j2, s = find(Js1) #if (len(i1) != len(i2)) | (norm(i1 - i2) != 0) | (norm(j1 - j2) != 0): # raise ValueError, 'something''s wrong with the Jacobian structure' # #i1, j1, s = find(Hs) #i2, j2, s = find(Hs1) #if (len(i1) != len(i2)) | (norm(i1 - i2) != 0) | (norm(j1 - j2) != 0): # raise ValueError, 'something''s wrong with the Hessian structure' ## define variable and constraint bounds # n is the number of variables n = x0.shape[0] # xl is the lower bound of x as bounded constraints xl = xmin # xu is the upper bound of x as bounded constraints xu = xmax neqnln = 2 * nb niqnln = 2 * nl2 # number of constraints m = neqnln + niqnln + nA # lower bound of constraint gl = r_[zeros(neqnln), -Inf * ones(niqnln), l] # upper bound of constraints gu = r_[zeros(neqnln), zeros(niqnln), u] # number of nonzeros in Jacobi matrix nnzj = Js.nnz # number of non-zeros in Hessian matrix, you can set it to 0 nnzh = Hs.nnz eval_hessian = True if eval_hessian: hessian = lambda x, lagrange, obj_factor, flag, user_data=None: \ eval_h(x, lagrange, obj_factor, flag, userdata) nlp = pyipopt.create(n, xl, xu, m, gl, gu, nnzj, nnzh, eval_f, eval_grad_f, eval_g, eval_jac_g, hessian) else: nnzh = 0 nlp = pyipopt.create(n, xl, xu, m, gl, gu, nnzj, nnzh, eval_f, eval_grad_f, eval_g, eval_jac_g) nlp.int_option('print_level', 5) nlp.num_option('tol', 1.0000e-12) nlp.int_option('max_iter', 250) nlp.num_option('dual_inf_tol', 0.10000) nlp.num_option('constr_viol_tol', 1.0000e-06) nlp.num_option('compl_inf_tol', 1.0000e-05) nlp.num_option('acceptable_tol', 1.0000e-08) nlp.num_option('acceptable_constr_viol_tol', 1.0000e-04) nlp.num_option('acceptable_compl_inf_tol', 0.0010000) nlp.str_option('mu_strategy', 'adaptive') iter = 0 def intermediate_callback(algmod, iter_count, obj_value, inf_pr, inf_du, mu, d_norm, regularization_size, alpha_du, alpha_pr, ls_trials, user_data=None): iter = iter_count return True nlp.set_intermediate_callback(intermediate_callback) ## run the optimization # returns final solution x, upper and lower bound for multiplier, final # objective function obj and the return status of ipopt x, zl, zu, obj, status, zg = nlp.solve(x0, m, userdata) info = {'x': x, 'zl': zl, 'zu': zu, 'obj': obj, 'status': status, 'lmbda': zg} nlp.close() success = (status == 0) | (status == 1) output = {'iterations': iter} f, _ = opf_costfcn(x, om) ## update solution data Va = x[vv['i1']['Va']:vv['iN']['Va']] Vm = x[vv['i1']['Vm']:vv['iN']['Vm']] Pg = x[vv['i1']['Pg']:vv['iN']['Pg']] Qg = x[vv['i1']['Qg']:vv['iN']['Qg']] V = Vm * exp(1j * Va) ##----- calculate return values ----- ## update voltages & generator outputs bus[:, VA] = Va * 180 / pi bus[:, VM] = Vm gen[:, PG] = Pg * baseMVA gen[:, QG] = Qg * baseMVA gen[:, VG] = Vm[gen[:, GEN_BUS].astype(int)] ## compute branch flows f_br = branch[:, F_BUS].astype(int) t_br = branch[:, T_BUS].astype(int) Sf = V[f_br] * conj(Yf * V) ## cplx pwr at "from" bus, p.u. St = V[t_br] * conj(Yt * V) ## cplx pwr at "to" bus, p.u. branch[:, PF] = Sf.real * baseMVA branch[:, QF] = Sf.imag * baseMVA branch[:, PT] = St.real * baseMVA branch[:, QT] = St.imag * baseMVA ## line constraint is actually on square of limit ## so we must fix multipliers muSf = zeros(nl) muSt = zeros(nl) if len(il) > 0: muSf[il] = 2 * info['lmbda'][2 * nb + arange(nl2)] * branch[il, RATE_A] / baseMVA muSt[il] = 2 * info['lmbda'][2 * nb + nl2 + arange(nl2)] * branch[il, RATE_A] / baseMVA ## update Lagrange multipliers bus[:, MU_VMAX] = info['zu'][vv['i1']['Vm']:vv['iN']['Vm']] bus[:, MU_VMIN] = info['zl'][vv['i1']['Vm']:vv['iN']['Vm']] gen[:, MU_PMAX] = info['zu'][vv['i1']['Pg']:vv['iN']['Pg']] / baseMVA gen[:, MU_PMIN] = info['zl'][vv['i1']['Pg']:vv['iN']['Pg']] / baseMVA gen[:, MU_QMAX] = info['zu'][vv['i1']['Qg']:vv['iN']['Qg']] / baseMVA gen[:, MU_QMIN] = info['zl'][vv['i1']['Qg']:vv['iN']['Qg']] / baseMVA bus[:, LAM_P] = info['lmbda'][nn['i1']['Pmis']:nn['iN']['Pmis']] / baseMVA bus[:, LAM_Q] = info['lmbda'][nn['i1']['Qmis']:nn['iN']['Qmis']] / baseMVA branch[:, MU_SF] = muSf / baseMVA branch[:, MU_ST] = muSt / baseMVA ## package up results nlnN = om.getN('nln') ## extract multipliers for nonlinear constraints kl = find(info['lmbda'][:2 * nb] < 0) ku = find(info['lmbda'][:2 * nb] > 0) nl_mu_l = zeros(nlnN) nl_mu_u = r_[zeros(2 * nb), muSf, muSt] nl_mu_l[kl] = -info['lmbda'][kl] nl_mu_u[ku] = info['lmbda'][ku] ## extract multipliers for linear constraints lam_lin = info['lmbda'][2 * nb + 2 * nl2 + arange(nA)] ## lmbda for linear constraints kl = find(lam_lin < 0) ## lower bound binding ku = find(lam_lin > 0) ## upper bound binding mu_l = zeros(nA) mu_l[kl] = -lam_lin[kl] mu_u = zeros(nA) mu_u[ku] = lam_lin[ku] mu = { 'var': {'l': info['zl'], 'u': info['zu']}, 'nln': {'l': nl_mu_l, 'u': nl_mu_u}, \ 'lin': {'l': mu_l, 'u': mu_u} } results = ppc results['bus'], results['branch'], results['gen'], \ results['om'], results['x'], results['mu'], results['f'] = \ bus, branch, gen, om, x, mu, f pimul = r_[ results['mu']['nln']['l'] - results['mu']['nln']['u'], results['mu']['lin']['l'] - results['mu']['lin']['u'], -ones(ny > 0), results['mu']['var']['l'] - results['mu']['var']['u'] ] raw = {'xr': x, 'pimul': pimul, 'info': info['status'], 'output': output} return results, success, raw
def runpf(casedata=None, ppopt=None, fname='', solvedcase=''): """Runs a power flow. Runs a power flow [full AC Newton's method by default] and optionally returns the solved values in the data matrices, a flag which is C{True} if the algorithm was successful in finding a solution, and the elapsed time in seconds. All input arguments are optional. If C{casename} is provided it specifies the name of the input data file or dict containing the power flow data. The default value is 'case9'. If the ppopt is provided it overrides the default PYPOWER options vector and can be used to specify the solution algorithm and output options among other things. If the 3rd argument is given the pretty printed output will be appended to the file whose name is given in C{fname}. If C{solvedcase} is specified the solved case will be written to a case file in PYPOWER format with the specified name. If C{solvedcase} ends with '.mat' it saves the case as a MAT-file otherwise it saves it as a Python-file. If the C{ENFORCE_Q_LIMS} options is set to C{True} [default is false] then if any generator reactive power limit is violated after running the AC power flow, the corresponding bus is converted to a PQ bus, with Qg at the limit, and the case is re-run. The voltage magnitude at the bus will deviate from the specified value in order to satisfy the reactive power limit. If the reference bus is converted to PQ, the first remaining PV bus will be used as the slack bus for the next iteration. This may result in the real power output at this generator being slightly off from the specified values. Enforcing of generator Q limits inspired by contributions from Mu Lin, Lincoln University, New Zealand (1/14/05). @author: Ray Zimmerman (PSERC Cornell) """ ## default arguments if casedata is None: casedata = join(dirname(__file__), 'case9') ppopt = ppoption(ppopt) ## options verbose = ppopt["VERBOSE"] qlim = ppopt["ENFORCE_Q_LIMS"] ## enforce Q limits on gens? dc = ppopt["PF_DC"] ## use DC formulation? ## read data ppc = loadcase(casedata) ## add zero columns to branch for flows if needed if ppc["branch"].shape[1] < QT: ppc["branch"] = c_[ppc["branch"], zeros((ppc["branch"].shape[0], QT - ppc["branch"].shape[1] + 1))] ## convert to internal indexing ppc = ext2int(ppc) baseMVA, bus, gen, branch = \ ppc["baseMVA"], ppc["bus"], ppc["gen"], ppc["branch"] ## get bus index lists of each type of bus ref, pv, pq = bustypes(bus, gen) ## generator info on = find(gen[:, GEN_STATUS] > 0) ## which generators are on? gbus = gen[on, GEN_BUS].astype(int) ## what buses are they at? ##----- run the power flow ----- t0 = time() if verbose > 0: v = ppver('all') stdout.write('PYPOWER Version %s, %s' % (v["Version"], v["Date"])) if dc: # DC formulation if verbose: stdout.write(' -- DC Power Flow\n') ## initial state Va0 = bus[:, VA] * (pi / 180) ## build B matrices and phase shift injections B, Bf, Pbusinj, Pfinj = makeBdc(baseMVA, bus, branch) ## compute complex bus power injections [generation - load] ## adjusted for phase shifters and real shunts Pbus = makeSbus(baseMVA, bus, gen).real - Pbusinj - bus[:, GS] / baseMVA ## "run" the power flow Va = dcpf(B, Pbus, Va0, ref, pv, pq) ## update data matrices with solution branch[:, [QF, QT]] = zeros((branch.shape[0], 2)) branch[:, PF] = (Bf * Va + Pfinj) * baseMVA branch[:, PT] = -branch[:, PF] bus[:, VM] = ones(bus.shape[0]) bus[:, VA] = Va * (180 / pi) ## update Pg for slack generator (1st gen at ref bus) ## (note: other gens at ref bus are accounted for in Pbus) ## Pg = Pinj + Pload + Gs ## newPg = oldPg + newPinj - oldPinj refgen = zeros(len(ref), dtype=int) for k in range(len(ref)): temp = find(gbus == ref[k]) refgen[k] = on[temp[0]] gen[refgen, PG] = gen[refgen, PG] + (B[ref, :] * Va - Pbus[ref]) * baseMVA success = 1 else: ## AC formulation alg = ppopt['PF_ALG'] if verbose > 0: if alg == 1: solver = 'Newton' elif alg == 2: solver = 'fast-decoupled, XB' elif alg == 3: solver = 'fast-decoupled, BX' elif alg == 4: solver = 'Gauss-Seidel' else: solver = 'unknown' print(' -- AC Power Flow (%s)\n' % solver) ## initial state # V0 = ones(bus.shape[0]) ## flat start V0 = bus[:, VM] * exp(1j * pi/180 * bus[:, VA]) V0[gbus] = gen[on, VG] / abs(V0[gbus]) * V0[gbus] if qlim: ref0 = ref ## save index and angle of Varef0 = bus[ref0, VA] ## original reference bus(es) limited = [] ## list of indices of gens @ Q lims fixedQg = zeros(gen.shape[0]) ## Qg of gens at Q limits repeat = True while repeat: ## build admittance matrices Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch) ## compute complex bus power injections [generation - load] Sbus = makeSbus(baseMVA, bus, gen) ## run the power flow alg = ppopt["PF_ALG"] if alg == 1: V, success, _ = newtonpf(Ybus, Sbus, V0, ref, pv, pq, ppopt) elif alg == 2 or alg == 3: Bp, Bpp = makeB(baseMVA, bus, branch, alg) V, success, _ = fdpf(Ybus, Sbus, V0, Bp, Bpp, ref, pv, pq, ppopt) elif alg == 4: V, success, _ = gausspf(Ybus, Sbus, V0, ref, pv, pq, ppopt) else: stderr.write('Only Newton''s method, fast-decoupled, and ' 'Gauss-Seidel power flow algorithms currently ' 'implemented.\n') ## update data matrices with solution bus, gen, branch = pfsoln(baseMVA, bus, gen, branch, Ybus, Yf, Yt, V, ref, pv, pq) if qlim: ## enforce generator Q limits ## find gens with violated Q constraints gen_status = gen[:, GEN_STATUS] > 0 qg_max_lim = gen[:, QG] > gen[:, QMAX] qg_min_lim = gen[:, QG] < gen[:, QMIN] mx = find( gen_status & qg_max_lim ) mn = find( gen_status & qg_min_lim ) if len(mx) > 0 or len(mn) > 0: ## we have some Q limit violations # No PV generators if len(pv) == 0: if verbose: if len(mx) > 0: print('Gen %d [only one left] exceeds upper Q limit : INFEASIBLE PROBLEM\n' % mx + 1) else: print('Gen %d [only one left] exceeds lower Q limit : INFEASIBLE PROBLEM\n' % mn + 1) success = 0 break ## one at a time? if qlim == 2: ## fix largest violation, ignore the rest k = argmax(r_[gen[mx, QG] - gen[mx, QMAX], gen[mn, QMIN] - gen[mn, QG]]) if k > len(mx): mn = mn[k - len(mx)] mx = [] else: mx = mx[k] mn = [] if verbose and len(mx) > 0: for i in range(len(mx)): print('Gen ' + str(mx[i] + 1) + ' at upper Q limit, converting to PQ bus\n') if verbose and len(mn) > 0: for i in range(len(mn)): print('Gen ' + str(mn[i] + 1) + ' at lower Q limit, converting to PQ bus\n') ## save corresponding limit values fixedQg[mx] = gen[mx, QMAX] fixedQg[mn] = gen[mn, QMIN] mx = r_[mx, mn].astype(int) ## convert to PQ bus gen[mx, QG] = fixedQg[mx] ## set Qg to binding for i in range(len(mx)): ## [one at a time, since they may be at same bus] gen[mx[i], GEN_STATUS] = 0 ## temporarily turn off gen, bi = gen[mx[i], GEN_BUS] ## adjust load accordingly, bus[bi, [PD, QD]] = (bus[bi, [PD, QD]] - gen[mx[i], [PG, QG]]) if len(ref) > 1 and any(bus[gen[mx, GEN_BUS], BUS_TYPE] == REF): raise ValueError('Sorry, PYPOWER cannot enforce Q ' 'limits for slack buses in systems ' 'with multiple slacks.') bus[gen[mx, GEN_BUS].astype(int), BUS_TYPE] = PQ ## & set bus type to PQ ## update bus index lists of each type of bus ref_temp = ref ref, pv, pq = bustypes(bus, gen) if verbose and ref != ref_temp: print('Bus %d is new slack bus\n' % ref) limited = r_[limited, mx].astype(int) else: repeat = 0 ## no more generator Q limits violated else: repeat = 0 ## don't enforce generator Q limits, once is enough if qlim and len(limited) > 0: ## restore injections from limited gens [those at Q limits] gen[limited, QG] = fixedQg[limited] ## restore Qg value, for i in range(len(limited)): ## [one at a time, since they may be at same bus] bi = gen[limited[i], GEN_BUS] ## re-adjust load, bus[bi, [PD, QD]] = bus[bi, [PD, QD]] + gen[limited[i], [PG, QG]] gen[limited[i], GEN_STATUS] = 1 ## and turn gen back on if ref != ref0: ## adjust voltage angles to make original ref bus correct bus[:, VA] = bus[:, VA] - bus[ref0, VA] + Varef0 ppc["et"] = time() - t0 ppc["success"] = success ##----- output results ----- ## convert back to original bus numbering & print results ppc["bus"], ppc["gen"], ppc["branch"] = bus, gen, branch results = int2ext(ppc) ## zero out result fields of out-of-service gens & branches if len(results["order"]["gen"]["status"]["off"]) > 0: results["gen"][ix_(results["order"]["gen"]["status"]["off"], [PG, QG])] = 0 if len(results["order"]["branch"]["status"]["off"]) > 0: results["branch"][ix_(results["order"]["branch"]["status"]["off"], [PF, QF, PT, QT])] = 0 if fname: fd = None try: fd = open(fname, "a") except Exception as detail: stderr.write("Error opening %s: %s.\n" % (fname, detail)) finally: if fd is not None: printpf(results, fd, ppopt) fd.close() else: printpf(results, stdout, ppopt) ## save solved case if solvedcase: savecase(solvedcase, results) return results, success
def make_sdp_mat(ppc, base_mva): """equivalent of the Matpower function 'makesdpmat' for use in python with pypower case format input: pypower case file ppc (in this file the r and x are in pu already) input: base_mva return: functions y_k, y_k_, m_k, yline_ft, yline_tf, y_line_ft, y_line_tf, y_l, y_l_ . each function returns a matrix needed for the SDP relaxation of AC-OPF @author: Daniel Molzahn (PSERC U of Wisc, Madison) """ n_bus = len(ppc['bus'][:, BUS_I]) y, yf, yt = makeYbus.makeYbus(baseMVA=base_mva, bus=ppc['bus'], branch=ppc['branch']) e_mat = identity(n_bus) # identity matrix # k^th basis vector def e(k): return e_mat.A[:, [k]] # y_k_small for each k def y_k_small(k): return e(k)*e(k).T*y # Set tau == 0 to 1 (tau == 0 indicates nominal voltage ratio) ppc['branch'][ppc['branch'][:, TAP] == 0, TAP] = 1 # Matrices used in SDP relaxation of OPF problem def y_k(k): m11 = np.real(y_k_small(k) + y_k_small(k).T) m12 = np.imag(y_k_small(k).T - y_k_small(k)) m21 = np.imag(y_k_small(k) - y_k_small(k).T) m22 = np.real(y_k_small(k) + y_k_small(k).T) mat = np.vstack((np.hstack((m11, m12)), np.hstack((m21, m22)))) return (1/2) * mat def y_k_(k): m11 = np.imag(y_k_small(k) + y_k_small(k).T) m12 = np.real(y_k_small(k) - y_k_small(k).T) m21 = np.real(y_k_small(k).T - y_k_small(k)) m22 = np.imag(y_k_small(k) + y_k_small(k).T) mat = np.vstack((np.hstack((m11, m12)), np.hstack((m21, m22)))) return -1 * (1 / 2) * mat def m_k(k): return block_diag(e(k)*e(k).T, e(k)*e(k).T) # For the line limit matrices, specify a line index corresponding to the entries in mpc.branch def gl(line_index): # Real part of line admittance return np.real(1 / (ppc['branch'][line_index, BR_R] + 1j * ppc['branch'][line_index, BR_X])) def bl(line_index): # Imaginary part of line admittance return np.imag(1 / (ppc['branch'][line_index, BR_R] + 1j * ppc['branch'][line_index, BR_X])) def bsl(line_index): # Line shunt susceptance return ppc['branch'][line_index, BR_B] def rl(line_index): return ppc['branch'][line_index, BR_R] def xl(line_index): return ppc['branch'][line_index, BR_X] def tau(line_index): return ppc['branch'][line_index, TAP] def theta(line_index): return ppc['branch'][line_index, SHIFT]*np.pi / 180 def gb_cos_ft(line_index): return gl(line_index)*np.cos(theta(line_index)) + bl(line_index)*np.cos(theta(line_index) + np.pi / 2) def gb_sin_ft(line_index): return gl(line_index)*np.sin(theta(line_index)) + bl(line_index)*np.sin(theta(line_index) + np.pi / 2) def gb_cos_tf(line_index): return gl(line_index)*np.cos(-theta(line_index)) + bl(line_index)*np.cos(-theta(line_index) + np.pi / 2) def gb_sin_tf(line_index): return gl(line_index)*np.sin(-theta(line_index)) + bl(line_index)*np.sin(-theta(line_index) + np.pi / 2) def yline_ft(l_index): i_loc = [ppc['branch'][l_index, F_BUS], ppc['branch'][l_index, F_BUS], ppc['branch'][l_index, F_BUS], ppc['branch'][l_index, F_BUS] + n_bus, ppc['branch'][l_index, F_BUS] + n_bus, ppc['branch'][l_index, F_BUS] + n_bus] j_loc = [ppc['branch'][l_index, F_BUS], ppc['branch'][l_index, T_BUS], ppc['branch'][l_index, T_BUS] + n_bus, ppc['branch'][l_index, F_BUS] + n_bus, ppc['branch'][l_index, T_BUS], ppc['branch'][l_index, T_BUS] + n_bus] data = ([gl(l_index) / (tau(l_index)**2), -gb_cos_ft(l_index) / tau(l_index), gb_sin_ft(l_index) / tau(l_index), gl(l_index) / (tau(l_index)**2), -gb_sin_ft(l_index) / tau(l_index), -gb_cos_ft(l_index) / tau(l_index)]) matrix = coo_matrix((data, (i_loc, j_loc)), shape=(2*n_bus, 2*n_bus)) return 0.5*(matrix + matrix.T) def y_line_ft(l_index): i_loc = [ppc['branch'][l_index, F_BUS], ppc['branch'][l_index, F_BUS], ppc['branch'][l_index, F_BUS], ppc['branch'][l_index, F_BUS] + n_bus, ppc['branch'][l_index, F_BUS] + n_bus, ppc['branch'][l_index, F_BUS] + n_bus] j_loc = [ppc['branch'][l_index, F_BUS], ppc['branch'][l_index, T_BUS], ppc['branch'][l_index, T_BUS] + n_bus, ppc['branch'][l_index, F_BUS] + n_bus, ppc['branch'][l_index, T_BUS], ppc['branch'][l_index, T_BUS] + n_bus] data = ([-(bl(l_index)+bsl(l_index)/2)/(tau(l_index)**2), gb_sin_ft(l_index)/tau(l_index), gb_cos_ft(l_index)/tau(l_index), -(bl(l_index)+bsl(l_index)/2)/(tau(l_index)**2), -gb_cos_ft(l_index)/tau(l_index), gb_sin_ft(l_index)/tau(l_index)]) matrix = coo_matrix((data, (i_loc, j_loc)), shape=(2*n_bus, 2*n_bus)) return 0.5*(matrix + matrix.T) def yline_tf(l_index): i_loc = [ppc['branch'][l_index, F_BUS], ppc['branch'][l_index, F_BUS], ppc['branch'][l_index, F_BUS] + n_bus, ppc['branch'][l_index, F_BUS] + n_bus, ppc['branch'][l_index, T_BUS], ppc['branch'][l_index, T_BUS] + n_bus] j_loc = [ppc['branch'][l_index, T_BUS], ppc['branch'][l_index, T_BUS] + n_bus, ppc['branch'][l_index, T_BUS], ppc['branch'][l_index, T_BUS] + n_bus, ppc['branch'][l_index, T_BUS], ppc['branch'][l_index, T_BUS] + n_bus] data = ([-gb_cos_tf(l_index)/tau(l_index), -gb_sin_tf(l_index)/tau(l_index), gb_sin_tf(l_index)/tau(l_index), -gb_cos_tf(l_index)/tau(l_index), gl(l_index), gl(l_index)]) matrix = coo_matrix((data, (i_loc, j_loc)), shape=(2*n_bus, 2*n_bus)) return 0.5*(matrix + matrix.T) def y_line_tf(l_index): i_loc = [ppc['branch'][l_index, F_BUS], ppc['branch'][l_index, F_BUS], ppc['branch'][l_index, F_BUS] + n_bus, ppc['branch'][l_index, F_BUS] + n_bus, ppc['branch'][l_index, T_BUS], ppc['branch'][l_index, T_BUS] + n_bus] j_loc = [ppc['branch'][l_index, T_BUS], ppc['branch'][l_index, T_BUS] + n_bus, ppc['branch'][l_index, T_BUS], ppc['branch'][l_index, T_BUS] + n_bus, ppc['branch'][l_index, T_BUS], ppc['branch'][l_index, T_BUS] + n_bus] data = ([gb_sin_tf(l_index)/tau(l_index), -gb_cos_tf(l_index)/tau(l_index), gb_cos_tf(l_index)/tau(l_index), gb_sin_tf(l_index)/tau(l_index), -(bl(l_index)+bsl(l_index)/2), -(bl(l_index)+bsl(l_index)/2)]) matrix = coo_matrix((data, (i_loc, j_loc)), shape=(2*n_bus, 2*n_bus)) return 0.5*(matrix + matrix.T) def y_l(l_index): i_loc = [ppc['branch'][l_index, F_BUS], ppc['branch'][l_index, F_BUS], ppc['branch'][l_index, F_BUS] + n_bus, ppc['branch'][l_index, F_BUS] + n_bus, ppc['branch'][l_index, T_BUS], ppc['branch'][l_index, T_BUS], ppc['branch'][l_index, T_BUS] + n_bus, ppc['branch'][l_index, T_BUS] + n_bus] j_loc = [ppc['branch'][l_index, F_BUS], ppc['branch'][l_index, T_BUS], ppc['branch'][l_index, F_BUS] + n_bus, ppc['branch'][l_index, T_BUS] + n_bus, ppc['branch'][l_index, F_BUS], ppc['branch'][l_index, T_BUS], ppc['branch'][l_index, F_BUS] + n_bus, ppc['branch'][l_index, T_BUS] + n_bus] data = rl(l_index) * (gl(l_index)**2 + bl(l_index)**2) * np.array([1, -1, 1, -1, -1, 1, -1, 1]) return coo_matrix((data, (i_loc, j_loc)), shape=(2*n_bus, 2*n_bus)) def y_l_(l_index): i_loc = [ppc['branch'][l_index, F_BUS], ppc['branch'][l_index, F_BUS], ppc['branch'][l_index, F_BUS] + n_bus, ppc['branch'][l_index, F_BUS] + n_bus, ppc['branch'][l_index, T_BUS], ppc['branch'][l_index, T_BUS], ppc['branch'][l_index, T_BUS] + n_bus, ppc['branch'][l_index, T_BUS] + n_bus] j_loc = [ppc['branch'][l_index, F_BUS], ppc['branch'][l_index, T_BUS], ppc['branch'][l_index, F_BUS] + n_bus, ppc['branch'][l_index, T_BUS] + n_bus, ppc['branch'][l_index, F_BUS], ppc['branch'][l_index, T_BUS], ppc['branch'][l_index, F_BUS] + n_bus, ppc['branch'][l_index, T_BUS] + n_bus] data = (xl(l_index) * (gl(l_index)**2 + bl(l_index)**2)) * np.array([1, -1, 1, -1, -1, 1, -1, 1]) matrix = coo_matrix((data, (i_loc, j_loc)), shape=(2*n_bus, 2*n_bus)) # i_loc = [ppc['branch'][l_index, F_BUS], ppc['branch'][l_index, F_BUS] + n_bus, ppc['branch'][l_index, T_BUS], ppc['branch'][l_index, T_BUS] + n_bus] data = (bsl(l_index) / 2) * np.array([1, 1, 1, 1]) matrix_2 = coo_matrix((data, (i_loc, i_loc)), shape=(2*n_bus, 2*n_bus)) return matrix - matrix_2 return y_k, y_k_, m_k, yline_ft, yline_tf, y_line_ft, y_line_tf, y_l, y_l_
def runcpf(basecasedata=None, targetcasedata=None, ppopt=None, fname='', solvedcase=''): # default arguments if basecasedata is None: basecasedata = join(dirname(__file__), 'case9') if targetcasedata is None: targetcasedata = join(dirname(__file__), 'case9target') ppopt = ppoption(ppopt) # options verbose = ppopt["VERBOSE"] step = ppopt["CPF_STEP"] parameterization = ppopt["CPF_PARAMETERIZATION"] adapt_step = ppopt["CPF_ADAPT_STEP"] cb_args = ppopt["CPF_USER_CALLBACK_ARGS"] # set up callbacks callback_names = ["cpf_default_callback"] if len(ppopt["CPF_USER_CALLBACK"]) > 0: if isinstance(ppopt["CPF_USER_CALLBACK"], list): callback_names = r_[callback_names, ppopt["CPF_USER_CALLBACK"]] else: callback_names.append(ppopt["CPF_USER_CALLBACK"]) callbacks = [] for callback_name in callback_names: callbacks.append(getattr(cpf_callbacks, callback_name)) # read base case data ppcbase = loadcase(basecasedata) nb = ppcbase["bus"].shape[0] # add zero columns to branch for flows if needed if ppcbase["branch"].shape[1] < QT: ppcbase["branch"] = c_[ppcbase["branch"], zeros((ppcbase["branch"].shape[0], QT - ppcbase["branch"].shape[1] + 1))] # convert to internal indexing ppcbase = ext2int(ppcbase) baseMVAb, busb, genb, branchb = \ ppcbase["baseMVA"], ppcbase["bus"], ppcbase["gen"], ppcbase["branch"] # get bus index lists of each type of bus ref, pv, pq = bustypes(busb, genb) # generator info onb = find(genb[:, GEN_STATUS] > 0) # which generators are on? gbusb = genb[onb, GEN_BUS].astype(int) # what buses are they at? # read target case data ppctarget = loadcase(targetcasedata) # add zero columns to branch for flows if needed if ppctarget["branch"].shape[1] < QT: ppctarget["branch"] = c_[ppctarget["branch"], zeros( (ppctarget["branch"].shape[0], QT - ppctarget["branch"].shape[1] + 1))] # convert to internal indexing ppctarget = ext2int(ppctarget) baseMVAt, bust, gent, brancht = \ ppctarget["baseMVA"], ppctarget["bus"], ppctarget["gen"], ppctarget["branch"] # get bus index lists of each type of bus # ref, pv, pq = bustypes(bust, gent) # generator info ont = find(gent[:, GEN_STATUS] > 0) # which generators are on? gbust = gent[ont, GEN_BUS].astype(int) # what buses are they at? # ----- run the power flow ----- t0 = time() if verbose > 0: v = ppver('all') stdout.write('PYPOWER Version %s, %s' % (v["Version"], v["Date"])) stdout.write(' -- AC Continuation Power Flow\n') # initial state # V0 = ones(bus.shape[0]) ## flat start V0 = busb[:, VM] * exp(1j * pi / 180 * busb[:, VA]) vcb = ones(V0.shape) # create mask of voltage-controlled buses vcb[pq] = 0 # exclude PQ buses k = find(vcb[gbusb]) # in-service gens at v-c buses V0[gbusb[k]] = genb[onb[k], VG] / abs(V0[gbusb[k]]) * V0[gbusb[k]] # build admittance matrices Ybus, Yf, Yt = makeYbus(baseMVAb, busb, branchb) # compute base case complex bus power injections (generation - load) Sbusb = makeSbus(baseMVAb, busb, genb) # compute target case complex bus power injections (generation - load) Sbust = makeSbus(baseMVAt, bust, gent) # scheduled transfer Sxfr = Sbust - Sbusb # Run the base case power flow solution if verbose > 2: ppopt_pf = ppoption(ppopt, VERBOSE=max(0, verbose - 1)) else: ppopt_pf = ppoption(ppopt, VERBOSE=max(0, verbose - 2)) lam = 0 V, success, iterations = newtonpf(Ybus, Sbusb, V0, ref, pv, pq, ppopt_pf) if verbose > 2: print('step %3d : lambda = %6.3f\n' % (0, 0)) elif verbose > 1: print('step %3d : lambda = %6.3f, %2d Newton steps\n', (0, 0, iterations)) lamprv = lam # lam at previous step Vprv = V # V at previous step continuation = 1 cont_steps = 0 # input args for callbacks cb_data = { "ppc_base": ppcbase, "ppc_target": ppctarget, "Sxfr": Sxfr, "Ybus": Ybus, "Yf": Yf, "Yt": Yt, "ref": ref, "pv": pv, "pq": pq, "ppopt": ppopt } cb_state = {} # invoke callbacks for k in range(len(callbacks)): cb_state, _ = callbacks[k](cont_steps, V, lam, V, lam, cb_data, cb_state, cb_args) if linalg.norm(Sxfr) == 0: if verbose: print( 'base case and target case have identical load and generation\n' ) continuation = 0 V0 = V lam0 = lam # tangent predictor z = [dx;dlam] z = zeros(2 * len(V) + 1) z[-1] = 1.0 while continuation: cont_steps = cont_steps + 1 # prediction for next step V0, lam0, z = cpf_predictor(V, lam, Ybus, Sxfr, pv, pq, step, z, Vprv, lamprv, parameterization) # save previous voltage, lambda before updating Vprv = V lamprv = lam # correction V, success, i, lam = cpf_corrector(Ybus, Sbusb, V0, ref, pv, pq, lam0, Sxfr, Vprv, lamprv, z, step, parameterization, ppopt_pf) if not success: continuation = 0 if verbose: print( 'step %3d : lambda = %6.3f, corrector did not converge in %d iterations\n' % (cont_steps, lam, i)) break if verbose > 2: print('step %3d : lambda = %6.3f\n' % (cont_steps, lam)) elif verbose > 1: print('step %3d : lambda = %6.3f, %2d corrector Newton steps\n' % (cont_steps, lam, i)) # invoke callbacks for k in range(len(callbacks)): cb_state, _ = callbacks[k](cont_steps, V, lam, V0, lam0, cb_data, cb_state, cb_args) if isinstance(ppopt["CPF_STOP_AT"], str): if ppopt["CPF_STOP_AT"].upper() == "FULL": if abs(lam) < 1e-8: # traced the full continuation curve if verbose: print( '\nTraced full continuation curve in %d continuation steps\n' % cont_steps) continuation = 0 elif lam < lamprv and lam - step < 0: # next step will overshoot step = lam # modify step-size parameterization = 1 # change to natural parameterization adapt_step = False # disable step-adaptivity else: # == 'NOSE' if lam < lamprv: # reached the nose point if verbose: print( '\nReached steady state loading limit in %d continuation steps\n' % cont_steps) continuation = 0 else: if lam < lamprv: if verbose: print( '\nReached steady state loading limit in %d continuation steps\n' % cont_steps) continuation = 0 elif abs(ppopt["CPF_STOP_AT"] - lam) < 1e-8: # reached desired lambda if verbose: print( '\nReached desired lambda %3.2f in %d continuation steps\n' % (ppopt["CPF_STOP_AT"], cont_steps)) continuation = 0 # will reach desired lambda in next step elif lam + step > ppopt["CPF_STOP_AT"]: step = ppopt["CPF_STOP_AT"] - lam # modify step-size parameterization = 1 # change to natural parameterization adapt_step = False # disable step-adaptivity if adapt_step and continuation: pvpq = r_[pv, pq] # Adapt stepsize cpf_error = linalg.norm( r_[angle(V[pq]), abs(V[pvpq]), lam] - r_[angle(V0[pq]), abs(V0[pvpq]), lam0], inf) if cpf_error < ppopt["CPF_ERROR_TOL"]: # Increase stepsize step = step * ppopt["CPF_ERROR_TOL"] / cpf_error if step > ppopt["CPF_STEP_MAX"]: step = ppopt["CPF_STEP_MAX"] else: # decrese stepsize step = step * ppopt["CPF_ERROR_TOL"] / cpf_error if step < ppopt["CPF_STEP_MIN"]: step = ppopt["CPF_STEP_MIN"] # invoke callbacks if success: cpf_results = {} for k in range(len(callbacks)): cb_state, cpf_results = callbacks[k](cont_steps, V, lam, V0, lam0, cb_data, cb_state, cb_args, results=cpf_results, is_final=True) else: cpf_results["iterations"] = i # update bus and gen matrices to reflect the loading and generation # at the noise point bust[:, PD] = busb[:, PD] + lam * (bust[:, PD] - busb[:, PD]) bust[:, QD] = busb[:, QD] + lam * (bust[:, QD] - busb[:, QD]) gent[:, PG] = genb[:, PG] + lam * (gent[:, PG] - genb[:, PG]) # update data matrices with solution bust, gent, brancht = pfsoln(baseMVAt, bust, gent, brancht, Ybus, Yf, Yt, V, ref, pv, pq) ppctarget["et"] = time() - t0 ppctarget["success"] = success # ----- output results ----- # convert back to original bus numbering & print results ppctarget["bus"], ppctarget["gen"], ppctarget[ "branch"] = bust, gent, brancht if success: n = cpf_results["iterations"] + 1 cpf_results["V_p"] = i2e_data(ppctarget, cpf_results["V_p"], full((nb, n), nan), "bus", 0) cpf_results["V_c"] = i2e_data(ppctarget, cpf_results["V_c"], full((nb, n), nan), "bus", 0) results = int2ext(ppctarget) results["cpf"] = cpf_results # zero out result fields of out-of-service gens & branches if len(results["order"]["gen"]["status"]["off"]) > 0: results["gen"][ix_(results["order"]["gen"]["status"]["off"], [PG, QG])] = 0 if len(results["order"]["branch"]["status"]["off"]) > 0: results["branch"][ix_(results["order"]["branch"]["status"]["off"], [PF, QF, PT, QT])] = 0 if fname: fd = None try: fd = open(fname, "a") except Exception as detail: stderr.write("Error opening %s: %s.\n" % (fname, detail)) finally: if fd is not None: printpf(results, fd, ppopt) fd.close() else: printpf(results, stdout, ppopt) # save solved case if solvedcase: savecase(solvedcase, results) return results, success
def Ybus(ppc): ppc = ext2int(ppc) baseMVA, bus, gen, branch = ppc["baseMVA"], ppc["bus"], ppc["gen"], ppc[ "branch"] Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch) return Ybus
def solveropfnlp_2(ppc, solver="ipopt"): if solver == "ipopt": opt = SolverFactory("ipopt", executable="/home/iso/PycharmProjects/opfLC_python3/Python3/py_solvers/ipopt-linux64/ipopt") if solver == "bonmin": opt = SolverFactory("bonmin", executable="/home/iso/PycharmProjects/opfLC_python3/Python3/py_solvers/bonmin-linux64/bonmin") if solver == "knitro": opt = SolverFactory("knitro", executable="D:/ICT/Artelys/Knitro 10.2.1/knitroampl/knitroampl") ppc = ext2int(ppc) # convert to continuous indexing starting from 0 # Gather information about the system # ============================================================= baseMVA, bus, gen, branch = \ ppc["baseMVA"], ppc["bus"], ppc["gen"], ppc["branch"] nb = bus.shape[0] # number of buses ng = gen.shape[0] # number of generators nl = branch.shape[0] # number of lines # generator buses gb = tolist(np.array(gen[:, GEN_BUS]).astype(int)) sb = find((bus[:, BUS_TYPE] == REF)) # slack bus index fr = branch[:, F_BUS].astype(int) # from bus indices to = branch[:, T_BUS].astype(int) # to bus indices tr = branch[:, TAP] # transformation ratios tr[find(tr == 0)] = 1 # set to 1 transformation ratios that are 0 r = branch[:, BR_R] # branch resistances x = branch[:, BR_X] # branch reactances b = branch[:, BR_B] # branch susceptances start_time = time.clock() # Admittance matrix computation # ============================================================= y = makeYbus(baseMVA, bus, branch)[0] # admittance matrix yk = 1./(r+x*1j) # branch admittance yft = yk + 0.5j*b # branch admittance + susceptance gk = yk.real # branch resistance yk = yk/tr # include /tr in yk # Optimization # ============================================================= branch[find(branch[:, RATE_A] == 0), RATE_A] = 9999 # set undefined Sflow limit to 9999 Smax = branch[:, RATE_A] / baseMVA # Max. Sflow # Power demand parameters Pd = bus[:, PD] / baseMVA Qd = bus[:, QD] / baseMVA # Max and min Pg and Qg Pg_max = zeros(nb) Pg_max[gb] = gen[:, PMAX] / baseMVA Pg_min = zeros(nb) Pg_min[gb] = gen[:, PMIN] / baseMVA Qg_max = zeros(nb) Qg_max[gb] = gen[:, QMAX] / baseMVA Qg_min = zeros(nb) Qg_min[gb] = gen[:, QMIN] / baseMVA # Vmax and Vmin vectors Vmax = bus[:, VMAX] Vmin = bus[:, VMIN] vm = bus[:, VM] va = bus[:, VA]*pi/180 # create a new optimization model model = ConcreteModel() # Define sets # ------------ model.bus = Set(ordered=True, initialize=range(nb)) # Set of all buses model.gen = Set(ordered=True, initialize=gb) # Set of buses with generation model.line = Set(ordered=True, initialize=range(nl)) # Set of all lines # Define variables # ----------------- # Voltage magnitudes vector (vm) model.vm = Var(model.bus) # Voltage angles vector (va) model.va = Var(model.bus) # Reactive power generation, synchronous machines(SM) (Qg) model.Qg = Var(model.gen) Qg0 = zeros(nb) Qg0[gb] = gen[:, QG]/baseMVA # Active power generation, synchronous machines(SM) (Pg) model.Pg = Var(model.gen) Pg0 = zeros(nb) Pg0[gb] = gen[:, PG] / baseMVA # Active and reactive power from at all branches model.Pf = Var(model.line) model.Qf = Var(model.line) # Active and reactive power to at all branches model.Pt = Var(model.line) model.Qt = Var(model.line) # Warm start the problem # ------------------------ for i in range(nb): model.vm[i] = vm[i] model.va[i] = va[i] if i in gb: model.Pg[i] = Pg0[i] model.Qg[i] = Qg0[i] for i in range(nl): model.Pf[i] = vm[fr[i]] ** 2 * abs(yft[i]) / (tr[i] ** 2) * np.cos(-ang(yft[i])) -\ vm[fr[i]] * vm[to[i]] * abs(yk[i]) * np.cos(va[fr[i]] - va[to[i]] - ang(yk[i])) model.Qf[i] = vm[fr[i]] ** 2 * abs(yft[i]) / (tr[i] ** 2) * np.sin(-ang(yft[i])) -\ vm[fr[i]] * vm[to[i]] * abs(yk[i]) * np.sin(va[fr[i]] - va[to[i]] - ang(yk[i])) model.Pt[i] = vm[to[i]] ** 2 * abs(yft[i]) * np.cos(-ang(yft[i])) -\ vm[to[i]] * vm[fr[i]] * abs(yk[i]) * np.cos(va[to[i]] - va[fr[i]] - ang(yk[i])) model.Qt[i] = vm[to[i]] ** 2 * abs(yft[i]) * np.sin(-ang(yft[i])) -\ vm[to[i]] * vm[fr[i]] * abs(yk[i]) * np.sin(va[to[i]] - va[fr[i]] - ang(yk[i])) # Define constraints # ---------------------------- # Equalities: # ------------ # Active power flow equalities def powerflowact(model, i): if i in gb: return model.Pg[i]-Pd[i] == sum(model.vm[i]*model.vm[j]*abs(y[i, j]) * cos(model.va[i] - model.va[j] - ang(y[i, j])) for j in range(nb)) else: return sum(model.vm[i]*model.vm[j]*abs(y[i, j]) * cos(model.va[i] - model.va[j] - ang(y[i, j])) for j in range(nb)) == -Pd[i] model.const1 = Constraint(model.bus, rule=powerflowact) # Reactive power flow equalities def powerflowreact(model, i): if i in gb: return model.Qg[i]-Qd[i] == sum(model.vm[i]*model.vm[j]*abs(y[i, j]) * sin(model.va[i] - model.va[j] - ang(y[i, j])) for j in range(nb)) else: return sum(model.vm[i]*model.vm[j]*abs(y[i, j]) * sin(model.va[i] - model.va[j] - ang(y[i, j])) for j in range(nb)) == -Qd[i] model.const2 = Constraint(model.bus, rule=powerflowreact) # Active power from def pfrom(model, i): return model.Pf[i] == model.vm[fr[i]] ** 2 * abs(yft[i]) / (tr[i] ** 2) * np.cos(-ang(yft[i])) - \ model.vm[fr[i]] * model.vm[to[i]] * abs(yk[i]) * \ cos(model.va[fr[i]] - model.va[to[i]] - ang(yk[i])) model.const3 = Constraint(model.line, rule=pfrom) # Reactive power from def qfrom(model, i): return model.Qf[i] == model.vm[fr[i]] ** 2 * abs(yft[i]) / (tr[i] ** 2) * np.sin(-ang(yft[i])) - \ model.vm[fr[i]] * model.vm[to[i]] * abs(yk[i]) * \ sin(model.va[fr[i]] - model.va[to[i]] - ang(yk[i])) model.const4 = Constraint(model.line, rule=qfrom) # Active power to def pto(model, i): return model.Pt[i] == model.vm[to[i]] ** 2 * abs(yft[i]) * np.cos(-ang(yft[i])) - \ model.vm[to[i]] * model.vm[fr[i]] * abs(yk[i]) * \ cos(model.va[to[i]] - model.va[fr[i]] - ang(yk[i])) model.const5 = Constraint(model.line, rule=pto) # Reactive power to def qto(model, i): return model.Qt[i] == model.vm[to[i]] ** 2 * abs(yft[i]) * np.sin(-ang(yft[i])) - \ model.vm[to[i]] * model.vm[fr[i]] * abs(yk[i]) * \ sin(model.va[to[i]] - model.va[fr[i]] - ang(yk[i])) model.const6 = Constraint(model.line, rule=qto) # Slack bus phase angle model.const7 = Constraint(expr=model.va[sb[0]] == 0) # Inequalities: # ---------------- # Active power generator limits Pg_min <= Pg <= Pg_max def genplimits(model, i): return Pg_min[i] <= model.Pg[i] <= Pg_max[i] model.const8 = Constraint(model.gen, rule=genplimits) # Reactive power generator limits Qg_min <= Qg <= Qg_max def genqlimits(model, i): return Qg_min[i] <= model.Qg[i] <= Qg_max[i] model.const9 = Constraint(model.gen, rule=genqlimits) # Voltage constraints ( Vmin <= V <= Vmax ) def vlimits(model, i): return Vmin[i] <= model.vm[i] <= Vmax[i] model.const10 = Constraint(model.bus, rule=vlimits) # Sfrom line limit def sfrommax(model, i): return model.Pf[i]**2 + model.Qf[i]**2 <= Smax[i]**2 model.const11 = Constraint(model.line, rule=sfrommax) # Sto line limit def stomax(model, i): return model.Pt[i]**2 + model.Qt[i]**2 <= Smax[i]**2 model.const12 = Constraint(model.line, rule=stomax) # Set objective function # ------------------------ def obj_fun(model): return sum(gk[i] * ((model.vm[fr[i]] / tr[i])**2 + model.vm[to[i]]**2 - 2/tr[i] * model.vm[fr[i]] * model.vm[to[i]] * cos(model.va[fr[i]] - model.va[to[i]])) for i in range(nl)) model.obj = Objective(rule=obj_fun, sense=minimize) mt = time.clock() - start_time # Modeling time # Execute solve command with the selected solver # ------------------------------------------------ start_time = time.clock() results = opt.solve(model, tee=True) et = time.clock() - start_time # Elapsed time print(results) # Update the case info with the optimized variables # ================================================== for i in range(nb): bus[i, VM] = model.vm[i].value # Bus voltage magnitudes bus[i, VA] = model.va[i].value*180/pi # Bus voltage angles # Include Pf - Qf - Pt - Qt in the branch matrix branchsol = zeros((nl, 17)) branchsol[:, :-4] = branch for i in range(nl): branchsol[i, PF] = model.Pf[i].value * baseMVA branchsol[i, QF] = model.Qf[i].value * baseMVA branchsol[i, PT] = model.Pt[i].value * baseMVA branchsol[i, QT] = model.Qt[i].value * baseMVA # Update gen matrix variables for i in range(ng): gen[i, PG] = model.Pg[gb[i]].value * baseMVA gen[i, QG] = model.Qg[gb[i]].value * baseMVA gen[i, VG] = bus[gb[i], VM] # Convert to external (original) numbering and save case results ppc = int2ext(ppc) ppc['bus'][:, 1:] = bus[:, 1:] branchsol[:, 0:2] = ppc['branch'][:, 0:2] ppc['branch'] = branchsol ppc['gen'][:, 1:] = gen[:, 1:] ppc['obj'] = value(obj_fun(model)) ppc['ploss'] = value(obj_fun(model)) * baseMVA ppc['et'] = et ppc['mt'] = mt ppc['success'] = 1 # ppc solved case is returned return ppc
def pipsopf_solver(om, ppopt, out_opt=None): """Solves AC optimal power flow using PIPS. Inputs are an OPF model object, a PYPOWER options vector and a dict containing keys (can be empty) for each of the desired optional output fields. outputs are a C{results} dict, C{success} flag and C{raw} output dict. C{results} is a PYPOWER case dict (ppc) with the usual baseMVA, bus branch, gen, gencost fields, along with the following additional fields: - C{order} see 'help ext2int' for details of this field - C{x} final value of optimization variables (internal order) - C{f} final objective function value - C{mu} shadow prices on ... - C{var} - C{l} lower bounds on variables - C{u} upper bounds on variables - C{nln} - C{l} lower bounds on nonlinear constraints - C{u} upper bounds on nonlinear constraints - C{lin} - C{l} lower bounds on linear constraints - C{u} upper bounds on linear constraints C{success} is C{True} if solver converged successfully, C{False} otherwise C{raw} is a raw output dict in form returned by MINOS - xr final value of optimization variables - pimul constraint multipliers - info solver specific termination code - output solver specific output information @see: L{opf}, L{pips} @author: Ray Zimmerman (PSERC Cornell) @author: Carlos E. Murillo-Sanchez (PSERC Cornell & Universidad Autonoma de Manizales) @author: Richard Lincoln """ ##----- initialization ----- ## optional output if out_opt is None: out_opt = {} ## options verbose = ppopt['VERBOSE'] feastol = ppopt['PDIPM_FEASTOL'] gradtol = ppopt['PDIPM_GRADTOL'] comptol = ppopt['PDIPM_COMPTOL'] costtol = ppopt['PDIPM_COSTTOL'] max_it = ppopt['PDIPM_MAX_IT'] max_red = ppopt['SCPDIPM_RED_IT'] step_control = (ppopt['OPF_ALG'] == 565) ## OPF_ALG == 565, PIPS-sc if feastol == 0: feastol = ppopt['OPF_VIOLATION'] opt = { 'feastol': feastol, 'gradtol': gradtol, 'comptol': comptol, 'costtol': costtol, 'max_it': max_it, 'max_red': max_red, 'step_control': step_control, 'cost_mult': 1e-4, 'verbose': verbose } ## unpack data ppc = om.get_ppc() baseMVA, bus, gen, branch, gencost = \ ppc["baseMVA"], ppc["bus"], ppc["gen"], ppc["branch"], ppc["gencost"] vv, _, nn, _ = om.get_idx() ## problem dimensions nb = bus.shape[0] ## number of buses nl = branch.shape[0] ## number of branches ny = om.getN('var', 'y') ## number of piece-wise linear costs ## linear constraints A, l, u = om.linear_constraints() ## bounds on optimization vars _, xmin, xmax = om.getv() ## build admittance matrices Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch) ## try to select an interior initial point ll, uu = xmin.copy(), xmax.copy() ll[xmin == -Inf] = -1e10 ## replace Inf with numerical proxies uu[xmax == Inf] = 1e10 x0 = (ll + uu) / 2 Varefs = bus[bus[:, BUS_TYPE] == REF, VA] * (pi / 180) ## angles set to first reference angle x0[vv["i1"]["Va"]:vv["iN"]["Va"]] = Varefs[0] if ny > 0: ipwl = find(gencost[:, MODEL] == PW_LINEAR) # PQ = r_[gen[:, PMAX], gen[:, QMAX]] # c = totcost(gencost[ipwl, :], PQ[ipwl]) c = gencost.flatten('F')[sub2ind(gencost.shape, ipwl, NCOST+2*gencost[ipwl, NCOST])] ## largest y-value in CCV data x0[vv["i1"]["y"]:vv["iN"]["y"]] = max(c) + 0.1 * abs(max(c)) # x0[vv["i1"]["y"]:vv["iN"]["y"]] = c + 0.1 * abs(c) ## find branches with flow limits il = find((branch[:, RATE_A] != 0) & (branch[:, RATE_A] < 1e10)) nl2 = len(il) ## number of constrained lines ##----- run opf ----- f_fcn = lambda x, return_hessian=False: opf_costfcn(x, om, return_hessian) gh_fcn = lambda x: opf_consfcn(x, om, Ybus, Yf[il, :], Yt[il,:], ppopt, il) hess_fcn = lambda x, lmbda, cost_mult: opf_hessfcn(x, lmbda, om, Ybus, Yf[il, :], Yt[il, :], ppopt, il, cost_mult) solution = pips(f_fcn, x0, A, l, u, xmin, xmax, gh_fcn, hess_fcn, opt) x, f, info, lmbda, output = solution["x"], solution["f"], \ solution["eflag"], solution["lmbda"], solution["output"] success = (info > 0) ## update solution data Va = x[vv["i1"]["Va"]:vv["iN"]["Va"]] Vm = x[vv["i1"]["Vm"]:vv["iN"]["Vm"]] Pg = x[vv["i1"]["Pg"]:vv["iN"]["Pg"]] Qg = x[vv["i1"]["Qg"]:vv["iN"]["Qg"]] V = Vm * exp(1j * Va) ##----- calculate return values ----- ## update voltages & generator outputs bus[:, VA] = Va * 180 / pi bus[:, VM] = Vm gen[:, PG] = Pg * baseMVA gen[:, QG] = Qg * baseMVA gen[:, VG] = Vm[ gen[:, GEN_BUS].astype(int) ] ## compute branch flows Sf = V[ branch[:, F_BUS].astype(int) ] * conj(Yf * V) ## cplx pwr at "from" bus, p["u"]. St = V[ branch[:, T_BUS].astype(int) ] * conj(Yt * V) ## cplx pwr at "to" bus, p["u"]. branch[:, PF] = Sf.real * baseMVA branch[:, QF] = Sf.imag * baseMVA branch[:, PT] = St.real * baseMVA branch[:, QT] = St.imag * baseMVA ## line constraint is actually on square of limit ## so we must fix multipliers muSf = zeros(nl) muSt = zeros(nl) if len(il) > 0: muSf[il] = \ 2 * lmbda["ineqnonlin"][:nl2] * branch[il, RATE_A] / baseMVA muSt[il] = \ 2 * lmbda["ineqnonlin"][nl2:nl2+nl2] * branch[il, RATE_A] / baseMVA ## update Lagrange multipliers bus[:, MU_VMAX] = lmbda["upper"][vv["i1"]["Vm"]:vv["iN"]["Vm"]] bus[:, MU_VMIN] = lmbda["lower"][vv["i1"]["Vm"]:vv["iN"]["Vm"]] gen[:, MU_PMAX] = lmbda["upper"][vv["i1"]["Pg"]:vv["iN"]["Pg"]] / baseMVA gen[:, MU_PMIN] = lmbda["lower"][vv["i1"]["Pg"]:vv["iN"]["Pg"]] / baseMVA gen[:, MU_QMAX] = lmbda["upper"][vv["i1"]["Qg"]:vv["iN"]["Qg"]] / baseMVA gen[:, MU_QMIN] = lmbda["lower"][vv["i1"]["Qg"]:vv["iN"]["Qg"]] / baseMVA bus[:, LAM_P] = \ lmbda["eqnonlin"][nn["i1"]["Pmis"]:nn["iN"]["Pmis"]] / baseMVA bus[:, LAM_Q] = \ lmbda["eqnonlin"][nn["i1"]["Qmis"]:nn["iN"]["Qmis"]] / baseMVA branch[:, MU_SF] = muSf / baseMVA branch[:, MU_ST] = muSt / baseMVA ## package up results nlnN = om.getN('nln') ## extract multipliers for nonlinear constraints kl = find(lmbda["eqnonlin"] < 0) ku = find(lmbda["eqnonlin"] > 0) nl_mu_l = zeros(nlnN) nl_mu_u = r_[zeros(2*nb), muSf, muSt] nl_mu_l[kl] = -lmbda["eqnonlin"][kl] nl_mu_u[ku] = lmbda["eqnonlin"][ku] mu = { 'var': {'l': lmbda["lower"], 'u': lmbda["upper"]}, 'nln': {'l': nl_mu_l, 'u': nl_mu_u}, 'lin': {'l': lmbda["mu_l"], 'u': lmbda["mu_u"]} } results = ppc results["bus"], results["branch"], results["gen"], \ results["om"], results["x"], results["mu"], results["f"] = \ bus, branch, gen, om, x, mu, f pimul = r_[ results["mu"]["nln"]["l"] - results["mu"]["nln"]["u"], results["mu"]["lin"]["l"] - results["mu"]["lin"]["u"], -ones(ny), results["mu"]["var"]["l"] - results["mu"]["var"]["u"], ] raw = {'xr': x, 'pimul': pimul, 'info': info, 'output': output} return results, success, raw
def run_sim(ppc, elements, dynopt=None, events=None, recorder=None): """ Run a time-domain simulation Inputs: ppc PYPOWER load flow case elements Dictionary of dynamic model objects (machines, controllers, etc) with Object ID as key events Events object recorder Recorder object (empty) Outputs: recorder Recorder object (with data) """ ######### # SETUP # ######### # Get version information ver = pydyn_ver() print('PYPOWER-Dynamics ' + ver['Version'] + ', ' + ver['Date']) # Program options if dynopt: h = dynopt['h'] t_sim = dynopt['t_sim'] max_err = dynopt['max_err'] max_iter = dynopt['max_iter'] verbose = dynopt['verbose'] else: # Default program options h = 0.01 # step length (s) t_sim = 5 # simulation time (s) max_err = 0.0001 # Maximum error in network iteration (voltage mismatches) max_iter = 25 # Maximum number of network iterations verbose = False # Make lists of current injection sources (generators, external grids, etc) and controllers sources = [] controllers = [] for element in elements.values(): if element.__module__ in [ 'pydyn.sym_order6a', 'pydyn.sym_order6b', 'pydyn.sym_order4', 'pydyn.ext_grid', 'pydyn.vsc_average', 'pydyn.asym_1cage', 'pydyn.asym_2cage' ]: sources.append(element) if element.__module__ == 'pydyn.controller': controllers.append(element) # Set up interfaces interfaces = init_interfaces(elements) ################## # INITIALISATION # ################## print('Initialising models...') # Run power flow and update bus voltages and angles in PYPOWER case object results, success = runpf(ppc) ppc["bus"][:, VM] = results["bus"][:, VM] ppc["bus"][:, VA] = results["bus"][:, VA] # Build Ybus matrix ppc_int = ext2int(ppc) baseMVA, bus, branch = ppc_int["baseMVA"], ppc_int["bus"], ppc_int[ "branch"] Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch) # Build modified Ybus matrix Ybus = mod_Ybus(Ybus, elements, bus, ppc_int['gen'], baseMVA) # Calculate initial voltage phasors v0 = bus[:, VM] * (np.cos(np.radians(bus[:, VA])) + 1j * np.sin(np.radians(bus[:, VA]))) # Initialise sources from load flow for source in sources: if source.__module__ in ['pydyn.asym_1cage', 'pydyn.asym_2cage']: # Asynchronous machine source_bus = int(ppc_int['bus'][source.bus_no, 0]) v_source = v0[source_bus] source.initialise(v_source, 0) else: # Generator or VSC source_bus = int(ppc_int['gen'][source.gen_no, 0]) S_source = np.complex(results["gen"][source.gen_no, 1] / baseMVA, results["gen"][source.gen_no, 2] / baseMVA) v_source = v0[source_bus] source.initialise(v_source, S_source) # Interface controllers and machines (for initialisation) for intf in interfaces: int_type = intf[0] var_name = intf[1] if int_type == 'OUTPUT': # If an output, interface in the reverse direction for initialisation intf[2].signals[var_name] = intf[3].signals[var_name] else: # Inputs are interfaced in normal direction during initialisation intf[3].signals[var_name] = intf[2].signals[var_name] # Initialise controllers for controller in controllers: controller.initialise() ############# # MAIN LOOP # ############# if events == None: print('Warning: no events!') # Factorise Ybus matrix Ybus_inv = splu(Ybus) y1 = [] v_prev = v0 print('Simulating...') for t in range(int(t_sim / h) + 1): if np.mod(t, 1 / h) == 0: print('t=' + str(t * h) + 's') # Interface controllers and machines for intf in interfaces: var_name = intf[1] intf[3].signals[var_name] = intf[2].signals[var_name] # Solve differential equations for j in range(4): # Solve step of differential equations for element in elements.values(): element.solve_step(h, j) # Interface with network equations v_prev = solve_network(sources, v_prev, Ybus_inv, ppc_int, len(bus), max_err, max_iter) if recorder != None: # Record signals or states recorder.record_variables(t * h, elements) if events != None: # Check event stack ppc, refactorise = events.handle_events(np.round(t * h, 5), elements, ppc, baseMVA) if refactorise == True: # Rebuild Ybus from new ppc_int prev_ybus = Ybus.todense().copy() ppc_int = ext2int(ppc) baseMVA, bus, branch = ppc_int["baseMVA"], ppc_int[ "bus"], ppc_int["branch"] Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch) # Rebuild modified Ybus Ybus = mod_Ybus(Ybus, elements, bus, ppc_int['gen'], baseMVA) ybus_delta = np.array(Ybus - prev_ybus) # Refactorise Ybus Ybus_inv = splu(Ybus) # Solve network equations v_prev = solve_network(sources, v_prev, Ybus_inv, ppc_int, len(bus), max_err, max_iter) return recorder
def admittance_from_UKGDS(filename,header=28,separate_Yslack=True): baseMVA,bus_matrix,branch_matrix = bus_branch_from_UKGDSexcel(filename,header) return makeYbus(baseMVA,bus_matrix,branch_matrix,separate_Yslack)
def t_jacobian(quiet=False): """Numerical tests of partial derivative code. @author: Ray Zimmerman (PSERC Cornell) """ t_begin(28, quiet) ## run powerflow to get solved case ppopt = ppoption(VERBOSE=0, OUT_ALL=0) ppc = loadcase(case30()) results, _ = runpf(ppc, ppopt) baseMVA, bus, gen, branch = \ results['baseMVA'], results['bus'], results['gen'], results['branch'] ## switch to internal bus numbering and build admittance matrices _, bus, gen, branch = ext2int1(bus, gen, branch) Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch) Ybus_full = Ybus.todense() Yf_full = Yf.todense() Yt_full = Yt.todense() Vm = bus[:, VM] Va = bus[:, VA] * (pi / 180) V = Vm * exp(1j * Va) f = branch[:, F_BUS].astype(int) ## list of "from" buses t = branch[:, T_BUS].astype(int) ## list of "to" buses #nl = len(f) nb = len(V) pert = 1e-8 Vm = array([Vm]).T # column array Va = array([Va]).T # column array Vc = array([V]).T # column array ##----- check dSbus_dV code ----- ## full matrices dSbus_dVm_full, dSbus_dVa_full = dSbus_dV(Ybus_full, V) ## sparse matrices dSbus_dVm, dSbus_dVa = dSbus_dV(Ybus, V) dSbus_dVm_sp = dSbus_dVm.todense() dSbus_dVa_sp = dSbus_dVa.todense() ## compute numerically to compare Vmp = (Vm * ones((1, nb)) + pert*eye(nb)) * (exp(1j * Va) * ones((1, nb))) Vap = (Vm * ones((1, nb))) * (exp(1j * (Va*ones((1, nb)) + pert*eye(nb)))) num_dSbus_dVm = (Vmp * conj(Ybus * Vmp) - Vc * ones((1, nb)) * conj(Ybus * Vc * ones((1, nb)))) / pert num_dSbus_dVa = (Vap * conj(Ybus * Vap) - Vc * ones((1, nb)) * conj(Ybus * Vc * ones((1, nb)))) / pert t_is(dSbus_dVm_sp, num_dSbus_dVm, 5, 'dSbus_dVm (sparse)') t_is(dSbus_dVa_sp, num_dSbus_dVa, 5, 'dSbus_dVa (sparse)') t_is(dSbus_dVm_full, num_dSbus_dVm, 5, 'dSbus_dVm (full)') t_is(dSbus_dVa_full, num_dSbus_dVa, 5, 'dSbus_dVa (full)') ##----- check dSbr_dV code ----- ## full matrices dSf_dVa_full, dSf_dVm_full, dSt_dVa_full, dSt_dVm_full, _, _ = \ dSbr_dV(branch, Yf_full, Yt_full, V) ## sparse matrices dSf_dVa, dSf_dVm, dSt_dVa, dSt_dVm, Sf, St = dSbr_dV(branch, Yf, Yt, V) dSf_dVa_sp = dSf_dVa.todense() dSf_dVm_sp = dSf_dVm.todense() dSt_dVa_sp = dSt_dVa.todense() dSt_dVm_sp = dSt_dVm.todense() ## compute numerically to compare Vmpf = Vmp[f, :] Vapf = Vap[f, :] Vmpt = Vmp[t, :] Vapt = Vap[t, :] Sf2 = (Vc[f] * ones((1, nb))) * conj(Yf * Vc * ones((1, nb))) St2 = (Vc[t] * ones((1, nb))) * conj(Yt * Vc * ones((1, nb))) Smpf = Vmpf * conj(Yf * Vmp) Sapf = Vapf * conj(Yf * Vap) Smpt = Vmpt * conj(Yt * Vmp) Sapt = Vapt * conj(Yt * Vap) num_dSf_dVm = (Smpf - Sf2) / pert num_dSf_dVa = (Sapf - Sf2) / pert num_dSt_dVm = (Smpt - St2) / pert num_dSt_dVa = (Sapt - St2) / pert t_is(dSf_dVm_sp, num_dSf_dVm, 5, 'dSf_dVm (sparse)') t_is(dSf_dVa_sp, num_dSf_dVa, 5, 'dSf_dVa (sparse)') t_is(dSt_dVm_sp, num_dSt_dVm, 5, 'dSt_dVm (sparse)') t_is(dSt_dVa_sp, num_dSt_dVa, 5, 'dSt_dVa (sparse)') t_is(dSf_dVm_full, num_dSf_dVm, 5, 'dSf_dVm (full)') t_is(dSf_dVa_full, num_dSf_dVa, 5, 'dSf_dVa (full)') t_is(dSt_dVm_full, num_dSt_dVm, 5, 'dSt_dVm (full)') t_is(dSt_dVa_full, num_dSt_dVa, 5, 'dSt_dVa (full)') ##----- check dAbr_dV code ----- ## full matrices dAf_dVa_full, dAf_dVm_full, dAt_dVa_full, dAt_dVm_full = \ dAbr_dV(dSf_dVa_full, dSf_dVm_full, dSt_dVa_full, dSt_dVm_full, Sf, St) ## sparse matrices dAf_dVa, dAf_dVm, dAt_dVa, dAt_dVm = \ dAbr_dV(dSf_dVa, dSf_dVm, dSt_dVa, dSt_dVm, Sf, St) dAf_dVa_sp = dAf_dVa.todense() dAf_dVm_sp = dAf_dVm.todense() dAt_dVa_sp = dAt_dVa.todense() dAt_dVm_sp = dAt_dVm.todense() ## compute numerically to compare num_dAf_dVm = (abs(Smpf)**2 - abs(Sf2)**2) / pert num_dAf_dVa = (abs(Sapf)**2 - abs(Sf2)**2) / pert num_dAt_dVm = (abs(Smpt)**2 - abs(St2)**2) / pert num_dAt_dVa = (abs(Sapt)**2 - abs(St2)**2) / pert t_is(dAf_dVm_sp, num_dAf_dVm, 4, 'dAf_dVm (sparse)') t_is(dAf_dVa_sp, num_dAf_dVa, 4, 'dAf_dVa (sparse)') t_is(dAt_dVm_sp, num_dAt_dVm, 4, 'dAt_dVm (sparse)') t_is(dAt_dVa_sp, num_dAt_dVa, 4, 'dAt_dVa (sparse)') t_is(dAf_dVm_full, num_dAf_dVm, 4, 'dAf_dVm (full)') t_is(dAf_dVa_full, num_dAf_dVa, 4, 'dAf_dVa (full)') t_is(dAt_dVm_full, num_dAt_dVm, 4, 'dAt_dVm (full)') t_is(dAt_dVa_full, num_dAt_dVa, 4, 'dAt_dVa (full)') ##----- check dIbr_dV code ----- ## full matrices dIf_dVa_full, dIf_dVm_full, dIt_dVa_full, dIt_dVm_full, _, _ = \ dIbr_dV(branch, Yf_full, Yt_full, V) ## sparse matrices dIf_dVa, dIf_dVm, dIt_dVa, dIt_dVm, _, _ = dIbr_dV(branch, Yf, Yt, V) dIf_dVa_sp = dIf_dVa.todense() dIf_dVm_sp = dIf_dVm.todense() dIt_dVa_sp = dIt_dVa.todense() dIt_dVm_sp = dIt_dVm.todense() ## compute numerically to compare num_dIf_dVm = (Yf * Vmp - Yf * Vc * ones((1, nb))) / pert num_dIf_dVa = (Yf * Vap - Yf * Vc * ones((1, nb))) / pert num_dIt_dVm = (Yt * Vmp - Yt * Vc * ones((1, nb))) / pert num_dIt_dVa = (Yt * Vap - Yt * Vc * ones((1, nb))) / pert t_is(dIf_dVm_sp, num_dIf_dVm, 5, 'dIf_dVm (sparse)') t_is(dIf_dVa_sp, num_dIf_dVa, 5, 'dIf_dVa (sparse)') t_is(dIt_dVm_sp, num_dIt_dVm, 5, 'dIt_dVm (sparse)') t_is(dIt_dVa_sp, num_dIt_dVa, 5, 'dIt_dVa (sparse)') t_is(dIf_dVm_full, num_dIf_dVm, 5, 'dIf_dVm (full)') t_is(dIf_dVa_full, num_dIf_dVa, 5, 'dIf_dVa (full)') t_is(dIt_dVm_full, num_dIt_dVm, 5, 'dIt_dVm (full)') t_is(dIt_dVa_full, num_dIt_dVa, 5, 'dIt_dVa (full)') t_end()
def opf_execute(om, ppopt): """Executes the OPF specified by an OPF model object. C{results} are returned with internal indexing, all equipment in-service, etc. @see: L{opf}, L{opf_setup} @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln """ ##----- setup ----- ## options dc = ppopt['PF_DC'] ## 1 = DC OPF, 0 = AC OPF alg = ppopt['OPF_ALG'] verbose = ppopt['VERBOSE'] ## build user-defined costs om.build_cost_params() ## get indexing vv, ll, nn, _ = om.get_idx() if verbose > 0: v = ppver('all') stdout.write('PYPOWER Version %s, %s' % (v['Version'], v['Date'])) ##----- run DC OPF solver ----- if dc: if verbose > 0: stdout.write(' -- DC Optimal Power Flow\n') results, success, raw = dcopf_solver(om, ppopt) else: ##----- run AC OPF solver ----- if verbose > 0: stdout.write(' -- AC Optimal Power Flow\n') ## if OPF_ALG not set, choose best available option if alg == 0: alg = 560 ## MIPS ## update deprecated algorithm codes to new, generalized formulation equivalents if alg == 100 | alg == 200: ## CONSTR alg = 300 elif alg == 120 | alg == 220: ## dense LP alg = 320 elif alg == 140 | alg == 240: ## sparse (relaxed) LP alg = 340 elif alg == 160 | alg == 260: ## sparse (full) LP alg = 360 ppopt['OPF_ALG_POLY'] = alg ## run specific AC OPF solver if alg == 560 or alg == 565: ## PIPS results, success, raw = pipsopf_solver(om, ppopt) elif alg == 580: ## IPOPT # pragma: no cover try: __import__('pyipopt') results, success, raw = ipoptopf_solver(om, ppopt) except ImportError: raise ImportError('OPF_ALG %d requires IPOPT ' '(see https://projects.coin-or.org/Ipopt/)' % alg) else: stderr.write( 'opf_execute: OPF_ALG %d is not a valid algorithm code\n' % alg) if ('output' not in raw) or ('alg' not in raw['output']): raw['output']['alg'] = alg if success: if not dc: ## copy bus voltages back to gen matrix results['gen'][:, VG] = results['bus'][ results['gen'][:, GEN_BUS].astype(int), VM] ## gen PQ capability curve multipliers if (ll['N']['PQh'] > 0) | (ll['N']['PQl'] > 0): # pragma: no cover mu_PQh = results['mu']['lin']['l'][ ll['i1']['PQh']:ll['iN']['PQh']] - results['mu']['lin'][ 'u'][ll['i1']['PQh']:ll['iN']['PQh']] mu_PQl = results['mu']['lin']['l'][ ll['i1']['PQl']:ll['iN']['PQl']] - results['mu']['lin'][ 'u'][ll['i1']['PQl']:ll['iN']['PQl']] Apqdata = om.userdata('Apqdata') results['gen'] = update_mupq(results['baseMVA'], results['gen'], mu_PQh, mu_PQl, Apqdata) ## compute g, dg, f, df, d2f if requested by RETURN_RAW_DER = 1 if ppopt['RETURN_RAW_DER']: # pragma: no cover ## move from results to raw if using v4.0 of MINOPF or TSPOPF if 'dg' in results: raw = {} raw['dg'] = results['dg'] raw['g'] = results['g'] ## compute g, dg, unless already done by post-v4.0 MINOPF or TSPOPF if 'dg' not in raw: ppc = om.get_ppc() Ybus, Yf, Yt = makeYbus(ppc['baseMVA'], ppc['bus'], ppc['branch']) g, geq, dg, dgeq = opf_consfcn(results['x'], om, Ybus, Yf, Yt, ppopt) raw['g'] = r_[geq, g] raw['dg'] = r_[dgeq.T, dg.T] ## true Jacobian organization ## compute df, d2f _, df, d2f = opf_costfcn(results['x'], om, True) raw['df'] = df raw['d2f'] = d2f ## delete g and dg fieldsfrom results if using v4.0 of MINOPF or TSPOPF if 'dg' in results: del results['dg'] del results['g'] ## angle limit constraint multipliers if ll['N']['ang'] > 0: iang = om.userdata('iang') results['branch'][iang, MU_ANGMIN] = results['mu']['lin']['l'][ ll['i1']['ang']:ll['iN']['ang']] * pi / 180 results['branch'][iang, MU_ANGMAX] = results['mu']['lin']['u'][ ll['i1']['ang']:ll['iN']['ang']] * pi / 180 else: ## assign empty g, dg, f, df, d2f if requested by RETURN_RAW_DER = 1 if not dc and ppopt['RETURN_RAW_DER']: raw['dg'] = array([]) raw['g'] = array([]) raw['df'] = array([]) raw['d2f'] = array([]) ## assign values and limit shadow prices for variables if om.var['order']: results['var'] = {'val': {}, 'mu': {'l': {}, 'u': {}}} for name in om.var['order']: if om.getN('var', name): idx = arange(vv['i1'][name], vv['iN'][name]) results['var']['val'][name] = results['x'][idx] results['var']['mu']['l'][name] = results['mu']['var']['l'][idx] results['var']['mu']['u'][name] = results['mu']['var']['u'][idx] ## assign shadow prices for linear constraints if om.lin['order']: results['lin'] = {'mu': {'l': {}, 'u': {}}} for name in om.lin['order']: if om.getN('lin', name): idx = arange(ll['i1'][name], ll['iN'][name]) results['lin']['mu']['l'][name] = results['mu']['lin']['l'][idx] results['lin']['mu']['u'][name] = results['mu']['lin']['u'][idx] ## assign shadow prices for nonlinear constraints if not dc: if om.nln['order']: results['nln'] = {'mu': {'l': {}, 'u': {}}} for name in om.nln['order']: if om.getN('nln', name): idx = arange(nn['i1'][name], nn['iN'][name]) results['nln']['mu']['l'][name] = results['mu']['nln']['l'][ idx] results['nln']['mu']['u'][name] = results['mu']['nln']['u'][ idx] ## assign values for components of user cost if om.cost['order']: results['cost'] = {} for name in om.cost['order']: if om.getN('cost', name): results['cost'][name] = om.compute_cost(results['x'], name) ## if single-block PWL costs were converted to POLY, insert dummy y into x ## Note: The "y" portion of x will be nonsense, but everything should at ## least be in the expected locations. pwl1 = om.userdata('pwl1') if (len(pwl1) > 0) and (alg != 545) and (alg != 550): ## get indexing vv, _, _, _ = om.get_idx() if dc: nx = vv['iN']['Pg'] else: nx = vv['iN']['Qg'] y = zeros(len(pwl1)) raw['xr'] = r_[raw['xr'][:nx], y, raw['xr'][nx:]] results['x'] = r_[results['x'][:nx], y, results['x'][nx:]] return results, success, raw
def solveropfnlp_4(ppc, solver="ipopt"): if solver == "ipopt": opt = SolverFactory( "ipopt", executable= "/home/iso/PycharmProjects/opfLC_python3/Python3/py_solvers/ipopt-linux64/ipopt" ) if solver == "bonmin": opt = SolverFactory( "bonmin", executable= "/home/iso/PycharmProjects/opfLC_python3/Python3/py_solvers/bonmin-linux64/bonmin" ) if solver == "knitro": opt = SolverFactory( "knitro", executable="D:/ICT/Artelys/Knitro 10.2.1/knitroampl/knitroampl") ppc = ext2int(ppc) # convert to continuous indexing starting from 0 # Gather information about the system # ============================================================= baseMVA, bus, gen, branch = \ ppc["baseMVA"], ppc["bus"], ppc["gen"], ppc["branch"] nb = bus.shape[0] # number of buses ng = gen.shape[0] # number of generators nl = branch.shape[0] # number of lines # generator buses gb = tolist(np.array(gen[:, GEN_BUS]).astype(int)) sb = find((bus[:, BUS_TYPE] == REF)) # slack bus index fr = branch[:, F_BUS].astype(int) # from bus indices to = branch[:, T_BUS].astype(int) # to bus indices tr0 = copy(branch[:, TAP]) # transformation ratios tr0[find(tr0 == 0)] = 1 # set to 1 transformation ratios that are 0 tp = find(branch[:, TAP] != 0) # lines with tap changers ntp = find(branch[:, TAP] == 0) # lines without tap changers # Tap changer settings dudtap = 0.01 # Voltage per unit variation with tap changes tapmax = 10 # Highest tap changer setting tapmin = -10 # Lowest tap changer setting # Shunt element options stepmax = 1 # maximum step of the shunt element Bs0 = bus[:, BS] / baseMVA # shunt elements susceptance sd = find(bus[:, BS] != 0) # buses with shunt devices r = branch[:, BR_R] # branch resistances x = branch[:, BR_X] # branch reactances b = branch[:, BR_B] # branch susceptances start_time = time.clock() # Admittance matrix computation # ============================================================= # Set tap ratios and shunt elements to neutral position branch[:, TAP] = 1 bus[:, BS] = 0 y = makeYbus(baseMVA, bus, branch)[0] # admittance matrix yk = 1. / (r + x * 1j) # branch admittance yft = yk + 0.5j * b # branch admittance + susceptance gk = yk.real # branch resistance # Optimization # ============================================================= branch[find(branch[:, RATE_A] == 0), RATE_A] = 9999 # set undefined Sflow limit to 9999 Smax = branch[:, RATE_A] / baseMVA # Max. Sflow # Power demand parameters Pd = bus[:, PD] / baseMVA Qd = bus[:, QD] / baseMVA # Max and min Pg and Qg Pg_max = zeros(nb) Pg_max[gb] = gen[:, PMAX] / baseMVA Pg_min = zeros(nb) Pg_min[gb] = gen[:, PMIN] / baseMVA Qg_max = zeros(nb) Qg_max[gb] = gen[:, QMAX] / baseMVA Qg_min = zeros(nb) Qg_min[gb] = gen[:, QMIN] / baseMVA # Vmax and Vmin vectors Vmax = bus[:, VMAX] Vmin = bus[:, VMIN] vm = bus[:, VM] va = bus[:, VA] * pi / 180 # create a new optimization model model = ConcreteModel() # Define sets # ------------ model.bus = Set(ordered=True, initialize=range(nb)) # Set of all buses model.gen = Set(ordered=True, initialize=gb) # Set of buses with generation model.line = Set(ordered=True, initialize=range(nl)) # Set of all lines model.taps = Set(ordered=True, initialize=tp) # Set of all lines with tap changers model.shunt = Set(ordered=True, initialize=sd) # Set of buses with shunt elements # Define variables # ----------------- # Voltage magnitudes vector (vm) model.vm = Var(model.bus) # Voltage angles vector (va) model.va = Var(model.bus) # Reactive power generation, synchronous machines(SM) (Qg) model.Qg = Var(model.gen) Qg0 = zeros(nb) Qg0[gb] = gen[:, QG] / baseMVA # Active power generation, synchronous machines(SM) (Pg) model.Pg = Var(model.gen) Pg0 = zeros(nb) Pg0[gb] = gen[:, PG] / baseMVA # Active and reactive power from at all branches model.Pf = Var(model.line) model.Qf = Var(model.line) # Active and reactive power to at all branches model.Pt = Var(model.line) model.Qt = Var(model.line) # Transformation ratios model.tr = Var(model.taps) # Tap changer positions + their bounds model.tap = Var(model.taps, bounds=(tapmin, tapmax)) # Shunt susceptance model.Bs = Var(model.shunt) # Shunt positions + their bounds model.s = Var(model.shunt, bounds=(0, stepmax)) # Warm start the problem # ------------------------ for i in range(nb): model.vm[i] = vm[i] model.va[i] = va[i] if i in gb: model.Pg[i] = Pg0[i] model.Qg[i] = Qg0[i] for i in range(nl): model.Pf[i] = vm[fr[i]] ** 2 * abs(yft[i]) / (tr0[i] ** 2) * np.cos(-ang(yft[i])) -\ vm[fr[i]] * vm[to[i]] * abs(yk[i]) / tr0[i] * np.cos(va[fr[i]] - va[to[i]] - ang(yk[i])) model.Qf[i] = vm[fr[i]] ** 2 * abs(yft[i]) / (tr0[i] ** 2) * np.sin(-ang(yft[i])) -\ vm[fr[i]] * vm[to[i]] * abs(yk[i]) / tr0[i] * np.sin(va[fr[i]] - va[to[i]] - ang(yk[i])) model.Pt[i] = vm[to[i]] ** 2 * abs(yft[i]) * np.cos(-ang(yft[i])) -\ vm[to[i]] * vm[fr[i]] * abs(yk[i]) / tr0[i] * np.cos(va[to[i]] - va[fr[i]] - ang(yk[i])) model.Qt[i] = vm[to[i]] ** 2 * abs(yft[i]) * np.sin(-ang(yft[i])) -\ vm[to[i]] * vm[fr[i]] * abs(yk[i]) / tr0[i] * np.sin(va[to[i]] - va[fr[i]] - ang(yk[i])) for i in tp: model.tr[i] = tr0[i] for i in sd: model.Bs[i] = Bs0[i] # Define constraints # ---------------------------- # Equalities: # ------------ # Active power flow equalities def powerflowact(model, i): bfrom_i = tp[find(fr[tp] == i)] # branches from bus i with transformer bto_i = tp[find(to[tp] == i)] # branches to bus i with transformer allbut_i = find(bus[:, BUS_I] != i) # Set of other buses if i in gb: return model.Pg[i]-Pd[i] == sum(model.vm[i] * model.vm[j] * abs(y[i, j]) * cos(model.va[i] - model.va[j] - ang(y[i, j])) for j in allbut_i) - \ sum(model.vm[i] * model.vm[to[j]] * abs(yk[j]) * cos(model.va[i] - model.va[to[j]] - ang(yk[j])) * (1 / model.tr[j] - 1) for j in bfrom_i) - \ sum(model.vm[i] * model.vm[fr[j]] * abs(yk[j]) * cos(model.va[i] - model.va[fr[j]] - ang(yk[j])) * (1 / model.tr[j] - 1) for j in bto_i) + \ model.vm[i] ** 2 * (sum(abs(yk[j]) * (1 / model.tr[j]**2 - 1) * np.cos(- ang(yk[j])) for j in bfrom_i) + real(y[i, i])) else: return sum(model.vm[i] * model.vm[j] * abs(y[i, j]) * cos(model.va[i] - model.va[j] - ang(y[i, j])) for j in allbut_i) - \ sum(model.vm[i] * model.vm[to[j]] * abs(yk[j]) * cos(model.va[i] - model.va[to[j]] - ang(yk[j])) * (1 / model.tr[j] - 1) for j in bfrom_i) - \ sum(model.vm[i] * model.vm[fr[j]] * abs(yk[j]) * cos(model.va[i] - model.va[fr[j]] - ang(yk[j])) * (1 / model.tr[j] - 1) for j in bto_i) + \ model.vm[i] ** 2 * (sum(abs(yk[j]) * (1 / model.tr[j]**2 - 1) * np.cos(- ang(yk[j])) for j in bfrom_i) + real(y[i, i])) == -Pd[i] model.const1 = Constraint(model.bus, rule=powerflowact) # Reactive power flow equalities def powerflowreact(model, i): bfrom_i = tp[find(fr[tp] == i)] # branches from bus i with transformer bto_i = tp[find(to[tp] == i)] # branches to bus i with transformer allbut_i = find(bus[:, BUS_I] != i) # Set of other buses sh = sd[find(sd == i)] # Detect shunt elements if i in gb: return model.Qg[i]-Qd[i] == \ sum(model.vm[i] * model.vm[j] * abs(y[i, j]) * sin(model.va[i] - model.va[j] - ang(y[i, j])) for j in allbut_i) - \ sum(model.vm[i] * model.vm[to[j]] * abs(yk[j]) * sin(model.va[i] - model.va[to[j]] - ang(yk[j])) * (1 / model.tr[j] - 1) for j in bfrom_i) - \ sum(model.vm[i] * model.vm[fr[j]] * abs(yk[j]) * sin(model.va[i] - model.va[fr[j]] - ang(yk[j])) * (1 / model.tr[j] - 1) for j in bto_i) + \ model.vm[i] ** 2 * (sum(abs(yk[j]) * (1 / model.tr[j] ** 2 - 1) * np.sin(- ang(yk[j])) for j in bfrom_i) - imag(y[i, i]) - sum(model.Bs[j] for j in sh)) else: return sum(model.vm[i] * model.vm[j] * abs(y[i, j]) * sin(model.va[i] - model.va[j] - ang(y[i, j])) for j in allbut_i) - \ sum(model.vm[i] * model.vm[to[j]] * abs(yk[j]) * sin(model.va[i] - model.va[to[j]] - ang(yk[j])) * (1 / model.tr[j] - 1) for j in bfrom_i) - \ sum(model.vm[i] * model.vm[fr[j]] * abs(yk[j]) * sin(model.va[i] - model.va[fr[j]] - ang(yk[j])) * (1 / model.tr[j] - 1) for j in bto_i) + \ model.vm[i] ** 2 * (sum(abs(yk[j]) * (1 / model.tr[j] ** 2 - 1) * np.sin(- ang(yk[j])) for j in bfrom_i) - imag(y[i, i]) - sum(model.Bs[j] for j in sh)) == - Qd[i] model.const2 = Constraint(model.bus, rule=powerflowreact) # Active power from def pfrom(model, i): if i in tp: return model.Pf[i] == model.vm[fr[i]] ** 2 * abs(yft[i]) / (model.tr[i] ** 2) * np.cos(-ang(yft[i])) - \ model.vm[fr[i]] * model.vm[to[i]] * abs(yk[i]) / model.tr[i] * \ cos(model.va[fr[i]] - model.va[to[i]] - ang(yk[i])) else: return model.Pf[i] == model.vm[fr[i]] ** 2 * abs(yft[i]) / tr0[i] ** 2 * np.cos(-ang(yft[i])) - \ model.vm[fr[i]] * model.vm[to[i]] * abs(yk[i]) / tr0[i] * \ cos(model.va[fr[i]] - model.va[to[i]] - ang(yk[i])) model.const3 = Constraint(model.line, rule=pfrom) # Reactive power from def qfrom(model, i): if i in tp: return model.Qf[i] == model.vm[fr[i]] ** 2 * abs(yft[i]) / (model.tr[i] ** 2) * np.sin(-ang(yft[i])) - \ model.vm[fr[i]] * model.vm[to[i]] * abs(yk[i]) / model.tr[i] * \ sin(model.va[fr[i]] - model.va[to[i]] - ang(yk[i])) else: return model.Qf[i] == model.vm[fr[i]] ** 2 * abs(yft[i]) / tr0[i] ** 2 * np.sin(-ang(yft[i])) - \ model.vm[fr[i]] * model.vm[to[i]] * abs(yk[i]) / tr0[i] * \ sin(model.va[fr[i]] - model.va[to[i]] - ang(yk[i])) model.const4 = Constraint(model.line, rule=qfrom) # Active power to def pto(model, i): if i in tp: return model.Pt[i] == model.vm[to[i]] ** 2 * abs(yft[i]) * np.cos(-ang(yft[i])) - \ model.vm[to[i]] * model.vm[fr[i]] * abs(yk[i]) / model.tr[i] * \ cos(model.va[to[i]] - model.va[fr[i]] - ang(yk[i])) else: return model.Pt[i] == model.vm[to[i]] ** 2 * abs(yft[i]) * np.cos(-ang(yft[i])) - \ model.vm[to[i]] * model.vm[fr[i]] * abs(yk[i]) / tr0[i] * \ cos(model.va[to[i]] - model.va[fr[i]] - ang(yk[i])) model.const5 = Constraint(model.line, rule=pto) # Reactive power to def qto(model, i): if i in tp: return model.Qt[i] == model.vm[to[i]] ** 2 * abs(yft[i]) * np.sin(-ang(yft[i])) - \ model.vm[to[i]] * model.vm[fr[i]] * abs(yk[i]) / model.tr[i] * \ sin(model.va[to[i]] - model.va[fr[i]] - ang(yk[i])) else: return model.Qt[i] == model.vm[to[i]] ** 2 * abs(yft[i]) * np.sin(-ang(yft[i])) - \ model.vm[to[i]] * model.vm[fr[i]] * abs(yk[i]) / tr0[i] * \ sin(model.va[to[i]] - model.va[fr[i]] - ang(yk[i])) model.const6 = Constraint(model.line, rule=qto) # Slack bus phase angle model.const7 = Constraint(expr=model.va[sb[0]] == 0) # Transformation ratio equalities def trfunc(model, i): return model.tr[i] == 1 + dudtap * model.tap[i] model.const8 = Constraint(model.taps, rule=trfunc) # Shunt susceptance equality def shuntfunc(model, i): return model.Bs[i] == model.s[i] / stepmax * Bs0[i] model.const9 = Constraint(model.shunt, rule=shuntfunc) # Inequalities: # ---------------- # Active power generator limits Pg_min <= Pg <= Pg_max def genplimits(model, i): return Pg_min[i] <= model.Pg[i] <= Pg_max[i] model.const10 = Constraint(model.gen, rule=genplimits) # Reactive power generator limits Qg_min <= Qg <= Qg_max def genqlimits(model, i): return Qg_min[i] <= model.Qg[i] <= Qg_max[i] model.const11 = Constraint(model.gen, rule=genqlimits) # Voltage constraints ( Vmin <= V <= Vmax ) def vlimits(model, i): return Vmin[i] <= model.vm[i] <= Vmax[i] model.const12 = Constraint(model.bus, rule=vlimits) # Sfrom line limit def sfrommax(model, i): return model.Pf[i]**2 + model.Qf[i]**2 <= Smax[i]**2 model.const13 = Constraint(model.line, rule=sfrommax) # Sto line limit def stomax(model, i): return model.Pt[i]**2 + model.Qt[i]**2 <= Smax[i]**2 model.const14 = Constraint(model.line, rule=stomax) # Set objective function # ------------------------ def obj_fun(model): return sum(gk[i] * ((model.vm[fr[i]] / model.tr[i])**2 + model.vm[to[i]]**2 - 2 / model.tr[i] * model.vm[fr[i]] * model.vm[to[i]] * cos(model.va[fr[i]] - model.va[to[i]])) for i in tp) + \ sum(gk[i] * ((model.vm[fr[i]] / tr0[i]) ** 2 + model.vm[to[i]] ** 2 - 2 / tr0[i] * model.vm[fr[i]] * model.vm[to[i]] * cos(model.va[fr[i]] - model.va[to[i]])) for i in ntp) model.obj = Objective(rule=obj_fun, sense=minimize) mt = time.clock() - start_time # Modeling time # Execute solve command with the selected solver # ------------------------------------------------ start_time = time.clock() results = opt.solve(model, tee=True) et = time.clock() - start_time # Elapsed time print(results) # Update the case info with the optimized variables and approximate the continuous variables to discrete values # ============================================================================================================== for i in range(nb): if i in sd: bus[i, BS] = round(model.s[i].value) * Bs0[i] * baseMVA bus[i, VM] = model.vm[i].value # Bus voltage magnitudes bus[i, VA] = model.va[i].value * 180 / pi # Bus voltage angles # Update transformation ratios for i in range(nl): if i in tp: branch[i, TAP] = 1 + dudtap * round(model.tap[i].value) # Update gen matrix variables for i in range(ng): gen[i, PG] = model.Pg[gb[i]].value * baseMVA gen[i, QG] = model.Qg[gb[i]].value * baseMVA gen[i, VG] = bus[gb[i], VM] # Convert to external (original) numbering and save case results ppc = int2ext(ppc) ppc['bus'][:, 1:] = bus[:, 1:] branch[:, 0:2] = ppc['branch'][:, 0:2] ppc['branch'] = branch ppc['gen'][:, 1:] = gen[:, 1:] # Execute a second optimization with only the discrete approximated values (requires solveropfnlp_2) sol = solveropfnlp_2(ppc) sol['mt'] = sol['mt'] + mt sol['et'] = sol['et'] + et sol['tap'] = zeros((tp.shape[0], 1)) for i in range(tp.shape[0]): sol['tap'][i] = round(model.tap[tp[i]].value) sol['shunt'] = zeros((sd.shape[0], 1)) for i in range(sd.shape[0]): sol['shunt'][i] = round(model.s[sd[i]].value) # ppc solved case is returned return sol
def run_sim(ppc, elements, dynopt=None, events=None, recorder=None): """ Run a time-domain simulation Inputs: ppc PYPOWER load flow case elements Dictionary of dynamic model objects (machines, controllers, etc) with Object ID as key events Events object recorder Recorder object (empty) Outputs: recorder Recorder object (with data) """ ######### # SETUP # ######### # Get version information ver = pydyn_ver() print('PYPOWER-Dynamics ' + ver['Version'] + ', ' + ver['Date']) # Program options if dynopt: h = dynopt['h'] t_sim = dynopt['t_sim'] max_err = dynopt['max_err'] max_iter = dynopt['max_iter'] verbose = dynopt['verbose'] else: # Default program options h = 0.01 # step length (s) t_sim = 5 # simulation time (s) max_err = 0.0001 # Maximum error in network iteration (voltage mismatches) max_iter = 25 # Maximum number of network iterations verbose = False if dynopt['sample_period']: sample_rate = max(int(dynopt['sample_period'] / h) - 1, 0) else: sample_rate = 0 # Make lists of current injection sources (generators, external grids, etc) and controllers sources = [] controllers = [] for element in elements.values(): if element.__module__ in [ 'pydyn.sym_order6a', 'pydyn.sym_order6b', 'pydyn.sym_order4', 'pydyn.ext_grid', 'pydyn.vsc_average', 'pydyn.asym_1cage', 'pydyn.asym_2cage' ]: sources.append(element) if element.__module__ == 'pydyn.controller': controllers.append(element) # Set up interfaces interfaces = init_interfaces(elements) interfaces0 = init_interfaces0(elements) # find events events_controllers = [] # find blocks that create events in controllers for element_id in elements.keys(): element = elements[element_id] if element.__module__ == 'pydyn.controller': for line in element.equations: if line[1] == 'EVENT': new_event = [element_id, line[0], line[2]] + line[3:] events_controllers.append(new_event) ################## # INITIALISATION # ################## print('Initialising models...') if not verbose: ppopt = ppoption(VERBOSE=0, OUT_ALL=0) else: ppopt = ppoption() #print('not verbose') # Run power flow and update bus voltages and angles in PYPOWER case object results, success = runpf(ppc, ppopt) ppc["bus"][:, VM] = results["bus"][:, VM] ppc["bus"][:, VA] = results["bus"][:, VA] # Build Ybus matrix ppc_int = ext2int(ppc) baseMVA, bus, branch = ppc_int["baseMVA"], ppc_int["bus"], ppc_int[ "branch"] Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch) # Build modified Ybus matrix try: Ybus = mod_Ybus(Ybus, elements, bus, ppc_int['gen'], baseMVA) except: bp() Ybus = mod_Ybus(Ybus, elements, bus, ppc_int['gen'], baseMVA) # Calculate initial voltage phasors v0 = bus[:, VM] * (np.cos(np.radians(bus[:, VA])) + 1j * np.sin(np.radians(bus[:, VA]))) # Initialise sources from load flow for source in sources: if source.__module__ in ['pydyn.asym_1cage', 'pydyn.asym_2cage']: # Asynchronous machine source_bus = ppc_int['bus'][source.bus_no, 0].astype(np.int64) v_source = v0[source_bus] source.initialise(v_source, 0) else: # Generator or VSC source_bus = ppc_int['gen'][source.gen_no, 0].astype(np.int64) S_source = np.complex(results["gen"][source.gen_no, 1] / baseMVA, results["gen"][source.gen_no, 2] / baseMVA) v_source = v0[source_bus] source.initialise(v_source, S_source) # initialise bus elements['bus'] = bus_int(ppc) elements['sys_matrices'] = sys_matrices_int(ppc) #elements['branch'] = ppc['branch'] # Do we need interfaces0? # Interface controllers and machines (for initialisation) #for intf in interfaces: for k in range(len(interfaces)): intf = interfaces[k] intf0 = interfaces0[k] int_type = intf[0] var_name = intf0[1] source_var = intf[1] source_id = intf[2] dest_var = intf[3] dest_id = intf[4] if int_type == 'OUTPUT': # If an output, interface in the reverse direction for initialisation #intf[2].signals[var_name] = intf[3].signals[var_name] #if (intf0[2] != source_id) or (var_name != source_var) or (var_name != dest_var): # bp() elements[source_id].signals[source_var] = elements[ dest_id].signals[dest_var] else: # Inputs are interfaced in normal direction during initialisation #intf[3].signals[var_name] = intf[2].signals[var_name] #if (intf0[3] != dest_id) or (var_name != source_var) or (var_name != dest_var): # bp() elements[dest_id].signals[dest_var] = elements[source_id].signals[ source_var] #try: # element_source.signals[ var_name_source ] = element_dest.signals[ var_name_dest ] #except: # bp() # Initialise controllers for controller in controllers: controller.initialise() ############# # MAIN LOOP # ############# sample_age = 0 if events == None: print('Warning: no events!') # Factorise Ybus matrix Ybus_inv = splu(Ybus) y1 = [] v_prev = v0 print('Simulating...') for t in range(int(t_sim / h) + 1): if np.mod(t, 1 / h) == 0 and verbose: print('t=' + str(t * h) + 's') # Interface controllers and machines #for intf in interfaces: for k in range(len(interfaces)): intf = interfaces[k] intf0 = interfaces0[k] var_name = intf0[1] source_var = intf[1] source_id = intf[2] dest_var = intf[3] dest_id = intf[4] #if var_name_dest not in element_dest.signals.keys(): #bp() #element_dest.signals[ var_name_dest ] = element_source.signals[ var_name_source ] #if (intf0[2] != source_id) or (var_name != source_var) or (var_name != dest_var) or (intf0[3] != dest_id): # bp() elements[dest_id].signals[dest_var] = elements[source_id].signals[ source_var] #intf[3].signals[var_name] = intf[2].signals[var_name] # Solve differential equations for j in range(4): # Solve step of differential equations for element in elements.values(): try: element.solve_step(h, j) except: bp() element.solve_step(h, j) # Interface with network equations v_prev = solve_network(sources, v_prev, Ybus_inv, ppc_int, len(bus), max_err, max_iter) # check for events for event_c in events_controllers: new_event = None ctrl = event_c[0] ctrl_var = event_c[2] var_result = event_c[1] condition = elements[ctrl].signals[ctrl_var] if condition >= 1: #event_type = event_c[0] #node = event_c[1] new_event = [np.round(t * h, 5)] + event_c[3:] #print(new_event) try: events.event_stack.append(new_event) elements[ctrl].signals[var_result] = 1.0 #bp() except: bp() else: elements[ctrl].signals[var_result] = 0.0 if sample_age < sample_rate: sample_age += 1 else: sample_age = 0 if recorder != None: # Record signals or states recorder.record_variables(t * h, elements) if events != None: #if new_event != None: # bp() # Check event stack ppc, refactorise = events.handle_events(np.round(t * h, 5), elements, ppc, baseMVA) if refactorise == True: # Rebuild Ybus from new ppc_int ppc_int = ext2int(ppc) baseMVA, bus, branch = ppc_int["baseMVA"], ppc_int[ "bus"], ppc_int["branch"] Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch) # Rebuild modified Ybus Ybus = mod_Ybus(Ybus, elements, bus, ppc_int['gen'], baseMVA) # Refactorise Ybus Ybus_inv = splu(Ybus) # Solve network equations v_prev = solve_network(sources, v_prev, Ybus_inv, ppc_int, len(bus), max_err, max_iter) # update the voltage in 'bus' matrix ppc['bus'][:, VM] = abs(v_prev) ppc['bus'][:, VA] = 2 * np.arctan(v_prev.imag / (abs(v_prev) + v_prev.real)) # update the system matrices elements['bus'].update(ppc) elements['sys_matrices'].update(ppc) #bp() return recorder
def t_hessian(quiet=False): """Numerical tests of 2nd derivative code. @author: Ray Zimmerman (PSERC Cornell) """ t_begin(44, quiet) ## run powerflow to get solved case ppopt = ppoption(VERBOSE=0, OUT_ALL=0) results, _ = runpf(case30(), ppopt) baseMVA, bus, gen, branch = \ results['baseMVA'], results['bus'], results['gen'], results['branch'] ## switch to internal bus numbering and build admittance matrices _, bus, gen, branch = ext2int1(bus, gen, branch) Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch) Vm = bus[:, VM] Va = bus[:, VA] * (pi / 180) V = Vm * exp(1j * Va) f = branch[:, F_BUS] ## list of "from" buses t = branch[:, T_BUS] ## list of "to" buses nl = len(f) nb = len(V) Cf = sparse((ones(nl), (range(nl), f)), (nl, nb)) ## connection matrix for line & from buses Ct = sparse((ones(nl), (range(nl), t)), (nl, nb)) ## connection matrix for line & to buses pert = 1e-8 ##----- check d2Sbus_dV2 code ----- t = ' - d2Sbus_dV2 (complex power injections)' lam = 10 * random.rand(nb) num_Haa = zeros((nb, nb), complex) num_Hav = zeros((nb, nb), complex) num_Hva = zeros((nb, nb), complex) num_Hvv = zeros((nb, nb), complex) dSbus_dVm, dSbus_dVa = dSbus_dV(Ybus, V) Haa, Hav, Hva, Hvv = d2Sbus_dV2(Ybus, V, lam) for i in range(nb): Vap = V.copy() Vap[i] = Vm[i] * exp(1j * (Va[i] + pert)) dSbus_dVm_ap, dSbus_dVa_ap = dSbus_dV(Ybus, Vap) num_Haa[:, i] = (dSbus_dVa_ap - dSbus_dVa).T * lam / pert num_Hva[:, i] = (dSbus_dVm_ap - dSbus_dVm).T * lam / pert Vmp = V.copy() Vmp[i] = (Vm[i] + pert) * exp(1j * Va[i]) dSbus_dVm_mp, dSbus_dVa_mp = dSbus_dV(Ybus, Vmp) num_Hav[:, i] = (dSbus_dVa_mp - dSbus_dVa).T * lam / pert num_Hvv[:, i] = (dSbus_dVm_mp - dSbus_dVm).T * lam / pert t_is(Haa.todense(), num_Haa, 4, ['Haa', t]) t_is(Hav.todense(), num_Hav, 4, ['Hav', t]) t_is(Hva.todense(), num_Hva, 4, ['Hva', t]) t_is(Hvv.todense(), num_Hvv, 4, ['Hvv', t]) ##----- check d2Sbr_dV2 code ----- t = ' - d2Sbr_dV2 (complex power flows)' lam = 10 * random.rand(nl) # lam = [1 zeros(nl-1, 1)] num_Gfaa = zeros((nb, nb), complex) num_Gfav = zeros((nb, nb), complex) num_Gfva = zeros((nb, nb), complex) num_Gfvv = zeros((nb, nb), complex) num_Gtaa = zeros((nb, nb), complex) num_Gtav = zeros((nb, nb), complex) num_Gtva = zeros((nb, nb), complex) num_Gtvv = zeros((nb, nb), complex) dSf_dVa, dSf_dVm, dSt_dVa, dSt_dVm, _, _ = dSbr_dV(branch, Yf, Yt, V) Gfaa, Gfav, Gfva, Gfvv = d2Sbr_dV2(Cf, Yf, V, lam) Gtaa, Gtav, Gtva, Gtvv = d2Sbr_dV2(Ct, Yt, V, lam) for i in range(nb): Vap = V.copy() Vap[i] = Vm[i] * exp(1j * (Va[i] + pert)) dSf_dVa_ap, dSf_dVm_ap, dSt_dVa_ap, dSt_dVm_ap, Sf_ap, St_ap = \ dSbr_dV(branch, Yf, Yt, Vap) num_Gfaa[:, i] = (dSf_dVa_ap - dSf_dVa).T * lam / pert num_Gfva[:, i] = (dSf_dVm_ap - dSf_dVm).T * lam / pert num_Gtaa[:, i] = (dSt_dVa_ap - dSt_dVa).T * lam / pert num_Gtva[:, i] = (dSt_dVm_ap - dSt_dVm).T * lam / pert Vmp = V.copy() Vmp[i] = (Vm[i] + pert) * exp(1j * Va[i]) dSf_dVa_mp, dSf_dVm_mp, dSt_dVa_mp, dSt_dVm_mp, Sf_mp, St_mp = \ dSbr_dV(branch, Yf, Yt, Vmp) num_Gfav[:, i] = (dSf_dVa_mp - dSf_dVa).T * lam / pert num_Gfvv[:, i] = (dSf_dVm_mp - dSf_dVm).T * lam / pert num_Gtav[:, i] = (dSt_dVa_mp - dSt_dVa).T * lam / pert num_Gtvv[:, i] = (dSt_dVm_mp - dSt_dVm).T * lam / pert t_is(Gfaa.todense(), num_Gfaa, 4, ['Gfaa', t]) t_is(Gfav.todense(), num_Gfav, 4, ['Gfav', t]) t_is(Gfva.todense(), num_Gfva, 4, ['Gfva', t]) t_is(Gfvv.todense(), num_Gfvv, 4, ['Gfvv', t]) t_is(Gtaa.todense(), num_Gtaa, 4, ['Gtaa', t]) t_is(Gtav.todense(), num_Gtav, 4, ['Gtav', t]) t_is(Gtva.todense(), num_Gtva, 4, ['Gtva', t]) t_is(Gtvv.todense(), num_Gtvv, 4, ['Gtvv', t]) ##----- check d2Ibr_dV2 code ----- t = ' - d2Ibr_dV2 (complex currents)' lam = 10 * random.rand(nl) # lam = [1, zeros(nl-1)] num_Gfaa = zeros((nb, nb), complex) num_Gfav = zeros((nb, nb), complex) num_Gfva = zeros((nb, nb), complex) num_Gfvv = zeros((nb, nb), complex) num_Gtaa = zeros((nb, nb), complex) num_Gtav = zeros((nb, nb), complex) num_Gtva = zeros((nb, nb), complex) num_Gtvv = zeros((nb, nb), complex) dIf_dVa, dIf_dVm, dIt_dVa, dIt_dVm, _, _ = dIbr_dV(branch, Yf, Yt, V) Gfaa, Gfav, Gfva, Gfvv = d2Ibr_dV2(Yf, V, lam) Gtaa, Gtav, Gtva, Gtvv = d2Ibr_dV2(Yt, V, lam) for i in range(nb): Vap = V.copy() Vap[i] = Vm[i] * exp(1j * (Va[i] + pert)) dIf_dVa_ap, dIf_dVm_ap, dIt_dVa_ap, dIt_dVm_ap, If_ap, It_ap = \ dIbr_dV(branch, Yf, Yt, Vap) num_Gfaa[:, i] = (dIf_dVa_ap - dIf_dVa).T * lam / pert num_Gfva[:, i] = (dIf_dVm_ap - dIf_dVm).T * lam / pert num_Gtaa[:, i] = (dIt_dVa_ap - dIt_dVa).T * lam / pert num_Gtva[:, i] = (dIt_dVm_ap - dIt_dVm).T * lam / pert Vmp = V.copy() Vmp[i] = (Vm[i] + pert) * exp(1j * Va[i]) dIf_dVa_mp, dIf_dVm_mp, dIt_dVa_mp, dIt_dVm_mp, If_mp, It_mp = \ dIbr_dV(branch, Yf, Yt, Vmp) num_Gfav[:, i] = (dIf_dVa_mp - dIf_dVa).T * lam / pert num_Gfvv[:, i] = (dIf_dVm_mp - dIf_dVm).T * lam / pert num_Gtav[:, i] = (dIt_dVa_mp - dIt_dVa).T * lam / pert num_Gtvv[:, i] = (dIt_dVm_mp - dIt_dVm).T * lam / pert t_is(Gfaa.todense(), num_Gfaa, 4, ['Gfaa', t]) t_is(Gfav.todense(), num_Gfav, 4, ['Gfav', t]) t_is(Gfva.todense(), num_Gfva, 4, ['Gfva', t]) t_is(Gfvv.todense(), num_Gfvv, 4, ['Gfvv', t]) t_is(Gtaa.todense(), num_Gtaa, 4, ['Gtaa', t]) t_is(Gtav.todense(), num_Gtav, 4, ['Gtav', t]) t_is(Gtva.todense(), num_Gtva, 4, ['Gtva', t]) t_is(Gtvv.todense(), num_Gtvv, 4, ['Gtvv', t]) ##----- check d2ASbr_dV2 code ----- t = ' - d2ASbr_dV2 (squared apparent power flows)' lam = 10 * random.rand(nl) # lam = [1 zeros(nl-1, 1)] num_Gfaa = zeros((nb, nb), complex) num_Gfav = zeros((nb, nb), complex) num_Gfva = zeros((nb, nb), complex) num_Gfvv = zeros((nb, nb), complex) num_Gtaa = zeros((nb, nb), complex) num_Gtav = zeros((nb, nb), complex) num_Gtva = zeros((nb, nb), complex) num_Gtvv = zeros((nb, nb), complex) dSf_dVa, dSf_dVm, dSt_dVa, dSt_dVm, Sf, St = dSbr_dV(branch, Yf, Yt, V) dAf_dVa, dAf_dVm, dAt_dVa, dAt_dVm = \ dAbr_dV(dSf_dVa, dSf_dVm, dSt_dVa, dSt_dVm, Sf, St) Gfaa, Gfav, Gfva, Gfvv = d2ASbr_dV2(dSf_dVa, dSf_dVm, Sf, Cf, Yf, V, lam) Gtaa, Gtav, Gtva, Gtvv = d2ASbr_dV2(dSt_dVa, dSt_dVm, St, Ct, Yt, V, lam) for i in range(nb): Vap = V.copy() Vap[i] = Vm[i] * exp(1j * (Va[i] + pert)) dSf_dVa_ap, dSf_dVm_ap, dSt_dVa_ap, dSt_dVm_ap, Sf_ap, St_ap = \ dSbr_dV(branch, Yf, Yt, Vap) dAf_dVa_ap, dAf_dVm_ap, dAt_dVa_ap, dAt_dVm_ap = \ dAbr_dV(dSf_dVa_ap, dSf_dVm_ap, dSt_dVa_ap, dSt_dVm_ap, Sf_ap, St_ap) num_Gfaa[:, i] = (dAf_dVa_ap - dAf_dVa).T * lam / pert num_Gfva[:, i] = (dAf_dVm_ap - dAf_dVm).T * lam / pert num_Gtaa[:, i] = (dAt_dVa_ap - dAt_dVa).T * lam / pert num_Gtva[:, i] = (dAt_dVm_ap - dAt_dVm).T * lam / pert Vmp = V.copy() Vmp[i] = (Vm[i] + pert) * exp(1j * Va[i]) dSf_dVa_mp, dSf_dVm_mp, dSt_dVa_mp, dSt_dVm_mp, Sf_mp, St_mp = \ dSbr_dV(branch, Yf, Yt, Vmp) dAf_dVa_mp, dAf_dVm_mp, dAt_dVa_mp, dAt_dVm_mp = \ dAbr_dV(dSf_dVa_mp, dSf_dVm_mp, dSt_dVa_mp, dSt_dVm_mp, Sf_mp, St_mp) num_Gfav[:, i] = (dAf_dVa_mp - dAf_dVa).T * lam / pert num_Gfvv[:, i] = (dAf_dVm_mp - dAf_dVm).T * lam / pert num_Gtav[:, i] = (dAt_dVa_mp - dAt_dVa).T * lam / pert num_Gtvv[:, i] = (dAt_dVm_mp - dAt_dVm).T * lam / pert t_is(Gfaa.todense(), num_Gfaa, 2, ['Gfaa', t]) t_is(Gfav.todense(), num_Gfav, 2, ['Gfav', t]) t_is(Gfva.todense(), num_Gfva, 2, ['Gfva', t]) t_is(Gfvv.todense(), num_Gfvv, 2, ['Gfvv', t]) t_is(Gtaa.todense(), num_Gtaa, 2, ['Gtaa', t]) t_is(Gtav.todense(), num_Gtav, 2, ['Gtav', t]) t_is(Gtva.todense(), num_Gtva, 2, ['Gtva', t]) t_is(Gtvv.todense(), num_Gtvv, 2, ['Gtvv', t]) ##----- check d2ASbr_dV2 code ----- t = ' - d2ASbr_dV2 (squared real power flows)' lam = 10 * random.rand(nl) # lam = [1 zeros(nl-1, 1)] num_Gfaa = zeros((nb, nb), complex) num_Gfav = zeros((nb, nb), complex) num_Gfva = zeros((nb, nb), complex) num_Gfvv = zeros((nb, nb), complex) num_Gtaa = zeros((nb, nb), complex) num_Gtav = zeros((nb, nb), complex) num_Gtva = zeros((nb, nb), complex) num_Gtvv = zeros((nb, nb), complex) dSf_dVa, dSf_dVm, dSt_dVa, dSt_dVm, Sf, St = dSbr_dV(branch, Yf, Yt, V) dAf_dVa, dAf_dVm, dAt_dVa, dAt_dVm = \ dAbr_dV(dSf_dVa.real, dSf_dVm.real, dSt_dVa.real, dSt_dVm.real, Sf.real, St.real) Gfaa, Gfav, Gfva, Gfvv = d2ASbr_dV2(dSf_dVa.real, dSf_dVm.real, Sf.real, Cf, Yf, V, lam) Gtaa, Gtav, Gtva, Gtvv = d2ASbr_dV2(dSt_dVa.real, dSt_dVm.real, St.real, Ct, Yt, V, lam) for i in range(nb): Vap = V.copy() Vap[i] = Vm[i] * exp(1j * (Va[i] + pert)) dSf_dVa_ap, dSf_dVm_ap, dSt_dVa_ap, dSt_dVm_ap, Sf_ap, St_ap = \ dSbr_dV(branch, Yf, Yt, Vap) dAf_dVa_ap, dAf_dVm_ap, dAt_dVa_ap, dAt_dVm_ap = \ dAbr_dV(dSf_dVa_ap.real, dSf_dVm_ap.real, dSt_dVa_ap.real, dSt_dVm_ap.real, Sf_ap.real, St_ap.real) num_Gfaa[:, i] = (dAf_dVa_ap - dAf_dVa).T * lam / pert num_Gfva[:, i] = (dAf_dVm_ap - dAf_dVm).T * lam / pert num_Gtaa[:, i] = (dAt_dVa_ap - dAt_dVa).T * lam / pert num_Gtva[:, i] = (dAt_dVm_ap - dAt_dVm).T * lam / pert Vmp = V.copy() Vmp[i] = (Vm[i] + pert) * exp(1j * Va[i]) dSf_dVa_mp, dSf_dVm_mp, dSt_dVa_mp, dSt_dVm_mp, Sf_mp, St_mp = \ dSbr_dV(branch, Yf, Yt, Vmp) dAf_dVa_mp, dAf_dVm_mp, dAt_dVa_mp, dAt_dVm_mp = \ dAbr_dV(dSf_dVa_mp.real, dSf_dVm_mp.real, dSt_dVa_mp.real, dSt_dVm_mp.real, Sf_mp.real, St_mp.real) num_Gfav[:, i] = (dAf_dVa_mp - dAf_dVa).T * lam / pert num_Gfvv[:, i] = (dAf_dVm_mp - dAf_dVm).T * lam / pert num_Gtav[:, i] = (dAt_dVa_mp - dAt_dVa).T * lam / pert num_Gtvv[:, i] = (dAt_dVm_mp - dAt_dVm).T * lam / pert t_is(Gfaa.todense(), num_Gfaa, 2, ['Gfaa', t]) t_is(Gfav.todense(), num_Gfav, 2, ['Gfav', t]) t_is(Gfva.todense(), num_Gfva, 2, ['Gfva', t]) t_is(Gfvv.todense(), num_Gfvv, 2, ['Gfvv', t]) t_is(Gtaa.todense(), num_Gtaa, 2, ['Gtaa', t]) t_is(Gtav.todense(), num_Gtav, 2, ['Gtav', t]) t_is(Gtva.todense(), num_Gtva, 2, ['Gtva', t]) t_is(Gtvv.todense(), num_Gtvv, 2, ['Gtvv', t]) ##----- check d2AIbr_dV2 code ----- t = ' - d2AIbr_dV2 (squared current magnitudes)' lam = 10 * random.rand(nl) # lam = [1 zeros(nl-1, 1)] num_Gfaa = zeros((nb, nb), complex) num_Gfav = zeros((nb, nb), complex) num_Gfva = zeros((nb, nb), complex) num_Gfvv = zeros((nb, nb), complex) num_Gtaa = zeros((nb, nb), complex) num_Gtav = zeros((nb, nb), complex) num_Gtva = zeros((nb, nb), complex) num_Gtvv = zeros((nb, nb), complex) dIf_dVa, dIf_dVm, dIt_dVa, dIt_dVm, If, It = dIbr_dV(branch, Yf, Yt, V) dAf_dVa, dAf_dVm, dAt_dVa, dAt_dVm = \ dAbr_dV(dIf_dVa, dIf_dVm, dIt_dVa, dIt_dVm, If, It) Gfaa, Gfav, Gfva, Gfvv = d2AIbr_dV2(dIf_dVa, dIf_dVm, If, Yf, V, lam) Gtaa, Gtav, Gtva, Gtvv = d2AIbr_dV2(dIt_dVa, dIt_dVm, It, Yt, V, lam) for i in range(nb): Vap = V.copy() Vap[i] = Vm[i] * exp(1j * (Va[i] + pert)) dIf_dVa_ap, dIf_dVm_ap, dIt_dVa_ap, dIt_dVm_ap, If_ap, It_ap = \ dIbr_dV(branch, Yf, Yt, Vap) dAf_dVa_ap, dAf_dVm_ap, dAt_dVa_ap, dAt_dVm_ap = \ dAbr_dV(dIf_dVa_ap, dIf_dVm_ap, dIt_dVa_ap, dIt_dVm_ap, If_ap, It_ap) num_Gfaa[:, i] = (dAf_dVa_ap - dAf_dVa).T * lam / pert num_Gfva[:, i] = (dAf_dVm_ap - dAf_dVm).T * lam / pert num_Gtaa[:, i] = (dAt_dVa_ap - dAt_dVa).T * lam / pert num_Gtva[:, i] = (dAt_dVm_ap - dAt_dVm).T * lam / pert Vmp = V.copy() Vmp[i] = (Vm[i] + pert) * exp(1j * Va[i]) dIf_dVa_mp, dIf_dVm_mp, dIt_dVa_mp, dIt_dVm_mp, If_mp, It_mp = \ dIbr_dV(branch, Yf, Yt, Vmp) dAf_dVa_mp, dAf_dVm_mp, dAt_dVa_mp, dAt_dVm_mp = \ dAbr_dV(dIf_dVa_mp, dIf_dVm_mp, dIt_dVa_mp, dIt_dVm_mp, If_mp, It_mp) num_Gfav[:, i] = (dAf_dVa_mp - dAf_dVa).T * lam / pert num_Gfvv[:, i] = (dAf_dVm_mp - dAf_dVm).T * lam / pert num_Gtav[:, i] = (dAt_dVa_mp - dAt_dVa).T * lam / pert num_Gtvv[:, i] = (dAt_dVm_mp - dAt_dVm).T * lam / pert t_is(Gfaa.todense(), num_Gfaa, 3, ['Gfaa', t]) t_is(Gfav.todense(), num_Gfav, 3, ['Gfav', t]) t_is(Gfva.todense(), num_Gfva, 3, ['Gfva', t]) t_is(Gfvv.todense(), num_Gfvv, 2, ['Gfvv', t]) t_is(Gtaa.todense(), num_Gtaa, 3, ['Gtaa', t]) t_is(Gtav.todense(), num_Gtav, 3, ['Gtav', t]) t_is(Gtva.todense(), num_Gtva, 3, ['Gtva', t]) t_is(Gtvv.todense(), num_Gtvv, 2, ['Gtvv', t]) t_end()
gen_active = find(gen[:, GEN_STATUS] == 1) genBus = gen[gen_active, GEN_BUS] ## convert to p.u. bus[:, [PD, QD]] /= baseMVA gen[:, [PMAX, PMIN, QMAX, QMIN]] /= baseMVA gencost[:, COST] *= baseMVA**2 gencost[:, COST + 1] *= baseMVA ## problem dimensions nb = bus.shape[0] # number of buses nl = branch.shape[0] # number of branches ng = len(gen_active) # number of piece-wise linear costs ## build admittance matrices Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch) ##---------- partition the system --------------------------------------------- na = 3 # number of regions # the input format for partition should be a nb*1 matlab matrix with elements denoting area number partitionName = 'OP14_2region.mat' # partitionName = 'OP118_w05_40region_sizediff.mat' # partitionName = 'OP118_rd_8region_scale100000.mat' partitionDir = '/Users/junyaoguo/Dropbox/ADMM/Partition' partitionFile = join(partitionDir, partitionName) partition = loadmat(partitionFile) # partitionnumber = 0 op = partition['OP14'][0] op[10:] = 3 # op[:] = 1 # op = partition['C'][partitionnumber]
def ipoptopf_solver(om, ppopt): """Solves AC optimal power flow using IPOPT. Inputs are an OPF model object and a PYPOWER options vector. Outputs are a C{results} dict, C{success} flag and C{raw} output dict. C{results} is a PYPOWER case dict (ppc) with the usual C{baseMVA}, C{bus} C{branch}, C{gen}, C{gencost} fields, along with the following additional fields: - C{order} see 'help ext2int' for details of this field - C{x} final value of optimization variables (internal order) - C{f} final objective function value - C{mu} shadow prices on ... - C{var} - C{l} lower bounds on variables - C{u} upper bounds on variables - C{nln} - C{l} lower bounds on nonlinear constraints - C{u} upper bounds on nonlinear constraints - C{lin} - C{l} lower bounds on linear constraints - C{u} upper bounds on linear constraints C{success} is C{True} if solver converged successfully, C{False} otherwise C{raw} is a raw output dict in form returned by MINOS - C{xr} final value of optimization variables - C{pimul} constraint multipliers - C{info} solver specific termination code - C{output} solver specific output information @see: L{opf}, L{pips} @author: Ray Zimmerman (PSERC Cornell) @author: Carlos E. Murillo-Sanchez (PSERC Cornell & Universidad Autonoma de Manizales) @author: Richard Lincoln """ import pyipopt ## unpack data ppc = om.get_ppc() baseMVA, bus, gen, branch, gencost = \ ppc['baseMVA'], ppc['bus'], ppc['gen'], ppc['branch'], ppc['gencost'] vv, _, nn, _ = om.get_idx() ## problem dimensions nb = shape(bus)[0] ## number of buses ng = shape(gen)[0] ## number of gens nl = shape(branch)[0] ## number of branches ny = om.getN('var', 'y') ## number of piece-wise linear costs ## linear constraints A, l, u = om.linear_constraints() ## bounds on optimization vars _, xmin, xmax = om.getv() ## build admittance matrices Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch) ## try to select an interior initial point ll = xmin.copy() uu = xmax.copy() ll[xmin == -Inf] = -2e19 ## replace Inf with numerical proxies uu[xmax == Inf] = 2e19 x0 = (ll + uu) / 2 Varefs = bus[bus[:, BUS_TYPE] == REF, VA] * (pi / 180) x0[vv['i1']['Va']:vv['iN']['Va']] = Varefs[ 0] ## angles set to first reference angle if ny > 0: ipwl = find(gencost[:, MODEL] == PW_LINEAR) # PQ = r_[gen[:, PMAX], gen[:, QMAX]] # c = totcost(gencost[ipwl, :], PQ[ipwl]) ## largest y-value in CCV data c = gencost.flatten('F')[sub2ind(shape(gencost), ipwl, NCOST + 2 * gencost[ipwl, NCOST])] x0[vv['i1']['y']:vv['iN']['y']] = max(c) + 0.1 * abs(max(c)) # x0[vv['i1']['y']:vv['iN']['y']) = c + 0.1 * abs(c) ## find branches with flow limits il = find((branch[:, RATE_A] != 0) & (branch[:, RATE_A] < 1e10)) nl2 = len(il) ## number of constrained lines ##----- run opf ----- ## build Jacobian and Hessian structure if A is not None and issparse(A): nA = A.shape[0] ## number of original linear constraints else: nA = 0 nx = len(x0) f = branch[:, F_BUS] ## list of "from" buses t = branch[:, T_BUS] ## list of "to" buses Cf = sparse((ones(nl), (arange(nl), f)), (nl, nb)) ## connection matrix for line & from buses Ct = sparse((ones(nl), (arange(nl), t)), (nl, nb)) ## connection matrix for line & to buses Cl = Cf + Ct Cb = Cl.T * Cl + speye(nb, nb) Cl2 = Cl[il, :] Cg = sparse((ones(ng), (gen[:, GEN_BUS], arange(ng))), (nb, ng)) nz = nx - 2 * (nb + ng) nxtra = nx - 2 * nb if nz > 0: Js = vstack([ hstack([Cb, Cb, Cg, sparse( (nb, ng)), sparse((nb, nz))]), hstack([Cb, Cb, sparse( (nb, ng)), Cg, sparse((nb, nz))]), hstack([Cl2, Cl2, sparse((nl2, 2 * ng)), sparse((nl2, nz))]), hstack([Cl2, Cl2, sparse((nl2, 2 * ng)), sparse((nl2, nz))]) ], 'coo') else: Js = vstack([ hstack([Cb, Cb, Cg, sparse((nb, ng))]), hstack([ Cb, Cb, sparse((nb, ng)), Cg, ]), hstack([ Cl2, Cl2, sparse((nl2, 2 * ng)), ]), hstack([ Cl2, Cl2, sparse((nl2, 2 * ng)), ]) ], 'coo') if A is not None and issparse(A): Js = vstack([Js, A], 'coo') f, _, d2f = opf_costfcn(x0, om, True) Hs = tril(d2f + vstack([ hstack([Cb, Cb, sparse((nb, nxtra))]), hstack([Cb, Cb, sparse((nb, nxtra))]), sparse((nxtra, nx)) ]), format='coo') ## set options struct for IPOPT # options = {} # options['ipopt'] = ipopt_options([], ppopt) ## extra data to pass to functions userdata = { 'om': om, 'Ybus': Ybus, 'Yf': Yf[il, :], 'Yt': Yt[il, :], 'ppopt': ppopt, 'il': il, 'A': A, 'nA': nA, 'neqnln': 2 * nb, 'niqnln': 2 * nl2, 'Js': Js, 'Hs': Hs } ## check Jacobian and Hessian structure #xr = rand(x0.shape) #lmbda = rand( 2 * nb + 2 * nl2) #Js1 = eval_jac_g(x, flag, userdata) #(xr, options.auxdata) #Hs1 = eval_h(xr, 1, lmbda, userdata) #i1, j1, s = find(Js) #i2, j2, s = find(Js1) #if (len(i1) != len(i2)) | (norm(i1 - i2) != 0) | (norm(j1 - j2) != 0): # raise ValueError, 'something''s wrong with the Jacobian structure' # #i1, j1, s = find(Hs) #i2, j2, s = find(Hs1) #if (len(i1) != len(i2)) | (norm(i1 - i2) != 0) | (norm(j1 - j2) != 0): # raise ValueError, 'something''s wrong with the Hessian structure' ## define variable and constraint bounds # n is the number of variables n = x0.shape[0] # xl is the lower bound of x as bounded constraints xl = xmin # xu is the upper bound of x as bounded constraints xu = xmax neqnln = 2 * nb niqnln = 2 * nl2 # number of constraints m = neqnln + niqnln + nA # lower bound of constraint gl = r_[zeros(neqnln), -Inf * ones(niqnln), l] # upper bound of constraints gu = r_[zeros(neqnln), zeros(niqnln), u] # number of nonzeros in Jacobi matrix nnzj = Js.nnz # number of non-zeros in Hessian matrix, you can set it to 0 nnzh = Hs.nnz eval_hessian = True if eval_hessian: hessian = lambda x, lagrange, obj_factor, flag, user_data=None: \ eval_h(x, lagrange, obj_factor, flag, userdata) nlp = pyipopt.create(n, xl, xu, m, gl, gu, nnzj, nnzh, eval_f, eval_grad_f, eval_g, eval_jac_g, hessian) else: nnzh = 0 nlp = pyipopt.create(n, xl, xu, m, gl, gu, nnzj, nnzh, eval_f, eval_grad_f, eval_g, eval_jac_g) nlp.int_option('print_level', 5) nlp.num_option('tol', 1.0000e-12) nlp.int_option('max_iter', 250) nlp.num_option('dual_inf_tol', 0.10000) nlp.num_option('constr_viol_tol', 1.0000e-06) nlp.num_option('compl_inf_tol', 1.0000e-05) nlp.num_option('acceptable_tol', 1.0000e-08) nlp.num_option('acceptable_constr_viol_tol', 1.0000e-04) nlp.num_option('acceptable_compl_inf_tol', 0.0010000) nlp.str_option('mu_strategy', 'adaptive') iter = 0 def intermediate_callback(algmod, iter_count, obj_value, inf_pr, inf_du, mu, d_norm, regularization_size, alpha_du, alpha_pr, ls_trials, user_data=None): iter = iter_count return True nlp.set_intermediate_callback(intermediate_callback) ## run the optimization # returns final solution x, upper and lower bound for multiplier, final # objective function obj and the return status of ipopt x, zl, zu, obj, status, zg = nlp.solve(x0, m, userdata) info = { 'x': x, 'zl': zl, 'zu': zu, 'obj': obj, 'status': status, 'lmbda': zg } nlp.close() success = (status == 0) | (status == 1) output = {'iterations': iter} f, _ = opf_costfcn(x, om) ## update solution data Va = x[vv['i1']['Va']:vv['iN']['Va']] Vm = x[vv['i1']['Vm']:vv['iN']['Vm']] Pg = x[vv['i1']['Pg']:vv['iN']['Pg']] Qg = x[vv['i1']['Qg']:vv['iN']['Qg']] V = Vm * exp(1j * Va) ##----- calculate return values ----- ## update voltages & generator outputs bus[:, VA] = Va * 180 / pi bus[:, VM] = Vm gen[:, PG] = Pg * baseMVA gen[:, QG] = Qg * baseMVA gen[:, VG] = Vm[gen[:, GEN_BUS].astype(int)] ## compute branch flows f_br = branch[:, F_BUS].astype(int) t_br = branch[:, T_BUS].astype(int) Sf = V[f_br] * conj(Yf * V) ## cplx pwr at "from" bus, p.u. St = V[t_br] * conj(Yt * V) ## cplx pwr at "to" bus, p.u. branch[:, PF] = Sf.real * baseMVA branch[:, QF] = Sf.imag * baseMVA branch[:, PT] = St.real * baseMVA branch[:, QT] = St.imag * baseMVA ## line constraint is actually on square of limit ## so we must fix multipliers muSf = zeros(nl) muSt = zeros(nl) if len(il) > 0: muSf[il] = 2 * info['lmbda'][2 * nb + arange(nl2)] * branch[il, RATE_A] / baseMVA muSt[il] = 2 * info['lmbda'][2 * nb + nl2 + arange(nl2)] * branch[il, RATE_A] / baseMVA ## update Lagrange multipliers bus[:, MU_VMAX] = info['zu'][vv['i1']['Vm']:vv['iN']['Vm']] bus[:, MU_VMIN] = info['zl'][vv['i1']['Vm']:vv['iN']['Vm']] gen[:, MU_PMAX] = info['zu'][vv['i1']['Pg']:vv['iN']['Pg']] / baseMVA gen[:, MU_PMIN] = info['zl'][vv['i1']['Pg']:vv['iN']['Pg']] / baseMVA gen[:, MU_QMAX] = info['zu'][vv['i1']['Qg']:vv['iN']['Qg']] / baseMVA gen[:, MU_QMIN] = info['zl'][vv['i1']['Qg']:vv['iN']['Qg']] / baseMVA bus[:, LAM_P] = info['lmbda'][nn['i1']['Pmis']:nn['iN']['Pmis']] / baseMVA bus[:, LAM_Q] = info['lmbda'][nn['i1']['Qmis']:nn['iN']['Qmis']] / baseMVA branch[:, MU_SF] = muSf / baseMVA branch[:, MU_ST] = muSt / baseMVA ## package up results nlnN = om.getN('nln') ## extract multipliers for nonlinear constraints kl = find(info['lmbda'][:2 * nb] < 0) ku = find(info['lmbda'][:2 * nb] > 0) nl_mu_l = zeros(nlnN) nl_mu_u = r_[zeros(2 * nb), muSf, muSt] nl_mu_l[kl] = -info['lmbda'][kl] nl_mu_u[ku] = info['lmbda'][ku] ## extract multipliers for linear constraints lam_lin = info['lmbda'][2 * nb + 2 * nl2 + arange(nA)] ## lmbda for linear constraints kl = find(lam_lin < 0) ## lower bound binding ku = find(lam_lin > 0) ## upper bound binding mu_l = zeros(nA) mu_l[kl] = -lam_lin[kl] mu_u = zeros(nA) mu_u[ku] = lam_lin[ku] mu = { 'var': {'l': info['zl'], 'u': info['zu']}, 'nln': {'l': nl_mu_l, 'u': nl_mu_u}, \ 'lin': {'l': mu_l, 'u': mu_u} } results = ppc results['bus'], results['branch'], results['gen'], \ results['om'], results['x'], results['mu'], results['f'] = \ bus, branch, gen, om, x, mu, f pimul = r_[results['mu']['nln']['l'] - results['mu']['nln']['u'], results['mu']['lin']['l'] - results['mu']['lin']['u'], -ones(ny > 0), results['mu']['var']['l'] - results['mu']['var']['u']] raw = {'xr': x, 'pimul': pimul, 'info': info['status'], 'output': output} return results, success, raw
def opf_execute(om, ppopt): """Executes the OPF specified by an OPF model object. C{results} are returned with internal indexing, all equipment in-service, etc. @see: L{opf}, L{opf_setup} @author: Ray Zimmerman (PSERC Cornell) """ ##----- setup ----- ## options dc = ppopt['PF_DC'] ## 1 = DC OPF, 0 = AC OPF alg = ppopt['OPF_ALG'] verbose = ppopt['VERBOSE'] ## build user-defined costs om.build_cost_params() ## get indexing vv, ll, nn, _ = om.get_idx() if verbose > 0: v = ppver('all') stdout.write('PYPOWER Version %s, %s' % (v['Version'], v['Date'])) ##----- run DC OPF solver ----- if dc: if verbose > 0: stdout.write(' -- DC Optimal Power Flow\n') results, success, raw = dcopf_solver(om, ppopt) else: ##----- run AC OPF solver ----- if verbose > 0: stdout.write(' -- AC Optimal Power Flow\n') ## if OPF_ALG not set, choose best available option if alg == 0: alg = 560 ## MIPS ## update deprecated algorithm codes to new, generalized formulation equivalents if alg == 100 | alg == 200: ## CONSTR alg = 300 elif alg == 120 | alg == 220: ## dense LP alg = 320 elif alg == 140 | alg == 240: ## sparse (relaxed) LP alg = 340 elif alg == 160 | alg == 260: ## sparse (full) LP alg = 360 ppopt['OPF_ALG_POLY'] = alg ## run specific AC OPF solver if alg == 560 or alg == 565: ## PIPS results, success, raw = pipsopf_solver(om, ppopt) elif alg == 580: ## IPOPT try: __import__('pyipopt') results, success, raw = ipoptopf_solver(om, ppopt) except ImportError: raise ImportError('OPF_ALG %d requires IPOPT ' '(see https://projects.coin-or.org/Ipopt/)' % alg) else: stderr.write('opf_execute: OPF_ALG %d is not a valid algorithm code\n' % alg) if ('output' not in raw) or ('alg' not in raw['output']): raw['output']['alg'] = alg if success: if not dc: ## copy bus voltages back to gen matrix results['gen'][:, VG] = results['bus'][results['gen'][:, GEN_BUS].astype(int), VM] ## gen PQ capability curve multipliers if (ll['N']['PQh'] > 0) | (ll['N']['PQl'] > 0): mu_PQh = results['mu']['lin']['l'][ll['i1']['PQh']:ll['iN']['PQh']] - results['mu']['lin']['u'][ll['i1']['PQh']:ll['iN']['PQh']] mu_PQl = results['mu']['lin']['l'][ll['i1']['PQl']:ll['iN']['PQl']] - results['mu']['lin']['u'][ll['i1']['PQl']:ll['iN']['PQl']] Apqdata = om.userdata('Apqdata') results['gen'] = update_mupq(results['baseMVA'], results['gen'], mu_PQh, mu_PQl, Apqdata) ## compute g, dg, f, df, d2f if requested by RETURN_RAW_DER = 1 if ppopt['RETURN_RAW_DER']: ## move from results to raw if using v4.0 of MINOPF or TSPOPF if 'dg' in results: raw = {} raw['dg'] = results['dg'] raw['g'] = results['g'] ## compute g, dg, unless already done by post-v4.0 MINOPF or TSPOPF if 'dg' not in raw: ppc = om.get_ppc() Ybus, Yf, Yt = makeYbus(ppc['baseMVA'], ppc['bus'], ppc['branch']) g, geq, dg, dgeq = opf_consfcn(results['x'], om, Ybus, Yf, Yt, ppopt) raw['g'] = r_[geq, g] raw['dg'] = r_[dgeq.T, dg.T] ## true Jacobian organization ## compute df, d2f _, df, d2f = opf_costfcn(results['x'], om, True) raw['df'] = df raw['d2f'] = d2f ## delete g and dg fieldsfrom results if using v4.0 of MINOPF or TSPOPF if 'dg' in results: del results['dg'] del results['g'] ## angle limit constraint multipliers if ll['N']['ang'] > 0: iang = om.userdata('iang') results['branch'][iang, MU_ANGMIN] = results['mu']['lin']['l'][ll['i1']['ang']:ll['iN']['ang']] * pi / 180 results['branch'][iang, MU_ANGMAX] = results['mu']['lin']['u'][ll['i1']['ang']:ll['iN']['ang']] * pi / 180 else: ## assign empty g, dg, f, df, d2f if requested by RETURN_RAW_DER = 1 if not dc and ppopt['RETURN_RAW_DER']: raw['dg'] = array([]) raw['g'] = array([]) raw['df'] = array([]) raw['d2f'] = array([]) ## assign values and limit shadow prices for variables if om.var['order']: results['var'] = {'val': {}, 'mu': {'l': {}, 'u': {}}} for name in om.var['order']: if om.getN('var', name): idx = arange(vv['i1'][name], vv['iN'][name]) results['var']['val'][name] = results['x'][idx] results['var']['mu']['l'][name] = results['mu']['var']['l'][idx] results['var']['mu']['u'][name] = results['mu']['var']['u'][idx] ## assign shadow prices for linear constraints if om.lin['order']: results['lin'] = {'mu': {'l': {}, 'u': {}}} for name in om.lin['order']: if om.getN('lin', name): idx = arange(ll['i1'][name], ll['iN'][name]) results['lin']['mu']['l'][name] = results['mu']['lin']['l'][idx] results['lin']['mu']['u'][name] = results['mu']['lin']['u'][idx] ## assign shadow prices for nonlinear constraints if not dc: if om.nln['order']: results['nln'] = {'mu': {'l': {}, 'u': {}}} for name in om.nln['order']: if om.getN('nln', name): idx = arange(nn['i1'][name], nn['iN'][name]) results['nln']['mu']['l'][name] = results['mu']['nln']['l'][idx] results['nln']['mu']['u'][name] = results['mu']['nln']['u'][idx] ## assign values for components of user cost if om.cost['order']: results['cost'] = {} for name in om.cost['order']: if om.getN('cost', name): results['cost'][name] = om.compute_cost(results['x'], name) ## if single-block PWL costs were converted to POLY, insert dummy y into x ## Note: The "y" portion of x will be nonsense, but everything should at ## least be in the expected locations. pwl1 = om.userdata('pwl1') if (len(pwl1) > 0) and (alg != 545) and (alg != 550): ## get indexing vv, _, _, _ = om.get_idx() if dc: nx = vv['iN']['Pg'] else: nx = vv['iN']['Qg'] y = zeros(len(pwl1)) raw['xr'] = r_[raw['xr'][:nx], y, raw['xr'][nx:]] results['x'] = r_[results['x'][:nx], y, results['x'][nx:]] return results, success, raw
def runpf(casedata=None, ppopt=None, fname='', solvedcase=''): """Runs a power flow. Runs a power flow [full AC Newton's method by default] and optionally returns the solved values in the data matrices, a flag which is C{True} if the algorithm was successful in finding a solution, and the elapsed time in seconds. All input arguments are optional. If C{casename} is provided it specifies the name of the input data file or dict containing the power flow data. The default value is 'case9'. If the ppopt is provided it overrides the default PYPOWER options vector and can be used to specify the solution algorithm and output options among other things. If the 3rd argument is given the pretty printed output will be appended to the file whose name is given in C{fname}. If C{solvedcase} is specified the solved case will be written to a case file in PYPOWER format with the specified name. If C{solvedcase} ends with '.mat' it saves the case as a MAT-file otherwise it saves it as a Python-file. If the C{ENFORCE_Q_LIMS} options is set to C{True} [default is false] then if any generator reactive power limit is violated after running the AC power flow, the corresponding bus is converted to a PQ bus, with Qg at the limit, and the case is re-run. The voltage magnitude at the bus will deviate from the specified value in order to satisfy the reactive power limit. If the reference bus is converted to PQ, the first remaining PV bus will be used as the slack bus for the next iteration. This may result in the real power output at this generator being slightly off from the specified values. Enforcing of generator Q limits inspired by contributions from Mu Lin, Lincoln University, New Zealand (1/14/05). @author: Ray Zimmerman (PSERC Cornell) """ ## default arguments if casedata is None: casedata = join(dirname(__file__), 'case9') ppopt = ppoption(ppopt) ## options verbose = ppopt["VERBOSE"] qlim = ppopt["ENFORCE_Q_LIMS"] ## enforce Q limits on gens? dc = ppopt["PF_DC"] ## use DC formulation? ## read data ppc = loadcase(casedata) ## add zero columns to branch for flows if needed if ppc["branch"].shape[1] < QT: ppc["branch"] = c_[ppc["branch"], zeros((ppc["branch"].shape[0], QT - ppc["branch"].shape[1] + 1))] ## convert to internal indexing ppc = ext2int(ppc) baseMVA, bus, gen, branch = \ ppc["baseMVA"], ppc["bus"], ppc["gen"], ppc["branch"] ## get bus index lists of each type of bus ref, pv, pq = bustypes(bus, gen) ## generator info on = find(gen[:, GEN_STATUS] > 0) ## which generators are on? gbus = gen[on, GEN_BUS].astype(int) ## what buses are they at? ##----- run the power flow ----- t0 = time() if verbose > 0: v = ppver('all') stdout.write('PYPOWER Version %s, %s' % (v["Version"], v["Date"])) if dc: # DC formulation if verbose: stdout.write(' -- DC Power Flow\n') ## initial state Va0 = bus[:, VA] * (pi / 180) ## build B matrices and phase shift injections B, Bf, Pbusinj, Pfinj = makeBdc(baseMVA, bus, branch) ## compute complex bus power injections [generation - load] ## adjusted for phase shifters and real shunts Pbus = makeSbus(baseMVA, bus, gen).real - Pbusinj - bus[:, GS] / baseMVA ## "run" the power flow Va = dcpf(B, Pbus, Va0, ref, pv, pq) ## update data matrices with solution branch[:, [QF, QT]] = zeros((branch.shape[0], 2)) branch[:, PF] = (Bf * Va + Pfinj) * baseMVA branch[:, PT] = -branch[:, PF] bus[:, VM] = ones(bus.shape[0]) bus[:, VA] = Va * (180 / pi) ## update Pg for slack generator (1st gen at ref bus) ## (note: other gens at ref bus are accounted for in Pbus) ## Pg = Pinj + Pload + Gs ## newPg = oldPg + newPinj - oldPinj refgen = zeros(len(ref), dtype=int) for k in range(len(ref)): temp = find(gbus == ref[k]) refgen[k] = on[temp[0]] gen[refgen, PG] = gen[refgen, PG] + (B[ref, :] * Va - Pbus[ref]) * baseMVA success = 1 else: ## AC formulation alg = ppopt['PF_ALG'] if verbose > 0: if alg == 1: solver = 'Newton' elif alg == 2: solver = 'fast-decoupled, XB' elif alg == 3: solver = 'fast-decoupled, BX' elif alg == 4: solver = 'Gauss-Seidel' else: solver = 'unknown' print(' -- AC Power Flow (%s)\n' % solver) ## initial state # V0 = ones(bus.shape[0]) ## flat start V0 = bus[:, VM] * exp(1j * pi / 180 * bus[:, VA]) V0[gbus] = gen[on, VG] / abs(V0[gbus]) * V0[gbus] if qlim: ref0 = ref ## save index and angle of Varef0 = bus[ref0, VA] ## original reference bus(es) limited = [] ## list of indices of gens @ Q lims fixedQg = zeros(gen.shape[0]) ## Qg of gens at Q limits repeat = True while repeat: ## build admittance matrices Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch) ## compute complex bus power injections [generation - load] Sbus = makeSbus(baseMVA, bus, gen) ## run the power flow alg = ppopt["PF_ALG"] if alg == 1: V, success, _ = newtonpf(Ybus, Sbus, V0, ref, pv, pq, ppopt) elif alg == 2 or alg == 3: Bp, Bpp = makeB(baseMVA, bus, branch, alg) V, success, _ = fdpf(Ybus, Sbus, V0, Bp, Bpp, ref, pv, pq, ppopt) elif alg == 4: V, success, _ = gausspf(Ybus, Sbus, V0, ref, pv, pq, ppopt) else: stderr.write('Only Newton' 's method, fast-decoupled, and ' 'Gauss-Seidel power flow algorithms currently ' 'implemented.\n') ## update data matrices with solution bus, gen, branch = pfsoln(baseMVA, bus, gen, branch, Ybus, Yf, Yt, V, ref, pv, pq) if qlim: ## enforce generator Q limits ## find gens with violated Q constraints gen_status = gen[:, GEN_STATUS] > 0 qg_max_lim = gen[:, QG] > gen[:, QMAX] qg_min_lim = gen[:, QG] < gen[:, QMIN] mx = find(gen_status & qg_max_lim) mn = find(gen_status & qg_min_lim) if len(mx) > 0 or len( mn) > 0: ## we have some Q limit violations # No PV generators if len(pv) == 0: if verbose: if len(mx) > 0: print( 'Gen %d [only one left] exceeds upper Q limit : INFEASIBLE PROBLEM\n' % mx + 1) else: print( 'Gen %d [only one left] exceeds lower Q limit : INFEASIBLE PROBLEM\n' % mn + 1) success = 0 break ## one at a time? if qlim == 2: ## fix largest violation, ignore the rest k = argmax(r_[gen[mx, QG] - gen[mx, QMAX], gen[mn, QMIN] - gen[mn, QG]]) if k > len(mx): mn = mn[k - len(mx)] mx = [] else: mx = mx[k] mn = [] if verbose and len(mx) > 0: for i in range(len(mx)): print('Gen ' + str(mx[i] + 1) + ' at upper Q limit, converting to PQ bus\n') if verbose and len(mn) > 0: for i in range(len(mn)): print('Gen ' + str(mn[i] + 1) + ' at lower Q limit, converting to PQ bus\n') ## save corresponding limit values fixedQg[mx] = gen[mx, QMAX] fixedQg[mn] = gen[mn, QMIN] mx = r_[mx, mn].astype(int) ## convert to PQ bus gen[mx, QG] = fixedQg[mx] ## set Qg to binding for i in range( len(mx) ): ## [one at a time, since they may be at same bus] gen[mx[i], GEN_STATUS] = 0 ## temporarily turn off gen, bi = gen[mx[i], GEN_BUS] ## adjust load accordingly, bus[bi, [PD, QD]] = (bus[bi, [PD, QD]] - gen[mx[i], [PG, QG]]) if len(ref) > 1 and any(bus[gen[mx, GEN_BUS], BUS_TYPE] == REF): raise ValueError('Sorry, PYPOWER cannot enforce Q ' 'limits for slack buses in systems ' 'with multiple slacks.') bus[gen[mx, GEN_BUS].astype(int), BUS_TYPE] = PQ ## & set bus type to PQ ## update bus index lists of each type of bus ref_temp = ref ref, pv, pq = bustypes(bus, gen) if verbose and ref != ref_temp: print('Bus %d is new slack bus\n' % ref) limited = r_[limited, mx].astype(int) else: repeat = 0 ## no more generator Q limits violated else: repeat = 0 ## don't enforce generator Q limits, once is enough if qlim and len(limited) > 0: ## restore injections from limited gens [those at Q limits] gen[limited, QG] = fixedQg[limited] ## restore Qg value, for i in range( len(limited )): ## [one at a time, since they may be at same bus] bi = gen[limited[i], GEN_BUS] ## re-adjust load, bus[bi, [PD, QD]] = bus[bi, [PD, QD]] + gen[limited[i], [PG, QG]] gen[limited[i], GEN_STATUS] = 1 ## and turn gen back on if ref != ref0: ## adjust voltage angles to make original ref bus correct bus[:, VA] = bus[:, VA] - bus[ref0, VA] + Varef0 ppc["et"] = time() - t0 ppc["success"] = success ##----- output results ----- ## convert back to original bus numbering & print results ppc["bus"], ppc["gen"], ppc["branch"] = bus, gen, branch results = int2ext(ppc) ## zero out result fields of out-of-service gens & branches if len(results["order"]["gen"]["status"]["off"]) > 0: results["gen"][ix_(results["order"]["gen"]["status"]["off"], [PG, QG])] = 0 if len(results["order"]["branch"]["status"]["off"]) > 0: results["branch"][ix_(results["order"]["branch"]["status"]["off"], [PF, QF, PT, QT])] = 0 if fname: fd = None try: fd = open(fname, "a") except Exception as detail: stderr.write("Error opening %s: %s.\n" % (fname, detail)) finally: if fd is not None: printpf(results, fd, ppopt) fd.close() else: printpf(results, stdout, ppopt) ## save solved case if solvedcase: savecase(solvedcase, results) return results, success
def runWorker(ID, s, output): client = EthJsonRpc('10.64.83.200', 39000 + ID) obj = web3.Web3(web3.HTTPProvider('http://10.64.83.200:3900' + str(ID))) print(client.eth_accounts()[0]) #print(client1.eth_accounts()[1]) print("Worker %d initialized successfully!" % (ID, )) all_gas = [] nu = 0 #iteration count itermax = s.admmopt.iterMaxlocal #get maximum iteration flag = False new_gas = 0 start_balance = obj.eth.getBalance( obj.toChecksumAddress(client.eth_accounts()[0])) j = 0 s.admmopt.tau = 1 last_block = obj.eth.blockNumber while (1): trans_by_block = obj.eth.getBlockTransactionCount(last_block - j) if (trans_by_block > 0): #dest = list(s.nbor.keys()) #if(ID==1):getNextValues(ID,s,client,obj,last_block - j, trans_by_block) #s.update_z() #s.choose_max_rho() break j = j + 1 prev_convegenceTable = [] P_history = [] Q_history = [] start_time2 = time() while nu <= itermax and not flag: if s.recvmsg: s.update_z() s.choose_max_rho() #print(ID,"i have rho", s.var["rho"]) start_time = time() result = s.pipslopf_solver() end_time = time() if result['eflag'] and nu % 20 == 0: print('Subproblem %d at iteration %d solved!' % (ID, nu)) # print("Time for solving subproblem %d: %ssecs to %ssecs" % (ID, start_time, end_time)) s.update_x() P_history.append(sum(s.var['Pg'])) Q_history.append(sum(s.var['Qg'])) if s.recvmsg: # not the initialization s.update_y() s.update_rho() prev_convegenceTable = s.gapAll[s.ID - 1] # check convergence #if(nu>10):flag = flag = s.converge() s.recvmsg = {} # clear the received messages #s.send() dest = s.nbor.keys() for k in dest: # prepare the message to be sent to neighbor k #tm.sleep(0.05) msg3 = message() msg3.config(s.ID, k, s.var, s.nbor[k].tlidx['int'], s.gapAll) data2 = json.dumps(msg3.__dict__, cls=MyEncoder) json_data = json.JSONEncoder().encode(data2) json_data = '0x' + json_data.encode("utf-8").hex() sampleDict = obj.txpool.content.pending new_gas = new_gas + 18000000000 * obj.eth.estimateGas({ 'to': obj.toChecksumAddress( '0xa8085d8331f16a5b76690e665d9f5eaaaa85ee1c'), 'data': json_data }) client.eth_sendTransaction( from_address=client.eth_accounts()[0], to_address="0xa8085d8331f16a5b76690e665d9f5eaaaa85ee1c", gas=0xf4240, gas_price=18000000000, value=1, # 2441406250 data=json_data) txpooljson = obj.txpool.content.pending.__dict__ recvmsg = {} twait = s.admmopt.pollWaitingtime dest = list(s.nbor.keys()) recvFlag = [0] * s.region['nnbor'] arrived = 0 # number of arrived neighbors pollround = 0 while arrived < s.region['nwait'] and pollround < 5: #for i in range(len(dest)): #k = dest[i] #s.recvmsg[k] = recvmsg[k] #recvFlag[i] = 1 for k in txpooljson.keys(): txpooljson2 = txpooljson[k].__dict__ last_nonce = max(txpooljson2, key=int) #print(ID,", last nonce",last_nonce) last_nonce_dict = txpooljson2[last_nonce].__dict__ hexjson = last_nonce_dict['input'][2:] jsonstring = codecs.decode(hexjson, "hex").decode('utf-8') jsonvalues = json.JSONDecoder().decode(jsonstring) values_dict = json.loads(jsonvalues, cls=MyDecoder) temp_msg = message() if (values_dict['fID'] in s.nbor.keys()): for last_nonce in range(int(max(txpooljson2, key=int)), 0, -1): last_nonce_dict = txpooljson2[str(last_nonce)].__dict__ hexjson = last_nonce_dict['input'][2:] jsonstring = codecs.decode(hexjson, "hex").decode('utf-8') jsonvalues = json.JSONDecoder().decode(jsonstring) values_dict = json.loads(jsonvalues, cls=MyDecoder) if (values_dict['tID'] == s.ID): break #print(ID,"last nonce=",last_nonce,"from",values_dict['fID']) temp_msg.fID = values_dict['fID'] temp_msg.tID = values_dict['tID'] temp_msg.fields['AVmd'] = numpy.asarray( values_dict['fields']['AVmd']) temp_msg.fields['AVms'] = numpy.asarray( values_dict['fields']['AVms']) temp_msg.fields['AVad'] = numpy.asarray( values_dict['fields']['AVad']) temp_msg.fields['AVas'] = numpy.asarray( values_dict['fields']['AVas']) temp_msg.fields['ymd'] = numpy.asarray( values_dict['fields']['ymd']) temp_msg.fields['yms'] = numpy.asarray( values_dict['fields']['yms']) temp_msg.fields['yad'] = numpy.asarray( values_dict['fields']['yad']) temp_msg.fields['yas'] = numpy.asarray( values_dict['fields']['yas']) temp_msg.fields['rho'] = values_dict['fields']['rho'] temp_msg.fields['convergeTable'] = values_dict['fields'][ 'convergeTable'] if (temp_msg.tID == s.ID): recvmsg[temp_msg.fID] = temp_msg #recvFlag[i] = 1 arrived = len(recvmsg) pollround += 1 s.recvmsg = copy.deepcopy(recvmsg) all_gas.append(new_gas) nu += 1 # record results print("Worker %d finished!" % (ID, )) for k in dest: starting_point = message2() starting_point.config(s.ID, k, s.var, s.nbor[k].tlidx['int'], s.gapAll) data2 = json.dumps(starting_point.__dict__, cls=MyEncoder) json_data = json.JSONEncoder().encode(data2) json_data = '0x' + json_data.encode("utf-8").hex() sampleDict = obj.txpool.content.pending client.eth_sendTransaction( from_address=client.eth_accounts()[0], to_address="0xa8085d8331f16a5b76690e665d9f5eaaaa85ee1c", gas=0xf4240, gas_price=18000000000, value=1, # 2441406250 data=json_data) print(starting_point.__dict__) x, f, info, lmbda, output2 = result["x"], result["f"], result[ "eflag"], result["lmbda"], result["output"] nx = len(x) nb = s.region['nb'] ng = s.region['ng'] iv = s.idx['var'] bus = s.region['bus'] gen = s.region['gen'] branch = s.region['branch'] baseMVA = s.region['baseMVA'] Ybus = s.region['Ybus'] ridx = s.idx['rbus']['int'] # idx ranges iVa = iv['iVa'] iVm = iv['iVm'] iPg = iv['iPg'] iQg = iv['iQg'] # grab Pg and Qg gen[:, PG] = x[iPg] * s.region["baseMVA"] gen[:, QG] = x[iQg] * s.region["baseMVA"] bus[:, PD] = bus[:, PD] * s.region["baseMVA"] bus[:, QD] = bus[:, QD] * s.region["baseMVA"] # reconstruct V Va, Vm = x[iVa], x[iVm] V = Vm * exp(1j * Va) #print(V) nl = shape(branch)[0] ## number of branches bus[:, VA] = Va * 180 / pi bus[:, VM] = Vm if shape(branch)[1] < MU_ANGMAX + 1: branch = c_[branch, zeros((nl, MU_ANGMAX + 1 - shape(branch)[1]))] Ybus2, Yf, Yt = makeYbus(baseMVA, bus, branch) #print(Yf) ## compute branch flows Sf = V[branch[:, F_BUS].astype(int)] * conj( Yf * V) ## cplx pwr at "from" bus, p["u"]. St = V[branch[:, T_BUS].astype(int)] * conj( Yt * V) ## cplx pwr at "to" bus, p["u"]. branch[:, PF] = Sf.real * baseMVA branch[:, QF] = Sf.imag * baseMVA branch[:, PT] = St.real * baseMVA branch[:, QT] = St.imag * baseMVA #gen[:, VG] = Vm[ gen[:, GEN_BUS].astype(int) ] nlam = len(lmbda["eqnonlin"]) // 2 lamP = zeros(nb) #for non-included pf balances use 0 as multiplier lamQ = zeros(nb) lamP[s.idx['rbus']['int']] = lmbda["eqnonlin"][:nlam] / s.region["baseMVA"] lamQ[s.idx['rbus']['int']] = lmbda["eqnonlin"][nlam:nlam + nlam] / s.region["baseMVA"] ong = find((gen[:, GEN_STATUS] > 0) & ~isload(gen)) objValue1 = 0 objValue2 = 0 fd = stdout fd.write('\n REGION %d' % (s.ID)) fd.write( '\nBus/Area Voltage Generation Load ') fd.write(' Lambda($/MVA-hr)') fd.write( '\n # Mag(pu) Ang(deg) P (MW) Q (MVAr) P (MW) Q (MVAr)') fd.write(' P Q ') fd.write( '\n----- ------- -------- -------- -------- -------- --------') fd.write(' ------- -------') for i in range(nb): for glob, loc in s.idx["mapping"].items(): if loc == i: busid = glob + 1 #bus[i,BUS_I]=glob+1 pass fd.write('\n%5d/' % busid) fd.write('%d%7.3f%9.3f' % tuple(bus[i, [BUS_AREA, VM, VA]])) if bus[i, BUS_TYPE] == REF: fd.write('*') else: fd.write(' ') g = find((gen[:, GEN_STATUS] > 0) & (gen[:, GEN_BUS] == bus[i, BUS_I]) & ~isload(gen)) ld = find((gen[:, GEN_STATUS] > 0) & (gen[:, GEN_BUS] == bus[i, BUS_I]) & isload(gen)) if any(g + 1): fd.write('%9.2f%10.2f' % (sum(gen[g, PG]), sum(gen[g, QG]))) objValue1 = objValue1 + (lamP[i]) * (sum(gen[g, PG])) else: fd.write(' - - ') if logical_or(bus[i, PD], bus[i, QD]) | any(ld + 1): if any(ld + 1): fd.write('%10.2f*%9.2f*' % (bus[i, PD] - sum(gen[ld, PG]), bus[i, QD] - sum(gen[ld, QG]))) objValue2 = objValue2 + (lamP[i]) * (bus[i, PD] - sum(gen[ld, PG])) else: fd.write('%10.2f%10.2f ' % tuple(bus[i, [PD, QD]])) else: fd.write(' - - ') fd.write('%9.3f' % lamP[i]) if abs(lamQ[i]) > 1e-4: fd.write('%8.3f' % lamQ[i]) else: fd.write(' -') fd.write( '\n -------- -------- -------- --------') nzld = find((bus[:, PD] != 0.0) | (bus[:, QD] != 0.0)) onld = find((gen[:, GEN_STATUS] > 0) & isload(gen)) fd.write('\n Total: %9.2f %9.2f %9.2f %9.2f' % (sum(gen[ong, PG]), sum(gen[ong, QG]), sum(bus[nzld, PD]) - sum(gen[onld, PG]), sum(bus[nzld, QD]) - sum(gen[onld, QG]))) fd.write('\n') print("Local iteration of worker %d is %d" % (ID, nu)) # calculate local generation cost gencost = s.region['gencost'] pg = s.var['Pg'] """ objValue21=0 for i in range(ng): if(pg[i]>0): objValue1 = objValue1 - gencost[i,COST+1]* pg[i] print(gencost[i,COST+1]* pg[i]) else: objValue21 = objValue21 + gencost[i,COST+1]* (-pg[i]) print(gencost[i,COST+1]* (-pg[i])) objValue2 = objValue21 - objValue2 objValue = objValue1 + objValue2 """ objValue = dot(gencost[:, COST], pg**2) + dot( gencost[:, COST + 1], pg) + sum(gencost[:, COST + 2]) print(objValue) varDual = { 'ymd': s.var['ymd'], 'yad': s.var['yad'], 'yms': s.var['yms'], 'yas': s.var['yas'] } varPrimal = { 'Vm': s.var['Vm'], 'Va': s.var['Va'], 'Pg': s.var['Pg'], 'Qg': s.var['Qg'] } Result = { 'objValue': objValue, 'varPrimal': varPrimal, 'varDual': varDual, 'localiter': nu, 'primalgap': s.pb['primalgap'], 'dualgap': s.pb['dualgap'] } ng = s.region['ng'] for i in range(ng): for glob, loc in s.idx["mapping"].items(): if gen[i, GEN_BUS] == loc: gen[i, GEN_BUS] = glob + 1 break for i in range(nb): for glob, loc in s.idx["mapping"].items(): if loc == i: bus[i, BUS_I] = glob + 1 nl = s.region['nl'] for i in range(nl): for glob, loc in s.idx["mapping"].items(): #print(glob, loc, branch[i, F_BUS]) if branch[i, F_BUS] == loc: #print(branch[tl1,F_BUS]) branch[i, F_BUS] = glob + 1 break for i in range(nl): for glob, loc in s.idx["mapping"].items(): #print(glob, loc, branch[i, F_BUS]) if branch[i, T_BUS] == loc: #print(branch[tl1,F_BUS]) branch[i, T_BUS] = glob + 1 break success = flag reg_nb = find(bus[:, BUS_AREA] == ID) lamP = lamP[ix_(reg_nb, )] lamP2 = numpy.array(lamP) reg_bus = bus[ix_(reg_nb, )] print(reg_bus[:, BUS_I]) reg_lam = numpy.array((reg_bus[:, BUS_I], lamP)) print(reg_lam) ppc = {} ppc["bus"], ppc["gen"], ppc["branch"] = reg_bus, gen, branch ids1 = reg_bus[:, BUS_I] ids = numpy.array(ids1) #ppc["success"] = success #ppc=ext2int(ppc) P_history = [i * s.region["baseMVA"] for i in P_history] Q_history = [i * s.region["baseMVA"] for i in Q_history] #results = int2ext(ppc) """ fig = plt.figure() curfig = fig.add_subplot(1, 2, 1) curfig.plot(P_history, color = 'red', linewidth = 2.5, label = 'P') curfig = fig.add_subplot(1, 2, 2) curfig.plot(Q_history, color = 'blue', linewidth = 2.5, label = 'Q') curfig.set_yscale('log') curfig.legend(loc='upper right') """ #plt.show() """ print(all_gas) while( obj.txpool.inspect.pending.__dict__): pass end_time2 = time() final_balance = obj.eth.getBalance(obj.toChecksumAddress(client.eth_accounts()[0])) print("sasasa",ID, " ",start_balance - final_balance , "Time ", end_time2 - start_time2) """ output.put((ID - 1, Result, ppc["bus"], ppc["gen"], ppc["branch"], success, lamP2, ids, P_history, Q_history)) """
def pipsopf_solver(om, ppopt, out_opt=None): """Solves AC optimal power flow using PIPS. Inputs are an OPF model object, a PYPOWER options vector and a dict containing keys (can be empty) for each of the desired optional output fields. outputs are a C{results} dict, C{success} flag and C{raw} output dict. C{results} is a PYPOWER case dict (ppc) with the usual baseMVA, bus branch, gen, gencost fields, along with the following additional fields: - C{order} see 'help ext2int' for details of this field - C{x} final value of optimization variables (internal order) - C{f} final objective function value - C{mu} shadow prices on ... - C{var} - C{l} lower bounds on variables - C{u} upper bounds on variables - C{nln} - C{l} lower bounds on nonlinear constraints - C{u} upper bounds on nonlinear constraints - C{lin} - C{l} lower bounds on linear constraints - C{u} upper bounds on linear constraints C{success} is C{True} if solver converged successfully, C{False} otherwise C{raw} is a raw output dict in form returned by MINOS - xr final value of optimization variables - pimul constraint multipliers - info solver specific termination code - output solver specific output information @see: L{opf}, L{pips} @author: Ray Zimmerman (PSERC Cornell) @author: Carlos E. Murillo-Sanchez (PSERC Cornell & Universidad Autonoma de Manizales) @author: Richard Lincoln """ ##----- initialization ----- ## optional output if out_opt is None: out_opt = {} ## options verbose = ppopt['VERBOSE'] feastol = ppopt['PDIPM_FEASTOL'] gradtol = ppopt['PDIPM_GRADTOL'] comptol = ppopt['PDIPM_COMPTOL'] costtol = ppopt['PDIPM_COSTTOL'] max_it = ppopt['PDIPM_MAX_IT'] max_red = ppopt['SCPDIPM_RED_IT'] step_control = (ppopt['OPF_ALG'] == 565) ## OPF_ALG == 565, PIPS-sc if feastol == 0: feastol = ppopt['OPF_VIOLATION'] opt = { 'feastol': feastol, 'gradtol': gradtol, 'comptol': comptol, 'costtol': costtol, 'max_it': max_it, 'max_red': max_red, 'step_control': step_control, 'cost_mult': 1e-4, 'verbose': verbose } ## unpack data ppc = om.get_ppc() baseMVA, bus, gen, branch, gencost = \ ppc["baseMVA"], ppc["bus"], ppc["gen"], ppc["branch"], ppc["gencost"] vv, _, nn, _ = om.get_idx() ## problem dimensions nb = bus.shape[0] ## number of buses nl = branch.shape[0] ## number of branches ny = om.getN('var', 'y') ## number of piece-wise linear costs ## linear constraints A, l, u = om.linear_constraints() ## bounds on optimization vars _, xmin, xmax = om.getv() ## build admittance matrices Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch) ## try to select an interior initial point ll, uu = xmin.copy(), xmax.copy() ll[xmin == -Inf] = -1e10 ## replace Inf with numerical proxies uu[xmax == Inf] = 1e10 x0 = (ll + uu) / 2 Varefs = bus[bus[:, BUS_TYPE] == REF, VA] * (pi / 180) ## angles set to first reference angle x0[vv["i1"]["Va"]:vv["iN"]["Va"]] = Varefs[0] if ny > 0: ipwl = find(gencost[:, MODEL] == PW_LINEAR) # PQ = r_[gen[:, PMAX], gen[:, QMAX]] # c = totcost(gencost[ipwl, :], PQ[ipwl]) c = gencost.flatten('F')[sub2ind(gencost.shape, ipwl, NCOST+2*gencost[ipwl, NCOST])] ## largest y-value in CCV data x0[vv["i1"]["y"]:vv["iN"]["y"]] = max(c) + 0.1 * abs(max(c)) # x0[vv["i1"]["y"]:vv["iN"]["y"]] = c + 0.1 * abs(c) ## find branches with flow limits il = find((branch[:, RATE_A] != 0) & (branch[:, RATE_A] < 1e10)) nl2 = len(il) ## number of constrained lines ##----- run opf ----- f_fcn = lambda x, return_hessian=False: opf_costfcn(x, om, return_hessian) gh_fcn = lambda x: opf_consfcn(x, om, Ybus, Yf[il, :], Yt[il,:], ppopt, il) hess_fcn = lambda x, lmbda, cost_mult: opf_hessfcn(x, lmbda, om, Ybus, Yf[il, :], Yt[il, :], ppopt, il, cost_mult) solution = pips(f_fcn, x0, A, l, u, xmin, xmax, gh_fcn, hess_fcn, opt) x, f, info, lmbda, output = solution["x"], solution["f"], \ solution["eflag"], solution["lmbda"], solution["output"] success = (info > 0) ## update solution data Va = x[vv["i1"]["Va"]:vv["iN"]["Va"]] Vm = x[vv["i1"]["Vm"]:vv["iN"]["Vm"]] Pg = x[vv["i1"]["Pg"]:vv["iN"]["Pg"]] Qg = x[vv["i1"]["Qg"]:vv["iN"]["Qg"]] V = Vm * exp(1j * Va) ##----- calculate return values ----- ## update voltages & generator outputs bus[:, VA] = Va * 180 / pi bus[:, VM] = Vm gen[:, PG] = Pg * baseMVA gen[:, QG] = Qg * baseMVA gen[:, VG] = Vm[ gen[:, GEN_BUS].astype(int) ] ## compute branch flows Sf = V[ branch[:, F_BUS].astype(int) ] * conj(Yf * V) ## cplx pwr at "from" bus, p["u"]. St = V[ branch[:, T_BUS].astype(int) ] * conj(Yt * V) ## cplx pwr at "to" bus, p["u"]. branch[:, PF] = Sf.real * baseMVA branch[:, QF] = Sf.imag * baseMVA branch[:, PT] = St.real * baseMVA branch[:, QT] = St.imag * baseMVA ## line constraint is actually on square of limit ## so we must fix multipliers muSf = zeros(nl) muSt = zeros(nl) if len(il) > 0: muSf[il] = \ 2 * lmbda["ineqnonlin"][:nl2] * branch[il, RATE_A] / baseMVA muSt[il] = \ 2 * lmbda["ineqnonlin"][nl2:nl2+nl2] * branch[il, RATE_A] / baseMVA ## update Lagrange multipliers bus[:, MU_VMAX] = lmbda["upper"][vv["i1"]["Vm"]:vv["iN"]["Vm"]] bus[:, MU_VMIN] = lmbda["lower"][vv["i1"]["Vm"]:vv["iN"]["Vm"]] gen[:, MU_PMAX] = lmbda["upper"][vv["i1"]["Pg"]:vv["iN"]["Pg"]] / baseMVA gen[:, MU_PMIN] = lmbda["lower"][vv["i1"]["Pg"]:vv["iN"]["Pg"]] / baseMVA gen[:, MU_QMAX] = lmbda["upper"][vv["i1"]["Qg"]:vv["iN"]["Qg"]] / baseMVA gen[:, MU_QMIN] = lmbda["lower"][vv["i1"]["Qg"]:vv["iN"]["Qg"]] / baseMVA bus[:, LAM_P] = \ lmbda["eqnonlin"][nn["i1"]["Pmis"]:nn["iN"]["Pmis"]] / baseMVA bus[:, LAM_Q] = \ lmbda["eqnonlin"][nn["i1"]["Qmis"]:nn["iN"]["Qmis"]] / baseMVA branch[:, MU_SF] = muSf / baseMVA branch[:, MU_ST] = muSt / baseMVA ## package up results nlnN = om.getN('nln') ## extract multipliers for nonlinear constraints kl = find(lmbda["eqnonlin"] < 0) ku = find(lmbda["eqnonlin"] > 0) nl_mu_l = zeros(nlnN) nl_mu_u = r_[zeros(2*nb), muSf, muSt] nl_mu_l[kl] = -lmbda["eqnonlin"][kl] nl_mu_u[ku] = lmbda["eqnonlin"][ku] mu = { 'var': {'l': lmbda["lower"], 'u': lmbda["upper"]}, 'nln': {'l': nl_mu_l, 'u': nl_mu_u}, 'lin': {'l': lmbda["mu_l"], 'u': lmbda["mu_u"]} } results = ppc results["bus"], results["branch"], results["gen"], \ results["om"], results["x"], results["mu"], results["f"] = \ bus, branch, gen, om, x, mu, f pimul = r_[ results["mu"]["nln"]["l"] - results["mu"]["nln"]["u"], results["mu"]["lin"]["l"] - results["mu"]["lin"]["u"], -ones(ny > 0), results["mu"]["var"]["l"] - results["mu"]["var"]["u"], ] raw = {'xr': x, 'pimul': pimul, 'info': info, 'output': output} return results, success, raw
def admmopf_consfcn(self, x=None): if x is None: x = self.pb['x0'] nx = len(x) nb = self.region['nb'] ng = self.region['ng'] iv = self.idx['var'] bus = self.region['bus'] gen = self.region['gen'] branch = self.region['branch'] Ybus = self.region['Ybus'] Yf = self.region['Yf'] Yt = self.region['Yt'] baseMVA = self.region['baseMVA'] ridx = self.idx['rbus']['int'] # idx ranges iVa = iv['iVa'] iVm = iv['iVm'] iPg = iv['iPg'] iQg = iv['iQg'] # grab Pg and Qg gen[:, PG] = x[iPg] gen[:, QG] = x[iQg] # rebuid Sbus Sbus = makeSbus(1, bus, gen) # reconstruct V Va, Vm = x[iVa], x[iVm] V = Vm * exp(1j * Va) # evaluate power flow equations mis = V * conj(Ybus * V) - Sbus g = r_[mis.real, mis.imag] row = ridx + [i + nb for i in ridx] g = g[row] il = find((branch[:, RATE_A] != 0) & (branch[:, RATE_A] < 1e10)) nl2 = len(il) Ybus2, Yf2, Yt2 = makeYbus(baseMVA, bus, branch) Yf = Yf2[il, :] Yt = Yt2[il, :] if nl2 > 0: flow_max = (branch[il, RATE_A] / baseMVA)**2 flow_max[flow_max == 0] = Inf ## compute branch power flows ## complex power injected at "from" bus (p.u.) Sf = V[branch[il, F_BUS].astype(int)] * conj(Yf * V) ## complex power injected at "to" bus (p.u.) St = V[branch[il, T_BUS].astype(int)] * conj(Yt * V) h = r_[Sf.real**2 - flow_max, ## branch P limits (from bus) St.real**2 - flow_max] ## branch P limits (to bus) else: h = array([]) # ---- evaluate constraint gradients ------- # compute partials of injected bus powers dSbus_dVm, dSbus_dVa = dSbus_dV(Ybus, V) # w.r.t. V neg_Cg = sparse((-ones(ng), (gen[:, GEN_BUS], range(ng))), (nb, ng)) # construct Jacobian of equality constraints (power flow) and transpose it dg = lil_matrix((2 * nb, nx)) blank = sparse((nb, ng)) dg = vstack([ \ #P mismatch w.r.t Va, Vm, Pg, Qg hstack([dSbus_dVa.real, dSbus_dVm.real, neg_Cg, blank]), # Q mismatch w.r.t Va, Vm, Pg, Qg hstack([dSbus_dVa.imag, dSbus_dVm.imag, blank, neg_Cg]) ], "csr") dg = dg[row, :] dg = dg.T if nl2 > 0: ## compute partials of Flows w.r.t. V ## power dFf_dVa, dFf_dVm, dFt_dVa, dFt_dVm, Ff, Ft = \ dSbr_dV(branch[il, :], Yf, Yt, V) dFf_dVa = dFf_dVa.real dFf_dVm = dFf_dVm.real dFt_dVa = dFt_dVa.real dFt_dVm = dFt_dVm.real Ff = Ff.real Ft = Ft.real ## squared magnitude of flow (of complex power or current, or real power) df_dVa, df_dVm, dt_dVa, dt_dVm = \ dAbr_dV(dFf_dVa, dFf_dVm, dFt_dVa, dFt_dVm, Ff, Ft) ## construct Jacobian of inequality constraints (branch limits) ## and transpose it. dh = lil_matrix((2 * nl2, nx)) dh[:, r_[iVa, iVm].T] = vstack( [ hstack([df_dVa, df_dVm]), ## "from" flow limit hstack([dt_dVa, dt_dVm]) ## "to" flow limit ], "csr") dh = dh.T else: dh = None h = array([]) dh = None return h, g, dh, dg
def run_sim(ppc, elements, dynopt = None, events = None, recorder = None): """ Run a time-domain simulation Inputs: ppc PYPOWER load flow case elements Dictionary of dynamic model objects (machines, controllers, etc) with Object ID as key events Events object recorder Recorder object (empty) Outputs: recorder Recorder object (with data) """ ######### # SETUP # ######### # Get version information ver = pydyn_ver() print('PYPOWER-Dynamics ' + ver['Version'] + ', ' + ver['Date']) # Program options if dynopt: h = dynopt['h'] t_sim = dynopt['t_sim'] max_err = dynopt['max_err'] max_iter = dynopt['max_iter'] verbose = dynopt['verbose'] else: # Default program options h = 0.01 # step length (s) t_sim = 5 # simulation time (s) max_err = 0.0001 # Maximum error in network iteration (voltage mismatches) max_iter = 25 # Maximum number of network iterations verbose = False # Make lists of current injection sources (generators, external grids, etc) and controllers sources = [] controllers = [] for element in elements.values(): if element.__module__ in ['pydyn.sym_order6a', 'pydyn.sym_order6b', 'pydyn.sym_order4', 'pydyn.ext_grid', 'pydyn.vsc_average', 'pydyn.asym_1cage', 'pydyn.asym_2cage']: sources.append(element) if element.__module__ == 'pydyn.controller': controllers.append(element) # Set up interfaces interfaces = init_interfaces(elements) ################## # INITIALISATION # ################## print('Initialising models...') # Run power flow and update bus voltages and angles in PYPOWER case object results, success = runpf(ppc) ppc["bus"][:, VM] = results["bus"][:, VM] ppc["bus"][:, VA] = results["bus"][:, VA] # Build Ybus matrix ppc_int = ext2int(ppc) baseMVA, bus, branch = ppc_int["baseMVA"], ppc_int["bus"], ppc_int["branch"] Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch) # Build modified Ybus matrix Ybus = mod_Ybus(Ybus, elements, bus, ppc_int['gen'], baseMVA) # Calculate initial voltage phasors v0 = bus[:, VM] * (np.cos(np.radians(bus[:, VA])) + 1j * np.sin(np.radians(bus[:, VA]))) # Initialise sources from load flow for source in sources: if source.__module__ in ['pydyn.asym_1cage', 'pydyn.asym_2cage']: # Asynchronous machine source_bus = ppc_int['bus'][source.bus_no,0] v_source = v0[source_bus] source.initialise(v_source,0) else: # Generator or VSC source_bus = ppc_int['gen'][source.gen_no,0] S_source = np.complex(results["gen"][source.gen_no, 1] / baseMVA, results["gen"][source.gen_no, 2] / baseMVA) v_source = v0[source_bus] source.initialise(v_source,S_source) # Interface controllers and machines (for initialisation) for intf in interfaces: int_type = intf[0] var_name = intf[1] if int_type == 'OUTPUT': # If an output, interface in the reverse direction for initialisation intf[2].signals[var_name] = intf[3].signals[var_name] else: # Inputs are interfaced in normal direction during initialisation intf[3].signals[var_name] = intf[2].signals[var_name] # Initialise controllers for controller in controllers: controller.initialise() ############# # MAIN LOOP # ############# if events == None: print('Warning: no events!') # Factorise Ybus matrix Ybus_inv = splu(Ybus) y1 = [] v_prev = v0 print('Simulating...') for t in range(int(t_sim / h) + 1): if np.mod(t,1/h) == 0: print('t=' + str(t*h) + 's') # Interface controllers and machines for intf in interfaces: var_name = intf[1] intf[3].signals[var_name] = intf[2].signals[var_name] # Solve differential equations for j in range(4): # Solve step of differential equations for element in elements.values(): element.solve_step(h,j) # Interface with network equations v_prev = solve_network(sources, v_prev, Ybus_inv, ppc_int, len(bus), max_err, max_iter) if recorder != None: # Record signals or states recorder.record_variables(t*h, elements) if events != None: # Check event stack ppc, refactorise = events.handle_events(np.round(t*h,5), elements, ppc, baseMVA) if refactorise == True: # Rebuild Ybus from new ppc_int ppc_int = ext2int(ppc) baseMVA, bus, branch = ppc_int["baseMVA"], ppc_int["bus"], ppc_int["branch"] Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch) # Rebuild modified Ybus Ybus = mod_Ybus(Ybus, elements, bus, ppc_int['gen'], baseMVA) # Refactorise Ybus Ybus_inv = splu(Ybus) # Solve network equations v_prev = solve_network(sources, v_prev, Ybus_inv, ppc_int, len(bus), max_err, max_iter) return recorder