Action Sequence Graph Tutorial

This tutorial covers use of the ActionArchitecture in the Function class, which is useful for representing a Function’s progress through an action sequence graph, the different actions/tasks to be performed by a function (e.g., modes of operation, etc)..

[1]:


import fmdtools.sim.propagate as prop import inspect

Action architectures are used within a function block to represent Action Sequence Graphs - actions that the function performs and their sequence. Actions in an ActionArchitecture are respresented by Action blocks, which are similar to function and component blocks in that they have: - flow connections - modes, and - behaviors

Flow connections are routed to the action in the function block definition and represent the shared variables between the actions.

Modes are similar to function modes and are instantiated (as they are in components) at both the Function and Action level. Using the name= option enables one to tag these modes as action modes at the function level while using the same local name

Below we define the states to performed by the ASG: - Outcome, which tracks the number of actions and perceptions performed by the ASG - HazardState, which specifies whether a hazard is present, percieved, or mitigated

[2]:
from demo_model import OutcomeStates, Outcome, HazardState, Hazard
print(inspect.getsource(OutcomeStates))
print(inspect.getsource(Outcome))
print(inspect.getsource(HazardState))
print(inspect.getsource(Hazard))
class OutcomeStates(State):
    """State tracking the number of actions and perceptions performed."""

    num_perceptions: int = 0
    num_actions: int = 0

class Outcome(Flow):

    __slots__ = ()
    container_s = OutcomeStates

class HazardState(State):
    """Whether a hazard is present, percieved, or mitigated by the human."""

    present: bool = False
    percieved: bool = False
    mitigated: bool = False

class Hazard(Flow):

    __slots__ = ()
    container_s = HazardState

Below we define three actions for use in a given model: - Perceive, a user’s perception abilities/behaviors. In this function the user percieves a hazard (unless their perception fails) - Act, the user’s action which they perform to mitigate the hazard. - Done, the user’s state when they are done performing the action.

[3]:
from demo_model import Perceive, Act, Done
print(inspect.getsource(Perceive))
print(inspect.getsource(Act))
print(inspect.getsource(Done))
class Perceive(Action):
    """A user's perception abilities/behaviors for percieving the hazard."""

    __slots__ = ('hazard', 'outcome')
    container_m = ActionMode
    flow_hazard = Hazard
    flow_outcome = Outcome

    def behavior(self, time):
        if not self.m.in_mode('failed', 'unable'):
            self.hazard.s.percieved = self.hazard.s.present
            self.outcome.s.num_perceptions += self.hazard.s.percieved
        else:
            self.hazard.s.percieved = False
            self.m.remove_fault('failed', 'nom')

    def percieved(self):
        return self.hazard.s.percieved

class Act(Action):
    """User actions to mitigate the hazard."""

    __slots__ = ('hazard', 'outcome')
    container_m = ActionMode
    flow_hazard = Hazard
    flow_outcome = Outcome

    def behavior(self, time):
        if not self.m.in_mode('failed', 'unable'):
            self.outcome.s.num_actions += 1
            self.hazard.s.mitigated = True
        elif self.m.in_mode('failed'):
            self.hazard.s.mitigated = False
            self.m.remove_fault('failed', 'nom')
        else:
            self.hazard.s.mitigated = False

    def acted(self):
        return not self.m.in_mode('failed')

class Done(Action):
    """User state after performing the action."""

    __slots__ = ('hazard')
    flow_hazard = Hazard

    def behavior(self, time):
        if not self.hazard.s.present:
            self.hazard.s.mitigated = False

    def ready(self):
        return not self.hazard.s.present

To proceed through the sequence of actions, conditions must be met between each action. In these actions, we have defined the following conditions: - Percieve.percieved: perception is done if the hazard is percieved - Act.acted: the action is complete if the action was performed - Done.complete: the hazard mitigation is over (and mitigated state is reset to False)

To create the overall ASG structure, the following adds the flows, actions, and conditions to the ActionArchitecture class

[4]:
from demo_model import Human
print(inspect.getsource(Human))
class Human(ActionArchitecture):
    """Overall human action sequence graph specifying what the user will do when."""
    initial_action = "perceive"

    def init_architecture(self, *args, **kwargs):
        # flows from external fxn/model can be defined here,
        self.add_flow("hazard", Hazard)
        # along with flows internal to the ASG class
        self.add_flow("outcome", fclass=Outcome)

        self.add_act("perceive", Perceive, "outcome", "hazard")
        self.add_act("act", Act, "outcome", "hazard")
        self.add_act("done", Done, "hazard")

        self.add_cond("perceive", "act", "percieved", self.acts['perceive'].percieved)
        self.add_cond("act", "done", "acted", self.acts['act'].acted)
        self.add_cond("done", "perceive", "ready", self.acts['done'].ready)

