---
title: The EKV Normalized Functions
subtitle: For the Design of Analog Circuits (Version 1)
author: Christian Enz (christian.enz@epfl.ch)
affiliation: EPFL
date: today
execute:
  echo: false
  warning: false
toc: true
number-sections: true
number-depth: 3
highlight-style: pygments
bibliography: references.bib
csl: ieee.csl
format: 
  html:
    include-in-header: mathjax.html
    code-fold: true
    html-math-method: mathjax
    css: styles.css
  pdf:
    keep-tex: true
    pdf-engine: pdflatex
    documentclass: scrreprt
    papersize: a4
    geometry:
      - top=20mm
      - bottom=20mm
      - left=20mm
      - right=20mm
    include-in-header:
      text: |
        \usepackage{scrlayer-scrpage}
        \usepackage{datetime2}
        \DTMsetup{datesep=.}
        \DTMsetstyle{ddmmyyyy}
        \rohead[\thepage]{\thepage}
        \cohead[The EKV Normalized Functions]{The EKV Normalized Functions}
        \lofoot[$\copyright$ C. Enz]{$\copyright$ C. Enz}
        \cofoot[Analog IC Design]{Analog IC Design}
        \rofoot[\today]{\today}
---

```{python}
# Initialization

from ekv_functions import *

import pandas as pd
import matplotlib.pyplot as plt
import subprocess
import shutil
import os
import os.path as op
import sys
sys.path.append(".")
import re

from IPython.display import Markdown
from tabulate import tabulate

import scipy
kB=scipy.constants.Boltzmann
q=scipy.constants.e
T0=scipy.constants.zero_Celsius
T=300
UT=kB*T/q

plt.rcParams['svg.fonttype'] = 'none'
plt.rcParams['pdf.fonttype'] = 42
plt.rcParams['font.family'] = 'Arial'
plt.rcParams['mathtext.fontset'] = 'custom'
plt.rcParams['mathtext.rm'] = 'Arial'
plt.rcParams['mathtext.it'] = 'Arial:italic'
plt.rcParams['mathtext.bf'] = 'Arial:bold'

inkscapePath = r"C:\\Program Files\\Inkscape\bin\\inkscape.exe"
savePlotPath = "./Plots/"
#savePlots=True
savePlots=False

if savePlots:
  lw=1
  curvelw=2
  msize=6
  textFontSize=12
  BodePlotParams = {'figure.figsize': (6,6)}
  pltStyle='plt_style_medium.mplstyle'
else:
  lw=1
  curvelw=1
  msize=5
  textFontSize=9
  BodePlotParams = {'figure.figsize': (5,5)}
  twoPlotsParams = {'figure.figsize': (8,5)}
  pltStyle='plt_style_small.mplstyle'

plt.style.use(pltStyle)

circuitName="CS_OL_optimization_with_CF"
version = "_v1" # Allows you to save different versions
#newSim=True
newSim=False
refloat=r'.*?([+-]?\d+([.]\d*)?([eE][+-]?\d+)?|[.]\d+([eE][+-]?\d+)?)'

def saveFigures(savePath, plotName):
#  print("Patience, saving plot!")
  figFolder = savePath + r"\\{}.{}"
  pngFile = figFolder.format(plotName,"png")
  pdfFile = figFolder.format(plotName,"pdf")
  svgFile = figFolder.format(plotName,"svg")
  plt.savefig(pngFile, format="png", dpi=1200, bbox_inches="tight", transparent=True)
  plt.savefig(pdfFile, format="pdf", dpi=1200, bbox_inches="tight", transparent=True)
  plt.savefig(svgFile, format="svg", dpi=1200, bbox_inches="tight", transparent=True)
  cmd_emf = '"{}" "{}\\{}.svg" --export-filename="{}\\{}.emf" --export-overwrite'.format(inkscapePath, savePath, plotName, savePath, plotName)
#    cmd_emf = '"{}" "{}\{}.pdf" --export-filename="{}\{}.emf" --export-overwrite'.format(inkscapePath, savePath, plotName, savePath, plotName)
#    cmd_pdf = '"{}" "{}\{}.svg" --export-filename="{}\{}.pdf" --export-overwrite'.format(inkscapePath, savePath, plotName, savePath, plotName)
  subprocess.call(cmd_emf, shell=True)
#    subprocess.call(cmd_pdf, shell=True)
```

# Introduction
This notebook presents all the normlaized functions that are used for the design of analog circuits using the $G_m/I_D$ approach with the inversion coefficient @bib:enz:book:2006 @bib:ekv2p6:doc:rev2:1998 @bib:enz:sscmag:autumn:2017 @bib:enz:sscmag:winter:2017.

# Large-signal functions
## Normalized current versus charge
### Long-channel
The normalized drain current or inversion coefficient $IC$ is defined as the drain current in saturation normalized to the specific current $I_{spec}$
\begin{equation}
  IC \triangleq \frac{\left.I_{D}\right|_{\mathrm{saturation}}}{I_{spec}},
\end{equation}
where the specific current is given by
\begin{equation}
  I_{spec} = I_{spec\Box} \cdot \frac{W}{L}
\end{equation}
with
\begin{equation}
  I_{spec\Box} \triangleq 2n \cdot \mu \cdot C_{ox} \cdot U_T^2.
\end{equation}
The inversion coefficient $IC$ gives the level of inversion of the transistor according to
\begin{align}
  IC < 0.1 & \textsf{weak inversion (WI)},\\
  0.1 \leq IC < 10 & \textsf{moderate inversion (MI)},\\
  10 \leq IC & \textsf{strong inversion (SI)}.
\end{align}

The inversion coefficient for a long-channel transistor is a function of the normalized source charge according to
\begin{equation}\label{eqn:ic_qs}
  IC = q_s^2 + q_s
\end{equation}
where $q_s$ is the inversion charge $Q_i$ evaluated at the source and normalized to $Q_{spec} \triangleq -2 n C_{ox} U_T$
\begin{equation}
  q_s \triangleq \frac{Q_i(x=0)}{Q_{spec}}.
\end{equation}
The expression of $IC$ versus $q_s$ can be inverted to express the normalized charge as a function of the inversion coefficient according to
\begin{equation}
  q_s = \frac{\sqrt{4IC+1}-1}{2}
\end{equation}

### Short-channel
The normalized drain current in the simplified EKV (sEKV) model is given by
\begin{equation}\label{eqn:ic_qs_short}
  IC \triangleq \frac{\left.I_D\right|_{saturation}}{I_{spec}} = \frac{2(q_s^2+q_s)}{1 + \sqrt{1 + \lambda_c^2 (q_s^2+q_s)}},
\end{equation}
where parameter $\lambda_c$ is the velocity saturation parameter which scales as
\begin{equation}
  \lambda_c = \frac{L_{sat}}{L}
\end{equation}
where $L_{sat} = 2 \mu U_T/v_{sat}$ is the length over which the carriers velocity is saturating to $v_{sat}$. $\lambda_c$ is therefore the fraction of the channel over which the carriers are in full velocity saturation. The long-channel case is obtained by setting $\lambda_c = 0$ for which the normalized drain current \eqref{eqn:ic_qs_short} reduces to \eqref{eqn:ic_qs}.

