Engee documentation
Notebook

Launching the FMI model in Engee

We run FMU models in the Julia environment to get the simulation results and use them in further calculations.

Task description

The FMI (Functional Mockup Interface) standard implies packing the model into an FMU (Functional Mockup Unit) file containing either a set of equations or code modeling the system (presumably, various programming languages that can be called in Engee are supported: Python, Java, Fortran, C++, etc. including Julia).

For example, consider a model of a spring and a pendulum assembled in Dymola. To run it, we need a library FMI.jl and a set of training models that are assembled in the package FMIZoo.jl.

In [ ]:
Pkg.add( ["FMI", "FMIZoo"] )

Connecting the installed libraries:

In [ ]:
using FMI
using FMIZoo

We are going to conduct a simulation of such a model.:

image.png

Library FMI.jl It allows you to run FMI/FMU models in several ways, and we will show two of them.:

  • through the command simulate
  • by creating your own simulation loop and command fmi2DoStep.

Let's create a time vector (set the beginning tStart, ending Stop simulations and time step tStep):

In [ ]:
tStart = 0.0
tStep = 0.1
tStop = 8.0
tSave = tStart:tStep:tStop
Out[0]:
0.0:0.1:8.0

The simplest scenario for launching an FMU model

Loading the model SpringFrictionPendulum1D from the model package FMIZoo.jl.

In [ ]:
fmu = loadFMU( "SpringFrictionPendulum1D", "Dymola", "2022x" )
info(fmu)
#################### Begin information for FMU ####################
	Model name:			SpringFrictionPendulum1D
	FMI-Version:			2.0
	GUID:				{2e178ad3-5e9b-48ec-a7b2-baa5669efc0c}
	Generation tool:		Dymola Version 2022x (64-bit), 2021-10-08
	Generation time:		2022-05-19T06:54:12Z
	Var. naming conv.:		structured
	Event indicators:		24
	Inputs:				0
	Outputs:			0
	States:				2
		33554432 ["mass.s"]
		33554433 ["mass.v", "mass.v_relfric"]
	Parameters:			12
		16777216 ["fricScale"]
		16777217 ["s0"]
		16777218 ["v0"]
		16777219 ["fixed.s0"]
		...
		16777223 ["mass.smin"]
		16777224 ["mass.v_small"]
		16777225 ["mass.L"]
		16777226 ["mass.m"]
		16777227 ["mass.fexp"]
	Supports Co-Simulation:		true
		Model identifier:	SpringFrictionPendulum1D
		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:	SpringFrictionPendulum1D
		Get/Set State:		true
		Serialize State:	true
		Dir. Derivatives:	true
##################### End information for FMU #####################

Let's run the calculation of this model, specifying the beginning and end of the simulation time and the variable to be saved. mass.s. Since there is friction in the model, fluctuations occur with decreasing amplitude, and after a while the load comes to rest.

In [ ]:
simData = simulate(fmu, (tStart, tStop); recordValues=["mass.s"], saveat=tSave)
plot( simData )
Out[0]:

After completing the calculation, you need to unload the FMU model from memory and delete all temporary data.

In [ ]:
unloadFMU(fmu)

Creating your own simulation cycle

In a more complex case, for example, when you need to link the work of several models at once, you can organize a calculation cycle in which the model will perform steps in its phase space along a certain time vector.

Download the FMU model:

In [ ]:
fmu = loadFMU("SpringFrictionPendulum1D", "Dymola", "2022x")
Out[0]:
Model name:	SpringFrictionPendulum1D
Type:		1

The step function expects a specially prepared model (a specific implementation), which we will create using fmi2Instantiate. In this way, many models can be initialized from a single FMU file at once.

In [ ]:
instanceFMU = fmi2Instantiate!(fmu)
Out[0]:
FMU:            SpringFrictionPendulum1D
    InstanceName:   SpringFrictionPendulum1D
    Address:        Ptr{Nothing} @0x000000001d62f8c0
    State:          0
    Logging:        false
    FMU time:       -Inf
    FMU states:     nothing

In the next cell, we set up the experiment (fmi2SetupExperiment), where we specify the beginning and end of the time interval for simulation, then the model must be switched to initialization mode (fmi2EnterInitializationMode) and get its initial state after initialization (fmi2ExitInitializationMode).

In [ ]:
fmi2SetupExperiment(instanceFMU, tStart, tStop)

fmi2EnterInitializationMode(instanceFMU) # установить исходное состояние
fmi2ExitInitializationMode(instanceFMU)  # прочитать исходное состояние
Out[0]:
0x00000000

Now we can run the model in the calculation cycle. Function fmi2DoStep with a fixed pitch tStep allows you to perform the calculated steps of this model. If the model has input parameters or received data, they can be changed in a loop.

In [ ]:
values = []

for t in tSave
    # выставить входные параметры модели, если требуется
    # ...

    fmi2DoStep(instanceFMU, tStep)
    
    # сохранить выходы модели
    value = fmi2GetReal(instanceFMU, "mass.s")
    push!(values, value)
end

plot(tSave, values)
Out[0]:

At the end of the calculation, we must free up the memory and delete the intermediate files.

In [ ]:
fmi2Terminate(instanceFMU)
fmi2FreeInstance!(instanceFMU)
unloadFMU(fmu)
[OK][CvodeStatistics][SpringFrictionPendulum1D]: Sundials CVode Statistics
    Stop time                                : 8.00 s
    Simulation time                          : 1.42 s
    Number of external steps                 : 80
    Number of internal steps                 : 191
    Number of non-linear iterations          : 263
    Number of non-linear convergence failures: 0
    Number of f function evaluations         : 291
    Number of g function evaluations         : 339
    Number of Jacobian-evaluations (direct)  : 7
    Maximum integration order                : 5
    Suggested tolerance scale factor         : 1.0
    Grouping used                            : no

Conclusion

We launched a simple FMU model with a single command, and also figured out how to organize a calculation cycle where we could update the input parameters of the model and make operational decisions based on the output data.