{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "0e827775",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "# n-link Pendulum\n",
    "This notebook walks through the process required to create an n-link pendulum multibody simulation in **kdflex**. This problem is an extension to the 2-link pendulum by creating the pendulum using a procedural approach to allow for an n number of linked bodies to be built at once. This method uses the {py:meth}`Karana.Dynamics.PhysicalBody.addSerialChain` method which can be useful for automating the creation of large multibody systems.\n",
    "\n",
    "Requirements:\n",
    "- [2-link Pendulum](../example_2_link_pendulum/notebook.ipynb)\n",
    "\n",
    "In this tutorial we will:\n",
    "- Create the multibody\n",
    "- Setup the **kdFlex** scene\n",
    "- Add a root visual geometry\n",
    "- Set up the state propagator and models\n",
    "- Set initial state\n",
    "- Register a timed event\n",
    "- Run the simulation\n",
    "- Clean up the simulation\n",
    "\n",
    "![](../resources/nb_images/n_link_pendulum_flipped.PNG)\n",
    "\n",
    "For a more in-depth descriptions of **kdflex** concepts see [usage](usage_page).\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b7e2551c",
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "import atexit\n",
    "import numpy as np\n",
    "from typing import cast\n",
    "from math import pi\n",
    "\n",
    "import Karana.Core as kc\n",
    "import Karana.Math as km\n",
    "import Karana.Frame as kf\n",
    "import Karana.Dynamics as kd\n",
    "import Karana.Dynamics.SOADyn_types as kdt\n",
    "import Karana.Scene as ks\n",
    "import Karana.KUtils as ku\n",
    "import Karana.Models as kmdl\n",
    "\n",
    "from Karana.Core import discard, allReady\n",
    "from Karana.Frame import FrameContainer\n",
    "from Karana.Integrators import IntegratorType\n",
    "from Karana.Dynamics import (\n",
    "    Multibody,\n",
    "    PhysicalBody,\n",
    "    PhysicalBody,\n",
    "    HingeType,\n",
    "    StatePropagator,\n",
    "    TimedEvent,\n",
    "    PhysicalBodyParams,\n",
    ")\n",
    "from Karana.Math import SpatialInertia, HomTran\n",
    "from Karana.Models import Gravity, UniformGravity, UpdateProxyScene, SyncRealTime, OutputUpdateType\n",
    "from Karana.Scene import (\n",
    "    BoxGeometry,\n",
    "    CylinderGeometry,\n",
    "    Color,\n",
    "    PhysicalMaterialInfo,\n",
    "    PhysicalMaterial,\n",
    "    ScenePartSpec,\n",
    ")\n",
    "from Karana.Scene import ProxySceneNode, ProxyScenePart"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9bde08ea",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## Create the Multibody\n",
    "\n",
    "We create a [Multibody](treembody_sec) in a similar manner to the 2-link pendulum. But instead of defining each body, we define the parameters for one body and pivot and procedurally create our chain by repeating this pattern.\n",
    "\n",
    "The procedural approach differs from the manual approach in two main ways:\n",
    "- Definition of body parameters using {py:class}`Karana.Scene.ScenePartSpec`, which define each body and its joint connections and visual geometries.\n",
    "- Usage of {py:meth}`Karana.Dynamics.PhysicalBody.addSerialChain`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a5b75454",
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "fc = FrameContainer(\"root\")\n",
    "\n",
    "\n",
    "def createMbody(n_links: int):\n",
    "    \"\"\"Create the multibody.\n",
    "\n",
    "    Parameters\n",
    "    ----------\n",
    "    n_links : int\n",
    "        The number of pendulum links to use.\n",
    "    \"\"\"\n",
    "    mb = kd.Multibody(\"mb\", fc)\n",
    "\n",
    "    # create visual materials to color the bodies\n",
    "    mat_info = ks.PhysicalMaterialInfo()\n",
    "    mat_info.color = ks.Color.FIREBRICK\n",
    "    brown = ks.PhysicalMaterial(mat_info)\n",
    "\n",
    "    # using scene part spec to define visual geometry for each procedural body\n",
    "    sp_body = ks.ScenePartSpec()\n",
    "    sp_body.name = \"sp_body\"\n",
    "    sp_body.geometry = ks.BoxGeometry(0.05, 0.05, 1)\n",
    "    sp_body.material = brown\n",
    "    sp_body.transform = km.HomTran([0.0, 0.0, 0.0])\n",
    "    sp_body.scale = [1, 1, 1]\n",
    "\n",
    "    sp_pivot = ks.ScenePartSpec()\n",
    "    sp_pivot.name = \"sp_pivot\"\n",
    "    sp_pivot.geometry = ks.CylinderGeometry(0.1, 0.1)\n",
    "    sp_pivot.material = brown\n",
    "    sp_pivot.transform = km.HomTran([0.0, 0.0, -0.5])\n",
    "    sp_pivot.scale = [1, 1, 1]\n",
    "\n",
    "    # Here, we use the addSerialChain method to add multiple instances of the same body\n",
    "    # connected in a chain. We add n instances of the pendulum link with the following parameters:\n",
    "    params = kd.PhysicalBodyParams(\n",
    "        spI=km.SpatialInertia(2.0, np.zeros(3), np.diag([3, 2, 1])),\n",
    "        axes=[np.array([0.0, 1.0, 0.0])],\n",
    "        body_to_joint_transform=km.HomTran(np.array([0, 0, 0.5])),\n",
    "        inb_to_joint_transform=km.HomTran(np.array([0, 0, -0.5])),\n",
    "        scene_part_specs=[sp_body, sp_pivot],\n",
    "    )\n",
    "    kd.PhysicalBody.addSerialChain(\n",
    "        \"link\",\n",
    "        n_links,\n",
    "        cast(kd.PhysicalBody, mb.virtualRoot()),\n",
    "        htype=kd.HingeType.REVOLUTE,\n",
    "        params=params,\n",
    "    )\n",
    "\n",
    "    # finalize and verify the multibody\n",
    "    mb.ensureHealthy()\n",
    "    mb.resetData()\n",
    "    assert kc.allReady()\n",
    "\n",
    "    return mb"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3ebe427e",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "In this cell we create the multibody by calling the createMbody we defined in the previous cell. We also print the tree structure using {py:meth}`Karana.Dynamics.SubTree.dumpTree` to ensure everything was created properly."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "d4a29bd7",
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "|-mb_MBVROOT_\n",
      "   |-[PIN] link_0\n",
      "      |-[PIN] link_1\n",
      "         |-[PIN] link_2\n",
      "            |-[PIN] link_3\n",
      "               |-[PIN] link_4\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# initialization\n",
    "n_links = 5\n",
    "mb = createMbody(n_links)\n",
    "\n",
    "# Let's dump the multibody structure to make sure we got 5 links.\n",
    "# The names listed in the tree are the same names as the argument\n",
    "# passed into the getBody() method on the multibody to obtain a\n",
    "# specific part. The name link_0 is used later on in the notebook.\n",
    "mb.dumpTree()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c9258a5a",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## Setup the kdFlex Scene\n",
    "\n",
    "Next we setup **kdflex**'s graphics by calling the setupGraphics helper method on the multibody. This method takes care of setting up the graphics environment. \n",
    "\n",
    "See [Visualization](visualization_sec) and [Scene Layer](scene_layer_sec) for more information relating to this section."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "d8e44d9b",
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[WebUI] Listening at http://newton:36115\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "\n",
       "        <iframe\n",
       "            width=\"100%\"\n",
       "            height=\"300px\"\n",
       "            src=\"http://newton:36115\"\n",
       "            frameborder=\"0\"\n",
       "            allowfullscreen\n",
       "            \n",
       "        ></iframe>\n",
       "        "
      ],
      "text/plain": [
       "<IPython.lib.display.IFrame at 0x7e9356156ff0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "cleanup_graphics, web_scene = mb.setupGraphics(port=0, axes=0.5)\n",
    "\n",
    "# position the viewpoint camera of the visualization\n",
    "web_scene.defaultCamera().pointCameraAt(\n",
    "    [0.0, 5.0 + n_links / 2, -n_links / 2], [0, 0, -n_links / 2], [0, 0, 1]\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c9a18b36",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## Add Root Visual Geometry\n",
    "\n",
    "Here we manually add a visual geometry to the top of the pendulum by implicitly attaching it to the root frame."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "c95ec307",
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# create an box geometry for the first pendulum's root attachment\n",
    "box_geom = ks.BoxGeometry(0.25, 0.25, 0.25)\n",
    "\n",
    "# create visual material to color the geometry\n",
    "mat_info = ks.PhysicalMaterialInfo()\n",
    "mat_info.color = ks.Color.BLACK\n",
    "black = ks.PhysicalMaterial(mat_info)\n",
    "\n",
    "# create an unattached object for the top of the pendulum (implicitly attached to the root frame)\n",
    "proxy_scene = mb.getScene()\n",
    "root_scene_node = ks.ProxySceneNode(\"root_scene_node\", scene=proxy_scene)\n",
    "root_part = ks.ProxyScenePart(\"obstacle_part\", scene=proxy_scene, geometry=box_geom, material=black)\n",
    "root_part.setTranslation([0.0, 0.0, -0.5])\n",
    "del root_part, box_geom, black, web_scene, root_scene_node"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b2339c78",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## Setup State Propagator, Initialize the State, and Register Models\n",
    "\n",
    "Now, we setup the {py:class}`Karana.Dynamics.StatePropagator`, initialize the state, and register models.\n",
    "\n",
    "When accessing or modifying [generalized coordinates](coords_sec) for a subhinge, it is recommended to directly set the subhinge's values rather than for the entire multibody in order to avoid ambiguity.\n",
    "\n",
    "See [Models](system_level_models_sec) for more concepts and information."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "e5671002",
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# Set up state propagator and select integrator type: rk4 or cvode\n",
    "sp = StatePropagator(mb, integrator_type=IntegratorType.RK4)\n",
    "integrator = sp.getIntegrator()\n",
    "\n",
    "# Initialize the multibody state.\n",
    "# Here we will set the first pendulum position to pi/8 radians and its velocity to 0.0\n",
    "bd1 = mb.getBody(\"link_0\")\n",
    "bd1.parentHinge().subhinge(0).setQ(pi / 8)\n",
    "bd1.parentHinge().subhinge(0).setU(0.0)\n",
    "del bd1\n",
    "\n",
    "# Initialize the integrator state\n",
    "t_init = np.timedelta64(0, \"ns\")\n",
    "x_init = sp.assembleState()\n",
    "sp.setTime(t_init)\n",
    "sp.setState(x_init)\n",
    "\n",
    "# Syncs up graphics\n",
    "proxy_scene.update()\n",
    "\n",
    "# add a gravitational model to the state propagator\n",
    "ug = kmdl.Gravity(\"grav_model\", sp, kmdl.UniformGravity(\"uniform_gravity\"), mb)\n",
    "ug.getGravityInterface().setGravity(np.array([0, 0, -9.81]), 0.0, kmdl.OutputUpdateType.PRE_HOP)\n",
    "del ug\n",
    "\n",
    "# Makes sure the visualization scene is updated after each state change.\n",
    "kmdl.UpdateProxyScene(\"update_proxy_scene\", sp, proxy_scene)\n",
    "\n",
    "# sync the simulation time with real-time.\n",
    "kmdl.SyncRealTime(\"sync_real_time\", sp, 1.0)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "42b741a2",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## Register a Timed Event\n",
    "\n",
    "To terminate a simulation step every 0.1 seconds, we create a timed event and register it to our state propagator here. See [timed events](timed_events_sec) for more information."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "d5e4c74d",
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def fn(t):\n",
    "    \"\"\"Print out the time and state.\n",
    "\n",
    "    Parameters\n",
    "    ----------\n",
    "    t : float\n",
    "        The current time.\n",
    "    \"\"\"\n",
    "    print(f\"t = {float(integrator.getTime()) / 1e9}s; x = {integrator.getX()}\")\n",
    "\n",
    "\n",
    "h = np.timedelta64(int(1e8), \"ns\")\n",
    "t = kd.TimedEvent(\"hop_size\", h, fn, False)\n",
    "t.period = h\n",
    "\n",
    "# register the timed event\n",
    "sp.registerTimedEvent(t)\n",
    "del t"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "06e5af80",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## Run the Simulation\n",
    "\n",
    "Now, run the simulation for 10 seconds."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "f92e5b77",
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "t = 0.0s; x = [0.39269908 0.         0.         0.         0.         0.\n",
      " 0.         0.         0.         0.        ]\n",
      "t = 0.1s; x = [ 3.80340278e-01  8.12769330e-03  2.79542493e-03  9.65472375e-04\n",
      "  3.60501105e-04 -2.44079278e-01  1.58209078e-01  5.61281060e-02\n",
      "  1.98558651e-02  7.55656592e-03]\n",
      "t = 0.2s; x = [ 0.34507586  0.02998853  0.01128284  0.00417915  0.0016506  -0.45263048\n",
      "  0.26728678  0.11379523  0.04589122  0.01929917]\n",
      "t = 0.3s; x = [ 0.29201121  0.05862807  0.02553738  0.01051414  0.00450408 -0.59621144\n",
      "  0.28937159  0.17042355  0.08278804  0.03952919]\n",
      "t = 0.4s; x = [ 0.22858298  0.08442833  0.04502264  0.02114456  0.00997379 -0.65878327\n",
      "  0.21103326  0.21604765  0.13148721  0.07219775]\n",
      "t = 0.5s; x = [ 0.16292629  0.09803276  0.06781809  0.03706003  0.01945294 -0.64274301\n",
      "  0.05118844  0.23340882  0.18713059  0.12000619]\n",
      "t = 0.6s; x = [ 0.10204012  0.09344022  0.09010517  0.05844584  0.03450736 -0.56771071\n",
      " -0.14379414  0.20298946  0.2385518   0.18357696]\n",
      "t = 0.7s; x = [ 0.05034501  0.07006893  0.10634499  0.08409136  0.05662697 -0.4644049\n",
      " -0.31469685  0.11140524  0.26926843  0.26059414]\n",
      "t = 0.8s; x = [ 0.0089759   0.03314723  0.11035788  0.11096246  0.08686916 -0.36631556\n",
      " -0.40764749 -0.0393914   0.25987457  0.3444787 ]\n",
      "t = 0.9s; x = [-0.02401012 -0.00778327  0.09722528  0.1341314   0.12536219 -0.29988576\n",
      " -0.39310854 -0.22567117  0.19297599  0.42310855]\n",
      "t = 1.0s; x = [-0.05246333 -0.04205473  0.0654555   0.14733862  0.17074469 -0.27582795\n",
      " -0.27909201 -0.40395145  0.06058528  0.4790463 ]\n",
      "t = 1.1s; x = [-0.08038306 -0.06165693  0.0183864   0.14428316  0.21974101 -0.28671776\n",
      " -0.10884258 -0.52392586 -0.12940734  0.49202529]\n",
      "t = 1.2s; x = [-0.11031576 -0.06395388 -0.03603824  0.12034923  0.26707807 -0.31222695\n",
      "  0.05708549 -0.54668726 -0.35138554  0.44316667]\n",
      "t = 1.3s; x = [-0.14251163 -0.05224098 -0.0872181   0.07422596  0.3058818  -0.32834531\n",
      "  0.16428388 -0.45978731 -0.5660497   0.32021597]\n",
      "t = 1.4s; x = [-0.17503309 -0.03410181 -0.12491763  0.00882916  0.32859259 -0.31664405\n",
      "  0.18371342 -0.28279303 -0.73013116  0.1222799 ]\n",
      "t = 1.5s; x = [-0.20465545 -0.01836173 -0.14220918 -0.06892218  0.32820347 -0.27036074\n",
      "  0.1196508  -0.06016746 -0.80870079 -0.13890113]\n",
      "t = 1.6s; x = [-0.22810883 -0.01195431 -0.13720729 -0.14941656  0.29939021 -0.19499518\n",
      "  0.00374064  0.1544468  -0.78359706 -0.44218085]\n",
      "t = 1.7s; x = [-0.24312836 -0.01787394 -0.11320413 -0.22213766  0.23921358 -0.10428151\n",
      " -0.11943948  0.3131201  -0.65459013 -0.76168005]\n",
      "t = 1.8s; x = [-0.24899699 -0.03464815 -0.07758699 -0.27732384  0.14742483 -0.01452862\n",
      " -0.20733399  0.38301929 -0.43643261 -1.0698156 ]\n",
      "t = 1.9s; x = [-0.24653589 -0.05715578 -0.04002648 -0.30730058  0.02654583  0.06053203\n",
      " -0.23104906  0.35199086 -0.15558255 -1.33904285]\n",
      "t = 2.0s; x = [-0.23765431 -0.07837751 -0.01024779 -0.30748363 -0.11822947  0.11324859\n",
      " -0.18209532  0.23115668  0.15324888 -1.54380069]\n",
      "t = 2.1s; x = [-0.22466505 -0.09151193  0.00423693 -0.27700468 -0.27932687  0.14320403\n",
      " -0.07291046  0.05292     0.45107227 -1.66223368]\n",
      "t = 2.2s; x = [-2.09585781e-01 -9.18782916e-02 -2.37091089e-05 -2.18975171e-01\n",
      " -4.47199708e-01  1.56520052e-01  6.74624198e-02 -1.35136407e-01\n",
      "  6.97676726e-01 -1.67681152e+00]\n",
      "t = 2.3s; x = [-0.19355405 -0.07827129 -0.02131216 -0.14048839 -0.61079679  0.16436295\n",
      "  0.19977416 -0.27892849  0.85427638 -1.57532899]\n",
      "t = 2.4s; x = [-0.17644386 -0.05355494 -0.05269741 -0.0521945  -0.75825205  0.18018423\n",
      "  0.28381218 -0.33089775  0.89013121 -1.35444998]\n",
      "t = 2.5s; x = [-0.15688231 -0.02408816 -0.08355787  0.03307348 -0.87798937  0.21445583\n",
      "  0.29233193 -0.2675606   0.79421763 -1.02360717]\n",
      "t = 2.6s; x = [-0.13285325  0.00226462 -0.10269115  0.10272751 -0.95996763  0.26888664\n",
      "  0.22373089 -0.1014471   0.58262782 -0.60310816]\n",
      "t = 2.7s; x = [-0.10269606  0.01886823 -0.10183263  0.14695996 -0.99639634  0.33492132\n",
      "  0.10324417  0.12316162  0.29313363 -0.1163738 ]\n",
      "t = 2.8s; x = [-0.06597349  0.02255626 -0.07811923  0.16033529 -0.98175144  0.39752019\n",
      " -0.02708492  0.34510085 -0.02623681  0.4147474 ]\n",
      "t = 2.9s; x = [-0.02384167  0.01465071 -0.03502809  0.14242231 -0.91275049  0.44074808\n",
      " -0.12175522  0.50103316 -0.32413494  0.96607598]\n",
      "t = 3.0s; x = [ 2.11199614e-02  6.13849564e-04  1.81336600e-02  9.79359634e-02\n",
      " -7.88988499e-01  4.52823094e-01 -1.45831759e-01  5.40434121e-01\n",
      " -5.49826061e-01  1.50308468e+00]\n",
      "t = 3.1s; x = [ 0.06553732 -0.01173136  0.06839572  0.03633586 -0.61422131  0.43012601\n",
      " -0.08893914  0.44327321 -0.66075494  1.97715488]\n",
      "t = 3.2s; x = [ 0.10616573 -0.01509535  0.10286151 -0.02951276 -0.39760136  0.37880153\n",
      "  0.02790091  0.23171511 -0.63294536  2.33046598]\n",
      "t = 3.3s; x = [ 0.14078428 -0.00562796  0.11285603 -0.08563255 -0.15400894  0.31247792\n",
      "  0.15914976 -0.03401721 -0.4692197   2.50914153]\n",
      "t = 3.4s; x = [ 0.16868008  0.01546096  0.09689682 -0.1198983   0.09734974  0.24657585\n",
      "  0.25257218 -0.27426868 -0.20416339  2.48446163]\n",
      "t = 3.5s; x = [ 0.19047406  0.0423184   0.06128264 -0.12513275  0.33656002  0.19131739\n",
      "  0.27084128 -0.41801679  0.09987652  2.2728851 ]\n",
      "t = 3.6s; x = [ 0.2073418   0.06681439  0.01778261 -0.10096206  0.54760154  0.14734156\n",
      "  0.20703666 -0.43015763  0.37358915  1.93270504]\n",
      "t = 3.7s; x = [ 0.2200855   0.08169628 -0.02058687 -0.05317572  0.7212133   0.10725692\n",
      "  0.08413663 -0.32015169  0.56626014  1.535448  ]\n",
      "t = 3.8s; x = [ 0.22859301  0.08295218 -0.04348364  0.0088017   0.85475339  0.06107629\n",
      " -0.05859564 -0.12892601  0.6560276   1.13901836]\n",
      "t = 3.9s; x = [ 2.31823995e-01  7.06902888e-02 -4.54296049e-02  7.46197353e-02\n",
      "  9.50280280e-01  7.49466911e-04 -1.79946170e-01  8.98322040e-02\n",
      "  6.44890100e-01  7.79505108e-01]\n",
      "t = 4.0s; x = [ 0.22813213  0.04882271 -0.0263858   0.13497684  1.01251613 -0.07751167\n",
      " -0.24635349  0.28271363  0.55069698  0.47500705]\n",
      "t = 4.1s; x = [ 0.21577632  0.0239152   0.00875724  0.18289807  1.04730442 -0.17154888\n",
      " -0.23948408  0.40605869  0.40121571  0.23042478]\n",
      "t = 4.2s; x = [ 0.19358923  0.00333098  0.05162246  0.21442703  1.06042208 -0.27197369\n",
      " -0.16234303  0.43512262  0.22788008  0.03981165]\n",
      "t = 4.3s; x = [ 0.16168195 -0.00698051  0.09256045  0.22861876  1.05658799 -0.36300125\n",
      " -0.03952654  0.36943938  0.05833632 -0.11161055]\n",
      "t = 4.4s; x = [ 0.12191942 -0.00431178  0.1230106   0.22686348  1.03873657 -0.42610624\n",
      "  0.0904737   0.23019824 -0.08885445 -0.24412866]\n",
      "t = 4.5s; x = [ 0.07794729  0.00999031  0.13726899  0.21190811  1.00765606 -0.44502019\n",
      "  0.18701743  0.05177534 -0.20489287 -0.37981067]\n",
      "t = 4.6s; x = [ 0.0347247   0.03088294  0.13333129  0.1869921   0.9619683  -0.41043294\n",
      "  0.21844856 -0.12765255 -0.28820599 -0.53946224]\n",
      "t = 4.7s; x = [-0.00236392  0.0509763   0.11286045  0.15528465  0.89838921 -0.32360298\n",
      "  0.1705714  -0.27406608 -0.34130606 -0.73991692]\n",
      "t = 4.8s; x = [-0.02867385  0.06255015  0.08045034  0.11960025  0.81228408 -0.19815624\n",
      "  0.05134652 -0.3636717  -0.36850799 -0.99070074]\n",
      "t = 4.9s; x = [-0.04152416  0.05981188  0.04239197  0.08227612  0.69861919 -0.05938532\n",
      " -0.10904872 -0.3867539  -0.37501297 -1.28936127]\n",
      "t = 5.0s; x = [-0.04120898  0.04091735  0.00516238  0.04507414  0.55345507  0.05940766\n",
      " -0.26313874 -0.34940279 -0.36724127 -1.61545644]\n",
      "t = 5.1s; x = [-0.03145138  0.00918322 -0.02614378  0.0090287   0.37600734  0.12464417\n",
      " -0.35785732 -0.27267887 -0.35330934 -1.92618118]\n",
      "t = 5.2s; x = [-0.01883213 -0.0273192  -0.0490563  -0.02567712  0.17070719  0.11500941\n",
      " -0.354967   -0.18637704 -0.34165294 -2.16256067]\n",
      "t = 5.3s; x = [-0.01090998 -0.05837252 -0.06398519 -0.05954894 -0.05227146  0.03350287\n",
      " -0.25210671 -0.11593966 -0.33676575 -2.27390011]\n",
      "t = 5.4s; x = [-0.013645   -0.07555008 -0.07310922 -0.09314723 -0.27938504 -0.09218678\n",
      " -0.08573936 -0.07009018 -0.33481748 -2.24690192]\n",
      "t = 5.5s; x = [-0.02952832 -0.07524288 -0.07855803 -0.12622427 -0.49795644 -0.22308123\n",
      "  0.08832279 -0.04010936 -0.32403706 -2.1101422 ]\n",
      "t = 5.6s; x = [-0.05724813 -0.0593766  -0.08112509 -0.15717896 -0.69933942 -0.32406074\n",
      "  0.21830743 -0.00998342 -0.29021451 -1.91096895]\n",
      "t = 5.7s; x = [-0.09254362 -0.03422665 -0.08013309 -0.18313357 -0.87947095 -0.37203703\n",
      "  0.2704937   0.03215915 -0.22286821 -1.69062901]\n",
      "t = 5.8s; x = [-0.12957117 -0.00825374 -0.07422636 -0.20050944 -1.0375892  -0.35858333\n",
      "  0.23541652  0.08746104 -0.11878085 -1.47315148]\n",
      "t = 5.9s; x = [-0.16237153  0.01039233 -0.06251558 -0.20580007 -1.17443705 -0.28949943\n",
      "  0.1282829   0.14584044  0.01748972 -1.26506045]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "t = 6.0s; x = [-0.18615354  0.01609418 -0.04551395 -0.19629537 -1.29070167 -0.18163193\n",
      " -0.01684898  0.19027369  0.17479083 -1.05954529]\n",
      "t = 6.1s; x = [-0.1981584   0.0071807  -0.02552306 -0.1706863  -1.38593739 -0.05790963\n",
      " -0.15683465  0.20307134  0.33633851 -0.84160628]\n",
      "t = 6.2s; x = [-0.19799978 -0.01369751 -0.00638082 -0.12961254 -1.4579977   0.05803007\n",
      " -0.25016724  0.17219048  0.48004419 -0.59303447]\n",
      "t = 6.3s; x = [-0.18746433 -0.04028503  0.00739537 -0.07612949 -1.50295577  0.14721868\n",
      " -0.26802898  0.09684694  0.58023532 -0.29719111]\n",
      "t = 6.4s; x = [-0.16980041 -0.06449746  0.01191643 -0.01584034 -1.51547253  0.19994929\n",
      " -0.20376013 -0.00955003  0.61269037  0.05703766]\n",
      "t = 6.5s; x = [-0.14867613 -0.0788431   0.00526621  0.04357951 -1.48945075  0.21746814\n",
      " -0.07548785 -0.12182958  0.56111246  0.47369297]\n",
      "t = 6.6s; x = [-0.127164   -0.07864085 -0.0116496   0.09339251 -1.41870673  0.20985952\n",
      "  0.08031338 -0.21001636  0.42080794  0.95069319]\n",
      "t = 6.7s; x = [-0.10705606 -0.06331629 -0.03500713  0.12498393 -1.29753325  0.19193959\n",
      "  0.21984607 -0.24683593  0.19884582  1.4804533 ]\n",
      "t = 6.8s; x = [-0.08860352 -0.03658338 -0.05865115  0.13103892 -1.12136876  0.17903564\n",
      "  0.30282015 -0.2138738  -0.08551123  2.046639  ]\n",
      "t = 6.9s; x = [-0.07067001 -0.00559414 -0.07527141  0.10694529 -0.88809385  0.182925\n",
      "  0.30231338 -0.10742495 -0.39632676  2.61450754]\n",
      "t = 7.0s; x = [-0.05127849  0.020863   -0.07818781  0.05287975 -0.600675    0.20811379\n",
      "  0.21389976  0.05521539 -0.67233724  3.11401464]\n",
      "t = 7.1s; x = [-0.02848641  0.03501968 -0.06372436 -0.02338837 -0.27144685  0.24910105\n",
      "  0.0633272   0.2311316  -0.82469492  3.42882892]\n",
      "t = 7.2s; x = [-1.44512634e-03  3.32875646e-02 -3.35564848e-02 -1.05037565e-01\n",
      "  7.47209484e-02  2.89836764e-01 -9.30567961e-02  3.59510755e-01\n",
      " -7.71208119e-01  3.43852667e+00]\n",
      "t = 7.3s; x = [ 0.02877045  0.01840248  0.00492836 -0.17037526  0.40514547  0.30954132\n",
      " -0.19102397  0.3930221  -0.50608832  3.12255454]\n",
      "t = 7.4s; x = [ 5.92746842e-02 -1.73403089e-03  4.15911210e-02 -2.01856621e-01\n",
      "  6.91787718e-01  2.94520963e-01 -1.96280393e-01  3.25499026e-01\n",
      " -1.11235549e-01  2.58539209e+00]\n",
      "t = 7.5s; x = [ 0.08647908 -0.01793598  0.06751042 -0.19173     0.91931112  0.24459566\n",
      " -0.11637462  0.18425033  0.31072427  1.95934153]\n",
      "t = 7.6s; x = [ 0.10737596 -0.0232652   0.07734262 -0.14148165  1.08382479  0.17085397\n",
      "  0.01427992  0.01123001  0.68026068  1.33685508]\n",
      "t = 7.7s; x = [ 0.12043009 -0.01487805  0.07026594 -0.05934732  1.18867167  0.09085895\n",
      "  0.15030458 -0.14608817  0.94059667  0.77319496]\n",
      "t = 7.8s; x = [ 0.12597128  0.00546729  0.05018584  0.04166307  1.24164843  0.02339104\n",
      "  0.24694982 -0.24244321  1.05343394  0.30417217]\n",
      "t = 7.9s; x = [ 0.12600131  0.03210644  0.0248265   0.14596259  1.25338175 -0.01793868\n",
      "  0.27305331 -0.24940885  1.00737909 -0.04993694]\n",
      "t = 8.0s; x = [ 0.12339485  0.05745323  0.00330726  0.23852112  1.2355191  -0.02970198\n",
      "  0.22241179 -0.16852776  0.82469687 -0.28968144]\n",
      "t = 8.1s; x = [ 0.12085433  0.07460502 -0.00686185  0.30781343  1.1985979  -0.01822437\n",
      "  0.11377964 -0.02858938  0.5501177  -0.4357001 ]\n",
      "t = 8.2s; x = [ 0.12015512  0.07935507 -0.00178956  0.34706239  1.15046903  0.00512034\n",
      " -0.0196753   0.12938674  0.2311179  -0.51934286]\n",
      "t = 8.3s; x = [ 0.12187333  0.0710573   0.01826356  0.35388978  1.09570288  0.02825335\n",
      " -0.14138627  0.26484082 -0.09240412 -0.57383983]\n",
      "t = 8.4s; x = [ 0.12544723  0.05257182  0.04927286  0.32953921  1.03562444  0.04079972\n",
      " -0.21885886  0.34374393 -0.38732801 -0.63055689]\n",
      "t = 8.5s; x = [ 0.1294379   0.02953253  0.08430533  0.27838303  0.96856882  0.03590111\n",
      " -0.23023214  0.3426895  -0.62382746 -0.71768131]\n",
      "t = 8.6s; x = [ 0.13197988  0.00893803  0.11482378  0.20763287  0.89029893  0.01216308\n",
      " -0.17103701  0.25378281 -0.7753097  -0.85791407]\n",
      "t = 8.7s; x = [ 0.131394   -0.00282292  0.13244969  0.12684184  0.79475199 -0.02529309\n",
      " -0.05809971  0.08843762 -0.82240302 -1.06424707]\n",
      "t = 8.8s; x = [ 0.12680286 -0.00204977  0.13094022  0.04689842  0.67527313 -0.06595087\n",
      "  0.07253666 -0.12233454 -0.75876737 -1.33458738]\n",
      "t = 8.9s; x = [ 0.11851378  0.0107119   0.10800671 -0.02158101  0.52641491 -0.09738785\n",
      "  0.17383805 -0.33138334 -0.5971235  -1.64596804]\n",
      "t = 9.0s; x = [ 0.1079486   0.03023146  0.06656039 -0.07041209  0.34625555 -0.11074522\n",
      "  0.20199773 -0.48389902 -0.37368709 -1.95079226]\n",
      "t = 9.1s; x = [ 0.09697512  0.04787895  0.0147018  -0.09612066  0.13866722 -0.10676005\n",
      "  0.13611048 -0.53433329 -0.14468482 -2.18334232]\n",
      "t = 9.2s; x = [ 0.08678186  0.0548107  -0.03630141 -0.10113093 -0.08596943 -0.09798972\n",
      " -0.00600398 -0.46781498  0.03231758 -2.28442431]\n",
      "t = 9.3s; x = [ 0.07691257  0.04582501 -0.07570181 -0.0925646  -0.31300371 -0.10331063\n",
      " -0.17213604 -0.30912858  0.12451076 -2.23150205]\n",
      "t = 9.4s; x = [ 0.06511558  0.02165249 -0.09672299 -0.07901576 -0.52778595 -0.13829805\n",
      " -0.30056131 -0.10942553  0.13505369 -2.04551212]\n",
      "t = 9.5s; x = [ 0.04805458 -0.01146767 -0.09805222 -0.06736524 -0.71911973 -0.2083674\n",
      " -0.34620081  0.0766451   0.09217341 -1.77017964]\n",
      "t = 9.6s; x = [ 0.02247882 -0.04426964 -0.08325909 -0.06119633 -0.8802723  -0.3063202\n",
      " -0.29475566  0.20807519  0.03087558 -1.44798454]\n",
      "t = 9.7s; x = [-0.01354305 -0.0677052  -0.0589626  -0.06082355 -1.00815836 -0.41345672\n",
      " -0.16417895  0.2654784  -0.01991817 -1.10852869]\n",
      "t = 9.8s; x = [-0.05967527 -0.07581401 -0.03260224 -0.06422622 -1.10194107 -0.50410881\n",
      "  0.00369028  0.25132849 -0.04283414 -0.76755169]\n",
      "t = 9.9s; x = [-0.11294311 -0.06744222 -0.01048559 -0.06826189 -1.16181314 -0.55231871\n",
      "  0.15703804  0.18461507 -0.03268267 -0.43066941]\n",
      "t = 10.0s; x = [-0.16805539 -0.04646982  0.00346486 -0.06981977 -1.18821722 -0.53879608\n",
      "  0.24948818  0.09258339  0.00506396 -0.097956  ]\n"
     ]
    }
   ],
   "source": [
    "# run the simulation\n",
    "print(f\"t = {float(integrator.getTime()) / 1e9}s; x = {integrator.getX()}\")\n",
    "\n",
    "# run the simulation\n",
    "sp.advanceTo(10.0)\n",
    "\n",
    "# dump the state propagator info\n",
    "sp.dump(\"sp\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "822f49cf",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## Clean Up the Simulation\n",
    "\n",
    "Below, we cleanup our simulation. We first delete local variables, cleanup our visualizer, discard remaining Karana objects, and optionally verify using allDestroyed."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "4ee98829",
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<function __main__.cleanup()>"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Cleanup\n",
    "def cleanup():\n",
    "    \"\"\"Cleanup the simulation.\"\"\"\n",
    "    global integrator, proxy_scene\n",
    "    del integrator, proxy_scene\n",
    "    kc.discard(sp)\n",
    "    cleanup_graphics()\n",
    "    kc.discard(mb)\n",
    "    kc.discard(fc)\n",
    "\n",
    "\n",
    "atexit.register(cleanup)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8d650788",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## Summary\n",
    "Great work! You now understand how to procedurally create a multibody using {py:meth}`Karana.Dynamics.PhysicalBody.addSerialChain` instead of defining each part individually. You learned how creating these bodies requires the use of parameters like {py:class}`Karana.Scene.ScenePartSpec`, and how to add a root geometry that the pendulum attaches to.\n",
    "\n",
    "## Further Readings\n",
    "[Benchmark the n-link pendulum](../example_rigid_timing/notebook.ipynb)  \n",
    "[Benchmark the n-link pendulum against conventional methods](../example_fully_augmented_benchmark/notebook.ipynb)  \n",
    "[Simulate collisions with a n-link pendulum](../example_procedural_collision/notebook.ipynb) \n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "argv": [
    "python",
    "-m",
    "ipykernel_launcher",
    "-f",
    "{connection_file}"
   ],
   "display_name": "Python 3 (ipykernel)",
   "env": null,
   "interrupt_mode": "signal",
   "kernel_protocol_version": "5.5",
   "language": "python",
   "metadata": {
    "debugger": true
   },
   "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
}