The short-channel asymptotes are given by
\begin{equation}
  IC \cong
  \begin{cases}
    q_s & \textsf{in weak inversion ($\lambda_c \cdot q_s \ll 1$)},\\
    \frac{2 q_s}{\lambda_c} & \textsf{in strong inversion ($\lambda_c \cdot q_s \gg 1$)}.
  \end{cases}
\end{equation}

The inversion charge $IC$ is plotted versus $q_s$ in @fig-ic_qs for the long- and short-channel cases.

```{python}
#| label: fig-ic_qs
#| fig-cap: Inversion coefficient $IC$ versus normalized source charge $q_s$.

Nptsdec=20
logqsmin=-3
logqsmax=2
Ndec=logqsmax-logqsmin
Npts=Ndec*Nptsdec+1
qsmin=pow(10,logqsmin)
qsmax=pow(10,logqsmax)
qs=np.logspace(logqsmin,logqsmax,Npts,endpoint=True,base=10.0)
IC0=np.zeros(Npts)
IC1=np.zeros(Npts)
ICwi=np.zeros(Npts)
ICsi=np.zeros(Npts)
ICsivs=np.zeros(Npts)
lambdac=[0,0.5]

for k in range(0,Npts):
    IC0[k]=ic_q_short(qs[k],lambdac[0])
    IC1[k]=ic_q_short(qs[k],lambdac[1])
    ICwi[k]=qs[k]
    ICsi[k]=qs[k]**2
    ICsivs[k]=2*qs[k]/lambdac[1]

plt.loglog(qs, ICwi, 'k--', linewidth=lw)
plt.loglog(qs, ICsi, 'k--', linewidth=lw)
plt.loglog(qs, ICsivs, 'k--', linewidth=lw)
plt.loglog(qs, IC0, 'r', label='$\\lambda_c = $' + f'{lambdac[0]:.1f}')
plt.loglog(qs, IC1, 'b', label='$\\lambda_c = $' + f'{lambdac[1]:.1f}')
plt.xlim(qsmin,qsmax)
plt.xlabel('$q_s$')
plt.ylim(1E-3,1E3)
#plt.yticks(np.arange(0,1.2,0.1))
plt.ylabel('Inversion Coefficient $IC$')
plt.legend(loc='best')
plt.text(10, 100, '$\\cong q_s^2$', ha='left', va='bottom', size=textFontSize, rotation=55, rotation_mode='anchor')
plt.text(40, 150, '$\\cong 2q_s/\\lambda_c$', ha='center', va='bottom', size=textFontSize, rotation=31, rotation_mode='anchor')
plt.text(0.01, 0.01, '$\\cong q_s$', ha='center', va='bottom', size=textFontSize, rotation=31, rotation_mode='anchor')
if savePlots: saveFigures(savePlotPath, 'ic_qs')
plt.show()
```

## Normalized charge versus current
Equation \eqref{eqn:ic_qs_short} can be inverted to express the normalized source charge in function of the inversion coefficient
\begin{equation}\label{eqn:qs_ic_short}
  q_s = \frac{\sqrt{4\,IC+1+(\lambda_c\,IC)^2}-1}{2}
\end{equation}
which for long-channel ($\lambda_c=0$) reduces to
\begin{equation}\label{eqn:qs_ic}
  q_s =\frac{\sqrt{4\,IC+1}-1}{2}.
\end{equation}

The normalized source charge $q_s$ is plotted versus the inversion coefficient $IC$ in @fig-qs_ic for both the long- and short-channel cases.

```{python}
#| label: fig-qs_ic
#| fig-cap: Normalized source charge $q_s$ versus inversion coefficient $IC$.

Nptsdec=20
logICmin=-3
logICmax=3
Ndec=logICmax-logICmin
Npts=Ndec*Nptsdec+1
ICmin=pow(10,logICmin)
ICmax=pow(10,logICmax)
IC=np.logspace(logICmin,logICmax,Npts,endpoint=True,base=10.0)
qs0=np.zeros(Npts)
qs1=np.zeros(Npts)
qswi=np.zeros(Npts)
qssi=np.zeros(Npts)
qssivs=np.zeros(Npts)
lambdac=[0,0.5]

for k in range(0,Npts):
    qs0[k]=q_ic_short(IC[k],lambdac[0])
    qs1[k]=q_ic_short(IC[k],lambdac[1])
    qswi[k]=IC[k]
    qssi[k]=sqrt(IC[k])
    qssivs[k]=lambdac[1]*IC[k]/2

plt.loglog(IC, qswi, 'k--', linewidth=lw)
plt.loglog(IC, qssi, 'k--', linewidth=lw)
plt.loglog(IC, qssivs, 'k--', linewidth=lw)
plt.loglog(IC, qs0, 'r', label='$\\lambda_c = $' + f'{lambdac[0]:.1f}')
plt.loglog(IC, qs1, 'b', label='$\\lambda_c = $' + f'{lambdac[1]:.1f}')
plt.xlim(ICmin,ICmax)
plt.xlabel('Inversion Coefficient $IC$')
plt.ylim(1e-3,1e3)
plt.ylabel('$q_s$')
plt.legend(loc='best')
plt.text(400, 6, '$\\cong \\sqrt{IC}$', ha='center', va='bottom', size=textFontSize, rotation=23, rotation_mode='anchor')
plt.text(90, 25, '$\\cong \\lambda_c\\,IC/2$', ha='center', va='bottom', size=textFontSize, rotation=40, rotation_mode='anchor')
plt.text(0.01, 0.012, '$\\cong IC$', ha='center', va='bottom', size=textFontSize, rotation=40, rotation_mode='anchor')
if savePlots: saveFigures(savePlotPath, 'qs_ic')
plt.show()
```

## Normalized saturation voltage versus charge
The charge is related to the voltage according to
\begin{equation}\label{eqn:vps_qs}
  v_p-v_s = 2 q_s + \ln(q_s)
\end{equation}
which is plotted which is plotted in @fig-vps_ic.

```{python}
#| label: fig-vps_qs
#| fig-cap: Normalized saturation voltage $v_p-v_s$ versus normalized source charge $q_s$.

Nptsdec=20
logqsmin=-3
logqsmax=2
Ndec=logqsmax-logqsmin
Npts=Ndec*Nptsdec+1
qsmin=pow(10,logqsmin)
qsmax=pow(10,logqsmax)
qs=np.logspace(logqsmin,logqsmax,Npts,endpoint=True,base=10.0)
vps=np.zeros(Npts)
vpswi=np.zeros(Npts)
vpssi=np.zeros(Npts)
vpsvs0=np.zeros(Npts)
vpsvs1=np.zeros(Npts)
lambdac=[0,0.5]

for k in range(0,Npts):
  vps[k]=v_q(qs[k])
  vpswi[k]=ln(qs[k])
  vpssi[k]=2*qs[k]

plt.semilogx(qs, vpswi, 'k--', linewidth=lw)
plt.semilogx(qs, vpssi, 'k--', linewidth=lw)
plt.semilogx(qs, vps, 'r')
plt.xlim(qsmin,qsmax)
plt.xlabel('$q_s$')
plt.ylim(-10,60)
plt.yticks(np.arange(-10,70,10))
plt.ylabel('$v_p-v_s$')
if savePlots: saveFigures(savePlotPath, 'vps_qs')
plt.show()
```

