Engee 文档
Notebook

在 Engee 中运行 FMI 模型

在 Julia 环境中运行 FMU 模型,获取仿真结果并用于进一步计算。

任务描述

FMI(功能模拟接口)标准涉及将模型打包到 FMU(功能模拟单元)文件中,该文件包含一组方程或系统建模代码(可能支持 Engee 中可调用的各种编程语言:Python、Java、Fortran、C++ 等,包括 Julia)。

举个例子,让我们看看用 Dymola 建立的弹簧和钟摆模型。要运行该模型,我们需要FMI.jl 库和一组训练模型,这些模型收集在FMIZoo.jl 软件包中。

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

让我们连接已安装的库:

In [ ]:
using FMI
using FMIZoo

我们将对这样一个模型进行模拟:

image.png

FMI.jl 库允许您以多种方式运行 FMI/FMU 模型,我们将展示其中两种:

  • 通过命令simulate
  • 通过创建自己的仿真循环和命令fmi2DoStep

创建时间矢量(设置模拟的开始tStart 、结束Stop 和时间步长tStep ):

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

运行调频单元模型的最简单方案

让我们从模型软件包FMIZoo.jl 中加载模型SpringFrictionPendulum1D

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 #####################

运行该模型的计算,指定模拟时间的开始和结束以及存储变量mass.s 。由于模型中存在摩擦,振荡幅度会逐渐减小,一段时间后载荷会静止。

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

计算结束后,必须从内存中卸载 FMU 模型,并删除所有时间数据。

In [ ]:
unloadFMU(fmu)

创建自己的模拟周期

在更复杂的情况下,例如需要将多个模型的工作联系起来时,可以组织一个计算循环,让模型沿着某个时间矢量在其相空间中采取步骤。

让我们加载 FMU 模型:

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

步骤函数需要一个专门准备的模型(具体实现),我们将使用fmi2Instantiate 创建该模型。这样就可以通过一个 FMU 文件初始化多个模型。

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

在下一个单元中,我们将进行实验设置 (fmi2SetupExperiment) ,指定模拟时间间隔的开始和结束,然后将模型调入初始化模式 (fmi2EnterInitializationMode) 并在初始化后获得其初始状态 (fmi2ExitInitializationMode) 。

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

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

现在我们可以在计算循环中运行模型。固定步长函数fmi2DoStep tStep 允许该模型的计算步长。如果模型有输入参数或接受的数据,可以在循环中进行更改。

In [ ]:
values = []

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

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

plot(tSave, values)
Out[0]:

计算结束时,我们必须释放内存并删除中间文件。

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

结论

我们使用单一命令运行了一个简单的调频装置模型,并找出了如何组织一个计算循环,在此循环中,我们可以更新模型的输入参数,并根据输出数据做出运行决策。