@@ -78,6 +78,7 @@ struct NonLinMPC{
7878 # dummy vals (updated just before optimization):
7979 d0, D̂0, D̂e = zeros (NT, nd), zeros (NT, nd* Hp), zeros (NT, nd + nd* Hp)
8080 Uop, Yop, Dop = repeat (model. uop, Hp), repeat (model. yop, Hp), repeat (model. dop, Hp)
81+ test_custom_functions (NT, model, JE, gc!, nc, Uop, Yop, Dop, p)
8182 nΔŨ = size (Ẽ, 2 )
8283 ΔŨ = zeros (NT, nΔŨ)
8384 buffer = PredictiveControllerBuffer {NT} (nu, ny, nd, Hp)
@@ -131,16 +132,15 @@ include the manipulated inputs, predicted outputs and measured disturbances, ext
131132 \m athbf{Ŷ_e} = \b egin{bmatrix} \m athbf{ŷ}(k) \\ \m athbf{Ŷ} \e nd{bmatrix} , \q uad
132133 \m athbf{D̂_e} = \b egin{bmatrix} \m athbf{d}(k) \\ \m athbf{D̂} \e nd{bmatrix}
133134```
134- The vector ``\m athbf{D̂ }`` comprises the measured disturbance predictions over ``H_p``. The
135- argument `` \m athbf{p}`` is a custom parameter object of any type, but use a mutable one if
136- you want to modify it later e.g.: a vector .
135+ The argument ``\m athbf{p }`` is a custom parameter object of any type, but use a mutable one
136+ if you want to modify it later e.g.: a vector. See [`LinMPC`](@ref) Extended Help for the
137+ definition of the other variables .
137138
138139!!! tip
139140 Replace any of the arguments of ``J_E`` and ``\m athbf{g_c}`` functions with `_` if not
140141 needed (see e.g. the default value of `JE` below).
141142
142- See [`LinMPC`](@ref) Extended Help for the definition of the other variables. This method
143- uses the default state estimator :
143+ This method uses the default state estimator:
144144
145145- if `model` is a [`LinModel`](@ref), a [`SteadyKalmanFilter`](@ref) with default arguments;
146146- else, an [`UnscentedKalmanFilter`](@ref) with default arguments.
@@ -198,11 +198,17 @@ NonLinMPC controller with a sample time Ts = 10.0 s, Ipopt optimizer, UnscentedK
198198
199199 The economic cost ``J_E`` and custom constraint ``\m athbf{g_c}`` functions receive the
200200 extended vectors ``\m athbf{U_e}`` (`nu*Hp+nu` elements), ``\m athbf{Ŷ_e}`` (`ny+ny*Hp`
201- elements) and ``\m athbf{D̂_e}`` (`nd+nd*Hp` elements) as arguments. The last two time
202- steps in ``\m athbf{U_e}`` are forced to be equal, that is ``\m athbf{u}(k+H_p) =
203- \m athbf{u}(k+H_p-1)``, since ``H_c ≤ H_p`` implies that ``\m athbf{Δu}(k+H_p) =
204- \m athbf{0}``. If `LHS` represents the result of the left-hand side in the inequality
205- ``\m athbf{g_c}(\m athbf{U_e}, \m athbf{Ŷ_e}, \m athbf{D̂_e}, \m athbf{p}, ϵ) ≤ \m athbf{0}``,
201+ elements) and ``\m athbf{D̂_e}`` (`nd+nd*Hp` elements) as arguments. They all include the
202+ values from ``k`` to ``k + H_p`` (inclusively). The custom constraint also receives the
203+ slack ``ϵ`` (scalar), which is always zero if `Cwt=Inf`.
204+
205+ More precisely, the last two time steps in ``\m athbf{U_e}`` are forced to be equal, i.e.
206+ ``\m athbf{u}(k+H_p) = \m athbf{u}(k+H_p-1)``, since ``H_c ≤ H_p`` implies that
207+ ``\m athbf{Δu}(k+H_p) = \m athbf{0}``. The vectors ``\m athbf{ŷ}(k)`` and ``\m athbf{d}(k)``
208+ are the current state estimator output and measured disturbance, respectively, and
209+ ``\m athbf{Ŷ}`` and ``\m athbf{D̂}``, their respective predictions from ``k+1`` to ``k+H_p``.
210+ If `LHS` represents the result of the left-hand side in the inequality
211+ ``\m athbf{g_c}(\m athbf{U_e}, \m athbf{Ŷ_e}, \m athbf{D̂_e}, \m athbf{p}, ϵ) ≤ \m athbf{0}``,
206212 the function `gc` can be implemented in two possible ways:
207213
208214 1. **Non-mutating function** (out-of-place): define it as `gc(Ue, Ŷe, D̂e, p, ϵ) -> LHS`.
@@ -388,25 +394,34 @@ function get_mutating_gc(NT, gc)
388394 return gc!
389395end
390396
391- function test_custom_functions (JE, gc!, uop; Uop, dop, Dop, ΔŨ, p)
392- # TODO : contunue here (important to guide the user, sim! can be used on NonLinModel,
393- # but there is no similar function for the custom functions of NonLinMPC)
394- Ue = [Uop; uop]
395- D̂e = [dop; Dop]
396-
397- Ŷ0, x̂0end = predict! (Ȳ, x̂0, x̂0next, u0, û0, mpc, model, ΔŨ)
398- Ŷe, Ue = extended_predictions! (Ŷe, Ue, Ū, mpc, model, Ŷ0, ΔŨ)
399- ϵ = (nϵ == 1 ) ? ΔŨ[end ] : zero (JNT) # ϵ = 0 if nϵ == 0 (meaning no relaxation)
400- mpc. con. gc! (gc, Ue, Ŷe, mpc. D̂e, mpc. p, ϵ)
401- g = con_nonlinprog! (g, mpc, model, x̂0end, Ŷ0, gc, ϵ)
402- J = obj_nonlinprog! (Ȳ, Ū, mpc, model, Ŷe, Ue, ΔŨ)
403-
404-
405-
406-
407- Ŷ0, x̂0next =
408- Ŷ0, x̂0end = predict! (Ŷ0, x̂0, x̂0next, u0, û0, mpc, model, mpc. ΔŨ)
409- JE = JE (Uop, Uop, Dop, p)
397+ function test_custom_functions (NT, model:: SimModel , JE, gc!, nc, Uop, Yop, Dop, p)
398+ uop, dop, yop = model. uop, model. dop, model. yop
399+ Ue, Ŷe, D̂e = [Uop; uop], [yop; Yop], [dop; Dop]
400+ try
401+ JE (Ue, Ŷe, D̂e, p)
402+ catch err
403+ @warn (
404+ """
405+ Calling the JE function with Ue, Ŷe, D̂e arguments fixed at uop=$uop ,
406+ yop=$yop , dop=$dop failed with the following stacktrace.
407+ """ ,
408+ exception= (err, catch_backtrace ())
409+ )
410+ end
411+ ϵ, gc = 0 , Vector {NT} (undef, nc)
412+ try
413+ gc! (gc, Ue, Ŷe, D̂e, p, ϵ)
414+ catch err
415+ @warn (
416+ """
417+ Calling the gc function with Ue, Ŷe, D̂e, ϵ arguments fixed at uop=$uop ,
418+ yop=$yop , dop=$dop , ϵ=0 failed with the following stacktrace. Did you
419+ forget to set the keyword argument nc?
420+ """ ,
421+ exception= (err, catch_backtrace ())
422+ )
423+ end
424+ return nothing
410425end
411426
412427"""
@@ -492,13 +507,13 @@ function get_optim_functions(mpc::NonLinMPC, ::JuMP.GenericModel{JNT}) where JNT
492507 Ȳ, Ū = get_tmp (Ȳ_cache, ΔŨ1), get_tmp (Ū_cache, ΔŨ1)
493508 x̂0, x̂0next = get_tmp (x̂0_cache, ΔŨ1), get_tmp (x̂0next_cache, ΔŨ1)
494509 u0, û0 = get_tmp (u0_cache, ΔŨ1), get_tmp (û0_cache, ΔŨ1)
495- g, gc = get_tmp (g_cache, ΔŨ1), get_tmp (gc_cache, ΔŨ1)
510+ gc = get_tmp (gc_cache, ΔŨ1)
496511 Ŷ0, x̂0end = predict! (Ȳ, x̂0, x̂0next, u0, û0, mpc, model, ΔŨ)
497- Ŷe, Ue = extended_predictions! (Ŷe, Ue , Ū, mpc, model, Ŷ0, ΔŨ)
498- ϵ = (nϵ == 1 ) ? ΔŨ[end ] : zero (JNT ) # ϵ = 0 if nϵ == 0 (meaning no relaxation)
512+ Ue, Ŷe = extended_predictions! (Ue, Ŷe , Ū, mpc, model, Ŷ0, ΔŨ)
513+ ϵ = (nϵ ≠ 0 ) ? ΔŨ[end ] : zero (T ) # ϵ = 0 if nϵ == 0 (meaning no relaxation)
499514 mpc. con. gc! (gc, Ue, Ŷe, mpc. D̂e, mpc. p, ϵ)
500515 g = con_nonlinprog! (g, mpc, model, x̂0end, Ŷ0, gc, ϵ)
501- return obj_nonlinprog! (Ȳ, Ū, mpc, model, Ŷe, Ue , ΔŨ):: T
516+ return obj_nonlinprog! (Ȳ, Ū, mpc, model, Ue, Ŷe , ΔŨ):: T
502517 end
503518 function gfunc_i (i, ΔŨtup:: NTuple{N, T} ) where {N, T<: Real }
504519 ΔŨ1 = ΔŨtup[begin ]
@@ -511,10 +526,10 @@ function get_optim_functions(mpc::NonLinMPC, ::JuMP.GenericModel{JNT}) where JNT
511526 Ȳ, Ū = get_tmp (Ȳ_cache, ΔŨ1), get_tmp (Ū_cache, ΔŨ1)
512527 x̂0, x̂0next = get_tmp (x̂0_cache, ΔŨ1), get_tmp (x̂0next_cache, ΔŨ1)
513528 u0, û0 = get_tmp (u0_cache, ΔŨ1), get_tmp (û0_cache, ΔŨ1)
514- g, gc = get_tmp (g_cache, ΔŨ1), get_tmp (gc_cache, ΔŨ1)
529+ gc = get_tmp (gc_cache, ΔŨ1)
515530 Ŷ0, x̂0end = predict! (Ȳ, x̂0, x̂0next, u0, û0, mpc, model, ΔŨ)
516- Ŷe, Ue = extended_predictions! (Ŷe, Ue , Ū, mpc, model, Ŷ0, ΔŨ)
517- ϵ = (nϵ == 1 ) ? ΔŨ[end ] : zero (JNT ) # ϵ = 0 if nϵ == 0 (meaning no relaxation)
531+ Ue, Ŷe = extended_predictions! (Ue, Ŷe , Ū, mpc, model, Ŷ0, ΔŨ)
532+ ϵ = (nϵ ≠ 0 ) ? ΔŨ[end ] : zero (T ) # ϵ = 0 if nϵ == 0 (meaning no relaxation)
518533 mpc. con. gc! (gc, Ue, Ŷe, mpc. D̂e, mpc. p, ϵ)
519534 g = con_nonlinprog! (g, mpc, model, x̂0end, Ŷ0, gc, ϵ)
520535 end
@@ -672,7 +687,7 @@ function con_nonlinprog!(g, mpc::NonLinMPC, ::SimModel, x̂0end, Ŷ0, gc, ϵ)
672687end
673688
674689" Evaluate the economic term `E*JE` of the objective function for [`NonLinMPC`](@ref)."
675- function obj_econ! (Ue, Ŷe, mpc:: NonLinMPC , model:: SimModel )
690+ function obj_econ ( mpc:: NonLinMPC , model:: SimModel , Ue, Ŷe )
676691 E_JE = iszero (mpc. E) ? 0.0 : mpc. E* mpc. JE (Ue, Ŷe, mpc. D̂e, mpc. p)
677692 return E_JE
678693end
0 commit comments