Package SimPy :: Module Recording
[hide private]
[frames] | no frames]

Source Code for Module SimPy.Recording

  1  # coding=utf-8 
  2  """ 
  3  This file contains the classes for recording simulation results, Histogram, 
  4  Monitor and Tally. 
  5  """ 
  6  # $Revision: 504 $ $Date: 2010-05-11 08:29:10 +0200 (Tue, 11 May 2010) $ 
  7  # SimPy version: 2.1.0 
  8   
  9  # Required for backward compatibility 
 10  import SimPy.Globals as Globals 
 11   
 12   
13 -class Histogram(list):
14 """ A histogram gathering and sampling class""" 15
16 - def __init__(self, name = '', low = 0.0, high = 100.0, nbins = 10):
17 list.__init__(self) 18 self.name = name 19 self.low = float(low) 20 self.high = float(high) 21 self.nbins = nbins 22 self.binsize = (self.high - self.low) / nbins 23 self._nrObs = 0 24 self._sum = 0 25 self[:] = [[low + (i - 1) * self.binsize, 0] for i in range(self.nbins + 2)]
26
27 - def addIn(self, y):
28 """ add a value into the correct bin""" 29 self._nrObs += 1 30 self._sum += y 31 b = int((y - self.low + self.binsize) / self.binsize) 32 if b < 0: b = 0 33 if b > self.nbins + 1: b = self.nbins + 1 34 assert 0 <= b <=self.nbins + 1, 'Histogram.addIn: b out of range: %s'%b 35 self[b][1] += 1
36
37 - def __str__(self):
38 histo = self 39 ylab = 'value' 40 nrObs = self._nrObs 41 width = len(str(nrObs)) 42 res = [] 43 res.append(' < Histogram %s:'%self.name) 44 res.append('\nNumber of observations: %s'%nrObs) 45 if nrObs: 46 su = self._sum 47 cum = histo[0][1] 48 fmt = '%s' 49 line = '\n%s <= %s < %s: %s (cum: %s/%s%s)'\ 50 %(fmt, '%s', fmt, '%s', '%s', '%5.1f', '%s') 51 line1 = '\n%s%s < %s: %s (cum: %s/%s%s)'\ 52 %('%s', '%s', fmt, '%s', '%s', '%5.1f', '%s') 53 l1width = len(('%s <= '%fmt)%histo[1][0]) 54 res.append(line1\ 55 %(' ' * l1width, ylab, histo[1][0], str(histo[0][1]).rjust(width),\ 56 str(cum).rjust(width),(float(cum) / nrObs) * 100, '%') 57 ) 58 for i in range(1, len(histo) - 1): 59 cum += histo[i][1] 60 res.append(line\ 61 %(histo[i][0], ylab, histo[i + 1][0], str(histo[i][1]).rjust(width),\ 62 str(cum).rjust(width),(float(cum) / nrObs) * 100, '%') 63 ) 64 cum += histo[-1][1] 65 linen = '\n%s <= %s %s : %s (cum: %s/%s%s)'\ 66 %(fmt, '%s', '%s', '%s', '%s', '%5.1f', '%s') 67 lnwidth = len(('<%s'%fmt)%histo[1][0]) 68 res.append(linen\ 69 %(histo[-1][0], ylab, ' ' * lnwidth, str(histo[-1][1]).rjust(width),\ 70 str(cum).rjust(width),(float(cum) / nrObs) * 100, '%') 71 ) 72 res.append('\n > ') 73 return ' '.join(res)
74 75
76 -class Monitor(list):
77 """ Monitored variables 78 79 A Class for monitored variables, that is, variables that allow one 80 to gather simple statistics. A Monitor is a subclass of list and 81 list operations can be performed on it. An object is established 82 using m = Monitor(name = '..'). It can be given a 83 unique name for use in debugging and in tracing and ylab and tlab 84 strings for labelling graphs. 85 """
86 - def __init__(self, name = 'a_Monitor', ylab = 'y', tlab = 't', sim = None):
87 list.__init__(self) 88 if not sim: sim = Globals.sim # Use global simulation if sim is None 89 self.sim = sim 90 self.startTime = 0.0 91 self.name = name 92 self.ylab = ylab 93 self.tlab = tlab 94 self.sim.allMonitors.append(self)
95
96 - def setHistogram(self, name = '', low = 0.0, high = 100.0, nbins = 10):
97 """Sets histogram parameters. 98 Must be called before call to getHistogram""" 99 if name == '': 100 histname = self.name 101 else: 102 histname = name 103 self.histo = Histogram(name = histname, low = low, high = high, nbins = nbins)
104
105 - def observe(self, y,t = None):
106 """record y and t""" 107 if t is None: t = self.sim.now() 108 self.append([t, y])
109
110 - def tally(self, y):
111 """ deprecated: tally for backward compatibility""" 112 self.observe(y, 0)
113
114 - def accum(self, y,t = None):
115 """ deprecated: accum for backward compatibility""" 116 self.observe(y, t)
117
118 - def reset(self, t = None):
119 """reset the sums and counts for the monitored variable """ 120 self[:] = [] 121 if t is None: t = self.sim.now() 122 self.startTime = t
123
124 - def tseries(self):
125 """ the series of measured times""" 126 return list(zip(*self)[0])
127
128 - def yseries(self):
129 """ the series of measured values""" 130 return list(zip(*self)[1])
131
132 - def count(self):
133 """ deprecated: the number of observations made """ 134 return self.__len__()
135
136 - def total(self):
137 """ the sum of the y""" 138 if self.__len__() == 0: return 0 139 else: 140 sum = 0.0 141 for i in range(self.__len__()): 142 sum += self[i][1] 143 return sum # replace by sum() later
144
145 - def mean(self):
146 """ the simple average of the monitored variable""" 147 try: return 1.0 * self.total() / self.__len__() 148 except: print 'SimPy: No observations for mean'
149
150 - def var(self):
151 """ the sample variance of the monitored variable """ 152 n = len(self) 153 tot = self.total() 154 ssq = 0.0 155 for i in range(self.__len__()): 156 ssq += self[i][1] ** 2 # replace by sum() eventually 157 try: return (ssq - float(tot * tot) / n) / n 158 except: print 'SimPy: No observations for sample variance'
159
160 - def timeAverage(self, t = None):
161 """ the time - weighted average of the monitored variable. 162 163 If t is used it is assumed to be the current time, 164 otherwise t = self.sim.now() 165 """ 166 N = self.__len__() 167 if N == 0: 168 print 'SimPy: No observations for timeAverage' 169 return None 170 171 if t is None: t = self.sim.now() 172 sum = 0.0 173 tlast = self[0][0] 174 ylast = self[0][1] 175 for i in range(N): 176 ti, yi = self[i] 177 sum += ylast * (ti - tlast) 178 tlast = ti 179 ylast = yi 180 sum += ylast * (t - tlast) 181 T = t - self[0][0] 182 if T == 0: 183 print 'SimPy: No elapsed time for timeAverage' 184 return None 185 return sum / float(T)
186
187 - def timeVariance(self, t = None):
188 """ the time - weighted Variance of the monitored variable. 189 190 If t is used it is assumed to be the current time, 191 otherwise t = self.sim.now() 192 """ 193 N = self.__len__() 194 if N == 0: 195 print 'SimPy: No observations for timeVariance' 196 return None 197 if t is None: t = self.sim.now() 198 sm = 0.0 199 ssq = 0.0 200 tlast = self[0][0] 201 # print 'DEBUG: 1 twVar ', t, tlast 202 ylast = self[0][1] 203 for i in range(N): 204 ti, yi = self[i] 205 sm += ylast * (ti - tlast) 206 ssq += ylast * ylast * (ti - tlast) 207 tlast = ti 208 ylast = yi 209 sm += ylast * (t - tlast) 210 ssq += ylast * ylast * (t - tlast) 211 T = t - self[0][0] 212 if T == 0: 213 print 'SimPy: No elapsed time for timeVariance' 214 return None 215 mn = sm / float(T) 216 return ssq / float(T) - mn * mn
217 218
219 - def histogram(self, low = 0.0, high = 100.0, nbins = 10):
220 """ A histogram of the monitored y data values. 221 """ 222 h = Histogram(name = self.name, low = low, high = high, nbins = nbins) 223 ys = self.yseries() 224 for y in ys: h.addIn(y) 225 return h
226
227 - def getHistogram(self):
228 """Returns a histogram based on the parameters provided in 229 preceding call to setHistogram. 230 """ 231 ys = self.yseries() 232 h = self.histo 233 for y in ys: h.addIn(y) 234 return h
235
236 - def printHistogram(self, fmt = '%s'):
237 """Returns formatted frequency distribution table string from Monitor. 238 Precondition: setHistogram must have been called. 239 fmt == format of bin range values 240 """ 241 try: 242 histo = self.getHistogram() 243 except: 244 raise FatalSimerror('histogramTable: call setHistogram first'\ 245 ' for Monitor %s'%self.name) 246 ylab = self.ylab 247 nrObs = self.count() 248 width = len(str(nrObs)) 249 res = [] 250 res.append('\nHistogram for %s:'%histo.name) 251 res.append('\nNumber of observations: %s'%nrObs) 252 su = sum(self.yseries()) 253 cum = histo[0][1] 254 line = '\n%s <= %s < %s: %s (cum: %s/%s%s)'\ 255 %(fmt, '%s', fmt, '%s', '%s', '%5.1f', '%s') 256 line1 = '\n%s%s < %s: %s (cum: %s/%s%s)'\ 257 %('%s', '%s', fmt, '%s', '%s', '%5.1f', '%s') 258 l1width = len(('%s <= '%fmt)%histo[1][0]) 259 res.append(line1\ 260 %(' ' * l1width, ylab, histo[1][0], str(histo[0][1]).rjust(width),\ 261 str(cum).rjust(width),(float(cum) / nrObs) * 100, '%') 262 ) 263 for i in range(1, len(histo) - 1): 264 cum += histo[i][1] 265 res.append(line\ 266 %(histo[i][0], ylab, histo[i + 1][0], str(histo[i][1]).rjust(width),\ 267 str(cum).rjust(width),(float(cum) / nrObs) * 100, '%') 268 ) 269 cum += histo[-1][1] 270 linen = '\n%s <= %s %s : %s (cum: %s/%s%s)'\ 271 %(fmt, '%s', '%s', '%s', '%s', '%5.1f', '%s') 272 lnwidth = len(('<%s'%fmt)%histo[1][0]) 273 res.append(linen\ 274 %(histo[-1][0], ylab, ' ' * lnwidth, str(histo[-1][1]).rjust(width),\ 275 str(cum).rjust(width),(float(cum) / nrObs) * 100, '%') 276 ) 277 return ' '.join(res)
278
279 -class Tally:
280 - def __init__(self, name = 'a_Tally', ylab = 'y', tlab = 't', sim = None):
281 if not sim: sim = Globals.sim # use global simulation if sim is None 282 self.sim = sim 283 self.name = name 284 self.ylab = ylab 285 self.tlab = tlab 286 self.reset() 287 self.startTime = 0.0 288 self.histo = None 289 self.sum = 0.0 290 self._sum_of_squares = 0 291 self._integral = 0.0 # time - weighted sum 292 self._integral2 = 0.0 # time - weighted sum of squares 293 self.sim.allTallies.append(self)
294
295 - def setHistogram(self, name = '', low = 0.0, high = 100.0, nbins = 10):
296 """Sets histogram parameters. 297 Must be called to prior to observations initiate data collection 298 for histogram. 299 """ 300 if name == '': 301 hname = self.name 302 else: 303 hname = name 304 self.histo = Histogram(name = hname, low = low, high = high, nbins = nbins)
305
306 - def observe(self, y, t = None):
307 if t is None: 308 t = self.sim.now() 309 self._integral += (t - self._last_timestamp) * self._last_observation 310 yy = self._last_observation * self._last_observation 311 self._integral2 += (t - self._last_timestamp) * yy 312 self._last_timestamp = t 313 self._last_observation = y 314 self._total += y 315 self._count += 1 316 self._sum += y 317 self._sum_of_squares += y * y 318 if self.histo: 319 self.histo.addIn(y)
320
321 - def reset(self, t = None):
322 if t is None: 323 t = self.sim.now() 324 self.startTime = t 325 self._last_timestamp = t 326 self._last_observation = 0.0 327 self._count = 0 328 self._total = 0.0 329 self._integral = 0.0 330 self._integral2 = 0.0 331 self._sum = 0.0 332 self._sum_of_squares = 0.0
333
334 - def count(self):
335 return self._count
336
337 - def total(self):
338 return self._total
339
340 - def mean(self):
341 return 1.0 * self._total / self._count
342
343 - def timeAverage(self, t = None):
344 if t is None: 345 t = self.sim.now() 346 integ = self._integral + (t - self._last_timestamp) * self._last_observation 347 if (t > self.startTime): 348 return 1.0 * integ / (t - self.startTime) 349 else: 350 print 'SimPy: No elapsed time for timeAverage' 351 return None
352
353 - def var(self):
354 return 1.0 * (self._sum_of_squares - (1.0 * (self._sum * self._sum)\ 355 / self._count)) / (self._count)
356
357 - def timeVariance(self, t = None):
358 """ the time - weighted Variance of the Tallied variable. 359 360 If t is used it is assumed to be the current time, 361 otherwise t = self.sim.now() 362 """ 363 if t is None: 364 t = self.sim.now() 365 twAve = self.timeAverage(t) 366 #print 'Tally timeVariance DEBUG: twave:', twAve 367 last = self._last_observation 368 twinteg2 = self._integral2 + (t - self._last_timestamp) * last * last 369 #print 'Tally timeVariance DEBUG:tinteg2:', twinteg2 370 if (t > self.startTime): 371 return 1.0 * twinteg2 / (t - self.startTime) - twAve * twAve 372 else: 373 print 'SimPy: No elapsed time for timeVariance' 374 return None
375 376 377
378 - def __len__(self):
379 return self._count
380
381 - def __eq__(self, l):
382 return len(l) == self._count
383
384 - def getHistogram(self):
385 return self.histo
386
387 - def printHistogram(self, fmt = '%s'):
388 """Returns formatted frequency distribution table string from Tally. 389 Precondition: setHistogram must have been called. 390 fmt == format of bin range values 391 """ 392 try: 393 histo = self.getHistogram() 394 except: 395 raise FatalSimerror('histogramTable: call setHistogram first'\ 396 ' for Tally %s'%self.name) 397 ylab = self.ylab 398 nrObs = self.count() 399 width = len(str(nrObs)) 400 res = [] 401 res.append('\nHistogram for %s:'%histo.name) 402 res.append('\nNumber of observations: %s'%nrObs) 403 su = self.total() 404 cum = histo[0][1] 405 line = '\n%s <= %s < %s: %s (cum: %s/%s%s)'\ 406 %(fmt, '%s', fmt, '%s', '%s', '%5.1f', '%s') 407 line1 = '\n%s%s < %s: %s (cum: %s/%s%s)'\ 408 %('%s', '%s', fmt, '%s', '%s', '%5.1f', '%s') 409 l1width = len(('%s <= '%fmt)%histo[1][0]) 410 res.append(line1\ 411 %(' ' * l1width, ylab, histo[1][0], str(histo[0][1]).rjust(width),\ 412 str(cum).rjust(width),(float(cum) / nrObs) * 100, '%') 413 ) 414 for i in range(1, len(histo) - 1): 415 cum += histo[i][1] 416 res.append(line\ 417 %(histo[i][0], ylab, histo[i + 1][0], str(histo[i][1]).rjust(width),\ 418 str(cum).rjust(width),(float(cum) / nrObs) * 100, '%') 419 ) 420 cum += histo[-1][1] 421 linen = '\n%s <= %s %s : %s (cum: %s/%s%s)'\ 422 %(fmt, '%s', '%s', '%s', '%s', '%5.1f', '%s') 423 lnwidth = len(('<%s'%fmt)%histo[1][0]) 424 res.append(linen\ 425 %(histo[-1][0], ylab, ' ' * lnwidth, str(histo[-1][1]).rjust(width),\ 426 str(cum).rjust(width),(float(cum) / nrObs) * 100, '%') 427 ) 428 return ' '.join(res)
429