Skip to content

Commit 94d5381

Browse files
committed
[ModelicaSystem] use OMCPath for nearly all file system interactions
remove pathlib - use OMCPath and (for type hints) os.PathLike
1 parent bbf9759 commit 94d5381

1 file changed

Lines changed: 42 additions & 33 deletions

File tree

OMPython/ModelicaSystem.py

Lines changed: 42 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,15 @@
3838
import numbers
3939
import numpy as np
4040
import os
41-
import pathlib
4241
import platform
4342
import re
4443
import subprocess
45-
import tempfile
4644
import textwrap
4745
from typing import Optional, Any
4846
import warnings
4947
import xml.etree.ElementTree as ET
5048

51-
from OMPython.OMCSession import OMCSessionException, OMCSessionZMQ, OMCProcessLocal
49+
from OMPython.OMCSession import OMCSessionException, OMCSessionZMQ, OMCProcessLocal, OMCPath
5250

5351
# define logger using the current module name as ID
5452
logger = logging.getLogger(__name__)
@@ -114,8 +112,8 @@ def __getitem__(self, index: int):
114112
class ModelicaSystemCmd:
115113
"""A compiled model executable."""
116114

117-
def __init__(self, runpath: pathlib.Path, modelname: str, timeout: Optional[float] = None) -> None:
118-
self._runpath = pathlib.Path(runpath).resolve().absolute()
115+
def __init__(self, runpath: OMCPath, modelname: str, timeout: Optional[float] = None) -> None:
116+
self._runpath = runpath
119117
self._model_name = modelname
120118
self._timeout = timeout
121119

