Basics of Discrete Event Simulation using SimPy in Python

SimPy (rhymes with “Blimpie”) is a python package for process-oriented discrete-event simulation.
The easiest way to install SimPy is via pip:
pip install simpy
And the output you may get will be something like,
At the time of writing, simpy-3.0.11 is the most recent version of SimPy, and we will use it for all the below examples.
In case, SimPy is already installed, use the –U option for pip to upgrade.
pip install –U simpy
Note: You need to have python 2.7 or above version and for Linux/Unix/MacOS you may need root privileges to install SimPy.
To check if SimPy was successfully installed, open a python shell and import simpy.
SimPy, which is a discrete-event simulation library. The active components of simpy (like messages, vehicles or customers) are modeled with processes. In SimPy, active entities are known as processes. A process is a Python generator that yields discrete events. Please note, I’m not returning anything but I’m a yield(ing), that is the difference between a normal function and a generator. This allows us to create events and yield them in order to wait for them to be triggered.
When a process yields an event, the process gets suspended. SimPy allows us to resume the suspended process whenever the event is triggered. In case, multiple processes wait for the same event, SimPy resumes them in the same order in which they yielded that event.
def gen(x):
y = yield x+1
return y
>>> g = gen(1)
>>> next(g)
2
>>> next(g)
Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
next(g)
StopIteration
Above Iteration stops after variable x first yield.
Events of Timeout type are triggered after a certain amount of (simulated) time has passed. Timeout event allows a process to hold or sleep for a certain amount of time. All the events including Timeout can be created by calling the appropriate method of the Environment that the process lives in.
#Import important library
from random import randint
import simpy
#Config
TALKS_PER_SESSION = 3
TALK_LENGTH = 30
BREAK_LENGTH = 15
ATTENDEES = 1
def attendee(env, name, knowledge=0, hunger=0):
   talks =0
   breaks =0
   #Repeat sessions
   while True:
      # Visit talks
      for i in range(TALKS_PER_SESSION):
      print('Talk {0} begins at {1}'.format(talks+1, env.now))
      knowledge += randint(0, 3) / (1 + hunger)
      hunger += randint(1, 4)
      talks += 1
      yield env.timeout(TALK_LENGTH)
      print(f'Talk {talks} ends at {env.now}')
   print('Attendee %s finished talks with knowledge %.2f and hunger ' '%.2f' %( name, knowledge, hunger))
   #Take a break, Go to buffet
   food = randint(3, 12)
   hunger -= min(food, hunger)
   yield env.timeout(BREAK_LENGTH)
   print('Attendee %s finished eating with hunger %.2f ' %(name, hunger))
# Run Simulation
env = simpy.Environment()
for i in range(ATTENDEES):
   env.process(attendee(env, i))
env.run(until=250)
If we run the above program, we’ll see the output something like,
Above we try to replicate the conference hall scenario, where there is a random number of speakers with talks per session of 4 with talk length of 40 min and break the length of 30 min.
Our attendee process requires a reference to an Environment (env), name, knowledge, and hunger in order to create new events. Sessions will go in an infinite loop till it becomes False. The attendee() function is a generator which will not terminate but will pass the control flow back to the simulation once a yield statement is reached.
At last, we run a demo simulation 'conference attendee" until a set value of 250 is not reached (includes- 3 talks, 1 break, 3 talks, 1 break, 1 talk).