Author: | Tony Vignaux <Vignaux@users.sourceforge.net>, |
---|---|
Author: | Klaus Muller <Muller@users.sourceforge.net> |
Date: | 2005-November-1 gav |
SimPy version: | 1.6.1 |
Web-site: | http://simpy.sourceforge.net/ |
Python-Version: | 2.2, 2.3, 2.4 |
This document outlines the commands available in version 1.6.1 of SimPy.
A SimPy model is made up of Processes, Resources and Monitors and operations on them.
Basic structure of a SimPy simulation:
now() always returns the current simulation time and stopSimulation() will stop all simulation activity.
SimPy requires Python 2.2 or later [1].
[1] | If Python 2.2 is used, the command: from __future__ import generators must be placed at the top of all SimPy scripts. |
Processes inherit from class Process, imported from SimPy.Simulation.
- A Process execution method (PEM), 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 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 a resource (see Resources, below)
- yield request,self,rp,priority to release a unit of a resource (see Resources, below)
- yield release,self,r to request a unit of a resource with priority (see Resources, below)
- yield (request,self,resource[,priority]),(hold,self,waittime) to request a unit of a resource, perhaps with priority, but to renege after waittime (see reneging, below)
- yield (request,self,resource),(waitevent,self,events) to request a unit of a resource, perhaps with priority, but to renege if an event occurs (see reneging, below)
By the process itself:
By other processes:
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:
The interruption is reset at the victims next call to a yield hold,. Alternatively it can be reset by calling
self.interruptReset()
These include signalling between processes using events and a general wait-until command.
Events in SimPy are implemented by class SimEvent. A new event, myEvent, is generated by:
myEvent=SimEvent("MyEvent").
Associated with a SimEvent are
- a boolean occurred to show whether an event has happened (has been signalled)
- a list waits, implementing a set of processes waiting for the event
- a list queues, implementing a FIFO queue of processes queueing for the event
- an attribute signalparam to receive an (optional) payload from the signal method
Processes can wait for events by issuing:
yield waitevent,self,<events part>
<events part> 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]
Processes can queue for events by issuing:
yield queueevent,self,<events part> (with <events part> as defined above)
If one of the events in <events part> has already happened, the process continues. The occurred flag of the event(s) is toggled to False.
If none of the events in the <events part> has happened, the process is passivated after joining the FIFO queue of processes queuing for all the events.
The occurrence of an event is signalled by:
<event>.signal(<payload parameter>)
The optional <payload parameter> can be of any Python type.
When issued, signal causes the occurred flag of the event to be toggled to True, if waiting set and and queue are empty. Otherwise, all processes in the event's waits list are reactivated at the current time, as well as the first process in its queues FIFO queue.
A process can wait for an arbitrary condition by issuing:
yield waituntil,self,<cond>
where <cond> is a reference to a function (without parameters) which returns the state of the condition to be waited for as a boolean value.
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:
A Resource, r, has the following attributes:
- r.n The number of currently free units
- r.waitQ, a waiting queue (list) of processes (FIFO by default) The number of Processes waiting is len(r.waitQ)
- r.activeQ, a queue (list) of processes holding units,. The number of Proceeses in the active queue is len(r.activeQ)
- r.waitMon A Monitor recording the number in r.waitQ (see Monitor, below)
- r.actMon A Monitor recording the number in r.activeQ (see Monitor, below)
A unit of resource, r, can be requested and later released by a process using the following yield commands:
If a Resource, r is defined with priority queueing (that is qType==PriorityQ) a request can be made for a unit by:
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:
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.
SimPy provides an extended (compound) yield request statement form to model reneging.
yield (request,self,resource[,priority]),(<reneging clause>).
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.
The reneging clause used is (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.
The reneging clause used is (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
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.
SimPy uses the standard random variate routines in the Python random module. To use them, import methods from the random module:
A good range of distributions is available. For example:
Monitors are used to observe variables of interest and to return a simple data summary at any time during simulation run. Each monitoring object observes one variable. Both varieties of monitor use the same observe method to record data on the 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.
To define a new Tally object:
To define a new Monitor object:
For a monitor, m:
The Monitor variety is a sub-class of List and has a few extra methods:
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.
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.:
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.:
Then, after observing the data we return the histogram by:
histogram parameters as set up.
These messages are returned by simulate(), as in message=simulate(until=123).
Upon a normal end of a simulation, simulate() returns the message:
The following messages, returned by simulate(), are produced at a premature termination of the simulation but allow continuation of the program.
These messages are generated when SimPy-related fatal exceptions occur. They end the SimPy program. Fatal SimPy error messages are output to sysout.
We will be grateful for any corrections or suggestions for improvements to the document.
Version: | $Revision: 1.1.1.8 $ |
---|---|
Python-Version: | 2.2, 2.3, 2.5 |
Created: | 2002-December-10 |