Source code for gemseo.core.mdofunctions.function_from_discipline

# -*- coding: utf-8 -*-
# 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
#    OTHER AUTHORS   - MACROSCOPIC CHANGES
#        :author: Benoit Pauwels - Stacked data management
#               (e.g. iteration index)
#        :author: Gilberto Ruiz Jimenez
"""The MDOFunction subclass to create a function from an MDODiscipline."""
from __future__ import division, unicode_literals

import logging
from typing import TYPE_CHECKING, Iterable, Optional, Sequence

from numpy import empty, ndarray

from gemseo.core.mdofunctions.function_generator import MDOFunctionGenerator
from gemseo.core.mdofunctions.mdo_function import MDOFunction

if TYPE_CHECKING:
    from gemseo.core.discipline import MDODiscipline
    from gemseo.core.formulation import MDOFormulation

LOGGER = logging.getLogger(__name__)


[docs]class FunctionFromDiscipline(MDOFunction): """An :class:`.MDOFunction` object from an :class:`.MDODiscipline.""" def __init__( self, output_names, # type: Sequence[str] mdo_formulation, # type: MDOFormulation discipline=None, # type: Optional[MDODiscipline] top_level_disc=True, # type:bool x_names=None, # type: Optional[Sequence[str]] all_data_names=None, # type:Optional[Iterable[str]] differentiable=True, # type: bool ): # type: (...) -> None """ Args: output_names: The names of the outputs. mdo_formulation: The MDOFormulation object in which the function is located. discipline: The discipline computing these outputs. If None, the discipline is detected from the inner disciplines. top_level_disc: If True, search the discipline among the top level ones. x_names: The names of the design variables. If None, use self.get_x_names_of_disc(discipline). all_data_names: The reference data names for masking x. If None, use self.get_optim_variables_names(). differentiable: If True, then inputs and outputs are added to the list of variables to be differentiated. """ self.__output_names = output_names self.__mdo_formulation = mdo_formulation self.__discipline = discipline self.__top_level_disc = top_level_disc self.__x_names = x_names self.__all_data_names = all_data_names self.__differentiable = differentiable if self.__discipline is None: self.__gen = self.__mdo_formulation._get_generator_from( self.__output_names, top_level_disc=self.__top_level_disc ) self.__discipline = self.__gen.discipline else: self.__gen = MDOFunctionGenerator(self.__discipline) if self.__x_names is None: self.__x_names = self.__mdo_formulation.get_x_names_of_disc( self.__discipline ) self.__out_x_func = self.__gen.get_function( self.__x_names, self.__output_names, differentiable=self.__differentiable ) super(FunctionFromDiscipline, self).__init__( self._func, jac=self._func_jac, name=self.__out_x_func.name, f_type=MDOFunction.TYPE_OBJ, args=self.__x_names, expr=self.__out_x_func.expr, dim=self.__out_x_func.dim, outvars=self.__out_x_func.outvars, ) def _func( self, x_vect, # type: ndarray ): # type: (...) -> ndarray """Compute the outputs. Args: x_vect: The design variable vector. Returns: The value of the outputs. """ x_of_disc = self.__mdo_formulation.mask_x_swap_order( self.__x_names, x_vect, self.__all_data_names ) obj_allx_val = self.__out_x_func(x_of_disc) return obj_allx_val def _func_jac( self, x_vect # type: ndarray ): # type: (...) -> ndarray """Compute the gradient of the outputs. Args: x_vect: The design variable vector. Returns: The value of the gradient of the outputs. """ x_of_disc = self.__mdo_formulation.mask_x_swap_order( self.__x_names, x_vect, self.__all_data_names ) loc_jac = self.__out_x_func.jac(x_of_disc) # pylint: disable=E1102 if len(loc_jac.shape) == 1: # This is surprising but there is a duality between the # masking operation in the function inputs and the # unmasking of its outputs jac = self.__mdo_formulation.unmask_x_swap_order( self.__x_names, loc_jac, self.__all_data_names ) else: n_outs = loc_jac.shape[0] jac = empty((n_outs, x_vect.size), dtype=x_vect.dtype) for func_ind in range(n_outs): gr_u = self.__mdo_formulation.unmask_x_swap_order( self.__x_names, loc_jac[func_ind, :], self.__all_data_names ) jac[func_ind, :] = gr_u return jac