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: 229 $ $Date: 2009-01-26 16:17:21 +0100 (Mo, 26 Jan 2009) $
      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