Skip to content

Commit 7079280

Browse files
authored
Merge branch 'master' into type_hints
2 parents b84a73b + ea1cdcc commit 7079280

7 files changed

Lines changed: 181 additions & 102 deletions

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[tuple[float, float]]] = {}
@@ -415,44 +383,86 @@ def __init__(
415383
for opt in commandLineOptions:
416384
self.setCommandLineOptions(commandLineOptions=opt)
417385

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

421-
if not isinstance(lmodel, list):
422-
raise ModelicaSystemError(f"Invalid input type for lmodel: {type(lmodel)} - list expected!")
443+
if not isinstance(libraries, list):
444+
raise ModelicaSystemError(f"Invalid input type for libraries: {type(libraries)} - list expected!")
423445

424-
self._lmodel = lmodel # may be needed if model is derived from other model
425-
self._model_name = modelName # Model class name
426-
if fileName is not None:
427-
file_name = self._session.omcpath(fileName).resolve()
446+
# set variables
447+
self._model_name = name # Model class name
448+
self._libraries = libraries # may be needed if model is derived from other model
449+
if file is not None:
450+
file_name = self._session.omcpath(file).resolve()
428451
else:
429452
file_name = None
430-
self._file_name: Optional[OMCPath] = file_name # Model file/package name
431-
self._simulated = False # True if the model has already been simulated
432-
self._result_file: Optional[OMCPath] = None # for storing result file
433-
self._variable_filter = variableFilter
453+
self._file_name = file_name # Model file/package name
454+
self._variable_filter = variable_filter
434455

435456
if self._file_name is not None and not self._file_name.is_file(): # if file does not exist
436457
raise IOError(f"{self._file_name} does not exist!")
437458

438-
# set default command Line Options for linearization as
439-
# linearize() will use the simulation executable and runtime
440-
# flag -l to perform linearization
441-
self.setCommandLineOptions("--linearizationDumpLanguage=python")
442-
self.setCommandLineOptions("--generateSymbolicLinearization")
443-
444-
self._work_dir: OMCPath = self.setWorkDirectory(customBuildDirectory)
445-
459+
if self._libraries:
460+
self._loadLibrary(libraries=self._libraries)
446461
if self._file_name is not None:
447-
self._loadLibrary(lmodel=self._lmodel)
448462
self._loadFile(fileName=self._file_name)
449463

450-
# allow directly loading models from MSL without fileName
451-
elif fileName is None and modelName is not None:
452-
self._loadLibrary(lmodel=self._lmodel)
453-
454464
if build:
455-
self.buildModel(variableFilter)
465+
self.buildModel(variable_filter)
456466

457467
def session(self) -> OMCSessionZMQ:
458468
"""
@@ -472,9 +482,9 @@ def _loadFile(self, fileName: OMCPath):
472482
self.sendExpression(f'loadFile("{fileName.as_posix()}")')
473483

474484
# for loading file/package, loading model and building model
475-
def _loadLibrary(self, lmodel: list):
485+
def _loadLibrary(self, libraries: list):
476486
# load Modelica standard libraries or Modelica files if needed
477-
for element in lmodel:
487+
for element in libraries:
478488
if element is not None:
479489
if isinstance(element, str):
480490
if element.endswith(".mo"):
@@ -1615,9 +1625,13 @@ def _createCSVData(self, csvfile: Optional[OMCPath] = None) -> OMCPath:
16151625

16161626
return csvfile
16171627

1618-
def convertMo2Fmu(self, version: str = "2.0", fmuType: str = "me_cs",
1619-
fileNamePrefix: str = "<default>",
1620-
includeResources: bool = True) -> str:
1628+
def convertMo2Fmu(
1629+
self,
1630+
version: str = "2.0",
1631+
fmuType: str = "me_cs",
1632+
fileNamePrefix: Optional[str] = None,
1633+
includeResources: bool = True,
1634+
) -> str:
16211635
"""Translate the model into a Functional Mockup Unit.
16221636
16231637
Args:
@@ -1634,12 +1648,13 @@ def convertMo2Fmu(self, version: str = "2.0", fmuType: str = "me_cs",
16341648
'/tmp/tmpmhfx9umo/CauerLowPassAnalog.fmu'
16351649
"""
16361650

1637-
if fileNamePrefix == "<default>":
1638-
fileNamePrefix = self._model_name
1639-
if includeResources:
1640-
includeResourcesStr = "true"
1641-
else:
1642-
includeResourcesStr = "false"
1651+
if fileNamePrefix is None:
1652+
if self._model_name is None:
1653+
fileNamePrefix = "<default>"
1654+
else:
1655+
fileNamePrefix = self._model_name
1656+
includeResourcesStr = "true" if includeResources else "false"
1657+
16431658
properties = (f'version="{version}", fmuType="{fmuType}", '
16441659
f'fileNamePrefix="{fileNamePrefix}", includeResources={includeResourcesStr}')
16451660
fmu = self._requestApi(apiName='buildModelFMU', entity=self._model_name, properties=properties)
@@ -1931,15 +1946,17 @@ def __init__(
19311946
"""
19321947

19331948
self._mod = ModelicaSystem(
1934-
fileName=fileName,
1935-
modelName=modelName,
1936-
lmodel=lmodel,
19371949
commandLineOptions=commandLineOptions,
1938-
variableFilter=variableFilter,
19391950
customBuildDirectory=customBuildDirectory,
19401951
omhome=omhome,
19411952
omc_process=omc_process,
19421953
)
1954+
self._mod.model(
1955+
file=fileName,
1956+
name=modelName,
1957+
libraries=lmodel,
1958+
variable_filter=variableFilter,
1959+
)
19431960

19441961
self._model_name = modelName
19451962

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)