def __init__(self, beta, w, dos): try: nw, norbitals, nspins = dos.shape except ValueError: raise ValueError("dos must be a nw, norbital, nspins array") if w.shape != (nw, ): raise ValueError("w and dos are not consistent in shape") Lattice.__init__(self, beta, norbitals, nspins) integrate = lambda fw: scipy.integrate.trapz(fw, w) cum_int = lambda fw: scipy.integrate.cumtrapz(fw, w, initial=0) if (dos < 0).any() or dos.imag.any(): warn("dos is not a positive real function.", UserWarning, 2) dos_w_last = dos.transpose(1, 2, 0) norm = integrate(dos_w_last) if not np.allclose(norm, 1): warn("dos seems not to be properly normalised.\n Norms: %s" % norm, UserWarning, 2) self.w = w self.dos = dos self.int_dos = cum_int(dos_w_last).transpose(2, 0, 1) self.hloc = integrate(dos_w_last * w) self.hmom2 = integrate(dos_w_last * w**2) self.hloc = orbspin.promote_diagonal(self.hloc) self.hmom2 = orbspin.promote_diagonal(self.hmom2)
def __init__(self, beta, half_bw, crystalfield=None): # crystalfield marks the centers of the DOS (hmean) if crystalfield is None: crystalfield = np.zeros_like(half_bw) self.crystalfield = np.asarray(crystalfield) self.d = np.asarray(half_bw) norbitals, nspins = self.d.shape Lattice.__init__(self, beta, norbitals, nspins) # add dummy iw dimension to satisfy requirements for promote_diagonal tstar2 = self.d**2 / 4. self.hloc = orbspin.promote_diagonal(self.crystalfield) self.hmom2 = orbspin.promote_diagonal(tstar2) + self.hloc**2
def load_old(self, oldfile, olditer=-1): hf = hdf5.File(oldfile, "r") file_version = tuple(hf.attrs["outfile-version"]) if olditer == -1: oldit = hf["dmft-last"] else: oldit = hf["dmft-%03d" % olditer] ineqs = [ node for name, node in sorted(oldit.iteritems()) if name.startswith("ineq-") ] if not ineqs: raise ValueError("No impurity results found in iteration") mu = oldit["mu/value"].value smom_dd = None dc_latt = None if file_version >= (2, 1): siw_dd = [ ineq["siw-full/value"].value.transpose(4, 0, 1, 2, 3) for ineq in ineqs ] smom_dd = [ ineq["smom-full/value"].value.transpose(4, 0, 1, 2, 3) for ineq in ineqs ] try: dc_latt = oldit["dc-latt/value"].value except KeyError: pass elif file_version >= (2, 0): # FIXME: double-counting for old runs siw_dd = [ orbspin.promote_diagonal(ineq["siw/value"].value.transpose( 2, 0, 1)) for ineq in ineqs ] else: # FIXME: double-counting for very old runs siw_dd = [ orbspin.promote_diagonal(siw_ineq.transpose(2, 0, 1)) for siw_ineq in oldit["siw/value"].value ] hf.close() return mu, siw_dd, smom_dd, dc_latt
def doublecounting_from_cfg(cfg, ineq_list, mylattice, atom_list, u_full): dc_type = cfg["General"]["dc"] # No double counting needed (d-only and max. 1 ineq atom and not # user-enforced) if len(ineq_list) == 1 and not np.any([atom.np for atom in atom_list]) \ and not isinstance(dc_type, list): return doublecounting.Zero(mylattice.norbitals, mylattice.nspins) lda_dens = mylattice.densities if isinstance(dc_type, list): # user-supplied DC dc_user_values = np.array(dc_type, float) if dc_user_values.size != mylattice.norbitals: raise ValueError("Expected %d elements" % mylattice.norbitals) dc_user_values = np.repeat(dc_user_values[:,None], mylattice.nspins, 1) dc_user_values = orbspin.promote_diagonal(dc_user_values) return doublecounting.UserSupplied(dc_user_values) elif dc_type == 'fll' or dc_type == 'anisimov': return doublecounting.FullyLocalisedLimit(lda_dens, atom_list, u_full) elif dc_type == 'amf': return doublecounting.AroundMeanField(lda_dens, atom_list, u_full) elif dc_type == 'siginfbar': return doublecounting.SigInfBar(ineq_list, lda_dens, atom_list, u_full) elif dc_type == 'sigzerobar': return doublecounting.SigZeroBar(ineq_list, lda_dens, atom_list, u_full) elif dc_type == 'trace': return doublecounting.Trace(ineq_list, lda_dens, atom_list, u_full) else: raise NotImplemented("Unknown double counting scheme")
def from_shell(self, dc_shell): dc_diag = np.zeros((self.norbitals, self.nspins)) for isl, sl in enumerate(self.slices): dc_diag[sl] = dc_shell[isl, np.newaxis] # note the minus sign! self.dc_shell = dc_shell self.dc_value = -orbspin.promote_diagonal(dc_diag)
def gloc(self, iw, mu, siw, siw_gw=None): ziw = (1j * iw + mu)[:, None, None, None, None] * self.eye - siw orbspin.warn_offdiagonal(siw) zeta = orbspin.extract_diagonal(ziw - self.hloc) d2 = self.d[np.newaxis]**2 glociw = 2 * zeta / d2 * (1 - np.sqrt(1 - d2 / zeta**2)) glociw = orbspin.promote_diagonal(glociw) return glociw
def get(self, siws=None, smoms=None, giws=None, occs=None): if giws is None: # initializing with FLL double counting... better than 0 dc_fll = FullyLocalisedLimit(self.shell_av['densities'], self.shell_av['atom_list'], self.shell_av['u_full']) self.dc_value = dc_fll.get() return self.dc_value for ineq, giw, occ, occ0 in zip(self.ineq_list, giws, occs, self.d_dens_sum): # DOS as "approximated" by first Matsubara frequency iw0 = giw.shape[0] // 2 dos_fermi_level = -1 / np.pi * orbspin.trace(giw[iw0].imag) # impurity density from double occupations. # TODO: this is tied to diagonal hybr dens_impurity = orbspin.promote_diagonal( orbspin.extract_diagonal(occ)) dens_impurity = np.array(dens_impurity).astype(np.float) dens_impurity = np.sum(dens_impurity) # we do not use the non interacting density from Weiss field # but that from the DFT Hamiltonian. # that is a lot more stable # impurity non-interacting density (from Weiss field) #iw = tf.matfreq(beta, 'fermi', imp_result.problem.niwf) #eye = np.eye(imp_result.problem.norbitals * imp_result.problem.nspins) \ # .reshape(imp_result.problem.norbitals, imp_result.problem.nspins, # imp_result.problem.norbitals, imp_result.problem.nspins) #g0iw = orbspin.invert(imp_result.problem.g0iwinv) #g0iw_model = orbspin.invert(1j * iw * eye + imp_result.problem.muimp) #dens_model = lattice.fermi(-imp_result.problem.muimp) # FIXME offdiag #dens0_impurity = (g0iw - g0iw_model).real.sum(0) / imp_result.problem.beta \ # + dens_model if ineq.occ_dc_number is not None: dens0_impurity = ineq.occ_dc_number else: dens0_impurity = occ0 # If the DOS at the Fermi level is large, we do not want to adjust # too much for stability reasons (fudging) if dos_fermi_level > 1.: delta_dc = (dens0_impurity - dens_impurity) / dos_fermi_level else: delta_dc = dens0_impurity - dens_impurity # Fudging factor delta_dc *= 0.2 delta_dc = delta_dc * np.eye(ineq.nd * self.nspins).reshape( ineq.nd, self.nspins, ineq.nd, self.nspins) ineq.d_setpart(self.dc_value, ineq.d_downfold(self.dc_value) - delta_dc) return self.dc_value
def gloc(self, iw, mu, siw, siw_gw=None): orbspin.warn_offdiagonal(siw) siw = orbspin.extract_diagonal(siw) zeta = (1j * iw + mu)[:, None, None] - siw # compute integral de N(e)/(zeta - e) glociw = self.integrator( self.dos.transpose(1, 2, 0)[None, :, :, :] / (zeta[:, :, :, None] - self.w[None, None, None, :])) glociw = orbspin.promote_diagonal(glociw) return glociw
def lattice_convention(qtty): return orbspin.promote_diagonal(qtty.transpose(2, 0, 1))
def set_problem(self, problem, compute_fourpoint=0): # problem self.problem = problem problem_params = self.config_to_pstring({ "Hamiltonian": problem.interaction.name, "beta": problem.beta, "Nd": problem.norbitals, "ParaMag": int(problem.paramag), "QuantumNumbers": " ".join(problem.interaction.quantum_numbers), "Phonon": int(problem.use_phonons), "g_phonon": " ".join(problem.phonon_g), "omega0": " ".join(problem.phonon_omega0), "Screening": int(problem.use_screening), "Uw": self.Uw, }) symm_params = self.symm_to_pstring(problem.symmetry_moves) all_params = self.param_string + problem_params + symm_params ctqmc.init_paras(all_params) ctqmc.fourpnt = compute_fourpoint # prepare parameters if self.config["QMC"]["offdiag"] == 0: self.ftau = orbspin.promote_diagonal( orbspin.extract_diagonal(self.problem.ftau)) self.ftau = self.problem.ftau.transpose(1, 2, 3, 4, 0) # CT-HYB uses a different convention for muimp #self.muimp = -self.prepare_input(self.problem.muimp) self.muimp = -self.problem.muimp self.umatrix = self.problem.interaction.u_matrix.reshape( self.problem.nflavours, self.problem.nflavours, self.problem.nflavours, self.problem.nflavours) if self.Uw == 0: self.screening = self.problem.screening.transpose(1, 2, 3, 4, 0).real # START DYNAMICAL U if self.Uw == 1: #print " " #print " ****************************" #print " ****** U(w) is active ******" #print " ****************************" ### ===> UPDATE This is now done in the init above. ### ===> UPDATE _Retarded2Shifts = dynamicalU.Replace_Retarded_Interaction_by_a_Shift_of_instantaneous_Potentials(problem.beta, self.umatrix, self.Uw_Mat) ### ===> UPDATE The umatrix shifting is now performed in config.py. ### ===> UPDATE The umatrix shifting is now performed in config.py. ### ===> # This should be executed for all atoms in the first iteration ### ===> try: ### ===> self.iteration_ID # Not defined for first DMFT iteration ### ===> except: ### ===> self.umatrix_original = copy.copy(self.umatrix) ### ===> self.iteration_ID = 1 ### ===> if (self.umatrix==self.umatrix_original).all(): ### ===> self.umatrix = _Retarded2Shifts.shift_instantaneous_densitydensity_potentials(self.umatrix) ### ===> UPDATE The umatrix shifting is now performed in config.py. ### ===> UPDATE The umatrix shifting is now performed in config.py. ### ===> UPDATE This is now done in the init above. ### ===> UPDATE self.screening = _Retarded2Shifts.create_tau_dependent_UV_screening_function(problem.nftau, problem.beta, self.screening) # Perform chemical potential shift in each DMFT iteration if problem.norbitals == 1 and self.epsn == 0: self.muimp = self._Retarded2Shifts.shift_chemical_potential_for_single_band_case( self.muimp, self.umatrix)
def postprocessing(self, siw_method="improved", smom_method="estimate"): # This function is disjoint from the initialisation, because it is a # "derived" quantity that should be computed after averaging over bins fallback = False if siw_method == "dyson": if self.g_diagonal_only: # If the solver is only capable of supplying the spin/orbital- # diagonal terms of the Green's function, we need to make sure # that also only the diagonal terms of G_0 are taken into # account in the Dyson equation, otherwise Sigma erroneously # "counteracts" these terms. As an added performance benefit, # we can use 1/giw rather than the inversion in this case. self.siw = orbspin.promote_diagonal( orbspin.extract_diagonal(self.problem.g0inviw) - 1 / orbspin.extract_diagonal(self.giw)) else: self.siw = self.problem.g0inviw - orbspin.invert(self.giw) elif siw_method == "improved": if self.gsigmaiw is None: warn( "Cannot compute improved estimators - GSigmaiw missing\n" "Falling back to Dyson equation", UserWarning, 2) siw_method = "dyson" fallback = True raise NotImplementedError() # FIXME elif siw_method == 'improved_worm': if self.gsigmaiw is None: warn( "Cannot compute improved estimators - GSigmaiw missing\n" "Falling back to Dyson equation", UserWarning, 2) siw_method = "dyson" fallback = True if self.g_diagonal_only: #TODO: check gsigma sign self.siw = -orbspin.promote_diagonal( orbspin.extract_diagonal(self.gsigmaiw) / orbspin.extract_diagonal(self.giw)) else: raise NotImplementedError("Offdiagonal worm improved \n" "estimators not implemented") else: raise ValueError("unknown siw_method: %s" % siw_method) if smom_method == "extract": warn("Extracting moment of self-energy from the data", UserWarning, 2) self.smom = self.siw[:1].real.copy() elif smom_method == "estimate": if self.smom is None: warn( "Cannot compute smom estimate - rho1 and rho2 missing\n" "Falling back to extraction", UserWarning, 2) smom_method = "extract" fallback = True else: raise ValueError("unknown smom_method: %s" % smom_method) if fallback: self.postprocessing(siw_method, smom_method)
def set_siws(self, siw_dd=None, smom_dd=None, dc_full=None, init=False, hartree_start=False, giws=None, occs=None): new_run = siw_dd is None and init nspins = self.lattice.nspins norbitals = self.lattice.norbitals if siw_dd is None: siw_dd = [] smom_dd = [] for ineq in self.ineq_list: siw_block = np.zeros( (self.niwf, ineq.nd, nspins, ineq.nd, nspins), np.complex) if not self.paramag: siw_block[:, :, 0, :, 0] += .5 * ineq.se_shift siw_block[:, :, 1, :, 1] -= .5 * ineq.se_shift siw_dd.append(siw_block) smom_dd.append(siw_block[:1].real) if smom_dd is None: warn("extracting moments from Sigma. Better use siw_mom", UserWarning, 2) smom_dd = [siw_block[:1].real for siw_block in siw_dd] # Enforce paramagnetic option if self.paramag: siw_dd = [orbspin.symm_spins(siw_block) for siw_block in siw_dd] smom_dd = [ orbspin.symm_spins(smom_block) for smom_block in smom_dd ] # get double-counting if we do not force it to a value. # Note that the double-counting is essentially a correction to the # Hamiltonian and therefore present from the start. # fix for self consitent dc: If self.dc_full is present # (from last iteration or from init), then do not call the # standard dc call here but do it later # this implies that set_siws() can initialize dc with dc_full # exaclty once, which makes sense try: self.dc_full except AttributeError: if dc_full is None: self.dc_full = self.dc.get() else: self.dc_full = dc_full # We also have to set the double counting inside the # dc class so that trace double counting works self.dc.set(dc_full) if self.use_gw == 1: self.dc_full = self.dc_full * 0 # Try to mimic the behaviour of the old code if new_run and hartree_start: self.sigma_hartree = -self.dc_full for ineq_no, ineq in enumerate(self.ineq_list): siw_dd[ineq_no][:] += ineq.d_downfold(self.sigma_hartree) ineq.d_setpart(self.sigma_hartree, 0) # Perform mixing of self-energy and double-counting with the same # strategy: this is important as to keep them "consistent", i.e., # double-count correlations at the same rate as switching them on. if init: # First iteration: initialise mixers self.dc_mixer = self.siw_mix_strategy.mixer(dc_full) self.siw_mixer = [ self.siw_mix_strategy.mixer(siw_block) for siw_block in siw_dd ] self.smom_mixer = [ self.siw_mix_strategy.mixer(smom_block) for smom_block in smom_dd ] self.siw_dd = siw_dd self.smom_dd = smom_dd else: # In-the-middle iteration: perform mixing self.dc_full = self.dc_mixer(self.dc_full) self.siw_dd = [ mixer(siw_block) for mixer, siw_block in zip(self.siw_mixer, siw_dd) ] self.smom_dd = [ mixer(smom_block) for mixer, smom_block in zip(self.smom_mixer, smom_dd) ] # Fix the distance between selected d-orbitals and the p-manifold to the original distance of the LDA/GW Hamiltonian if self.dc_dp == 1: self.dc_full = self.dc_full * 0 dp_dc = doublecounting.Fixed_dp_Distance() self.dc_full = -dp_dc.get(self.dc_full, self.siw_dd, self.smom_dd, self.iwf, self.natoms, self.dc_dp_orbitals) # not doing mixing for self consistent dc # if we do want to mix... simply move this part above the mixing routine # if dc_full is not None, it comes from a restarted run. We do not want to # overwrite the restart value here! if self.dc.self_cons and (dc_full is None): # this currently should only work for trace dc # todo: fix siginfbar self.dc_full = self.dc.get(siws=siw_dd, smoms=smom_dd, giws=giws, occs=occs) # Upfold self-energy. self.siw_full = 0 self.siw_moments = 0 for ineq, siw_bl, siw_bl_mom in zip(self.ineq_list, self.siw_dd, self.smom_dd): # Here, we potentially encounter a memory problem (a set of # nflavours x nflavours x nfrequencies) object may very well # overflow, so we split the upfolded self-energy in frequency # "chunks" self.siw_full += ineq.d_upfold(siw_bl[self.my_slice]) self.siw_moments += ineq.d_upfold(siw_bl_mom) # Add double-counting to the self-energy self.siw_full += self.dc_full self.siw_moments[0] += self.dc_full # This is an approximation: really, the partial densities and Hartree # self-energy an inter-dependent system. Here, we assume that the # partial densities won't change much adding a static self-energy # shift... if not (hartree_start and new_run): if self.use_hartree: #self.siw2gloc() # computes densities as well hartree = np.einsum("isjt,jt->is", self.udp_full + self.upp_full, self.densities) self.sigma_hartree = orbspin.promote_diagonal(hartree) else: self.sigma_hartree = np.zeros_like(self.siw_full[0]) self.siw_full += self.sigma_hartree self.siw_moments[0] += self.sigma_hartree.real # Invalidate lattice quantities (call to siw2gloc necessary) self.glociw = None self.densities = None self.densmatrix = None # Only if the DMFT Self-Energy is non-zero, we add the GW Self-Energy if (self.use_gw == 1) and (np.sum(self.siw_full) != 0): # siw_full is d+p siw_dd is d-only self.siw_gw, self.smom_gw = self.gw.get_GW_Sigma( self.beta, self.my_iwf) self.siw_gw = self.siw_gw.transpose(1, 0, 2, 3, 4, 5)