{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "upset-transaction",
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output",
     "active-ipynb"
    ]
   },
   "outputs": [],
   "source": [
    "try:\n",
    "    from openmdao.utils.notebook_utils import notebook_mode  # noqa: F401\n",
    "except ImportError:\n",
    "    !python -m pip install openmdao[notebooks]"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "allied-means",
   "metadata": {},
   "source": [
    "# Automatic Setting of Execution Order\n",
    "\n",
    "By default, subsystems of a Group are executed in the same order as they were added to that Group.\n",
    "Depending on the data connections between those subsystems, that order could result in some subsystems being executed *before* their inputs have been \n",
    "updated.  This can result in a performance impact by increasing the number of solver iterations required to achieve convergence.\n",
    "\n",
    "In come cases it's obvious what the order should be and you can simply declare the subsystems in the correct order.  However, as models evolve and \n",
    "become more complex, it becomes more likely that out of order execution may occur.  One way to prevent this from happening is to set the `auto_order` option on the Group.  This will cause OpenMDAO to set the Group's subsystem execution order based on the dependency graph of that Group.  Note that if connections within a Group result in some subsystems forming a cycle in the graph, then OpenMDAO will not attempt to reorder the cycle.  It will, \n",
    "however, order the cycle properly with respect to any subsystems outside of the cycle.  Below is a simple example where OpenMDAO will automatically\n",
    "reorder the subsystems of group `G1` based on their data dependencies."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "96efe017",
   "metadata": {},
   "outputs": [],
   "source": [
    "import openmdao.api as om\n",
    "\n",
    "class SimpleComp(om.ExplicitComponent):\n",
    "    def setup(self):\n",
    "        self.add_input('x')\n",
    "        self.add_output('y')\n",
    "        self.declare_partials('*', '*')\n",
    "\n",
    "    def compute(self, inputs, outputs):\n",
    "        print(f\"running {self.name}\")\n",
    "        outputs['y'] = 2.0*inputs['x']\n",
    "\n",
    "\n",
    "p = om.Problem()\n",
    "model = p.model\n",
    "G1 = model.add_subsystem('G1', om.Group())\n",
    "G1.add_subsystem('C2', SimpleComp())\n",
    "G1.add_subsystem('C1', SimpleComp())\n",
    "G1.add_subsystem('C3', SimpleComp())\n",
    "G1.connect('C1.y', 'C2.x')\n",
    "G1.connect('C2.y', 'C3.x')\n",
    "\n",
    "# tell OpenMDAO to auto-order the components in G1\n",
    "G1.options['auto_order'] = True\n",
    "\n",
    "p.setup()\n",
    "p.run_model()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "30a2a155",
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output",
     "active-ipynb"
    ]
   },
   "outputs": [],
   "source": [
    "order = [s.name for s in G1._subsystems_myproc]\n",
    "assert order == ['C1', 'C2', 'C3'], f\"order should be ['C1', 'C2', 'C3'] but is {order}\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f3b2be1f",
   "metadata": {},
   "source": [
    "The components in G1 were declared in the order ['C2', 'C1', 'C3'], but they should execute in the order ['C1', 'C2', 'C3'] based on how they are connected to each other.\n",
    "\n",
    "## Turning Off Auto Ordering Globally\n",
    "\n",
    "For auto ordering to be possible, the framework must be able to change the order of a `Group`'s subsystems *after* all of that `Group`'s connections are known.  To make this possible, the setup order of subsystems in a `Group` is converted to alphabetical (instead of order of execution) in order to make the order of OpenMDAO's internal data structures, including the order of variables within `Vectors`, independent of subsystem execution order.  This allows the execution order of subsystems to be changed after connection information is known.  It also has the side benefit of removing previous restrictions on the `Group` `set_order` function that did not allow calling it during the `configure` function.\n",
    "\n",
    "As a result of ordering the setup of subsystems alphabetically, some very slight numerical changes may occur in a model due to, for example, rows and/or columns changing order in a jacobian.  In some cases, if a model is very sensitive, this might cause convergence failures or make convergence take \n",
    "longer.  To mitigate this, an option called `allow_post_setup_reorder` has been added to `Problem`.  This option defaults to `True`, but if it is set\n",
    "to `False`, then the setup order of subsystems will *not* be alphabetical, and all `auto_order` options on Groups in that `Problem` will be ignored.\n",
    "Setting the `allow_post_setup_reorder` option is shown below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "63240441",
   "metadata": {},
   "outputs": [],
   "source": [
    "import openmdao.api as om\n",
    "\n",
    "p = om.Problem(allow_post_setup_reorder=False)"
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "Edit Metadata",
  "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.11.4"
  },
  "orphan": true
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
