Package PyFoam :: Package Basics :: Module TimeLineCollection
[hide private]
[frames] | no frames]

Source Code for Module PyFoam.Basics.TimeLineCollection

  1  #  ICE Revision: $Id: /local/openfoam/Python/PyFoam/PyFoam/Basics/TimeLineCollection.py 5754 2009-10-28T13:12:56.435362Z bgschaid  $  
  2  """Collection of array of timelines""" 
  3   
  4  from PyFoam.Error import error 
  5  from math import ceil 
  6  from copy import deepcopy 
  7  from threading import Lock 
  8   
  9  transmissionLock=Lock() 
 10   
11 -def mean(a,b):
12 """Mean value of a and b""" 13 return 0.5*(a+b)
14
15 -def signedMax(a,b):
16 """Absolute Maximum of a and b with the sign preserved""" 17 if a<0. or b<0.: 18 return min(a,b) 19 else: 20 return max(a,b)
21
22 -class TimeLinesRegistry(object):
23 """Collects references to TimeLineCollection objects""" 24 25 nr=1 26
27 - def __init__(self):
28 self.lines={}
29
30 - def add(self,line,nr=None):
31 if nr: 32 if nr in self.lines: 33 error("Number",nr,"already existing") 34 TimeLinesRegistry.nr=max(nr+1,TimeLinesRegistry) 35 else: 36 nr=TimeLinesRegistry.nr 37 TimeLinesRegistry.nr+=1 38 self.lines[nr]=line 39 40 return nr
41
42 - def get(self,nr):
43 try: 44 return self.lines[nr] 45 except KeyError: 46 error(nr,"not a known data set:",self.lines.keys())
47
48 - def prepareForTransfer(self):
49 """Makes sure that the data about the timelines is to be transfered via XMLRPC""" 50 51 transmissionLock.acquire() 52 53 lst={} 54 for i,p in self.lines.iteritems(): 55 slaves=[] 56 for s in p.slaves: 57 slaves.append(s.lineNr) 58 59 lst[str(i)]={ "nr" : i, 60 "times" : deepcopy(p.times), 61 "values": deepcopy(p.values), 62 "lastValid" : deepcopy(p.lastValid), 63 "slaves": slaves } 64 65 transmissionLock.release() 66 67 return lst
68
69 - def resolveSlaves(self):
70 """Looks through all the registered lines and replaces integers with 71 the actual registered line""" 72 for i,p in self.lines.iteritems(): 73 if len(p.slaves)>0: 74 slaves=[] 75 for s in p.slaves: 76 if type(s)==int: 77 try: 78 slaves.append(self.lines[s]) 79 except KeyError: 80 error(s,"not a known data set:",self.lines.keys()) 81 else: 82 slaves.append(s) 83 p.slaves=slaves
84 85 _allLines=TimeLinesRegistry() 86
87 -def allLines():
88 return _allLines
89
90 -class TimeLineCollection(object):
91 92 possibleAccumulations=["first", "last", "min", "max", "average", "sum","count"] 93
94 - def __init__(self, 95 deflt=0., 96 extendCopy=False, 97 splitThres=None, 98 splitFun=None, 99 noEmptyTime=True, 100 advancedSplit=False, 101 preloadData=None, 102 accumulation="first", 103 registry=None):
104 """@param deflt: default value for timelines if none has been defined before 105 @param extendCopy: Extends the timeline by cpying the last element 106 @param splitThres: Threshold after which the number of points is halved 107 @param splitFun: Function that is used for halving. If none is specified the mean function is used 108 @param noEmptyTime: if there is no valid entry no data is stored for this time 109 @param advancedSplit: Use another split algorithm than one that condenses two values into one 110 @param preloadData: a dictionary with a dictionary to initialize the values 111 @param accumulation: if more than one value is given at any time-step, how to accumulate them (possible values: "first", "last", "min", "max", "average", "sum","count") 112 """ 113 114 self.cTime=None 115 self.times=[] 116 self.values={} 117 self.lastValid={} 118 self.setDefault(deflt) 119 self.setExtend(extendCopy) 120 self.thres=None 121 self.fun=None 122 123 if not (accumulation in TimeLineCollection.possibleAccumulations): 124 error("Value",accumulation,"not in list of possible values:",TimeLineCollection.possibleAccumulations) 125 self.accumulation=accumulation 126 self.accumulations={} 127 self.occured={} 128 129 self.slaves=[] 130 131 self.setSplitting(splitThres=splitThres, 132 splitFun=splitFun, 133 advancedSplit=advancedSplit, 134 noEmptyTime=noEmptyTime) 135 136 self.lineNr=None 137 if preloadData: 138 self.times=preloadData["times"] 139 self.values=preloadData["values"] 140 self.slaves=preloadData["slaves"] 141 self.lineNr=int(preloadData["nr"]) 142 if "lastValid" in preloadData: 143 self.lastValid=preloadData["lastValid"] 144 else: 145 self.resetValid(val=True) 146 147 if registry==None: 148 registry=allLines() 149 self.lineNr=registry.add(self,self.lineNr)
150
151 - def resetValid(self,val=False):
152 """Helper function that resets the information whether the last entry is valid""" 153 self.lastValid={} 154 for n in self.values: 155 self.lastValid[n]=val 156 for s in self.slaves: 157 s.resetValid(val=val)
158
159 - def nrValid(self):
160 """Helper function that gets the number of valid values""" 161 nr=self.lastValid.values().count(True) 162 for s in self.slaves: 163 nr+=s.nrValid() 164 return nr
165
166 - def addSlave(self,slave):
167 """Adds a slave time-line-collection""" 168 self.slaves.append(slave) 169 slave.setSplitting(splitThres=self.thres, 170 splitFun=self.fun, 171 advancedSplit=self.advancedSplit, 172 noEmptyTime=self.noEmptyTime)
173
174 - def setAccumulator(self,name,accu):
175 """Sets a special accumulator fopr a timeline 176 @param name: Name of the timeline 177 @param accu: Name of the accumulator""" 178 if not (accu in TimeLineCollection.possibleAccumulations): 179 error("Value",accu,"not in list of possible values:",TimeLineCollection.possibleAccumulations,"When setting for",name) 180 self.accumulations[name]=accu
181
182 - def setSplitting(self,splitThres=None,splitFun=None,advancedSplit=False,noEmptyTime=True):
183 """Sets the parameters for splitting""" 184 185 self.advancedSplit = advancedSplit 186 if self.advancedSplit: 187 self.splitLevels = [] 188 if splitThres: 189 self.thres=splitThres 190 if (self.thres % 2)==1: 191 self.thres+=1 192 193 if splitFun: 194 self.fun=splitFun 195 elif not self.fun: 196 self.fun=mean 197 198 for s in self.slaves: 199 s.setSplitting(splitThres=splitThres,splitFun=splitFun,advancedSplit=advancedSplit,noEmptyTime=noEmptyTime) 200 201 self.noEmptyTime=noEmptyTime
202
203 - def setDefault(self,deflt):
204 """@param deflt: default value to be used""" 205 self.defaultValue=float(deflt)
206
207 - def setExtend(self,mode):
208 """@param mode: whether or not to extend the timeline by copying or setting the default value""" 209 self.extendCopy=mode
210
211 - def nr(self):
212 """Number of elements in timelines""" 213 return len(self.times)
214
215 - def setTime(self,time,noLock=False,forceAppend=False):
216 """Sets the time. If time is new all the timelines are extended 217 @param time: the new current time 218 @param noLock: do not acquire the lock that ensures consistent data transmission""" 219 220 if not noLock: 221 transmissionLock.acquire() 222 223 dTime=float(time) 224 225 if dTime!=self.cTime: 226 self.cTime=dTime 227 append=True 228 if self.noEmptyTime and not forceAppend: 229 if self.nrValid()==0: 230 append=False 231 if append: 232 self.times.append(self.cTime) 233 for v in self.values.values(): 234 if len(v)>0 and self.extendCopy: 235 val=v[-1] 236 else: 237 val=self.defaultValue 238 v.append(val) 239 else: 240 if len(self.times)>0: 241 self.times[-1]=self.cTime 242 243 self.resetValid() 244 245 if self.thres and append: 246 if len(self.times)>=self.thres: 247 if self.advancedSplit: 248 # Clumsy algorithm where the maximum and the minimum of a 249 # data-window are preserved in that order 250 if len(self.splitLevels)<len(self.times): 251 self.splitLevels+=[0]*(len(self.times)-len(self.splitLevels)) 252 splitTill=int(len(self.times)*0.75) 253 if self.splitLevels[splitTill]!=0: 254 # Shouldn't happen. But just in case 255 splitTill=self.splitLevels.index(0) 256 splitFrom=0 257 maxLevel=self.splitLevels[0] 258 for l in range(maxLevel): 259 li=self.splitLevels.index(l) 260 if li>=0 and li<splitTill/2: 261 splitFrom=li 262 break 263 window=4 264 if ((splitTill-splitFrom)/window)!=0: 265 splitTill=splitFrom+window*int(ceil((splitTill-splitFrom)/float(window))) 266 267 # prepare data that will not be split 268 times=self.times[:splitFrom] 269 levels=self.splitLevels[:splitFrom] 270 values={} 271 for k in self.values: 272 values[k]=self.values[k][:splitFrom] 273 274 for start in range(splitFrom,splitTill,window): 275 end=start+window-1 276 sTime=self.times[start] 277 eTime=self.times[end] 278 times+=[sTime,(eTime-sTime)*(2./3)+sTime] 279 levels+=[self.splitLevels[start]+1,self.splitLevels[end]+1] 280 for k in self.values: 281 minV=self.values[k][start] 282 minI=0 283 maxV=self.values[k][start] 284 maxI=0 285 for j in range(1,window): 286 val=self.values[k][start+j] 287 if val>maxV: 288 maxV=val 289 maxI=j 290 if val<minV: 291 minV=val 292 minI=j 293 if minI<maxI: 294 values[k]+=[minV,maxV] 295 else: 296 values[k]+=[maxV,minV] 297 firstUnsplit=int(splitTill/window)*window 298 self.times=times+self.times[firstUnsplit:] 299 self.splitLevels=levels+self.splitLevels[firstUnsplit:] 300 # print self.splitLevels 301 for k in self.values: 302 self.values[k]=values[k]+self.values[k][firstUnsplit:] 303 assert len(self.times)==len(self.values[k]) 304 else: 305 self.times=self.split(self.times,min) 306 for k in self.values.keys(): 307 self.values[k]=self.split(self.values[k],self.fun) 308 self.occured={} 309 310 for s in self.slaves: 311 s.setTime(time,noLock=True,forceAppend=append) 312 313 if not noLock: 314 transmissionLock.release()
315
316 - def split(self,array,func):
317 """Makes the array smaller by joining every two points 318 @param array: the field to split 319 @param func: The function to use for joining two points""" 320 321 newLen=len(array)/2 322 newArray=[0.]*newLen 323 324 for i in range(newLen): 325 newArray[i]=func(array[2*i],array[2*i+1]) 326 327 return newArray
328
329 - def getTimes(self,name=None):
330 """@return: A list of the time values""" 331 tm=None 332 if name in self.values or name==None: 333 tm=self.times 334 else: 335 for s in self.slaves: 336 if name in s.values: 337 tm=s.times 338 break 339 return tm
340
341 - def getValueNames(self):
342 """@return: A list with the names of the safed values""" 343 names=self.values.keys() 344 for i,s in enumerate(self.slaves): 345 for n in s.getValueNames(): 346 names.append("%s_slave%02d" % (n,i)) 347 return names
348
349 - def getValues(self,name):
350 """Gets a timeline 351 @param name: Name of the timeline 352 @return: List with the values""" 353 354 if not self.values.has_key(name): 355 if len(self.slaves)>0: 356 if name.find("_slave")>0: 357 nr=int(name[-2:]) 358 nm=name[:name.find("_slave")] 359 return self.slaves[nr].getValues(nm) 360 self.values[name]=self.nr()*[self.defaultValue] 361 return self.values[name]
362
363 - def setValue(self,name,value):
364 """Sets the value of the last element in a timeline 365 @param name: name of the timeline 366 @param value: the last element""" 367 368 transmissionLock.acquire() 369 370 data=self.getValues(name) 371 val=float(value) 372 if len(data)>0: 373 accu=self.accumulation 374 if not self.occured.has_key(name): 375 if accu=="count": 376 newValue=1L 377 else: 378 newValue=val 379 self.occured[name]=1 380 else: 381 oldValue=data[-1] 382 n=self.occured[name] 383 self.occured[name]+=1 384 if name in self.accumulations: 385 accu=self.accumulations[name] 386 if accu=="first": 387 newValue=oldValue 388 elif accu=="last": 389 newValue=val 390 elif accu=="max": 391 newValue=max(val,oldValue) 392 elif accu=="min": 393 newValue=min(val,oldValue) 394 elif accu=="sum": 395 newValue=val+oldValue 396 elif accu=="average": 397 newValue=(n*oldValue+val)/(n+1) 398 elif accu=="count": 399 newValue=n+1 400 else: 401 error("Unimplemented accumulator",accu,"for",name) 402 403 data[-1]=newValue 404 405 self.lastValid[name]=True 406 407 transmissionLock.release()
408