{
 "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": [
    "# VectorMagnitudeComp\n",
    "\n",
    "`VectorMagnitudeComp` computes the magnitude (L2 norm) of a single input of some given length.\n",
    "It may be vectorized to provide the result at one or more points simultaneously.\n",
    "\n",
    "$$\n",
    "    \\lvert a_i \\rvert = \\sqrt{\\bar{a}_i \\cdot \\bar{a}_i}\n",
    "$$\n",
    "\n",
    "## VectorMagnitudeComp Options\n",
    "\n",
    "The default `vec_size` is 1, providing the magnitude of $a$ at a singlepoint.  The length of $a$ is provided by option `length`.\n",
    "\n",
    "Other options for VectorMagnitudeComp allow the user to rename the input variable $a$ and the output $a\\_mag$, as well as specifying their units."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input"
    ]
   },
   "outputs": [],
   "source": [
    "import openmdao.api as om\n",
    "om.show_options_table(\"openmdao.components.vector_magnitude_comp.VectorMagnitudeComp\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## VectorMagnitudeComp Constructor\n",
    "\n",
    "The call signature for the `VectorMagnitudeComp` constructor is:\n",
    "\n",
    "```{eval-rst}\n",
    "    .. automethod:: openmdao.components.vector_magnitude_comp.VectorMagnitudeComp.__init__\n",
    "        :noindex:\n",
    "```\n",
    "\n",
    "## VectorMagnitudeComp Usage\n",
    "\n",
    "There are often situations when numerous magnitudes need to be computed, essentially in parallel.\n",
    "You can reduce the number of components required by having one `VectorMagnitudeComp` perform multiple operations.\n",
    "This is also convenient when the different operations have common inputs.\n",
    "\n",
    "The `add_magnitude` method is used to create additional magnitude calculations after instantiation.\n",
    "\n",
    "```{eval-rst}\n",
    "    .. automethod:: openmdao.components.vector_magnitude_comp.VectorMagnitudeComp.add_magnitude\n",
    "       :noindex:\n",
    "```\n",
    "\n",
    "## VectorMagnitudeComp Example\n",
    "\n",
    "In the following example VectorMagnitudeComp is used to compute the radius vector magnitude\n",
    "given a radius 3-vector at 100 points simultaneously. Note the use of `in_name` and `mag_name` to assign names to the inputs and outputs. Units are assigned using `units`.  The units of the output magnitude are the same as those for the input."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "allow-assert"
    ]
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import openmdao.api as om\n",
    "\n",
    "n = 100\n",
    "\n",
    "p = om.Problem()\n",
    "\n",
    "comp = om.VectorMagnitudeComp(vec_size=n, length=3,\n",
    "                              in_name='r', mag_name='r_mag', units='km')\n",
    "\n",
    "p.model.add_subsystem(name='vec_mag_comp', subsys=comp,\n",
    "                      promotes_inputs=[('r', 'pos')])\n",
    "\n",
    "p.setup()\n",
    "\n",
    "p.set_val('pos', 1.0 + np.random.rand(n, 3))\n",
    "\n",
    "p.run_model()\n",
    "\n",
    "# Verify the results against numpy.dot in a for loop.\n",
    "expected = []\n",
    "for i in range(n):\n",
    "    a_i = p.get_val('pos')[i, :]\n",
    "    expected.append(np.sqrt(np.dot(a_i, a_i)))\n",
    "\n",
    "    actual_i = p.get_val('vec_mag_comp.r_mag')[i]\n",
    "    rel_error = np.abs(expected[i] - actual_i)/actual_i\n",
    "    assert rel_error < 1e-9, f\"Relative error: {rel_error}\"\n",
    "\n",
    "print(p.get_val('vec_mag_comp.r_mag'))"
   ]
  },
  {
   "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(p.get_val('vec_mag_comp.r_mag'), np.array(expected))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## VectorMagnitudeComp Example with Multiple Magnitudes\n",
    "\n",
    "Note that, when defining multiple magnitudes, an input name in one call to `add_magnitude` may not be an output name in another call, and vice-versa."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "allow_assert",
     "allow-assert"
    ]
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "n = 100\n",
    "\n",
    "p = om.Problem()\n",
    "\n",
    "comp = om.VectorMagnitudeComp(vec_size=n, length=3,\n",
    "                              in_name='r', mag_name='r_mag', units='km')\n",
    "\n",
    "comp.add_magnitude(vec_size=n, length=3,\n",
    "                   in_name='b', mag_name='b_mag', units='ft')\n",
    "\n",
    "p.model.add_subsystem(name='vec_mag_comp', subsys=comp,\n",
    "                      promotes_inputs=['r', 'b'])\n",
    "\n",
    "p.setup()\n",
    "\n",
    "p.set_val('r', 1.0 + np.random.rand(n, 3))\n",
    "p.set_val('b', 1.0 + np.random.rand(n, 3))\n",
    "\n",
    "p.run_model()\n",
    "\n",
    "# Verify the results against numpy.dot in a for loop.\n",
    "expected_r = []\n",
    "expected_b = []\n",
    "for i in range(n):\n",
    "    a_i = p.get_val('r')[i, :]\n",
    "    expected_r.append(np.sqrt(np.dot(a_i, a_i)))\n",
    "\n",
    "    actual_i = p.get_val('vec_mag_comp.r_mag')[i]\n",
    "    rel_error = np.abs(expected_r[i] - actual_i)/actual_i\n",
    "    assert rel_error < 1e-9, f\"Relative error: {rel_error}\"\n",
    "\n",
    "    b_i = p.get_val('b')[i, :]\n",
    "    expected_b.append(np.sqrt(np.dot(b_i, b_i)))\n",
    "\n",
    "    actual_i = p.get_val('vec_mag_comp.b_mag')[i]\n",
    "    rel_error = np.abs(expected_b[i] - actual_i)/actual_i\n",
    "    assert rel_error < 1e-9, f\"Relative error: {rel_error}\"\n",
    "\n",
    "print(p.get_val('vec_mag_comp.r_mag'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(p.get_val('vec_mag_comp.b_mag'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert_near_equal(p.get_val('vec_mag_comp.r_mag'), np.array(expected_r))\n",
    "assert_near_equal(p.get_val('vec_mag_comp.b_mag'), np.array(expected_b))"
   ]
  }
 ],
 "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
}
