{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "036eb6db",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:09.947659Z",
     "iopub.status.busy": "2026-10-02T14:41:09.947407Z",
     "iopub.status.idle": "2026-10-02T14:41:09.952217Z",
     "shell.execute_reply": "2026-10-02T14:41:09.951643Z"
    },
    "papermill": {
     "duration": 0.008342,
     "end_time": "2026-10-02T14:41:09.953052+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:09.944710+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": "7421a152",
   "metadata": {
    "papermill": {
     "duration": 0.001423,
     "end_time": "2026-10-02T14:41:09.956150+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:09.954727+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "# BalanceComp\n",
    "\n",
    "`BalanceComp` is a specialized implementation of [ImplicitComponent](../../core_features/working_with_components/implicit_component.ipynb) that is intended to provide a simple way to implement most implicit equations without the need to define your own residuals.\n",
    "\n",
    "## BalanceComp Options\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "85a98816",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:09.959854Z",
     "iopub.status.busy": "2026-10-02T14:41:09.959651Z",
     "iopub.status.idle": "2026-10-02T14:41:11.105217Z",
     "shell.execute_reply": "2026-10-02T14:41:11.104739Z"
    },
    "papermill": {
     "duration": 1.148114,
     "end_time": "2026-10-02T14:41:11.105643+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:09.957529+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;\">guess_func</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;function&#x27;]</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">A callable function in the form f(inputs, outputs, residuals) that can provide an initial &quot;guess&quot; value of the state variable(s) based on the inputs, outputs and residuals.</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",
       "    </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.balance_comp.BalanceComp\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f3d45ac6",
   "metadata": {
    "papermill": {
     "duration": 0.001205,
     "end_time": "2026-10-02T14:41:11.108492+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:11.107287+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "## BalanceComp Constructor\n",
    "\n",
    "The call signature for the `BalanceComp` constructor is:\n",
    "\n",
    "```{eval-rst}\n",
    "    .. automethod:: openmdao.components.balance_comp.BalanceComp.__init__()\n",
    "        :noindex:\n",
    "```"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "a4147dfd",
   "metadata": {
    "papermill": {
     "duration": 0.001021,
     "end_time": "2026-10-02T14:41:11.111360+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:11.110339+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "## Using the BalanceComp\n",
    "\n",
    "`BalanceComp` allows you to add one or more state variables and its associated\n",
    "implicit equations.  For each ``balance`` added to the component it\n",
    "solves the following equation:\n",
    "\n",
    "$$\n",
    "  \\begin{align}\n",
    "  \\mathcal{R}_{name} =\n",
    "  \\frac{f_{mult}(x,...) \\times f_{lhs}(x,...) - f_{rhs}(x,...)}{f_{norm}(f_{rhs}(x,...))}\n",
    "  \\end{align}\n",
    "$$\n",
    "\n",
    "The optional normalization function $f_{norm}(f_{rhs})$ is computed as:\n",
    "\n",
    "$$\n",
    "  \\begin{align}\n",
    "  f_{norm}(f_{rhs}(x,...)) =\n",
    "  \\begin{cases}\n",
    "   \\left| f_{rhs} \\right|, & \\text{if normalize and } \\left| f_{rhs} \\right| \\geq 2 \\\\\n",
    "   0.25 f_{rhs}^2 + 1,     & \\text{if normalize and } \\left| f_{rhs} \\right| < 2 \\\\\n",
    "   1,                      & \\text{if not normalize}\n",
    "  \\end{cases}\n",
    "  \\end{align}\n",
    "$$\n",
    "\n",
    "The following inputs and outputs are associated with each implicit state.\n",
    "\n",
    "```{eval-rst}\n",
    "=========== ======= ====================================================\n",
    "Name        I/O     Description\n",
    "=========== ======= ====================================================\n",
    "{name}      output  implicit state variable\n",
    "lhs:{name}  input   left-hand side of equation to be balanced\n",
    "rhs:{name}  input   right-hand side of equation to be balanced\n",
    "mult:{name} input   left-hand side multiplier of equation to be balanced\n",
    "=========== ======= ====================================================\n",
    "```\n",
    "\n",
    "The default value for the `rhs:{name}` input can be set to via the\n",
    "`rhs_val` argument (see arguments below). If the rhs value is fixed (e.g. 0),\n",
    "then the input can be left unconnected. The `lhs:{name}` input must always have\n",
    "something connected to it and should be dependent upon the value of the implicit state variable.\n",
    "\n",
    "The multiplier is optional and will default to 1.0 if not connected.\n",
    "\n",
    "`BalanceComp` supports vectorized implicit states. Simply provide a default\n",
    "value or shape when adding the balance that reflects the correct shape.\n",
    "\n",
    "You can provide the arguments to create a balance when instantiating a `BalanceComp`\n",
    "or you can use the ``add_balance`` method to create one or more state variables after\n",
    "instantiation.  The constructor accepts all the same arguments as the ``add_balance``\n",
    "method:\n",
    "\n",
    "```{eval-rst}\n",
    "    .. automethod:: openmdao.components.balance_comp.BalanceComp.add_balance\n",
    "       :noindex:\n",
    "```\n",
    "\n",
    "Note that the `kwargs` arguments can include any of the keyword arguments normally available\n",
    "when creating an output variable with the\n",
    "`add_output` method of a [Component](../../../_srcdocs/packages/core/component).\n",
    "\n",
    "## Example:  Scalar Root Finding\n",
    "\n",
    "The following example uses a BalanceComp to implicitly solve the\n",
    "equation:\n",
    "\n",
    "$$\n",
    "    2 \\cdot x^2 = 4\n",
    "$$\n",
    "\n",
    "Here, our LHS is connected to a computed value for $x^2$, the multiplier is 2, and the RHS\n",
    "is 4.  The expected solution is $x=\\sqrt{2}$.  We initialize $x$ with a value of 1 so that\n",
    "it finds the positive root."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "e5747836",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:11.115183Z",
     "iopub.status.busy": "2026-10-02T14:41:11.114732Z",
     "iopub.status.idle": "2026-10-02T14:41:12.325810Z",
     "shell.execute_reply": "2026-10-02T14:41:12.325339Z"
    },
    "papermill": {
     "duration": 1.213669,
     "end_time": "2026-10-02T14:41:12.326667+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:11.112998+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1790952072.310858] [runnervm8df0l:6353 :0]        ib_iface.c:1269 UCX  ERROR mana_0: iface 0x55e4f5108c70 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.311117] [runnervm8df0l:6353 :0]      ucp_worker.c:1412 UCX  ERROR uct_iface_open(ud_verbs/mana_0:1) failed: Input/output error\n",
      "[1.41421356]\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[runnervm8df0l:06353] pml_ucx.c:313  Error: Failed to create UCP worker\n"
     ]
    }
   ],
   "source": [
    "import openmdao.api as om\n",
    "\n",
    "prob = om.Problem()\n",
    "\n",
    "bal = om.BalanceComp()\n",
    "bal.add_balance('x', use_mult=True)\n",
    "\n",
    "exec_comp = om.ExecComp('y=x**2', x={'val': 1}, y={'val': 1})\n",
    "\n",
    "prob.model.add_subsystem(name='exec', subsys=exec_comp)\n",
    "prob.model.add_subsystem(name='balance', subsys=bal)\n",
    "\n",
    "prob.model.connect('balance.x', 'exec.x')\n",
    "prob.model.connect('exec.y', 'balance.lhs:x')\n",
    "\n",
    "prob.model.linear_solver = om.DirectSolver(assemble_jac=True)\n",
    "prob.model.nonlinear_solver = om.NewtonSolver(solve_subsystems=False, maxiter=100, iprint=0)\n",
    "\n",
    "prob.setup()\n",
    "\n",
    "prob.set_val('balance.rhs:x', 4)\n",
    "prob.set_val('balance.mult:x', 2.)\n",
    "\n",
    "# A reasonable initial guess to find the positive root.\n",
    "prob['balance.x'] = 1.0\n",
    "\n",
    "prob.run_model()\n",
    "\n",
    "print(prob.get_val('balance.x'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "12403449",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:12.331663Z",
     "iopub.status.busy": "2026-10-02T14:41:12.331514Z",
     "iopub.status.idle": "2026-10-02T14:41:12.334195Z",
     "shell.execute_reply": "2026-10-02T14:41:12.333528Z"
    },
    "papermill": {
     "duration": 0.005718,
     "end_time": "2026-10-02T14:41:12.334678+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:12.328960+00:00",
     "status": "completed"
    },
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from numpy.testing import assert_almost_equal\n",
    "\n",
    "assert_almost_equal(prob.get_val('balance.x'), np.sqrt(2), decimal=7)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "836eda5a",
   "metadata": {
    "papermill": {
     "duration": 0.001103,
     "end_time": "2026-10-02T14:41:12.337058+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:12.335955+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "Alternatively, we could simplify the code by using the `mult_val` argument."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "153443ad",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:12.340289Z",
     "iopub.status.busy": "2026-10-02T14:41:12.340100Z",
     "iopub.status.idle": "2026-10-02T14:41:12.351245Z",
     "shell.execute_reply": "2026-10-02T14:41:12.350563Z"
    },
    "papermill": {
     "duration": 0.013899,
     "end_time": "2026-10-02T14:41:12.352078+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:12.338179+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1.41421356]\n"
     ]
    }
   ],
   "source": [
    "prob = om.Problem()\n",
    "\n",
    "bal = om.BalanceComp()\n",
    "bal.add_balance('x', use_mult=True, mult_val=2.0)\n",
    "\n",
    "exec_comp = om.ExecComp('y=x**2', x={'val': 1}, y={'val': 1})\n",
    "\n",
    "prob.model.add_subsystem(name='exec', subsys=exec_comp)\n",
    "prob.model.add_subsystem(name='balance', subsys=bal)\n",
    "\n",
    "prob.model.connect('balance.x', 'exec.x')\n",
    "prob.model.connect('exec.y', 'balance.lhs:x')\n",
    "\n",
    "prob.model.linear_solver = om.DirectSolver(assemble_jac=True)\n",
    "prob.model.nonlinear_solver = om.NewtonSolver(solve_subsystems=False, maxiter=100, iprint=0)\n",
    "\n",
    "prob.setup()\n",
    "\n",
    "prob.set_val('balance.rhs:x', 4)\n",
    "\n",
    "# A reasonable initial guess to find the positive root.\n",
    "prob.set_val('balance.x', 1.0)\n",
    "\n",
    "prob.run_model()\n",
    "print(prob.get_val('balance.x'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "9c4e6bdc",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:12.355223Z",
     "iopub.status.busy": "2026-10-02T14:41:12.355076Z",
     "iopub.status.idle": "2026-10-02T14:41:12.357595Z",
     "shell.execute_reply": "2026-10-02T14:41:12.357029Z"
    },
    "papermill": {
     "duration": 0.004761,
     "end_time": "2026-10-02T14:41:12.358070+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:12.353309+00:00",
     "status": "completed"
    },
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert_almost_equal(prob.get_val('balance.x'), np.sqrt(2), decimal=7)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3e206ef3",
   "metadata": {
    "papermill": {
     "duration": 0.001539,
     "end_time": "2026-10-02T14:41:12.361170+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:12.359631+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "## Example:  Vectorized Root Finding\n",
    "\n",
    "The following example uses a BalanceComp to implicitly solve the equation:\n",
    "\n",
    "$$\n",
    "    b \\cdot x + c  = 0\n",
    "$$\n",
    "\n",
    "for various values of $b$, and $c$.  Here, our LHS is connected to a computed value of\n",
    "the linear equation.  The multiplier is one and the RHS is zero (the defaults), and thus\n",
    "they need not be connected."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "9aeae9fc",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:12.364906Z",
     "iopub.status.busy": "2026-10-02T14:41:12.364787Z",
     "iopub.status.idle": "2026-10-02T14:41:12.393360Z",
     "shell.execute_reply": "2026-10-02T14:41:12.392575Z"
    },
    "papermill": {
     "duration": 0.031101,
     "end_time": "2026-10-02T14:41:12.393865+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:12.362764+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[-2.52327640e-02 -2.23079847e-02 -3.79303206e-03 -4.78294136e-02\n",
      " -9.68444467e-03 -1.55469834e-02 -2.11765849e-01 -1.37130515e-03\n",
      " -1.50232135e-02 -1.75259692e-03 -6.65272994e-03 -7.34160306e-02\n",
      " -7.83935719e-03 -2.28807796e-03 -9.73079274e-03 -6.95325917e-03\n",
      " -1.21378812e-03 -2.82123632e-03 -4.66984474e-03 -5.72179633e-01\n",
      " -3.00739731e-03 -4.98219984e-03 -7.86009638e-03 -8.87962344e-03\n",
      " -2.74373215e-01 -6.50111768e-04 -3.89053311e-03 -8.05681602e-03\n",
      " -6.38926226e-02 -2.62842697e-04 -6.10950481e-03 -4.64221739e-03\n",
      " -2.63078627e-02 -8.52712988e-03 -2.10432086e-03 -1.67865023e-02\n",
      " -4.55490205e-03 -2.88193976e-03 -1.39239333e-03 -4.55488523e-03\n",
      " -4.71411649e-03 -8.54740260e-03 -7.86455043e-06 -6.67135050e-04\n",
      " -1.48933085e-02 -2.85644317e-02 -1.65709883e-02 -5.49188860e-03\n",
      " -7.22480144e-03 -1.18836809e-03 -5.98810520e-03 -1.11137948e-02\n",
      " -3.65671895e-02 -1.30993094e-02 -1.23576961e-03 -1.35987108e-02\n",
      " -1.14993021e-01 -2.02021271e-03 -6.82627475e-04 -6.84669180e-03\n",
      " -8.96978047e-03 -1.71489342e-02 -8.05758597e-03 -2.49409040e-02\n",
      " -1.75345270e-02 -2.21687744e-03 -3.60413870e-02 -4.04781509e-03\n",
      " -2.02480019e-02 -5.15746262e-03 -3.96282636e-03 -2.93881893e-02\n",
      " -1.17324540e-03 -6.83080580e-03 -7.29709441e-03 -3.79830018e-02\n",
      " -7.21322805e-03 -3.27528145e-02 -3.08794125e-02 -3.15667524e-02\n",
      " -3.08412854e-03 -3.37670527e-03 -2.61521615e-02 -2.13665327e-02\n",
      " -6.07582173e-02 -2.85527644e-02 -1.61719276e-02 -9.44870512e-03\n",
      " -1.07772588e-02 -1.13261837e-02 -7.72136677e-03 -1.57727974e-03\n",
      " -6.51434056e-02 -1.36630808e-02 -1.67471605e-02 -1.56000064e-02\n",
      " -2.05300852e-02 -4.44362620e-02 -3.64640986e-03 -3.71270250e-03]\n"
     ]
    }
   ],
   "source": [
    "n = 100\n",
    "\n",
    "prob = om.Problem()\n",
    "\n",
    "exec_comp = om.ExecComp('y=b*x+c',\n",
    "                        b={'val': np.random.uniform(0.01, 100, size=n)},\n",
    "                        c={'val': np.random.rand(n)},\n",
    "                        x={'val': np.zeros(n)},\n",
    "                        y={'val': np.ones(n)})\n",
    "\n",
    "prob.model.add_subsystem(name='exec', subsys=exec_comp)\n",
    "prob.model.add_subsystem(name='balance', subsys=om.BalanceComp('x', val=np.ones(n)))\n",
    "\n",
    "prob.model.connect('balance.x', 'exec.x')\n",
    "prob.model.connect('exec.y', 'balance.lhs:x')\n",
    "\n",
    "prob.model.linear_solver = om.DirectSolver(assemble_jac=True)\n",
    "prob.model.nonlinear_solver = om.NewtonSolver(solve_subsystems=False, maxiter=100, iprint=0)\n",
    "\n",
    "prob.setup()\n",
    "\n",
    "prob.set_val('balance.x', np.random.rand(n))\n",
    "\n",
    "prob.run_model()\n",
    "\n",
    "b = prob.get_val('exec.b')\n",
    "c = prob.get_val('exec.c')\n",
    "\n",
    "print(prob.get_val('balance.x'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "6ea82d8f",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:12.397140Z",
     "iopub.status.busy": "2026-10-02T14:41:12.397008Z",
     "iopub.status.idle": "2026-10-02T14:41:12.400070Z",
     "shell.execute_reply": "2026-10-02T14:41:12.399520Z"
    },
    "papermill": {
     "duration": 0.005451,
     "end_time": "2026-10-02T14:41:12.400617+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:12.395166+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[-2.52327640e-02 -2.23079847e-02 -3.79303206e-03 -4.78294136e-02\n",
      " -9.68444467e-03 -1.55469834e-02 -2.11765849e-01 -1.37130515e-03\n",
      " -1.50232135e-02 -1.75259692e-03 -6.65272994e-03 -7.34160306e-02\n",
      " -7.83935719e-03 -2.28807796e-03 -9.73079274e-03 -6.95325917e-03\n",
      " -1.21378812e-03 -2.82123632e-03 -4.66984474e-03 -5.72179633e-01\n",
      " -3.00739731e-03 -4.98219984e-03 -7.86009638e-03 -8.87962344e-03\n",
      " -2.74373215e-01 -6.50111768e-04 -3.89053311e-03 -8.05681602e-03\n",
      " -6.38926226e-02 -2.62842697e-04 -6.10950481e-03 -4.64221739e-03\n",
      " -2.63078627e-02 -8.52712988e-03 -2.10432086e-03 -1.67865023e-02\n",
      " -4.55490205e-03 -2.88193976e-03 -1.39239333e-03 -4.55488523e-03\n",
      " -4.71411649e-03 -8.54740260e-03 -7.86455043e-06 -6.67135050e-04\n",
      " -1.48933085e-02 -2.85644317e-02 -1.65709883e-02 -5.49188860e-03\n",
      " -7.22480144e-03 -1.18836809e-03 -5.98810520e-03 -1.11137948e-02\n",
      " -3.65671895e-02 -1.30993094e-02 -1.23576961e-03 -1.35987108e-02\n",
      " -1.14993021e-01 -2.02021271e-03 -6.82627475e-04 -6.84669180e-03\n",
      " -8.96978047e-03 -1.71489342e-02 -8.05758597e-03 -2.49409040e-02\n",
      " -1.75345270e-02 -2.21687744e-03 -3.60413870e-02 -4.04781509e-03\n",
      " -2.02480019e-02 -5.15746262e-03 -3.96282636e-03 -2.93881893e-02\n",
      " -1.17324540e-03 -6.83080580e-03 -7.29709441e-03 -3.79830018e-02\n",
      " -7.21322805e-03 -3.27528145e-02 -3.08794125e-02 -3.15667524e-02\n",
      " -3.08412854e-03 -3.37670527e-03 -2.61521615e-02 -2.13665327e-02\n",
      " -6.07582173e-02 -2.85527644e-02 -1.61719276e-02 -9.44870512e-03\n",
      " -1.07772588e-02 -1.13261837e-02 -7.72136677e-03 -1.57727974e-03\n",
      " -6.51434056e-02 -1.36630808e-02 -1.67471605e-02 -1.56000064e-02\n",
      " -2.05300852e-02 -4.44362620e-02 -3.64640986e-03 -3.71270250e-03]\n"
     ]
    }
   ],
   "source": [
    "print(-c/b)  # expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "6eca4de3",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:12.403830Z",
     "iopub.status.busy": "2026-10-02T14:41:12.403687Z",
     "iopub.status.idle": "2026-10-02T14:41:12.406289Z",
     "shell.execute_reply": "2026-10-02T14:41:12.405675Z"
    },
    "papermill": {
     "duration": 0.004882,
     "end_time": "2026-10-02T14:41:12.406770+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:12.401888+00:00",
     "status": "completed"
    },
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert_almost_equal(prob.get_val('balance.x'), -c/b, decimal=6)\n",
    "assert_almost_equal(-c/b, prob.get_val('balance.x'), decimal=6)  # expected"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "725f1ca7",
   "metadata": {
    "papermill": {
     "duration": 0.001219,
     "end_time": "2026-10-02T14:41:12.409233+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:12.408014+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "## Example:  Providing an Initial Guess for a State Variable\n",
    "\n",
    "`BalanceComp` has a `guess_func` option that can be used to supply an initial guess\n",
    "value for the state variables.  This option provides the same functionality as the\n",
    "`guess_nonlinear` method of [ImplicitComponent](../../../_srcdocs/packages/core/implicitcomponent).\n",
    "\n",
    "The Kepler example script shows how `guess_func` can be used."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "55e153ec",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:12.412280Z",
     "iopub.status.busy": "2026-10-02T14:41:12.412155Z",
     "iopub.status.idle": "2026-10-02T14:41:12.420742Z",
     "shell.execute_reply": "2026-10-02T14:41:12.420100Z"
    },
    "papermill": {
     "duration": 0.010732,
     "end_time": "2026-10-02T14:41:12.421217+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:12.410485+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NL: Newton 0 ; 1.30402513 1\n",
      "NL: Newton 1 ; 0.117134648 0.0898254536\n",
      "NL: Newton 2 ; 0.00208780933 0.00160104992\n",
      "NL: Newton 3 ; 7.36738647e-07 5.64972738e-07\n",
      "NL: Newton 4 ; 9.19264664e-14 7.04943981e-14\n",
      "NL: Newton Converged\n",
      "[115.91942563]\n"
     ]
    }
   ],
   "source": [
    "prob = om.Problem()\n",
    "\n",
    "bal = om.BalanceComp()\n",
    "\n",
    "bal.add_balance(name='E', val=0.0, units='rad', eq_units='rad', rhs_name='M')\n",
    "\n",
    "# Use M (mean anomaly) as the initial guess for E (eccentric anomaly)\n",
    "def guess_function(inputs, outputs, residuals):\n",
    "    if np.abs(residuals['E']) > 1.0E-2:\n",
    "        outputs['E'] = inputs['M']\n",
    "\n",
    "bal.options['guess_func'] = guess_function\n",
    "\n",
    "# ExecComp used to compute the LHS of Kepler's equation.\n",
    "lhs_comp = om.ExecComp('lhs=E - ecc * sin(E)',\n",
    "                       lhs={'val': 0.0, 'units': 'rad'},\n",
    "                       E={'val': 0.0, 'units': 'rad'},\n",
    "                       ecc={'val': 0.0})\n",
    "\n",
    "prob.model.add_subsystem(name='balance', subsys=bal,\n",
    "                         promotes_inputs=['M'],\n",
    "                         promotes_outputs=['E'])\n",
    "\n",
    "prob.model.set_input_defaults('M', 85.0, units='deg')\n",
    "\n",
    "prob.model.add_subsystem(name='lhs_comp', subsys=lhs_comp,\n",
    "                         promotes_inputs=['E', 'ecc'])\n",
    "\n",
    "# Explicit connections\n",
    "prob.model.connect('lhs_comp.lhs', 'balance.lhs:E')\n",
    "\n",
    "# Set up solvers\n",
    "prob.model.linear_solver = om.DirectSolver()\n",
    "prob.model.nonlinear_solver = om.NewtonSolver(solve_subsystems=False, maxiter=100, iprint=2)\n",
    "\n",
    "prob.setup()\n",
    "\n",
    "prob.set_val('ecc', 0.6)\n",
    "\n",
    "prob.run_model()\n",
    "\n",
    "print(np.degrees(prob.get_val('E')))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "f810c1c6",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:12.424372Z",
     "iopub.status.busy": "2026-10-02T14:41:12.424245Z",
     "iopub.status.idle": "2026-10-02T14:41:12.426831Z",
     "shell.execute_reply": "2026-10-02T14:41:12.426113Z"
    },
    "papermill": {
     "duration": 0.004804,
     "end_time": "2026-10-02T14:41:12.427249+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:12.422445+00:00",
     "status": "completed"
    },
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert_almost_equal(np.degrees(prob.get_val('E')), 115.9, decimal=1)"
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "Tags",
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.14"
  },
  "orphan": true,
  "papermill": {
   "default_parameters": {},
   "duration": 3.67372,
   "end_time": "2026-10-02T14:41:12.944178+00:00",
   "environment_variables": {},
   "exception": null,
   "input_path": "/home/runner/work/OpenMDAO/OpenMDAO/openmdao/docs/openmdao_book/features/building_blocks/components/balance_comp.ipynb",
   "output_path": "/home/runner/work/OpenMDAO/OpenMDAO/openmdao/docs/_executed_book/features/building_blocks/components/balance_comp.ipynb",
   "parameters": {},
   "start_time": "2026-10-02T14:41:09.270458+00:00",
   "version": "2.7.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}