def run(self, mix=0.4, maxiter=117, dnmax=1e-9): if self.channels is None: self.initialize() dn = self.Z pb = ProgressBar(log(dnmax / dn), 0, 53, self.fd) self.log() for iter in range(maxiter): if iter > 1: self.vr_sg *= mix self.vr_sg += (1 - mix) * vr_old_sg dn = self.gd.integrate(abs(self.n_sg - n_old_sg).sum(0)) pb(log(dnmax / dn)) if dn <= dnmax: break vr_old_sg = self.vr_sg n_old_sg = self.n_sg self.step() self.summary() if dn > dnmax: raise RuntimeError('Did not converge!')
def _calculate(self, pd, chi0_wGG, chi0_wxvG, chi0_wvv, Q_aGii, m1, m2, spins): wfs = self.calc.wfs if self.keep_occupied_states: self.mykpts = [ self.get_k_point(s, K, n1, n2) for s, K, n1, n2 in self.mysKn1n2 ] if self.eta == 0.0: update = self.update_hermitian elif self.hilbert: update = self.update_hilbert else: update = self.update q_c = pd.kd.bzk_kc[0] optical_limit = not self.no_optical_limit and np.allclose(q_c, 0.0) pb = ProgressBar(self.fd) self.timer.start('Loop') # kpt1 occupied and kpt2 empty: for kn, (s, K, n1, n2) in enumerate(self.mysKn1n2): pb.update(kn / len(self.mysKn1n2)) if self.keep_occupied_states: kpt1 = self.mykpts[kn] else: kpt1 = self.get_k_point(s, K, n1, n2) if kpt1.s not in spins: continue with self.timer('k+q'): K2 = wfs.kd.find_k_plus_q(q_c, [kpt1.K])[0] with self.timer('get k2'): kpt2 = self.get_k_point(kpt1.s, K2, m1, m2, block=True) with self.timer('fft-indices'): Q_G = self.get_fft_indices(kpt1.K, kpt2.K, q_c, pd, kpt1.shift_c - kpt2.shift_c) for n in range(kpt1.n2 - kpt1.n1): eps1 = kpt1.eps_n[n] # Only update if there exists deps <= omegamax if self.omegamax is not None: m = [ m for m, d in enumerate(eps1 - kpt2.eps_n) if abs(d) <= self.omegamax ] else: m = range(0, kpt2.n2 - kpt2.n1) if not len(m): continue deps_m = (eps1 - kpt2.eps_n)[m] f1 = kpt1.f_n[n] with self.timer('conj'): ut1cc_R = kpt1.ut_nR[n].conj() with self.timer('paw'): C1_aGi = [ np.dot(Q_Gii, P1_ni[n].conj()) for Q_Gii, P1_ni in zip(Q_aGii, kpt1.P_ani) ] n_mG = self.calculate_pair_densities(ut1cc_R, C1_aGi, kpt2, pd, Q_G)[m] df_m = (f1 - kpt2.f_n)[m] # This is not quite right for degenerate partially occupied # bands, but good enough for now: df_m[df_m <= 1e-20] = 0.0 if optical_limit: self.update_optical_limit(n, m, kpt1, kpt2, deps_m, df_m, n_mG, chi0_wxvG, chi0_wvv) update(n_mG, deps_m, df_m, chi0_wGG) if optical_limit and self.intraband: # Avoid that more ranks are summing up # the intraband contributions if kpt1.n1 == 0 and self.blockcomm.rank == 0: assert self.nocc2 <= kpt2.nb, \ print('Error: Too few unoccupied bands') self.update_intraband(kpt2) self.timer.stop('Loop') pb.finish() with self.timer('Sum CHI_0'): for chi0_GG in chi0_wGG: self.kncomm.sum(chi0_GG) if optical_limit: self.kncomm.sum(chi0_wxvG) self.kncomm.sum(chi0_wvv) if self.intraband: self.kncomm.sum(self.chi0_vv) print('Memory used: {0:.3f} MB / CPU'.format(maxrss() / 1024**2), file=self.fd) if (self.eta == 0.0 or self.hilbert) and self.blockcomm.size == 1: # Fill in upper/lower triangle also: nG = pd.ngmax il = np.tril_indices(nG, -1) iu = il[::-1] if self.hilbert: for chi0_GG in chi0_wGG: chi0_GG[il] = chi0_GG[iu].conj() else: for chi0_GG in chi0_wGG: chi0_GG[iu] = chi0_GG[il].conj() if self.hilbert: with self.timer('Hilbert transform'): ht = HilbertTransform(self.omega_w, self.eta, self.timeordered) ht(chi0_wGG) if optical_limit: ht(chi0_wvv) ht(chi0_wxvG) print('Hilbert transform done', file=self.fd) if optical_limit and self.intraband: # Add intraband contribution omega_w = self.omega_w.copy() if omega_w[0] == 0.0: omega_w[0] = 1e-14 chi0_vv = self.chi0_vv self.world.broadcast(chi0_vv, 0) chi0_wvv += ( chi0_vv[np.newaxis] / (omega_w[:, np.newaxis, np.newaxis] * (omega_w[:, np.newaxis, np.newaxis] + 1j * self.eta))) return pd, chi0_wGG, chi0_wxvG, chi0_wvv
def diagonalize_full_hamiltonian(self, ham, atoms, occupations, txt, nbands=None, scalapack=None, expert=False): assert self.dtype == complex if nbands is None: nbands = self.pd.ngmin // self.bd.comm.size * self.bd.comm.size else: assert nbands <= self.pd.ngmin if expert: iu = nbands else: iu = None self.bd = bd = BandDescriptor(nbands, self.bd.comm) p = functools.partial(print, file=txt) p('Diagonalizing full Hamiltonian ({0} lowest bands)'.format(nbands)) p('Matrix size (min, max): {0}, {1}'.format(self.pd.ngmin, self.pd.ngmax)) mem = 3 * self.pd.ngmax**2 * 16 / bd.comm.size / 1024**2 p('Approximate memory usage per core: {0:.3f} MB'.format(mem)) if bd.comm.size > 1: if isinstance(scalapack, (list, tuple)): nprow, npcol, b = scalapack else: nprow = int(round(bd.comm.size**0.5)) while bd.comm.size % nprow != 0: nprow -= 1 npcol = bd.comm.size // nprow b = 64 p('ScaLapack grid: {0}x{1},'.format(nprow, npcol), 'block-size:', b) bg = BlacsGrid(bd.comm, bd.comm.size, 1) bg2 = BlacsGrid(bd.comm, nprow, npcol) scalapack = True else: nprow = npcol = 1 scalapack = False self.pt.set_positions(atoms.get_scaled_positions()) self.kpt_u[0].P_ani = None self.allocate_arrays_for_projections(self.pt.my_atom_indices) myslice = bd.get_slice() pb = ProgressBar(txt) nkpt = len(self.kpt_u) for u, kpt in enumerate(self.kpt_u): pb.update(u / nkpt) npw = len(self.pd.Q_qG[kpt.q]) if scalapack: mynpw = -(-npw // bd.comm.size) md = BlacsDescriptor(bg, npw, npw, mynpw, npw) md2 = BlacsDescriptor(bg2, npw, npw, b, b) else: md = md2 = MatrixDescriptor(npw, npw) with self.timer('Build H and S'): H_GG, S_GG = self.hs(ham, kpt.q, kpt.s, md) if scalapack: r = Redistributor(bd.comm, md, md2) H_GG = r.redistribute(H_GG) S_GG = r.redistribute(S_GG) psit_nG = md2.empty(dtype=complex) eps_n = np.empty(npw) with self.timer('Diagonalize'): if not scalapack: md2.general_diagonalize_dc(H_GG, S_GG, psit_nG, eps_n, iu=iu) else: md2.general_diagonalize_dc(H_GG, S_GG, psit_nG, eps_n) del H_GG, S_GG kpt.eps_n = eps_n[myslice].copy() if scalapack: md3 = BlacsDescriptor(bg, npw, npw, bd.mynbands, npw) r = Redistributor(bd.comm, md2, md3) psit_nG = r.redistribute(psit_nG) kpt.psit_nG = psit_nG[:bd.mynbands].copy() del psit_nG with self.timer('Projections'): self.pt.integrate(kpt.psit_nG, kpt.P_ani, kpt.q) kpt.f_n = None pb.finish() occupations.calculate(self)
def spectral_function_integration(self, domain=None, integrand=None, x=None, kwargs=None, out_wxx=None): """Integrate response function. Assume that the integral has the form of a response function. For the linear tetrahedron method it is possible calculate frequency dependent weights and do a point summation using these weights.""" if out_wxx is None: raise NotImplementedError nG = out_wxx.shape[2] mynG = (nG + self.blockcomm.size - 1) // self.blockcomm.size self.Ga = min(self.blockcomm.rank * mynG, nG) self.Gb = min(self.Ga + mynG, nG) # assert mynG * (self.blockcomm.size - 1) < nG, \ # print('mynG', mynG, 'nG', nG, 'nblocks', self.blockcomm.size) # Input domain td = self.tesselate(domain[0]) args = domain[1:] get_matrix_element, get_eigenvalues = integrand # The kwargs contain any constant # arguments provided by the user if kwargs is not None: get_matrix_element = partial(get_matrix_element, **kwargs[0]) get_eigenvalues = partial(get_eigenvalues, **kwargs[1]) # Relevant quantities bzk_kc = td.points nk = len(bzk_kc) with self.timer('pts'): # Point to simplex pts_k = [[] for n in range(nk)] for s, K_k in enumerate(td.simplices): A_kv = np.append(td.points[K_k], np.ones(4)[:, np.newaxis], axis=1) D_kv = np.append((A_kv[:, :-1]**2).sum(1)[:, np.newaxis], A_kv, axis=1) a = np.linalg.det(D_kv[:, np.arange(5) != 0]) if np.abs(a) < 1e-10: continue for K in K_k: pts_k[K].append(s) # Change to numpy arrays: for k in range(nk): pts_k[k] = np.array(pts_k[k], int) with self.timer('neighbours'): # Nearest neighbours neighbours_k = [None for n in range(nk)] for k in range(nk): neighbours_k[k] = np.unique(td.simplices[pts_k[k]]) # Distribute everything myterms_t = self.distribute_domain(list(args) + [list(range(nk))]) with self.timer('eigenvalues'): # Store eigenvalues deps_tMk = None # t for term shape = [len(domain_l) for domain_l in args] nterms = int(np.prod(shape)) for t in range(nterms): if len(shape) == 0: arguments = () else: arguments = np.unravel_index(t, shape) for K in range(nk): k_c = bzk_kc[K] deps_M = -get_eigenvalues(k_c, *arguments) if deps_tMk is None: deps_tMk = np.zeros([nterms] + list(deps_M.shape) + [nk], float) deps_tMk[t, :, K] = deps_M omega_w = x.get_data() # Calculate integrations weight pb = ProgressBar(self.fd) for _, arguments in pb.enumerate(myterms_t): K = arguments[-1] if len(shape) == 0: t = 0 else: t = np.ravel_multi_index(arguments[:-1], shape) deps_Mk = deps_tMk[t] teteps_Mk = deps_Mk[:, neighbours_k[K]] i0_M, i1_M = x.get_index_range(teteps_Mk.min(1), teteps_Mk.max(1)) n_MG = get_matrix_element(bzk_kc[K], *arguments[:-1]) for n_G, deps_k, i0, i1 in zip(n_MG, deps_Mk, i0_M, i1_M): if i0 == i1: continue W_w = self.get_kpoint_weight(K, deps_k, pts_k, omega_w[i0:i1], td) for iw, weight in enumerate(W_w): if self.blockcomm.size > 1: myn_G = n_G[self.Ga:self.Gb].reshape((-1, 1)) gemm(weight, n_G.reshape((-1, 1)), myn_G, 1.0, out_wxx[i0 + iw], 'c') else: czher(weight, n_G.conj(), out_wxx[i0 + iw]) self.kncomm.sum(out_wxx) if self.blockcomm.size == 1: # Fill in upper/lower triangle also: nx = out_wxx.shape[1] il = np.tril_indices(nx, -1) iu = il[::-1] for out_xx in out_wxx: out_xx[il] = out_xx[iu].conj()
def response_function_integration(self, domain=None, integrand=None, x=None, kwargs=None, out_wxx=None, timeordered=False, hermitian=False, intraband=False, hilbert=False, wings=False, **extraargs): """Integrate a response function over bands and kpoints. func: method omega_w: ndarray out: np.ndarray timeordered: Bool """ if out_wxx is None: raise NotImplementedError nG = out_wxx.shape[2] mynG = (nG + self.blockcomm.size - 1) // self.blockcomm.size self.Ga = min(self.blockcomm.rank * mynG, nG) self.Gb = min(self.Ga + mynG, nG) # assert mynG * (self.blockcomm.size - 1) < nG, \ # print('mynG', mynG, 'nG', nG, 'nblocks', self.blockcomm.size) mydomain_t = self.distribute_domain(domain) nbz = len(domain[0]) get_matrix_element, get_eigenvalues = integrand prefactor = (2 * np.pi)**3 / self.vol / nbz out_wxx /= prefactor # The kwargs contain any constant # arguments provided by the user if kwargs is not None: get_matrix_element = partial(get_matrix_element, **kwargs[0]) get_eigenvalues = partial(get_eigenvalues, **kwargs[1]) # Sum kpoints # Calculate integrations weight pb = ProgressBar(self.fd) for _, arguments in pb.enumerate(mydomain_t): n_MG = get_matrix_element(*arguments) if n_MG is None: continue deps_M = get_eigenvalues(*arguments) if intraband: self.update_intraband(n_MG, out_wxx, **extraargs) elif hermitian and not wings: self.update_hermitian(n_MG, deps_M, x, out_wxx, **extraargs) elif hermitian and wings: self.update_optical_limit(n_MG, deps_M, x, out_wxx, **extraargs) elif hilbert and not wings: self.update_hilbert(n_MG, deps_M, x, out_wxx, **extraargs) elif hilbert and wings: self.update_hilbert_optical_limit(n_MG, deps_M, x, out_wxx, **extraargs) elif wings: self.update_optical_limit(n_MG, deps_M, x, out_wxx, **extraargs) else: self.update(n_MG, deps_M, x, out_wxx, **extraargs) # Sum over self.kncomm.sum(out_wxx) if (hermitian or hilbert) and self.blockcomm.size == 1 and not wings: # Fill in upper/lower triangle also: nx = out_wxx.shape[1] il = np.tril_indices(nx, -1) iu = il[::-1] if hilbert: for out_xx in out_wxx: out_xx[il] = out_xx[iu].conj() else: for out_xx in out_wxx: out_xx[iu] = out_xx[il].conj() out_wxx *= prefactor
def diagonalize_full_hamiltonian(self, ham, atoms, occupations, log, nbands=None, ecut=None, scalapack=None, expert=False): if self.dtype != complex: raise ValueError('Your wavefunctions are not complex as ' 'required by the PW diagonalization routine.\n' 'Please supply GPAW(..., dtype=complex, ...) ' 'as an argument to the calculator to enforce ' 'complex wavefunctions.') if nbands is None and ecut is None: nbands = self.pd.ngmin // self.bd.comm.size * self.bd.comm.size elif nbands is None: ecut /= units.Hartree vol = abs(np.linalg.det(self.gd.cell_cv)) nbands = int(vol * ecut**1.5 * 2**0.5 / 3 / pi**2) else: assert nbands <= self.pd.ngmin if expert: iu = nbands else: iu = None self.bd = bd = BandDescriptor(nbands, self.bd.comm) log('Diagonalizing full Hamiltonian ({0} lowest bands)'.format(nbands)) log('Matrix size (min, max): {0}, {1}'.format(self.pd.ngmin, self.pd.ngmax)) mem = 3 * self.pd.ngmax**2 * 16 / bd.comm.size / 1024**2 log('Approximate memory usage per core: {0:.3f} MB'.format(mem)) if bd.comm.size > 1: if isinstance(scalapack, (list, tuple)): nprow, npcol, b = scalapack else: nprow = int(round(bd.comm.size**0.5)) while bd.comm.size % nprow != 0: nprow -= 1 npcol = bd.comm.size // nprow b = 64 log('ScaLapack grid: {0}x{1},'.format(nprow, npcol), 'block-size:', b) bg = BlacsGrid(bd.comm, bd.comm.size, 1) bg2 = BlacsGrid(bd.comm, nprow, npcol) scalapack = True else: nprow = npcol = 1 scalapack = False self.set_positions(atoms.get_scaled_positions()) self.kpt_u[0].P_ani = None self.allocate_arrays_for_projections(self.pt.my_atom_indices) myslice = bd.get_slice() pb = ProgressBar(log.fd) nkpt = len(self.kpt_u) for u, kpt in enumerate(self.kpt_u): pb.update(u / nkpt) npw = len(self.pd.Q_qG[kpt.q]) if scalapack: mynpw = -(-npw // bd.comm.size) md = BlacsDescriptor(bg, npw, npw, mynpw, npw) md2 = BlacsDescriptor(bg2, npw, npw, b, b) else: md = md2 = MatrixDescriptor(npw, npw) with self.timer('Build H and S'): H_GG, S_GG = self.hs(ham, kpt.q, kpt.s, md) if scalapack: r = Redistributor(bd.comm, md, md2) H_GG = r.redistribute(H_GG) S_GG = r.redistribute(S_GG) psit_nG = md2.empty(dtype=complex) eps_n = np.empty(npw) with self.timer('Diagonalize'): if not scalapack: md2.general_diagonalize_dc(H_GG, S_GG, psit_nG, eps_n, iu=iu) else: md2.general_diagonalize_dc(H_GG, S_GG, psit_nG, eps_n) del H_GG, S_GG kpt.eps_n = eps_n[myslice].copy() if scalapack: md3 = BlacsDescriptor(bg, npw, npw, bd.mynbands, npw) r = Redistributor(bd.comm, md2, md3) psit_nG = r.redistribute(psit_nG) kpt.psit_nG = psit_nG[:bd.mynbands].copy() del psit_nG with self.timer('Projections'): self.pt.integrate(kpt.psit_nG, kpt.P_ani, kpt.q) kpt.f_n = None pb.finish() occupations.calculate(self) return nbands