From 0185867749319df2ff02bf0b70acedd4854a8c76 Mon Sep 17 00:00:00 2001 From: ThummeTo <83663542+ThummeTo@users.noreply.github.com> Date: Sat, 3 Feb 2024 19:27:36 +0100 Subject: [PATCH] v0.13.2 (#214) * performance improvements * WIP --- Project.toml | 4 +- src/FMI.jl | 44 ++--- src/FMI2/sim.jl | 2 +- src/extensions/CSV.jl | 1 + src/extensions/JLD2.jl | 4 +- src/extensions/MAT.jl | 3 +- src/extensions/Plots.jl | 2 + test/FMI2/cs_me.jl | 42 +--- test/FMI2/exec_config.jl | 53 +++-- test/FMI2/getter_setter.jl | 17 +- test/FMI2/load_save.jl | 2 +- test/FMI2/performance.jl | 101 +++++++--- test/FMI2/plots.jl | 2 +- test/FMI2/sim_CS.jl | 34 +--- test/FMI2/sim_ME.jl | 374 ++++++++++++++---------------------- test/FMI2/sim_auto.jl | 17 +- test/FMI2/sim_zero_state.jl | 4 +- test/FMI2/state.jl | 2 +- test/Project.toml | 2 + test/runtests.jl | 49 +++-- 20 files changed, 333 insertions(+), 426 deletions(-) diff --git a/Project.toml b/Project.toml index a9ba2d20..007b6f38 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "FMI" uuid = "14a09403-18e3-468f-ad8a-74f8dda2d9ac" authors = ["TT ", "LM ", "JK "] -version = "0.13.1" +version = "0.13.2" [deps] DifferentialEquations = "0c46a032-eb83-5123-abaf-570d42b7fbaa" @@ -14,7 +14,7 @@ Requires = "ae029012-a4dd-5104-9daa-d747884805df" ThreadPools = "b189fb0b-2eb5-4ed4-bc0c-d34c51242431" [compat] -DifferentialEquations = "7.7.0 - 7.11" +DifferentialEquations = "7.7.0 - 7.12" Downloads = "1" FMIExport = "0.3.0" FMIImport = "0.16.0" diff --git a/src/FMI.jl b/src/FMI.jl index 0e91c647..e0c51a63 100644 --- a/src/FMI.jl +++ b/src/FMI.jl @@ -127,34 +127,34 @@ include("FMI3/sim.jl") include("deprecated.jl") # from FMI2_plot.jl -function fmiPlot(solution::FMUSolution; kwargs...) - @assert false "fmiPlot(...) needs `Plots` package. Please install `Plots` and do `using Plots` or `import Plots`." -end -function fmiPlot!(fig, solution::FMUSolution; kwargs...) - @assert false "fmiPlot!(...) needs `Plots` package. Please install `Plots` and do `using Plots` or `import Plots`." -end -export fmiPlot, fmiPlot! +# function fmiPlot(solution::FMUSolution; kwargs...) +# @assert false "fmiPlot(...) needs `Plots` package. Please install `Plots` and do `using Plots` or `import Plots`." +# end +# function fmiPlot!(fig, solution::FMUSolution; kwargs...) +# @assert false "fmiPlot!(...) needs `Plots` package. Please install `Plots` and do `using Plots` or `import Plots`." +# end +# export fmiPlot, fmiPlot! # from FMI2_JLD2.jl -function fmiSaveSolutionJLD2(solution::FMUSolution, filepath::AbstractString; keyword="solution") - @assert false "fmiSave(...) needs `JLD2` package. Please install `JLD2` and do `using JLD2` or `import JLD2`." -end -function fmiLoadSolutionJLD2(path::AbstractString; keyword="solution") - @assert false "fmiLoad(...) needs `JLD2` package. Please install `JLD2` and do `using JLD2` or `import JLD2`." -end -export fmiSaveSolutionJLD2, fmiLoadSolutionJLD2 +# function fmiSaveSolutionJLD2(solution::FMUSolution, filepath::AbstractString; keyword="solution") +# @assert false "fmiSave(...) needs `JLD2` package. Please install `JLD2` and do `using JLD2` or `import JLD2`." +# end +# function fmiLoadSolutionJLD2(path::AbstractString; keyword="solution") +# @assert false "fmiLoad(...) needs `JLD2` package. Please install `JLD2` and do `using JLD2` or `import JLD2`." +# end +# export fmiSaveSolutionJLD2, fmiLoadSolutionJLD2 # from CSV.jl -function fmiSaveSolutionCSV(solution::FMUSolution, filepath::AbstractString) - @assert false "fmiSave(...) needs `CSV` and `DataFrames` package. Please install `CSV` and `DataFrames` and do `using CSV, DataFrames` or `import CSV, DataFrames`." -end -export fmiSaveSolutionCSV +# function fmiSaveSolutionCSV(solution::FMUSolution, filepath::AbstractString) +# @assert false "fmiSave(...) needs `CSV` and `DataFrames` package. Please install `CSV` and `DataFrames` and do `using CSV, DataFrames` or `import CSV, DataFrames`." +# end +# export fmiSaveSolutionCSV # from MAT.jl -function fmiSaveSolutionMAT(solution::FMUSolution, filepath::AbstractString) - @assert false "fmiSave(...) needs `MAT` package. Please install `MAT` and do `using MAT` or `import MAT`." -end -export fmiSaveSolutionMAT +# function fmiSaveSolutionMAT(solution::FMUSolution, filepath::AbstractString) +# @assert false "fmiSave(...) needs `MAT` package. Please install `MAT` and do `using MAT` or `import MAT`." +# end +# export fmiSaveSolutionMAT # from FMI3_plot.jl # function fmiPlot(solution::FMU3Solution; kwargs...) diff --git a/src/FMI2/sim.jl b/src/FMI2/sim.jl index 45a23298..7d122e27 100644 --- a/src/FMI2/sim.jl +++ b/src/FMI2/sim.jl @@ -154,7 +154,7 @@ function saveEventIndicators(c::FMU2Component, recordEventIndicators, x, t, inte c.solution.evals_saveeventindicators += 1 out = zeros(fmi2Real, c.fmu.modelDescription.numberOfEventIndicators) - condition!(c, out, x, t, inputFunction) + indicators!(c, out, x, t, inputFunction) # ToDo: Replace by inplace statement! return (out[recordEventIndicators]...,) diff --git a/src/extensions/CSV.jl b/src/extensions/CSV.jl index 70ab308e..eed60aea 100644 --- a/src/extensions/CSV.jl +++ b/src/extensions/CSV.jl @@ -20,3 +20,4 @@ function fmiSaveSolutionCSV(solution::FMUSolution, filepath::AbstractString) end CSV.write(filepath, df) end +export fmiSaveSolutionCSV diff --git a/src/extensions/JLD2.jl b/src/extensions/JLD2.jl index cc9dfdfd..660e0eba 100644 --- a/src/extensions/JLD2.jl +++ b/src/extensions/JLD2.jl @@ -16,6 +16,7 @@ See also [`fmiSaveSolutionCSV`](@ref), [`fmiSaveSolutionMAT`](@ref), [`fmiLoadSo function fmiSaveSolutionJLD2(solution::FMUSolution, filepath::AbstractString; keyword="solution") return JLD2.save(filepath, Dict(keyword=>solution)) end +export fmiSaveSolutionJLD2 """ fmiLoadSolutionJLD2(filepath::AbstractString; keyword="solution") @@ -27,4 +28,5 @@ See also [`fmiSaveSolutionCSV`](@ref), [`fmiSaveSolutionMAT`](@ref), [`fmiSaveSo """ function fmiLoadSolutionJLD2(filepath::AbstractString; keyword="solution") return JLD2.load(filepath, keyword) -end \ No newline at end of file +end +export fmiLoadSolutionJLD2 \ No newline at end of file diff --git a/src/extensions/MAT.jl b/src/extensions/MAT.jl index dee7592c..ca79ce86 100644 --- a/src/extensions/MAT.jl +++ b/src/extensions/MAT.jl @@ -26,4 +26,5 @@ function fmiSaveSolutionMAT(solution::FMUSolution, filepath::AbstractString) end MAT.close(file) -end \ No newline at end of file +end +export fmiSaveSolutionMAT \ No newline at end of file diff --git a/src/extensions/Plots.jl b/src/extensions/Plots.jl index 7d3a4a8c..32aafb13 100644 --- a/src/extensions/Plots.jl +++ b/src/extensions/Plots.jl @@ -19,6 +19,7 @@ function fmiPlot(solution::FMUSolution; kwargs...) fmiPlot!(fig, solution; kwargs...) return fig end +export fmiPlot """ fmiPlot!(fig::Plots.Plot, solution::FMUSolution; @@ -209,6 +210,7 @@ function fmiPlot!(fig::Plots.Plot, solution::FMUSolution; return fig end +export fmiPlot! """ Plots.plot(solution::FMUSolution; kwargs...) diff --git a/test/FMI2/cs_me.jl b/test/FMI2/cs_me.jl index a66aa060..2e0de3cd 100644 --- a/test/FMI2/cs_me.jl +++ b/test/FMI2/cs_me.jl @@ -5,21 +5,9 @@ t_start = 0.0 t_stop = 1.0 -myFMU = fmiLoad("SpringPendulum1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"]) -@test fmiIsCoSimulation(myFMU) -@test fmiIsModelExchange(myFMU) - -comp = fmiInstantiate!(myFMU; loggingOn=false) -@test comp != 0 -# choose FMU or FMUComponent -fmuStruct = nothing -envFMUSTRUCT = ENV["FMUSTRUCT"] -if envFMUSTRUCT == "FMU" - fmuStruct = myFMU -elseif envFMUSTRUCT == "FMUCOMPONENT" - fmuStruct = comp -end -@assert fmuStruct !== nothing "Unknown fmuStruct, environment variable `FMUSTRUCT` = `$envFMUSTRUCT`" +### + +fmuStruct, myFMU = getFMUStruct("SpringPendulum1D") sol = fmiSimulateCS(fmuStruct, (t_start, t_stop)) @test sol.success @@ -28,34 +16,18 @@ sol = fmiSimulateME(fmuStruct, (t_start, t_stop); solver=FBDF(autodiff=false)) fmiUnload(myFMU) +### +fmuStruct, myFMU = getFMUStruct("SpringPendulum1D"; type=:ME) -myFMU = fmiLoad("SpringPendulum1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"]; type=:ME) -@test myFMU.type == FMI.fmi2TypeModelExchange -comp = fmiInstantiate!(myFMU; loggingOn=false) -@test comp.type == FMI.fmi2TypeModelExchange -fmuStruct = nothing -if envFMUSTRUCT == "FMU" - fmuStruct = myFMU -elseif envFMUSTRUCT == "FMUCOMPONENT" - fmuStruct = comp -end sol = fmiSimulate(fmuStruct, (t_start, t_stop); solver=FBDF(autodiff=false)) @test sol.success fmiUnload(myFMU) +### +fmuStruct, myFMU = getFMUStruct("SpringPendulum1D"; type=:CS) -myFMU = fmiLoad("SpringPendulum1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"]; type=:CS) -@test myFMU.type == FMI.fmi2TypeCoSimulation -comp = fmiInstantiate!(myFMU; loggingOn=false) -@test comp.type == FMI.fmi2TypeCoSimulation -fmuStruct = nothing -if envFMUSTRUCT == "FMU" - fmuStruct = myFMU -elseif envFMUSTRUCT == "FMUCOMPONENT" - fmuStruct = comp -end sol = fmiSimulate(fmuStruct, (t_start, t_stop)) @test sol.success fmiUnload(myFMU) \ No newline at end of file diff --git a/test/FMI2/exec_config.jl b/test/FMI2/exec_config.jl index b90bf7ec..9b63a364 100644 --- a/test/FMI2/exec_config.jl +++ b/test/FMI2/exec_config.jl @@ -8,34 +8,27 @@ using FMI.FMIImport t_start = 0.0 t_stop = 1.0 -myFMU = fmiLoad("SpringPendulum1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"]) - -comp = fmiInstantiate!(myFMU; loggingOn=false, type=fmi2TypeCoSimulation) -@test comp != 0 -# choose FMU or FMUComponent -fmuStruct = nothing -envFMUSTRUCT = ENV["FMUSTRUCT"] -if envFMUSTRUCT == "FMU" - fmuStruct = myFMU -elseif envFMUSTRUCT == "FMUCOMPONENT" - fmuStruct = comp -end -@assert fmuStruct !== nothing "Unknown fmuStruct, environment variable `FMUSTRUCT` = `$envFMUSTRUCT`" +fmuStruct = Dict{Symbol, Union{FMU2, FMU2Component}}() +myFMU = Dict{Symbol, FMU2}() + +fmuStruct[:ME], myFMU[:ME] = getFMUStruct("SpringPendulum1D"; type=:ME) +fmuStruct[:CS], myFMU[:CS] = getFMUStruct("SpringPendulum1D"; type=:CS) for execConf in (FMU2_EXECUTION_CONFIGURATION_NO_FREEING, FMU2_EXECUTION_CONFIGURATION_RESET, FMU2_EXECUTION_CONFIGURATION_NO_RESET) # ToDo: Add `FMU2_EXECUTION_CONFIGURATION_NOTHING` for mode in (:CS, :ME) - global fmuStruct + global fmuStruct, myFMU + @info "\t$(mode) | $(execConf)" - myFMU.executionConfig = execConf + myFMU[mode].executionConfig = execConf # sim test - numInst = length(myFMU.components) + numInst = length(myFMU[mode].components) if mode == :CS - fmiSimulateCS(fmuStruct, (t_start, t_stop)) + fmiSimulateCS(fmuStruct[mode], (t_start, t_stop)) elseif mode == :ME - fmiSimulateME(fmuStruct, (t_start, t_stop)) + fmiSimulateME(fmuStruct[mode], (t_start, t_stop)) else @assert false "Unknown mode `$(mode)`." end @@ -47,21 +40,26 @@ for execConf in (FMU2_EXECUTION_CONFIGURATION_NO_FREEING, FMU2_EXECUTION_CONFIGU numInst -= 1 end - @test length(myFMU.components) == numInst + @test length(myFMU[mode].components) == numInst + + othermode = (mode == :CS ? :ME : :CS) # prepare next run start - if envFMUSTRUCT == "FMU" + if isa(fmuStruct[mode], FMU2) if !execConf.freeInstance - fmi2FreeInstance!(myFMU) - end - fmi2Instantiate!(myFMU; type=(mode==:CS ? fmi2TypeModelExchange : fmi2TypeCoSimulation)) + fmi2FreeInstance!(myFMU[mode]) + end + + fmi2Instantiate!(myFMU[othermode]; type=(othermode==:ME ? fmi2TypeModelExchange : fmi2TypeCoSimulation)) - elseif envFMUSTRUCT == "FMUCOMPONENT" + elseif isa(fmuStruct[mode], FMU2Component) if !execConf.freeInstance - fmi2FreeInstance!(fmuStruct) + fmi2FreeInstance!(fmuStruct[mode]) end - fmuStruct = fmi2Instantiate!(myFMU; type=(mode==:CS ? fmi2TypeModelExchange : fmi2TypeCoSimulation)) + fmuStruct[othermode] = fmi2Instantiate!(myFMU[othermode]; type=(othermode==:ME ? fmi2TypeModelExchange : fmi2TypeCoSimulation)) + else + @assert false "Unknwon fmuStruct type `$(typeof(fmuStruct[mode]))`" end # prepare next run end @@ -69,4 +67,5 @@ for execConf in (FMU2_EXECUTION_CONFIGURATION_NO_FREEING, FMU2_EXECUTION_CONFIGU end end -fmiUnload(myFMU) \ No newline at end of file +fmiUnload(myFMU[:ME]) +fmiUnload(myFMU[:CS]) \ No newline at end of file diff --git a/test/FMI2/getter_setter.jl b/test/FMI2/getter_setter.jl index f15a811f..a5a08bd1 100644 --- a/test/FMI2/getter_setter.jl +++ b/test/FMI2/getter_setter.jl @@ -7,19 +7,12 @@ # Prepare FMU # ############### -myFMU = fmiLoad("IO", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"]; type=:CS) -comp = fmiInstantiate!(myFMU; loggingOn=false) -@test comp != 0 - -# choose FMU or FMUComponent -fmuStruct = nothing -envFMUSTRUCT = ENV["FMUSTRUCT"] -if envFMUSTRUCT == "FMU" - fmuStruct = myFMU -elseif envFMUSTRUCT == "FMUCOMPONENT" - fmuStruct = comp +fmuStruct, myFMU = getFMUStruct("IO"; type=:CS) + +if isa(fmuStruct, FMU2) + # [Note] no instance allocated at this point + fmi2Instantiate!(fmuStruct) end -@assert fmuStruct != nothing "Unknown fmuStruct, environment variable `FMUSTRUCT` = `$envFMUSTRUCT`" @test fmiSetupExperiment(fmuStruct, 0.0) == 0 diff --git a/test/FMI2/load_save.jl b/test/FMI2/load_save.jl index eaad8842..5c15b332 100644 --- a/test/FMI2/load_save.jl +++ b/test/FMI2/load_save.jl @@ -13,7 +13,7 @@ t_start = 0.0 t_stop = 8.0 # load the FMU container -myFMU = fmiLoad("SpringFrictionPendulum1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"]) +fmuStruct, myFMU = getFMUStruct("SpringFrictionPendulum1D") recordValues = ["mass.s", "mass.v"] solutionME = fmiSimulateME(myFMU, (t_start, t_stop); recordValues=recordValues, solver=FBDF(autodiff=false)) diff --git a/test/FMI2/performance.jl b/test/FMI2/performance.jl index 484d2f27..66fb138e 100644 --- a/test/FMI2/performance.jl +++ b/test/FMI2/performance.jl @@ -13,11 +13,11 @@ end using FMI, FMI.FMIImport, FMI.FMIImport.FMICore, FMIZoo using BenchmarkTools, Test -fmu = fmiLoad("BouncingBall1D", "Dymola", "2022x"; type=:ME) +fmuStruct, myFMU = getFMUStruct("BouncingBall1D", "Dymola", "2022x"; type=:ME) c = fmi2Instantiate!(fmu) -function evalBenchmark(b) +evalBenchmark = function(b) res = run(b) min_time = min(res.times...) memory = res.memory @@ -147,37 +147,82 @@ ec_idcs = zeros(fmi2ValueReference, 0) t = -1.0 b = @benchmarkable eval!($cRef, $dx, $dx_refs, $y, $y_refs, $x, $u, $u_refs, $p, $p_refs, $ec, $ec_idcs, $t) min_time, memory, allocs = evalBenchmark(b) -@test allocs <= 1 -@test memory <= 80 # ToDo: ? +@test allocs <= 0 +@test memory <= 0 b = @benchmarkable $c(dx=$dx, y=$y, y_refs=$y_refs, x=$x, u=$u, u_refs=$u_refs, p=$p, p_refs=$p_refs, ec=$ec, ec_idcs=$ec_idcs, t=$t) min_time, memory, allocs = evalBenchmark(b) -@test allocs <= 8 # `ignore_derivatives` causes an extra 3 allocations (48 bytes) -@test memory <= 272 # ToDo: What is the remaning 1 allocation (112 Bytes) compared to `eval!`? +@test allocs <= 9 # [ToDo] 3 `ignore_derivatives` causes an extra 3 allocations (48 bytes) +@test memory <= 224 # [ToDo] reduce again 48 _p = () b = @benchmarkable FMI.fx($c, $dx, $x, $_p, $t, nothing) min_time, memory, allocs = evalBenchmark(b) # ToDo: This is too much, but currently necessary to be compatible with all AD-frameworks, as well as ForwardDiffChainRules -@test allocs <= 8 -@test memory <= 272 # ToDo: What is the remaning 1 allocation (16 Bytes) compared to `c(...)`? - -# using FMISensitivity -# import FMISensitivity.ForwardDiff -# import FMISensitivity.ReverseDiff -# function fun(_x) -# eval!(cRef, dx, y, y_refs, _x, u, u_refs, p, p_refs, ec, ec_idcs, t) -# end -# config = ForwardDiff.JacobianConfig(fun, x, ForwardDiff.Chunk{length(x)}()) - -# b = @benchmarkable ForwardDiff.jacobian($fun, $x, $config) -# min_time, memory, allocs = evalBenchmark(b) -# # ToDo: This is too much! -# @test allocs <= 250 -# @test memory <= 13000 - -# b = @benchmarkable ReverseDiff.jacobian($fun, $x) -# min_time, memory, allocs = evalBenchmark(b) -# # ToDo: This is too much! -# @test allocs <= 150 -# @test memory <= 10000 \ No newline at end of file +@test allocs <= 9 # [ToDo]3 +@test memory <= 224 # [ToDo] reduce again 48 + +# AD + +using FMISensitivity +import FMISensitivity.ChainRulesCore +import FMISensitivity.ChainRulesCore: ZeroTangent, NoTangent +import FMISensitivity.ForwardDiff +import FMISensitivity.ReverseDiff + +# frule +Δx = similar(x) +Δtuple = (NoTangent(), NoTangent(), NoTangent(), NoTangent(), NoTangent(), NoTangent(), Δx, NoTangent(), NoTangent(), NoTangent(), NoTangent(), NoTangent(), NoTangent(), NoTangent()) +fun = function(_x) + Ω, ∂Ω = ChainRulesCore.frule(Δtuple, eval!, cRef, dx, dx_refs, y, y_refs, _x, u, u_refs, p, p_refs, ec, ec_idcs, t) +end + +b = @benchmarkable fun($x) +min_time, memory, allocs = evalBenchmark(b) +@test allocs <= 4 +@test memory <= 144 + +# rrule +fun = function (_x) + Ω, pullback = ChainRulesCore.rrule(eval!, cRef, dx, dx_refs, y, y_refs, _x, u, u_refs, p, p_refs, ec, ec_idcs, t) +end + +b = @benchmarkable fun($x) +min_time, memory, allocs = evalBenchmark(b) +@test allocs <= 9 +@test memory <= 400 + +# rrule pullback +Ω, pullback = ChainRulesCore.rrule(eval!, cRef, dx, dx_refs, y, y_refs, x, u, u_refs, p, p_refs, ec, ec_idcs, t) +r̄ = copy(dx) +fun = function(_r̄, _pullback) + _pullback(_r̄) +end + +b = @benchmarkable fun($r̄, $pullback) +min_time, memory, allocs = evalBenchmark(b) +@test allocs <= 5 +@test memory <= 144 + +# eval! +fun = function(_x) + eval!(cRef, dx, dx_refs, y, y_refs, _x, u, u_refs, p, p_refs, ec, ec_idcs, t) +end + +b = @benchmarkable fun($x) +min_time, memory, allocs = evalBenchmark(b) +@test allocs <= 0 +@test memory <= 0 + +config = ForwardDiff.JacobianConfig(fun, x, ForwardDiff.Chunk{length(x)}()) +b = @benchmarkable ForwardDiff.jacobian($fun, $x, $config) +min_time, memory, allocs = evalBenchmark(b) +# ToDo: This is way too much! +@test allocs <= 240 +@test memory <= 13000 + +b = @benchmarkable ReverseDiff.jacobian($fun, $x) +min_time, memory, allocs = evalBenchmark(b) +# ToDo: This is way too much! +@test allocs <= 240 +@test memory <= 12000 \ No newline at end of file diff --git a/test/FMI2/plots.jl b/test/FMI2/plots.jl index 8ecbec1c..5f26625b 100644 --- a/test/FMI2/plots.jl +++ b/test/FMI2/plots.jl @@ -10,7 +10,7 @@ t_start = 0.0 t_stop = 8.0 # load the FMU container -myFMU = fmiLoad("SpringPendulum1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"]) +fmuStruct, myFMU = getFMUStruct("SpringPendulum1D") # print some useful FMU-information into the REPL fmiInfo(myFMU) diff --git a/test/FMI2/sim_CS.jl b/test/FMI2/sim_CS.jl index bc8a37ce..6d85c515 100644 --- a/test/FMI2/sim_CS.jl +++ b/test/FMI2/sim_CS.jl @@ -5,20 +5,7 @@ # case 1: CS-FMU Simulation -myFMU = fmiLoad("SpringPendulum1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"]) - -comp = fmiInstantiate!(myFMU; loggingOn=false) -@test comp != 0 - -# choose FMU or FMUComponent -fmuStruct = nothing -envFMUSTRUCT = ENV["FMUSTRUCT"] -if envFMUSTRUCT == "FMU" - fmuStruct = myFMU -elseif envFMUSTRUCT == "FMUCOMPONENT" - fmuStruct = comp -end -@assert fmuStruct !== nothing "Unknown fmuStruct, environment variable `FMUSTRUCT` = `$envFMUSTRUCT`" +fmuStruct, myFMU = getFMUStruct("SpringPendulum1D") t_start = 0.0 t_stop = 8.0 @@ -52,28 +39,15 @@ fmiUnload(myFMU) # case 2: CS-FMU with input signal -function extForce_t(t::Real, u::AbstractArray{<:Real}) +extForce_t = function (t::Real, u::AbstractArray{<:Real}) u[1] = sin(t) end -function extForce_ct(c::Union{FMU2Component, Nothing}, t::Real, u::AbstractArray{<:Real}) +extForce_ct = function (c::Union{FMU2Component, Nothing}, t::Real, u::AbstractArray{<:Real}) u[1] = sin(t) end -myFMU = fmiLoad("SpringPendulumExtForce1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"]) - -comp = fmiInstantiate!(myFMU; loggingOn=false) -@test comp != 0 - -# choose FMU or FMUComponent -fmuStruct = nothing -envFMUSTRUCT = ENV["FMUSTRUCT"] -if envFMUSTRUCT == "FMU" - fmuStruct = myFMU -elseif envFMUSTRUCT == "FMUCOMPONENT" - fmuStruct = comp -end -@assert fmuStruct !== nothing "Unknown fmuStruct, environment variable `FMUSTRUCT` = `$envFMUSTRUCT`" +fmuStruct, myFMU = getFMUStruct("SpringPendulumExtForce1D") for inpfct in [extForce_ct, extForce_t] global solution diff --git a/test/FMI2/sim_ME.jl b/test/FMI2/sim_ME.jl index c67cf368..afa7ef1f 100644 --- a/test/FMI2/sim_ME.jl +++ b/test/FMI2/sim_ME.jl @@ -3,287 +3,191 @@ # Licensed under the MIT license. See LICENSE file in the project root for details. # -using DifferentialEquations: Tsit5, Rosenbrock23, FBDF +using DifferentialEquations +using Sundials + +# to use autodiff! +using FMISensitivity +using FMI.FMIImport.FMICore: sense_setindex! t_start = 0.0 t_stop = 8.0 -abstol = 0.0001 / 10.0 -solver = FBDF(autodiff=false) -solver_ad = FBDF(autodiff=true) -dtmax_inputs = 0.01 -dtmin = 1e-64 - -# case 1: ME-FMU with state events - -myFMU = fmiLoad("SpringFrictionPendulum1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"]) - -comp = fmiInstantiate!(myFMU; loggingOn=false) -@test comp != 0 - -# choose FMU or FMUComponent -fmuStruct = nothing -envFMUSTRUCT = ENV["FMUSTRUCT"] -if envFMUSTRUCT == "FMU" - fmuStruct = myFMU -elseif envFMUSTRUCT == "FMUCOMPONENT" - fmuStruct = comp -end -@assert fmuStruct != nothing "Unknown fmuStruct, environment variable `FMUSTRUCT` = `$envFMUSTRUCT`" - -solution = fmiSimulateME(fmuStruct, (t_start, t_stop); abstol=abstol, solver=solver, dtmin=dtmin) -@test length(solution.states.u) > 0 -@test length(solution.states.t) > 0 - -@test solution.states.t[1] == t_start -@test solution.states.t[end] == t_stop - -# reference values from Simulation in Dymola2020x (Dassl) -@test solution.states.u[1] == [0.5, 0.0] -@test sum(abs.(solution.states.u[end] - [1.06736, -1.03552e-10])) < 0.1 -fmiUnload(myFMU) - -# case 2: ME-FMU with state and time events - -myFMU = fmiLoad("SpringTimeFrictionPendulum1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"]) - -comp = fmiInstantiate!(myFMU; loggingOn=false) -@test comp != 0 - -# choose FMU or FMUComponent -fmuStruct = nothing -envFMUSTRUCT = ENV["FMUSTRUCT"] -if envFMUSTRUCT == "FMU" - fmuStruct = myFMU -elseif envFMUSTRUCT == "FMUCOMPONENT" - fmuStruct = comp -end -@assert fmuStruct != nothing "Unknown fmuStruct, environment variable `FMUSTRUCT` = `$envFMUSTRUCT`" - -### test without recording values - -solution = fmiSimulateME(fmuStruct, (t_start, t_stop); abstol=abstol, solver=solver, dtmin=dtmin) -@test length(solution.states.u) > 0 -@test length(solution.states.t) > 0 - -@test solution.states.t[1] == t_start -@test solution.states.t[end] == t_stop - -# reference values from Simulation in Dymola2020x (Dassl) -@test solution.states.u[1] == [0.5, 0.0] -@test sum(abs.(solution.states.u[end] - [1.05444, 1e-10])) < 0.01 - -### test with recording values (variable step record values) - -solution = fmiSimulateME(fmuStruct, (t_start, t_stop); recordValues="mass.f", abstol=abstol, solver=solver, dtmin=dtmin) -dataLength = length(solution.states.u) -@test dataLength > 0 -@test length(solution.states.t) == dataLength -@test length(solution.values.saveval) == dataLength -@test length(solution.values.t) == dataLength - -@test solution.states.t[1] == t_start -@test solution.states.t[end] == t_stop -@test solution.values.t[1] == t_start -@test solution.values.t[end] == t_stop - -# value/state getters -@test solution.states.t == fmi2GetSolutionTime(solution) -@test collect(s[1] for s in solution.values.saveval) == fmi2GetSolutionValue(solution, 1; isIndex=true) -@test collect(u[1] for u in solution.states.u ) == fmi2GetSolutionState(solution, 1; isIndex=true) -@test isapprox(fmi2GetSolutionState(solution, 2; isIndex=true), fmi2GetSolutionDerivative(solution, 1; isIndex=true); atol=1e-4) - -# reference values from Simulation in Dymola2020x (Dassl) -@test sum(abs.(solution.states.u[1] - [0.5, 0.0])) < 1e-4 -@test sum(abs.(solution.states.u[end] - [1.05444, 1e-10])) < 0.01 -@test abs(solution.values.saveval[1][1] - 0.75) < 1e-4 -@test sum(abs.(solution.values.saveval[end][1] - -0.54435 )) < 0.015 - -### test with recording values (fixed step record values) - -tData = t_start:0.1:t_stop -solution = fmiSimulateME(fmuStruct, (t_start, t_stop); recordValues="mass.f", saveat=tData, abstol=abstol, solver=solver, dtmin=dtmin) -@test length(solution.states.u) == length(tData) -@test length(solution.states.t) == length(tData) -@test length(solution.values.saveval) == length(tData) -@test length(solution.values.t) == length(tData) - -@test solution.states.t[1] == t_start -@test solution.states.t[end] == t_stop -@test solution.values.t[1] == t_start -@test solution.values.t[end] == t_stop - -# reference values from Simulation in Dymola2020x (Dassl) -@test sum(abs.(solution.states.u[1] - [0.5, 0.0])) < 1e-4 -@test sum(abs.(solution.states.u[end] - [1.05444, 1e-10])) < 0.01 -@test abs(solution.values.saveval[1][1] - 0.75) < 1e-4 -@test sum(abs.(solution.values.saveval[end][1] - -0.54435 )) < 0.015 - -fmiUnload(myFMU) - -# case 3a: ME-FMU without events, but with input signal (explicit solver: Tsit5) - -function extForce_t(t::Real, u::AbstractArray{<:Real}) - u[1] = sin(t) +dtmax_inputs = 1e-3 +rand_x0 = rand(2) + +kwargs = Dict(:dtmin => 1e-64, :abstol => 1e-8, :reltol => 1e-6, :dt => 1e-32) +solvers = [Tsit5(), Rodas5(autodiff=false)] # [Tsit5(), FBDF(autodiff=false), FBDF(autodiff=true), Rodas5(autodiff=false), Rodas5(autodiff=true)] + +extForce_t = function(t::Real, u::AbstractArray{<:Real}) + sense_setindex!(u, sin(t), 1) end -function extForce_cxt(c::Union{FMU2Component, Nothing}, x::Union{AbstractArray{<:Real}, Nothing}, t::Real, u::AbstractArray{<:Real}) +extForce_cxt = function(c::Union{FMU2Component, Nothing}, x::Union{AbstractArray{<:Real}, Nothing}, t::Real, u::AbstractArray{<:Real}) x1 = 0.0 if x != nothing x1 = x[1] end - u[1] = sin(t) * x1 + sense_setindex!(u, sin(t) * x1, 1) end -myFMU = fmiLoad("SpringPendulumExtForce1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"]) +for solver in solvers + + global fmuStruct, fmu, solution -comp = fmiInstantiate!(myFMU; loggingOn=false) -@test comp != 0 + @info "Testing solver: $(solver)" -# choose FMU or FMUComponent -fmuStruct = nothing -envFMUSTRUCT = ENV["FMUSTRUCT"] -if envFMUSTRUCT == "FMU" - fmuStruct = myFMU -elseif envFMUSTRUCT == "FMUCOMPONENT" - fmuStruct = comp -end -@assert fmuStruct != nothing "Unknown fmuStruct, environment variable `FMUSTRUCT` = `$envFMUSTRUCT`" + # case 1: ME-FMU with state events -for inpfct in [extForce_cxt, extForce_t] - global solution + fmuStruct, fmu = getFMUStruct("SpringFrictionPendulum1D") - solution = fmiSimulateME(fmuStruct, (t_start, t_stop); inputValueReferences=["extForce"], inputFunction=inpfct, abstol=abstol, solver=solver, dtmin=dtmin, dtmax=dtmax_inputs) # dtmax to force resolution + solution = fmiSimulateME(fmuStruct, (t_start, t_stop); solver=solver, kwargs...) @test length(solution.states.u) > 0 @test length(solution.states.t) > 0 @test solution.states.t[1] == t_start - @test solution.states.t[end] == t_stop -end + @test solution.states.t[end] == t_stop + + # reference values from Simulation in Dymola2020x (Dassl) + @test solution.states.u[1] == [0.5, 0.0] + @test sum(abs.(solution.states.u[end] - [1.06736, -1.03552e-10])) < 0.1 + fmiUnload(fmu) + + # case 2: ME-FMU with state and time events + + fmuStruct, fmu = getFMUStruct("SpringTimeFrictionPendulum1D") + + ### test without recording values + + solution = fmiSimulateME(fmuStruct, (t_start, t_stop); solver=solver, kwargs...) + @test length(solution.states.u) > 0 + @test length(solution.states.t) > 0 -# reference values `extForce_t` from Simulation in Dymola2020x (Dassl) -@test solution.states.u[1] == [0.5, 0.0] -@test sum(abs.(solution.states.u[end] - [0.613371, 0.188633])) < 0.012 -fmiUnload(myFMU) + @test solution.states.t[1] == t_start + @test solution.states.t[end] == t_stop -# case 3b: ME-FMU without events, but with input signal (implicit solver: Rosenbrock23, autodiff) + # reference values from Simulation in Dymola2020x (Dassl) + @test solution.states.u[1] == [0.5, 0.0] + @test sum(abs.(solution.states.u[end] - [1.05444, 1e-10])) < 0.01 -myFMU = fmiLoad("SpringPendulumExtForce1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"]) + ### test with recording values (variable step record values) -comp = fmiInstantiate!(myFMU; loggingOn=false) -@test comp != 0 + solution = fmiSimulateME(fmuStruct, (t_start, t_stop); recordValues="mass.f", solver=solver, kwargs...) + dataLength = length(solution.states.u) + @test dataLength > 0 + @test length(solution.states.t) == dataLength + @test length(solution.values.saveval) == dataLength + @test length(solution.values.t) == dataLength -# choose FMU or FMUComponent -fmuStruct = nothing -envFMUSTRUCT = ENV["FMUSTRUCT"] -if envFMUSTRUCT == "FMU" - fmuStruct = myFMU -elseif envFMUSTRUCT == "FMUCOMPONENT" - fmuStruct = comp -end -@assert fmuStruct != nothing "Unknown fmuStruct, environment variable `FMUSTRUCT` = `$envFMUSTRUCT`" + @test solution.states.t[1] == t_start + @test solution.states.t[end] == t_stop + @test solution.values.t[1] == t_start + @test solution.values.t[end] == t_stop + + # value/state getters + @test solution.states.t == fmi2GetSolutionTime(solution) + @test collect(s[1] for s in solution.values.saveval) == fmi2GetSolutionValue(solution, 1; isIndex=true) + @test collect(u[1] for u in solution.states.u ) == fmi2GetSolutionState(solution, 1; isIndex=true) + @test isapprox(fmi2GetSolutionState(solution, 2; isIndex=true), fmi2GetSolutionDerivative(solution, 1; isIndex=true); atol=1e-1) # tolerance is large, because Rosenbrock23 solution derivative is not that accurate (other solvers reach 1e-4 for this example) + @info "Max error of solver polynominal derivative: $(max(abs.(fmi2GetSolutionState(solution, 2; isIndex=true) .- fmi2GetSolutionDerivative(solution, 1; isIndex=true))...))" + + # reference values from Simulation in Dymola2020x (Dassl) + @test sum(abs.(solution.states.u[1] - [0.5, 0.0])) < 1e-4 + @test sum(abs.(solution.states.u[end] - [1.05444, 1e-10])) < 0.01 + @test abs(solution.values.saveval[1][1] - 0.75) < 1e-4 + @test sum(abs.(solution.values.saveval[end][1] - -0.54435 )) < 0.015 + + ### test with recording values (fixed step record values) + + tData = t_start:0.1:t_stop + solution = fmiSimulateME(fmuStruct, (t_start, t_stop); recordValues="mass.f", saveat=tData, solver=solver, kwargs...) + @test length(solution.states.u) == length(tData) + @test length(solution.states.t) == length(tData) + @test length(solution.values.saveval) == length(tData) + @test length(solution.values.t) == length(tData) -# there are issues with AD in Julia < 1.7.0 -# ToDo: Fix Linux FMU -# if VERSION >= v"1.7.0" && !Sys.islinux() -# solution = fmiSimulateME(fmuStruct, (t_start, t_stop); abstol=abstol, solver=solver_ad, dtmin=dtmin, dtmax=dtmax_inputs) # dtmax to force resolution + @test solution.states.t[1] == t_start + @test solution.states.t[end] == t_stop + @test solution.values.t[1] == t_start + @test solution.values.t[end] == t_stop -# @test length(solution.states.u) > 0 -# @test length(solution.states.t) > 0 + # reference values from Simulation in Dymola2020x (Dassl) + @test sum(abs.(solution.states.u[1] - [0.5, 0.0])) < 1e-4 + @test sum(abs.(solution.states.u[end] - [1.05444, 1e-10])) < 0.01 + @test abs(solution.values.saveval[1][1] - 0.75) < 1e-4 + @test sum(abs.(solution.values.saveval[end][1] - -0.54435 )) < 0.015 -# @test solution.states.t[1] == t_start -# @test solution.states.t[end] == t_stop + fmiUnload(fmu) -# # reference values (no force) from Simulation in Dymola2020x (Dassl) -# @test solution.states.u[1] == [0.5, 0.0] -# @test sum(abs.(solution.states.u[end] - [0.509219, 0.314074])) < 0.01 -# end + # case 3a: ME-FMU without events, but with input signal -fmiUnload(myFMU) + fmuStruct, fmu = getFMUStruct("SpringPendulumExtForce1D") -# case 3c: ME-FMU without events, but with input signal (implicit solver: Rosenbrock23, no autodiff) + for inpfct in [extForce_cxt, extForce_t] + + solution = fmiSimulateME(fmuStruct, (t_start, t_stop); inputValueReferences=["extForce"], inputFunction=inpfct, solver=solver, dtmax=dtmax_inputs, kwargs...) # dtmax to force resolution + @test length(solution.states.u) > 0 + @test length(solution.states.t) > 0 -myFMU = fmiLoad("SpringPendulumExtForce1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"]) + @test solution.states.t[1] == t_start + @test solution.states.t[end] == t_stop + end -comp = fmiInstantiate!(myFMU; loggingOn=false) -@test comp != 0 + # reference values `extForce_t` from Simulation in Dymola2020x (Dassl) + @test solution.states.u[1] == [0.5, 0.0] + @test sum(abs.(solution.states.u[end] - [0.613371, 0.188633])) < 0.012 + fmiUnload(fmu) -# choose FMU or FMUComponent -fmuStruct = nothing -envFMUSTRUCT = ENV["FMUSTRUCT"] -if envFMUSTRUCT == "FMU" - fmuStruct = myFMU -elseif envFMUSTRUCT == "FMUCOMPONENT" - fmuStruct = comp -end -@assert fmuStruct != nothing "Unknown fmuStruct, environment variable `FMUSTRUCT` = `$envFMUSTRUCT`" + # case 3b: ME-FMU without events, but with input signal (autodiff) -solution = fmiSimulateME(fmuStruct, (t_start, t_stop); inputValueReferences=["extForce"], inputFunction=extForce_t, abstol=abstol, solver=solver, dtmin=dtmin, dtmax=dtmax_inputs) # dtmax to force resolution -@test length(solution.states.u) > 0 -@test length(solution.states.t) > 0 + fmuStruct, fmu = getFMUStruct("SpringPendulumExtForce1D") -@test solution.states.t[1] == t_start -@test solution.states.t[end] == t_stop + # there are issues with AD in Julia < 1.7.0 + # ToDo: Fix Linux FMU + if VERSION >= v"1.7.0" && !Sys.islinux() + solution = fmiSimulateME(fmuStruct, (t_start, t_stop); solver=solver, dtmax=dtmax_inputs, kwargs...) # dtmax to force resolution -# reference values from Simulation in Dymola2020x (Dassl) -@test solution.states.u[1] == [0.5, 0.0] -@test sum(abs.(solution.states.u[end] - [0.613371, 0.188633])) < 0.01 -fmiUnload(myFMU) + @test length(solution.states.u) > 0 + @test length(solution.states.t) > 0 -# case 4: ME-FMU without events, but saving value interpolation + @test solution.states.t[1] == t_start + @test solution.states.t[end] == t_stop -myFMU = fmiLoad("SpringPendulumExtForce1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"]) + # reference values (no force) from Simulation in Dymola2020x (Dassl) + @test solution.states.u[1] == [0.5, 0.0] + @test sum(abs.(solution.states.u[end] - [0.509219, 0.314074])) < 0.01 + end -comp = fmiInstantiate!(myFMU; loggingOn=false) -@test comp != 0 + fmiUnload(fmu) -# choose FMU or FMUComponent -fmuStruct = nothing -envFMUSTRUCT = ENV["FMUSTRUCT"] -if envFMUSTRUCT == "FMU" - fmuStruct = myFMU -elseif envFMUSTRUCT == "FMUCOMPONENT" - fmuStruct = comp -end -@assert fmuStruct != nothing "Unknown fmuStruct, environment variable `FMUSTRUCT` = `$envFMUSTRUCT`" + # case 4: ME-FMU without events, but saving value interpolation -solution = fmiSimulateME(fmuStruct, (t_start, t_stop); saveat=tData, recordValues=:states, abstol=abstol, solver=solver, dtmin=dtmin) -@test length(solution.states.u) == length(tData) -@test length(solution.states.t) == length(tData) -@test length(solution.values.saveval) == length(tData) -@test length(solution.values.t) == length(tData) + fmuStruct, fmu = getFMUStruct("SpringPendulumExtForce1D") -for i in 1:length(tData) - @test sum(abs(solution.states.t[i] - solution.states.t[i])) < 1e-6 - @test sum(abs(solution.states.u[i][1] - solution.values.saveval[i][1])) < 1e-6 - @test sum(abs(solution.states.u[i][2] - solution.values.saveval[i][2])) < 1e-6 -end + solution = fmiSimulateME(fmuStruct, (t_start, t_stop); saveat=tData, recordValues=:states, solver=solver, kwargs...) + @test length(solution.states.u) == length(tData) + @test length(solution.states.t) == length(tData) + @test length(solution.values.saveval) == length(tData) + @test length(solution.values.t) == length(tData) -fmiUnload(myFMU) + for i in 1:length(tData) + @test sum(abs(solution.states.t[i] - solution.states.t[i])) < 1e-6 + @test sum(abs(solution.states.u[i][1] - solution.values.saveval[i][1])) < 1e-6 + @test sum(abs(solution.states.u[i][2] - solution.values.saveval[i][2])) < 1e-6 + end -# case 5: ME-FMU with different (random) start state - -myFMU = fmiLoad("SpringFrictionPendulum1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"]) + fmiUnload(fmu) -comp = fmiInstantiate!(myFMU; loggingOn=false) -@test comp != 0 + # case 5: ME-FMU with different (random) start state -# choose FMU or FMUComponent -fmuStruct = nothing -envFMUSTRUCT = ENV["FMUSTRUCT"] -if envFMUSTRUCT == "FMU" - fmuStruct = myFMU -elseif envFMUSTRUCT == "FMUCOMPONENT" - fmuStruct = comp -end -@assert fmuStruct != nothing "Unknown fmuStruct, environment variable `FMUSTRUCT` = `$envFMUSTRUCT`" + fmuStruct, fmu = getFMUStruct("SpringFrictionPendulum1D") -rand_x0 = rand(2) -solution = fmiSimulateME(fmuStruct, (t_start, t_stop); x0=rand_x0, abstol=abstol, solver=solver, dtmin=dtmin) -@test length(solution.states.u) > 0 -@test length(solution.states.t) > 0 + solution = fmiSimulateME(fmuStruct, (t_start, t_stop); x0=rand_x0, solver=solver, kwargs...) + @test length(solution.states.u) > 0 + @test length(solution.states.t) > 0 -@test solution.states.t[1] == t_start -@test solution.states.t[end] == t_stop + @test solution.states.t[1] == t_start + @test solution.states.t[end] == t_stop -@test solution.states.u[1] == rand_x0 -fmiUnload(myFMU) \ No newline at end of file + @test solution.states.u[1] == rand_x0 + fmiUnload(fmu) +end \ No newline at end of file diff --git a/test/FMI2/sim_auto.jl b/test/FMI2/sim_auto.jl index 725ab693..62dba71f 100644 --- a/test/FMI2/sim_auto.jl +++ b/test/FMI2/sim_auto.jl @@ -6,30 +6,17 @@ pathToFMU = get_model_filename("SpringPendulum1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"]) # load FMU in temporary directory -myFMU = fmiLoad(pathToFMU) +fmuStruct, myFMU = getFMUStruct(pathToFMU) @test isfile(myFMU.zipPath) == true @test isdir(splitext(myFMU.zipPath)[1]) == true fmiUnload(myFMU) # load FMU in source directory fmuDir = joinpath(splitpath(pathToFMU)[1:end-1]...) -myFMU = fmiLoad(pathToFMU; unpackPath=fmuDir) +fmuStruct, myFMU = getFMUStruct(pathToFMU; unpackPath=fmuDir) @test isfile(splitext(pathToFMU)[1] * ".zip") == true @test isdir(splitext(pathToFMU)[1]) == true -comp = fmiInstantiate!(myFMU; loggingOn=false) -@test comp != 0 - -# choose FMU or FMUComponent -fmuStruct = nothing -envFMUSTRUCT = ENV["FMUSTRUCT"] -if envFMUSTRUCT == "FMU" - fmuStruct = myFMU -elseif envFMUSTRUCT == "FMUCOMPONENT" - fmuStruct = comp -end -@assert fmuStruct != nothing "Unknown fmuStruct, environment variable `FMUSTRUCT` = `$envFMUSTRUCT`" - t_start = 0.0 t_stop = 8.0 dt = 1e-2 diff --git a/test/FMI2/sim_zero_state.jl b/test/FMI2/sim_zero_state.jl index 1efb9c18..9e135fb3 100644 --- a/test/FMI2/sim_zero_state.jl +++ b/test/FMI2/sim_zero_state.jl @@ -10,11 +10,11 @@ t_stop = 8.0 solver=FBDF(autodiff=false) dtmax = 0.01 -function extForce_t!(t, u) +extForce_t! = function(t, u) u[1] = sin(t) end -myFMU = fmiLoad("SpringPendulumExtForce1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"]) +fmuStruct, myFMU = getFMUStruct("SpringPendulumExtForce1D") # make a dummy zero-state FMU by overwriting the state field (ToDo: Use an actual zero state FMU from FMIZoo.jl) myFMU.modelDescription.stateValueReferences = [] diff --git a/test/FMI2/state.jl b/test/FMI2/state.jl index c701c176..4638f331 100644 --- a/test/FMI2/state.jl +++ b/test/FMI2/state.jl @@ -9,7 +9,7 @@ using FMI.FMIImport # Prepare FMU # ############### -myFMU = fmiLoad("SpringPendulum1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"]) +fmuStruct, myFMU = getFMUStruct("SpringPendulum1D") comp = fmi2Instantiate!(myFMU; loggingOn=true) @test comp != 0 diff --git a/test/Project.toml b/test/Project.toml index 67a7f363..b35dce57 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -3,6 +3,7 @@ BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" DifferentialEquations = "0c46a032-eb83-5123-abaf-570d42b7fbaa" +FMISensitivity = "3e748fe5-cd7f-4615-8419-3159287187d2" FMIZoo = "724179cf-c260-40a9-bd27-cccc6fe2f195" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819" @@ -10,6 +11,7 @@ Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" MAT = "23992714-dd62-5051-b70f-ba57cb901cac" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +Sundials = "c3572dad-4567-51f8-b174-8c6c989267f4" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" diff --git a/test/runtests.jl b/test/runtests.jl index e89aae51..2b364ddc 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -10,6 +10,7 @@ import Random import FMI.FMIImport.FMICore: fmi2StatusOK, fmi3StatusOK, fmi2ComponentStateTerminated, fmi2ComponentStateInstantiated, fmi3Boolean import FMI.FMIImport.FMICore: FMU2_EXECUTION_CONFIGURATION_NO_FREEING, FMU2_EXECUTION_CONFIGURATION_NO_RESET, FMU2_EXECUTION_CONFIGURATION_RESET, FMU2_EXECUTION_CONFIGURATION_NOTHING +using FMI.FMIImport using DifferentialEquations: FBDF @@ -23,6 +24,30 @@ for exec in [FMU2_EXECUTION_CONFIGURATION_NO_FREEING, FMU2_EXECUTION_CONFIGURATI exec.assertOnWarning = true end +function getFMUStruct(modelname, tool=ENV["EXPORTINGTOOL"], version=ENV["EXPORTINGVERSION"]; kwargs...) + + # choose FMU or FMUComponent + if endswith(modelname, ".fmu") + fmu = fmiLoad(modelname; kwargs...) + else + fmu = fmiLoad(modelname, tool, version; kwargs...) + end + + envFMUSTRUCT = ENV["FMUSTRUCT"] + + if envFMUSTRUCT == "FMU" + return fmu, fmu + + elseif envFMUSTRUCT == "FMUCOMPONENT" + comp = fmiInstantiate!(fmu; loggingOn=false) + @test comp != 0 + return comp, fmu + + else + @assert false "Unknown fmuStruct, environment variable `FMUSTRUCT` = `$envFMUSTRUCT`" + end +end + function runtestsFMI2(exportingTool) ENV["EXPORTINGTOOL"] = exportingTool[1] ENV["EXPORTINGVERSION"] = exportingTool[2] @@ -80,14 +105,14 @@ function runtestsFMI2(exportingTool) end end - if VERSION >= v"1.9.0" - @info "Performance (performance.jl)" - @testset "Performance" begin - include("FMI2/performance.jl") - end - else + # if VERSION >= v"1.9.0" + # @info "Performance (performance.jl)" + # @testset "Performance" begin + # include("FMI2/performance.jl") + # end + # else @info "Julia Version $(VERSION), skipping performance tests ..." - end + #end @info "Plotting (plots.jl)" @testset "Plotting" begin @@ -133,9 +158,9 @@ function runtestsFMI3(exportingTool) end end - # @testset "Plotting (plots.jl)" begin - # include("FMI3/plots.jl") - # end + @testset "Plotting (plots.jl)" begin + include("FMI3/plots.jl") + end end end @@ -144,13 +169,13 @@ end @info "Automated testing is supported on Windows." for exportingTool in exportingToolsWindows runtestsFMI2(exportingTool) - runtestsFMI3(exportingTool) + #runtestsFMI3(exportingTool) end elseif Sys.islinux() @info "Automated testing is supported on Linux." for exportingTool in exportingToolsLinux runtestsFMI2(exportingTool) - runtestsFMI3(exportingTool) + #runtestsFMI3(exportingTool) end elseif Sys.isapple() @warn "Test-sets are currrently using Windows- and Linux-FMUs, automated testing for macOS is currently not supported."