def test_orth_ao(self): c0 = orth.pre_orth_ao(mol, method='scf') self.assertAlmostEqual(numpy.linalg.norm(c0), 5.7742626195362039, 9) self.assertAlmostEqual(abs(c0).sum(), 33.461490657433551, 8) c = orth.orth_ao(mol, 'lowdin', c0) self.assertAlmostEqual(numpy.linalg.norm(c), 8.9823854843222257, 9) self.assertAlmostEqual(abs(c).sum(), 94.933979307106767, 8) c = orth.orth_ao(mol, 'meta_lowdin', c0) self.assertAlmostEqual(numpy.linalg.norm(c), 8.9823854843222257, 9) self.assertAlmostEqual(abs(c).sum(), 93.029386338534394, 8)
def test_orth_ao(self): c0 = orth.pre_orth_ao(mol, method='scf') self.assertAlmostEqual(numpy.linalg.norm(c0), 7.2617698799320358, 9) self.assertAlmostEqual(abs(c0).sum(), 40.116080631662804, 8) c = orth.orth_ao(mol, 'lowdin', c0) self.assertAlmostEqual(numpy.linalg.norm(c), 10.967144073462256, 9) self.assertAlmostEqual(abs(c).sum(), 112.23459140302003, 8) c = orth.orth_ao(mol, 'meta_lowdin', c0) self.assertAlmostEqual(numpy.linalg.norm(c), 10.967144073462256, 9) self.assertAlmostEqual(abs(c).sum(), 111.61017124719302, 8)
def analyze(mf, verbose=logger.DEBUG, **kwargs): '''Analyze the given SCF object: print orbital energies, occupancies; print orbital coefficients; Mulliken population analysis ''' from pyscf.lo import orth from pyscf.tools import dump_mat mo_energy = mf.mo_energy mo_occ = mf.mo_occ mo_coeff = mf.mo_coeff if isinstance(verbose, logger.Logger): log = verbose else: log = logger.Logger(mf.stdout, verbose) log.note('**** MO energy ****') if mf._focka_ao is None: for i,c in enumerate(mo_occ): log.note('MO #%-3d energy= %-18.15g occ= %g', i+1, mo_energy[i], c) else: mo_ea = numpy.einsum('ik,ik->k', mo_coeff, mf._focka_ao.dot(mo_coeff)) mo_eb = numpy.einsum('ik,ik->k', mo_coeff, mf._fockb_ao.dot(mo_coeff)) log.note(' Roothaan | alpha | beta') for i,c in enumerate(mo_occ): log.note('MO #%-3d energy= %-18.15g | %-18.15g | %-18.15g occ= %g', i+1, mo_energy[i], mo_ea[i], mo_eb[i], c) ovlp_ao = mf.get_ovlp() if verbose >= logger.DEBUG: log.debug(' ** MO coefficients (expansion on meta-Lowdin AOs) **') label = mf.mol.spheric_labels(True) orth_coeff = orth.orth_ao(mf.mol, 'meta_lowdin', s=ovlp_ao) c = reduce(numpy.dot, (orth_coeff.T, ovlp_ao, mo_coeff)) dump_mat.dump_rec(mf.stdout, c, label, start=1, **kwargs) dm = mf.make_rdm1(mo_coeff, mo_occ) return mf.mulliken_meta(mf.mol, dm, s=s, verbose=log)
def analyze(mf, verbose=logger.DEBUG): '''Analyze the given SCF object: print orbital energies, occupancies; print orbital coefficients; Mulliken population analysis; Diople moment. ''' from pyscf.lo import orth from pyscf.tools import dump_mat mo_energy = mf.mo_energy mo_occ = mf.mo_occ mo_coeff = mf.mo_coeff if isinstance(verbose, logger.Logger): log = verbose else: log = logger.Logger(mf.stdout, verbose) log.note('**** MO energy ****') for i,c in enumerate(mo_occ): log.note('MO #%-3d energy= %-18.15g occ= %g', i+1, mo_energy[i], c) ovlp_ao = mf.get_ovlp() if verbose >= logger.DEBUG: log.debug(' ** MO coefficients (expansion on meta-Lowdin AOs) **') label = mf.mol.spheric_labels(True) orth_coeff = orth.orth_ao(mf.mol, 'meta_lowdin', s=ovlp_ao) c = reduce(numpy.dot, (orth_coeff.T, ovlp_ao, mo_coeff)) dump_mat.dump_rec(mf.stdout, c, label, start=1) dm = mf.make_rdm1(mo_coeff, mo_occ) return (mf.mulliken_meta(mf.mol, dm, s=ovlp_ao, verbose=log), mf.dip_moment(mf.mol, dm, verbose=log))
def mulliken_meta(cell, dm_ao_kpts, verbose=logger.DEBUG, pre_orth_method=PRE_ORTH_METHOD, s=None): '''A modified Mulliken population analysis, based on meta-Lowdin AOs. Note this function only computes the Mulliken population for the gamma point density matrix. ''' from pyscf.lo import orth if s is None: s = khf.get_ovlp(cell) log = logger.new_logger(cell, verbose) log.note('Analyze output for *gamma point*.') log.info(' To include the contributions from k-points, transform to a ' 'supercell then run the population analysis on the supercell\n' ' from pyscf.pbc.tools import k2gamma\n' ' k2gamma.k2gamma(mf).mulliken_meta()') log.note("KUHF mulliken_meta") dm_ao_gamma = dm_ao_kpts[:,0,:,:].real s_gamma = s[0,:,:].real c = orth.restore_ao_character(cell, pre_orth_method) orth_coeff = orth.orth_ao(cell, 'meta_lowdin', pre_orth_ao=c, s=s_gamma) c_inv = np.dot(orth_coeff.T, s_gamma) dm_a = reduce(np.dot, (c_inv, dm_ao_gamma[0], c_inv.T.conj())) dm_b = reduce(np.dot, (c_inv, dm_ao_gamma[1], c_inv.T.conj())) log.note(' ** Mulliken pop alpha/beta on meta-lowdin orthogonal AOs **') return mol_uhf.mulliken_pop(cell, (dm_a,dm_b), np.eye(orth_coeff.shape[0]), log)
def atomic_init_guess(mol, mo_coeff): s = mol.intor_symmetric('int1e_ovlp') c = orth.orth_ao(mol, s=s) mo = reduce(numpy.dot, (c.conj().T, s, mo_coeff)) nmo = mo_coeff.shape[1] # Find the AOs which have largest overlap to MOs idx = numpy.argsort(numpy.einsum('pi,pi->p', mo.conj(), mo)) nmo = mo.shape[1] idx = idx[-nmo:] u, w, vh = numpy.linalg.svd(mo[idx]) return lib.dot(vh, u.conj().T)
def test_ghost_atm_meta_lowdin(self): mol = gto.Mole() mol.atom = [["O" , (0. , 0. , 0.)], ['ghost' , (0. , -0.757, 0.587)], [1 , (0. , 0.757 , 0.587)] ] mol.spin = 1 mol.basis = {'O':'ccpvdz', 'H':'ccpvdz', 'GHOST': gto.basis.load('631g','H')} mol.build() c = orth.orth_ao(mol, method='meta_lowdin') self.assertAlmostEqual(numpy.linalg.norm(c), 7.9067188905237256, 9)
def analyze(mf, verbose=logger.DEBUG, **kwargs): '''Analyze the given SCF object: print orbital energies, occupancies; print orbital coefficients; Occupancy for each irreps; Mulliken population analysis ''' from pyscf.lo import orth from pyscf.tools import dump_mat mol = mf.mol if not mol.symmetry: return hf.analyze(mf, verbose, **kwargs) mo_energy = mf.mo_energy mo_occ = mf.mo_occ mo_coeff = mf.mo_coeff log = logger.Logger(mf.stdout, verbose) nirrep = len(mol.irrep_id) ovlp_ao = mf.get_ovlp() orbsym = symm.label_orb_symm(mol, mol.irrep_id, mol.symm_orb, mo_coeff, s=ovlp_ao, check=False) orbsym = numpy.array(orbsym) wfnsym = 0 noccs = [sum(orbsym[mo_occ>0]==ir) for ir in mol.irrep_id] log.note('total symmetry = %s', symm.irrep_id2name(mol.groupname, wfnsym)) log.note('occupancy for each irrep: ' + (' %4s'*nirrep), *mol.irrep_name) log.note('double occ ' + (' %4d'*nirrep), *noccs) log.note('**** MO energy ****') irname_full = {} for k,ir in enumerate(mol.irrep_id): irname_full[ir] = mol.irrep_name[k] irorbcnt = {} for k, j in enumerate(orbsym): if j in irorbcnt: irorbcnt[j] += 1 else: irorbcnt[j] = 1 log.note('MO #%d (%s #%d), energy= %.15g occ= %g', k+1, irname_full[j], irorbcnt[j], mo_energy[k], mo_occ[k]) if verbose >= logger.DEBUG: label = mol.spheric_labels(True) molabel = [] irorbcnt = {} for k, j in enumerate(orbsym): if j in irorbcnt: irorbcnt[j] += 1 else: irorbcnt[j] = 1 molabel.append('#%-d(%s #%d)' % (k+1, irname_full[j], irorbcnt[j])) log.debug(' ** MO coefficients (expansion on meta-Lowdin AOs) **') orth_coeff = orth.orth_ao(mol, 'meta_lowdin', s=ovlp_ao) c = reduce(numpy.dot, (orth_coeff.T, ovlp_ao, mo_coeff)) dump_mat.dump_rec(mf.stdout, c, label, molabel, start=1, **kwargs) dm = mf.make_rdm1(mo_coeff, mo_occ) return mf.mulliken_meta(mol, dm, s=ovlp_ao, verbose=log)
def atomic_init_guess(mol, mo_coeff): s = mol.intor_symmetric('int1e_ovlp') c = orth.orth_ao(mol, s=s) mo = reduce(numpy.dot, (c.conj().T, s, mo_coeff)) # Find the AOs which have largest overlap to MOs idx = numpy.argsort(numpy.einsum('pi,pi->p', mo.conj(), mo)) nmo = mo.shape[1] idx = sorted(idx[-nmo:]) # Rotate mo_coeff, make it as close as possible to AOs u, w, vh = numpy.linalg.svd(mo[idx]) return lib.dot(u, vh).conj().T
def analyze(mf, verbose=logger.DEBUG, with_meta_lowdin=WITH_META_LOWDIN, **kwargs): '''Analyze the given SCF object: print orbital energies, occupancies; print orbital coefficients; Mulliken population analysis; Dipole moment ''' from pyscf.lo import orth from pyscf.tools import dump_mat mo_energy = mf.mo_energy mo_occ = mf.mo_occ mo_coeff = mf.mo_coeff nmo = len(mo_occ[0]) log = logger.new_logger(mf, verbose) if log.verbose >= logger.NOTE: log.note('**** MO energy ****') log.note(' alpha | beta alpha | beta') for i in range(nmo): log.note('MO #%-3d energy= %-18.15g | %-18.15g occ= %g | %g', i+MO_BASE, mo_energy[0][i], mo_energy[1][i], mo_occ[0][i], mo_occ[1][i]) ovlp_ao = mf.get_ovlp() if log.verbose >= logger.DEBUG: label = mf.mol.ao_labels() if with_meta_lowdin: log.debug(' ** MO coefficients (expansion on meta-Lowdin AOs) for alpha spin **') orth_coeff = orth.orth_ao(mf.mol, 'meta_lowdin', s=ovlp_ao) c_inv = numpy.dot(orth_coeff.T, ovlp_ao) dump_mat.dump_rec(mf.stdout, c_inv.dot(mo_coeff[0]), label, start=MO_BASE, **kwargs) log.debug(' ** MO coefficients (expansion on meta-Lowdin AOs) for beta spin **') dump_mat.dump_rec(mf.stdout, c_inv.dot(mo_coeff[1]), label, start=MO_BASE, **kwargs) else: log.debug(' ** MO coefficients (expansion on AOs) for alpha spin **') dump_mat.dump_rec(mf.stdout, mo_coeff[0], label, start=MO_BASE, **kwargs) log.debug(' ** MO coefficients (expansion on AOs) for beta spin **') dump_mat.dump_rec(mf.stdout, mo_coeff[1], label, start=MO_BASE, **kwargs) dm = mf.make_rdm1(mo_coeff, mo_occ) if with_meta_lowdin: return (mf.mulliken_meta(mf.mol, dm, s=ovlp_ao, verbose=log), mf.dip_moment(mf.mol, dm, verbose=log)) else: return (mf.mulliken_pop(mf.mol, dm, s=ovlp_ao, verbose=log), mf.dip_moment(mf.mol, dm, verbose=log))
def mulliken_meta(mol, dm_ao, verbose=logger.DEBUG, pre_orth_method=PRE_ORTH_METHOD, s=None): '''Mulliken population analysis, based on meta-Lowdin AOs. ''' from pyscf.lo import orth if s is None: s = hf.get_ovlp(mol) log = logger.new_logger(mol, verbose) if isinstance(dm_ao, numpy.ndarray) and dm_ao.ndim == 2: dm_ao = numpy.array((dm_ao*.5, dm_ao*.5)) c = orth.restore_ao_character(mol, pre_orth_method) orth_coeff = orth.orth_ao(mol, 'meta_lowdin', pre_orth_ao=c, s=s) c_inv = numpy.dot(orth_coeff.T, s) dm_a = reduce(numpy.dot, (c_inv, dm_ao[0], c_inv.T.conj())) dm_b = reduce(numpy.dot, (c_inv, dm_ao[1], c_inv.T.conj())) log.note(' ** Mulliken pop alpha/beta on meta-lowdin orthogonal AOs **') return mulliken_pop(mol, (dm_a,dm_b), numpy.eye(orth_coeff.shape[0]), log)
def mulliken_pop_meta_lowdin_ao(mol, dm_ao, verbose=logger.DEBUG, pre_orth_method='ANO'): '''Mulliken population analysis, based on meta-Lowdin AOs. ''' from pyscf.lo import orth if isinstance(verbose, logger.Logger): log = verbose else: log = logger.Logger(mol.stdout, verbose) c = orth.pre_orth_ao(mol, pre_orth_method) orth_coeff = orth.orth_ao(mol, 'meta_lowdin', pre_orth_ao=c) c_inv = numpy.linalg.inv(orth_coeff) dm_a = reduce(numpy.dot, (c_inv, dm_ao[0], c_inv.T.conj())) dm_b = reduce(numpy.dot, (c_inv, dm_ao[1], c_inv.T.conj())) log.info(' ** Mulliken pop alpha/beta on meta-lowdin orthogonal AOs **') return mulliken_pop(mol, (dm_a,dm_b), numpy.eye(orth_coeff.shape[0]), log)
def analyze(mf, verbose=logger.DEBUG, with_meta_lowdin=WITH_META_LOWDIN, **kwargs): '''Analyze the given SCF object: print orbital energies, occupancies; print orbital coefficients; Mulliken population analysis ''' from pyscf.lo import orth from pyscf.tools import dump_mat mo_energy = mf.mo_energy mo_occ = mf.mo_occ mo_coeff = mf.mo_coeff log = logger.new_logger(mf, verbose) if log.verbose >= logger.NOTE: log.note('**** MO energy ****') if getattr(mo_energy, 'mo_ea', None) is not None: mo_ea = mo_energy.mo_ea mo_eb = mo_energy.mo_eb log.note(' Roothaan | alpha | beta') for i,c in enumerate(mo_occ): log.note('MO #%-3d energy= %-18.15g | %-18.15g | %-18.15g occ= %g', i+MO_BASE, mo_energy[i], mo_ea[i], mo_eb[i], c) else: for i,c in enumerate(mo_occ): log.note('MO #%-3d energy= %-18.15g occ= %g', i+MO_BASE, mo_energy[i], c) ovlp_ao = mf.get_ovlp() if log.verbose >= logger.DEBUG: label = mf.mol.ao_labels() if with_meta_lowdin: log.debug(' ** MO coefficients (expansion on meta-Lowdin AOs) **') orth_coeff = orth.orth_ao(mf.mol, 'meta_lowdin', s=ovlp_ao) c = reduce(numpy.dot, (orth_coeff.T, ovlp_ao, mo_coeff)) else: log.debug(' ** MO coefficients (expansion on AOs) **') c = mo_coeff dump_mat.dump_rec(mf.stdout, c, label, start=MO_BASE, **kwargs) dm = mf.make_rdm1(mo_coeff, mo_occ) if with_meta_lowdin: pop_and_charge = mf.mulliken_meta(mf.mol, dm, s=ovlp_ao, verbose=log) else: pop_and_charge = mf.mulliken_pop(mf.mol, dm, s=ovlp_ao, verbose=log) dip = mf.dip_moment(mf.mol, dm, verbose=log) return pop_and_charge, dip
def mulliken_meta(mol, dm, verbose=logger.DEBUG, pre_orth_method='ANO', s=None): '''Mulliken population analysis, based on meta-Lowdin AOs. In the meta-lowdin, the AOs are grouped in three sets: core, valence and Rydberg, the orthogonalization are carreid out within each subsets. Args: mol : an instance of :class:`Mole` dm : ndarray or 2-item list of ndarray Density matrix. ROHF dm is a 2-item list of 2D array Kwargs: verbose : int or instance of :class:`lib.logger.Logger` pre_orth_method : str Pre-orthogonalization, which localized GTOs for each atom. To obtain the occupied and unoccupied atomic shells, there are three methods | 'ano' : Project GTOs to ANO basis | 'minao' : Project GTOs to MINAO basis | 'scf' : Fraction-averaged RHF ''' from pyscf.lo import orth if s is None: s = get_ovlp(mol) if isinstance(verbose, logger.Logger): log = verbose else: log = logger.Logger(mol.stdout, verbose) c = orth.pre_orth_ao(mol, pre_orth_method) orth_coeff = orth.orth_ao(mol, 'meta_lowdin', pre_orth_ao=c, s=s) c_inv = numpy.dot(orth_coeff.T, s) if isinstance(dm, numpy.ndarray) and dm.ndim == 2: dm = reduce(numpy.dot, (c_inv, dm, c_inv.T.conj())) else: # ROHF dm = reduce(numpy.dot, (c_inv, dm[0]+dm[1], c_inv.T.conj())) log.info(' ** Mulliken pop on meta-lowdin orthogonal AOs **') return mulliken_pop(mol, dm, numpy.eye(orth_coeff.shape[0]), log)
def atomic_pops(mol, mo_coeff, method='meta_lowdin'): '''kwarg method can be one of mulliken, lowdin, meta_lowdin ''' s = mol.intor_symmetric('cint1e_ovlp_sph') nmo = mo_coeff.shape[1] proj = numpy.empty((mol.natm,nmo,nmo)) if method.lower() == 'mulliken': for i, (b0, b1, p0, p1) in enumerate(mol.offset_nr_by_atom()): csc = reduce(numpy.dot, (mo_coeff[p0:p1].T, s[p0:p1], mo_coeff)) proj[i] = (csc + csc.T) * .5 elif method.lower() in ('lowdin', 'meta_lowdin'): csc = reduce(numpy.dot, (mo_coeff.T, s, orth.orth_ao(mol, method, s=s))) for i, (b0, b1, p0, p1) in enumerate(mol.offset_nr_by_atom()): proj[i] = numpy.dot(csc[:,p0:p1], csc[:,p0:p1].T) else: raise KeyError('method = %s' % method) return proj
def mulliken_meta(mol, dm_ao, verbose=logger.DEBUG, pre_orth_method="ANO", s=None): """Mulliken population analysis, based on meta-Lowdin AOs. """ from pyscf.lo import orth if s is None: s = hf.get_ovlp(mol) if isinstance(verbose, logger.Logger): log = verbose else: log = logger.Logger(mol.stdout, verbose) if isinstance(dm_ao, numpy.ndarray) and dm_ao.ndim == 2: dm_ao = numpy.array((dm_ao * 0.5, dm_ao * 0.5)) c = orth.pre_orth_ao(mol, pre_orth_method) orth_coeff = orth.orth_ao(mol, "meta_lowdin", pre_orth_ao=c, s=s) c_inv = numpy.dot(orth_coeff.T, s) dm_a = reduce(numpy.dot, (c_inv, dm_ao[0], c_inv.T.conj())) dm_b = reduce(numpy.dot, (c_inv, dm_ao[1], c_inv.T.conj())) log.info(" ** Mulliken pop alpha/beta on meta-lowdin orthogonal AOs **") return mulliken_pop(mol, (dm_a, dm_b), numpy.eye(orth_coeff.shape[0]), log)
def analyze(mf, verbose=logger.DEBUG, **kwargs): '''Analyze the given SCF object: print orbital energies, occupancies; print orbital coefficients; Mulliken population analysis; Dipole moment ''' from pyscf.lo import orth from pyscf.tools import dump_mat mo_energy = mf.mo_energy mo_occ = mf.mo_occ mo_coeff = mf.mo_coeff if isinstance(verbose, logger.Logger): log = verbose else: log = logger.Logger(mf.stdout, verbose) ss, s = mf.spin_square((mo_coeff[0][:,mo_occ[0]>0], mo_coeff[1][:,mo_occ[1]>0]), mf.get_ovlp()) log.note('multiplicity <S^2> = %.8g 2S+1 = %.8g', ss, s) log.note('**** MO energy ****') log.note(' alpha | beta alpha | beta') for i in range(mo_occ.shape[1]): log.note('MO #%-3d energy= %-18.15g | %-18.15g occ= %g | %g', i+1, mo_energy[0][i], mo_energy[1][i], mo_occ[0][i], mo_occ[1][i]) ovlp_ao = mf.get_ovlp() if verbose >= logger.DEBUG: log.debug(' ** MO coefficients (expansion on meta-Lowdin AOs) for alpha spin **') label = mf.mol.spheric_labels(True) orth_coeff = orth.orth_ao(mf.mol, 'meta_lowdin', s=ovlp_ao) c_inv = numpy.dot(orth_coeff.T, ovlp_ao) dump_mat.dump_rec(mf.stdout, c_inv.dot(mo_coeff[0]), label, start=1, **kwargs) log.debug(' ** MO coefficients (expansion on meta-Lowdin AOs) for beta spin **') dump_mat.dump_rec(mf.stdout, c_inv.dot(mo_coeff[1]), label, start=1, **kwargs) dm = mf.make_rdm1(mo_coeff, mo_occ) return (mf.mulliken_meta(mf.mol, dm, s=ovlp_ao, verbose=log), mf.dip_moment(mf.mol, dm, verbose=log))
def analyze(self, mo_coeff=None, ci=None, verbose=None, large_ci_tol=LARGE_CI_TOL, with_meta_lowdin=WITH_META_LOWDIN, **kwargs): from pyscf.lo import orth from pyscf.tools import dump_mat if mo_coeff is None: mo_coeff = self.mo_coeff if ci is None: ci = self.ci log = logger.new_logger(self, verbose) nelecas = self.nelecas ncas = self.ncas ncore = self.ncore mocore_a = mo_coeff[0][:,:ncore[0]] mocas_a = mo_coeff[0][:,ncore[0]:ncore[0]+ncas] mocore_b = mo_coeff[1][:,:ncore[1]] mocas_b = mo_coeff[1][:,ncore[1]:ncore[1]+ncas] label = self.mol.ao_labels() if (isinstance(ci, (list, tuple)) and not isinstance(self.fcisolver, addons.StateAverageFCISolver)): log.warn('Mulitple states found in UCASCI solver. Density ' 'matrix of first state is generated in .analyze() function.') civec = ci[0] else: civec = ci casdm1a, casdm1b = self.fcisolver.make_rdm1s(civec, ncas, nelecas) dm1a = numpy.dot(mocore_a, mocore_a.T) dm1a += reduce(numpy.dot, (mocas_a, casdm1a, mocas_a.T)) dm1b = numpy.dot(mocore_b, mocore_b.T) dm1b += reduce(numpy.dot, (mocas_b, casdm1b, mocas_b.T)) if log.verbose >= logger.DEBUG2: log.debug2('alpha density matrix (on AO)') dump_mat.dump_tri(self.stdout, dm1a, label) log.debug2('beta density matrix (on AO)') dump_mat.dump_tri(self.stdout, dm1b, label) if log.verbose >= logger.INFO: ovlp_ao = self._scf.get_ovlp() occa, ucasa = self._eig(-casdm1a, ncore[0], ncore[0]+ncas) occb, ucasb = self._eig(-casdm1b, ncore[1], ncore[1]+ncas) log.info('Natural alpha-occupancy %s', str(-occa)) log.info('Natural beta-occupancy %s', str(-occb)) mocas_a = numpy.dot(mocas_a, ucasa) mocas_b = numpy.dot(mocas_b, ucasb) if with_meta_lowdin: orth_coeff = orth.orth_ao(self.mol, 'meta_lowdin', s=ovlp_ao) mocas_a = reduce(numpy.dot, (orth_coeff.T, ovlp_ao, mocas_a)) mocas_b = reduce(numpy.dot, (orth_coeff.T, ovlp_ao, mocas_b)) log.info('Natural alpha-orbital (expansion on meta-Lowdin AOs) in CAS space') dump_mat.dump_rec(log.stdout, mocas_a, label, start=1, **kwargs) log.info('Natural beta-orbital (expansion on meta-Lowdin AOs) in CAS space') dump_mat.dump_rec(log.stdout, mocas_b, label, start=1, **kwargs) else: log.info('Natural alpha-orbital (expansion on AOs) in CAS space') dump_mat.dump_rec(log.stdout, mocas_a, label, start=1, **kwargs) log.info('Natural beta-orbital (expansion on AOs) in CAS space') dump_mat.dump_rec(log.stdout, mocas_b, label, start=1, **kwargs) tol = getattr(__config__, 'mcscf_addons_map2hf_tol', 0.4) s = reduce(numpy.dot, (mo_coeff[0].T, ovlp_ao, self._scf.mo_coeff[0])) idx = numpy.argwhere(abs(s)>tol) for i,j in idx: log.info('alpha <mo-mcscf|mo-hf> %d %d %12.8f' % (i+1,j+1,s[i,j])) s = reduce(numpy.dot, (mo_coeff[1].T, ovlp_ao, self._scf.mo_coeff[1])) idx = numpy.argwhere(abs(s)>tol) for i,j in idx: log.info('beta <mo-mcscf|mo-hf> %d %d %12.8f' % (i+1,j+1,s[i,j])) if getattr(self.fcisolver, 'large_ci', None) and ci is not None: log.info('\n** Largest CI components **') if isinstance(ci, (tuple, list)): for i, state in enumerate(ci): log.info(' [alpha occ-orbitals] [beta occ-orbitals] state %-3d CI coefficient', i) res = self.fcisolver.large_ci(state, self.ncas, self.nelecas, large_ci_tol, return_strs=False) for c,ia,ib in res: log.info(' %-20s %-30s %.12f', ia, ib, c) else: log.info(' [alpha occ-orbitals] [beta occ-orbitals] CI coefficient') res = self.fcisolver.large_ci(ci, self.ncas, self.nelecas, large_ci_tol, return_strs=False) for c,ia,ib in res: log.info(' %-20s %-30s %.12f', ia, ib, c) return dm1a, dm1b
def cas_natorb(mc, mo_coeff=None, ci=None, eris=None, sort=False, casdm1=None, verbose=None, with_meta_lowdin=WITH_META_LOWDIN): '''Transform active orbitals to natrual orbitals, and update the CI wfn accordingly Args: mc : a CASSCF/CASCI object or RHF object Kwargs: sort : bool Sort natural orbitals wrt the occupancy. Returns: A tuple, the first item is natural orbitals, the second is updated CI coefficients, the third is the natural occupancy associated to the natural orbitals. ''' from pyscf.lo import orth from pyscf.tools import dump_mat from pyscf.tools.mo_mapping import mo_1to1map if mo_coeff is None: mo_coeff = mc.mo_coeff if ci is None: ci = mc.ci log = logger.new_logger(mc, verbose) ncore = mc.ncore ncas = mc.ncas nocc = ncore + ncas nelecas = mc.nelecas if casdm1 is None: casdm1 = mc.fcisolver.make_rdm1(ci, ncas, nelecas) # orbital symmetry is reserved in this _eig call occ, ucas = mc._eig(-casdm1, ncore, nocc) if sort: casorb_idx = numpy.argsort(occ.round(9), kind='mergesort') occ = occ[casorb_idx] ucas = ucas[:,casorb_idx] occ = -occ mo_occ = numpy.zeros(mo_coeff.shape[1]) mo_occ[:ncore] = 2 mo_occ[ncore:nocc] = occ mo_coeff1 = mo_coeff.copy() mo_coeff1[:,ncore:nocc] = numpy.dot(mo_coeff[:,ncore:nocc], ucas) if getattr(mo_coeff, 'orbsym', None) is not None: orbsym = numpy.copy(mo_coeff.orbsym) if sort: orbsym[ncore:nocc] = orbsym[ncore:nocc][casorb_idx] mo_coeff1 = lib.tag_array(mo_coeff1, orbsym=orbsym) fcivec = None if getattr(mc.fcisolver, 'transform_ci_for_orbital_rotation', None): if isinstance(ci, numpy.ndarray): fcivec = mc.fcisolver.transform_ci_for_orbital_rotation(ci, ncas, nelecas, ucas) elif (isinstance(ci, (list, tuple)) and all(isinstance(x[0], numpy.ndarray) for x in ci)): fcivec = [mc.fcisolver.transform_ci_for_orbital_rotation(x, ncas, nelecas, ucas) for x in ci] elif getattr(mc.fcisolver, 'states_transform_ci_for_orbital_rotation', None): fcivec = mc.fcisolver.states_transform_ci_for_orbital_rotation(ci, ncas, nelecas, ucas) if fcivec is None: log.info('FCI vector not available, call CASCI to update wavefunction') mocas = mo_coeff1[:,ncore:nocc] hcore = mc.get_hcore() dm_core = numpy.dot(mo_coeff1[:,:ncore]*2, mo_coeff1[:,:ncore].T) ecore = mc.energy_nuc() ecore+= numpy.einsum('ij,ji', hcore, dm_core) h1eff = reduce(numpy.dot, (mocas.T, hcore, mocas)) if getattr(eris, 'ppaa', None) is not None: ecore += eris.vhf_c[:ncore,:ncore].trace() h1eff += reduce(numpy.dot, (ucas.T, eris.vhf_c[ncore:nocc,ncore:nocc], ucas)) aaaa = ao2mo.restore(4, eris.ppaa[ncore:nocc,ncore:nocc,:,:], ncas) aaaa = ao2mo.incore.full(aaaa, ucas) else: if getattr(mc, 'with_df', None): raise NotImplementedError('cas_natorb for DFCASCI/DFCASSCF') corevhf = mc.get_veff(mc.mol, dm_core) ecore += numpy.einsum('ij,ji', dm_core, corevhf) * .5 h1eff += reduce(numpy.dot, (mocas.T, corevhf, mocas)) aaaa = ao2mo.kernel(mc.mol, mocas) # See label_symmetry_ function in casci_symm.py which initialize the # orbital symmetry information in fcisolver. This orbital symmetry # labels should be reordered to match the sorted active space orbitals. if sort and getattr(mo_coeff1, 'orbsym', None) is not None: mc.fcisolver.orbsym = mo_coeff1.orbsym[ncore:nocc] max_memory = max(400, mc.max_memory-lib.current_memory()[0]) e, fcivec = mc.fcisolver.kernel(h1eff, aaaa, ncas, nelecas, ecore=ecore, max_memory=max_memory, verbose=log) log.debug('In Natural orbital, CASCI energy = %s', e) if log.verbose >= logger.INFO: ovlp_ao = mc._scf.get_ovlp() # where_natorb gives the new locations of the natural orbitals where_natorb = mo_1to1map(ucas) log.debug('where_natorb %s', str(where_natorb)) log.info('Natural occ %s', str(occ)) if with_meta_lowdin: log.info('Natural orbital (expansion on meta-Lowdin AOs) in CAS space') label = mc.mol.ao_labels() orth_coeff = orth.orth_ao(mc.mol, 'meta_lowdin', s=ovlp_ao) mo_cas = reduce(numpy.dot, (orth_coeff.T, ovlp_ao, mo_coeff1[:,ncore:nocc])) else: log.info('Natural orbital (expansion on AOs) in CAS space') label = mc.mol.ao_labels() mo_cas = mo_coeff1[:,ncore:nocc] dump_mat.dump_rec(log.stdout, mo_cas, label, start=1) if mc._scf.mo_coeff is not None: s = reduce(numpy.dot, (mo_coeff1[:,ncore:nocc].T, mc._scf.get_ovlp(), mc._scf.mo_coeff)) idx = numpy.argwhere(abs(s)>.4) for i,j in idx: log.info('<CAS-nat-orb|mo-hf> %d %d %12.8f', ncore+i+1, j+1, s[i,j]) return mo_coeff1, fcivec, mo_occ
def cas_natorb(mc, mo_coeff=None, ci=None, eris=None, sort=False, casdm1=None, verbose=None, with_meta_lowdin=WITH_META_LOWDIN): '''Transform active orbitals to natrual orbitals, and update the CI wfn Args: mc : a CASSCF/CASCI object or RHF object Kwargs: sort : bool Sort natural orbitals wrt the occupancy. Returns: A tuple, the first item is natural orbitals, the second is updated CI coefficients, the third is the natural occupancy associated to the natural orbitals. ''' from pyscf.lo import orth from pyscf.tools import dump_mat from pyscf.tools.mo_mapping import mo_1to1map if mo_coeff is None: mo_coeff = mc.mo_coeff if ci is None: ci = mc.ci log = logger.new_logger(mc, verbose) ncore = mc.ncore ncas = mc.ncas nocc = ncore + ncas nelecas = mc.nelecas if casdm1 is None: casdm1 = mc.fcisolver.make_rdm1(ci, ncas, nelecas) # orbital symmetry is reserved in this _eig call occ, ucas = mc._eig(-casdm1, ncore, nocc) if sort: casorb_idx = numpy.argsort(occ.round(9), kind='mergesort') occ = occ[casorb_idx] ucas = ucas[:,casorb_idx] occ = -occ mo_occ = numpy.zeros(mo_coeff.shape[1]) mo_occ[:ncore] = 2 mo_occ[ncore:nocc] = occ mo_coeff1 = mo_coeff.copy() mo_coeff1[:,ncore:nocc] = numpy.dot(mo_coeff[:,ncore:nocc], ucas) if getattr(mo_coeff, 'orbsym', None) is not None: orbsym = numpy.copy(mo_coeff.orbsym) if sort: orbsym[ncore:nocc] = orbsym[ncore:nocc][casorb_idx] mo_coeff1 = lib.tag_array(mo_coeff1, orbsym=orbsym) if isinstance(ci, numpy.ndarray): fcivec = fci.addons.transform_ci_for_orbital_rotation(ci, ncas, nelecas, ucas) elif isinstance(ci, (tuple, list)) and isinstance(ci[0], numpy.ndarray): # for state-average eigenfunctions fcivec = [fci.addons.transform_ci_for_orbital_rotation(x, ncas, nelecas, ucas) for x in ci] else: log.info('FCI vector not available, call CASCI for wavefunction') mocas = mo_coeff1[:,ncore:nocc] hcore = mc.get_hcore() dm_core = numpy.dot(mo_coeff1[:,:ncore]*2, mo_coeff1[:,:ncore].T) ecore = mc.energy_nuc() ecore+= numpy.einsum('ij,ji', hcore, dm_core) h1eff = reduce(numpy.dot, (mocas.T, hcore, mocas)) if getattr(eris, 'ppaa', None) is not None: ecore += eris.vhf_c[:ncore,:ncore].trace() h1eff += reduce(numpy.dot, (ucas.T, eris.vhf_c[ncore:nocc,ncore:nocc], ucas)) aaaa = ao2mo.restore(4, eris.ppaa[ncore:nocc,ncore:nocc,:,:], ncas) aaaa = ao2mo.incore.full(aaaa, ucas) else: if getattr(mc, 'with_df', None): raise NotImplementedError('cas_natorb for DFCASCI/DFCASSCF') corevhf = mc.get_veff(mc.mol, dm_core) ecore += numpy.einsum('ij,ji', dm_core, corevhf) * .5 h1eff += reduce(numpy.dot, (mocas.T, corevhf, mocas)) aaaa = ao2mo.kernel(mc.mol, mocas) # See label_symmetry_ function in casci_symm.py which initialize the # orbital symmetry information in fcisolver. This orbital symmetry # labels should be reordered to match the sorted active space orbitals. if sort and getattr(mo_coeff1, 'orbsym', None) is not None: mc.fcisolver.orbsym = mo_coeff1.orbsym[ncore:nocc] max_memory = max(400, mc.max_memory-lib.current_memory()[0]) e, fcivec = mc.fcisolver.kernel(h1eff, aaaa, ncas, nelecas, ecore=ecore, max_memory=max_memory, verbose=log) log.debug('In Natural orbital, CASCI energy = %s', e) if log.verbose >= logger.INFO: ovlp_ao = mc._scf.get_ovlp() # where_natorb gives the new locations of the natural orbitals where_natorb = mo_1to1map(ucas) log.debug('where_natorb %s', str(where_natorb)) log.info('Natural occ %s', str(occ)) if with_meta_lowdin: log.info('Natural orbital (expansion on meta-Lowdin AOs) in CAS space') label = mc.mol.ao_labels() orth_coeff = orth.orth_ao(mc.mol, 'meta_lowdin', s=ovlp_ao) mo_cas = reduce(numpy.dot, (orth_coeff.T, ovlp_ao, mo_coeff1[:,ncore:nocc])) else: log.info('Natural orbital (expansion on AOs) in CAS space') label = mc.mol.ao_labels() mo_cas = mo_coeff1[:,ncore:nocc] dump_mat.dump_rec(log.stdout, mo_cas, label, start=1) if mc._scf.mo_coeff is not None: s = reduce(numpy.dot, (mo_coeff1[:,ncore:nocc].T, mc._scf.get_ovlp(), mc._scf.mo_coeff)) idx = numpy.argwhere(abs(s)>.4) for i,j in idx: log.info('<CAS-nat-orb|mo-hf> %d %d %12.8f', ncore+i+1, j+1, s[i,j]) return mo_coeff1, fcivec, mo_occ
def analyze(self, mo_coeff=None, ci=None, verbose=None, large_ci_tol=LARGE_CI_TOL, with_meta_lowdin=WITH_META_LOWDIN, **kwargs): from pyscf.lo import orth from pyscf.tools import dump_mat if mo_coeff is None: mo_coeff = self.mo_coeff if ci is None: ci = self.ci log = logger.new_logger(self, verbose) nelecas = self.nelecas ncas = self.ncas ncore = self.ncore mocore_a = mo_coeff[0][:, :ncore[0]] mocas_a = mo_coeff[0][:, ncore[0]:ncore[0] + ncas] mocore_b = mo_coeff[1][:, :ncore[1]] mocas_b = mo_coeff[1][:, ncore[1]:ncore[1] + ncas] label = self.mol.ao_labels() if (isinstance(ci, (list, tuple)) and not isinstance(self.fcisolver, addons.StateAverageFCISolver)): log.warn( 'Mulitple states found in UCASCI solver. Density ' 'matrix of first state is generated in .analyze() function.') civec = ci[0] else: civec = ci casdm1a, casdm1b = self.fcisolver.make_rdm1s(civec, ncas, nelecas) dm1a = numpy.dot(mocore_a, mocore_a.T) dm1a += reduce(numpy.dot, (mocas_a, casdm1a, mocas_a.T)) dm1b = numpy.dot(mocore_b, mocore_b.T) dm1b += reduce(numpy.dot, (mocas_b, casdm1b, mocas_b.T)) if log.verbose >= logger.DEBUG2: log.debug2('alpha density matrix (on AO)') dump_mat.dump_tri(self.stdout, dm1a, label) log.debug2('beta density matrix (on AO)') dump_mat.dump_tri(self.stdout, dm1b, label) if log.verbose >= logger.INFO: ovlp_ao = self._scf.get_ovlp() occa, ucasa = self._eig(-casdm1a, ncore[0], ncore[0] + ncas) occb, ucasb = self._eig(-casdm1b, ncore[1], ncore[1] + ncas) log.info('Natural alpha-occupancy %s', str(-occa)) log.info('Natural beta-occupancy %s', str(-occb)) mocas_a = numpy.dot(mocas_a, ucasa) mocas_b = numpy.dot(mocas_b, ucasb) if with_meta_lowdin: orth_coeff = orth.orth_ao(self.mol, 'meta_lowdin', s=ovlp_ao) mocas_a = reduce(numpy.dot, (orth_coeff.T, ovlp_ao, mocas_a)) mocas_b = reduce(numpy.dot, (orth_coeff.T, ovlp_ao, mocas_b)) log.info( 'Natural alpha-orbital (expansion on meta-Lowdin AOs) in CAS space' ) dump_mat.dump_rec(log.stdout, mocas_a, label, start=1, **kwargs) log.info( 'Natural beta-orbital (expansion on meta-Lowdin AOs) in CAS space' ) dump_mat.dump_rec(log.stdout, mocas_b, label, start=1, **kwargs) else: log.info( 'Natural alpha-orbital (expansion on AOs) in CAS space') dump_mat.dump_rec(log.stdout, mocas_a, label, start=1, **kwargs) log.info( 'Natural beta-orbital (expansion on AOs) in CAS space') dump_mat.dump_rec(log.stdout, mocas_b, label, start=1, **kwargs) tol = getattr(__config__, 'mcscf_addons_map2hf_tol', 0.4) s = reduce(numpy.dot, (mo_coeff[0].T, ovlp_ao, self._scf.mo_coeff[0])) idx = numpy.argwhere(abs(s) > tol) for i, j in idx: log.info('alpha <mo-mcscf|mo-hf> %d %d %12.8f' % (i + 1, j + 1, s[i, j])) s = reduce(numpy.dot, (mo_coeff[1].T, ovlp_ao, self._scf.mo_coeff[1])) idx = numpy.argwhere(abs(s) > tol) for i, j in idx: log.info('beta <mo-mcscf|mo-hf> %d %d %12.8f' % (i + 1, j + 1, s[i, j])) if hasattr(self.fcisolver, 'large_ci') and ci is not None: log.info('\n** Largest CI components **') if isinstance(ci, (tuple, list)): for i, state in enumerate(ci): log.info( ' [alpha occ-orbitals] [beta occ-orbitals] state %-3d CI coefficient', i) res = self.fcisolver.large_ci(state, self.ncas, self.nelecas, large_ci_tol, return_strs=False) for c, ia, ib in res: log.info(' %-20s %-30s %.12f', ia, ib, c) else: log.info( ' [alpha occ-orbitals] [beta occ-orbitals] CI coefficient' ) res = self.fcisolver.large_ci(ci, self.ncas, self.nelecas, large_ci_tol, return_strs=False) for c, ia, ib in res: log.info(' %-20s %-30s %.12f', ia, ib, c) return dm1a, dm1b
def cas_natorb(mc, mo_coeff=None, ci=None, eris=None, sort=False, casdm1=None, verbose=None): '''Transform active orbitals to natrual orbitals, and update the CI wfn Args: mc : a CASSCF/CASCI object or RHF object Kwargs: sort : bool Sort natural orbitals wrt the occupancy. Be careful with this option since the resultant natural orbitals might have the different symmetry to the irreps indicated by CASSCF.orbsym Returns: A tuple, the first item is natural orbitals, the second is updated CI coefficients, the third is the natural occupancy associated to the natural orbitals. ''' from pyscf.lo import orth from pyscf.tools import dump_mat from pyscf.tools.mo_mapping import mo_1to1map if isinstance(verbose, logger.Logger): log = verbose else: log = logger.Logger(mc.stdout, mc.verbose) if mo_coeff is None: mo_coeff = mc.mo_coeff if ci is None: ci = mc.ci ncore = mc.ncore ncas = mc.ncas nocc = ncore + ncas nelecas = mc.nelecas if casdm1 is None: casdm1 = mc.fcisolver.make_rdm1(ci, ncas, nelecas) # orbital symmetry is reserved in this _eig call occ, ucas = mc._eig(-casdm1, ncore, nocc) if sort: idx = numpy.argsort(occ) occ = occ[idx] ucas = ucas[:,idx] # restore phase # where_natorb gives the location of the natural orbital for the input cas # orbitals. gen_strings4orblist map thes sorted strings (on CAS orbital) to # the unsorted determinant strings (on natural orbital). e.g. (3o,2e) system # CAS orbital 1 2 3 # natural orbital 3 1 2 <= by mo_1to1map # CASorb-strings 0b011, 0b101, 0b110 # == (1,2), (1,3), (2,3) # natorb-strings (3,1), (3,2), (1,2) # == 0B101, 0B110, 0B011 <= by gen_strings4orblist # then argsort to translate the string representation to the address # [2(=0B011), 0(=0B101), 1(=0B110)] # to indicate which CASorb-strings address to be loaded in each natorb-strings slot where_natorb = mo_1to1map(ucas) for i, k in enumerate(where_natorb): if ucas[i,k] < 0: ucas[:,k] *= -1 occ = -occ mo_occ = numpy.zeros(mo_coeff.shape[1]) mo_occ[:ncore] = 2 mo_occ[ncore:nocc] = occ if isinstance(ci, numpy.ndarray): fcivec = fci.addons.transform_ci_for_orbital_rotation(ci, ncas, nelecas, ucas) elif isinstance(ci, (tuple, list)) and isinstance(ci[0], numpy.ndarray): # for state-average eigenfunctions fcivec = [fci.addons.transform_ci_for_orbital_rotation(x, ncas, nelecas, ucas) for x in ci] else: log.info('FCI vector not available, call CASCI for wavefunction') mocas = mo_coeff1[:,ncore:nocc] h1eff = reduce(numpy.dot, (mocas.T, mc.get_hcore(), mocas)) if eris is not None and hasattr(eris, 'ppaa'): h1eff += reduce(numpy.dot, (ucas.T, eris.vhf_c[ncore:nocc,ncore:nocc], ucas)) aaaa = ao2mo.restore(4, eris.ppaa[ncore:nocc,ncore:nocc,:,:], ncas) aaaa = ao2mo.incore.full(aaaa, ucas) else: dm_core = numpy.dot(mo_coeff[:,:ncore]*2, mo_coeff[:,:ncore].T) vj, vk = mc._scf.get_jk(mc.mol, dm_core) h1eff += reduce(numpy.dot, (mocas.T, vj-vk*.5, mocas)) aaaa = ao2mo.kernel(mc.mol, mocas) max_memory = max(400, mc.max_memory-lib.current_memory()[0]) e_cas, fcivec = mc.fcisolver.kernel(h1eff, aaaa, ncas, nelecas, max_memory=max_memory, verbose=log) log.debug('In Natural orbital, CI energy = %.12g', e_cas) mo_coeff1 = mo_coeff.copy() mo_coeff1[:,ncore:nocc] = numpy.dot(mo_coeff[:,ncore:nocc], ucas) if log.verbose >= logger.INFO: ovlp_ao = mc._scf.get_ovlp() log.debug('where_natorb %s', str(where_natorb)) log.info('Natural occ %s', str(occ)) log.info('Natural orbital (expansion on meta-Lowdin AOs) in CAS space') label = mc.mol.spheric_labels(True) orth_coeff = orth.orth_ao(mc.mol, 'meta_lowdin', s=ovlp_ao) mo_cas = reduce(numpy.dot, (orth_coeff.T, ovlp_ao, mo_coeff1[:,ncore:nocc])) dump_mat.dump_rec(log.stdout, mo_cas, label, start=1) if mc._scf.mo_coeff is not None: s = reduce(numpy.dot, (mo_coeff1[:,ncore:nocc].T, mc._scf.get_ovlp(), mc._scf.mo_coeff)) idx = numpy.argwhere(abs(s)>.4) for i,j in idx: log.info('<CAS-nat-orb|mo-hf> %d %d %12.8f', ncore+i+1, j+1, s[i,j]) return mo_coeff1, fcivec, mo_occ
def analyze(self, verbose=None, **kwargs): if verbose is None: verbose = self.verbose from pyscf.lo import orth from pyscf.tools import dump_mat if not self.mol.symmetry: return rohf.ROHF.analyze(self, verbose, **kwargs) mo_energy = self.mo_energy mo_occ = self.mo_occ mo_coeff = self.mo_coeff log = logger.Logger(self.stdout, verbose) mol = self.mol nirrep = len(mol.irrep_id) ovlp_ao = self.get_ovlp() orbsym = get_orbsym(self.mol, mo_coeff) wfnsym = 0 ndoccs = [] nsoccs = [] for k,ir in enumerate(mol.irrep_id): ndoccs.append(sum(orbsym[mo_occ==2] == ir)) nsoccs.append(sum(orbsym[mo_occ==1] == ir)) if nsoccs[k] % 2: wfnsym ^= ir if mol.groupname in ('Dooh', 'Coov'): log.info('TODO: total symmetry for %s', mol.groupname) else: log.info('total symmetry = %s', symm.irrep_id2name(mol.groupname, wfnsym)) log.info('occupancy for each irrep: ' + (' %4s'*nirrep), *mol.irrep_name) log.info('double occ ' + (' %4d'*nirrep), *ndoccs) log.info('single occ ' + (' %4d'*nirrep), *nsoccs) log.info('**** MO energy ****') irname_full = {} for k,ir in enumerate(mol.irrep_id): irname_full[ir] = mol.irrep_name[k] irorbcnt = {} if self._focka_ao is None: for k, j in enumerate(orbsym): if j in irorbcnt: irorbcnt[j] += 1 else: irorbcnt[j] = 1 log.note('MO #%-3d (%s #%-2d), energy= %-18.15g occ= %g', k+1, irname_full[j], irorbcnt[j], mo_energy[k], mo_occ[k]) else: mo_ea = numpy.einsum('ik,ik->k', mo_coeff, self._focka_ao.dot(mo_coeff)) mo_eb = numpy.einsum('ik,ik->k', mo_coeff, self._fockb_ao.dot(mo_coeff)) log.note(' Roothaan | alpha | beta') for k, j in enumerate(orbsym): if j in irorbcnt: irorbcnt[j] += 1 else: irorbcnt[j] = 1 log.note('MO #%-4d(%-3s #%-2d) energy= %-18.15g | %-18.15g | %-18.15g occ= %g', k+1, irname_full[j], irorbcnt[j], mo_energy[k], mo_ea[k], mo_eb[k], mo_occ[k]) if verbose >= logger.DEBUG: label = mol.ao_labels() molabel = [] irorbcnt = {} for k, j in enumerate(orbsym): if j in irorbcnt: irorbcnt[j] += 1 else: irorbcnt[j] = 1 molabel.append('#%-d(%s #%d)' % (k+1, irname_full[j], irorbcnt[j])) log.debug(' ** MO coefficients (expansion on meta-Lowdin AOs) **') orth_coeff = orth.orth_ao(mol, 'meta_lowdin', s=ovlp_ao) c = reduce(numpy.dot, (orth_coeff.T, ovlp_ao, mo_coeff)) dump_mat.dump_rec(self.stdout, c, label, molabel, start=1, **kwargs) dm = self.make_rdm1(mo_coeff, mo_occ) return self.mulliken_meta(mol, dm, s=ovlp_ao, verbose=verbose)
def __init__(self, the_mf, active_orbs, localizationtype, ao_rotation=None, use_full_hessian=True, localization_threshold=1e-6): assert ((localizationtype == 'meta_lowdin') or (localizationtype == 'boys') or (localizationtype == 'lowdin') or (localizationtype == 'iao')) self.num_mf_stab_checks = 0 # Information on the full HF problem self.mol = the_mf.mol self._scf = the_mf self.max_memory = the_mf.max_memory self.get_jk_ao = partial(the_mf.get_jk, self.mol) self.get_veff_ao = partial(the_mf.get_veff, self.mol) self.get_k_ao = partial(the_mf.get_k, self.mol) self.fullovlpao = the_mf.get_ovlp self.fullEhf = the_mf.e_tot self.fullRDM_ao = np.asarray(the_mf.make_rdm1()) if self.fullRDM_ao.ndim == 3: self.fullSDM_ao = self.fullRDM_ao[0] - self.fullRDM_ao[1] self.fullRDM_ao = self.fullRDM_ao[0] + self.fullRDM_ao[1] else: self.fullSDM_ao = np.zeros_like(self.fullRDM_ao) self.fullJK_ao = self.get_veff_ao( dm=self.fullRDM_ao, dm_last=0, vhf_last=0, hermi=1) #Last 3 numbers: dm_last, vhf_last, hermi if self.fullJK_ao.ndim == 3: self.fullJK_ao = self.fullJK_ao[0] # Because I gave it a spin-summed 1-RDM, the two spins for JK will necessarily be identical self.fullFOCK_ao = the_mf.get_hcore() + self.fullJK_ao self.e_tot = the_mf.e_tot self.x2c = isinstance(the_mf, x2c._X2C_SCF) # Active space information self._which = localizationtype self.active = np.zeros([self.mol.nao_nr()], dtype=int) self.active[active_orbs] = 1 self.norbs_tot = np.sum(self.active) # Number of active space orbitals self.nelec_tot = int( np.rint(self.mol.nelectron - np.sum(the_mf.mo_occ[self.active == 0])) ) # Total number of electrons minus frozen part # Localize the orbitals if ((self._which == 'meta_lowdin') or (self._which == 'boys')): if (self._which == 'meta_lowdin'): assert (self.norbs_tot == self.mol.nao_nr() ) # Full active space required if (self._which == 'boys'): self.ao2loc = the_mf.mo_coeff[:, self.active == 1] if (self.norbs_tot == self.mol.nao_nr() ): # If you want the full active, do meta-Lowdin nao.AOSHELL[4] = ['1s0p0d0f', '2s1p0d0f' ] # redefine the valence shell for Be self.ao2loc = orth.orth_ao(self.mol, 'meta_lowdin') if (ao_rotation != None): self.ao2loc = np.dot(self.ao2loc, ao_rotation.T) if (self._which == 'boys'): old_verbose = self.mol.verbose self.mol.verbose = 5 loc = boys.Boys(self.mol, self.ao2loc) # loc = localizer.localizer( self.mol, self.ao2loc, self._which, use_full_hessian ) self.mol.verbose = old_verbose # self.ao2loc = loc.optimize( threshold=localization_threshold ) self.ao2loc = loc.kernel() self.TI_OK = False # Check yourself if OK, then overwrite if (self._which == 'lowdin'): assert (self.norbs_tot == self.mol.nao_nr() ) # Full active space required ovlp = self.mol.intor('cint1e_ovlp_sph') ovlp_eigs, ovlp_vecs = np.linalg.eigh(ovlp) assert (np.linalg.norm( np.dot(np.dot(ovlp_vecs, np.diag(ovlp_eigs)), ovlp_vecs.T) - ovlp) < 1e-10) self.ao2loc = np.dot( np.dot(ovlp_vecs, np.diag(np.power(ovlp_eigs, -0.5))), ovlp_vecs.T) self.TI_OK = False # Check yourself if OK, then overwrite if (self._which == 'iao'): assert (self.norbs_tot == self.mol.nao_nr() ) # Full active space assumed self.ao2loc = iao_helper.localize_iao(self.mol, the_mf) if (ao_rotation != None): self.ao2loc = np.dot(self.ao2loc, ao_rotation.T) self.TI_OK = False # Check yourself if OK, then overwrite #self.molden( 'dump.molden' ) # Debugging mode assert (self.loc_ortho() < 1e-8) # Stored inverse overlap matrix self.ao_ovlp_inv = np.dot(self.ao2loc, self.ao2loc.conjugate().T) self.ao_ovlp = the_mf.get_ovlp() assert (is_matrix_eye(np.dot(self.ao_ovlp, self.ao_ovlp_inv))) # Effective Hamiltonian due to frozen part self.frozenDM_mo = np.array(the_mf.mo_occ, copy=True) self.frozenDM_mo[self.active == 1] = 0 # Only the frozen MO occupancies nonzero self.frozenDM_ao = np.dot( np.dot(the_mf.mo_coeff, np.diag(self.frozenDM_mo)), the_mf.mo_coeff.T) self.frozenJK_ao = self.get_veff_ao( self.frozenDM_ao, 0, 0, 1) #Last 3 numbers: dm_last, vhf_last, hermi if self.frozenJK_ao.ndim == 3: self.frozenJK_ao = self.frozenJK_ao[0] # Because I gave it a spin-summed 1-RDM, the two spins for JK will necessarily be identical self.frozenOEI_ao = self.fullFOCK_ao - self.fullJK_ao + self.frozenJK_ao # Localized OEI and ERI self.activeCONST = self.mol.energy_nuc() + np.einsum( 'ij,ij->', self.frozenOEI_ao - 0.5 * self.frozenJK_ao, self.frozenDM_ao) self.activeOEI = represent_operator_in_basis(self.frozenOEI_ao, self.ao2loc) self.activeFOCK = represent_operator_in_basis(self.fullFOCK_ao, self.ao2loc) self.activeVSPIN = np.zeros_like( self.activeFOCK) # FIXME: correct behavior for ROHF init! self.activeJKidem = self.activeFOCK - self.activeOEI self.activeJKcorr = np.zeros((self.norbs_tot, self.norbs_tot), dtype=self.activeOEI.dtype) self.oneRDM_loc = self.ao2loc.conjugate( ).T @ self.ao_ovlp @ self.fullRDM_ao @ self.ao_ovlp @ self.ao2loc self.oneSDM_loc = self.ao2loc.conjugate( ).T @ self.ao_ovlp @ self.fullSDM_ao @ self.ao_ovlp @ self.ao2loc self.oneRDMcorr_loc = np.zeros((self.norbs_tot, self.norbs_tot), dtype=self.activeOEI.dtype) self.loc2idem = np.eye(self.norbs_tot, dtype=self.activeOEI.dtype) self.nelec_idem = self.nelec_tot self._eri = None self.with_df = None assert (abs(np.trace(self.oneRDM_loc) - self.nelec_tot) < 1e-8), '{} {}'.format(np.trace(self.oneRDM_loc), self.nelec_tot) sys.stdout.flush() def _is_mem_enough(): return 2 * (self.norbs_tot** 4) / 1e6 + current_memory()[0] < self.max_memory * 0.95 # Unfortunately, there is currently no way to do the integral transformation directly on the antisymmetrized two-electron # integrals, at least none already implemented in PySCF. Therefore the smallest possible memory footprint involves # two arrays of fourfold symmetry, which works out to roughly one half of an array with no symmetry if hasattr(the_mf, 'with_df') and hasattr( the_mf.with_df, '_cderi') and the_mf.with_df._cderi is not None: print("Found density-fitting three-center integrals scf object") loc2ao = self.ao2loc.conjugate().T locOao = np.dot(loc2ao, self.ao_ovlp) self.with_df = the_mf.with_df self.with_df.loc2eri_bas = lambda x: np.dot(self.ao2loc, x) self.with_df.loc2eri_op = lambda x: reduce(np.dot, (self.ao2loc, x, loc2ao)) self.with_df.eri2loc_bas = lambda x: np.dot(locOao, x) self.with_df.eri2loc_op = lambda x: reduce(np.dot, (loc2ao, x, self .ao2loc)) elif the_mf._eri is not None: print("Found eris on scf object") loc2ao = self.ao2loc.conjugate().T locOao = np.dot(loc2ao, self.ao_ovlp) self._eri = the_mf._eri self._eri = tag_array(self._eri, loc2eri_bas=lambda x: np.dot(self.ao2loc, x)) self._eri = tag_array( self._eri, loc2eri_op=lambda x: reduce(np.dot, (self.ao2loc, x, loc2ao))) self._eri = tag_array(self._eri, eri2loc_bas=lambda x: np.dot(locOao, x)) self._eri = tag_array( self._eri, eri2loc_op=lambda x: reduce(np.dot, (loc2ao, x, self.ao2loc))) elif _is_mem_enough(): print("Storing eris in memory") self._eri = ao2mo.restore( 8, ao2mo.outcore.full_iofree(self.mol, self.ao2loc, compact=True), self.norbs_tot) self._eri = tag_array(self._eri, loc2eri_bas=lambda x: x) self._eri = tag_array(self._eri, loc2eri_op=lambda x: x) self._eri = tag_array(self._eri, eri2loc_bas=lambda x: x) self._eri = tag_array(self._eri, eri2loc_op=lambda x: x) else: print("Direct calculation") sys.stdout.flush() # Symmetry information try: self.loc2symm = [ orthonormalize_a_basis(scipy.linalg.solve(self.ao2loc, ao2ir)) for ao2ir in self.mol.symm_orb ] self.symmetry = self.mol.groupname self.wfnsym = the_mf.wfnsym self.ir_names = self.mol.irrep_name self.ir_ids = self.mol.irrep_id self.enforce_symmetry = True except (AttributeError, TypeError) as e: if self.mol.symmetry: raise (e) self.loc2symm = [np.eye(self.norbs_tot)] self.symmetry = False self.wfnsym = 'A' self.ir_names = ['A'] self.ir_ids = [0] self.enforce_symmetry = False print("Initial loc2symm nonorthonormality: {}".format( measure_basis_nonorthonormality( np.concatenate(self.loc2symm, axis=1)))) for loc2ir1, loc2ir2 in itertools.combinations(self.loc2symm, 2): proj = loc2ir1 @ loc2ir1.conjugate().T loc2ir2[:, :] -= proj @ loc2ir2 for loc2ir in self.loc2symm: loc2ir[:, :] = orthonormalize_a_basis(loc2ir) print("Final loc2symm nonorthonormality: {}".format( measure_basis_nonorthonormality( np.concatenate(self.loc2symm, axis=1))))
def analyze(mf, verbose=logger.DEBUG, with_meta_lowdin=WITH_META_LOWDIN, **kwargs): from pyscf.lo import orth from pyscf.tools import dump_mat mol = mf.mol if not mol.symmetry: return uhf.analyze(mf, verbose, with_meta_lowdin, **kwargs) mo_energy = mf.mo_energy mo_occ = mf.mo_occ mo_coeff = mf.mo_coeff ovlp_ao = mf.get_ovlp() log = logger.new_logger(mf, verbose) if log.verbose >= logger.NOTE: nirrep = len(mol.irrep_id) ovlp_ao = mf.get_ovlp() orbsyma, orbsymb = get_orbsym(mf.mol, mo_coeff, ovlp_ao, False) tot_sym = 0 noccsa = [sum(orbsyma[mo_occ[0]>0]==ir) for ir in mol.irrep_id] noccsb = [sum(orbsymb[mo_occ[1]>0]==ir) for ir in mol.irrep_id] for i, ir in enumerate(mol.irrep_id): if (noccsa[i]+noccsb[i]) % 2: tot_sym ^= ir if mol.groupname in ('Dooh', 'Coov', 'SO3'): log.note('TODO: total wave-function symmetry for %s', mol.groupname) else: log.note('Wave-function symmetry = %s', symm.irrep_id2name(mol.groupname, tot_sym)) log.note('alpha occupancy for each irrep: '+(' %4s'*nirrep), *mol.irrep_name) log.note(' '+(' %4d'*nirrep), *noccsa) log.note('beta occupancy for each irrep: '+(' %4s'*nirrep), *mol.irrep_name) log.note(' '+(' %4d'*nirrep), *noccsb) log.note('**** MO energy ****') irname_full = {} for k, ir in enumerate(mol.irrep_id): irname_full[ir] = mol.irrep_name[k] irorbcnt = {} for k, j in enumerate(orbsyma): if j in irorbcnt: irorbcnt[j] += 1 else: irorbcnt[j] = 1 log.note('alpha MO #%d (%s #%d), energy= %.15g occ= %g', k+MO_BASE, irname_full[j], irorbcnt[j], mo_energy[0][k], mo_occ[0][k]) irorbcnt = {} for k, j in enumerate(orbsymb): if j in irorbcnt: irorbcnt[j] += 1 else: irorbcnt[j] = 1 log.note('beta MO #%d (%s #%d), energy= %.15g occ= %g', k+MO_BASE, irname_full[j], irorbcnt[j], mo_energy[1][k], mo_occ[1][k]) if mf.verbose >= logger.DEBUG: label = mol.ao_labels() molabel = [] irorbcnt = {} for k, j in enumerate(orbsyma): if j in irorbcnt: irorbcnt[j] += 1 else: irorbcnt[j] = 1 molabel.append('#%-d(%s #%d)' % (k+MO_BASE, irname_full[j], irorbcnt[j])) if with_meta_lowdin: log.debug(' ** alpha MO coefficients (expansion on meta-Lowdin AOs) **') orth_coeff = orth.orth_ao(mol, 'meta_lowdin', s=ovlp_ao) c_inv = numpy.dot(orth_coeff.T, ovlp_ao) mo = c_inv.dot(mo_coeff[0]) else: log.debug(' ** alpha MO coefficients (expansion on AOs) **') mo = mo_coeff[0] dump_mat.dump_rec(mf.stdout, mo, label, start=MO_BASE, **kwargs) molabel = [] irorbcnt = {} for k, j in enumerate(orbsymb): if j in irorbcnt: irorbcnt[j] += 1 else: irorbcnt[j] = 1 molabel.append('#%-d(%s #%d)' % (k+MO_BASE, irname_full[j], irorbcnt[j])) if with_meta_lowdin: log.debug(' ** beta MO coefficients (expansion on meta-Lowdin AOs) **') mo = c_inv.dot(mo_coeff[1]) else: log.debug(' ** beta MO coefficients (expansion on AOs) **') mo = mo_coeff[1] dump_mat.dump_rec(mol.stdout, mo, label, molabel, start=MO_BASE, **kwargs) dm = mf.make_rdm1(mo_coeff, mo_occ) if with_meta_lowdin: pop_and_charge = mf.mulliken_meta(mol, dm, s=ovlp_ao, verbose=log) else: pop_and_charge = mf.mulliken_pop(mol, dm, s=ovlp_ao, verbose=log) dip = mf.dip_moment(mol, dm, verbose=log) return pop_and_charge, dip
def analyze(self, verbose=logger.DEBUG): from pyscf.lo import orth from pyscf.tools import dump_mat mo_energy = self.mo_energy mo_occ = self.mo_occ mo_coeff = self.mo_coeff log = logger.Logger(self.stdout, verbose) mol = self.mol nirrep = len(mol.irrep_id) ovlp_ao = self.get_ovlp() orbsym = symm.label_orb_symm(mol, mol.irrep_id, mol.symm_orb, mo_coeff, s=ovlp_ao) orbsym = numpy.array(orbsym) wfnsym = 0 ndoccs = [] nsoccs = [] for k,ir in enumerate(mol.irrep_id): ndoccs.append(sum(orbsym[mo_occ==2] == ir)) nsoccs.append(sum(orbsym[mo_occ==1] == ir)) if nsoccs[k] % 2: wfnsym ^= ir if mol.groupname in ('Dooh', 'Coov'): log.info('TODO: total symmetry for %s', mol.groupname) else: log.info('total symmetry = %s', symm.irrep_id2name(mol.groupname, wfnsym)) log.info('occupancy for each irrep: ' + (' %4s'*nirrep), *mol.irrep_name) log.info('double occ ' + (' %4d'*nirrep), *ndoccs) log.info('single occ ' + (' %4d'*nirrep), *nsoccs) log.info('**** MO energy ****') irname_full = {} for k,ir in enumerate(mol.irrep_id): irname_full[ir] = mol.irrep_name[k] irorbcnt = {} if self._focka_ao is None: for k, j in enumerate(orbsym): if j in irorbcnt: irorbcnt[j] += 1 else: irorbcnt[j] = 1 log.note('MO #%-3d (%s #%-2d), energy= %-18.15g occ= %g', k+1, irname_full[j], irorbcnt[j], mo_energy[k], mo_occ[k]) else: mo_ea = numpy.einsum('ik,ik->k', mo_coeff, self._focka_ao.dot(mo_coeff)) mo_eb = numpy.einsum('ik,ik->k', mo_coeff, self._fockb_ao.dot(mo_coeff)) log.note(' Roothaan | alpha | beta') for k, j in enumerate(orbsym): if j in irorbcnt: irorbcnt[j] += 1 else: irorbcnt[j] = 1 log.note('MO #%-4d(%-3s #%-2d) energy= %-18.15g | %-18.15g | %-18.15g occ= %g', k+1, irname_full[j], irorbcnt[j], mo_energy[k], mo_ea[k], mo_eb[k], mo_occ[k]) if verbose >= logger.DEBUG: label = mol.spheric_labels(True) molabel = [] irorbcnt = {} for k, j in enumerate(orbsym): if j in irorbcnt: irorbcnt[j] += 1 else: irorbcnt[j] = 1 molabel.append('#%-d(%s #%d)' % (k+1, irname_full[j], irorbcnt[j])) log.debug(' ** MO coefficients (expansion on meta-Lowdin AOs) **') orth_coeff = orth.orth_ao(mol, 'meta_lowdin', s=ovlp_ao) c = reduce(numpy.dot, (orth_coeff.T, ovlp_ao, mo_coeff)) dump_mat.dump_rec(self.stdout, c, label, molabel, start=1) dm = self.make_rdm1(mo_coeff, mo_occ) return self.mulliken_meta(mol, dm, s=ovlp_ao, verbose=verbose)
def cas_natorb(mc, mo_coeff=None, ci=None, eris=None, sort=False, casdm1=None, verbose=None): '''Transform active orbitals to natrual orbitals, and update the CI wfn Args: mc : a CASSCF/CASCI object or RHF object Kwargs: sort : bool Sort natural orbitals wrt the occupancy. Be careful with this option since the resultant natural orbitals might have the different symmetry to the irreps indicated by CASSCF.orbsym Returns: A tuple, the first item is natural orbitals, the second is updated CI coefficients, the third is the natural occupancy associated to the natural orbitals. ''' from pyscf.lo import orth from pyscf.tools import dump_mat from pyscf.tools.mo_mapping import mo_1to1map if isinstance(verbose, logger.Logger): log = verbose else: log = logger.Logger(mc.stdout, mc.verbose) if mo_coeff is None: mo_coeff = mc.mo_coeff if ci is None: ci = mc.ci ncore = mc.ncore ncas = mc.ncas nocc = ncore + ncas nelecas = mc.nelecas if casdm1 is None: casdm1 = mc.fcisolver.make_rdm1(ci, ncas, nelecas) occ, ucas = mc._eig(-casdm1, ncore, nocc) if sort: idx = numpy.argsort(occ) occ = occ[idx] ucas = ucas[:,idx] occ = -occ # where_natorb gives the location of the natural orbital for the input cas # orbitals. gen_strings4orblist map thes sorted strings (on CAS orbital) to # the unsorted determinant strings (on natural orbital). e.g. (3o,2e) system # CAS orbital 1 2 3 # natural orbital 3 1 2 <= by mo_1to1map # CASorb-strings 0b011, 0b101, 0b110 # == (1,2), (1,3), (2,3) # natorb-strings (3,1), (3,2), (1,2) # == 0B101, 0B110, 0B011 <= by gen_strings4orblist # then argsort to translate the string representation to the address # [2(=0B011), 0(=0B101), 1(=0B110)] # to indicate which CASorb-strings address to be loaded in each natorb-strings slot where_natorb = mo_1to1map(ucas) #guide_stringsa = fci.cistring.gen_strings4orblist(where_natorb, nelecas[0]) #guide_stringsb = fci.cistring.gen_strings4orblist(where_natorb, nelecas[1]) #old_det_idxa = numpy.argsort(guide_stringsa) #old_det_idxb = numpy.argsort(guide_stringsb) #ci0 = ci[old_det_idxa[:,None],old_det_idxb] if isinstance(ci, numpy.ndarray): ci0 = fci.addons.reorder(ci, nelecas, where_natorb) elif isinstance(ci, (tuple, list)) and isinstance(ci[0], numpy.ndarray): # for state-average eigenfunctions ci0 = [fci.addons.reorder(x, nelecas, where_natorb) for x in ci] else: log.info('FCI vector not available, so not using old wavefunction as initial guess') ci0 = None # restore phase, to ensure the reordered ci vector is the correct initial guess for i, k in enumerate(where_natorb): if ucas[i,k] < 0: ucas[:,k] *= -1 mo_coeff1 = mo_coeff.copy() mo_coeff1[:,ncore:nocc] = numpy.dot(mo_coeff[:,ncore:nocc], ucas) if log.verbose >= logger.INFO: log.debug('where_natorb %s', str(where_natorb)) log.info('Natural occ %s', str(occ)) log.info('Natural orbital (expansion on meta-Lowdin AOs) in CAS space') label = mc.mol.spheric_labels(True) orth_coeff = orth.orth_ao(mc.mol, 'meta_lowdin', s=ovlp_ao) mo_cas = reduce(numpy.dot, (orth_coeff.T, ovlp_ao, mo_coeff1[:,ncore:nocc])) dump_mat.dump_rec(log.stdout, mo_cas, label, start=1) if mc._scf.mo_coeff is not None: s = reduce(numpy.dot, (mo_coeff1[:,ncore:nocc].T, mc._scf.get_ovlp(), mc._scf.mo_coeff)) idx = numpy.argwhere(abs(s)>.4) for i,j in idx: log.info('<CAS-nat-orb|mo-hf> %d %d %12.8f', ncore+i+1, j+1, s[i,j]) mocas = mo_coeff1[:,ncore:nocc] h1eff = reduce(numpy.dot, (mocas.T, mc.get_hcore(), mocas)) if eris is not None and hasattr(eris, 'ppaa'): h1eff += reduce(numpy.dot, (ucas.T, eris.vhf_c[ncore:nocc,ncore:nocc], ucas)) aaaa = ao2mo.restore(4, eris.ppaa[ncore:nocc,ncore:nocc,:,:], ncas) aaaa = ao2mo.incore.full(aaaa, ucas) else: dm_core = numpy.dot(mo_coeff[:,:ncore]*2, mo_coeff[:,:ncore].T) vj, vk = mc._scf.get_jk(mc.mol, dm_core) h1eff += reduce(numpy.dot, (mocas.T, vj-vk*.5, mocas)) aaaa = ao2mo.kernel(mc.mol, mocas) e_cas, fcivec = mc.fcisolver.kernel(h1eff, aaaa, ncas, nelecas, ci0=ci0) log.debug('In Natural orbital, CI energy = %.12g', e_cas) return mo_coeff1, fcivec, occ
def analyze(casscf, mo_coeff=None, ci=None, verbose=logger.INFO): from pyscf.lo import orth from pyscf.tools import dump_mat if mo_coeff is None: mo_coeff = casscf.mo_coeff if ci is None: ci = casscf.ci if isinstance(verbose, logger.Logger): log = verbose else: log = logger.Logger(casscf.stdout, verbose) nelecas = casscf.nelecas ncas = casscf.ncas ncore = casscf.ncore nocc = ncore + ncas label = casscf.mol.spheric_labels(True) if hasattr(casscf.fcisolver, 'make_rdm1s'): casdm1a, casdm1b = casscf.fcisolver.make_rdm1s(ci, ncas, nelecas) casdm1 = casdm1a + casdm1b mocore = mo_coeff[:,:ncore] mocas = mo_coeff[:,ncore:nocc] dm1b = numpy.dot(mocore, mocore.T) dm1a = dm1b + reduce(numpy.dot, (mocas, casdm1a, mocas.T)) dm1b += reduce(numpy.dot, (mocas, casdm1b, mocas.T)) dm1 = dm1a + dm1b if log.verbose >= logger.DEBUG1: log.info('alpha density matrix (on AO)') dump_mat.dump_tri(log.stdout, dm1a, label) log.info('beta density matrix (on AO)') dump_mat.dump_tri(log.stdout, dm1b, label) else: casdm1 = casscf.fcisolver.make_rdm1(ci, ncas, nelecas) mocore = mo_coeff[:,:ncore] mocas = mo_coeff[:,ncore:nocc] dm1a =(numpy.dot(mocore, mocore.T) * 2 + reduce(numpy.dot, (mocas, casdm1, mocas.T))) dm1b = None dm1 = dm1a if log.verbose >= logger.INFO: ovlp_ao = casscf._scf.get_ovlp() # note the last two args of ._eig for mc1step_symm occ, ucas = casscf._eig(-casdm1, ncore, nocc) log.info('Natural occ %s', str(-occ)) for i, k in enumerate(numpy.argmax(abs(ucas), axis=0)): if ucas[k,i] < 0: ucas[:,i] *= -1 orth_coeff = orth.orth_ao(casscf.mol, 'meta_lowdin', s=ovlp_ao) mo_cas = reduce(numpy.dot, (orth_coeff.T, ovlp_ao, mo_coeff[:,ncore:nocc], ucas)) log.info('Natural orbital (expansion on meta-Lowdin AOs) in CAS space') dump_mat.dump_rec(log.stdout, mo_cas, label, start=1) if casscf._scf.mo_coeff is not None: s = reduce(numpy.dot, (casscf.mo_coeff.T, ovlp_ao, casscf._scf.mo_coeff)) idx = numpy.argwhere(abs(s)>.4) for i,j in idx: log.info('<mo-mcscf|mo-hf> %d %d %12.8f', i+1, j+1, s[i,j]) if ci is not None and numpy.ndim(ci) >= 2: log.info('** Largest CI components **') if ci[0].ndim == 2: for i, state in enumerate(ci): log.info(' string alpha, string beta, state %d CI coefficients', i) for c,ia,ib in fci.addons.large_ci(state, casscf.ncas, casscf.nelecas): log.info(' %9s %9s %.12f', ia, ib, c) else: log.info(' string alpha, string beta, CI coefficients') for c,ia,ib in fci.addons.large_ci(ci, casscf.ncas, casscf.nelecas): log.info(' %9s %9s %.12f', ia, ib, c) casscf._scf.mulliken_meta(casscf.mol, dm1, s=ovlp_ao, verbose=log) return dm1a, dm1b
def analyze(mf, verbose=logger.DEBUG, with_meta_lowdin=WITH_META_LOWDIN, **kwargs): '''Analyze the given SCF object: print orbital energies, occupancies; print orbital coefficients; Occupancy for each irreps; Mulliken population analysis ''' from pyscf.lo import orth from pyscf.tools import dump_mat mol = mf.mol if not mol.symmetry: return hf.analyze(mf, verbose, with_meta_lowdin, **kwargs) mo_energy = mf.mo_energy mo_occ = mf.mo_occ mo_coeff = mf.mo_coeff ovlp_ao = mf.get_ovlp() log = logger.new_logger(mf, verbose) if log.verbose >= logger.NOTE: nirrep = len(mol.irrep_id) orbsym = get_orbsym(mf.mol, mo_coeff, ovlp_ao, False) wfnsym = 0 noccs = [sum(orbsym[mo_occ > 0] == ir) for ir in mol.irrep_id] if mol.groupname in ('SO3', 'Dooh', 'Coov'): log.note('TODO: total wave-function symmetry for %s', mol.groupname) else: log.note('Wave-function symmetry = %s', symm.irrep_id2name(mol.groupname, wfnsym)) log.note('occupancy for each irrep: ' + (' %4s' * nirrep), *mol.irrep_name) log.note(' ' + (' %4d' * nirrep), *noccs) log.note('**** MO energy ****') irname_full = {} for k, ir in enumerate(mol.irrep_id): irname_full[ir] = mol.irrep_name[k] irorbcnt = {} for k, j in enumerate(orbsym): if j in irorbcnt: irorbcnt[j] += 1 else: irorbcnt[j] = 1 log.note('MO #%d (%s #%d), energy= %.15g occ= %g', k + MO_BASE, irname_full[j], irorbcnt[j], mo_energy[k], mo_occ[k]) if log.verbose >= logger.DEBUG: label = mol.ao_labels() molabel = [] irorbcnt = {} for k, j in enumerate(orbsym): if j in irorbcnt: irorbcnt[j] += 1 else: irorbcnt[j] = 1 molabel.append('#%-d(%s #%d)' % (k + MO_BASE, irname_full[j], irorbcnt[j])) if with_meta_lowdin: log.debug(' ** MO coefficients (expansion on meta-Lowdin AOs) **') orth_coeff = orth.orth_ao(mol, 'meta_lowdin', s=ovlp_ao) c = reduce(numpy.dot, (orth_coeff.T, ovlp_ao, mo_coeff)) else: log.debug(' ** MO coefficients (expansion on AOs) **') c = mo_coeff dump_mat.dump_rec(mf.stdout, c, label, molabel, start=MO_BASE, **kwargs) dm = mf.make_rdm1(mo_coeff, mo_occ) if with_meta_lowdin: pop_and_charge = mf.mulliken_meta(mol, dm, s=ovlp_ao, verbose=log) else: pop_and_charge = mf.mulliken_pop(mol, dm, s=ovlp_ao, verbose=log) dip = mf.dip_moment(mol, dm, verbose=log) return pop_and_charge, dip
def analyze(casscf, mo_coeff=None, ci=None, verbose=None, large_ci_tol=LARGE_CI_TOL, with_meta_lowdin=WITH_META_LOWDIN, **kwargs): from pyscf.lo import orth from pyscf.tools import dump_mat from pyscf.mcscf import addons log = logger.new_logger(casscf, verbose) if mo_coeff is None: mo_coeff = casscf.mo_coeff if ci is None: ci = casscf.ci nelecas = casscf.nelecas ncas = casscf.ncas ncore = casscf.ncore nocc = ncore + ncas mocore = mo_coeff[:,:ncore] mocas = mo_coeff[:,ncore:nocc] label = casscf.mol.ao_labels() if (isinstance(ci, (list, tuple)) and not isinstance(casscf.fcisolver, addons.StateAverageFCISolver)): log.warn('Mulitple states found in CASCI/CASSCF solver. Density ' 'matrix of first state is generated in .analyze() function.') civec = ci[0] else: civec = ci if getattr(casscf.fcisolver, 'make_rdm1s', None): casdm1a, casdm1b = casscf.fcisolver.make_rdm1s(civec, ncas, nelecas) casdm1 = casdm1a + casdm1b dm1b = numpy.dot(mocore, mocore.T) dm1a = dm1b + reduce(numpy.dot, (mocas, casdm1a, mocas.T)) dm1b += reduce(numpy.dot, (mocas, casdm1b, mocas.T)) dm1 = dm1a + dm1b if log.verbose >= logger.DEBUG2: log.info('alpha density matrix (on AO)') dump_mat.dump_tri(log.stdout, dm1a, label, **kwargs) log.info('beta density matrix (on AO)') dump_mat.dump_tri(log.stdout, dm1b, label, **kwargs) else: casdm1 = casscf.fcisolver.make_rdm1(civec, ncas, nelecas) dm1a =(numpy.dot(mocore, mocore.T) * 2 + reduce(numpy.dot, (mocas, casdm1, mocas.T))) dm1b = None dm1 = dm1a if log.verbose >= logger.INFO: ovlp_ao = casscf._scf.get_ovlp() # note the last two args of ._eig for mc1step_symm occ, ucas = casscf._eig(-casdm1, ncore, nocc) log.info('Natural occ %s', str(-occ)) mocas = numpy.dot(mocas, ucas) if with_meta_lowdin: log.info('Natural orbital (expansion on meta-Lowdin AOs) in CAS space') orth_coeff = orth.orth_ao(casscf.mol, 'meta_lowdin', s=ovlp_ao) mocas = reduce(numpy.dot, (orth_coeff.T, ovlp_ao, mocas)) else: log.info('Natural orbital (expansion on AOs) in CAS space') dump_mat.dump_rec(log.stdout, mocas, label, start=1, **kwargs) if log.verbose >= logger.DEBUG2: if not casscf.natorb: log.debug2('NOTE: mc.mo_coeff in active space is different to ' 'the natural orbital coefficients printed in above.') if with_meta_lowdin: c = reduce(numpy.dot, (orth_coeff.T, ovlp_ao, mo_coeff)) log.debug2('MCSCF orbital (expansion on meta-Lowdin AOs)') else: c = mo_coeff log.debug2('MCSCF orbital (expansion on AOs)') dump_mat.dump_rec(log.stdout, c, label, start=1, **kwargs) if casscf._scf.mo_coeff is not None: addons.map2hf(casscf, casscf._scf.mo_coeff) if getattr(casscf.fcisolver, 'large_ci', None) and ci is not None: log.info('** Largest CI components **') if isinstance(ci, (tuple, list)): for i, civec in enumerate(ci): res = casscf.fcisolver.large_ci(civec, casscf.ncas, casscf.nelecas, large_ci_tol, return_strs=False) log.info(' [alpha occ-orbitals] [beta occ-orbitals] state %-3d CI coefficient', i) for c,ia,ib in res: log.info(' %-20s %-30s %.12f', ia, ib, c) else: log.info(' [alpha occ-orbitals] [beta occ-orbitals] CI coefficient') res = casscf.fcisolver.large_ci(ci, casscf.ncas, casscf.nelecas, large_ci_tol, return_strs=False) for c,ia,ib in res: log.info(' %-20s %-30s %.12f', ia, ib, c) if with_meta_lowdin: casscf._scf.mulliken_meta(casscf.mol, dm1, s=ovlp_ao, verbose=log) else: casscf._scf.mulliken_pop(casscf.mol, dm1, s=ovlp_ao, verbose=log) return dm1a, dm1b
def analyze(casscf, mo_coeff=None, ci=None, verbose=None, large_ci_tol=LARGE_CI_TOL, with_meta_lowdin=WITH_META_LOWDIN, **kwargs): from pyscf.lo import orth from pyscf.tools import dump_mat from pyscf.mcscf import addons log = logger.new_logger(casscf, verbose) if mo_coeff is None: mo_coeff = casscf.mo_coeff if ci is None: ci = casscf.ci nelecas = casscf.nelecas ncas = casscf.ncas ncore = casscf.ncore nocc = ncore + ncas mocore = mo_coeff[:,:ncore] mocas = mo_coeff[:,ncore:nocc] label = casscf.mol.ao_labels() if (isinstance(ci, (list, tuple, RANGE_TYPE)) and not isinstance(casscf.fcisolver, addons.StateAverageFCISolver)): log.warn('Mulitple states found in CASCI/CASSCF solver. Density ' 'matrix of the first state is generated in .analyze() function.') civec = ci[0] else: civec = ci if getattr(casscf.fcisolver, 'make_rdm1s', None): casdm1a, casdm1b = casscf.fcisolver.make_rdm1s(civec, ncas, nelecas) casdm1 = casdm1a + casdm1b dm1b = numpy.dot(mocore, mocore.T) dm1a = dm1b + reduce(numpy.dot, (mocas, casdm1a, mocas.T)) dm1b += reduce(numpy.dot, (mocas, casdm1b, mocas.T)) dm1 = dm1a + dm1b if log.verbose >= logger.DEBUG2: log.info('alpha density matrix (on AO)') dump_mat.dump_tri(log.stdout, dm1a, label, **kwargs) log.info('beta density matrix (on AO)') dump_mat.dump_tri(log.stdout, dm1b, label, **kwargs) else: casdm1 = casscf.fcisolver.make_rdm1(civec, ncas, nelecas) dm1a =(numpy.dot(mocore, mocore.T) * 2 + reduce(numpy.dot, (mocas, casdm1, mocas.T))) dm1b = None dm1 = dm1a if log.verbose >= logger.INFO: ovlp_ao = casscf._scf.get_ovlp() # note the last two args of ._eig for mc1step_symm occ, ucas = casscf._eig(-casdm1, ncore, nocc) log.info('Natural occ %s', str(-occ)) mocas = numpy.dot(mocas, ucas) if with_meta_lowdin: log.info('Natural orbital (expansion on meta-Lowdin AOs) in CAS space') orth_coeff = orth.orth_ao(casscf.mol, 'meta_lowdin', s=ovlp_ao) mocas = reduce(numpy.dot, (orth_coeff.T, ovlp_ao, mocas)) else: log.info('Natural orbital (expansion on AOs) in CAS space') dump_mat.dump_rec(log.stdout, mocas, label, start=1, **kwargs) if log.verbose >= logger.DEBUG2: if not casscf.natorb: log.debug2('NOTE: mc.mo_coeff in active space is different to ' 'the natural orbital coefficients printed in above.') if with_meta_lowdin: c = reduce(numpy.dot, (orth_coeff.T, ovlp_ao, mo_coeff)) log.debug2('MCSCF orbital (expansion on meta-Lowdin AOs)') else: c = mo_coeff log.debug2('MCSCF orbital (expansion on AOs)') dump_mat.dump_rec(log.stdout, c, label, start=1, **kwargs) if casscf._scf.mo_coeff is not None: addons.map2hf(casscf, casscf._scf.mo_coeff) if (ci is not None and (getattr(casscf.fcisolver, 'large_ci', None) or getattr(casscf.fcisolver, 'states_large_ci', None))): log.info('** Largest CI components **') if isinstance(ci, (list, tuple, RANGE_TYPE)): if hasattr(casscf.fcisolver, 'states_large_ci'): # defined in state_average_mix_ mcscf object res = casscf.fcisolver.states_large_ci(ci, casscf.ncas, casscf.nelecas, large_ci_tol, return_strs=False) else: res = [casscf.fcisolver.large_ci(civec, casscf.ncas, casscf.nelecas, large_ci_tol, return_strs=False) for civec in ci] for i, civec in enumerate(ci): log.info(' [alpha occ-orbitals] [beta occ-orbitals] state %-3d CI coefficient', i) for c,ia,ib in res[i]: log.info(' %-20s %-30s %.12f', ia, ib, c) else: log.info(' [alpha occ-orbitals] [beta occ-orbitals] CI coefficient') res = casscf.fcisolver.large_ci(ci, casscf.ncas, casscf.nelecas, large_ci_tol, return_strs=False) for c,ia,ib in res: log.info(' %-20s %-30s %.12f', ia, ib, c) if with_meta_lowdin: casscf._scf.mulliken_meta(casscf.mol, dm1, s=ovlp_ao, verbose=log) else: casscf._scf.mulliken_pop(casscf.mol, dm1, s=ovlp_ao, verbose=log) return dm1a, dm1b
def cas_natorb(mc, mo_coeff=None, ci=None, eris=None, sort=False, casdm1=None, verbose=None, with_meta_lowdin=WITH_META_LOWDIN): '''Transform active orbitals to natrual orbitals, and update the CI wfn accordingly Args: mc : a CASSCF/CASCI object or RHF object Kwargs: sort : bool Sort natural orbitals wrt the occupancy. Returns: A tuple, the first item is natural orbitals, the second is updated CI coefficients, the third is the natural occupancy associated to the natural orbitals. ''' from pyscf.lo import orth from pyscf.tools import dump_mat from pyscf.tools.mo_mapping import mo_1to1map if mo_coeff is None: mo_coeff = mc.mo_coeff if ci is None: ci = mc.ci log = logger.new_logger(mc, verbose) ncore = mc.ncore ncas = mc.ncas nocc = ncore + ncas nelecas = mc.nelecas nmo = mo_coeff.shape[1] if casdm1 is None: casdm1 = mc.fcisolver.make_rdm1(ci, ncas, nelecas) # orbital symmetry is reserved in this _eig call cas_occ, ucas = mc._eig(-casdm1, ncore, nocc) if sort: casorb_idx = numpy.argsort(cas_occ.round(9), kind='mergesort') cas_occ = cas_occ[casorb_idx] ucas = ucas[:,casorb_idx] cas_occ = -cas_occ mo_occ = numpy.zeros(mo_coeff.shape[1]) mo_occ[:ncore] = 2 mo_occ[ncore:nocc] = cas_occ mo_coeff1 = mo_coeff.copy() mo_coeff1[:,ncore:nocc] = numpy.dot(mo_coeff[:,ncore:nocc], ucas) if getattr(mo_coeff, 'orbsym', None) is not None: orbsym = numpy.copy(mo_coeff.orbsym) if sort: orbsym[ncore:nocc] = orbsym[ncore:nocc][casorb_idx] mo_coeff1 = lib.tag_array(mo_coeff1, orbsym=orbsym) else: orbsym = numpy.zeros(nmo, dtype=int) # When occupancies of active orbitals equal to 2 or 0, these orbitals # need to be canonicalized along with inactive(core or virtual) orbitals # using general Fock matrix. Because they are strongly coupled with # inactive orbitals, the 0th order Hamiltonian of MRPT methods can be # strongly affected. Numerical uncertainty may be found in the perturbed # correlation energy. # See issue https://github.com/pyscf/pyscf/issues/1041 occ2_idx = numpy.where(2 - cas_occ < FRAC_OCC_THRESHOLD)[0] occ0_idx = numpy.where(cas_occ < FRAC_OCC_THRESHOLD)[0] if occ2_idx.size > 0 or occ0_idx.size > 0: fock_ao = mc.get_fock(mo_coeff, ci, eris, casdm1, verbose) def _diag_subfock_(idx): c = mo_coeff1[:,idx] fock = reduce(numpy.dot, (c.conj().T, fock_ao, c)) w, c = mc._eig(fock, None, None, orbsym[idx]) mo_coeff1[:,idx] = mo_coeff1[:,idx].dot(c) if occ2_idx.size > 0: log.warn('Active orbitals %s (occs = %s) are canonicalized with core orbitals', occ2_idx, cas_occ[occ2_idx]) full_occ2_idx = numpy.append(numpy.arange(ncore), ncore + occ2_idx) _diag_subfock_(full_occ2_idx) if occ0_idx.size > 0: log.warn('Active orbitals %s (occs = %s) are canonicalized with external orbitals', occ0_idx, cas_occ[occ0_idx]) full_occ0_idx = numpy.append(ncore + occ0_idx, numpy.arange(nocc, nmo)) _diag_subfock_(full_occ0_idx) # Rotate CI according to the unitary coefficients ucas if applicable fcivec = None if getattr(mc.fcisolver, 'transform_ci_for_orbital_rotation', None): if isinstance(ci, numpy.ndarray): fcivec = mc.fcisolver.transform_ci_for_orbital_rotation(ci, ncas, nelecas, ucas) elif (isinstance(ci, (list, tuple)) and all(isinstance(x[0], numpy.ndarray) for x in ci)): fcivec = [mc.fcisolver.transform_ci_for_orbital_rotation(x, ncas, nelecas, ucas) for x in ci] elif getattr(mc.fcisolver, 'states_transform_ci_for_orbital_rotation', None): fcivec = mc.fcisolver.states_transform_ci_for_orbital_rotation(ci, ncas, nelecas, ucas) # Rerun fcisolver to get wavefunction if it cannot be transformed from # existed one. if fcivec is None: log.info('FCI vector not available, call CASCI to update wavefunction') mocas = mo_coeff1[:,ncore:nocc] hcore = mc.get_hcore() dm_core = numpy.dot(mo_coeff1[:,:ncore]*2, mo_coeff1[:,:ncore].conj().T) ecore = mc.energy_nuc() ecore+= numpy.einsum('ij,ji', hcore, dm_core) h1eff = reduce(numpy.dot, (mocas.conj().T, hcore, mocas)) if getattr(eris, 'ppaa', None) is not None: ecore += eris.vhf_c[:ncore,:ncore].trace() h1eff += reduce(numpy.dot, (ucas.conj().T, eris.vhf_c[ncore:nocc,ncore:nocc], ucas)) aaaa = ao2mo.restore(4, eris.ppaa[ncore:nocc,ncore:nocc,:,:], ncas) aaaa = ao2mo.incore.full(aaaa, ucas) else: if getattr(mc, 'with_df', None): aaaa = mc.with_df.ao2mo(mocas) else: aaaa = ao2mo.kernel(mc.mol, mocas) corevhf = mc.get_veff(mc.mol, dm_core) ecore += numpy.einsum('ij,ji', dm_core, corevhf) * .5 h1eff += reduce(numpy.dot, (mocas.conj().T, corevhf, mocas)) # See label_symmetry_ function in casci_symm.py which initialize the # orbital symmetry information in fcisolver. This orbital symmetry # labels should be reordered to match the sorted active space orbitals. if sort and getattr(mo_coeff1, 'orbsym', None) is not None: mc.fcisolver.orbsym = mo_coeff1.orbsym[ncore:nocc] max_memory = max(400, mc.max_memory-lib.current_memory()[0]) e, fcivec = mc.fcisolver.kernel(h1eff, aaaa, ncas, nelecas, ecore=ecore, max_memory=max_memory, verbose=log) log.debug('In Natural orbital, CASCI energy = %s', e) if log.verbose >= logger.INFO: ovlp_ao = mc._scf.get_ovlp() # where_natorb gives the new locations of the natural orbitals where_natorb = mo_1to1map(ucas) log.debug('where_natorb %s', str(where_natorb)) log.info('Natural occ %s', str(cas_occ)) if with_meta_lowdin: log.info('Natural orbital (expansion on meta-Lowdin AOs) in CAS space') label = mc.mol.ao_labels() orth_coeff = orth.orth_ao(mc.mol, 'meta_lowdin', s=ovlp_ao) mo_cas = reduce(numpy.dot, (orth_coeff.conj().T, ovlp_ao, mo_coeff1[:,ncore:nocc])) else: log.info('Natural orbital (expansion on AOs) in CAS space') label = mc.mol.ao_labels() mo_cas = mo_coeff1[:,ncore:nocc] dump_mat.dump_rec(log.stdout, mo_cas, label, start=1) if mc._scf.mo_coeff is not None: s = reduce(numpy.dot, (mo_coeff1[:,ncore:nocc].conj().T, mc._scf.get_ovlp(), mc._scf.mo_coeff)) idx = numpy.argwhere(abs(s)>.4) for i,j in idx: log.info('<CAS-nat-orb|mo-hf> %d %d %12.8f', ncore+i+1, j+1, s[i,j]) return mo_coeff1, fcivec, mo_occ
def cas_natorb(mc, mo_coeff=None, ci=None, eris=None, sort=False, casdm1=None, verbose=None): '''Transform active orbitals to natrual orbitals, and update the CI wfn Args: mc : a CASSCF/CASCI object or RHF object Kwargs: sort : bool Sort natural orbitals wrt the occupancy. Returns: A tuple, the first item is natural orbitals, the second is updated CI coefficients, the third is the natural occupancy associated to the natural orbitals. ''' from pyscf.lo import orth from pyscf.tools import dump_mat from pyscf.tools.mo_mapping import mo_1to1map if isinstance(verbose, logger.Logger): log = verbose else: log = logger.Logger(mc.stdout, mc.verbose) if mo_coeff is None: mo_coeff = mc.mo_coeff if ci is None: ci = mc.ci ncore = mc.ncore ncas = mc.ncas nocc = ncore + ncas nelecas = mc.nelecas if casdm1 is None: casdm1 = mc.fcisolver.make_rdm1(ci, ncas, nelecas) # orbital symmetry is reserved in this _eig call occ, ucas = mc._eig(-casdm1, ncore, nocc) if sort: casorb_idx = numpy.argsort(occ) occ = occ[casorb_idx] ucas = ucas[:, casorb_idx] # restore phase # where_natorb gives the location of the natural orbital for the input cas # orbitals. gen_strings4orblist map thes sorted strings (on CAS orbital) to # the unsorted determinant strings (on natural orbital). e.g. (3o,2e) system # CAS orbital 1 2 3 # natural orbital 3 1 2 <= by mo_1to1map # CASorb-strings 0b011, 0b101, 0b110 # == (1,2), (1,3), (2,3) # natorb-strings (3,1), (3,2), (1,2) # == 0B101, 0B110, 0B011 <= by gen_strings4orblist # then argsort to translate the string representation to the address # [2(=0B011), 0(=0B101), 1(=0B110)] # to indicate which CASorb-strings address to be loaded in each natorb-strings slot where_natorb = mo_1to1map(ucas) for i, k in enumerate(where_natorb): if ucas[i, k] < 0: ucas[:, k] *= -1 occ = -occ mo_occ = numpy.zeros(mo_coeff.shape[1]) mo_occ[:ncore] = 2 mo_occ[ncore:nocc] = occ if isinstance(ci, numpy.ndarray): fcivec = fci.addons.transform_ci_for_orbital_rotation( ci, ncas, nelecas, ucas) elif isinstance(ci, (tuple, list)) and isinstance(ci[0], numpy.ndarray): # for state-average eigenfunctions fcivec = [ fci.addons.transform_ci_for_orbital_rotation( x, ncas, nelecas, ucas) for x in ci ] else: log.info('FCI vector not available, call CASCI for wavefunction') mocas = mo_coeff[:, ncore:nocc] h1eff = reduce(numpy.dot, (mocas.T, mc.get_hcore(), mocas)) if eris is not None and hasattr(eris, 'ppaa'): h1eff += reduce(numpy.dot, (ucas.T, eris.vhf_c[ncore:nocc, ncore:nocc], ucas)) aaaa = ao2mo.restore(4, eris.ppaa[ncore:nocc, ncore:nocc, :, :], ncas) aaaa = ao2mo.incore.full(aaaa, ucas) else: dm_core = numpy.dot(mo_coeff[:, :ncore] * 2, mo_coeff[:, :ncore].T) vj, vk = mc._scf.get_jk(mc.mol, dm_core) h1eff += reduce(numpy.dot, (mocas.T, vj - vk * .5, mocas)) aaaa = ao2mo.kernel(mc.mol, mocas) max_memory = max(400, mc.max_memory - lib.current_memory()[0]) e_cas, fcivec = mc.fcisolver.kernel(h1eff, aaaa, ncas, nelecas, max_memory=max_memory, verbose=log) log.debug('In Natural orbital, CI energy = %.12g', e_cas) mo_coeff1 = mo_coeff.copy() mo_coeff1[:, ncore:nocc] = numpy.dot(mo_coeff[:, ncore:nocc], ucas) if hasattr(mo_coeff, 'orbsym'): orbsym = numpy.copy(mo_coeff.orbsym) if sort: orbsym[ncore:nocc] = orbsym[ncore:nocc][casorb_idx] mo_coeff1 = lib.tag_array(mo_coeff1, orbsym=orbsym) if log.verbose >= logger.INFO: ovlp_ao = mc._scf.get_ovlp() log.debug('where_natorb %s', str(where_natorb)) log.info('Natural occ %s', str(occ)) log.info('Natural orbital (expansion on meta-Lowdin AOs) in CAS space') label = mc.mol.ao_labels() orth_coeff = orth.orth_ao(mc.mol, 'meta_lowdin', s=ovlp_ao) mo_cas = reduce(numpy.dot, (orth_coeff.T, ovlp_ao, mo_coeff1[:, ncore:nocc])) dump_mat.dump_rec(log.stdout, mo_cas, label, start=1) if mc._scf.mo_coeff is not None: s = reduce(numpy.dot, (mo_coeff1[:, ncore:nocc].T, mc._scf.get_ovlp(), mc._scf.mo_coeff)) idx = numpy.argwhere(abs(s) > .4) for i, j in idx: log.info('<CAS-nat-orb|mo-hf> %d %d %12.8f', ncore + i + 1, j + 1, s[i, j]) return mo_coeff1, fcivec, mo_occ
def analyze(mf, verbose=logger.DEBUG, with_meta_lowdin=WITH_META_LOWDIN, **kwargs): from pyscf.lo import orth from pyscf.tools import dump_mat mol = mf.mol if not mol.symmetry: return uhf.analyze(mf, verbose, with_meta_lowdin, **kwargs) mo_energy = mf.mo_energy mo_occ = mf.mo_occ mo_coeff = mf.mo_coeff ovlp_ao = mf.get_ovlp() log = logger.new_logger(mf, verbose) if log.verbose >= logger.NOTE: nirrep = len(mol.irrep_id) ovlp_ao = mf.get_ovlp() orbsyma, orbsymb = get_orbsym(mf.mol, mo_coeff, ovlp_ao, False) tot_sym = 0 noccsa = [sum(orbsyma[mo_occ[0]>0]==ir) for ir in mol.irrep_id] noccsb = [sum(orbsymb[mo_occ[1]>0]==ir) for ir in mol.irrep_id] for i, ir in enumerate(mol.irrep_id): if (noccsa[i]+noccsb[i]) % 2: tot_sym ^= ir if mol.groupname in ('Dooh', 'Coov', 'SO3'): log.note('TODO: total wave-function symmetry for %s', mol.groupname) else: log.note('Wave-function symmetry = %s', symm.irrep_id2name(mol.groupname, tot_sym)) log.note('alpha occupancy for each irrep: '+(' %4s'*nirrep), *mol.irrep_name) log.note(' '+(' %4d'*nirrep), *noccsa) log.note('beta occupancy for each irrep: '+(' %4s'*nirrep), *mol.irrep_name) log.note(' '+(' %4d'*nirrep), *noccsb) log.note('**** MO energy ****') irname_full = {} for k, ir in enumerate(mol.irrep_id): irname_full[ir] = mol.irrep_name[k] irorbcnt = {} for k, j in enumerate(orbsyma): if j in irorbcnt: irorbcnt[j] += 1 else: irorbcnt[j] = 1 log.note('alpha MO #%d (%s #%d), energy= %.15g occ= %g', k+MO_BASE, irname_full[j], irorbcnt[j], mo_energy[0][k], mo_occ[0][k]) irorbcnt = {} for k, j in enumerate(orbsymb): if j in irorbcnt: irorbcnt[j] += 1 else: irorbcnt[j] = 1 log.note('beta MO #%d (%s #%d), energy= %.15g occ= %g', k+MO_BASE, irname_full[j], irorbcnt[j], mo_energy[1][k], mo_occ[1][k]) if mf.verbose >= logger.DEBUG: label = mol.ao_labels() molabel = [] irorbcnt = {} for k, j in enumerate(orbsyma): if j in irorbcnt: irorbcnt[j] += 1 else: irorbcnt[j] = 1 molabel.append('#%-d(%s #%d)' % (k+MO_BASE, irname_full[j], irorbcnt[j])) if with_meta_lowdin: log.debug(' ** alpha MO coefficients (expansion on meta-Lowdin AOs) **') orth_coeff = orth.orth_ao(mol, 'meta_lowdin', s=ovlp_ao) c_inv = numpy.dot(orth_coeff.conj().T, ovlp_ao) mo = c_inv.dot(mo_coeff[0]) else: log.debug(' ** alpha MO coefficients (expansion on AOs) **') mo = mo_coeff[0] dump_mat.dump_rec(mf.stdout, mo, label, start=MO_BASE, **kwargs) molabel = [] irorbcnt = {} for k, j in enumerate(orbsymb): if j in irorbcnt: irorbcnt[j] += 1 else: irorbcnt[j] = 1 molabel.append('#%-d(%s #%d)' % (k+MO_BASE, irname_full[j], irorbcnt[j])) if with_meta_lowdin: log.debug(' ** beta MO coefficients (expansion on meta-Lowdin AOs) **') mo = c_inv.dot(mo_coeff[1]) else: log.debug(' ** beta MO coefficients (expansion on AOs) **') mo = mo_coeff[1] dump_mat.dump_rec(mol.stdout, mo, label, molabel, start=MO_BASE, **kwargs) dm = mf.make_rdm1(mo_coeff, mo_occ) if with_meta_lowdin: pop_and_charge = mf.mulliken_meta(mol, dm, s=ovlp_ao, verbose=log) else: pop_and_charge = mf.mulliken_pop(mol, dm, s=ovlp_ao, verbose=log) dip = mf.dip_moment(mol, dm, verbose=log) return pop_and_charge, dip
def analyze(mf, verbose=logger.DEBUG): from pyscf.lo import orth from pyscf.tools import dump_mat mo_energy = mf.mo_energy mo_occ = mf.mo_occ mo_coeff = mf.mo_coeff mol = mf.mol log = pyscf.lib.logger.Logger(mf.stdout, verbose) nirrep = len(mol.irrep_id) ovlp_ao = mf.get_ovlp() orbsyma = symm.label_orb_symm(mol, mol.irrep_id, mol.symm_orb, mo_coeff[0], s=ovlp_ao) orbsymb = symm.label_orb_symm(mol, mol.irrep_id, mol.symm_orb, mo_coeff[1], s=ovlp_ao) orbsyma = numpy.array(orbsyma) orbsymb = numpy.array(orbsymb) tot_sym = 0 noccsa = [sum(orbsyma[mo_occ[0]>0]==ir) for ir in mol.irrep_id] noccsb = [sum(orbsymb[mo_occ[1]>0]==ir) for ir in mol.irrep_id] for i, ir in enumerate(mol.irrep_id): if (noccsa[i]+noccsb[i]) % 2: tot_sym ^= ir if mol.groupname in ('Dooh', 'Coov', 'SO3'): log.note('TODO: total symmetry for %s', mol.groupname) else: log.note('total symmetry = %s', symm.irrep_id2name(mol.groupname, tot_sym)) log.note('alpha occupancy for each irrep: '+(' %4s'*nirrep), *mol.irrep_name) log.note(' '+(' %4d'*nirrep), *noccsa) log.note('beta occupancy for each irrep: '+(' %4s'*nirrep), *mol.irrep_name) log.note(' '+(' %4d'*nirrep), *noccsb) ss, s = mf.spin_square((mo_coeff[0][:,mo_occ[0]>0], mo_coeff[1][:,mo_occ[1]>0]), ovlp_ao) log.note('multiplicity <S^2> = %.8g 2S+1 = %.8g', ss, s) if verbose >= logger.NOTE: log.note('**** MO energy ****') irname_full = {} for k, ir in enumerate(mol.irrep_id): irname_full[ir] = mol.irrep_name[k] irorbcnt = {} for k, j in enumerate(orbsyma): if j in irorbcnt: irorbcnt[j] += 1 else: irorbcnt[j] = 1 log.note('alpha MO #%d (%s #%d), energy= %.15g occ= %g', k+1, irname_full[j], irorbcnt[j], mo_energy[0][k], mo_occ[0][k]) irorbcnt = {} for k, j in enumerate(orbsymb): if j in irorbcnt: irorbcnt[j] += 1 else: irorbcnt[j] = 1 log.note('beta MO #%d (%s #%d), energy= %.15g occ= %g', k+1, irname_full[j], irorbcnt[j], mo_energy[1][k], mo_occ[1][k]) ovlp_ao = mf.get_ovlp() if mf.verbose >= logger.DEBUG: label = mol.spheric_labels(True) molabel = [] irorbcnt = {} for k, j in enumerate(orbsyma): if j in irorbcnt: irorbcnt[j] += 1 else: irorbcnt[j] = 1 molabel.append('#%-d(%s #%d)' % (k+1, irname_full[j], irorbcnt[j])) log.debug(' ** alpha MO coefficients (expansion on meta-Lowdin AOs) **') orth_coeff = orth.orth_ao(mol, 'meta_lowdin', s=ovlp_ao) c_inv = numpy.dot(orth_coeff.T, ovlp_ao) dump_mat.dump_rec(mol.stdout, c_inv.dot(mo_coeff[0]), label, molabel, start=1) molabel = [] irorbcnt = {} for k, j in enumerate(orbsymb): if j in irorbcnt: irorbcnt[j] += 1 else: irorbcnt[j] = 1 molabel.append('#%-d(%s #%d)' % (k+1, irname_full[j], irorbcnt[j])) log.debug(' ** beta MO coefficients (expansion on meta-Lowdin AOs) **') dump_mat.dump_rec(mol.stdout, c_inv.dot(mo_coeff[1]), label, molabel, start=1) dm = mf.make_rdm1(mo_coeff, mo_occ) return mf.mulliken_meta(mol, dm, s=ovlp_ao, verbose=log)
def __init__(self, the_mf, active_orbs, localizationtype, ao_rotation=None, use_full_hessian=True, localization_threshold=1e-6): assert ((localizationtype == 'meta_lowdin') or (localizationtype == 'boys') or (localizationtype == 'lowdin') or (localizationtype == 'iao')) # Information on the full SCF problem self.mo_occ = the_mf.mo_occ self.mol = the_mf.mol self.s1e_ao = the_mf.get_ovlp() self.fullEhf = the_mf.e_tot #self.fullDMao = np.dot(np.dot( the_mf.mo_coeff, np.diag( the_mf.mo_occ )), the_mf.mo_coeff.T ) self.fullDMao = the_mf.make_rdm1() #self.fullJKao = scf.hf.get_veff( self.mol, self.fullDMao, 0, 0, 1 ) #Last 3 numbers: dm_last, vhf_last, hermi #self.fullJKao = the_mf.get_veff() #self.fullFOCKao = self.mol.intor_symmetric('int1e_kin') + self.mol.intor_symmetric('int1e_nuc') + self.fullJKao self.fullFOCKao = the_mf.get_fock() self.fullOEIao = the_mf.get_hcore() # Active space information self._which = localizationtype self.active = np.zeros([self.mol.nao_nr()], dtype=int) self.active[active_orbs] = 1 self.NOrb = np.sum(self.active) # Number of active space orbitals self.Nelec = int( np.rint(self.mol.nelectron - np.sum(the_mf.mo_occ[self.active == 0])) ) # Total number of electrons minus frozen part # Localize the orbitals if ((self._which == 'meta_lowdin') or (self._which == 'boys')): if (self._which == 'meta_lowdin'): assert (self.NOrb == self.mol.nao_nr() ) # Full active space required if (self._which == 'boys'): self.ao2loc = the_mf.mo_coeff[:, self.active == 1] if (self.NOrb == self.mol.nao_nr() ): # If you want the full active, do meta-Lowdin nao.AOSHELL[4] = ['1s0p0d0f', '2s1p0d0f' ] # redefine the valence shell for Be self.ao2loc = orth.orth_ao(self.mol, 'meta_lowdin') if (ao_rotation is not None): self.ao2loc = np.dot(self.ao2loc, ao_rotation.T) if (self._which == 'boys'): old_verbose = self.mol.verbose self.mol.verbose = 5 #loc = localizer.localizer( self.mol, self.ao2loc, self._which, use_full_hessian ) boys = Boys(self.mol, self.ao2loc) self.mol.verbose = old_verbose #self.ao2loc = loc.optimize( threshold=localization_threshold ) self.ao2loc = boys.kernel() self.TI_OK = False # Check yourself if OK, then overwrite if (self._which == 'lowdin'): assert (self.NOrb == self.mol.nao_nr() ) # Full active space required ovlp = self.mol.intor_symmetric('int1e_ovlp') ovlp_eigs, ovlp_vecs = np.linalg.eigh(ovlp) assert (np.linalg.norm( np.dot(np.dot(ovlp_vecs, np.diag(ovlp_eigs)), ovlp_vecs.T) - ovlp) < 1e-10) self.ao2loc = np.dot( np.dot(ovlp_vecs, np.diag(np.power(ovlp_eigs, -0.5))), ovlp_vecs.T) self.TI_OK = False # Check yourself if OK, then overwrite if (self._which == 'iao'): assert (self.NOrb == self.mol.nao_nr() ) # Full active space assumed self.ao2loc = iao_helper.localize_iao(self.mol, the_mf) if (ao_rotation is not None): self.ao2loc = np.dot(self.ao2loc, ao_rotation.T) self.TI_OK = False # Check yourself if OK, then overwrite #self.molden( 'dump.molden' ) # Debugging mode assert (self.loc_ortho() < 1e-9) ''' # Effective Hamiltonian due to frozen part self.frozenDMmo = np.array( the_mf.mo_occ, copy=True ) self.frozenDMmo[ self.active==1 ] = 0 # Only the frozen MO occupancies nonzero self.frozenDMao = np.dot(np.dot( the_mf.mo_coeff, np.diag( self.frozenDMmo )), the_mf.mo_coeff.T ) self.frozenJKao = scf.hf.get_veff( self.mol, self.frozenDMao, 0, 0, 1 ) #Last 3 numbers: dm_last, vhf_last, hermi self.frozenOEIao = self.fullFOCKao - self.fullJKao + self.frozenJKao ''' self.frozenOEIao = self.fullOEIao # Active space OEI and ERI self.activeCONST = self.mol.energy_nuc( ) #+ np.einsum( 'ij,ij->', self.frozenOEIao - 0.5*self.frozenJKao, self.frozenDMao ) self.activeOEI = np.dot(np.dot(self.ao2loc.T, self.frozenOEIao), self.ao2loc) self.activeFOCK = np.dot(np.dot(self.ao2loc.T, self.fullFOCKao), self.ao2loc) if (self.NOrb <= 150): self.ERIinMEM = True if (self.mol.cart): intor = 'int2e_cart' else: intor = 'int2e_sph' self.activeERI = ao2mo.outcore.full_iofree( self.mol, self.ao2loc, intor, compact=False).reshape(self.NOrb, self.NOrb, self.NOrb, self.NOrb) else: self.ERIinMEM = False self.activeERI = None
def analyze(casscf, mo_coeff=None, ci=None, verbose=logger.INFO, large_ci_tol=.1, **kwargs): from pyscf.lo import orth from pyscf.tools import dump_mat if mo_coeff is None: mo_coeff = casscf.mo_coeff if ci is None: ci = casscf.ci if isinstance(verbose, logger.Logger): log = verbose else: log = logger.Logger(casscf.stdout, verbose) nelecas = casscf.nelecas ncas = casscf.ncas ncore = casscf.ncore nocc = ncore + ncas label = casscf.mol.spheric_labels(True) if isinstance(ci, (tuple, list)): ci0 = ci[0] log.info('** Natural natural orbitals are based on the first root **') else: ci0 = ci if ci0 is None and hasattr(casscf, 'casdm1'): casdm1 = casscf.casdm1 mocore = mo_coeff[:,:ncore] mocas = mo_coeff[:,ncore:nocc] dm1a =(numpy.dot(mocore, mocore.T) * 2 + reduce(numpy.dot, (mocas, casdm1, mocas.T))) dm1b = None dm1 = dm1a elif hasattr(casscf.fcisolver, 'make_rdm1s'): casdm1a, casdm1b = casscf.fcisolver.make_rdm1s(ci0, ncas, nelecas) casdm1 = casdm1a + casdm1b mocore = mo_coeff[:,:ncore] mocas = mo_coeff[:,ncore:nocc] dm1b = numpy.dot(mocore, mocore.T) dm1a = dm1b + reduce(numpy.dot, (mocas, casdm1a, mocas.T)) dm1b += reduce(numpy.dot, (mocas, casdm1b, mocas.T)) dm1 = dm1a + dm1b if log.verbose >= logger.DEBUG1: log.info('alpha density matrix (on AO)') dump_mat.dump_tri(log.stdout, dm1a, label, **kwargs) log.info('beta density matrix (on AO)') dump_mat.dump_tri(log.stdout, dm1b, label, **kwargs) else: casdm1 = casscf.fcisolver.make_rdm1(ci0, ncas, nelecas) mocore = mo_coeff[:,:ncore] mocas = mo_coeff[:,ncore:nocc] dm1a =(numpy.dot(mocore, mocore.T) * 2 + reduce(numpy.dot, (mocas, casdm1, mocas.T))) dm1b = None dm1 = dm1a if log.verbose >= logger.INFO: ovlp_ao = casscf._scf.get_ovlp() # note the last two args of ._eig for mc1step_symm occ, ucas = casscf._eig(-casdm1, ncore, nocc) log.info('Natural occ %s', str(-occ)) for i, k in enumerate(numpy.argmax(abs(ucas), axis=0)): if ucas[k,i] < 0: ucas[:,i] *= -1 orth_coeff = orth.orth_ao(casscf.mol, 'meta_lowdin', s=ovlp_ao) mo_cas = reduce(numpy.dot, (orth_coeff.T, ovlp_ao, mo_coeff[:,ncore:nocc], ucas)) log.info('Natural orbital (expansion on meta-Lowdin AOs) in CAS space') dump_mat.dump_rec(log.stdout, mo_cas, label, start=1, **kwargs) if casscf._scf.mo_coeff is not None: s = reduce(numpy.dot, (casscf.mo_coeff.T, ovlp_ao, casscf._scf.mo_coeff)) idx = numpy.argwhere(abs(s)>.4) for i,j in idx: log.info('<mo-mcscf|mo-hf> %d %d %12.8f', i+1, j+1, s[i,j]) if hasattr(casscf.fcisolver, 'large_ci') and ci is not None: log.info('** Largest CI components **') if isinstance(ci, (tuple, list)): for i, civec in enumerate(ci): res = casscf.fcisolver.large_ci(civec, casscf.ncas, casscf.nelecas) log.info(' string alpha, string beta, state %d CI coefficient', i) for c,ia,ib in res: log.info(' %9s %9s %.12f', ia, ib, c) else: log.info(' string alpha, string beta, CI coefficient') res = casscf.fcisolver.large_ci(ci, casscf.ncas, casscf.nelecas) for c,ia,ib in res: log.info(' %9s %9s %.12f', ia, ib, c) casscf._scf.mulliken_meta(casscf.mol, dm1, s=ovlp_ao, verbose=log) return dm1a, dm1b
def analyze(mf, verbose=logger.DEBUG, with_meta_lowdin=WITH_META_LOWDIN, **kwargs): '''Analyze the given SCF object: print orbital energies, occupancies; print orbital coefficients; Mulliken population analysis; Dipole moment ''' from pyscf.lo import orth from pyscf.tools import dump_mat mo_energy = mf.mo_energy mo_occ = mf.mo_occ mo_coeff = mf.mo_coeff nmo = len(mo_occ[0]) log = logger.new_logger(mf, verbose) if log.verbose >= logger.NOTE: log.note('**** MO energy ****') log.note( ' alpha | beta alpha | beta' ) for i in range(nmo): log.note('MO #%-3d energy= %-18.15g | %-18.15g occ= %g | %g', i + MO_BASE, mo_energy[0][i], mo_energy[1][i], mo_occ[0][i], mo_occ[1][i]) ovlp_ao = mf.get_ovlp() if log.verbose >= logger.DEBUG: label = mf.mol.ao_labels() if with_meta_lowdin: log.debug( ' ** MO coefficients (expansion on meta-Lowdin AOs) for alpha spin **' ) orth_coeff = orth.orth_ao(mf.mol, 'meta_lowdin', s=ovlp_ao) c_inv = numpy.dot(orth_coeff.T, ovlp_ao) dump_mat.dump_rec(mf.stdout, c_inv.dot(mo_coeff[0]), label, start=MO_BASE, **kwargs) log.debug( ' ** MO coefficients (expansion on meta-Lowdin AOs) for beta spin **' ) dump_mat.dump_rec(mf.stdout, c_inv.dot(mo_coeff[1]), label, start=MO_BASE, **kwargs) else: log.debug( ' ** MO coefficients (expansion on AOs) for alpha spin **') dump_mat.dump_rec(mf.stdout, mo_coeff[0], label, start=MO_BASE, **kwargs) log.debug( ' ** MO coefficients (expansion on AOs) for beta spin **') dump_mat.dump_rec(mf.stdout, mo_coeff[1], label, start=MO_BASE, **kwargs) dm = mf.make_rdm1(mo_coeff, mo_occ) if with_meta_lowdin: return (mf.mulliken_meta(mf.mol, dm, s=ovlp_ao, verbose=log), mf.dip_moment(mf.mol, dm, verbose=log)) else: return (mf.mulliken_pop(mf.mol, dm, s=ovlp_ao, verbose=log), mf.dip_moment(mf.mol, dm, verbose=log))
def analyze(mf, verbose=logger.DEBUG, **kwargs): from pyscf.lo import orth from pyscf.tools import dump_mat mol = mf.mol if not mol.symmetry: return uhf.analyze(mf, verbose, **kwargs) mo_energy = mf.mo_energy mo_occ = mf.mo_occ mo_coeff = mf.mo_coeff log = logger.Logger(mf.stdout, verbose) nirrep = len(mol.irrep_id) ovlp_ao = mf.get_ovlp() orbsyma = symm.label_orb_symm(mol, mol.irrep_id, mol.symm_orb, mo_coeff[0], ovlp_ao, False) orbsymb = symm.label_orb_symm(mol, mol.irrep_id, mol.symm_orb, mo_coeff[1], ovlp_ao, False) orbsyma = numpy.array(orbsyma) orbsymb = numpy.array(orbsymb) tot_sym = 0 noccsa = [sum(orbsyma[mo_occ[0]>0]==ir) for ir in mol.irrep_id] noccsb = [sum(orbsymb[mo_occ[1]>0]==ir) for ir in mol.irrep_id] for i, ir in enumerate(mol.irrep_id): if (noccsa[i]+noccsb[i]) % 2: tot_sym ^= ir if mol.groupname in ('Dooh', 'Coov', 'SO3'): log.note('TODO: total symmetry for %s', mol.groupname) else: log.note('total symmetry = %s', symm.irrep_id2name(mol.groupname, tot_sym)) log.note('alpha occupancy for each irrep: '+(' %4s'*nirrep), *mol.irrep_name) log.note(' '+(' %4d'*nirrep), *noccsa) log.note('beta occupancy for each irrep: '+(' %4s'*nirrep), *mol.irrep_name) log.note(' '+(' %4d'*nirrep), *noccsb) ss, s = mf.spin_square((mo_coeff[0][:,mo_occ[0]>0], mo_coeff[1][:,mo_occ[1]>0]), ovlp_ao) log.note('multiplicity <S^2> = %.8g 2S+1 = %.8g', ss, s) if verbose >= logger.NOTE: log.note('**** MO energy ****') irname_full = {} for k, ir in enumerate(mol.irrep_id): irname_full[ir] = mol.irrep_name[k] irorbcnt = {} for k, j in enumerate(orbsyma): if j in irorbcnt: irorbcnt[j] += 1 else: irorbcnt[j] = 1 log.note('alpha MO #%d (%s #%d), energy= %.15g occ= %g', k+1, irname_full[j], irorbcnt[j], mo_energy[0][k], mo_occ[0][k]) irorbcnt = {} for k, j in enumerate(orbsymb): if j in irorbcnt: irorbcnt[j] += 1 else: irorbcnt[j] = 1 log.note('beta MO #%d (%s #%d), energy= %.15g occ= %g', k+1, irname_full[j], irorbcnt[j], mo_energy[1][k], mo_occ[1][k]) ovlp_ao = mf.get_ovlp() if mf.verbose >= logger.DEBUG: label = mol.spheric_labels(True) molabel = [] irorbcnt = {} for k, j in enumerate(orbsyma): if j in irorbcnt: irorbcnt[j] += 1 else: irorbcnt[j] = 1 molabel.append('#%-d(%s #%d)' % (k+1, irname_full[j], irorbcnt[j])) log.debug(' ** alpha MO coefficients (expansion on meta-Lowdin AOs) **') orth_coeff = orth.orth_ao(mol, 'meta_lowdin', s=ovlp_ao) c_inv = numpy.dot(orth_coeff.T, ovlp_ao) dump_mat.dump_rec(mol.stdout, c_inv.dot(mo_coeff[0]), label, molabel, start=1, **kwargs) molabel = [] irorbcnt = {} for k, j in enumerate(orbsymb): if j in irorbcnt: irorbcnt[j] += 1 else: irorbcnt[j] = 1 molabel.append('#%-d(%s #%d)' % (k+1, irname_full[j], irorbcnt[j])) log.debug(' ** beta MO coefficients (expansion on meta-Lowdin AOs) **') dump_mat.dump_rec(mol.stdout, c_inv.dot(mo_coeff[1]), label, molabel, start=1, **kwargs) dm = mf.make_rdm1(mo_coeff, mo_occ) return mf.mulliken_meta(mol, dm, s=ovlp_ao, verbose=log)
def atomic_pops(mol, mo_coeff, method='meta_lowdin', mf=None): ''' Kwargs: method : string The atomic population projection scheme. It can be mulliken, lowdin, meta_lowdin, iao, or becke Returns: A 3-index tensor [A,i,j] indicates the population of any orbital-pair density |i><j| for each species (atom in this case). This tensor is used to construct the population and gradients etc. You can customize the PM localization wrt other population metric, such as the charge of a site, the charge of a fragment (a group of atoms) by overwriting this tensor. See also the example pyscf/examples/loc_orb/40-hubbard_model_PM_localization.py for the PM localization of site-based population for hubbard model. ''' method = method.lower().replace('_', '-') nmo = mo_coeff.shape[1] proj = numpy.empty((mol.natm, nmo, nmo)) if getattr(mol, 'pbc_intor', None): # whether mol object is a cell s = mol.pbc_intor('int1e_ovlp_sph', hermi=1) else: s = mol.intor_symmetric('int1e_ovlp') if method == 'becke': from pyscf.dft import gen_grid if not (getattr(mf, 'grids', None) and getattr(mf, '_numint', None)): # Call DFT to initialize grids and numint objects mf = mol.RKS() grids = mf.grids ni = mf._numint if not isinstance(grids, gen_grid.Grids): raise NotImplementedError('PM becke scheme for PBC systems') # The atom-wise Becke grids (without concatenated to a vector of grids) coords, weights = grids.get_partition(mol, concat=False) for i in range(mol.natm): ao = ni.eval_ao(mol, coords[i], deriv=0) aow = numpy.einsum('pi,p->pi', ao, weights[i]) charge_matrix = lib.dot(aow.conj().T, ao) proj[i] = reduce(lib.dot, (mo_coeff.conj().T, charge_matrix, mo_coeff)) elif method == 'mulliken': for i, (b0, b1, p0, p1) in enumerate(mol.offset_nr_by_atom()): csc = reduce(numpy.dot, (mo_coeff[p0:p1].conj().T, s[p0:p1], mo_coeff)) proj[i] = (csc + csc.conj().T) * .5 elif method in ('lowdin', 'meta-lowdin'): c = orth.restore_ao_character(mol, 'ANO') #csc = reduce(lib.dot, (mo_coeff.conj().T, s, orth_local_ao_coeff)) csc = reduce(lib.dot, (mo_coeff.conj().T, s, orth.orth_ao(mol, method, c, s=s))) for i, (b0, b1, p0, p1) in enumerate(mol.offset_nr_by_atom()): proj[i] = numpy.dot(csc[:, p0:p1], csc[:, p0:p1].conj().T) elif method in ('iao', 'ibo'): from pyscf.lo import iao assert mf is not None # FIXME: How to handle UHF/UKS object? orb_occ = mf.mo_coeff[:, mf.mo_occ > 0] iao_coeff = iao.iao(mol, orb_occ) # # IAO is generally not orthogonalized. For simplicity, we take Lowdin # orthogonalization here. Other orthogonalization can be used. Results # should be very closed to the Lowdin-orth orbitals # # PM with Mulliken population of non-orth IAOs can be found in # ibo.PipekMezey function # iao_coeff = orth.vec_lowdin(iao_coeff, s) csc = reduce(lib.dot, (mo_coeff.conj().T, s, iao_coeff)) iao_mol = iao.reference_mol(mol) for i, (b0, b1, p0, p1) in enumerate(iao_mol.offset_nr_by_atom()): proj[i] = numpy.dot(csc[:, p0:p1], csc[:, p0:p1].conj().T) else: raise KeyError('method = %s' % method) return proj
def analyze(self, verbose=None, with_meta_lowdin=WITH_META_LOWDIN, **kwargs): if verbose is None: verbose = self.verbose from pyscf.lo import orth from pyscf.tools import dump_mat if not self.mol.symmetry: return rohf.ROHF.analyze(self, verbose, with_meta_lowdin, **kwargs) mol = self.mol mo_energy = self.mo_energy mo_occ = self.mo_occ mo_coeff = self.mo_coeff ovlp_ao = self.get_ovlp() log = logger.new_logger(self, verbose) if log.verbose >= logger.NOTE: nirrep = len(mol.irrep_id) orbsym = get_orbsym(self.mol, mo_coeff) wfnsym = 0 ndoccs = [] nsoccs = [] for k, ir in enumerate(mol.irrep_id): ndoccs.append(sum(orbsym[mo_occ == 2] == ir)) nsoccs.append(sum(orbsym[mo_occ == 1] == ir)) if nsoccs[k] % 2 == 1: wfnsym ^= ir if mol.groupname in ('SO3', 'Dooh', 'Coov'): log.note('TODO: total wave-function symmetry for %s', mol.groupname) else: log.note('Wave-function symmetry = %s', symm.irrep_id2name(mol.groupname, wfnsym)) log.note('occupancy for each irrep: ' + (' %4s' * nirrep), *mol.irrep_name) log.note('double occ ' + (' %4d' * nirrep), *ndoccs) log.note('single occ ' + (' %4d' * nirrep), *nsoccs) log.note('**** MO energy ****') irname_full = {} for k, ir in enumerate(mol.irrep_id): irname_full[ir] = mol.irrep_name[k] irorbcnt = {} if getattr(mo_energy, 'mo_ea', None) is not None: mo_ea = mo_energy.mo_ea mo_eb = mo_energy.mo_eb log.note( ' Roothaan | alpha | beta' ) for k, j in enumerate(orbsym): if j in irorbcnt: irorbcnt[j] += 1 else: irorbcnt[j] = 1 log.note( 'MO #%-4d(%-3s #%-2d) energy= %-18.15g | %-18.15g | %-18.15g occ= %g', k + MO_BASE, irname_full[j], irorbcnt[j], mo_energy[k], mo_ea[k], mo_eb[k], mo_occ[k]) else: for k, j in enumerate(orbsym): if j in irorbcnt: irorbcnt[j] += 1 else: irorbcnt[j] = 1 log.note('MO #%-3d (%s #%-2d), energy= %-18.15g occ= %g', k + MO_BASE, irname_full[j], irorbcnt[j], mo_energy[k], mo_occ[k]) if log.verbose >= logger.DEBUG: label = mol.ao_labels() molabel = [] irorbcnt = {} for k, j in enumerate(orbsym): if j in irorbcnt: irorbcnt[j] += 1 else: irorbcnt[j] = 1 molabel.append('#%-d(%s #%d)' % (k + MO_BASE, irname_full[j], irorbcnt[j])) if with_meta_lowdin: log.debug( ' ** MO coefficients (expansion on meta-Lowdin AOs) **') orth_coeff = orth.orth_ao(mol, 'meta_lowdin', s=ovlp_ao) c = reduce(numpy.dot, (orth_coeff.T, ovlp_ao, mo_coeff)) else: log.debug(' ** MO coefficients (expansion on AOs) **') c = mo_coeff dump_mat.dump_rec(self.stdout, c, label, molabel, start=MO_BASE, **kwargs) dm = self.make_rdm1(mo_coeff, mo_occ) if with_meta_lowdin: pop_and_charge = self.mulliken_meta(mol, dm, s=ovlp_ao, verbose=log) else: pop_and_charge = self.mulliken_pop(mol, dm, s=ovlp_ao, verbose=log) dip = self.dip_moment(mol, dm, verbose=log) return pop_and_charge, dip
def analyze(casscf, mo_coeff=None, ci=None, verbose=logger.INFO, large_ci_tol=.1, **kwargs): from pyscf.lo import orth from pyscf.tools import dump_mat if mo_coeff is None: mo_coeff = casscf.mo_coeff if ci is None: ci = casscf.ci if isinstance(verbose, logger.Logger): log = verbose else: log = logger.Logger(casscf.stdout, verbose) nelecas = casscf.nelecas ncas = casscf.ncas ncore = casscf.ncore nocc = ncore + ncas label = casscf.mol.ao_labels() if isinstance(ci, (tuple, list)): ci0 = ci[0] log.info('** Natural natural orbitals are based on the first root **') else: ci0 = ci if ci0 is None and hasattr(casscf, 'casdm1'): casdm1 = casscf.casdm1 mocore = mo_coeff[:,:ncore] mocas = mo_coeff[:,ncore:nocc] dm1a =(numpy.dot(mocore, mocore.T) * 2 + reduce(numpy.dot, (mocas, casdm1, mocas.T))) dm1b = None dm1 = dm1a elif hasattr(casscf.fcisolver, 'make_rdm1s'): casdm1a, casdm1b = casscf.fcisolver.make_rdm1s(ci0, ncas, nelecas) casdm1 = casdm1a + casdm1b mocore = mo_coeff[:,:ncore] mocas = mo_coeff[:,ncore:nocc] dm1b = numpy.dot(mocore, mocore.T) dm1a = dm1b + reduce(numpy.dot, (mocas, casdm1a, mocas.T)) dm1b += reduce(numpy.dot, (mocas, casdm1b, mocas.T)) dm1 = dm1a + dm1b if log.verbose >= logger.DEBUG1: log.info('alpha density matrix (on AO)') dump_mat.dump_tri(log.stdout, dm1a, label, **kwargs) log.info('beta density matrix (on AO)') dump_mat.dump_tri(log.stdout, dm1b, label, **kwargs) else: casdm1 = casscf.fcisolver.make_rdm1(ci0, ncas, nelecas) mocore = mo_coeff[:,:ncore] mocas = mo_coeff[:,ncore:nocc] dm1a =(numpy.dot(mocore, mocore.T) * 2 + reduce(numpy.dot, (mocas, casdm1, mocas.T))) dm1b = None dm1 = dm1a if log.verbose >= logger.INFO: ovlp_ao = casscf._scf.get_ovlp() # note the last two args of ._eig for mc1step_symm occ, ucas = casscf._eig(-casdm1, ncore, nocc) log.info('Natural occ %s', str(-occ)) for i, k in enumerate(numpy.argmax(abs(ucas), axis=0)): if ucas[k,i] < 0: ucas[:,i] *= -1 orth_coeff = orth.orth_ao(casscf.mol, 'meta_lowdin', s=ovlp_ao) mo_cas = reduce(numpy.dot, (orth_coeff.T, ovlp_ao, mo_coeff[:,ncore:nocc], ucas)) log.info('Natural orbital (expansion on meta-Lowdin AOs) in CAS space') dump_mat.dump_rec(log.stdout, mo_cas, label, start=1, **kwargs) if casscf._scf.mo_coeff is not None: s = reduce(numpy.dot, (casscf.mo_coeff.T, ovlp_ao, casscf._scf.mo_coeff)) idx = numpy.argwhere(abs(s)>.4) for i,j in idx: log.info('<mo-mcscf|mo-hf> %d %d %12.8f', i+1, j+1, s[i,j]) if hasattr(casscf.fcisolver, 'large_ci') and ci is not None: log.info('** Largest CI components **') if isinstance(ci, (tuple, list)): for i, civec in enumerate(ci): res = casscf.fcisolver.large_ci(civec, casscf.ncas, casscf.nelecas, large_ci_tol, return_strs=False) log.info(' [alpha occ-orbitals] [beta occ-orbitals] state %-3d CI coefficient', i) for c,ia,ib in res: log.info(' %-20s %-30s %.12f', ia, ib, c) else: log.info(' [alpha occ-orbitals] [beta occ-orbitals] CI coefficient') res = casscf.fcisolver.large_ci(ci, casscf.ncas, casscf.nelecas, large_ci_tol, return_strs=False) for c,ia,ib in res: log.info(' %-20s %-30s %.12f', ia, ib, c) casscf._scf.mulliken_meta(casscf.mol, dm1, s=ovlp_ao, verbose=log) return dm1a, dm1b