def _dft_common_init_(mf): mf.xc = 'LDA,VWN' mf.nlc = '' mf.grids = gen_grid.Grids(mf.mol) mf.grids.level = getattr(__config__, 'dft_rks_RKS_grids_level', mf.grids.level) mf.nlcgrids = gen_grid.Grids(mf.mol) mf.nlcgrids.level = getattr(__config__, 'dft_rks_RKS_nlcgrids_level', mf.nlcgrids.level) # Use rho to filter grids mf.small_rho_cutoff = getattr(__config__, 'dft_rks_RKS_small_rho_cutoff', 1e-7) ################################################## # don't modify the following attributes, they are not input options mf._numint = numint.NumInt() mf._keys = mf._keys.union(['xc', 'nlc', 'omega', 'grids', 'nlcgrids', 'small_rho_cutoff'])
def make_e_psi1(pcmobj, dm, r_vdw, ui, ylm_1sph, cached_pol, Xvec, L): mol = pcmobj.mol natm = mol.natm lmax = pcmobj.lmax nlm = (lmax + 1)**2 grids = pcmobj.grids if not (isinstance(dm, numpy.ndarray) and dm.ndim == 2): dm = dm[0] + dm[1] ni = numint.NumInt() max_memory = pcmobj.max_memory - lib.current_memory()[0] make_rho, nset, nao = ni._gen_rho_evaluator(mol, dm) den = numpy.empty((4, grids.weights.size)) ao_loc = mol.ao_loc_nr() vmat = numpy.zeros((3, nao, nao)) psi1 = numpy.zeros((natm, 3)) i1 = 0 for ia, (coords, weight, weight1) in enumerate(rks_grad.grids_response_cc(grids)): i0, i1 = i1, i1 + weight.size ao = ni.eval_ao(mol, coords, deriv=1) mask = gen_grid.make_mask(mol, coords) den[:, i0:i1] = make_rho(0, ao, mask, 'GGA') fak_pol, leak_idx = cached_pol[mol.atom_symbol(ia)] eta_nj = 0 p1 = 0 for l in range(lmax + 1): fac = 4 * numpy.pi / (l * 2 + 1) p0, p1 = p1, p1 + (l * 2 + 1) eta_nj += fac * numpy.einsum('mn,m->n', fak_pol[l], Xvec[ia, p0:p1]) psi1 -= numpy.einsum('n,n,zxn->zx', den[0, i0:i1], eta_nj, weight1) psi1[ia] -= numpy.einsum('xn,n,n->x', den[1:4, i0:i1], eta_nj, weight) vtmp = numpy.zeros((3, nao, nao)) aow = numpy.einsum('pi,p->pi', ao[0], weight * eta_nj) rks_grad._d1_dot_(vtmp, mol, ao[1:4], aow, mask, ao_loc, True) vmat += vtmp aoslices = mol.aoslice_by_atom() for ia in range(natm): shl0, shl1, p0, p1 = aoslices[ia] psi1[ia] += numpy.einsum('xij,ij->x', vmat[:, p0:p1], dm[p0:p1]) * 2 return psi1
def make_psi_vmat(pcmobj, dm, r_vdw, ui, grids, ylm_1sph, cached_pol, L_X, L): mol = pcmobj.mol natm = mol.natm lmax = pcmobj.lmax nlm = (lmax+1)**2 i1 = 0 scaled_weights = numpy.empty(grids.weights.size) for ia in range(natm): fak_pol, leak_idx = cached_pol[mol.atom_symbol(ia)] i0, i1 = i1, i1 + fak_pol[0].shape[1] eta_nj = 0 p1 = 0 for l in range(lmax+1): fac = 4*numpy.pi/(l*2+1) p0, p1 = p1, p1 + (l*2+1) eta_nj += fac * numpy.einsum('mn,m->n', fak_pol[l], L_X[ia,p0:p1]) scaled_weights[i0:i1] = eta_nj * grids.weights[i0:i1] if not (isinstance(dm, numpy.ndarray) and dm.ndim == 2): dm = dm[0] + dm[1] ni = numint.NumInt() max_memory = pcmobj.max_memory - lib.current_memory()[0] make_rho, nset, nao = ni._gen_rho_evaluator(mol, dm) shls_slice = (0, mol.nbas) ao_loc = mol.ao_loc_nr() den = numpy.empty(grids.weights.size) vmat = numpy.zeros((nao,nao)) p1 = 0 aow = None for ao, mask, weight, coords \ in ni.block_loop(mol, grids, nao, 0, max_memory): p0, p1 = p1, p1 + weight.size den[p0:p1] = weight * make_rho(0, ao, mask, 'LDA') aow = numpy.ndarray(ao.shape, order='F', buffer=aow) aow = numpy.einsum('pi,p->pi', ao, scaled_weights[p0:p1], out=aow) vmat -= numint._dot_ao_ao(mol, ao, aow, mask, shls_slice, ao_loc) ao = aow = scaled_weights = None nelec_leak = 0 psi = numpy.empty((natm,nlm)) i1 = 0 for ia in range(natm): fak_pol, leak_idx = cached_pol[mol.atom_symbol(ia)] i0, i1 = i1, i1 + fak_pol[0].shape[1] nelec_leak += den[i0:i1][leak_idx].sum() p1 = 0 for l in range(lmax+1): fac = 4*numpy.pi/(l*2+1) p0, p1 = p1, p1 + (l*2+1) psi[ia,p0:p1] = -fac * numpy.einsum('n,mn->m', den[i0:i1], fak_pol[l]) # Contribution of nuclear charge to the total density # The factor numpy.sqrt(4*numpy.pi) is due to the product of 4*pi * Y_0^0 psi[ia,0] += numpy.sqrt(4*numpy.pi)/r_vdw[ia] * mol.atom_charge(ia) logger.debug(pcmobj, 'electron leak %f', nelec_leak) # <Psi, L^{-1}g> -> Psi = SL the adjoint equation to LX = g L_S = numpy.linalg.solve(L.T.reshape(natm*nlm,-1), psi.ravel()).reshape(natm,-1) coords_1sph, weights_1sph = make_grids_one_sphere(pcmobj.lebedev_order) # JCP, 141, 184108, Eq (39) xi_jn = numpy.einsum('n,jn,xn,jx->jn', weights_1sph, ui, ylm_1sph, L_S) extern_point_idx = ui > 0 cav_coords = (mol.atom_coords().reshape(natm,1,3) + numpy.einsum('r,gx->rgx', r_vdw, coords_1sph)) cav_coords = cav_coords[extern_point_idx] xi_jn = xi_jn[extern_point_idx] max_memory = pcmobj.max_memory - lib.current_memory()[0] blksize = int(max(max_memory*1e6/8/nao**2, 400)) cintopt = gto.moleintor.make_cintopt(mol._atm, mol._bas, mol._env, 'int3c2e') vmat_tril = 0 for i0, i1 in lib.prange(0, xi_jn.size, blksize): fakemol = gto.fakemol_for_charges(cav_coords[i0:i1]) v_nj = df.incore.aux_e2(mol, fakemol, intor='int3c2e', aosym='s2ij', cintopt=cintopt) vmat_tril += numpy.einsum('xn,n->x', v_nj, xi_jn[i0:i1]) vmat += lib.unpack_tril(vmat_tril) return psi, vmat, L_S
def make_psi_vmat(pcmobj, dm, r_vdw, ui, ylm_1sph, cached_pol, Xvec, L, with_nuc=True): ''' The first order derivative of E_ddCOSMO wrt density matrix Kwargs: with_nuc (bool): Mute the contribution of nuclear charges when computing the second order derivatives of energy. ''' mol = pcmobj.mol natm = mol.natm lmax = pcmobj.lmax nlm = (lmax + 1)**2 dms = numpy.asarray(dm) is_single_dm = dms.ndim == 2 grids = pcmobj.grids ni = numint.NumInt() max_memory = pcmobj.max_memory - lib.current_memory()[0] make_rho, n_dm, nao = ni._gen_rho_evaluator(mol, dms) dms = dms.reshape(n_dm, nao, nao) Xvec = Xvec.reshape(n_dm, natm, nlm) i1 = 0 scaled_weights = numpy.empty((n_dm, grids.weights.size)) for ia in range(natm): fak_pol, leak_idx = cached_pol[mol.atom_symbol(ia)] fac_pol = _vstack_factor_fak_pol(fak_pol, lmax) i0, i1 = i1, i1 + fac_pol.shape[1] scaled_weights[:, i0:i1] = numpy.einsum('mn,im->in', fac_pol, Xvec[:, ia]) scaled_weights *= grids.weights shls_slice = (0, mol.nbas) ao_loc = mol.ao_loc_nr() den = numpy.empty((n_dm, grids.weights.size)) vmat = numpy.zeros((n_dm, nao, nao)) p1 = 0 aow = None for ao, mask, weight, coords \ in ni.block_loop(mol, grids, nao, 0, max_memory): p0, p1 = p1, p1 + weight.size for i in range(n_dm): den[i, p0:p1] = make_rho(i, ao, mask, 'LDA') aow = numint._scale_ao(ao, scaled_weights[i, p0:p1], out=aow) vmat[i] -= numint._dot_ao_ao(mol, ao, aow, mask, shls_slice, ao_loc) den *= grids.weights ao = aow = scaled_weights = None nelec_leak = 0 psi = numpy.zeros((n_dm, natm, nlm)) i1 = 0 for ia in range(natm): fak_pol, leak_idx = cached_pol[mol.atom_symbol(ia)] fac_pol = _vstack_factor_fak_pol(fak_pol, lmax) i0, i1 = i1, i1 + fac_pol.shape[1] nelec_leak += den[:, i0:i1][:, leak_idx].sum(axis=1) psi[:, ia] = -numpy.einsum('in,mn->im', den[:, i0:i1], fac_pol) logger.debug(pcmobj, 'electron leaks %s', nelec_leak) # Contribution of nuclear charges to the total density # The factor numpy.sqrt(4*numpy.pi) is due to the product of 4*pi * Y_0^0 if with_nuc: for ia in range(natm): psi[:, ia, 0] += numpy.sqrt( 4 * numpy.pi) / r_vdw[ia] * mol.atom_charge(ia) # <Psi, L^{-1}g> -> Psi = SL the adjoint equation to LX = g L_S = numpy.linalg.solve( L.reshape(natm * nlm, -1).T, psi.reshape(n_dm, -1).T) L_S = L_S.reshape(natm, nlm, n_dm).transpose(2, 0, 1) coords_1sph, weights_1sph = make_grids_one_sphere(pcmobj.lebedev_order) # JCP, 141, 184108, Eq (39) xi_jn = numpy.einsum('n,jn,xn,ijx->ijn', weights_1sph, ui, ylm_1sph, L_S) extern_point_idx = ui > 0 cav_coords = (mol.atom_coords().reshape(natm, 1, 3) + numpy.einsum('r,gx->rgx', r_vdw, coords_1sph)) cav_coords = cav_coords[extern_point_idx] xi_jn = xi_jn[:, extern_point_idx] max_memory = pcmobj.max_memory - lib.current_memory()[0] blksize = int(max(max_memory * .9e6 / 8 / nao**2, 400)) cintopt = gto.moleintor.make_cintopt(mol._atm, mol._bas, mol._env, 'int3c2e') vmat_tril = 0 for i0, i1 in lib.prange(0, cav_coords.shape[0], blksize): fakemol = gto.fakemol_for_charges(cav_coords[i0:i1]) v_nj = df.incore.aux_e2(mol, fakemol, intor='int3c2e', aosym='s2ij', cintopt=cintopt) vmat_tril += numpy.einsum('xn,in->ix', v_nj, xi_jn[:, i0:i1]) vmat += lib.unpack_tril(vmat_tril) if is_single_dm: psi = psi[0] L_S = L_S[0] vmat = vmat[0] return psi, vmat, L_S
mf._numint.libxc = dft.xcfun # PySCF-1.6.1 and newer supports the .TDDFT method to create a TDDFT # object after importing tdscf module. td = mf.TDDFT() print(td.kernel()[0] * 27.2114) # # Overwriting the relevant attributes of the ground state mf object, # the TDDFT calculations can be run with different XC, grids. # mf.xc = 'lda,vwn' mf.grids.set(level=2).kernel(with_non0tab=True) td = mf.TDDFT() print(td.kernel()[0] * 27.2114) # # Overwriting the ground state SCF object is unsafe. A better solution is to # create a new fake SCF object to hold different XC, grids parameters. # from pyscf.dft import numint mf = dft.RKS(mol).run(xc='pbe0') mf1 = copy.copy(mf) mf1.xc = 'lda,vwn' mf1.grids = dft.Grids(mol) mf1.grids.level = 2 mf1._numint = numint.NumInt() mf1._numint.libxc = dft.xcfun td = mf1.TDDFT() print(td.kernel()[0] * 27.2114)