{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "53243aad",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:44.654287Z",
     "iopub.status.busy": "2026-10-02T14:41:44.654068Z",
     "iopub.status.idle": "2026-10-02T14:41:44.658731Z",
     "shell.execute_reply": "2026-10-02T14:41:44.658086Z"
    },
    "papermill": {
     "duration": 0.008361,
     "end_time": "2026-10-02T14:41:44.659264+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:44.650903+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": "81dff0e2",
   "metadata": {
    "papermill": {
     "duration": 0.001611,
     "end_time": "2026-10-02T14:41:44.662769+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:44.661158+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "# SubmodelComp\n",
    "\n",
    "`SubmodelComp` provides a way to evaluate an OpenMDAO system within an OpenMDAO system.\n",
    "\n",
    "# SubmodelComp Options"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "19dde2c9",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:44.666963Z",
     "iopub.status.busy": "2026-10-02T14:41:44.666806Z",
     "iopub.status.idle": "2026-10-02T14:41:47.143361Z",
     "shell.execute_reply": "2026-10-02T14:41:47.142483Z"
    },
    "papermill": {
     "duration": 2.479691,
     "end_time": "2026-10-02T14:41:47.144094+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:44.664403+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1790952107.134920] [runnervm8df0l:6811 :0]        ib_iface.c:1269 UCX  ERROR mana_0: iface 0x563fc46899d0 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",
      "[1790952107.135195] [runnervm8df0l:6811 :0]      ucp_worker.c:1412 UCX  ERROR uct_iface_open(ud_verbs/mana_0:1) failed: Input/output error\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[runnervm8df0l:06811] pml_ucx.c:313  Error: Failed to create UCP worker\n"
     ]
    },
    {
     "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;\">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;\">do_coloring</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, attempt to compute a total coloring for the submodel.</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",
       "    </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(om.SubmodelComp(problem=om.Problem()))"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "66052076",
   "metadata": {
    "papermill": {
     "duration": 0.00175,
     "end_time": "2026-10-02T14:41:47.148947+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:47.147197+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "# SubmodelComp Constructor\n",
    "\n",
    "The call signature for the `SubmodelComp` constructor is\n",
    "\n",
    "```{eval-rst}\n",
    "    .. automethod:: openmdao.components.submodel_comp.SubmodelComp.__init__\n",
    "        :noindex:\n",
    "```\n",
    "\n",
    "# Using the SubmodelComp\n",
    "\n",
    "`SubmodelComp` allows you to add a component that contains a complete `Problem` in itself. The only required argument is `problem`. This takes an instantiated problem that already has a model. That way, the user can declare any desired options for the problem and have it used for the underlying subproblem.\n",
    "\n",
    "Other arguments include `inputs`, `outputs`, and `reports`. Input and output argument formats are discussed in the next section, but if the inputs and outputs are not specified in the `SubmodelComp`'s instantiation, they will have to be added later using the `add_input` and `add_output` methods. The `reports` option controls report generation for the underlying subproblem. `SubmodelComp` performs more efficiently if it doesn't have to write reports, but if reports are desired, then the `reports` argument can be set to `True`.\n",
    "\n",
    "`SubmodelComp` provides a way to evaluate an OpenMDAO model within an OpenMDAO system. Unlike a typical subsystem, using a subproblem exposes only a limited number of inputs and outputs of the underlying model to the parent system. This may be beneficial from a performance standpoint when there are many inputs and outputs of the internal model that aren't needed in the top-level model.\n",
    "\n",
    "Note that this means that nonlinear and linear solvers are needed on the subproblem's model if implicit behavior is present, since the internal variables are invisible to solvers in the parent system.\n",
    "\n",
    "At this time, OpenMDAO does not evaluate gradients through the optimization process, so a gradient-based driver at the outer level cannot access derivatives across the optimization on the inner level.\n",
    "\n",
    "## Inputs and Outputs\n",
    "\n",
    "Inputs and outputs can be passed into `SubmodelComp` as a list of `str`, `tuple`, or both. If the list element is `str`, then it must specify the promoted name of a variable in the model. All inputs and outputs you wish to use must be promoted to the model level. If the list element is `tuple`, then the first element of the `tuple` must be the group level promoted name or the absolute name of the variable, and the second element is an alias that `SubmodelComp` will use to refer to the variable. For example:\n",
    "\n",
    "    (path.to.var, desired_var_name)\n",
    "    or (var, desired_var_name) if the variable is already promoted at the group level\n",
    "\n",
    "Input and output names can also include wildcards if the type is `str`.\n",
    "\n",
    "If not specified at instantiation, inputs and outputs will have to be declared using the `add_input` and `add_output` methods.\n",
    "\n",
    "# Example: Using SubmodelComp"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "1a260d3d",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:47.186498Z",
     "iopub.status.busy": "2026-10-02T14:41:47.186135Z",
     "iopub.status.idle": "2026-10-02T14:41:47.227039Z",
     "shell.execute_reply": "2026-10-02T14:41:47.226342Z"
    },
    "papermill": {
     "duration": 0.077238,
     "end_time": "2026-10-02T14:41:47.227526+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:47.150288+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "import openmdao.api as om\n",
    "from numpy import pi\n",
    "\n",
    "p = om.Problem()\n",
    "\n",
    "# create a model that computes x = r*cos(theta)\n",
    "submodel1 = om.Group()\n",
    "submodel1.add_subsystem('sub1_ivc_r', om.IndepVarComp('r', 1.),\n",
    "                        promotes_outputs=['r'])\n",
    "submodel1.add_subsystem('sub1_ivc_theta', om.IndepVarComp('theta', pi),\n",
    "                        promotes_outputs=['theta'])\n",
    "submodel1.add_subsystem('subComp1', om.ExecComp('x = r*cos(theta)'),\n",
    "                        promotes_inputs=['r', 'theta'],\n",
    "                        promotes_outputs=['x'])\n",
    "\n",
    "# create a model that computes y = r*sin(theta)\n",
    "submodel2 = om.Group()\n",
    "submodel2.add_subsystem('sub2_ivc_r', om.IndepVarComp('r', 2),\n",
    "                        promotes_outputs=['r'])\n",
    "submodel2.add_subsystem('sub2_ivc_theta', om.IndepVarComp('theta', pi/2),\n",
    "                        promotes_outputs=['theta'])\n",
    "submodel2.add_subsystem('subComp2', om.ExecComp('y = r*sin(theta)'),\n",
    "                        promotes_inputs=['r', 'theta'],\n",
    "                        promotes_outputs=['y'])\n",
    "\n",
    "# wrap the x and y models as sub-problems\n",
    "subprob1 = om.Problem(model=submodel1)\n",
    "subprob2 = om.Problem(model=submodel2)\n",
    "\n",
    "subcomp1 = om.SubmodelComp(problem=subprob1,\n",
    "                            inputs=['r', 'theta'], outputs=['x'])\n",
    "subcomp2 = om.SubmodelComp(problem=subprob2,\n",
    "                            inputs=['r', 'theta'], outputs=['y'])\n",
    "\n",
    "# add both sub-problems and a Component that uses their outputs\n",
    "p.model.add_subsystem('sub1', subcomp1, \n",
    "                      promotes_inputs=['r','theta'],\n",
    "                      promotes_outputs=['x'])\n",
    "p.model.add_subsystem('sub2', subcomp2, \n",
    "                      promotes_inputs=['r','theta'],\n",
    "                      promotes_outputs=['y'])\n",
    "p.model.add_subsystem('supComp', om.ExecComp('z = x**2 + y'),\n",
    "                      promotes_inputs=['x', 'y'],\n",
    "                      promotes_outputs=['z'])\n",
    "\n",
    "p.model.set_input_defaults('r', 1)\n",
    "p.model.set_input_defaults('theta', pi)\n",
    "\n",
    "p.setup(force_alloc_complex=True)\n",
    "\n",
    "p.run_model()\n",
    "\n",
    "cpd = p.check_partials(method='cs', out_stream=None)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "1b41c3b0",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:47.231092Z",
     "iopub.status.busy": "2026-10-02T14:41:47.230866Z",
     "iopub.status.idle": "2026-10-02T14:41:47.233621Z",
     "shell.execute_reply": "2026-10-02T14:41:47.233082Z"
    },
    "papermill": {
     "duration": 0.005156,
     "end_time": "2026-10-02T14:41:47.234148+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:47.228992+00:00",
     "status": "completed"
    },
    "tags": [
     "allow-assert",
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert(sorted(cpd['sub1'].keys()) == [('x', 'r'), ('x', 'theta')])\n",
    "assert(sorted(cpd['sub2'].keys()) == [('y', 'r'), ('y', 'theta')])"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "7f436f34",
   "metadata": {
    "papermill": {
     "duration": 0.001231,
     "end_time": "2026-10-02T14:41:47.236737+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:47.235506+00:00",
     "status": "completed"
    },
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "source": [
    "# Example: Variable Aliases"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "f69d6e4c",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:47.240179Z",
     "iopub.status.busy": "2026-10-02T14:41:47.240009Z",
     "iopub.status.idle": "2026-10-02T14:41:47.249364Z",
     "shell.execute_reply": "2026-10-02T14:41:47.248813Z"
    },
    "papermill": {
     "duration": 0.011841,
     "end_time": "2026-10-02T14:41:47.249978+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:47.238137+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2 Input(s) in 'model'\n",
      "\n",
      "varname  val   prom_name\n",
      "-------  ----  ---------\n",
      "subcomp\n",
      "  a      [1.]  a        \n",
      "  b      [2.]  b        \n",
      "\n",
      "\n",
      "1 Explicit Output(s) in 'model'\n",
      "\n",
      "varname  val   prom_name\n",
      "-------  ----  ---------\n",
      "subcomp\n",
      "  c      [5.]  c        \n",
      "\n",
      "\n",
      "0 Implicit Output(s) in 'model'\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "import openmdao.api as om\n",
    "\n",
    "# create a Problem that will be nested in another Problem\n",
    "subprob = om.Problem()\n",
    "submodel = subprob.model\n",
    "submodel.add_subsystem('subsys', om.ExecComp('z = x**2 + y**2'))\n",
    "\n",
    "# wrap that Problem in a SubmodelComp, renaming the inputs and output\n",
    "subcomp = om.SubmodelComp(problem=subprob,\n",
    "                          inputs=[('subsys.x', 'a'), ('subsys.y', 'b')],\n",
    "                          outputs=[('subsys.z', 'c')])\n",
    "\n",
    "# create a top-level Problem that contains the SubmodelComp\n",
    "p = om.Problem()\n",
    "p.model.add_subsystem('subcomp', subcomp, promotes_inputs=['a', 'b'], promotes_outputs=['c'])\n",
    "p.setup()\n",
    "\n",
    "p.set_val('a', 1)\n",
    "p.set_val('b', 2)\n",
    "\n",
    "p.run_model()\n",
    "\n",
    "inputs = p.model.list_inputs()\n",
    "outputs = p.model.list_outputs()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "6921adbe",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:47.253417Z",
     "iopub.status.busy": "2026-10-02T14:41:47.253235Z",
     "iopub.status.idle": "2026-10-02T14:41:47.258058Z",
     "shell.execute_reply": "2026-10-02T14:41:47.257384Z"
    },
    "papermill": {
     "duration": 0.007163,
     "end_time": "2026-10-02T14:41:47.258471+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:47.251308+00:00",
     "status": "completed"
    },
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "np.float64(0.0)"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from openmdao.utils.assert_utils import assert_near_equal\n",
    "\n",
    "inputs = dict(inputs)\n",
    "assert_near_equal(inputs['subcomp.a']['val'], 1.)\n",
    "assert_near_equal(inputs['subcomp.b']['val'], 2.)\n",
    "\n",
    "outputs = dict(outputs)\n",
    "assert_near_equal(outputs['subcomp.c']['val'], 5.)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "b1a58eb6",
   "metadata": {
    "papermill": {
     "duration": 0.001233,
     "end_time": "2026-10-02T14:41:47.261069+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:47.259836+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "# Example: Using Variables with the Same Name"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "c38ef105",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:47.298468Z",
     "iopub.status.busy": "2026-10-02T14:41:47.298273Z",
     "iopub.status.idle": "2026-10-02T14:41:47.396407Z",
     "shell.execute_reply": "2026-10-02T14:41:47.395765Z"
    },
    "papermill": {
     "duration": 0.101013,
     "end_time": "2026-10-02T14:41:47.397202+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:47.296189+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2 Input(s) in 'model'\n",
      "\n",
      "varname  val   prom_name\n",
      "-------  ----  ---------\n",
      "comp\n",
      "  x      [1.]  comp.x   \n",
      "  y      [2.]  comp.y   \n",
      "\n",
      "\n",
      "1 Explicit Output(s) in 'model'\n",
      "\n",
      "varname  val    prom_name\n",
      "-------  -----  ---------\n",
      "comp\n",
      "  z      [73.]  comp.z   \n",
      "\n",
      "\n",
      "0 Implicit Output(s) in 'model'\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "import openmdao.api as om\n",
    "\n",
    "p = om.Problem()\n",
    "\n",
    "model = om.Group()\n",
    "\n",
    "model.add_subsystem('x1Comp', om.ExecComp('x1 = x*3'))\n",
    "model.add_subsystem('x2Comp', om.ExecComp('x2 = x**3'))\n",
    "model.add_subsystem('model', om.ExecComp('z = x1**2 + x2**2'))\n",
    "\n",
    "model.connect('x1Comp.x1', 'model.x1')\n",
    "model.connect('x2Comp.x2', 'model.x2')\n",
    "\n",
    "subprob = om.Problem()\n",
    "subprob.model.add_subsystem('submodel', model)\n",
    "\n",
    "comp = om.SubmodelComp(problem=subprob, \n",
    "                       inputs=[('submodel.x1Comp.x', 'x'), ('submodel.x2Comp.x', 'y')],\n",
    "                       outputs=[('submodel.model.z', 'z')])\n",
    "\n",
    "p.model.add_subsystem('comp', comp)\n",
    "\n",
    "p.model.set_input_defaults('comp.x', 1)\n",
    "p.model.set_input_defaults('comp.y', 2)\n",
    "\n",
    "p.setup()\n",
    "\n",
    "p.run_model()\n",
    "\n",
    "inputs = p.model.list_inputs()\n",
    "outputs = p.model.list_outputs()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "ece99a41",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:47.401433Z",
     "iopub.status.busy": "2026-10-02T14:41:47.401249Z",
     "iopub.status.idle": "2026-10-02T14:41:47.406171Z",
     "shell.execute_reply": "2026-10-02T14:41:47.405469Z"
    },
    "papermill": {
     "duration": 0.007673,
     "end_time": "2026-10-02T14:41:47.406655+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:47.398982+00:00",
     "status": "completed"
    },
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "np.float64(0.0)"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from openmdao.utils.assert_utils import assert_near_equal\n",
    "\n",
    "inputs = dict(inputs)\n",
    "assert_near_equal(inputs['comp.x']['val'], 1.)\n",
    "assert_near_equal(inputs['comp.y']['val'], 2.)\n",
    "\n",
    "outputs = dict(outputs)\n",
    "assert_near_equal(outputs['comp.z']['val'], 73.)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "bd32e476",
   "metadata": {
    "papermill": {
     "duration": 0.001424,
     "end_time": "2026-10-02T14:41:47.497919+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:47.496495+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "# Example: Adding Inputs and Outputs Using Wildcards"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "07c98553",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:47.545637Z",
     "iopub.status.busy": "2026-10-02T14:41:47.545447Z",
     "iopub.status.idle": "2026-10-02T14:41:47.554537Z",
     "shell.execute_reply": "2026-10-02T14:41:47.553914Z"
    },
    "papermill": {
     "duration": 0.055682,
     "end_time": "2026-10-02T14:41:47.555021+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:47.499339+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3 Input(s) in 'model'\n",
      "\n",
      "varname  val   prom_name\n",
      "-------  ----  ---------\n",
      "comp\n",
      "  x1     [1.]  x1       \n",
      "  x2     [2.]  x2       \n",
      "  x3     [3.]  x3       \n",
      "\n",
      "\n",
      "1 Explicit Output(s) in 'model'\n",
      "\n",
      "varname  val    prom_name\n",
      "-------  -----  ---------\n",
      "comp\n",
      "  z      [14.]  z        \n",
      "\n",
      "\n",
      "0 Implicit Output(s) in 'model'\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "import openmdao.api as om\n",
    "\n",
    "p = om.Problem()\n",
    "\n",
    "submodel = om.Group()\n",
    "submodel.add_subsystem('sub', om.ExecComp('z = x1**2 + x2**2 + x3**2'), promotes=['*'])\n",
    "\n",
    "subprob = om.Problem()\n",
    "subprob.model.add_subsystem('submodel', submodel, promotes=['*'])\n",
    "\n",
    "comp = om.SubmodelComp(problem=subprob, inputs=['x*'], outputs=['*'])\n",
    "\n",
    "p.model.add_subsystem('comp', comp, promotes_inputs=['*'], promotes_outputs=['*'])\n",
    "p.setup()\n",
    "\n",
    "p.set_val('x1', 1)\n",
    "p.set_val('x2', 2)\n",
    "p.set_val('x3', 3)\n",
    "\n",
    "p.run_model()\n",
    "\n",
    "inputs = p.model.list_inputs()\n",
    "outputs = p.model.list_outputs()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "a153fd42",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:47.558670Z",
     "iopub.status.busy": "2026-10-02T14:41:47.558532Z",
     "iopub.status.idle": "2026-10-02T14:41:47.561942Z",
     "shell.execute_reply": "2026-10-02T14:41:47.561339Z"
    },
    "papermill": {
     "duration": 0.006028,
     "end_time": "2026-10-02T14:41:47.562542+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:47.556514+00:00",
     "status": "completed"
    },
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "np.float64(0.0)"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from openmdao.utils.assert_utils import assert_near_equal\n",
    "\n",
    "inputs = dict(inputs)\n",
    "assert_near_equal(inputs['comp.x1']['val'], 1.)\n",
    "assert_near_equal(inputs['comp.x2']['val'], 2.)\n",
    "assert_near_equal(inputs['comp.x3']['val'], 3.)\n",
    "\n",
    "outputs = dict(outputs)\n",
    "assert_near_equal(outputs['comp.z']['val'], 14.)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "4cc33f99",
   "metadata": {
    "papermill": {
     "duration": 0.002094,
     "end_time": "2026-10-02T14:41:47.566804+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:47.564710+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "# Example: Adding Inputs and Outputs After Instantiation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "7419c806",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:47.571554Z",
     "iopub.status.busy": "2026-10-02T14:41:47.571422Z",
     "iopub.status.idle": "2026-10-02T14:41:47.585100Z",
     "shell.execute_reply": "2026-10-02T14:41:47.584449Z"
    },
    "papermill": {
     "duration": 0.01674,
     "end_time": "2026-10-02T14:41:47.585599+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:47.568859+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "import openmdao.api as om\n",
    "\n",
    "p = om.Problem()\n",
    "\n",
    "# create a model that computes x = r*cos(theta)\n",
    "submodel1 = om.Group()\n",
    "submodel1.add_subsystem('sub1_ivc_r', om.IndepVarComp('r', 1.),\n",
    "                        promotes_outputs=['r'])\n",
    "submodel1.add_subsystem('sub1_ivc_theta', om.IndepVarComp('theta', pi),\n",
    "                        promotes_outputs=['theta'])\n",
    "submodel1.add_subsystem('subComp1', om.ExecComp('x = r*cos(theta)'))\n",
    "\n",
    "# create a model that computes y = r*sin(theta)\n",
    "submodel2 = om.Group()\n",
    "submodel2.add_subsystem('sub2_ivc_r', om.IndepVarComp('r', 2),\n",
    "                        promotes_outputs=['r'])\n",
    "submodel2.add_subsystem('sub2_ivc_theta', om.IndepVarComp('theta', pi/2),\n",
    "                        promotes_outputs=['theta'])\n",
    "submodel2.add_subsystem('subComp2', om.ExecComp('y = r*sin(theta)'))\n",
    "\n",
    "# wrap the x and y models as sub-problems\n",
    "subprob1 = om.Problem()\n",
    "subprob1.model.add_subsystem('submodel1', submodel1, promotes=['*'])\n",
    "subprob2 = om.Problem()\n",
    "subprob2.model.add_subsystem('submodel2', submodel2, promotes=['*'])\n",
    "\n",
    "comp1 = om.SubmodelComp(problem=subprob1)\n",
    "comp2 = om.SubmodelComp(problem=subprob2)\n",
    "\n",
    "# add inputs and outputs for the sub-problems\n",
    "comp1.add_input('subComp1.r', name='r')\n",
    "comp1.add_input('subComp1.theta', name='theta')\n",
    "comp2.add_input('subComp2.r', name='r')\n",
    "comp2.add_input('subComp2.theta', name='theta')\n",
    "\n",
    "comp1.add_output('subComp1.x', name='x')\n",
    "comp2.add_output('subComp2.y', name='y')\n",
    "\n",
    "# add both sub-problems and a Component that uses their outputs\n",
    "p.model.add_subsystem('sub1', comp1, \n",
    "                      promotes_inputs=['r','theta'],\n",
    "                      promotes_outputs=['x'])\n",
    "p.model.add_subsystem('sub2', comp2, \n",
    "                      promotes_inputs=['r','theta'],\n",
    "                      promotes_outputs=['y'])\n",
    "p.model.add_subsystem('supComp', om.ExecComp('z = x**2 + y'),\n",
    "                      promotes_inputs=['x', 'y'],\n",
    "                      promotes_outputs=['z'])\n",
    "\n",
    "p.model.set_input_defaults('r', 1)\n",
    "p.model.set_input_defaults('theta', pi)\n",
    "\n",
    "p.setup(force_alloc_complex=True)\n",
    "\n",
    "p.run_model()\n",
    "\n",
    "cpd = p.check_partials(method='cs', out_stream=None)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "76e7d83b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:47.620131Z",
     "iopub.status.busy": "2026-10-02T14:41:47.620010Z",
     "iopub.status.idle": "2026-10-02T14:41:47.622753Z",
     "shell.execute_reply": "2026-10-02T14:41:47.622101Z"
    },
    "papermill": {
     "duration": 0.035628,
     "end_time": "2026-10-02T14:41:47.623239+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:47.587611+00:00",
     "status": "completed"
    },
    "tags": [
     "remove-input",
     "remove-output",
     "allow-assert"
    ]
   },
   "outputs": [],
   "source": [
    "assert(sorted(cpd['sub1'].keys()) == [('x', 'r'), ('x', 'theta')])\n",
    "assert(sorted(cpd['sub2'].keys()) == [('y', 'r'), ('y', 'theta')])"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "a6f29779",
   "metadata": {
    "papermill": {
     "duration": 0.001402,
     "end_time": "2026-10-02T14:41:47.626273+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:47.624871+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "# Example: Adding Inputs and Outputs in Configure"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "e9ee19dd",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:47.629962Z",
     "iopub.status.busy": "2026-10-02T14:41:47.629782Z",
     "iopub.status.idle": "2026-10-02T14:41:47.646859Z",
     "shell.execute_reply": "2026-10-02T14:41:47.646123Z"
    },
    "papermill": {
     "duration": 0.019589,
     "end_time": "2026-10-02T14:41:47.647315+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:47.627726+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "import openmdao.api as om\n",
    "\n",
    "class Subsys1(om.Group):\n",
    "    def setup(self):\n",
    "        model = om.Group()\n",
    "        comp = om.ExecComp('x = r*cos(theta)')\n",
    "        model.add_subsystem('comp', comp, promotes_inputs=['r', 'theta'],\n",
    "                            promotes_outputs=['x'])\n",
    "        subprob = om.Problem()\n",
    "        subprob.model.add_subsystem('model', model)\n",
    "        subprob.model.promotes('model', any=['*'])\n",
    "        self.add_subsystem('submodel1', om.SubmodelComp(problem=subprob))\n",
    "\n",
    "    def configure(self):\n",
    "        self._get_subsystem('submodel1').add_input('r')\n",
    "        self._get_subsystem('submodel1').add_input('theta')\n",
    "        self._get_subsystem('submodel1').add_output('x')\n",
    "\n",
    "        self.promotes('submodel1', ['r', 'theta', 'x'])\n",
    "\n",
    "class Subsys2(om.Group):\n",
    "    def setup(self):\n",
    "        model = om.Group()\n",
    "        comp = om.ExecComp('y = r*sin(theta)')\n",
    "        model.add_subsystem('comp', comp, promotes_inputs=['r', 'theta'],\n",
    "                            promotes_outputs=['y'])\n",
    "        subprob = om.Problem()\n",
    "        subprob.model.add_subsystem('model', model)\n",
    "        subprob.model.promotes('model', any=['*'])\n",
    "        self.add_subsystem('submodel2', om.SubmodelComp(problem=subprob))\n",
    "\n",
    "    def configure(self):\n",
    "        self._get_subsystem('submodel2').add_input('r')\n",
    "        self._get_subsystem('submodel2').add_input('theta')\n",
    "        self._get_subsystem('submodel2').add_output('y')\n",
    "\n",
    "        self.promotes('submodel2', ['r', 'theta', 'y'])\n",
    "\n",
    "p = om.Problem()\n",
    "p.model.add_subsystem('sub1', Subsys1(), \n",
    "                      promotes_inputs=['r', 'theta'],\n",
    "                      promotes_outputs=['x'])\n",
    "p.model.add_subsystem('sub2', Subsys2(), \n",
    "                      promotes_inputs=['r', 'theta'],\n",
    "                      promotes_outputs=['y'])\n",
    "p.model.add_subsystem('supComp', om.ExecComp('z = x**2 + y'),\n",
    "                      promotes_inputs=['x', 'y'],\n",
    "                      promotes_outputs=['z'])\n",
    "\n",
    "p.setup(force_alloc_complex=True)\n",
    "\n",
    "p.set_val('r', 1)\n",
    "p.set_val('theta', pi)\n",
    "\n",
    "p.run_model()\n",
    "cpd = p.check_partials(method='cs', out_stream=None)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "502b9457",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:47.656285Z",
     "iopub.status.busy": "2026-10-02T14:41:47.656155Z",
     "iopub.status.idle": "2026-10-02T14:41:47.658809Z",
     "shell.execute_reply": "2026-10-02T14:41:47.658193Z"
    },
    "papermill": {
     "duration": 0.010427,
     "end_time": "2026-10-02T14:41:47.659223+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:47.648796+00:00",
     "status": "completed"
    },
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert(sorted(cpd['sub1.submodel1'].keys()) == [('x', 'r'), ('x', 'theta')])\n",
    "assert(sorted(cpd['sub2.submodel2'].keys()) == [('y', 'r'), ('y', 'theta')])"
   ]
  }
 ],
 "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": 4.320251,
   "end_time": "2026-10-02T14:41:48.178508+00:00",
   "environment_variables": {},
   "exception": null,
   "input_path": "/home/runner/work/OpenMDAO/OpenMDAO/openmdao/docs/openmdao_book/features/building_blocks/components/submodel_comp.ipynb",
   "output_path": "/home/runner/work/OpenMDAO/OpenMDAO/openmdao/docs/_executed_book/features/building_blocks/components/submodel_comp.ipynb",
   "parameters": {},
   "start_time": "2026-10-02T14:41:43.858257+00:00",
   "version": "2.7.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}