diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index 3e681df0..2cb6f9f8 100644 --- a/OMPython/ModelicaSystem.py +++ b/OMPython/ModelicaSystem.py @@ -195,6 +195,11 @@ def _set_compatibility_helper( input_args.append(args[0]) elif pkey in kwargs: input_args.append(kwargs[pkey]) + elif kwargs: + # New-style call, e.g. setInputs(u1=10, u2=0) instead of the old + # setInputs(name={"u1": 10, "u2": 0}) / setInputs("u1=10,u2=0"). + # Treat kwargs itself as the payload instead of silently dropping it. + input_args.append(kwargs) # the code below is based on _prepare_input_data2() diff --git a/OMPython/modelica_system_omc.py b/OMPython/modelica_system_omc.py index 48e831b4..8ee5c3b0 100644 --- a/OMPython/modelica_system_omc.py +++ b/OMPython/modelica_system_omc.py @@ -140,15 +140,24 @@ def model( if not file_path.is_file(): raise IOError(f"Model file {file_path} does not exist!") - self._file_name = self.getWorkDirectory() / file_path.name - if (isinstance(self._session, OMCSessionLocal) - and file_path.as_posix() == self._file_name.as_posix()): - pass - elif self._file_name.is_file(): - raise IOError(f"Simulation model file {self._file_name} exist - not overwriting!") + if isinstance(self._session, OMCSessionLocal) and file_path.name == "package.mo": + # Structured/directory-based package: OMC requires the enclosing + # directory's name to match the package name declared inside + # package.mo, and the package may span sibling .mo files in that + # same directory tree (e.g. Tests/StableBouncingBall.mo). Copying + # just this one file into a randomly-named work directory breaks + # both of those, so load it in place instead. + self._file_name = self._session.omcpath(file_path) else: - content = file_path.read_text(encoding='utf-8') - self._file_name.write_text(content) + self._file_name = self.getWorkDirectory() / file_path.name + if (isinstance(self._session, OMCSessionLocal) + and file_path.as_posix() == self._file_name.as_posix()): + pass + elif self._file_name.is_file(): + raise IOError(f"Simulation model file {self._file_name} exist - not overwriting!") + else: + content = file_path.read_text(encoding='utf-8') + self._file_name.write_text(content) if self._file_name is not None: self._loadFile(fileName=self._file_name) diff --git a/tests/resources/BouncingBalls/Tests/BouncingBall.mo b/tests/resources/BouncingBalls/Tests/BouncingBall.mo new file mode 100644 index 00000000..9441e4ea --- /dev/null +++ b/tests/resources/BouncingBalls/Tests/BouncingBall.mo @@ -0,0 +1,22 @@ +within BouncingBalls.Tests; + +model BouncingBall "The 'classic' bouncing ball model" + type Height=Real(unit="m"); + type Velocity=Real(unit="m/s"); + parameter Real e=0.8 "Coefficient of restitution"; + parameter Height h0=1.0 "Initial height"; + Height h "Height"; + Velocity v(start=0.0, fixed=true) "Velocity"; +initial equation + h = h0; +equation + v = der(h); + der(v) = -9.81; + when h<0 then + reinit(v, -e*pre(v)); + end when; +annotation( + experiment(StartTime = 0, StopTime = 10, Tolerance = 1e-06, Interval = 0.01), + __OpenModelica_commandLineOptions = "--matchingAlgorithm=PFPlusExt --indexReductionMethod=dynamicStateSelection -d=initialization,NLSanalyticJacobian", + __OpenModelica_simulationFlags(lv = "LOG_STDOUT,LOG_ASSERT,LOG_STATS", s = "dassl", variableFilter = ".*")); +end BouncingBall; diff --git a/tests/resources/BouncingBalls/Tests/package.mo b/tests/resources/BouncingBalls/Tests/package.mo new file mode 100644 index 00000000..12b1de6f --- /dev/null +++ b/tests/resources/BouncingBalls/Tests/package.mo @@ -0,0 +1,3 @@ +within BouncingBalls; +package Tests "Tests" +end Tests; diff --git a/tests/resources/BouncingBalls/Tests/package.order b/tests/resources/BouncingBalls/Tests/package.order new file mode 100644 index 00000000..7854c89d --- /dev/null +++ b/tests/resources/BouncingBalls/Tests/package.order @@ -0,0 +1 @@ +BouncingBall diff --git a/tests/resources/BouncingBalls/package.mo b/tests/resources/BouncingBalls/package.mo new file mode 100644 index 00000000..997750f4 --- /dev/null +++ b/tests/resources/BouncingBalls/package.mo @@ -0,0 +1,3 @@ +within ; +package BouncingBalls "Test" +end BouncingBalls; diff --git a/tests/resources/BouncingBalls/package.order b/tests/resources/BouncingBalls/package.order new file mode 100644 index 00000000..c1543f72 --- /dev/null +++ b/tests/resources/BouncingBalls/package.order @@ -0,0 +1 @@ +Tests diff --git a/tests/test_ModelicaSystemOMC.py b/tests/test_ModelicaSystemOMC.py index a5e996c2..171db6b9 100644 --- a/tests/test_ModelicaSystemOMC.py +++ b/tests/test_ModelicaSystemOMC.py @@ -140,6 +140,21 @@ def test_relative_path(model_firstorder): model_file.unlink() # clean up the temporary file +def test_load_structured_package(): + # Regression test for loading a directory-based package via its package.mo: + # OMC requires the enclosing directory's name to match the package name, and + # the package spans a sibling file in a nested subdirectory + # (Tests/BouncingBall.mo). Both break if package.mo gets copied out of + # its directory instead of being loaded in place. + package_dir = pathlib.Path(__file__).parent / "resources" / "BouncingBalls" + + mod = OMPython.ModelicaSystemOMC() + mod.model( + model_file=package_dir / "package.mo", + model_name="BouncingBalls.Tests.BouncingBall") + mod.simulate() + + def test_customBuildDirectory(tmp_path, model_firstorder): tmpdir = tmp_path / "tmpdir1" tmpdir.mkdir() diff --git a/tests/test_linearization.py b/tests/test_linearization.py index 7070a45b..e7a19a0c 100644 --- a/tests/test_linearization.py +++ b/tests/test_linearization.py @@ -82,11 +82,10 @@ def test_getters(tmp_path): assert mod.getLinearInputs() == ["u1", "u2"] assert mod.getLinearStates() == ["omega", "phi"] assert mod.getLinearOutputs() == ["y1", "y2"] - assert np.isclose(A, [[0, param_g / param_l], [1, 0]]).all() + assert np.isclose(A, [[0, param_g/param_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