The Simulation Lifecycle¶
The life cycle of a
vivarium simulation is a representation of
the different execution states and their transitions. An execution state
is a clearly delineated execution period during the simulation around which
we build and enforce concrete programmatic contracts. These states
can be grouped into five important phases. The phases are closely related
groups of execution states. Contracts are not enforced directly around
phases, but they are a useful tool for thinking about the execution flow
during a simulation.
Arguments passed from the simulation
entry point are parsed and core
framework systems are initialized.
Components register themselves with simulation services and
request access to resources by interacting with the simulation
builder; the initial population is created.
The core logic (as encoded in the simulation components) is executed.
The population state is finalized and results are tabulated.
The simulation itself maintains a formal representation of its internal
execution state using the tools in the
module. The tools allow the simulation to make concrete contracts about flow
of execution in simulations and to constrain the availability of certain
framework services to particular life cycle states. This makes error handling
much more robust and allows users to more easily reason about complex
Make a graph of service availability in the simulation.
The Bootstrap Phase¶
The bootstrap and initialization phases look like an atomic operation to an external user. Bootstrap only exists as a separate phase because certain operations must take place before the internal representation of the simulation life cycle exists.
During bootstrap, all user input arguments are parsed into an internal representation of the simulation plugins, components, and configuration. The internal plugin representation is then parsed into the simulation managers, the set of private and public services used to build and run simulations. Finally, the formal representation of the simulation lifecycle is constructed and the initialization phase begins.
The Initialization Phase¶
Two important things happen here:
- The internal representation of the simulation components is parsed into python import paths and all components are instantiated and registered with the component manager.
- The internal representation of the configuration is updated with all component configuration defaults.
At this point, all input arguments have been parsed, all components have been instantiated and registered with the framework, and the configuration is effectively complete. In an interactive setting, this is a useful phase in the simulation life cycle because you can add locally created components and modify the configuration.
The Setup Phase¶
The setup phase is broken down into three life cycle states.
The first state is named the same as the phase and is where the bulk of the
phases work is done. During the setup state, the simulation managers and then
the simulation components will have their
setup method called with
the simulation builder as an argument. The
builder allows the components to request services like
randomness or views into the
population state table or to register themselves
with various simulation subsystems. Setting up components may also involve
loading data, registering or getting pipelines,
creating lookup tables, and registering
population initializers, among other things.
The specifics of this are determined by the
setup method on each component
- the framework itself simply calls that method with a
This is a short state that exists in the simulation mainly so that framework
managers can coordinate shared state and do any necessary
cleanup. This is the first actual event emitted by
the simulation framework. Normal
should never listen for this event. This may be enforced at a later date.
It’s not until this stage that the framework actually generates the base population for the simulation. Here, the framework rewinds the simulation clock one time step and generates the population. This time step fence-posting ensures that simulants enter the simulation on the correct start date. Note that this rewinding of the clock is purely what it sounds like - there is no concept of a time step being taken here. Instead, the clock is literally reset back the duration of one time step. Once the simulant population is generated, the clock is reset to the simulation start time, again by changing the clock time only without any time step being taken.
The Main Event Loop¶
At this stage, all the preparation work has been completed and the framework begins to move through the simulation. This occurs as an event loop. Like the the setup phase, the main loop phase is broken into a series of simulation states. The framework signals the state transitions by emitting a series of events for each time step:
- time_step__prepare A state in which simulation components can do any work necessary to prepare for the time step.
- time_step The phase in which the bulk of the simulation work is done. Simulation state is updated.
- time_step__cleanup A phase for simulation components to do any post time step cleanup.
- collect_metrics A life-cycle phase specifically reserved for computing and recording simulation outputs.
By listening for these events, individual components can perform actions, including manipulating the state table. This sequence of events is repeated until the simulation clock passes the simulation end time.
We have multiple sources of time during this process. The
vivarium.framework.engine.SimulationContext itself holds onto a
clock. This simulation clock is the actual time in the simulation. Events
(including e.g., time_step) come with a time as well. This time is the
time at the start of the next time step, that is, the time when any changes
made during the loop will happen.
The Simulation End Phase¶
The final phase in the simulation life cycle is fittingly enough, simulation end. It is split into two states. During the first, the simulation_end event is emitted to signal that the event loop has finished and the state table is final. At this point, final simulation outputs are safe to compute. The second state is report in which the simulation will accumulate all final outputs and return them.