{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "829f19bc",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:47:16.123278Z",
     "iopub.status.busy": "2026-10-02T14:47:16.122983Z",
     "iopub.status.idle": "2026-10-02T14:47:16.129076Z",
     "shell.execute_reply": "2026-10-02T14:47:16.128389Z"
    },
    "papermill": {
     "duration": 0.009812,
     "end_time": "2026-10-02T14:47:16.129893+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:47:16.120081+00:00",
     "status": "completed"
    },
    "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",
   "id": "e952dc93",
   "metadata": {
    "papermill": {
     "duration": 0.002526,
     "end_time": "2026-10-02T14:47:16.133425+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:47:16.130899+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "# OpenMDAO Solvers\n",
    "\n",
    "Every OpenMDAO solver is either a linear solver, inheriting from the `LinearSolver` class, or\n",
    "a nonlinear solver, inheriting from the `NonlinearSolver` class. A solver can be either monolithic\n",
    "or recursive in behavior.  Monolithic solvers treat the associated system as a single block. Recursive\n",
    "solvers, as the name suggests, recurse down through their system hierarchy asking each sub-system to\n",
    "operate on itself.\n",
    "\n",
    "The following is a list of available OpenMDAO solvers separated by type:\n",
    "\n",
    "**Linear**\n",
    "\n",
    "- **Monolithic**\n",
    "\n",
    "    - DirectSolver\n",
    "    - PETScKrylov\n",
    "    - ScipyKrylov\n",
    "    - LinearUserDefined\n",
    "\n",
    "- **Recursive**\n",
    "\n",
    "    - LinearBlockGS\n",
    "    - LinearBlockJac\n",
    "    - LinearRunOnce\n",
    "\n",
    "\n",
    "**Nonlinear**\n",
    "\n",
    "- **Monolithic**\n",
    "\n",
    "    - Newton  (options['solve_subsystems'] = False)\n",
    "    - Broyden  (options['solve_subsystems'] = False)\n",
    "    - BoundsEnforceLS  (options['solve_subsystems'] = False)\n",
    "    - ArmijoGoldsteinLS  (options['solve_subsystems'] = False)\n",
    "\n",
    "- **Recursive**\n",
    "\n",
    "    - NonlinearBlockGS\n",
    "    - NonlinearBlockJac\n",
    "    - NonlinearRunOnce\n",
    "    - Newton  (options['solve_subsystems'] = True)\n",
    "    - BoundsEnforceLS  (options['solve_subsystems'] = True)\n",
    "    - ArmijoGoldsteinLS  (options['solve_subsystems'] = True)\n",
    "\n",
    "\n",
    "Below is a figure depicting how a model containing both recursive and monolithic solvers\n",
    "would function.  The numbered circles represent the order of calls to subsystems in the model.\n",
    "\n",
    "![Solver execution example](images/solver_call_diag.svg)\n",
    "\n",
    "## Writing Custom Solvers\n",
    "\n",
    "If your solver will be linear, you'll need to inherit from `LinearSolver`, or perhaps from\n",
    "`BlockLinearSolver`.  If your solver will be nonlinear, inherit from `NonlinearSolver`.\n",
    "If your solver will be monolithic, you'll most likely override the entire `solve` function,\n",
    "and if your solver will be recursive, you may be able to get away with only overriding a couple of\n",
    "lower level functions like `_iter_initialize` and `_single_iteration`.  The best thing\n",
    "to do is to start with the OpenMDAO solver that is most similar to what you want to do and go from\n",
    "there.\n",
    "\n",
    "Some solvers, especially recursive ones, can have confusing calling structures, because it's\n",
    "not always obvious which class is the owner of a given method.  A command line tool,\n",
    "`openmdao call_tree` was developed to help clarify what the actual call structure is. So, for\n",
    "example, if we wanted to see the call structure of `NonlinearBlockGS.solve`, we could do the\n",
    "following:\n",
    "\n",
    "```\n",
    "openmdao call_tree openmdao.api.NonlinearBlockGS.solve\n",
    "```\n",
    "\n",
    "The output above shows that `NonlinearBlockGS` does not override the `solve` method,\n",
    "but instead overrides some lower level methods like `_iter_initialize`, `_run_apply`,\n",
    "and `_single_iteration` and relies on the `Solver._solve` method to provide the main\n",
    "skeleton for the entire solve including the iteration loop."
   ]
  }
 ],
 "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.13.14"
  },
  "papermill": {
   "default_parameters": {},
   "duration": 0.882404,
   "end_time": "2026-10-02T14:47:16.348252+00:00",
   "environment_variables": {},
   "exception": null,
   "input_path": "/home/runner/work/OpenMDAO/OpenMDAO/openmdao/docs/openmdao_book/theory_manual/solver_api.ipynb",
   "output_path": "/home/runner/work/OpenMDAO/OpenMDAO/openmdao/docs/_executed_book/theory_manual/solver_api.ipynb",
   "parameters": {},
   "start_time": "2026-10-02T14:47:15.465848+00:00",
   "version": "2.7.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}