{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Following S. Datta, Lessons from Nanoelectronics B"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Sec. 19.1.1 of Datta, Lessons...B\n",
    "- see Lessons from Nanoelectronics: B, Sec. 17.4.1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from random import randint\n",
    "\n",
    "from fuNEGF.models import LinearChain"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "N = 100  # keep even ; the number of atoms in the channel; the (first, last) atom is in contact with the (left, right) channel\n",
    "eps_0 = 0  # eV; the on-site energ\n",
    "t = 1.0  # eV;  the hopping parameter\n",
    "a = 1.0  # Angstrom; the lattice constant\n",
    "\n",
    "N = 2 * (N // 2)  # make sure N is even"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Model definition\n",
    "\n",
    "### 1. Hamiltonian\n",
    "\n",
    "1D chain with nearest-neighbor hopping:\n",
    "\n",
    "$$ \\hat{H}_{ij} = \\begin{cases}\\epsilon_0, & \\text { if } i=j \\\\ t, & \\text { if } i \\neq j \\end{cases}$$\n",
    "\n",
    "or in matrix form\n",
    "\n",
    "$$ \\hat{H}=\\left[\\begin{array}{ccccc}\n",
    "\\epsilon_0 & t & 0 & 0 & \\cdots & 0 & 0 & 0 \\\\\n",
    "t & \\epsilon_0 & t & 0 & \\cdots & 0 & 0 & 0 \\\\\n",
    "0 & t & \\epsilon_0 & t & \\cdots & 0 & 0 & 0 \\\\\n",
    "0 & 0 & t & \\epsilon_0 & \\cdots & 0 & 0 & 0 \\\\\n",
    "\\vdots & \\vdots & \\vdots & \\vdots & \\ddots & \\vdots & \\vdots & \\vdots \\\\\n",
    "0 & 0 & 0 & 0 & \\cdots & \\epsilon_0 & t & 0 \\\\\n",
    " 0 & 0 & 0 & 0 & \\cdots & t & \\epsilon_0  & t \\\\\n",
    "0 & 0 & 0 & 0 & \\cdots & 0 & t & \\epsilon_0\n",
    "\\end{array}\\right]\n",
    "$$\n",
    "\n",
    "giving a dispersion relation (within an approximation -- periodic boundary conditions are actually *not* applied in the Hamiltonian above)\n",
    "\n",
    "$$ E (k) = \\epsilon + 2 t \\cos(k a)\\,, \\;\\;\\;\\;\\;\\; k \\in \\{ 0, 1, ..., N-1 \\} \\cdot 2\\pi/(N a) $$\n",
    "\n",
    "### 2. Self-energy\n",
    "\n",
    "Self-energies for contact 1 and 2\n",
    "\n",
    "$$\n",
    "\\Sigma_1=\\left[\\begin{array}{ccccc}\n",
    "\\mathrm{te}^{i k a} & 0 & 0 & \\cdots & 0 \\\\\n",
    "0 & 0 & 0 & \\cdots & 0 \\\\\n",
    "0 & 0 & 0 & \\cdots & 0 \\\\\n",
    "\\vdots & \\vdots & \\vdots & \\ddots & \\vdots \\\\\n",
    "0 & 0 & 0 & \\cdots & 0\n",
    "\\end{array}\\right], \\quad \\Sigma_2=\\left[\\begin{array}{ccccc}\n",
    "0 & \\cdots & 0 & 0 & 0 \\\\\n",
    "\\vdots & \\ddots & \\vdots & \\vdots & \\vdots \\\\\n",
    "0 & \\cdots & 0 & 0 & 0 \\\\\n",
    "0 & \\cdots & 0 & 0 & 0 \\\\\n",
    "0 & \\cdots & 0 & 0 & \\mathrm{te}^{i k a}\n",
    "\\end{array}\\right]\n",
    "$$\n",
    "\n",
    "with the broadening functions $\\Gamma \\equiv i\\left[ \\Sigma - \\Sigma^\\dagger\\right] $ \n",
    "\n",
    "$$\n",
    "\\Gamma_1=\\frac{\\hbar v}{a}\\left[\\begin{array}{ccccc}\n",
    "1 & 0 & 0 & \\cdots & 0 \\\\\n",
    "0 & 0 & 0 & \\cdots & 0 \\\\\n",
    "0 & 0 & 0 & \\cdots & 0 \\\\\n",
    "\\vdots & \\vdots & \\vdots & \\ddots & \\vdots \\\\\n",
    "0 & 0 & 0 & \\cdots & 0\n",
    "\\end{array}\\right], \\quad \\Gamma_2=\\frac{\\hbar v}{a}\\left[\\begin{array}{ccccc}\n",
    "0 & \\cdots & 0 & 0 & 0 \\\\\n",
    "\\vdots & \\ddots & \\vdots & \\vdots & \\vdots \\\\\n",
    "0 & \\cdots & 0 & 0 & 0 \\\\\n",
    "0 & \\cdots & 0 & 0 & 0 \\\\\n",
    "0 & \\cdots & 0 & 0 & 1\n",
    "\\end{array}\\right]\n",
    "$$\n",
    "\n",
    "where $v=\\mathrm{d} E /(\\hbar \\mathrm{d} k) = -2 a t / \\hbar \\sin (k a)$ so that $\\frac{\\hbar v}{a} = -2 t / \\sin (k a)$\n",
    "\n",
    "### 3. In-scattering\n",
    "$$ \\Sigma^\\mathrm{in}_{1,2} = \\Gamma_{1,2} \\cdot f_{1,2} $$\n",
    " where $f_i$ is the Fermi-Dirac distribution function for contact $i$."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## NEGF Equations\n",
    "\n",
    "The *retarded Green's function*\n",
    "$$\n",
    "\\mathbf{G}^{\\mathrm{R}}=[E \\mathbf{I}-\\mathbf{H}-\\mathbf{\\Sigma}]^{-1}\n",
    "$$\n",
    "\n",
    "along with the *advanced Green's function*\n",
    "$$\n",
    "    \\mathbf{G}^{\\mathrm{A}} = \\left[ \\mathbf{G}^{\\mathrm{R}} \\right]^\\dagger\n",
    "$$\n",
    "\n",
    "provide the *spectral function*\n",
    "$$\n",
    "\\mathbf{A}=i\\left[\\mathbf{G}^{\\mathrm{R}}-\\mathbf{G}^{\\mathrm{A}}\\right]\n",
    "$$\n",
    "\n",
    "and are used to solve for the *\"electron occupation\" Green's function* ($\\mathbf{G}^{\\mathrm{n}} \\equiv -i \\mathbf{G}^< $)\n",
    "$$\n",
    "\\mathbf{G}^{\\mathrm{n}}=\\mathbf{G}^{\\mathrm{R}} \\Sigma^{\\mathrm{in}} \\mathbf{G}^{\\mathrm{A}}\n",
    "$$\n",
    "\n",
    "which gives the *density matrix* \n",
    "\n",
    "$$\n",
    "    \\hat{\\rho} = \\mathbf{G}^{\\mathrm{n}} / 2\\pi \\,.\n",
    "$$\n",
    "\n",
    "\n",
    "Both, the self-energy $\\mathbf{\\Sigma}$ and the in-scattering term $\\Sigma^{\\mathrm{in}}$ are sums of the left contact and right contact, while the self-energy also contains an intrinsic term\n",
    "\n",
    "$$ \\begin{align}\n",
    "        \\mathbf{\\Sigma}^{\\mathrm{in}} &= \\mathbf{\\Sigma}^{\\mathrm{in}}_1 + \\mathbf{\\Sigma}^{\\mathrm{in}}_2 \\,, \\\\\n",
    "        \\mathbf{\\Sigma} &= \\mathbf{\\Sigma}_1 + \\mathbf{\\Sigma}_2 + \\mathbf{\\Sigma}_0 \n",
    "   \\end{align}        \n",
    "$$\n",
    "\n",
    "NOTE: We use the (physically expressive) notation of S. Datta, where the self-energies and Green's functions in relation to the standard notation (on the right) are defined as  \n",
    "\n",
    "$$\n",
    "\\begin{align}\n",
    "    \\Sigma &\\equiv \\Sigma^\\mathrm{R} \\,, \\\\\n",
    "    G^\\mathrm{n} &\\equiv -i G^< \\,, \\\\\n",
    "    \\Sigma^\\mathrm{in} &\\equiv -i \\Sigma^< \\,.\n",
    "\\end{align}\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Class definition\n",
    "- packaged into the \"fuNEGF\" package; imported in the beginning"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Case studies"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Construct (a pure) Hamiltonian"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 45096 characters]",
      "text/plain": [
       "<Figure size 650x350 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "chain = LinearChain(N, eps_0, t, a, H_impurity=None, plot_dispersion=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- when periodic Hamiltonian enforced, the analytical dispersion relation indeed gives a precise dependence (energy error $\\sim 10^{-15}$ eV)\n",
    "- the order of the diagonalized eigenvectors needs to be imposed: {0, 2, 4, ..., N, N-1, ...., 1}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Impurity - Quantum Resistors in Series\n",
    "- a constant on-site potential on a single (or multiple) sites"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "$$ \\hat{H}=\\left[\\begin{array}{ccccc}\n",
    "\\ddots & \\vdots & \\vdots & \\vdots & \\ddots \\\\\n",
    "\\cdots & \\varepsilon & t & 0 & \\cdots \\\\\n",
    "\\cdots & t & \\varepsilon+U & t & \\cdots \\\\\n",
    "\\cdots & 0 & t & \\varepsilon & \\cdots \\\\\n",
    "\\ddots & \\vdots & \\vdots & \\vdots & \\ddots\n",
    "\\end{array}\\right]\n",
    "$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 15184 characters]",
      "text/plain": [
       "<Figure size 550x150 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "H_impurity = np.zeros((N, N), dtype=complex)\n",
    "\n",
    "# (1) add a single impurity in the middle\n",
    "H_impurity[N // 2, N // 2] += -2 * t\n",
    "\n",
    "# (2) add two impurities symmetrically at <position_scaterrer> from each end of the linear chain\n",
    "# position_scaterrer = 18\n",
    "# H_impurity[position_scaterrer, position_scaterrer] += -t\n",
    "# H_impurity[-position_scaterrer, -position_scaterrer] += -t\n",
    "\n",
    "chain.add_H_impurity(H_impurity, plot_dispersion=False)\n",
    "\n",
    "chain.plot_onsite_energy()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Plot the *Transmission function*\n",
    "\n",
    "$\\bar{T}(E)=\\operatorname{Trace}\\left[\\boldsymbol{\\Gamma}_1 \\mathbf{G}^R \\boldsymbol{\\Gamma}_2 \\mathbf{G}^A\\right]$\n",
    "\n",
    "Cases:\n",
    "1. without impurities it is constant $T(E) = 1$ \n",
    "2. with a single impurity of $U=-2 t$, it reaches maximum of $T(E) = 0.5$\n",
    "3. with two impurities of $U=-t$ each, the function looks almost the same, but with strong oscillations"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "c:\\Python310\\lib\\site-packages\\matplotlib\\cbook\\__init__.py:1335: ComplexWarning: Casting complex values to real discards the imaginary part\n",
      "  return np.asarray(x, float)\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 18644 characters]",
      "text/plain": [
       "<Figure size 250x300 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "chain.plot_transmission()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Phase / momentum relaxation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_onsite_and_occupation(E_to_plot, D0_phase, D0_phase_momentum, N_sc):\n",
    "    fig, axes = plt.subplots(2, 1, figsize=(4.5, 6), sharex=True, height_ratios=[1, 2])\n",
    "    plt.suptitle(\n",
    "        r\"occupation at $E = \"\n",
    "        + f\"{E_to_plot:.2f}\"\n",
    "        + r\"$ eV\"\n",
    "        + \"\\n\"\n",
    "        + r\"phase relaxed with $D_0^\\mathrm{phase} = \"\n",
    "        + f\"{D0_phase:.2f}\"\n",
    "        + r\"$ eV$^2$ \"\n",
    "        + \"\\n\"\n",
    "        + r\"phase+momentum relaxed with $D_0^\\mathrm{ph,mom} = \"\n",
    "        + f\"{D0_phase_momentum:.2f}\"\n",
    "        + r\"$ eV$^2$ \",\n",
    "        fontsize=12,\n",
    "    )\n",
    "\n",
    "    chain.plot_onsite_energy(ax=axes[0])\n",
    "    chain.plot_occupation(\n",
    "        D0_phase, D0_phase_momentum, E_to_plot=E_to_plot, N_sc=N_sc, ax=axes[1]\n",
    "    )\n",
    "\n",
    "    # make x-axes common\n",
    "    axes[0].set_xlabel(\"\")\n",
    "    plt.tight_layout()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "E_to_plot = eps_0 + 0.5 * t\n",
    "N_sc = 70"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 1) No relaxation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 84232 characters]",
      "text/plain": [
       "<Figure size 450x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# no relaxation\n",
    "D0_phase = 0.00 * t**2\n",
    "D0_phase_momentum = 0.00 * t**2\n",
    "plot_onsite_and_occupation(E_to_plot, D0_phase, D0_phase_momentum, N_sc)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2. Phase relaxation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 54468 characters]",
      "text/plain": [
       "<Figure size 450x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# only phase\n",
    "D0_phase = 0.09 * t**2\n",
    "D0_phase_momentum = 0.00 * t**2\n",
    "plot_onsite_and_occupation(E_to_plot, D0_phase, D0_phase_momentum, N_sc)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 3. Phase and momentum relaxation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 53844 characters]",
      "text/plain": [
       "<Figure size 450x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# phase and momentum\n",
    "D0_phase = 0.09 * t**2\n",
    "D0_phase_momentum = 0.03 * t**2\n",
    "plot_onsite_and_occupation(E_to_plot, D0_phase, D0_phase_momentum, N_sc)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- after each barrier, the potential drops (when we put f=1 on the left and f=0 on the right)\n",
    "- wild oscillations in occupation can be conveniently damped by phase relaxation\n",
    "- additional momentum relaxation causes a steady decrease in the potential"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Multiple resistors"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 190052 characters]",
      "text/plain": [
       "<Figure size 1200x800 with 20 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# RELAXATION\n",
    "D0_phase = 0.12 * t**2\n",
    "D0_phase_momentum = 0.03 * t**2\n",
    "N_sc = 90\n",
    "\n",
    "# IMPURITY HAMILTONIANS\n",
    "H_imp_clean = np.zeros((N, N))\n",
    "H_imp_single = np.zeros((N, N))\n",
    "H_imp_single[N // 2, N // 2] = -2 * t\n",
    "H_imp_double = np.zeros((N, N))\n",
    "H_imp_double[5 * N // 12, 5 * N // 12] = -t\n",
    "H_imp_double[3 * N // 4, 3 * N // 4] = -t\n",
    "\n",
    "# multiple random impurities\n",
    "H_imp_multiple = np.zeros((N, N))\n",
    "N_random_impurities = randint(3, 7)\n",
    "potential_ratios = [randint(1, 5) for _ in range(N_random_impurities)]\n",
    "potentials = [\n",
    "    potential_ratios[i] / sum(potential_ratios) * (-2 * t)\n",
    "    for i in range(N_random_impurities)\n",
    "]\n",
    "positions = [randint(1, N - 1) for _ in range(N_random_impurities)]\n",
    "for i in range(N_random_impurities):\n",
    "    H_imp_multiple[positions[i], positions[i]] = potentials[i]\n",
    "\n",
    "H_impurities = [H_imp_clean, H_imp_single, H_imp_double, H_imp_multiple]\n",
    "\n",
    "impurity_titles = [\n",
    "    \"clean\",\n",
    "    r\"single impurity $U=-2t$\",\n",
    "    r\"double imp.  $2 \\times (U=-t)$\",\n",
    "    r\"random imps. (total $U=-2t$)\",\n",
    "]\n",
    "\n",
    "# CALCULATE AND PLOT\n",
    "fig, axes = plt.subplots(\n",
    "    5,\n",
    "    len(H_impurities),\n",
    "    figsize=(2.5 * len(H_impurities) + 2, 8),\n",
    "    height_ratios=[0.9, 1, 1, 1, 1.3],\n",
    "    layout=\"constrained\",\n",
    ")\n",
    "fig.set_constrained_layout_pads(\n",
    "    w_pad=0.35\n",
    ")  # , h_pad=4./72., hspace=0./72., wspace=0./72.)\n",
    "for i, H_imp in enumerate(H_impurities):\n",
    "    chain = LinearChain(N, eps_0, t, a, H_impurity=H_imp, plot_dispersion=False)\n",
    "    chain.plot_onsite_energy(axes[0, i])\n",
    "    chain.plot_occupation(\n",
    "        D0_phase=0, D0_phase_momentum=0, E_to_plot=E_to_plot, N_sc=N_sc, ax=axes[1, i]\n",
    "    )\n",
    "    chain.plot_occupation(\n",
    "        D0_phase=D0_phase,\n",
    "        D0_phase_momentum=0,\n",
    "        E_to_plot=E_to_plot,\n",
    "        N_sc=N_sc,\n",
    "        ax=axes[2, i],\n",
    "    )\n",
    "    chain.plot_occupation(\n",
    "        D0_phase=D0_phase,\n",
    "        D0_phase_momentum=D0_phase_momentum,\n",
    "        E_to_plot=E_to_plot,\n",
    "        N_sc=N_sc,\n",
    "        ax=axes[3, i],\n",
    "    )\n",
    "    chain.plot_transmission(axes[4, i])\n",
    "\n",
    "# labels, titles, limits, etc.\n",
    "axes[0, 0].set_ylabel(\"On-site (eV)\")\n",
    "axes[1, 0].set_ylabel(\"Occupation\")\n",
    "axes[2, 0].set_ylabel(\"Occupation\")\n",
    "axes[3, 0].set_ylabel(\"Occupation\")\n",
    "axes[4, 0].set_ylabel(\"Energy (eV)\")\n",
    "for j in range(1, len(H_impurities)):\n",
    "    for i in range(5):\n",
    "        axes[i, j].set_yticklabels([])\n",
    "        axes[i, j].set_ylabel(\"\")\n",
    "        axes[i, j].tick_params(axis=\"y\", direction=\"in\")\n",
    "# set all titles empty string\n",
    "for j in range(len(H_impurities)):\n",
    "    axes[4, j].set_xlabel(\"Transmission\")\n",
    "    for i in range(5):\n",
    "        axes[i, j].set_title(\"\")\n",
    "    axes[0, j].set_title(impurity_titles[j], fontsize=14)\n",
    "    for i in range(3):\n",
    "        axes[i, j].set_xticklabels([])\n",
    "        axes[i, j].set_xlabel(\"\")\n",
    "        # ticks inside\n",
    "        axes[i, j].tick_params(axis=\"x\", direction=\"in\")\n",
    "text_left = N // 2\n",
    "text_top = 0.8\n",
    "axes[1, 0].text(\n",
    "    text_left,\n",
    "    text_top,\n",
    "    \"no relaxation\" + r\"$\\rightarrow$\",\n",
    "    fontsize=12,\n",
    "    rotation=0,\n",
    "    ha=\"center\",\n",
    "    va=\"center\",\n",
    ")\n",
    "axes[2, 0].text(\n",
    "    text_left,\n",
    "    text_top,\n",
    "    \"phase\\nrelaxed\" + r\"$\\rightarrow$\",\n",
    "    fontsize=12,\n",
    "    rotation=0,\n",
    "    ha=\"center\",\n",
    "    va=\"center\",\n",
    ")\n",
    "axes[3, 0].text(\n",
    "    text_left,\n",
    "    text_top,\n",
    "    \"phase+momentum\\nrelaxed\" + r\"$\\rightarrow$\",\n",
    "    fontsize=12,\n",
    "    rotation=0,\n",
    "    ha=\"center\",\n",
    "    va=\"center\",\n",
    ")\n",
    "# all on-site energy plots have the same y-axis\n",
    "margin = 0.08\n",
    "y_max = max([axes[0, i].get_ylim()[1] for i in range(len(H_impurities))])\n",
    "y_min = min([axes[0, i].get_ylim()[0] for i in range(len(H_impurities))])\n",
    "for j in range(len(H_impurities)):\n",
    "    axes[0, j].set_ylim(\n",
    "        [y_min - margin * (y_max - y_min), y_max + margin * (y_max - y_min)]\n",
    "    )"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "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.10.0"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
