def calculate_hamiltonian_matrix(self, useibl=True): """Calculate H_kij = H^o_i(k)j(k) + H^u_i(k)j(k) i(k): Bloch sum of omega_i """ epso_kn = [eps_n[:M] for eps_n, M in zip(self.eps_kn, self.M_k)] self.Ho_kii = np.asarray([np.dot(dagger(Uo_ni) * epso_n, Uo_ni) for Uo_ni, epso_n in zip(self.Uo_kni, epso_kn)]) if self.h_lcao_kii!=None and useibl: print "Using h_lcao and infinite band limit" Vo_kni = self.Vo_kni Huf_kii = [h_lcao_ii - np.dot(dagger(Vo_ni) * epso_n, Vo_ni) for h_lcao_ii, Vo_ni, epso_n in zip(self.h_lcao_kii, self.Vo_kni, epso_kn)] self.Huf_kii = np.asarray(Huf_kii) else: print "Using finite band limit (not using h_lcao)" epsu_kn = [eps_n[M:self.N] for eps_n, M in zip(self.eps_kn, self.M_k)] Huf_kii = [np.dot(dagger(Vu_ni) * epsu_n, Vu_ni) for Vu_ni, epsu_n in zip(self.Vu_kni, epsu_kn)] self.Huf_kii = np.asarray(Huf_kii) Hu_kii = [dots(dagger(Uu_li), dagger(b_il), Huf_ii, b_il, Uu_li) for Uu_li, b_il, Huf_ii in zip(self.Uu_kli, self.b_kil, self.Huf_kii)] self.Hu_kii = np.asarray(Hu_kii) self.H_kii = self.Ho_kii + self.Hu_kii
def calculate_edf(self, useibl=True): """Calculate the coefficients b_il in the expansion of the EDF. ``|phi_l> = sum_i b_il |f^u_i>``, in terms of ``|f^u_i> = P^u|f_i>``. To use the infinite band limit set useibl=True. N is the total number of bands to use. """ for k, L in enumerate(self.L_k): if L == 0: assert L != 0, 'L_k=0 for k=%i. Not implemented' % k self.Vo_kni = [V_ni[:M] for V_ni, M in zip(self.V_kni, self.M_k)] self.Fo_kii = np.asarray( [np.dot(dagger(Vo_ni), Vo_ni) for Vo_ni in self.Vo_kni]) if useibl: self.Fu_kii = self.s_lcao_kii - self.Fo_kii else: self.Vu_kni = [ V_ni[M:self.N] for V_ni, M in zip(self.V_kni, self.M_k) ] self.Fu_kii = np.asarray( [np.dot(dagger(Vu_ni), Vu_ni) for Vu_ni in self.Vu_kni]) self.b_kil = [] for Fu_ii, L in zip(self.Fu_kii, self.L_k): b_i, b_ii = la.eigh(Fu_ii) ls = b_i.real.argsort()[-L:] b_il = b_ii[:, ls] #pick out the eigenvec with largest eigenvals. normalize2(b_il, Fu_ii) #normalize the EDF: <phi_l|phi_l> = 1 self.b_kil.append(b_il)
def calculate_edf(self, useibl=True): """Calculate the coefficients b_il in the expansion of the EDF. ``|phi_l> = sum_i b_il |f^u_i>``, in terms of ``|f^u_i> = P^u|f_i>``. To use the infinite band limit set useibl=True. N is the total number of bands to use. """ for k, L in enumerate(self.L_k): if L==0: assert L!=0, 'L_k=0 for k=%i. Not implemented' % k self.Vo_kni = [V_ni[:M] for V_ni, M in zip(self.V_kni, self.M_k)] self.Fo_kii = np.asarray([np.dot(dagger(Vo_ni), Vo_ni) for Vo_ni in self.Vo_kni]) if useibl: self.Fu_kii = self.s_lcao_kii - self.Fo_kii else: self.Vu_kni = [V_ni[M:self.N] for V_ni, M in zip(self.V_kni, self.M_k)] self.Fu_kii = np.asarray([np.dot(dagger(Vu_ni), Vu_ni) for Vu_ni in self.Vu_kni]) self.b_kil = [] for Fu_ii, L in zip(self.Fu_kii, self.L_k): b_i, b_ii = la.eigh(Fu_ii) ls = b_i.real.argsort()[-L:] b_il = b_ii[:, ls] #pick out the eigenvec with largest eigenvals. normalize2(b_il, Fu_ii) #normalize the EDF: <phi_l|phi_l> = 1 self.b_kil.append(b_il)
def single_zeta(paw, spin, verbose=False): angular = [ ['1'], ['y', 'z', 'x'], ['xy', 'yz', '3z^2-r^2', 'xz', 'x^2-y^2'], [ '3x^2y-y^3', 'xyz', '5yz^2-yr^2', '5z^3-3zr^2', '5xz^2-xr^2', 'x^2z-y^2z', 'x^3-3xy^2' ], ] if verbose: print('index atom orbital') p_jn = [] for a, P_ni in paw.wfs.kpt_u[spin].P_ani.items(): setup = paw.wfs.setups[a] i = 0 for l, n in zip(setup.l_j, setup.n_j): if n < 0: break for j in range(i, i + 2 * l + 1): p_jn.append(P_ni[:, j]) if verbose: print('%5i %4i %s_%s' % (len(p_jn), a, 'spdf'[l], angular[l][j - i])) i += 2 * l + 1 projections_nj = dagger(np.array(p_jn)) assert projections_nj.shape[0] >= projections_nj.shape[1] return projections_nj
def localize(self, calc, M=None, T=0, projections=None, ortho=True, verbose=False): # M is size of Hilbert space to fix. Default is ~ number of occ. bands. if M is None: M = 0 f_n = calc.get_occupation_numbers(0, self.spin) while f_n[M] > .01: M += 1 if projections is None: projections = single_zeta(calc, self.spin, verbose=verbose) self.U_nn, self.S_jj = get_locfun_rotation(projections, M, T, ortho) if self.Z_nnc is None: self.value = 1 return self.value self.Z_jjc = np.empty(self.S_jj.shape + (3,)) for c in range(3): self.Z_jjc[:, :, c] = np.dot(dagger(self.U_nn), np.dot(self.Z_nnc[:, :, c], self.U_nn)) self.value = np.sum(np.abs(self.Z_jjc.diagonal())**2) return self.value # / Bohr**6
def localize(self, calc, M=None, T=0, projections=None, ortho=True, verbose=False): # M is size of Hilbert space to fix. Default is ~ number of occ. bands. if M is None: M = 0 f_n = calc.get_occupation_numbers(0, self.spin) while f_n[M] > .01: M += 1 if projections is None: projections = single_zeta(calc, self.spin, verbose=verbose) self.U_nn, self.S_jj = get_locfun_rotation(projections, M, T, ortho) if self.Z_nnc is None: self.value = 1 return self.value self.Z_jjc = np.empty(self.S_jj.shape + (3, )) for c in range(3): self.Z_jjc[:, :, c] = np.dot(dagger(self.U_nn), np.dot(self.Z_nnc[:, :, c], self.U_nn)) self.value = np.sum(np.abs(self.Z_jjc.diagonal())**2) return self.value # / Bohr**6
def single_zeta(paw, spin, verbose=False): angular = [['1'], ['y', 'z', 'x'], ['xy', 'yz', '3z^2-r^2', 'xz', 'x^2-y^2'], ['3x^2y-y^3', 'xyz', '5yz^2-yr^2', '5z^3-3zr^2', '5xz^2-xr^2', 'x^2z-y^2z', 'x^3-3xy^2'], ] if verbose: print('index atom orbital') p_jn = [] for a, P_ni in paw.wfs.kpt_u[spin].P_ani.items(): setup = paw.wfs.setups[a] i = 0 for l, n in zip(setup.l_j, setup.n_j): if n < 0: break for j in range(i, i + 2 * l + 1): p_jn.append(P_ni[:, j]) if verbose: print('%5i %4i %s_%s' % (len(p_jn), a, 'spdf'[l], angular[l][j - i])) i += 2 * l + 1 projections_nj = dagger(np.array(p_jn)) assert projections_nj.shape[0] >= projections_nj.shape[1] return projections_nj
def get_centers(self): z_jjc = np.empty(self.S_jj.shape+(3,)) for c in range(3): z_jjc = np.dot(dagger(self.U_nn), np.dot(self.Z_nnc[:,:,c], self.U_nn)) scaled_c = -np.angle(z_jjc.diagonal()).T / (2 * pi) return (scaled_c % 1.0) * self.cell_c
def get_locfun_rotation(projections_nj, M=None, T=0, ortho=False): """Mikkel Strange's localized functions. projections_nj = <psi_n|p_j> psi_n: eigenstates p_j: localized function Nw = number of localized functions M = Number of fixed states T = Number of virtual states to exclude (from above) """ Nbands, Nw = projections_nj.shape if M is None: M = Nw L = Nw - M # Extra degrees of freedom V = Nbands - M - T# Virtual states a0_nj = projections_nj[:M, :] a0_vj = projections_nj[M:M + V, :] if V == 0: D_jj = np.dot(dagger(projections_nj), projections_nj) U_nj = 1.0 / np.sqrt(D_jj.diagonal()) * projections_nj S_jj = np.dot(dagger(U_nj), U_nj) assert np.diagonal(np.linalg.cholesky(S_jj)).min() > .01, \ 'Close to linear dependence.' if ortho: lowdin(U_nj, S_jj) S_jj = np.identity(len(S_jj)) return U_nj, S_jj #b_v, b_vv = np.linalg.eigh(np.dot(a0_vj, dagger(a0_vj))) #T_vp = b_vv[:, np.argsort(-b_v)[:L]] b_j, b_jj = np.linalg.eigh(np.dot(dagger(a0_vj), a0_vj)) T_vp = np.dot(a0_vj, b_jj[:, np.argsort(-b_j.real)[:L]]) R_vv = np.dot(T_vp, dagger(T_vp)) D_jj = np.dot(dagger(a0_nj), a0_nj) + np.dot(dagger(a0_vj), np.dot(R_vv, a0_vj)) D2_j = 1.0 / np.sqrt(D_jj.diagonal()) ap_nj = D2_j * a0_nj ap_vj = D2_j * np.dot(R_vv, a0_vj) S_jj = np.dot(dagger(ap_nj), ap_nj) + np.dot(dagger(ap_vj), ap_vj) # Check for linear dependencies Scd = np.diagonal(np.linalg.cholesky(S_jj)).min() if Scd < 0.01: print(('Warning: possibly near linear dependence.\n' 'Minimum eigenvalue of cholesky decomposition is %s' % Scd)) if ortho: lowdin(ap_nj, S_jj) lowdin(ap_vj, S_jj) S_jj = np.identity(len(S_jj)) U_nj = np.concatenate([ap_nj.flat, ap_vj.flat]).reshape(M+V, Nw) return U_nj, S_jj
def get_norm_of_projection(self): norm_kn = np.zeros((self.nk, self.N)) Sinv_kii = np.asarray([la.inv(S_ii) for S_ii in self.S_kii]) normo_kn = np.asarray([dots(Uo_ni, Sinv_ii, dagger(Uo_ni)).diagonal() for Uo_ni, Sinv_ii in zip(self.Uo_kni, Sinv_kii)]) Vu_kni = np.asarray([V_ni[M:self.N] for V_ni, M in zip(self.V_kni, self.M_k)]) Pu_kni = [dots(Vu_ni, b_il, Uu_li) for Vu_ni, b_il, Uu_li in zip(Vu_kni, self.b_kil, self.Uu_kli)] normu_kn = np.asarray([dots(Pu_ni, Sinv_ii, dagger(Pu_ni)).diagonal() for Pu_ni, Sinv_ii in zip(Pu_kni, Sinv_kii)]) return np.hstack((normo_kn, normu_kn))
def get_centers(self): z_jjc = np.empty(self.S_jj.shape + (3, )) for c in range(3): z_jjc = np.dot(dagger(self.U_nn), np.dot(self.Z_nnc[:, :, c], self.U_nn)) scaled_c = -np.angle(z_jjc.diagonal()).T / (2 * pi) return (scaled_c % 1.0) * self.cell_c
def get_locfun_rotation(projections_nj, M=None, T=0, ortho=False): """Mikkel Strange's localized functions. projections_nj = <psi_n|p_j> psi_n: eigenstates p_j: localized function Nw = number of localized functions M = Number of fixed states T = Number of virtual states to exclude (from above) """ Nbands, Nw = projections_nj.shape if M is None: M = Nw L = Nw - M # Extra degrees of freedom V = Nbands - M - T # Virtual states a0_nj = projections_nj[:M, :] a0_vj = projections_nj[M:M + V, :] if V == 0: D_jj = np.dot(dagger(projections_nj), projections_nj) U_nj = 1.0 / np.sqrt(D_jj.diagonal()) * projections_nj S_jj = np.dot(dagger(U_nj), U_nj) assert np.diagonal(np.linalg.cholesky(S_jj)).min() > .01, \ 'Close to linear dependence.' if ortho: lowdin(U_nj, S_jj) S_jj = np.identity(len(S_jj)) return U_nj, S_jj #b_v, b_vv = np.linalg.eigh(np.dot(a0_vj, dagger(a0_vj))) #T_vp = b_vv[:, np.argsort(-b_v)[:L]] b_j, b_jj = np.linalg.eigh(np.dot(dagger(a0_vj), a0_vj)) T_vp = np.dot(a0_vj, b_jj[:, np.argsort(-b_j.real)[:L]]) R_vv = np.dot(T_vp, dagger(T_vp)) D_jj = np.dot(dagger(a0_nj), a0_nj) + np.dot(dagger(a0_vj), np.dot(R_vv, a0_vj)) D2_j = 1.0 / np.sqrt(D_jj.diagonal()) ap_nj = D2_j * a0_nj ap_vj = D2_j * np.dot(R_vv, a0_vj) S_jj = np.dot(dagger(ap_nj), ap_nj) + np.dot(dagger(ap_vj), ap_vj) # Check for linear dependencies Scd = np.diagonal(np.linalg.cholesky(S_jj)).min() if Scd < 0.01: print(('Warning: possibly near linear dependence.\n' 'Minimum eigenvalue of cholesky decomposition is %s' % Scd)) if ortho: lowdin(ap_nj, S_jj) lowdin(ap_vj, S_jj) S_jj = np.identity(len(S_jj)) U_nj = np.concatenate([ap_nj.flat, ap_vj.flat]).reshape(M + V, Nw) return U_nj, S_jj
def calculate_rotations(self): Uo_kni = [Vo_ni.copy() for Vo_ni in self.Vo_kni] Uu_kli = [np.dot(dagger(b_il), Fu_ii) for b_il, Fu_ii in zip(self.b_kil, self.Fu_kii)] #Normalize such that <omega_i|omega_i> = <f_i|f_i> for Uo_ni, Uu_li, s_ii in zip(Uo_kni, Uu_kli, self.s_lcao_kii): normalize(Uo_ni, Uu_li, s_ii.diagonal()) self.Uo_kni = Uo_kni self.Uu_kli = Uu_kli
def get_hamiltonian(self, calc): # U^T diag(eps_n) U eps_n = calc.get_eigenvalues(kpt=0, spin=self.spin) return np.dot(dagger(self.U_nn) * eps_n, self.U_nn)
def calculate_overlaps(self): Wo_kii = [np.dot(dagger(Uo_ni), Uo_ni) for Uo_ni in self.Uo_kni] Wu_kii = [np.dot(dagger(Uu_li), Uu_li) for Uu_li in self.Uu_kli] #Wu_kii = [dots(dagger(Uu_li), dagger(b_il), Fu_ii, b_il, Uu_li) #for Uu_li, b_il, Fu_ii in zip(self.Uu_kli, self.b_kil, self.Fu_kii)] self.S_kii = np.asarray(Wo_kii) + np.asarray(Wu_kii)
def normalize2(C, S): C /= np.sqrt(dots(dagger(C), S, C).diagonal())