#!/usr/bin/env python # coding: utf-8 # ###### The latest version of this IPython notebook is available at [http://github.com/jckantor/ESTM60203](http://github.com/jckantor/ESTM60203) for noncommercial use under terms of the [Creative Commons Attribution Noncommericial ShareAlike License (CC BY-NC-SA 4.0)](http://creativecommons.org/licenses/by-nc-sa/4.0/). # J.C. Kantor (Kantor.1@nd.edu) # # Getting Started with Discrete Event Simulation # This [IPython notebook](http://ipython.org/notebook.html) demonstrates elementary use of the [SimPy](http://simpy.readthedocs.org/en/latest/) package for discrete event simulation. # ### Initializations # In[1]: from IPython.core.display import HTML HTML(open("styles/custom.css", "r").read()) # ### SimPy Installation # In[3]: get_ipython().system('pip install simpy') import simpy as simpy #simpy.test() # ## Introduction to Modeling with SimPy # ### A Minimal SimPy Model # 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. # In[4]: import simpy # create the simulation environment env = simpy.Environment() # run the simulation env.run() # ### Adding a Process # 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. # In[13]: 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) # ### Mutliple Instances of a Process # # In[15]: 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) # ### Processes Manage their own State # In[16]: 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) # ## Application Examples # ### Geometric Brownian Motion # In[17]: 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') # In[25]: 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) # ## Application # 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. # In[26]: 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) # In[ ]: