FMI 3.0 Import
Running Functional Mock-up Units as FastSim blocks, in both interface types.
What is an FMU?
FMI is an open standard for exchanging simulation models between tools. An FMU is a ZIP archive holding compiled model code, a modelDescription.xml declaring its variables and capabilities, and optional resources.
FastSim implements FMI 3.0 natively in Rust — it loads the shared library, parses the description and drives the fmi3* entry points itself. There is no Python FMI library in the loop, so an FMU is as cheap to step as any other block.
ModelExchangeFMU and CoSimulationFMU wrap an FMU as an ordinary block: its outputs become block outputs, its inputs become block inputs, and it connects like anything else.
Model Exchange or Co-Simulation?
The two interface types split the work differently:
| Model Exchange | Co-Simulation | |
|---|---|---|
| who integrates | FastSim's solver | the FMU's own solver |
| the FMU provides | MATHINLINE0ENDMATH | a step of size MATHINLINE1ENDMATH |
| step size | chosen by FastSim, adaptive if the solver is | the communication grid |
| events | located by FastSim via the FMU's event indicators | handled inside, reported back |
Model Exchange puts the FMU under one consistent integrator with the rest of the model, which is what you want for a stiff or tightly coupled system. Co-Simulation is the choice when the FMU's internal solver is part of the model's identity.
The Reference FMUs
These examples use the Reference-FMUs published by the Modelica Association — the same archives every FMI tool is tested against. FastSim keeps them as test fixtures, so the paths below point into the repository.
Model Exchange: Dahlquist
The Dahlquist test equation is the standard scalar stiffness probe:
MATHDISPLAY0ENDMATH
The FMU supplies MATHINLINE1ENDMATH; FastSim's solver does the integrating.
Verification
The analytic solution is MATHINLINE0ENDMATH (the FMU's default MATHINLINE1ENDMATH). Nothing about this check involves the FMU or FastSim — it is the exact answer.
Overriding Start Values
Variables declared in modelDescription.xml can be set before instantiation. Here the decay rate MATHINLINE0ENDMATH goes from 1 to 3, so the solution must become MATHINLINE1ENDMATH.
An unknown name is rejected rather than silently ignored:
Events: the Bouncing Ball
The BouncingBall FMU exposes an event indicator for the floor contact. In Model Exchange, FastSim locates the crossing, lets the FMU resolve the event, and carries on — the same machinery its own ZeroCrossing events use.
Outputs are height MATHINLINE0ENDMATH and velocity MATHINLINE1ENDMATH.
Verification
Two properties the physics fixes, independent of either implementation:
- the ball never passes through the floor,
- each bounce scales the energy by MATHINLINE0ENDMATH, so successive apex heights satisfy MATHINLINE1ENDMATH with the same MATHINLINE2ENDMATH every time.
Apex heights are the robust quantity here: the samples immediately around a bounce straddle a discontinuity, so a velocity ratio taken from them depends on where the grid happens to fall. The apex does not.
Co-Simulation
The same FMU, now integrating itself. FastSim advances it on a communication grid and reads the outputs back; the FMU reports events it handled internally.
The two agree on the trajectory but not bit-for-bit, and they should not: Model Exchange puts the ball under FastSim's adaptive solver with the event located to tolerance, while Co-Simulation hands each step to the FMU's own integrator on a fixed grid. Before the first bounce both are integrating the same smooth free fall, and what separates them there is the reference FMU's own forward-Euler step: over a fall of duration MATHINLINE0ENDMATH that lags the exact parabola by $ frac{1}{2}g,\Delta t,t$, which is the number below. After the first bounce the bounce instants drift by a fraction of a communication step, and near a bounce a small shift in time is a large difference in height — so the whole-run figure is dominated by that, not by any disagreement about the physics.