Skip to content

Commit 82feb84

Browse files
committed
Merge branch 'ModelicaSystemDoE_use_OMCPath' into merge_all
2 parents b216ee0 + 36443e9 commit 82feb84

9 files changed

Lines changed: 1292 additions & 181 deletions

OMPython/ModelicaSystem.py

Lines changed: 559 additions & 149 deletions
Large diffs are not rendered by default.

OMPython/OMCSession.py

Lines changed: 446 additions & 20 deletions
Large diffs are not rendered by default.

OMPython/__init__.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@
3636
CONDITIONS OF OSMC-PL.
3737
"""
3838

39-
from OMPython.ModelicaSystem import LinearizationResult, ModelicaSystem, ModelicaSystemCmd, ModelicaSystemError
40-
from OMPython.OMCSession import (OMCSessionCmd, OMCSessionException, OMCSessionZMQ,
39+
from OMPython.ModelicaSystem import (LinearizationResult, ModelicaSystem, ModelicaSystemCmd, ModelicaSystemDoE,
40+
ModelicaSystemError)
41+
from OMPython.OMCSession import (OMCSessionCmd, OMCSessionException, OMCSessionRunData, OMCSessionZMQ,
4142
OMCProcessPort, OMCProcessLocal, OMCProcessDocker, OMCProcessDockerContainer,
4243
OMCProcessWSL)
4344

@@ -46,10 +47,12 @@
4647
'LinearizationResult',
4748
'ModelicaSystem',
4849
'ModelicaSystemCmd',
50+
'ModelicaSystemDoE',
4951
'ModelicaSystemError',
5052

5153
'OMCSessionCmd',
5254
'OMCSessionException',
55+
'OMCSessionRunData',
5356
'OMCSessionZMQ',
5457
'OMCProcessPort',
5558
'OMCProcessLocal',

tests/test_FMIExport.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import OMPython
22
import shutil
33
import os
4+
import pathlib
45

56

67
def test_CauerLowPassAnalog():
78
mod = OMPython.ModelicaSystem(modelName="Modelica.Electrical.Analog.Examples.CauerLowPassAnalog",
89
lmodel=["Modelica"])
9-
tmp = mod.getWorkDirectory()
10+
tmp = pathlib.Path(mod.getWorkDirectory())
1011
try:
1112
fmu = mod.convertMo2Fmu(fileNamePrefix="CauerLowPassAnalog")
1213
assert os.path.exists(fmu)
@@ -16,7 +17,7 @@ def test_CauerLowPassAnalog():
1617

1718
def test_DrumBoiler():
1819
mod = OMPython.ModelicaSystem(modelName="Modelica.Fluid.Examples.DrumBoiler.DrumBoiler", lmodel=["Modelica"])
19-
tmp = mod.getWorkDirectory()
20+
tmp = pathlib.Path(mod.getWorkDirectory())
2021
try:
2122
fmu = mod.convertMo2Fmu(fileNamePrefix="DrumBoiler")
2223
assert os.path.exists(fmu)

tests/test_ModelicaSystem.py

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,36 @@
22
import os
33
import pathlib
44
import pytest
5+
import sys
56
import tempfile
67
import numpy as np
78

9+
skip_on_windows = pytest.mark.skipif(
10+
sys.platform.startswith("win"),
11+
reason="OpenModelica Docker image is Linux-only; skipping on Windows.",
12+
)
13+
14+
skip_python_older_312 = pytest.mark.skipif(
15+
sys.version_info < (3, 12),
16+
reason="OMCPath(non-local) only working for Python >= 3.12.",
17+
)
18+
819

920
@pytest.fixture
10-
def model_firstorder(tmp_path):
11-
mod = tmp_path / "M.mo"
12-
mod.write_text("""model M
21+
def model_firstorder_content():
22+
return ("""model M
1323
Real x(start = 1, fixed = true);
1424
parameter Real a = -1;
1525
equation
1626
der(x) = x*a;
1727
end M;
1828
""")
29+
30+
31+
@pytest.fixture
32+
def model_firstorder(tmp_path, model_firstorder_content):
33+
mod = tmp_path / "M.mo"
34+
mod.write_text(model_firstorder_content)
1935
return mod
2036

