Klaus G. Muller | 9 Feb 13:14
Picon
Picon
Favicon

Re: A new approach for monitoring simulation data

Stefan,
I agree that your proposed new Monitor is powerful and very flexible to 
use. However, I don't think that it is as simple and/or intuitive as the 
Monitor now in SimPy.

Yes, numpy/scipy/matplotlib are standard fare for many scientific Python 
users. SimPy users, however, often come from the Operations Research 
side and are not necessarily experienced scientific Python users. Would 
you believe that many newbie SimPy users already have difficulties with 
SimPy installation? Expecting them to install numpy/scipy/matplotlib 
just to get SimPy going might just be to steep a learning curve.

I would be very happy if you could develop your new Monitor as an 
optional SimPy library/module and publish it, together with sufficient 
user documentation with examples. Give it a name other than Monitor, though.

Regards,

Klaus Müller

------------------------------------------------------------------------------
The Planet: dedicated and managed hosting, cloud storage, colocation
Stay online with enterprise data centers and the best network in the business
Choose flexible plans and management services without long-term contracts
Personal 24x7 support from experience hosting pros just a phone call away.
http://p.sf.net/sfu/theplanet-com
Ruby Stevenson | 3 Feb 22:48
Picon

process synchronization question

hi, all

I have a question on process synchronization:

two process A and B are defined, there is a one-to-one mapping between
the two: meaning one instance of A talks to one instance of B, only.

- A submitted bunch of jobs to B
- A then wait until B finishes to proceed.

What is the best way for an instance of B to notify A that the job is done?


>From what I can gather, A could define a SimEvent, and B use "signal"
method to notify A. The problem is it seems I need to encode instance name of A (or something unique) into this event for B to precisely signal this instance of A only, is this the case? Encoding a name then parsing it to verify seems awkward though. Are there are better ways of achieving this? Thanks for help Ruby ------------------------------------------------------------------------------ The Planet: dedicated and managed hosting, cloud storage, colocation Stay online with enterprise data centers and the best network in the business Choose flexible plans and management services without long-term contracts Personal 24x7 support from experience hosting pros just a phone call away. http://p.sf.net/sfu/theplanet-com
Stefan Scherfke | 2 Feb 09:32
Picon

Re: A new approach for monitoring simulation data

Hello Rayene,

Am 2010-01-20 um 12:23 schrieb Rayene Ben Rayana:
> 
> Yeah, it is ugly :)
> Does ('name', lambda: self.name) work ?

Yep, seems to work:

>>> class A(object):
   ...     foo = 3
   ... 
>>> a = A()
>>> f = lambda: a.foo
>>> f()
   3
>>> a.foo = 4
>>> f()
   4
>>> 

>>> Other things to think of :
>>> 
>>> 1. An easy way to obtain a throughput plot (delta_value/delta_time)
>>> 2. Checking if the monitored data changed since last time. If not, give the user the possibility
>>> to skip the storage of that line.
>>> 
>>> example : 
>>> Instead of :
>>> 
>>> [time, x, y]
>>> 1, 14, 5
>>> 2, 14, 5
>>> 3, 14, 5
>>> 4, 12, 6
>>> 
>>> We can store :
>>> [time, x, y]
>>> 1, 14, 5
>>> 4, 12, 6
>>> 
>>> Useful when data does not change too often.
>> 
>> This is really a nice idea. I will discuss this with Ontje.
> 
> Just keep in mind to let the user decide to activate it or not.
> It is a tradeoff between computing time and memory usage.

Do you have a concrete use case for this? 

If your variables don’t change in an event, this event is useless and 
could better be skipped (yield a longer hold). If – on the other hand – 
only one variable value changes, you need to monitor anyways.

Maybe we can add a method like ``Monitor.last()`` which returns a tuple
with the last monitored values, so you can check for yourself:

	if self.monitor.last() != (self.x , self.y):
		self.monitor()

Cheers,
Stefan

------------------------------------------------------------------------------
The Planet: dedicated and managed hosting, cloud storage, colocation
Stay online with enterprise data centers and the best network in the business
Choose flexible plans and management services without long-term contracts
Personal 24x7 support from experience hosting pros just a phone call away.
http://p.sf.net/sfu/theplanet-com
John A Schroeder | 27 Jan 20:07
Favicon

Unavailability modeling issue


Hello All,

I am evaluating SimPy to see if it might be useful in my work.   I have stumbled across something that looks like a software bug, but my just be my lack of understanding.

