2222import re
2323
2424from OMPython .OMCSession import (
25+ ModelExecutionData ,
26+ ModelExecutionException ,
27+
2528 OMCSessionException ,
26- OMCSessionRunData ,
2729 OMCSession ,
2830 OMCSessionLocal ,
2931 OMCPath ,
3537
3638class ModelicaSystemError (Exception ):
3739 """
38- Exception used in ModelicaSystem and ModelicaSystemCmd classes.
40+ Exception used in ModelicaSystem classes.
3941 """
4042
4143
@@ -90,7 +92,7 @@ def __getitem__(self, index: int):
9092 return {0 : self .A , 1 : self .B , 2 : self .C , 3 : self .D }[index ]
9193
9294
93- class ModelicaSystemCmd :
95+ class ModelExecutionCmd :
9496 """
9597 All information about a compiled model executable. This should include data about all structured parameters, i.e.
9698 parameters which need a recompilation of the model. All non-structured parameters can be easily changed without
@@ -99,16 +101,22 @@ class ModelicaSystemCmd:
99101
100102 def __init__ (
101103 self ,
102- session : OMCSession ,
103- runpath : OMCPath ,
104- modelname : Optional [str ] = None ,
104+ runpath : os .PathLike ,
105+ cmd_prefix : list [str ],
106+ cmd_local : bool = False ,
107+ cmd_windows : bool = False ,
108+ timeout : float = 10.0 ,
109+ model_name : Optional [str ] = None ,
105110 ) -> None :
106- if modelname is None :
107- raise ModelicaSystemError ("Missing model name!" )
111+ if model_name is None :
112+ raise ModelExecutionException ("Missing model name!" )
108113
109- self ._session = session
110- self ._runpath = runpath
111- self ._model_name = modelname
114+ self ._cmd_local = cmd_local
115+ self ._cmd_windows = cmd_windows
116+ self ._cmd_prefix = cmd_prefix
117+ self ._runpath = pathlib .PurePosixPath (runpath )
118+ self ._model_name = model_name
119+ self ._timeout = timeout
112120
113121 # dictionaries of command line arguments for the model executable
114122 self ._args : dict [str , str | None ] = {}
@@ -153,26 +161,26 @@ def override2str(
153161 elif isinstance (oval , numbers .Number ):
154162 oval_str = str (oval )
155163 else :
156- raise ModelicaSystemError (f"Invalid value for override key { okey } : { type (oval )} " )
164+ raise ModelExecutionException (f"Invalid value for override key { okey } : { type (oval )} " )
157165
158166 return f"{ okey } ={ oval_str } "
159167
160168 if not isinstance (key , str ):
161- raise ModelicaSystemError (f"Invalid argument key: { repr (key )} (type: { type (key )} )" )
169+ raise ModelExecutionException (f"Invalid argument key: { repr (key )} (type: { type (key )} )" )
162170 key = key .strip ()
163171
164172 if isinstance (val , dict ):
165173 if key != 'override' :
166- raise ModelicaSystemError ("Dictionary input only possible for key 'override'!" )
174+ raise ModelExecutionException ("Dictionary input only possible for key 'override'!" )
167175
168176 for okey , oval in val .items ():
169177 if not isinstance (okey , str ):
170- raise ModelicaSystemError ("Invalid key for argument 'override': "
171- f"{ repr (okey )} (type: { type (okey )} )" )
178+ raise ModelExecutionException ("Invalid key for argument 'override': "
179+ f"{ repr (okey )} (type: { type (okey )} )" )
172180
173181 if not isinstance (oval , (str , bool , numbers .Number , type (None ))):
174- raise ModelicaSystemError (f"Invalid input for 'override'.{ repr (okey )} : "
175- f"{ repr (oval )} (type: { type (oval )} )" )
182+ raise ModelExecutionException (f"Invalid input for 'override'.{ repr (okey )} : "
183+ f"{ repr (oval )} (type: { type (oval )} )" )
176184
177185 if okey in self ._arg_override :
178186 if oval is None :
@@ -194,7 +202,7 @@ def override2str(
194202 elif isinstance (val , numbers .Number ):
195203 argval = str (val )
196204 else :
197- raise ModelicaSystemError (f"Invalid argument value for { repr (key )} : { repr (val )} (type: { type (val )} )" )
205+ raise ModelExecutionException (f"Invalid argument value for { repr (key )} : { repr (val )} (type: { type (val )} )" )
198206
199207 if key in self ._args :
200208 logger .warning (f"Override model executable argument: { repr (key )} = { repr (argval )} "
@@ -234,7 +242,7 @@ def get_cmd_args(self) -> list[str]:
234242
235243 return cmdl
236244
237- def definition (self ) -> OMCSessionRunData :
245+ def definition (self ) -> ModelExecutionData :
238246 """
239247 Define all needed data to run the model executable. The data is stored in an OMCSessionRunData object.
240248 """
@@ -243,18 +251,50 @@ def definition(self) -> OMCSessionRunData:
243251 if not isinstance (result_file , str ):
244252 result_file = (self ._runpath / f"{ self ._model_name } .mat" ).as_posix ()
245253
246- omc_run_data = OMCSessionRunData (
247- cmd_path = self ._runpath .as_posix (),
254+ # as this is the local implementation, pathlib.Path can be used
255+ cmd_path = self ._runpath
256+
257+ cmd_library_path = None
258+ if self ._cmd_local and self ._cmd_windows :
259+ cmd_library_path = ""
260+
261+ # set the process environment from the generated .bat file in windows which should have all the dependencies
262+ # for this pathlib.PurePosixPath() must be converted to a pathlib.Path() object, i.e. WindowsPath
263+ path_bat = pathlib .Path (cmd_path ) / f"{ self ._model_name } .bat"
264+ if not path_bat .is_file ():
265+ raise ModelExecutionException ("Batch file (*.bat) does not exist " + str (path_bat ))
266+
267+ content = path_bat .read_text (encoding = 'utf-8' )
268+ for line in content .splitlines ():
269+ match = re .match (pattern = r"^SET PATH=([^%]*)" , string = line , flags = re .IGNORECASE )
270+ if match :
271+ cmd_library_path = match .group (1 ).strip (';' ) # Remove any trailing semicolons
272+ my_env = os .environ .copy ()
273+ my_env ["PATH" ] = cmd_library_path + os .pathsep + my_env ["PATH" ]
274+
275+ cmd_model_executable = cmd_path / f"{ self ._model_name } .exe"
276+ else :
277+ # for Linux the paths to the needed libraries should be included in the executable (using rpath)
278+ cmd_model_executable = cmd_path / self ._model_name
279+
280+ # define local(!) working directory
281+ cmd_cwd_local = None
282+ if self ._cmd_local :
283+ cmd_cwd_local = cmd_path .as_posix ()
284+
285+ omc_run_data = ModelExecutionData (
286+ cmd_path = cmd_path .as_posix (),
248287 cmd_model_name = self ._model_name ,
249288 cmd_args = self .get_cmd_args (),
250- cmd_result_path = result_file ,
289+ cmd_result_file = result_file ,
290+ cmd_prefix = self ._cmd_prefix ,
291+ cmd_library_path = cmd_library_path ,
292+ cmd_model_executable = cmd_model_executable .as_posix (),
293+ cmd_cwd_local = cmd_cwd_local ,
294+ cmd_timeout = self ._timeout ,
251295 )
252296
253- omc_run_data_updated = self ._session .omc_run_data_update (
254- omc_run_data = omc_run_data ,
255- )
256-
257- return omc_run_data_updated
297+ return omc_run_data
258298
259299 @staticmethod
260300 def parse_simflags (simflags : str ) -> dict [str , Optional [str | dict [str , Any ] | numbers .Number ]]:
@@ -263,15 +303,19 @@ def parse_simflags(simflags: str) -> dict[str, Optional[str | dict[str, Any] | n
263303
264304 The return data can be used as input for self.args_set().
265305 """
266- warnings .warn ("The argument 'simflags' is depreciated and will be removed in future versions; "
267- "please use 'simargs' instead" , DeprecationWarning , stacklevel = 2 )
306+ warnings .warn (
307+ message = "The argument 'simflags' is depreciated and will be removed in future versions; "
308+ "please use 'simargs' instead" ,
309+ category = DeprecationWarning ,
310+ stacklevel = 2 ,
311+ )
268312
269313 simargs : dict [str , Optional [str | dict [str , Any ] | numbers .Number ]] = {}
270314
271315 args = [s for s in simflags .split (' ' ) if s ]
272316 for arg in args :
273317 if arg [0 ] != '-' :
274- raise ModelicaSystemError (f"Invalid simulation flag: { arg } " )
318+ raise ModelExecutionException (f"Invalid simulation flag: { arg } " )
275319 arg = arg [1 :]
276320 parts = arg .split ('=' )
277321 if len (parts ) == 1 :
@@ -283,12 +327,12 @@ def parse_simflags(simflags: str) -> dict[str, Optional[str | dict[str, Any] | n
283327 for item in override .split (',' ):
284328 kv = item .split ('=' )
285329 if not 0 < len (kv ) < 3 :
286- raise ModelicaSystemError (f"Invalid value for '-override': { override } " )
330+ raise ModelExecutionException (f"Invalid value for '-override': { override } " )
287331 if kv [0 ]:
288332 try :
289333 override_dict [kv [0 ]] = kv [1 ]
290334 except (KeyError , IndexError ) as ex :
291- raise ModelicaSystemError (f"Invalid value for '-override': { override } " ) from ex
335+ raise ModelExecutionException (f"Invalid value for '-override': { override } " ) from ex
292336
293337 simargs [parts [0 ]] = override_dict
294338
@@ -542,15 +586,17 @@ def buildModel(self, variableFilter: Optional[str] = None):
542586 logger .debug ("OM model build result: %s" , build_model_result )
543587
544588 # check if the executable exists ...
545- om_cmd = ModelicaSystemCmd (
546- session = self ._session ,
589+ om_cmd = ModelExecutionCmd (
547590 runpath = self .getWorkDirectory (),
548- modelname = self ._model_name ,
591+ cmd_local = self ._session .model_execution_local ,
592+ cmd_windows = self ._session .model_execution_windows ,
593+ cmd_prefix = self ._session .model_execution_prefix (cwd = self .getWorkDirectory ()),
594+ model_name = self ._model_name ,
549595 )
550596 # ... by running it - output help for command help
551597 om_cmd .arg_set (key = "help" , val = "help" )
552598 cmd_definition = om_cmd .definition ()
553- returncode = self . _session . run_model_executable ( cmd_run_data = cmd_definition )
599+ returncode = cmd_definition . run ( )
554600 if returncode != 0 :
555601 raise ModelicaSystemError ("Model executable not working!" )
556602
@@ -1035,7 +1081,7 @@ def simulate_cmd(
10351081 result_file : OMCPath ,
10361082 simflags : Optional [str ] = None ,
10371083 simargs : Optional [dict [str , Optional [str | dict [str , Any ] | numbers .Number ]]] = None ,
1038- ) -> ModelicaSystemCmd :
1084+ ) -> ModelExecutionCmd :
10391085 """
10401086 This method prepares the simulates model according to the simulation options. It returns an instance of
10411087 ModelicaSystemCmd which can be used to run the simulation.
@@ -1057,10 +1103,12 @@ def simulate_cmd(
10571103 An instance if ModelicaSystemCmd to run the requested simulation.
10581104 """
10591105
1060- om_cmd = ModelicaSystemCmd (
1061- session = self ._session ,
1106+ om_cmd = ModelExecutionCmd (
10621107 runpath = self .getWorkDirectory (),
1063- modelname = self ._model_name ,
1108+ cmd_local = self ._session .model_execution_local ,
1109+ cmd_windows = self ._session .model_execution_windows ,
1110+ cmd_prefix = self ._session .model_execution_prefix (cwd = self .getWorkDirectory ()),
1111+ model_name = self ._model_name ,
10641112 )
10651113
10661114 # always define the result file to use
@@ -1166,7 +1214,7 @@ def simulate(
11661214 self ._result_file .unlink ()
11671215 # ... run simulation ...
11681216 cmd_definition = om_cmd .definition ()
1169- returncode = self . _session . run_model_executable ( cmd_run_data = cmd_definition )
1217+ returncode = cmd_definition . run ( )
11701218 # and check returncode *AND* resultfile
11711219 if returncode != 0 and self ._result_file .is_file ():
11721220 # check for an empty (=> 0B) result file which indicates a crash of the model executable
@@ -1769,10 +1817,12 @@ def linearize(
17691817 "use ModelicaSystem() to build the model first"
17701818 )
17711819
1772- om_cmd = ModelicaSystemCmd (
1773- session = self ._session ,
1820+ om_cmd = ModelExecutionCmd (
17741821 runpath = self .getWorkDirectory (),
1775- modelname = self ._model_name ,
1822+ cmd_local = self ._session .model_execution_local ,
1823+ cmd_windows = self ._session .model_execution_windows ,
1824+ cmd_prefix = self ._session .model_execution_prefix (cwd = self .getWorkDirectory ()),
1825+ model_name = self ._model_name ,
17761826 )
17771827
17781828 # See comment in simulate_cmd regarding override file and OM version
@@ -1819,7 +1869,7 @@ def linearize(
18191869 linear_file .unlink (missing_ok = True )
18201870
18211871 cmd_definition = om_cmd .definition ()
1822- returncode = self . _session . run_model_executable ( cmd_run_data = cmd_definition )
1872+ returncode = cmd_definition . run ( )
18231873 if returncode != 0 :
18241874 raise ModelicaSystemError (f"Linearize failed with return code: { returncode } " )
18251875 if not linear_file .is_file ():
@@ -2005,7 +2055,7 @@ def __init__(
20052055 self ._parameters = {}
20062056
20072057 self ._doe_def : Optional [dict [str , dict [str , Any ]]] = None
2008- self ._doe_cmd : Optional [dict [str , OMCSessionRunData ]] = None
2058+ self ._doe_cmd : Optional [dict [str , ModelExecutionData ]] = None
20092059
20102060 def get_session (self ) -> OMCSession :
20112061 """
@@ -2124,7 +2174,7 @@ def get_doe_definition(self) -> Optional[dict[str, dict[str, Any]]]:
21242174 """
21252175 return self ._doe_def
21262176
2127- def get_doe_command (self ) -> Optional [dict [str , OMCSessionRunData ]]:
2177+ def get_doe_command (self ) -> Optional [dict [str , ModelExecutionData ]]:
21282178 """
21292179 Get the definitions of simulations commands to run for this DoE.
21302180 """
@@ -2170,13 +2220,13 @@ def worker(worker_id, task_queue):
21702220 if cmd_definition is None :
21712221 raise ModelicaSystemError ("Missing simulation definition!" )
21722222
2173- resultfile = cmd_definition .cmd_result_path
2223+ resultfile = cmd_definition .cmd_result_file
21742224 resultpath = self .get_session ().omcpath (resultfile )
21752225
21762226 logger .info (f"[Worker { worker_id } ] Performing task: { resultpath .name } " )
21772227
21782228 try :
2179- returncode = self . get_session (). run_model_executable ( cmd_run_data = cmd_definition )
2229+ returncode = cmd_definition . run ( )
21802230 logger .info (f"[Worker { worker_id } ] Simulation { resultpath .name } "
21812231 f"finished with return code: { returncode } " )
21822232 except ModelicaSystemError as ex :
0 commit comments