{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "a35c1436",
   "metadata": {},
   "source": [
    "# Load"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "383c7550",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Available engines: ['cythonized_euler_engine', 'cythonized_rk4_engine', 'euler_engine', 'rk4_engine', 'scipy_engine', 'verlet_engine']\n"
     ]
    }
   ],
   "source": [
    "import io\n",
    "import logging\n",
    "import math\n",
    "import sys\n",
    "import pandas as pd\n",
    "from typing import Tuple, get_args\n",
    "from py_ballisticcalc import Ammo, Atmo, Weapon, Shot, Calculator, HitResult\n",
    "from py_ballisticcalc import RangeError, TrajFlag, BaseEngineConfigDict, SciPyEngineConfigDict\n",
    "from py_ballisticcalc import TableG7, logger, loadMetricUnits\n",
    "from py_ballisticcalc.drag_model import DragModel\n",
    "from py_ballisticcalc.helpers import must_fire\n",
    "from py_ballisticcalc.unit import *\n",
    "from py_ballisticcalc.interface import _EngineLoader\n",
    "logger.setLevel(logging.WARNING)\n",
    "print(\"\\nAvailable engines: \" + str(sorted([e.name for e in _EngineLoader.iter_engines()])))\n",
    "loadMetricUnits()\n",
    "PreferredUnits.drop = Distance.Meter\n",
    "PreferredUnits.distance = Distance.Meter"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7fbb7ac2",
   "metadata": {},
   "source": [
    "## Reference Calculator\n",
    "\n",
    "This _reference calculator_ will determine the \"correct\" trajectory against which the error of all others will be measured.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "b0b1c795",
   "metadata": {},
   "outputs": [],
   "source": [
    "ref_config = SciPyEngineConfigDict(\n",
    "    relative_tolerance=1e-12,\n",
    "    absolute_tolerance=1e-12,\n",
    "    integration_method=\"LSODA\",\n",
    ")\n",
    "ref_calc = Calculator(ref_config, engine='scipy_engine')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1b0e4c84",
   "metadata": {},
   "source": [
    "# Scenarios"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "80c37ca9",
   "metadata": {},
   "source": [
    "## 2km flat-fire trajectory\n",
    "\n",
    "A typical 7.62mm bullet launched at an elevation (60mils, about 3.375 degrees) sufficient to reach approximately 2km.  We use the reference calculator to compute the trajectory."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "a3f7bed0",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>time</th>\n",
       "      <th>distance</th>\n",
       "      <th>velocity</th>\n",
       "      <th>mach</th>\n",
       "      <th>height</th>\n",
       "      <th>angle</th>\n",
       "      <th>density_ratio</th>\n",
       "      <th>drag</th>\n",
       "      <th>energy</th>\n",
       "      <th>flag</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>0.001 s</td>\n",
       "      <td>0.7 m</td>\n",
       "      <td>800 m/s</td>\n",
       "      <td>2.35 mach</td>\n",
       "      <td>-0.0 m</td>\n",
       "      <td>3.3744 °</td>\n",
       "      <td>1.00002e+00</td>\n",
       "      <td>2.635e-04</td>\n",
       "      <td>3196 J</td>\n",
       "      <td>ZERO_UP</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>10</th>\n",
       "      <td>1.564 s</td>\n",
       "      <td>814.0 m</td>\n",
       "      <td>340 m/s</td>\n",
       "      <td>1.00 mach</td>\n",
       "      <td>38.8 m</td>\n",
       "      <td>1.5851 °</td>\n",
       "      <td>9.96307e-01</td>\n",
       "      <td>3.605e-04</td>\n",
       "      <td>579 J</td>\n",
       "      <td>MACH</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>13</th>\n",
       "      <td>2.440 s</td>\n",
       "      <td>1086.3 m</td>\n",
       "      <td>292 m/s</td>\n",
       "      <td>0.86 mach</td>\n",
       "      <td>42.7 m</td>\n",
       "      <td>-0.0000 °</td>\n",
       "      <td>9.95929e-01</td>\n",
       "      <td>1.255e-04</td>\n",
       "      <td>426 J</td>\n",
       "      <td>APEX</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>22</th>\n",
       "      <td>5.534 s</td>\n",
       "      <td>1861.7 m</td>\n",
       "      <td>219 m/s</td>\n",
       "      <td>0.64 mach</td>\n",
       "      <td>0.0 m</td>\n",
       "      <td>-6.9538 °</td>\n",
       "      <td>1.00002e+00</td>\n",
       "      <td>1.134e-04</td>\n",
       "      <td>240 J</td>\n",
       "      <td>ZERO_DOWN</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "       time  distance velocity       mach  height      angle density_ratio  \\\n",
       "1   0.001 s     0.7 m  800 m/s  2.35 mach  -0.0 m   3.3744 °   1.00002e+00   \n",
       "10  1.564 s   814.0 m  340 m/s  1.00 mach  38.8 m   1.5851 °   9.96307e-01   \n",
       "13  2.440 s  1086.3 m  292 m/s  0.86 mach  42.7 m  -0.0000 °   9.95929e-01   \n",
       "22  5.534 s  1861.7 m  219 m/s  0.64 mach   0.0 m  -6.9538 °   1.00002e+00   \n",
       "\n",
       "         drag  energy       flag  \n",
       "1   2.635e-04  3196 J    ZERO_UP  \n",
       "10  3.605e-04   579 J       MACH  \n",
       "13  1.255e-04   426 J       APEX  \n",
       "22  1.134e-04   240 J  ZERO_DOWN  "
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 54444 characters]",
      "text/plain": [
       "<Figure size 640x480 with 3 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "dm = DragModel(0.22, TableG7, Weight.Gram(10), Distance.Centimeter(7.62), Distance.Centimeter(3.0))\n",
    "ammo = Ammo(dm, Velocity.MPS(800))\n",
    "weapon = Weapon(sight_height=Distance.Centimeter(4), twist=Distance.Centimeter(30), zero_elevation=Angular.Mil(60.0))\n",
    "baseline_shot = Shot(weapon=weapon, ammo=ammo, atmo=Atmo.icao())\n",
    "range = Distance.Meter(2000)\n",
    "range_step = Distance.Meter(100)\n",
    "reference_trajectory = ref_calc.fire(shot=baseline_shot, trajectory_range=range, trajectory_step=range_step, extra_data=True)\n",
    "reference_trajectory.plot()\n",
    "ref = reference_trajectory.dataframe(True).drop(columns=['slant_height', 'drop_angle', 'windage', 'windage_angle', 'slant_distance', 'ogw'])\n",
    "ref[ref.flag != \"RANGE\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bd7bf991",
   "metadata": {},
   "source": [
    "### Define error\n",
    "\n",
    "We will take the ZERO_DOWN (return-to-zero) row as our reference point for assessing the error in other engines."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "0c8831f6",
   "metadata": {},
   "outputs": [],
   "source": [
    "reference_row = reference_trajectory.flag(TrajFlag.ZERO_DOWN)\n",
    "reference_distance = reference_row.distance\n",
    "reference_distance_meter = reference_distance >> Distance.Meter\n",
    "def check_error(hit: HitResult, output: bool = False) -> float:\n",
    "    \"\"\"Error is vertical distance from zero at the reference distance.\"\"\"\n",
    "    chkpt = hit.get_at_distance(reference_distance)\n",
    "    if chkpt is not None:\n",
    "        chk_x = chkpt.distance >> Distance.Meter\n",
    "        chk_h = chkpt.height >> Distance.Meter\n",
    "        chk_err = math.sqrt((chk_x-reference_distance_meter)**2 + chk_h**2)\n",
    "        if output:\n",
    "            print(f'At {chkpt.time}s: ({chk_x}, {chk_h})m ==> Error = {chk_err:.8f}m')\n",
    "        return chk_err\n",
    "    return float('inf')\n",
    "\n",
    "summary = []"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3a1621a1",
   "metadata": {},
   "source": [
    "## SciPy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "cc16d0a5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "62.3 ms ± 1.03 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
      "Error=0.00016476m.  Integration steps: 1776.  Speed: 0.06231s\n"
     ]
    }
   ],
   "source": [
    "def scipy_chk(timeit: bool = False, **kwargs):\n",
    "    config = SciPyEngineConfigDict(\n",
    "        **kwargs,\n",
    "    )\n",
    "    calc = Calculator(config, engine='scipy_engine')\n",
    "    hit, e = must_fire(calc, shot=baseline_shot, trajectory_range=range, trajectory_step=reference_distance)\n",
    "    err = check_error(hit)\n",
    "    evals = calc.integration_step_count\n",
    "    if timeit:\n",
    "        speed = %timeit -o must_fire(calc, shot=baseline_shot, trajectory_range=range, trajectory_step=reference_distance)\n",
    "        return err, evals, speed.average\n",
    "    return err, evals\n",
    "logger.setLevel(logging.WARNING)\n",
    "# Run with default tolerance:\n",
    "err, count, speed = scipy_chk(timeit=True, integration_method=\"LSODA\")\n",
    "print(f'Error={err:.8f}m.  Integration steps: {count}.  Speed: {speed:.5f}s')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2500dad4",
   "metadata": {},
   "source": [
    "### Integration Methods\n",
    "\n",
    "Here's a quick look at each of the integration methods listed in the SciPyIntegrationEngine, using its default error tolerance settings."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "f4da2491",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "59.8 ms ± 1.58 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
      "40 ms ± 11.8 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
      "123 ms ± 1 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
      "353 ms ± 97.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
      "195 ms ± 8.33 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
      "68.7 ms ± 16.4 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Engine</th>\n",
       "      <th>Error (m)</th>\n",
       "      <th>Integration Steps</th>\n",
       "      <th>Speed (s)</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Setting</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Radau</th>\n",
       "      <td>SciPy</td>\n",
       "      <td>0.000003</td>\n",
       "      <td>4591</td>\n",
       "      <td>0.352530</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>BDF</th>\n",
       "      <td>SciPy</td>\n",
       "      <td>0.000036</td>\n",
       "      <td>1965</td>\n",
       "      <td>0.194772</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>RK23</th>\n",
       "      <td>SciPy</td>\n",
       "      <td>0.000076</td>\n",
       "      <td>1583</td>\n",
       "      <td>0.059769</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>LSODA</th>\n",
       "      <td>SciPy</td>\n",
       "      <td>0.000165</td>\n",
       "      <td>1776</td>\n",
       "      <td>0.068729</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>DOP853</th>\n",
       "      <td>SciPy</td>\n",
       "      <td>0.000852</td>\n",
       "      <td>5402</td>\n",
       "      <td>0.122707</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>RK45</th>\n",
       "      <td>SciPy</td>\n",
       "      <td>0.001351</td>\n",
       "      <td>1118</td>\n",
       "      <td>0.039986</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        Engine  Error (m)  Integration Steps  Speed (s)\n",
       "Setting                                                \n",
       "Radau    SciPy   0.000003               4591   0.352530\n",
       "BDF      SciPy   0.000036               1965   0.194772\n",
       "RK23     SciPy   0.000076               1583   0.059769\n",
       "LSODA    SciPy   0.000165               1776   0.068729\n",
       "DOP853   SciPy   0.000852               5402   0.122707\n",
       "RK45     SciPy   0.001351               1118   0.039986"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from py_ballisticcalc.engines.scipy_engine import INTEGRATION_METHOD\n",
    "method_summary = []\n",
    "for method in get_args(INTEGRATION_METHOD):\n",
    "    err, count, speed = scipy_chk(timeit=True, integration_method=method)\n",
    "    method_summary.append(('SciPy', method, err, count, speed))\n",
    "pd.DataFrame(method_summary, columns=['Engine', 'Setting', 'Error (m)', 'Integration Steps', 'Speed (s)']).set_index('Setting').sort_values(by='Error (m)', ascending=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9bf3210e",
   "metadata": {},
   "source": [
    "### Tolerance\n",
    "\n",
    "SciPy allows us to specify both \"absolute\" and \"relative\" error tolerance.  The meaning of the two terms and their interaction isn't always intuitive, so let's see what happens as we vary each while holding out the other term.  We'll do this for both the LSODA and RK45 methods.\n",
    "\n",
    "#### LSODA"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "939e5522",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style type=\"text/css\">\n",
       "</style>\n",
       "<table id=\"T_14352\">\n",
       "  <thead>\n",
       "    <tr>\n",
       "      <th class=\"blank level0\" >&nbsp;</th>\n",
       "      <th id=\"T_14352_level0_col0\" class=\"col_heading level0 col0\" >Method</th>\n",
       "      <th id=\"T_14352_level0_col1\" class=\"col_heading level0 col1\" >Absolute Tolerance</th>\n",
       "      <th id=\"T_14352_level0_col2\" class=\"col_heading level0 col2\" >Relative Tolerance</th>\n",
       "      <th id=\"T_14352_level0_col3\" class=\"col_heading level0 col3\" >Error (m)</th>\n",
       "      <th id=\"T_14352_level0_col4\" class=\"col_heading level0 col4\" >Integration Steps</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th id=\"T_14352_level0_row0\" class=\"row_heading level0 row0\" >0</th>\n",
       "      <td id=\"T_14352_row0_col0\" class=\"data row0 col0\" >LSODA</td>\n",
       "      <td id=\"T_14352_row0_col1\" class=\"data row0 col1\" >1e-13</td>\n",
       "      <td id=\"T_14352_row0_col2\" class=\"data row0 col2\" >1e-01</td>\n",
       "      <td id=\"T_14352_row0_col3\" class=\"data row0 col3\" >2.00897324879</td>\n",
       "      <td id=\"T_14352_row0_col4\" class=\"data row0 col4\" >100</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_14352_level0_row1\" class=\"row_heading level0 row1\" >1</th>\n",
       "      <td id=\"T_14352_row1_col0\" class=\"data row1 col0\" >LSODA</td>\n",
       "      <td id=\"T_14352_row1_col1\" class=\"data row1 col1\" >1e-13</td>\n",
       "      <td id=\"T_14352_row1_col2\" class=\"data row1 col2\" >1e-02</td>\n",
       "      <td id=\"T_14352_row1_col3\" class=\"data row1 col3\" >0.78836705253</td>\n",
       "      <td id=\"T_14352_row1_col4\" class=\"data row1 col4\" >121</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_14352_level0_row2\" class=\"row_heading level0 row2\" >3</th>\n",
       "      <td id=\"T_14352_row2_col0\" class=\"data row2 col0\" >LSODA</td>\n",
       "      <td id=\"T_14352_row2_col1\" class=\"data row2 col1\" >1e-13</td>\n",
       "      <td id=\"T_14352_row2_col2\" class=\"data row2 col2\" >1e-04</td>\n",
       "      <td id=\"T_14352_row2_col3\" class=\"data row2 col3\" >0.02085849732</td>\n",
       "      <td id=\"T_14352_row2_col4\" class=\"data row2 col4\" >162</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_14352_level0_row3\" class=\"row_heading level0 row3\" >9</th>\n",
       "      <td id=\"T_14352_row3_col0\" class=\"data row3 col0\" >LSODA</td>\n",
       "      <td id=\"T_14352_row3_col1\" class=\"data row3 col1\" >1e-01</td>\n",
       "      <td id=\"T_14352_row3_col2\" class=\"data row3 col2\" >1e-13</td>\n",
       "      <td id=\"T_14352_row3_col3\" class=\"data row3 col3\" >0.01841224363</td>\n",
       "      <td id=\"T_14352_row3_col4\" class=\"data row3 col4\" >82</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_14352_level0_row4\" class=\"row_heading level0 row4\" >10</th>\n",
       "      <td id=\"T_14352_row4_col0\" class=\"data row4 col0\" >LSODA</td>\n",
       "      <td id=\"T_14352_row4_col1\" class=\"data row4 col1\" >1e-02</td>\n",
       "      <td id=\"T_14352_row4_col2\" class=\"data row4 col2\" >1e-13</td>\n",
       "      <td id=\"T_14352_row4_col3\" class=\"data row4 col3\" >0.00539150910</td>\n",
       "      <td id=\"T_14352_row4_col4\" class=\"data row4 col4\" >171</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_14352_level0_row5\" class=\"row_heading level0 row5\" >5</th>\n",
       "      <td id=\"T_14352_row5_col0\" class=\"data row5 col0\" >LSODA</td>\n",
       "      <td id=\"T_14352_row5_col1\" class=\"data row5 col1\" >1e-13</td>\n",
       "      <td id=\"T_14352_row5_col2\" class=\"data row5 col2\" >1e-06</td>\n",
       "      <td id=\"T_14352_row5_col3\" class=\"data row5 col3\" >0.00227701489</td>\n",
       "      <td id=\"T_14352_row5_col4\" class=\"data row5 col4\" >496</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_14352_level0_row6\" class=\"row_heading level0 row6\" >11</th>\n",
       "      <td id=\"T_14352_row6_col0\" class=\"data row6 col0\" >LSODA</td>\n",
       "      <td id=\"T_14352_row6_col1\" class=\"data row6 col1\" >1e-03</td>\n",
       "      <td id=\"T_14352_row6_col2\" class=\"data row6 col2\" >1e-13</td>\n",
       "      <td id=\"T_14352_row6_col3\" class=\"data row6 col3\" >0.00207619842</td>\n",
       "      <td id=\"T_14352_row6_col4\" class=\"data row6 col4\" >458</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_14352_level0_row7\" class=\"row_heading level0 row7\" >4</th>\n",
       "      <td id=\"T_14352_row7_col0\" class=\"data row7 col0\" >LSODA</td>\n",
       "      <td id=\"T_14352_row7_col1\" class=\"data row7 col1\" >1e-13</td>\n",
       "      <td id=\"T_14352_row7_col2\" class=\"data row7 col2\" >1e-05</td>\n",
       "      <td id=\"T_14352_row7_col3\" class=\"data row7 col3\" >0.00130312391</td>\n",
       "      <td id=\"T_14352_row7_col4\" class=\"data row7 col4\" >263</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_14352_level0_row8\" class=\"row_heading level0 row8\" >2</th>\n",
       "      <td id=\"T_14352_row8_col0\" class=\"data row8 col0\" >LSODA</td>\n",
       "      <td id=\"T_14352_row8_col1\" class=\"data row8 col1\" >1e-13</td>\n",
       "      <td id=\"T_14352_row8_col2\" class=\"data row8 col2\" >1e-03</td>\n",
       "      <td id=\"T_14352_row8_col3\" class=\"data row8 col3\" >0.00087850598</td>\n",
       "      <td id=\"T_14352_row8_col4\" class=\"data row8 col4\" >155</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_14352_level0_row9\" class=\"row_heading level0 row9\" >6</th>\n",
       "      <td id=\"T_14352_row9_col0\" class=\"data row9 col0\" >LSODA</td>\n",
       "      <td id=\"T_14352_row9_col1\" class=\"data row9 col1\" >1e-13</td>\n",
       "      <td id=\"T_14352_row9_col2\" class=\"data row9 col2\" >1e-07</td>\n",
       "      <td id=\"T_14352_row9_col3\" class=\"data row9 col3\" >0.00079173517</td>\n",
       "      <td id=\"T_14352_row9_col4\" class=\"data row9 col4\" >1041</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_14352_level0_row10\" class=\"row_heading level0 row10\" >12</th>\n",
       "      <td id=\"T_14352_row10_col0\" class=\"data row10 col0\" >LSODA</td>\n",
       "      <td id=\"T_14352_row10_col1\" class=\"data row10 col1\" >1e-04</td>\n",
       "      <td id=\"T_14352_row10_col2\" class=\"data row10 col2\" >1e-13</td>\n",
       "      <td id=\"T_14352_row10_col3\" class=\"data row10 col3\" >0.00019150664</td>\n",
       "      <td id=\"T_14352_row10_col4\" class=\"data row10 col4\" >1013</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_14352_level0_row11\" class=\"row_heading level0 row11\" >7</th>\n",
       "      <td id=\"T_14352_row11_col0\" class=\"data row11 col0\" >LSODA</td>\n",
       "      <td id=\"T_14352_row11_col1\" class=\"data row11 col1\" >1e-13</td>\n",
       "      <td id=\"T_14352_row11_col2\" class=\"data row11 col2\" >1e-08</td>\n",
       "      <td id=\"T_14352_row11_col3\" class=\"data row11 col3\" >0.00009840609</td>\n",
       "      <td id=\"T_14352_row11_col4\" class=\"data row11 col4\" >1787</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_14352_level0_row12\" class=\"row_heading level0 row12\" >13</th>\n",
       "      <td id=\"T_14352_row12_col0\" class=\"data row12 col0\" >LSODA</td>\n",
       "      <td id=\"T_14352_row12_col1\" class=\"data row12 col1\" >1e-05</td>\n",
       "      <td id=\"T_14352_row12_col2\" class=\"data row12 col2\" >1e-13</td>\n",
       "      <td id=\"T_14352_row12_col3\" class=\"data row12 col3\" >0.00004666034</td>\n",
       "      <td id=\"T_14352_row12_col4\" class=\"data row12 col4\" >1968</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_14352_level0_row13\" class=\"row_heading level0 row13\" >14</th>\n",
       "      <td id=\"T_14352_row13_col0\" class=\"data row13 col0\" >LSODA</td>\n",
       "      <td id=\"T_14352_row13_col1\" class=\"data row13 col1\" >1e-06</td>\n",
       "      <td id=\"T_14352_row13_col2\" class=\"data row13 col2\" >1e-13</td>\n",
       "      <td id=\"T_14352_row13_col3\" class=\"data row13 col3\" >0.00000226239</td>\n",
       "      <td id=\"T_14352_row13_col4\" class=\"data row13 col4\" >2908</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_14352_level0_row14\" class=\"row_heading level0 row14\" >8</th>\n",
       "      <td id=\"T_14352_row14_col0\" class=\"data row14 col0\" >LSODA</td>\n",
       "      <td id=\"T_14352_row14_col1\" class=\"data row14 col1\" >1e-13</td>\n",
       "      <td id=\"T_14352_row14_col2\" class=\"data row14 col2\" >1e-09</td>\n",
       "      <td id=\"T_14352_row14_col3\" class=\"data row14 col3\" >0.00000179623</td>\n",
       "      <td id=\"T_14352_row14_col4\" class=\"data row14 col4\" >2796</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_14352_level0_row15\" class=\"row_heading level0 row15\" >16</th>\n",
       "      <td id=\"T_14352_row15_col0\" class=\"data row15 col0\" >LSODA</td>\n",
       "      <td id=\"T_14352_row15_col1\" class=\"data row15 col1\" >1e-08</td>\n",
       "      <td id=\"T_14352_row15_col2\" class=\"data row15 col2\" >1e-13</td>\n",
       "      <td id=\"T_14352_row15_col3\" class=\"data row15 col3\" >0.00000001624</td>\n",
       "      <td id=\"T_14352_row15_col4\" class=\"data row15 col4\" >4108</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_14352_level0_row16\" class=\"row_heading level0 row16\" >15</th>\n",
       "      <td id=\"T_14352_row16_col0\" class=\"data row16 col0\" >LSODA</td>\n",
       "      <td id=\"T_14352_row16_col1\" class=\"data row16 col1\" >1e-07</td>\n",
       "      <td id=\"T_14352_row16_col2\" class=\"data row16 col2\" >1e-13</td>\n",
       "      <td id=\"T_14352_row16_col3\" class=\"data row16 col3\" >0.00000000704</td>\n",
       "      <td id=\"T_14352_row16_col4\" class=\"data row16 col4\" >3411</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_14352_level0_row17\" class=\"row_heading level0 row17\" >17</th>\n",
       "      <td id=\"T_14352_row17_col0\" class=\"data row17 col0\" >LSODA</td>\n",
       "      <td id=\"T_14352_row17_col1\" class=\"data row17 col1\" >1e-09</td>\n",
       "      <td id=\"T_14352_row17_col2\" class=\"data row17 col2\" >1e-13</td>\n",
       "      <td id=\"T_14352_row17_col3\" class=\"data row17 col3\" >0.00000000028</td>\n",
       "      <td id=\"T_14352_row17_col4\" class=\"data row17 col4\" >4965</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n"
      ],
      "text/plain": [
       "<pandas.io.formats.style.Styler at 0x2c32ddabf20>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "tol_tests = []\n",
    "method = 'LSODA'\n",
    "# Run with rtol as limiting factor\n",
    "rtol = 1.\n",
    "atol = 1e-13\n",
    "while rtol > 1e-8:\n",
    "    rtol /= 10.0\n",
    "    err, count = scipy_chk(integration_method=method, relative_tolerance=rtol, absolute_tolerance=atol)\n",
    "    tol_tests.append((method, atol, rtol, err, count))\n",
    "# Run with atol as limiting factor\n",
    "atol = 1.\n",
    "rtol = 1e-13\n",
    "while atol > 1e-8:\n",
    "    atol /= 10.0\n",
    "    err, count = scipy_chk(integration_method=method, relative_tolerance=rtol, absolute_tolerance=atol)\n",
    "    tol_tests.append((method, atol, rtol, err, count))\n",
    "display(\n",
    "    pd.DataFrame(tol_tests, columns=['Method', 'Absolute Tolerance', 'Relative Tolerance', 'Error (m)', 'Integration Steps'])\n",
    "      .sort_values(by='Error (m)', ascending=False)\n",
    "      .style.format({'Absolute Tolerance': '{:.0e}', 'Relative Tolerance': '{:.0e}', 'Error (m)': '{:.11f}'}))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "0c51a373",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 97416 characters]",
      "text/plain": [
       "<Figure size 1200x800 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "df_tol = pd.DataFrame(tol_tests, columns=['Method', 'Absolute Tolerance', 'Relative Tolerance', 'Error (m)', 'Integration Steps'])\n",
    "# Separate the two series of data\n",
    "atol_fixed = df_tol[df_tol['Absolute Tolerance'] == 1e-13].copy()\n",
    "rtol_fixed = df_tol[df_tol['Relative Tolerance'] == 1e-13].copy()\n",
    "\n",
    "fig, ax = plt.subplots(figsize=(12, 8))\n",
    "# Plot the series where Absolute Tolerance is fixed\n",
    "ax.scatter(\n",
    "    np.log10(atol_fixed['Relative Tolerance']),\n",
    "    atol_fixed['Error (m)'],\n",
    "    s=atol_fixed['Integration Steps'] * 0.5,  # Scale size for better visibility\n",
    "    label='Relative Tolerance',\n",
    "    alpha=0.7,\n",
    "    edgecolors='w',\n",
    "    linewidth=0.5\n",
    ")\n",
    "# Plot the series where Relative Tolerance is fixed\n",
    "ax.scatter(\n",
    "    np.log10(rtol_fixed['Absolute Tolerance']),\n",
    "    rtol_fixed['Error (m)'],\n",
    "    s=rtol_fixed['Integration Steps'] * 0.5,  # Scale size for better visibility\n",
    "    label='Absolute Tolerance',\n",
    "    alpha=0.7,\n",
    "    edgecolors='w',\n",
    "    linewidth=0.5\n",
    ")\n",
    "ax.set_xlabel('Log10(Tolerance)')\n",
    "ax.set_ylabel('Error (m)')\n",
    "ax.set_title('Error vs. Tolerance for LSODA (Point size reflects #/integration steps)')\n",
    "ax.legend()\n",
    "ax.grid(True, which='both', linestyle='--', linewidth=0.5)\n",
    "ax.set_yscale('log') # Use a log scale for error to see the full range\n",
    "# Add a legend for bubble sizes\n",
    "for steps in [100, 500, 1000]:\n",
    "    ax.scatter([], [], s=steps * 0.5, c='k', alpha=0.7, label=f'{steps} steps')\n",
    "ax.legend(title='Series / Bubble Size', scatterpoints=1, labelspacing=1.5, borderpad=2, loc='lower right')\n",
    "#plt.savefig('ErrorTolerance.svg', bbox_inches='tight')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "80797783",
   "metadata": {},
   "source": [
    "#### RK45"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "73b359e5",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style type=\"text/css\">\n",
       "</style>\n",
       "<table id=\"T_7e9d4\">\n",
       "  <thead>\n",
       "    <tr>\n",
       "      <th class=\"blank level0\" >&nbsp;</th>\n",
       "      <th id=\"T_7e9d4_level0_col0\" class=\"col_heading level0 col0\" >Method</th>\n",
       "      <th id=\"T_7e9d4_level0_col1\" class=\"col_heading level0 col1\" >Absolute Tolerance</th>\n",
       "      <th id=\"T_7e9d4_level0_col2\" class=\"col_heading level0 col2\" >Relative Tolerance</th>\n",
       "      <th id=\"T_7e9d4_level0_col3\" class=\"col_heading level0 col3\" >Error (m)</th>\n",
       "      <th id=\"T_7e9d4_level0_col4\" class=\"col_heading level0 col4\" >Integration Steps</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th id=\"T_7e9d4_level0_row0\" class=\"row_heading level0 row0\" >18</th>\n",
       "      <td id=\"T_7e9d4_row0_col0\" class=\"data row0 col0\" >RK45</td>\n",
       "      <td id=\"T_7e9d4_row0_col1\" class=\"data row0 col1\" >1e-13</td>\n",
       "      <td id=\"T_7e9d4_row0_col2\" class=\"data row0 col2\" >1e-02</td>\n",
       "      <td id=\"T_7e9d4_row0_col3\" class=\"data row0 col3\" >4.80902599635</td>\n",
       "      <td id=\"T_7e9d4_row0_col4\" class=\"data row0 col4\" >110</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_7e9d4_level0_row1\" class=\"row_heading level0 row1\" >19</th>\n",
       "      <td id=\"T_7e9d4_row1_col0\" class=\"data row1 col0\" >RK45</td>\n",
       "      <td id=\"T_7e9d4_row1_col1\" class=\"data row1 col1\" >1e-13</td>\n",
       "      <td id=\"T_7e9d4_row1_col2\" class=\"data row1 col2\" >1e-03</td>\n",
       "      <td id=\"T_7e9d4_row1_col3\" class=\"data row1 col3\" >1.42335689541</td>\n",
       "      <td id=\"T_7e9d4_row1_col4\" class=\"data row1 col4\" >116</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_7e9d4_level0_row2\" class=\"row_heading level0 row2\" >20</th>\n",
       "      <td id=\"T_7e9d4_row2_col0\" class=\"data row2 col0\" >RK45</td>\n",
       "      <td id=\"T_7e9d4_row2_col1\" class=\"data row2 col1\" >1e-13</td>\n",
       "      <td id=\"T_7e9d4_row2_col2\" class=\"data row2 col2\" >1e-04</td>\n",
       "      <td id=\"T_7e9d4_row2_col3\" class=\"data row2 col3\" >0.13834681911</td>\n",
       "      <td id=\"T_7e9d4_row2_col4\" class=\"data row2 col4\" >134</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_7e9d4_level0_row3\" class=\"row_heading level0 row3\" >21</th>\n",
       "      <td id=\"T_7e9d4_row3_col0\" class=\"data row3 col0\" >RK45</td>\n",
       "      <td id=\"T_7e9d4_row3_col1\" class=\"data row3 col1\" >1e-13</td>\n",
       "      <td id=\"T_7e9d4_row3_col2\" class=\"data row3 col2\" >1e-05</td>\n",
       "      <td id=\"T_7e9d4_row3_col3\" class=\"data row3 col3\" >0.05931755799</td>\n",
       "      <td id=\"T_7e9d4_row3_col4\" class=\"data row3 col4\" >170</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_7e9d4_level0_row4\" class=\"row_heading level0 row4\" >29</th>\n",
       "      <td id=\"T_7e9d4_row4_col0\" class=\"data row4 col0\" >RK45</td>\n",
       "      <td id=\"T_7e9d4_row4_col1\" class=\"data row4 col1\" >1e-03</td>\n",
       "      <td id=\"T_7e9d4_row4_col2\" class=\"data row4 col2\" >1e-13</td>\n",
       "      <td id=\"T_7e9d4_row4_col3\" class=\"data row4 col3\" >0.02401295393</td>\n",
       "      <td id=\"T_7e9d4_row4_col4\" class=\"data row4 col4\" >176</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_7e9d4_level0_row5\" class=\"row_heading level0 row5\" >28</th>\n",
       "      <td id=\"T_7e9d4_row5_col0\" class=\"data row5 col0\" >RK45</td>\n",
       "      <td id=\"T_7e9d4_row5_col1\" class=\"data row5 col1\" >1e-02</td>\n",
       "      <td id=\"T_7e9d4_row5_col2\" class=\"data row5 col2\" >1e-13</td>\n",
       "      <td id=\"T_7e9d4_row5_col3\" class=\"data row5 col3\" >0.02320768712</td>\n",
       "      <td id=\"T_7e9d4_row5_col4\" class=\"data row5 col4\" >110</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_7e9d4_level0_row6\" class=\"row_heading level0 row6\" >22</th>\n",
       "      <td id=\"T_7e9d4_row6_col0\" class=\"data row6 col0\" >RK45</td>\n",
       "      <td id=\"T_7e9d4_row6_col1\" class=\"data row6 col1\" >1e-13</td>\n",
       "      <td id=\"T_7e9d4_row6_col2\" class=\"data row6 col2\" >1e-06</td>\n",
       "      <td id=\"T_7e9d4_row6_col3\" class=\"data row6 col3\" >0.01041117103</td>\n",
       "      <td id=\"T_7e9d4_row6_col4\" class=\"data row6 col4\" >218</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_7e9d4_level0_row7\" class=\"row_heading level0 row7\" >23</th>\n",
       "      <td id=\"T_7e9d4_row7_col0\" class=\"data row7 col0\" >RK45</td>\n",
       "      <td id=\"T_7e9d4_row7_col1\" class=\"data row7 col1\" >1e-13</td>\n",
       "      <td id=\"T_7e9d4_row7_col2\" class=\"data row7 col2\" >1e-07</td>\n",
       "      <td id=\"T_7e9d4_row7_col3\" class=\"data row7 col3\" >0.00970797018</td>\n",
       "      <td id=\"T_7e9d4_row7_col4\" class=\"data row7 col4\" >428</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_7e9d4_level0_row8\" class=\"row_heading level0 row8\" >30</th>\n",
       "      <td id=\"T_7e9d4_row8_col0\" class=\"data row8 col0\" >RK45</td>\n",
       "      <td id=\"T_7e9d4_row8_col1\" class=\"data row8 col1\" >1e-04</td>\n",
       "      <td id=\"T_7e9d4_row8_col2\" class=\"data row8 col2\" >1e-13</td>\n",
       "      <td id=\"T_7e9d4_row8_col3\" class=\"data row8 col3\" >0.00335060298</td>\n",
       "      <td id=\"T_7e9d4_row8_col4\" class=\"data row8 col4\" >512</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_7e9d4_level0_row9\" class=\"row_heading level0 row9\" >24</th>\n",
       "      <td id=\"T_7e9d4_row9_col0\" class=\"data row9 col0\" >RK45</td>\n",
       "      <td id=\"T_7e9d4_row9_col1\" class=\"data row9 col1\" >1e-13</td>\n",
       "      <td id=\"T_7e9d4_row9_col2\" class=\"data row9 col2\" >1e-08</td>\n",
       "      <td id=\"T_7e9d4_row9_col3\" class=\"data row9 col3\" >0.00077076278</td>\n",
       "      <td id=\"T_7e9d4_row9_col4\" class=\"data row9 col4\" >1364</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_7e9d4_level0_row10\" class=\"row_heading level0 row10\" >25</th>\n",
       "      <td id=\"T_7e9d4_row10_col0\" class=\"data row10 col0\" >RK45</td>\n",
       "      <td id=\"T_7e9d4_row10_col1\" class=\"data row10 col1\" >1e-13</td>\n",
       "      <td id=\"T_7e9d4_row10_col2\" class=\"data row10 col2\" >1e-09</td>\n",
       "      <td id=\"T_7e9d4_row10_col3\" class=\"data row10 col3\" >0.00017411794</td>\n",
       "      <td id=\"T_7e9d4_row10_col4\" class=\"data row10 col4\" >2960</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_7e9d4_level0_row11\" class=\"row_heading level0 row11\" >31</th>\n",
       "      <td id=\"T_7e9d4_row11_col0\" class=\"data row11 col0\" >RK45</td>\n",
       "      <td id=\"T_7e9d4_row11_col1\" class=\"data row11 col1\" >1e-05</td>\n",
       "      <td id=\"T_7e9d4_row11_col2\" class=\"data row11 col2\" >1e-13</td>\n",
       "      <td id=\"T_7e9d4_row11_col3\" class=\"data row11 col3\" >0.00016674269</td>\n",
       "      <td id=\"T_7e9d4_row11_col4\" class=\"data row11 col4\" >1460</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_7e9d4_level0_row12\" class=\"row_heading level0 row12\" >32</th>\n",
       "      <td id=\"T_7e9d4_row12_col0\" class=\"data row12 col0\" >RK45</td>\n",
       "      <td id=\"T_7e9d4_row12_col1\" class=\"data row12 col1\" >1e-06</td>\n",
       "      <td id=\"T_7e9d4_row12_col2\" class=\"data row12 col2\" >1e-13</td>\n",
       "      <td id=\"T_7e9d4_row12_col3\" class=\"data row12 col3\" >0.00002848544</td>\n",
       "      <td id=\"T_7e9d4_row12_col4\" class=\"data row12 col4\" >2978</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_7e9d4_level0_row13\" class=\"row_heading level0 row13\" >33</th>\n",
       "      <td id=\"T_7e9d4_row13_col0\" class=\"data row13 col0\" >RK45</td>\n",
       "      <td id=\"T_7e9d4_row13_col1\" class=\"data row13 col1\" >1e-07</td>\n",
       "      <td id=\"T_7e9d4_row13_col2\" class=\"data row13 col2\" >1e-13</td>\n",
       "      <td id=\"T_7e9d4_row13_col3\" class=\"data row13 col3\" >0.00000990008</td>\n",
       "      <td id=\"T_7e9d4_row13_col4\" class=\"data row13 col4\" >5144</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_7e9d4_level0_row14\" class=\"row_heading level0 row14\" >26</th>\n",
       "      <td id=\"T_7e9d4_row14_col0\" class=\"data row14 col0\" >RK45</td>\n",
       "      <td id=\"T_7e9d4_row14_col1\" class=\"data row14 col1\" >1e-13</td>\n",
       "      <td id=\"T_7e9d4_row14_col2\" class=\"data row14 col2\" >1e-10</td>\n",
       "      <td id=\"T_7e9d4_row14_col3\" class=\"data row14 col3\" >0.00000145266</td>\n",
       "      <td id=\"T_7e9d4_row14_col4\" class=\"data row14 col4\" >5084</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_7e9d4_level0_row15\" class=\"row_heading level0 row15\" >34</th>\n",
       "      <td id=\"T_7e9d4_row15_col0\" class=\"data row15 col0\" >RK45</td>\n",
       "      <td id=\"T_7e9d4_row15_col1\" class=\"data row15 col1\" >1e-08</td>\n",
       "      <td id=\"T_7e9d4_row15_col2\" class=\"data row15 col2\" >1e-13</td>\n",
       "      <td id=\"T_7e9d4_row15_col3\" class=\"data row15 col3\" >0.00000067744</td>\n",
       "      <td id=\"T_7e9d4_row15_col4\" class=\"data row15 col4\" >7028</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_7e9d4_level0_row16\" class=\"row_heading level0 row16\" >35</th>\n",
       "      <td id=\"T_7e9d4_row16_col0\" class=\"data row16 col0\" >RK45</td>\n",
       "      <td id=\"T_7e9d4_row16_col1\" class=\"data row16 col1\" >1e-09</td>\n",
       "      <td id=\"T_7e9d4_row16_col2\" class=\"data row16 col2\" >1e-13</td>\n",
       "      <td id=\"T_7e9d4_row16_col3\" class=\"data row16 col3\" >0.00000010331</td>\n",
       "      <td id=\"T_7e9d4_row16_col4\" class=\"data row16 col4\" >8972</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_7e9d4_level0_row17\" class=\"row_heading level0 row17\" >27</th>\n",
       "      <td id=\"T_7e9d4_row17_col0\" class=\"data row17 col0\" >RK45</td>\n",
       "      <td id=\"T_7e9d4_row17_col1\" class=\"data row17 col1\" >1e-13</td>\n",
       "      <td id=\"T_7e9d4_row17_col2\" class=\"data row17 col2\" >1e-11</td>\n",
       "      <td id=\"T_7e9d4_row17_col3\" class=\"data row17 col3\" >0.00000007490</td>\n",
       "      <td id=\"T_7e9d4_row17_col4\" class=\"data row17 col4\" >7022</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_7e9d4_level0_row18\" class=\"row_heading level0 row18\" >36</th>\n",
       "      <td id=\"T_7e9d4_row18_col0\" class=\"data row18 col0\" >RK45</td>\n",
       "      <td id=\"T_7e9d4_row18_col1\" class=\"data row18 col1\" >1e-10</td>\n",
       "      <td id=\"T_7e9d4_row18_col2\" class=\"data row18 col2\" >1e-13</td>\n",
       "      <td id=\"T_7e9d4_row18_col3\" class=\"data row18 col3\" >0.00000001004</td>\n",
       "      <td id=\"T_7e9d4_row18_col4\" class=\"data row18 col4\" >10184</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_7e9d4_level0_row19\" class=\"row_heading level0 row19\" >37</th>\n",
       "      <td id=\"T_7e9d4_row19_col0\" class=\"data row19 col0\" >RK45</td>\n",
       "      <td id=\"T_7e9d4_row19_col1\" class=\"data row19 col1\" >1e-11</td>\n",
       "      <td id=\"T_7e9d4_row19_col2\" class=\"data row19 col2\" >1e-13</td>\n",
       "      <td id=\"T_7e9d4_row19_col3\" class=\"data row19 col3\" >0.00000000722</td>\n",
       "      <td id=\"T_7e9d4_row19_col4\" class=\"data row19 col4\" >10016</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n"
      ],
      "text/plain": [
       "<pandas.io.formats.style.Styler at 0x2c32e4091f0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "method = 'RK45'\n",
    "# Run with rtol as limiting factor\n",
    "rtol = 1e-1\n",
    "atol = 1e-13\n",
    "while rtol > 1e-10:\n",
    "    rtol /= 10.0\n",
    "    try:\n",
    "        err, count = scipy_chk(integration_method=method, relative_tolerance=rtol, absolute_tolerance=atol)\n",
    "        tol_tests.append((method, atol, rtol, err, count))\n",
    "    except ArithmeticError as e:\n",
    "        print(f\"Failed to reach distance with {rtol=}\")\n",
    "        continue\n",
    "# Run with atol as limiting factor\n",
    "atol = 1e-1\n",
    "rtol = 1e-13\n",
    "while atol > 1e-10:\n",
    "    atol /= 10.0\n",
    "    try:\n",
    "        err, count = scipy_chk(integration_method=method, relative_tolerance=rtol, absolute_tolerance=atol)\n",
    "        tol_tests.append((method, atol, rtol, err, count))\n",
    "    except ArithmeticError as e:\n",
    "        print(f\"Failed to reach distance with {atol=}\")\n",
    "        continue\n",
    "df_tol = pd.DataFrame(tol_tests, columns=['Method', 'Absolute Tolerance', 'Relative Tolerance', 'Error (m)', 'Integration Steps']).sort_values(by='Error (m)', ascending=False)\n",
    "display(df_tol[df_tol['Method'] == 'RK45'].sort_values(by='Error (m)', ascending=False)\n",
    "      .style.format({'Absolute Tolerance': '{:.0e}', 'Relative Tolerance': '{:.0e}', 'Error (m)': '{:.11f}'}))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "619dc278",
   "metadata": {},
   "source": [
    "#### Chart"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "a0dc8bb4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 136396 characters]",
      "text/plain": [
       "<Figure size 1200x800 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "fig, ax = plt.subplots(figsize=(12, 8))\n",
    "for method in ['LSODA', 'RK45']:\n",
    "    atol_fixed = df_tol[(df_tol.Method == method) & (df_tol['Absolute Tolerance'] == 1e-13)].copy()\n",
    "    rtol_fixed = df_tol[(df_tol.Method == method) & (df_tol['Relative Tolerance'] == 1e-13)].copy()\n",
    "    # Plot the series where Absolute Tolerance is fixed\n",
    "    ax.scatter(\n",
    "        np.log10(atol_fixed['Relative Tolerance']),\n",
    "        atol_fixed['Error (m)'],\n",
    "        s=atol_fixed['Integration Steps'] * 0.5,  # Scale size for better visibility\n",
    "        label=method + ' Relative Tolerance',\n",
    "        alpha=0.7,\n",
    "        edgecolors='w',\n",
    "        linewidth=0.5\n",
    "    )\n",
    "    # Plot the series where Relative Tolerance is fixed\n",
    "    ax.scatter(\n",
    "        np.log10(rtol_fixed['Absolute Tolerance']),\n",
    "        rtol_fixed['Error (m)'],\n",
    "        s=rtol_fixed['Integration Steps'] * 0.5,  # Scale size for better visibility\n",
    "        label=method + ' Absolute Tolerance',\n",
    "        alpha=0.7,\n",
    "        edgecolors='w',\n",
    "        linewidth=0.5\n",
    "    )\n",
    "ax.set_xlabel('Log10(Tolerance)')\n",
    "ax.set_ylabel('Error (m)')\n",
    "ax.set_title('Error vs. Tolerance (Point size reflects #/integration steps)')\n",
    "ax.legend()\n",
    "ax.grid(True, which='both', linestyle='--', linewidth=0.5)\n",
    "ax.set_yscale('log') # Use a log scale for error to see the full range\n",
    "# Add a legend for bubble sizes\n",
    "for steps in [100, 500, 1000]:\n",
    "    ax.scatter([], [], s=steps * 0.5, c='k', alpha=0.7, label=f'{steps} steps')\n",
    "ax.legend(title='Series / Bubble Size', scatterpoints=1, labelspacing=1.5, borderpad=2)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ae19ed81",
   "metadata": {},
   "source": [
    "### Chosen Methods\n",
    "\n",
    "For purposes of charting against the non-SciPy methods, we'll vary these two SciPy methods over a range of tolerance settings that give monotonically increasing error with speed."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "3ec70a17",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "299 ms ± 121 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
      "199 ms ± 8.46 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
      "169 ms ± 1.58 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
      "136 ms ± 4.34 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
      "89.4 ms ± 23.3 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
      "38.6 ms ± 1.2 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
      "14.4 ms ± 343 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n",
      "5.63 ms ± 50.6 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n",
      "4.62 ms ± 1.47 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)\n",
      "2.7 ms ± 35.7 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<style type=\"text/css\">\n",
       "</style>\n",
       "<table id=\"T_31c18\">\n",
       "  <thead>\n",
       "    <tr>\n",
       "      <th class=\"blank level0\" >&nbsp;</th>\n",
       "      <th id=\"T_31c18_level0_col0\" class=\"col_heading level0 col0\" >Engine</th>\n",
       "      <th id=\"T_31c18_level0_col1\" class=\"col_heading level0 col1\" >atol</th>\n",
       "      <th id=\"T_31c18_level0_col2\" class=\"col_heading level0 col2\" >Error (m)</th>\n",
       "      <th id=\"T_31c18_level0_col3\" class=\"col_heading level0 col3\" >Integration Steps</th>\n",
       "      <th id=\"T_31c18_level0_col4\" class=\"col_heading level0 col4\" >Speed (s)</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th id=\"T_31c18_level0_row0\" class=\"row_heading level0 row0\" >0</th>\n",
       "      <td id=\"T_31c18_row0_col0\" class=\"data row0 col0\" >SciPy RK45</td>\n",
       "      <td id=\"T_31c18_row0_col1\" class=\"data row0 col1\" >1e-10</td>\n",
       "      <td id=\"T_31c18_row0_col2\" class=\"data row0 col2\" >0.0000000151</td>\n",
       "      <td id=\"T_31c18_row0_col3\" class=\"data row0 col3\" >8702</td>\n",
       "      <td id=\"T_31c18_row0_col4\" class=\"data row0 col4\" >0.299046</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_31c18_level0_row1\" class=\"row_heading level0 row1\" >1</th>\n",
       "      <td id=\"T_31c18_row1_col0\" class=\"data row1 col0\" >SciPy RK45</td>\n",
       "      <td id=\"T_31c18_row1_col1\" class=\"data row1 col1\" >1e-09</td>\n",
       "      <td id=\"T_31c18_row1_col2\" class=\"data row1 col2\" >0.0000001255</td>\n",
       "      <td id=\"T_31c18_row1_col3\" class=\"data row1 col3\" >7520</td>\n",
       "      <td id=\"T_31c18_row1_col4\" class=\"data row1 col4\" >0.198652</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_31c18_level0_row2\" class=\"row_heading level0 row2\" >2</th>\n",
       "      <td id=\"T_31c18_row2_col0\" class=\"data row2 col0\" >SciPy RK45</td>\n",
       "      <td id=\"T_31c18_row2_col1\" class=\"data row2 col1\" >1e-08</td>\n",
       "      <td id=\"T_31c18_row2_col2\" class=\"data row2 col2\" >0.0000002763</td>\n",
       "      <td id=\"T_31c18_row2_col3\" class=\"data row2 col3\" >6722</td>\n",
       "      <td id=\"T_31c18_row2_col4\" class=\"data row2 col4\" >0.169317</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_31c18_level0_row3\" class=\"row_heading level0 row3\" >3</th>\n",
       "      <td id=\"T_31c18_row3_col0\" class=\"data row3 col0\" >SciPy RK45</td>\n",
       "      <td id=\"T_31c18_row3_col1\" class=\"data row3 col1\" >1e-07</td>\n",
       "      <td id=\"T_31c18_row3_col2\" class=\"data row3 col2\" >0.0000020576</td>\n",
       "      <td id=\"T_31c18_row3_col3\" class=\"data row3 col3\" >5156</td>\n",
       "      <td id=\"T_31c18_row3_col4\" class=\"data row3 col4\" >0.135883</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_31c18_level0_row4\" class=\"row_heading level0 row4\" >4</th>\n",
       "      <td id=\"T_31c18_row4_col0\" class=\"data row4 col0\" >SciPy RK45</td>\n",
       "      <td id=\"T_31c18_row4_col1\" class=\"data row4 col1\" >1e-06</td>\n",
       "      <td id=\"T_31c18_row4_col2\" class=\"data row4 col2\" >0.0001278431</td>\n",
       "      <td id=\"T_31c18_row4_col3\" class=\"data row4 col3\" >3080</td>\n",
       "      <td id=\"T_31c18_row4_col4\" class=\"data row4 col4\" >0.089423</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_31c18_level0_row5\" class=\"row_heading level0 row5\" >5</th>\n",
       "      <td id=\"T_31c18_row5_col0\" class=\"data row5 col0\" >SciPy RK45</td>\n",
       "      <td id=\"T_31c18_row5_col1\" class=\"data row5 col1\" >1e-05</td>\n",
       "      <td id=\"T_31c18_row5_col2\" class=\"data row5 col2\" >0.0001206575</td>\n",
       "      <td id=\"T_31c18_row5_col3\" class=\"data row5 col3\" >1448</td>\n",
       "      <td id=\"T_31c18_row5_col4\" class=\"data row5 col4\" >0.038640</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_31c18_level0_row6\" class=\"row_heading level0 row6\" >6</th>\n",
       "      <td id=\"T_31c18_row6_col0\" class=\"data row6 col0\" >SciPy RK45</td>\n",
       "      <td id=\"T_31c18_row6_col1\" class=\"data row6 col1\" >1e-04</td>\n",
       "      <td id=\"T_31c18_row6_col2\" class=\"data row6 col2\" >0.0033475213</td>\n",
       "      <td id=\"T_31c18_row6_col3\" class=\"data row6 col3\" >512</td>\n",
       "      <td id=\"T_31c18_row6_col4\" class=\"data row6 col4\" >0.014426</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_31c18_level0_row7\" class=\"row_heading level0 row7\" >7</th>\n",
       "      <td id=\"T_31c18_row7_col0\" class=\"data row7 col0\" >SciPy RK45</td>\n",
       "      <td id=\"T_31c18_row7_col1\" class=\"data row7 col1\" >1e-03</td>\n",
       "      <td id=\"T_31c18_row7_col2\" class=\"data row7 col2\" >0.0240124628</td>\n",
       "      <td id=\"T_31c18_row7_col3\" class=\"data row7 col3\" >176</td>\n",
       "      <td id=\"T_31c18_row7_col4\" class=\"data row7 col4\" >0.005635</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_31c18_level0_row8\" class=\"row_heading level0 row8\" >8</th>\n",
       "      <td id=\"T_31c18_row8_col0\" class=\"data row8 col0\" >SciPy RK45</td>\n",
       "      <td id=\"T_31c18_row8_col1\" class=\"data row8 col1\" >1e-02</td>\n",
       "      <td id=\"T_31c18_row8_col2\" class=\"data row8 col2\" >0.0232075855</td>\n",
       "      <td id=\"T_31c18_row8_col3\" class=\"data row8 col3\" >110</td>\n",
       "      <td id=\"T_31c18_row8_col4\" class=\"data row8 col4\" >0.004622</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_31c18_level0_row9\" class=\"row_heading level0 row9\" >9</th>\n",
       "      <td id=\"T_31c18_row9_col0\" class=\"data row9 col0\" >SciPy RK45</td>\n",
       "      <td id=\"T_31c18_row9_col1\" class=\"data row9 col1\" >1e-01</td>\n",
       "      <td id=\"T_31c18_row9_col2\" class=\"data row9 col2\" >0.0297321427</td>\n",
       "      <td id=\"T_31c18_row9_col3\" class=\"data row9 col3\" >68</td>\n",
       "      <td id=\"T_31c18_row9_col4\" class=\"data row9 col4\" >0.002697</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n"
      ],
      "text/plain": [
       "<pandas.io.formats.style.Styler at 0x2c330ff91f0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "method = 'RK45'\n",
    "atol = 1e-10\n",
    "while atol < 1.0:\n",
    "    err, count, speed = scipy_chk(timeit=True, integration_method=method, absolute_tolerance=atol, relative_tolerance=1e-12)\n",
    "    summary.append(('SciPy '+method, atol, err, count, speed))\n",
    "    atol *= 10.0\n",
    "df = pd.DataFrame(summary, columns=['Engine', 'atol', 'Error (m)', 'Integration Steps', 'Speed (s)'])\n",
    "display(df[df.Engine == 'SciPy '+method].style.format({'atol': '{:.0e}', 'Error (m)': '{:.10f}'}))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "ba0417ec",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "192 ms ± 995 μs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
      "192 ms ± 13.3 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
      "163 ms ± 2.79 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
      "128 ms ± 673 μs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
      "106 ms ± 17.7 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
      "66.3 ms ± 882 μs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
      "43.3 ms ± 13.9 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
      "16.8 ms ± 1.34 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)\n",
      "8.78 ms ± 1.64 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)\n",
      "3.88 ms ± 87.8 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<style type=\"text/css\">\n",
       "</style>\n",
       "<table id=\"T_a2728\">\n",
       "  <thead>\n",
       "    <tr>\n",
       "      <th class=\"blank level0\" >&nbsp;</th>\n",
       "      <th id=\"T_a2728_level0_col0\" class=\"col_heading level0 col0\" >Engine</th>\n",
       "      <th id=\"T_a2728_level0_col1\" class=\"col_heading level0 col1\" >atol</th>\n",
       "      <th id=\"T_a2728_level0_col2\" class=\"col_heading level0 col2\" >Error (m)</th>\n",
       "      <th id=\"T_a2728_level0_col3\" class=\"col_heading level0 col3\" >Integration Steps</th>\n",
       "      <th id=\"T_a2728_level0_col4\" class=\"col_heading level0 col4\" >Speed (s)</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th id=\"T_a2728_level0_row0\" class=\"row_heading level0 row0\" >10</th>\n",
       "      <td id=\"T_a2728_row0_col0\" class=\"data row0 col0\" >SciPy LSODA</td>\n",
       "      <td id=\"T_a2728_row0_col1\" class=\"data row0 col1\" >1e-10</td>\n",
       "      <td id=\"T_a2728_row0_col2\" class=\"data row0 col2\" >0.0000000019</td>\n",
       "      <td id=\"T_a2728_row0_col3\" class=\"data row0 col3\" >4836</td>\n",
       "      <td id=\"T_a2728_row0_col4\" class=\"data row0 col4\" >0.191704</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_a2728_level0_row1\" class=\"row_heading level0 row1\" >11</th>\n",
       "      <td id=\"T_a2728_row1_col0\" class=\"data row1 col0\" >SciPy LSODA</td>\n",
       "      <td id=\"T_a2728_row1_col1\" class=\"data row1 col1\" >1e-09</td>\n",
       "      <td id=\"T_a2728_row1_col2\" class=\"data row1 col2\" >0.0000000029</td>\n",
       "      <td id=\"T_a2728_row1_col3\" class=\"data row1 col3\" >4631</td>\n",
       "      <td id=\"T_a2728_row1_col4\" class=\"data row1 col4\" >0.192327</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_a2728_level0_row2\" class=\"row_heading level0 row2\" >12</th>\n",
       "      <td id=\"T_a2728_row2_col0\" class=\"data row2 col0\" >SciPy LSODA</td>\n",
       "      <td id=\"T_a2728_row2_col1\" class=\"data row2 col1\" >1e-08</td>\n",
       "      <td id=\"T_a2728_row2_col2\" class=\"data row2 col2\" >0.0000000075</td>\n",
       "      <td id=\"T_a2728_row2_col3\" class=\"data row2 col3\" >4179</td>\n",
       "      <td id=\"T_a2728_row2_col4\" class=\"data row2 col4\" >0.162791</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_a2728_level0_row3\" class=\"row_heading level0 row3\" >13</th>\n",
       "      <td id=\"T_a2728_row3_col0\" class=\"data row3 col0\" >SciPy LSODA</td>\n",
       "      <td id=\"T_a2728_row3_col1\" class=\"data row3 col1\" >1e-07</td>\n",
       "      <td id=\"T_a2728_row3_col2\" class=\"data row3 col2\" >0.0000000195</td>\n",
       "      <td id=\"T_a2728_row3_col3\" class=\"data row3 col3\" >3492</td>\n",
       "      <td id=\"T_a2728_row3_col4\" class=\"data row3 col4\" >0.128291</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_a2728_level0_row4\" class=\"row_heading level0 row4\" >14</th>\n",
       "      <td id=\"T_a2728_row4_col0\" class=\"data row4 col0\" >SciPy LSODA</td>\n",
       "      <td id=\"T_a2728_row4_col1\" class=\"data row4 col1\" >1e-06</td>\n",
       "      <td id=\"T_a2728_row4_col2\" class=\"data row4 col2\" >0.0000005716</td>\n",
       "      <td id=\"T_a2728_row4_col3\" class=\"data row4 col3\" >2825</td>\n",
       "      <td id=\"T_a2728_row4_col4\" class=\"data row4 col4\" >0.106481</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_a2728_level0_row5\" class=\"row_heading level0 row5\" >15</th>\n",
       "      <td id=\"T_a2728_row5_col0\" class=\"data row5 col0\" >SciPy LSODA</td>\n",
       "      <td id=\"T_a2728_row5_col1\" class=\"data row5 col1\" >1e-05</td>\n",
       "      <td id=\"T_a2728_row5_col2\" class=\"data row5 col2\" >0.0000220671</td>\n",
       "      <td id=\"T_a2728_row5_col3\" class=\"data row5 col3\" >1906</td>\n",
       "      <td id=\"T_a2728_row5_col4\" class=\"data row5 col4\" >0.066297</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_a2728_level0_row6\" class=\"row_heading level0 row6\" >16</th>\n",
       "      <td id=\"T_a2728_row6_col0\" class=\"data row6 col0\" >SciPy LSODA</td>\n",
       "      <td id=\"T_a2728_row6_col1\" class=\"data row6 col1\" >1e-04</td>\n",
       "      <td id=\"T_a2728_row6_col2\" class=\"data row6 col2\" >0.0002787119</td>\n",
       "      <td id=\"T_a2728_row6_col3\" class=\"data row6 col3\" >992</td>\n",
       "      <td id=\"T_a2728_row6_col4\" class=\"data row6 col4\" >0.043315</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_a2728_level0_row7\" class=\"row_heading level0 row7\" >17</th>\n",
       "      <td id=\"T_a2728_row7_col0\" class=\"data row7 col0\" >SciPy LSODA</td>\n",
       "      <td id=\"T_a2728_row7_col1\" class=\"data row7 col1\" >1e-03</td>\n",
       "      <td id=\"T_a2728_row7_col2\" class=\"data row7 col2\" >0.0031272629</td>\n",
       "      <td id=\"T_a2728_row7_col3\" class=\"data row7 col3\" >425</td>\n",
       "      <td id=\"T_a2728_row7_col4\" class=\"data row7 col4\" >0.016802</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_a2728_level0_row8\" class=\"row_heading level0 row8\" >18</th>\n",
       "      <td id=\"T_a2728_row8_col0\" class=\"data row8 col0\" >SciPy LSODA</td>\n",
       "      <td id=\"T_a2728_row8_col1\" class=\"data row8 col1\" >1e-02</td>\n",
       "      <td id=\"T_a2728_row8_col2\" class=\"data row8 col2\" >0.0018361749</td>\n",
       "      <td id=\"T_a2728_row8_col3\" class=\"data row8 col3\" >174</td>\n",
       "      <td id=\"T_a2728_row8_col4\" class=\"data row8 col4\" >0.008785</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_a2728_level0_row9\" class=\"row_heading level0 row9\" >19</th>\n",
       "      <td id=\"T_a2728_row9_col0\" class=\"data row9 col0\" >SciPy LSODA</td>\n",
       "      <td id=\"T_a2728_row9_col1\" class=\"data row9 col1\" >1e-01</td>\n",
       "      <td id=\"T_a2728_row9_col2\" class=\"data row9 col2\" >0.0171775568</td>\n",
       "      <td id=\"T_a2728_row9_col3\" class=\"data row9 col3\" >82</td>\n",
       "      <td id=\"T_a2728_row9_col4\" class=\"data row9 col4\" >0.003883</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n"
      ],
      "text/plain": [
       "<pandas.io.formats.style.Styler at 0x2c330836e10>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "method = 'LSODA'\n",
    "atol = 1e-10\n",
    "while atol < 1.0:\n",
    "    err, count, speed = scipy_chk(timeit=True, integration_method=method, absolute_tolerance=atol, relative_tolerance=1e-12)\n",
    "    summary.append(('SciPy '+method, atol, err, count, speed))\n",
    "    atol *= 10.0\n",
    "df = pd.DataFrame(summary, columns=['Engine', 'atol', 'Error (m)', 'Integration Steps', 'Speed (s)'])\n",
    "display(df[df.Engine == 'SciPy '+method].style.format({'atol': '{:.0e}', 'Error (m)': '{:.10f}'}))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "191ea610",
   "metadata": {},
   "source": [
    "## Other engines"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "d401998f",
   "metadata": {},
   "outputs": [],
   "source": [
    "def chk_engine(engine_name: str, timeit: bool = False, step_multiplier: float = 1.0):\n",
    "    config = BaseEngineConfigDict(\n",
    "        cStepMultiplier=step_multiplier,\n",
    "    )\n",
    "    calc = Calculator(config, engine=engine_name)\n",
    "    hit, e = must_fire(calc, shot=baseline_shot, trajectory_range=range, trajectory_step=reference_distance)\n",
    "    err = check_error(hit, False)\n",
    "    evals = calc.integration_step_count\n",
    "    if timeit:\n",
    "        speed = %timeit -o must_fire(calc, shot=baseline_shot, trajectory_range=range, trajectory_step=reference_distance)\n",
    "        return err, evals, speed.average\n",
    "    return err, evals"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "87a2d42a",
   "metadata": {},
   "source": [
    "## RK4\n",
    "\n",
    "The Runge-Kutta 4th-order solver essentially evaluates 4 points per integration step.  To facilitate comparison with the other engines we'll set its base time step to 1ms."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "4c436f50",
   "metadata": {},
   "outputs": [],
   "source": [
    "from py_ballisticcalc.engines import RK4IntegrationEngine\n",
    "rk_time_step = RK4IntegrationEngine.DEFAULT_TIME_STEP = 0.001"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "fa30594c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10.9 s ± 273 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
      "2.15 s ± 62.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
      "537 ms ± 172 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
      "90 ms ± 2.49 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
      "17.3 ms ± 483 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n",
      "3.63 ms ± 110 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n",
      "980 μs ± 234 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Engine</th>\n",
       "      <th>Step (s)</th>\n",
       "      <th>Error (m)</th>\n",
       "      <th>Integration Steps</th>\n",
       "      <th>Speed (s)</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>20</th>\n",
       "      <td>RK4</td>\n",
       "      <td>0.00004</td>\n",
       "      <td>0.000123</td>\n",
       "      <td>347848</td>\n",
       "      <td>10.854290</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>21</th>\n",
       "      <td>RK4</td>\n",
       "      <td>0.00020</td>\n",
       "      <td>0.000605</td>\n",
       "      <td>69570</td>\n",
       "      <td>2.152400</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>22</th>\n",
       "      <td>RK4</td>\n",
       "      <td>0.00100</td>\n",
       "      <td>0.002704</td>\n",
       "      <td>13914</td>\n",
       "      <td>0.536611</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>23</th>\n",
       "      <td>RK4</td>\n",
       "      <td>0.00500</td>\n",
       "      <td>0.014660</td>\n",
       "      <td>2783</td>\n",
       "      <td>0.090032</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>24</th>\n",
       "      <td>RK4</td>\n",
       "      <td>0.02500</td>\n",
       "      <td>0.067012</td>\n",
       "      <td>557</td>\n",
       "      <td>0.017320</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>25</th>\n",
       "      <td>RK4</td>\n",
       "      <td>0.12500</td>\n",
       "      <td>0.346481</td>\n",
       "      <td>112</td>\n",
       "      <td>0.003627</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>26</th>\n",
       "      <td>RK4</td>\n",
       "      <td>0.62500</td>\n",
       "      <td>2.987859</td>\n",
       "      <td>23</td>\n",
       "      <td>0.000980</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   Engine  Step (s)  Error (m)  Integration Steps  Speed (s)\n",
       "20    RK4   0.00004   0.000123             347848  10.854290\n",
       "21    RK4   0.00020   0.000605              69570   2.152400\n",
       "22    RK4   0.00100   0.002704              13914   0.536611\n",
       "23    RK4   0.00500   0.014660               2783   0.090032\n",
       "24    RK4   0.02500   0.067012                557   0.017320\n",
       "25    RK4   0.12500   0.346481                112   0.003627\n",
       "26    RK4   0.62500   2.987859                 23   0.000980"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "step = 5.0\n",
    "multiplier = 1.0/step**2\n",
    "while multiplier <= 1000.0:\n",
    "    err, count, speed = chk_engine('rk4_engine', timeit=True, step_multiplier=multiplier)\n",
    "    summary.append(('RK4', multiplier*rk_time_step, err, count, speed))\n",
    "    multiplier *= step\n",
    "df = pd.DataFrame(summary, columns=['Engine', 'Step (s)', 'Error (m)', 'Integration Steps', 'Speed (s)'])\n",
    "df[df.Engine == 'RK4']"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "985e3d94",
   "metadata": {},
   "source": [
    "## Verlet\n",
    "\n",
    "Our velocity Verlet solver has a base time step of 1ms:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "e5e161cd",
   "metadata": {},
   "outputs": [],
   "source": [
    "from py_ballisticcalc.engines import VelocityVerletIntegrationEngine\n",
    "verlet_time_step = VelocityVerletIntegrationEngine.DEFAULT_TIME_STEP"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "bcbfd819",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4.47 s ± 102 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
      "879 ms ± 57.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
      "175 ms ± 1.71 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
      "43.7 ms ± 16 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
      "7.51 ms ± 529 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n",
      "1.56 ms ± 23.3 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Engine</th>\n",
       "      <th>Step (s)</th>\n",
       "      <th>Error (m)</th>\n",
       "      <th>Integration Steps</th>\n",
       "      <th>Speed (s)</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>27</th>\n",
       "      <td>Verlet</td>\n",
       "      <td>0.00004</td>\n",
       "      <td>0.005741</td>\n",
       "      <td>347846</td>\n",
       "      <td>4.467510</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>28</th>\n",
       "      <td>Verlet</td>\n",
       "      <td>0.00020</td>\n",
       "      <td>0.028685</td>\n",
       "      <td>69568</td>\n",
       "      <td>0.878940</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>29</th>\n",
       "      <td>Verlet</td>\n",
       "      <td>0.00100</td>\n",
       "      <td>0.143862</td>\n",
       "      <td>13912</td>\n",
       "      <td>0.174871</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>30</th>\n",
       "      <td>Verlet</td>\n",
       "      <td>0.00500</td>\n",
       "      <td>0.720899</td>\n",
       "      <td>2781</td>\n",
       "      <td>0.043736</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>31</th>\n",
       "      <td>Verlet</td>\n",
       "      <td>0.02500</td>\n",
       "      <td>3.698342</td>\n",
       "      <td>555</td>\n",
       "      <td>0.007510</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>32</th>\n",
       "      <td>Verlet</td>\n",
       "      <td>0.12500</td>\n",
       "      <td>21.619532</td>\n",
       "      <td>109</td>\n",
       "      <td>0.001565</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "    Engine  Step (s)  Error (m)  Integration Steps  Speed (s)\n",
       "27  Verlet   0.00004   0.005741             347846   4.467510\n",
       "28  Verlet   0.00020   0.028685              69568   0.878940\n",
       "29  Verlet   0.00100   0.143862              13912   0.174871\n",
       "30  Verlet   0.00500   0.720899               2781   0.043736\n",
       "31  Verlet   0.02500   3.698342                555   0.007510\n",
       "32  Verlet   0.12500  21.619532                109   0.001565"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "step = 5.0\n",
    "multiplier = 1.0/step**2\n",
    "while multiplier <= 500.0:\n",
    "    err, count, speed = chk_engine('verlet_engine', timeit=True, step_multiplier=multiplier)\n",
    "    summary.append(('Verlet', multiplier*verlet_time_step, err, count, speed))\n",
    "    multiplier *= step\n",
    "df = pd.DataFrame(summary, columns=['Engine', 'Step (s)', 'Error (m)', 'Integration Steps', 'Speed (s)'])\n",
    "df[df.Engine == 'Verlet']"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "00f38cea",
   "metadata": {},
   "source": [
    "## Euler\n",
    "\n",
    "Our Euler integrator uses a base \"step\" of 0.5ft, which it then converts to a time step by dividing by `max(1.0, velocity)` (where velocity is in fps).  This makes the algorithm to take smaller steps when things are changing more quickly, which should produce lower error for the same total number of steps than it would if step size were held constant."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "c1ee0e69",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4.93 s ± 94.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
      "977 ms ± 8.94 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
      "290 ms ± 110 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
      "39.7 ms ± 892 μs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
      "9.26 ms ± 2.66 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)\n",
      "1.77 ms ± 33.7 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)\n",
      "609 μs ± 201 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Engine</th>\n",
       "      <th>Multiplier</th>\n",
       "      <th>Error (m)</th>\n",
       "      <th>Integration Steps</th>\n",
       "      <th>Speed (s)</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>33</th>\n",
       "      <td>Euler</td>\n",
       "      <td>0.04</td>\n",
       "      <td>0.002065</td>\n",
       "      <td>545834</td>\n",
       "      <td>4.931240</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>34</th>\n",
       "      <td>Euler</td>\n",
       "      <td>0.20</td>\n",
       "      <td>0.010319</td>\n",
       "      <td>109166</td>\n",
       "      <td>0.976583</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>35</th>\n",
       "      <td>Euler</td>\n",
       "      <td>1.00</td>\n",
       "      <td>0.051491</td>\n",
       "      <td>21833</td>\n",
       "      <td>0.289522</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>36</th>\n",
       "      <td>Euler</td>\n",
       "      <td>5.00</td>\n",
       "      <td>0.258586</td>\n",
       "      <td>4366</td>\n",
       "      <td>0.039727</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>37</th>\n",
       "      <td>Euler</td>\n",
       "      <td>25.00</td>\n",
       "      <td>1.296182</td>\n",
       "      <td>873</td>\n",
       "      <td>0.009258</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>38</th>\n",
       "      <td>Euler</td>\n",
       "      <td>125.00</td>\n",
       "      <td>6.662502</td>\n",
       "      <td>174</td>\n",
       "      <td>0.001765</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>39</th>\n",
       "      <td>Euler</td>\n",
       "      <td>625.00</td>\n",
       "      <td>37.926920</td>\n",
       "      <td>34</td>\n",
       "      <td>0.000609</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   Engine  Multiplier  Error (m)  Integration Steps  Speed (s)\n",
       "33  Euler        0.04   0.002065             545834   4.931240\n",
       "34  Euler        0.20   0.010319             109166   0.976583\n",
       "35  Euler        1.00   0.051491              21833   0.289522\n",
       "36  Euler        5.00   0.258586               4366   0.039727\n",
       "37  Euler       25.00   1.296182                873   0.009258\n",
       "38  Euler      125.00   6.662502                174   0.001765\n",
       "39  Euler      625.00  37.926920                 34   0.000609"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "step = 5.0\n",
    "multiplier = 1.0/step**2\n",
    "while multiplier <= 1000.0:\n",
    "    err, count, speed = chk_engine('euler_engine', timeit=True, step_multiplier=multiplier)\n",
    "    summary.append(('Euler', multiplier, err, count, speed))\n",
    "    multiplier *= step\n",
    "df = pd.DataFrame(summary, columns=['Engine', 'Multiplier', 'Error (m)', 'Integration Steps', 'Speed (s)'])\n",
    "df[df.Engine == 'Euler']"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "88503400",
   "metadata": {},
   "source": [
    "## Euler constant step\n",
    "\n",
    "Here we monkey-patch the `EulerIntegrationEngine` to use a constant time step to see if that has any effect on performance.  (It does not.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "4fff1d54",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3.1 s ± 30.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
      "699 ms ± 215 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
      "123 ms ± 2.13 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
      "24.7 ms ± 529 μs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
      "6.9 ms ± 1.49 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)\n",
      "1.17 ms ± 30.5 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Engine</th>\n",
       "      <th>Step (s)</th>\n",
       "      <th>Error (m)</th>\n",
       "      <th>Integration Steps</th>\n",
       "      <th>Speed (s)</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>40</th>\n",
       "      <td>Euler const</td>\n",
       "      <td>0.00004</td>\n",
       "      <td>0.005773</td>\n",
       "      <td>347846</td>\n",
       "      <td>3.099126</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>41</th>\n",
       "      <td>Euler const</td>\n",
       "      <td>0.00020</td>\n",
       "      <td>0.028739</td>\n",
       "      <td>69568</td>\n",
       "      <td>0.699337</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>42</th>\n",
       "      <td>Euler const</td>\n",
       "      <td>0.00100</td>\n",
       "      <td>0.144473</td>\n",
       "      <td>13912</td>\n",
       "      <td>0.122625</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>43</th>\n",
       "      <td>Euler const</td>\n",
       "      <td>0.00500</td>\n",
       "      <td>0.723063</td>\n",
       "      <td>2781</td>\n",
       "      <td>0.024713</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>44</th>\n",
       "      <td>Euler const</td>\n",
       "      <td>0.02500</td>\n",
       "      <td>3.669263</td>\n",
       "      <td>555</td>\n",
       "      <td>0.006902</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>45</th>\n",
       "      <td>Euler const</td>\n",
       "      <td>0.12500</td>\n",
       "      <td>19.707921</td>\n",
       "      <td>110</td>\n",
       "      <td>0.001173</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "         Engine  Step (s)  Error (m)  Integration Steps  Speed (s)\n",
       "40  Euler const   0.00004   0.005773             347846   3.099126\n",
       "41  Euler const   0.00020   0.028739              69568   0.699337\n",
       "42  Euler const   0.00100   0.144473              13912   0.122625\n",
       "43  Euler const   0.00500   0.723063               2781   0.024713\n",
       "44  Euler const   0.02500   3.669263                555   0.006902\n",
       "45  Euler const   0.12500  19.707921                110   0.001173"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from py_ballisticcalc.engines.euler import EulerIntegrationEngine\n",
    "def time_step(self, base_step: float, velocity: float) -> float:\n",
    "    return base_step / 500.0  # Set a constant time step of 1ms for Euler engine\n",
    "EulerIntegrationEngine.time_step = time_step\n",
    "step = 5.0\n",
    "multiplier = 1.0/step**2\n",
    "while multiplier <= 500.0:\n",
    "    err, count, speed = chk_engine('euler_engine', step_multiplier=multiplier, timeit=True)\n",
    "    summary.append(('Euler const', multiplier*0.001, err, count, speed))\n",
    "    multiplier *= step\n",
    "df = pd.DataFrame(summary, columns=['Engine', 'Step (s)', 'Error (m)', 'Integration Steps', 'Speed (s)'])\n",
    "df[df.Engine == 'Euler const']"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0bf39bb4",
   "metadata": {},
   "source": [
    "## RK4 Compiled"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "4c8112e2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "56.2 ms ± 1.84 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
      "11 ms ± 46.6 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n",
      "2.28 ms ± 32.9 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n",
      "556 μs ± 23.2 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)\n",
      "183 μs ± 5.05 μs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)\n",
      "107 μs ± 727 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)\n",
      "91.9 μs ± 608 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Engine</th>\n",
       "      <th>Multiplier</th>\n",
       "      <th>Error (m)</th>\n",
       "      <th>Integration Steps</th>\n",
       "      <th>Speed (s)</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>46</th>\n",
       "      <td>RK4 Cython</td>\n",
       "      <td>0.04</td>\n",
       "      <td>0.000126</td>\n",
       "      <td>347848</td>\n",
       "      <td>0.056213</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>47</th>\n",
       "      <td>RK4 Cython</td>\n",
       "      <td>0.20</td>\n",
       "      <td>0.000609</td>\n",
       "      <td>69570</td>\n",
       "      <td>0.010968</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>48</th>\n",
       "      <td>RK4 Cython</td>\n",
       "      <td>1.00</td>\n",
       "      <td>0.002707</td>\n",
       "      <td>13914</td>\n",
       "      <td>0.002283</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>49</th>\n",
       "      <td>RK4 Cython</td>\n",
       "      <td>5.00</td>\n",
       "      <td>0.014663</td>\n",
       "      <td>2783</td>\n",
       "      <td>0.000556</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>50</th>\n",
       "      <td>RK4 Cython</td>\n",
       "      <td>25.00</td>\n",
       "      <td>0.067015</td>\n",
       "      <td>557</td>\n",
       "      <td>0.000183</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>51</th>\n",
       "      <td>RK4 Cython</td>\n",
       "      <td>125.00</td>\n",
       "      <td>0.346484</td>\n",
       "      <td>112</td>\n",
       "      <td>0.000107</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>52</th>\n",
       "      <td>RK4 Cython</td>\n",
       "      <td>625.00</td>\n",
       "      <td>2.987861</td>\n",
       "      <td>23</td>\n",
       "      <td>0.000092</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        Engine  Multiplier  Error (m)  Integration Steps  Speed (s)\n",
       "46  RK4 Cython        0.04   0.000126             347848   0.056213\n",
       "47  RK4 Cython        0.20   0.000609              69570   0.010968\n",
       "48  RK4 Cython        1.00   0.002707              13914   0.002283\n",
       "49  RK4 Cython        5.00   0.014663               2783   0.000556\n",
       "50  RK4 Cython       25.00   0.067015                557   0.000183\n",
       "51  RK4 Cython      125.00   0.346484                112   0.000107\n",
       "52  RK4 Cython      625.00   2.987861                 23   0.000092"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "step = 5.0\n",
    "multiplier = 1.0/step**2\n",
    "while multiplier <= 1000.0:\n",
    "    # To get same step as other engines without recompiling, check the base step size in rk4_engine.pyx\n",
    "    err, count, speed = chk_engine('cythonized_rk4_engine', timeit=True, step_multiplier=multiplier*(2.0/3.0))\n",
    "    summary.append(('RK4 Cython', multiplier, err, count, speed))\n",
    "    multiplier *= step\n",
    "df = pd.DataFrame(summary, columns=['Engine', 'Multiplier', 'Error (m)', 'Integration Steps', 'Speed (s)'])\n",
    "df[df.Engine == 'RK4 Cython']"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "32932be0",
   "metadata": {},
   "source": [
    "# Summary\n",
    "\n",
    "The following chart shows that the highly adaptive SciPy solvers are in a class of their own: They produce the smallest errors, and can do so with an order of magnitude less compute.  Certainly part of this is thanks to their compiled back-end.  The compiled version of our RK4 solver is – despite a constant time step – even faster, and with the smallest multiplier setting its error is under 1 millimeter at almost 2km.\n",
    "\n",
    "The poor showing by the velocity Verlet engine is striking but not unexpected: It is a second-order integrator, but one of its notable design advantages is conservation of energy. However, a ballistic trajectory with air resistance is a _dissipative_ system: The drag force varies with velocity, and this breaks the time-reversibility that the Verlet formulas were designed to exploit.  The result, as we see here, is that the Verlet's performance is reduced to that of the simpler, first-order Euler method.\n",
    "\n",
    "## Chart"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "aef21d58",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 92220 characters]",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from matplotlib import pyplot as plt\n",
    "dfplot = pd.DataFrame(summary, columns=['Engine', 'Multiplier', 'Error (m)', 'Integration Steps', 'Speed'])\n",
    "fig, ax = plt.subplots(figsize=(8, 6))\n",
    "for engine, group in dfplot.groupby('Engine'):\n",
    "    ax.plot(group['Speed'], group['Error (m)'], marker='o', label=engine)\n",
    "ax.set_xlabel('Speed (s)')\n",
    "ax.set_ylabel('Error (m)')\n",
    "ax.set_xscale('log')\n",
    "ax.set_yscale('log')\n",
    "ax.set_title('Log Error vs Log Speed by Engine')\n",
    "ax.legend(title='Engine')\n",
    "ax.grid(True, which='major', alpha=0.3)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c457b105",
   "metadata": {},
   "source": [
    "## Data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "5aee87c8",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style type=\"text/css\">\n",
       "</style>\n",
       "<table id=\"T_fdf9e\">\n",
       "  <thead>\n",
       "    <tr>\n",
       "      <th class=\"blank level0\" >&nbsp;</th>\n",
       "      <th id=\"T_fdf9e_level0_col0\" class=\"col_heading level0 col0\" >Engine</th>\n",
       "      <th id=\"T_fdf9e_level0_col1\" class=\"col_heading level0 col1\" >Multiplier</th>\n",
       "      <th id=\"T_fdf9e_level0_col2\" class=\"col_heading level0 col2\" >Error (m)</th>\n",
       "      <th id=\"T_fdf9e_level0_col3\" class=\"col_heading level0 col3\" >Integration Steps</th>\n",
       "      <th id=\"T_fdf9e_level0_col4\" class=\"col_heading level0 col4\" >Speed</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row0\" class=\"row_heading level0 row0\" >0</th>\n",
       "      <td id=\"T_fdf9e_row0_col0\" class=\"data row0 col0\" >SciPy RK45</td>\n",
       "      <td id=\"T_fdf9e_row0_col1\" class=\"data row0 col1\" >0.0000000001</td>\n",
       "      <td id=\"T_fdf9e_row0_col2\" class=\"data row0 col2\" >0.000000015108</td>\n",
       "      <td id=\"T_fdf9e_row0_col3\" class=\"data row0 col3\" >8702</td>\n",
       "      <td id=\"T_fdf9e_row0_col4\" class=\"data row0 col4\" >0.299046</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row1\" class=\"row_heading level0 row1\" >1</th>\n",
       "      <td id=\"T_fdf9e_row1_col0\" class=\"data row1 col0\" >SciPy RK45</td>\n",
       "      <td id=\"T_fdf9e_row1_col1\" class=\"data row1 col1\" >0.0000000010</td>\n",
       "      <td id=\"T_fdf9e_row1_col2\" class=\"data row1 col2\" >0.000000125516</td>\n",
       "      <td id=\"T_fdf9e_row1_col3\" class=\"data row1 col3\" >7520</td>\n",
       "      <td id=\"T_fdf9e_row1_col4\" class=\"data row1 col4\" >0.198652</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row2\" class=\"row_heading level0 row2\" >2</th>\n",
       "      <td id=\"T_fdf9e_row2_col0\" class=\"data row2 col0\" >SciPy RK45</td>\n",
       "      <td id=\"T_fdf9e_row2_col1\" class=\"data row2 col1\" >0.0000000100</td>\n",
       "      <td id=\"T_fdf9e_row2_col2\" class=\"data row2 col2\" >0.000000276307</td>\n",
       "      <td id=\"T_fdf9e_row2_col3\" class=\"data row2 col3\" >6722</td>\n",
       "      <td id=\"T_fdf9e_row2_col4\" class=\"data row2 col4\" >0.169317</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row3\" class=\"row_heading level0 row3\" >3</th>\n",
       "      <td id=\"T_fdf9e_row3_col0\" class=\"data row3 col0\" >SciPy RK45</td>\n",
       "      <td id=\"T_fdf9e_row3_col1\" class=\"data row3 col1\" >0.0000001000</td>\n",
       "      <td id=\"T_fdf9e_row3_col2\" class=\"data row3 col2\" >0.000002057640</td>\n",
       "      <td id=\"T_fdf9e_row3_col3\" class=\"data row3 col3\" >5156</td>\n",
       "      <td id=\"T_fdf9e_row3_col4\" class=\"data row3 col4\" >0.135883</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row4\" class=\"row_heading level0 row4\" >4</th>\n",
       "      <td id=\"T_fdf9e_row4_col0\" class=\"data row4 col0\" >SciPy RK45</td>\n",
       "      <td id=\"T_fdf9e_row4_col1\" class=\"data row4 col1\" >0.0000010000</td>\n",
       "      <td id=\"T_fdf9e_row4_col2\" class=\"data row4 col2\" >0.000127843121</td>\n",
       "      <td id=\"T_fdf9e_row4_col3\" class=\"data row4 col3\" >3080</td>\n",
       "      <td id=\"T_fdf9e_row4_col4\" class=\"data row4 col4\" >0.089423</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row5\" class=\"row_heading level0 row5\" >5</th>\n",
       "      <td id=\"T_fdf9e_row5_col0\" class=\"data row5 col0\" >SciPy RK45</td>\n",
       "      <td id=\"T_fdf9e_row5_col1\" class=\"data row5 col1\" >0.0000100000</td>\n",
       "      <td id=\"T_fdf9e_row5_col2\" class=\"data row5 col2\" >0.000120657467</td>\n",
       "      <td id=\"T_fdf9e_row5_col3\" class=\"data row5 col3\" >1448</td>\n",
       "      <td id=\"T_fdf9e_row5_col4\" class=\"data row5 col4\" >0.038640</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row6\" class=\"row_heading level0 row6\" >6</th>\n",
       "      <td id=\"T_fdf9e_row6_col0\" class=\"data row6 col0\" >SciPy RK45</td>\n",
       "      <td id=\"T_fdf9e_row6_col1\" class=\"data row6 col1\" >0.0001000000</td>\n",
       "      <td id=\"T_fdf9e_row6_col2\" class=\"data row6 col2\" >0.003347521269</td>\n",
       "      <td id=\"T_fdf9e_row6_col3\" class=\"data row6 col3\" >512</td>\n",
       "      <td id=\"T_fdf9e_row6_col4\" class=\"data row6 col4\" >0.014426</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row7\" class=\"row_heading level0 row7\" >7</th>\n",
       "      <td id=\"T_fdf9e_row7_col0\" class=\"data row7 col0\" >SciPy RK45</td>\n",
       "      <td id=\"T_fdf9e_row7_col1\" class=\"data row7 col1\" >0.0010000000</td>\n",
       "      <td id=\"T_fdf9e_row7_col2\" class=\"data row7 col2\" >0.024012462772</td>\n",
       "      <td id=\"T_fdf9e_row7_col3\" class=\"data row7 col3\" >176</td>\n",
       "      <td id=\"T_fdf9e_row7_col4\" class=\"data row7 col4\" >0.005635</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row8\" class=\"row_heading level0 row8\" >8</th>\n",
       "      <td id=\"T_fdf9e_row8_col0\" class=\"data row8 col0\" >SciPy RK45</td>\n",
       "      <td id=\"T_fdf9e_row8_col1\" class=\"data row8 col1\" >0.0100000000</td>\n",
       "      <td id=\"T_fdf9e_row8_col2\" class=\"data row8 col2\" >0.023207585512</td>\n",
       "      <td id=\"T_fdf9e_row8_col3\" class=\"data row8 col3\" >110</td>\n",
       "      <td id=\"T_fdf9e_row8_col4\" class=\"data row8 col4\" >0.004622</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row9\" class=\"row_heading level0 row9\" >9</th>\n",
       "      <td id=\"T_fdf9e_row9_col0\" class=\"data row9 col0\" >SciPy RK45</td>\n",
       "      <td id=\"T_fdf9e_row9_col1\" class=\"data row9 col1\" >0.1000000000</td>\n",
       "      <td id=\"T_fdf9e_row9_col2\" class=\"data row9 col2\" >0.029732142680</td>\n",
       "      <td id=\"T_fdf9e_row9_col3\" class=\"data row9 col3\" >68</td>\n",
       "      <td id=\"T_fdf9e_row9_col4\" class=\"data row9 col4\" >0.002697</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row10\" class=\"row_heading level0 row10\" >10</th>\n",
       "      <td id=\"T_fdf9e_row10_col0\" class=\"data row10 col0\" >SciPy LSODA</td>\n",
       "      <td id=\"T_fdf9e_row10_col1\" class=\"data row10 col1\" >0.0000000001</td>\n",
       "      <td id=\"T_fdf9e_row10_col2\" class=\"data row10 col2\" >0.000000001929</td>\n",
       "      <td id=\"T_fdf9e_row10_col3\" class=\"data row10 col3\" >4836</td>\n",
       "      <td id=\"T_fdf9e_row10_col4\" class=\"data row10 col4\" >0.191704</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row11\" class=\"row_heading level0 row11\" >11</th>\n",
       "      <td id=\"T_fdf9e_row11_col0\" class=\"data row11 col0\" >SciPy LSODA</td>\n",
       "      <td id=\"T_fdf9e_row11_col1\" class=\"data row11 col1\" >0.0000000010</td>\n",
       "      <td id=\"T_fdf9e_row11_col2\" class=\"data row11 col2\" >0.000000002898</td>\n",
       "      <td id=\"T_fdf9e_row11_col3\" class=\"data row11 col3\" >4631</td>\n",
       "      <td id=\"T_fdf9e_row11_col4\" class=\"data row11 col4\" >0.192327</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row12\" class=\"row_heading level0 row12\" >12</th>\n",
       "      <td id=\"T_fdf9e_row12_col0\" class=\"data row12 col0\" >SciPy LSODA</td>\n",
       "      <td id=\"T_fdf9e_row12_col1\" class=\"data row12 col1\" >0.0000000100</td>\n",
       "      <td id=\"T_fdf9e_row12_col2\" class=\"data row12 col2\" >0.000000007548</td>\n",
       "      <td id=\"T_fdf9e_row12_col3\" class=\"data row12 col3\" >4179</td>\n",
       "      <td id=\"T_fdf9e_row12_col4\" class=\"data row12 col4\" >0.162791</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row13\" class=\"row_heading level0 row13\" >13</th>\n",
       "      <td id=\"T_fdf9e_row13_col0\" class=\"data row13 col0\" >SciPy LSODA</td>\n",
       "      <td id=\"T_fdf9e_row13_col1\" class=\"data row13 col1\" >0.0000001000</td>\n",
       "      <td id=\"T_fdf9e_row13_col2\" class=\"data row13 col2\" >0.000000019473</td>\n",
       "      <td id=\"T_fdf9e_row13_col3\" class=\"data row13 col3\" >3492</td>\n",
       "      <td id=\"T_fdf9e_row13_col4\" class=\"data row13 col4\" >0.128291</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row14\" class=\"row_heading level0 row14\" >14</th>\n",
       "      <td id=\"T_fdf9e_row14_col0\" class=\"data row14 col0\" >SciPy LSODA</td>\n",
       "      <td id=\"T_fdf9e_row14_col1\" class=\"data row14 col1\" >0.0000010000</td>\n",
       "      <td id=\"T_fdf9e_row14_col2\" class=\"data row14 col2\" >0.000000571611</td>\n",
       "      <td id=\"T_fdf9e_row14_col3\" class=\"data row14 col3\" >2825</td>\n",
       "      <td id=\"T_fdf9e_row14_col4\" class=\"data row14 col4\" >0.106481</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row15\" class=\"row_heading level0 row15\" >15</th>\n",
       "      <td id=\"T_fdf9e_row15_col0\" class=\"data row15 col0\" >SciPy LSODA</td>\n",
       "      <td id=\"T_fdf9e_row15_col1\" class=\"data row15 col1\" >0.0000100000</td>\n",
       "      <td id=\"T_fdf9e_row15_col2\" class=\"data row15 col2\" >0.000022067056</td>\n",
       "      <td id=\"T_fdf9e_row15_col3\" class=\"data row15 col3\" >1906</td>\n",
       "      <td id=\"T_fdf9e_row15_col4\" class=\"data row15 col4\" >0.066297</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row16\" class=\"row_heading level0 row16\" >16</th>\n",
       "      <td id=\"T_fdf9e_row16_col0\" class=\"data row16 col0\" >SciPy LSODA</td>\n",
       "      <td id=\"T_fdf9e_row16_col1\" class=\"data row16 col1\" >0.0001000000</td>\n",
       "      <td id=\"T_fdf9e_row16_col2\" class=\"data row16 col2\" >0.000278711932</td>\n",
       "      <td id=\"T_fdf9e_row16_col3\" class=\"data row16 col3\" >992</td>\n",
       "      <td id=\"T_fdf9e_row16_col4\" class=\"data row16 col4\" >0.043315</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row17\" class=\"row_heading level0 row17\" >17</th>\n",
       "      <td id=\"T_fdf9e_row17_col0\" class=\"data row17 col0\" >SciPy LSODA</td>\n",
       "      <td id=\"T_fdf9e_row17_col1\" class=\"data row17 col1\" >0.0010000000</td>\n",
       "      <td id=\"T_fdf9e_row17_col2\" class=\"data row17 col2\" >0.003127262948</td>\n",
       "      <td id=\"T_fdf9e_row17_col3\" class=\"data row17 col3\" >425</td>\n",
       "      <td id=\"T_fdf9e_row17_col4\" class=\"data row17 col4\" >0.016802</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row18\" class=\"row_heading level0 row18\" >18</th>\n",
       "      <td id=\"T_fdf9e_row18_col0\" class=\"data row18 col0\" >SciPy LSODA</td>\n",
       "      <td id=\"T_fdf9e_row18_col1\" class=\"data row18 col1\" >0.0100000000</td>\n",
       "      <td id=\"T_fdf9e_row18_col2\" class=\"data row18 col2\" >0.001836174923</td>\n",
       "      <td id=\"T_fdf9e_row18_col3\" class=\"data row18 col3\" >174</td>\n",
       "      <td id=\"T_fdf9e_row18_col4\" class=\"data row18 col4\" >0.008785</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row19\" class=\"row_heading level0 row19\" >19</th>\n",
       "      <td id=\"T_fdf9e_row19_col0\" class=\"data row19 col0\" >SciPy LSODA</td>\n",
       "      <td id=\"T_fdf9e_row19_col1\" class=\"data row19 col1\" >0.1000000000</td>\n",
       "      <td id=\"T_fdf9e_row19_col2\" class=\"data row19 col2\" >0.017177556825</td>\n",
       "      <td id=\"T_fdf9e_row19_col3\" class=\"data row19 col3\" >82</td>\n",
       "      <td id=\"T_fdf9e_row19_col4\" class=\"data row19 col4\" >0.003883</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row20\" class=\"row_heading level0 row20\" >20</th>\n",
       "      <td id=\"T_fdf9e_row20_col0\" class=\"data row20 col0\" >RK4</td>\n",
       "      <td id=\"T_fdf9e_row20_col1\" class=\"data row20 col1\" >0.0000400000</td>\n",
       "      <td id=\"T_fdf9e_row20_col2\" class=\"data row20 col2\" >0.000123035842</td>\n",
       "      <td id=\"T_fdf9e_row20_col3\" class=\"data row20 col3\" >347848</td>\n",
       "      <td id=\"T_fdf9e_row20_col4\" class=\"data row20 col4\" >10.854290</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row21\" class=\"row_heading level0 row21\" >21</th>\n",
       "      <td id=\"T_fdf9e_row21_col0\" class=\"data row21 col0\" >RK4</td>\n",
       "      <td id=\"T_fdf9e_row21_col1\" class=\"data row21 col1\" >0.0002000000</td>\n",
       "      <td id=\"T_fdf9e_row21_col2\" class=\"data row21 col2\" >0.000605268033</td>\n",
       "      <td id=\"T_fdf9e_row21_col3\" class=\"data row21 col3\" >69570</td>\n",
       "      <td id=\"T_fdf9e_row21_col4\" class=\"data row21 col4\" >2.152400</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row22\" class=\"row_heading level0 row22\" >22</th>\n",
       "      <td id=\"T_fdf9e_row22_col0\" class=\"data row22 col0\" >RK4</td>\n",
       "      <td id=\"T_fdf9e_row22_col1\" class=\"data row22 col1\" >0.0010000000</td>\n",
       "      <td id=\"T_fdf9e_row22_col2\" class=\"data row22 col2\" >0.002703803690</td>\n",
       "      <td id=\"T_fdf9e_row22_col3\" class=\"data row22 col3\" >13914</td>\n",
       "      <td id=\"T_fdf9e_row22_col4\" class=\"data row22 col4\" >0.536611</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row23\" class=\"row_heading level0 row23\" >23</th>\n",
       "      <td id=\"T_fdf9e_row23_col0\" class=\"data row23 col0\" >RK4</td>\n",
       "      <td id=\"T_fdf9e_row23_col1\" class=\"data row23 col1\" >0.0050000000</td>\n",
       "      <td id=\"T_fdf9e_row23_col2\" class=\"data row23 col2\" >0.014660093339</td>\n",
       "      <td id=\"T_fdf9e_row23_col3\" class=\"data row23 col3\" >2783</td>\n",
       "      <td id=\"T_fdf9e_row23_col4\" class=\"data row23 col4\" >0.090032</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row24\" class=\"row_heading level0 row24\" >24</th>\n",
       "      <td id=\"T_fdf9e_row24_col0\" class=\"data row24 col0\" >RK4</td>\n",
       "      <td id=\"T_fdf9e_row24_col1\" class=\"data row24 col1\" >0.0250000000</td>\n",
       "      <td id=\"T_fdf9e_row24_col2\" class=\"data row24 col2\" >0.067012190866</td>\n",
       "      <td id=\"T_fdf9e_row24_col3\" class=\"data row24 col3\" >557</td>\n",
       "      <td id=\"T_fdf9e_row24_col4\" class=\"data row24 col4\" >0.017320</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row25\" class=\"row_heading level0 row25\" >25</th>\n",
       "      <td id=\"T_fdf9e_row25_col0\" class=\"data row25 col0\" >RK4</td>\n",
       "      <td id=\"T_fdf9e_row25_col1\" class=\"data row25 col1\" >0.1250000000</td>\n",
       "      <td id=\"T_fdf9e_row25_col2\" class=\"data row25 col2\" >0.346480874621</td>\n",
       "      <td id=\"T_fdf9e_row25_col3\" class=\"data row25 col3\" >112</td>\n",
       "      <td id=\"T_fdf9e_row25_col4\" class=\"data row25 col4\" >0.003627</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row26\" class=\"row_heading level0 row26\" >26</th>\n",
       "      <td id=\"T_fdf9e_row26_col0\" class=\"data row26 col0\" >RK4</td>\n",
       "      <td id=\"T_fdf9e_row26_col1\" class=\"data row26 col1\" >0.6250000000</td>\n",
       "      <td id=\"T_fdf9e_row26_col2\" class=\"data row26 col2\" >2.987859319938</td>\n",
       "      <td id=\"T_fdf9e_row26_col3\" class=\"data row26 col3\" >23</td>\n",
       "      <td id=\"T_fdf9e_row26_col4\" class=\"data row26 col4\" >0.000980</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row27\" class=\"row_heading level0 row27\" >27</th>\n",
       "      <td id=\"T_fdf9e_row27_col0\" class=\"data row27 col0\" >Verlet</td>\n",
       "      <td id=\"T_fdf9e_row27_col1\" class=\"data row27 col1\" >0.0000400000</td>\n",
       "      <td id=\"T_fdf9e_row27_col2\" class=\"data row27 col2\" >0.005741066171</td>\n",
       "      <td id=\"T_fdf9e_row27_col3\" class=\"data row27 col3\" >347846</td>\n",
       "      <td id=\"T_fdf9e_row27_col4\" class=\"data row27 col4\" >4.467510</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row28\" class=\"row_heading level0 row28\" >28</th>\n",
       "      <td id=\"T_fdf9e_row28_col0\" class=\"data row28 col0\" >Verlet</td>\n",
       "      <td id=\"T_fdf9e_row28_col1\" class=\"data row28 col1\" >0.0002000000</td>\n",
       "      <td id=\"T_fdf9e_row28_col2\" class=\"data row28 col2\" >0.028684658960</td>\n",
       "      <td id=\"T_fdf9e_row28_col3\" class=\"data row28 col3\" >69568</td>\n",
       "      <td id=\"T_fdf9e_row28_col4\" class=\"data row28 col4\" >0.878940</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row29\" class=\"row_heading level0 row29\" >29</th>\n",
       "      <td id=\"T_fdf9e_row29_col0\" class=\"data row29 col0\" >Verlet</td>\n",
       "      <td id=\"T_fdf9e_row29_col1\" class=\"data row29 col1\" >0.0010000000</td>\n",
       "      <td id=\"T_fdf9e_row29_col2\" class=\"data row29 col2\" >0.143861812182</td>\n",
       "      <td id=\"T_fdf9e_row29_col3\" class=\"data row29 col3\" >13912</td>\n",
       "      <td id=\"T_fdf9e_row29_col4\" class=\"data row29 col4\" >0.174871</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row30\" class=\"row_heading level0 row30\" >30</th>\n",
       "      <td id=\"T_fdf9e_row30_col0\" class=\"data row30 col0\" >Verlet</td>\n",
       "      <td id=\"T_fdf9e_row30_col1\" class=\"data row30 col1\" >0.0050000000</td>\n",
       "      <td id=\"T_fdf9e_row30_col2\" class=\"data row30 col2\" >0.720899203569</td>\n",
       "      <td id=\"T_fdf9e_row30_col3\" class=\"data row30 col3\" >2781</td>\n",
       "      <td id=\"T_fdf9e_row30_col4\" class=\"data row30 col4\" >0.043736</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row31\" class=\"row_heading level0 row31\" >31</th>\n",
       "      <td id=\"T_fdf9e_row31_col0\" class=\"data row31 col0\" >Verlet</td>\n",
       "      <td id=\"T_fdf9e_row31_col1\" class=\"data row31 col1\" >0.0250000000</td>\n",
       "      <td id=\"T_fdf9e_row31_col2\" class=\"data row31 col2\" >3.698341672852</td>\n",
       "      <td id=\"T_fdf9e_row31_col3\" class=\"data row31 col3\" >555</td>\n",
       "      <td id=\"T_fdf9e_row31_col4\" class=\"data row31 col4\" >0.007510</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row32\" class=\"row_heading level0 row32\" >32</th>\n",
       "      <td id=\"T_fdf9e_row32_col0\" class=\"data row32 col0\" >Verlet</td>\n",
       "      <td id=\"T_fdf9e_row32_col1\" class=\"data row32 col1\" >0.1250000000</td>\n",
       "      <td id=\"T_fdf9e_row32_col2\" class=\"data row32 col2\" >21.619531780237</td>\n",
       "      <td id=\"T_fdf9e_row32_col3\" class=\"data row32 col3\" >109</td>\n",
       "      <td id=\"T_fdf9e_row32_col4\" class=\"data row32 col4\" >0.001565</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row33\" class=\"row_heading level0 row33\" >33</th>\n",
       "      <td id=\"T_fdf9e_row33_col0\" class=\"data row33 col0\" >Euler</td>\n",
       "      <td id=\"T_fdf9e_row33_col1\" class=\"data row33 col1\" >0.0400000000</td>\n",
       "      <td id=\"T_fdf9e_row33_col2\" class=\"data row33 col2\" >0.002065299159</td>\n",
       "      <td id=\"T_fdf9e_row33_col3\" class=\"data row33 col3\" >545834</td>\n",
       "      <td id=\"T_fdf9e_row33_col4\" class=\"data row33 col4\" >4.931240</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row34\" class=\"row_heading level0 row34\" >34</th>\n",
       "      <td id=\"T_fdf9e_row34_col0\" class=\"data row34 col0\" >Euler</td>\n",
       "      <td id=\"T_fdf9e_row34_col1\" class=\"data row34 col1\" >0.2000000000</td>\n",
       "      <td id=\"T_fdf9e_row34_col2\" class=\"data row34 col2\" >0.010319218162</td>\n",
       "      <td id=\"T_fdf9e_row34_col3\" class=\"data row34 col3\" >109166</td>\n",
       "      <td id=\"T_fdf9e_row34_col4\" class=\"data row34 col4\" >0.976583</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row35\" class=\"row_heading level0 row35\" >35</th>\n",
       "      <td id=\"T_fdf9e_row35_col0\" class=\"data row35 col0\" >Euler</td>\n",
       "      <td id=\"T_fdf9e_row35_col1\" class=\"data row35 col1\" >1.0000000000</td>\n",
       "      <td id=\"T_fdf9e_row35_col2\" class=\"data row35 col2\" >0.051491261822</td>\n",
       "      <td id=\"T_fdf9e_row35_col3\" class=\"data row35 col3\" >21833</td>\n",
       "      <td id=\"T_fdf9e_row35_col4\" class=\"data row35 col4\" >0.289522</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row36\" class=\"row_heading level0 row36\" >36</th>\n",
       "      <td id=\"T_fdf9e_row36_col0\" class=\"data row36 col0\" >Euler</td>\n",
       "      <td id=\"T_fdf9e_row36_col1\" class=\"data row36 col1\" >5.0000000000</td>\n",
       "      <td id=\"T_fdf9e_row36_col2\" class=\"data row36 col2\" >0.258585982707</td>\n",
       "      <td id=\"T_fdf9e_row36_col3\" class=\"data row36 col3\" >4366</td>\n",
       "      <td id=\"T_fdf9e_row36_col4\" class=\"data row36 col4\" >0.039727</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row37\" class=\"row_heading level0 row37\" >37</th>\n",
       "      <td id=\"T_fdf9e_row37_col0\" class=\"data row37 col0\" >Euler</td>\n",
       "      <td id=\"T_fdf9e_row37_col1\" class=\"data row37 col1\" >25.0000000000</td>\n",
       "      <td id=\"T_fdf9e_row37_col2\" class=\"data row37 col2\" >1.296181660382</td>\n",
       "      <td id=\"T_fdf9e_row37_col3\" class=\"data row37 col3\" >873</td>\n",
       "      <td id=\"T_fdf9e_row37_col4\" class=\"data row37 col4\" >0.009258</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row38\" class=\"row_heading level0 row38\" >38</th>\n",
       "      <td id=\"T_fdf9e_row38_col0\" class=\"data row38 col0\" >Euler</td>\n",
       "      <td id=\"T_fdf9e_row38_col1\" class=\"data row38 col1\" >125.0000000000</td>\n",
       "      <td id=\"T_fdf9e_row38_col2\" class=\"data row38 col2\" >6.662501833408</td>\n",
       "      <td id=\"T_fdf9e_row38_col3\" class=\"data row38 col3\" >174</td>\n",
       "      <td id=\"T_fdf9e_row38_col4\" class=\"data row38 col4\" >0.001765</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row39\" class=\"row_heading level0 row39\" >39</th>\n",
       "      <td id=\"T_fdf9e_row39_col0\" class=\"data row39 col0\" >Euler</td>\n",
       "      <td id=\"T_fdf9e_row39_col1\" class=\"data row39 col1\" >625.0000000000</td>\n",
       "      <td id=\"T_fdf9e_row39_col2\" class=\"data row39 col2\" >37.926920486984</td>\n",
       "      <td id=\"T_fdf9e_row39_col3\" class=\"data row39 col3\" >34</td>\n",
       "      <td id=\"T_fdf9e_row39_col4\" class=\"data row39 col4\" >0.000609</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row40\" class=\"row_heading level0 row40\" >40</th>\n",
       "      <td id=\"T_fdf9e_row40_col0\" class=\"data row40 col0\" >Euler const</td>\n",
       "      <td id=\"T_fdf9e_row40_col1\" class=\"data row40 col1\" >0.0000400000</td>\n",
       "      <td id=\"T_fdf9e_row40_col2\" class=\"data row40 col2\" >0.005772678040</td>\n",
       "      <td id=\"T_fdf9e_row40_col3\" class=\"data row40 col3\" >347846</td>\n",
       "      <td id=\"T_fdf9e_row40_col4\" class=\"data row40 col4\" >3.099126</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row41\" class=\"row_heading level0 row41\" >41</th>\n",
       "      <td id=\"T_fdf9e_row41_col0\" class=\"data row41 col0\" >Euler const</td>\n",
       "      <td id=\"T_fdf9e_row41_col1\" class=\"data row41 col1\" >0.0002000000</td>\n",
       "      <td id=\"T_fdf9e_row41_col2\" class=\"data row41 col2\" >0.028739393185</td>\n",
       "      <td id=\"T_fdf9e_row41_col3\" class=\"data row41 col3\" >69568</td>\n",
       "      <td id=\"T_fdf9e_row41_col4\" class=\"data row41 col4\" >0.699337</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row42\" class=\"row_heading level0 row42\" >42</th>\n",
       "      <td id=\"T_fdf9e_row42_col0\" class=\"data row42 col0\" >Euler const</td>\n",
       "      <td id=\"T_fdf9e_row42_col1\" class=\"data row42 col1\" >0.0010000000</td>\n",
       "      <td id=\"T_fdf9e_row42_col2\" class=\"data row42 col2\" >0.144472828703</td>\n",
       "      <td id=\"T_fdf9e_row42_col3\" class=\"data row42 col3\" >13912</td>\n",
       "      <td id=\"T_fdf9e_row42_col4\" class=\"data row42 col4\" >0.122625</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row43\" class=\"row_heading level0 row43\" >43</th>\n",
       "      <td id=\"T_fdf9e_row43_col0\" class=\"data row43 col0\" >Euler const</td>\n",
       "      <td id=\"T_fdf9e_row43_col1\" class=\"data row43 col1\" >0.0050000000</td>\n",
       "      <td id=\"T_fdf9e_row43_col2\" class=\"data row43 col2\" >0.723063376203</td>\n",
       "      <td id=\"T_fdf9e_row43_col3\" class=\"data row43 col3\" >2781</td>\n",
       "      <td id=\"T_fdf9e_row43_col4\" class=\"data row43 col4\" >0.024713</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row44\" class=\"row_heading level0 row44\" >44</th>\n",
       "      <td id=\"T_fdf9e_row44_col0\" class=\"data row44 col0\" >Euler const</td>\n",
       "      <td id=\"T_fdf9e_row44_col1\" class=\"data row44 col1\" >0.0250000000</td>\n",
       "      <td id=\"T_fdf9e_row44_col2\" class=\"data row44 col2\" >3.669262894574</td>\n",
       "      <td id=\"T_fdf9e_row44_col3\" class=\"data row44 col3\" >555</td>\n",
       "      <td id=\"T_fdf9e_row44_col4\" class=\"data row44 col4\" >0.006902</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row45\" class=\"row_heading level0 row45\" >45</th>\n",
       "      <td id=\"T_fdf9e_row45_col0\" class=\"data row45 col0\" >Euler const</td>\n",
       "      <td id=\"T_fdf9e_row45_col1\" class=\"data row45 col1\" >0.1250000000</td>\n",
       "      <td id=\"T_fdf9e_row45_col2\" class=\"data row45 col2\" >19.707921247320</td>\n",
       "      <td id=\"T_fdf9e_row45_col3\" class=\"data row45 col3\" >110</td>\n",
       "      <td id=\"T_fdf9e_row45_col4\" class=\"data row45 col4\" >0.001173</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row46\" class=\"row_heading level0 row46\" >46</th>\n",
       "      <td id=\"T_fdf9e_row46_col0\" class=\"data row46 col0\" >RK4 Cython</td>\n",
       "      <td id=\"T_fdf9e_row46_col1\" class=\"data row46 col1\" >0.0400000000</td>\n",
       "      <td id=\"T_fdf9e_row46_col2\" class=\"data row46 col2\" >0.000126054373</td>\n",
       "      <td id=\"T_fdf9e_row46_col3\" class=\"data row46 col3\" >347848</td>\n",
       "      <td id=\"T_fdf9e_row46_col4\" class=\"data row46 col4\" >0.056213</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row47\" class=\"row_heading level0 row47\" >47</th>\n",
       "      <td id=\"T_fdf9e_row47_col0\" class=\"data row47 col0\" >RK4 Cython</td>\n",
       "      <td id=\"T_fdf9e_row47_col1\" class=\"data row47 col1\" >0.2000000000</td>\n",
       "      <td id=\"T_fdf9e_row47_col2\" class=\"data row47 col2\" >0.000609078318</td>\n",
       "      <td id=\"T_fdf9e_row47_col3\" class=\"data row47 col3\" >69570</td>\n",
       "      <td id=\"T_fdf9e_row47_col4\" class=\"data row47 col4\" >0.010968</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row48\" class=\"row_heading level0 row48\" >48</th>\n",
       "      <td id=\"T_fdf9e_row48_col0\" class=\"data row48 col0\" >RK4 Cython</td>\n",
       "      <td id=\"T_fdf9e_row48_col1\" class=\"data row48 col1\" >1.0000000000</td>\n",
       "      <td id=\"T_fdf9e_row48_col2\" class=\"data row48 col2\" >0.002706821484</td>\n",
       "      <td id=\"T_fdf9e_row48_col3\" class=\"data row48 col3\" >13914</td>\n",
       "      <td id=\"T_fdf9e_row48_col4\" class=\"data row48 col4\" >0.002283</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row49\" class=\"row_heading level0 row49\" >49</th>\n",
       "      <td id=\"T_fdf9e_row49_col0\" class=\"data row49 col0\" >RK4 Cython</td>\n",
       "      <td id=\"T_fdf9e_row49_col1\" class=\"data row49 col1\" >5.0000000000</td>\n",
       "      <td id=\"T_fdf9e_row49_col2\" class=\"data row49 col2\" >0.014663104024</td>\n",
       "      <td id=\"T_fdf9e_row49_col3\" class=\"data row49 col3\" >2783</td>\n",
       "      <td id=\"T_fdf9e_row49_col4\" class=\"data row49 col4\" >0.000556</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row50\" class=\"row_heading level0 row50\" >50</th>\n",
       "      <td id=\"T_fdf9e_row50_col0\" class=\"data row50 col0\" >RK4 Cython</td>\n",
       "      <td id=\"T_fdf9e_row50_col1\" class=\"data row50 col1\" >25.0000000000</td>\n",
       "      <td id=\"T_fdf9e_row50_col2\" class=\"data row50 col2\" >0.067015190833</td>\n",
       "      <td id=\"T_fdf9e_row50_col3\" class=\"data row50 col3\" >557</td>\n",
       "      <td id=\"T_fdf9e_row50_col4\" class=\"data row50 col4\" >0.000183</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row51\" class=\"row_heading level0 row51\" >51</th>\n",
       "      <td id=\"T_fdf9e_row51_col0\" class=\"data row51 col0\" >RK4 Cython</td>\n",
       "      <td id=\"T_fdf9e_row51_col1\" class=\"data row51 col1\" >125.0000000000</td>\n",
       "      <td id=\"T_fdf9e_row51_col2\" class=\"data row51 col2\" >0.346483819282</td>\n",
       "      <td id=\"T_fdf9e_row51_col3\" class=\"data row51 col3\" >112</td>\n",
       "      <td id=\"T_fdf9e_row51_col4\" class=\"data row51 col4\" >0.000107</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_fdf9e_level0_row52\" class=\"row_heading level0 row52\" >52</th>\n",
       "      <td id=\"T_fdf9e_row52_col0\" class=\"data row52 col0\" >RK4 Cython</td>\n",
       "      <td id=\"T_fdf9e_row52_col1\" class=\"data row52 col1\" >625.0000000000</td>\n",
       "      <td id=\"T_fdf9e_row52_col2\" class=\"data row52 col2\" >2.987861477640</td>\n",
       "      <td id=\"T_fdf9e_row52_col3\" class=\"data row52 col3\" >23</td>\n",
       "      <td id=\"T_fdf9e_row52_col4\" class=\"data row52 col4\" >0.000092</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n"
      ],
      "text/plain": [
       "<pandas.io.formats.style.Styler at 0x2c334b61700>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(dfplot.style.format({'Multiplier': '{:.10f}', 'Error (m)': '{:.12f}'}))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "py-ballisticcalc",
   "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.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