## Normalized saturation voltage versus inversion coefficient
The source charge can be expressed in terms of the inversion coefficient as \eqref{eqn:qs_ic} for a long-channel transistor or \eqref{eqn:qs_ic_short}. Replacing in \eqref{eqn:vps_qs} allows to express the saturation voltage in terms of the inversion coefficient. For a long-channel transistor we get
\begin{equation}
  v_p-v_s = \ln(\sqrt{4 IC+1}-1) + \sqrt{4 IC+1} - 1 -\ln(2)
\end{equation}
The saturation voltage $v_p-v_s$ is plotted versus the inversion coefficient $IC$ in @fig-vps_ic for both the long- and short-channel case.

```{python}
#| label: fig-vps_ic
#| fig-cap: Normalized saturation voltage $v_p-v_s$ versus inversion coefficient $IC$.

Nptsdec=20
logICmin=-3
logICmax=3
Ndec=logICmax-logICmin
Npts=Ndec*Nptsdec+1
ICmin=pow(10,logICmin)
ICmax=pow(10,logICmax)
IC=np.logspace(logICmin,logICmax,Npts,endpoint=True,base=10.0)
vps=np.zeros(Npts)
vpswi=np.zeros(Npts)
vpssi=np.zeros(Npts)
vpsvs0=np.zeros(Npts)
vpsvs1=np.zeros(Npts)
lambdac=[0,0.5]

for k in range(0,Npts):
  vps[k]=vps_ic(IC[k])
  vpswi[k]=ln(IC[k])
  vpssi[k]= 2*sqrt(IC[k])
  vpsvs0[k]=vps_ic_short(IC[k],lambdac[0])
  vpsvs1[k]=vps_ic_short(IC[k],lambdac[1])

plt.semilogx(IC, vpswi, 'k--', linewidth=lw)
plt.semilogx(IC, vpssi, 'k--', linewidth=lw)
plt.semilogx(IC, vps, 'r', label='$\\lambda_c = $' + f'{lambdac[0]:.1f}')
#plt.semilogx(IC, vpsvs0, 'b--', label='$\\lambda_c = $' + f'{lambdac[0]:.1f}')
plt.semilogx(IC, vpsvs1, 'b', label='$\\lambda_c = $' + f'{lambdac[1]:.1f}')

plt.xlim(ICmin,ICmax)
plt.xlabel('Inversion Coefficient $IC$')
plt.ylim(-10,60)
plt.yticks(np.arange(-10,70,10))
plt.ylabel('$v_p-v_s$')
plt.legend(loc='best')
if savePlots: saveFigures(savePlotPath, 'vps_ic')
plt.show()
```

## Normalized charge versus saturation voltage
The voltage versus charge equation \eqref{eqn:vps_qs} can actually be inverted using the Lambert W-function of order 0. The Lambert function $W(z)$ is defined as the function satisfying
\begin{equation}
  W(z) \cdot e^{W(z)} = z.
\end{equation}
The voltage versus charge equation can be written as
\begin{equation}\label{eqn:q_v}
  2q \cdot e^{2q} = e^v
\end{equation}
where $q \triangleq  q_s$ and $v \triangleq v_p-v_s$. Eqn. \eqref{eqn:q_v} can now be solved for $q$ using the Lambert W-function by setting $z=2e^v$ which leads to
\begin{equation}
  q(v) =\frac{1}{2} W\left(2e^v\right)
\end{equation}
or
\begin{equation}
q_s = \frac{1}{2} W\left(2 e^{v_p-v_s}\right).
\end{equation}
The Lambert W-function is available in the *scipy* Python package as *lambertw*. It is also available in Mathematic as the ProductLog[z] function.

The charge versus voltage can also be approximated by the EKV function which is compared to the exact Lambert W-function in @fig-qs_vps.

```{python}
#| label: fig-qs_vps
#| fig-cap: Normalized source charge $q_s$ versus normalized saturation voltage $v_p-v_s$.

Npts=101
vpsmin=-10
vpsmax=60
vps=np.linspace(vpsmin,vpsmax,Npts,endpoint=True)
qlam=np.zeros(Npts)
qapp=np.zeros(Npts)

for k in range(0,Npts):
    qlam[k]=q_v_lambert(vps[k])
    qapp[k]=q_v(vps[k])

plt.semilogy(vps,qlam,'ro',markevery=2,markersize=4,label='Lambert W-function')
plt.semilogy(vps,qapp,'b-',label='Approximation')
plt.xlim(vpsmin,vpsmax)
plt.xticks(np.arange(-10,70,10))
plt.xlabel('$v_p-v_s$')
plt.ylim(1e-5,1e2)
plt.yticks([1e-5,1e-4,1e-3,1e-2,1e-1,1e0,1e1,1e2])
plt.ylabel('$q_s$')
plt.legend(loc='best')
if savePlots: saveFigures(savePlotPath, 'qs_vps')
plt.show()
```

## Inversion coefficient versus saturation voltage
Finally, the inversion charge $IC$ is plotted versus the saturation voltage $v_p-v_s$ in @fig-ic_vps for both the long- and short-channel cases. We see that velocity saturation reduces the current in strong inversion but has no effect in weak inversion.

```{python}
#| label: fig-ic_vps
#| fig-cap: Inversion coefficient versus normalized saturation voltage $v_p-v_s$.

Npts=101
vpsmin=-10
vpsmax=60
vps=np.linspace(vpsmin,vpsmax,Npts,endpoint=True)
IClam0=np.zeros(Npts)
ICapp0=np.zeros(Npts)
IClam1=np.zeros(Npts)
ICapp1=np.zeros(Npts)
lambdac=[0,0.5]

for k in range(0,Npts):
    IClam0[k]=ic_vps_lambert(vps[k],lambdac[0])
    ICapp0[k]=ic_vps_short(vps[k],lambdac[0])
    IClam1[k]=ic_vps_lambert(vps[k],lambdac[1])
    ICapp1[k]=ic_vps_short(vps[k],lambdac[1])

plt.semilogy(vps,IClam0,'ro',markevery=2,markersize=4,label='Lambert W-function with $\\lambda_c=$' + f'{lambdac[0]:.1f}')
plt.semilogy(vps,ICapp0,'r-',label='Approximation with $\\lambda_c=$' + f'{lambdac[0]:.1f}')
plt.semilogy(vps,IClam1,'bo',markevery=2,markersize=4,label='Lambert W-function with $\\lambda_c=$' + f'{lambdac[1]:.1f}')
plt.semilogy(vps,ICapp1,'b-',label='Approximation with $\\lambda_c=$' + f'{lambdac[1]:.1f}')
plt.xlim(vpsmin,vpsmax)
plt.xticks(np.arange(-10,70,10))
plt.xlabel('$v_p-v_s$')
plt.ylim(1e-3,1e3)
plt.ylabel('$IC$')
plt.legend(loc='best')
if savePlots: saveFigures(savePlotPath, 'idsat_vps_lambert')
plt.show()
```

## Drain-to-source saturation voltage versus inversion coefficient
The $v_p-v_s$ voltage works well in strong inversion to estimate the saturation voltage for a long-channel transistor. However, it becomes negative in weak inversion and cannot be used for estimating the saturation voltage in weak inversion which should be equal to a few $U_T$, typically $4\,U_T$. An approximation of the saturation voltage valid from weak to strong inversion can be defined as
\begin{equation}
  v_{ds_{sat}} = 2\,\sqrt{IC + v_{ds_{sat,wi}}},
