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

Source Code for Module SimPy.Recording

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