Skip to content
Merged

Eis #17

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
Binary file modified examples/DrivingForce.fmu
Binary file not shown.
Binary file added examples/DrivingForce6D.fmu
Binary file not shown.
Binary file modified examples/HarmonicOscillator.fmu
Binary file not shown.
Binary file modified examples/HarmonicOscillator6D.fmu
Binary file not shown.
4 changes: 2 additions & 2 deletions examples/bouncing_ball_3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,9 @@ def next_bounce(self):
p_bounce[2] = 0
return (self.time + dt_bounce, p_bounce)

def setup_experiment(self, start_time: float = 0.0):
def setup_experiment(self, start_time: float = 0.0, stop_time: float | None = None, tolerance: float | None = None):
"""Set initial (non-interface) variables."""
super().setup_experiment(start_time)
super().setup_experiment(start_time, stop_time, tolerance)
self.stopped = False
self.time = start_time

Expand Down
4 changes: 2 additions & 2 deletions examples/bouncing_ball_3d_pythonfmu.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,9 @@ def next_bounce(self) -> tuple[float, np.ndarray]:
p_bounceY = self.posY + self.speedY * dt_bounce
return (self.time + dt_bounce, np.array((p_bounceX, p_bounceY, 0), float))

def setup_experiment(self, start_time: float):
def setup_experiment(self, start_time: float, stop_time: float | None = None, tolerance: float | None = None):
"""Set initial (non-interface) variables."""
super().setup_experiment(start_time)
super().setup_experiment(start_time, stop_time, tolerance)
# print(f"SETUP_EXPERIMENT g={self.g}, e={self.e}")
self.stopped = False
self.time = start_time
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ dependencies = [
"numpy>=2.0",
"pint>=0.24.4",
"jsonpath-ng>=1.7.0",
"pythonfmu==0.6.9",
"pythonfmu>=0.7.0",
"flexparser>=0.4",
]

Expand Down
4 changes: 2 additions & 2 deletions src/component_model/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class Model(Fmi2Slave):
Make sure that `super().do_step(time, dt)` is always called first in the extended function.
* Optionally extend any other fmi2 function, i.e.

