API

Contents

This page documents the public API and the execution model used by Sirens. The Tutorial is a narrative introduction; this page is a reference for understanding the structure and interfaces.

Core concepts

A Sirens simulation has four layers:

  1. A component is an immutable description of a model and its configuration.
  2. A component integrator is the mutable runtime state created from a component via init(component).
  3. A SirenProblem stores components, connectors, the global time span, and component time scales.
  4. A SirenSolution stores selected variables at selected global times.

The typical workflow is:

prob = SirenProblem(components=..., connectors=..., tspan=...)
sol = solve(prob, alg; save_vars=..., saveat=...)

The convenience function solve is provided by CommonSolve and performs initialization followed by solving. For manual stepping, use init(prob, alg) to create a SirenIntegrator, then call step! repeatedly or solve! to run to completion.

Problems and algorithms

SirenProblem

Sirens.SirenProblemType
SirenProblem <: AbstractSirenProblem
SirenProblem(components, connectors, tspan, timescales=ones(length(components)))
SirenProblem(;
    components::Union{Tuple, Vector},
    connectors::Union{Tuple, Vector},
    tspan::Tuple{Float64, Float64},
    timescales::Vector{Float64}=ones(length(components)))

Defines a Sirens hybrid simulation problem.

Arguments

  • components::Union{Tuple, Vector}: Tuple or Vector of Components. Order is significant because it determines stepping order when multiple components can be stepped together. Component names must be unique. Using a tuple preserves type information for each element.
  • connectors::Union{Tuple, Vector}: Tuple or Vector of Connectors. Order is significant; connectors are applied in order, and later connectors can observe changes made by earlier ones. Using a tuple preserves type information for each element.
  • tspan::Tuple{Float64, Float64}: The time span of the simulation, from start to end time.
  • timescales::Vector{Float64}=ones(length(components)): Timescales for each component. For component i, global time is computed as t_global[i] = timescales[i] * t_local[i]. A component with timescale 0.1 advances ten local time units per one global time unit. This allows components using different time units to be connected in a single simulation.

Notes on Ordering

  • components order determines stepping priority when multiple components are ready.
  • connectors order is critical. Connectors are applied before component steps, in the order given. A connector is eligible only when every input is no later than every output in global time. Later connectors see changes made by earlier connectors. This is particularly important for setting #ids and #init_states in a DuplicatedComponent.
source

SirenIntegrator

Sirens.SirenIntegratorType
SirenIntegrator <: AbstractSirenIntegrator
SirenIntegrator(;
    integrators::Tuple,
    connectors::Tuple,
    tspan::Tuple{Float64, Float64},
    currtime::Float64,
    alg::AbstractSirenSolver,
    save_vars::Vector{<:Union{ConnectedVariable,AbstractString}},
    saveat::Union{Function, AbstractVector},
    timescales::Vector{Float64})

Created using init(prob::SirenProblem, alg::AbstractSirenSolver; save_vars=[]). All fields are considered internal.

source

Initialization and solving

CommonSolve.initMethod
init(prob::AbstractSirenProblem, alg::AbstractSirenSolver;
save_vars = nothing, saveat = nothing)

Defines the integrator for a Sirens hybrid simulation.

Arguments

  • prob::AbstractSirenProblem: The problem to be solved.
  • alg::AbstractSirenSolver: The Sirens solver algorithm to be used.
  • save_vars: Variables to be saved during the simulation. Options include:
    • nothing (default): Save all non-special variables (those not starting with '#').
    • :all: Save all variables, including special variables.
    • :none or String[]: Save no variables (time is still recorded).
    • Vector{String}: A vector of connected variable fullnames to save, including optional indices like "forest.life[1]" or "tree[1:10].life".
    • Tuple{Vararg{ConnectedVariable}}: A tuple of ConnectedVariable objects to save.
  • saveat: When to save the variables during the simulation. Options include:
    • nothing (default): Save after initialization and after every Sirens syncronisation event.
    • A number Δt: Save at times tspan[1]:Δt:tspan[2].
    • A vector of times: Save at exactly these time points.
    • A function (integrator, t) -> Bool: Save when it returns true (checked at scheduled stops).

Returns

  • SirenIntegrator: A mutable integrator ready for solving.
source
CommonSolve.solve!Function
solve!(sirenInt::AbstractSirenIntegrator)

Solves the problem using the SirenIntegrator by advancing it until the end of the time span, recording solutions according to the saveat configuration.

