From cf7326f76acaee8821831e5ba1c0b643c633e499 Mon Sep 17 00:00:00 2001 From: syntron Date: Sun, 24 Aug 2025 21:30:28 +0200 Subject: [PATCH 1/7] [ModelicaSystem] update convertMo2Fmu() and convertFmu2Mo() to use pathlib.Path() --- OMPython/ModelicaSystem.py | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index bed1ed8f..1be04588 100644 --- a/OMPython/ModelicaSystem.py +++ b/OMPython/ModelicaSystem.py @@ -1631,7 +1631,7 @@ def convertMo2Fmu( fmuType: str = "me_cs", fileNamePrefix: Optional[str] = None, includeResources: bool = True, - ) -> str: + ) -> pathlib.Path: """Translate the model into a Functional Mockup Unit. Args: @@ -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 = pathlib.Path(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.as_posix()}") - return fmu + return fmu_path # to convert FMU to Modelica model - def convertFmu2Mo(self, fmuName): # 20 + def convertFmu2Mo( + self, + fmuName: os.PathLike, + ) -> pathlib.Path: """ 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". @@ -1675,13 +1679,16 @@ def convertFmu2Mo(self, fmuName): # 20 >>> convertFmu2Mo("c:/BouncingBall.Fmu") """ - fileName = self._requestApi(apiName='importFMU', entity=fmuName) + fmu_path = pathlib.Path(fmuName) + + filename = self._requestApi(apiName='importFMU', entity=fmu_path.as_posix()) + filepath = pathlib.Path(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()}") - return fileName + return filepath def optimize(self) -> dict[str, Any]: """Perform model-based optimization. From 3bbf782c74b4b0eeaabee32c5d2f89253c018e4e Mon Sep 17 00:00:00 2001 From: syntron Date: Sun, 24 Aug 2025 21:38:27 +0200 Subject: [PATCH 2/7] [ModelicaSystem] define convertFmu2Mo() as entry point like definition() * rename fmuName => fmu --- OMPython/ModelicaSystem.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index 1be04588..817a1510 100644 --- a/OMPython/ModelicaSystem.py +++ b/OMPython/ModelicaSystem.py @@ -1669,7 +1669,7 @@ def convertMo2Fmu( # to convert FMU to Modelica model def convertFmu2Mo( self, - fmuName: os.PathLike, + fmu: os.PathLike, ) -> pathlib.Path: """ In order to load FMU, at first it needs to be translated into Modelica model. This method is used to generate @@ -1679,7 +1679,7 @@ def convertFmu2Mo( >>> convertFmu2Mo("c:/BouncingBall.Fmu") """ - fmu_path = pathlib.Path(fmuName) + fmu_path = pathlib.Path(fmu) filename = self._requestApi(apiName='importFMU', entity=fmu_path.as_posix()) filepath = pathlib.Path(filename) @@ -1688,6 +1688,11 @@ def convertFmu2Mo( 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 filepath def optimize(self) -> dict[str, Any]: From 2006ffa3fe59fb51599a1bcea77903a33ba1d409 Mon Sep 17 00:00:00 2001 From: syntron Date: Sun, 24 Aug 2025 22:04:25 +0200 Subject: [PATCH 3/7] [ModelicaSystem] fix definition of fmu_path --- OMPython/ModelicaSystem.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index 817a1510..41809452 100644 --- a/OMPython/ModelicaSystem.py +++ b/OMPython/ModelicaSystem.py @@ -1658,11 +1658,11 @@ 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 = pathlib.Path(fmu) + fmu_path = self._work_dir / fmu # report proper error message if not fmu_path.is_file(): - raise ModelicaSystemError(f"Missing FMU file: {fmu.as_posix()}") + raise ModelicaSystemError(f"Missing FMU file: {fmu_path.as_posix()}") return fmu_path From 972fb1862ec53640c80fd79f29aad1af0f0d3871 Mon Sep 17 00:00:00 2001 From: syntron Date: Sun, 24 Aug 2025 22:17:07 +0200 Subject: [PATCH 4/7] [ModelicaSystem] fix path in convertFmu2Mo() --- OMPython/ModelicaSystem.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index 41809452..4323d295 100644 --- a/OMPython/ModelicaSystem.py +++ b/OMPython/ModelicaSystem.py @@ -1658,7 +1658,7 @@ 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._work_dir / fmu + fmu_path = self._getconn.omcpath(fmu) # report proper error message if not fmu_path.is_file(): @@ -1682,7 +1682,7 @@ def convertFmu2Mo( fmu_path = pathlib.Path(fmu) filename = self._requestApi(apiName='importFMU', entity=fmu_path.as_posix()) - filepath = pathlib.Path(filename) + filepath = self._work_dir / filename # report proper error message if not filepath.is_file(): From a26b7979829f856c374d559a267e71c84ab33a9f Mon Sep 17 00:00:00 2001 From: syntron Date: Mon, 25 Aug 2025 09:19:52 +0200 Subject: [PATCH 5/7] [test_FMIImport] running example / test for convertFmu2Mo() --- tests/test_FMIImport.py | 57 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 tests/test_FMIImport.py diff --git a/tests/test_FMIImport.py b/tests/test_FMIImport.py new file mode 100644 index 00000000..81167a9e --- /dev/null +++ b/tests/test_FMIImport.py @@ -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) From 84797256667630cf1701e9e03b7070388bf13890 Mon Sep 17 00:00:00 2001 From: syntron Date: Tue, 4 Nov 2025 12:19:15 +0100 Subject: [PATCH 6/7] [ModelicaSystem] replace pathlib by OMCPath --- OMPython/ModelicaSystem.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index 4323d295..89165ff3 100644 --- a/OMPython/ModelicaSystem.py +++ b/OMPython/ModelicaSystem.py @@ -1631,7 +1631,7 @@ def convertMo2Fmu( fmuType: str = "me_cs", fileNamePrefix: Optional[str] = None, includeResources: bool = True, - ) -> pathlib.Path: + ) -> OMCPath: """Translate the model into a Functional Mockup Unit. Args: @@ -1670,7 +1670,7 @@ def convertMo2Fmu( def convertFmu2Mo( self, fmu: os.PathLike, - ) -> pathlib.Path: + ) -> 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". @@ -1679,7 +1679,10 @@ def convertFmu2Mo( >>> convertFmu2Mo("c:/BouncingBall.Fmu") """ - fmu_path = pathlib.Path(fmu) + fmu_path = self._getconn.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 From 84d8379da39fa6ee553274f6f5f24e8bde402345 Mon Sep 17 00:00:00 2001 From: syntron Date: Thu, 6 Nov 2025 15:31:49 +0100 Subject: [PATCH 7/7] [ModelicaSystem] fix _getconn => _session --- OMPython/ModelicaSystem.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index 89165ff3..79ef78a8 100644 --- a/OMPython/ModelicaSystem.py +++ b/OMPython/ModelicaSystem.py @@ -1658,7 +1658,7 @@ 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._getconn.omcpath(fmu) + fmu_path = self._session.omcpath(fmu) # report proper error message if not fmu_path.is_file(): @@ -1679,7 +1679,7 @@ def convertFmu2Mo( >>> convertFmu2Mo("c:/BouncingBall.Fmu") """ - fmu_path = self._getconn.omcpath(fmu) + fmu_path = self._session.omcpath(fmu) if not fmu_path.is_file(): raise ModelicaSystemError(f"Missing FMU file: {fmu_path.as_posix()}")