1 """
2 This file contains the classes for recording simulation results, Histogram,
3 Monitor and Tally.
4 """
5
6
7
8
9 import SimPy.Globals as Globals
10
11
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
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
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
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
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
105 """record y and t"""
106 if t is None: t = self.sim.now()
107 self.append([t, y])
108
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
124 """ the series of measured times"""
125 return list(zip(*self)[0])
126
128 """ the series of measured values"""
129 return list(zip(*self)[1])
130
132 """ deprecated: the number of observations made """
133 return self.__len__()
134
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
143
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
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
156 try: return (ssq - float(tot * tot) / n) / n
157 except: print 'SimPy: No observations for sample variance'
158
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
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
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
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
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
279 - def __init__(self, name = 'a_Tally', ylab = 'y', tlab = 't', sim = None):
280 if not sim: sim = Globals.sim
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
291 self._integral2 = 0.0
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
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
335
338
340 return 1.0 * self._total / self._count
341
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
353 return 1.0 * (self._sum_of_squares - (1.0 * (self._sum * self._sum)\
354 / self._count)) / (self._count)
355
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
366 last = self._last_observation
367 twinteg2 = self._integral2 + (t - self._last_timestamp) * last * last
368
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
379
381 return len(l) == self._count
382
385
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