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 ,
@@ -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