figs/sm_SimPy_Logo.png

SimPy Cheatsheet

Authors:
Version:12.021
Date:2003-June-9
SimPy version:1.3
Web-site:http://simpy.sourceforge.net/
Python-Version:2.2, 2.3
Created:2002-December-10

Important note for users of previous SimPy versions

With version 1.3, SimPy now has a tracing utility, SimulationTrace, which can aid with developing, debugging, understanding, teaching, documenting SimPy models. See the SimPy Manual for details on how to use this utility.

SimPy

This document briefly outlines the commands available in SimPy. It refers to SimPy version 1.3 or later. The facilities described require Python 2.2 or later.

NOTE: When using Python 2.2, the following import statement must be used at the head of SimPy scripts:

  • from __future__ import generators

A SimPy model is made up of Processes, Resources 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:

    • there are no more events to execute (then now() is the time of the last event), or
    • the simulation time reaches endtime, now() == endtime
    • the stopSimulation() command is executed (then now() is the time when stopSimulation() was executed).
  • stopSimulation() will stop all simulation activity.

  • now() always returns the current simulation time.

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 at least these two methods:

    • __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.

    • An execution method, which may have arguments, describes the actions of a process object and must contain at least one of the yield statements to make it a Python generator function. The yield statements are:

      • yield hold,self,t to execute a time delay of length t (unless the process is interrupted, see below). The process continues at the statement following after a delay in simulated time.
      • yield passivate,self to suspend operations indefinitely.
      • yield request,self,r (see Resources, below)
      • yield request,self,rp,priority (see Resources, below)
      • yield release,self,r (see Resources, below)
  • 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 execution method 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()

Resources

The modeller may define Resources. These inherit from class Resource which is imported at the start of the program: from SimPy.Simulation import Resource

A Resource, r, is established using the command:

  • r = Resource(capacity=1, name='a_resource', unitName='units', qType=FIFO, preemptable=0)
  • capacity is the number of identical units of the resource available. Its default setting is 1 but can be any positive integer.
  • 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 describes the queue discipling of the waiting queue of processes; typically, this is FIFO (First-in, First-out). and this is the default. An alternative is PriorityQ (see below)
  • preemptable indicates, if it has a non-zero value, that a process being put into the PriorityQ may also pre-empt a lower-priority process already using a unit of the resource. This only has an effect when qType == PriorityQ (see below)

A Resource, r, has the following attributes:

  • The number of currently free units r.n
  • A waiting queue (list) of processes r.waitQ (FIFO by default) The number of Proceeses waiting is len(r.waitQ)
  • A queue (list) of processes holding units, r.activeQ. The number of Proceeses in the active queue is len(r.activeQ)

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,priority, where priority is real or integer. Larger values of priority 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,priority, where priority is real or integer. Larger values of priority 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.

Random variates

SimPy uses the standard random variate routines in the Python random module.

Monitors

A Monitor is an object that can record statistics about values observed in the simulation. These inherit from class Monitor which is imported at the start of the program:

  • from SimPy.Monitor import Monitor

A monitor is defined using the command

  • m = Monitor(name='')

There are two modes of statistical gathering, tallying which is used to record observations of isolated values (such as waiting times) and accumulating which is used to record time averages of values that continue in time (such as numbers of customers in the system). A monitor, m, has the following methods:

  • m.tally(x) adds the value x to the current totals for the monitor. Additions are also made to the number of observations and to the sum of squares of x.
  • m.accum(x,t) adds the time-value product, (t-tlast)x, to the accumulated totals since the last time accum was called (tlast). (Note, this uses the previous value of x and therefore should be called just after the value is changed. That value will then be used next time). If t is missing the current simulation time, now(), is used. The starting time is assumed to be 0.0 unless m.reset(t) has been called in which case the starting time is the time of the last reset.
  • m.reset(t) Resets all the counts and sums. Sets the starting time to t or, if t is missing, to the current simulation time, now().
  • m.count() returns the number of observations made.
  • m.mean() returns the straightforward average of the tallied values (an error if m.count()==0)
  • m.var() the variance of the tallied values (an error if m.count()==0)
  • m.timeAverage(t) the time average of the accumulated values measured over the period lastT to t. Here, lastT is 0.0 or the last time m.reset(t) was called. If t is missing in the call it is assumed to be now(), the current time. (an error if t - lastT <= 0.0)

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().

Monitor error messages

  • SimPy: Zero observations for mean. No observations were made by the monitor before attempting to calculate the mean.
  • SimPy: Zero observations for variance. No observations were made by the monitor before attempting to calculate the variance.
  • SimPy: Zero time elapsed in timeAverage. No simulation time has elapsed before attempting to calculate the time average for the monitor object.

Acknowledgments

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