使用 PyFMI 进行灵敏度分析 - for-loop 中的 FMU

T. *_*rgi 5 python modelica fmi jmodelica

主要目标

区域供热网络的敏感性分析。

方法

  1. 使用 AixLib 和 BuildingSystem 库的系统 Modelica 模型(在 Dymola 中)

  2. 将模型导出为 FMU 协同仿真

  3. 使用SALib(灵敏度分析python库)定义样本(参数扫描)

  4. 使用 PyFMI 在 Python 中的 for 循环中为所有单个样本运行模型(并并行化 for 循环,可能使用 JobLib 在多个处理器上执行模拟)

  5. SALib 执行基于方差的敏感性分析 ( http://salib.readthedocs.io/en/latest/basics.html#an-example )

第一步

Ishigami 函数的简单模型模型(与时间无关)。该函数常用于测试敏感性分析方法(https://www.sfu.ca/~ssurjano/ishigami.html)。

python 代码(包括使用 PyFMI 加载 FMU 和参数扫描)工作正常。

问题

经过一定量的模拟后,我们得到一个错误。错误输出看起来并不总是相同的。有时我们得到

FMUException:加载二进制文件时出错。无法加载 DLL:Eine DLL-Initialisierungsroutine ist fehlgeschlagen。

翻译:DLL 初始化例程失败。

有时我们会得到:

FMUException:加载二进制文件时出错。无法加载 DLL:Für diesen Befehl ist nicht genügend Speicher verfügbar。

翻译:此命令没有足够的可用内存

该错误发生在大约 650 次模拟运行后。这与模拟是在一个接一个地重新运行的较小循环块中执行还是一个单独的 for 循环运行所有模拟无关。通过重新启动 python 控制台/进程,可以再次运行新的模拟。

工作环境:

Windows 10、Python 2.7、使用 pip(不是 JModelica)安装的 PyFMI、Jupyther 笔记本上的 Python 编码(在 Mozilla Firefox 上)

我们只有 Python 和 PyFMI 的基本知识,并且正在为这个错误而苦苦挣扎!

依恋

你可以在下面找到

  • Modelica 模型用于从 Dymola 导出协同仿真 FMU(使用 CVode)

  • Python 代码作为 py 文件

  • python代码的输出散点图。

我也在JModelica论坛发了个帖子,可以直接下载文件(FMU、Jupyter notebook等):http ://www.jmodelica.org/27925

Modelica 模型

model IshigamiFunction

  final parameter Real a = 7;

  final parameter Real b = 0.05;

  parameter Real x1 = 1;

  parameter Real x2 = 1;

  parameter Real x3 = 1;

  Real f;

equation

  f = sin(x1) + a * sin(x2)^2 + b * x3^4 * sin(x1);

end IshigamiFunction;
Run Code Online (Sandbox Code Playgroud)

Python代码

import numpy as np
import pylab as pl
from pyfmi import load_fmu
from SALib.sample import saltelli
from SALib.analyze import sobol
from ipywidgets import FloatProgress
from IPython.display import display


n = 100

problem = {
    'num_vars': 3,
    'names': ['x1', 'x2', 'x3'],
    'bounds': [[-np.pi, np.pi],
               [-np.pi, np.pi],
               [-np.pi, np.pi]]
}

param_values = saltelli.sample(problem, n)

fmu = 'Model\IshigamiFunction\IshigamiFunction.fmu'
n_sim = param_values.shape[0]

# Progress bar
f = FloatProgress(min = 0, max = n_sim, description='Progress:')
display(f)

# Numpy array to save results
y = np.zeros([param_values.shape[0]])
x1 = np.zeros([param_values.shape[0]])
x2 = np.zeros([param_values.shape[0]])
x3 = np.zeros([param_values.shape[0]])

for i, X in enumerate(param_values):
    model = load_fmu(fmu)  
    model.set(problem['names'], X)
    res = model.simulate(final_time = 1)
    y[i] = res['f'][-1]
    x1[i] = res['x1'][-1]
    x2[i] = res['x2'][-1]
    x3[i] = res['x3'][-1]
    f.value += 1


# Scatter plots
fig = pl.figure(figsize=(20, 5))
pl.clf()

pl.subplot(1,3,1)
pl.plot(x1, y, 'or')
pl.ylabel('x1')
pl.xlabel('f')

pl.subplot(1,3,2)
pl.plot(x2, y, 'ob')
pl.ylabel('x2')
pl.xlabel('f')

pl.subplot(1,3,3)
pl.plot(x3, y, 'og')
pl.ylabel('x3')
pl.xlabel('f')

pl.suptitle('Scatter plots')
pl.show()

# Sensitivity analysis
Si = sobol.analyze(problem, y, print_to_console=True)
Run Code Online (Sandbox Code Playgroud)

python脚本的输出图 在此处输入图片说明

更新

我做了一些更多的测试,这就是我发现的:

根据 FMU 是从 Dymola 还是从 JModelica 导出,行为会有所不同:

使用从 Dymola 导出的 FMU:

  • 将这load_fmu条线从 for 循环中取出似乎有效
  • 即使load_fmu不在 for 循环中,有时也会崩溃
  • model.reset()model.set(...)命令之前添加新行似乎工作正常
  • 有或没有模拟时的结果是不同的 model.reset() -> 为什么?
  • model.instantiate()而不是model.reset()-> 不起作用。任务管理器中的内存使用量上升到 350 MB 左右,然后

    FMUException: 无法实例化模型。有关可能的更多信息,请参阅日志。

log_level=4 的日志文件:

FMIL: module = FMILIB, log level = 4: XML specifies FMI standard version 2.0
FMIL: module = FMILIB, log level = 4: Loading 'win32' binary with 'default' platform types
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiInstantiateModel completed
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiInstantiateSlave
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiInstantiateModel completed
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiInstantiateSlave
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiSetReal: x1 = -1.76101
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiSetReal: x2 = -2.53414
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiSetReal: x3 = 0.116583
FMIL: module = Model, log level = 4: [][FMU status:OK] fmi2SetupExperiment: startTime is set to 0
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiEnterSlaveInitializationMode...
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiSetTime to 0
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiEnterSlaveInitializationMode completed
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiExitSlaveInitializationMode...
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiExitSlaveInitializationMode completed
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiGetReal: x1 = -1.76101
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiGetReal: x2 = -2.53414
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiGetReal: x3 = 0.116583
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiGetReal: a = 7
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiGetReal: b = 0.05
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiGetReal: f = 1.29856
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiGetDerivatives
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiGetDerivatives
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiGetDerivatives
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiGetDerivatives
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiGetDerivatives
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiGetDerivatives
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiGetDerivatives
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiGetDerivatives
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiSetTime to 0.002
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiDoStep
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiSetTime to 0.004
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiDoStep
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiSetTime to 0.006
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiDoStep
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiGetDerivatives
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiSetTime to 0.008
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiDoStep
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiSetTime to 0.01
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiDoStep
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiSetTime to 0.012
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiDoStep
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiSetTime to 0.014
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiDoStep
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiSetTime to 0.016
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiDoStep
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiSetTime to 0.018
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiDoStep
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiSetTime to 0.02
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiDoStep

...

FMIL: module = Model, log level = 4: [][FMU status:OK] fmiDoStep
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiSetTime to 0.99
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiDoStep
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiSetTime to 0.992
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiDoStep
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiSetTime to 0.994
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiDoStep
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiSetTime to 0.996
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiDoStep
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiSetTime to 0.998
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiDoStep
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiSetTime to 1
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiDoStep
FMIL: module = Model, log level = 1: [][FMU status:Fatal] The license file was not found. Use the environment variable "DYMOLA_RUNTIME_LICENSE" to specify your Dymola license file.

FMIL: module = Model, log level = 1: [][FMU status:Fatal] Instantiation failed
FMIL: module = Model, log level = 4: [][FMU status:OK] fmiFreeModelInstance
Run Code Online (Sandbox Code Playgroud)

使用从 JModelica 导出的 FMU:

  • 即使load_fmu在 for 循环中也能正常工作(但速度较慢)
  • 此体验与第 5.4.2 章 ( http://www.jmodelica.org/api-docs/usersguide/2.1/ch05s04.html#d0e1854 ) 中JModelica 文档中给出的示例不符,其中load_fmu命令在 for -环形
  • 命令model.reset()ormodel.instatiate()在 for 循环中是必需的(与 Dymola FMU 相反)-> 为什么?

我的问题:

为什么要执行一个循环,用不同的参数多次模拟 FMU 模型?

什么是两者的区别model.reset()model.instatiate()或者他们没有?

依恋

这是一个图表,显示了有model.reset()和没有它的 for 循环之间的区别。 在此处输入图片说明

从 JModelica 导出的 FMU(不需要任何许可证)可以在这里下载:http ://www.jmodelica.org/27925#comment-6668

小智 5

Dymola FMU(对于其他供应商的 FMU 可能也是如此)的正确方法是在 for 循环外调用 fmi/fmi2Instantiate。如果 FMU 在没有二进制导出许可证的情况下导出,这些函数将分配内存并执行许可证检查。通过调用 fmiResetSlave/fmi2Reset,您可以将 FMU 重置为实例化状态,而无需分配新的内存。

  • fmiInstantiateSlave/fmi2Instantiate

    创建一个可用于模拟的 FMU 实例,多次调用将创建多个实例,每个实例都需要新的内存分配和适当的删除。

  • 重置

    在实例化之后和调用 fmiInitializeSlave/fmi2Intialize 之前将您的实例重置为状态。这更快,不需要新的动态内存分配,应该在您的情况下使用。

此外,在调用 fmiFreeSalveInstance/fmi2FreeInstance 时,Dymola FMU 的许可证检查在没有二进制导出的情况下可能会泄漏旧 Dymola 版本中的内存。这在大多数情况下不是问题,因为您通常在终止 FMU 时终止程序。通过在 for 循环中实例化您的 FMU,这会变得很严重,您的记忆最终将结束。如果您联系 Dymola 支持,则修复包应该可用。


tbe*_*beu 1

对我来说这看起来像是一个记忆问题。你能在Win任务管理器中观察运行过程中分配的内存吗?顺便说一句,您的 FMU(来自您的交叉帖子)需要 DYMOLA_RUNTIME_LICENSE,这将复制限制为仅限 Dymola 用户。