What is FMU? The Universal Language of Simulation
This article is available in English only.
The Problem: Every Simulation Tool Is an Island
Imagine a hydraulic pump model that took six months to build in one simulation tool. A colleague modeled the electric motor in another. A third team keeps the thermal management model in yet another package. Then the project manager asks: "Can we simulate the complete system together?"
This is where most teams hit a wall. Each tool has its own model format, solver and interface. Getting them to talk to each other usually means exporting data, writing glue code, reconciling incompatible time steps — and losing weeks of integration work.
The Functional Mock-up Interface (FMI) is the standard written to remove that wall. The Functional Mock-up Unit (FMU) is what it standardizes: a simulation model packaged so that any tool that speaks FMI can run it.
FMI in One Paragraph
FMI is a free, tool-independent standard for exchanging dynamic simulation models. It defines two things: a container (the .fmu file) and a C programming interface that every FMU implements. A tool that exports FMUs writes the container; a tool that imports them loads the model and drives it through the interface. FMI started in the European MODELISAR project (2008–2011) and has been maintained since 2012 as a project of the Modelica Association. The standard's website lists more than 280 tools that support it.
What's Inside an FMU
An FMU is a ZIP archive with a .fmu extension. Rename it to .zip and you can look inside:
myPump.fmu
├── modelDescription.xml ← the contract: variables, units, capabilities
├── binaries/
│ ├── x86_64-windows/
│ │ └── myPump.dll ← compiled model code (Windows)
│ └── x86_64-linux/
│ └── myPump.so ← compiled model code (Linux)
├── sources/ ← optional: C source code
├── resources/ ← optional: data tables, parameter files
└── documentation/ ← optional: HTML documentation
The folder names follow FMI 3.0, which names platforms like x86_64-windows; FMI 2.0 FMUs use names such as win64 and linux64. FMI 3.0 also adds optional terminalsAndIcons/ and extra/ folders.
Three parts matter most:
modelDescription.xmlis the contract. Every variable is declared here with its name, type, unit and role. An importing tool reads this file before it touches any code, so it knows which ports to create and which parameters to show.binaries/holds the model compiled for one or more platforms. This is the executable part — and the reason a model can be shared without its equations.sources/is optional. An FMU that ships its C sources can be recompiled for a platform its author never built for.
Reading a modelDescription.xml
Here is a shortened FMI 3.0 description of a simple pump model:
<fmiModelDescription fmiVersion="3.0" modelName="Pump"
instantiationToken="{8c4e810f-3df3-4a00-8276-176fa3c9f000}">
<CoSimulation modelIdentifier="myPump"/>
<UnitDefinitions>
<Unit name="rad/s"><BaseUnit s="-1" rad="1"/></Unit>
<Unit name="Pa"><BaseUnit kg="1" m="-1" s="-2"/></Unit>
<Unit name="m3"><BaseUnit m="3"/></Unit>
</UnitDefinitions>
<ModelVariables>
<Float64 name="speed" valueReference="1" causality="input" unit="rad/s" start="0"/>
<Float64 name="pressure" valueReference="2" causality="output" unit="Pa"/>
<Float64 name="displacement" valueReference="3" causality="parameter"
variability="fixed" unit="m3" start="1e-5"/>
</ModelVariables>
<ModelStructure>
<Output valueReference="2"/>
<InitialUnknown valueReference="2"/>
</ModelStructure>
</fmiModelDescription>
A few things to notice:
- Causality says what a variable is for: an
inputis set by the importer at every step, anoutputis read after every step, aparameteris fixed before the simulation starts. - Value references are the numbers the importer actually uses to read and write values. Names are for people; the C interface only sees
valueReference="2". - Units are declared, not assumed. A speed in rad/s connected to a port that expects rpm is off by a factor of 9.55 — and the simulation runs without complaint. The unit definitions give the importer what it needs to catch that.
Three Ways to Run an FMU
FMI defines three interface types. One FMU can support more than one; its modelDescription.xml says which — the <CoSimulation> element in the example above.
| Model Exchange (ME) | Co-Simulation (CS) | Scheduled Execution (SE) | |
|---|---|---|---|
| The FMU provides | The model equations | The equations and its own solver | Separately callable model partitions |
| Time integration by | The importer's solver | The FMU, between communication points | The importer's scheduler, which runs each partition |
| Coupling | Tight: one solver for the whole system | Loose: values exchanged at communication points | Clock-driven |
| Typical use | Tightly coupled physics in one environment | Coupling tools and subsystems | Real-time setups such as virtual control units |
| Available since | FMI 1.0 | FMI 1.0 | FMI 3.0 |
Model Exchange hands the importer the model's equations — state derivatives and event indicators — and lets the importer's solver integrate them. The whole system shares one solver and one step-size control, which is the most accurate way to couple stiff or tightly interacting physics. The price: the importer must bring a capable solver and handle events itself.
Co-Simulation is the natural choice when models come from different tools. Each FMU carries its own solver, tuned by the model's author. The importer — often called the master or orchestrator — advances every FMU by one communication step, passes outputs on to inputs, and repeats. Between communication points an FMU typically holds its inputs constant.
Scheduled Execution, added in FMI 3.0, targets real-time setups: the FMU exposes separate model partitions — for example the tasks of a control unit running at different rates — and the importer's scheduler decides when each one runs.
What an Importer Actually Does
Driving a co-simulation FMU follows the same pattern in every tool. Simplified, with FMI 3.0 function names:
instance = fmi3InstantiateCoSimulation(...)
fmi3EnterInitializationMode(instance, ...) ← start time, tolerance
fmi3SetFloat64(instance, ...) ← parameters and start values
fmi3ExitInitializationMode(instance)
t = startTime
while t < stopTime:
fmi3SetFloat64(instance, inputs) ← values from other components
fmi3DoStep(instance, t, h, ...) ← the FMU advances with its own solver
fmi3GetFloat64(instance, outputs) ← values for other components
t = t + h
fmi3Terminate(instance)
fmi3FreeInstance(instance)
Two consequences follow from this loop:
- The communication step is a numerical parameter. Values cross between FMUs only once per step. If two FMUs interact faster than that — a stiff spring between two masses, a fast control loop around a plant — the coupled result drifts or goes unstable, even when each FMU's own solver is perfectly accurate.
- The importer never sees the equations. It sees values at communication points and the status each FMU reports. That is what makes FMUs shareable, and also what makes them harder to debug.
Why Engineering Teams Use FMUs
- Model exchange across tools. A supplier delivers a component model; the integrator runs it in their own environment.
- Protecting know-how. A binary-only FMU runs without revealing its equations or source code.
- Reuse along the development cycle. The same model can move from desktop simulation to software-in-the-loop and hardware-in-the-loop setups.
- Early system integration. Subsystems are assembled into a virtual prototype long before hardware exists, so interface mistakes surface early instead of at the end of the project.
Connecting many FMUs into one system has its own companion standard, SSP (System Structure and Parameterization), also maintained by the Modelica Association.
What an FMU Is Not
- Not platform-independent by default. Compiled binaries run only on the platforms they were built for. An FMU with only
x86_64-windowsbinaries will not run on a Linux server — and a web browser cannot load a native.dllor.soat all. Running an FMU in a browser means compiling the model for the web, for example from a source-code FMU to WebAssembly, or running it on a server. - Not version-agnostic. FMI 2.0 and FMI 3.0 are different interfaces. An importer has to support the version an FMU was exported with, and in practice you will still meet many FMI 2.0 FMUs.
- Not automatically correct. An FMU is only as good as the model and the exporter behind it. The Modelica Association publishes a set of Reference FMUs for testing importers and exporters.
FMI Versions at a Glance
| Version | Released | What it brought |
|---|---|---|
| FMI 1.0 | 2010 | Model Exchange (January) and Co-Simulation (October), published as two separate specifications |
| FMI 2.0 | 2014 | One specification for both interface types; maintenance releases 2.0.1 to 2.0.5 followed |
| FMI 3.0 | 2022 | Scheduled Execution, array variables, clocks, more integer types, a 32-bit float and a binary type, structural parameters, terminals and icons, event handling and early return in co-simulation; maintenance releases 3.0.1 and 3.0.2 followed |
Conclusion
FMI is one of the most successful standards in engineering simulation because it standardizes the right thing: not how a model is built, but how it is packaged and driven. A .fmu file carries a contract (modelDescription.xml), executable model code, and optionally sources and data. Whether it runs under your solver (Model Exchange), under its own (Co-Simulation) or under your scheduler (Scheduled Execution) is written in that contract.
This post covers the standard itself. We will go deeper into working with FMUs in a follow-up.