Note the use of the following methods: - add_flow adds a flow so it can pass variables between actions. Here Outcome is an internal flow, while Hazard is an external flow.

[5]:
from fmdtools.define.architecture.action import ActionArchitecture
help(ActionArchitecture.add_flow)
Help on function add_flow in module fmdtools.define.architecture.base:

add_flow(self, name, fclass=<class 'fmdtools.define.flow.base.Flow'>, **kwargs)
    Add a flow with given attributes to the model.

    Parameters
    ----------
    name : str
        Unique flow name to give the flow in the model
    fclass : Class, optional
        Class to instantiate (e.g. CommsFlow, MultiFlow). Default is Flow.
        Class must take flowname, p, s as input to __init__()
        May alternatively provide already-instanced object.
    kwargs: kwargs
        Dicts for non-default values to p, s, etc

  • add_act adds the action to the function and hands it the given flows and parameters. Here the actions are “Percieve”, “Act”, and “Done”

[6]:
help(ActionArchitecture.add_act)
Help on function add_act in module fmdtools.define.architecture.action:

add_act(self, name, actclass, *flownames, **fkwargs)
    Associate an Action with the architecture. Called after add_flow.

    Parameters
    ----------
    name : str
        Internal Name for the Action
    actclass : Action
        Action class to instantiate
    *flownames : flow
        Flows (optional) which connect the actions
    duration:
        Duration of the action. Default is 0.0
    **kwargs : any
        kwargs to instantiate the Action with.

  • add_cond specifies the conditions for going from one action to another.

[7]:
help(ActionArchitecture.add_cond)
Help on function add_cond in module fmdtools.define.architecture.action:

add_cond(self, start_action, end_action, name='auto', condition='pass')
    Associate a Condition with the ActionArchitecture.

    Conditions specify when to precede from one action to the next.

    Parameters
    ----------
    start_action : str
        Action where the condition is checked
    end_action : str
        Action that the condition leads to.
    name : str
        Name for the condition.
        Defaults to numbered conditions if none are provided.
    condition : method
        Method in the class to use as a condition.
        Defaults to self.condition_pass if none are provided.

  • ASG.build finally constructs the structure of the ASG (see: self.action_graph and self.flow_graph) and determines the settings for the simulation. In DetectHazard, default options are used, with the first action specified as “Percieve” and also with it specified that the actions propagate in the dynamic step (rather than static step)

[8]:
help(ActionArchitecture.build)
Help on function build in module fmdtools.define.architecture.action:

build(self, **kwargs)
    Construct the overall model structure.

    Use in subclasses to build the model after init_architecture is called.

    Parameters
    ----------
    update_seed : bool
        Whether to update the seed

We can look at the attributes of the ASG by instantiating it:

[9]:
h = Human()
h
[9]:
<demo_model.Human at 0x1e67fe944a0>

ActionArchitectureGraph can also be used to visualize the flow of actions (e.g., which are active, flows vs conditions, etc.).

[10]:
from fmdtools.analyze.graph import ActionArchitectureGraph
[11]:
ag = ActionArchitectureGraph(h)
[12]:
fig, ax = ag.draw()
../../_images/examples_asg_demo_Action_Sequence_Graph_22_0.png

As shown, the “Percieve” action is active (green), while the inactive actions are shown in blue. This action is active because it was defined as initial_action in the ASG definition.

These are stored as attributes in the underlying graph structure:

[13]:
ag.g.nodes['perceive']
[13]:
{'bipartite': 0,
 'label': 'Action',
 'active': True,
 'states': {},
 'faults': [],
 'indicators': []}

Function-level Simulation

ActionArchitectures must be instantiated within a Function in order to be simulable with methods in propagate. Below we instantiate a Function and show how an ActionArchitecture can be simulated independent of other Model attributes.

[14]:
from demo_model import DetectHazard
print(inspect.getsource(DetectHazard))
class DetectHazard(Function):
    """Function containing the human."""

    __slots__ = ('hazard')
    container_m = Mode
    arch_aa = Human
    flow_hazard = Hazard

If we update the action, we can see the ASG progress between states:

[15]:
ex_fxn = DetectHazard('detect_hazard')
ex_fxn.t.dt=1.0

