[1]:
from model import taxiway_model, create_fault_scen_metrics

from fmdtools.sim import propagate as prop
import networkx as nx
from common import plot_tstep, plot_course
[2]:
import pandas as pd

Taxiway Model Overview

The taxiway model has three main agents: - Helicopter, which lands and takes off from a helipad - Aircraft, which lands at a runway, taxis to a gate, and takes off from a runway (and may be UAVs or Piloted Aircraft) - ATC, which coordinates operations

These agents interact via the flows: - Ground, a MultiFlow tracking the map as well as agent assignments/allocations - Location, a MultiFlow tracking the position/velocity of each route on the map, and - Requests, a CommsFlow tracking the messages sent between the ATC and the Aircraft/Helicopters

[3]:
from fmdtools.analyze.graph import FunctionArchitectureTypeGraph
[4]:
mdl = taxiway_model()
[5]:
tg = FunctionArchitectureTypeGraph(mdl)
[6]:
tg.set_edge_labels(title='')
[7]:
fig, ax = tg.draw(figsize=(7,5), withlegend=True, legend_bbox=(0, .2))
../../_images/examples_taxiway_Paper_Notebook_7_0.png
[8]:
fig.savefig("modelstructure.eps", format="eps", bbox_inches = 'tight', pad_inches = 0)
The PostScript backend does not support transparency; partially transparent artists will be rendered opaque.
[9]:
mdl.flows['location']
[9]:
location Location flow: LocationState(x=30.0, y=25.0, xd=0.0, yd=0.0, speed=0.0, stage='flight', mode='standby')
   ma1 Location flow: LocationState(x=10.0, y=20.0, xd=0.0, yd=0.0, speed=0.0, stage='takeoff', mode='standby')
   percieved Location flow: LocationState(x=10.0, y=20.0, xd=0.0, yd=0.0, speed=0.0, stage='takeoff', mode='standby')
   closest Location flow: LocationState(x=10.0, y=20.0, xd=0.0, yd=0.0, speed=0.0, stage='takeoff', mode='standby')
   ma2 Location flow: LocationState(x=30.0, y=25.0, xd=0.0, yd=0.0, speed=0.0, stage='flight', mode='standby')
   percieved Location flow: LocationState(x=30.0, y=25.0, xd=0.0, yd=0.0, speed=0.0, stage='flight', mode='standby')
   closest Location flow: LocationState(x=10.0, y=20.0, xd=0.0, yd=0.0, speed=0.0, stage='takeoff', mode='standby')
   ma3 Location flow: LocationState(x=30.0, y=25.0, xd=0.0, yd=0.0, speed=0.0, stage='flight', mode='standby')
   percieved Location flow: LocationState(x=30.0, y=25.0, xd=0.0, yd=0.0, speed=0.0, stage='flight', mode='standby')
   closest Location flow: LocationState(x=30.0, y=25.0, xd=0.0, yd=0.0, speed=0.0, stage='flight', mode='standby')
   ua1 Location flow: LocationState(x=45.0, y=-5.0, xd=0.0, yd=0.0, speed=0.0, stage='park', mode='standby')
   percieved Location flow: LocationState(x=45.0, y=-5.0, xd=0.0, yd=0.0, speed=0.0, stage='park', mode='standby')
   closest Location flow: LocationState(x=30.0, y=25.0, xd=0.0, yd=0.0, speed=0.0, stage='flight', mode='standby')
   ua2 Location flow: LocationState(x=30.0, y=25.0, xd=0.0, yd=0.0, speed=0.0, stage='flight', mode='standby')
   percieved Location flow: LocationState(x=30.0, y=25.0, xd=0.0, yd=0.0, speed=0.0, stage='flight', mode='standby')
   closest Location flow: LocationState(x=30.0, y=25.0, xd=0.0, yd=0.0, speed=0.0, stage='flight', mode='standby')
   ua3 Location flow: LocationState(x=30.0, y=25.0, xd=0.0, yd=0.0, speed=0.0, stage='flight', mode='standby')
   percieved Location flow: LocationState(x=30.0, y=25.0, xd=0.0, yd=0.0, speed=0.0, stage='flight', mode='standby')
   closest Location flow: LocationState(x=30.0, y=25.0, xd=0.0, yd=0.0, speed=0.0, stage='flight', mode='standby')
   h1 Location flow: LocationState(x=65.0, y=15.0, xd=0.0, yd=0.0, speed=0.0, stage='land', mode='standby')
   percieved Location flow: LocationState(x=65.0, y=15.0, xd=0.0, yd=0.0, speed=0.0, stage='land', mode='standby')
   closest Location flow: LocationState(x=45.0, y=-5.0, xd=0.0, yd=0.0, speed=0.0, stage='park', mode='standby')
   h2 Location flow: LocationState(x=30.0, y=25.0, xd=0.0, yd=0.0, speed=0.0, stage='flight', mode='standby')
   percieved Location flow: LocationState(x=30.0, y=25.0, xd=0.0, yd=0.0, speed=0.0, stage='flight', mode='standby')
   closest Location flow: LocationState(x=30.0, y=25.0, xd=0.0, yd=0.0, speed=0.0, stage='flight', mode='standby')