Arguments

  • sirenInt::AbstractSirenIntegrator: The integrator to be solved.

Returns

  • SirenSolution: The solution of the problem, containing saved times and states.

Behavior

  • Records initial state if saveat is satisfied at t=tspan[1].
  • Repeatedly calls step!(integrator) until currtime >= tspan[2].
  • Records state after each step if saveat is satisfied.
source
CommonSolve.step!Method
step!(int::AbstractSirenIntegrator)

Advance the state of the integrator int by one time step.

Arguments

  • int::Union{AbstractSirenIntegrator, AbstractComponentIntegrator}: The integrator to advance.
source

Solvers

Sirens.MinimumTimeStepperType
MinimumTimeStepper() <: AbstractSirenSolver

A solver that advances the SirenIntegrator by stepping to the next event.

Algorithm

The minimum stepper chooses the smallest upcoming value of (local_time + timestep) * timescale across all components. It then:

  1. Applies connectors whose input global times are no later than their output global times.
  2. Steps all components whose next local event reaches the new global time.
  3. Adjusts component times after stepping to mitigate floating-point roundoff accumulation.

Timestepping and Multirate Behavior

For component i with timescale s_i, the global time is t_global = s_i * t_local.

The minimum stepper guarantees that possible connection applications cannot be jumped over. If the method synchronized at different timepoints, a connection that could have been applied if time were treated continuously might be missed.

Connections

Connections use the most recently available state at each synchronization event. They do not interpolate between component states and do not guarantee identical local times across components.

See also Connector.

source

Connections

Connected variables

Sirens.ConnectedVariableType
ConnectedVariable <: AbstractConnectedVariable

Points to a variable within a component.

Fields

  • component::String: Name of the component.
  • variable::String: Name of the variable.
  • variableindex::Union{Nothing,Vector{Int}}: Index or range for the variable, if applicable.
  • duplicatedindex::Union{Nothing,Vector{Int}}: Index for duplicated components, if applicable.
source
Sirens.ConnectedVariableMethod
ConnectedVariable(name::AbstractString)

Construct a ConnectedVariable from its canonical fullname.

Arguments

  • name::AbstractString: The full variable name.

Syntax and Examples

Connected variables are specified as strings in one of these forms:

SyntaxMeaning
component.variableA variable in a component
component.variable[i]An index into a variable
component[j].variableInstance j of a duplicated component
component[j].variable[i]Both kinds of indexing

Examples

  • ConnectedVariable("comp.var"): Variable var in component comp.
  • ConnectedVariable("comp.var[1:5]"): Variable indices 1 through 5 (variable index).
  • ConnectedVariable("comp[2].var"): Variable var from duplicated instance 2 (duplicated index).
  • ConnectedVariable("comp[1:3].var[4]"): Variable index 4 from duplicated instances 1-3.

Parsing

Indices are parsed as Julia expressions, so use literal integer indices and ranges:

  • 1 for a single index.
  • 1:5 for a range.
  • [1, 3] for a vector of indices.
source
Base.fullnameFunction
fullname(var::AbstractConnectedVariable)

Return the full name of a ConnectedVariable as a string.

Arguments

  • var::AbstractConnectedVariable: The connected variable to get the full name for.

Returns

  • String: The full name of the connected variable.
source

Connectors

Sirens.ConnectorType
Connector <: AbstractConnector

Represents a connection between multiple ConnectedVariables, possibly with a transformation function.

Fields

  • inputs::Tuple{<:AbstractConnectedVariable}: Input variables for the connector.
  • outputs::Tuple{<:AbstractConnectedVariable}: Output variables for the connector.
  • func::Union{Nothing,Function}: Optional function to transform inputs to outputs.
source
Sirens.ImplicitConnectorType
ImplicitConnector(; inputs::Vector{T}, outputs::Vector{S}) where {T<:AbstractString} where {S<:AbstractString}

Construct an ImplicitConnector from string names for inputs and outputs.

Implicit connectors are only used for algebraic loop detection and are not executed during the simulation.

Arguments

  • inputs::Vector{<:AbstractString}: Names of input variables.
  • outputs::Vector{<:AbstractString}: Names of output variables.
source
Sirens.runconnectionFunction
runconnection(sirenInt::AbstractSirenIntegrator, conn::AbstractConnector)

Extract all the input states from sirenInt, apply the connection function, and return the output.

Arguments

  • sirenInt::AbstractSirenIntegrator: The Sirens integrator containing the components.
  • conn::AbstractConnector: The connector defining the connection.
