Building a flow
A flow is a callable that integrates a system from an initial condition to a final time. Building one involves three steps:
Data → build_system → build_integrator → build_flow → FlowThe shortcut constructor Flows.Flow(data; opts...) collapses all three steps into one call. This page explains both paths.
Shortcut: Flows.Flow
The simplest way to build a flow is to pass data directly to Flows.Flow:
# From a VectorField → StateFlow
vf = Data.VectorField(x -> -x)
flow = Flows.Flow(vf; reltol=1e-8, abstol=1e-8)Flow
system: VectorFieldSystem
wraps: VectorField: autonomous, fixed (no variable), out-of-place
rhs: IPVFOoPRHS (out-of-place VF → in-place interface)
integrator: SciML (abstol = 1.0e-8, reltol = 1.0e-8)# From a HamiltonianVectorField → HamiltonianFlow
hvf = Data.HamiltonianVectorField((x, p) -> (p, -x))
hflow = Flows.Flow(hvf; reltol=1e-10)Flow
system: HamiltonianVectorFieldSystem
wraps: HamiltonianVectorField: autonomous, fixed (no variable), out-of-place
integrator: SciML (reltol = 1.0e-10)using LinearAlgebra
# From a scalar Hamiltonian (AD computes the derivatives) → HamiltonianFlow
import DifferentiationInterface, ForwardDiff
h = Data.Hamiltonian((x, p) -> 0.5 * (dot(x, x) + dot(p, p)))
hflow_ad = Flows.Flow(h; reltol=1e-10)Flow
system: HamiltonianSystem
time_dependence: CTBase.Traits.Autonomous
variable_dependence: CTBase.Traits.Fixed
hamiltonian: Hamiltonian: autonomous, fixed (no variable)
natural call: h(x, p)
uniform call: h(t, x, p, v)
backend: DifferentiationInterface(ad_backend=AutoForwardDiff())
integrator: SciML (reltol = 1.0e-10)Options passed as keyword arguments are forwarded to the integrator strategy; see Integrating for the full list.
Explicit pipeline
The explicit form gives full control over each step.
Step 1 — Build the system
build_system wraps data into an AbstractSystem that exposes the ODE right-hand side:
# VectorField → VectorFieldSystem
sys = Systems.build_system(vf)VectorFieldSystem
wraps: VectorField: autonomous, fixed (no variable), out-of-place
rhs: IPVFOoPRHS (out-of-place VF → in-place interface)# HamiltonianVectorField → HamiltonianVectorFieldSystem
hsys = Systems.build_system(hvf)HamiltonianVectorFieldSystem
wraps: HamiltonianVectorField: autonomous, fixed (no variable), out-of-placeA system exposes:
Systems.rhs(sys)— the ODE right-hand side function(du, u, p, t) -> …Traits.time_dependence(sys),Traits.variable_dependence(sys)— delegated from the data
Step 2 — Build the integrator
integ = Integrators.build_integrator(; reltol=1e-8, abstol=1e-8)SciML (instance, id=:sciml)
├─ internalnorm = real_norm [default]
├─ alg = Tsit5{typeof(OrdinaryDiffEqCore.trivial_limiter!), typeof(OrdinaryDiffEqCore.trivial_limiter!), FastBroadcast.Serial}(OrdinaryDiffEqCore.trivial_limiter!, OrdinaryDiffEqCore.trivial_limiter!, FastBroadcast.Serial()) [default]
├─ reltol = 1.0e-8 [user]
├─ save_everystep = auto [default]
├─ abstol = 1.0e-8 [user]
├─ save_start = auto [default]
└─ dense = auto [default]
Tip: use describe(SciML) to see all available options.
The default integrator is SciML (backed by OrdinaryDiffEqTsit5 when loaded). See Integrating for how to choose a different algorithm.
Step 3 — Combine into a flow
flow_explicit = Flows.build_flow(sys, integ)Flow
system: VectorFieldSystem
wraps: VectorField: autonomous, fixed (no variable), out-of-place
rhs: IPVFOoPRHS (out-of-place VF → in-place interface)
integrator: SciML (abstol = 1.0e-8, reltol = 1.0e-8)This produces the same StateFlow as the shortcut:
typeof(flow_explicit) == typeof(flow)trueFlow types
| Input data | Flow type |
|---|---|
VectorField | StateFlow |
HamiltonianVectorField | HamiltonianFlow |
Hamiltonian (with AD) | HamiltonianFlow |
Both StateFlow and HamiltonianFlow are concrete subtypes of AbstractFlow. Their trait parameters mirror the underlying data:
Traits.time_dependence(flow) # Autonomous (inherited from vf)
Traits.variable_dependence(flow) # FixedCTBase.Traits.FixedAbstractSystem contract
Any concrete system must implement:
rhs(system)— returns an ODE function(du, u, p, t) -> nothing
Traits are propagated automatically from the data layer:
Traits.time_dependence(system)Traits.variable_dependence(system)
AbstractFlow contract
Any concrete flow must implement:
system(flow)— the associatedAbstractSystemintegrator(flow)— the associatedAbstractIntegrator
Calling a flow delegates through _invoke_flow which builds the ODE problem, solves it, and wraps the result — see Integrating.
Hamiltonian vector field getter
For a HamiltonianFlow, you can retrieve the underlying HamiltonianVectorField at any time:
# HamiltonianVectorField-backed flow
hvf_back = Flows.hamiltonian_vector_field(hflow)HamiltonianVectorField: autonomous, fixed (no variable), out-of-place
natural call: f(x, p)
uniform call: f(t, x, p, v)For an AD-backed flow (built from Hamiltonian), the getter materialises the vector field on demand:
hvf_ad = Flows.hamiltonian_vector_field(hflow_ad)HamiltonianVectorField: autonomous, fixed (no variable), out-of-place
natural call: f(x, p)
uniform call: f(t, x, p, v)See also
CTFlows.Flows.Flow,CTFlows.Flows.StateFlow,CTFlows.Flows.HamiltonianFlow— concrete flow types.CTFlows.Flows.build_flow,CTFlows.Systems.build_system,CTFlows.Integrators.build_integrator— pipeline builders.CTFlows.Flows.AbstractFlow,CTFlows.Flows.AbstractStateFlow,CTFlows.Flows.AbstractHamiltonianFlow— abstract supertypes.CTFlows.Flows.system,CTFlows.Flows.integrator— flow accessors.CTFlows.Systems.AbstractSystem,CTFlows.Systems.rhs— system contract.