Spaces:
Sleeping
Sleeping
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})') | |
def point(self): | |
return self.location | |
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})') | |
def frame(self): | |
return self.location | |
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.') | |