Source code for gemseo.disciplines.ode.ode_function
# 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: Francois Gallard
# :author: Isabelle Santos
# :author: Giulio Gargantini
# OTHER AUTHORS - MACROSCOPIC CHANGES
"""ODE Function."""
from __future__ import annotations
from typing import TYPE_CHECKING
from numpy import array
from numpy import concatenate
from gemseo.core.mdo_functions.discipline_adapter_generator import (
DisciplineAdapterGenerator,
)
if TYPE_CHECKING:
from collections.abc import Iterable
from collections.abc import Mapping
from gemseo.core.discipline import Discipline
from gemseo.core.mdo_functions.discipline_adapter import DisciplineAdapter
from gemseo.typing import RealArray
[docs]
class ODEFunction:
"""A function wrapping a discipline for ODEs.
This function has time and state as arguments,
and an attribute 'terminal'.
"""
_adapter: DisciplineAdapter
"""The :class:`MDOFunction` wrapping the discipline."""
__parameter_names: tuple[str, ...]
"""The names of the parameters."""
terminal: bool
"""An attribute used by ODE libraries."""
def __init__(
self,
discipline: Discipline,
time_name: str,
state_names: Iterable[str] | Mapping[str, str],
output_names: Iterable[str] = (),
terminal: bool = False,
) -> None:
"""
Args:
discipline: The wrapped discipline.
time_name: The name of the time variable.
state_names: The names of the state variables.
output_names: The names of the output variables.
If empty,
use all ``discipline``'s output variables.
terminal: Whether this is a termination function.
""" # noqa: D205, D212, D415
self.terminal = False
excluded_names = [time_name, *state_names]
self.__parameter_names = tuple(
name
for name in discipline.io.input_grammar.defaults
if name not in excluded_names
)
generator = DisciplineAdapterGenerator(discipline=discipline)
if not output_names:
output_names = discipline.io.output_grammar
self._adapter = generator.get_function(
input_names=[time_name, *state_names],
output_names=output_names,
differentiated_input_names_substitute=[*state_names],
)
self.terminal = terminal
def __call__(self, time: RealArray, state: RealArray) -> RealArray:
"""
Args:
time: The time at the evaluation of the function.
state: The state of the ODE at the evaluation of the function.
Returns:
The value of the function at the given time and state.
""" # noqa: D205, D212, D415
return self._adapter.evaluate(concatenate((array([time]), state)))
[docs]
def evaluate_jacobian(self, time: RealArray, state: RealArray) -> RealArray:
"""
Args:
time: The time at the evaluation of the function.
state: The state of the ODE at the evaluation of the function.
Returns:
The Jacobian wrt state of the function at the given time and state.
""" # noqa: D205, D212, D415
return self._adapter.jac(concatenate((array([time]), state))).reshape((
state.size,
-1,
))