[10]:
mdl.flows['requests']
[10]:
requests Requests flow: RequestState(atc_com='None', asset_req='None', route='           ')
   atc: atc Requests flow: RequestState(atc_com='None', asset_req='land', route='           ')
       out: atc_out Requests flow: RequestState(atc_com='None', asset_req='None', route='           ')
       in: {'ma1': ('asset_req',), 'ma2': ('asset_req',), 'ma3': ('asset_req',), 'ua1': ('asset_req',), 'ua2': ('asset_req',), 'ua3': ('asset_req',), 'h1': ('asset_req',), 'h2': ('asset_req',)}
       received: {}
       h1: h1 Requests flow: RequestState(atc_com='None', asset_req='taxi', route='           ')
       h2: h2 Requests flow: RequestState(atc_com='None', asset_req='land', route='           ')
       ua1: ua1 Requests flow: RequestState(atc_com='None', asset_req='taxi', route='           ')
       ua2: ua2 Requests flow: RequestState(atc_com='None', asset_req='land', route='           ')
       ua3: ua3 Requests flow: RequestState(atc_com='None', asset_req='land', route='           ')
       ma1: ma1 Requests flow: RequestState(atc_com='None', asset_req='takeoff', route='           ')
       ma2: ma2 Requests flow: RequestState(atc_com='None', asset_req='land', route='           ')
       ma3: ma3 Requests flow: RequestState(atc_com='None', asset_req='land', route='           ')
   ma1: ma1 Requests flow: RequestState(atc_com='None', asset_req='takeoff', route='           ')
       out: ma1_out Requests flow: RequestState(atc_com='None', asset_req='takeoff', route='           ')
       in: {}
       received: {}
   ma2: ma2 Requests flow: RequestState(atc_com='None', asset_req='land', route='           ')
       out: ma2_out Requests flow: RequestState(atc_com='None', asset_req='land', route='           ')
       in: {}
       received: {}
   ma3: ma3 Requests flow: RequestState(atc_com='None', asset_req='land', route='           ')
       out: ma3_out Requests flow: RequestState(atc_com='None', asset_req='land', route='           ')
       in: {}
       received: {}
   ua1: ua1 Requests flow: RequestState(atc_com='None', asset_req='taxi', route='           ')
       out: ua1_out Requests flow: RequestState(atc_com='None', asset_req='taxi', route='           ')
       in: {}
       received: {}
   ua2: ua2 Requests flow: RequestState(atc_com='None', asset_req='land', route='           ')
       out: ua2_out Requests flow: RequestState(atc_com='None', asset_req='land', route='           ')
       in: {}
       received: {}
   ua3: ua3 Requests flow: RequestState(atc_com='None', asset_req='land', route='           ')
       out: ua3_out Requests flow: RequestState(atc_com='None', asset_req='land', route='           ')
       in: {}
       received: {}
   h1: h1 Requests flow: RequestState(atc_com='None', asset_req='taxi', route='           ')
       out: h1_out Requests flow: RequestState(atc_com='None', asset_req='taxi', route='           ')
       in: {}
       received: {}
   h2: h2 Requests flow: RequestState(atc_com='None', asset_req='land', route='           ')
       out: h2_out Requests flow: RequestState(atc_com='None', asset_req='land', route='           ')
       in: {}
       received: {}
