{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "4dddd2de",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:29.225901Z",
     "iopub.status.busy": "2026-10-02T14:41:29.225650Z",
     "iopub.status.idle": "2026-10-02T14:41:29.231125Z",
     "shell.execute_reply": "2026-10-02T14:41:29.230218Z"
    },
    "papermill": {
     "duration": 0.008912,
     "end_time": "2026-10-02T14:41:29.231663+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:29.222751+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": "53ab356c",
   "metadata": {
    "papermill": {
     "duration": 0.00168,
     "end_time": "2026-10-02T14:41:29.235320+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:29.233640+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "# KSComp\n",
    "\n",
    "KSComp provides a way to aggregate many constraints into a single constraint. This is usually done for performance\n",
    "reasons, in particular, to reduce the calculation time needed for the total derivatives of your model. The KSComp\n",
    "implements the Kreisselmeier-Steinhauser Function to aggregate constraint vector input \"g\" into a single scalar output 'KS'.\n",
    "\n",
    "By default, the constraint vector \"g\" is assumed be of the form where g<=0 satisfies the constraints, but other forms can be specified using the \"upper\" and \"lower_flag\" options.\n",
    "\n",
    "The output \"KS\" should be constrained with an upper-bound of zero to satisfy the aggregated constraint.\n",
    "By default, it is left to the user to provide this constraint.  However, setting option \"add_constraint\"\n",
    "to True will cause the KSComp to automatically add a constraint to the optimization.\n",
    "\n",
    "## KSComp Options"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "3d39c173",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:29.239940Z",
     "iopub.status.busy": "2026-10-02T14:41:29.239782Z",
     "iopub.status.idle": "2026-10-02T14:41:30.471011Z",
     "shell.execute_reply": "2026-10-02T14:41:30.470269Z"
    },
    "papermill": {
     "duration": 1.234744,
     "end_time": "2026-10-02T14:41:30.471820+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:29.237076+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;\">add_constraint</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, add a constraint on the resulting output of the KSComp. If False, the user will be expected to add a constraint explicitly.</td></tr>\n",
       "       <tr style=\"background-color: #F3F3F3;\"><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">adder</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;int&#x27;, &#x27;float&#x27;]</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">Adder for constraint, if added, default is zero.</td></tr>\n",
       "       <tr style=\"background-color: ghostwhite;\"><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">always_opt</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">False</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">[True, False]</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">[&#x27;bool&#x27;]</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">If True, force nonlinear operations on this component to be included in the optimization loop even if this component is not relevant to the design variables and responses.</td></tr>\n",
       "       <tr style=\"background-color: #F3F3F3;\"><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">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;\">lower_flag</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;\">Set to True to reverse sign of input constraints.</td></tr>\n",
       "       <tr style=\"background-color: #F3F3F3;\"><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">minimum</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;\">Return the minimum instead of the maximum by multiplying both the inputs and output by -1. It is not recommended to use both this option and the lower_flag option (it will return the negative of the aggregated max.)</td></tr>\n",
       "       <tr style=\"background-color: ghostwhite;\"><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">parallel_deriv_color</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">N/A</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">N/A</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">[&#x27;str&#x27;]</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">If specified, this design var will be grouped for parallel derivative calculations with other variables sharing the same parallel_deriv_color.</td></tr>\n",
       "       <tr style=\"background-color: #F3F3F3;\"><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">ref</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;int&#x27;, &#x27;float&#x27;]</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">Unit reference for constraint, if added, default is one.</td></tr>\n",
       "       <tr style=\"background-color: ghostwhite;\"><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">ref0</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;int&#x27;, &#x27;float&#x27;]</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">Zero-reference for constraint, if added, default is zero.</td></tr>\n",
       "       <tr style=\"background-color: #F3F3F3;\"><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">rho</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">50.0</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;\">Constraint Aggregation Factor.</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;\">scaler</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;int&#x27;, &#x27;float&#x27;]</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">Scaler for constraint, if added, default is one.</td></tr>\n",
       "       <tr style=\"background-color: ghostwhite;\"><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">units</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">N/A</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">N/A</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">[&#x27;str&#x27;]</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">Units to be assigned to all variables in this component. Default is None, which means variables are unitless.</td></tr>\n",
       "       <tr style=\"background-color: #F3F3F3;\"><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">upper</td><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">0.0</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;\">Upper bound for constraint, default is zero.</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;\">The number of rows to independently aggregate.</td></tr>\n",
       "       <tr style=\"background-color: ghostwhite;\"><td style=\"border: 1px solid #999; border-collapse: collapse; padding: 5px; text-align: left;\">width</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;\">Width of constraint vector.</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.ks_comp.KSComp\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "34641489",
   "metadata": {
    "papermill": {
     "duration": 0.091864,
     "end_time": "2026-10-02T14:41:30.565356+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:30.473492+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "## KSComp Constructor\n",
    "\n",
    "The call signature for the `KSComp` constructor is:\n",
    "\n",
    "```{eval-rst}\n",
    "    .. automethod:: openmdao.components.ks_comp.KSComp.__init__\n",
    "        :noindex:\n",
    "```\n",
    "\n",
    "## KSComp Example\n",
    "\n",
    "The following example is perhaps the simplest possible. It shows a component that represents a constraint\n",
    "of width two. We would like to aggregate the values of this constraint vector into a single scalar\n",
    "value using the KSComp."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "0f1b5810",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:30.607951Z",
     "iopub.status.busy": "2026-10-02T14:41:30.607551Z",
     "iopub.status.idle": "2026-10-02T14:41:31.838900Z",
     "shell.execute_reply": "2026-10-02T14:41:31.838468Z"
    },
    "papermill": {
     "duration": 1.272669,
     "end_time": "2026-10-02T14:41:31.839519+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:30.566850+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1790952091.826888] [runnervm8df0l:6611 :0]        ib_iface.c:1269 UCX  ERROR mana_0: iface 0x5627f3a2ee80 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",
      "[1790952091.827125] [runnervm8df0l:6611 :0]      ucp_worker.c:1412 UCX  ERROR uct_iface_open(ud_verbs/mana_0:1) failed: Input/output error\n",
      "[[15.]]\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[runnervm8df0l:06611] pml_ucx.c:313  Error: Failed to create UCP worker\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "import openmdao.api as om\n",
    "\n",
    "prob = om.Problem()\n",
    "model = prob.model\n",
    "\n",
    "model.add_subsystem('comp', om.ExecComp('y = 3.0*x',\n",
    "                                        x=np.zeros((2, )),\n",
    "                                        y=np.zeros((2, ))), promotes_inputs=['x'])\n",
    "\n",
    "model.add_subsystem('ks', om.KSComp(width=2))\n",
    "\n",
    "model.connect('comp.y', 'ks.g')\n",
    "\n",
    "prob.setup()\n",
    "prob.set_val('x', np.array([5.0, 4.0]))\n",
    "prob.run_model()\n",
    "\n",
    "print(prob.get_val('ks.KS'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "91a3fe94",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:31.844830Z",
     "iopub.status.busy": "2026-10-02T14:41:31.844637Z",
     "iopub.status.idle": "2026-10-02T14:41:31.850125Z",
     "shell.execute_reply": "2026-10-02T14:41:31.849384Z"
    },
    "papermill": {
     "duration": 0.008711,
     "end_time": "2026-10-02T14:41:31.850621+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:31.841910+00:00",
     "status": "completed"
    },
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "np.float64(0.0)"
      ]
     },
     "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('ks.KS'), [[15.0]])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dac06e9d",
   "metadata": {
    "papermill": {
     "duration": 0.001476,
     "end_time": "2026-10-02T14:41:31.853992+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:31.852516+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "A more practical example that uses the KSComp can be found in the [beam optimization](../../../examples/beam_optimization_example_part_2) example.\n",
    "\n",
    "You can also independently aggregate multiple rows of an output as separate constraints by declaring the vec_size argument:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "0f5495e8",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:31.857931Z",
     "iopub.status.busy": "2026-10-02T14:41:31.857796Z",
     "iopub.status.idle": "2026-10-02T14:41:31.864213Z",
     "shell.execute_reply": "2026-10-02T14:41:31.863251Z"
    },
    "papermill": {
     "duration": 0.008937,
     "end_time": "2026-10-02T14:41:31.864675+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:31.855738+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[15.]\n",
      " [30.]]\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "\n",
    "import openmdao.api as om\n",
    "\n",
    "prob = om.Problem()\n",
    "model = prob.model\n",
    "\n",
    "model.add_subsystem('comp', om.ExecComp('y = 3.0*x',\n",
    "                                        x=np.zeros((2, 2)),\n",
    "                                        y=np.zeros((2, 2))), promotes_inputs=['x'])\n",
    "model.add_subsystem('ks', om.KSComp(width=2, vec_size=2))\n",
    "\n",
    "model.connect('comp.y', 'ks.g')\n",
    "\n",
    "prob.setup()\n",
    "prob.set_val('x', np.array([[5.0, 4.0], [10.0, 8.0]]))\n",
    "prob.run_model()\n",
    "\n",
    "print(prob.get_val('ks.KS'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "62f1f31a",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:31.868304Z",
     "iopub.status.busy": "2026-10-02T14:41:31.868185Z",
     "iopub.status.idle": "2026-10-02T14:41:31.871515Z",
     "shell.execute_reply": "2026-10-02T14:41:31.870850Z"
    },
    "papermill": {
     "duration": 0.005956,
     "end_time": "2026-10-02T14:41:31.872206+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:31.866250+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": [
    "assert_near_equal(prob.get_val('ks.KS'), np.array([[15], [30]]))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "11b9e2d6",
   "metadata": {
    "papermill": {
     "duration": 0.002169,
     "end_time": "2026-10-02T14:41:31.876510+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:31.874341+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "## KSComp Option Examples\n",
    "\n",
    "Normally, the input constraint vector is assumed to be of the form g<=0 is satisfied. If you would like to set a\n",
    "different upper bound for the constraint, you can declare it in the \"upper\" option in the options dictionary.\n",
    "\n",
    "In the following example, we specify a new upper bound of 16 for the constraint vector. Note that the KS output\n",
    "is still satisfied if it is less than zero.\n",
    "\n",
    "**upper**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "fd34ae97",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:31.886528Z",
     "iopub.status.busy": "2026-10-02T14:41:31.886411Z",
     "iopub.status.idle": "2026-10-02T14:41:31.891647Z",
     "shell.execute_reply": "2026-10-02T14:41:31.891067Z"
    },
    "papermill": {
     "duration": 0.008447,
     "end_time": "2026-10-02T14:41:31.892202+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:31.883755+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[-1.]]\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "\n",
    "prob = om.Problem()\n",
    "model = prob.model\n",
    "\n",
    "model.add_subsystem('comp', om.ExecComp('y = 3.0*x',\n",
    "                                        x=np.zeros((2, )),\n",
    "                                        y=np.zeros((2, ))), promotes_inputs=['x'])\n",
    "model.add_subsystem('ks', om.KSComp(width=2))\n",
    "\n",
    "model.connect('comp.y', 'ks.g')\n",
    "\n",
    "model.ks.options['upper'] = 16.0\n",
    "prob.setup()\n",
    "prob.set_val('x', np.array([5.0, 4.0]))\n",
    "prob.run_model()\n",
    "\n",
    "print(prob['ks.KS'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "651fb90b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:31.896045Z",
     "iopub.status.busy": "2026-10-02T14:41:31.895876Z",
     "iopub.status.idle": "2026-10-02T14:41:31.899430Z",
     "shell.execute_reply": "2026-10-02T14:41:31.898846Z"
    },
    "papermill": {
     "duration": 0.006097,
     "end_time": "2026-10-02T14:41:31.899872+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:31.893775+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": [
    "assert_near_equal(prob['ks.KS'], np.array([[-1.0]]))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fde28b02",
   "metadata": {
    "papermill": {
     "duration": 0.001492,
     "end_time": "2026-10-02T14:41:31.902898+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:31.901406+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "Normally, the input constraint vector is satisfied if it is negative and violated if it is positive. You can\n",
    "reverse this behavior by setting the \"lower_flag\" option to True. In the following example, we turn on the\n",
    "\"lower_flag\" so that positive values of the input constraint are considered satisfied. Note that the KS output\n",
    "is still satisfied if it is less than zero.\n",
    "\n",
    "**lower_flag**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "691e9c33",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:31.907316Z",
     "iopub.status.busy": "2026-10-02T14:41:31.907151Z",
     "iopub.status.idle": "2026-10-02T14:41:31.913714Z",
     "shell.execute_reply": "2026-10-02T14:41:31.913160Z"
    },
    "papermill": {
     "duration": 0.009418,
     "end_time": "2026-10-02T14:41:31.914170+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:31.904752+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[-12.]]\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "\n",
    "prob = om.Problem()\n",
    "model = prob.model\n",
    "\n",
    "model.add_subsystem('comp', om.ExecComp('y = 3.0*x',\n",
    "                                        x=np.zeros((2, )),\n",
    "                                        y=np.zeros((2, ))), promotes_inputs=['x'])\n",
    "\n",
    "model.add_subsystem('ks', om.KSComp(width=2))\n",
    "\n",
    "model.connect('comp.y', 'ks.g')\n",
    "\n",
    "model.ks.options['lower_flag'] = True\n",
    "prob.setup()\n",
    "prob.set_val('x', np.array([5.0, 4.0]))\n",
    "prob.run_model()\n",
    "\n",
    "print(prob.get_val('ks.KS'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "e9561214",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:31.918041Z",
     "iopub.status.busy": "2026-10-02T14:41:31.917907Z",
     "iopub.status.idle": "2026-10-02T14:41:31.921091Z",
     "shell.execute_reply": "2026-10-02T14:41:31.920543Z"
    },
    "papermill": {
     "duration": 0.005929,
     "end_time": "2026-10-02T14:41:31.921718+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:31.915789+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": [
    "assert_near_equal(prob.get_val('ks.KS'), [[-12.0]])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "37507a8c",
   "metadata": {
    "papermill": {
     "duration": 0.001542,
     "end_time": "2026-10-02T14:41:31.924840+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:31.923298+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "Typically, the KSComp is used to provide a constraint which aggregates many values into a single scalar constraint.\n",
    "Consider the following simple example, where we seek to maximize the peak of a parabola but also\n",
    "keep the peak of the parabola below a certain threshold value.  Clearly, the solution here is to have the peak of\n",
    "the parabola lie on the peak constraint.\n",
    "\n",
    "Note the resulting value of the offset \"k\" is not exactly 4.0 as we might expect.  The KS function\n",
    "provides a differentiable constraint aggregation, but the resulting scalar constraint is slightly\n",
    "conservative.\n",
    "\n",
    "**add_constraint**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "564234c5",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:31.928747Z",
     "iopub.status.busy": "2026-10-02T14:41:31.928555Z",
     "iopub.status.idle": "2026-10-02T14:41:31.980495Z",
     "shell.execute_reply": "2026-10-02T14:41:31.979882Z"
    },
    "papermill": {
     "duration": 0.054815,
     "end_time": "2026-10-02T14:41:31.981198+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:31.926383+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Optimization terminated successfully    (Exit mode 0)\n",
      "            Current function value: -3.9837172503087404\n",
      "            Iterations: 2\n",
      "            Function evaluations: 2\n",
      "            Gradient evaluations: 2\n",
      "Optimization Complete\n",
      "-----------------------------------\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "n = 50\n",
    "prob = om.Problem()\n",
    "model = prob.model\n",
    "\n",
    "prob.driver = om.ScipyOptimizeDriver()\n",
    "\n",
    "model.add_subsystem('comp', om.ExecComp('y = -3.0*x**2 + k',\n",
    "                                        x=np.zeros((n, )),\n",
    "                                        y=np.zeros((n, )),\n",
    "                                        k=0.0), promotes_inputs=['x', 'k'])\n",
    "\n",
    "model.add_subsystem('ks', om.KSComp(width=n, upper=4.0, add_constraint=True))\n",
    "\n",
    "model.add_design_var('k', lower=-10, upper=10)\n",
    "model.add_objective('k', scaler=-1)\n",
    "\n",
    "model.connect('comp.y', 'ks.g')\n",
    "\n",
    "prob.setup()\n",
    "prob.set_val('x', np.linspace(-np.pi/2, np.pi/2, n))\n",
    "prob.set_val('k', 5.)\n",
    "\n",
    "prob.run_driver()\n",
    "\n",
    "fig, ax = plt.subplots()\n",
    "\n",
    "x = prob.get_val('x')\n",
    "y = prob.get_val('comp.y')\n",
    "\n",
    "ax.plot(x, y, 'r.')\n",
    "ax.plot(x, 4.0*np.ones_like(x), 'k--')\n",
    "ax.set_xlabel('x')\n",
    "ax.set_ylabel('y')\n",
    "ax.grid(True)\n",
    "ax.text(-0.25, 0, f\"k = {prob.get_val('k')[0]:6.3f}\")\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "bbf7029a",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:31.985448Z",
     "iopub.status.busy": "2026-10-02T14:41:31.985238Z",
     "iopub.status.idle": "2026-10-02T14:41:31.988189Z",
     "shell.execute_reply": "2026-10-02T14:41:31.987314Z"
    },
    "papermill": {
     "duration": 0.005715,
     "end_time": "2026-10-02T14:41:31.988645+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:31.982930+00:00",
     "status": "completed"
    },
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert(max(prob.get_val('comp.y')) <= 4.0)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "be9b03ec",
   "metadata": {
    "papermill": {
     "duration": 0.00168,
     "end_time": "2026-10-02T14:41:32.104488+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:32.102808+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "**units**\n",
    "\n",
    "Finally, note that you can pass a units option to the KSComp that will define units on its input and output variables. There is only one unit, shared between both inputs and outputs."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "e8e094cb",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:32.108597Z",
     "iopub.status.busy": "2026-10-02T14:41:32.108423Z",
     "iopub.status.idle": "2026-10-02T14:41:32.197376Z",
     "shell.execute_reply": "2026-10-02T14:41:32.196544Z"
    },
    "papermill": {
     "duration": 0.091745,
     "end_time": "2026-10-02T14:41:32.197899+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:32.106154+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[2.7432]\n"
     ]
    }
   ],
   "source": [
    "n = 10\n",
    "\n",
    "model = om.Group()\n",
    "\n",
    "model.add_subsystem('ks', om.KSComp(width=n, units='m'), promotes_inputs=[('g', 'x')])\n",
    "model.set_input_defaults('x', range(n), units='ft')\n",
    "\n",
    "prob = om.Problem(model=model)\n",
    "prob.setup()\n",
    "prob.run_model()\n",
    "\n",
    "print(prob.get_val('ks.KS', indices=0))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "757d6f45",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:32.203117Z",
     "iopub.status.busy": "2026-10-02T14:41:32.202984Z",
     "iopub.status.idle": "2026-10-02T14:41:32.206397Z",
     "shell.execute_reply": "2026-10-02T14:41:32.205632Z"
    },
    "papermill": {
     "duration": 0.006892,
     "end_time": "2026-10-02T14:41:32.206853+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:32.199961+00:00",
     "status": "completed"
    },
    "tags": [
     "remove-output",
     "remove-input"
    ]
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "np.float64(1.7543840396176098e-09)"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "assert_near_equal(prob.get_val('ks.KS', indices=0), np.amax(prob.get_val('x', units='m')), tolerance=1e-8)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1f6d99b2",
   "metadata": {
    "papermill": {
     "duration": 0.001646,
     "end_time": "2026-10-02T14:41:32.210225+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:32.208579+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "**Example: KSComp for aggregating a constraint with a lower bound.**\n",
    "\n",
    "When you are using the KSComp to aggregate a constraint with a lower bound, set the \"lower_flag\"\n",
    "to True, and it will create a constraint that is satisfied when it is greater than the value\n",
    "specified in \"upper\". Because KSComp is a smooth max function, you can pass in any values to\n",
    "\"g\" and it will approximate both the min and max."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "a561dc54",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:32.214207Z",
     "iopub.status.busy": "2026-10-02T14:41:32.214072Z",
     "iopub.status.idle": "2026-10-02T14:41:32.228987Z",
     "shell.execute_reply": "2026-10-02T14:41:32.228277Z"
    },
    "papermill": {
     "duration": 0.017692,
     "end_time": "2026-10-02T14:41:32.229576+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:32.211884+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Optimization terminated successfully    (Exit mode 0)\n",
      "            Current function value: -10.0\n",
      "            Iterations: 3\n",
      "            Function evaluations: 3\n",
      "            Gradient evaluations: 3\n",
      "Optimization Complete\n",
      "-----------------------------------\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[-1.58393376]]\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "n = 50\n",
    "prob = om.Problem()\n",
    "model = prob.model\n",
    "\n",
    "prob.driver = om.ScipyOptimizeDriver()\n",
    "\n",
    "model.add_subsystem('comp', om.ExecComp('y = -3.0*x**2 + k',\n",
    "                                        x=np.zeros((n, )),\n",
    "                                        y=np.zeros((n, )),\n",
    "                                        k=0.0), promotes_inputs=['x', 'k'])\n",
    "\n",
    "model.add_subsystem('ks', om.KSComp(width=n, upper=1.0, lower_flag=True))\n",
    "\n",
    "model.add_design_var('k', lower=-10, upper=10)\n",
    "model.add_objective('k', scaler=-1)\n",
    "\n",
    "model.connect('comp.y', 'ks.g')\n",
    "\n",
    "prob.setup()\n",
    "prob.set_val('x', np.linspace(-np.pi/2, np.pi/2, n))\n",
    "prob.set_val('k', 5.)\n",
    "\n",
    "prob.run_driver()\n",
    "\n",
    "fig, ax = plt.subplots()\n",
    "\n",
    "x = prob.get_val('x')\n",
    "y = prob.get_val('comp.y')\n",
    "\n",
    "ax.plot(x, y, 'r.')\n",
    "ax.set_xlabel('x')\n",
    "ax.set_ylabel('y')\n",
    "ax.grid(True)\n",
    "ax.text(-0.25, 0, f\"k = {prob.get_val('k')[0]:6.3f}\")\n",
    "\n",
    "plt.show()\n",
    "\n",
    "print(prob['ks.KS'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "e9c22208",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:32.233897Z",
     "iopub.status.busy": "2026-10-02T14:41:32.233733Z",
     "iopub.status.idle": "2026-10-02T14:41:32.236472Z",
     "shell.execute_reply": "2026-10-02T14:41:32.235706Z"
    },
    "papermill": {
     "duration": 0.005576,
     "end_time": "2026-10-02T14:41:32.236916+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:32.231340+00:00",
     "status": "completed"
    },
    "tags": [
     "remove-output",
     "remove-input"
    ]
   },
   "outputs": [],
   "source": [
    "assert(prob['ks.KS'] <= 0)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "70449d77",
   "metadata": {
    "papermill": {
     "duration": 0.001728,
     "end_time": "2026-10-02T14:41:32.240394+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:32.238666+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "**Example: KSComp for aggregating a constraint with a non-zero upper bound.**\n",
    "\n",
    "Here we use the KSComp to aggregate a constraint with an upper bound."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "9a447ce1",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:32.244657Z",
     "iopub.status.busy": "2026-10-02T14:41:32.244487Z",
     "iopub.status.idle": "2026-10-02T14:41:32.263033Z",
     "shell.execute_reply": "2026-10-02T14:41:32.262246Z"
    },
    "papermill": {
     "duration": 0.021343,
     "end_time": "2026-10-02T14:41:32.263490+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:32.242147+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Optimization terminated successfully    (Exit mode 0)\n",
      "            Current function value: -10.0\n",
      "            Iterations: 3\n",
      "            Function evaluations: 3\n",
      "            Gradient evaluations: 3\n",
      "Optimization Complete\n",
      "-----------------------------------\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[-4.98371725]]\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "n = 50\n",
    "prob = om.Problem()\n",
    "model = prob.model\n",
    "\n",
    "prob.driver = om.ScipyOptimizeDriver()\n",
    "\n",
    "model.add_subsystem('comp', om.ExecComp('y = -3.0*x**2 + k',\n",
    "                                        x=np.zeros((n, )),\n",
    "                                        y=np.zeros((n, )),\n",
    "                                        k=0.0), promotes_inputs=['x', 'k'])\n",
    "\n",
    "model.add_subsystem('ks', om.KSComp(width=n, upper=15.0))\n",
    "\n",
    "model.add_design_var('k', lower=-10, upper=10)\n",
    "model.add_objective('k', scaler=-1)\n",
    "\n",
    "model.connect('comp.y', 'ks.g')\n",
    "\n",
    "prob.setup()\n",
    "prob.set_val('x', np.linspace(-np.pi/2, np.pi/2, n))\n",
    "prob.set_val('k', 5.)\n",
    "\n",
    "prob.run_driver()\n",
    "\n",
    "fig, ax = plt.subplots()\n",
    "x = prob.get_val('x')\n",
    "y = prob.get_val('comp.y')\n",
    "\n",
    "ax.plot(x, y, 'r.')\n",
    "ax.set_xlabel('x')\n",
    "ax.set_ylabel('y')\n",
    "ax.grid(True)\n",
    "ax.text(-0.25, 0, f\"k = {prob.get_val('k')[0]:6.3f}\")\n",
    "\n",
    "plt.show()\n",
    "\n",
    "print(prob['ks.KS'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "745ba64b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:41:32.267963Z",
     "iopub.status.busy": "2026-10-02T14:41:32.267834Z",
     "iopub.status.idle": "2026-10-02T14:41:32.270176Z",
     "shell.execute_reply": "2026-10-02T14:41:32.269477Z"
    },
    "papermill": {
     "duration": 0.005249,
     "end_time": "2026-10-02T14:41:32.270593+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:41:32.265344+00:00",
     "status": "completed"
    },
    "tags": [
     "remove-output",
     "remove-input"
    ]
   },
   "outputs": [],
   "source": [
    "assert(prob['ks.KS'] <= 0)"
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "Tags",
  "kernelspec": {
   "display_name": "dev311",
   "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.130901,
   "end_time": "2026-10-02T14:41:32.687441+00:00",
   "environment_variables": {},
   "exception": null,
   "input_path": "/home/runner/work/OpenMDAO/OpenMDAO/openmdao/docs/openmdao_book/features/building_blocks/components/ks_comp.ipynb",
   "output_path": "/home/runner/work/OpenMDAO/OpenMDAO/openmdao/docs/_executed_book/features/building_blocks/components/ks_comp.ipynb",
   "parameters": {},
   "start_time": "2026-10-02T14:41:28.556540+00:00",
   "version": "2.7.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}