Today another technical post with a quick introduction to job shop simulation in SimPy – another discrete-event simulation model development option in Python. This package is among my favorite simulation tools. You can see some examples that I built with it here:
In this article I model a small job shop with 2 machines. Both machines can run the same jobs, i.e. can process the same product families / part numbers, are have the same processing times and capacities.
Below Python code implements the simple job shop in Python using SimPy and random.
import simpy
import random
SEED = 42
MACHINES = 2 # amount of machines
INTERVAL = 10.0 # mean time between two jobs
DURATION = 5.0 # mean processing time of a job
JOBS = 10 # number of jobs that have to be completed
def production(env, name, machines):
""" simulates job execution, i.e. production on the machine
used for modelling the production process on the machine,
once job execution on the machine has been initiated
Args:
# env (simpy.Environment): contains simulation state
# name (str): name of job being executed
# machines (simpy.Resource): can be used by limited number of processes
"""
with machines.request() as request:
yield request
print(f"{name} started at time {env.now}")
yield env.timeout(DURATION)
print(f"{name} completed at time {env.now}")
def generate_jobs(env, machines, jobs):
""" simulates generation of new jobs
generates new job based on interval time between jobs;
creates job by iniviting production process
Args:
# env (simpy.Environment): contains simulation state
# machines (simpy.Resource): can be used by limited number of processes
# jobs (int): amount of jobs to generate
"""
for i in range(jobs):
yield env.timeout(random.expovariate(1.0/INTERVAL))
env.process(production(env, f"job {i+1}", machines))
# implementing actual simulation run
random.seed(SEED)
env = simpy.Environment()
machines = simpy.Resource(env, capacity=MACHINES)
env.process(generate_jobs(env, machines, JOBS))
# executing simulation run
env.run(until=100.0)
In above code I implement a simulation model that has two machines and 10 jobs. The job duration is independent of the machine, but is randomly distributed. The simulation runs for 100 time steps. Here is the output that I get:
job 1 started at time 10.200602872748009
job 2 started at time 10.453891263175398
job 1 completed at time 15.200602872748009
job 3 started at time 15.200602872748009
job 2 completed at time 15.453891263175398
job 4 started at time 16.195993760626187
job 3 completed at time 20.200602872748007
job 4 completed at time 21.195993760626187
job 5 started at time 29.531920488707016
job 5 completed at time 34.531920488707016
job 6 started at time 40.82365057669294
job 6 completed at time 45.82365057669294
job 7 started at time 63.09653157220674
job 8 started at time 64.0060556182724
job 7 completed at time 68.09653157220674
job 8 completed at time 69.0060556182724
job 9 started at time 69.4865172122373
job 10 started at time 69.78901898431089
job 9 completed at time 74.4865172122373
job 10 completed at time 74.78901898431089
You can learn more in the documentation here:
SimPy vs. salabim in Python
There is another popular module in Python for discrete-event simulation: salabim. I never developed a commercial application in salabim (as of today), but here are some differences between SimPy and salabim.
SimPy and salabim have different syntax for creating and managing simulation components. SimPy uses Python generator functions to define processes, while salabim uses Python classes that inherit from the Component class. salabim also provides a more object-oriented interface for creating and managing simulation components.
I believe salabim to be faster for larger simulations, due to its more efficient implementation. salabim provides some additional features that are not available in SimPy, such as automatic batching of events, more advanced queuing mechanisms, and a built-in statistical analysis library.
SimPy is in my opinion easier to learn and easier to use than salabim, due to its simpler syntax and more beginner-friendly documentation. It also has a larger and more active user community than salabim, which means that it has more third-party libraries, examples, and support resources available.
Concluding remarks on job shop simulation in SimPy
SimPy is free to use and can be used for complex process simulations. A job shop is just one example of what can be modelled in SimPy. In this case I demonstrated a very simple job shop. In the same way, you can model and simulate a more complex and realistic job shop. E.g. with various routings, machine groups and departments, different layouts and production lines, and e.g. more complex production schedules. You can also use SimPy for supply chain model implementation and simulation, as I e.g. did here:
Data scientist focusing on simulation, optimization and modeling in R, SQL, VBA and Python
2 comments
Thank you for this nice introduction to DES with Python.
As the core developer of salabim, I want to comment on your comparison of SimPy and salabim. You say that syntax of SimPy is simpler and that it is easier for beginners. I would say that the syntax of salabim is different and arguably easier to read and write. But essentially they are quite similar. See also code at the bottom of this comment.
SimPy has certainly a larger user base and more third party packages. On the other hand salabim offers so much more functionality out of the box that there’s less need for extra tooling.
With the extras that salabim offers, you forgot to mention the main feature: animation. In my opinion it’s neary essential have an animation when dealing with dynamic processes and interactions, both for the developer and the client, Salabim can even do that in 3D!
This is the equivalent model in salabim. Note that it only took three lines of code to add a useful animation!
import salabim as sim
SEED = 42
MACHINES = 2 # amount of machines
INTERVAL = 10.0 # mean time between two jobs
DURATION = 5.0 # mean processing time of a job
JOBS = 10 # number of jobs that have to be completed
class Product(sim.Component):
def process(self):
yield self.request(machines)
print(f”{self.name()} started at time {env.now()}”)
yield self.hold(DURATION)
print(f”{self.name()} completed at time {env.now()}”)
env = sim.Environment(random_seed=SEED)
machines = sim.Resource(“machines”, capacity=MACHINES)
sim.ComponentGenerator(Product, iat=sim.Exponential(INTERVAL), number=JOBS)
env.animate(True)
machines.claimers().animate(x=500, y=100, title=”work in progress”)
machines.requesters().animate(x=200, y=100, title=”work waiting”)
env.run(100.0)
Dear Ruud van der Ham, thank you for your comment. I will publish your example on the blog next week and point out the benefits of salabim as summarized by you 🙂