Workflow Steps#

An ActivitySim component is written as a Python function with a @workflow.step decorator:

import pandas as pd
from activitysim.core import workflow

@workflow.step
def component_name(
    state: workflow.State,
    named_temp_table: pd.DataFrame,
    named_table: pd.DataFrame,
    cached_object: bool = False,
) -> None:
    ... # do something

Similar to a typical Python class method, the first argument to a workflow step must be a reference to a State object named state. Unlike a typical Python class method, this is rigorously enforced – if you decorate a function as a workflow.step and the first argument is not named state a TypeError will be raised.

Similar to the legacy ORCA-based implementation of ActivitySim, when called by the automated processes that orchestrate component execution, the names of all subsequent arguments should generally match objects that are expected to be already stored as keys in the state context, or have decorated constructors declared elsewhere in the imported codebase. However, if an argument is provided with a default value, then the default value is used unless it is explicitly overloaded in the function call; i.e. the default value in the function signature takes precedence over any value stored in the state’s context.

Unlike typical Python functions, the type annotations for the decorated function’s arguments are not totally ignored, at least when the function is called via the State.run mechanisms. When asking for a data table to be provided, the type annotation is respected if it is pandas.DataFrame or xarray.Dataset; the caller will receive the table in the indicated format.

The decorator will spin off a reference of the decorated function in the _RUNNABLE_STEPS class attribute for State, facilitating the automatic discovery and/or execution of this function via the State.run mechanisms. The original function also remains available to import and use without changes.

The decorated function may mutate the state argument by adding or removing things from the state’s context. Most existing workflow steps operate in this manner. The return type annotation can be given as “None” to flag that this mutation behavior is indeed baked in to the decorated function – indeed, by implication it must be as there is no other pathway to output a result, although that is not otherwise checked.

Alternatively, the wrapped function can return a Mapping[str, Any] that will be used to update the state’s context. This happens automatically when the step is called via the State.run accessor, or can (must) be handled separately by the caller if the function is executed directly. (Future work may migrate ActivitySim to favor or require this “pure” function behavior.)

API#

step([wrapped_func, step_name, cache, kind, ...])

Decorator for ActivitySim model components and related functions.