Skip to content

Commit

Permalink
v0.16.0 (#98)
Browse files Browse the repository at this point in the history
* Performance improvements (#97)

* PrepareValueReference multiple dispatch

* Reduce allocations

* Unsense-related allocations

* Minor change

* Optimise Jacobian invalidation (requires update in FMICore)

* Type stability

* Keyword and non-keyword definitions

* Avoid allocations

* Avoid c.x pointing to external array

* further performance optimizations

* performance improvements

* ready for fmissensitivity.jl

---------

Co-authored-by: CasBex <[email protected]>
  • Loading branch information
ThummeTo and CasBex authored Oct 9, 2023
1 parent 7f75ca1 commit 35112f1
Show file tree
Hide file tree
Showing 12 changed files with 370 additions and 1,526 deletions.
10 changes: 2 additions & 8 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,23 +1,17 @@
name = "FMIImport"
uuid = "9fcbc62e-52a0-44e9-a616-1359a0008194"
authors = ["TT <[email protected]>", "LM <[email protected]>", "JK <[email protected]>"]
version = "0.15.8"
version = "0.16.0"

[deps]
ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
Downloads = "f43a241f-c20a-4ad4-852c-f6b1247861c6"
EzXML = "8f5d6c58-4d21-5cfd-889c-e3ad7ee6a615"
FMICore = "8af89139-c281-408e-bce2-3005eb87462f"
ForwardDiffChainRules = "c9556dd2-1aed-4cfe-8560-1557cf593001"
Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
SciMLSensitivity = "1ed8b502-d754-442c-8d5d-10ac956f44a1"
ZipFile = "a5390f91-8eb1-5f08-bee0-b1d1ffed6cea"

[compat]
ChainRulesCore = "1.16"
EzXML = "1.1.0"
FMICore = "0.17.3"
ForwardDiffChainRules = "0.1.1"
SciMLSensitivity = "7.35, 7.36"
FMICore = "0.18.0"
ZipFile = "0.10.0"
julia = "1.6"
69 changes: 43 additions & 26 deletions src/FMI2/c.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ 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
Expand Down Expand Up @@ -234,9 +236,10 @@ end

# helper
function checkStatus(c::FMU2Component, status::fmi2Status)
@assert (status != fmi2StatusWarning) || !c.fmu.executionConfig.assertOnWarning "Assert on `fmi2StatusWarning`. See stack for errors."
if status == fmi2StatusWarning
@assert !c.fmu.executionConfig.assertOnWarning "Assert on `fmi2StatusWarning`. See stack for errors."

if status == fmi2StatusError
elseif status == fmi2StatusError
c.state = fmi2ComponentStateError
@assert !c.fmu.executionConfig.assertOnError "Assert on `fmi2StatusError`. See stack for errors."

Expand Down Expand Up @@ -561,6 +564,7 @@ function fmi2GetReal!(c::FMU2Component, vr::AbstractArray{fmi2ValueReference}, n
return status
end


"""
fmi2SetReal(c::FMU2Component, vr::AbstractArray{fmi2ValueReference}, nvr::Csize_t, value::AbstractArray{fmi2Real})
Expand Down Expand Up @@ -597,14 +601,23 @@ function fmi2SetReal(c::FMU2Component,
c.compAddr, vr, nvr, value)
checkStatus(c, status)

if track
if status == fmi2StatusOK
for j in (c.A, c.B, c.C, c.D, c.E, c.F)
if any(collect(v in j.∂f_refs for v in vr))
FMICore.invalidate!(j)
end
end
end
if track && status == fmi2StatusOK
check_invalidate!(vr, c.∂ẋ_∂x)
check_invalidate!(vr, c.∂ẋ_∂u)
check_invalidate!(vr, c.∂ẋ_∂p)

check_invalidate!(vr, c.∂y_∂x)
check_invalidate!(vr, c.∂y_∂u)
check_invalidate!(vr, c.∂y_∂p)

check_invalidate!(vr, c.∂e_∂x)
check_invalidate!(vr, c.∂e_∂u)
check_invalidate!(vr, c.∂e_∂p)

# [NOTE] No need to check for:
# check_invalidate!(vr, c.∂ẋ_∂t)
# check_invalidate!(vr, c.∂y_∂t)
# check_invalidate!(vr, c.∂e_∂t)
end

return status
Expand Down Expand Up @@ -650,6 +663,7 @@ function fmi2GetInteger!(c::FMU2Component, vr::AbstractArray{fmi2ValueReference}
return status
end


"""
fmi2SetInteger(c::FMU2Component, vr::AbstractArray{fmi2ValueReference}, nvr::Csize_t, value::AbstractArray{fmi2Integer})
Expand Down Expand Up @@ -687,6 +701,7 @@ function fmi2SetInteger(c::FMU2Component, vr::AbstractArray{fmi2ValueReference},
return status
end


"""
fmi2GetBoolean!(c::FMU2Component, vr::AbstractArray{fmi2ValueReference}, nvr::Csize_t, value::AbstractArray{fmi2Boolean})
Expand Down Expand Up @@ -726,6 +741,7 @@ function fmi2GetBoolean!(c::FMU2Component, vr::AbstractArray{fmi2ValueReference}
return status
end


"""
fmi2SetBoolean(c::FMU2Component, vr::AbstractArray{fmi2ValueReference}, nvr::Csize_t, value::AbstractArray{fmi2Boolean})
Expand Down Expand Up @@ -761,6 +777,7 @@ function fmi2SetBoolean(c::FMU2Component, vr::AbstractArray{fmi2ValueReference},
return status
end


"""
fmi2GetString!(c::FMU2Component, vr::AbstractArray{fmi2ValueReference}, nvr::Csize_t, value::Union{AbstractArray{Ptr{Cchar}}, AbstractArray{Ptr{UInt8}}})
Expand Down Expand Up @@ -800,6 +817,7 @@ function fmi2GetString!(c::FMU2Component, vr::AbstractArray{fmi2ValueReference},
return status
end


"""
fmi2SetString(c::FMU2Component, vr::AbstractArray{fmi2ValueReference}, nvr::Csize_t, value::Union{AbstractArray{Ptr{Cchar}}, AbstractArray{Ptr{UInt8}}})
Expand Down Expand Up @@ -837,6 +855,7 @@ function fmi2SetString(c::FMU2Component, vr::AbstractArray{fmi2ValueReference},
return status
end


"""
fmi2GetFMUstate!(c::FMU2Component, FMUstate::Ref{fmi2FMUstate})
Expand Down Expand Up @@ -1015,6 +1034,7 @@ function fmi2SerializeFMUstate!(c::FMU2Component, FMUstate::fmi2FMUstate, serial
return status
end


"""
fmi2DeSerializeFMUstate!(c::FMU2Component, serializedState::AbstractArray{fmi2Byte}, size::Csize_t, FMUstate::Ref{fmi2FMUstate})
Expand Down Expand Up @@ -1054,6 +1074,7 @@ function fmi2DeSerializeFMUstate!(c::FMU2Component, serializedState::AbstractArr
return status
end


"""
fmi2GetDirectionalDerivative!(c::FMU2Component,
vUnknown_ref::AbstractArray{fmi2ValueReference},
Expand Down Expand Up @@ -1156,14 +1177,15 @@ More detailed:
See also [`fmi2SetRealInputDerivatives`](@ref).
"""
function fmi2SetRealInputDerivatives(c::FMU2Component, vr::AbstractArray{fmi2ValueReference}, nvr::Csize_t, order::AbstractArray{fmi2Integer}, value::AbstractArray{fmi2Real})
function fmi2SetRealInputDerivatives(c::FMU2Component, vr::Array{fmi2ValueReference}, nvr::Csize_t, order::Array{fmi2Integer}, value::Array{fmi2Real})

status = fmi2SetRealInputDerivatives(c.fmu.cSetRealInputDerivatives,
c.compAddr, vr, nvr, order, value)
checkStatus(c, status)
return status
end


"""
fmi2GetRealOutputDerivatives!(c::FMU2Component,
vr::AbstractArray{fmi2ValueReference},
Expand Down Expand Up @@ -1195,14 +1217,15 @@ More detailed:
- FMISpec2.0.2[p.18]: 2.1.3 Status Returned by Functions
- FMISpec2.0.2[p.104]: 4.2.1 Transfer of Input / Output Values and Parameters
"""
function fmi2GetRealOutputDerivatives!(c::FMU2Component, vr::AbstractArray{fmi2ValueReference}, nvr::Csize_t, order::AbstractArray{fmi2Integer}, value::AbstractArray{fmi2Real})
function fmi2GetRealOutputDerivatives!(c::FMU2Component, vr::Array{fmi2ValueReference}, nvr::Csize_t, order::Array{fmi2Integer}, value::Array{fmi2Real})

status = fmi2GetRealOutputDerivatives!(c.fmu.cGetRealOutputDerivatives,
c.compAddr, vr, nvr, order, value)
checkStatus(c, status)
return status
end


"""
fmi2DoStep(c::FMU2Component,
currentCommunicationPoint::fmi2Real,
Expand Down Expand Up @@ -1491,7 +1514,6 @@ function fmi2GetStringStatus!(c::FMU2Component, s::fmi2StatusKind, value::fmi2St
end

# Model Exchange specific Functions
#TOD0
"""
fmi2SetTime(c::FMU2Component,
time::fmi2Real;
Expand Down Expand Up @@ -1558,19 +1580,15 @@ function fmi2SetTime(c::FMU2Component, time::fmi2Real; soft::Bool=false, track::
if status == fmi2StatusOK
c.t = time

FMICore.invalidate!(c.A)
FMICore.invalidate!(c.B)
FMICore.invalidate!(c.C)
FMICore.invalidate!(c.D)
FMICore.invalidate!(c.E)
FMICore.invalidate!(c.F)
invalidate!(c.∂ẋ_∂t)
invalidate!(c.∂y_∂t)
invalidate!(c.∂e_∂t)
end
end

return status
end

#TODO
"""
fmi2SetContinuousStates(c::FMU2Component,
x::AbstractArray{fmi2Real},
Expand Down Expand Up @@ -1618,13 +1636,11 @@ function fmi2SetContinuousStates(c::FMU2Component,

if track
if status == fmi2StatusOK
c.x = copy(x)
isnothing(c.x) ? (c.x = copy(x);) : copyto!(c.x, x)

FMICore.invalidate!(c.A)
FMICore.invalidate!(c.C)

FMICore.invalidate!(c.E)
FMICore.invalidate!(c.F)
invalidate!(c.∂ẋ_∂x)
invalidate!(c.∂y_∂x)
invalidate!(c.∂e_∂x)
end
end

Expand Down Expand Up @@ -1857,6 +1873,7 @@ function fmi2GetDerivatives!(c::FMU2Component,
status = fmi2GetDerivatives!(c.fmu.cGetDerivatives,
c.compAddr, derivatives, nx)
checkStatus(c, status)

return status
end

Expand Down
79 changes: 33 additions & 46 deletions src/FMI2/convert.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,46 +3,32 @@
# Licensed under the MIT license. See LICENSE file in the project root for details.
#

using ChainRulesCore: ignore_derivatives
import SciMLSensitivity.ForwardDiff
# ToDo: Fix this: import SciMLSensitivity.ForwardDiff

# ToDo: Replace by multiple dispatch version ...
# Receives one or an array of value references in an arbitrary format (see fmi2ValueReferenceFormat) and converts it into an Array{fmi2ValueReference} (if not already).
function prepareValueReference(md::fmi2ModelDescription, vr::fmi2ValueReferenceFormat)
tvr = typeof(vr)
if isa(vr, AbstractArray{fmi2ValueReference,1})
return vr
elseif tvr == fmi2ValueReference
return [vr]
elseif tvr == String
return [fmi2StringToValueReference(md, vr)]
elseif isa(vr, AbstractArray{String,1})
return fmi2StringToValueReference(md, vr)
elseif tvr == Int64
return [fmi2ValueReference(vr)]
elseif isa(vr, AbstractArray{Int64,1})
return fmi2ValueReference.(vr)
elseif tvr == Nothing
prepareValueReference(md::fmi2ModelDescription, vr::AbstractVector{fmi2ValueReference}) = vr
prepareValueReference(md::fmi2ModelDescription, vr::fmi2ValueReference) = [vr]
prepareValueReference(md::fmi2ModelDescription, vr::String) = [fmi2StringToValueReference(md, vr)]
prepareValueReference(md::fmi2ModelDescription, vr::AbstractVector{String}) = fmi2StringToValueReference(md, vr)
prepareValueReference(md::fmi2ModelDescription, vr::AbstractVector{<:Integer}) = fmi2ValueReference.(vr)
prepareValueReference(md::fmi2ModelDescription, vr::Integer) = [fmi2ValueReference(vr)]
prepareValueReference(md::fmi2ModelDescription, vr::Nothing) = fmi2ValueReference[]
function prepareValueReference(md::fmi2ModelDescription, vr::Symbol)
if vr == :states
return md.stateValueReferences
elseif vr == :derivatives
return md.derivativeValueReferences
elseif vr == :inputs
return md.inputValueReferences
elseif vr == :outputs
return md.outputValueReferences
elseif vr == :all
return md.valueReferences
elseif vr == :none
return Array{fmi2ValueReference,1}()
elseif tvr == Symbol
if vr == :states
return md.stateValueReferences
elseif vr == :derivatives
return md.derivativeValueReferences
elseif vr == :inputs
return md.inputValueReferences
elseif vr == :outputs
return md.outputValueReferences
elseif vr == :all
return md.valueReferences
elseif vr == :none
return Array{fmi2ValueReference,1}()
else
@assert false "Unknwon symbol `$vr`, can't convert to value reference."
end
else
@assert false "Unknwon symbol `$vr`, can't convert to value reference."
end

@assert false "prepareValueReference(...): Unknown value reference structure `$tvr`."
end
function prepareValueReference(fmu::FMU2, vr::fmi2ValueReferenceFormat)
prepareValueReference(fmu.modelDescription, vr)
Expand All @@ -69,16 +55,17 @@ Returns an array of ValueReferences coresponding to the variable names.
See also [`fmi2StringToValueReference`](@ref).
"""
function fmi2StringToValueReference(md::fmi2ModelDescription, names::AbstractArray{String})
vr = Array{fmi2ValueReference}(undef,0)
for name in names
reference = fmi2StringToValueReference(md, name)
if reference == nothing
@warn "Value reference for variable '$name' not found, skipping."
else
push!(vr, reference)
end
end
vr
# vr = Array{fmi2ValueReference}(undef,0)
# for name in names
# reference = fmi2StringToValueReference(md, name)
# if reference == nothing
# @warn "Value reference for variable '$name' not found, skipping."
# else
# push!(vr, reference)
# end
# end
# vr
return broadcast(fmi2StringToValueReference, (md,), names)
end

"""
Expand Down
Loading

2 comments on commit 35112f1

@ThummeTo
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/93080

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.16.0 -m "<description of version>" 35112f1924961881d92757094548bab6c7b590d6
git push origin v0.16.0

Please sign in to comment.