Skip to content

Commit c8b42eb

Browse files
test: add more comprehensive tests for FMIComponent
1 parent 300fd82 commit c8b42eb

File tree

1 file changed

+183
-37
lines changed

1 file changed

+183
-37
lines changed

test/extensions/fmi.jl

Lines changed: 183 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -81,83 +81,229 @@ const FMU_DIR = joinpath(@__DIR__, "fmus")
8181
end
8282
end
8383

84-
@testset "IO Model" begin
85-
@testset "v2, ME" begin
86-
fmu = loadFMU(joinpath(FMU_DIR, "SimpleAdder.fmu"); type = :ME)
87-
@named adder = MTK.FMIComponent(Val(2); fmu, type = :ME)
88-
@test MTK.isinput(adder.a)
89-
@test MTK.isinput(adder.b)
90-
@test MTK.isoutput(adder.out)
91-
@test !MTK.isinput(adder.c) && !MTK.isoutput(adder.c)
84+
@mtkmodel SimpleAdder begin
85+
@variables begin
86+
a(t)
87+
b(t)
88+
c(t)
89+
out(t)
90+
out2(t)
91+
end
92+
@parameters begin
93+
value = 1.0
94+
end
95+
@equations begin
96+
out ~ a + b + value
97+
D(c) ~ out
98+
out2 ~ 2c
99+
end
100+
end
101+
102+
@mtkmodel StateSpace begin
103+
@variables begin
104+
x(t)
105+
y(t)
106+
u(t)
107+
end
108+
@parameters begin
109+
A = 1.0
110+
B = 1.0
111+
C = 1.0
112+
_D = 1.0
113+
end
114+
@equations begin
115+
D(x) ~ A * x + B * u
116+
y ~ C * x + _D * u
117+
end
118+
end
92119

120+
@testset "IO Model" begin
121+
function build_simple_adder(adder)
93122
@variables a(t) b(t) c(t) [guess = 1.0]
94123
@mtkbuild sys = ODESystem(
95124
[adder.a ~ a, adder.b ~ b, D(a) ~ t,
96125
D(b) ~ adder.out + adder.c, c^2 ~ adder.out + adder.value],
97126
t;
98127
systems = [adder])
99-
100128
# c will be solved for by initialization
101129
# this tests that initialization also works with FMUs
102-
prob = ODEProblem(sys, [sys.adder.c => 1.0, sys.a => 1.0, sys.b => 1.0], (0.0, 1.0))
103-
sol = solve(prob, Rodas5P(autodiff = false))
130+
prob = ODEProblem(sys, [sys.adder.c => 2.0, sys.a => 1.0, sys.b => 1.0],
131+
(0.0, 1.0), [sys.adder.value => 2.0])
132+
return sys, prob
133+
end
134+
135+
@named adder = SimpleAdder()
136+
truesys, trueprob = build_simple_adder(adder)
137+
truesol = solve(trueprob, abstol = 1e-8, reltol = 1e-8)
138+
@test SciMLBase.successful_retcode(truesol)
139+
140+
@testset "v2, ME" begin
141+
fmu = loadFMU(joinpath(FMU_DIR, "SimpleAdder.fmu"); type = :ME)
142+
@named adder = MTK.FMIComponent(Val(2); fmu, type = :ME)
143+
@test MTK.isinput(adder.a)
144+
@test MTK.isinput(adder.b)
145+
@test MTK.isoutput(adder.out)
146+
@test MTK.isoutput(adder.out2)
147+
@test !MTK.isinput(adder.c) && !MTK.isoutput(adder.c)
148+
149+
sys, prob = build_simple_adder(adder)
150+
sol = solve(prob, Rodas5P(autodiff = false), abstol = 1e-8, reltol = 1e-8)
104151
@test SciMLBase.successful_retcode(sol)
152+
153+
@test truesol(sol.t;
154+
idxs = [truesys.a, truesys.b, truesys.c, truesys.adder.c]).usol[[
155+
sys.a, sys.b, sys.c, sys.adder.c]] rtol=1e-7
105156
end
106157
@testset "v2, CS" begin
107158
fmu = loadFMU(joinpath(FMU_DIR, "SimpleAdder.fmu"); type = :CS)
108159
@named adder = MTK.FMIComponent(
109-
Val(2); fmu, type = :CS, communication_step_size = 0.001)
160+
Val(2); fmu, type = :CS, communication_step_size = 1e-3,
161+
reinitializealg = BrownFullBasicInit())
110162
@test MTK.isinput(adder.a)
111163
@test MTK.isinput(adder.b)
112164
@test MTK.isoutput(adder.out)
165+
@test MTK.isoutput(adder.out2)
113166
@test !MTK.isinput(adder.c) && !MTK.isoutput(adder.c)
114-
@variables a(t) b(t) c(t) [guess = 1.0]
115-
@mtkbuild sys = ODESystem(
116-
[adder.a ~ a, adder.b ~ b, D(a) ~ t,
117-
D(b) ~ adder.out + adder.c, c^2 ~ adder.out + adder.value],
118-
t;
119-
systems = [adder])
120167

