Modelica Association logo FMI logo prostep ivip logo

The FMI 3.0 Implementers' Guide is a free resource intended to give non-normative recommendations and guidance to implementers of the Functional Mock-up Interface standard version 3.0. All guidance is based on FMI 3.0 unless otherwise noted or applicable. This document is a joint publication by the Modelica Association Project FMI and the prostep ivip SmartSE project. It is continually revised based on implementer and user feedback and input. All of the content is to be considered non-normative and shall not be considered to supplant any normative statement in the FMI 3.0 standard, or any other standard or layered standard. Releases and issues can be found on github.com/modelica/fmi-guides.


Copyright © 2016-2020 prostep ivip project SmartSE and 2021-2023 The Modelica Association Project FMI.

This document is licensed under the Attribution-ShareAlike 4.0 International license. The code is released under the 2-Clause BSD License. The licenses text can be found in the LICENSE.txt file that accompanies this distribution.

Attention is drawn to the possibility that some of the elements of this document may be the subject of patent rights. Modelica Association and prostep ivip e.V. shall not be held responsible for identifying such patent rights.

1. Introduction

The Functional Mock-up Interface (FMI) has gained widespread acceptance in industrial usage both at the level of users and simulation tool vendors as a mechanism for the interchange of models for integration into other environments. During the initial adoption of FMI, several problems and omissions that have hindered the realization of the full potential of FMI adoption have been identified. Many of these issues have resulted in clarifications and enhancements in the FMI 3.0 standard. However, other considerations are less normative or require more deliberated guidance, so inclusion in the standard itself was deemed unwise.

This document provides best practice recommendations to implementers of FMI, focusing on FMI 3.0, derived from the industrial experience of the Smart SE project members and the MAP FMI community in employing FMI. The overarching goal of the recommendations is to aid interoperability of FMI implementations and ensure good ease-of-use for the user in employing FMI.

This document is intended for simulation tool vendors planning to support FMI in their products, either for importing FMUs or for exporting FMUs, or both. It assumes a technical audience familiar with the specification documents and tries to give further information and hints in areas where either the relevant specification documents have been found sometimes to be less clear than expected or where user expectations are relevant to implementation choices. It also considers errors and bugs encountered in the practical use of existing FMI implementations and guides to avoid common pitfalls.

This document will be maintained and expanded over time as new issues or needs for clarification appear.

Since interoperability of implementations is of crucial concern for FMI users, guidance is, where applicable, based on the general robustness principle (also often termed Postel’s law):

Be conservative in what you do, be liberal in what you accept from others.

Note that the guidance in this document does in no way supplant the standards themselves: What is standard-compliant or not is the sole domain of the official standard documents.

This guidance only expands on the content of the standards by giving advice on how to best deal with design decisions and the realities of diverging or not fully standard-compliant implementations: Even if erroneous implementations are fixed at a later date, they will affect users in the meantime, and with generated FMUs, probably a long time after the exporters are fixed if the FMUs are not regenerated regularly.

2. General Considerations

2.1. Versions of FMI to support

When planning on which versions of FMI to support in your implementation, support for the newest FMI version (currently FMI 3.0) is, of course, highly recommended.

It should be noted that there is no cross-version compatibility between major versions of FMI.

The life cycles of both simulation environments and generated FMUs can wildly differ and span many years in development projects. Therefore support for both legacy FMU integration and generation of legacy FMUs is crucial for flexible interoperation in development projects.

Currently, this means that support for the very widely supported FMI version 2.0 should be strongly considered. Similarly, support for the integration of FMUs using different FMI versions should also be strongly considered.

For FMI 1.0/2.0 and 3.0 interoperability the bridging between the different variable types of 1.0/2.0 and 3.0 and the handling of arrays needs to be taken into account. In projects that employ the SSP standard, the variant handling facilities of SSP can be used to package multiple versions of an FMU into one package.

When bugfix releases of the FMI standard become available, implementations should take these into account in a timely fashion to react to detected clarifications in the standard.

2.2. Support for Interface Types

Generally, implementations should support all applicable FMI interface types: For FMI 3.0, this includes the Model Exchange, Co-Simulation, and Scheduled Execution interface types.

There are inherent trade-offs in the properties of interface types in terms of complexity, numerical accuracy, performance, ease-of-use, and reproducibility of results. For this reason, often not just one interface type is appropriate for the export of a given FMU.

FMI provides the ability to package multiple interface types in one FMU, allowing importers and users to select an appropriate interface type at the point of use. This also enables importers to fall back to a different interface type if an interface type is not supported or is not beneficial for the current use case.

Especially for a more specialized or complex interface type, like Scheduled Execution, which is only relevant in some use cases, implementing other interface types as a fallback is recommended.

There are, of course, cases where the generation of only certain interface types makes sense: For example, for FMUs generated from ECU software, the model exchange interface type is likely not sensible or possible to generate.

Importing implementations should usually support all interface types. Mixing FMUs with different interface types in one model or project should also be supported unless technical considerations make this impossible or not useful.

2.3. Support for Optional Features

The FMI standard provides many optional features. These features are optional since support is not uniformly required for all use cases. However, for certain use cases, some optional features are often critically important. Therefore broad support for optional features is required to achieve acceptable simulation performance for a broad range of use cases.

The Modelica Association Project FMI and the prostep ivip SmartSE project provide a joint publication on FMI Feature Profiles, which gives use-case-based guidance on required and recommended optional features. The goal of this document is a more straightforward agreement between tool vendors and users on the required features for specific use cases.

This document can be found on the FMI standard homepage at https://fmi-standard.org/ and should be taken into account, among other considerations, when defining feature support for implementations.

2.4. Testing

All implementers are advised to participate widely in the FMI Cross-Checking Initiative to test compatibility with current implementations of other tools. Robust participation, including testing with a relevant selection of available releases, is recommended.

FMU exporting implementations should also use the FMU Compliance Checker [FMUCC] and VDMCheck [VDMCheck] to do basic validation of generated FMUs.

FMU importing implementations should make use of the Reference FMUs [RefFMUs] to do basic validation of FMU import.

Implementers should test FMU integration with complex test cases, including the behavior of multiple connected FMUs with actual and pseudo algebraic loops, iteration during initialization, handling of structured naming, handling of Unicode characters inside and outside the basic multilingual plane, license handling, etc.

2.5. Licensing and License Handling

If generated FMUs require licenses to run (FMU runtime licenses), the mechanism needed to supply these licenses to the FMU needs to be documented, ideally included in the FMU documentation.

The license handling should be tested widely as part of cross-implementation checks to ensure that the license handling works correctly in varied circumstances.

If an error or problem occurs in the license handling, e.g., a missing license, an informative message should be provided to the user through the FMU logging callback mechanism. This allows importing implementations to provide proper feedback to the user.

Exchange of FMUs can be eased tremendously if an option exists to generate FMUs that do not require license mechanisms at the receiving party.

If license mechanisms are used for IP protection, alternative approaches can be useful to consider (see, for example, Part D IP Protection of the prostep ivip SmartSE Recommendation 2022 PSI 11 V3.0 [SMARTSEV3]).

3. Packaging and Code Generation/Loading

3.1. ZIP Archive Structure

3.1.1. Pathnames and Prefixes

Exporting implementations should ensure that the generated FMU ZIP archives are compliant with the ZIP specification. This implies that only the forward slash / shall be used as a path separator and that path specifications like . or .. do not have specific interpretations, like the current directory, etc.

More specifically, the ZIP archive should only contain entries with no leading prefix: For example, valid entries would be modelDescription.xml or binaries/x86_64-windows/myfmu.dll, not ./modelDescription.xml, or myfmu/binaries/x86_64-windows/myfmu.dll.

