{
 "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  # noqa: F401\n",
    "except ImportError:\n",
    "    !python -m pip install openmdao[notebooks]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Sanity Checking Your Model\n",
    "\n",
    "In the first two tutorials we showed you the basics of how to build up a model from a set of components,\n",
    "group them together, connect them together, and optimize them.\n",
    "\n",
    "Sometimes you put your model together and things don't work quite the way you would expect.\n",
    "When this happens, OpenMDAO has a number of [debugging](../../features/debugging/debugging) features to help you\n",
    "understand the structure of your model better and sort out the issue.\n",
    "Many debugging features are all accessed via a [command line script](../../other_useful_docs/om_command) that is installed along with OpenMDAO itself.\n",
    "There are a lot of different tools that are accessible from that script, but in this tutorial we'll focus on the most important one:\n",
    "[check setup](../../other_useful_docs/om_command).\n",
    "\n",
    "\n",
    "## Check Setup\n",
    "\n",
    "Check setup runs through a host of different tests to make sure your model is setup correctly and warn you about things that commonly cause problems.\n",
    "It will:\n",
    "\n",
    "1. identify any unconnected inputs (forgetting to connect things is one of the most common mistakes).\n",
    "2. look for any cycles in your model that indicate the need for solvers (did you mean to create that cycle?).\n",
    "3. recurse down the model hierarchy and give every group and component a chance to perform its own custom checks.\n",
    "\n",
    "For example, if you tried to build the [sellar problem using connections](sellar-connect),\n",
    "but forgot to issue one of the connections then your problem wouldn't run correctly and you'd get the wrong answer."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "from openmdao.utils.notebook_utils import get_code\n",
    "from myst_nb import glue\n",
    "glue(\"code_src1\", get_code(\"openmdao.test_suite.components.sellar.SellarDis1\"), display=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    ":::{Admonition} `SellarDis1` class definition \n",
    ":class: dropdown\n",
    "\n",
    "{glue:}`code_src1`\n",
    ":::"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "from openmdao.utils.notebook_utils import get_code\n",
    "from myst_nb import glue\n",
    "glue(\"code_src2\", get_code(\"openmdao.test_suite.components.sellar.SellarDis2\"), display=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    ":::{Admonition} `SellarDis2` class definition \n",
    ":class: dropdown\n",
    "\n",
    "{glue:}`code_src2`\n",
    ":::"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import openmdao.api as om\n",
    "\n",
    "from openmdao.test_suite.components.sellar import SellarDis1, SellarDis2\n",
    "\n",
    "\n",
    "class SellarMDAConnect(om.Group):\n",
    "\n",
    "    def setup(self):\n",
    "        cycle = self.add_subsystem('cycle', om.Group(), promotes_inputs=['x', 'z'])\n",
    "        cycle.add_subsystem('d1', SellarDis1(), promotes_inputs=['x', 'z'])\n",
    "        cycle.add_subsystem('d2', SellarDis2(), promotes_inputs=['z'])\n",
    "        cycle.connect('d1.y1', 'd2.y1')\n",
    "\n",
    "        ######################################\n",
    "        # This is a \"forgotten\" connection!!\n",
    "        ######################################\n",
    "        #cycle.connect('d2.y2', 'd1.y2')\n",
    "\n",
    "        self.set_input_defaults('x', 1.0)\n",
    "        self.set_input_defaults('z', np.array([5.0, 2.0]))\n",
    "\n",
    "        # Nonlinear Block Gauss Seidel is a gradient free solver\n",
    "        cycle.nonlinear_solver = om.NonlinearBlockGS()\n",
    "\n",
    "        self.add_subsystem('obj_cmp', om.ExecComp('obj = x**2 + z[1] + y1 + exp(-y2)',\n",
    "                                                  z=np.array([0.0, 0.0]), x=0.0),\n",
    "                           promotes_inputs=['x', 'z'])\n",
    "\n",
    "        self.add_subsystem('con_cmp1', om.ExecComp('con1 = 3.16 - y1'))\n",
    "        self.add_subsystem('con_cmp2', om.ExecComp('con2 = y2 - 24.0'))\n",
    "\n",
    "        self.connect('cycle.d1.y1', ['obj_cmp.y1', 'con_cmp1.y1'])\n",
    "        self.connect('cycle.d2.y2', ['obj_cmp.y2', 'con_cmp2.y2'])\n",
    "\n",
    "\n",
    "prob = om.Problem()\n",
    "\n",
    "prob.model = SellarMDAConnect()\n",
    "\n",
    "prob.driver = om.ScipyOptimizeDriver()\n",
    "prob.driver.options['optimizer'] = 'SLSQP'\n",
    "# prob.driver.options['maxiter'] = 100\n",
    "prob.driver.options['tol'] = 1e-8\n",
    "\n",
    "prob.set_solver_print(level=0)\n",
    "\n",
    "prob.model.add_design_var('x', lower=0, upper=10)\n",
    "prob.model.add_design_var('z', lower=0, upper=10)\n",
    "prob.model.add_objective('obj_cmp.obj')\n",
    "prob.model.add_constraint('con_cmp1.con1', upper=0)\n",
    "prob.model.add_constraint('con_cmp2.con2', upper=0)\n",
    "\n",
    "prob.setup()\n",
    "\n",
    "prob.set_val('x', 2.0)\n",
    "prob.set_val('z', [-1., -1.])\n",
    "\n",
    "prob.run_driver()\n",
    "print('minimum found at')\n",
    "print(prob.get_val('x')[0])\n",
    "print(prob.get_val('z'))\n",
    "print('minumum objective')\n",
    "print(prob.get_val('obj_cmp.obj')[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "tags": [
     "remove-input"
    ]
   },
   "source": [
    "If you are in colab, the shell command will not find the file because it is a single notebook without the included file."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!openmdao check -c all sellar.py"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This output tells you several things:\n",
    "\n",
    "1. You have an unconnected input: `cycle.d1.y2`\n",
    "2. There are no reported cycles in your model, but there should be because this is supposed to be a coupled model!\n",
    "\n",
    "Whenever you encounter a problem, before you look at anything else you should always run this check first and look over the output carefully."
   ]
  }
 ],
 "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.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
