class SumkLDATools(SumkLDA): """Extends the SumkLDA class with some tools for analysing the data.""" def __init__(self, hdf_file, mu = 0.0, h_field = 0.0, use_lda_blocks = False, lda_data = 'SumK_LDA', symm_corr_data = 'SymmCorr', par_proj_data = 'SumK_LDA_ParProj', symm_par_data = 'SymmPar', bands_data = 'SumK_LDA_Bands'): self.Gupf_refreq = None SumkLDA.__init__(self,hdf_file=hdf_file,mu=mu,h_field=h_field,use_lda_blocks=use_lda_blocks,lda_data=lda_data, symm_corr_data=symm_corr_data,par_proj_data=par_proj_data,symm_par_data=symm_par_data, bands_data=bands_data) def downfold_pc(self,ik,ir,ish,sig,gf_to_downfold,gf_inp): """Downfolding a block of the Greens function""" gf_downfolded = gf_inp.copy() isp = self.names_to_ind[self.SO][sig] # get spin index for proj. matrices gf_downfolded.from_L_G_R(self.Proj_Mat_pc[ik][isp][ish][ir],gf_to_downfold,self.Proj_Mat_pc[ik][isp][ish][ir].conjugate().transpose()) # downfolding G return gf_downfolded def rotloc_all(self,ish,gf_to_rotate,direction): """Local <-> Global rotation of a GF block. direction: 'toLocal' / 'toGlobal' """ assert ((direction=='toLocal')or(direction=='toGlobal')),"Give direction 'toLocal' or 'toGlobal' in rotloc!" #gf_rotated = gf_to_rotate.copy() #if (direction=='toGlobal'): # gf_rotated.from_L_G_R(self.rotmat_all[ish],gf_to_rotate,self.rotmat_all[ish].conjugate().transpose()) #elif (direction=='toLocal'): # gf_rotated.from_L_G_R(self.rotmat_all[ish].conjugate().transpose(),gf_to_rotate,self.rotmat_all[ish]) gf_rotated = gf_to_rotate.copy() if (direction=='toGlobal'): #if (self.rotmat_timeinv[ish]==1): gf_rotated <<= gf_rotated.transpose() #gf_rotated.from_L_G_R(self.rotmat[ish].transpose(),gf_rotated,self.rotmat[ish].conjugate()) if ((self.rotmat_all_timeinv[ish]==1) and (self.SO)): gf_rotated <<= gf_rotated.transpose() gf_rotated.from_L_G_R(self.rotmat_all[ish].conjugate(),gf_rotated,self.rotmat_all[ish].transpose()) else: gf_rotated.from_L_G_R(self.rotmat_all[ish],gf_rotated,self.rotmat_all[ish].conjugate().transpose()) elif (direction=='toLocal'): if ((self.rotmat_all_timeinv[ish]==1)and(self.SO)): gf_rotated <<= gf_rotated.transpose() gf_rotated.from_L_G_R(self.rotmat_all[ish].transpose(),gf_rotated,self.rotmat_all[ish].conjugate()) else: gf_rotated.from_L_G_R(self.rotmat_all[ish].conjugate().transpose(),gf_rotated,self.rotmat_all[ish]) return gf_rotated def lattice_gf_realfreq(self, ik, mu, broadening, mesh=None, beta=40, with_Sigma=True): """Calculates the lattice Green function on the real frequency axis. If self energy is present and with_Sigma=True, the mesh is taken from Sigma. Otherwise, the mesh has to be given.""" ntoi = self.names_to_ind[self.SO] bln = self.blocnames[self.SO] if (not hasattr(self,"Sigma_imp")): with_Sigma=False if (with_Sigma): assert self.Sigma_imp[0].note=='ReFreq',"Real frequency Sigma needed for lattice_gf_realfreq!" beta = self.Sigma_imp[0].beta stmp = self.add_DC() else: assert (not (mesh is None)),"Without Sigma, give the mesh for lattice_gf_realfreq!" if (self.Gupf_refreq is None): # first setting up of Gupf_refreq BS = [ range(self.N_Orbitals[ik][ntoi[ib]]) for ib in bln ] GFStruct = [ (bln[ib], BS[ib]) for ib in range(self.NspinblocsGF[self.SO]) ] a_list = [a for a,al in GFStruct] if (with_Sigma): glist = lambda : [ GfReFreq(indices = al, mesh =self.Sigma_imp[0].mesh) for a,al in GFStruct] else: glist = lambda : [ GfReFreq(indices = al, beta = beta, mesh_array = mesh) for a,al in GFStruct] self.Gupf_refreq = BlockGf(name_list = a_list, block_list = glist(),make_copies=False) self.Gupf_refreq.zero() GFsize = [ gf.N1 for sig,gf in self.Gupf_refreq] unchangedsize = all( [ self.N_Orbitals[ik][ntoi[bln[ib]]]==GFsize[ib] for ib in range(self.NspinblocsGF[self.SO]) ] ) if (not unchangedsize): BS = [ range(self.N_Orbitals[ik][ntoi[ib]]) for ib in bln ] GFStruct = [ (bln[ib], BS[ib]) for ib in range(self.NspinblocsGF[self.SO]) ] a_list = [a for a,al in GFStruct] if (with_Sigma): glist = lambda : [ GfReFreq(indices = al, mesh =self.Sigma_imp[0].mesh) for a,al in GFStruct] else: glist = lambda : [ GfReFreq(indices = al, beta = beta, mesh_array = mesh) for a,al in GFStruct] self.Gupf_refreq = BlockGf(name_list = a_list, block_list = glist(),make_copies=False) self.Gupf_refreq.zero() idmat = [numpy.identity(self.N_Orbitals[ik][ntoi[bl]],numpy.complex_) for bl in bln] self.Gupf_refreq <<= gf_init.A_Omega_Plus_B(A=1,B=1j*broadening) M = copy.deepcopy(idmat) for ibl in range(self.NspinblocsGF[self.SO]): ind = ntoi[bln[ibl]] M[ibl] = self.Hopping[ik][ind] - (idmat[ibl]*mu) - (idmat[ibl] * self.h_field * (1-2*ibl)) self.Gupf_refreq -= M if (with_Sigma): tmp = self.Gupf_refreq.copy() # init temporary storage for icrsh in xrange(self.N_corr_shells): for sig,gf in tmp: tmp[sig] <<= self.upfold(ik,icrsh,sig,stmp[icrsh][sig],gf) self.Gupf_refreq -= tmp # adding to the upfolded GF self.Gupf_refreq.invert() return self.Gupf_refreq def check_input_dos(self, om_min, om_max, n_om, beta=10, broadening=0.01): delta_om = (om_max-om_min)/(n_om-1) mesh = numpy.zeros([n_om],numpy.float_) DOS = {} for bn in self.blocnames[self.SO]: DOS[bn] = numpy.zeros([n_om],numpy.float_) DOSproj = [ {} for icrsh in range(self.N_inequiv_corr_shells) ] DOSproj_orb = [ {} for icrsh in range(self.N_inequiv_corr_shells) ] for icrsh in range(self.N_inequiv_corr_shells): for bn in self.blocnames[self.corr_shells[self.invshellmap[icrsh]][4]]: dl = self.corr_shells[self.invshellmap[icrsh]][3] DOSproj[icrsh][bn] = numpy.zeros([n_om],numpy.float_) DOSproj_orb[icrsh][bn] = numpy.zeros([dl,dl,n_om],numpy.float_) for i in range(n_om): mesh[i] = om_min + delta_om * i # init: Gloc = [] for icrsh in range(self.N_corr_shells): b_list = [a for a,al in self.GFStruct_corr[icrsh]] glist = lambda : [ GfReFreq(indices = al, beta = beta, mesh_array = mesh) for a,al in self.GFStruct_corr[icrsh]] Gloc.append(BlockGf(name_list = b_list, block_list = glist(),make_copies=False)) for icrsh in xrange(self.N_corr_shells): Gloc[icrsh].zero() # initialize to zero for ik in xrange(self.Nk): Gupf=self.lattice_gf_realfreq(ik=ik,mu=self.Chemical_Potential,broadening=broadening,beta=beta,mesh=mesh,with_Sigma=False) Gupf *= self.BZ_weights[ik] # non-projected DOS for iom in range(n_om): for sig,gf in Gupf: asd = gf._data.array[:,:,iom].imag.trace()/(-3.1415926535) DOS[sig][iom] += asd for icrsh in xrange(self.N_corr_shells): tmp = Gloc[icrsh].copy() for sig,gf in tmp: tmp[sig] <<= self.downfold(ik,icrsh,sig,Gupf[sig],gf) # downfolding G Gloc[icrsh] += tmp if (self.symm_op!=0): Gloc = self.Symm_corr.symmetrize(Gloc) if (self.use_rotations): for icrsh in xrange(self.N_corr_shells): for sig,gf in Gloc[icrsh]: Gloc[icrsh][sig] <<= self.rotloc(icrsh,gf,direction='toLocal') # Gloc can now also be used to look at orbitally resolved quantities for ish in range(self.N_inequiv_corr_shells): for sig,gf in Gloc[self.invshellmap[ish]]: # loop over spins for iom in range(n_om): DOSproj[ish][sig][iom] += gf._data.array[:,:,iom].imag.trace()/(-3.1415926535) DOSproj_orb[ish][sig][:,:,:] += gf._data.array[:,:,:].imag/(-3.1415926535) # output: if (mpi.is_master_node()): for bn in self.blocnames[self.SO]: f=open('DOS%s.dat'%bn, 'w') for i in range(n_om): f.write("%s %s\n"%(mesh[i],DOS[bn][i])) f.close() for ish in range(self.N_inequiv_corr_shells): f=open('DOS%s_proj%s.dat'%(bn,ish),'w') for i in range(n_om): f.write("%s %s\n"%(mesh[i],DOSproj[ish][bn][i])) f.close() for i in range(self.corr_shells[self.invshellmap[ish]][3]): for j in range(i,self.corr_shells[self.invshellmap[ish]][3]): Fname = 'DOS'+bn+'_proj'+str(ish)+'_'+str(i)+'_'+str(j)+'.dat' f=open(Fname,'w') for iom in range(n_om): f.write("%s %s\n"%(mesh[iom],DOSproj_orb[ish][bn][i,j,iom])) f.close() def read_par_proj_input_from_hdf(self): """ Reads the data for the partial projectors from the HDF file """ thingstoread = ['Dens_Mat_below','N_parproj','Proj_Mat_pc','rotmat_all','rotmat_all_timeinv'] retval = self.read_input_from_HDF(SubGrp=self.par_proj_data,thingstoread = thingstoread) return retval def dos_partial(self,broadening=0.01): """calculates the orbitally-resolved DOS""" assert hasattr(self,"Sigma_imp"), "Set Sigma First!!" #thingstoread = ['Dens_Mat_below','N_parproj','Proj_Mat_pc','rotmat_all'] #retval = self.read_input_from_HDF(SubGrp=self.par_proj_data, thingstoread=thingstoread) retval = self.read_par_proj_input_from_hdf() if not retval: return retval if self.symm_op: self.Symm_par = Symmetry(self.hdf_file,subgroup=self.symm_par_data) mu = self.Chemical_Potential GFStruct_proj = [ [ (al, range(self.shells[i][3])) for al in self.blocnames[self.SO] ] for i in xrange(self.N_shells) ] Gproj = [BlockGf(name_block_generator = [ (a,GfReFreq(indices = al, mesh = self.Sigma_imp[0].mesh)) for a,al in GFStruct_proj[ish] ], make_copies = False ) for ish in xrange(self.N_shells)] for ish in range(self.N_shells): Gproj[ish].zero() Msh = [x for x in self.Sigma_imp[0].mesh] n_om = len(Msh) DOS = {} for bn in self.blocnames[self.SO]: DOS[bn] = numpy.zeros([n_om],numpy.float_) DOSproj = [ {} for ish in range(self.N_shells) ] DOSproj_orb = [ {} for ish in range(self.N_shells) ] for ish in range(self.N_shells): for bn in self.blocnames[self.SO]: dl = self.shells[ish][3] DOSproj[ish][bn] = numpy.zeros([n_om],numpy.float_) DOSproj_orb[ish][bn] = numpy.zeros([dl,dl,n_om],numpy.float_) ikarray=numpy.array(range(self.Nk)) for ik in mpi.slice_array(ikarray): S = self.lattice_gf_realfreq(ik=ik,mu=mu,broadening=broadening) S *= self.BZ_weights[ik] # non-projected DOS for iom in range(n_om): for sig,gf in S: DOS[sig][iom] += gf._data.array[:,:,iom].imag.trace()/(-3.1415926535) #projected DOS: for ish in xrange(self.N_shells): tmp = Gproj[ish].copy() for ir in xrange(self.N_parproj[ish]): for sig,gf in tmp: tmp[sig] <<= self.downfold_pc(ik,ir,ish,sig,S[sig],gf) Gproj[ish] += tmp # collect data from mpi: for sig in DOS: DOS[sig] = mpi.all_reduce(mpi.world,DOS[sig],lambda x,y : x+y) for ish in xrange(self.N_shells): Gproj[ish] <<= mpi.all_reduce(mpi.world,Gproj[ish],lambda x,y : x+y) mpi.barrier() if (self.symm_op!=0): Gproj = self.Symm_par.symmetrize(Gproj) # rotation to local coord. system: if (self.use_rotations): for ish in xrange(self.N_shells): for sig,gf in Gproj[ish]: Gproj[ish][sig] <<= self.rotloc_all(ish,gf,direction='toLocal') for ish in range(self.N_shells): for sig,gf in Gproj[ish]: for iom in range(n_om): DOSproj[ish][sig][iom] += gf._data.array[:,:,iom].imag.trace()/(-3.1415926535) DOSproj_orb[ish][sig][:,:,:] += gf._data.array[:,:,:].imag / (-3.1415926535) if (mpi.is_master_node()): # output to files for bn in self.blocnames[self.SO]: f=open('./DOScorr%s.dat'%bn, 'w') for i in range(n_om): f.write("%s %s\n"%(Msh[i],DOS[bn][i])) f.close() # partial for ish in range(self.N_shells): f=open('DOScorr%s_proj%s.dat'%(bn,ish),'w') for i in range(n_om): f.write("%s %s\n"%(Msh[i],DOSproj[ish][bn][i])) f.close() for i in range(self.shells[ish][3]): for j in range(i,self.shells[ish][3]): Fname = './DOScorr'+bn+'_proj'+str(ish)+'_'+str(i)+'_'+str(j)+'.dat' f=open(Fname,'w') for iom in range(n_om): f.write("%s %s\n"%(Msh[iom],DOSproj_orb[ish][bn][i,j,iom])) f.close() def spaghettis(self,broadening,shift=0.0,plot_range=None, ishell=None, invert_Akw=False, fermi_surface=False): """ Calculates the correlated band structure with a real-frequency self energy. ATTENTION: Many things from the original input file are are overwritten!!!""" assert hasattr(self,"Sigma_imp"), "Set Sigma First!!" thingstoread = ['Nk','N_Orbitals','Proj_Mat','Hopping','N_parproj','Proj_Mat_pc'] retval = self.read_input_from_HDF(SubGrp=self.bands_data,thingstoread=thingstoread) if not retval: return retval if fermi_surface: ishell=None # print hamiltonian for checks: if ((self.SP==1)and(self.SO==0)): f1=open('hamup.dat','w') f2=open('hamdn.dat','w') for ik in xrange(self.Nk): for i in xrange(self.N_Orbitals[ik][0]): f1.write('%s %s\n'%(ik,self.Hopping[ik][0][i,i].real)) for i in xrange(self.N_Orbitals[ik][1]): f2.write('%s %s\n'%(ik,self.Hopping[ik][1][i,i].real)) f1.write('\n') f2.write('\n') f1.close() f2.close() else: f=open('ham.dat','w') for ik in xrange(self.Nk): for i in xrange(self.N_Orbitals[ik][0]): f.write('%s %s\n'%(ik,self.Hopping[ik][0][i,i].real)) f.write('\n') f.close() #========================================= # calculate A(k,w): mu = self.Chemical_Potential bln = self.blocnames[self.SO] # init DOS: M = [x for x in self.Sigma_imp[0].mesh] n_om = len(M) if plot_range is None: om_minplot = M[0]-0.001 om_maxplot = M[n_om-1] + 0.001 else: om_minplot = plot_range[0] om_maxplot = plot_range[1] if (ishell is None): Akw = {} for ibn in bln: Akw[ibn] = numpy.zeros([self.Nk, n_om ],numpy.float_) else: Akw = {} for ibn in bln: Akw[ibn] = numpy.zeros([self.shells[ishell][3],self.Nk, n_om ],numpy.float_) if fermi_surface: om_minplot = -2.0*broadening om_maxplot = 2.0*broadening Akw = {} for ibn in bln: Akw[ibn] = numpy.zeros([self.Nk,1],numpy.float_) if not (ishell is None): GFStruct_proj = [ (al, range(self.shells[ishell][3])) for al in bln ] Gproj = BlockGf(name_block_generator = [ (a,GfReFreq(indices = al, mesh = self.Sigma_imp[0].mesh)) for a,al in GFStruct_proj ], make_copies = False) Gproj.zero() for ik in xrange(self.Nk): S = self.lattice_gf_realfreq(ik=ik,mu=mu,broadening=broadening) if (ishell is None): # non-projected A(k,w) for iom in range(n_om): if (M[iom]>om_minplot) and (M[iom]<om_maxplot): if fermi_surface: for sig,gf in S: Akw[sig][ik,0] += gf._data.array[:,:,iom].imag.trace()/(-3.1415926535) * (M[1]-M[0]) else: for sig,gf in S: Akw[sig][ik,iom] += gf._data.array[:,:,iom].imag.trace()/(-3.1415926535) Akw[sig][ik,iom] += ik*shift # shift Akw for plotting in xmgrace else: # projected A(k,w): Gproj.zero() tmp = Gproj.copy() for ir in xrange(self.N_parproj[ishell]): for sig,gf in tmp: tmp[sig] <<= self.downfold_pc(ik,ir,ishell,sig,S[sig],gf) Gproj += tmp # TO BE FIXED: # rotate to local frame #if (self.use_rotations): # for sig,gf in Gproj: Gproj[sig] <<= self.rotloc(0,gf,direction='toLocal') for iom in range(n_om): if (M[iom]>om_minplot) and (M[iom]<om_maxplot): for ish in range(self.shells[ishell][3]): for ibn in bln: Akw[ibn][ish,ik,iom] = Gproj[ibn]._data.array[ish,ish,iom].imag/(-3.1415926535) # END k-LOOP if (mpi.is_master_node()): if (ishell is None): for ibn in bln: # loop over GF blocs: if (invert_Akw): maxAkw=Akw[ibn].max() minAkw=Akw[ibn].min() # open file for storage: if fermi_surface: f=open('FS_'+ibn+'.dat','w') else: f=open('Akw_'+ibn+'.dat','w') for ik in range(self.Nk): if fermi_surface: if (invert_Akw): Akw[ibn][ik,0] = 1.0/(minAkw-maxAkw)*(Akw[ibn][ik,0] - maxAkw) f.write('%s %s\n'%(ik,Akw[ibn][ik,0])) else: for iom in range(n_om): if (M[iom]>om_minplot) and (M[iom]<om_maxplot): if (invert_Akw): Akw[ibn][ik,iom] = 1.0/(minAkw-maxAkw)*(Akw[ibn][ik,iom] - maxAkw) if (shift>0.0001): f.write('%s %s\n'%(M[iom],Akw[ibn][ik,iom])) else: f.write('%s %s %s\n'%(ik,M[iom],Akw[ibn][ik,iom])) f.write('\n') f.close() else: for ibn in bln: for ish in range(self.shells[ishell][3]): if (invert_Akw): maxAkw=Akw[ibn][ish,:,:].max() minAkw=Akw[ibn][ish,:,:].min() f=open('Akw_'+ibn+'_proj'+str(ish)+'.dat','w') for ik in range(self.Nk): for iom in range(n_om): if (M[iom]>om_minplot) and (M[iom]<om_maxplot): if (invert_Akw): Akw[ibn][ish,ik,iom] = 1.0/(minAkw-maxAkw)*(Akw[ibn][ish,ik,iom] - maxAkw) if (shift>0.0001): f.write('%s %s\n'%(M[iom],Akw[ibn][ish,ik,iom])) else: f.write('%s %s %s\n'%(ik,M[iom],Akw[ibn][ish,ik,iom])) f.write('\n') f.close() def constr_Sigma_ME(self, filename, beta, n_om, orb = 0): """Uses Data from files to construct a GF object on the real axis.""" #first get the mesh out of one of the files: if (len(self.GFStruct_Solver[orb][0][1])==1): Fname = filename+'_'+self.GFStruct_Solver[orb][0][0]+'.dat' else: Fname = filename+'_'+self.GFStruct_Solver[orb][0][0]+'/'+str(self.GFStruct_Solver[orb][0][1][0])+'_'+str(self.GFStruct_Solver[orb][0][1][0])+'.dat' R = read_fortran_file(Fname) mesh = numpy.zeros([n_om],numpy.float_) try: for i in xrange(n_om): mesh[i] = R.next() sk = R.next() sk = R.next() except StopIteration : # a more explicit error if the file is corrupted. raise "SumkLDA.read_Sigma_ME : reading file failed!" R.close() # now initialize the GF with the mesh a_list = [a for a,al in self.GFStruct_Solver[orb]] glist = lambda : [ GfReFreq(indices = al, beta = beta, mesh_array = mesh) for a,al in self.GFStruct_Solver[orb] ] SigmaME = BlockGf(name_list = a_list, block_list = glist(),make_copies=False) SigmaME.load(filename) SigmaME.note='ReFreq' # This is important for the put_Sigma routine!!! return SigmaME def partial_charges(self): """Calculates the orbitally-resolved density matrix for all the orbitals considered in the input. The theta-projectors are used, hence case.parproj data is necessary""" #thingstoread = ['Dens_Mat_below','N_parproj','Proj_Mat_pc','rotmat_all'] #retval = self.read_input_from_HDF(SubGrp=self.par_proj_data,thingstoread=thingstoread) retval = self.read_par_proj_input_from_hdf() if not retval: return retval if self.symm_op: self.Symm_par = Symmetry(self.hdf_file,subgroup=self.symm_par_data) # Density matrix in the window bln = self.blocnames[self.SO] ntoi = self.names_to_ind[self.SO] self.Dens_Mat_window = [ [numpy.zeros([self.shells[ish][3],self.shells[ish][3]],numpy.complex_) for ish in range(self.N_shells)] for isp in range(len(bln)) ] # init the density matrix mu = self.Chemical_Potential GFStruct_proj = [ [ (al, range(self.shells[i][3])) for al in bln ] for i in xrange(self.N_shells) ] if hasattr(self,"Sigma_imp"): Gproj = [BlockGf(name_block_generator = [ (a,GfImFreq(indices = al, mesh = self.Sigma_imp[0].mesh)) for a,al in GFStruct_proj[ish] ], make_copies = False) for ish in xrange(self.N_shells)] else: Gproj = [BlockGf(name_block_generator = [ (a,GfImFreq(indices = al, beta = 40)) for a,al in GFStruct_proj[ish] ], make_copies = False) for ish in xrange(self.N_shells)] for ish in xrange(self.N_shells): Gproj[ish].zero() ikarray=numpy.array(range(self.Nk)) #print mpi.rank, mpi.slice_array(ikarray) #print "K-Sum starts on node",mpi.rank," at ",datetime.now() for ik in mpi.slice_array(ikarray): #print mpi.rank, ik, datetime.now() S = self.latticeGF_Matsubara(ik=ik,mu=mu) S *= self.BZ_weights[ik] for ish in xrange(self.N_shells): tmp = Gproj[ish].copy() for ir in xrange(self.N_parproj[ish]): for sig,gf in tmp: tmp[sig] <<= self.downfold_pc(ik,ir,ish,sig,S[sig],gf) Gproj[ish] += tmp #print "K-Sum done on node",mpi.rank," at ",datetime.now() #collect data from mpi: for ish in xrange(self.N_shells): Gproj[ish] <<= mpi.all_reduce(mpi.world,Gproj[ish],lambda x,y : x+y) mpi.barrier() #print "Data collected on node",mpi.rank," at ",datetime.now() # Symmetrisation: if (self.symm_op!=0): Gproj = self.Symm_par.symmetrize(Gproj) #print "Symmetrisation done on node",mpi.rank," at ",datetime.now() for ish in xrange(self.N_shells): # Rotation to local: if (self.use_rotations): for sig,gf in Gproj[ish]: Gproj[ish][sig] <<= self.rotloc_all(ish,gf,direction='toLocal') isp = 0 for sig,gf in Gproj[ish]: #dmg.append(Gproj[ish].density()[sig]) self.Dens_Mat_window[isp][ish] = Gproj[ish].density()[sig] isp+=1 # add Density matrices to get the total: Dens_Mat = [ [ self.Dens_Mat_below[ntoi[bln[isp]]][ish]+self.Dens_Mat_window[isp][ish] for ish in range(self.N_shells)] for isp in range(len(bln)) ] return Dens_Mat
class SumkLDA: """This class provides a general SumK method for combining ab-initio code and pytriqs.""" def __init__(self, hdf_file, mu = 0.0, h_field = 0.0, use_lda_blocks = False, lda_data = 'SumK_LDA', symm_corr_data = 'SymmCorr', par_proj_data = 'SumK_LDA_ParProj', symm_par_data = 'SymmPar', bands_data = 'SumK_LDA_Bands'): """ Initialises the class from data previously stored into an HDF5 """ if not (type(hdf_file)==StringType): mpi.report("Give a string for the HDF5 filename to read the input!") else: self.hdf_file = hdf_file self.lda_data = lda_data self.par_proj_data = par_proj_data self.bands_data = bands_data self.symm_par_data = symm_par_data self.symm_corr_data = symm_corr_data self.block_names = [ ['up','down'], ['ud'] ] self.n_spin_blocks_gf = [2,1] self.Gupf = None self.h_field = h_field # read input from HDF: things_to_read = ['energy_unit','n_k','k_dep_projection','SP','SO','charge_below','density_required', 'symm_op','n_shells','shells','n_corr_shells','corr_shells','use_rotations','rot_mat', 'rot_mat_time_inv','n_reps','dim_reps','T','n_orbitals','proj_mat','bz_weights','hopping'] optional_things = ['gf_struct_solver','map_inv','map','chemical_potential','dc_imp','dc_energ','deg_shells'] #ar=HDFArchive(self.hdf_file,'a') #del ar self.retval = self.read_input_from_hdf(subgrp=self.lda_data,things_to_read=things_to_read,optional_things=optional_things) #ar=HDFArchive(self.hdf_file,'a') #del ar if (self.SO) and (abs(self.h_field)>0.000001): self.h_field=0.0 mpi.report("For SO, the external magnetic field is not implemented, setting it to 0!!") self.inequiv_shells(self.corr_shells) # determine the number of inequivalent correlated shells # field to convert block_names to indices self.names_to_ind = [{}, {}] for ibl in range(2): for inm in range(self.n_spin_blocks_gf[ibl]): self.names_to_ind[ibl][self.block_names[ibl][inm]] = inm * self.SP #(self.Nspinblocs-1) # GF structure used for the local things in the k sums self.gf_struct_corr = [ [ (al, range( self.corr_shells[i][3])) for al in self.block_names[self.corr_shells[i][4]] ] for i in xrange(self.n_corr_shells) ] if not (self.retval['gf_struct_solver']): # No gf_struct was stored in HDF, so first set a standard one: self.gf_struct_solver = [ [ (al, range( self.corr_shells[self.invshellmap[i]][3]) ) for al in self.block_names[self.corr_shells[self.invshellmap[i]][4]] ] for i in xrange(self.n_inequiv_corr_shells) ] self.map = [ {} for i in xrange(self.n_inequiv_corr_shells) ] self.map_inv = [ {} for i in xrange(self.n_inequiv_corr_shells) ] for i in xrange(self.n_inequiv_corr_shells): for al in self.block_names[self.corr_shells[self.invshellmap[i]][4]]: self.map[i][al] = [al for j in range( self.corr_shells[self.invshellmap[i]][3] ) ] self.map_inv[i][al] = al if not (self.retval['dc_imp']): # init the double counting: self.__init_dc() if not (self.retval['chemical_potential']): self.chemical_potential = mu if not (self.retval['deg_shells']): self.deg_shells = [ [] for i in range(self.n_inequiv_corr_shells)] if self.symm_op: #mpi.report("Do the init for symm:") self.Symm_corr = Symmetry(hdf_file,subgroup=self.symm_corr_data) # determine the smallest blocs, if wanted: if (use_lda_blocks): dm=self.analyse_BS() # now save things again to HDF5: if (mpi.is_master_node()): ar=HDFArchive(self.hdf_file,'a') ar[self.lda_data]['h_field'] = self.h_field del ar self.save() def read_input_from_hdf(self, subgrp, things_to_read, optional_things=[]): """ Reads data from the HDF file """ retval = True # init variables on all nodes: for it in things_to_read: exec "self.%s = 0"%it for it in optional_things: exec "self.%s = 0"%it if (mpi.is_master_node()): ar=HDFArchive(self.hdf_file,'a') if (subgrp in ar): # first read the necessary things: for it in things_to_read: if (it in ar[subgrp]): exec "self.%s = ar['%s']['%s']"%(it,subgrp,it) else: mpi.report("Loading %s failed!"%it) retval = False if ((retval) and (len(optional_things)>0)): # if necessary things worked, now read optional things: retval = {} for it in optional_things: if (it in ar[subgrp]): exec "self.%s = ar['%s']['%s']"%(it,subgrp,it) retval['%s'%it] = True else: retval['%s'%it] = False else: mpi.report("Loading failed: No %s subgroup in HDF5!"%subgrp) retval = False del ar # now do the broadcasting: for it in things_to_read: exec "self.%s = mpi.bcast(self.%s)"%(it,it) for it in optional_things: exec "self.%s = mpi.bcast(self.%s)"%(it,it) retval = mpi.bcast(retval) return retval def save(self): """Saves some quantities into an HDF5 arxiv""" if not (mpi.is_master_node()): return # do nothing on nodes ar=HDFArchive(self.hdf_file,'a') ar[self.lda_data]['chemical_potential'] = self.chemical_potential ar[self.lda_data]['dc_energ'] = self.dc_energ ar[self.lda_data]['dc_imp'] = self.dc_imp del ar def load(self): """Loads some quantities from an HDF5 arxiv""" things_to_read=['chemical_potential','dc_imp','dc_energ'] retval = self.read_input_from_hdf(subgrp=self.lda_data,things_to_read=things_to_read) return retval def downfold(self,ik,icrsh,sig,gf_to_downfold,gf_inp): """Downfolding a block of the Greens function""" gf_downfolded = gf_inp.copy() isp = self.names_to_ind[self.SO][sig] # get spin index for proj. matrices gf_downfolded.from_L_G_R(self.proj_mat[ik][isp][icrsh],gf_to_downfold,self.proj_mat[ik][isp][icrsh].conjugate().transpose()) # downfolding G return gf_downfolded def upfold(self,ik,icrsh,sig,gf_to_upfold,gf_inp): """Upfolding a block of the Greens function""" gf_upfolded = gf_inp.copy() isp = self.names_to_ind[self.SO][sig] # get spin index for proj. matrices gf_upfolded.from_L_G_R(self.proj_mat[ik][isp][icrsh].conjugate().transpose(),gf_to_upfold,self.proj_mat[ik][isp][icrsh]) return gf_upfolded def rotloc(self,icrsh,gf_to_rotate,direction): """Local <-> Global rotation of a GF block. direction: 'toLocal' / 'toGlobal' """ assert ((direction=='toLocal')or(direction=='toGlobal')),"Give direction 'toLocal' or 'toGlobal' in rotloc!" gf_rotated = gf_to_rotate.copy() if (direction=='toGlobal'): #if (self.rot_mat_time_inv[icrsh]==1): gf_rotated <<= gf_rotated.transpose() #gf_rotated.from_L_G_R(self.rot_mat[icrsh].transpose(),gf_rotated,self.rot_mat[icrsh].conjugate()) if ((self.rot_mat_time_inv[icrsh]==1) and (self.SO)): gf_rotated <<= gf_rotated.transpose() gf_rotated.from_L_G_R(self.rot_mat[icrsh].conjugate(),gf_rotated,self.rot_mat[icrsh].transpose()) else: gf_rotated.from_L_G_R(self.rot_mat[icrsh],gf_rotated,self.rot_mat[icrsh].conjugate().transpose()) elif (direction=='toLocal'): if ((self.rot_mat_time_inv[icrsh]==1)and(self.SO)): gf_rotated <<= gf_rotated.transpose() gf_rotated.from_L_G_R(self.rot_mat[icrsh].transpose(),gf_rotated,self.rot_mat[icrsh].conjugate()) else: gf_rotated.from_L_G_R(self.rot_mat[icrsh].conjugate().transpose(),gf_rotated,self.rot_mat[icrsh]) return gf_rotated def lattice_gf_matsubara(self,ik,mu,beta=40,with_Sigma=True): """Calculates the lattice Green function from the LDA hopping and the self energy at k-point number ik and chemical potential mu.""" ntoi = self.names_to_ind[self.SO] bln = self.block_names[self.SO] if (not hasattr(self,"Sigma_imp")): with_Sigma=False if (with_Sigma): stmp = self.add_dc() beta = self.Sigma_imp[0].beta #override beta if Sigma is present if (self.Gupf is None): # first setting up of Gupf BS = [ range(self.n_orbitals[ik][ntoi[ib]]) for ib in bln ] gf_struct = [ (bln[ib], BS[ib]) for ib in range(self.n_spin_blocks_gf[self.SO]) ] a_list = [a for a,al in gf_struct] if (with_Sigma): #take the mesh from Sigma if necessary glist = lambda : [ GfImFreq(indices = al, mesh = self.Sigma_imp[0].mesh) for a,al in gf_struct] else: glist = lambda : [ GfImFreq(indices = al, beta = beta) for a,al in gf_struct] self.Gupf = BlockGf(name_list = a_list, block_list = glist(),make_copies=False) self.Gupf.zero() GFsize = [ gf.N1 for sig,gf in self.Gupf] unchangedsize = all( [ self.n_orbitals[ik][ntoi[bln[ib]]]==GFsize[ib] for ib in range(self.n_spin_blocks_gf[self.SO]) ] ) if ((not unchangedsize)or(self.Gupf.beta!=beta)): BS = [ range(self.n_orbitals[ik][ntoi[ib]]) for ib in bln ] gf_struct = [ (bln[ib], BS[ib]) for ib in range(self.n_spin_blocks_gf[self.SO]) ] a_list = [a for a,al in gf_struct] if (with_Sigma): glist = lambda : [ GfImFreq(indices = al, mesh = self.Sigma_imp[0].mesh) for a,al in gf_struct] else: glist = lambda : [ GfImFreq(indices = al, beta = beta) for a,al in gf_struct] self.Gupf = BlockGf(name_list = a_list, block_list = glist(),make_copies=False) self.Gupf.zero() idmat = [numpy.identity(self.n_orbitals[ik][ntoi[bl]],numpy.complex_) for bl in bln] #for ibl in range(self.n_spin_blocks_gf[self.SO]): mupat[ibl] *= mu self.Gupf <<= gf_init.A_Omega_Plus_B(A=1,B=0) M = copy.deepcopy(idmat) for ibl in range(self.n_spin_blocks_gf[self.SO]): ind = ntoi[bln[ibl]] M[ibl] = self.hopping[ik][ind] - (idmat[ibl]*mu) - (idmat[ibl] * self.h_field * (1-2*ibl)) self.Gupf -= M if (with_Sigma): tmp = self.Gupf.copy() # init temporary storage for icrsh in xrange(self.n_corr_shells): for sig,gf in tmp: tmp[sig] <<= self.upfold(ik,icrsh,sig,stmp[icrsh][sig],gf) self.Gupf -= tmp # adding to the upfolded GF self.Gupf.invert() return self.Gupf def check_projectors(self): dens_mat = [numpy.zeros([self.corr_shells[ish][3],self.corr_shells[ish][3]],numpy.complex_) for ish in range(self.n_corr_shells)] for ik in range(self.n_k): for ish in range(self.n_corr_shells): Norb = self.corr_shells[ish][3] dens_mat[ish][:,:] += numpy.dot(self.proj_mat[ik][0][ish],self.proj_mat[ik][0][ish].transpose().conjugate()) * self.bz_weights[ik] if (self.symm_op!=0): dens_mat = self.Symm_corr.symmetrize(dens_mat) # Rotate to local coordinate system: if (self.use_rotations): for icrsh in xrange(self.n_corr_shells): if (self.rot_mat_time_inv[icrsh]==1): dens_mat[icrsh] = dens_mat[icrsh].conjugate() dens_mat[icrsh] = numpy.dot( numpy.dot(self.rot_mat[icrsh].conjugate().transpose(),dens_mat[icrsh]) , self.rot_mat[icrsh] ) return dens_mat def simple_point_dens_mat(self): ntoi = self.names_to_ind[self.SO] bln = self.block_names[self.SO] MMat = [numpy.zeros( [self.n_orbitals[0][ntoi[bl]],self.n_orbitals[0][ntoi[bl]]], numpy.complex_) for bl in bln] dens_mat = [ {} for icrsh in xrange(self.n_corr_shells)] for icrsh in xrange(self.n_corr_shells): for bl in self.block_names[self.corr_shells[icrsh][4]]: dens_mat[icrsh][bl] = numpy.zeros([self.corr_shells[icrsh][3],self.corr_shells[icrsh][3]], numpy.complex_) ikarray=numpy.array(range(self.n_k)) for ik in mpi.slice_array(ikarray): unchangedsize = all( [ self.n_orbitals[ik][ntoi[bln[ib]]]==len(MMat[ib]) for ib in range(self.n_spin_blocks_gf[self.SO]) ] ) if (not unchangedsize): MMat = [numpy.zeros( [self.n_orbitals[ik][ntoi[bl]],self.n_orbitals[ik][ntoi[bl]]], numpy.complex_) for bl in bln] for ibl,bl in enumerate(bln): ind = ntoi[bl] for inu in range(self.n_orbitals[ik][ind]): if ( (self.hopping[ik][ind][inu,inu]-self.h_field*(1-2*ibl)) < 0.0): MMat[ibl][inu,inu] = 1.0 else: MMat[ibl][inu,inu] = 0.0 for icrsh in range(self.n_corr_shells): for ibn,bn in enumerate(self.block_names[self.corr_shells[icrsh][4]]): isp = self.names_to_ind[self.corr_shells[icrsh][4]][bn] #print ik, bn, isp dens_mat[icrsh][bn] += self.bz_weights[ik] * numpy.dot( numpy.dot(self.proj_mat[ik][isp][icrsh],MMat[ibn]) , self.proj_mat[ik][isp][icrsh].transpose().conjugate() ) # get data from nodes: for icrsh in range(self.n_corr_shells): for sig in dens_mat[icrsh]: dens_mat[icrsh][sig] = mpi.all_reduce(mpi.world,dens_mat[icrsh][sig],lambda x,y : x+y) mpi.barrier() if (self.symm_op!=0): dens_mat = self.Symm_corr.symmetrize(dens_mat) # Rotate to local coordinate system: if (self.use_rotations): for icrsh in xrange(self.n_corr_shells): for bn in dens_mat[icrsh]: if (self.rot_mat_time_inv[icrsh]==1): dens_mat[icrsh][bn] = dens_mat[icrsh][bn].conjugate() dens_mat[icrsh][bn] = numpy.dot( numpy.dot(self.rot_mat[icrsh].conjugate().transpose(),dens_mat[icrsh][bn]) , self.rot_mat[icrsh]) return dens_mat def density_gf(self,beta): """Calculates the density without setting up Gloc. It is useful for Hubbard I, and very fast.""" dens_mat = [ {} for icrsh in xrange(self.n_corr_shells)] for icrsh in xrange(self.n_corr_shells): for bl in self.block_names[self.corr_shells[icrsh][4]]: dens_mat[icrsh][bl] = numpy.zeros([self.corr_shells[icrsh][3],self.corr_shells[icrsh][3]], numpy.complex_) ikarray=numpy.array(range(self.n_k)) for ik in mpi.slice_array(ikarray): Gupf = self.lattice_gf_matsubara(ik=ik, beta=beta, mu=self.chemical_potential) Gupf *= self.bz_weights[ik] dm = Gupf.density() MMat = [dm[bl] for bl in self.block_names[self.SO]] for icrsh in range(self.n_corr_shells): for ibn,bn in enumerate(self.block_names[self.corr_shells[icrsh][4]]): isp = self.names_to_ind[self.corr_shells[icrsh][4]][bn] #print ik, bn, isp dens_mat[icrsh][bn] += numpy.dot( numpy.dot(self.proj_mat[ik][isp][icrsh],MMat[ibn]),self.proj_mat[ik][isp][icrsh].transpose().conjugate() ) # get data from nodes: for icrsh in range(self.n_corr_shells): for sig in dens_mat[icrsh]: dens_mat[icrsh][sig] = mpi.all_reduce(mpi.world,dens_mat[icrsh][sig],lambda x,y : x+y) mpi.barrier() if (self.symm_op!=0): dens_mat = self.Symm_corr.symmetrize(dens_mat) # Rotate to local coordinate system: if (self.use_rotations): for icrsh in xrange(self.n_corr_shells): for bn in dens_mat[icrsh]: if (self.rot_mat_time_inv[icrsh]==1): dens_mat[icrsh][bn] = dens_mat[icrsh][bn].conjugate() dens_mat[icrsh][bn] = numpy.dot( numpy.dot(self.rot_mat[icrsh].conjugate().transpose(),dens_mat[icrsh][bn]) , self.rot_mat[icrsh] ) return dens_mat def analyse_BS(self, threshold = 0.00001, include_shells = None, dm = None): """ Determines the Greens function block structure from the simple point integration""" if (dm==None): dm = self.simple_point_dens_mat() dens_mat = [dm[self.invshellmap[ish]] for ish in xrange(self.n_inequiv_corr_shells) ] if include_shells is None: include_shells=range(self.n_inequiv_corr_shells) for ish in include_shells: #self.gf_struct_solver.append([]) self.gf_struct_solver[ish] = [] a_list = [a for a,al in self.gf_struct_corr[self.invshellmap[ish]] ] for a in a_list: dm = dens_mat[ish][a] dmbool = (abs(dm) > threshold) # gives an index list of entries larger that threshold offdiag = [] for i in xrange(len(dmbool)): for j in xrange(i,len(dmbool)): if ((dmbool[i,j])&(i!=j)): offdiag.append([i,j]) NBlocs = len(dmbool) blocs = [ [i] for i in range(NBlocs) ] for i in range(len(offdiag)): if (offdiag[i][0]!=offdiag[i][1]): for j in range(len(blocs[offdiag[i][1]])): blocs[offdiag[i][0]].append(blocs[offdiag[i][1]][j]) del blocs[offdiag[i][1]] for j in range(i+1,len(offdiag)): if (offdiag[j][0]==offdiag[i][1]): offdiag[j][0]=offdiag[i][0] if (offdiag[j][1]==offdiag[i][1]): offdiag[j][1]=offdiag[i][0] if (offdiag[j][0]>offdiag[i][1]): offdiag[j][0] -= 1 if (offdiag[j][1]>offdiag[i][1]): offdiag[j][1] -= 1 offdiag[j].sort() NBlocs-=1 for i in range(NBlocs): blocs[i].sort() self.gf_struct_solver[ish].append( ('%s%s'%(a,i),blocs[i]) ) # map is the mapping of the blocs from the SK blocs to the CTQMC blocs: self.map[ish][a] = range(len(dmbool)) for ibl in range(NBlocs): for j in range(len(blocs[ibl])): self.map[ish][a][blocs[ibl][j]] = '%s%s'%(a,ibl) self.map_inv[ish]['%s%s'%(a,ibl)] = a # now calculate degeneracies of orbitals: dm = {} for bl in self.gf_struct_solver[ish]: bln = bl[0] ind = bl[1] # get dm for the blocks: dm[bln] = numpy.zeros([len(ind),len(ind)],numpy.complex_) for i in range(len(ind)): for j in range(len(ind)): dm[bln][i,j] = dens_mat[ish][self.map_inv[ish][bln]][ind[i],ind[j]] for bl in self.gf_struct_solver[ish]: for bl2 in self.gf_struct_solver[ish]: if (dm[bl[0]].shape==dm[bl2[0]].shape) : if ( ( (abs(dm[bl[0]]-dm[bl2[0]])<threshold).all() ) and (bl[0]!=bl2[0]) ): # check if it was already there: ind1=-1 ind2=-2 for n,ind in enumerate(self.deg_shells[ish]): if (bl[0] in ind): ind1=n if (bl2[0] in ind): ind2=n if ((ind1<0)and(ind2>=0)): self.deg_shells[ish][ind2].append(bl[0]) elif ((ind1>=0)and(ind2<0)): self.deg_shells[ish][ind1].append(bl2[0]) elif ((ind1<0)and(ind2<0)): self.deg_shells[ish].append([bl[0],bl2[0]]) if (mpi.is_master_node()): ar=HDFArchive(self.hdf_file,'a') ar[self.lda_data]['gf_struct_solver'] = self.gf_struct_solver ar[self.lda_data]['map'] = self.map ar[self.lda_data]['map_inv'] = self.map_inv try: ar[self.lda_data]['deg_shells'] = self.deg_shells except: mpi.report("deg_shells not stored, degeneracies not found") del ar return dens_mat def symm_deg_gf(self,gf_to_symm,orb): """Symmetrises a GF for the given degenerate shells self.deg_shells""" for degsh in self.deg_shells[orb]: #loop over degenerate shells: ss = gf_to_symm[degsh[0]].copy() ss.zero() Ndeg = len(degsh) for bl in degsh: ss += gf_to_symm[bl] / (1.0*Ndeg) for bl in degsh: gf_to_symm[bl] <<= ss def eff_atomic_levels(self): """Calculates the effective atomic levels needed as input for the Hubbard I Solver.""" # define matrices for inequivalent shells: eff_atlevels = [ {} for ish in range(self.n_inequiv_corr_shells) ] for ish in range(self.n_inequiv_corr_shells): for bn in self.block_names[self.corr_shells[self.invshellmap[ish]][4]]: eff_atlevels[ish][bn] = numpy.identity(self.corr_shells[self.invshellmap[ish]][3], numpy.complex_) # Chemical Potential: for ish in xrange(self.n_inequiv_corr_shells): for ii in eff_atlevels[ish]: eff_atlevels[ish][ii] *= -self.chemical_potential # double counting term: #if hasattr(self,"dc_imp"): for ish in xrange(self.n_inequiv_corr_shells): for ii in eff_atlevels[ish]: eff_atlevels[ish][ii] -= self.dc_imp[self.invshellmap[ish]][ii] # sum over k: if not hasattr(self,"Hsumk"): # calculate the sum over k. Does not depend on mu, so do it only once: self.Hsumk = [ {} for ish in range(self.n_corr_shells) ] for icrsh in range(self.n_corr_shells): for bn in self.block_names[self.corr_shells[icrsh][4]]: dim = self.corr_shells[icrsh][3] #*(1+self.corr_shells[icrsh][4]) self.Hsumk[icrsh][bn] = numpy.zeros([dim,dim],numpy.complex_) for icrsh in range(self.n_corr_shells): for ibn, bn in enumerate(self.block_names[self.corr_shells[icrsh][4]]): isp = self.names_to_ind[self.corr_shells[icrsh][4]][bn] for ik in xrange(self.n_k): MMat = numpy.identity(self.n_orbitals[ik][isp], numpy.complex_) MMat = self.hopping[ik][isp] - (1-2*ibn) * self.h_field * MMat self.Hsumk[icrsh][bn] += self.bz_weights[ik] * numpy.dot( numpy.dot(self.proj_mat[ik][isp][icrsh],MMat), #self.hopping[ik][isp]) , self.proj_mat[ik][isp][icrsh].conjugate().transpose() ) # symmetrisation: if (self.symm_op!=0): self.Hsumk = self.Symm_corr.symmetrize(self.Hsumk) # Rotate to local coordinate system: if (self.use_rotations): for icrsh in xrange(self.n_corr_shells): for bn in self.Hsumk[icrsh]: if (self.rot_mat_time_inv[icrsh]==1): self.Hsumk[icrsh][bn] = self.Hsumk[icrsh][bn].conjugate() #if (self.corr_shells[icrsh][4]==0): self.Hsumk[icrsh][bn] = self.Hsumk[icrsh][bn].conjugate() self.Hsumk[icrsh][bn] = numpy.dot( numpy.dot(self.rot_mat[icrsh].conjugate().transpose(),self.Hsumk[icrsh][bn]) , self.rot_mat[icrsh] ) # add to matrix: for ish in xrange(self.n_inequiv_corr_shells): for bn in eff_atlevels[ish]: eff_atlevels[ish][bn] += self.Hsumk[self.invshellmap[ish]][bn] return eff_atlevels def __init_dc(self): # construct the density matrix dm_imp and double counting arrays #self.dm_imp = [ {} for i in xrange(self.n_corr_shells)] self.dc_imp = [ {} for i in xrange(self.n_corr_shells)] for i in xrange(self.n_corr_shells): l = self.corr_shells[i][3] for j in xrange(len(self.gf_struct_corr[i])): self.dc_imp[i]['%s'%self.gf_struct_corr[i][j][0]] = numpy.zeros([l,l],numpy.float_) self.dc_energ = [0.0 for i in xrange(self.n_corr_shells)] def set_lichtenstein_dc(self,Sigma_imp): """Sets a double counting term according to Lichtenstein et al. PRL2001""" assert isinstance(Sigma_imp,list), "Sigma_imp has to be a list of impurity self energies for the correlated shells, even if it is of length 1!" assert len(Sigma_imp)==self.n_inequiv_corr_shells, "give exactly one Sigma for each inequivalent corr. shell!" for i in xrange(self.n_corr_shells): l = (self.corr_shells[i][4]+1) * self.corr_shells[i][3] for j in xrange(len(self.gf_struct_corr[i])): self.dc_imp[i]['%s'%self.gf_struct_corr[i][j][0]] = numpy.identity(l,numpy.float_) # transform the CTQMC blocks to the full matrix: for icrsh in xrange(self.n_corr_shells): s = self.shellmap[icrsh] # s is the index of the inequivalent shell corresponding to icrsh for ibl in range(len(self.gf_struct_solver[s])): for i in range(len(self.gf_struct_solver[s][ibl][1])): for j in range(len(self.gf_struct_solver[s][ibl][1])): bl = self.gf_struct_solver[s][ibl][0] ind1 = self.gf_struct_solver[s][ibl][1][i] ind2 = self.gf_struct_solver[s][ibl][1][j] self.dm_imp[icrsh][self.map_inv[s][bl]][ind1,ind2] = Sigma_imp[s][bl]._data.array[i,j,0].real # self energy at smallest matsubara, could be done better for icrsh in xrange(self.n_corr_shells): # trace: Sigtr = 0.0 a_list = [a for a,al in self.gf_struct_corr[icrsh]] for bl in a_list: Sigtr += self.dm_imp[icrsh][bl].trace() for bl in a_list: self.dc_imp[icrsh][bl] *= (Sigtr / (self.corr_shells[icrsh][3] * 2.0)) #self.dc_imp[icrsh]['up'][:,:] += self.dc_imp[icrsh]['down'][:,:] #self.dc_imp[icrsh]['up'][:,:] /= 2.0 #self.dc_imp[icrsh]['down'][:,:] = self.dc_imp[icrsh]['up'][:,:] def set_dc(self,dens_mat,U_interact,J_hund,orb=0,use_dc_formula=0,use_val=None): """Sets the double counting term for inequiv orbital orb use_dc_formula=0: LDA+U FLL double counting, use_dc_formula=1: Held's formula. use_dc_formula=2: AMF Be sure that you use the correct interaction Hamiltonian!""" #if (not hasattr(self,"dc_imp")): self.__init_dc() dm = [ {} for i in xrange(self.n_corr_shells)] for i in xrange(self.n_corr_shells): l = self.corr_shells[i][3] #*(1+self.corr_shells[i][4]) for j in xrange(len(self.gf_struct_corr[i])): dm[i]['%s'%self.gf_struct_corr[i][j][0]] = numpy.zeros([l,l],numpy.float_) for icrsh in xrange(self.n_corr_shells): iorb = self.shellmap[icrsh] # iorb is the index of the inequivalent shell corresponding to icrsh if (iorb==orb): # do this orbital l = self.corr_shells[icrsh][3] #*(1+self.corr_shells[icrsh][4]) for j in xrange(len(self.gf_struct_corr[icrsh])): self.dc_imp[icrsh]['%s'%self.gf_struct_corr[icrsh][j][0]] = numpy.identity(l,numpy.float_) # transform the CTQMC blocks to the full matrix: for ibl in range(len(self.gf_struct_solver[iorb])): for i in range(len(self.gf_struct_solver[iorb][ibl][1])): for j in range(len(self.gf_struct_solver[iorb][ibl][1])): bl = self.gf_struct_solver[iorb][ibl][0] ind1 = self.gf_struct_solver[iorb][ibl][1][i] ind2 = self.gf_struct_solver[iorb][ibl][1][j] dm[icrsh][self.map_inv[iorb][bl]][ind1,ind2] = dens_mat[bl][i,j].real # only real part relevant for trace M = self.corr_shells[icrsh][3] Ncr = {} Ncrtot = 0.0 a_list = [a for a,al in self.gf_struct_corr[icrsh]] for bl in a_list: Ncr[bl] = dm[icrsh][bl].trace() Ncrtot += Ncr[bl] # average the densities if there is no SP: if (self.SP==0): for bl in a_list: Ncr[bl] = Ncrtot / len(a_list) # correction for SO: we have only one block in this case, but in DC we need N/2 elif (self.SP==1 and self.SO==1): for bl in a_list: Ncr[bl] = Ncrtot / 2.0 if (use_val is None): if (use_dc_formula==0): self.dc_energ[icrsh] = U_interact / 2.0 * Ncrtot * (Ncrtot-1.0) for bl in a_list: Uav = U_interact*(Ncrtot-0.5) - J_hund*(Ncr[bl] - 0.5) self.dc_imp[icrsh][bl] *= Uav self.dc_energ[icrsh] -= J_hund / 2.0 * (Ncr[bl]) * (Ncr[bl]-1.0) mpi.report("DC for shell %(icrsh)i and block %(bl)s = %(Uav)f"%locals()) elif (use_dc_formula==1): self.dc_energ[icrsh] = (U_interact + J_hund * (2.0-(M-1)) / (2*M-1) ) / 2.0 * Ncrtot * (Ncrtot-1.0) for bl in a_list: # Held's formula, with U_interact the interorbital onsite interaction Uav = (U_interact + J_hund * (2.0-(M-1)) / (2*M-1) ) * (Ncrtot-0.5) self.dc_imp[icrsh][bl] *= Uav mpi.report("DC for shell %(icrsh)i and block %(bl)s = %(Uav)f"%locals()) elif (use_dc_formula==2): self.dc_energ[icrsh] = 0.5 * U_interact * Ncrtot * Ncrtot for bl in a_list: # AMF Uav = U_interact*(Ncrtot - Ncr[bl]/M) - J_hund * (Ncr[bl] - Ncr[bl]/M) self.dc_imp[icrsh][bl] *= Uav self.dc_energ[icrsh] -= (U_interact + (M-1)*J_hund)/M * 0.5 * Ncr[bl] * Ncr[bl] mpi.report("DC for shell %(icrsh)i and block %(bl)s = %(Uav)f"%locals()) # output: mpi.report("DC energy for shell %s = %s"%(icrsh,self.dc_energ[icrsh])) else: a_list = [a for a,al in self.gf_struct_corr[icrsh]] for bl in a_list: self.dc_imp[icrsh][bl] *= use_val self.dc_energ[icrsh] = use_val * Ncrtot # output: mpi.report("DC for shell %(icrsh)i = %(use_val)f"%locals()) mpi.report("DC energy = %s"%self.dc_energ[icrsh]) def find_dc(self,orb,guess,dens_mat,dens_req=None,precision=0.01): """searches for DC in order to fulfill charge neutrality. If dens_req is given, then DC is set such that the LOCAL charge of orbital orb coincides with dens_req.""" mu = self.chemical_potential def F(dc): self.set_dc(dens_mat=dens_mat,U_interact=0,J_hund=0,orb=orb,use_val=dc) if (dens_req is None): return self.total_density(mu=mu) else: return self.extract_G_loc()[orb].total_density() if (dens_req is None): Dens_rel = self.density_required - self.charge_below else: Dens_rel = dens_req dcnew = dichotomy.dichotomy(function = F, x_init = guess, y_value = Dens_rel, precision_on_y = precision, delta_x=0.5, max_loops = 100, x_name="Double-Counting", y_name= "Total Density", verbosity = 3)[0] return dcnew def put_Sigma(self, Sigma_imp): """Puts the impurity self energies for inequivalent atoms into the class, respects the multiplicity of the atoms.""" assert isinstance(Sigma_imp,list), "Sigma_imp has to be a list of Sigmas for the correlated shells, even if it is of length 1!" assert len(Sigma_imp)==self.n_inequiv_corr_shells, "give exactly one Sigma for each inequivalent corr. shell!" # init self.Sigma_imp: if (Sigma_imp[0].note=='ReFreq'): # Real frequency Sigma: self.Sigma_imp = [ BlockGf( name_block_generator = [ (a,GfReFreq(indices = al, mesh = Sigma_imp[0].mesh)) for a,al in self.gf_struct_corr[i] ], make_copies = False) for i in xrange(self.n_corr_shells) ] self.Sigma_imp[0].note = 'ReFreq' else: # Imaginary frequency Sigma: self.Sigma_imp = [ BlockGf( name_block_generator = [ (a,GfImFreq(indices = al, mesh = Sigma_imp[0].mesh)) for a,al in self.gf_struct_corr[i] ], make_copies = False) for i in xrange(self.n_corr_shells) ] # transform the CTQMC blocks to the full matrix: for icrsh in xrange(self.n_corr_shells): s = self.shellmap[icrsh] # s is the index of the inequivalent shell corresponding to icrsh for ibl in range(len(self.gf_struct_solver[s])): for i in range(len(self.gf_struct_solver[s][ibl][1])): for j in range(len(self.gf_struct_solver[s][ibl][1])): bl = self.gf_struct_solver[s][ibl][0] ind1 = self.gf_struct_solver[s][ibl][1][i] ind2 = self.gf_struct_solver[s][ibl][1][j] self.Sigma_imp[icrsh][self.map_inv[s][bl]][ind1,ind2] <<= Sigma_imp[s][bl][ind1,ind2] # rotation from local to global coordinate system: if (self.use_rotations): for icrsh in xrange(self.n_corr_shells): for sig,gf in self.Sigma_imp[icrsh]: self.Sigma_imp[icrsh][sig] <<= self.rotloc(icrsh,gf,direction='toGlobal') def add_dc(self): """Substracts the double counting term from the impurity self energy.""" # Be careful: Sigma_imp is already in the global coordinate system!! sres = [s.copy() for s in self.Sigma_imp] for icrsh in xrange(self.n_corr_shells): for bl,gf in sres[icrsh]: dccont = numpy.dot(self.rot_mat[icrsh],numpy.dot(self.dc_imp[icrsh][bl],self.rot_mat[icrsh].conjugate().transpose())) sres[icrsh][bl] -= dccont return sres def set_mu(self,mu): """Sets a new chemical potential""" self.chemical_potential = mu #print "Chemical potential in SumK set to ",mu def sorts_of_atoms(self,lst): """ This routine should determine the number of sorts in the double list lst """ sortlst = [ lst[i][1] for i in xrange(len(lst)) ] sortlst.sort() sorts = 1 for i in xrange(len(sortlst)-1): if sortlst[i+1]>sortlst[i]: sorts += 1 return sorts def number_of_atoms(self,lst): """ This routine should determine the number of atoms in the double list lst """ atomlst = [ lst[i][0] for i in xrange(len(lst)) ] atomlst.sort() atoms = 1 for i in xrange(len(atomlst)-1): if atomlst[i+1]>atomlst[i]: atoms += 1 return atoms def inequiv_shells(self,lst): """ The number of inequivalent shells is calculated from lst, and a mapping is given as map(i_corr_shells) = i_inequiv_corr_shells invmap(i_inequiv_corr_shells) = i_corr_shells in order to put the Self energies to all equivalent shells, and for extracting Gloc """ tmp = [] self.shellmap = [0 for i in range(len(lst))] self.invshellmap = [0] self.n_inequiv_corr_shells = 1 tmp.append( lst[0][1:3] ) if (len(lst)>1): for i in range(len(lst)-1): fnd = False for j in range(self.n_inequiv_corr_shells): if (tmp[j]==lst[i+1][1:3]): fnd = True self.shellmap[i+1] = j if (fnd==False): self.shellmap[i+1] = self.n_inequiv_corr_shells self.n_inequiv_corr_shells += 1 tmp.append( lst[i+1][1:3] ) self.invshellmap.append(i+1) def total_density(self, mu): """ Calculates the total charge for the energy window for a given mu. Since in general n_orbitals depends on k, the calculation is done in the following order: G_aa'(k,iw) -> n(k) = Tr G_aa'(k,iw) -> sum_k n_k mu: chemical potential The calculation is done in the global coordinate system, if distinction is made between local/global! """ dens = 0.0 ikarray=numpy.array(range(self.n_k)) for ik in mpi.slice_array(ikarray): S = self.lattice_gf_matsubara(ik=ik,mu=mu) dens += self.bz_weights[ik] * S.total_density() # collect data from mpi: dens = mpi.all_reduce(mpi.world,dens,lambda x,y : x+y) mpi.barrier() return dens def find_mu(self, precision = 0.01): """Searches for mu in order to give the desired charge A desired precision can be specified in precision.""" F = lambda mu : self.total_density(mu = mu) Dens_rel = self.density_required - self.charge_below self.chemical_potential = dichotomy.dichotomy(function = F, x_init = self.chemical_potential, y_value = Dens_rel, precision_on_y = precision, delta_x=0.5, max_loops = 100, x_name="chemical_potential", y_name= "Total Density", verbosity = 3)[0] return self.chemical_potential def find_mu_nonint(self, dens_req, orb = None, beta = 40, precision = 0.01): def F(mu): #gnonint = self.nonint_G(beta=beta,mu=mu) gnonint = self.extract_G_loc(mu=mu,with_Sigma=False) if (orb is None): dens = 0.0 for ish in range(self.n_inequiv_corr_shells): dens += gnonint[ish].total_density() else: dens = gnonint[orb].total_density() return dens self.chemical_potential = dichotomy.dichotomy(function = F, x_init = self.chemical_potential, y_value = dens_req, precision_on_y = precision, delta_x=0.5, max_loops = 100, x_name="chemical_potential", y_name= "Local Density", verbosity = 3)[0] return self.chemical_potential def extract_G_loc(self, mu=None, with_Sigma = True): """ extracts the local downfolded Green function at the chemical potential of the class. At the end, the local G is rotated from the gloabl coordinate system to the local system. if with_Sigma = False: Sigma is not included => non-interacting local GF """ if (mu is None): mu = self.chemical_potential Gloc = [ self.Sigma_imp[icrsh].copy() for icrsh in xrange(self.n_corr_shells) ] # this list will be returned for icrsh in xrange(self.n_corr_shells): Gloc[icrsh].zero() # initialize to zero ikarray=numpy.array(range(self.n_k)) for ik in mpi.slice_array(ikarray): S = self.lattice_gf_matsubara(ik=ik,mu=mu,with_Sigma = with_Sigma) S *= self.bz_weights[ik] for icrsh in xrange(self.n_corr_shells): tmp = Gloc[icrsh].copy() # init temporary storage for sig,gf in tmp: tmp[sig] <<= self.downfold(ik,icrsh,sig,S[sig],gf) Gloc[icrsh] += tmp #collect data from mpi: for icrsh in xrange(self.n_corr_shells): Gloc[icrsh] <<= mpi.all_reduce(mpi.world,Gloc[icrsh],lambda x,y : x+y) mpi.barrier() # Gloc[:] is now the sum over k projected to the local orbitals. # here comes the symmetrisation, if needed: if (self.symm_op!=0): Gloc = self.Symm_corr.symmetrize(Gloc) # Gloc is rotated to the local coordinate system: if (self.use_rotations): for icrsh in xrange(self.n_corr_shells): for sig,gf in Gloc[icrsh]: Gloc[icrsh][sig] <<= self.rotloc(icrsh,gf,direction='toLocal') # transform to CTQMC blocks: Glocret = [ BlockGf( name_block_generator = [ (a,GfImFreq(indices = al, mesh = Gloc[0].mesh)) for a,al in self.gf_struct_solver[i] ], make_copies = False) for i in xrange(self.n_inequiv_corr_shells) ] for ish in xrange(self.n_inequiv_corr_shells): for ibl in range(len(self.gf_struct_solver[ish])): for i in range(len(self.gf_struct_solver[ish][ibl][1])): for j in range(len(self.gf_struct_solver[ish][ibl][1])): bl = self.gf_struct_solver[ish][ibl][0] ind1 = self.gf_struct_solver[ish][ibl][1][i] ind2 = self.gf_struct_solver[ish][ibl][1][j] Glocret[ish][bl][ind1,ind2] <<= Gloc[self.invshellmap[ish]][self.map_inv[ish][bl]][ind1,ind2] # return only the inequivalent shells: return Glocret def calc_density_correction(self,filename = 'dens_mat.dat'): """ Calculates the density correction in order to feed it back to the DFT calculations.""" assert (type(filename)==StringType), "filename has to be a string!" ntoi = self.names_to_ind[self.SO] bln = self.block_names[self.SO] # Set up deltaN: deltaN = {} for ib in bln: deltaN[ib] = [ numpy.zeros( [self.n_orbitals[ik][ntoi[ib]],self.n_orbitals[ik][ntoi[ib]]], numpy.complex_) for ik in range(self.n_k)] ikarray=numpy.array(range(self.n_k)) dens = {} for ib in bln: dens[ib] = 0.0 for ik in mpi.slice_array(ikarray): S = self.lattice_gf_matsubara(ik=ik,mu=self.chemical_potential) for sig,g in S: deltaN[sig][ik] = S[sig].density() dens[sig] += self.bz_weights[ik] * S[sig].total_density() #put mpi Barrier: for sig in deltaN: for ik in range(self.n_k): deltaN[sig][ik] = mpi.all_reduce(mpi.world,deltaN[sig][ik],lambda x,y : x+y) dens[sig] = mpi.all_reduce(mpi.world,dens[sig],lambda x,y : x+y) mpi.barrier() # now save to file: if (mpi.is_master_node()): if (self.SP==0): f=open(filename,'w') else: f=open(filename+'up','w') f1=open(filename+'dn','w') # write chemical potential (in Rydberg): f.write("%.14f\n"%(self.chemical_potential/self.energy_unit)) if (self.SP!=0): f1.write("%.14f\n"%(self.chemical_potential/self.energy_unit)) # write beta in ryderg-1 f.write("%.14f\n"%(S.beta*self.energy_unit)) if (self.SP!=0): f1.write("%.14f\n"%(S.beta*self.energy_unit)) if (self.SP==0): for ik in range(self.n_k): f.write("%s\n"%self.n_orbitals[ik][0]) for inu in range(self.n_orbitals[ik][0]): for imu in range(self.n_orbitals[ik][0]): valre = (deltaN['up'][ik][inu,imu].real + deltaN['down'][ik][inu,imu].real) / 2.0 valim = (deltaN['up'][ik][inu,imu].imag + deltaN['down'][ik][inu,imu].imag) / 2.0 f.write("%.14f %.14f "%(valre,valim)) f.write("\n") f.write("\n") f.close() elif ((self.SP==1)and(self.SO==0)): for ik in range(self.n_k): f.write("%s\n"%self.n_orbitals[ik][0]) for inu in range(self.n_orbitals[ik][0]): for imu in range(self.n_orbitals[ik][0]): f.write("%.14f %.14f "%(deltaN['up'][ik][inu,imu].real,deltaN['up'][ik][inu,imu].imag)) f.write("\n") f.write("\n") f.close() for ik in range(self.n_k): f1.write("%s\n"%self.n_orbitals[ik][1]) for inu in range(self.n_orbitals[ik][1]): for imu in range(self.n_orbitals[ik][1]): f1.write("%.14f %.14f "%(deltaN['down'][ik][inu,imu].real,deltaN['down'][ik][inu,imu].imag)) f1.write("\n") f1.write("\n") f1.close() else: for ik in range(self.n_k): f.write("%s\n"%self.n_orbitals[ik][0]) for inu in range(self.n_orbitals[ik][0]): for imu in range(self.n_orbitals[ik][0]): f.write("%.14f %.14f "%(deltaN['ud'][ik][inu,imu].real,deltaN['ud'][ik][inu,imu].imag)) f.write("\n") f.write("\n") f.close() for ik in range(self.n_k): f1.write("%s\n"%self.n_orbitals[ik][0]) for inu in range(self.n_orbitals[ik][0]): for imu in range(self.n_orbitals[ik][0]): f1.write("%.14f %.14f "%(deltaN['ud'][ik][inu,imu].real,deltaN['ud'][ik][inu,imu].imag)) f1.write("\n") f1.write("\n") f1.close() return deltaN, dens