<jupyter_start><jupyter_text>Table of Contents1&nbsp;&nbsp;General Information2&nbsp;&nbsp;Defined constants and temperature dependencies2.1&nbsp;&nbsp;Bandgap $E_G$2.2&nbsp;&nbsp;Effectvie masses $m_n$ $m_p$2.3&nbsp;&nbsp;Effectvie density of states $N_c$ $N_v$3&nbsp;&nbsp;The Bulk model (Boltzmann)3.1&nbsp;&nbsp;Parameters3.2&nbsp;&nbsp;Computation of charge carries3.3&nbsp;&nbsp;Computation of Fermi level3.4&nbsp;&nbsp;Calculation of charge carrier in a doped semiconductor4&nbsp;&nbsp;The Bulk model (Fermi-Dirac)5&nbsp;&nbsp;The QV model (Boltzmann)5.1&nbsp;&nbsp;Parameters5.2&nbsp;&nbsp;Metal-semiconductor work function $\phi_{ms}$5.3&nbsp;&nbsp;The surface, acceptor and bulk potential5.4&nbsp;&nbsp;The electric field at the surface $E_{f,s}$5.5&nbsp;&nbsp;The potential loop5.6&nbsp;&nbsp;Computing the mobile channel charge $Q_m$6&nbsp;&nbsp;The QV model (Fermi Dirac)7&nbsp;&nbsp;The IV model (Boltzmann)7.1&nbsp;&nbsp;Parameters7.2&nbsp;&nbsp;The channel voltage $V_{ch}$7.3&nbsp;&nbsp;Effective mobility $\mu_{eff}$ as a function of the gate voltage7.4&nbsp;&nbsp;The drain to source current<jupyter_code>%load_ext autoreload
%autoreload 1
from CryMOS.constants import *
from CryMOS.QV import *
from CryMOS.plots import *
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
matplotlib.style.use('seaborn-notebook');<jupyter_output><empty_output><jupyter_text>CryMOS model documentation<jupyter_code>%load_ext autoreload
%autoreload 1
%matplotlib notebook
%aimport CryMOS.Bulk
from CryMOS.Bulk import *
from CryMOS.constants import *
from CryMOS.QV import *
from CryMOS.plots import *
from CryMOS.IV import *
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
matplotlib.style.use('seaborn-notebook')<jupyter_output>The autoreload extension is already loaded. To reload it, use:
  %reload_ext autoreload<jupyter_text>General InformationWith the pm-mos-model current voltage characteristics of NMOS transistorsdown to cryogenic temperatures can be computed. The model relies on thecharge sheet and fully depletion approximations \cite{TSIVIDIS} \cite{BREWS}. The default parametersof this model (see chapter 2) refer to silicon as the bulk material of thetransistor. With the model also charge carrier concentrations and the Fermi energy of a bulk semiconductor can be computed.   Defined constants and temperature dependenciesConstants and the temperature dependency of the bandgap and effective masses are defined in "constants.py".  Bandgap $E_G$ The bandgap (as a function of temperature) is computed by an empiricalrelation by Varshni \cite{VARSHNI}:\begin{equation}\label{eq:varshni}E_g(T) = E_g(0) - \frac{\alpha T^2}{T + \beta},\end{equation}where the values for $E_g(0)$ , $\alpha$ and $\beta$ are taken from reference \cite{ALEX}:\begin{equation}\begin{gathered}\label{eq:varshni_params}E_g(0) = 1.1692 \,\mathrm{eV} \\\alpha = 4.9    \,\mathrm{eV K^{-1}}									 \\\beta = 655     \,\mathrm{K}.\end{gathered}\end{equation}<jupyter_code>temp = np.linspace(1,500,100)
