Skip to content

Commit 7d4a557

Browse files
committed
Merge branch 'ModelicaSystem_use_OMCPath' into MP_merge
2 parents e1f489b + 466740c commit 7d4a557

3 files changed

Lines changed: 51 additions & 43 deletions

File tree

.github/workflows/Test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
timeout-minutes: 30
1313
strategy:
1414
matrix:
15-
python-version: ['3.10', '3.12']
15+
python-version: ['3.12']
1616
os: ['ubuntu-latest', 'windows-latest']
1717
omc-version: ['stable']
1818

OMPython/ModelicaSystem.py

Lines changed: 49 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
"""
3434

3535
import ast
36-
import csv
3736
from dataclasses import dataclass
3837
import logging
3938
import numbers
@@ -43,13 +42,12 @@
4342
import platform
4443
import re
4544
import subprocess
46-
import tempfile
4745
import textwrap
4846
from typing import Optional, Any
4947
import warnings
5048
import xml.etree.ElementTree as ET
5149

52-
from OMPython.OMCSession import OMCSessionException, OMCSessionZMQ, OMCProcessLocal
50+
from OMPython.OMCSession import OMCSessionException, OMCSessionZMQ, OMCProcessLocal, OMCPath
5351

5452
# define logger using the current module name as ID
5553
logger = logging.getLogger(__name__)
@@ -398,9 +396,9 @@ def __init__(
398396

399397
self._lmodel = lmodel # may be needed if model is derived from other model
400398
self._model_name = modelName # Model class name
401-
self._file_name = pathlib.Path(fileName).resolve() if fileName is not None else None # Model file/package name
399+
self._file_name: Optional[OMCPath] = self._getconn.omcpath(fileName).resolve() if fileName is not None else None # Model file/package name
402400
self._simulated = False # True if the model has already been simulated
403-
self._result_file: Optional[pathlib.Path] = None # for storing result file
401+
self._result_file: Optional[OMCPath] = None # for storing result file
404402
self._variable_filter = variableFilter
405403

406404
if self._file_name is not None and not self._file_name.is_file(): # if file does not exist
@@ -412,7 +410,7 @@ def __init__(
412410
self.setCommandLineOptions("--linearizationDumpLanguage=python")
413411
self.setCommandLineOptions("--generateSymbolicLinearization")
414412

415-
self._tempdir = self.setTempDirectory(customBuildDirectory)
413+
self._tempdir: OMCPath = self.setTempDirectory(customBuildDirectory)
416414

417415
if self._file_name is not None:
418416
self._loadLibrary(lmodel=self._lmodel)
@@ -432,7 +430,7 @@ def setCommandLineOptions(self, commandLineOptions: Optional[str] = None):
432430
exp = f'setCommandLineOptions("{commandLineOptions}")'
433431
self.sendExpression(exp)
434432

435-
def _loadFile(self, fileName: pathlib.Path):
433+
def _loadFile(self, fileName: OMCPath):
436434
# load file
437435
self.sendExpression(f'loadFile("{fileName.as_posix()}")')
438436

@@ -460,14 +458,14 @@ def _loadLibrary(self, lmodel: list):
460458
'1)["Modelica"]\n'
461459
'2)[("Modelica","3.2.3"), "PowerSystems"]\n')
462460

463-
def setTempDirectory(self, customBuildDirectory: Optional[str | os.PathLike | pathlib.Path] = None) -> pathlib.Path:
461+
def setTempDirectory(self, customBuildDirectory: Optional[str | os.PathLike | pathlib.Path] = None) -> OMCPath:
464462
# create a unique temp directory for each session and build the model in that directory
465463
if customBuildDirectory is not None:
466464
if not os.path.exists(customBuildDirectory):
467465
raise IOError(f"{customBuildDirectory} does not exist")
468-
tempdir = pathlib.Path(customBuildDirectory).absolute()
466+
tempdir = self._getconn.omcpath(customBuildDirectory).absolute()
469467
else:
470-
tempdir = pathlib.Path(tempfile.mkdtemp()).absolute()
468+
tempdir = self._getconn.omcpath_tempdir().absolute()
471469
if not tempdir.is_dir():
472470
raise IOError(f"{tempdir} could not be created")
473471

@@ -477,7 +475,7 @@ def setTempDirectory(self, customBuildDirectory: Optional[str | os.PathLike | pa
477475

478476
return tempdir
479477

480-
def getWorkDirectory(self) -> pathlib.Path:
478+
def getWorkDirectory(self) -> OMCPath:
481479
return self._tempdir
482480

483481
def buildModel(self, variableFilter: Optional[str] = None):
@@ -492,7 +490,7 @@ def buildModel(self, variableFilter: Optional[str] = None):
492490
buildModelResult = self._requestApi(apiName="buildModel", entity=self._model_name, properties=varFilter)
493491
logger.debug("OM model build result: %s", buildModelResult)
494492

495-
xml_file = pathlib.Path(buildModelResult[0]).parent / buildModelResult[1]
493+
xml_file = self._getconn.omcpath(buildModelResult[0]).parent / buildModelResult[1]
496494
self._xmlparse(xml_file=xml_file)
497495

498496
def sendExpression(self, expr: str, parsed: bool = True) -> Any:
@@ -524,7 +522,7 @@ def _requestApi(
524522

525523
return self.sendExpression(exp)
526524

527-
def _xmlparse(self, xml_file: pathlib.Path):
525+
def _xmlparse(self, xml_file: OMCPath):
528526
if not xml_file.is_file():
529527
raise ModelicaSystemError(f"XML file not generated: {xml_file}")
530528

@@ -944,7 +942,7 @@ def getOptimizationOptions(self, names: Optional[str | list[str]] = None) -> dic
944942

945943
def simulate_cmd(
946944
self,
947-
result_file: pathlib.Path,
945+
result_file: OMCPath,
948946
simflags: Optional[str] = None,
949947
simargs: Optional[dict[str, Optional[str | dict[str, str]]]] = None,
950948
timeout: Optional[float] = None,
@@ -971,7 +969,11 @@ def simulate_cmd(
971969
An instance if ModelicaSystemCmd to run the requested simulation.
972970
"""
973971

