{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "c30b6a4f-cb8a-4da2-8939-beaa5420a8a8",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import math\n",
    "import json\n",
    "import statistics\n",
    "from pathlib import Path\n",
    "from matplotlib import pyplot as plt\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "c8fbf52a-76df-4d5a-a50d-0e16d4a5921c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "74lvc_mesh_new_p0.3mm\n",
      "74lvc_mesh_new_p0.4mm\n",
      "74lvc_mesh_new_p0.6mm\n",
      "74lvc_mesh_new_p1.0mm\n",
      "74lvc_mesh_p0.3mm\n",
      "74lvc_mesh_p0.3mm_flip\n",
      "74lvc_mesh_p0.3mm_term\n",
      "74lvc_mesh_p0.4mm\n",
      "74lvc_mesh_p0.6mm\n",
      "74lvc_mesh_p1.0mm\n",
      "74lvc_new2_mesh_p0.3mm\n",
      "74lvc_new2_mesh_p0.4mm\n",
      "74lvc_new2_mesh_p0.6mm\n",
      "74lvc_new2_mesh_p1.0mm\n",
      "74lvc_new2_open\n",
      "74lvc_new_mesh_p0.3mm\n",
      "74lvc_new_mesh_p0.3mm_flip\n",
      "74lvc_new_mesh_p0.3mm_term\n",
      "74lvc_new_open\n",
      "74lvc_open\n",
      "max3748_mesh_p0.3mm\n",
      "max3748_mesh_p0.3mm_flip\n",
      "max3748_mesh_p0.3mm_term\n",
      "max3748_mesh_p0.4mm\n",
      "max3748_mesh_p0.6mm\n",
      "max3748_mesh_p1.0mm\n",
      "max3748_open\n",
      "pi3hdx_clipline_offset_a_mesh_p0.3mm\n",
      "pi3hdx_clipline_offset_a_mesh_p0.3mm_flip\n",
      "pi3hdx_clipline_offset_a_mesh_p0.3mm_term\n",
      "pi3hdx_clipline_offset_a_mesh_p0.4mm\n",
      "pi3hdx_clipline_offset_a_mesh_p0.6mm\n",
      "pi3hdx_clipline_offset_a_mesh_p1.0mm\n",
      "pi3hdx_clipline_offset_a_mesh_p1.0mm_manip_scope_probe_a\n",
      "pi3hdx_clipline_offset_a_mesh_p1.0mm_manip_scope_probe_b\n",
      "pi3hdx_clipline_offset_a_mesh_p1.0mm_manip_scope_probe_none\n",
      "pi3hdx_clipline_offset_a_mesh_p1.0mm_manip_short\n",
      "pi3hdx_clipline_offset_a_open\n",
      "pi3hdx_clipline_offset_b_mesh_p0.3mm\n",
      "pi3hdx_clipline_offset_b_mesh_p0.3mm_flip\n",
      "pi3hdx_clipline_offset_b_mesh_p0.3mm_term\n",
      "pi3hdx_clipline_offset_b_mesh_p0.4mm\n",
      "pi3hdx_clipline_offset_b_mesh_p0.6mm\n",
      "pi3hdx_clipline_offset_b_mesh_p1.0mm\n",
      "pi3hdx_clipline_offset_b_mesh_p1.0mm_manip_short\n",
      "pi3hdx_clipline_offset_b_open\n",
      "pi3hdx_clipline_risetime\n",
      "pi3hdx_clipline_risetime_new\n",
      "pi3hdx_manip_test2_baseline\n",
      "pi3hdx_manip_test2_baseline2\n",
      "pi3hdx_manip_test2_open\n",
      "pi3hdx_manip_test2_scope_probe\n",
      "pi3hdx_manip_test2_short\n",
      "pi3hdx_manip_test_baseline\n",
      "pi3hdx_manip_test_baseline2\n",
      "pi3hdx_manip_test_baseline3\n",
      "pi3hdx_manip_test_open\n",
      "pi3hdx_manip_test_scope_probe\n",
      "pi3hdx_manip_test_scope_probe2\n",
      "pi3hdx_manip_test_short\n",
      "run2_74lvc_mesh_p0.3mm\n",
      "run2_74lvc_mesh_p0.4mm\n",
      "run2_74lvc_mesh_p0.6mm\n",
      "run2_74lvc_mesh_p1.0mm\n",
      "run2_74lvc_open\n",
      "run3_74lvc_mesh_p0.3mm\n",
      "run3_74lvc_mesh_p0.4mm\n",
      "run3_74lvc_mesh_p0.6mm\n",
      "run3_74lvc_mesh_p1.0mm\n",
      "run3_74lvc_open\n",
      "run3_max3748_mesh_p0.3mm\n",
      "run3_max3748_mesh_p0.4mm\n",
      "run3_max3748_mesh_p0.6mm\n",
      "run3_max3748_mesh_p1.0mm\n",
      "run3_max3748_open\n",
      "run3_pi3hdx_mesh_p0.3mm\n",
      "run3_pi3hdx_mesh_p0.4mm\n",
      "run3_pi3hdx_mesh_p0.6mm\n",
      "run3_pi3hdx_mesh_p04mm\n",
      "run3_pi3hdx_mesh_p1.0mm\n",
      "run3_pi3hdx_open\n",
      "run3_tdp0604_mesh_p0.3mm\n",
      "run3_tdp0604_mesh_p0.4mm\n",
      "run3_tdp0604_mesh_p0.6mm\n",
      "run3_tdp0604_mesh_p1.0mm\n",
      "run3_tdp0604_open\n",
      "tdp0604_mesh_p0.3mm\n",
      "tdp0604_mesh_p0.3mm_flip\n",
      "tdp0604_mesh_p0.3mm_term\n",
      "tdp0604_mesh_p0.4mm\n",
      "tdp0604_mesh_p0.6mm\n",
      "tdp0604_mesh_p1.0mm\n",
      "tdp0604_new_avg_flip_mesh_p0.3mm\n",
      "tdp0604_new_avg_flip_mesh_p0.4mm\n",
      "tdp0604_new_avg_flip_mesh_p0.6mm\n",
      "tdp0604_new_avg_flip_mesh_p1.0mm\n",
      "tdp0604_new_avg_mesh_p0.3mm\n",
      "tdp0604_new_avg_mesh_p0.4mm\n",
      "tdp0604_new_avg_mesh_p0.6mm\n",
      "tdp0604_new_avg_mesh_p1.0mm\n",
      "tdp0604_new_avg_term_flip_mesh_1.0mm_open\n",
      "tdp0604_new_avg_term_flip_mesh_1.0mm_open_fix\n",
      "tdp0604_new_avg_term_flip_mesh_1.0mm_open_fix_finished\n",
      "tdp0604_new_avg_term_flip_mesh_1.0mm_open_fix_wire\n",
      "tdp0604_new_avg_term_flip_mesh_1.0mm_probe_a\n",
      "tdp0604_new_avg_term_flip_mesh_1.0mm_probe_b\n",
      "tdp0604_new_avg_term_flip_mesh_1.0mm_probe_no\n",
      "tdp0604_new_avg_term_flip_mesh_1.0mm_short\n",
      "tdp0604_new_avg_term_flip_mesh_p0.3mm\n",
      "tdp0604_new_avg_term_flip_mesh_p0.4mm\n",
      "tdp0604_new_avg_term_flip_mesh_p0.6mm\n",
      "tdp0604_new_avg_term_flip_mesh_p1.0mm\n",
      "tdp0604_new_avg_term_flip_open\n",
      "tdp0604_new_mesh_p0.3mm\n",
      "tdp0604_new_mesh_p0.4mm\n",
      "tdp0604_new_mesh_p0.6mm\n",
      "tdp0604_new_mesh_p1.0mm\n",
      "tdp0604_new_open\n",
      "tdp0604_open\n",
      "works\n"
     ]
    }
   ],
   "source": [
    "meas = {}\n",
    "for f in Path('../data').glob('*.json'):\n",
    "    d = json.loads(f.read_bytes())\n",
    "    d['adc1'] = np.array(d['adc1'])\n",
    "    d['adc2'] = np.array(d['adc2'])\n",
    "    d['var1'] = np.array(d['var1'])\n",
    "    d['var2'] = np.array(d['var2'])\n",
    "    meas[d['meta']] = d\n",
    "\n",
    "for n in sorted(meas):\n",
    "    print(n)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 115,
   "id": "1dde57f3-45f9-480a-875f-c5b708ce61b1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "32\n",
      "0.007047986984252929 0.2918441636221749 6\n",
      "0.011429834365844726 0.38580293655395503 6\n",
      "statistics: 0.006896400451660156 0.2943451166152954 3\n",
      "Rise time t_r = 0.113 ns\n",
      "statistics: 0.011183834075927735 0.39527184963226314 3\n",
      "Rise time t_r = 0.185 ns\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 43252 characters]",
      "text/plain": [
       "<Figure size 640x480 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def plot_and_calc(run, ivl_start, ivl_end, ax=None, ylabel=True, grid=True):\n",
    "    d = meas[run]\n",
    "    sampling_period_ns = 1000 / 168 / 32\n",
    "    t = np.linspace(0, len(d['adc1'])*sampling_period_ns, len(d['adc1']))\n",
    "    if ax is None:\n",
    "        fig, ax = plt.subplots()\n",
    "    ivl_w = ivl_end - ivl_start\n",
    "    ivl = (ivl_start - ivl_w*0.5, ivl_end + ivl_w*0.5)\n",
    "\n",
    "    t_slice = t[(ivl[0] <= t) & (t <= ivl[1])]\n",
    "    adc1_slice = d['adc1'][(ivl[0] < t) & (t < ivl[1])]\n",
    "    adc2_slice = d['adc2'][(ivl[0] < t) & (t < ivl[1])]\n",
    "    print(len(t_slice))\n",
    "\n",
    "    def find_offset(t, y):\n",
    "        y_sorted = sorted(y)\n",
    "        low = statistics.mean(y_sorted[:len(y_sorted)//5])\n",
    "        high = statistics.mean(y_sorted[-len(y_sorted)//5:])\n",
    "        print(low, high, len(y_sorted)//5)\n",
    "        amp = high - low\n",
    "        th = low + 0.5 * amp\n",
    "        \n",
    "        for t1, t2, y1, y2 in zip(t, t[1:], y, y[1:]):\n",
    "            if y1 <= th < y2:\n",
    "                return t1 + (t2 - t1) * (th - y1) / (y2 - y1)\n",
    "\n",
    "    t1 = t_slice - find_offset(t_slice, adc1_slice)\n",
    "    t2 = t_slice - find_offset(t_slice, adc2_slice)\n",
    "    \n",
    "    def edge_process(xs, ys, color):\n",
    "        y_sorted = sorted(ys)\n",
    "        low = statistics.mean(y_sorted[:len(y_sorted)//10])\n",
    "        high = statistics.mean(y_sorted[-len(y_sorted)//10:])\n",
    "        print('statistics:', low, high, len(y_sorted)//10)\n",
    "        amp = high - low\n",
    "        th_low = low + 0.3 * amp\n",
    "        th_high = low + 0.7 * amp\n",
    "    \n",
    "        x_low, x_high = None, None\n",
    "        state = -1\n",
    "        for x1, x2, y1, y2 in zip(xs, xs[1:], ys, ys[1:]):\n",
    "            if state == -1 and y1 < th_low and y2 > th_low:\n",
    "                x_low = x1 + (x2 - x1) * (th_low - y1) / (y2 - y1)\n",
    "                state = 0\n",
    "            if state == 0 and y1 < th_high and y2 > th_high:\n",
    "                state = 1\n",
    "                x_high = x1 + (x2 - x1) * (th_high - y1) / (y2 - y1)\n",
    "        #ax.axvline(x_low, color='red')\n",
    "        #ax.axvline(x_high, color='blue')\n",
    "        tr = x_high - x_low\n",
    "        print(f'Rise time t_r = {tr:.3f} ns')\n",
    "        return th_low, th_high, tr\n",
    "        \n",
    "    \n",
    "    th_low1, th_high1, tr1 = edge_process(t1, adc1_slice, 'red')\n",
    "    th_low2, th_high2, tr2 = edge_process(t2, adc2_slice, 'blue')\n",
    "\n",
    "    if grid:\n",
    "        ax.grid()\n",
    "    else:\n",
    "        ax.axhline(th_low1, linewidth=1, linestyle='--', color='black', alpha=0.3, zorder=-2)\n",
    "        ax.axhline(th_high1, linewidth=1, linestyle='--', color='black', alpha=0.3, zorder=-2)\n",
    "        \n",
    "    #ax.plot(t, d['adc1'], color='red', alpha=0.8)\n",
    "    #ax.twinx().plot(t, d['adc2'], color='blue', alpha=0.8)\n",
    "    #ax.set_xlim(ivl)\n",
    "    l1 = ax.plot(t1, adc1_slice, color='black', linestyle='-', alpha=0.8)\n",
    "    tx = ax.twinx()\n",
    "    l2 = tx.plot(t2, adc2_slice, color='black', linestyle='--', alpha=0.8)\n",
    "    ax.set_xlim([-ivl_w/2, ivl_w/2])\n",
    "    ax.set_xlabel(r'$t\\;[\\mathrm{ns}]$')\n",
    "    if ylabel:\n",
    "        ax.set_ylabel(r'$V\\;[\\mathrm{V}]$')\n",
    "        tx.set_ylabel(r'$V\\;[\\mathrm{V}]$')\n",
    "    ax.legend(l1 + l2, [\n",
    "        fr'$t_r = {tr1:.3f}\\;\\mathrm{{ns}}$',\n",
    "        fr'$t_r = {tr2:.3f}\\;\\mathrm{{ns}}$'])\n",
    "    \n",
    "plot_and_calc('pi3hdx_clipline_risetime_new', 87, 90)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 116,
   "id": "fb6a9b36-3bcd-47ca-8b81-233000301aa9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "54\n",
      "0.7864820194244384 0.8391632080078124 10\n",
      "0.8373546695709229 0.8928957939147949 10\n",
      "statistics: 0.7859932708740234 0.8456388473510742 5\n",
      "Rise time t_r = 0.434 ns\n",
      "statistics: 0.8369855117797851 0.8988834857940673 5\n",
      "Rise time t_r = 0.350 ns\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 51032 characters]",
      "text/plain": [
       "<Figure size 640x480 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_and_calc('run3_max3748_open', 98, 103)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 122,
   "id": "792a2135-8a6b-4665-b9f7-ad0e30f50fb1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "33\n",
      "0.034509801864624025 0.3555932317461286 6\n",
      "0.12060098648071288 0.38929174968174524 6\n",
      "statistics: 0.030162048339843747 0.3609941482543945 3\n",
      "Rise time t_r = 0.547 ns\n",
      "statistics: 0.11962747573852539 0.392157769203186 3\n",
      "Rise time t_r = 0.470 ns\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 45580 characters]",
      "text/plain": [
       "<Figure size 640x480 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_and_calc('run3_tdp0604_open', 97, 100)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 124,
   "id": "66fe35f2-33d3-48f9-acb4-a07249284b3f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "65\n",
      "1.3896547904381384 2.0004766977750337 13\n",
      "1.382514447432298 1.9899575526897724 13\n",
      "statistics: 1.3864324569702147 2.00717533656529 6\n",
      "Rise time t_r = 1.184 ns\n",
      "statistics: 1.3810959815979003 1.9999185153416223 6\n",
      "Rise time t_r = 1.111 ns\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 39304 characters]",
      "text/plain": [
       "<Figure size 640x480 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_and_calc('run3_74lvc_open', 97, 103)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 128,
   "id": "ab54ba7f-1e18-4373-899c-30e740399a55",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "65\n",
      "1.3896547904381384 2.0004766977750337 13\n",
      "1.382514447432298 1.9899575526897724 13\n",
      "statistics: 1.3864324569702147 2.00717533656529 6\n",
      "Rise time t_r = 1.184 ns\n",
      "statistics: 1.3810959815979003 1.9999185153416223 6\n",
      "Rise time t_r = 1.111 ns\n",
      "54\n",
      "0.7864820194244384 0.8391632080078124 10\n",
      "0.8373546695709229 0.8928957939147949 10\n",
      "statistics: 0.7859932708740234 0.8456388473510742 5\n",
      "Rise time t_r = 0.434 ns\n",
      "statistics: 0.8369855117797851 0.8988834857940673 5\n",
      "Rise time t_r = 0.350 ns\n",
      "33\n",
      "0.034509801864624025 0.3555932317461286 6\n",
      "0.12060098648071288 0.38929174968174524 6\n",
      "statistics: 0.030162048339843747 0.3609941482543945 3\n",
      "Rise time t_r = 0.547 ns\n",
      "statistics: 0.11962747573852539 0.392157769203186 3\n",
      "Rise time t_r = 0.470 ns\n",
      "32\n",
      "0.007047986984252929 0.2918441636221749 6\n",
      "0.011429834365844726 0.38580293655395503 6\n",
      "statistics: 0.006896400451660156 0.2943451166152954 3\n",
      "Rise time t_r = 0.113 ns\n",
      "statistics: 0.011183834075927735 0.39527184963226314 3\n",
      "Rise time t_r = 0.185 ns\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 75012 characters]",
      "text/plain": [
       "<Figure size 700x300 with 8 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "with plt.rc_context({\n",
    "        'legend.fontsize': 8,\n",
    "    # RC config from ChatGPT:\n",
    "    \"font.size\": 8,  # General font size\n",
    "    \"axes.labelsize\": 8,  # Axis labels\n",
    "    \"axes.titlesize\": 8,  # Axis titles\n",
    "    \"xtick.labelsize\": 8,  # X-axis tick labels\n",
    "    \"ytick.labelsize\": 8,  # Y-axis tick labels\n",
    "    \"legend.fontsize\": 8,  # Legend font size\n",
    "    \"figure.titlesize\": 12,  # Overall figure title\n",
    "    \"mathtext.fontset\": \"cm\",  # Use Computer Modern for math\n",
    "\n",
    "    # Line and Marker settings\n",
    "    \"lines.linewidth\": 1,\n",
    "    \"lines.markersize\": 3,\n",
    "\n",
    "    # Tick settings\n",
    "    \"xtick.major.size\": 4,\n",
    "    \"xtick.minor.size\": 2,\n",
    "    \"ytick.major.size\": 4,\n",
    "    \"ytick.minor.size\": 2,\n",
    "    \"xtick.direction\": \"in\",\n",
    "    \"ytick.direction\": \"in\",\n",
    "\n",
    "    # Axes settings\n",
    "    \"axes.linewidth\": 0.8,\n",
    "    \"axes.grid\": True,\n",
    "    \"grid.linewidth\": 0.5,\n",
    "    \"grid.linestyle\": \"--\",\n",
    "\n",
    "    # Legend settings\n",
    "    \"legend.frameon\": False,\n",
    "\n",
    "    # Figure size for single-column figures in print (inches)\n",
    "#    \"figure.figsize\": (3.5, 2.5),  # Adjust based on journal specifications\n",
    "#    \"figure.dpi\": 300,  # High resolution for print\n",
    "\n",
    "    # Savefig settings for high-quality output\n",
    "    \"savefig.dpi\": 600,  # Ensures high-resolution images\n",
    "    \"savefig.bbox\": \"tight\",  # Trim extra whitespace\n",
    "    \"savefig.transparent\": True,  # Avoid background color\n",
    "}):\n",
    "    fig, axs = plt.subplots(2, 2, sharex=True, figsize=[7, 3])\n",
    "    axs = axs.flatten()\n",
    "    \n",
    "    plot_and_calc('run3_74lvc_open', 97, 103, ax=axs[0], ylabel=None, grid=False)\n",
    "    plot_and_calc('run3_max3748_open', 98, 103, ax=axs[1], ylabel=None, grid=False)\n",
    "    plot_and_calc('run3_tdp0604_open', 97, 100, ax=axs[2], ylabel=None, grid=False)\n",
    "    plot_and_calc('pi3hdx_clipline_risetime_new', 87, 90, ax=axs[3], ylabel=None, grid=False)\n",
    "    \n",
    "    axs[0].set_xlabel(None)\n",
    "    axs[1].set_xlabel(None)\n",
    "    axs[2].set_xlabel(None)\n",
    "    \n",
    "    # axs[0].set_title('74LVC1G157 MUX Gate with Complimentary Outputs')\n",
    "    # axs[1].set_title('MAX3748 Optical Network Limiting Amplifier')\n",
    "    # axs[2].set_title('TDP0604 6 Gb/s HDMI Hybrid Redriver')\n",
    "    # axs[3].set_title('PI3HDX12211 12 Gb/s HDMI Linear Redriver')\n",
    "    \n",
    "    axs[0].set_title('74LVC1G157')\n",
    "    axs[1].set_title('MAX3748')\n",
    "    axs[2].set_title('TDP0604')\n",
    "    axs[3].set_title('PI3HDX12211')\n",
    "    \n",
    "    for ax in axs:\n",
    "        ax.get_legend().set_loc('lower right')\n",
    "    \n",
    "    # Only do this on the last axis because of sharex=True\n",
    "    ax.set_xlim([-1.8, 1.8])\n",
    "    \n",
    "    #for ax in axs:\n",
    "    #    ax.grid()\n",
    "    fig.tight_layout()\n",
    "\n",
    "fig.savefig('../paper/fig_edge_risetime.pdf')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "f5fd14eb-a12a-42c0-944e-8fd53f7d4dbc",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x78807630a5d0>]"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 28004 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "\n",
    "# just out of curiousity:\n",
    "d = meas['pi3hdx_clipline_risetime']\n",
    "fig, ax = plt.subplots()\n",
    "ax.grid()\n",
    "ax.plot(d['adc1'] - d['adc2'])"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
