{
 "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": [
    "# Solver Debug Printing\n",
    "\n",
    "When working with a model and you have a situation where a nonlinear solver is not converging, it may be helpful to know the complete set of input and output values from the initialization of the failing case so that it can be recreated for debugging purposes. `NonlinearSolver` provides the `debug_print` option for this purpose:\n",
    "\n",
    "\n",
    "## NonlinearSolver Options"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input"
    ]
   },
   "outputs": [],
   "source": [
    "import openmdao.api as om\n",
    "om.show_options_table(\"openmdao.solvers.solver.NonlinearSolver\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Usage\n",
    "\n",
    "This example shows how to use the `debug_print` option for a `NonlinearSolver`. When this option is set to True, the values of the input and output variables will be displayed and written to a file if the solver fails to converge."
   ]
  },
  {
   "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_src67\", get_code(\"openmdao.test_suite.scripts.circuit_analysis.Circuit\"), display=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    ":::{Admonition} `Circuit` class definition \n",
    ":class: dropdown\n",
    "\n",
    "{glue:}`code_src67`\n",
    ":::"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from packaging.version import Version\n",
    "import numpy as np\n",
    "\n",
    "import openmdao.api as om\n",
    "from openmdao.test_suite.scripts.circuit_analysis import Circuit\n",
    "from openmdao.utils.general_utils import printoptions\n",
    "\n",
    "p = om.Problem()\n",
    "model = p.model\n",
    "\n",
    "model.add_subsystem('circuit', Circuit())\n",
    "\n",
    "p.setup()\n",
    "\n",
    "nl = model.circuit.nonlinear_solver = om.NewtonSolver(solve_subsystems=False)\n",
    "\n",
    "nl.options['iprint'] = 2\n",
    "nl.options['debug_print'] = True\n",
    "nl.options['err_on_non_converge'] = True\n",
    "\n",
    "# set some poor initial guesses so that we don't converge\n",
    "p.set_val('circuit.I_in', 0.1, units='A')\n",
    "p.set_val('circuit.Vg', 0.0, units='V')\n",
    "p.set_val('circuit.n1.V', 10.)\n",
    "p.set_val('circuit.n2.V', 1e-3)\n",
    "\n",
    "opts = {}\n",
    "# formatting has changed in numpy 1.14 and beyond.\n",
    "if Version(np.__version__) >= Version(\"1.14\"):\n",
    "    opts[\"legacy\"] = '1.13'\n",
    "\n",
    "with printoptions(**opts):\n",
    "    # run the model\n",
    "    try:\n",
    "        p.run_model()\n",
    "    except om.AnalysisError:\n",
    "        pass\n",
    "\n",
    "with open('solver_errors.0.out', 'r') as f:\n",
    "    print(f.read())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In larger models, OpenMDAO makes it easier to set the `debug_print` option on `set_solver_print` method on the Problem or System.\n",
    "To leave the print level of the solver unchanged, but enable debug printing, one can call `set_solver_print(level=None, debug_print=True)`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from packaging.version import Version\n",
    "import numpy as np\n",
    "\n",
    "import openmdao.api as om\n",
    "from openmdao.test_suite.scripts.circuit_analysis import Circuit\n",
    "from openmdao.utils.general_utils import printoptions\n",
    "\n",
    "p = om.Problem()\n",
    "model = p.model\n",
    "\n",
    "model.add_subsystem('circuit', Circuit())\n",
    "\n",
    "p.setup()\n",
    "\n",
    "nl = model.circuit.nonlinear_solver = om.NewtonSolver(solve_subsystems=False, err_on_non_converge=True)\n",
    "\n",
    "p.set_solver_print(level=2, debug_print=True)\n",
    "\n",
    "# set some poor initial guesses so that we don't converge\n",
    "p.set_val('circuit.I_in', 0.1, units='A')\n",
    "p.set_val('circuit.Vg', 0.0, units='V')\n",
    "p.set_val('circuit.n1.V', 10.)\n",
    "p.set_val('circuit.n2.V', 1e-3)\n",
    "\n",
    "opts = {}\n",
    "# formatting has changed in numpy 1.14 and beyond.\n",
    "if Version(np.__version__) >= Version(\"1.14\"):\n",
    "    opts[\"legacy\"] = '1.13'\n",
    "\n",
    "with printoptions(**opts):\n",
    "    # run the model\n",
    "    try:\n",
    "        p.run_model()\n",
    "    except om.AnalysisError:\n",
    "        pass\n",
    "\n",
    "with open('solver_errors.0.out', 'r') as f:\n",
    "    print(f.read())"
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "Tags",
  "kernelspec": {
   "display_name": "py312",
   "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.12.10"
  },
  "orphan": true
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
