{
 "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": [
    "# PETScDirectSolver\n",
    "\n",
    "PETScDirectSolver is a linear solver that assembles the system Jacobian and solves the linear system with LU factorization and back substitution using the `petsc4py` library. It can handle any system topology. Since it assembles a global Jacobian for all of its subsystems, any linear solver that is assigned in any of its subsystems does not participate in this calculation (though they may be used in other ways such as in subsystem Newton solves). Unlike the standard DirectSolver which always uses SuperLU (for sparse) or LAPACK (dense) through scipy, the PETScDirectSolver has access to multiple DirectSolvers available in PETSc.\n",
    "\n",
    "PETSc is more general and has more options than scipy for direct solvers, but due to the generality PETSc provides a thicker wrapper around its solvers. So when considering whether to use `DirectSolver` or `PETScDirectSolver`, one should consider the size of their problem and sparsity pattern. Typically PETSc will be more beneficial for larger matrices where the factorization / solving speedup from a different solver is larger than the slowdown due to overhead from the heavier wrapper. For more info about the solvers exposed by the PETScDirectSolver, see this summary table of direct solvers from the [PETSc documentation](https://petsc.org/release/overview/linear_solve_table/#direct-solvers).\n",
    "\n",
    "Of the available sparse direct solvers, SuperLU, KLU, UMFPACK, and PETSc can only be used in serial. The MUMPS and SuperLU-Dist solvers can be used in serial, but will also be run distributed if the script is run with MPI. For dense matrices the solver will ignore the sparse algorithms and automatically use LAPACK, which only runs in serial.\n",
    "\n",
    "As an example, here we calculate the total derivatives of the Sellar system objective with respect to the design variable 'z', using the \"klu\" direct solver in the PETScDirectSolver."
   ]
  },
  {
   "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_src23\", get_code(\"openmdao.test_suite.components.sellar_feature.SellarDerivatives\"), display=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    ":::{Admonition} `SellarDerivatives` class definition \n",
    ":class: dropdown\n",
    "\n",
    "{glue:}`code_src23`\n",
    ":::"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import openmdao.api as om\n",
    "from openmdao.test_suite.components.sellar_feature import SellarDerivatives\n",
    "\n",
    "prob = om.Problem()\n",
    "model = prob.model = SellarDerivatives()\n",
    "\n",
    "model.nonlinear_solver = om.NonlinearBlockGS()\n",
    "model.linear_solver = om.PETScDirectSolver(sparse_solver_name='klu')\n",
    "\n",
    "prob.setup()\n",
    "prob.run_model()\n",
    "\n",
    "wrt = ['z']\n",
    "of = ['obj']\n",
    "\n",
    "J = prob.compute_totals(of=of, wrt=wrt, return_format='flat_dict')\n",
    "print(J['obj', 'z'][0][0])\n",
    "print(J['obj', 'z'][0][1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "from openmdao.utils.assert_utils import assert_near_equal\n",
    "\n",
    "assert_near_equal(J['obj', 'z'][0][0], 9.61001056, .00001)\n",
    "assert_near_equal(J['obj', 'z'][0][1], 1.78448534, .00001)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## PETScDirectSolver Options"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input"
    ]
   },
   "outputs": [],
   "source": [
    "om.show_options_table(\"openmdao.solvers.linear.petsc_direct_solver.PETScDirectSolver\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## PETScDirectSolver Constructor\n",
    "\n",
    "The call signature for the `PETScDirectSolver` constructor is:\n",
    "\n",
    "```{eval-rst}\n",
    "    .. automethod:: openmdao.solvers.linear.petsc_direct_solver.PETScDirectSolver.__init__\n",
    "        :noindex:\n",
    "```"
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "Tags",
  "kernelspec": {
   "display_name": "petsc-test",
   "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.10"
  },
  "orphan": true
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
