Source code for gemseo.disciplines.ode.base_functor

# 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
"""A discipline for solving ordinary differential equations (ODEs)."""

from __future__ import annotations

from collections.abc import Mapping
from typing import TYPE_CHECKING

from numpy import array
from numpy import concatenate

from gemseo.core.mdo_functions.discipline_adapter_generator import (
    DisciplineAdapterGenerator,
)
from gemseo.utils.data_conversion import concatenate_dict_of_arrays_to_array

if TYPE_CHECKING:
    from collections.abc import Iterable

    from gemseo.core.discipline import Discipline
    from gemseo.core.mdo_functions.discipline_adapter import DisciplineAdapter
    from gemseo.disciplines.ode.ode_discipline import ODEDiscipline
    from gemseo.typing import RealArray


[docs] class BaseFunctor: """A function wrapping a RHS discipline. This function has time and state as arguments, and an attribute 'terminal'. Its subclasses are used to evaluate the outputs of the RHS discipline or its Jacobian. """ _adapter: DisciplineAdapter """The :class:`MDOFunction` wrapping the discipline defining the dynamic.""" __parameter_names: tuple[str, ...] """The names of the parameters.""" __ode_discipline: ODEDiscipline """The ODE discipline providing the local data.""" terminal: bool """An attribute used by ODE libraries.""" def __init__( self, ode_discipline: ODEDiscipline, discipline: Discipline, state_names: Iterable[str] | Mapping[str, str], time_name: str, ) -> None: """ Args: ode_discipline: The ODE discipline providing the local data. discipline: The wrapped discipline defining the dynamic. state_names: Either the names of the state variables, passed as ``(state_name, ...)``, or the names of the state variables bound to the associated discipline outputs, passed as ``{state_name: output_name, ...}``. time_name: The name of the time variables. """ # noqa: D205, D212, D415 self.terminal = False self.__ode_discipline = ode_discipline excluded_names = [time_name, *state_names] self.__parameter_names = tuple( name for name in discipline.default_input_data if name not in excluded_names ) generator = DisciplineAdapterGenerator(discipline=discipline) if isinstance(state_names, Mapping): output_names = state_names.values() state_names = tuple(state_names.keys()) else: output_names = discipline.io.output_grammar.names self._adapter = generator.get_function( input_names=[ time_name, *state_names, *self.__parameter_names, ], output_names=output_names, ) def _compute_input_vector(self, time: RealArray, state: RealArray) -> RealArray: """Compute an input vector concatenating time, state and parameters. Args: time: The current time. state: The current state. Returns: The concatenation of time, state and parameters. """ return concatenate(( array([time]), state, concatenate_dict_of_arrays_to_array( self.__ode_discipline.local_data, names=self.__parameter_names, ), ))