Package pypower :: Module opf_model
[hide private]
[frames] | no frames]

Source Code for Module pypower.opf_model

  1  # Copyright (C) 1996-2011 Power System Engineering Research Center (PSERC) 
  2  # Copyright (C) 2011 Richard Lincoln 
  3  # 
  4  # PYPOWER is free software: you can redistribute it and/or modify 
  5  # it under the terms of the GNU General Public License as published 
  6  # by the Free Software Foundation, either version 3 of the License, 
  7  # or (at your option) any later version. 
  8  # 
  9  # PYPOWER is distributed in the hope that it will be useful, 
 10  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 11  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
 12  # GNU General Public License for more details. 
 13  # 
 14  # You should have received a copy of the GNU General Public License 
 15  # along with PYPOWER. If not, see <http://www.gnu.org/licenses/>. 
 16   
 17  """Implements the OPF model object used to encapsulate a given OPF 
 18  problem formulation. 
 19  """ 
 20   
 21  from sys import stderr 
 22   
 23  from numpy import array, zeros, ones, Inf, dot, arange, r_ 
 24  from numpy import flatnonzero as find 
 25  from scipy.sparse import lil_matrix, csr_matrix as sparse 
 26   
 27   
28 -class opf_model(object):
29 """This class implements the OPF model object used to encapsulate 30 a given OPF problem formulation. It allows for access to optimization 31 variables, constraints and costs in named blocks, keeping track of the 32 ordering and indexing of the blocks as variables, constraints and costs 33 are added to the problem. 34 35 @author: Ray Zimmerman (PSERC Cornell) 36 @author: Richard Lincoln 37 """ 38
39 - def __init__(self, ppc):
40 #: PYPOWER case dict used to build the object. 41 self.ppc = ppc 42 43 #: data for optimization variable sets that make up the 44 # full optimization variable x 45 self.var = { 46 'idx': { 47 'i1': {}, ## starting index within x 48 'iN': {}, ## ending index within x 49 'N': {} ## number of elements in this variable set 50 }, 51 'N': 0, ## total number of elements in x 52 'NS': 0, ## number of variable sets or named blocks 53 'data': { ## bounds and initial value data 54 'v0': {}, ## vector of initial values 55 'vl': {}, ## vector of lower bounds 56 'vu': {}, ## vector of upper bounds 57 }, 58 'order': [] ## list of names for variable blocks in the order they appear in x 59 } 60 61 #: data for nonlinear constraints that make up the 62 # full set of nonlinear constraints ghn(x) 63 self.nln = { 64 'idx': { 65 'i1': {}, ## starting index within ghn(x) 66 'iN': {}, ## ending index within ghn(x) 67 'N': {} ## number of elements in this constraint set 68 }, 69 'N': 0 , ## total number of elements in ghn(x) 70 'NS': 0, ## number of nonlinear constraint sets or named blocks 71 'order': [] ## list of names for nonlinear constraint blocks in the order they appear in ghn(x) 72 } 73 74 #: data for linear constraints that make up the 75 # full set of linear constraints ghl(x) 76 self.lin = { 77 'idx': { 78 'i1': {}, ## starting index within ghl(x) 79 'iN': {}, ## ending index within ghl(x) 80 'N': {} ## number of elements in this constraint set 81 }, 82 'N': 0, ## total number of elements in ghl(x) 83 'NS': 0, ## number of linear constraint sets or named blocks 84 'data': { ## data for l <= A*xx <= u linear constraints 85 'A': {}, ## sparse linear constraint matrix 86 'l': {}, ## left hand side vector, bounding A*x below 87 'u': {}, ## right hand side vector, bounding A*x above 88 'vs': {} ## cell array of variable sets that define the xx for this constraint block 89 }, 90 'order': [] ## list of names for linear constraint blocks in the order they appear in ghl(x) 91 } 92 93 #: data for user-defined costs 94 self.cost = { 95 'idx': { 96 'i1': {}, ## starting row index within full N matrix 97 'iN': {}, ## ending row index within full N matrix 98 'N': {} ## number of rows in this cost block in full N matrix 99 }, 100 'N': 0, ## total number of rows in full N matrix 101 'NS': 0, ## number of cost blocks 102 'data': { ## data for each user-defined cost block 103 'N': {}, ## see help for add_costs() for details 104 'H': {}, ## " 105 'Cw': {}, ## " 106 'dd': {}, ## " 107 'rh': {}, ## " 108 'kk': {}, ## " 109 'mm': {}, ## " 110 'vs': {} ## list of variable sets that define xx for this cost block, where the N for this block multiplies xx' 111 }, 112 'order': [] ## of names for cost blocks in the order they appear in the rows of the full N matrix 113 } 114 115 self.user_data = {}
116 117
118 - def __repr__(self):
119 """String representation of the object. 120 """ 121 s = '' 122 if self.var['NS']: 123 s += '\n%-22s %5s %8s %8s %8s\n' % ('VARIABLES', 'name', 'i1', 'iN', 'N') 124 s += '%-22s %5s %8s %8s %8s\n' % ('=========', '------', '-----', '-----', '------') 125 for k in range(self.var['NS']): 126 name = self.var['order'][k] 127 idx = self.var['idx'] 128 s += '%15d:%12s %8d %8d %8d\n' % (k, name, idx['i1'][name], idx['iN'][name], idx['N'][name]) 129 130 s += '%15s%31s\n' % (('var[\'NS\'] = %d' % self.var['NS']), ('var[\'N\'] = %d' % self.var['N'])) 131 s += '\n' 132 else: 133 s += '%s : <none>\n', 'VARIABLES' 134 135 if self.nln['NS']: 136 s += '\n%-22s %5s %8s %8s %8s\n' % ('NON-LINEAR CONSTRAINTS', 'name', 'i1', 'iN', 'N') 137 s += '%-22s %5s %8s %8s %8s\n' % ('======================', '------', '-----', '-----', '------') 138 for k in range(self.nln['NS']): 139 name = self.nln['order'][k] 140 idx = self.nln['idx'] 141 s += '%15d:%12s %8d %8d %8d\n' % (k, name, idx['i1'][name], idx['iN'][name], idx['N'][name]) 142 143 s += '%15s%31s\n' % (('nln.NS = %d' % self.nln['NS']), ('nln.N = %d' % self.nln['N'])) 144 s += '\n' 145 else: 146 s += '%s : <none>\n', 'NON-LINEAR CONSTRAINTS' 147 148 if self.lin['NS']: 149 s += '\n%-22s %5s %8s %8s %8s\n' % ('LINEAR CONSTRAINTS', 'name', 'i1', 'iN', 'N') 150 s += '%-22s %5s %8s %8s %8s\n' % ('==================', '------', '-----', '-----', '------') 151 for k in range(self.lin['NS']): 152 name = self.lin['order'][k] 153 idx = self.lin['idx'] 154 s += '%15d:%12s %8d %8d %8d\n' % (k, name, idx['i1'][name], idx['iN'][name], idx['N'][name]) 155 156 s += '%15s%31s\n' % (('lin.NS = %d' % self.lin['NS']), ('lin.N = %d' % self.lin['N'])) 157 s += '\n' 158 else: 159 s += '%s : <none>\n', 'LINEAR CONSTRAINTS' 160 161 if self.cost['NS']: 162 s += '\n%-22s %5s %8s %8s %8s\n' % ('COSTS', 'name', 'i1', 'iN', 'N') 163 s += '%-22s %5s %8s %8s %8s\n' % ('=====', '------', '-----', '-----', '------') 164 for k in range(self.cost['NS']): 165 name = self.cost['order'][k] 166 idx = self.cost['idx'] 167 s += '%15d:%12s %8d %8d %8d\n' % (k, name, idx['i1'][name], idx['iN'][name], idx['N'][name]) 168 169 s += '%15s%31s\n' % (('cost.NS = %d' % self.cost['NS']), ('cost.N = %d' % self.cost['N'])) 170 s += '\n' 171 else: 172 s += '%s : <none>\n' % 'COSTS' 173 174 #s += ' ppc = ' 175 #if len(self.ppc): 176 # s += '\n' 177 # 178 #s += str(self.ppc) + '\n' 179 180 s += ' userdata = ' 181 if len(self.user_data): 182 s += '\n' 183 184 s += str(self.user_data) 185 186 return s
187 188
189 - def add_constraints(self, name, AorN, l, u=None, varsets=None):
190 """Adds a set of constraints to the model. 191 192 Linear constraints are of the form C{l <= A * x <= u}, where 193 C{x} is a vector made of of the vars specified in C{varsets} (in 194 the order given). This allows the C{A} matrix to be defined only 195 in terms of the relevant variables without the need to manually 196 create a lot of zero columns. If C{varsets} is empty, C{x} is taken 197 to be the full vector of all optimization variables. If C{l} or 198 C{u} are empty, they are assumed to be appropriately sized vectors 199 of C{-Inf} and C{Inf}, respectively. 200 201 For nonlinear constraints, the 3rd argument, C{N}, is the number 202 of constraints in the set. Currently, this is used internally 203 by PYPOWER, but there is no way for the user to specify 204 additional nonlinear constraints. 205 """ 206 if u is None: ## nonlinear 207 ## prevent duplicate named constraint sets 208 if name in self.nln["idx"]["N"]: 209 stderr.write("opf_model.add_constraints: nonlinear constraint set named '%s' already exists\n" % name) 210 211 ## add info about this nonlinear constraint set 212 self.nln["idx"]["i1"][name] = self.nln["N"] #+ 1 ## starting index 213 self.nln["idx"]["iN"][name] = self.nln["N"] + AorN ## ing index 214 self.nln["idx"]["N"][name] = AorN ## number of constraints 215 216 ## update number of nonlinear constraints and constraint sets 217 self.nln["N"] = self.nln["idx"]["iN"][name] 218 self.nln["NS"] = self.nln["NS"] + 1 219 220 ## put name in ordered list of constraint sets 221 # self.nln["order"][self.nln["NS"]] = name 222 self.nln["order"].append(name) 223 else: ## linear 224 ## prevent duplicate named constraint sets 225 if name in self.lin["idx"]["N"]: 226 stderr.write('opf_model.add_constraints: linear constraint set named ''%s'' already exists\n' % name) 227 228 if varsets is None: 229 varsets = [] 230 231 N, M = AorN.shape 232 if len(l) == 0: ## default l is -Inf 233 l = -Inf * ones(N) 234 235 if len(u) == 0: ## default u is Inf 236 u = Inf * ones(N) 237 238 if len(varsets) == 0: 239 varsets = self.var["order"] 240 241 ## check sizes 242 if (l.shape[0] != N) or (u.shape[0] != N): 243 stderr.write('opf_model.add_constraints: sizes of A, l and u must match\n') 244 245 nv = 0 246 for k in range(len(varsets)): 247 nv = nv + self.var["idx"]["N"][varsets[k]] 248 249 if M != nv: 250 stderr.write('opf_model.add_constraints: number of columns of A does not match\nnumber of variables, A is %d x %d, nv = %d\n' % (N, M, nv)) 251 252 ## add info about this linear constraint set 253 self.lin["idx"]["i1"][name] = self.lin["N"] #+ 1 ## starting index 254 self.lin["idx"]["iN"][name] = self.lin["N"] + N ## ing index 255 self.lin["idx"]["N"][name] = N ## number of constraints 256 self.lin["data"]["A"][name] = AorN 257 self.lin["data"]["l"][name] = l 258 self.lin["data"]["u"][name] = u 259 self.lin["data"]["vs"][name] = varsets 260 261 ## update number of vars and var sets 262 self.lin["N"] = self.lin["idx"]["iN"][name] 263 self.lin["NS"] = self.lin["NS"] + 1 264 265 ## put name in ordered list of var sets 266 # self.lin["order"][self.lin["NS"]] = name 267 self.lin["order"].append(name)
268 269
270 - def add_costs(self, name, cp, varsets):
271 """Adds a set of user costs to the model. 272 273 Adds a named block of user-defined costs to the model. Each set is 274 defined by the C{cp} dict described below. All user-defined sets of 275 costs are combined together into a single set of cost parameters in 276 a single C{cp} dict by L{build_cost_params}. This full aggregate set of 277 cost parameters can be retrieved from the model by L{get_cost_params}. 278 279 Let C{x} refer to the vector formed by combining the specified 280 C{varsets}, and C{f_u(x, cp)} be the cost at C{x} corresponding to the 281 cost parameters contained in C{cp}, where C{cp} is a dict with the 282 following fields:: 283 N - nw x nx sparse matrix 284 Cw - nw x 1 vector 285 H - nw x nw sparse matrix (optional, all zeros by default) 286 dd, mm - nw x 1 vectors (optional, all ones by default) 287 rh, kk - nw x 1 vectors (optional, all zeros by default) 288 289 These parameters are used as follows to compute C{f_u(x, CP)}:: 290 291 R = N*x - rh 292 293 / kk(i), R(i) < -kk(i) 294 K(i) = < 0, -kk(i) <= R(i) <= kk(i) 295 \ -kk(i), R(i) > kk(i) 296 297 RR = R + K 298 299 U(i) = / 0, -kk(i) <= R(i) <= kk(i) 300 \ 1, otherwise 301 302 DDL(i) = / 1, dd(i) = 1 303 \ 0, otherwise 304 305 DDQ(i) = / 1, dd(i) = 2 306 \ 0, otherwise 307 308 Dl = diag(mm) * diag(U) * diag(DDL) 309 Dq = diag(mm) * diag(U) * diag(DDQ) 310 311 w = (Dl + Dq * diag(RR)) * RR 312 313 f_u(x, CP) = 1/2 * w'*H*w + Cw'*w 314 """ 315 ## prevent duplicate named cost sets 316 if name in self.cost["idx"]["N"]: 317 stderr.write('opf_model.add_costs: cost set named \'%s\' already exists\n' % name) 318 319 if varsets is None: 320 varsets = [] 321 322 if len(varsets) == 0: 323 varsets = self.var["order"] 324 325 nw, nx = cp["N"].shape 326 327 ## check sizes 328 nv = 0 329 for k in range(len(varsets)): 330 nv = nv + self.var["idx"]["N"][varsets[k]] 331 332 if nx != nv: 333 if nw == 0: 334 cp["N"] = sparse(nw, nx) 335 else: 336 stderr.write('opf_model.add_costs: number of columns in N (%d x %d) does not match\nnumber of variables (%d)\n' % (nw, nx, nv)) 337 338 if cp["Cw"].shape[0] != nw: 339 stderr.write('opf_model.add_costs: number of rows of Cw (%d x %d) and N (%d x %d) must match\n' % (cp["Cw"].shape[0], nw, nx)) 340 341 if 'H' in cp: 342 if (cp["H"].shape[0] != nw) | (cp["H"].shape[1] != nw): 343 stderr.write('opf_model.add_costs: both dimensions of H (%d x %d) must match the number of rows in N (%d x %d)\n' % (cp["H"].shape, nw, nx)) 344 345 if 'dd' in cp: 346 if cp["dd"].shape[0] != nw: 347 stderr.write('opf_model.add_costs: number of rows of dd (%d x %d) and N (%d x %d) must match\n' % (cp["dd"].shape, nw, nx)) 348 349 if 'rh' in cp: 350 if cp["rh"].shape[0] != nw: 351 stderr.write('opf_model.add_costs: number of rows of rh (%d x %d) and N (%d x %d) must match\n' % (cp["rh"].shape, nw, nx)) 352 353 if 'kk' in cp: 354 if cp["kk"].shape[0] != nw: 355 stderr.write('opf_model.add_costs: number of rows of kk (%d x %d) and N (%d x %d) must match\n' % (cp["kk"].shape, nw, nx)) 356 357 if 'mm' in cp: 358 if cp["mm"].shape[0] != nw: 359 stderr.write('opf_model.add_costs: number of rows of mm (%d x %d) and N (%d x %d) must match\n' % (cp["mm"].shape, nw, nx)) 360 361 ## add info about this user cost set 362 self.cost["idx"]["i1"][name] = self.cost["N"] #+ 1 ## starting index 363 self.cost["idx"]["iN"][name] = self.cost["N"] + nw ## ing index 364 self.cost["idx"]["N"][name] = nw ## number of costs (nw) 365 self.cost["data"]["N"][name] = cp["N"] 366 self.cost["data"]["Cw"][name] = cp["Cw"] 367 self.cost["data"]["vs"][name] = varsets 368 if 'H' in cp: 369 self.cost["data"]["H"][name] = cp["H"] 370 371 if 'dd' in cp: 372 self.cost["data"]["dd"]["name"] = cp["dd"] 373 374 if 'rh' in cp: 375 self.cost["data"]["rh"]["name"] = cp["rh"] 376 377 if 'kk' in cp: 378 self.cost["data"]["kk"]["name"] = cp["kk"] 379 380 if 'mm' in cp: 381 self.cost["data"]["mm"]["name"] = cp["mm"] 382 383 ## update number of vars and var sets 384 self.cost["N"] = self.cost["idx"]["iN"][name] 385 self.cost["NS"] = self.cost["NS"] + 1 386 387 ## put name in ordered list of var sets 388 self.cost["order"].append(name)
389 390
391 - def add_vars(self, name, N, v0=None, vl=None, vu=None):
392 """ Adds a set of variables to the model. 393 394 Adds a set of variables to the model, where N is the number of 395 variables in the set, C{v0} is the initial value of those variables, 396 and C{vl} and C{vu} are the lower and upper bounds on the variables. 397 The defaults for the last three arguments, which are optional, 398 are for all values to be initialized to zero (C{v0 = 0}) and unbounded 399 (C{VL = -Inf, VU = Inf}). 400 """ 401 ## prevent duplicate named var sets 402 if name in self.var["idx"]["N"]: 403 stderr.write('opf_model.add_vars: variable set named ''%s'' already exists\n' % name) 404 405 if v0 is None or len(v0) == 0: 406 v0 = zeros(N) ## init to zero by default 407 408 if vl is None or len(vl) == 0: 409 vl = -Inf * ones(N) ## unbounded below by default 410 411 if vu is None or len(vu) == 0: 412 vu = Inf * ones(N) ## unbounded above by default 413 414 415 ## add info about this var set 416 self.var["idx"]["i1"][name] = self.var["N"] #+ 1 ## starting index 417 self.var["idx"]["iN"][name] = self.var["N"] + N ## ing index 418 self.var["idx"]["N"][name] = N ## number of vars 419 self.var["data"]["v0"][name] = v0 ## initial value 420 self.var["data"]["vl"][name] = vl ## lower bound 421 self.var["data"]["vu"][name] = vu ## upper bound 422 423 ## update number of vars and var sets 424 self.var["N"] = self.var["idx"]["iN"][name] 425 self.var["NS"] = self.var["NS"] + 1 426 427 ## put name in ordered list of var sets 428 # self.var["order"][self.var["NS"]] = name 429 self.var["order"].append(name)
430 431
432 - def build_cost_params(self):
433 """Builds and saves the full generalized cost parameters. 434 435 Builds the full set of cost parameters from the individual named 436 sub-sets added via L{add_costs}. Skips the building process if it has 437 already been done, unless a second input argument is present. 438 439 These cost parameters can be retrieved by calling L{get_cost_params} 440 and the user-defined costs evaluated by calling L{compute_cost}. 441 """ 442 ## initialize parameters 443 nw = self.cost["N"] 444 # nnzN = 0 445 # nnzH = 0 446 # for k in range(self.cost["NS"]): 447 # name = self.cost["order"][k] 448 # nnzN = nnzN + nnz(self.cost["data"]["N"][name]) 449 # if name in self.cost["data"]["H"]: 450 # nnzH = nnzH + nnz(self.cost["data"]["H"][name]) 451 452 ## FIXME Zero dimensional sparse matrices 453 N = zeros((nw, self.var["N"])) 454 H = zeros((nw, nw)) ## default => no quadratic term 455 456 Cw = zeros(nw) 457 dd = ones(nw) ## default => linear 458 rh = zeros(nw) ## default => no shift 459 kk = zeros(nw) ## default => no dead zone 460 mm = ones(nw) ## default => no scaling 461 462 ## fill in each piece 463 for k in range(self.cost["NS"]): 464 name = self.cost["order"][k] 465 Nk = self.cost["data"]["N"][name] ## N for kth cost set 466 i1 = self.cost["idx"]["i1"][name] ## starting row index 467 iN = self.cost["idx"]["iN"][name] ## ing row index 468 if self.cost["idx"]["N"][name]: ## non-zero number of rows to add 469 vsl = self.cost["data"]["vs"][name] ## var set list 470 kN = 0 ## initialize last col of Nk used 471 for v in vsl: 472 j1 = self.var["idx"]["i1"][v] ## starting column in N 473 jN = self.var["idx"]["iN"][v] ## ing column in N 474 k1 = kN ## starting column in Nk 475 kN = kN + self.var["idx"]["N"][v] ## ing column in Nk 476 N[i1:iN, j1:jN] = Nk[:, k1:kN].todense() 477 478 Cw[i1:iN] = self.cost["data"]["Cw"][name] 479 if name in self.cost["data"]["H"]: 480 H[i1:iN, i1:iN] = self.cost["data"]["H"][name].todense() 481 482 if name in self.cost["data"]["dd"]: 483 dd[i1:iN] = self.cost["data"]["dd"][name] 484 485 if name in self.cost["data"]["rh"]: 486 rh[i1:iN] = self.cost["data"]["rh"][name] 487 488 if name in self.cost["data"]["kk"]: 489 kk[i1:iN] = self.cost["data"]["kk"][name] 490 491 if name in self.cost["data"]["mm"]: 492 mm[i1:iN] = self.cost["data"]["mm"][name] 493 494 if nw: 495 N = sparse(N) 496 H = sparse(H) 497 498 ## save in object 499 self.cost["params"] = { 500 'N': N, 'Cw': Cw, 'H': H, 'dd': dd, 'rh': rh, 'kk': kk, 'mm': mm }
501 502
503 - def compute_cost(self, x, name=None):
504 """ Computes a user-defined cost. 505 506 Computes the value of a user defined cost, either for all user 507 defined costs or for a named set of costs. Requires calling 508 L{build_cost_params} first to build the full set of parameters. 509 510 Let C{x} be the full set of optimization variables and C{f_u(x, cp)} be 511 the user-defined cost at C{x}, corresponding to the set of cost 512 parameters in the C{cp} dict returned by L{get_cost_params}, where 513 C{cp} is a dict with the following fields:: 514 N - nw x nx sparse matrix 515 Cw - nw x 1 vector 516 H - nw x nw sparse matrix (optional, all zeros by default) 517 dd, mm - nw x 1 vectors (optional, all ones by default) 518 rh, kk - nw x 1 vectors (optional, all zeros by default) 519 520 These parameters are used as follows to compute C{f_u(x, cp)}:: 521 522 R = N*x - rh 523 524 / kk(i), R(i) < -kk(i) 525 K(i) = < 0, -kk(i) <= R(i) <= kk(i) 526 \ -kk(i), R(i) > kk(i) 527 528 RR = R + K 529 530 U(i) = / 0, -kk(i) <= R(i) <= kk(i) 531 \ 1, otherwise 532 533 DDL(i) = / 1, dd(i) = 1 534 \ 0, otherwise 535 536 DDQ(i) = / 1, dd(i) = 2 537 \ 0, otherwise 538 539 Dl = diag(mm) * diag(U) * diag(DDL) 540 Dq = diag(mm) * diag(U) * diag(DDQ) 541 542 w = (Dl + Dq * diag(RR)) * RR 543 544 F_U(X, CP) = 1/2 * w'*H*w + Cw'*w 545 """ 546 if name is None: 547 cp = self.get_cost_params() 548 else: 549 cp = self.get_cost_params(name) 550 551 N, Cw, H, dd, rh, kk, mm = \ 552 cp["N"], cp["Cw"], cp["H"], cp["dd"], cp["rh"], cp["kk"], cp["mm"] 553 nw = N.shape[0] 554 r = N * x - rh ## Nx - rhat 555 iLT = find(r < -kk) ## below dead zone 556 iEQ = find((r == 0) & (kk == 0)) ## dead zone doesn't exist 557 iGT = find(r > kk) ## above dead zone 558 iND = r_[iLT, iEQ, iGT] ## rows that are Not in the Dead region 559 iL = find(dd == 1) ## rows using linear function 560 iQ = find(dd == 2) ## rows using quadratic function 561 LL = sparse((ones(len(iL)), (iL, iL)), (nw, nw)) 562 QQ = sparse((ones(len(iQ)), (iQ, iQ)), (nw, nw)) 563 kbar = sparse((r_[ ones(len(iLT)), 564 zeros(len(iEQ)), 565 -ones(len(iGT))], (iND, iND)), (nw, nw)) * kk 566 rr = r + kbar ## apply non-dead zone shift 567 M = sparse((mm[iND], (iND, iND)), (nw, nw)) ## dead zone or scale 568 diagrr = sparse((rr, (arange(nw), arange(nw))), (nw, nw)) 569 570 ## linear rows multiplied by rr(i), quadratic rows by rr(i)^2 571 w = M * (LL + QQ * diagrr) * rr 572 573 f = dot(w * H, w) / 2 + dot(Cw, w) 574 575 return f
576 577
578 - def get_cost_params(self, name=None):
579 """Returns the cost parameter struct for user-defined costs. 580 581 Requires calling L{build_cost_params} first to build the full set of 582 parameters. Returns the full cost parameter struct for all user-defined 583 costs that incorporates all of the named cost sets added via 584 L{add_costs}, or, if a name is provided it returns the cost dict 585 corresponding to the named set of cost rows (C{N} still has full number 586 of columns). 587 588 The cost parameters are returned in a dict with the following fields:: 589 N - nw x nx sparse matrix 590 Cw - nw x 1 vector 591 H - nw x nw sparse matrix (optional, all zeros by default) 592 dd, mm - nw x 1 vectors (optional, all ones by default) 593 rh, kk - nw x 1 vectors (optional, all zeros by default) 594 """ 595 if not 'params' in self.cost: 596 stderr.write('opf_model.get_cost_params: must call build_cost_params first\n') 597 598 cp = self.cost["params"] 599 600 if name is not None: 601 if self.getN('cost', name): 602 idx = arange(self.cost["idx"]["i1"][name], self.cost["idx"]["iN"][name]) 603 cp["N"] = cp["N"][idx, :] 604 cp["Cw"] = cp["Cw"][idx] 605 cp["H"] = cp["H"][idx, :] 606 cp["dd"] = cp["dd"][idx] 607 cp["rh"] = cp["rh"][idx] 608 cp["kk"] = cp["kk"][idx] 609 cp["mm"] = cp["mm"][idx] 610 611 return cp
612 613
614 - def get_idx(self):
615 """ Returns the idx struct for vars, lin/nln constraints, costs. 616 617 Returns a structure for each with the beginning and ending 618 index value and the number of elements for each named block. 619 The 'i1' field (that's a one) is a dict with all of the 620 starting indices, 'iN' contains all the ending indices and 621 'N' contains all the sizes. Each is a dict whose keys are 622 the named blocks. 623 624 Examples:: 625 [vv, ll, nn] = get_idx(om) 626 627 For a variable block named 'z' we have:: 628 vv['i1']['z'] - starting index for 'z' in optimization vector x 629 vv['iN']['z'] - ending index for 'z' in optimization vector x 630 vv["N"] - number of elements in 'z' 631 632 To extract a 'z' variable from x:: 633 z = x(vv['i1']['z']:vv['iN']['z']) 634 635 To extract the multipliers on a linear constraint set 636 named 'foo', where mu_l and mu_u are the full set of 637 linear constraint multipliers:: 638 mu_l_foo = mu_l(ll['i1']['foo']:ll['iN']['foo']) 639 mu_u_foo = mu_u(ll['i1']['foo']:ll['iN']['foo']) 640 641 The number of nonlinear constraints in a set named 'bar':: 642 nbar = nn["N"].bar 643 (note: the following is preferable :: 644 nbar = getN(om, 'nln', 'bar') 645 ... if you haven't already called L{get_idx} to get C{nn}.) 646 """ 647 vv = self.var["idx"] 648 ll = self.lin["idx"] 649 nn = self.nln["idx"] 650 cc = self.cost["idx"] 651 652 return vv, ll, nn, cc
653 654
655 - def get_ppc(self):
656 """Returns the PYPOWER case dict. 657 """ 658 return self.ppc
659 660
661 - def getN(self, selector, name=None):
662 """Returns the number of variables, constraints or cost rows. 663 664 Returns either the total number of variables/constraints/cost rows 665 or the number corresponding to a specified named block. 666 667 Examples:: 668 N = getN(om, 'var') : total number of variables 669 N = getN(om, 'lin') : total number of linear constraints 670 N = getN(om, 'nln') : total number of nonlinear constraints 671 N = getN(om, 'cost') : total number of cost rows (in N) 672 N = getN(om, 'var', name) : number of variables in named set 673 N = getN(om, 'lin', name) : number of linear constraints in named set 674 N = getN(om, 'nln', name) : number of nonlinear cons. in named set 675 N = getN(om, 'cost', name) : number of cost rows (in N) in named set 676 """ 677 if name is None: 678 N = getattr(self, selector)["N"] 679 else: 680 if name in getattr(self, selector)["idx"]["N"]: 681 N = getattr(self, selector)["idx"]["N"][name] 682 else: 683 N = 0 684 return N
685 686
687 - def getv(self, name=None):
688 """Returns initial value, lower bound and upper bound for opt variables. 689 690 Returns the initial value, lower bound and upper bound for the full 691 optimization variable vector, or for a specific named variable set. 692 693 Examples:: 694 x, xmin, xmax = getv(om) 695 Pg, Pmin, Pmax = getv(om, 'Pg') 696 """ 697 if name is None: 698 v0 = array([]); vl = array([]); vu = array([]) 699 for k in range(self.var["NS"]): 700 name = self.var["order"][k] 701 v0 = r_[ v0, self.var["data"]["v0"][name] ] 702 vl = r_[ vl, self.var["data"]["vl"][name] ] 703 vu = r_[ vu, self.var["data"]["vu"][name] ] 704 else: 705 if name in self.var["idx"]["N"]: 706 v0 = self.var["data"]["v0"][name] 707 vl = self.var["data"]["vl"][name] 708 vu = self.var["data"]["vu"][name] 709 else: 710 v0 = array([]) 711 vl = array([]) 712 vu = array([]) 713 714 return v0, vl, vu
715 716
717 - def linear_constraints(self):
718 """Builds and returns the full set of linear constraints. 719 720 Builds the full set of linear constraints based on those added by 721 L{add_constraints}:: 722 723 L <= A * x <= U 724 """ 725 726 ## initialize A, l and u 727 # nnzA = 0 728 # for k in range(self.lin["NS"]): 729 # nnzA = nnzA + nnz(self.lin["data"].A.(self.lin.order{k})) 730 731 if self.lin["N"]: 732 A = lil_matrix((self.lin["N"], self.var["N"])) 733 u = Inf * ones(self.lin["N"]) 734 l = -u 735 else: 736 A = None 737 u = array([]) 738 l = array([]) 739 740 return A, l, u 741 742 ## fill in each piece 743 for k in range(self.lin["NS"]): 744 name = self.lin["order"][k] 745 N = self.lin["idx"]["N"][name] 746 if N: ## non-zero number of rows to add 747 Ak = self.lin["data"]["A"][name] ## A for kth linear constrain set 748 i1 = self.lin["idx"]["i1"][name] ## starting row index 749 iN = self.lin["idx"]["iN"][name] ## ing row index 750 vsl = self.lin["data"]["vs"][name] ## var set list 751 kN = 0 ## initialize last col of Ak used 752 # FIXME: Sparse matrix with fancy indexing 753 Ai = zeros((N, self.var["N"])) 754 for v in vsl: 755 j1 = self.var["idx"]["i1"][v] ## starting column in A 756 jN = self.var["idx"]["iN"][v] ## ing column in A 757 k1 = kN ## starting column in Ak 758 kN = kN + self.var["idx"]["N"][v] ## ing column in Ak 759 Ai[:, j1:jN] = Ak[:, k1:kN].todense() 760 761 A[i1:iN, :] = Ai 762 763 l[i1:iN] = self.lin["data"]["l"][name] 764 u[i1:iN] = self.lin["data"]["u"][name] 765 766 return A.tocsr(), l, u
767 768
769 - def userdata(self, name, val=None):
770 """Used to save or retrieve values of user data. 771 772 This function allows the user to save any arbitrary data in the object 773 for later use. This can be useful when using a user function to add 774 variables, constraints, costs, etc. For example, suppose some special 775 indexing is constructed when adding some variables or constraints. 776 This indexing data can be stored and used later to "unpack" the results 777 of the solved case. 778 """ 779 if val is not None: 780 self.user_data[name] = val 781 return self 782 else: 783 if name in self.user_data: 784 return self.user_data[name] 785 else: 786 return array([])
787