Source code for gemseo.problems.ode.oscillator_discipline

# 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.
# Contributors:
#    INITIAL AUTHORS - API and implementation and/or documentation
#        :author: Isabelle Santos
#        :author: Giulio Gargantini
#    OTHER AUTHORS   - MACROSCOPIC CHANGES
r"""A harmonic oscillator.

In classical mechanics,
the position :math:`x` of a harmonic oscillator is described by the equation

.. math::

    \frac{d^2x}{dt^2} = -\omega^2 x


with :math:`\omega \in \mathbb{R}_+^*`.
This second-order Ordinary Differential Equation (ODE)
has an analytical solution:

.. math::

    x(t) = \lambda \sin(\omega t) + \mu \cos(\omega t)


where :math:`\lambda` and :math:`\mu` are two constants
defined by the initial conditions.

This solution can be re-written
as a two-dimensional first-order ODE:

.. math::

    \begin{cases}
    \frac{dx}{dt} = v, \\
    \frac{dv}{dt} = -\omega^2 x.
    \end{cases}

where :math:`v` represents the velocity of the oscillator.
"""

from __future__ import annotations

from typing import TYPE_CHECKING

from numpy import array
from numpy import ndarray

from gemseo.core.discipline.base_discipline import CacheType
from gemseo.disciplines.auto_py import AutoPyDiscipline
from gemseo.disciplines.ode.ode_discipline import ODEDiscipline
from gemseo.utils.constants import READ_ONLY_EMPTY_DICT

if TYPE_CHECKING:
    from collections.abc import Mapping

    from gemseo.typing import RealArray


_time = array([0.0])
_position = array([0.0])
_velocity = array([1.0])


[docs] class OscillatorDiscipline(ODEDiscipline): """A discipline representing a harmonic oscillator.""" __omega_squared: float """The squared angular velocity of the oscillator.""" def __init__( self, omega: float, times: RealArray, return_trajectories: bool = False, final_state_names: Mapping[str, str] = READ_ONLY_EMPTY_DICT, cache_inner_discipline_is_none=True, ): """ Args: omega: The positive angular velocity of the oscillator. """ # noqa: D205, D212, D415 self.__omega_squared = omega**2 rhs_discipline = AutoPyDiscipline(py_func=self._compute_rhs) if cache_inner_discipline_is_none: rhs_discipline.set_cache(cache_type=CacheType.NONE) super().__init__( times=times, state_names=("position", "velocity"), rhs_discipline=rhs_discipline, return_trajectories=return_trajectories, final_state_names=final_state_names, rtol=1e-12, atol=1e-12, ) def _compute_rhs( self, time: ndarray = _time, position: ndarray = _position, velocity: ndarray = _velocity, ) -> tuple[ndarray, ndarray]: """Compute the right-hand side of the ODE equation. Args: time: The value of the time. position: The position of the system at ``time``. velocity: The velocity of the system at ``time``. Returns: The derivative of the position at ``time``. The derivative of the velocity at ``time``. """ position_dot = velocity velocity_dot = -self.__omega_squared * position return position_dot, velocity_dot # noqa: RET504