.. meta::
   :description: SimPy Python Simulation Language 
   :keywords: simulation python stochastic

===========================================================
|simpylogo|  SimPy Cheatsheet
===========================================================

:Authors:  - Tony Vignaux <Vignaux@users.sourceforge.net>,
           - Klaus Muller <Muller@users.sourceforge.net>
:SimPy version: 1.7.1
:Web-site: http://simpy.sourceforge.net/
:Python-Version: 2.2, 2.3, 2.4
:Revision: $Revision: 1.1.1.18 $ 
:Date: $Date: 2006/06/05 10:07:28 $ 


.. contents:: Contents 
   :depth: 2

SimPy
-------------------

This document outlines the commands available in version 1.7 of
*SimPy*. A more  complete  description is held in the Manual_.

A SimPy model is made up of Processes_, Resources_, Levels_, Stores_
and Monitors_ and operations on them.


Basic structure of a *SimPy* simulation:

- **from SimPy.Simulation import *** which imports all facilities for
  the simulation program.
- **initialize()**  which sets up the simulation model
- *... the activation of at least one process....*
- **simulate(until=endtime)** starts the simulation which will run
  until one of the following:
  
  * there are no more events to execute. *now()==last event time*
  * the simulation time reaches *endtime*. *now()==endtime*
  * the *stopSimulation()* command is executed. *now()==stop time* 
   

**now()** always returns the current simulation time and
**stopSimulation()** will stop all simulation activity.