121-
# c will be solved for by initialization
122-
# this tests that initialization also works with FMUs
123-
prob = ODEProblem(sys, [sys.adder.c => 1.0, sys.a => 1.0, sys.b => 1.0],
124-
(0.0, 1.0); use_scc = false)
125-
sol = solve(prob, Rodas5P(autodiff = false))
168+
sys, prob = build_simple_adder(adder)
169+
sol = solve(prob, Rodas5P(autodiff = false), abstol = 1e-8, reltol = 1e-8)
126170
@test SciMLBase.successful_retcode(sol)
171+
172+
@test truesol(sol.t; idxs = [truesys.a, truesys.b, truesys.c]).usol.u rtol=1e-2
173+
# sys.adder.c is a discrete variable
174+
@test truesol(sol.t; idxs = truesys.adder.c)sol(sol.t; idxs = sys.adder.c) rtol=1e-3
127175
end
128176

177+
function build_sspace_model(sspace)
178+
@variables u(t)=1.0 x(t)=1.0 y(t) [guess = 1.0]
179+
@mtkbuild sys = ODESystem(
180+
[sspace.u ~ u, D(u) ~ t, D(x) ~ sspace.x + sspace.y, y^2 ~ sspace.y + sspace.x], t;
181+
systems = [sspace]
182+
)
183+
184+
prob = ODEProblem(
185+
sys, [sys.sspace.x => 1.0], (0.0, 1.0), [sys.sspace.A => 2.0]; use_scc = false)
186+
return sys, prob
187+
end
188+
189+
@named sspace = StateSpace()
190+
truesys, trueprob = build_sspace_model(sspace)
191+
truesol = solve(trueprob, abstol = 1e-8, reltol = 1e-8)
192+
@test SciMLBase.successful_retcode(truesol)
193+
129194
@testset "v3, ME" begin
130195
fmu = loadFMU(joinpath(FMU_DIR, "StateSpace.fmu"); type = :ME)
131196
@named sspace = MTK.FMIComponent(Val(3); fmu, type = :ME)
132197
@test MTK.isinput(sspace.u)
133198
@test MTK.isoutput(sspace.y)
134199
@test !MTK.isinput(sspace.x) && !MTK.isoutput(sspace.x)
135-
@variables u(t)=1.0 x(t)=1.0 y(t) [guess = 1.0]
136-
@mtkbuild sys = ODESystem(
137-
[sspace.u ~ u, D(u) ~ t, D(x) ~ sspace.x + sspace.y, y^2 ~ sspace.y + x], t;
138-
systems = [sspace]
139-
)
140200

141-
prob = ODEProblem(sys, [sys.sspace.x => 1.0], (0.0, 1.0); use_scc = false)
142-
sol = solve(prob, Rodas5P(autodiff = false))
201+
sys, prob = build_sspace_model(sspace)
202+
sol = solve(prob, Rodas5P(autodiff = false); abstol = 1e-8, reltol = 1e-8)
143203
@test SciMLBase.successful_retcode(sol)
204+
205+
@test truesol(sol.t;
206+
idxs = [truesys.u, truesys.x, truesys.y, truesys.sspace.x]).usol[[
207+
sys.u, sys.x, sys.y, sys.sspace.x]] rtol=1e-7
144208
end
145209