\end{equation}
where $v_{ds_{sat,wi}} \cong 4$.

```{python}
#| label: fig-vdssat
#| fig-cap: Saturation voltage $v_{dssat}$ versus inversion coefficient $IC$.

Nptsdec=20
logICmin=-3
logICmax=3
Ndec=logICmax-logICmin
Npts=Ndec*Nptsdec+1
ICmin=pow(10,logICmin)
ICmax=pow(10,logICmax)
IC=np.logspace(logICmin,logICmax,Npts,endpoint=True,base=10.0)
vdssat=np.zeros(Npts)
vps=np.zeros(Npts)

for k in range(0,Npts):
    vdssat[k]=vdssat_ic(IC[k])
    vps[k]=vps_ic(IC[k])

plt.plot([ICmin,ICmax],[4,4],'k--')
plt.semilogx(IC, vdssat, 'r-', label='$v_{dssat}$')
plt.semilogx(IC, vps, 'b--', label='$v_p-v_s$')
plt.xlim(ICmin,ICmax)
plt.xlabel('$IC$')
plt.ylim(-10,60)
#plt.yticks(np.arange(0,1.2,0.1))
plt.ylabel('$v_{dssat}$')
plt.legend(loc='best')
if savePlots: saveFigures(savePlotPath, 'vdssat_ic')
plt.show()
```

## Inversion coefficient versus drain-to-source saturation voltage

```{python}
#| label: fig-ic_vdssat
#| fig-cap: Inversion coefficient $IC$ versus saturation voltage $v_{dssat}$.

Npts=101
vdssatmin=0
vdssatmax=60
vdssat=np.linspace(vdssatmin,vdssatmax,Npts,endpoint=True)
ICsat=np.zeros(Npts)
ICvps=np.zeros(Npts)

for k in range(0,Npts):
    ICsat[k]=ic_vdssat(vdssat[k])
    ICvps[k]=ic_vps(vdssat[k])

plt.plot([4,4],[1e-3,1e3],'k--')
plt.semilogy(vdssat,ICsat,'r-')
plt.semilogy(vdssat,ICvps,'b--')
plt.xlim(vdssatmin,vdssatmax)
plt.xlabel('$v_{dssat}$')
#plt.xticks(np.arange(0,22,2))
plt.ylim(1e-3,1e3)
plt.ylabel('$IC$')
if savePlots: saveFigures(savePlotPath, 'ic_vdssat')
plt.show()
```

## Slope factor versus inversion coefficient

The slope factor $n$ in weak inversion is actually depending on the pinch-off voltage according to

\begin{equation}
  n = n_{wi} = 1 + \frac{\Gamma_b}{2\sqrt{\Psi_0 + V_P}} = 1 + \frac{\gamma_b}{2\sqrt{\psi_0 + v_p}}
\end{equation}

where $\Gamma_b$ is the substrate factor given by

\begin{equation}
  \Gamma_b = \gamma_b \cdot \sqrt{U_T} = \frac{\sqrt{2qN_b \epsilon_{Si}}}{C_{ox}}
\end{equation}

and $\Psi_0 \cong 2\Phi_F + \text{ a few } U_T$. The slope factor normally depends on the pich-off voltage which depends on the gate voltage. For $v_s=0$, we can express the $v_p$ as a function of $IC$ and plot the slope factor versus $IC$.

```{python}
#| label: fig-n_ic
#| fig-cap: Slope factor $n$ versus inversion coefficient $IC$.

Nptsdec=20
logICmin=-3
logICmax=3
Ndec=logICmax-logICmin
Npts=Ndec*Nptsdec+1
ICmin=pow(10,logICmin)
ICmax=pow(10,logICmax)
IC=np.logspace(logICmin,logICmax,Npts,endpoint=True,base=10.0)
nwi=np.zeros(Npts)

#GAMMA=0.8 #0.645
#PHI=1 #0.86
#n0=1+GAMMA/(2*sqrt(PHI))
#gammab=GAMMA/sqrt(UT)
#psi0=PHI/UT
gammab=4
psi0=30
n0=1+gammab/(2*sqrt(psi0))

for k in range(0,Npts):
    nwi[k]=n_ic(IC[k],gammab,psi0)

plt.semilogx(IC,nwi,'r')
plt.plot([ICmin,ICmax],[n0,n0],'k--',linewidth=lw)
plt.xlim(ICmin,ICmax)
plt.xlabel('Inversion Coefficient $IC$')
plt.ylim(1,1.5)
#plt.yticks(np.arange(1,1.5,0.1))
plt.ylabel('Slope factor')
textstr = '\n'.join((
    f'$U_T =$ {UT*1e3:.1f} mV',
    f'$\\gamma_b =$ {gammab:.0f}',
    f'$\\psi_0 =$ {psi0:.0f}',
    f'$n_0 =$ {n0:.2f}'))
plt.text(0.05, 0.07, textstr, ha='left', va='bottom', size=textFontSize, transform=plt.gca().transAxes,
         bbox={'facecolor':'w', 'alpha':1.0, 'pad':5})
#plt.legend(loc='best')
if savePlots: saveFigures(savePlotPath, 'n_ic')
plt.show()
```

# Small-signal functions
It can be shown that for a long-channel transistor the source transconductance $G_{ms}$ is actually proportional to the inversion charge taken at the source $Q_i(x=0)$ or to the normalized source charge $q_s$. The normalized source transconductance $g_{ms}$ can therefore be expressed in terms of the inversion coefficient $IC$ according to
\begin{equation}
  g_{ms} \triangleq \frac{G_{ms}}{G_{spec}} = q_s(IC) = \frac{\sqrt{4IC+1}-1}{2},
\end{equation}
where the specific conductance $G_{spec}$ is defined as
\begin{equation}
  G_{spec} = \frac{I_{spec}}{U_T} = 2n \cdot \mu \cdot C_{ox} \cdot U_T.
\end{equation}

If velocity saturation is accounted for the normalized source transconductance becomes
\begin{equation}
  g_{ms} = \frac{\sqrt{4\,IC+1+(\lambda_c\,IC)^2}-1}{2+\lambda_c^2\,IC}.
\end{equation}

The normalized source transconductance in weak and in strong inversion reduces to
\begin{equation}
  g_{ms} =
  \begin{cases}
    IC & \textsf{in weak inversion ($IC \ll 1$)},\\
    \frac{1}{\lambda_c} & \textsf{in strong inversion ($IC \gg 1$)}.
  \end{cases}
\end{equation}
The normalized source transconductance $g_{ms}$ is plotted versus the inversion coefficient in @fig-gms_ic for both the long- and short-channel cases. We see that the normalized source transconductance saturates to $1/\lambda_c$ in SI.