I am using a variation of one of the MachRep.py examples to test my understanding of SimPy.   The issue I have been trying to resolve is how to deal with the situation where the last scheduled  machine "upTime" or "downTime" is longer than the machine mission time (or simulation end time).   When this occurs, as it will in almost every simulation, it will cause an error in the unavailability calculation.  I have tried to limit the scheduled time using code like the following:

if (now() + self.downTime) > GlobalVars.simEndTime:
    self.downTime = GlobalVars.simEndTime - now() - .0001
           
The thing I do not understand is why, without subtracting the .0001 in the above code, SimPy appears to enter some sort of infinite loop.  I have appended the full source code from my test problem.  If I remove the .0001 in the above example, the calculation appears to repeat the above statement forever.  What is happening?

John A. Schroeder
Idaho National Laboratory
Battelle Energy Alliance, LLC
P.O. Box 1625
Idaho Falls, ID  83415-3850

Ph:   208-526-8755
FAX:  208-526-2930


# EPowerSim01.py ===============================================================
#
# One machine, which sometimes break down.
# Up time is exponentially distributed
# Down time is exponentially distributed  
#
# Output is long-run fraction of down time (unavailability).

from SimPy.Simulation import Monitor, Process, hold, initialize
from SimPy.Simulation import activate, simulate, now
from SimPy.SimPlot import SimPlot
from random import Random
from math import exp

## Global data -----------------------------------------------------------------

class GlobalVars:       # Global variables

    failureRate = 1.0E-3    # machine failure rate
    dtMon = Monitor('Down Times')
    utMon = Monitor('Up Times')
    uaMon = Monitor('Unavailability')
    mUA = 0                 # machine unavailability
    repairRate  = 1.0E-1    # machine repair rate
    rnd = Random(12345)     # random number stream
    simEndTime = 8760.0     # simulation end time
    trials = 5000           # number of simulations to perform

## Model components ------------------------------------------------------------

# Calculate the true unavailability of a machine with failure rate l, repair
# rate m, and mission time t.
def UA(l,m,t):
   
    return (l /(l + m)) * (1.0 - exp(-1.0*(l + m) * t))

class Machine(Process):

    def __init__(self, id, fr, rr): # Required constructor

        Process.__init__(self)      # must call parent constructor
        # Instance variables
        self.ID = id                # ID for this Machine object
        self.failureRate = fr       # failure rate
        self.repairRate = rr        # repair rate
        self.totalDownTime = 0      # Keep track of total down time
        self.totalUpTime = 0        # Keep track of total up time
        self.upTime = 0
        self.downTime = 0
       
    def getDT(self):  # Return the down time
       
        return self.totalDownTime
       
    def getUA(self):  # Return the machine unavailability

        return self.totalDownTime / (self.totalUpTime + self.totalDownTime)
               
    def getUT(self):  # Return the total up time
       
        return self.totalUpTime
       
    def run(self): # Required Process Execution Method (PEM)
 
        while True:

            # Machine is in the "up" state, hold for
            # exponentially distributed up time.
            self.upTime = GlobalVars.rnd.expovariate(self.failureRate)
            if (now() + self.upTime) > GlobalVars.simEndTime:
                self.upTime = GlobalVars.simEndTime - now()
            GlobalVars.utMon.observe(self.upTime)
            # keep track of total up time
            self.totalUpTime += self.upTime
            yield hold,self,self.upTime
           
            # Machine is now in the "down" state, hold for
            # exponentially distributed repair time.
            #print "%7.3E Machine %d failed" % (now(), self.ID)          
            self.downTime = GlobalVars.rnd.expovariate(self.repairRate)
            #print "  Down time pick:   %7.3E" % (downTime)
            if (now() + self.downTime) > GlobalVars.simEndTime:
                self.downTime = GlobalVars.simEndTime - now() - .0001
                #print "  Now + down time:  %7.3E" % (now() + self.downTime)
            GlobalVars.dtMon.observe(self.downTime)
            # keep track of total down time
            self.totalDownTime += self.downTime
            yield hold,self,self.downTime
            #print "%7.3E Machine %d repaired " % (now(),self.ID)
           
## Experiment data -------------------------------------------------------------


## Model/Experiment ------------------------------------------------------------
 
