Source code for gemseo.formulations.mdf
# 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
"""The Multi-disciplinary Design Feasible (MDF) formulation."""
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import Any
from typing import ClassVar
from gemseo.formulations.base_mdo_formulation import BaseMDOFormulation
from gemseo.formulations.mdf_settings import MDF_Settings
from gemseo.mda.factory import MDAFactory
if TYPE_CHECKING:
from collections.abc import Sequence
from gemseo.algos.design_space import DesignSpace
from gemseo.core.discipline import Discipline
from gemseo.core.grammars.json_grammar import JSONGrammar
from gemseo.mda.base_mda import BaseMDA
from gemseo.typing import StrKeyMapping
[docs]
class MDF(BaseMDOFormulation):
"""The Multidisciplinary Design Feasible (MDF) formulation.
This formulation draws an optimization architecture
where:
- the coupling of strongly coupled disciplines is made consistent
by means of a Multidisciplinary Design Analysis (MDA),
- the optimization problem
with respect to the local and global design variables is made at the top level.
Note that the multidisciplinary analysis is made at each optimization iteration.
"""
mda: BaseMDA
"""The MDA used in the formulation."""
Settings: ClassVar[type[MDF_Settings]] = MDF_Settings
_settings: MDF_Settings
__mda_factory: ClassVar[MDAFactory] = MDAFactory()
"""The MDA factory."""
def __init__( # noqa: D107
self,
disciplines: Sequence[Discipline],
objective_name: str,
design_space: DesignSpace,
settings_model: MDF_Settings | None = None,
**settings: Any,
) -> None:
super().__init__(
disciplines,
objective_name,
design_space,
settings_model=settings_model,
**settings,
)
self.mda = self.__mda_factory.create(
self._settings.main_mda_settings._TARGET_CLASS_NAME,
self.disciplines,
settings_model=self._settings.main_mda_settings,
)
self._update_design_space()
self._build_objective_from_disc(objective_name, discipline=self.mda)
[docs]
def get_top_level_disciplines( # noqa:D102
self, include_sub_formulations: bool = False
) -> tuple[Discipline, ...]:
return (self.mda,)
[docs]
@classmethod
def get_sub_options_grammar(cls, **options: str) -> JSONGrammar: # noqa:D102
return cls.__mda_factory.get_options_grammar(cls.__check_mda(**options))
[docs]
@classmethod
def get_default_sub_option_values(cls, **options: str) -> StrKeyMapping: # noqa:D102
return cls.__mda_factory.get_default_option_values(cls.__check_mda(**options))
@staticmethod
def __check_mda(**options: str) -> str:
"""Check that main_mda_name is available.
Args:
options: The options.
Returns:
The main MDA.
Raises:
ValueError: When main_mda_name is not available.
"""
main_mda_name = options.get("main_mda_name")
if main_mda_name is None:
msg = "main_mda_name option required to deduce the sub options of MDF."
raise ValueError(msg)
return main_mda_name
def _update_design_space(self) -> None:
"""Update the design space by removing the coupling variables."""
self._set_default_input_values_from_design_space()
# No couplings in design space (managed by MDA)
self._remove_couplings_from_ds()
# Cleanup
self._remove_unused_variables()
def _remove_couplings_from_ds(self) -> None:
"""Remove the coupling variables from the design space."""
design_space = self.optimization_problem.design_space
for coupling in self.mda.coupling_structure.all_couplings:
if coupling in design_space:
design_space.remove_variable(coupling)