974-
om_cmd = ModelicaSystemCmd(runpath=self._tempdir, modelname=self._model_name, timeout=timeout)
972+
om_cmd = ModelicaSystemCmd(
973+
runpath=pathlib.Path(self.getWorkDirectory()),
974+
modelname=self._model_name,
975+
timeout=timeout,
976+
)
975977

976978
# always define the result file to use
977979
om_cmd.arg_set(key="r", val=result_file.as_posix())
@@ -983,15 +985,13 @@ def simulate_cmd(
983985
if simargs:
984986
om_cmd.args_set(args=simargs)
985987

986-
overrideFile = self._tempdir / f"{self._model_name}_override.txt"
988+
overrideFile = self.getWorkDirectory() / f"{self._model_name}_override.txt"
987989
if self._override_variables or self._simulate_options_override:
988990
tmpdict = self._override_variables.copy()
989991
tmpdict.update(self._simulate_options_override)
990-
# write to override file
991-
with open(file=overrideFile, mode="w", encoding="utf-8") as fh:
992-
for key, value in tmpdict.items():
993-
fh.write(f"{key}={value}\n")
994992

993+
override_content = "\n".join([f"{key}={value}" for key, value in tmpdict.items()]) + "\n"
994+
overrideFile.write_text(override_content)
995995
om_cmd.arg_set(key="overrideFile", val=overrideFile.as_posix())
996996

997997
if self._inputs: # if model has input quantities
@@ -1042,11 +1042,14 @@ def simulate(
10421042

10431043
if resultfile is None:
10441044
# default result file generated by OM
1045-
self._result_file = self._tempdir / f"{self._model_name}_res.mat"
1045+
self._result_file = self.getWorkDirectory() / f"{self._model_name}_res.mat"
10461046
elif os.path.exists(resultfile):
1047-
self._result_file = pathlib.Path(resultfile)
1047+
self._result_file = self._getconn.omcpath(resultfile)
10481048
else:
1049-
self._result_file = self._tempdir / resultfile
1049+
self._result_file = self.getWorkDirectory() / resultfile
1050+
1051+
if not isinstance(self._result_file, OMCPath):
1052+
raise ModelicaSystemError(f"Invalid result file path: {self._result_file} - must be an OMCPath object!")
10501053

10511054
om_cmd = self.simulate_cmd(
10521055
result_file=self._result_file,
@@ -1065,7 +1068,7 @@ def simulate(
10651068
# check for an empty (=> 0B) result file which indicates a crash of the model executable
10661069
# see: https://github.com/OpenModelica/OMPython/issues/261
10671070
# https://github.com/OpenModelica/OpenModelica/issues/13829
1068-
if self._result_file.stat().st_size == 0:
1071+
if self._result_file.size() == 0:
10691072
self._result_file.unlink()
10701073
raise ModelicaSystemError("Empty result file - this indicates a crash of the model executable!")
10711074

@@ -1110,7 +1113,7 @@ def getSolutions(self, varList: Optional[str | list[str]] = None, resultfile: Op
11101113
raise ModelicaSystemError("No result file found. Run simulate() first.")
11111114
result_file = self._result_file
11121115
else:
1113-
result_file = pathlib.Path(resultfile)
1116+
result_file = self._getconn.omcpath(resultfile)
11141117

11151118
# check for result file exits
11161119
if not result_file.is_file():
@@ -1406,7 +1409,7 @@ def setInputs(
14061409

14071410
return True
14081411

1409-
def _createCSVData(self, csvfile: Optional[pathlib.Path] = None) -> pathlib.Path:
1412+
def _createCSVData(self, csvfile: Optional[OMCPath] = None) -> OMCPath:
14101413
"""
14111414
Create a csv file with inputs for the simulation/optimization of the model. If csvfile is provided as argument,
14121415
this file is used; else a generic file name is created.
@@ -1452,11 +1455,12 @@ def _createCSVData(self, csvfile: Optional[pathlib.Path] = None) -> pathlib.Path
14521455
csv_rows.append(row)
14531456

14541457
if csvfile is None:
1455-
csvfile = self._tempdir / f'{self._model_name}.csv'
1458+
csvfile = self.getWorkDirectory() / f'{self._model_name}.csv'
1459+
1460+
# basic definition of a CSV file using csv_rows as input
1461+
csv_content = "\n".join([",".join(map(str, row)) for row in csv_rows]) + "\n"
14561462

1457-
with open(file=csvfile, mode="w", encoding="utf-8", newline="") as fh:
1458-
writer = csv.writer(fh)
1459-
writer.writerows(csv_rows)
1463+
csvfile.write_text(csv_content)
14601464

14611465
return csvfile
14621466

@@ -1576,17 +1580,21 @@ def linearize(self, lintime: Optional[float] = None, simflags: Optional[str] = N
15761580
"use ModelicaSystem() to build the model first"
15771581
)
15781582

1579-
om_cmd = ModelicaSystemCmd(runpath=self._tempdir, modelname=self._model_name, timeout=timeout)
1580-
1581-
overrideLinearFile = self._tempdir / f'{self._model_name}_override_linear.txt'
1583+
om_cmd = ModelicaSystemCmd(
1584+
runpath=pathlib.Path(self.getWorkDirectory()),
1585+
modelname=self._model_name,
1586+
timeout=timeout,
1587+
)
15821588

1583-
with open(file=overrideLinearFile, mode="w", encoding="utf-8") as fh:
1584-
for key1, value1 in self._override_variables.items():
1585-
fh.write(f"{key1}={value1}\n")
1586-
for key2, value2 in self._linearization_options.items():
1587-
fh.write(f"{key2}={value2}\n")
1589+
override_content = (
1590+
"\n".join([f"{key}={value}" for key, value in self._override_variables.items()])
1591+
+ "\n".join([f"{key}={value}" for key, value in self._linearization_options.items()])
1592+
+ "\n"
1593+
)
1594+
override_file = self.getWorkDirectory() / f'{self._model_name}_override_linear.txt'
1595+
override_file.write_text(override_content)
15881596

1589-
om_cmd.arg_set(key="overrideFile", val=overrideLinearFile.as_posix())
1597+
om_cmd.arg_set(key="overrideFile", val=override_file.as_posix())
15901598

15911599
if self._inputs:
15921600
for key in self._inputs:
@@ -1608,7 +1616,7 @@ def linearize(self, lintime: Optional[float] = None, simflags: Optional[str] = N
16081616
om_cmd.args_set(args=simargs)
16091617

16101618
# the file create by the model executable which contains the matrix and linear inputs, outputs and states
1611-
linear_file = self._tempdir / "linearized_model.py"
1619+
linear_file = self.getWorkDirectory() / "linearized_model.py"
16121620

16131621
linear_file.unlink(missing_ok=True)
16141622

@@ -1618,7 +1626,7 @@ def linearize(self, lintime: Optional[float] = None, simflags: Optional[str] = N
16181626

16191627
self._simulated = True
16201628

1621-
if not linear_file.exists():
1629+
if not linear_file.is_file():
16221630
raise ModelicaSystemError(f"Linearization failed: {linear_file} not found!")
16231631

16241632
# extract data from the python file with the linearized model using the ast module - this allows to get the

tests/test_ModelicaSystem.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ def test_customBuildDirectory(tmp_path, model_firstorder):
105105
tmpdir = tmp_path / "tmpdir1"
106106
tmpdir.mkdir()
107107
m = OMPython.ModelicaSystem(filePath, "M", customBuildDirectory=tmpdir)
108-
assert m.getWorkDirectory().resolve() == tmpdir.resolve()
108+
assert pathlib.Path(m.getWorkDirectory()).resolve() == tmpdir.resolve()
109109
result_file = tmpdir / "a.mat"
110110
assert not result_file.exists()
111111
m.simulate(resultfile="a.mat")

0 commit comments

Comments
 (0)