Multiple Instances of an FMU

Tutorial by Johannes Stoljar, Tobias Thummerer

🚧 This tutorial is under revision and will be replaced by an up-to-date version soon 🚧

License

# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher, Johannes Stoljar
# Licensed under the MIT license. 
# See LICENSE (https://github.com/thummeto/FMI.jl/blob/main/LICENSE) file in the project root for details.

Motivation

This Julia Package FMI.jl is motivated by the use of simulation models in Julia. Here the FMI specification is implemented. FMI (Functional Mock-up Interface) is a free standard (fmi-standard.org) that defines a container and an interface to exchange dynamic models using a combination of XML files, binaries and C code zipped into a single file. The user can thus use simulation models in the form of an FMU (Functional Mock-up Units). Besides loading the FMU, the user can also set values for parameters and states and simulate the FMU both as co-simulation and model exchange simulation.

Introduction to the example

In this example we want to show that it is possible to create different instances of an FMU. The different instances can then be used to run independent simulations. After the FMU has been simulated, the simulation results are displayed in a graph. The used model is a one-dimensional spring pendulum without friction. The object-orientated structure of the SpringPendulum1D can be seen in the following graphic.

svg

Target group

The example is primarily intended for users who work in the field of simulations. The example wants to show how simple it is to use FMUs in Julia.

Other formats

Besides, this Jupyter Notebook there is also a Julia file with the same name, which contains only the code cells and for the documentation there is a Markdown file corresponding to the notebook.

Getting started

Installation prerequisites

DescriptionCommandAlternative
1.Enter Package Manager via]
2.Install FMI viaadd FMIadd " https://github.com/ThummeTo/FMI.jl "
3.Install FMIZoo viaadd FMIZooadd " https://github.com/ThummeTo/FMIZoo.jl "
4.Install Plots viaadd Plots

Code section

To run the example, the previously installed packages must be included.

# imports
using FMI
using FMIZoo
using Plots

Simulation setup

Next, the start time and end time of the simulation are set. Finally, the recorded values are specified to store the results of the simulation.

tStart = 0.0
tStop = 8.0

vrs = ["mass.s"]
1-element Vector{String}:
 "mass.s"

Import FMU

In the next lines of code the FMU model from FMIZoo.jl is loaded and the information about the FMU is shown.

# we use an FMU from the FMIZoo.jl
pathToFMU = get_model_filename("SpringPendulum1D", "Dymola", "2022x")

myFMU = loadFMU(pathToFMU)
info(myFMU)
#################### Begin information for FMU ####################
	Model name:			SpringPendulum1D
	FMI-Version:			2.0
	GUID:				{fc15d8c4-758b-48e6-b00e-5bf47b8b14e5}
	Generation tool:		Dymola Version 2022x (64-bit), 2021-10-08
	Generation time:		2022-05-19T06:54:23Z
	Var. naming conv.:		structured
	Event indicators:		0
	Inputs:				0


	Outputs:			0
	States:				2
		33554432 ["mass.s"]
		33554433 ["mass.v"]
	Parameters:			7
		16777216 ["mass_s0"]
		16777217 ["mass_v0"]
		16777218 ["fixed.s0"]
		16777219 ["spring.c"]
		16777220 ["spring.s_rel0"]
		16777221 ["mass.m"]
		16777222 ["mass.L"]
	Supports Co-Simulation:		true
		Model identifier:	SpringPendulum1D
		Get/Set State:		true
		Serialize State:	true
		Dir. Derivatives:	true
		Var. com. steps:	true
		Input interpol.:	true
		Max order out. der.:	1
	Supports Model-Exchange:	true
		Model identifier:	SpringPendulum1D
		Get/Set State:		true
		Serialize State:	true
		Dir. Derivatives:	true
##################### End information for FMU #####################

First Instance

To create an instance of the FMU it is necessary to call the command fmi2Instantiate!(). With the component address you now have a unique instance of the FMU.

c1 = fmi2Instantiate!(myFMU; loggingOn=true)
comp1Address = c1.addr
println(c1)
FMU:            SpringPendulum1D
    InstanceName:   SpringPendulum1D
    Address:        Ptr{Nothing} @0x000001bffe8baaf0
    State:          0
    Logging:        true
    FMU time:       -Inf
    FMU states:     nothing

Next, a dictionary for the parameters is created. With this dictionary you can set the initial states of the variables of the FMU. For the spring constant spring.c a value of $10.0 \frac{N}{m}$ and for the position of the mass mass.s a value of $1.0 m$ is set. The created dictionary with the specified variables for recording are passed to the command for simulation. In addition, other keywords are set. On the one hand the keyword instantiate=false is set, which prevents that in the simulation command a new instance is created. On the other hand the keyword freeInstance=false is set, this prevents that after the simulation command the instance is released.

param1 = Dict("spring.c"=>10.0, "mass_s0"=>1.0)
data1 = simulate(c1, (tStart, tStop); parameters=param1, recordValues=vrs, instantiate=false, freeInstance=false)
fig = plot(data1)

svg

For control, you can compare again the address of the instance to the previous address, and it should be the same address. As soon as this is not the case an error would be thrown by the macro @assert.

@assert c1.addr === comp1Address

Second Instance

To create a second instance of the FMU it is necessary to call the command fmi2Instantiate!(). With the component address you now have a unique instance of the FMU.

c2 = fmi2Instantiate!(myFMU; loggingOn=true)
comp2Address = c2.addr
println(c2)
FMU:            SpringPendulum1D
    InstanceName:   SpringPendulum1D
    Address:        Ptr{Nothing} @0x000001bffe8be690
    State:          0
    Logging:        true
    FMU time:       -Inf
    FMU states:     nothing

The addresses of the instantiated FMUs must differ, and you can see that in the comparison below.

@assert comp1Address !== comp2Address

Again, a dictionary for the parameters is created. With this dictionary you can set the initial states of the variables of the FMU. For the spring constant spring.c a value of $1.0 \frac{N}{m}$ and for the position of the mass mass.s a value of $2.0 m$ is set. The created dictionary with the specified variables for recording are passed to the command for simulation. As before, the two keywords instantiate=false and freeInstance=false are set.

param2 = Dict("spring.c"=>1.0, "mass.s"=>2.0)
data2 = simulateCS(c2, (tStart, tStop);  parameters=param2, recordValues=vrs, instantiate=false, freeInstance=false)
plot!(fig, data2)

svg

For control, you can compare again the address of the instance comp2 to the previous address comp2Address and it should be the same address.

@assert c2.addr === comp2Address

Unload FMU

After plotting the data, the FMU is unloaded and all unpacked data on disc is removed.

unloadFMU(myFMU)

Summary

Based on the example it can be seen that it is possible to create different instances of an FMU. The different instances can then be used to perform different simulations.