Integrating
Once a flow is built, calling it integrates the underlying ODE. There are two call styles depending on whether you need the full trajectory or just the final state.
Call styles
Point integration — final state only
flow(t0, x0, tf) # returns xf::Vector (StateFlow)
hflow(t0, x0, p0, tf) # returns (xf, pf) (HamiltonianFlow)x0 = [1.0, 0.0]
xf = flow(0.0, x0, 1.0)2-element Vector{Float64}:
0.36787944127643124
0.0p0 = [0.0, 1.0]
xf, pf = hflow(0.0, x0, p0, 1.0)
(xf, pf)([0.5403023057842606, 0.8414709847533869], [-0.8414709847533869, 0.5403023057842606])Trajectory integration — full time history
flow((t0, tf), x0) # returns VectorFieldTrajectory
hflow((t0, tf), x0, p0) # returns HamiltonianVectorFieldTrajectorysol = flow((0.0, 1.0), x0)VectorFieldTrajectory
├─ result: SciMLIntegrationResult
├─ tspan: (0.0, 1.0)
├─ time points: 16
└─ final state: [0.36787944127643124, 0.0]hsol = hflow((0.0, 1.0), x0, p0)HamiltonianVectorFieldTrajectory
├─ result: SciMLIntegrationResult
├─ tspan: (0.0, 1.0)
├─ time points: 18
├─ final state: [0.5403023057842606, 0.8414709847533869]
└─ final costate: [-0.8414709847533869, 0.5403023057842606]Variable parameters
For a NonFixed flow, pass the variable variable keyword:
vf_v = Data.VectorField((x, v) -> -v[1] .* x; is_variable=true)
flow_v = Flows.Flow(vf_v)
xf_v = flow_v(0.0, [1.0, 0.0], 1.0; variable=[2.0])2-element Vector{Float64}:
0.13533528348480484
0.0The variable argument is required when is_variable(flow) is true, and silently ignored for Fixed flows.
Variable costate
For a NonFixed HamiltonianFlow, pass variable_costate=true to also integrate the augmented adjoint (xf, pf, pvf) instead of (xf, pf):
h_v = Data.Hamiltonian((x, p, v) -> v[1] * p^2 / 2; is_variable=true)
hflow_v = Flows.Flow(h_v)
xf, pf, pvf = hflow_v(0.0, 1.0, 0.5, 1.0; variable=[2.0], variable_costate=true)
(xf, pf, pvf)(1.9999999999999998, 0.5, -0.12499999999999997)This is only available for point evaluation, and only when variable is provided (NonFixed flows require it).
Free times
The positional tf argument is the evaluation time; it is independent of the variable value, even when a variable component represents a free initial or final time. Passing flow(t0, x0, p0, t1; variable=v) with t1 ≠ v is valid.
When a variable component is a free time, the flow keeps integrating the same naive adjoint — no special-casing — with the augmented costate started at variable_costate=true always uses). This zero start is exactly what makes the mitigated free-time transversality conditions valid: written at
where H is obtained from CTFlows.Systems.hamiltonian(flow); a nonzero
See test/suite/flows/test_variable_costate_free_time.jl for worked shooting residuals built on this test/suite/integration/test_goddard.jl) instead close their free final time with the classical
Hamiltonian / pseudo-Hamiltonian getters
Any Hamiltonian flow exposes its underlying Hamiltonian and — when the flow was built from a control law — its pseudo-Hamiltonian, control law, and their gradients:
Systems.hamiltonian(hflow_v) # the callable H(t, x, p, v)
Systems.hamiltonian_gradient(hflow_v) # functor: (t, x, p, v) -> (∂H/∂x, ∂H/∂p)
Systems.variable_gradient(hflow_v) # functor: (t, x, p, v) -> ∂H/∂vCTFlows.Systems.HamiltonianVariableGradient{CTBase.Data.Hamiltonian{Main.var"#11#12", CTBase.Traits.Autonomous, CTBase.Traits.NonFixed}, CTBase.Differentiation.DifferentiationInterface{CTBase.Strategies.StrategyOptions{@NamedTuple{ad_backend::CTBase.Options.OptionValue{AutoForwardDiff{nothing, Nothing}}}}}}(Hamiltonian: autonomous, variable
natural call: h(x, p, v)
uniform call: h(t, x, p, v), DifferentiationInterface(ad_backend=AutoForwardDiff()))Systems.pseudo_hamiltonian, Systems.control_law, Systems.pseudo_hamiltonian_gradient and Systems.pseudo_variable_gradient are available on flows built from a pseudo-Hamiltonian (or an OCP) and a control law — see Control laws. Calling them on a flow with no associated control law throws IncorrectArgument.
Configuration objects
The convenience call signatures above internally build configuration objects that bundle the integration parameters. You can also construct them explicitly and pass them to Flows._invoke_flow:
| Config type | Usage | Arguments |
|---|---|---|
StateEndPointConfig | state final value | (t0, x0, tf) |
StateTrajectoryConfig | state full trajectory | (tspan, x0) |
HamiltonianEndPointConfig | state+costate final value | (t0, x0, p0, tf) |
HamiltonianTrajectoryConfig | state+costate trajectory | (tspan, x0, p0) |
AugmentedHamiltonianEndPointConfig | state+costate+variable-costate final value | (t0, x0, p0, pv0, tf) |
cfg = Configs.StateTrajectoryConfig((0.0, 1.0), [1.0, 0.0])
Configs.tspan(cfg)(0.0, 1.0)Configuration objects separate what to integrate from how to integrate (the flow). This separation is useful when the same config must be passed to several flows.
Integrator options
Options are passed as keyword arguments to Flows.Flow(data; opts...) or Integrators.build_integrator(; opts...).
The default integrator is SciML backed by OrdinaryDiffEqTsit5 (loaded when import OrdinaryDiffEqTsit5 appears in your session).
Common options
| Option | Default | Description |
|---|---|---|
reltol | 1e-6 | Relative tolerance |
abstol | 1e-8 | Absolute tolerance |
alg | Tsit5() | ODE algorithm (any SciML algorithm) |
saveat | [] | Extra time points to save |
dense | true | Dense output for interpolation |
# Tighter tolerances
flow_tight = Flows.Flow(vf; reltol=1e-12, abstol=1e-12)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-12, reltol = 1.0e-12)Different algorithm (requires the matching OrdinaryDiffEq package to be loaded).
using OrdinaryDiffEqRosenbrock
flow_rodas = Flows.Flow(vf; alg=Rodas4())Flow
├─ system: VectorFieldSystem
│ ├─ wraps: VectorField: autonomous, fixed (no variable), out-of-place
│ └─ rhs: IPVFOoPRHS (out-of-place VF → in-place interface)
└─ integrator: SciML (alg = OrdinaryDiffEqRosenbrock.Rodas4{AutoForwardDiff{nothing, Nothing}, Nothing, typeof(OrdinaryDiffEqCore.trivial_limiter!), typeof(OrdinaryDiffEqCore.trivial_limiter!), Nothing}(nothing, OrdinaryDiffEqCore.trivial_limiter!, OrdinaryDiffEqCore.trivial_limiter!, AutoForwardDiff(), nothing, 20, 0.03))Unsafe mode
By default, a SolverFailure exception is thrown if the ODE solver returns a non-success retcode. Pass unsafe=true to suppress this check:
xf_unsafe = flow(0.0, [1.0, 0.0], 1.0; unsafe=true)2-element Vector{Float64}:
0.36787944127643124
0.0Use unsafe=true inside shooting methods or optimisation loops where you want to handle failures gracefully instead of relying on exceptions.
SciML integrator internals
The SciML integrator strategy itself — its options, construction, and the CommonSolve.solve method that wraps SciML's solve — is provided by CTSolvers.Integrators. CTFlows contributes only the glue: Integrators.build_problem turns a system and a configuration into a SciML ODEProblem, and Integrators.build_options selects the integrator's cached option bundle for the configuration. Integration is then CommonSolve.solve(prob, integ). Keeping build_problem separate from the solve step lets the same problem definition be re-solved with different parameters efficiently.
integ = Integrators.build_integrator(; reltol=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 [default]
├─ save_start = auto [default]
└─ dense = auto [default]
Tip: use describe(SciML) to see all available options.See also
CTFlows.Configs.StateEndPointConfig,CTFlows.Configs.StateTrajectoryConfig— state configuration objects.CTFlows.Configs.HamiltonianEndPointConfig,CTFlows.Configs.HamiltonianTrajectoryConfig— Hamiltonian configuration objects.CTFlows.Configs.tspan,CTFlows.Configs.initial_state,CTFlows.Configs.initial_costate— configuration accessors.CTFlows.Integrators.build_problem,CTFlows.Integrators.build_options— the CTFlows-side integrator glue.CTSolvers.Integrators.SciML,CTSolvers.Integrators.build_integrator,CTSolvers.Integrators.AbstractIntegrator— the integrator strategy (provided by CTSolvers).