Source code for gemseo_umdo.use_cases.spring_mass_model.model

# Copyright 2021 IRT Saint Exupéry, https://www.irt-saintexupery.com
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License version 3 as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
"""The GEMSEO-free spring-mass model."""
from __future__ import annotations

from typing import Sequence

from numpy import arange
from numpy.typing import NDArray
from scipy.integrate import odeint


[docs]class SpringMassModel: r"""The GEMSEO-free spring-mass model. This model computes the time displacement of an object attached to a spring in function of the stiffness of the spring. It computes also its maximum displacement. The ordinary differential equation is $$m\frac{d^2z(t)}{dt^2} = -kz(t) + mg$$ with $\left.\frac{dz(t)}{dt}\right|_{t=0}=z(0)=z_0$. """ def __init__( self, mass: float = 1.5, initial_state: tuple[float, float] = (0, 0), initial_time: float = 0.0, final_time: float = 10.0, time_step: float = 0.1, gravity: float = 9.8, ) -> None: """ Args: mass: The mass of the object. initial_state: The initial position and velocity of the object. initial_time: The initial time. final_time: The final time. time_step: The time step. gravity: The gravity acceleration. """ # noqa: D205 D212 D415 self.__mass = mass self.__gravity = gravity self.__initial_state = initial_state self.__time = arange(initial_time, final_time, time_step) self.__cost = 1.0 / time_step @property def cost(self) -> float: """The evaluation cost.""" return self.__cost def __call__(self, stiffness: float = 2.25) -> tuple[NDArray[float], float]: """Compute the displacement of the object w.r.t. the stiffness of the spring. Args: stiffness: The stiffness of the spring. Returns: The displacement of the object at the different times, as well as its maximum displacement. """ displacements = odeint( self.__integration_func, self.__initial_state, self.__time, args=(stiffness, self.__mass, self.__gravity), )[:, 0] return (displacements, max(displacements)) @staticmethod def __integration_func( state: Sequence[float], t: float, k: float, m: float, g: float ) -> list[float, float]: """Compute the derivative of the state (velocity/acceleration) at a given time. Args: state: The velocity and acceleration of the object. t: The time. k: The stiffness of the spring. m: The mass of the object. g: The gravity acceleration. Returns: The derivative of the velocity, the derivative of the acceleration. """ return [state[1], -k * state[0] / m + g]