Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 3

Simulating Port Operations with SimPy

Recommended resources

Welcome back. In our previous lesson, you created a static blueprint of the container gate-in workflow using BPMN. That model gives us the logical sequence of operations. Now, we'll breathe life into it by modeling the dynamic, unpredictable nature of a real port.

This lesson introduces you to discrete-event simulation (DES), a powerful technique for analyzing complex systems. Your goal is to implement a simulation in Python using the SimPy library to model the core dynamics of port operations: random vehicle arrivals and the subsequent queuing at a service point, like a gate. This is a fundamental skill for any systems architect, as it allows you to test capacity, identify bottlenecks, and quantify system performance before deploying expensive infrastructure.

From Static Processes to Dynamic Systems

A BPMN diagram shows the what and the who, but a simulation shows the how many and how long. Real-world systems are not perfectly synchronized. Trucks don't arrive on a fixed schedule, and service times vary. These variations cause queues to form and resources to become bottlenecks. DES is the tool we use to understand and manage this variability.

To start, let's establish a clear understanding of what Discrete Event Simulation is and why it's so critical for modeling systems like the one at Chittagong Port.

Lara Kattan - Simulations in Python: Discrete Event Simulation with SimPy | PyData NYC 2022

This segment from a PyData talk by Lara Kattan provides an excellent, concise definition of simulation in general and Discrete Event Simulation (DES) in particular. It explains the core idea that the system's state only changes at specific, discrete moments in time when an "event" occurs.

Please watch the first part of the talk, from the introduction to the definition of DES. Pay close attention to the explanation of how DES assumes events happen at specified intervals, which is the foundational concept for how SimPy works.


The Theory of Queues

The phenomenon of waiting in line is so fundamental to systems engineering that it has its own mathematical field: Queuing Theory. This theory provides the analytical framework for understanding the relationship between arrival rates, service times, and resource capacity. While we will use simulation (a computational approach), understanding the theory provides deep insight.

A common way to describe a simple queue is with Kendall's Notation (A/S/C):

  • A: The distribution of time between arrivals (the inter-arrival time).
  • S: The distribution of time it takes to service one entity.
  • C: The number of parallel servers or channels.

A foundational model is the M/M/1 queue, which assumes Markovian (or memoryless) processes for arrivals and services, modeled by the exponential distribution, and a single server. This is a good starting point for our port gate.

The PyData talk provides more detail on this theoretical foundation.

Lara Kattan - Simulations in Python: Discrete Event Simulation with SimPy | PyData NYC 2022

This next segment introduces queuing theory, Kendall's notation, and the M/M/1 model. Given your background in engineering, this theoretical context will help frame the practical simulation you are about to build.

Watch the section on Queuing Theory. Focus on understanding the meaning of the A/S/C notation and the assumptions behind the M/M/1 model, where both arrivals and service times are drawn from exponential distributions.

The most critical takeaway from queuing theory is the non-linear relationship between resource utilization and waiting time. As a resource (like our port gate) approaches 100% utilization, the average time an entity has to wait in the queue doesn't just increase linearly—it increases exponentially.

This graph illustrates a core concept in queuing theory. As resource utilization increases, average waiting time grows exponentially, especially in systems with high variability. Our simulation will demonstrate this phenomenon firsthand.

This graph illustrates a core concept in queuing theory. As resource utilization increases, average waiting time grows exponentially, especially in systems with high variability. Our simulation will demonstrate this phenomenon firsthand.


Simulating a System with SimPy

Now, let's translate this theory into a working Python program using SimPy. A SimPy simulation is built around three core concepts, which map directly to the theory.

  1. Environment (simpy.Environment): This is the simulation manager. It keeps track of the simulation clock and orchestrates events.
  2. Processes (generator functions): These are the "actors" in our simulation. A process describes the lifecycle of an entity—the sequence of actions and events it experiences. In our case, a process will model a truck arriving, waiting for the gate, being processed, and leaving.
  3. Resources (simpy.Resource): These are the limited assets that processes compete for, causing queues to form. For us, the gate is a resource with a fixed capacity (e.g., one truck at a time).

The Real Python article "SimPy: Simulating Real-World Processes With Python" provides a superb, step-by-step guide to building a simulation from the ground up. We will use it as our primary reference, adapting its movie theater example to our port logistics scenario.

SimPy: Simulating Real-World Processes With Python

This article is a comprehensive, hands-on tutorial for SimPy. It covers everything from setup to implementation and analysis.

First, ensure you have SimPy installed by following the instructions in the section How to Get Started. Then, read the section Creating the Environment. This part explains how to define resources like simpy.Resource and the processes associated with them. Although it uses a class-based structure for the Theater, we will use a more direct functional approach, but the concepts of defining resources and process times are identical.


Hands-On: Modeling the Port Gate

Let's write the code. We'll model a single gate (C=1) where trucks arrive randomly (a Poisson process, meaning exponential inter-arrival times, our A='M') and are serviced for a random amount of time (also exponential, our S='M'). This is a practical implementation of an M/M/1 queue.

Create a new Python file (e.g., gate_simulation.py) and add the following code.

1. Setup and Constants

First, we import the necessary libraries and define our simulation parameters. You can adjust these later to see how the system behaves under different conditions.

