{
 "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": [
    "# ExplicitComponent\n",
    "\n",
    "Explicit variables are those that are computed as an explicit function of other variables. For instance, $z$ would be an explicit variable, given $z=sin(y)$, while $y$ would not be, given that it is defined implicitly by the nonlinear equation $cos(x⋅y)−z⋅y=0$.\n",
    "\n",
    "In OpenMDAO, explicit variables are defined by writing a class that inherits from the [ExplicitComponent](../../../_srcdocs/packages/core/explicitcomponent)  class. The explicit variables would be considered outputs while the variables on which they depend would be considered inputs (e.g., $y$ in $z=sin(y)$).\n",
    "\n",
    "## ExplicitComponent Methods\n",
    "\n",
    "The implementation of each method will be illustrated using a simple explicit component that computes the output area as a function of inputs *length* and *width*."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import openmdao.api as om\n",
    "\n",
    "\n",
    "class RectangleComp(om.ExplicitComponent):\n",
    "    \"\"\"\n",
    "    A simple Explicit Component that computes the area of a rectangle.\n",
    "    \"\"\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* `setup()` :\n",
    "\n",
    "Declare input and output variables via `add_input` and `add_output`. Information such as variable names, sizes, units, and bounds are declared here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def setup(self):\n",
    "    self.add_input('length', val=1.)\n",
    "    self.add_input('width', val=1.)\n",
    "    self.add_output('area', val=1.)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* `setup_partials()` :\n",
    "\n",
    "Declare partial derivatives that this component provides, using wild cards to say that this component provides derivatives of all outputs with respect to all inputs."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def setup_partials(self):\n",
    "    self.declare_partials('*', '*')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* `compute(inputs, outputs)` :\n",
    "\n",
    "Compute the `outputs` given the `inputs`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def compute(self, inputs, outputs):\n",
    "    outputs['area'] = inputs['length'] * inputs['width']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* `compute_partials(inputs, partials)` :\n",
    "\n",
    "```{note}\n",
    "[Optional] Compute the `partials` (partial derivatives) given the `inputs`.\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def compute_partials(self, inputs, partials):\n",
    "    partials['area', 'length'] = inputs['width']\n",
    "    partials['area', 'width'] = inputs['length']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(matrix-free-api)=\n",
    "### The Matrix-Free API: Providing derivatives as a matrix-vector product\n",
    "\n",
    "* `compute_jacvec_product(inputs, d_inputs, d_outputs, mode)` :\n",
    "\n",
    "```{note}\n",
    "[Optional] Provide the partial derivatives as a matrix-vector product. If `mode` is 'fwd', this method must compute $d\\_outputs=J⋅d\\_inputs$, where J is the partial derivative Jacobian. If `mode` is 'rev', this method must compute $d\\_inputs=J^T⋅d\\_outputs$.\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode):\n",
    "    if mode == 'fwd':\n",
    "        if 'area' in d_outputs:\n",
    "            if 'length' in d_inputs:\n",
    "                d_outputs['area'] += inputs['width'] * d_inputs['length']\n",
    "            if 'width' in d_inputs:\n",
    "                d_outputs['area'] += inputs['length'] * d_inputs['width']\n",
    "    elif mode == 'rev':\n",
    "        if 'area' in d_outputs:\n",
    "            if 'length' in d_inputs:\n",
    "                d_inputs['length'] += inputs['width'] * d_outputs['area']\n",
    "            if 'width' in d_inputs:\n",
    "                d_inputs['width'] += inputs['length'] * d_outputs['area']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that the last two are optional. A user may use finite-difference or complex-step methods, in which case these methods are not necessary. For efficiency reasons, OpenMDAO does not allow components to implement both `compute_partials` _and_ `compute_jacvec_product`."
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "Tags",
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.9.6"
  },
  "orphan": true
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
