point.py 20 KB

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