point.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  1. from .vector import Vector, _check_vector
  2. from .frame import _check_frame
  3. from warnings import warn
  4. from sympy.utilities.misc import filldedent
  5. __all__ = ['Point']
  6. class Point:
  7. """This object represents a point in a dynamic system.
  8. It stores the: position, velocity, and acceleration of a point.
  9. The position is a vector defined as the vector distance from a parent
  10. point to this point.
  11. Parameters
  12. ==========
  13. name : string
  14. The display name of the Point
  15. Examples
  16. ========
  17. >>> from sympy.physics.vector import Point, ReferenceFrame, dynamicsymbols
  18. >>> from sympy.physics.vector import init_vprinting
  19. >>> init_vprinting(pretty_print=False)
  20. >>> N = ReferenceFrame('N')
  21. >>> O = Point('O')
  22. >>> P = Point('P')
  23. >>> u1, u2, u3 = dynamicsymbols('u1 u2 u3')
  24. >>> O.set_vel(N, u1 * N.x + u2 * N.y + u3 * N.z)
  25. >>> O.acc(N)
  26. u1'*N.x + u2'*N.y + u3'*N.z
  27. ``symbols()`` can be used to create multiple Points in a single step, for
  28. example:
  29. >>> from sympy.physics.vector import Point, ReferenceFrame, dynamicsymbols
  30. >>> from sympy.physics.vector import init_vprinting
  31. >>> init_vprinting(pretty_print=False)
  32. >>> from sympy import symbols
  33. >>> N = ReferenceFrame('N')
  34. >>> u1, u2 = dynamicsymbols('u1 u2')
  35. >>> A, B = symbols('A B', cls=Point)
  36. >>> type(A)
  37. <class 'sympy.physics.vector.point.Point'>
  38. >>> A.set_vel(N, u1 * N.x + u2 * N.y)
  39. >>> B.set_vel(N, u2 * N.x + u1 * N.y)
  40. >>> A.acc(N) - B.acc(N)
  41. (u1' - u2')*N.x + (-u1' + u2')*N.y
  42. """
  43. def __init__(self, name):
  44. """Initialization of a Point object. """
  45. self.name = name
  46. self._pos_dict = {}
  47. self._vel_dict = {}
  48. self._acc_dict = {}
  49. self._pdlist = [self._pos_dict, self._vel_dict, self._acc_dict]
  50. def __str__(self):
  51. return self.name
  52. __repr__ = __str__
  53. def _check_point(self, other):
  54. if not isinstance(other, Point):
  55. raise TypeError('A Point must be supplied')
  56. def _pdict_list(self, other, num):
  57. """Returns a list of points that gives the shortest path with respect
  58. to position, velocity, or acceleration from this point to the provided
  59. point.
  60. Parameters
  61. ==========
  62. other : Point
  63. A point that may be related to this point by position, velocity, or
  64. acceleration.
  65. num : integer
  66. 0 for searching the position tree, 1 for searching the velocity
  67. tree, and 2 for searching the acceleration tree.
  68. Returns
  69. =======
  70. list of Points
  71. A sequence of points from self to other.
  72. Notes
  73. =====
  74. It is not clear if num = 1 or num = 2 actually works because the keys
  75. to ``_vel_dict`` and ``_acc_dict`` are :class:`ReferenceFrame` objects
  76. which do not have the ``_pdlist`` attribute.
  77. """
  78. outlist = [[self]]
  79. oldlist = [[]]
  80. while outlist != oldlist:
  81. oldlist = outlist.copy()
  82. for v in outlist:
  83. templist = v[-1]._pdlist[num].keys()
  84. for v2 in templist:
  85. if not v.__contains__(v2):
  86. littletemplist = v + [v2]
  87. if not outlist.__contains__(littletemplist):
  88. outlist.append(littletemplist)
  89. for v in oldlist:
  90. if v[-1] != other:
  91. outlist.remove(v)
  92. outlist.sort(key=len)
  93. if len(outlist) != 0:
  94. return outlist[0]
  95. raise ValueError('No Connecting Path found between ' + other.name +
  96. ' and ' + self.name)
  97. def a1pt_theory(self, otherpoint, outframe, interframe):
  98. """Sets the acceleration of this point with the 1-point theory.
  99. The 1-point theory for point acceleration looks like this:
  100. ^N a^P = ^B a^P + ^N a^O + ^N alpha^B x r^OP + ^N omega^B x (^N omega^B
  101. x r^OP) + 2 ^N omega^B x ^B v^P
  102. where O is a point fixed in B, P is a point moving in B, and B is
  103. rotating in frame N.
  104. Parameters
  105. ==========
  106. otherpoint : Point
  107. The first point of the 1-point theory (O)
  108. outframe : ReferenceFrame
  109. The frame we want this point's acceleration defined in (N)
  110. fixedframe : ReferenceFrame
  111. The intermediate frame in this calculation (B)
  112. Examples
  113. ========
  114. >>> from sympy.physics.vector import Point, ReferenceFrame
  115. >>> from sympy.physics.vector import dynamicsymbols
  116. >>> from sympy.physics.vector import init_vprinting
  117. >>> init_vprinting(pretty_print=False)
  118. >>> q = dynamicsymbols('q')
  119. >>> q2 = dynamicsymbols('q2')
  120. >>> qd = dynamicsymbols('q', 1)
  121. >>> q2d = dynamicsymbols('q2', 1)
  122. >>> N = ReferenceFrame('N')
  123. >>> B = ReferenceFrame('B')
  124. >>> B.set_ang_vel(N, 5 * B.y)
  125. >>> O = Point('O')
  126. >>> P = O.locatenew('P', q * B.x + q2 * B.y)
  127. >>> P.set_vel(B, qd * B.x + q2d * B.y)
  128. >>> O.set_vel(N, 0)
  129. >>> P.a1pt_theory(O, N, B)
  130. (-25*q + q'')*B.x + q2''*B.y - 10*q'*B.z
  131. """
  132. _check_frame(outframe)
  133. _check_frame(interframe)
  134. self._check_point(otherpoint)
  135. dist = self.pos_from(otherpoint)
  136. v = self.vel(interframe)
  137. a1 = otherpoint.acc(outframe)
  138. a2 = self.acc(interframe)
  139. omega = interframe.ang_vel_in(outframe)
  140. alpha = interframe.ang_acc_in(outframe)
  141. self.set_acc(outframe, a2 + 2 * (omega.cross(v)) + a1 +
  142. (alpha.cross(dist)) + (omega.cross(omega.cross(dist))))
  143. return self.acc(outframe)
  144. def a2pt_theory(self, otherpoint, outframe, fixedframe):
  145. """Sets the acceleration of this point with the 2-point theory.
  146. The 2-point theory for point acceleration looks like this:
  147. ^N a^P = ^N a^O + ^N alpha^B x r^OP + ^N omega^B x (^N omega^B x r^OP)
  148. where O and P are both points fixed in frame B, which is rotating in
  149. frame N.
  150. Parameters
  151. ==========
  152. otherpoint : Point
  153. The first point of the 2-point theory (O)
  154. outframe : ReferenceFrame
  155. The frame we want this point's acceleration defined in (N)
  156. fixedframe : ReferenceFrame
  157. The frame in which both points are fixed (B)
  158. Examples
  159. ========
  160. >>> from sympy.physics.vector import Point, ReferenceFrame, dynamicsymbols
  161. >>> from sympy.physics.vector import init_vprinting
  162. >>> init_vprinting(pretty_print=False)
  163. >>> q = dynamicsymbols('q')
  164. >>> qd = dynamicsymbols('q', 1)
  165. >>> N = ReferenceFrame('N')
  166. >>> B = N.orientnew('B', 'Axis', [q, N.z])
  167. >>> O = Point('O')
  168. >>> P = O.locatenew('P', 10 * B.x)
  169. >>> O.set_vel(N, 5 * N.x)
  170. >>> P.a2pt_theory(O, N, B)
  171. - 10*q'**2*B.x + 10*q''*B.y
  172. """
  173. _check_frame(outframe)
  174. _check_frame(fixedframe)
  175. self._check_point(otherpoint)
  176. dist = self.pos_from(otherpoint)
  177. a = otherpoint.acc(outframe)
  178. omega = fixedframe.ang_vel_in(outframe)
  179. alpha = fixedframe.ang_acc_in(outframe)
  180. self.set_acc(outframe, a + (alpha.cross(dist)) +
  181. (omega.cross(omega.cross(dist))))
  182. return self.acc(outframe)
  183. def acc(self, frame):
  184. """The acceleration Vector of this Point in a ReferenceFrame.
  185. Parameters
  186. ==========
  187. frame : ReferenceFrame
  188. The frame in which the returned acceleration vector will be defined
  189. in.
  190. Examples
  191. ========
  192. >>> from sympy.physics.vector import Point, ReferenceFrame
  193. >>> N = ReferenceFrame('N')
  194. >>> p1 = Point('p1')
  195. >>> p1.set_acc(N, 10 * N.x)
  196. >>> p1.acc(N)
  197. 10*N.x
  198. """
  199. _check_frame(frame)
  200. if not (frame in self._acc_dict):
  201. if self.vel(frame) != 0:
  202. return (self._vel_dict[frame]).dt(frame)
  203. else:
  204. return Vector(0)
  205. return self._acc_dict[frame]
  206. def locatenew(self, name, value):
  207. """Creates a new point with a position defined from this point.
  208. Parameters
  209. ==========
  210. name : str
  211. The name for the new point
  212. value : Vector
  213. The position of the new point relative to this point
  214. Examples
  215. ========
  216. >>> from sympy.physics.vector import ReferenceFrame, Point
  217. >>> N = ReferenceFrame('N')
  218. >>> P1 = Point('P1')
  219. >>> P2 = P1.locatenew('P2', 10 * N.x)
  220. """
  221. if not isinstance(name, str):
  222. raise TypeError('Must supply a valid name')
  223. if value == 0:
  224. value = Vector(0)
  225. value = _check_vector(value)
  226. p = Point(name)
  227. p.set_pos(self, value)
  228. self.set_pos(p, -value)
  229. return p
  230. def pos_from(self, otherpoint):
  231. """Returns a Vector distance between this Point and the other Point.
  232. Parameters
  233. ==========
  234. otherpoint : Point
  235. The otherpoint we are locating this one relative to
  236. Examples
  237. ========
  238. >>> from sympy.physics.vector import Point, ReferenceFrame
  239. >>> N = ReferenceFrame('N')
  240. >>> p1 = Point('p1')
  241. >>> p2 = Point('p2')
  242. >>> p1.set_pos(p2, 10 * N.x)
  243. >>> p1.pos_from(p2)
  244. 10*N.x
  245. """
  246. outvec = Vector(0)
  247. plist = self._pdict_list(otherpoint, 0)
  248. for i in range(len(plist) - 1):
  249. outvec += plist[i]._pos_dict[plist[i + 1]]
  250. return outvec
  251. def set_acc(self, frame, value):
  252. """Used to set the acceleration of this Point in a ReferenceFrame.
  253. Parameters
  254. ==========
  255. frame : ReferenceFrame
  256. The frame in which this point's acceleration is defined
  257. value : Vector
  258. The vector value of this point's acceleration in the frame
  259. Examples
  260. ========
  261. >>> from sympy.physics.vector import Point, ReferenceFrame
  262. >>> N = ReferenceFrame('N')
  263. >>> p1 = Point('p1')
  264. >>> p1.set_acc(N, 10 * N.x)
  265. >>> p1.acc(N)
  266. 10*N.x
  267. """
  268. if value == 0:
  269. value = Vector(0)
  270. value = _check_vector(value)
  271. _check_frame(frame)
  272. self._acc_dict.update({frame: value})
  273. def set_pos(self, otherpoint, value):
  274. """Used to set the position of this point w.r.t. another point.
  275. Parameters
  276. ==========
  277. otherpoint : Point
  278. The other point which this point's location is defined relative to
  279. value : Vector
  280. The vector which defines the location of this point
  281. Examples
  282. ========
  283. >>> from sympy.physics.vector import Point, ReferenceFrame
  284. >>> N = ReferenceFrame('N')
  285. >>> p1 = Point('p1')
  286. >>> p2 = Point('p2')
  287. >>> p1.set_pos(p2, 10 * N.x)
  288. >>> p1.pos_from(p2)
  289. 10*N.x
  290. """
  291. if value == 0:
  292. value = Vector(0)
  293. value = _check_vector(value)
  294. self._check_point(otherpoint)
  295. self._pos_dict.update({otherpoint: value})
  296. otherpoint._pos_dict.update({self: -value})
  297. def set_vel(self, frame, value):
  298. """Sets the velocity Vector of this Point in a ReferenceFrame.
  299. Parameters
  300. ==========
  301. frame : ReferenceFrame
  302. The frame in which this point's velocity is defined
  303. value : Vector
  304. The vector value of this point's velocity in the frame
  305. Examples
  306. ========
  307. >>> from sympy.physics.vector import Point, ReferenceFrame
  308. >>> N = ReferenceFrame('N')
  309. >>> p1 = Point('p1')
  310. >>> p1.set_vel(N, 10 * N.x)
  311. >>> p1.vel(N)
  312. 10*N.x
  313. """
  314. if value == 0:
  315. value = Vector(0)
  316. value = _check_vector(value)
  317. _check_frame(frame)
  318. self._vel_dict.update({frame: value})
  319. def v1pt_theory(self, otherpoint, outframe, interframe):
  320. """Sets the velocity of this point with the 1-point theory.
  321. The 1-point theory for point velocity looks like this:
  322. ^N v^P = ^B v^P + ^N v^O + ^N omega^B x r^OP
  323. where O is a point fixed in B, P is a point moving in B, and B is
  324. rotating in frame N.
  325. Parameters
  326. ==========
  327. otherpoint : Point
  328. The first point of the 1-point theory (O)
  329. outframe : ReferenceFrame
  330. The frame we want this point's velocity defined in (N)
  331. interframe : ReferenceFrame
  332. The intermediate frame in this calculation (B)
  333. Examples
  334. ========
  335. >>> from sympy.physics.vector import Point, ReferenceFrame
  336. >>> from sympy.physics.vector import dynamicsymbols
  337. >>> from sympy.physics.vector import init_vprinting
  338. >>> init_vprinting(pretty_print=False)
  339. >>> q = dynamicsymbols('q')
  340. >>> q2 = dynamicsymbols('q2')
  341. >>> qd = dynamicsymbols('q', 1)
  342. >>> q2d = dynamicsymbols('q2', 1)
  343. >>> N = ReferenceFrame('N')
  344. >>> B = ReferenceFrame('B')
  345. >>> B.set_ang_vel(N, 5 * B.y)
  346. >>> O = Point('O')
  347. >>> P = O.locatenew('P', q * B.x + q2 * B.y)
  348. >>> P.set_vel(B, qd * B.x + q2d * B.y)
  349. >>> O.set_vel(N, 0)
  350. >>> P.v1pt_theory(O, N, B)
  351. q'*B.x + q2'*B.y - 5*q*B.z
  352. """
  353. _check_frame(outframe)
  354. _check_frame(interframe)
  355. self._check_point(otherpoint)
  356. dist = self.pos_from(otherpoint)
  357. v1 = self.vel(interframe)
  358. v2 = otherpoint.vel(outframe)
  359. omega = interframe.ang_vel_in(outframe)
  360. self.set_vel(outframe, v1 + v2 + (omega.cross(dist)))
  361. return self.vel(outframe)
  362. def v2pt_theory(self, otherpoint, outframe, fixedframe):
  363. """Sets the velocity of this point with the 2-point theory.
  364. The 2-point theory for point velocity looks like this:
  365. ^N v^P = ^N v^O + ^N omega^B x r^OP
  366. where O and P are both points fixed in frame B, which is rotating in
  367. frame N.
  368. Parameters
  369. ==========
  370. otherpoint : Point
  371. The first point of the 2-point theory (O)
  372. outframe : ReferenceFrame
  373. The frame we want this point's velocity defined in (N)
  374. fixedframe : ReferenceFrame
  375. The frame in which both points are fixed (B)
  376. Examples
  377. ========
  378. >>> from sympy.physics.vector import Point, ReferenceFrame, dynamicsymbols
  379. >>> from sympy.physics.vector import init_vprinting
  380. >>> init_vprinting(pretty_print=False)
  381. >>> q = dynamicsymbols('q')
  382. >>> qd = dynamicsymbols('q', 1)
  383. >>> N = ReferenceFrame('N')
  384. >>> B = N.orientnew('B', 'Axis', [q, N.z])
  385. >>> O = Point('O')
  386. >>> P = O.locatenew('P', 10 * B.x)
  387. >>> O.set_vel(N, 5 * N.x)
  388. >>> P.v2pt_theory(O, N, B)
  389. 5*N.x + 10*q'*B.y
  390. """
  391. _check_frame(outframe)
  392. _check_frame(fixedframe)
  393. self._check_point(otherpoint)
  394. dist = self.pos_from(otherpoint)
  395. v = otherpoint.vel(outframe)
  396. omega = fixedframe.ang_vel_in(outframe)
  397. self.set_vel(outframe, v + (omega.cross(dist)))
  398. return self.vel(outframe)
  399. def vel(self, frame):
  400. """The velocity Vector of this Point in the ReferenceFrame.
  401. Parameters
  402. ==========
  403. frame : ReferenceFrame
  404. The frame in which the returned velocity vector will be defined in
  405. Examples
  406. ========
  407. >>> from sympy.physics.vector import Point, ReferenceFrame, dynamicsymbols
  408. >>> N = ReferenceFrame('N')
  409. >>> p1 = Point('p1')
  410. >>> p1.set_vel(N, 10 * N.x)
  411. >>> p1.vel(N)
  412. 10*N.x
  413. Velocities will be automatically calculated if possible, otherwise a
  414. ``ValueError`` will be returned. If it is possible to calculate
  415. multiple different velocities from the relative points, the points
  416. defined most directly relative to this point will be used. In the case
  417. of inconsistent relative positions of points, incorrect velocities may
  418. be returned. It is up to the user to define prior relative positions
  419. and velocities of points in a self-consistent way.
  420. >>> p = Point('p')
  421. >>> q = dynamicsymbols('q')
  422. >>> p.set_vel(N, 10 * N.x)
  423. >>> p2 = Point('p2')
  424. >>> p2.set_pos(p, q*N.x)
  425. >>> p2.vel(N)
  426. (Derivative(q(t), t) + 10)*N.x
  427. """
  428. _check_frame(frame)
  429. if not (frame in self._vel_dict):
  430. valid_neighbor_found = False
  431. is_cyclic = False
  432. visited = []
  433. queue = [self]
  434. candidate_neighbor = []
  435. while queue: # BFS to find nearest point
  436. node = queue.pop(0)
  437. if node not in visited:
  438. visited.append(node)
  439. for neighbor, neighbor_pos in node._pos_dict.items():
  440. if neighbor in visited:
  441. continue
  442. try:
  443. # Checks if pos vector is valid
  444. neighbor_pos.express(frame)
  445. except ValueError:
  446. continue
  447. if neighbor in queue:
  448. is_cyclic = True
  449. try:
  450. # Checks if point has its vel defined in req frame
  451. neighbor_velocity = neighbor._vel_dict[frame]
  452. except KeyError:
  453. queue.append(neighbor)
  454. continue
  455. candidate_neighbor.append(neighbor)
  456. if not valid_neighbor_found:
  457. self.set_vel(frame, self.pos_from(neighbor).dt(frame) + neighbor_velocity)
  458. valid_neighbor_found = True
  459. if is_cyclic:
  460. warn(filldedent("""
  461. Kinematic loops are defined among the positions of points. This
  462. is likely not desired and may cause errors in your calculations.
  463. """))
  464. if len(candidate_neighbor) > 1:
  465. warn(filldedent(f"""
  466. Velocity of {self.name} automatically calculated based on point
  467. {candidate_neighbor[0].name} but it is also possible from
  468. points(s): {str(candidate_neighbor[1:])}. Velocities from these
  469. points are not necessarily the same. This may cause errors in
  470. your calculations."""))
  471. if valid_neighbor_found:
  472. return self._vel_dict[frame]
  473. else:
  474. raise ValueError(filldedent(f"""
  475. Velocity of point {self.name} has not been defined in
  476. ReferenceFrame {frame.name}."""))
  477. return self._vel_dict[frame]
  478. def partial_velocity(self, frame, *gen_speeds):
  479. """Returns the partial velocities of the linear velocity vector of this
  480. point in the given frame with respect to one or more provided
  481. generalized speeds.
  482. Parameters
  483. ==========
  484. frame : ReferenceFrame
  485. The frame with which the velocity is defined in.
  486. gen_speeds : functions of time
  487. The generalized speeds.
  488. Returns
  489. =======
  490. partial_velocities : tuple of Vector
  491. The partial velocity vectors corresponding to the provided
  492. generalized speeds.
  493. Examples
  494. ========
  495. >>> from sympy.physics.vector import ReferenceFrame, Point
  496. >>> from sympy.physics.vector import dynamicsymbols
  497. >>> N = ReferenceFrame('N')
  498. >>> A = ReferenceFrame('A')
  499. >>> p = Point('p')
  500. >>> u1, u2 = dynamicsymbols('u1, u2')
  501. >>> p.set_vel(N, u1 * N.x + u2 * A.y)
  502. >>> p.partial_velocity(N, u1)
  503. N.x
  504. >>> p.partial_velocity(N, u1, u2)
  505. (N.x, A.y)
  506. """
  507. from sympy.physics.vector.functions import partial_velocity
  508. vel = self.vel(frame)
  509. partials = partial_velocity([vel], gen_speeds, frame)[0]
  510. if len(partials) == 1:
  511. return partials[0]
  512. else:
  513. return tuple(partials)