Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
d0b6fe2
rename classes: *Process* => *Session*
syntron Nov 23, 2025
e4fd725
[ModelicaSystem*] omc_process => session
syntron Nov 23, 2025
fa448ea
[OMCSession*] fix docstrings
syntron Nov 23, 2025
fc04903
[ModelicaSystem*] remove dependency on depreciated OMCSessionZMQ
syntron Nov 23, 2025
6313dd6
[OMCSessionCmd] use OMCSession (old OMCProcess)
syntron Nov 23, 2025
3ccb5fd
[ModelicaSystem] fix initialisation of default OMCSession - use *Local
syntron Nov 26, 2025
fa97850
[unittests] use new definitions / remove OMCSessionZMQ
syntron Nov 23, 2025
8b19a46
[OMCSessionPort] add missing function / catch possible errors
syntron Nov 23, 2025
dd67406
[OMCSessionPort] fix exception message
syntron Nov 26, 2025
331e0a3
[OMCSession] improve logging
syntron Nov 23, 2025
73a38e5
[OMCSession*] define set_timeout()
syntron Nov 26, 2025
fe1775e
[OMCSession*] align all usages of timeout to the same structure
syntron Nov 25, 2025
938a124
[OMCSession*] simplify code for timeout loops
syntron Nov 26, 2025
9ad6f7c
add OMCPath to the public interface
syntron Nov 27, 2025
97e8333
[OMCSession] fix definiton of _timeout variable - use set_timeout() c…
syntron Nov 27, 2025
033e3a8
use keyword arguments if possible (FKA100 - flake8-force-keyword-argu…
syntron Nov 27, 2025
cbb6e56
[OMCSession*] some additional cleanup (mypy / flake8)
syntron Nov 27, 2025
223c895
[OMCSession] move call to set_timeout() to __post_init__
syntron Nov 27, 2025
fc43bdf
update README.md - replace OMCSessionZMQ with OMCSessionLocal
syntron Nov 27, 2025
589b17e
Merge branch 'OMCSessionPort' into v4.1.0-syntron
syntron Nov 28, 2025
b2499f1
Merge branch 'OMCSession_logging' into v4.1.0-syntron
syntron Nov 28, 2025
0ff9630
Merge branch 'linter_fix' into v4.1.0-syntron
syntron Nov 28, 2025
304e117
Merge branch 'OMCPath_to_init' into v4.1.0-syntron
syntron Nov 28, 2025
b1aba64
Merge branch 'ModelicaSystem_timeout' into v4.1.0-syntron
syntron Nov 28, 2025
afa3970
[OMCSessionZMQ] remove depreciated function execute()
syntron Jun 17, 2025
c682058
[ModelicaSystemCmd] remove depreciated simflags
syntron Jun 17, 2025
30c29b8
[test_ModelicaSystemCmd] update test_simflags
syntron Oct 31, 2025
5c76069
fix test_ZMQ
syntron Nov 29, 2025
5c667ae
keep 'import warnings'
syntron Nov 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 29 additions & 90 deletions OMPython/ModelicaSystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,8 @@
from OMPython.OMCSession import (
OMCSessionException,
OMCSessionRunData,
OMCSessionZMQ,
OMCProcess,
OMCProcessLocal,
OMCSession,
OMCSessionLocal,
OMCPath,
)

Expand Down Expand Up @@ -127,7 +126,7 @@ class ModelicaSystemCmd:

def __init__(
self,
session: OMCSessionZMQ,
session: OMCSession,
runpath: OMCPath,
modelname: Optional[str] = None,
) -> None:
Expand Down Expand Up @@ -284,56 +283,18 @@ def definition(self) -> OMCSessionRunData:

return omc_run_data_updated

@staticmethod
def parse_simflags(simflags: str) -> dict[str, Optional[str | dict[str, Any] | numbers.Number]]:
"""
Parse a simflag definition; this is deprecated!

The return data can be used as input for self.args_set().
"""
warnings.warn("The argument 'simflags' is depreciated and will be removed in future versions; "
"please use 'simargs' instead", DeprecationWarning, stacklevel=2)

simargs: dict[str, Optional[str | dict[str, Any] | numbers.Number]] = {}

args = [s for s in simflags.split(' ') if s]
for arg in args:
if arg[0] != '-':
raise ModelicaSystemError(f"Invalid simulation flag: {arg}")
arg = arg[1:]
parts = arg.split('=')
if len(parts) == 1:
simargs[parts[0]] = None
elif parts[0] == 'override':
override = '='.join(parts[1:])

override_dict = {}
for item in override.split(','):
kv = item.split('=')
if not 0 < len(kv) < 3:
raise ModelicaSystemError(f"Invalid value for '-override': {override}")
if kv[0]:
try:
override_dict[kv[0]] = kv[1]
except (KeyError, IndexError) as ex:
raise ModelicaSystemError(f"Invalid value for '-override': {override}") from ex

simargs[parts[0]] = override_dict

return simargs


class ModelicaSystem:
"""
Class to simulate a Modelica model using OpenModelica via OMCSessionZMQ.
Class to simulate a Modelica model using OpenModelica via OMCSession.
"""

def __init__(
self,
command_line_options: Optional[list[str]] = None,
work_directory: Optional[str | os.PathLike] = None,
omhome: Optional[str] = None,
omc_process: Optional[OMCProcess] = None,
session: Optional[OMCSession] = None,
) -> None:
"""Create a ModelicaSystem instance. To define the model use model() or convertFmu2Mo().

Expand All @@ -344,8 +305,8 @@ def __init__(
work_directory: Path to a directory to be used for temporary
files like the model executable. If left unspecified, a tmp
directory will be created.
omhome: path to OMC to be used when creating the OMC session (see OMCSessionZMQ).
omc_process: definition of a (local) OMC process to be used. If
omhome: path to OMC to be used when creating the OMC session (see OMCSession).
session: definition of a (local) OMC session to be used. If
unspecified, a new local session will be created.
"""

Expand Down Expand Up @@ -373,10 +334,10 @@ def __init__(
self._linearized_outputs: list[str] = [] # linearization output list
self._linearized_states: list[str] = [] # linearization states list

if omc_process is not None:
self._session = OMCSessionZMQ(omc_process=omc_process)
if session is not None:
self._session = session
else:
self._session = OMCSessionZMQ(omhome=omhome)
self._session = OMCSessionLocal(omhome=omhome)

# set commandLineOptions using default values or the user defined list
if command_line_options is None:
Expand Down Expand Up @@ -461,13 +422,13 @@ def model(
if model_file is not None:
file_path = pathlib.Path(model_file)
# special handling for OMCProcessLocal - consider a relative path
if isinstance(self._session.omc_process, OMCProcessLocal) and not file_path.is_absolute():
if isinstance(self._session, OMCSessionLocal) and not file_path.is_absolute():
file_path = pathlib.Path.cwd() / file_path
if not file_path.is_file():
raise IOError(f"Model file {file_path} does not exist!")

self._file_name = self.getWorkDirectory() / file_path.name
if (isinstance(self._session.omc_process, OMCProcessLocal)
if (isinstance(self._session, OMCSessionLocal)
and file_path.as_posix() == self._file_name.as_posix()):
pass
elif self._file_name.is_file():
Expand All @@ -482,7 +443,7 @@ def model(
if build:
self.buildModel(variable_filter)

def session(self) -> OMCSessionZMQ:
def get_session(self) -> OMCSession:
"""
Return the OMC session used for this class.
"""
Expand Down Expand Up @@ -585,7 +546,7 @@ def buildModel(self, variableFilter: Optional[str] = None):

def sendExpression(self, expr: str, parsed: bool = True) -> Any:
try:
retval = self._session.sendExpression(expr, parsed)
retval = self._session.sendExpression(command=expr, parsed=parsed)
except OMCSessionException as ex:
raise ModelicaSystemError(f"Error executing {repr(expr)}: {ex}") from ex

Expand Down Expand Up @@ -1052,7 +1013,6 @@ def getOptimizationOptions(
def simulate_cmd(
self,
result_file: OMCPath,
simflags: Optional[str] = None,
simargs: Optional[dict[str, Optional[str | dict[str, Any] | numbers.Number]]] = None,
) -> ModelicaSystemCmd:
"""
Expand All @@ -1065,12 +1025,6 @@ def simulate_cmd(
However, if only non-structural parameters are used, it is possible to reuse an existing instance of
ModelicaSystem to create several version ModelicaSystemCmd to run the model using different settings.

Parameters
----------
result_file
simflags
simargs

Returns
-------
An instance if ModelicaSystemCmd to run the requested simulation.
Expand All @@ -1085,11 +1039,7 @@ def simulate_cmd(
# always define the result file to use
om_cmd.arg_set(key="r", val=result_file.as_posix())

# allow runtime simulation flags from user input
if simflags is not None:
om_cmd.args_set(args=om_cmd.parse_simflags(simflags=simflags))

if simargs:
if simargs is not None:
om_cmd.args_set(args=simargs)

if self._override_variables or self._simulate_options_override:
Expand Down Expand Up @@ -1127,7 +1077,6 @@ def simulate_cmd(
def simulate(
self,
resultfile: Optional[str | os.PathLike] = None,
simflags: Optional[str] = None,
simargs: Optional[dict[str, Optional[str | dict[str, Any] | numbers.Number]]] = None,
) -> None:
"""Simulate the model according to simulation options.
Expand All @@ -1136,8 +1085,6 @@ def simulate(

Args:
resultfile: Path to a custom result file
simflags: String of extra command line flags for the model binary.
This argument is deprecated, use simargs instead.
simargs: Dict with simulation runtime flags.

Examples:
Expand All @@ -1164,7 +1111,6 @@ def simulate(

om_cmd = self.simulate_cmd(
result_file=self._result_file,
simflags=simflags,
simargs=simargs,
)

Expand Down Expand Up @@ -1197,7 +1143,7 @@ def plot(
plot is created by OMC which needs access to the local display. This is not the case for docker and WSL.
"""

if not isinstance(self._session.omc_process, OMCProcessLocal):
if not isinstance(self._session, OMCSessionLocal):
raise ModelicaSystemError("Plot is using the OMC plot functionality; "
"thus, it is only working if OMC is running locally!")

Expand Down Expand Up @@ -1612,9 +1558,9 @@ def _createCSVData(self, csvfile: Optional[OMCPath] = None) -> OMCPath:
for signal_name, signal_values in inputs.items():
signal = np.array(signal_values)
interpolated_inputs[signal_name] = np.interp(
all_times,
signal[:, 0], # times
signal[:, 1], # values
x=all_times,
xp=signal[:, 0], # times
fp=signal[:, 1], # values
)

# Write CSV file
Expand Down Expand Up @@ -1747,7 +1693,6 @@ def optimize(self) -> dict[str, Any]:
def linearize(
self,
lintime: Optional[float] = None,
simflags: Optional[str] = None,
simargs: Optional[dict[str, Optional[str | dict[str, Any] | numbers.Number]]] = None,
) -> LinearizationResult:
"""Linearize the model according to linearization options.
Expand All @@ -1756,8 +1701,6 @@ def linearize(

Args:
lintime: Override "stopTime" value.
simflags: String of extra command line flags for the model binary.
This argument is deprecated, use simargs instead.
simargs: A dict with command line flags and possible options; example: "simargs={'csvInput': 'a.csv'}"

Returns:
Expand Down Expand Up @@ -1803,11 +1746,7 @@ def linearize(

om_cmd.arg_set(key="l", val=str(lintime or self._linearization_options["stopTime"]))

# allow runtime simulation flags from user input
if simflags is not None:
om_cmd.args_set(args=om_cmd.parse_simflags(simflags=simflags))

if simargs:
if simargs is not None:
om_cmd.args_set(args=simargs)

# the file create by the model executable which contains the matrix and linear inputs, outputs and states
Expand Down Expand Up @@ -1954,7 +1893,7 @@ def __init__(
variable_filter: Optional[str] = None,
work_directory: Optional[str | os.PathLike] = None,
omhome: Optional[str] = None,
omc_process: Optional[OMCProcess] = None,
session: Optional[OMCSession] = None,
# simulation specific input
# TODO: add more settings (simulation options, input options, ...)
simargs: Optional[dict[str, Optional[str | dict[str, str] | numbers.Number]]] = None,
Expand All @@ -1974,7 +1913,7 @@ def __init__(
command_line_options=command_line_options,
work_directory=work_directory,
omhome=omhome,
omc_process=omc_process,
session=session,
)
self._mod.model(
model_file=model_file,
Expand All @@ -1988,9 +1927,9 @@ def __init__(
self._simargs = simargs

if resultpath is None:
self._resultpath = self.session().omcpath_tempdir()
self._resultpath = self.get_session().omcpath_tempdir()
else:
self._resultpath = self.session().omcpath(resultpath)
self._resultpath = self.get_session().omcpath(resultpath)
if not self._resultpath.is_dir():
raise ModelicaSystemError("Argument resultpath must be set to a valid path within the environment used "
f"for the OpenModelica session: {resultpath}!")
Expand All @@ -2003,11 +1942,11 @@ def __init__(
self._doe_def: Optional[dict[str, dict[str, Any]]] = None
self._doe_cmd: Optional[dict[str, OMCSessionRunData]] = None

def session(self) -> OMCSessionZMQ:
def get_session(self) -> OMCSession:
"""
Return the OMC session used for this class.
"""
return self._mod.session()
return self._mod.get_session()

def prepare(self) -> int:
"""
Expand Down Expand Up @@ -2046,7 +1985,7 @@ def prepare(self) -> int:

pk_value = pc_structure[idx_structure]
if isinstance(pk_value, str):
pk_value_str = self.session().escape_str(pk_value)
pk_value_str = self.get_session().escape_str(pk_value)
expression = f"setParameterValue({self._model_name}, {pk_structure}, \"{pk_value_str}\")"
elif isinstance(pk_value, bool):
pk_value_bool_str = "true" if pk_value else "false"
Expand Down Expand Up @@ -2167,12 +2106,12 @@ def worker(worker_id, task_queue):
raise ModelicaSystemError("Missing simulation definition!")

resultfile = cmd_definition.cmd_result_path
resultpath = self.session().omcpath(resultfile)
resultpath = self.get_session().omcpath(resultfile)

logger.info(f"[Worker {worker_id}] Performing task: {resultpath.name}")

try:
returncode = self.session().run_model_executable(cmd_run_data=cmd_definition)
returncode = self.get_session().run_model_executable(cmd_run_data=cmd_definition)
logger.info(f"[Worker {worker_id}] Simulation {resultpath.name} "
f"finished with return code: {returncode}")
except ModelicaSystemError as ex:
Expand Down
Loading