Skip to content

Commit ea1cdcc

Browse files
authored
ModelicaSystem update __init__() (#350)
* [ModelicaSystem] split __init__() new: * __init__() - initialisation * model_definition() - model related definitions * [ModelicaSystem] split __init__() - mypy fix in ModelicaSystemCmd * [ModelicaSystem] split __init__() - mypy fix in convertMo2Fmu() * [ModelicaSystem] split __init__() - update unittest * [ModelicaSystem] rename model_definition() => model() * [ModelicaSystem] fix error message * [ModelicaSystem.definition()] check if it was called before * [ModelicaSystem] rename lmodel => libraries * [ModelicaSystem] update docstring for __init__() and model() * [test_ModelicaSystem] test_relative_path will fail at the moment; a fix is available * [test_ModelicaSystem] fix rebase fallout * [test_ModelicaSystem] fix rebase fallout (2) * [ModelicaSystemDoE] fix usage of ModelicaSystem * [ModelicaSystem] fix mypy * [ModelicaSystem] fix default value for fileNamePrefix
1 parent 42c4f87 commit ea1cdcc

File tree

7 files changed

+181
-102
lines changed

7 files changed

+181
-102
lines changed

OMPython/ModelicaSystem.py

Lines changed: 96 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,12 @@ def __init__(
124124
self,
125125
session: OMCSessionZMQ,
126126
runpath: OMCPath,
127-
modelname: str,
127+
modelname: Optional[str] = None,
128128
timeout: Optional[float] = None,
129129
) -> None:
130+
if modelname is None:
131+
raise ModelicaSystemError("Missing model name!")
132+
130133
self._session = session
131134
self._runpath = runpath
132135
self._model_name = modelname
@@ -321,60 +324,25 @@ def parse_simflags(simflags: str) -> dict[str, Optional[str | dict[str, Any] | n
321324
class ModelicaSystem:
322325
def __init__(
323326
self,
324-
fileName: Optional[str | os.PathLike] = None,
325-
modelName: Optional[str] = None,
326-
lmodel: Optional[list[str | tuple[str, str]]] = None,
327327
commandLineOptions: Optional[list[str]] = None,
328-
variableFilter: Optional[str] = None,
329328
customBuildDirectory: Optional[str | os.PathLike] = None,
330329
omhome: Optional[str] = None,
331330
omc_process: Optional[OMCProcess] = None,
332-
build: bool = True,
333331
) -> None:
334-
"""Initialize, load and build a model.
335-
336-
The constructor loads the model file and builds it, generating exe and
337-
xml files, etc.
332+
"""Create a ModelicaSystem instance. To define the model use model() or convertFmu2Mo().
338333
339334
Args:
340-
fileName: Path to the model file. Either absolute or relative to
341-
the current working directory.
342-
modelName: The name of the model class. If it is contained within
343-
a package, "PackageName.ModelName" should be used.
344-
lmodel: List of libraries to be loaded before the model itself is
345-
loaded. Two formats are supported for the list elements:
346-
lmodel=["Modelica"] for just the library name
347-
and lmodel=[("Modelica","3.2.3")] for specifying both the name
348-
and the version.
349335
commandLineOptions: List with extra command line options as elements. The list elements are
350336
provided to omc via setCommandLineOptions(). If set, the default values will be overridden.
351337
To disable any command line options, use an empty list.
352-
variableFilter: A regular expression. Only variables fully
353-
matching the regexp will be stored in the result file.
354-
Leaving it unspecified is equivalent to ".*".
355338
customBuildDirectory: Path to a directory to be used for temporary
356339
files like the model executable. If left unspecified, a tmp
357340
directory will be created.
358-
omhome: OPENMODELICAHOME value to be used when creating the OMC
359-
session.
341+
omhome: path to OMC to be used when creating the OMC session (see OMCSessionZMQ).
360342
omc_process: definition of a (local) OMC process to be used. If
361343
unspecified, a new local session will be created.
362-
build: Boolean controlling whether or not the model should be
363-
built when constructor is called. If False, the constructor
364-
simply loads the model without compiling.
365-
366-
Examples:
367-
mod = ModelicaSystem("ModelicaModel.mo", "modelName")
368-
mod = ModelicaSystem("ModelicaModel.mo", "modelName", ["Modelica"])
369-
mod = ModelicaSystem("ModelicaModel.mo", "modelName", [("Modelica","3.2.3"), "PowerSystems"])
370344
"""
371345

372-
if fileName is None and modelName is None and not lmodel: # all None
373-
raise ModelicaSystemError("Cannot create ModelicaSystem object without any arguments")
374-
375-
if modelName is None:
376-
raise ModelicaSystemError("A modelname must be provided (argument modelName)!")
377-
378346
self._quantities: list[dict[str, Any]] = []
379347
self._params: dict[str, str] = {} # even numerical values are stored as str
380348
self._inputs: dict[str, list | None] = {}
@@ -408,44 +376,86 @@ def __init__(
408376
for opt in commandLineOptions:
409377
self.setCommandLineOptions(commandLineOptions=opt)
410378

411-
if lmodel is None:
412-
lmodel = []
379+
self._simulated = False # True if the model has already been simulated
380+
self._result_file: Optional[OMCPath] = None # for storing result file
381+
382+
self._work_dir: OMCPath = self.setWorkDirectory(customBuildDirectory)
383+
384+
self._model_name: Optional[str] = None
385+
self._libraries: Optional[list[str | tuple[str, str]]] = None
386+
self._file_name: Optional[OMCPath] = None
387+
self._variable_filter: Optional[str] = None
388+
389+
def model(
390+
self,
391+
name: Optional[str] = None,
392+
file: Optional[str | os.PathLike] = None,
393+
libraries: Optional[list[str | tuple[str, str]]] = None,
394+
variable_filter: Optional[str] = None,
395+
build: bool = True,
396+
) -> None:
397+
"""Load and build a Modelica model.
398+
399+
This method loads the model file and builds it if requested (build == True).
400+
401+
Args:
402+
file: Path to the model file. Either absolute or relative to
403+
the current working directory.
404+
name: The name of the model class. If it is contained within
405+
a package, "PackageName.ModelName" should be used.
406+
libraries: List of libraries to be loaded before the model itself is
407+
loaded. Two formats are supported for the list elements:
408+
lmodel=["Modelica"] for just the library name
409+
and lmodel=[("Modelica","3.2.3")] for specifying both the name
410+
and the version.
411+
variable_filter: A regular expression. Only variables fully
412+
matching the regexp will be stored in the result file.
413+
Leaving it unspecified is equivalent to ".*".
414+
build: Boolean controlling whether the model should be
415+
built when constructor is called. If False, the constructor
416+
simply loads the model without compiling.
417+
418+
Examples:
419+
mod = ModelicaSystem()
420+
# and then one of the lines below
421+
mod.model(name="modelName", file="ModelicaModel.mo", )
422+
mod.model(name="modelName", file="ModelicaModel.mo", libraries=["Modelica"])
423+
mod.model(name="modelName", file="ModelicaModel.mo", libraries=[("Modelica","3.2.3"), "PowerSystems"])
424+
"""
425+
426+
if self._model_name is not None:
427+
raise ModelicaSystemError("Can not reuse this instance of ModelicaSystem "
428+
f"defined for {repr(self._model_name)}!")
429+
430+
if name is None or not isinstance(name, str):
431+
raise ModelicaSystemError("A model name must be provided!")
432+
433+
if libraries is None:
434+
libraries = []
413435

414-
if not isinstance(lmodel, list):
415-
raise ModelicaSystemError(f"Invalid input type for lmodel: {type(lmodel)} - list expected!")
436+
if not isinstance(libraries, list):
437+
raise ModelicaSystemError(f"Invalid input type for libraries: {type(libraries)} - list expected!")
416438

417-
self._lmodel = lmodel # may be needed if model is derived from other model
418-
self._model_name = modelName # Model class name
419-
if fileName is not None:
420-
file_name = self._session.omcpath(fileName).resolve()
439+
# set variables
440+
self._model_name = name # Model class name
441+
self._libraries = libraries # may be needed if model is derived from other model
442+
if file is not None:
443+
file_name = self._session.omcpath(file).resolve()
421444
else:
422445
file_name = None
423-
self._file_name: Optional[OMCPath] = file_name # Model file/package name
424-
self._simulated = False # True if the model has already been simulated
425-
self._result_file: Optional[OMCPath] = None # for storing result file
426-
self._variable_filter = variableFilter
446+
self._file_name = file_name # Model file/package name
447+
self._variable_filter = variable_filter
427448

428449
if self._file_name is not None and not self._file_name.is_file(): # if file does not exist
429450
raise IOError(f"{self._file_name} does not exist!")
430451

431-
# set default command Line Options for linearization as
432-
# linearize() will use the simulation executable and runtime
433-
# flag -l to perform linearization
434-
self.setCommandLineOptions("--linearizationDumpLanguage=python")
435-
self.setCommandLineOptions("--generateSymbolicLinearization")
436-
437-
self._work_dir: OMCPath = self.setWorkDirectory(customBuildDirectory)
438-
452+
if self._libraries:
453+
self._loadLibrary(libraries=self._libraries)
439454
if self._file_name is not None:
440-
self._loadLibrary(lmodel=self._lmodel)
441455
self._loadFile(fileName=self._file_name)
442456

443-
# allow directly loading models from MSL without fileName
444-
elif fileName is None and modelName is not None:
445-
self._loadLibrary(lmodel=self._lmodel)
446-
447457
if build:
448-
self.buildModel(variableFilter)
458+
self.buildModel(variable_filter)
449459

450460
def session(self) -> OMCSessionZMQ:
451461
"""
@@ -465,9 +475,9 @@ def _loadFile(self, fileName: OMCPath):
465475
self.sendExpression(f'loadFile("{fileName.as_posix()}")')
466476

467477
# for loading file/package, loading model and building model
468-
def _loadLibrary(self, lmodel: list):
478+
def _loadLibrary(self, libraries: list):
469479
# load Modelica standard libraries or Modelica files if needed
470-
for element in lmodel:
480+
for element in libraries:
471481
if element is not None:
472482
if isinstance(element, str):
473483
if element.endswith(".mo"):
@@ -1587,9 +1597,13 @@ def _createCSVData(self, csvfile: Optional[OMCPath] = None) -> OMCPath:
15871597

15881598
return csvfile
15891599

1590-
def convertMo2Fmu(self, version: str = "2.0", fmuType: str = "me_cs",
1591-
fileNamePrefix: str = "<default>",
1592-
includeResources: bool = True) -> str:
1600+
def convertMo2Fmu(
1601+
self,
1602+
version: str = "2.0",
1603+
fmuType: str = "me_cs",
1604+
fileNamePrefix: Optional[str] = None,
1605+
includeResources: bool = True,
1606+
) -> str:
15931607
"""Translate the model into a Functional Mockup Unit.
15941608
15951609
Args:
@@ -1606,12 +1620,13 @@ def convertMo2Fmu(self, version: str = "2.0", fmuType: str = "me_cs",
16061620
'/tmp/tmpmhfx9umo/CauerLowPassAnalog.fmu'
16071621
"""
16081622

1609-
if fileNamePrefix == "<default>":
1610-
fileNamePrefix = self._model_name
1611-
if includeResources:
1612-
includeResourcesStr = "true"
1613-
else:
1614-
includeResourcesStr = "false"
1623+
if fileNamePrefix is None:
1624+
if self._model_name is None:
1625+
fileNamePrefix = "<default>"
1626+
else:
1627+
fileNamePrefix = self._model_name
1628+
includeResourcesStr = "true" if includeResources else "false"
1629+
16151630
properties = (f'version="{version}", fmuType="{fmuType}", '
16161631
f'fileNamePrefix="{fileNamePrefix}", includeResources={includeResourcesStr}')
16171632
fmu = self._requestApi(apiName='buildModelFMU', entity=self._model_name, properties=properties)
@@ -1903,15 +1918,17 @@ def __init__(
19031918
"""
19041919

19051920
self._mod = ModelicaSystem(
1906-
fileName=fileName,
1907-
modelName=modelName,
1908-
lmodel=lmodel,
19091921
commandLineOptions=commandLineOptions,
1910-
variableFilter=variableFilter,
19111922
customBuildDirectory=customBuildDirectory,
19121923
omhome=omhome,
19131924
omc_process=omc_process,
19141925
)
1926+
self._mod.model(
1927+
file=fileName,
1928+
name=modelName,
1929+
libraries=lmodel,
1930+
variable_filter=variableFilter,
1931+
)
19151932

19161933
self._model_name = modelName
19171934

tests/test_FMIExport.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55

66

77
def test_CauerLowPassAnalog():
8-
mod = OMPython.ModelicaSystem(modelName="Modelica.Electrical.Analog.Examples.CauerLowPassAnalog",
9-
lmodel=["Modelica"])
8+
mod = OMPython.ModelicaSystem()
9+
mod.model(
10+
name="Modelica.Electrical.Analog.Examples.CauerLowPassAnalog",
11+
libraries=["Modelica"],
12+
)
1013
tmp = pathlib.Path(mod.getWorkDirectory())
1114
try:
1215
fmu = mod.convertMo2Fmu(fileNamePrefix="CauerLowPassAnalog")
@@ -16,7 +19,11 @@ def test_CauerLowPassAnalog():
1619

1720

1821
def test_DrumBoiler():
19-
mod = OMPython.ModelicaSystem(modelName="Modelica.Fluid.Examples.DrumBoiler.DrumBoiler", lmodel=["Modelica"])
22+
mod = OMPython.ModelicaSystem()
23+
mod.model(
24+
name="Modelica.Fluid.Examples.DrumBoiler.DrumBoiler",
25+
libraries=["Modelica"],
26+
)
2027
tmp = pathlib.Path(mod.getWorkDirectory())
2128
try:
2229
fmu = mod.convertMo2Fmu(fileNamePrefix="DrumBoiler")

0 commit comments

Comments
 (0)