From cc44f53e2cf48676784534f6844236b875b3b7d9 Mon Sep 17 00:00:00 2001 From: syntron Date: Wed, 30 Oct 2024 21:41:17 +0100 Subject: [PATCH 1/4] [ModelicaSystem.simulate] include c library files from external Modelica libraries --- OMPython/__init__.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/OMPython/__init__.py b/OMPython/__init__.py index 14ee86e1b..20c724edb 100755 --- a/OMPython/__init__.py +++ b/OMPython/__init__.py @@ -1288,7 +1288,19 @@ def simulate(self, resultfile=None, simflags=None, verbose=True): # 11 os.chdir(self.tempdir) if (platform.system() == "Windows"): omhome = os.path.join(os.environ.get("OPENMODELICAHOME")) - dllPath = os.path.join(omhome, "bin").replace("\\", "/") + os.pathsep + os.path.join(omhome, "lib/omc").replace("\\", "/") + os.pathsep + os.path.join(omhome, "lib/omc/cpp").replace("\\", "/") + os.pathsep + os.path.join(omhome, "lib/omc/omsicpp").replace("\\", "/") + dllPath = os.path.join(omhome, "bin").replace("\\", "/") + os.pathsep + \ + os.path.join(omhome, "lib/omc").replace("\\", "/") + os.pathsep + \ + os.path.join(omhome, "lib/omc/cpp").replace("\\", "/") + os.pathsep + \ + os.path.join(omhome, "lib/omc/omsicpp").replace("\\", "/") + for element in self.lmodel: + if element is not None: + if isinstance(element, str): + if element.endswith("package.mo"): + pkgpath = element[:-10] + '/Resources/Library/' + for wver in ['win32', 'win64']: + pkgpath_wver = pkgpath + '/' + wver + if os.path.exists(pkgpath_wver): + dllPath = pkgpath_wver + os.pathsep + dllPath my_env = os.environ.copy() my_env["PATH"] = dllPath + os.pathsep + my_env["PATH"] if not verbose: From 7fd956a885ee620577a00dd67fc61ae1e04e59aa Mon Sep 17 00:00:00 2001 From: syntron Date: Wed, 30 Oct 2024 21:42:10 +0100 Subject: [PATCH 2/4] [ModelicaSystem.simulate] simplify usage of subprocess.Popen() * difference between win and linux is only in the definition of my_env --- OMPython/__init__.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/OMPython/__init__.py b/OMPython/__init__.py index 20c724edb..59872b66e 100755 --- a/OMPython/__init__.py +++ b/OMPython/__init__.py @@ -1303,15 +1303,15 @@ def simulate(self, resultfile=None, simflags=None, verbose=True): # 11 dllPath = pkgpath_wver + os.pathsep + dllPath my_env = os.environ.copy() my_env["PATH"] = dllPath + os.pathsep + my_env["PATH"] - if not verbose: - p = subprocess.Popen(cmd, env=my_env, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT) - else: - p = subprocess.Popen(cmd, env=my_env) else: - if not verbose: - p = subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT) - else: - p = subprocess.Popen(cmd) + my_env = None + + p = subprocess.Popen(cmd, env=my_env, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = p.communicate() + if stderr: + logger.warning(f"OM error: {stderr.decode('ascii')}") + if verbose and stdout: + logger.info(f"OM output:\n{stdout.decode('ascii').strip()}") p.wait() p.terminate() os.chdir(currentDir) From 3dfc0dea6210cd585f6c6f3cbb68b25193dbbd16 Mon Sep 17 00:00:00 2001 From: syntron Date: Wed, 30 Oct 2024 21:42:46 +0100 Subject: [PATCH 3/4] [ModelicaSystem._run_cmd] merge all calls to executeable into one function * merge all calls to executeables into one function - _run_cmd() * simplify & cleanup the code --- OMPython/__init__.py | 105 ++++++++++++++++++++++--------------------- 1 file changed, 55 insertions(+), 50 deletions(-) diff --git a/OMPython/__init__.py b/OMPython/__init__.py index 59872b66e..b0a4af91e 100755 --- a/OMPython/__init__.py +++ b/OMPython/__init__.py @@ -928,6 +928,55 @@ def setTempDirectory(self, customBuildDirectory): def getWorkDirectory(self): return self.tempdir + def _run_cmd(self, cmd: list, verbose: bool = True): + logger.debug("Run OM command {} in {}".format(cmd, self.tempdir)) + + if platform.system() == "Windows": + omhome = os.path.join(os.environ.get("OPENMODELICAHOME")) + dllPath = (os.path.join(omhome, "bin") + + os.pathsep + os.path.join(omhome, "lib/omc") + + os.pathsep + os.path.join(omhome, "lib/omc/cpp") + + os.pathsep + os.path.join(omhome, "lib/omc/omsicpp")) + + # include path to resources of defined external libraries + for element in self.lmodel: + if element is not None: + if isinstance(element, str): + if element.endswith("package.mo"): + pkgpath = element[:-10] + '/Resources/Library/' + for wver in ['win32', 'win64']: + pkgpath_wver = pkgpath + '/' + wver + if os.path.exists(pkgpath_wver): + dllPath = pkgpath_wver + os.pathsep + dllPath + + # fix backslash in path definitions + dllPath = dllPath.replace("\\", "/") + + my_env = os.environ.copy() + my_env["PATH"] = dllPath + os.pathsep + my_env["PATH"] + else: + # TODO: how to handle path to resources of external libraries for any system not Windows? + my_env = None + + currentDir = os.getcwd() + try: + os.chdir(self.tempdir) + p = subprocess.Popen(cmd, env=my_env, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = p.communicate() + + stdout = stdout.decode('ascii').strip() + stderr = stderr.decode('ascii').strip() + if stderr: + logger.warning("OM error: {}".format(stderr)) + if verbose and stdout: + logger.info("OM output:\n{}".format(stdout)) + p.wait() + p.terminate() + os.chdir(currentDir) + except Exception as e: + os.chdir(currentDir) + raise Exception("Error running command {}: {}".format(repr(cmd), e)) + def buildModel(self, variableFilter=None, verbose=True): if variableFilter is not None: self.variableFilter = variableFilter @@ -1280,44 +1329,15 @@ def simulate(self, resultfile=None, simflags=None, verbose=True): # 11 getExeFile = os.path.join(self.tempdir, '{}.{}'.format(self.modelName, "exe")).replace("\\", "/") else: getExeFile = os.path.join(self.tempdir, self.modelName).replace("\\", "/") - currentDir = os.getcwd() - if (os.path.exists(getExeFile)): + + if os.path.exists(getExeFile): cmd = getExeFile + override + csvinput + r + simflags cmd = cmd.split(" ") - #print(cmd) - os.chdir(self.tempdir) - if (platform.system() == "Windows"): - omhome = os.path.join(os.environ.get("OPENMODELICAHOME")) - dllPath = os.path.join(omhome, "bin").replace("\\", "/") + os.pathsep + \ - os.path.join(omhome, "lib/omc").replace("\\", "/") + os.pathsep + \ - os.path.join(omhome, "lib/omc/cpp").replace("\\", "/") + os.pathsep + \ - os.path.join(omhome, "lib/omc/omsicpp").replace("\\", "/") - for element in self.lmodel: - if element is not None: - if isinstance(element, str): - if element.endswith("package.mo"): - pkgpath = element[:-10] + '/Resources/Library/' - for wver in ['win32', 'win64']: - pkgpath_wver = pkgpath + '/' + wver - if os.path.exists(pkgpath_wver): - dllPath = pkgpath_wver + os.pathsep + dllPath - my_env = os.environ.copy() - my_env["PATH"] = dllPath + os.pathsep + my_env["PATH"] - else: - my_env = None + self._run_cmd(cmd=cmd, verbose=verbose) - p = subprocess.Popen(cmd, env=my_env, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = p.communicate() - if stderr: - logger.warning(f"OM error: {stderr.decode('ascii')}") - if verbose and stdout: - logger.info(f"OM output:\n{stdout.decode('ascii').strip()}") - p.wait() - p.terminate() - os.chdir(currentDir) self.simulationFlag = True else: - raise Exception("Error: Application file path not found: " + getExeFile) + raise Exception("Error: Application file path not found: " + getExeFile) # to extract simulation results def getSolutions(self, varList=None, resultfile=None): # 12 @@ -1753,23 +1773,11 @@ def linearize(self, lintime = None, simflags= None): # 22 if simflags is None: simflags = "" - currentDir = os.getcwd() if (os.path.exists(getExeFile)): cmd = getExeFile + linruntime + override + csvinput + simflags - # print(cmd) - os.chdir(self.tempdir) - if (platform.system() == "Windows"): - omhome = os.path.join(os.environ.get("OPENMODELICAHOME")) - dllPath = os.path.join(omhome, "bin").replace("\\", "/") + os.pathsep + os.path.join(omhome, "lib/omc").replace("\\", "/") + os.pathsep + os.path.join(omhome, "lib/omc/cpp").replace("\\", "/") + os.pathsep + os.path.join(omhome, "lib/omc/omsicpp").replace("\\", "/") - my_env = os.environ.copy() - my_env["PATH"] = dllPath + os.pathsep + my_env["PATH"] - p = subprocess.Popen(cmd, env=my_env) - p.wait() - p.terminate() - else: - os.system(cmd) + cmd = cmd.split(' ') + self._run_cmd(cmd=cmd) else: - os.chdir(currentDir) raise Exception("Error: Application file path not found: " + getExeFile) # code to get the matrix and linear inputs, outputs and states @@ -1792,13 +1800,10 @@ def linearize(self, lintime = None, simflags= None): # 22 self.linearoutputs = outputVars self.linearstates = stateVars return [A, B, C, D] - os.chdir(currentDir) except: - os.chdir(currentDir) raise Exception("ModuleNotFoundError: No module named 'linearized_model'") else: errormsg = self.getconn.sendExpression("getErrorString()") - os.chdir(currentDir) return print("Linearization failed: ", "\"" , linearFile,"\"" ," not found \n", errormsg) From 82d3f4c6b2d87a30368ff7f6cbf30db868e82e6d Mon Sep 17 00:00:00 2001 From: arun3688 Date: Thu, 7 Nov 2024 13:33:16 +0100 Subject: [PATCH 4/4] add linearization test --- tests/test_linearization.py | 38 +++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 tests/test_linearization.py diff --git a/tests/test_linearization.py b/tests/test_linearization.py new file mode 100644 index 000000000..c35979d2c --- /dev/null +++ b/tests/test_linearization.py @@ -0,0 +1,38 @@ +import OMPython +import tempfile, shutil, os +import pytest + +class Test_Linearization: + def loadModel(self): + self.tmp = tempfile.mkdtemp(prefix='tmpOMPython.tests') + with open("%s/linearTest.mo" % self.tmp, "w") as fout: + fout.write(""" +model linearTest + Real x1(start=1); + Real x2(start=-2); + Real x3(start=3); + Real x4(start=-5); + parameter Real a=3,b=2,c=5,d=7,e=1,f=4; +equation + a*x1 = b*x2 -der(x1); + der(x2) + c*x3 + d*x1 = x4; + f*x4 - e*x3 - der(x3) = x1; + der(x4) = x1 + x2 + der(x3) + x4; +end linearTest; +""") + + def __del__(self): + shutil.rmtree(self.tmp, ignore_errors=True) + + def test_example(self): + self.loadModel() + filePath = os.path.join(self.tmp,"linearTest.mo").replace("\\", "/") + print(filePath) + mod = OMPython.ModelicaSystem(filePath, "linearTest") + [A, B, C, D] = mod.linearize() + expected_matrixA = [[-3, 2, 0, 0], [-7, 0, -5, 1], [-1, 0, -1, 4], [0, 1, -1, 5]] + assert A == expected_matrixA, f"Matrix does not match the expected value. Got: {A}, Expected: {expected_matrixA}" + assert B == [], f"Matrix does not match the expected value. Got: {B}, Expected: {[]}" + assert C == [], f"Matrix does not match the expected value. Got: {C}, Expected: {[]}" + assert D == [], f"Matrix does not match the expected value. Got: {D}, Expected: {[]}" +