BiLevel-based MDO on the Sobieski SSBJ test case

from __future__ import annotations

from copy import deepcopy
from logging import WARNING

from gemseo import configure_logger
from gemseo import create_discipline
from gemseo import create_scenario
from gemseo import execute_post
from gemseo.problems.mdo.sobieski.core.design_space import SobieskiDesignSpace

configure_logger()
<RootLogger root (INFO)>

Instantiate the disciplines

First, we instantiate the four disciplines of the use case: SobieskiPropulsion, SobieskiAerodynamics, SobieskiMission and SobieskiStructure.

propu, aero, mission, struct = create_discipline([
    "SobieskiPropulsion",
    "SobieskiAerodynamics",
    "SobieskiMission",
    "SobieskiStructure",
])

Build, execute and post-process the scenario

Then, we build the scenario which links the disciplines with the formulation and the optimization algorithm. Here, we use the BiLevel formulation. We tell the scenario to minimize -y_4 instead of minimizing y_4 (range), which is the default option.

We need to define the design space.

design_space = SobieskiDesignSpace()

Then, we build a sub-scenario for each strongly coupled disciplines, using the following algorithm, maximum number of iterations and algorithm options:

algo_options = {
    "xtol_rel": 1e-7,
    "xtol_abs": 1e-7,
    "ftol_rel": 1e-7,
    "ftol_abs": 1e-7,
    "ineq_tolerance": 1e-4,
}
sub_sc_opts = {"max_iter": 30, "algo": "SLSQP", "algo_options": algo_options}

Build a sub-scenario for Propulsion

This sub-scenario will minimize SFC.

sc_prop = create_scenario(
    propu,
    "DisciplinaryOpt",
    "y_34",
    design_space.filter("x_3", copy=True),
    name="PropulsionScenario",
)
sc_prop.default_inputs = sub_sc_opts
sc_prop.add_constraint("g_3", constraint_type="ineq")

Build a sub-scenario for Aerodynamics

This sub-scenario will minimize L/D.

sc_aero = create_scenario(
    aero,
    "DisciplinaryOpt",
    "y_24",
    design_space.filter("x_2", copy=True),
    name="AerodynamicsScenario",
    maximize_objective=True,
)
sc_aero.default_inputs = sub_sc_opts
sc_aero.add_constraint("g_2", constraint_type="ineq")

Build a sub-scenario for Structure

This sub-scenario will maximize log(aircraft total weight / (aircraft total weight - fuel weight)).

sc_str = create_scenario(
    struct,
    "DisciplinaryOpt",
    "y_11",
    design_space.filter("x_1", copy=True),
    name="StructureScenario",
    maximize_objective=True,
)
sc_str.add_constraint("g_1", constraint_type="ineq")
sc_str.default_inputs = sub_sc_opts

Build a scenario for Mission

This scenario is based on the three previous sub-scenarios and on the Mission and aims to maximize the range (Breguet).

