{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Entropyk: Inverse Control Example\n",
    "\n",
    "This notebook demonstrates **Entropyk's One-Shot Inverse Solver**. Unlike traditional approaches that wrap a forward solver in an optimizer (like `scipy.optimize`), Entropyk embeds constraints directly into the Newton-Raphson system.\n",
    "\n",
    "This allows finding continuous control variables (like compressor speed or valve opening) to achieve target outputs (like superheat or cooling capacity) **simultaneously** with the thermodynamic state."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-02-21T19:41:20.922472Z",
     "iopub.status.busy": "2026-02-21T19:41:20.922358Z",
     "iopub.status.idle": "2026-02-21T19:41:21.276770Z",
     "shell.execute_reply": "2026-02-21T19:41:21.276339Z"
    }
   },
   "outputs": [],
   "source": [
    "import entropyk\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. Build the System\n",
    "First, we build a standard refrigeration cycle."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-02-21T19:41:21.278270Z",
     "iopub.status.busy": "2026-02-21T19:41:21.278168Z",
     "iopub.status.idle": "2026-02-21T19:41:21.280991Z",
     "shell.execute_reply": "2026-02-21T19:41:21.280607Z"
    }
   },
   "outputs": [],
   "source": [
    "system = entropyk.System()\n",
    "\n",
    "# Add components\n",
    "comp_idx = system.add_component(entropyk.Compressor(speed_rpm=3000.0, displacement=0.0001, efficiency=0.85, fluid=\"R134a\"))\n",
    "cond_idx = system.add_component(entropyk.Condenser(ua=5000.0))\n",
    "exv_idx = system.add_component(entropyk.ExpansionValve(fluid=\"R134a\", opening=0.5))\n",
    "evap_idx = system.add_component(entropyk.Evaporator(ua=3000.0))\n",
    "\n",
    "# Connect cycle\n",
    "system.add_edge(comp_idx, cond_idx)\n",
    "system.add_edge(cond_idx, exv_idx)\n",
    "system.add_edge(exv_idx, evap_idx)\n",
    "system.add_edge(evap_idx, comp_idx)\n",
    "\n",
    "# Register names for inverse control references\n",
    "system.register_component_name(\"evaporator\", evap_idx)\n",
    "system.register_component_name(\"valve\", exv_idx)\n",
    "system.register_component_name(\"compressor\", comp_idx)\n",
    "\n",
    "system.finalize()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. Introduce Inverse Control\n",
    "\n",
    "We want to find the **Expansion Valve Opening** required to achieve exactly **5.0 K of Superheat** at the evaporator outlet.\n",
    "\n",
    "1. Define a `Constraint` (Superheat = 5K)\n",
    "2. Define a `BoundedVariable` (Valve Opening between 0% and 100%)\n",
    "3. Bind them together in the system."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-02-21T19:41:21.282084Z",
     "iopub.status.busy": "2026-02-21T19:41:21.282025Z",
     "iopub.status.idle": "2026-02-21T19:41:21.284245Z",
     "shell.execute_reply": "2026-02-21T19:41:21.283853Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Constraint: Constraint(id='sh_ctrl', target=5, tol=0.0001)\n",
      "Bounded Var: BoundedVariable(id='exv_opening', value=0.5, bounds=[0, 1])\n",
      "\n",
      "✅ Inverse control configured successfully!\n"
     ]
    }
   ],
   "source": [
    "# 1. Superheat Constraint (Target: 5.0 K, Tolerance: 1e-4)\n",
    "sh_constraint = entropyk.Constraint.superheat(\n",
    "    id=\"sh_ctrl\",\n",
    "    component_id=\"evaporator\",\n",
    "    target_value=5.0,\n",
    "    tolerance=1e-4\n",
    ")\n",
    "system.add_constraint(sh_constraint)\n",
    "print(\"Constraint:\", sh_constraint)\n",
    "\n",
    "# 2. Valve Opening Bounded Variable (Initial: 50%, Min: 0%, Max: 100%)\n",
    "exv_opening = entropyk.BoundedVariable(\n",
    "    id=\"exv_opening\",\n",
    "    value=0.5,\n",
    "    min=0.0,\n",
    "    max=1.0,\n",
    "    component_id=\"valve\"\n",
    ")\n",
    "system.add_bounded_variable(exv_opening)\n",
    "print(\"Bounded Var:\", exv_opening)\n",
    "\n",
    "# 3. Link constraint and variable\n",
    "system.link_constraint_to_control(\"sh_ctrl\", \"exv_opening\")\n",
    "\n",
    "print(\"\\n✅ Inverse control configured successfully!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. Solve the System\n",
    "When we call `solve()`, Entropyk simultaneously computes the real state and the required valve opening!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-02-21T19:41:21.300083Z",
     "iopub.status.busy": "2026-02-21T19:41:21.299984Z",
     "iopub.status.idle": "2026-02-21T19:41:21.322564Z",
     "shell.execute_reply": "2026-02-21T19:41:21.322165Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Solver error: Solver did not converge after 200 iterations (final residual norm: 3.098e5)\n"
     ]
    }
   ],
   "source": [
    "config = entropyk.NewtonConfig(max_iterations=200, tolerance=1e-6)\n",
    "\n",
    "try:\n",
    "    result = config.solve(system)\n",
    "    print(\"Solved in\", result.iterations, \"iterations!\")\n",
    "    # At this point normally you would read result.state_vector \n",
    "    # Note: Dummy PyO3 models might yield trivial values in tests\n",
    "    print(\"Final State:\", result.to_numpy()[:6], \"...\")\n",
    "except entropyk.SolverError as e:\n",
    "    print(\"Solver error:\", e)\n"
   ]
  }
 ],
 "metadata": {
  "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.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
