Repository: sympy/sympy
File: sympy/physics/continuum_mechanics/beam.py (Python)

Code before:
        2 meters from the starting point and a parabolic ramp load of magnitude
        2 N/m is applied below the beam starting from 2 meters to 3 meters
        away from the starting point of the beam.

        >>> from sympy.physics.continuum_mechanics.beam import Beam
        >>> from sympy import symbols
        >>> E, I = symbols('E, I')
        >>> b = Beam(4, E, I)
        >>> b.apply_load(-3, 0, -2)
        >>> b.apply_load(4, 2, -1)
        >>> b.apply_load(-2, 2, 2, end=3)
        >>> b.load
        -3*SingularityFunction(x, 0, -2) + 4*SingularityFunction(x, 2, -1) - 2*SingularityFunction(x, 2, 2) + 2*SingularityFunction(x, 3, 0) + 4*SingularityFunction(x, 3, 1) + 2*SingularityFunction(x, 3, 2)

        """
        x = self.variable
        value = sympify(value)
        start = sympify(start)
        order = sympify(order)

        self._applied_loads.append((value, start, order, end))
        self._load += value*SingularityFunction(x, start, order)
        self._original_load += value*SingularityFunction(x, start, order)

        if end!=None:
            if start - end>0:
                # If load is applied in reverse (start > end), reverse the effect by applying a negative load at the end.
                self._handle_end(x, -value, start, order, end,type = "remove")
            # load has an end point within the length of the beam.
            else:
                self._handle_end(x, value, start, order, end, type="apply")

    def remove_load(self, value, start, order, end=None):
        """
        This method removes a particular load present on the beam object.
        Returns a ValueError if the load passed as an argument is not
        present on the beam.

        Parameters
        ==========
        value : Sympifyable
            The magnitude of an applied load.
        start : Sympifyable
            The starting point of the applied load. For point moments and
            point forces this is the location of application.
        order : Integer
            The order of the applied load.
            - For moments, order= -2
            - For point loads, order=-1
            - For constant distributed load, order=0
            - For ramp loads, order=1

Review comment (bug):
If `start` or `end` are expressions potentially involving symbols or just complicated numeric expressions then this check can fail:
```python
In [2]: start = cos(1)**2 + sin(1)**2

In [3]: end = 1

In [4]: start - end
Out[4]: 
        2         2   
-1 + cos (1) + sin (1)

In [5]: start - end > 0
Out[5]: 
        2         2       
-1 + cos (1) + sin (1) > 0

In [6]: if start - end > 0:
   ...:     print('greater')
   ...: 
---------------------------------------------------------------------------
...
TypeError: cannot determine truth value of Relational: -1 + cos(1)**2 + sin(1)**2 > 0
```

Code after:

               - For moments, order = -2
               - For point loads, order =-1
               - For constant distributed load, order = 0
               - For ramp loads, order = 1
               - For parabolic ramp loads, order = 2
               - ... so on.

        end : Sympifyable, optional
            An optional argument that can be used if the load has an end point
            within the length of the beam.

        Examples
        ========
        There is a beam of length 4 meters. A moment of magnitude 3 Nm is
        applied in the clockwise direction at the starting point of the beam.
        A point load of magnitude 4 N is applied from the top of the beam at
        2 meters from the starting point and a parabolic ramp load of magnitude
        2 N/m is applied below the beam starting from 2 meters to 3 meters
        away from the starting point of the beam.

        >>> from sympy.physics.continuum_mechanics.beam import Beam
        >>> from sympy import symbols
        >>> E, I = symbols('E, I')
        >>> b = Beam(4, E, I)
        >>> b.apply_load(-3, 0, -2)
        >>> b.apply_load(4, 2, -1)
        >>> b.apply_load(-2, 2, 2, end=3)
        >>> b.load
        -3*SingularityFunction(x, 0, -2) + 4*SingularityFunction(x, 2, -1) - 2*SingularityFunction(x, 2, 2) + 2*SingularityFunction(x, 3, 0) + 4*SingularityFunction(x, 3, 1) + 2*SingularityFunction(x, 3, 2)

        """
        x = self.variable
        value = sympify(value)
        start = sympify(start)

        self._applied_loads.append((value, start, order, end))
        self._load += value*SingularityFunction(x, start, order)
        self._original_load += value*SingularityFunction(x, start, order)

        if end is not None:
            if order < 0:
                msg = ("If 'end' is provided the 'order' of the load cannot "
                       "be negative, i.e. 'end' is only valid for distributed loads.")
                raise ValueError(msg)

            f = value * (x - start)**order
            for i in range(0, order + 1):
                coeff = f.diff(x, i).subs(x, end)
                term = coeff * SingularityFunction(x, end, i) / factorial(i)
                self._load -= term