Airport Simulation with SimPy: From Check-In to Takeoff

Airports are complex systems under constant pressure. Passengers flow through multiple touchpoints. Delays cascade. Simulation helps you see where things go wrong before they do.

The Airport Model

Components: - Passengers - Arrive for flights - Check-in - Desks and kiosks - Security - Screening queues - Gates - Boarding areas - Aircraft - Arrivals and departures

Basic Passenger Flow

import simpy
import random

class Airport:
    def __init__(self, env, config):
        self.env = env
        self.check_in_desks = simpy.Resource(env, capacity=config['check_in_desks'])
        self.security_lanes = simpy.Resource(env, capacity=config['security_lanes'])
        self.gates = simpy.Resource(env, capacity=config['gates'])
        self.stats = []

    def passenger(self, passenger_id, flight_time):
        arrival = self.env.now
        record = {'id': passenger_id, 'arrival': arrival, 'flight': flight_time}

        # Check-in
        with self.check_in_desks.request() as req:
            yield req
            yield self.env.timeout(random.uniform(2, 5))
        record['checked_in'] = self.env.now

        # Security
        with self.security_lanes.request() as req:
            yield req
            yield self.env.timeout(random.uniform(3, 10))
        record['cleared_security'] = self.env.now

        # Wait at gate (until boarding)
        boarding_time = flight_time - 30  # Board 30 mins before
        if self.env.now < boarding_time:
            yield self.env.timeout(boarding_time - self.env.now)

        # Board
        record['boarded'] = self.env.now
        record['made_flight'] = self.env.now < flight_time

        self.stats.append(record)

def passenger_arrivals(env, airport, flight_schedule):
    """Generate passengers for scheduled flights."""
    passenger_id = 0

    for flight_time, num_passengers in flight_schedule:
        # Passengers arrive 1-3 hours before flight
        for _ in range(num_passengers):
            arrival_offset = random.uniform(60, 180)
            arrival_time = flight_time - arrival_offset

            if arrival_time > env.now:
                yield env.timeout(arrival_time - env.now)

            env.process(airport.passenger(passenger_id, flight_time))
            passenger_id += 1

# Run
env = simpy.Environment()
airport = Airport(env, {'check_in_desks': 10, 'security_lanes': 4, 'gates': 20})

# Flight schedule: (departure_time, passengers)
flights = [(60, 150), (120, 200), (180, 180), (240, 220)]
env.process(passenger_arrivals(env, airport, flights))
env.run(until=300)

Security with Different Lanes

class SecurityCheckpoint:
    def __init__(self, env, regular_lanes, priority_lanes):
        self.env = env
        self.regular = simpy.Resource(env, capacity=regular_lanes)
        self.priority = simpy.Resource(env, capacity=priority_lanes)

    def screen_passenger(self, passenger_type):
        if passenger_type in ['business', 'frequent_flyer']:
            lane = self.priority
            process_time = random.uniform(2, 5)
        else:
            lane = self.regular
            process_time = random.uniform(4, 12)

        with lane.request() as req:
            yield req
            yield self.env.timeout(process_time)

Aircraft Turnaround

class Aircraft:
    def __init__(self, env, flight_id, aircraft_type):
        self.env = env
        self.flight_id = flight_id
        self.type = aircraft_type

    def turnaround(self, gate, ground_crew, fuel_truck, catering):
        """Complete turnaround between arrival and departure."""
        # Request gate
        with gate.request() as gate_req:
            yield gate_req

            # Parallel activities
            disembark = self.env.process(self.disembark())
            clean = self.env.process(self.clean())

            yield disembark
            yield clean

            # Sequential activities requiring resources
            with ground_crew.request() as crew:
                yield crew
                yield self.env.timeout(random.uniform(10, 20))  # Baggage

            with fuel_truck.request() as fuel:
                yield fuel
                yield self.env.timeout(random.uniform(15, 25))

            with catering.request() as cater:
                yield cater
                yield self.env.timeout(random.uniform(10, 15))

            # Board
            yield from self.board()

    def disembark(self):
        passengers = random.randint(100, 180)
        yield self.env.timeout(passengers * 0.1)

    def clean(self):
        yield self.env.timeout(random.uniform(15, 25))

    def board(self):
        passengers = random.randint(100, 180)
        yield self.env.timeout(passengers * 0.15)

Baggage Handling

class BaggageSystem:
    def __init__(self, env, num_carousels, num_handlers):
        self.env = env
        self.carousels = simpy.Resource(env, capacity=num_carousels)
        self.handlers = simpy.Resource(env, capacity=num_handlers)
        self.bags_processed = 0

    def process_flight_bags(self, flight_id, num_bags):
        """Process bags from check-in to aircraft."""
        bags_loaded = 0

        with self.carousels.request() as carousel:
            yield carousel

            while bags_loaded < num_bags:
                # Handler picks up bags
                with self.handlers.request() as handler:
                    yield handler
                    batch = min(5, num_bags - bags_loaded)
                    yield self.env.timeout(batch * 0.5)
                    bags_loaded += batch

        self.bags_processed += num_bags

Complete Airport Simulation

