From ca7ffaf0b2133999bcc26ffb5a3a69b9114094de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sluka?= Date: Wed, 28 May 2025 20:50:56 +0200 Subject: [PATCH 1/2] Remove unittest --- tests/__init__.py | 1 - tests/test_ArrayDimension.py | 37 +- tests/test_FMIExport.py | 40 +- tests/test_FMIRegression.py | 81 ++-- tests/test_ModelicaSystem.py | 724 ++++++++++++++++---------------- tests/test_ModelicaSystemCmd.py | 58 ++- tests/test_OMParser.py | 58 +-- tests/test_OMSessionCmd.py | 27 +- tests/test_ZMQ.py | 83 ++-- tests/test_docker.py | 28 +- tests/test_linearization.py | 152 ++++--- tests/test_optimization.py | 102 ++--- tests/test_typedParser.py | 51 ++- 13 files changed, 683 insertions(+), 759 deletions(-) diff --git a/tests/__init__.py b/tests/__init__.py index df2f5174..e69de29b 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1 +0,0 @@ -__all__ = ['tests.test_OMParser', 'tests.test_ZMQ', 'tests.test_ModelicaSystem'] diff --git a/tests/test_ArrayDimension.py b/tests/test_ArrayDimension.py index abb368cb..13b3c11b 100644 --- a/tests/test_ArrayDimension.py +++ b/tests/test_ArrayDimension.py @@ -1,34 +1,19 @@ import OMPython -import tempfile -import shutil -import os -# do not change the prefix class name, the class name should have prefix "Test" -# according to the documenation of pytest -class Test_ArrayDimension: - def test_ArrayDimension(self): - omc = OMPython.OMCSessionZMQ() +def test_ArrayDimension(tmp_path): + omc = OMPython.OMCSessionZMQ() - # create a temp dir for each session - tempdir = tempfile.mkdtemp() - if not os.path.exists(tempdir): - return print(tempdir, " cannot be created") + omc.sendExpression(f'cd("{tmp_path.as_posix()}")') - tempdirExp = "".join(["cd(", "\"", tempdir, "\"", ")"]).replace("\\", "/") - omc.sendExpression(tempdirExp) + omc.sendExpression('loadString("model A Integer x[5+1,1+6]; end A;")') + omc.sendExpression("getErrorString()") - omc.sendExpression("loadString(\"model A Integer x[5+1,1+6]; end A;\")") - omc.sendExpression("getErrorString()") + result = omc.sendExpression("getComponents(A)") + assert result[0][-1] == (6, 7), "array dimension does not match" - result = omc.sendExpression("getComponents(A)") - assert result[0][-1] == (6, 7), f"array dimension does not match the expected value. Got: {result[0][-1]}, Expected: {(6, 7)}" + omc.sendExpression('loadString("model A Integer y = 5; Integer x[y+1,1+9]; end A;")') + omc.sendExpression("getErrorString()") - omc.sendExpression("loadString(\"model A Integer y = 5; Integer x[y+1,1+9]; end A;\")") - omc.sendExpression("getErrorString()") - - result = omc.sendExpression("getComponents(A)") - assert result[-1][-1] == ('y+1', 10), f"array dimension does not match the expected value. Got: {result[-1][-1]}, Expected: {('y+1', 10)}" - - omc.__del__() - shutil.rmtree(tempdir, ignore_errors=True) + result = omc.sendExpression("getComponents(A)") + assert result[-1][-1] == ('y+1', 10), "array dimension does not match" diff --git a/tests/test_FMIExport.py b/tests/test_FMIExport.py index 0d6d0ff9..f47b87ae 100644 --- a/tests/test_FMIExport.py +++ b/tests/test_FMIExport.py @@ -1,34 +1,24 @@ import OMPython -import unittest import shutil import os -class testFMIExport(unittest.TestCase): - def __init__(self, *args, **kwargs): - super(testFMIExport, self).__init__(*args, **kwargs) - self.tmp = "" - - def __del__(self): - shutil.rmtree(self.tmp, ignore_errors=True) - - def testCauerLowPassAnalog(self): - print("testing Cauer") - mod = OMPython.ModelicaSystem(modelName="Modelica.Electrical.Analog.Examples.CauerLowPassAnalog", - lmodel=["Modelica"]) - self.tmp = mod.getWorkDirectory() - +def test_CauerLowPassAnalog(): + mod = OMPython.ModelicaSystem(modelName="Modelica.Electrical.Analog.Examples.CauerLowPassAnalog", + lmodel=["Modelica"]) + tmp = mod.getWorkDirectory() + try: fmu = mod.convertMo2Fmu(fileNamePrefix="CauerLowPassAnalog") - self.assertEqual(True, os.path.exists(fmu)) + assert os.path.exists(fmu) + finally: + shutil.rmtree(tmp, ignore_errors=True) - def testDrumBoiler(self): - print("testing DrumBoiler") - mod = OMPython.ModelicaSystem(modelName="Modelica.Fluid.Examples.DrumBoiler.DrumBoiler", lmodel=["Modelica"]) - self.tmp = mod.getWorkDirectory() +def test_DrumBoiler(): + mod = OMPython.ModelicaSystem(modelName="Modelica.Fluid.Examples.DrumBoiler.DrumBoiler", lmodel=["Modelica"]) + tmp = mod.getWorkDirectory() + try: fmu = mod.convertMo2Fmu(fileNamePrefix="DrumBoiler") - self.assertEqual(True, os.path.exists(fmu)) - - -if __name__ == '__main__': - unittest.main() + assert os.path.exists(fmu) + finally: + shutil.rmtree(tmp, ignore_errors=True) diff --git a/tests/test_FMIRegression.py b/tests/test_FMIRegression.py index b39fdf1a..60c23e07 100644 --- a/tests/test_FMIRegression.py +++ b/tests/test_FMIRegression.py @@ -1,65 +1,68 @@ import OMPython import tempfile +import pathlib import shutil import os -# do not change the prefix class name, the class name should have prefix "Test" -# according to the documenation of pytest -class Test_FMIRegression: +def buildModelFMU(modelName): + omc = OMPython.OMCSessionZMQ() - def buildModelFMU(self, modelName): - omc = OMPython.OMCSessionZMQ() - - # create a temp dir for each session - tempdir = tempfile.mkdtemp() - if not os.path.exists(tempdir): - return print(tempdir, " cannot be created") - - tempdirExp = "".join(["cd(", "\"", tempdir, "\"", ")"]).replace("\\", "/") - omc.sendExpression(tempdirExp) + tempdir = pathlib.Path(tempfile.mkdtemp()) + try: + omc.sendExpression(f'cd("{tempdir.as_posix()}")') omc.sendExpression("loadModel(Modelica)") omc.sendExpression("getErrorString()") fileNamePrefix = modelName.split(".")[-1] - exp = "buildModelFMU(" + modelName + ", fileNamePrefix=\"" + fileNamePrefix + "\"" + ")" - + exp = f'buildModelFMU({modelName}, fileNamePrefix="{fileNamePrefix}")' fmu = omc.sendExpression(exp) assert os.path.exists(fmu) - - omc.__del__() + finally: + del omc shutil.rmtree(tempdir, ignore_errors=True) - def test_Modelica_Blocks_Examples_Filter(self): - self.buildModelFMU("Modelica.Blocks.Examples.Filter") - def test_Modelica_Blocks_Examples_RealNetwork1(self): - self.buildModelFMU("Modelica.Blocks.Examples.RealNetwork1") +def test_Modelica_Blocks_Examples_Filter(): + buildModelFMU("Modelica.Blocks.Examples.Filter") + + +def test_Modelica_Blocks_Examples_RealNetwork1(): + buildModelFMU("Modelica.Blocks.Examples.RealNetwork1") + + +def test_Modelica_Electrical_Analog_Examples_CauerLowPassAnalog(): + buildModelFMU("Modelica.Electrical.Analog.Examples.CauerLowPassAnalog") + + +def test_Modelica_Electrical_Digital_Examples_FlipFlop(): + buildModelFMU("Modelica.Electrical.Digital.Examples.FlipFlop") + + +def test_Modelica_Mechanics_Rotational_Examples_FirstGrounded(): + buildModelFMU("Modelica.Mechanics.Rotational.Examples.FirstGrounded") + + +def test_Modelica_Mechanics_Rotational_Examples_CoupledClutches(): + buildModelFMU("Modelica.Mechanics.Rotational.Examples.CoupledClutches") + - def test_Modelica_Electrical_Analog_Examples_CauerLowPassAnalog(self): - self.buildModelFMU("Modelica.Electrical.Analog.Examples.CauerLowPassAnalog") +def test_Modelica_Mechanics_MultiBody_Examples_Elementary_DoublePendulum(): + buildModelFMU("Modelica.Mechanics.MultiBody.Examples.Elementary.DoublePendulum") - def test_Modelica_Electrical_Digital_Examples_FlipFlop(self): - self.buildModelFMU("Modelica.Electrical.Digital.Examples.FlipFlop") - def test_Modelica_Mechanics_Rotational_Examples_FirstGrounded(self): - self.buildModelFMU("Modelica.Mechanics.Rotational.Examples.FirstGrounded") +def test_Modelica_Mechanics_MultiBody_Examples_Elementary_FreeBody(): + buildModelFMU("Modelica.Mechanics.MultiBody.Examples.Elementary.FreeBody") - def test_Modelica_Mechanics_Rotational_Examples_CoupledClutches(self): - self.buildModelFMU("Modelica.Mechanics.Rotational.Examples.CoupledClutches") - def test_Modelica_Mechanics_MultiBody_Examples_Elementary_DoublePendulum(self): - self.buildModelFMU("Modelica.Mechanics.MultiBody.Examples.Elementary.DoublePendulum") +def test_Modelica_Fluid_Examples_PumpingSystem(): + buildModelFMU("Modelica.Fluid.Examples.PumpingSystem") - def test_Modelica_Mechanics_MultiBody_Examples_Elementary_FreeBody(self): - self.buildModelFMU("Modelica.Mechanics.MultiBody.Examples.Elementary.FreeBody") - def test_Modelica_Fluid_Examples_PumpingSystem(self): - self.buildModelFMU("Modelica.Fluid.Examples.PumpingSystem") +def test_Modelica_Fluid_Examples_TraceSubstances_RoomCO2WithControls(): + buildModelFMU("Modelica.Fluid.Examples.TraceSubstances.RoomCO2WithControls") - def test_Modelica_Fluid_Examples_TraceSubstances_RoomCO2WithControls(self): - self.buildModelFMU("Modelica.Fluid.Examples.TraceSubstances.RoomCO2WithControls") - def test_Modelica_Clocked_Examples_SimpleControlledDrive_ClockedWithDiscreteTextbookController(self): - self.buildModelFMU("Modelica.Clocked.Examples.SimpleControlledDrive.ClockedWithDiscreteTextbookController") +def test_Modelica_Clocked_Examples_SimpleControlledDrive_ClockedWithDiscreteTextbookController(): + buildModelFMU("Modelica.Clocked.Examples.SimpleControlledDrive.ClockedWithDiscreteTextbookController") diff --git a/tests/test_ModelicaSystem.py b/tests/test_ModelicaSystem.py index 66dfd90d..202e066d 100644 --- a/tests/test_ModelicaSystem.py +++ b/tests/test_ModelicaSystem.py @@ -1,390 +1,388 @@ import OMPython -import unittest -import tempfile -import shutil import os import pathlib +import pytest +import tempfile import numpy as np -class ModelicaSystemTester(unittest.TestCase): - def __init__(self, *args, **kwargs): - super(ModelicaSystemTester, self).__init__(*args, **kwargs) - self.tmp = pathlib.Path(tempfile.mkdtemp(prefix='tmpOMPython.tests')) - with open(self.tmp / "M.mo", "w") as fout: - fout.write("""model M +@pytest.fixture +def model_firstorder(tmp_path): + mod = tmp_path / "M.mo" + mod.write_text("""model M Real x(start = 1, fixed = true); parameter Real a = -1; equation der(x) = x*a; end M; - """) - - def __del__(self): - shutil.rmtree(self.tmp, ignore_errors=True) - - def testModelicaSystemLoop(self): - def worker(): - filePath = (self.tmp / "M.mo").as_posix() - m = OMPython.ModelicaSystem(filePath, "M") - m.simulate() - m.convertMo2Fmu(fmuType="me") - for _ in range(10): - 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") - - # method 1 - mod.setParameters("e=1.234") - mod.setParameters("g=321.0") - assert mod.getParameters("e") == ["1.234"] - assert mod.getParameters("g") == ["321.0"] - assert mod.getParameters() == { - "e": "1.234", - "g": "321.0", - } - - # method 2 - mod.setParameters(["e=21.3", "g=0.12"]) - assert mod.getParameters() == { - "e": "21.3", - "g": "0.12", - } - assert mod.getParameters(["e", "g"]) == ["21.3", "0.12"] - assert mod.getParameters(["g", "e"]) == ["0.12", "21.3"] - - def test_setSimulationOptions(self): - omc = OMPython.OMCSessionZMQ() - model_path = omc.sendExpression("getInstallationDirectoryPath()") + "/share/doc/omc/testmodels/" - mod = OMPython.ModelicaSystem(model_path + "BouncingBall.mo", "BouncingBall") - - # method 1 - mod.setSimulationOptions("stopTime=1.234") - mod.setSimulationOptions("tolerance=1.1e-08") - assert mod.getSimulationOptions("stopTime") == ["1.234"] - assert mod.getSimulationOptions("tolerance") == ["1.1e-08"] - assert mod.getSimulationOptions(["tolerance", "stopTime"]) == ["1.1e-08", "1.234"] - d = mod.getSimulationOptions() - assert isinstance(d, dict) - assert d["stopTime"] == "1.234" - assert d["tolerance"] == "1.1e-08" - - # method 2 - mod.setSimulationOptions(["stopTime=2.1", "tolerance=1.2e-08"]) - d = mod.getSimulationOptions() - assert d["stopTime"] == "2.1" - assert d["tolerance"] == "1.2e-08" - - def test_relative_path(self): - cwd = pathlib.Path.cwd() - (fd, name) = tempfile.mkstemp(prefix='tmpOMPython.tests', dir=cwd, text=True) - try: - with os.fdopen(fd, 'w') as f: - f.write((self.tmp / "M.mo").read_text()) - - model_file = pathlib.Path(name).relative_to(cwd) - model_relative = str(model_file) - assert "/" not in model_relative - - mod = OMPython.ModelicaSystem(model_relative, "M") - assert float(mod.getParameters("a")[0]) == -1 - finally: - # clean up the temporary file - model_file.unlink() - - def test_customBuildDirectory(self): - filePath = (self.tmp / "M.mo").as_posix() - tmpdir = self.tmp / "tmpdir1" - tmpdir.mkdir() - m = OMPython.ModelicaSystem(filePath, "M", customBuildDirectory=tmpdir) - assert m.getWorkDirectory().resolve() == tmpdir.resolve() - result_file = tmpdir / "a.mat" - assert not result_file.exists() - m.simulate(resultfile="a.mat") - assert result_file.is_file() - - def test_getSolutions(self): - filePath = (self.tmp / "M.mo").as_posix() - mod = OMPython.ModelicaSystem(filePath, "M") - x0 = 1 - a = -1 - tau = -1 / a - stopTime = 5*tau - mod.setSimulationOptions([f"stopTime={stopTime}", "stepSize=0.1", "tolerance=1e-8"]) - mod.simulate() - - x = mod.getSolutions("x") - t, x2 = mod.getSolutions(["time", "x"]) - assert (x2 == x).all() - sol_names = mod.getSolutions() - assert isinstance(sol_names, tuple) - assert "time" in sol_names - assert "x" in sol_names - assert "der(x)" in sol_names - with self.assertRaises(OMPython.ModelicaSystemError): - mod.getSolutions("t") # variable 't' does not exist - assert np.isclose(t[0], 0), "time does not start at 0" - assert np.isclose(t[-1], stopTime), "time does not end at stopTime" - x_analytical = x0 * np.exp(a*t) - assert np.isclose(x, x_analytical, rtol=1e-4).all() - - def test_getters(self): - model_file = self.tmp / "M_getters.mo" - model_file.write_text(""" +""") + return mod + + +def test_ModelicaSystem_loop(model_firstorder): + def worker(): + filePath = model_firstorder.as_posix() + m = OMPython.ModelicaSystem(filePath, "M") + m.simulate() + m.convertMo2Fmu(fmuType="me") + for _ in range(10): + worker() + + +def test_setParameters(): + omc = OMPython.OMCSessionZMQ() + model_path = omc.sendExpression("getInstallationDirectoryPath()") + "/share/doc/omc/testmodels/" + mod = OMPython.ModelicaSystem(model_path + "BouncingBall.mo", "BouncingBall") + + # method 1 + mod.setParameters("e=1.234") + mod.setParameters("g=321.0") + assert mod.getParameters("e") == ["1.234"] + assert mod.getParameters("g") == ["321.0"] + assert mod.getParameters() == { + "e": "1.234", + "g": "321.0", + } + + # method 2 + mod.setParameters(["e=21.3", "g=0.12"]) + assert mod.getParameters() == { + "e": "21.3", + "g": "0.12", + } + assert mod.getParameters(["e", "g"]) == ["21.3", "0.12"] + assert mod.getParameters(["g", "e"]) == ["0.12", "21.3"] + + +def test_setSimulationOptions(): + omc = OMPython.OMCSessionZMQ() + model_path = omc.sendExpression("getInstallationDirectoryPath()") + "/share/doc/omc/testmodels/" + mod = OMPython.ModelicaSystem(model_path + "BouncingBall.mo", "BouncingBall") + + # method 1 + mod.setSimulationOptions("stopTime=1.234") + mod.setSimulationOptions("tolerance=1.1e-08") + assert mod.getSimulationOptions("stopTime") == ["1.234"] + assert mod.getSimulationOptions("tolerance") == ["1.1e-08"] + assert mod.getSimulationOptions(["tolerance", "stopTime"]) == ["1.1e-08", "1.234"] + d = mod.getSimulationOptions() + assert isinstance(d, dict) + assert d["stopTime"] == "1.234" + assert d["tolerance"] == "1.1e-08" + + # method 2 + mod.setSimulationOptions(["stopTime=2.1", "tolerance=1.2e-08"]) + d = mod.getSimulationOptions() + assert d["stopTime"] == "2.1" + assert d["tolerance"] == "1.2e-08" + + +def test_relative_path(model_firstorder): + cwd = pathlib.Path.cwd() + (fd, name) = tempfile.mkstemp(prefix='tmpOMPython.tests', dir=cwd, text=True) + try: + with os.fdopen(fd, 'w') as f: + f.write(model_firstorder.read_text()) + + model_file = pathlib.Path(name).relative_to(cwd) + model_relative = str(model_file) + assert "/" not in model_relative + + mod = OMPython.ModelicaSystem(model_relative, "M") + assert float(mod.getParameters("a")[0]) == -1 + finally: + model_file.unlink() # clean up the temporary file + + +def test_customBuildDirectory(tmp_path, model_firstorder): + filePath = model_firstorder.as_posix() + tmpdir = tmp_path / "tmpdir1" + tmpdir.mkdir() + m = OMPython.ModelicaSystem(filePath, "M", customBuildDirectory=tmpdir) + assert m.getWorkDirectory().resolve() == tmpdir.resolve() + result_file = tmpdir / "a.mat" + assert not result_file.exists() + m.simulate(resultfile="a.mat") + assert result_file.is_file() + + +def test_getSolutions(model_firstorder): + filePath = model_firstorder.as_posix() + mod = OMPython.ModelicaSystem(filePath, "M") + x0 = 1 + a = -1 + tau = -1 / a + stopTime = 5*tau + mod.setSimulationOptions([f"stopTime={stopTime}", "stepSize=0.1", "tolerance=1e-8"]) + mod.simulate() + + x = mod.getSolutions("x") + t, x2 = mod.getSolutions(["time", "x"]) + assert (x2 == x).all() + sol_names = mod.getSolutions() + assert isinstance(sol_names, tuple) + assert "time" in sol_names + assert "x" in sol_names + assert "der(x)" in sol_names + with pytest.raises(OMPython.ModelicaSystemError): + mod.getSolutions("t") # variable 't' does not exist + assert np.isclose(t[0], 0), "time does not start at 0" + assert np.isclose(t[-1], stopTime), "time does not end at stopTime" + x_analytical = x0 * np.exp(a*t) + assert np.isclose(x, x_analytical, rtol=1e-4).all() + + +def test_getters(tmp_path): + model_file = tmp_path / "M_getters.mo" + model_file.write_text(""" model M_getters - Real x(start = 1, fixed = true); - output Real y "the derivative"; - parameter Real a = -0.5; - parameter Real b = 0.1; +Real x(start = 1, fixed = true); +output Real y "the derivative"; +parameter Real a = -0.5; +parameter Real b = 0.1; equation - der(x) = x*a + b; - y = der(x); +der(x) = x*a + b; +y = der(x); end M_getters; """) - mod = OMPython.ModelicaSystem(model_file.as_posix(), "M_getters") - - q = mod.getQuantities() - assert isinstance(q, list) - assert sorted(q, key=lambda d: d["name"]) == sorted([ - { - 'alias': 'noAlias', - 'aliasvariable': None, - 'causality': 'local', - 'changeable': 'true', - 'description': None, - 'max': None, - 'min': None, - 'name': 'x', - 'start': '1.0', - 'unit': None, - 'variability': 'continuous', - }, - { - 'alias': 'noAlias', - 'aliasvariable': None, - 'causality': 'local', - 'changeable': 'false', - 'description': None, - 'max': None, - 'min': None, - 'name': 'der(x)', - 'start': None, - 'unit': None, - 'variability': 'continuous', - }, - { - 'alias': 'noAlias', - 'aliasvariable': None, - 'causality': 'output', - 'changeable': 'false', - 'description': 'the derivative', - 'max': None, - 'min': None, - 'name': 'y', - 'start': '-0.4', - 'unit': None, - 'variability': 'continuous', - }, - { - 'alias': 'noAlias', - 'aliasvariable': None, - 'causality': 'parameter', - 'changeable': 'true', - 'description': None, - 'max': None, - 'min': None, - 'name': 'a', - 'start': '-0.5', - 'unit': None, - 'variability': 'parameter', - }, - { - 'alias': 'noAlias', - 'aliasvariable': None, - 'causality': 'parameter', - 'changeable': 'true', - 'description': None, - 'max': None, - 'min': None, - 'name': 'b', - 'start': '0.1', - 'unit': None, - 'variability': 'parameter', - } - ], key=lambda d: d["name"]) - - assert mod.getQuantities("y") == [ - { - 'alias': 'noAlias', - 'aliasvariable': None, - 'causality': 'output', - 'changeable': 'false', - 'description': 'the derivative', - 'max': None, - 'min': None, - 'name': 'y', - 'start': '-0.4', - 'unit': None, - 'variability': 'continuous', - } - ] - - assert mod.getQuantities(["y", "x"]) == [ - { - 'alias': 'noAlias', - 'aliasvariable': None, - 'causality': 'output', - 'changeable': 'false', - 'description': 'the derivative', - 'max': None, - 'min': None, - 'name': 'y', - 'start': '-0.4', - 'unit': None, - 'variability': 'continuous', - }, - { - 'alias': 'noAlias', - 'aliasvariable': None, - 'causality': 'local', - 'changeable': 'true', - 'description': None, - 'max': None, - 'min': None, - 'name': 'x', - 'start': '1.0', - 'unit': None, - 'variability': 'continuous', - }, - ] - - assert mod.getInputs() == {} - # getOutputs before simulate() - assert mod.getOutputs() == {'y': '-0.4'} - assert mod.getOutputs("y") == ["-0.4"] - assert mod.getOutputs(["y", "y"]) == ["-0.4", "-0.4"] - - # getContinuous before simulate(): - assert mod.getContinuous() == { - 'x': '1.0', - 'der(x)': None, - 'y': '-0.4' + mod = OMPython.ModelicaSystem(model_file.as_posix(), "M_getters") + + q = mod.getQuantities() + assert isinstance(q, list) + assert sorted(q, key=lambda d: d["name"]) == sorted([ + { + 'alias': 'noAlias', + 'aliasvariable': None, + 'causality': 'local', + 'changeable': 'true', + 'description': None, + 'max': None, + 'min': None, + 'name': 'x', + 'start': '1.0', + 'unit': None, + 'variability': 'continuous', + }, + { + 'alias': 'noAlias', + 'aliasvariable': None, + 'causality': 'local', + 'changeable': 'false', + 'description': None, + 'max': None, + 'min': None, + 'name': 'der(x)', + 'start': None, + 'unit': None, + 'variability': 'continuous', + }, + { + 'alias': 'noAlias', + 'aliasvariable': None, + 'causality': 'output', + 'changeable': 'false', + 'description': 'the derivative', + 'max': None, + 'min': None, + 'name': 'y', + 'start': '-0.4', + 'unit': None, + 'variability': 'continuous', + }, + { + 'alias': 'noAlias', + 'aliasvariable': None, + 'causality': 'parameter', + 'changeable': 'true', + 'description': None, + 'max': None, + 'min': None, + 'name': 'a', + 'start': '-0.5', + 'unit': None, + 'variability': 'parameter', + }, + { + 'alias': 'noAlias', + 'aliasvariable': None, + 'causality': 'parameter', + 'changeable': 'true', + 'description': None, + 'max': None, + 'min': None, + 'name': 'b', + 'start': '0.1', + 'unit': None, + 'variability': 'parameter', } - assert mod.getContinuous("y") == ['-0.4'] - assert mod.getContinuous(["y", "x"]) == ['-0.4', '1.0'] - assert mod.getContinuous("a") == ["NotExist"] # a is a parameter - - stopTime = 1.0 - a = -0.5 - b = 0.1 - x0 = 1.0 - x_analytical = -b/a + (x0 + b/a) * np.exp(a * stopTime) - dx_analytical = (x0 + b/a) * a * np.exp(a * stopTime) - mod.setSimulationOptions(f"stopTime={stopTime}") - mod.simulate() - - # getOutputs after simulate() - d = mod.getOutputs() - assert d.keys() == {"y"} - assert np.isclose(d["y"], dx_analytical, 1e-4) - assert mod.getOutputs("y") == [d["y"]] - assert mod.getOutputs(["y", "y"]) == [d["y"], d["y"]] - - # getContinuous after simulate() should return values at end of simulation: - with self.assertRaises(OMPython.ModelicaSystemError): - mod.getContinuous("a") # a is a parameter - with self.assertRaises(OMPython.ModelicaSystemError): - mod.getContinuous(["x", "a", "y"]) # a is a parameter - d = mod.getContinuous() - assert d.keys() == {"x", "der(x)", "y"} - assert np.isclose(d["x"], x_analytical, 1e-4) - assert np.isclose(d["der(x)"], dx_analytical, 1e-4) - assert np.isclose(d["y"], dx_analytical, 1e-4) - assert mod.getContinuous("x") == [d["x"]] - assert mod.getContinuous(["y", "x"]) == [d["y"], d["x"]] - - with self.assertRaises(OMPython.ModelicaSystemError): - mod.setSimulationOptions("thisOptionDoesNotExist=3") - - def test_simulate_inputs(self): - model_file = self.tmp / "M_input.mo" - model_file.write_text(""" + ], key=lambda d: d["name"]) + + assert mod.getQuantities("y") == [ + { + 'alias': 'noAlias', + 'aliasvariable': None, + 'causality': 'output', + 'changeable': 'false', + 'description': 'the derivative', + 'max': None, + 'min': None, + 'name': 'y', + 'start': '-0.4', + 'unit': None, + 'variability': 'continuous', + } + ] + + assert mod.getQuantities(["y", "x"]) == [ + { + 'alias': 'noAlias', + 'aliasvariable': None, + 'causality': 'output', + 'changeable': 'false', + 'description': 'the derivative', + 'max': None, + 'min': None, + 'name': 'y', + 'start': '-0.4', + 'unit': None, + 'variability': 'continuous', + }, + { + 'alias': 'noAlias', + 'aliasvariable': None, + 'causality': 'local', + 'changeable': 'true', + 'description': None, + 'max': None, + 'min': None, + 'name': 'x', + 'start': '1.0', + 'unit': None, + 'variability': 'continuous', + }, + ] + + assert mod.getInputs() == {} + # getOutputs before simulate() + assert mod.getOutputs() == {'y': '-0.4'} + assert mod.getOutputs("y") == ["-0.4"] + assert mod.getOutputs(["y", "y"]) == ["-0.4", "-0.4"] + + # getContinuous before simulate(): + assert mod.getContinuous() == { + 'x': '1.0', + 'der(x)': None, + 'y': '-0.4' + } + assert mod.getContinuous("y") == ['-0.4'] + assert mod.getContinuous(["y", "x"]) == ['-0.4', '1.0'] + assert mod.getContinuous("a") == ["NotExist"] # a is a parameter + + stopTime = 1.0 + a = -0.5 + b = 0.1 + x0 = 1.0 + x_analytical = -b/a + (x0 + b/a) * np.exp(a * stopTime) + dx_analytical = (x0 + b/a) * a * np.exp(a * stopTime) + mod.setSimulationOptions(f"stopTime={stopTime}") + mod.simulate() + + # getOutputs after simulate() + d = mod.getOutputs() + assert d.keys() == {"y"} + assert np.isclose(d["y"], dx_analytical, 1e-4) + assert mod.getOutputs("y") == [d["y"]] + assert mod.getOutputs(["y", "y"]) == [d["y"], d["y"]] + + # getContinuous after simulate() should return values at end of simulation: + with pytest.raises(OMPython.ModelicaSystemError): + mod.getContinuous("a") # a is a parameter + with pytest.raises(OMPython.ModelicaSystemError): + mod.getContinuous(["x", "a", "y"]) # a is a parameter + d = mod.getContinuous() + assert d.keys() == {"x", "der(x)", "y"} + assert np.isclose(d["x"], x_analytical, 1e-4) + assert np.isclose(d["der(x)"], dx_analytical, 1e-4) + assert np.isclose(d["y"], dx_analytical, 1e-4) + assert mod.getContinuous("x") == [d["x"]] + assert mod.getContinuous(["y", "x"]) == [d["y"], d["x"]] + + with pytest.raises(OMPython.ModelicaSystemError): + mod.setSimulationOptions("thisOptionDoesNotExist=3") + + +def test_simulate_inputs(tmp_path): + model_file = tmp_path / "M_input.mo" + model_file.write_text(""" model M_input - Real x(start=0, fixed=true); - input Real u1; - input Real u2; - output Real y; +Real x(start=0, fixed=true); +input Real u1; +input Real u2; +output Real y; equation - der(x) = u1 + u2; - y = x; +der(x) = u1 + u2; +y = x; end M_input; """) - mod = OMPython.ModelicaSystem(model_file.as_posix(), "M_input") - - mod.setSimulationOptions("stopTime=1.0") - - # integrate zero (no setInputs call) - it should default to None -> 0 - assert mod.getInputs() == { - "u1": None, - "u2": None, - } - mod.simulate() - y = mod.getSolutions("y")[0] - assert np.isclose(y[-1], 0.0) - - # integrate a constant - mod.setInputs("u1=2.5") - assert mod.getInputs() == { - "u1": [ - (0.0, 2.5), - (1.0, 2.5), - ], - "u2": None, - } - mod.simulate() - y = mod.getSolutions("y")[0] - assert np.isclose(y[-1], 2.5) - - # now let's integrate the sum of two ramps - mod.setInputs("u1=[(0.0, 0.0), (0.5, 2), (1.0, 0)]") - assert mod.getInputs("u1") == [[ - (0.0, 0.0), - (0.5, 2.0), - (1.0, 0.0), - ]] + mod = OMPython.ModelicaSystem(model_file.as_posix(), "M_input") + + mod.setSimulationOptions("stopTime=1.0") + + # integrate zero (no setInputs call) - it should default to None -> 0 + assert mod.getInputs() == { + "u1": None, + "u2": None, + } + mod.simulate() + y = mod.getSolutions("y")[0] + assert np.isclose(y[-1], 0.0) + + # integrate a constant + mod.setInputs("u1=2.5") + assert mod.getInputs() == { + "u1": [ + (0.0, 2.5), + (1.0, 2.5), + ], + "u2": None, + } + mod.simulate() + y = mod.getSolutions("y")[0] + assert np.isclose(y[-1], 2.5) + + # now let's integrate the sum of two ramps + mod.setInputs("u1=[(0.0, 0.0), (0.5, 2), (1.0, 0)]") + assert mod.getInputs("u1") == [[ + (0.0, 0.0), + (0.5, 2.0), + (1.0, 0.0), + ]] + mod.simulate() + y = mod.getSolutions("y")[0] + assert np.isclose(y[-1], 1.0) + + # let's try some edge cases + # unmatched startTime + with pytest.raises(OMPython.ModelicaSystemError): + mod.setInputs("u1=[(-0.5, 0.0), (1.0, 1)]") mod.simulate() - y = mod.getSolutions("y")[0] - assert np.isclose(y[-1], 1.0) - - # let's try some edge cases - # unmatched startTime - with self.assertRaises(OMPython.ModelicaSystemError): - mod.setInputs("u1=[(-0.5, 0.0), (1.0, 1)]") - mod.simulate() - # unmatched stopTime - with self.assertRaises(OMPython.ModelicaSystemError): - mod.setInputs("u1=[(0.0, 0.0), (0.5, 1)]") - mod.simulate() - - # Let's use both inputs, but each one with different number of of - # samples. This has an effect when generating the csv file. - mod.setInputs([ - "u1=[(0.0, 0), (1.0, 1)]", - "u2=[(0.0, 0), (0.25, 0.5), (0.5, 1.0), (1.0, 0)]", - ]) + # unmatched stopTime + with pytest.raises(OMPython.ModelicaSystemError): + mod.setInputs("u1=[(0.0, 0.0), (0.5, 1)]") mod.simulate() - assert pathlib.Path(mod.csvFile).read_text() == """time,u1,u2,end + + # Let's use both inputs, but each one with different number of of + # samples. This has an effect when generating the csv file. + mod.setInputs([ + "u1=[(0.0, 0), (1.0, 1)]", + "u2=[(0.0, 0), (0.25, 0.5), (0.5, 1.0), (1.0, 0)]", + ]) + mod.simulate() + assert pathlib.Path(mod.csvFile).read_text() == """time,u1,u2,end 0.0,0.0,0.0,0 0.25,0.25,0.5,0 0.5,0.5,1.0,0 1.0,1.0,0.0,0 """ - y = mod.getSolutions("y")[0] - assert np.isclose(y[-1], 1.0) - - -if __name__ == '__main__': - unittest.main() + y = mod.getSolutions("y")[0] + assert np.isclose(y[-1], 1.0) diff --git a/tests/test_ModelicaSystemCmd.py b/tests/test_ModelicaSystemCmd.py index 6257a2a6..f82510df 100644 --- a/tests/test_ModelicaSystemCmd.py +++ b/tests/test_ModelicaSystemCmd.py @@ -1,42 +1,32 @@ import OMPython -import pathlib -import shutil -import tempfile -import unittest +import pytest -import logging -logger = logging.getLogger(__name__) -logging.basicConfig(level=logging.DEBUG) - - -class ModelicaSystemCmdTester(unittest.TestCase): - def __init__(self, *args, **kwargs): - super(ModelicaSystemCmdTester, self).__init__(*args, **kwargs) - self.tmp = pathlib.Path(tempfile.mkdtemp(prefix='tmpOMPython.tests')) - self.model = self.tmp / "M.mo" - with open(self.model, "w") as fout: - fout.write("""model M +@pytest.fixture +def model_firstorder(tmp_path): + mod = tmp_path / "M.mo" + mod.write_text("""model M Real x(start = 1, fixed = true); parameter Real a = -1; equation der(x) = x*a; end M; - """) - self.mod = OMPython.ModelicaSystem(self.model.as_posix(), "M") - - def __del__(self): - shutil.rmtree(self.tmp, ignore_errors=True) - - def test_simflags(self): - mscmd = OMPython.ModelicaSystemCmd(runpath=self.mod.tempdir, modelname=self.mod.modelName) - mscmd.args_set(args={"noEventEmit": None, "noRestart": None, "override": {'b': 2}}) - mscmd.args_set(args=mscmd.parse_simflags(simflags="-noEventEmit -noRestart -override=a=1,x=3")) - - logger.info(mscmd.get_cmd()) - - assert mscmd.get_cmd() == [mscmd.get_exe().as_posix(), '-noEventEmit', '-noRestart', '-override=b=2,a=1,x=3'] - - -if __name__ == '__main__': - unittest.main() +""") + return mod + + +def test_simflags(model_firstorder): + mod = OMPython.ModelicaSystem(model_firstorder.as_posix(), "M") + mscmd = OMPython.ModelicaSystemCmd(runpath=mod.tempdir, modelname=mod.modelName) + mscmd.args_set({ + "noEventEmit": None, + "noRestart": None, + "override": {'b': 2} + }) + mscmd.args_set(args=mscmd.parse_simflags(simflags="-noEventEmit -noRestart -override=a=1,x=3")) + + assert mscmd.get_cmd() == [ + mscmd.get_exe().as_posix(), + '-noEventEmit', '-noRestart', + '-override=b=2,a=1,x=3' + ] diff --git a/tests/test_OMParser.py b/tests/test_OMParser.py index 74aba789..875604e5 100644 --- a/tests/test_OMParser.py +++ b/tests/test_OMParser.py @@ -1,43 +1,43 @@ from OMPython import OMParser -import unittest typeCheck = OMParser.typeCheck -class TypeCheckTester(unittest.TestCase): - def testNewlineBehaviour(self): - pass +def test_newline_behaviour(): + pass - def testBoolean(self): - self.assertEqual(typeCheck('TRUE'), True) - self.assertEqual(typeCheck('True'), True) - self.assertEqual(typeCheck('true'), True) - self.assertEqual(typeCheck('FALSE'), False) - self.assertEqual(typeCheck('False'), False) - self.assertEqual(typeCheck('false'), False) - def testInt(self): - self.assertEqual(typeCheck('2'), 2) - self.assertEqual(type(typeCheck('1')), int) - self.assertEqual(type(typeCheck('123123123123123123232323')), int) - self.assertEqual(type(typeCheck('9223372036854775808')), int) +def test_boolean(): + assert typeCheck('TRUE') is True + assert typeCheck('True') is True + assert typeCheck('true') is True + assert typeCheck('FALSE') is False + assert typeCheck('False') is False + assert typeCheck('false') is False - def testFloat(self): - self.assertEqual(type(typeCheck('1.2e3')), float) - # def testDict(self): - # self.assertEqual(type(typeCheck('{"a": "b"}')), dict) +def test_int(): + assert typeCheck('2') == 2 + assert type(typeCheck('1')) == int + assert type(typeCheck('123123123123123123232323')) == int + assert type(typeCheck('9223372036854775808')) == int - def testIdent(self): - self.assertEqual(typeCheck('blabla2'), "blabla2") - pass - def testStr(self): - pass +def test_float(): + assert type(typeCheck('1.2e3')) == float - def testUnStringable(self): - pass +# def test_dict(): +# assert type(typeCheck('{"a": "b"}')) == dict -if __name__ == '__main__': - unittest.main() + +def test_ident(): + assert typeCheck('blabla2') == "blabla2" + + +def test_str(): + pass + + +def test_UnStringable(): + pass diff --git a/tests/test_OMSessionCmd.py b/tests/test_OMSessionCmd.py index 5e369636..c76e8ca3 100644 --- a/tests/test_OMSessionCmd.py +++ b/tests/test_OMSessionCmd.py @@ -1,24 +1,17 @@ import OMPython -import unittest -class OMCSessionCmdTester(unittest.TestCase): - def __init__(self, *args, **kwargs): - super(OMCSessionCmdTester, self).__init__(*args, **kwargs) +def test_isPackage(): + omczmq = OMPython.OMCSessionZMQ() + omccmd = OMPython.OMCSessionCmd(session=omczmq) + assert not omccmd.isPackage('Modelica') - def test_isPackage(self): - omczmq = OMPython.OMCSessionZMQ() - omccmd = OMPython.OMCSessionCmd(session=omczmq) - assert not omccmd.isPackage('Modelica') - def test_isPackage2(self): - mod = OMPython.ModelicaSystem(modelName="Modelica.Electrical.Analog.Examples.CauerLowPassAnalog", - lmodel=["Modelica"]) - omccmd = OMPython.OMCSessionCmd(session=mod.getconn) - assert omccmd.isPackage('Modelica') +def test_isPackage2(): + mod = OMPython.ModelicaSystem(modelName="Modelica.Electrical.Analog.Examples.CauerLowPassAnalog", + lmodel=["Modelica"]) + omccmd = OMPython.OMCSessionCmd(session=mod.getconn) + assert omccmd.isPackage('Modelica') - # TODO: add more checks ... - -if __name__ == '__main__': - unittest.main() +# TODO: add more checks ... diff --git a/tests/test_ZMQ.py b/tests/test_ZMQ.py index 539bd733..5f78719d 100644 --- a/tests/test_ZMQ.py +++ b/tests/test_ZMQ.py @@ -1,51 +1,42 @@ import OMPython -import unittest -import tempfile -import shutil +import pathlib import os +import pytest -class ZMQTester(unittest.TestCase): - def __init__(self, *args, **kwargs): - super(ZMQTester, self).__init__(*args, **kwargs) - self.simpleModel = """model M +@pytest.fixture +def model_time_str(): + return """model M Real r = time; -end M;""" - self.tmp = tempfile.mkdtemp(prefix='tmpOMPython.tests') - self.origDir = os.getcwd() - os.chdir(self.tmp) - self.om = OMPython.OMCSessionZMQ() - os.chdir(self.origDir) - - def __del__(self): - shutil.rmtree(self.tmp, ignore_errors=True) - del self.om - - def clean(self): - del self.om - self.om = None - - def testHelloWorld(self): - self.assertEqual("HelloWorld!", self.om.sendExpression('"HelloWorld!"')) - self.clean() - - def testTranslate(self): - self.assertEqual(("M",), self.om.sendExpression(self.simpleModel)) - self.assertEqual(True, self.om.sendExpression('translateModel(M)')) - self.clean() - - def testSimulate(self): - self.assertEqual(True, self.om.sendExpression('loadString("%s")' % self.simpleModel)) - self.om.sendExpression('res:=simulate(M, stopTime=2.0)') - self.assertNotEqual("", self.om.sendExpression('res.resultFile')) - self.clean() - - def test_execute(self): - self.assertEqual('"HelloWorld!"\n', self.om.execute('"HelloWorld!"')) - self.assertEqual('"HelloWorld!"\n', self.om.sendExpression('"HelloWorld!"', parsed=False)) - self.assertEqual('HelloWorld!', self.om.sendExpression('"HelloWorld!"', parsed=True)) - self.clean() - - -if __name__ == '__main__': - unittest.main() +end M; +""" + + +@pytest.fixture +def om(tmp_path): + origDir = pathlib.Path.cwd() + os.chdir(tmp_path) + om = OMPython.OMCSessionZMQ() + os.chdir(origDir) + return om + + +def testHelloWorld(om): + assert om.sendExpression('"HelloWorld!"') == "HelloWorld!" + + +def test_Translate(om, model_time_str): + assert om.sendExpression(model_time_str) == ("M",) + assert om.sendExpression('translateModel(M)') is True + + +def test_Simulate(om, model_time_str): + assert om.sendExpression(f'loadString("{model_time_str}")') is True + om.sendExpression('res:=simulate(M, stopTime=2.0)') + assert om.sendExpression('res.resultFile') + + +def test_execute(om): + assert om.execute('"HelloWorld!"') == '"HelloWorld!"\n' + assert om.sendExpression('"HelloWorld!"', parsed=False) == '"HelloWorld!"\n' + assert om.sendExpression('"HelloWorld!"', parsed=True) == 'HelloWorld!' diff --git a/tests/test_docker.py b/tests/test_docker.py index fc518f26..540d123a 100644 --- a/tests/test_docker.py +++ b/tests/test_docker.py @@ -1,21 +1,15 @@ import OMPython -import unittest import pytest -class DockerTester(unittest.TestCase): - @pytest.mark.skip(reason="This test would fail") - def testDocker(self): - om = OMPython.OMCSessionZMQ(docker="openmodelica/openmodelica:v1.16.1-minimal") - assert om.sendExpression("getVersion()") == "OpenModelica 1.16.1" - omInner = OMPython.OMCSessionZMQ(dockerContainer=om._dockerCid) - assert omInner.sendExpression("getVersion()") == "OpenModelica 1.16.1" - om2 = OMPython.OMCSessionZMQ(docker="openmodelica/openmodelica:v1.16.1-minimal", port=11111) - assert om2.sendExpression("getVersion()") == "OpenModelica 1.16.1" - del om2 - del omInner - del om - - -if __name__ == '__main__': - unittest.main() +@pytest.mark.skip(reason="This test would fail") +def test_docker(): + om = OMPython.OMCSessionZMQ(docker="openmodelica/openmodelica:v1.16.1-minimal") + assert om.sendExpression("getVersion()") == "OpenModelica 1.16.1" + omInner = OMPython.OMCSessionZMQ(dockerContainer=om._dockerCid) + assert omInner.sendExpression("getVersion()") == "OpenModelica 1.16.1" + om2 = OMPython.OMCSessionZMQ(docker="openmodelica/openmodelica:v1.16.1-minimal", port=11111) + assert om2.sendExpression("getVersion()") == "OpenModelica 1.16.1" + del om2 + del omInner + del om diff --git a/tests/test_linearization.py b/tests/test_linearization.py index bf759fde..e9f0f6d7 100644 --- a/tests/test_linearization.py +++ b/tests/test_linearization.py @@ -1,17 +1,12 @@ import OMPython -import tempfile -import shutil -import unittest -import pathlib +import pytest import numpy as np -class Test_Linearization(unittest.TestCase): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.tmp = pathlib.Path(tempfile.mkdtemp(prefix='tmpOMPython.tests')) - with open(self.tmp / "linearTest.mo", "w") as fout: - fout.write(""" +@pytest.fixture +def model_linearTest(tmp_path): + mod = tmp_path / "M.mo" + mod.write_text(""" model linearTest Real x1(start=1); Real x2(start=-2); @@ -25,84 +20,83 @@ def __init__(self, *args, **kwargs): der(x4) = x1 + x2 + der(x3) + x4; end linearTest; """) + return mod - def __del__(self): - shutil.rmtree(self.tmp, ignore_errors=True) - def test_example(self): - filePath = (self.tmp / "linearTest.mo").as_posix() - 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: {[]}" - assert mod.getLinearInputs() == [] - assert mod.getLinearOutputs() == [] - assert mod.getLinearStates() == ["x1", "x2", "x3", "x4"] +def test_example(model_linearTest): + mod = OMPython.ModelicaSystem(model_linearTest, "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: {[]}" + assert mod.getLinearInputs() == [] + assert mod.getLinearOutputs() == [] + assert mod.getLinearStates() == ["x1", "x2", "x3", "x4"] - def test_getters(self): - model_file = self.tmp / "pendulum.mo" - model_file.write_text(""" + +def test_getters(tmp_path): + model_file = tmp_path / "pendulum.mo" + model_file.write_text(""" model Pendulum - Real phi(start=Modelica.Constants.pi, fixed=true); - Real omega(start=0, fixed=true); - input Real u1; - input Real u2; - output Real y1; - output Real y2; - parameter Real l = 1.2; - parameter Real g = 9.81; +Real phi(start=Modelica.Constants.pi, fixed=true); +Real omega(start=0, fixed=true); +input Real u1; +input Real u2; +output Real y1; +output Real y2; +parameter Real l = 1.2; +parameter Real g = 9.81; equation - der(phi) = omega + u2; - der(omega) = -g/l * sin(phi); - y1 = y2 + 0.5*omega; - y2 = phi + u1; +der(phi) = omega + u2; +der(omega) = -g/l * sin(phi); +y1 = y2 + 0.5*omega; +y2 = phi + u1; end Pendulum; """) - mod = OMPython.ModelicaSystem(model_file.as_posix(), "Pendulum", ["Modelica"]) + mod = OMPython.ModelicaSystem(model_file.as_posix(), "Pendulum", ["Modelica"]) - d = mod.getLinearizationOptions() - assert isinstance(d, dict) - assert "startTime" in d - assert "stopTime" in d - assert mod.getLinearizationOptions(["stopTime", "startTime"]) == [d["stopTime"], d["startTime"]] - mod.setLinearizationOptions("stopTime=0.02") - assert mod.getLinearizationOptions("stopTime") == ["0.02"] + d = mod.getLinearizationOptions() + assert isinstance(d, dict) + assert "startTime" in d + assert "stopTime" in d + assert mod.getLinearizationOptions(["stopTime", "startTime"]) == [d["stopTime"], d["startTime"]] + mod.setLinearizationOptions("stopTime=0.02") + assert mod.getLinearizationOptions("stopTime") == ["0.02"] - mod.setInputs(["u1=10", "u2=0"]) - [A, B, C, D] = mod.linearize() - g = float(mod.getParameters("g")[0]) - l = float(mod.getParameters("l")[0]) - assert mod.getLinearInputs() == ["u1", "u2"] - assert mod.getLinearStates() == ["omega", "phi"] - assert mod.getLinearOutputs() == ["y1", "y2"] - assert np.isclose(A, [[0, g/l], [1, 0]]).all() - assert np.isclose(B, [[0, 0], [0, 1]]).all() - assert np.isclose(C, [[0.5, 1], [0, 1]]).all() - assert np.isclose(D, [[1, 0], [1, 0]]).all() + mod.setInputs(["u1=10", "u2=0"]) + [A, B, C, D] = mod.linearize() + g = float(mod.getParameters("g")[0]) + l = float(mod.getParameters("l")[0]) + assert mod.getLinearInputs() == ["u1", "u2"] + assert mod.getLinearStates() == ["omega", "phi"] + assert mod.getLinearOutputs() == ["y1", "y2"] + assert np.isclose(A, [[0, g/l], [1, 0]]).all() + assert np.isclose(B, [[0, 0], [0, 1]]).all() + assert np.isclose(C, [[0.5, 1], [0, 1]]).all() + assert np.isclose(D, [[1, 0], [1, 0]]).all() - # test LinearizationResult - result = mod.linearize() - assert result[0] == A - assert result[1] == B - assert result[2] == C - assert result[3] == D - with self.assertRaises(KeyError): - result[4] + # test LinearizationResult + result = mod.linearize() + assert result[0] == A + assert result[1] == B + assert result[2] == C + assert result[3] == D + with pytest.raises(KeyError): + result[4] - A2, B2, C2, D2 = result - assert A2 == A - assert B2 == B - assert C2 == C - assert D2 == D + A2, B2, C2, D2 = result + assert A2 == A + assert B2 == B + assert C2 == C + assert D2 == D - assert result.n == 2 - assert result.m == 2 - assert result.p == 2 - assert np.isclose(result.x0, [0, np.pi]).all() - assert np.isclose(result.u0, [10, 0]).all() - assert result.stateVars == ["omega", "phi"] - assert result.inputVars == ["u1", "u2"] - assert result.outputVars == ["y1", "y2"] + assert result.n == 2 + assert result.m == 2 + assert result.p == 2 + assert np.isclose(result.x0, [0, np.pi]).all() + assert np.isclose(result.u0, [10, 0]).all() + assert result.stateVars == ["omega", "phi"] + assert result.inputVars == ["u1", "u2"] + assert result.outputVars == ["y1", "y2"] diff --git a/tests/test_optimization.py b/tests/test_optimization.py index 283df062..672de4a6 100644 --- a/tests/test_optimization.py +++ b/tests/test_optimization.py @@ -1,42 +1,30 @@ import OMPython -import tempfile -import shutil -import unittest -import pathlib import numpy as np -class Test_Linearization(unittest.TestCase): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.tmp = pathlib.Path(tempfile.mkdtemp(prefix='tmpOMPython.tests')) - - def __del__(self): - shutil.rmtree(self.tmp, ignore_errors=True) - - def test_example(self): - model_file = self.tmp / "BangBang2021.mo" - model_file.write_text(""" +def test_optimization_example(tmp_path): + model_file = tmp_path / "BangBang2021.mo" + model_file.write_text(""" model BangBang2021 "Model to verify that optimization gives bang-bang optimal control" - parameter Real m = 1; - parameter Real p = 1 "needed for final constraints"; +parameter Real m = 1; +parameter Real p = 1 "needed for final constraints"; - Real a; - Real v(start = 0, fixed = true); - Real pos(start = 0, fixed = true); - Real pow(min = -30, max = 30) = f * v annotation(isConstraint = true); +Real a; +Real v(start = 0, fixed = true); +Real pos(start = 0, fixed = true); +Real pow(min = -30, max = 30) = f * v annotation(isConstraint = true); - input Real f(min = -10, max = 10); +input Real f(min = -10, max = 10); - Real costPos(nominal = 1) = -pos "minimize -pos(tf)" annotation(isMayer=true); +Real costPos(nominal = 1) = -pos "minimize -pos(tf)" annotation(isMayer=true); - Real conSpeed(min = 0, max = 0) = p * v " 0<= p*v(tf) <=0" annotation(isFinalConstraint = true); +Real conSpeed(min = 0, max = 0) = p * v " 0<= p*v(tf) <=0" annotation(isFinalConstraint = true); equation - der(pos) = v; - der(v) = a; - f = m * a; +der(pos) = v; +der(v) = a; +f = m * a; annotation(experiment(StartTime = 0, StopTime = 1, Tolerance = 1e-07, Interval = 0.01), __OpenModelica_simulationFlags(s="optimization", optimizerNP="1"), @@ -45,33 +33,33 @@ def test_example(self): end BangBang2021; """) - mod = OMPython.ModelicaSystem(model_file.as_posix(), "BangBang2021") - - mod.setOptimizationOptions(["numberOfIntervals=16", "stopTime=1", - "stepSize=0.001", "tolerance=1e-8"]) - - # test the getter - assert mod.getOptimizationOptions()["stopTime"] == "1" - assert mod.getOptimizationOptions("stopTime") == ["1"] - assert mod.getOptimizationOptions(["tolerance", "stopTime"]) == ["1e-8", "1"] - - r = mod.optimize() - # it is necessary to specify resultfile, otherwise it wouldn't find it. - time, f, v = mod.getSolutions(["time", "f", "v"], resultfile=r["resultFile"]) - assert np.isclose(f[0], 10) - assert np.isclose(f[-1], -10) - - def f_fcn(time, v): - if time < 0.3: - return 10 - if time <= 0.5: - return 30 / v - if time < 0.7: - return -30 / v - return -10 - f_expected = [f_fcn(t, v) for t, v in zip(time, v)] - - # The sharp edge at time=0.5 probably won't match, let's leave that out. - matches = np.isclose(f, f_expected, 1e-3) - assert matches[:498].all() - assert matches[502:].all() + mod = OMPython.ModelicaSystem(model_file.as_posix(), "BangBang2021") + + mod.setOptimizationOptions(["numberOfIntervals=16", "stopTime=1", + "stepSize=0.001", "tolerance=1e-8"]) + + # test the getter + assert mod.getOptimizationOptions()["stopTime"] == "1" + assert mod.getOptimizationOptions("stopTime") == ["1"] + assert mod.getOptimizationOptions(["tolerance", "stopTime"]) == ["1e-8", "1"] + + r = mod.optimize() + # it is necessary to specify resultfile, otherwise it wouldn't find it. + time, f, v = mod.getSolutions(["time", "f", "v"], resultfile=r["resultFile"]) + assert np.isclose(f[0], 10) + assert np.isclose(f[-1], -10) + + def f_fcn(time, v): + if time < 0.3: + return 10 + if time <= 0.5: + return 30 / v + if time < 0.7: + return -30 / v + return -10 + f_expected = [f_fcn(t, v) for t, v in zip(time, v)] + + # The sharp edge at time=0.5 probably won't match, let's leave that out. + matches = np.isclose(f, f_expected, 1e-3) + assert matches[:498].all() + assert matches[502:].all() diff --git a/tests/test_typedParser.py b/tests/test_typedParser.py index 4024285e..afdda8c1 100644 --- a/tests/test_typedParser.py +++ b/tests/test_typedParser.py @@ -1,40 +1,39 @@ from OMPython import OMTypedParser -import unittest typeCheck = OMTypedParser.parseString -class TypeCheckTester(unittest.TestCase): - def testNewlineBehaviour(self): - pass +def test_newline_behaviour(): + pass - def testBoolean(self): - self.assertEqual(typeCheck('true'), True) - self.assertEqual(typeCheck('false'), False) - def testInt(self): - self.assertEqual(typeCheck('2'), 2) - self.assertEqual(type(typeCheck('1')), int) - self.assertEqual(type(typeCheck('123123123123123123232323')), int) - self.assertEqual(type(typeCheck('9223372036854775808')), int) +def test_boolean(): + assert typeCheck('true') is True + assert typeCheck('false') is False - def testFloat(self): - self.assertEqual(type(typeCheck('1.2e3')), float) - def testIdent(self): - self.assertEqual(typeCheck('blabla2'), "blabla2") - pass +def test_int(): + assert typeCheck('2') == 2 + assert type(typeCheck('1')) == int + assert type(typeCheck('123123123123123123232323')) == int + assert type(typeCheck('9223372036854775808')) == int - def testEmpty(self): - self.assertEqual(typeCheck(''), None) - pass - def testStr(self): - pass +def test_float(): + assert type(typeCheck('1.2e3')) == float - def testUnStringable(self): - pass +def test_ident(): + assert typeCheck('blabla2') == "blabla2" -if __name__ == '__main__': - unittest.main() + +def test_empty(): + assert typeCheck('') is None + + +def test_str(): + pass + + +def test_UnStringable(): + pass From 38c0a48b734ca704770df8f0f643fbfd9df0b9b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sluka?= Date: Wed, 28 May 2025 21:14:52 +0200 Subject: [PATCH 2/2] Improve OMTypedParser test coverage Removing the extra lines + hitting convertString2 in tests has brought coverage up to 100%. --- OMPython/OMTypedParser.py | 22 ---------------------- tests/test_typedParser.py | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/OMPython/OMTypedParser.py b/OMPython/OMTypedParser.py index 28807a92..4a585b46 100644 --- a/OMPython/OMTypedParser.py +++ b/OMPython/OMTypedParser.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- __author__ = "Anand Kalaiarasi Ganeson, ganan642@student.liu.se, 2012-03-19, and Martin Sjölund" __license__ = """ @@ -52,8 +51,6 @@ opAssoc, ) -import sys - def convertNumbers(s, l, toks): n = toks[0] @@ -142,22 +139,3 @@ def parseString(string): if len(res) == 0: return return res[0] - - -if __name__ == "__main__": - testdata = """ - (1.0,{{1,true,3},{"4\\" -",5.9,6,NONE ( )},record ABC - startTime = ErrorLevel.warning, - 'stop*Time' = SOME(1.0) -end ABC;}) - """ - expected = (1.0, ((1, True, 3), ('4"\n', 5.9, 6, None), {"'stop*Time'": 1.0, 'startTime': 'ErrorLevel.warning'})) - results = parseString(testdata) - if results != expected: - print("Results:", results) - print("Expected:", expected) - print("Failed") - sys.exit(1) - print("Matches expected output") - print(type(results), repr(results)) diff --git a/tests/test_typedParser.py b/tests/test_typedParser.py index afdda8c1..60daedec 100644 --- a/tests/test_typedParser.py +++ b/tests/test_typedParser.py @@ -37,3 +37,17 @@ def test_str(): def test_UnStringable(): pass + + +def test_everything(): + # this test used to be in OMTypedParser.py's main() + testdata = """ + (1.0,{{1,true,3},{"4\\" +",5.9,6,NONE ( )},record ABC + startTime = ErrorLevel.warning, + 'stop*Time' = SOME(1.0) +end ABC;}) + """ + expected = (1.0, ((1, True, 3), ('4"\n', 5.9, 6, None), {"'stop*Time'": 1.0, 'startTime': 'ErrorLevel.warning'})) + results = typeCheck(testdata) + assert results == expected