The fmu_tools
library offers a set of utilities to import/export any existing model from/into an FMU (version 2.0 or 3.0).
For the FMU export, the library, mainly through its main class FmuComponentBase
, provides a higher C++ layer that:
The CMake infrastructure takes care of:
For the FMU import, the library can dynamically link to any FMU and offers some auxiliary function to easily access internal variables by name, recognizes Modelica visualization shape structures by parsing the modelDescription.xml.
WARNING: please mind that we are currently dealing only with CoSimulation FMUs.
A simple use of the library consists in just taking the project and modifying myFmuComponent.h and myFmuComponent.cpp based on the user needs. This is the recommended way for simpler projects.
Those who like to use fmu_tools
directly from their own projects are invited to look at the Chrono library to have an idea on a more advanced integration. Especially the chrono/template_project_fmu folder.
In either case, this library offers a set of tools that need to be further specialized for the user-specific problem. This is done by inheriting from FmuComponentBase
, plus some additional customization. One example is provided by the myFmuComponent
class. The impatient user could also just copy and modify this class straight away.
For those that want to develop their own indipendent class, they are required to:
FMU_MODEL_IDENTIFIER
to any valid name (consider your operating system and C-function naming standards); a new target with same name will be created;FmuComponentBase
; please refer to myFmuComponent
for an example;FmuComponentBase::instantiateType(_fmuType)
;AddFmuVariable
; various measurement units are supported and some default units are already declared; please also remember that all the variables exposed by the FMU must be updated at every _doStep
call; it's up to the user to register any updating functions to the list of m_[pre|post]StepCallbacks
; these will be called immediately before and after the simulation step;time
variable comes pre-binded to the FMU: remember to update it as well;FmuComponentBase::is_cosimulation_available()
and FmuComponentBase::is_modelexchange_available()
so that they would return the proper answer;_doStep
method of the base class with the problem-specific implementation;FmuComponentBase::_enterInitializationMode()
and FmuComponentBase::_exitInitializationMode()
: between these two calls the user should be able to set the parameters of the simulation; after the call to _exitInitializationMode
the model should be ready to run;FmuComponentBase::_preModelDescriptionExport()
and FmuComponentBase::_postModelDescriptionExport()
: to generate the modelDescription.xml file the class gets constructed, but not initialized; if some setup (e.g. calling _exitInitializationMode
) is required to populate the model with all the proper variables this could be done in these methods.fmi2Instantiate_getPointer
similarly to:
FmuComponentBase* fmi2Instantiate_getPointer(
fmi2String instanceName,
fmi2Type fmuType,
fmi2String fmuGUID)
{
return new myFmuComponent(instanceName, fmuType, fmuGUID);
}
Adding variables to the FMU is possible in two different flavours: by just passing the address of any given variable whose type is directly supported by the FMU interface or by providing a pair of getter/setter methods. Since the definition of these function pairs might not be immediate an helper macro MAKE_GETSET_PAIR
is offered.
When everything is set up, build the PACK_FMU target to generate the FMU file.
UnitDefinitionType
class, that stores the name of the unit (e.g. "rad/s2") together with the exponents of each SI base unit (e.g. rad=1, s=-2). The user should create its own object of type UnitDefinitionType
and then pass it to FmuComponentBase
through its method addUnitDefinition
. After this step, the user can use the unit name in any following call to AddFmuVariable
;RUN_TESTS
target will run the fmuChecker utility over the newly created fmu and makes sure no error is returned (only 'win64' and 'linux64' are available for now);createModelDescription()
). This function actually instantiates an object of the class inherited from FmuComponentBase
and then calls its FmuComponentBase::ExportModelDescription
method. As you may see, no initialization is made on the class/fmu. However, this might be a problem for some models that have no valid default parameters. In order to overcome this limitation, the FmuComponentBase::ExportModelDescription
method calls the pair of FmuComponentBase::_preModelDescriptionExport()
and FmuComponentBase::_postModelDescriptionExport()
.The target fmu_host
shows how to load and run FMUs. By default it is set up to use the FMU generated by the FMU export target, but it can be redirected to any other FMU.
The FMUs gets automatically unzipped.
file
scheme)fmu_tools
into other projectThe creation of an FMU requires few steps that are automatically carried on by CMake. In order to reuse the code in your own toolchain we are currently suggesting to use CMake FetchContent feature. In this way the targets gets correctly imported in your project togetheir with their properties.
In order to better grasp what is happening under the hood it is important to understand both the target/command dependencies and the order in which they are triggered.
With the various blocks below following the convention:
flowchart TB
TARGET_EXAMPLE["This is a CMake target"]
CUSTOM_COMMAND_EXAMPLE[["This is a CMake custom command"]]
OUTPUT_EXAMPLE[/"This is a build or command output"/]
the dependencies can be depicted as:
flowchart TB
subgraph DEPENDENCIES_target["Dependencies and resources"]
direction TB
COPY_LIB[["`Copy dependencies (FMU_DEPENDENCIES) into FMU_RUNTIME_OUTPUT_DIRECTORY`"]]
COPY_RESOURCES[["`Copy FMU_RESOURCES_DIRECTORY into 'FMU_DIRECTORY/resources'`"]]
end
subgraph FMU_MODEL_IDENTIFIER_target[Build FMU source]
direction TB
FMU_MODEL_IDENTIFIER-->FMU_MI_OUTPUT[/"`Build 'FMU_MODEL_IDENTIFIER' binaries in FMU_RUNTIME_OUTPUT_DIRECTORY=FMU_DIRECTORY/bin/_os_/`"/]
end
subgraph modelDescriptionGen[Create modelDescription.xml]
direction TB
fmi_modeldescription_build["fmi_modeldescription"]-->fmi_modeldescription_output[/"fmi_modeldescription.exe"/]
fmi_modeldescription[["Execute 'fmi_modeldescription.exe FMU_MODEL_IDENTIFIER'"]]-->MODEL_DESCRIPTION[/"Create 'modelDescription.xml' in FMU_DIRECTORY"/]
fmi_modeldescription_output -.-> fmi_modeldescription
end
subgraph zip_procedure["Create FMU file"]
direction TB
ZIP[["Execute 'tar --format=zip FMU_DIRECTORY'"]]-->FMU_MODEL_IDENTIFIER_ZIP[/"Create 'FMU_MODEL_IDENTIFIER.fmu' in FMU_DIRECTORY"/]
end
FMU_MI_OUTPUT --> ZIP
MODEL_DESCRIPTION --> ZIP
COPY_LIB --> ZIP
COPY_RESOURCES --> ZIP
FMU_MODEL_IDENTIFIER -.->|POST_BUILD| fmi_modeldescription
FMU_MODEL_IDENTIFIER -.->|POST_BUILD| ZIP
FMU_MODEL_IDENTIFIER -.->|PRE_BUILD| COPY_LIB
FMU_MODEL_IDENTIFIER -.->|POST_BUILD| COPY_RESOURCES
The above layout triggers the various commands/builds in the following order:
flowchart TB
subgraph FMU_MODEL_IDENTIFIER_target[ ]
direction TB
FMU_MODEL_IDENTIFIER["<b>TARGET:</b> FMU_MODEL_IDENTIFIER"]-->FMU_MI_OUTPUT[/"Build 'FMU_MODEL_IDENTIFIER' binaries in 'FMU_RUNTIME_OUTPUT_DIRECTORY'"/]
end
subgraph modelDescGenBuild[ ]
direction TB
fmi_modeldescription_build["<b>TARGET:</b> fmi_modeldescription"]-->fmi_modeldescription_output[/"Build 'fmi_modeldescription.exe'"/]
end
subgraph modelDescription[ ]
direction TB
fmi_modeldescription[["<b>COMMAND:</b> Execute 'fmi_modeldescription.exe FMU_MODEL_IDENTIFIER'"]]-->MODEL_DESCRIPTION[/"Create 'modelDescription.xml'"/]
end
subgraph dependenciesCopy[ ]
direction TB
dependencies_copy[["<b>COMMAND:</b> Copy 'FMU_DEPENDENCIES' into 'FMU_RUNTIME_OUTPUT_DIRECTORY'"]]
end
subgraph resourcesFolder[ ]
direction TB
resources_folder[["<b>COMMAND:</b> Copy 'FMU_RESOURCES' into 'FMU_DIRECTORY/resources'"]]
end
subgraph zip_procedure[ ]
direction TB
ZIP[["<b>COMMAND:</b> Execute 'tar --format=zip FMU_DIRECTORY'"]]-->FMU_MODEL_IDENTIFIER_ZIP[/"Create 'FMU_MODEL_IDENTIFIER.fmu' ZIP archive"/]
end
fmi_modeldescription_output --> dependenciesCopy
dependenciesCopy --> FMU_MODEL_IDENTIFIER
FMU_MI_OUTPUT --> fmi_modeldescription
MODEL_DESCRIPTION -->resourcesFolder
resourcesFolder --> ZIP