[11]:
mdl.flows['ground']
[11]:
ground Environment flow: TaxiwayStates(area_allocation={'takeoff1': {'ma1'}, 'landing1': set(), 'helipad1': {'h1'}, 'gate1': set(), 'gate2': set(), 'gate3': set(), 'gate4': set(), 'gate5': set(), 'gate6': {'ua1'}, 'air_loc': set()}, asset_area={'h1': 'helipad1', 'h2': 'air_loc', 'ua1': 'gate6', 'ua2': 'air_loc', 'ua3': 'air_loc', 'ma1': 'takeoff1', 'ma2': 'air_loc', 'ma3': 'air_loc'}, asset_assignment={'h1': 'helipad1', 'h2': 'air_loc', 'ua1': 'gate6', 'ua2': 'air_loc', 'ua3': 'air_loc', 'ma1': 'takeoff1', 'ma2': 'air_loc', 'ma3': 'air_loc'})
   atc Environment flow: TaxiwayStates(area_allocation={'takeoff1': {'ma1'}, 'landing1': set(), 'helipad1': {'h1'}, 'gate1': set(), 'gate2': set(), 'gate3': set(), 'gate4': set(), 'gate5': set(), 'gate6': {'ua1'}, 'air_loc': set()}, asset_area={'h1': 'helipad1', 'h2': 'air_loc', 'ua1': 'gate6', 'ua2': 'air_loc', 'ua3': 'air_loc', 'ma1': 'takeoff1', 'ma2': 'air_loc', 'ma3': 'air_loc'}, asset_assignment={'h1': 'helipad1', 'h2': 'air_loc', 'ua1': 'gate6', 'ua2': 'air_loc', 'ua3': 'air_loc', 'ma1': 'takeoff1', 'ma2': 'air_loc', 'ma3': 'air_loc'})

Model Simulation

[12]:
fig, ax = mdl.flows['ground'].show_map()
../../_images/examples_taxiway_Paper_Notebook_13_0.png
[13]:
endresults, mdlhist = prop.nominal(mdl)
[14]:
fig, ax=plot_tstep(mdl, mdlhist, 16, show_area_allocation=False, locattr="stage", title="Taxiway Activity ")
../../_images/examples_taxiway_Paper_Notebook_15_0.png
[15]:
fig.savefig("modelactivity.eps", format="eps", bbox_inches = 'tight', pad_inches = 0)
[16]:
fig, ax = plot_course(mdl, mdlhist, "ma2", title="One aircraft's route over time")
../../_images/examples_taxiway_Paper_Notebook_17_0.png
[17]:
fig.savefig("assetroute.eps", format="eps", bbox_inches = 'tight', pad_inches = 0)
[18]:
from fmdtools.analyze import phases
phasemaps = phases.from_hist(mdlhist)
phases_to_plot ={"ma2": phasemaps["ma2"], 'h2': phasemaps['h2']}
fig = phases.phaseplot(phases_to_plot, figsize=(4,4), title_padding=-0.02, title="Asset Operational Phases", phase_ticks="both")
../../_images/examples_taxiway_Paper_Notebook_19_0.png
[19]:
fig.savefig("assetmodes.eps", format="eps", bbox_inches = 'tight', pad_inches = 0)

Fault Simulation and Analysis

Perception Fault - AC Vision Fault

[20]:
endresults, mdlhist = prop.one_fault(mdl, "ma3", "lost_sight",
                                    desired_result={93: {"graph.flows.location":{'include_glob':False}},
                                                    110:{"graph.flows.location":{'include_glob':False}},
                                                    20:["graph"], 120:['graph', "endclass"]})
[21]:
fig, ax = plot_tstep(mdl, mdlhist.faulty, 93,  locattr="stage", assets_to_label=["ma3", "ma2"], areas_to_label=[],
           title="MA3 approaches MA2 with no vision cone ", show_area_allocation=False)
