{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3e4eca87",
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output",
     "active-ipynb"
    ]
   },
   "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": "code",
   "execution_count": null,
   "id": "41ac5cc5",
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "from openmdao.api import clean_outputs\n",
    "\n",
    "clean_outputs(prompt=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "109bd62c",
   "metadata": {},
   "source": [
    "# Accessing Recorded Metadata\n",
    "\n",
    "In addition to the cases themselves, a `CaseReader` may also record\n",
    "certain metadata about the model and its constituent systems and solvers."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "28211656",
   "metadata": {},
   "source": [
    "## Problem Metadata\n",
    "\n",
    "By default, a case recorder will save metadata about the model to assist in later visualization\n",
    "and debugging.  This information is made available via the `problem_metadata` attribute of\n",
    "a `CaseReader`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "57459d74",
   "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_src84\", get_code(\"openmdao.test_suite.components.sellar_feature.SellarDerivatives\"), display=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9e5e6640",
   "metadata": {},
   "source": [
    ":::{Admonition} `SellarDerivatives` class definition \n",
    ":class: dropdown\n",
    "\n",
    "{glue:}`code_src84`\n",
    ":::"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "341416e9",
   "metadata": {},
   "outputs": [],
   "source": [
    "import openmdao.api as om\n",
    "from openmdao.test_suite.components.sellar_feature import SellarDerivatives\n",
    "\n",
    "prob = om.Problem(SellarDerivatives())\n",
    "\n",
    "prob.model.nonlinear_solver = om.NonlinearBlockGS()\n",
    "prob.model.linear_solver = om.ScipyKrylov()\n",
    "\n",
    "recorder = om.SqliteRecorder(\"cases.sql\")\n",
    "prob.driver.add_recorder(recorder)\n",
    "\n",
    "prob.setup()\n",
    "prob.run_driver()\n",
    "prob.cleanup()\n",
    "\n",
    "cr = om.CaseReader(prob.get_outputs_dir() / \"cases.sql\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a8694a87",
   "metadata": {},
   "outputs": [],
   "source": [
    "# access list of connections stored in metadata\n",
    "cr.problem_metadata['connections_list']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5f3236bd",
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "expected = [\n",
    "    {'src': '_auto_ivc.v0', 'tgt': 'd1.x'},\n",
    "    {'src': '_auto_ivc.v0', 'tgt': 'obj_cmp.x'},\n",
    "    {'src': '_auto_ivc.v1', 'tgt': 'd1.z'},\n",
    "    {'src': '_auto_ivc.v1', 'tgt': 'd2.z'},\n",
    "    {'src': '_auto_ivc.v1', 'tgt': 'obj_cmp.z'},\n",
    "    {'src': 'd1.y1', 'tgt': 'con_cmp1.y1'},\n",
    "    {'src': 'd1.y1', 'tgt': 'd2.y1', 'cycle_arrows': [[0, 1]]},\n",
    "    {'src': 'd1.y1', 'tgt': 'obj_cmp.y1'},\n",
    "    {'src': 'd2.y2', 'tgt': 'con_cmp2.y2'},\n",
    "    {'src': 'd2.y2', 'tgt': 'd1.y2', 'cycle_arrows': [[1, 0]]},\n",
    "    {'src': 'd2.y2', 'tgt': 'obj_cmp.y2'}\n",
    "]\n",
    "\n",
    "connections = sorted(cr.problem_metadata['connections_list'], key=lambda x: (x['src'], x['tgt']))\n",
    "for i, meta in enumerate(connections):\n",
    "    for key in meta:\n",
    "        if key != 'cycle_arrows':\n",
    "            assert meta[key] == expected[i][key]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ae3924e2",
   "metadata": {},
   "outputs": [],
   "source": [
    "# access the model tree stored in metadata\n",
    "cr.problem_metadata['tree']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9e3fc674",
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert set(cr.problem_metadata['tree'].keys()) == {\n",
    "    'name', 'type', 'class', 'expressions', 'component_type',\n",
    "    'subsystem_type', 'is_parallel', 'linear_solver', 'linear_solver_options',\n",
    "    'nonlinear_solver', 'nonlinear_solver_options', 'children', 'options'\n",
    "}\n",
    "\n",
    "assert cr.problem_metadata['tree']['name'] == 'root'\n",
    "\n",
    "assert set([child[\"name\"] for child in cr.problem_metadata['tree'][\"children\"]]) == {\n",
    "    '_auto_ivc', 'con_cmp1', 'con_cmp2', 'd1', 'd2', 'obj_cmp'\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ebfcb9c8",
   "metadata": {},
   "source": [
    "## System Options\n",
    "\n",
    "All case recorders record the component options and scaling factors for all systems in the model.\n",
    "\n",
    "These values are accessible using the `list_model_options` function of a case reader object.\n",
    "This function displays and returns a dictionary of the option values for each system in the model.\n",
    "\n",
    "If the model has been run multiple times, you can specify the run for which to get/display options.\n",
    "\n",
    "The following examples use the `SellarDerivsGrouped` model, which provides system-level options to set and control the solvers."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a1c74657",
   "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_src85\", get_code(\"openmdao.test_suite.components.sellar.SellarDerivativesGrouped\"), display=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ec425b16",
   "metadata": {},
   "source": [
    ":::{Admonition} `SellarDerivativesGrouped` class definition \n",
    ":class: dropdown\n",
    "\n",
    "{glue:}`code_src85`\n",
    ":::"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5fcdc462",
   "metadata": {},
   "outputs": [],
   "source": [
    "import openmdao.api as om\n",
    "from openmdao.test_suite.components.sellar import SellarDerivativesGrouped\n",
    "\n",
    "prob = om.Problem(model=SellarDerivativesGrouped())\n",
    "\n",
    "prob.add_recorder(om.SqliteRecorder(\"cases.sql\"))\n",
    "\n",
    "# set option and run model\n",
    "prob.model.options['nonlinear_solver'] = om.NonlinearBlockGS()\n",
    "prob.model.options['nl_maxiter'] = 1\n",
    "prob.setup()\n",
    "prob.run_model()\n",
    "\n",
    "# change option and run again\n",
    "prob.model.options['nl_maxiter'] = 9\n",
    "prob.setup()\n",
    "prob.run_model()\n",
    "\n",
    "# clean up after runs and open a case reader\n",
    "prob.cleanup()\n",
    "cr = om.CaseReader(prob.get_outputs_dir() / \"cases.sql\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e3370c20",
   "metadata": {},
   "outputs": [],
   "source": [
    "# get/display options for initial run\n",
    "options = cr.list_model_options()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "749b4503",
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert sorted(options.keys()) == sorted([\n",
    "    'root', '_auto_ivc', 'con_cmp1', 'con_cmp2', 'mda', 'mda.d1', 'mda.d2', 'obj_cmp'\n",
    "])\n",
    "\n",
    "assert sorted(options['mda.d1'].keys()) == sorted(prob.model.mda.d1.options._dict.keys())\n",
    "\n",
    "assert options['root']['nl_maxiter'] == 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2191a3c2",
   "metadata": {},
   "outputs": [],
   "source": [
    "# check nl_maxiter option for the second run\n",
    "options = cr.list_model_options(run_number=1, out_stream=None)\n",
    "options['root']['nl_maxiter']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4f5e71bf",
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert options['root']['nl_maxiter'] == 9"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ef49722f",
   "metadata": {},
   "source": [
    "## Solver Options\n",
    "\n",
    "All case recorders record the solver options for all solvers in the model.\n",
    "\n",
    "These values are accessible using the `list_solver_options` function of a case reader object.\n",
    "\n",
    "This function displays and returns a dictionary of the option values for each solver in the model.\n",
    "If the model has been run multiple times, you can specify the run for which to get/display options."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6a859134",
   "metadata": {},
   "outputs": [],
   "source": [
    "import openmdao.api as om\n",
    "from openmdao.test_suite.components.sellar import SellarDerivativesGrouped\n",
    "\n",
    "prob = om.Problem(model=SellarDerivativesGrouped())\n",
    "\n",
    "prob.model.options['nonlinear_solver'] = om.NonlinearBlockGS()\n",
    "prob.model.options['linear_solver'] = om.ScipyKrylov()\n",
    "\n",
    "# configure a Newton solver with linesearch for the Sellar MDA Group\n",
    "newton = om.NewtonSolver(solve_subsystems=True, max_sub_solves=4)\n",
    "newton.linesearch = om.BoundsEnforceLS()\n",
    "prob.model.options['mda_nonlinear_solver'] = newton\n",
    "\n",
    "prob.model.options['mda_linear_solver'] = om.ScipyKrylov()\n",
    "\n",
    "prob.add_recorder(om.SqliteRecorder(\"cases.sql\"))\n",
    "\n",
    "prob.setup()\n",
    "\n",
    "# initial run\n",
    "newton.linesearch.options['bound_enforcement'] = 'vector'\n",
    "prob.run_model()\n",
    "\n",
    "# change linesearch and run again\n",
    "newton.linesearch.options['bound_enforcement'] = 'wall'\n",
    "prob.run_model()\n",
    "\n",
    "# clean up after runs and open a case reader\n",
    "prob.cleanup()\n",
    "cr = om.CaseReader(prob.get_outputs_dir() / \"cases.sql\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b5c7bdbf",
   "metadata": {},
   "outputs": [],
   "source": [
    "# get/display options for initial run\n",
    "options = cr.list_solver_options()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3db53820",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(sorted(options.keys()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "40331bce",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(options['root.NonlinearBlockGS']['maxiter'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1ffa82bb",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(options['root.ScipyKrylov']['maxiter'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "459deeeb",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(options['mda.NewtonSolver']['maxiter'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "96d11fdf",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(options['mda.NewtonSolver']['solve_subsystems'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "294c040f",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(options['mda.NewtonSolver']['max_sub_solves'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a2804eab",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(options['mda.BoundsEnforceLS']['bound_enforcement'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7ca69a03",
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert sorted(options.keys()) == [\n",
    "    'mda.BoundsEnforceLS', 'mda.NewtonSolver', 'mda.ScipyKrylov',\n",
    "    'root.NonlinearBlockGS', 'root.ScipyKrylov'\n",
    "]\n",
    "assert options['root.NonlinearBlockGS']['maxiter'] == 10\n",
    "assert options['root.ScipyKrylov']['maxiter'] == 1000\n",
    "assert options['mda.NewtonSolver']['maxiter'] == 10\n",
    "assert options['mda.NewtonSolver']['solve_subsystems']\n",
    "assert options['mda.NewtonSolver']['max_sub_solves'] == 4\n",
    "assert options['mda.BoundsEnforceLS']['bound_enforcement'] == 'vector'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "baafc172",
   "metadata": {},
   "outputs": [],
   "source": [
    "# get options for second run\n",
    "options = cr.list_solver_options(run_number=1, out_stream=None)\n",
    "print(options['mda.BoundsEnforceLS']['bound_enforcement'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3c4ed853",
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert options['mda.BoundsEnforceLS']['bound_enforcement'] == 'wall'"
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "Tags",
  "kernelspec": {
   "display_name": "dev311",
   "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.14"
  },
  "orphan": true
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
