(UPDATED) Fast-Time Simulation Series: Release the Nerds
UPDATE: As I got deeper into this process, I had to make some changes to the schedule building code below. I have updated it to the current code (more changes might come) but I’ve kept the old code at the bottom.
Fast-time simulation is a topic that has always teased my interest. I’ve enjoyed building models of some pretty complex systems in Excel, but I’ve pushed that platform to the limit and making the jump up to more sophisticated modelling has always seemed like too big of a leap. It seemed the realm of hardcore coders or professional platforms that don’t have a “play around for free” option.
With ChatGPT on my side, I decided to give it a go this weekend. So, come along with me as I either leap to new heights or fall into an abyss.
ChatGPT’s Got Some Coding Skills
One of the most exciting capabilities of ChatGPT (for plenty of other nerds out there and me) is that it can write computer code. Like much of what it does, the first time you see it punch out some code based on your description of what you want, your jaw drops.
And then you see the little explanation you get.
And then, you notice the “copy code” button.
For those into programming, it might interest you to know that in addition to writing code in a bunch of programming languages, it can translate between languages. Also, it can add comments to your existing code (as a very informally trained python programmer, I could improve at putting in comments).
For When Time is of the Essence
As much as I love aircraft, I can’t sit watch a year’s worth of aircraft fly in and out of an airport. Especially if I want to predict when problems will arise and how bad they’ll be. Fast-time simulation lets you do that and can let you do it lots and lots of times.
The particular scenario I want to model is a familiar one to many airport managers out there. Is my apron going to fill up during my peak busy day (or hour), and what will the knock-on effects be.
Yes, you could model this (and I have) in Excel but introducing a little real-world randomness is pretty tricky in Excel, especially if you are looking at a minute-by-minute model over a six-month schedule. Furthermore, the complexity increases when you want to introduce a particular type of randomness.
While aircraft don’t always arrive and depart on time, we often have data on the distribution of their on-time performance. We could use those figures to introduce realistic “unpredictability” into our model. However, it is no use modelling a schedule established to ensure apron capacity wasn’t exceeded. That’s not the real world.
I’m sure all of us have experienced one of those horror days where everyone who should be early is late, and everyone who should be late is early. It gets crazy quickly. It doesn’t happen every day, but it does happen. Fast-time simulations let to test out hundreds, if not thousands, of days to see how the airport performs when things go well and when things go wrong.
Setting a Schedule
To set me on this path, the first thing I had to build was a schedule. I initially thought about using a real-life schedule and changing the names. I’m sure I’ve got one lying around here somewhere. But then I had the silly idea that I might want more control. So, let’s build one, I thought.
I should have asked ChatGPT to help me, but I got all cocky, and here I now am, writing a blog post 30 minutes past midnight. I got there in the end, though.
The function I built (see below) uses a broad distribution of flights each hour to construct the hourly schedule. Randomness is introduced into the scheduled arrival and departure times using variables I just made up. I did use ChatGPT to help with some specific aspects of this function and to help me write the comments.
The result is a daily schedule of just under 90 flights. Here is the daily flight distribution:
If you are interested, here is the code (feel free to critique and please note that this was updated after I originally posted this article):
def build_schedule(mean_busy_day_overview): # create an empty schedule dataframe schedule = pd.DataFrame(columns=['acft_rego', 'sta', 'std']) # iterate over the hours and flights in the mean_busy_day_overview for hour, flights in mean_busy_day_overview.items(): start_time = 0 if flights == 0: None else: # calculate the average time between flights average_time_between = int(60 / flights) # iterate over the number of flights in the hour for flight in range(0, flights): # Randomly generate an aircraft registration # create a list of alphabet letters letters = list(string.ascii_uppercase) # standardised registration initial letter rego_code_start = 'FTS' # use random.randint() to generate a random 3 digits number rego_number = random.randint(10,99) # use random.choices() to generate two random letters rego_code_end = ''.join(random.choices(letters, k=2)) # concatenate the flight_code and flight_number acft_rego = rego_code_start + str(rego_number) + rego_code_end # generate a random time variation for the flight # initialization time rand_time_var = int(random.normalvariate(0, 5)) # calculate the flight initialization time flight_init_min = start_time + average_time_between + rand_time_var # ensure that the flight initialization time does not exceed # 60 minutes or is less than 0 minutes if flight_init_min >= 60: flight_init_min = 60 - 1 elif flight_init_min < 0: flight_init_min = 0 # ensure that the flight initialization time is in the # format of HH:MM if len(str(flight_init_min)) == 1: flight_init_min = str(flight_init_min).zfill(2) # calculate the scheduled takeoff time (sta) sta = str(hour) + ':' + str(flight_init_min) # standardised turn around time sched_turn_time = 40 # calculate the scheduled takeoff time (std) std = datetime.strptime(sta, "%H:%M") + timedelta(minutes=sched_turn_time) std = std.strftime("%H:%M") # create a new row with the calculated values new_row = {'acft_rego': acft_rego, 'sta': sta, 'std': std} # append the new row to the schedule dataframe schedule = schedule.append(new_row, ignore_index=True) # update the start time for the next flight start_time = start_time + int(flight_init_min) return schedule
Tomorrow, I’m going to build an airport for me to test this schedule out.
Update: I needed to change the original code I posted because it no longer suited the simulation program as a whole. I didn’t need the actual times of arrival and departure or the turn-around time but I’ve kept the code here for future reference. I also removed the OTP discussion as that was not longer relevant.
def build_schedule(mean_busy_day_overview): # create an empty schedule dataframe schedule = pd.DataFrame(columns=['sta', 'flight_number', 'turn_time', 'std', 'ata', 'atd']) # iterate over the hours and flights in the mean_busy_day_overview for hour, flights in mean_busy_day_overview.items(): start_time = 0 if flights == 0: None else: # calculate the average time between flights average_time_between = int(60 / flights) # iterate over the number of flights in the hour for flight in range(0, flights): # generate a random time variation for the flight initialization time rand_time_var = int(random.normalvariate(0, 3)) # calculate the flight initialization time flight_init_min = start_time + average_time_between + rand_time_var # ensure that the flight initialization time does not exceed 60 minutes or is less than 0 minutes if flight_init_min >= 60: flight_init_min = 60 - 1 elif flight_init_min < 0: flight_init_min = 0 # ensure that the flight initialization time is in the format of HH:MM if len(str(flight_init_min)) == 1: flight_init_min = str(flight_init_min).zfill(2) # calculate the scheduled takeoff time (sta) sta = str(hour) + ':' + str(flight_init_min) # Randomly generate a flight number # create a list of alphabet letters letters = list(string.ascii_uppercase) # use random.choices() to generate two random letters flight_code = ''.join(random.choices(letters, k=2)) # use random.randint() to generate a random 3 digits number flight_number = random.randint(100,999) # concatenate the flight_code and flight_number flight_number = flight_code + str(flight_number) # generate a random turn time variation rand_turn_var = random.normalvariate(0, 10) turn_time = int(40 + rand_turn_var) # calculate the scheduled takeoff time (std) std = datetime.strptime(sta, "%H:%M") + timedelta(minutes=turn_time) std = std.strftime("%H:%M") # generate random variations for the actual arrival and departure times rand_arr_var = int(random.normalvariate(0, 10)) rand_dep_var = int(random.normalvariate(0, 5)) # calculate the actual arrival time (ata) ata = datetime.strptime(sta, "%H:%M") + timedelta(minutes=rand_arr_var) ata = ata.strftime("%H:%M") # calculate the actual departure time (atd) atd = datetime.strptime(std, "%H:%M") + timedelta(minutes=rand_dep_var) atd = atd.strftime("%H:%M") # create a new row with the calculated values new_row = {'sta': sta, 'flight_number': flight_number, 'turn_time': turn_time, 'std': std, 'ata': ata, 'atd': atd} # append the new row to the schedule dataframe schedule = schedule.append(new_row, ignore_index=True) # update the start time for the next flight start_time = start_time + int(flight_init_min) # return the final schedule dataframe schedule.to_csv('schedule.csv') return schedule