```{python}
#| label: fig-gms_ic
#| fig-cap: Normalized source transconductance $g_{ms}$ versus inversion coefficient $IC$.

Nptsdec=20
logICmin=-3
logICmax=3
Ndec=logICmax-logICmin
Npts=Ndec*Nptsdec+1
ICmin=pow(10,logICmin)
ICmax=pow(10,logICmax)
IC=np.logspace(logICmin,logICmax,Npts,endpoint=True,base=10.0)
gms0=np.zeros(Npts)
gms1=np.zeros(Npts)
gms2=np.zeros(Npts)
lambdac=[0,0.2,0.5]

for k in range(0,Npts):
    gms0[k]=gms_ic_short(IC[k],lambdac[0])
    gms1[k]=gms_ic_short(IC[k],lambdac[1])
    gms2[k]=gms_ic_short(IC[k],lambdac[2])

plt.plot([ICmin,ICmax],[1/lambdac[1],1/lambdac[1]],'k--',linewidth=lw)
plt.plot([ICmin,ICmax],[1/lambdac[2],1/lambdac[2]],'k--',linewidth=lw)
plt.plot([ICmin,1],[ICmin,1],'k--',linewidth=lw)
plt.plot([1,ICmax],[1,sqrt(ICmax)],'k--',linewidth=lw)
plt.plot([ICmin,1],[1,1],'k--',linewidth=lw)
plt.plot([1,1],[1e-3,1],'k--',linewidth=lw)
plt.loglog(IC, gms0, 'r', label='$\\lambda_c = $' + f'{lambdac[0]:.1f}')
plt.loglog(IC, gms1, 'b', label='$\\lambda_c = $' + f'{lambdac[1]:.1f}')
plt.loglog(IC, gms2, 'g', label='$\\lambda_c = $' + f'{lambdac[2]:.1f}')
plt.xlim(ICmin,ICmax)
plt.xlabel('Inversion Coefficient $IC$')
plt.ylim(1E-3,1E2)
plt.ylabel('$g_{ms}$')
plt.text(0.03, 0.03, '$\\propto IC$', ha='center', va='bottom', size=textFontSize, rotation=37, rotation_mode='anchor')
plt.text(300, 20, '$\\propto \\sqrt{IC}$', ha='center', va='bottom', size=textFontSize, rotation=25, rotation_mode='anchor')
plt.text(1.2E3, 1/lambdac[1], '$1/\\lambda_c$ = ' + f'{1/lambdac[1]:.0f}', ha='left', va='center', size=textFontSize)
plt.text(1.2E3, 1/lambdac[2], '$1/\\lambda_c$ = ' + f'{1/lambdac[2]:.0f}', ha='left', va='center', size=textFontSize)
plt.legend(loc='lower right')
if savePlots: saveFigures(savePlotPath, 'gms_ic')
plt.show()
```

## Inversion coefficient versus transconductance
For long-channel transistor, it is easy to invert the normalized source transconductance to express the inversion coefficient $IC$ required to achieve a given normalized source transconductance
\begin{equation}\label{eqn:ic_gms}
  IC = g_{ms} \cdot (g_{ms}+1).
\end{equation}
The formula becomes much more complicated when including velocity saturation. For $\lambda_c > 0$ we get
\begin{equation}\label{eqn:ic_gms_short}
  IC = \frac{2-\lambda_c^2\,g_{ms}\,(1+2\,g_{ms})-\sqrt{4-(\lambda_c\,g_{ms})^2\,(4-\lambda_c^2)}}
  {\lambda_c^2\,((\lambda_c\,g_{ms})^2 - 1)},
\end{equation}
which for $\lambda_c \rightarrow 0$ reduces to \eqref{eqn:ic_gms}.

$IC$ is plotted versus $g_{ms}$ in @fig-ic_gms for the log- and short-channel cases.

```{python}
#| label: fig-ic_gms
#| fig-cap: Normalized source transconductance $g_{ms}$ versus inversion coefficient $IC$.

Nptsdec=100
loggmsmin=-3
loggmsmax=1
Ndec=loggmsmax-loggmsmin
Npts=Ndec*Nptsdec+1
gmsmin=pow(10,loggmsmin)
gmsmax=pow(10,loggmsmax)
lambdac=[0,0.2,0.5]
gmsmax1=1/lambdac[1]
gmsmax2=1/lambdac[2]
gms0=np.logspace(loggmsmin,loggmsmax,Npts,endpoint=True,base=10.0)
gms1=np.logspace(loggmsmin,log(gmsmax1),Npts,endpoint=False,base=10.0)
gms2=np.logspace(loggmsmin,log(gmsmax2),Npts,endpoint=False,base=10.0)
IC0=np.zeros(Npts)
IC1=np.zeros(Npts)
IC2=np.zeros(Npts)

for k in range(0,Npts):
    IC0[k]=ic_gms(gms0[k])
    IC1[k]=ic_gms_short(gms1[k],lambdac[1])
    IC2[k]=ic_gms_short(gms2[k],lambdac[2])

plt.plot([gmsmin,1],[gmsmin,1],'k--',linewidth=lw)
plt.plot([1,gmsmax],[1,gmsmax**2],'k--',linewidth=lw)
plt.plot([gmsmax1,gmsmax1],[gmsmin,1e2],'k--',linewidth=lw)
plt.plot([gmsmax2,gmsmax2],[gmsmin,1e2],'k--',linewidth=lw)
plt.plot([gmsmin,1],[1,1],'k--',linewidth=lw)
plt.plot([1,1],[1e-3,1],'k--',linewidth=lw)
plt.loglog(gms0, IC0, 'r', label='$\lambda_c = $' + f'{lambdac[0]:.1f}')
plt.loglog(gms1, IC1, 'b', label='$\lambda_c = $' + f'{lambdac[1]:.1f}')
plt.loglog(gms2, IC2, 'g', label='$\lambda_c = $' + f'{lambdac[2]:.1f}')
plt.xlim(gmsmin,gmsmax)
plt.xlabel('$g_{ms}$')
plt.ylim(1e-3,1e2)
plt.ylabel('Inversion Coefficient $IC$')
#plt.text(0.03, 0.03, '$\propto IC$', ha='center', va='bottom', size=15,
#        rotation=37, rotation_mode='anchor')
#plt.text(300, 20, '$\propto \sqrt{IC}$', ha='center', va='bottom', size=15,
#        rotation=25, rotation_mode='anchor')
plt.text(1/lambdac[1], 8E-4, '$1/\lambda_c$', ha='center', va='top', size=textFontSize)
plt.text(1/lambdac[2], 8E-4, '$1/\lambda_c$', ha='center', va='top', size=textFontSize)
plt.legend(loc='upper left')
if savePlots: saveFigures(savePlotPath, 'ic_gms_short')
plt.show()
```

## Normalized $G_m/I_D$ versus inversion coefficient

The normalized $G_m/I_D$ for the long-channel transistor can then be expressed in terms of the inversion coefficient as
\begin{equation}
  \frac{G_m \cdot n \, U_T}{I_D} = \frac{G_{ms} \cdot U_T}{I_D} = \frac{g_{ms}(IC)}{IC} = \frac{\sqrt{4IC+1}-1}{2 IC}
\end{equation}

For short-channel devices it becomes
\begin{equation}
  \frac{G_m \cdot n \, U_T}{I_D} = \frac{G_{ms} \cdot U_T}{I_D} = \frac{g_{ms}}{IC}
  = \frac{\sqrt{4\,IC+1+(\lambda_c\,IC)^2}-1}{IC\,(2+\lambda_c^2\,IC)}.
\end{equation}

The normalized source transconductance in strong inversion reduces to
\begin{equation}
  \frac{g_{ms}}{IC} =
  \begin{cases}
    \frac{1}{\sqrt{IC}} & \text{for long-channel ($\lambda_c = 0$)},\\
    \frac{1}{\lambda_c \cdot IC} & \text{for short-channel ($\lambda_c > 0$)}.
  \end{cases}
