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