2137

@@ -106,16 +122,40 @@ def test_customBuildDirectory(tmp_path, model_firstorder):
106122
tmpdir = tmp_path / "tmpdir1"
107123
tmpdir.mkdir()
108124
m = OMPython.ModelicaSystem(filePath, "M", customBuildDirectory=tmpdir)
109-
assert m.getWorkDirectory().resolve() == tmpdir.resolve()
125+
assert pathlib.Path(m.getWorkDirectory()).resolve() == tmpdir.resolve()
110126
result_file = tmpdir / "a.mat"
111127
assert not result_file.exists()
112128
m.simulate(resultfile="a.mat")
113129
assert result_file.is_file()
114130

115131

132+
@skip_on_windows
133+
@skip_python_older_312
134+
def test_getSolutions_docker(model_firstorder_content):
135+
omcp = OMPython.OMCProcessDocker(docker="openmodelica/openmodelica:v1.25.0-minimal")
136+
omc = OMPython.OMCSessionZMQ(omc_process=omcp)
137+
138+
modelpath = omc.omcpath_tempdir() / 'M.mo'
139+
modelpath.write_text(model_firstorder_content)
140+
141+
file_path = pathlib.Path(modelpath)
142+
mod = OMPython.ModelicaSystem(
143+
fileName=file_path,
144+
modelName="M",
145+
omc_process=omc.omc_process,
146+
)
147+
148+
_run_getSolutions(mod)
149+
150+
116151
def test_getSolutions(model_firstorder):
117152
filePath = model_firstorder.as_posix()
118153
mod = OMPython.ModelicaSystem(filePath, "M")
154+
155+
_run_getSolutions(mod)
156+
157+
158+
def _run_getSolutions(mod):
119159
x0 = 1
120160
a = -1
121161
tau = -1 / a

tests/test_ModelicaSystemCmd.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@ def model_firstorder(tmp_path):
1818
@pytest.fixture
1919
def mscmd_firstorder(model_firstorder):
2020
mod = OMPython.ModelicaSystem(fileName=model_firstorder.as_posix(), modelName="M")
21-
mscmd = OMPython.ModelicaSystemCmd(runpath=mod.getWorkDirectory(), modelname=mod._model_name)
21+
mscmd = OMPython.ModelicaSystemCmd(
22+
session=mod._getconn,
23+
runpath=mod.getWorkDirectory(),
24+
modelname=mod._model_name,
25+
)
2226
return mscmd
2327

2428

@@ -32,8 +36,7 @@ def test_simflags(mscmd_firstorder):
3236
with pytest.deprecated_call():
3337
mscmd.args_set(args=mscmd.parse_simflags(simflags="-noEventEmit -noRestart -override=a=1,x=3"))
3438

