{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "1e728153",
   "metadata": {},
   "source": [
    "# Flexible Two-Link Pendulum from Explicit Modal Data\n",
    "This notebook walks you through creating and running a flexible two-link pendulum using embedded mass properties and modal data. No FEMBridge H5 file or external model resources are needed."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0c88163b",
   "metadata": {},
   "source": [
    "Complete the [Flexible Two-Link Pendulum from FEMBridge H5 File](../example_flexible_2_link_pendulum_fembridge_h5/notebook.ipynb) before working through this example. That tutorial introduces the flexible links and their connections using the FEMBridge H5 reader. Here, we build the same system by supplying those parameters directly.\n",
    "\n",
    "![](../resources/nb_images/flexible_double_pendulum.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "3aba56d3",
   "metadata": {},
   "outputs": [],
   "source": [
    "import atexit\n",
    "\n",
    "import numpy as np\n",
    "import Karana.Dynamics as kd\n",
    "import Karana.Integrators as ki\n",
    "import Karana.Math as km\n",
    "import Karana.Models as kmdl\n",
    "from Karana.KUtils.Sim import Sim\n",
    "from Karana.Math.Kquantities import ureg"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4570cd7a",
   "metadata": {},
   "source": [
    "## Create the Simulation\n",
    "\n",
    "As in the [Flexible Two-Link Pendulum from FEMBridge H5 File](../example_flexible_2_link_pendulum_fembridge_h5/notebook.ipynb) example, use {py:class}`Karana.KUtils.Sim.Sim` to manage the {py:class}`Karana.Dynamics.Multibody`, {py:class}`Karana.Dynamics.StatePropagator`, and {py:class}`Karana.Frame.FrameContainer`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "bcfaba78",
   "metadata": {},
   "outputs": [],
   "source": [
    "sim: Sim = Sim()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "65aace70",
   "metadata": {},
   "source": [
    "## Define the Flexible-Body Data\n",
    "\n",
    "The cell below contains all the flexible-body data for both links. Stiffness and damping describe each mode's response. The pnode matrices describe deformation at a link's attachment to its parent; the onode matrices describe deformation of the parent link where the second link attaches."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "2b666fae",
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [],
   "source": [
    "stiffness = (\n",
    "    np.array(\n",
    "        [\n",
    "            2.0549859619140625e02,\n",
    "            8.2208087158203125e02,\n",
    "            1.5590296630859375e03,\n",
    "            4.2521279296875000e03,\n",
    "            5.9859155273437500e03,\n",
    "            6.2461474609375000e03,\n",
    "            1.0984625000000000e04,\n",
    "            1.6334771484375000e04,\n",
    "            1.7013275390625000e04,\n",
    "            2.3981865234375000e04,\n",
    "            3.6454070312500000e04,\n",
    "            3.8421781250000000e04,\n",
    "            4.4583210937500000e04,\n",
    "            6.5733140625000000e04,\n",
    "            6.8271796875000000e04,\n",
    "            7.0989156250000000e04,\n",
    "            1.0067981250000000e05,\n",
    "            1.0831357031250000e05,\n",
    "            1.2580945312500000e05,\n",
    "            1.4629000000000000e05,\n",
    "            1.5550515625000000e05,\n",
    "            2.0705278125000000e05,\n",
    "            2.4092304687500000e05,\n",
    "            2.8475368750000000e05,\n",
    "            3.6889328125000000e05,\n",
    "            3.7765946875000000e05,\n",
    "            1.0977122500000000e06,\n",
    "            1.3563998750000000e06,\n",
    "            2.0293360000000000e06,\n",
    "            2.0319782500000000e06,\n",
    "        ]\n",
    "    )\n",
    "    * 1\n",
    "    / ureg.second**2\n",
    ")\n",
    "\n",
    "damping = (\n",
    "    np.array(\n",
    "        [\n",
    "            1.4335222244262695,\n",
    "            2.8671956062316895,\n",
    "            3.9484550952911377,\n",
    "            6.520833492279053,\n",
    "            7.736869812011719,\n",
    "            7.903256893157959,\n",
    "            10.480755805969238,\n",
    "            12.780755996704102,\n",
    "            13.043495178222656,\n",
    "            15.486079216003418,\n",
    "            19.09294891357422,\n",
    "            19.60147476196289,\n",
    "            21.114736557006836,\n",
    "            25.638473510742188,\n",
    "            26.128873825073242,\n",
    "            26.6437931060791,\n",
    "            31.730085372924805,\n",
    "            32.91102600097656,\n",
    "            35.46963119506836,\n",
    "            38.24787521362305,\n",
    "            39.434139251708984,\n",
    "            45.5030517578125,\n",
    "            49.08391189575195,\n",
    "            53.3623161315918,\n",
    "            60.7365837097168,\n",
    "            61.45400619506836,\n",
    "            104.7717514038086,\n",
    "            116.4645767211914,\n",
    "            142.4547576904297,\n",
    "            142.5474853515625,\n",
    "        ]\n",
    "    )\n",
    "    * 1\n",
    "    / ureg.second\n",
    ")\n",
    "\n",
    "pnode_rotational_nodal_matrix = (\n",
    "    np.array(\n",
    "        [\n",
    "            [\n",
    "                -8.7804180701374357e-14,\n",
    "                1.3318334065801074e-13,\n",
    "                1.8897804328343870e-12,\n",
    "                -1.4149886369705200e00,\n",
    "                7.8455280466682709e-12,\n",
    "                1.0688759773114231e-11,\n",
    "                1.7728558204810341e-11,\n",
    "                -1.7290732734487335e-10,\n",
    "                1.4163824319839478e00,\n",
    "                2.5227823302609309e-11,\n",
    "                4.3101175406512482e-11,\n",
    "                -1.4225283861160278e00,\n",
    "                -7.8806829117983312e-11,\n",
    "                1.3332267734256220e-08,\n",
    "                -1.4247089624404907e00,\n",
    "                -1.5215069482099608e-10,\n",
    "                4.5725540021024358e-10,\n",
    "                1.4460003376007080e00,\n",
    "                -1.3106669083384759e-09,\n",
    "                -2.5435269446205666e-09,\n",
    "                -1.4450565576553345e00,\n",
    "                1.7595263912539849e-08,\n",
    "                1.6446072947928769e-08,\n",
    "                3.2748053073883057e00,\n",
    "                3.4513854980468750e00,\n",
    "                -1.7104116523114499e-07,\n",
    "                -6.9197341190374573e-09,\n",
    "                1.9573762610747281e-09,\n",
    "                -3.1380192888619263e-10,\n",
    "                -2.3596394171931934e-08,\n",
    "            ],\n",
    "            [\n",
    "                3.9978453516960144e-01,\n",
    "                4.8768313606201152e-13,\n",
    "                6.7652267217636108e-01,\n",
    "                4.1729653586786153e-12,\n",
    "                9.4854187965393066e-01,\n",
    "                -7.6654533415210224e-12,\n",
    "                -2.5538911263556230e-12,\n",
    "                -1.2228012084960938e00,\n",
    "                -1.8501807030890660e-10,\n",
    "                -6.0502464149791990e-11,\n",
    "                1.5080687999725342e00,\n",
    "                2.1977331066125316e-10,\n",
    "                -6.5942591664924777e-11,\n",
    "                -7.3056311000740948e-11,\n",
    "                2.7314042694293050e-10,\n",
    "                1.7911773920059204e00,\n",
    "                2.2985376102457877e-10,\n",
    "                -2.4041439949940013e-09,\n",
    "                -2.1219370365142822e00,\n",
    "                -1.6310841566280487e-09,\n",
    "                9.5187688842202078e-09,\n",
    "                -2.4142284393310547e00,\n",
    "                7.9199127256401880e-09,\n",
    "                3.3329307314033940e-08,\n",
    "                1.6916922973564397e-08,\n",
    "                7.5579531522151910e-10,\n",
    "                1.0055750676585262e-09,\n",
    "                1.1572696685791016e01,\n",
    "                6.3097668316913769e-06,\n",
    "                1.3330304145812988e01,\n",
    "            ],\n",
    "            [\n",
    "                -2.1422994671629891e-14,\n",
    "                3.9983057975769043e-01,\n",
    "                -9.9141875264940893e-13,\n",
    "                -4.1158182005791855e-13,\n",
    "                2.3157218330405183e-12,\n",
    "                6.7797201871871948e-01,\n",
    "                -2.2694472169570989e-12,\n",
    "                3.1399062427254869e-15,\n",
    "                -8.7556843719749367e-11,\n",
    "                9.5145952701568604e-01,\n",
    "                -1.1117929493709156e-11,\n",
    "                -6.5505682475541782e-12,\n",
    "                1.0318101408002267e-11,\n",
    "                1.2528030872344971e00,\n",
    "                1.3316149960473922e-08,\n",
    "                -1.5040831080614936e-10,\n",
    "                -1.2691611650517132e-10,\n",
    "                6.5451112411796242e-12,\n",
    "                1.2883413780251374e-10,\n",
    "                1.5405853986740112e00,\n",
    "                4.7240318323815700e-09,\n",
    "                -4.8095052385122017e-09,\n",
    "                -2.1795218407838490e-10,\n",
    "                -4.1114192605995470e-10,\n",
    "                1.5331005798202568e-08,\n",
    "                -3.8136291347079521e-10,\n",
    "                6.5432209968566895e00,\n",
    "                -6.7400804937278735e-09,\n",
    "                -8.1601934432983398e00,\n",
    "                3.8655598473269492e-06,\n",
    "            ],\n",
    "        ]\n",
    "    )\n",
    "    * 1\n",
    "    / ureg.kilogram**0.5\n",
    "    / ureg.meter\n",
    ")\n",
    "\n",
    "pnode_translational_nodal_matrix = (\n",
    "    np.array(\n",
    "        [\n",
    "            [\n",
    "                2.7754031898082543e-15,\n",
    "                -5.0917856729722471e-15,\n",
    "                2.2923313637915332e-14,\n",
    "                2.8765171126120259e-14,\n",
    "                -7.9399635195646912e-14,\n",
    "                -1.9797106120232610e-13,\n",
    "                -9.1426663100719452e-02,\n",
    "                4.2241961339430090e-13,\n",
    "                2.9904015461795774e-12,\n",
    "                -3.2055433340279910e-14,\n",
    "                -2.2486450635544886e-12,\n",
    "                2.0224353875636369e-12,\n",
    "                -9.2606998980045319e-02,\n",
    "                -3.1051075339433298e-12,\n",
    "                2.7231760249546433e-11,\n",
    "                5.3401428244670424e-12,\n",
    "                -9.3051664531230927e-02,\n",
    "                3.5926265434804705e-11,\n",
    "                -5.2582043537016676e-13,\n",
    "                -1.2796286382940614e-12,\n",
    "                -2.7264185353814696e-10,\n",
    "                -3.7048333845213222e-10,\n",
    "                -1.6954097151756287e-01,\n",
    "                -6.8241634565424647e-11,\n",
    "                1.0004707284849701e-08,\n",
    "                1.8491511046886444e-01,\n",
    "                -1.0605829409149337e-10,\n",
    "                -2.0018814383959693e-10,\n",
    "                -2.5507124190582431e-10,\n",
    "                1.4170779133459988e-11,\n",
    "            ],\n",
    "            [\n",
    "                1.7857731352682871e-14,\n",
    "                -1.2895497679710388e-01,\n",
    "                5.0469296710570211e-13,\n",
    "                1.4830003273579723e-13,\n",
    "                2.2143495804344271e-13,\n",
    "                -1.2917165458202362e-01,\n",
    "                9.5749124774668903e-13,\n",
    "                -3.2501479806101852e-12,\n",
    "                2.3330729709281073e-11,\n",
    "                -1.2931926548480988e-01,\n",
    "                6.8807234215872981e-12,\n",
    "                1.1152834298450154e-12,\n",
    "                -2.4501425194278781e-12,\n",
    "                -1.3287858664989471e-01,\n",
    "                -1.4815725313255257e-09,\n",
    "                2.6213401241315104e-11,\n",
    "                1.5548331719350550e-11,\n",
    "                -5.6011757731955214e-12,\n",
    "                -3.3139300331663790e-11,\n",
    "                -1.3302773237228394e-01,\n",
    "                -9.6526875381641730e-10,\n",
    "                5.6235799350545790e-10,\n",
    "                6.4579513611717765e-12,\n",
    "                5.0465747032657493e-12,\n",
    "                -1.9988291022343674e-09,\n",
    "                5.2925268334558240e-11,\n",
    "                -2.4544046819210052e-01,\n",
    "                1.7036156929606250e-09,\n",
    "                2.5699537992477417e-01,\n",
    "                -1.2084851164217980e-07,\n",
    "            ],\n",
    "            [\n",
    "                1.2893499433994293e-01,\n",
    "                5.6514875109358806e-14,\n",
    "                1.2874217331409454e-01,\n",
    "                1.3768617322662546e-12,\n",
    "                1.2868846952915192e-01,\n",
    "                -1.8167339377664349e-12,\n",
    "                -1.9923957774413414e-13,\n",
    "                -1.2851846218109131e-01,\n",
    "                -2.4721573746444570e-11,\n",
    "                -6.4888836491527879e-12,\n",
    "                1.2940323352813721e-01,\n",
    "                2.9212219448959686e-11,\n",
    "                -5.9720158505915943e-12,\n",
    "                -5.1810587070599823e-12,\n",
    "                5.4645839936418028e-11,\n",
    "                1.2924693524837494e-01,\n",
    "                1.3108258402338979e-11,\n",
    "                -1.8804607870848145e-10,\n",
    "                -1.3234081864356995e-01,\n",
    "                -1.0863067390065595e-10,\n",
    "                7.5034894964076670e-10,\n",
    "                -1.3154146075248718e-01,\n",
    "                4.3030842777902478e-10,\n",
    "                1.5404281183961643e-09,\n",
    "                1.1600880256423807e-09,\n",
    "                5.4484788208908341e-11,\n",
    "                -7.9801554253577933e-10,\n",
    "                2.7184343338012695e-01,\n",
    "                1.2937020699155255e-07,\n",
    "                2.7561143040657043e-01,\n",
    "            ],\n",
    "        ]\n",
    "    )\n",
    "    * 1\n",
    "    / ureg.kilogram**0.5\n",
    ")\n",
    "\n",
    "onode_rotational_nodal_matrix = (\n",
    "    np.array(\n",
    "        [\n",
    "            [\n",
    "                -8.6583287739992743e-14,\n",
    "                1.7627124261100635e-13,\n",
    "                -1.0863175593442409e-12,\n",
    "                1.4149886369705200e00,\n",
    "                7.9129281252421357e-12,\n",
    "                1.0983847512080480e-11,\n",
    "                1.4612895962717865e-11,\n",
    "                -1.7806878194193132e-10,\n",
    "                1.4163824319839478e00,\n",
    "                3.7916628170542310e-11,\n",
    "                2.7872498146314095e-12,\n",
    "                1.4225283861160278e00,\n",
    "                -7.1291765893488623e-12,\n",
    "                1.3369689355613446e-08,\n",
    "                -1.4247089624404907e00,\n",
    "                -1.6345905184955711e-10,\n",
    "                -4.6855302970882917e-10,\n",
    "                -1.4460003376007080e00,\n",
    "                -1.2006163840894146e-09,\n",
    "                -3.0329854183719362e-09,\n",
    "                -1.4450565576553345e00,\n",
    "                -1.6474725583748295e-08,\n",
    "                1.7620140013718810e-08,\n",
    "                -3.2748048305511475e00,\n",
    "                3.4513862133026123e00,\n",
    "                -1.7238281202480721e-07,\n",
    "                -7.7800326181431956e-09,\n",
    "                8.5113271985903793e-10,\n",
    "                -3.3836883295279563e-10,\n",
    "                2.2904204755036517e-08,\n",
    "            ],\n",
    "            [\n",
    "                -3.9978453516960144e-01,\n",
    "                2.4559615951179337e-13,\n",
    "                6.7652267217636108e-01,\n",
    "                4.5209933886847242e-12,\n",
    "                -9.4854187965393066e-01,\n",
    "                2.7858501096267307e-12,\n",
    "                2.2444233971352645e-12,\n",
    "                -1.2228012084960938e00,\n",
    "                -1.4732919051407833e-10,\n",
    "                -6.4562050150485106e-11,\n",
    "                -1.5080687999725342e00,\n",
    "                1.6435636185363478e-10,\n",
    "                9.7319487538105598e-12,\n",
    "                -4.8732212709623468e-11,\n",
    "                -4.7446135909012810e-10,\n",
    "                1.7911773920059204e00,\n",
    "                8.4595053184699509e-11,\n",
    "                -2.0604216111053120e-09,\n",
    "                2.1219370365142822e00,\n",
    "                -2.1892343493590261e-09,\n",
    "                -7.9673005970448685e-09,\n",
    "                -2.4142284393310547e00,\n",
    "                7.9014794707177316e-09,\n",
    "                3.2964663887469214e-08,\n",
    "                -8.2604998397073359e-09,\n",
    "                1.1526696264141378e-09,\n",
    "                -1.1792337772931205e-08,\n",
    "                -1.1572696685791016e01,\n",
    "                6.3096549638430588e-06,\n",
    "                1.3330304145812988e01,\n",
    "            ],\n",
    "            [\n",
    "                -6.5415494778008152e-15,\n",
    "                -3.9983057975769043e-01,\n",
    "                8.1797470272843009e-13,\n",
    "                -1.8824607942325572e-13,\n",
    "                2.7387419589131046e-12,\n",
    "                6.7797201871871948e-01,\n",
    "                -1.2075339643133343e-12,\n",
    "                2.2185623130277499e-11,\n",
    "                -6.7277079740524215e-11,\n",
    "                -9.5145952701568604e-01,\n",
    "                -7.5160121182360484e-12,\n",
    "                1.9983395146971894e-11,\n",
    "                -4.6109736038038585e-13,\n",
    "                1.2528030872344971e00,\n",
    "                1.3345824001476103e-08,\n",
    "                -1.6329573804263475e-10,\n",
    "                -8.4735524152890207e-11,\n",
    "                4.9959231196439191e-11,\n",
    "                -1.2323862763619076e-10,\n",
    "                -1.5405853986740112e00,\n",
    "                1.3525973230343880e-08,\n",
    "                1.7289771836459522e-09,\n",
    "                3.0440698572942182e-11,\n",
    "                2.4196064596360145e-10,\n",
    "                1.8548199420820310e-08,\n",
    "                -1.4224420252784142e-10,\n",
    "                6.5432209968566895e00,\n",
    "                -6.8832544108943239e-09,\n",
    "                8.1601934432983398e00,\n",
    "                -3.8644657252007164e-06,\n",
    "            ],\n",
    "        ]\n",
    "    )\n",
    "    * 1\n",
    "    / ureg.kilogram**0.5\n",
    "    / ureg.meter\n",
    ")\n",
    "\n",
    "onode_translational_nodal_matrix = (\n",
    "    np.array(\n",
    "        [\n",
    "            [\n",
    "                2.9427273487582896e-15,\n",
    "                -4.0225819128837745e-15,\n",
    "                1.1849583509240032e-14,\n",
    "                6.2739181525776205e-14,\n",
    "                1.0581886895324934e-14,\n",
    "                -5.1359531048639911e-14,\n",
    "                9.1426663100719452e-02,\n",
    "                -2.3032300350799007e-13,\n",
    "                -1.6042896178181110e-12,\n",
    "                1.2740215783388353e-13,\n",
    "                -1.7914277916988852e-12,\n",
    "                2.3628841938627687e-12,\n",
    "                -9.2606998980045319e-02,\n",
    "                9.8308275582603688e-13,\n",
    "                1.0472090208879514e-11,\n",
    "                -1.1178382438442602e-12,\n",
    "                9.3051664531230927e-02,\n",
    "                -3.3235192375968836e-11,\n",
    "                1.8714580232725986e-11,\n",
    "                -2.5711592143567996e-12,\n",
    "                -2.0022128399688199e-10,\n",
    "                -3.9000899731966854e-10,\n",
    "                -1.6954097151756287e-01,\n",
    "                -9.0685401896162254e-12,\n",
    "                -7.8951849502573168e-09,\n",
    "                -1.8491511046886444e-01,\n",
    "                -1.6235338073933292e-10,\n",
    "                -2.2078622263776992e-10,\n",
    "                -2.9524604983066638e-10,\n",
    "                3.4076808042016182e-10,\n",
    "            ],\n",
    "            [\n",
    "                -3.0964722990144177e-15,\n",
    "                -1.2895497679710388e-01,\n",
    "                1.1582729675559122e-13,\n",
    "                -1.2900367352044334e-13,\n",
    "                5.0861116533684747e-14,\n",
    "                1.2917165458202362e-01,\n",
    "                -1.4665182859318476e-13,\n",
    "                5.4946173479175631e-12,\n",
    "                -2.4108966559244216e-11,\n",
    "                -1.2931926548480988e-01,\n",
    "                -4.6050494147131804e-12,\n",
    "                5.0170835368124056e-12,\n",
    "                2.1023911778161875e-12,\n",
    "                1.3287858664989471e-01,\n",
    "                1.4958071448134547e-09,\n",
    "                -2.8242883726159462e-11,\n",
    "                -6.8265189508143198e-12,\n",
    "                1.1269198465016506e-12,\n",
    "                2.7932298401339706e-12,\n",
    "                -1.3302773237228394e-01,\n",
    "                1.8008662339141779e-09,\n",
    "                1.9832317732504379e-10,\n",
    "                4.3102885843859795e-11,\n",
    "                1.1691965971782903e-11,\n",
    "                2.4208715032614236e-09,\n",
    "                -1.5542650499966726e-11,\n",
    "                2.4544046819210052e-01,\n",
    "                -6.4333072202771291e-10,\n",
    "                2.5699537992477417e-01,\n",
    "                -1.2385028469452664e-07,\n",
    "            ],\n",
    "            [\n",
    "                1.2893499433994293e-01,\n",
    "                2.6642108454815773e-14,\n",
    "                -1.2874217331409454e-01,\n",
    "                -1.4349850517916818e-12,\n",
    "                1.2868846952915192e-01,\n",
    "                1.3476874238978198e-13,\n",
    "                -4.6366773268080586e-13,\n",
    "                1.2851846218109131e-01,\n",
    "                1.1438605271307800e-11,\n",
    "                9.6095605431334441e-12,\n",
    "                1.2940323352813721e-01,\n",
    "                -2.0630334246485305e-11,\n",
    "                -2.8843188688149057e-12,\n",
    "                8.6330265852696542e-12,\n",
    "                6.3978364273076238e-11,\n",
    "                -1.2924693524837494e-01,\n",
    "                -1.3816107112540887e-11,\n",
    "                1.4823016425413726e-10,\n",
    "                -1.3234081864356995e-01,\n",
    "                1.5347030590806554e-10,\n",
    "                6.3881455680814270e-10,\n",
    "                1.3154146075248718e-01,\n",
    "                -3.9892997238943906e-10,\n",
    "                -1.5325606339544606e-09,\n",
    "                6.3776822711858472e-10,\n",
    "                -4.6185048840907683e-11,\n",
    "                1.7030360455194682e-10,\n",
    "                2.7184343338012695e-01,\n",
    "                -1.3205570326135785e-07,\n",
    "                -2.7561143040657043e-01,\n",
    "            ],\n",
    "        ]\n",
    "    )\n",
    "    * 1\n",
    "    / ureg.kilogram**0.5\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6de6f703",
   "metadata": {},
   "source": [
    "## Create the First Link\n",
    "\n",
    "{py:class}`Karana.Dynamics.PhysicalModalBodyParams` collects the mass properties, hinge parameters, and modal data for a flexible body.\n",
    "\n",
    "Use {py:class}`Karana.Dynamics.PhysicalHingeParams` to define a revolute hinge about the y-axis. {py:class}`Karana.Dynamics.ParentPhysicalBodyAttachmentParams` places the first link's attachment at the virtual root. The pnode is at one end of the beam.\n",
    "\n",
    "Create a {py:class}`Karana.Dynamics.PhysicalModalBody`, connect it with a {py:class}`Karana.Dynamics.PhysicalHinge`, and apply the parameters using {py:meth}`setParams() <Karana.Dynamics.PhysicalModalBody.setParams>`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "68e0e138",
   "metadata": {},
   "outputs": [],
   "source": [
    "link_1_params: kd.PhysicalModalBodyParams = kd.PhysicalModalBodyParams(\n",
    "    # 'link_1' mass properties\n",
    "    spatial_inertia=km.SpatialInertia(\n",
    "        240.0 * ureg.kilogram,\n",
    "        np.array([0.75, 0.0, -0.0]) * ureg.meter,\n",
    "        np.array(\n",
    "            [\n",
    "                [1.0000079870224, 0.0, 0.0],\n",
    "                [0.0, 180.03599548339844, 0.0],\n",
    "                [0.0, 0.0, 180.03599548339844],\n",
    "            ]\n",
    "        )\n",
    "        * ureg.kilogram\n",
    "        * ureg.meter**2,\n",
    "    ),\n",
    "    # Define the attachment of 'link_1' to its parent body\n",
    "    body_to_joint_transform=km.HomTran(\n",
    "        km.UnitQuaternion(np.array([0.0, 0.0, 0.0, 1.0])),\n",
    "        np.array([0.0, 0.0, 0.0]) * ureg.meter,\n",
    "    ),\n",
    "    # define the 'link_1' parent hinge type and parameters\n",
    "    hinge_params=kd.PhysicalHingeParams(\n",
    "        hinge_type=kd.HingeType.REVOLUTE,\n",
    "        subhinge_params=[\n",
    "            kd.PinSubhingeParams(\n",
    "                unit_axis=np.array([0.0, 1.0, 0.0]),\n",
    "                prescribed=False,\n",
    "            ),\n",
    "        ],\n",
    "    ),\n",
    "    # Define the location and orientation of the 'link_1' parent hinge\n",
    "    # relative to the parent body's frame\n",
    "    parent_body_attachment_params=kd.ParentPhysicalBodyAttachmentParams(\n",
    "        inb_to_joint_transform=km.HomTran(\n",
    "            km.UnitQuaternion(np.array([0.0, 0.0, 0.0, 1.0])),\n",
    "            np.array([0.0, 0.0, 0.0]) * ureg.meter,\n",
    "        )\n",
    "    ),\n",
    "    scene_part_specs=[],\n",
    "    scene_file_object_specs=[],\n",
    "    nmodes=30,\n",
    "    stiffness=stiffness,\n",
    "    damping=damping,\n",
    "    # Rotational nodal matrix for the body's pnode. Size (3 x 30)\n",
    "    pnode_rotational_nodal_matrix=pnode_rotational_nodal_matrix,\n",
    "    # Translational nodal matrix for the body's pnode. Size (3 x 30)\n",
    "    pnode_translational_nodal_matrix=pnode_translational_nodal_matrix,\n",
    ")\n",
    "\n",
    "# Create the 'link_1' body\n",
    "link_1: kd.PhysicalModalBody = kd.PhysicalModalBody.create(\"link_1\", sim.mb, 30)\n",
    "\n",
    "# Connect the 'link_1' body to its parent via a hinge\n",
    "kd.PhysicalHinge(sim.mb.virtualRoot(), link_1, kd.HingeType.REVOLUTE)\n",
    "\n",
    "# Set the parameters for the 'link_1' body\n",
    "link_1.setParams(link_1_params)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2f69e80e",
   "metadata": {},
   "source": [
    "## Attach the Second Link\n",
    "\n",
    "Deep-copy the first link's parameters to reuse the identical beam data. Replace the parent attachment with {py:class}`Karana.Dynamics.ParentPhysicalModalBodyAttachmentParams`, which includes the far-end transform and the parent's onode matrices. The second link keeps its own near-end pnode matrices.\n",
    "\n",
    "This is the equivalent to adding the parent attachment with the FEMBridge reader in the [Flexible Two-Link Pendulum from FEMBridge H5 File](../example_flexible_2_link_pendulum_fembridge_h5/notebook.ipynb) example."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "e85f7c60",
   "metadata": {},
   "outputs": [],
   "source": [
    "link_2_params = link_1_params.model_copy(deep=True)\n",
    "# Define the location and orientation of the 'link_2' parent hinge\n",
    "# relative to the parent body's frame\n",
    "link_2_params.parent_body_attachment_params = kd.ParentPhysicalModalBodyAttachmentParams(\n",
    "    inb_to_joint_transform=km.HomTran(\n",
    "        km.UnitQuaternion(np.array([0.0, 0.0, 0.0, 1.0])),\n",
    "        np.array([1.5, 0.0, 0.0]) * ureg.meter,\n",
    "    ),\n",
    "    onode_rotational_nodal_matrix=onode_rotational_nodal_matrix,\n",
    "    onode_translational_nodal_matrix=onode_translational_nodal_matrix,\n",
    ")\n",
    "\n",
    "# Create the 'link_2' body\n",
    "link_2: kd.PhysicalModalBody = kd.PhysicalModalBody.create(\"link_2\", sim.mb, 30)\n",
    "\n",
    "# Connect the 'link_2' body to its parent via a hinge\n",
    "kd.PhysicalHinge(link_1, link_2, kd.HingeType.REVOLUTE)\n",
    "\n",
    "# Set the parameters for the 'link_2' body\n",
    "link_2.setParams(link_2_params)\n",
    "del link_1_params, link_2_params\n",
    "\n",
    "# Finalize the multibody for the added bodies\n",
    "sim.mb.ensureHealthy()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f92e34f3",
   "metadata": {},
   "source": [
    "## Add Gravity\n",
    "\n",
    "Use {py:class}`Karana.Models.UniformGravity` and {py:class}`Karana.Models.Gravity` to apply gravity in the negative z-direction."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "6cd84fd3",
   "metadata": {},
   "outputs": [],
   "source": [
    "uniform_gravity = kmdl.UniformGravity(\"uniform_gravity\")\n",
    "uniform_gravity.setGravity(\n",
    "    np.array([0.0, 0.0, -9.81]) * ureg.meter / ureg.second**2,\n",
    "    0.0,\n",
    "    kmdl.OutputUpdateType.PRE_HOP,\n",
    ")\n",
    "grav_model = kmdl.Gravity(\"grav_model\", sim.sp, uniform_gravity, sim.sp.getSubTree())\n",
    "del grav_model, uniform_gravity"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3929d6f2",
   "metadata": {},
   "source": [
    "## Set the Initial State\n",
    "\n",
    "Reset the multibody and set the hinge angles to 110° and 15°. Both links start at rest with zero modal coordinates, so releasing them produces swinging motion and flexible deformation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "6e031c3e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sim.mb.resetData()\n",
    "link_1.parentHinge().subhinge(0).setQ(110.0 * ureg.deg)\n",
    "link_2.parentHinge().subhinge(0).setQ(15.0 * ureg.deg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f42d276c",
   "metadata": {},
   "source": [
    "## Configure the Integrator\n",
    "Here, we configure the integrator. The performance of flexible-body problems in particular is sensitive to the integrator chosen. kdFlex offers a large variety of integrators and options to meet simulations' needs. Here, SUNDIALS's ARKODE's implicit ESDIRK45 method does well."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "127809d1",
   "metadata": {},
   "outputs": [],
   "source": [
    "integrator_options = ki.ArkImplicitIntegratorOptions()\n",
    "integrator_options.rtol = 1e-5\n",
    "integrator_options.atol = 3e-8\n",
    "integrator_options.max_nl_iters = 5\n",
    "sim.sp.setIntegrator(ki.IntegratorType.ARKIMPLICIT_ESDIRK45, integrator_options)\n",
    "\n",
    "sim.sp.ensureHealthy()\n",
    "sim.sp.hardReset()\n",
    "sim.sp.setState(sim.sp.assembleState())\n",
    "\n",
    "# Here, a maximum hop size is added so we get visualization updates every 1/60 seconds\n",
    "sim.sp.setMaxHopSize(1.0 / 60.0)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "50d12eaf",
   "metadata": {},
   "source": [
    "## Open the kdFlex Viewer\n",
    "\n",
    "Use {py:meth}`setupGui() <Karana.KUtils.Sim.Sim.setupGui>` to open the viewer and set the camera position. This cell is optional if you only want to run the dynamics.\n",
    "\n",
    "This standalone model includes the joint coupling matrices, but no sensor nodes along the beams. Its stick view shows the connected body and joint frames, but graphics do not show the detailed bending shape like in the [Flexible Two-Link Pendulum from FEMBridge H5 File](../example_flexible_2_link_pendulum_fembridge_h5/notebook.ipynb) example. This motions affect on the dynamics is still captured numerically, but it is just not shown graphically."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "7b18bde9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[WebUI] Karana Viewer is running on port 35899\n",
      "Open this URL to connect:\n",
      "\t\u001b[1mhttp://newton:35899\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "\n",
       "        <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"http://newton:35899\">\n",
       "            <b>Click to open Karana Viewer in a new tab</b>\n",
       "        </a>\n",
       "        "
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Set the stick parts to be a bit larger so they are easier to see\n",
    "sim.mb.createStickParts()\n",
    "sim.mb.scaleStickParts(5.0)\n",
    "\n",
    "# Create the GUI and set the camera location\n",
    "sim.setupGui(port=0)\n",
    "target = sim.mb.virtualRoot().frameToFrame(link_2).relTransform().getTranslation().m\n",
    "sim.mb.getScene().viewAroundFrame(\n",
    "    sim.mb.virtualRoot(),\n",
    "    target + np.array([0.0, 6.0, 0.0]),\n",
    "    target,\n",
    "    np.array([0.0, 0.0, 1.0]),\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "96392047",
   "metadata": {},
   "source": [
    "## Run the Simulation\n",
    "Advance to 10 seconds."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "fa7684a0",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<SpStatusEnum.REACHED_END_TIME: 1>"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sim.sp.advanceTo(10.0)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e594747d",
   "metadata": {},
   "source": [
    "## Clean Up the Simulation\n",
    "Below, we cleanup our simulation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "f502569d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<function __main__.cleanup() -> None>"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def cleanup() -> None:\n",
    "    \"\"\"Cleanup the simulation.\"\"\"\n",
    "    global link_1, link_2, sim\n",
    "    del link_1, link_2, sim\n",
    "\n",
    "\n",
    "atexit.register(cleanup)"
   ]
  }
 ],
 "metadata": {
  "docs": {
   "tags": [
    "dynamics",
    "time domain",
    "pendulum",
    "flexible"
   ]
  },
  "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.12.3"
  },
  "name": "notebook.ipynb"
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