\end{equation}

The normalized $G_m/I_D$ function is plotted below versus $IC$.

```{python}
#| label: fig-gmsid_ic
#| fig-cap: Normalized $G_m/I_D$ versus inversion coefficient $IC$.

Nptsdec=20
logICmin=-3
logICmax=3
Ndec=logICmax-logICmin
Npts=Ndec*Nptsdec+1
ICmin=pow(10,logICmin)
ICmax=pow(10,logICmax)
IC=np.logspace(logICmin,logICmax,Npts,endpoint=True,base=10.0)
gm_over_id0=np.zeros(Npts)
gm_over_id1=np.zeros(Npts)
gm_over_id2=np.zeros(Npts)
lambdac=[0,0.2,0.5]
Nfcts = len(lambdac)

for k in range(0,Npts):
    gm_over_id0[k]=gmsid_ic_short(IC[k],lambdac[0])
    gm_over_id1[k]=gmsid_ic_short(IC[k],lambdac[1])
    gm_over_id2[k]=gmsid_ic_short(IC[k],lambdac[2])

plt.rcParams.update(BodePlotParams)
fig, axs = plt.subplots(2, sharex=True)

axs[0].semilogx(IC, gm_over_id0, 'r', label='$\\lambda_c = $' + f'{lambdac[0]:.1f}')
axs[0].semilogx(IC, gm_over_id1, 'b', label='$\\lambda_c = $' + f'{lambdac[1]:.1f}')
axs[0].semilogx(IC, gm_over_id2, 'g', label='$\\lambda_c = $' + f'{lambdac[2]:.1f}')
#axs[0].set_xlabel('Inversion Coefficient')
axs[0].set_xlim(ICmin,ICmax)
axs[0].set_ylabel('$G_m \\cdot n \\, U_T/I_D$')
axs[0].set_ylim(0,1.1)  
axs[0].legend(loc='upper right')

axs[1].plot([ICmin,1/lambdac[1]],[1,1],'k--',linewidth=lw)
axs[1].plot([1,ICmax],[1,1/sqrt(ICmax)],'k--',linewidth=lw)
axs[1].plot([1/lambdac[1],ICmax],[1,1/(lambdac[1]*ICmax)],'k--',linewidth=lw)
axs[1].plot([1/lambdac[2],ICmax],[1,1/(lambdac[2]*ICmax)],'k--',linewidth=lw)
axs[1].plot([1/lambdac[1],1/lambdac[1]],[1E-3,1],'k--',linewidth=lw)
axs[1].plot([1/lambdac[2],1/lambdac[2]],[1E-3,1],'k--',linewidth=lw)

axs[1].loglog(IC, gm_over_id0, 'r', label='$\\lambda_c = $' + f'{lambdac[0]:.1f}')
axs[1].loglog(IC, gm_over_id1, 'b', label='$\\lambda_c = $' + f'{lambdac[1]:.1f}')
axs[1].loglog(IC, gm_over_id2, 'g', label='$\\lambda_c = $' + f'{lambdac[2]:.1f}')
axs[1].set_xlabel('Inversion Coefficient')
axs[1].set_xlim(ICmin,ICmax)
axs[1].set_ylabel('$G_m \\cdot n \\, U_T/I_D$')
axs[1].set_ylim(1E-3,2)
axs[1].legend(loc='lower left')
axs[1].annotate('$\\propto 1/\\sqrt{IC}$',
                xy=(ICmin,1E-3), xycoords='data',
                xytext=(1.3E2, 0.1), textcoords='data',
                ha='left', va='bottom', size=textFontSize,
                rotation=-33, rotation_mode='anchor')
axs[1].annotate('$\\propto 1/(\\lambda_c \\cdot IC)$',
                xy=(ICmin,1E-3), xycoords='data',
                xytext=(1.3E2, 0.04), textcoords='data',
                ha='left', va='bottom', size=textFontSize,
                rotation=-50, rotation_mode='anchor')
axs[1].annotate('1/$\\lambda_c$',
                xy=(ICmin,1E-3), xycoords='data',
                xytext=(1/lambdac[1], 0.8E-3), textcoords='data',
                ha='center', va='top', size=textFontSize)
axs[1].annotate('$1/\\lambda_c$',
                xy=(ICmin,1E-3), xycoords='data',
                xytext=(1/lambdac[2], 0.8E-3), textcoords='data',
                ha='center', va='top', size=textFontSize)
plt.subplots_adjust(hspace=0)
if savePlots: saveFigures(savePlotPath, 'gmsid_ic')
plt.show()
plt.style.use(pltStyle)
```

## Inversion coefficient versus normalized $G_m/I_D$
For long-channel transistors, the normalized $G_m/I_D$ can be inverted to express the inversion coefficient as a function of the desired $G_m/I_D$. This results in
\begin{equation}
  IC = \frac{1-gmsid}{gmsid^2},
\end{equation}
where $gmsid = G_m n U_T/I_D$.

$IC$ is plotted versus g_{ms}/i_d$ in @fig-ic_gmsid.

```{python}
#| label: fig-ic_gmsid
#| fig-cap: Inversion coefficient $IC$ versus normalized $G_m/I_D$.

Nptsdec=500
loggmsidmin=-3
loggmsidmax=0
Ndec=loggmsidmax-loggmsidmin
Npts=Ndec*Nptsdec+1
gmsidmin=pow(10,loggmsidmin)
gmsidmax=pow(10,loggmsidmax)
gmsid=np.logspace(loggmsidmin,loggmsidmax,Npts,endpoint=True,base=10.0)
IC=np.zeros(Npts)

for k in range(0,Npts):
    IC[k]=ic_gmsid(gmsid[k])

#plt.plot([gmsmin,1],[gmsmin,1],'k--',linewidth=linew)
#plt.plot([1,gmsmax],[1,gmsmax**2],'k--',linewidth=linew)
#plt.plot([gmsmax1,gmsmax1],[gmsmin,1E2],'k--',linewidth=linew)
#plt.plot([gmsmax2,gmsmax2],[gmsmin,1E2],'k--',linewidth=linew)
plt.semilogy(gmsid, IC, 'r')
plt.xlim(gmsidmin,gmsidmax)
plt.xticks(np.arange(0,1.1,0.1))
plt.xlabel('$g_{ms}/i_d$')
plt.ylim(1e-3,1e3)
plt.ylabel('Inversion Coefficient $IC$')
#plt.legend(loc='upper right')
if savePlots: saveFigures(savePlotPath, 'ic_gmsid')
plt.show()
```

There is unfortunately no closed-form expression for the inversion coefficient $IC$ as a function of the normalized  $G_m/I_D$ when including velocity saturation. However, it can be solved numerically using fsolve.

