def total_energy(h, nk=10, nbands=None, use_kpm=False, random=True, kp=None): """Return the total energy""" if h.is_sparse and not use_kpm: print("Sparse Hamiltonian but no bands given, taking 20") nbands = 20 from klist import kmesh if kp is None: # no kpoints given kp = kmesh(h.dimensionality, nk=nk) if random == True: # random k-mesh kp = [np.random.random(3) for k in kp] f = h.get_hk_gen() # get generator etot = 0.0 # initialize iv = 0 for k in kp: # loop over kpoints hk = f(k) # kdependent hamiltonian if use_kpm: # Kernel polynomial method etot += kpm.total_energy(hk, scale=10., ntries=20, npol=100) # using KPM else: # conventional diagonalization if nbands is None: vv = lg.eigvalsh(hk) # diagonalize k hamiltonian else: vv, aa = slg.eigsh(hk, k=nbands, which="LM", sigma=0.0) etot += np.sum(vv[vv < 0.0]) # sum energies below fermi energy etot = etot / len(kp) # normalize return etot
def ldos2d(h, e=0.0, delta=0.001, nrep=3, nk=None, mode="green", random=True, num_wf=20): """ Calculate DOS for a 2d system""" if mode == "green": import green if h.dimensionality != 2: raise # only for 1d if nk is not None: print("LDOS using normal integration with nkpoints", nk) gb, gs = green.bloch_selfenergy(h, energy=e, delta=delta, mode="full", nk=nk) d = [-(gb[i, i]).imag for i in range(len(gb))] # get imaginary part else: print("LDOS using renormalization adaptative Green function") gb, gs = green.bloch_selfenergy(h, energy=e, delta=delta, mode="adaptive") d = [-(gb[i, i]).imag for i in range(len(gb))] # get imaginary part elif mode == "arpack": # arpack diagonalization import klist if nk is None: nk = 10 hkgen = h.get_hk_gen() # get generator ds = [] # empty list for k in klist.kmesh(h.dimensionality, nk=nk): # loop over kpoints print("Doing", k) if random: print("Random k-point") k = np.random.random(3) # random k-point hk = csc_matrix(hkgen(k)) # get Hamiltonian ds += [ ldos_arpack(hk, num_wf=num_wf, robust=False, tol=0, e=e, delta=delta) ] d = ds[0] * 0.0 # inititlize for di in ds: d += di # add d /= len(ds) # normalize d = spatial_dos(h, d) # convert to spatial resolved DOS g = h.geometry # store geometry x, y = g.x, g.y # get the coordinates go = h.geometry.copy() # copy geometry go = go.supercell(nrep) # create supercell write_ldos(go.x, go.y, d.tolist() * (nrep**2), z=go.z) # write in file
def multi_ldos(h, es=[0.0], delta=0.001, nrep=3, nk=2, numw=3, random=False): """Calculate many LDOS, by diagonalizing the Hamiltonian""" print("Calculating eigenvectors in LDOS") if h.is_sparse: # sparse Hamiltonian from bandstructure import smalleig print("SPARSE Matrix") evals, ws = [], [] # empty list ks = klist.kmesh(h.dimensionality, nk=nk) # get grid hk = h.get_hk_gen() # get generator for k in ks: # loop print("Diagonalizing in LDOS, SPARSE mode") if random: k = np.random.random(3) # random vector print("RANDOM vector in LDOS") e, w = smalleig(hk(k), numw=numw, evecs=True) evals += [ie for ie in e] ws += [iw for iw in w] # evals = np.concatenate([evals,e]) # store # ws = np.concatenate([ws,w]) # store # raise # (evals,ws) = h.eigenvectors(nk) # get the different eigenvectors else: print("DENSE Matrix") (evals, ws) = h.eigenvectors(nk) # get the different eigenvectors ds = [(np.conjugate(v) * v).real for v in ws] # calculate densities del ws # remove the wavefunctions os.system("rm -rf MULTILDOS") # remove folder os.system("mkdir MULTILDOS") # create folder go = h.geometry.copy() # copy geometry go = go.supercell(nrep) # create supercell fo = open("MULTILDOS/MULTILDOS.TXT", "w") # files with the names for e in es: # loop over energies print("MULTILDOS for energy", e) out = np.array([0.0 for i in range(h.intra.shape[0])]) # initialize for (d, ie) in zip(ds, evals): # loop over wavefunctions fac = delta / ((e - ie)**2 + delta**2) # factor to create a delta out += fac * d # add contribution out /= np.pi # normalize out = spatial_dos(h, out) # resum if necessary name0 = "LDOS_" + str(e) + "_.OUT" # name of the output name = "MULTILDOS/" + name0 write_ldos(go.x, go.y, out.tolist() * (nrep**h.dimensionality), output_file=name) # write in file fo.write(name0 + "\n") # name of the file fo.flush() # flush fo.close() # close file # Now calculate the DOS from dos import calculate_dos es2 = np.linspace(min(es), max(es), len(es) * 10) ys = calculate_dos(evals, es2, delta) # use the Fortran routine from dos import write_dos write_dos(es2, ys, output_file="MULTILDOS/DOS.OUT")
def dos2d(h, use_kpm=False, scale=10., nk=100, ntries=1, delta=None, ndos=500, numw=20, random=True, kpm_window=1.0): """ Calculate density of states of a 2d system""" if h.dimensionality != 2: raise # only for 2d ks = [] from klist import kmesh ks = kmesh(h.dimensionality, nk=nk) if random: ks = [np.random.random(2) for ik in ks] print("Random k-mesh") if not use_kpm: # conventional method hkgen = h.get_hk_gen() # get generator if delta is None: delta = 6. / nk # conventiona algorithm calculate_dos_hkgen(hkgen, ks, ndos=ndos, delta=delta, is_sparse=h.is_sparse, numw=numw) else: # use the kpm npol = ndos // 10 h.turn_sparse() # turn the hamiltonian sparse hkgen = h.get_hk_gen() # get generator mus = np.array([0.0j for i in range(2 * npol)]) # initialize polynomials import kpm tr = timing.Testimator("DOS") ik = 0 for k in ks: # loop over kpoints # print("KPM DOS at k-point",k) ik += 1 tr.remaining(ik, len(ks)) if random: kr = np.random.random(2) print("Random sampling in DOS") hk = hkgen(kr) # hamiltonian else: hk = hkgen(k) # hamiltonian mus += kpm.random_trace(hk / scale, ntries=ntries, n=npol) mus /= len(ks) # normalize by the number of kpoints xs = np.linspace(-0.9, 0.9, ndos) * kpm_window # x points ys = kpm.generate_profile(mus, xs) # generate the profile write_dos(xs * scale, ys) # write in file return (xs, ys)
def eigenvalues(h,nk): """Return all the eigenvalues of a Hamiltonian""" import klist h.turn_dense() ks = klist.kmesh(h.dimensionality,nk=nk) # get grid hkgen = h.get_hk_gen() # get generator e0 = 0.0 import timing est = timing.Testimator(maxite=len(ks)) for k in ks: # loop est.iterate() es = eigvalsh(hkgen(k)).tolist() # add e0 += np.sum(es[es<0.]) # add contribution return e0/len(ks) # return the ground state energy
def eigenvalues(h, nk): """Return all the eigenvalues of a Hamiltonian""" import klist h.turn_dense() ks = klist.kmesh(h.dimensionality, nk=nk) # get grid hkgen = h.get_hk_gen() # get generator e0 = 0.0 import timing est = timing.Testimator(maxite=len(ks)) for k in ks: # loop est.iterate() es = eigvalsh(hkgen(k)).tolist() # add e0 += np.sum(es[es < 0.]) # add contribution return e0 / len(ks) # return the ground state energy
def update_occupied_states(self, fermi_shift=0.0): """Get the eigenvectors for a mesh of kpoints""" mine = None # minimum energy if self.scfmode == "fermi" and self.is_sparse: self.hamiltonian.turn_sparse() print("WARNING!!! using sparse mode") print("Use this mode only if you know what you are doing!!!!\n\n") es, ws, ks = self.hamiltonian.eigenvectors(self.nkgrid, kpoints=True, sparse=True, numw=self.num_waves) if np.max(np.abs(es)) * 0.9 < self.energy_cutoff: print("NOT ENOUGH STATES, recalling with", self.num_waves * 2) self.num_waves += 5 self.update_occupied_states() # call again # raise else: # any other, use brute force es, ws, ks = self.hamiltonian.eigenvectors(self.nkgrid, kpoints=True) # self.kfac = float(len(ks))/self.hamiltonian.intra.shape[0] # number of kpoints of the calculation # mine = min(es)*0.9 # minimum energy retained self.kfac = len( klist.kmesh(self.hamiltonian.dimensionality, nk=self.nkgrid)) if self.scfmode == "filling": # get the fermi energy if the mode requires it self.fermi = get_fermi_energy(es, self.filling, fermi_shift=fermi_shift) elif self.scfmode == "fermi": pass # do nothing self.gap = get_gap(es, self.fermi) # store the gap if self.energy_cutoff is not None: if self.gap > self.energy_cutoff / 2: print("\nEnergy cutoff is to small, halting", self.gap, "\n\n") raise print("Warning!!!! Performing calculation with an energy cutoff") eoccs, voccs, koccs = get_occupied_states(es, ws, ks, self.fermi, mine=self.energy_cutoff, smearing=self.smearing) self.wavefunctions = voccs # store wavefunctions # print(len(voccs),voccs.shape,len(voccs[0])) self.energies = eoccs # store energies self.kvectors = koccs # store kvectors
def eigenvectors(h, nk=10, kpoints=False, k=None, sparse=False, numw=None): import scipy.linalg as lg from scipy.sparse import csc_matrix as csc shape = h.intra.shape if h.dimensionality == 0: vv = lg.eigh(h.intra) vecs = [v for v in vv[1].transpose()] if kpoints: return vv[0], vecs, [[0., 0., 0.] for e in vv[0]] else: return vv[0], vecs elif h.dimensionality > 0: f = h.get_hk_gen() if k is None: from klist import kmesh kp = kmesh(h.dimensionality, nk=nk) # generate a mesh else: kp = np.array([k]) # kpoint given on input # vvs = [lg.eigh(f(k)) for k in kp] # diagonalize k hamiltonian nkp = len(kp) # total number of k-points if sparse: # sparse Hamiltonians vvs = [ slg.eigsh(csc(f(k)), k=numw, which="LM", sigma=0.0, tol=1e-10) for k in kp ] # else: # dense Hamiltonians import parallel if parallel.cores > 1: # in parallel vvs = parallel.multieigh([f(k) for k in kp]) # multidiagonalization else: vvs = [lg.eigh(f(k)) for k in kp] # nume = sum([len(v[0]) for v in vvs]) # number of eigenvalues calculated eigvecs = np.zeros((nume, h.intra.shape[0]), dtype=np.complex) # eigenvectors eigvals = np.zeros(nume) # eigenvalues #### New way #### # eigvals = np.array([iv[0] for iv in vvs]).reshape(nkp*shape[0],order="F") # eigvecs = np.array([iv[1].transpose() for iv in vvs]).reshape((nkp*shape[0],shape[1]),order="F") # if kpoints: # return also the kpoints # kvectors = [] # empty list # for ik in kp: # for i in range(h.intra.shape[0]): kvectors.append(ik) # store # return eigvals,eigvecs,kvectors # else: # return eigvals,eigvecs #### Old way, slightly slower but clearer #### iv = 0 kvectors = [] # empty list for ik in range(len(kp)): # loop over kpoints vv = vvs[ik] # get eigenvalues and eigenvectors for (e, v) in zip(vv[0], vv[1].transpose()): eigvecs[iv] = v.copy() eigvals[iv] = e.copy() kvectors.append(kp[ik]) iv += 1 if kpoints: # return also the kpoints # for iik in range(len(kp)): # ik = kp[iik] # store kpoint # for e in vvs[iik][0]: kvectors.append(ik) # store return eigvals, eigvecs, kvectors else: return eigvals, eigvecs else: raise