def model(trial):

    print "Starting trial", trial+1
   
    initialize()
       
    # Create the Machine object
    edg1 = Machine(1, GlobalVars.failureRate, GlobalVars.repairRate)
       
    # Register the machine thread and execute its run() method,
    activate(edg1,edg1.run())
       
    # Run until simulation reaches end time.
    simulate(until=GlobalVars.simEndTime)

    ut = edg1.getUT()
    dt = edg1.getDT()
    ua = dt / (ut+dt)
    GlobalVars.uaMon.observe(ua)
               
    print "  Total up time:    %7.3E" % (ut)
    print "  Total down time:  %7.3E" % (dt)
    print "  Sum of up + down: %7.3E" % (ut+dt)
    print "  Unavailability:   %7.3E" % (ua)
   
    # Save the machine unavailability for late use.
    GlobalVars.mUA = edg1.getUA()    
    print " "
               
def main():
   
    for trial in range(GlobalVars.trials):
        model(trial)  
               
## Results ---------------------------------------------------------------------  
   
    print "Machine %d mean up time:   %7.3E" % (1, GlobalVars.utMon.mean())
    print "Machine %d mean down time: %7.3E" % (1, GlobalVars.dtMon.mean())
    print "Machine %d mean UA:        %7.3E" % (1, GlobalVars.uaMon.mean())
    print "Machine %d true UA:        %7.3E" % (1, UA(GlobalVars.failureRate,
                                                      GlobalVars.repairRate,
                                                      GlobalVars.simEndTime))

    #histo = GlobalVars.dtMon.histogram(low=0.0, high=100. ,nbins=50)            
    #plt = SimPlot()                                                  
    #plt.plotHistogram(histo,xlab='Time (hr)', ylab='Count',                      
    #                  title="Down times",
    #                  color="red",width=2)                        
    #plt.mainloop()                                                
 
if __name__ == '__main__': main()

------------------------------------------------------------------------------
The Planet: dedicated and managed hosting, cloud storage, colocation
Stay online with enterprise data centers and the best network in the business
Choose flexible plans and management services without long-term contracts
Personal 24x7 support from experience hosting pros just a phone call away.
http://p.sf.net/sfu/theplanet-com
_______________________________________________
Simpy-users mailing list
Simpy-users <at> lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/simpy-users
Harriv | 19 Jan 21:16
Picon

Simpy book?

Hi,

 There's this page about SimPy book -
http://simpy.sourceforge.net/simpy_book.htm

 What's the current status of it? I hope it is not abandoned..

------------------------------------------------------------------------------
Throughout its 18-year history, RSA Conference consistently attracts the
world's best and brightest in the field, creating opportunities for Conference
attendees to learn about information security's most important issues through
interactions with peers, luminaries and emerging and established companies.
http://p.sf.net/sfu/rsaconf-dev2dev
Stefan Scherfke | 19 Jan 16:30
Picon

A new approach for monitoring simulation data

Hello,

Ontje and I want to present and discuss a new way for monitoring SimPy
simulations. We have attached a commented sample implementation of our approach
with some usage examples.

Before we describe our idea, we want to point out some things we didn’t like
about SimPy’s ``Monitor`` and ``Tally`` classes:

* They use global variables.
* If you monitor multiple attributes, timestamps are stored with each of them
  (data duplication, requires more memory as necessary).
* They combine data collection and analysis in one class. These are different
  functional aspects and should in our opinion be separated.
* Coupling with Histogram and GUI stuff.

We never used any of both for our projects, because they never really fitted our
needs. So we debated intensely how an optimal monitoring would look like. It
should meet the following requirements:

* Easy to use (simple API, little typing)
* Memory and CPU efficiency

  * No impact on simulation speed if you don’t use it.
  * As little impact as possible if you use it.

* Flexibility and easy extensibility
* Separation of data collection and data analysis

Description of our approach
===========================

We know that there are many ways one might want to monitor simulations. We
focused on the use case, that you want to monitor several attributes for one
process—maybe with timestamp—so that your data looks something like this:

    ([t0, t1, t2], [x0, x1, x2], [y0, y1, y2])

Our approach can easily be extended to meet other use cases, too, but more on
this later. We also state that our approach does not cover data analysis. This
is something that should in our opinion be done by specialized frameworks like
e.g. NumPy, SciPy and Matplotlib.

Basic idea
----------

For performance reasons our monitoring works on SimPy processes rather than on
the simulation itself. This has the advantage of a minimized or even nonexisting
performance impact on the simulation speed if you monitor only a few processes or
don’t monitor anything at all.

