{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "7fc4d3a7",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:10.281632Z",
     "iopub.status.busy": "2026-10-02T14:41:10.281269Z",
     "iopub.status.idle": "2026-10-02T14:41:10.286312Z",
     "shell.execute_reply": "2026-10-02T14:41:10.285500Z"
    },
    "papermill": {
     "duration": 0.00808,
     "end_time": "2026-10-02T14:41:10.286958+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:10.278878+00:00",
     "status": "completed"
    },
    "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",
   "id": "92038a6c",
   "metadata": {
    "papermill": {
     "duration": 0.001246,
     "end_time": "2026-10-02T14:41:10.289788+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:10.288542+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "# CrossProductComp\n",
    "\n",
    "`CrossProductComp` performs a cross product between two 3-vector inputs.  It may be vectorized to provide the result at one or more points simultaneously.\n",
    "\n",
    "$$\n",
    "    c_i = \\bar{a}_i \\times \\bar{b}_i\n",
    "$$\n",
    "\n",
    "The first dimension of the inputs holds the vectorized dimension.\n",
    "The default `vec_size` is 1, providing the cross product of $a$ and $b$ at a single\n",
    "point.  The lengths of $a$ and $b$ at each point must be 3.\n",
    "\n",
    "The shape of $a$ and $b$ will always be `(vec_size, 3)`, but the connection rules\n",
    "of OpenMDAO allow the incoming connection to have shape `(3,)` when `vec_size` is 1, since\n",
    "the storage order of the underlying data is the same.  The output vector `c` of\n",
    "CrossProductComp will always have shape `(vec_size, 3)`.\n",
    "\n",
    "## CrossProductComp Options\n",
    "\n",
    "Options for CrossProductComp allow the user to rename the input variables $a$ and $b$ and the output $c$, as well as specifying their units."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "55519766",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:10.293305Z",
     "iopub.status.busy": "2026-10-02T14:41:10.293125Z",
     "iopub.status.idle": "2026-10-02T14:41:11.661006Z",
     "shell.execute_reply": "2026-10-02T14:41:11.660402Z"
    },
    "papermill": {
     "duration": 1.37056,
     "end_time": "2026-10-02T14:41:11.661530+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:10.290970+00:00",
     "status": "completed"
    },
    "tags": [
     "remove-input"
    ]
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "<!DOCTYPE html>\n",
       "<html lang=\"en\">\n",
       "<head>\n",
       "    <style>\n",
       "        h2 {\n",
       "            text-align: center;\n",
       "        }\n",
       "    </style>\n",
       "</head>\n",
       "<body>\n",
       "    <h2></h2>\n",
       "        <table style=\"border: 1px solid #999; border-collapse: collapse;\">\n",
       "        <tr><th style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; background-color: #E9E9E9; text-align: left;\">Option</th><th style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; background-color: #E9E9E9; text-align: left;\">Default</th><th style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; background-color: #E9E9E9; text-align: left;\">Acceptable Values</th><th style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; background-color: #E9E9E9; text-align: left;\">Acceptable Types</th><th style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; background-color: #E9E9E9; text-align: left;\">Description</th></tr>\n",
       "       <tr style=\"background-color: ghostwhite;\"><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">a_name</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">a</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">N/A</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">[&#x27;str&#x27;]</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">The variable name for vector a.</td></tr>\n",
       "       <tr style=\"background-color: #F3F3F3;\"><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">a_units</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">N/A</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">N/A</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">[&#x27;str&#x27;]</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">The units for vector a.</td></tr>\n",
       "       <tr style=\"background-color: ghostwhite;\"><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">always_opt</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">False</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">[True, False]</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">[&#x27;bool&#x27;]</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">If True, force nonlinear operations on this component to be included in the optimization loop even if this component is not relevant to the design variables and responses.</td></tr>\n",
       "       <tr style=\"background-color: #F3F3F3;\"><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">b_name</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">b</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">N/A</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">[&#x27;str&#x27;]</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">The variable name for vector b.</td></tr>\n",
       "       <tr style=\"background-color: ghostwhite;\"><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">b_units</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">N/A</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">N/A</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">[&#x27;str&#x27;]</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">The units for vector b.</td></tr>\n",
       "       <tr style=\"background-color: #F3F3F3;\"><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">c_name</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">c</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">N/A</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">[&#x27;str&#x27;]</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">The variable name for vector c.</td></tr>\n",
       "       <tr style=\"background-color: ghostwhite;\"><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">c_units</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">N/A</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">N/A</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">[&#x27;str&#x27;]</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">The units for vector c.</td></tr>\n",
       "       <tr style=\"background-color: #F3F3F3;\"><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">default_shape</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">(1,)</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">N/A</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">[&#x27;tuple&#x27;]</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">Default shape for variables that do not set val to a non-scalar value or set shape, shape_by_conn, copy_shape, or compute_shape. Default is (1,).</td></tr>\n",
       "       <tr style=\"background-color: ghostwhite;\"><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">derivs_method</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">N/A</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">[&#x27;jax&#x27;, &#x27;cs&#x27;, &#x27;fd&#x27;, None]</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">N/A</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">The method to use for computing derivatives</td></tr>\n",
       "       <tr style=\"background-color: #F3F3F3;\"><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">distributed</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">False</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">[True, False]</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">[&#x27;bool&#x27;]</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">If True, set all variables in this component as distributed across multiple processes</td></tr>\n",
       "       <tr style=\"background-color: ghostwhite;\"><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">run_root_only</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">False</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">[True, False]</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">[&#x27;bool&#x27;]</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">If True, call compute, compute_partials, linearize, apply_linear, apply_nonlinear, solve_linear, solve_nonlinear, and compute_jacvec_product only on rank 0 and broadcast the results to the other ranks.</td></tr>\n",
       "       <tr style=\"background-color: #F3F3F3;\"><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">use_jit</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">True</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">[True, False]</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">[&#x27;bool&#x27;]</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">If True, attempt to use jit on compute_primal, assuming jax or some other AD package capable of jitting is active.</td></tr>\n",
       "       <tr style=\"background-color: ghostwhite;\"><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">vec_size</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">1</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">N/A</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">[&#x27;int&#x27;]</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">The number of points at which the cross product is computed</td></tr>\n",
       "    </table>\n",
       "</body>\n",
       "</html>\n"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import openmdao.api as om\n",
    "om.show_options_table(\"openmdao.components.cross_product_comp.CrossProductComp\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c00855f8",
   "metadata": {
    "papermill": {
     "duration": 0.00088,
     "end_time": "2026-10-02T14:41:11.663598+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:11.662718+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "## CrossProductComp Constructor\n",
    "\n",
    "The call signature for the `CrossProductComp` constructor is:\n",
    "\n",
    "```{eval-rst}\n",
    "    .. automethod:: openmdao.components.cross_product_comp.CrossProductComp.__init__\n",
    "        :noindex:\n",
    "```\n",
    "\n",
    "## CrossProductComp Usage\n",
    "\n",
    "There are often situations when numerous products need to be computed, essentially in parallel.\n",
    "You can reduce the number of components required by having one `CrossProductComp` perform multiple operations.\n",
    "This is also convenient when the different operations have common inputs.\n",
    "\n",
    "The `add_product` method is used to create additional products after instantiation.\n",
    "\n",
    "```{eval-rst}\n",
    "    .. automethod:: openmdao.components.cross_product_comp.CrossProductComp.add_product\n",
    "       :noindex:\n",
    "```\n",
    "\n",
    "## CrossProductComp Example\n",
    "\n",
    "In the following example CrossProductComp is used to compute torque as the\n",
    "cross product of force ($F$) and radius ($r$) at 100 points simultaneously.\n",
    "Note the use of `a_name`, `b_name`, and `c_name` to assign names to the inputs and outputs.\n",
    "Units are assigned using `a_units`, `b_units`, and `c_units`.\n",
    "Note that no internal checks are performed to ensure that `c_units` are consistent\n",
    "with `a_units` and `b_units`.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "27421b8e",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:11.666266Z",
     "iopub.status.busy": "2026-10-02T14:41:11.666021Z",
     "iopub.status.idle": "2026-10-02T14:41:12.883052Z",
     "shell.execute_reply": "2026-10-02T14:41:12.882231Z"
    },
    "papermill": {
     "duration": 1.219476,
     "end_time": "2026-10-02T14:41:12.883974+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:11.664498+00:00",
     "status": "completed"
    },
    "tags": [
     "allow-assert"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1790952072.870789] [runnervm8df0l:6371 :0]        ib_iface.c:1269 UCX  ERROR mana_0: iface 0x5570c4a28800 failed to create UD QP TX wr:256 sge:6 inl:64 resp:0 RX wr:4096 sge:1 resp:0 failed: Operation not supported\n",
      "[1790952072.871019] [runnervm8df0l:6371 :0]      ucp_worker.c:1412 UCX  ERROR uct_iface_open(ud_verbs/mana_0:1) failed: Input/output error\n",
      "[[-8.37364803e-02  2.16320867e-01 -9.06059063e-02]\n",
      " [ 1.10815363e-01  7.03287006e-02 -6.56732664e-01]\n",
      " [-3.81875927e-01  1.46799881e-01  3.38641640e-01]\n",
      " [-7.56291417e-02  2.66839901e-01 -9.16212283e-02]\n",
      " [-1.30347239e-02 -8.90145437e-02  1.73824930e-02]\n",
      " [ 8.76564740e-03  5.29334368e-01 -1.85523437e-01]\n",
      " [ 6.60438287e-02 -9.69407456e-02  1.81493494e-01]\n",
      " [ 1.26416565e-04 -3.17697186e-02  1.35442348e-01]\n",
      " [-1.96886280e-01  9.19928194e-02  1.15383850e-01]\n",
      " [ 4.98981050e-02  1.25379854e-01 -1.38891019e-01]\n",
      " [-1.48594038e-01  1.43494619e-01 -2.94527112e-01]\n",
      " [ 1.11642372e-01 -2.56949456e-01  3.71293213e-01]\n",
      " [ 5.80214209e-02  1.24073755e-01 -1.53957745e-01]\n",
      " [ 2.19581952e-01  2.27415256e-01 -3.90943514e-01]\n",
      " [ 2.54987570e-01 -3.46410542e-01  4.57729660e-02]\n",
      " [ 2.18657800e-02 -1.17431560e-01  1.06874706e-01]\n",
      " [ 1.65884667e-01 -4.04852516e-01  3.11524562e-01]\n",
      " [ 3.13622661e-01 -3.72515287e-01  2.68620294e-01]\n",
      " [ 1.16682459e-01  1.01697379e-02 -1.12797236e-01]\n",
      " [-1.18111022e-01  4.17869032e-02  1.27500710e-01]\n",
      " [-1.15757280e-01  1.12122454e-01  5.82130972e-02]\n",
      " [-2.59304312e-01  2.00007201e-01  2.51275637e-02]\n",
      " [-1.34970018e-01  2.85872098e-01 -2.30068695e-01]\n",
      " [ 1.02321420e-01 -1.54955741e-01  9.81674752e-03]]\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[runnervm8df0l:06371] pml_ucx.c:313  Error: Failed to create UCP worker\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "import openmdao.api as om\n",
    "\n",
    "n = 24\n",
    "\n",
    "p = om.Problem()\n",
    "\n",
    "p.model.add_subsystem(name='cross_prod_comp',\n",
    "                      subsys=om.CrossProductComp(vec_size=n,\n",
    "                                                 a_name='r', b_name='F', c_name='torque',\n",
    "                                                 a_units='m', b_units='N', c_units='N*m'),\n",
    "                      promotes_inputs=['r', 'F'])\n",
    "\n",
    "p.setup()\n",
    "\n",
    "p.set_val('r', np.random.rand(n, 3))\n",
    "p.set_val('F', np.random.rand(n, 3))\n",
    "\n",
    "p.run_model()\n",
    "\n",
    "# Check the output in units of ft*lbf to ensure that our units work as expected.\n",
    "expected = []\n",
    "for i in range(n):\n",
    "    a_i = p.get_val('r')[i, :]\n",
    "    b_i = p.get_val('F')[i, :]\n",
    "    expected.append(np.cross(a_i, b_i) * 0.73756215)\n",
    "\n",
    "    actual_i = p.get_val('cross_prod_comp.torque', units='ft*lbf')[i]\n",
    "    rel_error = np.abs(expected[i] - actual_i)/actual_i\n",
    "    assert np.all(rel_error < 1e-8), f\"Relative error: {rel_error}\"\n",
    "\n",
    "print(p.get_val('cross_prod_comp.torque', units='ft*lbf'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "12b1f36b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:12.887937Z",
     "iopub.status.busy": "2026-10-02T14:41:12.887785Z",
     "iopub.status.idle": "2026-10-02T14:41:12.892093Z",
     "shell.execute_reply": "2026-10-02T14:41:12.891410Z"
    },
    "papermill": {
     "duration": 0.006855,
     "end_time": "2026-10-02T14:41:12.892543+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:12.885688+00:00",
     "status": "completed"
    },
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "np.float64(2.04537869907584e-09)"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from openmdao.utils.assert_utils import assert_near_equal\n",
    "\n",
    "assert_near_equal(p.get_val('cross_prod_comp.torque', units='ft*lbf'), np.array(expected), tolerance=1e-8)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a62739dd",
   "metadata": {
    "papermill": {
     "duration": 0.006471,
     "end_time": "2026-10-02T14:41:12.900104+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:12.893633+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "## DotProductComp Example with Multiple Products\n",
    "\n",
    "When defining multiple products:\n",
    "\n",
    "- An input name in one call to `add_product` may not be an output name in another call, and vice-versa.\n",
    "- The units and shape of variables used across multiple products must be the same in each one."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "d60706b7",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:12.903576Z",
     "iopub.status.busy": "2026-10-02T14:41:12.903448Z",
     "iopub.status.idle": "2026-10-02T14:41:12.913128Z",
     "shell.execute_reply": "2026-10-02T14:41:12.912465Z"
    },
    "papermill": {
     "duration": 0.012403,
     "end_time": "2026-10-02T14:41:12.913905+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:12.901502+00:00",
     "status": "completed"
    },
    "tags": [
     "allow-assert"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[ 5.39420338e-01 -5.27679823e-01  7.62122276e-02]\n",
      " [-4.16897261e-01  4.89412928e-01  2.13322687e-01]\n",
      " [-2.55644704e-01  1.87887887e-01  5.99171914e-01]\n",
      " [-4.80844975e-02  2.03534018e-01 -2.94102949e-01]\n",
      " [ 2.67874546e-01 -7.34877039e-01  2.11913429e-01]\n",
      " [-3.58426682e-01  7.57501941e-02  2.63215257e-02]\n",
      " [ 3.09468401e-01 -2.88329607e-01  2.75817887e-01]\n",
      " [-5.05339362e-01  2.54591527e-01  1.64740986e-01]\n",
      " [-1.14929535e-01  5.37064892e-01 -4.97357938e-01]\n",
      " [-6.61968334e-01  3.18501470e-02  3.47389982e-01]\n",
      " [-4.22171351e-01 -2.34917200e-01  4.58073710e-01]\n",
      " [ 5.25918045e-01 -6.17009233e-01  4.36110194e-01]\n",
      " [ 2.22264198e-01 -8.71431800e-02 -6.20686651e-02]\n",
      " [-2.25728862e-01  1.31583422e-01  6.23781266e-02]\n",
      " [ 2.86065996e-01 -7.18558956e-04 -1.68030235e-02]\n",
      " [-5.90733550e-02  1.79214865e-01 -1.31225354e-01]\n",
      " [-9.96864417e-02  4.45945802e-01 -1.44338585e-02]\n",
      " [ 3.31315466e-01  2.46914001e-01 -4.91735447e-01]\n",
      " [ 1.52668643e-01 -1.00785609e-01 -5.38916650e-01]\n",
      " [-1.49342583e-01  2.96160271e-02  7.48028438e-02]\n",
      " [ 6.56959918e-02  1.43694397e-01 -1.80248800e-01]\n",
      " [-8.45752671e-02 -7.30126291e-02  1.06783330e-01]\n",
      " [-4.76158678e-01  3.83939807e-01  2.97037709e-01]\n",
      " [-3.20925039e-01 -3.37558885e-01  7.00093521e-01]]\n"
     ]
    }
   ],
   "source": [
    "n = 24\n",
    "\n",
    "p = om.Problem()\n",
    "\n",
    "cpc = om.CrossProductComp(vec_size=n,\n",
    "                          a_name='r', b_name='F', c_name='torque',\n",
    "                          a_units='m', b_units='N', c_units='N*m')\n",
    "\n",
    "cpc.add_product(vec_size=n,\n",
    "                a_name='r', b_name='p', c_name='L',\n",
    "                a_units='m', b_units='kg*m/s', c_units='kg*m**2/s')\n",
    "\n",
    "p.model.add_subsystem(name='cross_prod_comp', subsys=cpc,\n",
    "                      promotes_inputs=['r', 'F', 'p'])\n",
    "\n",
    "p.setup()\n",
    "\n",
    "p.set_val('r', np.random.rand(n, 3))\n",
    "p.set_val('F', np.random.rand(n, 3))\n",
    "p.set_val('p', np.random.rand(n, 3))\n",
    "\n",
    "p.run_model()\n",
    "\n",
    "# Check the output.\n",
    "expected_T = []\n",
    "expected_L = []\n",
    "for i in range(n):\n",
    "    a_i = p.get_val('r')[i, :]\n",
    "    b_i = p.get_val('F')[i, :]\n",
    "    expected_T.append(np.cross(a_i, b_i))\n",
    "\n",
    "    actual_i = p.get_val('cross_prod_comp.torque')[i]\n",
    "    rel_error = np.abs(expected_T[i] - actual_i)/actual_i\n",
    "    assert np.all(rel_error < 1e-8), f\"Relative error: {rel_error}\"\n",
    "\n",
    "    b_i = p.get_val('p')[i, :]\n",
    "    expected_L.append(np.cross(a_i, b_i))\n",
    "\n",
    "    actual_i = p.get_val('cross_prod_comp.L')[i]\n",
    "    rel_error = np.abs(expected_L[i] - actual_i)/actual_i\n",
    "    assert np.all(rel_error < 1e-8), f\"Relative error: {rel_error}\"\n",
    "\n",
    "print(p.get_val('cross_prod_comp.torque'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "e5d0c0cd",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:12.917756Z",
     "iopub.status.busy": "2026-10-02T14:41:12.917609Z",
     "iopub.status.idle": "2026-10-02T14:41:12.920443Z",
     "shell.execute_reply": "2026-10-02T14:41:12.919728Z"
    },
    "papermill": {
     "duration": 0.005166,
     "end_time": "2026-10-02T14:41:12.920884+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:12.915718+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[-0.02976415 -0.1346321   0.17520703]\n",
      " [-0.27229264  0.58952198  0.07570326]\n",
      " [-0.30623091 -0.12934774  0.81499972]\n",
      " [-0.25606549  0.15863341  0.03900362]\n",
      " [ 0.22806284 -0.56502059  0.13770608]\n",
      " [-0.05372327  0.3687119  -0.07623891]\n",
      " [ 0.23586482 -0.18977517 -0.37125858]\n",
      " [ 0.20539231 -0.31833756  0.11132083]\n",
      " [ 0.11385881  0.29644518 -0.28328328]\n",
      " [-0.38000296 -0.02971061  0.21790282]\n",
      " [-0.71896125  0.06685563  0.5944584 ]\n",
      " [ 0.56252114 -0.65527971  0.05167142]\n",
      " [ 0.48540713 -0.17097331 -0.47762516]\n",
      " [-0.23754146  0.1800353   0.03608386]\n",
      " [ 0.20501553  0.00450646 -0.05503295]\n",
      " [-0.587529    0.43483075 -0.01407459]\n",
      " [-0.19111024  0.09874218  0.0044351 ]\n",
      " [-0.48732466  0.34198887 -0.23578373]\n",
      " [ 0.2432161  -0.24359209  0.07011508]\n",
      " [-0.07513793 -0.00701538  0.03811882]\n",
      " [ 0.14246738 -0.12945128 -0.07339697]\n",
      " [-0.15223063  0.38830663 -0.24180632]\n",
      " [-0.66804135  0.08848046  0.48512098]\n",
      " [-0.51309516  0.13806604  0.35682369]]\n"
     ]
    }
   ],
   "source": [
    "print(p.get_val('cross_prod_comp.L'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "cdf1882c",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:12.923836Z",
     "iopub.status.busy": "2026-10-02T14:41:12.923646Z",
     "iopub.status.idle": "2026-10-02T14:41:12.927100Z",
     "shell.execute_reply": "2026-10-02T14:41:12.926516Z"
    },
    "papermill": {
     "duration": 0.00569,
     "end_time": "2026-10-02T14:41:12.927667+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:12.921977+00:00",
     "status": "completed"
    },
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "np.float64(0.0)"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "assert_near_equal(p.get_val('cross_prod_comp.torque'), np.array(expected_T), tolerance=1e-8)\n",
    "assert_near_equal(p.get_val('cross_prod_comp.L'), np.array(expected_L), tolerance=1e-8)"
   ]
  }
 ],
 "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.13.14"
  },
  "orphan": true,
  "papermill": {
   "default_parameters": {},
   "duration": 3.82649,
   "end_time": "2026-10-02T14:41:13.445303+00:00",
   "environment_variables": {},
   "exception": null,
   "input_path": "/home/runner/work/OpenMDAO/OpenMDAO/openmdao/docs/openmdao_book/features/building_blocks/components/cross_product_comp.ipynb",
   "output_path": "/home/runner/work/OpenMDAO/OpenMDAO/openmdao/docs/_executed_book/features/building_blocks/components/cross_product_comp.ipynb",
   "parameters": {},
   "start_time": "2026-10-02T14:41:09.618813+00:00",
   "version": "2.7.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}