def make_current_exact_bandstructure(path, P, sys): Nk1 = P.Nk1 n = P.n kx_in_path = path[:, 0] ky_in_path = path[:, 1] mel_x = evaluate_njit_matrix(sys.melxjit, kx=kx_in_path, ky=ky_in_path, dtype=P.type_complex_np) mel_y = evaluate_njit_matrix(sys.melyjit, kx=kx_in_path, ky=ky_in_path, dtype=P.type_complex_np) mel_in_path = P.E_dir[0]*mel_x + P.E_dir[1]*mel_y mel_ortho = P.E_ort[0]*mel_x + P.E_ort[1]*mel_y @conditional_njit(P.type_complex_np) def current_exact_path(solution, _E_field=0, _A_field=0): J_exact_E_dir = 0 J_exact_ortho = 0 for i_k in range(Nk1): for i in range(n): J_exact_E_dir += - ( mel_in_path[i_k, i, i].real * solution[i_k, i, i].real ) J_exact_ortho += - ( mel_ortho[i_k, i, i].real * solution[i_k, i, i].real ) for j in range(n): if i != j: J_exact_E_dir += - np.real( mel_in_path[i_k, i, j] * solution[i_k, j, i] ) J_exact_ortho += - np.real( mel_ortho[i_k, i, j] * solution[i_k, j, i] ) return J_exact_E_dir, J_exact_ortho return current_exact_path
def diagonalize_path(self, path, P): kx_in_path = path[:, 0] ky_in_path = path[:, 1] pathlen = path[:, 0].size e_path = np.empty([pathlen, P.n], dtype=P.type_real_np) wf_path = np.empty([pathlen, P.n, P.n], dtype=P.type_complex_np) h_in_path = evaluate_njit_matrix(self.hfjit, kx_in_path, ky_in_path) for i in range(pathlen): e_path[i], wf_buff = lin.eigh(h_in_path[i, :, :]) if self.degenerate_eigenvalues: for j in range(int(P.n / 2)): wf1 = np.copy(wf_buff[:, 2 * j]) wf2 = np.copy(wf_buff[:, 2 * j + 1]) wf_buff[:, 2 * j] *= wf2[P.n - 2] wf_buff[:, 2 * j] -= wf1[P.n - 2] * wf2 wf_buff[:, 2 * j + 1] *= wf1[P.n - 1] wf_buff[:, 2 * j + 1] -= wf2[P.n - 1] * wf1 wf_gauged_entry = np.copy(wf_buff[P.gidx, :]) wf_buff[P.gidx, :] = np.abs(wf_gauged_entry) wf_buff[~( np.arange(np.size(wf_buff, axis=0)) == P.gidx)] *= np.exp( 1j * np.angle(wf_gauged_entry.conj())) wf_path[i] = wf_buff return e_path, wf_path
def evaluate_dipole(self, kx, ky, **fkwargs): """ Transforms the symbolic expression for the berry connection/dipole moment matrix to an expression that is numerically evaluated. Parameters ---------- kx, ky : np.ndarray array of all point combinations fkwargs : keyword arguments passed to the symbolic expression """ # Evaluate all kpoints without BZ self.Ax_eval = evaluate_njit_matrix(self.Axfjit, kx, ky, **fkwargs) self.Ay_eval = evaluate_njit_matrix(self.Ayfjit, kx, ky, **fkwargs) return self.Ax_eval, self.Ay_eval
def eigensystem_dipole_path(self, path, P): # Set eigenfunction first time eigensystem_dipole_path is called if self.e is None: self.make_eigensystem_dipole(P) # Retrieve the set of k-points for the current path kx_in_path = path[:, 0] ky_in_path = path[:, 1] pathlen = path[:, 0].size self.e_in_path = np.zeros([pathlen, P.n], dtype=P.type_real_np) if P.do_semicl: self.dipole_path_x = np.zeros([pathlen, P.n, P.n], dtype=P.type_complex_np) self.dipole_path_y = np.zeros([pathlen, P.n, P.n], dtype=P.type_complex_np) else: # Calculate the dipole components along the path self.dipole_path_x = evaluate_njit_matrix(self.Axfjit, kx=kx_in_path, ky=ky_in_path, dtype=P.type_complex_np) self.dipole_path_y = evaluate_njit_matrix(self.Ayfjit, kx=kx_in_path, ky=ky_in_path, dtype=P.type_complex_np) for n, e in enumerate(self.efjit): self.e_in_path[:, n] = e(kx=kx_in_path, ky=ky_in_path) self.wf_in_path = evaluate_njit_matrix(self.Ujit, kx=kx_in_path, ky=ky_in_path, dtype=P.type_complex_np) self.dipole_in_path = P.E_dir[0] * self.dipole_path_x + P.E_dir[ 1] * self.dipole_path_y self.dipole_ortho = P.E_ort[0] * self.dipole_path_x + P.E_ort[ 1] * self.dipole_path_y
def eigensystem_dipole_path(self, path, P): # Retrieve the set of k-points for the current path kx_in_path = path[:, 0] ky_in_path = path[:, 1] pathlen = path[:, 0].size self.e_in_path = np.zeros([pathlen, P.n], dtype=P.type_real_np) self.dipole_path_x = evaluate_njit_matrix(self.dipole_xfjit, kx=kx_in_path, ky=ky_in_path, dtype=P.type_complex_np) self.dipole_path_y = evaluate_njit_matrix(self.dipole_yfjit, kx=kx_in_path, ky=ky_in_path, dtype=P.type_complex_np) for n, e in enumerate(self.efjit): self.e_in_path[:, n] = e(kx=kx_in_path, ky=ky_in_path) self.dipole_in_path = P.E_dir[0] * self.dipole_path_x + P.E_dir[ 1] * self.dipole_path_y self.dipole_ortho = P.E_ort[0] * self.dipole_path_x + P.E_ort[ 1] * self.dipole_path_y
def diagonalize_path(path, P, S): n = P.n gidx = P.gidx Nk1 = P.Nk1 Nk2 = P.Nk2 epsilon = P.epsilon hamiltonian = S.hnp e_path = np.empty([Nk1, n], dtype=P.type_real_np) wf_path = np.empty([Nk1, n, n], dtype=P.type_complex_np) kx_in_path = path[:, 0] ky_in_path = path[:, 1] h_in_path = evaluate_njit_matrix(hamiltonian, kx_in_path, ky_in_path) for i in range(Nk1): e_path[i], wf_buff = lin.eigh(h_in_path[i, :, :]) wf_gauged_entry = np.copy(wf_buff[gidx, :]) wf_buff[gidx, :] = np.abs(wf_gauged_entry) wf_buff[~(np.arange(np.size(wf_buff, axis=0)) == gidx)] *= np.exp( 1j * np.angle(wf_gauged_entry.conj())) wf_path[i] = wf_buff return e_path, wf_path
def make_current_exact_path_hderiv(path, P, sys): """ Function that calculates the exact current via eq. (79) """ E_dir = P.E_dir E_ort = P.E_ort Nk1 = P.Nk1 n = P.n n_sheets = P.n_sheets type_complex_np = P.type_complex_np type_real_np = P.type_real_np sheet_current= P.sheet_current wf_in_path = sys.wf_in_path kx = path[:, 0] ky = path[:, 1] dhdkx = evaluate_njit_matrix(sys.hderivfjit[0], kx=kx, ky=ky, dtype=type_complex_np) dhdky = evaluate_njit_matrix(sys.hderivfjit[1], kx=kx, ky=ky, dtype=type_complex_np) matrix_element_x = np.empty([Nk1, n, n], dtype=type_complex_np) matrix_element_y = np.empty([Nk1, n, n], dtype=type_complex_np) for i_k in range(Nk1): buff = dhdkx[i_k, :, :] @ wf_in_path[i_k, :, :] matrix_element_x[i_k, :, :] = np.conjugate(wf_in_path[i_k, :, :].T) @ buff buff = dhdky[i_k, :, :] @ wf_in_path[i_k,:,:] matrix_element_y[i_k, :, :] = np.conjugate(wf_in_path[i_k, :, :].T) @ buff mel_in_path = matrix_element_x * E_dir[0] + matrix_element_y * E_dir[1] mel_ortho = matrix_element_x * E_ort[0] + matrix_element_y * E_ort[1] @conditional_njit(type_complex_np) def current_exact_path_hderiv(solution, _E_field=0, _A_field=0): if sheet_current: J_exact_E_dir = np.zeros((n_sheets, n_sheets), dtype=type_real_np) J_exact_ortho = np.zeros((n_sheets, n_sheets), dtype=type_real_np) n_s = int(n/n_sheets) for i_k in range(Nk1): sol = np.zeros((n,n), dtype=type_complex_np) for a in range(n): for b in range(n): for c in range(n): for d in range(n): sol[a, d] += np.conjugate(wf_in_path[i_k, b, a]) * wf_in_path[i_k, b, c] * solution[i_k, c, d] # sol = np.dot( wf_in_path[i_k, :, :], np.dot(solution[i_k, :, :], np.conjugate(wf_in_path[i_k, :, :].T) ) ) for s_i in range(n_sheets): for s_j in range(n_sheets): for i in range(n_s): for j in range(n_s): J_exact_E_dir[s_i, s_j] += - np.real( mel_in_path[i_k, n_s*s_i + i, n_s*s_j + j] * sol[n_s*s_i + i, n_s*s_j + j] ) J_exact_ortho[s_i, s_j] += - np.real( mel_ortho[i_k, n_s*s_i + i, n_s*s_j + j] * sol[n_s*s_i + i, n_s*s_j + j] ) else: J_exact_E_dir = 0 J_exact_ortho = 0 for i_k in range(Nk1): for i in range(n): J_exact_E_dir += - ( mel_in_path[i_k, i, i].real * solution[i_k, i, i].real ) J_exact_ortho += - ( mel_ortho[i_k, i, i].real * solution[i_k, i, i].real ) for j in range(n): if i != j: J_exact_E_dir += - np.real( mel_in_path[i_k, i, j] * solution[i_k, j, i] ) J_exact_ortho += - np.real( mel_ortho[i_k, i, j] * solution[i_k, j, i] ) return J_exact_E_dir, J_exact_ortho return current_exact_path_hderiv
def make_emission_exact_path_length(path, P, sys): """ Construct a function that calculates the emission for the system solution per path. Works for length gauge. Parameters ---------- sys : TwoBandSystem Hamiltonian and related functions path : np.ndarray [type_real_np] kx and ky components of path E_dir : np.ndarray [type_real_np] Direction of the electric field do_semicl : bool if semiclassical calculation should be done curvature : SymbolicCurvature Curvature is only needed for semiclassical calculation Returns: -------- emission_kernel : function Calculates per timestep current of a path """ E_dir = P.E_dir E_ort = P.E_ort kx_in_path = path[:, 0] ky_in_path = path[:, 1] pathlen = kx_in_path.size ########################################################## # Berry curvature container ########################################################## if P.do_semicl: Bcurv = np.empty((pathlen, 2), dtype=P.type_complex_np) h_deriv_x = evaluate_njit_matrix(sys.hderivfjit[0], kx=kx_in_path, ky=ky_in_path, dtype=P.type_complex_np) h_deriv_y = evaluate_njit_matrix(sys.hderivfjit[1], kx=kx_in_path, ky=ky_in_path, dtype=P.type_complex_np) h_deriv_E_dir= h_deriv_x*E_dir[0] + h_deriv_y*E_dir[1] h_deriv_ortho = h_deriv_x*E_ort[0] + h_deriv_y*E_ort[1] U = evaluate_njit_matrix(sys.Ujit, kx=kx_in_path, ky=ky_in_path, dtype=P.type_complex_np) U_h = evaluate_njit_matrix(sys.Ujit_h, kx=kx_in_path, ky=ky_in_path, dtype=P.type_complex_np) if P.do_semicl: Bcurv[:, 0] = sys.Bfjit[0][0](kx=kx_in_path, ky=ky_in_path) Bcurv[:, 1] = sys.Bfjit[1][1](kx=kx_in_path, ky=ky_in_path) symmetric_insulator = P.symmetric_insulator do_semicl = P.do_semicl @conditional_njit(P.type_complex_np) def emission_exact_path_length(solution, E_field, _A_field=1): ''' Parameters: ----------- solution : np.ndarray [type_complex_np] Per timestep solution, idx 0 is k; idx 1 is fv, pvc, pcv, fc E_field : type_real_np Per timestep E_field _A_field : dummy In the length gauge this is just a dummy variable Returns: -------- I_E_dir : type_real_np Parallel to electric field component of current I_ortho : type_real_np Orthogonal to electric field component of current ''' solution = solution.reshape(pathlen, 4) I_E_dir = 0 I_ortho = 0 rho_vv = solution[:, 0] rho_vc = solution[:, 1] rho_cv = solution[:, 2] rho_cc = solution[:, 3] if symmetric_insulator: rho_vv = -rho_cc for i_k in range(pathlen): dH_U_E_dir = h_deriv_E_dir[i_k] @ U[i_k] U_h_H_U_E_dir = U_h[i_k] @ dH_U_E_dir dH_U_ortho = h_deriv_ortho[i_k] @ U[i_k] U_h_H_U_ortho = U_h[i_k] @ dH_U_ortho I_E_dir += - U_h_H_U_E_dir[0, 0].real * rho_vv[i_k].real I_E_dir += - U_h_H_U_E_dir[1, 1].real * rho_cc[i_k].real I_E_dir += - 2*np.real(U_h_H_U_E_dir[0, 1] * rho_cv[i_k]) I_ortho += - U_h_H_U_ortho[0, 0].real * rho_vv[i_k].real I_ortho += - U_h_H_U_ortho[1, 1].real * rho_cc[i_k].real I_ortho += - 2*np.real(U_h_H_U_ortho[0, 1] * rho_cv[i_k]) if do_semicl: # '-' because there is q^2 compared to q only at the SBE current I_ortho += -E_field * Bcurv[i_k, 0].real * rho_vv[i_k].real I_ortho += -E_field * Bcurv[i_k, 1].real * rho_vc[i_k].real return I_E_dir, I_ortho return emission_exact_path_length
def evaluate(self, kx, ky, **fkwargs): # Evaluate all kpoints without BZ self.B_eval = evaluate_njit_matrix(self.Bfjit, kx, ky, **fkwargs) return self.B_eval