We also wanted a very easy API: Create a monitor like ``m = Monitor(config)``
and trigger the monitoring of a process’ current state by just calling ``m()``.

How to exactly use it
---------------------

Monitors should be instanciated in ``MyProc.__init__()``. You must pass
some configuration for the *series* you want to create. Each *series* is
defined by a *name* and a *collector* function::

    class MyProc(Process):
        def __init__(self):
            # ...
            self.monitor = Monitor(
                ('a', func1),
                ('b', func2),
                # ...
            )

You can then retrieve the data for e.g. the series “a” via ``self.monitor.a``.
The collector functions describe, how the data should be collected. The two
common cases here are:

1. Automatically get an attribute’s value (e.g. ``self.a``)
2. Manually pass the value to be monitored

You can solve both cases with custom lambda functions, but we’ve also create a
shortcut for each of them.

And advanced example::

    class MyProc(Process):
        def __init__(self, sim):
            # ...
            self.monitor = Monitor(
                ('time', sim.now),
                get(self, 'a', 'b'),
                ('diff', manual)
            )

This will create a monitor containing the series ``time``, ``a``, ``b`` and
``diff``, where ``time`` stores the values returned by ``sim.now()``, ``self.a``
and ``self.b`` get collected automatically and and a value for ``diff`` has to
be passed manually. All this happens with the PEM of a process::

    def run(self)
        while True:
            # ...
            self.monitor(diff=self.get_diff()

That’s it!

Conclusion
==========

We hope we could make our idea clear. We think our solution is quite clean and
flexible. It can easily be extended (e.g. by adding other monitor classes) and
also easily be added to SimPy without interfering with any existing
functionality. Since it isn’t integrated into the simulation core, it won’t slow
down the simulation, if you don’t want monitoring.

The attached script contains the whole monitoring framework and an example
simulation that tests and shows how to use it. You need SimPy, NumPy and
Matplotlib to execute it (NumPy and Matplotlib are only required for the example
analysis part in the end, so if you uncomment these lines, you don’t need them).

Other monitor classes we thought of are:

* a *SimpleMonitor* that just creates one list of values (e.g. [x0, x1, ...]),
* a *GroupedMonitor* that collects groups of series (useful for a central
  collector process which just monitors other processes in the simulation) and
* aggregate monitors like e.g. and AvgMonitor. This could be useful if you are
  just interested in the average of all values (and not the values themselves),
  so it only needs to store the sum and count of all values.

What do you think of it?

Cheers,
Ontje and Stefan

Attachment (monitoring.py): text/x-python-script, 7760 bytes
------------------------------------------------------------------------------
Throughout its 18-year history, RSA Conference consistently attracts the
world's best and brightest in the field, creating opportunities for Conference
attendees to learn about information security's most important issues through
interactions with peers, luminaries and emerging and established companies.
http://p.sf.net/sfu/rsaconf-dev2dev
_______________________________________________
Simpy-users mailing list
Simpy-users <at> lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/simpy-users
Klaus G. Muller | 7 Jan 15:27
Picon
Picon
Favicon

Re: Notifiy a PEM that a simulation is over

Stefan,
I don't think that anything should happen to a process in a hold when a simulation has come to an end. If you end the simulation at t = 10, t = 15 is outside the simulation time frame. If, on the other hand, the event at t  = 15 is part of the model execution, just don't end the simulation at t = 10, but let it end with the event at t = 15.

I clearly argue from the semantics a modeling point of view, not from a Python programming point of view.

Klaus Müller


Stefan Scherfke wrote:
Hi Klaus, happy New Year to you, too! charge() should be called no matter whether the charging process was interrupted or not. The code I posted works quite fine during the simulation. The problem arises only, when the whole simulation ends while the controller is "in a hold": Imagine I want to simulate until t = 10. If the controller yields "hold, self, 10" at t = 5, it should continue to work (and call charge()) at t = 15. But at t = 15 the simulation already has stopped without the controller realizing this, so charge() doesn’t get called. Normally, if you delete/close a generator (here the PEM), it raises a GeneratorExit error to the generator/PEM, so I can except and handle the end of it. Since a SimPy process keeps a reference to its PEM in _nextpoint, if the PEM was in a "hold" when the simulation finished, then no GeneratorExit will be raised. So the simulator should imho check the event list for all outstanding events and either call close() directly on each generator or just set _nextpoint = None for each process. I can try to implement and test this and post my results here later. :-) cheers, Stefan Am 2010-01-07 um 13:47 schrieb Klaus G. Muller:
Stefan, Happy New Year! An interesting question. My answer to this is one of semantics: the simulation is NOT over, if something still needs to be done in the model. The battery controller process (the one in the "yield hold") should therefore be interrupted by whatever ends the charge process, like so: charge_start = self.sim.now() duration = self.get_charge_duration() yield hold, self, duration if self.interrupted(): # This will be called if the battery is full or if someone # pulled the plug (and thus interrupted the charging) duration = self.sim.now() - charge_start self.battery.charge(self.kw, duration) What do you think? I believe this is a clean way of miodeling the charge termination action. Klaus Müller Stefan Scherfke wrote:
Moin moin, is there an easy way to notify a PEM that the simulation finished while the PEM was in a "yield hold"? Example for a battery controller: … charge_start = self.sim.now() duration = self.get_charge_duration() yield hold, self, duration # This will be called if the battery is full or if someone # pulled the plug (and thus interruped the charging) duration = self.sim.now() - charge_start self.battery.charge(self.kw, duration) … If the simulation ends while the PEM has yielded "yield hold, self, duration", "self.battery.charge()" wont get called. I’ve tryied to wrap the yield in a try: yield except GeneratorExit, but this works neither. It seems like the generator is not deleted correctly, when the simulation finishes and so no GeneratorExit is raised. My current work-around for this is to call charge() manually for each process, when the simulation has finished, but this is not really satisfying … ;-) Ontje and I are currently fiddling around with it and try to find out, where references to the PEM could be still alive after the sim. The eventlist is set to None and process._nextpoint is also None … Any ideas? Best regards, Stefan ------------------------------------------------------------------------------ This SF.Net email is sponsored by the Verizon Developer Community Take advantage of Verizon's best-in-class app development support A streamlined, 14 day to market process makes app distribution fast and easy Join now and get one step closer to millions of Verizon customers http://p.sf.net/sfu/verizon-dev2dev _______________________________________________ Simpy-users mailing list Simpy-users <at> lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/simpy-users

