test_wrapping_geometry.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. """Tests for the ``sympy.physics.mechanics.wrapping_geometry.py`` module."""
  2. import pytest
  3. from sympy import (
  4. Integer,
  5. Rational,
  6. S,
  7. Symbol,
  8. acos,
  9. cos,
  10. pi,
  11. sin,
  12. sqrt,
  13. )
  14. from sympy.core.relational import Eq
  15. from sympy.physics.mechanics import (
  16. Point,
  17. ReferenceFrame,
  18. WrappingCylinder,
  19. WrappingSphere,
  20. dynamicsymbols,
  21. )
  22. from sympy.simplify.simplify import simplify
  23. r = Symbol('r', positive=True)
  24. x = Symbol('x')
  25. q = dynamicsymbols('q')
  26. N = ReferenceFrame('N')
  27. class TestWrappingSphere:
  28. @staticmethod
  29. def test_valid_constructor():
  30. r = Symbol('r', positive=True)
  31. pO = Point('pO')
  32. sphere = WrappingSphere(r, pO)
  33. assert isinstance(sphere, WrappingSphere)
  34. assert hasattr(sphere, 'radius')
  35. assert sphere.radius == r
  36. assert hasattr(sphere, 'point')
  37. assert sphere.point == pO
  38. @staticmethod
  39. @pytest.mark.parametrize('position', [S.Zero, Integer(2)*r*N.x])
  40. def test_geodesic_length_point_not_on_surface_invalid(position):
  41. r = Symbol('r', positive=True)
  42. pO = Point('pO')
  43. sphere = WrappingSphere(r, pO)
  44. p1 = Point('p1')
  45. p1.set_pos(pO, position)
  46. p2 = Point('p2')
  47. p2.set_pos(pO, position)
  48. error_msg = r'point .* does not lie on the surface of'
  49. with pytest.raises(ValueError, match=error_msg):
  50. sphere.geodesic_length(p1, p2)
  51. @staticmethod
  52. @pytest.mark.parametrize(
  53. 'position_1, position_2, expected',
  54. [
  55. (r*N.x, r*N.x, S.Zero),
  56. (r*N.x, r*N.y, S.Half*pi*r),
  57. (r*N.x, r*-N.x, pi*r),
  58. (r*-N.x, r*N.x, pi*r),
  59. (r*N.x, r*sqrt(2)*S.Half*(N.x + N.y), Rational(1, 4)*pi*r),
  60. (
  61. r*sqrt(2)*S.Half*(N.x + N.y),
  62. r*sqrt(3)*Rational(1, 3)*(N.x + N.y + N.z),
  63. r*acos(sqrt(6)*Rational(1, 3)),
  64. ),
  65. ]
  66. )
  67. def test_geodesic_length(position_1, position_2, expected):
  68. r = Symbol('r', positive=True)
  69. pO = Point('pO')
  70. sphere = WrappingSphere(r, pO)
  71. p1 = Point('p1')
  72. p1.set_pos(pO, position_1)
  73. p2 = Point('p2')
  74. p2.set_pos(pO, position_2)
  75. assert simplify(Eq(sphere.geodesic_length(p1, p2), expected))
  76. @staticmethod
  77. @pytest.mark.parametrize(
  78. 'position_1, position_2, vector_1, vector_2',
  79. [
  80. (r * N.x, r * N.y, N.y, N.x),
  81. (r * N.x, -r * N.y, -N.y, N.x),
  82. (
  83. r * N.y,
  84. sqrt(2)/2 * r * N.x - sqrt(2)/2 * r * N.y,
  85. N.x,
  86. sqrt(2)/2 * N.x + sqrt(2)/2 * N.y,
  87. ),
  88. (
  89. r * N.x,
  90. r / 2 * N.x + sqrt(3)/2 * r * N.y,
  91. N.y,
  92. sqrt(3)/2 * N.x - 1/2 * N.y,
  93. ),
  94. (
  95. r * N.x,
  96. sqrt(2)/2 * r * N.x + sqrt(2)/2 * r * N.y,
  97. N.y,
  98. sqrt(2)/2 * N.x - sqrt(2)/2 * N.y,
  99. ),
  100. ]
  101. )
  102. def test_geodesic_end_vectors(position_1, position_2, vector_1, vector_2):
  103. r = Symbol('r', positive=True)
  104. pO = Point('pO')
  105. sphere = WrappingSphere(r, pO)
  106. p1 = Point('p1')
  107. p1.set_pos(pO, position_1)
  108. p2 = Point('p2')
  109. p2.set_pos(pO, position_2)
  110. expected = (vector_1, vector_2)
  111. assert sphere.geodesic_end_vectors(p1, p2) == expected
  112. @staticmethod
  113. @pytest.mark.parametrize(
  114. 'position',
  115. [r * N.x, r * cos(q) * N.x + r * sin(q) * N.y]
  116. )
  117. def test_geodesic_end_vectors_invalid_coincident(position):
  118. r = Symbol('r', positive=True)
  119. pO = Point('pO')
  120. sphere = WrappingSphere(r, pO)
  121. p1 = Point('p1')
  122. p1.set_pos(pO, position)
  123. p2 = Point('p2')
  124. p2.set_pos(pO, position)
  125. with pytest.raises(ValueError):
  126. _ = sphere.geodesic_end_vectors(p1, p2)
  127. @staticmethod
  128. @pytest.mark.parametrize(
  129. 'position_1, position_2',
  130. [
  131. (r * N.x, -r * N.x),
  132. (-r * N.y, r * N.y),
  133. (
  134. r * cos(q) * N.x + r * sin(q) * N.y,
  135. -r * cos(q) * N.x - r * sin(q) * N.y,
  136. )
  137. ]
  138. )
  139. def test_geodesic_end_vectors_invalid_diametrically_opposite(
  140. position_1,
  141. position_2,
  142. ):
  143. r = Symbol('r', positive=True)
  144. pO = Point('pO')
  145. sphere = WrappingSphere(r, pO)
  146. p1 = Point('p1')
  147. p1.set_pos(pO, position_1)
  148. p2 = Point('p2')
  149. p2.set_pos(pO, position_2)
  150. with pytest.raises(ValueError):
  151. _ = sphere.geodesic_end_vectors(p1, p2)
  152. class TestWrappingCylinder:
  153. @staticmethod
  154. def test_valid_constructor():
  155. N = ReferenceFrame('N')
  156. r = Symbol('r', positive=True)
  157. pO = Point('pO')
  158. cylinder = WrappingCylinder(r, pO, N.x)
  159. assert isinstance(cylinder, WrappingCylinder)
  160. assert hasattr(cylinder, 'radius')
  161. assert cylinder.radius == r
  162. assert hasattr(cylinder, 'point')
  163. assert cylinder.point == pO
  164. assert hasattr(cylinder, 'axis')
  165. assert cylinder.axis == N.x
  166. @staticmethod
  167. @pytest.mark.parametrize(
  168. 'position, expected',
  169. [
  170. (S.Zero, False),
  171. (r*N.y, True),
  172. (r*N.z, True),
  173. (r*(N.y + N.z).normalize(), True),
  174. (Integer(2)*r*N.y, False),
  175. (r*(N.x + N.y), True),
  176. (r*(Integer(2)*N.x + N.y), True),
  177. (Integer(2)*N.x + r*(Integer(2)*N.y + N.z).normalize(), True),
  178. (r*(cos(q)*N.y + sin(q)*N.z), True)
  179. ]
  180. )
  181. def test_point_is_on_surface(position, expected):
  182. r = Symbol('r', positive=True)
  183. pO = Point('pO')
  184. cylinder = WrappingCylinder(r, pO, N.x)
  185. p1 = Point('p1')
  186. p1.set_pos(pO, position)
  187. assert cylinder.point_on_surface(p1) is expected
  188. @staticmethod
  189. @pytest.mark.parametrize('position', [S.Zero, Integer(2)*r*N.y])
  190. def test_geodesic_length_point_not_on_surface_invalid(position):
  191. r = Symbol('r', positive=True)
  192. pO = Point('pO')
  193. cylinder = WrappingCylinder(r, pO, N.x)
  194. p1 = Point('p1')
  195. p1.set_pos(pO, position)
  196. p2 = Point('p2')
  197. p2.set_pos(pO, position)
  198. error_msg = r'point .* does not lie on the surface of'
  199. with pytest.raises(ValueError, match=error_msg):
  200. cylinder.geodesic_length(p1, p2)
  201. @staticmethod
  202. @pytest.mark.parametrize(
  203. 'axis, position_1, position_2, expected',
  204. [
  205. (N.x, r*N.y, r*N.y, S.Zero),
  206. (N.x, r*N.y, N.x + r*N.y, S.One),
  207. (N.x, r*N.y, -x*N.x + r*N.y, sqrt(x**2)),
  208. (-N.x, r*N.y, x*N.x + r*N.y, sqrt(x**2)),
  209. (N.x, r*N.y, r*N.z, S.Half*pi*sqrt(r**2)),
  210. (-N.x, r*N.y, r*N.z, Integer(3)*S.Half*pi*sqrt(r**2)),
  211. (N.x, r*N.z, r*N.y, Integer(3)*S.Half*pi*sqrt(r**2)),
  212. (-N.x, r*N.z, r*N.y, S.Half*pi*sqrt(r**2)),
  213. (N.x, r*N.y, r*(cos(q)*N.y + sin(q)*N.z), sqrt(r**2*q**2)),
  214. (
  215. -N.x, r*N.y,
  216. r*(cos(q)*N.y + sin(q)*N.z),
  217. sqrt(r**2*(Integer(2)*pi - q)**2),
  218. ),
  219. ]
  220. )
  221. def test_geodesic_length(axis, position_1, position_2, expected):
  222. r = Symbol('r', positive=True)
  223. pO = Point('pO')
  224. cylinder = WrappingCylinder(r, pO, axis)
  225. p1 = Point('p1')
  226. p1.set_pos(pO, position_1)
  227. p2 = Point('p2')
  228. p2.set_pos(pO, position_2)
  229. assert simplify(Eq(cylinder.geodesic_length(p1, p2), expected))
  230. @staticmethod
  231. @pytest.mark.parametrize(
  232. 'axis, position_1, position_2, vector_1, vector_2',
  233. [
  234. (N.z, r * N.x, r * N.y, N.y, N.x),
  235. (N.z, r * N.x, -r * N.x, N.y, N.y),
  236. (N.z, -r * N.x, r * N.x, -N.y, -N.y),
  237. (-N.z, r * N.x, -r * N.x, -N.y, -N.y),
  238. (-N.z, -r * N.x, r * N.x, N.y, N.y),
  239. (N.z, r * N.x, -r * N.y, N.y, -N.x),
  240. (
  241. N.z,
  242. r * N.y,
  243. sqrt(2)/2 * r * N.x - sqrt(2)/2 * r * N.y,
  244. - N.x,
  245. - sqrt(2)/2 * N.x - sqrt(2)/2 * N.y,
  246. ),
  247. (
  248. N.z,
  249. r * N.x,
  250. r / 2 * N.x + sqrt(3)/2 * r * N.y,
  251. N.y,
  252. sqrt(3)/2 * N.x - 1/2 * N.y,
  253. ),
  254. (
  255. N.z,
  256. r * N.x,
  257. sqrt(2)/2 * r * N.x + sqrt(2)/2 * r * N.y,
  258. N.y,
  259. sqrt(2)/2 * N.x - sqrt(2)/2 * N.y,
  260. ),
  261. (
  262. N.z,
  263. r * N.x,
  264. r * N.x + N.z,
  265. N.z,
  266. -N.z,
  267. ),
  268. (
  269. N.z,
  270. r * N.x,
  271. r * N.y + pi/2 * r * N.z,
  272. sqrt(2)/2 * N.y + sqrt(2)/2 * N.z,
  273. sqrt(2)/2 * N.x - sqrt(2)/2 * N.z,
  274. ),
  275. (
  276. N.z,
  277. r * N.x,
  278. r * cos(q) * N.x + r * sin(q) * N.y,
  279. N.y,
  280. sin(q) * N.x - cos(q) * N.y,
  281. ),
  282. ]
  283. )
  284. def test_geodesic_end_vectors(
  285. axis,
  286. position_1,
  287. position_2,
  288. vector_1,
  289. vector_2,
  290. ):
  291. r = Symbol('r', positive=True)
  292. pO = Point('pO')
  293. cylinder = WrappingCylinder(r, pO, axis)
  294. p1 = Point('p1')
  295. p1.set_pos(pO, position_1)
  296. p2 = Point('p2')
  297. p2.set_pos(pO, position_2)
  298. expected = (vector_1, vector_2)
  299. end_vectors = tuple(
  300. end_vector.simplify()
  301. for end_vector in cylinder.geodesic_end_vectors(p1, p2)
  302. )
  303. assert end_vectors == expected
  304. @staticmethod
  305. @pytest.mark.parametrize(
  306. 'axis, position',
  307. [
  308. (N.z, r * N.x),
  309. (N.z, r * cos(q) * N.x + r * sin(q) * N.y + N.z),
  310. ]
  311. )
  312. def test_geodesic_end_vectors_invalid_coincident(axis, position):
  313. r = Symbol('r', positive=True)
  314. pO = Point('pO')
  315. cylinder = WrappingCylinder(r, pO, axis)
  316. p1 = Point('p1')
  317. p1.set_pos(pO, position)
  318. p2 = Point('p2')
  319. p2.set_pos(pO, position)
  320. with pytest.raises(ValueError):
  321. _ = cylinder.geodesic_end_vectors(p1, p2)