=====================================================
SimPy's Object Oriented API - **New in Release 2.0**
=====================================================

:Authors: - Klaus Muller <Muller@users.sourceforge.net>
:SimPy release: |release|
: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) $

.. contents:: Contents
   :depth: 2

.. highlight:: python
   :linenothreshold: 12

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 |uumlaut| nsdorf, starting from SimPy 1.9. Thanks, guys, for this great job!

.. |uumlaut| unicode:: U+00FC
   :trim:


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::

    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::

    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::

    ## 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::

    ## 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)






