{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "4b218f8e",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:24.313129Z",
     "iopub.status.busy": "2026-10-02T14:41:24.312921Z",
     "iopub.status.idle": "2026-10-02T14:41:24.317341Z",
     "shell.execute_reply": "2026-10-02T14:41:24.316703Z"
    },
    "papermill": {
     "duration": 0.007121,
     "end_time": "2026-10-02T14:41:24.317851+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:24.310730+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": "4c73a409",
   "metadata": {
    "papermill": {
     "duration": 0.027849,
     "end_time": "2026-10-02T14:41:24.346960+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:24.319111+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "# ImplicitFuncComp\n",
    "\n",
    "`ImplicitFuncComp` is a component that provides a shortcut for building an ImplicitComponent based on a python function. That function takes inputs and states as arguments and returns residual values. The function may take some inputs that are non-differentiable and are assumed to be static during the computation of derivatives.  These static values may be of any hashable type.  All other arguments and return values must be either floats or numpy arrays. The mapping between a state argument and its residual output must be specified in the metadata when the output (state) is added by setting 'resid' to the name of the residual.\n",
    "\n",
    "It may seem confusing to use `add_output` to specify state variables since the state variables\n",
    "are actually input arguments to the function, but in OpenMDAO's view of the world, states are outputs so we use `add_output` to specify them.  Also, using the metadata to specify which input arguments are actually states gives more flexibility in terms of how the function arguments are ordered. For example, if it's desirable for a function to be passable to `scipy.optimize.newton`, then the function's arguments can be ordered with the states first, followed by the inputs, in order to match the order expected by `scipy.optimize.newton`.\n",
    "\n",
    "The `add_output` function is part of the [Function Metadata API](../func_api.ipynb).  You use this API to specify various metadata that OpenMDAO needs in order to properly configure a fully functional implicit component. You should read and understand the [Function Metadata API](../func_api.ipynb) before you continue with this section.\n",
    "\n",
    "## ImplicitFuncComp Options\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "b3172240",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:24.350810Z",
     "iopub.status.busy": "2026-10-02T14:41:24.350581Z",
     "iopub.status.idle": "2026-10-02T14:41:25.656938Z",
     "shell.execute_reply": "2026-10-02T14:41:25.656365Z"
    },
    "papermill": {
     "duration": 1.309592,
     "end_time": "2026-10-02T14:41:25.658096+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:24.348504+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;\">assembled_jac_type</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;csc&#x27;, &#x27;csr&#x27;, &#x27;dense&#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;\">Linear solver(s) in this group or implicit component, if using an assembled jacobian, will use this type.</td></tr>\n",
       "       <tr style=\"background-color: ghostwhite;\"><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: #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",
       "    </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",
    "def func(a):\n",
    "    y = a * 2.\n",
    "    return y\n",
    "om.show_options_table(om.ImplicitFuncComp(func))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cb24afcb",
   "metadata": {
    "papermill": {
     "duration": 0.044677,
     "end_time": "2026-10-02T14:41:25.704488+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:25.659811+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "## ImplicitFuncComp Constructor\n",
    "\n",
    "The call signature for the `ImplicitFuncComp` constructor is:\n",
    "\n",
    "```{eval-rst}\n",
    "    .. automethod:: openmdao.components.implicit_func_comp.ImplicitFuncComp.__init__\n",
    "        :noindex:\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "22820469",
   "metadata": {
    "papermill": {
     "duration": 0.001382,
     "end_time": "2026-10-02T14:41:25.770129+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:25.768747+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "## ImplicitFuncComp Example: A simple implicit function component\n",
    "\n",
    "The simplest implicit function component requires the definition of a function that takes\n",
    "inputs and states as arguments and returns residual values.  This function maps to the `apply_nonlinear`\n",
    "method in the OpenMDAO component API. Here's an example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "f37b67c7",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:25.773612Z",
     "iopub.status.busy": "2026-10-02T14:41:25.773262Z",
     "iopub.status.idle": "2026-10-02T14:41:26.990740Z",
     "shell.execute_reply": "2026-10-02T14:41:26.990114Z"
    },
    "papermill": {
     "duration": 1.219865,
     "end_time": "2026-10-02T14:41:26.991254+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:25.771389+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1790952086.978837] [runnervm8df0l:6534 :0]        ib_iface.c:1269 UCX  ERROR mana_0: iface 0x5644692d5540 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",
      "[1790952086.979089] [runnervm8df0l:6534 :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:06534] pml_ucx.c:313  Error: Failed to create UCP worker\n"
     ]
    }
   ],
   "source": [
    "import openmdao.api as om\n",
    "import openmdao.func_api as omf\n",
    "\n",
    "def apply_nl(a, b, c, x):  # inputs a, b, c and state x\n",
    "    R_x = a * x ** 2 + b * x + c\n",
    "    return R_x\n",
    "\n",
    "f = (omf.wrap(apply_nl)\n",
    "        .add_output('x', resid='R_x', val=0.0)\n",
    "        .declare_partials(of='*', wrt='*', method='cs')\n",
    "        )\n",
    "\n",
    "p = om.Problem()\n",
    "p.model.add_subsystem('comp', om.ImplicitFuncComp(f))\n",
    "\n",
    "p.model.nonlinear_solver = om.NewtonSolver(solve_subsystems=False, iprint=0)\n",
    "\n",
    "# need this since comp is implicit and doesn't have a solve_linear\n",
    "p.model.linear_solver = om.DirectSolver()\n",
    "\n",
    "p.setup()\n",
    "\n",
    "p.set_val('comp.a', 2.)\n",
    "p.set_val('comp.b', -8.)\n",
    "p.set_val('comp.c', 6.)\n",
    "p.run_model()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "b380e639",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:27.012782Z",
     "iopub.status.busy": "2026-10-02T14:41:27.012613Z",
     "iopub.status.idle": "2026-10-02T14:41:27.043022Z",
     "shell.execute_reply": "2026-10-02T14:41:27.042240Z"
    },
    "papermill": {
     "duration": 0.050692,
     "end_time": "2026-10-02T14:41:27.043503+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:26.992811+00:00",
     "status": "completed"
    },
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/runner/work/OpenMDAO/OpenMDAO/.pixi/envs/dev/lib/python3.13/site-packages/openmdao/utils/relevance.py:1232: OpenMDAOWarning:The top level group has a nonlinear solver that computes gradients, so the entire model will be included in the optimization iteration.\n"
     ]
    }
   ],
   "source": [
    "from openmdao.utils.assert_utils import assert_check_partials, assert_check_totals\n",
    "\n",
    "assert_check_partials(p.check_partials(includes=['comp'], out_stream=None), atol=1e-5)\n",
    "assert_check_totals(p.check_totals(of=['comp.x'], wrt=['comp.a', 'comp.b', 'comp.c'], out_stream=None))"
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "Tags",
  "interpreter": {
   "hash": "245bb6672fbc289f90037d9f00b5ee20de7d921e65d14dbc4c07ab973781223d"
  },
  "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.90296,
   "end_time": "2026-10-02T14:41:27.559882+00:00",
   "environment_variables": {},
   "exception": null,
   "input_path": "/home/runner/work/OpenMDAO/OpenMDAO/openmdao/docs/openmdao_book/features/building_blocks/components/implicit_func_comp.ipynb",
   "output_path": "/home/runner/work/OpenMDAO/OpenMDAO/openmdao/docs/_executed_book/features/building_blocks/components/implicit_func_comp.ipynb",
   "parameters": {},
   "start_time": "2026-10-02T14:41:23.656922+00:00",
   "version": "2.7.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}