3838import numbers
3939import numpy as np
4040import os
41- import pathlib
4241import platform
4342import re
4443import subprocess
45- import tempfile
4644import textwrap
4745from typing import Optional , Any
4846import warnings
4947import 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
5452logger = logging .getLogger (__name__ )
@@ -114,8 +112,8 @@ def __getitem__(self, index: int):
114112class 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
349347class 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 ,
@@ -1102,10 +1110,15 @@ def simulate(
11021110 if resultfile is None :
11031111 # default result file generated by OM
11041112 self ._result_file = self .getWorkDirectory () / f"{ self ._model_name } _res.mat"
1105- elif os . path . exists (resultfile ):
1106- self ._result_file = pathlib . Path ( resultfile )
1113+ elif isinstance (resultfile , OMCPath ):
1114+ self ._result_file = resultfile
11071115 else :
1108- self ._result_file = self .getWorkDirectory () / resultfile
1116+ self ._result_file = self ._getconn .omcpath (resultfile )
1117+ if not self ._result_file .is_absolute ():
1118+ self ._result_file = self .getWorkDirectory () / resultfile
1119+
1120+ if not isinstance (self ._result_file , OMCPath ):
1121+ raise ModelicaSystemError (f"Invalid result file path: { self ._result_file } - must be an OMCPath object!" )
11091122
11101123 om_cmd = self .simulate_cmd (
11111124 result_file = self ._result_file ,
@@ -1124,15 +1137,19 @@ def simulate(
11241137 # check for an empty (=> 0B) result file which indicates a crash of the model executable
11251138 # see: https://github.com/OpenModelica/OMPython/issues/261
11261139 # https://github.com/OpenModelica/OpenModelica/issues/13829
1127- if self ._result_file .stat (). st_size == 0 :
1140+ if self ._result_file .size () == 0 :
11281141 self ._result_file .unlink ()
11291142 raise ModelicaSystemError ("Empty result file - this indicates a crash of the model executable!" )
11301143
11311144 logger .warning (f"Return code = { returncode } but result file exists!" )
11321145
11331146 self ._simulated = True
11341147
1135- def getSolutions (self , varList : Optional [str | list [str ]] = None , resultfile : Optional [str ] = None ) -> tuple [str ] | np .ndarray :
1148+ def getSolutions (
1149+ self ,
1150+ varList : Optional [str | list [str ]] = None ,
1151+ resultfile : Optional [str | os .PathLike ] = None ,
1152+ ) -> tuple [str ] | np .ndarray :
11361153 """Extract simulation results from a result data file.
11371154
11381155 Args:
@@ -1169,7 +1186,7 @@ def getSolutions(self, varList: Optional[str | list[str]] = None, resultfile: Op
11691186 raise ModelicaSystemError ("No result file found. Run simulate() first." )
11701187 result_file = self ._result_file
11711188 else :
1172- result_file = pathlib . Path (resultfile )
1189+ result_file = self . _getconn . omcpath (resultfile )
11731190
11741191 # check if the result file exits
11751192 if not result_file .is_file ():
@@ -1461,7 +1478,7 @@ def setInputs(
14611478
14621479 return True
14631480
1464- def _createCSVData (self , csvfile : Optional [pathlib . Path ] = None ) -> pathlib . Path :
1481+ def _createCSVData (self , csvfile : Optional [OMCPath ] = None ) -> OMCPath :
14651482 """
14661483 Create a csv file with inputs for the simulation/optimization of the model. If csvfile is provided as argument,
14671484 this file is used; else a generic file name is created.
@@ -1628,7 +1645,6 @@ def linearize(
16281645 * `result = linearize(); A = result[0]` mostly just for backwards
16291646 compatibility, because linearize() used to return `[A, B, C, D]`.
16301647 """
1631-
16321648 if len (self ._quantities ) == 0 :
16331649 # if self._quantities has no content, the xml file was not parsed; see self._xmlparse()
16341650 raise ModelicaSystemError (
@@ -1642,15 +1658,15 @@ def linearize(
16421658 timeout = timeout ,
16431659 )
16441660
1645- overrideLinearFile = self . getWorkDirectory () / f' { self . _model_name } _override_linear.txt'
1646-
1647- with open ( file = overrideLinearFile , mode = "w" , encoding = "utf-8" ) as fh :
1648- for key1 , value1 in self . _override_variables . items ():
1649- fh . write ( f" { key1 } = { value1 } \n " )
1650- for key2 , value2 in self ._linearization_options . items ():
1651- fh . write ( f" { key2 } = { value2 } \n " )
1661+ override_content = (
1662+ " \n " . join ([ f" { key } = { value } " for key , value in self . _override_variables . items ()])
1663+ + " \n " . join ([ f" { key } = { value } " for key , value in self . _linearization_options . items ()])
1664+ + " \n "
1665+ )
1666+ override_file = self . getWorkDirectory () / f' { self ._model_name } _override_linear.txt'
1667+ override_file . write_text ( override_content )
16521668
1653- om_cmd .arg_set (key = "overrideFile" , val = overrideLinearFile .as_posix ())
1669+ om_cmd .arg_set (key = "overrideFile" , val = override_file .as_posix ())
16541670
16551671 if self ._inputs :
16561672 for key in self ._inputs :
@@ -1678,7 +1694,7 @@ def linearize(
16781694 returncode = om_cmd .run ()
16791695 if returncode != 0 :
16801696 raise ModelicaSystemError (f"Linearize failed with return code: { returncode } " )
1681- if not linear_file .exists ():
1697+ if not linear_file .is_file ():
16821698 raise ModelicaSystemError (f"Linearization failed: { linear_file } not found!" )
16831699
16841700 self ._simulated = True
0 commit comments