source
Sirens.runconnection!Function
runconnection!(sirenInt::AbstractSirenIntegrator, conn::AbstractConnector)

Extract all input states from sirenInt, apply the connection function, and set output states in sirenInt.

Arguments

  • sirenInt::AbstractSirenIntegrator: The Sirens integrator containing components.
  • conn::AbstractConnector: The connector defining inputs, outputs, and transformation.

Behavior

  1. Calls runconnection to compute outputs.
  2. Sets each output value in the corresponding component via setstate!.
  3. Outputs are set in order; later outputs can depend on earlier ones if they share state.
source

Timestepping and multirate solving

SirenProblem accepts one timescales value per component. For component i with timescale $s_i$:

\[t_{global,i} = s_i \cdot t_{local,i}\]

A component with $s_i = 0.1$ advances ten local time units per one global time unit.

See the MinimumTimeStepper docstring for details on the stepping algorithm.

Saving and solutions

Saving options

Saving configuration is passed during init or solve:

CommonSolve.initMethod
init(prob::AbstractSirenProblem, alg::AbstractSirenSolver;
save_vars = nothing, saveat = nothing)

Defines the integrator for a Sirens hybrid simulation.

Arguments

  • prob::AbstractSirenProblem: The problem to be solved.
  • alg::AbstractSirenSolver: The Sirens solver algorithm to be used.
  • save_vars: Variables to be saved during the simulation. Options include:
    • nothing (default): Save all non-special variables (those not starting with '#').
    • :all: Save all variables, including special variables.
    • :none or String[]: Save no variables (time is still recorded).
    • Vector{String}: A vector of connected variable fullnames to save, including optional indices like "forest.life[1]" or "tree[1:10].life".
    • Tuple{Vararg{ConnectedVariable}}: A tuple of ConnectedVariable objects to save.
  • saveat: When to save the variables during the simulation. Options include:
    • nothing (default): Save after initialization and after every Sirens syncronisation event.
    • A number Δt: Save at times tspan[1]:Δt:tspan[2].
    • A vector of times: Save at exactly these time points.
    • A function (integrator, t) -> Bool: Save when it returns true (checked at scheduled stops).

Returns

  • SirenIntegrator: A mutable integrator ready for solving.
source

SirenSolution

Sirens.SirenSolutionType
SirenSolution{X, Y<:SirenSolutionData} <: AbstractSirenSolution

Stores the solution of a SirenProblem over time.

Fields

  • t::X: Time points at which the solution is saved.
  • u::Y<:SirenSolutionData: A dictionary-like structure storing the saved states for each variable in the problem.

Interpolation

A solution can be interpolated at arbitrary times using callable syntax:

(sol::AbstractSirenSolution)(t::Real)

This returns a new SirenSolution with interpolated states at time t.

Interpolation Rules:

  • For numeric states and numeric arrays: Uses linear interpolation between saved time points.
  • For non-numeric states (e.g., Agents.jl models, objects): Uses constant interpolation (returns the state from the last saved time point before or at t).

The time t must be within [sol.t[1], sol.t[end]], otherwise a BoundsError is thrown.

Examples

sol(2.5)  # Interpolate solution at time t=2.5
source

A solution has sol.t (saved global times) and sol.u (dictionary mapping variable names to saved states).

Solution indexing and interpolation

Base.getindexMethod
Base.getindex(sol::AbstractSirenSolution, var::AbstractString)
Base.getindex(sol::AbstractSirenSolution, var::AbstractConnectedVariable)
Base.getindex(sol::AbstractSirenSolution, index::Int)

Get the solution for a variable var or at a time index index from a SirenSolution.

Arguments

  • sol::AbstractSirenSolution: The solution object.
  • var::Union{AbstractString, AbstractConnectedVariable}: The variable name, optionally with indices like "comp.var[1:3]" or "comp[2].var[4]".
  • index::Int: The time index (1-based) into the saved times.

Returns

  • If var is provided, returns a vector of saved states for that variable across all times.
  • If index is provided, returns a new SirenSolution containing only the data at that time index for each variable.

Examples

sol["comp.var"]        # All saved states for variable "comp.var"
sol[ConnectedVariable("comp[1].var")]  # States for duplicated instance 1
sol[3]                 # Solution data at the 3rd saved time point
source

See the SirenSolution docstring for details on interpolation behavior, including handling of numeric vs. non-numeric states.

Components

