Skip to content

Commit 09a54a1

Browse files
committed
added: JacobianBuffer to get rid of the allocation with jacobian!
1 parent fc2ce6a commit 09a54a1

File tree

4 files changed

+103
-11
lines changed

4 files changed

+103
-11
lines changed

src/model/linearization.jl

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,31 @@
1+
2+
function jacobianA!(A, jb::JacobianBuffer, model::SimModel, x, u, d)
3+
jb.x .= x; jb.u .= u; jb.d .= d
4+
return ForwardDiff.jacobian!(A, jb.f_x!, jb.xnext, jb.x, jb.f_x_cfg)
5+
end
6+
jacobianA!( _ , _ , model::LinModel, _ , _ , _ ) = model.A
7+
function jacobianBu!(Bu, jb::JacobianBuffer, model::SimModel, x, u, d)
8+
jb.x .= x; jb.u .= u; jb.d .= d
9+
return ForwardDiff.jacobian!(Bu, jb.f_u!, jb.xnext, jb.u, jb.f_u_cfg)
10+
end
11+
jacobianBu!( _ , _ , model::LinModel, _ , _ , _ ) = model.Bu
12+
function jacobianBd!(Bd, jb::JacobianBuffer, model::SimModel, x, u, d)
13+
jb.x .= x; jb.u .= u; jb.d .= d
14+
return ForwardDiff.jacobian!(Bd, jb.f_d!, jb.xnext, jb.d, jb.f_d_cfg)
15+
end
16+
jacobianBd!( _ , _ , model::LinModel, _ , _ , _ ) = model.Bd
17+
function jacobianC!(C, jb::JacobianBuffer, model::SimModel, x, d)
18+
jb.x .= x; jb.d .= d
19+
return ForwardDiff.jacobian!(C, jb.h_x!, jb.y, jb.x, jb.h_x_cfg)
20+
end
21+
jacobianC!( _ , _ , model::LinModel, _ , _ ) = model.C
22+
function jacobianDd!(Dd, jb::JacobianBuffer, model::SimModel, x, d)
23+
jb.x .= x; jb.d .= d
24+
return ForwardDiff.jacobian!(Dd, jb.h_d!, jb.y, jb.d, jb.h_d_cfg)
25+
end
26+
jacobianDd!( _ , _ , model::LinModel, _ , _ ) = model.Dd
27+
28+
129
"""
230
LinModel(model::NonLinModel; x=model.x0+model.xop, u=model.uop, d=model.dop)
331

src/model/linmodel.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ struct LinModel{NT<:Real} <: SimModel{NT}
2121
yname::Vector{String}
2222
dname::Vector{String}
2323
xname::Vector{String}
24-
buffer::SimModelBuffer{NT}
24+
buffer::SimModelBuffer{NT, Nothing}
2525
function LinModel{NT}(A, Bu, C, Bd, Dd, Ts) where {NT<:Real}
2626
A, Bu = to_mat(A, 1, 1), to_mat(Bu, 1, 1)
2727
nu, nx = size(Bu, 2), size(A, 2)

src/model/nonlinmodel.jl

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
struct NonLinModel{
2-
NT<:Real, F<:Function, H<:Function, P<:Any, DS<:DiffSolver
2+
NT<:Real, F<:Function, H<:Function, P<:Any, DS<:DiffSolver, SMB<:SimModelBuffer
33
} <: SimModel{NT}
44
x0::Vector{NT}
55
f!::F
@@ -21,10 +21,10 @@ struct NonLinModel{
2121
yname::Vector{String}
2222
dname::Vector{String}
2323
xname::Vector{String}
24-
buffer::SimModelBuffer{NT}
24+
buffer::SMB
2525
function NonLinModel{NT, F, H, P, DS}(
26-
f!::F, h!::H, Ts, nu, nx, ny, nd, p::P, solver::DS
27-
) where {NT<:Real, F<:Function, H<:Function, P<:Any, DS<:DiffSolver}
26+
f!::F, h!::H, Ts, nu, nx, ny, nd, p::P, solver::DS, buffer::SMB
27+
) where {NT<:Real, F<:Function, H<:Function, P<:Any, DS<:DiffSolver, SMB<:SimModelBuffer}
2828
Ts > 0 || error("Sampling time Ts must be positive")
2929
uop = zeros(NT, nu)
3030
yop = zeros(NT, ny)
@@ -37,8 +37,7 @@ struct NonLinModel{
3737
xname = ["\$x_{$i}\$" for i in 1:nx]
3838
x0 = zeros(NT, nx)
3939
t = zeros(NT, 1)
40-
buffer = SimModelBuffer{NT}(nu, nx, ny, nd)
41-
return new{NT, F, H, P, DS}(
40+
return new{NT, F, H, P, DS, SMB}(
4241
x0,
4342
f!, h!,
4443
p,
@@ -146,7 +145,9 @@ function NonLinModel{NT}(
146145
f!, h! = get_mutating_functions(NT, f, h)
147146
f!, h! = get_solver_functions(NT, solver, f!, h!, Ts, nu, nx, ny, nd)
148147
F, H, P, DS = get_types(f!, h!, p, solver)
149-
return NonLinModel{NT, F, H, P, DS}(f!, h!, Ts, nu, nx, ny, nd, p, solver)
148+
jacobian = JacobianBuffer{NT}(f!, h!, nu, nx, ny, nd)
149+
buffer = SimModelBuffer{NT}(nu, nx, ny, nd, jacobian)
150+
return NonLinModel{NT, F, H, P, DS}(f!, h!, Ts, nu, nx, ny, nd, p, solver, buffer)
150151
end
151152

152153
function NonLinModel(

src/sim_model.jl

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,73 @@ julia> y = model()
2020
"""
2121
abstract type SimModel{NT<:Real} end
2222

