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

Source Code for Module PyFoam.Basics.TimeLineCollection

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