ex_fxn.aa.flows['hazard']
[15]:
hazard Hazard flow: HazardState(present=False, percieved=False, mitigated=False)
[16]:
ex_fxn.aa.flows['hazard'].s.present=True
ex_fxn('dynamic', time= 1)
[17]:
ag = ActionArchitectureGraph(ex_fxn.aa)
ag.draw()
[17]:
(<Figure size 1200x1000 with 1 Axes>, <Axes: >)
../../_images/examples_asg_demo_Action_Sequence_Graph_30_1.png
[18]:
ex_fxn.aa.flows['hazard']
[18]:
hazard Hazard flow: HazardState(present=True, percieved=True, mitigated=True)
[19]:
assert ex_fxn.aa.flows['hazard'].s.present
[20]:
ex_fxn.aa.flows['outcome']
[20]:
outcome Outcome flow: OutcomeStates(num_perceptions=1, num_actions=1)
[21]:
assert ex_fxn.aa.flows['outcome'].s.num_perceptions==1
assert ex_fxn.aa.flows['outcome'].s.num_actions==1

As shown, each of the actions are progressed throuh in a single timestep until the ASG is in the “Done” action

[22]:
ex_fxn.aa.flows['hazard'].s.present=False
ex_fxn('dynamic', time= 2)
[23]:
ag = ActionArchitectureGraph(ex_fxn.aa)
fig, ax = ag.draw()
../../_images/examples_asg_demo_Action_Sequence_Graph_37_0.png

As shown, now that the hazard is no longer present, the “Ready” Condition is triggered and the ASG goes back to the percieve state.

[24]:
ex_fxn.aa.flows['outcome']
[24]:
outcome Outcome flow: OutcomeStates(num_perceptions=1, num_actions=1)
[25]:
ex_fxn.aa.flows['hazard']
[25]:
hazard Hazard flow: HazardState(present=False, percieved=False, mitigated=False)
[26]:
ex_fxn.hazard
[26]:
hazard Hazard flow: HazardState(present=False, percieved=False, mitigated=False)
[27]:
assert ex_fxn.aa.flows['hazard'].s.present==False
assert ex_fxn.aa.flows['hazard'].s.mitigated==False
assert id(ex_fxn.hazard) == id(ex_fxn.aa.flows['hazard'])

This is essentially what a step-though of a simulation looks like. In practice, we simulate these behaviors using propagate as long as the ActionArchitecture is contained in a Function. Here we use the disturbances argument to simulate an external hazard occuring at t=5:

[28]:
result_indiv, hist_indiv = prop.nominal(ex_fxn, disturbances={5:{'aa.flows.hazard.s.present':True}})
[29]:
fig, axs = hist_indiv.plot_line('aa.flows.hazard.s.present',
                                'aa.flows.hazard.s.percieved',
                                'aa.flows.hazard.s.mitigated', figsize=(10,5))
../../_images/examples_asg_demo_Action_Sequence_Graph_45_0.png
[30]:
hist_indiv.aa.active_actions
[30]:
array([{'perceive'}, {'perceive'}, {'perceive'}, {'perceive'},
       {'perceive'}, {'done'}, {'done'}, {'done'}, {'done'}, {'done'},
       {'done'}, {'done'}, {'done'}, {'done'}, {'done'}, {'done'},
       {'done'}, {'done'}, {'done'}, {'done'}, {'done'}, {'done'},
       {'done'}, {'done'}, {'done'}, {'done'}, {'done'}, {'done'},
       {'done'}, {'done'}, {'done'}, {'done'}, {'done'}, {'done'},
       {'done'}, {'done'}, {'done'}, {'done'}, {'done'}, {'done'},
       {'done'}, {'done'}, {'done'}, {'done'}, {'done'}, {'done'},
       {'done'}, {'done'}, {'done'}, {'done'}, {'done'}, {'done'},
       {'done'}, {'done'}, {'done'}, {'done'}, {'done'}, {'done'},
       {'done'}, {'done'}, {'done'}, {'done'}, {'done'}, {'done'},
       {'done'}, {'done'}, {'done'}, {'done'}, {'done'}, {'done'},
       {'done'}, {'done'}, {'done'}, {'done'}, {'done'}, {'done'},
       {'done'}, {'done'}, {'done'}, {'done'}, {'done'}, {'done'},
       {'done'}, {'done'}, {'done'}, {'done'}, {'done'}, {'done'},
       {'done'}, {'done'}, {'done'}, {'done'}, {'done'}, {'done'},
       {'done'}, {'done'}, {'done'}, {'done'}, {'done'}, {'done'},
       {'done'}], dtype=object)
[31]:
assert hist_indiv.aa.flows.hazard.s.mitigated[5]
assert hist_indiv.aa.active_actions[5]=={'done'}

Model Simulation

