{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1cca02b4",
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output",
     "active-ipynb"
    ]
   },
   "outputs": [],
   "source": [
    "try:\n",
    "    from openmdao.utils.notebook_utils import notebook_mode\n",
    "except ImportError:\n",
    "    !python -m pip install openmdao[notebooks]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a9a09c5c",
   "metadata": {},
   "source": [
    "# Unit Testing Partial Derivatives\n",
    "\n",
    "If you want to check the implementations of a `Component`’s partial derivatives as part of a unit test, you can make use of a custom assert function, `assert_check_partials`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6bcb0ce1",
   "metadata": {},
   "source": [
    "```{eval-rst}\n",
    "    .. autofunction:: openmdao.utils.assert_utils.assert_check_partials\n",
    "        :noindex:\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e8562dfb",
   "metadata": {},
   "source": [
    "In your unit test, after calling `check_partials` on a Component, you can call the `assert_check_partials` function with the returned value from `check_partials`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9b61d6f7",
   "metadata": {},
   "source": [
    "## Usage\n",
    "\n",
    "In the following code, `compute_partials` is intentionally coded incorrectly to demonstrate how `assert_check_partials` can be used to detect this kind of error."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cbf87320",
   "metadata": {
    "tags": [
     "allow-assert"
    ]
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import openmdao.api as om\n",
    "from openmdao.utils.assert_utils import assert_check_partials\n",
    "\n",
    "class BrokenDerivComp(om.ExplicitComponent):\n",
    "    def setup(self):\n",
    "        self.add_input('x1', 3.0)\n",
    "        self.add_input('x2', 5.0)\n",
    "\n",
    "        self.add_output('y', 5.5)\n",
    "\n",
    "    def setup_partials(self):\n",
    "        self.declare_partials(of='*', wrt='*')\n",
    "\n",
    "    def compute(self, inputs, outputs):\n",
    "        \"\"\" Compute outputs. \"\"\"\n",
    "        outputs['y'] = 3.0 * inputs['x1'] + 4.0 * inputs['x2']\n",
    "\n",
    "    def compute_partials(self, inputs, partials):\n",
    "        \"\"\"Intentionally incorrect derivative.\"\"\"\n",
    "        J = partials\n",
    "        J['y', 'x1'] = np.array([4.0])\n",
    "        J['y', 'x2'] = np.array([40])\n",
    "\n",
    "\n",
    "prob = om.Problem()\n",
    "prob.model = BrokenDerivComp()\n",
    "\n",
    "prob.set_solver_print(level=0)\n",
    "\n",
    "prob.setup()\n",
    "prob.run_model()\n",
    "\n",
    "data = prob.check_partials(out_stream=None)\n",
    "print(data)\n",
    "\n",
    "try:\n",
    "    assert_check_partials(data, atol=1.e-6, rtol=1.e-6)\n",
    "except ValueError as err:\n",
    "    print(str(err))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "223ed35c",
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "from openmdao.utils.assert_utils import assert_near_equal\n",
    "from openmdao.utils.assert_utils import assert_check_partials\n",
    "try:\n",
    "    assert_check_partials(data, atol=1.e-6, rtol=1.e-6)\n",
    "except ValueError as err:\n",
    "    err_string = str(err)\n",
    "    assert err_string.count('Assert Check Partials failed for the following Components') == 1\n",
    "    assert err_string.count('Assert Check Partials failed for the following Components') == 1\n",
    "    assert err_string.count('1e-06') == 2\n",
    "    assert err_string.count('Component:') == 1\n",
    "    assert err_string.count('< output > wrt < variable >') == 1\n",
    "    assert err_string.count('norm') == 2\n",
    "    assert err_string.count('y wrt x1') == 2\n",
    "    assert err_string.count('y wrt x2') == 2\n",
    "    assert err_string.count('abs') == 4\n",
    "    assert err_string.count('rel') == 4\n",
    "    assert err_string.count('fwd-fd') == 4\n",
    "else:\n",
    "    self.fail('Exception expected.')"
   ]
  }
 ],
 "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.5"
  },
  "orphan": true
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
