Skip to content

Commit 55ed356

Browse files
authored
Merge pull request #491 from JuliaControl/imc_dae
added: `InternalModel` now supports `NonLinModelDAE`
2 parents b8c9964 + 900d0d6 commit 55ed356

3 files changed

Lines changed: 57 additions & 20 deletions

File tree

‎Project.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "ModelPredictiveControl"
22
uuid = "61f9bdb8-6ae4-484a-811f-bbf86720c31c"
3-
version = "2.15.1"
3+
version = "2.16.0"
44
authors = ["Francis Gagnon"]
55

66
[deps]

‎src/estimator/internal_model.jl‎

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
struct InternalModel{NT<:Real, SM<:SimModelODE} <: StateEstimator{NT}
1+
struct InternalModel{NT<:Real, SM<:SimModel} <: StateEstimator{NT}
22
model::SM
33
x̂op::Vector{NT}
44
f̂op::Vector{NT}
@@ -30,8 +30,9 @@ struct InternalModel{NT<:Real, SM<:SimModelODE} <: StateEstimator{NT}
3030
buffer::StateEstimatorBuffer{NT}
3131
function InternalModel{NT}(
3232
model::SM, i_ym, Asm, Bsm, Csm, Dsm
33-
) where {NT<:Real, SM<:SimModelODE}
34-
nu, ny, nd, nk̄ = model.nu, model.ny, model.nd, model.nk̄
33+
) where {NT<:Real, SM<:SimModel}
34+
nu, ny, nd = model.nu, model.ny, model.nd
35+
nk̄ = get_nk̄(model, default_transcription(model))
3536
nym, nyu = validate_ym(model, i_ym)
3637
validate_internalmodel(model, nym, Csm, Dsm)
3738
As, Bs, Cs, Ds = stoch_ym2y(model, i_ym, Asm, Bsm, Csm, Dsm)
@@ -61,11 +62,12 @@ struct InternalModel{NT<:Real, SM<:SimModelODE} <: StateEstimator{NT}
6162
end
6263

6364
@doc raw"""
64-
InternalModel(model::SimModelODE; i_ym=1:model.ny, stoch_ym=ss(I,I,I,I,model.Ts))
65+
InternalModel(model::SimModel; i_ym=1:model.ny, stoch_ym=ss(I,I,I,I,model.Ts))
6566
66-
Construct an internal model estimator based on `model` ([`LinModel`](@ref) or [`NonLinModel`](@ref)).
67+
Construct an internal model estimator based on `model`.
6768
68-
`i_ym` provides the `model` output indices that are measured ``\mathbf{y^m}``, the rest are
69+
It supports [`LinModel`](@ref), [`NonLinModel`](@ref) and [`NonLinModelDAE`](@ref). `i_ym`
70+
provides the `model` output indices that are measured ``\mathbf{y^m}``, the rest are
6971
unmeasured ``\mathbf{y^u}``. `model` evaluates the deterministic predictions
7072
``\mathbf{ŷ_d}``, and `stoch_ym`, the stochastic predictions of the measured outputs
7173
``\mathbf{ŷ_s^m}`` (the unmeasured ones being ``\mathbf{ŷ_s^u=0}``). The predicted outputs
@@ -74,8 +76,8 @@ estimator is allocation-free if `model` simulations do not allocate.
7476
7577
!!! warning
7678
`InternalModel` estimator does not work if `model` is integrating or unstable. The
77-
constructor verifies these aspects for `LinModel` but not for `NonLinModel`. Uses any
78-
other state estimator in such cases.
79+
constructor verifies these aspects only for `LinModel` types. Uses any other state
80+
estimator in such cases.
7981
8082
# Examples
8183
```jldoctest
@@ -86,6 +88,7 @@ InternalModel estimator with a sample time Ts = 0.5 s:
8688
└ dimensions:
8789
├ 1 manipulated inputs u
8890
├ 2 estimated states x̂
91+
├ 0 algebraic variables a
8992
├ 1 measured outputs ym
9093
├ 1 unmeasured outputs yu
9194
└ 0 measured disturbances d
@@ -108,7 +111,7 @@ function InternalModel(
108111
model::SM;
109112
i_ym::AbstractVector{Int} = 1:model.ny,
110113
stoch_ym::LTISystem = (In = I(length(i_ym)); ss(In, In, In, In, model.Ts))
111-
) where {NT<:Real, SM<:SimModelODE{NT}}
114+
) where {NT<:Real, SM<:SimModel{NT}}
112115
stoch_ym = minreal(ss(stoch_ym))
113116
if iscontinuous(stoch_ym)
114117
stoch_ym = c2d(stoch_ym, model.Ts, :tustin)
@@ -124,7 +127,7 @@ function InternalModel(
124127
end
125128

126129
"Validate if deterministic `model` and stochastic model `Csm, Dsm` for `InternalModel`s."
127-
function validate_internalmodel(model::SimModelODE, nym, Csm, Dsm)
130+
function validate_internalmodel(model::SimModel, nym, Csm, Dsm)
128131
validate_poles(model)
129132
if size(Csm,1) ≠ nym || size(Dsm,1) ≠ nym
130133
error("Stochastic model output quantity ($(size(Csm,1))) is different from "*
@@ -144,7 +147,7 @@ function validate_poles(model::LinModel)
144147
end
145148
return nothing
146149
end
147-
validate_poles(::SimModelODE) = nothing
150+
validate_poles(::SimModel) = nothing
148151

149152
@doc raw"""
150153
matrices_internalmodel(model::LinModel) -> Â, B̂u, Ĉ, B̂d, D̂d, x̂op, f̂op
@@ -162,35 +165,41 @@ function matrices_internalmodel(model::LinModel)
162165
return Â, B̂u, Ĉ, B̂d, D̂d, x̂op, f̂op
163166
end
164167
"Return empty matrices, and `x̂op` & `f̂op` vectors, if `model` is not a [`LinModel`](@ref)."
165-
function matrices_internalmodel(model::SimModelODE{NT}) where NT<:Real
168+
function matrices_internalmodel(model::SimModel{NT}) where NT<:Real
166169
nu, nx, nd, ny = model.nu, model.nx, model.nd, model.ny
167170
Â, B̂u, Ĉ, B̂d, D̂d = zeros(NT,0,nx), zeros(NT,0,nu), zeros(NT,ny,0), zeros(NT,0,nd), zeros(NT,ny,0)
168171
x̂op, f̂op = copy(model.xop), copy(model.fop)
169172
return Â, B̂u, Ĉ, B̂d, D̂d, x̂op, f̂op
170173
end
171174

172175
@doc raw"""
173-
f̂!(x̂0next, _ , k, estim::InternalModel, model::NonLinModel, x̂0, u0, d0)
176+
f̂!(x̂0next, _ , k̄, estim::InternalModel, model::SimModel, x̂0, u0, d0)
174177
175-
State update function ``\mathbf{f̂}`` of [`InternalModel`](@ref) for [`NonLinModel`](@ref).
178+
State update function ``\mathbf{f̂}`` of [`InternalModel`](@ref).
176179
177180
It calls [`f!`](@ref) directly since this estimator does not augment the states.
178181
"""
179-
function f̂!(x̂0next, _ , k, estim::InternalModel, model::NonLinModel, x̂0, u0, d0)
180-
f!(x̂0next, k, model, x̂0, u0, d0, model.p)
182+
function f̂!(x̂0next, _ , k̄, estim::InternalModel, model::SimModel, x̂0, u0, d0)
183+
f!(x̂0next, k̄, model, x̂0, u0, d0, model.p)
184+
x̂0next .+= estim.f̂op .- estim.x̂op
185+
return nothing
186+
end
187+
function f̂!(x̂0next, _ , k̄, estim::InternalModel, model::LinModel, x̂0, u0, d0)
188+
f!(x̂0next, k̄, model, x̂0, u0, d0, model.p)
181189
x̂0next .+= estim.f̂op .- estim.x̂op
182190
return nothing
183191
end
184192

185193
@doc raw"""
186-
ĥ!(ŷ0, estim::InternalModel, model::NonLinModel, x̂0, d0)
194+
ĥ!(ŷ0, estim::InternalModel, model::SimModel, x̂0, d0)
187195
188196
Output function ``\mathbf{ĥ}`` of [`InternalModel`](@ref), it calls [`h!`](@ref).
189197
"""
190-
ĥ!(ŷ0, ::InternalModel, model::NonLinModel, x̂0, d0) = h!(ŷ0, model, x̂0, d0, model.p)
198+
ĥ!(ŷ0, ::InternalModel, model::SimModel, x̂0, d0) = h!(ŷ0, model, x̂0, d0, model.p)
199+
ĥ!(ŷ0, ::InternalModel, model::LinModel, x̂0, d0) = h!(ŷ0, model, x̂0, d0, model.p)
191200

192201
@doc raw"""
193-
ĥ!(ŷ0, estim::InternalModel, model::NonLinModel, x̂0, â0, d0)
202+
ĥ!(ŷ0, estim::InternalModel, model::SimModel, x̂0, â0, d0)
194203
195204
Same, but calls [`h_dae!`](@ref) instead.
196205
"""
@@ -375,10 +384,12 @@ end
375384
"Print InternalModel information without i/o integrators."
376385
function print_estim_dim(io::IO, estim::InternalModel, n; firstchars=" ")
377386
nu, nd = estim.model.nu, estim.model.nd
387+
na = get_na(estim.model)
378388
nx̂, nym, nyu = estim.nx̂, estim.nym, estim.nyu
379389
f = firstchars
380390
println(io, "$f ├$(lpad(nu, n)) manipulated inputs u")
381391
println(io, "$f ├$(lpad(nx̂, n)) estimated states x̂")
392+
println(io, "$f ├$(lpad(na, n)) algebraic variables a")
382393
println(io, "$f ├$(lpad(nym, n)) measured outputs ym")
383394
println(io, "$f ├$(lpad(nyu, n)) unmeasured outputs yu")
384395
print(io, "$f └$(lpad(nd, n)) measured disturbances d")

‎test/2_test_state_estim.jl‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,17 @@ end
467467
internalmodel8 = InternalModel(linmodel3)
468468
@test isa(internalmodel8, InternalModel{Float32})
469469

470+
function fq_dae!(ẋ, res, x, a, u, d, p)
471+
ẋ[] = -p[] * (x[] - 0.2 * u[] - 0.2 * d[])
472+
res[] = x[] - a[]
473+
return nothing
474+
end
475+
function h_dae!(y, x, a, d, _ )
476+
y[] = 2 * x[] + a[] + 0.1 * d[]
477+
end
478+
dae = NonLinModelDAE(fq_dae!, h_dae!, 100.0, 1, 1, 1, 1, 1; p=[0.01])
479+
internalmodel9 = InternalModel(dae)
480+
470481
unstablemodel = LinModel(ss(diagm([0.5, -0.5, 1.5]), ones(3,1), I, 0, 1))
471482
@test_throws ErrorException InternalModel(unstablemodel)
472483
@test_throws ErrorException InternalModel(linmodel, i_ym=[1,4])
@@ -522,6 +533,21 @@ end
522533
preparestate!(internalmodel4, [50+7, NaN])
523534
)
524535
@test internalmodel4.ŷs ≈ [7, 0]
536+
function fq_dae!(ẋ, res, x, a, u, d, p)
537+
ẋ[] = -p[] * (x[] - 0.2 * u[] - 0.2 * d[])
538+
res[] = x[] - a[]
539+
return nothing
540+
end
541+
function h_dae!(y, x, a, d, _ )
542+
y[] = 2 * x[] + a[] + 0.1 * d[]
543+
end
544+
dae = NonLinModelDAE(fq_dae!, h_dae!, 100.0, 1, 1, 1, 1, 1; p=[0.01])
545+
internalmodel5 = InternalModel(dae)
546+
initstate!(internalmodel5, [0], [0], [0])
547+
preparestate!(internalmodel5, [6], [0])
548+
updatestate!(internalmodel5, [0], [6], [0])
549+
@test internalmodel5.x̂d ≈ [0]
550+
@test internalmodel5.x̂s ≈ [6]
525551
end
526552

527553
@testitem "IM set model" setup=[SetupMPCtests] begin

0 commit comments

Comments
 (0)