------------------------------------------------------------------------------
This SF.Net email is sponsored by the Verizon Developer Community
Take advantage of Verizon's best-in-class app development support
A streamlined, 14 day to market process makes app distribution fast and easy
Join now and get one step closer to millions of Verizon customers
http://p.sf.net/sfu/verizon-dev2dev 
_______________________________________________
Simpy-users mailing list
Simpy-users <at> lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/simpy-users
Stefan Scherfke | 7 Jan 10:17
Picon

Notifiy a PEM that a simulation is over

Moin moin,

is there an easy way to notify a PEM that the simulation finished while the PEM was in a "yield hold"?

Example for a battery controller:

    …

    charge_start = self.sim.now()
    duration = self.get_charge_duration()
    yield hold, self, duration

    # This will be called if the battery is full or if someone
    # pulled the plug (and thus interruped the charging) 
    duration = self.sim.now() - charge_start
    self.battery.charge(self.kw, duration)

    …

If the simulation ends while the PEM has yielded "yield hold, self, duration", "self.battery.charge()"
wont get called.

I’ve tryied to wrap the yield in a try: yield except GeneratorExit, but this works neither. It seems like
the generator is not deleted correctly, when the simulation finishes and so no GeneratorExit is raised.

My current work-around for this is to call charge() manually for each process, when the simulation has
finished, but this is not really satisfying … ;-)

Ontje and I are currently fiddling around with it and try to find out, where references to the PEM could be
still alive after the sim. The eventlist is set to None and process._nextpoint is also None …

Any ideas?

Best regards,
Stefan
------------------------------------------------------------------------------
This SF.Net email is sponsored by the Verizon Developer Community
Take advantage of Verizon's best-in-class app development support
A streamlined, 14 day to market process makes app distribution fast and easy
Join now and get one step closer to millions of Verizon customers
http://p.sf.net/sfu/verizon-dev2dev 
Klaus G. Muller | 25 Dec 14:00
Picon
Picon
Favicon

Merry Christmas!

All,
I have been on a cruise for 24 days. Connectivity on the ship was poor 
and expensive (61 US cents per minute), so I could not participate in 
the SimPy users list discussions for a while. Now I am back and raring 
to go!

Merry Christmas to all of you!

Klaus Müller