35-
assert mscmd.get_cmd() == [
36-
mscmd.get_exe().as_posix(),
39+
assert mscmd.get_cmd_args() == [
3740
'-noEventEmit',
3841
'-noRestart',
3942
'-override=a=1,b=2,x=3',

tests/test_ModelicaSystemDoE.py

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import numpy as np
2+
import OMPython
3+
import pathlib
4+
import pytest
5+
import sys
6+
7+
skip_on_windows = pytest.mark.skipif(
8+
sys.platform.startswith("win"),
9+
reason="OpenModelica Docker image is Linux-only; skipping on Windows.",
10+
)
11+
12+
skip_python_older_312 = pytest.mark.skipif(
13+
sys.version_info < (3, 12),
14+
reason="OMCPath(non-local) only working for Python >= 3.12.",
15+
)
16+
17+
18+
@pytest.fixture
19+
def model_doe(tmp_path: pathlib.Path) -> pathlib.Path:
20+
# see: https://trac.openmodelica.org/OpenModelica/ticket/4052
21+
mod = tmp_path / "M.mo"
22+
# TODO: update for bool and string parameters; check if these can be used in DoE
23+
mod.write_text("""
24+
model M
25+
parameter Integer p=1;
26+
parameter Integer q=1;
27+
parameter Real a = -1;
28+
parameter Real b = -1;
29+
Real x[p];
30+
Real y[q];
31+
equation
32+
der(x) = a * fill(1.0, p);
33+
der(y) = b * fill(1.0, q);
34+
end M;
35+
""")
36+
return mod
37+
38+
39+
@pytest.fixture
40+
def param_doe() -> dict[str, list]:
41+
param = {
42+
# structural
43+
'p': [1, 2],
44+
'q': [3, 4],
45+
# simple
46+
'a': [5, 6],
47+
'b': [7, 8],
48+
}
49+
return param
50+
51+
52+
def test_ModelicaSystemDoE_local(tmp_path, model_doe, param_doe):
53+
tmpdir = tmp_path / 'DoE'
54+
tmpdir.mkdir(exist_ok=True)
55+
56+
doe_mod = OMPython.ModelicaSystemDoE(
57+
fileName=model_doe.as_posix(),
58+
modelName="M",
59+
parameters=param_doe,
60+
resultpath=tmpdir,
61+
simargs={"override": {'stopTime': 1.0}},
62+
)
63+
64+
_run_ModelicaSystemDoe(doe_mod=doe_mod)
65+
66+
67+
@skip_on_windows
68+
@skip_python_older_312
69+
def test_ModelicaSystemDoE_docker(tmp_path, model_doe, param_doe):
70+
omcp = OMPython.OMCProcessDocker(docker="openmodelica/openmodelica:v1.25.0-minimal")
71+
omc = OMPython.OMCSessionZMQ(omc_process=omcp)
72+
assert omc.sendExpression("getVersion()") == "OpenModelica 1.25.0"
73+
74+
modelpath = omc.omcpath_tempdir() / 'M.mo'
75+
modelpath.write_text(model_doe.read_text())
76+
77+
doe_mod = OMPython.ModelicaSystemDoE(
78+
fileName=modelpath.as_posix(),
79+
modelName="M",
80+
parameters=param_doe,
81+
omc_process=omcp,
82+
resultpath=modelpath.parent,
83+
simargs={"override": {'stopTime': 1.0}},
84+
)
85+
86+
_run_ModelicaSystemDoe(doe_mod=doe_mod)
87+
88+
89+
@pytest.mark.skip(reason="Not able to run WSL on github")
90+
@skip_python_older_312
91+
def test_ModelicaSystemDoE_WSL(tmp_path, model_doe, param_doe):
92+
tmpdir = tmp_path / 'DoE'
93+
tmpdir.mkdir(exist_ok=True)
94+
95+
doe_mod = OMPython.ModelicaSystemDoE(
96+
fileName=model_doe.as_posix(),
97+
modelName="M",
98+
parameters=param_doe,
99+
resultpath=tmpdir,
100+
simargs={"override": {'stopTime': 1.0}},
101+
)
102+
103+
_run_ModelicaSystemDoe(doe_mod=doe_mod)
104+
105+
106+
def _run_ModelicaSystemDoe(doe_mod):
107+
doe_count = doe_mod.prepare()
108+
assert doe_count == 16
109+
110+
doe_def = doe_mod.get_doe_definition()
111+
assert isinstance(doe_def, dict)
112+
assert len(doe_def.keys()) == doe_count
113+
114+
doe_cmd = doe_mod.get_doe_command()
115+
assert isinstance(doe_cmd, dict)
116+
assert len(doe_cmd.keys()) == doe_count
117+
118+
doe_status = doe_mod.simulate()
119+
assert doe_status is True
120+
121+
doe_sol = doe_mod.get_doe_solutions()
122+
assert isinstance(doe_sol, dict)
123+
assert len(doe_sol.keys()) == doe_count
124+
125+
assert sorted(doe_def.keys()) == sorted(doe_cmd.keys())
126+
assert sorted(doe_cmd.keys()) == sorted(doe_sol.keys())
127+
128+
for resultfilename in doe_def:
129+
row = doe_def[resultfilename]
130+
131+
assert resultfilename in doe_sol
132+
sol = doe_sol[resultfilename]
133+
134+
var_dict = {
135+
# simple / non-structural parameters
136+
'a': float(row['a']),
137+
'b': float(row['b']),
138+
# structural parameters
139+
'p': float(row['p']),
140+
'q': float(row['q']),
141+
# variables using the structural parameters
142+
f"x[{row['p']}]": float(row['a']),
143+
f"y[{row['p']}]": float(row['b']),
144+
}
145+
146+
for var in var_dict:
147+
assert var in sol['data']
148+
assert np.isclose(sol['data'][var][-1], var_dict[var])

tests/test_OMCPath.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import sys
2+
import OMPython
3+
import pytest
4+
5+
skip_on_windows = pytest.mark.skipif(
6+
sys.platform.startswith("win"),
7+
reason="OpenModelica Docker image is Linux-only; skipping on Windows.",
8+
)
9+
10+
skip_python_older_312 = pytest.mark.skipif(
11+
sys.version_info < (3, 12),
12+
reason="OMCPath(non-local) only working for Python >= 3.12.",
13+
)
14+
15+
16+
def test_OMCPath_OMCSessionZMQ():
17+
om = OMPython.OMCSessionZMQ()
18+
19+
_run_OMCPath_checks(om)
20+
21+
del om
22+
23+
24+
def test_OMCPath_OMCProcessLocal():
25+
omp = OMPython.OMCProcessLocal()
26+
om = OMPython.OMCSessionZMQ(omc_process=omp)
27+
28+
_run_OMCPath_checks(om)
29+
30+
del om
31+
32+
33+
@skip_on_windows
34+
@skip_python_older_312
35+
def test_OMCPath_OMCProcessDocker():
36+
omcp = OMPython.OMCProcessDocker(docker="openmodelica/openmodelica:v1.25.0-minimal")
37+
om = OMPython.OMCSessionZMQ(omc_process=omcp)
38+
assert om.sendExpression("getVersion()") == "OpenModelica 1.25.0"
39+
40+
_run_OMCPath_checks(om)
41+
42+
del omcp
43+
del om
44+
45+
46+
@pytest.mark.skip(reason="Not able to run WSL on github")
47+
@skip_python_older_312
48+
def test_OMCPath_OMCProcessWSL():
49+
omcp = OMPython.OMCProcessWSL(
50+
wsl_omc='omc',
51+
wsl_user='omc',
52+
timeout=30.0,
53+
)
54+
om = OMPython.OMCSessionZMQ(omc_process=omcp)
55+
56+
_run_OMCPath_checks(om)
57+
58+
del omcp
59+
del om
60+
61+
62+
def _run_OMCPath_checks(om: OMPython.OMCSessionZMQ):
63+
p1 = om.omcpath_tempdir()
64+
p2 = p1 / 'test'
65+
p2.mkdir()
66+
assert p2.is_dir()
67+
p3 = p2 / '..' / p2.name / 'test.txt'
68+
assert p3.is_file() is False
69+
assert p3.write_text('test')
70+
assert p3.is_file()
71+
assert p3.size() > 0
72+
p3 = p3.resolve().absolute()
73+
assert str(p3) == str((p2 / 'test.txt').resolve().absolute())
74+
assert p3.read_text() == "test"
75+
assert p3.is_file()
76+
assert p3.parent.is_dir()
77+
p3.unlink()
78+
assert p3.is_file() is False

tests/test_optimization.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ def test_optimization_example(tmp_path):
5050

5151
r = mod.optimize()
5252
# it is necessary to specify resultfile, otherwise it wouldn't find it.
53-
time, f, v = mod.getSolutions(["time", "f", "v"], resultfile=r["resultFile"])
53+
resultfile_str = r["resultFile"]
54+
resultfile_omcpath = mod._getconn.omcpath(resultfile_str)
55+
time, f, v = mod.getSolutions(["time", "f", "v"], resultfile=resultfile_omcpath.as_posix())
5456
assert np.isclose(f[0], 10)
5557
assert np.isclose(f[-1], -10)
5658

0 commit comments

Comments
 (0)