import simpy
import random
import statistics

# Simulation Constants
RANDOM_SEED = 42
SIM_TIME = 120  # Simulation time in minutes
AVG_ARRIVAL_INTERVAL = 5  # Average time between truck arrivals in minutes
AVG_SERVICE_TIME = 4  # Average time to process a truck at the gate in minutes
NUM_GATES = 1 # Number of gates available

# --- Metrics Collection ---
wait_times = []

2. The Truck Process

This function is the heart of our simulation. It's a Python generator that models the lifecycle of a single truck. It will request access to the gate resource, wait if necessary, use the resource for a period, and then release it.

This logic is analogous to the go_to_movies() function in the Real Python article.

SimPy: Simulating Real-World Processes With Python

Now, let's focus on how an entity moves through the system and interacts with resources.

Read the section Moving Through the Environment. Pay close attention to the with resource.request() as request: pattern. This is the standard SimPy idiom for requesting a resource, waiting in the queue (yield request), using it, and automatically releasing it upon completion.

Now, let's implement our truck process using this pattern.

def truck(env, name, gate):
    """A truck process: arrives, requests the gate, gets serviced, and leaves."""
    print(f'{env.now:.2f}: {name} arrives')
    arrival_time = env.now

    # Request one of the gates
    with gate.request() as request:
        yield request  # Wait in queue for the gate to be free

        wait = env.now - arrival_time
        wait_times.append(wait)
        print(f'{env.now:.2f}: {name} enters gate after waiting {wait:.2f} minutes')

        # Simulate service time
        service_time = random.expovariate(1.0 / AVG_SERVICE_TIME)
        yield env.timeout(service_time)
    
    print(f'{env.now:.2f}: {name} leaves gate')

Note the use of random.expovariate(1.0 / AVG_SERVICE_TIME). This is how we model a process with an exponential distribution, as discussed in queuing theory. The parameter to expovariate is , the rate, which is .

3. The Arrival Generator

Next, we need a process that generates the trucks. This function will run in a loop, creating new truck processes at random intervals for the duration of the simulation.

This is analogous to the run_theater() function in the Real Python guide.

SimPy: Simulating Real-World Processes With Python

This section shows how to create a generator process that continuously creates new agents (moviegoers) and sends them into the system.

Read the section Making Things Happen. The key elements are the while True loop and the yield env.timeout(...) call, which pauses the generator for a specified time before creating the next entity.

Here is our implementation, setup, which generates truck arrivals.

def setup(env, num_gates, arrival_interval):
    """Creates a gate and generates trucks arriving at random intervals."""
    gate = simpy.Resource(env, capacity=num_gates)

    # Create initial trucks
    for i in range(2):
        env.process(truck(env, f'Truck {i}', gate))

    # Create trucks while the simulation is running
    i = 2
    while True:
        # Wait for the next arrival
        inter_arrival_time = random.expovariate(1.0 / arrival_interval)
        yield env.timeout(inter_arrival_time)
        
        env.process(truck(env, f'Truck {i}', gate))
        i += 1

4. Running the Simulation

Finally, we need the main block to set up the environment, start the setup process, and run the simulation.

# --- Main Simulation Block ---
print('Starting Port Gate Simulation')
random.seed(RANDOM_SEED)

# Create a SimPy environment
env = simpy.Environment()

# Start the setup process
env.process(setup(env, NUM_GATES, AVG_ARRIVAL_INTERVAL))

# Run the simulation
env.run(until=SIM_TIME)

# --- Analysis ---
print("\n--- Simulation Results ---")
avg_wait = statistics.mean(wait_times)
print(f"Average wait time: {avg_wait:.2f} minutes")

Run your complete script. You should see a log of trucks arriving, waiting, and leaving. The final output will give you the average wait time for all trucks that were serviced.


System Stability and Capacity

What happens if trucks arrive faster than the gate can process them? In queuing theory terms, this is when the arrival rate () is greater than or equal to the service rate (). The system becomes unstable, and the queue will grow indefinitely.

This graph shows the number of customers in a simulated system over time. The steady upward trend indicates that the arrival rate exceeds the service rate, leading to an unstable system where the queue grows without bound. Simulation is crucial for identifying such capacity shortfalls.

This graph shows the number of customers in a simulated system over time. The steady upward trend indicates that the arrival rate exceeds the service rate, leading to an unstable system where the queue grows without bound. Simulation is crucial for identifying such capacity shortfalls.

Try changing AVG_ARRIVAL_INTERVAL to be less than AVG_SERVICE_TIME (e.g., 3 minutes) and re-run the simulation. You will likely see the wait times increase dramatically, confirming the principles we discussed.


Conclusion

In this lesson, you have successfully built a dynamic model of a core port operation. You've gone from the theoretical foundations of queuing theory to a practical, hands-on implementation using SimPy. You learned how to represent a system with an Environment, model actors with Processes, and manage contention with Resources. Most importantly, you have created a tool that can quantify system performance (like average wait time) and test the impact of changing operational parameters.

In our next lesson, we will take a major step toward building our enterprise-grade system. You will learn how to connect your SimPy simulation to the Camunda workflow engine. The events generated by our simulation (e.g., "truck arrives at gate") will trigger real workflow instances, bridging the gap between simulation and process orchestration.