diff --git a/Project.toml b/Project.toml index fce8751..38a83e3 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" @@ -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 20bb5b6..474ab61 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 @@ -116,12 +114,17 @@ 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 compAddr = c.compAddr + # invalidate all active snapshots + 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 @@ -138,7 +141,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 @@ -1144,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 f0116db..ebcfbfe 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,25 @@ 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." - + if secure_pointers unloadBinary(fmu) end @@ -1171,7 +1181,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 @@ -1585,3 +1595,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/FMI2/prep.jl b/src/FMI2/prep.jl index 22cdeaf..5a4a1de 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 @@ -354,7 +371,7 @@ function finishSolveFMU(fmu::FMU2, c::FMU2Component, freeInstance::Union{Nothing # freeInstance (hard) if freeInstance - fmi2FreeInstance!(c; popComponent=popComponent) + fmi2FreeInstance!(c; popComponent=popComponent) # , doccall=freeInstance c = nothing end end diff --git a/src/FMIImport.jl b/src/FMIImport.jl index ec1018c..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 @@ -54,6 +55,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