Basic Simulation¶
In this example, we will demonstrate some of the basics of PassengerSim.
We'll interface with the tool through the passengersim Python interface,
which is imported into Python in the same manner as any other Python library.
import passengersim as pax
pax.versions()
passengersim 0.59.dev9+ga3f6c1b98 passengersim.core 0.59.dev1+g671876c7d
Using PassengerSim generally involves three steps: configuring a simulation,
running it, and analyzing the results. The first and last of these steps
rely primarily on the open-source passengersim code that runs entirely in
Python, while running a simulation relies on the passengersim.core library,
which is the proprietary licensed code.
To get started, we will load a PassengerSim configuration from YAML input files. These files are structured plain text inputs that define everything needed to run a simulation: carriers, flight legs, passenger choice models, simulation controls, and more. The demo YAML file shown below shows how one of these YAML input files might be structured, with "include" statements that point to files containing much of the fundamental network configuration and a handful of specific settings can supplement or replace settings from the included files.
from passengersim.utils.codeview import show_file
show_file(pax.demo_network("3MKT/DEMO"))
include: - 01-base.yaml - 02-buyup.yaml - 03-ap.yaml simulation_controls: demand_multiplier: 1.0 num_trials: 2 num_samples: 400 burn_samples: 50 random_seed: 314 carriers: AL1: rm_system: E AL2: rm_system: E db: null outputs: reports: []
We can load the YAML files into a Config object, which will both handle loading
the raw content and validating that it includes everything necessary to prepare
a PassengerSim simulation.
cfg = pax.Config.from_yaml(pax.demo_network("3MKT/DEMO"))
After loading, it's possible to modify the Config object in Python to change aspects
of a simulation. For this example, we won't change anything here, and just create the
Simulation object from the existing Config. We can choose to create a regular
Simulation which will run all trials and samples sequentially in a single process, or
a MultiSimulation which will run each trial in parallel, with each trial running in
a seperate subprocess. The later option is generally much faster on multi-core computers,
but detailed simulation model development or debugging can usually be done more conveniently
in the single process workflow.
Since our demo is configured to run two trials, we'll run a MultiSimulation.
sim = pax.MultiSimulation(cfg)
Running the simulation is as simple as calling the run command, which runs the simulation and returns a summary output object.
summary = sim.run()
By default, PassengerSim will generate a summary output that includes a wide variety of aggregated summary output data, which can be reviewed or visualized using an included suite of analysis tools.
summary
<passengersim.summaries.SimulationTables created on 2025-11-16> * bid_price_history (256 row DataFrame) * cabins (5600 row DataFrame) * carriers (2 row DataFrame) * carrier_history (NoneType) * carrier_history2 (1400 row DataFrame) * forecast_accuracy (NoneType) * cp_segmentation (12 row DataFrame) * demand_to_come_summary (34 row DataFrame) * demand_to_come (NoneType) * demands (6 row DataFrame) * displacement_history (68 row DataFrame) * fare_class_mix (12 row DataFrame) * path_forecasts (NoneType) * leg_forecasts (NoneType) * edgar (NoneType) * legbuckets (48 row DataFrame) * legs (8 row DataFrame) * leg_detail (NoneType) * local_and_flow_yields (NoneType) * pathclasses (72 row DataFrame) * path_legs (16 row DataFrame) * paths (12 row DataFrame) * segmentation_by_timeframe (335 row DataFrame) * segmentation_detail (NoneType) <*>
The raw data of the summary tables shown can be accessed via direct attribute access
on the SimulationTables object. For example, we can see the summary data collected
on the legs like this:
summary.legs
| carrier | flt_no | orig | dest | distance | gt_sold | gt_capacity | gt_sold_local | gt_revenue | avg_load_factor | avg_local | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| leg_id | |||||||||||
| 101 | AL1 | 101 | BOS | ORD | 863.753282 | 59070 | 70000 | 24791 | 9.579868e+06 | 84.385714 | 41.968851 |
| 102 | AL1 | 102 | BOS | ORD | 863.753282 | 59022 | 70000 | 24830 | 9.620903e+06 | 84.317143 | 42.069059 |
| 201 | AL2 | 201 | BOS | ORD | 863.753282 | 59096 | 70000 | 24785 | 9.624332e+06 | 84.422857 | 41.940233 |
| 202 | AL2 | 202 | BOS | ORD | 863.753282 | 59088 | 70000 | 24926 | 9.665881e+06 | 84.411429 | 42.184538 |
| 111 | AL1 | 111 | ORD | LAX | 1739.799337 | 75043 | 84000 | 40764 | 2.211726e+07 | 89.336905 | 54.320856 |
| 112 | AL1 | 112 | ORD | LAX | 1739.799337 | 75141 | 84000 | 40949 | 2.223502e+07 | 89.453571 | 54.496214 |
| 211 | AL2 | 211 | ORD | LAX | 1739.799337 | 75223 | 84000 | 40912 | 2.225997e+07 | 89.551190 | 54.387621 |
| 212 | AL2 | 212 | ORD | LAX | 1739.799337 | 75193 | 84000 | 41031 | 2.228137e+07 | 89.515476 | 54.567579 |
In addition to accessing the raw data, PassengerSim also has a built-in collection of visualization tools for reviewing the data. These tools include aggregate summary figures, e.g. some figures showing carrier revenue, load factors, or RASM.
summary.fig_carrier_revenues()
summary.fig_carrier_load_factors()
summary.fig_carrier_rasm()
We can also examine more detailed slices of simulation results. One commonly used visualization show the fare class mix across all bookings on each carrier.
summary.fig_fare_class_mix()
We can also dive deeper into this fare class data, to look not just at how many bookings are in each fare class, but also when those bookings occured and what passenger segments they came from. The figure below clearly shows that the lower fare classes are booked almost exclusively by leisure travlers, and primarily in the early time frames.
summary.fig_bookings_by_timeframe(by_class=True, by_carrier="AL1")
We can also do a deeper dive into the passengers on an individual leg, to look at the breakdown of their path origins and destinations, and the fare classes sold on that particular leg.
summary.fig_select_leg_analysis(101)
A different perspective on the results can be seen in this figure of carrier revenue results, which are plotted sample-by-sample. Here we can see how carrier do against each other: if AL1 is having a good day, is AL2 also having a similarly good day?
summary.fig_carrier_head_to_head_revenue("AL1", "AL2")