{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "b48d3ce3",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:38.239475Z",
     "iopub.status.busy": "2026-10-02T14:41:38.239285Z",
     "iopub.status.idle": "2026-10-02T14:41:38.243759Z",
     "shell.execute_reply": "2026-10-02T14:41:38.243011Z"
    },
    "papermill": {
     "duration": 0.007296,
     "end_time": "2026-10-02T14:41:38.244358+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:38.237062+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]"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "4da90742",
   "metadata": {
    "papermill": {
     "duration": 0.025299,
     "end_time": "2026-10-02T14:41:38.271023+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:38.245724+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "# MetaModelUnStructuredComp\n",
    "\n",
    "`MetaModelUnStructuredComp` lets you quickly create a component with surrogate models\n",
    "used to compute the outputs based on unstructured training data. Generally, this is\n",
    "used to construct a low-computational-cost replacement for computationally\n",
    "expensive components.\n",
    "\n",
    "You can define `MetaModelUnStructuredComp` with as many inputs and outputs as you like,\n",
    "and you can also use a different surrogate model for each output.\n",
    "\n",
    "```{Note}\n",
    "What's the difference between `MetaModelUnStructuredComp` and a surrogate model? In\n",
    "OpenMDAO, \"surrogate model\" refers to the model for a single response, and\n",
    "`MetaModelUnStructuredComp` represents a collection of surrogate models trained at the\n",
    "same locations in the design space.\n",
    "```\n",
    "\n",
    "## MetaModelUnStructuredComp Options\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "4373b5c5",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:38.324274Z",
     "iopub.status.busy": "2026-10-02T14:41:38.324108Z",
     "iopub.status.idle": "2026-10-02T14:41:39.915141Z",
     "shell.execute_reply": "2026-10-02T14:41:39.914457Z"
    },
    "papermill": {
     "duration": 1.595223,
     "end_time": "2026-10-02T14:41:39.917262+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:38.322039+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;\">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;\">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;\">default_surrogate</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;SurrogateModel&#x27;, &#x27;NoneType&#x27;]</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">Surrogate that will be used for all outputs that don&#x27;t have a specific surrogate assigned to them.</td></tr>\n",
       "       <tr style=\"background-color: #F3F3F3;\"><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: ghostwhite;\"><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: #F3F3F3;\"><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: ghostwhite;\"><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: #F3F3F3;\"><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;\">Number of points that will be simultaneously predicted by the surrogate.</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.meta_model_unstructured_comp.MetaModelUnStructuredComp\")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "470c3003",
   "metadata": {
    "papermill": {
     "duration": 0.001608,
     "end_time": "2026-10-02T14:41:39.920265+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:39.918657+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "## MetaModelUnStructuredComp Constructor\n",
    "\n",
    "The call signature for the `MetaModelUnStructuredComp` constructor is:\n",
    "\n",
    "```{eval-rst}\n",
    "    .. automethod:: openmdao.components.meta_model_unstructured_comp.MetaModelUnStructuredComp.__init__\n",
    "        :noindex:\n",
    "```\n",
    "\n",
    "## Simple Example\n",
    "\n",
    "The following example demonstrates a simple `Problem` in which a\n",
    "`MetaModelUnStructuredComp` uses surrogates to mimic the sine and cosine functions.\n",
    "\n",
    "In this example, the `MetaModelUnStructuredComp` `trig` has a single input,\n",
    "`x`, and two outputs, `sin_x` and `cos_x`.\n",
    "\n",
    "`KrigingSurrogate` is given as the surrogate for the `sin_x` output.\n",
    "Although no surrogate has been given for the `cos_x` output, a\n",
    "`default_surrogate` is specified for the component. Any output which has\n",
    "not had a surrogate assigned will use one of the default type.\n",
    "If `default_surrogate` is not specified, then a surrogate must be\n",
    "given for all outputs.\n",
    "\n",
    "\n",
    "The first time a `MetaModelUnStructuredComp` runs, it will train the surrogates using the\n",
    "training data that has been provided, and then it will predict the output\n",
    "values. This training step only occurs on the first run."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "4a349453",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:39.999809Z",
     "iopub.status.busy": "2026-10-02T14:41:39.999296Z",
     "iopub.status.idle": "2026-10-02T14:41:41.264411Z",
     "shell.execute_reply": "2026-10-02T14:41:41.263630Z"
    },
    "papermill": {
     "duration": 1.268727,
     "end_time": "2026-10-02T14:41:41.265166+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:39.996439+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1790952101.246840] [runnervm8df0l:6727 :0]        ib_iface.c:1269 UCX  ERROR mana_0: iface 0x55c9082b5370 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",
      "[1790952101.247092] [runnervm8df0l:6727 :0]      ucp_worker.c:1412 UCX  ERROR uct_iface_open(ud_verbs/mana_0:1) failed: Input/output error\n",
      "[0.43161039]\n",
      "[-0.25241565]\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[runnervm8df0l:06727] pml_ucx.c:313  Error: Failed to create UCP worker\n"
     ]
    }
   ],
   "source": [
    "# create a MetaModelUnStructuredComp, specifying surrogates for the outputs\n",
    "import numpy as np\n",
    "import openmdao.api as om\n",
    "\n",
    "trig = om.MetaModelUnStructuredComp()\n",
    "\n",
    "x_train = np.linspace(0,10,20)\n",
    "\n",
    "trig.add_input('x', 0., training_data=x_train)\n",
    "\n",
    "trig.add_output('sin_x', 0.,\n",
    "                training_data=.5*np.sin(x_train),\n",
    "                surrogate=om.KrigingSurrogate())\n",
    "trig.add_output('cos_x', 0.,\n",
    "                training_data=.5*np.cos(x_train))\n",
    "\n",
    "trig.options['default_surrogate'] = om.KrigingSurrogate()\n",
    "\n",
    "# add it to a Problem, run and check the predicted values\n",
    "prob = om.Problem()\n",
    "prob.model.add_subsystem('trig', trig)\n",
    "prob.setup()\n",
    "\n",
    "prob.set_val('trig.x', 2.1)\n",
    "prob.run_model()\n",
    "\n",
    "print(prob.get_val('trig.sin_x'))\n",
    "print(prob.get_val('trig.cos_x'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "b33bf763",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:41.269853Z",
     "iopub.status.busy": "2026-10-02T14:41:41.269710Z",
     "iopub.status.idle": "2026-10-02T14:41:41.274093Z",
     "shell.execute_reply": "2026-10-02T14:41:41.273334Z"
    },
    "papermill": {
     "duration": 0.007066,
     "end_time": "2026-10-02T14:41:41.274549+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:41.267483+00:00",
     "status": "completed"
    },
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "np.float64(2.9329107823070632e-05)"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from openmdao.utils.assert_utils import assert_near_equal\n",
    "\n",
    "assert_near_equal(prob.get_val('trig.sin_x'), .5*np.sin(prob.get_val('trig.x')), 1e-4)\n",
    "assert_near_equal(prob.get_val('trig.cos_x'), .5*np.cos(prob.get_val('trig.x')), 1e-4)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "e0a310c1",
   "metadata": {
    "papermill": {
     "duration": 0.001149,
     "end_time": "2026-10-02T14:41:41.276974+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:41.275825+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "## Available Surrogates\n",
    "\n",
    "The following surrogates are available to use with `MetaModelUnStructuredComp`.\n",
    "\n",
    "[KrigingSurrogate](../surrogates/kriging)\n",
    "  \n",
    "  **Derivatives**: Yes\n",
    "\n",
    "  **Description**: Based on Kriging interpolation; prediction returns mean predicted value, optionally returns RMSE.\n",
    "\n",
    "\n",
    "[NearestNeighbor](../surrogates/nearestneighbor)\n",
    "  \n",
    "  **Derivatives**: Yes\n",
    "\n",
    "  **Description**: Based on the N-Dimensional Interpolation library by Stephen Marone.\n",
    "\n",
    "\n",
    "[ResponseSurface](../surrogates/responsesurface)\n",
    "  \n",
    "  **Derivatives**: Yes\n",
    "\n",
    "  **Description**: Based on second order response surface equations.\n",
    "\n",
    "\n",
    "## Advanced usage\n",
    "\n",
    "You can specify the training data after instantiation if you like, by setting the component's\n",
    "[options](../../core_features/options/options). Training data is provided in the options to the `trig`\n",
    "component using the variable names prefixed with `train_`.  This can be done anytime before\n",
    "the `MetaModelUnStructuredComp` runs for the first time.\n",
    "\n",
    "The inputs and outputs of a `MetaModelUnStructuredComp` are not limited to scalar values. The\n",
    "following modified version of the example uses an array to predict sine and\n",
    "cosine as a single output array of two values.  You will also note that the default\n",
    "surrogate can be passed as an argument to the `MetaModelUnStructuredComp` constructor, as an\n",
    "alternative to specifying it later.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "27200571",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:41.280142Z",
     "iopub.status.busy": "2026-10-02T14:41:41.279961Z",
     "iopub.status.idle": "2026-10-02T14:41:41.290128Z",
     "shell.execute_reply": "2026-10-02T14:41:41.289264Z"
    },
    "papermill": {
     "duration": 0.012481,
     "end_time": "2026-10-02T14:41:41.290595+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:41.278114+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ 0.43161089 -0.25241613]\n"
     ]
    }
   ],
   "source": [
    "# create a MetaModelUnStructuredComp that predicts sine and cosine as an array\n",
    "trig = om.MetaModelUnStructuredComp(default_surrogate=om.KrigingSurrogate())\n",
    "trig.add_input('x', 0)\n",
    "trig.add_output('y', np.zeros(2))\n",
    "\n",
    "# add it to a Problem\n",
    "prob = om.Problem()\n",
    "prob.model.add_subsystem('trig', trig)\n",
    "prob.setup()\n",
    "\n",
    "# provide training data\n",
    "trig.options['train_x'] = np.linspace(0, 10, 20)\n",
    "trig.options['train_y'] = np.column_stack((\n",
    "    .5*np.sin(trig.options['train_x']),\n",
    "    .5*np.cos(trig.options['train_x'])\n",
    "))\n",
    "\n",
    "# train the surrogate and check predicted value\n",
    "prob.set_val('trig.x', 2.1)\n",
    "prob.run_model()\n",
    "print(prob.get_val('trig.y'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "28971178",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:41.293890Z",
     "iopub.status.busy": "2026-10-02T14:41:41.293760Z",
     "iopub.status.idle": "2026-10-02T14:41:41.297360Z",
     "shell.execute_reply": "2026-10-02T14:41:41.296558Z"
    },
    "papermill": {
     "duration": 0.005981,
     "end_time": "2026-10-02T14:41:41.297919+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:41.291938+00:00",
     "status": "completed"
    },
    "tags": [
     "remove-output",
     "remove-input"
    ]
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "np.float64(1.8593344342355566e-05)"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "assert_near_equal(prob.get_val('trig.y'), np.append(.5*np.sin(prob.get_val('trig.x')), \n",
    "                                                    .5*np.cos(prob.get_val('trig.x'))),1e-4)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "36350e59",
   "metadata": {
    "papermill": {
     "duration": 0.001245,
     "end_time": "2026-10-02T14:41:41.300416+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:41.299171+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "In addition, it's possible to vectorize the input and output variables so that you can\n",
    "make multiple predictions for the inputs and outputs in a single execution of the\n",
    "`MetaModelUnStructuredComp` component. This is done by setting the `vec_size` argument when\n",
    "constructing the `MetaModelUnStructuredComp` component and giving it the number of predictions to make.  The following example vectorizes the `trig`\n",
    "component so that it makes three predictions at a time.  In this case, the input is\n",
    "three independent values of `x` and the output is the corresponding predicted values\n",
    "for the sine and cosine functions at those three points.  Note that a vectorized\n",
    "`MetaModelUnStructuredComp` component requires the first dimension of all input and output variables\n",
    "to be the same size as specified in the `vec_size` argument."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "2d759ecb",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:41.303852Z",
     "iopub.status.busy": "2026-10-02T14:41:41.303726Z",
     "iopub.status.idle": "2026-10-02T14:41:41.313875Z",
     "shell.execute_reply": "2026-10-02T14:41:41.312939Z"
    },
    "papermill": {
     "duration": 0.012616,
     "end_time": "2026-10-02T14:41:41.314392+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:41.301776+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[ 0.43161089 -0.25241613]\n",
      " [-0.0291842  -0.49914069]\n",
      " [-0.4580858  -0.20039903]]\n"
     ]
    }
   ],
   "source": [
    "size = 3\n",
    "\n",
    "# create a vectorized MetaModelUnStructuredComp for sine and cosine\n",
    "trig = om.MetaModelUnStructuredComp(vec_size=size, default_surrogate=om.KrigingSurrogate())\n",
    "trig.add_input('x', np.zeros(size))\n",
    "trig.add_output('y', np.zeros((size, 2)))\n",
    "\n",
    "# add it to a Problem\n",
    "prob = om.Problem()\n",
    "prob.model.add_subsystem('trig', trig)\n",
    "prob.setup()\n",
    "\n",
    "# provide training data\n",
    "trig.options['train_x'] = np.linspace(0, 10, 20)\n",
    "trig.options['train_y'] = np.column_stack((\n",
    "    .5*np.sin(trig.options['train_x']),\n",
    "    .5*np.cos(trig.options['train_x'])\n",
    "))\n",
    "\n",
    "# train the surrogate and check predicted value\n",
    "prob.set_val('trig.x', np.array([2.1, 3.2, 4.3]))\n",
    "prob.run_model()\n",
    "\n",
    "print(prob.get_val('trig.y'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "7dc31b1b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:41.318725Z",
     "iopub.status.busy": "2026-10-02T14:41:41.318603Z",
     "iopub.status.idle": "2026-10-02T14:41:41.322316Z",
     "shell.execute_reply": "2026-10-02T14:41:41.321604Z"
    },
    "papermill": {
     "duration": 0.006512,
     "end_time": "2026-10-02T14:41:41.322805+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:41.316293+00:00",
     "status": "completed"
    },
    "tags": [
     "remove-output",
     "remove-input"
    ]
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "np.float64(1.4043656845941314e-05)"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "assert_near_equal(prob.get_val('trig.y'), np.column_stack((\n",
    "                     .5*np.sin(prob.get_val('trig.x')),\n",
    "                     .5*np.cos(prob.get_val('trig.x')))),1e-4)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "c57b76a5",
   "metadata": {
    "papermill": {
     "duration": 0.001963,
     "end_time": "2026-10-02T14:41:41.386626+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:41.384663+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "## Using Surrogates That Do Not Define Linearize Method\n",
    "\n",
    "In some cases, you might define surrogates but not define a `linearize` method. In this case, the\n",
    "`MetaModelUnStructuredComp` derivatives will be computed using finite differences for the output variables that use that\n",
    "surrogate. By default, the default options for the finite differencing method will be used.\n",
    "\n",
    "If you would like to specify finite differencing options, you can do so by calling the `declare_partials`\n",
    "method in the component's `setup_partials` or in a parent group's configure method.\n",
    "This example, which uses a surrogate with no\n",
    "`linearize` method and no training for simplicity, shows `declare_partials` called in `setup_partials`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "493a8f22",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:41.390951Z",
     "iopub.status.busy": "2026-10-02T14:41:41.390813Z",
     "iopub.status.idle": "2026-10-02T14:41:41.432771Z",
     "shell.execute_reply": "2026-10-02T14:41:41.431999Z"
    },
    "papermill": {
     "duration": 0.044777,
     "end_time": "2026-10-02T14:41:41.433227+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:41.388450+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO: checking out_of_order...\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:     out_of_order check complete (0.000326 sec).\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO: checking system...\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:     system check complete (0.000015 sec).\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO: checking solvers...\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:     solvers check complete (0.000054 sec).\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO: checking dup_inputs...\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:     dup_inputs check complete (0.000011 sec).\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO: checking missing_recorders...\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING: The Problem has no recorder of any kind attached\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:     missing_recorders check complete (0.000375 sec).\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO: checking unserializable_options...\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:     unserializable_options check complete (0.000112 sec).\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO: checking comp_has_no_outputs...\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:     comp_has_no_outputs check complete (0.000015 sec).\n"
     ]
    }
   ],
   "source": [
    "from numpy import sin\n",
    "\n",
    "class SinSurrogate(om.SurrogateModel):\n",
    "    def train(self, x, y):\n",
    "        pass\n",
    "\n",
    "    def predict(self, x):\n",
    "        return sin(x)\n",
    "\n",
    "class TrigWithFdInSetup(om.MetaModelUnStructuredComp):\n",
    "    def setup(self):\n",
    "        surrogate = SinSurrogate()\n",
    "        self.add_input('x', 0.)\n",
    "        self.add_output('sin_x', 0., surrogate=surrogate)\n",
    "\n",
    "    def setup_partials(self):\n",
    "        self.declare_partials('sin_x', 'x', method='fd',\n",
    "                              form='backward', step=1e-7, step_calc='rel')\n",
    "\n",
    "# Testing explicitly setting fd inside of setup\n",
    "prob = om.Problem()\n",
    "\n",
    "trig = TrigWithFdInSetup()\n",
    "prob.model.add_subsystem('trig', trig, promotes_inputs=['x'])\n",
    "\n",
    "prob.setup(check=True)\n",
    "prob.set_val('x', 5.)\n",
    "\n",
    "trig.train = False\n",
    "prob.run_model()\n",
    "\n",
    "J = prob.compute_totals(of=['trig.sin_x'], wrt=['x'])\n",
    "deriv_using_fd = J[('trig.sin_x', 'x')]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "733dd77e",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:41.544123Z",
     "iopub.status.busy": "2026-10-02T14:41:41.543856Z",
     "iopub.status.idle": "2026-10-02T14:41:41.546662Z",
     "shell.execute_reply": "2026-10-02T14:41:41.546083Z"
    },
    "papermill": {
     "duration": 0.005726,
     "end_time": "2026-10-02T14:41:41.547287+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:41.541561+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0.28366195]\n"
     ]
    }
   ],
   "source": [
    "print(deriv_using_fd[0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "d1e3496d",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:41.551071Z",
     "iopub.status.busy": "2026-10-02T14:41:41.550941Z",
     "iopub.status.idle": "2026-10-02T14:41:41.554764Z",
     "shell.execute_reply": "2026-10-02T14:41:41.554201Z"
    },
    "papermill": {
     "duration": 0.006492,
     "end_time": "2026-10-02T14:41:41.555321+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:41.548829+00:00",
     "status": "completed"
    },
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "np.float64(8.459886607223184e-07)"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "assert_near_equal(deriv_using_fd[0], np.cos(prob['x']), 1e-4)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "bbbf6f76",
   "metadata": {
    "papermill": {
     "duration": 0.001494,
     "end_time": "2026-10-02T14:41:41.558482+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:41.556988+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "Complex step has not been tested with `MetaModelUnStructuredComp` and will result in an exception if used."
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "Tags",
  "kernelspec": {
   "display_name": "dev",
   "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": 4.405789,
   "end_time": "2026-10-02T14:41:41.974508+00:00",
   "environment_variables": {},
   "exception": null,
   "input_path": "/home/runner/work/OpenMDAO/OpenMDAO/openmdao/docs/openmdao_book/features/building_blocks/components/metamodelunstructured_comp.ipynb",
   "output_path": "/home/runner/work/OpenMDAO/OpenMDAO/openmdao/docs/_executed_book/features/building_blocks/components/metamodelunstructured_comp.ipynb",
   "parameters": {},
   "start_time": "2026-10-02T14:41:37.568719+00:00",
   "version": "2.7.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}