```{python}
#| label: fig-ic_gmsid_short
#| fig-cap: Inversion coefficient $IC$ versus normalized $G_m/I_D$.

from scipy.optimize import fsolve
def g(ic, *args):
    gmsid=args[0]
    lambdac=args[1]
    return gmsid_ic_short(ic,lambdac)-gmsid

Npts=1001
lambdac=[0,0.2,0.5]
gmsidmin=gmsid_ic_short(1e3,lambdac[2])
gmsidmax=1
gmsid=np.linspace(gmsidmin,gmsidmax,Npts,endpoint=True)
IC0=np.zeros(Npts)
IC1=np.zeros(Npts)
IC2=np.zeros(Npts)

for k in range(0,Npts):
    if k==0:
        ICguess1=1E3
        ICguess2=1E3
    else:
        ICguess1=IC1[k-1]
        ICguess2=IC2[k-1]
    IC0[k]=ic_gmsid(gmsid[k])
    IC1[k]=fsolve(g,ICguess1,(gmsid[k],lambdac[1]))[0]
    IC2[k]=fsolve(g,ICguess2,(gmsid[k],lambdac[2]))[0]

#plt.plot([gmsmin,1],[gmsmin,1],'k--',linewidth=linew)
#plt.plot([1,gmsmax],[1,gmsmax**2],'k--',linewidth=linew)
#plt.plot([gmsmax1,gmsmax1],[gmsmin,1E2],'k--',linewidth=linew)
#plt.plot([gmsmax2,gmsmax2],[gmsmin,1E2],'k--',linewidth=linew)
plt.semilogy(gmsid, IC0, 'r', label='$\\lambda_c = $' + f'{lambdac[0]:.1f}')
plt.semilogy(gmsid, IC1, 'b', label='$\\lambda_c = $' + f'{lambdac[1]:.1f}')
plt.semilogy(gmsid, IC2, 'g', label='$\\lambda_c = $' + f'{lambdac[2]:.1f}')
plt.xlim(gmsidmin,gmsidmax)
plt.xticks(np.arange(0,1.1,0.1))
plt.xlabel('$g_{ms}/i_d$')
plt.ylim(1e-3,1e3)
plt.ylabel('Inversion Coefficient $IC$')
plt.legend(loc='upper right')
if savePlots: saveFigures(savePlotPath, 'gmsid_ic_short')
plt.show()
```

## Noise functions
### Thermal noise

```{python}
#| label: fig-gamman_ic
#| fig-cap: Thermal noise excess factor $\gamma_n$ versus inversion coefficient $IC$.

Nptsdec=20
logICmin=-3
logICmax=3
Ndec=logICmax-logICmin
Npts=Ndec*Nptsdec+1
ICmin=pow(10,logICmin)
ICmax=pow(10,logICmax)
IC=np.logspace(logICmin,logICmax,Npts,endpoint=True,base=10.0)
n=np.zeros(Npts)
gamman0=np.zeros(Npts)
gamman1=np.zeros(Npts)

#GAMMA=0.8 #0.645
#PHI=1 #0.86
#n0=1+GAMMA/(2*sqrt(PHI))
#gammab=GAMMA/sqrt(UT)
#psi0=PHI/UT
gammab=4
psi0=30
n0=1+gammab/(2*sqrt(psi0))
gammansi=2*n0/3
gammanwi=n0/2

for k in range(0,Npts):
    n[k]=n_ic(IC[k],gammab,psi0)
    gamman0[k]=gamman_ic(IC[k],n0)
    gamman1[k]=gamman_ic(IC[k],n[k])

plt.semilogx([ICmin,ICmax], [gammansi,gammansi], 'k--', linewidth=lw)
plt.semilogx([ICmin,ICmax], [gammanwi,gammanwi], 'k--', linewidth=lw)
plt.semilogx(IC, gamman0, 'r', label='constant $n$')
plt.semilogx(IC, gamman1, 'b', label='Variable $n$')
plt.xlabel('Inversion Coefficient')
plt.xlim(ICmin,ICmax)
plt.ylabel('$\\gamma_n$')
plt.ylim(0,1.1)
plt.legend(loc='lower left')
plt.annotate('$\\gamma_n = n_0/2 =$' + f'{n0/2:.2f}',
                xy=(ICmin,0), xycoords='data',
                xytext=(1,0.65), textcoords='data',
                ha='center', va='top', size=textFontSize, backgroundcolor='white')
plt.annotate('$\\gamma_n = 2 n_0/3 =$' + f'{2*n0/3:.2f}',
                xy=(ICmin,0), xycoords='data',
                xytext=(1,0.97), textcoords='data',
                ha='center', va='bottom', size=textFontSize, backgroundcolor='white')
if savePlots: saveFigures(savePlotPath, 'gamman_ic')
plt.show()
```



```{python}
#| label: fig-gamman_ic_short
#| fig-cap: Thermal noise excess factor $\gamma_n$ versus inversion coefficient $IC$.

Npts=101
ICmin=0
ICmax=100
IC=np.linspace(ICmin,ICmax,Npts,endpoint=True)

n=1.3
gammanwi=1
lambdac=[0.05,0.1,0.15,0.2,0.25,0.3]
Ncurves=len(lambdac)
colors=plt.cm.rainbow(np.linspace(0, 1, Ncurves))
gamman=np.zeros(Npts)

for m in range(0,Ncurves):
  for k in range(0,Npts):
    gamman[k]=gamman_ic_short(IC[k],gammanwi,n,lambdac[m])
  plt.plot(IC, gamman, color=colors[m], label=f'$\\lambda_c =$ {lambdac[m]:.2f}')

plt.xlabel('Inversion Coefficient')
plt.xlim(ICmin,ICmax)
plt.ylim(0,4)
plt.ylabel('$\\gamma_n$')
plt.legend(loc='center left', fontsize=textFontSize, bbox_to_anchor=(1, 0.5))
textstr = '\n'.join((
    f'$n =$ {n:.1f}',
    f'$\\gamma_{{nwi}} =$ {gammanwi:.0f}'))
plt.text(0.05, 0.95, textstr, ha='left', va='top', size=textFontSize, transform=plt.gca().transAxes,
         bbox={'facecolor':'w', 'alpha':1.0, 'pad':5})
if savePlots: saveFigures(savePlotPath, 'gamman_ic_short')
plt.show()
```

# Normalized intrinsic capacitances
The normalized intrinsic capacitances of a long-channel transistor are given by
\begin{align}
  c_{gsi} &\triangleq \frac{C_{GSi}}{C_{OX}} = \frac{q_s}{3}\,\frac{2 q_s+4 q_d+3}{(q_s+q_d+1)^2},\\
  c_{gdi} &\triangleq \frac{C_{GDi}}{C_{OX}} = \frac{q_d}{3}\,\frac{2 q_d+4 q_s+3}{(q_s+q_d+1)^2},\\
  c_{gbi} &\triangleq \frac{C_{GBi}}{C_{OX}} = \frac{n-1}{n}\,(1-c_{gsi}-c_{gdi}),\\
  c_{bsi} &\triangleq \frac{C_{BSi}}{C_{OX}} = (n-1)\,c_{gsi},\\
  c_{bdi} &\triangleq \frac{C_{BDi}}{C_{OX}} = (n-1)\,c_{gdi},\\
  c_{ggi} &\triangleq \frac{C_{GGi}}{C_{OX}} = c_{gsi} + c_{gdi} + c_{gbi},
\end{align}
where $C_{OX} \triangleq W_{eff}\,L_{eff}\,C_{ox}$.

The normalized intrinsic capacitances are plotted versus $v_p$ in @fig-int_cap_vps. Note that the dashed lines represents the intrinsic capacitances $c_{gbi}$, $c_{bsi}$, $c_{bdi}$ and $c_{ggi}$ calculated with a constant slope factor $n$.

