From 0d28ce1c6ec84ddfc5329bbc74e10a9304bb16fa Mon Sep 17 00:00:00 2001 From: syntron Date: Sat, 26 Apr 2025 21:34:50 +0200 Subject: [PATCH 01/16] [ModelicaSystem] remove not needed variables --- OMPython/ModelicaSystem.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index 5ae3768d..3f43b7c7 100644 --- a/OMPython/ModelicaSystem.py +++ b/OMPython/ModelicaSystem.py @@ -183,10 +183,6 @@ def __init__( else: self.getconn = OMCSessionZMQ(omhome=omhome) - # needed for properly deleting the session - self._omc_log_file = self.getconn._omc_log_file - self._omc_process = self.getconn._omc_process - # set commandLineOptions if provided by users self.setCommandLineOptions(commandLineOptions=commandLineOptions) From b4a1615e5afc25384142fb091b5612a7a3382787 Mon Sep 17 00:00:00 2001 From: syntron Date: Sat, 26 Apr 2025 21:36:54 +0200 Subject: [PATCH 02/16] [ModelicaSystem] csvFile --- OMPython/ModelicaSystem.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index 3f43b7c7..188c9f7d 100644 --- a/OMPython/ModelicaSystem.py +++ b/OMPython/ModelicaSystem.py @@ -722,8 +722,8 @@ def simulate(self, resultfile=None, simflags=None, timeout: Optional[int] = None errstr = f"!!! stopTime not matched for Input {i}" self._raise_error(errstr=errstr) return - self.createCSVData() # create csv file - csvinput = " -csvInput=" + self.csvFile + self.csvFile = self.createCSVData() # create csv file + csvinput = " -csvInput=" + self.csvFile.as_posix() else: csvinput = "" @@ -944,7 +944,7 @@ def checkValidInputs(self, name): else: ModelicaSystemError('Error!!! Value must be in tuple format') - def createCSVData(self) -> None: + def createCSVData(self) -> pathlib.Path: start_time: float = float(self.simulateOptions["startTime"]) stop_time: float = float(self.simulateOptions["stopTime"]) @@ -985,12 +985,14 @@ def createCSVData(self) -> None: ] csv_rows.append(row) - self.csvFile: str = (pathlib.Path(self.tempdir) / f'{self.modelName}.csv').as_posix() + csvFile = pathlib.Path(self.tempdir) / f'{self.modelName}.csv' - with open(self.csvFile, "w", newline="") as f: + with open(csvFile, "w", newline="") as f: writer = csv.writer(f) writer.writerows(csv_rows) + return csvFile + # to convert Modelica model to FMU def convertMo2Fmu(self, version="2.0", fmuType="me_cs", fileNamePrefix="", includeResources=True): # 19 """ @@ -1093,8 +1095,8 @@ def linearize(self, lintime: Optional[float] = None, simflags: Optional[str] = N for l in tupleList: if l[0] < float(self.simulateOptions["startTime"]): raise ModelicaSystemError('Input time value is less than simulation startTime') - self.createCSVData() - csvinput = " -csvInput=" + self.csvFile + self.csvFile = self.createCSVData() + csvinput = " -csvInput=" + self.csvFile.as_posix() else: csvinput = "" From d205ef7c2e969742b987308a2f02db6510f85da1 Mon Sep 17 00:00:00 2001 From: syntron Date: Sat, 26 Apr 2025 21:37:24 +0200 Subject: [PATCH 03/16] [ModelicaSystem] tempdir --- OMPython/ModelicaSystem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index 188c9f7d..526fd821 100644 --- a/OMPython/ModelicaSystem.py +++ b/OMPython/ModelicaSystem.py @@ -277,7 +277,7 @@ def setTempDirectory(self, customBuildDirectory): raise IOError(self.tempdir, " cannot be created") logger.info("Define tempdir as %s", self.tempdir) - exp = f'cd("{pathlib.Path(self.tempdir).as_posix()}")' + exp = f'cd("{pathlib.Path(self.tempdir).absolute().as_posix()}")' self.sendExpression(exp) def getWorkDirectory(self): From 34fad396f5675fdd125dd9d485a72bcc95678f6c Mon Sep 17 00:00:00 2001 From: syntron Date: Sat, 26 Apr 2025 21:37:46 +0200 Subject: [PATCH 04/16] [ModelicaSystem] batFilePath --- OMPython/ModelicaSystem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index 526fd821..edf4d824 100644 --- a/OMPython/ModelicaSystem.py +++ b/OMPython/ModelicaSystem.py @@ -292,7 +292,7 @@ def _run_cmd(self, cmd: list, timeout: Optional[int] = None): # set the process environment from the generated .bat file in windows which should have all the dependencies batFilePath = pathlib.Path(self.tempdir) / f"{self.modelName}.bat" if not batFilePath.exists(): - ModelicaSystemError("Batch file (*.bat) does not exist " + batFilePath) + ModelicaSystemError("Batch file (*.bat) does not exist " + str(batFilePath)) with open(batFilePath, 'r') as file: for line in file: From 7e4f4efc53b4aa963ed4a0d4bf2234298ee90546 Mon Sep 17 00:00:00 2001 From: syntron Date: Sat, 26 Apr 2025 21:39:04 +0200 Subject: [PATCH 05/16] [ModelicaSystem] fileName / lmodel --- OMPython/ModelicaSystem.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index edf4d824..a50d657b 100644 --- a/OMPython/ModelicaSystem.py +++ b/OMPython/ModelicaSystem.py @@ -202,7 +202,7 @@ def __init__( self._raiseerrors = raiseerrors - if fileName is not None and not self.fileName.is_file(): # if file does not exist + if self.fileName is not None and not self.fileName.is_file(): # if file does not exist raise IOError(f"File Error: {self.fileName} does not exist!!!") # set default command Line Options for linearization as @@ -213,13 +213,13 @@ def __init__( self.setTempDirectory(customBuildDirectory) - if fileName is not None: - self.loadLibrary() - self.loadFile() + if self.fileName is not None: + self.loadLibrary(lmodel=self.lmodel) + self.loadFile(fileName=self.fileName) # allow directly loading models from MSL without fileName - if fileName is None and modelName is not None: - self.loadLibrary() + elif fileName is None and modelName is not None: + self.loadLibrary(lmodel=self.lmodel) self.buildModel(variableFilter) @@ -231,17 +231,17 @@ def setCommandLineOptions(self, commandLineOptions: str): if not self.sendExpression(exp): self._check_error() - def loadFile(self): + def loadFile(self, fileName: pathlib.Path): # load file - loadMsg = self.sendExpression(f'loadFile("{self.fileName.as_posix()}")') + loadMsg = self.sendExpression(f'loadFile("{fileName.as_posix()}")') # Show notification or warnings to the user when verbose=True OR if some error occurred i.e., not result if self._verbose or not loadMsg: self._check_error() # for loading file/package, loading model and building model - def loadLibrary(self): + def loadLibrary(self, lmodel: list): # load Modelica standard libraries or Modelica files if needed - for element in self.lmodel: + for element in lmodel: if element is not None: if isinstance(element, str): if element.endswith(".mo"): From dbb084e5b85c2c7e687ff3225b8d897e79a47b2a Mon Sep 17 00:00:00 2001 From: syntron Date: Mon, 28 Apr 2025 08:52:27 +0200 Subject: [PATCH 06/16] [tests] fix definition of lmodel - should be a list --- tests/test_FMIExport.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_FMIExport.py b/tests/test_FMIExport.py index 15e94227..0d6d0ff9 100644 --- a/tests/test_FMIExport.py +++ b/tests/test_FMIExport.py @@ -14,7 +14,8 @@ def __del__(self): def testCauerLowPassAnalog(self): print("testing Cauer") - mod = OMPython.ModelicaSystem(modelName="Modelica.Electrical.Analog.Examples.CauerLowPassAnalog", lmodel="Modelica") + mod = OMPython.ModelicaSystem(modelName="Modelica.Electrical.Analog.Examples.CauerLowPassAnalog", + lmodel=["Modelica"]) self.tmp = mod.getWorkDirectory() fmu = mod.convertMo2Fmu(fileNamePrefix="CauerLowPassAnalog") @@ -22,7 +23,7 @@ def testCauerLowPassAnalog(self): def testDrumBoiler(self): print("testing DrumBoiler") - mod = OMPython.ModelicaSystem(modelName="Modelica.Fluid.Examples.DrumBoiler.DrumBoiler", lmodel="Modelica") + mod = OMPython.ModelicaSystem(modelName="Modelica.Fluid.Examples.DrumBoiler.DrumBoiler", lmodel=["Modelica"]) self.tmp = mod.getWorkDirectory() fmu = mod.convertMo2Fmu(fileNamePrefix="DrumBoiler") From 78062acb1fab40653e18752fe581956ab5e9447b Mon Sep 17 00:00:00 2001 From: syntron Date: Sat, 26 Apr 2025 21:39:23 +0200 Subject: [PATCH 07/16] [ModelicaSystem] resultfile --- OMPython/ModelicaSystem.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index a50d657b..c8edf2c0 100644 --- a/OMPython/ModelicaSystem.py +++ b/OMPython/ModelicaSystem.py @@ -679,14 +679,14 @@ def simulate(self, resultfile=None, simflags=None, timeout: Optional[int] = None >>> simulate(simflags="-noEventEmit -noRestart -override=e=0.3,g=10") # set runtime simulation flags """ if resultfile is None: - r = "" + # default result file generated by OM self.resultfile = (pathlib.Path(self.tempdir) / f"{self.modelName}_res.mat").as_posix() + elif os.path.exists(resultfile): + self.resultfile = resultfile else: - if os.path.exists(resultfile): - self.resultfile = resultfile - else: - self.resultfile = (pathlib.Path(self.tempdir) / resultfile).as_posix() - r = " -r=" + self.resultfile + self.resultfile = (pathlib.Path(self.tempdir) / resultfile).as_posix() + # always define the resultfile to use + resultfileflag = " -r=" + self.resultfile # allow runtime simulation flags from user input if simflags is None: @@ -731,7 +731,7 @@ def simulate(self, resultfile=None, simflags=None, timeout: Optional[int] = None if not exe_file.exists(): raise Exception(f"Error: Application file path not found: {exe_file}") - cmd = exe_file.as_posix() + override + csvinput + r + simflags + cmd = exe_file.as_posix() + override + csvinput + resultfileflag + simflags cmd = [s for s in cmd.split(' ') if s] self._run_cmd(cmd=cmd, timeout=timeout) self.simulationFlag = True From 485a3b977fdb91df97c600119d66b428057428a2 Mon Sep 17 00:00:00 2001 From: syntron Date: Sat, 26 Apr 2025 21:54:27 +0200 Subject: [PATCH 08/16] [ModelicaSystem] check session --- OMPython/ModelicaSystem.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index c8edf2c0..9ffd775f 100644 --- a/OMPython/ModelicaSystem.py +++ b/OMPython/ModelicaSystem.py @@ -179,6 +179,8 @@ def __init__( self._verbose = verbose if session is not None: + if not isinstance(session, OMCSessionZMQ): + raise ModelicaSystemError("Invalid session data provided!") self.getconn = session else: self.getconn = OMCSessionZMQ(omhome=omhome) From b3e602f6645452b0b8c6ce5ff8b5b8de5f214fb4 Mon Sep 17 00:00:00 2001 From: syntron Date: Thu, 24 Apr 2025 20:14:24 +0200 Subject: [PATCH 09/16] [ModelicaSystem] remove _check_error() --- OMPython/ModelicaSystem.py | 31 ++++++++----------------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index 9ffd775f..7668f9f6 100644 --- a/OMPython/ModelicaSystem.py +++ b/OMPython/ModelicaSystem.py @@ -230,15 +230,11 @@ def setCommandLineOptions(self, commandLineOptions: str): if commandLineOptions is None: return exp = f'setCommandLineOptions("{commandLineOptions}")' - if not self.sendExpression(exp): - self._check_error() + self.sendExpression(exp) def loadFile(self, fileName: pathlib.Path): # load file - loadMsg = self.sendExpression(f'loadFile("{fileName.as_posix()}")') - # Show notification or warnings to the user when verbose=True OR if some error occurred i.e., not result - if self._verbose or not loadMsg: - self._check_error() + self.sendExpression(f'loadFile("{fileName.as_posix()}")') # for loading file/package, loading model and building model def loadLibrary(self, lmodel: list): @@ -250,22 +246,19 @@ def loadLibrary(self, lmodel: list): apiCall = "loadFile" else: apiCall = "loadModel" - result = self.requestApi(apiCall, element) + self.requestApi(apiCall, element) elif isinstance(element, tuple): if not element[1]: - libname = f"loadModel({element[0]})" + expr_load_lib = f"loadModel({element[0]})" else: - libname = f'loadModel({element[0]}, {{"{element[1]}"}})' - result = self.sendExpression(libname) + expr_load_lib = f'loadModel({element[0]}, {{"{element[1]}"}})' + self.sendExpression(expr_load_lib) else: raise ModelicaSystemError("loadLibrary() failed, Unknown type detected: " f"{element} is of type {type(element)}, " "The following patterns are supported:\n" '1)["Modelica"]\n' '2)[("Modelica","3.2.3"), "PowerSystems"]\n') - # Show notification or warnings to the user when verbose=True OR if some error occurred i.e., not result - if self._verbose or not result: - self._check_error() def setTempDirectory(self, customBuildDirectory): # create a unique temp directory for each session and build the model in that directory @@ -323,12 +316,6 @@ def _run_cmd(self, cmd: list, timeout: Optional[int] = None): except Exception as e: raise ModelicaSystemError(f"Exception {type(e)} running command {cmd}: {e}") - def _check_error(self): - errstr = self.sendExpression("getErrorString()") - if not errstr: - return - self._raise_error(errstr=errstr) - def _raise_error(self, errstr: str): if self._raiseerrors: raise ModelicaSystemError(f"OM error: {errstr}") @@ -347,7 +334,6 @@ def buildModel(self, variableFilter=None): buildModelResult = self.requestApi("buildModel", self.modelName, properties=varFilter) if self._verbose: logger.info("OM model build result: %s", buildModelResult) - self._check_error() self.xmlFile = pathlib.Path(buildModelResult[0]).parent / buildModelResult[1] self.xmlparse() @@ -1017,7 +1003,7 @@ def convertMo2Fmu(self, version="2.0", fmuType="me_cs", fileNamePrefix=" Date: Sat, 26 Apr 2025 22:12:25 +0200 Subject: [PATCH 10/16] [ModelicaSystem] static strip_space() --- OMPython/ModelicaSystem.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index 7668f9f6..67065a55 100644 --- a/OMPython/ModelicaSystem.py +++ b/OMPython/ModelicaSystem.py @@ -770,7 +770,8 @@ def getSolutions(self, varList=None, resultfile=None): # 12 raise ModelicaSystemError("Unhandled input for getSolutions()") - def strip_space(self, name): + @staticmethod + def _strip_space(name): if isinstance(name, str): return name.replace(" ", "") elif isinstance(name, list): @@ -787,7 +788,7 @@ def setMethodHelper(self, args1, args2, args3, args4=None): args4 - dict() which stores the new override variables list, """ def apply_single(args1): - args1 = self.strip_space(args1) + args1 = self._strip_space(args1) value = args1.split("=") if value[0] in args2: if args3 == "parameter" and self.isParameterChangeable(value[0], value[1]): @@ -811,7 +812,7 @@ def apply_single(args1): elif isinstance(args1, list): result = [] - args1 = self.strip_space(args1) + args1 = self._strip_space(args1) for var in args1: result.append(apply_single(var)) @@ -888,7 +889,7 @@ def setInputs(self, name): # 15 >>> setInputs(["Name1=value1","Name2=value2"]) """ if isinstance(name, str): - name = self.strip_space(name) + name = self._strip_space(name) value = name.split("=") if value[0] in self.inputlist: tmpvalue = eval(value[1]) @@ -903,7 +904,7 @@ def setInputs(self, name): # 15 errstr = value[0] + " is not an input" self._raise_error(errstr=errstr) elif isinstance(name, list): - name = self.strip_space(name) + name = self._strip_space(name) for var in name: value = var.split("=") if value[0] in self.inputlist: From a9ef148fa19922ec4c5f41e9db185a4799c2e9b5 Mon Sep 17 00:00:00 2001 From: syntron Date: Sat, 26 Apr 2025 22:42:40 +0200 Subject: [PATCH 11/16] [ModelicaSystem] exception handling --- OMPython/ModelicaSystem.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index 67065a55..74fc0c2e 100644 --- a/OMPython/ModelicaSystem.py +++ b/OMPython/ModelicaSystem.py @@ -158,7 +158,7 @@ def __init__( mod = ModelicaSystem("ModelicaModel.mo", "modelName", [("Modelica","3.2.3"), "PowerSystems"]) """ if fileName is None and modelName is None and not lmodel: # all None - raise Exception("Cannot create ModelicaSystem object without any arguments") + raise ModelicaSystemError("Cannot create ModelicaSystem object without any arguments") self.quantitiesList = [] self.paramlist = {} @@ -205,7 +205,7 @@ def __init__( self._raiseerrors = raiseerrors if self.fileName is not None and not self.fileName.is_file(): # if file does not exist - raise IOError(f"File Error: {self.fileName} does not exist!!!") + raise IOError(f"{self.fileName} does not exist!") # set default command Line Options for linearization as # linearize() will use the simulation executable and runtime @@ -313,8 +313,8 @@ def _run_cmd(self, cmd: list, timeout: Optional[int] = None): logger.info("OM output for command %s:\n%s", cmd, stdout) except subprocess.TimeoutExpired: raise ModelicaSystemError(f"Timeout running command {repr(cmd)}") - except Exception as e: - raise ModelicaSystemError(f"Exception {type(e)} running command {cmd}: {e}") + except Exception as ex: + raise ModelicaSystemError(f"Error running command {cmd}") from ex def _raise_error(self, errstr: str): if self._raiseerrors: @@ -437,8 +437,8 @@ def getContinuous(self, names=None): # 4 try: value = self.getSolutions(i) self.continuouslist[i] = value[0][-1] - except Exception: - raise ModelicaSystemError(f"OM error: {i} could not be computed") + except OMCSessionException as ex: + raise ModelicaSystemError(f"{i} could not be computed") from ex return self.continuouslist elif isinstance(names, str): @@ -447,7 +447,7 @@ def getContinuous(self, names=None): # 4 self.continuouslist[names] = value[0][-1] return [self.continuouslist.get(names)] else: - raise ModelicaSystemError(f"OM error: {names} is not continuous") + raise ModelicaSystemError(f"{names} is not continuous") elif isinstance(names, list): valuelist = [] @@ -457,7 +457,7 @@ def getContinuous(self, names=None): # 4 self.continuouslist[i] = value[0][-1] valuelist.append(value[0][-1]) else: - raise ModelicaSystemError(f"OM error: {i} is not continuous") + raise ModelicaSystemError(f"{i} is not continuous") return valuelist raise ModelicaSystemError("Unhandled input for getContinous()") @@ -717,7 +717,7 @@ def simulate(self, resultfile=None, simflags=None, timeout: Optional[int] = None exe_file = self.get_exe_file() if not exe_file.exists(): - raise Exception(f"Error: Application file path not found: {exe_file}") + raise ModelicaSystemError(f"Application file path not found: {exe_file}") cmd = exe_file.as_posix() + override + csvinput + resultfileflag + simflags cmd = [s for s in cmd.split(' ') if s] @@ -1099,7 +1099,7 @@ def linearize(self, lintime: Optional[float] = None, simflags: Optional[str] = N simflags = " " + simflags if not exe_file.exists(): - raise Exception(f"Error: Application file path not found: {exe_file}") + raise ModelicaSystemError(f"Application file path not found: {exe_file}") else: cmd = exe_file.as_posix() + linruntime + override + csvinput + simflags cmd = [s for s in cmd.split(' ') if s] @@ -1129,8 +1129,8 @@ def linearize(self, lintime: Optional[float] = None, simflags: Optional[str] = N self.linearstates = stateVars return LinearizationResult(n, m, p, A, B, C, D, x0, u0, stateVars, inputVars, outputVars) - except ModuleNotFoundError: - raise Exception("ModuleNotFoundError: No module named 'linearized_model'") + except ModuleNotFoundError as ex: + raise ModelicaSystemError("No module named 'linearized_model'") from ex def getLinearInputs(self): """ From b2dd76ceeb1cbb3a2954f160970e1f2a0ae6135e Mon Sep 17 00:00:00 2001 From: syntron Date: Mon, 28 Apr 2025 15:53:17 +0200 Subject: [PATCH 12/16] [ModelicaSystem] remove _raise_error() --- OMPython/ModelicaSystem.py | 36 +++++++----------------------------- 1 file changed, 7 insertions(+), 29 deletions(-) diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index 74fc0c2e..df0ada1d 100644 --- a/OMPython/ModelicaSystem.py +++ b/OMPython/ModelicaSystem.py @@ -117,7 +117,6 @@ def __init__( variableFilter: Optional[str] = None, customBuildDirectory: Optional[str | os.PathLike] = None, verbose: bool = True, - raiseerrors: bool = False, omhome: Optional[str] = None, session: Optional[OMCSessionBase] = None ): @@ -145,8 +144,6 @@ def __init__( files like the model executable. If left unspecified, a tmp directory will be created. verbose: If True, enable verbose logging. - raiseerrors: If True, raise exceptions instead of just logging - OpenModelica errors. omhome: OPENMODELICAHOME value to be used when creating the OMC session. session: OMC session to be used. If unspecified, a new session @@ -202,8 +199,6 @@ def __init__( self.resultfile = "" # for storing result file self.variableFilter = variableFilter - self._raiseerrors = raiseerrors - if self.fileName is not None and not self.fileName.is_file(): # if file does not exist raise IOError(f"{self.fileName} does not exist!") @@ -316,12 +311,6 @@ def _run_cmd(self, cmd: list, timeout: Optional[int] = None): except Exception as ex: raise ModelicaSystemError(f"Error running command {cmd}") from ex - def _raise_error(self, errstr: str): - if self._raiseerrors: - raise ModelicaSystemError(f"OM error: {errstr}") - else: - logger.error(errstr) - def buildModel(self, variableFilter=None): if variableFilter is not None: self.variableFilter = variableFilter @@ -353,17 +342,12 @@ def requestApi(self, apiName, entity=None, properties=None): # 2 exp = f'{apiName}({entity})' else: exp = f'{apiName}()' - try: - res = self.sendExpression(exp) - except Exception as e: - self._raise_error(errstr=f"Exception {type(e)} raised: {e}") - res = None - return res + + return self.sendExpression(exp) def xmlparse(self): if not self.xmlFile.exists(): - self._raise_error(errstr=f"XML file not generated: {self.xmlFile}") - return + ModelicaSystemError(f"XML file not generated: {self.xmlFile}") tree = ET.parse(self.xmlFile) rootCQ = tree.getroot() @@ -703,13 +687,9 @@ def simulate(self, resultfile=None, simflags=None, timeout: Optional[int] = None self.inputlist[i] = [(float(self.simulateOptions["startTime"]), 0.0), (float(self.simulateOptions["stopTime"]), 0.0)] if float(self.simulateOptions["startTime"]) != val[0][0]: - errstr = f"!!! startTime not matched for Input {i}" - self._raise_error(errstr=errstr) - return + raise ModelicaSystemError(f"startTime not matched for Input {i}!") if float(self.simulateOptions["stopTime"]) != val[-1][0]: - errstr = f"!!! stopTime not matched for Input {i}" - self._raise_error(errstr=errstr) - return + raise ModelicaSystemError(f"stopTime not matched for Input {i}!") self.csvFile = self.createCSVData() # create csv file csvinput = " -csvInput=" + self.csvFile.as_posix() else: @@ -901,8 +881,7 @@ def setInputs(self, name): # 15 self.inputlist[value[0]] = tmpvalue self.inputFlag = True else: - errstr = value[0] + " is not an input" - self._raise_error(errstr=errstr) + raise ModelicaSystemError(f"{value[0]} is not an input") elif isinstance(name, list): name = self._strip_space(name) for var in name: @@ -917,8 +896,7 @@ def setInputs(self, name): # 15 self.inputlist[value[0]] = tmpvalue self.inputFlag = True else: - errstr = value[0] + " is not an input" - self._raise_error(errstr=errstr) + raise ModelicaSystemError(f"{value[0]} is not an input!") def checkValidInputs(self, name): if name != sorted(name, key=lambda x: x[0]): From 3dc4fa63b71482cede68d65e360d741909b2282b Mon Sep 17 00:00:00 2001 From: syntron Date: Mon, 28 Apr 2025 08:51:51 +0200 Subject: [PATCH 13/16] [tests] remove raiseerror=True --- tests/test_ModelicaSystem.py | 15 +++++++-------- tests/test_linearization.py | 2 +- tests/test_optimization.py | 3 +-- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/tests/test_ModelicaSystem.py b/tests/test_ModelicaSystem.py index 5704c950..9a0ce1c9 100644 --- a/tests/test_ModelicaSystem.py +++ b/tests/test_ModelicaSystem.py @@ -35,7 +35,7 @@ def worker(): def test_setParameters(self): omc = OMPython.OMCSessionZMQ() model_path = omc.sendExpression("getInstallationDirectoryPath()") + "/share/doc/omc/testmodels/" - mod = OMPython.ModelicaSystem(model_path + "BouncingBall.mo", "BouncingBall", raiseerrors=True) + mod = OMPython.ModelicaSystem(model_path + "BouncingBall.mo", "BouncingBall") # method 1 mod.setParameters("e=1.234") @@ -59,7 +59,7 @@ def test_setParameters(self): def test_setSimulationOptions(self): omc = OMPython.OMCSessionZMQ() model_path = omc.sendExpression("getInstallationDirectoryPath()") + "/share/doc/omc/testmodels/" - mod = OMPython.ModelicaSystem(model_path + "BouncingBall.mo", "BouncingBall", raiseerrors=True) + mod = OMPython.ModelicaSystem(model_path + "BouncingBall.mo", "BouncingBall") # method 1 mod.setSimulationOptions("stopTime=1.234") @@ -89,7 +89,7 @@ def test_relative_path(self): model_relative = str(model_file) assert "/" not in model_relative - mod = OMPython.ModelicaSystem(model_relative, "M", raiseerrors=True) + mod = OMPython.ModelicaSystem(model_relative, "M") assert float(mod.getParameters("a")[0]) == -1 finally: # clean up the temporary file @@ -99,8 +99,7 @@ def test_customBuildDirectory(self): filePath = (self.tmp / "M.mo").as_posix() tmpdir = self.tmp / "tmpdir1" tmpdir.mkdir() - m = OMPython.ModelicaSystem(filePath, "M", raiseerrors=True, - customBuildDirectory=tmpdir) + m = OMPython.ModelicaSystem(filePath, "M", customBuildDirectory=tmpdir) assert pathlib.Path(m.getWorkDirectory()).resolve() == tmpdir.resolve() result_file = tmpdir / "a.mat" assert not result_file.exists() @@ -109,7 +108,7 @@ def test_customBuildDirectory(self): def test_getSolutions(self): filePath = (self.tmp / "M.mo").as_posix() - mod = OMPython.ModelicaSystem(filePath, "M", raiseerrors=True) + mod = OMPython.ModelicaSystem(filePath, "M") x0 = 1 a = -1 tau = -1 / a @@ -145,7 +144,7 @@ def test_getters(self): y = der(x); end M_getters; """) - mod = OMPython.ModelicaSystem(model_file.as_posix(), "M_getters", raiseerrors=True) + mod = OMPython.ModelicaSystem(model_file.as_posix(), "M_getters") q = mod.getQuantities() assert isinstance(q, list) @@ -323,7 +322,7 @@ def test_simulate_inputs(self): y = x; end M_input; """) - mod = OMPython.ModelicaSystem(model_file.as_posix(), "M_input", raiseerrors=True) + mod = OMPython.ModelicaSystem(model_file.as_posix(), "M_input") mod.setSimulationOptions("stopTime=1.0") diff --git a/tests/test_linearization.py b/tests/test_linearization.py index 2cc49fed..bf759fde 100644 --- a/tests/test_linearization.py +++ b/tests/test_linearization.py @@ -61,7 +61,7 @@ def test_getters(self): y2 = phi + u1; end Pendulum; """) - mod = OMPython.ModelicaSystem(model_file.as_posix(), "Pendulum", ["Modelica"], raiseerrors=True) + mod = OMPython.ModelicaSystem(model_file.as_posix(), "Pendulum", ["Modelica"]) d = mod.getLinearizationOptions() assert isinstance(d, dict) diff --git a/tests/test_optimization.py b/tests/test_optimization.py index ae93d0b8..283df062 100644 --- a/tests/test_optimization.py +++ b/tests/test_optimization.py @@ -45,8 +45,7 @@ def test_example(self): end BangBang2021; """) - mod = OMPython.ModelicaSystem(model_file.as_posix(), "BangBang2021", - raiseerrors=True) + mod = OMPython.ModelicaSystem(model_file.as_posix(), "BangBang2021") mod.setOptimizationOptions(["numberOfIntervals=16", "stopTime=1", "stepSize=0.001", "tolerance=1e-8"]) From 128af6789ead965e34d4f36ebc347b5d52b4e7ea Mon Sep 17 00:00:00 2001 From: syntron Date: Sat, 26 Apr 2025 22:54:50 +0200 Subject: [PATCH 14/16] [ModelicaSystem] remove verbose - use logger.debug --- OMPython/ModelicaSystem.py | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index df0ada1d..40fb754e 100644 --- a/OMPython/ModelicaSystem.py +++ b/OMPython/ModelicaSystem.py @@ -116,7 +116,6 @@ def __init__( commandLineOptions: Optional[str] = None, variableFilter: Optional[str] = None, customBuildDirectory: Optional[str | os.PathLike] = None, - verbose: bool = True, omhome: Optional[str] = None, session: Optional[OMCSessionBase] = None ): @@ -143,7 +142,6 @@ def __init__( customBuildDirectory: Path to a directory to be used for temporary files like the model executable. If left unspecified, a tmp directory will be created. - verbose: If True, enable verbose logging. omhome: OPENMODELICAHOME value to be used when creating the OMC session. session: OMC session to be used. If unspecified, a new session @@ -173,8 +171,6 @@ def __init__( self.linearstates = [] # linearization states list self.tempdir = "" - self._verbose = verbose - if session is not None: if not isinstance(session, OMCSessionZMQ): raise ModelicaSystemError("Invalid session data provided!") @@ -300,12 +296,13 @@ def _run_cmd(self, cmd: list, timeout: Optional[int] = None): timeout=timeout) stdout = cmdres.stdout.strip() stderr = cmdres.stderr.strip() + + logger.debug("OM output for command %s:\n%s", cmd, stdout) + if cmdres.returncode != 0: raise ModelicaSystemError(f"Error running command {cmd}: return code = {cmdres.returncode}") if stderr: raise ModelicaSystemError(f"Error running command {cmd}: {stderr}") - if self._verbose and stdout: - logger.info("OM output for command %s:\n%s", cmd, stdout) except subprocess.TimeoutExpired: raise ModelicaSystemError(f"Timeout running command {repr(cmd)}") except Exception as ex: @@ -321,8 +318,7 @@ def buildModel(self, variableFilter=None): varFilter = 'variableFilter=".*"' logger.debug("varFilter=%s", varFilter) buildModelResult = self.requestApi("buildModel", self.modelName, properties=varFilter) - if self._verbose: - logger.info("OM model build result: %s", buildModelResult) + logger.debug("OM model build result: %s", buildModelResult) self.xmlFile = pathlib.Path(buildModelResult[0]).parent / buildModelResult[1] self.xmlparse() @@ -821,12 +817,10 @@ def setParameters(self, pvals): # 14 def isParameterChangeable(self, name, value): q = self.getQuantities(name) if q[0]["changeable"] == "false": - if self._verbose: - logger.info("setParameters() failed : It is not possible to set " - f'the following signal "{name}", It seems to be structural, final, ' - "protected or evaluated or has a non-constant binding, use sendExpression(" - f"setParameterValue({self.modelName}, {name}, {value}), " - "parsed=false) and rebuild the model using buildModel() API") + logger.verbose(f"setParameters() failed : It is not possible to set the following signal {repr(name)}. " + "It seems to be structural, final, protected or evaluated or has a non-constant binding, " + f"use sendExpression(\"setParameterValue({self.modelName}, {name}, {value})\", " + "parsed=False) and rebuild the model using buildModel() API") return False return True From d67a8d65bc212791460599e8b29d939fc9cf0c79 Mon Sep 17 00:00:00 2001 From: syntron Date: Mon, 28 Apr 2025 08:52:58 +0200 Subject: [PATCH 15/16] [ModelicaSystem] check that lmodel is defined as list --- OMPython/ModelicaSystem.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index 40fb754e..e0e34767 100644 --- a/OMPython/ModelicaSystem.py +++ b/OMPython/ModelicaSystem.py @@ -184,6 +184,9 @@ def __init__( if lmodel is None: lmodel = [] + if not isinstance(lmodel, list): + raise ModelicaSystemError(f"Invalid input type for lmodel: {type(lmodel)} - list expected!") + self.xmlFile = None self.lmodel = lmodel # may be needed if model is derived from other model self.modelName = modelName # Model class name From 1433499b1489c86d9c1c323752d6162de068b0ee Mon Sep 17 00:00:00 2001 From: syntron Date: Wed, 30 Apr 2025 20:25:05 +0200 Subject: [PATCH 16/16] [ModelicaSystem] remove OMCSessionException for now --- OMPython/ModelicaSystem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index e0e34767..97dc844c 100644 --- a/OMPython/ModelicaSystem.py +++ b/OMPython/ModelicaSystem.py @@ -420,7 +420,7 @@ def getContinuous(self, names=None): # 4 try: value = self.getSolutions(i) self.continuouslist[i] = value[0][-1] - except OMCSessionException as ex: + except Exception as ex: raise ModelicaSystemError(f"{i} could not be computed") from ex return self.continuouslist