- def setup_experiment(self, start)
- def setup_experiment(self, start_time)
- def enter_initialization_mode(self):
- def exit_initialization_mode(self):
- def terminate(self):
Expand Down Expand Up @@ -152,7 +152,7 @@ def __init__(
self.time = self.default_experiment.start_time # keeping track of time when dynamic calculations are performed
self.derivatives: dict = {} # dict of non-explicit derivatives {dername : basevar, ...}

def setup_experiment(self, start_time: float = 0.0):
def setup_experiment(self, start_time: float = 0.0, stop_time: None | float = None, tolerance: None | float = None):
"""Minimum version of setup_experiment, just setting the start_time. Derived models may need to extend this."""
self.time = start_time

Expand Down
2 changes: 1 addition & 1 deletion src/component_model/utils/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def extremum(x: tuple | list | np.ndarray, y: tuple | list | np.ndarray, aerr: f
else:
return (-1, (x0, z0))
else:
return (0, (0, 0))
return (0.0, (0.0, 0.0))


def extremum_series(t: tuple | list | np.ndarray, y: tuple | list | np.ndarray, which: str = "max"):
Expand Down
23 changes: 11 additions & 12 deletions src/component_model/utils/controls.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ def idx(self, name: str) -> int:
def limit(self, ident: int | str, order: int, minmax: int, value: float | None = None) -> float:
"""Get/Set the single limit for 'idx', 'order', 'minmax'."""
idx = ident if isinstance(ident, int) else self.names.index(ident)
assert 0 <= idx < 3, f"Only idx = 0,1,2 allowed. Found {idx}"
assert 0 <= order < 3, f"Only order = 0,1,2 allowed. Found {order}"
assert 0 <= minmax < 2, f"Only minmax = 0,1 allowed. Found {minmax}"
if value is not None:
Expand Down Expand Up @@ -152,8 +151,6 @@ def setgoal(self, ident: int | str, order: int, value: float | None, t0: float =
"""
idx = ident if isinstance(ident, int) else self.names.index(ident)
# check the index, the order and the value with respect to limits
if not 0 <= idx < 3:
raise ValueError(f"Only idx = 0,1,2 allowed. Found {idx}") from None
if not 0 <= order < 3:
raise ValueError(f"Only order = 0,1,2 allowed. Found {order}") from None
# assert value is None or self.goals[idx] is None, "Change of goals is currently not implemented."
Expand Down Expand Up @@ -236,27 +233,29 @@ def step(self, time: float, dt: float):
for idx in range(self.dim):
goals = self.goals[idx]
if goals is not None:
_time = time # copies needed in case that there are several goals
_dt = dt
_current = self.current[idx]

_t, _current[2] = goals[self.rows[idx]]
while time > _t: # move row so that it starts in the right time-acc row
while _time > _t: # move row so that it starts in the right time-acc row
self.rows[idx] += 1
_t, _current[2] = goals[self.rows[idx]]
while dt > 0:
if time + dt < _t: # covers the whole
while _dt > 0:
if _time + _dt < _t: # covers the whole
_current[0] = self.check_limit(
idx, 0, _current[0] + _current[1] * dt + 0.5 * _current[2] * dt * dt
idx, 0, _current[0] + _current[1] * _dt + 0.5 * _current[2] * _dt * _dt
)
_current[1] = self.check_limit(idx, 1, _current[1] + _current[2] * dt)
dt = 0
_current[1] = self.check_limit(idx, 1, _current[1] + _current[2] * _dt)
_dt = 0
else: # dt must be split
dt1 = _t - time
dt1 = _t - _time
_current[0] = self.check_limit(
idx, 0, _current[0] + _current[1] * dt1 + 0.5 * _current[2] * dt1 * dt1
)
_current[1] = self.check_limit(idx, 1, _current[1] + _current[2] * dt1)
time = _t
dt -= dt1
_time = _t
_dt -= dt1
self.rows[idx] += 1
_t, _current[2] = goals[self.rows[idx]]

Expand Down
11 changes: 10 additions & 1 deletion tests/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@

@pytest.fixture(scope="package")
def build_fmu():
return _build_fmu()


def _build_fmu():
build_path = Path.cwd()
build_path.mkdir(exist_ok=True)
fmu_path = FmuBuilder.build_FMU(__file__, project_files=[], dest=build_path)
Expand Down Expand Up @@ -85,7 +89,7 @@ def __init__(self, **kwargs):
# it is also possible to explicitly define getters and setters as lambdas in case the variable is not backed by a Python field.
# self.register_variable(Real("myReal", causality=Fmi2Causality.output, getter=lambda: self.realOut, setter=lambda v: set_real_out(v))

def setup_experiment(self, start_time: float):
def setup_experiment(self, start_time: float, stop_time=None, tolerance=None):
"""1. After instantiation the expriment is set up. In addition to start and end time also constant input variables are set."""
assert [self.vars[idx].getter() for idx in range(5)] == [
1,
Expand Down Expand Up @@ -141,3 +145,8 @@ def test_use_fmu(build_fmu):
if __name__ == "__main__":
retcode = pytest.main(["-rA", "-v", __file__])
assert retcode == 0, f"Non-zero return code {retcode}"
import os

os.chdir(Path(__file__).parent / "test_working_directory")
# test_make_fmu( _build_fmu())
# test_use_fmu( _build_fmu())
4 changes: 2 additions & 2 deletions tests/test_bouncing_ball_3d_fmu.py
Original file line number Diff line number Diff line change
Expand Up @@ -363,12 +363,12 @@ def test_from_fmu(bouncing_ball_fmu):


if __name__ == "__main__":
retcode = 0 # pytest.main(["-rA", "-v", "--rootdir", "../", "--show", "False", __file__])
retcode = pytest.main(["-rA", "-v", "--rootdir", "../", "--show", "False", __file__])
assert retcode == 0, f"Non-zero return code {retcode}"
import os

os.chdir(Path(__file__).parent / "test_working_directory")
# test_bouncing_ball_class(show=False)
# test_make_bouncing_ball(_bouncing_ball_fmu())
test_use_fmu(_bouncing_ball_fmu(), True)
# test_use_fmu(_bouncing_ball_fmu(), True)
# test_from_fmu( _bouncing_ball_fmu())
Loading