Skip to content
1,947 changes: 1,109 additions & 838 deletions OMPython/ModelicaSystem.py

Large diffs are not rendered by default.

908 changes: 565 additions & 343 deletions OMPython/OMCSession.py

Large diffs are not rendered by default.

63 changes: 47 additions & 16 deletions OMPython/__init__.py
Original file line number Diff line number Diff line change
@@ -1,51 +1,82 @@
# -*- coding: utf-8 -*-
"""
OMPython is a Python interface to OpenModelica.
To get started, create an OMCSessionZMQ object:
from OMPython import OMCSessionZMQ
omc = OMCSessionZMQ()
To get started on a local OMC server, create an OMCSessionLocal object:

```
import OMPython
omc = OMPython.OMCSessionLocal()
omc.sendExpression("command")
```

"""

from OMPython.ModelicaSystem import (
LinearizationResult,
ModelicaSystem,
ModelicaSystemCmd,
ModelicaSystemOMC,
ModelExecutionCmd,
ModelicaSystemDoE,
ModelicaDoEOMC,
ModelicaSystemError,
ModelicaSystemRunner,
ModelicaDoERunner,

doe_get_solutions,
)
from OMPython.OMCSession import (
OMPathABC,
OMCPath,
OMCSession,

OMSessionRunner,

OMCSessionABC,

ModelExecutionData,
ModelExecutionException,

OMCSessionCmd,
OMCSessionException,
OMCSessionRunData,
OMCSessionZMQ,
OMCSessionPort,
OMCSessionLocal,
OMCSessionDocker,
OMCSessionDockerContainer,
OMCSessionException,
OMCSessionLocal,
OMCSessionPort,
OMCSessionWSL,
OMCSessionZMQ,
)

# global names imported if import 'from OMPython import *' is used
__all__ = [
'LinearizationResult',

'ModelExecutionData',
'ModelExecutionException',

'ModelicaSystem',
'ModelicaSystemCmd',
'ModelicaSystemOMC',
'ModelExecutionCmd',
'ModelicaSystemDoE',
'ModelicaDoEOMC',
'ModelicaSystemError',

'ModelicaSystemRunner',
'ModelicaDoERunner',

'OMPathABC',
'OMCPath',

'OMCSession',
'OMSessionRunner',

'OMCSessionABC',

'doe_get_solutions',

'OMCSessionCmd',
'OMCSessionDocker',
'OMCSessionDockerContainer',
'OMCSessionException',
'OMCSessionRunData',
'OMCSessionZMQ',
'OMCSessionPort',
'OMCSessionLocal',
'OMCSessionDocker',
'OMCSessionDockerContainer',
'OMCSessionWSL',
'OMCSessionZMQ',
]
4 changes: 2 additions & 2 deletions tests/test_FMIExport.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@


