{
 "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\n",
    "except ImportError:\n",
    "    !python -m pip install openmdao[notebooks]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Memory Profiling\n",
    "\n",
    "The `openmdao mem` command can be used to obtain an estimate of the memory usage of python function calls.\n",
    "\n",
    "For example:\n",
    "``` bash\n",
    "    openmdao mem -p openmdao -p openaerostruct --tree run_CRM.py\n",
    "```\n",
    "\n",
    "This will generate output to the console that looks like this:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "3486.46  (1 calls)  Problem.setup\n",
    "|3486.46  (1 calls)  System._setup:(Group)\n",
    "|   |3485.49  (1 calls)  Group._setup_procs\n",
    "|   |   |3481.97  (1 calls)  Group._setup_procs:(AeroPoint)\n",
    "|   |   |   |3479.86  (1 calls)  Group._setup_procs:(VLMStates)\n",
    "|   |   |   |   |2617.19  (2 calls)  Component._setup_procs:(EvalVelMtx)\n",
    "|   |   |   |   |   |3917.41  (2 calls)  EvalVelMtx.setup\n",
    "|   |   |   |   |   |   |  27.19  (2 calls)  ExplicitComponent.add_output:(EvalVelMtx)\n",
    "|   |   |   |   |   |   |   |  27.19  (2 calls)  Component.add_output:(EvalVelMtx)\n",
    "|   |   |   |   |   |   |   |   |  27.09  (2 calls)  </Users/banaylor/dev/blue/openmdao/utils/general_utils.py:151>.ensure_compatible\n",
    "|   |   |   |   | 454.27  (2 calls)  Component._setup_procs:(GetVectors)\n",
    "|   |   |   |   |   | 454.27  (2 calls)  GetVectors.setup\n",
    "|   |   |   |   | 207.82  (1 calls)  Component._setup_procs:(VLMMtxRHSComp)\n",
    "|   |   |   |   |   | 207.82  (1 calls)  VLMMtxRHSComp.setup\n",
    "|   |   |   |   |   |   |  27.09  (3 calls)  Component.add_input:(VLMMtxRHSComp)\n",
    "|   |   |   |   |   |   |   |  27.09  (3 calls)  </Users/banaylor/dev/blue/openmdao/utils/general_utils.py:151>.ensure_compatible\n",
    "|   |   |   |   |   |   |   9.13  (2 calls)  ExplicitComponent.add_output:(VLMMtxRHSComp)\n",
    "|   |   |   |   |   |   |   |   9.12  (2 calls)  Component.add_output:(VLMMtxRHSComp)\n",
    "|   |   |   |   |   |   |   |   |   9.03  (2 calls)  </Users/banaylor/dev/blue/openmdao/utils/general_utils.py:151>.ensure_compatible\n",
    "|   |   |   |   | 162.62  (1 calls)  Component._setup_procs:(EvalVelocities)\n",
    "|   |   |   |   |   | 162.62  (1 calls)  EvalVelocities.setup\n",
    "|   |   |   |   |  36.21  (1 calls)  Component._setup_procs:(SolveMatrix)\n",
    "|   |   |   |   |   |  36.21  (1 calls)  SolveMatrix.setup\n",
    "|   |   |   |   1.31  (1 calls)  Component._setup_procs:(VLMGeometry)\n",
    "|   |   |   |   |   1.31  (1 calls)  VLMGeometry.setup\n",
    "|   |   |   3.35  (1 calls)  Group._setup_procs:(Geometry)\n",
    "|   |   |   |   2.85  (1 calls)  Group._setup_procs:(GeometryMesh)\n",
    "|   |   |   |   |   1.15  (1 calls)  Component._setup_procs:(Rotate)\n",
    "|   |   |   |   |   |   1.15  (1 calls)  Rotate.setup\n",
    "1399.43  (1 calls)  Problem.final_setup\n",
    "|1399.41  (1 calls)  System._final_setup:(Group)\n",
    "|   | 689.68  (1 calls)  Group._setup_transfers\n",
    "|   |   | 689.68  (1 calls)  DefaultTransfer._setup_transfers\n",
    "|   |   |   | 688.50  (1 calls)  Group._setup_transfers:(AeroPoint)\n",
    "|   |   |   |   | 688.50  (1 calls)  DefaultTransfer._setup_transfers\n",
    "|   |   |   |   |   | 688.33  (1 calls)  Group._setup_transfers:(VLMStates)\n",
    "|   |   |   |   |   |   | 688.33  (1 calls)  DefaultTransfer._setup_transfers\n",
    "|   |   |   |   |   |   |   | 516.52  (54 calls)  DefaultTransfer._setup_transfers.merge\n",
    "|   | 354.49  (1 calls)  System._setup_bounds:(Group)\n",
    "|   | 354.33  (1 calls)  System.set_initial_values:(Group)\n",
    "4.27  (1 calls)  /Users/banaylor/dev/blue/openmdao/api.py:1<module>\n",
    "|   1.09  (1 calls)  /Users/banaylor/dev/blue/openmdao/drivers/pyoptsparse_driver.py:7<module>\n",
    "|   1.06  (1 calls)  /Users/banaylor/dev/blue/openmdao/components/meta_model_structured_comp.py:1<module>\n",
    "169.36  (2 calls)  ExplicitComponent._solve_nonlinear:(GetVectors)\n",
    "| 113.30  (2 calls)  DefaultVector.set_const\n",
    "|  56.69  (2 calls)  GetVectors.compute\n",
    "17.88  (1 calls)  ExplicitComponent._solve_nonlinear:(VLMMtxRHSComp)\n",
    "|   9.04  (1 calls)  DefaultVector.set_const\n",
    "|   8.84  (1 calls)  VLMMtxRHSComp.compute\n",
    "8.57  (13 calls)  Group._transfer:(VLMStates)\n",
    "|   8.57  (13 calls)  DefaultTransfer.transfer\n",
    "147.45  (2 calls)  EvalVelMtx.compute\n",
    "| 686.95  (10 calls)  </Users/banaylor/dev/OpenAeroStruct/openaerostruct/aerodynamics/eval_mtx.py:18>._compute_finite_vortex\n",
    "|   |  57.44  (10 calls)  </Users/banaylor/dev/OpenAeroStruct/openaerostruct/utils/vector_algebra.py:10>.compute_dot\n",
    "|   |  21.36  (20 calls)  </Users/banaylor/dev/OpenAeroStruct/openaerostruct/utils/vector_algebra.py:90>.compute_norm\n",
    "54.19  (2 calls)  DefaultVector.set_const\n",
    "\n",
    "Max mem usage: 5400.57 MB\n",
    "```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "The memory use is mapped to the call tree structure. Note that functions are tracked based on their full call tree path, so that the same function can appear multiple times in the tree, called from different places, and the different memory usage for those multiple calls can be seen in the tree.\n",
    "\n",
    "To see a flat listing rather than a tree, don’t use the *–tree* arg, and you’ll get output like this:\n",
    "\n",
    "```\n",
    "1.06  (225 calls)  Vector.__init__:(DefaultVector)\n",
    "1.06  (1 calls)  /Users/banaylor/dev/blue/openmdao/components/meta_model_structured_comp.py:1<module>\n",
    "1.09  (1 calls)  /Users/banaylor/dev/blue/openmdao/drivers/pyoptsparse_driver.py:7<module>\n",
    "1.15  (1 calls)  Component._setup_procs:(Rotate)\n",
    "1.15  (1 calls)  Rotate.setup\n",
    "1.31  (1 calls)  Component._setup_procs:(VLMGeometry)\n",
    "1.31  (1 calls)  VLMGeometry.setup\n",
    "2.85  (1 calls)  Group._setup_procs:(GeometryMesh)\n",
    "3.35  (1 calls)  Group._setup_procs:(Geometry)\n",
    "4.27  (1 calls)  /Users/banaylor/dev/blue/openmdao/api.py:1<module>\n",
    "8.57  (13 calls)  Group._transfer:(VLMStates)\n",
    "8.58  (43 calls)  DefaultTransfer.transfer\n",
    "8.84  (1 calls)  VLMMtxRHSComp.compute\n",
    "9.12  (2 calls)  Component.add_output:(VLMMtxRHSComp)\n",
    "9.13  (2 calls)  ExplicitComponent.add_output:(VLMMtxRHSComp)\n",
    "17.88  (1 calls)  ExplicitComponent._solve_nonlinear:(VLMMtxRHSComp)\n",
    "21.36  (24 calls)  </Users/banaylor/dev/OpenAeroStruct/openaerostruct/utils/vector_algebra.py:90>.compute_norm\n",
    "27.09  (3 calls)  Component.add_input:(VLMMtxRHSComp)\n",
    "27.19  (2 calls)  Component.add_output:(EvalVelMtx)\n",
    "27.19  (2 calls)  ExplicitComponent.add_output:(EvalVelMtx)\n",
    "36.21  (1 calls)  Component._setup_procs:(SolveMatrix)\n",
    "36.21  (1 calls)  SolveMatrix.setup\n",
    "56.69  (2 calls)  GetVectors.compute\n",
    "57.44  (14 calls)  </Users/banaylor/dev/OpenAeroStruct/openaerostruct/utils/vector_algebra.py:10>.compute_dot\n",
    "64.25  (145 calls)  </Users/banaylor/dev/blue/openmdao/utils/general_utils.py:151>.ensure_compatible\n",
    "147.45  (2 calls)  EvalVelMtx.compute\n",
    "162.62  (1 calls)  Component._setup_procs:(EvalVelocities)\n",
    "162.62  (1 calls)  EvalVelocities.setup\n",
    "169.36  (2 calls)  ExplicitComponent._solve_nonlinear:(GetVectors)\n",
    "177.06  (36 calls)  DefaultVector.set_const\n",
    "207.82  (1 calls)  Component._setup_procs:(VLMMtxRHSComp)\n",
    "207.82  (1 calls)  VLMMtxRHSComp.setup\n",
    "354.33  (1 calls)  System.set_initial_values:(Group)\n",
    "354.49  (1 calls)  System._setup_bounds:(Group)\n",
    "454.27  (2 calls)  Component._setup_procs:(GetVectors)\n",
    "454.27  (2 calls)  GetVectors.setup\n",
    "517.44  (186 calls)  DefaultTransfer._setup_transfers.merge\n",
    "686.95  (10 calls)  </Users/banaylor/dev/OpenAeroStruct/openaerostruct/aerodynamics/eval_mtx.py:18>._compute_finite_vortex\n",
    "688.33  (1 calls)  Group._setup_transfers:(VLMStates)\n",
    "688.50  (1 calls)  Group._setup_transfers:(AeroPoint)\n",
    "689.68  (1 calls)  Group._setup_transfers\n",
    "1399.41  (1 calls)  System._final_setup:(Group)\n",
    "1399.44  (2 calls)  Problem.final_setup\n",
    "2068.28  (7 calls)  DefaultTransfer._setup_transfers\n",
    "2617.19  (2 calls)  Component._setup_procs:(EvalVelMtx)\n",
    "3479.86  (1 calls)  Group._setup_procs:(VLMStates)\n",
    "3481.97  (1 calls)  Group._setup_procs:(AeroPoint)\n",
    "3485.49  (1 calls)  Group._setup_procs\n",
    "3486.46  (1 calls)  System._setup:(Group)\n",
    "3486.46  (1 calls)  Problem.setup\n",
    "3917.41  (2 calls)  EvalVelMtx.setup\n",
    "\n",
    "Max mem usage: 5400.57 MB\n",
    "```\n",
    "\n",
    "The -*p* argument(s) determine which package(s) will be traced. In the example above, the *openmdao* and *openaerostruct* packages were traced. If no -*p* args are supplied, the openmdao package is assumed. Note however that if *any* -*p* args are supplied, then the *openmdao* package will not be traced unless explicitly specified.\n",
    "\n",
    "The output can be filtered by minimum memory usage so that the parts with memory usage below a certain amount will not be shown. The default minimum memory usage is 1 MB. If you wanted to set the minimum memory usage to 100 MB, for example, you could do it like this:\n",
    "\n",
    "``` bash\n",
    "    openmdao mem <your_python_script_here> --min=100\n",
    "```\n",
    "\n",
    "Running *openmdao mem* generates a raw memory dump file with a default name of *mem_trace.raw*. To display the memory profile using a pre-existing memory dump file, you can use the openmdao mempost command, for example:\n",
    "\n",
    "``` bash\n",
    "    openmdao mempost mem_trace.raw --min=100\n",
    "```\n",
    "\n",
    "This just allows you to take different looks at the memory profile without having to re-run your code.\n",
    "\n",
    "As usual, any additional options for the *openmdao mem* and *openmdao mempost* commands can be seen by providing the -*h* argument, for example:\n",
    "\n",
    "``` bash\n",
    "    openmdao mem -h\n",
    "```\n",
    "\n",
    "```\n",
    "usage: openmdao [-h] [--version] command [command_options] filename mem\n",
    "       [-h] [-o OUTFILE] [--min MIN_MEM] [-c] [--nogc] [-p PACKAGES] [-t] file\n",
    "\n",
    "positional arguments:\n",
    "  file                  Python file to profile.\n",
    "\n",
    "optional arguments:\n",
    "  -h, --help            show this help message and exit\n",
    "  -o OUTFILE, --outfile OUTFILE\n",
    "                        Name of file containing memory dump. Default is\n",
    "                        mem_trace.raw.\n",
    "  --min MIN_MEM         Dump function trace with memory usage in MB above\n",
    "                        min_mem to the given file. Default is 1 MB.\n",
    "  -c, --colors          Display colors if the terminal supports it. Requires\n",
    "                        'colorama' python package. Use 'pip install colorama'\n",
    "                        to install it.\n",
    "  --nogc                Disables automatic garbage collection.\n",
    "  -p PACKAGES, --package PACKAGES\n",
    "                        Determines which packages will be traced. Default\n",
    "                        package is openmdao.\n",
    "  -t, --tree            Display memory use in tree format, showing memory use\n",
    "                        for each unique call sequence down to a function.\n",
    "```\n",
    "\n",
    "```{Note}\n",
    "These memory usage numbers are only estimates, based on the changes in the process memory measured before and after each method call. The exact memory use is difficult to determine due to the presence of python’s own internal memory management and garbage collection.\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.5"
  },
  "orphan": true
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
