# Tutorial 12: UTB within the EMA

This tutorial will go through the definition of an Hamiltonian class able to simulate Ultra-Thin Body Silicon FET, within the Effective Mass Approximation.

Let’s first start from a simple example, which can be easily generalized. In particular, let be the central part of the device to be simulated, the one shown in the picture below. To make things simpler, let’s consider a uniform grid, both in the vertical direction (x-axis) and the longitudinal direction (y-direction, which corresponds to the transport direction).

Within the EMA, the hopping parameter between is equal to where me=(ml*mt1*mt2)^(1/3), ml, mt1 and mt2 are the relative effective masses along the three direction in the Brillouin zone, and a is the grid spacing between two atoms.

According to the EMA Hamiltonian (for reference please read S. Datta, “Electronic transport in mesoscopic systems”, Cambridge), the on-site energy is equal to 4t, while the hopping parameter between two atoms is -t.

As a consequence, the Hamiltonian matrix to be defined will be:

1 0 0
1 1 4t
2 2 4t
3 3 4t
4 4 4t
5 5 4t
6 6 4t
7 7 4t
8 8 4t
9 9 4t
10 10 4t
11 11 4t
12 12 4t
13 13 4t
14 14 4t
15 15 4t
1 2 -t
1 4 -t
2 3 -t
2 5 -t
3 6 -t
4 5 -t
4 7 -t
5 6 -t
5 8 -t
6 9 -t
7 8 -t
7 10 -t
8 9 -t
8 11 -t
9 12 -t
10 11 -t
10 13 -t
11 12 -t
11 14 -t
12 15 -t
13 14 -t
14 15 -t

A script which build the Hamiltonian of the above structure, given the number of nx and ny points, is the following:

Lx=1e-9;
Ly=2e-9;
a=0.5e-9;
nx=int(Lx/a)+1;
ny=int(Ly/a)+1;
me=(0.98*0.19*0.19)**(1/3)*m0;
t=hbar**2/(2*me*a**2)/q;
print nx,ny

# I define the Hamiltonian
H=Hamiltonian(nx,ny);
#Initialization of the list
h=[];
#I consider only one orbital
h.append([1,0,0]);
#I work on the on-site energies
for j in range(0,ny):
for i in range(0,nx):
ix=i+j*nx;
h.append([ix+1,ix+1,4*t]);

#I work on the hopping parameters
#First and second off-diagonal
for j in range(0,ny):
for i in range(0,nx):
ix=i+j*nx;
if (i<nx-1): h.append([ix+1,ix+2,-t])
if (j<ny-1): h.append([ix+1,ix+nx+1,-t])

The defined list has then to be converted in a numpy matrix of complex numbers as follows:

H.H=array(h,dtype=complex);

Now we can compute for example the transmission coefficient between 0 and 2 eV.

H.Eupper=2;
H.Elower=0;
H.charge_T()
a=[H.E,H.T]
savetxt(“T.out”,transpose(a));

If we now want to compute within a self-consistent scheme transport through a UTB device, we have first to define a class for the UTB device, which is a Hamiltonian sub-class.

class UTB(Hamiltonian):
Lx=3;
Ly=3;
a=1;
def atoms_coordinates(self):
self.x=self.a*linspace(0,self.n-1,self.n);
self.y=self.a*linspace(0,self.Nc-1,self.Nc);
return;

def gap(self):
# This is an rough exstimation of
# the Energy gap: for sure this is
# the largest attainable value, within
# the pz tight-binding model
return 0;

def compute_charge_T(self):
# Number of slices and atoms
nx=self.n;
ny=self.Nc;
me=(0.98*0.19*0.19)**(1/3)*m0;
thop=hbar**2/(2*me*self.a**2)/q*1e18;
#Initialization of the list
h=[];
#I consider only one orbital
h.append([1,0,0]);
#I work on the on-site energies
for j in range(0,ny):
for i in range(0,nx):
ix=i+j*nx;
h.append([ix+1,ix+1,4*thop]);

#I work on the hopping parameters
#First and second off-diagonal
for j in range(0,ny):
for i in range(0,nx):
ix=i+j*nx;
if (i<nx-1): h.append([ix+1,ix+2,-thop])
if (j<ny-1): h.append([ix+1,ix+nx+1,-thop])

self.H=array(h,dtype=complex);
self.Ei=-self.Phi;
self.charge_T();
return;

def current(self):
vt=kboltz*self.Temp/q;
E=array(self.E);
T=array(self.T);
arg=2*q*q/(2*pi*hbar)*T*(Fermi((E-self.mu1)/vt)-Fermi((E-self.mu2)/vt))*self.dE
return sum(arg);

Let’s discuss in details the above python script.

The class UTB is a sub-class of Hamiltonian, so that it automatically inherit all the fields of a Hamiltonian class (Eupper, Elower, dE, eta, mu1, mu2 etc.) as well as the methods. In particular, the most important one obviously is the charge_T() method, which compute the transmission coefficient and the charge in correspondence of each atoms.

Here we define four methods: atoms_coordinates(), which define the x and y coordinates in correspondence of each atom. Please note that atom 1 is in correspondence of the origin (0,0); gap(), which roughly estimates the gap, and which is important when using the MPIze as well as the MPIze_kt commands;  compute_charge_T, which takes care of the definition of the UTB Hamiltonian, given the number of x and y points, and the computation of the charge and the transmission coefficient of the UTB; current(), which computes the current through the Landauer’s formula, once known the transmission coefficient.

Let’s focus on the compute_charge_T method.

Because of the considered device, we have the following correspondence between the index n and Nc of the Hamiltonian class and nx and ny of the UTB class.

nx=self.n;
ny=self.Nc;

Then we compute the Hamiltonian and assign the computed Hamiltonian to the attribute H of the UTB class, as in

self.H=array(h,dtype=complex);

The intrinsic Fermi level is assign as follows

self.Ei=-self.Phi;

and we can compute the charge and the T with the following line of code

self.charge_T();

At the end, the charge is stored in self.charge and the transmission coefficient in self.T

If the above mentioned module is saved in the module_UTB.py file, we can call the UTB class as follows

from module_UTB import *
Lx=1;
Ly=2;
a=0.5;
nx=int(Lx/a)+1;
ny=int(Ly/a)+1;

device=UTB(nx,ny);
device.a=0.5;

device.Eupper=2;
device.Elower=0;
device.compute_charge_T()
a=[device.E,device.T]
savetxt(“T.out”,transpose(a));

which do the very same thing as the first script in this section, but in a more efficient way from the point of view of the coding.