Skip to content

Commit

Permalink
Fix merge conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
Jorgelmh committed Oct 10, 2024
2 parents 3a2c3bb + f0c6cb9 commit ddeed1a
Show file tree
Hide file tree
Showing 17 changed files with 469 additions and 104 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/_build_package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ jobs:
run: python -m build
- name: Run twine check
run: twine check --strict dist/*
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
path: ./dist/*.tar.gz
2 changes: 1 addition & 1 deletion .github/workflows/_publish_package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
permissions:
id-token: write
steps:
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
name: artifact
path: ./dist/
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/_publish_package_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
permissions:
id-token: write
steps:
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
name: artifact
path: ./dist/
Expand Down
9 changes: 9 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,12 @@ def setup_logging(caplog: LogCaptureFixture):
@pytest.fixture(autouse=True)
def logger():
return logging.getLogger()


def pytest_addoption(parser):
parser.addoption("--show", action="store", default=False)


@pytest.fixture(scope="session")
def show(request):
return request.config.getoption("--show") == "True"
48 changes: 0 additions & 48 deletions tests/data/BouncingBall.cases

This file was deleted.

44 changes: 44 additions & 0 deletions tests/data/BouncingBall3D/BouncingBall.cases
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{name : 'BouncingBall',
description : 'Simple Case Study with the 3D BouncingBall FMU (3D position and speed',
modelFile : "OspSystemStructure.xml",
timeUnit : "second",
variables : {
g : ['bb', 'g', "Gravity acting on the ball"],
e : ['bb', 'e', "Coefficient of restitution"],
x : ['bb', 'pos', "3D Position of the ball in meters"],
v : ['bb', 'speed', "3D speed of ball in meters/second"],
x_b : ['bb', 'p_bounce', "Expected 3D Position where the next bounce will occur (in meters)"],
},
base : {
description : "Ball dropping from height 1 m. Results should be the same as the basic BouncingBall",
spec: {
stepSize : 0.01,
stopTime : '3',
g : -9.81,
e : 1.0,
x[2] : 1.0,
}},
restitution : {
description : "Smaller coefficient of restitution e",
spec: {
e : 0.5,
}},
restitutionAndGravity : {
description : "Based restitution (e change), change also the gravity g",
parent : 'restitution',
spec : {
g : -1.5
}},
gravity : {
description : "Gravity like on the moon",
spec : {
g : -1.5
}},
results : {
spec : [
e@0.0,
g@0.0,
x@step,
v@step,
]}
}
Binary file added tests/data/BouncingBall3D/BouncingBall3D.fmu
Binary file not shown.
11 changes: 11 additions & 0 deletions tests/data/BouncingBall3D/OspSystemStructure.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<OspSystemStructure xmlns="http://opensimulationplatform.com/MSMI/OSPSystemStructure"
version="0.1">
<BaseStepSize>0.01</BaseStepSize>
<Simulators>
<Simulator name="bb" source="BouncingBall.fmu">
</Simulator>
</Simulators>
<Connections>
</Connections>
</OspSystemStructure>
156 changes: 156 additions & 0 deletions tests/data/BouncingBall3D/bouncing_ball_3d.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
from math import sqrt

import numpy as np

from component_model.model import Model
from component_model.variable import Variable


class BouncingBall3D(Model):
"""Another BouncingBall model, made in Python and using Model and Variable to construct a FMU.
Special features:
* The ball has a 3-D vector as position and speed
* As output variable the model estimates the next bouncing point
* As input variables, the restitution coefficient `e` and the ground angle at the bouncing point can be changed.
* Internal units are SI (m,s,rad)
Args:
pos (np.array)=(0,0,1): The 3-D position in of the ball at time [m]
speed (np.array)=(1,0,0): The 3-D speed of the ball at time [m/s]
g (float)=9.81: The gravitational acceleration [m/s^2]
e (float)=0.9: The coefficient of restitution (dimensionless): |speed after| / |speed before| collision
min_speed_z (float)=1e-6: The minimum speed in z-direction when bouncing stops [m/s]
"""

def __init__(
self,
name: str = "BouncingBall3D",
description="Another BouncingBall model, made in Python and using Model and Variable to construct a FMU",
pos: tuple = (0, 0, 10),
speed: tuple = (1, 0, 0),
g: float = 9.81,
e: float = 0.9,
min_speed_z: float = 1e-6,
**kwargs,
):
super().__init__(name, description, author="DNV, SEACo project", **kwargs)
self._pos = self._interface( 'pos', pos)
self._speed = self._interface( 'speed', speed)
self.a = np.array((0, 0, -g), float)
self._g = self._interface( 'g', g)
self._e = self._interface( 'e', e)
self.min_speed_z = min_speed_z
self.stopped = False
self.time = 0.0
self._p_bounce = self._interface( 'p_bounce', ('0m', '0m','0m')) # instantiates self.p_bounce. z always 0.
self.t_bounce, self.p_bounce = self.next_bounce()

def _interface(self, name:str, start:float|tuple):
"""Define a FMU2 interface variable, using the variable interface.
Args:
name (str): base name of the variable
start (str|float|tuple): start value of the variable (optionally with units)
Returns:
the variable object. As a side effect the variable value is made available as self.<name>
"""
if name == 'pos':
return Variable(
self,
name="pos",
description="The 3D position of the ball [m] (height in inch as displayUnit example.",
causality="output",
variability="continuous",
initial="exact",
start=start,
rng=((0, "100 m"), None, (0, "10 m")),
)
elif name == 'speed':
return Variable(
self,
name="speed",
description="The 3D speed of the ball, i.e. d pos / dt [m/s]",
causality="output",
variability="continuous",
initial="exact",
start=start,
rng=((0, "1 m/s"), None, ("-100 m/s", "100 m/s")),
)
elif name == 'g':
return Variable(
self,
name="g",
description="The gravitational acceleration (absolute value).",
causality="parameter",
variability="fixed",
start=start,
rng=(),
)
elif name == 'e':
return Variable(
self,
name="e",
description="The coefficient of restitution, i.e. |speed after| / |speed before| bounce.",
causality="parameter",
variability="fixed",
start=start,
rng=(),
)
elif name == 'p_bounce':
return Variable(
self,
name="p_bounce",
description="The expected position of the next bounce as 3D vector",
causality="output",
variability="continuous",
start=start,
rng=(),
)

def do_step(self, time, dt):
"""Perform a simulation step from `time` to `time + dt`."""
if not super().do_step(time, dt):
return False
self.t_bounce, self.p_bounce = self.next_bounce()
# print(f"Step@{time}. pos:{self.pos}, speed{self.speed}, t_bounce:{self.t_bounce}, p_bounce:{self.p_bounce}")
while dt > self.t_bounce: # if the time is this long
dt -= self.t_bounce
self.pos = self.p_bounce
self.speed -= self.a * self.t_bounce # speed before bouncing
self.speed[2] = -self.speed[2] # speed after bouncing if e==1.0
self.speed *= self.e # speed reduction due to coefficient of restitution
if self.speed[2] < self.min_speed_z:
self.stopped = True
self.a[2] = 0.0
self.speed[2] = 0.0
self.pos[2] = 0.0
self.t_bounce, self.p_bounce = self.next_bounce()
self.pos += self.speed * dt + 0.5 * self.a * dt**2
self.speed += self.a * dt
if self.pos[2] < 0:
self.pos[2] = 0
print(f"@{time}. pos {self.pos}, speed {self.speed}, bounce {self.t_bounce}")
return True

def next_bounce(self):
"""Calculate time until next bounce and position where the ground will be hit,
based on current time, pos and speed.
"""
if self.stopped: # stopped bouncing
return (1e300, np.array((1e300, 1e300, 0), float))
# return ( float('inf'), np.array( (float('inf'), float('inf'), 0), float))
else:
t_bounce = (self.speed[2] + sqrt(self.speed[2] ** 2 + 2 * self.g * self.pos[2])) / self.g
p_bounce = self.pos + self.speed * t_bounce # linear. not correct for z-direction!
p_bounce[2] = 0
return (t_bounce, p_bounce)

def setup_experiment(self, start: float):
"""Set initial (non-interface) variables."""
super().setup_experiment(start)
self.stopped = False
self.a = np.array((0, 0, -self.g), float)

45 changes: 0 additions & 45 deletions tests/data/BouncingBall_0.cases

This file was deleted.

Loading

0 comments on commit ddeed1a

Please sign in to comment.