def __init__(self, calc=None, atoms=None, gpw_fname=None, pickle_fname=None): if not _has_gpaw: raise ImportError("GPAW was not imported. Please install gpaw.") if pickle_fname is None: if gpw_fname is not None: calc = GPAW(gpw_fname, fixdensity=True, symmetry='off', txt='TB2J_wrapper.log', basis='dzp', mode='lcao') atoms = calc.atoms atoms.calc = calc E = atoms.get_potential_energy() tb = TightBinding(atoms, calc) self.H_NMM, self.S_NMM = tb.h_and_s() self.Rlist = tb.R_cN.T else: with open(fname, 'rb') as myfile: self.H_NMM, self.S_NMM, self.Rlist = pickle.load(myfile) self.nR, self.nbasis, _ = self.H_NMM.shape self.positions = np.zeros((self.nbasis, 3))
def test_Ham(): calc=GPAW('/home/hexu/projects/gpaw/bccFe/atoms_nscf.gpw', fixdensity=True, symmetry='off', txt='nscf.txt') atoms=calc.atoms atoms.calc=calc calc.set(kpts=(3,3,3)) E = atoms.get_potential_energy() tb=TightBinding(atoms, calc) kpath=monkhorst_pack([3,3,3]) evals, evecs=tb.band_structure(kpath, blochstates=True)
def calculate_supercell_matrix(self, dump=0, name=None, filter=None, include_pseudo=True, atoms=None): """Calculate matrix elements of the el-ph coupling in the LCAO basis. This function calculates the matrix elements between LCAOs and local atomic gradients of the effective potential. The matrix elements are calculated for the supercell used to obtain finite-difference approximations to the derivatives of the effective potential wrt to atomic displacements. Parameters ---------- dump: int Dump supercell matrix to pickle file (default: 0). 0: Supercell matrix not saved 1: Supercell matrix saved in a single pickle file. 2: Dump matrix for different gradients in separate files. Useful for large systems where the total array gets too large for a single pickle file. name: string User specified name of the generated pickle file(s). If not provided, the string in the ``name`` attribute is used. filter: str Fourier filter atomic gradients of the effective potential. The specified components (``normal`` or ``umklapp``) are removed (default: None). include_pseudo: bool Include the contribution from the psedupotential in the atomic gradients. If ``False``, only the gradient of the effective potential is included (default: True). atoms: Atoms object Calculate supercell for an ``Atoms`` object different from the one provided in the ``__init__`` method (WARNING, NOT working!). """ assert self.calc_lcao is not None, "Set LCAO calculator" # Supercell atoms if atoms is None: atoms_N = self.atoms * self.N_c else: atoms_N = atoms # Initialize calculator if required and extract useful quantities calc = self.calc_lcao if not hasattr(calc.wfs, 'S_qMM'): calc.initialize(atoms_N) calc.initialize_positions(atoms_N) self.set_basis_info() basis = calc.input_parameters['basis'] # Extract useful objects from the calculator wfs = calc.wfs gd = calc.wfs.gd kd = calc.wfs.kd kpt_u = wfs.kpt_u setups = wfs.setups nao = setups.nao bfs = wfs.basis_functions dtype = wfs.dtype spin = 0 # XXX # If gamma calculation, overlap with neighboring cell cannot be removed if kd.gamma: print("WARNING: Gamma-point calculation.") else: # Bloch to real-space converter tb = TightBinding(atoms_N, calc) self.timer.write_now("Calculating supercell matrix") self.timer.write_now("Calculating real-space gradients") # Calculate finite-difference gradients (in Hartree / Bohr) V1t_xG, dH1_xasp = self.calculate_gradient() self.timer.write_now("Finished real-space gradients") # Fourier filter the atomic gradients of the effective potential if filter is not None: self.timer.write_now("Fourier filtering gradients") V1_xG = V1t_xG.copy() self.fourier_filter(V1t_xG, components=filter) self.timer.write_now("Finished Fourier filtering") # For the contribution from the derivative of the projectors dP_aqvMi = self.calculate_dP_aqvMi(wfs) # Equilibrium atomic Hamiltonian matrix (projector coefficients) dH_asp = pickle.load(open(self.name + '.eq.pckl'))[1] # Check that the grid is the same as in the calculator assert np.all(V1t_xG.shape[-3:] == (gd.N_c + gd.pbc_c - 1)), \ "Mismatch in grids." # Calculate < i k | grad H | j k >, i.e. matrix elements in Bloch basis # List for supercell matrices; g_xNNMM = [] self.timer.write_now("Calculating gradient of PAW Hamiltonian") # Do each cartesian component separately for i, a in enumerate(self.indices): for v in range(3): # Corresponding array index x = 3 * i + v V1t_G = V1t_xG[x] self.timer.write_now("%s-gradient of atom %u" % (['x', 'y', 'z'][v], a)) # Array for different k-point components g_qMM = np.zeros((len(kpt_u), nao, nao), dtype) # 1) Gradient of effective potential self.timer.write_now( "Starting gradient of effective potential") for kpt in kpt_u: # Matrix elements geff_MM = np.zeros((nao, nao), dtype) bfs.calculate_potential_matrix(V1t_G, geff_MM, q=kpt.q) tri2full(geff_MM, 'L') # Insert in array g_qMM[kpt.q] += geff_MM self.timer.write_now( "Finished gradient of effective potential") if include_pseudo: self.timer.write_now("Starting gradient of pseudo part") # 2) Gradient of non-local part (projectors) self.timer.write_now("Starting gradient of dH^a") P_aqMi = calc.wfs.P_aqMi # 2a) dH^a part has contributions from all other atoms for kpt in kpt_u: # Matrix elements gp_MM = np.zeros((nao, nao), dtype) dH1_asp = dH1_xasp[x] for a_, dH1_sp in dH1_asp.items(): dH1_ii = unpack2(dH1_sp[spin]) gp_MM += np.dot( P_aqMi[a_][kpt.q], np.dot(dH1_ii, P_aqMi[a_][kpt.q].T.conjugate())) g_qMM[kpt.q] += gp_MM self.timer.write_now("Finished gradient of dH^a") self.timer.write_now("Starting gradient of projectors") # 2b) dP^a part has only contributions from the same atoms dP_qvMi = dP_aqvMi[a] dH_ii = unpack2(dH_asp[a][spin]) for kpt in kpt_u: #XXX Sort out the sign here; conclusion -> sign = +1 ! P1HP_MM = +1 * np.dot( dP_qvMi[kpt.q][v], np.dot(dH_ii, P_aqMi[a][kpt.q].T.conjugate())) # Matrix elements gp_MM = P1HP_MM + P1HP_MM.T.conjugate() g_qMM[kpt.q] += gp_MM self.timer.write_now("Finished gradient of projectors") self.timer.write_now("Finished gradient of pseudo part") # Extract R_c=(0, 0, 0) block by Fourier transforming if kd.gamma or kd.N_c is None: g_MM = g_qMM[0] else: # Convert to array g_MM = tb.bloch_to_real_space(g_qMM, R_c=(0, 0, 0))[0] # Reshape to global unit cell indices N = np.prod(self.N_c) # Number of basis function in the primitive cell assert (nao % N) == 0, "Alarm ...!" nao_cell = nao / N g_NMNM = g_MM.reshape((N, nao_cell, N, nao_cell)) g_NNMM = g_NMNM.swapaxes(1, 2).copy() self.timer.write_now("Finished supercell matrix") if dump != 2: g_xNNMM.append(g_NNMM) else: if name is not None: fname = '%s.supercell_matrix_x_%2.2u.%s.pckl' % ( name, x, basis) else: fname = self.name + \ '.supercell_matrix_x_%2.2u.%s.pckl' % (x, basis) if kd.comm.rank == 0: fd = open(fname, 'w') M_a = self.basis_info['M_a'] nao_a = self.basis_info['nao_a'] pickle.dump((g_NNMM, M_a, nao_a), fd, 2) fd.close() self.timer.write_now("Finished gradient of PAW Hamiltonian") if dump != 2: # Collect gradients in one array self.g_xNNMM = np.array(g_xNNMM) # Dump to pickle file using binary mode together with basis info if dump and kd.comm.rank == 0: if name is not None: fname = '%s.supercell_matrix.%s.pckl' % (name, basis) else: fname = self.name + '.supercell_matrix.%s.pckl' % basis fd = open(fname, 'w') M_a = self.basis_info['M_a'] nao_a = self.basis_info['nao_a'] pickle.dump((self.g_xNNMM, M_a, nao_a), fd, 2) fd.close()
def calculate_supercell_matrix(self, dump=0, name=None, filter=None, include_pseudo=True, atoms=None): """Calculate matrix elements of the el-ph coupling in the LCAO basis. This function calculates the matrix elements between LCAOs and local atomic gradients of the effective potential. The matrix elements are calculated for the supercell used to obtain finite-difference approximations to the derivatives of the effective potential wrt to atomic displacements. Parameters ---------- dump: int Dump supercell matrix to pickle file (default: 0). 0: Supercell matrix not saved 1: Supercell matrix saved in a single pickle file. 2: Dump matrix for different gradients in separate files. Useful for large systems where the total array gets too large for a single pickle file. name: string User specified name of the generated pickle file(s). If not provided, the string in the ``name`` attribute is used. filter: str Fourier filter atomic gradients of the effective potential. The specified components (``normal`` or ``umklapp``) are removed (default: None). include_pseudo: bool Include the contribution from the psedupotential in the atomic gradients. If ``False``, only the gradient of the effective potential is included (default: True). atoms: Atoms object Calculate supercell for an ``Atoms`` object different from the one provided in the ``__init__`` method (WARNING, NOT working!). """ assert self.calc_lcao is not None, "Set LCAO calculator" # Supercell atoms if atoms is None: atoms_N = self.atoms * self.N_c else: atoms_N = atoms # Initialize calculator if required and extract useful quantities calc = self.calc_lcao if not hasattr(calc.wfs, 'S_qMM'): calc.initialize(atoms_N) calc.initialize_positions(atoms_N) self.set_basis_info() basis = calc.input_parameters['basis'] # Extract useful objects from the calculator wfs = calc.wfs gd = calc.wfs.gd kd = calc.wfs.kd kpt_u = wfs.kpt_u setups = wfs.setups nao = setups.nao bfs = wfs.basis_functions dtype = wfs.dtype spin = 0 # XXX # If gamma calculation, overlap with neighboring cell cannot be removed if kd.gamma: print "WARNING: Gamma-point calculation." else: # Bloch to real-space converter tb = TightBinding(atoms_N, calc) self.timer.write_now("Calculating supercell matrix") self.timer.write_now("Calculating real-space gradients") # Calculate finite-difference gradients (in Hartree / Bohr) V1t_xG, dH1_xasp = self.calculate_gradient() self.timer.write_now("Finished real-space gradients") # Fourier filter the atomic gradients of the effective potential if filter is not None: self.timer.write_now("Fourier filtering gradients") V1_xG = V1t_xG.copy() self.fourier_filter(V1t_xG, components=filter) self.timer.write_now("Finished Fourier filtering") # For the contribution from the derivative of the projectors dP_aqvMi = self.calculate_dP_aqvMi(wfs) # Equilibrium atomic Hamiltonian matrix (projector coefficients) dH_asp = pickle.load(open(self.name + '.eq.pckl'))[1] # Check that the grid is the same as in the calculator assert np.all(V1t_xG.shape[-3:] == (gd.N_c + gd.pbc_c - 1)), \ "Mismatch in grids." # Calculate < i k | grad H | j k >, i.e. matrix elements in Bloch basis # List for supercell matrices; g_xNNMM = [] self.timer.write_now("Calculating gradient of PAW Hamiltonian") # Do each cartesian component separately for i, a in enumerate(self.indices): for v in range(3): # Corresponding array index x = 3 * i + v V1t_G = V1t_xG[x] self.timer.write_now("%s-gradient of atom %u" % (['x','y','z'][v], a)) # Array for different k-point components g_qMM = np.zeros((len(kpt_u), nao, nao), dtype) # 1) Gradient of effective potential self.timer.write_now("Starting gradient of effective potential") for kpt in kpt_u: # Matrix elements geff_MM = np.zeros((nao, nao), dtype) bfs.calculate_potential_matrix(V1t_G, geff_MM, q=kpt.q) tri2full(geff_MM, 'L') # Insert in array g_qMM[kpt.q] += geff_MM self.timer.write_now("Finished gradient of effective potential") if include_pseudo: self.timer.write_now("Starting gradient of pseudo part") # 2) Gradient of non-local part (projectors) self.timer.write_now("Starting gradient of dH^a") P_aqMi = calc.wfs.P_aqMi # 2a) dH^a part has contributions from all other atoms for kpt in kpt_u: # Matrix elements gp_MM = np.zeros((nao, nao), dtype) dH1_asp = dH1_xasp[x] for a_, dH1_sp in dH1_asp.items(): dH1_ii = unpack2(dH1_sp[spin]) gp_MM += np.dot(P_aqMi[a_][kpt.q], np.dot(dH1_ii, P_aqMi[a_][kpt.q].T.conjugate())) g_qMM[kpt.q] += gp_MM self.timer.write_now("Finished gradient of dH^a") self.timer.write_now("Starting gradient of projectors") # 2b) dP^a part has only contributions from the same atoms dP_qvMi = dP_aqvMi[a] dH_ii = unpack2(dH_asp[a][spin]) for kpt in kpt_u: #XXX Sort out the sign here; conclusion -> sign = +1 ! P1HP_MM = +1 * np.dot(dP_qvMi[kpt.q][v], np.dot(dH_ii, P_aqMi[a][kpt.q].T.conjugate())) # Matrix elements gp_MM = P1HP_MM + P1HP_MM.T.conjugate() g_qMM[kpt.q] += gp_MM self.timer.write_now("Finished gradient of projectors") self.timer.write_now("Finished gradient of pseudo part") # Extract R_c=(0, 0, 0) block by Fourier transforming if kd.gamma or kd.N_c is None: g_MM = g_qMM[0] else: # Convert to array g_MM = tb.bloch_to_real_space(g_qMM, R_c=(0, 0, 0))[0] # Reshape to global unit cell indices N = np.prod(self.N_c) # Number of basis function in the primitive cell assert (nao % N) == 0, "Alarm ...!" nao_cell = nao / N g_NMNM = g_MM.reshape((N, nao_cell, N, nao_cell)) g_NNMM = g_NMNM.swapaxes(1, 2).copy() self.timer.write_now("Finished supercell matrix") if dump != 2: g_xNNMM.append(g_NNMM) else: if name is not None: fname = '%s.supercell_matrix_x_%2.2u.%s.pckl' % (name, x, basis) else: fname = self.name + \ '.supercell_matrix_x_%2.2u.%s.pckl' % (x, basis) if kd.comm.rank == 0: fd = open(fname, 'w') M_a = self.basis_info['M_a'] nao_a = self.basis_info['niAO_a'] pickle.dump((g_NNMM, M_a, nao_a), fd, 2) fd.close() self.timer.write_now("Finished gradient of PAW Hamiltonian") if dump != 2: # Collect gradients in one array self.g_xNNMM = np.array(g_xNNMM) # Dump to pickle file using binary mode together with basis info if dump and kd.comm.rank == 0: if name is not None: fname = '%s.supercell_matrix.%s.pckl' % (name, basis) else: fname = self.name + '.supercell_matrix.%s.pckl' % basis fd = open(fname, 'w') M_a = self.basis_info['M_a'] nao_a = self.basis_info['nao_a'] pickle.dump((self.g_xNNMM, M_a, nao_a), fd, 2) fd.close()