Dynamic Configuration

Most of the time, we are not actually interested in just running a simulation that we’ve pre-configured in files on disk. Instead, we will want to load a simulation from pre-configured in files on disk, but then change a couple things before we run it. Perhaps we’re debugging a new RM system, and want to just run a few iterations to see if it works. Or maybe we have a model mostly ready to go, but we want to modify a couple of RM system parameters, or change the level of demand, or just monkey around with the network schedule a bit.

We can do all of that and more, right here in Python, especially in a Jupyter notebook.

import passengersim as pax

pax.versions()
passengersim 0.80
passengersim.core 0.80

Let’s start by loading our demo model from its YAML files into a PassengerSim Config object.

cfg = pax.Config.from_yaml(pax.demo_network("3MKT/08-untrunc-em"))

The cfg now holds the data that defines everything about the simulation that will be run, including the carriers, RM systems, networks, … everything. Before we use this config to initialize a simulation, we can inspect and even modify it in Python. For example, let’s take a look at the flight legs in this network.

cfg.legs
[Leg(leg_id=101, carrier='AL1', fltno=101, orig='BOS', dest='ORD', date='2020-03-01', dep_time='08:00', arr_time='10:00', capacity=100, distance=863.753),
 Leg(leg_id=102, carrier='AL1', fltno=102, orig='BOS', dest='ORD', date='2020-03-01', dep_time='14:00', arr_time='16:00', capacity=100, distance=863.753),
 Leg(leg_id=201, carrier='AL2', fltno=201, orig='BOS', dest='ORD', date='2020-03-01', dep_time='08:00', arr_time='10:00', capacity=100, distance=863.753),
 Leg(leg_id=202, carrier='AL2', fltno=202, orig='BOS', dest='ORD', date='2020-03-01', dep_time='14:00', arr_time='16:00', capacity=100, distance=863.753),
 Leg(leg_id=111, carrier='AL1', fltno=111, orig='ORD', dest='LAX', date='2020-03-01', dep_time='11:00', arr_time='13:00', capacity=120, distance=1739.8),
 Leg(leg_id=112, carrier='AL1', fltno=112, orig='ORD', dest='LAX', date='2020-03-01', dep_time='17:00', arr_time='19:00', capacity=120, distance=1739.8),
 Leg(leg_id=211, carrier='AL2', fltno=211, orig='ORD', dest='LAX', date='2020-03-01', dep_time='11:00', arr_time='13:00', capacity=120, distance=1739.8),
 Leg(leg_id=212, carrier='AL2', fltno=212, orig='ORD', dest='LAX', date='2020-03-01', dep_time='17:00', arr_time='19:00', capacity=120, distance=1739.8)]

We can see here a list of legs, and each leg has all the attributes needed to define it in the simulation: origin, destination, departure and arrival times, capacity, and more. We can manipulate these attributes if we like. Let’s make the capacity on that first leg a little larger.

cfg.legs[0].capacity = 180

We can do the same and peek in on the network fares.

