Basics Intermediate Advanced
aialgorithmsapibest-practicescareercommunitydatabasesdata-sciencedata-structuresdata-vizdevopsdjangodockereditorsflaskfront-endgamedevguimachine-learningnewsnumpyprojectspythonstdlibtestingtoolsweb-devweb-scraping
- What Simulation Is
- How Simulation Works
- How to Get Started With simpy
- How to Simulate With the simpy Package
- Brainstorming a Simulation Algorithm
- Setting Up the Environment
- Creating the Environment: Class Definition
- Moving Through the Environment: Function Definition
- Making Things Happen: Function Definition
- Calculating the Wait Time: Function Definition
- Choosing Parameters: User Input Function Definition
- Finalizing the Setup: Main Function Definition
- How to Run the Simulation
- When to Change Things Up
- Conclusion
Recommended Course

Simulating Real-World Processes in Python With SimPy
27m · 11 lessons

SimPy: Simulating Real-World Processes With Python
Table of Contents
- What Simulation Is
- How Simulation Works
- How to Get Started With simpy
- How to Simulate With the simpy Package
- Brainstorming a Simulation Algorithm
- Setting Up the Environment
- Creating the Environment: Class Definition
- Moving Through the Environment: Function Definition
- Making Things Happen: Function Definition
- Calculating the Wait Time: Function Definition
- Choosing Parameters: User Input Function Definition
- Finalizing the Setup: Main Function Definition
- How to Run the Simulation
- When to Change Things Up
- Conclusion
Recommended Course
The real world is full of systems, like airports and highways, that frequently experience congestion and delay. When these systems are not optimized, their inefficiency can lead to countless unhappy customers and hours of wasted time. In this tutorial, you’ll learn how to use Python’ssimpy framework to create virtual simulations that will help you solve problems like these.
In this tutorial, you’ll learn how to:
- Use a simulation to model a real-world process
- Create a step-by-step algorithm to approximate a complex system
- Design and run a real-world simulation in Python with
simpy
In this tutorial, you’ll create a simulation for a local movie theater. Your goal is to provide the manager with a script to help find the optimal number of employees to have on staff. You can download the source code for this script by clicking on the link below:
Download Code:Click here to download the code you’ll use to learn about SimPy in this tutorial.
What Simulation Is
Asimulation is a representation of a real-world system. One can use mathematical or computational models of this system to study how it works, or what happens when parts of it are changed. Simulations are used in airports, restaurants, mechanics, government agencies, and many other systems where poor resource allocation can lead to congestion, customer dissatisfaction, and critical transportation delays.
Asystem can be any environment where things happen. Examples of real-world systems include car washes, banks, manufacturing plants, airports, post offices, call centers, and more. These systems haveagents that undergoprocesses within them. For instance:
- A car wash will have cars go through the washing process.
- An airport will have passengers go through the security check process.
- A call center will have customers go through the process of speaking with a telemarketer.
This relationship is summed up in the table below:
| System | Agent | Process |
|---|---|---|
| Car wash | Car | Wash |
| Airport | Passenger | Security check |
| Call center | Customer | Speak with a telemarketer |
Understanding the processes that agents go through within a system is an important component of logistical planning, especially for large-scale organizations. For example, an airport can see passenger wait times at a security checkpoint skyrocket if there aren’t enough workers that day. Similarly, time-sensitive mail can be delayed by days (or even weeks) if it isn’t routed properly.
These instances of congestion can havereal-life consequences on time and money, so it’s important to be able to model these processes beforehand. This gives you an idea of where the system might run into problems and how resources should be allocated ahead of time to solve those problems in the most efficient way possible.
How Simulation Works
In Python, you can use thesimpy framework for event simulation. First, take a quick look at how a simulated process would run in Python. Below is a code snippet from a simulation of a security checkpoint system. The following three lines of code set up the environment, pass all necessary functions, and run the simulation:
# Set up the environmentenv=simpy.Environment()# Assume you've defined checkpoint_run() beforehandenv.process(checkpoint_run(env,num_booths,check_time,passenger_arrival))# Let's go!env.run(until=10)The first line of code above establishes theenvironment. You’ll do this by assigningsimpy.Environment() to the desiredvariable. Here, it’s simply namedenv. This tellssimpy to create an environment object namedenv that will manage the simulation time and move the simulation through each subsequent time step.
Once you have your environment established, you’ll pass in all of the variables that will act as yourparameters. These are the things you can vary to see how the system will react to changes. For this security checkpoint system, you’re using the following parameters:
env: the environment object to schedule and process eventsnum_booths: the number of ID check boothscheck_time:the length of time it takes to check a passenger’s IDpassenger_arrival: the rate at which passengers arrive at the queue
Then, it’s time to run the simulation! You can do this by callingenv.run() and specifying how long you want the simulation to run for. The simulation runs in minutes, so this sample code will run the simulation for 10 real-time minutes.
Note: Don’t worry! You won’t have to wait 10 actual minutes for the simulation to finish. Because simulation gives you a virtual look at a real-time process, those 10 minutes will pass in mere seconds on the computer.
To recap, here are the three steps to running a simulation in Python:
- Establish the environment.
- Pass in the parameters.
- Run the simulation.
But there’s a lot more going on underneath the hood! You’ll need to understand how to choose those parameters, and you’ll have to define all the functions that will be called when the simulation is run.
Let’s get started!
How to Get Started Withsimpy
There are a few to-dos you should check off your list before creating simulations in Python. The first thing you need to do is make sure you have a solid understanding ofPython basics. In particular, you’ll need to have a good grasp of classes and generators.
Note: If you need to freshen up on these topics, then check outIntro to Object-Oriented Programming (OOP) in Python andIntroduction to Python Generators. These are crucial pieces of the simulation process, so you’ll need to understand them before moving forward.
The next thing you’ll want to do is install the required package. The main framework you’ll be using issimpy. This is the core package that will create, manage, and run your simulation. You can install it withpip:
$python3-mpipinstallsimpyYou’ll also need a few built-in Python modules. You’ll use thestatistics module to calculate average wait times and therandom module togenerate random numbers. These come as part of the Python standard library, so you don’t need to install anything new.
Finally, you’ll need to choose how you want to run your simulation. In general, you can choose one of two options:
- Run it interactively: Use aJupyter Notebook, where each code block will contain its own class or function definition. The output will be displayed at the bottom of the notebook.
- Run it in the shell: Save your simulation as a
.pyfile and tell Python to run it in yourterminal. The output will be printed directly to the console.
Choose whichever method you’re most comfortable with! The outcome should be the same.
Throughout this tutorial, you’ll see references to a standalone file namedsimulate.py. As you move through this tutorial, the code blocks will referencesimulate.py to help you keep track of how all the pieces fit together. For your reference, you can access the full code forsimulate.py at the link below:
Download Code:Click here to download the code you’ll use to learn about SimPy in this tutorial.
Feel free to save the filesimulate.py and follow along in your favorite editor!
How to Simulate With thesimpy Package
The first step to running a simulation insimpy is to choose a process to model. Simulation is all about creating a virtual environment to reflect a real-world system. In that same spirit, you’ll “simulate” a situation for your simulation!
Imagine you’ve been hired to help the manager for a small, local movie theater. The theater has been receiving poor reviews due to theirlong wait times. The manager, who is just as concerned about cost as he is about customer satisfaction, can only afford to keep so many employees on staff.
The manager is particularly worried about what chaos can unfold once those blockbusters start coming out: lines wrapping around the theater, employees stretched to their limit, angry moviegoers missing the opening scenes… This is definitely a situation to avoid!
After checking the reviews, the manager was able to determine that a given moviegoer to their theater is willing to spend at most 10 minutes from the time they arrive until the time they put their butt in a seat. In other words, theaverage wait time for a night at the theater needs to be 10 minutes or less. The manager has asked for your help to figure out a solution to getting customer wait times under this 10 minute requirement.
Brainstorming a Simulation Algorithm
Before you write out a single line of code, it’s important that you first figure out how your process would run in real life. This is to ensure that, when you pass it along to the machine, the process is an accurate reflection of what customers will really experience. Here’s how you might think through the steps a moviegoer might take to write out your algorithm:
- Arrive at the theater, get in line, and wait to purchase a ticket.
- Buy a ticket from the box office.
- Wait in line to have the ticket checked.
- Get the ticket checked by an usher.
- Choose whether or not to get in line for the concession stand:
- If they get in line, then they purchase food.
- If they don’t get in line, then they skip to the last step.
- Go find their seat.
This is a step-by-step iteration for a moviegoer who purchases their ticket at the theater box office. You can already see which parts of this process can be controlled. You can affect how long a customer is waiting by having more cashiers available at the box office.
There are also parts of the process that can’t be controlled, like the very first step. You can’t control how many customers will arrive, or how quickly they’ll do so. You can make a guess, but you can’t simply choose a number, because that would be a poor reflection of reality. For this parameter, the best thing you can do isuse available data to determine an appropriate arrival time.
Note: Using historical data ensures that the solution you find will accurately reflect what you can expect to see in real life.
With these things in mind, it’s time to build your simulation!
Setting Up the Environment
Before you start building your simulation, you need to make sure that yourdevelopment environment is properly configured. The very first thing you’ll want to do is import the necessarypackages. You can do this by declaringimport statements at the top of your file:
importsimpyimportrandomimportstatisticsThese are the main libraries you’ll use to build a script for the theater manager. Remember, the goal is to find the optimal number of employees that gives an average wait time of less than 10 minutes. To do this, you’ll need to collect the length of time that it takes for each moviegoer to make it to their seats. The next step is to declare a list to hold these times:
wait_times=[]This list will contain the total amount of time each moviegoer spends moving through the theater, from arrival to sitting in their seat. You declare this list at the very top of the file so that you can use it inside any function you define later on.
Creating the Environment: Class Definition
The first part of the simulation you’ll want to build is the blueprint for the system. This is going to be the overall environment inside which things happen, and people or objects move from one place to another. Remember, anenvironment can be one of many different systems, like a bank, a car wash, or a security checkpoint. In this case, the environment is a movie theater, so that will be the name of yourclass:
classTheater(object):def__init__(self):# More to come!Now it’s time to think through the parts of amovie theater. Of course, there’s the theater itself, which is what you’ve called your environment. Later, you’ll explicitly declare the theater as an actualenvironment using one of thesimpy functions. For now, call itenv for short and add it to the class definition:
classTheater(object):def__init__(self,env):self.env=envAlright, what else might there be in a theater? You can figure this out by thinking through the simulation algorithm you planned out earlier. When a moviegoer arrives, they’ll need to get in line at the box office, where a cashier will be waiting to help them out. Now you’ve discovered two things about the theater environment:
- There arecashiers.
- Moviegoers canpurchase tickets from them.
Cashiers are aresource that the theater makes available to its customers, and they help moviegoers through theprocess of purchasing a ticket. Right now, you don’t know how many cashiers are available in the simulated theater. In fact, that’s the very problem you’re trying to solve. How do wait times change, depending on the number of cashiers working on a given night?
You can go ahead and call this unknown variablenum_cashiers. The exact value this variable will take can be sorted out later. For now, just know that it’s an indispensable part of the theater environment. Add it to the class definition:
classTheater(object):def__init__(self,env,num_cashiers):self.env=envself.cashier=simpy.Resource(env,num_cashiers)Here, you add the new parameternum_cashiers to your__init__() definition. Then, you create a resourceself.cashier and usesimpy.Resource() to declare how many can be in this environment at any given time.
Note: Insimpy,resources are the parts of the environment (env) that are limited in number. Using one of them takes time, and only so many (num_cashiers) are available to be used at once.
There’s one more step that you’ll need to take. A cashier isn’t going to purchase a ticket forthemselves, right? They’re going to help the moviegoer! Again, you know that this process of purchasing a ticket is going to take a certain amount of time. But just how much time?
Say you’ve asked the manager for historical data on the theater, like employee performance reviews or ticket purchase receipts. Based on this data, you’ve learned that it takes, on average, between 1 and 2 minutes to issue a ticket at the box office. How do you getsimpy to mimic this behavior? It only takes one line of code:
yieldself.env.timeout(random.randint(1,3))env.timeout() tellssimpy to trigger an event after a certain amount of time has passed. In this case, the event is that a ticket was purchased.
The time this takes could be one minute, two minutes, or three minutes. You want each moviegoer to spend a different amount of time at the cashier. To do this, you userandom.randint() to choose a random number between the given low and high values. Then, for each moviegoer, the simulation will wait for the chosen amount of time.
Let’s wrap this up in a tidy function and add it to the class definition:
classTheater(object):def__init__(self,env,num_cashiers):self.env=envself.cashier=simpy.Resource(env,num_cashiers)defpurchase_ticket(self,moviegoer):yieldself.env.timeout(random.randint(1,3))The one initiating the event inpurchase_ticket() is themoviegoer, so they must be passed as a required argument.
Note: You’ll see how the moviegoer actually purchases the ticket in the next section!
That’s it! You’ve selected a time-bound resource, defined its related process, and codified this in your class definition. For this tutorial, there are two more resources you’ll need to declare:
- Ushers to check tickets
- Servers to sell food
After checking the data the manager sent over, you determine that servers take anywhere between 1 and 5 minutes to complete an order. In addition, ushers are remarkably fast at checking tickets, with an average speed of 3 seconds!
You’ll need to add these resources to your class and define the corresponding functionscheck_ticket() andsell_food(). Can you figure out what the code should look like? When you’ve got an idea, you can expand the code block below to check your understanding:
classTheater(object):def__init__(self,env,num_cashiers,num_servers,num_ushers):self.env=envself.cashier=simpy.Resource(env,num_cashiers)self.server=simpy.Resource(env,num_servers)self.usher=simpy.Resource(env,num_ushers)defpurchase_ticket(self,moviegoer):yieldself.env.timeout(random.randint(1,3))defcheck_ticket(self,moviegoer):yieldself.env.timeout(3/60)defsell_food(self,moviegoer):yieldself.env.timeout(random.randint(1,5))Take a close look at the new resources and functions. Notice how they follow the same format as described above.sell_food() usesrandom.randint() to generate a random number between 1 and 5 minutes, representing the time it would take a moviegoer to place an order and receive their food.
The time delay forcheck_ticket() is a bit different because the ushers only take 3 seconds. Sincesimpy works in minutes, this value needs to be passed as a fraction of a minute, or3 / 60.
Moving Through the Environment: Function Definition
Alright, you’ve set up the environment by defining a class. You have resources and processes. Now you need amoviegoer to use them. When amoviegoer arrives at the theater, they’ll request a resource, wait for its process to complete, and then leave. You’ll create a function, calledgo_to_movies(), to keep track of this:
defgo_to_movies(env,moviegoer,theater):# Moviegoer arrives at the theaterarrival_time=env.nowThere are three arguments passed to this function:
env: Themoviegoerwill be controlled by the environment, so you’ll pass this as the first argument.moviegoer: This variable tracks each person as they move through the system.theater: This parameter gives you access to the processes you defined in the overall class definition.
You also declare a variablearrival_time to hold the time at which eachmoviegoer arrives at the theater. You can get this time using thesimpy call toenv.now.
You’ll want each of the processes from yourtheater to have correspondingrequests ingo_to_movies(). For example, the first process in the class ispurchase_ticket(), which uses acashier resource. Themoviegoer will need to make a request to thecashier resource to help them through thepurchase_ticket() process. Here’s a table to summarize this:
Process intheater | Request ingo_to_movies() |
|---|---|
purchase_ticket() | Request acashier |
check_ticket() | Request anusher |
sell_food() | Request aserver |
The cashier is ashared resource, which means that many moviegoers will use the same cashier. However, a cashier can only help one moviegoer at a time, so you’ll need to include some waiting behavior in your code. Here’s how that works:
defgo_to_movies(env,moviegoer,theater):# Moviegoer arrives at the theaterarrival_time=env.nowwiththeater.cashier.request()asrequest:yieldrequestyieldenv.process(theater.purchase_ticket(moviegoer))Here’s how this code works:
theater.cashier.request():moviegoergenerates a request to use acashier.yield request:moviegoerwaits for acashierto become available if all are currently in use. To learn more about theyieldkeyword, check outHow to Use Generators and yield in Python.yield env.process():moviegoeruses an availablecashierto complete the given process. In this case, that’s to purchase a ticket with a call totheater.purchase_ticket().
After a resource is used, it must be freed up for the next agent to use. You could do this explicitly withrelease(), but in the code above, you use awith statement instead. This shortcut tells the simulation to automatically release the resource once the process is complete. In other words, once the ticket is bought, themoviegoer will leave, and the cashier will automatically be ready to take the next customer.
When a cashier is freed up, themoviegoer will spend some time buying their ticket.env.process() tells the simulation to go to theTheater instance and run thepurchase_ticket() process on thismoviegoer. Themoviegoer will repeat thisrequest, use, release cycle to have their ticket checked:
defgo_to_movies(env,moviegoer,theater):# Moviegoer arrives at the theaterarrival_time=env.nowwiththeater.cashier.request()asrequest:yieldrequestyieldenv.process(theater.purchase_ticket(moviegoer))withtheater.usher.request()asrequest:yieldrequestyieldenv.process(theater.check_ticket(moviegoer))Here, the structure for the code is the same.
Then, there’s the optional step of buying food from the concession stand. You can’t know whether a moviegoer will want to purchase snacks and drinks. One way to deal with this uncertainty is to introduce a bit ofrandomness to the function.
Eachmoviegoer either will or will not want to buy food, which you can store as theBoolean valuesTrue orFalse. Then, use therandom module to have the simulation randomly decide whether or notthis particularmoviegoer is going to proceed to the concession stand:
defgo_to_movies(env,moviegoer,theater):# Moviegoer arrives at the theaterarrival_time=env.nowwiththeater.cashier.request()asrequest:yieldrequestyieldenv.process(theater.purchase_ticket(moviegoer))withtheater.usher.request()asrequest:yieldrequestyieldenv.process(theater.check_ticket(moviegoer))ifrandom.choice([True,False]):withtheater.server.request()asrequest:yieldrequestyieldenv.process(theater.sell_food(moviegoer))Thisconditional statement will return one of two outcomes:
True: Themoviegoerwill request a server and order food.False: Themoviegoerwill instead go to find their seats without purchasing any snacks.
Now, remember the goal of this simulation is to determine the number of cashiers, ushers, and servers that should be on staff to keep wait times under 10 minutes. To do this, you’ll need to know how long it took any givenmoviegoer to make it to their seats. You useenv.now at the beginning of the function to track thearrival_time, and again at the end when eachmoviegoer is finished with all processes and heading into the theater:
defgo_to_movies(env,moviegoer,theater):# Moviegoer arrives at the theaterarrival_time=env.nowwiththeater.cashier.request()asrequest:yieldrequestyieldenv.process(theater.purchase_ticket(moviegoer))withtheater.usher.request()asrequest:yieldrequestyieldenv.process(theater.check_ticket(moviegoer))ifrandom.choice([True,False]):withtheater.server.request()asrequest:yieldrequestyieldenv.process(theater.sell_food(moviegoer))# Moviegoer heads into the theaterwait_times.append(env.now-arrival_time)You useenv.now to get the time at which themoviegoer has finished all processes and made it to their seats. You subtract the moviegoer’sarrival_time from this departure time and append the resulting time difference to your waiting list,wait_times.
Note: You could store the departure time in a separate variable likedeparture_time, but this would make your code very repetitive, which violates theD.R.Y. principle.
Thismoviegoer is ready to watch some previews!
Making Things Happen: Function Definition
Now you’ll need to define a function to run the simulation.run_theater() will be responsible for creating an instance of a theater and generating moviegoers until the simulation stops. The first thing this function should do is create an instance of a theater:
defrun_theater(env,num_cashiers,num_servers,num_ushers):theater=Theater(env,num_cashiers,num_servers,num_ushers)Since this is the main process, you’ll need to pass all of the unknowns you’ve declared so far:
num_cashiersnum_serversnum_ushers
These are all variables that the simulation needs to create and control the environment, so it’s absolutely vital to pass them all. Then, you define a variabletheater and tell the simulation to set up the theater with a certain number of cashiers, servers, and ushers.
You also might want to start your simulation with a few moviegoers waiting at the theater. There will probably be a few people ready to go as soon as the doors open! The manager says to expect around 3 moviegoers in line ready to buy tickets as soon as the box office opens. You can tell the simulation to go ahead and move through this initial group like so:
defrun_theater(env,num_cashiers,num_servers,num_ushers):theater=Theater(env,num_cashiers,num_servers,num_ushers)formoviegoerinrange(3):env.process(go_to_movies(env,moviegoer,theater))You userange() to populate the theater with 3 moviegoers. Then, you useenv.process() to tell the simulation to prepare to move them through the theater. The rest of the moviegoers will make it to the theater in their own time. So, the function should keep sending new customers into the theater as long as the simulation is running.
You don’t know how long it will take new moviegoers to make it to the theater, so you decide to look at past data. Using timestamped receipts from the box office, you learn that moviegoers tend to arrive at the theater, on average, every 12 seconds. Now all you have to do is tell the function to wait this long before generating a new person:
defrun_theater(env,num_cashiers,num_servers,num_ushers):theater=Theater(env,num_cashiers,num_servers,num_ushers)formoviegoerinrange(3):env.process(go_to_movies(env,moviegoer,theater))whileTrue:yieldenv.timeout(0.20)# Wait a bit before generating a new person# Almost done!...Note that you use the decimal number0.20 to represent 12 seconds. To get this number, you simply divide 12 seconds by 60 seconds, which is the number of seconds in a minute.
After waiting, the function should incrementmoviegoer by 1 and generate the next person. Thegenerator function is the same one you used to initialize the first 3 moviegoers:
defrun_theater(env,num_cashiers,num_servers,num_ushers):theater=Theater(env,num_cashiers,num_servers,num_ushers)formoviegoerinrange(3):env.process(go_to_movies(env,moviegoer,theater))whileTrue:yieldenv.timeout(0.20)# Wait a bit before generating a new personmoviegoer+=1env.process(go_to_movies(env,moviegoer,theater))That’s it! When you call this function, the simulation will generate 3 moviegoers to start and begin moving them through the theater withgo_to_movies(). After that, new moviegoers will arrive at the theater with an interval of 12 seconds and move through the theater in their own time.
Calculating the Wait Time: Function Definition
At this point, you should have a listwait_times that contains the total amount of time it took each moviegoer to make it to their seat. Now you’ll want to define a function to help calculate the average time amoviegoer spends from the time they arrive to the time they finish checking their ticket.get_average_wait_time() does just this:
defget_average_wait_time(wait_times):average_wait=statistics.mean(wait_times)This function takes yourwait_times list as an argument and usesstatistics.mean() to calculate the average wait time.
Since you’re creating a script that will be used by the movie theater manager, you’ll want to make sure that the output can be read easily by the user. You can add a function calledcalculate_wait_time() to do this:
defcalculate_wait_time(arrival_times,departure_times):average_wait=statistics.mean(wait_times)# Pretty print the resultsminutes,frac_minutes=divmod(average_wait,1)seconds=frac_minutes*60returnround(minutes),round(seconds)The last part of the function usesdivmod() to return the results in minutes and seconds, so the manager can easily understand the program’s output.
Choosing Parameters: User Input Function Definition
As you’ve built these functions, you’ve run into a few variables that have not been clearly defined:
num_cashiersnum_serversnum_ushers
These variables are the parameters that you canchange to see how the simulation changes. If a blockbuster movie has customers lining up around the block, how many cashiers should be working? What if people are flying through the box office but getting stuck at concessions? What value ofnum_servers will help ease the flow?
Note: That’s the beauty of simulation. It allows you to try these things out so that you can determine the best possible decision in real life.
Whoever is using your simulation needs to be able to change the values of these parameters to try out different scenarios. To this end, you’ll create a helper function to get these values from the user:
defget_user_input():num_cashiers=input("Input # of cashiers working: ")num_servers=input("Input # of servers working: ")num_ushers=input("Input # of ushers working: ")params=[num_cashiers,num_servers,num_ushers]ifall(str(i).isdigit()foriinparams):# Check input is validparams=[int(x)forxinparams]else:print("Could not parse input. The simulation will use default values:","\n1 cashier, 1 server, 1 usher.",)params=[1,1,1]returnparamsThis function simply calls Python’sinput() function to retrieve data from the user. Because user input runs the risk of being messy, you can include anif/else clause to catch anything invalid. If the user inputs bad data, then the simulation will run with default values.
Finalizing the Setup: Main Function Definition
The last function you’ll want to create ismain(). This will ensure your script runs in the proper order when you execute it on the command line. You can read more aboutmain() inDefining Main Functions in Python. Here’s what yourmain() should look like:
defmain():# Setuprandom.seed(42)num_cashiers,num_servers,num_ushers=get_user_input()# Run the simulationenv=simpy.Environment()env.process(run_theater(env,num_cashiers,num_servers,num_ushers))env.run(until=90)# View the resultsmins,secs=get_average_wait_time(wait_times)print("Running simulation...",f"\nThe average wait time is{mins} minutes and{secs} seconds.",)Here’s howmain() works:
- Set up your environment by declaring a random seed. This ensures your output will look like what you see in this tutorial.
- Query the user of your program for some input.
- Create the environment and save it as the variable
env, which will move the simulation through each time step. - Tell
simpyto run the processrun_theater(), which creates the theater environment and generates moviegoers to move through it. - Determine how long you want the simulation to run. As a default value, the simulation is set to run for 90 minutes.
- Store the output of
get_average_wait_time()in two variables,minsandsecs. - Use
print()to show the results to the user.
With this, the setup is complete!
How to Run the Simulation
With just a few more lines of code, you’ll be able to watch your simulation come to life. But first, here’s an overview of the functions and classes you’ve defined so far:
Theater: This class definition serves as a blueprint for the environment you want to simulate. It determines some information about that environment, like what kinds of resources are available, and what processes are associated with them.go_to_movies(): This function makes explicit requests to use a resource, goes through the associated process, and then releases it to the next moviegoer.run_theater(): This function controls the simulation. It uses theTheaterclass blueprint to create an instance of a theater, and then calls ongo_to_movies()to generate and move people through the theater.get_average_wait_time(): This function finds the average time it takes amoviegoerto make it through the theater.calculate_wait_time(): This function ensures the final output is easy for the user to read.get_user_input(): This function allows the user to define some parameters, like how many cashiers are available.main(): This function ensures that your script runs properly in the command line.
Now, you only need two more lines of code to invoke your main function:
if__name__=='__main__':main()With that, your script is ready to run! Open up your terminal, navigate to where you’ve storedsimulate.py, and run the following command:
$pythonsimulate.pyInput # of cashiers working:You’ll be prompted to select the parameters you want for your simulation. Here’s what the output looks like with default parameters:
$pythonsimulate.pyInput # of cashiers working: 1Input # of servers working: 1Input # of ushers working: 1Running simulation...The average wait time is 42 minutes and 53 seconds.Whoa! That’s a long time to be waiting around!
When to Change Things Up
Remember, your goal is to approach the manager with a solution for how many employees he’ll need on staff to keep wait times under 10 minutes. To this end, you’ll want to play around with your parameters to see which numbers offer an optimal solution.
First, try something completely insane and max out the resources! Say there were 100 cashiers, 100 servers, and 100 ushers working in this theater. This is impossible, of course, but using insanely high numbers will quickly tell you what the system’s limit is. Try it now:
$pythonsimulate.pyInput # of cashiers working: 100Input # of servers working: 100Input # of ushers working: 100Running simulation...The average wait time is 3 minutes and 29 seconds.Even if you maxed out the resources, you would only get wait times down to 3 and a half minutes. Now try and change the numbers to see if you can get wait times down to 10 minutes, like the manager requested. What solution did you come up with? You can expand the code block below to see one possible solution:
$pythonsimulate.pyInput # of cashiers working: 9Input # of servers working: 6Input # of ushers working: 1Running simulation...The average wait time is 9 minutes and 60 seconds.At this point, you would present your results to the manager and make a suggestion to help improve the theater. For instance, to cut down on costs, he might want to install 10 ticket kiosks at the front of the theater instead of keeping 10 cashiers on hand each night.
Conclusion
In this tutorial, you’ve learned how tobuild and run a simulation in Python using thesimpy framework. You’ve come to understand how systems have agents undergo processes, and how you can create virtual representations of those systems to fortify them against congestion and delay. While the type of simulation can vary, the overall execution is the same! You’ll be able to apply what you’ve learned here to a variety of different scenarios.
Now you can:
- Brainstorm a simulation algorithm step by step
- Create a virtual environment in Python with
simpy - Define functions that represent agents and processes
- Change parameters of your simulation to find the optimal solution
There’s so much you can do withsimpy, so don’t let your exploration stop here. Take what you’ve learned and apply it to new scenarios. Your solutions could help save people valuable time and money, so dive in and see what other processes you can optimize! You can download the source code for the script you built in this tutorial by clicking on the link below:
Download Code:Click here to download the code you’ll use to learn about SimPy in this tutorial.
Recommended Course
🐍 Python Tricks 💌
Get a short & sweetPython Trick delivered to your inbox every couple of days. No spam ever. Unsubscribe any time. Curated by the Real Python team.

AboutJaya Zhané
Jaya is an avid Pythonista and writes for Real Python. She's a Master's student at Georgia Tech and is interested in data science, AI, machine learning and natural language processing.
» More about JayaMasterReal-World Python Skills With Unlimited Access to Real Python
Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:
MasterReal-World Python Skills
With Unlimited Access to Real Python
Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:
What Do You Think?
What’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment below and let us know.
Commenting Tips: The most useful comments are those written with the goal of learning from or helping out other students.Get tips for asking good questions andget answers to common questions in our support portal.
Looking for a real-time conversation? Visit theReal Python Community Chat or join the next“Office Hours” Live Q&A Session. Happy Pythoning!
Keep Learning
Keep reading Real Python by creating a free account or signing in:
Already have an account?Sign-In





