From b183946081d586dcc2e16c26826f71827012f467 Mon Sep 17 00:00:00 2001 From: TT Date: Tue, 5 Dec 2023 10:31:37 +0100 Subject: [PATCH 1/6] performance improvements --- Project.toml | 2 +- src/FMI2/c.jl | 6 ++++-- src/FMI2/prep.jl | 6 ++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Project.toml b/Project.toml index fce8751..b31c0c7 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "FMIImport" uuid = "9fcbc62e-52a0-44e9-a616-1359a0008194" authors = ["TT ", "LM ", "JK "] -version = "0.16.3" +version = "0.16.4" [deps] Downloads = "f43a241f-c20a-4ad4-852c-f6b1247861c6" diff --git a/src/FMI2/c.jl b/src/FMI2/c.jl index 20bb5b6..9d6450c 100644 --- a/src/FMI2/c.jl +++ b/src/FMI2/c.jl @@ -116,7 +116,7 @@ Removes the component from the FMUs component list. See Also [`fmi2FreeInstance!`](@ref). """ lk_fmi2FreeInstance = ReentrantLock() -function fmi2FreeInstance!(c::FMU2Component; popComponent::Bool = true) +function fmi2FreeInstance!(c::FMU2Component; popComponent::Bool=true, doccall::Bool=true) global lk_fmi2FreeInstance @@ -138,7 +138,9 @@ function fmi2FreeInstance!(c::FMU2Component; popComponent::Bool = true) end end - fmi2FreeInstance!(c.fmu.cFreeInstance, compAddr) + if doccall + fmi2FreeInstance!(c.fmu.cFreeInstance, compAddr) + end nothing end diff --git a/src/FMI2/prep.jl b/src/FMI2/prep.jl index 22cdeaf..f246dad 100644 --- a/src/FMI2/prep.jl +++ b/src/FMI2/prep.jl @@ -353,10 +353,8 @@ function finishSolveFMU(fmu::FMU2, c::FMU2Component, freeInstance::Union{Nothing end # freeInstance (hard) - if freeInstance - fmi2FreeInstance!(c; popComponent=popComponent) - c = nothing - end + fmi2FreeInstance!(c; popComponent=popComponent, doccall=freeInstance) + c = nothing end return c From 99bb49c0019d74e19d49065a91f261c33250d733 Mon Sep 17 00:00:00 2001 From: TT Date: Mon, 11 Dec 2023 16:20:43 +0100 Subject: [PATCH 2/6] optimizations --- src/FMI2/ext.jl | 61 ++++++++++++++++++++++++++++++++++++++++++++++++ src/FMIImport.jl | 1 + 2 files changed, 62 insertions(+) diff --git a/src/FMI2/ext.jl b/src/FMI2/ext.jl index f0116db..476cb5a 100644 --- a/src/FMI2/ext.jl +++ b/src/FMI2/ext.jl @@ -1585,3 +1585,64 @@ function fmi2SampleJacobian!(c::FMU2Component, nothing end + +""" + fmi2SetDiscreteStates(c::FMU2Component, + x::Union{AbstractArray{Float32},AbstractArray{Float64}}) + +Set a new (discrete) state vector and reinitialize chaching of variables that depend on states. + +# Arguments +[ToDo] +""" +function fmi2SetDiscreteStates(c::FMU2Component, xd::AbstractArray{Union{fmi2Real, fmi2Integer, fmi2Boolean}}) + + if length(c.fmu.modelDescription.discreteStateValueReferences) <= 0 + return fmi2StatusOK + end + + status = fmi2Set(c, c.fmu.modelDescription.discreteStateValueReferences, xd) + if status == fmi2StatusOK + fast_copy!(c, :x_d, xd) + end + return status +end + +""" + fmi2GetDiscreteStates!(c::FMU2Component, + x::Union{AbstractArray{Float32},AbstractArray{Float64}}) + +Set a new (discrete) state vector and reinitialize chaching of variables that depend on states. + +# Arguments +[ToDo] +""" +function fmi2GetDiscreteStates!(c::FMU2Component, xd::AbstractArray{Union{fmi2Real, fmi2Integer, fmi2Boolean}}) + + if length(c.fmu.modelDescription.discreteStateValueReferences) <= 0 + return fmi2StatusOK + end + + status = fmi2Get!(c, c.fmu.modelDescription.discreteStateValueReferences, xd) + if status == fmi2StatusOK + fast_copy!(c, :x_d, xd) + end + return status +end + +""" + fmi2GetDiscreteStates(c::FMU2Component, + x::Union{AbstractArray{Float32},AbstractArray{Float64}}) + +Set a new (discrete) state vector and reinitialize chaching of variables that depend on states. + +# Arguments +[ToDo] +""" +function fmi2GetDiscreteStates(c::FMU2Component) + + ndx = length(c.fmu.modelDescription.discreteStateValueReferences) + xd = Vector{Union{fmi2Real, fmi2Integer, fmi2Boolean}}() + fmi2GetDiscreteStates!(c, xd) + return xd +end \ No newline at end of file diff --git a/src/FMIImport.jl b/src/FMIImport.jl index ec1018c..ca3cf25 100644 --- a/src/FMIImport.jl +++ b/src/FMIImport.jl @@ -54,6 +54,7 @@ export fmi2GetFMUstate!, fmi2SetFMUstate, fmi2FreeFMUstate!, fmi2SerializedFMUst export fmi2GetDirectionalDerivative!, fmi2SetRealInputDerivatives, fmi2GetRealOutputDerivatives export fmi2DoStep, fmi2CancelStep, fmi2GetStatus!, fmi2GetRealStatus!, fmi2GetIntegerStatus!, fmi2GetBooleanStatus!, fmi2GetStringStatus! export fmi2SetTime, fmi2SetContinuousStates, fmi2EnterEventMode, fmi2NewDiscreteStates!, fmi2EnterContinuousTimeMode, fmi2CompletedIntegratorStep! +export fmi2SetDiscreteStates, fmi2GetDiscreteStates!, fmi2GetDiscreteStates export fmi2GetDerivatives!, fmi2GetEventIndicators!, fmi2GetContinuousStates!, fmi2GetNominalsOfContinuousStates!, fmi2GetRealOutputDerivatives! # FMI2_convert.jl From c0f44156e801cb52ecf2625e147d80955a1b49d2 Mon Sep 17 00:00:00 2001 From: TT Date: Tue, 9 Jan 2024 10:26:32 +0100 Subject: [PATCH 3/6] WIPY --- Project.toml | 2 +- src/FMI2/c.jl | 24 ++++++++++++++++++++++-- src/FMI2/ext.jl | 21 +++++++++++++++++---- src/FMI2/prep.jl | 17 +++++++++++++++++ src/FMIImport.jl | 1 + 5 files changed, 58 insertions(+), 7 deletions(-) diff --git a/Project.toml b/Project.toml index b31c0c7..38a83e3 100644 --- a/Project.toml +++ b/Project.toml @@ -14,7 +14,7 @@ ZipFile = "a5390f91-8eb1-5f08-bee0-b1d1ffed6cea" [compat] Downloads = "1" EzXML = "1.1.0" -FMICore = "0.19.0" +FMICore = "0.20.0" Libdl = "1" RelocatableFolders = "1" ZipFile = "0.10.0" diff --git a/src/FMI2/c.jl b/src/FMI2/c.jl index 9d6450c..17c36dd 100644 --- a/src/FMI2/c.jl +++ b/src/FMI2/c.jl @@ -21,8 +21,6 @@ import FMICore: fmi2DoStep, fmi2CancelStep, fmi2GetStatus!, fmi2GetRealStatus!, import FMICore: fmi2SetTime, fmi2SetContinuousStates, fmi2EnterEventMode, fmi2NewDiscreteStates!, fmi2EnterContinuousTimeMode, fmi2CompletedIntegratorStep! import FMICore: fmi2GetDerivatives!, fmi2GetEventIndicators!, fmi2GetContinuousStates!, fmi2GetNominalsOfContinuousStates! -using FMICore: invalidate!, check_invalidate! - """ Source: FMISpec2.0.2[p.21]: 2.1.5 Creation, Destruction and Logging of FMU Instances @@ -124,6 +122,11 @@ function fmi2FreeInstance!(c::FMU2Component; popComponent::Bool=true, doccall::B @assert c.threadid == Threads.threadid() "Thread #$(Threads.threadid()) tried to free component with address $(c.compAddr), but doesn't own it.\nThe component is owned by thread $(c.threadid)" + # invalidate all active snapshots + # for snapshot in c.snapshots + # cleanup!(snapshot) + # end + if popComponent lock(lk_fmi2FreeInstance) do ind = findall(x -> x.compAddr == compAddr, c.fmu.components) @@ -1146,6 +1149,23 @@ function fmi2GetDirectionalDerivative!(c::FMU2Component, return status end +# for AD primitives +function fmi2GetDirectionalDerivative!(c::FMU2Component, + vUnknown_ref::AbstractArray{fmi2ValueReference}, + nUnknown::Csize_t, + vKnown_ref::AbstractArray{fmi2ValueReference}, + nKnown::Csize_t, + dvKnown::AbstractArray{fmi2Real}, + dvUnknown::AbstractArray{<:Real}) + + logWarning(c.fmu, "fmi2GetDirectionalDerivative! is called on `dvUnknown::AbstractArray{<:Real}`, this is slow.\nConsider using `Float64` instead.", 1) + + _dvUnknown = zeros(fmi2Real, length(dvUnknown)) + status = fmi2GetDirectionalDerivative!(c::FMU2Component, vUnknown_ref, nUnknown, vKnown_ref, nKnown, dvKnown, _dvUnknown) + dvUnknown[:] = _dvUnknown + return status +end + # Functions specificly for isCoSimulation """ fmi2SetRealInputDerivatives(c::FMU2Component, diff --git a/src/FMI2/ext.jl b/src/FMI2/ext.jl index 476cb5a..362aacf 100644 --- a/src/FMI2/ext.jl +++ b/src/FMI2/ext.jl @@ -586,7 +586,7 @@ function fmi2Reload(fmu::FMU2) end """ - fmi2Unload(fmu::FMU2, cleanUp::Bool = true) + fmi2Unload(fmu::FMU2, cleanUp::Bool=true; secure_pointers::Bool=true) Unload a FMU. Free the allocated memory, close the binaries and remove temporary zip and unziped FMU model description. @@ -598,15 +598,28 @@ Free the allocated memory, close the binaries and remove temporary zip and unzip # Keywords - `secure_pointers=true` whether pointers to C-functions should be overwritten with dummies with Julia assertions, instead of pointing to dead memory (slower, but more user safe) """ -function fmi2Unload(fmu::FMU2, cleanUp::Bool = true; secure_pointers::Bool=true) +function fmi2Unload(fmu::FMU2, cleanUp::Bool=true; secure_pointers::Bool=true) while length(fmu.components) > 0 - fmi2FreeInstance!(fmu.components[end]) + c = fmu.components[end] + + # release allocated memory for snapshots (they might be used elsewhere too) + if !isnothing(c.solution) + for iter in c.solution.snapshots + t, snapshot = iter + cleanup!(c, snapshot) + end + end + + fmi2FreeInstance!(c) end # the components are removed from the component list via call to fmi2FreeInstance! @assert length(fmu.components) == 0 "fmi2Unload(...): Failure during deleting components, $(length(fmu.components)) remaining in stack." + # release auto finalized snapshots + GC.gc() + if secure_pointers unloadBinary(fmu) end @@ -1171,7 +1184,7 @@ More detailed: function fmi2Set(comp::FMU2Component, vrs::fmi2ValueReferenceFormat, srcArray::AbstractArray; filter=nothing) vrs = prepareValueReference(comp, vrs) - @assert length(vrs) == length(srcArray) "fmi2Set(...): Number of value references doesn't match number of `srcArray` elements." + @assert length(vrs) == length(srcArray) "fmi2Set(...): Number of value references [$(length(vrs))] doesn't match number of `srcArray` elements [$(length(srcArray))]." retcodes = zeros(fmi2Status, length(vrs)) # fmi2StatusOK diff --git a/src/FMI2/prep.jl b/src/FMI2/prep.jl index f246dad..ea42ee8 100644 --- a/src/FMI2/prep.jl +++ b/src/FMI2/prep.jl @@ -140,6 +140,9 @@ function prepareSolveFMU(fmu::FMU2, #@assert retcode == fmi2StatusOK "fmi2Simulate(...): Setting initial state failed with return code $(retcode)." retcodes = fmi2Set(c, fmu.modelDescription.stateValueReferences, x0; filter=setInInitialization) @assert all(retcodes .== fmi2StatusOK) "fmi2Simulate(...): Setting initial inputs failed with return code $(retcodes)." + + # safe start state in component + c.x = copy(x0) end # exit setup (hard) @@ -173,6 +176,20 @@ function handleEvents(c::FMU2Component) @assert c.state == fmi2ComponentStateEventMode "handleEvents(...): Must be in event mode!" + # invalidate all cached jacobians/gradients + invalidate!(c.∂ẋ_∂x) + invalidate!(c.∂ẋ_∂u) + invalidate!(c.∂ẋ_∂p) + invalidate!(c.∂y_∂x) + invalidate!(c.∂y_∂u) + invalidate!(c.∂y_∂p) + invalidate!(c.∂e_∂x) + invalidate!(c.∂e_∂u) + invalidate!(c.∂e_∂p) + invalidate!(c.∂ẋ_∂t) + invalidate!(c.∂y_∂t) + invalidate!(c.∂e_∂t) + #@debug "Handle Events..." # trigger the loop diff --git a/src/FMIImport.jl b/src/FMIImport.jl index ca3cf25..5fd60a6 100644 --- a/src/FMIImport.jl +++ b/src/FMIImport.jl @@ -13,6 +13,7 @@ using FMICore.Requires import FMICore.ChainRulesCore: ignore_derivatives using RelocatableFolders +using FMICore: invalidate!, check_invalidate! # functions that have (currently) no better place From e8aa06e708a86a69fcf73ca7151ac4cbb82df015 Mon Sep 17 00:00:00 2001 From: TT Date: Mon, 15 Jan 2024 10:32:47 +0100 Subject: [PATCH 4/6] WIP --- src/FMI2/c.jl | 6 +++--- src/FMI2/ext.jl | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/FMI2/c.jl b/src/FMI2/c.jl index 17c36dd..74844ec 100644 --- a/src/FMI2/c.jl +++ b/src/FMI2/c.jl @@ -123,9 +123,9 @@ function fmi2FreeInstance!(c::FMU2Component; popComponent::Bool=true, doccall::B @assert c.threadid == Threads.threadid() "Thread #$(Threads.threadid()) tried to free component with address $(c.compAddr), but doesn't own it.\nThe component is owned by thread $(c.threadid)" # invalidate all active snapshots - # for snapshot in c.snapshots - # cleanup!(snapshot) - # end + for snapshot in c.snapshots + cleanup!(c, snapshot) + end if popComponent lock(lk_fmi2FreeInstance) do diff --git a/src/FMI2/ext.jl b/src/FMI2/ext.jl index 362aacf..4261048 100644 --- a/src/FMI2/ext.jl +++ b/src/FMI2/ext.jl @@ -604,12 +604,12 @@ function fmi2Unload(fmu::FMU2, cleanUp::Bool=true; secure_pointers::Bool=true) c = fmu.components[end] # release allocated memory for snapshots (they might be used elsewhere too) - if !isnothing(c.solution) - for iter in c.solution.snapshots - t, snapshot = iter - cleanup!(c, snapshot) - end - end + # if !isnothing(c.solution) + # for iter in c.solution.snapshots + # t, snapshot = iter + # cleanup!(c, snapshot) + # end + # end fmi2FreeInstance!(c) end @@ -618,7 +618,7 @@ function fmi2Unload(fmu::FMU2, cleanUp::Bool=true; secure_pointers::Bool=true) @assert length(fmu.components) == 0 "fmi2Unload(...): Failure during deleting components, $(length(fmu.components)) remaining in stack." # release auto finalized snapshots - GC.gc() + #GC.gc() if secure_pointers unloadBinary(fmu) From 89842392e8f1541b2b1249248e590a481e530133 Mon Sep 17 00:00:00 2001 From: TT Date: Thu, 18 Jan 2024 04:52:40 +0100 Subject: [PATCH 5/6] WIP --- src/FMI2/c.jl | 8 ++++---- src/FMI2/ext.jl | 3 --- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/FMI2/c.jl b/src/FMI2/c.jl index 74844ec..474ab61 100644 --- a/src/FMI2/c.jl +++ b/src/FMI2/c.jl @@ -120,13 +120,13 @@ function fmi2FreeInstance!(c::FMU2Component; popComponent::Bool=true, doccall::B compAddr = c.compAddr - @assert c.threadid == Threads.threadid() "Thread #$(Threads.threadid()) tried to free component with address $(c.compAddr), but doesn't own it.\nThe component is owned by thread $(c.threadid)" - # invalidate all active snapshots - for snapshot in c.snapshots - cleanup!(c, snapshot) + while length(c.snapshots) > 0 + FMICore.freeSnapshot!(c.snapshots[end]) end + @assert c.threadid == Threads.threadid() "Thread #$(Threads.threadid()) tried to free component with address $(c.compAddr), but doesn't own it.\nThe component is owned by thread $(c.threadid)" + if popComponent lock(lk_fmi2FreeInstance) do ind = findall(x -> x.compAddr == compAddr, c.fmu.components) diff --git a/src/FMI2/ext.jl b/src/FMI2/ext.jl index 4261048..ebcfbfe 100644 --- a/src/FMI2/ext.jl +++ b/src/FMI2/ext.jl @@ -616,9 +616,6 @@ function fmi2Unload(fmu::FMU2, cleanUp::Bool=true; secure_pointers::Bool=true) # the components are removed from the component list via call to fmi2FreeInstance! @assert length(fmu.components) == 0 "fmi2Unload(...): Failure during deleting components, $(length(fmu.components)) remaining in stack." - - # release auto finalized snapshots - #GC.gc() if secure_pointers unloadBinary(fmu) From ad20bdac40d54d63341e0584fe91368eada8caec Mon Sep 17 00:00:00 2001 From: TT Date: Sat, 3 Feb 2024 10:36:57 +0100 Subject: [PATCH 6/6] WIP --- src/FMI2/prep.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/FMI2/prep.jl b/src/FMI2/prep.jl index ea42ee8..5a4a1de 100644 --- a/src/FMI2/prep.jl +++ b/src/FMI2/prep.jl @@ -370,8 +370,10 @@ function finishSolveFMU(fmu::FMU2, c::FMU2Component, freeInstance::Union{Nothing end # freeInstance (hard) - fmi2FreeInstance!(c; popComponent=popComponent, doccall=freeInstance) - c = nothing + if freeInstance + fmi2FreeInstance!(c; popComponent=popComponent) # , doccall=freeInstance + c = nothing + end end return c