SimPy’s Object Oriented API - New in Release 2.0

Authors:
SimPy release:

2.0.1

Python version:

2.3 and later (not 3.0)

Revision:

$Revision: 313 $

Date:

$Date: 2009-04-05 01:25:11 +0200 (So, 05 Apr 2009) $

Introduction

This document describes the object oriented (OO) API provided by SimPy 2.0. This is an add-on to the existing API. There is full backward compatibility: Programs running under SimPy 1.9.1 and earlier releases work unchanged under version 2.0.

The new API allows different, often more concise, cleaner program patterns. In particular larger SimPy programs written with the OO API should be easier to maintain and extend. Users are advised to familiarize themselves with this programming paradigm new to SimPy by reading the models in the SimPyModels folder. Most of them are provided in two implementations, i.e. in the existing and in the OO API. Similarly, the programs in the Bank tutorials are provided with both APIs.

The new version has been developed very elegantly by Stefan Scherfke and Ontje Lünsdorf, starting from SimPy 1.9. Thanks, guys, for this great job!

Basic SimPy OO API Design

In SimPy 2.0, a class Simulation has been introduced. SimulationTrace, SimulationStep and SimulationRT are subclasses of Simulation. Multiple instances of these classes can co-exist in a SimPy program.

Backward compatibility

SimPy 2.0 offers both the old/existing API and an object-oriented API where simulation capabilities are provided by instantiating Simulation, SimulationTrace, SimulationStep or SimulationRT are subclasses of Simulation.

Each SimulationXX instance has its own event list and therefore its own simulation time. A SimulationXX instance can effectively be considered as a simulated, isolated parallel world. Any Process, Resource, Store, Level, Monitor, Tally or SimEvent instance belongs to one and only one world (i.e., Simulationxx instance).

The following program shows what this means for API and program structure:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
from SimPy.Simulation import *
"""Object Oriented SimPy API"""

class Car(Process):
    def run(self,res):
        yield request,self,res
        yield hold,self,10
        yield release,self,res
        print "Time: %s"%self.sim.now()

s=Simulation()
s.initialize()
r = Resource(capacity=5,sim=s)
auto = Car(sim=s)
s.activate(auto,auto.run(res=r))
s.simulate(until=100)

Using the existing API, the following program is semantically the same and also works under the OO version:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from SimPy.Simulation import *
"""Traditional SimPy API"""
class Car(Process):
    def run(self,res):
        yield request,self,res
        yield hold,self,10
        yield release,self,res
        print "Time: %s"%now()

initialize()
r = Resource(capacity=5)
auto = Car()
activate(auto,auto.run(res=r))
simulate(until=100)

This full (backwards) compatibility is achieved by the automatic generation of a SimulationXX instance “behind the scenes”.

Models as SimulationXX subclasses

The OO API can be used to generate model classes which are SimulationXX subclasses. This ties a model and a SimulationXX instance beautifully together. See the following example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
## CarModel.py
from SimPy.Simulation import *
"""Object Oriented SimPy API"""

class Car(Process):
    def run(self,res):
        yield request,self,res
        yield hold,self,10
        yield release,self,res
        print "%s done at %s"%(self.name,self.sim.now())

class Model(Simulation):
    def __init__(self,name,nrCars):
        Simulation.__init__(self)
        self.name=name
        self.nrCars=nrCars
    def runModel(self):
        self.initialize()
        r = Resource(capacity=5,sim=self)
        for i in range(self.nrCars):
            auto = Car(name="Car%s"%i,sim=self)
            self.activate(auto,auto.run(res=r))
        self.simulate(until=100)

if __name__=="__main__":
    experiment = Model(name="Experiment 1",nrCars=10)
    experiment.runModel()
    print experiment.now()

class Model here is a subclass of Simulation. Every model execution, i.e. call to runModel, reinitializes the simulation (creates a clean event list and sets the time to 0) (see line 20). runModel can thus be called repeatedly for multiple runs of the same experiment.

Model extension by subclassing

With the OO API, it is now very easy and clean to extend a model by subclassing. This effectively allows the creation of model libraries.

The model in the previous example can be extended to one with a variable number of Resource units (capacity) by subclassing as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
## CarModelExtension.py
from CarModel import *

class ModelExtension(Model):
    def __init__(self,name,nrCars,capacity):
        Model.__init__(self,name=name,nrCars=nrCars)
        self.resCapacity=capacity
    def runModel(self):
        self.initialize()
        r = Resource(capacity=self.resCapacity,sim=self)
        for i in range(self.nrCars):
            auto = Car(name="Car%s"%i,sim=self)
            self.activate(auto,auto.run(res=r))
        self.simulate(until=100)

experiment1=ModelExtension(name="Experiment 2",nrCars=10,capacity=3)
experiment1.runModel()

API changes

New classes

class Simulation