fig, ax = plt.subplots()
ax.plot(temp,E_g_Si(temp)/e)
ax.set_xlabel(r"$T$ (K)")
ax.set_ylabel(r"$E_g$  (eV)")
plt.show()<jupyter_output><empty_output><jupyter_text>Effectvie masses $m_n$ $m_p$ The following temperature dependencies are used for the effective masses of electrons $m_n$ and for the effective masses of holes $m_h$ \cite{CAIAFA}:\begin{equation}\label{eq:me_emp}m_n = -1.084e-9 T^3 + 7.580e-7 T^2 + 2.862e-4 T + +1.057\end{equation}and\begin{multline}\label{eq:mh_emp}m_p = 1.872e-11 T^4 - 1.969e-8 T^3 + 5.857e-6 T^2  + +2.712e-4 T + 0.584.\end{multline} Effectvie density of states $N_c$ $N_v$ The above stated temperature dependence of the effective masses allows the computation of the effective density of states \cite{SZE}:\begin{equation}\label{eq:eff_dens_N_c}N_c = 2 \left(\frac{m_{n} k_b T}{2 \pi \hbar^2}\right)^{3/2},\end{equation}and\begin{equation}\label{eq:eff_dens_N_v}N_v = 2 \left(\frac{m_{p} k_b T}{2 \pi \hbar^2}\right)^{3/2}.\end{equation}<jupyter_code>temp = np.linspace(0,300,300)
fig, ax = plt.subplots()
ax.plot(temp,m_n_Si(temp)/m_e,label=r"$m_n$")
ax.plot(temp,m_p_Si(temp)/m_e,label=r"$m_p$")
ax.set_xlabel(r"$T$ (K)")
ax.set_ylabel(r"$m$  ($m_e$)")
ax.legend()
plt.show()

fig, ax = plt.subplots()
ax.semilogy(temp,N_c_Si(temp),label=r"$N_c$")
ax.semilogy(temp,N_v_Si(temp),label=r"$N_v$")
ax.set_xlabel(r"$T$ (K)")
ax.set_ylabel(r"$N$  (m$^{-3}$)")
ax.legend()
plt.show()<jupyter_output><empty_output><jupyter_text>The Bulk model (Boltzmann)The Bulk model class allows to compute properties of a doped semiconductor reyling on the Boltzmann approximation.  Parameters In order to compute charge carrier densities and the Fermi level the following parameters need to be specified:   parameter description default value      <th   scope='row'>temp temperature  $300  \, \mathrm{K}$     E_g bandgap  eq. \ref{eq:varshni}    N_c effective density of states in the conduction band eq. \ref{eq:eff_dens_N_c}    <th                               scope='row'>N_v effective density of states in the valence band eq. \ref{eq:eff_dens_N_v}    <th                                                            scope='row'>E_v valence band 0.    E_c conduction band E_v + E_g    E_A acceptor energy level E_v + $0.44 \, \mathrm{eV}$ (Boron as acceptor)     <th                                                            scope='row'>E_D donor energy level E_c - $0.45  \, \mathrm{eV}$ (Phosphorous as donor)    g_A acceptor degeneracy factor 4    g_D donor degeneracy factor 2    <th                                                                                                       Computation of charge carriesIf the Fermi level is known, charge carriers can be computed using the Boltzmann approximation \cite{SZE}:\begin{equation}\label{n_dop}n = N_c \exp{\left(\frac{E_c - E_f}{k_b T}\right)} ,\end{equation}\begin{equation}\label{p_dop}p = N_v \exp{\left(\frac{E_f - E_v}{k_b T}\right)} ,\end{equation}<jupyter_code>#%% calculate charge carrier densities N with Boltzmann approximation
ef = np.linspace(0,1.2,100)*e  
bulk = BulkModel(temp = 200.) 
fig, ax = plt.subplots()
ax.semilogy(ef/e,bulk.n(ef),label='n')
ax.semilogy(ef/e,bulk.p(ef),label='p')
ax.legend()
ax.set_xlabel(r"$E_f$ (eV)")
ax.set_ylabel(r"$N$ (m$^{-3}$)")
plt.show()<jupyter_output><empty_output><jupyter_text>Computation of Fermi level The Fermi level is computed requiring the charge neutrality condition. \begin{equation}\label{eq:char_neutr}n + N_A ^- = p + N_D^+ , \end{equation}where incomplete ionization is considered as follows \cite{SZE}:\begin{equation}\begin{aligned}\label{eq:ionized}N_A^- = \frac{N_A}{1+ g_A \exp{\left((E_A-E_f)/ (k_b T)\right)}} 		\\N_D^+ = \frac{N_D}{1+ g_D \exp{\left((E_f-E_D)/ (k_b T)\right)}}.\end{aligned}\end{equation}<jupyter_code>#%% calculate the Fermi level at 100 K 
bulk = BulkModel(temp = 100., N_A = 1e23, N_D =1e18) # units are always SI basic units
print(bulk.E_f/e)
fig, ax = plt.subplots()
ax.axhline(bulk.E_v / e, color='k', label='E_v')
ax.axhline(bulk.E_c / e, color='k', label='E_c')
ax.axhline(bulk.E_f / e, color='r', label='E_f')
ax.legend()
ax.set_ylabel(r"$E$ (eV)")
plt.show()<jupyter_output>0.04275767943401833<jupyter_text>Calculation of charge carrier in a doped semiconductor To calculate the charge carriers in a doped semiconductor, first the Fermi energy is computed with eq. \ref{eq:char_neutr}, then carrier densities can be computed using eq. \ref{n_dop} and eq. \ref{p_dop}.<jupyter_code>#%% calculate n and p at 200 Kelvin for a certain doping level
bulk = BulkModel(temp = 200., N_A = 1e23, N_D =1e18) # units are always SI basic units
print(bulk.bulk_n , bulk.bulk_p)

