{
 "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  # noqa: F401\n",
    "except ImportError:\n",
    "    !python -m pip install openmdao[notebooks]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# OpenMDAO Docs Style Guide\n",
    "\n",
    "This document outlines OpenMDAO-v3 documentation conventions regarding\n",
    "both content and formatting.\n",
    "\n",
    "\n",
    "## General Docstring Conventions\n",
    "\n",
    "General docstring rules:\n",
    "\n",
    "- All docstrings should begin and end with triple double quotes (\"\"\").\n",
    "- Modules, classes, methods, and functions must have docstrings\n",
    "  whether the object is public or private.\n",
    "\n",
    "Two types of docstrings:\n",
    "\n",
    "1. One-line docstrings:\n",
    "\n",
    "```\n",
    "     \"\"\"Do something.\"\"\"\n",
    "```\n",
    "\n",
    "   - Phrase or sentence ended by a period.\n",
    "   - No empty space between the text and the triple double quotes.\n",
    "\n",
    "2. Multi-line docstrings:\n",
    "```\n",
    "     \"\"\"Summary line.\n",
    "\n",
    "     Paragraph 1.\n",
    "     \"\"\"\n",
    "```\n",
    "\n",
    "   - Summary line ended by a period.\n",
    "   - No empty space between the summary line and\n",
    "     the opening triple double quotes.\n",
    "   - Paragraphs separated by blank lines.\n",
    "   - Can contain a list of attributes/args/returns, explained below.\n",
    "   - No empty line at the end, before closing triple double quotes.\n",
    "\n",
    "Detailed docstring rules:\n",
    "\n",
    "1. Modules:\n",
    "\n",
    "   - Either one-line or multi-line.\n",
    "   - No blank line after the docstring.\n",
    "   - List the classes and functions inside (this can be automated).\n",
    "\n",
    "2. Classes:\n",
    "\n",
    "   - Either one-line or multi-line.\n",
    "   - List the attributes, if any (then must be multi-line).\n",
    "   - Blank line after the docstring.\n",
    "\n",
    "```\n",
    "     \"\"\"Summary line.\n",
    "\n",
    "     Paragraph 1.\n",
    "\n",
    "     Attributes\n",
    "     ----------\n",
    "     attribute_name : Type\n",
    "         description ending with a period.\n",
    "     \"\"\"\n",
    "```\n",
    "\n",
    "3. Methods or functions:\n",
    "\n",
    "   - Either one-line or multi-line.\n",
    "   - List the arguments (except for self) and the returned variables, if any.\n",
    "   - The summary line/one-line docstring should be an imperative sentence,\n",
    "     not a descriptive phrase:\n",
    "\n",
    "     - Incorrect: `\"\"\"Does something.\"\"\"`\n",
    "\n",
    "     - Correct: `\"\"\"Do something.\"\"\"`\n",
    "\n",
    "   - No blank line after the docstring.\n",
    "\n",
    "```\n",
    "     \"\"\"Do something.\n",
    "\n",
    "     Paragraph 1.\n",
    "\n",
    "     Parameters\n",
    "     ----------\n",
    "     argument_name : Type\n",
    "         description ending with a period.\n",
    "\n",
    "     Returns\n",
    "     -------\n",
    "     Type\n",
    "         description ending with a period.\n",
    "     \"\"\"\n",
    "```\n",
    "\n",
    "   - Sphinx does not correctly handle decorated methods. To ensure a method's\n",
    "     call signature appears correctly in the docs, put the call signature of the method\n",
    "     into the first line of the docstring. [See Sphinx and Decorated Methods](sphinx_decorators.ipynb) for more information.) For example:\n",
    "\n",
    "```\n",
    "     \"\"\"\n",
    "     method_name(self, arg1, arg2)\n",
    "     Do something.\n",
    "\n",
    "     Paragraph 1.\n",
    "\n",
    "     Parameters\n",
    "     ----------\n",
    "     argument_name : Type\n",
    "         description ending with a period.\n",
    "\n",
    "     Returns\n",
    "     -------\n",
    "     Type\n",
    "         description ending with a period.\n",
    "     \"\"\"\n",
    "```\n",
    "\n",
    "## Notebook Guidelines\n",
    "\n",
    "1. Each notebook should include this block at the top to import OpenMDAO if not already available.  This is necessary on the cloude-based notebook environments like colab.\n",
    "\n",
    "```\n",
    "try:\n",
    "    import openmdao.api as om\n",
    "except ImportError:\n",
    "    !python -m pip install openmdao[notebooks]\n",
    "    import openmdao.api as om\n",
    "```\n",
    "\n",
    "This cell should be tagged with the following metadata.  The \"remove-input\" and \"remove-output\" tags prevent it from showing up in the documentation, and the \"hide_input\" portion collapses the input cell. To add tags in Jupyter Notebook, navigate to `View` -> `Cell Toolbar` -> `Tags`.\n",
    "\n",
    "```\n",
    "{\n",
    "  \"hide_input\": true,\n",
    "  \"tags\": [\n",
    "    \"remove-input\",\n",
    "    \"remove-output\"\n",
    "  ],\n",
    "  \"trusted\": true\n",
    "}\n",
    "```\n",
    "\n",
    "2\\. Executed code in notebooks should be tested using the same assertions used in unittests.\n",
    "\n",
    "For instance, in the paraboloid case we have:\n",
    "\n",
    "```\n",
    "# This code block is hidden by default.\n",
    "# It exists to verify that the above code works correctly.\n",
    "\n",
    "from openmdao.utils.assert_utils import assert_near_equal\n",
    "\n",
    "# minimum value\n",
    "assert_near_equal(prob.get_val('paraboloid.f'), -27.33333, 1e-6);\n",
    "\n",
    "# location of the minimum\n",
    "assert_near_equal(prob.get_val('paraboloid.x'), 6.6667, 1e-4);\n",
    "assert_near_equal(prob.get_val('paraboloid.y'), -7.33333, 1e-4);\n",
    "```\n",
    "\n",
    "It's not necessary to show this in the documentation, so remove it using the same\n",
    "hiding metadata above.\n",
    "\n",
    "3. We will use a Github Action to check all output cells from the notebooks when committing them.  This will prevent git from picking up on meaningless diffs in the output cells and their metadata.\n",
    "\n",
    "4. Since 'n2.html' files and other build artifacts need to be manually copied over to the output `_build` directory to make the docs, each example notebook should be kept in its own directory.\n",
    "\n",
    "\n",
    "## Notebook Guidelines for MPI Features\n",
    "\n",
    "1. Each notebook should include this block at the top to import OpenMDAO if not already available.  This includes some directives that are required to interact with an mpi server that will be launched.\n",
    "\n",
    "```\n",
    "%matplotlib inline\n",
    "from ipyparallel import Client, error  # noqa: F401\n",
    "cluster=Client(profile=\"mpi\")\n",
    "view=cluster[:]\n",
    "view.block=True\n",
    "\n",
    "try:\n",
    "    import openmdao.api as om\n",
    "except ImportError:\n",
    "    !python -m pip install openmdao[notebooks]\n",
    "    import openmdao.api as om\n",
    "```\n",
    "\n",
    "This cell should be tagged with the following metadata.  The \"remove-input\" and \"remove-output\" tags prevent it from showing up in the documentation, and the \"hide_input\" portion collapses the input cell. To add tags in Jupyter Notebook, navigate to `View` -> `Cell Toolbar` -> `Tags`.\n",
    "\n",
    "```\n",
    "{\n",
    "  \"hide_input\": true,\n",
    "  \"tags\": [\n",
    "    \"remove-input\",\n",
    "    \"remove-output\"\n",
    "  ],\n",
    "  \"trusted\": true\n",
    "}\n",
    "```\n",
    "\n",
    "2. Any cell that needs to run in MPI should start with the following line.\n",
    "\n",
    "```\n",
    "%%px\n",
    "```\n",
    "\n",
    "3. The following steps are required in order to test and run the mpi examples. These are in addition to the normal requirements for setting up OpenMDAO under MPI.\n",
    "\n",
    "The following only need to be done once:\n",
    "\n",
    "```\n",
    "pip install ipyparallel\n",
    "\n",
    "jupyter serverextension enable --py ipyparallel\n",
    "\n",
    "ipython profile create --parallel --profile=mpi\n",
    "```\n",
    "\n",
    "You will also need to edit the file ~/.ipython/profile_mpi/ipcluster_config.py and add the following line to the \n",
    "end:\n",
    "```\n",
    "c.IPClusterEngines.engine_launcher_class = 'MPIEngineSetLauncher'\n",
    "```\n",
    "\n",
    "Finally, you will need to launch an MPI server that the notebooks can use. If you cycle power on your computer, or you kill this process, then you will need to launch it again.\n",
    "```\n",
    "ipcluster start -n 2 --profile=mpi &\n",
    "```\n",
    "\n",
    "\n",
    "## Embedding Autodocumentation Snippets into Documentation\n",
    "\n",
    "Sometimes in a feature doc, you want to reproduce a particular method or class or module right there within the text. The syntax to do this is provided by the sphinx.ext.autodoc module, in three commands, automodule, autoclass, and automethod. The syntax of these, inside of a markdown file or Jupyter cell is detailed in the following example code:"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "**AUTOMODULE EXAMPLE:**\n",
    "```{eval-rst}\n",
    "    .. automodule:: openmdao.core.group\n",
    "        :noindex:\n",
    "```\n",
    "\n",
    "**AUTOCLASS EXAMPLE:**\n",
    "```{eval-rst}\n",
    "    .. autoclass:: openmdao.core.group.Group\n",
    "        :noindex:\n",
    "```\n",
    "\n",
    "**AUTOMETHOD EXAMPLE:**\n",
    "```{eval-rst}\n",
    "  .. automethod:: openmdao.core.group.Group.add_subsystem\n",
    "      :noindex:\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The :noindex: argument is needed to prevent unwanted replication interactions with the OpenMDAO source documentation. The above syntax will pull docstring info and produce the following output:\n",
    "\n",
    "## Adding a Link to a Document in a `.ipynb` File\n",
    "\n",
    "Sometimes in a document, you don't want or need to embed/display the entire\n",
    "document of a class to make your point. At these times, you want to just provide\n",
    "the user with an easy way to link to the autodoc for quick reference.\n",
    "\n",
    "We'll do this with a `[]()` link.  The basic syntax looks like this:\n",
    "```\n",
    "    You might find this [file](openmdao_book/path/to/file.ipynb) helpful.\n",
    "```\n",
    "This could be a link to a file or a link to a website like our [home page](https://openmdao.org)\n",
    "\n",
    "## Custom Functions for Embedding Items into OpenMDAO Documentation\n",
    "\n",
    "### `display_source`\n",
    "`om.display_source` is a custom function from OpenMDAO that takes one argument, which is a class, test, or method's full, dotted path (e.g. \"openmdao.core.tests.test_expl_comp.RectangleComp\").\n",
    "\n",
    "The syntax for invoking the function within a `.md` or `ipynb` file looks like this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import openmdao.api as om\n",
    "om.display_source(\"openmdao.core.tests.test_expl_comp.RectangleComp\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```{Note}\n",
    "When using this function in a doc, don't forget to apply `remove-input` tag to the cell for cleanliness\n",
    "```\n",
    "\n",
    "Embedding in this fashion has the benefit of allowing you to drop entire code blocks into a feature doc that may, for example, illustrate a usage example. Another great benefit of this method is that now your embedded example changes along with the code, so the docs maintain themselves.\n",
    "\n",
    "By default, docstrings will be included. There is an option to the directive to strip the docstrings:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "om.display_source(\"openmdao.core.tests.test_expl_comp.RectangleComp\", hide_doc_string=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Embedding More Than Just Code\n",
    "\n",
    "Sometimes developers will want to embed code, code output, or even plots into a document. Because our docs use Jupyter Notebooks, developers are now able to embed code examples directly into the documents just like this notebook and even write tests for the examples. Below is an example of the flexibility this allows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# build the model\n",
    "prob = om.Problem()\n",
    "\n",
    "prob.model.add_subsystem('paraboloid', om.ExecComp('f = (x-3)**2 + x*y + (y+4)**2 - 3'))\n",
    "\n",
    "# setup the optimization\n",
    "prob.driver = om.ScipyOptimizeDriver()\n",
    "prob.driver.options['optimizer'] = 'SLSQP'\n",
    "\n",
    "prob.model.add_design_var('paraboloid.x', lower=-50, upper=50)\n",
    "prob.model.add_design_var('paraboloid.y', lower=-50, upper=50)\n",
    "prob.model.add_objective('paraboloid.f')\n",
    "\n",
    "prob.setup()\n",
    "\n",
    "# Set initial values.\n",
    "prob.set_val('paraboloid.x', 3.0)\n",
    "prob.set_val('paraboloid.y', -4.0)\n",
    "\n",
    "# run the optimization\n",
    "prob.run_driver();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "allow-assert"
    ]
   },
   "outputs": [],
   "source": [
    "from openmdao.utils.assert_utils import assert_near_equal\n",
    "assert_near_equal(prob.get_val('paraboloid.x'), 6.66666666, tolerance=1.0E-5)\n",
    "assert_near_equal(prob.get_val('paraboloid.y'), -7.33333333, tolerance=1.0E-5)\n",
    "assert_near_equal(prob.get_val('paraboloid.f'), -27.33333333, tolerance=1.0E-5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When adding tests, use `remove-output` and `remove-input` to keep the docs clean while still allowing tests to be run when building the docs.\n",
    "\n",
    "If you want to hide the output use the `remove-output` tag and/or `remove-input` to hide the code when the doc gets built. To add tags in Jupyter Notebook, navigate to `View` -> `Cell Toolbar` -> `Tags`. The output includes any output that the script produces, including plots. Below is an example of `remove-output`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "# build the model\n",
    "prob = om.Problem()\n",
    "\n",
    "prob.model.add_subsystem('paraboloid', om.ExecComp('f = (x-3)**2 + x*y + (y+4)**2 - 3'))\n",
    "\n",
    "# setup the optimization\n",
    "prob.driver = om.ScipyOptimizeDriver()\n",
    "prob.driver.options['optimizer'] = 'SLSQP'\n",
    "\n",
    "prob.model.add_design_var('paraboloid.x', lower=-50, upper=50)\n",
    "prob.model.add_design_var('paraboloid.y', lower=-50, upper=50)\n",
    "prob.model.add_objective('paraboloid.f')\n",
    "\n",
    "prob.setup()\n",
    "\n",
    "# Set initial values.\n",
    "prob.set_val('paraboloid.x', 3.0)\n",
    "prob.set_val('paraboloid.y', -4.0)\n",
    "\n",
    "# run the optimization\n",
    "prob.run_driver();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The tests will automatically detect if you have an assert in a cell but forgot to tag it with `remove-output` and `remove-input`.  If you need to include an assert in a visible cell block, you can tag that block with `allow-assert` so that the test will skip it.\n",
    "\n",
    "### `show_options_table`\n",
    "\n",
    "`om.show_options_table()` is function that lets a developer display a set of options directly into a feature doc by including the module dot path name. The syntax for invoking the directive looks like this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "om.show_options_table(\"openmdao.solvers.linear.linear_block_jac.LinearBlockJac\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Shell Commands\n",
    "\n",
    "If a developer wants to run a shell command, all they need to do is start the line with `!`. For example: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!openmdao tree ../circuit.py"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Citations\n",
    "\n",
    "To insert a citation, use `om.cite()`. The single argument is the module dot path or the name of a function that returns an instance of the desired class when called with no arguments."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "om.cite(\"openmdao.drivers.scipy_optimizer.ScipyOptimizeDriver\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### N2\n",
    "\n",
    "To embed an `N2` diagram simply call `om.n2()` inside of a Jupyter code cell and the output will be automatically formatted and displayed when given a case file or `om.Problem()`. In the example below, we will display the n2 from the paraboloid example from above."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "om.n2(prob)"
   ]
  }
 ],
 "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.7.3"
  },
  "orphan": true
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
