Skip to content

Commit 50c920e

Browse files
committed
first implementing in place version of NonLinModel
1 parent d5205d9 commit 50c920e

File tree

3 files changed

+58
-62
lines changed

3 files changed

+58
-62
lines changed

Diff for: .github/workflows/CI.yml

+1-1
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

Diff for: src/model/nonlinmodel.jl

+47-54
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ struct NonLinModel{NT<:Real, F<:Function, H<:Function} <: SimModel{NT}
2323
end
2424

2525
@doc raw"""
26-
NonLinModel{NT}(f!::Function, h!::Function, Ts, nu, nx, ny, nd=0)
26+
NonLinModel{NT}(f::Function, h::Function, Ts, nu, nx, ny, nd=0)
2727
28-
Construct a nonlinear model from discrete-time state-space functions `f!` and `h!`.
28+
Construct a nonlinear model from discrete-time state-space functions `f` and `h`.
2929
3030
The state update ``\mathbf{f}`` and output ``\mathbf{h}`` functions are defined as :
3131
```math
@@ -45,7 +45,7 @@ Nonlinear continuous-time state-space functions are not supported for now. In su
4545
manually call a differential equation solver in `f` (see [Manual](@ref man_nonlin)).
4646
4747
!!! warning
48-
`f!` and `h!` must be pure Julia functions to use the model in [`NonLinMPC`](@ref),
48+
`f` and `h` must be pure Julia functions to use the model in [`NonLinMPC`](@ref),
4949
[`ExtendedKalmanFilter`](@ref), [`MovingHorizonEstimator`](@ref) and [`linearize`](@ref).
5050
5151
See also [`LinModel`](@ref).
@@ -61,82 +61,75 @@ Discrete-time nonlinear model with a sample time Ts = 10.0 s and:
6161
```
6262
"""
6363
function NonLinModel{NT}(
64-
f!::Function, h!::Function, Ts::Real, nu::Int, nx::Int, ny::Int, nd::Int=0
64+
f::Function, h::Function, Ts::Real, nu::Int, nx::Int, ny::Int, nd::Int=0
6565
) where {NT<:Real}
66-
iscontinous = validate_fcts(NT, f!, h!)
67-
if iscontinous
68-
fc! = f!
69-
f! = let nx=nx, Ts=Ts, NT=NT
70-
= zeros(NT, nx)
71-
xterm = zeros(NT, nx)
72-
k1, k2, k3, k4 = zeros(NT, nx), zeros(NT, nx), zeros(NT, nx), zeros(NT, nx)
73-
function rk4!(x, u, d)
74-
xterm .= x
75-
fc!(ẋ, xterm, u, d)
76-
k1 .=
77-
xterm .= @. x + k1 * Ts/2
78-
fc!(ẋ, xterm, u, d)
79-
k2 .=
80-
xterm .= @. x + k2 * Ts/2
81-
fc!(ẋ, xterm, u, d)
82-
k3 .=
83-
xterm .= @. x + k3 * Ts
84-
fc!(ẋ, xterm, u, d)
85-
k4 .=
86-
x .+= @. (k1 + 2k2 + 2k3 + k4)*Ts/6
87-
return x
88-
end
89-
rk4!
90-
end
66+
ismutating_f = validate_f(NT, f)
67+
ismutating_h = validate_h(NT, h)
68+
f! = let f = f
69+
ismutating_f ? f : (xnext, x, u, d) -> xnext .= f(x, u, d)
70+
end
71+
h! = let h = h
72+
ismutating_h ? h : (y, x, d) -> y .= h(x, d)
9173
end
9274
F, H = getFuncTypes(f!, h!)
9375
return NonLinModel{NT, F, H}(f!, h!, Ts, nu, nx, ny, nd)
9476
end
9577

9678
function NonLinModel(
97-
f!::Function, h!::Function, Ts::Real, nu::Int, nx::Int, ny::Int, nd::Int=0
79+
f::Function, h::Function, Ts::Real, nu::Int, nx::Int, ny::Int, nd::Int=0
9880
)
99-
return NonLinModel{Float64}(f!, h!, Ts, nu, nx, ny, nd)
81+
return NonLinModel{Float64}(f, h, Ts, nu, nx, ny, nd)
10082
end
10183

102-
getFuncTypes(f!::F, h!::H) where {F<:Function, H<:Function} = F, H
84+
getFuncTypes(f::F, h::H) where {F<:Function, H<:Function} = F, H
10385

10486

