def __init__(self, paramfile): for row in paramfile: if row[0]=='BIAS_COMBINE': self.BIAS_COMBINE = row[1] if row[0]=='COMP_COMBINE': self.COMP_COMBINE = row[1] if row[0]=='FLAT_COMBINE': self.FLAT_COMBINE = row[1] if row[0]=='FIBER_TRACE': self.FIBER_TRACE = row[1] if row[0]=='BIAS_SUBTRACT': self.BIAS_SUBTRACT = row[1] if row[0]=='LAMBDA_SOLVE': self.LAMBDA_SOLVE = row[1] if row[0]=='COSMIC_RAYS': self.COSMIC_RAYS = row[1] if row[0]=='SKY_SUBTRACT': self.SKY_SUBTRACT = row[1] if row[0]=='WRITE_SPECTRA': self.WRITE_SPECTRA = row[1] if row[0]=='FIBERS_TOTAL': self.FIBERS_TOTAL = int(row[1]) if row[0]=='FIBERS_EXCLUDE': fibs = np.array( [ int(i) for i in row[1].split(',') ] ) fibs.sort() fibs = fibs[ np.find( 0<fibs ) ] fibs = fibs[ np.find( fibs<self.FIBERS_TOTAL+1 ) ] self.FIBERS_EXCLUDE = fibs if row[0]=='FIBER_WIDTH': self.FIBER_WIDTH = int(row[1]) if row[0]=='FIBER_SEP': self.FIBER_SEP = int(row[1]) if row[0]=='LO_BUFFER': self.LO_BUFFER = int(row[1]) if row[0]=='HI_BUFFER': self.HI_BUFFER = int(row[1]) if row[0]=='LINE_LIST': self.LINE_LIST = row[1] if row[0]=='COMP_HEADER': self.COMP_HEADER = row[1] if row[0]=='COMP_NAME': self.COMP_NAME = row[1] if row[0]=='POLYFIT_ORDER': self.POLYFIT_ORDER = int(row[1]) if row[0]=='CR_SCAN_DX': self.CR_SCAN_DX = int(row[1])
def bustypes(bus, gen): """Builds index lists of each type of bus (C{REF}, C{PV}, C{PQ}). Generators with "out-of-service" status are treated as L{PQ} buses with zero generation (regardless of C{Pg}/C{Qg} values in gen). Expects C{bus} and C{gen} have been converted to use internal consecutive bus numbering. @param bus: bus data @param gen: generator data @return: index lists of each bus type @author: Ray Zimmerman (PSERC Cornell) """ # get generator status nb = bus.shape[0] ng = gen.shape[0] # gen connection matrix, element i, j is 1 if, generator j at bus i is ON Cg = sparse((gen[:, GEN_STATUS] > 0, (gen[:, GEN_BUS], range(ng))), (nb, ng)) # number of generators at each bus that are ON bus_gen_status = (Cg * ones(ng, int)).astype(bool) # form index lists for slack, PV, and PQ buses ref = find((bus[:, BUS_TYPE] == REF) & bus_gen_status) # ref bus index pv = find((bus[:, BUS_TYPE] == PV) & bus_gen_status) # PV bus indices pq = find((bus[:, BUS_TYPE] == PQ) | ~bus_gen_status) # PQ bus indices # pick a new reference bus if for some reason there is none (may have been # shut down) if len(ref) == 0: ref = pv[0] # use the first PV bus pv = pv[1:] # take it off PV list return ref, pv, pq
def bustypes(bus, gen): """Builds index lists of each type of bus (C{REF}, C{PV}, C{PQ}). Generators with "out-of-service" status are treated as L{PQ} buses with zero generation (regardless of C{Pg}/C{Qg} values in gen). Expects C{bus} and C{gen} have been converted to use internal consecutive bus numbering. @param bus: bus data @param gen: generator data @return: index lists of each bus type @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln changes by Uni Kassel (Florian Schaefer): If new ref bus is chosen -> Init as numpy array """ # get generator status # nb = bus.shape[0] # ng = gen.shape[0] # gen connection matrix, element i, j is 1 if, generator j at bus i is ON #Cg = sparse((gen[:, GEN_STATUS] > 0, # (gen[:, GEN_BUS], range(ng))), (nb, ng)) # number of generators at each bus that are ON #bus_gen_status = (Cg * ones(ng, int)).astype(bool) # form index lists for slack, PV, and PQ buses ref = find((bus[:, BUS_TYPE] == REF)) # ref bus index pv = find((bus[:, BUS_TYPE] == PV)) # PV bus indices pq = find((bus[:, BUS_TYPE] == PQ)) # PQ bus indices return ref, pv, pq
def makePTDF(baseMVA, bus, branch, slack=None): """Builds the DC PTDF matrix for a given choice of slack. Returns the DC PTDF matrix for a given choice of slack. The matrix is C{nbr x nb}, where C{nbr} is the number of branches and C{nb} is the number of buses. The C{slack} can be a scalar (single slack bus) or an C{nb x 1} column vector of weights specifying the proportion of the slack taken up at each bus. If the C{slack} is not specified the reference bus is used by default. For convenience, C{slack} can also be an C{nb x nb} matrix, where each column specifies how the slack should be handled for injections at that bus. @see: L{makeLODF} @author: Ray Zimmerman (PSERC Cornell) """ ## use reference bus for slack by default if slack is None: slack = find(bus[:, BUS_TYPE] == REF) slack = slack[0] ## set the slack bus to be used to compute initial PTDF if isscalar(slack): slack_bus = slack else: slack_bus = 0 ## use bus 1 for temp slack bus nb = bus.shape[0] nbr = branch.shape[0] noref = arange(1, nb) ## use bus 1 for voltage angle reference noslack = find(arange(nb) != slack_bus) ## check that bus numbers are equal to indices to bus (one set of bus numbers) if any(bus[:, BUS_I] != arange(nb)): stderr.write('makePTDF: buses must be numbered consecutively') ## compute PTDF for single slack_bus Bbus, Bf, _, _ = makeBdc(baseMVA, bus, branch) Bbus, Bf = Bbus.todense(), Bf.todense() H = zeros((nbr, nb)) H[:, noslack] = solve( Bbus[ix_(noslack, noref)].T, Bf[:, noref].T ).T # = Bf[:, noref] * inv(Bbus[ix_(noslack, noref)]) ## distribute slack, if requested if not isscalar(slack): if len(slack.shape) == 1: ## slack is a vector of weights slack = slack / sum(slack) ## normalize weights ## conceptually, we want to do ... ## H = H * (eye(nb, nb) - slack * ones((1, nb))) ## ... we just do it more efficiently v = dot(H, slack) for k in range(nb): H[:, k] = H[:, k] - v else: H = dot(H, slack) return H
def ppci_to_pfsoln(ppci, options): internal = ppci["internal"] if options["only_v_results"]: # time series relevant hack which ONLY saves V from ppci _update_v(internal["bus"], internal["V"]) return internal["bus"], internal["gen"], internal["branch"] else: # reads values from internal ppci storage to bus, gen, branch and returns it if options['distributed_slack']: # consider buses with non-zero slack weights as if they were slack buses, # and gens with non-zero slack weights as if they were reference machines # this way, the function pfsoln will extract results for distributed slack gens, too # also, the function pfsoln will extract results for the PQ buses for xwards gens_with_slack_weights = find(internal["gen"][:, SL_FAC] != 0) # gen_buses_with_slack_weights = internal["gen"][gens_with_slack_weights, GEN_BUS].astype(int32) buses_with_slack_weights = internal["bus"][find( internal["bus"][:, SL_FAC_BUS] != 0), BUS_I].astype(int32) # buses_with_slack_weights = union1d(gen_buses_with_slack_weights, buses_with_slack_weights) ref = union1d(internal["ref"], buses_with_slack_weights) ref_gens = union1d(internal["ref_gens"], gens_with_slack_weights) else: ref = internal["ref"] ref_gens = internal["ref_gens"] _, pfsoln = _get_numba_functions(ppci, options) result_pfsoln = pfsoln(internal["baseMVA"], internal["bus"], internal["gen"], internal["branch"], internal["Ybus"], internal["Yf"], internal["Yt"], internal["V"], ref, ref_gens) return result_pfsoln
def sumAnyNBinom(p, anyN=1): """Returns probability of a positive outcome given that a positive outcome requires at least anyN events, and given that the independent probability of each event is in vector p. Parameters ---------- p : list or 1darray Vector of probabilities of each independent event. anyN : int Minimum number of events required to be considered a positive outcome. Returns ------- tot : float Overall probability of a positive outcome.""" if isinstance(p, list): p = np.asarray(p) n = len(p) tmp = np.zeros(n) tot = np.zeros(2**n) for eventi, event in enumerate(itertools.product(*tuple([[0, 1]] * n))): event = np.array(event) if np.sum(event) >= anyN: tmp[np.find(event == 1)] = p[np.find(event == 1)] tmp[np.find(event == 0)] = 1 - p[np.find(event == 0)] tot[eventi] = np.prod(tmp) return tot.sum()
def _sys_event(self, C): sysdef = self.sysdef row, col = C.shape if self.systype.lower() == "series": self.cutset = 1 - np.prod(np.ones((row, col)) - C, axis=1, dtype=int) ncutsets = [] elif self.systype.lower() == "parallel": self.cutset = np.prod(C, axis=1, dtype=int) ncutsets = [] else: if self.sysdef[1].lower() == "link": self.sysdef = -self.sysdef self.sysdef[0] = np.hstack((0, self.sysdef[0], 0)) sysnonzero = np.find(self.sysdef[0] != 0)[0] syszero = np.find(self.sysdef[0] == 0)[0] int1 = syszero - np.hstack((0, syszero[:-1])) sizeCutSets = int1[int1 > 1] - 1 ncutsets = sizeCutSets.shape[0] for i in xrange(ncutsets): cCutSet = np.ones(row, 1) for j in xrange(sizeCutSets[i]): comp = self.sysdef[0][sysnonzero[np.sum(sizeCutSets[:i]) + j]] if comp < 0: cCutSet = cCutSet * (np.ones((row, 1)) - C.T[:, abs(comp)]) else: cCutSet = cCutSet * C.T[:, comp] cCutSets[:, i] = cCutSet self.cutset = np.ones(row, 1) - np.prod(np.ones(row, ncutsets) - cCutSets, axis=1)
def rhoa( snd, cal=260e-9, corrramp=True ): ''' compute apparent resistivity from sounding ''' Tx = np.prod( [float(a) for a in snd['LOOP_SIZE'].split()] ) if 'COIL_SIZE' in snd: Rx = snd['COIL_SIZE'] else: Rx = Tx v = snd['VOLTAGE'] istart, istop = 0, len(v) # default: take all mav = np.find( v == max(v) ) if len(mav) > 1: #several equal big ones: start after istart = max(mav)+1 if min(v) < 0.0: # negative values: stop at first istop = min( np.find( v < 0.0 ) ) v = v[istart:istop] dv = snd['ST_DEV'][istart:istop] #/ snd['CURRENT'] t = snd['TIME'][istart:istop] if corrramp and 'RAMP_TIME' in snd: t = t - snd['RAMP_TIME'] if Rx == 1: # apparently B-field rhoa = rhoafromB( v * cal, t, Tx ) else: rhoa = rhoafromU( v, t, Tx, Rx ) rhoaerr = dv / v * (2./3.) return rhoa, t, rhoaerr
def bustypes(bus, gen, Sbus): """ Builds index lists of each type of bus (C{REF}, C{PV}, C{PQ}). Generators with "out-of-service" status are treated as L{PQ} buses with zero generation (regardless of C{Pg}/C{Qg} values in gen). Expects C{bus} and C{gen} have been converted to use internal consecutive bus numbering. @param bus: bus data @param gen: generator data @return: index lists of each bus type @author: Ray Zimmerman (PSERC Cornell) """ # flag to indicate that it is impossible to solve the grid the_grid_is_disabled = False # get generator status nb = bus.shape[0] ng = gen.shape[0] # gen connection matrix, element i, j is 1 if, generator j at bus i is ON Cg = sparse((gen[:, GEN_STATUS] > 0, (gen[:, GEN_BUS], range(ng))), (nb, ng)) # number of generators at each bus that are ON bus_gen_status = (Cg * ones(ng, int)).astype(bool) # form index lists for slack, PV, and PQ buses ref = find((bus[:, BUS_TYPE] == REF) & bus_gen_status) # ref bus index pv = find((bus[:, BUS_TYPE] == PV) & bus_gen_status) # PV bus indices pq = find((bus[:, BUS_TYPE] == PQ) | ~bus_gen_status) # PQ bus indices # pick a new reference bus if for some reason there is none (may have been # shut down) if len(ref) == 0: if len(pv) > 0: ref = [pv[0]] # use the first PV bus pv = pv[1:] # take it off PV list else: # look for positive power injections to take the largest as the slack positive_power_injections = Sbus.real[where(Sbus.real > 0)[0]] if len(positive_power_injections) > 0: idx = where(Sbus.real == max(positive_power_injections))[0] if len(idx) == 1: ref = idx i = where(pq == idx[0])[0][0] pq = delete(pq, i) else: warn('It was not possible to find a slack bus') the_grid_is_disabled = True else: warn('It was not possible to find a slack bus') the_grid_is_disabled = True # create the types array types = zeros(nb) types[ref] = 3 types[pv] = 2 types[pq] = 1 return ref, pv, pq, types, the_grid_is_disabled
def userfcn_reserves_ext2int(ppc, *args): """This is the 'ext2int' stage userfcn callback that prepares the input data for the formulation stage. It expects to find a 'reserves' field in ppc as described above. The optional args are not currently used. """ ## initialize some things r = ppc['reserves'] o = ppc['order'] ng0 = o['ext']['gen'].shape[0] ## number of original gens (+ disp loads) nrz = r['req'].shape[0] ## number of reserve zones if nrz > 1: ppc['reserves']['rgens'] = any(r['zones'], 0) ## mask of gens available to provide reserves else: ppc['reserves']['rgens'] = r['zones'] igr = find(ppc['reserves']['rgens']) ## indices of gens available to provide reserves ngr = len(igr) ## number of gens available to provide reserves ## check data for consistent dimensions if r['zones'].shape[0] != nrz: stderr.write('userfcn_reserves_ext2int: the number of rows in ppc[\'reserves\'][\'req\'] (%d) and ppc[\'reserves\'][\'zones\'] (%d) must match\n' % (nrz, r['zones'].shape[0])) if (r['cost'].shape[0] != ng0) & (r['cost'].shape[0] != ngr): stderr.write('userfcn_reserves_ext2int: the number of rows in ppc[\'reserves\'][\'cost\'] (%d) must equal the total number of generators (%d) or the number of generators able to provide reserves (%d)\n' % (r['cost'].shape[0], ng0, ngr)) if 'qty' in r: if r['qty'].shape[0] != r['cost'].shape[0]: stderr.write('userfcn_reserves_ext2int: ppc[\'reserves\'][\'cost\'] (%d x 1) and ppc[\'reserves\'][\'qty\'] (%d x 1) must be the same dimension\n' % (r['cost'].shape[0], r['qty'].shape[0])) ## convert both cost and qty from ngr x 1 to full ng x 1 vectors if necessary if r['cost'].shape[0] < ng0: if 'original' not in ppc['reserves']: ppc['reserves']['original'] = {} ppc['reserves']['original']['cost'] = r['cost'].copy() ## save original cost = zeros(ng0) cost[igr] = r['cost'] ppc['reserves']['cost'] = cost if 'qty' in r: ppc['reserves']['original']['qty'] = r['qty'].copy() ## save original qty = zeros(ng0) qty[igr] = r['qty'] ppc['reserves']['qty'] = qty ##----- convert stuff to internal indexing ----- ## convert all reserve parameters (zones, costs, qty, rgens) if 'qty' in r: ppc = e2i_field(ppc, ['reserves', 'qty'], 'gen') ppc = e2i_field(ppc, ['reserves', 'cost'], 'gen') ppc = e2i_field(ppc, ['reserves', 'zones'], 'gen', 1) ppc = e2i_field(ppc, ['reserves', 'rgens'], 'gen', 1) ## save indices of gens available to provide reserves ppc['order']['ext']['reserves']['igr'] = igr ## external indexing ppc['reserves']['igr'] = find(ppc['reserves']['rgens']) ## internal indexing return ppc
def getHNR(y, Fs, F0, Nfreqs): print('holla') NBins = len(y) N0 = round(Fs / F0) N0_delta = round(N0 * 0.1) y = [x * z for x, z in zip(np.hamming(len(y)), y)] fftY = np.fft(y, NBins) aY = np.log10(abs(fftY)) ay = np.ifft(aY) peakinx = np.zeros(np.floor(len(y)) / 2 / N0) for k in range(1, len(peakinx)): ayseg = ay[(k * N0 - N0_delta):(k * N0 + N0_delta)] val, inx = max( abs(ayseg)) # MAX does not behave the same - doesn't return inx?? peakinx[k] = inx + (k * N0) - N0_delta - 1 s_ayseg = np.sign(np.diff(ayseg)) l_inx = inx - np.find( (np.sign(s_ayseg[inx - 1:-1:1]) != np.sign(inx)))[0] + 1 r_inx = inx + np.find(np.sign(s_ayseg[inx + 1:]) == np.sign(inx))[0] l_inx = l_inx + k * N0 - N0_delta - 1 r_inx = r_inx + k * N0 - N0_delta - 1 for num in range(l_inx, r_inx): ay[num] = 0 midL = round(len(y) / 2) + 1 ay[midL:] = ay[(midL - 1):-1:(midL - 1 - (len(ay) - midL))] Nap = np.real(np.fft(ay)) N = Nap # ???? why? Ha = aY - Nap # change these names ffs Hdelta = F0 / Fs * len(y) for f in [ num + 0.0001 for num in range(Hdelta, round(len(y) / 2), Hdelta) ]: fstart = np.ceil(f - Hdelta) Bdf = abs(min(Ha[fstart:round(f)])) N[fstart:round(f)] = N[fstart:round(f)] - Bdf H = aY - N n = np.zeros(len(Nfreqs)) for k in range(1, len(Nfreqs)): Ef = round(Nfreqs[k] / Fs * len(y)) n[k] = (20 * np.mean(H[1:Ef])) - (20 * np.mean(N[1:Ef])) return n
def get_influence_matrix(self): """ :return: binary matrix showing influence of input to output """ m = None for layer in self.modules(): if isinstance(layer, nn.Linear): if m is None: m = np.find(layer.weight) else: m = m* np.find(layer.weight) return m
def pfsoln(baseMVA, bus0, gen0, branch0, Ybus, Yf, Yt, V, ref, ref_gens, Ibus=None): """Updates bus, gen, branch data structures to match power flow soln. @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln """ # initialize return values bus = bus0 gen = gen0 branch = branch0 # ----- update Qg for all gens and Pg for slack bus(es) ----- # generator info on = find(gen[:, GEN_STATUS] > 0) # which generators are on? gbus = gen[on, GEN_BUS].astype(int) # what buses are they at? # xward: add ref buses that are not at the generators xbus = setdiff1d(ref, gbus) # compute total injected bus powers Ibus = zeros(len(V)) if Ibus is None else Ibus Sbus = V[gbus] * conj(Ybus[gbus, :] * V - Ibus[gbus]) Sbus_xw = V[xbus] * conj(Ybus[xbus, :] * V - Ibus[xbus]) _update_v(bus, V) _update_q(baseMVA, bus, gen, gbus, Sbus, on) _update_p(baseMVA, bus, gen, ref, gbus, on, r_[Sbus, Sbus_xw], ref_gens) # ----- update/compute branch power flows ----- out = find(branch[:, BR_STATUS] == 0) # out-of-service branches br = find(branch[:, BR_STATUS]).astype(int) # in-service branches if len(out): raise RuntimeError # complex power at "from" bus Sf = V[real(branch[br, F_BUS]).astype(int)] * conj(Yf[br, :] * V) * baseMVA # complex power injected at "to" bus St = V[real(branch[br, T_BUS]).astype(int)] * conj(Yt[br, :] * V) * baseMVA branch[ix_(br, [PF, QF, PT, QT])] = c_[Sf.real, Sf.imag, St.real, St.imag] branch[ix_(out, [PF, QF, PT, QT])] = zeros((len(out), 4)) return bus, gen, branch
def modcost(gencost, alpha, modtype='SCALE_F'): """Modifies generator costs by shifting or scaling (F or X). For each generator cost F(X) (for real or reactive power) in C{gencost}, this function modifies the cost by scaling or shifting the function by C{alpha}, depending on the value of C{modtype}, and and returns the modified C{gencost}. Rows of C{gencost} can be a mix of polynomial or piecewise linear costs. C{modtype} takes one of the 4 possible values (let F_alpha(X) denote the the modified function):: SCALE_F (default) : F_alpha(X) == F(X) * ALPHA SCALE_X : F_alpha(X * ALPHA) == F(X) SHIFT_F : F_alpha(X) == F(X) + ALPHA SHIFT_X : F_alpha(X + ALPHA) == F(X) @author: Ray Zimmerman (PSERC Cornell) """ gencost = gencost.copy() ng, m = gencost.shape if ng != 0: ipwl = find(gencost[:, MODEL] == PW_LINEAR) ipol = find(gencost[:, MODEL] == POLYNOMIAL) c = gencost[ipol, COST:m] if modtype == 'SCALE_F': gencost[ipol, COST:m] = alpha * c gencost[ipwl, COST + 1:m:2] = alpha * gencost[ipwl, COST + 1:m:2] elif modtype == 'SCALE_X': for k in range(len(ipol)): n = gencost[ipol[k], NCOST].astype(int) for i in range(n): gencost[ipol[k], COST + i] = c[k, i] / alpha**(n - i - 1) gencost[ipwl, COST:m - 1:2] = alpha * gencost[ipwl, COST:m - 1:2] elif modtype == 'SHIFT_F': for k in range(len(ipol)): n = gencost[ipol[k], NCOST].astype(int) gencost[ipol[k], COST + n - 1] = alpha + c[k, n - 1] gencost[ipwl, COST + 1:m:2] = alpha + gencost[ipwl, COST + 1:m:2] elif modtype == 'SHIFT_X': for k in range(len(ipol)): n = gencost[ipol[k], NCOST].astype(int) gencost[ipol[k], COST:COST + n] = \ polyshift(c[k, :n].T, alpha).T gencost[ipwl, COST:m - 1:2] = alpha + gencost[ipwl, COST:m - 1:2] else: sys.stderr.write('modcost: "%s" is not a valid modtype\n' % modtype) return gencost
def modcost(gencost, alpha, modtype='SCALE_F'): """Modifies generator costs by shifting or scaling (F or X). For each generator cost F(X) (for real or reactive power) in C{gencost}, this function modifies the cost by scaling or shifting the function by C{alpha}, depending on the value of C{modtype}, and and returns the modified C{gencost}. Rows of C{gencost} can be a mix of polynomial or piecewise linear costs. C{modtype} takes one of the 4 possible values (let F_alpha(X) denote the the modified function):: SCALE_F (default) : F_alpha(X) == F(X) * ALPHA SCALE_X : F_alpha(X * ALPHA) == F(X) SHIFT_F : F_alpha(X) == F(X) + ALPHA SHIFT_X : F_alpha(X + ALPHA) == F(X) @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln """ gencost = gencost.copy() ng, m = gencost.shape if ng != 0: ipwl = find(gencost[:, MODEL] == PW_LINEAR) ipol = find(gencost[:, MODEL] == POLYNOMIAL) c = gencost[ipol, COST:m] if modtype == 'SCALE_F': gencost[ipol, COST:m] = alpha * c gencost[ipwl, COST+1:m:2] = alpha * gencost[ipwl, COST + 1:m:2] elif modtype == 'SCALE_X': for k in range(len(ipol)): n = gencost[ipol[k], NCOST].astype(int) for i in range(n): gencost[ipol[k], COST + i] = c[k, i] / alpha**(n - i - 1) gencost[ipwl, COST:m - 1:2] = alpha * gencost[ipwl, COST:m - 1:2] elif modtype == 'SHIFT_F': for k in range(len(ipol)): n = gencost[ipol[k], NCOST].astype(int) gencost[ipol[k], COST + n - 1] = alpha + c[k, n - 1] gencost[ipwl, COST+1:m:2] = alpha + gencost[ipwl, COST + 1:m:2] elif modtype == 'SHIFT_X': for k in range(len(ipol)): n = gencost[ipol[k], NCOST].astype(int) gencost[ipol[k], COST:COST + n] = \ polyshift(c[k, :n].T, alpha).T gencost[ipwl, COST:m - 1:2] = alpha + gencost[ipwl, COST:m - 1:2] else: sys.stderr.write('modcost: "%s" is not a valid modtype\n' % modtype) return gencost
def getHNR(y, Fs, F0, Nfreqs): print 'holla' NBins = len(y) N0 = round(Fs/F0) N0_delta = round(N0 * 0.1) y = [x*z for x,z in zip(np.hamming(len(y)),y)] fftY = np.fft(y, NBins) aY = np.log10(abs(fftY)) ay = np.ifft(aY) peakinx = np.zeros(np.floor(len(y))/2/N0) for k in range(1, len(peakinx)): ayseg = ay[k*N0 - N0_delta : k*N0 + N0_delta] val, inx = max(abs(ayseg)) #MAX does not behave the same - doesn't return inx?? peakinx[k] = inx + (k * N0) - N0_delta - 1 s_ayseg = np.sign(np.diff(ayseg)) l_inx = inx - np.find((np.sign(s_ayseg[inx-1:-1:1]) != np.sign(inx)))[0] + 1 r_inx = inx + np.find(np.sign(s_ayseg[inx+1:]) == np.sign(inx))[0] l_inx = l_inx + k*N0 - N0_delta - 1 r_inx = r_inx + k*N0 - N0_delta - 1 for num in range(l_inx, r_inx): ay[num] = 0 midL = round(len(y)/2)+1 ay[midL:] = ay[midL-1: -1 : midL-1-(len(ay)-midL)] Nap = np.real(np.fft(ay)) N = Nap #???? why? Ha = aY - Nap #change these names ffs Hdelta = F0/Fs * len(y) for f in [num+0.0001 for num in range(Hdelta, round(len(y)/2), Hdelta)]: fstart = np.ceil(f - Hdelta) Bdf = abs(min(Ha[fstart:round(f)])) N[fstart:round(f)] = N[fstart:round(f)] - Bdf H = aY - N n = np.zeros(len(Nfreqs)) for k in range(1, len(Nfreqs)): Ef = round(Nfreqs[k] / Fs * len(y)) n[k] = (20 * np.mean(H[1:Ef])) - (20 * np.mean(N[1:Ef])) return n
def makeSbus(baseMVA, bus, gen): """Builds the vector of complex bus power injections. Returns the vector of complex bus power injections, that is, generation minus load. Power is expressed in per unit. @see: L{makeYbus} kk @author: Ray Zimmerman (PSERC Cornell) """ ## generator info on = find(gen[:, GEN_STATUS] > 0) ## which generators are on? gbus = gen[on, GEN_BUS] ## what buses are they at? ## form net complex bus power injection vector nb = bus.shape[0] ngon = on.shape[0] ## connection matrix, element i, j is 1 if gen on(j) at bus i is ON Cg = sparse((ones(ngon), (gbus, range(ngon))), (nb, ngon)) ## power injected by gens plus power injected by loads converted to p.u. Sbus = (Cg * (gen[on, PG] + 1j * gen[on, QG]) - (bus[:, PD] + 1j * bus[:, QD])) / baseMVA return Sbus
def makeSbus(baseMVA, bus, gen): """Builds the vector of complex bus power injections. Returns the vector of complex bus power injections, that is, generation minus load. Power is expressed in per unit. @see: L{makeYbus} @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln """ ## generator info on = find(gen[:, GEN_STATUS] > 0) ## which generators are on? gbus = gen[on, GEN_BUS] ## what buses are they at? ## form net complex bus power injection vector nb = bus.shape[0] ngon = on.shape[0] ## connection matrix, element i, j is 1 if gen on(j) at bus i is ON Cg = sparse((ones(ngon), (gbus, range(ngon))), (nb, ngon)) ## power injected by gens plus power injected by loads converted to p.u. Sbus = ( Cg * (gen[on, PG] + 1j * gen[on, QG]) - (bus[:, PD] + 1j * bus[:, QD]) ) / baseMVA return Sbus
def Theta_abs_mean(dc_bus_sol): dc_bus_sol = dc_bus_sol.copy() Theta_slack = dc_bus_sol[find(dc_bus_sol[:, 5] == 1)[0], 2] dc_bus_sol[:, 2] -= Theta_slack dc_ThetaMean = np.mean(abs(dc_bus_sol[:, 2])) return dc_ThetaMean
def fix_entsoe(dic, limit=10): """ Handle DST shift and interpolate remaining nans. Error if time starts with last sunday in october 02:00...""" time = dic[list(dic)[0]].index month, day, wd, hour = time.month, time.day, time.weekday, time.hour shift = find((month == 10) & (wd == 6) & (hour == 2) & (day > 24)) # DST shift hours in October for i, df in dic.items(): if i in self.bidz: # DST shift in October for s in shift: if sum(np.isnan(df.iloc[s, :])) > 0 and sum(df.iloc[ s - 1, :]) / sum(df.iloc[s - 2, :]) > 1.5: df.iloc[s - 1, :] /= 2 df.iloc[s, :] = df.iloc[s - 1, :] # Remaining nans df.interpolate( limit=limit, inplace=True) # interpolate up to limit samples if np.sum(arr(np.isnan(df))) > 0: print( 'Too many nans in Entso-e data for %s, might not work properly.' % i) return dic
def quick_gci(g, fmin=20, fmax=1000, fs=20000, theta=0, reps=2, reps2=None, gamma=1, inside=False): """ Return GCIs using QuickGCI algorithm. """ Bh, Ah = butter1(fmin / (fs / 2), 'high') Bl, Al = butter1(fmax / (fs / 2), 'low') for i in range(reps): g = sig.filtfilt(Bh, Ah, g) g = sig.filtfilt(Bl, Al, g) x = fasthilbert(g) * exp(1j * theta) q = abs(x)**gamma * imag(-x) for i in range(reps if reps2 is None else reps2): q = sig.filtfilt(Bl, Al, q) r = fasthilbert(q) dphi = der(angle(r)) gci = find(dphi < -1.5 * pi) if not inside: return gci else: return gci, locals()
def gadfli(g, fmin=20, fmax=1000, fs=20000, m=0.25, tau=-0.25, theta=0, reps=2, inside=False): """ Return GCIs using GADFLI algorithm. """ Bh, Ah = butter1(fmin / (fs / 2), 'high') Bl, Al = butter1(fmax / (fs / 2), 'low') for i in range(reps): g = sig.filtfilt(Bh, Ah, g) g = sig.filtfilt(Bl, Al, g) dg = der(g) h = fasthilbert(dg) * exp(1j * theta) dphi = der(angle(h)) gci_c = find(dphi < -1.5 * pi) # candidates rh = real(h) kept = inlier_elim(rh, m) scale = kept.std() gci = array([i for i in gci_c if rh[i] < tau * scale]) if not inside: return gci else: return gci, locals()
def makeSbus(baseMVA, bus, gen, vm=None): """Builds the vector of complex bus power injections. Returns the vector of complex bus power injections, that is, generation minus load. Power is expressed in per unit. @see: L{makeYbus} @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln """ ## generator info on = find(gen[:, GEN_STATUS] > 0) ## which generators are on? gbus = gen[on, GEN_BUS] ## what buses are they at? ## form net complex bus power injection vector nb = bus.shape[0] ngon = on.shape[0] ## connection matrix, element i, j is 1 if gen on(j) at bus i is ON Cg = sparse((ones(ngon), (gbus, range(ngon))), (nb, ngon)) S_load = bus[:, PD] + 1j * bus[:, QD] if vm is not None: ci = bus[:, CID] cz = bus[:, CZD] cp = (1 - ci - cz) volt_depend = cp + ci * vm + cz * vm ** 2 S_load *= volt_depend ## power injected by gens plus power injected by loads converted to p.u. Sbus = (Cg * (gen[on, PG] + 1j * gen[on, QG]) - S_load) / baseMVA return Sbus
def userfcn_iflims_ext2int(ppc, *args): """This is the 'ext2int' stage userfcn callback that prepares the input data for the formulation stage. It expects to find an 'if' field in ppc as described above. The optional args are not currently used. """ ## initialize some things ifmap = ppc['if']['map'] o = ppc['order'] nl0 = o['ext']['branch'].shape[0] ## original number of branches nl = ppc['branch'].shape[0] ## number of on-line branches ## save if.map for external indexing ppc['order']['ext']['ifmap'] = ifmap ##----- convert stuff to internal indexing ----- e2i = zeros(nl0) e2i[o['branch']['status']['on']] = arange(nl) ## ext->int branch index mapping d = sign(ifmap[:, 1]) br = abs(ifmap[:, 1]).astype(int) ifmap[:, 1] = d * e2i[br] ifmap = delete(ifmap, find(ifmap[:, 1] == 0), 0) ## delete branches that are out ppc['if']['map'] = ifmap return ppc
def _update_p(baseMVA, bus, gen, ref, gbus, on, Sbus, ref_gens): # update Pg for slack bus(es) # inj P + local Pd for slack_bus in ref: gens_at_bus = find( gbus == slack_bus) # which is(are) the reference gen(s)? # xward results come at the PQ buses and not at the PV buses: if len(gens_at_bus) == 0: bus[slack_bus, PD] = -Sbus[ slack_bus].real * baseMVA # negative because it is a PQ bus continue p_bus = Sbus[gens_at_bus[0]].real * baseMVA + bus[slack_bus, PD] if len(gens_at_bus) > 1: # more than one generator at this ref bus # subtract off what is generated by other gens at this bus ext_grids = intersect1d(gens_at_bus, ref_gens) pv_gens = setdiff1d(gens_at_bus, ext_grids) p_ext_grids = p_bus - sum(gen[pv_gens, PG]) slack_weights = gen[ext_grids, SL_FAC] sum_slack_weights = sum(slack_weights) if sum_slack_weights > 0: # distribute bus slack power according to the distributed slack contribution factors gen[ext_grids, PG] = gen[ext_grids, PG] + (p_ext_grids - sum( gen[ext_grids, PG])) * slack_weights / sum_slack_weights else: gen[ext_grids, PG] = p_ext_grids / len(ext_grids) else: gen[on[gens_at_bus[0]], PG] = p_bus
def _update_q(baseMVA, bus, gen, gbus, Sbus, on): # update Qg for all generators gen[:, QG] = zeros(gen.shape[0]) # zero out all Qg gen[on, QG] = Sbus.imag * baseMVA + bus[gbus, QD] # inj Q + local Qd # ... at this point any buses with more than one generator will have # the total Q dispatch for the bus assigned to each generator. This # must be split between them. We do it first equally, then in proportion # to the reactive range of the generator. if len(on) > 1: # build connection matrix, element i, j is 1 if gen on(i) at bus j is ON nb = bus.shape[0] ngon = on.shape[0] Cg = csr_matrix((ones(ngon), (range(ngon), gbus)), (ngon, nb)) # divide Qg by number of generators at the bus to distribute equally ngg = Cg * Cg.sum(0).T # ngon x 1, number of gens at this gen's bus ngg = asarray(ngg).flatten() # 1D array gen[on, QG] = gen[on, QG] / ngg # divide proportionally Cmin = csr_matrix((gen[on, QMIN], (range(ngon), gbus)), (ngon, nb)) Cmax = csr_matrix((gen[on, QMAX], (range(ngon), gbus)), (ngon, nb)) Qg_tot = Cg.T * gen[on, QG] # nb x 1 vector of total Qg at each bus Qg_min = Cmin.sum(0).T # nb x 1 vector of min total Qg at each bus Qg_max = Cmax.sum(0).T # nb x 1 vector of max total Qg at each bus Qg_min = asarray(Qg_min).flatten() # 1D array Qg_max = asarray(Qg_max).flatten() # 1D array # gens at buses with Qg range = 0 ig = find(Cg * Qg_min == Cg * Qg_max) Qg_save = gen[on[ig], QG] gen[on, QG] = gen[on, QMIN] + (Cg * ((Qg_tot - Qg_min) / (Qg_max - Qg_min + EPS))) * \ (gen[on, QMAX] - gen[on, QMIN]) # ^ avoid div by 0 gen[on[ig], QG] = Qg_save # (terms are mult by 0 anyway)
def pfsoln(baseMVA, bus, gen, branch, Ybus, Yf, Yt, V, ref, ref_gens, Ibus=None): """Updates bus, gen, branch data structures to match power flow soln. @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln """ # generator info on = find(gen[:, GEN_STATUS] > 0) # which generators are on? gbus = gen[on, GEN_BUS].astype(int) # what buses are they at? # xward: add ref buses that are not at the generators xbus = setdiff1d(ref, gbus) # compute total injected bus powers Ibus = zeros(len(V)) if Ibus is None else Ibus Sbus = V[gbus] * conj(Ybus[gbus, :] * V - Ibus[gbus]) Sbus_xw = V[xbus] * conj(Ybus[xbus, :] * V - Ibus[xbus]) _update_v(bus, V) # update gen results _update_q(baseMVA, bus, gen, gbus, Sbus, on) _update_p(baseMVA, bus, gen, ref, gbus, on, r_[Sbus, Sbus_xw], ref_gens) # ----- update/compute branch power flows ----- branch = _update_branch_flows(Yf, Yt, V, baseMVA, branch) return bus, gen, branch
def line_walk(self, p): NC = self.number_of_cells() NP = p.shape[0] cell = self.entity('cell') cidx = np.random.randint(0, NC, NP) isNotOK = np.ones(NP, dtype=np.bool) while isNotOK.sum() > 0: idx0, = np.find(isNotOK) cidx0 = cidx[isNotOK] pp = p[isNotOK] a = np.zeros((len(cidx0), 3), dtype=self.ftype) p0 = cell[cidx0, 0] p1 = cell[cidx0, 1] p2 = cell[cidx0, 2] v0 = p0 - pp v1 = p1 - pp v2 = p2 - pp a[:, 0] = np.cross(v1, v2) a[:, 1] = np.cross(v2, v0) a[:, 2] = np.cross(v0, v1) idx = np.argmin(a, axis=-1) isOK = a[range(a.shape[0]), idx] >= 0
def userfcn_iflims_ext2int(ppc, *args): """This is the 'ext2int' stage userfcn callback that prepares the input data for the formulation stage. It expects to find an 'if' field in ppc as described above. The optional args are not currently used. """ ## initialize some things ifmap = ppc['if']['map'] o = ppc['order'] nl0 = o['ext']['branch'].shape[0] ## original number of branches nl = ppc['branch'].shape[0] ## number of on-line branches ## save if.map for external indexing ppc['order']['ext']['ifmap'] = ifmap ##----- convert stuff to internal indexing ----- e2i = zeros(nl0) e2i[o['branch']['status']['on']] = arange( nl) ## ext->int branch index mapping d = sign(ifmap[:, 1]) br = abs(ifmap[:, 1]).astype(int) ifmap[:, 1] = d * e2i[br] ifmap = delete(ifmap, find(ifmap[:, 1] == 0), 0) ## delete branches that are out ppc['if']['map'] = ifmap return ppc
def optimal_power_flow_energy_reserve(*args): casedata = args[0] # Target power flow modelling beta = args[1] # The reserve level mpc = loadcase(casedata) # Import the power flow modelling ## convert to internal indexing mpc = ext2int(mpc) baseMVA, bus, gen, branch,gencost = mpc["baseMVA"], mpc["bus"], mpc["gen"], mpc["branch"],mpc["gencost"] # nb = shape(mpc['bus'])[0] ## number of buses nl = shape(mpc['branch'])[0] ## number of branches ng = shape(mpc['gen'])[0] ## number of dispatchable injections ## Formualte the stat = branch[:, BR_STATUS] ## ones at in-service branches b = stat / branch[:, BR_X] ## series susceptance tap = ones(nl) ## default tap ratio = 1 i = find(branch[:, TAP]) ## indices of non-zero tap ratios tap[i] = branch[i, TAP] ## assign non-zero tap ratios ## build connection matrix Cft = Cf - Ct for line and from - to buses f = branch[:, F_BUS] ## list of "from" buses t = branch[:, T_BUS] ## list of "to" buses i = r_[range(nl), range(nl)] ## double set of row indices ## connection matrix Cft = sparse((r_[ones(nl), -ones(nl)], (i, r_[f, t])), (nl, nb)) ## build Bf such that Bf * Va is the vector of real branch powers injected ## at each branch's "from" bus Bf = sparse((r_[b, -b], (i, r_[f, t])), shape=(nl, nb)) ## = spdiags(b, 0, nl, nl) * Cft ## build Bbus Bbus = Cft.T * Bf # The distribution factor Distribution_factor = sparse(Bf*inv(Bbus)) Cg = sparse((ones(ng), (gen[:, GEN_BUS], arange(ng))), (nb, ng)) # Sparse index generation method is different from the way of matlab Cd = sparse((ones(nb), (bus[:, BUS_I], arange(nb))), (nb, nb)) # Sparse index load Pd = sum(bus[:,PD]) # Total power demand # Formulate the problem lb = concatenate((gen[:,PMIN],zeros(ng))) # extend the ub = concatenate((gen[:,PMAX],gen[:,PMAX])) Aeq = sparse(concatenate((ones(ng),zeros(ng)))) beq = [Pd] Aineq = sparse(hstack([Distribution_factor * Cg,zeros((nl,ng))])) Aineq = vstack([Aineq, -Aineq]) # The ramp reserve requirement Aineq = vstack([Aineq, sparse((r_[ones(ng), ones(ng)], (r_[arange(ng), arange(ng)], r_[arange(ng), ng+arange(ng)])), (ng, 2*ng))]) Aineq = vstack([Aineq, sparse((r_[-ones(ng), ones(ng)], (r_[arange(ng), arange(ng)], r_[arange(ng), ng+arange(ng)])), (ng, 2*ng))]) bineq = concatenate((branch[:, RATE_A] + Distribution_factor * Cd * bus[:, PD], branch[:, RATE_A] - Distribution_factor * Cd * bus[:, PD])) bineq = concatenate((bineq, gen[:, PMAX])) bineq = concatenate((bineq, -gen[:, PMIN])) c = concatenate((gencost[:,5],zeros(ng))) Q = diag(concatenate((gencost[:,4],zeros(ng)))) (Pg,obj) = miqp_gurobi(c = c,Q = Q, Aeq = Aeq, beq = beq, A = Aineq, b = bineq, xmin = lb,xmax = ub) obj = obj + sum(gencost[:,6]) return Pg, obj
def makeAang(baseMVA, branch, nb, ppopt): """Construct constraints for branch angle difference limits. Constructs the parameters for the following linear constraint limiting the voltage angle differences across branches, where C{Va} is the vector of bus voltage angles. C{nb} is the number of buses:: lang <= Aang * Va <= uang C{iang} is the vector of indices of branches with angle difference limits. @author: Ray Zimmerman (PSERC Cornell) @author: Carlos E. Murillo-Sanchez (PSERC Cornell & Universidad Autonoma de Manizales) @author: Richard Lincoln """ ## options ignore_ang_lim = ppopt['OPF_IGNORE_ANG_LIM'] if ignore_ang_lim: Aang = zeros((0, nb)) lang = array([]) uang = array([]) iang = array([]) else: iang = find(((branch[:, ANGMIN] != 0) & (branch[:, ANGMIN] > -360)) | ((branch[:, ANGMAX] != 0) & (branch[:, ANGMAX] < 360))) iangl = find(branch[iang, ANGMIN]) iangh = find(branch[iang, ANGMAX]) nang = len(iang) if nang > 0: ii = r_[arange(nang), arange(nang)] jj = r_[branch[iang, F_BUS], branch[iang, T_BUS]] Aang = sparse((r_[ones(nang), -ones(nang)], (ii, jj)), (nang, nb)) uang = Inf * ones(nang) lang = -uang lang[iangl] = branch[iang[iangl], ANGMIN] * pi / 180 uang[iangh] = branch[iang[iangh], ANGMAX] * pi / 180 else: Aang = zeros((0, nb)) lang = array([]) uang = array([]) return Aang, lang, uang, iang
def userfcn_reserves_formulation(om, *args): """This is the 'formulation' stage userfcn callback that defines the user costs and constraints for fixed reserves. It expects to find a 'reserves' field in the ppc stored in om, as described above. By the time it is passed to this callback, ppc['reserves'] should have two additional fields: - C{igr} C{1 x ngr}, indices of generators available for reserves - C{rgens} C{1 x ng}, 1 if gen avaiable for reserves, 0 otherwise It is also assumed that if cost or qty were C{ngr x 1}, they have been expanded to C{ng x 1} and that everything has been converted to internal indexing, i.e. all gens are on-line (by the 'ext2int' callback). The optional args are not currently used. """ ## initialize some things ppc = om.get_ppc() r = ppc['reserves'] igr = r['igr'] ## indices of gens available to provide reserves ngr = len(igr) ## number of gens available to provide reserves ng = ppc['gen'].shape[0] ## number of on-line gens (+ disp loads) ## variable bounds Rmin = zeros(ngr) ## bound below by 0 Rmax = Inf * ones(ngr) ## bound above by ... k = find(ppc['gen'][igr, RAMP_10]) Rmax[k] = ppc['gen'][igr[k], RAMP_10] ## ... ramp rate and ... if 'qty' in r: k = find(r['qty'][igr] < Rmax) Rmax[k] = r['qty'][igr[k]] ## ... stated max reserve qty Rmax = Rmax / ppc['baseMVA'] ## constraints I = speye(ngr, ngr, format='csr') ## identity matrix Ar = hstack([sparse((ones(ngr), (arange(ngr), igr)), (ngr, ng)), I], 'csr') ur = ppc['gen'][igr, PMAX] / ppc['baseMVA'] lreq = r['req'] / ppc['baseMVA'] ## cost Cw = r['cost'][igr] * ppc['baseMVA'] ## per unit cost coefficients ## add them to the model om.add_vars('R', ngr, [], Rmin, Rmax) om.add_constraints('Pg_plus_R', Ar, [], ur, ['Pg', 'R']) om.add_constraints('Rreq', sparse(r['zones'][:, igr]), lreq, [], ['R']) om.add_costs('Rcost', {'N': I, 'Cw': Cw}, ['R']) return om
def userfcn_reserves_formulation(om, *args): """This is the 'formulation' stage userfcn callback that defines the user costs and constraints for fixed reserves. It expects to find a 'reserves' field in the ppc stored in om, as described above. By the time it is passed to this callback, ppc['reserves'] should have two additional fields: - C{igr} C{1 x ngr}, indices of generators available for reserves - C{rgens} C{1 x ng}, 1 if gen avaiable for reserves, 0 otherwise It is also assumed that if cost or qty were C{ngr x 1}, they have been expanded to C{ng x 1} and that everything has been converted to internal indexing, i.e. all gens are on-line (by the 'ext2int' callback). The optional args are not currently used. """ ## initialize some things ppc = om.get_ppc() r = ppc['reserves'] igr = r['igr'] ## indices of gens available to provide reserves ngr = len(igr) ## number of gens available to provide reserves ng = ppc['gen'].shape[0] ## number of on-line gens (+ disp loads) ## variable bounds Rmin = zeros(ngr) ## bound below by 0 Rmax = Inf * ones(ngr) ## bound above by ... k = find(ppc['gen'][igr, RAMP_10]) Rmax[k] = ppc['gen'][igr[k], RAMP_10] ## ... ramp rate and ... if 'qty' in r: k = find(r['qty'][igr] < Rmax) Rmax[k] = r['qty'][igr[k]] ## ... stated max reserve qty Rmax = Rmax / ppc['baseMVA'] ## constraints I = speye(ngr, ngr, format='csr') ## identity matrix Ar = hstack([sparse((ones(ngr), (arange(ngr), igr)), (ngr, ng)), I], 'csr') ur = ppc['gen'][igr, PMAX] / ppc['baseMVA'] lreq = r['req'] / ppc['baseMVA'] ## cost Cw = r['cost'][igr] * ppc['baseMVA'] ## per unit cost coefficients ## add them to the model om.add_vars('R', ngr, [], Rmin, Rmax) om.add_constraints('Pg_plus_R', Ar, [], ur, ['Pg', 'R']) om.add_constraints('Rreq', sparse( r['zones'][:, igr] ), lreq, [], ['R']) om.add_costs('Rcost', {'N': I, 'Cw': Cw}, ['R']) return om
def userfcn_dcline_int2ext(results, args): """This is the 'int2ext' stage userfcn callback that converts everything back to external indexing and packages up the results. It expects to find a 'dcline' field in the results struct as described for ppc above. It also expects that the last 2*ndc entries in the gen and gencost matrices correspond to the in-service DC lines (where ndc is the number of rows in MPC.dcline. These extra rows are removed from gen and gencost and the flow is taken from the PG of these gens and placed in the flow column of the appropiate dcline row. The optional args are not currently used. """ c = idx_dcline.c ## initialize some things o = results['order'] k = find(o['ext']['dcline'][:, c['BR_STATUS']]) ndc = len(k) ## number of in-service DC lines ng = results['gen'].shape[0] - 2*ndc; ## number of original gens/disp loads ## extract dummy gens fg = results['gen'][ng:ng + ndc, :] tg = results['gen'][ng + ndc:ng + 2 * ndc, :] ## remove dummy gens #results['gen'] = results['gen'][:ng + 1, :] #results['gencost'] = results['gencost'][:ng + 1, :] results['gen'] = results['gen'][:ng, :] results['gencost'] = results['gencost'][:ng, :] ## get the solved flows results['dcline'][:, c['PF']] = -fg[:, PG] results['dcline'][:, c['PT']] = tg[:, PG] results['dcline'][:, c['QF']] = fg[:, QG] results['dcline'][:, c['QT']] = tg[:, QG] results['dcline'][:, c['VF']] = fg[:, VG] results['dcline'][:, c['VT']] = tg[:, VG] if fg.shape[1] >= MU_QMIN: results['dcline'] = c_[results['dcline'], zeros((ndc, 6))] results['dcline'][:, c['MU_PMIN'] ] = fg[:, MU_PMAX] + tg[:, MU_PMIN] results['dcline'][:, c['MU_PMAX'] ] = fg[:, MU_PMIN] + tg[:, MU_PMAX] results['dcline'][:, c['MU_QMINF']] = fg[:, MU_QMIN] results['dcline'][:, c['MU_QMAXF']] = fg[:, MU_QMAX] results['dcline'][:, c['MU_QMINT']] = tg[:, MU_QMIN] results['dcline'][:, c['MU_QMAXT']] = tg[:, MU_QMAX] results['order']['int'] = {} ##----- convert stuff back to external indexing ----- results['order']['int']['dcline'] = results['dcline'] ## save internal version ## copy results to external version o['ext']['dcline'][k, c['PF']:c['VT'] + 1] = results['dcline'][:, c['PF']:c['VT'] + 1] if results['dcline'].shape[1] == c['MU_QMAXT'] + 1: o['ext']['dcline'] = c_[o['ext']['dcline'], zeros((ndc, 6))] o['ext']['dcline'][k, c['MU_PMIN']:c['MU_QMAXT'] + 1] = \ results['dcline'][:, c['MU_PMIN']:c['MU_QMAXT'] + 1] results['dcline'] = o['ext']['dcline'] ## use external version return results
def inlier_elim(b, m): """ Apply inlier elimination, return remaining samples. """ while True: s = b.std() idx = find(abs(b) >= m * s) if len(idx) != len(b): b = b[idx] else: return b
def findTieline(bus, branch): tl = find(bus[branch[:, F_BUS].astype(int), BUS_AREA] != \ bus[branch[:, T_BUS].astype(int), BUS_AREA]) ntl = len(tl) busBD = branch[ix_(tl, [F_BUS, T_BUS])].astype(int) busBDregion = hstack((bus[ix_(busBD[:, 0], [BUS_AREA])], \ bus[ix_(busBD[:, 1], [BUS_AREA])])).astype(int) tieline = hstack((busBD, busBDregion, tl.reshape((ntl, 1)))) return tieline, ntl
def userfcn_dcline_int2ext(results, args): """This is the 'int2ext' stage userfcn callback that converts everything back to external indexing and packages up the results. It expects to find a 'dcline' field in the results struct as described for ppc above. It also expects that the last 2*ndc entries in the gen and gencost matrices correspond to the in-service DC lines (where ndc is the number of rows in MPC.dcline. These extra rows are removed from gen and gencost and the flow is taken from the PG of these gens and placed in the flow column of the appropiate dcline row. The optional args are not currently used. """ c = idx_dcline.c ## initialize some things o = results['order'] k = find(o['ext']['dcline'][:, c.BR_STATUS]) ndc = len(k) ## number of in-service DC lines ng = results['gen'].shape[0] - 2 * ndc ## number of original gens/disp loads ## extract dummy gens fg = results['gen'][ng + range(ndc + 1), :] tg = results['gen'][ng + ndc + range(ndc + 1), :] ## remove dummy gens results['gen'] = results['gen'][:ng + 1, :] results['gencost'] = results['gencost'][:ng + 1, :] ## get the solved flows results['dcline'][:, c.PF] = -fg[:, PG] results['dcline'][:, c.PT] = tg[:, PG] results['dcline'][:, c.QF] = fg[:, QG] results['dcline'][:, c.QT] = tg[:, QG] results['dcline'][:, c.VF] = fg[:, VG] results['dcline'][:, c.VT] = tg[:, VG] if fg.shape[1] >= MU_QMIN: results['dcline'][:, c.MU_PMIN] = fg[:, MU_PMAX] + tg[:, MU_PMIN] results['dcline'][:, c.MU_PMAX] = fg[:, MU_PMIN] + tg[:, MU_PMAX] results['dcline'][:, c.MU_QMINF] = fg[:, MU_QMIN] results['dcline'][:, c.MU_QMAXF] = fg[:, MU_QMAX] results['dcline'][:, c.MU_QMINT] = tg[:, MU_QMIN] results['dcline'][:, c.MU_QMAXT] = tg[:, MU_QMAX] ##----- convert stuff back to external indexing ----- results['order']['int']['dcline'] = results[ 'dcline'] ## save internal version ## copy results to external version o['ext']['dcline'][k, c.PF:c.VT + 1] = results['dcline'][:, c.PF:c.VT + 1] if results['dcline'].shape[1] == c.MU_QMAXT: o['ext']['dcline'][k, c.MU_PMIN:c.MU_QMAXT + 1] = \ results['dcline'][:, c.MU_PMIN:c.MU_QMAXT + 1] results['dcline'] = o['ext']['dcline'] ## use external version return results
def plot_settings(self): """ Plotting settings (colors, linewidths etc.), possibly depending on bus variable. """ var = 'Vbase' # base colors etc on Vbase var_lim = [380, 300, 0] # different categories of Vbase, should be a list # bus settings self.sets_variable_lim = var_lim var = self.bus.loc[:, var] self.bus_set = arr([ find(v >= arr(var_lim))[0] if v >= var_lim[-1] else -1 for v in var ]) self.bus_color = ['r', (230. / 255, 152. / 255, 0), 'g'] self.bus_name_color = ['k'] * 3 self.bus_lw = [1.5, 1, 1] self.bus_name_fs = [0, 0, 0] # line settings var_line = var.loc[self.line.bus0] self.line_set = arr([ find(v >= arr(var_lim))[0] if v >= var_lim[-1] else -1 for v in var_line ]) self.line_lw = [1, 1, 1] self.line_color = ['r', (230. / 255, 152. / 255, 0), 'g'] # Link self.link_lw = 1 self.link_color = 'b' # Interactive plot self.interactive = True # interactive map mode self.picker_node = 7 # tolerance for interactive picking self.picker_arc = 3 self.significant_figures = 3 # when info is displayed self.info_fc = [213. / 255, 230. / 255, 1] # color for info box self.info_ec = 'k' # color info-box edge self.info_lw = 1 # info-box edge width self.equal_aspect = False
def bustypes(bus, gen): """Builds index lists of each type of bus (C{REF}, C{PV}, C{PQ}). Generators with "out-of-service" status are treated as L{PQ} buses with zero generation (regardless of C{Pg}/C{Qg} values in gen). Expects C{bus} and C{gen} have been converted to use internal consecutive bus numbering. @param bus: bus data @param gen: generator data @return: index lists of each bus type @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln changes by Uni Kassel (Florian Schaefer): If new ref bus is chosen -> Init as numpy array """ # get generator status nb = bus.shape[0] ng = gen.shape[0] # gen connection matrix, element i, j is 1 if, generator j at bus i is ON Cg = sparse((gen[:, GEN_STATUS] > 0, (gen[:, GEN_BUS], range(ng))), (nb, ng)) # number of generators at each bus that are ON bus_gen_status = (Cg * ones(ng, int)).astype(bool) # form index lists for slack, PV, and PQ buses ref = find((bus[:, BUS_TYPE] == REF) & bus_gen_status) # ref bus index pv = find((bus[:, BUS_TYPE] == PV) & bus_gen_status) # PV bus indices pq = find((bus[:, BUS_TYPE] == PQ) | ~bus_gen_status) # PQ bus indices # throw an error since no reference bus is defined if len(ref) == 0: raise KeyError( "No reference bus (ext_grid) is available. Abort power flow calculation. Please add an ext_grid" ) # bugfix Pypower: must be an numpy array! # ref = zeros(1, dtype=int) # ref[0] = pv[0] # use the first PV bus # pv = pv[1:] # take it off PV list return ref, pv, pq
def totcost(gencost, Pg): """Computes total cost for generators at given output level. Computes total cost for generators given a matrix in gencost format and a column vector or matrix of generation levels. The return value has the same dimensions as PG. Each row of C{gencost} is used to evaluate the cost at the points specified in the corresponding row of C{Pg}. @author: Ray Zimmerman (PSERC Cornell) @author: Carlos E. Murillo-Sanchez (PSERC Cornell & Universidad Autonoma de Manizales) @author: Richard Lincoln """ ng, m = gencost.shape totalcost = zeros(ng) if len(gencost) > 0: ipwl = find(gencost[:, MODEL] == PW_LINEAR) ipol = find(gencost[:, MODEL] == POLYNOMIAL) if len(ipwl) > 0: p = gencost[:, COST:(m-1):2] c = gencost[:, (COST+1):m:2] for i in ipwl: ncost = gencost[i, NCOST] for k in arange(ncost - 1): p1, p2 = p[i, k], p[i, k+1] c1, c2 = c[i, k], c[i, k+1] m = (c2 - c1) / (p2 - p1) b = c1 - m * p1 Pgen = Pg[i] if Pgen < p2: totalcost[i] = m * Pgen + b break totalcost[i] = m * Pgen + b if len(ipol) > 0: totalcost[ipol] = polycost(gencost[ipol, :], Pg[ipol]) return totalcost
def polycost(gencost, Pg, der=0): """Evaluates polynomial generator cost & derivatives. C{f = polycost(gencost, Pg)} returns the vector of costs evaluated at C{Pg} C{df = polycost(gencost, Pg, 1)} returns the vector of first derivatives of costs evaluated at C{Pg} C{d2f = polycost(gencost, Pg, 2)} returns the vector of second derivatives of costs evaluated at C{Pg} C{gencost} must contain only polynomial costs C{Pg} is in MW, not p.u. (works for C{Qg} too) @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln """ if any(gencost[:, MODEL] == PW_LINEAR): sys.stderr.write('polycost: all costs must be polynomial\n') ng = len(Pg) maxN = max( gencost[:, NCOST].astype(int) ) minN = min( gencost[:, NCOST].astype(int) ) ## form coefficient matrix where 1st column is constant term, 2nd linear, etc. c = zeros((ng, maxN)) for n in arange(minN, maxN + 1): k = find(gencost[:, NCOST] == n) ## cost with n coefficients c[k, :n] = gencost[k, (COST + n - 1):COST - 1:-1] ## do derivatives for d in range(1, der + 1): if c.shape[1] >= 2: c = c[:, 1:maxN - d + 1] else: c = zeros((ng, 1)) break for k in range(2, maxN - d + 1): c[:, k-1] = c[:, k-1] * k ## evaluate polynomial if len(c) == 0: f = zeros(Pg.shape) else: f = c[:, :1].flatten() ## constant term for k in range(1, c.shape[1]): f = f + c[:, k] * Pg**k return f
def despike(datax,datay,threshold=3.0,interpolate=False): """ Remove spikes larger than threshold*(standard deviation of diffs in datay). interpolate=False (default) just removes the points; interpolate=True will just make them equal to the point to the left (if you need to keep array length constant, say). """ d = np.diff(datay) spikes = np.find(abs(d)>threshold*std(d)) if (len(spikes)>0.1*len(d)): print 'Did not remove spikes because it wanted to remove too many.' return datax,datay # if we're trying to remove a lot of points, just send it back... spikes=np.delete(spikes,find(diff(spikes)==1)+1) # if spike is one point, don't delete point after as well. if interpolate==False: datax = np.delete(datax,spikes+1) datay = np.delete(datay,spikes+1) else: # don't actually 'interpolate', just set it equal to the previous point # (actual interpolation could get messy in the case of a two-point spike, for example) for i in spikes+1: datay[i] = datay[i-3] if (np.any(np.abs(diff(datay))>threshold*np.std(d))): datax,datay = despike(datax,datay,threshold,interpolate) return datax, datay
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 makeBdc(baseMVA, bus, branch): """Builds the B matrices and phase shift injections for DC power flow. Returns the B matrices and phase shift injection vectors needed for a DC power flow. The bus real power injections are related to bus voltage angles by:: P = Bbus * Va + PBusinj The real power flows at the from end the lines are related to the bus voltage angles by:: Pf = Bf * Va + Pfinj Does appropriate conversions to p.u. @see: L{dcpf} @author: Carlos E. Murillo-Sanchez (PSERC Cornell & Universidad Autonoma de Manizales) @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln """ ## constants nb = bus.shape[0] ## number of buses nl = branch.shape[0] ## number of lines ## check that bus numbers are equal to indices to bus (one set of bus nums) if any(bus[:, BUS_I] != range(nb)): stderr.write('makeBdc: buses must be numbered consecutively in ' 'bus matrix\n') ## for each branch, compute the elements of the branch B matrix and the phase ## shift "quiescent" injections, where ## ## | Pf | | Bff Bft | | Vaf | | Pfinj | ## | | = | | * | | + | | ## | Pt | | Btf Btt | | Vat | | Ptinj | ## stat = branch[:, BR_STATUS] ## ones at in-service branches b = stat / branch[:, BR_X] ## series susceptance tap = ones(nl) ## default tap ratio = 1 i = find(branch[:, TAP]) ## indices of non-zero tap ratios tap[i] = branch[i, TAP] ## assign non-zero tap ratios b = b / tap ## build connection matrix Cft = Cf - Ct for line and from - to buses f = branch[:, F_BUS] ## list of "from" buses t = branch[:, T_BUS] ## list of "to" buses i = r_[range(nl), range(nl)] ## double set of row indices ## connection matrix Cft = sparse((r_[ones(nl), -ones(nl)], (i, r_[f, t])), (nl, nb)) ## build Bf such that Bf * Va is the vector of real branch powers injected ## at each branch's "from" bus Bf = sparse((r_[b, -b], (i, r_[f, t])))## = spdiags(b, 0, nl, nl) * Cft ## build Bbus Bbus = Cft.T * Bf ## build phase shift injection vectors Pfinj = b * (-branch[:, SHIFT] * pi / 180) ## injected at the from bus ... # Ptinj = -Pfinj ## and extracted at the to bus Pbusinj = Cft.T * Pfinj ## Pbusinj = Cf * Pfinj + Ct * Ptinj return Bbus, Bf, Pbusinj, Pfinj
def ext2int(ppc, val_or_field=None, ordering=None, dim=0): """Converts external to internal indexing. This function has two forms, the old form that operates on and returns individual matrices and the new form that operates on and returns an entire PYPOWER case dict. 1. C{ppc = ext2int(ppc)} If the input is a single PYPOWER case dict, then all isolated buses, off-line generators and branches are removed along with any generators, branches or areas connected to isolated buses. Then the buses are renumbered consecutively, beginning at 0, and the generators are sorted by increasing bus number. Any 'ext2int' callback routines registered in the case are also invoked automatically. All of the related indexing information and the original data matrices are stored under the 'order' key of the dict to be used by C{int2ext} to perform the reverse conversions. If the case is already using internal numbering it is returned unchanged. Example:: ppc = ext2int(ppc) @see: L{int2ext}, L{e2i_field}, L{e2i_data} @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln """ ppc = deepcopy(ppc) if val_or_field is None: # nargin == 1 first = 'order' not in ppc if first or ppc["order"]["state"] == 'e': ## initialize order if first: o = { 'ext': { 'bus': None, 'branch': None, 'gen': None }, 'bus': { 'e2i': None, 'i2e': None, 'status': {} }, 'gen': { 'e2i': None, 'i2e': None, 'status': {} }, 'branch': { 'status': {} } } else: o = ppc["order"] ## sizes nb = ppc["bus"].shape[0] ng = ppc["gen"].shape[0] ng0 = ng if 'A' in ppc: dc = True if ppc["A"].shape[1] < (2 * nb + 2 * ng) else False elif 'N' in ppc: dc = True if ppc["N"].shape[1] < (2 * nb + 2 * ng) else False else: dc = False ## save data matrices with external ordering if 'ext' not in o: o['ext'] = {} o["ext"]["bus"] = ppc["bus"].copy() o["ext"]["branch"] = ppc["branch"].copy() o["ext"]["gen"] = ppc["gen"].copy() if 'areas' in ppc: if len(ppc["areas"]) == 0: ## if areas field is empty del ppc['areas'] ## delete it (so it's ignored) else: ## otherwise o["ext"]["areas"] = ppc["areas"].copy() ## save it ## check that all buses have a valid BUS_TYPE bt = ppc["bus"][:, BUS_TYPE] err = find(~((bt == PQ) | (bt == PV) | (bt == REF) | (bt == NONE))) if len(err) > 0: sys.stderr.write('ext2int: bus %d has an invalid BUS_TYPE\n' % err) ## determine which buses, branches, gens are connected and ## in-service n2i = sparse((range(nb), (ppc["bus"][:, BUS_I], zeros(nb))), shape=(max(ppc["bus"][:, BUS_I]) + 1, 1)) n2i = array( n2i.todense().flatten() )[0, :] # as 1D array bs = (bt != NONE) ## bus status o["bus"]["status"]["on"] = find( bs ) ## connected o["bus"]["status"]["off"] = find( ~bs ) ## isolated gs = ( (ppc["gen"][:, GEN_STATUS] > 0) & ## gen status bs[ n2i[ppc["gen"][:, GEN_BUS].astype(int)] ] ) o["gen"]["status"]["on"] = find( gs ) ## on and connected o["gen"]["status"]["off"] = find( ~gs ) ## off or isolated brs = ( ppc["branch"][:, BR_STATUS].astype(int) & ## branch status bs[n2i[ppc["branch"][:, F_BUS].astype(int)]] & bs[n2i[ppc["branch"][:, T_BUS].astype(int)]] ).astype(bool) o["branch"]["status"]["on"] = find( brs ) ## on and conn o["branch"]["status"]["off"] = find( ~brs ) if 'areas' in ppc: ar = bs[ n2i[ppc["areas"][:, PRICE_REF_BUS].astype(int)] ] o["areas"] = {"status": {}} o["areas"]["status"]["on"] = find( ar ) o["areas"]["status"]["off"] = find( ~ar ) ## delete stuff that is "out" if len(o["bus"]["status"]["off"]) > 0: # ppc["bus"][o["bus"]["status"]["off"], :] = array([]) ppc["bus"] = ppc["bus"][o["bus"]["status"]["on"], :] if len(o["branch"]["status"]["off"]) > 0: # ppc["branch"][o["branch"]["status"]["off"], :] = array([]) ppc["branch"] = ppc["branch"][o["branch"]["status"]["on"], :] if len(o["gen"]["status"]["off"]) > 0: # ppc["gen"][o["gen"]["status"]["off"], :] = array([]) ppc["gen"] = ppc["gen"][o["gen"]["status"]["on"], :] if 'areas' in ppc and (len(o["areas"]["status"]["off"]) > 0): # ppc["areas"][o["areas"]["status"]["off"], :] = array([]) ppc["areas"] = ppc["areas"][o["areas"]["status"]["on"], :] ## update size nb = ppc["bus"].shape[0] ## apply consecutive bus numbering o["bus"]["i2e"] = ppc["bus"][:, BUS_I].copy() o["bus"]["e2i"] = zeros(max(o["bus"]["i2e"]) + 1) o["bus"]["e2i"][o["bus"]["i2e"].astype(int)] = arange(nb) ppc["bus"][:, BUS_I] = \ o["bus"]["e2i"][ ppc["bus"][:, BUS_I].astype(int) ].copy() ppc["gen"][:, GEN_BUS] = \ o["bus"]["e2i"][ ppc["gen"][:, GEN_BUS].astype(int) ].copy() ppc["branch"][:, F_BUS] = \ o["bus"]["e2i"][ ppc["branch"][:, F_BUS].astype(int) ].copy() ppc["branch"][:, T_BUS] = \ o["bus"]["e2i"][ ppc["branch"][:, T_BUS].astype(int) ].copy() if 'areas' in ppc: ppc["areas"][:, PRICE_REF_BUS] = \ o["bus"]["e2i"][ ppc["areas"][:, PRICE_REF_BUS].astype(int) ].copy() ## reorder gens in order of increasing bus number o["gen"]["e2i"] = argsort(ppc["gen"][:, GEN_BUS]) o["gen"]["i2e"] = argsort(o["gen"]["e2i"]) ppc["gen"] = ppc["gen"][o["gen"]["e2i"].astype(int), :] if 'int' in o: del o['int'] o["state"] = 'i' ppc["order"] = o ## update gencost, A and N if 'gencost' in ppc: ordering = ['gen'] ## Pg cost only if ppc["gencost"].shape[0] == (2 * ng0): ordering.append('gen') ## include Qg cost ppc = e2i_field(ppc, 'gencost', ordering) if 'A' in ppc or 'N' in ppc: if dc: ordering = ['bus', 'gen'] else: ordering = ['bus', 'bus', 'gen', 'gen'] if 'A' in ppc: ppc = e2i_field(ppc, 'A', ordering, 1) if 'N' in ppc: ppc = e2i_field(ppc, 'N', ordering, 1) ## execute userfcn callbacks for 'ext2int' stage if 'userfcn' in ppc: ppc = run_userfcn(ppc['userfcn'], 'ext2int', ppc) else: ## convert extra data if isinstance(val_or_field, str) or isinstance(val_or_field, list): ## field warn('Calls of the form ppc = ext2int(ppc, ' '\'field_name\', ...) have been deprecated. Please ' 'replace ext2int with e2i_field.', DeprecationWarning) gen, branch = val_or_field, ordering ppc = e2i_field(ppc, gen, branch, dim) else: ## value warn('Calls of the form val = ext2int(ppc, val, ...) have been ' 'deprecated. Please replace ext2int with e2i_data.', DeprecationWarning) gen, branch = val_or_field, ordering ppc = e2i_data(ppc, gen, branch, dim) return ppc
def qps_ipopt(H, c, A, l, u, xmin, xmax, x0, opt): """Quadratic Program Solver based on IPOPT. Uses IPOPT to solve the following QP (quadratic programming) problem:: min 1/2 x'*H*x + c'*x x subject to:: l <= A*x <= u (linear constraints) xmin <= x <= xmax (variable bounds) Inputs (all optional except C{H}, C{C}, C{A} and C{L}): - C{H} : matrix (possibly sparse) of quadratic cost coefficients - C{C} : vector of linear cost coefficients - C{A, l, u} : define the optional linear constraints. Default values for the elements of C{l} and C{u} are -Inf and Inf, respectively. - C{xmin, xmax} : optional lower and upper bounds on the C{x} variables, defaults are -Inf and Inf, respectively. - C{x0} : optional starting value of optimization vector C{x} - C{opt} : optional options structure with the following fields, all of which are also optional (default values shown in parentheses) - C{verbose} (0) - controls level of progress output displayed - 0 = no progress output - 1 = some progress output - 2 = verbose progress output - C{max_it} (0) - maximum number of iterations allowed - 0 = use algorithm default - C{ipopt_opt} - options struct for IPOPT, values in C{verbose} and C{max_it} override these options - C{problem} : The inputs can alternatively be supplied in a single C{problem} dict with fields corresponding to the input arguments described above: C{H, c, A, l, u, xmin, xmax, x0, opt} Outputs: - C{x} : solution vector - C{f} : final objective function value - C{exitflag} : exit flag - 1 = first order optimality conditions satisfied - 0 = maximum number of iterations reached - -1 = numerically failed - C{output} : output struct with the following fields: - C{iterations} - number of iterations performed - C{hist} - dict list with trajectories of the following: C{feascond}, C{gradcond}, C{compcond}, C{costcond}, C{gamma}, C{stepsize}, C{obj}, C{alphap}, C{alphad} - message - exit message - C{lmbda} : dict containing the Langrange and Kuhn-Tucker multipliers on the constraints, with fields: - C{mu_l} - lower (left-hand) limit on linear constraints - C{mu_u} - upper (right-hand) limit on linear constraints - C{lower} - lower bound on optimization variables - C{upper} - upper bound on optimization variables Calling syntax options:: x, f, exitflag, output, lmbda = \ qps_ipopt(H, c, A, l, u, xmin, xmax, x0, opt) x = qps_ipopt(H, c, A, l, u) x = qps_ipopt(H, c, A, l, u, xmin, xmax) x = qps_ipopt(H, c, A, l, u, xmin, xmax, x0) x = qps_ipopt(H, c, A, l, u, xmin, xmax, x0, opt) x = qps_ipopt(problem), where problem is a struct with fields: H, c, A, l, u, xmin, xmax, x0, opt all fields except 'c', 'A' and 'l' or 'u' are optional x = qps_ipopt(...) x, f = qps_ipopt(...) x, f, exitflag = qps_ipopt(...) x, f, exitflag, output = qps_ipopt(...) x, f, exitflag, output, lmbda = qps_ipopt(...) Example:: H = [ 1003.1 4.3 6.3 5.9; 4.3 2.2 2.1 3.9; 6.3 2.1 3.5 4.8; 5.9 3.9 4.8 10 ] c = zeros((4, 1)) A = [ 1 1 1 1 0.17 0.11 0.10 0.18 ] l = [1, 0.10] u = [1, Inf] xmin = zeros((4, 1)) x0 = [1, 0, 0, 1] opt = {'verbose': 2) x, f, s, out, lambda = qps_ipopt(H, c, A, l, u, xmin, [], x0, opt) Problem from U{http://www.jmu.edu/docs/sasdoc/sashtml/iml/chap8/sect12.htm} @see: C{pyipopt}, L{ipopt_options} @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln """ ##----- input argument handling ----- ## gather inputs if isinstance(H, dict): ## problem struct p = H if 'opt' in p: opt = p['opt'] if 'x0' in p: x0 = p['x0'] if 'xmax' in p: xmax = p['xmax'] if 'xmin' in p: xmin = p['xmin'] if 'u' in p: u = p['u'] if 'l' in p: l = p['l'] if 'A' in p: A = p['A'] if 'c' in p: c = p['c'] if 'H' in p: H = p['H'] else: ## individual args assert H is not None assert c is not None assert A is not None assert l is not None if opt is None: opt = {} # if x0 is None: # x0 = array([]) # if xmax is None: # xmax = array([]) # if xmin is None: # xmin = array([]) ## define nx, set default values for missing optional inputs if len(H) == 0 or not any(any(H)): if len(A) == 0 and len(xmin) == 0 and len(xmax) == 0: stderr.write('qps_ipopt: LP problem must include constraints or variable bounds\n') else: if len(A) > 0: nx = shape(A)[1] elif len(xmin) > 0: nx = len(xmin) else: # if len(xmax) > 0 nx = len(xmax) H = sparse((nx, nx)) else: nx = shape(H)[0] if len(c) == 0: c = zeros(nx) if len(A) > 0 and (len(l) == 0 or all(l == -Inf)) and \ (len(u) == 0 or all(u == Inf)): A = None ## no limits => no linear constraints nA = shape(A)[0] ## number of original linear constraints if nA: if len(u) == 0: ## By default, linear inequalities are ... u = Inf * ones(nA) ## ... unbounded above and ... if len(l) == 0: l = -Inf * ones(nA) ## ... unbounded below. if len(x0) == 0: x0 = zeros(nx) ## default options if 'verbose' in opt: verbose = opt['verbose'] else: verbose = 0 if 'max_it' in opt: max_it = opt['max_it'] else: max_it = 0 ## make sure args are sparse/full as expected by IPOPT if len(H) > 0: if not issparse(H): H = sparse(H) if not issparse(A): A = sparse(A) ##----- run optimization ----- ## set options dict for IPOPT options = {} if 'ipopt_opt' in opt: options['ipopt'] = ipopt_options(opt['ipopt_opt']) else: options['ipopt'] = ipopt_options() options['ipopt']['jac_c_constant'] = 'yes' options['ipopt']['jac_d_constant'] = 'yes' options['ipopt']['hessian_constant'] = 'yes' options['ipopt']['least_square_init_primal'] = 'yes' options['ipopt']['least_square_init_duals'] = 'yes' # options['ipopt']['mehrotra_algorithm'] = 'yes' ## default 'no' if verbose: options['ipopt']['print_level'] = min(12, verbose * 2 + 1) else: options['ipopt']['print_level = 0'] if max_it: options['ipopt']['max_iter'] = max_it ## define variable and constraint bounds, if given if nA: options['cu'] = u options['cl'] = l if len(xmin) > 0: options['lb'] = xmin if len(xmax) > 0: options['ub'] = xmax ## assign function handles funcs = {} funcs['objective'] = lambda x: 0.5 * x.T * H * x + c.T * x funcs['gradient'] = lambda x: H * x + c funcs['constraints'] = lambda x: A * x funcs['jacobian'] = lambda x: A funcs['jacobianstructure'] = lambda : A funcs['hessian'] = lambda x, sigma, lmbda: tril(H) funcs['hessianstructure'] = lambda : tril(H) ## run the optimization x, info = pyipopt(x0, funcs, options) if info['status'] == 0 | info['status'] == 1: eflag = 1 else: eflag = 0 output = {} if 'iter' in info: output['iterations'] = info['iter'] output['info'] = info['status'] f = funcs['objective'](x) ## repackage lmbdas kl = find(info['lmbda'] < 0) ## lower bound binding ku = find(info['lmbda'] > 0) ## upper bound binding mu_l = zeros(nA) mu_l[kl] = -info['lmbda'][kl] mu_u = zeros(nA) mu_u[ku] = info['lmbda'][ku] lmbda = { 'mu_l': mu_l, 'mu_u': mu_u, 'lower': info['zl'], 'upper': info['zu'] } return x, f, eflag, output, lmbda
def pfsoln(baseMVA, bus0, gen0, branch0, Ybus, Yf, Yt, V, ref, pv, pq): """Updates bus, gen, branch data structures to match power flow soln. @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln """ ## initialize return values bus = bus0 gen = gen0 branch = branch0 ##----- update bus voltages ----- bus[:, VM] = abs(V) bus[:, VA] = angle(V) * 180 / pi ##----- update Qg for all gens and Pg for slack bus(es) ----- ## generator info on = find(gen[:, GEN_STATUS] > 0) ## which generators are on? gbus = gen[on, GEN_BUS].astype(int) ## what buses are they at? ## compute total injected bus powers Sbus = V[gbus] * conj(Ybus[gbus, :] * V) ## update Qg for all generators gen[:, QG] = zeros(gen.shape[0]) ## zero out all Qg gen[on, QG] = Sbus.imag * baseMVA + bus[gbus, QD] ## inj Q + local Qd ## ... at this point any buses with more than one generator will have ## the total Q dispatch for the bus assigned to each generator. This ## must be split between them. We do it first equally, then in proportion ## to the reactive range of the generator. if len(on) > 1: ## build connection matrix, element i, j is 1 if gen on(i) at bus j is ON nb = bus.shape[0] ngon = on.shape[0] Cg = csr_matrix((ones(ngon), (range(ngon), gbus)), (ngon, nb)) ## divide Qg by number of generators at the bus to distribute equally ngg = Cg * Cg.sum(0).T ## ngon x 1, number of gens at this gen's bus ngg = asarray(ngg).flatten() # 1D array gen[on, QG] = gen[on, QG] / ngg ## divide proportionally Cmin = csr_matrix((gen[on, QMIN], (range(ngon), gbus)), (ngon, nb)) Cmax = csr_matrix((gen[on, QMAX], (range(ngon), gbus)), (ngon, nb)) Qg_tot = Cg.T * gen[on, QG]## nb x 1 vector of total Qg at each bus Qg_min = Cmin.sum(0).T ## nb x 1 vector of min total Qg at each bus Qg_max = Cmax.sum(0).T ## nb x 1 vector of max total Qg at each bus Qg_min = asarray(Qg_min).flatten() # 1D array Qg_max = asarray(Qg_max).flatten() # 1D array ## gens at buses with Qg range = 0 ig = find(Cg * Qg_min == Cg * Qg_max) Qg_save = gen[on[ig], QG] gen[on, QG] = gen[on, QMIN] + \ (Cg * ((Qg_tot - Qg_min) / (Qg_max - Qg_min + EPS))) * \ (gen[on, QMAX] - gen[on, QMIN]) ## ^ avoid div by 0 gen[on[ig], QG] = Qg_save ## (terms are mult by 0 anyway) ## update Pg for slack bus(es) ## inj P + local Pd for k in range(len(ref)): refgen = find(gbus == ref[k]) ## which is(are) the reference gen(s)? gen[on[refgen[0]], PG] = \ Sbus[refgen[0]].real * baseMVA + bus[ref[k], PD] if len(refgen) > 1: ## more than one generator at this ref bus ## subtract off what is generated by other gens at this bus gen[on[refgen[0]], PG] = \ gen[on[refgen[0]], PG] - sum(gen[on[refgen[1:len(refgen)]], PG]) ##----- update/compute branch power flows ----- out = find(branch[:, BR_STATUS] == 0) ## out-of-service branches br = find(branch[:, BR_STATUS]).astype(int) ## in-service branches ## complex power at "from" bus Sf = V[ branch[br, F_BUS].astype(int) ] * conj(Yf[br, :] * V) * baseMVA ## complex power injected at "to" bus St = V[ branch[br, T_BUS].astype(int) ] * conj(Yt[br, :] * V) * baseMVA branch[ ix_(br, [PF, QF, PT, QT]) ] = c_[Sf.real, Sf.imag, St.real, St.imag] branch[ ix_(out, [PF, QF, PT, QT]) ] = zeros((len(out), 4)) return bus, gen, branch
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 makeAvl(baseMVA, gen): """Construct linear constraints for constant power factor var loads. Constructs parameters for the following linear constraint enforcing a constant power factor constraint for dispatchable loads:: lvl <= Avl * [Pg, Qg] <= uvl C{ivl} is the vector of indices of generators representing variable loads. @author: Ray Zimmerman (PSERC Cornell) @author: Carlos E. Murillo-Sanchez (PSERC Cornell & Universidad Autonoma de Manizales) @author: Richard Lincoln """ ## data dimensions ng = gen.shape[0] ## number of dispatchable injections Pg = gen[:, PG] / baseMVA Qg = gen[:, QG] / baseMVA Pmin = gen[:, PMIN] / baseMVA Qmin = gen[:, QMIN] / baseMVA Qmax = gen[:, QMAX] / baseMVA # Find out if any of these "generators" are actually dispatchable loads. # (see 'help isload' for details on what constitutes a dispatchable load) # Dispatchable loads are modeled as generators with an added constant # power factor constraint. The power factor is derived from the original # value of Pmin and either Qmin (for inductive loads) or Qmax (for # capacitive loads). If both Qmin and Qmax are zero, this implies a unity # power factor without the need for an additional constraint. ivl = find( isload(gen) & ((Qmin != 0) | (Qmax != 0)) ) nvl = ivl.shape[0] ## number of dispatchable loads ## at least one of the Q limits must be zero (corresponding to Pmax == 0) if any( (Qmin[ivl] != 0) & (Qmax[ivl] != 0) ): stderr.write('makeAvl: either Qmin or Qmax must be equal to zero for ' 'each dispatchable load.\n') # Initial values of PG and QG must be consistent with specified power # factor This is to prevent a user from unknowingly using a case file which # would have defined a different power factor constraint under a previous # version which used PG and QG to define the power factor. Qlim = (Qmin[ivl] == 0) * Qmax[ivl] + (Qmax[ivl] == 0) * Qmin[ivl] if any( abs( Qg[ivl] - Pg[ivl] * Qlim / Pmin[ivl] ) > 1e-6 ): stderr.write('makeAvl: For a dispatchable load, PG and QG must be ' 'consistent with the power factor defined by PMIN and ' 'the Q limits.\n') # make Avl, lvl, uvl, for lvl <= Avl * [Pg Qg] <= uvl if nvl > 0: xx = Pmin[ivl] yy = Qlim pftheta = arctan2(yy, xx) pc = sin(pftheta) qc = -cos(pftheta) ii = r_[ arange(nvl), arange(nvl) ] jj = r_[ ivl, ivl + ng ] Avl = sparse((r_[pc, qc], (ii, jj)), (nvl, 2 * ng)) lvl = zeros(nvl) uvl = lvl else: Avl = zeros((0, 2*ng)) lvl = array([]) uvl = array([]) return Avl, lvl, uvl, ivl
def total_load(bus, gen=None, load_zone=None, which_type=None): """Returns vector of total load in each load zone. @param bus: standard C{bus} matrix with C{nb} rows, where the fixed active and reactive loads are specified in columns C{PD} and C{QD} @param gen: (optional) standard C{gen} matrix with C{ng} rows, where the dispatchable loads are specified by columns C{PG}, C{QG}, C{PMIN}, C{QMIN} and C{QMAX} (in rows for which C{isload(GEN)} returns C{True}). If C{gen} is empty, it assumes there are no dispatchable loads. @param load_zone: (optional) C{nb} element vector where the value of each element is either zero or the index of the load zone to which the corresponding bus belongs. If C{load_zone(b) = k} then the loads at bus C{b} will added to the values of C{Pd[k]} and C{Qd[k]}. If C{load_zone} is empty, the default is defined as the areas specified in the C{bus} matrix, i.e. C{load_zone = bus[:, BUS_AREA]} and load will have dimension C{= max(bus[:, BUS_AREA])}. If C{load_zone = 'all'}, the result is a scalar with the total system load. @param which_type: (default is 'BOTH' if C{gen} is provided, else 'FIXED') - 'FIXED' : sum only fixed loads - 'DISPATCHABLE' : sum only dispatchable loads - 'BOTH' : sum both fixed and dispatchable loads @see: L{scale_load} @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln """ nb = bus.shape[0] ## number of buses if gen is None: gen = array([]) if load_zone is None: load_zone = array([], int) ## fill out and check which_type if len(gen) == 0: which_type = 'FIXED' if (which_type == None) and (len(gen) > 0): which_type = 'BOTH' ## 'FIXED', 'DISPATCHABLE' or 'BOTH' if (which_type[0] != 'F') and (which_type[0] != 'D') and (which_type[0] != 'B'): stderr.write("total_load: which_type should be 'FIXED, 'DISPATCHABLE or 'BOTH'\n") want_Q = True want_fixed = (which_type[0] == 'B') | (which_type[0] == 'F') want_disp = (which_type[0] == 'B') | (which_type[0] == 'D') ## initialize load_zone if isinstance(load_zone, basestring) and (load_zone == 'all'): load_zone = ones(nb, int) ## make a single zone of all buses elif len(load_zone) == 0: load_zone = bus[:, BUS_AREA].astype(int) ## use areas defined in bus data as zones nz = max(load_zone) ## number of load zones ## fixed load at each bus, & initialize dispatchable if want_fixed: Pdf = bus[:, PD] ## real power if want_Q: Qdf = bus[:, QD] ## reactive power else: Pdf = zeros(nb) ## real power if want_Q: Qdf = zeros(nb) ## reactive power ## dispatchable load at each bus if want_disp: ## need dispatchable ng = gen.shape[0] is_ld = isload(gen) and (gen[:, GEN_STATUS] > 0) ld = find(is_ld) ## create map of external bus numbers to bus indices i2e = bus[:, BUS_I].astype(int) e2i = zeros(max(i2e) + 1) e2i[i2e] = arange(nb) gbus = gen[:, GEN_BUS].astype(int) Cld = sparse((is_ld, (e2i[gbus], arange(ng))), (nb, ng)) Pdd = -Cld * gen[:, PMIN] ## real power if want_Q: Q = zeros(ng) Q[ld] = (gen[ld, QMIN] == 0) * gen[ld, QMAX] + \ (gen[ld, QMAX] == 0) * gen[ld, QMIN] Qdd = -Cld * Q ## reactive power else: Pdd = zeros(nb) if want_Q: Qdd = zeros(nb) ## compute load sums Pd = zeros(nz) if want_Q: Qd = zeros(nz) for k in range(1, nz + 1): idx = find(load_zone == k) Pd[k - 1] = sum(Pdf[idx]) + sum(Pdd[idx]) if want_Q: Qd[k - 1] = sum(Qdf[idx]) + sum(Qdd[idx]) return Pd, Qd
z = scipy.io.loadmat('tmp2'); mi = z['mi'] sx,sy = mi.shape #im = plt.imshow(mi, interpolation='bilinear', # origin='lower', extent=[1,sx,1,sx]) #plt.savefig('mi.pdf') #plt.show() print "MI Loaded" mi = np.triu(mi) a = mi.ravel(); print a level = ss.scoreatpercentile(a,99.) indexes = np.transpose(np.find(mi<level)) #mi[mi<level]=0 plt.savefig('mi2.pdf') exit() print level print "MI prunned" G = nx.Graph(); for r in indexes: #for i in xrange(sx): #if i % 100 == 0: # print i #for j in xrange(i): # z = mi[i][j] # if z > 0:
def t_auction_pips(quiet=False): """Tests for code in auction.py, using PIPS solver. @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln """ n_tests = 183 t_begin(n_tests, quiet) try: from pypower.extras.smartmarket import runmkt except ImportError: t_skip(n_tests, 'smartmarket code not available') return ppopt = ppoption ppopt['OPF_VIOLATION'] = 1e-7 ppopt['PDIPM_GRADTOL'] = 1e-6 ppopt['PDIPM_COMPTOL'] = 1e-7 ppopt['PDIPM_COSTTOL'] = 5e-9 ppopt['OPF_ALG'] = 560 ppopt['OUT_ALL_LIM'] = 1 ppopt['OUT_BRANCH'] = 0 ppopt['OUT_SYS_SUM'] = 0 ppopt['OUT_ALL'] = 0 ppopt['VERBOSE'] = 0 q = array([ [12, 24, 24], [12, 24, 24], [12, 24, 24], [12, 24, 24], [12, 24, 24], [12, 24, 24], [10, 10, 10], [10, 10, 10], [10, 10, 10], ]) ##----- one offer block marginal @ $50 ----- p = array([ [20, 50, 60], [20, 40, 70], [20, 42, 80], [20, 44, 90], [20, 46, 75], [20, 48, 60], [100, 70, 60], [100, 50, 20], [100, 60, 50] ]) t = 'one marginal offer @ $50, auction_type = 5' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1150, 100, [], [], mpopt) cq5 = cq.copy() cp5 = cp.copy() i2e = bus.bus_i e2i = sparse((max(i2e), 1)) e2i[i2e] = range(bus.size()) G = find( isload(gen) == False ) ## real generators L = find( isload(gen) ) ## dispatchable loads Gbus = e2i[gen.gen_bus[G]] Lbus = e2i[gen.gen_bus[L]] Qfudge = zeros(p.shape) Qfudge[L, :] = \ diag(gen.Qg[L] / gen.Pg[L] * bus.lam_Q[Lbus]) * ones(p[L :].shape) t_is( cq[G[0], 1:3], [23.32, 0], 2, t ) t_is( cp[G[0], :], 50, 4, t ) t_is( cq[L[1], 0:2], [10, 0], 2, t ) t_is( cp[L[1], :], 54.0312, 4, t ) t_is( cp[G, 0], bus.lam_P[Gbus], 8, [t, ' : gen prices'] ) t_is( cp[L, 0], bus.lam_P[Lbus] + Qfudge[L, 0], 8, [t, ' : load prices'] ) lao_X = p(G[0], 1) / bus.lam_P[Gbus[0], LAM_P] fro_X = p(G(5), 2) / bus.lam_P[Gbus[5], LAM_P] lab_X = p(L(2), 1) / (bus.lam_P[Lbus[2]] + Qfudge[L[2], 0]) frb_X = p(L(1), 1) / (bus.lam_P[Lbus[1]] + Qfudge[L[1], 0]) t_is( lao_X, 1, 4, 'lao_X') t_is( fro_X, 1.1324, 4, 'fro_X') t_is( lab_X, 1.0787, 4, 'lab_X') t_is( frb_X, 0.9254, 4, 'frb_X') t = 'one marginal offer @ $50, auction_type = 1' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1110, 100, [], [], mpopt) cp1 = cp.copy() t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp, cp5, 6, [t, ' : prices'] ) t = 'one marginal offer @ $50, auction_type = 2' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1120, 100, [], [], mpopt) cp2 = cp.copy() t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp[G, :], cp5[G, :] * fro_X, 8, [t, ' : gen prices'] ) t_is( cp[L[0:1], :], cp5[L[0:1], :] * fro_X, 8, [t, ' : load 1,2 prices'] ) t_is( cp[L[2], :], 60, 5, [t, ' : load 3 price'] ) ## clipped by accepted bid t = 'one marginal offer @ $50, auction_type = 3' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1130, 100, [], [], mpopt) cp3 = cp.copy() t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp, cp5 * lab_X, 8, [t, ' : prices'] ) t = 'one marginal offer @ $50, auction_type = 4' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1140, 100, [], [], mpopt) t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp[G[0], :], p[G[0], 1], 8, [t, ' : gen 1 price'] ) t_is( cp[G[1:6], :], cp5[G[1:6], :] * frb_X, 8, [t, ' : gen 2-6 prices'] ) t_is( cp[L, :], cp5[L, :] * frb_X, 8, [t, ' : load prices'] ) t = 'one marginal offer @ $50, auction_type = 6' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1160, 100, [], [], mpopt) t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp, cp3, 8, [t, ' : prices'] ) p2 = p.copy() p2[L, :] = array([ [100, 100, 100], [100, 0, 0], [100, 100, 0] ]) MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1160, 100, [], [], mpopt) t_is( cq, cq5, 5, [t, ' : quantities'] ) t_is( cp[G, :], cp5[G, :] * fro_X, 4, [t, ' : gen prices'] ) t_is( cp[L, :], cp5[L, :] * fro_X, 4, [t, ' : load prices'] ) ## load 3 not clipped as in FRO t = 'one marginal offer @ $50, auction_type = 7' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1170, 100, [], [], mpopt) t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp, cp5 * (lao_X + lab_X) / 2, 8, [t, ' : prices'] ) t_is( cp, (cp1 + cp3) / 2, 8, [t, ' : prices'] ) t = 'one marginal offer @ $50, auction_type = 8' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1180, 100, [], [], mpopt) t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp[G, :], cp1[G, :], 8, [t, ' : gen prices'] ) t_is( cp[L, :], cp3[L, :], 8, [t, ' : load prices'] ) t = 'one marginal offer @ $50, auction_type = 0' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1100, 100, [], [], mpopt) t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp, p, 8, [t, ' : prices'] ) ##----- one bid block marginal @ $55 ----- p[L[1], 1] = 55 t = 'one marginal bid @ $55, auction_type = 5' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1150, 100, [], [], mpopt) cq5 = cq.copy() cp5 = cp.copy() Qfudge = zeros(p.shape) Qfudge[L, :] = diag(gen.Qg[L] / gen.Pg[L] * bus.lam_Q[Lbus]) * ones(p[L, :].shape) t_is( cq[G[0], 1:3], [24, 0], 2, t ) t_is( cp[G[0], :], 50.016, 3, t ) t_is( cq[L[1], 0:2], [10, 0.63], 2, t ) t_is( cp[L[1], :], 55, 4, t ) t_is( cp[G, 0], bus.lam_P[Gbus], 8, [t, ' : gen prices'] ) t_is( cp[L, 0], bus.lam_P[Lbus] + Qfudge[L, 0], 8, [t, ' : load prices'] ) lao_X = p[G[0], 1] / bus.lam_P[Gbus[0]] fro_X = p[G[5], 2] / bus.lam_P[Gbus[5]] lab_X = p[L[1], 1] / (bus.lam_P[Lbus[1]] + Qfudge[L[1], 0]) frb_X = p[L[2], 2] / (bus.lam_P[Lbus[2]] + Qfudge[L[2], 0]) t_is( lao_X, 0.9997, 4, 'lao_X') t_is( fro_X, 1.1111, 4, 'fro_X') t_is( lab_X, 1, 4, 'lab_X') t_is( frb_X, 0.8960, 4, 'frb_X') t = 'one marginal bid @ $55, auction_type = 1' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1110, 100, [], [], mpopt) cp1 = cp.copy() t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp, cp5 * lao_X, 8, [t, ' : prices'] ) t = 'one marginal bid @ $55, auction_type = 2' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1120, 100, [], [], mpopt) cp2 = cp.copy() t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp[G, :], cp5[G, :] * fro_X, 8, [t, ' : gen prices'] ) t_is( cp[L[0], :], cp5[L[0], :] * fro_X, 8, [t, ' : load 1 price'] ) t_is( cp[L[1], :], 55, 5, [t, ' : load 2 price'] ) t_is( cp[L[2], :], 60, 5, [t, ' : load 3 price'] ) t = 'one marginal bid @ $55, auction_type = 3' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1130, 100, [], [], mpopt) cp3 = cp.copy() t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp, cp5, 7, [t, ' : prices'] ) t = 'one marginal bid @ $55, auction_type = 4' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1140, 100, [], [], mpopt) cp4 = cp.copy() t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp[G[0], :], 50, 5, [t, ' : gen 1 price'] ) t_is( cp[G[1:6], :], cp5[G[1:6], :] * frb_X, 8, [t, ' : gen 2-6 prices'] ) t_is( cp[L, :], cp5[L, :] * frb_X, 8, [t, ' : load prices'] ) t = 'one marginal bid @ $55, auction_type = 6' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1160, 100, [], [], mpopt) t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp, cp1, 8, [t, ' : prices'] ) p2 = p.copy() p2[G, :] = array([ [0, 0, 100], [0, 0, 100], [0, 0, 100], [0, 0, 100], [0, 0, 100], [0, 0, 100] ]) MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1160, 100, [], [], mpopt) t_is( cq, cq5, 3, [t, ' : quantities'] ) t_is( cp[G, :], cp5[G, :] * frb_X, 3, [t, ' : gen prices'] ) ## gen 1, not clipped this time t_is( cp[L, :], cp4[L, :], 3, [t, ' : load prices'] ) t = 'one marginal bid @ $55, auction_type = 7' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1170, 100, [], [], mpopt) t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp, cp5 * (lao_X + lab_X) / 2, 8, [t, ' : prices'] ) t_is( cp, (cp1 + cp3) / 2, 8, [t, ' : prices'] ) t = 'one marginal bid @ $55, auction_type = 8' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1180, 100, [], [], mpopt) t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp[G, :], cp1[G, :], 8, [t, ' : gen prices'] ) t_is( cp[L, :], cp3[L, :], 8, [t, ' : load prices'] ) t = 'one marginal bid @ $55, auction_type = 0' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1100, 100, [], [], mpopt) t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp, p, 8, [t, ' : prices'] ) ##----- one bid block marginal @ $54.50 and one offer block marginal @ $50 ----- p[L[1], 1] = 54.5 t = 'marginal offer @ $50, bid @ $54.50, auction_type = 5' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1150, 100, [], [], mpopt) cq5 = cq.copy() cp5 = cp.copy() Qfudge = zeros(p.shape) Qfudge[L, :] = diag(gen.Qg[L] / gen.Pg[L] * bus.lam_Q[Lbus]) * ones(p[L, :].shape) t_is( cq[G[0], 1:3], [23.74, 0], 2, t ) t_is( cp[G[0], :], 50, 4, t ) t_is( cq[L[1], 0:2], [10, 0.39], 2, t ) t_is( cp[L[1], :], 54.5, 4, t ) t_is( cp[G, 0], bus.lam_P[Gbus], 8, [t, ' : gen prices'] ) t_is( cp[L, 0], bus.lam_P[Lbus] + Qfudge[L, 0], 8, [t, ' : load prices'] ) lao_X = p[G[0], 1] / bus.lam_P[Gbus[0]] fro_X = p[G[5], 2] / bus.lam_P[Gbus[5]] lab_X = p[L[1], 1] / (bus.lam_P[Lbus[1]] + Qfudge[L[1], 0]) frb_X = p[L[2], 2] / (bus.lam_P[Lbus[2]] + Qfudge[L[2], 0]) t_is( lao_X, 1, 4, 'lao_X') t_is( fro_X, 1.1221, 4, 'fro_X') t_is( lab_X, 1, 4, 'lab_X') t_is( frb_X, 0.8976, 4, 'frb_X') t = 'marginal offer @ $50, bid @ $54.50, auction_type = 1' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1110, 100, [], [], mpopt) cp1 = cp.copy() t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp, cp5, 4, [t, ' : prices'] ) t = 'marginal offer @ $50, bid @ $54.50, auction_type = 2' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1120, 100, [], [], mpopt) cp2 = cp.copy() t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp[G, :], cp5[G, :] * fro_X, 5, [t, ' : gen prices'] ) t_is( cp[L[0], :], cp5[L[0], :] * fro_X, 5, [t, ' : load 1 price'] ) t_is( cp[L[1], :], 54.5, 5, [t, ' : load 2 price'] ) t_is( cp[L[2], :], 60, 5, [t, ' : load 3 price'] ) t = 'marginal offer @ $50, bid @ $54.50, auction_type = 3' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1130, 100, [], [], mpopt) cp3 = cp.copy() t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp, cp5, 6, [t, ' : prices'] ) t = 'marginal offer @ $50, bid @ $54.50, auction_type = 4' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1140, 100, [], [], mpopt) cp4 = cp.copy() t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp[G[0], :], 50, 5, [t, ' : gen 1 price'] ) t_is( cp[G[1:5], :], cp5[G[1:5], :] * frb_X, 8, [t, ' : gen 2-5 prices'] ) t_is( cp[G[5], :], 48, 5, [t, ' : gen 6 price'] ) t_is( cp[L, :], cp5[L, :] * frb_X, 8, [t, ' : load prices'] ) t = 'marginal offer @ $50, bid @ $54.50, auction_type = 6' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1160, 100, [], [], mpopt) t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp, cp5, 4, [t, ' : prices'] ) t = 'marginal offer @ $50, bid @ $54.50, auction_type = 7' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1170, 100, [], [], mpopt) t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp, cp5, 4, [t, ' : prices'] ) t = 'marginal offer @ $50, bid @ $54.50, auction_type = 8' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1180, 100, [], [], mpopt) t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp, cp5, 4, [t, ' : prices'] ) t = 'marginal offer @ $50, bid @ $54.50, auction_type = 0' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1100, 100, [], [], mpopt) t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp, p, 8, [t, ' : prices'] ) ##----- gen 1 at Pmin, load 3 block 2 marginal @ $60 ----- t = 'gen 1 @ Pmin, marginal bid @ $60, auction_type = 5' p[L[1], 1] = 50 ## undo previous change p2 = p.copy() p2[G[0], 1:3] = [65, 65] MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1150, 100, [], [], mpopt) Qfudge = zeros(p.shape) Qfudge[L, :] = diag(gen.Qg[L] / gen.Pg[L] * bus.lam_Q[Lbus]) * ones(p[L, :].shape) t_is( cp[G[0], :], 65, 4, [t, ' : gen 1 price'] ) t_is( cp[G[1], :], 54.2974, 4, [t, ' : gen 2 price'] ) cq5 = cq.copy() cp5 = cp.copy() cp_lam = cp5.copt() cp_lam[0, :] = bus.lam_P[Gbus[0]] ## unclipped lao_X = p2[G[5], 1] / bus.lam_P[Gbus[5]] fro_X = p2[G[5], 2] / bus.lam_P[Gbus[5]] lab_X = p2[L[2], 1] / (bus.lam_P[Lbus[2]] + Qfudge[L[2], 0]) frb_X = p2[L[1], 1] / (bus.lam_P[Lbus[1]] + Qfudge[L[1], 0]) t_is( lao_X, 0.8389, 4, 'lao_X') t_is( fro_X, 1.0487, 4, 'fro_X') t_is( lab_X, 1, 4, 'lab_X') t_is( frb_X, 0.8569, 4, 'frb_X') t = 'gen 1 @ Pmin, marginal bid @ $60, auction_type = 1' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1110, 100, [], [], mpopt) cp1 = cp.copy() t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp[G[0], :], 65, 8, [t, ' : gen 1 price'] ) t_is( cp[G[1:6], :], cp_lam[G[1:6], :] * lao_X, 8, [t, ' : gen 2-6 prices'] ) t_is( cp[L, :], cp_lam[L, :] * lao_X, 8, [t, ' : load prices'] ) t = 'gen 1 @ Pmin, marginal bid @ $60, auction_type = 2' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1120, 100, [], [], mpopt) t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp[G[0], :], 65, 8, [t, ' : gen 1 price'] ) t_is( cp[G[1:6], :], cp_lam[G[1:6], :] * fro_X, 8, [t, ' : gen 2-6 prices'] ) t_is( cp[L[0:2], :], cp_lam[L[0:2], :] * fro_X, 8, [t, ' : load 1-2 prices'] ) t_is( cp[L[2], :], 60, 8, [t, ' : load 3 price'] ) t = 'gen 1 @ Pmin, marginal bid @ $60, auction_type = 3' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1130, 100, [], [], mpopt) cp3 = cp.copy() t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp[G[0], :], 65, 8, [t, ' : gen 1 price'] ) t_is( cp[G[1:6], :], cp_lam[G[1:6], :], 6, [t, ' : gen 2-6 prices'] ) t_is( cp[L, :], cp_lam[L, :], 6, [t, ' : load prices'] ) t = 'gen 1 @ Pmin, marginal bid @ $60, auction_type = 4' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1140, 100, [], [], mpopt) cp4 = cp.copy() t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp[G[0], :], 65, 5, [t, ' : gen 1 price'] ) t_is( cp[G[1:6], :], cp5[G[1:6], :] * frb_X, 8, [t, ' : gen 2-6 prices'] ) t_is( cp[L, :], cp5[L, :] * frb_X, 8, [t, ' : load prices'] ) t = 'gen 1 @ Pmin, marginal bid @ $60, auction_type = 6' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1160, 100, [], [], mpopt) t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp, cp4, 8, [t, ' : prices'] ) t = 'gen 1 @ Pmin, marginal bid @ $60, auction_type = 7' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1170, 100, [], [], mpopt) t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp[G[0], :], 65, 4, [t, ' : gen 1 price'] ) t_is( cp[G[1:6], :], cp_lam[G[1:6], :] * (lao_X + lab_X) / 2, 8, [t, ' : gen 2-6 prices'] ) t_is( cp[L, :], cp_lam[L, :] * (lao_X + lab_X) / 2, 8, [t, ' : load prices'] ) t_is( cp, (cp1 + cp3) / 2, 8, [t, ' : prices'] ) t = 'gen 1 @ Pmin, marginal bid @ $60, auction_type = 8' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1180, 100, [], [], mpopt) t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp[G, :], cp1[G, :], 8, [t, ' : prices'] ) t_is( cp[L, :], cp3[L, :], 8, [t, ' : prices'] ) t = 'gen 1 @ Pmin, marginal bid @ $60, auction_type = 0' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1100, 100, [], [], mpopt) t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp, p2, 8, [t, ' : prices'] ) ##----- gen 1 at Pmin, gen 6 block 3 marginal @ $60 ----- t = 'gen 1 @ Pmin, marginal offer @ $60, auction_type = 5' p2[L, :] = array([ [100, 100, 100], [100, 0, 0], [100, 100, 0] ]) MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1150, 100, [], [], mpopt) Qfudge = zeros(p.shape) Qfudge[L, :] = diag(gen.Qg[L] / gen.Pg[L] * bus.lam_Q[Lbus]) * ones(p[L, :].shape) t_is( cp[G[0], :], 65, 4, [t, ' : gen 1 price'] ) t_is( cp[G[1], :], 57.1612, 4, [t, ' : gen 2 price'] ) cq5 = cq.copy() cp5 = cp.copy() cp_lam = cp5.copy() cp_lam[0, :] = bus.lamP[Gbus[0]] ## unclipped lao_X = p2[G[5], 2] / bus.lam_P[Gbus[5]] fro_X = p2[G[0], 2] / bus.lam_P[Gbus[0]] lab_X = p2[L[2], 1] / (bus.lam_P[Lbus[2]] + Qfudge[L[2], 0]) frb_X = p2[L[1], 1] / (bus.lam_P[Lbus[1]] + Qfudge[L[1], 0]) t_is( lao_X, 1, 4, 'lao_X') t_is( fro_X, 1.1425, 4, 'fro_X') t_is( lab_X, 1.5813, 4, 'lab_X') t_is( frb_X, 0, 4, 'frb_X') t = 'gen 1 @ Pmin, marginal offer @ $60, auction_type = 1' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1110, 100, [], [], mpopt) cp1 = cp.copy() t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp, cp5, 6, [t, ' : prices'] ) t = 'gen 1 @ Pmin, marginal offer @ $60, auction_type = 2' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1120, 100, [], [], mpopt) t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp, cp_lam * fro_X, 8, [t, ' : prices'] ) t = 'gen 1 @ Pmin, marginal offer @ $60, auction_type = 3' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1130, 100, [], [], mpopt) cp3 = cp.copy() t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp, cp_lam * lab_X, 8, [t, ' : prices'] ) t = 'gen 1 @ Pmin, marginal offer @ $60, auction_type = 4' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1140, 100, [], [], mpopt) t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp[G, 0], [654042444660], 4, [t, ' : gen prices'] ) t_is( cp[L, :], cp_lam[L, :] * frb_X, 8, [t, ' : prices'] ) t = 'gen 1 @ Pmin, marginal offer @ $60, auction_type = 6' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1160, 100, [], [], mpopt) t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp, cp_lam * fro_X, 8, [t, ' : prices'] ) t = 'gen 1 @ Pmin, marginal offer @ $60, auction_type = 7' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1170, 100, [], [], mpopt) t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp, cp_lam * (lao_X + lab_X) / 2, 8, [t, ' : prices'] ) t_is( cp, (cp_lam + cp3) / 2, 7, [t, ' : prices'] ) t = 'gen 1 @ Pmin, marginal offer @ $60, auction_type = 8' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1180, 100, [], [], mpopt) t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp[G, :], cp5[G, :], 7, [t, ' : prices'] ) t_is( cp[L, :], cp3[L, :], 8, [t, ' : prices'] ) t = 'gen 1 @ Pmin, marginal offer @ $60, auction_type = 0' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p2, 1100, 100, [], [], mpopt) t_is( cq, cq5, 8, [t, ' : quantities'] ) t_is( cp, p2, 8, [t, ' : prices'] ) ##----- gen 2 decommitted, one offer block marginal @ $60 ----- p[G[1], :] = p[G[1], :] + 100 t = 'price of decommited gen, auction_type = 5' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1150, 200, [], [], mpopt) cp5 = cp.copy() Qfudge = zeros(p.shape) Qfudge[L, :] = diag(gen.Qg[L] / gen.Pg[L] * bus.lam_Q[Lbus]) * ones(p[L, :].shape) t_is(sum(cq[1, :]), 0, 8, t) t_is(cp[1, 0], 59.194, 3, t) # Xo = p[0:6, :] / (diag(bus.lam_P[Gbus]) * ones(p[G, :].shape)) # ao = (cq[0:6, :] != 0) # ro = (cq[0:6, :] == 0) # Xb = p[6:9, :] / (diag(bus.lam_P[Lbus] + gen.Qg[L] / gen.Pg[L] * bus.lam_Q[Lbus]) * ones(p[L, :].shape)) # ab = (cq[6:9, :] != 0) # rb = (cq[6:9, :] == 0) # aXo = ao * Xo # rXo = ro * Xo # aXb = ab * Xb # rXb = rb * Xb lao_X = p[G[5], 2] / bus.lam_P[Gbus[5]] fro_X = p[G[0], 2] / bus.lam_P[Gbus[0]] lab_X = p[L[0], 1] / (bus.lam_P[Lbus[0]] + Qfudge[L[0], 0]) frb_X = p[L[0], 2] / (bus.lam_P[Lbus[0]] + Qfudge[L[0], 0]) t_is( lao_X, 1, 4, 'lao_X') t_is( fro_X, 1.0212, 4, 'fro_X') t_is( lab_X, 1.1649, 4, 'lab_X') t_is( frb_X, 0.9985, 4, 'frb_X') t = 'price of decommited gen, auction_type = 1' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1110, 200, [], [], mpopt) t_is(cp[1, 0], 59.194, 3, t) t = 'price of decommited gen, auction_type = 2' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1120, 200, [], [], mpopt) t_is(cp[1, 0], cp5[1, 0] * fro_X, 3, t) t = 'price of decommited gen, auction_type = 3' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1130, 200, [], [], mpopt) t_is(cp[1, 0], cp5[1, 0] * lab_X, 3, t) t = 'price of decommited gen, auction_type = 4' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1140, 200, [], [], mpopt) t_is(cp[1, 0], cp5[1, 0] * frb_X, 3, t) t = 'price of decommited gen, auction_type = 6' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1160, 200, [], [], mpopt) t_is(cp[1, 0], cp5[1, 0] * fro_X, 3, t) t = 'price of decommited gen, auction_type = 7' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1170, 200, [], [], mpopt) t_is(cp[1, 0], cp5[1, 0] * (lao_X + lab_X) / 2, 3, t) t = 'price of decommited gen, auction_type = 0' MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1100, 200, [], [], mpopt) t_is(cp[1, 0], 120, 3, t) t = 'single block, marginal offer @ $50, auction_type = 5' q = array([ [60], [36], [36], [36], [36], [36], [30], [10], [20] ]) p = array([ [50], [40], [42], [44], [46], [48], [100], [100], [100] ]) MVAbase, cq, cp, bus, gen, gencost, branch, f, dispatch, success, et = \ runmkt('t_auction_case', q, p, 1150, 100, [], [], mpopt) t_is( cq[G[0]], 35.32, 2, t ) t_is( cq[G[1:6]], q[G[1:6]], 8, [t, ' : gen qtys'] ) t_is( cp[G[0]], 50, 4, t ) t_is( cq[L], q[L], 8, [t, ' : load qtys'] ) t_is( cp[L[1], :], 54.03, 2, t ) t_is( cp[G], bus.lam_P[Gbus], 8, [t, ' : gen prices'] ) Qfudge = zeros(p.shape) Qfudge[L, :] = diag(gen.Qg[L] / gen.Pg[L] * bus.lam_Q[Lbus]) * ones(p[L, :].shape) t_is( cp[L], bus.lam_P[Lbus] + Qfudge[L, 0], 8, [t, ' : load prices'] ) t_end()
def qps_gurobi(H, c, A, l, u, xmin, xmax, x0, opt): """Quadratic Program Solver based on GUROBI. A wrapper function providing a PYPOWER standardized interface for using gurobipy to solve the following QP (quadratic programming) problem: min 1/2 x'*H*x + c'*x x subject to l <= A*x <= u (linear constraints) xmin <= x <= xmax (variable bounds) Inputs (all optional except H, c, A and l): H : matrix (possibly sparse) of quadratic cost coefficients c : vector of linear cost coefficients A, l, u : define the optional linear constraints. Default values for the elements of l and u are -Inf and Inf, respectively. xmin, xmax : optional lower and upper bounds on the C{x} variables, defaults are -Inf and Inf, respectively. x0 : optional starting value of optimization vector C{x} opt : optional options structure with the following fields, all of which are also optional (default values shown in parentheses) verbose (0) - controls level of progress output displayed 0 = no progress output 1 = some progress output 2 = verbose progress output grb_opt - options dict for Gurobi, value in verbose overrides these options problem : The inputs can alternatively be supplied in a single PROBLEM dict with fields corresponding to the input arguments described above: H, c, A, l, u, xmin, xmax, x0, opt Outputs: x : solution vector f : final objective function value exitflag : gurobipy exit flag 1 = converged 0 or negative values = negative of GUROBI_MEX exit flag (see gurobipy documentation for details) output : gurobipy output dict (see gurobipy documentation for details) lmbda : dict containing the Langrange and Kuhn-Tucker multipliers on the constraints, with fields: mu_l - lower (left-hand) limit on linear constraints mu_u - upper (right-hand) limit on linear constraints lower - lower bound on optimization variables upper - upper bound on optimization variables Note the calling syntax is almost identical to that of QUADPROG from MathWorks' Optimization Toolbox. The main difference is that the linear constraints are specified with A, l, u instead of A, b, Aeq, beq. Calling syntax options: x, f, exitflag, output, lmbda = ... qps_gurobi(H, c, A, l, u, xmin, xmax, x0, opt) r = qps_gurobi(H, c, A, l, u) r = qps_gurobi(H, c, A, l, u, xmin, xmax) r = qps_gurobi(H, c, A, l, u, xmin, xmax, x0) r = qps_gurobi(H, c, A, l, u, xmin, xmax, x0, opt) r = qps_gurobi(problem), where problem is a dict with fields: H, c, A, l, u, xmin, xmax, x0, opt all fields except 'c', 'A' and 'l' or 'u' are optional Example: (problem from from http://www.jmu.edu/docs/sasdoc/sashtml/iml/chap8/sect12.htm) H = [ 1003.1 4.3 6.3 5.9; 4.3 2.2 2.1 3.9; 6.3 2.1 3.5 4.8; 5.9 3.9 4.8 10 ] c = zeros((4, 1)) A = [ [1 1 1 1] [0.17 0.11 0.10 0.18] ] l = [1; 0.10] u = [1; Inf] xmin = zeros((4, 1)) x0 = [1; 0; 0; 1] opt = {'verbose': 2} x, f, s, out, lmbda = qps_gurobi(H, c, A, l, u, xmin, [], x0, opt) @see: L{gurobipy}. """ import gurobipy ##----- input argument handling ----- ## gather inputs if isinstance(H, dict): ## problem struct p = H if 'opt' in p: opt = p['opt'] if 'x0' in p: x0 = p['x0'] if 'xmax' in p: xmax = p['xmax'] if 'xmin' in p: xmin = p['xmin'] if 'u' in p: u = p['u'] if 'l' in p: l = p['l'] if 'A' in p: A = p['A'] if 'c' in p: c = p['c'] if 'H' in p: H = p['H'] else: ## individual args assert H is not None assert c is not None assert A is not None assert l is not None if opt is None: opt = {} # if x0 is None: # x0 = array([]) # if xmax is None: # xmax = array([]) # if xmin is None: # xmin = array([]) ## define nx, set default values for missing optional inputs if len(H) == 0 or not any(any(H)): if len(A) == 0 and len(xmin) == 0 and len(xmax) == 0: stderr.write('qps_gurobi: LP problem must include constraints or variable bounds\n') else: if len(A) > 0: nx = shape(A)[1] elif len(xmin) > 0: nx = len(xmin) else: # if len(xmax) > 0 nx = len(xmax) H = sparse((nx, nx)) else: nx = shape(H)[0] if len(c) == 0: c = zeros(nx) if len(A) > 0 and (len(l) == 0 or all(l == -Inf)) and \ (len(u) == 0 or all(u == Inf)): A = None ## no limits => no linear constraints nA = shape(A)[0] ## number of original linear constraints if nA: if len(u) == 0: ## By default, linear inequalities are ... u = Inf * ones(nA) ## ... unbounded above and ... if len(l) == 0: l = -Inf * ones(nA) ## ... unbounded below. if len(x0) == 0: x0 = zeros(nx) ## default options if 'verbose' in opt: verbose = opt['verbose'] else: verbose = 0 # if 'max_it' in opt: # max_it = opt['max_it'] # else: # max_it = 0 ## set up options struct for Gurobi if 'grb_opt' in opt: g_opt = gurobi_options(opt['grb_opt']) else: g_opt = gurobi_options() g_opt['Display'] = min(verbose, 3) if verbose: g_opt['DisplayInterval'] = 1 else: g_opt['DisplayInterval'] = Inf if not issparse(A): A = sparse(A) ## split up linear constraints ieq = find( abs(u-l) <= EPS ) ## equality igt = find( u >= 1e10 & l > -1e10 ) ## greater than, unbounded above ilt = find( l <= -1e10 & u < 1e10 ) ## less than, unbounded below ibx = find( (abs(u-l) > EPS) & (u < 1e10) & (l > -1e10) ) ## grab some dimensions nlt = len(ilt) ## number of upper bounded linear inequalities ngt = len(igt) ## number of lower bounded linear inequalities nbx = len(ibx) ## number of doubly bounded linear inequalities neq = len(ieq) ## number of equalities niq = nlt + ngt + 2 * nbx ## number of inequalities AA = [ A[ieq, :], A[ilt, :], -A[igt, :], A[ibx, :], -A[ibx, :] ] bb = [ u[ieq], u[ilt], -l[igt], u[ibx], -l[ibx] ] contypes = '=' * neq + '<' * niq ## call the solver if len(H) == 0 or not any(any(H)): lpqp = 'LP' else: lpqp = 'QP' rr, cc, vv = find(H) g_opt['QP']['qrow'] = int(rr.T - 1) g_opt['QP']['qcol'] = int(cc.T - 1) g_opt['QP']['qval'] = 0.5 * vv.T if verbose: methods = [ 'primal simplex', 'dual simplex', 'interior point', 'concurrent', 'deterministic concurrent' ] print('Gurobi Version %s -- %s %s solver\n' '<unknown>' % (methods[g_opt['Method'] + 1], lpqp)) x, f, eflag, output, lmbda = \ gurobipy(c.T, 1, AA, bb, contypes, xmin, xmax, 'C', g_opt) pi = lmbda['Pi'] rc = lmbda['RC'] output['flag'] = eflag if eflag == 2: eflag = 1 ## optimal solution found else: eflag = -eflag ## failed somehow ## check for empty results (in case optimization failed) lam = {} if len(x) == 0: x = NaN(nx, 1); lam['lower'] = NaN(nx) lam['upper'] = NaN(nx) else: lam['lower'] = zeros(nx) lam['upper'] = zeros(nx) if len(f) == 0: f = NaN if len(pi) == 0: pi = NaN(len(bb)) kl = find(rc > 0); ## lower bound binding ku = find(rc < 0); ## upper bound binding lam['lower'][kl] = rc[kl] lam['upper'][ku] = -rc[ku] lam['eqlin'] = pi[:neq + 1] lam['ineqlin'] = pi[neq + range(niq + 1)] mu_l = zeros(nA) mu_u = zeros(nA) ## repackage lmbdas kl = find(lam['eqlin'] > 0) ## lower bound binding ku = find(lam['eqlin'] < 0) ## upper bound binding mu_l[ieq[kl]] = lam['eqlin'][kl] mu_l[igt] = -lam['ineqlin'][nlt + range(ngt + 1)] mu_l[ibx] = -lam['ineqlin'][nlt + ngt + nbx + range(nbx)] mu_u[ieq[ku]] = -lam['eqlin'][ku] mu_u[ilt] = -lam['ineqlin'][:nlt + 1] mu_u[ibx] = -lam['ineqlin'][nlt + ngt + range(nbx + 1)] lmbda = { 'mu_l': mu_l, 'mu_u': mu_u, 'lower': lam['lower'], 'upper': lam['upper'] } return x, f, eflag, output, lmbda
def t_dcline(quiet=False): """Tests for DC line extension in L{{toggle_dcline}. @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln """ num_tests = 50 t_begin(num_tests, quiet) tdir = dirname(__file__) casefile = join(tdir, 't_case9_dcline') if quiet: verbose = False else: verbose = False t0 = '' ppopt = ppoption(OPF_VIOLATION=1e-6, PDIPM_GRADTOL=1e-8, PDIPM_COMPTOL=1e-8, PDIPM_COSTTOL=1e-9) ppopt = ppoption(ppopt, OPF_ALG=560, OPF_ALG_DC=200) ppopt = ppoption(ppopt, OUT_ALL=0, VERBOSE=verbose) ## set up indices ib_data = r_[arange(BUS_AREA + 1), arange(BASE_KV, VMIN + 1)] ib_voltage = arange(VM, VA + 1) ib_lam = arange(LAM_P, LAM_Q + 1) ib_mu = arange(MU_VMAX, MU_VMIN + 1) ig_data = r_[[GEN_BUS, QMAX, QMIN], arange(MBASE, APF + 1)] ig_disp = array([PG, QG, VG]) ig_mu = arange(MU_PMAX, MU_QMIN + 1) ibr_data = arange(ANGMAX + 1) ibr_flow = arange(PF, QT + 1) ibr_mu = array([MU_SF, MU_ST]) ibr_angmu = array([MU_ANGMIN, MU_ANGMAX]) ## load case ppc0 = loadcase(casefile) del ppc0['dclinecost'] ppc = ppc0 ppc = toggle_dcline(ppc, 'on') ppc = toggle_dcline(ppc, 'off') ndc = ppc['dcline'].shape[0] ## run AC OPF w/o DC lines t = ''.join([t0, 'AC OPF (no DC lines) : ']) r0 = runopf(ppc0, ppopt) success = r0['success'] t_ok(success, [t, 'success']) r = runopf(ppc, ppopt) success = r['success'] t_ok(success, [t, 'success']) t_is(r['f'], r0['f'], 8, [t, 'f']) t_is( r['bus'][:,ib_data ], r0['bus'][:,ib_data ], 10, [t, 'bus data']) t_is( r['bus'][:,ib_voltage], r0['bus'][:,ib_voltage], 3, [t, 'bus voltage']) t_is( r['bus'][:,ib_lam ], r0['bus'][:,ib_lam ], 3, [t, 'bus lambda']) t_is( r['bus'][:,ib_mu ], r0['bus'][:,ib_mu ], 2, [t, 'bus mu']) t_is( r['gen'][:,ig_data ], r0['gen'][:,ig_data ], 10, [t, 'gen data']) t_is( r['gen'][:,ig_disp ], r0['gen'][:,ig_disp ], 3, [t, 'gen dispatch']) t_is( r['gen'][:,ig_mu ], r0['gen'][:,ig_mu ], 3, [t, 'gen mu']) t_is(r['branch'][:,ibr_data ], r0['branch'][:,ibr_data ], 10, [t, 'branch data']) t_is(r['branch'][:,ibr_flow ], r0['branch'][:,ibr_flow ], 3, [t, 'branch flow']) t_is(r['branch'][:,ibr_mu ], r0['branch'][:,ibr_mu ], 2, [t, 'branch mu']) t = ''.join([t0, 'AC PF (no DC lines) : ']) ppc1 = {'baseMVA': r['baseMVA'], 'bus': r['bus'][:, :VMIN + 1].copy(), 'gen': r['gen'][:, :APF + 1].copy(), 'branch': r['branch'][:, :ANGMAX + 1].copy(), 'gencost': r['gencost'].copy(), 'dcline': r['dcline'][:, :c.LOSS1 + 1].copy()} ppc1['bus'][:, VM] = 1 ppc1['bus'][:, VA] = 0 rp = runpf(ppc1, ppopt) success = rp['success'] t_ok(success, [t, 'success']) t_is( rp['bus'][:,ib_voltage], r['bus'][:,ib_voltage], 3, [t, 'bus voltage']) t_is( rp['gen'][:,ig_disp ], r['gen'][:,ig_disp ], 3, [t, 'gen dispatch']) t_is(rp['branch'][:,ibr_flow ], r['branch'][:,ibr_flow ], 3, [t, 'branch flow']) ## run with DC lines t = ''.join([t0, 'AC OPF (with DC lines) : ']) ppc = toggle_dcline(ppc, 'on') r = runopf(ppc, ppopt) success = r['success'] t_ok(success, [t, 'success']) expected = array([ [10, 8.9, -10, 10, 1.0674, 1.0935], [2.2776, 2.2776, 0, 0, 1.0818, 1.0665], [0, 0, 0, 0, 1.0000, 1.0000], [10, 9.5, 0.0563, -10, 1.0778, 1.0665] ]) t_is(r['dcline'][:, c.PF:c.VT + 1], expected, 4, [t, 'P Q V']) expected = array([ [0, 0.8490, 0.6165, 0, 0, 0.2938], [0, 0, 0, 0.4290, 0.0739, 0], [0, 0, 0, 0, 0, 0], [0, 7.2209, 0, 0, 0.0739, 0] ]) t_is(r['dcline'][:, c.MU_PMIN:c.MU_QMAXT + 1], expected, 3, [t, 'mu']) t = ''.join([t0, 'AC PF (with DC lines) : ']) ppc1 = {'baseMVA': r['baseMVA'], 'bus': r['bus'][:, :VMIN + 1].copy(), 'gen': r['gen'][:, :APF + 1].copy(), 'branch': r['branch'][:, :ANGMAX + 1].copy(), 'gencost': r['gencost'].copy(), 'dcline': r['dcline'][:, :c.LOSS1 + 1].copy()} ppc1 = toggle_dcline(ppc1, 'on') ppc1['bus'][:, VM] = 1 ppc1['bus'][:, VA] = 0 rp = runpf(ppc1, ppopt) success = rp['success'] t_ok(success, [t, 'success']) t_is( rp['bus'][:,ib_voltage], r['bus'][:,ib_voltage], 3, [t, 'bus voltage']) #t_is( rp['gen'][:,ig_disp ], r['gen'][:,ig_disp ], 3, [t, 'gen dispatch']) t_is( rp['gen'][:2,ig_disp ], r['gen'][:2,ig_disp ], 3, [t, 'gen dispatch']) t_is( rp['gen'][2,PG ], r['gen'][2,PG ], 3, [t, 'gen dispatch']) t_is( rp['gen'][2,QG]+rp['dcline'][0,c.QF], r['gen'][2,QG]+r['dcline'][0,c.QF], 3, [t, 'gen dispatch']) t_is(rp['branch'][:,ibr_flow ], r['branch'][:,ibr_flow ], 3, [t, 'branch flow']) ## add appropriate P and Q injections and check angles and generation when running PF t = ''.join([t0, 'AC PF (with equivalent injections) : ']) ppc1 = {'baseMVA': r['baseMVA'], 'bus': r['bus'][:, :VMIN + 1].copy(), 'gen': r['gen'][:, :APF + 1].copy(), 'branch': r['branch'][:, :ANGMAX + 1].copy(), 'gencost': r['gencost'].copy(), 'dcline': r['dcline'][:, :c.LOSS1 + 1].copy()} ppc1['bus'][:, VM] = 1 ppc1['bus'][:, VA] = 0 for k in range(ndc): if ppc1['dcline'][k, c.BR_STATUS]: ff = find(ppc1['bus'][:, BUS_I] == ppc1['dcline'][k, c.F_BUS]) tt = find(ppc1['bus'][:, BUS_I] == ppc1['dcline'][k, c.T_BUS]) ppc1['bus'][ff, PD] = ppc1['bus'][ff, PD] + r['dcline'][k, c.PF] ppc1['bus'][ff, QD] = ppc1['bus'][ff, QD] - r['dcline'][k, c.QF] ppc1['bus'][tt, PD] = ppc1['bus'][tt, PD] - r['dcline'][k, c.PT] ppc1['bus'][tt, QD] = ppc1['bus'][tt, QD] - r['dcline'][k, c.QT] ppc1['bus'][ff, VM] = r['dcline'][k, c.VF] ppc1['bus'][tt, VM] = r['dcline'][k, c.VT] ppc1['bus'][ff, BUS_TYPE] = PV ppc1['bus'][tt, BUS_TYPE] = PV rp = runpf(ppc1, ppopt) success = rp['success'] t_ok(success, [t, 'success']) t_is( rp['bus'][:,ib_voltage], r['bus'][:,ib_voltage], 3, [t, 'bus voltage']) t_is( rp['gen'][:,ig_disp ], r['gen'][:,ig_disp ], 3, [t, 'gen dispatch']) t_is(rp['branch'][:,ibr_flow ], r['branch'][:,ibr_flow ], 3, [t, 'branch flow']) ## test DC OPF t = ''.join([t0, 'DC OPF (with DC lines) : ']) ppc = ppc0.copy() ppc['gen'][0, PMIN] = 10 ppc['branch'][4, RATE_A] = 100 ppc = toggle_dcline(ppc, 'on') r = rundcopf(ppc, ppopt) success = r['success'] t_ok(success, [t, 'success']) expected = array([ [10, 8.9, 0, 0, 1.01, 1], [2, 2, 0, 0, 1, 1], [0, 0, 0, 0, 1, 1], [10, 9.5, 0, 0, 1, 0.98] ]) t_is(r['dcline'][:, c.PF:c.VT + 1], expected, 4, [t, 'P Q V']) expected = array([ [0, 1.8602, 0, 0, 0, 0], [1.8507, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0.2681, 0, 0, 0, 0] ]) t_is(r['dcline'][:, c.MU_PMIN:c.MU_QMAXT + 1], expected, 3, [t, 'mu']) t = ''.join([t0, 'DC PF (with DC lines) : ']) ppc1 = {'baseMVA': r['baseMVA'], 'bus': r['bus'][:, :VMIN + 1].copy(), 'gen': r['gen'][:, :APF + 1].copy(), 'branch': r['branch'][:, :ANGMAX + 1].copy(), 'gencost': r['gencost'].copy(), 'dcline': r['dcline'][:, :c.LOSS1 + 1].copy()} ppc1 = toggle_dcline(ppc1, 'on') ppc1['bus'][:, VA] = 0 rp = rundcpf(ppc1, ppopt) success = rp['success'] t_ok(success, [t, 'success']) t_is( rp['bus'][:,ib_voltage], r['bus'][:,ib_voltage], 3, [t, 'bus voltage']) t_is( rp['gen'][:,ig_disp ], r['gen'][:,ig_disp ], 3, [t, 'gen dispatch']) t_is(rp['branch'][:,ibr_flow ], r['branch'][:,ibr_flow ], 3, [t, 'branch flow']) ## add appropriate P injections and check angles and generation when running PF t = ''.join([t0, 'DC PF (with equivalent injections) : ']) ppc1 = {'baseMVA': r['baseMVA'], 'bus': r['bus'][:, :VMIN + 1].copy(), 'gen': r['gen'][:, :APF + 1].copy(), 'branch': r['branch'][:, :ANGMAX + 1].copy(), 'gencost': r['gencost'].copy(), 'dcline': r['dcline'][:, :c.LOSS1 + 1].copy()} ppc1['bus'][:, VA] = 0 for k in range(ndc): if ppc1['dcline'][k, c.BR_STATUS]: ff = find(ppc1['bus'][:, BUS_I] == ppc1['dcline'][k, c.F_BUS]) tt = find(ppc1['bus'][:, BUS_I] == ppc1['dcline'][k, c.T_BUS]) ppc1['bus'][ff, PD] = ppc1['bus'][ff, PD] + r['dcline'][k, c.PF] ppc1['bus'][tt, PD] = ppc1['bus'][tt, PD] - r['dcline'][k, c.PT] ppc1['bus'][ff, BUS_TYPE] = PV ppc1['bus'][tt, BUS_TYPE] = PV rp = rundcpf(ppc1, ppopt) success = rp['success'] t_ok(success, [t, 'success']) t_is( rp['bus'][:,ib_voltage], r['bus'][:,ib_voltage], 3, [t, 'bus voltage']) t_is( rp['gen'][:,ig_disp ], r['gen'][:,ig_disp ], 3, [t, 'gen dispatch']) t_is(rp['branch'][:,ibr_flow ], r['branch'][:,ibr_flow ], 3, [t, 'branch flow']) ## run with DC lines t = ''.join([t0, 'AC OPF (with DC lines + poly cost) : ']) ppc = loadcase(casefile) ppc = toggle_dcline(ppc, 'on') r = runopf(ppc, ppopt) success = r['success'] t_ok(success, [t, 'success']) expected1 = array([ [10, 8.9, -10, 10, 1.0663, 1.0936], [7.8429, 7.8429, 0, 0, 1.0809, 1.0667], [0, 0, 0, 0, 1.0000, 1.0000], [6.0549, 5.7522, -0.5897, -10, 1.0778, 1.0667] ]) t_is(r['dcline'][:, c.PF:c.VT + 1], expected1, 4, [t, 'P Q V']) expected2 = array([ [0, 0.7605, 0.6226, 0, 0, 0.2980], [0, 0, 0, 0.4275, 0.0792, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0.0792, 0] ]) t_is(r['dcline'][:, c.MU_PMIN:c.MU_QMAXT + 1], expected2, 3, [t, 'mu']) ppc['dclinecost'][3, :8] = array([2, 0, 0, 4, 0, 0, 7.3, 0]) r = runopf(ppc, ppopt) success = r['success'] t_ok(success, [t, 'success']) t_is(r['dcline'][:, c.PF:c.VT + 1], expected1, 4, [t, 'P Q V']) t_is(r['dcline'][:, c.MU_PMIN:c.MU_QMAXT + 1], expected2, 3, [t, 'mu']) t = ''.join([t0, 'AC OPF (with DC lines + pwl cost) : ']) ppc['dclinecost'][3, :8] = array([1, 0, 0, 2, 0, 0, 10, 73]) r = runopf(ppc, ppopt) success = r['success'] t_ok(success, [t, 'success']) t_is(r['dcline'][:, c.PF:c.VT + 1], expected1, 4, [t, 'P Q V']) t_is(r['dcline'][:, c.MU_PMIN:c.MU_QMAXT + 1], expected2, 3, [t, 'mu']) t_end()
def dcopf_solver(om, ppopt, out_opt=None): """Solves a DC optimal power flow. Inputs are an OPF model object, a PYPOWER options dict and a dict containing fields (can be empty) for each of the desired optional output fields. Outputs are a C{results} dict, C{success} flag and C{raw} output dict. C{results} is a PYPOWER case dict (ppc) with the usual baseMVA, bus branch, gen, gencost fields, along with the following additional fields: - C{order} see 'help ext2int' for details of this field - C{x} final value of optimization variables (internal order) - C{f} final objective function value - C{mu} shadow prices on ... - C{var} - C{l} lower bounds on variables - C{u} upper bounds on variables - C{lin} - C{l} lower bounds on linear constraints - C{u} upper bounds on linear constraints - C{g} (optional) constraint values - C{dg} (optional) constraint 1st derivatives - C{df} (optional) obj fun 1st derivatives (not yet implemented) - C{d2f} (optional) obj fun 2nd derivatives (not yet implemented) C{success} is C{True} if solver converged successfully, C{False} otherwise. C{raw} is a raw output dict in form returned by MINOS - C{xr} final value of optimization variables - C{pimul} constraint multipliers - C{info} solver specific termination code - C{output} solver specific output information @see: L{opf}, L{qps_pypower} @author: Ray Zimmerman (PSERC Cornell) @author: Carlos E. Murillo-Sanchez (PSERC Cornell & Universidad Autonoma de Manizales) """ if out_opt is None: out_opt = {} ## options verbose = ppopt['VERBOSE'] alg = ppopt['OPF_ALG_DC'] if alg == 0: if have_fcn('cplex'): ## use CPLEX by default, if available alg = 500 elif have_fcn('mosek'): ## if not, then MOSEK, if available alg = 600 elif have_fcn('gurobi'): ## if not, then Gurobi, if available alg = 700 else: ## otherwise PIPS alg = 200 ## unpack data ppc = om.get_ppc() baseMVA, bus, gen, branch, gencost = \ ppc["baseMVA"], ppc["bus"], ppc["gen"], ppc["branch"], ppc["gencost"] cp = om.get_cost_params() N, H, Cw = cp["N"], cp["H"], cp["Cw"] fparm = array(c_[cp["dd"], cp["rh"], cp["kk"], cp["mm"]]) Bf = om.userdata('Bf') Pfinj = om.userdata('Pfinj') vv, ll, _, _ = om.get_idx() ## problem dimensions ipol = find(gencost[:, MODEL] == POLYNOMIAL) ## polynomial costs ipwl = find(gencost[:, MODEL] == PW_LINEAR) ## piece-wise linear costs nb = bus.shape[0] ## number of buses nl = branch.shape[0] ## number of branches nw = N.shape[0] ## number of general cost vars, w ny = om.getN('var', 'y') ## number of piece-wise linear costs nxyz = om.getN('var') ## total number of control vars of all types ## linear constraints & variable bounds A, l, u = om.linear_constraints() x0, xmin, xmax = om.getv() ## set up objective function of the form: f = 1/2 * X'*HH*X + CC'*X ## where X = [x;y;z]. First set up as quadratic function of w, ## f = 1/2 * w'*HHw*w + CCw'*w, where w = diag(M) * (N*X - Rhat). We ## will be building on the (optionally present) user supplied parameters. ## piece-wise linear costs any_pwl = int(ny > 0) if any_pwl: # Sum of y vars. Npwl = sparse((ones(ny), (zeros(ny), arange(vv["i1"]["y"], vv["iN"]["y"]))), (1, nxyz)) Hpwl = sparse((1, 1)) Cpwl = array([1]) fparm_pwl = array([[1, 0, 0, 1]]) else: Npwl = None#zeros((0, nxyz)) Hpwl = None#array([]) Cpwl = array([]) fparm_pwl = zeros((0, 4)) ## quadratic costs npol = len(ipol) if any(find(gencost[ipol, NCOST] > 3)): stderr.write('DC opf cannot handle polynomial costs with higher ' 'than quadratic order.\n') iqdr = find(gencost[ipol, NCOST] == 3) ilin = find(gencost[ipol, NCOST] == 2) polycf = zeros((npol, 3)) ## quadratic coeffs for Pg if len(iqdr) > 0: polycf[iqdr, :] = gencost[ipol[iqdr], COST:COST + 3] if npol: polycf[ilin, 1:3] = gencost[ipol[ilin], COST:COST + 2] polycf = dot(polycf, diag([ baseMVA**2, baseMVA, 1])) ## convert to p.u. if npol: Npol = sparse((ones(npol), (arange(npol), vv["i1"]["Pg"] + ipol)), (npol, nxyz)) # Pg vars Hpol = sparse((2 * polycf[:, 0], (arange(npol), arange(npol))), (npol, npol)) else: Npol = None Hpol = None Cpol = polycf[:, 1] fparm_pol = ones((npol, 1)) * array([[1, 0, 0, 1]]) ## combine with user costs NN = vstack([n for n in [Npwl, Npol, N] if n is not None and n.shape[0] > 0], "csr") # FIXME: Zero dimension sparse matrices. if (Hpwl is not None) and any_pwl and (npol + nw): Hpwl = hstack([Hpwl, sparse((any_pwl, npol + nw))]) if Hpol is not None: if any_pwl and npol: Hpol = hstack([sparse((npol, any_pwl)), Hpol]) if npol and nw: Hpol = hstack([Hpol, sparse((npol, nw))]) if (H is not None) and nw and (any_pwl + npol): H = hstack([sparse((nw, any_pwl + npol)), H]) HHw = vstack([h for h in [Hpwl, Hpol, H] if h is not None and h.shape[0] > 0], "csr") CCw = r_[Cpwl, Cpol, Cw] ffparm = r_[fparm_pwl, fparm_pol, fparm] ## transform quadratic coefficients for w into coefficients for X nnw = any_pwl + npol + nw M = sparse((ffparm[:, 3], (range(nnw), range(nnw)))) MR = M * ffparm[:, 1] HMR = HHw * MR MN = M * NN HH = MN.T * HHw * MN CC = MN.T * (CCw - HMR) C0 = 0.5 * dot(MR, HMR) + sum(polycf[:, 2]) # Constant term of cost. ## set up input for QP solver opt = {'alg': alg, 'verbose': verbose} if (alg == 200) or (alg == 250): ## try to select an interior initial point Varefs = bus[bus[:, BUS_TYPE] == REF, VA] * (pi / 180.0) lb, ub = xmin.copy(), xmax.copy() lb[xmin == -Inf] = -1e10 ## replace Inf with numerical proxies ub[xmax == Inf] = 1e10 x0 = (lb + ub) / 2; # angles set to first reference angle x0[vv["i1"]["Va"]:vv["iN"]["Va"]] = Varefs[0] if ny > 0: ipwl = find(gencost[:, MODEL] == PW_LINEAR) # largest y-value in CCV data c = gencost.flatten('F')[sub2ind(gencost.shape, ipwl, NCOST + 2 * gencost[ipwl, NCOST])] x0[vv["i1"]["y"]:vv["iN"]["y"]] = max(c) + 0.1 * abs(max(c)) ## set up options feastol = ppopt['PDIPM_FEASTOL'] gradtol = ppopt['PDIPM_GRADTOL'] comptol = ppopt['PDIPM_COMPTOL'] costtol = ppopt['PDIPM_COSTTOL'] max_it = ppopt['PDIPM_MAX_IT'] max_red = ppopt['SCPDIPM_RED_IT'] if feastol == 0: feastol = ppopt['OPF_VIOLATION'] ## = OPF_VIOLATION by default opt["pips_opt"] = { 'feastol': feastol, 'gradtol': gradtol, 'comptol': comptol, 'costtol': costtol, 'max_it': max_it, 'max_red': max_red, 'cost_mult': 1 } elif alg == 400: opt['ipopt_opt'] = ipopt_options([], ppopt) elif alg == 500: opt['cplex_opt'] = cplex_options([], ppopt) elif alg == 600: opt['mosek_opt'] = mosek_options([], ppopt) elif alg == 700: opt['grb_opt'] = gurobi_options([], ppopt) else: raise ValueError("Unrecognised solver [%d]." % alg) ##----- run opf ----- x, f, info, output, lmbda = \ qps_pypower(HH, CC, A, l, u, xmin, xmax, x0, opt) success = (info == 1) ##----- calculate return values ----- if not any(isnan(x)): ## update solution data Va = x[vv["i1"]["Va"]:vv["iN"]["Va"]] Pg = x[vv["i1"]["Pg"]:vv["iN"]["Pg"]] f = f + C0 ## update voltages & generator outputs bus[:, VA] = Va * 180 / pi gen[:, PG] = Pg * baseMVA ## compute branch flows branch[:, [QF, QT]] = zeros((nl, 2)) branch[:, PF] = (Bf * Va + Pfinj) * baseMVA branch[:, PT] = -branch[:, PF] ## package up results mu_l = lmbda["mu_l"] mu_u = lmbda["mu_u"] muLB = lmbda["lower"] muUB = lmbda["upper"] ## update Lagrange multipliers il = find((branch[:, RATE_A] != 0) & (branch[:, RATE_A] < 1e10)) bus[:, [LAM_P, LAM_Q, MU_VMIN, MU_VMAX]] = zeros((nb, 4)) gen[:, [MU_PMIN, MU_PMAX, MU_QMIN, MU_QMAX]] = zeros((gen.shape[0], 4)) branch[:, [MU_SF, MU_ST]] = zeros((nl, 2)) bus[:, LAM_P] = (mu_u[ll["i1"]["Pmis"]:ll["iN"]["Pmis"]] - mu_l[ll["i1"]["Pmis"]:ll["iN"]["Pmis"]]) / baseMVA branch[il, MU_SF] = mu_u[ll["i1"]["Pf"]:ll["iN"]["Pf"]] / baseMVA branch[il, MU_ST] = mu_u[ll["i1"]["Pt"]:ll["iN"]["Pt"]] / baseMVA gen[:, MU_PMIN] = muLB[vv["i1"]["Pg"]:vv["iN"]["Pg"]] / baseMVA gen[:, MU_PMAX] = muUB[vv["i1"]["Pg"]:vv["iN"]["Pg"]] / baseMVA pimul = r_[ mu_l - mu_u, -ones(int(ny > 0)), ## dummy entry corresponding to linear cost row in A muLB - muUB ] mu = { 'var': {'l': muLB, 'u': muUB}, 'lin': {'l': mu_l, 'u': mu_u} } results = deepcopy(ppc) results["bus"], results["branch"], results["gen"], \ results["om"], results["x"], results["mu"], results["f"] = \ bus, branch, gen, om, x, mu, f raw = {'xr': x, 'pimul': pimul, 'info': info, 'output': output} return results, success, raw