691 lines
24 KiB
Python
691 lines
24 KiB
Python
"""Tests for the ``sympy.physics.mechanics.pathway.py`` module."""
|
|
|
|
import pytest
|
|
|
|
from sympy import (
|
|
Rational,
|
|
Symbol,
|
|
cos,
|
|
pi,
|
|
sin,
|
|
sqrt,
|
|
)
|
|
from sympy.physics.mechanics import (
|
|
Force,
|
|
LinearPathway,
|
|
ObstacleSetPathway,
|
|
PathwayBase,
|
|
Point,
|
|
ReferenceFrame,
|
|
WrappingCylinder,
|
|
WrappingGeometryBase,
|
|
WrappingPathway,
|
|
WrappingSphere,
|
|
dynamicsymbols,
|
|
)
|
|
from sympy.simplify.simplify import simplify
|
|
|
|
|
|
def _simplify_loads(loads):
|
|
return [
|
|
load.__class__(load.location, load.vector.simplify())
|
|
for load in loads
|
|
]
|
|
|
|
|
|
class TestLinearPathway:
|
|
|
|
def test_is_pathway_base_subclass(self):
|
|
assert issubclass(LinearPathway, PathwayBase)
|
|
|
|
@staticmethod
|
|
@pytest.mark.parametrize(
|
|
'args, kwargs',
|
|
[
|
|
((Point('pA'), Point('pB')), {}),
|
|
]
|
|
)
|
|
def test_valid_constructor(args, kwargs):
|
|
pointA, pointB = args
|
|
instance = LinearPathway(*args, **kwargs)
|
|
assert isinstance(instance, LinearPathway)
|
|
assert hasattr(instance, 'attachments')
|
|
assert len(instance.attachments) == 2
|
|
assert instance.attachments[0] is pointA
|
|
assert instance.attachments[1] is pointB
|
|
assert isinstance(instance.attachments[0], Point)
|
|
assert instance.attachments[0].name == 'pA'
|
|
assert isinstance(instance.attachments[1], Point)
|
|
assert instance.attachments[1].name == 'pB'
|
|
|
|
@staticmethod
|
|
@pytest.mark.parametrize(
|
|
'attachments',
|
|
[
|
|
(Point('pA'), ),
|
|
(Point('pA'), Point('pB'), Point('pZ')),
|
|
]
|
|
)
|
|
def test_invalid_attachments_incorrect_number(attachments):
|
|
with pytest.raises(ValueError):
|
|
_ = LinearPathway(*attachments)
|
|
|
|
@staticmethod
|
|
@pytest.mark.parametrize(
|
|
'attachments',
|
|
[
|
|
(None, Point('pB')),
|
|
(Point('pA'), None),
|
|
]
|
|
)
|
|
def test_invalid_attachments_not_point(attachments):
|
|
with pytest.raises(TypeError):
|
|
_ = LinearPathway(*attachments)
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _linear_pathway_fixture(self):
|
|
self.N = ReferenceFrame('N')
|
|
self.pA = Point('pA')
|
|
self.pB = Point('pB')
|
|
self.pathway = LinearPathway(self.pA, self.pB)
|
|
self.q1 = dynamicsymbols('q1')
|
|
self.q2 = dynamicsymbols('q2')
|
|
self.q3 = dynamicsymbols('q3')
|
|
self.q1d = dynamicsymbols('q1', 1)
|
|
self.q2d = dynamicsymbols('q2', 1)
|
|
self.q3d = dynamicsymbols('q3', 1)
|
|
self.F = Symbol('F')
|
|
|
|
def test_properties_are_immutable(self):
|
|
instance = LinearPathway(self.pA, self.pB)
|
|
with pytest.raises(AttributeError):
|
|
instance.attachments = None
|
|
with pytest.raises(TypeError):
|
|
instance.attachments[0] = None
|
|
with pytest.raises(TypeError):
|
|
instance.attachments[1] = None
|
|
|
|
def test_repr(self):
|
|
pathway = LinearPathway(self.pA, self.pB)
|
|
expected = 'LinearPathway(pA, pB)'
|
|
assert repr(pathway) == expected
|
|
|
|
def test_static_pathway_length(self):
|
|
self.pB.set_pos(self.pA, 2*self.N.x)
|
|
assert self.pathway.length == 2
|
|
|
|
def test_static_pathway_extension_velocity(self):
|
|
self.pB.set_pos(self.pA, 2*self.N.x)
|
|
assert self.pathway.extension_velocity == 0
|
|
|
|
def test_static_pathway_to_loads(self):
|
|
self.pB.set_pos(self.pA, 2*self.N.x)
|
|
expected = [
|
|
(self.pA, - self.F*self.N.x),
|
|
(self.pB, self.F*self.N.x),
|
|
]
|
|
assert self.pathway.to_loads(self.F) == expected
|
|
|
|
def test_2D_pathway_length(self):
|
|
self.pB.set_pos(self.pA, 2*self.q1*self.N.x)
|
|
expected = 2*sqrt(self.q1**2)
|
|
assert self.pathway.length == expected
|
|
|
|
def test_2D_pathway_extension_velocity(self):
|
|
self.pB.set_pos(self.pA, 2*self.q1*self.N.x)
|
|
expected = 2*sqrt(self.q1**2)*self.q1d/self.q1
|
|
assert self.pathway.extension_velocity == expected
|
|
|
|
def test_2D_pathway_to_loads(self):
|
|
self.pB.set_pos(self.pA, 2*self.q1*self.N.x)
|
|
expected = [
|
|
(self.pA, - self.F*(self.q1 / sqrt(self.q1**2))*self.N.x),
|
|
(self.pB, self.F*(self.q1 / sqrt(self.q1**2))*self.N.x),
|
|
]
|
|
assert self.pathway.to_loads(self.F) == expected
|
|
|
|
def test_3D_pathway_length(self):
|
|
self.pB.set_pos(
|
|
self.pA,
|
|
self.q1*self.N.x - self.q2*self.N.y + 2*self.q3*self.N.z,
|
|
)
|
|
expected = sqrt(self.q1**2 + self.q2**2 + 4*self.q3**2)
|
|
assert simplify(self.pathway.length - expected) == 0
|
|
|
|
def test_3D_pathway_extension_velocity(self):
|
|
self.pB.set_pos(
|
|
self.pA,
|
|
self.q1*self.N.x - self.q2*self.N.y + 2*self.q3*self.N.z,
|
|
)
|
|
length = sqrt(self.q1**2 + self.q2**2 + 4*self.q3**2)
|
|
expected = (
|
|
self.q1*self.q1d/length
|
|
+ self.q2*self.q2d/length
|
|
+ 4*self.q3*self.q3d/length
|
|
)
|
|
assert simplify(self.pathway.extension_velocity - expected) == 0
|
|
|
|
def test_3D_pathway_to_loads(self):
|
|
self.pB.set_pos(
|
|
self.pA,
|
|
self.q1*self.N.x - self.q2*self.N.y + 2*self.q3*self.N.z,
|
|
)
|
|
length = sqrt(self.q1**2 + self.q2**2 + 4*self.q3**2)
|
|
pO_force = (
|
|
- self.F*self.q1*self.N.x/length
|
|
+ self.F*self.q2*self.N.y/length
|
|
- 2*self.F*self.q3*self.N.z/length
|
|
)
|
|
pI_force = (
|
|
self.F*self.q1*self.N.x/length
|
|
- self.F*self.q2*self.N.y/length
|
|
+ 2*self.F*self.q3*self.N.z/length
|
|
)
|
|
expected = [
|
|
(self.pA, pO_force),
|
|
(self.pB, pI_force),
|
|
]
|
|
assert self.pathway.to_loads(self.F) == expected
|
|
|
|
|
|
class TestObstacleSetPathway:
|
|
|
|
def test_is_pathway_base_subclass(self):
|
|
assert issubclass(ObstacleSetPathway, PathwayBase)
|
|
|
|
@staticmethod
|
|
@pytest.mark.parametrize(
|
|
'num_attachments, attachments',
|
|
[
|
|
(3, [Point(name) for name in ('pO', 'pA', 'pI')]),
|
|
(4, [Point(name) for name in ('pO', 'pA', 'pB', 'pI')]),
|
|
(5, [Point(name) for name in ('pO', 'pA', 'pB', 'pC', 'pI')]),
|
|
(6, [Point(name) for name in ('pO', 'pA', 'pB', 'pC', 'pD', 'pI')]),
|
|
]
|
|
)
|
|
def test_valid_constructor(num_attachments, attachments):
|
|
instance = ObstacleSetPathway(*attachments)
|
|
assert isinstance(instance, ObstacleSetPathway)
|
|
assert hasattr(instance, 'attachments')
|
|
assert len(instance.attachments) == num_attachments
|
|
for attachment in instance.attachments:
|
|
assert isinstance(attachment, Point)
|
|
|
|
@staticmethod
|
|
@pytest.mark.parametrize(
|
|
'attachments',
|
|
[[Point('pO')], [Point('pO'), Point('pI')]],
|
|
)
|
|
def test_invalid_constructor_attachments_incorrect_number(attachments):
|
|
with pytest.raises(ValueError):
|
|
_ = ObstacleSetPathway(*attachments)
|
|
|
|
@staticmethod
|
|
@pytest.mark.parametrize(
|
|
'attachments',
|
|
[
|
|
(None, Point('pA'), Point('pI')),
|
|
(Point('pO'), None, Point('pI')),
|
|
(Point('pO'), Point('pA'), None),
|
|
]
|
|
)
|
|
def test_invalid_constructor_attachments_not_point(attachments):
|
|
with pytest.raises(TypeError):
|
|
_ = WrappingPathway(*attachments) # type: ignore
|
|
|
|
def test_properties_are_immutable(self):
|
|
pathway = ObstacleSetPathway(Point('pO'), Point('pA'), Point('pI'))
|
|
with pytest.raises(AttributeError):
|
|
pathway.attachments = None # type: ignore
|
|
with pytest.raises(TypeError):
|
|
pathway.attachments[0] = None # type: ignore
|
|
with pytest.raises(TypeError):
|
|
pathway.attachments[1] = None # type: ignore
|
|
with pytest.raises(TypeError):
|
|
pathway.attachments[-1] = None # type: ignore
|
|
|
|
@staticmethod
|
|
@pytest.mark.parametrize(
|
|
'attachments, expected',
|
|
[
|
|
(
|
|
[Point(name) for name in ('pO', 'pA', 'pI')],
|
|
'ObstacleSetPathway(pO, pA, pI)'
|
|
),
|
|
(
|
|
[Point(name) for name in ('pO', 'pA', 'pB', 'pI')],
|
|
'ObstacleSetPathway(pO, pA, pB, pI)'
|
|
),
|
|
(
|
|
[Point(name) for name in ('pO', 'pA', 'pB', 'pC', 'pI')],
|
|
'ObstacleSetPathway(pO, pA, pB, pC, pI)'
|
|
),
|
|
]
|
|
)
|
|
def test_repr(attachments, expected):
|
|
pathway = ObstacleSetPathway(*attachments)
|
|
assert repr(pathway) == expected
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _obstacle_set_pathway_fixture(self):
|
|
self.N = ReferenceFrame('N')
|
|
self.pO = Point('pO')
|
|
self.pI = Point('pI')
|
|
self.pA = Point('pA')
|
|
self.pB = Point('pB')
|
|
self.q = dynamicsymbols('q')
|
|
self.qd = dynamicsymbols('q', 1)
|
|
self.F = Symbol('F')
|
|
|
|
def test_static_pathway_length(self):
|
|
self.pA.set_pos(self.pO, self.N.x)
|
|
self.pB.set_pos(self.pO, self.N.y)
|
|
self.pI.set_pos(self.pO, self.N.z)
|
|
pathway = ObstacleSetPathway(self.pO, self.pA, self.pB, self.pI)
|
|
assert pathway.length == 1 + 2 * sqrt(2)
|
|
|
|
def test_static_pathway_extension_velocity(self):
|
|
self.pA.set_pos(self.pO, self.N.x)
|
|
self.pB.set_pos(self.pO, self.N.y)
|
|
self.pI.set_pos(self.pO, self.N.z)
|
|
pathway = ObstacleSetPathway(self.pO, self.pA, self.pB, self.pI)
|
|
assert pathway.extension_velocity == 0
|
|
|
|
def test_static_pathway_to_loads(self):
|
|
self.pA.set_pos(self.pO, self.N.x)
|
|
self.pB.set_pos(self.pO, self.N.y)
|
|
self.pI.set_pos(self.pO, self.N.z)
|
|
pathway = ObstacleSetPathway(self.pO, self.pA, self.pB, self.pI)
|
|
expected = [
|
|
Force(self.pO, -self.F * self.N.x),
|
|
Force(self.pA, self.F * self.N.x),
|
|
Force(self.pA, self.F * sqrt(2) / 2 * (self.N.x - self.N.y)),
|
|
Force(self.pB, self.F * sqrt(2) / 2 * (self.N.y - self.N.x)),
|
|
Force(self.pB, self.F * sqrt(2) / 2 * (self.N.y - self.N.z)),
|
|
Force(self.pI, self.F * sqrt(2) / 2 * (self.N.z - self.N.y)),
|
|
]
|
|
assert pathway.to_loads(self.F) == expected
|
|
|
|
def test_2D_pathway_length(self):
|
|
self.pA.set_pos(self.pO, -(self.N.x + self.N.y))
|
|
self.pB.set_pos(
|
|
self.pO, cos(self.q) * self.N.x - (sin(self.q) + 1) * self.N.y
|
|
)
|
|
self.pI.set_pos(
|
|
self.pO, sin(self.q) * self.N.x + (cos(self.q) - 1) * self.N.y
|
|
)
|
|
pathway = ObstacleSetPathway(self.pO, self.pA, self.pB, self.pI)
|
|
expected = 2 * sqrt(2) + sqrt(2 + 2*cos(self.q))
|
|
assert (pathway.length - expected).simplify() == 0
|
|
|
|
def test_2D_pathway_extension_velocity(self):
|
|
self.pA.set_pos(self.pO, -(self.N.x + self.N.y))
|
|
self.pB.set_pos(
|
|
self.pO, cos(self.q) * self.N.x - (sin(self.q) + 1) * self.N.y
|
|
)
|
|
self.pI.set_pos(
|
|
self.pO, sin(self.q) * self.N.x + (cos(self.q) - 1) * self.N.y
|
|
)
|
|
pathway = ObstacleSetPathway(self.pO, self.pA, self.pB, self.pI)
|
|
expected = - (sqrt(2) * sin(self.q) * self.qd) / (2 * sqrt(cos(self.q) + 1))
|
|
assert (pathway.extension_velocity - expected).simplify() == 0
|
|
|
|
def test_2D_pathway_to_loads(self):
|
|
self.pA.set_pos(self.pO, -(self.N.x + self.N.y))
|
|
self.pB.set_pos(
|
|
self.pO, cos(self.q) * self.N.x - (sin(self.q) + 1) * self.N.y
|
|
)
|
|
self.pI.set_pos(
|
|
self.pO, sin(self.q) * self.N.x + (cos(self.q) - 1) * self.N.y
|
|
)
|
|
pathway = ObstacleSetPathway(self.pO, self.pA, self.pB, self.pI)
|
|
pO_pA_force_vec = sqrt(2) / 2 * (self.N.x + self.N.y)
|
|
pA_pB_force_vec = (
|
|
- sqrt(2 * cos(self.q) + 2) / 2 * self.N.x
|
|
+ sqrt(2) * sin(self.q) / (2 * sqrt(cos(self.q) + 1)) * self.N.y
|
|
)
|
|
pB_pI_force_vec = cos(self.q + pi/4) * self.N.x - sin(self.q + pi/4) * self.N.y
|
|
expected = [
|
|
Force(self.pO, self.F * pO_pA_force_vec),
|
|
Force(self.pA, -self.F * pO_pA_force_vec),
|
|
Force(self.pA, self.F * pA_pB_force_vec),
|
|
Force(self.pB, -self.F * pA_pB_force_vec),
|
|
Force(self.pB, self.F * pB_pI_force_vec),
|
|
Force(self.pI, -self.F * pB_pI_force_vec),
|
|
]
|
|
assert _simplify_loads(pathway.to_loads(self.F)) == expected
|
|
|
|
|
|
class TestWrappingPathway:
|
|
|
|
def test_is_pathway_base_subclass(self):
|
|
assert issubclass(WrappingPathway, PathwayBase)
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _wrapping_pathway_fixture(self):
|
|
self.pA = Point('pA')
|
|
self.pB = Point('pB')
|
|
self.r = Symbol('r', positive=True)
|
|
self.pO = Point('pO')
|
|
self.N = ReferenceFrame('N')
|
|
self.ax = self.N.z
|
|
self.sphere = WrappingSphere(self.r, self.pO)
|
|
self.cylinder = WrappingCylinder(self.r, self.pO, self.ax)
|
|
self.pathway = WrappingPathway(self.pA, self.pB, self.cylinder)
|
|
self.F = Symbol('F')
|
|
|
|
def test_valid_constructor(self):
|
|
instance = WrappingPathway(self.pA, self.pB, self.cylinder)
|
|
assert isinstance(instance, WrappingPathway)
|
|
assert hasattr(instance, 'attachments')
|
|
assert len(instance.attachments) == 2
|
|
assert isinstance(instance.attachments[0], Point)
|
|
assert instance.attachments[0] == self.pA
|
|
assert isinstance(instance.attachments[1], Point)
|
|
assert instance.attachments[1] == self.pB
|
|
assert hasattr(instance, 'geometry')
|
|
assert isinstance(instance.geometry, WrappingGeometryBase)
|
|
assert instance.geometry == self.cylinder
|
|
|
|
@pytest.mark.parametrize(
|
|
'attachments',
|
|
[
|
|
(Point('pA'), ),
|
|
(Point('pA'), Point('pB'), Point('pZ')),
|
|
]
|
|
)
|
|
def test_invalid_constructor_attachments_incorrect_number(self, attachments):
|
|
with pytest.raises(TypeError):
|
|
_ = WrappingPathway(*attachments, self.cylinder)
|
|
|
|
@staticmethod
|
|
@pytest.mark.parametrize(
|
|
'attachments',
|
|
[
|
|
(None, Point('pB')),
|
|
(Point('pA'), None),
|
|
]
|
|
)
|
|
def test_invalid_constructor_attachments_not_point(attachments):
|
|
with pytest.raises(TypeError):
|
|
_ = WrappingPathway(*attachments)
|
|
|
|
def test_invalid_constructor_geometry_is_not_supplied(self):
|
|
with pytest.raises(TypeError):
|
|
_ = WrappingPathway(self.pA, self.pB)
|
|
|
|
@pytest.mark.parametrize(
|
|
'geometry',
|
|
[
|
|
Symbol('r'),
|
|
dynamicsymbols('q'),
|
|
ReferenceFrame('N'),
|
|
ReferenceFrame('N').x,
|
|
]
|
|
)
|
|
def test_invalid_geometry_not_geometry(self, geometry):
|
|
with pytest.raises(TypeError):
|
|
_ = WrappingPathway(self.pA, self.pB, geometry)
|
|
|
|
def test_attachments_property_is_immutable(self):
|
|
with pytest.raises(TypeError):
|
|
self.pathway.attachments[0] = self.pB
|
|
with pytest.raises(TypeError):
|
|
self.pathway.attachments[1] = self.pA
|
|
|
|
def test_geometry_property_is_immutable(self):
|
|
with pytest.raises(AttributeError):
|
|
self.pathway.geometry = None
|
|
|
|
def test_repr(self):
|
|
expected = (
|
|
f'WrappingPathway(pA, pB, '
|
|
f'geometry={self.cylinder!r})'
|
|
)
|
|
assert repr(self.pathway) == expected
|
|
|
|
@staticmethod
|
|
def _expand_pos_to_vec(pos, frame):
|
|
return sum(mag*unit for (mag, unit) in zip(pos, frame))
|
|
|
|
@pytest.mark.parametrize(
|
|
'pA_vec, pB_vec, factor',
|
|
[
|
|
((1, 0, 0), (0, 1, 0), pi/2),
|
|
((0, 1, 0), (sqrt(2)/2, -sqrt(2)/2, 0), 3*pi/4),
|
|
((1, 0, 0), (Rational(1, 2), sqrt(3)/2, 0), pi/3),
|
|
]
|
|
)
|
|
def test_static_pathway_on_sphere_length(self, pA_vec, pB_vec, factor):
|
|
pA_vec = self._expand_pos_to_vec(pA_vec, self.N)
|
|
pB_vec = self._expand_pos_to_vec(pB_vec, self.N)
|
|
self.pA.set_pos(self.pO, self.r*pA_vec)
|
|
self.pB.set_pos(self.pO, self.r*pB_vec)
|
|
pathway = WrappingPathway(self.pA, self.pB, self.sphere)
|
|
expected = factor*self.r
|
|
assert simplify(pathway.length - expected) == 0
|
|
|
|
@pytest.mark.parametrize(
|
|
'pA_vec, pB_vec, factor',
|
|
[
|
|
((1, 0, 0), (0, 1, 0), Rational(1, 2)*pi),
|
|
((1, 0, 0), (-1, 0, 0), pi),
|
|
((-1, 0, 0), (1, 0, 0), pi),
|
|
((0, 1, 0), (sqrt(2)/2, -sqrt(2)/2, 0), 5*pi/4),
|
|
((1, 0, 0), (Rational(1, 2), sqrt(3)/2, 0), pi/3),
|
|
(
|
|
(0, 1, 0),
|
|
(sqrt(2)*Rational(1, 2), -sqrt(2)*Rational(1, 2), 1),
|
|
sqrt(1 + (Rational(5, 4)*pi)**2),
|
|
),
|
|
(
|
|
(1, 0, 0),
|
|
(Rational(1, 2), sqrt(3)*Rational(1, 2), 1),
|
|
sqrt(1 + (Rational(1, 3)*pi)**2),
|
|
),
|
|
]
|
|
)
|
|
def test_static_pathway_on_cylinder_length(self, pA_vec, pB_vec, factor):
|
|
pA_vec = self._expand_pos_to_vec(pA_vec, self.N)
|
|
pB_vec = self._expand_pos_to_vec(pB_vec, self.N)
|
|
self.pA.set_pos(self.pO, self.r*pA_vec)
|
|
self.pB.set_pos(self.pO, self.r*pB_vec)
|
|
pathway = WrappingPathway(self.pA, self.pB, self.cylinder)
|
|
expected = factor*sqrt(self.r**2)
|
|
assert simplify(pathway.length - expected) == 0
|
|
|
|
@pytest.mark.parametrize(
|
|
'pA_vec, pB_vec',
|
|
[
|
|
((1, 0, 0), (0, 1, 0)),
|
|
((0, 1, 0), (sqrt(2)*Rational(1, 2), -sqrt(2)*Rational(1, 2), 0)),
|
|
((1, 0, 0), (Rational(1, 2), sqrt(3)*Rational(1, 2), 0)),
|
|
]
|
|
)
|
|
def test_static_pathway_on_sphere_extension_velocity(self, pA_vec, pB_vec):
|
|
pA_vec = self._expand_pos_to_vec(pA_vec, self.N)
|
|
pB_vec = self._expand_pos_to_vec(pB_vec, self.N)
|
|
self.pA.set_pos(self.pO, self.r*pA_vec)
|
|
self.pB.set_pos(self.pO, self.r*pB_vec)
|
|
pathway = WrappingPathway(self.pA, self.pB, self.sphere)
|
|
assert pathway.extension_velocity == 0
|
|
|
|
@pytest.mark.parametrize(
|
|
'pA_vec, pB_vec',
|
|
[
|
|
((1, 0, 0), (0, 1, 0)),
|
|
((1, 0, 0), (-1, 0, 0)),
|
|
((-1, 0, 0), (1, 0, 0)),
|
|
((0, 1, 0), (sqrt(2)/2, -sqrt(2)/2, 0)),
|
|
((1, 0, 0), (Rational(1, 2), sqrt(3)/2, 0)),
|
|
((0, 1, 0), (sqrt(2)*Rational(1, 2), -sqrt(2)/2, 1)),
|
|
((1, 0, 0), (Rational(1, 2), sqrt(3)/2, 1)),
|
|
]
|
|
)
|
|
def test_static_pathway_on_cylinder_extension_velocity(self, pA_vec, pB_vec):
|
|
pA_vec = self._expand_pos_to_vec(pA_vec, self.N)
|
|
pB_vec = self._expand_pos_to_vec(pB_vec, self.N)
|
|
self.pA.set_pos(self.pO, self.r*pA_vec)
|
|
self.pB.set_pos(self.pO, self.r*pB_vec)
|
|
pathway = WrappingPathway(self.pA, self.pB, self.cylinder)
|
|
assert pathway.extension_velocity == 0
|
|
|
|
@pytest.mark.parametrize(
|
|
'pA_vec, pB_vec, pA_vec_expected, pB_vec_expected, pO_vec_expected',
|
|
(
|
|
((1, 0, 0), (0, 1, 0), (0, 1, 0), (1, 0, 0), (-1, -1, 0)),
|
|
(
|
|
(0, 1, 0),
|
|
(sqrt(2)/2, -sqrt(2)/2, 0),
|
|
(1, 0, 0),
|
|
(sqrt(2)/2, sqrt(2)/2, 0),
|
|
(-1 - sqrt(2)/2, -sqrt(2)/2, 0)
|
|
),
|
|
(
|
|
(1, 0, 0),
|
|
(Rational(1, 2), sqrt(3)/2, 0),
|
|
(0, 1, 0),
|
|
(sqrt(3)/2, -Rational(1, 2), 0),
|
|
(-sqrt(3)/2, Rational(1, 2) - 1, 0),
|
|
),
|
|
)
|
|
)
|
|
def test_static_pathway_on_sphere_to_loads(
|
|
self,
|
|
pA_vec,
|
|
pB_vec,
|
|
pA_vec_expected,
|
|
pB_vec_expected,
|
|
pO_vec_expected,
|
|
):
|
|
pA_vec = self._expand_pos_to_vec(pA_vec, self.N)
|
|
pB_vec = self._expand_pos_to_vec(pB_vec, self.N)
|
|
self.pA.set_pos(self.pO, self.r*pA_vec)
|
|
self.pB.set_pos(self.pO, self.r*pB_vec)
|
|
pathway = WrappingPathway(self.pA, self.pB, self.sphere)
|
|
|
|
pA_vec_expected = sum(
|
|
mag*unit for (mag, unit) in zip(pA_vec_expected, self.N)
|
|
)
|
|
pB_vec_expected = sum(
|
|
mag*unit for (mag, unit) in zip(pB_vec_expected, self.N)
|
|
)
|
|
pO_vec_expected = sum(
|
|
mag*unit for (mag, unit) in zip(pO_vec_expected, self.N)
|
|
)
|
|
expected = [
|
|
Force(self.pA, self.F*(self.r**3/sqrt(self.r**6))*pA_vec_expected),
|
|
Force(self.pB, self.F*(self.r**3/sqrt(self.r**6))*pB_vec_expected),
|
|
Force(self.pO, self.F*(self.r**3/sqrt(self.r**6))*pO_vec_expected),
|
|
]
|
|
assert pathway.to_loads(self.F) == expected
|
|
|
|
@pytest.mark.parametrize(
|
|
'pA_vec, pB_vec, pA_vec_expected, pB_vec_expected, pO_vec_expected',
|
|
(
|
|
((1, 0, 0), (0, 1, 0), (0, 1, 0), (1, 0, 0), (-1, -1, 0)),
|
|
((1, 0, 0), (-1, 0, 0), (0, 1, 0), (0, 1, 0), (0, -2, 0)),
|
|
((-1, 0, 0), (1, 0, 0), (0, -1, 0), (0, -1, 0), (0, 2, 0)),
|
|
(
|
|
(0, 1, 0),
|
|
(sqrt(2)/2, -sqrt(2)/2, 0),
|
|
(-1, 0, 0),
|
|
(-sqrt(2)/2, -sqrt(2)/2, 0),
|
|
(1 + sqrt(2)/2, sqrt(2)/2, 0)
|
|
),
|
|
(
|
|
(1, 0, 0),
|
|
(Rational(1, 2), sqrt(3)/2, 0),
|
|
(0, 1, 0),
|
|
(sqrt(3)/2, -Rational(1, 2), 0),
|
|
(-sqrt(3)/2, Rational(1, 2) - 1, 0),
|
|
),
|
|
(
|
|
(1, 0, 0),
|
|
(sqrt(2)/2, sqrt(2)/2, 0),
|
|
(0, 1, 0),
|
|
(sqrt(2)/2, -sqrt(2)/2, 0),
|
|
(-sqrt(2)/2, sqrt(2)/2 - 1, 0),
|
|
),
|
|
((0, 1, 0), (0, 1, 1), (0, 0, 1), (0, 0, -1), (0, 0, 0)),
|
|
(
|
|
(0, 1, 0),
|
|
(sqrt(2)/2, -sqrt(2)/2, 1),
|
|
(-5*pi/sqrt(16 + 25*pi**2), 0, 4/sqrt(16 + 25*pi**2)),
|
|
(
|
|
-5*sqrt(2)*pi/(2*sqrt(16 + 25*pi**2)),
|
|
-5*sqrt(2)*pi/(2*sqrt(16 + 25*pi**2)),
|
|
-4/sqrt(16 + 25*pi**2),
|
|
),
|
|
(
|
|
5*(sqrt(2) + 2)*pi/(2*sqrt(16 + 25*pi**2)),
|
|
5*sqrt(2)*pi/(2*sqrt(16 + 25*pi**2)),
|
|
0,
|
|
),
|
|
),
|
|
)
|
|
)
|
|
def test_static_pathway_on_cylinder_to_loads(
|
|
self,
|
|
pA_vec,
|
|
pB_vec,
|
|
pA_vec_expected,
|
|
pB_vec_expected,
|
|
pO_vec_expected,
|
|
):
|
|
pA_vec = self._expand_pos_to_vec(pA_vec, self.N)
|
|
pB_vec = self._expand_pos_to_vec(pB_vec, self.N)
|
|
self.pA.set_pos(self.pO, self.r*pA_vec)
|
|
self.pB.set_pos(self.pO, self.r*pB_vec)
|
|
pathway = WrappingPathway(self.pA, self.pB, self.cylinder)
|
|
|
|
pA_force_expected = self.F*self._expand_pos_to_vec(pA_vec_expected,
|
|
self.N)
|
|
pB_force_expected = self.F*self._expand_pos_to_vec(pB_vec_expected,
|
|
self.N)
|
|
pO_force_expected = self.F*self._expand_pos_to_vec(pO_vec_expected,
|
|
self.N)
|
|
expected = [
|
|
Force(self.pA, pA_force_expected),
|
|
Force(self.pB, pB_force_expected),
|
|
Force(self.pO, pO_force_expected),
|
|
]
|
|
assert _simplify_loads(pathway.to_loads(self.F)) == expected
|
|
|
|
def test_2D_pathway_on_cylinder_length(self):
|
|
q = dynamicsymbols('q')
|
|
pA_pos = self.r*self.N.x
|
|
pB_pos = self.r*(cos(q)*self.N.x + sin(q)*self.N.y)
|
|
self.pA.set_pos(self.pO, pA_pos)
|
|
self.pB.set_pos(self.pO, pB_pos)
|
|
expected = self.r*sqrt(q**2)
|
|
assert simplify(self.pathway.length - expected) == 0
|
|
|
|
def test_2D_pathway_on_cylinder_extension_velocity(self):
|
|
q = dynamicsymbols('q')
|
|
qd = dynamicsymbols('q', 1)
|
|
pA_pos = self.r*self.N.x
|
|
pB_pos = self.r*(cos(q)*self.N.x + sin(q)*self.N.y)
|
|
self.pA.set_pos(self.pO, pA_pos)
|
|
self.pB.set_pos(self.pO, pB_pos)
|
|
expected = self.r*(sqrt(q**2)/q)*qd
|
|
assert simplify(self.pathway.extension_velocity - expected) == 0
|
|
|
|
def test_2D_pathway_on_cylinder_to_loads(self):
|
|
q = dynamicsymbols('q')
|
|
pA_pos = self.r*self.N.x
|
|
pB_pos = self.r*(cos(q)*self.N.x + sin(q)*self.N.y)
|
|
self.pA.set_pos(self.pO, pA_pos)
|
|
self.pB.set_pos(self.pO, pB_pos)
|
|
|
|
pA_force = self.F*self.N.y
|
|
pB_force = self.F*(sin(q)*self.N.x - cos(q)*self.N.y)
|
|
pO_force = self.F*(-sin(q)*self.N.x + (cos(q) - 1)*self.N.y)
|
|
expected = [
|
|
Force(self.pA, pA_force),
|
|
Force(self.pB, pB_force),
|
|
Force(self.pO, pO_force),
|
|
]
|
|
|
|
loads = _simplify_loads(self.pathway.to_loads(self.F))
|
|
assert loads == expected
|