import simpy
import random
import numpy as np

class AirportSimulation:
    def __init__(self, env, config):
        self.env = env
        self.config = config

        # Resources
        self.check_in = simpy.Resource(env, capacity=config['check_in_desks'])
        self.kiosks = simpy.Resource(env, capacity=config['kiosks'])
        self.bag_drop = simpy.Resource(env, capacity=config['bag_drops'])
        self.security = simpy.Resource(env, capacity=config['security_lanes'])
        self.passport_control = simpy.Resource(env, capacity=config['passport_booths'])
        self.gates = simpy.Resource(env, capacity=config['gates'])

        # Stats
        self.passenger_stats = []
        self.missed_flights = 0

    def passenger(self, pax_id, flight_time, has_bags, is_international):
        arrival = self.env.now
        record = {
            'id': pax_id,
            'arrival': arrival,
            'flight_time': flight_time
        }

        # Check-in (desk or kiosk)
        if random.random() < 0.7:  # 70% use kiosk
            with self.kiosks.request() as req:
                yield req
                yield self.env.timeout(random.uniform(1, 3))
        else:
            with self.check_in.request() as req:
                yield req
                yield self.env.timeout(random.uniform(3, 7))

        # Bag drop if needed
        if has_bags:
            with self.bag_drop.request() as req:
                yield req
                yield self.env.timeout(random.uniform(2, 5))

        record['check_in_complete'] = self.env.now

        # Security
        with self.security.request() as req:
            yield req
            yield self.env.timeout(random.triangular(3, 15, 6))

        record['security_complete'] = self.env.now

        # Passport control for international
        if is_international:
            with self.passport_control.request() as req:
                yield req
                yield self.env.timeout(random.uniform(1, 3))

        record['airside'] = self.env.now

        # Check if made it in time for boarding
        boarding_time = flight_time - 30
        if self.env.now > boarding_time:
            self.missed_flights += 1
            record['made_flight'] = False
        else:
            record['made_flight'] = True
            # Wait for boarding
            if self.env.now < boarding_time:
                yield self.env.timeout(boarding_time - self.env.now)

        record['total_time'] = self.env.now - arrival
        self.passenger_stats.append(record)

    def flight_passengers(self, flight_time, num_pax, is_international):
        """Generate passengers for a flight."""
        for i in range(num_pax):
            # Arrival distribution: 60-180 mins before flight
            advance = random.triangular(60, 180, 120)
            arrival_time = max(0, flight_time - advance)

            # Wait until arrival time
            if arrival_time > self.env.now:
                yield self.env.timeout(arrival_time - self.env.now)

            has_bags = random.random() < 0.7
            self.env.process(self.passenger(
                f"{flight_time}_{i}",
                flight_time,
                has_bags,
                is_international
            ))

            # Small gap between passenger arrivals
            yield self.env.timeout(random.uniform(0.1, 0.5))

    def run(self, flight_schedule):
        """Run simulation with flight schedule."""
        for flight_time, num_pax, is_intl in flight_schedule:
            self.env.process(self.flight_passengers(flight_time, num_pax, is_intl))

        # Run until last flight + buffer
        last_flight = max(f[0] for f in flight_schedule)
        self.env.run(until=last_flight + 60)

    def report(self):
        pax = self.passenger_stats
        made_it = [p for p in pax if p['made_flight']]

        print("\n=== Airport Simulation Report ===")
        print(f"Total passengers: {len(pax)}")
        print(f"Made flight: {len(made_it)} ({len(made_it)/len(pax):.1%})")
        print(f"Missed flight: {self.missed_flights}")

        if made_it:
            total_times = [p['total_time'] for p in made_it]
            security_times = [p['security_complete'] - p['check_in_complete']
                            for p in made_it if 'security_complete' in p]

            print(f"\nTotal journey time (check-in to gate):")
            print(f"  Mean: {np.mean(total_times):.1f} min")
            print(f"  90th pct: {np.percentile(total_times, 90):.1f} min")

            print(f"\nSecurity wait + process:")
            print(f"  Mean: {np.mean(security_times):.1f} min")

# Config
config = {
    'check_in_desks': 15,
    'kiosks': 20,
    'bag_drops': 10,
    'security_lanes': 8,
    'passport_booths': 6,
    'gates': 30
}

# Flight schedule: (departure_time, passengers, is_international)
schedule = [
    (60, 180, True),
    (90, 150, False),
    (120, 200, True),
    (150, 160, False),
    (180, 220, True),
    (210, 190, False),
    (240, 175, True),
]

random.seed(42)
env = simpy.Environment()
sim = AirportSimulation(env, config)
sim.run(schedule)
sim.report()

Summary

Airport simulation captures: - Multi-stage passenger journey - Parallel and sequential processes - Time-critical deadlines - Resource contention at bottlenecks - Flight schedule dependencies

Every airport is a system. Simulate to optimise.

Next Steps


Build Professional Simulations

Break free from commercial software and learn how to build powerful, industry-standard simulations in Python. The Complete Simulation in Python with SimPy Bootcamp gives you everything you need.

Explore the Bootcamp