Skip to content

SciML flows

The CTFlowsSciMLFlows extension (activated by loading SciMLBase, e.g. through any OrdinaryDiffEq solver package) lets Flows.Flow consume SciML objects directly:

  • Flow(f::SciMLBase.AbstractODEFunction) — wraps the function in a SciMLFunctionSystem and runs the standard pipeline, producing a StateFlow.

  • Flow(prob::SciMLBase.AbstractODEProblem) — wraps the fully-assembled problem in a SciMLProblemFlow, bypassing the system-building pipeline (the problem already carries u0, tspan and p).

This is the bridge for users who already have SciML code and want the CTFlows call interface (point/trajectory styles, configs, multi-phase concatenation).


From an ODEFunction

julia
f = SciMLBase.ODEFunction((du, u, p, t) -> du .= -p .* u)
flow = Flows.Flow(f; reltol=1e-10)
Flow
├─ system: SciMLFunctionSystem
          ├─ wraps: ODEFunction: non-autonomous, variable, in-place
          └─ rhs: IPSciMLIpRHS (in-place SciML → in-place interface)
└─ integrator: SciML (reltol = 1.0e-10)

The resulting flow behaves like any StateFlow. SciML functions receive their parameter through p, which CTFlows maps to the variable keyword:

julia
julia> xf = flow(0.0, [1.0], 1.0; variable=2.0)   # ≈ exp(-2)
1-element Vector{Float64}:
 0.13533528337361272

Trajectory calls work the same way, returning a VectorFieldTrajectory — plot it directly once Plots is loaded:

julia
sol = flow((0.0, 1.0), [1.0]; variable=2.0)
VectorFieldTrajectory
├─ result: SciMLIntegrationResult
├─ tspan: (0.0, 1.0)
├─ time points: 28
├─ final state: [0.13533528337361272]
└─ variable: 2.0
julia
plot(sol)

Traits of SciML-backed flows

A SciML function has the uniform (du, u, p, t) signature, so the wrapped flow is always NonAutonomous / NonFixed: pass the parameter via variable at call time.


From an ODEProblem

A problem already bundles the dynamics, initial condition, time span, and parameter. Flows.Flow wraps it in a SciMLProblemFlow:

julia
prob = SciMLBase.ODEProblem((du, u, p, t) -> du .= -p .* u, [1.0], (0.0, 1.0), 2.0)
pflow = Flows.Flow(prob; reltol=1e-10)
SciMLProblemFlow
├─ tspan: (0.0, 1.0)
├─ u0: [1.0]
└─ integrator: SciML (reltol = 1.0e-10)

SciMLProblemFlow supports three call modes:

julia
julia> result = pflow();                       # no-arg: solve the problem as-is

julia> Integrators.final_state(result)
1-element Vector{Float64}:
 0.13533528337361272

julia> xf = pflow(0.5, [2.0], 2.0; variable=3.0)   # point call: remake, final state
1-element Vector{Float64}:
 0.02221799324740262

julia> result = pflow((0.5, 2.0), [2.0]; variable=3.0);  # trajectory call: remake, full result

julia> Integrators.times(result)[end]
2.0

Point and trajectory calls go through SciMLBase.remake, replacing u0, tspan and (when variable is given) p before solving. The no-arg and trajectory calls return an AbstractIntegrationResult — use Integrators.times, Integrators.evaluate_at and Integrators.final_state to inspect it (see Trajectories).


See also