Fast-Time Simulation: Building an Airport

Blog Update:  I wrote yesterday’s blog on Friday night through midnight. I thought I was on the right track with the schedule I was developing, but it did not fit with the rest of the simulation. So I have updated that blog with the amended code I used today.

It’s Sunday night, and while I haven’t quite finished with my first foray into fast-time simulation, I am enjoying the process. Today, I set about “building” an airport for my simulation. To make sure it worked, I ran a simulation of one day’s flight schedule. ChatGPT has been here to help me, but, as I will discuss here and in the days following, it hasn’t been as big of a help as I had hoped.

Class is in Session

When I first asked ChatGPT to help me code an airport parking simulation, I got a short program that would create six aircraft, allocate aircraft to gates and randomly depart two of them. I’m not sure what it thought I was after, but that wasn’t it. I’ve posted the code at the bottom.

While it was how bad this code was that inspired me to write the code for the schedule, it did introduce me to “classes”. I haven’t used this technique in Python before, but it seemed pretty intuitive, so I thought I’d try it out.

I’ve used the verb “build” a couple of times now, which is a good way of thinking about this. The first function that sits within my airport is to establish the number of stands (bays for my Aussie readers) and an overflow area. Of course, this limitless overflow area is a complete fabrication. Still, I need some way of tracking the aircraft I can’t park on stands.

After this, I built the airport’s processes. They include allocating stands, parking from the overflow (they get priority), parking from landing and departing aircraft. As a combination tech and airport operations nerd, I was in heaven. This was like writing an aerodrome manual in Python.

Now, just in case you are like me and this sounds cool, here is my airport code:

class Airport:
  def __init__(self, num_stands):
    self.stands = [None] * num_stands
    self.overflow = []

  # This function adds some randomness to the arrival times 
  # (in a more complication version, if could simulate weather or
  # other delay scenarios)
  def arrival_otp(self, dataframe):
    dataframe['ata'] = dataframe.apply(lambda row: 
                                       ((datetime.strptime(row['sta'], "%H:%M") 
                                       + timedelta(minutes=int(
                                           random.normalvariate(0, 10))
                                       )).strftime("%H:%M")
                                       ), axis=1)
    return dataframe
  
  # This function updates the log with the status of the stands 
  # and the overflow
  def log_update(self, time, dataframe):
    for stand, aircraft in enumerate(self.stands):
      if aircraft is not None:
        dataframe.at[time, stand] = aircraft.rego
      else:
        np.nan
    dataframe.at[time, 'overflow'] = len(self.overflow)

  # Aircraft in overflow will be parked in order and then
  # the aircraft will be removed from the overflow area
  def arrival_fm_overflow(self, aircraft, time, dataframe):
    stand = self.allocate_stand()
    if stand is not None:
      self.stands[stand] = aircraft
      dataframe.at[time, stand] = aircraft.rego
      aircraft.park(time, stand)
      self.overflow.remove(aircraft)
  
  # Upon landing, aircraft will either be allocated a stand or
  # if none are available, they will be sent to the overflow
  def arrival_fm_landing(self, aircraft, time, dataframe):
    stand = self.allocate_stand()
    if stand is not None:
      self.stands[stand] = aircraft
      dataframe.at[time, stand] = aircraft.rego
      aircraft.park(time, stand)
    else:
      self.overflow.append(aircraft)
    dataframe.at[time, 'overflow'] = len(self.overflow)

  # This function is used by the arrival functions to find the next
  # available stand
  def allocate_stand(self):
    for stand, aircraft in enumerate(self.stands):
      if aircraft is None:
        return stand
    return None

  # This function sends the aircraft on its merry way and sets the
  # stand's status to None (empty)
  def departure(self, aircraft, time, dataframe):
    if aircraft in self.stands:
      stand = self.stands.index(aircraft)
      self.stands[stand] = None
      dataframe.at[time, stand] = np.nan
    else:
      print(f"Aircraft  not found")

This is a pretty simple airport. It’s only got stands, and we’re either allocating them a position or pushing them back to depart. Of course, real airports have a lot more facilities and a bunch more processes. But this is how you would build them.

Some of the things I removed from the schedule code were repurposed here. The first function, called arrival_otp, introduces my desired randomness. The system needed to be more like the real world. In this area, you could model the impact of weather or workforce issues or even the NOTAM system going down (more on that tomorrow).

You may notice that I have aircraft in my code. I had to build them too, but they are simple and boring.

Test Run

I had to write a basic simulation code to test my airport and its processes. This is known as de-bugging, and my airport was infested. There is a lot of repetition in this code, and mixing up your stand and stands can be easy. That “s” makes a lot of difference!

For my test simulation, I built an airport with five parking stands and then sent 88 aircraft to land there over 15 hours or so. For all my little airport worker bots, their day would have sucked. And it did with up to eight aircraft in the overflow at one point.

Let’s do that again, and again, and again

In the next day or so, I will do this thousands of times with different airport and schedule parameters. Then, I’m going to see drill down on the relationship between the number of stands and delays. It might take a bit to finalise my code, but I am looking forward to playing God with this little airport.

Who knew I would enjoy that type of thing?


Writer’s Note: I know I had set a trend of having these posts go live just after 0700 (AEST) each day, but I never promised that. Turns out, I do not have all the time in the world. 🤷

Original ChaptGPT Code:

Dan Parsons

Dan is an airport operations manager currently working at Queenstown Airport in beautiful New Zealand. His previous roles have included airport and non-process infrastructure operation manager in the mining industry, government inspector with the Civil Aviation Safety Authority and airport trainer. Dan’s special interests include risk management, leadership and process hacks to make running airports easier. 

http://therunwaycentreline.com
Previous
Previous

NOTAM System Failure: Not What-If but When?

Next
Next

(UPDATED) Fast-Time Simulation Series: Release the Nerds