body.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  1. from sympy.core.backend import Symbol
  2. from sympy.physics.vector import Point, Vector, ReferenceFrame, Dyadic
  3. from sympy.physics.mechanics import RigidBody, Particle, inertia
  4. __all__ = ['Body']
  5. # XXX: We use type:ignore because the classes RigidBody and Particle have
  6. # inconsistent parallel axis methods that take different numbers of arguments.
  7. class Body(RigidBody, Particle): # type: ignore
  8. """
  9. Body is a common representation of either a RigidBody or a Particle SymPy
  10. object depending on what is passed in during initialization. If a mass is
  11. passed in and central_inertia is left as None, the Particle object is
  12. created. Otherwise a RigidBody object will be created.
  13. Explanation
  14. ===========
  15. The attributes that Body possesses will be the same as a Particle instance
  16. or a Rigid Body instance depending on which was created. Additional
  17. attributes are listed below.
  18. Attributes
  19. ==========
  20. name : string
  21. The body's name
  22. masscenter : Point
  23. The point which represents the center of mass of the rigid body
  24. frame : ReferenceFrame
  25. The reference frame which the body is fixed in
  26. mass : Sympifyable
  27. The body's mass
  28. inertia : (Dyadic, Point)
  29. The body's inertia around its center of mass. This attribute is specific
  30. to the rigid body form of Body and is left undefined for the Particle
  31. form
  32. loads : iterable
  33. This list contains information on the different loads acting on the
  34. Body. Forces are listed as a (point, vector) tuple and torques are
  35. listed as (reference frame, vector) tuples.
  36. Parameters
  37. ==========
  38. name : String
  39. Defines the name of the body. It is used as the base for defining
  40. body specific properties.
  41. masscenter : Point, optional
  42. A point that represents the center of mass of the body or particle.
  43. If no point is given, a point is generated.
  44. mass : Sympifyable, optional
  45. A Sympifyable object which represents the mass of the body. If no
  46. mass is passed, one is generated.
  47. frame : ReferenceFrame, optional
  48. The ReferenceFrame that represents the reference frame of the body.
  49. If no frame is given, a frame is generated.
  50. central_inertia : Dyadic, optional
  51. Central inertia dyadic of the body. If none is passed while creating
  52. RigidBody, a default inertia is generated.
  53. Examples
  54. ========
  55. Default behaviour. This results in the creation of a RigidBody object for
  56. which the mass, mass center, frame and inertia attributes are given default
  57. values. ::
  58. >>> from sympy.physics.mechanics import Body
  59. >>> body = Body('name_of_body')
  60. This next example demonstrates the code required to specify all of the
  61. values of the Body object. Note this will also create a RigidBody version of
  62. the Body object. ::
  63. >>> from sympy import Symbol
  64. >>> from sympy.physics.mechanics import ReferenceFrame, Point, inertia
  65. >>> from sympy.physics.mechanics import Body
  66. >>> mass = Symbol('mass')
  67. >>> masscenter = Point('masscenter')
  68. >>> frame = ReferenceFrame('frame')
  69. >>> ixx = Symbol('ixx')
  70. >>> body_inertia = inertia(frame, ixx, 0, 0)
  71. >>> body = Body('name_of_body', masscenter, mass, frame, body_inertia)
  72. The minimal code required to create a Particle version of the Body object
  73. involves simply passing in a name and a mass. ::
  74. >>> from sympy import Symbol
  75. >>> from sympy.physics.mechanics import Body
  76. >>> mass = Symbol('mass')
  77. >>> body = Body('name_of_body', mass=mass)
  78. The Particle version of the Body object can also receive a masscenter point
  79. and a reference frame, just not an inertia.
  80. """
  81. def __init__(self, name, masscenter=None, mass=None, frame=None,
  82. central_inertia=None):
  83. self.name = name
  84. self._loads = []
  85. if frame is None:
  86. frame = ReferenceFrame(name + '_frame')
  87. if masscenter is None:
  88. masscenter = Point(name + '_masscenter')
  89. if central_inertia is None and mass is None:
  90. ixx = Symbol(name + '_ixx')
  91. iyy = Symbol(name + '_iyy')
  92. izz = Symbol(name + '_izz')
  93. izx = Symbol(name + '_izx')
  94. ixy = Symbol(name + '_ixy')
  95. iyz = Symbol(name + '_iyz')
  96. _inertia = (inertia(frame, ixx, iyy, izz, ixy, iyz, izx),
  97. masscenter)
  98. else:
  99. _inertia = (central_inertia, masscenter)
  100. if mass is None:
  101. _mass = Symbol(name + '_mass')
  102. else:
  103. _mass = mass
  104. masscenter.set_vel(frame, 0)
  105. # If user passes masscenter and mass then a particle is created
  106. # otherwise a rigidbody. As a result a body may or may not have inertia.
  107. if central_inertia is None and mass is not None:
  108. self.frame = frame
  109. self.masscenter = masscenter
  110. Particle.__init__(self, name, masscenter, _mass)
  111. self._central_inertia = Dyadic(0)
  112. else:
  113. RigidBody.__init__(self, name, masscenter, frame, _mass, _inertia)
  114. @property
  115. def loads(self):
  116. return self._loads
  117. @property
  118. def x(self):
  119. """The basis Vector for the Body, in the x direction."""
  120. return self.frame.x
  121. @property
  122. def y(self):
  123. """The basis Vector for the Body, in the y direction."""
  124. return self.frame.y
  125. @property
  126. def z(self):
  127. """The basis Vector for the Body, in the z direction."""
  128. return self.frame.z
  129. @property
  130. def inertia(self):
  131. """The body's inertia about a point; stored as (Dyadic, Point)."""
  132. if self.is_rigidbody:
  133. return RigidBody.inertia.fget(self)
  134. return (self.central_inertia, self.masscenter)
  135. @inertia.setter
  136. def inertia(self, I):
  137. RigidBody.inertia.fset(self, I)
  138. @property
  139. def is_rigidbody(self):
  140. if hasattr(self, '_inertia'):
  141. return True
  142. return False
  143. def kinetic_energy(self, frame):
  144. """Kinetic energy of the body.
  145. Parameters
  146. ==========
  147. frame : ReferenceFrame or Body
  148. The Body's angular velocity and the velocity of it's mass
  149. center are typically defined with respect to an inertial frame but
  150. any relevant frame in which the velocities are known can be supplied.
  151. Examples
  152. ========
  153. >>> from sympy.physics.mechanics import Body, ReferenceFrame, Point
  154. >>> from sympy import symbols
  155. >>> m, v, r, omega = symbols('m v r omega')
  156. >>> N = ReferenceFrame('N')
  157. >>> O = Point('O')
  158. >>> P = Body('P', masscenter=O, mass=m)
  159. >>> P.masscenter.set_vel(N, v * N.y)
  160. >>> P.kinetic_energy(N)
  161. m*v**2/2
  162. >>> N = ReferenceFrame('N')
  163. >>> b = ReferenceFrame('b')
  164. >>> b.set_ang_vel(N, omega * b.x)
  165. >>> P = Point('P')
  166. >>> P.set_vel(N, v * N.x)
  167. >>> B = Body('B', masscenter=P, frame=b)
  168. >>> B.kinetic_energy(N)
  169. B_ixx*omega**2/2 + B_mass*v**2/2
  170. See Also
  171. ========
  172. sympy.physics.mechanics : Particle, RigidBody
  173. """
  174. if isinstance(frame, Body):
  175. frame = Body.frame
  176. if self.is_rigidbody:
  177. return RigidBody(self.name, self.masscenter, self.frame, self.mass,
  178. (self.central_inertia, self.masscenter)).kinetic_energy(frame)
  179. return Particle(self.name, self.masscenter, self.mass).kinetic_energy(frame)
  180. def apply_force(self, force, point=None, reaction_body=None, reaction_point=None):
  181. """Add force to the body(s).
  182. Explanation
  183. ===========
  184. Applies the force on self or equal and oppposite forces on
  185. self and other body if both are given on the desried point on the bodies.
  186. The force applied on other body is taken opposite of self, i.e, -force.
  187. Parameters
  188. ==========
  189. force: Vector
  190. The force to be applied.
  191. point: Point, optional
  192. The point on self on which force is applied.
  193. By default self's masscenter.
  194. reaction_body: Body, optional
  195. Second body on which equal and opposite force
  196. is to be applied.
  197. reaction_point : Point, optional
  198. The point on other body on which equal and opposite
  199. force is applied. By default masscenter of other body.
  200. Example
  201. =======
  202. >>> from sympy import symbols
  203. >>> from sympy.physics.mechanics import Body, Point, dynamicsymbols
  204. >>> m, g = symbols('m g')
  205. >>> B = Body('B')
  206. >>> force1 = m*g*B.z
  207. >>> B.apply_force(force1) #Applying force on B's masscenter
  208. >>> B.loads
  209. [(B_masscenter, g*m*B_frame.z)]
  210. We can also remove some part of force from any point on the body by
  211. adding the opposite force to the body on that point.
  212. >>> f1, f2 = dynamicsymbols('f1 f2')
  213. >>> P = Point('P') #Considering point P on body B
  214. >>> B.apply_force(f1*B.x + f2*B.y, P)
  215. >>> B.loads
  216. [(B_masscenter, g*m*B_frame.z), (P, f1(t)*B_frame.x + f2(t)*B_frame.y)]
  217. Let's remove f1 from point P on body B.
  218. >>> B.apply_force(-f1*B.x, P)
  219. >>> B.loads
  220. [(B_masscenter, g*m*B_frame.z), (P, f2(t)*B_frame.y)]
  221. To further demonstrate the use of ``apply_force`` attribute,
  222. consider two bodies connected through a spring.
  223. >>> from sympy.physics.mechanics import Body, dynamicsymbols
  224. >>> N = Body('N') #Newtonion Frame
  225. >>> x = dynamicsymbols('x')
  226. >>> B1 = Body('B1')
  227. >>> B2 = Body('B2')
  228. >>> spring_force = x*N.x
  229. Now let's apply equal and opposite spring force to the bodies.
  230. >>> P1 = Point('P1')
  231. >>> P2 = Point('P2')
  232. >>> B1.apply_force(spring_force, point=P1, reaction_body=B2, reaction_point=P2)
  233. We can check the loads(forces) applied to bodies now.
  234. >>> B1.loads
  235. [(P1, x(t)*N_frame.x)]
  236. >>> B2.loads
  237. [(P2, - x(t)*N_frame.x)]
  238. Notes
  239. =====
  240. If a new force is applied to a body on a point which already has some
  241. force applied on it, then the new force is added to the already applied
  242. force on that point.
  243. """
  244. if not isinstance(point, Point):
  245. if point is None:
  246. point = self.masscenter # masscenter
  247. else:
  248. raise TypeError("Force must be applied to a point on the body.")
  249. if not isinstance(force, Vector):
  250. raise TypeError("Force must be a vector.")
  251. if reaction_body is not None:
  252. reaction_body.apply_force(-force, point=reaction_point)
  253. for load in self._loads:
  254. if point in load:
  255. force += load[1]
  256. self._loads.remove(load)
  257. break
  258. self._loads.append((point, force))
  259. def apply_torque(self, torque, reaction_body=None):
  260. """Add torque to the body(s).
  261. Explanation
  262. ===========
  263. Applies the torque on self or equal and oppposite torquess on
  264. self and other body if both are given.
  265. The torque applied on other body is taken opposite of self,
  266. i.e, -torque.
  267. Parameters
  268. ==========
  269. torque: Vector
  270. The torque to be applied.
  271. reaction_body: Body, optional
  272. Second body on which equal and opposite torque
  273. is to be applied.
  274. Example
  275. =======
  276. >>> from sympy import symbols
  277. >>> from sympy.physics.mechanics import Body, dynamicsymbols
  278. >>> t = symbols('t')
  279. >>> B = Body('B')
  280. >>> torque1 = t*B.z
  281. >>> B.apply_torque(torque1)
  282. >>> B.loads
  283. [(B_frame, t*B_frame.z)]
  284. We can also remove some part of torque from the body by
  285. adding the opposite torque to the body.
  286. >>> t1, t2 = dynamicsymbols('t1 t2')
  287. >>> B.apply_torque(t1*B.x + t2*B.y)
  288. >>> B.loads
  289. [(B_frame, t1(t)*B_frame.x + t2(t)*B_frame.y + t*B_frame.z)]
  290. Let's remove t1 from Body B.
  291. >>> B.apply_torque(-t1*B.x)
  292. >>> B.loads
  293. [(B_frame, t2(t)*B_frame.y + t*B_frame.z)]
  294. To further demonstrate the use, let us consider two bodies such that
  295. a torque `T` is acting on one body, and `-T` on the other.
  296. >>> from sympy.physics.mechanics import Body, dynamicsymbols
  297. >>> N = Body('N') #Newtonion frame
  298. >>> B1 = Body('B1')
  299. >>> B2 = Body('B2')
  300. >>> v = dynamicsymbols('v')
  301. >>> T = v*N.y #Torque
  302. Now let's apply equal and opposite torque to the bodies.
  303. >>> B1.apply_torque(T, B2)
  304. We can check the loads (torques) applied to bodies now.
  305. >>> B1.loads
  306. [(B1_frame, v(t)*N_frame.y)]
  307. >>> B2.loads
  308. [(B2_frame, - v(t)*N_frame.y)]
  309. Notes
  310. =====
  311. If a new torque is applied on body which already has some torque applied on it,
  312. then the new torque is added to the previous torque about the body's frame.
  313. """
  314. if not isinstance(torque, Vector):
  315. raise TypeError("A Vector must be supplied to add torque.")
  316. if reaction_body is not None:
  317. reaction_body.apply_torque(-torque)
  318. for load in self._loads:
  319. if self.frame in load:
  320. torque += load[1]
  321. self._loads.remove(load)
  322. break
  323. self._loads.append((self.frame, torque))
  324. def clear_loads(self):
  325. """
  326. Clears the Body's loads list.
  327. Example
  328. =======
  329. >>> from sympy.physics.mechanics import Body
  330. >>> B = Body('B')
  331. >>> force = B.x + B.y
  332. >>> B.apply_force(force)
  333. >>> B.loads
  334. [(B_masscenter, B_frame.x + B_frame.y)]
  335. >>> B.clear_loads()
  336. >>> B.loads
  337. []
  338. """
  339. self._loads = []
  340. def remove_load(self, about=None):
  341. """
  342. Remove load about a point or frame.
  343. Parameters
  344. ==========
  345. about : Point or ReferenceFrame, optional
  346. The point about which force is applied,
  347. and is to be removed.
  348. If about is None, then the torque about
  349. self's frame is removed.
  350. Example
  351. =======
  352. >>> from sympy.physics.mechanics import Body, Point
  353. >>> B = Body('B')
  354. >>> P = Point('P')
  355. >>> f1 = B.x
  356. >>> f2 = B.y
  357. >>> B.apply_force(f1)
  358. >>> B.apply_force(f2, P)
  359. >>> B.loads
  360. [(B_masscenter, B_frame.x), (P, B_frame.y)]
  361. >>> B.remove_load(P)
  362. >>> B.loads
  363. [(B_masscenter, B_frame.x)]
  364. """
  365. if about is not None:
  366. if not isinstance(about, Point):
  367. raise TypeError('Load is applied about Point or ReferenceFrame.')
  368. else:
  369. about = self.frame
  370. for load in self._loads:
  371. if about in load:
  372. self._loads.remove(load)
  373. break
  374. def masscenter_vel(self, body):
  375. """
  376. Returns the velocity of the mass center with respect to the provided
  377. rigid body or reference frame.
  378. Parameters
  379. ==========
  380. body: Body or ReferenceFrame
  381. The rigid body or reference frame to calculate the velocity in.
  382. Example
  383. =======
  384. >>> from sympy.physics.mechanics import Body
  385. >>> A = Body('A')
  386. >>> B = Body('B')
  387. >>> A.masscenter.set_vel(B.frame, 5*B.frame.x)
  388. >>> A.masscenter_vel(B)
  389. 5*B_frame.x
  390. >>> A.masscenter_vel(B.frame)
  391. 5*B_frame.x
  392. """
  393. if isinstance(body, ReferenceFrame):
  394. frame=body
  395. elif isinstance(body, Body):
  396. frame = body.frame
  397. return self.masscenter.vel(frame)
  398. def ang_vel_in(self, body):
  399. """
  400. Returns this body's angular velocity with respect to the provided
  401. rigid body or reference frame.
  402. Parameters
  403. ==========
  404. body: Body or ReferenceFrame
  405. The rigid body or reference frame to calculate the angular velocity in.
  406. Example
  407. =======
  408. >>> from sympy.physics.mechanics import Body, ReferenceFrame
  409. >>> A = Body('A')
  410. >>> N = ReferenceFrame('N')
  411. >>> B = Body('B', frame=N)
  412. >>> A.frame.set_ang_vel(N, 5*N.x)
  413. >>> A.ang_vel_in(B)
  414. 5*N.x
  415. >>> A.ang_vel_in(N)
  416. 5*N.x
  417. """
  418. if isinstance(body, ReferenceFrame):
  419. frame=body
  420. elif isinstance(body, Body):
  421. frame = body.frame
  422. return self.frame.ang_vel_in(frame)
  423. def dcm(self, body):
  424. """
  425. Returns the direction cosine matrix of this body relative to the
  426. provided rigid body or reference frame.
  427. Parameters
  428. ==========
  429. body: Body or ReferenceFrame
  430. The rigid body or reference frame to calculate the dcm.
  431. Example
  432. =======
  433. >>> from sympy.physics.mechanics import Body
  434. >>> A = Body('A')
  435. >>> B = Body('B')
  436. >>> A.frame.orient_axis(B.frame, B.frame.x, 5)
  437. >>> A.dcm(B)
  438. Matrix([
  439. [1, 0, 0],
  440. [0, cos(5), sin(5)],
  441. [0, -sin(5), cos(5)]])
  442. >>> A.dcm(B.frame)
  443. Matrix([
  444. [1, 0, 0],
  445. [0, cos(5), sin(5)],
  446. [0, -sin(5), cos(5)]])
  447. """
  448. if isinstance(body, ReferenceFrame):
  449. frame=body
  450. elif isinstance(body, Body):
  451. frame = body.frame
  452. return self.frame.dcm(frame)
  453. def parallel_axis(self, point, frame=None):
  454. """Returns the inertia dyadic of the body with respect to another
  455. point.
  456. Parameters
  457. ==========
  458. point : sympy.physics.vector.Point
  459. The point to express the inertia dyadic about.
  460. frame : sympy.physics.vector.ReferenceFrame
  461. The reference frame used to construct the dyadic.
  462. Returns
  463. =======
  464. inertia : sympy.physics.vector.Dyadic
  465. The inertia dyadic of the rigid body expressed about the provided
  466. point.
  467. Example
  468. =======
  469. >>> from sympy.physics.mechanics import Body
  470. >>> A = Body('A')
  471. >>> P = A.masscenter.locatenew('point', 3 * A.x + 5 * A.y)
  472. >>> A.parallel_axis(P).to_matrix(A.frame)
  473. Matrix([
  474. [A_ixx + 25*A_mass, A_ixy - 15*A_mass, A_izx],
  475. [A_ixy - 15*A_mass, A_iyy + 9*A_mass, A_iyz],
  476. [ A_izx, A_iyz, A_izz + 34*A_mass]])
  477. """
  478. if self.is_rigidbody:
  479. return RigidBody.parallel_axis(self, point, frame)
  480. return Particle.parallel_axis(self, point, frame)