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