23-
struct SimModelBuffer{NT<:Real}
23+
struct JacobianBuffer{
24+
NT<:Real,
25+
FX<:Function,
26+
FU<:Function,
27+
FD<:Function,
28+
HX<:Function,
29+
HU<:Function,
30+
CFX<:ForwardDiff.JacobianConfig,
31+
CFU<:ForwardDiff.JacobianConfig,
32+
CFD<:ForwardDiff.JacobianConfig,
33+
CHU<:ForwardDiff.JacobianConfig,
34+
CHX<:ForwardDiff.JacobianConfig
35+
}
36+
xnext::Vector{NT}
37+
x::Vector{NT}
38+
y::Vector{NT}
39+
u::Vector{NT}
40+
d::Vector{NT}
41+
f_x!::FX
42+
f_u!::FU
43+
f_d!::FD
44+
h_x!::HX
45+
h_d!::HU
46+
f_x_cfg::CFX
47+
f_u_cfg::CFU
48+
f_d_cfg::CFD
49+
h_x_cfg::CHX
50+
h_d_cfg::CHU
51+
end
52+
53+
"""
54+
JacobianBuffer(NT::DataType, f!::Function, h!::Function, nx, nu, ny, nd)
55+
56+
Buffer object for calling `ForwardDiff.jacobian!` on `SimModel` without any allocation.
57+
"""
58+
function JacobianBuffer{NT}(
59+
f!::Function, h!::Function, nx::Int, nu::Int, ny::Int, nd::Int
60+
) where NT <: Real
61+
xnext = Vector{NT}(undef, nx)
62+
x = Vector{NT}(undef, nx)
63+
y = Vector{NT}(undef, ny)
64+
u = Vector{NT}(undef, nu)
65+
d = Vector{NT}(undef, nd)
66+
f_x!(y, x) = f!(y, x, u, d)
67+
f_u!(y, u) = f!(y, x, u, d)
68+
f_d!(y, d) = f!(y, x, u, d)
69+
h_x!(y, x) = h!(y, x, d)
70+
h_d!(y, d) = h!(y, x, d)
71+
f_x_cfg = ForwardDiff.JacobianConfig(f_x!, xnext, x)
72+
f_u_cfg = ForwardDiff.JacobianConfig(f_u!, xnext, u)
73+
f_d_cfg = ForwardDiff.JacobianConfig(f_d!, xnext, d)
74+
h_x_cfg = ForwardDiff.JacobianConfig(h_x!, y, x)
75+
h_d_cfg = ForwardDiff.JacobianConfig(h_d!, y, d)
76+
return JacobianBuffer(
77+
xnext, x, y, u, d,
78+
f_x!, f_u!, f_d!, h_x!, h_d!,
79+
f_x_cfg, f_u_cfg, f_d_cfg, h_x_cfg, h_d_cfg
80+
)
81+
end
82+
83+
struct SimModelBuffer{NT<:Real, JB<:Union{JacobianBuffer, Nothing}}
2484
u::Vector{NT}
2585
x::Vector{NT}
2686
y::Vector{NT}
2787
d::Vector{NT}
2888
empty::Vector{NT}
89+
jacobian::JB
2990
end
3091

3192
@doc raw"""
@@ -35,13 +96,15 @@ Create a buffer for `SimModel` objects for inputs, states, outputs, and disturba
3596
3697
The buffer is used to store intermediate results during simulation without allocating.
3798
"""
38-
function SimModelBuffer{NT}(nu::Int, nx::Int, ny::Int, nd::Int) where NT <: Real
99+
function SimModelBuffer{NT}(
100+
nu::Int, nx::Int, ny::Int, nd::Int, jacobian::JB=nothing
101+
) where {NT <: Real, JB <: Union{JacobianBuffer, Nothing}}
39102
u = Vector{NT}(undef, nu)
40103
x = Vector{NT}(undef, nx)
41104
y = Vector{NT}(undef, ny)
42105
d = Vector{NT}(undef, nd)
43106
empty = Vector{NT}(undef, 0)
44-
return SimModelBuffer{NT}(u, x, y, d, empty)
107+
return SimModelBuffer{NT, JB}(u, x, y, d, empty, jacobian)
45108
end
46109

47110

0 commit comments

Comments
 (0)