Skip to content

Commit 319b7f9

Browse files
committed
Merge branch 'ModelicaSystem_convertFmu2Mo' into v4.1.0-syntron
2 parents 7419194 + 6c0d699 commit 319b7f9

2 files changed

Lines changed: 80 additions & 10 deletions

File tree

OMPython/ModelicaSystem.py

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1592,7 +1592,7 @@ def convertMo2Fmu(
15921592
fmuType: str = "me_cs",
15931593
fileNamePrefix: Optional[str] = None,
15941594
includeResources: bool = True,
1595-
) -> str:
1595+
) -> pathlib.Path:
15961596
"""Translate the model into a Functional Mockup Unit.
15971597
15981598
Args:
@@ -1617,29 +1617,42 @@ def convertMo2Fmu(
16171617
properties = (f'version="{version}", fmuType="{fmuType}", '
16181618
f'fileNamePrefix="{fileNamePrefix}", includeResources={includeResourcesStr}')
16191619
fmu = self._requestApi(apiName='buildModelFMU', entity=self._model_name, properties=properties)
1620+
fmu_path = self._work_dir / fmu
16201621

16211622
# report proper error message
1622-
if not os.path.exists(fmu):
1623-
raise ModelicaSystemError(f"Missing FMU file: {fmu}")
1623+
if not fmu_path.is_file():
1624+
raise ModelicaSystemError(f"Missing FMU file: {fmu_path.as_posix()}")
16241625

1625-
return fmu
1626+
return fmu_path
16261627

16271628
# to convert FMU to Modelica model
1628-
def convertFmu2Mo(self, fmuName): # 20
1629+
def convertFmu2Mo(
1630+
self,
1631+
fmu: os.PathLike,
1632+
) -> pathlib.Path:
16291633
"""
1630-
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".
1634+
In order to load FMU, at first it needs to be translated into Modelica model. This method is used to generate
1635+
Modelica model from the given FMU. It generates "fmuName_me_FMU.mo".
16311636
Currently, it only supports Model Exchange conversion.
16321637
usage
16331638
>>> convertFmu2Mo("c:/BouncingBall.Fmu")
16341639
"""
16351640

1636-
fileName = self._requestApi(apiName='importFMU', entity=fmuName)
1641+
fmu_path = pathlib.Path(fmu)
1642+
1643+
filename = self._requestApi(apiName='importFMU', entity=fmu_path.as_posix())
1644+
filepath = self._work_dir / filename
16371645

16381646
# report proper error message
1639-
if not os.path.exists(fileName):
1640-
raise ModelicaSystemError(f"Missing file {fileName}")
1647+
if not filepath.is_file():
1648+
raise ModelicaSystemError(f"Missing file {filepath.as_posix()}")
1649+
1650+
self.model(
1651+
name=f"{fmu_path.stem}_me_FMU",
1652+
file=filepath,
1653+
)
16411654

1642-
return fileName
1655+
return filepath
16431656

16441657
def optimize(self) -> dict[str, Any]:
16451658
"""Perform model-based optimization.

tests/test_FMIImport.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import numpy as np
2+
import os
3+
import pytest
4+
import shutil
5+
6+
import OMPython
7+
8+
9+
@pytest.fixture
10+
def model_firstorder(tmp_path):
11+
mod = tmp_path / "M.mo"
12+
mod.write_text("""model M
13+
Real x(start = 1, fixed = true);
14+
parameter Real a = -1;
15+
equation
16+
der(x) = x*a;
17+
end M;
18+
""")
19+
return mod
20+
21+
22+
def test_FMIImport(model_firstorder):
23+
filePath = model_firstorder.as_posix()
24+
25+
# create model & simulate it
26+
mod1 = OMPython.ModelicaSystem()
27+
mod1.model(file=filePath, name="M")
28+
mod1.simulate()
29+
30+
# create FMU & check
31+
fmu = mod1.convertMo2Fmu(fileNamePrefix="M")
32+
assert os.path.exists(fmu)
33+
34+
# import FMU & check & simulate
35+
# TODO: why is '--allowNonStandardModelica=reinitInAlgorithms' needed? any example without this possible?
36+
mod2 = OMPython.ModelicaSystem(commandLineOptions=['--allowNonStandardModelica=reinitInAlgorithms'])
37+
mo = mod2.convertFmu2Mo(fmu=fmu)
38+
assert os.path.exists(mo)
39+
40+
mod2.simulate()
41+
42+
# get and verify result
43+
res1 = mod1.getSolutions(['time', 'x'])
44+
res2 = mod2.getSolutions(['time', 'x'])
45+
46+
# check last value for time
47+
assert res1[0][-1] == res2[0][-1] == 1.0
48+
# check last value for x
49+
assert np.isclose(res1[1][-1], 0.3678794515) # 0.36787945153397683
50+
assert np.isclose(res2[1][-1], 0.3678794515) # 0.3678794515707647
51+
52+
# cleanup
53+
tmp2 = mod1.getWorkDirectory()
54+
shutil.rmtree(tmp2, ignore_errors=True)
55+
56+
tmp2 = mod2.getWorkDirectory()
57+
shutil.rmtree(tmp2, ignore_errors=True)

0 commit comments

Comments
 (0)