I tried updated the example in that PR to modern MTK syntax.
using ModelingToolkit
using OrdinaryDiffEq
@parameters t #time
D = Differential(t)
@variables Cₛ(t), Cₓ(t), ν(t) # states
x = [Cₛ, Cₓ, ν]
n_x = length(x)
@parameters uᵢₙ # control input
u = [uᵢₙ]
n_u = length(u)
@parameters μₘₐₓ Cₛ_ᵢₙ Cₛ₀ Cₓ₀ # uncertain parameters
p = [μₘₐₓ, Cₛ_ᵢₙ, Cₛ₀, Cₓ₀]
n_p = length(p)
x₀ = [Cₛ₀, Cₓ₀, μₘₐₓ] # parametrized initial conditions; note third initial condition is also present in the dynamics
@parameters Kₛ, yₓ_ₛ, m #other constants
cons = [Kₛ, yₓ_ₛ, m]
μ = μₘₐₓ*Cₛ/Kₛ + Cₛ
σ = μ/yₓ_ₛ + m
all_parameters = [p;cons;u]
eqs = [D(Cₛ) ~ -σ*Cₓ + uᵢₙ/ν*Cₛ_ᵢₙ - uᵢₙ/ν*Cₛ,
D(Cₓ) ~ μ*Cₓ - uᵢₙ/ν*Cₓ,
D(ν) ~ uᵢₙ]
@named sys = ODESystem(eqs)
tspan = (0.0, 10.0)
prob = ODEProblem(sys, x.=>x₀, tspan, all_parameters .=> rand(length(all_parameters)))
solve(prob,Tsit5())
function forward_sensitivity_transform(sys,p)
iv = sys.iv
f = du_dt = [eq.rhs for eq ∈ sys.eqs]
f = collect(f)
u = states(sys)
D_p = [Differential(par) for par ∈ p]
sen_symbols = [Symbol(:d,state.metadata.value[2],:_d,par.val.metadata.parent.value[2]) for state ∈ u, par in p]
du_dp = [(@variables $sen_symbol(t))[1] for sen_symbol ∈ sen_symbols]
D = Differential(iv)
uₑₓₜ= vcat(u,vec(du_dp))
df_du = ModelingToolkit.jacobian(f,u)
df_dp = ModelingToolkit.jacobian(f,p)
ddu_dpdt = df_du*du_dp + df_dp
duₑₓₜ_dt = fₑₓₜ = vcat(du_dt,vec(ddu_dpdt))
eqsₑₓₜ = simplify.(D.(uₑₓₜ) .~ fₑₓₜ)
@named sysₑₓₜ = ODESystem(eqsₑₓₜ, iv, uₑₓₜ, sys.ps)
end
sys_sen = forward_sensitivity_transform(sys,p)
function forward_sensitivity_intial_condition(u₀,p)
du₀_dp = ModelingToolkit.jacobian(u₀,p)
u₀ₑₓₜ = vcat(u₀,vec(du₀_dp))
end
x0_sen = forward_sensitivity_intial_condition(x₀,p)
prob_sen = ODEProblem(sys_sen, states(sys_sen).=>x0_sen, tspan, all_parameters .=> rand(length(all_parameters)))
solve(prob_sen,Tsit5())
In constructing the names of the sensitivity states, I had to mess around with the internals quite a bit, and my way is likely not robust. I didn’t keep up with the changes to Symbolics.jl, so there is likely a better way of doing that.
Instead of constructing sensitivity state names with an underscore in them, I would prefer treating them as actual derivative objects. This is where we got stuck previously, as MTK did not have a way of then transforming the ODESystem to an ODEProblem. This seems to still be the case, so finishing the PR will likely take a lot of effort.