All time-dependent components expose name, timestep, variables, init, step!, getstate, setstate!, gettime, and settime! through the common interface. Each component type documents its special variables and specific behavior in its docstring.

Differential Equations Components

Sirens.DEComponentType
DEComponent(model::DiffEqBase.AbstractDEProblem, alg;
            name::String="DE", timestep::Float64=1.0, intkwargs::NamedTuple=(;),
            state_names::Dict{String,Any}=Dict{String,Any}())
DEComponent(model::DiffEqBase.AbstractDEProblem; kwargs...)

A Sirens component that wraps a SciML Differential Equations problem (ODEProblem, DAEProblem, etc).

Arguments

  • model::DiffEqBase.AbstractDEProblem: The SciML Differential Equations problem (e.g., ODEProblem, etc.)
  • alg: Algorithm from DifferentialEquations.jl to be used for solving the DEProblem. If no algorithm is provided, the algorithm will be automatically chosen by DifferentialEquations.jl.

Keyword Arguments

  • name::AbstractString: Name of the component. Defaults to "DE".
  • timestep::Real: Time step for the component. Defaults to 1.0.
  • intkwargs: Additional keyword arguments for the DE solver. Defaults to no keywords.
  • state_names: Dictionary mapping variable names (as strings) to their corresponding indices in the state vector or symbols from Symbolics.jl. Defaults to an empty dictionary. Map strings like "x" to indices (1, 2, ...) or symbolic variables.

Special Variables

  • #time: The current time (integrator.t).
  • #state: The full state vector (integrator.u).
  • #integrator: The underlying DifferentialEquations.jl integrator object.

Examples

function f!(du, u, p, t)
    du[1] = -u[1]
end
prob = ODEProblem(f!, [1.0], (0.0, 10.0))
comp = DEComponent(prob, Tsit5(); name="ode_comp",
                   state_names=Dict("x" => 1))
source

Agents Components

Sirens.AgentsComponentType
AgentsComponent(model::StandardABM; name="Agents Component",
                state_names=Dict{String,Any}(), timestep::Real=1.0)

A Sirens component that wraps an agent-based model (ABM) using the Agents.jl package.

Arguments

  • model::StandardABM: The agent-based model to be solved.

Keyword Arguments

  • name::AbstractString: The name of the component. Defaults to "Agents".
  • state_names: A dictionary mapping variable names (as strings) to their corresponding properties (agent properties or model properties) in the model. Defaults to an empty dictionary. Values can be agent properties (accessed per agent) or model properties.
  • timestep::Real=1: The time step for the component (not the ABM solver timestep), i.e. how frequently should the inputs and outputs be updated (in units of abmtime(model)). For example, if timestep=5, the component will step the ABM 5 times for every synchronization, and set #time=5.

Special Variables

  • #time: The component clock (independent from abmtime(model)).
  • #model: The current StandardABM object (read-only; use getstate with copy=true to get a copy).
  • #ids: The vector of all current agent IDs (read-only; cannot be used with setstate!).

