diff --git a/doc_src/_examples/formulations/plot_sobieski_bilevel_bcd_example.py b/doc_src/_examples/formulations/plot_sobieski_bilevel_bcd_example.py new file mode 100644 index 0000000000000000000000000000000000000000..d2e42a4c78799e25cb5cba3f1a0d134ea47ca439 --- /dev/null +++ b/doc_src/_examples/formulations/plot_sobieski_bilevel_bcd_example.py @@ -0,0 +1,226 @@ +# Copyright 2021 IRT Saint Exupéry, https://www.irt-saintexupery.com +# +# This work is licensed under a BSD 0-Clause License. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, +# OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING +# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# Contributors: +# INITIAL AUTHORS - API and implementation and/or documentation +# :author: Fabian Castañeda +# OTHER AUTHORS - MACROSCOPIC CHANGES +""" +BiLevel BCD-based MDO on the Sobieski SSBJ test case +================================================ +""" + +# %% +# .. note:: +# +# There are several variants of the BiLevel BCD formulation; this example shows +# the implementation of the BiLevel BCD MDF (BL-BCD-MDF) for other variants please +# refer to :cite:`david:hal-04758286`. + +from __future__ import annotations + +from gemseo import configure_logger +from gemseo.algos.opt.nlopt.settings.nlopt_cobyla_settings import NLOPT_COBYLA_Settings +from gemseo.algos.opt.scipy_local.settings.slsqp import SLSQP_Settings +from gemseo.formulations.bilevel_bcd_settings import BiLevel_BCD_Settings +from gemseo.formulations.mdf_settings import MDF_Settings +from gemseo.mda.gauss_seidel_settings import MDAGaussSeidel_Settings +from gemseo.problems.mdo.sobieski.core.design_space import SobieskiDesignSpace +from gemseo.problems.mdo.sobieski.disciplines import SobieskiAerodynamics +from gemseo.problems.mdo.sobieski.disciplines import SobieskiMission +from gemseo.problems.mdo.sobieski.disciplines import SobieskiPropulsion +from gemseo.problems.mdo.sobieski.disciplines import SobieskiStructure +from gemseo.scenarios.mdo_scenario import MDOScenario + +configure_logger() + +# %% +# Instantiate the disciplines +# ---------------------------- +# First, we instantiate the four disciplines of the use case: +# :class:`.SobieskiPropulsion`, +# :class:`.SobieskiAerodynamics`, +# :class:`.SobieskiMission` +# and :class:`.SobieskiStructure`. +propulsion_disc = SobieskiPropulsion() +aerodynamics_disc = SobieskiAerodynamics() +structure_disc = SobieskiStructure() +mission_disc = SobieskiMission() + +# %% +# Since they are going to be our disciplines for the sub-scenarios, we'll call them +# sub-disciplines. + +sub_disciplines = [structure_disc, propulsion_disc, aerodynamics_disc, mission_disc] + +# %% +# Build the scenario +# ---------------------- +# We build the scenario that allows to create the optimization problem from +# the disciplines and the formulation. +# Here, we use the :class:`.BiLevelBCD` formulation. +# We need to define the design space. + +design_space = SobieskiDesignSpace() +# %% +# For this formulation, we need to define the optimization sub-scenarios from +# all sub-disciplines coupled together. Each sub-scenario optimizes its own design +# variable according to the corresponding constraint and the objective y_4 (range) +# which we are maximizing. + +# %% +# Define Sub-scenario settings model +# -------------------------------------- +# The setting for all sub-scenarios is the same, so we can define a global, settings +# model to be used by each sub-scenario. + +sub_scenario_settings = MDF_Settings( + main_mda_name="MDAGaussSeidel", +) +sc_algo_settings = SLSQP_Settings(max_iter=50) + +# %% +# Build the Propulsion Sub-scenario +# ----------------------------------- +# This sub-scenario will optimize the propulsion's discipline design variable x_3 under +# the constraint g_3. + +propulsion_sc = MDOScenario( + sub_disciplines, + "y_4", + design_space.filter(["x_3"], copy=True), + formulation_settings_model=sub_scenario_settings, + maximize_objective=True, + name="PropulsionScenario", +) +propulsion_sc.set_algorithm(algo_settings_model=sc_algo_settings) +propulsion_sc.formulation.optimization_problem.objective *= 0.001 +propulsion_sc.add_constraint("g_3", constraint_type="ineq") + +# %% +# Build the Aerodynamics Sub-scenario +# ----------------------------------- +# This sub-scenario will optimize the aerodynamics' discipline design variable x_2 under +# the constraint g_2. + +aerodynamics_sc = MDOScenario( + sub_disciplines, + "y_4", + design_space.filter(["x_2"], copy=True), + formulation_settings_model=sub_scenario_settings, + maximize_objective=True, + name="AerodynamicsScenario", +) +aerodynamics_sc.set_algorithm(algo_settings_model=sc_algo_settings) +aerodynamics_sc.formulation.optimization_problem.objective *= 0.001 +aerodynamics_sc.add_constraint("g_2", constraint_type="ineq") + +# %% +# Build the Structure Sub-scenario +# ----------------------------------- +# This sub-scenario will optimize the structure's discipline design variable x_1 under +# the constraint g_1. + +structure_sc = MDOScenario( + sub_disciplines, + "y_4", + design_space.filter(["x_1"], copy=True), + formulation_settings_model=sub_scenario_settings, + maximize_objective=True, + name="StructureScenario", +) +structure_sc.set_algorithm(algo_settings_model=sc_algo_settings) +structure_sc.formulation.optimization_problem.objective *= 0.001 +structure_sc.add_constraint("g_1", constraint_type="ineq") + +# %% +# System's Scenario Settings +# --------------------------- +# The BiLevel BCD formulation allows to independently define the settings +# for the BCD MDA, such as shown below. + +bcd_mda_settings = MDAGaussSeidel_Settings(tolerance=1e-5, max_mda_iter=10) +system_settings = BiLevel_BCD_Settings( + bcd_mda_settings=bcd_mda_settings, +) +# Just like for the sub-scenario, we define the algorithm settings for the +# system scenario. + +system_sc_algo_settings = NLOPT_COBYLA_Settings(max_iter=100) + +# %% +# Build the System's Scenario +# ----------------------------------- +# The system level scenario is based on the three previous sub-scenarios for which we aim to maximize the range. + +sub_scenarios = [propulsion_sc, aerodynamics_sc, structure_sc, mission_disc] + +system_scenario = MDOScenario( + sub_scenarios, + "y_4", + design_space.filter(["x_shared"], copy=True), + formulation_settings_model=system_settings, + maximize_objective=True, +) +system_scenario.formulation.optimization_problem.objective *= 0.001 +system_scenario.set_algorithm(algo_settings_model=system_sc_algo_settings) +system_scenario.add_constraint("g_1", constraint_type="ineq") +system_scenario.add_constraint("g_2", constraint_type="ineq") +system_scenario.add_constraint("g_3", constraint_type="ineq") + + +# %% +# Visualize the XDSM +# ^^^^^^^^^^^^^^^^^^ +# Generate the XDSM on the fly: +# +# - ``log_workflow_status=True`` will log the status of the workflow in the console, +# - ``save_html`` (default ``True``) will generate a self-contained HTML file, +# that can be automatically opened using ``show_html=True``. +system_scenario.xdsmize(save_html=False, pdf_build=False) + +# %% +# Execute the main scenario +# ^^^^^^^^^^^^^^^^^^^^^^^^^ +system_scenario.execute() + +# %% +# Plot the history of the MDA residuals +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# For the first MDA: +system_scenario.formulation.mda1.plot_residual_history(save=False, show=True) + +# %% +# For the second MDA: +system_scenario.formulation.mda2.plot_residual_history(save=False, show=True) + +# %% +# For the BCD MDA: +system_scenario.formulation.bcd_mda.plot_residual_history(save=False, show=True) + +# %% +# Plot the system optimization history view +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +system_scenario.post_process(post_name="OptHistoryView", save=False, show=True) + +# %% +# Print execution metrics on disciplines and sub-scenarios +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +for disc in [propulsion_disc, aerodynamics_disc, mission_disc, structure_disc]: + print(f"{disc.name}: {disc.execution_statistics.n_executions} calls.") + +for sub_sc in [propulsion_sc, aerodynamics_sc, structure_sc]: + print(f"{sub_sc.name}: {sub_sc.execution_statistics.n_executions} calls.") diff --git a/doc_src/_examples/formulations/plot_sobieski_bilevel_example.py b/doc_src/_examples/formulations/plot_sobieski_bilevel_example.py index 31a8c386bf3435527f9d2592643e140ed3089310..87faab757a779cc1cf2df7b206e26a759ac07eee 100644 --- a/doc_src/_examples/formulations/plot_sobieski_bilevel_example.py +++ b/doc_src/_examples/formulations/plot_sobieski_bilevel_example.py @@ -170,6 +170,8 @@ system_scenario.execute(cobyla_settings) # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ # For the first MDA: system_scenario.formulation.mda1.plot_residual_history(save=False, show=True) + +# %% # For the second MDA: system_scenario.formulation.mda2.plot_residual_history(save=False, show=True) diff --git a/doc_src/references.bib b/doc_src/references.bib index cdb9a674e174b1023fe487a54b3c6bd0c4823104..cdd92b71d33253a6c52e2c16984a83b9c230bb30 100644 --- a/doc_src/references.bib +++ b/doc_src/references.bib @@ -1154,3 +1154,16 @@ abstract = {This paper describes the combination of several optimization technol year={2002}, publisher={SIAM} } + +@unpublished{david:hal-04758286, + TITLE = {{Locally convergent bi-level MDO architectures based on the block coordinate descent algorithm}}, + AUTHOR = {David, Yann and Gallard, Fran{\c c}ois and Rondepierre, Aude}, + URL = {https://hal.science/hal-04758286}, + NOTE = {working paper or preprint}, + YEAR = {2024}, + MONTH = Nov, + KEYWORDS = {Multidisciplinary Design Optimization (MDO) ; Bi-level optimization ; Block decomposition ; Block Coordinate Descent (BCD) ; NonLinear programming}, + PDF = {https://hal.science/hal-04758286v2/file/no_format.pdf}, + HAL_ID = {hal-04758286}, + HAL_VERSION = {v2}, +} diff --git a/src/gemseo/formulations/bilevel_bcd_settings.py b/src/gemseo/formulations/bilevel_bcd_settings.py index c2a852d050adda6b57f4817ce422d884fc52d40e..61e51c25142f2cdc94168775b10c088c078ae8e3 100644 --- a/src/gemseo/formulations/bilevel_bcd_settings.py +++ b/src/gemseo/formulations/bilevel_bcd_settings.py @@ -37,7 +37,7 @@ copy_field_opt = partial(copy_field, model=BaseMDASettings) class BiLevel_BCD_Settings(BiLevel_Settings): # noqa: N801 """Settings of the :class:`.BiLevel` formulation.""" - _TARGET_CLASS_NAME = "BiLevel_BCD" + _TARGET_CLASS_NAME = "BiLevelBCD" bcd_mda_settings: MDAGaussSeidel_Settings = Field( default=MDAGaussSeidel_Settings(warm_start=True),