@@ -229,7 +227,7 @@ def args_set(
229227
for arg in args:
230228
self.arg_set(key=arg, val=args[arg])
231229

232-
def get_exe(self) -> pathlib.Path:
230+
def get_exe(self) -> OMCPath:
233231
"""Get the path to the compiled model executable."""
234232
if platform.system() == "Windows":
235233
path_exe = self._runpath / f"{self._model_name}.exe"
@@ -349,7 +347,7 @@ def parse_simflags(simflags: str) -> dict[str, Optional[str | dict[str, Any] | n
349347
class ModelicaSystem:
350348
def __init__(
351349
self,
352-
fileName: Optional[str | os.PathLike | pathlib.Path] = None,
350+
fileName: Optional[str | os.PathLike] = None,
353351
modelName: Optional[str] = None,
354352
lmodel: Optional[list[str | tuple[str, str]]] = None,
355353
commandLineOptions: Optional[list[str]] = None,
@@ -446,15 +444,25 @@ def __init__(
446444

447445
self._lmodel = lmodel # may be needed if model is derived from other model
448446
self._model_name = modelName # Model class name
449-
self._file_name = pathlib.Path(fileName).resolve() if fileName is not None else None # Model file/package name
447+
if fileName is not None:
448+
file_name = self._getconn.omcpath(fileName).resolve()
449+
else:
450+
file_name = None
451+
self._file_name: Optional[OMCPath] = file_name # Model file/package name
450452
self._simulated = False # True if the model has already been simulated
451-
self._result_file: Optional[pathlib.Path] = None # for storing result file
453+
self._result_file: Optional[OMCPath] = None # for storing result file
452454
self._variable_filter = variableFilter
453455

454456
if self._file_name is not None and not self._file_name.is_file(): # if file does not exist
455457
raise IOError(f"{self._file_name} does not exist!")
456458

457-
self._work_dir: pathlib.Path = self.setWorkDirectory(customBuildDirectory)
459+
# set default command Line Options for linearization as
460+
# linearize() will use the simulation executable and runtime
461+
# flag -l to perform linearization
462+
self.setCommandLineOptions("--linearizationDumpLanguage=python")
463+
self.setCommandLineOptions("--generateSymbolicLinearization")
464+
465+
self._work_dir: OMCPath = self.setWorkDirectory(customBuildDirectory)
458466

459467
if self._file_name is not None:
460468
self._loadLibrary(lmodel=self._lmodel)
@@ -474,7 +482,7 @@ def setCommandLineOptions(self, commandLineOptions: str):
474482
exp = f'setCommandLineOptions("{commandLineOptions}")'
475483
self.sendExpression(exp)
476484

477-
def _loadFile(self, fileName: pathlib.Path):
485+
def _loadFile(self, fileName: OMCPath):
478486
# load file
479487
self.sendExpression(f'loadFile("{fileName.as_posix()}")')
480488

@@ -502,17 +510,17 @@ def _loadLibrary(self, lmodel: list):
502510
'1)["Modelica"]\n'
503511
'2)[("Modelica","3.2.3"), "PowerSystems"]\n')
504512

505-
def setWorkDirectory(self, customBuildDirectory: Optional[str | os.PathLike] = None) -> pathlib.Path:
513+
def setWorkDirectory(self, customBuildDirectory: Optional[str | os.PathLike] = None) -> OMCPath:
506514
"""
507515
Define the work directory for the ModelicaSystem / OpenModelica session. The model is build within this
508516
directory. If no directory is defined a unique temporary directory is created.
509517
"""
510518
if customBuildDirectory is not None:
511-
workdir = pathlib.Path(customBuildDirectory).absolute()
519+
workdir = self._getconn.omcpath(customBuildDirectory).absolute()
512520
if not workdir.is_dir():
513521
raise IOError(f"Provided work directory does not exists: {customBuildDirectory}!")
514522
else:
515-
workdir = pathlib.Path(tempfile.mkdtemp()).absolute()
523+
workdir = self._getconn.omcpath_tempdir().absolute()
516524
if not workdir.is_dir():
517525
raise IOError(f"{workdir} could not be created")
518526

@@ -525,7 +533,7 @@ def setWorkDirectory(self, customBuildDirectory: Optional[str | os.PathLike] = N
525533
# ... and also return the defined path
526534
return workdir
527535

528-
def getWorkDirectory(self) -> pathlib.Path:
536+
def getWorkDirectory(self) -> OMCPath:
529537
"""
530538
Return the defined working directory for this ModelicaSystem / OpenModelica session.
531539
"""
@@ -546,7 +554,7 @@ def buildModel(self, variableFilter: Optional[str] = None):
546554
buildModelResult = self._requestApi(apiName="buildModel", entity=self._model_name, properties=var_filter)
547555
logger.debug("OM model build result: %s", buildModelResult)
548556

549-
xml_file = pathlib.Path(buildModelResult[0]).parent / buildModelResult[1]
557+
xml_file = self._getconn.omcpath(buildModelResult[0]).parent / buildModelResult[1]
550558
self._xmlparse(xml_file=xml_file)
551559

552560
def sendExpression(self, expr: str, parsed: bool = True) -> Any:
@@ -578,7 +586,7 @@ def _requestApi(
578586

579587
return self.sendExpression(exp)
580588

581-
def _xmlparse(self, xml_file: pathlib.Path):
589+
def _xmlparse(self, xml_file: OMCPath):
582590
if not xml_file.is_file():
583591
raise ModelicaSystemError(f"XML file not generated: {xml_file}")
584592

@@ -998,7 +1006,7 @@ def getOptimizationOptions(self, names: Optional[str | list[str]] = None) -> dic
9981006

9991007
def simulate_cmd(
10001008
self,
1001-
result_file: pathlib.Path,
1009+
result_file: OMCPath,
10021010
simflags: Optional[str] = None,
10031011
simargs: Optional[dict[str, Optional[str | dict[str, Any] | numbers.Number]]] = None,
10041012
timeout: Optional[float] = None,
@@ -1103,10 +1111,13 @@ def simulate(
11031111
# default result file generated by OM
11041112
self._result_file = self.getWorkDirectory() / f"{self._model_name}_res.mat"
11051113
elif os.path.exists(resultfile):
1106-
self._result_file = pathlib.Path(resultfile)
1114+
self._result_file = self._getconn.omcpath(resultfile)
11071115
else:
11081116
self._result_file = self.getWorkDirectory() / resultfile
11091117

1118+
if not isinstance(self._result_file, OMCPath):
1119+
raise ModelicaSystemError(f"Invalid result file path: {self._result_file} - must be an OMCPath object!")
1120+
11101121
om_cmd = self.simulate_cmd(
11111122
result_file=self._result_file,
11121123
simflags=simflags,
@@ -1124,7 +1135,7 @@ def simulate(
11241135
# check for an empty (=> 0B) result file which indicates a crash of the model executable
11251136
# see: https://github.com/OpenModelica/OMPython/issues/261
11261137
# https://github.com/OpenModelica/OpenModelica/issues/13829
1127-
if self._result_file.stat().st_size == 0:
1138+
if self._result_file.size() == 0:
11281139
self._result_file.unlink()
11291140
raise ModelicaSystemError("Empty result file - this indicates a crash of the model executable!")
11301141

@@ -1173,7 +1184,7 @@ def getSolutions(
11731184
raise ModelicaSystemError("No result file found. Run simulate() first.")
11741185
result_file = self._result_file
11751186
else:
1176-
result_file = pathlib.Path(resultfile)
1187+
result_file = self._getconn.omcpath(resultfile)
11771188

11781189
# check if the result file exits
11791190
if not result_file.is_file():
@@ -1463,11 +1474,9 @@ def setInputs(
14631474
else:
14641475
raise ModelicaSystemError(f"Data cannot be evaluated for {repr(key)}: {repr(val)}")
14651476

1466-
self._has_inputs = True
1467-
14681477
return True
14691478

1470-
def _createCSVData(self, csvfile: Optional[pathlib.Path] = None) -> pathlib.Path:
1479+
def _createCSVData(self, csvfile: Optional[OMCPath] = None) -> OMCPath:
14711480
"""
14721481
Create a csv file with inputs for the simulation/optimization of the model. If csvfile is provided as argument,
14731482
this file is used; else a generic file name is created.
@@ -1647,15 +1656,15 @@ def linearize(
16471656
timeout=timeout,
16481657
)
16491658

1650-
overrideLinearFile = self.getWorkDirectory() / f'{self._model_name}_override_linear.txt'
1651-
1652-
with open(file=overrideLinearFile, mode="w", encoding="utf-8") as fh:
1653-
for key1, value1 in self._override_variables.items():
1654-
fh.write(f"{key1}={value1}\n")
1655-
for key2, value2 in self._linearization_options.items():
1656-
fh.write(f"{key2}={value2}\n")
1659+
override_content = (
1660+
"\n".join([f"{key}={value}" for key, value in self._override_variables.items()])
1661+
+ "\n".join([f"{key}={value}" for key, value in self._linearization_options.items()])
1662+
+ "\n"
1663+
)
1664+
override_file = self.getWorkDirectory() / f'{self._model_name}_override_linear.txt'
1665+
override_file.write_text(override_content)
16571666

1658-
om_cmd.arg_set(key="overrideFile", val=overrideLinearFile.as_posix())
1667+
om_cmd.arg_set(key="overrideFile", val=override_file.as_posix())
16591668

16601669
if self._inputs:
16611670
for key in self._inputs:
@@ -1683,7 +1692,7 @@ def linearize(
16831692
returncode = om_cmd.run()
16841693
if returncode != 0:
16851694
raise ModelicaSystemError(f"Linearize failed with return code: {returncode}")
1686-
if not linear_file.exists():
1695+
if not linear_file.is_file():
16871696
raise ModelicaSystemError(f"Linearization failed: {linear_file} not found!")
16881697

16891698
self._simulated = True

0 commit comments

Comments
 (0)