10587
"""
106-
validate_fcts(NT, f!, h!) -> iscontinuous
88+
validate_f(NT, f) -> ismutating
10789
108-
Validate `f!` and `h!` function argument signatures and return `true` if `f!` is continuous.
90+
Validate `f` function argument signature and return `true` if it is mutating.
10991
"""
110-
function validate_fcts(NT, f!, h!)
111-
isdiscrete = hasmethod(f!,
112-
Tuple{Vector{NT}, Vector{NT}, Vector{NT}}
113-
)
114-
iscontinuous = hasmethod(f!,
115-
Tuple{Vector{NT}, Vector{NT}, Vector{NT}, Vector{NT}}
116-
)
117-
if !isdiscrete && !iscontinuous
118-
println(isdiscrete)
119-
println(iscontinuous)
120-
error("state function has no method with type signature "*
121-
"f!(x::Vector{$(NT)}, u::Vector{$(NT)}, d::Vector{$(NT)}) or "*
122-
"f!(ẋ::Vector{$(NT)}, x::Vector{$(NT)}, u::Vector{$(NT)}, d::Vector{$(NT)})")
92+
function validate_f(NT, f)
93+
ismutating = hasmethod(f, Tuple{Vector{NT}, Vector{NT}, Vector{NT}, Vector{NT}})
94+
if !(ismutating || hasmethod(f, Tuple{Vector{NT}, Vector{NT}, Vector{NT}}))
95+
error(
96+
"the state function has no method with type signature "*
97+
"f(x::Vector{$(NT)}, u::Vector{$(NT)}, d::Vector{$(NT)}) or mutating form "*
98+
"f!(xnext::Vector{$(NT)}, x::Vector{$(NT)}, u::Vector{$(NT)}, d::Vector{$(NT)})"
99+
)
123100
end
124-
hargsvalid = hasmethod(h!, Tuple{Vector{NT}, Vector{NT}, Vector{NT}})
125-
if !hargsvalid
126-
error("output function has no method with type signature "*
127-
"h!(y::Vector{$(NT)}, x::Vector{$(NT)}, d::Vector{$(NT)})")
101+
return ismutating
102+
end
103+
104+
"""
105+
validate_h(NT, h) -> ismutating
106+
107+
Validate `h` function argument signature and return `true` if it is mutating.
108+
"""
109+
function validate_h(NT, h)
110+
ismutating = hasmethod(h, Tuple{Vector{NT}, Vector{NT}, Vector{NT}})
111+
if !(ismutating || hasmethod(h, Tuple{Vector{NT}, Vector{NT}}))
112+
error(
113+
"the output function has no method with type signature "*
114+
"h(x::Vector{$(NT)}, d::Vector{$(NT)}) or mutating form "*
115+
"h!(y::Vector{$(NT)}, x::Vector{$(NT)}, d::Vector{$(NT)})"
116+
)
128117
end
129-
return iscontinuous
118+
return ismutating
130119
end
131120

132121
"Do nothing if `model` is a [`NonLinModel`](@ref)."
133122
steadystate!(::SimModel, _ , _ ) = nothing
134123

135124

125+
126+
127+
136128
"Call ``\\mathbf{f!(x, u, d)}`` with `model.f!` function for [`NonLinModel`](@ref)."
137-
f!(x, model::NonLinModel, u, d) = model.f!(x, u, d)
129+
f!(xnext, model::NonLinModel, x, u, d) = model.f!(xnext, x, u, d)
138130

139131
"Call ``\\mathbf{h!(y, x, d)}`` with `model.h` function for [`NonLinModel`](@ref)."
140132
h!(y, model::NonLinModel, x, d) = model.h!(y, x, d)
141133

142-
typestr(model::NonLinModel) = "nonlinear"
134+
typestr(model::NonLinModel) = "nonlinear"
135+

Diff for: src/sim_model.jl

+10-7
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,8 @@ julia> x = updatestate!(model, [1])
143143
```
144144
"""
145145
function updatestate!(model::SimModel, u, d=empty(model.x))
146-
validate_args(model::SimModel, u, d)
147-
f!(model.x, model, u - model.uop, d - model.dop)
146+
validate_args(model::SimModel, d, u)
147+
f!(model.x, model, model.x, u .- model.uop, d .- model.dop)
148148
return model.x
149149
end
150150

@@ -165,20 +165,23 @@ julia> y = evaloutput(model)
165165
```
166166
"""
167167
function evaloutput(model::SimModel{NT}, d=empty(model.x)) where NT <: Real
168+
validate_args(model, d)
168169
y = Vector{NT}(undef, model.ny)
169-
h!(y, model, model.x, d - model.dop) + model.yop
170+
h!(y, model, model.x, d .- model.dop) .+ model.yop
170171
return y
171172
end
172173

173174
"""
174-
validate_args(model::SimModel, u, d)
175+
validate_args(model::SimModel, d, u=nothing)
175176
176-
Check `u` and `d` sizes against `model` dimensions.
177+
Check `d` and `u` (if provided) sizes against `model` dimensions.
177178
"""
178-
function validate_args(model::SimModel, u, d)
179+
function validate_args(model::SimModel, d, u=nothing)
179180
nu, nd = model.nu, model.nd
180-
size(u) (nu,) && throw(DimensionMismatch("u size $(size(u)) ≠ manip. input size ($nu,)"))
181181
size(d) (nd,) && throw(DimensionMismatch("d size $(size(d)) ≠ meas. dist. size ($nd,)"))
182+
if !isnothing(u)
183+
size(u) (nu,) && throw(DimensionMismatch("u size $(size(u)) ≠ manip. input size ($nu,)"))
184+
end
182185
end
183186

184187
"Convert vectors to single column matrices when necessary."

0 commit comments

Comments
 (0)