state_names Semantics

  • A key without a variable index accesses agent properties for all agents or model properties.
  • A key with a variable index (e.g., `"comp.var[1:5]"") accesses specific agent IDs.
  • Since the variableindex is used for accessing the properties of particular agents, use a connector function to index into complex properties.

Examples

comp = AgentsComponent(model;
    name="abm_comp",
    state_names=Dict("x" => :pos_x, "y" => :pos_y))
source

MethodOfLines Components

Sirens.MOLComponentType
MOLComponent(model::DiffEqBase.AbstractDEProblem, alg::DiffEqBase.AbstractDEAlgorithm;
             name::String="MOL", timestep::Real=1.0, intkwargs::NamedTuple=(;),
             state_names::Dict{String,Any}=Dict{String,Any}())

A Sirens component that wraps a Method of Lines discretized PDE as a SciML DifferentialEquations problem.

Arguments

  • model::DiffEqBase.AbstractDEProblem: The SciML Differential Equations problem (e.g., ODEProblem, etc.) from MethodOfLines discretization.
  • alg::DiffEqBase.AbstractDEAlgorithm: Algorithm from DifferentialEquations.jl to be used for solving the DEProblem.

Keyword Arguments

  • name::AbstractString: Name of the component. Defaults to "MOL".
  • timestep::Real: Time step for the component. Defaults to 1.0.
  • intkwargs: Additional keyword arguments for the DE solver. Defaults to no keywords.
  • state_names: Dictionary mapping variable names (as strings) to their corresponding indices in the state vector or symbols from Symbolics.jl. Defaults to an empty dictionary. For PDEs with spatial discretization, map logical variable names (e.g., "concentration") to state vector indices or ranges.

Special Variables

  • #time: The current time (integrator.t).
  • #state: The full discretized state vector (integrator.u).
  • #integrator: The underlying DifferentialEquations.jl integrator object.

Notes

MOLComponent is useful for connecting discretized PDEs to other models. When mapping between different spatial grids or resolutions (e.g., PDE grid to agent positions), use a connector function to perform interpolation or other spatial transformations.

source

JumpProcesses Components

Sirens.JumpComponentType
JumpComponent(model::JumpProblem, alg;
            name::String="Jump", timestep::Float64=1.0, intkwargs::NamedTuple=(;),
            state_names::Dict{String,Any}=Dict{String,Any}())

A Sirens component that wraps a JumpProcesses.jl jump process problem.

Arguments

  • model::JumpProblem: SciML Jump problem containing a continuous ODE and jump events.
  • alg: Algorithm from DifferentialEquations.jl to be used for solving the JumpProblem.

Keyword Arguments

  • name::AbstractString: Name of the component. Defaults to "Jump".
  • timestep::Real: Time step for the component. Defaults to 1.0.
  • intkwargs: Additional keyword arguments for the Jump solver. Defaults to no keywords.
  • state_names: Dictionary mapping variable names (as strings) to their corresponding indices in the state vector or symbols from Symbolics.jl. Defaults to an empty dictionary.

Special Variables

  • #time: The current time (integrator.t).
  • #state: The full state vector (integrator.u).
  • #integrator: The underlying DifferentialEquations.jl integrator object.
source

TrixiParticles Components

Sirens.TrixiParticlesComponentType
TrixiParticlesComponent(semi::TrixiParticles.Semidiscretization, alg;
            name::String="TrixiParticles", timestep::Float64=1.0,
            intkwargs::NamedTuple=(;), tspan=(0.0, Inf),
            state_names::Dict{String,Any}=Dict{String,Any}())

A Sirens component that wraps a TrixiParticles.jl particle method simulation.

Arguments

  • semi::TrixiParticles.Semidiscretization: TrixiParticles semidiscretization object.
  • alg: Algorithm from DifferentialEquations.jl or TrixiParticles.jl to solve the DynamicalODEProblem.

Keyword Arguments

  • name::AbstractString: Name of the component. Defaults to "TrixiParticles".
  • timestep::Real: Time step for the component. Defaults to 1.0.
  • intkwargs: Additional keyword arguments for the DE solver. Defaults to no keywords.
  • tspan: Time span for the simulation. Defaults to (0.0, Inf).
  • state_names: Dictionary mapping variable names (as strings) to their corresponding indices in the state vector or symbols from Symbolics.jl. Defaults to an empty dictionary. Typically maps particle positions, velocities, or properties.

Special Variables

  • #time: The current time (integrator.t).
  • #state: The full particle state vector (integrator.u).
  • #integrator: The underlying DifferentialEquations.jl integrator object.
  • #semi: The TrixiParticles semidiscretization object (read-only; cannot be used with setstate!).
source

Surrogate Components

Sirens.SurrogateComponentType
SurrogateComponent(args...; kwargs...)

Represents a component that is replaced with a surrogate in the simulation, speeding up computation of a complex step! function.

Arguments

  • component::AbstractTimeDependentComponent: The original component to be replaced with a surrogate.
  • surrogate: The surrogate model or method to use for the component.
  • lower_bound: Lower bounds for each state variable for surrogate sampling.
  • upper_bound: Upper bounds for each state variable for surrogate sampling.

Keyword Arguments

  • name::AbstractString: Name of the component. Defaults to the same as the original component.
  • timestep::Real: Time step for the component. Defaults to the same as the original component.
  • model: A Flux.jl model to use as the surrogate. If nothing, a default feedforward neural network is created.
  • state_names: Dictionary mapping variable names (as strings) to their corresponding indices in the state vector or symbols from ModelingToolkit/Symbolics. Defaults to the same as the original component.
  • n_samples::Integer: Number of samples to use for training the surrogate. Defaults to 1000.
  • n_epochs::Integer: Number of training epochs for the surrogate. Defaults to 1000.
source

Duplicated Components

Sirens.DuplicatedComponentType
DuplicatedComponent <: AbstractComponent

Represents a component that is duplicated in the simulation, allowing a single component to have multiple states.

Fields

  • component::AbstractTimeDependentComponent: The original component to be duplicated.
  • instances::Union{Int,Nothing}: Number of instances of the component. If nothing, then the number is variable and determined by the simulation.
  • name::String: Name of the duplicated component.
  • init_states::Vector: Vector of states for the duplicated component, where each state corresponds to a particular instance.
source

Time-independent Components

Sirens.TimeIndependentComponentType
TimeIndependentComponent(name::String, func::Function, initial_state)

TimeIndependentComponent represents a component that does not evolve in time, but instead computes its state based on its inputs, irregardless of the change in time.

Arguments

  • name::String: The name of the component.
  • func::Function: The function that computes the component's state based on its inputs. It should take a single input of the same type as initial_state.
  • initial_state: The initial state of the component. This should be a valid input to func.
source

Abstract Types

These abstract types are extension points and are useful for method signatures:

User-Defined Components

One of the main goals of Sirens is to make it simple and accessible to expand functionality with user-defined components. This is done through the component interface—a set of functions that can be implemented to support all of Sirens functionality.

Core interface functions

Sirens.nameFunction
name(int::AbstractComponentIntegrator)
name(comp::AbstractComponent)

Get the name of the integrator or component.

source
Sirens.timestepFunction
timestep(int::AbstractComponent)
timestep(comp::AbstractComponentIntegrator)

Get the proposed time step of the integrator or component. It can depend on the current state.

source
Sirens.variablesFunction
variables(comp::AbstractComponentIntegrator)
variables(comp::AbstractComponent)

Retrieve the variable names of a component.

Arguments

  • comp::Union{AbstractComponent, AbstractComponentIntegrator}: The component (or component integrator) whose variable names are to be retrieved.

Returns

  • A collection of variable names (as strings) associated with the component. This includes all special variables such as #time and #model if applicable.
source
Sirens.getstateFunction
getstate(comp::AbstractComponentIntegrator; copy = false)
getstate(comp::AbstractComponentIntegrator, key; copy = false)

Retrieve the state of a component.

Arguments

  • comp::AbstractComponentIntegrator: The component whose state is to be retrieved.
  • key: The key specifying which part of the component's state to retrieve.

Keyword Arguments

  • copy::Bool: If true, a deep copy of the state is returned; otherwise, a reference to the state is returned (assuming the state is mutable).
source
Sirens.setstate!Function
setstate!(comp::AbstractComponentIntegrator, state)
setstate!(comp::AbstractComponentIntegrator, key, value)

Set the state of a component.

Arguments

  • comp::AbstractComponentIntegrator: The component whose state is to be set.
  • state: The new state to set for the entire component.
  • key: The key specifying which part of the component's state to set.
  • value: The value to set for the specified part of the component's state.
source
Sirens.gettimeFunction
gettime(sirenInt::AbstractComponentIntegrator)

Get the current time of the integrator.

Arguments

  • int::AbstractComponentIntegrator: The integrator whose time is to be retrieved.

Returns

  • The current time of the integrator.
source
Sirens.settime!Function
settime!(sirenInt::AbstractComponentIntegrator, t)

Set the current time of the integrator.

Arguments

  • int::AbstractComponentIntegrator: The integrator whose time is to be set.
  • t: The time to set.
source
CommonSolve.step!Method
step!(int::AbstractComponentIntegrator)

Advance the state of the integrator int by one time step.

Arguments

  • int::AbstractComponentIntegrator: The integrator to advance.
source

Implementing a custom component

To implement a custom component:

  1. Define an immutable struct MyComponent to hold the configuration.
  2. Define a mutable struct MyComponentIntegrator to hold runtime state.
  3. Implement init(::MyComponent) to return a MyComponentIntegrator.
  4. Implement step!(integrator::MyComponentIntegrator) to advance the state.
  5. Implement name, timestep, and variables for both structs.
  6. Implement getstate and setstate! to expose your component's variables.
  7. Implement gettime and settime! to expose your component integrator's current time.

While there are quite a few functions here that need to be defined, the majority of them have default behaviour that you can opt in to.

  • name(integrator), timestep(integrator), and variables(integrator) can use the default behavior of name(integrator) = name(integrator.component) (and other equivalents) if the component is stored as a property of the integrator.
  • timestep(component) and name(component) can use the default behaviour of component.timestep and component.name, if the property or field exists.
  • gettime and settime! will default to attempting to access the #time special variable of getstate and setstate!.
  • getstate and setstate! do not need to implement any of the keyword argument features (i.e. copy).