Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 24 additions & 9 deletions OMPython/ModelicaSystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -1631,7 +1631,7 @@ def convertMo2Fmu(
fmuType: str = "me_cs",
fileNamePrefix: Optional[str] = None,
includeResources: bool = True,
) -> str:
) -> OMCPath:
"""Translate the model into a Functional Mockup Unit.

Args:
Expand All @@ -1658,15 +1658,19 @@ def convertMo2Fmu(
properties = (f'version="{version}", fmuType="{fmuType}", '
f'fileNamePrefix="{fileNamePrefix}", includeResources={includeResourcesStr}')
fmu = self._requestApi(apiName='buildModelFMU', entity=self._model_name, properties=properties)
fmu_path = self._session.omcpath(fmu)

# report proper error message
if not os.path.exists(fmu):
raise ModelicaSystemError(f"Missing FMU file: {fmu}")
if not fmu_path.is_file():
raise ModelicaSystemError(f"Missing FMU file: {fmu_path.as_posix()}")

return fmu
return fmu_path

# to convert FMU to Modelica model
def convertFmu2Mo(self, fmuName): # 20
def convertFmu2Mo(
self,
fmu: os.PathLike,
) -> OMCPath:
"""
In order to load FMU, at first it needs to be translated into Modelica model. This method is used to generate
Modelica model from the given FMU. It generates "fmuName_me_FMU.mo".
Expand All @@ -1675,13 +1679,24 @@ def convertFmu2Mo(self, fmuName): # 20
>>> convertFmu2Mo("c:/BouncingBall.Fmu")
"""

fileName = self._requestApi(apiName='importFMU', entity=fmuName)
fmu_path = self._session.omcpath(fmu)

if not fmu_path.is_file():
raise ModelicaSystemError(f"Missing FMU file: {fmu_path.as_posix()}")

filename = self._requestApi(apiName='importFMU', entity=fmu_path.as_posix())
filepath = self._work_dir / filename

# report proper error message
if not os.path.exists(fileName):
raise ModelicaSystemError(f"Missing file {fileName}")
if not filepath.is_file():
raise ModelicaSystemError(f"Missing file {filepath.as_posix()}")

self.model(
name=f"{fmu_path.stem}_me_FMU",
file=filepath,
)

return fileName
return filepath

def optimize(self) -> dict[str, Any]:
"""Perform model-based optimization.
Expand Down
57 changes: 57 additions & 0 deletions tests/test_FMIImport.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import numpy as np
import os
import pytest
import shutil

import OMPython


@pytest.fixture
def model_firstorder(tmp_path):
mod = tmp_path / "M.mo"
mod.write_text("""model M
Real x(start = 1, fixed = true);
parameter Real a = -1;
equation
der(x) = x*a;
end M;
""")
return mod


def test_FMIImport(model_firstorder):
filePath = model_firstorder.as_posix()

# create model & simulate it
mod1 = OMPython.ModelicaSystem()
mod1.model(file=filePath, name="M")
mod1.simulate()

# create FMU & check
fmu = mod1.convertMo2Fmu(fileNamePrefix="M")
assert os.path.exists(fmu)

# import FMU & check & simulate
# TODO: why is '--allowNonStandardModelica=reinitInAlgorithms' needed? any example without this possible?
mod2 = OMPython.ModelicaSystem(commandLineOptions=['--allowNonStandardModelica=reinitInAlgorithms'])
mo = mod2.convertFmu2Mo(fmu=fmu)
assert os.path.exists(mo)

mod2.simulate()

# get and verify result
res1 = mod1.getSolutions(['time', 'x'])
res2 = mod2.getSolutions(['time', 'x'])

# check last value for time
assert res1[0][-1] == res2[0][-1] == 1.0
# check last value for x
assert np.isclose(res1[1][-1], 0.3678794515) # 0.36787945153397683
assert np.isclose(res2[1][-1], 0.3678794515) # 0.3678794515707647

# cleanup
tmp2 = mod1.getWorkDirectory()
shutil.rmtree(tmp2, ignore_errors=True)

tmp2 = mod2.getWorkDirectory()
shutil.rmtree(tmp2, ignore_errors=True)