------------------------------------------------------------------------------
This SF.Net email is sponsored by the Verizon Developer Community
Take advantage of Verizon's best-in-class app development support
A streamlined, 14 day to market process makes app distribution fast and easy
Join now and get one step closer to millions of Verizon customers
http://p.sf.net/sfu/verizon-dev2dev 
Alan Marchiori | 8 Dec 00:11

Re: queueing activations?

Yes, the program you have would produce the output I wanted...

But, the example I gave was just for illustration.  In my real
application I have many asynchronous processes that may or may not
(re)activate other processes (depending on input data).  But if a
process is reactivated more than once only the _last_ one is
scheduled.  The problem with what you wrote is the Process that gets
activated doesn't know the next time it will be activated (because
this information comes from another process) so it cannot do a yield
hold.

I must be misusing SimPy in this case.  I am looking into using a
Resource object to queue the activations, but I'm not entirely sure
that's what I want.

And callback is from being a C programmer for many years, since the
PEM is a callback function pointer (if I understand this correctly),
but PEM is what I mean.

Thanks,

On Mon, Dec 7, 2009 at 3:24 PM, Tony Vignaux <vignaux <at> gmail.com> wrote:

> Alan, > > I am not quite sure what you are trying to do and, in particular, the use of > the name callback for the PEM confuses me. > I THINK that the following program is what you are after. I have changed the > identifier of the PEM to pem. > > from SimPy.Simulation import * > > class AProcess(Process): >    def __init__(self): >        Process.__init__(self) >    def pem(self): >        while now() <= 12: >            print "awake at", now() >            yield hold,self,1 > > > def main(): >    initialize() # required >    a = AProcess() >    activate(a, a.pem(), at=10.0) >    simulate(until=100) > > if __name__ == '__main__': main() > > > On Tue, Dec 8, 2009 at 7:50 AM, Alan Marchiori <alan <at> alanmarian.com> wrote: >> >> Hi, >> >> I'm just getting started with SimPy and maybe am a bit confused about >> how to queue events... I made this simple example: >> >> <code> >> from SimPy.Simulation import * >> >> class AProcess(Process): >>    def __init__(self): >>        Process.__init__(self) >>    def callback(self): >>        while True: >>            print "awake at", now() >>            yield passivate, self >> >> def main(): >>    initialize() # required >> >>    a = AProcess() >> >>    activate(a, a.callback(), at=10.0) >>    reactivate(a, at=11) >>    reactivate(a, at=12) >> >>    simulate(until=100) >> >> if __name__ == '__main__': main() >> </code> >> >> the output is: >> awake at 12 >> >> >> I expected the output to be: >> awake at 10 >> awake at 11 >> awake at 12 >> >> Is this sort of behavior possible (where activations are queued?) ? >> >> Thanks, >> >> >> ------------------------------------------------------------------------------ >> Join us December 9, 2009 for the Red Hat Virtual Experience, >> a free event focused on virtualization and cloud computing. >> Attend in-depth sessions from your desk. Your couch. Anywhere. >> http://p.sf.net/sfu/redhat-sfdev2dev >> _______________________________________________ >> Simpy-users mailing list >> Simpy-users <at> lists.sourceforge.net >> https://lists.sourceforge.net/lists/listinfo/simpy-users > >
------------------------------------------------------------------------------ Return on Information: Google Enterprise Search pays you back Get the facts. http://p.sf.net/sfu/google-dev2dev
Alan Marchiori | 7 Dec 19:50

queueing activations?

Hi,

I'm just getting started with SimPy and maybe am a bit confused about
how to queue events... I made this simple example:

<code>
from SimPy.Simulation import *

class AProcess(Process):
    def __init__(self):
        Process.__init__(self)
    def callback(self):
        while True:
            print "awake at", now()
            yield passivate, self

def main():
    initialize() # required

    a = AProcess()

    activate(a, a.callback(), at=10.0)
    reactivate(a, at=11)
    reactivate(a, at=12)

    simulate(until=100)

if __name__ == '__main__': main()
</code>

the output is:
awake at 12

I expected the output to be:
awake at 10
awake at 11
awake at 12

Is this sort of behavior possible (where activations are queued?) ?

Thanks,

------------------------------------------------------------------------------
Join us December 9, 2009 for the Red Hat Virtual Experience,
a free event focused on virtualization and cloud computing. 
Attend in-depth sessions from your desk. Your couch. Anywhere.
http://p.sf.net/sfu/redhat-sfdev2dev

Gmane