146210
@testset "v3, CS" begin
147211
fmu = loadFMU(joinpath(FMU_DIR, "StateSpace.fmu"); type = :CS)
148212
@named sspace = MTK.FMIComponent(
149-
Val(3); fmu, communication_step_size = 1e-3, type = :CS)
213+
Val(3); fmu, communication_step_size = 1e-4, type = :CS,
214+
reinitializealg = BrownFullBasicInit())
150215
@test MTK.isinput(sspace.u)
151216
@test MTK.isoutput(sspace.y)
152217
@test !MTK.isinput(sspace.x) && !MTK.isoutput(sspace.x)
153-
@variables u(t)=1.0 x(t)=1.0 y(t) [guess = 1.0]
218+
219+
sys, prob = build_sspace_model(sspace)
220+
sol = solve(prob, Rodas5P(autodiff = false); abstol = 1e-8, reltol = 1e-8)
221+
@test SciMLBase.successful_retcode(sol)
222+
223+
@test truesol(
224+
sol.t; idxs = [truesys.u, truesys.x, truesys.y]).usol[[sys.u, sys.x, sys.y]] rtol=1e-2
225+
@test truesol(sol.t; idxs = truesys.sspace.x).usol(sol.t; idxs = sys.sspace.x).u rtol=1e-2
226+
end
227+
end
228+
229+
@testset "FMUs in a loop" begin
230+
function build_looped_adders(adder1, adder2)
231+
@variables x(t) = 1
154232
@mtkbuild sys = ODESystem(
155-
[sspace.u ~ u, D(u) ~ t, D(x) ~ sspace.x + sspace.y, y^2 ~ sspace.y + x], t;
156-
systems = [sspace]
157-
)
233+
[D(x) ~ x, adder1.a ~ adder2.out2,
234+
adder2.a ~ adder1.out2, adder1.b ~ 1.0, adder2.b ~ 2.0],
235+
t;
236+
systems = [adder1, adder2])
237+
prob = ODEProblem(
238+
sys, [adder1.c => 1.0, adder2.c => 1.0, adder1.a => 2.0], (0.0, 1.0))
239+
return sys, prob
240+
end
241+
@named adder1 = SimpleAdder()
242+
@named adder2 = SimpleAdder()
243+
truesys, trueprob = build_looped_adders(adder1, adder2)
244+
truesol = solve(trueprob, Tsit5(), reltol = 1e-8)
245+
@test SciMLBase.successful_retcode(truesol)
246+
247+
@testset "v2, ME" begin
248+
fmu = loadFMU(joinpath(FMU_DIR, "SimpleAdder.fmu"); type = :ME)
249+
@named adder1 = MTK.FMIComponent(Val(2); fmu, type = :ME)
250+
@named adder2 = MTK.FMIComponent(Val(2); fmu, type = :ME)
251+
sys, prob = build_looped_adders(adder1, adder2)
252+
sol = solve(prob, Rodas5P(autodiff = false); reltol = 1e-8)
253+
@test SciMLBase.successful_retcode(sol)
254+
@test truesol(sol.t;
255+
idxs = [truesys.adder1.c, truesys.adder2.c]).usol(
256+
sol.t; idxs = [sys.adder1.c, sys.adder2.c]).u rtol=1e-7
257+
end
258+
@testset "v2, CS" begin
259+
fmu = loadFMU(joinpath(FMU_DIR, "SimpleAdder.fmu"); type = :CS)
260+
@named adder1 = MTK.FMIComponent(
261+
Val(2); fmu, type = :CS, communication_step_size = 1e-3)
262+
@named adder2 = MTK.FMIComponent(
263+
Val(2); fmu, type = :CS, communication_step_size = 1e-3)
264+
sys, prob = build_looped_adders(adder1, adder2)
265+
sol = solve(prob, Tsit5(); reltol = 1e-8)
266+
@test truesol(sol.t;
267+
idxs = [truesys.adder1.c, truesys.adder2.c]).usol(
268+
sol.t; idxs = [sys.adder1.c, sys.adder2.c]).u rtol=1e-3
269+
end
270+
271+
function build_looped_sspace(sspace1, sspace2)
272+
@variables x(t) = 1
273+
@mtkbuild sys = ODESystem([D(x) ~ x, sspace1.u ~ sspace2.x, sspace2.u ~ sspace1.y],
274+
t; systems = [sspace1, sspace2])
275+
prob = ODEProblem(sys, [sspace1.x => 1.0, sspace2.x => 1.0], (0.0, 1.0))
276+
return sys, prob
277+
end
278+
@named sspace1 = StateSpace()
279+
@named sspace2 = StateSpace()
280+
truesys, trueprob = build_looped_sspace(sspace1, sspace2)
281+
truesol = solve(trueprob, Rodas5P(), reltol = 1e-8)
282+
@test SciMLBase.successful_retcode(truesol)
283+
284+
@testset "v3, ME" begin
285+
fmu = loadFMU(joinpath(FMU_DIR, "StateSpace.fmu"); type = :ME)
286+
@named sspace1 = MTK.FMIComponent(Val(3); fmu, type = :ME)
287+
@named sspace2 = MTK.FMIComponent(Val(3); fmu, type = :ME)
288+
sys, prob = build_looped_sspace(sspace1, sspace2)
289+
sol = solve(prob, Rodas5P(autodiff = false); reltol = 1e-8)
290+
@test SciMLBase.successful_retcode(sol)
291+
@test truesol(sol.t;
292+
idxs = [truesys.sspace1.x, truesys.sspace2.x]).usol(
293+
sol.t; idxs = [sys.sspace1.x, sys.sspace2.x]).u rtol=1e-7
294+
end
158295

159-
prob = ODEProblem(sys, [sys.sspace.x => 1.0], (0.0, 1.0); use_scc = false)
160-
sol = solve(prob, Rodas5P(autodiff = false))
296+
@testset "v3, CS" begin
297+
fmu = loadFMU(joinpath(FMU_DIR, "StateSpace.fmu"); type = :CS)
298+
@named sspace1 = MTK.FMIComponent(
299+
Val(3); fmu, type = :CS, communication_step_size = 1e-4)
300+
@named sspace2 = MTK.FMIComponent(
301+
Val(3); fmu, type = :CS, communication_step_size = 1e-4)
302+
sys, prob = build_looped_sspace(sspace1, sspace2)
303+
sol = solve(prob, Rodas5P(autodiff = false); reltol = 1e-8)
161304
@test SciMLBase.successful_retcode(sol)
305+
@test truesol(sol.t;
306+
idxs = [truesys.sspace1.x, truesys.sspace2.x]).usol(
307+
sol.t; idxs = [sys.sspace1.x, sys.sspace2.x]).u rtol=1e-2
162308
end
163309
end

0 commit comments

Comments
 (0)