From 9888fe29a48e9e3338a633109b1d53df7ad9da9a Mon Sep 17 00:00:00 2001 From: franckgaga Date: Fri, 22 Aug 2025 20:53:20 -0400 Subject: [PATCH 1/2] changed: keep `f!` and `h!` functions available The fields in `NonLinModel` are now the `f!` and `h!` functions instead of `solver_f!` and `solver_h!`, as it was the case before. This is needed for the colocation methods. --- src/estimator/internal_model.jl | 9 +- src/model/linearization.jl | 18 ++-- src/model/nonlinmodel.jl | 70 ++++++++++------ src/model/solver.jl | 134 ++++++++++-------------------- src/sim_model.jl | 1 - test/1_test_sim_model.jl | 56 ++++++------- test/3_test_predictive_control.jl | 2 +- 7 files changed, 131 insertions(+), 159 deletions(-) diff --git a/src/estimator/internal_model.jl b/src/estimator/internal_model.jl index 94dde0b53..b1c6b0976 100644 --- a/src/estimator/internal_model.jl +++ b/src/estimator/internal_model.jl @@ -170,20 +170,19 @@ end State function ``\mathbf{f̂}`` of [`InternalModel`](@ref) for [`NonLinModel`](@ref). -It calls `model.solver_f!(x̂0next, k0, x̂0, u0 ,d0, model.p)` directly since this estimator -does not augment the states. +It calls [`f!`](@ref) directly since this estimator does not augment the states. """ function f̂!(x̂0next, _ , k0, ::InternalModel, model::NonLinModel, x̂0, u0, d0) - return model.solver_f!(x̂0next, k0, x̂0, u0, d0, model.p) + return f!(x̂0next, k0, model, x̂0, u0, d0, model.p) end @doc raw""" ĥ!(ŷ0, estim::InternalModel, model::NonLinModel, x̂0, d0) -Output function ``\mathbf{ĥ}`` of [`InternalModel`](@ref), it calls `model.solver_h!`. +Output function ``\mathbf{ĥ}`` of [`InternalModel`](@ref), it calls [`h!`](@ref). """ function ĥ!(ŷ0, ::InternalModel, model::NonLinModel, x̂0, d0) - return model.solver_h!(ŷ0, x̂0, d0, model.p) + return h!(ŷ0, model, x̂0, d0, model.p) end diff --git a/src/model/linearization.jl b/src/model/linearization.jl index 54958a772..8d18e85d2 100644 --- a/src/model/linearization.jl +++ b/src/model/linearization.jl @@ -1,9 +1,9 @@ """ get_linearization_func( - NT, solver_f!, solver_h!, nu, nx, ny, nd, ns, p, solver, backend + NT, f!, h!, Ts, nu, nx, ny, nd, ns, p, solver, backend ) -> linfunc! -Return `linfunc!` function that computes Jacobians of `solver_f!` and `solver_h!` functions. +Return `linfunc!` function that computes Jacobians of `f!` and `h!` functions. The function has the following signature: ``` @@ -13,12 +13,14 @@ and it should modifies in-place all the arguments before `backend`. The `backend is an `AbstractADType` object from `DifferentiationInterface`. The `cst_x`, `cst_u` and `cst_d` are `DifferentiationInterface.Constant` objects with the linearization points. """ -function get_linearization_func(NT, solver_f!, solver_h!, nu, nx, ny, nd, p, solver, backend) - f_x!(xnext, x, k, u, d) = solver_f!(xnext, k, x, u, d, p) - f_u!(xnext, u, k, x, d) = solver_f!(xnext, k, x, u, d, p) - f_d!(xnext, d, k, x, u) = solver_f!(xnext, k, x, u, d, p) - h_x!(y, x, d) = solver_h!(y, x, d, p) - h_d!(y, d, x) = solver_h!(y, x, d, p) +function get_linearization_func( + NT, f!::F, h!::H, Ts, nu, nx, ny, nd, p, solver, backend +) where {F<:Function, H<:Function} + f_x!(xnext, x, k, u, d) = solver_f!(xnext, k, f!, Ts, solver, x, u, d, p) + f_u!(xnext, u, k, x, d) = solver_f!(xnext, k, f!, Ts, solver, x, u, d, p) + f_d!(xnext, d, k, x, u) = solver_f!(xnext, k, f!, Ts, solver, x, u, d, p) + h_x!(y, x, d) = h!(y, x, d, p) + h_d!(y, d, x) = h!(y, x, d, p) strict = Val(true) xnext = zeros(NT, nx) y = zeros(NT, ny) diff --git a/src/model/nonlinmodel.jl b/src/model/nonlinmodel.jl index a1d9c9ccc..a001010b0 100644 --- a/src/model/nonlinmodel.jl +++ b/src/model/nonlinmodel.jl @@ -1,17 +1,33 @@ +"Abstract supertype of all differential equation solvers." +abstract type DiffSolver end + +"Empty solver for nonlinear discrete-time models." +struct EmptySolver <: DiffSolver + ni::Int # number of intermediate stages + EmptySolver() = new(-1) +end + +"Call `f!` function directly for discrete-time models (no solver)." +function solver_f!(xnext, _ , f!::F, _ , ::EmptySolver, x, u, d, p) where F + return f!(xnext, x, u, d, p) +end + +Base.show(io::IO, ::EmptySolver) = print(io, "Empty differential equation solver.") + struct NonLinModel{ NT<:Real, - F<:Function, - H<:Function, - PT<:Any, DS<:DiffSolver, + F <:Function, + H <:Function, + PT<:Any, JB<:AbstractADType, LF<:Function } <: SimModel{NT} x0::Vector{NT} - solver_f!::F - solver_h!::H - p::PT solver::DS + f!::F + h!::H + p::PT Ts::NT t::Vector{NT} nu::Int @@ -32,14 +48,14 @@ struct NonLinModel{ linfunc!::LF buffer::SimModelBuffer{NT} function NonLinModel{NT}( - solver_f!::F, solver_h!::H, Ts, nu, nx, ny, nd, - p::PT, solver::DS, jacobian::JB, linfunc!::LF + solver::DS, f!::F, h!::H, Ts, nu, nx, ny, nd, + p::PT, jacobian::JB, linfunc!::LF ) where { NT<:Real, + DS<:DiffSolver, F<:Function, H<:Function, PT<:Any, - DS<:DiffSolver, JB<:AbstractADType, LF<:Function } @@ -58,11 +74,11 @@ struct NonLinModel{ ni = solver.ni nk = nx*(ni+1) buffer = SimModelBuffer{NT}(nu, nx, ny, nd, ni) - return new{NT, F, H, PT, DS, JB, LF}( + return new{NT, DS, F, H, PT, JB, LF}( x0, - solver_f!, solver_h!, - p, - solver, + solver, + f!, h!, + p, Ts, t, nu, nx, ny, nd, nk, uop, yop, dop, xop, fop, @@ -136,7 +152,7 @@ julia> f!(ẋ, x, u, _ , p) = (ẋ .= p*x .+ u; nothing); julia> h!(y, x, _ , _ ) = (y .= 0.1x; nothing); julia> model1 = NonLinModel(f!, h!, 5.0, 1, 1, 1, p=-0.2) # continuous dynamics -NonLinModel with a sample time Ts = 5.0 s, RungeKutta(4) solver and: +NonLinModel with a sample time Ts = 5.0 s, RungeKutta{4} solver and: 1 manipulated inputs u 1 states x 1 outputs y @@ -176,12 +192,11 @@ function NonLinModel{NT}( ) where {NT<:Real} isnothing(solver) && (solver=EmptySolver()) f!, h! = get_mutating_functions(NT, f, h) - solver_f!, solver_h! = get_solver_functions(NT, solver, f!, h!, Ts, nu, nx, ny, nd) linfunc! = get_linearization_func( - NT, solver_f!, solver_h!, nu, nx, ny, nd, p, solver, jacobian + NT, f!, h!, Ts, nu, nx, ny, nd, p, solver, jacobian ) return NonLinModel{NT}( - solver_f!, solver_h!, Ts, nu, nx, ny, nd, p, solver, jacobian, linfunc! + solver, f!, h!, Ts, nu, nx, ny, nd, p, jacobian, linfunc! ) end @@ -270,22 +285,27 @@ Call [`linearize(model; x, u, d)`](@ref) and return the resulting linear model. """ LinModel(model::NonLinModel; kwargs...) = linearize(model; kwargs...) + """ f!(x0next, k0, model::NonLinModel, x0, u0, d0, p) -Call `model.solver_f!(x0next, k0, x0, u0, d0, p)` for [`NonLinModel`](@ref). +Compute `x0next` using the [`DiffSolver`](@ref) in `model.solver` and `model.f!`. -The method mutate `x0next` and `k0` arguments in-place. The latter is used to store the -intermediate stage values of `model.solver` [`DiffSolver`](@ref). +The method mutates `x0next` and `k0` arguments in-place. The latter is used to store the +intermediate stage values of the solver. """ -f!(x0next, k0, model::NonLinModel, x0, u0, d0, p) = model.solver_f!(x0next, k0, x0, u0, d0, p) +function f!(x0next, k0, model::NonLinModel, x0, u0, d0, p) + return solver_f!(x0next, k0, model.f!, model.Ts, model.solver, x0, u0, d0, p) +end """ h!(y0, model::NonLinModel, x0, d0, p) -Call `model.solver_h!(y0, x0, d0, p)` for [`NonLinModel`](@ref). +Compute `y0` by calling `model.h!` directly for [`NonLinModel`](@ref). """ -h!(y0, model::NonLinModel, x0, d0, p) = model.solver_h!(y0, x0, d0, p) +h!(y0, model::NonLinModel, x0, d0, p) = model.h!(y0, x0, d0, p) + +include("solver.jl") -detailstr(model::NonLinModel) = ", $(typeof(model.solver).name.name)($(model.solver.order)) solver" -detailstr(::NonLinModel{<:Real, <:Function, <:Function, <:Any, <:EmptySolver}) = ", empty solver" \ No newline at end of file +detailstr(model::NonLinModel) = ", $(typeof(model.solver)) solver" +detailstr(::NonLinModel{<:Real, <:EmptySolver}) = ", empty solver" \ No newline at end of file diff --git a/src/model/solver.jl b/src/model/solver.jl index 41a9c582c..eb81ad7a3 100644 --- a/src/model/solver.jl +++ b/src/model/solver.jl @@ -1,39 +1,5 @@ -"Abstract supertype of all differential equation solvers." -abstract type DiffSolver end - -"Empty solver for nonlinear discrete-time models." -struct EmptySolver <: DiffSolver - ni::Int # number of intermediate stages - EmptySolver() = new(-1) -end - -""" - get_solver_functions(NT::DataType, solver::EmptySolver, f!, h!, Ts, nu, nx, ny, nd) - -Get `solver_f!` and `solver_h!` functions for the `EmptySolver` (discrete models). - -The functions should have the following signature: -``` - solver_f!(xnext, k, x, u, d, p) -> nothing - solver_h!(y, x, d, p) -> nothing -``` -in which `xnext`, `k` and `y` arguments are mutated in-place. The `k` argument is -a vector of `nx*(solver.ni+1)` elements to store the solver intermediate stage values (and -also the current state value for when `supersample ≠ 1`). -""" -function get_solver_functions(::DataType, ::EmptySolver, f!, h!, _ , _ , _ , _ , _ ) - solver_f!(xnext, _ , x, u, d, p) = f!(xnext, x, u, d, p) - solver_h! = h! - return solver_f!, solver_h! -end - -function Base.show(io::IO, solver::EmptySolver) - print(io, "Empty differential equation solver.") -end - -struct RungeKutta <: DiffSolver +struct RungeKutta{N} <: DiffSolver ni::Int # number of intermediate stages - order::Int # order of the method supersample::Int # number of internal steps function RungeKutta(order::Int, supersample::Int) if order ≠ 4 && order ≠ 1 @@ -46,7 +12,7 @@ struct RungeKutta <: DiffSolver throw(ArgumentError("supersample must be greater than 0")) end ni = order # only true for order ≤ 4 with RungeKutta - return new(ni, order, supersample) + return new{order}(ni, supersample) end end @@ -61,60 +27,29 @@ This solver is allocation-free if the `f!` and `h!` functions do not allocate. """ RungeKutta(order::Int=4; supersample::Int=1) = RungeKutta(order, supersample) -"Get `solve_f!` and `solver_h!` functions for the explicit Runge-Kutta solvers." -function get_solver_functions(NT::DataType, solver::RungeKutta, f!, h!, Ts, _ , nx, _ , _ ) - solver_f! = if solver.order == 4 - get_rk4_function(NT, solver, f!, Ts, nx) - elseif solver.order == 1 - get_euler_function(NT, solver, f!, Ts, nx) - else - throw(ArgumentError("only 1st and 4th order Runge-Kutta is supported.")) +"Solve the differential equation with the 4th order Runge-Kutta method." +function solver_f!(xnext, k, f!::F, Ts, solver::RungeKutta{4}, x, u, d, p) where F + supersample = solver.supersample + Ts_inner = Ts/supersample + nx = length(x) + xcurr = @views k[1:nx] + k1 = @views k[(1nx + 1):(2nx)] + k2 = @views k[(2nx + 1):(3nx)] + k3 = @views k[(3nx + 1):(4nx)] + k4 = @views k[(4nx + 1):(5nx)] + @. xcurr = x + for i=1:supersample + f!(k1, xcurr, u, d, p) + @. xnext = xcurr + k1 * Ts_inner/2 + f!(k2, xnext, u, d, p) + @. xnext = xcurr + k2 * Ts_inner/2 + f!(k3, xnext, u, d, p) + @. xnext = xcurr + k3 * Ts_inner + f!(k4, xnext, u, d, p) + @. xcurr = xcurr + (k1 + 2k2 + 2k3 + k4)*Ts_inner/6 end - solver_h! = h! - return solver_f!, solver_h! -end - -"Get the f! function for the 4th order explicit Runge-Kutta solver." -function get_rk4_function(NT, solver, f!, Ts, nx) - Ts_inner = Ts/solver.supersample - function rk4_solver_f!(xnext, k, x, u, d, p) - xcurr = @views k[1:nx] - k1 = @views k[(1nx + 1):(2nx)] - k2 = @views k[(2nx + 1):(3nx)] - k3 = @views k[(3nx + 1):(4nx)] - k4 = @views k[(4nx + 1):(5nx)] - @. xcurr = x - for i=1:solver.supersample - f!(k1, xcurr, u, d, p) - @. xnext = xcurr + k1 * Ts_inner/2 - f!(k2, xnext, u, d, p) - @. xnext = xcurr + k2 * Ts_inner/2 - f!(k3, xnext, u, d, p) - @. xnext = xcurr + k3 * Ts_inner - f!(k4, xnext, u, d, p) - @. xcurr = xcurr + (k1 + 2k2 + 2k3 + k4)*Ts_inner/6 - end - @. xnext = xcurr - return nothing - end - return rk4_solver_f! -end - -"Get the f! function for the explicit Euler solver." -function get_euler_function(NT, solver, fc!, Ts, nx) - Ts_inner = Ts/solver.supersample - function euler_solver_f!(xnext, k, x, u, d, p) - xcurr = @views k[1:nx] - k1 = @views k[(1nx + 1):(2nx)] - @. xcurr = x - for i=1:solver.supersample - fc!(k1, xcurr, u, d, p) - @. xcurr = xcurr + k1 * Ts_inner - end - @. xnext = xcurr - return nothing - end - return euler_solver_f! + @. xnext = xcurr + return nothing end """ @@ -126,7 +61,24 @@ This is an alias for `RungeKutta(1; supersample)`, see [`RungeKutta`](@ref). """ const ForwardEuler(;supersample=1) = RungeKutta(1; supersample) -function Base.show(io::IO, solver::RungeKutta) - N, n = solver.order, solver.supersample + +"Solve the differential equation with the forward Euler method." +function solver_f!(xnext, k, f!::F, Ts, solver::RungeKutta{1}, x, u, d, p) where F + supersample = solver.supersample + Ts_inner = Ts/supersample + nx = length(x) + xcurr = @views k[1:nx] + k1 = @views k[(1nx + 1):(2nx)] + @. xcurr = x + for i=1:supersample + f!(k1, xcurr, u, d, p) + @. xcurr = xcurr + k1 * Ts_inner + end + @. xnext = xcurr + return nothing +end + +function Base.show(io::IO, solver::RungeKutta{N}) where N + n = solver.supersample print(io, "$(N)th order Runge-Kutta differential equation solver with $n supersamples.") end \ No newline at end of file diff --git a/src/sim_model.jl b/src/sim_model.jl index d79063e16..696068558 100644 --- a/src/sim_model.jl +++ b/src/sim_model.jl @@ -365,7 +365,6 @@ to_mat(A::AbstractMatrix, _ ...) = A to_mat(A::Real, dims...) = fill(A, dims) include("model/linmodel.jl") -include("model/solver.jl") include("model/linearization.jl") include("model/nonlinmodel.jl") diff --git a/test/1_test_sim_model.jl b/test/1_test_sim_model.jl index f4a8907b4..6156c93b3 100644 --- a/test/1_test_sim_model.jl +++ b/test/1_test_sim_model.jl @@ -168,10 +168,10 @@ end @test nonlinmodel1.nu == 2 @test nonlinmodel1.nd == 0 @test nonlinmodel1.ny == 2 - xnext, k, y = nonlinmodel1.buffer.x, nonlinmodel1.buffer.k, nonlinmodel1.buffer.y - nonlinmodel1.solver_f!(xnext, k, [0,0],[0,0],[1],nonlinmodel1.p) + xnext, y = nonlinmodel1.buffer.x, nonlinmodel1.buffer.y + nonlinmodel1.f!(xnext, [0,0],[0,0],[1],nonlinmodel1.p) @test xnext ≈ zeros(2,) - nonlinmodel1.solver_h!(y,[0,0],[1],nonlinmodel1.p) + nonlinmodel1.h!(y,[0,0],[1],nonlinmodel1.p) @test y ≈ zeros(2,) linmodel2 = LinModel(sys,Ts,i_d=[3]) @@ -183,10 +183,10 @@ end @test nonlinmodel2.nu == 2 @test nonlinmodel2.nd == 1 @test nonlinmodel2.ny == 2 - xnext, k, y = nonlinmodel2.buffer.x, nonlinmodel2.buffer.k, nonlinmodel2.buffer.y - nonlinmodel2.solver_f!(xnext, k,[0,0,0,0],[0,0],[0],nonlinmodel2.p) + xnext, y = nonlinmodel2.buffer.x, nonlinmodel2.buffer.y + nonlinmodel2.f!(xnext,[0,0,0,0],[0,0],[0],nonlinmodel2.p) @test xnext ≈ zeros(4,) - nonlinmodel2.solver_h!(y,[0,0,0,0],[0],nonlinmodel2.p) + nonlinmodel2.h!(y,[0,0,0,0],[0],nonlinmodel2.p) @test y ≈ zeros(2,) nonlinmodel3 = NonLinModel{Float32}(f2,h2,Ts,2,4,2,1,solver=nothing) @@ -204,10 +204,10 @@ end return nothing end nonlinmodel4 = NonLinModel(f1!, h1!, Ts, 2, 4, 2, 1, solver=nothing, p=linmodel2) - xnext, k, y = nonlinmodel4.buffer.x, nonlinmodel4.buffer.k, nonlinmodel4.buffer.y - nonlinmodel4.solver_f!(xnext,k,[0,0,0,0],[0,0],[0],nonlinmodel4.p) + xnext, y = nonlinmodel4.buffer.x, nonlinmodel4.buffer.y + nonlinmodel4.f!(xnext,[0,0,0,0],[0,0],[0],nonlinmodel4.p) @test xnext ≈ zeros(4) - nonlinmodel4.solver_h!(y,[0,0,0,0],[0],nonlinmodel4.p) + nonlinmodel4.h!(y,[0,0,0,0],[0],nonlinmodel4.p) @test y ≈ zeros(2) A = [0 0.5; -0.2 -0.1] @@ -223,9 +223,9 @@ end "4th order Runge-Kutta differential equation solver with 1 supersamples." nonlinmodel5 = NonLinModel(f3, h3, 1.0, 1, 2, 1, 1, solver=solver, p=p) xnext, k, y = nonlinmodel5.buffer.x, nonlinmodel5.buffer.k, nonlinmodel5.buffer.y - nonlinmodel5.solver_f!(xnext,k, [0; 0], [0], [0], nonlinmodel5.p) + ModelPredictiveControl.f!(xnext, k, nonlinmodel5, [0; 0], [0], [0], nonlinmodel5.p) @test xnext ≈ zeros(2) - nonlinmodel5.solver_h!(y, [0; 0], [0], nonlinmodel5.p) + ModelPredictiveControl.h!(y, nonlinmodel5, [0; 0], [0], nonlinmodel5.p) @test y ≈ zeros(1) function f2!(ẋ, x, u , d, p) @@ -241,15 +241,15 @@ end end nonlinmodel6 = NonLinModel(f2!, h2!, 1.0, 1, 2, 1, 1, solver=RungeKutta(), p=p) xnext, k, y = nonlinmodel6.buffer.x, nonlinmodel6.buffer.k, nonlinmodel6.buffer.y - nonlinmodel6.solver_f!(xnext,k, [0; 0], [0], [0], nonlinmodel6.p) + ModelPredictiveControl.f!(xnext, k, nonlinmodel6, [0; 0], [0], [0], nonlinmodel6.p) @test xnext ≈ zeros(2) - nonlinmodel6.solver_h!(y, [0; 0], [0], nonlinmodel6.p) + ModelPredictiveControl.h!(y, nonlinmodel6, [0; 0], [0], nonlinmodel6.p) @test y ≈ zeros(1) nonlinmodel7 = NonLinModel(f2!, h2!, 1.0, 1, 2, 1, 1, solver=ForwardEuler(), p=p) xnext, k, y = nonlinmodel7.buffer.x, nonlinmodel7.buffer.k, nonlinmodel7.buffer.y - nonlinmodel7.solver_f!(xnext, k, [0; 0], [0], [0], nonlinmodel7.p) + ModelPredictiveControl.f!(xnext, k, nonlinmodel7, [0; 0], [0], [0], nonlinmodel7.p) @test xnext ≈ zeros(2) - nonlinmodel7.solver_h!(y, [0; 0], [0], nonlinmodel7.p) + ModelPredictiveControl.h!(y, nonlinmodel7, [0; 0], [0], nonlinmodel7.p) @test y ≈ zeros(1) nonlinmodel8 = NonLinModel(f2!, h2!, 1.0, 1, 2, 1, 1, p=p, jacobian=AutoFiniteDiff()) @test nonlinmodel8.jacobian == AutoFiniteDiff() @@ -327,18 +327,18 @@ end nonlinmodel3 = NonLinModel(f1!,h1!,Ts,1,1,1,1,solver=RungeKutta()) linmodel3 = linearize(nonlinmodel3; x, u, d) x0, u0, d0 = x - nonlinmodel3.xop, u - nonlinmodel3.uop, d - nonlinmodel3.dop - xnext, k, y = nonlinmodel3.buffer.x, nonlinmodel3.buffer.k, nonlinmodel3.buffer.y + x0next, k0, y0 = nonlinmodel3.buffer.x, nonlinmodel3.buffer.k, nonlinmodel3.buffer.y backend = AutoForwardDiff() - f_A(xnext, x0, k) = nonlinmodel3.solver_f!(xnext, k, x0, u0, d0, nonlinmodel3.p) - f_Bu(xnext, u0, k) = nonlinmodel3.solver_f!(xnext, k, x0, u0, d0, nonlinmodel3.p) - f_Bd(xnext, d0, k) = nonlinmodel3.solver_f!(xnext, k, x0, u0, d0, nonlinmodel3.p) - h_C(y, x0) = nonlinmodel3.solver_h!(y, x0, d0, nonlinmodel3.p) - h_Dd(y, d0) = nonlinmodel3.solver_h!(y, x0, d0, nonlinmodel3.p) - A = jacobian(f_A, xnext, backend, x0, Cache(k)) - Bu = jacobian(f_Bu, xnext, backend, u0, Cache(k)) - Bd = jacobian(f_Bd, xnext, backend, d0, Cache(k)) - C = jacobian(h_C, y, backend, x0) - Dd = jacobian(h_Dd, y, backend, d0) + f_A(x0next, x0, k0) = ModelPredictiveControl.f!(x0next, k0, nonlinmodel3, x0, u0, d0, nonlinmodel3.p) + f_Bu(x0next, u0, k0) = ModelPredictiveControl.f!(x0next, k0, nonlinmodel3, x0, u0, d0, nonlinmodel3.p) + f_Bd(x0next, d0, k0) = ModelPredictiveControl.f!(x0next, k0, nonlinmodel3, x0, u0, d0, nonlinmodel3.p) + h_C(y0, x0) = ModelPredictiveControl.h!(y0, nonlinmodel3, x0, d0, nonlinmodel3.p) + h_Dd(y0, d0) = ModelPredictiveControl.h!(y0, nonlinmodel3, x0, d0, nonlinmodel3.p) + A = jacobian(f_A, x0next, backend, x0, Cache(k0)) + Bu = jacobian(f_Bu, x0next, backend, u0, Cache(k0)) + Bd = jacobian(f_Bd, x0next, backend, d0, Cache(k0)) + C = jacobian(h_C, y0, backend, x0) + Dd = jacobian(h_Dd, y0, backend, d0) @test linmodel3.A ≈ A @test linmodel3.Bu ≈ Bu @test linmodel3.Bd ≈ Bd @@ -367,8 +367,8 @@ end @test all(isapprox.(Ynl, Yl, atol=1e-6)) # test nd==0 also works with AutoFiniteDiff (does not support empty matrices): - f2!(xnext, x, u, _, _) = (xnext .= x .+ u) - h2!(y, x, _, _) = (y .= x) + f2!(x0next, x0, u0, _, _) = (x0next .= x0 .+ u0) + h2!(y0, x0, _, _) = (y0 .= x0) nonlinmodel4 = NonLinModel(f2!,h2!,Ts,1,1,1,0,solver=nothing,jacobian=AutoFiniteDiff()) @test_nowarn linearize(nonlinmodel4, x=[1], u=[2]) end diff --git a/test/3_test_predictive_control.jl b/test/3_test_predictive_control.jl index f8ffa4282..c04f6dbc7 100644 --- a/test/3_test_predictive_control.jl +++ b/test/3_test_predictive_control.jl @@ -798,7 +798,7 @@ end nonlinmodel2 = NonLinModel{Float32}(f, h, 3000.0, 1, 2, 1, 1, solver=nothing, p=linmodel2) nmpc7 = NonLinMPC(nonlinmodel2, Hp=10) y = similar(nonlinmodel2.yop) - nonlinmodel2.solver_h!(y, Float32[0,0], Float32[0], nonlinmodel2.p) + ModelPredictiveControl.h!(y, nonlinmodel2, Float32[0,0], Float32[0], nonlinmodel2.p) preparestate!(nmpc7, [0], [0]) @test moveinput!(nmpc7, [0], [0]) ≈ [0.0] atol=5e-2 nmpc8 = NonLinMPC(nonlinmodel, Nwt=[0], Hp=100, Hc=1, transcription=MultipleShooting()) From 0cd7d180caabd00c651ba2163ed954e6874d7fbb Mon Sep 17 00:00:00 2001 From: franckgaga Date: Fri, 22 Aug 2025 21:16:18 -0400 Subject: [PATCH 2/2] debug: printing of the `DiffSolver` name --- src/model/nonlinmodel.jl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/model/nonlinmodel.jl b/src/model/nonlinmodel.jl index a001010b0..1015c0fae 100644 --- a/src/model/nonlinmodel.jl +++ b/src/model/nonlinmodel.jl @@ -152,7 +152,7 @@ julia> f!(ẋ, x, u, _ , p) = (ẋ .= p*x .+ u; nothing); julia> h!(y, x, _ , _ ) = (y .= 0.1x; nothing); julia> model1 = NonLinModel(f!, h!, 5.0, 1, 1, 1, p=-0.2) # continuous dynamics -NonLinModel with a sample time Ts = 5.0 s, RungeKutta{4} solver and: +NonLinModel with a sample time Ts = 5.0 s, RungeKutta(4) solver and: 1 manipulated inputs u 1 states x 1 outputs y @@ -307,5 +307,8 @@ h!(y0, model::NonLinModel, x0, d0, p) = model.h!(y0, x0, d0, p) include("solver.jl") -detailstr(model::NonLinModel) = ", $(typeof(model.solver)) solver" -detailstr(::NonLinModel{<:Real, <:EmptySolver}) = ", empty solver" \ No newline at end of file +function detailstr(model::NonLinModel{<:Real, <:RungeKutta{N}}) where N + return ", $(nameof(typeof(model.solver)))($N) solver" +end +detailstr(::NonLinModel{<:Real, <:EmptySolver}) = ", empty solver" +detailstr(::NonLinModel) = "" \ No newline at end of file