To enable interoperation with not-quite conforming implementations, importing implementations should be prepared to support the extraction of FMU ZIP archives with leading ./ prefixes on entries in the archive. Similarly, implementations should be prepared to support the extraction of FMU ZIP archives that use "\" as the path separator.

3.1.2. Access to FMU package content

The exporting implementation should not rely on the importing implementation completely unpacking the FMU in one location. The standard only specifies access to the resources folder.

Some of the fallbacks (discussed in Section 3.7 and Section 5.1) for locating the resources folder relative to the executing DLL/shared object rely on the structure being completely recreated. However, these fallbacks are, by their nature non-portable mechanisms, that only come into play when other, standard-conforming mechanisms are not available.

When possible, implementations should unpack the complete ZIP archive content in one location since some FMUs depend on this even if the standard only specifies access to the resources folder.

Note that FMI 3.0 provides access to the resource folder using a native path, whereas FMI 1.0 and FMI 2.0 specify a URI to the resource folder that still needs to be translated to a system path for access.

3.2. Documentation and Additional Resources

3.2.1. Icons

Implementations should support using supplied icon information in the FMU when displaying the FMU in a graphical notation or user interface.

For FMI 3.0, icon information is available as part of the ports and icons information contained in the icons/terminalsAndIcons.xml file of the FMU. This optionally includes detailed icon information for individual ports, as well as global icon information.

For prior versions of FMI, only a model.png file in PNG format in the root of the ZIP archive is available.

Exporting implementations should, at a minimum, generate an informative icon for the FMU and package this in the ZIP archive in the way documented in the FMI standards. Whenever possible, more detailed information should be provided using the ports and icons information.

3.2.2. Meta Data

Exporting implementations should make use of the standard meta data attributes FMI provides in the root element of the modelDescription.xml:

  • description
    A user-readable description of the FMU/model at a suitable level of detail for a short overview. Note that additional information can be provided in the Section 3.2.3.

  • author
    Author information should ideally include organization and contact information, e.g. in the [RFC5322] name-addr format:
    Example User <user@example.com>
    Note that privacy regulations might necessitate the use of author information that is not personally identifiable, e.g. company or department names and addresses instead of individual users.

  • version
    This field provides a version for the FMU/model, which is not to be confused with the FMI version field fmiVersion.

  • copyright
    Copyright information in a suitably short form. Note that additional information can be provided in the Section 3.2.3.

  • license
    License information in a suitably short form. Note that additional information can be provided in the Section 3.2.3.

  • generationTool
    Description, including version number, on the tool generating the FMU.

  • generationDateAndTime
    Date and time when the FMU was generated. Note the specific [ISO8601-1]-derived format required here, and the prescribed use of UTC (Zulu time zone):
    2009-12-08T14:33:22Z

While some of the fields can be filled in a fully automated fashion, for most user input is needed. Generally the information to be included in generated FMUs should be fully under user control.

Importing implementations should make the meta data found in imported FMUs available to the user for inspection.

Note that not all of the meta data fields are available in FMI 1.0.

3.2.3. Documentation

The documentation folder of FMUs provides a specific way to ensure that important documentation needed to use FMUs effectively always travel with the FMU. Implementations should therefore allow easy access to any documentation supplied in the FMU.

Exporting implementations should allow the user to easily author/add documentation for the FMU.

Exporting implementations should also include any additional documentation needed to effectively run the FMU in the documentation:

  • Implementations which export FMUs that are not considered stand-alone, i.e., specifying needsExecutionTool=true in their model description, should provide sufficient information inside the documentation so that the recipient of the FMU will know what tools, licenses, and other dependencies are needed to run the FMU.

  • Implementations which export FMUs that require runtime licenses should provide information on what kind of license is needed and how to provide this license so the FMU can use it.

In many instances, for example, when open-source licenses are employed, license information and other additional information is needed to be shipped with an FMU for distribution. In these cases, exporting implementations should allow the user to easily add information items, like licenses, to the documentation shipped with an FMU.

Where the license information is required by the exporting implementation itself, it can directly add this information to the FMU itself.

This support by an implementation should not be understood to remove the need for the user to ensure that they are in compliance with relevant licenses and their legal obligations.

3.3. Support for 32&64bit Variants of Platforms

Common platforms in use today are available in 32bit and 64bit variants, for example Windows or Linux on Intel processors, or Linux ARM processors.

While these variants are usually closely related, and 64bit variants commonly support running 32bit and 64bit binaries side-by-side, this support is limited: Most platforms do not support running 32bit and 64bit inside one process image, but only in separate processes.

Since FMI is designed around the ability to run FMUs inside the host implementation process image for efficiency, this means that 32bit and 64bit variants of the same platform are usually not directly compatible.

For this reason exporting implementations should ideally generate FMUs that contain both 32bit and 64bit variants of the binaries, allowing importers to select an appropriate binary.

Since not all exporting implementations can generate both variants easily, importing implementations should consider providing the ability to bridge between 32bit and 64bit implementations, using either inter-process communication or system provided facilities where possible.

Going forward, this is likely to be less of a problem on desktop systems, which are migrating to 64bit only implementation spaces. For mobile or embedded platforms, this issue is likely to be still pertinent for some time.

3.4. Support for Multiple Platforms

FMU exporting implementations should strive to support common platforms out of the box. They should provide support for generating FMUs that contain multiple binary implementations, and where feasible, a source code implementation in one go.

3.5. Compiler Dependencies

Exporting implementations should document supported compilers, their versions, and required compiler flags and settings to compile generated source FMUs for common platforms.

For FMI 3.0, the compilers and required compiler flags and settings should be placed in the buildDescription.xml file of the FMU to allow automated building from source as far as possible.

When generating binary FMUs directly, the supported and used compilers, compiler flags, and settings should also be documented. This allows users to troubleshoot integration issues when integrating binary FMUs into other environments.

Where possible, user-supplied compilers, compiler flags, and settings should be supported to automate the generation of binary FMUs for non-common targets or to support user-specific requirements in the binary FMU compilation/linking stage.

3.6. Handling of FMU namespaces

Importing implementations should correctly handle the import of multiple FMI 1.0 FMUs with identical model identifiers. In the case of FMUs with dynamic library implementations, this is supported by most platforms since symbol lookup can be performed scoped to one dynamic library namespace (see, for example, the dlsym and GetProcAddr functions for Linux and Windows, respectively).

Starting with FMI 2.0, FMUs with dynamic library implementations will always use identical symbols for the entry points in any case, so that this has to be supported correctly.

For source code or static library implementations, the problem of conflicting model identifiers can usually only be solved through compilation/linking to corresponding separate dynamic libraries or other similar mechanisms to deal with the relevant scoping issues.

For source code FMUs in FMI 3.0, the actual prefix that is used for the entry points is controlled through the mechanisms of the fmi3Functions.h header in the following way:

  • The source code of the FMU predefines the FMI3_FUNCTION_PREFIX preprocessor macro with the function prefix that matches the modelIdentifier attribute given in the modelDescription.xml. This is done before including the fmi3Functions.h header.

  • If the fmi3Functions.h header supplied with the standard is used for compilation, then this predefined prefix will be used as the function prefix, unless the FMI3_OVERRIDE_FUNCTION_PREFIX preprocessor macro is defined. If this macro is defined, then the actual function prefix will be taken from the preprocessor macro FMI3_ACTUAL_FUNCTION_PREFIX, if it is defined, or no prefix will be used, if it is undefined.

  • If an importing implementation requires something different, it can provide a fmi3Functions.h header file that does whatever the implementation requires, ignoring or using the predefined FMI3_FUNCTION_PREFIX macro as it sees fit.

