{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "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": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "from openmdao.utils.assert_utils import assert_near_equal\n",
    "\n",
    "import os\n",
    "if os.path.exists('cases.sql'):\n",
    "    os.remove('cases.sql')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Getting Data from a Case\n",
    "\n",
    "The `Case` object contains all the information about a specific recorded case whether it was recorded by\n",
    "a problem, driver, system, or solver. `Case` objects have a number methods for accessing variables and their values."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Example of Getting Variable Data from Case Recording of a Driver\n",
    "\n",
    "Here are the methods typically used when retrieving data from the recording of a :code:`Driver`.\n",
    "\n",
    "```{eval-rst}\n",
    ".. automethod:: openmdao.recorders.case.Case.get_objectives\n",
    "    :noindex:\n",
    "\n",
    ".. automethod:: openmdao.recorders.case.Case.get_constraints\n",
    "    :noindex:\n",
    "\n",
    ".. automethod:: openmdao.recorders.case.Case.get_design_vars\n",
    "    :noindex:\n",
    "\n",
    ".. automethod:: openmdao.recorders.case.Case.get_responses\n",
    "    :noindex:\n",
    "```\n",
    "\n",
    "The following example shows how to use these methods to easily check the variables of interest\n",
    "from the first driver iteration."
   ]
  },
  {
   "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_src80\", get_code(\"openmdao.test_suite.components.sellar.SellarDerivatives\"), display=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    ":::{Admonition} `SellarDerivatives` class definition \n",
    ":class: dropdown\n",
    "\n",
    "{glue:}`code_src80`\n",
    ":::"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import openmdao.api as om\n",
    "from openmdao.test_suite.components.sellar import SellarDerivatives\n",
    "\n",
    "import numpy as np\n",
    "\n",
    "prob = om.Problem(model=SellarDerivatives())\n",
    "\n",
    "model = prob.model\n",
    "model.add_design_var('z', lower=np.array([-10.0, 0.0]),\n",
    "                          upper=np.array([10.0, 10.0]))\n",
    "model.add_design_var('x', lower=0.0, upper=10.0)\n",
    "model.add_objective('obj')\n",
    "model.add_constraint('con1', upper=0.0)\n",
    "model.add_constraint('con2', upper=0.0)\n",
    "\n",
    "driver = prob.driver = om.ScipyOptimizeDriver(optimizer='SLSQP', tol=1e-9, disp=False)\n",
    "\n",
    "driver.add_recorder(om.SqliteRecorder(\"cases.sql\"))\n",
    "\n",
    "driver.recording_options['includes'] = []\n",
    "driver.recording_options['record_objectives'] = True\n",
    "driver.recording_options['record_constraints'] = True\n",
    "driver.recording_options['record_desvars'] = True\n",
    "\n",
    "prob.setup()\n",
    "prob.set_solver_print(0)\n",
    "prob.run_driver()\n",
    "prob.cleanup()\n",
    "\n",
    "cr = om.CaseReader(\"cases.sql\")\n",
    "driver_cases = cr.list_cases('driver')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert driver_cases == [ f\"rank0:ScipyOptimize_SLSQP|{n}\" for n in range(driver.iter_count)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "case = cr.get_case(driver_cases[0])\n",
    "case.outputs.keys()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert sorted(case.outputs.keys()) == ['con1', 'con2', 'obj', 'x', 'z']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "objs = case.get_objectives()\n",
    "cons = case.get_constraints()\n",
    "dvs = case.get_design_vars()\n",
    "rsps = case.get_responses()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Using `keys()` will give you the promoted variable names:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print((sorted(objs.keys()), sorted(cons.keys()), sorted(dvs.keys())))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert sorted(objs.keys()) == ['obj']\n",
    "assert sorted(cons.keys()) == ['con1', 'con2']\n",
    "assert sorted(dvs.keys()) == ['x', 'z']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Alternatively, you can get the absolute names:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print((sorted(objs.absolute_names()), sorted(cons.absolute_names()), sorted(dvs.absolute_names())))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert sorted(objs.absolute_names()) == ['obj_cmp.obj']\n",
    "assert sorted(cons.absolute_names()) == ['con_cmp1.con1', 'con_cmp2.con2']\n",
    "assert sorted(dvs.absolute_names()) == ['x', 'z']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can access variable values using either the promoted or the absolute name:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "print('objective (obj):\\t', objs['obj'], objs['obj_cmp.obj'])\n",
    "print('constraint (con1):\\t', cons['con1'], cons['con_cmp1.con1'])\n",
    "# Note that x is supplied by an automatically generated IndepVarComp\n",
    "print('design vars (x):\\t', dvs['x'], dvs['_auto_ivc.v1'])\n",
    "print('response vars (con2):\\t', rsps['con2'], rsps['con_cmp2.con2'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert objs['obj'] == objs['obj_cmp.obj']\n",
    "assert cons['con1'] == cons['con_cmp1.con1']\n",
    "assert dvs['x'] == dvs['_auto_ivc.v1']\n",
    "assert rsps['con2'] == rsps['con_cmp2.con2']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can also access the variables directly from the case object:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print((case['obj'], case['obj_cmp.obj']))\n",
    "print((case['x'], case['_auto_ivc.v1']))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert case['obj'] == case['obj_cmp.obj']\n",
    "assert case['x'] == case['_auto_ivc.v1']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Getting Variable Data from Case Recording of a Problem"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here are the methods typically used when retrieving data from the recording of a `Problem`.\n",
    "\n",
    "```{eval-rst}\n",
    ".. automethod:: openmdao.recorders.case.Case.list_inputs\n",
    "    :noindex:\n",
    "\n",
    ".. automethod:: openmdao.recorders.case.Case.list_outputs\n",
    "    :noindex:\n",
    "```\n",
    "The following example shows how to use these methods."
   ]
  },
  {
   "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_src81\", get_code(\"openmdao.test_suite.components.sellar.SellarProblem\"), display=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    ":::{Admonition} `SellarProblem` class definition \n",
    ":class: dropdown\n",
    "\n",
    "{glue:}`code_src81`\n",
    ":::"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import openmdao.api as om\n",
    "from openmdao.test_suite.components.sellar import SellarProblem\n",
    "\n",
    "prob = SellarProblem()\n",
    "\n",
    "recorder = om.SqliteRecorder(\"cases.sql\")\n",
    "prob.model.add_recorder(recorder)\n",
    "prob.model.recording_options['record_residuals'] = True\n",
    "\n",
    "prob.setup()\n",
    "\n",
    "d1 = prob.model.d1\n",
    "d1.nonlinear_solver = om.NonlinearBlockGS(maxiter=5)\n",
    "d1.add_recorder(recorder)\n",
    "\n",
    "prob.run_driver()\n",
    "prob.cleanup()\n",
    "\n",
    "cr = om.CaseReader(\"cases.sql\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "system_cases = cr.list_cases('root.d1')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`list_inputs()` and `list_outputs()` will print a report to the screen as well as returning a list of the variables and their values."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "case = cr.get_case(system_cases[1])\n",
    "\n",
    "case_inputs = case.list_inputs()\n",
    "case_outputs = case.list_outputs()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pprint import pprint\n",
    "pprint(case_inputs)\n",
    "pprint(case_outputs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert_near_equal(case_inputs[0][1]['value'], [1.], tolerance=1e-10) # d1.x\n",
    "assert_near_equal(case_inputs[1][1]['value'], [12.27257053], tolerance=1e-10) # d1.y2\n",
    "assert_near_equal(case_inputs[2][1]['value'], [5., 2.], tolerance=1e-10) # d1.z\n",
    "assert_near_equal(case_outputs[0][1]['value'], [25.545485893882876], tolerance=1e-10) # d1.y1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `list_inputs()` and `list_outputs()` methods have optional arguments that let you filter based on variable names what gets listed. This is shown in the following examples."
   ]
  },
  {
   "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_src82\", get_code(\"openmdao.core.tests.test_expl_comp.RectangleComp\"), display=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    ":::{Admonition} `RectangleComp` class definition \n",
    ":class: dropdown\n",
    "\n",
    "{glue:}`code_src82`\n",
    ":::"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import openmdao.api as om\n",
    "from openmdao.core.tests.test_expl_comp import RectangleComp\n",
    "\n",
    "model = om.Group()\n",
    "prob = om.Problem(model)\n",
    "model.add_recorder(om.SqliteRecorder('cases.sql'))\n",
    "\n",
    "model.add_subsystem('rect', RectangleComp(), promotes=['length', 'width', 'area'])\n",
    "\n",
    "prob.setup(check=False)\n",
    "\n",
    "prob.set_val('length', 100.)\n",
    "prob.set_val('width', 60.0)\n",
    "\n",
    "prob.run_model()\n",
    "\n",
    "prob.cleanup()\n",
    "\n",
    "cr = om.CaseReader(\"cases.sql\")\n",
    "case = cr.get_case(0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Inputs with includes\n",
    "inputs = case.list_inputs(includes=['*length'])\n",
    "print(inputs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert objs['obj'] == objs['obj_cmp.obj']\n",
    "assert cons['con1'] == cons['con_cmp1.con1']\n",
    "assert dvs['x'] == dvs['_auto_ivc.v1']\n",
    "assert rsps['con2'] == rsps['con_cmp2.con2']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "# Inputs with excludes\n",
    "inputs = case.list_inputs(excludes=['*length'])\n",
    "print(inputs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert len(inputs) == 1\n",
    "assert inputs[0][0] == 'rect.width'\n",
    "assert inputs[0][1]['value'] == 60."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "# Outputs with includes\n",
    "outputs = case.list_outputs(includes=['*area'])\n",
    "print(outputs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert len(outputs) == 1\n",
    "assert outputs[0][0] == 'rect.area'\n",
    "assert outputs[0][1]['value'] == 6000."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally, you can also make use of the variable tagging feature when getting values from cases. This example shows how to do that."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import openmdao.api as om\n",
    "\n",
    "class RectangleCompWithTags(om.ExplicitComponent):\n",
    "    \"\"\"\n",
    "    A simple Explicit Component that also has input and output with tags.\n",
    "    \"\"\"\n",
    "\n",
    "    def setup(self):\n",
    "        self.add_input('length', val=1., tags=[\"tag1\", \"tag2\"])\n",
    "        self.add_input('width', val=1., tags=[\"tag2\"])\n",
    "        self.add_output('area', val=1., tags=\"tag1\")\n",
    "\n",
    "    def setup_partials(self):\n",
    "        self.declare_partials('*', '*')\n",
    "\n",
    "    def compute(self, inputs, outputs):\n",
    "        outputs['area'] = inputs['length'] * inputs['width']\n",
    "\n",
    "model = om.Group()\n",
    "prob = om.Problem(model)\n",
    "model.add_recorder(om.SqliteRecorder('cases.sql'))\n",
    "\n",
    "model.add_subsystem('rect', RectangleCompWithTags(), promotes=['length', 'width', 'area'])\n",
    "\n",
    "prob.setup(check=False)\n",
    "\n",
    "prob.set_val('length', 100.0)\n",
    "prob.set_val('width', 60.0)\n",
    "\n",
    "prob.run_model()\n",
    "\n",
    "prob.cleanup()\n",
    "\n",
    "cr = om.CaseReader(\"cases.sql\")\n",
    "case = cr.get_case(0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Inputs with tag that matches\n",
    "inputs = case.list_inputs(tags=\"tag1\")\n",
    "print(inputs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert len(inputs) == 1\n",
    "assert inputs[0][0] == 'rect.length'\n",
    "assert inputs[0][1]['value'] == 100."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "# Inputs with multiple tags\n",
    "inputs = case.list_inputs(tags=[\"tag1\", \"tag2\"])\n",
    "print(inputs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert len(inputs) == 2\n",
    "assert inputs[0][0] == 'rect.length'\n",
    "assert inputs[0][1]['value'] == 100.\n",
    "assert inputs[1][0] == 'rect.width'\n",
    "assert inputs[1][1]['value'] == 60."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "# Outputs with tag that does match\n",
    "outputs = case.list_outputs(tags=\"tag1\")\n",
    "print(outputs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert len(outputs) == 1\n",
    "assert outputs[0][0] == 'rect.area'\n",
    "assert outputs[0][1]['value'] == 6000."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Getting Variable Data from Case By Specifying Variable Name and Units Desired\n",
    "\n",
    "You can also get variable values from a `Case` like you would from a `Problem` using dictionary-like access\n",
    "or, if you want the value in different units, using the `get_val` method.\n",
    "\n",
    "\n",
    "```{eval-rst}\n",
    ".. automethod:: openmdao.recorders.case.Case.get_val\n",
    "    :noindex:\n",
    "```\n",
    "\n",
    "This example shows both methods of getting variable data by name."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import openmdao.api as om\n",
    "\n",
    "model = om.Group()\n",
    "model.add_recorder(om.SqliteRecorder('cases.sql'))\n",
    "\n",
    "speed = om.ExecComp('v=x/t', x={'units': 'm'}, t={'units': 's'}, v={'units': 'm/s'})\n",
    "\n",
    "model.add_subsystem('speed', speed, promotes=['x', 't', 'v'])\n",
    "\n",
    "prob = om.Problem(model)\n",
    "prob.setup()\n",
    "\n",
    "prob.set_val('x', 100., units='m')\n",
    "prob.set_val('t', 60., units='s')\n",
    "\n",
    "prob.run_model()\n",
    "prob.cleanup()\n",
    "\n",
    "cr = om.CaseReader('cases.sql')\n",
    "case = cr.get_case(0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(case['x'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert case['x'] == 100."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(case.get_val('x', units='ft'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert_near_equal(case.get_val('x', units='ft'), 328.0839895, 1e-8)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(case['v'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert_near_equal(case['v'], 1.66666667, 1e-8)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(case.get_val('v', units='ft/s'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert_near_equal(case.get_val('v', units='ft/s'),5.46806649, 1e-8)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Getting Derivative Data from a Case\n",
    "\n",
    "A driver has the ability to record derivatives but it is not enabled by default. If you do enable\n",
    "this option, the recorded cases will have a value for the `jacobian`."
   ]
  },
  {
   "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_src83\", get_code(\"openmdao.test_suite.components.sellar_feature.SellarMDA\"), display=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    ":::{Admonition} `SellarMDA` class definition \n",
    ":class: dropdown\n",
    "\n",
    "{glue:}`code_src83`\n",
    ":::"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import openmdao.api as om\n",
    "from openmdao.test_suite.components.sellar_feature import SellarMDA\n",
    "\n",
    "import numpy as np\n",
    "\n",
    "prob = om.Problem(model=SellarMDA())\n",
    "\n",
    "model = prob.model\n",
    "model.add_design_var('z', lower=np.array([-10.0, 0.0]),\n",
    "                          upper=np.array([10.0, 10.0]))\n",
    "model.add_design_var('x', lower=0.0, upper=10.0)\n",
    "model.add_objective('obj')\n",
    "model.add_constraint('con1', upper=0.0)\n",
    "model.add_constraint('con2', upper=0.0)\n",
    "\n",
    "driver = prob.driver = om.ScipyOptimizeDriver(optimizer='SLSQP', tol=1e-9, disp=False)\n",
    "driver.recording_options['record_derivatives'] = True\n",
    "\n",
    "driver.add_recorder(om.SqliteRecorder('cases.sql'))\n",
    "\n",
    "prob.setup()\n",
    "prob.set_solver_print(0)\n",
    "prob.run_driver()\n",
    "prob.cleanup()\n",
    "\n",
    "cr = om.CaseReader('cases.sql')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Get derivatives associated with the last iteration.\n",
    "derivs = cr.get_case(-1).derivatives\n",
    "derivs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Get specific derivative.\n",
    "print(derivs['obj', 'z'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "# check that derivatives have been recorded.\n",
    "assert set(derivs.keys()) == set([\n",
    "    ('obj', 'z'), ('con2', 'z'), ('con1', 'x'),\n",
    "    ('obj', 'x'), ('con2', 'x'), ('con1', 'z')\n",
    "])\n",
    "\n",
    "# check specific derivative.\n",
    "assert_near_equal(derivs['obj', 'z'], derivs['obj', 'z'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Problem recording can also include recording of the derivatives as this example shows."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "import openmdao.api as om\n",
    "from openmdao.test_suite.components.eggcrate import EggCrate\n",
    "\n",
    "prob = om.Problem()\n",
    "\n",
    "model = prob.model\n",
    "model.add_subsystem('egg_crate', EggCrate(), promotes=['x', 'y', 'f_xy'])\n",
    "model.add_design_var('x', lower=-50.0, upper=50.0)\n",
    "model.add_design_var('y', lower=-50.0, upper=50.0)\n",
    "model.add_objective('f_xy')\n",
    "\n",
    "prob.driver = om.ScipyOptimizeDriver(disp=False, tol=1e-9)\n",
    "\n",
    "prob.recording_options['record_derivatives'] = True\n",
    "prob.add_recorder(om.SqliteRecorder('cases.sql'))\n",
    "\n",
    "prob.setup()\n",
    "prob.set_solver_print(0)\n",
    "\n",
    "prob.set_val('x', 2.5)\n",
    "prob.set_val('y', 2.5)\n",
    "\n",
    "prob.run_driver()\n",
    "\n",
    "case_name_1 = \"c1\"\n",
    "prob.record(case_name_1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert_near_equal([prob.get_val('x'), prob.get_val('y'), prob.get_val('f_xy')],\n",
    "                  [[3.01960159], [3.01960159], [18.97639468]], 1e-6)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "prob.set_val('x', 0.1)\n",
    "prob.set_val('y', -0.1)\n",
    "\n",
    "prob.run_driver()\n",
    "\n",
    "case_name_2 = \"c2\"\n",
    "prob.record(case_name_2)\n",
    "\n",
    "prob.cleanup()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert_near_equal([prob.get_val('x'), prob.get_val('y'), prob.get_val('f_xy')],\n",
    "                  [[-2.14311975e-08], [2.14312031e-08], [2.388341e-14]], 1e-6)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cr = om.CaseReader('cases.sql')\n",
    "\n",
    "c1 = cr.get_case(case_name_1)\n",
    "c2 = cr.get_case(case_name_2)\n",
    "\n",
    "print(c1.derivatives)\n",
    "print(c2.derivatives)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "num_problem_cases = len(cr.list_cases('problem'))\n",
    "assert num_problem_cases == 2\n",
    "\n",
    "# check that derivatives have been recorded properly.\n",
    "assert_near_equal(c1.derivatives[('f_xy', 'x')][0], 0.0, 1e-4)\n",
    "assert_near_equal(c1.derivatives[('f_xy', 'y')][0], 0.0, 1e-4)\n",
    "\n",
    "assert_near_equal(c2.derivatives[('f_xy', 'x')][0], 0.0, 1e-4)\n",
    "assert_near_equal(c2.derivatives[('f_xy', 'y')][0], 0.0, 1e-4)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```{note}\n",
    "For both `Driver` and `Problem`, the recording of the derivatives is not affected by\n",
    "the `includes` and `excludes` options.\n",
    "```"
   ]
  }
 ],
 "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.1"
  },
  "orphan": true
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
