Skip to content
Extraits de code Groupes Projets
Valider 8d8e444c rédigé par Nicolas Roisin's avatar Nicolas Roisin
Parcourir les fichiers

adding features for data analysis

parent 003eaedf
Aucune branche associée trouvée
Aucune étiquette associée trouvée
1 requête de fusion!4Main
...@@ -2,7 +2,61 @@ import numpy as np ...@@ -2,7 +2,61 @@ import numpy as np
from scipy.interpolate import interp1d from scipy.interpolate import interp1d
import scipy.signal as signal import scipy.signal as signal
from scipy.optimize import curve_fit from scipy.optimize import curve_fit
import math
def finite_difference_coeff(stencil,order):
""" Function to calculate the coefficient to calculate a finite difference for derivative calculation
https://en.wikipedia.org/wiki/Finite_difference_coefficient
args:
- stencil (list of integer): the node point for the calculation of each derivation point. The size of the stencil depends on the accuracy and the order of the derivation. For central difference, the length of the stencil is odd and symetric around 0.
- order (integer): the order of the derivation
return:
- coeff (numpy array): a column array of the coefficients to be used to calculate the finite difference
"""
N=len(stencil)
stencil_matrix=np.zeros((N,N))
for i in range(N):
stencil_matrix[i]=np.array(stencil)**i
delta=np.zeros((N,1))
delta[order,0]= math.factorial(order)
coeff=np.linalg.inv(stencil_matrix) @ delta
return coeff
def finite_difference(x,h,order,accuracy=2,kind="central"):
""" Function to calculate the coefficient to calculate a finite difference for derivative calculation
args:
- x (array): the 1D array for which to calculate the finite difference. The size of the array should be higher than the size of the stencil given by 2 * |_ (order + 1) / 2 _| - 1 + 2 * |_ accuracy / 2_|
- h (scalar): the step size for an uniform grid spacing between each finite difference interval
- order (integer): the order of the derivation
- accuracy (integer): related to the number of points taken for the finite difference. see https://en.wikipedia.org/wiki/Finite_difference_coefficient
- kind (string): the type of finite difference calculated. For now, only "central" finite difference is implemented.
return:
- results (numpy array): an array with the results of the calculation of the finite difference
- x_results (numpy array): an array with the x value with the central value for each calculation of the finite difference
"""
if kind=="central":
n_stencil=int(2*np.floor((order+1)/2)-1+2*np.floor(accuracy/2))
stencil=range(-int(np.floor(n_stencil/2)),int(np.floor(n_stencil/2))+1)
coeff=finite_difference_coeff(stencil,order)/h**order
print(coeff)
Nx=len(x)
matrix_coeff=np.zeros((Nx-accuracy-order+1,Nx))
for i in range(Nx-accuracy-order+1):
matrix_coeff[i,i:n_stencil+i]=coeff[:,0]
results=matrix_coeff @ x
x_results=x[int((n_stencil-1)/2):-int((n_stencil-1)/2)]
return results,x_results
def moving_median(x,window_length=3): def moving_median(x,window_length=3):
""" Function to perform a median filter on a array """ Function to perform a median filter on a array
...@@ -19,7 +73,7 @@ def smooth(x, window_length=10, polyorder=2): ...@@ -19,7 +73,7 @@ def smooth(x, window_length=10, polyorder=2):
args: args:
- x (array_like) : the data to be filtered. - x (array_like) : the data to be filtered.
- window_length (scalar) : the length of the filter window . - window_length (scalar) : the length of the filter window.
- polyorder (scalar) : the order of the polynomial used to fit the samples. polyorder must be less than window_length. - polyorder (scalar) : the order of the polynomial used to fit the samples. polyorder must be less than window_length.
return: return:
- an array the same size as input containing the filtered result. - an array the same size as input containing the filtered result.
......
import numpy as np
import semiconductor as sc
def depletion_length(doping_in, doping_out, Vbias=0,temp=300):
phi_0 = kB * temp / q * np.log(doping_in * doping_out / sc.intrinsic_concentration(temp)**2 )
return np.sqrt(2 * epsilon_si * epsilon_0 / q * doping_out / doping_in / (doping_in + doping_out) * (phi_0 - Vbias))
def j_srh(mu_n,mu_p,ND,NA,tau=1e-7,temp=300,Vbias=0):
ld_n = depletion_length(ND, NA, Vbias)
ld_p = depletion_length(NA, ND, Vbias)
ni = sc.intrinsic_concentration(temp)
x=(ld_p + ld_n) # approximation by considering only the depletion region without diffusion mechanism
coeff_SRH = q * ni * x / (2 * tau)
return ( coeff_SRH * (np.exp(q * Vbias/ (2 * kB * temp)) - 1 ) )
def j_radial(mu_n,mu_p,tau_n,tau_p,ND,NA,ln,lp,temp=300,Vbias=0):
Dn = kB * temp / q * mu_n
Dp = kB * temp / q * mu_p
Ln = np.sqrt( Dn * tau_n )
Lp = np.sqrt( Dp * tau_p )
ld_n = depletion_length(ND, NA, Vbias)
ld_p = depletion_length(NA, ND, Vbias)
ni = sc.intrinsic_concentration(temp)
n_p0=ni**2/NA
p_n0=ni**2/ND
coeff_radial = Dn * n_p0 / Ln / np.tanh( (lp-ld_p) / Ln ) + Dp * p_n0 / Lp / np.tanh( (ln-ld_n) / Lp ) + ni**2 *b_rad* (ld_p + ld_n)
return q * ( coeff_radial * (np.exp(q * Vbias/ ( kB * temp)) - 1 ) )
#################################################################
#
# CONSTANTS
#
#################################################################
b_rad = 4.76e-15 # cm3/s - low-impurity value entre 1 et 10
kB = 1.38e-23 # J/K
q = 1.602e-19 # C
epsilon_0 = 8.8542e-12 # F/m
epsilon_si = 11.7
\ No newline at end of file
# -*- coding: utf-8 -*-
"""
Created on Thu Apr 3 10:42:13 2025
@author: nroisin
"""
import numpy as np import numpy as np
def stress_from_strain(strain_tensor,c11= 165.77,c12= 63.93,c44 = 79.62): def stress_from_strain(strain_tensor,c11= 165.77,c12= 63.93,c44 = 79.62):
""" Function to calculate the stress in silicon from a strain tensor
args:
- strain_tensor (numpy array): the strain tensor for which the stress should be calculated. The voigt notation should be used with a 1 x 6 vector but the function can handle 3 x 3 matrix but only take the upper half in this case.
- c11, c12 and c44 (scalar): the coefficient of the compliance matrix in GPa
return:
- stress_tensor (numpy array): the stress tensor calculated of dimension 1 x 6 using the voigt notation
"""
strain_voigt=np.zeros((1,6)) strain_voigt=np.zeros((1,6))
strain_shape=np.shape(strain_tensor) strain_shape=np.shape(strain_tensor)
...@@ -29,9 +33,21 @@ def stress_from_strain(strain_tensor,c11= 165.77,c12= 63.93,c44 = 79.62): ...@@ -29,9 +33,21 @@ def stress_from_strain(strain_tensor,c11= 165.77,c12= 63.93,c44 = 79.62):
[0,0,0,c44,0,0], [0,0,0,c44,0,0],
[0,0,0,0,c44,0], [0,0,0,0,c44,0],
[0,0,0,0,0,c44]]) [0,0,0,0,0,c44]])
return compliance_tensor @ strain_tensor
stress_tensor=compliance_tensor @ strain_tensor
return stress_tensor
def strain_from_stress(stress_tensor,c11= 165.77,c12= 63.93,c44 = 79.62): def strain_from_stress(stress_tensor,c11= 165.77,c12= 63.93,c44 = 79.62):
""" Function to calculate the strain in silicon from a stress tensor
args:
- stress_tensor (numpy array): the stress tensor for which the stress should be calculated. The voigt notation should be used with a 1 x 6 vector but the function can handle 3 x 3 matrix but only take the upper half in this case.
- c11, c12 and c44 (scalar): the coefficient of the compliance matrix in GPa
return:
- strain_tensor (numpy array): the strain tensor calculated of dimension 1 x 6 using the voigt notation
"""
stress_voigt=np.zeros((1,6)) stress_voigt=np.zeros((1,6))
stress_shape=np.shape(stress_tensor) stress_shape=np.shape(stress_tensor)
...@@ -54,11 +70,28 @@ def strain_from_stress(stress_tensor,c11= 165.77,c12= 63.93,c44 = 79.62): ...@@ -54,11 +70,28 @@ def strain_from_stress(stress_tensor,c11= 165.77,c12= 63.93,c44 = 79.62):
[0,0,0,c44,0,0], [0,0,0,c44,0,0],
[0,0,0,0,c44,0], [0,0,0,0,c44,0],
[0,0,0,0,0,c44]]) [0,0,0,0,0,c44]])
return np.linalg.inv(compliance_tensor) @ stress_tensor
def straintensor(direction,cristal_plane,N=11,emin=-0.05,emax=0.05,C11=165.77,C12=63.93,C44=79.62, poisson=True) : strain_tensor=np.linalg.inv(compliance_tensor) @ stress_tensor
# Constants return strain_tensor
retour=np.zeros((3,3,N))
def straintensor(strain_type,strain_direction,N=11,emin=-0.05,emax=0.05,c11=165.77,c12=63.93,c44=79.62, poisson=True):
""" Function to calculate the strain tensor for various strain type and direction
args:
- strain_type (string): the type of strain. Choice between uniaxial ("uni"), biaxial ("bi"), shear ("shear") and hydrostatic ("hydro").
- strain_direction (string): the crystal direction and orientation of the strain. Choice between [001], [110] and [111]. For uniaxial strain, the principal strain is oriented along strain_direction while the principal stresses are perpendicular for biaxial strain.
- N (int): the number of strain tensor calculated linearly between emin and emax
- emin (scalar): the minimal value for the principal strain direction
- emax (scalar): the maximal value for the principal strain direction
- c11, c12 and c44 (scalar): the coefficient of the compliance matrix in GPa
- poisson (boolean): if True, take into account the poisson effect to calculate the strain tensor
return:
- strain_tensor (numpy array): array of dimensions ( 3 x 3 x N ) with the strain tensors calculated
"""
strain_tensor=np.zeros((3,3,N))
eps=np.linspace(emin,emax,N) eps=np.linspace(emin,emax,N)
...@@ -76,7 +109,7 @@ def straintensor(direction,cristal_plane,N=11,emin=-0.05,emax=0.05,C11=165.77,C1 ...@@ -76,7 +109,7 @@ def straintensor(direction,cristal_plane,N=11,emin=-0.05,emax=0.05,C11=165.77,C1
# Matrix computation # Matrix computation
# Uniaxial # Uniaxial
if poisson: if poisson:
uniepar001=-C12/(C11+C12)*eps uniepar001=-c12/(c11+c12)*eps
else: else:
uniepar001=0 uniepar001=0
...@@ -91,7 +124,7 @@ def straintensor(direction,cristal_plane,N=11,emin=-0.05,emax=0.05,C11=165.77,C1 ...@@ -91,7 +124,7 @@ def straintensor(direction,cristal_plane,N=11,emin=-0.05,emax=0.05,C11=165.77,C1
uniepsilon001[2][2]=eps uniepsilon001[2][2]=eps
if poisson: if poisson:
uniepar110=- ( 4*C12*C44 ) / ( 2*C11*C44+(C11+2*C12)*(C11-C12) )*eps uniepar110=- ( 4*c12*c44 ) / ( 2*c11*c44+(c11+2*c12)*(c11-c12) )*eps
else: else:
uniepar110=0 uniepar110=0
...@@ -106,7 +139,7 @@ def straintensor(direction,cristal_plane,N=11,emin=-0.05,emax=0.05,C11=165.77,C1 ...@@ -106,7 +139,7 @@ def straintensor(direction,cristal_plane,N=11,emin=-0.05,emax=0.05,C11=165.77,C1
uniepsilon110[2][2]=uniepar110 uniepsilon110[2][2]=uniepar110
if poisson: if poisson:
uniepar111 = - (C11 + 2*C12 - 2*C44) / (C11 + 2*C12 + 2*C44)*eps uniepar111 = - (c11 + 2*c12 - 2*c44) / (c11 + 2*c12 + 2*c44)*eps
else: else:
uniepar111 = 0 uniepar111 = 0
...@@ -122,7 +155,7 @@ def straintensor(direction,cristal_plane,N=11,emin=-0.05,emax=0.05,C11=165.77,C1 ...@@ -122,7 +155,7 @@ def straintensor(direction,cristal_plane,N=11,emin=-0.05,emax=0.05,C11=165.77,C1
# Biaxial # Biaxial
if poisson: if poisson:
bieper001 = -2 * C12 / C11 * eps bieper001 = -2 * c12 / c11 * eps
else: else:
bieper001=0 bieper001=0
...@@ -137,7 +170,7 @@ def straintensor(direction,cristal_plane,N=11,emin=-0.05,emax=0.05,C11=165.77,C1 ...@@ -137,7 +170,7 @@ def straintensor(direction,cristal_plane,N=11,emin=-0.05,emax=0.05,C11=165.77,C1
biepsilon001[2][2] = bieper001 biepsilon001[2][2] = bieper001
if poisson: if poisson:
bieper110 = -(C11+3*C12-2*C44)/(C11+C12+2*C44)*eps bieper110 = -(c11+3*c12-2*c44)/(c11+c12+2*c44)*eps
else: else:
bieper110 = 0 bieper110 = 0
...@@ -152,7 +185,7 @@ def straintensor(direction,cristal_plane,N=11,emin=-0.05,emax=0.05,C11=165.77,C1 ...@@ -152,7 +185,7 @@ def straintensor(direction,cristal_plane,N=11,emin=-0.05,emax=0.05,C11=165.77,C1
biepsilon110[2][2] = eps biepsilon110[2][2] = eps
if poisson: if poisson:
bieper111 = -2*(C11+2*C12-2*C44)/(C11+2*C12+4*C44)* eps bieper111 = -2*(c11+2*c12-2*c44)/(c11+2*c12+4*c44)* eps
else: else:
bieper111=0 bieper111=0
biepsilon111[0][0] = (bieper111+2*eps)/3 biepsilon111[0][0] = (bieper111+2*eps)/3
...@@ -187,36 +220,49 @@ def straintensor(direction,cristal_plane,N=11,emin=-0.05,emax=0.05,C11=165.77,C1 ...@@ -187,36 +220,49 @@ def straintensor(direction,cristal_plane,N=11,emin=-0.05,emax=0.05,C11=165.77,C1
hydro[0][0] = eps/3 hydro[0][0] = eps/3
hydro[1][1] = eps/3 hydro[1][1] = eps/3
hydro[2][2] = eps/3 hydro[2][2] = eps/3
if direction == "uniaxial": if (strain_type == "uniaxial") or (strain_type == "uni"):
if cristal_plane == "001": if strain_direction == "001":
retour=uniepsilon001 strain_tensor=uniepsilon001
elif cristal_plane == "110": elif strain_direction == "110":
retour=uniepsilon110 strain_tensor=uniepsilon110
elif cristal_plane == "111": elif strain_direction == "111":
retour=uniepsilon111 strain_tensor=uniepsilon111
elif direction == "biaxial": elif (strain_type == "biaxial") or (strain_type == "bi"):
if cristal_plane == "001": if strain_direction == "001":
retour=biepsilon001 strain_tensor=biepsilon001
elif cristal_plane == "110": elif strain_direction == "110":
retour=biepsilon110 strain_tensor=biepsilon110
elif cristal_plane == "111": elif strain_direction == "111":
retour=biepsilon111 strain_tensor=biepsilon111
elif direction == "shear": elif strain_type == "shear":
if cristal_plane == "001": if strain_direction == "001":
retour=shear001 strain_tensor=shear001
elif cristal_plane == "110": elif strain_direction == "110":
retour=shear110 strain_tensor=shear110
elif cristal_plane == "111": elif strain_direction == "111":
retour=shear111 strain_tensor=shear111
elif direction =="hydro" : elif (strain_type =="hydro") or (strain_type =="hydrostatic") :
retour = hydro strain_tensor = hydro
return retour return strain_tensor
def straintensor_scalar(strain_type,strain_direction,eps=0,c11=16.577,c12=6.393,c44=7.962,poisson=True):
""" Function to calculate the strain tensor for various strain type and direction
args:
- strain_type (string): the type of strain. Choice between uniaxial ("uni"), biaxial ("bi"), shear ("shear") and hydrostatic ("hydro").
- strain_direction (string): the crystal direction and orientation of the strain. Choice between [001], [110] and [111]. For uniaxial strain, the principal strain is oriented along strain_direction while the principal stresses are perpendicular for biaxial strain.
- eps (scalar): the value of strain in the principal strain direction
- c11, c12 and c44 (scalar): the coefficient of the compliance matrix in GPa
- poisson (boolean): if True, take into account the poisson effect to calculate the strain tensor
return:
- strain_tensor (numpy array): array of dimensions ( 3 x 3 ) with the strain tensor calculated
"""
def straintensor_scalar(direction,cristal_plane,eps=0,C11=16.577,C12=6.393,C44=7.962,poisson=True) : strain_tensor=np.zeros((3,3))
# Constants
retour=np.zeros((3,3))
# Initiation # Initiation
...@@ -233,7 +279,7 @@ def straintensor_scalar(direction,cristal_plane,eps=0,C11=16.577,C12=6.393,C44=7 ...@@ -233,7 +279,7 @@ def straintensor_scalar(direction,cristal_plane,eps=0,C11=16.577,C12=6.393,C44=7
# Matrix computation # Matrix computation
# Uniaxial # Uniaxial
if poisson: if poisson:
uniepar001=-C12/(C11+C12)*eps uniepar001=-c12/(c11+c12)*eps
else: else:
uniepar001=0 uniepar001=0
...@@ -248,7 +294,7 @@ def straintensor_scalar(direction,cristal_plane,eps=0,C11=16.577,C12=6.393,C44=7 ...@@ -248,7 +294,7 @@ def straintensor_scalar(direction,cristal_plane,eps=0,C11=16.577,C12=6.393,C44=7
uniepsilon001[2][2]=eps uniepsilon001[2][2]=eps
if poisson: if poisson:
uniepar110=- ( 4*C12*C44 ) / ( 2*C11*C44+(C11+2*C12)*(C11-C12) )*eps uniepar110=- ( 4*c12*c44 ) / ( 2*c11*c44+(c11+2*c12)*(c11-c12) )*eps
else: else:
uniepar110=0 uniepar110=0
...@@ -264,7 +310,7 @@ def straintensor_scalar(direction,cristal_plane,eps=0,C11=16.577,C12=6.393,C44=7 ...@@ -264,7 +310,7 @@ def straintensor_scalar(direction,cristal_plane,eps=0,C11=16.577,C12=6.393,C44=7
uniepsilon110[2][2]=uniepar110 uniepsilon110[2][2]=uniepar110
if poisson: if poisson:
uniepar111 = - (C11 + 2*C12 - 2*C44) / (C11 + 2*C12 + 2*C44)*eps uniepar111 = - (c11 + 2*c12 - 2*c44) / (c11 + 2*c12 + 2*c44)*eps
else: else:
uniepar111=0 uniepar111=0
...@@ -280,7 +326,7 @@ def straintensor_scalar(direction,cristal_plane,eps=0,C11=16.577,C12=6.393,C44=7 ...@@ -280,7 +326,7 @@ def straintensor_scalar(direction,cristal_plane,eps=0,C11=16.577,C12=6.393,C44=7
# Biaxial # Biaxial
if poisson: if poisson:
bieper001 = -2 * C12 / C11 * eps bieper001 = -2 * c12 / c11 * eps
else: else:
bieper001=0 bieper001=0
...@@ -297,7 +343,7 @@ def straintensor_scalar(direction,cristal_plane,eps=0,C11=16.577,C12=6.393,C44=7 ...@@ -297,7 +343,7 @@ def straintensor_scalar(direction,cristal_plane,eps=0,C11=16.577,C12=6.393,C44=7
biepsilon001[2][2] = bieper001 biepsilon001[2][2] = bieper001
if poisson: if poisson:
bieper110 = -(C11+3*C12-2*C44)/(C11+C12+2*C44)*eps bieper110 = -(c11+3*c12-2*c44)/(c11+c12+2*c44)*eps
else: else:
bieper110=0 bieper110=0
...@@ -313,7 +359,7 @@ def straintensor_scalar(direction,cristal_plane,eps=0,C11=16.577,C12=6.393,C44=7 ...@@ -313,7 +359,7 @@ def straintensor_scalar(direction,cristal_plane,eps=0,C11=16.577,C12=6.393,C44=7
biepsilon110[2][2] = eps biepsilon110[2][2] = eps
if poisson: if poisson:
bieper111 = -2*(C11+2*C12-2*C44)/(C11+2*C12+4*C44)* eps bieper111 = -2*(c11+2*c12-2*c44)/(c11+2*c12+4*c44)* eps
else: else:
bieper111=0 bieper111=0
...@@ -350,29 +396,29 @@ def straintensor_scalar(direction,cristal_plane,eps=0,C11=16.577,C12=6.393,C44=7 ...@@ -350,29 +396,29 @@ def straintensor_scalar(direction,cristal_plane,eps=0,C11=16.577,C12=6.393,C44=7
hydro[0][0] = eps/3 hydro[0][0] = eps/3
hydro[1][1] = eps/3 hydro[1][1] = eps/3
hydro[2][2] = eps/3 hydro[2][2] = eps/3
if direction == "uni": if (strain_type == "uniaxial") or (strain_type == "uni"):
if cristal_plane == "001": if strain_direction == "001":
retour=uniepsilon001 strain_tensor=uniepsilon001
elif cristal_plane == "110": elif strain_direction == "110":
retour=uniepsilon110 strain_tensor=uniepsilon110
elif cristal_plane == "111": elif strain_direction == "111":
retour=uniepsilon111 strain_tensor=uniepsilon111
elif direction == "bi": elif (strain_type == "biaxial") or (strain_type == "bi"):
if cristal_plane == "001": if strain_direction == "001":
retour=biepsilon001 strain_tensor=biepsilon001
elif cristal_plane == "110": elif strain_direction == "110":
retour=biepsilon110 strain_tensor=biepsilon110
elif cristal_plane == "111": elif strain_direction == "111":
retour=biepsilon111 strain_tensor=biepsilon111
elif direction == "shear": elif strain_type == "shear":
if cristal_plane == "001": if strain_direction == "001":
retour=shear001 strain_tensor=shear001
elif cristal_plane == "110": elif strain_direction == "110":
retour=shear110 strain_tensor=shear110
elif cristal_plane == "111": elif strain_direction == "111":
retour=shear111 strain_tensor=shear111
elif direction =="hydro" : elif (strain_type =="hydro") or (strain_type =="hydrostatic") :
retour = hydro strain_tensor = hydro
return retour return strain_tensor
\ No newline at end of file \ No newline at end of file
import numpy as np import numpy as np
import matplotlib.pyplot as plt import mechanics as mec
def mos_parameter_extraction: def mobility_impurity(carrier="n",Ni=1e15,temp=300, dopant="phosphorus"):
# linear fit """ Function to calculate the silicon mobility according to Masetti relation (1983)
def threshold_voltage_extraction: args:
- carrier (string): "n" for electrons, "p" for holes
- temp (scalar): the temperature
- Ni (scalar): the impurity cencentration in cm-3
- dopant (string): the type of n-type dopant. "phosphorus" or "arsenic"
return:
- mu_LI (scalar): the electron or hole mobility with the impurity scattering taken into account
"""
def bjt_transistor_current: # Values are taken from Masetti et al. (1983)
if dopant=="phosphorus":
param_300_n={"mu_min":68.5,"Cref":9.20e16,"alpha":0.711}
correction_n={"mu_min":56.1,"Cref":3.41e20,"alpha":1.98}
mu_0=1414
elif dopant=="arsenic":
param_300_n={"mu_min":52.2,"Cref":9.68e16,"alpha":0.680}
correction_n={"mu_min":43.4,"Cref":3.43e20,"alpha":2.00}
mu_0=1417
def mos_transistor_current: param_300_p={"mu_min":44.9,"Cref":22.3e16,"alpha":0.72}
correction_p={"mu_min":29.0,"Cref":6.1e20,"alpha":2.0}
def diode_current: expon_temp={"mu_min":-0.45,"Cref":3.2,"alpha":0.065}
if carrier=="n":
param_300=param_300_n
correction=correction_n["mu_min"]/(1+(correction_n["Cref"]/Ni)**correction_n["alpha"])
else:
param_300=param_300_p
correction=correction_p["mu_min"]/(1+(correction_p["Cref"]/Ni)**correction_p["alpha"])
mu_0=470.5
mu_min=param_300["mu_min"]*(temp/300)**expon_temp["mu_min"]
Cref=param_300["Cref"]*(temp/300)**expon_temp["Cref"]
alpha=param_300["alpha"]*(temp/300)**expon_temp["alpha"]
mu_LI=mu_min+(mu_0-mu_min) / ( 1 + ( Ni / Cref )**alpha )-correction
return mu_LI
def intrinsic_concentration(temp):
""" Function to calculate the intrinsic concentration of silicon from K. Misiakos and Tsamakis, D., “Accurate measurements of the silicon intrinsic carrier density from 78 to 340 K”, Journal of Applied Physics, vol. 74, no. 5, p. 3293, 1993.
args:
- temp (scalar): the temperature
return:
- ni (scalar): the intrinsic concentration in silicon
"""
return 5.29e19 * (temp/300)**2.54 * np.exp(-6726/temp)
def tau_srh(Ni, tau_0=5e-5, Nc=5e16, A=1, B=1, C=0, E=0):
""" Function to calculate the SRH lifetime of silicon. The default parameters, similar for electron and hole, are those from D'Avanzo, D. C., Vanzi, M., Dutton, R. W.: One-Dimensional Semiconductor Device Analysis
(SEDAN). Report G-201-5, Stanford University, 1979.
args:
- Ni (scalar): the impurity density in the material
- tau_0 (scalar): intial value for the lifetime
- Nc (scalar): the critical impurity level
- A, B, C and D (scalar): the coefficient for the model
return:
- the SRH lifetime in silicon
"""
return tau_0/(A+B*(Ni/Nc)+C*(Ni/Nc)**E)
def tau_trap(tau_n, tau_p, NA, ND, Etrap=0.56,Eg=1.12, temp=300):
""" Function to calculate the lifetime of silicon due to a trap.
args:
- tau_n and tau_p (scalar): the life time of the electron and hole due to the trap density.
The lifetime can be calculated by tau_n = 1 / (sigma_n * v_th * Nt ) where sigma_n is the capture cross section, vth is the thermal velocity and Nt is the trap density.
Typical values for the thermal velocities are 2.3e7 and 1.65e7 cm/s for the electrons and holes, respectively.
Typical value for the capture cross section is 1e-15 cm² for a neutral defect.
Typical value for the trap density is 1e12 cm-3 for a neutral defect.
- NA and ND (scalar): the acceptor and donor density of the pn junction
- Etrap (scalar): the trap level referred to the maximum of the valence band
- Eg (scalar): the bandgap of the material
- temp (scalar): the temperature
return:
- the trap lifetime
"""
kB = 1.38e-23 # J/K
q = 1.602e-19 # C
nieff=intrinsic_concentration(temp)
def schottky_current: n_0 = nieff**2 / NA
p_0 = nieff**2 / ND
n1 = nieff * np.exp(- q * (Eg - Etrap) / (kB*temp))
p1 = nieff * np.exp(- q * Etrap / (kB*temp))
def photodiode_current: return (tau_p * (n_0 + n1) + tau_n * (p_0 + p1)) / (p_0 + n_0)
def mobility_doping:
def piezoresitivity_semiconductor(stress_tensor, pi11, pi12, pi44): def piezoresitivity_stress(stress_tensor, pi11, pi12, pi44):
""" Function to calculate the relative change due to stress in silicon
args:
- stress_tensor (numpy array): the stress tensor for which the stress should be calculated. The voigt notation should be used with a 1 x 6 vector but the function can handle 3 x 3 matrix but only take the upper half in this case.
- pi11, pi12 and pi44 (scalar): piezoresistive coefficients used to calculate the variations in the relative resistivity.
Values from Smith (1954) are pi11 = -1022 TPa-1, pi12 = 534 TPa-1 and pi44 = -136 TPa-1 for the electrons,
and pi11 = 66 TPa-1, pi12 = -11 TPa-1 and pi44 = 1381 TPa-1 for the holes.
return:
- an 1 x 6 tensor using the Voigt notation with the relative variation of the resistivity.
"""
stress_voigt=np.zeros((1,6)) stress_voigt=np.zeros((1,6))
stress_shape=np.shape(stress_tensor) stress_shape=np.shape(stress_tensor)
...@@ -43,4 +142,27 @@ def piezoresitivity_semiconductor(stress_tensor, pi11, pi12, pi44): ...@@ -43,4 +142,27 @@ def piezoresitivity_semiconductor(stress_tensor, pi11, pi12, pi44):
[0,0,0,0,0,pi44]]) [0,0,0,0,0,pi44]])
return piezo_tensor @ stress_tensor return piezo_tensor @ stress_tensor
\ No newline at end of file def piezoresitivity_strain(strain_tensor, pi11, pi12, pi44):
""" Function to calculate the relative change due to stress in silicon
args:
- stress_tensor (numpy array): the stress tensor for which the stress should be calculated. The voigt notation should be used with a 1 x 6 vector but the function can handle 3 x 3 matrix but only take the upper half in this case.
- pi11, pi12 and pi44 (scalar): piezoresistive coefficients used to calculate the variations in the relative resistivity.
Values from Smith (1954) are pi11 = -1022 TPa-1, pi12 = 534 TPa-1 and pi44 = -136 TPa-1 for the electrons,
and pi11 = 66 TPa-1, pi12 = -11 TPa-1 and pi44 = 1381 TPa-1 for the holes.
return:
- an 1 x 6 tensor using the Voigt notation with the relative variation of the resistivity.
"""
piezo_tensor=np.array([[pi11,pi12,pi12,0,0,0],
[pi12,pi11,pi12,0,0,0],
[pi12,pi12,pi11,0,0,0],
[0,0,0,pi44,0,0],
[0,0,0,0,pi44,0],
[0,0,0,0,0,pi44]])
stress_tensor= mec.stress_from_strain(strain_tensor)
return piezo_tensor @ stress_tensor
import data_processing as proc
import numpy as np
import scipy.interpolate as interp
def threshold_voltage_extraction(i,v,smoothing=True,accuracy=6): # second-derivative method
v_step=np.mean(v[1:]-v[:-1])
gm,v_gm=proc.finite_difference(i, v_step,order=2,accuracy=accuracy)
f = interp.InterpolatedUnivariateSpline(v_gm, gm, k=4)
cr_pts = f.derivative().roots()
cr_pts = np.append(cr_pts, (v_gm[0], v_gm[-1])) # also check the endpoints of the interval
cr_vals = f(cr_pts)
max_index = np.argmax(cr_vals)
vth=cr_pts[max_index]
gm_max=cr_vals[max_index]
return vth, gm_max
def mos_transistor_current(vg,vd,vs=0,vth=0.8,k=1e-4,vea=50,mos_type="nmos",early=True):
if mos_type=="nmos":
vth=abs(vth)
vds=vd-vs
vgs=vg-vs
if mos_type=="pmos":
vth=abs(vth)
vds=vs-vd
vgs=vs-vg
if vgs<vth:
mode="cutoff"
elif vgs>=vds:
mode="saturation"
elif vg-vs<vds:
mode="triode"
if mode=="cutoff":
jd=0
elif mode=="saturation":
jd=k/2*(vgs-vth)**2
elif mode=="triode":
jd=k*((vgs-vth)*(vds)-(vds)**2/2)
if early:
return jd*(1+(vds)/vea)
else:
return jd
\ No newline at end of file
0% Chargement en cours ou .
You are about to add 0 people to the discussion. Proceed with caution.
Terminez d'abord l'édition de ce message.
Veuillez vous inscrire ou vous pour commenter