doc: updating MTK example to v11 in manual.#340
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #340 +/- ##
=======================================
Coverage 98.57% 98.57%
=======================================
Files 27 27
Lines 5190 5190
=======================================
Hits 5116 5116
Misses 74 74 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
Try |
|
Thanks for the answer but using ERROR: AssertionError: `promote_to_concrete` can't make type Missing uniform with Float64
Stacktrace:
[1] promote_to_concrete(vs::Vector{Union{Missing, Real}}; tofloat::Bool, use_union::Bool)
@ ModelingToolkitBase ~/.julia/packages/ModelingToolkitBase/uIKoY/src/utils.jl:1091
[2] varmap_to_vars(varmap::ModelingToolkitBase.AtomicArrayDict{…}, vars::Vector{…}; tofloat::Bool, use_union::Bool, container_type::Type, buffer_eltype::Type, toterm::Function, check::Bool, allow_symbolic::Bool, is_initializeprob::Bool, substitution_limit::Int64, missing_values::ModelingToolkitBase.MissingGuessValue.var"typeof(MissingGuessValue)")
@ ModelingToolkitBase ~/.julia/packages/ModelingToolkitBase/uIKoY/src/systems/problem_utils.jl:418
[3] varmap_to_vars
@ ~/.julia/packages/ModelingToolkitBase/uIKoY/src/systems/problem_utils.jl:331 [inlined]
[4] get_p(sys::System, varmap::ReadOnlyDicts.ReadOnlyDict{…}; split::Bool, kwargs::@Kwargs{})
@ ModelingToolkitBase ~/.julia/packages/ModelingToolkitBase/uIKoY/src/systems/problem_utils.jl:2059
[5] get_p
@ ~/.julia/packages/ModelingToolkitBase/uIKoY/src/systems/problem_utils.jl:2048 [inlined]
[6] generate_f_h(model::System, inputs::Vector{Num}, outputs::Vector{Num})
@ Main ~/.julia/dev/ModelPredictiveControl/docs/src/manual/mtk.md:91
[7] top-level scope
@ ~/.julia/dev/ModelPredictiveControl/docs/src/manual/mtk.md:96
Some type information was truncated. Use `show(err)` to see complete types.If I do ReadOnlyDicts.ReadOnlyDict{SymbolicUtils.BasicSymbolicImpl.var"typeof(BasicSymbolicImpl)"{SymReal}, SymbolicUtils.BasicSymbolicImpl.var"typeof(BasicSymbolicImpl)"{SymReal}, ModelingToolkitBase.AtomicArrayDict{SymbolicUtils.BasicSymbolicImpl.var"typeof(BasicSymbolicImpl)"{SymReal}, Dict{SymbolicUtils.BasicSymbolicImpl.var"typeof(BasicSymbolicImpl)"{SymReal}, SymbolicUtils.BasicSymbolicImpl.var"typeof(BasicSymbolicImpl)"{SymReal}}}}(τ(t) => missing) |
|
You should pass a varmap as the second argument, bindings should not be used |
|
And how do I get the varmap of the 4 parameters ? I tried passing Nothing about this in the documentation also. |
|
a varmap is just op = Dict(p1 => val1, p2 => val2, ...) |
|
Alright thanks, but I still need to access the current value that I set at the line |
|
This is how I typically do it |
|
Not sure this is the good way of proceeding, constructing an I read the wall of text about this in the documentation here : https://docs.sciml.ai/ModelingToolkit/stable/tutorials/initialization/ Two observations:
|
|
The ODEProblem constructor is a thin wrapper around initialization so it isn't really much more expensive than just solving the initialization problem. The issue with your old approach is that parameters stored in the system may not be limited to simple values only, they may be the solution of a nonlinear problem (referred to as the "initialization problem") |
|
I'm not sure I'm following you, how it could be a nonlinear root solving problem for parameters (as in non-differential and non-algebraic states) ? |
|
It's possible in MTK to specify initialization equations that must hold at initialization and solve for parameters that satisfy those equations. For example @parameters constant_input=missing
equations = [
D(x) ~ x + p
]
initialization_equations = [
D(x) ~ 0
]to solve for the value of |
|
It's cool and all that it is now supported in MTK but as I wrote in the disclaimer, the goal of this example is to provide a starting template to merge both packages, not something that cover all the corner cases. And this example focus on parameters that are well-defined and known, as this is the most common cases. I will try to come up to something that work well for this specific case. |
|
Okay this what I do now: function generate_f_h(model, inputs, outputs)
(_, f_ip), x_sym, p_sym, io_sys = ModelingToolkit.generate_control_function(
model, inputs, split=false, simplify=true
)
if any(ModelingToolkit.is_alg_equation, equations(io_sys))
error("Systems with algebraic equations are not supported")
end
nu, nx, ny = length(inputs), length(x_sym), length(outputs)
function f!(ẋ, x, u, _ , p)
try
f_ip(ẋ, x, u, p, nothing)
catch err
if err isa MethodError
error("NonLinModel does not support a time argument t in the f function, "*
"see the constructor docstring for a workaround.")
else
rethrow()
end
end
return nothing
end
(_, h_ip) = ModelingToolkit.build_explicit_observed_function(
io_sys, outputs; inputs, return_inplace = true
)
u_nothing = fill(nothing, nu)
function h!(y, x, _ , p)
try
# MTK.jl supports a `u` argument in `h_ip` function but not this package. We set
# `u` as a vector of nothing and `h_ip` function will presumably throw an
# MethodError if this argument is used inside the function
h_ip(y, x, u_nothing, p, nothing)
catch err
if err isa MethodError
error("NonLinModel only support strictly proper systems (no manipulated "*
"input argument u in the output function h)")
else
rethrow()
end
end
return nothing
end
# this is how I extract the correct `p` vector:
ic = initial_conditions(io_sys)
p_map = Dict(sym => ic[sym] for sym in p_sym)
p = ModelingToolkit.varmap_to_vars(p_map, p_sym)
return f!, h!, p, x_sym, p_sym, nu, nx, ny
endIt work well on MTK.jl v11. The
As expected, the edit: I will add a clear error message in such cases. |
|
Oupsie there's a bug in the version that I just released. The order in |
|
In my experience, using a non-supported way of obtaining the parameter object leads to a constant maintenance burden. Your three lines of code ic = initial_conditions(io_sys)
p_map = Dict(sym => ic[sym] for sym in p_sym)
p = ModelingToolkit.varmap_to_vars(p_map, p_sym)could be just p = ModelingToolkit.ODEProblem(iosys, [], (0.0, 1.0)).pand it would work in more cases and be less likely to break on you. |
|
How do you know this is the official way of doing this ? I did not found anything on this in the documentation. Also, the returned p = ModelingToolkit.ODEProblem(io_sys, [], (0.0, 1.0)).p
display(p)displaying: 12-element Vector{Float64}:
0.4
0.3
1.2
9.8
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0It does work but this is misleading for the user, since the |
Okay I see that's how initialization problems are solved in this section https://docs.sciml.ai/ModelingToolkit/stable/tutorials/initialization/#bindings_and_ics, so that's presumably the official way. Do you know why |
MTK automatically adds parameters for initial conditions and inputs IIRC, possibly some additional things |
|
Okay thanks. As I said above, if the user want to know how the returned np = length(p_sym)
p = ModelingToolkit.ODEProblem(io_sys, [], (0.0, 1.0)).p[1:np]I will stick to my unofficial and limited-in-scope solution. |
|
MTK typically does not guaerantee the order of variables, to modify values, symbolic indexing is supposed to be used. There are some tools listed here https://docs.sciml.ai/ModelingToolkit/stable/examples/remake/#Re-creating-the-problem for example this does not rely on the order, it uses a symbolic map |
|
Yeah I get this, but when I call f_ip(xout,x,u,p,t) -> nothingAm I right ? If yes, doing the 3 lines of code mentioned in #340 (comment) should work in most situation in which the parameters are constant. |
Hello @baggepinnen,
I'm trying to update the MTK example to v11 but i'm stuck at the
varmat_to_varsfunction here:this code prints:
Do you know why
bindings(io_sys)does not contain the parameter values anymore ?