{
 "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": [
    "# Setting and Getting Component Variables\n",
    "\n",
    "You will both set and get the values in the dimensional and unscaled form via the\n",
    "[Problem](../../../_srcdocs/packages/core/problem) class.\n",
    "If you have promoted both inputs and outputs to the same name,\n",
    "then the output takes precedence and it determines the units you should work in.\n",
    "\n",
    "\n",
    "## Outputs and Independent Variables\n",
    "\n",
    "To set or get the output variable, you reference it by its promoted name.\n",
    "In the regular [Sellar](../../../basic_user_guide/multidisciplinary_optimization/sellar) problem, all the variables\n",
    "have been promoted to the top of the model.\n",
    "So to get the value of the \"y1\" output defined in the\n",
    "[SellarDis1WithDerivatives](../../../_srcdocs/packages/test_suite.components/sellar) component, you would do\n",
    "the following:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "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_src52\", get_code(\"openmdao.test_suite.components.sellar.SellarDerivatives\"), display=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    ":::{Admonition} `SellarDerivatives` class definition \n",
    ":class: dropdown\n",
    "\n",
    "{glue:}`code_src52`\n",
    ":::"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import openmdao.api as om\n",
    "from openmdao.test_suite.components.sellar import SellarDerivatives\n",
    "\n",
    "prob = om.Problem(model=SellarDerivatives())\n",
    "prob.model.nonlinear_solver = om.NonlinearBlockGS()\n",
    "\n",
    "prob.setup()\n",
    "\n",
    "prob.set_val('x', 2.75)\n",
    "\n",
    "prob.run_model()\n",
    "\n",
    "print(prob.get_val('y1'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "from openmdao.utils.assert_utils import assert_near_equal\n",
    "\n",
    "assert_near_equal(prob.get_val('y1'), 27.3049178437, 1e-6)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You use the same syntax when working with the independent variables of your problem. Independent variables can be used as design variables by a [Driver](../../../_srcdocs/packages/core/driver) or set directly by a user. OpenMDAO requires that every input variable must have a source.  The ultimate source for any flow of data in an OpenMDAO model is a special component, [IndepVarComp](../../../_srcdocs/packages/core/indepvarcomp), that does not have any inputs.  You can leave some of your inputs unconnected and OpenMDAO will automatically create an [IndepVarComp](../../../_srcdocs/packages/core/indepvarcomp) called `_auto_ivc` with an output that connects to each input, or you can create your own `IndepVarComp` manually. For example, consider our paraboloid tutorial problem which has two independent variables: `x` and `y`.\n",
    "\n",
    "These could be defined manually and set as follows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "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_src53\", get_code(\"openmdao.test_suite.components.paraboloid.Paraboloid\"), display=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    ":::{Admonition} `Paraboloid` class definition \n",
    ":class: dropdown\n",
    "\n",
    "{glue:}`code_src53`\n",
    ":::"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from openmdao.test_suite.components.paraboloid import Paraboloid\n",
    "\n",
    "prob = om.Problem()\n",
    "\n",
    "model = prob.model\n",
    "model.add_subsystem('p1', om.IndepVarComp('x', 0.0), promotes=['x'])\n",
    "model.add_subsystem('p2', om.IndepVarComp('y', 0.0), promotes=['y'])\n",
    "model.add_subsystem('comp', Paraboloid(), promotes=['x', 'y', 'f_xy'])\n",
    "\n",
    "prob.setup()\n",
    "\n",
    "prob['x'] = 2.\n",
    "prob['y'] = 10.\n",
    "prob.run_model()\n",
    "\n",
    "print(prob.get_val('f_xy'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert_near_equal(prob.get_val('f_xy'), 214.0, 1e-6)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "or, the inputs `x` and `y` could be left unconnected and OpenMDAO would connect them to\n",
    "outputs on `_auto_ivc`.  The names of the output variables on `_auto_ivc` are sequentially\n",
    "named as they are created and are of the form `v0`, `v1`, etc., but you don't really need to know\n",
    "those names.  You can just interact with the inputs that those outputs are connected to\n",
    "and the framework will ensure that the proper values are set into the outputs (and into any other\n",
    "inputs connected to the same output).  Here's what the paraboloid tutorial problem looks like\n",
    "without declaring the [IndepVarComp](../../../_srcdocs/packages/core/indepvarcomp):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from openmdao.test_suite.components.paraboloid import Paraboloid\n",
    "\n",
    "prob = om.Problem()\n",
    "\n",
    "prob.model.add_subsystem('comp', Paraboloid(), promotes=['x', 'y', 'f_xy'])\n",
    "\n",
    "prob.setup()\n",
    "\n",
    "prob.set_val('x', 2.)\n",
    "prob.set_val('y', 10.)\n",
    "prob.run_model()\n",
    "\n",
    "print(prob.get_val('f_xy'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert_near_equal(prob.get_val('f_xy'), 214.0, 1e-6)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As we said above, outputs are always referenced via their promoted name. So if you built the Sellar problem using connections (see [Sellar](../../../basic_user_guide/multidisciplinary_optimization/sellar)), instead of promoting everything, then you would access the variables like this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "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_src54\", get_code(\"openmdao.test_suite.components.sellar.SellarDerivativesConnected\"), display=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    ":::{Admonition} `SellarDerivativesConnected` class definition \n",
    ":class: dropdown\n",
    "\n",
    "{glue:}`code_src54`\n",
    ":::"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from openmdao.test_suite.components.sellar import SellarDerivativesConnected\n",
    "\n",
    "prob = om.Problem(model= SellarDerivativesConnected())\n",
    "prob.model.nonlinear_solver = om.NonlinearBlockGS()\n",
    "\n",
    "prob.setup()\n",
    "\n",
    "prob.set_val('x', 2.75)\n",
    "\n",
    "prob.run_model()\n",
    "\n",
    "print(prob.get_val('x'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(prob.get_val('d1.y1'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert_near_equal(prob.get_val('x'), 2.75, 1e-6)\n",
    "assert_near_equal(prob.get_val('d1.y1'), 27.3049178437, 1e-6)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Working with Array Variables\n",
    "\n",
    "When you have an array variable, for convenience we allow you to set the value with any\n",
    "properly-sized array, list, or tuple. In other words, the shape of the list has to match the shape of the actual data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "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_src55\", get_code(\"openmdao.test_suite.components.sellar.SellarDerivatives\"), display=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    ":::{Admonition} `SellarDerivatives` class definition \n",
    ":class: dropdown\n",
    "\n",
    "{glue:}`code_src55`\n",
    ":::"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from openmdao.test_suite.components.sellar import SellarDerivatives\n",
    "import numpy as np \n",
    "\n",
    "prob = om.Problem(model=SellarDerivatives())\n",
    "prob.model.nonlinear_solver = om.NonlinearBlockGS()\n",
    "\n",
    "prob.setup()\n",
    "\n",
    "# default value from the class definition\n",
    "print(prob.get_val('x'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert_near_equal(prob.get_val('x'), 1.0, 1e-6)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "prob.set_val('x', 2.75)\n",
    "print(prob.get_val('x'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-output",
     "remove-input"
    ]
   },
   "outputs": [],
   "source": [
    "assert_near_equal(prob.get_val('x'), 2.75, 1e-6)\n",
    "assert_near_equal(prob.get_val('z'), [5.0, 2.0], 1e-6)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# default value from the class definition\n",
    "print(prob.get_val('z'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "prob.set_val('z', [1.5, 1.5])\n",
    "print(prob.get_val('z'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert_near_equal(prob.get_val('z'), [1.5, 1.5], 1e-6)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "prob.run_model()\n",
    "print(prob.get_val('y1'))\n",
    "print(prob.get_val('y2'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert_near_equal(prob.get_val('z'), [1.5, 1.5], 1e-6)\n",
    "assert_near_equal(prob.get_val('y1'), 5.43379016853, 1e-6)\n",
    "assert_near_equal(prob.get_val('y2'), 5.33104915618, 1e-6)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "prob.set_val('z', np.array([2.5, 2.5])) # for convenience we convert the list to an array.\n",
    "print(prob.get_val('z'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert_near_equal(prob.get_val('z'), [2.5, 2.5], 1e-6)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "prob.run_model()\n",
    "print(prob.get_val('y1'))\n",
    "print(prob.get_val('y2'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert_near_equal(prob.get_val('y1'), 9.87161739688, 1e-6)\n",
    "assert_near_equal(prob.get_val('y2'), 8.14191301549, 1e-6)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "prob = om.Problem(model=SellarDerivatives())\n",
    "prob.model.nonlinear_solver = om.NonlinearBlockGS()\n",
    "\n",
    "prob.setup()\n",
    "\n",
    "assert_near_equal(prob.get_val('x'), 1.0, 1e-6)\n",
    "\n",
    "prob.set_val('x', 2.75)\n",
    "assert_near_equal(prob.get_val('x'), 2.75, 1e-6)\n",
    "\n",
    "# default value from the class definition\n",
    "assert_near_equal(prob.get_val('z'), [5.0, 2.0], 1e-6)\n",
    "\n",
    "prob.set_val('z', [1.5, 1.5])\n",
    "assert_near_equal(prob.get_val('z'), [1.5, 1.5], 1e-6)\n",
    "\n",
    "prob.run_model()\n",
    "assert_near_equal(prob.get_val('y1'), 5.43379016853, 1e-6)\n",
    "assert_near_equal(prob.get_val('y2'), 5.33104915618, 1e-6)\n",
    "\n",
    "prob.set_val('z', np.array([2.5, 2.5])) # for convenience we convert the list to an array.\n",
    "assert_near_equal(prob.get_val('z'), [2.5, 2.5], 1e-6)\n",
    "\n",
    "prob.run_model()\n",
    "assert_near_equal(prob.get_val('y1'), 9.87161739688, 1e-6)\n",
    "assert_near_equal(prob.get_val('y2'), 8.14191301549, 1e-6)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "prob = om.Problem()\n",
    "model = prob.model\n",
    "model.add_subsystem(name='indeps',\n",
    "                    subsys=om.IndepVarComp(name='X_c', shape=(3, 1)))\n",
    "prob.setup()\n",
    "\n",
    "new_val = -5*np.ones((3, 1))\n",
    "prob['indeps.X_c'] = new_val\n",
    "prob.final_setup()\n",
    "\n",
    "print(prob['indeps.X_c'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "new_val = 2.5*np.ones(3)\n",
    "prob['indeps.X_c'][:, 0] = new_val\n",
    "prob.final_setup()\n",
    "\n",
    "print(prob['indeps.X_c'])\n",
    "print(prob['indeps.X_c'][:, 0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert_near_equal(prob['indeps.X_c'], new_val.reshape((3, 1)), 1e-10)\n",
    "assert_near_equal(prob['indeps.X_c'][:, 0], new_val, 1e-10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Residuals\n",
    "\n",
    "If you want to look at the residual values associated with any particular output variable, you will reference them using the same naming conventions the outputs. Also like outputs, you will be given the residuals in the unscaled dimensional form."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from openmdao.test_suite.components.sellar import SellarDerivatives\n",
    "\n",
    "prob = om.Problem(model=SellarDerivatives())\n",
    "prob.model.nonlinear_solver = om.NonlinearBlockGS()\n",
    "\n",
    "prob.setup()\n",
    "\n",
    "prob.set_val('z', [1.5, 1.5])\n",
    "prob.run_model()\n",
    "\n",
    "inputs, outputs, residuals = prob.model.get_nonlinear_vectors()\n",
    "\n",
    "print(residuals['y1'])\n",
    "print(residuals['y2'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert(residuals['y1'] < 1e-6)\n",
    "assert(residuals['y2'] < 1e-6)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Inputs\n",
    "\n",
    "You can get or set the value of an input variable using either its promoted name or its absolute\n",
    "name. If you reference it by its promoted name, however, and that\n",
    "input is connected to an output because the input and output are promoted to the same name, then\n",
    "the promoted name will be interpreted as that of the output, and the units will be assumed to be\n",
    "those of the output as well.  If the input has not been connected to an output then the framework\n",
    "will connect it automatically to an output of `_auto_ivc`.  In this case, setting or getting using\n",
    "the input name will cause the framework to assume the units are those of the input, assuming\n",
    "there is no abiguity in units for example.\n",
    "\n",
    "### Connected Inputs Without a Source\n",
    "\n",
    "If multiple inputs have been promoted to the same name but *not* connected manually to an output or promoted\n",
    "to the same name as an output, then again the framework will connect all of those inputs to an\n",
    "`_auto_ivc` output.  If, however, there is any difference between the units or values of any of those inputs,\n",
    "then you must tell the framework what units and/or values to use when creating the corresponding\n",
    "`_auto_ivc` output.  You do this by calling the `set_input_defaults` function using the promoted\n",
    "input name on a Group that contains all of the promoted inputs.\n",
    "\n",
    "```{eval-rst}\n",
    "    .. automethod:: openmdao.core.group.Group.set_input_defaults\n",
    "        :noindex:\n",
    "```\n",
    "\n",
    "Below is an example of what you'll see if you do *not* call `set_input_defaults` to disambiguate\n",
    "your units and/or values:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "prob = om.Problem()\n",
    "prob.model.add_subsystem('C1', om.ExecComp('y=x*2.',\n",
    "                                             x={'val': 1.0, 'units': 'ft'},\n",
    "                                             y={'val': 0.0, 'units': 'ft'}),\n",
    "                         promotes=['x'])\n",
    "prob.model.add_subsystem('C2', om.ExecComp('y=x*3.',\n",
    "                                             x={'val': 1.0, 'units': 'inch'},\n",
    "                                             y={'val': 0.0, 'units': 'inch'}),\n",
    "                         promotes=['x'])\n",
    "\n",
    "try:\n",
    "    prob.setup()\n",
    "except RuntimeError as err:\n",
    "    print(str(err))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output",
     "allow-assert"
    ]
   },
   "outputs": [],
   "source": [
    "try:\n",
    "    prob.setup()\n",
    "except RuntimeError as err:\n",
    "    print(str(err))\n",
    "    assert(str(err) == \"<model> <class Group>: The following inputs, ['C1.x', 'C2.x'], promoted to 'x', are connected but their metadata entries ['units', 'val'] differ. Call <group>.set_input_defaults('x', units=?, val=?), where <group> is the model to remove the ambiguity.\")\n",
    "else:\n",
    "    raise RuntimeError(\"Exception expected.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The next example shows a successful run after calling `set_input_defaults`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "prob = om.Problem()\n",
    "G1 = prob.model.add_subsystem('G1', om.Group())\n",
    "G1.add_subsystem('C1', om.ExecComp('y=x*2.',\n",
    "                                    x={'val': 1.0, 'units': 'cm'},\n",
    "                                    y={'val': 0.0, 'units': 'cm'}),\n",
    "                 promotes=['x'])\n",
    "G1.add_subsystem('C2', om.ExecComp('y=x*3.',\n",
    "                                    x={'val': 1.0, 'units': 'mm'},\n",
    "                                    y={'val': 0.0, 'units': 'mm'}),\n",
    "                 promotes=['x'])\n",
    "\n",
    "# units and value to use for the _auto_ivc output are ambiguous.  This fixes that.\n",
    "G1.set_input_defaults('x', units='m', val=1.0)\n",
    "\n",
    "prob.setup()\n",
    "\n",
    "# set G1.x to 2.0 m, based on the units we gave in the set_input_defaults call\n",
    "prob.set_val('G1.x', 2.)\n",
    "\n",
    "prob.run_model()\n",
    "\n",
    "# we gave 'G1.x' units of 'm' in the set_input_defaults call\n",
    "print(prob.get_val('G1.x'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# using absolute value will give us the value of the input C1.x, in its units of 'cm'\n",
    "print(prob.get_val('G1.C1.x'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# using absolute value will give us the value of the input C2.x, in its units of 'mm'\n",
    "print(prob.get_val('G1.C2.x'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert_near_equal(prob.get_val('G1.x'), 2.0, 1e-6)\n",
    "assert_near_equal(prob.get_val('G1.C1.x'), 200.0, 1e-6)\n",
    "assert_near_equal(prob.get_val('G1.C2.x'), 2000.0, 1e-6)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Another possible scenario is to have multiple inputs promoted to the same name when those inputs have\n",
    "different units, but then connecting them manually to an output using the `connect` function.\n",
    "In this case, the framework will not raise an exception during setup if `set_input_defaults` was not\n",
    "called as it does in the case of multiple promoted inputs that connected to `_auto_ivc`.  However,\n",
    "if the user attempts to set or get the input using the promoted name, the framework *will* raise an\n",
    "exception if `set_input_defaults` has not been called to disambiguate the units of the promoted\n",
    "input.  The reason for this difference is that in the unconnected case, the framework won't know\n",
    "what value and units to assign to the `_auto_ivc` output if they're ambiguous.  In the manually\n",
    "connected case, the value and units of the output have already been supplied by the user, and\n",
    "the only time that there's an ambiguity is if the user tries to access the inputs using their\n",
    "promoted name.\n",
    "\n",
    "Specifying Units\n",
    "\n",
    "You can also set an input or request the value of any variable in a different unit than its declared\n",
    "unit, and OpenMDAO will\n",
    "perform the conversion for you. This is done with the `Problem` methods `get_val` and `set_val`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "prob = om.Problem()\n",
    "prob.model.add_subsystem('comp', om.ExecComp('y=x+1.',\n",
    "                                             x={'val': 100.0, 'units': 'cm'},\n",
    "                                             y={'units': 'm'}))\n",
    "\n",
    "prob.setup()\n",
    "prob.run_model()\n",
    "\n",
    "print(prob.get_val('comp.x'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(prob.get_val('comp.x', 'm'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "prob.set_val('comp.x', 10.0, 'mm')\n",
    "print(prob.get_val('comp.x'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(prob.get_val('comp.x', 'm'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "prob = om.Problem()\n",
    "prob.model.add_subsystem('comp', om.ExecComp('y=x+1.',\n",
    "                                             x={'val': 100.0, 'units': 'cm'},\n",
    "                                             y={'units': 'm'}))\n",
    "\n",
    "prob.setup()\n",
    "prob.run_model()\n",
    "\n",
    "assert_near_equal(prob.get_val('comp.x'), 100, 1e-6)\n",
    "assert_near_equal(prob.get_val('comp.x', 'm'), 1.0, 1e-6)\n",
    "prob.set_val('comp.x', 10.0, 'mm')\n",
    "assert_near_equal(prob.get_val('comp.x'), 1.0, 1e-6)\n",
    "assert_near_equal(prob.get_val('comp.x', 'm'), 1.0e-2, 1e-6)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When dealing with arrays, you can set or get specific indices or index ranges by adding the \"indices\"\n",
    "argument to the calls:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "prob = om.Problem()\n",
    "prob.model.add_subsystem('comp', om.ExecComp('y=x+1.',\n",
    "                                             x={'val': np.array([100.0, 33.3]), 'units': 'cm'},\n",
    "                                             y={'shape': (2, ), 'units': 'm'}))\n",
    "\n",
    "prob.setup()\n",
    "prob.run_model()\n",
    "\n",
    "print(prob.get_val('comp.x'))\n",
    "print(prob.get_val('comp.x', 'm'))\n",
    "print(prob.get_val('comp.x', 'km', indices=[0]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "prob.set_val('comp.x', 10.0, 'mm')\n",
    "print(prob.get_val('comp.x'))\n",
    "print(prob.get_val('comp.x', 'm', indices=0))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "prob.set_val('comp.x', 50.0, 'mm', indices=[1])\n",
    "print(prob.get_val('comp.x'))\n",
    "print(prob.get_val('comp.x', 'm', indices=1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "prob = om.Problem()\n",
    "prob.model.add_subsystem('comp', om.ExecComp('y=x+1.',\n",
    "                                             x={'val': np.array([100.0, 33.3]), 'units': 'cm'},\n",
    "                                             y={'shape': (2, ), 'units': 'm'}))\n",
    "\n",
    "prob.setup()\n",
    "prob.run_model()\n",
    "\n",
    "assert_near_equal(prob.get_val('comp.x'), np.array([100, 33.3]), 1e-6)\n",
    "assert_near_equal(prob.get_val('comp.x', 'm'), np.array([1.0, 0.333]), 1e-6)\n",
    "assert_near_equal(prob.get_val('comp.x', 'km', indices=[0]), 0.001, 1e-6)\n",
    "\n",
    "prob.set_val('comp.x', 10.0, 'mm')\n",
    "assert_near_equal(prob.get_val('comp.x'), np.array([1.0, 1.0]), 1e-6)\n",
    "assert_near_equal(prob.get_val('comp.x', 'm', indices=0), 1.0e-2, 1e-6)\n",
    "\n",
    "prob.set_val('comp.x', 50.0, 'mm', indices=[1])\n",
    "assert_near_equal(prob.get_val('comp.x'), np.array([1.0, 5.0]), 1e-6)\n",
    "assert_near_equal(prob.get_val('comp.x', 'm', indices=1), 5.0e-2, 1e-6)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "An alternate method of specifying the indices is by making use of the `slicer` object. This\n",
    "object serves as a helper function allowing the user to specify the indices value using the same syntax as you would when accessing a numpy array. This example shows that usage."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "prob = om.Problem()\n",
    "prob.model.add_subsystem('comp', om.ExecComp('y=x+1.',\n",
    "                                             x={'val': np.array([[1., 2.], [3., 4.]]), },\n",
    "                                             y={'shape': (2, 2), }))\n",
    "\n",
    "prob.setup()\n",
    "prob.run_model()\n",
    "\n",
    "print(prob.get_val('comp.x', indices=om.slicer[:, 0]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(prob.get_val('comp.x', indices=om.slicer[0, 1]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(prob.get_val('comp.x', indices=om.slicer[1, -1]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "prob.set_val('comp.x', [5., 6.], indices=om.slicer[:,0])\n",
    "print(prob.get_val('comp.x', indices=om.slicer[:, 0]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "prob.run_model()\n",
    "print(prob.get_val('comp.y', indices=om.slicer[:, 0]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "prob = om.Problem()\n",
    "prob.model.add_subsystem('comp', om.ExecComp('y=x+1.',\n",
    "                                             x={'val': np.array([[1., 2.], [3., 4.]]), },\n",
    "                                             y={'shape': (2, 2), }))\n",
    "\n",
    "prob.setup()\n",
    "prob.run_model()\n",
    "\n",
    "assert_near_equal(prob.get_val('comp.x', indices=om.slicer[:, 0]), [1., 3.], 1e-6)\n",
    "assert_near_equal(prob.get_val('comp.x', indices=om.slicer[0, 1]), 2., 1e-6)\n",
    "assert_near_equal(prob.get_val('comp.x', indices=om.slicer[1, -1]), 4., 1e-6)\n",
    "\n",
    "prob.set_val('comp.x', [5., 6.], indices=om.slicer[:,0])\n",
    "assert_near_equal(prob.get_val('comp.x', indices=om.slicer[:, 0]), [5., 6.], 1e-6)\n",
    "prob.run_model()\n",
    "assert_near_equal(prob.get_val('comp.y', indices=om.slicer[:, 0]), [6., 7.], 1e-6)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Retrieving Remote Variables\n",
    "\n",
    "If you're running under MPI, the `Problem.get_val` method also has a *get_remote* arg that allows\n",
    "you to get the value of a variable even if it's not local to the current MPI process.  For example,\n",
    "the code below will retrieve the value of `foo.bar.x` in all processes, whether the variable is\n",
    "local or not.\n",
    "\n",
    "```python\n",
    "    val = prob.get_val('foo.bar.x', get_remote=True)\n",
    "```\n",
    "\n",
    "```{Warning}\n",
    "If `get_remote` is True, `get_val` makes a collective MPI call, so make sure to call it\n",
    "in *all* ranks of the Problem's MPI communicator.  Otherwise, collective calls made\n",
    "in different ranks will get out of sync and result in cryptic MPI errors.\n",
    "```\n",
    "\n",
    "\n",
    "## Testing if a Variable or System is Local\n",
    "\n",
    "If you want to know if a given variable or system is local to the current process, the\n",
    "`Problem.is_local` method will tell you.  For example:\n",
    "\n",
    "```python\n",
    "    if prob.is_local('foo.bar.x'):\n",
    "        print(\"foo.bar.x is local!\")\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.1"
  },
  "orphan": true
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