Below, this function is placed in the context of a FunctionalArchitecture model so we can see how it behaves in the context of its interactions.

[32]:
from demo_model import ProduceHazard, PassStates, PassHazard, HazardModel
print(inspect.getsource(ProduceHazard))
print(inspect.getsource(PassStates))
print(inspect.getsource(PassHazard))
print(inspect.getsource(HazardModel))
class ProduceHazard(Function):
    """Function producing Hazards."""

    __slots__ = ('hazard',)
    flow_hazard = Hazard

    def dynamic_behavior(self, time):
        if not time % 4:
            self.hazard.s.present = True
        else:
            self.hazard.s.present = False

class PassStates(State):
    """Whether or not the hazard is ultimately passed or mitigated."""

    hazards_mitigated:  int = 0
    hazards_propagated: int = 0

class PassHazard(Function):
    """Accumulates total hazards/mitigations."""

    __slots__ = ('hazard',)
    container_s = PassStates
    flow_hazard = Hazard

    def dynamic_behavior(self, time):
        if self.hazard.s.present and self.hazard.s.mitigated:
            self.s.hazards_mitigated += 1
        elif self.hazard.s.present and not self.hazard.s.mitigated:
            self.s.hazards_propagated += 1

class HazardModel(FunctionArchitecture):
    """Overall model of the human in context."""

    __slots__ = ()
    default_sp = dict(end_time=60, dt=1.0)

    def init_architecture(self, **kwargs):
        self.add_flow("hazard", Hazard)
        self.add_fxn("produce_hazard", ProduceHazard, 'hazard')
        self.add_fxn("detect_hazard", DetectHazard, 'hazard')
        self.add_fxn("pass_hazard",  PassHazard, 'hazard')

As shown, this model connects the detect_hazard with a detect_hazard function, which are used (1) to load the system and then to (2) tabulate the number of mitigations.

Below we show how this translates into simulation results:

[33]:
mdl = HazardModel()
endstate,  mdlhist = prop.nominal(mdl)

Below we look at the states of the functions/flows to see how this has simulated.

[34]:
mdlhist
[34]:
flows.hazard.s.present:        array(61)
flows.hazard.s.percieved:      array(61)
flows.hazard.s.mitigated:      array(61)
fxns.detect_hazard.aa.acts.perceive: array(61)
fxns.detect_hazard.aa.acts.act: array(61)
fxns.detect_hazard.aa.acts.done: array(61)
fxns.detect_hazard.aa.active_actions: array(61)
fxns.detect_hazard.m           array(61)
fxns.detect_hazard.m           array(61)
fxns.detect_hazard.m.faults.act_failed: array(61)
fxns.detect_hazard.m.faults.act_unable: array(61)
fxns.pass_hazard.s.hazards_mitigated: array(61)
fxns.pass_hazard.s.hazards_propagated: array(61)
time:                          array(61)
[35]:
restab = mdlhist.get('fxns.detect_hazard.aa.active_actions', 'flows.hazard.s').as_table()
[36]:
restab
[36]:
fxns.detect_hazard.aa.active_actions flows.hazard.s.present flows.hazard.s.percieved flows.hazard.s.mitigated
0 {done} True True True
1 {perceive} False False False
2 {perceive} False False False
3 {perceive} False False False
4 {done} True True True
... ... ... ... ...
56 {done} True True True
57 {perceive} False False False
58 {perceive} False False False
59 {perceive} False False False
60 {done} True True True

61 rows × 4 columns

As shown, the ASG alternates between Perceive (when the hazard is not present) and Done (when the hazard is present). As a result, all of the present hazards (above) are also perceived and mitigated, and no hazards are propagated.

Or, in plot form:

[37]:
fig, axs = mdlhist.plot_line('fxns.pass_hazard.s.hazards_mitigated',
                                'fxns.pass_hazard.s.hazards_propagated',
                                'flows.hazard.s.present',
                                'flows.hazard.s.percieved',
                                'flows.hazard.s.mitigated', figsize=(10,5))
../../_images/examples_asg_demo_Action_Sequence_Graph_57_0.png

As shown, perceptions and actions track the hazards mitigated.

Fault Simulation

Below we will simulate a fault and see how it tracks in the model.

[38]:
result_fault, mdlhist_fault = prop.one_fault(mdl, 'detect_hazard','perceive_failed', time=4, desired_result='graph')
[39]:
mdlhist_fault.faulty.fxns.detect_hazard.aa.acts.act
[39]:
array([act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set()),
       act Act
       - ActionMode(mode=nominal, faults=set())], dtype=object)

As shown, this fault results in the hazard not being perceived (and thus the hazard propagating)

