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