*SimPy* requires *Python* 2.2 or later [#]_.  

.. [#] If Python 2.2 is used, the command: **from __future__ import
   generators** must be placed at the top of all *SimPy* scripts.

[Return to Top_ ]

.. _PEM:

Processes
------------------- 

Processes inherit from class **Process**, imported from
*SimPy.Simulation*.

*   **class Pclass(Process):**  defines a new Process class (here,
    *Pclass*). Such a class must have a Process Execution Method (PEM_)
    and may have an *__init__* and other  methods:
 
    - **A Process execution method (PEM)**, which may have arguments,
      describes the actions of a process object and must contain at
      least one of the following *yield* statements to make it a Python
      generator function:

      * **yield hold,self,t** to execute a time delay of length *t*
	(unless the process is interrupted, see `Asynchronous
	interruptions`_ , below). The process continues at the
	next statement, after a delay in simulated time.

      * **yield passivate,self** to suspend operations indefinitely.
      * **yield waituntil,self,<cond>** to  wait until a condition
	becomes True (see waituntil_, below).
      * **yield waitevent,self,<events part>** to wait until an event
	occurs (see events_, below).
      * **yield queueevent,self,<events part>** to queue until an
	event occurs (see events_, below).

      * **yield request,self,r** to request a unit of resource, *r* 
        (see `Resources`_, below).
      * **yield request,self,r,P** to request  a unit of resource, *r*,
	with priority, *P*  (see `Resources`_, below.)
      * **yield release,self,r** to  release  a unit of a resource 
        (see `Resources`_, below).
      * **yield	(request,self,resource [,P]),(hold,self,waittime)** to
	request a unit of a resource, perhaps with priority, *P*, 
        but to renege after *waittime* (see reneging_, below).
      * **yield (request,self,resource [,P]),(waitevent,self,events)** to
	request a unit of a resource, perhaps with priority, *P*, 
        but to renege if an event occurs (see reneging_, below).

      * **yield put,self,cB,q [,P]** to put an amount *q* into a *Level*,
        *cB*, perhaps with priority, *P*.  (see Levels_, below)

      * **yield get,self,cB,q [,P]** to get an amount *q* from a *Level*,
        *cB* perhaps with priority, *P*. (see Levels_, below)

      * **yield put,self,sB,L [,P]** to put a *list*, *L*, of objects into a *Store*,
        *cB*, perhaps with priority, *P*.  (see Stores_, below)

      * **yield get,self,sB,n [,P]** to get a *list* of length, *n* from a *Store*,
        *cB* perhaps with priority, *P*. (see Stores_, below)


    - **__init__(self,..)**, the first line of which must be a call to
      the Class *__init__* in the form:
      *Process.__init__(self,name='a_process')*. Other commands can
      be used to initialize attributes of the object.


* **p = Pclass(...)**, constructs a new *Pclass* object, called, *p*,
  where the arguments are those specified in the Class's *__init__*
  method.
 
Starting and stopping SimPy Processes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

By the process itself:

* **yield passivate,self** suspends the process itself.


By other processes:

* **activate(p,p.execute(args),at=t,delay=period,prior=boolean)**
  activates the PEM_,  *p.execute()*, of Process *p* with
  arguments *args*. The default action is to activate at the current
  time, otherwise one of the optional timing clauses operate. If
  *prior==True*, the process will be activated before any others in
  the event list at the specified time.

* **reactivate(p,at=t,delay=period,prior=boolean)** will reactivate
  *p* after it has been passivated.
  The optional timing clauses work as for *activate*.

* **self.cancel(p)** deletes all scheduled future events for process
  *p*. 
  Note: This new format replaces the *p.cancel()* form of earlier SimPy
  versions.


Asynchronous interruptions
~~~~~~~~~~~~~~~~~~~~~~~~~~

* **self.interrupt(victim)** interrupts another process. The interrupt
  is just a signal. After this statement, the interrupting process
  immediately continues its current method.

  The *victim* must be *active* to be interrupted (that is executing a
  *yield hold,self,t*) otherwise the interruption has no effect.

  The introduction of interrupts changes the semantics of *yield hold*.
  After *before=now(); yield hold,self,T*, we have the post-condition
  *now()== before+T OR (self.interrupted() AND now()< before+T)*. The program
  must allow for this, i.e., for interrupted, incomplete activities.

  When interrupted, the *victim* prematurely and immediately returns
  from its *yield hold*. It can sense if it has been interrupted by
  calling:

* **self.interrupted()** which returns *True* if it has been interrupted. If so:
  
  * *self.interruptCause* gives the *interruptor* instance.
  * *self.interruptLeft* is the time remaining in the interrupted *yield hold,*

  The interruption is reset at the *victims* next call to a *yield
  hold,*. Alternatively it can be reset by calling

* **self.interruptReset()**

Advanced synchronisation/scheduling capabilities
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

These include signalling between processes using *events* and a
general *wait-until* command.



Defining a SimEvent
~~~~~~~~~~~~~~~~~~~

Events in *SimPy* are objects of class **SimEvent** [#]_. 

.. [#] This name was chosen because the term 'event' is already being
   used in Python for e.g. tkinter events or in Python's standard
   library module *signal -- Set handlers for asynchronous events*.

A *SimEvent*, ``sE`` is established by the following statement::

    sE = SimEvent(name="a_SimEvent")

A SimEvent, ``sE``  has the following attributes:


    - ``sE.occurred`` (boolean, initially ``False``) to indicate
      whether an event has happened (has been signalled)

    - ``sE.waits`` a list of processes waiting for the event

    - ``sE.queues`` a FIFO queue of processes queueing for the event

    - ``SE.signalparam``  a possible payload  from the *signal* method
    

Waiting or Queueing for a SimEvent
++++++++++++++++++++++++++++++++++

::

    yield waitevent,self,<events part>

called by SimPy Process, ``SP``, waits for an event in *<events
part>*. This can be:
    
      - an event variable, e.g. ``myEvent``

      - a tuple of events, e.g. ``(myEvent,myOtherEvent,TimeOut)``, or

      - a list of events, e.g. ``[myEvent,myOtherEvent,TimeOut]``
    
If one of the events in the *<events part>* has already happened, the
process continues.  The ``occurred`` flag of the event(s) is toggled
to ``False``. Otherwise the process is passivated after joining the
waits`` list of the event.

::

    yield queueevent,self,<events part>

called by SimProcess, ``SP``, queues for an event in *<events part>*
which  is as defined above.

If one of the events in *<event>s part>* has already happened, the
process continues.  The ``occurred`` flag of the event(s) is toggled
to ``False``. Otherwise, the process is passivated after joining the
FIFO ``queues`` for all the events.

Signalling a SimEvent
+++++++++++++++++++++

:: 

    sE.signal(<payload parameter>)
    
Is used by a Process to signal a *SimEvent*, ``sE``, where the *payload parameter* ,
of any Python type, is optional. It can be read by the process(es)
triggered by the signal as the SimEvent attribute ``sE.signalparam``,
like ``message = sE.signalparam``.


When this is called the flag ``sE.occurred`` is toggled to ``True`` if
``sE.waits`` and ``sE.queues`` are empty. Otherwise, all processes in
the ``sE.waits`` list are reactivated at the current time, as well as
the *first* process in ``sE.queues`` FIFO queue.


"wait until" synchronisation -- waiting for any condition
==========================================================

::

   yield waituntil,self,<cond>**
    
called by SimProcess, ``SP``, allows it to wait for an arbitrary
condition, *<cond>*. This is a reference to a function (without
parameters) which returns the state of the condition to be waited for
as a *boolean* value.

[Return to Top_ ]

.. ===================================================================

Resources
-------------------
 
The modeller may define Resources. A Resource, ``r``, is established
by the following statement::
 
  r=Resource(capacity=1, 
             name="a_resource",
             unitName="units",
             qType=FIFO, preemptable=False, 
             monitored=False,
             monitorType=Monitor)

where

   - *capacity* is the number of identical units of the resource
     available.  
   - *name* is the name by which the resource is known (eg
     ``"gasStation"``).   
   - *unitName* is the name of a unit of the resource (eg ``"pump"``).   
   - *qType* is either ``FIFO`` or ``PriorityQ``. It specifies the
     queue discipline of the waiting queue of processes; typically,
     this is *FIFO* (First-in, First-out) and this is the presumed
     value. 
   - *preemptable* is a boolean (``False`` or ``True``) and indicates, if
     it is ``True`` that a process being put into the queue may also
     pre-empt a lower-priority process already using a unit of the
     resource.  This only has an effect when ``qType == PriorityQ``.
   - *monitored* is a boolean (``False`` or ``True``) that indicates
     if the size of the ``waitQ`` and ``activeQ`` queues (see below)
     are to be monitored. (see Monitors_, below)
   - *monitorType* is either ``Monitor`` or ``Tally`` and is the
     variety of monitor to be used. (see Monitors_, below)
    
A Resource, ``r``,  has the following attributes:
 
   - ``r.n`` The number of units that are currently free.
   - ``r.waitQ`` A waiting queue (list) of processes (FIFO by
     default). ``len(r.waitQ)`` is the 
     number of Processes held in the waiting queue at any time.
   - ``r.activeQ`` A queue (list) of processes holding units.
     ``len(r.activeQ)`` is the  number of Processes held in the
     active queue at any time.      
   - ``r.waitMon`` A Monitor_ automatically recording the activity  
     in *r.waitQ* if monitored==True
   - ``r.actMon``  A Monitor_ automatically recording the activity 
     in *r.activeQ* if monitored==True


A unit of resource, *r*, can be requested and later released by
a process using the following yield commands:

 
* **yield request,self,r** to request a unit of resource,
  *r*. The process may be temporarily queued and suspended until
  a unit is available.
* **yield release,self,r** releases a unit of *r*. This may
  have the side-effect of allocating the released unit to the next
  process in the Resource's waiting queue.
 

Requesting resources with priority
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If a *Resource*, *r* is defined with *priority* queueing (that is
*qType==PriorityQ*) a request can be made for a unit by:

* **yield request,self,r,p**, where *P* is the priority of the
  request and can be real or integer.  Larger values of *P*
  represent higher priorities and these will go to the head of the
  *r.waitQ* if there not enough units immediately.
 

Requesting a resource with preemptive priority
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If a *Resource*, *r*, is defined with *priority* queueing (that is
*qType=PriorityQ*) and also *preemption* (that is *preemptable=1*) a
request can be made for a unit by:

* **yield request,self,r,p**, where *P* is the priority of the
  request and can be real or integer.  Larger values of *P*
  represent higher priorities and if there are not enough units
  available immediately, one of the active processes may be preempted.
 
If there are several lower priority processes, that with the lowest
priority is suspended, put at the front of the *waitQ* and the higher
priority, preempting process gets its resource unit and is put into
the *activeQ*. The preempted process is the next one to get a resource
unit (unless another preemption occurs).  The time for which the
preempted process had the resource unit is taken into account when the
process gets into the *activeQ* again. Thus, the total hold time is
always the same, regardless of whether or not a process gets
preempted.

Reneging -- leaving a queue before acquiring a resource
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

SimPy provides an extended (compound) *yield request* statement form
to model reneging.
 
**yield (request,self,resource[,P]),(<reneging clause>)**. 

*where P* is the *priority* of the request if the resource is defined with
*qType=PriorityQ*.

The structure of a SimPy model with 
reneging is::
 
  yield (request,self,resource),(<reneging clause>)
  if self.acquired(resource):
     ## process got resource and did not renege
     . . . . 
     yield release,self,resource
  else:
     ## process reneged before acquiring resource
     . . . . . 
     
A call to method (``self.acquired(resource)``) is mandatory after a
compound *yield request* statement.  It is not only a predicate which
indicates whether or not the process has acquired the resource, but it
also removes the reneging process from the resource's waitQ.

SimPy 1.6 implements two reneging clauses, one for reneging after a
certain time and one for reneging when an event has happened.
 
Reneging after a time limit
+++++++++++++++++++++++++++

The reneging clause used is **(hold,self,waittime)**

* **yield (request,self,resource[,P]),(hold,self,waittime)** 

If the resource unit has not been acquired by *waittime*, the process
leaves the queue (reneges) and its execution continues. Method
*self.acquired(resource)* must be called to check whether the resource
has been acquired or not.


     
Reneging when an event has happened
+++++++++++++++++++++++++++++++++++
 
The reneging clause used is **(waitevent,self,events)**.

* **yield (request,self,resource[,P]),(waitevent,self,events)** 

where *events* is an event or list of events (see events_). If one of
the events has been signalled before the unit of resource has been
acquired the process reneges. As before, *self.acquired(resource)*
must be called to check whether the resource has been acquired or not

Monitoring a resource
+++++++++++++++++++++

If the argument *monitored* is set *True* for a resource, *r*, the
length of the waiting queue, *len(r.waitQ)*, and the active queue,
*len(r.activeQ)*, are both monitored automatically (see Monitors_,
below). The monitors are called *r.waitMon* and *r.actMon*,
respectively.

The argument *monitorType* indicates which variety of monitor is to be
used, either *Monitor* or *Tally*. The default is *Monitor*. If this
is chosen, a complete time series for both queue lengths are
maintained so that a graph of the queue length can be plotted and
statistics, such as the time average can be found at any time. If
*Tally* is chosen, statistics are accumulated continuously and time
averages can be reported but, to save memory, no complete time series
is kept.

[Return to Top_ ]

.. ===================================================================

Levels
-------------------

A *Level* holds a scalar (*real* or *integer*) level. Processes can
*put* amounts into the buffer *get* (remove) amounts from it. 

Defining a Level
~~~~~~~~~~~~~~~~

A *Level* is established by the following statement::

     cB = Level(name="a_level", unitName="units",
                      capacity="unbounded",
                      initialBuffered=0,
                      putQType=FIFO, getQType=FIFO,
                      monitored=False, monitorType=Monitor,

where

  - *name* (string type) is the name by which the buffer is known (eg
    ``"inventory"``).
  - *unitName* (string type) is the name of the unit of the buffer (eg
    ``"widgets"``).
  - *capacity* (positive real or integer) is the capacity of the
    buffer. The default value is set to "unbounded" which translates as ``sys.maxint``.

  - *initialBuffered* is the initial content of the buffer.   
  - *putQType* (``FIFO`` or ``PriorityQ``) is the (producer) queue
    discipline.
  - *getQType* (``FIFO`` or ``PriorityQ``) is the (consumer) queue discipline.
  - *monitored* (boolean) sets the monitoring of the queues and the buffer.   
  - *monitorType* (``Monitor`` or ``Tally``) sets the type of Monitor_
    to be used.  `

A Level, ``cB``, has the following additional attributes:

  - ``cB.amount`` is the amount currently held in the Level.
  - ``cB.putQ`` is a queue of processes waiting to add amounts to the
    buffer. ``len(cB.putQ`` is the number of processes waiting to add amounts.
  - ``cB.getQ`` is a queue of processes waiting to get amounts from
    the buffer. ``len(cB.getQ)`` is the number of processes waiting to
    get amounts. 
  - ``cB.monitored`` is ``True`` if the queues are to be monitored. In
    this case  ``cB.putQMon``, ``cB.getQMon``, and  ``cB.bufferMon`` exist. 
  - ``cB.putQMon`` is a Monitor_ observing ``cB.putQ``. 
  - ``cB.getQMon`` is a Monitor_ observing ``cB.getQ``. 
  - ``cB.bufferMon``  is a Monitor_ observing ``cB.amount``. 



Getting and Putting
~~~~~~~~~~~~~~~~~~~

::

     yield get,self,cB,q [,P]

called by SimPy Process, ``SP`` extracts an amount ``q`` from a
*Level*, ``cB`` with priority ``P`` if ``putQType==PriorityQ``.
Here ``q`` can be a positive real or integer. If ``q > cB.amount`` the
requesting process will be passivated and queued (in ``cB.getQ``). It
will be reactivated when there is enough available.


::

      yield put,self,cB,r [,P]

called by SimPy Process, ``SP`` can add an amount ``r`` to the
*Level*, ``cB`` with priority ``P`` if ``getQType==PriorityQ``. Here
``r`` is a positive real or integer. If (``cB.amount + r >
cB.capacity``) the putting process is passivated and queued (in
``cB.putQ``) until there is sufficient room.




.. ===================================================================

Stores
-------------------

A *Store* buffers a number of *distinguishable* objects (of any
type, including processes). Objects are put into the *Store* by
processes and taken out by others.

Defining a Store
~~~~~~~~~~~~~~~~~~~

A *Store* is established by the following statement::

  sB = Store(name="a_store", unitName="units",
                  capacity="unbounded",
                  initialBuffered=None,
                  putQType=FIFO, getQType=FIFO,
                  monitored=False, monitorType=Monitor)

where

  - *name* (string type) is the name by which the buffer is known (eg
    ``"Inventory"``). 
  - *unitName* (string type) is the name of the unit of the buffer (eg
    ``"widgets"``).
  - *capacity* (positive real or integer) is the capacity of the buffer.
  - *initialBuffered* (a list) is the initial content of the buffer. 
  - *putQType* (``FIFO`` or ``PriorityQ``) is the (producer) queue
    discipline.
  - *getQType* (``FIFO`` or ``PriorityQ``) is the (consumer) queue discipline.
  - *monitored* (boolean) sets the monitoring of the queues and the buffer.   
  - *monitorType* (``Monitor`` or ``Tally``) sets the type of monitor
    to be used.  `


A *Store* has the following additional attributes:

  - ``sB.theBuffer`` is a queue (list) containing the buffered
    objects (FIFO order unless the user is storing them in a particular
    order).
    This is read-only and cannot be changed by the user.
  - ``sB.nrBuffered`` is the number of objects currently
    buffered. This is read-only and cannot be changed by the user.
  - ``sB.putQ`` is a queue of processes waiting to add objects to the
    buffer. ``len(sB.putQ`` is the number of processes waiting to add objects.
  - ``sB.getQ`` is a queue of processes waiting to get objects from
    the buffer. ``len(sB.getQ)`` is the number of processes waiting to
    get objects. 

  - ``sB.monitored`` is set to ``True`` when the buffer is created if
    the queues are to be monitored. In this case ``sB.putQMon``,
    ``sB.getQMon``, and ``sB.bufferMon`` exist.

  - ``sB.putQMon`` is a monitor observing ``sB.putQ``.
  - ``sB.getQMon`` is a monitor observing ``sB.getQ``. 
  - ``sB.bufferMon``  is a monitor observing ``sB.nrBuffered``. 

Getting and Putting
~~~~~~~~~~~~~~~~~~~~
 
::

     yield get,self,sB,n [,P]   

called by SimPy Process, ``SP``, gets the first ``n`` objects from
Store, ``sB`` with priority ``P`` if ``getQType==PriorityQ``. The
retrieved objects are returned in the *list* ``SP.got``.  If the buffer
does not hold enough objects the requesting process will be passivated
and queued (in ``sB.getQ``). It will be reactivated when the request
can be satisfied.

::

      yield put,self,sB,S [,P]

called by SimPy Process, ``SP``, adds a list, ``S`` of objects to the
Store, ``sB`` with priority ``P`` if ``putQType==PriorityQ``. If this
statement would lead to an overflow (that is, ``sB.nrBuffered +
len(S) > cB.capacity``) the putting process is passivated and queued
(in ``sB.putQ``) until there is sufficient room.

The objects are stored in queue ``sB.theBuffer`` in FIFO order unless
the user has called an ``sB.addSort(f)`` method which will sort the
buffer on any ``yield get`` using the user-defined routine ``f``. (see
the Manual_ for more details.



.. ====================================================================

Random variates
-------------------
 
*SimPy* uses the standard random variate routines in the Python
*random* module. To use them, import methods from the random module:

* **from random import seed random,expovariate**

The *seed* method allows  the user to set the initial seed value for
the pseudo-random stream. It is usual to set this at the start of all
simulation programs.

* **seed(i)**, sets the intial seed value to *i* (a large integer)

A good range of distributions is available. For example:
 
* **random()**, returns the next (uniform) random number between 0 and 1
* **expovariate(lambd)**, returns a sample from the exponential
  distribution with mean *1.0/lambd*.
* **normalvariate(mu,sigma)**, returns a sample from the normal (Gaussian)
  distribution. *mu* is the mean, and *sigma* is the standard deviation.

[Return to Top_ ]

.. ====================================================================

Monitors
---------------------------- 

Monitors are used to observe variables of interest and to return a
simple data summary during a simulation run. Each monitoring object
observes one variable.

There are two varieties of monitoring objects, **Tally** and
**Monitor**. The simpler class **Tally**, records enough information
(sums and sums of squares) to return simple data summaries at any
time.  The more complicated class **Monitor**, keeps a complete series
of observed data values, *y*, and associated times, *t*.

Both varieties of monitor use the same *observe* method to record data
on the variable.


Defining a Monitor
~~~~~~~~~~~~~~~~~~~~

To define a new **Tally** object:

* **m=Tally(name=<string>, ylab=<string>, tlab=<string>)**
   
  - *name* is the name of the tally object (default='a_Tally'). 
  - *ylab* and *tlab* are provided as labels for plotting graphs(defaults='y' and 't', respectively)

To define a new **Monitor** object:

* **m=Monitor(name=<string>, ylab=<string>, tlab=<string>)** 

  - *name* is the name of the tally object(default='a_Monitor'). 
  - *ylab* and *tlab* are provided as labels for plotting graphs (defaults='y' and 't', respectively)



Observing data
~~~~~~~~~~~~~~~~~

For a monitor, **m**:
 
* **m.observe(y [,t])** records the current value of *y* and time *t* (the current
  time, *now()*, if *t* is missing). 
* **m.reset([t])** resets the observations. The recorded time series is set to
  the empty list, *[]* and the starting time to *t* or, if it is
  missing, to the current simulation time, *now()*.

Data Summaries
~~~~~~~~~~~~~~~~~

* **m.count()** the current number of observations.
* **m.total()**, the sum of the *y* values
* **m.mean()**, the simple average of the observations, unaffected by
  the time measurements
* **m.var()**, the sample variance of the observations.
* **m.timeAverage([t])**, the average of the *y* values weighted by
  the time differences between observations. This is calculated from
  time 0 (or the last time *m.reset([t])* was called) to time *t* (the
  current simulation time if *t* is missing). It is assumed that *y*
  is continuous in time.
  values.
* **m.__str__()**, a  string that briefly describes the current state
  of the monitor.

Special Monitor methods:
~~~~~~~~~~~~~~~~~~~~~~~~~~

The *Monitor* variety is a sub-class of *List* and has a few extra methods:

* **m[i]** holds the *i* th observation as a list, *[ti, yi]*
* **m.yseries()** a list of the recorded data values, *yi*
* **m.tseries()** a list of the recorded times, *ti*

Histograms
~~~~~~~~~~~~~~~~~

A *histogram* object is basically a list of *bins*. Each bin contains
the number of *y* values observed in its range of values. Both
varieties of monitor can return a *histogram* of the data but they do
this in different ways.  A *histogram* can be graphed using the
*plotHistogram* method in the `SimPlot`_ package.
 
.. _`SimPlot`: SimPlotManual/ManualPlotting.html

**Monitor** objects accumulate the histogram values from the stored
data series when needed. A histogram can be both set up (that is,
specifying the number of bins and the range of observations) and
returned in a single call, e.g.:

* **h = m.histogram(low=0.0,high=100.0,nbins=10)**

**Tally** objects accumulate values for the histogram as each value
is observed. The histogram must therefore be set up before any values
are observed using the *setHistogram* method, e.g.:

* **m.setHistogram(name = '',low=0.0,high=100.0,nbins=10)**

Then, after *observing* the data we return the  histogram by:

* **h = m.getHistogram()** to  return a completed histogram using the
    histogram parameters as set up.



..   Deprecated methods:

   The following methods are retained for backwards compatibility but are
   not recommended. They may be removed in future releases of SimPy:

   * **m.tally(y)**, records the current value of *y* and the current time, *now()*.
   * **m.accum(y [,t])** records the current value of *y* and time *t* (the current
     time, *now()*, if *t* is missing).
 
[Return to Top_ ]

.. -------------------------------------------------------------------------


Error Messages
------------------

Advisory messages
~~~~~~~~~~~~~~~~~

These messages are returned by *simulate()*, as in
*message=simulate(until=123)*.

Upon a normal end of a simulation, *simulate()* returns the message:

- **SimPy: Normal exit**. This means that no errors have occurred and 
  the simulation has run to the time specified by the *until* parameter.


The following messages, returned by *simulate()*, are produced at a premature
termination of the simulation but allow continuation of the program.

- **SimPy: No more events at time x**. All processes were completed prior
  to the endtime given in *simulate(until=endtime)*.

- **SimPy: No activities scheduled**. No activities were scheduled
  when *simulate()* was called.
	
Fatal error messages
~~~~~~~~~~~~~~~~~~~~
These messages are generated when SimPy-related fatal  exceptions occur.
They end the SimPy program. Fatal SimPy error messages are output to 
*sysout*.

- **Fatal SimPy error: activating function which is not a generator (contains no 'yield')**.
  A process tried to (re)activate a function which is not a
  SimPy process (=Python generator). SimPy processes must contain
  at least one *yield . . .* statement.

- **Fatal SimPy error: Simulation not initialized**. The SimPy program
  called *simulate()* before calling *initialize()*.

- **SimPy: Attempt to schedule event in the past**: A *yield hold* statement
  has a negative delay time parameter.

- **SimPy: initialBuffered exceeds capacity**: Attempt to initialize a Store
  or Level with more units in the buffer than its capacity allows.

- **SimPy: initialBuffered param of Level negative: x**: Attempt to
  initialize a Level with a negative amount x in the buffer.

- **SimPy: Level: wrong type of initialBuffered (parameter=x)**: Attempt to
  initialize a buffer with a non-numerical initial buffer content x.

- **SimPy: Level: put parameter not a number**: Attempt to add a
  non-numerical amount to a Level's buffer.

- **SimPy: Level: put parameter not positive number**: Attempt to add
  a negative number to a Level's amount.

- **SimPy: Level: get parameter not positive number: x**: Attempt to
  get a negative amount x from a Level.

- **SimPy: Store: initialBuffered not a list**: Attempt to initialize
  a Store with other than a list of items in the buffer.

- **SimPy: Item to put missing in yield put stmt**: A *yield put* was
  malformed by not having a parameter for the item(s) to put into the
  Store.

- **SimPy: put parameter is not a list**: *yield put* for a Store must
  have a parameter which is a list of items to put into the buffer.

- **SimPy: Store: get parameter not positive number: x**: A *yield
  get* for a Store had a negative value for the number to get from the
  buffer.

- **SimPy: Fatal error: illegal command: yield x**: A *yield*
  statement with an undefined command code (first parameter) x was
  executed.



Monitor error messages
~~~~~~~~~~~~~~~~~~~~~~

- **SimPy: No observations for mean**. No observations were made by the
  monitor before attempting to calculate the mean.
- **SimPy: No observations for sample variance**. No observations were made by the
  monitor before attempting to calculate the sample variance.
- **SimPy: No observations for timeAverage**, No observations
  were made by the monitor before attempting to calculate the time-average.
- **SimPy: No elapsed time for timeAverage**. No simulation
  time has elapsed before attempting to calculate the time-average.


[Return to Top_ ]

Acknowledgements
-------------------

We will be grateful for any corrections or suggestions for improvements
to the document.

[Return to Top_ ]

.. image:: http://sourceforge.net/sflogo.php?group_id=62366&amp;type=4
   :width: 125 
   :height: 37 
   :alt:  SourceForge Logo 

.. ----------------------------------------------------------------------------
.. some useful stuff used above
.. |simpylogo| image:: images/sm_SimPy_Logo.png
.. _Manual: Manual.html
.. _Top: Contents_
.. _Monitor: Monitors_
.. _reneging: `Reneging -- leaving a queue before acquiring a resource`_
.. _interruptions: `Asynchronous interruptions`_
.. _interrupted: `Asynchronous interruptions`_
.. _events: `Defining a SimEvent`_
.. _waituntil: `"wait until" synchronisation -- waiting for any condition`_


..
   Local Variables:
   mode: rst
   indent-tabs-mode: nil
   sentence-end-double-space: t
   fill-column: 70 
   End:

