Skip to content

Commit 7c71df1

Browse files
authored
Merge pull request #33 from JuliaControl/mutating_nonlinmodel
Added: mutating `NonLinModel`
2 parents 0590901 + dc39a89 commit 7c71df1

File tree

17 files changed

+308
-134
lines changed

17 files changed

+308
-134
lines changed

.github/workflows/CI.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,4 @@ jobs:
3636
- uses: julia-actions/julia-buildpkg@v1
3737
- uses: julia-actions/julia-runtest@v1
3838
- uses: julia-actions/julia-processcoverage@v1
39-
- uses: codecov/codecov-action@v3
39+
- uses: codecov/codecov-action@v4

docs/src/internals/sim_model.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ Pages = ["sim_model.md"]
77
## State-Space Functions
88

99
```@docs
10-
ModelPredictiveControl.f
11-
ModelPredictiveControl.h
10+
ModelPredictiveControl.f!
11+
ModelPredictiveControl.h!
1212
```
1313

1414
## Steady-State Calculation

docs/src/internals/state_estim.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ Pages = ["state_estim.md"]
77
## Augmented Model
88

99
```@docs
10-
ModelPredictiveControl.f̂
11-
ModelPredictiveControl.ĥ
10+
ModelPredictiveControl.f̂!
11+
ModelPredictiveControl.ĥ!
1212
```
1313

1414
## Constraint Relaxation

src/controller/execute.jl

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,9 @@ function predictstoch!(
226226
isnothing(ym) && error("Predictive controllers with InternalModel need the measured "*
227227
"outputs ym in keyword argument to compute control actions u")
228228
Ŷop, ny, yop = mpc.Ŷop, estim.model.ny, estim.model.yop
229-
ŷd = h(estim.model, estim.x̂d, d - estim.model.dop) .+ estim.model.yop
229+
ŷd = similar(estim.model.yop)
230+
h!(ŷd, estim.model, estim.x̂d, d - estim.model.dop)
231+
ŷd .+= estim.model.yop
230232
ŷs = zeros(NT, estim.model.ny)
231233
ŷs[estim.i_ym] .= @views ym .- ŷd[estim.i_ym] # ŷs=0 for unmeasured outputs
232234
Ŷop_LHS = similar(Ŷop)
@@ -325,15 +327,18 @@ function predict!(
325327
nu, ny, nd, Hp, Hc = model.nu, model.ny, model.nd, mpc.Hp, mpc.Hc
326328
u0 = u
327329
x̂ .= mpc.estim.
330+
x̂next = similar(x̂) # TODO: avoid this allocation if possible
328331
u0 .= mpc.estim.lastu0
329332
d0 = @views mpc.d0[1:end]
330333
for j=1:Hp
331334
if j Hc
332335
u0 .+= @views ΔŨ[(1 + nu*(j-1)):(nu*j)]
333336
end
334-
x̂[:] = (mpc.estim, model, x̂, u0, d0)
335-
d0 = @views mpc.D̂0[(1 + nd*(j-1)):(nd*j)]
336-
Ŷ[(1 + ny*(j-1)):(ny*j)] = (mpc.estim, model, x̂, d0)
337+
f̂!(x̂next, mpc.estim, model, x̂, u0, d0)
338+
x̂ .= x̂next
339+
d0 = @views mpc.D̂0[(1 + nd*(j-1)):(nd*j)]
340+
= @views Ŷ[(1 + ny*(j-1)):(ny*j)]
341+
ĥ!(ŷ, mpc.estim, model, x̂, d0)
337342
end
338343
Ŷ .=.+ mpc.Ŷop # Ŷop = Ŷs + Yop, and Ŷs=0 if mpc.estim is not an InternalModel
339344
end =

src/estimator/execute.jl

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ function remove_op!(estim::StateEstimator, u, ym, d)
1515
end
1616

1717
@doc raw"""
18-
(estim::StateEstimator, model::SimModel, x̂, u, d)
18+
!(x̂next, estim::StateEstimator, model::SimModel, x̂, u, d) -> x̂next
1919
20-
State function ``\mathbf{f̂}`` of the augmented model.
20+
Mutating state function ``\mathbf{f̂}`` of the augmented model.
2121
2222
By introducing an augmented state vector ``\mathbf{x̂}`` like in [`augment_model`](@ref), the
2323
function returns the next state of the augmented model, defined as:
@@ -26,28 +26,48 @@ function returns the next state of the augmented model, defined as:
2626
\mathbf{x̂}(k+1) &= \mathbf{f̂}\Big(\mathbf{x̂}(k), \mathbf{u}(k), \mathbf{d}(k)\Big) \\
2727
\mathbf{ŷ}(k) &= \mathbf{ĥ}\Big(\mathbf{x̂}(k), \mathbf{d}(k)\Big)
2828
\end{aligned}
29+
where ``\mathbf{x̂}(k+1)`` is stored in `x̂next` argument.
2930
```
3031
"""
31-
function (estim::StateEstimator, model::SimModel, x̂, u, d)
32+
function !(x̂next, estim::StateEstimator, model::SimModel, x̂, u, d)
3233
# `@views` macro avoid copies with matrix slice operator e.g. [a:b]
3334
@views x̂d, x̂s = x̂[1:model.nx], x̂[model.nx+1:end]
34-
return [f(model, x̂d, u + estim.Cs_u*x̂s, d); estim.As*x̂s]
35+
@views x̂d_next, x̂s_next = x̂next[1:model.nx], x̂next[model.nx+1:end]
36+
T = promote_type(eltype(x̂), eltype(u), eltype(d))
37+
u_us = Vector{T}(undef, model.nu) # TODO: avoid this allocation if possible
38+
u_us .= u .+ mul!(u_us, estim.Cs_u, x̂s)
39+
f!(x̂d_next, model, x̂d, u_us, d)
40+
mul!(x̂s_next, estim.As, x̂s)
41+
return x̂next
3542
end
3643
"Use the augmented model matrices if `model` is a [`LinModel`](@ref)."
37-
(estim::StateEstimator, ::LinModel, x̂, u, d) = estim.*+ estim.B̂u * u + estim.B̂d * d
44+
function f̂!(x̂next, estim::StateEstimator, ::LinModel, x̂, u, d)
45+
x̂next .= 0
46+
mul!(x̂next, estim.Â, x̂, 1, 1)
47+
mul!(x̂next, estim.B̂u, u, 1, 1)
48+
mul!(x̂next, estim.B̂d, d, 1, 1)
49+
return x̂next
50+
end
3851

3952
@doc raw"""
40-
(estim::StateEstimator, model::SimModel, x̂, d)
53+
!(ŷ, estim::StateEstimator, model::SimModel, x̂, d) -> ŷ
4154
42-
Output function ``\mathbf{ĥ}`` of the augmented model, see [`f̂`](@ref) for details.
55+
Mutating output function ``\mathbf{ĥ}`` of the augmented model, see [`f̂!`](@ref).
4356
"""
44-
function (estim::StateEstimator, model::SimModel, x̂, d)
57+
function !(ŷ, estim::StateEstimator, model::SimModel, x̂, d)
4558
# `@views` macro avoid copies with matrix slice operator e.g. [a:b]
4659
@views x̂d, x̂s = x̂[1:model.nx], x̂[model.nx+1:end]
47-
return h(model, x̂d, d) + estim.Cs_y*x̂s
60+
h!(ŷ, model, x̂d, d)
61+
mul!(ŷ, estim.Cs_y, x̂s, 1, 1)
62+
return
4863
end
4964
"Use the augmented model matrices if `model` is a [`LinModel`](@ref)."
50-
(estim::StateEstimator, ::LinModel, x̂, d) = estim.*+ estim.D̂d * d
65+
function ĥ!(ŷ, estim::StateEstimator, ::LinModel, x̂, d)
66+
ŷ .= 0
67+
mul!(ŷ, estim.Ĉ, x̂, 1, 1)
68+
mul!(ŷ, estim.D̂d, d, 1, 1)
69+
return
70+
end
5171

5272

5373
@doc raw"""
@@ -147,8 +167,12 @@ julia> ŷ = evaloutput(kf)
147167
20.0
148168
```
149169
"""
150-
function evaloutput(estim::StateEstimator, d=empty(estim.x̂))
151-
return (estim, estim.model, estim.x̂, d - estim.model.dop) + estim.model.yop
170+
function evaloutput(estim::StateEstimator{NT}, d=empty(estim.x̂)) where NT <: Real
171+
validate_args(estim.model, d)
172+
= Vector{NT}(undef, estim.model.ny)
173+
ĥ!(ŷ, estim, estim.model, estim.x̂, d - estim.model.dop)
174+
.+= estim.model.yop
175+
return
152176
end
153177

154178
"Functor allowing callable `StateEstimator` object as an alias for `evaloutput`."
@@ -186,7 +210,7 @@ updatestate!(::StateEstimator, _ ) = throw(ArgumentError("missing measured outpu
186210
Check `u`, `ym` and `d` sizes against `estim` dimensions.
187211
"""
188212
function validate_args(estim::StateEstimator, u, ym, d)
189-
validate_args(estim.model, u, d)
213+
validate_args(estim.model, d, u)
190214
nym = estim.nym
191215
size(ym) (nym,) && throw(DimensionMismatch("ym size $(size(ym)) ≠ meas. output size ($nym,)"))
192216
end

src/estimator/internal_model.jl

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -144,20 +144,20 @@ function matrices_internalmodel(model::SimModel{NT}) where NT<:Real
144144
end
145145

146146
@doc raw"""
147-
(::InternalModel, model::NonLinModel, x̂, u, d)
147+
!(x̂next, ::InternalModel, model::NonLinModel, x̂, u, d)
148148
149149
State function ``\mathbf{f̂}`` of [`InternalModel`](@ref) for [`NonLinModel`](@ref).
150150
151-
It calls `model.f(x̂, u ,d)` since this estimator does not augment the states.
151+
It calls `model.f!(x̂next, x̂, u ,d)` since this estimator does not augment the states.
152152
"""
153-
(::InternalModel, model::NonLinModel, x̂, u, d) = model.f(x̂, u, d)
153+
!(x̂next, ::InternalModel, model::NonLinModel, x̂, u, d) = model.f!(x̂next, x̂, u, d)
154154

155155
@doc raw"""
156-
(::InternalModel, model::NonLinModel, x̂, d)
156+
!(ŷ, ::InternalModel, model::NonLinModel, x̂, d)
157157
158-
Output function ``\mathbf{ĥ}`` of [`InternalModel`](@ref), it calls `model.h`.
158+
Output function ``\mathbf{ĥ}`` of [`InternalModel`](@ref), it calls `model.h!`.
159159
"""
160-
(::InternalModel, model::NonLinModel, x̂, d) = model.h(x̂, d)
160+
!(x̂next, ::InternalModel, model::NonLinModel, x̂, d) = model.h!(x̂next, x̂, d)
161161

162162

163163
@doc raw"""
@@ -218,12 +218,17 @@ function update_estimate!(
218218
model = estim.model
219219
x̂d, x̂s = estim.x̂d, estim.x̂s
220220
# -------------- deterministic model ---------------------
221-
ŷd = h(model, x̂d, d)
222-
x̂d[:] = f(model, x̂d, u, d) # this also updates estim.x̂ (they are the same object)
221+
ŷd, x̂dnext = Vector{NT}(undef, model.ny), Vector{NT}(undef, model.nx)
222+
h!(ŷd, model, x̂d, d)
223+
f!(x̂dnext, model, x̂d, u, d)
224+
x̂d .= x̂dnext # this also updates estim.x̂ (they are the same object)
223225
# --------------- stochastic model -----------------------
226+
x̂snext = Vector{NT}(undef, estim.nxs)
224227
ŷs = zeros(NT, model.ny)
225228
ŷs[estim.i_ym] = ym - ŷd[estim.i_ym] # ŷs=0 for unmeasured outputs
226-
x̂s[:] = estim.Âs*x̂s + estim.B̂s*ŷs
229+
mul!(x̂snext, estim.Âs, x̂s)
230+
mul!(x̂snext, estim.B̂s, ŷs, 1, 1)
231+
x̂s .= x̂snext
227232
return nothing
228233
end
229234

@@ -247,14 +252,32 @@ This estimator does not augment the state vector, thus ``\mathbf{x̂ = x̂_d}``.
247252
"""
248253
function init_estimate!(estim::InternalModel, model::LinModel{NT}, u, ym, d) where NT<:Real
249254
x̂d, x̂s = estim.x̂d, estim.x̂s
250-
x̂d[:] = (I - model.A)\(model.Bu*u + model.Bd*d)
251-
ŷd = h(model, x̂d, d)
255+
x̂d .= (I - model.A)\(model.Bu*u + model.Bd*d)
256+
ŷd = Vector{NT}(undef, model.ny)
257+
h!(ŷd, model, x̂d, d)
252258
ŷs = zeros(NT, model.ny)
253259
ŷs[estim.i_ym] = ym - ŷd[estim.i_ym] # ŷs=0 for unmeasured outputs
254-
x̂s[:] = (I-estim.Âs)\estim.B̂s*ŷs
260+
x̂s .= (I-estim.Âs)\estim.B̂s*ŷs
255261
return nothing
256262
end
257263

264+
@doc raw"""
265+
evalŷ(estim::InternalModel, ym, d) -> ŷ
266+
267+
Get [`InternalModel`](@ref) output `ŷ` from current measured outputs `ym` and dist. `d`.
268+
269+
[`InternalModel`](@ref) estimator needs current measured outputs ``\mathbf{y^m}(k)`` to
270+
estimate its outputs ``\mathbf{ŷ}(k)``, since the strategy imposes that
271+
``\mathbf{ŷ^m}(k) = \mathbf{y^m}(k)`` is always true.
272+
"""
273+
function evalŷ(estim::InternalModel{NT}, ym, d) where NT<:Real
274+
= Vector{NT}(undef, estim.model.ny)
275+
h!(ŷ, estim.model, estim.x̂d, d - estim.model.dop)
276+
.+= estim.model.yop
277+
ŷ[estim.i_ym] = ym
278+
return
279+
end
280+
258281
"Print InternalModel information without i/o integrators."
259282
function print_estim_dim(io::IO, estim::InternalModel, n)
260283
nu, nd = estim.model.nu, estim.model.nd

src/estimator/kalman.jl

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -567,8 +567,9 @@ function update_estimate!(estim::UnscentedKalmanFilter{NT}, u, ym, d) where NT<:
567567
nym, nx̂, nσ = estim.nym, estim.nx̂, estim.
568568
γ, m̂, Ŝ = estim.γ, estim.m̂, estim.
569569
# --- initialize matrices ---
570-
= Matrix{NT}(undef, nx̂, nσ)
570+
, X̂_next = Matrix{NT}(undef, nx̂, nσ), Matrix{NT}(undef, nx̂, nσ)
571571
ŷm = Vector{NT}(undef, nym)
572+
= Vector{NT}(undef, estim.model.ny)
572573
Ŷm = Matrix{NT}(undef, nym, nσ)
573574
sqrt_P̂ = LowerTriangular{NT, Matrix{NT}}(Matrix{NT}(undef, nx̂, nx̂))
574575
# --- correction step ---
@@ -578,7 +579,8 @@ function update_estimate!(estim::UnscentedKalmanFilter{NT}, u, ym, d) where NT<:
578579
X̂[:, 2:nx̂+1] .+= γ_sqrt_P̂
579580
X̂[:, nx̂+2:end] .-= γ_sqrt_P̂
580581
for j in axes(Ŷm, 2)
581-
Ŷm[:, j] = @views (estim, estim.model, X̂[:, j], d)[estim.i_ym]
582+
@views ĥ!(ŷ, estim, estim.model, X̂[:, j], d)
583+
@views Ŷm[:, j] .= ŷ[estim.i_ym]
582584
end
583585
mul!(ŷm, Ŷm, m̂)
584586
X̄, Ȳm = X̂, Ŷm
@@ -598,9 +600,9 @@ function update_estimate!(estim::UnscentedKalmanFilter{NT}, u, ym, d) where NT<:
598600
X̂_cor .= x̂_cor
599601
X̂_cor[:, 2:nx̂+1] .+= γ_sqrt_P̂_cor
600602
X̂_cor[:, nx̂+2:end] .-= γ_sqrt_P̂_cor
601-
X̂_next = X̂_cor
603+
X̂_next = similar(X̂_cor)
602604
for j in axes(X̂_next, 2)
603-
X̂_next[:, j] = @views (estim, estim.model, X̂_cor[:, j], u, d)
605+
@views f̂!(X̂_next[:, j], estim, estim.model, X̂_cor[:, j], u, d)
604606
end
605607
x̂_next = mul!(x̂, X̂_next, m̂)
606608
X̄_next = X̂_next
@@ -757,9 +759,13 @@ automatically computes the Jacobians:
757759
```
758760
The matrix ``\mathbf{Ĥ^m}`` is the rows of ``\mathbf{Ĥ}`` that are measured outputs.
759761
"""
760-
function update_estimate!(estim::ExtendedKalmanFilter, u, ym, d=empty(estim.x̂))
761-
= ForwardDiff.jacobian(x̂ -> (estim, estim.model, x̂, u, d), estim.x̂)
762-
= ForwardDiff.jacobian(x̂ -> (estim, estim.model, x̂, d), estim.x̂)
762+
function update_estimate!(
763+
estim::ExtendedKalmanFilter{NT}, u, ym, d=empty(estim.x̂)
764+
) where NT<:Real
765+
model = estim.model
766+
x̂next, ŷ = Vector{NT}(undef, estim.nx̂), Vector{NT}(undef, model.ny)
767+
= ForwardDiff.jacobian((x̂next, x̂) -> f̂!(x̂next, estim, model, x̂, u, d), x̂next, estim.x̂)
768+
= ForwardDiff.jacobian((ŷ, x̂) -> ĥ!(ŷ, estim, model, x̂, d), ŷ, estim.x̂)
763769
return update_estimate_kf!(estim, u, ym, d, F̂, Ĥ[estim.i_ym, :], estim.P̂, estim.x̂)
764770
end
765771

@@ -790,7 +796,7 @@ function validate_kfcov(nym, nx̂, Q̂, R̂, P̂0=nothing)
790796
end
791797

792798
"""
793-
update_estimate_kf!(estim, u, ym, d, Â, Ĉm, P̂, x̂=nothing)
799+
update_estimate_kf!(estim::StateEstimator, u, ym, d, Â, Ĉm, P̂, x̂=nothing)
794800
795801
Update time-varying/extended Kalman Filter estimates with augmented `Â` and `Ĉm` matrices.
796802
@@ -801,16 +807,22 @@ The implementation uses in-place operations and explicit factorization to reduce
801807
allocations. See e.g. [`KalmanFilter`](@ref) docstring for the equations. If `isnothing(x̂)`,
802808
only the covariance `P̂` is updated.
803809
"""
804-
function update_estimate_kf!(estim, u, ym, d, Â, Ĉm, P̂, x̂=nothing)
810+
function update_estimate_kf!(
811+
estim::StateEstimator{NT}, u, ym, d, Â, Ĉm, P̂, x̂=nothing
812+
) where NT<:Real
805813
Q̂, R̂, M̂ = estim.Q̂, estim.R̂, estim.
806814
mul!(M̂, P̂, Ĉm')
807815
rdiv!(M̂, cholesky!(Hermitian(Ĉm ** Ĉm' .+ R̂)))
808816
if !isnothing(x̂)
809817
mul!(estim.K̂, Â, M̂)
810-
ŷm = @views (estim, estim.model, x̂, d)[estim.i_ym]
818+
x̂next, ŷ = Vector{NT}(undef, estim.nx̂), Vector{NT}(undef, estim.model.ny)
819+
ĥ!(ŷ, estim, estim.model, x̂, d)
820+
ŷm = @views ŷ[estim.i_ym]
811821
= ŷm
812822
v̂ .= ym .- ŷm
813-
x̂ .= (estim, estim.model, x̂, u, d) .+ mul!(x̂, estim.K̂, v̂)
823+
f̂!(x̂next, estim, estim.model, x̂, u, d)
824+
mul!(x̂next, estim.K̂, v̂, 1, 1)
825+
estim.x̂ .= x̂next
814826
end
815827
.data .=* (P̂ .-* Ĉm * P̂) *' .+# .data is necessary for Hermitians
816828
return nothing

src/estimator/mhe/execute.jl

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,9 @@ function getinfo(estim::MovingHorizonEstimator{NT}) where NT<:Real
137137
= Vector{NT}(undef, ny*Nk)
138138
for i=1:Nk
139139
d0 = @views D[(1 + nd*(i-1)):(nd*i)] # operating point already removed in estim.D
140-
= @views X̂[(1 + nx̂*(i-1)):(nx̂*i)]
141-
Ŷ[(1 + ny*(i-1)):(ny*i)] = (estim, model, x̂, d0) + model.yop
140+
= @views X̂[(1 + nx̂*(i-1)):(nx̂*i)]
141+
@views ĥ!(Ŷ[(1 + ny*(i-1)):(ny*i)], estim, model, x̂, d0)
142+
Ŷ[(1 + ny*(i-1)):(ny*i)] .+= model.yop
142143
end
143144
Ŷm = Ym -
144145
info[:Ŵ] = estim.Ŵ[1:Nk*nŵ]
@@ -326,10 +327,11 @@ function update_cov!(P̂, estim::MovingHorizonEstimator, ::LinModel, u, ym, d)
326327
return update_estimate_kf!(estim, u, ym, d, estim.Â, estim.Ĉ[estim.i_ym, :], P̂)
327328
end
328329
"Update it with the `ExtendedKalmanFilter` if model is not a `LinModel`."
329-
function update_cov!(P̂, estim::MovingHorizonEstimator, ::SimModel, u, ym, d)
330+
function update_cov!(P̂, estim::MovingHorizonEstimator, model::SimModel, u, ym, d)
330331
# TODO: also support UnscentedKalmanFilter
331-
= ForwardDiff.jacobian(x̂ -> (estim, estim.model, x̂, u, d), estim.x̂)
332-
= ForwardDiff.jacobian(x̂ -> (estim, estim.model, x̂, d), estim.x̂)
332+
x̂next, ŷ = similar(estim.x̂), similar(model.yop)
333+
= ForwardDiff.jacobian((x̂next, x̂) -> f̂!(x̂next, estim, estim.model, x̂, u, d), x̂next, estim.x̂)
334+
= ForwardDiff.jacobian((ŷ, x̂) -> ĥ!(ŷ, estim, estim.model, x̂, d), ŷ, estim.x̂)
333335
return update_estimate_kf!(estim, u, ym, d, F̂, Ĥ[estim.i_ym, :], P̂)
334336
end
335337

@@ -394,16 +396,22 @@ function predict!(
394396
V̂, X̂, estim::MovingHorizonEstimator, model::SimModel, Z̃::Vector{T}
395397
) where {T<:Real}
396398
Nk = estim.Nk[]
397-
nu, nd, nx̂, nŵ, nym = model.nu, model.nd, estim.nx̂, estim.nx̂, estim.nym
399+
nu, nd, ny, nx̂, nŵ, nym = model.nu, model.nd, model.ny, estim.nx̂, estim.nx̂, estim.nym
398400
nx̃ = !isinf(estim.C) + nx̂
401+
# TODO: avoid these two allocations if possible:
402+
ŷ, x̂next = Vector{T}(undef, ny), Vector{T}(undef, nx̂)
399403
= @views Z̃[nx̃-nx̂+1:nx̃]
400404
for j=1:Nk
401405
u = @views estim.U[ (1 + nu * (j-1)):(nu*j)]
402406
ym = @views estim.Ym[(1 + nym * (j-1)):(nym*j)]
403407
d = @views estim.D[ (1 + nd * (j-1)):(nd*j)]
404408
= @views Z̃[(1 + nx̃ + nŵ*(j-1)):(nx̃ + nŵ*j)]
405-
V̂[(1 + nym*(j-1)):(nym*j)] = ym - (estim, model, x̂, d)[estim.i_ym]
406-
X̂[(1 + nx̂ *(j-1)):(nx̂ *j)] = (estim, model, x̂, u, d) +
409+
ĥ!(ŷ, estim, model, x̂, d)
410+
ŷm = @views ŷ[estim.i_ym]
411+
V̂[(1 + nym*(j-1)):(nym*j)] .= ym .- ŷm
412+
f̂!(x̂next, estim, model, x̂, u, d)
413+
x̂next .+=
414+
X̂[(1 + nx̂ *(j-1)):(nx̂ *j)] = x̂next
407415
= @views X̂[(1 + nx̂*(j-1)):(nx̂*j)]
408416
end
409417
return V̂, X̂

src/model/linearization.jl

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,13 @@ julia> linmodel.A
3939
"""
4040
function linearize(model::NonLinModel; x=model.x, u=model.uop, d=model.dop)
4141
u0, d0 = u - model.uop, d - model.dop
42-
y = model.h(x, d0) + model.yop
43-
A = ForwardDiff.jacobian(x -> model.f(x, u0, d0), x)
44-
Bu = ForwardDiff.jacobian(u0 -> model.f(x, u0, d0), u0)
45-
Bd = ForwardDiff.jacobian(d0 -> model.f(x, u0, d0), d0)
46-
C = ForwardDiff.jacobian(x -> model.h(x, d0), x)
47-
Dd = ForwardDiff.jacobian(d0 -> model.h(x, d0), d0)
42+
xnext, y = similar(x), similar(model.yop)
43+
y = model.h!(y, x, d0) .+ model.yop
44+
A = ForwardDiff.jacobian((xnext, x) -> model.f!(xnext, x, u0, d0), xnext, x)
45+
Bu = ForwardDiff.jacobian((xnext, u0) -> model.f!(xnext, x, u0, d0), xnext, u0)
46+
Bd = ForwardDiff.jacobian((xnext, d0) -> model.f!(xnext, x, u0, d0), xnext, d0)
47+
C = ForwardDiff.jacobian((y, x) -> model.h!(y, x, d0), y, x)
48+
Dd = ForwardDiff.jacobian((y, d0) -> model.h!(y, x, d0), y, d0)
4849
linmodel = LinModel(A, Bu, C, Bd, Dd, model.Ts)
4950
setop!(linmodel, uop=u, yop=y, dop=d)
5051
return linmodel

0 commit comments

Comments
 (0)