Запуск FMI модели в Engee
Запускаем FMU-модели в окружении Julia чтобы получить результаты симуляции и использовать их в дальнейших вычислениях.
Описание задачи
Стандарт FMI (Functional Mockup Interface) подразумевает упаковку модели в файл FMU (Functional Mockup Unit), содержащий либо набор уравнений, либо код, моделирующий систему (предположительно, поддерживаются различные языки программирования, которые можно вызвать в Engee: Python, Java, Fortran, C++ и т.д. включая Julia).
Для примера рассмотрим модель пружины и маятника, собранной в Dymola. Чтобы ее запустить нам потребуется библиотека FMI.jl и набор учебных моделей, которые собраны в пакете FMIZoo.jl.
Pkg.add( ["FMI", "FMIZoo"] )
Подключим установленные библиотеки:
using FMI
using FMIZoo
Мы собираемся провести симуляцию такой модели:

Библиотека FMI.jl позволяет запускать FMI/FMU-модели несколькими способами, и мы покажем два из них:
- через команду
simulate - через создание собственного цикла симуляции и команду
fmi2DoStep.
Создадим временной вектор (зададим начало tStart, окончание Stop симуляции и временной шаг tStep):
tStart = 0.0
tStep = 0.1
tStop = 8.0
tSave = tStart:tStep:tStop
Простейший сценарий запуска FMU модели
Загрузим модель SpringFrictionPendulum1D из пакета моделей FMIZoo.jl.
fmu = loadFMU( "SpringFrictionPendulum1D", "Dymola", "2022x" )
info(fmu)
Запустим расчет этой модели, указав начало и конец времени симуляции и сохраняемую переменную mass.s. Поскольку в модели есть трение, колебания происходят со снижающейся амплитудой, а спустя некоторое время груз приходит в состояние покоя.
simData = simulate(fmu, (tStart, tStop); recordValues=["mass.s"], saveat=tSave)
plot( simData )
После окончания расчета нужно выгрузить из памяти модель FMU и удалить все временные данные.
unloadFMU(fmu)
Создание собственого цикла симуляции
В более сложном случае, например когда вам нужно связать работу сразу нескольких моделей, можно организовать расчетный цикл, в котором модель будет осуществлять шаги в своем фазовом пространстве вдоль по некоторому временному вектору.
Загрузим модель FMU:
fmu = loadFMU("SpringFrictionPendulum1D", "Dymola", "2022x")
Функция шага ожидает специально подготовленную модель (конкретную реализацию), которую мы создадим при помощи fmi2Instantiate. Таким образом можно из одного FMU файла инициализировать сразу много моделей.
instanceFMU = fmi2Instantiate!(fmu)
В следующей ячейке мы осуществляем настройку эксперимента (fmi2SetupExperiment), где указываем начало и конец временного интервала для симуляции, затем модель нужно перевести в режим инициализации (fmi2EnterInitializationMode) и получить ее начальное состояние после инициализации (fmi2ExitInitializationMode).
fmi2SetupExperiment(instanceFMU, tStart, tStop)
fmi2EnterInitializationMode(instanceFMU) # установить исходное состояние
fmi2ExitInitializationMode(instanceFMU) # прочитать исходное состояние
Теперь мы можем запустить модель в расчетном цикле. Функция fmi2DoStep с фиксированным шагом tStep позволяет осуществлять расчетные шаги этой модели. Если у модели есть входные параметры или принимаемые данные, их можно изменять в цикле.
values = []
for t in tSave
# выставить входные параметры модели, если требуется
# ...
fmi2DoStep(instanceFMU, tStep)
# сохранить выходы модели
value = fmi2GetReal(instanceFMU, "mass.s")
push!(values, value)
end
plot(tSave, values)
В окончание расчета мы должны освободить память и удалить промежуточные файлы.
fmi2Terminate(instanceFMU)
fmi2FreeInstance!(instanceFMU)
unloadFMU(fmu)
Заключение
Мы запустили простую FMU модель при помощи одной команды, а также разобрались, как организовать расчетный цикл, где можно было бы обновлять входные параметры модели и принимать оперативные решения на основе выходных данных.