mechmotum / symbrim

A Modular and Extensible Open-Source Framework for Creating Symbolic Bicycle-Rider Models
https://mechmotum.github.io/symbrim/
Creative Commons Zero v1.0 Universal
10 stars 3 forks source link

Rear and Front frame should not use NewtonianBodyMixin #100

Closed tjstienstra closed 1 year ago

tjstienstra commented 1 year ago

At the moment the rear and front frame use the NewtonianBodyMixin. However, this automatically makes them use only a single reference frame, while it is possible that a rotation occurs within the frame due to flexibility. Therefore it is better to use the same concept as always in the joints framework, namely a point and an interframe per joint. While this seems an easy fix, it would be ideal to create the pin joint in the WhippleBicycle just based on the interframe and the point. However, the joints framework does not yet the creation of a joint without using a body. Either a zero-weight body will have to be created or the joints framework should be updated. As the last would be the best option this issue consists out of three tasks:

tjstienstra commented 1 year ago
class MasslessBody(RigidBody):
    """Massless rigid body

    Explanation
    -----------
    The main purpose of this class is to allow the usage of just a frame and point
    when defining a joint. This class should only be used as long as sympy's joints
    framework does not yet support passing only a frame and point to the joint
    constructor.
    """

    def __init__(self, name: str, masscenter: Point, frame: ReferenceFrame) -> None:
        """Initialize a massless body."""
        super().__init__(
            name, masscenter, frame, S.Zero, Inertia(masscenter, Dyadic(0)))

    @RigidBody.mass.setter
    def mass(self, mass: Expr) -> None:
        """Body's mass."""
        if mass != 0:
            raise AttributeError("Massless bodies must have zero mass.")
        RigidBody.mass.fset(self, mass)

    @RigidBody.inertia.setter
    def inertia(self, inertia: Inertia) -> None:
        """Body's inertia about a point; stored as (Dyadic, Point)."""
        if inertia[0] != Dyadic(0):
            raise AttributeError("Massless bodies do not have inertia.")
        RigidBody.inertia.fset(self, inertia)

    @RigidBody.potential_energy.setter
    def potential_energy(self, potential_energy: Expr) -> None:
        """Potential energy of the body."""
        if potential_energy != 0:
            raise AttributeError("Massless bodies do not have potential energy.")
        RigidBody.potential_energy.fset(self, potential_energy)

    def linear_momentum(self, frame: ReferenceFrame) -> Vector:
        """Linear momentum of the body."""
        return Vector(0)

    def angular_momentum(self, point: Point, frame: ReferenceFrame) -> Vector:
        """Angular momentum of the body about a point in a frame."""
        return Vector(0)

    def kinetic_energy(self, frame: ReferenceFrame) -> Expr:
        """Kinetic energy of the body."""
        return S.Zero

class Attachment:
    """Joint attachment.

    Explanation
    -----------
    This class holds an immutable reference to a reference frame and a point to be used
    in SymPy's joints framework.
    """
    def __init__(self, frame: ReferenceFrame, point: Point) -> None:
        if not isinstance(frame, ReferenceFrame):
            raise TypeError("Frame must be a ReferenceFrame.")
        if not isinstance(point, Point):
            raise TypeError("Point must be a Point.")
        self._frame = frame
        self._point = point

    @property
    def frame(self) -> ReferenceFrame:
        """Reference frame of the attachment."""
        return self._frame

    @property
    def point(self) -> Point:
        """Point of the attachment."""
        return self._point

    def to_massless_body(self, name: str) -> MasslessBody:
        """Convert the attachment to a massless body.

        Notes
        -----
        This method is used to convert an attachment to a massless body. This is
        necessary because sympy's joints framework does not yet support passing only a
        frame and point to the joint constructor. Therefore, this method may be removed
        once this functionality is added to sympy.
        """
        return MasslessBody(name, self.point, self.frame)

    def __getitem__(self, item):
        return (self.frame, self.point)[item]

Just wrote up this simple class, but I'll probably not yet convert to using attachments and instead use a separate point and interframe property.