{
 "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": [
    "# Declaring Continuous Variables\n",
    "\n",
    "## Calling add_input and add_output\n",
    "Every component in an OpenMDAO model is an instance of `ExplicitComponent`, `ImplicitComponent`, or a subclass of one of these classes. Regardless of the type, each component has input variables and output variables that it must declare.\n",
    "\n",
    "In explicit and implicit components, the user must call `add_input` and `add_output` to declare variables in the `setup` method. An example is given below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import openmdao.api as om\n",
    "\n",
    "\n",
    "class TestExplCompSimple(om.ExplicitComponent):\n",
    "\n",
    "    def setup(self):\n",
    "        self.add_input('length', val=1., desc='length of rectangle')\n",
    "        self.add_input('width', val=1., desc='width of rectangle')\n",
    "        self.add_output('area', val=1., desc='area of rectangle')\n",
    "\n",
    "    def setup_partials(self):\n",
    "        self.declare_partials('*', '*')\n",
    "\n",
    "    def compute(self, inputs, outputs):\n",
    "        outputs['area'] = inputs['length'] * inputs['width']\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "\"\"\"Just to run the code for TestExplCompSimple.\"\"\"\n",
    "from openmdao.utils.assert_utils import assert_near_equal\n",
    "\n",
    "p = om.Problem(model=TestExplCompSimple())\n",
    "p.setup()\n",
    "\n",
    "assert_near_equal(p.get_val('length'), 1.)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```{note}\n",
    "Variable names have few restrictions, but the following characters are not allowed in a variable name: ‘.’, ‘*’, ‘?’, ‘!’, ‘[‘, ‘]’.\n",
    "```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Method Signatures\n",
    "\n",
    "```{eval-rst}\n",
    "    .. automethod:: openmdao.core.component.Component.add_input\n",
    "        :noindex:\n",
    "```\n",
    "```{eval-rst}\n",
    "    .. automethod:: openmdao.core.component.Component.add_output\n",
    "        :noindex:\n",
    "```\n",
    "\n",
    "## Usage\n",
    "\n",
    "1\\. Declaring with only the default value. \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "class CompAddWithDefault(om.ExplicitComponent):\n",
    "    \"\"\"Component for tests for declaring only default value.\"\"\"\n",
    "\n",
    "    def setup(self):\n",
    "        self.add_input('x_a')\n",
    "        self.add_input('x_b', val=3.)\n",
    "        self.add_input('x_c', val=(3., 3.))\n",
    "        self.add_input('x_d', val=[3., 3.])\n",
    "        self.add_input('x_e', val=3. * np.ones((2, 2)))\n",
    "\n",
    "        self.add_output('y_a')\n",
    "        self.add_output('y_b', val=6.)\n",
    "        self.add_output('y_c', val=(6., 6., 6.))\n",
    "        self.add_output('y_d', val=[6., 6., 6.])\n",
    "        self.add_output('y_e', val=6. * np.ones((3, 2)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"Test declaring only default value.\"\"\"\n",
    "p = om.Problem(model=CompAddWithDefault())\n",
    "p.setup()\n",
    "\n",
    "print(p.get_val('x_a'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(p.get_val('x_b'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(p.get_val('x_c'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(p.get_val('x_d'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(p.get_val('x_e'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(p.get_val('y_a'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(p.get_val('y_b'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(p.get_val('y_c'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(p.get_val('y_d'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(p.get_val('y_e'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert_near_equal(p.get_val('x_a'), 1.)\n",
    "assert_near_equal(p.get_val('x_b'), 3.)\n",
    "assert_near_equal(p.get_val('x_c'), 3. * np.ones(2))\n",
    "assert_near_equal(p.get_val('x_d'), 3. * np.ones(2))\n",
    "assert_near_equal(p.get_val('x_e'), 3. * np.ones((2, 2)))\n",
    "assert_near_equal(p.get_val('y_a'), 1.)\n",
    "assert_near_equal(p.get_val('y_b'), 6.)\n",
    "assert_near_equal(p.get_val('y_c'), 6. * np.ones(3))\n",
    "assert_near_equal(p.get_val('y_d'), 6. * np.ones(3))\n",
    "assert_near_equal(p.get_val('y_e'), 6. * np.ones((3, 2)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "2\\. Declaring with only the *shape* argument.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class CompAddWithShape(om.ExplicitComponent):\n",
    "    \"\"\"Component for tests for declaring only shape.\"\"\"\n",
    "\n",
    "    def setup(self):\n",
    "        self.add_input('x_a', shape=2)\n",
    "        self.add_input('x_b', shape=(2, 2))\n",
    "        self.add_input('x_c', shape=[2, 2])\n",
    "\n",
    "        self.add_output('y_a', shape=3)\n",
    "        self.add_output('y_b', shape=(3, 3))\n",
    "        self.add_output('y_c', shape=[3, 3])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"Test declaring only shape.\"\"\"\n",
    "p = om.Problem(model=CompAddWithShape())\n",
    "p.setup()\n",
    "\n",
    "print(p.get_val('x_a'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(p.get_val('x_b'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(p.get_val('x_c'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(p.get_val('y_a'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(p.get_val('y_b'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(p.get_val('y_c'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert_near_equal(p.get_val('x_a'), np.ones(2))\n",
    "assert_near_equal(p.get_val('x_b'), np.ones((2, 2)))\n",
    "assert_near_equal(p.get_val('x_c'), np.ones((2, 2)))\n",
    "assert_near_equal(p.get_val('y_a'), np.ones(3))\n",
    "assert_near_equal(p.get_val('y_b'), np.ones((3, 3)))\n",
    "assert_near_equal(p.get_val('y_c'), np.ones((3, 3)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "4\\. Declaring an array variable with a scalar default value.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class CompAddArrayWithScalar(om.ExplicitComponent):\n",
    "    \"\"\"Component for tests for declaring a scalar val with an array variable.\"\"\"\n",
    "\n",
    "    def setup(self):\n",
    "        self.add_input('x_a', val=2.0, shape=(6))\n",
    "        self.add_input('x_b', val=2.0, shape=(3, 2))\n",
    "        self.add_output('y_a', val=3.0, shape=(6))\n",
    "        self.add_output('y_b', val=3.0, shape=(3, 2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"Test declaring a scalar val with an array variable.\"\"\"\n",
    "p = om.Problem(model=CompAddArrayWithScalar())\n",
    "p.setup()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(p.get_val('x_a'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(p.get_val('x_b'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(p.get_val('y_a'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(p.get_val('y_b'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert_near_equal(p.get_val('x_a'), 2. * np.ones(6))\n",
    "assert_near_equal(p.get_val('x_b'), 2. * np.ones((3, 2)))\n",
    "assert_near_equal(p.get_val('y_a'), 3. * np.ones(6))\n",
    "assert_near_equal(p.get_val('y_b'), 3. * np.ones((3, 2)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "6\\. Declaring an output with bounds, using *upper* and/or *lower* arguments.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class CompAddWithBounds(om.ExplicitComponent):\n",
    "    \"\"\"Component for tests for declaring bounds.\"\"\"\n",
    "\n",
    "    def setup(self):\n",
    "        self.add_input('x')\n",
    "\n",
    "        self.add_output('y_a', val=2.0, lower=0.)\n",
    "        self.add_output('y_b', val=2.0, lower=0., upper=10.)\n",
    "        self.add_output('y_c', val=2.0 * np.ones(6),  lower=np.zeros(6), upper=10.)\n",
    "        self.add_output('y_d', val=2.0 * np.ones(6), lower=0., upper=[12, 10, 10, 10, 10, 12])\n",
    "        self.add_output('y_e', val=2.0 * np.ones((3, 2)), lower=np.zeros((3, 2)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"Test declaring bounds.\"\"\"\n",
    "p = om.Problem(model=CompAddWithBounds())\n",
    "p.setup()\n",
    "\n",
    "print(p.get_val('y_a'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(p.get_val('y_b'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(p.get_val('y_c'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(p.get_val('y_d'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(p.get_val('y_e'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert_near_equal(p.get_val('y_a'), 2.)\n",
    "assert_near_equal(p.get_val('y_b'), 2.)\n",
    "assert_near_equal(p.get_val('y_c'), 2. * np.ones(6))\n",
    "assert_near_equal(p.get_val('y_d'), 2. * np.ones(6))\n",
    "assert_near_equal(p.get_val('y_e'), 2. * np.ones((3, 2)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "7\\. Adding tags to input and output variables. These tags can then be used to filter what gets returned from the `System.get_io_metadata` method and displayed/returned from the `System.list_inputs` and `System.list_outputs` methods.\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class RectangleCompWithTags(om.ExplicitComponent):\n",
    "    \"\"\"\n",
    "    A simple Explicit Component that also has input and output with tags.\n",
    "    \"\"\"\n",
    "\n",
    "    def setup(self):\n",
    "        self.add_input('length', val=1., tags=[\"tag1\", \"tag2\"])\n",
    "        self.add_input('width', val=1., tags=[\"tag2\"])\n",
    "        self.add_output('area', val=1., tags=\"tag1\")\n",
    "\n",
    "    def setup_partials(self):\n",
    "        self.declare_partials('*', '*')\n",
    "\n",
    "    def compute(self, inputs, outputs):\n",
    "        outputs['area'] = inputs['length'] * inputs['width']\n",
    "\n",
    "prob = om.Problem(RectangleCompWithTags())\n",
    "prob.setup(check=False)\n",
    "prob.run_model()\n",
    "\n",
    "# Inputs no tags\n",
    "inputs = prob.model.list_inputs(val=False, out_stream=None)\n",
    "print(sorted(inputs))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Inputs with tags\n",
    "inputs = prob.model.list_inputs(val=False, out_stream=None, tags=\"tag1\")\n",
    "print(sorted(inputs))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Inputs with multiple tags\n",
    "inputs = prob.model.list_inputs(val=False, out_stream=None, tags=[\"tag1\", \"tag2\"])\n",
    "print(sorted(inputs))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Inputs with tag that does not match\n",
    "inputs = prob.model.list_inputs(val=False, out_stream=None, tags=\"tag3\")\n",
    "print(sorted(inputs))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Outputs no tags\n",
    "outputs = prob.model.list_outputs(val=False, out_stream=None)\n",
    "print(sorted(outputs))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Outputs with tags\n",
    "outputs = prob.model.list_outputs(val=False, out_stream=None, tags=\"tag1\")\n",
    "print(sorted(outputs))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Outputs with multiple tags\n",
    "outputs = prob.model.list_outputs(val=False, out_stream=None, tags=[\"tag1\", \"tag3\"])\n",
    "print(sorted(outputs))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Outputs with tag that does not match\n",
    "outputs = prob.model.list_outputs(val=False, out_stream=None, tags=\"tag3\")\n",
    "print(sorted(outputs))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "# Inputs no tags\n",
    "inputs = prob.model.list_inputs(val=False, out_stream=None)\n",
    "assert(sorted(inputs) == [\n",
    "    ('length', {}),\n",
    "    ('width', {}),\n",
    "])\n",
    "\n",
    "# Inputs with tags\n",
    "inputs = prob.model.list_inputs(val=False, out_stream=None, tags=\"tag1\")\n",
    "assert(sorted(inputs) == [\n",
    "    ('length', {}),\n",
    "])\n",
    "\n",
    "# Inputs with multiple tags\n",
    "inputs = prob.model.list_inputs(val=False, out_stream=None, tags=[\"tag1\", \"tag2\"])\n",
    "assert(sorted(inputs) == [\n",
    "    ('length', {}),\n",
    "    ('width', {}),\n",
    "])\n",
    "\n",
    "# Inputs with tag that does not match\n",
    "inputs = prob.model.list_inputs(val=False, out_stream=None, tags=\"tag3\")\n",
    "assert(sorted(inputs) == [])\n",
    "\n",
    "# Outputs no tags\n",
    "outputs = prob.model.list_outputs(val=False, out_stream=None)\n",
    "assert(sorted(outputs) == [\n",
    "    ('area', {}),\n",
    "])\n",
    "\n",
    "# Outputs with tags\n",
    "outputs = prob.model.list_outputs(val=False, out_stream=None, tags=\"tag1\")\n",
    "assert(sorted(outputs) == [\n",
    "    ('area', {}),\n",
    "])\n",
    "\n",
    "# Outputs with multiple tags\n",
    "outputs = prob.model.list_outputs(val=False, out_stream=None, tags=[\"tag1\", \"tag3\"])\n",
    "assert(sorted(outputs) == [\n",
    "    ('area', {}),\n",
    "])\n",
    "\n",
    "# Outputs with tag that does not match\n",
    "outputs = prob.model.list_outputs(val=False, out_stream=None, tags=\"tag3\")\n",
    "assert(sorted(outputs) == [])\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
}
