

===============================================
 Simulation with Real Time Synchronization 
===============================================


:Authors: - Klaus Muller <Muller@users.sourceforge.net>
          - Tony Vignaux <Vignaux@users.sourceforge.net>
:SimPy version: 2.1
:Web-site: http://simpy.sourceforge.net/
:Python-Version: 2.3+ (not 3.0)
:Revision: $Revision: 496 $ 
:Date: $Date: 2010-05-11 07:05:35 +0200 (Di, 11 Mai 2010) $ 

.. contents:: Contents
   :depth: 3
   
This manual describes **SimulationRT**, a SimPy module which supports
synchronizing the execution of simulation models with real (wallclock) time.

Acknowledgement
===============

SimulationRT is based on an idea by Geoff Jarrad of CSIRO (Australia). He
contributed a lot to its development and testing on Windows and Unix.

The code for the adjustment of the execution speed during the simulation run
was contributed by Robert C. Ramsdell.

Synchronizing with wall clock time
===================================

SimulationRT allows synchronizing simulation time and real (wallclock) time. 
This capability can be used to implement e.g. interactive game applications or
to demonstrate a model's execution in real time. 

It is identical to Simulation, except for the *simulate* function which takes 
an additional parameter controlling real-time execution speed. 

Here is an example::

    ## RealTimeFireworks.py
    from SimPy.SimulationRT  import *
    from random import *
    import time
    class Launcher(Process):
        def launch(self):
            while True:
                print "Launch at %.1f; wallclock: %s"%(now(),time.clock()-startTime)
                yield hold,self,uniform(1,maxFlightTime)
		print "Boom!!! Aaaah!! at %.1f; wallclock: %s"%(now(),time.clock()-startTime)
    def model():
        initialize()
        for i in range(nrLaunchers):
            lau=Launcher()
            activate(lau,lau.launch())
        simulate(real_time=True,rel_speed=1,until=20) ##unit sim time = 1 sec clock
    nrLaunchers=2
    maxFlightTime=5.0 
    startTime=time.clock()
    model()
    
*rels_speed=1* sets the synchronization so that 1 simulation time unit
is executed in approximately 1 second of wallclock time.
Run under Python 2.2.2 on a Windows XP-box (1.7 GHz), this output resulted
over about 18 seconds of wallclock time::

	Launch at 0.0; wallclock: 0.000195555580376
	Launch at 0.0; wallclock: 0.00190107960634
	Boom!!! Aaaah!! at 1.8; wallclock: 1.78082661344
	Launch at 1.8; wallclock: 1.78274501368
	Boom!!! Aaaah!! at 2.8; wallclock: 2.84245930698
	Launch at 2.8; wallclock: 2.84435982785
	Boom!!! Aaaah!! at 4.1; wallclock: 4.08443978215
	Launch at 4.1; wallclock: 4.09004328762
	Boom!!! Aaaah!! at 5.2; wallclock: 5.14561822801
	Launch at 5.2; wallclock: 5.14878203794
	Boom!!! Aaaah!! at 7.0; wallclock: 6.99845622838
	Launch at 7.0; wallclock: 7.00175357483
	Boom!!! Aaaah!! at 7.4; wallclock: 7.39919794276
	Launch at 7.4; wallclock: 7.40245282571
	Boom!!! Aaaah!! at 9.7; wallclock: 9.69250728794
	Launch at 9.7; wallclock: 9.69912935862
	Boom!!! Aaaah!! at 10.6; wallclock: 10.5938587167
	Launch at 10.6; wallclock: 10.6006140445
	Boom!!! Aaaah!! at 13.8; wallclock: 13.8082362423
	Launch at 13.8; wallclock: 13.8134877477
	Boom!!! Aaaah!! at 14.1; wallclock: 14.1385670525
	Launch at 14.1; wallclock: 14.1438146468
	Boom!!! Aaaah!! at 16.4; wallclock: 16.411963811
	Launch at 16.4; wallclock: 16.4172373863
	Boom!!! Aaaah!! at 17.1; wallclock: 17.1429980626
	Launch at 17.1; wallclock: 17.1482308506
	Boom!!! Aaaah!! at 18.1; wallclock: 18.0742063586
	Launch at 18.1; wallclock: 18.0794469688
	
Clearly, the wallclock time does not deviate significantly from the simulation time.

Changing the execution speed during a simulation run 
==================================================================

By calling method *rtset* with a parameter, the ratio simulated time to wallclock time
can be changed during a run.