../../_images/examples_taxiway_Paper_Notebook_23_0.png
[22]:
fig.savefig("ac_vision_map.eps", format="eps", bbox_inches = 'tight', pad_inches = 0)
[23]:
endresults.t120p0.endclass #table should also include local, global metrics
[23]:
num_cycled:                            4
perc_cycled:                         0.5
num_crashed:                           2
[24]:
endresults.t93p0.graph.flows.location
endresults.t93p0.graph.flows.location.set_edge_labels(title="")
[25]:
#%matplotlib qt
#endresults.t93p0.graph.flows.location.move_nodes()
[26]:
%matplotlib inline
[27]:
pos = {'ma1': [-0.51, -0.08], 'ma1_percieved': [-0.88, 0.03],
       'ma1_closest': [-0.3, 0.12], 'ma2': [-0.66, 0.59],
       'ma2_percieved': [-0.81, 0.84], 'ma2_closest': [-0.67, 0.26],
       'ma3': [0.77, 0.49], 'ma3_percieved': [0.81, 0.83],
       'ma3_closest': [0.51, 0.28], 'ua1': [0.0, 0.62],
       'ua1_percieved': [0.0, 0.91], 'ua1_closest': [-0.02, 0.34],
       'ua2': [0.02, -0.64], 'ua2_percieved': [0.0, -0.88],
       'ua2_closest': [0.04, -0.38], 'ua3': [0.66, -0.48],
       'ua3_percieved': [0.8, -0.84], 'ua3_closest': [0.42, -0.23],
       'h1': [0.64, 0.11], 'h1_percieved': [0.93, -0.15],
       'h1_closest': [0.3, 0.07], 'h2': [-0.69, -0.52],
       'h2_percieved': [-0.79, -0.85], 'h2_closest': [-0.45, -0.32]}
endresults.t93p0.graph.flows.location.set_pos(**pos)
[28]:
fig, ax = endresults.t93p0.graph.flows.location.draw(figsize=(8,8), title="t=93", legend_bbox=(0.84,0.27))
../../_images/examples_taxiway_Paper_Notebook_30_0.png
[29]:
fig.savefig("ac_loc_93.eps", format="eps", bbox_inches = 'tight', pad_inches = 0)
The PostScript backend does not support transparency; partially transparent artists will be rendered opaque.
[30]:
#%matplotlib qt
#endresults.t120p0.graph.move_nodes()
[31]:
%matplotlib inline
[32]:
pos = {'atc': [-1.0, -0.15], 'ma1': [-0.86, 0.52], 'ma2': [-0.33, 0.95],
       'ma3': [0.62, -0.78], 'ua1': [-0.66, -0.76], 'ua2': [0.36, 0.94],
       'ua3': [-0.02, -1.0], 'h1': [0.89, 0.48], 'h2': [1.0, -0.2],
       'ground': [0.31, -0.26], 'location': [-0.31, -0.24], 'requests': [-0.01, 0.43]}
endresults.t120p0.graph.set_pos(**pos)
[33]:
endresults.t120p0.graph.set_edge_labels(title="")
endresults.t120p0.graph.set_node_styles(label=dict(FxnBlock=dict(node_size=2000),
                                                  MultiFlow=dict(node_size=2000),
                                                  CommsFlow=dict(node_size=2000)),
                                       degraded={}, faulty={})
[34]:
fig, ax = endresults.t120p0.graph.draw(figsize=(5,5), withlegend=False)
../../_images/examples_taxiway_Paper_Notebook_36_0.png
[35]:
fig.savefig("ac_faultprop_120.eps", format="eps", bbox_inches = 'tight', pad_inches = 0)
[36]:
ind_hist = create_fault_scen_metrics(mdlhist)
[37]:
fig, ax = ind_hist.plot_line("degraded_fields",
                             "cycled_assets",
                             "unsafe_distances",
                             "assets_without_sight",
                             "faulty_functions",
                             time_slice=[0,93],
                             time_slice_label = "fault injection/occurence",
                             ylabels={'off-nominal fields':'%'})
