A solution for saving a SimPy simulation state by pickling
Klaus Muller <kgmuller <at> xs4all.nl>
2007-02-15 14:41:18 GMT
Dear Carole,
I worked on your problem (pickling a SimPy simulation state) and came up
with a workaround to Python's limitation of not being capable of pickling
generators.
Basically, one has to
* maintain state in the PEMs (generator method) of the instances one wishes
to pickle,
* direct the program flow based on the initial state by "if state== . . . "
statements,
* for all restarted states, repeat the last yield statement executed before
the pickle
* pickle the interrupt (save) time,
* pickle the length of the Resource's activeQ (= the number of processes
holding a unit of that Resource)
* run through the activeQ of the Resource instance(s) with an index, say i:
- make each process in the activeQ passive (set i._nextpoint=None and
i._nextTime=None). Then it is not
a generator.
- remove any reference to generators( set i._priority={})
- pickle instance i
* pickle the length of the Resource's waitQ (= the number of processes
waiting for that Resource)
* run through the waitQ of the Resource instance(s) with an index, say i:
- remove any reference to generators( set i._priority={})
- pickle instance i
To reload and continue running the simulation,
* load the pickle time
* load the number of active processes
* generate that number of process instances and activate them at the
pickle/restart time
* load the number of waiting processes
* generate that number of process instances and activate them at the
pickle/restart time
* run the simulation
The attached file shows an adaptation of one of the models in the Bank
tutorial. It works!
PLEASE, comment!
Regards,
Klaus Müller
PS: We should all write to the Python developers and request that they
change pickle so that generators can be pickled. This requirement was
already raised in 1994, but no action so far!
#!/usr/bin/env python
""" bank08: A counter with a random service time"""
from SimPy.Simulation import *
from random import expovariate, seed
import pickle as p
class Source(Process):
""" Source generates customers randomly"""
def generate(self,number,interval):
for i in range(number):
c = Customer(name = "Customer%3.0f"%now())
activate(c,c.visit(timeInBank=12.0))
t = expovariate(1.0/interval)
yield hold,self,t
normal=0
inQ=1
actif=2
class Customer(Process):
""" Customer arrives, is served and leaves """
def visit(self,timeInBank=0,state=normal):
if state==normal:
self.state=state
self.arrive=now()
print "%7.4f %s: Here I am "%(now(),self.name)
self.state=inQ
yield request,self,counter
self.state=actif
wait=now()-self.arrive
print "%7.4f %s: Waited %6.3f"%(now(),self.name,wait)
self.tib = expovariate(1.0/timeInBank)
yield hold,self,self.tib
yield release,self,counter
print "%7.4f %s: Finished "%(now(),self.name)
elif self.state==inQ:
yield request,self,counter
self.state=actif
wait=now()-self.arrive
print "%7.4f %s: Waited %6.3f"%(now(),self.name,wait)
self.tib = expovariate(1.0/timeInBank)
yield hold,self,self.tib
yield release,self,counter
print "%7.4f %s: Finished "%(now(),self.name)
elif self.state==actif:
yield request,self,counter
yield hold,self,self.tib-trestart
yield release,self,counter
print "%7.4f %s: Finished "%(now(),self.name)
class Pickler(Process):
def picklesim(self):
#open file for pickling
f=open("pickle.dat","w")
#pickle time
p.dump(now(),f)
#pickle Customer instances in activeQ
#the number of instances in activeQ
p.dump(len(counter.activeQ),f)
#now the instances
for i in counter.activeQ:
i._nextpoint=None
i._priority={}
i._nextTime=None
#print i.name
p.dump(i,f)
#pickle Customer instances in waitQ
#the number of instances in waitQ
p.dump(len(counter.waitQ),f)
#now the instances
for i in counter.waitQ:
#i._nextpoint=None #remove generator
i._priority={}
p.dump(i,f)
#close pickle file
f.close()
#end simulation
stopSimulation()
yield hold,self,0
def model(theseed,restart=False):
global counter,trestart
if not restart: #initial run
seed(theseed)
counter = Resource(name="Karen")
initialize()
source = Source('Source')
activate(source,source.generate(10,interval=10.0),0.0)
tSimSave=250. # time when simulation state is saved
pick=Pickler()
activate(pick,pick.picklesim(),at=tSimSave)
simulate(until=1000.0)
else: #model restarted
seed(2*theseed)
f=open("pickle.dat")
trestart=p.load(f)
initialize()
counter = Resource(name="Karen")
#load the number of Customer instances in counter.activeQ
nrActive=p.load(f)
#regenerate the active Customer instances
for i in range(nrActive):
c=p.load(f)
activate(c,c.visit(timeInBank=12.,state=c.state),at=trestart)
#load the number of Customer instances in counter.waitQ
nrWaiting=p.load(f)
#regenerate the active Customer instances
for i in range(nrWaiting):
c=p.load(f)
activate(c,c.visit(timeInBank=12.,state=c.state),at=trestart)
#the Source process (needs pickling as well, but not yet done)
source = Source('Source')
activate(source,source.generate(10,interval=10.0),at=trestart)
simulate(until=1000.0)
model(theseed=123456,restart=False)
print "\n**** After pickle and reload/restart of run (t=%s)"%now()
model(theseed=123456,restart=True)
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys-and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
Simpy-users mailing list
Simpy-users <at> lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/simpy-users