body.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. from sympy.core.backend import Symbol
  2. from sympy.physics.vector import Point, Vector, ReferenceFrame
  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 = None
  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 is_rigidbody(self):
  131. if self.central_inertia is not None:
  132. return True
  133. return False
  134. def kinetic_energy(self, frame):
  135. """Kinetic energy of the body.
  136. Parameters
  137. ==========
  138. frame : ReferenceFrame or Body
  139. The Body's angular velocity and the velocity of it's mass
  140. center are typically defined with respect to an inertial frame but
  141. any relevant frame in which the velocities are known can be supplied.
  142. Examples
  143. ========
  144. >>> from sympy.physics.mechanics import Body, ReferenceFrame, Point
  145. >>> from sympy import symbols
  146. >>> m, v, r, omega = symbols('m v r omega')
  147. >>> N = ReferenceFrame('N')
  148. >>> O = Point('O')
  149. >>> P = Body('P', masscenter=O, mass=m)
  150. >>> P.masscenter.set_vel(N, v * N.y)
  151. >>> P.kinetic_energy(N)
  152. m*v**2/2
  153. >>> N = ReferenceFrame('N')
  154. >>> b = ReferenceFrame('b')
  155. >>> b.set_ang_vel(N, omega * b.x)
  156. >>> P = Point('P')
  157. >>> P.set_vel(N, v * N.x)
  158. >>> B = Body('B', masscenter=P, frame=b)
  159. >>> B.kinetic_energy(N)
  160. B_ixx*omega**2/2 + B_mass*v**2/2
  161. See Also
  162. ========
  163. sympy.physics.mechanics : Particle, RigidBody
  164. """
  165. if isinstance(frame, Body):
  166. frame = Body.frame
  167. if self.is_rigidbody:
  168. return RigidBody(self.name, self.masscenter, self.frame, self.mass,
  169. (self.central_inertia, self.masscenter)).kinetic_energy(frame)
  170. return Particle(self.name, self.masscenter, self.mass).kinetic_energy(frame)
  171. def apply_force(self, force, point=None, reaction_body=None, reaction_point=None):
  172. """Add force to the body(s).
  173. Explanation
  174. ===========
  175. Applies the force on self or equal and oppposite forces on
  176. self and other body if both are given on the desried point on the bodies.
  177. The force applied on other body is taken opposite of self, i.e, -force.
  178. Parameters
  179. ==========
  180. force: Vector
  181. The force to be applied.
  182. point: Point, optional
  183. The point on self on which force is applied.
  184. By default self's masscenter.
  185. reaction_body: Body, optional
  186. Second body on which equal and opposite force
  187. is to be applied.
  188. reaction_point : Point, optional
  189. The point on other body on which equal and opposite
  190. force is applied. By default masscenter of other body.
  191. Example
  192. =======
  193. >>> from sympy import symbols
  194. >>> from sympy.physics.mechanics import Body, Point, dynamicsymbols
  195. >>> m, g = symbols('m g')
  196. >>> B = Body('B')
  197. >>> force1 = m*g*B.z
  198. >>> B.apply_force(force1) #Applying force on B's masscenter
  199. >>> B.loads
  200. [(B_masscenter, g*m*B_frame.z)]
  201. We can also remove some part of force from any point on the body by
  202. adding the opposite force to the body on that point.
  203. >>> f1, f2 = dynamicsymbols('f1 f2')
  204. >>> P = Point('P') #Considering point P on body B
  205. >>> B.apply_force(f1*B.x + f2*B.y, P)
  206. >>> B.loads
  207. [(B_masscenter, g*m*B_frame.z), (P, f1(t)*B_frame.x + f2(t)*B_frame.y)]
  208. Let's remove f1 from point P on body B.
  209. >>> B.apply_force(-f1*B.x, P)
  210. >>> B.loads
  211. [(B_masscenter, g*m*B_frame.z), (P, f2(t)*B_frame.y)]
  212. To further demonstrate the use of ``apply_force`` attribute,
  213. consider two bodies connected through a spring.
  214. >>> from sympy.physics.mechanics import Body, dynamicsymbols
  215. >>> N = Body('N') #Newtonion Frame
  216. >>> x = dynamicsymbols('x')
  217. >>> B1 = Body('B1')
  218. >>> B2 = Body('B2')
  219. >>> spring_force = x*N.x
  220. Now let's apply equal and opposite spring force to the bodies.
  221. >>> P1 = Point('P1')
  222. >>> P2 = Point('P2')
  223. >>> B1.apply_force(spring_force, point=P1, reaction_body=B2, reaction_point=P2)
  224. We can check the loads(forces) applied to bodies now.
  225. >>> B1.loads
  226. [(P1, x(t)*N_frame.x)]
  227. >>> B2.loads
  228. [(P2, - x(t)*N_frame.x)]
  229. Notes
  230. =====
  231. If a new force is applied to a body on a point which already has some
  232. force applied on it, then the new force is added to the already applied
  233. force on that point.
  234. """
  235. if not isinstance(point, Point):
  236. if point is None:
  237. point = self.masscenter # masscenter
  238. else:
  239. raise TypeError("Force must be applied to a point on the body.")
  240. if not isinstance(force, Vector):
  241. raise TypeError("Force must be a vector.")
  242. if reaction_body is not None:
  243. reaction_body.apply_force(-force, point=reaction_point)
  244. for load in self._loads:
  245. if point in load:
  246. force += load[1]
  247. self._loads.remove(load)
  248. break
  249. self._loads.append((point, force))
  250. def apply_torque(self, torque, reaction_body=None):
  251. """Add torque to the body(s).
  252. Explanation
  253. ===========
  254. Applies the torque on self or equal and oppposite torquess on
  255. self and other body if both are given.
  256. The torque applied on other body is taken opposite of self,
  257. i.e, -torque.
  258. Parameters
  259. ==========
  260. torque: Vector
  261. The torque to be applied.
  262. reaction_body: Body, optional
  263. Second body on which equal and opposite torque
  264. is to be applied.
  265. Example
  266. =======
  267. >>> from sympy import symbols
  268. >>> from sympy.physics.mechanics import Body, dynamicsymbols
  269. >>> t = symbols('t')
  270. >>> B = Body('B')
  271. >>> torque1 = t*B.z
  272. >>> B.apply_torque(torque1)
  273. >>> B.loads
  274. [(B_frame, t*B_frame.z)]
  275. We can also remove some part of torque from the body by
  276. adding the opposite torque to the body.
  277. >>> t1, t2 = dynamicsymbols('t1 t2')
  278. >>> B.apply_torque(t1*B.x + t2*B.y)
  279. >>> B.loads
  280. [(B_frame, t1(t)*B_frame.x + t2(t)*B_frame.y + t*B_frame.z)]
  281. Let's remove t1 from Body B.
  282. >>> B.apply_torque(-t1*B.x)
  283. >>> B.loads
  284. [(B_frame, t2(t)*B_frame.y + t*B_frame.z)]
  285. To further demonstrate the use, let us consider two bodies such that
  286. a torque `T` is acting on one body, and `-T` on the other.
  287. >>> from sympy.physics.mechanics import Body, dynamicsymbols
  288. >>> N = Body('N') #Newtonion frame
  289. >>> B1 = Body('B1')
  290. >>> B2 = Body('B2')
  291. >>> v = dynamicsymbols('v')
  292. >>> T = v*N.y #Torque
  293. Now let's apply equal and opposite torque to the bodies.
  294. >>> B1.apply_torque(T, B2)
  295. We can check the loads (torques) applied to bodies now.
  296. >>> B1.loads
  297. [(B1_frame, v(t)*N_frame.y)]
  298. >>> B2.loads
  299. [(B2_frame, - v(t)*N_frame.y)]
  300. Notes
  301. =====
  302. If a new torque is applied on body which already has some torque applied on it,
  303. then the new torque is added to the previous torque about the body's frame.
  304. """
  305. if not isinstance(torque, Vector):
  306. raise TypeError("A Vector must be supplied to add torque.")
  307. if reaction_body is not None:
  308. reaction_body.apply_torque(-torque)
  309. for load in self._loads:
  310. if self.frame in load:
  311. torque += load[1]
  312. self._loads.remove(load)
  313. break
  314. self._loads.append((self.frame, torque))
  315. def clear_loads(self):
  316. """
  317. Clears the Body's loads list.
  318. Example
  319. =======
  320. >>> from sympy.physics.mechanics import Body
  321. >>> B = Body('B')
  322. >>> force = B.x + B.y
  323. >>> B.apply_force(force)
  324. >>> B.loads
  325. [(B_masscenter, B_frame.x + B_frame.y)]
  326. >>> B.clear_loads()
  327. >>> B.loads
  328. []
  329. """
  330. self._loads = []
  331. def remove_load(self, about=None):
  332. """
  333. Remove load about a point or frame.
  334. Parameters
  335. ==========
  336. about : Point or ReferenceFrame, optional
  337. The point about which force is applied,
  338. and is to be removed.
  339. If about is None, then the torque about
  340. self's frame is removed.
  341. Example
  342. =======
  343. >>> from sympy.physics.mechanics import Body, Point
  344. >>> B = Body('B')
  345. >>> P = Point('P')
  346. >>> f1 = B.x
  347. >>> f2 = B.y
  348. >>> B.apply_force(f1)
  349. >>> B.apply_force(f2, P)
  350. >>> B.loads
  351. [(B_masscenter, B_frame.x), (P, B_frame.y)]
  352. >>> B.remove_load(P)
  353. >>> B.loads
  354. [(B_masscenter, B_frame.x)]
  355. """
  356. if about is not None:
  357. if not isinstance(about, Point):
  358. raise TypeError('Load is applied about Point or ReferenceFrame.')
  359. else:
  360. about = self.frame
  361. for load in self._loads:
  362. if about in load:
  363. self._loads.remove(load)
  364. break
  365. def masscenter_vel(self, body):
  366. """
  367. Returns the velocity of the mass center with respect to the provided
  368. rigid body or reference frame.
  369. Parameters
  370. ==========
  371. body: Body or ReferenceFrame
  372. The rigid body or reference frame to calculate the velocity in.
  373. Example
  374. =======
  375. >>> from sympy.physics.mechanics import Body
  376. >>> A = Body('A')
  377. >>> B = Body('B')
  378. >>> A.masscenter.set_vel(B.frame, 5*B.frame.x)
  379. >>> A.masscenter_vel(B)
  380. 5*B_frame.x
  381. >>> A.masscenter_vel(B.frame)
  382. 5*B_frame.x
  383. """
  384. if isinstance(body, ReferenceFrame):
  385. frame=body
  386. elif isinstance(body, Body):
  387. frame = body.frame
  388. return self.masscenter.vel(frame)
  389. def ang_vel_in(self, body):
  390. """
  391. Returns this body's angular velocity with respect to the provided
  392. rigid body or reference frame.
  393. Parameters
  394. ==========
  395. body: Body or ReferenceFrame
  396. The rigid body or reference frame to calculate the angular velocity in.
  397. Example
  398. =======
  399. >>> from sympy.physics.mechanics import Body, ReferenceFrame
  400. >>> A = Body('A')
  401. >>> N = ReferenceFrame('N')
  402. >>> B = Body('B', frame=N)
  403. >>> A.frame.set_ang_vel(N, 5*N.x)
  404. >>> A.ang_vel_in(B)
  405. 5*N.x
  406. >>> A.ang_vel_in(N)
  407. 5*N.x
  408. """
  409. if isinstance(body, ReferenceFrame):
  410. frame=body
  411. elif isinstance(body, Body):
  412. frame = body.frame
  413. return self.frame.ang_vel_in(frame)
  414. def dcm(self, body):
  415. """
  416. Returns the direction cosine matrix of this body relative to the
  417. provided rigid body or reference frame.
  418. Parameters
  419. ==========
  420. body: Body or ReferenceFrame
  421. The rigid body or reference frame to calculate the dcm.
  422. Example
  423. =======
  424. >>> from sympy.physics.mechanics import Body
  425. >>> A = Body('A')
  426. >>> B = Body('B')
  427. >>> A.frame.orient_axis(B.frame, B.frame.x, 5)
  428. >>> A.dcm(B)
  429. Matrix([
  430. [1, 0, 0],
  431. [0, cos(5), sin(5)],
  432. [0, -sin(5), cos(5)]])
  433. >>> A.dcm(B.frame)
  434. Matrix([
  435. [1, 0, 0],
  436. [0, cos(5), sin(5)],
  437. [0, -sin(5), cos(5)]])
  438. """
  439. if isinstance(body, ReferenceFrame):
  440. frame=body
  441. elif isinstance(body, Body):
  442. frame = body.frame
  443. return self.frame.dcm(frame)