{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "b2a083ba",
   "metadata": {},
   "source": [
    "# 悬臂梁问题\n",
    "\n",
    "**类别：** 仿真优化\n",
    "\n",
    "使用 OptAgent 的 Python 接口描述变量、约束与目标。\n",
    "\n",
    "问题与原始示例来源：[Hexaly Code Templates](https://www.hexaly.com/templates/cantilevered-beam-problem)。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "817fef28",
   "metadata": {},
   "source": [
    "## 问题描述\n",
    "\n",
    "**悬臂梁问题** 由设计 I 型梁的横截面组成,以使梁在一定应力下不会变形或破坏的情况下获得最小体积。有关更多详细信息,请参阅 [Wikipedia](https://en.wikipedia.org/wiki/Cantilever)。我们记 L、H、h1、b1 和 b2 为梁的尺寸。\n",
    "\n",
    "梁的体积由下式给出:\n",
    "\n",
    "$V = f(H, h_1, b_1, b_2) = \\left[2h_1b_1 + \\left(H - 2h_1\\right)b_2\\right]L$\n",
    "\n",
    "约束条件为:\n",
    "\n",
    "- 梁根部处的最大弯曲应力,定义为:\n",
    "\n",
    "  $g_1(H, h_1, b_1, b_2) = \\frac{P L H}{2I}$\n",
    "\n",
    "- 梁尖端处的最大挠度,定义为:\n",
    "\n",
    "  $g_2(H, h_1, b_1, b_2) = \\frac{P L^3}{3EI}$\n",
    "\n",
    "### 学习要点\n",
    "\n",
    "- 使用浮点变量表示连续尺寸，用整数索引选择离散尺寸。\n",
    "- 使用模型数组按决策表达式查表。\n",
    "- 将应力、挠度与体积写成表达式，添加约束并最小化体积。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6a70c2b3",
   "metadata": {},
   "source": [
    "## 建模思路\n",
    "\n",
    "悬臂梁问题的 OptAgent 模型使用整型和浮点型决策变量。H、b1、b2 声明为浮点型决策变量,定义域分别为 [3.0, 7.0]、[2.0, 12.0] 和 [0.1, 2.0]。最后一个变量 h1 是离散的:它表示索引集合 {0, 1, ..., 7} 中的整数,通过数组查表映射到一组可行取值 {0.1, 0.26, 0.35, 0.5, 0.65, 0.75, 0.9, 1.0}。\n",
    "\n",
    "弯曲应力、挠度和体积直接由模型表达式计算，转动惯量 I 是中间表达式。模型约束应力不超过 5000、挠度不超过 0.10，并最小化体积。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "30210c8b",
   "metadata": {},
   "source": [
    "## Python 实现\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "beam-model",
   "metadata": {},
   "outputs": [],
   "source": [
    "from pathlib import Path\n",
    "\n",
    "from optagent import OptModel, solve\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "def main(output_file=None, time_limit=10):\n",
    "    load = 1000\n",
    "    elasticity = 10.0e6\n",
    "    length = 36\n",
    "    possible_values = [0.1, 0.26, 0.35, 0.5, 0.65, 0.75, 0.9, 1.0]\n",
    "\n",
    "    model = OptModel()\n",
    "    possible_values_array = model.array(possible_values)\n",
    "\n",
    "    height = model.float(3.0, 7.0)\n",
    "    h1_index = model.int(0, len(possible_values) - 1,\n",
    "    )\n",
    "    b1 = model.float(2.0, 12.0)\n",
    "    b2 = model.float(0.1, 2.0)\n",
    "    h1 = possible_values_array[h1_index]\n",
    "\n",
    "    inertia = (1.0 / 12.0) * b2 * (height - 2 * h1) ** 3 + 2 * (\n",
    "        (1.0 / 12.0) * b1 * h1**3 + b1 * h1 * (height - h1) ** 2 / 4.0\n",
    "    )\n",
    "    bending_stress = load * length * height / (2 * inertia)\n",
    "    tip_deflection = (load * length**3) * (3 * elasticity * inertia) ** -1\n",
    "    volume = (2 * h1 * b1 + (height - 2 * h1) * b2) * length\n",
    "\n",
    "    model.constraint(bending_stress <= 5000)\n",
    "    model.constraint(tip_deflection <= 0.10)\n",
    "    model.minimize(volume)\n",
    "\n",
    "    solution = solve(model, time_limit_s=float(time_limit))\n",
    "    result_values = {'volume': volume.value, 'height': height.value, 'h1': h1.value, 'b1': b1.value, 'b2': b2.value, 'bending_stress': bending_stress.value, 'tip_deflection': tip_deflection.value}\n",
    "    result_text = (\n",
    "        f\"Status = {solution.feasible}; Volume = {result_values['volume']}\\n\"\n",
    "        f\"H = {result_values['height']}\\n\"\n",
    "        f\"h1 = {result_values['h1']}\\n\"\n",
    "        f\"b1 = {result_values['b1']}\\n\"\n",
    "        f\"b2 = {result_values['b2']}\\n\"\n",
    "        f\"Bending stress = {result_values['bending_stress']}\\n\"\n",
    "        f\"Tip deflection = {result_values['tip_deflection']}\"\n",
    "    )\n",
    "    print(result_text)\n",
    "    if output_file is not None:\n",
    "        Path(output_file).write_text(result_text + \"\\n\", encoding=\"utf-8\")\n",
    "    return solution\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "beam-run-heading",
   "metadata": {},
   "source": [
    "## 本地运行\n",
    "\n",
    "悬臂梁模型不需要外部实例文件，直接调用 `main` 即可；调整 `time_limit` 可以控制求解时间。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "beam-run",
   "metadata": {},
   "outputs": [],
   "source": [
    "solution_cantilevered_beam = main(time_limit=1)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f4f5c0db",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "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.14.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
