def _nao_sub(mol, pre_occ, pre_nao, s=None): if s is None: s = mol.intor_symmetric('cint1e_ovlp_sph') core_lst, val_lst, rydbg_lst = _core_val_ryd_list(mol) nbf = mol.nao_nr() cnao = numpy.empty((nbf,nbf)) if core_lst: c = pre_nao[:,core_lst] s1 = reduce(numpy.dot, (c.T, s, c)) cnao[:,core_lst] = c1 = numpy.dot(c, orth.lowdin(s1)) c = pre_nao[:,val_lst] c -= reduce(numpy.dot, (c1, c1.T, s, c)) else: c = pre_nao[:,val_lst] s1 = reduce(numpy.dot, (c.T, s, c)) wt = pre_occ[val_lst] cnao[:,val_lst] = numpy.dot(c, orth.weight_orth(s1, wt)) if rydbg_lst: cvlst = core_lst + val_lst c1 = cnao[:,cvlst] c = pre_nao[:,rydbg_lst] c -= reduce(numpy.dot, (c1, c1.T, s, c)) s1 = reduce(numpy.dot, (c.T, s, c)) cnao[:,rydbg_lst] = numpy.dot(c, orth.lowdin(s1)) snorm = numpy.linalg.norm(reduce(numpy.dot, (cnao.T, s, cnao)) - numpy.eye(nbf)) if snorm > 1e-9: logger.warn(mol, 'Weak orthogonality for localized orbitals %s', snorm) return cnao
def _get_basis_type(mol): def classify(mol_basis): basis_type = 'other' if isinstance(mol_basis, (str, unicode)): mol_basis = gto.basis._format_basis_name(mol_basis) if mol_basis[:6] == 'def2tz': basis_type = 'def2-TZ' elif mol_basis[:6] == 'def2sv': basis_type = 'sv' elif mol_basis[:5] == '631g*': basis_type = '6-31gd' elif mol_basis[:4] == '631g' and 'd' in mol_basis: basis_type = '6-31gd' return basis_type if isinstance(mol.basis, dict): basis_types = [classify(b) for b in mol.basis.values()] basis_type = 'other' for bt in basis_types: if bt != 'other': basis_type = bt break if (len(basis_types) > 1 and all(b == basis_type for b in basis_types)): logger.warn('Mutliple types of basis found in mol.basis. ' 'Type %s is applied\n' % basis_type) else: basis_type = classify(mol.basis) return basis_type
def get_occ(mf, mo_energy=None, mo_coeff=None): '''Label the occupancies for each orbital. NOTE the occupancies are not assigned based on the orbital energy ordering. The first N orbitals are assigned to be occupied orbitals. Examples: >>> mol = gto.M(atom='H 0 0 0; O 0 0 1.1', spin=1) >>> mf = scf.hf.SCF(mol) >>> energy = numpy.array([-10., -1., 1, -2., 0, -3]) >>> mf.get_occ(energy) array([2, 2, 2, 2, 1, 0]) ''' if mo_energy is None: mo_energy = mf.mo_energy if getattr(mo_energy, 'mo_ea', None) is not None: mo_ea = mo_energy.mo_ea mo_eb = mo_energy.mo_eb else: mo_ea = mo_eb = mo_energy nmo = mo_ea.size mo_occ = numpy.zeros(nmo) if getattr(mf, 'nelec', None) is None: nelec = mf.mol.nelec else: nelec = mf.nelec ncore = nelec[1] nocc = nelec[0] nopen = abs(nocc - ncore) mo_occ = _fill_rohf_occ(mo_energy, mo_ea, mo_eb, ncore, nopen) if mf.verbose >= logger.INFO and nocc < nmo and ncore > 0: ehomo = max(mo_energy[mo_occ> 0]) elumo = min(mo_energy[mo_occ==0]) if ehomo+1e-3 > elumo: logger.warn(mf, 'H**O %.15g >= LUMO %.15g', ehomo, elumo) else: logger.info(mf, ' H**O = %.15g LUMO = %.15g', ehomo, elumo) if nopen > 0 and mf.verbose >= logger.DEBUG: core_idx = mo_occ == 2 open_idx = mo_occ == 1 vir_idx = mo_occ == 0 logger.debug(mf, ' Roothaan | alpha | beta') logger.debug(mf, ' Highest 2-occ = %18.15g | %18.15g | %18.15g', max(mo_energy[core_idx]), max(mo_ea[core_idx]), max(mo_eb[core_idx])) logger.debug(mf, ' Lowest 0-occ = %18.15g | %18.15g | %18.15g', min(mo_energy[vir_idx]), min(mo_ea[vir_idx]), min(mo_eb[vir_idx])) for i in numpy.where(open_idx)[0]: logger.debug(mf, ' 1-occ = %18.15g | %18.15g | %18.15g', mo_energy[i], mo_ea[i], mo_eb[i]) if mf.verbose >= logger.DEBUG: numpy.set_printoptions(threshold=nmo) logger.debug(mf, ' Roothaan mo_energy =\n%s', mo_energy) logger.debug1(mf, ' alpha mo_energy =\n%s', mo_ea) logger.debug1(mf, ' beta mo_energy =\n%s', mo_eb) numpy.set_printoptions(threshold=1000) return mo_occ
def label_orb_symm(mol, irrep_name, symm_orb, mo, s=None, check=True): '''Label the symmetry of given orbitals irrep_name can be either the symbol or the ID of the irreducible representation. If the ID is provided, it returns the numeric code associated with XOR operator, see :py:meth:`symm.param.IRREP_ID_TABLE` Args: mol : an instance of :class:`Mole` irrep_name : list of str or int A list of irrep ID or name, it can be either mol.irrep_id or mol.irrep_name. It can affect the return "label". symm_orb : list of 2d array the symmetry adapted basis mo : 2d array the orbitals to label Returns: list of symbols or integers to represent the irreps for the given orbitals Examples: >>> from pyscf import gto, scf, symm >>> mol = gto.M(atom='H 0 0 0; H 0 0 1', basis='ccpvdz',verbose=0, symmetry=1) >>> mf = scf.RHF(mol) >>> mf.kernel() >>> symm.label_orb_symm(mol, mol.irrep_name, mol.symm_orb, mf.mo_coeff) ['Ag', 'B1u', 'Ag', 'B1u', 'B2u', 'B3u', 'Ag', 'B2g', 'B3g', 'B1u'] >>> symm.label_orb_symm(mol, mol.irrep_id, mol.symm_orb, mf.mo_coeff) [0, 5, 0, 5, 6, 7, 0, 2, 3, 5] ''' nmo = mo.shape[1] if s is None: s = mol.intor_symmetric('cint1e_ovlp_sph') mo_s = numpy.dot(mo.T, s) norm = numpy.empty((len(irrep_name), nmo)) for i,ir in enumerate(irrep_name): moso = numpy.dot(mo_s, symm_orb[i]) norm[i] = numpy.einsum('ij,ij->i', moso, moso) iridx = numpy.argmax(norm, axis=0) orbsym = [irrep_name[i] for i in iridx] logger.debug(mol, 'irreps of each MO %s', str(orbsym)) if check: norm[iridx,numpy.arange(nmo)] = 0 orbidx = numpy.where(norm > THRESHOLD) if orbidx[1].size > 0: idx = numpy.where(norm > THRESHOLD*1e2) if idx[1].size > 0: logger.error(mol, 'orbitals %s not symmetrized, norm = %s', idx[1], norm[idx]) raise ValueError('orbitals %s not symmetrized' % idx[1]) else: logger.warn(mol, 'orbitals %s not strictly symmetrized.', orbidx[1]) logger.warn(mol, 'They can be symmetrized with ' 'pyscf.symm.symmetrize_orb function.') logger.debug(mol, 'norm = %s', norm[orbidx]) return orbsym
def _common_init_(self, mycc, mo_coeff=None): if mo_coeff is None: mo_coeff = mycc.mo_coeff mo_idx = ccsd.get_frozen_mask(mycc) if getattr(mo_coeff, 'orbspin', None) is not None: self.orbspin = mo_coeff.orbspin[mo_idx] mo_coeff = lib.tag_array(mo_coeff[:,mo_idx], orbspin=self.orbspin) self.mo_coeff = mo_coeff else: orbspin = scf.ghf.guess_orbspin(mo_coeff) self.mo_coeff = mo_coeff = mo_coeff[:,mo_idx] if not np.any(orbspin == -1): self.orbspin = orbspin[mo_idx] self.mo_coeff = lib.tag_array(mo_coeff, orbspin=self.orbspin) # Note: Recomputed fock matrix since SCF may not be fully converged. dm = mycc._scf.make_rdm1(mycc.mo_coeff, mycc.mo_occ) fockao = mycc._scf.get_fock(dm=dm) self.fock = reduce(np.dot, (mo_coeff.conj().T, fockao, mo_coeff)) self.nocc = mycc.nocc mo_e = self.mo_energy = self.fock.diagonal().real gap = abs(mo_e[:self.nocc,None] - mo_e[None,self.nocc:]).min() if gap < 1e-5: logger.warn(mycc, 'H**O-LUMO gap %s too small for GCCSD', gap) return self
def makov_payne_correction(mf): '''Makov-Payne correction (Phys. Rev. B, 51, 4014) ''' cell = mf.cell logger.note(mf, 'Makov-Payne correction for charged 3D PBC systems') # PRB 51 (1995), 4014 # PRB 77 (2008), 115139 if cell.dimension != 3: logger.warn(mf, 'Correction for low-dimension PBC systems' 'is not available.') return 0 de_mono, de_dip, de_quad, de = _dip_correction(mf) if mf.verbose >= logger.NOTE: write = mf.stdout.write write('Corrections (AU)\n') write(' Monopole Dipole Quadrupole total\n') write('SC %12.8f %12.8f %12.8f %12.8f\n' % (de_mono[0], de_dip , de_quad , de[0])) write('BCC %12.8f %12.8f %12.8f %12.8f\n' % (de_mono[1], de_dip , de_quad , de[1])) write('FCC %12.8f %12.8f %12.8f %12.8f\n' % (de_mono[2], de_dip , de_quad , de[2])) return de
def get_occ(self, mo_energy=None, mo_coeff=None): '''Label the occupancies for each orbital. NOTE the occupancies are not assigned based on the orbital energy ordering. The first N orbitals are assigned to be occupied orbitals. Examples: >>> mol = gto.M(atom='H 0 0 0; O 0 0 1.1', spin=1) >>> mf = scf.hf.SCF(mol) >>> energy = numpy.array([-10., -1., 1, -2., 0, -3]) >>> mf.get_occ(energy) array([2, 2, 2, 2, 1, 0]) ''' if mo_energy is None: mo_energy = self.mo_energy mo_occ = numpy.zeros_like(mo_energy) ncore = self.nelec[1] nopen = self.nelec[0] - ncore nocc = ncore + nopen mo_occ[:ncore] = 2 mo_occ[ncore:nocc] = 1 if nocc < len(mo_energy): logger.info(self, 'H**O = %.12g LUMO = %.12g', mo_energy[nocc-1], mo_energy[nocc]) if mo_energy[nocc-1]+1e-3 > mo_energy[nocc]: logger.warn(self.mol, '!! H**O %.12g == LUMO %.12g', mo_energy[nocc-1], mo_energy[nocc]) else: logger.info(self, 'H**O = %.12g no LUMO', mo_energy[nocc-1]) if nopen > 0: for i in range(ncore, nocc): logger.debug(self, 'singly occupied orbital energy = %.12g', mo_energy[i]) logger.debug(self, ' mo_energy = %s', mo_energy) return mo_occ
def build(self, mol=None): if mol is None: mol = self.mol if mol.symmetry: for irname in self.irrep_nelec: if irname not in mol.irrep_name: logger.warn(self, 'Molecule does not have irrep %s', irname) nelec_fix = self.irrep_nelec.values() if any(isinstance(x, (tuple, list)) for x in nelec_fix): msg =('Number of alpha/beta electrons cannot be assigned ' 'separately in GHF. irrep_nelec = %s' % self.irrep_nelec) raise ValueError(msg) nelec_fix = sum(nelec_fix) float_irname = set(mol.irrep_name) - set(self.irrep_nelec) if nelec_fix > mol.nelectron: msg =('More electrons defined by irrep_nelec than total num electrons. ' 'mol.nelectron = %d irrep_nelec = %s' % (mol.nelectron, self.irrep_nelec)) raise ValueError(msg) else: logger.info(mol, 'Freeze %d electrons in irreps %s', nelec_fix, self.irrep_nelec.keys()) if len(float_irname) == 0 and nelec_fix != mol.nelectron: msg =('Num electrons defined by irrep_nelec != total num electrons. ' 'mol.nelectron = %d irrep_nelec = %s' % (mol.nelectron, self.irrep_nelec)) raise ValueError(msg) else: logger.info(mol, ' %d free electrons in irreps %s', mol.nelectron-nelec_fix, ' '.join(float_irname)) return ghf.GHF.build(self, mol)
def get_occ(self, mo_energy=None, mo_coeff=None): '''Label the occupancies for each orbital Kwargs: mo_energy : 1D ndarray Obital energies mo_coeff : 2D ndarray Obital coefficients Examples: >>> from pyscf import gto, scf >>> mol = gto.M(atom='H 0 0 0; F 0 0 1.1') >>> mf = scf.hf.SCF(mol) >>> mf.get_occ(numpy.arange(mol.nao_nr())) array([2, 2, 2, 2, 2, 0]) ''' if mo_energy is None: mo_energy = self.mo_energy mo_occ = numpy.zeros_like(mo_energy) nocc = self.mol.nelectron // 2 mo_occ[:nocc] = 2 if nocc < mo_occ.size: logger.info(self, 'H**O = %.12g, LUMO = %.12g,', mo_energy[nocc-1], mo_energy[nocc]) if mo_energy[nocc-1]+1e-3 > mo_energy[nocc]: logger.warn(self, '!! H**O %.12g == LUMO %.12g', mo_energy[nocc-1], mo_energy[nocc]) else: logger.info(self, 'H**O = %.12g,', mo_energy[nocc-1]) if self.verbose >= logger.DEBUG: numpy.set_printoptions(threshold=len(mo_energy)) logger.debug(self, ' mo_energy = %s', mo_energy) numpy.set_printoptions() return mo_occ
def get_init_guess(self, mol=None, key='minao'): if mol is None: mol = self.mol if callable(key): dm = key(mol) elif key.lower() == '1e': dm = self.init_guess_by_1e(mol) elif getattr(mol, 'natm', 0) == 0: logger.info(self, 'No atom found in mol. Use 1e initial guess') dm = self.init_guess_by_1e(mol) elif key.lower() == 'atom': dm = self.init_guess_by_atom(mol) elif key.lower() == 'chkfile': try: dm = self.init_guess_by_chkfile() except (IOError, KeyError): logger.warn(self, 'Fail in reading %s. Use MINAO initial guess', self.chkfile) dm = self.init_guess_by_minao(mol) else: dm = self.init_guess_by_minao(mol) if self.verbose >= logger.DEBUG1: logger.debug1(self, 'Nelec from initial guess = %g', (dm*self.get_ovlp()).sum().real) return dm
def get_occ(mo_energy_kpts=None, mo_coeff=None): if mo_energy_kpts is None: mo_energy_kpts = mf.mo_energy if nelec is None: cell_nelec = mf.cell.nelec else: cell_nelec = nelec h**o=[-1e8,-1e8] lumo=[1e8,1e8] mo_occ_kpts = [[], []] for s in [0,1]: for k, mo_energy in enumerate(mo_energy_kpts[s]): e_idx = numpy.argsort(mo_energy) e_sort = mo_energy[e_idx] n = cell_nelec[s] mo_occ = numpy.zeros_like(mo_energy) mo_occ[e_idx[:n]] = 1 h**o[s] = max(h**o[s], e_sort[n-1]) lumo[s] = min(lumo[s], e_sort[n]) mo_occ_kpts[s].append(mo_occ) for nm,s in zip(['alpha','beta'],[0,1]): logger.info(mf, nm+' H**O = %.12g LUMO = %.12g', h**o[s], lumo[s]) if h**o[s] > lumo[s]: logger.warn(mf, "WARNING! H**O is greater than LUMO! " "This may lead to incorrect canonical occupation.") return mo_occ_kpts
def orbital_coeff(mol, fout, mo_coeff, spin='Alpha', symm=None, ene=None, occ=None, ignore_h=False): from pyscf.symm import label_orb_symm if ignore_h: mol, mo_coeff = remove_high_l(mol, mo_coeff) aoidx = order_ao_index(mol) nmo = mo_coeff.shape[1] if symm is None: symm = ['A']*nmo if mol.symmetry: try: symm = label_orb_symm(mol, mol.irrep_name, mol.symm_orb, mo_coeff, tol=1e-5) except ValueError as e: logger.warn(mol, str(e)) if ene is None: ene = numpy.arange(nmo) assert(spin == 'Alpha' or spin == 'Beta') if occ is None: occ = numpy.zeros(nmo) neleca, nelecb = mol.nelec if spin == 'Alpha': occ[:neleca] = 1 else: occ[:nelecb] = 1 fout.write('[MO]\n') for imo in range(nmo): fout.write(' Sym= %s\n' % symm[imo]) fout.write(' Ene= %15.10g\n' % ene[imo]) fout.write(' Spin= %s\n' % spin) fout.write(' Occup= %10.5f\n' % occ[imo]) for i,j in enumerate(aoidx): fout.write(' %3d %18.14g\n' % (i+1, mo_coeff[j,imo]))
def _common_init_(self, mycc, mo_coeff=None): if mo_coeff is None: mo_coeff = mycc.mo_coeff mo_idx = mycc.get_frozen_mask() self.mo_coeff = mo_coeff = \ (mo_coeff[0][:,mo_idx[0]], mo_coeff[1][:,mo_idx[1]]) # Note: Recomputed fock matrix since SCF may not be fully converged. dm = mycc._scf.make_rdm1(mycc.mo_coeff, mycc.mo_occ) fockao = mycc._scf.get_fock(dm=dm) self.focka = reduce(np.dot, (mo_coeff[0].conj().T, fockao[0], mo_coeff[0])) self.fockb = reduce(np.dot, (mo_coeff[1].conj().T, fockao[1], mo_coeff[1])) self.fock = (self.focka, self.fockb) nocca, noccb = self.nocc = mycc.nocc self.mol = mycc.mol mo_ea = self.focka.diagonal().real mo_eb = self.fockb.diagonal().real self.mo_energy = (mo_ea, mo_eb) gap_a = abs(mo_ea[:nocca,None] - mo_ea[None,nocca:]) gap_b = abs(mo_eb[:noccb,None] - mo_eb[None,noccb:]) if gap_a.size > 0: gap_a = gap_a.min() else: gap_a = 1e9 if gap_b.size > 0: gap_b = gap_b.min() else: gap_b = 1e9 if gap_a < 1e-5 or gap_b < 1e-5: logger.warn(mycc, 'H**O-LUMO gap (%s,%s) too small for UCCSD', gap_a, gap_b) return self
def energy(cc, t1=None, t2=None, eris=None): '''UCCSD correlation energy''' if t1 is None: t1 = cc.t1 if t2 is None: t2 = cc.t2 if eris is None: eris = cc.ao2mo() t1a, t1b = t1 t2aa, t2ab, t2bb = t2 nocca, noccb, nvira, nvirb = t2ab.shape eris_ovov = np.asarray(eris.ovov) eris_OVOV = np.asarray(eris.OVOV) eris_ovOV = np.asarray(eris.ovOV) fova = eris.focka[:nocca,nocca:] fovb = eris.fockb[:noccb,noccb:] e = np.einsum('ia,ia', fova, t1a) e += np.einsum('ia,ia', fovb, t1b) e += 0.25*np.einsum('ijab,iajb',t2aa,eris_ovov) e -= 0.25*np.einsum('ijab,ibja',t2aa,eris_ovov) e += 0.25*np.einsum('ijab,iajb',t2bb,eris_OVOV) e -= 0.25*np.einsum('ijab,ibja',t2bb,eris_OVOV) e += np.einsum('iJaB,iaJB',t2ab,eris_ovOV) e += 0.5*np.einsum('ia,jb,iajb',t1a,t1a,eris_ovov) e -= 0.5*np.einsum('ia,jb,ibja',t1a,t1a,eris_ovov) e += 0.5*np.einsum('ia,jb,iajb',t1b,t1b,eris_OVOV) e -= 0.5*np.einsum('ia,jb,ibja',t1b,t1b,eris_OVOV) e += np.einsum('ia,jb,iajb',t1a,t1b,eris_ovOV) if abs(e.imag) > 1e-4: logger.warn(cc, 'Non-zero imaginary part found in UCCSD energy %s', e) return e.real
def get_fermi(mf, mo_energy_kpts=None, mo_occ_kpts=None): '''A pair of Fermi level for spin-up and spin-down orbitals ''' if mo_energy_kpts is None: mo_energy_kpts = mf.mo_energy if mo_occ_kpts is None: mo_occ_kpts = mf.mo_occ # mo_energy_kpts and mo_occ_kpts are k-point UHF quantities assert(mo_energy_kpts[0][0].ndim == 1) assert(mo_occ_kpts[0][0].ndim == 1) nocca = sum(mo_occ.sum() for mo_occ in mo_occ_kpts[0]) noccb = sum(mo_occ.sum() for mo_occ in mo_occ_kpts[1]) # nocc may not be perfect integer when smearing is enabled nocca = int(nocca.round(3)) noccb = int(noccb.round(3)) fermi_a = np.sort(np.hstack(mo_energy_kpts[0]))[nocca-1] fermi_b = np.sort(np.hstack(mo_energy_kpts[1]))[noccb-1] for k, mo_e in enumerate(mo_energy_kpts[0]): mo_occ = mo_occ_kpts[0][k] if mo_occ[mo_e > fermi_a].sum() > 0.5: logger.warn(mf, 'Alpha occupied band above Fermi level: \n' 'k=%d, mo_e=%s, mo_occ=%s', k, mo_e, mo_occ) for k, mo_e in enumerate(mo_energy_kpts[1]): mo_occ = mo_occ_kpts[1][k] if mo_occ[mo_e > fermi_b].sum() > 0.5: logger.warn(mf, 'Beta occupied band above Fermi level: \n' 'k=%d, mo_e=%s, mo_occ=%s', k, mo_e, mo_occ) return (fermi_a, fermi_b)
def dump_flags(self): rhf_mag.Magnetizability.dump_flags(self) if self.gauge_orig is not None: logger.warn(self, 'Rotational g-tensor with ' 'perturbation-independent basis is in testing.\n' 'Results do not fully agree with those in ' 'JCP, 105, 2804.') return self
def dump_flags(self, verbose=None): oldCAS.dump_flags(self) self.with_solvent.check_sanity() self.with_solvent.dump_flags() if self.conv_tol < 1e-7: logger.warn(self, 'CASSCF+ddCOSMO may not be able to ' 'converge to conv_tol=%g', self.conv_tol) return self
def shielding(self, mo1=None): if getattr(self._scf, 'spin_square', None): s2 = self._scf.spin_square()[0] if s2 > 1e-4: logger.warn(self, '<S^2> = %s. UHF-NMR shielding may have large error.\n' 'paramagnetic NMR should include this result plus ' 'g-tensor and HFC tensors.', s2) return rhf_nmr.NMR.shielding(self, mo1)
def check_sanity(self): mol_hf.SCF.check_sanity(self) self.with_df.check_sanity() if (isinstance(self.exxdiv, str) and self.exxdiv.lower() != 'ewald' and isinstance(self.with_df, df.df.DF)): logger.warn(self, 'exxdiv %s is not supported in DF or MDF', self.exxdiv) return self
def dump_flags(self): if hasattr(self, "nelectron_alpha"): logger.warn(self, "Note the API updates: attribute nelectron_alpha was replaced by attribute nelec") # raise RuntimeError('API updates') self.nelec = (self.nelectron_alpha, self.mol.nelectron - self.nelectron_alpha) delattr(self, "nelectron_alpha") hf.SCF.dump_flags(self) logger.info(self, "number electrons alpha = %d beta = %d", *self.nelec)
def gen_atomic_grids(mol, atom_grid={}, radi_method=radi.gauss_chebyshev, level=3, prune=nwchem_prune, **kwargs): '''Generate number of radial grids and angular grids for the given molecule. Returns: A dict, with the atom symbol for the dict key. For each atom type, the dict value has two items: one is the meshgrid coordinates wrt the atom center; the second is the volume of that grid. ''' if isinstance(atom_grid, (list, tuple)): atom_grid = dict([(mol.atom_symbol(ia), atom_grid) for ia in range(mol.natm)]) atom_grids_tab = {} for ia in range(mol.natm): symb = mol.atom_symbol(ia) if symb not in atom_grids_tab: chg = gto.charge(symb) if symb in atom_grid: n_rad, n_ang = atom_grid[symb] if n_ang not in LEBEDEV_NGRID: if n_ang in LEBEDEV_ORDER: logger.warn(mol, 'n_ang %d for atom %d %s is not ' 'the supported Lebedev angular grids. ' 'Set n_ang to %d', n_ang, ia, symb, LEBEDEV_ORDER[n_ang]) n_ang = LEBEDEV_ORDER[n_ang] else: raise ValueError('Unsupported angular grids %d' % n_ang) else: n_rad = _default_rad(chg, level) n_ang = _default_ang(chg, level) rad, dr = radi_method(n_rad, chg, ia, **kwargs) rad_weight = 4*numpy.pi * rad**2 * dr if callable(prune): angs = prune(chg, rad, n_ang) else: angs = [n_ang] * n_rad logger.debug(mol, 'atom %s rad-grids = %d, ang-grids = %s', symb, n_rad, angs) angs = numpy.array(angs) coords = [] vol = [] for n in sorted(set(angs)): grid = numpy.empty((n,4)) libdft.MakeAngularGrid(grid.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(n)) idx = numpy.where(angs==n)[0] for i0, i1 in prange(0, len(idx), 12): # 12 radi-grids as a group coords.append(numpy.einsum('i,jk->jik',rad[idx[i0:i1]], grid[:,:3]).reshape(-1,3)) vol.append(numpy.einsum('i,j->ji', rad_weight[idx[i0:i1]], grid[:,3]).ravel()) atom_grids_tab[symb] = (numpy.vstack(coords), numpy.hstack(vol)) return atom_grids_tab
def check_irrep_nelec(mol, irrep_nelec, nelec): for irname in irrep_nelec.keys(): if irname not in mol.irrep_name: logger.warn(mol, 'Molecule does not have irrep %s', irname) float_irname = [] fix_na = 0 fix_nb = 0 for i, irname in enumerate(mol.irrep_name): if irname in irrep_nelec: if isinstance(irrep_nelec[irname], (int, numpy.integer)): nelecb = irrep_nelec[irname] // 2 neleca = irrep_nelec[irname] - nelecb else: neleca, nelecb = irrep_nelec[irname] norb = mol.symm_orb[i].shape[1] if neleca > norb or nelecb > norb: msg =('More electrons than orbitals for irrep %s ' 'nelec = %d + %d, norb = %d' % (irname, neleca,nelecb, norb)) raise ValueError(msg) fix_na += neleca fix_nb += nelecb else: float_irname.append(irname) if fix_na < fix_nb: raise ValueError('alpha electrons %d < beta electrons %d\n' 'irrep_nelec %s' % (fix_na, fix_nb, irrep_nelec)) if mol.spin < fix_na-fix_nb: raise ValueError('alpha electrons %d - beta electrons %d > mol.spin %d\n' 'irrep_nelec %s' % (fix_na, fix_nb, mol.spin, irrep_nelec)) if isinstance(nelec, (int, numpy.integer)): nelecb = nelec // 2 neleca = nelec - nelecb else: neleca, nelecb = nelec fix_ne = fix_na + fix_nb if ((fix_na > neleca) or (fix_nb > nelecb) or (fix_na+nelecb > mol.nelectron) or (fix_nb+neleca > mol.nelectron)): msg =('More electrons defined by irrep_nelec than total num electrons. ' 'mol.nelectron = %d irrep_nelec = %s' % (mol.nelectron, irrep_nelec)) raise ValueError(msg) else: logger.info(mol, 'fix %d electrons in irreps %s', fix_ne, irrep_nelec.items()) if len(set(float_irname)) == 0 and fix_ne != mol.nelectron: msg =('Num electrons defined by irrep_nelec != total num electrons. ' 'mol.nelectron = %d irrep_nelec = %s' % (mol.nelectron, irrep_nelec)) raise ValueError(msg) else: logger.info(mol, ' %d free electrons in irreps %s', mol.nelectron-fix_ne, ' '.join(float_irname)) return fix_na, fix_nb, float_irname
def init_guess_by_chkfile(mol, chkfile_name, project=None): '''Read SCF chkfile and make the density matrix for UHF initial guess. Kwargs: project : None or bool Whether to project chkfile's orbitals to the new basis. Note when the geometry of the chkfile and the given molecule are very different, this projection can produce very poor initial guess. In PES scanning, it is recommended to swith off project. If project is set to None, the projection is only applied when the basis sets of the chkfile's molecule are different to the basis sets of the given molecule (regardless whether the geometry of the two molecules are different). Note the basis sets are considered to be different if the two molecules are derived from the same molecule with different ordering of atoms. ''' from pyscf.scf import addons chk_mol, scf_rec = chkfile.load_scf(chkfile_name) if project is None: project = not gto.same_basis_set(chk_mol, mol) # Check whether the two molecules are similar im1 = scipy.linalg.eigvalsh(mol.inertia_moment()) im2 = scipy.linalg.eigvalsh(chk_mol.inertia_moment()) # im1+1e-7 to avoid 'divide by zero' error if abs((im1-im2)/(im1+1e-7)).max() > 0.01: logger.warn(mol, "Large deviations found between the input " "molecule and the molecule from chkfile\n" "Initial guess density matrix may have large error.") if project: s = hf.get_ovlp(mol) def fproj(mo): if project: mo = addons.project_mo_nr2nr(chk_mol, mo, mol) norm = numpy.einsum('pi,pi->i', mo.conj(), s.dot(mo)) mo /= numpy.sqrt(norm) return mo mo = scf_rec['mo_coeff'] mo_occ = scf_rec['mo_occ'] if getattr(mo[0], 'ndim', None) == 1: # RHF if numpy.iscomplexobj(mo): raise NotImplementedError('TODO: project DHF orbital to UHF orbital') mo_coeff = fproj(mo) mo_occa = (mo_occ>1e-8).astype(numpy.double) mo_occb = mo_occ - mo_occa dm = make_rdm1([mo_coeff,mo_coeff], [mo_occa,mo_occb]) else: #UHF if getattr(mo[0][0], 'ndim', None) == 2: # KUHF logger.warn(mol, 'k-point UHF results are found. Density matrix ' 'at Gamma point is used for the molecular SCF initial guess') mo = mo[0] dm = make_rdm1([fproj(mo[0]),fproj(mo[1])], mo_occ) return dm
def build(self, mol=None): if mol is None: mol = self.mol if mol.symmetry: for irname in self.irrep_nelec: if irname not in self.mol.irrep_name: logger.warn(self, 'No irrep %s', irname) hf_symm.check_irrep_nelec(mol, self.irrep_nelec, self.nelec) return uhf.UHF.build(self, mol)
def DFCASCI(mf, ncas, nelecas, auxbasis='weigend', **kwargs): if not hasattr(mf, '_tag_df') or not mf._tag_df: from pyscf.lib import logger logger.warn(mf, 'DFCASCI: the first argument%s is not density-fitting SCF object. ' 'Only orbital hessian are computed with density-fitting integrals. ' 'JK matrix and 2e MO integrals are computed with exact 2e integrals.', mf.__class__) mc = CASCI(mf, ncas, nelecas, **kwargs) return density_fit(mc, auxbasis)
def build_(self, mol=None): # specify alpha,beta for same irreps na = sum([x[0] for x in self.irrep_nelec.values()]) nb = sum([x[1] for x in self.irrep_nelec.values()]) nopen = self.mol.spin assert(na >= nb and nopen >= na-nb) for irname in self.irrep_nelec.keys(): if irname not in self.mol.irrep_name: logger.warn(self, '!! No irrep %s', irname) return hf.RHF.build_(self, mol)
def energy(cc, t1, t2, eris): nocc, nvir = t1.shape fock = eris.fock e = einsum('ia,ia', fock[:nocc,nocc:], t1) eris_oovv = np.array(eris.oovv) e += 0.25*np.einsum('ijab,ijab', t2, eris_oovv) e += 0.5 *np.einsum('ia,jb,ijab', t1, t1, eris_oovv) if abs(e.imag) > 1e-4: logger.warn(cc, 'Non-zero imaginary part found in GCCSD energy %s', e) return e.real
def dump_flags(self): hf.SCF.dump_flags(self) if hasattr(self, 'nelectron_alpha'): logger.warn(self, 'Note the API updates: attribute nelectron_alpha was replaced by attribute nelec') #raise RuntimeError('API updates') self.nelec = (self.nelectron_alpha, self.mol.nelectron-self.nelectron_alpha) delattr(self, 'nelectron_alpha') logger.info(self, 'num. doubly occ = %d num. singly occ = %d', self.nelec[1], self.nelec[0]-self.nelec[1])
def mo_comps(aolabels_or_baslst, mol, mo_coeff, cart=False, orth_method=ORTH_METHOD): '''Given AO(s), show how the AO(s) are distributed in MOs. Args: aolabels_or_baslst : filter function or AO labels or AO index If it's a function, the AO indices are the items for which the function return value is true. Kwargs: cart : bool whether the orbital coefficients are based on cartesian basis. orth_method : str The localization method to generated orthogonal AO upon which the AO contribution are computed. It can be one of 'meta_lowdin', 'lowdin' or 'nao'. Returns: A list of float to indicate the total contributions (normalized to 1) of localized AOs Examples: >>> from pyscf import gto, scf >>> from pyscf.tools import mo_mapping >>> mol = gto.M(atom='H 0 0 0; F 0 0 1', basis='6-31g') >>> mf = scf.RHF(mol).run() >>> comp = mo_mapping.mo_comps('F 2s', mol, mf.mo_coeff) >>> print('MO-id F-2s components') >>> for i,c in enumerate(comp): ... print('%-3d %.10f' % (i, c)) MO-id components 0 0.0000066344 1 0.8796915532 2 0.0590259826 3 0.0000000000 4 0.0000000000 5 0.0435028851 6 0.0155889103 7 0.0000000000 8 0.0000000000 9 0.0000822361 10 0.0021017982 ''' with lib.temporary_env(mol, cart=cart): assert(mo_coeff.shape[0] == mol.nao) s = mol.intor_symmetric('int1e_ovlp') lao = lo.orth.orth_ao(mol, orth_method, s=s) idx = gto.mole._aolabels2baslst(mol, aolabels_or_baslst) if len(idx) == 0: logger.warn(mol, 'Required orbitals are not found') mo1 = reduce(numpy.dot, (lao[:,idx].T, s, mo_coeff)) s1 = numpy.einsum('ki,ki->i', mo1, mo1) return s1
def _make_eris(mp, mo_coeff=None, ao2mofn=None, verbose=None): log = logger.new_logger(mp, verbose) time0 = (time.clock(), time.time()) eris = _ChemistsERIs(mp, mo_coeff) nocca, noccb = mp.get_nocc() nmoa, nmob = mp.get_nmo() nvira, nvirb = nmoa-nocca, nmob-noccb nao = eris.mo_coeff[0].shape[0] nmo_pair = nmoa * (nmoa+1) // 2 nao_pair = nao * (nao+1) // 2 mem_incore = (nao_pair**2 + nmo_pair**2) * 8/1e6 mem_now = lib.current_memory()[0] max_memory = max(0, mp.max_memory-mem_now) moa = eris.mo_coeff[0] mob = eris.mo_coeff[1] orboa = moa[:,:nocca] orbob = mob[:,:noccb] orbva = moa[:,nocca:] orbvb = mob[:,noccb:] if (mp.mol.incore_anyway or (mp._scf._eri is not None and mem_incore+mem_now < mp.max_memory)): log.debug('transform (ia|jb) incore') if callable(ao2mofn): eris.ovov = ao2mofn((orboa,orbva,orboa,orbva)).reshape(nocca*nvira,nocca*nvira) eris.ovOV = ao2mofn((orboa,orbva,orbob,orbvb)).reshape(nocca*nvira,noccb*nvirb) eris.OVOV = ao2mofn((orbob,orbvb,orbob,orbvb)).reshape(noccb*nvirb,noccb*nvirb) else: eris.ovov = ao2mo.general(mp._scf._eri, (orboa,orbva,orboa,orbva)) eris.ovOV = ao2mo.general(mp._scf._eri, (orboa,orbva,orbob,orbvb)) eris.OVOV = ao2mo.general(mp._scf._eri, (orbob,orbvb,orbob,orbvb)) elif getattr(mp._scf, 'with_df', None): logger.warn(mp, 'UMP2 detected DF being used in the HF object. ' 'MO integrals are computed based on the DF 3-index tensors.\n' 'It\'s recommended to use DF-UMP2 module.') log.debug('transform (ia|jb) with_df') eris.ovov = mp._scf.with_df.ao2mo((orboa,orbva,orboa,orbva)) eris.ovOV = mp._scf.with_df.ao2mo((orboa,orbva,orbob,orbvb)) eris.OVOV = mp._scf.with_df.ao2mo((orbob,orbvb,orbob,orbvb)) else: log.debug('transform (ia|jb) outcore') eris.feri = lib.H5TmpFile() _ao2mo_ovov(mp, (orboa,orbva,orbob,orbvb), eris.feri, max(2000, max_memory), log) eris.ovov = eris.feri['ovov'] eris.ovOV = eris.feri['ovOV'] eris.OVOV = eris.feri['OVOV'] time1 = log.timer('Integral transformation', *time0) return eris
def kernel(imds, orbs=None, linearized=False, eta=1e-3, tol=1e-9, method="fallback"): """ Calculates GW energies. Args: imds (AbstractIMDS): GW intermediates; orbs (Iterable): indexes of MO orbitals to correct; linearized (bool): whether to apply a single-step linearized correction to energies instead of iterative procedure; eta (float): imaginary energy for the Green's function; tol (float): tolerance for the search of zero; method (str): 'bisect' finds roots no matter what but, potentially, wrong ones, 'newton' finding roots close to the correct one but, potentially, failing during iterations, or 'fallback' using 'newton' and proceeding to 'bisect' in case of failure; Returns: Corrected orbital energies. """ if method not in ('newton', 'bisect', 'fallback'): raise ValueError("Cannot recognize method='{}'".format(method)) # Check implementation consistency _orbs = imds.entire_space if not isinstance(_orbs, list) or not len(_orbs) == imds.orb_dims: raise RuntimeError( "The object returned by 'imds.entire_space' is not a list of length {:d}: {}" .format( imds.orb_dims, repr(_orbs), )) # Assign default value if orbs is None: orbs = _orbs # Make sure it is a list if not isinstance(orbs, list): orbs = [orbs] # Add missing dimensions if len(orbs) < imds.orb_dims: orbs = _orbs[:-len(orbs)] + orbs shape = tuple(len(i) for i in orbs) gw_energies = numpy.zeros(shape, dtype=float) for i_p in product(*tuple(numpy.arange(i) for i in shape)): p = tuple(i[j] for i, j in zip(orbs, i_p)) if imds.orb_dims == 1: p = p[0] if linearized: raise NotImplementedError # v_mf = imds.vmf # vk = imds.vk # de = 1e-6 # ep = imds.e_mf[p] # # TODO: analytic sigma derivative # sigma = imds.get_sigma_element(ep, p, eta).real # dsigma = imds.get_sigma_element(ep + de, p, eta).real - sigma # zn = 1.0 / (1 - dsigma / de) # e = ep + zn * (sigma.real + vk[p] - v_mf[p]) # gw_energies[i_p] = e else: debug = LoggingFunction(imds.quasiparticle_eq(p, eta=eta)) if method == "newton": try: gw_energies[i_p] = newton(debug, imds.initial_guess(p), tol=tol, maxiter=100) except Exception as e: e.message = "When calculating root @p={} the following exception occurred:\n\n{}".format( repr(p), e.message, ) debug.plot_call_history("Exception during Newton " + str(p)) raise elif method == "bisect": gw_energies[i_p] = bisect(debug, -100, 100, xtol=tol, maxiter=100) elif method == "fallback": try: gw_energies[i_p] = newton(debug, imds.initial_guess(p), tol=tol, maxiter=100) except RuntimeError: logger.warn( imds.td._scf, "Failed to converge with newton, using bisect on the interval [{:.3e}, {:.3e}]" .format( min(debug.x), max(debug.x), )) gw_energies[i_p] = bisect(debug, min(debug.x), max(debug.x), xtol=tol, maxiter=100) return gw_energies
def check_sanity(self): cell = self.cell if cell.spin != 0 and len(self.kpts) % 2 != 0: logger.warn(self, 'Problematic nelec %s and number of k-points %d ' 'found in KRHF method.', cell.nelec, len(self.kpts)) return KSCF.check_sanity(self)
def kernel(mycc, eris, t1=None, t2=None, verbose=logger.NOTE): cpu1 = cpu0 = (logger.process_clock(), logger.perf_counter()) log = logger.new_logger(mycc, verbose) if t1 is None: t1 = mycc.t1 if t2 is None: t2 = mycc.t2 nocc, nvir = t1.shape nmo = nocc + nvir dtype = numpy.result_type(t1, t2, eris.ovoo.dtype) if mycc.incore_complete: ftmp = None eris_vvop = numpy.zeros((nvir, nvir, nocc, nmo), dtype) else: ftmp = lib.H5TmpFile() eris_vvop = ftmp.create_dataset('vvop', (nvir, nvir, nocc, nmo), dtype) orbsym = _sort_eri(mycc, eris, nocc, nvir, eris_vvop, log) mo_energy, t1T, t2T, vooo, fvo, restore_t2_inplace = \ _sort_t2_vooo_(mycc, orbsym, t1, t2, eris) cpu1 = log.timer_debug1('QCISD(T) sort_eri', *cpu1) cpu2 = list(cpu1) orbsym = numpy.hstack( (numpy.sort(orbsym[:nocc]), numpy.sort(orbsym[nocc:]))) o_ir_loc = numpy.append( 0, numpy.cumsum(numpy.bincount(orbsym[:nocc], minlength=8))) v_ir_loc = numpy.append( 0, numpy.cumsum(numpy.bincount(orbsym[nocc:], minlength=8))) o_sym = orbsym[:nocc] oo_sym = (o_sym[:, None] ^ o_sym).ravel() oo_ir_loc = numpy.append(0, numpy.cumsum(numpy.bincount(oo_sym, minlength=8))) nirrep = max(oo_sym) + 1 orbsym = orbsym.astype(numpy.int32) o_ir_loc = o_ir_loc.astype(numpy.int32) v_ir_loc = v_ir_loc.astype(numpy.int32) oo_ir_loc = oo_ir_loc.astype(numpy.int32) if dtype == numpy.complex: drv = _ccsd.libcc.QCIsd_t_zcontract else: drv = _ccsd.libcc.QCIsd_t_contract et_sum = numpy.zeros(1, dtype=dtype) def contract(a0, a1, b0, b1, cache): cache_row_a, cache_col_a, cache_row_b, cache_col_b = cache drv(et_sum.ctypes.data_as(ctypes.c_void_p), mo_energy.ctypes.data_as(ctypes.c_void_p), t1T.ctypes.data_as(ctypes.c_void_p), t2T.ctypes.data_as(ctypes.c_void_p), vooo.ctypes.data_as(ctypes.c_void_p), fvo.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(nocc), ctypes.c_int(nvir), ctypes.c_int(a0), ctypes.c_int(a1), ctypes.c_int(b0), ctypes.c_int(b1), ctypes.c_int(nirrep), o_ir_loc.ctypes.data_as(ctypes.c_void_p), v_ir_loc.ctypes.data_as(ctypes.c_void_p), oo_ir_loc.ctypes.data_as(ctypes.c_void_p), orbsym.ctypes.data_as(ctypes.c_void_p), cache_row_a.ctypes.data_as(ctypes.c_void_p), cache_col_a.ctypes.data_as(ctypes.c_void_p), cache_row_b.ctypes.data_as(ctypes.c_void_p), cache_col_b.ctypes.data_as(ctypes.c_void_p)) cpu2[:] = log.timer_debug1('contract %d:%d,%d:%d' % (a0, a1, b0, b1), *cpu2) # The rest 20% memory for cache b mem_now = lib.current_memory()[0] max_memory = max(0, mycc.max_memory - mem_now) bufsize = (max_memory * .5e6 / 8 - nocc**3 * 3 * lib.num_threads()) / ( nocc * nmo) #*.5 for async_io bufsize *= .5 #*.5 upper triangular part is loaded bufsize *= .8 #*.8 for [a0:a1]/[b0:b1] partition bufsize = max(8, bufsize) log.debug('max_memory %d MB (%d MB in use)', max_memory, mem_now) with lib.call_in_background(contract, sync=not mycc.async_io) as async_contract: for a0, a1 in reversed(list(lib.prange_tril(0, nvir, bufsize))): cache_row_a = numpy.asarray(eris_vvop[a0:a1, :a1], order='C') if a0 == 0: cache_col_a = cache_row_a else: cache_col_a = numpy.asarray(eris_vvop[:a0, a0:a1], order='C') async_contract( a0, a1, a0, a1, (cache_row_a, cache_col_a, cache_row_a, cache_col_a)) for b0, b1 in lib.prange_tril(0, a0, bufsize / 8): cache_row_b = numpy.asarray(eris_vvop[b0:b1, :b1], order='C') if b0 == 0: cache_col_b = cache_row_b else: cache_col_b = numpy.asarray(eris_vvop[:b0, b0:b1], order='C') async_contract( a0, a1, b0, b1, (cache_row_a, cache_col_a, cache_row_b, cache_col_b)) t2 = restore_t2_inplace(t2T) et_sum *= 2 if abs(et_sum[0].imag) > 1e-4: logger.warn(mycc, 'Non-zero imaginary part of QCISD(T) energy was found %s', et_sum[0]) et = et_sum[0].real log.timer('QCISD(T)', *cpu0) log.note('QCISD(T) correction = %.15g', et) return et
def fast_scf(mf): from pyscf.lib import logger logger.warn( mf, 'NOTE function fast_scf will be removed in the next release. ' 'Use function fast_newton instead') return fast_newton(mf)
def ipccsd_star_contract(eom, ipccsd_evals, ipccsd_evecs, lipccsd_evecs, imds=None): """ Returns: e_star (list of float): The IP-CCSD* energy. Notes: The user should check to make sure the right and left eigenvalues before running the perturbative correction. The 2hp right amplitudes are assumed to be of the form s^{a }_{ij}, i.e. the (ia) indices are coupled while the left are assumed to be of the form s^{ b}_{ij}, i.e. the (jb) indices are coupled. Reference: Saeh, Stanton "...energy surfaces of radicals" JCP 111, 8275 (1999); DOI:10.1063/1.480171 """ assert eom.partition is None if imds is None: imds = eom.make_imds() t1, t2 = imds.t1, imds.t2 eris = imds.eris assert (isinstance(eris, gccsd._PhysicistsERIs)) fock = eris.fock nocc, nvir = t1.shape nmo = nocc + nvir #fov = fock[:nocc, nocc:] foo = fock[:nocc, :nocc].diagonal() fvv = fock[nocc:, nocc:].diagonal() oovv = _cp(eris.oovv) ovvv = _cp(eris.ovvv) ovov = _cp(eris.ovov) #ovvo = -_cp(eris.ovov).transpose(0,1,3,2) ooov = _cp(eris.ooov) vooo = _cp(ooov).conj().transpose(3, 2, 1, 0) vvvo = _cp(ovvv).conj().transpose(3, 2, 1, 0) oooo = _cp(eris.oooo) # Create denominator eijk = foo[:, None, None] + foo[None, :, None] + foo[None, None, :] eab = fvv[:, None] + fvv[None, :] eijkab = eijk[:, :, :, None, None] - eab[None, None, None, :, :] # Permutation operators def pijk(tmp): '''P(ijk)''' return tmp + tmp.transpose(1, 2, 0, 3, 4) + tmp.transpose( 2, 0, 1, 3, 4) def pab(tmp): '''P(ab)''' return tmp - tmp.transpose(0, 1, 2, 4, 3) def pij(tmp): '''P(ij)''' return tmp - tmp.transpose(1, 0, 2, 3, 4) ipccsd_evecs = np.array(ipccsd_evecs) lipccsd_evecs = np.array(lipccsd_evecs) e_star = [] ipccsd_evecs, lipccsd_evecs = [ np.atleast_2d(x) for x in [ipccsd_evecs, lipccsd_evecs] ] ipccsd_evals = np.atleast_1d(ipccsd_evals) for ip_eval, ip_evec, ip_levec in zip(ipccsd_evals, ipccsd_evecs, lipccsd_evecs): # Enforcing <L|R> = 1 l1, l2 = vector_to_amplitudes_ip(ip_levec, nmo, nocc) r1, r2 = vector_to_amplitudes_ip(ip_evec, nmo, nocc) ldotr = np.dot(l1, r1) + 0.5 * np.dot(l2.ravel(), r2.ravel()) logger.info(eom, 'Left-right amplitude overlap : %14.8e', ldotr) if abs(ldotr) < 1e-7: logger.warn( eom, 'Small %s left-right amplitude overlap. Results ' 'may be inaccurate.', ldotr) l1 /= ldotr l2 /= ldotr # Denominator + eigenvalue(IP-CCSD) denom = eijkab + ip_eval denom = 1. / denom tmp = lib.einsum('ijab,k->ijkab', oovv, l1) lijkab = pijk(tmp) tmp = -lib.einsum('jima,mkb->ijkab', ooov, l2) tmp = pijk(tmp) lijkab += pab(tmp) tmp = lib.einsum('ieab,jke->ijkab', ovvv, l2) lijkab += pijk(tmp) tmp = lib.einsum('mbke,m->bke', ovov, r1) tmp = lib.einsum('bke,ijae->ijkab', tmp, t2) tmp = pijk(tmp) rijkab = -pab(tmp) tmp = lib.einsum('mnjk,n->mjk', oooo, r1) tmp = lib.einsum('mjk,imab->ijkab', tmp, t2) rijkab += pijk(tmp) tmp = lib.einsum('amij,mkb->ijkab', vooo, r2) tmp = pijk(tmp) rijkab -= pab(tmp) tmp = lib.einsum('baei,jke->ijkab', vvvo, r2) rijkab += pijk(tmp) deltaE = (1. / 12) * lib.einsum('ijkab,ijkab,ijkab', lijkab, rijkab, denom) deltaE = deltaE.real logger.info(eom, "Exc. energy, delta energy = %16.12f, %16.12f", ip_eval + deltaE, deltaE) e_star.append(ip_eval + deltaE) return e_star
def eval_ao(mol, coords, deriv=0, relativity=0, shls_slice=None, non0tab=None, out=None, verbose=None): '''Evaluate AO function value on the given grids. Args: mol : an instance of :class:`Mole` coords : 2D array, shape (N,3) The coordinates of the grids. Kwargs: deriv : int AO derivative order. It affects the shape of the return array. If deriv=0, the returned AO values are stored in a (N,nao) array. Otherwise the AO values are stored in an array of shape (M,N,nao). Here N is the number of grids, nao is the number of AO functions, M is the size associated to the derivative deriv. relativity : bool No effects. shls_slice : 2-element list (shl_start, shl_end). If given, only part of AOs (shl_start <= shell_id < shl_end) are evaluated. By default, all shells defined in mol will be evaluated. non0tab : 2D bool array mask array to indicate whether the AO values are zero. The mask array can be obtained by calling :func:`make_mask` out : ndarray If provided, results are written into this array. verbose : int or object of :class:`Logger` No effects. Returns: 2D array of shape (N,nao) for AO values if deriv = 0. Or 3D array of shape (:,N,nao) for AO values and AO derivatives if deriv > 0. In the 3D array, the first (N,nao) elements are the AO values, followed by (3,N,nao) for x,y,z compoents; Then 2nd derivatives (6,N,nao) for xx, xy, xz, yy, yz, zz; Then 3rd derivatives (10,N,nao) for xxx, xxy, xxz, xyy, xyz, xzz, yyy, yyz, yzz, zzz; ... Examples: >>> mol = gto.M(atom='O 0 0 0; H 0 0 1; H 0 1 0', basis='ccpvdz') >>> coords = numpy.random.random((100,3)) # 100 random points >>> ao_value = eval_ao(mol, coords) >>> print(ao_value.shape) (100, 24) >>> ao_value = eval_ao(mol, coords, deriv=1, shls_slice=(1,4)) >>> print(ao_value.shape) (4, 100, 7) >>> ao_value = eval_ao(mol, coords, deriv=2, shls_slice=(1,4)) >>> print(ao_value.shape) (10, 100, 7) ''' if isinstance(deriv, bool): logger.warn(mol, ''' You see this error message because of the API updates in pyscf v1.1. Argument "isgga" is replaced by argument "deriv", to support high order AO derivatives''') comp = (deriv+1)*(deriv+2)*(deriv+3)//6 feval = 'GTOval_sph_deriv%d' % deriv return mol.eval_gto(feval, coords, comp, shls_slice, non0tab, out)
def get_ab(mf, mo_energy=None, mo_coeff=None, mo_occ=None): r'''A and B matrices for TDDFT response function. A[i,a,j,b] = \delta_{ab}\delta_{ij}(E_a - E_i) + (ia||bj) B[i,a,j,b] = (ia||jb) ''' if mo_energy is None: mo_energy = mf.mo_energy if mo_coeff is None: mo_coeff = mf.mo_coeff if mo_occ is None: mo_occ = mf.mo_occ assert(mo_coeff.dtype == numpy.double) mol = mf.mol nao, nmo = mo_coeff.shape occidx = numpy.where(mo_occ==2)[0] viridx = numpy.where(mo_occ==0)[0] orbv = mo_coeff[:,viridx] orbo = mo_coeff[:,occidx] nvir = orbv.shape[1] nocc = orbo.shape[1] mo = numpy.hstack((orbo,orbv)) nmo = nocc + nvir e_ia = lib.direct_sum('a-i->ia', mo_energy[viridx], mo_energy[occidx]) a = numpy.diag(e_ia.ravel()).reshape(nocc,nvir,nocc,nvir) b = numpy.zeros_like(a) def add_hf_(a, b, hyb=1): eri_mo = ao2mo.general(mol, [orbo,mo,mo,mo], compact=False) eri_mo = eri_mo.reshape(nocc,nmo,nmo,nmo) a += numpy.einsum('iabj->iajb', eri_mo[:nocc,nocc:,nocc:,:nocc]) * 2 a -= numpy.einsum('ijba->iajb', eri_mo[:nocc,:nocc,nocc:,nocc:]) * hyb b += numpy.einsum('iajb->iajb', eri_mo[:nocc,nocc:,:nocc,nocc:]) * 2 b -= numpy.einsum('jaib->iajb', eri_mo[:nocc,nocc:,:nocc,nocc:]) * hyb if getattr(mf, 'xc', None) and getattr(mf, '_numint', None): from pyscf.dft import rks from pyscf.dft import numint ni = mf._numint ni.libxc.test_deriv_order(mf.xc, 2, raise_error=True) if getattr(mf, 'nlc', '') != '': logger.warn(mf, 'NLC functional found in DFT object. Its second ' 'deriviative is not available. Its contribution is ' 'not included in the response function.') omega, alpha, hyb = ni.rsh_and_hybrid_coeff(mf.xc, mol.spin) add_hf_(a, b, hyb) xctype = ni._xc_type(mf.xc) dm0 = mf.make_rdm1(mo_coeff, mo_occ) make_rho = ni._gen_rho_evaluator(mol, dm0, hermi=1)[0] mem_now = lib.current_memory()[0] max_memory = max(2000, mf.max_memory*.8-mem_now) if xctype == 'LDA': ao_deriv = 0 for ao, mask, weight, coords \ in ni.block_loop(mol, mf.grids, nao, ao_deriv, max_memory): rho = make_rho(0, ao, mask, 'LDA') fxc = ni.eval_xc(mf.xc, rho, 0, deriv=2)[2] frr = fxc[0] rho_o = lib.einsum('rp,pi->ri', ao, orbo) rho_v = lib.einsum('rp,pi->ri', ao, orbv) rho_ov = numpy.einsum('ri,ra->ria', rho_o, rho_v) w_ov = numpy.einsum('ria,r->ria', rho_ov, weight*frr) iajb = lib.einsum('ria,rjb->iajb', rho_ov, w_ov) * 2 a += iajb b += iajb elif xctype == 'GGA': ao_deriv = 1 for ao, mask, weight, coords \ in ni.block_loop(mol, mf.grids, nao, ao_deriv, max_memory): rho = make_rho(0, ao, mask, 'GGA') vxc, fxc = ni.eval_xc(mf.xc, rho, 0, deriv=2)[1:3] vgamma = vxc[1] frho, frhogamma, fgg = fxc[:3] rho_o = lib.einsum('xrp,pi->xri', ao, orbo) rho_v = lib.einsum('xrp,pi->xri', ao, orbv) rho_ov = numpy.einsum('xri,ra->xria', rho_o, rho_v[0]) rho_ov[1:4] += numpy.einsum('ri,xra->xria', rho_o[0], rho_v[1:4]) # sigma1 ~ \nabla(\rho_\alpha+\rho_\beta) dot \nabla(|b><j|) z_{bj} sigma1 = numpy.einsum('xr,xria->ria', rho[1:4], rho_ov[1:4]) w_ov = numpy.empty_like(rho_ov) w_ov[0] = numpy.einsum('r,ria->ria', frho, rho_ov[0]) w_ov[0] += numpy.einsum('r,ria->ria', 2*frhogamma, sigma1) f_ov = numpy.einsum('r,ria->ria', 4*fgg, sigma1) f_ov+= numpy.einsum('r,ria->ria', 2*frhogamma, rho_ov[0]) w_ov[1:] = numpy.einsum('ria,xr->xria', f_ov, rho[1:4]) w_ov[1:]+= numpy.einsum('r,xria->xria', 2*vgamma, rho_ov[1:4]) w_ov *= weight[:,None,None] iajb = lib.einsum('xria,xrjb->iajb', rho_ov, w_ov) * 2 a += iajb b += iajb elif xctype == 'NLC': raise NotImplementedError('NLC') elif xctype == 'MGGA': raise NotImplementedError('meta-GGA') else: add_hf_(a, b) return a, b
def _dip_correction(mf): '''Makov-Payne corrections for charged systems.''' from pyscf.pbc import gto from pyscf.pbc import tools from pyscf.pbc.dft import gen_grid log = logger.new_logger(mf) cell = mf.cell a = cell.lattice_vectors() b = np.linalg.inv(a).T grids = gen_grid.UniformGrids(cell) ke_cutoff = gto.estimate_ke_cutoff(cell, 1e-5) grids.mesh = tools.cutoff_to_mesh(a, ke_cutoff) dm = mf.make_rdm1() rho = mf.get_rho(dm, grids, mf.kpt) origin = _search_dipole_gauge_origin(cell, grids, rho, log) def shift_grids(r): r_frac = lib.dot(r - origin, b.T) # Grids on the boundary (r_frac == +/-0.5) of the new cell may lead to # unbalanced contributions to the dipole moment. Exclude them from the # dipole and quadrupole r_frac[r_frac == 0.5] = 0 r_frac[r_frac == -0.5] = 0 r_frac[r_frac > 0.5] -= 1 r_frac[r_frac < -0.5] += 1 r = lib.dot(r_frac, a) return r # SC BCC FCC madelung = (-2.83729747948, -3.63923344951, -4.58486207411) vol = cell.vol L = vol**(1. / 3) chg = cell.charge # epsilon is the dielectric constant of the system. For systems # surrounded by vacuum, epsilon = 1. epsilon = 1 # Energy correction of point charges of a simple cubic lattice. de_mono = -chg**2 * np.array(madelung) / (2 * L * epsilon) # dipole energy correction r_e = shift_grids(grids.coords) r_nuc = shift_grids(cell.atom_coords()) charges = cell.atom_charges() e_dip = np.einsum('g,g,gx->x', rho, grids.weights, r_e) nuc_dip = np.einsum('g,gx->x', charges, r_nuc) dip = nuc_dip - e_dip de_dip = -2. * np.pi / (3 * cell.vol) * np.linalg.norm(dip)**2 # quadrupole energy correction if abs(a - np.eye(3) * L).max() > 1e-5: logger.warn( mf, 'System is not cubic cell. Quadrupole energy ' 'correction is inaccurate since it is developed based on ' 'cubic cell.') e_quad = np.einsum('g,g,gx,gx->', rho, grids.weights, r_e, r_e) nuc_quad = np.einsum('g,gx,gx->', charges, r_nuc, r_nuc) quad = nuc_quad - e_quad de_quad = 2. * np.pi / (3 * cell.vol) * quad de = de_mono + de_dip + de_quad return de_mono, de_dip, de_quad, de
def _make_eris_incore(cc, mo_coeff=None): from pyscf.pbc import tools from pyscf.pbc.cc.ccsd import _adjust_occ log = logger.Logger(cc.stdout, cc.verbose) cput0 = (time.clock(), time.time()) eris = gccsd._PhysicistsERIs() cell = cc._scf.cell kpts = cc.kpts nkpts = cc.nkpts nocc = cc.nocc nmo = cc.nmo nvir = nmo - nocc eris.nocc = nocc #if any(nocc != numpy.count_nonzero(cc._scf.mo_occ[k] > 0) for k in range(nkpts)): # raise NotImplementedError('Different occupancies found for different k-points') if mo_coeff is None: mo_coeff = cc.mo_coeff nao = mo_coeff[0].shape[0] dtype = mo_coeff[0].dtype moidx = get_frozen_mask(cc) nocc_per_kpt = numpy.asarray(get_nocc(cc, per_kpoint=True)) nmo_per_kpt = numpy.asarray(get_nmo(cc, per_kpoint=True)) padded_moidx = [] for k in range(nkpts): kpt_nocc = nocc_per_kpt[k] kpt_nvir = nmo_per_kpt[k] - kpt_nocc kpt_padded_moidx = numpy.concatenate( (numpy.ones(kpt_nocc, dtype=numpy.bool), numpy.zeros(nmo - kpt_nocc - kpt_nvir, dtype=numpy.bool), numpy.ones(kpt_nvir, dtype=numpy.bool))) padded_moidx.append(kpt_padded_moidx) eris.mo_coeff = [] eris.orbspin = [] # Generate the molecular orbital coefficients with the frozen orbitals masked. # Each MO is tagged with orbspin, a list of 0's and 1's that give the overall # spin of each MO. # # Here we will work with two index arrays; one is for our original (small) moidx # array while the next is for our new (large) padded array. for k in range(nkpts): kpt_moidx = moidx[k] kpt_padded_moidx = padded_moidx[k] mo = numpy.zeros((nao, nmo), dtype=dtype) mo[:, kpt_padded_moidx] = mo_coeff[k][:, kpt_moidx] if getattr(mo_coeff[k], 'orbspin', None) is not None: orbspin_dtype = mo_coeff[k].orbspin[kpt_moidx].dtype orbspin = numpy.zeros(nmo, dtype=orbspin_dtype) orbspin[kpt_padded_moidx] = mo_coeff[k].orbspin[kpt_moidx] mo = lib.tag_array(mo, orbspin=orbspin) eris.orbspin.append(orbspin) # FIXME: What if the user freezes all up spin orbitals in # an RHF calculation? The number of electrons will still be # even. else: # guess orbital spin - assumes an RHF calculation assert (numpy.count_nonzero(kpt_moidx) % 2 == 0) orbspin = numpy.zeros(mo.shape[1], dtype=int) orbspin[1::2] = 1 mo = lib.tag_array(mo, orbspin=orbspin) eris.orbspin.append(orbspin) eris.mo_coeff.append(mo) # Re-make our fock MO matrix elements from density and fock AO dm = cc._scf.make_rdm1(cc.mo_coeff, cc.mo_occ) with lib.temporary_env(cc._scf, exxdiv=None): # _scf.exxdiv affects eris.fock. HF exchange correction should be # excluded from the Fock matrix. fockao = cc._scf.get_hcore() + cc._scf.get_veff(cell, dm) eris.fock = numpy.asarray([ reduce(numpy.dot, (mo.T.conj(), fockao[k], mo)) for k, mo in enumerate(eris.mo_coeff) ]) eris.mo_energy = [eris.fock[k].diagonal().real for k in range(nkpts)] # Add HFX correction in the eris.mo_energy to improve convergence in # CCSD iteration. It is useful for the 2D systems since their occupied and # the virtual orbital energies may overlap which may lead to numerical # issue in the CCSD iterations. # FIXME: Whether to add this correction for other exxdiv treatments? # Without the correction, MP2 energy may be largely off the correct value. madelung = tools.madelung(cell, kpts) eris.mo_energy = [ _adjust_occ(mo_e, nocc, -madelung) for k, mo_e in enumerate(eris.mo_energy) ] # Get location of padded elements in occupied and virtual space. nocc_per_kpt = get_nocc(cc, per_kpoint=True) nonzero_padding = padding_k_idx(cc, kind="joint") # Check direct and indirect gaps for possible issues with CCSD convergence. mo_e = [eris.mo_energy[kp][nonzero_padding[kp]] for kp in range(nkpts)] mo_e = numpy.sort([y for x in mo_e for y in x]) # Sort de-nested array gap = mo_e[numpy.sum(nocc_per_kpt)] - mo_e[numpy.sum(nocc_per_kpt) - 1] if gap < 1e-5: logger.warn( cc, 'H**O-LUMO gap %s too small for KCCSD. ' 'May cause issues in convergence.', gap) kconserv = kpts_helper.get_kconserv(cell, kpts) if getattr(mo_coeff[0], 'orbspin', None) is None: # The bottom nao//2 coefficients are down (up) spin while the top are up (down). mo_a_coeff = [mo[:nao // 2] for mo in eris.mo_coeff] mo_b_coeff = [mo[nao // 2:] for mo in eris.mo_coeff] eri = numpy.empty((nkpts, nkpts, nkpts, nmo, nmo, nmo, nmo), dtype=numpy.complex128) fao2mo = cc._scf.with_df.ao2mo for kp, kq, kr in kpts_helper.loop_kkk(nkpts): ks = kconserv[kp, kq, kr] eri_kpt = fao2mo((mo_a_coeff[kp], mo_a_coeff[kq], mo_a_coeff[kr], mo_a_coeff[ks]), (kpts[kp], kpts[kq], kpts[kr], kpts[ks]), compact=False) eri_kpt += fao2mo((mo_b_coeff[kp], mo_b_coeff[kq], mo_b_coeff[kr], mo_b_coeff[ks]), (kpts[kp], kpts[kq], kpts[kr], kpts[ks]), compact=False) eri_kpt += fao2mo((mo_a_coeff[kp], mo_a_coeff[kq], mo_b_coeff[kr], mo_b_coeff[ks]), (kpts[kp], kpts[kq], kpts[kr], kpts[ks]), compact=False) eri_kpt += fao2mo((mo_b_coeff[kp], mo_b_coeff[kq], mo_a_coeff[kr], mo_a_coeff[ks]), (kpts[kp], kpts[kq], kpts[kr], kpts[ks]), compact=False) eri_kpt = eri_kpt.reshape(nmo, nmo, nmo, nmo) eri[kp, kq, kr] = eri_kpt else: mo_a_coeff = [mo[:nao // 2] + mo[nao // 2:] for mo in eris.mo_coeff] eri = numpy.empty((nkpts, nkpts, nkpts, nmo, nmo, nmo, nmo), dtype=numpy.complex128) fao2mo = cc._scf.with_df.ao2mo for kp, kq, kr in kpts_helper.loop_kkk(nkpts): ks = kconserv[kp, kq, kr] eri_kpt = fao2mo((mo_a_coeff[kp], mo_a_coeff[kq], mo_a_coeff[kr], mo_a_coeff[ks]), (kpts[kp], kpts[kq], kpts[kr], kpts[ks]), compact=False) eri_kpt[(eris.orbspin[kp][:, None] != eris.orbspin[kq]).ravel()] = 0 eri_kpt[:, (eris.orbspin[kr][:, None] != eris.orbspin[ks]).ravel()] = 0 eri_kpt = eri_kpt.reshape(nmo, nmo, nmo, nmo) eri[kp, kq, kr] = eri_kpt # Check some antisymmetrized properties of the integrals if DEBUG: check_antisymm_3412(cc, cc.kpts, eri) # Antisymmetrizing (pq|rs)-(ps|rq), where the latter integral is equal to # (rq|ps); done since we aren't tracking the kpoint of orbital 's' eri = eri - eri.transpose(2, 1, 0, 5, 4, 3, 6) # Chemist -> physics notation eri = eri.transpose(0, 2, 1, 3, 5, 4, 6) # Set the various integrals eris.dtype = eri.dtype eris.oooo = eri[:, :, :, :nocc, :nocc, :nocc, :nocc].copy() / nkpts eris.ooov = eri[:, :, :, :nocc, :nocc, :nocc, nocc:].copy() / nkpts eris.ovoo = eri[:, :, :, :nocc, nocc:, :nocc, :nocc].copy() / nkpts eris.oovv = eri[:, :, :, :nocc, :nocc, nocc:, nocc:].copy() / nkpts eris.ovov = eri[:, :, :, :nocc, nocc:, :nocc, nocc:].copy() / nkpts eris.ovvv = eri[:, :, :, :nocc, nocc:, nocc:, nocc:].copy() / nkpts eris.vvvv = eri[:, :, :, nocc:, nocc:, nocc:, nocc:].copy() / nkpts log.timer('CCSD integral transformation', *cput0) return eris
def get_ab(mf, mo_energy=None, mo_coeff=None, mo_occ=None): r'''A and B matrices for TDDFT response function. A[i,a,j,b] = \delta_{ab}\delta_{ij}(E_a - E_i) + (ia||bj) B[i,a,j,b] = (ia||jb) Spin symmetry is considered in the returned A, B lists. List A has three items: (A_aaaa, A_aabb, A_bbbb). A_bbaa = A_aabb.transpose(2,3,0,1). B has three items: (B_aaaa, B_aabb, B_bbbb). B_bbaa = B_aabb.transpose(2,3,0,1). ''' if mo_energy is None: mo_energy = mf.mo_energy if mo_coeff is None: mo_coeff = mf.mo_coeff if mo_occ is None: mo_occ = mf.mo_occ mol = mf.mol nao = mol.nao_nr() occidx_a = numpy.where(mo_occ[0] == 1)[0] viridx_a = numpy.where(mo_occ[0] == 0)[0] occidx_b = numpy.where(mo_occ[1] == 1)[0] viridx_b = numpy.where(mo_occ[1] == 0)[0] orbo_a = mo_coeff[0][:, occidx_a] orbv_a = mo_coeff[0][:, viridx_a] orbo_b = mo_coeff[1][:, occidx_b] orbv_b = mo_coeff[1][:, viridx_b] nocc_a = orbo_a.shape[1] nvir_a = orbv_a.shape[1] nocc_b = orbo_b.shape[1] nvir_b = orbv_b.shape[1] mo_a = numpy.hstack((orbo_a, orbv_a)) mo_b = numpy.hstack((orbo_b, orbv_b)) nmo_a = nocc_a + nvir_a nmo_b = nocc_b + nvir_b e_ia_a = (mo_energy[0][viridx_a, None] - mo_energy[0][occidx_a]).T e_ia_b = (mo_energy[1][viridx_b, None] - mo_energy[1][occidx_b]).T a_aa = numpy.diag(e_ia_a.ravel()).reshape(nocc_a, nvir_a, nocc_a, nvir_a) a_bb = numpy.diag(e_ia_b.ravel()).reshape(nocc_b, nvir_b, nocc_b, nvir_b) a_ab = numpy.zeros((nocc_a, nvir_a, nocc_b, nvir_b)) b_aa = numpy.zeros_like(a_aa) b_ab = numpy.zeros_like(a_ab) b_bb = numpy.zeros_like(a_bb) a = (a_aa, a_ab, a_bb) b = (b_aa, b_ab, b_bb) def add_hf_(a, b, hyb=1): eri_aa = ao2mo.general(mol, [orbo_a, mo_a, mo_a, mo_a], compact=False) eri_ab = ao2mo.general(mol, [orbo_a, mo_a, mo_b, mo_b], compact=False) eri_bb = ao2mo.general(mol, [orbo_b, mo_b, mo_b, mo_b], compact=False) eri_aa = eri_aa.reshape(nocc_a, nmo_a, nmo_a, nmo_a) eri_ab = eri_ab.reshape(nocc_a, nmo_a, nmo_b, nmo_b) eri_bb = eri_bb.reshape(nocc_b, nmo_b, nmo_b, nmo_b) a_aa, a_ab, a_bb = a b_aa, b_ab, b_bb = b a_aa += numpy.einsum('iabj->iajb', eri_aa[:nocc_a, nocc_a:, nocc_a:, :nocc_a]) a_aa -= numpy.einsum('ijba->iajb', eri_aa[:nocc_a, :nocc_a, nocc_a:, nocc_a:]) * hyb b_aa += numpy.einsum('iajb->iajb', eri_aa[:nocc_a, nocc_a:, :nocc_a, nocc_a:]) b_aa -= numpy.einsum('jaib->iajb', eri_aa[:nocc_a, nocc_a:, :nocc_a, nocc_a:]) * hyb a_bb += numpy.einsum('iabj->iajb', eri_bb[:nocc_b, nocc_b:, nocc_b:, :nocc_b]) a_bb -= numpy.einsum('ijba->iajb', eri_bb[:nocc_b, :nocc_b, nocc_b:, nocc_b:]) * hyb b_bb += numpy.einsum('iajb->iajb', eri_bb[:nocc_b, nocc_b:, :nocc_b, nocc_b:]) b_bb -= numpy.einsum('jaib->iajb', eri_bb[:nocc_b, nocc_b:, :nocc_b, nocc_b:]) * hyb a_ab += numpy.einsum('iabj->iajb', eri_ab[:nocc_a, nocc_a:, nocc_b:, :nocc_b]) b_ab += numpy.einsum('iajb->iajb', eri_ab[:nocc_a, nocc_a:, :nocc_b, nocc_b:]) if getattr(mf, 'xc', None) and getattr(mf, '_numint', None): from pyscf.dft import rks from pyscf.dft import numint ni = mf._numint ni.libxc.test_deriv_order(mf.xc, 2, raise_error=True) if getattr(mf, 'nlc', '') != '': logger.warn( mf, 'NLC functional found in DFT object. Its second ' 'deriviative is not available. Its contribution is ' 'not included in the response function.') omega, alpha, hyb = ni.rsh_and_hybrid_coeff(mf.xc, mol.spin) add_hf_(a, b, hyb) xctype = ni._xc_type(mf.xc) dm0 = mf.make_rdm1(mo_coeff, mo_occ) make_rho = ni._gen_rho_evaluator(mol, dm0, hermi=1)[0] mem_now = lib.current_memory()[0] max_memory = max(2000, mf.max_memory * .8 - mem_now) if xctype == 'LDA': ao_deriv = 0 for ao, mask, weight, coords \ in ni.block_loop(mol, mf.grids, nao, ao_deriv, max_memory): rho0a = make_rho(0, ao, mask, 'LDA') rho0b = make_rho(1, ao, mask, 'LDA') fxc = ni.eval_xc(mf.xc, (rho0a, rho0b), 1, deriv=2)[2] u_u, u_d, d_d = fxc[0].T rho_o_a = lib.einsum('rp,pi->ri', ao, orbo_a) rho_v_a = lib.einsum('rp,pi->ri', ao, orbv_a) rho_o_b = lib.einsum('rp,pi->ri', ao, orbo_b) rho_v_b = lib.einsum('rp,pi->ri', ao, orbv_b) rho_ov_a = numpy.einsum('ri,ra->ria', rho_o_a, rho_v_a) rho_ov_b = numpy.einsum('ri,ra->ria', rho_o_b, rho_v_b) w_ov = numpy.einsum('ria,r->ria', rho_ov_a, weight * u_u) iajb = lib.einsum('ria,rjb->iajb', rho_ov_a, w_ov) a_aa += iajb b_aa += iajb w_ov = numpy.einsum('ria,r->ria', rho_ov_b, weight * u_d) iajb = lib.einsum('ria,rjb->iajb', rho_ov_a, w_ov) a_ab += iajb b_ab += iajb w_ov = numpy.einsum('ria,r->ria', rho_ov_b, weight * d_d) iajb = lib.einsum('ria,rjb->iajb', rho_ov_b, w_ov) a_bb += iajb b_bb += iajb elif xctype == 'GGA': ao_deriv = 1 for ao, mask, weight, coords \ in ni.block_loop(mol, mf.grids, nao, ao_deriv, max_memory): rho0a = make_rho(0, ao, mask, 'GGA') rho0b = make_rho(1, ao, mask, 'GGA') vxc, fxc = ni.eval_xc(mf.xc, (rho0a, rho0b), 1, deriv=2)[1:3] uu, ud, dd = vxc[1].T u_u, u_d, d_d = fxc[0].T u_uu, u_ud, u_dd, d_uu, d_ud, d_dd = fxc[1].T uu_uu, uu_ud, uu_dd, ud_ud, ud_dd, dd_dd = fxc[2].T rho_o_a = lib.einsum('xrp,pi->xri', ao, orbo_a) rho_v_a = lib.einsum('xrp,pi->xri', ao, orbv_a) rho_o_b = lib.einsum('xrp,pi->xri', ao, orbo_b) rho_v_b = lib.einsum('xrp,pi->xri', ao, orbv_b) rho_ov_a = numpy.einsum('xri,ra->xria', rho_o_a, rho_v_a[0]) rho_ov_b = numpy.einsum('xri,ra->xria', rho_o_b, rho_v_b[0]) rho_ov_a[1:4] += numpy.einsum('ri,xra->xria', rho_o_a[0], rho_v_a[1:4]) rho_ov_b[1:4] += numpy.einsum('ri,xra->xria', rho_o_b[0], rho_v_b[1:4]) # sigma1 ~ \nabla(\rho_\alpha+\rho_\beta) dot \nabla(|b><j|) z_{bj} a0a1 = numpy.einsum('xr,xria->ria', rho0a[1:4], rho_ov_a[1:4]) a0b1 = numpy.einsum('xr,xria->ria', rho0a[1:4], rho_ov_b[1:4]) b0a1 = numpy.einsum('xr,xria->ria', rho0b[1:4], rho_ov_a[1:4]) b0b1 = numpy.einsum('xr,xria->ria', rho0b[1:4], rho_ov_b[1:4]) w_ov = numpy.empty_like(rho_ov_a) w_ov[0] = numpy.einsum('r,ria->ria', u_u, rho_ov_a[0]) w_ov[0] += numpy.einsum('r,ria->ria', 2 * u_uu, a0a1) w_ov[0] += numpy.einsum('r,ria->ria', u_ud, b0a1) f_ov_a = numpy.einsum('r,ria->ria', 4 * uu_uu, a0a1) f_ov_b = numpy.einsum('r,ria->ria', 2 * uu_ud, a0a1) f_ov_a += numpy.einsum('r,ria->ria', 2 * uu_ud, b0a1) f_ov_b += numpy.einsum('r,ria->ria', ud_ud, b0a1) f_ov_a += numpy.einsum('r,ria->ria', 2 * u_uu, rho_ov_a[0]) f_ov_b += numpy.einsum('r,ria->ria', u_ud, rho_ov_a[0]) w_ov[1:] = numpy.einsum('ria,xr->xria', f_ov_a, rho0a[1:4]) w_ov[1:] += numpy.einsum('ria,xr->xria', f_ov_b, rho0b[1:4]) w_ov[1:] += numpy.einsum('r,xria->xria', 2 * uu, rho_ov_a[1:4]) w_ov *= weight[:, None, None] iajb = lib.einsum('xria,xrjb->iajb', rho_ov_a, w_ov) a_aa += iajb b_aa += iajb w_ov = numpy.empty_like(rho_ov_b) w_ov[0] = numpy.einsum('r,ria->ria', d_d, rho_ov_b[0]) w_ov[0] += numpy.einsum('r,ria->ria', 2 * d_dd, b0b1) w_ov[0] += numpy.einsum('r,ria->ria', d_ud, a0b1) f_ov_b = numpy.einsum('r,ria->ria', 4 * dd_dd, b0b1) f_ov_a = numpy.einsum('r,ria->ria', 2 * ud_dd, b0b1) f_ov_b += numpy.einsum('r,ria->ria', 2 * ud_dd, a0b1) f_ov_a += numpy.einsum('r,ria->ria', ud_ud, a0b1) f_ov_b += numpy.einsum('r,ria->ria', 2 * d_dd, rho_ov_b[0]) f_ov_a += numpy.einsum('r,ria->ria', d_ud, rho_ov_b[0]) w_ov[1:] = numpy.einsum('ria,xr->xria', f_ov_a, rho0a[1:4]) w_ov[1:] += numpy.einsum('ria,xr->xria', f_ov_b, rho0b[1:4]) w_ov[1:] += numpy.einsum('r,xria->xria', 2 * dd, rho_ov_b[1:4]) w_ov *= weight[:, None, None] iajb = lib.einsum('xria,xrjb->iajb', rho_ov_b, w_ov) a_bb += iajb b_bb += iajb w_ov = numpy.empty_like(rho_ov_b) w_ov[0] = numpy.einsum('r,ria->ria', u_d, rho_ov_b[0]) w_ov[0] += numpy.einsum('r,ria->ria', 2 * u_dd, b0b1) w_ov[0] += numpy.einsum('r,ria->ria', u_ud, a0b1) f_ov_a = numpy.einsum('r,ria->ria', 4 * uu_dd, b0b1) f_ov_b = numpy.einsum('r,ria->ria', 2 * ud_dd, b0b1) f_ov_a += numpy.einsum('r,ria->ria', 2 * uu_ud, a0b1) f_ov_b += numpy.einsum('r,ria->ria', ud_ud, a0b1) f_ov_a += numpy.einsum('r,ria->ria', 2 * d_uu, rho_ov_b[0]) f_ov_b += numpy.einsum('r,ria->ria', d_ud, rho_ov_b[0]) w_ov[1:] = numpy.einsum('ria,xr->xria', f_ov_a, rho0a[1:4]) w_ov[1:] += numpy.einsum('ria,xr->xria', f_ov_b, rho0b[1:4]) w_ov[1:] += numpy.einsum('r,xria->xria', ud, rho_ov_b[1:4]) w_ov *= weight[:, None, None] iajb = lib.einsum('xria,xrjb->iajb', rho_ov_a, w_ov) a_ab += iajb b_ab += iajb elif xctype == 'NLC': raise NotImplementedError('NLC') elif xctype == 'MGGA': raise NotImplementedError('meta-GGA') else: add_hf_(a, b) return a, b
def get_nto(tdobj, state=1, threshold=OUTPUT_THRESHOLD, verbose=None): r''' Natural transition orbital analysis. The natural transition density matrix between ground state and excited state :math:`Tia = \langle \Psi_{ex} | i a^\dagger | \Psi_0 \rangle` can be transformed to diagonal form through SVD :math:`T = O \sqrt{\lambda} V^\dagger`. O and V are occupied and virtual natural transition orbitals. The diagonal elements :math:`\lambda` are the weights of the occupied-virtual orbital pair in the excitation. Ref: Martin, R. L., JCP, 118, 4775-4777 Note in the TDHF/TDDFT calculations, the excitation part (X) is interpreted as the CIS coefficients and normalized to 1. The de-excitation part (Y) is ignored. Args: state : int Excited state ID. state = 1 means the first excited state. If state < 0, state ID is counted from the last excited state. Kwargs: threshold : float Above which the NTO coefficients will be printed in the output. Returns: A list (weights, NTOs). NTOs are natural orbitals represented in AO basis. The first N_occ NTOs are occupied NTOs and the rest are virtual NTOs. ''' if state == 0: logger.warn( tdobj, 'Excited state starts from 1. ' 'Set state=1 for first excited state.') state_id = state elif state < 0: state_id = state else: state_id = state - 1 mol = tdobj.mol mo_coeff = tdobj._scf.mo_coeff mo_occ = tdobj._scf.mo_occ orbo_a = mo_coeff[0][:, mo_occ[0] == 1] orbv_a = mo_coeff[0][:, mo_occ[0] == 0] orbo_b = mo_coeff[1][:, mo_occ[1] == 1] orbv_b = mo_coeff[1][:, mo_occ[1] == 0] nocc_a = orbo_a.shape[1] nvir_a = orbv_a.shape[1] nocc_b = orbo_b.shape[1] nvir_b = orbv_b.shape[1] cis_t1a, cis_t1b = tdobj.xy[state_id][0] norm = numpy.linalg.norm(cis_t1a)**2 + numpy.linalg.norm(cis_t1b)**2 cis_t1a *= 1. / norm cis_t1b *= 1. / norm if mol.symmetry: orbsyma, orbsymb = uhf_symm.get_orbsym(mol, mo_coeff) o_sym_a = orbsyma[mo_occ[0] == 1] v_sym_a = orbsyma[mo_occ[0] == 0] o_sym_b = orbsymb[mo_occ[1] == 1] v_sym_b = orbsymb[mo_occ[1] == 0] nto_o_a = numpy.eye(nocc_a) nto_v_a = numpy.eye(nvir_a) nto_o_b = numpy.eye(nocc_b) nto_v_b = numpy.eye(nvir_b) weights_o_a = numpy.zeros(nocc_a) weights_v_a = numpy.zeros(nvir_a) weights_o_b = numpy.zeros(nocc_b) weights_v_b = numpy.zeros(nvir_b) for ir in set(orbsyma): idx = numpy.where(o_sym_a == ir)[0] if idx.size > 0: dm_oo = numpy.dot(cis_t1a[idx], cis_t1a[idx].T) weights_o_a[idx], nto_o_a[idx[:, None], idx] = numpy.linalg.eigh(dm_oo) idx = numpy.where(v_sym_a == ir)[0] if idx.size > 0: dm_vv = numpy.dot(cis_t1a[:, idx].T, cis_t1a[:, idx]) weights_v_a[idx], nto_v_a[idx[:, None], idx] = numpy.linalg.eigh(dm_vv) for ir in set(orbsymb): idx = numpy.where(o_sym_b == ir)[0] if idx.size > 0: dm_oo = numpy.dot(cis_t1b[idx], cis_t1b[idx].T) weights_o_b[idx], nto_o_b[idx[:, None], idx] = numpy.linalg.eigh(dm_oo) idx = numpy.where(v_sym_b == ir)[0] if idx.size > 0: dm_vv = numpy.dot(cis_t1b[:, idx].T, cis_t1b[:, idx]) weights_v_b[idx], nto_v_b[idx[:, None], idx] = numpy.linalg.eigh(dm_vv) def sort(weights, nto, sym): # weights in descending order idx = numpy.argsort(-weights) weights = weights[idx] nto = nto[:, idx] sym = sym[idx] return weights, nto, sym weights_o_a, nto_o_a, o_sym_a = sort(weights_o_a, nto_o_a, o_sym_a) weights_v_a, nto_v_a, v_sym_a = sort(weights_v_a, nto_v_a, v_sym_a) weights_o_b, nto_o_b, o_sym_b = sort(weights_o_b, nto_o_b, o_sym_b) weights_v_b, nto_v_b, v_sym_b = sort(weights_v_b, nto_v_b, v_sym_b) nto_orbsyma = numpy.hstack((o_sym_a, v_sym_a)) nto_orbsymb = numpy.hstack((o_sym_b, v_sym_b)) if nocc_a < nvir_a: weights_a = weights_o_a else: weights_a = weights_v_a if nocc_b < nvir_b: weights_b = weights_o_b else: weights_b = weights_v_b else: nto_o_a, w_a, nto_v_aT = numpy.linalg.svd(cis_t1a) nto_o_b, w_b, nto_v_bT = numpy.linalg.svd(cis_t1b) nto_v_a = nto_v_aT.conj().T nto_v_b = nto_v_bT.conj().T weights_a = w_a**2 weights_b = w_b**2 nto_orbsyma = nto_orbsymb = None def _set_phase_(c): idx = numpy.argmax(abs(c.real), axis=0) c[:, c[idx, numpy.arange(c.shape[1])].real < 0] *= -1 _set_phase_(nto_o_a) _set_phase_(nto_o_b) _set_phase_(nto_v_a) _set_phase_(nto_v_b) occupied_nto_a = numpy.dot(orbo_a, nto_o_a) occupied_nto_b = numpy.dot(orbo_b, nto_o_b) virtual_nto_a = numpy.dot(orbv_a, nto_v_a) virtual_nto_b = numpy.dot(orbv_b, nto_v_b) nto_coeff = (numpy.hstack((occupied_nto_a, virtual_nto_a)), numpy.hstack((occupied_nto_b, virtual_nto_b))) if mol.symmetry: nto_coeff = (lib.tag_array(nto_coeff[0], orbsym=nto_orbsyma), lib.tag_array(nto_coeff[1], orbsym=nto_orbsymb)) log = logger.new_logger(tdobj, verbose) if log.verbose >= logger.INFO: log.info('State %d: %g eV NTO largest component %s', state_id + 1, tdobj.e[state_id] * nist.HARTREE2EV, weights_a[0] + weights_b[0]) fmt = '%' + str(lib.param.OUTPUT_DIGITS) + 'f (MO #%d)' o_idx_a = numpy.where(abs(nto_o_a[:, 0]) > threshold)[0] v_idx_a = numpy.where(abs(nto_v_a[:, 0]) > threshold)[0] o_idx_b = numpy.where(abs(nto_o_b[:, 0]) > threshold)[0] v_idx_b = numpy.where(abs(nto_v_b[:, 0]) > threshold)[0] log.info(' alpha occ-NTO: ' + ' + '.join([(fmt % (nto_o_a[i, 0], i + MO_BASE)) for i in o_idx_a])) log.info(' alpha vir-NTO: ' + ' + '.join([(fmt % (nto_v_a[i, 0], i + MO_BASE + nocc_a)) for i in v_idx_a])) log.info(' beta occ-NTO: ' + ' + '.join([(fmt % (nto_o_b[i, 0], i + MO_BASE)) for i in o_idx_b])) log.info(' beta vir-NTO: ' + ' + '.join([(fmt % (nto_v_b[i, 0], i + MO_BASE + nocc_b)) for i in v_idx_b])) return (weights_a, weights_b), nto_coeff
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) t1 = (time.clock(), time.time()) nkpts = len(kpts_lst) nao = cell.nao_nr() nao_pair = nao * (nao + 1) // 2 Gv, Gvbase, kws = cell.get_Gv_weights(mydf.gs) kpt_allow = numpy.zeros(3) if mydf.eta == 0: vpplocG = pseudo.pp_int.get_gth_vlocG_part1(cell, Gv) vpplocG = -numpy.einsum('ij,ij->j', cell.get_SI(Gv), vpplocG) 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) gs_guess = tools.cutoff_to_gs(cell.lattice_vectors(), ke_guess) if numpy.any(mydf.gs < gs_guess * .8): logger.warn( mydf, 'gs %s is not enough for AFTDF.get_nuc function ' 'to get integral accuracy %g.\nRecomended gs is %s.', mydf.gs, cell.precision, gs_guess) nuccell = copy.copy(cell) half_sph_norm = .5 / numpy.sqrt(numpy.pi) norm = half_sph_norm / gto.mole._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)) t1 = log.timer_debug1('vnuc pass1: analytic int', *t1) charge = -cell.atom_charges() coulG = tools.get_coulG(cell, kpt_allow, gs=mydf.gs, Gv=Gv) coulG *= kws aoaux = ft_ao.ft_ao(nuccell, Gv) vG = numpy.einsum('i,xi->x', charge, aoaux) * coulG max_memory = max(2000, mydf.max_memory - lib.current_memory()[0]) for aoaoks, p0, p1 in mydf.ft_loop(mydf.gs, 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', *t1) 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)
def init_guess_by_1e(self, cell=None): if cell is None: cell = self.cell if cell.dimension < 3: logger.warn(self, 'Hcore initial guess is not recommended in ' 'the SCF of low-dimensional systems.') return mol_uhf.UHF.init_guess_by_1e(self, cell)
def DMRG_COMPRESS_NEVPT(mc, maxM=500, root=0, nevptsolver=None, tol=1e-7, nevpt_integral=None): if isinstance(nevpt_integral, str) and h5py.is_hdf5(nevpt_integral): nevpt_integral_file = os.path.abspath(nevpt_integral) mol = chkfile.load_mol(nevpt_integral_file) fh5 = h5py.File(nevpt_integral_file, 'r') ncas = fh5['mc/ncas'][()] ncore = fh5['mc/ncore'][()] nvirt = fh5['mc/nvirt'][()] nelecas = fh5['mc/nelecas'][()] nroots = fh5['mc/nroots'][()] wfnsym = fh5['mc/wfnsym'][()] fh5.close() else: mol = mc.mol ncas = mc.ncas ncore = mc.ncore nvirt = mc.mo_coeff.shape[1] - mc.ncas - mc.ncore nelecas = mc.nelecas nroots = mc.fcisolver.nroots wfnsym = mc.fcisolver.wfnsym nevpt_integral_file = None if nevptsolver is None: nevptsolver = default_nevpt_schedule(mc.fcisolver, maxM, tol) #nevptsolver.__dict__.update(mc.fcisolver.__dict__) nevptsolver.wfnsym = wfnsym nevptsolver.block_extra_keyword = mc.fcisolver.block_extra_keyword nevptsolver.nroots = nroots nevptsolver.executable = settings.BLOCKEXE_COMPRESS_NEVPT if nevptsolver.executable == getattr(mc.fcisolver, 'executable', None): logger.warn( mc, 'DMRG executable file for nevptsolver is the same ' 'to the executable file for DMRG solver. If they are ' 'both compiled by MPI compilers, they may cause error or ' 'random results in DMRG-NEVPT calculation.') nevpt_scratch = os.path.abspath(nevptsolver.scratchDirectory) dmrg_scratch = os.path.abspath(mc.fcisolver.scratchDirectory) # Integrals are not given by the kwarg nevpt_integral if nevpt_integral_file is None: nevpt_integral_file = os.path.join(nevpt_scratch, 'nevpt_perturb_integral') write_chk(mc, root, nevpt_integral_file) conf = dmrgci.writeDMRGConfFile( nevptsolver, nelecas, False, with_2pdm=False, extraline=['fullrestart', 'nevpt_state_num %d' % root]) with open(conf, 'r') as f: block_conf = f.readlines() block_conf = [l for l in block_conf if 'prefix' not in l] block_conf = ''.join(block_conf) with h5py.File(nevpt_integral_file, 'a') as fh5: if 'dmrg.conf' in fh5: del (fh5['dmrg.conf']) fh5['dmrg.conf'] = block_conf if nevptsolver.verbose >= logger.DEBUG1: logger.debug1(nevptsolver, 'Block Input conf') logger.debug1(nevptsolver, block_conf) t0 = (time.clock(), time.time()) # function nevpt_integral_mpi is called in this cmd cmd = ' '.join( (nevptsolver.mpiprefix, os.path.realpath(os.path.join(__file__, '..', 'nevpt_mpi.py')), nevpt_integral_file, nevptsolver.executable, dmrg_scratch, nevpt_scratch)) logger.debug(nevptsolver, 'DMRG_COMPRESS_NEVPT cmd %s', cmd) try: output = subprocess.check_call(cmd, shell=True) except subprocess.CalledProcessError as err: logger.error(nevptsolver, cmd) raise err if nevptsolver.verbose >= logger.DEBUG1: logger.debug1( nevptsolver, open(os.path.join(nevpt_scratch, '0', 'dmrg.out')).read()) perturb_file = os.path.join(nevpt_scratch, '0', 'Perturbation_%d' % root) fh5 = h5py.File(perturb_file, 'r') Vi_e = fh5['Vi/energy'][()] Vr_e = fh5['Vr/energy'][()] fh5.close() logger.note(nevptsolver, 'Nevpt Energy:') logger.note(nevptsolver, 'Sr Subspace: E = %.14f' % (Vr_e)) logger.note(nevptsolver, 'Si Subspace: E = %.14f' % (Vi_e)) logger.timer(nevptsolver, 'MPS NEVPT calculation time', *t0) return perturb_file
def __init__(self, cis, mo_coeff=None, method="incore"): log = logger.Logger(cis.stdout, cis.verbose) cput0 = (time.clock(), time.time()) moidx = get_frozen_mask(cis) cell = cis._scf.cell nocc = cis.nocc nmo = cis.nmo nvir = nmo - nocc nkpts = cis.nkpts kpts = cis.kpts if mo_coeff is None: mo_coeff = cis.mo_coeff dtype = mo_coeff[0].dtype mo_coeff = self.mo_coeff = padded_mo_coeff(cis, mo_coeff) # Re-make our fock MO matrix elements from density and fock AO dm = cis._scf.make_rdm1(cis.mo_coeff, cis.mo_occ) exxdiv = cis._scf.exxdiv if cis.keep_exxdiv else None with lib.temporary_env(cis._scf, exxdiv=exxdiv): # _scf.exxdiv affects eris.fock. HF exchange correction should be # excluded from the Fock matrix. fockao = cis._scf.get_hcore() + cis._scf.get_veff(cell, dm) self.fock = np.asarray([reduce(np.dot, (mo.T.conj(), fockao[k], mo)) for k, mo in enumerate(mo_coeff)]) self.mo_energy = [self.fock[k].diagonal().real for k in range(nkpts)] if not cis.keep_exxdiv: # Add HFX correction in the self.mo_energy to improve convergence in # CCSD iteration. It is useful for the 2D systems since their occupied and # the virtual orbital energies may overlap which may lead to numerical # issue in the CCSD iterations. # FIXME: Whether to add this correction for other exxdiv treatments? # Without the correction, MP2 energy may be largely off the correct value. madelung = tools.madelung(cell, kpts) self.mo_energy = [ _adjust_occ(mo_e, nocc, -madelung) for k, mo_e in enumerate(self.mo_energy) ] # Get location of padded elements in occupied and virtual space. nocc_per_kpt = get_nocc(cis, per_kpoint=True) nonzero_padding = padding_k_idx(cis, kind="joint") # Check direct and indirect gaps for possible issues with CCSD convergence. mo_e = [self.mo_energy[kp][nonzero_padding[kp]] for kp in range(nkpts)] mo_e = np.sort([y for x in mo_e for y in x]) # Sort de-nested array gap = mo_e[np.sum(nocc_per_kpt)] - mo_e[np.sum(nocc_per_kpt) - 1] if gap < 1e-5: logger.warn( cis, "H**O-LUMO gap %s too small for KCCSD. " "May cause issues in convergence.", gap, ) memory_needed = (nkpts ** 3 * nocc ** 2 * nvir ** 2) * 16 / 1e6 # CIS only needs two terms: <aj|ib> and <aj|bi>; another factor of two for safety memory_needed *= 4 memory_now = lib.current_memory()[0] fao2mo = cis._scf.with_df.ao2mo kconserv = cis.khelper.kconserv khelper = cis.khelper if cis.direct and type(cis._scf.with_df) is not df.GDF: raise ValueError("CIS direct method must be used with GDF") if (cis.direct and type(cis._scf.with_df) is df.GDF and cell.dimension != 2): # cis._scf.with_df needs to be df.GDF only (not MDF) _init_cis_df_eris(cis, self) else: if ( method == "incore" and (memory_needed + memory_now < cis.max_memory) or cell.incore_anyway ): log.info("using incore ERI storage") self.ovov = np.empty( (nkpts, nkpts, nkpts, nocc, nvir, nocc, nvir), dtype=dtype ) self.voov = np.empty( (nkpts, nkpts, nkpts, nvir, nocc, nocc, nvir), dtype=dtype ) for (ikp, ikq, ikr) in khelper.symm_map.keys(): iks = kconserv[ikp, ikq, ikr] eri_kpt = fao2mo( (mo_coeff[ikp], mo_coeff[ikq], mo_coeff[ikr], mo_coeff[iks]), (kpts[ikp], kpts[ikq], kpts[ikr], kpts[iks]), compact=False, ) if dtype == np.float: eri_kpt = eri_kpt.real eri_kpt = eri_kpt.reshape(nmo, nmo, nmo, nmo) for (kp, kq, kr) in khelper.symm_map[(ikp, ikq, ikr)]: eri_kpt_symm = khelper.transform_symm( eri_kpt, kp, kq, kr ).transpose(0, 2, 1, 3) self.ovov[kp, kr, kq] = ( eri_kpt_symm[:nocc, nocc:, :nocc, nocc:] / nkpts ) self.voov[kp, kr, kq] = ( eri_kpt_symm[nocc:, :nocc, :nocc, nocc:] / nkpts ) self.dtype = dtype else: log.info("using HDF5 ERI storage") self.feri1 = lib.H5TmpFile() self.ovov = self.feri1.create_dataset( "ovov", (nkpts, nkpts, nkpts, nocc, nvir, nocc, nvir), dtype.char ) self.voov = self.feri1.create_dataset( "voov", (nkpts, nkpts, nkpts, nvir, nocc, nocc, nvir), dtype.char ) # <ia|pq> = (ip|aq) cput1 = time.clock(), time.time() for kp in range(nkpts): for kq in range(nkpts): for kr in range(nkpts): ks = kconserv[kp, kq, kr] orbo_p = mo_coeff[kp][:, :nocc] orbv_r = mo_coeff[kr][:, nocc:] buf_kpt = fao2mo( (orbo_p, mo_coeff[kq], orbv_r, mo_coeff[ks]), (kpts[kp], kpts[kq], kpts[kr], kpts[ks]), compact=False, ) if mo_coeff[0].dtype == np.float: buf_kpt = buf_kpt.real buf_kpt = buf_kpt.reshape(nocc, nmo, nvir, nmo).transpose( 0, 2, 1, 3 ) self.dtype = buf_kpt.dtype self.ovov[kp, kr, kq, :, :, :, :] = ( buf_kpt[:, :, :nocc, nocc:] / nkpts ) self.voov[kr, kp, ks, :, :, :, :] = ( buf_kpt[:, :, nocc:, :nocc].transpose(1, 0, 3, 2) / nkpts ) cput1 = log.timer_debug1("transforming ovpq", *cput1) log.timer("CIS integral transformation", *cput0)
def get_occ(mf, mo_energy=None, mo_coeff=None): if mo_energy is None: mo_energy = mf.mo_energy e_idx_a = numpy.argsort(mo_energy[0]) e_idx_b = numpy.argsort(mo_energy[1]) e_sort_a = mo_energy[0][e_idx_a] e_sort_b = mo_energy[1][e_idx_b] e_a = numpy.array(mo_energy[0][e_idx_a[:]]) e_b = numpy.array(mo_energy[1][e_idx_b[:]]) nmo = mo_energy[0].size n_a, n_b = mf.nelec mo_occ = numpy.zeros_like(mo_energy) # MP 2019 if not mf.mol.smearing: #print("Aufbau case") mo_occ[0, e_idx_a[:n_a]] = 1 mo_occ[1, e_idx_b[:n_b]] = 1 else: #print("Smearing case") if mf.verbose >= logger.INFO: logger.warn(mf, "WARNING: experimental, use at your own risk") mf.mol.FermiEnergy = numpy.zeros(2) mf.mol.FermiEnergy[0] = e_a[n_a] mf.mol.FermiEnergy[1] = e_b[n_b] from pyscf.scf.addons import fermi_smearing_mo_occ def nelec_cost_fn_a(e_f): mo_occ[0, :] = fermi_smearing_mo_occ(e_f, numpy.array(e_a), mf.mol.tau) nelec = numpy.sum(mo_occ[0, :]) return (nelec - n_a)**2 def nelec_cost_fn_b(e_f): mo_occ[1, :] = fermi_smearing_mo_occ(e_f, numpy.array(e_b), mf.mol.tau) nelec = numpy.sum(mo_occ[1, :]) return (nelec - n_b)**2 res = scipy.optimize.minimize(nelec_cost_fn_a, mf.mol.FermiEnergy[0], method='Powell', options={'maxiter': 2000}) mf.mol.FermiEnergy[0] = res.x res = scipy.optimize.minimize(nelec_cost_fn_b, mf.mol.FermiEnergy[1], method='Powell', options={'maxiter': 2000}) mf.mol.FermiEnergy[1] = res.x if n_a > 0: mo_occ[0, e_idx_a] = fermi_smearing_mo_occ( mf.mol.FermiEnergy[0], numpy.array(e_a), mf.mol.tau, n_a) * 2 else: mo_occ[0, :] = 0.0 if n_b > 0: mo_occ[1, e_idx_b] = fermi_smearing_mo_occ(mf.mol.FermiEnergy[1], numpy.array(e_b), mf.mol.tau, n_b) else: mo_occ[1, :] = 0.0 #print(mo_occ) if not numpy.isclose(numpy.sum(mo_occ), n_a + n_b): print('Fermi-Dirac did not converge. Try raising mol.tau.') mo_occ[0, e_idx_a[:n_a]] = 1 mo_occ[1, e_idx_b[:n_b]] = 1 return mo_occ if mf.verbose >= logger.INFO and n_a < nmo and n_b > 0 and n_b < nmo: if e_sort_a[n_a - 1] + 1e-3 > e_sort_a[n_a]: logger.warn(mf, 'alpha nocc = %d H**O %.15g >= LUMO %.15g', n_a, e_sort_a[n_a - 1], e_sort_a[n_a]) else: logger.info(mf, ' alpha nocc = %d H**O = %.15g LUMO = %.15g', n_a, e_sort_a[n_a - 1], e_sort_a[n_a]) if e_sort_b[n_b - 1] + 1e-3 > e_sort_b[n_b]: logger.warn(mf, 'beta nocc = %d H**O %.15g >= LUMO %.15g', n_b, e_sort_b[n_b - 1], e_sort_b[n_b]) else: logger.info(mf, ' beta nocc = %d H**O = %.15g LUMO = %.15g', n_b, e_sort_b[n_b - 1], e_sort_b[n_b]) if e_sort_a[n_a - 1] + 1e-3 > e_sort_b[n_b]: logger.warn(mf, 'system H**O %.15g >= system LUMO %.15g', e_sort_b[n_a - 1], e_sort_b[n_b]) numpy.set_printoptions(threshold=nmo) logger.debug(mf, ' alpha mo_energy =\n%s', mo_energy[0]) logger.debug(mf, ' beta mo_energy =\n%s', mo_energy[1]) numpy.set_printoptions(threshold=1000) if mo_coeff is not None and mf.verbose >= logger.DEBUG: ss, s = mf.spin_square( (mo_coeff[0][:, mo_occ[0] > 0], mo_coeff[1][:, mo_occ[1] > 0]), mf.get_ovlp()) logger.debug(mf, 'multiplicity <S^2> = %.8g 2S+1 = %.8g', ss, s) return mo_occ
def get_nto(tdobj, state=1, threshold=OUTPUT_THRESHOLD, verbose=None): r''' Natural transition orbital analysis. The natural transition density matrix between ground state and excited state :math:`Tia = \langle \Psi_{ex} | i a^\dagger | \Psi_0 \rangle` can be transformed to diagonal form through SVD :math:`T = O \sqrt{\lambda} V^\dagger`. O and V are occupied and virtual natural transition orbitals. The diagonal elements :math:`\lambda` are the weights of the occupied-virtual orbital pair in the excitation. Ref: Martin, R. L., JCP, 118, 4775-4777 Note in the TDHF/TDDFT calculations, the excitation part (X) is interpreted as the CIS coefficients and normalized to 1. The de-excitation part (Y) is ignored. Args: state : int Excited state ID. state = 1 means the first excited state. If state < 0, state ID is counted from the last excited state. Kwargs: threshold : float Above which the NTO coefficients will be printed in the output. Returns: A list (weights, NTOs). NTOs are natural orbitals represented in AO basis. The first N_occ NTOs are occupied NTOs and the rest are virtual NTOs. ''' if state == 0: logger.warn(tdobj, 'Excited state starts from 1. ' 'Set state=1 for first excited state.') state_id = state elif state < 0: state_id = state else: state_id = state - 1 mol = tdobj.mol mo_coeff = tdobj._scf.mo_coeff mo_occ = tdobj._scf.mo_occ orbo = mo_coeff[:,mo_occ==2] orbv = mo_coeff[:,mo_occ==0] nocc = orbo.shape[1] nvir = orbv.shape[1] cis_t1 = tdobj.xy[state_id][0] # TDDFT (X,Y) has X^2-Y^2=1. # Renormalizing X (X^2=1) to map it to CIS coefficients cis_t1 *= 1. / numpy.linalg.norm(cis_t1) # TODO: Comparing to the NTOs defined in JCP, 142, 244103. JCP, 142, 244103 # provides a method to incorporate the Y matrix in the transition density # matrix. However, it may break the point group symmetry of the NTO orbitals # when the system has degenerated irreducible representations. if mol.symmetry: orbsym = hf_symm.get_orbsym(mol, mo_coeff) o_sym = orbsym[mo_occ==2] v_sym = orbsym[mo_occ==0] nto_o = numpy.eye(nocc) nto_v = numpy.eye(nvir) weights_o = numpy.zeros(nocc) weights_v = numpy.zeros(nvir) for ir in set(orbsym): idx = numpy.where(o_sym == ir)[0] if idx.size > 0: dm_oo = numpy.dot(cis_t1[idx], cis_t1[idx].T) weights_o[idx], nto_o[idx[:,None],idx] = numpy.linalg.eigh(dm_oo) idx = numpy.where(v_sym == ir)[0] if idx.size > 0: dm_vv = numpy.dot(cis_t1[:,idx].T, cis_t1[:,idx]) weights_v[idx], nto_v[idx[:,None],idx] = numpy.linalg.eigh(dm_vv) # weights in descending order idx = numpy.argsort(-weights_o) weights_o = weights_o[idx] nto_o = nto_o[:,idx] o_sym = o_sym[idx] idx = numpy.argsort(-weights_v) weights_v = weights_v[idx] nto_v = nto_v[:,idx] v_sym = v_sym[idx] nto_orbsym = numpy.hstack((o_sym, v_sym)) if nocc < nvir: weights = weights_o else: weights = weights_v else: nto_o, w, nto_vT = numpy.linalg.svd(cis_t1) nto_v = nto_vT.conj().T weights = w**2 nto_orbsym = None idx = numpy.argmax(abs(nto_o.real), axis=0) nto_o[:,nto_o[idx,numpy.arange(nocc)].real<0] *= -1 idx = numpy.argmax(abs(nto_v.real), axis=0) nto_v[:,nto_v[idx,numpy.arange(nvir)].real<0] *= -1 occupied_nto = numpy.dot(orbo, nto_o) virtual_nto = numpy.dot(orbv, nto_v) nto_coeff = numpy.hstack((occupied_nto, virtual_nto)) if mol.symmetry: nto_coeff = lib.tag_array(nto_coeff, orbsym=nto_orbsym) log = logger.new_logger(tdobj, verbose) if log.verbose >= logger.INFO: log.info('State %d: %g eV NTO largest component %s', state_id+1, tdobj.e[state_id]*nist.HARTREE2EV, weights[0]) o_idx = numpy.where(abs(nto_o[:,0]) > threshold)[0] v_idx = numpy.where(abs(nto_v[:,0]) > threshold)[0] fmt = '%' + str(lib.param.OUTPUT_DIGITS) + 'f (MO #%d)' log.info(' occ-NTO: ' + ' + '.join([(fmt % (nto_o[i,0], i+MO_BASE)) for i in o_idx])) log.info(' vir-NTO: ' + ' + '.join([(fmt % (nto_v[i,0], i+MO_BASE+nocc)) for i in v_idx])) return weights, nto_coeff
def get_occ(mf, mo_energy=None, mo_coeff=None): '''Label the occupancies for each orbital. NOTE the occupancies are not assigned based on the orbital energy ordering. The first N orbitals are assigned to be occupied orbitals. Examples: >>> mol = gto.M(atom='H 0 0 0; O 0 0 1.1', spin=1) >>> mf = scf.hf.SCF(mol) >>> energy = numpy.array([-10., -1., 1, -2., 0, -3]) >>> mf.get_occ(energy) array([2, 2, 2, 2, 1, 0]) ''' if mo_energy is None: mo_energy = mf.mo_energy if getattr(mo_energy, 'mo_ea', None) is not None: mo_ea = mo_energy.mo_ea mo_eb = mo_energy.mo_eb else: mo_ea = mo_eb = mo_energy nmo = mo_ea.size mo_occ = numpy.zeros(nmo) if getattr(mf, 'nelec', None) is None: nelec = mf.mol.nelec else: nelec = mf.nelec ncore = nelec[1] nocc = nelec[0] nopen = abs(nocc - ncore) mo_occ = _fill_rohf_occ(mo_energy, mo_ea, mo_eb, ncore, nopen) if mf.verbose >= logger.INFO and nocc < nmo and ncore > 0: ehomo = max(mo_energy[mo_occ > 0]) elumo = min(mo_energy[mo_occ == 0]) if ehomo + 1e-3 > elumo: logger.warn(mf, 'H**O %.15g >= LUMO %.15g', ehomo, elumo) else: logger.info(mf, ' H**O = %.15g LUMO = %.15g', ehomo, elumo) if nopen > 0 and mf.verbose >= logger.DEBUG: core_idx = mo_occ == 2 open_idx = mo_occ == 1 vir_idx = mo_occ == 0 logger.debug( mf, ' Roothaan | alpha | beta' ) logger.debug(mf, ' Highest 2-occ = %18.15g | %18.15g | %18.15g', max(mo_energy[core_idx]), max(mo_ea[core_idx]), max(mo_eb[core_idx])) logger.debug(mf, ' Lowest 0-occ = %18.15g | %18.15g | %18.15g', min(mo_energy[vir_idx]), min(mo_ea[vir_idx]), min(mo_eb[vir_idx])) for i in numpy.where(open_idx)[0]: logger.debug(mf, ' 1-occ = %18.15g | %18.15g | %18.15g', mo_energy[i], mo_ea[i], mo_eb[i]) if mf.verbose >= logger.DEBUG: numpy.set_printoptions(threshold=nmo) logger.debug(mf, ' Roothaan mo_energy =\n%s', mo_energy) logger.debug1(mf, ' alpha mo_energy =\n%s', mo_ea) logger.debug1(mf, ' beta mo_energy =\n%s', mo_eb) numpy.set_printoptions(threshold=1000) return mo_occ
def init_guess_by_chkfile(mol, chkfile_name, project=None): '''Read SCF chkfile and make the density matrix for GHF initial guess. Kwargs: project : None or bool Whether to project chkfile's orbitals to the new basis. Note when the geometry of the chkfile and the given molecule are very different, this projection can produce very poor initial guess. In PES scanning, it is recommended to swith off project. If project is set to None, the projection is only applied when the basis sets of the chkfile's molecule are different to the basis sets of the given molecule (regardless whether the geometry of the two molecules are different). Note the basis sets are considered to be different if the two molecules are derived from the same molecule with different ordering of atoms. ''' from pyscf.scf import addons chk_mol, scf_rec = chkfile.load_scf(chkfile_name) if project is None: project = not gto.same_basis_set(chk_mol, mol) # Check whether the two molecules are similar enough def inertia_momentum(mol): im = gto.inertia_momentum(mol._atom, mol.atom_charges(), mol.atom_coords()) return scipy.linalg.eigh(im)[0] if abs(inertia_momentum(mol) - inertia_momentum(chk_mol)).sum() > 0.5: logger.warn( mol, "Large deviations found between the input " "molecule and the molecule from chkfile\n" "Initial guess density matrix may have large error.") if project: s = hf.get_ovlp(mol) def fproj(mo): if project: mo = addons.project_mo_nr2nr(chk_mol, mo, mol) norm = numpy.einsum('pi,pi->i', mo.conj(), s.dot(mo)) mo /= numpy.sqrt(norm) return mo nao = chk_mol.nao_nr() mo = scf_rec['mo_coeff'] mo_occ = scf_rec['mo_occ'] if hasattr(mo[0], 'ndim') and mo[0].ndim == 1: # RHF/GHF/DHF if nao * 2 == mo.shape[0]: # GHF or DHF if project: raise NotImplementedError('Project initial guess from ' 'different geometry') else: dm = hf.make_rdm1(mo, mo_occ) else: # RHF mo_coeff = fproj(mo) mo_occa = (mo_occ > 1e-8).astype(numpy.double) mo_occb = mo_occ - mo_occa dma, dmb = uhf.make_rdm1([mo_coeff] * 2, (mo_occa, mo_occb)) dm = scipy.linalg.block_diag(dma, dmb) else: #UHF if hasattr(mo[0][0], 'ndim') and mo[0][0].ndim == 2: # KUHF logger.warn( mol, 'k-point UHF results are found. Density matrix ' 'at Gamma point is used for the molecular SCF initial guess') mo = mo[0] dma, dmb = uhf.make_rdm1([fproj(mo[0]), fproj(mo[1])], mo_occ) dm = scipy.linalg.block_diag(dma, dmb) return dm
def nelectron_alpha(self, x): logger.warn( self, 'WARN: Attribute .nelectron_alpha is deprecated. ' 'Set .nelec instead') #raise RuntimeError('API updates') self.nelec = (x, self.mol.nelectron - x)
def get_jk(self, dm_kpts, hermi=1, kpts=None, kpts_band=None, with_j=True, with_k=True, omega=None, exxdiv=None): if omega is not None: # J/K for RSH functionals # TODO: call AFTDF.get_jk function raise NotImplementedError # Does not support to specify arbitrary kpts if kpts is not None and abs(kpts - self.kpts).max() > 1e-7: raise RuntimeError('kpts error') kpts = self.kpts if kpts_band is not None: raise NotImplementedError cpu0 = (time.clock(), time.time()) if self.supmol is None: self.build() nkpts = kpts.shape[0] vhfopt = self.vhfopt supmol = self.supmol bvkcell = self.bvkcell phase = self.phase cell = self.cell_rs nao = cell.nao orig_nao = self.cell.nao # * dense_bvk_ao_loc are the AOs which appear in supmol (some basis # are removed) # * sparse_ao_loc has dimension (Nk,nbas), corresponding to the # bvkcell with all basis dense_bvk_ao_loc = bvkcell.ao_loc sparse_ao_loc = nao * np.arange(nkpts)[:, None] + cell.ao_loc[:-1] sparse_ao_loc = np.append(sparse_ao_loc.ravel(), nao * nkpts) nbands = nkpts if dm_kpts.ndim != 4: dm = dm_kpts.reshape(-1, nkpts, orig_nao, orig_nao) else: dm = dm_kpts n_dm = dm.shape[0] rs_c_coeff = cell._contr_coeff sc_dm = lib.einsum('nkij,pi,qj->nkpq', dm, rs_c_coeff, rs_c_coeff) # Utilized symmetry sc_dm[R,S] = sc_dm[S-R] = sc_dm[(S-R)%N] #:sc_dm = lib.einsum('Rk,nkuv,Sk->nRuSv', phase, sc_dm, phase.conj()) sc_dm = lib.einsum('k,Sk,nkuv->nSuv', phase[0], phase.conj(), sc_dm) dm_translation = k2gamma.double_translation_indices( self.bvk_kmesh).astype(np.int32) dm_imag_max = abs(sc_dm.imag).max() is_complex_dm = dm_imag_max > 1e-6 if is_complex_dm: if dm_imag_max < 1e-2: logger.warn( self, 'DM in (BvK) cell has small imaginary part. ' 'It may be a signal of symmetry broken in k-point symmetry' ) sc_dm = np.vstack([sc_dm.real, sc_dm.imag]) else: sc_dm = sc_dm.real sc_dm = np.asarray(sc_dm.reshape(-1, nkpts, nao, nao), order='C') n_sc_dm = sc_dm.shape[0] dm_cond = [ lib.condense('NP_absmax', d, sparse_ao_loc, sparse_ao_loc[:cell.nbas + 1]) for d in sc_dm ] dm_cond = np.asarray(np.max(dm_cond, axis=0), order='C') libpbc.CVHFset_dm_cond(vhfopt._this, dm_cond.ctypes.data_as(ctypes.c_void_p), dm_cond.size) dm_cond = None bvk_nbas = bvkcell.nbas shls_slice = (0, cell.nbas, 0, bvk_nbas, 0, bvk_nbas, 0, bvk_nbas) if hermi: fdot_suffix = 's2kl' else: fdot_suffix = 's1' if with_j and with_k: fdot = 'PBCVHF_contract_jk_' + fdot_suffix vs = np.zeros((2, n_sc_dm, nao, nkpts, nao)) elif with_j: fdot = 'PBCVHF_contract_j_' + fdot_suffix vs = np.zeros((1, n_sc_dm, nao, nkpts, nao)) else: # with_k fdot = 'PBCVHF_contract_k_' + fdot_suffix vs = np.zeros((1, n_sc_dm, nao, nkpts, nao)) drv = libpbc.PBCVHF_direct_drv drv(getattr(libpbc, fdot), vs.ctypes.data_as(ctypes.c_void_p), sc_dm.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(n_dm), ctypes.c_int(nkpts), ctypes.c_int(nbands), ctypes.c_int(cell.nbas), self.ovlp_mask.ctypes.data_as(ctypes.c_void_p), self.bvk_cell_id.ctypes.data_as(ctypes.c_void_p), self.cell0_shl_id.ctypes.data_as(ctypes.c_void_p), supmol._images_loc.ctypes.data_as(ctypes.c_void_p), (ctypes.c_int * 8)(*shls_slice), dense_bvk_ao_loc.ctypes.data_as(ctypes.c_void_p), dm_translation.ctypes.data_as(ctypes.c_void_p), vhfopt._cintopt, vhfopt._this, supmol._atm.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(supmol.natm), supmol._bas.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(supmol.nbas), supmol._env.ctypes.data_as(ctypes.c_void_p)) if is_complex_dm: vs = vs[:, :n_dm] + vs[:, n_dm:] * 1j if with_j and with_k: vj, vk = vs elif with_j: vj, vk = vs[0], None else: vj, vk = None, vs[0] cpu1 = logger.timer(self, 'short range part vj and vk', *cpu0) lr_c_coeff = self.lr_aft.cell._contr_coeff lr_dm = lib.einsum('nkij,pi,qj->nkpq', dm, lr_c_coeff, lr_c_coeff) # For rho product other than diffused-diffused block, construct LR # parts in terms of full ERIs and SR ERIs vj1, vk1 = self.lr_aft.get_jk(lr_dm, hermi, kpts, kpts_band, with_j, with_k, exxdiv=exxdiv) cpu1 = logger.timer(self, 'AFT-vj and AFT-vk', *cpu1) # expRk is almost the same to phase, except a normalization factor expRk = np.exp(1j * np.dot(self.bvkmesh_Ls, kpts.T)) if with_j: vj = lib.einsum('npRq,pi,qj,Rk->nkij', vj, rs_c_coeff, rs_c_coeff, expRk) vj += lib.einsum('nkpq,pi,qj->nkij', vj1, lr_c_coeff, lr_c_coeff) if self.purify and kpts_band is None: vj = _purify(vj, phase) if gamma_point(kpts) and dm_kpts.dtype == np.double: vj = vj.real if hermi: vj = (vj + vj.conj().transpose(0, 1, 3, 2)) * .5 vj = vj.reshape(dm_kpts.shape) if with_k: vk = lib.einsum('npRq,pi,qj,Rk->nkij', vk, rs_c_coeff, rs_c_coeff, expRk) vk += lib.einsum('nkpq,pi,qj->nkij', vk1, lr_c_coeff, lr_c_coeff) if self.purify and kpts_band is None: vk = _purify(vk, phase) if gamma_point(kpts) and dm_kpts.dtype == np.double: vk = vk.real if hermi: vk = (vk + vk.conj().transpose(0, 1, 3, 2)) * .5 vk = vk.reshape(dm_kpts.shape) return vj, vk
def kernel(mf, mo_coeff=None, mo_occ=None, dm=None, conv_tol=1e-10, conv_tol_grad=None, max_cycle=50, dump_chk=True, callback=None, verbose=logger.NOTE): cput0 = (time.clock(), time.time()) log = logger.new_logger(mf, verbose) mol = mf._scf.mol if mol != mf.mol: logger.warn( mf, 'dual-basis SOSCF is an experimental feature. It is ' 'still in testing.') if conv_tol_grad is None: conv_tol_grad = numpy.sqrt(conv_tol) log.info('Set conv_tol_grad to %g', conv_tol_grad) # call mf._scf.get_hcore, mf._scf.get_ovlp because they might be overloaded h1e = mf._scf.get_hcore(mol) s1e = mf._scf.get_ovlp(mol) if mo_coeff is not None and mo_occ is not None: dm = mf.make_rdm1(mo_coeff, mo_occ) # call mf._scf.get_veff, to avoid "newton().density_fit()" polluting get_veff vhf = mf._scf.get_veff(mol, dm) fock = mf.get_fock(h1e, s1e, vhf, dm, level_shift_factor=0) mo_energy, mo_tmp = mf.eig(fock, s1e) mf.get_occ(mo_energy, mo_tmp) mo_tmp = None else: if dm is None: logger.debug( mf, 'Initial guess density matrix is not given. ' 'Generating initial guess from %s', mf.init_guess) dm = mf.get_init_guess(mf._scf.mol, mf.init_guess) vhf = mf._scf.get_veff(mol, dm) fock = mf.get_fock(h1e, s1e, vhf, dm, level_shift_factor=0) mo_energy, mo_coeff = mf.eig(fock, s1e) mo_occ = mf.get_occ(mo_energy, mo_coeff) dm, dm_last = mf.make_rdm1(mo_coeff, mo_occ), dm vhf = mf._scf.get_veff(mol, dm, dm_last=dm_last, vhf_last=vhf) # Save mo_coeff and mo_occ because they are needed by function rotate_mo mf.mo_coeff, mf.mo_occ = mo_coeff, mo_occ e_tot = mf._scf.energy_tot(dm, h1e, vhf) fock = mf.get_fock(h1e, s1e, vhf, dm, level_shift_factor=0) log.info('Initial guess E= %.15g |g|= %g', e_tot, numpy.linalg.norm(mf._scf.get_grad(mo_coeff, mo_occ, fock))) if dump_chk and mf.chkfile: chkfile.save_mol(mol, mf.chkfile) # Copy the integral file to soscf object to avoid the integrals being cached # twice. if mol is mf.mol and not getattr(mf, 'with_df', None): mf._eri = mf._scf._eri # If different direct_scf_cutoff is assigned to newton_ah mf.opt # object, mf.opt should be different to mf._scf.opt #mf.opt = mf._scf.opt rotaiter = _rotate_orb_cc(mf, h1e, s1e, conv_tol_grad, verbose=log) next(rotaiter) # start the iterator kftot = jktot = 0 scf_conv = False cput1 = log.timer('initializing second order scf', *cput0) for imacro in range(max_cycle): u, g_orb, kfcount, jkcount, dm_last, vhf = \ rotaiter.send((mo_coeff, mo_occ, dm, vhf, e_tot)) kftot += kfcount + 1 jktot += jkcount + 1 last_hf_e = e_tot norm_gorb = numpy.linalg.norm(g_orb) mo_coeff = mf.rotate_mo(mo_coeff, u, log) dm = mf.make_rdm1(mo_coeff, mo_occ) vhf = mf._scf.get_veff(mol, dm, dm_last=dm_last, vhf_last=vhf) fock = mf.get_fock(h1e, s1e, vhf, dm, level_shift_factor=0) # NOTE: DO NOT change the initial guess mo_occ, mo_coeff if mf.verbose >= logger.DEBUG: mo_energy, mo_tmp = mf.eig(fock, s1e) mf.get_occ(mo_energy, mo_tmp) # call mf._scf.energy_tot for dft, because the (dft).get_veff step saved _exc in mf._scf e_tot = mf._scf.energy_tot(dm, h1e, vhf) log.info('macro= %d E= %.15g delta_E= %g |g|= %g %d KF %d JK', imacro, e_tot, e_tot - last_hf_e, norm_gorb, kfcount + 1, jkcount) cput1 = log.timer('cycle= %d' % (imacro + 1), *cput1) if callable(mf.check_convergence): scf_conv = mf.check_convergence(locals()) elif abs(e_tot - last_hf_e) < conv_tol and norm_gorb < conv_tol_grad: scf_conv = True if dump_chk: mf.dump_chk(locals()) if callable(callback): callback(locals()) if scf_conv: break if callable(callback): callback(locals()) rotaiter.close() mo_energy, mo_coeff1 = mf._scf.canonicalize(mo_coeff, mo_occ, fock) if mf.canonicalization: log.info('Canonicalize SCF orbitals') mo_coeff = mo_coeff1 if dump_chk: mf.dump_chk(locals()) log.info('macro X = %d E=%.15g |g|= %g total %d KF %d JK', imacro + 1, e_tot, norm_gorb, kftot + 1, jktot + 1) if (numpy.any(mo_occ == 0) and mo_energy[mo_occ > 0].max() > mo_energy[mo_occ == 0].min()): log.warn('H**O %s > LUMO %s was found in the canonicalized orbitals.', mo_energy[mo_occ > 0].max(), mo_energy[mo_occ == 0].min()) return scf_conv, e_tot, mo_energy, mo_coeff, mo_occ
def get_pp_loc_part1(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[:cell.dimension] < mesh_guess[:cell.dimension] * .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) 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, mesh), axis=0) if cell.dimension < 2 or cell.low_dim_ft_type == 'inf_vacuum': mesh[:cell.dimension] = mesh_min[:cell.dimension] else: mesh = mesh_min Gv, Gvbase, kws = cell.get_Gv_weights(mesh) nuccell = _compensate_nuccell(mydf) # 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 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)
def dip_moment(cell, dm, unit='Debye', verbose=logger.NOTE, grids=None, rho=None, kpt=np.zeros(3), origin=None): ''' Dipole moment in the unit cell (is it well defined)? Args: cell : an instance of :class:`Cell` dm (ndarray) : density matrix Return: A list: the dipole moment on x, y and z components ''' from pyscf.pbc import gto from pyscf.pbc import tools from pyscf.pbc.dft import gen_grid from pyscf.pbc.dft import numint if cell.dimension != 3: # raise NotImplementedError logger.warn( cell, 'Dipole moment for low-dimension system is not supported.') return np.zeros(3) log = logger.new_logger(cell, verbose) a = cell.lattice_vectors() b = np.linalg.inv(a).T if grids is None: grids = gen_grid.UniformGrids(cell) #? FIXME: Less requirements on the density accuracy. #ke_cutoff = gto.estimate_ke_cutoff(cell, 1e-5) #grids.mesh = tools.cutoff_to_mesh(a, ke_cutoff) if rho is None: rho = numint.NumInt().get_rho(cell, dm, grids, kpt, cell.max_memory) if origin is None: origin = _search_dipole_gauge_origin(cell, grids, rho, log) # Move the unit cell to the position around the origin. def shift_grids(r): r_frac = lib.dot(r - origin, b.T) # Grids on the boundary (r_frac == +/-0.5) of the new cell may lead to # unbalanced contributions to the dipole moment. Exclude them from the # dipole and quadrupole r_frac[r_frac == 0.5] = 0 r_frac[r_frac == -0.5] = 0 r_frac[r_frac > 0.5] -= 1 r_frac[r_frac < -0.5] += 1 r = lib.dot(r_frac, a) return r r = shift_grids(grids.coords) e_dip = np.einsum('g,g,gx->x', rho, grids.weights, r) charges = cell.atom_charges() r = shift_grids(cell.atom_coords()) nuc_dip = np.einsum('g,gx->x', charges, r) dip = nuc_dip - e_dip if unit.upper() == 'DEBYE': dip *= nist.AU2DEBYE log.note('Dipole moment(X, Y, Z, Debye): %8.5f, %8.5f, %8.5f', *dip) else: log.note('Dipole moment(X, Y, Z, A.U.): %8.5f, %8.5f, %8.5f', *dip) return dip
def eaccsd_star_contract(eom, eaccsd_evals, eaccsd_evecs, leaccsd_evecs, imds=None): """ Returns: e_star (list of float): The EA-CCSD* energy. Notes: See `ipccsd_star_contract` for description of arguments. Reference: Saeh, Stanton "...energy surfaces of radicals" JCP 111, 8275 (1999); DOI:10.1063/1.480171 """ assert eom.partition is None if imds is None: imds = eom.make_imds() t1, t2 = imds.t1, imds.t2 eris = imds.eris assert isinstance(eris, gccsd._PhysicistsERIs) fock = eris.fock nocc, nvir = t1.shape nmo = nocc + nvir #fov = fock[:nocc, nocc:].diagonal() foo = fock[:nocc, :nocc].diagonal() fvv = fock[nocc:, nocc:].diagonal() vvvv = _cp(eris.vvvv) oovv = _cp(eris.oovv) ovvv = _cp(eris.ovvv) ovov = _cp(eris.ovov) #ovvo = -_cp(eris.ovov).transpose(0,1,3,2) ooov = _cp(eris.ooov) vooo = _cp(ooov).conj().transpose(3, 2, 1, 0) vvvo = _cp(ovvv).conj().transpose(3, 2, 1, 0) # Create denominator eabc = fvv[:, None, None] + fvv[None, :, None] + fvv[None, None, :] eij = foo[:, None] + foo[None, :] eijabc = eij[:, :, None, None, None] - eabc[None, None, :, :, :] # Permutation operators def pabc(tmp): '''P(abc)''' return tmp + tmp.transpose(0, 1, 3, 4, 2) + tmp.transpose( 0, 1, 4, 2, 3) def pij(tmp): '''P(ij)''' return tmp - tmp.transpose(1, 0, 2, 3, 4) def pab(tmp): '''P(ab)''' return tmp - tmp.transpose(0, 1, 3, 2, 4) eaccsd_evecs = np.array(eaccsd_evecs) leaccsd_evecs = np.array(leaccsd_evecs) e_star = [] eaccsd_evecs, leaccsd_evecs = [ np.atleast_2d(x) for x in [eaccsd_evecs, leaccsd_evecs] ] eaccsd_evals = np.atleast_1d(eaccsd_evals) for ea_eval, ea_evec, ea_levec in zip(eaccsd_evals, eaccsd_evecs, leaccsd_evecs): # Enforcing <L|R> = 1 l1, l2 = vector_to_amplitudes_ea(ea_levec, nmo, nocc) r1, r2 = vector_to_amplitudes_ea(ea_evec, nmo, nocc) ldotr = np.dot(l1, r1) + 0.5 * np.dot(l2.ravel(), r2.ravel()) logger.info(eom, 'Left-right amplitude overlap : %14.8e', ldotr) if abs(ldotr) < 1e-7: logger.warn( eom, 'Small %s left-right amplitude overlap. Results ' 'may be inaccurate.', ldotr) l1 /= ldotr l2 /= ldotr # Denominator + eigenvalue(EA-CCSD) denom = eijabc + ea_eval denom = 1. / denom tmp = lib.einsum('c,ijab->ijabc', l1, oovv) lijabc = -pabc(tmp) tmp = lib.einsum('jima,mbc->ijabc', ooov, l2) lijabc += -pabc(tmp) tmp = lib.einsum('ieab,jce->ijabc', ovvv, l2) tmp = pabc(tmp) lijabc += -pij(tmp) tmp = lib.einsum('bcef,f->bce', vvvv, r1) tmp = lib.einsum('bce,ijae->ijabc', tmp, t2) rijabc = -pabc(tmp) tmp = lib.einsum('mcje,e->mcj', ovov, r1) tmp = lib.einsum('mcj,imab->ijabc', tmp, t2) tmp = pabc(tmp) rijabc += pij(tmp) tmp = lib.einsum('amij,mcb->ijabc', vooo, r2) rijabc += pabc(tmp) tmp = lib.einsum('baei,jce->ijabc', vvvo, r2) tmp = pabc(tmp) rijabc -= pij(tmp) deltaE = (1. / 12) * lib.einsum('ijabc,ijabc,ijabc', lijabc, rijabc, denom) deltaE = deltaE.real logger.info(eom, "Exc. energy, delta energy = %16.12f, %16.12f", ea_eval + deltaE, deltaE) e_star.append(ea_eval + deltaE) return e_star
def build(self, j_only=None, with_j3c=True, kpts_band=None): if self.kpts_band is not None: self.kpts_band = numpy.reshape(self.kpts_band, (-1, 3)) if kpts_band is not None: kpts_band = numpy.reshape(kpts_band, (-1, 3)) if self.kpts_band is None: self.kpts_band = kpts_band else: self.kpts_band = unique( numpy.vstack((self.kpts_band, kpts_band)))[0] self.check_sanity() self.dump_flags() self.auxcell = make_modrho_basis(self.cell, self.auxbasis, self.exp_to_discard) # Remove duplicated k-points. Duplicated kpts may lead to a buffer # located in incore.wrap_int3c larger than necessary. Integral code # only fills necessary part of the buffer, leaving some space in the # buffer unfilled. uniq_idx = unique(self.kpts)[1] kpts = numpy.asarray(self.kpts)[uniq_idx] if self.kpts_band is None: kband_uniq = numpy.zeros((0, 3)) else: kband_uniq = [ k for k in self.kpts_band if len(member(k, kpts)) == 0 ] if j_only is None: j_only = self._j_only if j_only: kall = numpy.vstack([kpts, kband_uniq]) kptij_lst = numpy.hstack((kall, kall)).reshape(-1, 2, 3) else: kptij_lst = [(ki, kpts[j]) for i, ki in enumerate(kpts) for j in range(i + 1)] kptij_lst.extend([(ki, kj) for ki in kband_uniq for kj in kpts]) kptij_lst.extend([(ki, ki) for ki in kband_uniq]) kptij_lst = numpy.asarray(kptij_lst) if with_j3c: if isinstance(self._cderi_to_save, str): cderi = self._cderi_to_save else: cderi = self._cderi_to_save.name if isinstance(self._cderi, str): if self._cderi == cderi and os.path.isfile(cderi): logger.warn( self, 'DF integrals in %s (specified by ' '._cderi) is overwritten by GDF ' 'initialization. ', cderi) else: logger.warn( self, 'Value of ._cderi is ignored. ' 'DF integrals will be saved in file %s .', cderi) self._cderi = cderi t1 = (logger.process_clock(), logger.perf_counter()) self._make_j3c(self.cell, self.auxcell, kptij_lst, cderi) t1 = logger.timer_debug1(self, 'j3c', *t1) return self
def build(self, mol=None): for irname in self.irrep_nelec: if irname not in self.mol.irrep_name: logger.warn(self, '!! No irrep %s', irname) return uhf.UHF.build(self, mol)
def get_coulG(cell, k=np.zeros(3), exx=False, mf=None, mesh=None, Gv=None, wrap_around=True, omega=None, **kwargs): '''Calculate the Coulomb kernel for all G-vectors, handling G=0 and exchange. Args: k : (3,) ndarray k-point exx : bool or str Whether this is an exchange matrix element. mf : instance of :class:`SCF` Returns: coulG : (ngrids,) ndarray The Coulomb kernel. mesh : (3,) ndarray of ints (= nx,ny,nz) The number G-vectors along each direction. omega : float Enable Coulomb kernel erf(|omega|*r12)/r12 if omega > 0 and erfc(|omega|*r12)/r12 if omega < 0. Note this parameter is slightly different to setting cell.omega for the treatment of exxdiv (at G0). cell.omega affects Ewald probe charge at G0. It is used mostly with RSH functional for the long-range part of HF exchange. This parameter is used by real-space JK builder which requires Ewald probe charge to be computed with regular Coulomb interaction (1/r12) while the rest coulG is scaled as long-range Coulomb kernel. ''' exxdiv = exx if isinstance(exx, str): exxdiv = exx elif exx and mf is not None: exxdiv = mf.exxdiv if mesh is None: mesh = cell.mesh if 'gs' in kwargs: warnings.warn('cell.gs is deprecated. It is replaced by cell.mesh,' 'the number of PWs (=2*gs+1) along each direction.') mesh = [2*n+1 for n in kwargs['gs']] if Gv is None: Gv = cell.get_Gv(mesh) if abs(k).sum() > 1e-9: kG = k + Gv else: kG = Gv equal2boundary = np.zeros(Gv.shape[0], dtype=bool) if wrap_around and abs(k).sum() > 1e-9: # Here we 'wrap around' the high frequency k+G vectors into their lower # frequency counterparts. Important if you want the gamma point and k-point # answers to agree b = cell.reciprocal_vectors() box_edge = np.einsum('i,ij->ij', np.asarray(mesh)//2+0.5, b) assert(all(np.linalg.solve(box_edge.T, k).round(9).astype(int)==0)) reduced_coords = np.linalg.solve(box_edge.T, kG.T).T.round(9) on_edge = reduced_coords.astype(int) if cell.dimension >= 1: equal2boundary |= reduced_coords[:,0] == 1 equal2boundary |= reduced_coords[:,0] ==-1 kG[on_edge[:,0]== 1] -= 2 * box_edge[0] kG[on_edge[:,0]==-1] += 2 * box_edge[0] if cell.dimension >= 2: equal2boundary |= reduced_coords[:,1] == 1 equal2boundary |= reduced_coords[:,1] ==-1 kG[on_edge[:,1]== 1] -= 2 * box_edge[1] kG[on_edge[:,1]==-1] += 2 * box_edge[1] if cell.dimension == 3: equal2boundary |= reduced_coords[:,2] == 1 equal2boundary |= reduced_coords[:,2] ==-1 kG[on_edge[:,2]== 1] -= 2 * box_edge[2] kG[on_edge[:,2]==-1] += 2 * box_edge[2] absG2 = np.einsum('gi,gi->g', kG, kG) if getattr(mf, 'kpts', None) is not None: kpts = mf.kpts else: kpts = k.reshape(1,3) Nk = len(kpts) if exxdiv == 'vcut_sph': # PRB 77 193110 Rc = (3*Nk*cell.vol/(4*np.pi))**(1./3) with np.errstate(divide='ignore',invalid='ignore'): coulG = 4*np.pi/absG2*(1.0 - np.cos(np.sqrt(absG2)*Rc)) coulG[absG2==0] = 4*np.pi*0.5*Rc**2 if cell.dimension < 3: raise NotImplementedError elif exxdiv == 'vcut_ws': # PRB 87, 165122 assert(cell.dimension == 3) if not getattr(mf, '_ws_exx', None): mf._ws_exx = precompute_exx(cell, kpts) exx_alpha = mf._ws_exx['alpha'] exx_kcell = mf._ws_exx['kcell'] exx_q = mf._ws_exx['q'] exx_vq = mf._ws_exx['vq'] with np.errstate(divide='ignore',invalid='ignore'): coulG = 4*np.pi/absG2*(1.0 - np.exp(-absG2/(4*exx_alpha**2))) coulG[absG2==0] = np.pi / exx_alpha**2 # Index k+Gv into the precomputed vq and add on gxyz = np.dot(kG, exx_kcell.lattice_vectors().T)/(2*np.pi) gxyz = gxyz.round(decimals=6).astype(int) mesh = np.asarray(exx_kcell.mesh) gxyz = (gxyz + mesh)%mesh qidx = (gxyz[:,0]*mesh[1] + gxyz[:,1])*mesh[2] + gxyz[:,2] #qidx = [np.linalg.norm(exx_q-kGi,axis=1).argmin() for kGi in kG] maxqv = abs(exx_q).max(axis=0) is_lt_maxqv = (abs(kG) <= maxqv).all(axis=1) coulG = coulG.astype(exx_vq.dtype) coulG[is_lt_maxqv] += exx_vq[qidx[is_lt_maxqv]] if cell.dimension < 3: raise NotImplementedError else: # Ewald probe charge method to get the leading term of the finite size # error in exchange integrals G0_idx = np.where(absG2==0)[0] if cell.dimension != 2 or cell.low_dim_ft_type == 'inf_vacuum': with np.errstate(divide='ignore'): coulG = 4*np.pi/absG2 coulG[G0_idx] = 0 elif cell.dimension == 2: # The following 2D analytical fourier transform is taken from: # R. Sundararaman and T. Arias PRB 87, 2013 b = cell.reciprocal_vectors() Ld2 = np.pi/np.linalg.norm(b[2]) Gz = kG[:,2] Gp = np.linalg.norm(kG[:,:2], axis=1) weights = 1. - np.cos(Gz*Ld2) * np.exp(-Gp*Ld2) with np.errstate(divide='ignore', invalid='ignore'): coulG = weights*4*np.pi/absG2 if len(G0_idx) > 0: coulG[G0_idx] = -2*np.pi*Ld2**2 #-pi*L_z^2/2 elif cell.dimension == 1: logger.warn(cell, 'No method for PBC dimension 1, dim-type %s.' ' cell.low_dim_ft_type="inf_vacuum" should be set.', cell.low_dim_ft_type) raise NotImplementedError # Carlo A. Rozzi, PRB 73, 205119 (2006) a = cell.lattice_vectors() # Rc is the cylindrical radius Rc = np.sqrt(cell.vol / np.linalg.norm(a[0])) / 2 Gx = abs(kG[:,0]) Gp = np.linalg.norm(kG[:,1:], axis=1) with np.errstate(divide='ignore', invalid='ignore'): weights = 1 + Gp*Rc * scipy.special.j1(Gp*Rc) * scipy.special.k0(Gx*Rc) weights -= Gx*Rc * scipy.special.j0(Gp*Rc) * scipy.special.k1(Gx*Rc) coulG = 4*np.pi/absG2 * weights # TODO: numerical integation # coulG[Gx==0] = -4*np.pi * (dr * r * scipy.special.j0(Gp*r) * np.log(r)).sum() if len(G0_idx) > 0: coulG[G0_idx] = -np.pi*Rc**2 * (2*np.log(Rc) - 1) # The divergent part of periodic summation of (ii|ii) integrals in # Coulomb integrals were cancelled out by electron-nucleus # interaction. The periodic part of (ii|ii) in exchange cannot be # cancelled out by Coulomb integrals. Its leading term is calculated # using Ewald probe charge (the function madelung below) if cell.dimension > 0 and exxdiv == 'ewald' and len(G0_idx) > 0: coulG[G0_idx] += Nk*cell.vol*madelung(cell, kpts) coulG[equal2boundary] = 0 # Scale the coulG kernel for attenuated Coulomb integrals. # * omega is used by RealSpaceJKBuilder which requires ewald probe charge # being evaluated with regular Coulomb interaction (1/r12). # * cell.omega, which affects the ewald probe charge, is often set by # DFT-RSH functionals to build long-range HF-exchange for erf(omega*r12)/r12 if omega is not None: if omega > 0: coulG *= np.exp(-.25/omega**2 * absG2) else: coulG *= (1 - np.exp(-.25/omega**2 * absG2)) elif cell.omega != 0: coulG *= np.exp(-.25/cell.omega**2 * absG2) return coulG
def kernel(gw, mo_energy, mo_coeff, td_e, td_xy, eris=None, orbs=None, verbose=logger.NOTE): '''GW-corrected quasiparticle orbital energies Returns: A list : converged, mo_energy, mo_coeff ''' # mf must be DFT; for HF use xc = 'hf' mf = gw._scf assert (isinstance(mf, (dft.rks.RKS, dft.uks.UKS, dft.roks.ROKS, dft.uks.UKS, dft.rks_symm.RKS, dft.uks_symm.UKS, dft.rks_symm.ROKS, dft.uks_symm.UKS))) assert (gw.frozen == 0 or gw.frozen is None) if eris is None: eris = gw.ao2mo(mo_coeff) if orbs is None: orbs = range(gw.nmo) v_mf = mf.get_veff() - mf.get_j() v_mf = reduce(numpy.dot, (mo_coeff.T, v_mf, mo_coeff)) nocc = gw.nocc nmo = gw.nmo nvir = nmo - nocc vk_oo = -np.einsum('piiq->pq', eris.oooo) vk_ov = -np.einsum('iqpi->pq', eris.ovoo) vk_vv = -np.einsum('ipqi->pq', eris.ovvo).conj() vk = np.array(np.bmat([[vk_oo, vk_ov], [vk_ov.T, vk_vv]])) nexc = len(td_e) # factor of 2 for normalization, see tdscf/rhf.py td_xy = 2 * np.asarray(td_xy) # (nexc, 2, nocc, nvir) td_z = np.sum(td_xy, axis=1).reshape(nexc, nocc, nvir) tdm_oo = einsum('via,iapq->vpq', td_z, eris.ovoo) tdm_ov = einsum('via,iapq->vpq', td_z, eris.ovov) tdm_vv = einsum('via,iapq->vpq', td_z, eris.ovvv) tdm = [] for oo, ov, vv in zip(tdm_oo, tdm_ov, tdm_vv): tdm.append(np.array(np.bmat([[oo, ov], [ov.T, vv]]))) tdm = np.asarray(tdm) conv = True mo_energy = np.zeros_like(gw._scf.mo_energy) for p in orbs: tdm_p = tdm[:, :, p] if gw.linearized: ep = gw._scf.mo_energy[p] sigma = get_sigma_element(gw, ep, tdm_p, tdm_p, td_e).real dsigma_dw = get_sigma_deriv_element(gw, ep, tdm_p, tdm_p, td_e).real zn = 1.0 / (1 - dsigma_dw) mo_energy[p] = ep + zn * (sigma.real + vk[p, p] - v_mf[p, p]) else: def quasiparticle(omega): sigma = get_sigma_element(gw, omega, tdm_p, tdm_p, td_e) return omega - gw._scf.mo_energy[p] - (sigma.real + vk[p, p] - v_mf[p, p]) try: mo_energy[p] = newton(quasiparticle, gw._scf.mo_energy[p], tol=1e-6, maxiter=100) except RuntimeError: conv = False mo_energy[p] = gw._scf.mo_energy[p] logger.warn( gw, 'Root finding for GW eigenvalue %s did not converge. ' 'Setting it equal to the reference MO energy.' % (p)) mo_coeff = gw._scf.mo_coeff if gw.verbose >= logger.DEBUG: numpy.set_printoptions(threshold=nmo) logger.debug(gw, ' GW mo_energy =\n%s', mo_energy) numpy.set_printoptions(threshold=1000) return conv, mo_energy, mo_coeff
def _make_eris(mp, mo_coeff=None, ao2mofn=None, verbose=None): log = logger.new_logger(mp, verbose) time0 = (time.clock(), time.time()) eris = _ChemistsERIs() eris._common_init_(mp, mo_coeff) nocca, noccb = mp.get_nocc() nmoa, nmob = mp.get_nmo() nvira, nvirb = nmoa - nocca, nmob - noccb nao = eris.mo_coeff[0].shape[0] nmo_pair = nmoa * (nmoa + 1) // 2 nao_pair = nao * (nao + 1) // 2 mem_incore = (nao_pair**2 + nmo_pair**2) * 8 / 1e6 mem_now = lib.current_memory()[0] max_memory = max(0, mp.max_memory - mem_now) moa = eris.mo_coeff[0] mob = eris.mo_coeff[1] orboa = moa[:, :nocca] orbob = mob[:, :noccb] orbva = moa[:, nocca:] orbvb = mob[:, noccb:] if (mp.mol.incore_anyway or (mp._scf._eri is not None and mem_incore + mem_now < mp.max_memory)): log.debug('transform (ia|jb) incore') if callable(ao2mofn): eris.ovov = ao2mofn( (orboa, orbva, orboa, orbva)).reshape(nocca * nvira, nocca * nvira) eris.ovOV = ao2mofn( (orboa, orbva, orbob, orbvb)).reshape(nocca * nvira, noccb * nvirb) eris.OVOV = ao2mofn( (orbob, orbvb, orbob, orbvb)).reshape(noccb * nvirb, noccb * nvirb) else: eris.ovov = ao2mo.general(mp._scf._eri, (orboa, orbva, orboa, orbva)) eris.ovOV = ao2mo.general(mp._scf._eri, (orboa, orbva, orbob, orbvb)) eris.OVOV = ao2mo.general(mp._scf._eri, (orbob, orbvb, orbob, orbvb)) elif getattr(mp._scf, 'with_df', None): logger.warn( mp, 'UMP2 detected DF being used in the HF object. ' 'MO integrals are computed based on the DF 3-index tensors.\n' 'It\'s recommended to use DF-UMP2 module.') log.debug('transform (ia|jb) with_df') eris.ovov = mp._scf.with_df.ao2mo((orboa, orbva, orboa, orbva)) eris.ovOV = mp._scf.with_df.ao2mo((orboa, orbva, orbob, orbvb)) eris.OVOV = mp._scf.with_df.ao2mo((orbob, orbvb, orbob, orbvb)) else: log.debug('transform (ia|jb) outcore') eris.feri = lib.H5TmpFile() _ao2mo_ovov(mp, (orboa, orbva, orbob, orbvb), eris.feri, max(2000, max_memory), log) eris.ovov = eris.feri['ovov'] eris.ovOV = eris.feri['ovOV'] eris.OVOV = eris.feri['OVOV'] time1 = log.timer('Integral transformation', *time0) return eris