loads.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. from abc import ABC
  2. from collections import namedtuple
  3. from sympy.physics.mechanics.body_base import BodyBase
  4. from sympy.physics.vector import Vector, ReferenceFrame, Point
  5. __all__ = ['LoadBase', 'Force', 'Torque']
  6. class LoadBase(ABC, namedtuple('LoadBase', ['location', 'vector'])):
  7. """Abstract base class for the various loading types."""
  8. def __add__(self, other):
  9. raise TypeError(f"unsupported operand type(s) for +: "
  10. f"'{self.__class__.__name__}' and "
  11. f"'{other.__class__.__name__}'")
  12. def __mul__(self, other):
  13. raise TypeError(f"unsupported operand type(s) for *: "
  14. f"'{self.__class__.__name__}' and "
  15. f"'{other.__class__.__name__}'")
  16. __radd__ = __add__
  17. __rmul__ = __mul__
  18. class Force(LoadBase):
  19. """Force acting upon a point.
  20. Explanation
  21. ===========
  22. A force is a vector that is bound to a line of action. This class stores
  23. both a point, which lies on the line of action, and the vector. A tuple can
  24. also be used, with the location as the first entry and the vector as second
  25. entry.
  26. Examples
  27. ========
  28. A force of magnitude 2 along N.x acting on a point Po can be created as
  29. follows:
  30. >>> from sympy.physics.mechanics import Point, ReferenceFrame, Force
  31. >>> N = ReferenceFrame('N')
  32. >>> Po = Point('Po')
  33. >>> Force(Po, 2 * N.x)
  34. (Po, 2*N.x)
  35. If a body is supplied, then the center of mass of that body is used.
  36. >>> from sympy.physics.mechanics import Particle
  37. >>> P = Particle('P', point=Po)
  38. >>> Force(P, 2 * N.x)
  39. (Po, 2*N.x)
  40. """
  41. def __new__(cls, point, force):
  42. if isinstance(point, BodyBase):
  43. point = point.masscenter
  44. if not isinstance(point, Point):
  45. raise TypeError('Force location should be a Point.')
  46. if not isinstance(force, Vector):
  47. raise TypeError('Force vector should be a Vector.')
  48. return super().__new__(cls, point, force)
  49. def __repr__(self):
  50. return (f'{self.__class__.__name__}(point={self.point}, '
  51. f'force={self.force})')
  52. @property
  53. def point(self):
  54. return self.location
  55. @property
  56. def force(self):
  57. return self.vector
  58. class Torque(LoadBase):
  59. """Torque acting upon a frame.
  60. Explanation
  61. ===========
  62. A torque is a free vector that is acting on a reference frame, which is
  63. associated with a rigid body. This class stores both the frame and the
  64. vector. A tuple can also be used, with the location as the first item and
  65. the vector as second item.
  66. Examples
  67. ========
  68. A torque of magnitude 2 about N.x acting on a frame N can be created as
  69. follows:
  70. >>> from sympy.physics.mechanics import ReferenceFrame, Torque
  71. >>> N = ReferenceFrame('N')
  72. >>> Torque(N, 2 * N.x)
  73. (N, 2*N.x)
  74. If a body is supplied, then the frame fixed to that body is used.
  75. >>> from sympy.physics.mechanics import RigidBody
  76. >>> rb = RigidBody('rb', frame=N)
  77. >>> Torque(rb, 2 * N.x)
  78. (N, 2*N.x)
  79. """
  80. def __new__(cls, frame, torque):
  81. if isinstance(frame, BodyBase):
  82. frame = frame.frame
  83. if not isinstance(frame, ReferenceFrame):
  84. raise TypeError('Torque location should be a ReferenceFrame.')
  85. if not isinstance(torque, Vector):
  86. raise TypeError('Torque vector should be a Vector.')
  87. return super().__new__(cls, frame, torque)
  88. def __repr__(self):
  89. return (f'{self.__class__.__name__}(frame={self.frame}, '
  90. f'torque={self.torque})')
  91. @property
  92. def frame(self):
  93. return self.location
  94. @property
  95. def torque(self):
  96. return self.vector
  97. def gravity(acceleration, *bodies):
  98. """
  99. Returns a list of gravity forces given the acceleration
  100. due to gravity and any number of particles or rigidbodies.
  101. Example
  102. =======
  103. >>> from sympy.physics.mechanics import ReferenceFrame, Particle, RigidBody
  104. >>> from sympy.physics.mechanics.loads import gravity
  105. >>> from sympy import symbols
  106. >>> N = ReferenceFrame('N')
  107. >>> g = symbols('g')
  108. >>> P = Particle('P')
  109. >>> B = RigidBody('B')
  110. >>> gravity(g*N.y, P, B)
  111. [(P_masscenter, P_mass*g*N.y),
  112. (B_masscenter, B_mass*g*N.y)]
  113. """
  114. gravity_force = []
  115. for body in bodies:
  116. if not isinstance(body, BodyBase):
  117. raise TypeError(f'{type(body)} is not a body type')
  118. gravity_force.append(Force(body.masscenter, body.mass * acceleration))
  119. return gravity_force
  120. def _parse_load(load):
  121. """Helper function to parse loads and convert tuples to load objects."""
  122. if isinstance(load, LoadBase):
  123. return load
  124. elif isinstance(load, tuple):
  125. if len(load) != 2:
  126. raise ValueError(f'Load {load} should have a length of 2.')
  127. if isinstance(load[0], Point):
  128. return Force(load[0], load[1])
  129. elif isinstance(load[0], ReferenceFrame):
  130. return Torque(load[0], load[1])
  131. else:
  132. raise ValueError(f'Load not recognized. The load location {load[0]}'
  133. f' should either be a Point or a ReferenceFrame.')
  134. raise TypeError(f'Load type {type(load)} not recognized as a load. It '
  135. f'should be a Force, Torque or tuple.')