[40]:
fig, axs = mdlhist_fault.plot_line('fxns.pass_hazard.s.hazards_mitigated',
                                'fxns.pass_hazard.s.hazards_propagated',
                                'flows.hazard.s.present',
                                'flows.hazard.s.percieved',
                                'flows.hazard.s.mitigated', figsize=(10,5), time_slice=[4])
../../_images/examples_asg_demo_Action_Sequence_Graph_63_0.png

As shown, this only shows up in the PassHazard function (since the fault is removed in one timestep).

[41]:
result_fault.graph.draw()
[41]:
(<Figure size 1200x1000 with 1 Axes>, <Axes: >)
../../_images/examples_asg_demo_Action_Sequence_Graph_65_1.png

To see this in more detail, we will process the results history and then use graph.results_from at the time of the fault. Note how the model histories can be processed below:

[42]:
reshist = mdlhist_fault.get_degraded_hist(*mdl.fxns, *mdl.flows)
reshist.as_table()
[42]:
detect_hazard pass_hazard hazard total time
0 True False True 1 0.0
1 True False True 1 1.0
2 True False True 1 2.0
3 True False True 1 3.0
4 True False True 1 4.0
... ... ... ... ... ...
56 True False True 1 56.0
57 True False True 1 57.0
58 True False True 1 58.0
59 True False True 1 59.0
60 True False True 1 60.0

61 rows × 5 columns

[43]:
reshist = mdlhist_fault.get_faulty_hist(*mdl.fxns, *mdl.flows)
reshist.as_table()
[43]:
detect_hazard total time
0 False 0 0.0
1 False 0 1.0
2 False 0 2.0
3 False 0 3.0
4 True 1 4.0
... ... ... ...
56 True 1 56.0
57 True 1 57.0
58 True 1 58.0
59 True 1 59.0
60 True 1 60.0

61 rows × 3 columns

[44]:
mdlhist_fault.faulty.fxns.detect_hazard
[44]:
aa.acts.perceive:              array(61)
aa.acts.act:                   array(61)
aa.acts.done:                  array(61)
aa.active_actions:             array(61)
m.faults.perceive_failed:      array(61)
m.faults.perceive_unable:      array(61)
m.faults.act_failed:           array(61)
m.faults.act_unable:           array(61)
[45]:
mdl.fxns['detect_hazard'].h
[45]:
aa.acts.perceive:              array(61)
aa.acts.act:                   array(61)
aa.acts.done:                  array(61)
aa.active_actions:             array(61)
m.faults.perceive_failed:      array(61)
m.faults.perceive_unable:      array(61)
m.faults.act_failed:           array(61)
m.faults.act_unable:           array(61)

We can further use draw_from to view the state of the ASG. See below:

[46]:
result_fault.graph.draw_from(4, mdlhist_fault)
[46]:
(<Figure size 1200x1000 with 1 Axes>, <Axes: title={'center': ' t=4'}>)
../../_images/examples_asg_demo_Action_Sequence_Graph_72_1.png

Note the lack of a fault at this time-step, despite it being instantiated here. This is because the fault was removed at the end of the same time-step it was added in.

The ‘unable’ fault, on the other hand, stays throughout the simulation and thus shows up:

[47]:
endstate_unable, mdlhist_unable = prop.one_fault(mdl, 'detect_hazard','act_unable', time=4,
                                                 desired_result={'graph','graph.fxns.detect_hazard.aa'}, track='all')

fig, axs = mdlhist_unable.plot_line('fxns.pass_hazard.s.hazards_mitigated',
                                'fxns.pass_hazard.s.hazards_propagated',
                                'flows.hazard.s.present',
                                'flows.hazard.s.percieved',
                                'flows.hazard.s.mitigated',
                            figsize=(10, 5), time_slice=[4])
../../_images/examples_asg_demo_Action_Sequence_Graph_74_0.png
[48]:
endstate_unable.graph.draw_from(4, mdlhist_unable)
[48]:
(<Figure size 1200x1000 with 1 Axes>, <Axes: title={'center': ' t=4'}>)
../../_images/examples_asg_demo_Action_Sequence_Graph_75_1.png
[49]:
fig, ax = endstate_unable['graph.fxns.detect_hazard.aa'].draw_from(4, mdlhist_unable)
../../_images/examples_asg_demo_Action_Sequence_Graph_76_0.png
[50]:
fig, ax = endstate_unable['graph.fxns.detect_hazard.aa'].draw_from(5, mdlhist_unable)
../../_images/examples_asg_demo_Action_Sequence_Graph_77_0.png
[ ]: