Source code for gemseo.problems.ode.van_der_pol
# 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"""The Van der Pol (VDP) problem describing an oscillator with non-linear damping.
Van der Pol, B. & Van Der Mark, J.
Frequency Demultiplication.
Nature 120, 363-364 (1927).
The Van der Pol problem is written as follows:
.. math::
\frac{d^2 x(t)}{dt^2} -
\mu (1-x(t)^2) \frac{dx(t)}{dt} + x = 0
where :math:`x(t)` is the position coordinate as a function of time, and
:math:`\mu` is a scalar parameter indicating the stiffness.
This problem can be rewritten in a 2-dimensional form with only first-order
derivatives. Let :math:`y = \frac{dx}{dt}` and
:math:`s = \begin{pmatrix}x\\y\end{pmatrix}`. Then the Van der Pol problem is:
.. math::
\frac{ds}{dt} = f(s, t)
with
.. math::
f : s = \begin{pmatrix} x \\ y \end{pmatrix} \mapsto
\begin{pmatrix} y \\ \mu (1-x^2) y - x \end{pmatrix}
The jacobian of this function can be expressed analytically:
.. math::
\mathrm{Jac}\, f = \begin{pmatrix}
0 & 1 \\
-2\mu xy - 1 & \mu (1 - x^2)
\end{pmatrix}
There is no exact solution to the Van der Pol oscillator problem in terms of
known tabulated functions (see Panayotounakos *et al.*, On the Lack of Analytic
Solutions of the Van Der Pol Oscillator. ZAMM 83, nᵒ 9 (1 septembre 2003)).
"""
from __future__ import annotations
from typing import TYPE_CHECKING
from numpy import array
from numpy import zeros
from gemseo.algos.ode.ode_problem import ODEProblem
if TYPE_CHECKING:
from numpy._typing import ArrayLike
from gemseo.typing import RealArray
[docs]
class VanDerPol(ODEProblem):
"""Representation of an oscillator with non-linear damping."""
__mu: float
r"""The stiffness parameter.
Van der Pol is stiffer with larger values of :math:`\mu`.
"""
def __init__(
self,
mu: float = 1.0e3,
use_jacobian: bool = True,
state: tuple[float, float] = (0.0, 0.0),
times: ArrayLike = (0.0, 0.5),
) -> None:
"""
Args:
mu: The stiffness parameter.
use_jacobian: Whether to use the analytical expression of the Jacobian.
If ``False``, use finite differences to estimate the Jacobian.
state: The state vector of the system.
times: The initial and final times_eval.
""" # noqa: D205 D212
self.__mu = mu
initial_state = array([
2 + state[0],
-2 / 3
+ 10 / (81 * self.__mu)
- 292 / (2187 * self.__mu * self.__mu)
+ state[1],
])
super().__init__(
func=self._func,
jac_function_wrt_state=self._jac_wrt_state if use_jacobian else None,
initial_state=initial_state,
times=times,
solve_at_algorithm_times=True,
)
def _func(self, time: float, state: RealArray) -> RealArray:
"""Compute the right-hand side of the ODE.
Args:
time: The time at which the function is evaluated.
state: The state in which the function is evaluated.
Returns:
The value of the right-hand side of the ODE.
"""
return array([state[1], self.__mu * state[1] * (1 - state[0] ** 2) - state[0]])
def _jac_wrt_state(self, time: float, state: RealArray) -> RealArray:
"""Compute the Jacobian of the right-hand side of the ODE.
Args:
time: The time at which the function is evaluated.
state: The state in which the function is evaluated.
Returns:
The Jacobian of the right-hand side of the ODE.
"""
jac = zeros((2, 2))
jac[1, 0] = -self.__mu * 2 * state[1] * state[0] - 1
jac[0, 1] = 1
jac[1, 1] = self.__mu * (1 - state[0] * state[0])
return jac