def test_CauerLowPassAnalog():
mod = OMPython.ModelicaSystem()
mod = OMPython.ModelicaSystemOMC()
mod.model(
model_name="Modelica.Electrical.Analog.Examples.CauerLowPassAnalog",
libraries=["Modelica"],
Expand All @@ -20,7 +20,7 @@ def test_CauerLowPassAnalog():


def test_DrumBoiler():
mod = OMPython.ModelicaSystem()
mod = OMPython.ModelicaSystemOMC()
mod.model(
model_name="Modelica.Fluid.Examples.DrumBoiler.DrumBoiler",
libraries=["Modelica"],
Expand Down
4 changes: 2 additions & 2 deletions tests/test_FMIImport.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def model_firstorder(tmp_path):

def test_FMIImport(model_firstorder):
# create model & simulate it
mod1 = OMPython.ModelicaSystem()
mod1 = OMPython.ModelicaSystemOMC()
mod1.model(
model_file=model_firstorder,
model_name="M",
Expand All @@ -35,7 +35,7 @@ def test_FMIImport(model_firstorder):

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

Expand Down
51 changes: 34 additions & 17 deletions tests/test_ModelicaSystemDoE.py → tests/test_ModelicaDoEOMC.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,56 +51,73 @@ def param_doe() -> dict[str, list]:
return param


def test_ModelicaSystemDoE_local(tmp_path, model_doe, param_doe):
def test_ModelicaDoEOMC_local(tmp_path, model_doe, param_doe):
tmpdir = tmp_path / 'DoE'
tmpdir.mkdir(exist_ok=True)

doe_mod = OMPython.ModelicaSystemDoE(
mod = OMPython.ModelicaSystemOMC()
mod.model(
model_file=model_doe,
model_name="M",
)

doe_mod = OMPython.ModelicaDoEOMC(
mod=mod,
parameters=param_doe,
resultpath=tmpdir,
simargs={"override": {'stopTime': 1.0}},
simargs={"override": {'stopTime': '1.0'}},
)

_run_ModelicaSystemDoe(doe_mod=doe_mod)
_run_ModelicaDoEOMC(doe_mod=doe_mod)


@skip_on_windows
@skip_python_older_312
def test_ModelicaSystemDoE_docker(tmp_path, model_doe, param_doe):
def test_ModelicaDoEOMC_docker(tmp_path, model_doe, param_doe):
omcs = OMPython.OMCSessionDocker(docker="openmodelica/openmodelica:v1.25.0-minimal")
assert omcs.sendExpression("getVersion()") == "OpenModelica 1.25.0"

doe_mod = OMPython.ModelicaSystemDoE(
mod = OMPython.ModelicaSystemOMC(
session=omcs,
)
mod.model(
model_file=model_doe,
model_name="M",
)

doe_mod = OMPython.ModelicaDoEOMC(
mod=mod,
parameters=param_doe,
session=omcs,
simargs={"override": {'stopTime': 1.0}},
simargs={"override": {'stopTime': '1.0'}},
)

_run_ModelicaSystemDoe(doe_mod=doe_mod)
_run_ModelicaDoEOMC(doe_mod=doe_mod)


@pytest.mark.skip(reason="Not able to run WSL on github")
@skip_python_older_312
def test_ModelicaSystemDoE_WSL(tmp_path, model_doe, param_doe):
tmpdir = tmp_path / 'DoE'
tmpdir.mkdir(exist_ok=True)
def test_ModelicaDoEOMC_WSL(tmp_path, model_doe, param_doe):
omcs = OMPython.OMCSessionWSL()
assert omcs.sendExpression("getVersion()") == "OpenModelica 1.25.0"

doe_mod = OMPython.ModelicaSystemDoE(
mod = OMPython.ModelicaSystemOMC(
session=omcs,
)
mod.model(
model_file=model_doe,
model_name="M",
)

doe_mod = OMPython.ModelicaDoEOMC(
mod=mod,
parameters=param_doe,
resultpath=tmpdir,
simargs={"override": {'stopTime': 1.0}},
simargs={"override": {'stopTime': '1.0'}},
)

_run_ModelicaSystemDoe(doe_mod=doe_mod)
_run_ModelicaDoEOMC(doe_mod=doe_mod)


def _run_ModelicaSystemDoe(doe_mod):
def _run_ModelicaDoEOMC(doe_mod):
doe_count = doe_mod.prepare()
assert doe_count == 16

Expand Down
158 changes: 158 additions & 0 deletions tests/test_ModelicaDoERunner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import pathlib
import sys

import numpy as np
import pytest

import OMPython

skip_python_older_312 = pytest.mark.skipif(
sys.version_info < (3, 12),
reason="OMCPath(non-local) only working for Python >= 3.12.",
)


@pytest.fixture
def model_doe(tmp_path: pathlib.Path) -> pathlib.Path:
# see: https://trac.openmodelica.org/OpenModelica/ticket/4052
mod = tmp_path / "M.mo"
# TODO: update for bool and string parameters; check if these can be used in DoE
mod.write_text("""
model M
parameter Integer p=1;
parameter Integer q=1;
parameter Real a = -1;
parameter Real b = -1;
Real x[p];
Real y[q];
equation
der(x) = a * fill(1.0, p);
der(y) = b * fill(1.0, q);
end M;
""")
return mod


@pytest.fixture
def param_doe() -> dict[str, list]:
param = {
# simple
'a': [5, 6],
'b': [7, 8],
}
return param


def test_ModelicaDoERunner_ModelicaSystemOMC(tmp_path, model_doe, param_doe):
tmpdir = tmp_path / 'DoE'
tmpdir.mkdir(exist_ok=True)

mod = OMPython.ModelicaSystemOMC()
mod.model(
model_file=model_doe,
model_name="M",
)

resultfile_mod = mod.getWorkDirectory() / f"{mod.get_model_name()}_res_mod.mat"
_run_simulation(mod=mod, resultfile=resultfile_mod, param=param_doe)

doe_mod = OMPython.ModelicaDoERunner(
mod=mod,
parameters=param_doe,
resultpath=tmpdir,
)

_run_ModelicaDoERunner(doe_mod=doe_mod)

_check_runner_result(mod=mod, doe_mod=doe_mod)


def test_ModelicaDoERunner_ModelicaSystemRunner(tmp_path, model_doe, param_doe):
tmpdir = tmp_path / 'DoE'
tmpdir.mkdir(exist_ok=True)

mod = OMPython.ModelicaSystemOMC()
mod.model(
model_file=model_doe,
model_name="M",
)

resultfile_mod = mod.getWorkDirectory() / f"{mod.get_model_name()}_res_mod.mat"
_run_simulation(mod=mod, resultfile=resultfile_mod, param=param_doe)

# run the model using only the runner class
omcs = OMPython.OMSessionRunner(
version=mod.get_session().get_version(),
)
modr = OMPython.ModelicaSystemRunner(
session=omcs,
work_directory=mod.getWorkDirectory(),
)
modr.setup(
model_name="M",
)
doe_mod = OMPython.ModelicaDoERunner(
mod=modr,
parameters=param_doe,
resultpath=tmpdir,
)

_run_ModelicaDoERunner(doe_mod=doe_mod)

_check_runner_result(mod=mod, doe_mod=doe_mod)


def _run_simulation(mod, resultfile, param):
simOptions = {"stopTime": 1.0, "stepSize": 0.1, "tolerance": 1e-8}
mod.setSimulationOptions(**simOptions)
mod.simulate(resultfile=resultfile)

assert resultfile.exists()


def _run_ModelicaDoERunner(doe_mod):
doe_count = doe_mod.prepare()
assert doe_count == 4

doe_def = doe_mod.get_doe_definition()
assert isinstance(doe_def, dict)
assert len(doe_def.keys()) == doe_count

doe_cmd = doe_mod.get_doe_command()
assert isinstance(doe_cmd, dict)
assert len(doe_cmd.keys()) == doe_count

doe_status = doe_mod.simulate()
assert doe_status is True


def _check_runner_result(mod, doe_mod):
doe_cmd = doe_mod.get_doe_command()
doe_def = doe_mod.get_doe_definition()

doe_sol = OMPython.doe_get_solutions(
msomc=mod,
resultpath=doe_mod.get_resultpath(),
doe_def=doe_def,
)
assert isinstance(doe_sol, dict)
assert len(doe_sol.keys()) == len(doe_cmd.keys())

assert sorted(doe_def.keys()) == sorted(doe_cmd.keys())
assert sorted(doe_cmd.keys()) == sorted(doe_sol.keys())

for resultfilename in doe_def:
row = doe_def[resultfilename]

assert resultfilename in doe_sol
sol = doe_sol[resultfilename]

var_dict = {
# simple / non-structural parameters
'a': float(row['a']),
'b': float(row['b']),
}

for var in var_dict:
assert var in sol['data']
assert np.isclose(sol['data'][var][-1], var_dict[var])
Loading