1
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
12 """Mean value of a and b"""
13 return 0.5*(a+b)
14
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
23 """Collects references to TimeLineCollection objects"""
24
25 nr=1
26
29
30 - def add(self,line,nr=None):
41
43 try:
44 return self.lines[nr]
45 except KeyError:
46 error(nr,"not a known data set:",self.lines.keys())
47
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
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
89
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
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
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
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
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
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
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
249
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
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
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
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
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
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
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
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