Source code for gemseo.core.mdo_scenario
# 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 - initial API and implementation and/or initial
# documentation
# :author: Francois Gallard
# OTHER AUTHORS - MACROSCOPIC CHANGES
# :author: Pierre-Jean Barjhoux, Benoit Pauwels - MDOScenarioAdapter
# Jacobian computation
"""A scenario whose driver is an optimization algorithm."""
from __future__ import annotations
import logging
from typing import Any
from typing import Sequence
from gemseo.algos.design_space import DesignSpace
from gemseo.algos.opt.opt_factory import OptimizersFactory
from gemseo.algos.opt_result import OptimizationResult
from gemseo.core.discipline import MDODiscipline
from gemseo.core.scenario import Scenario
# The detection of formulations requires to import them,
# before calling get_formulation_from_name
LOGGER = logging.getLogger(__name__)
[docs]class MDOScenario(Scenario):
"""A multidisciplinary scenario to be executed by an optimizer.
A :class:`.MDOScenario` is a particular :class:`.Scenario` whose driver is an
optimization algorithm. This algorithm must be implemented in an
:class:`.OptimizationLibrary`.
"""
clear_history_before_run: bool
"""If True, clear history before run."""
# Constants for input variables in json schema
MAX_ITER = "max_iter"
X_OPT = "x_opt"
def __init__(
self,
disciplines: Sequence[MDODiscipline],
formulation: str,
objective_name: str | Sequence[str],
design_space: DesignSpace,
name: str | None = None,
grammar_type: str = MDODiscipline.JSON_GRAMMAR_TYPE,
**formulation_options: Any,
) -> None:
"""
Args:
disciplines: The disciplines
used to compute the objective, constraints and observables
from the design variables.
formulation: The name of the MDO formulation,
also the name of a class inheriting from :class:`.MDOFormulation`.
objective_name: The name of the objective.
If a sequence is passed, a vector objective function is created.
design_space: The design space.
name: The name to be given to this scenario.
If None, use the name of the class.
grammar_type: The type of grammar to use for IO declaration
, e.g. JSON_GRAMMAR_TYPE or SIMPLE_GRAMMAR_TYPE.
**formulation_options: The options
to be passed to the :class:`.MDOFormulation`.
"""
# This loads the right json grammars from class name
super().__init__(
disciplines,
formulation,
objective_name,
design_space,
name=name,
grammar_type=grammar_type,
**formulation_options,
)
def _run_algorithm(self) -> OptimizationResult:
problem = self.formulation.opt_problem
algo_name = self.local_data[self.ALGO]
max_iter = self.local_data[self.MAX_ITER]
options = self.local_data.get(self.ALGO_OPTIONS)
if options is None:
options = {}
if self.MAX_ITER in options:
LOGGER.warning(
"Double definition of algorithm option max_iter, keeping value: %s",
max_iter,
)
options.pop(self.MAX_ITER)
# Store the lib in case we rerun the same algorithm,
# for multilevel scenarios for instance
# This significantly speedups the process also because
# of the option grammar that is long to create
if self._algo_name is not None and self._algo_name == algo_name:
lib = self._lib
else:
lib = self._algo_factory.create(algo_name)
self._lib = lib
self._algo_name = algo_name
self.optimization_result = lib.execute(
problem, algo_name=algo_name, max_iter=max_iter, **options
)
return self.optimization_result
def _init_algo_factory(self):
self._algo_factory = OptimizersFactory()
def _update_grammar_input(self) -> None:
self.input_grammar.update(dict(algo=str, max_iter=int, algo_options=dict))
self.input_grammar.required_names.remove("algo_options")