```{python}
#| label: fig-int_cap_vps
#| fig-cap: Normalized intrinsic capacitance versus normalized pinch-off voltage $v_p$.

gammab=4
psi0=30
n0=n_vp(0,gammab,psi0)
vs=0
vd=30

Npts=101
vpmin=-20
vpmax=60
vp=np.linspace(vpmin,vpmax,Npts,endpoint=True)
n=np.zeros(Npts)
cgsi=np.zeros(Npts)
cgdi=np.zeros(Npts)
cgbi=np.zeros(Npts)
cgbi0=np.zeros(Npts)
cgi0=np.zeros(Npts)
cbsi=np.zeros(Npts)
cbsi0=np.zeros(Npts)
cbdi=np.zeros(Npts)
cbdi0=np.zeros(Npts)
cggi=np.zeros(Npts)
cggi0=np.zeros(Npts)

for k in range(0,Npts):
  n[k]=n_vp(vp[k],gammab,psi0)
  cgsi[k]=cgsi_v(vp[k],vs,vd)
  cgdi[k]=cgdi_v(vp[k],vs,vd)
  cgbi[k]=cgbi_v(vp[k],vs,vd,n[k])
  cgbi0[k]=cgbi_v(vp[k],vs,vd,n0)
  cgi0[k]=cgsi[k]+cgdi[k]+cgbi0[k]
  cbsi[k]=cbsi_v(vp[k],vs,vd,n[k])
  cbsi0[k]=cbsi_v(vp[k],vs,vd,n0)
  cbdi[k]=cbdi_v(vp[k],vs,vd,n[k])
  cbdi0[k]=cbdi_v(vp[k],vs,vd,n0)
  cggi[k]=cggi_v(vp[k],vs,vd,n[k])
  cggi0[k]=cggi_v(vp[k],vs,vd,n0)

plt.plot([vpmin,vpmax],[2/3,2/3],'k--',linewidth=lw)
plt.plot([vpmin,vpmax],[1-1/(3*n0),1-1/(3*n0)],'k--',linewidth=lw)
plt.plot([vpmin,vpmax],[(n0-1)/(3*n0),(n0-1)/(3*n0)],'k--',linewidth=lw)
plt.plot(vp,cgsi,'r-',label='$c_{gsi}$')
plt.plot(vp,cgdi,'b-',label='$c_{gdi}$')
plt.plot(vp,cgbi,'g-',label='$c_{gbi}$')
plt.plot(vp,cgbi0,'g--')
plt.plot(vp,cbsi,'m-',label='$c_{bsi}$')
plt.plot(vp,cbsi0,'m--')
plt.plot(vp,cbdi,'c-',label='$c_{bdi}$')
plt.plot(vp,cbdi0,'c--')
plt.plot(vp,cggi,'k-',label='$c_{gi}$')
plt.plot(vp,cggi0,'k--')
plt.xlim(vpmin,vpmax)
plt.xticks(np.arange(-20,70,10))
plt.xlabel('$v_p$')
plt.ylim(0,1)
plt.yticks(np.arange(0,1.1,0.1))
plt.ylabel('Normalized Intrinsic Cap.')
#plt.legend(loc='best')
plt.legend(loc='upper left', bbox_to_anchor=(1, 0.6))
textstr1 = '\n'.join((
    f'$v_s =$ {vs:.0f}',
    f'$v_d =$ {vd:.0f}'))
plt.text(0.05, 0.95, textstr1, ha='left', va='top', size=textFontSize, transform=plt.gca().transAxes,
         bbox={'facecolor':'w', 'alpha':1.0, 'pad':5})
textstr2 = '\n'.join((
    f'$U_T =$ {UT*1e3:.1f} mV',
    f'$\\gamma_b =$ {gammab:.0f}',
    f'$\\psi_0 =$ {psi0:.0f}',
    f'$n_0 =$ {n0:.2f}'))
plt.text(1.05, 0.97, textstr2, ha='left', va='top', size=textFontSize, transform=plt.gca().transAxes,
         bbox={'facecolor':'w', 'alpha':1.0, 'pad':5})
if savePlots: saveFigures(savePlotPath, 'int_cap_vp')
plt.show()
```


```{python}
#| label: fig-int_cap_vd
#| fig-cap: Normalized intrinsic capacitance versus normalized drain voltage $v_d$.

gammab=4
psi0=30
n0=n_vp(0,gammab,psi0)
vs=0
vp=30

Npts=101
vdmin=0
vdmax=60
vd=np.linspace(vdmin,vdmax,Npts,endpoint=True)
cgsi=np.zeros(Npts)
cgdi=np.zeros(Npts)
cgbi=np.zeros(Npts)
cbsi=np.zeros(Npts)
cbdi=np.zeros(Npts)
cggi=np.zeros(Npts)

for k in range(0,Npts):
  cgsi[k]=cgsi_v(vp,vs,vd[k])
  cgdi[k]=cgdi_v(vp,vs,vd[k])
  cgbi[k]=cgbi_v(vp,vs,vd[k],n0)
  cbsi[k]=cbsi_v(vp,vs,vd[k],n0)
  cbdi[k]=cbdi_v(vp,vs,vd[k],n0)
  cggi[k]=cggi_v(vp,vs,vd[k],n0)

plt.plot([vdmin,vdmax],[2/3,2/3],'k--',linewidth=lw)
plt.plot([vdmin,vdmax],[1-1/(3*n0),1-1/(3*n0)],'k--',linewidth=lw)
plt.plot([vdmin,vdmax],[(n0-1)/(3*n0),(n0-1)/(3*n0)],'k--',linewidth=lw)
plt.plot(vd,cgsi,'r-',label='$c_{gsi}$')
plt.plot(vd,cgdi,'b-',label='$c_{gdi}$')
plt.plot(vd,cgbi,'g-',label='$c_{gbi}$')
plt.plot(vd,cbsi,'m-',label='$c_{bsi}$')
plt.plot(vd,cbdi,'c-',label='$c_{bdi}$')
plt.plot(vd,cggi,'k-',label='$c_{gi}$')
plt.xlim(vdmin,vdmax)
#plt.xticks(np.arange(-20,70,10))
plt.xlabel('$v_d$')
plt.ylim(0,1)
plt.yticks(np.arange(0,1.1,0.1))
plt.ylabel('Normalized Intrinsic Cap.')
#plt.legend(loc='best')
plt.legend(loc='upper left', bbox_to_anchor=(1, 0.6))
textstr1 = '\n'.join((
    f'$v_s =$ {vs:.0f}',
    f'$v_p =$ {vp:.0f}'))
plt.text(0.8, 0.5, textstr1, ha='left', va='center', size=textFontSize, transform=plt.gca().transAxes,
         bbox={'facecolor':'w', 'alpha':1.0, 'pad':5})
textstr2 = '\n'.join((
    f'$U_T =$ {UT*1e3:.1f} mV',
    f'$\\gamma_b =$ {gammab:.0f}',
    f'$\\psi_0 =$ {psi0:.0f}',
    f'$n_0 =$ {n0:.2f}'))
plt.text(1.05, 0.97, textstr2, ha='left', va='top', size=textFontSize, transform=plt.gca().transAxes,
         bbox={'facecolor':'w', 'alpha':1.0, 'pad':5})
if savePlots: saveFigures(savePlotPath, 'int_cap_vd')
plt.show()
```

# References
