from abc import ABC from collections import namedtuple from sympy.physics.mechanics.body_base import BodyBase from sympy.physics.vector import Vector, ReferenceFrame, Point __all__ = ['LoadBase', 'Force', 'Torque'] class LoadBase(ABC, namedtuple('LoadBase', ['location', 'vector'])): """Abstract base class for the various loading types.""" def __add__(self, other): raise TypeError(f"unsupported operand type(s) for +: " f"'{self.__class__.__name__}' and " f"'{other.__class__.__name__}'") def __mul__(self, other): raise TypeError(f"unsupported operand type(s) for *: " f"'{self.__class__.__name__}' and " f"'{other.__class__.__name__}'") __radd__ = __add__ __rmul__ = __mul__ class Force(LoadBase): """Force acting upon a point. Explanation =========== A force is a vector that is bound to a line of action. This class stores both a point, which lies on the line of action, and the vector. A tuple can also be used, with the location as the first entry and the vector as second entry. Examples ======== A force of magnitude 2 along N.x acting on a point Po can be created as follows: >>> from sympy.physics.mechanics import Point, ReferenceFrame, Force >>> N = ReferenceFrame('N') >>> Po = Point('Po') >>> Force(Po, 2 * N.x) (Po, 2*N.x) If a body is supplied, then the center of mass of that body is used. >>> from sympy.physics.mechanics import Particle >>> P = Particle('P', point=Po) >>> Force(P, 2 * N.x) (Po, 2*N.x) """ def __new__(cls, point, force): if isinstance(point, BodyBase): point = point.masscenter if not isinstance(point, Point): raise TypeError('Force location should be a Point.') if not isinstance(force, Vector): raise TypeError('Force vector should be a Vector.') return super().__new__(cls, point, force) def __repr__(self): return (f'{self.__class__.__name__}(point={self.point}, ' f'force={self.force})') @property def point(self): return self.location @property def force(self): return self.vector class Torque(LoadBase): """Torque acting upon a frame. Explanation =========== A torque is a free vector that is acting on a reference frame, which is associated with a rigid body. This class stores both the frame and the vector. A tuple can also be used, with the location as the first item and the vector as second item. Examples ======== A torque of magnitude 2 about N.x acting on a frame N can be created as follows: >>> from sympy.physics.mechanics import ReferenceFrame, Torque >>> N = ReferenceFrame('N') >>> Torque(N, 2 * N.x) (N, 2*N.x) If a body is supplied, then the frame fixed to that body is used. >>> from sympy.physics.mechanics import RigidBody >>> rb = RigidBody('rb', frame=N) >>> Torque(rb, 2 * N.x) (N, 2*N.x) """ def __new__(cls, frame, torque): if isinstance(frame, BodyBase): frame = frame.frame if not isinstance(frame, ReferenceFrame): raise TypeError('Torque location should be a ReferenceFrame.') if not isinstance(torque, Vector): raise TypeError('Torque vector should be a Vector.') return super().__new__(cls, frame, torque) def __repr__(self): return (f'{self.__class__.__name__}(frame={self.frame}, ' f'torque={self.torque})') @property def frame(self): return self.location @property def torque(self): return self.vector def gravity(acceleration, *bodies): """ Returns a list of gravity forces given the acceleration due to gravity and any number of particles or rigidbodies. Example ======= >>> from sympy.physics.mechanics import ReferenceFrame, Particle, RigidBody >>> from sympy.physics.mechanics.loads import gravity >>> from sympy import symbols >>> N = ReferenceFrame('N') >>> g = symbols('g') >>> P = Particle('P') >>> B = RigidBody('B') >>> gravity(g*N.y, P, B) [(P_masscenter, P_mass*g*N.y), (B_masscenter, B_mass*g*N.y)] """ gravity_force = [] for body in bodies: if not isinstance(body, BodyBase): raise TypeError(f'{type(body)} is not a body type') gravity_force.append(Force(body.masscenter, body.mass * acceleration)) return gravity_force def _parse_load(load): """Helper function to parse loads and convert tuples to load objects.""" if isinstance(load, LoadBase): return load elif isinstance(load, tuple): if len(load) != 2: raise ValueError(f'Load {load} should have a length of 2.') if isinstance(load[0], Point): return Force(load[0], load[1]) elif isinstance(load[0], ReferenceFrame): return Torque(load[0], load[1]) else: raise ValueError(f'Load not recognized. The load location {load[0]}' f' should either be a Point or a ReferenceFrame.') raise TypeError(f'Load type {type(load)} not recognized as a load. It ' f'should be a Force, Torque or tuple.')