The simulation capabilities are provided by instantiating class Simulation. The three other SimPy run modes (SimulationTrace, SimulationRT and SimulationStep) are subclasses of Simulation.

Methods of class Simulation

The semantics and parameters of the methods are identical to those of the non-OO SimPy.Simulation functions of the same name.

  • initialize
  • activate
  • reactivate
  • simulate
  • now
  • stopSimulation
  • startCollection
  • allEventNotices
  • allEventTimes

Example calls (snippet):

from SimPy.Simulation import *
s = Simulation()
s.initialize()
s.simulate(until=100)

class SimulationTrace

The simulation capabilities plus tracing are provided by instantiating class SimulationTrace.

Methods of class SimulationTrace

The SimulationTrace subclass does not add any methods to those inherited from Simulation.

The semantics and parameters of the methods are identical to those of the non-OO SimPy.SimulationTrace functions of the same name.

Attribute trace

The initialization of class SimulationTrace instantiates an instance of class Trace. This becomes an attribute trace of the SimulationTrace instance.

Trace methods

The semantics and parameters of the Trace methods are identical to those of the non-OO SimPy.SimulationTrace trace instance of the same name.

  • trace.start
  • trace.stop
  • trace.treset
  • trace.tchange
  • trace.ttext

Example calls (snippet):

from SimPy.SimulationTrace import *
s = SimulationTrace()
s.initialize()
s.trace.ttext("Here we go")

class SimulationRT

The simulation capabilities plus real time synchronization are provided by instantiating class SimulationRT.

Methods of class SimulationRT

The SimulationRT subclass adds two methods to those inherited from Simulation.

The semantics and parameters of the methods are identical to those of the non-OO SimPy.SimulationRT functions of the same name.

  • rtnow
  • rtset

Example calls (snippet):

from SimPy.SimulationRT import *
class Car(Process):
   def __init__(self,whichSim):
      Process.__init__(self,sim=whichSim)
   def run(self):
      print self.sim.rtnow()
      yield hold,self,10

class SimulationStep

The simulation capabilities plus event stepping are provided by instantiating class SimulationStep.

Methods of class SimulationStep

The SimulationStep subclass adds three methods to those inherited from Simulation.

The semantics and parameters of the methods are identical to those of the non-OO SimPy.SimulationStep functions of the same name.

  • startStepping
  • stopStepping
  • simulateStep

Example call (snippet):

from SimPy.SimulationStep import *
s = SimulationStep()
s.initialize()
s.simulateStep(until=100,callback=myCallBack)

Classes with a SimulationXX attribute

All SimPy entity (Process, Resource, Store, Level, SimEvent) and monitoring (Monitor, Tally) classes have time-related functions. In the OO-API of SimPy, they therefore have a .sim attribute which is a reference to the SimulationXX instance to which they belong. This association is made by providing that reference as a parameter to the constructor of the class.

Important

All class instances instances must refer to the same SimulationXX instance, i.e., their .sim attributes must have the same value. That value must be the reference to the SimulationXX instance. Any deviation from this will lead to strange misfunctioning of a SimPy script.

The constructor calls (signatures) for the classes in question thus change as follows:

class Process

Process.__init__(self, name = 'a_process', sim = None)

Example (snippet):

class Car(Process):
   def __init__(self,name,whichSim):
       Process.__init__(self,name=name,sim=whichSim)

aSim = Simulation()
aSim.initialize()
c=Car(name="Mine",whichSim=aSim)

class Resource

Resource.__init__(self, capacity = 1, name = 'a_resource', unitName = 'units',
             qType = FIFO, preemptable = 0, monitored = False,
             monitorType = Monitor,sim=None)

Example (snippet):

aSim = Simulation()
aSim.initialize()
res=Resource(name="Server",sim=aSim)

classes Store and Level

Store.__init__(self, name = None, capacity = 'unbounded', unitName = 'units',
            putQType = FIFO, getQType = FIFO,
            monitored = False, monitorType = Monitor, initialBuffered = None,
            sim = None)

Level.__init__(self, name = None, capacity = 'unbounded', unitName = 'units',
            putQType = FIFO, getQType = FIFO,
            monitored = False, monitorType = Monitor, initialBuffered = None,
            sim = None)

Example (snippet):

aSim = Simulation()
aSim.initialize()
buffer = Store(name="Parts",sim=aSim)

class SimEvent

SimEvent.__init__(self, name = 'a_SimEvent', sim = None)

Example (snippet):

aSim = Simulation()
aSim.initialize()
evt = SimEvent("Boing!",sim=aSim)

classes Monitor and Tally

Monitor.__init__(self, name = 'a_Monitor', ylab = 'y', tlab = 't', sim = None)
Tally.__init__(self, name = 'a_Tally', ylab = 'y', tlab = 't', sim = None)

Example (snippet):

aSim = Simulation()
aSim.initialize()
myMoni = Monitor(name="Counting cars",sim=aSim)