#%% plot n,p as a function of N_A
N_A_values = np.logspace(12,22,100)
fig, ax = plt.subplots()
for N_A in N_A_values:
    bulk = BulkModel(temp = 200., N_A = N_A, N_D =0)
    ax.scatter(N_A, bulk.bulk_n,color='b')
    ax.scatter(N_A, bulk.bulk_p,color='r')
ax.set_xscale('log')
ax.set_yscale('log')
ax.set_ylim(1e-2,1e22)
ax.set_xlabel(r"$N_A$ (m$^{-3}$)")
ax.set_ylabel(r"$n / p$ (m$^{-3}$)")
plt.show()<jupyter_output>0.02676458460485093 7.073114976468905e+22<jupyter_text>The Bulk model (Fermi-Dirac)The Fermi-Dirac bulk model allows the calculation of charge carriers using Fermi Dirac integrals. The BulkModelFD is a child class that inherits all the paramters and methods from the parent class BulkModel. The calculation of charge carriers is, however, overwritten to be computed with Fermi Dirac integrals. The electron density is computed via \cite{SZE}:\begin{equation}\label{eq:n_fd}n = \int_{E_c}^{\infty} f(E) D_{c}(E) dE,\end{equation}where $f(E)$ is the Fermi function: \begin{equation}f(E) = \frac{1}{1 + \exp{\left(\frac{E - E_f}{k_b T} \right)}},\end{equation}and $D_c(E)$ the density of states in the conduction band. $D_c(E)$ is linked to the effective density of states in the conduction band $N_c$ via:\begin{equation}\label{eq:dc_fd}D_c = \frac{2}{\sqrt{\pi}} \frac{N_c}{(k_b T )^{3/2}}.\end{equation}Analogously the concentration of holes is computed via \cite{SZE}:\begin{equation}\label{eq:p_fd}p = \int_{-\infty}^{E_v} (1-f(E)) D_{v}(E) dE,\end{equation}where the density of states in the valence band $D_c(E)$ is linked to the effective density of states in the valence band $N_v$ via:\begin{equation}\label{eq:dv_fd}D_v = \frac{2}{\sqrt{\pi}} \frac{N_v}{(k_b T )^{3/2}}.\end{equation}The integrals in eq. \ref{eq:n_fd} and eq. \ref{eq:p_fd} can not be solved analytically.<jupyter_code>bulk_dirac = BulkModelFD(temp=100., N_A= 1e20) 
bulk       = BulkModel(temp=100., N_A= 1e20) 
#%% compare Bolzmann and Fermi Dirac model: charge carriers and Fermi energy  
print(bulk_dirac.bulk_p , bulk.bulk_p , bulk_dirac.bulk_p/bulk.bulk_p)
print(bulk_dirac.bulk_n , bulk.bulk_n , bulk_dirac.bulk_n/bulk.bulk_n)
print(bulk_dirac.E_f/e , bulk.E_f/e , bulk_dirac.E_f/bulk.E_f)<jupyter_output>9.75299181000535e+19 9.752994999403181e+19 0.9999996729827269
3.624609531172574e-30 3.624657518472814e-30 0.9999867608732699
0.08762266890485135 0.08762278299153486 0.9999986979792285<jupyter_text>The QV model (Boltzmann)This class is a parent class to the BulkModel (Boltzmann) class. It allows the computation of carrier concentrations in a MOS capacitor. The model relies on the charge sheet and fully depletion approximation and the main equations are taken from \cite{BECKERS}. Parameters The following additional parameters need to be defined:    parameter description default value      <th   scope='row'>chi electron affinity  $4.05  \, \mathrm{eV}$     phi_m metal work function  chi    cox oxide capacitance  $10  \, \mathrm{mFm^{-2}}$    <th                                                                                                              Metal-semiconductor work function $\phi_{ms}$$\phi_{ms}$ is computed via:\begin{equation}\phi_{ms} = \phi_m - \phi_s\end{equation} , where\begin{equation}\phi_{s} = E_c - E_f + \chi\end{equation}. The surface, acceptor and bulk potential The surface potential $\psi_s$ is a central quantity for the calculation of charge carriers. It should be noted, that all of the energies and potentials in the python model are linked to the Fermi energy $E_f$. In this convention the surface potential $\psi_s$ is defined as:\begin{equation}\label{eq:psisdef2}\psi_{s} = \frac{E_{f} - E_{i,s}}{e},\end{equation}wherer $E_{i,s}$ describes the intrinsic energy at the semiconductor oxide interface. Once the surface potential as a function of the gate voltage is computed, charge carrier density (sheet densities) in the channel can be calculated which allows in the following the calculation of a drain to source current. Analougsly the acceptor $\psi_A$ and bulk potential $\psi_b$ can be defined as follows: \begin{equation}\label{eq:psia}\psi_{A} = \frac{E_{A} - E_{i}}{e},\end{equation}\begin{equation}\label{eq:psib}\psi_{b} = \frac{E_{f} - E_{i,b}}{e}.\end{equation}It should be noted that $\psi_A$ and $\psi_b$ do not depend on any external bias and can thus be calculated right away.<jupyter_code>qv = BeckersQVpy(temp = 100. , N_A = 1e22)
print(qv.psi_a, qv.psi_b)<jupyter_output>-0.5340199039387991 -0.5235526838675402<jupyter_text>The electric field at the surface $E_{f,s}$With the above defined potentials the electric field at the semiconductor oxide interface of the transistor can be calculated as follows \cite{BECKERS}:\begin{multline}\label{eq:es2}\mathcal{E}_{s}^2 = \frac{2 e n_i \phi_t}{\epsilon_{si}} \left(\exp{(\frac{\psi_{s} - V_{ch}}{\phi_t})} - \exp{(\frac{\psi_{b} - V_{ch}}{\phi_t})} + \exp{(\frac{-\psi_{s}}{\phi_t})} - \exp{(\frac{-\psi_{b}}{\phi_t})}\right) \\+ \frac{2 e N_A}{\epsilon_{si}} \left(\psi_s - \psi_b - \phi_t \log{\left(\frac{1 + g_A \exp{\frac{\psi_A - \psi_b}{\phi_t}}}{1 + g_A \exp{\frac{\psi_A - (\psi_s - \psi_{b})}{\phi_t}}}\right)}\right),\end{multline}whrere $\phi_t = \frac{k_b T}{e}$ is the thermal voltage, and $V_{ch}$ the channel voltage (see section?) The potential loop The potential loop equation links the surface potential $\psi_s$ to the applied gate voltage $V_{GB}$ \cite{BECKERS}:   \begin{equation}\label{eq:pot_loop2}V_{GB} = (\psi_s - \psi_b )  + \phi_{ms} + \epsilon_{si} \mathcal{E}_{s}(\psi_{s}) / C_{ox} + Q_0 / C_{ox}.\end{equation}This equation is solved implicitly for every gate voltage.<jupyter_code>vg = np.linspace(0,1.5,100)
qv = BeckersQVpy(temp = 100. , N_A = 1e22)
fig, ax = plt.subplots()
ax.plot(vg,qv.psi_s(0,vg), label= r"$V_{ch}$ = 0 V")
ax.plot(vg,qv.psi_s(0.5,vg), label= r"$V_{ch}$ = 0.5 V")
ax.plot(vg,qv.psi_s(1,vg), label= r"$V_{ch}$ = 1 V")
ax.legend()
ax.set_xlabel(r"$V_{GB}$ (V)")
ax.set_ylabel(r"$\psi_{s}$ (V)")
plt.show()<jupyter_output><empty_output><jupyter_text>Computing the mobile channel charge $Q_m$Once $\psi_s$ is determined as a function of the gate voltage, the mobile channel charge can be computed \cite{BECKERS}. \begin{equation}Q_m = \frac{\epsilon_{si} \mathcal{E}_{s}}{C_{ox}} +  \epsilon_{si} \sqrt{\frac{2 e N_A (\psi_{s} - \psi_{b})}{\epsilon_{si}} - \frac{2 e N_A \phi_t}{\epsilon_{si}} \phi_t \log{\left(\frac{1 + g_A \exp{\frac{\psi_A - \psi_b}{\phi_t}}}{1 + g_A \exp{\frac{\psi_A - (\psi_s - \psi_{b})}{\phi_t}}}\right)}}\end{equation}<jupyter_code>vg = np.linspace(0,0.8,200)
qv = BeckersQVpy(temp = 100. , N_A = 1e22)
fig, ax = plt.subplots()
ax.plot(vg,qv.Q_m(qv.psi_s(0, vg),0), label= r"$V_{ch}$ = 0 V")
ax.set_xlabel(r"$V_{GB}$ (V)")
ax.set_ylabel(r"$Q_m$ (Cm$^{-2}$)")
plt.show()<jupyter_output><empty_output><jupyter_text>The QV model (Fermi Dirac)The DiracQVpy class is a child class to the BeckersQVpy class and BulkModelFD class and inherits all the methods and paramters. This means that charge carriers are computed with the Fermi Dirac integrals. The calculation of $E_s$ is overwritten in this class to be: \begin{multline}\label{eq:FD_psis}\mathcal{E}_{s}^{2} = \frac{2 e}{\epsilon_{si}} \int_{\psi_b}^{\psi_s} \left(-\int_{E_c}^{\infty} \frac{1}{1+ \exp{\left(\frac{E-e \psi - E_i}{k_b T}\right)}} D_{c}(E) dE + \right. \\ \left. \int_{-\infty}^{E_v} \left(1- \frac{1}{1+ \exp{\left(\frac{E-e \psi - E_i}{k_b T}\right)}}\right) D_{v}(E) dE - N_{A}^{-}  \right)  d\psi\end{multline}The rest of the computations stays the same.<jupyter_code># attention this takes some time to run...
vg = np.linspace(0,0.8,200)
qv = BeckersQVpy(temp = 100. , N_A = 1e22)
qv_dirac = DiracQVpy(temp = 100. , N_A = 1e22)