Here is an example::

    """variableTimeRatio.py
    Shows the SimulationRT capability to change the ratio simulation 
    time to wallclock time during the run of a simulation.
    """
    from SimulationRT import *

    class Changer(Process):
        def change(self,when,rat):
            global ratio
            yield hold,self,when
            rtset(rat)
            ratio=rat
    class Series(Process):
        def tick(self,nrTicks):
            oldratio=ratio
            for i in range(nrTicks):
                tLastSim=now()
                tLastWallclock=wallclock()
                yield hold,self,1
                diffSim=now()-tLastSim
                diffWall=wallclock()-tLastWallclock
                print "now(): %s, sim. time elapsed: %s, wall clock elapsed: "\
                    "%6.3f, sim/wall time ratio: %6.3f"\
                    %(now(),diffSim,diffWall,diffSim/diffWall)
                if not ratio==oldratio:
                    print "At simulation time %s: ratio simulation/wallclock "\
                        "time now changed to %s"%(now(),ratio)
                    oldratio=ratio
    initialize()
    ticks=15
    s=Series()
    activate(s,s.tick(nrTicks=ticks))
    c=Changer()
    activate(c,c.change(5,5))
    c=Changer()
    activate(c,c.change(10,0.1))
    ratio=1
    print "At simulation time %s: set ratio simulation/wallclock time to %s"\
           %(now(),ratio)
    simulate(until=100,real_time=True,rel_speed=ratio)
    
The program changes the time ratio twice, at simulation times 5 and 10.
    
When run on a Windows XP computer under Python 2.3, this results in this output::

    At simulation time 0: set ratio simulation/wallclock time to 1
    now(): 1, sim. time elapsed: 1, wall clock elapsed:  0.999, sim/wall time ratio:  1.001
    now(): 2, sim. time elapsed: 1, wall clock elapsed:  0.999, sim/wall time ratio:  1.001
    now(): 3, sim. time elapsed: 1, wall clock elapsed:  0.999, sim/wall time ratio:  1.001
    now(): 4, sim. time elapsed: 1, wall clock elapsed:  0.999, sim/wall time ratio:  1.001
    now(): 5, sim. time elapsed: 1, wall clock elapsed:  0.999, sim/wall time ratio:  1.001
    At simulation time 5: ratio simulation/wallclock time now changed to 5
    now(): 6, sim. time elapsed: 1, wall clock elapsed:  0.199, sim/wall time ratio:  5.021
    now(): 7, sim. time elapsed: 1, wall clock elapsed:  0.199, sim/wall time ratio:  5.020
    now(): 8, sim. time elapsed: 1, wall clock elapsed:  0.199, sim/wall time ratio:  5.020
    now(): 9, sim. time elapsed: 1, wall clock elapsed:  0.199, sim/wall time ratio:  5.020
    now(): 10, sim. time elapsed: 1, wall clock elapsed:  0.199, sim/wall time ratio:  5.019
    At simulation time 10: ratio simulation/wallclock time now changed to 0.1
    now(): 11, sim. time elapsed: 1, wall clock elapsed: 10.001, sim/wall time ratio:  0.100
    now(): 12, sim. time elapsed: 1, wall clock elapsed: 10.001, sim/wall time ratio:  0.100
    now(): 13, sim. time elapsed: 1, wall clock elapsed: 10.001, sim/wall time ratio:  0.100
    now(): 14, sim. time elapsed: 1, wall clock elapsed: 10.001, sim/wall time ratio:  0.100
    now(): 15, sim. time elapsed: 1, wall clock elapsed: 10.001, sim/wall time ratio:  0.100

Limitations
============
This module works much better under Windows than under Unix or Linux, i.e., it gives
much closer synchronization.
Unfortunately, the handling of time in Python is not platform-independent at all.
Here is a quote from the documentation of the *time* module::

    "clock()
    On Unix, return the current processor time as a floating point number expressed in seconds. 
    The precision, and in fact the very definition of the meaning of ``processor time'' , depends 
    on that of the C function of the same name, but in any case, this is the function to use for 
    benchmarking Python or timing algorithms.

    On Windows, this function returns wall-clock seconds elapsed since the first call to this 
    function, as a floating point number, based on the Win32 function QueryPerformanceCounter(). 
    The resolution is typically better than one microsecond. 
    "


The SimulationRT API
======================

Structure
---------
Basically, SimulationStep has the same API as Simulation, but with:

    - a change in the definition of simulate, and
    - an additional method to change execution speed during a simulation
      run.

**simulate**
------------------

Executes the simulation model.

Call:

	**simulate(<optional parameters>)**

Mandatory parameters:
	None.

Optional parameters:
	- **until=0** : the maximum simulation (end) time (positive floating point number; default: 0)
	- **real_time=False** : flag to switch real time synchronization on or off (boolean; default: False, meaning no synchronization)
	- **rel_speed=1** : ratio simulation time over wallclock time; example: *rel_speed=200* executes 200 units of simulation time in about one second (positive floating point number; default: 1, i.e. 1 sec of simulation time is executed in about 1 sec of wallclock time)

Return value:
	Simulation status at exit.

**rtset**
------------------

Changes the ratio simulation time over wall clock time.

Call:

	**rtset(<new ratio>)**

Mandatory parameters:
	None

Optional parameters:
	- **rel_speed=1** : ratio simulation time over wallclock time; example: *rel_speed=200* executes 200 units of simulation time in about one second (positive floating point number; default: 1, i.e. 1 sec of simulation time is executed in about 1 sec of wallclock time)

Return value:
	None

    
$Revision: 496 $ $Date: 2010-05-11 07:05:35 +0200 (Di, 11 Mai 2010) $ 
