Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 17 additions & 8 deletions OMPython/modelica_system_omc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
22 changes: 22 additions & 0 deletions tests/resources/BouncingBalls/Tests/BouncingBall.mo
Original file line number Diff line number Diff line change
@@ -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;
27 changes: 27 additions & 0 deletions tests/resources/BouncingBalls/Tests/StableBouncingBall.mo
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
within BouncingBalls.Tests;

model StableBouncingBall
"The 'classic' bouncing ball model with numerical tolerances"
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";
constant Height eps=1e-3 "Small height";
Boolean done "Flag when to turn off gravity";
Height h "Height";
Velocity v(start=0.0, fixed=true) "Velocity";
initial equation
h = h0;
done = false;
equation
v = der(h);
der(v) = if done then 0 else -9.81;
when {h<0,h<-eps} then
done = h<-eps;
reinit(v, -e*(if h<-eps then 0 else 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 StableBouncingBall;
8 changes: 8 additions & 0 deletions tests/resources/BouncingBalls/Tests/package.mo
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
within BouncingBalls;
package Tests "Tests"
extends Modelica.Icons.Package;

annotation (Documentation(info="<html>
<p>This package contains testing models</p>
</html>"));
end Tests;
2 changes: 2 additions & 0 deletions tests/resources/BouncingBalls/Tests/package.order
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
BouncingBall
StableBouncingBall
18 changes: 18 additions & 0 deletions tests/resources/BouncingBalls/package.mo
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
within ;
package BouncingBalls "Test"
extends Modelica.Icons.Package;

annotation(version="0.0.1",
versionDate = "2026-09-18",
uses(Modelica(version = "4.1.0")),
preferredView = "info", Documentation(info = "<html>
<h4>BouncingBalls</h4>

<p>BouncingBall test package</p>

<h4>Short description</h4>

<p>BlaBla</p>
</html>"));

end BouncingBalls;
1 change: 1 addition & 0 deletions tests/resources/BouncingBalls/package.order
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Tests
17 changes: 17 additions & 0 deletions tests/test_ModelicaSystemOMC.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,23 @@ 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/StableBouncingBall.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.StableBouncingBall",
libraries=["Modelica"],
)
mod.simulate()


def test_customBuildDirectory(tmp_path, model_firstorder):
tmpdir = tmp_path / "tmpdir1"
tmpdir.mkdir()
Expand Down
9 changes: 4 additions & 5 deletions tests/test_linearization.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(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()

assert np.isclose(A, [[0, param_g / param_l], [1, 0]], atol=1e-10, rtol=1e-9).all()
assert np.isclose(B, [[0, 0], [0, 1]], atol=1e-10, rtol=1e-9).all()
assert np.isclose(C, [[0.5, 1], [0, 1]], atol=1e-10, rtol=1e-9).all()
assert np.isclose(D, [[1, 0], [1, 0]], atol=1e-10, rtol=1e-9).all()
# test LinearizationResult
result = mod.linearize()
assert result[0] == A
Expand Down
Loading