Skip to content

Commit 0bef5f1

Browse files
committed
added: tests with Float32 type parameters
1 parent 476d71b commit 0bef5f1

File tree

14 files changed

+200
-141
lines changed

14 files changed

+200
-141
lines changed

src/controller/explicitmpc.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,10 @@ struct ExplicitMPC{NT<:Real, SE<:StateEstimator} <: PredictiveController{NT}
5151
P̃_chol = cholesky(P̃)
5252
Ks, Ps = init_stochpred(estim, Hp)
5353
# dummy vals (updated just before optimization):
54-
d0, D̂0, D̂E = zeros(nd), zeros(nd*Hp), zeros(nd + nd*Hp)
54+
d0, D̂0, D̂E = zeros(NT, nd), zeros(NT, nd*Hp), zeros(NT, nd + nd*Hp)
5555
Ŷop, Dop = repeat(model.yop, Hp), repeat(model.dop, Hp)
5656
nvar = size(Ẽ, 2)
57-
ΔŨ = zeros(nvar)
57+
ΔŨ = zeros(NT, nvar)
5858
mpc = new{NT, SE}(
5959
estim,
6060
ΔŨ, ŷ,
@@ -167,9 +167,9 @@ function ExplicitMPC(
167167
) where {NT<:Real, SE<:StateEstimator{NT}}
168168
isa(estim.model, LinModel) || error("estim.model type must be LinModel")
169169
Hp = default_Hp(estim.model, Hp)
170-
isnothing(M_Hp) && (M_Hp = Diagonal(repeat(Mwt, Hp)))
171-
isnothing(N_Hc) && (N_Hc = Diagonal(repeat(Nwt, Hc)))
172-
isnothing(L_Hp) && (L_Hp = Diagonal(repeat(Lwt, Hp)))
170+
isnothing(M_Hp) && (M_Hp = Diagonal{NT}(repeat(Mwt, Hp)))
171+
isnothing(N_Hc) && (N_Hc = Diagonal{NT}(repeat(Nwt, Hc)))
172+
isnothing(L_Hp) && (L_Hp = Diagonal{NT}(repeat(Lwt, Hp)))
173173
return ExplicitMPC{NT, SE}(estim, Hp, Hc, M_Hp, N_Hc, L_Hp)
174174
end
175175

src/controller/linmpc.jl

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
const DEFAULT_LINMPC_OPTIMIZER = OSQP.MathOptInterfaceOSQP.Optimizer
22

3-
struct LinMPC{NT<:Real, SE<:StateEstimator} <: PredictiveController{NT}
3+
struct LinMPC{
4+
NT<:Real,
5+
SE<:StateEstimator,
6+
JM<:JuMP.GenericModel
7+
} <: PredictiveController{NT}
48
estim::SE
5-
optim::JuMP.Model
9+
# note: `NT` and the number type `JNT` in `JuMP.GenericModel{JNT}` can be
10+
# different since solvers that support non-Float64 are scarce.
11+
optim::JM
612
con::ControllerConstraint{NT}
713
ΔŨ::Vector{NT}
814
::Vector{NT}
@@ -34,9 +40,9 @@ struct LinMPC{NT<:Real, SE<:StateEstimator} <: PredictiveController{NT}
3440
D̂E::Vector{NT}
3541
Ŷop::Vector{NT}
3642
Dop::Vector{NT}
37-
function LinMPC{NT, SE}(
38-
estim::SE, Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt, optim
39-
) where {NT<:Real, SE<:StateEstimator}
43+
function LinMPC{NT, SE, JM}(
44+
estim::SE, Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt, optim::JM
45+
) where {NT<:Real, SE<:StateEstimator, JM<:JuMP.GenericModel}
4046
model = estim.model
4147
nu, ny, nd = model.nu, model.ny, model.nd
4248
= copy(model.yop) # dummy vals (updated just before optimization)
@@ -56,7 +62,7 @@ struct LinMPC{NT<:Real, SE<:StateEstimator} <: PredictiveController{NT}
5662
Ŷop, Dop = repeat(model.yop, Hp), repeat(model.dop, Hp)
5763
nvar = size(Ẽ, 2)
5864
ΔŨ = zeros(NT, nvar)
59-
mpc = new{NT, SE}(
65+
mpc = new{NT, SE, JM}(
6066
estim, optim, con,
6167
ΔŨ, ŷ,
6268
Hp, Hc,
@@ -68,7 +74,7 @@ struct LinMPC{NT<:Real, SE<:StateEstimator} <: PredictiveController{NT}
6874
d0, D̂0, D̂E,
6975
Ŷop, Dop,
7076
)
71-
init_optimization!(mpc)
77+
init_optimization!(mpc, optim)
7278
return mpc
7379
end
7480
end
@@ -166,7 +172,7 @@ function LinMPC(
166172
M_Hp = nothing,
167173
N_Hc = nothing,
168174
L_Hp = nothing,
169-
optim::JuMP.Model = JuMP.Model(DEFAULT_LINMPC_OPTIMIZER, add_bridges=false),
175+
optim::JuMP.GenericModel = JuMP.Model(DEFAULT_LINMPC_OPTIMIZER, add_bridges=false),
170176
kwargs...
171177
)
172178
estim = SteadyKalmanFilter(model; kwargs...)
@@ -207,24 +213,24 @@ function LinMPC(
207213
M_Hp = nothing,
208214
N_Hc = nothing,
209215
L_Hp = nothing,
210-
optim::JuMP.Model = JuMP.Model(DEFAULT_LINMPC_OPTIMIZER, add_bridges=false),
211-
) where {NT<:Real, SE<:StateEstimator{NT}}
216+
optim::JM = JuMP.Model(DEFAULT_LINMPC_OPTIMIZER, add_bridges=false),
217+
) where {NT<:Real, SE<:StateEstimator{NT}, JM<:JuMP.GenericModel}
212218
isa(estim.model, LinModel) || error("estim.model type must be LinModel")
213219
Hp = default_Hp(estim.model, Hp)
214-
isnothing(M_Hp) && (M_Hp = Diagonal(repeat(Mwt, Hp)))
215-
isnothing(N_Hc) && (N_Hc = Diagonal(repeat(Nwt, Hc)))
216-
isnothing(L_Hp) && (L_Hp = Diagonal(repeat(Lwt, Hp)))
217-
return LinMPC{NT, SE}(estim, Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt, optim)
220+
isnothing(M_Hp) && (M_Hp = Diagonal{NT}(repeat(Mwt, Hp)))
221+
isnothing(N_Hc) && (N_Hc = Diagonal{NT}(repeat(Nwt, Hc)))
222+
isnothing(L_Hp) && (L_Hp = Diagonal{NT}(repeat(Lwt, Hp)))
223+
return LinMPC{NT, SE, JM}(estim, Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt, optim)
218224
end
219225

220226
"""
221-
init_optimization!(mpc::LinMPC)
227+
init_optimization!(mpc::LinMPC, optim::JuMP.GenericModel)
222228
223229
Init the quadratic optimization for [`LinMPC`](@ref) controllers.
224230
"""
225-
function init_optimization!(mpc::LinMPC)
231+
function init_optimization!(mpc::LinMPC, optim::JuMP.GenericModel)
226232
# --- variables and linear constraints ---
227-
optim, con = mpc.optim, mpc.con
233+
con = mpc.con
228234
nvar = length(mpc.ΔŨ)
229235
set_silent(optim)
230236
limit_solve_time(mpc)

src/controller/nonlinmpc.jl

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
const DEFAULT_NONLINMPC_OPTIMIZER = optimizer_with_attributes(Ipopt.Optimizer,"sb"=>"yes")
22

3-
struct NonLinMPC{NT<:Real, SE<:StateEstimator, JEfunc<:Function} <: PredictiveController{NT}
3+
struct NonLinMPC{
4+
NT<:Real,
5+
SE<:StateEstimator,
6+
JM<:JuMP.GenericModel,
7+
JEfunc<:Function
8+
} <: PredictiveController{NT}
49
estim::SE
5-
optim::JuMP.Model
10+
# note: `NT` and the number type `JNT` in `JuMP.GenericModel{JNT}` can be
11+
# different since solvers that support non-Float64 are scarce.
12+
optim::JM
613
con::ControllerConstraint{NT}
714
ΔŨ::Vector{NT}
815
::Vector{NT}
@@ -35,27 +42,28 @@ struct NonLinMPC{NT<:Real, SE<:StateEstimator, JEfunc<:Function} <: PredictiveCo
3542
D̂E::Vector{NT}
3643
Ŷop::Vector{NT}
3744
Dop::Vector{NT}
38-
function NonLinMPC{NT, SE, JEFunc}(
39-
estim::SE, Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt, Ewt, JE::JEFunc, optim
40-
) where {NT<:Real, SE<:StateEstimator, JEFunc<:Function}
45+
function NonLinMPC{NT, SE, JM, JEFunc}(
46+
estim::SE, Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt, Ewt, JE::JEFunc, optim::JM
47+
) where {NT<:Real, SE<:StateEstimator, JM<:JuMP.GenericModel, JEFunc<:Function}
4148
model = estim.model
4249
nu, ny, nd = model.nu, model.ny, model.nd
4350
= copy(model.yop) # dummy vals (updated just before optimization)
4451
validate_weights(model, Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt, Ewt)
4552
M_Hp, N_Hc, L_Hp = float(M_Hp), float(N_Hc), float(L_Hp) # debug julia 1.6
46-
R̂y, R̂u = zeros(ny*Hp), zeros(nu*Hp) # dummy vals (updated just before optimization)
53+
# dummy vals (updated just before optimization):
54+
R̂y, R̂u = zeros(NT, ny*Hp), zeros(NT, nu*Hp)
4755
noR̂u = iszero(L_Hp)
4856
S, T = init_ΔUtoU(nu, Hp, Hc)
4957
E, F, G, J, K, V, ex̂, fx̂, gx̂, jx̂, kx̂, vx̂ = init_predmat(estim, model, Hp, Hc)
5058
con, S̃, Ñ_Hc, Ẽ = init_defaultcon(estim, Hp, Hc, Cwt, S, N_Hc, E, ex̂, fx̂, gx̂, jx̂, kx̂, vx̂)
5159
P̃, q̃, p = init_quadprog(model, Ẽ, S̃, M_Hp, Ñ_Hc, L_Hp)
5260
Ks, Ps = init_stochpred(estim, Hp)
5361
# dummy vals (updated just before optimization):
54-
d0, D̂0, D̂E = zeros(nd), zeros(nd*Hp), zeros(nd + nd*Hp)
62+
d0, D̂0, D̂E = zeros(NT, nd), zeros(NT, nd*Hp), zeros(NT, nd + nd*Hp)
5563
Ŷop, Dop = repeat(model.yop, Hp), repeat(model.dop, Hp)
5664
nvar = size(Ẽ, 2)
57-
ΔŨ = zeros(nvar)
58-
mpc = new{NT, SE, JEFunc}(
65+
ΔŨ = zeros(NT, nvar)
66+
mpc = new{NT, SE, JM, JEFunc}(
5967
estim, optim, con,
6068
ΔŨ, ŷ,
6169
Hp, Hc,
@@ -67,7 +75,7 @@ struct NonLinMPC{NT<:Real, SE<:StateEstimator, JEfunc<:Function} <: PredictiveCo
6775
d0, D̂0, D̂E,
6876
Ŷop, Dop,
6977
)
70-
init_optimization!(mpc)
78+
init_optimization!(mpc, optim)
7179
return mpc
7280
end
7381
end
@@ -168,7 +176,7 @@ function NonLinMPC(
168176
M_Hp = nothing,
169177
N_Hc = nothing,
170178
L_Hp = nothing,
171-
optim::JuMP.Model = JuMP.Model(DEFAULT_NONLINMPC_OPTIMIZER, add_bridges=false),
179+
optim::JuMP.GenericModel = JuMP.Model(DEFAULT_NONLINMPC_OPTIMIZER, add_bridges=false),
172180
kwargs...
173181
)
174182
estim = UnscentedKalmanFilter(model; kwargs...)
@@ -188,7 +196,7 @@ function NonLinMPC(
188196
M_Hp = nothing,
189197
N_Hc = nothing,
190198
L_Hp = nothing,
191-
optim::JuMP.Model = JuMP.Model(DEFAULT_NONLINMPC_OPTIMIZER, add_bridges=false),
199+
optim::JuMP.GenericModel = JuMP.Model(DEFAULT_NONLINMPC_OPTIMIZER, add_bridges=false),
192200
kwargs...
193201
)
194202
estim = SteadyKalmanFilter(model; kwargs...)
@@ -231,13 +239,13 @@ function NonLinMPC(
231239
M_Hp = nothing,
232240
N_Hc = nothing,
233241
L_Hp = nothing,
234-
optim::JuMP.Model = JuMP.Model(DEFAULT_NONLINMPC_OPTIMIZER, add_bridges=false),
235-
) where {NT<:Real, SE<:StateEstimator{NT}, JEFunc<:Function}
242+
optim::JM = JuMP.Model(DEFAULT_NONLINMPC_OPTIMIZER, add_bridges=false),
243+
) where {NT<:Real, SE<:StateEstimator{NT}, JM<:JuMP.GenericModel, JEFunc<:Function}
236244
Hp = default_Hp(estim.model, Hp)
237-
isnothing(M_Hp) && (M_Hp = Diagonal(repeat(Mwt, Hp)))
238-
isnothing(N_Hc) && (N_Hc = Diagonal(repeat(Nwt, Hc)))
239-
isnothing(L_Hp) && (L_Hp = Diagonal(repeat(Lwt, Hp)))
240-
return NonLinMPC{NT, SE, JEFunc}(estim, Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt, Ewt, JE, optim)
245+
isnothing(M_Hp) && (M_Hp = Diagonal{NT}(repeat(Mwt, Hp)))
246+
isnothing(N_Hc) && (N_Hc = Diagonal{NT}(repeat(Nwt, Hc)))
247+
isnothing(L_Hp) && (L_Hp = Diagonal{NT}(repeat(Lwt, Hp)))
248+
return NonLinMPC{NT, SE, JM, JEFunc}(estim, Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt, Ewt, JE, optim)
241249
end
242250

243251
"""
@@ -256,13 +264,13 @@ function addinfo!(info, mpc::NonLinMPC)
256264
end
257265

258266
"""
259-
init_optimization!(mpc::NonLinMPC)
267+
init_optimization!(mpc::NonLinMPC, optim::JuMP.GenericModel)
260268
261269
Init the nonlinear optimization for [`NonLinMPC`](@ref) controllers.
262270
"""
263-
function init_optimization!(mpc::NonLinMPC{NT, SE, JEFunc}) where {NT<:Real, SE, JEFunc}
271+
function init_optimization!(mpc::NonLinMPC, optim::JuMP.GenericModel{JNT}) where JNT<:Real
264272
# --- variables and linear constraints ---
265-
optim, con = mpc.optim, mpc.con
273+
con = mpc.con
266274
nvar = length(mpc.ΔŨ)
267275
set_silent(optim)
268276
limit_solve_time(mpc)
@@ -277,10 +285,10 @@ function init_optimization!(mpc::NonLinMPC{NT, SE, JEFunc}) where {NT<:Real, SE,
277285
# inspired from https://jump.dev/JuMP.jl/stable/tutorials/nonlinear/tips_and_tricks/#User-defined-operators-with-vector-outputs
278286
Jfunc, gfunc = let mpc=mpc, model=model, ng=ng, nvar=nvar , nŶ=Hp*ny, nx̂=nx̂
279287
last_ΔŨtup_float, last_ΔŨtup_dual = nothing, nothing
280-
Ŷ_cache::DiffCache{Vector{NT}, Vector{NT}} = DiffCache(zeros(NT, nŶ), nvar + 3)
281-
g_cache::DiffCache{Vector{NT}, Vector{NT}} = DiffCache(zeros(NT, ng), nvar + 3)
282-
x̂_cache::DiffCache{Vector{NT}, Vector{NT}} = DiffCache(zeros(NT, nx̂), nvar + 3)
283-
function Jfunc(ΔŨtup::NT...)
288+
Ŷ_cache::DiffCache{Vector{JNT}, Vector{JNT}} = DiffCache(zeros(JNT, nŶ), nvar + 3)
289+
g_cache::DiffCache{Vector{JNT}, Vector{JNT}} = DiffCache(zeros(JNT, ng), nvar + 3)
290+
x̂_cache::DiffCache{Vector{JNT}, Vector{JNT}} = DiffCache(zeros(JNT, nx̂), nvar + 3)
291+
function Jfunc(ΔŨtup::JNT...)
284292
= get_tmp(Ŷ_cache, ΔŨtup[1])
285293
ΔŨ = collect(ΔŨtup)
286294
if ΔŨtup != last_ΔŨtup_float
@@ -304,7 +312,7 @@ function init_optimization!(mpc::NonLinMPC{NT, SE, JEFunc}) where {NT<:Real, SE,
304312
end
305313
return obj_nonlinprog(mpc, model, Ŷ, ΔŨ)
306314
end
307-
function gfunc_i(i, ΔŨtup::NTuple{N, NT}) where N
315+
function gfunc_i(i, ΔŨtup::NTuple{N, JNT}) where N
308316
g = get_tmp(g_cache, ΔŨtup[1])
309317
if ΔŨtup != last_ΔŨtup_float
310318
= get_tmp(x̂_cache, ΔŨtup[1])

src/estimator/internal_model.jl

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,10 @@ function matrices_internalmodel(model::LinModel)
142142
return Â, B̂u, Ĉ, B̂d, D̂d
143143
end
144144
"Return empty matrices if `model` is not a [`LinModel`](@ref)."
145-
function matrices_internalmodel(model::SimModel)
145+
function matrices_internalmodel(model::SimModel{NT}) where NT<:Real
146146
nu, nx, nd = model.nu, model.nx, model.nd
147-
return zeros(0, nx), zeros(0, nu), zeros(0, nx), zeros(0, nd), zeros(0, nd)
147+
Â, B̂u, Ĉ, B̂d, D̂d = zeros(NT,0,nx), zeros(NT,0,nu), zeros(NT,0,nx), zeros(NT,0,nd), zeros(NT,0,nd)
148+
return Â, B̂u, Ĉ, B̂d, D̂d
148149
end
149150

150151
@doc raw"""
@@ -216,14 +217,16 @@ The [`InternalModel`](@ref) updates the deterministic `x̂d` and stochastic `x̂
216217
This estimator does not augment the state vector, thus ``\mathbf{x̂ = x̂_d}``. See
217218
[`init_internalmodel`](@ref) for details.
218219
"""
219-
function update_estimate!(estim::InternalModel, u, ym, d=empty(estim.x̂))
220+
function update_estimate!(
221+
estim::InternalModel{NT, SM}, u, ym, d=empty(estim.x̂)
222+
) where {NT<:Real, SM}
220223
model = estim.model
221224
x̂d, x̂s = estim.x̂d, estim.x̂s
222225
# -------------- deterministic model ---------------------
223226
ŷd = h(model, x̂d, d)
224227
x̂d[:] = f(model, x̂d, u, d) # this also updates estim.x̂ (they are the same object)
225228
# --------------- stochastic model -----------------------
226-
ŷs = zeros(model.ny)
229+
ŷs = zeros(NT, model.ny)
227230
ŷs[estim.i_ym] = ym - ŷd[estim.i_ym] # ŷs=0 for unmeasured outputs
228231
x̂s[:] = estim.Âs*x̂s + estim.B̂s*ŷs
229232
return x̂d
@@ -247,11 +250,11 @@ of the measured ``\mathbf{ŷ_s^m} = \mathbf{y^m} - \mathbf{ŷ_d^m}`` and unmea
247250
This estimator does not augment the state vector, thus ``\mathbf{x̂ = x̂_d}``. See
248251
[`init_internalmodel`](@ref) for details.
249252
"""
250-
function init_estimate!(estim::InternalModel, model::LinModel, u, ym, d)
253+
function init_estimate!(estim::InternalModel, model::LinModel{NT}, u, ym, d) where NT<:Real
251254
x̂d, x̂s = estim.x̂d, estim.x̂s
252255
x̂d[:] = (I - model.A)\(model.Bu*u + model.Bd*d)
253256
ŷd = h(model, x̂d, d)
254-
ŷs = zeros(model.ny)
257+
ŷs = zeros(NT, model.ny)
255258
ŷs[estim.i_ym] = ym - ŷd[estim.i_ym] # ŷs=0 for unmeasured outputs
256259
x̂s[:] = (I-estim.Âs)\estim.B̂s*ŷs
257260
return nothing

src/estimator/kalman.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -540,7 +540,7 @@ noise, respectively.
540540
Kalman, H∞, and Nonlinear Approaches", John Wiley & Sons, p. 433–459, https://doi.org/10.1002/0470045345.ch14,
541541
ISBN9780470045343.
542542
"""
543-
function update_estimate!(estim::UnscentedKalmanFilter{NT, SM}, u, ym, d) where {NT<:Real, SM}
543+
function update_estimate!(estim::UnscentedKalmanFilter{NT}, u, ym, d) where NT<:Real
544544
x̂, P̂, Q̂, R̂, K̂ = estim.x̂, estim.P̂, estim.Q̂, estim.R̂, estim.
545545
nym, nx̂, nσ = estim.nym, estim.nx̂, estim.
546546
γ, m̂, Ŝ = estim.γ, estim.m̂, estim.

src/estimator/luenberger.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ struct Luenberger{NT<:Real, SM<:LinModel} <: StateEstimator{NT}
3535
error("Cannot compute the Luenberger gain K̂ with specified poles p̂.")
3636
end
3737
Ĉm, D̂dm = Ĉ[i_ym, :], D̂d[i_ym, :] # measured outputs ym only
38-
lastu0 = zeros(model.nu)
39-
= [zeros(model.nx); zeros(nxs)]
38+
lastu0 = zeros(NT, model.nu)
39+
= [zeros(NT, model.nx); zeros(NT, nxs)]
4040
return new{NT, SM}(
4141
model,
4242
lastu0, x̂,

src/model/nonlinmodel.jl

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ struct NonLinModel{NT<:Real, F<:Function, H<:Function} <: SimModel{NT}
1414
f::F, h::H, Ts, nu, nx, ny, nd
1515
) where {NT<:Real, F<:Function, H<:Function}
1616
Ts > 0 || error("Sampling time Ts must be positive")
17-
validate_fcts(f, h)
17+
validate_fcts(NT, f, h)
1818
uop = zeros(NT, nu)
1919
yop = zeros(NT, ny)
2020
dop = zeros(NT, nd)
@@ -74,24 +74,18 @@ function NonLinModel(
7474
end
7575

7676
"Validate `f` and `h` function argument signatures."
77-
function validate_fcts(f, h)
77+
function validate_fcts(NT, f, h)
7878
fargsvalid1 = hasmethod(f,
79-
Tuple{Vector{Float64}, Vector{Float64}, Vector{Float64}}
79+
Tuple{Vector{NT}, Vector{NT}, Vector{NT}}
8080
)
81-
fargsvalid2 = hasmethod(f,
82-
Tuple{Vector{ComplexF64}, Vector{Float64}, Vector{Float64}}
83-
)
84-
if !fargsvalid1 && !fargsvalid2
85-
error("state function has no method of type "*
86-
"f(x::Vector{Float64}, u::Vector{Float64}, d::Vector{Float64}) or "*
87-
"f(x::Vector{ComplexF64}, u::Vector{Float64}, d::Vector{Float64})")
81+
if !fargsvalid1
82+
error("state function has no method with type signature "*
83+
"f(x::Vector{$(NT)}, u::Vector{$(NT)}, d::Vector{$(NT)})")
8884
end
89-
hargsvalid1 = hasmethod(h,Tuple{Vector{Float64}, Vector{Float64}})
90-
hargsvalid2 = hasmethod(h,Tuple{Vector{ComplexF64}, Vector{Float64}})
91-
if !hargsvalid1 && !hargsvalid2
92-
error("output function has no method of type "*
93-
"h(x::Vector{Float64}, d::Vector{Float64}) or "*
94-
"h(x::Vector{ComplexF64}, d::Vector{Float64})")
85+
hargsvalid1 = hasmethod(h,Tuple{Vector{NT}, Vector{NT}})
86+
if !hargsvalid1
87+
error("output function has no method with type signature "*
88+
"h(x::Vector{$(NT)}, d::Vector{$(NT)})")
9589
end
9690
end
9791

0 commit comments

Comments
 (0)