J.C. Kantor (Kantor.1@nd.edu)
This IPython notebook demonstrates elementary use of the SimPy package for discrete event simulation.
from IPython.core.display import HTML
HTML(open("styles/custom.css", "r").read())
!pip install simpy
import simpy as simpy
#simpy.test()
Requirement already satisfied: simpy in /Users/jeff/anaconda/lib/python3.5/site-packages
A typical simpy model consists of an environment, processes that create events for the environment to process, and resources. We'll start by setting up an environment and running a simulation. This won't do anything, but it is valid (if useless) simulation.
import simpy
# create the simulation environment
env = simpy.Environment()
# run the simulation
env.run()
An example of a process is a clock that ticks at regular intervals, and at each tick prints a message showing the current time.
The clock is a regular python function that executes until it encounters the yield env.timeout(tick)
statement. At that point a new event is scheduled for tick time units in the future after which execution will continue.
The env.process(clock(env, 2.0))
statement adds a clock to the simulation environment. The env.run(until=10)
statement processes the environment 10 simulated time units.
import simpy
# define a clock process
def clock(env,tick,name):
while True:
print("Time = {:8.6f} minutes".format(env.now), name)
yield env.timeout(tick)
# create the simulation environment
env = simpy.Environment()
# add the clock process to the environment. Set the tick interval.
env.process(clock(env, 2.0,'tick'))
env.process(clock(env,1.2,'tock'))
# run the simulation for a fixed period of time
env.run(until=20)
Time = 0.000000 minutes tick Time = 0.000000 minutes tock Time = 1.200000 minutes tock Time = 2.000000 minutes tick Time = 2.400000 minutes tock Time = 3.600000 minutes tock Time = 4.000000 minutes tick Time = 4.800000 minutes tock Time = 6.000000 minutes tick Time = 6.000000 minutes tock Time = 7.200000 minutes tock Time = 8.000000 minutes tick Time = 8.400000 minutes tock Time = 9.600000 minutes tock Time = 10.000000 minutes tick Time = 10.800000 minutes tock Time = 12.000000 minutes tock Time = 12.000000 minutes tick Time = 13.200000 minutes tock Time = 14.000000 minutes tick Time = 14.400000 minutes tock Time = 15.600000 minutes tock Time = 16.000000 minutes tick Time = 16.800000 minutes tock Time = 18.000000 minutes tock Time = 18.000000 minutes tick Time = 19.200000 minutes tock
import simpy
# define a clock process
def clock(env,name,tick):
while True:
print "Clock {:s} ticks. Time = {:8.6f} minutes".format(name, env.now)
yield env.timeout(tick)
# create the simulation environment
env = simpy.Environment()
# add the clock process to the environment. Set the tick interval.
env.process(clock(env, "A", 2.0))
env.process(clock(env, "B", 1.3))
# run the simulation for a fixed period of time
env.run(until=10)
Clock A ticks. Time = 0.000000 minutes Clock B ticks. Time = 0.000000 minutes Clock B ticks. Time = 1.300000 minutes Clock A ticks. Time = 2.000000 minutes Clock B ticks. Time = 2.600000 minutes Clock B ticks. Time = 3.900000 minutes Clock A ticks. Time = 4.000000 minutes Clock B ticks. Time = 5.200000 minutes Clock A ticks. Time = 6.000000 minutes Clock B ticks. Time = 6.500000 minutes Clock B ticks. Time = 7.800000 minutes Clock A ticks. Time = 8.000000 minutes Clock B ticks. Time = 9.100000 minutes
import simpy
# define a clock process
def clock(env,name,tick):
nTicks = 0
while True:
nTicks += 1
print ("Clock {:s}, tick number {:d}. Time = {:8.6f} minutes".format(name, nTicks, env.now))
yield env.timeout(tick)
# create the simulation environment
env = simpy.Environment()
# add the clock process to the environment. Set the tick interval.
env.process(clock(env, "A", 2.0))
env.process(clock(env, "B", 1.3))
# run the simulation for a fixed period of time
env.run(until=20)
Clock A, tick number 1. Time = 0.000000 minutes Clock B, tick number 1. Time = 0.000000 minutes Clock B, tick number 2. Time = 1.300000 minutes Clock A, tick number 2. Time = 2.000000 minutes Clock B, tick number 3. Time = 2.600000 minutes Clock B, tick number 4. Time = 3.900000 minutes Clock A, tick number 3. Time = 4.000000 minutes Clock B, tick number 5. Time = 5.200000 minutes Clock A, tick number 4. Time = 6.000000 minutes Clock B, tick number 6. Time = 6.500000 minutes Clock B, tick number 7. Time = 7.800000 minutes Clock A, tick number 5. Time = 8.000000 minutes Clock B, tick number 8. Time = 9.100000 minutes Clock A, tick number 6. Time = 10.000000 minutes Clock B, tick number 9. Time = 10.400000 minutes Clock B, tick number 10. Time = 11.700000 minutes Clock A, tick number 7. Time = 12.000000 minutes Clock B, tick number 11. Time = 13.000000 minutes Clock A, tick number 8. Time = 14.000000 minutes Clock B, tick number 12. Time = 14.300000 minutes Clock B, tick number 13. Time = 15.600000 minutes Clock A, tick number 9. Time = 16.000000 minutes Clock B, tick number 14. Time = 16.900000 minutes Clock A, tick number 10. Time = 18.000000 minutes Clock B, tick number 15. Time = 18.200000 minutes Clock B, tick number 16. Time = 19.500000 minutes
import simpy
import random
# geometric brownian motion
def gbm(env,name,tick,P,mu,sigma):
t = 0;
while True:
Plog.append(P)
tlog.append(t)
yield env.timeout(tick)
P += P*(mu*tick + sigma*random.normalvariate(0,1)*sqrt(tick))
t += tick
# create the simulation environment
env = simpy.Environment()
# add the clock process to the environment. Set the tick interval.
env.process(gbm_old(env, "A", sqrt(1.0/252), 80.0, 0, .3))
# run the simulation for a fixed period of time
Plog = []
tlog = []
env.run(until=10)
plot(tlog,Plog)
xlabel('Date')
ylabel('Price')
--------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-17-bd3b196fecc8> in <module>() 16 17 # add the clock process to the environment. Set the tick interval. ---> 18 env.process(gbm_old(env, "A", sqrt(1.0/252), 80.0, 0, .3)) 19 20 # run the simulation for a fixed period of time NameError: name 'gbm_old' is not defined
import simpy
import random
import numpy as np
class gbm(object):
def __init__(self,env,name,tick,val,mu,sigma):
self.env = env
self.name = name
self.tick = tick
self.val = val
self.mu = mu
self.sigma = sigma
self.t = 0
def process(self):
while True:
yield self.env.timeout(self.tick)
self.t += self.tick
self.val += self.val*(self.mu*self.tick + self.sigma*random.normalvariate(0,1)*np.sqrt(self.tick))
def reporter(env,tick,gbm):
t = 0
while True:
yield env.timeout(tick)
t += tick
print(t, gbm.val)
env = simpy.Environment()
a = gbm(env,'A',1.0/np.sqrt(12.0),80.0,0,0.30)
env.process(a.process())
env.process(reporter(env,1.0,a))
env.run(until=20)
1.0 80.7675367205 2.0 74.887121441 3.0 43.3863034774 4.0 36.1322746874 5.0 27.3210018971 6.0 22.1435662098 7.0 25.1007915851 8.0 32.5795722168 9.0 46.6315095102 10.0 51.1479983156 11.0 40.2624445257 12.0 55.4630517992 13.0 62.2144445446 14.0 90.4502257938 15.0 90.0176180144 16.0 106.023491267 17.0 146.273459787 18.0 198.014117565 19.0 391.887038298
Setting up a class provides a means of modeling more complex behaviors. Here we'll consider a Roomba cleaning robot that can be either in a running mode or a charging mode.
class Roomba(object):
def __init__(self,env,name,charge_duration,clean_duration):
self.env = env
self.name = name
self.charge_duration = charge_duration
self.clean_duration = clean_duration
self.proc = env.process(self.run())
def run(self):
while True:
yield env.process(self.charge())
yield env.process(self.clean())
def clean(self):
print ("{:<3s} start cleaning at {:4.1f}".format(self.name,env.now))
yield env.timeout(self.clean_duration)
def charge(self):
print("{:<3s} start charging at {:4.1f}".format(self.name,env.now))
yield env.timeout(self.charge_duration)
import simpy
env = simpy.Environment()
A = Roomba(env,'A',1.1,2.3)
B = Roomba(env,'B',0.9,3.1)
# start processes
env.run(until=24)
A start charging at 0.0 B start charging at 0.0 B start cleaning at 0.9 A start cleaning at 1.1 A start charging at 3.4 B start charging at 4.0 A start cleaning at 4.5 B start cleaning at 4.9 A start charging at 6.8 A start cleaning at 7.9 B start charging at 8.0 B start cleaning at 8.9 A start charging at 10.2 A start cleaning at 11.3 B start charging at 12.0 B start cleaning at 12.9 A start charging at 13.6 A start cleaning at 14.7 B start charging at 16.0 B start cleaning at 16.9 A start charging at 17.0 A start cleaning at 18.1 B start charging at 20.0 A start charging at 20.4 B start cleaning at 20.9 A start cleaning at 21.5 A start charging at 23.8