{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "active-ipynb",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "try:\n",
    "    from openmdao.utils.notebook_utils import notebook_mode\n",
    "except ImportError:\n",
    "    !python -m pip install openmdao[notebooks]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# The System Setup Stack: Understanding When to Use setup and configure\n",
    "\n",
    "This document explains what happens during the OpenMDAO `Problem` `setup` process, and how some of the model\n",
    "API methods interact during that process.\n",
    "\n",
    "The purpose of the `setup` process is to prepare the data structures that OpenMDAO needs to efficiently\n",
    "run your model or driver. In particular, this includes setting up the vectors used for passing data\n",
    "to inputs, converging solvers, and calculating derivatives. It also includes setting up the MPI\n",
    "communicators.\n",
    "\n",
    "Setup also performs some level of model checking, mainly for critical errors. More extensive model\n",
    "checking can be done by setting \"check\" when calling `setup`, or by using the [openmdao command\n",
    "line check](../other_useful_docs/om_command.ipynb). It is recommended that you do this after making any changes to the configuration\n",
    "of your model.  The \"check\" argument to `setup` can be set to `True`, which will cause a default\n",
    "set of checks to run.  It can also be set to 'all', which will run all available checks.\n",
    "A value of `None` or `True` will result in no checks being run. Finally,\n",
    "it can be set to a specific list of checks to run as a list of strings.  The checks that are available can be\n",
    "determined by running the following command:\n",
    "```\n",
    "openmdao check -h\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input"
    ]
   },
   "outputs": [],
   "source": [
    "!openmdao check -h"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "By default, the output of all checks will be written to a file called 'openmdao_checks.out' in\n",
    "addition to `stdout`.  Checks can also be performed by calling the `check_config` method on\n",
    "your problem object.\n",
    "\n",
    "\n",
    "The OpenMDAO `Group` API includes three methods that are invoked during the `setup` process:\n",
    "`initialize`, `setup`, and `configure`. Most of the time, `setup` is all you need to build a group. The specific use case for `configure` is shown below in the next section. The `initialize` method is only used for declaring options for your group (and also in `Component`), and their placement here allows them to be passed into the group as instantiation arguments.\n",
    "\n",
    "One question that is often asked is: why can't we just put all of our model building code into our group's\n",
    "`__init__` method so that everything is there when I instantiate the class? The answer is, when\n",
    "running a parallel model under MPI, certain systems might only be executed on certain processors.\n",
    "To save memory across the model, these systems are not fully set up on processors where they are\n",
    "not local. The only way to do this is to isolate the model building process into a custom method\n",
    "(`setup`) and only call it on the processors where that system is active. While\n",
    "not everyone will run their models in parallel, it is a good practice to follow the stricter\n",
    "guideline so that, if someone wants to include your model in a larger parallel model, they won't\n",
    "be forced to allocate any unnecessary memory.\n",
    "\n",
    "## Usage of setup vs. configure\n",
    "\n",
    "The need for two methods for setting up a group arose from a need to sometimes change the linear or\n",
    "nonlinear solvers in a subgroup after it had been added. When `setup` is called on the `problem`, the\n",
    "`setup` method in each group is called recursively from top to bottom of the hierarchy. For example,\n",
    "a group may contain several components and groups. Setup is first called in that top group, during\n",
    "which, those components and groups are instantiated. However, the `setup` methods belonging to those sub-components\n",
    "and groups cannot be called until the top group's `setup` finishes. This means they are in a state where\n",
    "components and groups that are declared in the subgroup don't exist yet.\n",
    "\n",
    "To remedy this, there is a second api method called `configure` that lets you make changes to your subsystems\n",
    "after they have been created. The `configure` method is only needed with groups, and it is called\n",
    "recursively from the bottom of the hierarchy to the top, so that at any level, you can be sure that\n",
    "`configure` has already run for all your subsystems. This assures that changes made in higher-level groups\n",
    "take precedence over those in lower-level ones. Top precedence is given to changes made after calling `setup`\n",
    "on the `Problem`.\n",
    "\n",
    "A second use case for `configure` is issuing connections to subsystems when you need information (e.g. path names)\n",
    "that has been set during setup of those subsystems.  Since `configure` runs after `setup` has been\n",
    "called on all subsystems, you can be sure that this information will be available.\n",
    "\n",
    "Here is a quick guide covering what you can do in the `setup` and `configure` methods.\n",
    "\n",
    "| Action                                                                             \t| Setup \t| Configure |\n",
    "|------------------------------------------------------------------------------------\t|-------\t|-----------|\n",
    "| Add subsystems                                                                     \t| o     \t|           |\n",
    "| Issue connections                                                                  \t| o     \t| o         |\n",
    "| Set system execution order                                                         \t| o     \t|           |\n",
    "| Add inputs and outputs to components within this group                             \t| o     \t| o         |\n",
    "| Promote variables from subsystems                                                  \t| o     \t| o         |\n",
    "| Assign solvers at **this** group level                                             \t| o     \t| o         |\n",
    "| Assign solvers within subsystems                                                   \t| o     \t| o         |\n",
    "| Change solver settings for any solver at **this** group level                      \t| o     \t| o         |\n",
    "| Change solver settings in subsystems                                               \t|       \t| o         |\n",
    "| Assign Jacobians at **this** group level                                           \t| o     \t| o         |\n",
    "| Assign Jacobians within subsystems                                                 \t|       \t| o         |\n",
    "| Add design variables, objectives, and constraints relative to **this** group level \t| o     \t| o         |\n",
    "| Add design variables, objectives, and constraints to subsystems                    \t|       \t| o         |\n",
    "| Add a case recorder to the group or to a solver in **this** group                  \t| o     \t| o         |\n",
    "| Add a case recorder to the group or to a solver in a subsystem                     \t|       \t| o         |\n",
    "\n",
    "\n",
    "Keep in mind that, when `configure` is being run, you are already done calling `setup` on every group and component in the model, so if you add a new subsystem here, setup will never be called on it, and it will not be properly integrated into the model hierarchy.\n",
    "\n",
    "\n",
    "## Problem setup and final_setup\n",
    "\n",
    "OpenMDAO 2.0 introduced a new change to the setup process in which the original monolithic process\n",
    "is split into two separate phases triggered by the methods: `setup` and `final_setup`. The `final_setup` method is\n",
    "however something you will probably never have to call, as it is called automatically the first time that\n",
    "you call `run_model` or `run_driver` after running `setup`. The reason that the `setup` process was split into two\n",
    "phases is to allow you to perform certain actions after `setup`:\n",
    "\n",
    "**Post-setup actions**\n",
    "\n",
    " - Set values of inputs and indepvarcomps\n",
    " - Change settings on solvers\n",
    " - Change options on systems\n",
    " - Add recorders\n",
    " - Assign Jacobians\n",
    " - Add training data to metamodels\n",
    "\n",
    "If you do anything that changes the model hierarchy, such as adding a component to a group, then\n",
    "you will need to run `setup` again.\n",
    "\n",
    "During setup, the following things happen:\n",
    "\n",
    " - MPI processors are allocated\n",
    " - For each custom Group, setup function is called recursively from top to bottom\n",
    " - Model hierarchy is created\n",
    " - For each custom Group, configure function is called recursively from bottom to top\n",
    " - Connections are assembled and verified\n",
    " - Variables are sized\n",
    "\n",
    "This is just enough to allow you to perform the post-setup actions listed above, but there are\n",
    "still more things to do before the model can run. In `final_setup`, the following happens:\n",
    "\n",
    " - All vectors for the nonlinear and linear systems are created and allocated\n",
    " - Data transfers are created (i.e., scatters for MPI)\n",
    " - Solvers are set up\n",
    " - Jacobians are set up and allocated\n",
    " - Recorders are set up\n",
    " - Drivers are set up\n",
    " - Initial values are loaded into the inputs and outputs vectors"
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "Tags",
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