cfg.fares
[Fare(carrier='AL1', orig='BOS', dest='ORD', booking_class='Y0', brand='', price=400.0, advance_purchase=0, restrictions=[], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL1', orig='BOS', dest='ORD', booking_class='Y1', brand='', price=300.0, advance_purchase=0, restrictions=['R2'], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL1', orig='BOS', dest='ORD', booking_class='Y2', brand='', price=200.0, advance_purchase=3, restrictions=['R1'], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL1', orig='BOS', dest='ORD', booking_class='Y3', brand='', price=150.0, advance_purchase=7, restrictions=['R1', 'R2'], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL1', orig='BOS', dest='ORD', booking_class='Y4', brand='', price=125.0, advance_purchase=14, restrictions=['R1', 'R3'], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL1', orig='BOS', dest='ORD', booking_class='Y5', brand='', price=100.0, advance_purchase=21, restrictions=['R1', 'R2', 'R3'], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL1', orig='ORD', dest='LAX', booking_class='Y0', brand='', price=500.0, advance_purchase=0, restrictions=[], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL1', orig='ORD', dest='LAX', booking_class='Y1', brand='', price=400.0, advance_purchase=0, restrictions=['R2'], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL1', orig='ORD', dest='LAX', booking_class='Y2', brand='', price=300.0, advance_purchase=3, restrictions=['R1'], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL1', orig='ORD', dest='LAX', booking_class='Y3', brand='', price=225.0, advance_purchase=7, restrictions=['R1', 'R2'], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL1', orig='ORD', dest='LAX', booking_class='Y4', brand='', price=175.0, advance_purchase=14, restrictions=['R1', 'R3'], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL1', orig='ORD', dest='LAX', booking_class='Y5', brand='', price=150.0, advance_purchase=21, restrictions=['R1', 'R2', 'R3'], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL1', orig='BOS', dest='LAX', booking_class='Y0', brand='', price=750.0, advance_purchase=0, restrictions=[], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL1', orig='BOS', dest='LAX', booking_class='Y1', brand='', price=625.0, advance_purchase=0, restrictions=['R2'], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL1', orig='BOS', dest='LAX', booking_class='Y2', brand='', price=450.0, advance_purchase=3, restrictions=['R1'], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL1', orig='BOS', dest='LAX', booking_class='Y3', brand='', price=325.0, advance_purchase=7, restrictions=['R1', 'R2'], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL1', orig='BOS', dest='LAX', booking_class='Y4', brand='', price=250.0, advance_purchase=14, restrictions=['R1', 'R3'], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL1', orig='BOS', dest='LAX', booking_class='Y5', brand='', price=200.0, advance_purchase=21, restrictions=['R1', 'R2', 'R3'], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL2', orig='BOS', dest='ORD', booking_class='Y0', brand='', price=400.0, advance_purchase=0, restrictions=[], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL2', orig='BOS', dest='ORD', booking_class='Y1', brand='', price=300.0, advance_purchase=0, restrictions=['R2'], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL2', orig='BOS', dest='ORD', booking_class='Y2', brand='', price=200.0, advance_purchase=3, restrictions=['R1'], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL2', orig='BOS', dest='ORD', booking_class='Y3', brand='', price=150.0, advance_purchase=7, restrictions=['R1', 'R2'], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL2', orig='BOS', dest='ORD', booking_class='Y4', brand='', price=125.0, advance_purchase=14, restrictions=['R1', 'R3'], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL2', orig='BOS', dest='ORD', booking_class='Y5', brand='', price=100.0, advance_purchase=21, restrictions=['R1', 'R2', 'R3'], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL2', orig='ORD', dest='LAX', booking_class='Y0', brand='', price=500.0, advance_purchase=0, restrictions=[], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL2', orig='ORD', dest='LAX', booking_class='Y1', brand='', price=400.0, advance_purchase=0, restrictions=['R2'], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL2', orig='ORD', dest='LAX', booking_class='Y2', brand='', price=300.0, advance_purchase=3, restrictions=['R1'], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL2', orig='ORD', dest='LAX', booking_class='Y3', brand='', price=225.0, advance_purchase=7, restrictions=['R1', 'R2'], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL2', orig='ORD', dest='LAX', booking_class='Y4', brand='', price=175.0, advance_purchase=14, restrictions=['R1', 'R3'], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL2', orig='ORD', dest='LAX', booking_class='Y5', brand='', price=150.0, advance_purchase=21, restrictions=['R1', 'R2', 'R3'], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL2', orig='BOS', dest='LAX', booking_class='Y0', brand='', price=750.0, advance_purchase=0, restrictions=[], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL2', orig='BOS', dest='LAX', booking_class='Y1', brand='', price=625.0, advance_purchase=0, restrictions=['R2'], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL2', orig='BOS', dest='LAX', booking_class='Y2', brand='', price=450.0, advance_purchase=3, restrictions=['R1'], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL2', orig='BOS', dest='LAX', booking_class='Y3', brand='', price=325.0, advance_purchase=7, restrictions=['R1', 'R2'], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL2', orig='BOS', dest='LAX', booking_class='Y4', brand='', price=250.0, advance_purchase=14, restrictions=['R1', 'R3'], category=None, cabin='Y', min_stay=0, saturday_night_required=False),
 Fare(carrier='AL2', orig='BOS', dest='LAX', booking_class='Y5', brand='', price=200.0, advance_purchase=21, restrictions=['R1', 'R2', 'R3'], category=None, cabin='Y', min_stay=0, saturday_night_required=False)]

There are a lot of fares for our tiny little network. Perhaps we don’t want to edit them one at a time. We can do a little Python programming to modify fares all at once according to some logic. We put a bigger plane on one of AL1’s Boston to Chicago legs, so let’s lower their fares in that market a smidge to help fill that plane.

for f in cfg.fares:
    if f.carrier == "AL1" and f.orig == "BOS" and f.dest == "ORD":
        f.price -= 5.0

Not all our changes need to be about the network; we can also manipulate the settings for the simulation itself. Our modified network is just a small experiment, we we don’t need to sit around waiting for a statistically useful number of samples to run. Let’s cut way back so our simulation runs faster.

cfg.simulation_controls.num_trials = 2
cfg.simulation_controls.num_samples = 200

Now that we’ve modified our cfg all we want, we can use it to initialize a simulation to run.

sim = pax.Simulation(cfg)
summary = sim.run()
database filename ':memory:' is not supported, using temporary file-based database at /var/folders/sb/mb_3f0t16lx0wm3tr8r5g9rm0000gn/T/tmps3tk87mk/passengersim_temp_db.sqlite

Task Completed after 1.77 seconds

Having run the simulation, we can take a quick peek at the results.

summary.fig_carrier_revenues()
summary.fig_carrier_rasm()
summary.fig_fare_class_mix()