ControlSystemsMTK: Easy Way to Initialize Output?

Is there a convenient way to the initialize the unknowns in a ODESystem created by ControlSystemsMTK based on a desired initial output?

I needed a simple time delay model for something, and I figured using pade from ControlSystems and converting it using ControlSystemsMTK would be fine. And it works, but setting the initial states for the converted system isn’t effortless.

using ControlSystemsBase, ControlSystemsMTK, Plots, ModelingToolkit, OrdinaryDiffEq
delay_tf = pade(0.8, 2, 5)
@named delay = ODESystem(delay_tf)

@Independent_variables t
connections = [delay.input.u ~ 1.0 + 0.2 * sin(t)]
sys = compose(ODESystem(connections, t, name=:bob), delay)
sys = structural_simplify(sys)

u0 = [delay.x[5] => 20.0] # Close enough for now
prob = ODEProblem(sys, u0, (0.0, 10.0))
sol = solve(prob, Tsit5())

display(plot(sol.t, sol[delay.output.u]))
sol[delay.output.u][1] == 20.0 # is false

I tried a few simple attempts at leaning on MTK’s initialization methods but didn’t find anything quickly.

My last thought was maybe the system was in a canonical form, I could figure out how to initialize the states from that, but it looks like the equations in the created model don’t follow a canonical form.

Specifying the output instead of one of the state variables should work?

u0 = [delay.output.u => 20.0]

If you want to obtain the delay on a canonical form, try

julia> ss(delay_tf, balance=false)
StateSpace{Continuous, Float64}
A = 
 -18.75  -187.49999999999997  -1171.8749999999998  -4394.531249999999  -7690.429687499997
   1.0      0.0                   0.0                  0.0                 0.0
   0.0      1.0                   0.0                  0.0                 0.0
   0.0      0.0                   1.0                  0.0                 0.0
   0.0      0.0                   0.0                  1.0                 0.0
B = 
 1.0
 0.0
 0.0
 0.0
 0.0
C = 
 0.0  0.0  117.18749999999997  -1757.8124999999993  7690.429687499997
D = 
 0.0

Continuous-time state-space model

but I recommend against going this route, the solver will not like this realization and will likely take longer time to perform the simulation if you avoid balancing.

I did try [delay.output.u => 20.0] and few variations thereof but they usually came back with an overdetermined warning.

using ControlSystemsBase, ControlSystemsMTK, ModelingToolkit, OrdinaryDiffEq
delay_tf = pade(0.8, 2, 5)
@named delay = ODESystem(delay_tf)

@independent_variables t
connections = [delay.input.u ~ 1.0 + 0.2 * sin(t)]
sys = compose(ODESystem(connections, t, name=:bob), delay)
sys = structural_simplify(sys)

u0 = [delay.output.u => 20.0]
prob = ODEProblem(sys, u0, (0.0, 10.0))
sol = solve(prob, Tsit5())

using Plots
display(plot(sol.t, sol[delay.output.u]))
sol[delay.output.u][1] == 20.0
julia> include("mwe.jl")
┌ Warning: Initialization system is overdetermined. 1 equations for 0 unknowns. Initialization will default to using least squares. `SCCNonlinearProblem` can only be used for initialization of fully determined systems and hence will not be used here. To suppress this warning pass warn_initialize_determined = false. To make this warning into an error, pass fully_determined = true
└ @ ModelingToolkit ~/.julia/packages/ModelingToolkit/KpwHT/src/systems/diffeqs/abstractodesystem.jl:1517
false

julia> sol
retcode: InitialFailure
Interpolation: specialized 4th order "free" interpolation
t: 1-element Vector{Float64}:
 0.0
u: 1-element Vector{Vector{Float64}}:
 [0.0, 0.0, 0.0, 0.0, 0.0]

I’ll check out the unbalanced version. Thanks!

you can unset some variables by

u0 = [delay.output.u => 20.0; collect(delay.x[:] .=> nothing)]

this should prevent it from being over-determined


This works

u0 = [delay.output.u => 20.0; delay.x[5] => nothing]#; delay.x[4] => nothing; delay.x[3] => nothing]
prob = ODEProblem(sys, u0, (0.0, 10.0))
sol = solve(prob, Tsit5())

using Plots
display(plot(sol.t, sol[delay.output.u]))
sol[delay.output.u][1] ≈ 20.0
# true