Behaviors
Behaviors define how an instance's state evolves over simulated time. A behavior is declared on the ontology — so every instance of the ontology carries its dynamics with it, in any model it is composed into.
The primary behavior construct is the state manager.
State Managers
A state manager pairs a declarative I/O specification with an implementation:
consumed— a SedaroQL query for the state the behavior readsproduced— a query naming the state the behavior writesoutputs— a query naming the state recorded to the simulation's output streamengine— the engine whose clock the behavior runs on- an implementation — a pure function from consumed values to produced values
@statemanager(
engine="gnc",
consumed="(length, prev!(angle), prev!(angleRate), prev!(timeStep as s))",
produced="(angle, angleRate)",
outputs="(angle, angleRate)",
)
def propagate_angle(length, theta, omega, step):
...
The queries are the language-level contract. They give the Simulation Compiler everything it needs to:
- type-check the behavior against the model, including unit conversions declared with
ascasts (timeStep as s); - schedule behaviors within and across engines based on true data dependencies (
prev!(...)marks reads of the previous frame, breaking cycles); - detect input changes efficiently and skip redundant work;
- record exactly the declared
outputsto the result stream.
Consuming and Producing State
Queries may reach across the model: a behavior can consume fields of related instances, aggregate over all instances of a type (Thruster.torque), or reference other agents. See the SedaroQL reference for the full expression grammar, including spreads, matches, and the reserved queries (time, timeStep, elapsedTime, startTime, stopTime).
Implementations Are Pluggable
The implementation attached to a state manager is a function reference, not part of the model's semantics. The same declared behavior can be implemented in Python or in compiled Rust, and swapped without touching the model — a key piece of SedaroML's language-agnosticism.
Implementations are expected to be pure: consumed values in, produced values out, no mutation of inputs and no hidden state. This is what makes simulations deterministic and reproducible (see Execution Semantics). For pseudo-randomness, seed deterministically via the seed!(...) query rather than global RNG state.
Engines
A model splits its simulation into named engines — e.g. gnc, power, thermal — each with its own internal clock that advances asynchronously from the others. Every state manager belongs to exactly one engine. A model's engine set is the union of the engines declared by behaviors across its whole tree of instances; engines exchange state with each other through the consumed/produced queries that cross them.
Execution of engines — scheduling, state exchange, determinism guarantees, interpolation — is the Simulator's responsibility. SedaroML's job is to declare which behaviors belong to which engine and what they read and write.
Time-Step Guidance
Ontologies can be opinionated about how fast their engine's clock may advance. A time-step manager is a special state manager that produces the engine's time step:
@time_step(engine="gnc")
def time_step() -> float:
return 0.1 # seconds
Time-step guidance may be dynamic — it can consume state like any other behavior (e.g. shrink the step during a burn, relax it during coast). Because the guidance lives on the ontology, a component enforces its own accuracy requirements in any model it is composed into.
Every engine used by a model must have time-step guidance; a model whose engine has no time-step manager is rejected before build.