../../_images/examples_taxiway_Paper_Notebook_39_0.png
[38]:
fig.savefig("fault_history_ac_vision.eps", format="eps", bbox_inches = 'tight', pad_inches = 0)
The PostScript backend does not support transparency; partially transparent artists will be rendered opaque.
[39]:
final_values = ind_hist.faulty.get_slice(-1)
final_values
[39]:
{'incorrect_fields': 0,
 'assets_without_sight': 0,
 'unsafe_distances': 0,
 'overbooked_locations': 0,
 'incorrect_perception': 0,
 'duplicate_land_commands': 0,
 'cycled_assets': 4,
 'degraded_fields': 74.2603550295858,
 'faulty_functions': 0.2222222222222222,
 'time': 120.0}
[40]:
final_res = pd.DataFrame.from_dict({k: [v] for k, v in final_values.items()}, orient='index')
final_res
[40]:
0
incorrect_fields 0.000000
assets_without_sight 0.000000
unsafe_distances 0.000000
overbooked_locations 0.000000
incorrect_perception 0.000000
duplicate_land_commands 0.000000
cycled_assets 4.000000
degraded_fields 74.260355
faulty_functions 0.222222
time 120.000000
[41]:
print(final_res.to_latex())
\begin{tabular}{lr}
\toprule
 & 0 \\
\midrule
incorrect_fields & 0.000000 \\
assets_without_sight & 0.000000 \\
unsafe_distances & 0.000000 \\
overbooked_locations & 0.000000 \\
incorrect_perception & 0.000000 \\
duplicate_land_commands & 0.000000 \\
cycled_assets & 4.000000 \\
degraded_fields & 74.260355 \\
faulty_functions & 0.222222 \\
time & 120.000000 \\
\bottomrule
\end{tabular}

Communications Fault - Poor land command by ATC

[42]:
from fmdtools.analyze.graph import MultiFlowGraph, CommsFlowGraph
ground_args = {'include_glob':True, "include_states":True,
              'send_connections':{"asset_area":"asset_area",
                                  "area_allocation":"area_allocation",
                                  "asset_assignment":"asset_assignment"}}
req_args = {'include_glob':False, "ports_only":True}

endresults, mdlhist = prop.sequence(mdl, faultseq={8:{"atc":["wrong_land_command"]},10:{"ua2":["lost_sight"]}},
                                         desired_result={10:{"graph.flows.requests":(CommsFlowGraph, req_args)},
                                                         11:{"graph.flows.requests":(CommsFlowGraph, req_args),
                                                            "graph.flows.ground":(MultiFlowGraph, ground_args)},
                                                         19:{"graph.flows.requests":{'include_glob':False, "ports_only":True}},
                                                         20:["graph"], 120:"endclass"})
[43]:
fig, ax = plot_tstep(mdl, mdlhist.faulty, 19, title="Aircraft crashed", areas_to_label=[])
../../_images/examples_taxiway_Paper_Notebook_46_0.png
[44]:
fig.savefig("atc_comms_map.eps", format="eps", bbox_inches = 'tight', pad_inches = 0)
[45]:
endresults.t120p0.endclass
[45]:
num_cycled:                            4
perc_cycled:                         0.5
num_crashed:                           2
[46]:
endresults.t11p0.graph.flows.ground
[46]:
<fmdtools.analyze.graph.MultiFlowGraph at 0x1f5aa481a50>
[47]:
import networkx as nx
[48]:
pos = nx.spring_layout(nx.MultiGraph(endresults.t11p0.graph.flows.ground.g))
endresults.t11p0.graph.flows.ground.set_pos(**pos)
endresults.t11p0.graph.flows.ground.set_node_labels(title="last", subtext="indicators")
endresults.t11p0.graph.flows.ground.set_edge_labels(title="")
endresults.t11p0.graph.flows.ground.set_node_styles(label={'State':dict(node_size=800),
                                                          'MultiFlow':dict(node_size=800)},
                                                   degraded={}, faulty={})
[49]:
fig, ax = endresults.t11p0.graph.flows.ground.draw(figsize=(7,7), legend_labelspacing=3, legend_bbox=(0.79,0.6))
../../_images/examples_taxiway_Paper_Notebook_52_0.png
[50]:
fig.savefig("atc_comms_ground_11.eps", format="eps", bbox_inches = 'tight', pad_inches = 0)
The PostScript backend does not support transparency; partially transparent artists will be rendered opaque.
[51]:
pos = nx.spring_layout(nx.MultiGraph(endresults.t10p0.graph.flows.requests.g), iterations=500)
endresults.t10p0.graph.flows.requests.set_pos(**pos)
[52]:
endresults.t10p0.graph.flows.requests.set_edge_labels(title="")
endresults.t10p0.graph.flows.requests.set_node_styles(label={'CommsFlow':dict(node_size=900)},
                                                   degraded={}, faulty={})
