1
2 """
3 This file contains the classes for recording simulation results, Histogram,
4 Monitor and Tally.
5 """
6
7
8
9
10 import SimPy.Globals as Globals
11
12
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
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
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
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
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
106 """record y and t"""
107 if t is None: t = self.sim.now()
108 self.append([t, y])
109
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
125 """ the series of measured times"""
126 return list(zip(*self)[0])
127
129 """ the series of measured values"""
130 return list(zip(*self)[1])
131
133 """ deprecated: the number of observations made """
134 return self.__len__()
135
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
144
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
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
157 try: return (ssq - float(tot * tot) / n) / n
158 except: print 'SimPy: No observations for sample variance'
159
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
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
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
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
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
280 - def __init__(self, name = 'a_Tally', ylab = 'y', tlab = 't', sim = None):
281 if not sim: sim = Globals.sim
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
292 self._integral2 = 0.0
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
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
336
339
341 return 1.0 * self._total / self._count
342
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
354 return 1.0 * (self._sum_of_squares - (1.0 * (self._sum * self._sum)\
355 / self._count)) / (self._count)
356
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
367 last = self._last_observation
368 twinteg2 = self._integral2 + (t - self._last_timestamp) * last * last
369
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
380
382 return len(l) == self._count
383
386
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