Skip to content

Commit e72ab43

Browse files
authored
Merge branch 'master' into improve_exception
2 parents 4cddc90 + 83552d8 commit e72ab43

5 files changed

Lines changed: 61 additions & 29 deletions

File tree

OMPython/ModelicaSystem.py

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import logging
3939
import numbers
4040
import os
41+
import pathlib
4142
import queue
4243
import textwrap
4344
import threading
@@ -322,6 +323,10 @@ def parse_simflags(simflags: str) -> dict[str, Optional[str | dict[str, Any] | n
322323

323324

324325
class ModelicaSystem:
326+
"""
327+
Class to simulate a Modelica model using OpenModelica via OMCSessionZMQ.
328+
"""
329+
325330
def __init__(
326331
self,
327332
commandLineOptions: Optional[list[str]] = None,
@@ -446,18 +451,30 @@ def model(
446451
# set variables
447452
self._model_name = name # Model class name
448453
self._libraries = libraries # may be needed if model is derived from other model
449-
if file is not None:
450-
file_name = self._session.omcpath(file).resolve()
451-
else:
452-
file_name = None
453-
self._file_name = file_name # Model file/package name
454454
self._variable_filter = variable_filter
455455

456-
if self._file_name is not None and not self._file_name.is_file(): # if file does not exist
457-
raise IOError(f"{self._file_name} does not exist!")
458-
459456
if self._libraries:
460457
self._loadLibrary(libraries=self._libraries)
458+
459+
self._file_name = None
460+
if file is not None:
461+
file_path = pathlib.Path(file)
462+
# special handling for OMCProcessLocal - consider a relative path
463+
if isinstance(self._session.omc_process, OMCProcessLocal) and not file_path.is_absolute():
464+
file_path = pathlib.Path.cwd() / file_path
465+
if not file_path.is_file():
466+
raise IOError(f"Model file {file_path} does not exist!")
467+
468+
self._file_name = self.getWorkDirectory() / file_path.name
469+
if (isinstance(self._session.omc_process, OMCProcessLocal)
470+
and file_path.as_posix() == self._file_name.as_posix()):
471+
pass
472+
elif self._file_name.is_file():
473+
raise IOError(f"Simulation model file {self._file_name} exist - not overwriting!")
474+
else:
475+
content = file_path.read_text(encoding='utf-8')
476+
self._file_name.write_text(content)
477+
461478
if self._file_name is not None:
462479
self._loadFile(fileName=self._file_name)
463480

@@ -1685,7 +1702,7 @@ def convertFmu2Mo(
16851702
raise ModelicaSystemError(f"Missing FMU file: {fmu_path.as_posix()}")
16861703

16871704
filename = self._requestApi(apiName='importFMU', entity=fmu_path.as_posix())
1688-
filepath = self._work_dir / filename
1705+
filepath = self.getWorkDirectory() / filename
16891706

16901707
# report proper error message
16911708
if not filepath.is_file():
@@ -2037,7 +2054,7 @@ def prepare(self) -> int:
20372054

20382055
pk_value = pc_structure[idx_structure]
20392056
if isinstance(pk_value, str):
2040-
pk_value_str = pk_value.replace('"', '\\"')
2057+
pk_value_str = self.session().escape_str(pk_value)
20412058
expression = f"setParameterValue({self._model_name}, {pk_structure}, \"{pk_value_str}\")"
20422059
elif isinstance(pk_value, bool):
20432060
pk_value_bool_str = "true" if pk_value else "false"

OMPython/OMCSession.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ def write_text(self, data: str, encoding=None, errors=None, newline=None):
336336
if not isinstance(data, str):
337337
raise TypeError(f"data must be str, not {data.__class__.__name__}")
338338

339-
data_omc = data.replace('"', '\\"')
339+
data_omc = self._session.escape_str(data)
340340
self._session.sendExpression(f'writeFile("{self.as_posix()}", "{data_omc}", false);')
341341

342342
return len(data)
@@ -576,6 +576,13 @@ def __del__(self):
576576

577577
self.omc_zmq = None
578578

579+
@staticmethod
580+
def escape_str(value: str) -> str:
581+
"""
582+
Escape a string such that it can be used as string within OMC expressions, i.e. escape all double quotes.
583+
"""
584+
return value.replace("\\", "\\\\").replace('"', '\\"')
585+
579586
def omcpath(self, *path) -> OMCPath:
580587
"""
581588
Create an OMCPath object based on the given path segments and the current OMC session.

tests/test_ModelicaSystem.py

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,11 @@ def worker():
5151

5252
def test_setParameters():
5353
omc = OMPython.OMCSessionZMQ()
54-
model_path = omc.sendExpression("getInstallationDirectoryPath()") + "/share/doc/omc/testmodels/"
54+
model_path_str = omc.sendExpression("getInstallationDirectoryPath()") + "/share/doc/omc/testmodels"
55+
model_path = omc.omcpath(model_path_str)
5556
mod = OMPython.ModelicaSystem()
5657
mod.model(
57-
file=model_path + "BouncingBall.mo",
58+
file=model_path / "BouncingBall.mo",
5859
name="BouncingBall",
5960
)
6061

@@ -85,10 +86,11 @@ def test_setParameters():
8586

8687
def test_setSimulationOptions():
8788
omc = OMPython.OMCSessionZMQ()
88-
model_path = omc.sendExpression("getInstallationDirectoryPath()") + "/share/doc/omc/testmodels/"
89+
model_path_str = omc.sendExpression("getInstallationDirectoryPath()") + "/share/doc/omc/testmodels"
90+
model_path = omc.omcpath(model_path_str)
8991
mod = OMPython.ModelicaSystem()
9092
mod.model(
91-
file=model_path + "BouncingBall.mo",
93+
file=model_path / "BouncingBall.mo",
9294
name="BouncingBall",
9395
)
9496

@@ -112,7 +114,6 @@ def test_setSimulationOptions():
112114
assert d["tolerance"] == "1.2e-08"
113115

114116

115-
@pytest.mark.skip("will fail / fix available")
116117
def test_relative_path(model_firstorder):
117118
cwd = pathlib.Path.cwd()
118119
(fd, name) = tempfile.mkstemp(prefix='tmpOMPython.tests', dir=cwd, text=True)
@@ -152,30 +153,25 @@ def test_customBuildDirectory(tmp_path, model_firstorder):
152153

153154
@skip_on_windows
154155
@skip_python_older_312
155-
def test_getSolutions_docker(model_firstorder_content):
156+
def test_getSolutions_docker(model_firstorder):
156157
omcp = OMPython.OMCProcessDocker(docker="openmodelica/openmodelica:v1.25.0-minimal")
157158
omc = OMPython.OMCSessionZMQ(omc_process=omcp)
158159

159-
modelpath = omc.omcpath_tempdir() / 'M.mo'
160-
modelpath.write_text(model_firstorder_content)
161-
162-
file_path = pathlib.Path(modelpath)
163160
mod = OMPython.ModelicaSystem(
164161
omc_process=omc.omc_process,
165162
)
166163
mod.model(
167164
name="M",
168-
file=file_path,
165+
file=model_firstorder.as_posix(),
169166
)
170167

171168
_run_getSolutions(mod)
172169

173170

174171
def test_getSolutions(model_firstorder):
175-
filePath = model_firstorder.as_posix()
176172
mod = OMPython.ModelicaSystem()
177173
mod.model(
178-
file=filePath,
174+
file=model_firstorder.as_posix(),
179175
name="M",
180176
)
181177

tests/test_ModelicaSystemDoE.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,15 +71,13 @@ def test_ModelicaSystemDoE_docker(tmp_path, model_doe, param_doe):
7171
omc = OMPython.OMCSessionZMQ(omc_process=omcp)
7272
assert omc.sendExpression("getVersion()") == "OpenModelica 1.25.0"
7373

74-
modelpath = omc.omcpath_tempdir() / 'M.mo'
75-
modelpath.write_text(model_doe.read_text())
76-
74+
modelpath = omc.omcpath_tempdir()
7775
doe_mod = OMPython.ModelicaSystemDoE(
78-
fileName=modelpath.as_posix(),
76+
fileName=model_doe.as_posix(),
7977
modelName="M",
8078
parameters=param_doe,
8179
omc_process=omcp,
82-
resultpath=modelpath.parent,
80+
resultpath=modelpath,
8381
simargs={"override": {'stopTime': 1.0}},
8482
)
8583

tests/test_OMCPath.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,17 @@ def _run_OMCPath_checks(om: OMPython.OMCSessionZMQ):
7676
assert p3.parent.is_dir()
7777
p3.unlink()
7878
assert p3.is_file() is False
79+
80+
81+
def test_OMCPath_write_file(tmpdir):
82+
om = OMPython.OMCSessionZMQ()
83+
84+
data = "abc # \\t # \" # \\n # xyz"
85+
86+
p1 = om.omcpath_tempdir()
87+
p2 = p1 / 'test.txt'
88+
p2.write_text(data=data)
89+
90+
assert data == p2.read_text()
91+
92+
del om

0 commit comments

Comments
 (0)