system_scenario = create_scenario(
    [sc_prop, sc_aero, sc_str, mission],
    "BiLevel",
    "y_4",
    design_space.filter("x_shared", copy=True),
    apply_cstr_tosub_scenarios=False,
    parallel_scenarios=False,
    multithread_scenarios=True,
    tolerance=1e-14,
    max_mda_iter=30,
    maximize_objective=True,
    sub_scenarios_log_level=WARNING,
)
system_scenario.add_constraint(["g_1", "g_2", "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)


Execute the main scenario

system_scenario.execute({
    "max_iter": 50,
    "algo": "NLOPT_COBYLA",
    "algo_options": algo_options,
})
    INFO - 01:02:40:
    INFO - 01:02:40: *** Start MDOScenario execution ***
    INFO - 01:02:40: MDOScenario
    INFO - 01:02:40:    Disciplines: AerodynamicsScenario PropulsionScenario SobieskiMission StructureScenario
    INFO - 01:02:40:    MDO formulation: BiLevel
    INFO - 01:02:40: Optimization problem:
    INFO - 01:02:40:    minimize -y_4(x_shared)
    INFO - 01:02:40:    with respect to x_shared
    INFO - 01:02:40:    subject to constraints:
    INFO - 01:02:40:       g_1_g_2_g_3(x_shared) <= 0
    INFO - 01:02:40:    over the design space:
    INFO - 01:02:40:       +-------------+-------------+-------+-------------+-------+
    INFO - 01:02:40:       | Name        | Lower bound | Value | Upper bound | Type  |
    INFO - 01:02:40:       +-------------+-------------+-------+-------------+-------+
    INFO - 01:02:40:       | x_shared[0] |     0.01    |  0.05 |     0.09    | float |
    INFO - 01:02:40:       | x_shared[1] |    30000    | 45000 |    60000    | float |
    INFO - 01:02:40:       | x_shared[2] |     1.4     |  1.6  |     1.8     | float |
    INFO - 01:02:40:       | x_shared[3] |     2.5     |  5.5  |     8.5     | float |
    INFO - 01:02:40:       | x_shared[4] |      40     |   55  |      70     | float |
    INFO - 01:02:40:       | x_shared[5] |     500     |  1000 |     1500    | float |
    INFO - 01:02:40:       +-------------+-------------+-------+-------------+-------+
    INFO - 01:02:40: Solving optimization problem with algorithm NLOPT_COBYLA:
    INFO - 01:02:40:      2%|▏         | 1/50 [00:00<00:15,  3.10 it/sec, obj=-553]
 WARNING - 01:02:40: Optimization found no feasible point !  The least infeasible point is selected.
 WARNING - 01:02:40:       The solution is not feasible.
    INFO - 01:02:40:      4%|▍         | 2/50 [00:00<00:13,  3.53 it/sec, obj=-574]
 WARNING - 01:02:41: Optimization found no feasible point !  The least infeasible point is selected.
 WARNING - 01:02:41:       The solution is not feasible.
    INFO - 01:02:41:      6%|▌         | 3/50 [00:00<00:12,  3.81 it/sec, obj=-813]
 WARNING - 01:02:41: Optimization found no feasible point !  The least infeasible point is selected.
 WARNING - 01:02:41:       The solution is not feasible.
    INFO - 01:02:41:      8%|▊         | 4/50 [00:01<00:11,  3.92 it/sec, obj=-751]
 WARNING - 01:02:41: Optimization found no feasible point !  The least infeasible point is selected.
 WARNING - 01:02:41:       The solution is not feasible.
    INFO - 01:02:41:     10%|█         | 5/50 [00:01<00:12,  3.72 it/sec, obj=-734]
 WARNING - 01:02:41: Optimization found no feasible point !  The least infeasible point is selected.
 WARNING - 01:02:41:       The solution is not feasible.
    INFO - 01:02:41:     12%|█▏        | 6/50 [00:01<00:11,  3.85 it/sec, obj=-977]
 WARNING - 01:02:42: Optimization found no feasible point !  The least infeasible point is selected.
 WARNING - 01:02:42:       The solution is not feasible.
    INFO - 01:02:42:     14%|█▍        | 7/50 [00:01<00:11,  3.84 it/sec, obj=-1.05e+3]
 WARNING - 01:02:42: Optimization found no feasible point !  The least infeasible point is selected.
 WARNING - 01:02:42:       The solution is not feasible.
    INFO - 01:02:42:     16%|█▌        | 8/50 [00:02<00:10,  3.82 it/sec, obj=-1.67e+3]
    INFO - 01:02:42:     18%|█▊        | 9/50 [00:02<00:10,  3.83 it/sec, obj=-1.73e+3]
 WARNING - 01:02:42: Optimization found no feasible point !  The least infeasible point is selected.
 WARNING - 01:02:42:       The solution is not feasible.
    INFO - 01:02:42:     20%|██        | 10/50 [00:02<00:10,  3.77 it/sec, obj=-2.59e+3]
    INFO - 01:02:43:     22%|██▏       | 11/50 [00:02<00:10,  3.79 it/sec, obj=-2.94e+3]
 WARNING - 01:02:43: MDAJacobi has reached its maximum number of iterations but the normed residual 2.189447656198491e-13 is still above the tolerance 1e-14.
    INFO - 01:02:43:     24%|██▍       | 12/50 [00:03<00:10,  3.68 it/sec, obj=-2.64e+3]
 WARNING - 01:02:43: Optimization found no feasible point !  The least infeasible point is selected.
 WARNING - 01:02:43:       The solution is not feasible.
    INFO - 01:02:43:     26%|██▌       | 13/50 [00:03<00:09,  3.73 it/sec, obj=-2.85e+3]
 WARNING - 01:02:43: MDAJacobi has reached its maximum number of iterations but the normed residual 1.8505396985126146e-13 is still above the tolerance 1e-14.
    INFO - 01:02:44:     28%|██▊       | 14/50 [00:03<00:09,  3.71 it/sec, obj=-2.79e+3]
 WARNING - 01:02:44: MDAJacobi has reached its maximum number of iterations but the normed residual 3.0145345591022575e-14 is still above the tolerance 1e-14.
 WARNING - 01:02:44: Optimization found no feasible point !  The least infeasible point is selected.
 WARNING - 01:02:44:       The solution is not feasible.
    INFO - 01:02:44:     30%|███       | 15/50 [00:04<00:09,  3.62 it/sec, obj=-2.4e+3]
    INFO - 01:02:44:     32%|███▏      | 16/50 [00:04<00:09,  3.67 it/sec, obj=-3.07e+3]
 WARNING - 01:02:44: Optimization found no feasible point !  The least infeasible point is selected.
 WARNING - 01:02:44:       The solution is not feasible.
    INFO - 01:02:44:     34%|███▍      | 17/50 [00:04<00:08,  3.73 it/sec, obj=-3.01e+3]
    INFO - 01:02:45:     36%|███▌      | 18/50 [00:04<00:08,  3.77 it/sec, obj=-3.39e+3]
    INFO - 01:02:45:     38%|███▊      | 19/50 [00:04<00:08,  3.81 it/sec, obj=-3.84e+3]
 WARNING - 01:02:45: MDAJacobi has reached its maximum number of iterations but the normed residual 3.8877123894689336e-14 is still above the tolerance 1e-14.
    INFO - 01:02:45:     40%|████      | 20/50 [00:05<00:07,  3.83 it/sec, obj=-3.58e+3]
    INFO - 01:02:45:     42%|████▏     | 21/50 [00:05<00:07,  3.86 it/sec, obj=-3.66e+3]
    INFO - 01:02:45:     44%|████▍     | 22/50 [00:05<00:07,  3.90 it/sec, obj=-3.77e+3]
 WARNING - 01:02:46: Optimization found no feasible point !  The least infeasible point is selected.
 WARNING - 01:02:46:       The solution is not feasible.
    INFO - 01:02:46:     46%|████▌     | 23/50 [00:05<00:06,  3.94 it/sec, obj=-3.75e+3]
    INFO - 01:02:46:     48%|████▊     | 24/50 [00:06<00:06,  3.95 it/sec, obj=-2.94e+3]
 WARNING - 01:02:46: Optimization found no feasible point !  The least infeasible point is selected.
 WARNING - 01:02:46:       The solution is not feasible.
    INFO - 01:02:46:     50%|█████     | 25/50 [00:06<00:06,  3.97 it/sec, obj=-3.71e+3]
    INFO - 01:02:46:     52%|█████▏    | 26/50 [00:06<00:05,  4.05 it/sec, obj=-3.96e+3]
    INFO - 01:02:46:     54%|█████▍    | 27/50 [00:06<00:05,  4.10 it/sec, obj=-3.87e+3]
 WARNING - 01:02:47: Optimization found no feasible point !  The least infeasible point is selected.
 WARNING - 01:02:47:       The solution is not feasible.
    INFO - 01:02:47:     56%|█████▌    | 28/50 [00:06<00:05,  4.15 it/sec, obj=-3.95e+3]
    INFO - 01:02:47:     58%|█████▊    | 29/50 [00:06<00:04,  4.21 it/sec, obj=-3.93e+3]
 WARNING - 01:02:47: Optimization found no feasible point !  The least infeasible point is selected.
 WARNING - 01:02:47:       The solution is not feasible.
    INFO - 01:02:47:     60%|██████    | 30/50 [00:07<00:04,  4.26 it/sec, obj=-3.97e+3]
    INFO - 01:02:47:     62%|██████▏   | 31/50 [00:07<00:04,  4.30 it/sec, obj=-3.96e+3]
    INFO - 01:02:47:     64%|██████▍   | 32/50 [00:07<00:04,  4.35 it/sec, obj=-3.96e+3]
    INFO - 01:02:47:     66%|██████▌   | 33/50 [00:07<00:03,  4.41 it/sec, obj=-3.95e+3]
    INFO - 01:02:47:     68%|██████▊   | 34/50 [00:07<00:03,  4.47 it/sec, obj=-3.94e+3]
 WARNING - 01:02:48: Optimization found no feasible point !  The least infeasible point is selected.
 WARNING - 01:02:48:       The solution is not feasible.
    INFO - 01:02:48:     70%|███████   | 35/50 [00:07<00:03,  4.52 it/sec, obj=-3.97e+3]
 WARNING - 01:02:48: Optimization found no feasible point !  The least infeasible point is selected.
 WARNING - 01:02:48:       The solution is not feasible.
    INFO - 01:02:48:     72%|███████▏  | 36/50 [00:07<00:03,  4.56 it/sec, obj=-3.98e+3]
    INFO - 01:02:48:     74%|███████▍  | 37/50 [00:08<00:02,  4.60 it/sec, obj=-3.96e+3]
    INFO - 01:02:48:     76%|███████▌  | 38/50 [00:08<00:02,  4.67 it/sec, obj=-3.95e+3]
    INFO - 01:02:48:     78%|███████▊  | 39/50 [00:08<00:02,  4.72 it/sec, obj=-3.95e+3]
    INFO - 01:02:48:     80%|████████  | 40/50 [00:08<00:02,  4.76 it/sec, obj=-3.96e+3]
    INFO - 01:02:48:     82%|████████▏ | 41/50 [00:08<00:01,  4.82 it/sec, obj=-3.96e+3]
    INFO - 01:02:48:     84%|████████▍ | 42/50 [00:08<00:01,  4.87 it/sec, obj=-3.96e+3]
    INFO - 01:02:49:     86%|████████▌ | 43/50 [00:08<00:01,  4.90 it/sec, obj=-3.95e+3]
 WARNING - 01:02:49: MDAJacobi has reached its maximum number of iterations but the normed residual 3.8001101574110354e-14 is still above the tolerance 1e-14.
    INFO - 01:02:49:     88%|████████▊ | 44/50 [00:08<00:01,  4.89 it/sec, obj=-3.96e+3]
    INFO - 01:02:49:     90%|█████████ | 45/50 [00:09<00:01,  4.92 it/sec, obj=-3.96e+3]
    INFO - 01:02:49:     92%|█████████▏| 46/50 [00:09<00:00,  4.97 it/sec, obj=-3.96e+3]
    INFO - 01:02:49:     94%|█████████▍| 47/50 [00:09<00:00,  5.02 it/sec, obj=-3.96e+3]
 WARNING - 01:02:49: Optimization found no feasible point !  The least infeasible point is selected.
 WARNING - 01:02:49:       The solution is not feasible.
    INFO - 01:02:49:     96%|█████████▌| 48/50 [00:09<00:00,  5.06 it/sec, obj=-3.96e+3]
    INFO - 01:02:49:     98%|█████████▊| 49/50 [00:09<00:00,  5.11 it/sec, obj=-3.96e+3]
    INFO - 01:02:50:    100%|██████████| 50/50 [00:09<00:00,  5.15 it/sec, obj=-3.96e+3]
    INFO - 01:02:50: Optimization result:
    INFO - 01:02:50:    Optimizer info:
    INFO - 01:02:50:       Status: None
    INFO - 01:02:50:       Message: Maximum number of iterations reached. GEMSEO Stopped the driver
    INFO - 01:02:50:       Number of calls to the objective function by the optimizer: 52
    INFO - 01:02:50:    Solution:
    INFO - 01:02:50:       The solution is feasible.
    INFO - 01:02:50:       Objective: -3963.3800122701787
    INFO - 01:02:50:       Standardized constraints:
    INFO - 01:02:50:          g_1_g_2_g_3 = [-0.01805093 -0.03333915 -0.04424381 -0.05182998 -0.05732217 -0.13720865
    INFO - 01:02:50:  -0.10279135  0.         -0.76718646 -0.23281354  0.         -0.183255  ]
    INFO - 01:02:50:       Design space:
    INFO - 01:02:50:          +-------------+-------------+---------------------+-------------+-------+
    INFO - 01:02:50:          | Name        | Lower bound |        Value        | Upper bound | Type  |
    INFO - 01:02:50:          +-------------+-------------+---------------------+-------------+-------+
    INFO - 01:02:50:          | x_shared[0] |     0.01    | 0.05999999999999999 |     0.09    | float |
    INFO - 01:02:50:          | x_shared[1] |    30000    |        60000        |    60000    | float |
    INFO - 01:02:50:          | x_shared[2] |     1.4     |         1.4         |     1.8     | float |
    INFO - 01:02:50:          | x_shared[3] |     2.5     |         2.5         |     8.5     | float |
    INFO - 01:02:50:          | x_shared[4] |      40     |          70         |      70     | float |
    INFO - 01:02:50:          | x_shared[5] |     500     |         1500        |     1500    | float |
    INFO - 01:02:50:          +-------------+-------------+---------------------+-------------+-------+
    INFO - 01:02:50: *** End MDOScenario execution (time: 0:00:09.719112) ***

{'max_iter': 50, 'algo_options': {'xtol_rel': 1e-07, 'xtol_abs': 1e-07, 'ftol_rel': 1e-07, 'ftol_abs': 1e-07, 'ineq_tolerance': 0.0001}, 'algo': 'NLOPT_COBYLA'}

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)
  • MDAJacobi: residual plot
  • MDAJacobi: residual plot

Plot the system optimization history view

system_scenario.post_process("OptHistoryView", save=False, show=True)
  • Evolution of the optimization variables
  • Evolution of the objective value
  • Distance to the optimum
  • Evolution of the inequality constraints
<gemseo.post.opt_history_view.OptHistoryView object at 0x7f1184d908b0>

Plot the structure optimization histories of the 2 first iterations

struct_databases = system_scenario.formulation.scenario_adapters[2].databases
for database in struct_databases[:2]:
    opt_problem = deepcopy(sc_str.formulation.optimization_problem)
    opt_problem.database = database
    execute_post(opt_problem, "OptHistoryView", save=False, show=True)

for disc in [propu, aero, mission, struct]:
    print(f"{disc.name}: {disc.n_calls} calls.")
  • Evolution of the optimization variables
  • Evolution of the objective value
  • Distance to the optimum
  • Evolution of the inequality constraints
  • Evolution of the optimization variables
  • Evolution of the objective value
  • Distance to the optimum
  • Evolution of the inequality constraints
SobieskiPropulsion: 1290 calls.
SobieskiAerodynamics: 1343 calls.
SobieskiMission: 50 calls.
SobieskiStructure: 1394 calls.

Total running time of the script: (0 minutes 13.362 seconds)

Gallery generated by Sphinx-Gallery