Source code for ThermoElectric.lifetime


import numpy as np
from numpy.linalg import norm
from scipy.interpolate import PchipInterpolator as interpolator
from scipy.special import jv
from .accum import *


[docs]def tau_p(energy: np.ndarray, alpha_term: np.ndarray, D_v: float, D_a: float, temp: np.ndarray, vel_sound: float, DoS: np.ndarray, rho: float) -> dict: """ Electron-phonon scattering rate using Ravich model Parameters ---------- energy: np.ndarray Energy range alpha_term: np.ndarray Non-parabolic term D_v: float Hole deformation potential D_a: float Electron deformation potential temp: np.ndarray Temperature vel_sound: float Sound velocity DoS: np.ndarray Density of state rho: float Mass density Returns ------- output: dict parabolic and non-parabolic electron-phonon lifetime """ h_bar = 6.582119e-16 # Reduced Planck constant in eV.s k_bolt = 8.617330350e-5 # Boltzmann constant in eV/K e2C = 1.6021765e-19 # e to Coulomb unit change nonparabolic_term = (1 - ((alpha_term.T * energy) / (1 + 2 * alpha_term.T * energy) * (1 - D_v / D_a))) ** 2 \ - 8 / 3 * (alpha_term.T * energy) * (1 + alpha_term.T * energy) / ( 1 + 2 * alpha_term.T * energy) ** 2 * (D_v / D_a) tau_ph_parabolic = rho * vel_sound ** 2 * h_bar \ / np.pi / k_bolt / temp.T / D_a**2 * 1e9 / e2C / DoS # Lifetime for parabolic band tau_ph_nonparabolic = tau_ph_parabolic / nonparabolic_term # Lifetime in nonparabolic band output = {'parabolic_ph_lifetime': tau_ph_parabolic, 'nonparabolic_ph_lifetime': tau_ph_nonparabolic} return output
[docs]def tau_strongly_screened_coulomb(DoS: np.ndarray, screen_len: np.ndarray, n_imp: np.ndarray, dielectric: float) -> np.ndarray: """ Electron-impurity scattering model in highly doped dielectrics Note that for highly doped semiconductors, screen length plays a significant role, therefor should be computed carefully. Highly suggest to use following matlab file "Fermi.m" from: https://www.mathworks.com/matlabcentral/fileexchange/13616-fermi If committed to use python, the package "dfint" works with python2 pip install fdint Parameters ---------- DoS: np.ndarray Density of states screen_len: np.ndarray Screening length n_imp: np.ndarray impurity scattering dielectric: float Dielectric constant Returns ------- tau: np.ndarray Electron-impurity lifetime """ h_bar = 6.582119e-16 # Reduced Planck constant in eV.s e2C = 1.6021765e-19 # e to Coulomb unit change e_o = 8.854187817e-12 # Permittivity in vacuum F/m tau = h_bar / n_imp.T / np.pi / DoS / \ (screen_len.T ** 2 / (4 * np.pi * dielectric * e_o)) ** 2 \ * 1 / e2C ** 2 return tau
[docs]def tau_screened_coulomb(energy: np.ndarray, mass_c: np.ndarray, screen_len: np.ndarray, n_imp: np.ndarray, dielectric: float) -> np.ndarray: """ Electron-ion scattering rate — Brook-Herring model Note that for highly doped semiconductors, screen length plays a significant role, therefor should be computed carefully. Highly suggest to use following matlab file "Fermi.m" from: https://www.mathworks.com/matlabcentral/fileexchange/13616-fermi If committed to use python, the package "dfint" works with python2 pip install fdint Parameters ---------- energy: np.ndarray Energy range mass_c: np.ndarray Conduction band effective mass screen_len: np.ndarray Screening length n_imp: np.ndarray impurity scattering dielectric: float Dielectric constant Returns ------- tau: np.ndarray Electron-impurity lifetime """ h_bar = 6.582119e-16 # Reduced Planck constant in eV.s e2C = 1.6021765e-19 # e to Coulomb unit change e_o = 8.854187817e-12 # Permittivity in vacuum F/m gamma = 8 * mass_c.T * screen_len.T ** 2 * energy / h_bar ** 2 / e2C # Gamma term tau_ = np.log(1 + gamma) - gamma / (1 + gamma) tau = 16 * np.pi * np.sqrt(2 * mass_c.T) * (4 * np.pi * dielectric * e_o) ** 2 \ / n_imp.T / tau_ * energy ** (3 / 2) / e2C ** (5.0/2) tau[np.isnan(tau)] = 0 return tau
[docs]def tau_unscreened_coulomb(energy: np.ndarray, mass_c: np.ndarray, n_imp: np.ndarray, dielectric: float) -> np.ndarray: """ Electron-ion scattering rate for shallow dopants ~10^18 1/cm^3 (no screening effect is considered) Parameters ---------- energy: np.ndarray Energy range mass_c: np.ndarray Conduction band effective mass n_imp: np.ndarray impurity scattering dielectric: float Dielectric constant Returns ------- tau: np.ndarray Electron-impurity lifetime """ e2C = 1.6021765e-19 # e to Coulomb unit change e_o = 8.854187817e-12 # Permittivity in vacuum F/m gamma = 4 * np.pi * (4 * np.pi * dielectric * e_o) * energy / n_imp.T ** (1.0 / 3) / e2C # Gamma term tau_ = np.log(1 + gamma ** 2) tau = 16 * np.pi * np.sqrt(2 * mass_c) * (4 * np.pi * dielectric * e_o) ** 2 \ / n_imp.T / tau_ * energy ** (3 / 2) / e2C ** (5.0 / 2) tau[np.isnan(tau)] = 0 return tau
[docs]def tau_2d_cylinder(energy: np.ndarray, num_kpoints: np.ndarray, Uo: float, relative_mass: np.ndarray, volume_frac: float, valley: np.ndarray, dk_len: float, ro: np.ndarray, lattice_parameter: float, n_sample=2000) -> np.ndarray: """ A fast algorithm that uses Fermi’s golden rule to compute the energy dependent electron scattering rate from cylindrical nano-particles or nano-scale pores infinitely extended perpendicular to the current. Parameters ---------- energy: np.ndarray Energy range num_kpoints: np.ndarray Number of kpoints in each direction Uo: float Barrier height relative_mass: np.ndarray Relative mass of electron volume_frac: float Defects volume fraction valley: np.ndarray Conduction band valley indices dk_len: float Sample size ro: np.ndarray Cylinder radius lattice_parameter: float lattice parameter n_sample: int Mesh sample size Returns ------- tau_cylinder: np.ndarray Electron-defect lifetime """ h_bar = 6.582119e-16 # Reduced Planck constant in eV.s e2C = 1.6021765e-19 # e to Coulomb unit change mass_e = 9.109e-31 # Electron rest mass in Kg m_eff = np.array(relative_mass) * mass_e # Electron conduction effective mass ko = 2 * np.pi / lattice_parameter * np.array(valley) del_k = 2 * np.pi / lattice_parameter * dk_len * np.array([1, 1, 1]) N = volume_frac / np.pi / ro ** 2 # Number density kx = np.linspace(ko[0], ko[0] + del_k[0], num_kpoints[0], endpoint=True) # kpoints mesh ky = np.linspace(ko[1], ko[1] + del_k[1], num_kpoints[1], endpoint=True) # kpoints mesh kz = np.linspace(ko[2], ko[2] + del_k[2], num_kpoints[2], endpoint=True) # kpoints mesh [xk, yk, zk] = np.meshgrid(kx, ky, kz) xk_ = np.reshape(xk, -1) yk_ = np.reshape(yk, -1) zk_ = np.reshape(zk, -1) kpoint = np.array([xk_, yk_, zk_]) # kpoints mesh sampling mag_kpoint = norm(kpoint, axis=0) E = h_bar ** 2 / 2 * \ ((kpoint[0, :] - ko[0]) ** 2 / m_eff[0] + (kpoint[1, :] - ko[1]) ** 2 / m_eff[1] + (kpoint[2, :] - ko[2]) ** 2 / m_eff[2]) * e2C t = np.linspace(0, 2 * np.pi, n_sample) a = np.expand_dims(np.sqrt(2 * m_eff[1] / h_bar ** 2 * E / e2C), axis=0) b = np.expand_dims(np.sqrt(2 * m_eff[2] / h_bar ** 2 * E / e2C), axis=0) ds = np.sqrt((a.T * np.sin(t)) ** 2 + (b.T * np.cos(t)) ** 2) cos_theta = ((a * kpoint[0]).T * np.cos(t) + (b * kpoint[1]).T * np.sin(t) + np.expand_dims(kpoint[2] ** 2, axis=1)) / \ np.sqrt(a.T ** 2 * np.cos(t) ** 2 + b.T ** 2 * np.sin(t) ** 2 + np.expand_dims(kpoint[2] ** 2, axis=1)) / np.expand_dims(mag_kpoint, axis=1) delE = h_bar ** 2 * \ np.abs((a.T * np.cos(t) - ko[0]) / m_eff[0] + (b.T * np.sin(t) - ko[1]) / m_eff[1] + ( np.expand_dims(kpoint[2] ** 2, axis=1) - ko[2] / m_eff[2])) # q_points qx = np.expand_dims(kpoint[0], axis=1) - a.T * np.cos(t) qy = np.expand_dims(kpoint[1], axis=1) - b.T * np.sin(t) qr = np.sqrt(qx ** 2 + qy ** 2) tau = np.empty((len(ro), len(E))) for r_idx in np.arange(len(ro)): J = jv(1, ro[r_idx] * qr) # Bessel func. SR = 2 * np.pi / h_bar * Uo ** 2 * (2 * np.pi) ** 3 * ( ro[r_idx] * J / qr) ** 2 # Scattering rate f = SR * (1 - cos_theta) / delE * ds int_ = np.trapz(f, t, axis=1) tau[r_idx] = 1 / (N[r_idx] / (2 * np.pi) ** 3 * int_) * e2C Ec, indices, return_indices = np.unique(E, return_index=True, return_inverse=True) tau_c = np.empty((len(ro), len(indices))) tau_cylinder = np.empty((len(ro), len(energy[0]))) for r_idx in np.arange(len(ro)): tau_c[r_idx] = accum(return_indices, tau[r_idx], func=np.mean, dtype=float) # Map lifetime to desired energy range for tau_idx in np.arange(len(tau_c)): ESpline = interpolator(Ec[30:], tau_c[tau_idx, 30:]) tau_cylinder[tau_idx] = ESpline(energy) return tau_cylinder
[docs]def tau3D_spherical(num_kpoints: np.ndarray, Uo: float, relative_mass: np.ndarray, volume_frac: float, valley: np.ndarray, dk_len: float, ro: np.ndarray, lattice_parameter: float, n_sample=32) -> np.ndarray: """ A fast algorithm that uses Fermi’s golden rule to compute the energy dependent electron scattering rate from spherical nano-particles or nano-scale pores. Parameters ---------- num_kpoints: np.ndarray Number of kpoints in each direction Uo: float Barrier height relative_mass: np.ndarray Relative mass of electron volume_frac: float Defects volume fraction valley: np.ndarray Conduction band valley indices dk_len: float Sample size ro: np.ndarray Cylinder radius lattice_parameter: float lattice parameter n_sample: int Mesh sample size Returns ------- tau: np.ndarray Electron-defect lifetime """ h_bar = 6.582119e-16 # Reduced Planck constant in eV.s e2C = 1.6021765e-19 # e to Coulomb unit change mass_e = 9.109e-31 # Electron rest mass in Kg m_eff = np.array(relative_mass) * mass_e # Electron conduction band effective mass ko = 2 * np.pi / lattice_parameter * np.array(valley) del_k = 2 * np.pi / lattice_parameter * dk_len * np.array([1, 1, 1]) N = 3 * volume_frac / 4 / np.pi / ro ** 3 # Number density of defects kx = np.linspace(ko[0], ko[0] + del_k[0], num_kpoints[0], endpoint=True) # kpoints mesh ky = np.linspace(ko[1], ko[1] + del_k[1], num_kpoints[1], endpoint=True) # kpoints mesh kz = np.linspace(ko[2], ko[2] + del_k[2], num_kpoints[2], endpoint=True) # kpoints mesh [xk, yk, zk] = np.meshgrid(kx, ky, kz) xk_ = np.reshape(xk, -1) yk_ = np.reshape(yk, -1) zk_ = np.reshape(zk, -1) kpoint = np.array([xk_, yk_, zk_]) # kpoint mesh sampling # Energy levels in ellipsoidal band structure E = h_bar ** 2 / 2 * \ ((kpoint[0, :] - ko[0]) ** 2 / m_eff[0] + (kpoint[1, :] - ko[1]) ** 2 / m_eff[1] + (kpoint[2, :] - ko[2]) ** 2 / m_eff[2]) * e2C scattering_rate = np.zeros((len(ro), len(E))) nu = np.linspace(0, np.pi, n_sample) z_ = -1 * np.cos(nu) r = np.sqrt(1.0 - z_ ** 2)[:, None] theta = np.linspace(0, 2 * np.pi, n_sample)[None, :] x_ = r * np.cos(theta) y_ = r * np.sin(theta) for u in np.arange(len(E)): Q = np.zeros((2 * (n_sample - 2) * (n_sample - 1), 3)) A = np.zeros((2 * (n_sample - 2) * (n_sample - 1), 1)) k = 0 a_axis = np.sqrt(2 / (h_bar ** 2 * e2C) * m_eff[0] * E[u]) b_axis = np.sqrt(2 / (h_bar ** 2 * e2C) * m_eff[1] * E[u]) c_axis = np.sqrt(2 / (h_bar ** 2 * e2C) * m_eff[2] * E[u]) y = -1 * b_axis * y_ + ko[1] x = -1 * a_axis * x_ + ko[0] Z_ = c_axis * z_ + ko[2] z = np.tile(Z_[:, None], (1, n_sample)) for j in np.arange(1, n_sample - 1): for i in np.arange(2, n_sample): S = np.array(np.array([x[i, j], y[i, j], z[i, j]]) + np.array([x[i - 1, j], y[i - 1, j], z[i - 1, j]]) + np.array([x[i - 1, j - 1], y[i - 1, j - 1], z[i - 1, j - 1]])) Q[k] = S / 3 a = norm(np.array([x[i, j], y[i, j], z[i, j]]) - np.array([x[i - 1, j], y[i - 1, j], z[i - 1, j]])) b = norm(np.array([x[i - 1, j], y[i - 1, j], z[i - 1, j]]) - np.array([x[i - 1, j - 1], y[i - 1, j - 1], z[i - 1, j - 1]])) c = norm(np.array([x[i - 1, j - 1], y[i - 1, j - 1], z[i - 1, j - 1]]) - np.array([x[i, j], y[i, j], z[i, j]])) s = a + b + c s = s / 2 A[k] = np.sqrt(s * (s - a) * (s - b) * (s - c)) # Surface area of the triangular mesh elements k += 1 for j in np.arange(1, n_sample - 1): for i in np.arange(1, n_sample - 1): S = np.array([x[i, j - 1], y[i, j - 1], z[i, j - 1]]) + \ np.array([x[i, j], y[i, j], z[i, j]]) + \ np.array([x[i - 1, j - 1], y[i - 1, j - 1], z[i - 1, j - 1]]) Q[k] = S / 3 a = norm(np.array([x[i, j - 1], y[i, j - 1], z[i, j - 1]]) - np.array([x[i, j], y[i, j], z[i, j]])) b = norm(np.array([x[i, j], y[i, j], z[i, j]]) - np.array([x[i - 1, j - 1], y[i - 1, j - 1], z[i - 1, j - 1]])) c = norm(np.array([x[i - 1, j - 1], y[i - 1, j - 1], z[i - 1, j - 1]]) - np.array([x[i, j - 1], y[i, j - 1], z[i, j - 1]])) s = a + b + c s = s / 2 A[k] = np.sqrt(s * (s - a) * (s - b) * (s - c)) k += 1 for i in np.arange(2, n_sample): S = np.array([x[i, 0], y[i, 0], z[i, 0]]) + \ np.array([x[i - 1, 0], y[i - 1, 0], z[i - 1, 0]]) + \ np.array([x[i - 1, -2], y[i - 1, -2], z[i - 1, -2]]) Q[k] = S / 3 a = norm(np.array([x[i, 0], y[i, 0], z[i, 0]]) - np.array([x[i - 1, 0], y[i - 1, 0], z[i - 1, 0]])) b = norm(np.array([x[i - 1, 0], y[i - 1, 0], z[i - 1, 0]]) - np.array([x[i - 1, -2], y[i - 1, -2], z[i - 1, -2]])) c = norm(np.array([x[i - 1, -2], y[i - 1, -2], z[i - 1, -2]]) - np.array([x[i, 0], y[i, 0], z[i, 0]])) s = a + b + c s = s / 2 A[k] = np.sqrt(s * (s - a) * (s - b) * (s - c)) k += 1 for i in np.arange(1, n_sample - 1): S = np.array([x[i, -2], y[i, -2], z[i, -2]]) + \ np.array([x[i, 0], y[i, 0], z[i, 0]]) + \ np.array([x[i - 1, -2], y[i - 1, -2], z[i - 1, -2]]) Q[k] = S / 3 a = norm(np.array([x[i, -2], y[i, -2], z[i, -2]]) - np.array([x[i, 0], y[i, 0], z[i, 0]])) b = norm(np.array([x[i, 0], y[i, 0], z[i, 0]]) - np.array([x[i - 1, -2], y[i - 1, -2], z[i - 1, -2]])) c = norm(np.array([x[i - 1, -2], y[i - 1, -2], z[i - 1, -2]]) - np.array([x[i, -2], y[i, -2], z[i, -2]])) s = a + b + c s = s / 2 A[k] = np.sqrt(s * (s - a) * (s - b) * (s - c)) k += 1 qx = kpoint[0, u] - Q[:, 0] qy = kpoint[1, u] - Q[:, 1] qz = kpoint[2, u] - Q[:, 2] q = np.sqrt(qx ** 2 + qy ** 2 + qz ** 2) cos_theta = np.matmul(kpoint[:, u][None, :], Q.T) / norm(kpoint[:, u]) / np.sqrt(np.sum(Q ** 2, axis=1)) delE = np.abs(h_bar ** 2 * ( (Q[:, 0] - ko[0]) / m_eff[0] + (Q[:, 1] - ko[1]) / m_eff[1] + (Q[:, 2] - ko[2]) / m_eff[2])) for ro_idx in np.arange(len(ro)): M = 4 * np.pi * Uo * (1 / q * np.sin(ro[ro_idx] * q) - ro[ro_idx] * np.cos(ro[ro_idx] * q)) / ( q ** 2) # Matrix element SR = 2 * np.pi / h_bar * M * np.conj(M) # Scattering rate f = SR / delE * (1 - cos_theta) scattering_rate[ro_idx, u] = N[ro_idx] / (2 * np.pi) ** 3 * np.sum(f * A.T) tau = 1/scattering_rate return tau