Functional Mock-up Interface

FMI logo Functional Mock-up Interface (FMI) is a tool-independent standard that enables both model exchange and co-simulation of dynamical models. It is designed to enable export of models from simulation tools so they can be imported into others, and to couple different simulation tools in one simulation. FMI is a completely free and open standard*), and the list of tools that support it is long, and growing.

This article is intended for people who create “hand-made” models (as opposed to exporting automatically generated models from some modelling tool), or who simply wish to gain an understanding of how FMI works. However, it only gives an overview of the standard from a practical point of view, and is not a complete reference. For that, we refer readers to the FMI web site and more specifically to the standard itself. (Unlike most standards documents, the FMI specification is actually quite readable, and provides a lot of useful, practical knowledge about modelling and simulation.)

There are two versions of FMI: Version 1.0, which was published in 2010, and version 2.0, which was released in 2014 and contains several improvements over the former. Version 2.0 is not backwards compatible with FMI 1.0, but upcoming versions are expected to be compatible with v2.0. In this article, we try to keep the discussion at a level where it applies to both versions, but FMI 2.0 will be used in examples.

Under FMI, models are packaged and distributed as self-contained units called functional mock-up units (FMUs). An FMU is simply a ZIP archive file that, as a minimum, contains the model description and model code. The code is usually in binary form, as dynamic libraries for one or more target platforms, but may also be in source form. In addition, the FMU can contain documentation and resources (e.g. data files). An FMU has a .fmu extension instead of .zip.

Here is an example of what the directory structure inside an FMU could look like:

modelDescription.xml         // Detailed information about the FMU
model.png                    // Model icon (optional)
binaries/
    win32/                   // Target platforms are specified as: <os><architecture>
        foo.dll              // Model code, must export the FMI-specified functions
        ...                  // Any other libraries
    linux64/
        foo.so
        ...
    ...
sources/
    ...                      // Entire model source code, except standard FMI headers
documentation/
    index.html               // Entry point for model documentation
    ...                      // Any other documentation files
resources/
    ...                      // Resources, e.g. data files, typically platform independent

FMI specifies the above directory structure, the format of the model description XML file, and the signature of the functions that must be present in the dynamic libraries that contain the model code.

Model description

The modelDescription.xml file contains detailed information about the FMU, and is read by the importing tool to learn about the properties and capabilities of the model. Among the most important things are:

  • Metadata about the model, such as its name and description, author and model version.
  • Whether the FMU supports model exchange, co-simulation, or both.
  • A globally unique identifier (GUID), used to ensure that the model description and the model code are in sync.
  • Model properties and capabilities, such as whether it requires an external tool to run, whether it supports (re)storing its state, whether it provides derivatives, etc.
  • Information about variables, including their names, categories, types, start values and more.

Here is an example of a simple model description file for a co-simulation subsimulator with one parameter, one input variable and one output variable:

<fmiModelDescription
    fmiVersion="2.0"
    modelName="com.acme.exploding_device"
    guid="4688079e-0f73-4659-a551-6ebb802c41cf"
    description="A device that explodes when The Road Runner comes near it"
    author="Wile E. Coyote"
    version="391.13.2">
  <CoSimulation modelIdentifier="exploding_device_cosim" canHandleVariableStepSize="true" />
  <ModelVariables>
    <ScalarVariable name="activated" valueReference="0"
        description="Whether the device is activated"
        causality="parameter" variability="tunable">
      <Boolean start="false" />
    </ScalarVariable>
    <ScalarVariable name="distance" valueReference="0"
        description="Distance between device and Road Runner"
        causality="input" variability="discrete">
      <Real start="1000.0" unit="m" />
    </ScalarVariable>
    <ScalarVariable name="power" valueReference="1"
        description="Explosive energy released per unit time"
        causality="output" variability="continuous">
      <Real start="0.0" unit="W" />
    </ScalarVariable>
  </ModelVariables>
</fmiModelDescription>

Model code

While an FMU can contain model code both in source code form and in object code form (static libraries), the most common solution is to use shared libraries (also called “shared objects” or “dynamic-link libraries”). Then, the FMU must include shared libraries for each of the platforms it is supposed to support. “Platform” here includes a combination of software and hardware platform, such as “win32” or “linux64”.

The shared libraries must expose a number of functions, the names and signatures of which are specified by FMI. Some examples from FMI 2.0:

// Get the values of one or more real variables
fmi2Status fmi2GetReal (
    fmi2Component c,
    const fmi2ValueReference vr[],
    size_t nvr,
    fmi2Real value[]);
 
// Set the values of one or more integer variables
fmi2Status fmi2SetInteger(
    fmi2Component c,
    const fmi2ValueReference vr[],
    size_t nvr,
    const fmi2Integer value[]);
 
// Perform a co-simulation time step
fmi2Status fmi2DoStep(
    fmi2Component c,
    fmi2Real currentCommunicationPoint,
    fmi2Real communicationStepSize,
    fmi2Boolean noSetFMUStatePriorToCurrentPoint);

These functions, along with several others, must be implemented by the FMU author, as FMI provides no default implementations for any of them.

The following is a list of tips and “best practices” for FMU authors, based on practical experience.

General

Build self-contained FMUs

If an FMU has no external dependencies, sharing and deployment is trivial: Just send or upload the file. If the FMU requires some other software to be pre-installed on the computer where it will be used, it's more of a hassle. Even more cumbersome is if it requires other software to actually run in parallel with it. Therefore, it is good advice to bundle all dependencies inside the FMU. Shared library dependencies should be placed in the platform-specific directories under binaries/ in the FMU.

Of course, there is usually a trade-off involved, and there may be good reasons to not bundle dependencies. An example is if the dependencies are very large, or if their licence does not permit it.

Use the FMU Compliance Checker

From the FMI downloads page:

The FMU Compliance Checker is a free software provided by the Modelica Association, implemented by Modelon AB to check a given FMU's compliance with the FMI standard. […] A wide and increasing range of problems are checked for and reported to the user. This is an excellent tool for developers creating FMUs or creating FMU export functionality in their tool.

Enough said; just do it.

Model description

Auto-generate the GUID

The purpose of the guid attribute of the <fmiModelDescription> element is to ensure that the model description and the model code are compatible and that they stay in sync. The GUID is read from the XML file and passed to the fmi2Instantantiate() function, which is supposed to report an error if the GUID does not match the one stored in the code.

FMI recommends that the GUID is generated as a “fingerprint of the relevant information” in the XML file (though it does not specify how). This is a very good recommendation, for at least two reasons:

Firstly, in “hand-made” FMUs, unless you are very careful, the model description and the model code could very easily get out of sync. This especially holds for value references, which are just plain numbers that it can be difficult to keep track of when variables are added or removed. The GUID can then serve as a reminder that changes to the model description should normally be reflected in changes to the code.

Secondly, the co-simulation software may assume that the GUID does actually uniquely identify a particular version of a particular FMU. (The FMU name and version number are not reliable for this purpose; how many different FMUs called “spring”, “mass” or “damper” do you think exist?) This could be used for caching of FMU contents: If an FMU's GUID has not changed, there is no need to unzip the whole thing again, just use a previously unpacked one. (Yes, this matters sometimes. Some FMUs are huge, and actually take quite a bit of time to unpack.)

Use UUIDs for GUIDs

While GUID, or “globally unique identifier”, could mean a whole lot of different things, Universally Unique Identifier (UUID) is actually defined by a standard. And it is defined in such a way that, if used properly, it is extremely unlikely for two different UUIDs to collide. There are different variants, and especially the ones based on MD5 and SHA-1 hashing are useful in this context, as they can be directly used to make a fingerprint of the model description.

Be careful with value references

Value references are numbers that link the information about each variable in the model description to actual variable values passed to the fmi2SetXxx() functions and returned by the fmi2GetXxx() functions in the model code. Value references have some properties that may not be immediately obvious:

  • It is possible for several variables that have the same data type (real, integer, boolean or string) to have the same value reference. They will then all be aliases of the same variable.
  • It is also possible for several variables of different data types to have the same value references. These will then be different variables. (I.e., it is not possible for two variables with different data types to be aliases of each other.)

In terms of model code, a common implementation is to have one array for each data type, and let the value references be indexes into each of those arrays. While this is simple to implement, and in most cases the most efficient implementation, it has its drawbacks. It creates a very intimate connection between the model description and the model code, which depending on the circumstances and the programming language, could easily lead to wrong values being read and written, crashes, or, worst of all, silent data corruption due to buffer overruns.


*) The FMI specification document itself is licensed under the CC-BY-SA (Creative Commons Attribution-Sharealike 4.0 International) licence, which is the license used by Wikipedia. The C header files are licensed under a modified 2-clause BSD licence.