diff --git a/Project.toml b/Project.toml index 7d800a0..4c6b276 100644 --- a/Project.toml +++ b/Project.toml @@ -1,10 +1,14 @@ name = "FMICore" uuid = "8af89139-c281-408e-bce2-3005eb87462f" authors = ["TT ", "LM ", "JK "] -version = "0.17.3" +version = "0.18.0" [deps] +ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" +Requires = "ae029012-a4dd-5104-9daa-d747884805df" [compat] +ChainRulesCore = "1.16.0" +Requires = "1.3.0" julia = "1.6" diff --git a/src/FMI2/cconst.jl b/src/FMI2/cconst.jl index 53d0b03..f8d94bc 100644 --- a/src/FMI2/cconst.jl +++ b/src/FMI2/cconst.jl @@ -23,8 +23,11 @@ const fmi2ValueReference = Cuint const fmi2FMUstate = Ptr{Cvoid} const fmi2Component = Ptr{Cvoid} const fmi2ComponentEnvironment = Ptr{Cvoid} -const fmi2Enum = Array{Array{String}} # TODO: remove, this is not part of the spec! -export fmi2Char, fmi2String, fmi2Boolean, fmi2Real, fmi2Integer, fmi2Byte, fmi2ValueReference, fmi2FMUstate, fmi2Component, fmi2ComponentEnvironment, fmi2Enum +export fmi2Char, fmi2String, fmi2Boolean, fmi2Real, fmi2Integer, fmi2Byte, fmi2ValueReference, fmi2FMUstate, fmi2Component, fmi2ComponentEnvironment + +# wildcards for how a user can pass a fmi2ValueReference +fmi2ValueReferenceFormat = Union{Nothing, String, AbstractArray{String,1}, fmi2ValueReference, AbstractArray{fmi2ValueReference,1}, Int64, AbstractArray{Int64,1}, Symbol} +export fmi2ValueReferenceFormat """ Source: FMISpec2.0.2[p.18]: 2.1.3 Status Returned by Functions diff --git a/src/FMI2/cfunc.jl b/src/FMI2/cfunc.jl index 53cbf81..54670b2 100644 --- a/src/FMI2/cfunc.jl +++ b/src/FMI2/cfunc.jl @@ -8,7 +8,7 @@ Source: FMISpec2.0.2[p.19]: 2.1.5 Creation, Destruction and Logging of FMU Insta The function returns a new instance of an FMU. """ -function fmi2Instantiate(cfunc::Ptr{Nothing}, +function fmi2Instantiate(cfunc::Ptr{Cvoid}, instanceName::fmi2String, fmuType::fmi2Type, fmuGUID::fmi2String, @@ -35,7 +35,7 @@ If a null pointer is provided for “c”, the function call is ignored (does no Removes the component from the FMUs component list. """ -function fmi2FreeInstance!(cfunc::Ptr{Nothing}, c::fmi2Component) +function fmi2FreeInstance!(cfunc::Ptr{Cvoid}, c::fmi2Component) ccall(cfunc, Cvoid, (fmi2Component,), c) @debug "fmi2FreeInstance(c: $(c)) → [nothing]" @@ -49,7 +49,7 @@ Source: FMISpec2.0.2[p.22]: 2.1.4 Inquire Platform and Version Number of Header Returns the string to uniquely identify the “fmi2TypesPlatform.h” header file used for compilation of the functions of the FMU. The standard header file, as documented in this specification, has fmi2TypesPlatform set to “default” (so this function usually returns “default”). """ -function fmi2GetTypesPlatform(cfunc::Ptr{Nothing}) +function fmi2GetTypesPlatform(cfunc::Ptr{Cvoid}) str = ccall(cfunc, fmi2String, ()) @debug "fmi2GetTypesPlatform() → $(str)" return str @@ -61,7 +61,7 @@ Source: FMISpec2.0.2[p.22]: 2.1.4 Inquire Platform and Version Number of Header Returns the version of the “fmi2Functions.h” header file which was used to compile the functions of the FMU. The function returns “fmiVersion” which is defined in this header file. The standard header file as documented in this specification has version “2.0” """ -function fmi2GetVersion(cfunc::Ptr{Nothing}) +function fmi2GetVersion(cfunc::Ptr{Cvoid}) str = ccall(cfunc, fmi2String, ()) @debug "fmi2GetVersion() → $(str)" return str @@ -73,7 +73,7 @@ Source: FMISpec2.0.2[p.22]: 2.1.5 Creation, Destruction and Logging of FMU Insta The function controls debug logging that is output via the logger function callback. If loggingOn = fmi2True, debug logging is enabled, otherwise it is switched off. """ -function fmi2SetDebugLogging(cfunc::Ptr{Nothing}, c::fmi2Component, logginOn::fmi2Boolean, nCategories::Csize_t, categories::Union{Ptr{fmi2String}, AbstractArray{fmi2String}}) +function fmi2SetDebugLogging(cfunc::Ptr{Cvoid}, c::fmi2Component, logginOn::fmi2Boolean, nCategories::Csize_t, categories::Union{Ptr{fmi2String}, AbstractArray{fmi2String}}) status = ccall(cfunc, fmi2Status, (fmi2Component, fmi2Boolean, Csize_t, Ptr{fmi2String}), @@ -88,7 +88,7 @@ Source: FMISpec2.0.2[p.23]: 2.1.6 Initialization, Termination, and Resetting an Informs the FMU to setup the experiment. This function must be called after fmi2Instantiate and before fmi2EnterInitializationMode is called.The function controls debug logging that is output via the logger function callback. If loggingOn = fmi2True, debug logging is enabled, otherwise it is switched off. """ -function fmi2SetupExperiment(cfunc::Ptr{Nothing}, +function fmi2SetupExperiment(cfunc::Ptr{Cvoid}, c::fmi2Component, toleranceDefined::fmi2Boolean, tolerance::fmi2Real, @@ -110,7 +110,7 @@ Source: FMISpec2.0.2[p.23]: 2.1.6 Initialization, Termination, and Resetting an Informs the FMU to enter Initialization Mode. Before calling this function, all variables with attribute can be set with the “fmi2SetXXX” functions (the ScalarVariable attributes are defined in the Model Description File, see section 2.2.7). Setting other variables is not allowed. Furthermore, fmi2SetupExperiment must be called at least once before calling fmi2EnterInitializationMode, in order that startTime is defined. """ -function fmi2EnterInitializationMode(cfunc::Ptr{Nothing}, c::fmi2Component) +function fmi2EnterInitializationMode(cfunc::Ptr{Cvoid}, c::fmi2Component) status = ccall(cfunc, fmi2Status, (fmi2Component,), @@ -125,7 +125,7 @@ Source: FMISpec2.0.2[p.23]: 2.1.6 Initialization, Termination, and Resetting an Informs the FMU to exit Initialization Mode. """ -function fmi2ExitInitializationMode(cfunc::Ptr{Nothing}, c::fmi2Component) +function fmi2ExitInitializationMode(cfunc::Ptr{Cvoid}, c::fmi2Component) status = ccall(cfunc, fmi2Status, (fmi2Component,), @@ -140,7 +140,7 @@ Source: FMISpec2.0.2[p.24]: 2.1.6 Initialization, Termination, and Resetting an Informs the FMU that the simulation run is terminated. """ -function fmi2Terminate(cfunc::Ptr{Nothing}, c::fmi2Component) +function fmi2Terminate(cfunc::Ptr{Cvoid}, c::fmi2Component) status = ccall(cfunc, fmi2Status, (fmi2Component,), @@ -155,7 +155,7 @@ Source: FMISpec2.0.2[p.24]: 2.1.6 Initialization, Termination, and Resetting an Is called by the environment to reset the FMU after a simulation run. The FMU goes into the same state as if fmi2Instantiate would have been called. """ -function fmi2Reset(cfunc::Ptr{Nothing}, c::fmi2Component) +function fmi2Reset(cfunc::Ptr{Cvoid}, c::fmi2Component) status = ccall(cfunc, fmi2Status, (fmi2Component,), @@ -170,7 +170,7 @@ Source: FMISpec2.0.2[p.24]: 2.1.7 Getting and Setting Variable Values Functions to get and set values of variables idetified by their valueReference """ -function fmi2GetReal!(cfunc::Ptr{Nothing}, c::fmi2Component, vr::Union{AbstractArray{fmi2ValueReference}, Ptr{fmi2ValueReference}}, nvr::Csize_t, value::Union{AbstractArray{fmi2Real}, Ptr{fmi2Real}}) +function fmi2GetReal!(cfunc::Ptr{Cvoid}, c::fmi2Component, vr::Union{AbstractArray{fmi2ValueReference}, Ptr{fmi2ValueReference}}, nvr::Csize_t, value::Union{AbstractArray{fmi2Real}, Ptr{fmi2Real}}) status = ccall(cfunc, fmi2Status, (fmi2Component, Ptr{fmi2ValueReference}, Csize_t, Ptr{fmi2Real}), @@ -185,7 +185,7 @@ Source: FMISpec2.0.2[p.24]: 2.1.7 Getting and Setting Variable Values Functions to get and set values of variables idetified by their valueReference """ -function fmi2SetReal(cfunc::Ptr{Nothing}, c::fmi2Component, vr::Union{AbstractArray{fmi2ValueReference}, Ptr{fmi2ValueReference}}, nvr::Csize_t, value::Union{AbstractArray{fmi2Real}, Ptr{fmi2Real}}) +function fmi2SetReal(cfunc::Ptr{Cvoid}, c::fmi2Component, vr::Union{AbstractArray{fmi2ValueReference}, Ptr{fmi2ValueReference}}, nvr::Csize_t, value::Union{AbstractArray{fmi2Real}, Ptr{fmi2Real}}) status = ccall(cfunc, fmi2Status, (fmi2Component, Ptr{fmi2ValueReference}, Csize_t, Ptr{fmi2Real}), @@ -200,7 +200,7 @@ Source: FMISpec2.0.2[p.24]: 2.1.7 Getting and Setting Variable Values Functions to get and set values of variables idetified by their valueReference """ -function fmi2GetInteger!(cfunc::Ptr{Nothing}, c::fmi2Component, vr::Union{AbstractArray{fmi2ValueReference}, Ptr{fmi2ValueReference}}, nvr::Csize_t, value::Union{AbstractArray{fmi2Integer}, Ptr{fmi2Integer}}) +function fmi2GetInteger!(cfunc::Ptr{Cvoid}, c::fmi2Component, vr::Union{AbstractArray{fmi2ValueReference}, Ptr{fmi2ValueReference}}, nvr::Csize_t, value::Union{AbstractArray{fmi2Integer}, Ptr{fmi2Integer}}) status = ccall(cfunc, fmi2Status, (fmi2Component, Ptr{fmi2ValueReference}, Csize_t, Ptr{fmi2Integer}), @@ -215,7 +215,7 @@ Source: FMISpec2.0.2[p.24]: 2.1.7 Getting and Setting Variable Values Functions to get and set values of variables idetified by their valueReference """ -function fmi2SetInteger(cfunc::Ptr{Nothing}, c::fmi2Component, vr::Union{AbstractArray{fmi2ValueReference}, Ptr{fmi2ValueReference}}, nvr::Csize_t, value::Union{AbstractArray{fmi2Integer}, Ptr{fmi2Integer}}) +function fmi2SetInteger(cfunc::Ptr{Cvoid}, c::fmi2Component, vr::Union{AbstractArray{fmi2ValueReference}, Ptr{fmi2ValueReference}}, nvr::Csize_t, value::Union{AbstractArray{fmi2Integer}, Ptr{fmi2Integer}}) status = ccall(cfunc, fmi2Status, (fmi2Component, Ptr{fmi2ValueReference}, Csize_t, Ptr{fmi2Integer}), @@ -230,7 +230,7 @@ Source: FMISpec2.0.2[p.24]: 2.1.7 Getting and Setting Variable Values Functions to get and set values of variables idetified by their valueReference """ -function fmi2GetBoolean!(cfunc::Ptr{Nothing}, c::fmi2Component, vr::Union{AbstractArray{fmi2ValueReference}, Ptr{fmi2ValueReference}}, nvr::Csize_t, value::Union{AbstractArray{fmi2Boolean}, Ptr{fmi2Boolean}}) +function fmi2GetBoolean!(cfunc::Ptr{Cvoid}, c::fmi2Component, vr::Union{AbstractArray{fmi2ValueReference}, Ptr{fmi2ValueReference}}, nvr::Csize_t, value::Union{AbstractArray{fmi2Boolean}, Ptr{fmi2Boolean}}) status = ccall(cfunc, fmi2Status, (fmi2Component, Ptr{fmi2ValueReference}, Csize_t, Ptr{fmi2Boolean}), @@ -245,7 +245,7 @@ Source: FMISpec2.0.2[p.24]: 2.1.7 Getting and Setting Variable Values Functions to get and set values of variables idetified by their valueReference """ -function fmi2SetBoolean(cfunc::Ptr{Nothing}, c::fmi2Component, vr::Union{AbstractArray{fmi2ValueReference}, Ptr{fmi2ValueReference}}, nvr::Csize_t, value::Union{AbstractArray{fmi2Boolean}, Ptr{fmi2Boolean}}) +function fmi2SetBoolean(cfunc::Ptr{Cvoid}, c::fmi2Component, vr::Union{AbstractArray{fmi2ValueReference}, Ptr{fmi2ValueReference}}, nvr::Csize_t, value::Union{AbstractArray{fmi2Boolean}, Ptr{fmi2Boolean}}) status = ccall(cfunc, fmi2Status, (fmi2Component, Ptr{fmi2ValueReference}, Csize_t, Ptr{fmi2Boolean}), @@ -260,7 +260,7 @@ Source: FMISpec2.0.2[p.24]: 2.1.7 Getting and Setting Variable Values Functions to get and set values of variables idetified by their valueReference """ -function fmi2GetString!(cfunc::Ptr{Nothing}, c::fmi2Component, vr::Union{AbstractArray{fmi2ValueReference}, Ptr{fmi2ValueReference}}, nvr::Csize_t, value::Union{AbstractArray{fmi2String}, Ptr{fmi2String}}) +function fmi2GetString!(cfunc::Ptr{Cvoid}, c::fmi2Component, vr::Union{AbstractArray{fmi2ValueReference}, Ptr{fmi2ValueReference}}, nvr::Csize_t, value::Union{AbstractArray{fmi2String}, Ptr{fmi2String}}) status = ccall(cfunc, fmi2Status, (fmi2Component, Ptr{fmi2ValueReference}, Csize_t, Ptr{fmi2String}), @@ -275,7 +275,7 @@ Source: FMISpec2.0.2[p.24]: 2.1.7 Getting and Setting Variable Values Functions to get and set values of variables idetified by their valueReference """ -function fmi2SetString(cfunc::Ptr{Nothing}, c::fmi2Component, vr::Union{AbstractArray{fmi2ValueReference}, Ptr{fmi2ValueReference}}, nvr::Csize_t, value::Union{AbstractArray{fmi2String}, Ptr{fmi2String}}) +function fmi2SetString(cfunc::Ptr{Cvoid}, c::fmi2Component, vr::Union{AbstractArray{fmi2ValueReference}, Ptr{fmi2ValueReference}}, nvr::Csize_t, value::Union{AbstractArray{fmi2String}, Ptr{fmi2String}}) status = ccall(cfunc, fmi2Status, (fmi2Component, Ptr{fmi2ValueReference}, Csize_t, Ptr{fmi2String}), @@ -290,7 +290,7 @@ Source: FMISpec2.0.2[p.26]: 2.1.8 Getting and Setting the Complete FMU State fmi2GetFMUstate makes a copy of the internal FMU state and returns a pointer to this copy """ -function fmi2GetFMUstate!(cfunc::Ptr{Nothing}, c::fmi2Component, FMUstate::Ref{fmi2FMUstate}) +function fmi2GetFMUstate!(cfunc::Ptr{Cvoid}, c::fmi2Component, FMUstate::Ref{fmi2FMUstate}) status = ccall(cfunc, fmi2Status, (fmi2Component, Ptr{fmi2FMUstate}), @@ -305,7 +305,7 @@ Source: FMISpec2.0.2[p.26]: 2.1.8 Getting and Setting the Complete FMU State fmi2SetFMUstate copies the content of the previously copied FMUstate back and uses it as actual new FMU state. """ -function fmi2SetFMUstate(cfunc::Ptr{Nothing}, c::fmi2Component, FMUstate::fmi2FMUstate) +function fmi2SetFMUstate(cfunc::Ptr{Cvoid}, c::fmi2Component, FMUstate::fmi2FMUstate) status = ccall(cfunc, fmi2Status, (fmi2Component, fmi2FMUstate), @@ -320,7 +320,7 @@ Source: FMISpec2.0.2[p.26]: 2.1.8 Getting and Setting the Complete FMU State fmi2FreeFMUstate frees all memory and other resources allocated with the fmi2GetFMUstate call for this FMUstate. """ -function fmi2FreeFMUstate!(cfunc::Ptr{Nothing}, c::fmi2Component, FMUstate::Ref{fmi2FMUstate}) +function fmi2FreeFMUstate!(cfunc::Ptr{Cvoid}, c::fmi2Component, FMUstate::Ref{fmi2FMUstate}) status = ccall(cfunc, fmi2Status, (fmi2Component, Ptr{fmi2FMUstate}), @@ -335,7 +335,7 @@ Source: FMISpec2.0.2[p.26]: 2.1.8 Getting and Setting the Complete FMU State fmi2SerializedFMUstateSize returns the size of the byte vector, in order that FMUstate can be stored in it. """ -function fmi2SerializedFMUstateSize!(cfunc::Ptr{Nothing}, c::fmi2Component, FMUstate::fmi2FMUstate, size::Ref{Csize_t}) +function fmi2SerializedFMUstateSize!(cfunc::Ptr{Cvoid}, c::fmi2Component, FMUstate::fmi2FMUstate, size::Ref{Csize_t}) status = ccall(cfunc, fmi2Status, (fmi2Component, fmi2FMUstate, Ptr{Csize_t}), @@ -350,7 +350,7 @@ Source: FMISpec2.0.2[p.26]: 2.1.8 Getting and Setting the Complete FMU State fmi2SerializeFMUstate serializes the data which is referenced by pointer FMUstate and copies this data in to the byte vector serializedState of length size """ -function fmi2SerializeFMUstate!(cfunc::Ptr{Nothing}, c::fmi2Component, FMUstate::fmi2FMUstate, serialzedState::AbstractArray{fmi2Byte}, size::Csize_t) +function fmi2SerializeFMUstate!(cfunc::Ptr{Cvoid}, c::fmi2Component, FMUstate::fmi2FMUstate, serialzedState::AbstractArray{fmi2Byte}, size::Csize_t) status = ccall(cfunc, fmi2Status, (fmi2Component, fmi2FMUstate, Ptr{fmi2Byte}, Csize_t), @@ -365,7 +365,7 @@ Source: FMISpec2.0.2[p.26]: 2.1.8 Getting and Setting the Complete FMU State fmi2DeSerializeFMUstate deserializes the byte vector serializedState of length size, constructs a copy of the FMU state and returns FMUstate, the pointer to this copy. """ -function fmi2DeSerializeFMUstate!(cfunc::Ptr{Nothing}, c::fmi2Component, serializedState::AbstractArray{fmi2Byte}, size::Csize_t, FMUstate::Ref{fmi2FMUstate}) +function fmi2DeSerializeFMUstate!(cfunc::Ptr{Cvoid}, c::fmi2Component, serializedState::AbstractArray{fmi2Byte}, size::Csize_t, FMUstate::Ref{fmi2FMUstate}) status = ccall(cfunc, fmi2Status, (fmi2Component, Ptr{fmi2Byte}, Csize_t, Ptr{fmi2FMUstate}), @@ -382,7 +382,7 @@ This function computes the directional derivatives of an FMU. ΔvUnknown = ∂h / ∂vKnown ⋅ ΔvKnown """ -function fmi2GetDirectionalDerivative!(cfunc::Ptr{Nothing}, +function fmi2GetDirectionalDerivative!(cfunc::Ptr{Cvoid}, c::fmi2Component, vUnknown_ref::AbstractArray{fmi2ValueReference}, nUnknown::Csize_t, @@ -408,7 +408,7 @@ Sets the n-th time derivative of real input variables. vr defines the value references of the variables the array order specifies the corresponding order of derivation of the variables """ -function fmi2SetRealInputDerivatives(cfunc::Ptr{Nothing}, c::fmi2Component, vr::AbstractArray{fmi2ValueReference}, nvr::Csize_t, order::AbstractArray{fmi2Integer}, value::AbstractArray{fmi2Real}) +function fmi2SetRealInputDerivatives(cfunc::Ptr{Cvoid}, c::fmi2Component, vr::AbstractArray{fmi2ValueReference}, nvr::Csize_t, order::AbstractArray{fmi2Integer}, value::AbstractArray{fmi2Real}) status = ccall(cfunc, fmi2Status, (fmi2Component, Ptr{fmi2ValueReference}, Csize_t, Ptr{fmi2Integer}, Ptr{fmi2Real}), @@ -425,7 +425,7 @@ Retrieves the n-th derivative of output values. vr defines the value references of the variables the array order specifies the corresponding order of derivation of the variables """ -function fmi2GetRealOutputDerivatives!(cfunc::Ptr{Nothing}, c::fmi2Component, vr::AbstractArray{fmi2ValueReference}, nvr::Csize_t, order::AbstractArray{fmi2Integer}, value::AbstractArray{fmi2Real}) +function fmi2GetRealOutputDerivatives!(cfunc::Ptr{Cvoid}, c::fmi2Component, vr::AbstractArray{fmi2ValueReference}, nvr::Csize_t, order::AbstractArray{fmi2Integer}, value::AbstractArray{fmi2Real}) status = ccall(cfunc, fmi2Status, (fmi2Component, Ptr{fmi2ValueReference}, Csize_t, Ptr{fmi2Integer}, Ptr{fmi2Real}), @@ -440,7 +440,7 @@ Source: FMISpec2.0.2[p.104]: 4.2.2 Computation The computation of a time step is started. """ -function fmi2DoStep(cfunc::Ptr{Nothing}, c::fmi2Component, currentCommunicationPoint::fmi2Real, communicationStepSize::fmi2Real, noSetFMUStatePriorToCurrentPoint::fmi2Boolean) +function fmi2DoStep(cfunc::Ptr{Cvoid}, c::fmi2Component, currentCommunicationPoint::fmi2Real, communicationStepSize::fmi2Real, noSetFMUStatePriorToCurrentPoint::fmi2Boolean) status = ccall(cfunc, fmi2Status, (fmi2Component, fmi2Real, fmi2Real, fmi2Boolean), c, currentCommunicationPoint, communicationStepSize, noSetFMUStatePriorToCurrentPoint) @@ -454,7 +454,7 @@ Source: FMISpec2.0.2[p.105]: 4.2.2 Computation Can be called if fmi2DoStep returned fmi2Pending in order to stop the current asynchronous execution. """ -function fmi2CancelStep(cfunc::Ptr{Nothing}, c::fmi2Component) +function fmi2CancelStep(cfunc::Ptr{Cvoid}, c::fmi2Component) status = ccall(cfunc, fmi2Status, (fmi2Component,), @@ -469,7 +469,7 @@ Source: FMISpec2.0.2[p.106]: 4.2.3 Retrieving Status Information from the Slave Informs the master about the actual status of the simulation run. Which status information is to be returned is specified by the argument fmi2StatusKind. """ -function fmi2GetStatus!(cfunc::Ptr{Nothing}, c::fmi2Component, s::fmi2StatusKind, value::Ptr{fmi2Status}) +function fmi2GetStatus!(cfunc::Ptr{Cvoid}, c::fmi2Component, s::fmi2StatusKind, value::Ptr{fmi2Status}) status = ccall(cfunc, fmi2Status, (fmi2Component, fmi2StatusKind, Ptr{fmi2Status}), @@ -484,7 +484,7 @@ Source: FMISpec2.0.2[p.106]: 4.2.3 Retrieving Status Information from the Slave Informs the master about the actual status of the simulation run. Which status information is to be returned is specified by the argument fmi2StatusKind. """ -function fmi2GetRealStatus!(cfunc::Ptr{Nothing}, c::fmi2Component, s::fmi2StatusKind, value::Ptr{fmi2Real}) +function fmi2GetRealStatus!(cfunc::Ptr{Cvoid}, c::fmi2Component, s::fmi2StatusKind, value::Ptr{fmi2Real}) status = ccall(cfunc, fmi2Status, (fmi2Component, fmi2StatusKind, Ptr{fmi2Real}), @@ -499,7 +499,7 @@ Source: FMISpec2.0.2[p.106]: 4.2.3 Retrieving Status Information from the Slave Informs the master about the actual status of the simulation run. Which status information is to be returned is specified by the argument fmi2StatusKind. """ -function fmi2GetIntegerStatus!(cfunc::Ptr{Nothing}, c::fmi2Component, s::fmi2StatusKind, value::Ptr{fmi2Integer}) +function fmi2GetIntegerStatus!(cfunc::Ptr{Cvoid}, c::fmi2Component, s::fmi2StatusKind, value::Ptr{fmi2Integer}) status = ccall(cfunc, fmi2Status, (fmi2Component, fmi2StatusKind, Ptr{fmi2Integer}), @@ -514,7 +514,7 @@ Source: FMISpec2.0.2[p.106]: 4.2.3 Retrieving Status Information from the Slave Informs the master about the actual status of the simulation run. Which status information is to be returned is specified by the argument fmi2StatusKind. """ -function fmi2GetBooleanStatus!(cfunc::Ptr{Nothing}, c::fmi2Component, s::fmi2StatusKind, value::Ptr{fmi2Boolean}) +function fmi2GetBooleanStatus!(cfunc::Ptr{Cvoid}, c::fmi2Component, s::fmi2StatusKind, value::Ptr{fmi2Boolean}) status = ccall(cfunc, fmi2Status, (fmi2Component, fmi2StatusKind, Ptr{fmi2Boolean}), @@ -529,7 +529,7 @@ Source: FMISpec2.0.2[p.106]: 4.2.3 Retrieving Status Information from the Slave Informs the master about the actual status of the simulation run. Which status information is to be returned is specified by the argument fmi2StatusKind. """ -function fmi2GetStringStatus!(cfunc::Ptr{Nothing}, c::fmi2Component, s::fmi2StatusKind, value::Ptr{fmi2String}) +function fmi2GetStringStatus!(cfunc::Ptr{Cvoid}, c::fmi2Component, s::fmi2StatusKind, value::Ptr{fmi2String}) status = ccall(cfunc, fmi2Status, (fmi2Component, fmi2StatusKind, Ptr{fmi2String}), @@ -546,7 +546,7 @@ Source: FMISpec2.0.2[p.83]: 3.2.1 Providing Independent Variables and Re-initial Set a new time instant and re-initialize caching of variables that depend on time, provided the newly provided time value is different to the previously set time value (variables that depend solely on constants or parameters need not to be newly computed in the sequel, but the previously computed values can be reused). """ -function fmi2SetTime(cfunc::Ptr{Nothing}, c::fmi2Component, time::fmi2Real) +function fmi2SetTime(cfunc::Ptr{Cvoid}, c::fmi2Component, time::fmi2Real) status = ccall(cfunc, fmi2Status, (fmi2Component, fmi2Real), @@ -561,7 +561,7 @@ Source: FMISpec2.0.2[p.83]: 3.2.1 Providing Independent Variables and Re-initial Set a new (continuous) state vector and re-initialize caching of variables that depend on the states. Argument nx is the length of vector x and is provided for checking purposes """ -function fmi2SetContinuousStates(cfunc::Ptr{Nothing}, c::fmi2Component, x::Union{AbstractArray{fmi2Real}, Ptr{fmi2Real}}, nx::Csize_t) +function fmi2SetContinuousStates(cfunc::Ptr{Cvoid}, c::fmi2Component, x::Union{AbstractArray{fmi2Real}, Ptr{fmi2Real}}, nx::Csize_t) status = ccall(cfunc, fmi2Status, (fmi2Component, Ptr{fmi2Real}, Csize_t), @@ -576,7 +576,7 @@ Source: FMISpec2.0.2[p.84]: 3.2.2 Evaluation of Model Equations The model enters Event Mode from the Continuous-Time Mode and discrete-time equations may become active (and relations are not “frozen”). """ -function fmi2EnterEventMode(cfunc::Ptr{Nothing}, c::fmi2Component) +function fmi2EnterEventMode(cfunc::Ptr{Cvoid}, c::fmi2Component) status = ccall(cfunc, fmi2Status, (fmi2Component,), @@ -591,7 +591,7 @@ Source: FMISpec2.0.2[p.84]: 3.2.2 Evaluation of Model Equations The FMU is in Event Mode and the super dense time is incremented by this call. """ -function fmi2NewDiscreteStates!(cfunc::Ptr{Nothing}, c::fmi2Component, eventInfo::Ptr{fmi2EventInfo}) +function fmi2NewDiscreteStates!(cfunc::Ptr{Cvoid}, c::fmi2Component, eventInfo::Ptr{fmi2EventInfo}) status = ccall(cfunc, fmi2Status, (fmi2Component, Ptr{fmi2EventInfo}), @@ -607,7 +607,7 @@ Source: FMISpec2.0.2[p.85]: 3.2.2 Evaluation of Model Equations The model enters Continuous-Time Mode and all discrete-time equations become inactive and all relations are “frozen”. This function has to be called when changing from Event Mode (after the global event iteration in Event Mode over all involved FMUs and other models has converged) into Continuous-Time Mode. """ -function fmi2EnterContinuousTimeMode(cfunc::Ptr{Nothing}, c::fmi2Component) +function fmi2EnterContinuousTimeMode(cfunc::Ptr{Cvoid}, c::fmi2Component) status = ccall(cfunc, fmi2Status, (fmi2Component,), @@ -624,7 +624,7 @@ This function must be called by the environment after every completed step of th If enterEventMode == fmi2True, the event mode must be entered If terminateSimulation == fmi2True, the simulation shall be terminated """ -function fmi2CompletedIntegratorStep!(cfunc::Ptr{Nothing}, +function fmi2CompletedIntegratorStep!(cfunc::Ptr{Cvoid}, c::fmi2Component, noSetFMUStatePriorToCurrentPoint::fmi2Boolean, enterEventMode::Ptr{fmi2Boolean}, @@ -643,15 +643,15 @@ Source: FMISpec2.0.2[p.86]: 3.2.2 Evaluation of Model Equations Compute state derivatives at the current time instant and for the current states. """ -function fmi2GetDerivatives!(cfunc::Ptr{Nothing}, +function fmi2GetDerivatives!(cfunc::Ptr{Cvoid}, c::fmi2Component, - derivatives::Union{AbstractArray{fmi2Real}, Ptr{fmi2Real}}, + derivatives::Union{Array{fmi2Real}, Ptr{fmi2Real}}, nx::Csize_t) status = ccall(cfunc, fmi2Status, (fmi2Component, Ptr{fmi2Real}, Csize_t), c, derivatives, nx) - @debug "fmi2GetDerivatives(c: $(c), derivatives: $(derivatives), nx: $(nx)) → $(status)" + @debug "fmi2GetDerivatives(c: " c ", derivatives: " derivatives ", nx: " nx ") → " status ")" return status end export fmi2GetDerivatives! @@ -661,7 +661,7 @@ Source: FMISpec2.0.2[p.86]: 3.2.2 Evaluation of Model Equations Compute event indicators at the current time instant and for the current states. """ -function fmi2GetEventIndicators!(cfunc::Ptr{Nothing}, c::fmi2Component, eventIndicators::Union{AbstractArray{fmi2Real}, Ptr{fmi2Real}}, ni::Csize_t) +function fmi2GetEventIndicators!(cfunc::Ptr{Cvoid}, c::fmi2Component, eventIndicators::Union{AbstractArray{fmi2Real}, Ptr{fmi2Real}}, ni::Csize_t) status = ccall(cfunc, fmi2Status, (fmi2Component, Ptr{fmi2Real}, Csize_t), @@ -676,7 +676,7 @@ Source: FMISpec2.0.2[p.86]: 3.2.2 Evaluation of Model Equations Return the new (continuous) state vector x. """ -function fmi2GetContinuousStates!(cfunc::Ptr{Nothing}, c::fmi2Component, +function fmi2GetContinuousStates!(cfunc::Ptr{Cvoid}, c::fmi2Component, x::Union{AbstractArray{fmi2Real}, Ptr{fmi2Real}}, nx::Csize_t) status = ccall(cfunc, @@ -693,7 +693,7 @@ Source: FMISpec2.0.2[p.86]: 3.2.2 Evaluation of Model Equations Return the nominal values of the continuous states. """ -function fmi2GetNominalsOfContinuousStates!(cfunc::Ptr{Nothing}, c::fmi2Component, x_nominal::Union{AbstractArray{fmi2Real}, Ptr{fmi2Real}}, nx::Csize_t) +function fmi2GetNominalsOfContinuousStates!(cfunc::Ptr{Cvoid}, c::fmi2Component, x_nominal::Union{AbstractArray{fmi2Real}, Ptr{fmi2Real}}, nx::Csize_t) status = ccall(cfunc, fmi2Status, (fmi2Component, Ptr{fmi2Real}, Csize_t), diff --git a/src/FMI2/cfunc_unload.jl b/src/FMI2/cfunc_unload.jl new file mode 100644 index 0000000..f1ff23c --- /dev/null +++ b/src/FMI2/cfunc_unload.jl @@ -0,0 +1,156 @@ +# +# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher +# Licensed under the MIT license. See LICENSE file in the project root for details. +# + +function unload_fmi2GetTypesPlatform() + @assert false "The function `fmi2GetTypesPlatform` is not callable anymore, because the FMU is unloaded!" + str = "" + return pointer(str) +end + +function unload_fmi2GetVersion() + @assert false "The function `fmi2GetVersion()` is not callable anymore, because the FMU is unloaded!" + str = "" + return pointer(str) +end + +# 2.1.5 +function unload_fmi2Instantiate(_instanceName::fmi2String, + fmuType::fmi2Type, + _fmuGUID::fmi2String, + _fmuResourceLocation::fmi2String, + _functions::Ptr{fmi2CallbackFunctions}, + visible::fmi2Boolean, + loggingOn::fmi2Boolean) + + @assert false "The function `fmi2Instantiate` is not callable anymore, because the FMU is unloaded!" + return C_NULL +end + +function unload_fmi2FreeInstance(_component::fmi2Component) + @assert false "The function `fmi2FreeInstance` is not callable anymore, because the FMU is unloaded!" + return nothing +end + +function unload_fmi2SetDebugLogging(_component::fmi2Component, loggingOn::fmi2Boolean, nCategories::Csize_t, categories::Ptr{fmi2String}) + @assert false "The function `fmi2SetDebugLogging` is not callable anymore, because the FMU is unloaded!" + return fmi2StatusFatal +end + +function unload_fmi2SetupExperiment(_component::fmi2Component, toleranceDefined::fmi2Boolean, tolerance::fmi2Real, startTime::fmi2Real, stopTimeDefined::fmi2Boolean, stopTime::fmi2Real) + @assert false "The function `fmi2SetupExperiment` is not callable anymore, because the FMU is unloaded!" + return fmi2StatusFatal +end + +function unload_fmi2EnterInitializationMode(_component::fmi2Component) + @assert false "The function `fmi2EnterInitializationMode` is not callable anymore, because the FMU is unloaded!" + return fmi2StatusFatal +end + +function unload_fmi2ExitInitializationMode(_component::fmi2Component) + @assert false "The function `fmi2ExitInitializationMod` is not callable anymore, because the FMU is unloaded!" + return fmi2StatusFatal +end + +function unload_fmi2Terminate(_component::fmi2Component) + @assert false "The function `fmi2Terminate` is not callable anymore, because the FMU is unloaded!" + return fmi2StatusFatal +end + +function unload_fmi2Reset(_component::fmi2Component) + @assert false "The function `fmi2Reset` is not callable anymore, because the FMU is unloaded!" + return fmi2StatusFatal +end + +function unload_fmi2GetReal(_component::fmi2Component, _vr::Ptr{fmi2ValueReference}, nvr::Csize_t, _value::Ptr{fmi2Real}) + @assert false "The function `fmi2GetReal` is not callable anymore, because the FMU is unloaded!" + return fmi2StatusFatal +end + +function unload_fmi2GetInteger(_component::fmi2Component, _vr::Ptr{fmi2ValueReference}, nvr::Csize_t, _value::Ptr{fmi2Integer}) + @assert false "The function `fmi2GetInteger` is not callable anymore, because the FMU is unloaded!" + return fmi2StatusFatal +end + +function unload_fmi2GetBoolean(_component::fmi2Component, _vr::Ptr{fmi2ValueReference}, nvr::Csize_t, _value::Ptr{fmi2Boolean}) + @assert false "The function `fmi2GetBoolean` is not callable anymore, because the FMU is unloaded!" + return fmi2StatusFatal +end + +function unload_fmi2GetString(_component::fmi2Component, _vr::Ptr{fmi2ValueReference}, nvr::Csize_t, _value::Ptr{fmi2String}) + @assert false "The function `fmi2GetString` is not callable anymore, because the FMU is unloaded!" + return fmi2StatusFatal +end + +function unload_fmi2SetReal(_component::fmi2Component, _vr::Ptr{fmi2ValueReference}, nvr::Csize_t, _value::Ptr{fmi2Real}) + @assert false "The function `fmi2SetReal` is not callable anymore, because the FMU is unloaded!" + return fmi2StatusFatal +end + +function unload_fmi2SetInteger(_component::fmi2Component, _vr::Ptr{fmi2ValueReference}, nvr::Csize_t, _value::Ptr{fmi2Integer}) + @assert false "The function `fmi2SetInteger` is not callable anymore, because the FMU is unloaded!" + return fmi2StatusFatal +end + +function unload_fmi2SetBoolean(_component::fmi2Component, _vr::Ptr{fmi2ValueReference}, nvr::Csize_t, _value::Ptr{fmi2Boolean}) + @assert false "The function `fmi2SetBoolean` is not callable anymore, because the FMU is unloaded!" + return fmi2StatusFatal +end + +function unload_fmi2SetString(_component::fmi2Component, _vr::Ptr{fmi2ValueReference}, nvr::Csize_t, _value::Ptr{fmi2String}) + @assert false "The function `fmi2SetString` is not callable anymore, because the FMU is unloaded!" + return fmi2StatusFatal +end + +function unload_fmi2SetTime(_component::fmi2Component, time::fmi2Real) + @assert false "The function `fmi2SetTime` is not callable anymore, because the FMU is unloaded!" + return fmi2StatusFatal +end + +function unload_fmi2SetContinuousStates(_component::fmi2Component, _x::Ptr{fmi2Real}, nx::Csize_t) + @assert false "The function `fmi2SetContinuousStates` is not callable anymore, because the FMU is unloaded!" + return fmi2StatusFatal +end + +function unload_fmi2EnterEventMode(_component::fmi2Component) + @assert false "The function `fmi2EnterEventMode` is not callable anymore, because the FMU is unloaded!" + return fmi2StatusFatal +end + +function unload_fmi2NewDiscreteStates(_component::fmi2Component, _fmi2eventInfo::Ptr{fmi2EventInfo}) + @assert false "The function `fmi2NewDiscreteStates` is not callable anymore, because the FMU is unloaded!" + return fmi2StatusFatal +end + +function unload_fmi2EnterContinuousTimeMode(_component::fmi2Component) + @assert false "The function `fmi2EnterContinuousTimeMode` is not callable anymore, because the FMU is unloaded!" + return fmi2StatusFatal +end + +function unload_fmi2CompletedIntegratorStep(_component::fmi2Component, noSetFMUStatePriorToCurrentPoint::fmi2Boolean, enterEventMode::Ptr{fmi2Boolean}, terminateSimulation::Ptr{fmi2Boolean}) + @assert false "The function `fmi2CompletedIntegratorStep` is not callable anymore, because the FMU is unloaded!" + return fmi2StatusFatal +end + +function unload_fmi2GetDerivatives(_component::fmi2Component, _derivatives::Ptr{fmi2Real}, nx::Csize_t) + @assert false "The function `fmi2GetDerivatives` is not callable anymore, because the FMU is unloaded!" + return fmi2StatusFatal +end + +function unload_fmi2GetEventIndicators(_component::fmi2Component, _eventIndicators::Ptr{fmi2Real}, ni::Csize_t) + @assert false "The function `fmi2GetEventIndicators` is not callable anymore, because the FMU is unloaded!" + return fmi2StatusFatal +end + +function unload_fmi2GetContinuousStates(_component::fmi2Component, _x::Ptr{fmi2Real}, nx::Csize_t) + @assert false "The function `fmi2GetContinuousStates` is not callable anymore, because the FMU is unloaded!" + return fmi2StatusFatal +end + +function unload_fmi2GetNominalsOfContinuousStates(_component::fmi2Component, _x_nominal::Ptr{fmi2Real}, nx::Csize_t) + @assert false "The function `fmi2GetNominalsOfContinuousStates` is not callable anymore, because the FMU is unloaded!" + return fmi2StatusFatal +end + +# ToDo: Add CS functions! \ No newline at end of file diff --git a/src/FMI2/convert.jl b/src/FMI2/convert.jl index 7bc8a3f..6945df4 100644 --- a/src/FMI2/convert.jl +++ b/src/FMI2/convert.jl @@ -4,7 +4,10 @@ # """ -Formats a fmi2Status/Integer to String. + + fmi2StatusToString(status) + +Converts a fmi2Status/Integer `status` to String. """ function fmi2StatusToString(status::Union{fmi2Status, Integer}) if status == fmi2StatusOK @@ -20,13 +23,41 @@ function fmi2StatusToString(status::Union{fmi2Status, Integer}) elseif status == fmi2StatusPending return "Pending" else - @assert false "fmi2StatusToString($(status)): Unknown FMU status." + @assert false "fmi2StatusToString($(status)): Unknown FMU status `$(status)`." + end +end +export fmi2StatusToString + +""" + + fmi2StringToStatus(s) + +Converts a String `s` to fmi2Status. +""" +function fmi2StatusToString(s::AbstractString) + if s == "OK" + return fmi2StatusOK + elseif s == "Warning" + return fmi2StatusWarning + elseif s == "Discard" + return fmi2StatusDiscard + elseif s == "Error" + return fmi2StatusError + elseif s == "Fatal" + return fmi2StatusFatal + elseif s == "Pending" + return fmi2StatusPending + else + @assert false "fmi2StatusToString($(s)): Unknown FMU status `$(s)`." end end export fmi2StatusToString """ -ToDo. + + fmi2CausalityToString(c) + +Converts a fmi2Causality `c` to String. """ function fmi2CausalityToString(c::fmi2Causality) if c == fmi2CausalityParameter @@ -48,7 +79,10 @@ end export fmi2CausalityToString """ -ToDo. + + fmi2StringToCausality(s) + +Converts a String `s` to fmi2Causality. """ function fmi2StringToCausality(s::AbstractString) if s == "parameter" @@ -70,7 +104,10 @@ end export fmi2StringToCausality """ -ToDo. + + fmi2VariabilityToString(c) + +Converts a fmi2Variablitiy `c` to fmi2Variability. """ function fmi2VariabilityToString(c::fmi2Variability) if c == fmi2VariabilityConstant @@ -90,7 +127,10 @@ end export fmi2VariabilityToString """ -ToDo. + + fmi2StringToVariability(s) + +Converts a String `s` to fmi2Variablitiy. """ function fmi2StringToVariability(s::AbstractString) if s == "constant" @@ -110,7 +150,10 @@ end export fmi2StringToVariability """ -ToDo. + + fmi2InitialToString(c) + +Converts a fmi2Initial `c` to String. """ function fmi2InitialToString(c::fmi2Initial) if c == fmi2InitialApprox @@ -126,7 +169,10 @@ end export fmi2InitialToString """ -ToDo. + + fmi2StringToInitial(s) + +Converts a String `s` to fmi2Initial. """ function fmi2StringToInitial(s::AbstractString) if s == "approx" @@ -142,18 +188,21 @@ end export fmi2StringToInitial """ -ToDo. + + fmi2DependencyKindToString(dk) + +Converts a fmi2DependencyKind `dk` to String. """ -function fmi2DependencyKindToString(c::fmi2DependencyKind) - if c == fmi2DependencyKindDependent +function fmi2DependencyKindToString(dk::fmi2DependencyKind) + if dk == fmi2DependencyKindDependent return "dependent" - elseif c == fmi2DependencyKindConstant + elseif dk == fmi2DependencyKindConstant return "constant" - elseif c == fmi2DependencyKindFixed + elseif dk == fmi2DependencyKindFixed return "fixed" - elseif c == fmi2DependencyKindTunable + elseif dk == fmi2DependencyKindTunable return "tunable" - elseif c == fmi2DependencyKindDiscrete + elseif dk == fmi2DependencyKindDiscrete return "discrete" else @assert false "fmi2DependencyKindToString($(c)): Unknown dependency kind." @@ -162,7 +211,10 @@ end export fmi2DependencyKindToString """ -ToDo. + + fmi2StringToDependencyKind(s) + +Converts a String `s` to fmi2DependencyKind. """ function fmi2StringToDependencyKind(s::AbstractString) if s == "dependent" diff --git a/src/FMI2/core.jl b/src/FMI2/core.jl deleted file mode 100644 index e3210d3..0000000 --- a/src/FMI2/core.jl +++ /dev/null @@ -1,205 +0,0 @@ -# -# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher -# Licensed under the MIT license. See LICENSE file in the project root for details. -# - -# What is included in this file: -# - fmi2xxx-structs and -functions from the FMI2-specification - -""" -Source: FMISpec2.0.2[p.16]: 2.1.2 Platform Dependent Definitions - -FMI2 Data Types -To simplify porting, no C types are used in the function interfaces, but the alias types are defined in this section. -All definitions in this section are provided in the header file “fmi2TypesPlatform.h”. -""" -const fmi2Char = Cuchar -const fmi2String = Ptr{fmi2Char} -const fmi2Boolean = Cint -const fmi2Real = Creal # defined in FMICore.jl, dependent on the Julia architecture it's `cfloat` or `cdouble` -const fmi2Integer = Cint -const fmi2Byte = Char -const fmi2ValueReference = Cuint -const fmi2FMUstate = Ptr{Cvoid} -const fmi2Component = Ptr{Cvoid} -const fmi2ComponentEnvironment = Ptr{Cvoid} -const fmi2Enum = Array{Array{String}} # TODO: correct it -export fmi2Char, fmi2String, fmi2Boolean, fmi2Real, fmi2Integer, fmi2Byte, fmi2ValueReference, fmi2FMUstate, fmi2Component, fmi2ComponentEnvironment, fmi2Enum - -""" -Source: FMISpec2.0.2[p.18]: 2.1.3 Status Returned by Functions - -Status returned by functions. The status has the following meaning: -fmi2OK – all well -fmi2Warning – things are not quite right, but the computation can continue. Function “logger” was called in the model (see below), and it is expected that this function has shown the prepared information message to the user. -fmi2Discard – this return status is only possible if explicitly defined for the corresponding function -(ModelExchange: fmi2SetReal, fmi2SetInteger, fmi2SetBoolean, fmi2SetString, fmi2SetContinuousStates, fmi2GetReal, fmi2GetDerivatives, fmi2GetContinuousStates, fmi2GetEventIndicators; -CoSimulation: fmi2SetReal, fmi2SetInteger, fmi2SetBoolean, fmi2SetString, fmi2DoStep, fmiGetXXXStatus): -For “model exchange”: It is recommended to perform a smaller step size and evaluate the model equations again, for example because an iterative solver in the model did not converge or because a function is outside of its domain (for example sqrt()). If this is not possible, the simulation has to be terminated. -For “co-simulation”: fmi2Discard is returned also if the slave is not able to return the required status information. The master has to decide if the simulation run can be continued. In both cases, function “logger” was called in the FMU (see below) and it is expected that this function has shown the prepared information message to the user if the FMU was called in debug mode (loggingOn = fmi2True). Otherwise, “logger” should not show a message. -fmi2Error – the FMU encountered an error. The simulation cannot be continued with this FMU instance. If one of the functions returns fmi2Error, it can be tried to restart the simulation from a formerly stored FMU state by calling fmi2SetFMUstate. -This can be done if the capability flag canGetAndSetFMUstate is true and fmi2GetFMUstate was called before in non-erroneous state. If not, the simulation cannot be continued and fmi2FreeInstance or fmi2Reset must be called afterwards.4 Further processing is possible after this call; especially other FMU instances are not affected. Function “logger” was called in the FMU (see below), and it is expected that this function has shown the prepared information message to the user. -fmi2Fatal – the model computations are irreparably corrupted for all FMU instances. [For example, due to a run-time exception such as access violation or integer division by zero during the execution of an fmi function]. Function “logger” was called in the FMU (see below), and it is expected that this function has shown the prepared information message to the user. It is not possible to call any other function for any of the FMU instances. -fmi2Pending – this status is returned only from the co-simulation interface, if the slave executes the function in an asynchronous way. That means the slave starts to compute but returns immediately. The master has to call fmi2GetStatus(..., fmi2DoStepStatus) to determine if the slave has finished the computation. Can be returned only by fmi2DoStep and by fmi2GetStatus (see section 4.2.3). -""" -const fmi2Status = Cuint -const fmi2StatusOK = Cuint(0) -const fmi2StatusWarning = Cuint(1) -const fmi2StatusDiscard = Cuint(2) -const fmi2StatusError = Cuint(3) -const fmi2StatusFatal = Cuint(4) -const fmi2StatusPending = Cuint(5) -export fmi2Status, fmi2StatusOK, fmi2StatusWarning, fmi2StatusDiscard, fmi2StatusError, fmi2StatusFatal, fmi2StatusPending - -""" -Source: FMISpec2.0.2[p.19-22]: 2.1.5 Creation, Destruction and Logging of FMU Instances - -The struct contains pointers to functions provided by the environment to be used by the FMU. It is not allowed to change these functions between fmi2Instantiate(..) and fmi2Terminate(..) calls. Additionally, a pointer to the environment is provided (componentEnvironment) that needs to be passed to the “logger” function, in order that the logger function can utilize data from the environment, such as mapping a valueReference to a string. In the unlikely case that fmi2Component is also needed in the logger, it has to be passed via argument componentEnvironment. Argument componentEnvironment may be a null pointer. The componentEnvironment pointer is also passed to the stepFinished(..) function in order that the environment can provide an efficient way to identify the slave that called stepFinished(..). -""" -mutable struct fmi2CallbackFunctions - logger::Ptr{Cvoid} - allocateMemory::Ptr{Cvoid} - freeMemory::Ptr{Cvoid} - stepFinished::Ptr{Cvoid} - componentEnvironment::Ptr{Cvoid} -end -export fmi2CallbackFunctions - -""" -A not further specified annotation struct. -""" -mutable struct fmi2Annotation - # No implementation -end -export fmi2Annotation - -""" -Source: FMISpec2.0.2[p.48]: 2.2.7 Definition of Model Variables (ModelVariables) - -Enumeration that defines the causality of the variable. Allowed values of this enumeration: - -"parameter": Independent parameter (a data value that is constant during the simulation and is provided by the environment and cannot be used in connections). variability must be "fixed" or "tunable". initial must be exact or not present (meaning exact). -"calculatedParameter": A data value that is constant during the simulation and is computed during initialization or when tunable parameters change. variability must be "fixed" or "tunable". initial must be "approx", "calculated" or not present (meaning calculated). -"input": The variable value can be provided from another model or slave. It is not allowed to define initial. -"output": The variable value can be used by another model or slave. The algebraic relationship to the inputs is defined via the dependencies attribute of . -"local": Local variable that is calculated from other variables or is a continuous-time state (see section 2.2.8). It is not allowed to use the variable value in another model or slave. -"independent": The independent variable (usually “time”). All variables are a function of this independent variable. variability must be "continuous". At most one ScalarVariable of an FMU can be defined as "independent". If no variable is defined as "independent", it is implicitly present with name = "time" and unit = "s". If one variable is defined as "independent", it must be defined as "Real" without a "start" attribute. It is not allowed to call function fmi2SetReal on an "independent" variable. Instead, its value is initialized with fmi2SetupExperiment and after initialization set by fmi2SetTime for ModelExchange and by arguments currentCommunicationPoint and communicationStepSize of fmi2DoStep for CoSimulation. [The actual value can be inquired with fmi2GetReal.] -The default of causality is “local”. A continuous-time state must have causality = "local" or "output", see also section 2.2.8. -[causality = "calculatedParameter" and causality = "local" with variability = "fixed" or "tunable" are similar. The difference is that a calculatedParameter can be used in another model or slave, whereas a local variable cannot. For example, when importing an FMU in a Modelica environment, a "calculatedParameter" should be imported in a public section as final parameter, whereas a "local" variable should be imported in a protected section of the model.] -Added prefix "fmi2" to help with redefinition of constans in enums. -""" -const fmi2Causality = Cuint -const fmi2CausalityParameter = Cuint(0) -const fmi2CausalityCalculatedParameter = Cuint(1) -const fmi2CausalityInput = Cuint(2) -const fmi2CausalityOutput = Cuint(3) -const fmi2CausalityLocal = Cuint(4) -const fmi2CausalityIndependent = Cuint(5) -export fmi2Causality, fmi2CausalityParameter, fmi2CausalityCalculatedParameter, fmi2CausalityInput, fmi2CausalityOutput, fmi2CausalityLocal, fmi2CausalityIndependent - -""" -Source: FMISpec2.0.2[p.49]: 2.2.7 Definition of Model Variables (ModelVariables) - -Enumeration that defines the time dependency of the variable, in other words, it defines the time instants when a variable can change its value. - -"constant": The value of the variable never changes. -"fixed": The value of the variable is fixed after initialization, in other words, after fmi2ExitInitializationMode was called the variable value does not change anymore. -"tunable": The value of the variable is constant between external events (ModelExchange) and between Communication Points (Co-Simulation) due to changing variables with causality = "parameter" or "input" and variability = "tunable". Whenever a parameter or input signal with variability = "tunable" changes, an event is triggered externally (ModelExchange), or the change is performed at the next Communication Point (Co-Simulation) and the variables with variability = "tunable" and causality = "calculatedParameter" or "output" must be newly computed. -"discrete": ModelExchange: The value of the variable is constant between external and internal events (= time, state, step events defined implicitly in the FMU). Co-Simulation: By convention, the variable is from a “real” sampled data system and its value is only changed at Communication Points (also inside the slave). -"continuous": Only a variable of type = “Real” can be “continuous”. ModelExchange: No restrictions on value changes. Co-Simulation: By convention, the variable is from a differential -The default is “continuous”. -Added prefix "fmi2" to help with redefinition of constans in enums. -""" -const fmi2Variability = Cuint -const fmi2VariabilityConstant = Cuint(0) -const fmi2VariabilityFixed = Cuint(1) -const fmi2VariabilityTunable = Cuint(2) -const fmi2VariabilityDiscrete = Cuint(3) -const fmi2VariabilityContinuous = Cuint(4) -export fmi2Variability, fmi2VariabilityConstant, fmi2VariabilityFixed, fmi2VariabilityTunable, fmi2VariabilityDiscrete, fmi2VariabilityContinuous - -""" -Source: FMISpec2.0.2[p.48]: 2.2.7 Definition of Model Variables (ModelVariables) - -Enumeration that defines how the variable is initialized. It is not allowed to provide a value for initial if causality = "input" or "independent": - -"exact": The variable is initialized with the start value (provided under Real, Integer, Boolean, String or Enumeration). -"approx": The variable is an iteration variable of an algebraic loop and the iteration at initialization starts with the start value. -"calculated": The variable is calculated from other variables during initialization. It is not allowed to provide a “start” value. -If initial is not present, it is defined by the table below based on causality and variability. If initial = exact or approx, or causality = ″input″, a start value must be provided. If initial = calculated, or causality = ″independent″, it is not allowed to provide a start value. -If fmiSetXXX is not called on a variable with causality = ″input″, then the FMU must use the start value as value of this input. -Added prefix "fmi2" to help with redefinition of constans in enums. -""" -const fmi2Initial = Cuint -const fmi2InitialExact = Cuint(0) -const fmi2InitialApprox = Cuint(1) -const fmi2InitialCalculated = Cuint(2) -export fmi2Initial, fmi2InitialExact, fmi2InitialApprox, fmi2InitialCalculated - -""" -Source: FMISpec2.0.2[p.16]: 2.1.2 Platform Dependent Definitions - -To simplify porting, no C types are used in the function interfaces, but the alias types are defined in this section. -All definitions in this section are provided in the header file “fmi2TypesPlatform.h”. -""" -const fmi2True = fmi2Boolean(true) -const fmi2False = fmi2Boolean(false) -export fmi2True, fmi2False - -""" -Source: FMISpec2.0.2[p.19]: 2.1.5 Creation, Destruction and Logging of FMU Instances - -Argument fmuType defines the type of the FMU: -- fmi2ModelExchange: FMU with initialization and events; between events simulation of continuous systems is performed with external integrators from the environment. -- fmi2CoSimulation: Black box interface for co-simulation. -""" -const fmi2Type = Cuint -const fmi2TypeModelExchange = Cuint(0) -const fmi2TypeCoSimulation = Cuint(1) -export fmi2Type, fmi2TypeModelExchange, fmi2TypeCoSimulation - -""" -Source: FMISpec2.0.2[p.106]: 4.2.3 Retrieving Status Information from the Slave - -CoSimulation specific Enum representing state of FMU after fmi2DoStep returned fmi2Pending. -""" -const fmi2StatusKind = Cuint -const fmi2StatusKindDoStepStatus = Cuint(0) -const fmi2StatusKindPendingStatus = Cuint(1) -const fmi2StatusKindLastSuccessfulTime = Cuint(2) -const fmi2StatusKindTerminated = Cuint(3) -export fmi2StatusKind, fmi2StatusKindDoStepStatus, fmi2StatusKindPendingStatus, fmi2StatusKindLastSuccessfulTime, fmi2StatusKindTerminated - -""" -Source: FMISpec2.0.2[p.84]: 3.2.2 Evaluation of Model Equations - -If return argument fmi2eventInfo.newDiscreteStatesNeeded = fmi2True, the FMU should stay in Event Mode, and the FMU requires to set new inputs to the FMU (fmi2SetXXX on inputs) to compute and get the outputs (fmi2GetXXX on outputs) and to call fmi2NewDiscreteStates again. Depending on the connection with other FMUs, the environment shall -- call fmi2Terminate, if terminateSimulation = fmi2True is returned by at least one FMU. -- call fmi2EnterContinuousTimeMode if all FMUs return newDiscreteStatesNeeded = fmi2False. -- stay in Event Mode otherwise. -When the FMU is terminated, it is assumed that an appropriate message is printed by the logger function (see section 2.1.5) to explain the reason for the termination. -If nominalsOfContinuousStatesChanged = fmi2True, then the nominal values of the states have changed due to the function call and can be inquired with fmi2GetNominalsOfContinuousStates. -If valuesOfContinuousStatesChanged = fmi2True. then at least one element of the continuous state vector has changed its value due to the function call. The new values of the states can be retrieved with fmi2GetContinuousStates or individually for each state for which reinit = "true" by calling getReal. If no element of the continuous state vector has changed its value, valuesOfContinuousStatesChanged must return fmi2False. [If fmi2True would be returned in this case, an infinite event loop may occur.] -If nextEventTimeDefined = fmi2True, then the simulation shall integrate at most until time = nextEventTime, and shall call fmi2EnterEventMode at this time instant. If integration is stopped before nextEventTime, for example, due to a state event, the definition of nextEventTime becomes obsolete. -""" -mutable struct fmi2EventInfo - newDiscreteStatesNeeded::fmi2Boolean - terminateSimulation::fmi2Boolean - nominalsOfContinuousStatesChanged::fmi2Boolean - valuesOfContinuousStatesChanged::fmi2Boolean - nextEventTimeDefined::fmi2Boolean - nextEventTime::fmi2Real - - # constructor - function fmi2EventInfo() - inst = new() - inst.newDiscreteStatesNeeded = fmi2False - inst.terminateSimulation = fmi2False - inst.nominalsOfContinuousStatesChanged = fmi2False - inst.valuesOfContinuousStatesChanged = fmi2False - inst.nextEventTimeDefined = fmi2False - inst.nextEventTime = 0.0 - return inst - end -end -export fmi2EventInfo diff --git a/src/FMI2/ctype.jl b/src/FMI2/ctype.jl index 6122923..90ddf7b 100644 --- a/src/FMI2/ctype.jl +++ b/src/FMI2/ctype.jl @@ -559,15 +559,15 @@ function getAttributes(sv::fmi2ScalarVariable) variability = sv.variability initial = sv.initial - if causality == nothing + if isnothing(causality) causality = fmi2CausalityLocal # this is the default according FMI-spec p. 48 end - if variability == nothing + if isnothing(variability) variability = fmi2VariabilityContinuous # this is the default according FMI-spec p. 49 end - if initial == nothing + if isnothing(initial) # setting default value for initial according FMI-spec p. 51 if causality != nothing && variability != nothing if causality == fmi2CausalityParameter diff --git a/src/FMI2/eval.jl b/src/FMI2/eval.jl new file mode 100644 index 0000000..3331bf3 --- /dev/null +++ b/src/FMI2/eval.jl @@ -0,0 +1,293 @@ +# +# Copyright (c) 2022 Tobias Thummerer, Lars Mikelsons +# Licensed under the MIT license. See LICENSE file in the project root for details. +# + +# What is included in the file `eval.jl`? +# - calling function for FMU2 and FMU2Component + +import FMICore: fmi2ValueReference +import ChainRulesCore: ignore_derivatives + +""" + + (fmu::FMU2)(;dx::AbstractVector{<:Real}, + y::AbstractVector{<:Real}, + y_refs::AbstractVector{<:fmi2ValueReference}, + x::AbstractVector{<:Real}, + u::AbstractVector{<:Real}, + u_refs::AbstractVector{<:fmi2ValueReference}, + p::AbstractVector{<:Real}, + p_refs::AbstractVector{<:fmi2ValueReference}, + ec::AbstractVector{<:Real}, + t::Real) + +Evaluates a `FMU2` by setting the component state `x`, inputs `u` and/or time `t`. If no component is available, one is allocated. The result of the evaluation might be the system output `y` and/or state-derivative `dx`. +Not all options are available for any FMU type, e.g. setting state is not supported for CS-FMUs. Assertions will be generated for wrong use. + +# Keywords +- `dx`: An array to store the state-derivatives in. If not provided but necessary, a suitable array is allocated and returned. Not supported by CS-FMUs. +- `y`: An array to store the system outputs in. If not provided but requested, a suitable array is allocated and returned. +- `y_refs`: An array of value references to indicate which system outputs shall be returned. +- `x`: An array containing the states to be set. Not supported by CS-FMUs. +- `u`: An array containing the inputs to be set. +- `u_refs`: An array of value references to indicate which system inputs want to be set. +- `p`: An array of FMU parameters to be set. +- `p_refs`: An array of parameter references to indicate which system parameter sensitivities need to be determined. +- `ec`: An array of real valued implicit event conditions ("event indicators") +- `t`: A scalar value holding the system time to be set. + +# Returns (as Tuple) +- `y::Union{AbstractVector{<:Real}, Nothing}`: The system output `y` (if requested, otherwise `nothing`). +- `dx::Union{AbstractVector{<:Real}, Nothing}`: The system state-derivaitve (if ME-FMU, otherwise `nothing`). +- `ec::Union{AbstractVector{<:Real}, Nothing}`: The system event indicators (if ME-FMU, otherwise `nothing`). +""" +function (fmu::FMU2)(dx::AbstractVector{<:Real}, + y::AbstractVector{<:Real}, + y_refs::AbstractVector{<:fmi2ValueReference}, + x::AbstractVector{<:Real}, + u::AbstractVector{<:Real}, + u_refs::AbstractVector{<:fmi2ValueReference}, + p::AbstractVector{<:Real}, + p_refs::AbstractVector{<:fmi2ValueReference}, + ec::AbstractVector{<:Real}, + ec_idcs::AbstractVector{<:fmi2ValueReference}, + t::Real) + + if hasCurrentComponent(fmu) + c = getCurrentComponent(fmu) + else + logWarn(fmu, "No FMU2Component found. Allocating one.") + c = fmi2Instantiate!(fmu) + fmi2EnterInitializationMode(c) + fmi2ExitInitializationMode(c) + end + + return 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) +end + +function (fmu::FMU2)(;dx::AbstractVector{<:Real}=fmu.default_dx, + y::AbstractVector{<:Real}=fmu.default_y, + y_refs::AbstractVector{<:fmi2ValueReference}=fmu.default_y_refs, + x::AbstractVector{<:Real}=fmu.empty_fmi2Real, + u::AbstractVector{<:Real}=fmu.empty_fmi2Real, + u_refs::AbstractVector{<:fmi2ValueReference}=fmu.empty_fmi2ValueReference, + p::AbstractVector{<:Real}=fmu.default_p, + p_refs::AbstractVector{<:fmi2ValueReference}=fmu.default_p_refs, + ec::AbstractVector{<:Real}=fmu.default_ec, + ec_idcs::AbstractVector{<:fmi2ValueReference}=fmu.default_ec_idcs, + t::Real=fmu.default_t) + + return (fmu)(dx, y, y_refs, x, u, u_refs, p, p_refs, ec, ec_idcs, t) +end + +""" + + (c::FMU2Component)(;dx::AbstractVector{<:Real}, + y::AbstractVector{<:Real}, + y_refs::AbstractVector{<:fmi2ValueReference}, + x::AbstractVector{<:Real}, + u::AbstractVector{<:Real}, + u_refs::AbstractVector{<:fmi2ValueReference}, + p::AbstractVector{<:Real}, + p_refs::AbstractVector{<:fmi2ValueReference}, + ec::AbstractVector{<:Real}, + t::Real) + +Evaluates a `FMU2Component` by setting the component state `x`, inputs `u` and/or time `t`. The result of the evaluation might be the system output `y` and/or state-derivative `dx`. +Not all options are available for any FMU type, e.g. setting state is not supported for CS-FMUs. Assertions will be generated for wrong use. + +# Keywords +- `dx`: An array to store the state-derivatives in. If not provided but necessary, a suitable array is allocated and returned. Not supported by CS-FMUs. +- `y`: An array to store the system outputs in. If not provided but requested, a suitable array is allocated and returned. +- `y_refs`: An array of value references to indicate which system outputs shall be returned. +- `x`: An array containing the states to be set. Not supported by CS-FMUs. +- `u`: An array containing the inputs to be set. +- `u_refs`: An array of value references to indicate which system inputs want to be set. +- `p`: An array of FMU parameters to be set. +- `p_refs`: An array of parameter references to indicate which system parameter sensitivities need to be determined. +- `ec`: An array of real valued implicit event conditions ("event indicators") +- `t`: A scalar value holding the system time to be set. + +# Returns (as Tuple) +- `y::Union{AbstractVector{<:Real}, Nothing}`: The system output `y` (if requested, otherwise `nothing`). +- `dx::Union{AbstractVector{<:Real}, Nothing}`: The system state-derivaitve (if ME-FMU, otherwise `nothing`). +- `ec::Union{AbstractVector{<:Real}, Nothing}`: The system event indicators (if ME-FMU, otherwise `nothing`). +""" +function (c::FMU2Component)(dx::AbstractVector{<:Real}, + y::AbstractVector{<:Real}, + y_refs::AbstractVector{<:fmi2ValueReference}, + x::AbstractVector{<:Real}, + u::AbstractVector{<:Real}, + u_refs::AbstractVector{<:fmi2ValueReference}, + p::AbstractVector{<:Real}, + p_refs::AbstractVector{<:fmi2ValueReference}, + ec::AbstractVector{<:Real}, + ec_idcs::AbstractVector{<:fmi2ValueReference}, + t::Real) + + @assert (length(y) == length(y_refs)) "Length of `y` must match length of `y_refs`." + @assert (length(u) == length(u_refs)) "Length of `u` must match length of `u_refs`." + @assert (length(p) == length(p_refs)) "Length of `p` must match length of `p_refs`." + @assert (length(ec) == length(ec_idcs)) || (length(ec) == c.fmu.modelDescription.numberOfEventIndicators) "Length of `ec` ($(length(ec))) must match:\n- number of given event indicators `ec_idcs` (=$(length(ec_idcs))) or\n- absolute number of event indicators (=$(c.fmu.modelDescription.numberOfEventIndicators))." + + # Co-Simulation only + if !isnothing(c.fmu.modelDescription.coSimulation) + if c.type == fmi2TypeCoSimulation::fmi2Type + @assert length(ec) <= 0 "Keyword `ec != []` is invalid for CS-FMUs. Setting a buffer for event indicators is not possible in CS." + @assert length(dx) <= 0 "Keyword `dx != []` is invalid for CS-FMUs. Setting a state-derivative is not possible in CS." + @assert length(x) <= 0 "Keyword `x != []` is invalid for CS-FMUs. Setting a state is not possible in CS." + @assert t < 0.0 "Keyword `t != []` is invalid for CS-FMUs. Setting explicit time is not possible in CS." + end + end + + # Model-Exchange only + if !isnothing(c.fmu.modelDescription.modelExchange) + if c.type == fmi2TypeModelExchange::fmi2Type + # [ToDo] do some checks... + end + end + + # [ToDo] This is necessary, because ForwardDiffChainRules.jl can't handle arguments with type `Ptr{Nothing}`. + cRef = nothing + ignore_derivatives() do + cRef = pointer_from_objref(c) + cRef = UInt64(cRef) + end + + if c.fmu.executionConfig.concat_eval + + ret = eval!(cRef, dx, y, y_refs, x, u, u_refs, p, p_refs, ec, ec_idcs, t) + + len_dx = length(dx) + len_y = length(y_refs) + len_ec = length(ec) + + if len_dx > 0 + c.eval_output.dx = ret[1:len_dx] + else + c.eval_output.dx = nothing + end + + if len_y > 0 + c.eval_output.y = ret[1+len_dx:len_dx+len_y] + else + c.eval_output.y = nothing + end + + if len_ec > 0 + c.eval_output.ec = ret[1+len_dx+len_y:end] + else + c.eval_output.ec = nothing + end + + else + + c.eval_output.dx, c.eval_output.y, c.eval_output.ec = eval!(cRef, dx, y, y_refs, x, u, u_refs, p, p_refs, ec, ec_idcs, t) + end + + return c.eval_output +end + +function (c::FMU2Component)(;dx::AbstractVector{<:Real}=c.fmu.default_dx, + y::AbstractVector{<:Real}=c.fmu.default_y, + y_refs::AbstractVector{<:fmi2ValueReference}=c.fmu.default_y_refs, + x::AbstractVector{<:Real}=c.fmu.empty_fmi2Real, + u::AbstractVector{<:Real}=c.fmu.empty_fmi2Real, + u_refs::AbstractVector{<:fmi2ValueReference}=c.fmu.empty_fmi2ValueReference, + p::AbstractVector{<:Real}=c.fmu.default_p, + p_refs::AbstractVector{<:fmi2ValueReference}=c.fmu.default_p_refs, + ec::AbstractVector{<:Real}=c.fmu.default_ec, + ec_idcs::AbstractVector{<:fmi2ValueReference}=c.fmu.default_ec_idcs, + t::Real=c.fmu.default_t) + (c)(dx, y, y_refs, x, u, u_refs, p, p_refs, ec, ec_idcs, t) +end + +function eval!(cRef, dx, y, y_refs, x, u, u_refs, p, p_refs, ec, ec_idcs, t) + @assert isa(ec, AbstractArray{fmi2Real}) "eval!(...): Wrong dispatched: `ec` is `ForwardDiff.Dual` or `ReverseDiff.TrackedReal`.\nThis is most likely because you tried differentiating over a FMU.\nIf so, you need to `import FMISensitivity` first." + @assert isa(x, AbstractArray{fmi2Real}) "eval!(...): Wrong dispatched: `x` is `ForwardDiff.Dual` or `ReverseDiff.TrackedReal`.\nThis is most likely because you tried differentiating over a FMU.\nIf so, you need to `import FMISensitivity` first." + @assert isa(u, AbstractArray{fmi2Real}) "eval!(...): Wrong dispatched: `u` is `ForwardDiff.Dual` or `ReverseDiff.TrackedReal`.\nThis is most likely because you tried differentiating over a FMU.\nIf so, you need to `import FMISensitivity` first." + @assert isa(t, fmi2Real) "eval!(...): Wrong dispatched: `t` is `ForwardDiff.Dual` or `ReverseDiff.TrackedReal`.\nThis is most likely because you tried differentiating over a FMU.\nIf so, you need to `import FMISensitivity` first." + @assert isa(p, AbstractArray{fmi2Real}) "eval!(...): Wrong dispatched: `p` is `ForwardDiff.Dual` or `ReverseDiff.TrackedReal`.\nThis is most likely because you tried differentiating over a FMU.\nIf so, you need to `import FMISensitivity` first." + @assert false "Fatal error, no dispatch implemented!\nPlease open an issue with MWE and attach error message:\neval!($(typeof(cRef)), $(typeof(dx)), $(typeof(y)), $(typeof(y_refs)), $(typeof(x)), $(typeof(u)), $(typeof(u_refs)), $(typeof(p)), $(typeof(p_refs)), $(typeof(t)))" +end + +function eval!(cRef::UInt64, + dx::AbstractVector{<:fmi2Real}, + y::AbstractVector{<:fmi2Real}, + y_refs::AbstractVector{<:fmi2ValueReference}, + x::AbstractVector{<:fmi2Real}, + u::AbstractVector{<:fmi2Real}, + u_refs::AbstractVector{<:fmi2ValueReference}, + p::AbstractVector{<:fmi2Real}, + p_refs::AbstractVector{<:fmi2ValueReference}, + ec::AbstractVector{<:fmi2Real}, + ec_idcs::AbstractVector{<:fmi2ValueReference}, + t::fmi2Real) + + c = unsafe_pointer_to_objref(Ptr{Nothing}(cRef)) + + # set state + if length(x) > 0 && !c.fmu.isZeroState + fmi2SetContinuousStates(c, x) + end + + # set time + if t >= 0.0 + fmi2SetTime(c, t) + end + + # set input + if length(u) > 0 + fmi2SetReal(c, u_refs, u) + end + + # set parameters DURING simulation, this is very uncommon, but can be necessary if "tunable" parameters are optimized during simulation + if length(p) > 0 && c.fmu.executionConfig.set_p_every_step + fmi2SetReal(c, p_refs, p) + end + + # get derivative + if length(dx) > 0 + getDerivatives!(c, dx) + end + + # get output + if length(y) > 0 + getOutputs!(c, y, y_refs) + end + + # get event indicators + if length(ec) > 0 + + if length(ec_idcs) == c.fmu.modelDescription.numberOfEventIndicators || length(ec_idcs) == 0 # pick ALL event indicators + fmi2GetEventIndicators!(c, ec) + + else # pick only some specific ones + fmi2GetEventIndicators!(c, c.eventIndicatorBuffer) + ec[:] = c.eventIndicatorBuffer[ec_idcs] + + end + end + + if c.fmu.executionConfig.concat_eval + # ToDo: This allocations could be skipped by in-place modification + return vcat(y, dx, ec) # [y..., dx..., ec...] + else + return y, dx, ec + end +end + +function getDerivatives!(c::FMU2Component, dx::AbstractArray{<:fmi2Real}) + if c.fmu.isZeroState + dx[:] = [1.0] + else + fmi2GetDerivatives!(c, dx) + end + return nothing +end + +function getOutputs!(c::FMU2Component, y::AbstractArray{<:fmi2Real}, y_refs) + fmi2GetReal!(c, y_refs, y) + return nothing +end diff --git a/src/FMI2/struct.jl b/src/FMI2/struct.jl index 1e54d4d..3954b6b 100644 --- a/src/FMI2/struct.jl +++ b/src/FMI2/struct.jl @@ -61,12 +61,16 @@ mutable struct FMU2Solution{C} <: FMUSolution where {C} evals_∂ẋ_∂x::Integer evals_∂y_∂x::Integer + evals_∂e_∂x::Integer evals_∂ẋ_∂u::Integer evals_∂y_∂u::Integer + evals_∂e_∂u::Integer evals_∂ẋ_∂t::Integer evals_∂y_∂t::Integer + evals_∂e_∂t::Integer evals_∂ẋ_∂p::Integer evals_∂y_∂p::Integer + evals_∂e_∂p::Integer evals_fx_inplace::Integer evals_fx_outofplace::Integer @@ -93,12 +97,16 @@ mutable struct FMU2Solution{C} <: FMUSolution where {C} inst.evals_∂ẋ_∂x = 0 inst.evals_∂y_∂x = 0 + inst.evals_∂e_∂x = 0 inst.evals_∂ẋ_∂u = 0 inst.evals_∂y_∂u = 0 + inst.evals_∂e_∂u = 0 inst.evals_∂ẋ_∂t = 0 inst.evals_∂y_∂t = 0 + inst.evals_∂e_∂t = 0 inst.evals_∂ẋ_∂p = 0 inst.evals_∂y_∂p = 0 + inst.evals_∂e_∂p = 0 inst.evals_fx_inplace = 0 inst.evals_fx_outofplace = 0 @@ -136,9 +144,12 @@ function Base.show(io::IO, sol::FMU2Solution) print(io, "\t∂ẋ_∂u: $(sol.evals_∂ẋ_∂u)\n") print(io, "\t∂y_∂x: $(sol.evals_∂y_∂x)\n") print(io, "\t∂y_∂u: $(sol.evals_∂y_∂u)\n") + print(io, "\t∂e_∂x: $(sol.evals_∂e_∂x)\n") + print(io, "\t∂e_∂u: $(sol.evals_∂e_∂u)\n") print(io, "Gradient-Evaluations:\n") print(io, "\t∂ẋ_∂t: $(sol.evals_∂ẋ_∂t)\n") print(io, "\t∂y_∂t: $(sol.evals_∂y_∂t)\n") + print(io, "\t∂e_∂t: $(sol.evals_∂e_∂t)\n") print(io, "Callback-Evaluations:\n") print(io, "\tCondition (event-indicators): $(sol.evals_condition)\n") print(io, "\tTime-Choice (event-instances): $(sol.evals_timechoice)\n") @@ -146,7 +157,7 @@ function Base.show(io::IO, sol::FMU2Solution) print(io, "\tSave values: $(sol.evals_savevalues)\n") print(io, "\tSteps completed: $(sol.evals_stepcompleted)\n") - if sol.states != nothing + if !isnothing(sol.states) print(io, "States [$(length(sol.states))]:\n") if length(sol.states.u) > 10 for i in 1:9 @@ -160,7 +171,7 @@ function Base.show(io::IO, sol::FMU2Solution) end end - if sol.values != nothing + if !isnothing(sol.values) print(io, "Values [$(length(sol.values.saveval))]:\n") if length(sol.values.saveval) > 10 for i in 1:9 @@ -174,7 +185,7 @@ function Base.show(io::IO, sol::FMU2Solution) end end - if sol.events != nothing + if !isnothing(sol.events) print(io, "Events [$(length(sol.events))]:\n") if length(sol.events) > 10 for i in 1:9 @@ -214,15 +225,78 @@ mutable struct FMU2ComponentEnvironment end export FMU2ComponentEnvironment +mutable struct FMU2EvaluationOutput <: AbstractArray{Real, 1} + dx::Union{AbstractArray{<:Real}, Nothing} + y::Union{AbstractArray{<:Real}, Nothing} + ec::Union{AbstractArray{<:Real}, Nothing} + + function FMU2EvaluationOutput(dx::Union{AbstractArray{<:Real}, Nothing}, y::Union{AbstractArray{<:Real}, Nothing}, ec::Union{AbstractArray{<:Real}, Nothing}) + return new(dx, y, ec) + end + + function FMU2EvaluationOutput() + return FMU2EvaluationOutput(nothing, nothing, nothing) + end +end + +function Base.length(out::FMU2EvaluationOutput) + len_dx = isnothing(out.dx) ? 0 : length(out.dx) + len_y = isnothing(out.y) ? 0 : length(out.y) + return len_dx+len_y +end + +function Base.size(out::FMU2EvaluationOutput) + return (length(out),) +end + +function Base.getindex(out::FMU2EvaluationOutput, ind::CartesianIndex{A}) where {A} + Base.getindex(out, ind.I[1]) +end + +function Base.getindex(out::FMU2EvaluationOutput, ind) + @assert ind >= 1 "`getindex` for index $(ind) not supported." + + len_dx = isnothing(out.dx) ? 0 : length(out.dx) + if ind <= len_dx + return out.dx[ind] + else + ind -= len_dx + end + + len_y = isnothing(out.y) ? 0 : length(out.y) + if ind <= len_y + return out.y[ind] + else + ind -= len_y + end + + @assert false "`getindex` for index $(ind+len_y+len_dx) out of bounds [$(len_dx+len_y)]." + + #len = length(out.ec) + #@assert ind <= len "`getindex` for index $(ind): out of bounds." + #return out.ec[ind] +end + +function Base.setindex!(out::FMU2EvaluationOutput, v, I::Colon) + len_dx = length(out.dx) + for index in 1:length(v) + if index <= len_dx + setindex!(out.dx, v[index], index) + else + setindex!(out.y, v[index-len_dx], index-len_dx) + end + end +end + """ The mutable struct represents an allocated instance of an FMU in the FMI 2.0.2 Standard. """ -mutable struct FMU2Component{F} +mutable struct FMU2Component{F} #, J, G} compAddr::fmi2Component - fmu::F # type is always FMU2, but this would cause a circular dependency + fmu::F state::fmi2ComponentState componentEnvironment::FMU2ComponentEnvironment - problem # ODEProblem, but this is no dependency of FMICore.jl + problem # ToDo: ODEProblem, but this is not a dependency of FMICore.jl nor FMIImport.jl ... type::Union{fmi2Type, Nothing} solution::FMU2Solution force::Bool @@ -235,7 +309,6 @@ mutable struct FMU2Component{F} eventInfo::Union{fmi2EventInfo, Nothing} t::fmi2Real # the system time - next_t::fmi2Real # the next system time to be automatically set for the next evaluation t_offset::fmi2Real # time offset between simulation environment and FMU x::Union{Array{fmi2Real, 1}, Nothing} # the system states (or sometimes u) ẋ::Union{Array{fmi2Real, 1}, Nothing} # the system state derivative (or sometimes u̇) @@ -255,37 +328,46 @@ mutable struct FMU2Component{F} p_vrs::Array{fmi2ValueReference, 1} # the system parameter value references # sensitivities - A::Union{FMUJacobian, Nothing} - B::Union{FMUJacobian, Nothing} - C::Union{FMUJacobian, Nothing} - D::Union{FMUJacobian, Nothing} - E::Union{FMUJacobian, Nothing} - F::Union{FMUJacobian, Nothing} - - # deprecated - realValues::Dict - senseFunc::Symbol - jac_ẋy_x::Union{Matrix{fmi2Real}, Nothing} - jac_ẋy_u::Union{Matrix{fmi2Real}, Nothing} - jac_x::Union{Array{fmi2Real}, Nothing} - jac_u::Union{Array{fmi2Real}, Nothing} - jac_t::Union{fmi2Real, Nothing} - - jacobianUpdate! # function for a custom jacobian constructor (optimization) + ∂ẋ_∂x #::Union{J, Nothing} + ∂ẋ_∂u #::Union{J, Nothing} + ∂ẋ_∂p #::Union{J, Nothing} + ∂ẋ_∂t #::Union{G, Nothing} + + ∂y_∂x #::Union{J, Nothing} + ∂y_∂u #::Union{J, Nothing} + ∂y_∂p #::Union{J, Nothing} + ∂y_∂t #::Union{G, Nothing} + + ∂e_∂x #::Union{J, Nothing} + ∂e_∂u #::Union{J, Nothing} + ∂e_∂p #::Union{J, Nothing} + ∂e_∂t #::Union{G, Nothing} + + # performance (pointers to prevent repeating allocations) + _enterEventMode::Array{fmi2Boolean, 1} + _ptr_enterEventMode::Ptr{fmi2Boolean} + _terminateSimulation::Array{fmi2Boolean, 1} + _ptr_terminateSimulation::Ptr{fmi2Boolean} + + # misc skipNextDoStep::Bool # allows skipping the next `fmi2DoStep` like it is not called progressMeter # progress plot + eval_output::FMU2EvaluationOutput + + eventIndicatorBuffer::AbstractArray{<:fmi2Real} # constructor function FMU2Component{F}() where {F} - inst = new() + inst = new{F}() inst.state = fmi2ComponentStateInstantiated inst.t = -Inf - inst.next_t = -1.0 inst.t_offset = 0.0 inst.eventInfo = fmi2EventInfo() inst.problem = nothing inst.type = nothing + inst.eval_output = FMU2EvaluationOutput() + inst.loggingOn = fmi2False inst.visible = fmi2False inst.instanceName = "" @@ -304,24 +386,30 @@ mutable struct FMU2Component{F} inst.y_vrs = Array{fmi2ValueReference, 1}() inst.p_vrs = Array{fmi2ValueReference, 1}() - inst.A = nothing - inst.B = nothing - inst.C = nothing - inst.D = nothing + inst.∂ẋ_∂x = nothing + inst.∂ẋ_∂u = nothing + inst.∂ẋ_∂p = nothing + inst.∂ẋ_∂t = nothing + + inst.∂y_∂x = nothing + inst.∂y_∂u = nothing + inst.∂y_∂p = nothing + inst.∂y_∂t = nothing + + inst.∂e_∂x = nothing + inst.∂e_∂u = nothing + inst.∂e_∂p = nothing + inst.∂e_∂t = nothing # initialize further variables inst.skipNextDoStep = false - inst.jacobianUpdate! = nothing inst.progressMeter = nothing - # deprecated - inst.senseFunc = :auto - inst.realValues = Dict() - inst.jac_x = Array{fmi2Real, 1}() - inst.jac_u = nothing - inst.jac_t = -1.0 - inst.jac_ẋy_x = zeros(fmi2Real, 0, 0) - inst.jac_ẋy_u = zeros(fmi2Real, 0, 0) + # performance (pointers to prevent repeating allocations) + inst._enterEventMode = zeros(fmi2Boolean, 1) + inst._terminateSimulation = zeros(fmi2Boolean, 1) + inst._ptr_enterEventMode = pointer(inst._enterEventMode) + inst._ptr_terminateSimulation = pointer(inst._terminateSimulation) return inst end @@ -329,15 +417,15 @@ mutable struct FMU2Component{F} function FMU2Component(fmu::F) where {F} inst = FMU2Component{F}() inst.fmu = fmu - + inst.eventIndicatorBuffer = zeros(fmi2Real, fmu.modelDescription.numberOfEventIndicators) + return inst end function FMU2Component(compAddr::fmi2Component, fmu::F) where {F} - inst = FMU2Component{F}() + inst = FMU2Component(fmu) inst.compAddr = compAddr - inst.fmu = fmu - + return inst end end @@ -374,26 +462,28 @@ mutable struct FMU2ExecutionConfiguration <: FMUExecutionConfiguration handleStateEvents::Bool # handle state events during simulation/training handleTimeEvents::Bool # handle time events during simulation/training - handleEventIndicators::Union{Array{Integer}, Nothing} # indices of event indicators to be handled, if `nothing` all are handled assertOnError::Bool # wheter an exception is thrown if a fmi2XXX-command fails (>= fmi2StatusError) assertOnWarning::Bool # wheter an exception is thrown if a fmi2XXX-command warns (>= fmi2StatusWarning) autoTimeShift::Bool # wheter to shift all time-related functions for simulation intervals not starting at 0.0 - concat_y_dx::Bool # wheter FMU/Component evaluation should return a tuple (y, dx) or a conacatenation (y..., dx...) + concat_eval::Bool # wheter FMU/Component evaluation should return a tuple (y, dx, ec) or a conacatenation [y..., dx..., ec...] + inplace_eval::Bool # wheter FMU/Component evaluation should happen in place - sensealg # algorithm for sensitivity estimation over solve call (ToDo: Datatype) - useComponentShadow::Bool # whether FMU outputs/derivatives/jacobians should be cached for frule/rrule (useful for ForwardDiff) + sensealg # algorithm for sensitivity estimation over solve call ([ToDo] Datatype/Nothing) rootSearchInterpolationPoints::UInt # number of root search interpolation points - inPlace::Bool # whether faster in-place-fx should be used useVectorCallbacks::Bool # whether to vector (faster) or scalar (slower) callbacks maxNewDiscreteStateCalls::UInt # max calls for fmi2NewDiscreteStates before throwing an exception - maxStateEventsPerSecond::UInt # max state events allowed to occur per second (more is interpreted as event jittering) + maxStateEventsPerSecond::UInt # max state events allowed to occur per second (more is interpreted as event chattering) eval_t_gradients::Bool # if time gradients ∂ẋ_∂t and ∂y_∂t should be sampled (not part of the FMI standard) JVPBuiltInDerivatives::Bool # use built-in directional derivatives for JVP-sensitivities over FMU without caching the jacobian (because this is done in the FMU, but not per default) + sensitivity_strategy::Symbol # build up strategy for jacobians/gradients, available is `:FMIDirectionalDerivative`, `:FiniteDiff` + + set_p_every_step::Bool # whether parameters are set for every simulation step - this is uncommon, because parameters are (often) set just one time: during/after intialization + function FMU2ExecutionConfiguration() inst = new() @@ -410,18 +500,16 @@ mutable struct FMU2ExecutionConfiguration <: FMUExecutionConfiguration inst.handleStateEvents = true inst.handleTimeEvents = true - inst.handleEventIndicators = nothing - + inst.assertOnError = false inst.assertOnWarning = false inst.autoTimeShift = false - inst.concat_y_dx = true - + inst.concat_eval = true # [ToDo] this is currently necessary because of ReverseDiff.jl issue #221 + inst.sensealg = nothing # auto inst.rootSearchInterpolationPoints = 10 - inst.inPlace = true inst.useVectorCallbacks = true inst.maxNewDiscreteStateCalls = 100 @@ -429,9 +517,9 @@ mutable struct FMU2ExecutionConfiguration <: FMUExecutionConfiguration inst.eval_t_gradients = false inst.JVPBuiltInDerivatives = false + inst.sensitivity_strategy = :FMIDirectionalDerivative - # deprecated - inst.useComponentShadow = false + inst.set_p_every_step = false return inst end @@ -474,6 +562,9 @@ FMU2_EXECUTION_CONFIGURATION_NOTHING.instantiate = false FMU2_EXECUTION_CONFIGURATION_NOTHING.freeInstance = false export FMU2_EXECUTION_CONFIGURATION_NOTHING +FMU2_EXECUTION_CONFIGURATIONS = (FMU2_EXECUTION_CONFIGURATION_NO_FREEING, FMU2_EXECUTION_CONFIGURATION_NO_RESET, FMU2_EXECUTION_CONFIGURATION_RESET, FMU2_EXECUTION_CONFIGURATION_NOTHING) +export FMU2_EXECUTION_CONFIGURATIONS + """ The mutable struct representing a FMU (and a container for all its instances) in the FMI 2.0.2 Standard. Also contains the paths to the FMU and ZIP folder as well als all the FMI 2.0.2 function pointers. @@ -551,9 +642,16 @@ mutable struct FMU2 <: FMU hasTimeEvents::Union{Bool, Nothing} isZeroState::Bool - # parameters that are catched by optimizers (like in FMIFlux.jl) - optim_p_refs::AbstractVector{<:fmi2ValueReference} - optim_p::AbstractVector{<:Real} + # parameters that need sensitivities and/or are catched by optimizers (like in FMIFlux.jl) + default_t::Real + default_p_refs::AbstractVector{<:fmi2ValueReference} + default_p::AbstractVector{<:Real} + + default_ec::AbstractVector{<:Real} + default_ec_idcs::AbstractVector{<:fmi2ValueReference} + default_dx::AbstractVector{<:Real} + default_y::AbstractVector{<:Real} + default_y_refs::AbstractVector{<:fmi2ValueReference} # c-libraries libHandle::Ptr{Nothing} @@ -563,7 +661,14 @@ mutable struct FMU2 <: FMU # multi-threading threadComponents::Dict{Integer, Union{FMU2Component, Nothing}} - # START: experimental section (to FMIFlux.jl) + # default values for function calls + empty_fmi2Real::Array{fmi2Real, 1} + empty_fmi2ValueReference::Array{fmi2ValueReference,1} + + # indices of event indicators to be handled, if `nothing` all are handled + handleEventIndicators::Union{Vector{fmi2ValueReference}, Nothing} + + # START: experimental section (to FMIFlux.jl) - probably deprecated soon dependencies::Matrix{Union{fmi2DependencyKind, Nothing}} # END: experimental section @@ -579,13 +684,26 @@ mutable struct FMU2 <: FMU inst.hasStateEvents = nothing inst.hasTimeEvents = nothing - inst.optim_p_refs = Vector{fmi2ValueReference}() - inst.optim_p = Vector{fmi2Real}() - inst.executionConfig = FMU2_EXECUTION_CONFIGURATION_NO_RESET inst.threadComponents = Dict{Integer, Union{FMU2Component, Nothing}}() inst.cFunctionPtrs = Dict{String, Ptr{Nothing}}() + # default values for function calls + inst.empty_fmi2Real = zeros(fmi2Real,0) + inst.empty_fmi2ValueReference = zeros(fmi2ValueReference,0) + + inst.default_t = -1.0 + inst.default_p_refs = inst.empty_fmi2ValueReference + inst.default_p = inst.empty_fmi2Real + + inst.default_ec = inst.empty_fmi2Real + inst.default_ec_idcs = inst.empty_fmi2ValueReference + inst.default_y = inst.empty_fmi2Real + inst.default_y_refs = inst.empty_fmi2ValueReference + inst.default_dx = inst.empty_fmi2Real + + inst.handleEventIndicators = nothing + return inst end end @@ -594,7 +712,65 @@ export FMU2 """ Overload the Base.show() function for custom printing of the FMU2. """ -Base.show(io::IO, fmu::FMU2) = print(io, -"Model name: $(fmu.modelDescription.modelName) -Type: $(fmu.type)" -) \ No newline at end of file +function Base.show(io::IO, fmu::FMU2) + print(io, "Model name:\t$(fmu.modelDescription.modelName)\nType:\t\t$(fmu.type)") +end + +""" + ToDo: Doc String +""" +function hasCurrentComponent(fmu::FMU2) + tid = Threads.threadid() + return haskey(fmu.threadComponents, tid) && fmu.threadComponents[tid] != nothing +end +export hasCurrentComponent + +""" + ToDo: Doc String +""" +function getCurrentComponent(fmu::FMU2) + tid = Threads.threadid() + @assert hasCurrentComponent(fmu) ["No FMU instance allocated (in current thread with ID `$(tid)`), have you already called `fmi2Instantiate!`?"] + return fmu.threadComponents[tid] +end +export getCurrentComponent + +""" + ToDo: Doc String +""" +struct FMU2InputFunction{F} + fct!::F + vrs::Vector{fmi2ValueReference} + buffer::Vector{fmi2Real} + + function FMU2InputFunction(fct, vrs::Array{fmi2ValueReference}) + buffer = zeros(fmi2Real, length(vrs)) + + _fct = nothing + + if hasmethod(fct, Tuple{fmi2Real, AbstractArray{fmi2Real,1}}) + _fct = (c, x, t, u) -> fct(t, u) + elseif hasmethod(fct, Tuple{Union{FMU2Component, Nothing}, fmi2Real, AbstractArray{fmi2Real,1}}) + _fct = (c, x, t, u) -> fct(c, t, u) + elseif hasmethod(fct, Tuple{Union{FMU2Component, Nothing}, AbstractArray{fmi2Real,1}, AbstractArray{fmi2Real,1}}) + _fct = (c, x, t, u) -> fct(c, x, u) + elseif hasmethod(fct, Tuple{AbstractArray{fmi2Real,1}, fmi2Real, AbstractArray{fmi2Real,1}}) + _fct = (c, x, t, u) -> fct(x, t, u) + else + _fct = fct + end + @assert hasmethod(_fct, Tuple{FMU2Component, Union{AbstractArray{fmi2Real,1}, Nothing}, fmi2Real, AbstractArray{fmi2Real,1}}) "The given input function does not fit the needed input function pattern for FMUs, which are: \n- `inputFunction!(t::fmi2Real, u::AbstractArray{fmi2Real})`\n- `inputFunction!(comp::FMU2Component, t::fmi2Real, u::AbstractArray{fmi2Real})`\n- `inputFunction!(comp::FMU2Component, x::Union{AbstractArray{fmi2Real,1}, Nothing}, u::AbstractArray{fmi2Real})`\n- `inputFunction!(x::Union{AbstractArray{fmi2Real,1}, Nothing}, t::fmi2Real, u::AbstractArray{fmi2Real})`\n- `inputFunction!(comp::FMU2Component, x::Union{AbstractArray{fmi2Real,1}, Nothing}, t::fmi2Real, u::AbstractArray{fmi2Real})`" + + return new{typeof(_fct)}(_fct, vrs, buffer) + end +end +export FMU2InputFunction + +""" + ToDo: Doc String +""" +function eval!(ipf::FMU2InputFunction, c, x, t) + ipf.fct!(c, x, t, ipf.buffer) + return ipf.buffer +end +export eval! diff --git a/src/FMI3/cconst.jl b/src/FMI3/cconst.jl index 2ab4eb4..a012b34 100644 --- a/src/FMI3/cconst.jl +++ b/src/FMI3/cconst.jl @@ -20,7 +20,7 @@ const fmi3UInt32 = Cuint const fmi3Int64 = Clonglong const fmi3UInt64 = Culonglong const fmi3Boolean = Cuchar -const fmi3Char = Cuchar # changed to Cuchar to work with pointer function +const fmi3Char = Cuchar # changed to `Cuchar` to work with pointer function const fmi3String = Ptr{fmi3Char} const fmi3Byte = Cuchar const fmi3Binary = Ptr{fmi3Byte} @@ -28,10 +28,13 @@ const fmi3ValueReference = Cuint const fmi3FMUState = Ptr{Cvoid} const fmi3Instance = Ptr{Cvoid} const fmi3InstanceEnvironment = Ptr{Cvoid} -const fmi3Enum = Array{Array{String}} # TODO: correct it const fmi3Clock = Cint export fmi3Float32, fmi3Float64, fmi3Int8, fmi3UInt8, fmi3Int16, fmi3UInt16, fmi3Int32, fmi3UInt32, fmi3Int64, fmi3UInt64 -export fmi3Boolean, fmi3Char, fmi3String, fmi3Byte, fmi3Binary, fmi3ValueReference, fmi3FMUState, fmi3Instance, fmi3InstanceEnvironment, fmi3Enum, fmi3Clock +export fmi3Boolean, fmi3Char, fmi3String, fmi3Byte, fmi3Binary, fmi3ValueReference, fmi3FMUState, fmi3Instance, fmi3InstanceEnvironment, fmi3Clock + +# wildcards for how a user can pass a fmi3ValueReference +fmi3ValueReferenceFormat = Union{Nothing, String, AbstractArray{String,1}, fmi3ValueReference, AbstractArray{fmi3ValueReference,1}, Int64, AbstractArray{Int64,1}} +export fmi3ValueReferenceFormat """ Source: FMISpec3.0, Version D5ef1c1: 2.2.3. Status Returned by Functions diff --git a/src/FMI3/convert.jl b/src/FMI3/convert.jl index 662801a..fa98bb8 100644 --- a/src/FMI3/convert.jl +++ b/src/FMI3/convert.jl @@ -4,7 +4,7 @@ # """ -Formats the fmi3Status/Integer into a String. +ToDo. """ function fmi3StatusToString(status::Union{fmi3Status, Integer}) if status == fmi3StatusOK @@ -23,6 +23,26 @@ function fmi3StatusToString(status::Union{fmi3Status, Integer}) end export fmi3StatusToString +""" +ToDo. +""" +function fmi3StringToStatus(s::AbstractString) + if s == "OK" + return fmi3StatusOK + elseif s == "Warning" + return fmi3StatusWarning + elseif s == "Discard" + return fmi3StatusDiscard + elseif s == "Error" + return fmi3StatusError + elseif s == "Fatal" + return fmi3StatusFatal + else + return "Unknown" + end +end +export fmi3StringToStatus + """ ToDo. """ diff --git a/src/FMI3/ctype.jl b/src/FMI3/ctype.jl index a16d2b4..437e386 100644 --- a/src/FMI3/ctype.jl +++ b/src/FMI3/ctype.jl @@ -36,7 +36,7 @@ export fmi3VariableDependency # Custom helper, not part of the FMI-Spec. fmi3Unknown = fmi3VariableDependency -mutable struct mvFloat32 <: fmi3Variable +mutable struct fmi3VariableFloat32 <: fmi3Variable # common attributes # mandatory @@ -73,7 +73,7 @@ mutable struct mvFloat32 <: fmi3Variable dependenciesKind #::Array{fmi3String} # Constructor for not further specified ModelVariable - function mvFloat32(name::String, valueReference::fmi3ValueReference) + function fmi3VariableFloat32(name::String, valueReference::fmi3ValueReference) inst = new() inst.name = name inst.valueReference = valueReference @@ -104,7 +104,7 @@ mutable struct mvFloat32 <: fmi3Variable end end -mutable struct mvFloat64 <: fmi3Variable +mutable struct fmi3VariableFloat64 <: fmi3Variable # common attributes # mandatory @@ -141,7 +141,7 @@ mutable struct mvFloat64 <: fmi3Variable dependenciesKind #::Array{fmi3String} # Constructor for not further specified ModelVariable - function mvFloat64(name::String, valueReference::fmi3ValueReference) + function fmi3VariableFloat64(name::String, valueReference::fmi3ValueReference) inst = new() inst.name = name inst.valueReference = valueReference @@ -172,7 +172,7 @@ mutable struct mvFloat64 <: fmi3Variable end end -mutable struct mvInt8 <: fmi3Variable +mutable struct fmi3VariableInt8 <: fmi3Variable # common attributes # mandatory @@ -202,7 +202,7 @@ mutable struct mvInt8 <: fmi3Variable dependenciesKind #::Array{fmi3String} # Constructor for not further specified ModelVariable - function mvInt8(name::String, valueReference::fmi3ValueReference) + function fmi3VariableInt8(name::String, valueReference::fmi3ValueReference) inst = new() inst.name = name inst.valueReference = valueReference @@ -226,7 +226,7 @@ mutable struct mvInt8 <: fmi3Variable end end -mutable struct mvUInt8 <: fmi3Variable +mutable struct fmi3VariableUInt8 <: fmi3Variable # common attributes # mandatory @@ -256,7 +256,7 @@ mutable struct mvUInt8 <: fmi3Variable dependenciesKind #::Array{fmi3String} # Constructor for not further specified ModelVariable - function mvUInt8(name::String, valueReference::fmi3ValueReference) + function fmi3VariableUInt8(name::String, valueReference::fmi3ValueReference) inst = new() inst.name = name inst.valueReference = valueReference @@ -280,7 +280,7 @@ mutable struct mvUInt8 <: fmi3Variable end end -mutable struct mvInt16 <: fmi3Variable +mutable struct fmi3VariableInt16 <: fmi3Variable # common attributes # mandatory @@ -310,7 +310,7 @@ mutable struct mvInt16 <: fmi3Variable dependenciesKind #::Array{fmi3String} # Constructor for not further specified ModelVariable - function mvInt16(name::String, valueReference::fmi3ValueReference) + function fmi3VariableInt16(name::String, valueReference::fmi3ValueReference) inst = new() inst.name = name inst.valueReference = valueReference @@ -334,7 +334,7 @@ mutable struct mvInt16 <: fmi3Variable end end -mutable struct mvUInt16 <: fmi3Variable +mutable struct fmi3VariableUInt16 <: fmi3Variable # common attributes # mandatory @@ -364,7 +364,7 @@ mutable struct mvUInt16 <: fmi3Variable dependenciesKind #::Array{fmi3String} # Constructor for not further specified ModelVariable - function mvUInt16(name::String, valueReference::fmi3ValueReference) + function fmi3VariableUInt16(name::String, valueReference::fmi3ValueReference) inst = new() inst.name = name inst.valueReference = valueReference @@ -388,7 +388,7 @@ mutable struct mvUInt16 <: fmi3Variable end end -mutable struct mvInt32 <: fmi3Variable +mutable struct fmi3VariableInt32 <: fmi3Variable # common attributes # mandatory @@ -418,7 +418,7 @@ mutable struct mvInt32 <: fmi3Variable dependenciesKind #::Array{fmi3String} # Constructor for not further specified ModelVariable - function mvInt32(name::String, valueReference::fmi3ValueReference) + function fmi3VariableInt32(name::String, valueReference::fmi3ValueReference) inst = new() inst.name = name inst.valueReference = valueReference @@ -442,7 +442,7 @@ mutable struct mvInt32 <: fmi3Variable end end -mutable struct mvUInt32 <: fmi3Variable +mutable struct fmi3VariableUInt32 <: fmi3Variable # common attributes # mandatory @@ -472,7 +472,7 @@ mutable struct mvUInt32 <: fmi3Variable dependenciesKind #::Array{fmi3String} # Constructor for not further specified ModelVariable - function mvUInt32(name::String, valueReference::fmi3ValueReference) + function fmi3VariableUInt32(name::String, valueReference::fmi3ValueReference) inst = new() inst.name = name inst.valueReference = valueReference @@ -496,7 +496,7 @@ mutable struct mvUInt32 <: fmi3Variable end end -mutable struct mvInt64 <: fmi3Variable +mutable struct fmi3VariableInt64 <: fmi3Variable # common attributes # mandatory @@ -526,7 +526,7 @@ mutable struct mvInt64 <: fmi3Variable dependenciesKind #::Array{fmi3String} # Constructor for not further specified ModelVariable - function mvInt64(name::String, valueReference::fmi3ValueReference) + function fmi3VariableInt64(name::String, valueReference::fmi3ValueReference) inst = new() inst.name = name inst.valueReference = valueReference @@ -550,7 +550,7 @@ mutable struct mvInt64 <: fmi3Variable end end -mutable struct mvUInt64 <: fmi3Variable +mutable struct fmi3VariableUInt64 <: fmi3Variable # common attributes # mandatory @@ -580,7 +580,7 @@ mutable struct mvUInt64 <: fmi3Variable dependenciesKind #::Array{fmi3String} # Constructor for not further specified ModelVariable - function mvUInt64(name::String, valueReference::fmi3ValueReference) + function fmi3VariableUInt64(name::String, valueReference::fmi3ValueReference) inst = new() inst.name = name inst.valueReference = valueReference @@ -604,7 +604,7 @@ mutable struct mvUInt64 <: fmi3Variable end end -mutable struct mvBoolean <: fmi3Variable +mutable struct fmi3VariableBoolean <: fmi3Variable # common attributes # mandatory @@ -631,7 +631,7 @@ mutable struct mvBoolean <: fmi3Variable dependenciesKind #::Array{fmi3String} # Constructor for not further specified ModelVariable - function mvBoolean(name::String, valueReference::fmi3ValueReference) + function fmi3VariableBoolean(name::String, valueReference::fmi3ValueReference) inst = new() inst.name = name inst.valueReference = valueReference @@ -652,7 +652,7 @@ mutable struct mvBoolean <: fmi3Variable end end -mutable struct mvString <: fmi3Variable +mutable struct fmi3VariableString <: fmi3Variable # common attributes # mandatory @@ -675,7 +675,7 @@ mutable struct mvString <: fmi3Variable dependenciesKind #::Array{fmi3String} # Constructor for not further specified ModelVariable - function mvString(name::String, valueReference::fmi3ValueReference) + function fmi3VariableString(name::String, valueReference::fmi3ValueReference) inst = new() inst.name = name inst.valueReference = valueReference @@ -692,7 +692,7 @@ mutable struct mvString <: fmi3Variable end end -mutable struct mvBinary <: fmi3Variable +mutable struct fmi3VariableBinary <: fmi3Variable # common attributes # mandatory @@ -721,7 +721,7 @@ mutable struct mvBinary <: fmi3Variable dependenciesKind #::Array{fmi3String} # Constructor for not further specified ModelVariable - function mvBinary(name::String, valueReference::fmi3ValueReference) + function fmi3VariableBinary(name::String, valueReference::fmi3ValueReference) inst = new() inst.name = name inst.valueReference = valueReference @@ -744,7 +744,7 @@ mutable struct mvBinary <: fmi3Variable end end -mutable struct mvClock <: fmi3Variable +mutable struct fmi3VariableClock <: fmi3Variable # common attributes # mandatory @@ -776,7 +776,7 @@ mutable struct mvClock <: fmi3Variable dependenciesKind #::Array{fmi3String} # Constructor for not further specified ModelVariable - function mvClock(name::String, valueReference::fmi3ValueReference) + function fmi3VariableClock(name::String, valueReference::fmi3ValueReference) inst = new() inst.name = name inst.valueReference = valueReference @@ -802,7 +802,7 @@ mutable struct mvClock <: fmi3Variable end end -mutable struct mvEnumeration <: fmi3Variable +mutable struct fmi3VariableEnumeration <: fmi3Variable # common attributes # mandatory @@ -828,11 +828,11 @@ mutable struct mvEnumeration <: fmi3Variable start::Union{fmi3Int64, Nothing} # dependencies - dependencies #::Array{fmi3Int32} - dependenciesKind #::Array{fmi3String} + dependencies # ToDo: Typing! ::Array{fmi3Int32} + dependenciesKind # ToDo: Typing! ::Array{fmi3String} # Constructor for not further specified ModelVariable - function mvEnumeration(name::String, valueReference::fmi3ValueReference) + function fmi3VariableEnumeration(name::String, valueReference::fmi3ValueReference) inst = new() inst.name = name inst.valueReference = valueReference @@ -1131,8 +1131,7 @@ mutable struct fmi3ModelDescription stringValueReferences::Dict{String, fmi3ValueReference} numberOfContinuousStates::Int - enumerations::fmi3Enum - + # additional fields (non-FMI-specific) valueReferenceIndicies::Dict{UInt,UInt} @@ -1153,8 +1152,7 @@ mutable struct fmi3ModelDescription inst.modelVariables = Array{fmi3Variable, 1}() inst.modelStructure = fmi3ModelDescriptionModelStructure() inst.numberOfEventIndicators = nothing - inst.enumerations = [] - + inst.valueReferences = [] inst.inputValueReferences = [] inst.outputValueReferences = [] diff --git a/src/FMI3/struct.jl b/src/FMI3/struct.jl index b47e03c..b8c89ee 100644 --- a/src/FMI3/struct.jl +++ b/src/FMI3/struct.jl @@ -244,6 +244,9 @@ FMU3_EXECUTION_CONFIGURATION_NO_FREEING.instantiate = true FMU3_EXECUTION_CONFIGURATION_NO_FREEING.freeInstance = false export FMU3_EXECUTION_CONFIGURATION_NO_FREEING +FMU3_EXECUTION_CONFIGURATIONS = (FMU3_EXECUTION_CONFIGURATION_NO_FREEING, FMU3_EXECUTION_CONFIGURATION_NO_RESET, FMU3_EXECUTION_CONFIGURATION_RESET) +export FMU3_EXECUTION_CONFIGURATIONS + """ Container for event related information. """ diff --git a/src/FMICore.jl b/src/FMICore.jl index 2dde7f3..d72c620 100644 --- a/src/FMICore.jl +++ b/src/FMICore.jl @@ -5,6 +5,10 @@ module FMICore +using Requires +import ChainRulesCore +import Base: show + # check float size (32 or 64 bits) juliaArch = Sys.WORD_SIZE @assert (juliaArch == 64 || juliaArch == 32) "FMICore: Unknown Julia Architecture with $(juliaArch)-bit, must be 64- or 32-bit." @@ -13,70 +17,61 @@ if juliaArch == 32 Creal = Cfloat end -""" -The mutable struct representing an abstract (version unknown) FMU. -""" -abstract type FMU end -export FMU - -""" -ToDo. -""" -abstract type FMUSolution end -export FMUSolution - -""" -ToDo. -""" -abstract type FMUExecutionConfiguration end -export FMUExecutionConfiguration - -""" -ToDo. -""" -abstract type FMUEvent end -export FMUEvent +# checks if integrator has NaNs (that is not good...) +function assert_integrator_valid(integrator) + @assert !isnan(integrator.opts.internalnorm(integrator.u, integrator.t)) "NaN in `integrator.u` @ $(integrator.t)." +end -include("logging.jl") -include("jacobian.jl") +# copy only if field can't be overwritten +function fast_copy!(str, dst::Symbol, src) + @assert false "fast_copy! not implemented for src of type $(typeof(src))" +end +function fast_copy!(str, dst::Symbol, src::Nothing) + setfield!(str, dst, nothing) +end +function fast_copy!(str, dst::Symbol, src::AbstractArray) + tmp = getfield(str, dst) + if isnothing(tmp) || length(tmp) != length(src) + setfield!(str, dst, copy(src)) + else + tmp[:] = src + end +end -include("FMI2/cconst.jl") -include("FMI2/ctype.jl") -include("FMI2/cfunc.jl") -include("FMI2/convert.jl") -include("FMI2/struct.jl") +include("types.jl") +include("printing.jl") +include("FMI2/cconst.jl") include("FMI3/cconst.jl") + +include("FMI2/ctype.jl") include("FMI3/ctype.jl") + +include("FMI2/cfunc.jl") include("FMI3/cfunc.jl") + +include("FMI2/cfunc_unload.jl") +# ToDo: include("FMI3/cfunc_unload.jl") + +include("FMI2/convert.jl") include("FMI3/convert.jl") + +include("FMI2/struct.jl") include("FMI3/struct.jl") -function logInfo(component::FMU2Component, message, status::fmi2Status=fmi2StatusOK) - if component.loggingOn == fmi2True - ccall(component.callbackFunctions.logger, Cvoid, (fmi2ComponentEnvironment, fmi2String, fmi2Status, fmi2String, fmi2String), component.callbackFunctions.componentEnvironment, component.instanceName, status, "info", message * "\n") - end -end -function logInfo(::Nothing, message, status::fmi2Status=fmi2StatusOK) - @info "logInfo(::Nothing, $(message), $(status))" -end +include("FMI2/eval.jl") +# ToDo: include("FMI3/eval.jl") -function logWarning(component::FMU2Component, message, status::fmi2Status=fmi2StatusWarning) - if component.loggingOn == fmi2True - ccall(component.callbackFunctions.logger, Cvoid, (fmi2ComponentEnvironment, fmi2String, fmi2Status, fmi2String, fmi2String), component.callbackFunctions.componentEnvironment, component.instanceName, status, "warning", message * "\n") - end -end -function logWarning(::Nothing, message, status::fmi2Status=fmi2StatusOK) - @warn "logWarning(::Nothing, $(message), $(status))" -end +include("jacobian.jl") +include("logging.jl") +include("sense.jl") -function logError(component::FMU2Component, message, status::fmi2Status=fmi2StatusError) - if component.loggingOn == fmi2True - ccall(component.callbackFunctions.logger, Cvoid, (fmi2ComponentEnvironment, fmi2String, fmi2Status, fmi2String, fmi2String), component.callbackFunctions.componentEnvironment, component.instanceName, status, "error", message * "\n") +# Requires init +function __init__() + @require FMISensitivity="3e748fe5-cd7f-4615-8419-3159287187d2" begin + import .FMISensitivity + include("extensions/FMISensitivity.jl") end end -function logError(::Nothing, message, status::fmi2Status=fmi2StatusOK) - @error "logError(::Nothing, $(message), $(status))" -end end # module diff --git a/src/extensions/FMISensitivity.jl b/src/extensions/FMISensitivity.jl new file mode 100644 index 0000000..ed81697 --- /dev/null +++ b/src/extensions/FMISensitivity.jl @@ -0,0 +1,56 @@ +# +# Copyright (c) 2023 Tobias Thummerer, Lars Mikelsons +# Licensed under the MIT license. See LICENSE file in the project root for details. +# + +import .FMISensitivity.ForwardDiff +import .FMISensitivity.ReverseDiff + +########### + +import .FMISensitivity.SciMLSensitivity.ForwardDiff +import .FMISensitivity.SciMLSensitivity.ReverseDiff + +# check if scalar/vector is ForwardDiff.Dual +function isdual(e::ForwardDiff.Dual{T, V, N}) where {T, V, N} + return true +end +function isdual(e::AbstractVector{<:ForwardDiff.Dual{T, V, N}}) where {T, V, N} + return true +end + +# check if scalar/vector is ForwardDiff.Dual +function istracked(e::ReverseDiff.TrackedReal) + return true +end +function istracked(e::AbstractVector{<:ReverseDiff.TrackedReal}) + return true +end +function istracked(e::ReverseDiff.TrackedArray) + return true +end + +# makes Reals from ForwardDiff.Dual scalar/vector +function undual(e::ForwardDiff.Dual) + return ForwardDiff.value(e) +end + +# makes Reals from ReverseDiff.TrackedXXX scalar/vector +function untrack(e::ReverseDiff.TrackedReal) + return ReverseDiff.value(e) +end +function untrack(e::ReverseDiff.TrackedArray) + return ReverseDiff.value(e) +end + +# makes Reals from ForwardDiff/ReverseDiff.TrackedXXX scalar/vector +function unsense(e::ReverseDiff.TrackedReal) + return ReverseDiff.value(e) +end +function unsense(e::ReverseDiff.TrackedArray) + return ReverseDiff.value(e) +end +function unsense(e::ForwardDiff.Dual) + return ForwardDiff.value(e) +end + diff --git a/src/jacobian.jl b/src/jacobian.jl index 7f79869..d5e3dbe 100644 --- a/src/jacobian.jl +++ b/src/jacobian.jl @@ -1,83 +1,17 @@ # -# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher +# Copyright (c) 2023 Tobias Thummerer, Lars Mikelsons # Licensed under the MIT license. See LICENSE file in the project root for details. # -mutable struct FMUJacobian{R, V} - mtx::Matrix{R} - valid::Bool - - ∂f_refs::AbstractArray{<:V} - ∂x_refs::AbstractArray{<:V} - - updateFct! - - b::Vector{R} - c::Vector{R} - - numUpdates::Integer - numJVPs::Integer - numVJPs::Integer - - function FMUJacobian{R, V}(∂f_refs::AbstractArray{<:V}, ∂x_refs::AbstractArray{<:V}, updateFct!) where {R, V} - inst = new{R, V}() - inst.mtx = zeros(R, length(∂f_refs), length(∂x_refs)) - inst.b = zeros(R, length(∂f_refs)) - inst.c = zeros(R, length(∂x_refs)) - inst.valid = false - inst.∂f_refs = ∂f_refs - inst.∂x_refs = ∂x_refs - inst.updateFct! = updateFct! - - inst.numUpdates = 0 - inst.numJVPs = 0 - inst.numVJPs = 0 - - return inst - end -end - -function update!(jac::FMUJacobian{R, V}; ∂f_refs=jac.∂f_refs, ∂x_refs=jac.∂x_refs) where {R, V} - - if ∂f_refs != jac.∂f_refs || ∂x_refs != jac.∂x_refs - if size(jac.mtx) != (length(∂f_refs), length(∂x_refs)) - jac.mtx = zeros(R, length(∂f_refs), length(∂x_refs)) - end - - if size(jac.b) != (length(∂f_refs),) - jac.b = zeros(R, length(∂f_refs)) - end - - if size(jac.c) != (length(∂x_refs),) - jac.c = zeros(R, length(∂x_refs)) - end - - jac.valid = false - end - - if !jac.valid - jac.updateFct!(jac, ∂f_refs, ∂x_refs) - jac.numUpdates += 1 - jac.valid = true - end - return nothing +function check_invalidate!(vr, jac::Nothing) + return end -function invalidate!(jac::FMUJacobian) - jac.valid = false - return nothing +function invalidate!(jac::Nothing) + return end -function jvp!(jac::FMUJacobian, x; ∂f_refs=jac.∂f_refs, ∂x_refs=jac.∂x_refs) - update!(jac; ∂f_refs=∂f_refs, ∂x_refs=∂x_refs) - jac.b[:] = jac.mtx * x - jac.numJVPs += 1 - return jac.b +function uncolor!(jac::Nothing) + return end -function vjp!(jac::FMUJacobian, x; ∂f_refs=jac.∂f_refs, ∂x_refs=jac.∂x_refs) - update!(jac; ∂f_refs=∂f_refs, ∂x_refs=∂x_refs) - jac.c[:] = jac.mtx' * x - jac.numVJPs += 1 - return jac.c -end \ No newline at end of file diff --git a/src/logging.jl b/src/logging.jl index 3c467a0..d8bcc0a 100644 --- a/src/logging.jl +++ b/src/logging.jl @@ -1,51 +1,40 @@ # -# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons +# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher # Licensed under the MIT license. See LICENSE file in the project root for details. # -""" -Log levels for non-standard infos, warnings and errors. -""" -const FMULogLevel = Cuint -const FMULogLevelNone = Cuint(0) -const FMULogLevelInfo = Cuint(1) -const FMULogLevelWarn = Cuint(2) -const FMULogLevelError = Cuint(3) -export FMULogLevel, FMULogLevelNone, FMULogLevelInfo, FMULogLevelWarn, FMULogLevelError - -""" -Logs a message with level `info` if the log level allows it. -""" -function logInfo(fmu::FMU, message) - if fmu.logLevel <= FMULogLevelInfo - @info message +function logInfo(component::FMU2Component, message, status::fmi2Status=fmi2StatusOK) + if component.loggingOn == fmi2True + ccall(component.callbackFunctions.logger, + Cvoid, + (fmi2ComponentEnvironment, fmi2String, fmi2Status, fmi2String, fmi2String), + component.callbackFunctions.componentEnvironment, component.instanceName, status, "info", message * "\n") end end -export logInfo +function logInfo(::Nothing, message, status::fmi2Status=fmi2StatusOK) + @info "logInfo(nothing, $(message), $(status))" +end -""" -Logs a message with level `warn` if the log level allows it. -""" -function logWarning(fmu::FMU, message) - if fmu.logLevel <= FMULogLevelWarn - @warn message +function logWarning(component::FMU2Component, message, status::fmi2Status=fmi2StatusWarning) + if component.loggingOn == fmi2True + ccall(component.callbackFunctions.logger, + Cvoid, + (fmi2ComponentEnvironment, fmi2String, fmi2Status, fmi2String, fmi2String), + component.callbackFunctions.componentEnvironment, component.instanceName, status, "warning", message * "\n") end end -export logWarning - -function logWarn(fmu::FMU, message) - @warn "logWarn is deprecated, use logWarning." - logWarning(fmu, message) +function logWarning(::Nothing, message, status::fmi2Status=fmi2StatusOK) + @warn "logWarning(nothing, $(message), $(status))" end -export logWarn -""" -Logs a message with level `error` if the log level allows it. -""" -function logError(fmu::FMU, message) - if fmu.logLevel <= FMULogLevelError - @error message +function logError(component::FMU2Component, message, status::fmi2Status=fmi2StatusError) + if component.loggingOn == fmi2True + ccall(component.callbackFunctions.logger, + Cvoid, + (fmi2ComponentEnvironment, fmi2String, fmi2Status, fmi2String, fmi2String), + component.callbackFunctions.componentEnvironment, component.instanceName, status, "error", message * "\n") end end -export logError - +function logError(::Nothing, message, status::fmi2Status=fmi2StatusOK) + @error "logError(nothing, $(message), $(status))" +end \ No newline at end of file diff --git a/src/printing.jl b/src/printing.jl new file mode 100644 index 0000000..8e5fa8d --- /dev/null +++ b/src/printing.jl @@ -0,0 +1,45 @@ +# +# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons +# Licensed under the MIT license. See LICENSE file in the project root for details. +# + +""" +Log levels for non-standard printing of infos, warnings and errors. +""" +const FMULogLevel = Cuint +const FMULogLevelNone = Cuint(0) +const FMULogLevelInfo = Cuint(1) +const FMULogLevelWarn = Cuint(2) +const FMULogLevelError = Cuint(3) +export FMULogLevel, FMULogLevelNone, FMULogLevelInfo, FMULogLevelWarn, FMULogLevelError + +""" +Prints a message with level `info` if the log level allows it. +""" +function logInfo(fmu::FMU, message, maxlog=0) + if fmu.logLevel <= FMULogLevelInfo + @info message maxlog=maxlog + end +end +export logInfo + +""" +Prints a message with level `warn` if the log level allows it. +""" +function logWarning(fmu::FMU, message, maxlog=0) + if fmu.logLevel <= FMULogLevelWarn + @warn message maxlog=maxlog + end +end +export logWarning + +""" +Prints a message with level `error` if the log level allows it. +""" +function logError(fmu::FMU, message, maxlog=0) + if fmu.logLevel <= FMULogLevelError + @error message maxlog=maxlog + end +end +export logError + diff --git a/src/sense.jl b/src/sense.jl new file mode 100644 index 0000000..f699ea8 --- /dev/null +++ b/src/sense.jl @@ -0,0 +1,84 @@ +# +# Copyright (c) 2022 Tobias Thummerer, Lars Mikelsons +# Licensed under the MIT license. See LICENSE file in the project root for details. +# + +# this file containes placeholders for sensitivity (AD) related functions, +# if FMISensitivity is not loaded, these functions dispatch to neutral statements, +# if FMISensitivity is loaded, these functions are extended. + +# check if scalar/vector is ForwardDiff.Dual +function isdual(e) + return false +end +function isdual(e::Tuple) + return any(isdual.(e)) +end + +# check if scalar/vector is ForwardDiff.Dual +function istracked(e) + return false +end +function istracked(e::Tuple) + return any(istracked.(e)) +end + +# makes Reals from ForwardDiff.Dual scalar/vector +function undual(e::AbstractArray) + return undual.(e) +end +function undual(e::AbstractArray{fmi2Real}) + return e +end +function undual(e::Tuple) + if !isdual(e) + return e + end + return undual.(e) +end +function undual(::Nothing) + return nothing +end +function undual(e) + return e +end + +# makes Reals from ReverseDiff.TrackedXXX scalar/vector +function untrack(e::AbstractArray) + return untrack.(e) +end +function untrack(e::AbstractArray{fmi2Real}) + return e +end +function untrack(e::Tuple) + if !istracked(e) + return e + end + return untrack.(e) +end +function untrack(::Nothing) + return nothing +end +function untrack(e) + return e +end + +# makes Reals from ForwardDiff/ReverseDiff.TrackedXXX scalar/vector +function unsense(e::AbstractArray) + return unsense.(e) +end +function unsense(e::AbstractArray{fmi2Real}) + return e +end +function unsense(e::Tuple) + if !isdual(e) && !istracked(e) + return e + end + return unsense.(e) +end +function unsense(::Nothing) + return nothing +end +function unsense(e) + return e +end diff --git a/src/types.jl b/src/types.jl new file mode 100644 index 0000000..86f39e7 --- /dev/null +++ b/src/types.jl @@ -0,0 +1,40 @@ +# +# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher +# Licensed under the MIT license. See LICENSE file in the project root for details. +# + +""" + + FMU + +The abstract type for FMUs (FMI 2 & 3). +""" +abstract type FMU end +export FMU + +""" + + FMUSolution + +The abstract type for a solution generated by simulating an FMU (FMI 2 & 3). +""" +abstract type FMUSolution end +export FMUSolution + +""" + + FMUExecutionConfiguration + +The abstract type for the configuration used to simulate (execute) an FMU (FMI 2 & 3). +""" +abstract type FMUExecutionConfiguration end +export FMUExecutionConfiguration + +""" + + FMUEvent + +The abstract type for an event triggered by a FMU (FMI 2 & 3). +""" +abstract type FMUEvent end +export FMUEvent \ No newline at end of file