As the actual function symbols generated for an FMU are dependent on the implementation and its supplied fmi3Functions.h header any any other predefined preprocessor macros, FMU exporters must ensure that they only refer to the FMI API entry-points via the names generated from the fmi3Functions.h mechanisms. In other words, any references in the exported source code should be amenable to preprocessor expansion.

For FMI 2.0 source code FMUs a similar approach can be used, however there are fewer guarantees on pre-defined preprocessor macros, since this area was only clarified in later patch releases of 2.0.

3.7. Handling of Code Dependencies

Importing implementations should consider changing the working directory of the process to the relevant binary subdirectory of the unpacked FMU when loading the FMU dynamic library. This is to allow unsophisticated exporting implementations to load dependent dynamic libraries relative to this directory. It is, of course, the responsibility of the FMU to implement proper dependent dynamic library loading regardless of the current working directory of the process. However, in practice, a number of current or former implementations did not correctly implement this. They can thus fail to load when the current directory is not the directory that contains the FMU dynamic library.

Exporting implementations should ideally avoid reliance on additional dynamic libraries: Generated dynamic libraries should ideally be stand-alone.

Where this is not feasible, implementations should prefer to use manual dynamic loading of dependent libraries at runtime (for example, using dlopen or LoadLibrary). The load path of the libraries should be based on the path to the resources folder provided. When the resources path is not available (for example, in FMI 1.0 ME) or not valid, an implementation can use dynamic library-relative path derivation, either against the binary folder or the resources folder.

Relying on pre-linking, where the dynamic loading of the dependent libraries is automatically handled by the platform dynamic linker/loader, is not likely to work in all cases: For example, on Windows, the searched paths will be based on the location of the importer executable, not the location of the FMU DLL. Furthermore, in case of failure, automatic linking is unlikely to provide user-understandable error messages.

Note that simple calls to LoadLibrary or LoadLibraryEx on Windows, without specifying the full path to the library are also not going to work in general, for the same reasons: The search path will be based on the location of the importer executable and not the FMU DLL.

Importers should consider using flags like RTLD_LOCAL on POSIX platforms with dlopen, to avoid symbols from loaded shared objects being resolved against other loaded shared objects (i.e. other FMUs) by accident.

Note that for POSIX platforms, like Linux, dlclose is not required to unload a shared object, but it can, as specified in IEEE Std 1003.1-2017 (and earlier). In particular, shared objects compiled with GCC without the flag -fno-gnu-unique, can be marked as unloadable, and thus will not generally be unloaded upon dlclose. In many instances shared objects loaded with RTLD_GLOBAL cannot unload until all relocations of other objects to symbols in the shared object have been unloaded.

3.8. Interaction between FMU and Importer

The FMU code will run in the process environment that the importer provides. This might be the same process as the importer, or it might be a separate process or processes. The FMU code cannot make any assumptions on this, but must rather be conservative in its behavior in the face of this.

3.8.1. Global State

The functions the FMU provides must not change global settings which affect the overall process and/or thread environment:

For example, an FMU function must not change the current working directory of the process, since this is by definition visible outside of the current execution thread.

FMU funtions can, on the other hand, change the floating point control word of the CPU during their execution, since this is directly tied to the thread of execution. However they must restore the floating point control word before returning, so as to not affect state outside of the current thread of execution.

4. Parameter and Variable Handling

4.1. Support for Physical Units

Importing implementations should support the processing of physical unit information when present on the variables of an imported FMU, including support for unit consistency checks and optional automatic unit conversions, especially when FMI 2.0/3.0 SI-based unit information is available.

The unit information should be overridable by the user to account for exceptional circumstances: For example, when erroneous unit information on a variable has to be manually adjusted since changes to the FMU are not otherwise practicable.

Exporting implementations should generate physical unit information on all exported variables when such information is available in the model (or can be automatically derived).

On FMI 2.0 and later versions, the support for SI-based units, using the BaseUnit element should be used. This enables a number of important use cases:

Unit check when connecting variables of different FMUs

When only one of an input variable v2 and an output variable v1, connected with equation v2 = v1, defines a BaseUnit element, or neither does, the connection equation v2 = v1 holds.

When two variables v1 and v2 (of the same or different FMUs) are connected and for both of them BaseUnit elements are defined, then they must have identical exponents of their BaseUnit. If both factor and offset attributes are also identical, the connection equation v2 = v1 again holds. If factor and offset are not identical, the tool may either trigger an error or, if supported, perform a conversion; in other words, use the connection equation (in this case the relativeQuantity of the TypeDefinition, see below, has to be taken into account in order to determine whether offset shall or shall not be utilized):

factor(v1) * v1 + (if relativeQuantity(v1) then 0 else offset(v1)) = factor(v2) * v2 + (if relativeQuantity(v2) then 0 else offset(v2))
where relativeQuantity(v1) = relativeQuantity(v2) is required.

As a result, wrong connections can be detected (for example, connecting a force with an angle-based variable would trigger an error) and conversions between, say, US and SI units can be either automatically performed or, if not supported, an error is triggered as well.

This approach is not satisfactory for variables belonging to different quantities that have, however, the same BaseUnit, such as quantities Energy and Torque, or AngularVelocity and Frequency. To handle such cases, quantity definitions have to be taken into account (see TypeDefinitions) and quantity names need to be standardized.

This approach allows a general treatment of units, without being forced to standardize the grammar and allowed values for units (for example, in FMI 1.0, a unit could be defined as N.m in one FMU and as N*m in another FMU, and a tool would have to reject a connection, since the units are not identical. In FMI 2.0 and later, the connection would be accepted, provided both elements have the same BaseUnit definition).

Dimensional analysis of equations

In order to check the validity of equations in a modeling language, the defined units can be used for dimensional analysis, by using the BaseUnit definition of the respective unit. For this purpose, the BaseUnit rad has to be treated as 1. Example:

\[\begin{align*} J \cdot \alpha = \tau \rightarrow [kg.m^2]*[rad/s^2] = [kg.m^2/s^2] & \quad \text{// o.k. ("rad" is treated as "1")} \\ J \cdot \alpha = f \rightarrow [kg.m^2]*[rad/s^2] = [kg.m/s^2] & \quad \text{// error, since dimensions do not agree} \end{align*}\]
Unit propagation

If unit definitions are missing for variables, they might be deduced from the equations where the variables are used. If no unit computation is needed, rad is propagated. If a unit computation is needed and one of the involved units has rad as a BaseUnit, then unit propagation is not possible. Examples:

  • a = b + c, and Unit of c is provided, but not Unit of a and b:
    The Unit definition of c (in other words, Unit.name, BaseUnit, DisplayUnit) is also used for a and b. For example, if BaseUnit(c) = rad/s, then BaseUnit(a) = BaseUnit(b) = rad/s.

  • a = b*c, and Unit of a and of c is provided, but not Unit of b:
    If rad is either part of the BaseUnit of a and/or of c, then the BaseUnit of b cannot be deduced (otherwise it can be deduced). Example: If BaseUnit(a) = kg.m/s2 and BaseUnit(c) = m/s2, then the BaseUnit(b) can be deduced to be `kg. In such a case Unit.name of b cannot be deduced from the Unit.name of a and c, and a tool would typically construct the Unit.name of b from the deduced BaseUnit.

4.2. Support for Min/Max Range Information

Importing implementations should support the processing of min/max limit information when present on the variables of an imported FMU. This support should include support for limit consistency checks and optional runtime checking.

The limit information should be overridable by the user to account for exceptional circumstances: For example, when erroneous limits on a variable have to be manually adjusted since changes to the FMU are not otherwise practicable.

Exporting implementations should generate min/max limit information on all exported variables that support this information in FMI when such limit information is available in the model (or can be automatically derived). Ideally, it should be possible to generate FMUs that optionally check the limits (especially user-supplied limits) at runtime and produce suitable errors and diagnostic logging information when limits are breached.

Implementations are well-advised to heed the explanations in section 2.2.6.3 of the FMI 3.0 standard when implementing min/max checking and handling routines.

4.3. Support for Structured Naming Convention

Importing and exporting implementations should support the structured naming convention for variables specified in the FMI standards to support the interchange of array/matrix and record/struct information.

Starting with FMI 3.0, FMI also supports array variables directly (called native arrays in the following discussion), i.e. without needing to use scalar variables and the structured naming convention. Hence, for FMI 3.0, implementations should support both native arrays and the structured naming convention. This support should include support for bridging between these two ways of expressing arrays, especially when supporting older versions of FMI as well as FMI 3.0. When exporting arrays, preference should be given to native arrays over structured naming convention mappings.

Importing implementations should map complex interfaces expressed via the structured naming convention to suitable internal data structures and formats, like vectors/arrays, records/structs, or busses.

Exporting implementations should map their internal data structures to suitable FMI structured names, mapping vectors to arrays, and records/structs or busses to hierarchical names.

If an implementation supports importing as well as exporting FMUs, the mapping of data structures should allow for efficient round-tripping: It should be possible to export (part of) a model as an FMU with complex data structures at its interface and have that FMU use structured naming in such a way that the resulting FMU can be imported into the same tool and support the same native interface.

Parameter editors and similar editing UIs and APIs should efficiently support complex data types so that a multi-dimensional array can be efficiently edited, both in terms of speed and user interface.

Exporting complex data structures through the structured naming convention can easily lead to 10,000 or more scalar variables. Implementations should therefore be prepared to deal with FMUs containing very many scalar variables efficiently.

4.4. Selection of Exported Parameters/Variables

In order to generate FMUs that are easily handled and also to guard against exposing too many internal details (for both IP protection reasons and reasons of clarity of interfaces), exporting implementations should allow the user to efficiently select which parameters or variables are to be exposed in the FMU as parameters and variables, and which should be kept hidden.

In the case of state variables, this must be done in accordance with the requirements of the standards: Certain state variables always have to be exposed in Model Exchange FMUs.

5. Simulation

5.1. Instantiation

Importing implementations must pass valid values into the following instantiation calls for all of their arguments:

  • FMI 3.0: fmi3InstantiateModelExchange, fmi3InstantiateCoSimulation, and fmi3InstantiateScheduledExecution

  • FMI 2.0: fmi2Instantiate

  • FMI 1.0: fmiInstantiate, fmiInstantiateSlave

Note especially that null pointers are not valid for any of the string arguments to these functions, unless explicitly allowed in the respective standard. Implementations must pass proper values especially for the following fields:

  • instanceName must not be the null pointer, the empty string or a string only containing whitespace.

  • instantiationToken (3.0), fmuGUID (2.0), and GUID (1.0) must match the value of the instantiationToken (3.0) and guid (2.0/1.0) attribute from the modelDescription.xml file, respectively.

  • For FMI 3.0, resourcePath must be a valid native path to the FMU’s resources directory. If an FMU has no resources directory or if no such path can be provided due to implementation restrictions the resourcePath argument must be a null pointer.

  • For FMI 2.0/1.0 fmuResourceLocation/fmuLocation/ must be a valid URI (per RFC 3986), and should be a URI with the file scheme, since that is currently the only scheme that is required to be supported by the FMU. Note that the description of this in the FMI 1.0 standards is very unclear and confusing, so that in practice, until a proper cleanup version of the 1.0 standard is released, as far as valid URIs are concerned the implementation should follow the spirit of the rules as laid out in the FMI 2.0 standard for the fmuResourceLocation argument, but pointing to the root of the unpacked FMU archive rather than the resources directory. Note also that file://C:/ and similar are not valid file URIs, but should rather be supplied as either file:///C:/ (empty authority) or as file:/C:/ (missing authority) to be valid URIs. Furthermore the validity of a URI is not influenced by the existence or non-existence of the file/directory pointed to, so that in the case of FMI 2.0, a URI pointing to the resources directory would still be a valid URI, even if the unpacked FMU archive did not contain a resources directory.

  • mimeType (FMI 1.0 only) must not be null and must be a valid MIME type; unless specific requirements indicate otherwise, application/x-fmu-sharedlibrary is the proper string to pass here.

If an importer tries to create a second instance of an FMU where the capability flag canBeInstantiatedOnlyOncePerProcess is true, the FMU should provide an error message and return NULL.

Exporting implementations should guard (by safely handling these cases without crashing) against common errors in calls to the instantiation functions, including:

  • instanceName, GUID/fmuGUID/instantiationToken, fmuLocation/fmuResourceLocation and mimeType might all be null pointers or empty strings, or otherwise invalid,

  • GUID/fmuGUID/instantationToken is possibly not the guid/instantiationToken attribute from the model description, but something else entirely (i.e. neither the guid attribute from this version of the FMU nor from a former version),

  • Especially under FMI 1.0 fmuLocation is very likely not pointing to the right location (either pointing to the resources directory, or the root directory of the unzipped FMU, to the zipped FMU, or to a different directory entirely), or is an invalid URI (e.g. file://C:/something or the empty string) or of an unknown scheme. This of course makes resource-loading difficult. For Model Exchange FMUs (which are not passed an fmuLocation argument in any case) and for Co-Simulation FMUs (which often get these kinds of illegal/unclear URIs) as a fallback mechanism, it is recommended to load resources from paths derived from the location of the SharedObject/DLL for platforms that support this: On Win32 for example this can be achieved through the use of GetModuleFileName with the module_handle of the DLL (as provided in a DllMain argument), or via dladdr on a known symbol in the FMU on OS X or Linux and similar ELF-based systems. Note that this kind of mechanism should only be used as a fallback, if fmuLocation is not provided (ME FMUs) or is invalid in some way.

5.2. Initialization

When performing initialization of variable values, including parameters and initial values for other variables, importers should not rely on default values being already set in the FMU (i.e. burned into the binary so to speak): While the FMI standards specify that default values shall both be specified in the modelDescription.xml file and are already set in the FMU upon instantiation, some implementations generate FMUs where this is not true, i.e. the default values of variables when not set do not match the values as specified in the modelDescription.xml file. Since there is little effort in actually setting default values through the FMI API at initialization time, the more conservative approach is to always set default values through the API based on the modelDescription.xml file, or on the actual parameter/initial values as specified by the user, not relying on any burned-in default values.

Exporting implementations must ensure that any default values specified for variables in the modelDescription.xml file are actually “burned-in”: The values shall be set automatically as default values upon FMU instantiation, so that importing implementations can rely on the standard-mandated behavior that unset variables have their default values as specified in the modelDescription.xml.

Note that for all start values, especially the mandatory start values for input and parameter variables, it is the responsibility of the FMU — and hence the exporter or the user of the exporter — to provide suitable start values: An importer should be able to rely on these start values, i.e. it is always at liberty to use them unchanged, if no better start values are available to the importer.

If an FMU provides start values that e.g. result in a division-by-zero or some other error inside the FMU if unchanged, these are obviously not suitable start values, and should not have been selected.

Importers should however be aware of the existence of such FMUs, which can easily result from negligence during creation of an FMU: Importers should, if more suitable start values are available, set them at the earliest permissible point in time, to avoid uneccesarily triggering such errors in the FMU. For FMI 2.0/3.0, for example, different start values for inputs and parameters can and should already be set after instantiation, prior to entering initialization mode.

Importers should respect the different order of necessary function calls between FMI 1.0 and FMI 2.0/3.0 when performing initialization (this phase has seen larger changes between FMI 1.0 and 2.0/3.0 due to the introduction of explicit phases/modes in the FMI API, whereas these phases where only implicit in FMI 1.0).

5.3. Startup

For FMI 1.0 Co-Simulation FMUs importing implementations should honor the canHandleEvents capability of the FMU: If this capability flag is false, then the implementation cannot call the doStep function with zero communicationStepSize, even at time 0 (used e.g. for generating valid initial values). In these cases the integration environment must start simulation with the first simulation step, without any event iteration. With FMI 2.0 Co-Simulation FMUs do not support 0-size time steps in any case. With FMI 3.0, Co-Simulation FMUs can support event handling explicitly via the hasEventMode capability flag. Importers can make use of this feature by passing fmi3True for the eventModeUsed argument of the fmi3InstantiateCoSimulation call.

5.4. Support for Solvers

Importing implementations should support a wide variety of solver algorithms, in order to support Model-Exchange FMUs as fully as possible. Ideally this should include support for attaching external solvers through specified APIs, so that experienced users can extend the supported solver base for specific new domains where appropriate. Supporting the use of several different solvers in one model that integrates a number of FMUs, so that e.g. different Model Exchange FMUs can be solved through different solvers than other parts of the model – without resorting to Co-Simulation FMUs – can be beneficial in terms of performance and usability.

In any case the proper documentation of the employed solver algorithms and their configurable settings is very beneficial in understanding and handling differences between implementations.

Exporting implementations should offer all of their built-in solver algorithms (including solver settings like step-size, tolerance, etc.) for export when generating Co-Simulation FMUs, so that the same solvers can be used inside the environment as in exported FMUs. This should ideally also include the ability to export Co-Simulation FMUs using user-supplied solvers, where appropriate (e.g. where the environment supports the integration of external solvers for model evaluation).

5.5. Support for Mixing FMUs Types

Importing implementations should support mixing FMUs of different types (and different FMI versions) in one simulation model, ensuring proper semantics for connections between those FMUs and each other as well as the rest of the simulation model. This should also be checked in cross-checking with other implementations.

As a side issue, importing implementations should try to use as much of the fine-grained direct dependency information potentially present in FMI 1.0 (and even more so in FMI 2.0/3.0) as possible, in order to avoid algorithmic loops being detected where they are not really present.

5.6. Logging Support

Importing implementations should allow fine-grained selection of FMU logging output recording/display, either based on the new FMU-defined logging categories for FMI 2.0/3.0 or on the raw string category argument of the logging callback in FMI 1.0.

Note that since the logging callback type signature in FMI 1.0 and 2.0 uses a variable argument list, this can have implications for the calling convention of that function on platforms that have different calling conventions for C functions with variable argument lists than for functions with fixed argument lists.

Starting with FMI 3.0, the logging callback uses a fixed argument list.

Exporting implementations should support the fine-grained selection of logging categories in FMI 2.0/3.0 and should use fine-grained category names in the category argument for FMI 1.0 logging callback calls.

In FMI 1.0 they should try to not produce verbose logging output when the debug logging flag is fmiFalse.

5.7. Handling of Dependency Information

FMI 2.0/3.0 provide comprehensive information about the structure of a model encapsulated as an FMU, as defined in the element ModelStructure of the modelDescription.xml.

This element defines the dependencies between variables, both during initialization as well as at runtime, which may differ.

The following examples demonstrate in more detail how this information can be understood and used.

5.7.1. Example 1

An FMU is defined by the following equations:

\[\begin{align*} \frac{d}{\text{dt}}\begin{bmatrix} x_{1} \\ x_{2} \\ x_{3} \\ \end{bmatrix} &= \begin{bmatrix} f_{1}\left( x_{2} \right) \\ f_{2}\left( x_{1} \right) + 3 \cdot p^{2} \cdot x_{2} + 2 \cdot u_{1} + 3 \cdot u_{3} \\ f_{3}\left( x_{1},x_{3},u_{1},u_{2},u_{3} \right) \\ \end{bmatrix} \\ y &= g_1(x_2, x_3) \end{align*}\]

where \({u_{1}}\) is a continuous-time input (variability = continuous), \({u_{2}}\) is any type of input, \({u_{3}}\) is a floating-point discrete-time input (variability = discrete), and \({p}\) is a fixed parameter (variability = fixed).

The initialization is defined by:

\[x_1 = 1.1, \frac{dx_2}{dt} = 0, y = 3.3,\]

and therefore, the initialization equations are:

\[\begin{align*} x_{2} &= \frac{1}{3 \cdot p^{2}} \cdot ( f_{2}\left( x_{1} \right) + 2 \cdot u_{1} + 3 \cdot u_{3} ) \\ x_{3} &= g_{1}^{- 1}( x_{2}, y) \end{align*}\]

The model structure for this equation system can be defined as:

<ModelVariables>
   <Float64 name="p"       valueReference= "1" causality="parameter" variability="fixed" start="0"/>
   <Float64 name="u1"      valueReference= "2" causality="input" start="0"/>
   <Float64 name="u2"      valueReference= "3" causality="input" start="0"/>
   <Float64 name="u3"      valueReference= "4" causality="input" variability="discrete" start="0"/>
   <Float64 name="x1"      valueReference= "5"/>
   <Float64 name="x2"      valueReference= "6"/>
   <Float64 name="x3"      valueReference= "7"/>
   <Float64 name="der(x1)" valueReference= "8" derivative="5"/>
   <Float64 name="der(x2)" valueReference= "9" derivative="6"/>
   <Float64 name="der(x3)" valueReference="10" derivative="7"/>
   <Float64 name="y"       valueReference="11" causality="output"/>
</ModelVariables>
<ModelStructure>
   <Output valueReference="11" dependencies="6 7"/>
   <ContinuousStateDerivative valueReference="8"  dependencies="6"/>
   <ContinuousStateDerivative valueReference="9"  dependencies="2 4 5 6" dependenciesKind="constant constant dependent fixed"/>
   <ContinuousStateDerivative valueReference="10" dependencies="2 3 4 5 6" />
   <InitialUnknown valueReference="6" dependencies="2 4 5"/>
   <InitialUnknown valueReference="7" dependencies="2 4 5 11"/>
   <InitialUnknown valueReference="8"/>
   <InitialUnknown valueReference="10"/>
   <InitialUnknown valueReference="11"/>
</ModelStructure>

5.7.2. Example 2

An FMU is defined by the following equation:

\[y = \left\{ \begin{matrix} 2 \cdot u \ \mathrm{if} \ u > 0 \\ 3 \cdot u \ \mathrm{else} \\ \end{matrix}\right.\]

where \({u}\) is a continuous-time input with valueReference = 1 and \({y}\) is a continuous-time output with valueReference = 2.

The definition of the model structure is then:

<ModelVariables>
   <Float64 name="u" valueReference= "1" causality="input" start="1"/>
   <Float64 name="y" valueReference= "2" causality="output"/>
</ModelVariables>
<ModelStructure>
  <Output valueReference="2" dependencies="1" dependenciesKind="discrete"/>
  <InitialUnknown valueReference="2"/>
</ModelStructure>

Note that \({y = d \cdot u}\) where \({d}\) changes only during event mode (\({d = 2 \cdot u}\) or \({3 \cdot u\ }\) depending on relation \({u > 0}\) that changes only at event mode). Therefore dependenciesKind = discrete.

5.7.3. Example 3

An FMU is defined by the following equation:

\[y = \left\{ \begin{matrix} 2\ \ \mathrm{if}\ \ u > 0 \\ 3\ \ \mathrm{else} \\ \end{matrix}\right.\]

where \({u}\) is a continuous-time input with valueReference = 1 and \({y}\) is a continuous-time output with valueReference = 2.

The definition of the model structure is then:

<ModelVariables>
   <Float64 name="u" valueReference= "1" causality="input" start="1"/>
   <Float64 name="y" valueReference= "2" causality="output"/>
</ModelVariables>
<ModelStructure>
  <Output valueReference="2" dependencies="1" dependenciesKind="dependent"/>
  <InitialUnknown valueReference="2"/>
</ModelStructure>

Note that \({y = c}\) where \({c}\) changes only during event mode (\({c = 2}\) or \({3\ }\) depending on relation \({u > 0}\) that changes only at event mode). Therefore dependenciesKind = dependent because it is not a linear relationship on \({u}\).

5.7.4. Example 4

An FMU is defined by the following equations:

\[\frac{dx}{dt}=u, y=x\]

where \({u}\) is a continuous-time input with valueReference = 1, \({y}\) is a continuous-time output with valueReference = 2 and \({dxdt}\) is a continuous-time derivative with valueReference = 4.

The definition of the model structure is then:

<ModelVariables>
   <Float64 name="u" valueReference= "1" causality="input" start="0"/>
   <Float64 name="y" valueReference= "2" causality="output"/>
   <Float64 name="x" valueReference= "3"/>
   <Float64 name="dxdt" valueReference= "4"/>
</ModelVariables>
<ModelStructure>
  <Output valueReference="2" dependencies="3" dependenciesKind="constant"/>
  <ContinuousStateDerivative valueReference="4" dependencies="1" dependenciesKind="constant"/>
  <InitialUnknown valueReference="2" dependencies="3"/>
</ModelStructure>

6. Co-Simulation Specific Issues

6.1. Early Return

Starting with FMI 3.0, co-simulation FMUs can optionally support early return from fmi3DoStep calls to allow more timely handling of internal events.

This capability can be used stand-alone, or in combination with Event-Handling in Co-Simulation, as described below.

An FMU indicates its early return capability through the mightReturnEarlyFromDoStep and canReturnEarlyAfterIntermediateUpdate capability flags in its modelDescription.xml file. Here mightReturnEarlyFromDoStep indicates the FMUs capability to return early on its own volition, whereas canReturnEarlyAfterIntermediateUpdate additionally indicates the FMUs capability to be forced by the importer to return early during intermediate update callbacks.

An importer indicates that it wants to use this capability by passing in fmi3True for the earlyReturnAllowed argument in the call to fmi3InstantiateCoSimulation.

An FMU can then return early from a call to fmi3DoStep, i.e. prior to fully completing its step to the full communication step, by setting the earlyReturn argument to fmi3True, and lastSuccesfulTime to the exact time the FMU computed up to, prior to returning from fmi3DoStep.

This allows an FMU to e.g. signal an internal event has occurred that the importer should take note of.

Note that if the FMU is used without external event handling enabled, any events will still be handled completely internally, and the early return is just used to give the importer a chance to take note of the event. If early return capability is used in conjunction with external event handling, as described in Section 6.2 below, then events are not only signalled but also need to be handled by the importer if so indicated by the FMU.

6.2. Event-Handling in Co-Simulation

Starting with FMI 3.0, co-simulation FMUs can optionally support external event-handling, i.e. notifying the co-simulation algorithm of the importer about events, which the importer then handles by putting the FMU into event mode, where event iteration can take place. This is similar to the way model exchange has always supported external event handling.

An FMU indicates this capability through the hasEventMode capability flag in its modelDescription.xml file. An importer indicates that it wants to use this capability by passing in fmi3True for the eventModeUsed argument in the call to fmi3InstantiateCoSimulation.

The FMU then indicates that event handling is needed by setting the eventHandlingNeeded argument to fmi3True in the call to fmi3DoStep prior to returning.

Note that this can interact with Early Return in the following way: If an FMU supports early return, and the importer has signaled support for early return (through earlyReturnAllowed), then if an event occurs prior to the end of the communication step, the FMU would return early with both earlyReturn and eventHandlingNeeded arguments set to fmi3True, and lastSuccesfulTime giving the exact time of the event.

If however an FMU either does not support early return, or is instantiated with earlyReturnAllowed set to fmi3False, then even if an event occurs prior to the end of the communication step, the FMU must continue to the end of the communication step. It will indicate the event as usual through eventHandlingNeeded being set to fmi3True, and will have to expect the importer to enter event mode as usual. However the exact point in time when the event occured will have already passed, and is not available to the importer. This might require at least mitigative internal event handling by the FMU itself.

For this reason it is usually recommended to use event handling support and early return capabilities together, both for exporters and importers, as this will give the best event handling control.

7. Model-Exchange Specific Issues

7.1. Completed Integrator Step Function

Since the initial release, the FMI API provides a function of the FMU that the integrator is supposed to call at the end of every completed step.

However the exact semantics, capability flags, and rules when to call this function vary between all major releases of FMI:

  • In FMI 1.0 the function is called fmiCompletedIntegratorStep and the function must be called by the environment after every completed step of the integrator.

  • In FMI 2.0 the function is called fmi2CompletedIntegratorStep and it must be called at every completed step of an integrator — provided the capability flag completedIntegratorStepNotNeeded is not provided or false.

  • In FMI 3.0 the function is called fmi3CompletedIntegratorStep and it must be called after every completed step of the integrator — provided the capability flag needsCompletedIntegratorStep is true.

Note especially that the name and logic of the capability flag controlling whether an importing implementation needs to call the function is changed between 2.0 and 3.0: Whereas in 1.0 the function must be called unconditionally, in 2.0 the function must be called unless the FMU actively disables this through the completedIntegratorStepNotNeeded capability flag, and in 3.0 the function must only be called if the FMU actively requests this through the needsCompletedIntegratorStep capability flag.

Implementers migrating between releases or supporting new ones should take this into account.

8. Scheduled-Execution Specific Issues

8.1. Use-cases for Scheduled Execution

The scheduled execution interface type is intended for use cases, where the ability to directly control the scheduling of internal time partitions of multiple FMUs from the outside is considered necessary.

Note that in cases where the actual direct control of the runtime of internal time partitions is not required, the use of clocked co-simulation or model exchange interface types may be more appropriate.

8.2. Priority Levels of Clocks

In scheduled execution clocks must be assigned priority information through the priority attribute on the clock variable.

A clock’s priority information is required to support the scheduled execution of several model partitions, especially in a preemptive scheduling regime.

The clock priorities are local to an FMU. It is not possible for an exporting tool to know in advance the priorities of other FMUs that will be connected to an FMU in a simulation setup. It is the responsibility of the importer to derive a computational order for the computation of two or more distinct FMUs based on the local FMU clock priorities and input-output relationships of connected FMUs.

For periodic clocks it is recommended to derive the priorities based on a rate-monotonic scheduling scheme (smallest period leads to highest priority, that is, has the smallest priority value).

Although the priority attribute is defined as 32-bit unsigned integer type, in case this is mapped by implementations directly to target platform priority levels, it is recommended to restrict the number of distinct priority levels for an FMU to the available priority levels on the target platform. A common number of different priority levels is, e.g., 100 (0 to 99), as defined in Linux-based operating systems.

8.3. Handling of Preemptive Scheduling

To support safe preemptive scheduling of model partitions in scheduled execution, FMI 3.0 provides the callbacks fmi3LockPreemptionCallback and fmi3UnlockPreemptionCallback so that FMUs can guard critical sections of its code against preemption.

An FMU’s code has to be prepared to correctly handle preemption of

  • fmi3ActivateModelPartition,

  • fmi3Get{VariableType},

  • fmi3Set{VariableType},

  • fmi3GetClock,

  • and fmi3GetIntervalDecimal

among others.

In general this means that the FMU’s code has to secure access to its global states and variables wherever data inconsistencies due to potential preemptions are anticipated.

Note that in order to avoid data inconsistencies and safeguard predictable and deadlock-free behavior with fmi3Get{VariableType} and fmi3Set{VariableType} a unique assignment of variables to model partitions via its associated clock is strongly recommended. If every variable is assigned uniquely to a model partition it is usually not necessary to use preemption locks in the context of fmi3Get{VariableType} and fmi3Set{VariableType}:

The following example illustrates why every variable should be assigned uniquely to one model partition via its associated clock:

  • An output variable is changed by two different model partitions:
    The values returned by fmi3Get{VariableType} that is placed after fmi3ActivateModelPartition may return the values computed by the other model partition if that model partition has higher priority and preempted the execution meanwhile.

  • An input variable is used by two different model partitions:
    The variable values of a model partitions of lower priority may have been overwritten by a call to fmi3Set{VariableType} for a model partition of higher priority.

Similarly, for fmi3GetClock the FMU has to ensure that the active state of an output clock is securely reset and cannot be observed twice for the same clock tick in case this call is preempted.

For fmi3GetIntervalDecimal the FMU has to ensure that the countdown clock’s interval qualifier is reset to fmi3IntervalUnchanged and cannot be observed twice for the same clock tick in case this call is preempted.

Depending on their implementation, fmi3CallbackLockPreemption and fmi3CallbackUnlockPreemption have a non-negligible overhead, as well as a strong impact on the scheduler and therefore their use should be as rare and short as possible.

In general, it is recommended to reduce dependencies between different model partitions of one FMU by design. One may also consider if the code can be preempted by other parts of the same FMU: E.g. a model partition cannot be interrupted if it is the only model partition or if it holds the highest priority. Similarly, a model partition, while it might be interrupted, might not need to guard data accesses if it is the only model partition of the FMU, or the one with the highest priority. In such cases no locks may be necessary.

9. Advanced Features

9.1. Partial Derivatives

FMI 3.0 provides optional access to partial derivatives for variables of an FMU. Partial derivatives can be used:

  • in Newton algorithms to solve algebraic loops,

  • in implicit integration algorithms in Model Exchange,

  • in iterative Co-Simulation algorithms, and

  • in optimization algorithms.

To avoid expensive numeric approximations of these derivatives, FMI offers dedicated functions to retrieve partial derivatives for variables of an FMU:

  • fmi3GetDirectionalDerivative to compute the directional derivatives \(\mathbf{v}_{\mathit{sensitivity}} = \mathbf{J} \cdot \mathbf{v}_{\mathit{seed}}\), and

  • fmi3GetAdjointDerivative to calculate the adjoint derivatives \(\mathbf{\bar{v}}_{\mathit{sensitivity}}^T = \mathbf{\bar{v}}_{\mathit{seed}}^T \cdot \mathbf{J}\)

with the Jacobian

\[\mathbf{J} = \begin{bmatrix} \frac{\partial g_1}{\partial v_{\mathit{known},1}} & \cdots & \frac{\partial g_1}{\partial v_{\mathit{known},n}} \\ \vdots & \ddots & \vdots \\ \frac{\partial g_m}{\partial v_{\mathit{known},1}} & \cdots & \frac{\partial g_m}{\partial v_{\mathit{known},n}} \end{bmatrix}\]

where \(\mathbf{v}_{\mathit{known}}\) are the \(n\) knowns, and \(\mathbf{g}\) are the \(m\) functions to calculate the \(m\) unknown variables \(\mathbf{v}_{\mathit{unknwon}}\) from the knowns.

The functions can be used to compute Jacobian-vector products or to construct the partial derivative matrices column-wise or row-wise by choosing the seed vector \(\mathbf{v}_{\mathit{seed}} \in \mathbb{R}^n\) or \(\mathbf{\bar{v}}_{\mathit{seed}} \in \mathbb{R}^m\), respectively, accordingly.

For information on the call signature see the FMI specification.

9.1.1. Directional Derivatives

The function fmi3GetDirectionalDerivative computes the directional derivative

\[\mathbf{v}_{\mathit{sensitivity}} = \mathbf{J} \cdot \mathbf{v}_{\mathit{seed}}\]

One can either retrieve the \(\mathit{i}\)-th column of the Jacobian by specifying the \(\mathit{i}\)-th unit vector \(\mathbf{e}_{\mathit{i}}\) as the seed vector \(\mathbf{v}_{\mathit{seed}}\), or compute a Jacobian-vector product \(\mathbf{Jv}\) by using \(\mathbf{v}\) as the seed vector \(\mathbf{v}_{\mathit{seed}}\). Therefore, the function can be utilized for the following purposes, among others:

  • Solving algebraic loops with a nonlinear solver requires matrix \({\frac{\partial \mathbf{g}}{\partial \mathbf{u}}}\).

  • Numerical integrators of stiff methods need matrix \({\frac{\partial \mathbf{f}}{\partial \mathbf{x}}}\).

  • If the FMU is connected with other FMUs, the partial derivatives of the state derivatives and outputs with respect to the continuous states and the inputs are needed in order to compute the Jacobian for the system of the connected FMUs.

  • If the FMU shall be linearized, the same derivatives as in the previous item are needed.

  • If the FMU is used as the model for an extended Kalman filter, \({\frac{\partial \mathbf{f}}{\partial \mathbf{x}}}\) and \({\frac{\partial \mathbf{g}}{\partial \mathbf{x}}}\) are needed.

  • If matrix-free linear solvers shall be used, Jacobian-vector products \({\mathbf{Jv}}\) are needed (e.g. as a user-supplied routine in CVODE [CVODE570]).

Example 1:
Assume an FMU has the output equations

\[\begin{bmatrix} y_1 \\ y_2 \end{bmatrix} = \begin{bmatrix} g_1(x, u_1, u_3, u_4) \\ g_2(x, u_1) \end{bmatrix}\]

and this FMU is connected, so that \({y_1, u_1, u_3}\) appear in an algebraic loop. Then the nonlinear solver needs a Jacobian and this Jacobian can be computed (without numerical differentiation) provided the partial derivative of \({y_1}\) with respect to \({u_1}\) and \({u_3}\) is available. Depending on the environment where the FMUs are connected, these derivatives can be provided:

(a) with one wrapper function around function fmi3GetDirectionalDerivative to compute the directional derivatives with respect to these two variables (in other words, \({v_{\mathit{unknown}} = y_1}\), \({v_{\mathit{known}} = \left \{ u_1, u_3 \right \}}\)), and then the environment calls this wrapper function with \({v_{\mathit{seed}} = \left \{ 1, 0 \right \}}\) to compute the partial derivative with respect to \({u_1}\) and \({v_{\mathit{seed}} = \left \{ 0, 1 \right \}}\) to compute the partial derivative with respect to \({u_3}\), or

(b) with two direct function calls of fmi3GetDirectionalDerivative (in other words, \({v_{\mathit{unknown}} = y_1, v_{\mathit{known}} = u_1, v_{\mathit{seed}} = 1}\); and \({v_{\mathit{unknown}} = y_1, v_{\mathit{known}} = u_3, v_{\mathit{seed}} = 1}\)).

Note that a direct implementation of this function with analytic derivatives:

(a) Provides the directional derivative for all input variables; so in the above example: \({\Delta y_1 = \frac{\partial g_1}{\partial x} \cdot \Delta x + \frac{\partial g_1}{\partial u_1} \cdot \Delta u_1 + \frac{\partial g_1}{\partial u_3} \cdot \Delta u_3 + \frac{\partial g_1}{\partial u_4} \cdot \Delta u_4}\)

(b) Initializes all seed-values to zero; so in the above example: \({\Delta x = \Delta u_1 = \Delta u_3 = \Delta u_4 = 0}\)

(c) Computes the directional derivative with the seed-values provided in the function arguments; so in the above example: \({v_{\mathit{sensitivity}} = \Delta y_1 (\Delta x = 0, \Delta u_1 = 1, \Delta u_3 = 0, \Delta u_4 = 0)}\)] and \({v_{\mathit{sensitivity}} = \Delta y_1 (\Delta x = 0, \Delta u_1 = 0, \Delta u_3 = 1, \Delta u_4 = 0)}\)]

Example 2:
If a dense matrix shall be computed, the columns of the matrix can be easily constructed by successive calls of fmi3GetDirectionalDerivative. For example, constructing the system Jacobian \({\mathbf{A} = \frac{\partial \mathbf{f}}{\partial \mathbf{x}}}\) as dense matrix can be performed in the following way:

    //   c[]      column vector

    // set time, states and inputs
    fmi3SetTime(S, time);
    fmi3SetContinuousStates(S, x, nx);
    // fmi3Set{VariableType}(S, ...);

    // if required at this step, compute the Jacobian as a dense matrix
    for (i = 0; i < nx; i++) {
        // construct the Jacobian matrix column wise
        fmi3GetDirectionalDerivative(S, vr_dx, nx, &vr_x[i], 1, &dk, 1, c, nx);
        for (j = 0; j < nx; j++) {
            J[j][i] = c[j];
        }
    }

If the sparsity of a matrix shall be taken into account, then the matrix can be constructed in the following way:

  • The incidence information of the matrix (whether an element is zero or not zero) is extracted from the XML file from element ModelStructure.

  • A so called graph coloring algorithm is employed to determine the columns of the matrix that can be computed by one call of fmi3GetDirectionalDerivative.

  • For the columns determined in (2), one call to fmi3GetDirectionalDerivative is made. After each such call, the elements of the resulting directional derivative vector are copied into their correct locations of the partial derivative matrix.

More details and implementational notes are available from [ABL12].

Example 3:
Directional derivatives for higher dimension variables are almost treated in the same way. Consider, for example, an FMU which calculates its output \({Y}\) by multiplying its 2x2 input \({U}\) with a 3x2 constant gain \({K}\), with

\[K= \begin{bmatrix} a, b \\ c, d \\ e, f \end{bmatrix}\]

The output \({Y=K U}\) is a matrix of size 3x2. The directional derivative of an output element \({Y(i,j)}\) with respect to the input \({U}\) and the seed \({\Delta U}\) is:

\[\Delta Y(i,j) = \frac{\partial Y(i,j)}{\partial U(1,1)} \cdot \Delta U(1,1) + \frac{\partial Y(i,j)}{\partial U(1,2)} \cdot \Delta U(1,2) + \frac{\partial Y(i,j)}{\partial U(2,1)} \cdot \Delta U(2,1) + \frac{\partial Y(i,j)}{\partial U(2,2)} \cdot \Delta U(2,2)\]
\[\Delta \mathbf{Y} = \begin{bmatrix} a \Delta U(1,1)+b \Delta U(2,1), a \Delta U(1,2)+ b \Delta U(2,2) \\ c \Delta U(1,1)+d \Delta U(2,1), c \Delta U(1,2)+ d \Delta U(2,2) \\ e \Delta U(1,1)+f \Delta U(2,1), e \Delta U(1,2)+ f \Delta U(2,2) \end{bmatrix}\]

To get the directional derivative of \({Y}\) with respect to \({U(2,1)}\) the command fmi3GetDirectionalDerivative(m, vr_Y, 1, vr_U, 1, {0.0, 0.0, 1.0, 0.0}, 4, dd, 6) can be used where vr_Y and vr_U are references of the variable \({Y}\) and \({U}\), respectively. Note that in order to get the directional derivative of \({Y}\) with respect to \({U(2,1)}\), the seed value {0, 0, 1.0, 0} has been used. The retrieved directional derivative dd is stored in a matrix of size 3x2, so nSensitivity is 6.

9.1.2. Adjoint Derivatives

The function fmi3GetAdjointDerivative computes the adjoint derivative

\[\mathbf{\bar{v}}_{\mathit{sensitivity}}^T = \mathbf{\bar{v}}_{\mathit{seed}}^T \cdot \mathbf{J} \quad \text{or} \quad \mathbf{\bar{v}}_{\mathit{sensitivity}} = \mathbf{J}^T \cdot \mathbf{\bar{v}}_{\mathit{seed}}\]

One can either retrieve the \(\mathit{i}\)-th row of the Jacobian by specifying the \(\mathit{i}\)-th unit vector \(\mathbf{e}_{\mathit{i}}\) as the seed vector \(\mathbf{\bar{v}}_{\mathit{seed}}\), or compute a vector-Jacobian product \(\mathbf{v}^T\mathbf{J}\) by using \(\mathbf{v}\) as the seed vector \(\mathbf{\bar{v}}_{\mathit{seed}}\).

Adjoint derivatives are beneficial in several contexts:

  • in artificial intelligence (AI) frameworks the adjoint derivatives are called "vector gradient products" (VJPs). There adjoint derivatives are used in the backpropagation process to perform gradient-based optimization of parameters using reverse mode automatic differentiation (AD), see, e.g., [BPRS15].

  • in parameter estimation (see [BKF17])

Typically, reverse mode automatic differentiation (AD) is more efficient for these use cases than forward mode AD because the number of knowns is much higher than the number of unknowns (\(\mathit{n} \gg \mathit{m}\)), as explained in the cited references. If the full Jacobian is needed and the number of knowns and unknowns are somewhat equal (\(\mathit{m} \approx \mathit{n}\)) or small, the column-wise construct using fmi3GetDirectionalDerivative is generally more efficient.

Example:
Assume an FMU has the output equations

\[\begin{bmatrix} y_1 \\ y_2 \end{bmatrix} = \begin{bmatrix} h_1(u_1, u_2) \\ h_2(u_1, u_2) \end{bmatrix}\]

and \(\left( w_1, w_2 \right)^T \cdot \mathbf{ \frac{\partial h}{\partial u} }\) for some vector \(\left( w_1, w_2 \right)^T\) is needed. Then one can get this with one function call of fmi3GetAdjointDerivative (with arguments \(\mathbf{v}_{\mathit{unknown}} = \text{valueReferences of} \left \{ y_1, y_2 \right \}\), \(\mathbf{v}_{\mathit{known}} = \text{valueReferences of} \left \{ u_1, u_2 \right \}\), \(\mathbf{\bar{v}}_{\mathit{seed}} = \left( w_1, w_2 \right)^T\)), while with fmi3GetDirectionalDerivative at least two calls would be necessary to first construct the Jacobian column-wise and then multiplying from the right with \(\left( w_1, w_2 \right)^T\).

If a dense matrix shall be computed, the rows of the matrix can be easily constructed by successive calls of fmi3GetAdjointDerivative. For example, constructing the system Jacobian \({\mathbf{A} = \frac{\partial \mathbf{f}}{\partial \mathbf{x}}}\) as a dense matrix can be performed in the following way:

    for (i = 0; i < nx; i++) {
        // construct the Jacobian matrix column wise
        fmi3GetAdjointDerivative(S, &vr_dx[i], 1, vr_x, nx, &dk, 1, &J[i][0], nx);
    }

References