FMIExchange.jl provides ways to easily load Model Exchange Functional Mock-up Units (FMUs) and simulate them using the DifferentialEquations.jl package. This package cannot run simulations on its own, but merely provides tools to construct ODEFunctions and callbacks from FMUs. The user retains full control over the simulation with the powerful and familiar interface of DifferentialEquations.jl.
FMIExchange.jl allows to simulate multiple FMUs at the same time and even combine them with native Julia ODEs; this is the main use-case of this package. FMIExchange.jl includes some functionality to automate the process of combining multiple models into one simulation: automatic addressing, human-readable address map generation and functions for connecting model inputs and outputs.
FMIExchange.jl is not a package for simulating or manipulating FMUs: it doesn't include a simulator but merely provides convenience functions to allow simulating FMUs with the DifferentialEquations.jl package. FMIImport.jl and FMICore.jl provide functionality for importing and manipulating FMUs. FMI.jl has built-in FMU simulation functionality, but this is limited to simulating a single FMU at a time and it is impossible to combine native ODEs with FMUs.
FMIExchange.jl is not a modelling package. While it has some basic simulation composition functionality it lacks some crucial features such as algebraic loop resolution. Some packages that support this are ModelingToolkit.jl and Causal.jl. Pull requests to make FMIExchange.jl compatible with these packages are welcome!
Below example walks you through simulating an FMU using FMIExchange.jl and DifferentialEquations.jl. The example FMU is a bouncing ball in a 2D space.
Import packages. Define FMU file location, and simulation start and stop times.
using FMIExchange
using DifferentialEquations
bbloc = joinpath("deps", "fmu", "BouncingBall2D.fmu") # fmu file location
bbstart = 0.0 # simulation start
bbstop = 60.0 # simulation stop
Define the FMU inputs, outputs, states. This step ensures that correct references to the model variables are made in the simulation. Optionally, the model parameters of the FMU can be changed.
bbins = String[] # FMU inputs (this FMU has none)
bbouts = String[] # FMU outputs (this FMU has none)
bbstates = ["dx", "dy", "x", "y"] # FMU states
bbparamters = Dict("eps"=>1e-2) # FMU parameters (optional)
Use the CachedFMU2
function to load the FMU (the name CachedFMU2
is to avoid naming conflicts with related packages).
We then wrap it in a CachedModel
struct which will automatically preallocate caches for faster calls to the FMU.
fmu = CachedFMU2(bbloc, bbstart, bbstop, bbins, bbouts, bbstates, bbparameters)
model = CachedModel(fmu)
FMU events are handled through callbacks.
FMIExchange.jl can automatically generate the callbacks to handle these events using the get_callbacks
function.
# For the bouncing ball, an event occurs whenever the ball hits a wall
cbs = get_callbacks(model, bbstart, bbstop)
Finally use the CachedModel
and the callbacks as you would typically simulate a native Julia ODE.
# Solve using DifferentialEquations.jl
sol = solve(ODEProblem{true}(model, Float64[1.0, 0.0, 0.5, 1.0], (bbstart, bbstop), Float64[]),
callback=CallbackSet(cbs...), saveat=bbstart:0.01:bbstop)
FMIExchange.jl can be installed through the Julia package manager as below
using Pkg
Pkg.add("FMIExchange.jl")
To run the tests you need to generate the FMUs.
This requires OpenModelica to be installed and the compiler omc
to be available in your PATH
variable.
The Modelica Standard Library, Buildings, IDEAS and MoPED libraries should be available in your Modelica path.
You can find your Modelica path by creating a mos
script with the following contents
getModelicaPath()
and running it with omc
.
On Linux, the result is ~/.openmodelica/libraries
.
Newer versions of OpenModelica have been found to generate faulty FMUs. The Dockerfile contains a static OpenModelica version (built from source) which can be used to compile the FMUs in this repo.
# pull image from the github repository
$ docker pull ghcr.io/electa-git/fmiexchange.jl:latest
$ docker tag ghcr.io/electa-git/fmiexchange.jl:latest fmiexchange.jl:latest
# OR build the image yourself
$ docker build deps -t fmiexchange.jl:latest
# Run the image
$ docker run -v ./deps:/deps:Z -it fmiexchange.jl:latest
It is possible to download the compiled FMUs from the Github workflow runs of this repository if the artifacts are still available on Github.
First extract all FMUs to deps/fmu/
, then run tests as normal.
It is possible that these FMUs do not work on your architecture / OS, in which case you will have to generate the FMUs manually.
The package is available under the BSD 3-clause license here. FMIExchange.jl was developed at KU Leuven - Electa by Lucas Bex.
A portion of this package reuses and modifies code from FMI.jl. The details, including a link to the copyright notice of FMI.jl, can be found in src/callbacks_fmijl.jl. The modifications include the following:
- Compatibility with the interface of FMIExchange.jl
- Rigorous caching for better performance