fig, ax = plt.subplots()
ax.plot(vg,qv.Q_m(qv.psi_s(0, vg),0), label= r"Boltzmann")
ax.plot(vg,qv_dirac.Q_m(qv_dirac.psi_s(0, vg),0), label= r"Dirac",linestyle='--')
ax.set_xlabel(r"$V_{GB}$ (V)")
ax.set_ylabel(r"$Q_m$ (Cm$^{-2}$)")
plt.show()

fig, ax = plt.subplots()
ax.plot(vg,qv.psi_s(0,vg), label= r"$V_{ch}$ = 0 V")
ax.plot(vg,qv_dirac.psi_s(0,vg), label= r"$V_{ch}$ = 0 V",linestyle='--')
ax.plot(vg,qv.psi_s(0.5,vg), label= r"$V_{ch}$ = 0.5 V")
ax.plot(vg,qv_dirac.psi_s(0.5,vg), label= r"$V_{ch}$ = 0.5 V",linestyle='--')
ax.legend()
ax.set_xlabel(r"$V_{GB}$ (V)")
ax.set_ylabel(r"$\psi_{s}$ (V)")
plt.show()<jupyter_output><empty_output><jupyter_text>The IV model (Boltzmann)The previously introduced QV classes allow for the computation of the mobile channel charge $Q_m$. In the IV model also drain to source currents can be calculated. The class is a child class to the QV class.  ParametersThe following additional parameters need to be defined:    parameter description default value      <th   scope='row'>u0 zero field mobility  $0.045 \, \mathrm{cm^{2}V^{-1}s^{-1}}$    theta mobility reduction paramter  0    mobmod mobility model selection parameter  0    w transistor width None    l transistor length None    <th                                          The channel voltage $V_{ch}$When a drain to source voltage $V_{DS}$ is applied and an inversion layer is present, electrons can be transported along the channel. This creates a non-equilibrium situation for the electrons, that can be incorporated in our model via the introduction of the channel voltage $V_{ch}$ \cite{WU}. The channel voltage has a value of $V_{DS}$ right at the drain side of the transistor and drops to $V_S$ at the source side of the transistor and is thus a function of the horizontal position $x$.  $V_{ch}$  shifts the potential $\psi_s$ in the calculation of the electron concentration  $n$ in eq. \ref{eq:es2}.The computation for $\psi_s$ and $E_{s}$ now also depends on the horizontal position $x$. To calculate a current flowing from source to drain an integral along this axis ($x$) will be performed as will be shown in the next section. Effective mobility $\mu_{eff}$ as a function of the gate voltagePer default a very simple mobility model (mobmod==0) is used:\begin{equation}\label{eq:ids_mudef}\mu_{eff}(V_{GB}) = \frac{\mu_{0}}{1 + \theta V_{GB}}.\end{equation},however, several more sophisticated mobility models are implemented as well (modmod== 1,2), for details see the definition of $\mu_{eff}$ in IV.py . The drain to source currentAn expression for $I_{DS}$ starting from drift and diffusion transport equation can be written in an integral form in terms of the channel voltage $V_{ch}$ \cite{ENZ}:\begin{equation}I_{DS} = - \mu_{eff}(V_{GB})  \int_{V_{SB}}^{V_{DB}} Q_I(V_{ch}) dV_{ch}.\end{equation}<jupyter_code>iv = BeckersIVpy(temp=50.,N_A=1e22,cox = 20e-3, w=10e-6,l=10e-6)
vg = np.linspace(0,1.0,100)
vds = 0.050
fig, ax = plt.subplots()
ax2 = ax.twinx()
ax.plot(vg,iv.i_ds(vds,vg))
ax2.semilogy(vg,iv.i_ds(vds,vg))
ax.set_xlabel(r"$V_{GB}$ (V)")
ax.set_ylabel(r"$I_{DS}$ (A)")
ax2.set_ylabel(r"$I_{DS}$ (A)")
plt.show()
#%% plot currents for different temperatures
from matplotlib import cm
from matplotlib.colors import Normalize
norm = Normalize(vmin=0, vmax=305)
my_cmap = cm.plasma

fig, ax = plt.subplots()
vg = np.linspace(0,0.4,1000)
vds = 0.050
temp_lis = [20,50,100,150,200,300]
for temp in temp_lis:
    iv = BeckersIVpy(temp=temp,N_A=1e22,cox = 20e-3, w=10e-6,l=10e-6)
    ax.semilogy(vg,iv.i_ds(vds,vg),c = my_cmap(norm(float(temp))))

ax.set_xlabel(r"$V_{GB}$ (V)")
ax.set_ylabel(r"$I_{DS}$ (A)")
plt.show()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1525548211100619, 'fraction_numerical': 0.03436155702444654, 'mean_word_length': 4.7006578947368425, 'pattern_counts': {'":': 0, '<': 57, '<?xml version=': 0, '>': 54, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 17, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '13', 'dir': 'jupyter-structured-clean-dedup', 'id': '71405', 'license': 'Apache-2.0', 'n_tokens_mistral': 7533, 'n_tokens_neox': 6928, 'n_words': 1848, 'path': 'examples/doc.ipynb', 'repo_name': 'michi7x7/pm-mos-model', 'size': '19062'}