def _fake_nuc(cell): fakenuc = copy.copy(cell) fakenuc._atm = cell._atm.copy() fakenuc._atm[:,gto.PTR_COORD] = numpy.arange(gto.PTR_ENV_START, gto.PTR_ENV_START+cell.natm*3,3) _bas = [] _env = [0]*gto.PTR_ENV_START + [cell.atom_coords().ravel()] ptr = gto.PTR_ENV_START + cell.natm * 3 half_sph_norm = .5/numpy.sqrt(numpy.pi) for ia in range(cell.natm): symb = cell.atom_symbol(ia) if symb in cell._pseudo: pp = cell._pseudo[symb] rloc, nexp, cexp = pp[1:3+1] eta = .5 / rloc**2 else: eta = 1e16 norm = half_sph_norm/gto.gaussian_int(2, eta) _env.extend([eta, norm]) _bas.append([ia, 0, 1, 1, 0, ptr, ptr+1, 0]) ptr += 2 fakenuc._bas = numpy.asarray(_bas, dtype=numpy.int32) fakenuc._env = numpy.asarray(numpy.hstack(_env), dtype=numpy.double) fakenuc.rcut = cell.rcut return fakenuc
def make_modchg_basis(auxcell, smooth_eta): # * chgcell defines smooth gaussian functions for each angular momentum for # auxcell. The smooth functions may be used to carry the charge chgcell = copy.copy( auxcell) # smooth model density for coulomb integral to carry charge half_sph_norm = .5 / numpy.sqrt(numpy.pi) chg_bas = [] chg_env = [smooth_eta] ptr_eta = auxcell._env.size ptr = ptr_eta + 1 l_max = auxcell._bas[:, gto.ANG_OF].max() # gaussian_int(l*2+2) for multipole integral: # \int (r^l e^{-ar^2} * Y_{lm}) (r^l Y_{lm}) r^2 dr d\Omega norms = [ half_sph_norm / gto.gaussian_int(l * 2 + 2, smooth_eta) for l in range(l_max + 1) ] for ia in range(auxcell.natm): for l in set(auxcell._bas[auxcell._bas[:, gto.ATOM_OF] == ia, gto.ANG_OF]): chg_bas.append([ia, l, 1, 1, 0, ptr_eta, ptr, 0]) chg_env.append(norms[l]) ptr += 1 chgcell._atm = auxcell._atm chgcell._bas = numpy.asarray(chg_bas, dtype=numpy.int32).reshape(-1, gto.BAS_SLOTS) chgcell._env = numpy.hstack((auxcell._env, chg_env)) chgcell.rcut = _estimate_rcut(smooth_eta, l_max, 1., auxcell.precision) logger.debug1(auxcell, 'make compensating basis, num shells = %d, num cGTOs = %d', chgcell.nbas, chgcell.nao_nr()) logger.debug1(auxcell, 'chgcell.rcut %s', chgcell.rcut) return chgcell
def _fake_nuc(cell): fakenuc = copy.copy(cell) fakenuc._atm = cell._atm.copy() fakenuc._atm[:, gto.PTR_COORD] = numpy.arange( gto.PTR_ENV_START, gto.PTR_ENV_START + cell.natm * 3, 3) _bas = [] _env = [0] * gto.PTR_ENV_START + [cell.atom_coords().ravel()] ptr = gto.PTR_ENV_START + cell.natm * 3 half_sph_norm = .5 / numpy.sqrt(numpy.pi) for ia in range(cell.natm): symb = cell.atom_symbol(ia) if symb in cell._pseudo: pp = cell._pseudo[symb] rloc, nexp, cexp = pp[1:3 + 1] eta = .5 / rloc**2 else: eta = 1e16 norm = half_sph_norm / gto.gaussian_int(2, eta) _env.extend([eta, norm]) _bas.append([ia, 0, 1, 1, 0, ptr, ptr + 1, 0]) ptr += 2 fakenuc._bas = numpy.asarray(_bas, dtype=numpy.int32) fakenuc._env = numpy.asarray(numpy.hstack(_env), dtype=numpy.double) fakenuc.rcut = cell.rcut return fakenuc
def make_modchg_basis(auxcell, smooth_eta): # * chgcell defines smooth gaussian functions for each angular momentum for # auxcell. The smooth functions may be used to carry the charge chgcell = copy.copy(auxcell) # smooth model density for coulomb integral to carry charge half_sph_norm = .5/numpy.sqrt(numpy.pi) chg_bas = [] chg_env = [smooth_eta] ptr_eta = auxcell._env.size ptr = ptr_eta + 1 l_max = auxcell._bas[:,gto.ANG_OF].max() # gaussian_int(l*2+2) for multipole integral: # \int (r^l e^{-ar^2} * Y_{lm}) (r^l Y_{lm}) r^2 dr d\Omega norms = [half_sph_norm/gto.gaussian_int(l*2+2, smooth_eta) for l in range(l_max+1)] for ia in range(auxcell.natm): for l in set(auxcell._bas[auxcell._bas[:,gto.ATOM_OF]==ia, gto.ANG_OF]): chg_bas.append([ia, l, 1, 1, 0, ptr_eta, ptr, 0]) chg_env.append(norms[l]) ptr += 1 chgcell._atm = auxcell._atm chgcell._bas = numpy.asarray(chg_bas, dtype=numpy.int32).reshape(-1,gto.BAS_SLOTS) chgcell._env = numpy.hstack((auxcell._env, chg_env)) chgcell.rcut = _estimate_rcut(smooth_eta, l_max, 1., auxcell.precision) logger.debug1(auxcell, 'make compensating basis, num shells = %d, num cGTOs = %d', chgcell.nbas, chgcell.nao_nr()) logger.debug1(auxcell, 'chgcell.rcut %s', chgcell.rcut) return chgcell
def get_aux_chg(auxcell): r""" Compute charge of the auxiliary basis, \int_Omega dr chi_P(r) Returns: The function returns a 1d numpy array of size auxcell.nao_nr(). """ def get_nd(l): if auxcell.cart: return (l + 1) * (l + 2) // 2 else: return 2 * l + 1 naux = auxcell.nao_nr() qs = np.zeros(naux) shift = 0 half_sph_norm = np.sqrt(4 * np.pi) for ib in range(auxcell.nbas): l = auxcell.bas_angular(ib) if l == 0: npm = auxcell.bas_nprim(ib) nc = auxcell.bas_nctr(ib) es = auxcell.bas_exp(ib) ptr = auxcell._bas[ib, mol_gto.PTR_COEFF] cs = auxcell._env[ptr:ptr + npm * nc].reshape(nc, npm).T norms = mol_gto.gaussian_int(l + 2, es) q = np.einsum("i,ij->j", norms, cs)[0] * half_sph_norm else: # higher angular momentum AOs carry no charge q = 0. nd = get_nd(l) qs[shift:shift + nd] = q shift += nd return qs
def make_modrho_basis(cell, auxbasis=None, drop_eta=None): auxcell = addons.make_auxmol(cell, auxbasis) # Note libcint library will multiply the norm of the integration over spheric # part sqrt(4pi) to the basis. half_sph_norm = numpy.sqrt(.25 / numpy.pi) steep_shls = [] ndrop = 0 rcut = [] _env = auxcell._env.copy() for ib in range(len(auxcell._bas)): l = auxcell.bas_angular(ib) np = auxcell.bas_nprim(ib) nc = auxcell.bas_nctr(ib) es = auxcell.bas_exp(ib) ptr = auxcell._bas[ib, gto.PTR_COEFF] cs = auxcell._env[ptr:ptr + np * nc].reshape(nc, np).T if drop_eta is not None and numpy.any(es < drop_eta): cs = cs[es >= drop_eta] es = es[es >= drop_eta] np, ndrop = len(es), ndrop + np - len(es) if np > 0: pe = auxcell._bas[ib, gto.PTR_EXP] auxcell._bas[ib, gto.NPRIM_OF] = np _env[pe:pe + np] = es # int1 is the multipole value. l*2+2 is due to the radial part integral # \int (r^l e^{-ar^2} * Y_{lm}) (r^l Y_{lm}) r^2 dr d\Omega int1 = gto.gaussian_int(l * 2 + 2, es) s = numpy.einsum('pi,p->i', cs, int1) # The auxiliary basis normalization factor is not a must for density expansion. # half_sph_norm here to normalize the monopole (charge). This convention can # simplify the formulism of \int \bar{\rho}, see function auxbar. cs = numpy.einsum('pi,i->pi', cs, half_sph_norm / s) _env[ptr:ptr + np * nc] = cs.T.reshape(-1) steep_shls.append(ib) r = _estimate_rcut(es, l, abs(cs).max(axis=1), cell.precision) rcut.append(r.max()) auxcell._env = _env auxcell.rcut = max(rcut) auxcell._bas = numpy.asarray(auxcell._bas[steep_shls], order='C') logger.info(cell, 'Drop %d primitive fitting functions', ndrop) logger.info(cell, 'make aux basis, num shells = %d, num cGTOs = %d', auxcell.nbas, auxcell.nao_nr()) logger.info(cell, 'auxcell.rcut %s', auxcell.rcut) return auxcell
def _compensate_nuccell(mydf): '''A cell of the compensated Gaussian charges for nucleus''' cell = mydf.cell nuccell = copy.copy(cell) half_sph_norm = .5/numpy.sqrt(numpy.pi) norm = half_sph_norm/gto.gaussian_int(2, mydf.eta) chg_env = [mydf.eta, norm] ptr_eta = cell._env.size ptr_norm = ptr_eta + 1 chg_bas = [[ia, 0, 1, 1, 0, ptr_eta, ptr_norm, 0] for ia in range(cell.natm)] nuccell._atm = cell._atm nuccell._bas = numpy.asarray(chg_bas, dtype=numpy.int32) nuccell._env = numpy.hstack((cell._env, chg_env)) return nuccell
def make_modrho_basis(cell, auxbasis=None, drop_eta=None): auxcell = addons.make_auxmol(cell, auxbasis) # Note libcint library will multiply the norm of the integration over spheric # part sqrt(4pi) to the basis. half_sph_norm = numpy.sqrt(.25/numpy.pi) steep_shls = [] ndrop = 0 rcut = [] for ib in range(len(auxcell._bas)): l = auxcell.bas_angular(ib) np = auxcell.bas_nprim(ib) nc = auxcell.bas_nctr(ib) es = auxcell.bas_exp(ib) ptr = auxcell._bas[ib,gto.PTR_COEFF] cs = auxcell._env[ptr:ptr+np*nc].reshape(nc,np).T if drop_eta is not None and numpy.any(es < drop_eta): cs = cs[es>=drop_eta] es = es[es>=drop_eta] np, ndrop = len(es), ndrop+np-len(es) if np > 0: pe = auxcell._bas[ib,gto.PTR_EXP] auxcell._bas[ib,gto.NPRIM_OF] = np auxcell._env[pe:pe+np] = es # int1 is the multipole value. l*2+2 is due to the radial part integral # \int (r^l e^{-ar^2} * Y_{lm}) (r^l Y_{lm}) r^2 dr d\Omega int1 = gto.gaussian_int(l*2+2, es) s = numpy.einsum('pi,p->i', cs, int1) # The auxiliary basis normalization factor is not a must for density expansion. # half_sph_norm here to normalize the monopole (charge). This convention can # simplify the formulism of \int \bar{\rho}, see function auxbar. cs = numpy.einsum('pi,i->pi', cs, half_sph_norm/s) auxcell._env[ptr:ptr+np*nc] = cs.T.reshape(-1) steep_shls.append(ib) r = _estimate_rcut(es, l, abs(cs).max(axis=1), cell.precision) rcut.append(r.max()) auxcell.rcut = max(rcut) auxcell._bas = numpy.asarray(auxcell._bas[steep_shls], order='C') logger.info(cell, 'Drop %d primitive fitting functions', ndrop) logger.info(cell, 'make aux basis, num shells = %d, num cGTOs = %d', auxcell.nbas, auxcell.nao_nr()) logger.info(cell, 'auxcell.rcut %s', auxcell.rcut) return auxcell
def fake_cell_vloc(cell, cn=0): '''Generate fake cell for V_{loc}. Each term of V_{loc} (erf, C_1, C_2, C_3, C_4) is a gaussian type function. The integral over V_{loc} can be transfered to the 3-center integrals, in which the auxiliary basis is given by the fake cell. The kwarg cn indiciates which term to generate for the fake cell. If cn = 0, the erf term is generated. C_1,..,C_4 are generated with cn = 1..4 ''' fake_env = [cell.atom_coords().ravel()] fake_atm = cell._atm.copy() fake_atm[:, gto.PTR_COORD] = numpy.arange(0, cell.natm * 3, 3) ptr = cell.natm * 3 fake_bas = [] half_sph_norm = .5 / numpy.sqrt(numpy.pi) for ia in range(cell.natm): if cell.atom_charge(ia) == 0: # pass ghost atoms continue symb = cell.atom_symbol(ia) if cn == 0: if symb in cell._pseudo: pp = cell._pseudo[symb] rloc, nexp, cexp = pp[1:3 + 1] alpha = .5 / rloc**2 else: alpha = 1e16 norm = half_sph_norm / gto.gaussian_int(2, alpha) fake_env.append([alpha, norm]) fake_bas.append([ia, 0, 1, 1, 0, ptr, ptr + 1, 0]) ptr += 2 elif symb in cell._pseudo: pp = cell._pseudo[symb] rloc, nexp, cexp = pp[1:3 + 1] if cn <= nexp: alpha = .5 / rloc**2 norm = cexp[cn - 1] / rloc**(cn * 2 - 2) / half_sph_norm fake_env.append([alpha, norm]) fake_bas.append([ia, 0, 1, 1, 0, ptr, ptr + 1, 0]) ptr += 2 fakecell = copy.copy(cell) fakecell._atm = numpy.asarray(fake_atm, dtype=numpy.int32) fakecell._bas = numpy.asarray(fake_bas, dtype=numpy.int32) fakecell._env = numpy.asarray(numpy.hstack(fake_env), dtype=numpy.double) return fakecell
def fake_cell_vloc(cell, cn=0): '''Generate fake cell for V_{loc}. Each term of V_{loc} (erf, C_1, C_2, C_3, C_4) is a gaussian type function. The integral over V_{loc} can be transfered to the 3-center integrals, in which the auxiliary basis is given by the fake cell. The kwarg cn indiciates which term to generate for the fake cell. If cn = 0, the erf term is generated. C_1,..,C_4 are generated with cn = 1..4 ''' fake_env = [cell.atom_coords().ravel()] fake_atm = cell._atm.copy() fake_atm[:,gto.PTR_COORD] = numpy.arange(0, cell.natm*3, 3) ptr = cell.natm * 3 fake_bas = [] half_sph_norm = .5/numpy.sqrt(numpy.pi) for ia in range(cell.natm): if cell.atom_charge(ia) == 0: # pass ghost atoms continue symb = cell.atom_symbol(ia) if cn == 0: if symb in cell._pseudo: pp = cell._pseudo[symb] rloc, nexp, cexp = pp[1:3+1] alpha = .5 / rloc**2 else: alpha = 1e16 norm = half_sph_norm / gto.gaussian_int(2, alpha) fake_env.append([alpha, norm]) fake_bas.append([ia, 0, 1, 1, 0, ptr, ptr+1, 0]) ptr += 2 elif symb in cell._pseudo: pp = cell._pseudo[symb] rloc, nexp, cexp = pp[1:3+1] if cn <= nexp: alpha = .5 / rloc**2 norm = cexp[cn-1]/rloc**(cn*2-2) / half_sph_norm fake_env.append([alpha, norm]) fake_bas.append([ia, 0, 1, 1, 0, ptr, ptr+1, 0]) ptr += 2 fakecell = copy.copy(cell) fakecell._atm = numpy.asarray(fake_atm, dtype=numpy.int32) fakecell._bas = numpy.asarray(fake_bas, dtype=numpy.int32) fakecell._env = numpy.asarray(numpy.hstack(fake_env), dtype=numpy.double) return fakecell
def auxbar(self, fused_cell=None): r''' Potential average = \sum_L V_L*Lpq The coulomb energy is computed with chargeless density \int (rho-C) V, C = (\int rho) / vol = Tr(gamma,S)/vol It is equivalent to removing the averaged potential from the short range V vs = vs - (\int V)/vol * S ''' if fused_cell is None: fused_cell, fuse = fuse_auxcell(self, self.auxcell) aux_loc = fused_cell.ao_loc_nr() vbar = numpy.zeros(aux_loc[-1]) if fused_cell.dimension != 3: return vbar half_sph_norm = .5 / numpy.sqrt(numpy.pi) for i in range(fused_cell.nbas): l = fused_cell.bas_angular(i) if l == 0: es = fused_cell.bas_exp(i) if es.size == 1: vbar[aux_loc[i]] = -1 / es[0] else: # Remove the normalization to get the primitive contraction coeffcients norms = half_sph_norm / gto.gaussian_int(2, es) cs = numpy.einsum('i,ij->ij', 1 / norms, fused_cell._libcint_ctr_coeff(i)) vbar[aux_loc[i]:aux_loc[i + 1]] = numpy.einsum( 'in,i->n', cs, -1 / es) # TODO: fused_cell.cart and l%2 == 0: # 6d 10f ... # Normalization coefficients are different in the same shell for cartesian # basis. E.g. the d-type functions, the 5 d-type orbitals are normalized wrt # the integral \int r^2 * r^2 e^{-a r^2} dr. The s-type 3s orbital should be # normalized wrt the integral \int r^0 * r^2 e^{-a r^2} dr. The different # normalization was not built in the basis. vbar *= numpy.pi / fused_cell.vol return vbar
def auxbar(self, fused_cell=None): r''' Potential average = \sum_L V_L*Lpq The coulomb energy is computed with chargeless density \int (rho-C) V, C = (\int rho) / vol = Tr(gamma,S)/vol It is equivalent to removing the averaged potential from the short range V vs = vs - (\int V)/vol * S ''' if fused_cell is None: fused_cell, fuse = fuse_auxcell(self, self.auxcell) aux_loc = fused_cell.ao_loc_nr() vbar = numpy.zeros(aux_loc[-1]) if fused_cell.dimension != 3: return vbar half_sph_norm = .5/numpy.sqrt(numpy.pi) for i in range(fused_cell.nbas): l = fused_cell.bas_angular(i) if l == 0: es = fused_cell.bas_exp(i) if es.size == 1: vbar[aux_loc[i]] = -1/es[0] else: # Remove the normalization to get the primitive contraction coeffcients norms = half_sph_norm/gto.gaussian_int(2, es) cs = numpy.einsum('i,ij->ij', 1/norms, fused_cell._libcint_ctr_coeff(i)) vbar[aux_loc[i]:aux_loc[i+1]] = numpy.einsum('in,i->n', cs, -1/es) # TODO: fused_cell.cart and l%2 == 0: # 6d 10f ... # Normalization coefficients are different in the same shell for cartesian # basis. E.g. the d-type functions, the 5 d-type orbitals are normalized wrt # the integral \int r^2 * r^2 e^{-a r^2} dr. The s-type 3s orbital should be # normalized wrt the integral \int r^0 * r^2 e^{-a r^2} dr. The different # normalization was not built in the basis. vbar *= numpy.pi/fused_cell.vol return vbar
def get_nuc(mydf, kpts=None): cell = mydf.cell if kpts is None: kpts_lst = numpy.zeros((1, 3)) else: kpts_lst = numpy.reshape(kpts, (-1, 3)) log = logger.Logger(mydf.stdout, mydf.verbose) t0 = t1 = (time.clock(), time.time()) mesh = numpy.asarray(mydf.mesh) nkpts = len(kpts_lst) nao = cell.nao_nr() nao_pair = nao * (nao + 1) // 2 charges = cell.atom_charges() kpt_allow = numpy.zeros(3) if mydf.eta == 0: if cell.dimension > 0: ke_guess = estimate_ke_cutoff(cell, cell.precision) mesh_guess = tools.cutoff_to_mesh(cell.lattice_vectors(), ke_guess) if numpy.any(mesh < mesh_guess * .8): logger.warn( mydf, 'mesh %s is not enough for AFTDF.get_nuc function ' 'to get integral accuracy %g.\nRecommended mesh is %s.', mesh, cell.precision, mesh_guess) Gv, Gvbase, kws = cell.get_Gv_weights(mesh) vpplocG = pseudo.pp_int.get_gth_vlocG_part1(cell, Gv) vpplocG = -numpy.einsum('ij,ij->j', cell.get_SI(Gv), vpplocG) v1 = -vpplocG.copy() if cell.dimension == 1 or cell.dimension == 2: G0idx, SI_on_z = pbcgto.cell._SI_for_uniform_model_charge(cell, Gv) coulG = 4 * numpy.pi / numpy.linalg.norm(Gv[G0idx], axis=1)**2 vpplocG[G0idx] += charges.sum() * SI_on_z * coulG vpplocG *= kws vG = vpplocG vj = numpy.zeros((nkpts, nao_pair), dtype=numpy.complex128) else: if cell.dimension > 0: ke_guess = estimate_ke_cutoff_for_eta(cell, mydf.eta, cell.precision) mesh_guess = tools.cutoff_to_mesh(cell.lattice_vectors(), ke_guess) if numpy.any(mesh < mesh_guess * .8): logger.warn( mydf, 'mesh %s is not enough for AFTDF.get_nuc function ' 'to get integral accuracy %g.\nRecommended mesh is %s.', mesh, cell.precision, mesh_guess) mesh_min = numpy.min( (mesh_guess[:cell.dimension], mesh[:cell.dimension]), axis=0) mesh[:cell.dimension] = mesh_min.astype(int) Gv, Gvbase, kws = cell.get_Gv_weights(mesh) nuccell = copy.copy(cell) half_sph_norm = .5 / numpy.sqrt(numpy.pi) norm = half_sph_norm / gto.gaussian_int(2, mydf.eta) chg_env = [mydf.eta, norm] ptr_eta = cell._env.size ptr_norm = ptr_eta + 1 chg_bas = [[ia, 0, 1, 1, 0, ptr_eta, ptr_norm, 0] for ia in range(cell.natm)] nuccell._atm = cell._atm nuccell._bas = numpy.asarray(chg_bas, dtype=numpy.int32) nuccell._env = numpy.hstack((cell._env, chg_env)) # PP-loc part1 is handled by fakenuc in _int_nuc_vloc vj = lib.asarray(mydf._int_nuc_vloc(nuccell, kpts_lst)) t0 = t1 = log.timer_debug1('vnuc pass1: analytic int', *t0) coulG = tools.get_coulG(cell, kpt_allow, mesh=mesh, Gv=Gv) * kws aoaux = ft_ao.ft_ao(nuccell, Gv) vG = numpy.einsum('i,xi->x', -charges, aoaux) * coulG if cell.dimension == 1 or cell.dimension == 2: G0idx, SI_on_z = pbcgto.cell._SI_for_uniform_model_charge(cell, Gv) vG[G0idx] += charges.sum() * SI_on_z * coulG[G0idx] max_memory = max(2000, mydf.max_memory - lib.current_memory()[0]) for aoaoks, p0, p1 in mydf.ft_loop(mesh, kpt_allow, kpts_lst, max_memory=max_memory, aosym='s2'): for k, aoao in enumerate(aoaoks): # rho_ij(G) nuc(-G) / G^2 # = [Re(rho_ij(G)) + Im(rho_ij(G))*1j] [Re(nuc(G)) - Im(nuc(G))*1j] / G^2 if gamma_point(kpts_lst[k]): vj[k] += numpy.einsum('k,kx->x', vG[p0:p1].real, aoao.real) vj[k] += numpy.einsum('k,kx->x', vG[p0:p1].imag, aoao.imag) else: vj[k] += numpy.einsum('k,kx->x', vG[p0:p1].conj(), aoao) t1 = log.timer_debug1('contracting Vnuc [%s:%s]' % (p0, p1), *t1) log.timer_debug1('contracting Vnuc', *t0) vj_kpts = [] for k, kpt in enumerate(kpts_lst): if gamma_point(kpt): vj_kpts.append(lib.unpack_tril(vj[k].real.copy())) else: vj_kpts.append(lib.unpack_tril(vj[k])) if kpts is None or numpy.shape(kpts) == (3, ): vj_kpts = vj_kpts[0] return numpy.asarray(vj_kpts)