/[escript]/trunk/escript/py_src/timeseries.py
ViewVC logotype

Contents of /trunk/escript/py_src/timeseries.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1107 - (show annotations)
Thu Apr 19 02:14:18 2007 UTC (12 years, 7 months ago) by gross
File MIME type: text/x-python
File size: 42660 byte(s)
Small bug in ReadMeh call fixed.
Test for typ of verbose argument in pdetools.SaddlePointSolver added.


1 # $Id$
2
3 """
4 Time serieas analysis
5
6 @var __author__: name of author
7 @var __copyright__: copyrights
8 @var __license__: licence agreement
9 @var __url__: url entry point on documentation
10 @var __version__: version
11 @var __date__: date of the version
12 """
13
14
15 __author__="Lutz Gross, l.gross@uq.edu.au"
16 __copyright__=""" Copyright (c) 2006 by ACcESS MNRF
17 http://www.access.edu.au
18 Primary Business: Queensland, Australia"""
19 __license__="""Licensed under the Open Software License version 3.0
20 http://www.opensource.org/licenses/osl-3.0.php"""
21 __url__="http://www.iservo.edu.au/esys/escript"
22 __version__="$Revision$"
23 __date__="$Date$"
24
25
26 import numarray
27 from types import SliceType
28 DEFAULT_BUFFER_SIZE=1000
29 DEFAULT_FLOAT_TYPE=numarray.Float64
30
31 class TimeSeriesBase:
32 """The TimeSeriesBase class is the base class for all class of the TimeSeries module."""
33
34 def __init__(self,debug=False,description="TimeSeriesBase"):
35 self.__debug=debug
36 self.setDescription(description)
37
38 def __str__(self):
39 return self.__description
40
41 def setDescription(self,text):
42 self.__description=text
43
44 def setDebugOn(self):
45 """switch on degugging mode"""
46 self.__debug=True
47
48 def setDebugOff(self):
49 """switch off degugging mode"""
50 self.__debug=False
51
52 def setDebug(self,flag=False):
53 """sets debug mode to flag"""
54 if flag:
55 self.setDebugOn()
56 else:
57 self.setDebugOff()
58
59 def debug(self):
60 """returns true if debug mode is on"""
61 return self.__debug
62
63 #============================================================================================================
64 class TimeSeriesBaseDataset(TimeSeriesBase):
65 """provides an interface for accessing a set of linearly ordered data."""
66 def __init__(self,buffer,offset=0,debug=False,description="TimeSeriesDataset"):
67 TimeSeriesBase.__init__(self,debug,description)
68 self.__buffer=buffer
69 self.__offset=offset
70 if self.debug(): print "Debug: %s: offset %d to buffer"%(self,self.getOffset())
71
72 def __len__(self):
73 """needed to handle negative indexing in slicing"""
74 return 0
75
76 def getNumComponents(self):
77 """returns the number of components of the data (may be overwritten by subclass)"""
78 return self.getBaseBuffer().getNumComponents()
79
80 def getIdOfLastDatum(self):
81 """returns the identification number of the last datum in the data set (may be overwritten by subclass)"""
82 return self.getBaseBuffer().getIdOfLastDatum()-self.getOffset()
83
84 def getIdOfFirstDatum(self):
85 """returns the identification number of the first datum (may be overwritten by subclass)"""
86 return self.getBaseBuffer().getIdOfFirstDatum()-self.getOffset()
87
88 def getIdOfFirstAvailableDatum(self):
89 """returns the identification number of the first avaiable datum (may be overwritten by subclass)"""
90 return self.getBaseBuffer().getIdOfFirstAvailableDatum()-self.getOffset()
91
92 def getOffsetInBaseBuffer(self):
93 """returns the offset to access elements in getBaseBuffer() (may be overwritten by subclass)"""
94 return self.getOffset()
95
96 def getIdOfLastUnreferencedDatum(self):
97 """returns the identification number of the last datum which has been unused by all TimeSeries refering to the TimeSeriesBaseDataset (may be overwritten by subclass)"""
98 return self.getBaseBuffer().getIdOfLastUnreferencedDatum()-self.getOffset()
99
100 def updateIdOfLastUnreferencedDatum(self,last_unreferenced_datum):
101 """updates the identification number of the last unused datum (to be overwritten by subclass)"""
102 self.getBaseBuffer().updateIdOfLastUnreferencedDatum(last_unreferenced_datum+self.getOffset())
103
104 def append(self,values):
105 """appends data to the buffer. If the buffer would be full the buffer is rearranged before the data are appended (to be overwritten by subclass)"""
106 self.getBaseBuffer().append(values)
107
108 def getBaseBufferSize(self):
109 """returns the size of the buffer (to be overwritten by subclass)"""
110 return self.getBaseBuffer().getBaseBufferSize()
111
112 def needsRearrangement(self,num_new_data=0):
113 """returns True if the buffer will be full after num_new_data have been appended (to be overwritten by subclass)"""
114 return self.getBaseBuffer().needsRearrangement(num_new_data)
115
116 def isEmpty(self):
117 """returns true if no data are appeneded to buffer"""
118 return self.getNumData()<=0
119
120 def getNumData(self):
121 """returns the number of data (not all of them are accessible)"""
122 return self.getIdOfLastDatum()-self.getIdOfFirstDatum()+1
123
124 def getBaseBuffer(self):
125 """return the buffer referenced by the TimeSeriesBaseDataset"""
126 return self.__buffer
127
128 def getOffset(self):
129 """return the offset when referring to dataset elements"""
130 return self.__offset
131
132 def __getitem__(self,index):
133 """returns the datum index"""
134 if type(index)==SliceType:
135 start=index.start
136 end=index.stop
137 if start==end:
138 return self[start]
139 else:
140 if start<self.getIdOfFirstDatum() or start>self.getIdOfLastDatum() or \
141 end-1<self.getIdOfFirstDatum() or end-1>self.getIdOfLastDatum(): raise IndexError,"%s: Index [%d:%d] out of range"%(self,start,end)
142 return self.getBaseBuffer()[start+self.getOffsetInBaseBuffer():end+self.getOffsetInBaseBuffer()]
143 else:
144 if index<self.getIdOfFirstDatum() or index>self.getIdOfLastDatum(): raise IndexError,"%s: Index %d out of range"%(self,index)
145 return self.getBaseBuffer()[index+self.getOffsetInBaseBuffer()]
146
147 class TimeSeriesBaseBuffer(TimeSeriesBaseDataset):
148 """An inplementation of TimeSeriesBaseDataset which actually is storing data into a numarray buffer"""
149 def __init__(self,buffer_size=DEFAULT_BUFFER_SIZE,numComponents=1,type=DEFAULT_FLOAT_TYPE,id_of_first_datum=0,debug=False,description="TimeSeriesBaseBuffer"):
150 if numComponents<2:
151 buffer=numarray.zeros((buffer_size,),type)
152 else:
153 buffer=numarray.zeros((buffer_size,numComponents),type)
154 TimeSeriesBaseDataset.__init__(self,buffer,id_of_first_datum-1,debug,description)
155 self.__num_data_in_buffer=0
156 self.__id_last_unreferenced_datum=id_of_first_datum-1
157 self.__id_last_datum=id_of_first_datum-1
158 self.__id_first_datum=id_of_first_datum
159 if self.debug(): print "Debug: %s : buffer of size %d with %d components allocated (first datum is %d)."% \
160 (self,self.getBaseBufferSize(),self.getNumComponents(),id_of_first_datum)
161
162
163 def getBaseBufferSize(self):
164 """returns the size of the buffer"""
165 return self.getBaseBuffer().shape[0]
166
167 def getNumComponents(self):
168 """returns the number of components of the data (overwrites TimeSeriesBaseDataset method)"""
169 if self.getBaseBuffer().rank==1:
170 return 1
171 else:
172 self.getBaseBuffer().shape[1]
173
174 def getNumDataInBaseBuffer(self):
175 """returns the number of data currently in the buffer"""
176 return self.__num_data_in_buffer
177
178 def getIdOfLastDatum(self):
179 """returns the identification number of the last datum in the data set (overwrites method from TimeSeriesBaseDataset)"""
180 return self.__id_last_datum
181
182 def getIdOfFirstDatum(self):
183 """returns the identification number of the first datum (overwrites method from TimeSeriesBaseDataset)"""
184 return self.__id_first_datum
185
186 def getOffsetInBaseBuffer(self):
187 """returns the offset to access elements in the buffer (overwrites method from TimeSeriesBaseDataset)"""
188 return -self.getIdOfLastDatum()+self.getNumDataInBaseBuffer()-1
189
190 def getIdOfLastUnreferencedDatum(self):
191 """returns the identification number of the last datum which has been unused by all TimeSeries refering to the TimeSeriesBaseDataset (overwrites method from TimeSeriesBaseDataset)"""
192 return self.__id_last_unreferenced_datum
193
194 def updateIdOfLastUnreferencedDatum(self,last_unreferenced_datum):
195 """updates the identification number of the last unused datum (to be overwritten by subclass)"""
196 self.getBaseBuffer().updateIdOfLastUnreferencedDatum(last_unreferenced_datum-self.getOffset())
197
198 def updateIdOfLastUnreferencedDatum(self,last_unreferenced_datum):
199 """updates the identification number of the last unused datum (overwrites TimeSeriesBaseDataset method)"""
200 if self.__id_last_unreferenced_datum>last_unreferenced_datum:
201 self.__id_last_unreferenced_datum=last_unreferenced_datum
202 if self.debug(): print "Debug: %s: last unused datum is now %s"%(self,last_unreferenced_datum)
203
204 def needsRearrangement(self,num_new_data=0):
205 """returns True if the buffer will be full after num_new_data have been appended"""
206 return self.getNumDataInBaseBuffer()+num_new_data>self.getBaseBufferSize()
207
208 def getIdOfFirstAvailableDatum(self):
209 """returns the identification number of the first avaiable datum (overwrites TimeSeriesBaseDataset method)"""
210 return self.getIdOfLastDatum()-self.__num_data_in_buffer+1
211
212 def append(self,data):
213 """appends data to the buffer. If the buffer would be full the buffer is rearranged before the data are appended (overwrites TimeSeriesBaseDataset method)"""
214 data=numarray.array(data)
215 nc=self.getNumComponents()
216 if data.rank==0:
217 if nc==1:
218 num_new_data=1
219 else:
220 raise ValueError,"%s: illegal data shape"%self
221 elif data.rank==1:
222 if nc==1:
223 num_new_data=data.shape[0]
224 else:
225 num_new_data=1
226 elif data.rank==2:
227 if not nc==data.shape[1]: raise ValueError,"%s: illegal data shape"%self
228 num_new_data=data.shape[0]
229 else:
230 raise ValueError,"%s: illegal rank"%self
231
232 # check is buffer will be overflown when data are appended:
233 if self.needsRearrangement(num_new_data):
234 nn=self.getNumDataInBaseBuffer()
235 num_protected_data=self.getIdOfLastDatum()-self.getIdOfLastUnreferencedDatum()
236 if num_protected_data+num_new_data>self.getBaseBufferSize():
237 raise ValueError,"%s: buffer overflow: buffer size has to be bigger than %d"%(self,num_protected_data+num_new_data)
238 if num_protected_data>0: self.getBaseBuffer()[0:num_protected_data]=self.getBaseBuffer()[nn-num_protected_data:nn]
239 self.__num_data_in_buffer=num_protected_data
240 self.__id_last_unreferenced_datum=self.__id_last_datum
241 if self.debug():
242 print "Debug: %s: rearrangement: first data in buffer is %d."%(self,self.getIdOfLastDatum()-self.getNumDataInBaseBuffer()+1)
243 # copy data over:
244 nn=self.getNumDataInBaseBuffer()
245 self.getBaseBuffer()[nn:nn+num_new_data]=data
246 self.__num_data_in_buffer+=num_new_data
247 self.__id_last_datum+=num_new_data
248 self.__id_last_unreferenced_datum+=num_new_data
249 if self.debug(): print "Debug: %s: %d data appended. Last unreferenced datum is now %d."%(self,num_new_data,self.__id_last_unreferenced_datum)
250
251 # ======================================
252 class TimeSeriesControlerView(TimeSeriesBase):
253 """A TimeSeriesControlerView is attached to a Controler and moves forward in time by increasing the id of the last processed datum.
254 Any implementation of a TimeSeriesControlerView must provide the getControler method which returns the controler"""
255 def __init__(self,id_first_datum=0,debug=False,description="TimeSeries"):
256 TimeSeriesBase.__init__(self,debug,description)
257 self.__id_last_processed_datum=id_first_datum-1
258 if self.debug(): print "Debug: %s created with first datum %d"%(str(self),id_first_datum)
259
260 def getIdOfLastProcessedDatum(self):
261 return self.__id_last_processed_datum
262
263 def updateIdOfLastProcessedDatum(self,id_last_processed_datum):
264 self.__id_last_processed_datum=id_last_processed_datum
265
266 # def getControler(self):
267 # """returns the Controler of the time series (to be overwritten by subclass)"""
268 # pass
269
270 class TimeSeries(TimeSeriesBaseDataset,TimeSeriesControlerView):
271 """makes TimeSeriesBaseDataset look like a TimeSeries and introduces operations
272 Any implementation of a TimeSeriesControlerView must provide the getControler method which returns the controler"""
273 def __init__(self,dataset,debug=False,description="TimeSeries"):
274 TimeSeriesControlerView.__init__(self,dataset.getIdOfFirstDatum(),debug,description)
275 TimeSeriesBaseDataset.__init__(self,dataset,0,debug,description)
276
277 def getDataset(self):
278 """returns the TimeSeriesBaseDataset of the time series"""
279 return self.getBaseBuffer()
280
281 # def getControler(self):
282 # """returns the Controler of the time series (to be overwritten by subclass)"""
283 # pass
284
285 def __add__(self,arg):
286 if isinstance(arg,TimeSeriesBaseDataset):
287 return TimeSeriesAdd(self,arg)
288 else:
289 return TimeSeriesAddScalar(self,arg)
290
291 def __sub__(self,arg):
292 return self+(-1.)*arg
293
294 def __mul__(self,arg):
295 if isinstance(arg,TimeSeriesBaseDataset):
296 return TimeSeriesMult(self,arg)
297 else:
298 return TimeSeriesMultScalar(self,arg)
299
300 def __div__(self,arg):
301 if isinstance(arg,TimeSeriesBaseDataset):
302 return TimeSeriesDiv(self,arg)
303 else:
304 return TimeSeriesMultScalar(self,1./arg)
305
306 def __pow__(self,arg):
307 if isinstance(arg,TimeSeriesBaseDataset):
308 return TimeSeriesPower(self,arg)
309 else:
310 return TimeSeriesPowerScalar(self,arg)
311
312 def __radd__(self,arg):
313 return self.__add__(arg)
314
315 def __rsub__(self,arg):
316 return arg+(-1.)*self
317
318 def __rmul__(self,arg):
319 return self.__mul__(arg)
320
321 def __rdiv__(self,arg):
322 if isinstance(arg,TimeSeriesBaseDataset):
323 return TimeSeriesDiv(arg,self)
324 else:
325 return TimeSeriesDivScalar(self,arg)
326
327 def __rpow__(self,arg):
328 if isinstance(arg,TimeSeriesBaseDataset):
329 return TimeSeriesPower(arg,self)
330 else:
331 return Exp(numarray.log(arg)*self)
332
333 def __lshift__(self,arg):
334 return TimeSeriesShift(self,-arg)
335
336 def __rshift__(self,arg):
337 return TimeSeriesShift(self,arg)
338
339 def __neg__(self):
340 return (-1.0)*self
341
342 def __pos__(self):
343 return (1.0)*self
344
345 class TimeSeriesOperator(TimeSeriesControlerView):
346 """a TimeSeriesOperator decribes an operation acting on list of TimeSeries time_series_args. It allows to update its output (if there is any)
347 through the update method which is overwritten by a particular implementation of the class. The update method is called to process the data [start:end] using
348 [start-left_wing_size:end+right_wing_size] of its arguments"""
349 def __init__(self,controler,time_series_args=[],left_wing_size=0,right_wing_size=0,debug=False,description="TimeSeriesOperator"):
350 id_first_datum=controler.getIdOfFirstDatum()
351 for i in time_series_args: id_first_datum=max(id_first_datum,i.getIdOfFirstDatum())
352 TimeSeriesControlerView.__init__(self,id_first_datum+left_wing_size,debug,description)
353 self.__left_wing_size=left_wing_size
354 self.__right_wing_size=right_wing_size
355 self.__time_series_args=time_series_args
356 self.__controler=controler
357 controler.appendOperatorToUpdateList(self)
358 if self.debug(): print "Debug: %s: with left/right wing size %d/%d and %d arguments."%(str(self),left_wing_size,right_wing_size,len(time_series_args))
359
360 def __del__(self):
361 self.getControler().removeOperatorFromUpdateList(self)
362
363 def getControler(self):
364 """returns the Controler updating the TimeSeriesOperator"""
365 return self.__controler
366
367 def getLeftWingSize(self):
368 """returns the left wing size"""
369 return self.__left_wing_size
370
371 def getRightWingSize(self):
372 """returns the right wing size"""
373 return self.__right_wing_size
374
375 def getArguments(self,index=None):
376 """returns the list of arguments or, index is present, the argument with index index. In the latter case None is returned if no arguments are present"""
377 if index==None:
378 return self.__time_series_args
379 else:
380 if len(self.__time_series_args)>0:
381 return self.__time_series_args[index]
382 else:
383 return None
384
385 def getArgumentDataset(self,index):
386 """returns the dataset of in the argument with index index"""
387 arg=self.getArguments(index)
388 if arg==None:
389 return None
390 else:
391 return self.getArguments(index).getDataset()
392
393 def flush(self):
394 """calls the update method with all the maximum processable range. It also updates the id of unused datum for all arguments"""
395 start=self.getIdOfLastProcessedDatum()+1
396 end=self.getControler().getIdOfLastDatum()
397 for i in self.getArguments(): end=min(end,i.getIdOfLastDatum())
398 if start<=end-self.getRightWingSize():
399 if self.debug(): print "Debug: %s: range [%d:%d] is updated."%(self,start,end-self.getRightWingSize())
400 self.update(start,end-self.getRightWingSize()+1)
401 for i in self.getArguments(): i.updateIdOfLastUnreferencedDatum(end-self.getLeftWingSize())
402 self.updateIdOfLastProcessedDatum(end)
403
404 def update(self,start,end):
405 """updates the the data [start:end] using [start-left_wing_size:end+right_wing_size] of its arguments (is overwritten by a particular TimeSeriesOperator)"""
406 pass
407
408
409 class TimeSeriesFilter(TimeSeries,TimeSeriesOperator):
410 """a TimeSeriesFilter is a TimeSeries taht is created trough a TimeSeriesOperator"""
411 def __init__(self,controler,dataset,time_series_args=[],left_wing_size=0,right_wing_size=0,debug=False,description="TimeSeriesFilter"):
412 TimeSeriesOperator.__init__(self,controler,time_series_args,left_wing_size,right_wing_size,debug,description)
413 TimeSeries.__init__(self,dataset,debug,description)
414
415 def update(self,start,end):
416 """appends zeros to the dataset. This method should be overwritten by a particular TimeSeriesFilter"""
417 nc=self.getNumComponents()
418 if nc>1:
419 self.getDataset().append(numarray.zeros([nc,end-start]))
420 else:
421 self.getDataset().append(numarray.zeros(end-start))
422
423 class Controler(TimeSeries):
424 """controls a set of TimeSeries"""
425 def __init__(self,buffer_size=DEFAULT_BUFFER_SIZE,debug=False,description="TimeSeriesControler"):
426 TimeSeries.__init__(self,TimeSeriesBaseBuffer(buffer_size,1,DEFAULT_FLOAT_TYPE,0,debug,"node buffer of "+description),debug,"nodes of "+description)
427 self.setFlushRate()
428 self.__update_time_series=list()
429
430 def getControler(self):
431 """returns the Controler of the time series (overwrites method of by TimeSeries)"""
432 return self
433
434 def setFlushRate(self,rate=50):
435 """set the flush rate, i.e. after rate new time nodes have been checked in the flush method is called."""
436 self.__flush_rate=rate
437 if self.debug(): print "Debug: %s: flush rate is set to %d"%(self,rate)
438
439 def needsFlushing(self):
440 """returns true if the depending TimeSeriesFilters needs to be flushed becuase the time nodes buffer is full or because of the set flush rate"""
441 return self.needsRearrangement(1) or (self.getNumData()+1)%self.__flush_rate==0
442
443 def flush(self):
444 """flushes all dependend TimeSeriesFilters by processing their flush method"""
445 if self.debug(): print "Debug: %s: start flushing"%self
446 for time_serie in self.__update_time_series: time_serie.flush()
447
448 def appendOperatorToUpdateList(self,time_serie):
449 if not time_serie.getControler()==self: raise ValueError,"%s: TimeSeries %s is not defined on this controler."%(self,time_serie)
450 if not self.isEmpty(): raise ValueError,"%s: you can only check in a time series time_serie is controler is empty."%self
451 self.__update_time_series.append(time_serie)
452 if self.debug(): print "Debug: %s: %s has been added to update list."%(self,time_serie)
453
454 def removeOperatorFromUpdateList(self,time_serie):
455 self.__update_time_series.remove(time_serie)
456 if self.debug(): print "Debug: %s: %s has been removed from update list."%(self,time_serie)
457
458 def nextTime(self,value):
459 if self.needsFlushing(): self.flush()
460 self.getDataset().append(value)
461 if self.debug(): print "Debug: %s: new time node %e has been added."%(self,value)
462
463 class TimeSeriesShift(TimeSeries):
464 """creates a shift of the time series, i.e. if d[n] is the datum at time t[n], the value at t[n] becomes v[n+shift] on the output"""
465 def __init__(self,time_serie,shift=1):
466 if shift<0:
467 dsc="(%s)<<%d"%(time_serie,-shift)
468 else:
469 dsc="(%s)>>%d"%(time_serie,shift)
470 self.__controler=time_serie.getControler()
471 TimeSeries.__init__(self,TimeSeriesBaseDataset(time_serie.getDataset(),-shift,time_serie.debug(),"buffer view to "+dsc),time_serie.debug(),dsc)
472
473 def getControler(self):
474 return self.__controler
475
476 class TimeSeriesAdd(TimeSeriesFilter):
477 """adds two TimeSeries"""
478 def __init__(self,time_serie_1,time_serie_2):
479 dsc="(%s)+(%s)"%(time_serie_1,time_serie_2)
480 dbg=time_serie_1.debug() or time_serie_2.debug()
481 cntrl=time_serie_1.getControler()
482 if not cntrl==time_serie_2.getControler():
483 raise ValueError("TimeSeriesAdd: %s and %s have different controler."%(time_serie_1,time_serie_2))
484 id_first_datum=max(time_serie_1.getIdOfFirstDatum(),time_serie_2.getIdOfFirstDatum())
485 TimeSeriesFilter.__init__(self,cntrl, \
486 TimeSeriesBaseBuffer(cntrl.getBaseBufferSize(),time_serie_1.getNumComponents(),DEFAULT_FLOAT_TYPE,id_first_datum,dbg,"buffer for "+dsc), \
487 [time_serie_1,time_serie_2],0,0,dbg,dsc)
488
489 def update(self,start,end):
490 self.append(self.getArgumentDataset(0)[start:end]+self.getArgumentDataset(1)[start:end])
491
492 class TimeSeriesAddScalar(TimeSeriesFilter):
493 """adds a single value to a TimeSeries"""
494 def __init__(self,time_serie,scalar):
495 dsc="(%s)+(%s)"%(time_serie,scalar)
496 dbg=time_serie.debug()
497 cntrl=time_serie.getControler()
498 id_first_datum=time_serie.getIdOfFirstDatum()
499 TimeSeriesFilter.__init__(self,cntrl, \
500 TimeSeriesBaseBuffer(cntrl.getBaseBufferSize(),time_serie.getNumComponents(),DEFAULT_FLOAT_TYPE,id_first_datum,dbg,"buffer for "+dsc), \
501 [time_serie],0,0,dbg,dsc)
502 self.__scalar=scalar
503
504 def update(self,start,end):
505 self.append(self.getArgumentDataset(0)[start:end]+self.__scalar)
506
507 class TimeSeriesMult(TimeSeriesFilter):
508 """multiplies two TimeSeries"""
509 def __init__(self,time_serie_1,time_serie_2):
510 dsc="(%s)*(%s)"%(time_serie_1,time_serie_2)
511 dbg=time_serie_1.debug() or time_serie_2.debug()
512 cntrl=time_serie_1.getControler()
513 if not cntrl==time_serie_2.getControler():
514 raise ValueError("TimeSeriesMult: %s and %s have different controler."%(time_serie_1,time_serie_2))
515 id_first_datum=max(time_serie_1.getIdOfFirstDatum(),time_serie_2.getIdOfFirstDatum())
516 TimeSeriesFilter.__init__(self,cntrl, \
517 TimeSeriesBaseBuffer(cntrl.getBaseBufferSize(),time_serie_1.getNumComponents(),DEFAULT_FLOAT_TYPE,id_first_datum,dbg,"buffer for "+dsc), \
518 [time_serie_1,time_serie_2],0,0,dbg,dsc)
519
520 def update(self,start,end):
521 self.append(self.getArgumentDataset(0)[start:end]*self.getArgumentDataset(1)[start:end])
522
523 class TimeSeriesMultScalar(TimeSeriesFilter):
524 """multiplies a TimeSeries with a single value"""
525 def __init__(self,time_serie,scalar):
526 dsc="(%s)*%s"%(time_serie,scalar)
527 dbg=time_serie.debug()
528 cntrl=time_serie.getControler()
529 id_first_datum=time_serie.getIdOfFirstDatum()
530 TimeSeriesFilter.__init__(self,cntrl, \
531 TimeSeriesBaseBuffer(cntrl.getBaseBufferSize(),time_serie.getNumComponents(),DEFAULT_FLOAT_TYPE,id_first_datum,dbg,"buffer for "+dsc), \
532 [time_serie],0,0,dbg,dsc)
533 self.__scalar=scalar
534
535 def update(self,start,end):
536 self.append(self.getArgumentDataset(0)[start:end]*self.__scalar)
537
538 class TimeSeriesDiv(TimeSeriesFilter):
539 """divides two TimeSeries"""
540 def __init__(self,time_serie_1,time_serie_2):
541 dsc="(%s)/(%s)"%(time_serie_1,time_serie_2)
542 dbg=time_serie_1.debug() or time_serie_2.debug()
543 cntrl=time_serie_1.getControler()
544 if not cntrl==time_serie_2.getControler():
545 raise ValueError("TimeSeriesDiv: %s and %s have different controler."%(time_serie_1,time_serie_2))
546 id_first_datum=max(time_serie_1.getIdOfFirstDatum(),time_serie_2.getIdOfFirstDatum())
547 TimeSeriesFilter.__init__(self,cntrl, \
548 TimeSeriesBaseBuffer(cntrl.getBaseBufferSize(),time_serie_1.getNumComponents(),DEFAULT_FLOAT_TYPE,id_first_datum,dbg,"buffer for "+dsc), \
549 [time_serie_1,time_serie_2],0,0,dbg,dsc)
550
551 def update(self,start,end):
552 self.append(self.getArgumentDataset(0)[start:end]/self.getArgumentDataset(1)[start:end])
553
554 class TimeSeriesDivScalar(TimeSeriesFilter):
555 """divides a scalar be a TimeSerie"""
556 def __init__(self,time_serie,scalar):
557 dsc="(%s)/(%s)"%(scalar,time_serie)
558 dbg=time_serie.debug()
559 cntrl=time_serie.getControler()
560 id_first_datum=time_serie.getIdOfFirstDatum()
561 TimeSeriesFilter.__init__(self,cntrl, \
562 TimeSeriesBaseBuffer(cntrl.getBaseBufferSize(),time_serie.getNumComponents(),DEFAULT_FLOAT_TYPE,id_first_datum,dbg,"buffer for "+dsc), \
563 [time_serie],0,0,dbg,dsc)
564 self.__scalar=scalar
565
566 def update(self,start,end):
567 self.append(self.__scalar/self.getArgumentDataset(0)[start:end])
568
569 class TimeSeriesPower(TimeSeriesFilter):
570 """raise one TimeSeries to the power of an other TimeSeries"""
571 def __init__(self,time_serie_1,time_serie_2):
572 dsc="(%s)**(%s)"%(time_serie_1,time_serie_2)
573 dbg=time_serie_1.debug() or time_serie_2.debug()
574 cntrl=time_serie_1.getControler()
575 if not cntrl==time_serie_2.getControler():
576 raise ValueError("TimeSeriesPower: %s and %s have different controler."%(time_serie_1,time_serie_2))
577 id_first_datum=max(time_serie_1.getIdOfFirstDatum(),time_serie_2.getIdOfFirstDatum())
578 TimeSeriesFilter.__init__(self,cntrl, \
579 TimeSeriesBaseBuffer(cntrl.getBaseBufferSize(),time_serie_1.getNumComponents(),DEFAULT_FLOAT_TYPE,id_first_datum,dbg,"buffer for "+dsc), \
580 [time_serie_1,time_serie_2],0,0,dbg,dsc)
581
582 def update(self,start,end):
583 self.append(self.getArgumentDataset(0)[start:end]**self.getArgumentDataset(1)[start:end])
584
585 class TimeSeriesPowerScalar(TimeSeriesFilter):
586 """raises a TimeSerie to the power of a scalar"""
587 def __init__(self,time_serie,scalar):
588 dsc="(%s)**(%s)"%(time_serie,scalar)
589 dbg=time_serie.debug()
590 cntrl=time_serie.getControler()
591 id_first_datum=time_serie.getIdOfFirstDatum()
592 TimeSeriesFilter.__init__(self,cntrl, \
593 TimeSeriesBaseBuffer(cntrl.getBaseBufferSize(),time_serie.getNumComponents(),DEFAULT_FLOAT_TYPE,id_first_datum,dbg,"buffer for "+dsc), \
594 [time_serie],0,0,dbg,dsc)
595 self.__scalar=scalar
596
597 def update(self,start,end):
598 self.append(self.getArgumentDataset(0)[start:end]**self.__scalar)
599
600 class Exp(TimeSeriesFilter):
601 """"""
602 def __init__(self,time_serie):
603 dsc="exp(%s)"%(time_serie)
604 dbg=time_serie.debug()
605 cntrl=time_serie.getControler()
606 id_first_datum=time_serie.getIdOfFirstDatum()
607 TimeSeriesFilter.__init__(self,cntrl, \
608 TimeSeriesBaseBuffer(cntrl.getBaseBufferSize(),time_serie.getNumComponents(),DEFAULT_FLOAT_TYPE,id_first_datum,dbg,"buffer for "+dsc), \
609 [time_serie],0,0,dbg,dsc)
610
611 def update(self,start,end):
612 self.append(numarray.exp(self.getArgumentDataset(0)[start:end]))
613
614 class Writer(TimeSeriesOperator):
615 """writes the time series into an output strim ostream which mast have the writeline method. The values are seperated by the string seperator."""
616 def __init__(self,time_serie,ostream,seperator=",",commend_tag="#"):
617 dsc="write %s to %s"%(time_serie,ostream)
618 dbg=time_serie.debug()
619 cntrl=time_serie.getControler()
620 self.__ostream=ostream
621 self.__seperator=seperator
622 TimeSeriesOperator.__init__(self,cntrl,[time_serie],0,0,dbg,dsc)
623 ostream.writelines("%s time series %s\n"%(commend_tag,str(self)))
624
625 def update(self,start,end):
626 cntrl=self.getControler()
627 arg=self.getArguments(0)
628 n=arg.getNumComponents()
629 if n<2:
630 for i in range(start,end): self.__ostream.writelines("%s%s%s\n"%(cntrl[i],self.__seperator,arg[i]))
631 else:
632 for i in range(start,end):
633 l="%s"%cntrl[i]
634 for j in range(n): l=l+"%s%s"(self.__seperator,arg[i][j])
635 self.__ostream.writelines("%s\n"%l)
636
637 class DataCatcher(TimeSeries):
638 """collects data into a time series."""
639 def __init__(self,controler,numComponents=1,description="DataCatcher"):
640 self.__controler=controler
641 dbg=controler.debug()
642 TimeSeries.__init__(self,TimeSeriesBaseBuffer(controler.getBaseBufferSize(),numComponents,DEFAULT_FLOAT_TYPE,controler.getIdOfFirstDatum(),dbg,"buffer for "+description),dbg,description)
643
644 def getControler(self):
645 return self.__controler
646
647 def nextValue(self,value):
648 """append a value to the time series"""
649 id_last=self.getIdOfLastDatum()
650 id_current=self.getControler().getIdOfLastDatum()
651 if id_last+1==id_current:
652 self.getDataset().append(value)
653 elif id_last+1<id_current:
654 if self.isEmpty():
655 self.getDataset().append(value)
656 id_last+=1
657 t_last=self.getControler()[id_last]
658 t_current=self.getControler()[id_current]
659 value_last=self[id_last]
660 out=(value_last-value)/(t_last-t_current)*(self.getControler()[id_last+1:id_current+1]-t_current)+value
661 self.getDataset().append(out)
662 else :
663 raise ValueError,"%s: a new time node must be introduced before a new value can be added."
664 self.updateIdOfLastUnreferencedDatum(id_last)
665
666
667 class TimeSeriesCumulativeSum(TimeSeriesFilter):
668 """cummulative sum of the time series values"""
669 def __init__(self,time_serie):
670 dsc="cumsum(%s)"%(time_serie)
671 dbg=time_serie.debug()
672 cntrl=time_serie.getControler()
673 id_first_datum=time_serie.getIdOfFirstDatum()
674 TimeSeriesFilter.__init__(self,cntrl, \
675 TimeSeriesBaseBuffer(cntrl.getBaseBufferSize(),time_serie.getNumComponents(),DEFAULT_FLOAT_TYPE,id_first_datum,dbg,"buffer for "+dsc), \
676 [time_serie],0,0,dbg,dsc)
677 self.__last_value=0
678
679 def update(self,start,end):
680 out=numarray.cumsum(self.getArgumentDataset(0)[start:end])+self.__last_value
681 self.__last_value=out[end-start-1]
682 self.append(out)
683
684
685 class Reader(TimeSeriesBase):
686 """reads a list of input streams and creates a time series for each input stream but on the same Controler where the first column
687 is used to create the time nodes"""
688 def __init__(self,list_of_istreams,buffer_size=DEFAULT_BUFFER_SIZE,seperator=",",commend_tag="#",debug=False):
689 TimeSeriesBase.__init__(self,debug=debug,description="reader")
690 if not isinstance(list_of_istreams,list):
691 self.__list_of_istreams=[list_of_istreams]
692 else:
693 self.__list_of_istreams=list_of_istreams
694 self.__cntrl=Controler(buffer_size,debug,"reader controler")
695 self.__seperator=seperator
696 self.__commend_tag=commend_tag
697 self.__time_series={}
698 self.__t={}
699 self.__v={}
700 # set up the time series:
701 for i in self.__list_of_istreams:
702 line=self.__commend_tag
703 while not line=="" and line[0]==self.__commend_tag:
704 line=i.readline().strip()
705 if line=="":
706 list_of_istreams.remove(i)
707 else:
708 d=line.split(self.__seperator)
709 self.__t[i]=float(d[0])
710 tmp=[]
711 for j in d[1:]: tmp.append(float(j))
712 self.__v[i]=numarray.array(tmp)
713 self.__time_series[i]=DataCatcher(self.__cntrl,len(d)-1,str(i))
714
715 #
716 def run(self):
717 while len(self.__list_of_istreams)>0:
718 if len(self.__time_series)>0:
719 # find list all times with minumum time node:
720 tminargs=[]
721 for i in self.__time_series:
722 if len(tminargs)==0:
723 tminargs.append(i)
724 elif abs(t[tminargs[0]]-self.__t[i])<1.e-8*abs(self.__t[i]):
725 tminargs.append(i)
726 elif self.__t[i]<t[tminargs[0]]:
727 tminargs=[i]
728 # find list all times with minumum time node:
729 self.__cntrl.nextTime(self.__t[tminargs[0]])
730 for i in tminargs:
731 self.__time_series[i].nextValue(self.__v[i])
732 # find next line without leading "#"
733 line="#"
734 while not line=="" and line[0]==self.__commend_tag:
735 line=i.readline().strip()
736 # if eof reached iostream is removed for searching
737 if line=="":
738 self.__list_of_istreams.remove(i)
739 else:
740 d=line.split(self.__seperator)
741 self.__t[i]=float(d[0])
742 tmp=[]
743 for j in d[1:]: tmp.append(float(j))
744 self.__v[i]=numarray.array(tmp)
745
746 def getControler(self):
747 """returns the controler shared by all time series created through the input streams"""
748 return self.__cntrl
749
750 def getTimeSeries(self,istream=None):
751 """returns the time series as a tuple. If istream is present its time series is returned"""
752 if istream==None:
753 out=self.__time_series.values()
754 if len(out)>1:
755 return tuple(out)
756 elif len(out)>0:
757 return out[0]
758 else:
759 return None
760 else:
761 return self.__time_series[istream]
762
763
764 class Plotter(TimeSeriesOperator):
765 def __init__(self,time_series,window_size=DEFAULT_BUFFER_SIZE/4,file_name=None,format=None):
766 if isinstance(time_series,list):
767 dbg=time_series[0].getControler().debug()
768 text=""
769 for i in time_series:
770 if len(text)==0:
771 text=str(i)
772 else:
773 text=text+","+str(i)
774 TimeSeriesOperator.__init__(self,time_series[0].getControler(),time_series,window_size,0,dbg,"plot(%s)"%text)
775 else:
776 dbg=time_series.getControler().debug()
777 text=str(time_series)
778 TimeSeriesOperator.__init__(self,time_series.getControler(),[time_series],window_size,0,dbg,"plot(%s)"%text)
779 from pyvisi.renderers.gnuplot import LinePlot,Scene,PsImage
780 self.__renderer=Scene()
781 self.__line_plot=LinePlot(self.__renderer)
782 self.__line_plot.setTitle(text)
783 self.__line_plot.setLineStyle("lines")
784 self.__line_plot.setXLabel("time")
785 self.__line_plot.setYLabel("values")
786 self.__file_name=file_name
787 if format==None:
788 self.__format=PsImage()
789 else:
790 self.__format=format
791 self.__window_size=window_size
792
793 def update(self,start,end):
794 s=max(end-self.__window_size,self.getControler().getIdOfFirstAvailableDatum())
795 args=[self.getControler()[s:end]]
796 for arg in self.getArguments(): args.append(arg[s:end])
797 self.__line_plot.setData(*args)
798 self.__line_plot.render()
799 if self.__file_name==None:
800 raise SystemError,"Online viewing is not avilabel yet!"
801 else:
802 self.__renderer.save(fname=self.__file_name, format=self.__format)
803
804
805 def viewer(time_serie,seperator=","):
806 """creates a viewer for a time series"""
807 import sys
808 return Writer(time_serie,sys.stdout,seperator)
809
810 def differential(time_serie):
811 """calculates the derivative Dv of the time series v:
812
813 Dv[n]=(v[n]-v[n-1])/(t[n]-t[n-1])
814
815 """
816 out=(((time_serie<<1)-time_serie)/((time_serie.getControler()<<1)-time_serie.getControler())+ \
817 ((time_serie>>1)-time_serie)/((time_serie.getControler()>>1)-time_serie.getControler()))/2.
818 out.setDescription("d(%s)/dt"%str(time_serie))
819 out.setDebug(time_serie.debug())
820 return out
821
822 def integral(time_serie):
823 """calculates the intagral Iv of the time series v using the trapozidal rule:
824
825 Iv[n]=int_{t_0}^{t_n} v ~ sum_{0<i<=n} n (v[i]+v[i-1])/2*(t[i]-t[i-1])
826
827 """
828 out=TimeSeriesCumulativeSum(((time_serie>>1)+time_serie)/2.*(time_serie.getControler()-(time_serie.getControler()>>1)))
829 out.setDescription("I (%s) dt"%str(time_serie))
830 out.setDebug(time_serie.debug())
831 return out
832
833 def smooth(time_serie,range=5):
834 """smoothes a time series using the at each time the previous and next range values"""
835 i=integral(time_serie)
836 out=((i>>range)-(i<<range))/((time_serie.getControler()>>range)-(time_serie.getControler()<<range))
837 out.setDescription("smooth(%s,-%d:%d) dt"%(str(time_serie),range,range))
838 out.setDebug(time_serie.debug())
839 return out
840
841 def leakySmooth(time_serie,l=0.99):
842 """leaky smoother: s(t)=int_{t_0}^{t} v(r) l^{t-r} dr/ int_{t_0}^{t} l^{t-r} dr """
843 w=l**(-time_serie.getControler())
844 out=integrate(time_serie*w)/integrate(w)
845 out.setDescription("leaky smoother(%s)"%str(time_serie))
846 return out
847
848 # test
849
850 if __name__=="__main__":
851 # tests the interfaces to data sets:
852 print "Test of Datasets:"
853 print "================="
854 bf=TimeSeriesBaseBuffer(buffer_size=5,numComponents=1,debug=True,description="TestBaseBuffer")
855 bfv_l=TimeSeriesBaseDataset(bf,offset=1,debug=True,description="offset 1")
856 bfv_r=TimeSeriesBaseDataset(bf,offset=-1,debug=True,description="offset -1")
857 bf.append([1.,2.,3.,4.])
858 print "should be all 2. :",bfv_l[0]
859 print bf[1]
860 print bfv_r[2]
861 bf.append([5.,6.,7.])
862 print "should be all 5. :",bfv_l[3],bf[4],bfv_r[5]
863 print "should be all 6. :",bfv_l[4],bf[5],bfv_r[6]
864 print "should be all 7. :",bfv_l[5],bf[6],bfv_r[7]
865 print "should be all [6., 7.] :",bfv_l[4:6],bf[5:7],bfv_r[6:8]
866
867 print "Test of Controler"
868 print "================="
869 b=Controler(buffer_size=15,debug=True)
870 s3=b>>3
871 s1=b>>1
872 s_3=b<<3
873 print s_3
874 print b
875 print b+s3
876 sum=(s_3+b)+(b+s3)
877
878 for i in range(30):
879 b.nextTime(i*1.)
880 b.flush()
881 print "should be all 28. :",s_3.getDataset()[25],b.getDataset()[28],s3.getDataset()[31]
882 print "should be all 29. :",s_3.getDataset()[26],b.getDataset()[29],s3.getDataset()[32]
883 print "should be all 96. :",sum.getDataset()[24]
884
885 print "Test of operators"
886 print "================="
887 b=Controler(buffer_size=15,debug=True)
888 b.setFlushRate(2)
889 q=DataCatcher(b)
890 b1=b<<1
891 a=b+b1
892 a_s=b1+1.
893 s_a=1.+b1
894 d=b-b1
895 d_s=b1-1.
896 s_d=1.-b1
897 m=b*b1
898 m_s=b1*2.
899 s_m=2.*b1
900 dv=b/b1
901 dv_s=b1/2.
902 s_dv=2./b1
903 p=b**b1
904 p_s=b1**2.
905 s_p=2.**b1
906 pb=+b
907 mb=-b
908 sum=TimeSeriesCumulativeSum(b)
909 diff=differential(b)
910 smt=smooth(b,2)
911 int=integral(b*2)
912 fl=file("/tmp/test.csv","w")
913 w=Writer(q,fl)
914 v=viewer(q)
915 plo=Plotter([a,a_s],window_size=4,file_name="s.ps")
916 for i in range(30):
917 b.nextTime(i*1.)
918 if i%2==1: q.nextValue(i*28.)
919 b.flush()
920 print "a[28] should be %e: %e"%(28.+29.,a[28])
921 print "a_s[28] should be %e: %e"%(29.+1.,a_s[28])
922 print "s_a[28] should be %e: %e"%(29.+1.,s_a[28])
923 print "d[28] should be %e: %e"%(28.-29.,d[28])
924 print "d_s[28] should %e: %e"%(29.-1.,d_s[28])
925 print "s_d[28] should %e: %e"%(1.-29.,s_d[28])
926 print "m[28] should be %e: %e"%(28.*29.,m[28])
927 print "m_s[28] should be %e: %e"%(29.*2.,m_s[28])
928 print "s_m[28] should be %e: %e"%(29.*2.,s_m[28])
929 print "dv[28] should be %e: %e"%(28./29.,dv[28])
930 print "dv_s[28] should be %e: %e"%(29./2.,dv_s[28])
931 print "s_dv[28] should be %e: %e"%(2./29.,s_dv[28])
932 print "p[28] should be %e: %e"%(28.**29.,p[28])
933 print "p_s[28] should be %e: %e"%(29.**2,p_s[28])
934 print "s_p[28] should be %e: %e"%(2.**29.,s_p[28])
935 print "pb[28] should be %e: %e"%(28.,pb[28])
936 print "mb[28] should be %e: %e"%(-28.,mb[28])
937 print "sum[28] should be %e: %e"%(28*29./2,sum[28])
938 print "diff[28] should be %e: %e"%(1.,diff[28])
939 print "smt[27] should be %e: %e"%(27.,smt[27])
940 print "int[28] should be %e: %e"%(28.**2,int[28])
941 print "q[27] should be %e: %e"%(27*28.,q[27])
942 print "q[28] should be %e: %e"%(28*28.,q[28])
943 print "q[29] should be %e: %e"%(29*28.,q[29])
944 fl.flush()
945
946 rin=Reader(file("/tmp/test.csv","r+"),buffer_size=15,debug=True)
947 rin.run()
948 inp=rin.getTimeSeries()
949 print "inp[27] should be %e: %e"%(27*28.,inp[27])
950 print "inp[28] should be %e: %e"%(28*28.,inp[28])
951 print "inp[29] should be %e: %e"%(29*28.,inp[29])
952

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

  ViewVC Help
Powered by ViewVC 1.1.26