Tutorial 15: QM1D

In this tutorial, we will focus on the QM1D class, which allows the study of systems confined in one-dimension.

Let’s consider for example the MOS structure described in the picture below The MOS structure is composed by a 2-nm thick SiO2 oxide, and a Si bulk 100-nm thick and p-doped. Metallic gates are placed at the top and at the bottom.

First, we need to define the 1D grid,

from NanoTCAD_ViDES import *

x=nonuniformgrid(array([0,0.1,2,0.05,102,10]))

grid=grid1D(x,x)

In this case, the 1D Schroedinger is solved in the whole 1D domain, since the second argument of the grid1D command is the same as the first argument. In order to solve the 1D Schroedinger equation in a sub-domain, we should have defined another vector (e.g. Xc, with max(Xc)<102 and min(Xc)>0) and then write the command

grid=grid1D(x,Xc)

Let’s now define the top and bottom gates (using, as usual, the gate command) and the oxide and the bulk regions (using, as usual, the region command). For the top gate we impose a voltage equal to 1 V.

top_gate=gate("hex",grid.xmin,grid.xmin+0.05);
top_gate.Ef=-1.0;
bottom_gate=gate("hex",grid.xmax-1,grid.xmax);

SiO2=region("hex",grid.xmin,2);
SiO2.set_material("SiO2")

For each region, we have to define the material parameter (i.e. the relative dielectric constant, the effective masses, the energy band-gap, the electron affinity etc.), by means of the set_material command.

SiO2.set_material("SiO2")
Si.set_material("Si")

Up to now, in the NanoTCAD ViDES module, only SiO2 and Si materials are pre-defined. Other materials can however be simply added, changing the method set_material of the region class inNanoTCAD_ViDES.py module.

For example, if we want to add the Si3N4 material, the set_material method has to be changed in this way, i.e. from this

class region:
def __init__(self,*args):
self.name="none";
self.geometry="hex";
self.eps=3.9;
self.rho=0;
if (args=="hex"):
if (len(args)>5):
self.xmin=args;
self.xmax=args;
self.ymin=args;
self.ymax=args;
self.zmin=args;
self.zmax=args;
elif ((len(args)>3)&(len(args)<=5)):
self.xmin=args;
self.xmax=args;
self.ymin=args;
self.ymax=args;
elif (len(args)<=3):
self.xmin=args;
self.xmax=args;
def set_material(self,material):
if (material.lower()=="sio2"):
self.eps=3.9;
self.mel=0.5;
self.met=0.5;
self.Egap=8.05;
self.chi=0.95;
self.mhole=0.42
if (material.lower()=="si"):
self.eps=11.8;
self.mel=0.916;
self.met=0.19;
self.Egap=1.124519;
self.chi=4.05;
self.mhole=0.549;

To this:

class region:
def __init__(self,*args):
self.name="none";
self.geometry="hex";
self.eps=3.9;
self.rho=0;
if (args=="hex"):
if (len(args)>5):
self.xmin=args;
self.xmax=args;
self.ymin=args;
self.ymax=args;
self.zmin=args;
self.zmax=args;
elif ((len(args)>3)&(len(args)<=5)):
self.xmin=args;
self.xmax=args;
self.ymin=args;
self.ymax=args;
elif (len(args)<=3):
self.xmin=args;
self.xmax=args;
def set_material(self,material):
if (material.lower()=="sio2"):
self.eps=3.9;
self.mel=0.5;
self.met=0.5;
self.Egap=8.05;
self.chi=0.95;
self.mhole=0.42
if (material.lower()=="si"):
self.eps=11.8;
self.mel=0.916;
self.met=0.19;
self.Egap=1.124519;
self.chi=4.05;
self.mhole=0.549;
if (material.lower()=="si3n4"):
self.eps=25;
self.mel=0.1;
self.met=0.1;
self.Egap=5.68;
self.chi=1.65;
self.mhole=0.5;

Let’s go back to the definition of the device structure.  Since the p-doping of the Si-bulk is equal to 10^24 m^-3, considering a full ionization of the dopants, we define in the Si region a fixed charge equal to -1e24.

Si.rho=-1e24;

Finally, in order to initialize the structure, we use (as usual) the interface command:

p=interface1D(grid,top_gate,bottom_gate,SiO2,Si);

We now define the QM1D class, specifying the number of points of the 1D domain where the 1D Schroedinger equation is solved, the number of eigenvalues to be considered (here 3), the interface 1D and the one-dimensional grid vector, where the Schroedinger equation is solved.

nx=size(x);
M=QM1D(nx,3,x,p);

Now we can solve self-consistently the 1D Poisson equation and then save all the data.

b=[x,M.Psi[:,0]];
savetxt("Psi1.out",transpose(b));

dist=avervect(x)*1e-9;
a=[x,M.charge/dist];
savetxt("free_charge.out",transpose(a));

a=[x,p.Phi];
savetxt("Phi.out",transpose(a));

a=[x,M.Ei+M.Egap*0.5];
savetxt("Ec.out",transpose(a));

a=[x,M.Ei];
savetxt("Ei.out",transpose(a));

a=[x,p.fixed_charge];
savetxt("rho2.out",transpose(a));

By default, the method charge_T applied to the QM1D class take into account quantum confinement for electrons, while a semiclassical approximation for holes. Anyway, the user can define his/her own method, simply defining within the script a function.

For example, if the uses wants to consider a semiclassical approximation both for electrons and holes, then he/she has to define the following function:

def charge_SC(self):
vt=self.Temp*kboltz/q;
dist=avervect(self.x)
self.Ei=-self.Phi;
#Electrons
for i in range(0,size(self.charge)):
self.charge[i]=-6*dist[i]*1e-9*(2/sqrt(pi))*2*(vt/(2*pi)*(self.massl[i]*m0/hbar)*(q/hbar))**1.5*fphalf(-(self.Ei[i]+self.Egap[i]*0.5-self.Ef)/vt)
#Holes
for i in range(0,size(self.charge)):
self.charge[i]=self.charge[i]+dist[i]*1e-9*(2/sqrt(pi))*2*(vt/(2*pi)*(self.massh[i]*m0/hbar)*(q/hbar))**1.5*fphalf((self.Ei[i]-self.Egap[i]*0.5-self.Ef)/vt)

The script has to be changed as follows, when defining the QM1D class:

M=QM1D(nx,3,x,p,charge_SC);

The entire script can be downloaded here