Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
76 changes: 66 additions & 10 deletions OMPython/ModelicaSystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,11 @@
@depreciated_class(msg="Please use class ModelicaSystemOMC instead!")
class ModelicaSystem(ModelicaSystemOMC):
"""
Compatibility class.
High-level interface for loading, compiling, and simulating Modelica models.

This class provides backwards compatibility with OMPython v4.0.0 while extending
ModelicaSystemOMC. It manages model compilation, parameter modification, simulation
runs, and result inspection via an OMC session.
"""

def __init__(
Expand All @@ -59,6 +63,26 @@ def __init__(
omc_process: Optional[OMCSessionLocal] = None,
build: bool = True,
) -> None:
"""Initialize and optionally build a Modelica model.

Args:
fileName: Path to the Modelica source file (.mo). Either absolute
or relative to the working directory.
modelName: The name of the Modelica model class (e.g. "ModelName" or
"PackageName.ModelName").
lmodel: List of libraries to load before the model. Entries can be
library names (e.g. ["Modelica"]) or name-version tuples (e.g.
[("Modelica", "3.2.3")]).
commandLineOptions: Extra command-line options passed to OMC.
variableFilter: Regular expression filter for variables to store in
the simulation result file. Defaults to ".*".
customBuildDirectory: Path to directory for build artifacts and
executable. If unspecified, a temporary directory is created.
omhome: Path to the OpenModelica installation directory.
omc_process: Pre-existing OMCSessionLocal instance. If unspecified,
a new local session is created.
build: If True (default), builds the model executable upon initialization.
"""
super().__init__(
command_line_options=commandLineOptions,
work_directory=customBuildDirectory,
Expand All @@ -75,6 +99,11 @@ def __init__(
self._getconn = self._session

def setCommandLineOptions(self, commandLineOptions: str):
"""Pass command-line flags to the underlying OMC compiler instance.

Args:
commandLineOptions: Flag or option string (e.g. "--generateSymbolicLinearization").
"""
super().set_command_line_options(command_line_option=commandLineOptions)

def simulate_cmd( # type: ignore[override]
Expand Down Expand Up @@ -148,6 +177,19 @@ def _set_compatibility_helper(
args: Any,
kwargs: dict[str, Any],
) -> dict[str, Any]:
"""Convert the legacy positional/keyword inputs into a value dictionary.

Args:
pkey: The keyword name to look up in ``kwargs``.
args: The positional arguments given to the wrapper.
kwargs: The keyword arguments given to the wrapper.

Returns:
A dictionary mapping variable names to values.

Raises:
ModelicaSystemError: If a ``key=value`` string is invalid.
"""
input_args = []
if len(args) == 1:
input_args.append(args[0])
Expand All @@ -157,6 +199,7 @@ def _set_compatibility_helper(
# the code below is based on _prepare_input_data2()

def prepare_str(str_in: str) -> dict[str, str]:
"""Parse a single ``key=value`` string into a one-entry dictionary."""
str_in = str_in.replace(" ", "")
key_val_list: list[str] = str_in.split("=")
if len(key_val_list) != 2:
Expand Down Expand Up @@ -392,19 +435,19 @@ def getOutputs(
@depreciated_class(msg="Please use class ModelicaDoEOMC instead!")
class ModelicaSystemDoE(ModelicaDoEOMC):
"""
Compatibility class.
Compatibility class for Design of Experiments (DoE) with Modelica models.

Inherits from ModelicaDoEOMC to provide backwards compatibility with OMPython v4.0.0.
"""


@depreciated_class(msg="Please use class ModelExecutionConfig instead!")
class ModelicaSystemCmd(ModelExecutionConfig):
"""
Compatibility class; not much content.
Compatibility wrapper for model execution configuration.

Missing definitions:
* get_exe() - see self.definition.cmd_model_executable
* get_cmd() - use self.get_cmd_args() or self.definition().get_cmd()
* run() - use self.definition().run()
Subclasses ModelExecutionConfig to store configuration for running compiled model
binaries.
"""

def __init__(
Expand All @@ -413,6 +456,13 @@ def __init__(
modelname: str,
timeout: Optional[float] = None,
) -> None:
"""Initialize ModelicaSystemCmd.

Args:
runpath: Working directory path where the model binary is located.
modelname: Name of the Modelica model.
timeout: Execution timeout in seconds.
"""
super().__init__(
runpath=runpath,
timeout=timeout,
Expand All @@ -422,10 +472,16 @@ def __init__(


def parse_simflags(simflags: str) -> dict[str, Optional[str | dict[str, Any] | numbers.Number]]:
"""
Parse a simflag definition; this is deprecated!
"""Parse legacy simulation flag string into a dictionary suitable for simargs.

Args:
simflags: Space-separated simulation flags (e.g. "-s=dassl -override=stopTime=2.0").

Returns:
Dictionary mapping flag names to values or override dictionaries.

The return data can be used as input for self.args_set().
Raises:
ModelExecutionException: If a flag or override definition is malformed.
"""
warnings.warn(
message="The argument 'simflags' is depreciated and will be removed in future versions; "
Expand Down
76 changes: 76 additions & 0 deletions OMPython/OMCSession.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,32 @@ class OMCSessionCmd:
"""

def __init__(self, session: OMSessionABC, readonly: bool = False):
"""Initialize the OMC API compatibility wrapper.

Args:
session: The OMC session to send expressions to.
readonly: Whether responses may be served from a cache.
"""
if not isinstance(session, OMSessionABC):
raise OMCSessionException("Invalid OMC process definition!")
self._session = session
self._readonly = readonly
self._omc_cache: dict[tuple[str, bool], Any] = {}

def _ask(self, question: str, opt: Optional[list[str]] = None, parsed: bool = True):
"""Send an OMC API question to the session.

Args:
question: The OMC API function name to call.
opt: Optional list of arguments for the API call.
parsed: Whether to parse the OMC response.

Returns:
The (optionally parsed) OMC response.

Raises:
OMSessionException: If the options are invalid or the call fails.
"""
if opt is None:
expression = question
elif isinstance(opt, list):
Expand All @@ -81,66 +99,87 @@ def _ask(self, question: str, opt: Optional[list[str]] = None, parsed: bool = Tr

# TODO: Open Modelica Compiler API functions. Would be nice to generate these.
def loadFile(self, filename):
"""Load a Modelica file. Deprecated."""
return self._ask(question='loadFile', opt=[f'"{filename}"'])

def loadModel(self, className):
"""Load a Modelica model/package. Deprecated."""
return self._ask(question='loadModel', opt=[className])

def isModel(self, className):
"""Check if ``className`` is a model. Deprecated."""
return self._ask(question='isModel', opt=[className])

def isPackage(self, className):
"""Check if ``className`` is a package. Deprecated."""
return self._ask(question='isPackage', opt=[className])

def isPrimitive(self, className):
"""Check if ``className`` is a primitive. Deprecated."""
return self._ask(question='isPrimitive', opt=[className])

def isConnector(self, className):
"""Check if ``className`` is a connector. Deprecated."""
return self._ask(question='isConnector', opt=[className])

def isRecord(self, className):
"""Check if ``className`` is a record. Deprecated."""
return self._ask(question='isRecord', opt=[className])

def isBlock(self, className):
"""Check if ``className`` is a block. Deprecated."""
return self._ask(question='isBlock', opt=[className])

def isType(self, className):
"""Check if ``className`` is a type. Deprecated."""
return self._ask(question='isType', opt=[className])

def isFunction(self, className):
"""Check if ``className`` is a function. Deprecated."""
return self._ask(question='isFunction', opt=[className])

def isClass(self, className):
"""Check if ``className`` is a class. Deprecated."""
return self._ask(question='isClass', opt=[className])

def isParameter(self, className):
"""Check if ``className`` is a parameter. Deprecated."""
return self._ask(question='isParameter', opt=[className])

def isConstant(self, className):
"""Check if ``className`` is a constant. Deprecated."""
return self._ask(question='isConstant', opt=[className])

def isProtected(self, className):
"""Check if ``className`` is protected. Deprecated."""
return self._ask(question='isProtected', opt=[className])

def getPackages(self, className="AllLoadedClasses"):
"""Get the loaded packages. Deprecated."""
return self._ask(question='getPackages', opt=[className])

def getClassRestriction(self, className):
"""Get the class restriction of ``className``. Deprecated."""
return self._ask(question='getClassRestriction', opt=[className])

def getDerivedClassModifierNames(self, className):
"""Get the derived class modifier names. Deprecated."""
return self._ask(question='getDerivedClassModifierNames', opt=[className])

def getDerivedClassModifierValue(self, className, modifierName):
"""Get the value of a derived class modifier. Deprecated."""
return self._ask(question='getDerivedClassModifierValue', opt=[className, modifierName])

def typeNameStrings(self, className):
"""Get the type name strings of ``className``. Deprecated."""
return self._ask(question='typeNameStrings', opt=[className])

def getComponents(self, className):
"""Get the components of ``className``. Deprecated."""
return self._ask(question='getComponents', opt=[className])

def getClassComment(self, className):
"""Get the comment of ``className``. Deprecated."""
try:
return self._ask(question='getClassComment', opt=[className])
except pyparsing.ParseException as ex:
Expand All @@ -153,22 +192,28 @@ def getNthComponent(self, className, comp_id):
return self._ask(question='getNthComponent', opt=[className, comp_id])

def getNthComponentAnnotation(self, className, comp_id):
"""Get the annotation of the n-th component. Deprecated."""
return self._ask(question='getNthComponentAnnotation', opt=[className, comp_id])

def getImportCount(self, className):
"""Get the number of imports of ``className``. Deprecated."""
return self._ask(question='getImportCount', opt=[className])

def getNthImport(self, className, importNumber):
"""Get the n-th import of ``className``. Deprecated."""
# [Path, id, kind]
return self._ask(question='getNthImport', opt=[className, importNumber])

def getInheritanceCount(self, className):
"""Get the number of inherited classes of ``className``. Deprecated."""
return self._ask(question='getInheritanceCount', opt=[className])

def getNthInheritedClass(self, className, inheritanceDepth):
"""Get the n-th inherited class of ``className``. Deprecated."""
return self._ask(question='getNthInheritedClass', opt=[className, inheritanceDepth])

def getParameterNames(self, className):
"""Get the parameter names of ``className``. Deprecated."""
try:
return self._ask(question='getParameterNames', opt=[className])
except KeyError as ex:
Expand All @@ -177,6 +222,7 @@ def getParameterNames(self, className):
return []

def getParameterValue(self, className, parameterName):
"""Get the value of a parameter. Deprecated."""
try:
return self._ask(question='getParameterValue', opt=[className, parameterName])
except pyparsing.ParseException as ex:
Expand All @@ -185,18 +231,23 @@ def getParameterValue(self, className, parameterName):
return ""

def getComponentModifierNames(self, className, componentName):
"""Get the modifier names of a component. Deprecated."""
return self._ask(question='getComponentModifierNames', opt=[className, componentName])

def getComponentModifierValue(self, className, componentName):
"""Get the modifier value of a component. Deprecated."""
return self._ask(question='getComponentModifierValue', opt=[className, componentName])

def getExtendsModifierNames(self, className, componentName):
"""Get the modifier names of an extends clause. Deprecated."""
return self._ask(question='getExtendsModifierNames', opt=[className, componentName])

def getExtendsModifierValue(self, className, extendsName, modifierName):
"""Get the modifier value of an extends clause. Deprecated."""
return self._ask(question='getExtendsModifierValue', opt=[className, extendsName, modifierName])

def getNthComponentModification(self, className, comp_id):
"""Get the modification of the n-th component. Deprecated."""
# FIXME: OMPython exception Results KeyError exception

# get {$Code(....)} field
Expand All @@ -217,6 +268,19 @@ def getNthComponentModification(self, className, comp_id):
# end getClassNames;
def getClassNames(self, className=None, recursive=False, qualified=False, sort=False, builtin=False,
showProtected=False):
"""Get class names, optionally filtered. Deprecated.

Args:
className: Name of the class to query (defaults to all loaded classes).
recursive: Whether to include nested classes.
qualified: Whether to return qualified names.
sort: Whether to sort the result.
builtin: Whether to include built-in classes.
showProtected: Whether to include protected classes.

Returns:
The parsed list of class names.
"""
opt = [className] if className else [] + [f'recursive={str(recursive).lower()}',
f'qualified={str(qualified).lower()}',
f'sort={str(sort).lower()}',
Expand Down Expand Up @@ -249,6 +313,7 @@ def __init__(
super().__init__(timeout=timeout)

def __del__(self):
"""Clean up the underlying OMC process."""
if hasattr(self, 'omc_process'):
del self.omc_process

Expand All @@ -273,6 +338,14 @@ def omcpath_tempdir(self, tempdir_base: Optional[OMPathABC] = None) -> OMPathABC
return self.omc_process.omcpath_tempdir(tempdir_base=tempdir_base)

def execute(self, command: str):
"""Execute a raw command on the OMC server. Deprecated.

Args:
command: The raw OMC expression to execute.

Returns:
The unparsed OMC response.
"""
warnings.warn(
message="This function is depreciated and will be removed in future versions; "
"please use sendExpression() instead",
Expand All @@ -298,12 +371,15 @@ def sendExpression(
return self.omc_process.sendExpression(expr=command, parsed=parsed, raise_on_error=raise_on_error)

def get_version(self) -> str:
"""Get the version of the OMC server. Deprecated."""
return self.omc_process.get_version()

def model_execution_prefix(self, cwd: Optional[OMPathABC] = None) -> list[str]:
"""Get the model execution command prefix. Deprecated."""
return self.omc_process.model_execution_prefix(cwd=cwd)

def set_workdir(self, workdir: OMPathABC) -> None:
"""Set the working directory. Deprecated."""
return self.omc_process.set_workdir(workdir=workdir)


Expand Down
Loading
Loading