{
 "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": [
    "# Case Recording"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Case Recording Options\n",
    "\n",
    "The available recording options for different OpenMDAO classes are summarized in this table:\n",
    "\n",
    "|Recording Options        |Driver|System|Solver|Problem|\n",
    "|:------------------------|:----:|:----:|:----:|:-----:|\n",
    "|record_constraints       |  X   |      |      |  X    |\n",
    "|record_desvars           |  X   |      |      |  X    |\n",
    "|record_objectives        |  X   |      |      |  X    |\n",
    "|record_derivatives       |  X   |      |      |  X    |\n",
    "|record_responses         |  X   |      |      |  X    |\n",
    "|record_inputs            |  X   |  X   |  X   |  X    |\n",
    "|record_outputs           |  X   |  X   |  X   |  X    |\n",
    "|record_residuals         |  X   |  X   |      |  X    |\n",
    "|record_abs_error         |      |      |  X   |       |\n",
    "|record_rel_error         |      |      |  X   |       |\n",
    "|record_solver_residuals  |      |      |  X   |       |\n",
    "|includes                 |  X   |  X   |  X   |  X    |\n",
    "|excludes                 |  X   |  X   |  X   |  X    |\n",
    "|options_excludes         |      |  X   |      |       |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Specifying a Case Prefix\n",
    "It is possible to record data from multiple executions by specifying a prefix that will be used to differentiate the cases. This prefix can be specified when calling run_model or run_driver and will be prepended to the case ID in the recorded case data:"
   ]
  },
  {
   "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_src87\", 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_src87`\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",
    "prob = om.Problem(model=SellarDerivatives())\n",
    "prob.setup()\n",
    "\n",
    "recorder = om.SqliteRecorder(\"cases.sql\", record_viewer_data=False)\n",
    "prob.model.add_recorder(recorder)\n",
    "prob.driver.add_recorder(recorder)\n",
    "\n",
    "prob.set_solver_print(0)\n",
    "\n",
    "prob.run_model(case_prefix='Model_Run1')\n",
    "prob.run_driver(case_prefix='Driver_Run1')\n",
    "\n",
    "prob.run_model('Model_Run2')\n",
    "prob.run_driver('Driver_Run2')\n",
    "\n",
    "prob.cleanup()\n",
    "\n",
    "cr = om.CaseReader(\"cases.sql\")\n",
    "\n",
    "# all cases recorded by the root system\n",
    "model_cases = cr.list_cases('root', recurse=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert '\\n'.join(model_cases) == '\\n'.join([\n",
    "    'Model_Run1_rank0:root._solve_nonlinear|0',\n",
    "    'Driver_Run1_rank0:Driver|0|root._solve_nonlinear|0',\n",
    "    'Model_Run2_rank0:root._solve_nonlinear|0',\n",
    "    'Driver_Run2_rank0:Driver|0|root._solve_nonlinear|0'\n",
    "])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# all cases recorded by the driver\n",
    "driver_cases = cr.list_cases('driver', recurse=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert '\\n'.join(driver_cases) == '\\n'.join([\n",
    "    'Driver_Run1_rank0:Driver|0', \n",
    "    'Driver_Run2_rank0:Driver|0'\n",
    "])\n",
    "\n",
    "import os\n",
    "if os.path.exists('cases.sql'):\n",
    "    os.remove('cases.sql')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```{note}\n",
    "A recorder can be attached to more than one object. Also, more than one recorder can be attached to an object.\n",
    "```\n",
    "```{note}\n",
    "In this example, we have disabled the saving of data needed by the standalone N2 visualizer and debugging tool by setting `record_viewer_data` to `False`.\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Recording Options Include and Exclude Matching\n",
    "\n",
    "The `includes` and `excludes` recording options provide support for Unix shell-style wildcards, which are not \n",
    "the same as regular expressions. The documentation for the `fnmatchcase` function from the Python standard library\n",
    "documents the wildcards."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Recording Options Precedence\n",
    "\n",
    "The precedence of recording options that determines what gets recorded can sometime be a little confusing. \n",
    "Here is an example that might help. The code shows how the record_desvars and includes options interact."
   ]
  },
  {
   "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_src88\", get_code(\"openmdao.test_suite.components.paraboloid.Paraboloid\"), display=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    ":::{Admonition} `Paraboloid` class definition \n",
    ":class: dropdown\n",
    "\n",
    "{glue:}`code_src88`\n",
    ":::"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import openmdao.api as om\n",
    "from openmdao.test_suite.components.paraboloid import Paraboloid\n",
    "\n",
    "prob = om.Problem()\n",
    "prob.driver = om.ScipyOptimizeDriver(optimizer='SLSQP', tol=1e-9, disp=False)\n",
    "\n",
    "model = prob.model\n",
    "\n",
    "model.add_subsystem('comp', Paraboloid(), promotes=['*'])\n",
    "model.add_subsystem('con', om.ExecComp('c = x - y'), promotes=['*'])\n",
    "\n",
    "model.set_input_defaults('x', val=50.0)\n",
    "model.set_input_defaults('y', val=50.0)\n",
    "\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",
    "model.add_constraint('c', lower=15.0)\n",
    "\n",
    "case_file = 'cases.sql'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# First case with record_desvars = True and includes = []\n",
    "recorder = om.SqliteRecorder(case_file)\n",
    "prob.driver.add_recorder(recorder)\n",
    "prob.driver.recording_options['record_desvars'] = True\n",
    "prob.driver.recording_options['record_outputs'] = True\n",
    "prob.driver.recording_options['includes'] = []\n",
    "prob.driver.recording_options['excludes'] = []\n",
    "\n",
    "prob.setup()\n",
    "prob.run_driver()\n",
    "prob.cleanup()\n",
    "\n",
    "cr = om.CaseReader(case_file)\n",
    "case = cr.get_case(0)\n",
    "\n",
    "case.outputs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert sorted(case.outputs.keys()) == ['c', 'f_xy', 'x', 'y']\n",
    "\n",
    "import os\n",
    "if os.path.exists('cases.sql'):\n",
    "    os.remove('cases.sql')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Second case with record_desvars = False and includes = []\n",
    "recorder = om.SqliteRecorder(case_file)\n",
    "prob.driver.add_recorder(recorder)\n",
    "prob.driver.recording_options['record_desvars'] = False\n",
    "prob.driver.recording_options['record_outputs'] = True\n",
    "prob.driver.recording_options['includes'] = []\n",
    "\n",
    "prob.setup()\n",
    "prob.run_driver()\n",
    "prob.cleanup()\n",
    "\n",
    "cr = om.CaseReader(case_file)\n",
    "case = cr.get_case(0)\n",
    "\n",
    "case.outputs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert sorted(case.outputs.keys()) == ['c', 'f_xy']\n",
    "\n",
    "import os\n",
    "if os.path.exists('cases.sql'):\n",
    "    os.remove('cases.sql')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "# Third case with record_desvars = True and includes = ['*']\n",
    "recorder = om.SqliteRecorder(case_file)\n",
    "prob.driver.add_recorder(recorder)\n",
    "prob.driver.recording_options['record_desvars'] = True\n",
    "prob.driver.recording_options['record_outputs'] = True\n",
    "prob.driver.recording_options['includes'] = ['*']\n",
    "\n",
    "prob.setup()\n",
    "prob.run_driver()\n",
    "prob.cleanup()\n",
    "\n",
    "cr = om.CaseReader(case_file)\n",
    "case = cr.get_case(0)\n",
    "\n",
    "case.outputs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert sorted(case.outputs.keys()) == ['c', 'f_xy', 'x', 'y']\n",
    "\n",
    "import os\n",
    "if os.path.exists('cases.sql'):\n",
    "    os.remove('cases.sql')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Fourth case with record_desvars = False, record_outputs = True, and includes = ['*']\n",
    "recorder = om.SqliteRecorder(case_file)\n",
    "prob.driver.add_recorder(recorder)\n",
    "prob.driver.recording_options['record_desvars'] = False\n",
    "prob.driver.recording_options['record_outputs'] = True\n",
    "prob.driver.recording_options['includes'] = ['*']\n",
    "\n",
    "prob.setup()\n",
    "prob.run_driver()\n",
    "prob.cleanup()\n",
    "\n",
    "cr = om.CaseReader(case_file)\n",
    "case = cr.get_case(0)\n",
    "\n",
    "case.outputs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert sorted(case.outputs.keys()) == ['c', 'f_xy', 'x', 'y']"
   ]
  }
 ],
 "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
}
