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

Source Code for Module pypower.scale_load

  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  """Scales fixed and/or dispatchable loads. 
 18  """ 
 19   
 20  from sys import stderr 
 21   
 22  from numpy import array, zeros, arange, in1d, ix_ 
 23  from numpy import flatnonzero as find 
 24   
 25  from scipy.sparse import csr_matrix as sparse 
 26   
 27  from isload import isload 
 28   
 29  from idx_bus import PD, QD, BUS_AREA, BUS_I 
 30  from idx_gen import PG, QG, QMAX, QMIN, GEN_BUS, GEN_STATUS, PMIN 
 31   
 32   
33 -def scale_load(load, bus, gen=None, load_zone=None, opt=None):
34 """Scales fixed and/or dispatchable loads. 35 36 Assumes consecutive bus numbering when dealing with dispatchable loads. 37 38 @param load: Each element specifies the amount of scaling for the 39 corresponding load zone, either as a direct scale factor 40 or as a target quantity. If there are C{nz} load zones this 41 vector has C{nz} elements. 42 @param bus: Standard C{bus} matrix with C{nb} rows, where the fixed active 43 and reactive loads available for scaling are specified in 44 columns C{PD} and C{QD} 45 @param gen: (optional) standard C{gen} matrix with C{ng} rows, where the 46 dispatchable loads available for scaling are specified by 47 columns C{PG}, C{QG}, C{PMIN}, C{QMIN} and C{QMAX} (in rows for which 48 C{isload(gen)} returns C{true}). If C{gen} is empty, it assumes 49 there are no dispatchable loads. 50 @param load_zone: (optional) C{nb} element vector where the value of 51 each element is either zero or the index of the load zone 52 to which the corresponding bus belongs. If C{load_zone[b] = k} 53 then the loads at bus C{b} will be scaled according to the 54 value of C{load[k]}. If C{load_zone[b] = 0}, the loads at bus C{b} 55 will not be modified. If C{load_zone} is empty, the default is 56 determined by the dimensions of the C{load} vector. If C{load} is 57 a scalar, a single system-wide zone including all buses is 58 used, i.e. C{load_zone = ones(nb)}. If C{load} is a vector, the 59 default C{load_zone} is defined as the areas specified in the 60 C{bus} matrix, i.e. C{load_zone = bus[:, BUS_AREA]}, and C{load} 61 should have dimension C{= max(bus[:, BUS_AREA])}. 62 @param opt: (optional) dict with three possible fields, 'scale', 63 'pq' and 'which' that determine the behavior as follows: 64 - C{scale} (default is 'FACTOR') 65 - 'FACTOR' : C{load} consists of direct scale factors, where 66 C{load[k] =} scale factor C{R[k]} for zone C{k} 67 - 'QUANTITY' : C{load} consists of target quantities, where 68 C{load[k] =} desired total active load in MW for 69 zone C{k} after scaling by an appropriate C{R(k)} 70 - C{pq} (default is 'PQ') 71 - 'PQ' : scale both active and reactive loads 72 - 'P' : scale only active loads 73 - C{which} (default is 'BOTH' if GEN is provided, else 'FIXED') 74 - 'FIXED' : scale only fixed loads 75 - 'DISPATCHABLE' : scale only dispatchable loads 76 - 'BOTH' : scale both fixed and dispatchable loads 77 78 @see: L{total_load} 79 80 @author: Ray Zimmerman (PSERC Cornell) 81 @author: Richard Lincoln 82 """ 83 nb = bus.shape[0] ## number of buses 84 85 ##----- process inputs ----- 86 bus = bus.copy() 87 if gen is None: 88 gen = array([]) 89 else: 90 gen = gen.copy() 91 if load_zone is None: 92 load_zone = array([], int) 93 if opt is None: 94 opt = {} 95 96 ## fill out and check opt 97 if len(gen) == 0: 98 opt["which"] = 'FIXED' 99 if 'pq' not in opt: 100 opt["pq"] = 'PQ' ## 'PQ' or 'P' 101 if 'which' not in opt: 102 opt["which"] = 'BOTH' ## 'FIXED', 'DISPATCHABLE' or 'BOTH' 103 if 'scale' not in opt: 104 opt["scale"] = 'FACTOR' ## 'FACTOR' or 'QUANTITY' 105 if (opt["pq"] != 'P') and (opt["pq"] != 'PQ'): 106 stderr.write("scale_load: opt['pq'] must equal 'PQ' or 'P'\n") 107 if (opt["which"][0] != 'F') and (opt["which"][0] != 'D') and (opt["which"][0] != 'B'): 108 stderr.write("scale_load: opt.which should be 'FIXED, 'DISPATCHABLE or 'BOTH'\n") 109 if (opt["scale"][0] != 'F') and (opt["scale"][0] != 'Q'): 110 stderr.write("scale_load: opt.scale should be 'FACTOR or 'QUANTITY'\n") 111 if (len(gen) == 0) and (opt["which"][0] != 'F'): 112 stderr.write('scale_load: need gen matrix to scale dispatchable loads\n') 113 114 ## create dispatchable load connection matrix 115 if len(gen) > 0: 116 ng = gen.shape[0] 117 is_ld = isload(gen) & (gen[:, GEN_STATUS] > 0) 118 ld = find(is_ld) 119 120 ## create map of external bus numbers to bus indices 121 i2e = bus[:, BUS_I].astype(int) 122 e2i = zeros(max(i2e) + 1, int) 123 e2i[i2e] = arange(nb) 124 125 gbus = gen[:, GEN_BUS].astype(int) 126 Cld = sparse((is_ld, (e2i[gbus], arange(ng))), (nb, ng)) 127 else: 128 ng = 0 129 ld = array([], int) 130 131 if len(load_zone) == 0: 132 if len(load) == 1: ## make a single zone of all load buses 133 load_zone = zeros(nb, int) ## initialize 134 load_zone[bus[:, PD] != 0] = 1 ## FIXED loads 135 if len(gen) > 0: 136 gbus = gen[ld, GEN_BUS].astype(int) 137 load_zone[e2i[gbus]] = 1 ## DISPATCHABLE loads 138 else: ## use areas defined in bus data as zones 139 load_zone = bus[:, BUS_AREA] 140 141 ## check load_zone to make sure it's consistent with size of load vector 142 if max(load_zone) > len(load): 143 stderr.write('scale_load: load vector must have a value for each load zone specified\n') 144 145 ##----- compute scale factors for each zone ----- 146 scale = load.copy() 147 Pdd = zeros(nb) ## dispatchable P at each bus 148 if opt["scale"][0] == 'Q': ## 'QUANTITY' 149 ## find load capacity from dispatchable loads 150 if len(gen) > 0: 151 Pdd = -Cld * gen[:, PMIN] 152 153 ## compute scale factors 154 for k in range(len(load)): 155 idx = find(load_zone == k + 1) 156 fixed = sum(bus[idx, PD]) 157 dispatchable = sum(Pdd[idx]) 158 total = fixed + dispatchable 159 if opt["which"][0] == 'B': ## 'BOTH' 160 if total != 0: 161 scale[k] = load[k] / total 162 elif load[k] == total: 163 scale[k] = 1 164 else: 165 raise ScalingError('scale_load: impossible to make zone %d load equal %g by scaling non-existent loads\n' % (k, load[k])) 166 elif opt["which"][0] == 'F': ## 'FIXED' 167 if fixed != 0: 168 scale[k] = (load[k] - dispatchable) / fixed 169 elif load[k] == dispatchable: 170 scale[k] = 1 171 else: 172 raise ScalingError('scale_load: impossible to make zone %d load equal %g by scaling non-existent fixed load\n' % (k, load[k])) 173 elif opt["which"][0] == 'D': ## 'DISPATCHABLE' 174 if dispatchable != 0: 175 scale[k] = (load[k] - fixed) / dispatchable 176 elif load[k] == fixed: 177 scale[k] = 1 178 else: 179 raise ScalingError('scale_load: impossible to make zone %d load equal %g by scaling non-existent dispatchable load\n' % (k, load[k])) 180 181 ##----- do the scaling ----- 182 ## fixed loads 183 if opt["which"][0] != 'D': ## includes 'FIXED', not 'DISPATCHABLE' only 184 for k in range(len(scale)): 185 idx = find(load_zone == k + 1) 186 bus[idx, PD] = bus[idx, PD] * scale[k] 187 if opt["pq"] == 'PQ': 188 bus[idx, QD] = bus[idx, QD] * scale[k] 189 190 ## dispatchable loads 191 if opt["which"][0] != 'F': ## includes 'DISPATCHABLE', not 'FIXED' only 192 for k in range(len(scale)): 193 idx = find(load_zone == k + 1) 194 gbus = gen[ld, GEN_BUS].astype(int) 195 i = find( in1d(e2i[gbus], idx) ) 196 ig = ld[i] 197 198 gen[ix_(ig, [PG, PMIN])] = gen[ix_(ig, [PG, PMIN])] * scale[k] 199 if opt["pq"] == 'PQ': 200 gen[ix_(ig, [QG, QMIN, QMAX])] = gen[ix_(ig, [QG, QMIN, QMAX])] * scale[k] 201 202 return bus, gen
203 204
205 -class ScalingError(Exception):
206 pass
207