[53]:
fig, ax = endresults.t10p0.graph.flows.requests.draw(figsize=(10,10), legend_bbox=(0.7,0.9))
../../_images/examples_taxiway_Paper_Notebook_57_0.png
[54]:
fig.savefig("atc_comms_requests_10.eps", format="eps", bbox_inches = 'tight', pad_inches = 0)
The PostScript backend does not support transparency; partially transparent artists will be rendered opaque.
[55]:
pos = {'atc': [-1.0, -0.22], 'ma1': [-0.86, 0.52], 'ma2': [-0.33, 0.95],
       'ma3': [0.62, -0.78], 'ua1': [-0.66, -0.76], 'ua2': [0.36, 0.94],
       'ua3': [-0.02, -1.0], 'h1': [0.89, 0.48], 'h2': [1.0, -0.2],
       'ground': [0.31, -0.26], 'location': [-0.31, -0.24], 'requests': [-0.01, 0.43]}
endresults.t20p0.graph.set_pos(**pos)
endresults.t20p0.graph.set_edge_labels(title="")
endresults.t20p0.graph.set_node_styles(label=dict(FxnBlock=dict(node_size=2000),
                                                  MultiFlow=dict(node_size=2000),
                                                  CommsFlow=dict(node_size=2000)),
                                       degraded={}, faulty={})
[56]:
fig, ax = endresults.t20p0.graph.draw(withlegend=False, figsize=(5,5))
../../_images/examples_taxiway_Paper_Notebook_60_0.png
[57]:
fig.savefig("atc_comms_resgraph.eps", format="eps", bbox_inches = 'tight', pad_inches = 0)
[58]:
ind_hist = create_fault_scen_metrics(mdlhist)
[59]:
fig, ax = ind_hist.plot_line("degraded_fields",
                            "cycled_assets",
                            "unsafe_distances",
                            "assets_without_sight",
                            "faulty_functions",
                            time_slice=[8, 10],
                            time_slice_label = "fault injection/occurence",
                            ylabels={'off-nominal fields':'%'})
../../_images/examples_taxiway_Paper_Notebook_63_0.png
[60]:
fig.savefig("fault_history_atc_comms.eps", format="eps", bbox_inches = 'tight', pad_inches = 0)
The PostScript backend does not support transparency; partially transparent artists will be rendered opaque.
[61]:
final_values = ind_hist.faulty.get_slice(-1)
final_values
[61]:
{'incorrect_fields': 0,
 'assets_without_sight': 0,
 'unsafe_distances': 0,
 'overbooked_locations': 0,
 'incorrect_perception': 0,
 'duplicate_land_commands': 0,
 'cycled_assets': 4,
 'degraded_fields': 64.20118343195266,
 'faulty_functions': 0.3333333333333333,
 'time': 120.0}
[62]:
final_res = pd.DataFrame.from_dict({k: [v] for k, v in final_values.items()}, orient='index')
final_res
[62]:
0
incorrect_fields 0.000000
assets_without_sight 0.000000
unsafe_distances 0.000000
overbooked_locations 0.000000
incorrect_perception 0.000000
duplicate_land_commands 0.000000
cycled_assets 4.000000
degraded_fields 64.201183
faulty_functions 0.333333
time 120.000000
[63]:
print(final_res.to_latex())
\begin{tabular}{lr}
\toprule
 & 0 \\
\midrule
incorrect_fields & 0.000000 \\
assets_without_sight & 0.000000 \\
unsafe_distances & 0.000000 \\
overbooked_locations & 0.000000 \\
incorrect_perception & 0.000000 \\
duplicate_land_commands & 0.000000 \\
cycled_assets & 4.000000 \\
degraded_fields & 64.201183 \\
faulty_functions & 0.333333 \\
time & 120.000000 \\
\bottomrule
\end{tabular}

[ ]: