def uhf_internal(mf, with_symmetry=True, verbose=None): log = logger.new_logger(mf, verbose) g, hop, hdiag = newton_ah.gen_g_hop_uhf(mf, mf.mo_coeff, mf.mo_occ, with_symmetry=with_symmetry) hdiag *= 2 def precond(dx, e, x0): hdiagd = hdiag - e hdiagd[abs(hdiagd)<1e-8] = 1e-8 return dx/hdiagd def hessian_x(x): # See comments in function rhf_internal return hop(x).real * 2 x0 = numpy.zeros_like(g) x0[g!=0] = 1. / hdiag[g!=0] if not with_symmetry: # allow to break point group symmetry x0[numpy.argmin(hdiag)] = 1 e, v = lib.davidson(hessian_x, x0, precond, tol=1e-4, verbose=log) if e < -1e-5: log.note('UHF/UKS wavefunction has an internal instablity.') nocca = numpy.count_nonzero(mf.mo_occ[0]> 0) nvira = numpy.count_nonzero(mf.mo_occ[0]==0) mo = (_rotate_mo(mf.mo_coeff[0], mf.mo_occ[0], v[:nocca*nvira]), _rotate_mo(mf.mo_coeff[1], mf.mo_occ[1], v[nocca*nvira:])) else: log.note('UHF/UKS wavefunction is stable in the intenral stability analysis') mo = mf.mo_coeff return mo
def casci(self, mo_coeff, ci0=None, eris=None, verbose=None, envs=None): if eris is None: fcasci = copy.copy(self) fcasci.ao2mo = self.get_h2cas else: fcasci = _fake_h_for_fast_casci(self, mo_coeff, eris) log = logger.new_logger(self, verbose) e_tot, e_cas, fcivec = ucasci.kernel(fcasci, mo_coeff, ci0, log) if envs is not None and log.verbose >= logger.INFO: log.debug('CAS space CI energy = %.15g', e_cas) if 'imicro' in envs: # Within CASSCF iteration log.info('macro iter %d (%d JK %d micro), ' 'UCASSCF E = %.15g dE = %.8g', envs['imacro'], envs['njk'], envs['imicro'], e_tot, e_tot-envs['elast']) if 'norm_gci' in envs: log.info(' |grad[o]|=%5.3g ' '|grad[c]|= %s |ddm|=%5.3g', envs['norm_gorb0'], envs['norm_gci'], envs['norm_ddm']) else: log.info(' |grad[o]|=%5.3g |ddm|=%5.3g', envs['norm_gorb0'], envs['norm_ddm']) else: # Initialization step log.info('UCASCI E = %.15g', e_tot) return e_tot, e_cas, fcivec
def rhf_external(mf, with_symmetry=True, verbose=None): log = logger.new_logger(mf, verbose) hop1, hdiag1, hop2, hdiag2 = _gen_hop_rhf_external(mf, with_symmetry) def precond(dx, e, x0): hdiagd = hdiag1 - e hdiagd[abs(hdiagd)<1e-8] = 1e-8 return dx/hdiagd x0 = numpy.zeros_like(hdiag1) x0[hdiag1>1e-5] = 1. / hdiag1[hdiag1>1e-5] if not with_symmetry: # allow to break point group symmetry x0[numpy.argmin(hdiag1)] = 1 e1, v1 = lib.davidson(hop1, x0, precond, tol=1e-4, verbose=log) if e1 < -1e-5: log.note('RHF/RKS wavefunction has a real -> complex instablity') else: log.note('RHF/RKS wavefunction is stable in the real -> complex stability analysis') def precond(dx, e, x0): hdiagd = hdiag2 - e hdiagd[abs(hdiagd)<1e-8] = 1e-8 return dx/hdiagd x0 = v1 e3, v3 = lib.davidson(hop2, x0, precond, tol=1e-4, verbose=log) if e3 < -1e-5: log.note('RHF/RKS wavefunction has a RHF/RKS -> UHF/UKS instablity.') mo = (_rotate_mo(mf.mo_coeff, mf.mo_occ, v3), mf.mo_coeff) else: log.note('RHF/RKS wavefunction is stable in the RHF/RKS -> UHF/UKS stability analysis') mo = (mf.mo_coeff, mf.mo_coeff) return mo
def kernel(self, h1, h2, norb, nelec, ci0=None, verbose=0, **kwargs): # Note self.orbsym is initialized lazily in mc1step_symm.kernel function log = logger.new_logger(self, verbose) es = [] cs = [] for solver, c0 in loop_solver(fcisolvers, ci0): e, c = solver.kernel(h1, h2, norb, get_nelec(solver, nelec), c0, orbsym=self.orbsym, verbose=log, **kwargs) if solver.nroots == 1: es.append(e) cs.append(c) else: es.extend(e) cs.extend(c) e_states[0] = es if log.verbose >= logger.DEBUG: if has_spin_square: ss, multip = collect(solver.spin_square(c0, norb, get_nelec(solver, nelec)) for solver, c0 in loop_civecs(fcisolvers, cs)) for i, ei in enumerate(es): log.debug('state %d E = %.15g S^2 = %.7f', i, ei, ss[i]) else: for i, ei in enumerate(es): log.debug('state %d E = %.15g', i, ei) return numpy.einsum('i,i', numpy.array(es), weights), cs
def ghf_stability(mf, verbose=None): log = logger.new_logger(mf, verbose) with_symmetry = True g, hop, hdiag = newton_ah.gen_g_hop_ghf(mf, mf.mo_coeff, mf.mo_occ, with_symmetry=with_symmetry) hdiag *= 2 def precond(dx, e, x0): hdiagd = hdiag - e hdiagd[abs(hdiagd)<1e-8] = 1e-8 return dx/hdiagd def hessian_x(x): # See comments in function rhf_internal return hop(x).real * 2 x0 = numpy.zeros_like(g) x0[g!=0] = 1. / hdiag[g!=0] if not with_symmetry: # allow to break point group symmetry x0[numpy.argmin(hdiag)] = 1 e, v = lib.davidson(hessian_x, x0, precond, tol=1e-4, verbose=log) if e < -1e-5: log.note('GHF wavefunction has an internal instablity') mo = _rotate_mo(mf.mo_coeff, mf.mo_occ, v) else: log.note('GHF wavefunction is stable in the intenral stability analysis') mo = mf.mo_coeff return mo
def gen_hop(hobj, mo_energy=None, mo_coeff=None, mo_occ=None, verbose=None): log = logger.new_logger(hobj, verbose) mol = hobj.mol mf = hobj.base if mo_energy is None: mo_energy = mf.mo_energy if mo_occ is None: mo_occ = mf.mo_occ if mo_coeff is None: mo_coeff = mf.mo_coeff natm = mol.natm nao, nmo = mo_coeff.shape mocc = mo_coeff[:,mo_occ>0] nocc = mocc.shape[1] atmlst = range(natm) max_memory = max(2000, hobj.max_memory - lib.current_memory()[0]) de2 = hobj.partial_hess_elec(mo_energy, mo_coeff, mo_occ, atmlst, max_memory, log) de2 += hobj.hess_nuc() # Compute H1 integrals and store in hobj.chkfile hobj.make_h1(mo_coeff, mo_occ, hobj.chkfile, atmlst, log) aoslices = mol.aoslice_by_atom() s1a = -mol.intor('int1e_ipovlp', comp=3) fvind = gen_vind(mf, mo_coeff, mo_occ) def h_op(x): x = x.reshape(natm,3) hx = numpy.einsum('abxy,ax->by', de2, x) h1ao = 0 s1ao = 0 for ia in range(natm): shl0, shl1, p0, p1 = aoslices[ia] h1ao_i = lib.chkfile.load(hobj.chkfile, 'scf_f1ao/%d' % ia) h1ao += numpy.einsum('x,xij->ij', x[ia], h1ao_i) s1ao_i = numpy.zeros((3,nao,nao)) s1ao_i[:,p0:p1] += s1a[:,p0:p1] s1ao_i[:,:,p0:p1] += s1a[:,p0:p1].transpose(0,2,1) s1ao += numpy.einsum('x,xij->ij', x[ia], s1ao_i) s1vo = reduce(numpy.dot, (mo_coeff.T, s1ao, mocc)) h1vo = reduce(numpy.dot, (mo_coeff.T, h1ao, mocc)) mo1, mo_e1 = cphf.solve(fvind, mo_energy, mo_occ, h1vo, s1vo) mo1 = numpy.dot(mo_coeff, mo1) mo_e1 = mo_e1.reshape(nocc,nocc) dm1 = numpy.einsum('pi,qi->pq', mo1, mocc) dme1 = numpy.einsum('pi,qi,i->pq', mo1, mocc, mo_energy[mo_occ>0]) dme1 = dme1 + dme1.T + reduce(numpy.dot, (mocc, mo_e1.T, mocc.T)) for ja in range(natm): q0, q1 = aoslices[ja][2:] h1ao = lib.chkfile.load(hobj.chkfile, 'scf_f1ao/%s'%ja) hx[ja] += numpy.einsum('xpq,pq->x', h1ao, dm1) * 4 hx[ja] -= numpy.einsum('xpq,pq->x', s1a[:,q0:q1], dme1[q0:q1]) * 2 hx[ja] -= numpy.einsum('xpq,qp->x', s1a[:,q0:q1], dme1[:,q0:q1]) * 2 return hx.ravel() hdiag = numpy.einsum('aaxx->ax', de2).ravel() return h_op, hdiag
def dump_flags(self, verbose=None): log = logger.new_logger(self, verbose) log.info('') log.info('******** CASCI flags ********') ncore = self.ncore ncas = self.ncas nvir = self.mo_coeff.shape[1] - ncore - ncas log.info('CAS (%de+%de, %do), ncore = %d, nvir = %d', \ self.nelecas[0], self.nelecas[1], ncas, ncore, nvir) assert(self.ncas > 0) log.info('natorb = %s', self.natorb) log.info('canonicalization = %s', self.canonicalization) log.info('sorting_mo_energy = %s', self.sorting_mo_energy) log.info('max_memory %d (MB)', self.max_memory) if getattr(self.fcisolver, 'dump_flags', None): self.fcisolver.dump_flags(log.verbose) if self.mo_coeff is None: log.error('Orbitals for CASCI are not specified. The relevant SCF ' 'object may not be initialized.') if (getattr(self._scf, 'with_solvent', None) and not getattr(self, 'with_solvent', None)): log.warn('''Solvent model %s was found in SCF object. It is not applied to the CASSCF object. The CASSCF result is not affected by the SCF solvent model. To enable the solvent model for CASSCF, a decoration to CASSCF object as below needs be called from pyscf import solvent mc = mcscf.CASSCF(...) mc = solvent.ddCOSMO(mc) ''', self._scf.with_solvent.__class__) return self
def rhf_internal(mf, verbose=None): log = logger.new_logger(mf, verbose) g, hop, hdiag = newton_ah.gen_g_hop_rhf(mf, mf.mo_coeff, mf.mo_occ) def precond(dx, e, x0): hdiagd = hdiag*2 - e hdiagd[abs(hdiagd)<1e-8] = 1e-8 return dx/hdiagd # The results of hop(x) corresponds to a displacement that reduces # gradients g. It is the vir-occ block of the matrix vector product # (Hessian*x). The occ-vir block equals to x2.T.conj(). The overall # Hessian for internal reotation is x2 + x2.T.conj(). This is # the reason we apply (.real * 2) below def hessian_x(x): return hop(x).real * 2 x0 = numpy.zeros_like(g) x0[g!=0] = 1. / hdiag[g!=0] e, v = lib.davidson(hessian_x, x0, precond, tol=1e-4, verbose=log) if e < -1e-5: log.log('KRHF/KRKS wavefunction has an internal instablity') mo = _rotate_mo(mf.mo_coeff, mf.mo_occ, v) else: log.log('KRHF/KRKS wavefunction is stable in the intenral stability analysis') mo = mf.mo_coeff return mo
def mulliken_pop(mol, dm, s=None, verbose=logger.DEBUG): r'''Mulliken population analysis .. math:: M_{ij} = D_{ij} S_{ji} Mulliken charges .. math:: \delta_i = \sum_j M_{ij} ''' if s is None: s = get_ovlp(mol) log = logger.new_logger(mol, verbose) pop = numpy.einsum('ij,ji->i', dm, s).real log.info(' ** Mulliken pop **') for i, s in enumerate(mol.spinor_labels()): log.info('pop of %s %10.5f', s, pop[i]) log.note(' ** Mulliken atomic charges **') chg = numpy.zeros(mol.natm) for i, s in enumerate(mol.spinor_labels(fmt=None)): chg[s[0]] += pop[i] chg = mol.atom_charges() - chg for ia in range(mol.natm): symb = mol.atom_symbol(ia) log.note('charge of %d%s = %10.5f', ia, symb, chg[ia]) return pop, chg
def get_jk_coulomb(mol, dm, hermi=1, coulomb_allow='SSSS', opt_llll=None, opt_ssll=None, opt_ssss=None, verbose=None): log = logger.new_logger(mol, verbose) if coulomb_allow.upper() == 'LLLL': log.debug('Coulomb integral: (LL|LL)') j1, k1 = _call_veff_llll(mol, dm, hermi, opt_llll) n2c = j1.shape[1] vj = numpy.zeros_like(dm) vk = numpy.zeros_like(dm) vj[...,:n2c,:n2c] = j1 vk[...,:n2c,:n2c] = k1 elif coulomb_allow.upper() == 'SSLL' \ or coulomb_allow.upper() == 'LLSS': log.debug('Coulomb integral: (LL|LL) + (SS|LL)') vj, vk = _call_veff_ssll(mol, dm, hermi, opt_ssll) j1, k1 = _call_veff_llll(mol, dm, hermi, opt_llll) n2c = j1.shape[1] vj[...,:n2c,:n2c] += j1 vk[...,:n2c,:n2c] += k1 else: # coulomb_allow == 'SSSS' log.debug('Coulomb integral: (LL|LL) + (SS|LL) + (SS|SS)') vj, vk = _call_veff_ssll(mol, dm, hermi, opt_ssll) j1, k1 = _call_veff_llll(mol, dm, hermi, opt_llll) n2c = j1.shape[1] vj[...,:n2c,:n2c] += j1 vk[...,:n2c,:n2c] += k1 j1, k1 = _call_veff_ssss(mol, dm, hermi, opt_ssss) vj[...,n2c:,n2c:] += j1 vk[...,n2c:,n2c:] += k1 return vj, vk
def get_jk(self, mol=None, dm=None, hermi=1): if mol is None: mol = self.mol if dm is None: dm = self.make_rdm1() t0 = (time.clock(), time.time()) log = logger.new_logger(self) if self.direct_scf and self.opt[0] is None: self.opt = self.init_direct_scf(mol) opt_llll, opt_ssll, opt_ssss, opt_gaunt = self.opt vj, vk = get_jk_coulomb(mol, dm, hermi, self._coulomb_now, opt_llll, opt_ssll, opt_ssss, log) if self.with_breit: if 'SSSS' in self._coulomb_now.upper(): vj1, vk1 = _call_veff_gaunt_breit(mol, dm, hermi, opt_gaunt, True) log.debug('Add Breit term') vj += vj1 vk += vk1 elif self.with_gaunt and 'SS' in self._coulomb_now.upper(): log.debug('Add Gaunt term') vj1, vk1 = _call_veff_gaunt_breit(mol, dm, hermi, opt_gaunt, False) vj += vj1 vk += vk1 log.timer('vj and vk', *t0) return vj, vk
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 casci(self, mo_coeff, ci0=None, eris=None, verbose=None, envs=None): log = logger.new_logger(self, verbose) log.debug('Running CASCI with solvent. Note the total energy ' 'has duplicated contributions from solvent.') # In oldCAS.casci function, dE was computed based on the total # energy without removing the duplicated solvent contributions. # However, envs['elast'] is the last total energy with correct # solvent effects. Hack envs['elast'] to make oldCAS.casci print # the correct energy difference. envs['elast'] = self._e_tot_without_solvent e_tot, e_cas, fcivec = oldCAS.casci(self, mo_coeff, ci0, eris, verbose, envs) self._e_tot_without_solvent = e_tot log.debug('Computing corrections to the total energy.') dm = self.make_rdm1(ci=fcivec, ao_repr=True) with_solvent = self.with_solvent if with_solvent.epcm is not None: edup = numpy.einsum('ij,ji->', with_solvent.vpcm, dm) ediel = with_solvent.epcm e_tot = e_tot - edup + ediel log.info('Removing duplication %.15g, ' 'adding E_diel = %.15g to total energy:\n' ' E(CASSCF+solvent) = %.15g', edup, ediel, e_tot) # Update solvent effects for next iteration if needed if not with_solvent.frozen: with_solvent.epcm, with_solvent.vpcm = with_solvent.kernel(dm) return e_tot, e_cas, fcivec
def rhf_internal(mf, verbose=None): log = logger.new_logger(mf, verbose) mol = mf.mol mo_coeff = mf.mo_coeff mo_energy = mf.mo_energy mo_occ = mf.mo_occ nmo = mo_coeff.shape[1] nocc = numpy.count_nonzero(mo_occ) nvir = nmo - nocc eri_mo = ao2mo.full(mol, mo_coeff) eri_mo = ao2mo.restore(1, eri_mo, nmo) eai = lib.direct_sum('a-i->ai', mo_energy[nocc:], mo_energy[:nocc]) # A h = numpy.einsum('ckld->kcld', eri_mo[nocc:,:nocc,:nocc,nocc:]) * 2 h-= numpy.einsum('cdlk->kcld', eri_mo[nocc:,nocc:,:nocc,:nocc]) for a in range(nvir): for i in range(nocc): h[i,a,i,a] += eai[a,i] # B h+= numpy.einsum('ckdl->kcld', eri_mo[nocc:,:nocc,nocc:,:nocc]) * 2 h-= numpy.einsum('cldk->kcld', eri_mo[nocc:,:nocc,nocc:,:nocc]) nov = nocc * nvir e = scipy.linalg.eigh(h.reshape(nov,nov))[0] log.debug('rhf_internal: lowest eigs = %s', e[e<=max(e[0],1e-5)]) if e[0] < -1e-5: log.log('RHF wavefunction has an internal instablity') else: log.log('RHF wavefunction is stable in the intenral stablity analysis')
def kernel(casci, mo_coeff=None, ci0=None, verbose=logger.NOTE): '''CASCI solver ''' if mo_coeff is None: mo_coeff = casci.mo_coeff log = logger.new_logger(casci, verbose) t0 = (time.clock(), time.time()) log.debug('Start CASCI') ncas = casci.ncas nelecas = casci.nelecas # 2e eri_cas = casci.get_h2eff(mo_coeff) t1 = log.timer('integral transformation to CAS space', *t0) # 1e h1eff, energy_core = casci.get_h1eff(mo_coeff) log.debug('core energy = %.15g', energy_core) t1 = log.timer('effective h1e in CAS space', *t1) if h1eff.shape[0] != ncas: raise RuntimeError('Active space size error. nmo=%d ncore=%d ncas=%d' % (mo_coeff.shape[1], casci.ncore, ncas)) # FCI max_memory = max(400, casci.max_memory-lib.current_memory()[0]) e_tot, fcivec = casci.fcisolver.kernel(h1eff, eri_cas, ncas, nelecas, ci0=ci0, verbose=log, max_memory=max_memory, ecore=energy_core) t1 = log.timer('FCI solver', *t1) e_cas = e_tot - energy_core return e_tot, e_cas, fcivec
def kernel(casci, mo_coeff=None, ci0=None, verbose=logger.NOTE): '''UHF-CASCI solver ''' if mo_coeff is None: mo_coeff = casci.mo_coeff log = logger.new_logger(casci, verbose) t0 = (time.clock(), time.time()) log.debug('Start uhf-based CASCI') ncas = casci.ncas nelecas = casci.nelecas ncore = casci.ncore mo_core, mo_cas, mo_vir = extract_orbs(mo_coeff, ncas, nelecas, ncore) # 1e h1eff, energy_core = casci.h1e_for_cas(mo_coeff) log.debug('core energy = %.15g', energy_core) t1 = log.timer('effective h1e in CAS space', *t0) # 2e eri_cas = casci.get_h2eff(mo_cas) t1 = log.timer('integral transformation to CAS space', *t1) # FCI max_memory = max(400, casci.max_memory-lib.current_memory()[0]) e_tot, fcivec = casci.fcisolver.kernel(h1eff, eri_cas, ncas, nelecas, ci0=ci0, verbose=log, max_memory=max_memory, ecore=energy_core) t1 = log.timer('FCI solver', *t1) e_cas = e_tot - energy_core return e_tot, e_cas, fcivec
def polarizability_with_freq(polobj, freq=None): from pyscf.prop.nmr import rhf as rhf_nmr log = logger.new_logger(polobj) mf = polobj._scf mol = mf.mol mo_energy = mf.mo_energy mo_coeff = mf.mo_coeff mo_occ = mf.mo_occ occidx = mo_occ > 0 orbo = mo_coeff[:, occidx] orbv = mo_coeff[:,~occidx] charges = mol.atom_charges() coords = mol.atom_coords() charge_center = numpy.einsum('i,ix->x', charges, coords) / charges.sum() with mol.with_common_orig(charge_center): int_r = mol.intor_symmetric('int1e_r', comp=3) h1 = lib.einsum('xpq,pi,qj->xij', int_r, orbv.conj(), orbo) mo1 = cphf_with_freq(mf, mo_energy, mo_occ, h1, freq, polobj.max_cycle_cphf, polobj.conv_tol, verbose=log)[0] e2 = numpy.einsum('xpi,ypi->xy', h1, mo1[0]) e2 += numpy.einsum('xpi,ypi->xy', h1, mo1[1]) # *-1 from the definition of dipole moment. *2 for double occupancy e2 *= -2 log.debug('Polarizability tensor with freq %s', freq) log.debug('%s', e2) return e2
def mulliken_pop(mol, dm, s=None, verbose=logger.DEBUG): '''Mulliken population analysis ''' if s is None: s = hf.get_ovlp(mol) log = logger.new_logger(mol, verbose) if isinstance(dm, numpy.ndarray) and dm.ndim == 2: dm = numpy.array((dm*.5, dm*.5)) pop_a = numpy.einsum('ij,ji->i', dm[0], s).real pop_b = numpy.einsum('ij,ji->i', dm[1], s).real log.info(' ** Mulliken pop alpha | beta **') for i, s in enumerate(mol.ao_labels()): log.info('pop of %s %10.5f | %-10.5f', s, pop_a[i], pop_b[i]) log.info('In total %10.5f | %-10.5f', sum(pop_a), sum(pop_b)) log.note(' ** Mulliken atomic charges ( Nelec_alpha | Nelec_beta ) **') nelec_a = numpy.zeros(mol.natm) nelec_b = numpy.zeros(mol.natm) for i, s in enumerate(mol.ao_labels(fmt=None)): nelec_a[s[0]] += pop_a[i] nelec_b[s[0]] += pop_b[i] chg = mol.atom_charges() - (nelec_a + nelec_b) for ia in range(mol.natm): symb = mol.atom_symbol(ia) log.note('charge of %d%s = %10.5f ( %10.5f %10.5f )', ia, symb, chg[ia], nelec_a[ia], nelec_b[ia]) return (pop_a,pop_b), chg
def make_fcdip(hfcobj, dm0, hfc_nuc=None, verbose=None): '''The contribution of Fermi-contact term and dipole-dipole interactions''' log = logger.new_logger(hfcobj, verbose) mol = hfcobj.mol if hfc_nuc is None: hfc_nuc = range(mol.natm) if isinstance(dm0, numpy.ndarray) and dm0.ndim == 2: # RHF DM return numpy.zeros((3,3)) dma, dmb = dm0 spindm = dma - dmb effspin = mol.spin * .5 e_gyro = .5 * nist.G_ELECTRON nuc_mag = .5 * (nist.E_MASS/nist.PROTON_MASS) # e*hbar/2m au2MHz = nist.HARTREE2J / nist.PLANCK * 1e-6 fac = nist.ALPHA**2 / 2 / effspin * e_gyro * au2MHz hfc = [] for i, atm_id in enumerate(hfc_nuc): nuc_gyro = get_nuc_g_factor(mol.atom_symbol(atm_id)) * nuc_mag h1 = _get_integrals_fcdip(mol, atm_id) fcsd = numpy.einsum('xyij,ji->xy', h1, spindm) h1fc = _get_integrals_fc(mol, atm_id) fc = numpy.einsum('ij,ji', h1fc, spindm) sd = fcsd + numpy.eye(3) * fc log.info('FC of atom %d %s (in MHz)', atm_id, fac * nuc_gyro * fc) if hfcobj.verbose >= logger.INFO: _write(hfcobj, align(fac*nuc_gyro*sd)[0], 'SD of atom %d (in MHz)' % atm_id) hfc.append(fac * nuc_gyro * fcsd) return numpy.asarray(hfc)
def kernel(self, mo_coeff=None, ci=None, atmlst=None, mf_grad=None, state=None, verbose=None): cput0 = (time.clock(), time.time()) log = logger.new_logger(self, verbose) if ci is None: ci = self.base.ci if isinstance(ci, (list, tuple)): if state is None: state = self.state else: self.state = state ci = ci[state] logger.info(self, 'Multiple roots are found in CASCI solver. ' 'Nuclear gradients of root %d are computed.', state) if atmlst is None: atmlst = self.atmlst else: self.atmlst = atmlst if self.verbose >= logger.WARN: self.check_sanity() if self.verbose >= logger.INFO: self.dump_flags() self.de = kernel(self.base, mo_coeff, ci, atmlst, mf_grad, log) log.timer('CASCI gradients', *cput0) self._finalize() return self.de
def analyze(mf, verbose=logger.DEBUG, **kwargs): from pyscf.tools import dump_mat log = logger.new_logger(mf, verbose) mo_energy = mf.mo_energy mo_occ = mf.mo_occ mo_coeff = mf.mo_coeff log.info('**** MO energy ****') for i in range(len(mo_energy)): if mo_occ[i] > 0: log.info('occupied MO #%d energy= %.15g occ= %g', \ i+1, mo_energy[i], mo_occ[i]) else: log.info('virtual MO #%d energy= %.15g occ= %g', \ i+1, mo_energy[i], mo_occ[i]) mol = mf.mol if mf.verbose >= logger.DEBUG1: log.debug(' ** MO coefficients of large component of postive state (real part) **') label = mol.spinor_labels() n2c = mo_coeff.shape[0] // 2 dump_mat.dump_rec(mf.stdout, mo_coeff[n2c:,:n2c].real, label, start=1) dm = mf.make_rdm1(mo_coeff, mo_occ) pop_chg = mf.mulliken_pop(mol, dm, mf.get_ovlp(), log) dip = mf.dip_moment(mol, dm, verbose=log) return pop_chg, dip
def kernel(self, *args, **kwargs): with_solvent = self.with_solvent # The underlying ._scf object is decorated with solvent effects. # The resultant Fock matrix and orbital energies both include the # effects from solvent. It means that solvent effects for post-HF # methods are automatically counted if solvent is enabled at scf # level. if with_solvent.frozen: return old_method.kernel(self, *args, **kwargs) log = logger.new_logger(self) log.info('\n** Self-consistently update the solvent effects for %s **', old_method) ##TODO: Suppress a few output messages #log1 = copy.copy(log) #log1.note, log1.info = log1.info, log1.debug e_last = 0 for cycle in range(self.with_solvent.max_cycle): log.info('\n** Solvent self-consistent cycle %d:', cycle) # Solvent effects are applied when accessing the # underlying ._scf objects. The flag frozen=True ensures that # the generated potential with_solvent.vpcm is passed to the # the post-HF object, without being updated in the implicit # call to the _scf iterations. with lib.temporary_env(with_solvent, frozen=True): e_tot = basic_scanner(self.mol) dm = basic_scanner.make_rdm1(ao_repr=True) if with_solvent.epcm is not None: edup = numpy.einsum('ij,ji->', with_solvent.vpcm, dm) e_tot = e_tot - edup + with_solvent.epcm log.debug(' E_diel = %.15g', with_solvent.epcm) # To generate the solvent potential for ._scf object. Since # frozen is set when calling basic_scanner, the solvent # effects are frozen during the scf iterations. with_solvent.epcm, with_solvent.vpcm = with_solvent.kernel(dm) de = e_tot - e_last log.info('Sovlent cycle %d E_tot = %.15g dE = %g', cycle, e_tot, de) if abs(e_tot-e_last).max() < with_solvent.conv_tol: break e_last = e_tot # An extra cycle to compute the total energy log.info('\n** Extra cycle for solvent effects') res = old_method.kernel(self) with lib.temporary_env(with_solvent, frozen=True): #Update everything except the _scf object and _keys basic_scanner(self.mol) new_keys = self._keys self.__dict__.update(basic_scanner.__dict__) self._keys = new_keys self._scf = scf_with_solvent self._finalize() return res
def hess_elec(hessobj, mo_energy=None, mo_coeff=None, mo_occ=None, mo1=None, mo_e1=None, h1ao=None, atmlst=None, max_memory=4000, verbose=None): log = logger.new_logger(hessobj, verbose) time0 = t1 = (time.clock(), time.time()) mol = hessobj.mol mf = hessobj.base if mo_energy is None: mo_energy = mf.mo_energy if mo_occ is None: mo_occ = mf.mo_occ if mo_coeff is None: mo_coeff = mf.mo_coeff if atmlst is None: atmlst = range(mol.natm) de2 = hessobj.partial_hess_elec(mo_energy, mo_coeff, mo_occ, atmlst, max_memory, log) if h1ao is None: h1ao = hessobj.make_h1(mo_coeff, mo_occ, hessobj.chkfile, atmlst, log) t1 = log.timer_debug1('making H1', *time0) if mo1 is None or mo_e1 is None: mo1, mo_e1 = hessobj.solve_mo1(mo_energy, mo_coeff, mo_occ, h1ao, None, atmlst, max_memory, log) t1 = log.timer_debug1('solving MO1', *t1) if isinstance(h1ao, str): h1ao = lib.chkfile.load(h1ao, 'scf_f1ao') h1ao = dict([(int(k), h1ao[k]) for k in h1ao]) if isinstance(mo1, str): mo1 = lib.chkfile.load(mo1, 'scf_mo1') mo1 = dict([(int(k), mo1[k]) for k in mo1]) nao, nmo = mo_coeff.shape mocc = mo_coeff[:,mo_occ>0] s1a = -mol.intor('int1e_ipovlp', comp=3) aoslices = mol.aoslice_by_atom() for i0, ia in enumerate(atmlst): shl0, shl1, p0, p1 = aoslices[ia] s1ao = numpy.zeros((3,nao,nao)) s1ao[:,p0:p1] += s1a[:,p0:p1] s1ao[:,:,p0:p1] += s1a[:,p0:p1].transpose(0,2,1) s1oo = numpy.einsum('xpq,pi,qj->xij', s1ao, mocc, mocc) for j0 in range(i0+1): ja = atmlst[j0] q0, q1 = aoslices[ja][2:] # *2 for double occupancy, *2 for +c.c. dm1 = numpy.einsum('ypi,qi->ypq', mo1[ja], mocc) de2[i0,j0] += numpy.einsum('xpq,ypq->xy', h1ao[ia], dm1) * 4 dm1 = numpy.einsum('ypi,qi,i->ypq', mo1[ja], mocc, mo_energy[mo_occ>0]) de2[i0,j0] -= numpy.einsum('xpq,ypq->xy', s1ao, dm1) * 4 de2[i0,j0] -= numpy.einsum('xpq,ypq->xy', s1oo, mo_e1[ja]) * 2 for j0 in range(i0): de2[j0,i0] = de2[i0,j0].T log.timer('RHF hessian', *time0) return de2
def dump_flags(self, verbose=None): log = logger.new_logger(self, verbose) log.info('') log.info('******** SHCI flags ********') log.info('executable = %s', self.executable) log.info('mpiprefix = %s', self.mpiprefix) log.info('runtimedir = %s', self.runtimedir) log.debug1('config = %s', self.config) log.info('') return self
def solve_withs1(fvind, mo_energy, mo_occ, h1, s1, max_cycle=20, tol=1e-9, hermi=False, verbose=logger.WARN): '''For field dependent basis. First order overlap matrix is non-zero. The first order orbitals are set to C^1_{ij} = -1/2 S1 e1 = h1 - s1*e0 + (e0_j-e0_i)*c1 + vhf[c1] Kwargs: hermi : boolean Whether the matrix defined by fvind is Hermitian or not. Returns: First order orbital coefficients (in MO basis) and first order orbital energy matrix ''' log = logger.new_logger(verbose=verbose) t0 = (time.clock(), time.time()) occidx = mo_occ > 0 viridx = mo_occ == 0 e_a = mo_energy[viridx] e_i = mo_energy[occidx] e_ai = 1 / lib.direct_sum('a-i->ai', e_a, e_i) nvir, nocc = e_ai.shape nmo = nocc + nvir s1 = s1.reshape(-1,nmo,nocc) hs = mo1base = h1.reshape(-1,nmo,nocc) - s1*e_i mo_e1 = hs[:,occidx,:].copy() mo1base[:,viridx] *= -e_ai mo1base[:,occidx] = -s1[:,occidx] * .5 def vind_vo(mo1): v = fvind(mo1.reshape(h1.shape)).reshape(-1,nmo,nocc) v[:,viridx,:] *= e_ai v[:,occidx,:] = 0 return v.ravel() mo1 = lib.krylov(vind_vo, mo1base.ravel(), tol=tol, max_cycle=max_cycle, hermi=hermi, verbose=log) mo1 = mo1.reshape(mo1base.shape) log.timer('krylov solver in CPHF', *t0) v1mo = fvind(mo1.reshape(h1.shape)).reshape(-1,nmo,nocc) mo1[:,viridx] = mo1base[:,viridx] - v1mo[:,viridx]*e_ai # mo_e1 has the same symmetry as the first order Fock matrix (hermitian or # anti-hermitian). mo_e1 = v1mo - s1*lib.direct_sum('i+j->ij',e_i,e_i) mo_e1 += mo1[:,occidx] * lib.direct_sum('i-j->ij', e_i, e_i) mo_e1 += v1mo[:,occidx,:] if h1.ndim == 3: return mo1, mo_e1 else: return mo1.reshape(h1.shape), mo_e1.reshape(nocc,nocc)
def _make_eris(mp, mo_coeff=None, ao2mofn=None, verbose=None): log = logger.new_logger(mp, verbose) time0 = (time.clock(), time.time()) eris = _ChemistsERIs(mp, mo_coeff) nocca, noccb = mp.get_nocc() nmoa, nmob = mp.get_nmo() nvira, nvirb = nmoa-nocca, nmob-noccb nao = eris.mo_coeff[0].shape[0] nmo_pair = nmoa * (nmoa+1) // 2 nao_pair = nao * (nao+1) // 2 mem_incore = (nao_pair**2 + nmo_pair**2) * 8/1e6 mem_now = lib.current_memory()[0] max_memory = max(0, mp.max_memory-mem_now) moa = eris.mo_coeff[0] mob = eris.mo_coeff[1] orboa = moa[:,:nocca] orbob = mob[:,:noccb] orbva = moa[:,nocca:] orbvb = mob[:,noccb:] if (mp.mol.incore_anyway or (mp._scf._eri is not None and mem_incore+mem_now < mp.max_memory)): log.debug('transform (ia|jb) incore') if callable(ao2mofn): eris.ovov = ao2mofn((orboa,orbva,orboa,orbva)).reshape(nocca*nvira,nocca*nvira) eris.ovOV = ao2mofn((orboa,orbva,orbob,orbvb)).reshape(nocca*nvira,noccb*nvirb) eris.OVOV = ao2mofn((orbob,orbvb,orbob,orbvb)).reshape(noccb*nvirb,noccb*nvirb) else: eris.ovov = ao2mo.general(mp._scf._eri, (orboa,orbva,orboa,orbva)) eris.ovOV = ao2mo.general(mp._scf._eri, (orboa,orbva,orbob,orbvb)) eris.OVOV = ao2mo.general(mp._scf._eri, (orbob,orbvb,orbob,orbvb)) elif getattr(mp._scf, 'with_df', None): logger.warn(mp, 'UMP2 detected DF being used in the HF object. ' 'MO integrals are computed based on the DF 3-index tensors.\n' 'It\'s recommended to use DF-UMP2 module.') log.debug('transform (ia|jb) with_df') eris.ovov = mp._scf.with_df.ao2mo((orboa,orbva,orboa,orbva)) eris.ovOV = mp._scf.with_df.ao2mo((orboa,orbva,orbob,orbvb)) eris.OVOV = mp._scf.with_df.ao2mo((orbob,orbvb,orbob,orbvb)) else: log.debug('transform (ia|jb) outcore') eris.feri = lib.H5TmpFile() _ao2mo_ovov(mp, (orboa,orbva,orbob,orbvb), eris.feri, max(2000, max_memory), log) eris.ovov = eris.feri['ovov'] eris.ovOV = eris.feri['ovOV'] eris.OVOV = eris.feri['OVOV'] time1 = log.timer('Integral transformation', *time0) return eris
def hyper_polarizability(polobj, with_cphf=True): from pyscf.prop.nmr import uhf as uhf_nmr log = logger.new_logger(polobj) mf = polobj._scf mol = mf.mol mo_energy = mf.mo_energy mo_coeff = mf.mo_coeff mo_occ = mf.mo_occ occidxa = mo_occ[0] > 0 occidxb = mo_occ[1] > 0 mo0a, mo0b = mo_coeff orboa = mo0a[:, occidxa] orbva = mo0a[:,~occidxa] orbob = mo0b[:, occidxb] orbvb = mo0b[:,~occidxb] charges = mol.atom_charges() coords = mol.atom_coords() charge_center = numpy.einsum('i,ix->x', charges, coords) / charges.sum() with mol.with_common_orig(charge_center): int_r = mol.intor_symmetric('int1e_r', comp=3) h1a = lib.einsum('xpq,pi,qj->xij', int_r, mo0a.conj(), orboa) h1b = lib.einsum('xpq,pi,qj->xij', int_r, mo0b.conj(), orbob) s1a = numpy.zeros_like(h1a) s1b = numpy.zeros_like(h1b) vind = polobj.gen_vind(mf, mo_coeff, mo_occ) if with_cphf: mo1, e1 = ucphf.solve(vind, mo_energy, mo_occ, (h1a,h1b), (s1a,s1b), polobj.max_cycle_cphf, polobj.conv_tol, verbose=log) else: mo1, e1 = uhf_nmr._solve_mo1_uncoupled(mo_energy, mo_occ, (h1a,h1b), (s1a,s1b)) mo1a = lib.einsum('xqi,pq->xpi', mo1[0], mo0a) mo1b = lib.einsum('xqi,pq->xpi', mo1[1], mo0b) dm1a = lib.einsum('xpi,qi->xpq', mo1a, orboa) dm1b = lib.einsum('xpi,qi->xpq', mo1b, orbob) dm1a = dm1a + dm1a.transpose(0,2,1) dm1b = dm1b + dm1b.transpose(0,2,1) vresp = _gen_uhf_response(mf, hermi=1) h1ao = int_r + vresp(numpy.stack((dm1a, dm1b))) s0 = mf.get_ovlp() e3 = lib.einsum('xpq,ypi,zqi->xyz', h1ao[0], mo1a, mo1a) e3 += lib.einsum('xpq,ypi,zqi->xyz', h1ao[1], mo1b, mo1b) e3 -= lib.einsum('pq,xpi,yqj,zij->xyz', s0, mo1a, mo1a, e1[0]) e3 -= lib.einsum('pq,xpi,yqj,zij->xyz', s0, mo1b, mo1b, e1[1]) e3 = (e3 + e3.transpose(1,2,0) + e3.transpose(2,0,1) + e3.transpose(0,2,1) + e3.transpose(1,0,2) + e3.transpose(2,1,0)) e3 = -e3 log.debug('Static hyper polarizability tensor\n%s', e3) return e3
def make_h10(mol, dm0, gauge_orig=None, verbose=logger.WARN): log = logger.new_logger(mol, verbose=verbose) if gauge_orig is None: # A10_i dot p + p dot A10_i consistents with <p^2 g> # A10_j dot p + p dot A10_j consistents with <g p^2> # A10_j dot p + p dot A10_j => i/2 (rjxp - pxrj) = irjxp log.debug('First-order GIAO Fock matrix') h1 = -.5 * mol.intor('int1e_giao_irjxp', 3) + make_h10giao(mol, dm0) else: with mol.with_common_origin(gauge_orig): h1 = -.5 * mol.intor('int1e_cg_irxp', 3) h1 = (h1, h1) return h1
def kernel(self, t2=None, atmlst=None, mf_grad=None, verbose=None, _kern=kernel): log = logger.new_logger(self, verbose) if t2 is None: t2 = self.base.t2 if t2 is None: t2 = self.base.kernel() if atmlst is None: atmlst = self.atmlst else: self.atmlst = atmlst self.de = _kern(self.base, t2, atmlst, mf_grad, verbose=log) self._finalize() return self.de
def guess_wfnsym(self, norb, nelec, fcivec=None, orbsym=None, wfnsym=None, **kwargs): if orbsym is None: orbsym = self.orbsym if fcivec is None: wfnsym = direct_spin1_symm._id_wfnsym(self, norb, nelec, orbsym, wfnsym) else: strsa, strsb = getattr(fcivec, '_strs', self._strs) wfnsym = addons._guess_wfnsym(fcivec, strsa, strsb, orbsym) verbose = kwargs.get('verbose', None) log = logger.new_logger(self, verbose) log.debug('Guessing CI wfn symmetry = %s', wfnsym) return wfnsym
def _partial_hess_ejk(hessobj, mo_energy=None, mo_coeff=None, mo_occ=None, atmlst=None, max_memory=4000, verbose=None, with_k=True): '''Partial derivative ''' log = logger.new_logger(hessobj, verbose) time0 = t1 = (time.clock(), time.time()) mol = hessobj.mol mf = hessobj.base if mo_energy is None: mo_energy = mf.mo_energy if mo_occ is None: mo_occ = mf.mo_occ if mo_coeff is None: mo_coeff = mf.mo_coeff if atmlst is None: atmlst = range(mol.natm) nao, nmo = mo_coeff.shape mocc = mo_coeff[:,mo_occ>0] mocc_2 = np.einsum('pi,i->pi', mocc, mo_occ[mo_occ>0]**.5) nocc = mocc.shape[1] dm0 = numpy.dot(mocc, mocc.T) * 2 # Energy weighted density matrix dme0 = numpy.einsum('pi,qi,i->pq', mocc, mocc, mo_energy[mo_occ>0]) * 2 auxmol = hessobj.base.with_df.auxmol naux = auxmol.nao nbas = mol.nbas auxslices = auxmol.aoslice_by_atom() aoslices = mol.aoslice_by_atom() aux_loc = auxmol.ao_loc blksize = min(480, hessobj.max_memory*.3e6/8/nao**2) aux_ranges = ao2mo.outcore.balance_partition(auxmol.ao_loc, blksize) hcore_deriv = hessobj.hcore_generator(mol) s1aa, s1ab, s1a = rhf_hess.get_ovlp(mol) ftmp = lib.H5TmpFile() get_int3c = _int3c_wrapper(mol, auxmol, 'int3c2e', 's1') # Without RI basis response # (20|0)(0|00) # (11|0)(0|00) # (10|0)(0|10) int2c = auxmol.intor('int2c2e', aosym='s1') int2c_low = scipy.linalg.cho_factor(int2c, lower=True) int2c_ip1 = auxmol.intor('int2c2e_ip1', aosym='s1') rhoj0_P = 0 if with_k: if hessobj.max_memory*.8e6/8 < naux*nocc*(nocc+nao): raise RuntimeError('Memory not enough. You need to increase mol.max_memory') rhok0_Pl_ = np.empty((naux,nao,nocc)) for i, (shl0, shl1, p0, p1) in enumerate(aoslices): int3c = get_int3c((shl0, shl1, 0, nbas, 0, auxmol.nbas)) rhoj0_P += np.einsum('klp,kl->p', int3c, dm0[p0:p1]) if with_k: tmp = lib.einsum('ijp,jk->pik', int3c, mocc_2) tmp = scipy.linalg.cho_solve(int2c_low, tmp.reshape(naux,-1), overwrite_b=True) rhok0_Pl_[:,p0:p1] = tmp.reshape(naux,p1-p0,nocc) int3c = tmp = None rhoj0_P = scipy.linalg.cho_solve(int2c_low, rhoj0_P) get_int3c_ipip1 = _int3c_wrapper(mol, auxmol, 'int3c2e_ipip1', 's1') vj1_diag = 0 vk1_diag = 0 for shl0, shl1, nL in aux_ranges: shls_slice = (0, nbas, 0, nbas, shl0, shl1) p0, p1 = aux_loc[shl0], aux_loc[shl1] int3c_ipip1 = get_int3c_ipip1(shls_slice) vj1_diag += np.einsum('xijp,p->xij', int3c_ipip1, rhoj0_P[p0:p1]).reshape(3,3,nao,nao) if with_k: tmp = lib.einsum('Plj,Jj->PlJ', rhok0_Pl_[p0:p1], mocc_2) vk1_diag += lib.einsum('xijp,plj->xil', int3c_ipip1, tmp).reshape(3,3,nao,nao) int3c_ipip1 = get_int3c_ipip1 = tmp = None t1 = log.timer_debug1('contracting int2e_ipip1', *t1) get_int3c_ip1 = _int3c_wrapper(mol, auxmol, 'int3c2e_ip1', 's1') rho_ip1 = ftmp.create_dataset('rho_ip1', (nao,nao,naux,3), 'f8') rhok_ip1_IkP = ftmp.create_group('rhok_ip1_IkP') rhok_ip1_PkI = ftmp.create_group('rhok_ip1_PkI') rhoj1 = np.empty((mol.natm,naux,3)) wj1 = np.empty((mol.natm,naux,3)) for i0, ia in enumerate(atmlst): shl0, shl1, p0, p1 = aoslices[ia] shls_slice = (shl0, shl1, 0, nbas, 0, auxmol.nbas) int3c_ip1 = get_int3c_ip1(shls_slice) tmp_ip1 = scipy.linalg.cho_solve(int2c_low, int3c_ip1.reshape(-1,naux).T, overwrite_b=True).reshape(naux,3,p1-p0,nao) rhoj1[i0] = np.einsum('pxij,ji->px', tmp_ip1, dm0[:,p0:p1]) wj1[i0] = np.einsum('xijp,ji->px', int3c_ip1, dm0[:,p0:p1]) rho_ip1[p0:p1] = tmp_ip1.transpose(2,3,0,1) if with_k: tmp = lib.einsum('pykl,li->ikpy', tmp_ip1, dm0) rhok_ip1_IkP['%.4d'%ia] = tmp rhok_ip1_PkI['%.4d'%ia] = tmp.transpose(2,1,0,3) tmp = None ej = lib.einsum('ipx,jpy->ijxy', rhoj1, wj1) * 4 ek = np.zeros_like(ej) e1 = np.zeros_like(ej) rhoj1 = wj1 = None if with_k: vk2buf = 0 for shl0, shl1, nL in aux_ranges: shls_slice = (0, nbas, 0, nbas, shl0, shl1) p0, p1 = aux_loc[shl0], aux_loc[shl1] int3c_ip1 = get_int3c_ip1(shls_slice) vk2buf += lib.einsum('xijp,pkjy->xyki', int3c_ip1, _load_dim0(rhok_ip1_PkI, p0, p1)) int3c_ip1 = None get_int3c_ip2 = _int3c_wrapper(mol, auxmol, 'int3c2e_ip2', 's1') wj_ip2 = np.empty((naux,3)) wk_ip2_Ipk = ftmp.create_dataset('wk_ip2', (nao,naux,3,nao), 'f8') if hessobj.auxbasis_response > 1: wk_ip2_P__ = np.empty((naux,3,nocc,nocc)) for shl0, shl1, nL in aux_ranges: shls_slice = (0, nbas, 0, nbas, shl0, shl1) p0, p1 = aux_loc[shl0], aux_loc[shl1] int3c_ip2 = get_int3c_ip2(shls_slice) wj_ip2[p0:p1] = np.einsum('yklp,lk->py', int3c_ip2, dm0) if with_k: wk_ip2_Ipk[:,p0:p1] = lib.einsum('yklp,il->ipyk', int3c_ip2, dm0) if hessobj.auxbasis_response > 1: wk_ip2_P__[p0:p1] = lib.einsum('xuvp,ui,vj->pxij', int3c_ip2, mocc_2, mocc_2) int3c_ip2 = None if hessobj.auxbasis_response > 1: get_int3c_ipip2 = _int3c_wrapper(mol, auxmol, 'int3c2e_ipip2', 's1') rhok0_P__ = lib.einsum('plj,li->pij', rhok0_Pl_, mocc_2) rho2c_0 = lib.einsum('pij,qji->pq', rhok0_P__, rhok0_P__) int2c_inv = np.linalg.inv(int2c) int2c_ipip1 = auxmol.intor('int2c2e_ipip1', aosym='s1') int2c_ip_ip = lib.einsum('xpq,qr,ysr->xyps', int2c_ip1, int2c_inv, int2c_ip1) int2c_ip_ip -= auxmol.intor('int2c2e_ip1ip2', aosym='s1').reshape(3,3,naux,naux) int2c = int2c_low = None get_int3c_ipvip1 = _int3c_wrapper(mol, auxmol, 'int3c2e_ipvip1', 's1') get_int3c_ip1ip2 = _int3c_wrapper(mol, auxmol, 'int3c2e_ip1ip2', 's1') for i0, ia in enumerate(atmlst): shl0, shl1, p0, p1 = aoslices[ia] shls_slice = (shl0, shl1, 0, nbas, 0, auxmol.nbas) # (10|0)(0|10) without response of RI basis if with_k: int3c_ip1 = get_int3c_ip1(shls_slice) vk1 = lib.einsum('xijp,ikpy->xykj', int3c_ip1, _load_dim0(rhok_ip1_IkP, p0, p1)) vk1[:,:,:,p0:p1] += vk2buf[:,:,:,p0:p1] t1 = log.timer_debug1('contracting int2e_ip1ip2 for atom %d'%ia, *t1) int3c_ip1 = None # (11|0)(0|00) without response of RI basis int3c_ipvip1 = get_int3c_ipvip1(shls_slice) vj1 = np.einsum('xijp,p->xji', int3c_ipvip1, rhoj0_P).reshape(3,3,nao,p1-p0) if with_k: tmp = lib.einsum('pki,ji->pkj', rhok0_Pl_, mocc_2[p0:p1]) vk1 += lib.einsum('xijp,pki->xjk', int3c_ipvip1, tmp).reshape(3,3,nao,nao) t1 = log.timer_debug1('contracting int2e_ipvip1 for atom %d'%ia, *t1) int3c_ipvip1 = tmp = None e1[i0,i0] -= numpy.einsum('xypq,pq->xy', s1aa[:,:,p0:p1], dme0[p0:p1])*2 ej[i0,i0] += numpy.einsum('xypq,pq->xy', vj1_diag[:,:,p0:p1], dm0[p0:p1])*2 if with_k: ek[i0,i0] += numpy.einsum('xypq,pq->xy', vk1_diag[:,:,p0:p1], dm0[p0:p1]) for j0, ja in enumerate(atmlst[:i0+1]): q0, q1 = aoslices[ja][2:] ej[i0,j0] += numpy.einsum('xypq,pq->xy', vj1[:,:,q0:q1], dm0[q0:q1,p0:p1])*2 e1[i0,j0] -= numpy.einsum('xypq,pq->xy', s1ab[:,:,p0:p1,q0:q1], dme0[p0:p1,q0:q1])*2 if with_k: ek[i0,j0] += numpy.einsum('xypq,pq->xy', vk1[:,:,q0:q1], dm0[q0:q1]) h1ao = hcore_deriv(ia, ja) e1[i0,j0] += numpy.einsum('xypq,pq->xy', h1ao, dm0) # # The first order RI basis response # (10|1)(0|00) # (10|0)(1|0)(0|00) # (10|0)(0|1)(0|00) # (10|0)(1|00) # if hessobj.auxbasis_response: wk1_Pij = rho_ip1[p0:p1].transpose(2,3,0,1) rhoj1_P = np.einsum('pxij,ji->px', wk1_Pij, dm0[:,p0:p1]) # (10|1)(0|0)(0|00) int3c_ip1ip2 = get_int3c_ip1ip2(shls_slice) wj11_p = np.einsum('xijp,ji->xp', int3c_ip1ip2, dm0[:,p0:p1]) # (10|0)(1|0)(0|00) wj0_01 = np.einsum('ypq,q->yp', int2c_ip1, rhoj0_P) if with_k: rhok0_P_I = lib.einsum('plj,il->pji', rhok0_Pl_, dm0[p0:p1]) rhok0_PJI = lib.einsum('pji,Jj->pJi', rhok0_P_I, mocc_2) wk1_pJI = lib.einsum('ypq,qji->ypji', int2c_ip1, rhok0_PJI) wk1_IpJ = lib.einsum('ipyk,kj->ipyj', wk_ip2_Ipk[p0:p1], dm0) #rho2c_PQ = lib.einsum('qij,uj,iupx->xqp', rhok0_Pl_, mocc_2[p0:p1], rhok_ip1_IkP['%.4d'%ia]) rho2c_PQ = lib.einsum('pxij,qji->xqp', wk1_Pij, rhok0_PJI) for j0, (q0, q1) in enumerate(auxslices[:,2:]): # (10|1)(0|00) _ej = np.einsum('xp,p->x', wj11_p[:,q0:q1], rhoj0_P[q0:q1]).reshape(3,3) # (10|0)(0|1)(0|00) _ej -= lib.einsum('yqp,q,px->xy', int2c_ip1[:,q0:q1], rhoj0_P[q0:q1], rhoj1_P) # (10|0)(1|0)(0|00) _ej -= lib.einsum('px,yp->xy', rhoj1_P[q0:q1], wj0_01[:,q0:q1]) # (10|0)(1|00) _ej += lib.einsum('px,py->xy', rhoj1_P[q0:q1], wj_ip2[q0:q1]) if hessobj.auxbasis_response > 1: ej[i0,j0] += _ej * 2 ej[j0,i0] += _ej.T * 2 else: ej[i0,j0] += _ej ej[j0,i0] += _ej.T if with_k: _ek = lib.einsum('xijp,pji->x', int3c_ip1ip2[:,:,:,q0:q1], rhok0_PJI[q0:q1]).reshape(3,3) _ek -= lib.einsum('pxij,ypji->xy', wk1_Pij[q0:q1], wk1_pJI[:,q0:q1]) _ek -= lib.einsum('xqp,yqp->xy', rho2c_PQ[:,q0:q1], int2c_ip1[:,q0:q1]) _ek += lib.einsum('pxij,ipyj->xy', wk1_Pij[q0:q1], wk1_IpJ[:,q0:q1]) if hessobj.auxbasis_response > 1: ek[i0,j0] += _ek ek[j0,i0] += _ek.T else: ek[i0,j0] += _ek * .5 ek[j0,i0] += _ek.T * .5 int3c_ip1ip2 = rhok0_P_I = rhok0_PJI = wk1_pJI = wk1_IpJ = rho2c_PQ = None # # The second order RI basis response # if hessobj.auxbasis_response > 1: # (00|2)(0|00) # (00|0)(2|0)(0|00) shl0, shl1, p0, p1 = auxslices[ia] shls_slice = (0, nbas, 0, nbas, shl0, shl1) int3c_ipip2 = get_int3c_ipip2(shls_slice) ej[i0,i0] += np.einsum('xijp,ji,p->x', int3c_ipip2, dm0, rhoj0_P[p0:p1]).reshape(3,3) ej[i0,i0] -= np.einsum('p,xpq,q->x', rhoj0_P[p0:p1], int2c_ipip1[:,p0:p1], rhoj0_P).reshape(3,3) if with_k: rhok0_PJI = lib.einsum('Pij,Jj,Ii->PJI', rhok0_P__[p0:p1], mocc_2, mocc_2) ek[i0,i0] += .5 * np.einsum('xijp,pij->x', int3c_ipip2, rhok0_PJI).reshape(3,3) ek[i0,i0] -= .5 * np.einsum('pq,xpq->x', rho2c_0[p0:p1], int2c_ipip1[:,p0:p1]).reshape(3,3) rhok0_PJI = None # (00|0)(1|1)(0|00) # (00|1)(1|0)(0|00) # (00|1)(0|1)(0|00) # (00|1)(1|00) rhoj1 = lib.einsum('px,pq->xq', wj_ip2[p0:p1], int2c_inv[p0:p1]) # (00|0)(0|1)(1|0)(0|00) rhoj0_01 = lib.einsum('xp,pq->xq', wj0_01[:,p0:p1], int2c_inv[p0:p1]) # (00|0)(1|0)(1|0)(0|00) ip1_2c_2c = lib.einsum('xpq,qr->xpr', int2c_ip1[:,p0:p1], int2c_inv) rhoj0_10 = lib.einsum('p,xpq->xq', rhoj0_P[p0:p1], ip1_2c_2c) if with_k: # (00|0)(0|1)(1|0)(0|00) ip1_rho2c = .5 * lib.einsum('xpq,qr->xpr', int2c_ip1[:,p0:p1], rho2c_0) rho2c_1 = lib.einsum('xrq,rp->xpq', ip1_rho2c, int2c_inv[p0:p1]) # (00|0)(1|0)(1|0)(0|00) rho2c_1 += lib.einsum('xrp,rq->xpq', ip1_2c_2c, rho2c_0[p0:p1]) # (00|1)(0|1)(0|00) # (00|1)(1|0)(0|00) int3c_ip2 = get_int3c_ip2(shls_slice) tmp = lib.einsum('xuvr,vj,ui->xrij', int3c_ip2, mocc_2, mocc_2) tmp = lib.einsum('xrij,qij,rp->xpq', tmp, rhok0_P__, int2c_inv[p0:p1]) rho2c_1 -= tmp rho2c_1 -= tmp.transpose(0,2,1) int3c_ip2 = tmp = None for j0, (q0, q1) in enumerate(auxslices[:,2:]): _ej = 0 # (00|0)(1|1)(0|00) # (00|0)(1|0)(0|1)(0|00) _ej += .5 * np.einsum('p,xypq,q->xy', rhoj0_P[p0:p1], int2c_ip_ip[:,:,p0:p1,q0:q1], rhoj0_P[q0:q1]) # (00|1)(1|0)(0|00) _ej -= lib.einsum('xp,yp->xy', rhoj1[:,q0:q1], wj0_01[:,q0:q1]) # (00|1)(1|00) _ej += .5 * lib.einsum('xp,py->xy', rhoj1[:,q0:q1], wj_ip2[q0:q1]) # (00|0)(0|1)(1|0)(0|00) _ej += .5 * np.einsum('xp,yp->xy', rhoj0_01[:,q0:q1], wj0_01[:,q0:q1]) # (00|1)(0|1)(0|00) _ej -= lib.einsum('yqp,q,xp->xy', int2c_ip1[:,q0:q1], rhoj0_P[q0:q1], rhoj1) # (00|0)(1|0)(1|0)(0|00) _ej += np.einsum('xp,yp->xy', rhoj0_10[:,q0:q1], wj0_01[:,q0:q1]) ej[i0,j0] += _ej ej[j0,i0] += _ej.T if with_k: # (00|0)(1|1)(0|00) # (00|0)(1|0)(0|1)(0|00) _ek = .5 * np.einsum('pq,xypq->xy', rho2c_0[p0:p1,q0:q1], int2c_ip_ip[:,:,p0:p1,q0:q1]) # (00|1)(0|1)(0|00) # (00|1)(1|0)(0|00) # (00|0)(0|1)(1|0)(0|00) # (00|0)(1|0)(1|0)(0|00) _ek += np.einsum('xpq,ypq->xy', rho2c_1[:,q0:q1], int2c_ip1[:,q0:q1]) # (00|1)(1|00) _ek += .5 * lib.einsum('pxij,pq,qyij->xy', wk_ip2_P__[p0:p1], int2c_inv[p0:p1,q0:q1], wk_ip2_P__[q0:q1]) ek[i0,j0] += _ek * .5 ek[j0,i0] += _ek.T * .5 for i0, ia in enumerate(atmlst): for j0 in range(i0): e1[j0,i0] = e1[i0,j0].T ej[j0,i0] = ej[i0,j0].T ek[j0,i0] = ek[i0,j0].T log.timer('RHF partial hessian', *time0) return e1, ej, ek
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] # 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) 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 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 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._scf.energy_nuc() ecore += numpy.einsum('ij,ji', hcore, dm_core) h1eff = reduce(numpy.dot, (mocas.T, hcore, mocas)) if eris is not None and hasattr(eris, 'ppaa'): 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 hasattr(mo_coeff1, 'orbsym') and sort: 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() 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(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 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 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.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(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 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.') log.debug2(' ** CASCI/CASSCF orbital coefficients (expansion on meta-Lowdin AOs) **') c = reduce(numpy.dot, (orth_coeff.T, ovlp_ao, mo_coeff)) dump_mat.dump_rec(log.stdout, c, 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
def _contract_vvvv_t2(mycc, mol, vvL, t2, out=None, verbose=None): '''Ht2 = numpy.einsum('ijcd,acdb->ijab', t2, vvvv) Args: vvvv : None or integral object if vvvv is None, contract t2 to AO-integrals using AO-direct algorithm ''' _dgemm = lib.numpy_helper._dgemm time0 = time.clock(), time.time() log = logger.new_logger(mol, verbose) naux = vvL.shape[-1] nvira, nvirb = t2.shape[-2:] x2 = t2.reshape(-1, nvira, nvirb) nocc2 = x2.shape[0] nvir2 = nvira * nvirb Ht2 = numpy.ndarray(x2.shape, buffer=out) Ht2[:] = 0 max_memory = max(MEMORYMIN, mycc.max_memory - lib.current_memory()[0]) def contract_blk_(eri, i0, i1, j0, j1): ic = i1 - i0 jc = j1 - j0 #:Ht2[:,j0:j1] += numpy.einsum('xef,efab->xab', x2[:,i0:i1], eri) _dgemm('N', 'N', nocc2, jc * nvirb, ic * nvirb, x2.reshape(-1, nvir2), eri.reshape(-1, jc * nvirb), Ht2.reshape(-1, nvir2), 1, 1, i0 * nvirb, 0, j0 * nvirb) if i0 > j0: #:Ht2[:,i0:i1] += numpy.einsum('xef,abef->xab', x2[:,j0:j1], eri) _dgemm('N', 'T', nocc2, ic * nvirb, jc * nvirb, x2.reshape(-1, nvir2), eri.reshape(-1, jc * nvirb), Ht2.reshape(-1, nvir2), 1, 1, j0 * nvirb, 0, i0 * nvirb) #TODO: check if vvL can be entirely loaded into memory nvir_pair = nvirb * (nvirb + 1) // 2 dmax = numpy.sqrt(max_memory * .7e6 / 8 / nvirb**2 / 2) dmax = int(min((nvira + 3) // 4, max(ccsd.BLKMIN, dmax))) vvblk = (max_memory * 1e6 / 8 - dmax**2 * (nvirb**2 * 1.5 + naux)) / naux vvblk = int(min((nvira + 3) // 4, max(ccsd.BLKMIN, vvblk / naux))) eribuf = numpy.empty((dmax, dmax, nvir_pair)) loadbuf = numpy.empty((dmax, dmax, nvirb, nvirb)) tril2sq = lib.square_mat_in_trilu_indices(nvira) for i0, i1 in lib.prange(0, nvira, dmax): off0 = i0 * (i0 + 1) // 2 off1 = i1 * (i1 + 1) // 2 vvL0 = _cp(vvL[off0:off1]) for j0, j1 in lib.prange(0, i1, dmax): ijL = vvL0[tril2sq[i0:i1, j0:j1] - off0].reshape(-1, naux) eri = numpy.ndarray(((i1 - i0) * (j1 - j0), nvir_pair), buffer=eribuf) for p0, p1 in lib.prange(0, nvir_pair, vvblk): vvL1 = _cp(vvL[p0:p1]) eri[:, p0:p1] = lib.ddot(ijL, vvL1.T) vvL1 = None tmp = numpy.ndarray((i1 - i0, nvirb, j1 - j0, nvirb), buffer=loadbuf) _ccsd.libcc.CCload_eri(tmp.ctypes.data_as(ctypes.c_void_p), eri.ctypes.data_as(ctypes.c_void_p), (ctypes.c_int * 4)(i0, i1, j0, j1), ctypes.c_int(nvirb)) contract_blk_(tmp, i0, i1, j0, j1) time0 = log.timer_debug1('vvvv [%d:%d,%d:%d]' % (i0, i1, j0, j1), *time0) return Ht2.reshape(t2.shape)
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 hasattr(casscf.fcisolver, 'make_rdm1s'): 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 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) 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 ucphf_with_freq(mf, mo_energy, mo_occ, h1, freq=0, max_cycle=20, tol=1e-9, hermi=False, verbose=logger.WARN): log = logger.new_logger(verbose=verbose) t0 = (logger.process_clock(), logger.perf_counter()) occidxa = mo_occ[0] > 0 occidxb = mo_occ[1] > 0 viridxa = ~occidxa viridxb = ~occidxb mo_ea, mo_eb = mo_energy # e_ai - freq may produce very small elements which can cause numerical # issue in krylov solver LEVEL_SHIF = 0.1 e_ai_a = lib.direct_sum('a-i->ai', mo_ea[viridxa], mo_ea[occidxa]).ravel() e_ai_b = lib.direct_sum('a-i->ai', mo_eb[viridxb], mo_eb[occidxb]).ravel() diag = (e_ai_a - freq, e_ai_b - freq, e_ai_a + freq, e_ai_b + freq) diag[0][diag[0] < LEVEL_SHIF] += LEVEL_SHIF diag[1][diag[1] < LEVEL_SHIF] += LEVEL_SHIF diag[2][diag[2] < LEVEL_SHIF] += LEVEL_SHIF diag[3][diag[3] < LEVEL_SHIF] += LEVEL_SHIF mo0a, mo0b = mf.mo_coeff nao, nmoa = mo0a.shape orbva = mo0a[:, viridxa] orbvb = mo0b[:, viridxb] orboa = mo0a[:, occidxa] orbob = mo0b[:, occidxb] nvira = orbva.shape[1] nvirb = orbvb.shape[1] nocca = orboa.shape[1] noccb = orbob.shape[1] h1a = h1[0].reshape(-1, nvira * nocca) h1b = h1[1].reshape(-1, nvirb * noccb) ncomp = h1a.shape[0] mo1base = numpy.hstack( (-h1a / diag[0], -h1b / diag[1], -h1a / diag[2], -h1b / diag[3])) offsets = numpy.cumsum((nocca * nvira, noccb * nvirb, nocca * nvira)) vresp = mf.gen_response(hermi=0) def vind(xys): nz = len(xys) dm1a = numpy.empty((nz, nao, nao)) dm1b = numpy.empty((nz, nao, nao)) for i in range(nz): xa, xb, ya, yb = numpy.split(xys[i], offsets) dmx = reduce(numpy.dot, (orbva, xa.reshape(nvira, nocca), orboa.T)) dmy = reduce(numpy.dot, (orboa, ya.reshape(nvira, nocca).T, orbva.T)) dm1a[i] = dmx + dmy # AX + BY dmx = reduce(numpy.dot, (orbvb, xb.reshape(nvirb, noccb), orbob.T)) dmy = reduce(numpy.dot, (orbob, yb.reshape(nvirb, noccb).T, orbvb.T)) dm1b[i] = dmx + dmy # AX + BY v1ao = vresp(numpy.stack((dm1a, dm1b))) v1voa = lib.einsum('xpq,pi,qj->xij', v1ao[0], orbva, orboa).reshape(nz, -1) v1vob = lib.einsum('xpq,pi,qj->xij', v1ao[1], orbvb, orbob).reshape(nz, -1) v1ova = lib.einsum('xpq,pi,qj->xji', v1ao[0], orboa, orbva).reshape(nz, -1) v1ovb = lib.einsum('xpq,pi,qj->xji', v1ao[1], orbob, orbvb).reshape(nz, -1) for i in range(nz): xa, xb, ya, yb = numpy.split(xys[i], offsets) v1voa[i] += (e_ai_a - freq - diag[0]) * xa v1voa[i] /= diag[0] v1vob[i] += (e_ai_b - freq - diag[1]) * xb v1vob[i] /= diag[1] v1ova[i] += (e_ai_a + freq - diag[2]) * ya v1ova[i] /= diag[2] v1ovb[i] += (e_ai_b + freq - diag[3]) * yb v1ovb[i] /= diag[3] v = numpy.hstack((v1voa, v1vob, v1ova, v1ovb)) return v # FIXME: krylov solver is not accurate enough for many freqs. Using tight # tol and lindep could offer small help. A better linear equation solver # is needed. mo1 = lib.krylov(vind, mo1base, tol=tol, max_cycle=max_cycle, hermi=hermi, lindep=1e-18, verbose=log) log.timer('krylov solver in CPHF', *t0) dm1a = numpy.empty((ncomp, nao, nao)) dm1b = numpy.empty((ncomp, nao, nao)) for i in range(ncomp): xa, xb, ya, yb = numpy.split(mo1[i], offsets) dmx = reduce(numpy.dot, (orbva, xa.reshape(nvira, nocca) * 2, orboa.T)) dmy = reduce(numpy.dot, (orboa, ya.reshape(nvira, nocca).T * 2, orbva.T)) dm1a[i] = dmx + dmy dmx = reduce(numpy.dot, (orbvb, xb.reshape(nvirb, noccb) * 2, orbob.T)) dmy = reduce(numpy.dot, (orbob, yb.reshape(nvirb, noccb).T * 2, orbvb.T)) dm1b[i] = dmx + dmy v1ao = vresp(numpy.stack((dm1a, dm1b))) mo_e1_a = lib.einsum('xpq,pi,qj->xij', v1ao[0], orboa, orboa) mo_e1_b = lib.einsum('xpq,pi,qj->xij', v1ao[1], orbob, orbob) mo_e1 = (mo_e1_a, mo_e1_b) xa, xb, ya, yb = numpy.split(mo1, offsets, axis=1) mo1 = (xa.reshape(ncomp, nvira, nocca), xb.reshape(ncomp, nvirb, noccb), ya.reshape(ncomp, nvira, nocca), yb.reshape(ncomp, nvirb, noccb)) return mo1, mo_e1
def kernel(mf, mo_coeff, mo_occ, conv_tol=1e-10, conv_tol_grad=None, max_cycle=50, dump_chk=True, callback=None, verbose=logger.NOTE): cput0 = (time.clock(), time.time()) log = logger.new_logger(mf, verbose) mol = mf._scf.mol if mol != mf.mol: logger.warn(mf, 'dual-basis SOSCF is an experimental feature. It is ' 'still in testing.') if conv_tol_grad is None: conv_tol_grad = numpy.sqrt(conv_tol) log.info('Set conv_tol_grad to %g', conv_tol_grad) scf_conv = False e_tot = mf.e_tot # call mf._scf.get_hcore, mf._scf.get_ovlp because they might be overloaded h1e = mf._scf.get_hcore(mol) s1e = mf._scf.get_ovlp(mol) dm = mf.make_rdm1(mo_coeff, mo_occ) # call mf._scf.get_veff, to avoid "newton().density_fit()" polluting get_veff vhf = mf._scf.get_veff(mol, dm) e_tot = mf._scf.energy_tot(dm, h1e, vhf) fock = mf.get_fock(h1e, s1e, vhf, dm, level_shift_factor=0) log.info('Initial guess E= %.15g |g|= %g', e_tot, numpy.linalg.norm(mf._scf.get_grad(mo_coeff, mo_occ, fock))) # NOTE: DO NOT change the initial guess mo_occ, mo_coeff mo_energy, mo_tmp = mf.eig(fock, s1e) mf.get_occ(mo_energy, mo_tmp) if dump_chk and mf.chkfile: chkfile.save_mol(mol, mf.chkfile) # Copy the integral file to soscf object to avoid the integrals being cached # twice. if mol == mf.mol and not getattr(mf, 'with_df', None): mf._eri = mf._scf._eri rotaiter = rotate_orb_cc(mf, mo_coeff, mo_occ, fock, h1e, conv_tol_grad, log) u, g_orb, kfcount, jkcount = next(rotaiter) kftot = kfcount + 1 jktot = jkcount cput1 = log.timer('initializing second order scf', *cput0) for imacro in range(max_cycle): dm_last = dm last_hf_e = e_tot norm_gorb = numpy.linalg.norm(g_orb) mo_coeff = mf.rotate_mo(mo_coeff, u, log) dm = mf.make_rdm1(mo_coeff, mo_occ) vhf = mf._scf.get_veff(mol, dm, dm_last=dm_last, vhf_last=vhf) fock = mf.get_fock(h1e, s1e, vhf, dm, level_shift_factor=0) # NOTE: DO NOT change the initial guess mo_occ, mo_coeff if mf.verbose >= logger.DEBUG: mo_energy, mo_tmp = mf.eig(fock, s1e) mf.get_occ(mo_energy, mo_tmp) # call mf._scf.energy_tot for dft, because the (dft).get_veff step saved _exc in mf._scf e_tot = mf._scf.energy_tot(dm, h1e, vhf) log.info('macro= %d E= %.15g delta_E= %g |g|= %g %d KF %d JK', imacro, e_tot, e_tot-last_hf_e, norm_gorb, kfcount+1, jkcount) cput1 = log.timer('cycle= %d'%(imacro+1), *cput1) if (abs((e_tot-last_hf_e)/e_tot)*1e2 < conv_tol and norm_gorb < conv_tol_grad): scf_conv = True if dump_chk: mf.dump_chk(locals()) if callable(callback): callback(locals()) if scf_conv: break u, g_orb, kfcount, jkcount = rotaiter.send((mo_coeff, mo_occ, fock)) kftot += kfcount + 1 jktot += jkcount if callable(callback): callback(locals()) rotaiter.close() mo_energy, mo_coeff1 = mf._scf.canonicalize(mo_coeff, mo_occ, fock) if mf.canonicalization: log.info('Canonicalize SCF orbitals') mo_coeff = mo_coeff1 if dump_chk: mf.dump_chk(locals()) log.info('macro X = %d E=%.15g |g|= %g total %d KF %d JK', imacro+1, e_tot, norm_gorb, kftot, jktot) if (numpy.any(mo_occ==0) and mo_energy[mo_occ>0].max() > mo_energy[mo_occ==0].min()): log.warn('H**O %s > LUMO %s was found in the canonicalized orbitals.', mo_energy[mo_occ>0].max(), mo_energy[mo_occ==0].min()) return scf_conv, e_tot, mo_energy, mo_coeff, mo_occ
def get_nto(tdobj, state=1, threshold=OUTPUT_THRESHOLD, verbose=None): r''' Natural transition orbital analysis. The natural transition density matrix between ground state and excited state :math:`Tia = \langle \Psi_{ex} | i a^\dagger | \Psi_0 \rangle` can be transformed to diagonal form through SVD :math:`T = O \sqrt{\lambda} V^\dagger`. O and V are occupied and virtual natural transition orbitals. The diagonal elements :math:`\lambda` are the weights of the occupied-virtual orbital pair in the excitation. Ref: Martin, R. L., JCP, 118, 4775-4777 Note in the TDHF/TDDFT calculations, the excitation part (X) is interpreted as the CIS coefficients and normalized to 1. The de-excitation part (Y) is ignored. Args: state : int Excited state ID. state = 1 means the first excited state. If state < 0, state ID is counted from the last excited state. Kwargs: threshold : float Above which the NTO coefficients will be printed in the output. Returns: A list (weights, NTOs). NTOs are natural orbitals represented in AO basis. The first N_occ NTOs are occupied NTOs and the rest are virtual NTOs. ''' if state == 0: logger.warn( tdobj, 'Excited state starts from 1. ' 'Set state=1 for first excited state.') state_id = state elif state < 0: state_id = state else: state_id = state - 1 mol = tdobj.mol mo_coeff = tdobj._scf.mo_coeff mo_occ = tdobj._scf.mo_occ orbo_a = mo_coeff[0][:, mo_occ[0] == 1] orbv_a = mo_coeff[0][:, mo_occ[0] == 0] orbo_b = mo_coeff[1][:, mo_occ[1] == 1] orbv_b = mo_coeff[1][:, mo_occ[1] == 0] nocc_a = orbo_a.shape[1] nvir_a = orbv_a.shape[1] nocc_b = orbo_b.shape[1] nvir_b = orbv_b.shape[1] cis_t1a, cis_t1b = tdobj.xy[state_id][0] norm = numpy.linalg.norm(cis_t1a)**2 + numpy.linalg.norm(cis_t1b)**2 cis_t1a *= 1. / norm cis_t1b *= 1. / norm if mol.symmetry: orbsyma, orbsymb = uhf_symm.get_orbsym(mol, mo_coeff) o_sym_a = orbsyma[mo_occ[0] == 1] v_sym_a = orbsyma[mo_occ[0] == 0] o_sym_b = orbsymb[mo_occ[1] == 1] v_sym_b = orbsymb[mo_occ[1] == 0] nto_o_a = numpy.eye(nocc_a) nto_v_a = numpy.eye(nvir_a) nto_o_b = numpy.eye(nocc_b) nto_v_b = numpy.eye(nvir_b) weights_o_a = numpy.zeros(nocc_a) weights_v_a = numpy.zeros(nvir_a) weights_o_b = numpy.zeros(nocc_b) weights_v_b = numpy.zeros(nvir_b) for ir in set(orbsyma): idx = numpy.where(o_sym_a == ir)[0] if idx.size > 0: dm_oo = numpy.dot(cis_t1a[idx], cis_t1a[idx].T) weights_o_a[idx], nto_o_a[idx[:, None], idx] = numpy.linalg.eigh(dm_oo) idx = numpy.where(v_sym_a == ir)[0] if idx.size > 0: dm_vv = numpy.dot(cis_t1a[:, idx].T, cis_t1a[:, idx]) weights_v_a[idx], nto_v_a[idx[:, None], idx] = numpy.linalg.eigh(dm_vv) for ir in set(orbsymb): idx = numpy.where(o_sym_b == ir)[0] if idx.size > 0: dm_oo = numpy.dot(cis_t1b[idx], cis_t1b[idx].T) weights_o_b[idx], nto_o_b[idx[:, None], idx] = numpy.linalg.eigh(dm_oo) idx = numpy.where(v_sym_b == ir)[0] if idx.size > 0: dm_vv = numpy.dot(cis_t1b[:, idx].T, cis_t1b[:, idx]) weights_v_b[idx], nto_v_b[idx[:, None], idx] = numpy.linalg.eigh(dm_vv) def sort(weights, nto, sym): # weights in descending order idx = numpy.argsort(-weights) weights = weights[idx] nto = nto[:, idx] sym = sym[idx] return weights, nto, sym weights_o_a, nto_o_a, o_sym_a = sort(weights_o_a, nto_o_a, o_sym_a) weights_v_a, nto_v_a, v_sym_a = sort(weights_v_a, nto_v_a, v_sym_a) weights_o_b, nto_o_b, o_sym_b = sort(weights_o_b, nto_o_b, o_sym_b) weights_v_b, nto_v_b, v_sym_b = sort(weights_v_b, nto_v_b, v_sym_b) nto_orbsyma = numpy.hstack((o_sym_a, v_sym_a)) nto_orbsymb = numpy.hstack((o_sym_b, v_sym_b)) if nocc_a < nvir_a: weights_a = weights_o_a else: weights_a = weights_v_a if nocc_b < nvir_b: weights_b = weights_o_b else: weights_b = weights_v_b else: nto_o_a, w_a, nto_v_aT = numpy.linalg.svd(cis_t1a) nto_o_b, w_b, nto_v_bT = numpy.linalg.svd(cis_t1b) nto_v_a = nto_v_aT.conj().T nto_v_b = nto_v_bT.conj().T weights_a = w_a**2 weights_b = w_b**2 nto_orbsyma = nto_orbsymb = None def _set_phase_(c): idx = numpy.argmax(abs(c.real), axis=0) c[:, c[idx, numpy.arange(c.shape[1])].real < 0] *= -1 _set_phase_(nto_o_a) _set_phase_(nto_o_b) _set_phase_(nto_v_a) _set_phase_(nto_v_b) occupied_nto_a = numpy.dot(orbo_a, nto_o_a) occupied_nto_b = numpy.dot(orbo_b, nto_o_b) virtual_nto_a = numpy.dot(orbv_a, nto_v_a) virtual_nto_b = numpy.dot(orbv_b, nto_v_b) nto_coeff = (numpy.hstack((occupied_nto_a, virtual_nto_a)), numpy.hstack((occupied_nto_b, virtual_nto_b))) if mol.symmetry: nto_coeff = (lib.tag_array(nto_coeff[0], orbsym=nto_orbsyma), lib.tag_array(nto_coeff[1], orbsym=nto_orbsymb)) log = logger.new_logger(tdobj, verbose) if log.verbose >= logger.INFO: log.info('State %d: %g eV NTO largest component %s', state_id + 1, tdobj.e[state_id] * nist.HARTREE2EV, weights_a[0] + weights_b[0]) fmt = '%' + str(lib.param.OUTPUT_DIGITS) + 'f (MO #%d)' o_idx_a = numpy.where(abs(nto_o_a[:, 0]) > threshold)[0] v_idx_a = numpy.where(abs(nto_v_a[:, 0]) > threshold)[0] o_idx_b = numpy.where(abs(nto_o_b[:, 0]) > threshold)[0] v_idx_b = numpy.where(abs(nto_v_b[:, 0]) > threshold)[0] log.info(' alpha occ-NTO: ' + ' + '.join([(fmt % (nto_o_a[i, 0], i + MO_BASE)) for i in o_idx_a])) log.info(' alpha vir-NTO: ' + ' + '.join([(fmt % (nto_v_a[i, 0], i + MO_BASE + nocc_a)) for i in v_idx_a])) log.info(' beta occ-NTO: ' + ' + '.join([(fmt % (nto_o_b[i, 0], i + MO_BASE)) for i in o_idx_b])) log.info(' beta vir-NTO: ' + ' + '.join([(fmt % (nto_v_b[i, 0], i + MO_BASE + nocc_b)) for i in v_idx_b])) return (weights_a, weights_b), nto_coeff
def analyze(tdobj, verbose=None): log = logger.new_logger(tdobj, verbose) mol = tdobj.mol mo_coeff = tdobj._scf.mo_coeff mo_occ = tdobj._scf.mo_occ nocc_a = numpy.count_nonzero(mo_occ[0] == 1) nocc_b = numpy.count_nonzero(mo_occ[1] == 1) e_ev = numpy.asarray(tdobj.e) * nist.HARTREE2EV e_wn = numpy.asarray(tdobj.e) * nist.HARTREE2WAVENUMBER wave_length = 1e11 / e_wn log.note('\n** Excitation energies and oscillator strengths **') if mol.symmetry: orbsyma, orbsymb = uhf_symm.get_orbsym(mol, mo_coeff) orbsyma = orbsyma % 10 x_syma = (orbsyma[mo_occ[0] == 1, None] ^ orbsyma[mo_occ[0] == 0]).ravel() else: x_syma = None f_oscillator = tdobj.oscillator_strength() for i, ei in enumerate(tdobj.e): x, y = tdobj.xy[i] if x_syma is None: log.note('Excited State %3d: %12.5f eV %9.2f nm f=%.4f', i + 1, e_ev[i], wave_length[i], f_oscillator[i]) else: wfnsym_id = x_syma[abs(x[0]).argmax()] wfnsym = symm.irrep_id2name(mol.groupname, wfnsym_id) log.note('Excited State %3d: %4s %12.5f eV %9.2f nm f=%.4f', i + 1, wfnsym, e_ev[i], wave_length[i], f_oscillator[i]) if log.verbose >= logger.INFO: for o, v in zip(*numpy.where(abs(x[0]) > 0.1)): log.info(' %4da -> %4da %12.5f', o + MO_BASE, v + MO_BASE + nocc_a, x[0][o, v]) for o, v in zip(*numpy.where(abs(x[1]) > 0.1)): log.info(' %4db -> %4db %12.5f', o + MO_BASE, v + MO_BASE + nocc_b, x[1][o, v]) if log.verbose >= logger.INFO: log.info('\n** Transition electric dipole moments (AU) **') log.info( 'state X Y Z Dip. S. Osc.' ) trans_dip = tdobj.transition_dipole() for i, ei in enumerate(tdobj.e): dip = trans_dip[i] log.info('%3d %11.4f %11.4f %11.4f %11.4f %11.4f', i + 1, dip[0], dip[1], dip[2], numpy.dot(dip, dip), f_oscillator[i]) log.info( '\n** Transition velocity dipole moments (imaginary part AU) **') log.info( 'state X Y Z Dip. S. Osc.' ) trans_v = tdobj.transition_velocity_dipole() f_v = tdobj.oscillator_strength(gauge='velocity', order=0) for i, ei in enumerate(tdobj.e): v = trans_v[i] log.info('%3d %11.4f %11.4f %11.4f %11.4f %11.4f', i + 1, v[0], v[1], v[2], numpy.dot(v, v), f_v[i]) log.info('\n** Transition magnetic dipole moments (AU) **') log.info('state X Y Z') trans_m = tdobj.transition_magnetic_dipole() for i, ei in enumerate(tdobj.e): m = trans_m[i] log.info('%3d %11.4f %11.4f %11.4f', i + 1, m[0], m[1], m[2]) return tdobj
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: mf.dump_scf_summary(log) 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.conj().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(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: self.dump_scf_summary(log) 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.conj().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 _ao2mo_ovov(mp, orbs, feri, max_memory=2000, verbose=None): time0 = (time.clock(), time.time()) log = logger.new_logger(mp, verbose) orboa = numpy.asarray(orbs[0], order='F') orbva = numpy.asarray(orbs[1], order='F') orbob = numpy.asarray(orbs[2], order='F') orbvb = numpy.asarray(orbs[3], order='F') nao, nocca = orboa.shape noccb = orbob.shape[1] nvira = orbva.shape[1] nvirb = orbvb.shape[1] mol = mp.mol int2e = mol._add_suffix('int2e') ao2mopt = _ao2mo.AO2MOpt(mol, int2e, 'CVHFnr_schwarz_cond', 'CVHFsetnr_direct_scf') nbas = mol.nbas assert (nvira <= nao) assert (nvirb <= nao) ao_loc = mol.ao_loc_nr() dmax = max( 4, min(nao / 3, numpy.sqrt(max_memory * .95e6 / 8 / (nao + nocca)**2))) sh_ranges = ao2mo.outcore.balance_partition(ao_loc, dmax) dmax = max(x[2] for x in sh_ranges) eribuf = numpy.empty((nao, dmax, dmax, nao)) ftmp = lib.H5TmpFile() disk = (nocca**2 * (nao * (nao + dmax) / 2 + nvira**2) + noccb**2 * (nao * (nao + dmax) / 2 + nvirb**2) + nocca * noccb * (nao**2 + nvira * nvirb)) log.debug('max_memory %s MB (dmax = %s) required disk space %g MB', max_memory, dmax, disk * 8 / 1e6) fint = gto.moleintor.getints4c aa_blk_slices = [] ab_blk_slices = [] count_ab = 0 count_aa = 0 time1 = time0 with lib.call_in_background(ftmp.__setitem__) as save: for ish0, ish1, ni in sh_ranges: for jsh0, jsh1, nj in sh_ranges: i0, i1 = ao_loc[ish0], ao_loc[ish1] j0, j1 = ao_loc[jsh0], ao_loc[jsh1] eri = fint(int2e, mol._atm, mol._bas, mol._env, shls_slice=(0, nbas, ish0, ish1, jsh0, jsh1, 0, nbas), aosym='s1', ao_loc=ao_loc, cintopt=ao2mopt._cintopt, out=eribuf) tmp_i = lib.ddot(orboa.T, eri.reshape(nao, (i1 - i0) * (j1 - j0) * nao)) tmp_li = lib.ddot( orbob.T, tmp_i.reshape(nocca * (i1 - i0) * (j1 - j0), nao).T) tmp_li = tmp_li.reshape(noccb, nocca, (i1 - i0), (j1 - j0)) save('ab/%d' % count_ab, tmp_li.transpose(1, 0, 2, 3)) ab_blk_slices.append((i0, i1, j0, j1)) count_ab += 1 if ish0 >= jsh0: tmp_li = lib.ddot( orboa.T, tmp_i.reshape(nocca * (i1 - i0) * (j1 - j0), nao).T) tmp_li = tmp_li.reshape(nocca, nocca, (i1 - i0), (j1 - j0)) save('aa/%d' % count_aa, tmp_li.transpose(1, 0, 2, 3)) tmp_i = lib.ddot( orbob.T, eri.reshape(nao, (i1 - i0) * (j1 - j0) * nao)) tmp_li = lib.ddot( orbob.T, tmp_i.reshape(noccb * (i1 - i0) * (j1 - j0), nao).T) tmp_li = tmp_li.reshape(noccb, noccb, (i1 - i0), (j1 - j0)) save('bb/%d' % count_aa, tmp_li.transpose(1, 0, 2, 3)) aa_blk_slices.append((i0, i1, j0, j1)) count_aa += 1 time1 = log.timer_debug1( 'partial ao2mo [%d:%d,%d:%d]' % (ish0, ish1, jsh0, jsh1), *time1) time1 = time0 = log.timer('mp2 ao2mo_ovov pass1', *time0) eri = eribuf = tmp_i = tmp_li = None fovov = feri.create_dataset('ovov', (nocca * nvira, nocca * nvira), 'f8', chunks=(nvira, nvira)) fovOV = feri.create_dataset('ovOV', (nocca * nvira, noccb * nvirb), 'f8', chunks=(nvira, nvirb)) fOVOV = feri.create_dataset('OVOV', (noccb * nvirb, noccb * nvirb), 'f8', chunks=(nvirb, nvirb)) occblk = int( min(max(nocca, noccb), max(4, 250 / nocca, max_memory * .9e6 / 8 / (nao**2 * nocca) / 5))) def load_aa(h5g, nocc, i0, eri): if i0 < nocc: i1 = min(i0 + occblk, nocc) for k, (p0, p1, q0, q1) in enumerate(aa_blk_slices): eri[:i1 - i0, :, p0:p1, q0:q1] = h5g[str(k)][i0:i1] if p0 != q0: dat = numpy.asarray(h5g[str(k)][:, i0:i1]) eri[:i1 - i0, :, q0:q1, p0:p1] = dat.transpose(1, 0, 3, 2) def load_ab(h5g, nocca, i0, eri): if i0 < nocca: i1 = min(i0 + occblk, nocca) for k, (p0, p1, q0, q1) in enumerate(ab_blk_slices): eri[:i1 - i0, :, p0:p1, q0:q1] = h5g[str(k)][i0:i1] def save(h5dat, nvir, i0, i1, dat): for i in range(i0, i1): h5dat[i * nvir:(i + 1) * nvir] = dat[i - i0].reshape(nvir, -1) with lib.call_in_background(save) as bsave: with lib.call_in_background(load_aa) as prefetch: buf_prefecth = numpy.empty((occblk, nocca, nao, nao)) buf = numpy.empty_like(buf_prefecth) load_aa(ftmp['aa'], nocca, 0, buf_prefecth) for i0, i1 in lib.prange(0, nocca, occblk): buf, buf_prefecth = buf_prefecth, buf prefetch(ftmp['aa'], nocca, i1, buf_prefecth) eri = buf[:i1 - i0].reshape((i1 - i0) * nocca, nao, nao) dat = _ao2mo.nr_e2(eri, orbva, (0, nvira, 0, nvira), 's1', 's1') bsave( fovov, nvira, i0, i1, dat.reshape(i1 - i0, nocca, nvira, nvira).transpose(0, 2, 1, 3)) time1 = log.timer_debug1( 'pass2 ao2mo for aa [%d:%d]' % (i0, i1), *time1) buf_prefecth = numpy.empty((occblk, noccb, nao, nao)) buf = numpy.empty_like(buf_prefecth) load_aa(ftmp['bb'], noccb, 0, buf_prefecth) for i0, i1 in lib.prange(0, noccb, occblk): buf, buf_prefecth = buf_prefecth, buf prefetch(ftmp['bb'], noccb, i1, buf_prefecth) eri = buf[:i1 - i0].reshape((i1 - i0) * noccb, nao, nao) dat = _ao2mo.nr_e2(eri, orbvb, (0, nvirb, 0, nvirb), 's1', 's1') bsave( fOVOV, nvirb, i0, i1, dat.reshape(i1 - i0, noccb, nvirb, nvirb).transpose(0, 2, 1, 3)) time1 = log.timer_debug1( 'pass2 ao2mo for bb [%d:%d]' % (i0, i1), *time1) orbvab = numpy.asarray(numpy.hstack((orbva, orbvb)), order='F') with lib.call_in_background(load_ab) as prefetch: load_ab(ftmp['ab'], nocca, 0, buf_prefecth) for i0, i1 in lib.prange(0, nocca, occblk): buf, buf_prefecth = buf_prefecth, buf prefetch(ftmp['ab'], nocca, i1, buf_prefecth) eri = buf[:i1 - i0].reshape((i1 - i0) * noccb, nao, nao) dat = _ao2mo.nr_e2(eri, orbvab, (0, nvira, nvira, nvira + nvirb), 's1', 's1') bsave( fovOV, nvira, i0, i1, dat.reshape(i1 - i0, noccb, nvira, nvirb).transpose(0, 2, 1, 3)) time1 = log.timer_debug1( 'pass2 ao2mo for ab [%d:%d]' % (i0, i1), *time1) time0 = log.timer('mp2 ao2mo_ovov pass2', *time0)
def kernel(mycc, eris, t1=None, t2=None, verbose=logger.NOTE): cpu1 = cpu0 = (time.clock(), time.time()) log = logger.new_logger(mycc, verbose) if t1 is None: t1 = mycc.t1 if t2 is None: t2 = mycc.t2 nocc, nvir = t1.shape nmo = nocc + nvir _tmpfile = tempfile.NamedTemporaryFile(dir=lib.param.TMPDIR) ftmp = h5py.File(_tmpfile.name) eris_vvop = ftmp.create_dataset('vvop', (nvir, nvir, nocc, nmo), 'f8') orbsym = _sort_eri(mycc, eris, nocc, nvir, eris_vvop, log) ftmp[ 't2'] = t2 # read back late. Cache t2T in t2 to reduce memory footprint mo_energy, t1T, t2T, vooo = _sort_t2_vooo_(mycc, orbsym, t1, t2, eris) cpu1 = log.timer_debug1('CCSD(T) sort_eri', *cpu1) cpu2 = list(cpu1) orbsym = numpy.hstack( (numpy.sort(orbsym[:nocc]), numpy.sort(orbsym[nocc:]))) o_ir_loc = numpy.append( 0, numpy.cumsum(numpy.bincount(orbsym[:nocc], minlength=8))) v_ir_loc = numpy.append( 0, numpy.cumsum(numpy.bincount(orbsym[nocc:], minlength=8))) o_sym = orbsym[:nocc] oo_sym = (o_sym[:, None] ^ o_sym).ravel() oo_ir_loc = numpy.append(0, numpy.cumsum(numpy.bincount(oo_sym, minlength=8))) nirrep = max(oo_sym) + 1 orbsym = orbsym.astype(numpy.int32) o_ir_loc = o_ir_loc.astype(numpy.int32) v_ir_loc = v_ir_loc.astype(numpy.int32) oo_ir_loc = oo_ir_loc.astype(numpy.int32) et_sum = [0] def contract(a0, a1, b0, b1, cache): cache_row_a, cache_col_a, cache_row_b, cache_col_b = cache drv = _ccsd.libcc.CCsd_t_contract drv.restype = ctypes.c_double et = drv(mo_energy.ctypes.data_as(ctypes.c_void_p), t1T.ctypes.data_as(ctypes.c_void_p), t2T.ctypes.data_as(ctypes.c_void_p), vooo.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(nocc), ctypes.c_int(nvir), ctypes.c_int(a0), ctypes.c_int(a1), ctypes.c_int(b0), ctypes.c_int(b1), ctypes.c_int(nirrep), o_ir_loc.ctypes.data_as(ctypes.c_void_p), v_ir_loc.ctypes.data_as(ctypes.c_void_p), oo_ir_loc.ctypes.data_as(ctypes.c_void_p), orbsym.ctypes.data_as(ctypes.c_void_p), cache_row_a.ctypes.data_as(ctypes.c_void_p), cache_col_a.ctypes.data_as(ctypes.c_void_p), cache_row_b.ctypes.data_as(ctypes.c_void_p), cache_col_b.ctypes.data_as(ctypes.c_void_p)) cpu2[:] = log.timer_debug1('contract %d:%d,%d:%d' % (a0, a1, b0, b1), *cpu2) et_sum[0] += et return et # The rest 20% memory for cache b mem_now = lib.current_memory()[0] max_memory = max(2000, mycc.max_memory - mem_now) bufsize = max(1, (max_memory * 1e6 / 8 - nocc**3 * 100) * .7 / (nocc * nmo)) log.debug('max_memory %d MB (%d MB in use)', max_memory, mem_now) for a0, a1 in reversed(list(lib.prange_tril(0, nvir, bufsize))): with lib.call_in_background(contract) as async_contract: cache_row_a = numpy.asarray(eris_vvop[a0:a1, :a1], order='C') if a0 == 0: cache_col_a = cache_row_a else: cache_col_a = numpy.asarray(eris_vvop[:a0, a0:a1], order='C') async_contract( a0, a1, a0, a1, (cache_row_a, cache_col_a, cache_row_a, cache_col_a)) for b0, b1 in lib.prange_tril(0, a0, bufsize / 6): cache_row_b = numpy.asarray(eris_vvop[b0:b1, :b1], order='C') if b0 == 0: cache_col_b = cache_row_b else: cache_col_b = numpy.asarray(eris_vvop[:b0, b0:b1], order='C') async_contract( a0, a1, b0, b1, (cache_row_a, cache_col_a, cache_row_b, cache_col_b)) cache_row_b = cache_col_b = None cache_row_a = cache_col_a = None t2[:] = ftmp['t2'] ftmp.close() _tmpfile = None et = et_sum[0] * 2 log.timer('CCSD(T)', *cpu0) log.note('CCSD(T) correction = %.15g', et) return et
def dump_flags(self, verbose=None): log = logger.new_logger(self, verbose) hf.RHF.dump_flags(self, log) log.info('atom = %s', self.mol.atom_symbol(0))
def grad_elec(mc_grad, mo_coeff=None, ci=None, atmlst=None, verbose=None): mc = mc_grad.base with_df = mc.with_df if mo_coeff is None: mo_coeff = mc.mo_coeff if ci is None: ci = mc.ci if mc.frozen is not None: raise NotImplementedError time0 = time.clock(), time.time() log = logger.new_logger(mc_grad, verbose) mol = mc_grad.mol auxmol = with_df.auxmol ncore = mc.ncore ncas = mc.ncas nocc = ncore + ncas nelecas = mc.nelecas nao, nmo = mo_coeff.shape nao_pair = nao * (nao + 1) // 2 # Necessary kludge because gfock isn't zero in occ-virt space in SA-CASSCf # Among many other potential applications! if hasattr(mc, '_tag_gfock_ov_nonzero'): if mc._tag_gfock_ov_nonzero: nocc = nmo mo_occ = mo_coeff[:, :nocc] mo_core = mo_coeff[:, :ncore] mo_cas = mo_coeff[:, ncore:ncore + ncas] casdm1, casdm2 = mc.fcisolver.make_rdm12(ci, ncas, nelecas) # gfock = Generalized Fock, Adv. Chem. Phys., 69, 63 dm_core = numpy.dot(mo_core, mo_core.T) * 2 dm_cas = reduce(numpy.dot, (mo_cas, casdm1, mo_cas.T)) # MRH flag: this is one of my kludges # It would be better to just pass the ERIS object used in orbital optimization # But I am too lazy at the moment aapa = with_df.ao2mo((mo_cas, mo_cas, mo_occ, mo_cas), compact=False) aapa = aapa.reshape(ncas, ncas, nocc, ncas) vj, vk = mc._scf.get_jk(mol, (dm_core, dm_cas)) h1 = mc.get_hcore() vhf_c = vj[0] - vk[0] * .5 vhf_a = vj[1] - vk[1] * .5 gfock = numpy.zeros((nocc, nocc)) gfock[:, :ncore] = reduce(numpy.dot, (mo_occ.T, h1 + vhf_c + vhf_a, mo_core)) * 2 gfock[:, ncore:ncore + ncas] = reduce(numpy.dot, (mo_occ.T, h1 + vhf_c, mo_cas, casdm1)) gfock[:, ncore:ncore + ncas] += numpy.einsum('uviw,vuwt->it', aapa, casdm2) dme0 = reduce(numpy.dot, (mo_occ, (gfock + gfock.T) * .5, mo_occ.T)) aapa = vj = vk = vhf_c = vhf_a = h1 = gfock = None dm1 = dm_core + dm_cas vj, vk = mc_grad.get_jk(mol, (dm_core, dm_cas)) vhf1c, vhf1a = vj - vk * .5 hcore_deriv = mc_grad.hcore_generator(mol) s1 = mc_grad.get_ovlp(mol) dfcasdm2 = casdm2 = solve_df_rdm2(mc_grad, mo_cas=mo_cas, casdm2=casdm2) if atmlst is None: atmlst = range(mol.natm) aoslices = mol.aoslice_by_atom() de = grad_elec_dferi(mc_grad, mo_cas=mo_cas, dfcasdm2=dfcasdm2, atmlst=atmlst, max_memory=mc_grad.max_memory)[0] if mc_grad.auxbasis_response: de_aux = vj.aux - vk.aux * .5 de_aux = de_aux.sum((0, 1)) - de_aux[1, 1] de_aux += grad_elec_auxresponse_dferi(mc_grad, mo_cas=mo_cas, dfcasdm2=dfcasdm2, atmlst=atmlst, max_memory=mc_grad.max_memory)[0] de += de_aux dfcasdm2 = casdm2 = None for k, ia in enumerate(atmlst): shl0, shl1, p0, p1 = aoslices[ia] h1ao = hcore_deriv(ia) de[k] += numpy.einsum('xij,ij->x', h1ao, dm1) de[k] -= numpy.einsum('xij,ij->x', s1[:, p0:p1], dme0[p0:p1]) * 2 de[k] += numpy.einsum('xij,ij->x', vhf1c[:, p0:p1], dm1[p0:p1]) * 2 de[k] += numpy.einsum('xij,ij->x', vhf1a[:, p0:p1], dm_core[p0:p1]) * 2 log.timer('CASSCF nuclear gradients', *time0) return de
def _make_eris(mp, mo_coeff=None, ao2mofn=None, verbose=None): log = logger.new_logger(mp, verbose) time0 = (time.clock(), time.time()) eris = _ChemistsERIs(mp, mo_coeff) nocca, noccb = mp.get_nocc() nmoa, nmob = mp.get_nmo() nvira, nvirb = nmoa - nocca, nmob - noccb nao = eris.mo_coeff[0].shape[0] nmo_pair = nmoa * (nmoa + 1) // 2 nao_pair = nao * (nao + 1) // 2 mem_incore = (nao_pair**2 + nmo_pair**2) * 8 / 1e6 mem_now = lib.current_memory()[0] max_memory = max(0, mp.max_memory - mem_now) moa = eris.mo_coeff[0] mob = eris.mo_coeff[1] orboa = moa[:, :nocca] orbob = mob[:, :noccb] orbva = moa[:, nocca:] orbvb = mob[:, noccb:] if (mp.mol.incore_anyway or (mp._scf._eri is not None and mem_incore + mem_now < mp.max_memory)): log.debug('transform (ia|jb) incore') if callable(ao2mofn): eris.ovov = ao2mofn( (orboa, orbva, orboa, orbva)).reshape(nocca * nvira, nocca * nvira) eris.ovOV = ao2mofn( (orboa, orbva, orbob, orbvb)).reshape(nocca * nvira, noccb * nvirb) eris.OVOV = ao2mofn( (orbob, orbvb, orbob, orbvb)).reshape(noccb * nvirb, noccb * nvirb) else: eris.ovov = ao2mo.general(mp._scf._eri, (orboa, orbva, orboa, orbva)) eris.ovOV = ao2mo.general(mp._scf._eri, (orboa, orbva, orbob, orbvb)) eris.OVOV = ao2mo.general(mp._scf._eri, (orbob, orbvb, orbob, orbvb)) elif hasattr(mp._scf, 'with_df'): logger.warn( mp, 'UMP2 detected DF being used in the HF object. ' 'MO integrals are computed based on the DF 3-index tensors.\n' 'It\'s recommended to use DF-UMP2 module.') log.debug('transform (ia|jb) with_df') eris.ovov = mp._scf.with_df.ao2mo((orboa, orbva, orboa, orbva)) eris.ovOV = mp._scf.with_df.ao2mo((orboa, orbva, orbob, orbvb)) eris.OVOV = mp._scf.with_df.ao2mo((orbob, orbvb, orbob, orbvb)) else: log.debug('transform (ia|jb) outcore') eris.feri = lib.H5TmpFile() _ao2mo_ovov(mp, (orboa, orbva, orbob, orbvb), eris.feri, max(2000, max_memory), log) eris.ovov = eris.feri['ovov'] eris.ovOV = eris.feri['ovOV'] eris.OVOV = eris.feri['OVOV'] time1 = log.timer('Integral transformation', *time0) return eris
def grad_elec(mc_grad, mo_coeff=None, ci=None, atmlst=None, verbose=None): mc = mc_grad.base if mo_coeff is None: mo_coeff = mc._scf.mo_coeff if ci is None: ci = mc.ci time0 = time.clock(), time.time() log = logger.new_logger(mc_grad, verbose) mol = mc_grad.mol ncore = mc.ncore ncas = mc.ncas nocc = ncore + ncas nelecas = mc.nelecas nao, nmo = mo_coeff.shape nao_pair = nao * (nao + 1) // 2 mo_energy = mc._scf.mo_energy mo_occ = mo_coeff[:, :nocc] mo_core = mo_coeff[:, :ncore] mo_cas = mo_coeff[:, ncore:nocc] neleca, nelecb = mol.nelec assert (neleca == nelecb) orbo = mo_coeff[:, :neleca] orbv = mo_coeff[:, neleca:] casdm1, casdm2 = mc.fcisolver.make_rdm12(ci, ncas, nelecas) dm_core = numpy.dot(mo_core, mo_core.T) * 2 dm_cas = reduce(numpy.dot, (mo_cas, casdm1, mo_cas.T)) aapa = ao2mo.kernel(mol, (mo_cas, mo_cas, mo_coeff, mo_cas), compact=False) aapa = aapa.reshape(ncas, ncas, nmo, ncas) vj, vk = mc._scf.get_jk(mol, (dm_core, dm_cas)) h1 = mc.get_hcore() vhf_c = vj[0] - vk[0] * .5 vhf_a = vj[1] - vk[1] * .5 # Imat = h1_{pi} gamma1_{iq} + h2_{pijk} gamma_{iqkj} Imat = numpy.zeros((nmo, nmo)) Imat[:, :nocc] = reduce(numpy.dot, (mo_coeff.T, h1 + vhf_c + vhf_a, mo_occ)) * 2 Imat[:, ncore:nocc] = reduce(numpy.dot, (mo_coeff.T, h1 + vhf_c, mo_cas, casdm1)) Imat[:, ncore:nocc] += lib.einsum('uviw,vuwt->it', aapa, casdm2) aapa = vj = vk = vhf_c = vhf_a = h1 = None ee = mo_energy[:, None] - mo_energy zvec = numpy.zeros_like(Imat) zvec[:ncore, ncore:neleca] = Imat[:ncore, ncore:neleca] / -ee[:ncore, ncore:neleca] zvec[ncore:neleca, :ncore] = Imat[ ncore:neleca, :ncore] / -ee[ncore:neleca, :ncore] zvec[nocc:, neleca:nocc] = Imat[nocc:, neleca:nocc] / -ee[nocc:, neleca:nocc] zvec[neleca:nocc, nocc:] = Imat[neleca:nocc, nocc:] / -ee[neleca:nocc, nocc:] zvec_ao = reduce(numpy.dot, (mo_coeff, zvec + zvec.T, mo_coeff.T)) vhf = mc._scf.get_veff(mol, zvec_ao) * 2 xvo = reduce(numpy.dot, (orbv.T, vhf, orbo)) xvo += Imat[neleca:, :neleca] - Imat[:neleca, neleca:].T def fvind(x): x = x.reshape(xvo.shape) dm = reduce(numpy.dot, (orbv, x, orbo.T)) v = mc._scf.get_veff(mol, dm + dm.T) v = reduce(numpy.dot, (orbv.T, v, orbo)) return v * 2 dm1resp = cphf.solve(fvind, mo_energy, mc._scf.mo_occ, xvo, max_cycle=30)[0] zvec[neleca:, :neleca] = dm1resp zeta = numpy.einsum('ij,j->ij', zvec, mo_energy) zeta = reduce(numpy.dot, (mo_coeff, zeta, mo_coeff.T)) zvec_ao = reduce(numpy.dot, (mo_coeff, zvec + zvec.T, mo_coeff.T)) p1 = numpy.dot(mo_coeff[:, :neleca], mo_coeff[:, :neleca].T) vhf_s1occ = reduce(numpy.dot, (p1, mc._scf.get_veff(mol, zvec_ao), p1)) Imat[:ncore, ncore:neleca] = 0 Imat[ncore:neleca, :ncore] = 0 Imat[nocc:, neleca:nocc] = 0 Imat[neleca:nocc, nocc:] = 0 Imat[neleca:, :neleca] = Imat[:neleca, neleca:].T im1 = reduce(numpy.dot, (mo_coeff, Imat, mo_coeff.T)) casci_dm1 = dm_core + dm_cas hf_dm1 = mc._scf.make_rdm1(mo_coeff, mc._scf.mo_occ) hcore_deriv = mc_grad.hcore_generator(mol) s1 = mc_grad.get_ovlp(mol) diag_idx = numpy.arange(nao) diag_idx = diag_idx * (diag_idx + 1) // 2 + diag_idx casdm2_cc = casdm2 + casdm2.transpose(0, 1, 3, 2) dm2buf = ao2mo._ao2mo.nr_e2(casdm2_cc.reshape(ncas**2, ncas**2), mo_cas.T, (0, nao, 0, nao)).reshape(ncas**2, nao, nao) dm2buf = lib.pack_tril(dm2buf) dm2buf[:, diag_idx] *= .5 dm2buf = dm2buf.reshape(ncas, ncas, nao_pair) casdm2 = casdm2_cc = None if atmlst is None: atmlst = range(mol.natm) aoslices = mol.aoslice_by_atom() de = numpy.zeros((len(atmlst), 3)) max_memory = mc_grad.max_memory - lib.current_memory()[0] blksize = int(max_memory * .9e6 / 8 / ((aoslices[:, 3] - aoslices[:, 2]).max() * nao_pair)) blksize = min(nao, max(2, blksize)) for k, ia in enumerate(atmlst): shl0, shl1, p0, p1 = aoslices[ia] h1ao = hcore_deriv(ia) de[k] += numpy.einsum('xij,ij->x', h1ao, casci_dm1) de[k] += numpy.einsum('xij,ij->x', h1ao, zvec_ao) vhf1 = numpy.zeros((3, nao, nao)) q1 = 0 for b0, b1, nf in _shell_prange(mol, 0, mol.nbas, blksize): q0, q1 = q1, q1 + nf dm2_ao = lib.einsum('ijw,pi,qj->pqw', dm2buf, mo_cas[p0:p1], mo_cas[q0:q1]) shls_slice = (shl0, shl1, b0, b1, 0, mol.nbas, 0, mol.nbas) eri1 = mol.intor('int2e_ip1', comp=3, aosym='s2kl', shls_slice=shls_slice).reshape( 3, p1 - p0, nf, nao_pair) de[k] -= numpy.einsum('xijw,ijw->x', eri1, dm2_ao) * 2 for i in range(3): eri1tmp = lib.unpack_tril(eri1[i].reshape((p1 - p0) * nf, -1)) eri1tmp = eri1tmp.reshape(p1 - p0, nf, nao, nao) de[k, i] -= numpy.einsum('ijkl,ij,kl', eri1tmp, hf_dm1[p0:p1, q0:q1], zvec_ao) * 2 de[k, i] -= numpy.einsum('ijkl,kl,ij', eri1tmp, hf_dm1, zvec_ao[p0:p1, q0:q1]) * 2 de[k, i] += numpy.einsum('ijkl,il,kj', eri1tmp, hf_dm1[p0:p1], zvec_ao[q0:q1]) de[k, i] += numpy.einsum('ijkl,jk,il', eri1tmp, hf_dm1[q0:q1], zvec_ao[p0:p1]) #:vhf1c, vhf1a = mc_grad.get_veff(mol, (dm_core, dm_cas)) #:de[k] += numpy.einsum('xij,ij->x', vhf1c[:,p0:p1], casci_dm1[p0:p1]) * 2 #:de[k] += numpy.einsum('xij,ij->x', vhf1a[:,p0:p1], dm_core[p0:p1]) * 2 de[k, i] -= numpy.einsum('ijkl,lk,ij', eri1tmp, dm_core[q0:q1], casci_dm1[p0:p1]) * 2 de[k, i] += numpy.einsum('ijkl,jk,il', eri1tmp, dm_core[q0:q1], casci_dm1[p0:p1]) de[k, i] -= numpy.einsum('ijkl,lk,ij', eri1tmp, dm_cas[q0:q1], dm_core[p0:p1]) * 2 de[k, i] += numpy.einsum('ijkl,jk,il', eri1tmp, dm_cas[q0:q1], dm_core[p0:p1]) eri1 = eri1tmp = None de[k] -= numpy.einsum('xij,ij->x', s1[:, p0:p1], im1[p0:p1]) de[k] -= numpy.einsum('xij,ji->x', s1[:, p0:p1], im1[:, p0:p1]) de[k] -= numpy.einsum('xij,ij->x', s1[:, p0:p1], zeta[p0:p1]) * 2 de[k] -= numpy.einsum('xij,ji->x', s1[:, p0:p1], zeta[:, p0:p1]) * 2 de[k] -= numpy.einsum('xij,ij->x', s1[:, p0:p1], vhf_s1occ[p0:p1]) * 2 de[k] -= numpy.einsum('xij,ji->x', s1[:, p0:p1], vhf_s1occ[:, p0:p1]) * 2 log.timer('CASCI nuclear gradients', *time0) return de
def grad_elec(td_grad, x_y, singlet=True, atmlst=None, max_memory=2000, verbose=logger.INFO): ''' Electronic part of TDA, TDDFT nuclear gradients Args: td_grad : grad.tdrhf.Gradients or grad.tdrks.Gradients object. x_y : a two-element list of numpy arrays TDDFT X and Y amplitudes. If Y is set to 0, this function computes TDA energy gradients. ''' log = logger.new_logger(td_grad, verbose) time0 = time.clock(), time.time() mol = td_grad.mol mf = td_grad.base._scf mo_coeff = mf.mo_coeff mo_energy = mf.mo_energy mo_occ = mf.mo_occ nao, nmo = mo_coeff.shape nocc = (mo_occ>0).sum() nvir = nmo - nocc x, y = x_y xpy = (x+y).reshape(nocc,nvir).T xmy = (x-y).reshape(nocc,nvir).T orbv = mo_coeff[:,nocc:] orbo = mo_coeff[:,:nocc] dvv = numpy.einsum('ai,bi->ab', xpy, xpy) + numpy.einsum('ai,bi->ab', xmy, xmy) doo =-numpy.einsum('ai,aj->ij', xpy, xpy) - numpy.einsum('ai,aj->ij', xmy, xmy) dmxpy = reduce(numpy.dot, (orbv, xpy, orbo.T)) dmxmy = reduce(numpy.dot, (orbv, xmy, orbo.T)) dmzoo = reduce(numpy.dot, (orbo, doo, orbo.T)) dmzoo+= reduce(numpy.dot, (orbv, dvv, orbv.T)) mem_now = lib.current_memory()[0] max_memory = max(2000, td_grad.max_memory*.9-mem_now) ni = mf._numint ni.libxc.test_deriv_order(mf.xc, 3, raise_error=True) omega, alpha, hyb = ni.rsh_and_hybrid_coeff(mf.xc, mol.spin) # dm0 = mf.make_rdm1(mo_coeff, mo_occ), but it is not used when computing # fxc since rho0 is passed to fxc function. dm0 = None rho0, vxc, fxc = ni.cache_xc_kernel(mf.mol, mf.grids, mf.xc, [mo_coeff]*2, [mo_occ*.5]*2, spin=1) f1vo, f1oo, vxc1, k1ao = \ _contract_xc_kernel(td_grad, mf.xc, dmxpy, dmzoo, True, True, singlet, max_memory) if abs(hyb) > 1e-10: dm = (dmzoo, dmxpy+dmxpy.T, dmxmy-dmxmy.T) vj, vk = mf.get_jk(mol, dm, hermi=0) vk *= hyb if abs(omega) > 1e-10: vk += mf.get_k(mol, dm, hermi=0, omega=omega) * (alpha-hyb) veff0doo = vj[0] * 2 - vk[0] + f1oo[0] + k1ao[0] * 2 wvo = reduce(numpy.dot, (orbv.T, veff0doo, orbo)) * 2 if singlet: veff = vj[1] * 2 - vk[1] + f1vo[0] * 2 else: veff = -vk[1] + f1vo[0] * 2 veff0mop = reduce(numpy.dot, (mo_coeff.T, veff, mo_coeff)) wvo -= numpy.einsum('ki,ai->ak', veff0mop[:nocc,:nocc], xpy) * 2 wvo += numpy.einsum('ac,ai->ci', veff0mop[nocc:,nocc:], xpy) * 2 veff = -vk[2] veff0mom = reduce(numpy.dot, (mo_coeff.T, veff, mo_coeff)) wvo -= numpy.einsum('ki,ai->ak', veff0mom[:nocc,:nocc], xmy) * 2 wvo += numpy.einsum('ac,ai->ci', veff0mom[nocc:,nocc:], xmy) * 2 else: vj = mf.get_j(mol, (dmzoo, dmxpy+dmxpy.T), hermi=1) veff0doo = vj[0] * 2 + f1oo[0] + k1ao[0] * 2 wvo = reduce(numpy.dot, (orbv.T, veff0doo, orbo)) * 2 if singlet: veff = vj[1] * 2 + f1vo[0] * 2 else: veff = f1vo[0] * 2 veff0mop = reduce(numpy.dot, (mo_coeff.T, veff, mo_coeff)) wvo -= numpy.einsum('ki,ai->ak', veff0mop[:nocc,:nocc], xpy) * 2 wvo += numpy.einsum('ac,ai->ci', veff0mop[nocc:,nocc:], xpy) * 2 veff0mom = numpy.zeros((nmo,nmo)) # set singlet=None, generate function for CPHF type response kernel vresp = mf.gen_response(singlet=None, hermi=1) def fvind(x): dm = reduce(numpy.dot, (orbv, x.reshape(nvir,nocc)*2, orbo.T)) v1ao = vresp(dm+dm.T) return reduce(numpy.dot, (orbv.T, v1ao, orbo)).ravel() z1 = cphf.solve(fvind, mo_energy, mo_occ, wvo, max_cycle=td_grad.cphf_max_cycle, tol=td_grad.cphf_conv_tol)[0] z1 = z1.reshape(nvir,nocc) time1 = log.timer('Z-vector using CPHF solver', *time0) z1ao = reduce(numpy.dot, (orbv, z1, orbo.T)) veff = vresp(z1ao+z1ao.T) im0 = numpy.zeros((nmo,nmo)) im0[:nocc,:nocc] = reduce(numpy.dot, (orbo.T, veff0doo+veff, orbo)) im0[:nocc,:nocc]+= numpy.einsum('ak,ai->ki', veff0mop[nocc:,:nocc], xpy) im0[:nocc,:nocc]+= numpy.einsum('ak,ai->ki', veff0mom[nocc:,:nocc], xmy) im0[nocc:,nocc:] = numpy.einsum('ci,ai->ac', veff0mop[nocc:,:nocc], xpy) im0[nocc:,nocc:]+= numpy.einsum('ci,ai->ac', veff0mom[nocc:,:nocc], xmy) im0[nocc:,:nocc] = numpy.einsum('ki,ai->ak', veff0mop[:nocc,:nocc], xpy)*2 im0[nocc:,:nocc]+= numpy.einsum('ki,ai->ak', veff0mom[:nocc,:nocc], xmy)*2 zeta = lib.direct_sum('i+j->ij', mo_energy, mo_energy) * .5 zeta[nocc:,:nocc] = mo_energy[:nocc] zeta[:nocc,nocc:] = mo_energy[nocc:] dm1 = numpy.zeros((nmo,nmo)) dm1[:nocc,:nocc] = doo dm1[nocc:,nocc:] = dvv dm1[nocc:,:nocc] = z1 dm1[:nocc,:nocc] += numpy.eye(nocc)*2 # for ground state im0 = reduce(numpy.dot, (mo_coeff, im0+zeta*dm1, mo_coeff.T)) # Initialize hcore_deriv with the underlying SCF object because some # extensions (e.g. QM/MM, solvent) modifies the SCF object only. mf_grad = td_grad.base._scf.nuc_grad_method() hcore_deriv = mf_grad.hcore_generator(mol) s1 = mf_grad.get_ovlp(mol) dmz1doo = z1ao + dmzoo oo0 = reduce(numpy.dot, (orbo, orbo.T)) if abs(hyb) > 1e-10: dm = (oo0, dmz1doo+dmz1doo.T, dmxpy+dmxpy.T, dmxmy-dmxmy.T) vj, vk = td_grad.get_jk(mol, dm) vk *= hyb if abs(omega) > 1e-10: with mol.with_range_coulomb(omega): vk += td_grad.get_k(mol, dm) * (alpha-hyb) vj = vj.reshape(-1,3,nao,nao) vk = vk.reshape(-1,3,nao,nao) if singlet: veff1 = vj * 2 - vk else: veff1 = numpy.vstack((vj[:2]*2-vk[:2], -vk[2:])) else: vj = td_grad.get_j(mol, (oo0, dmz1doo+dmz1doo.T, dmxpy+dmxpy.T)) vj = vj.reshape(-1,3,nao,nao) veff1 = numpy.zeros((4,3,nao,nao)) if singlet: veff1[:3] = vj * 2 else: veff1[:2] = vj[:2] * 2 fxcz1 = _contract_xc_kernel(td_grad, mf.xc, z1ao, None, False, False, True, max_memory)[0] veff1[0] += vxc1[1:] veff1[1] +=(f1oo[1:] + fxcz1[1:] + k1ao[1:]*2)*2 # *2 for dmz1doo+dmz1oo.T veff1[2] += f1vo[1:] * 2 time1 = log.timer('2e AO integral derivatives', *time1) if atmlst is None: atmlst = range(mol.natm) offsetdic = mol.offset_nr_by_atom() de = numpy.zeros((len(atmlst),3)) for k, ia in enumerate(atmlst): shl0, shl1, p0, p1 = offsetdic[ia] # Ground state gradients h1ao = hcore_deriv(ia) h1ao[:,p0:p1] += veff1[0,:,p0:p1] h1ao[:,:,p0:p1] += veff1[0,:,p0:p1].transpose(0,2,1) # oo0*2 for doubly occupied orbitals e1 = numpy.einsum('xpq,pq->x', h1ao, oo0) * 2 e1 += numpy.einsum('xpq,pq->x', h1ao, dmz1doo) e1 -= numpy.einsum('xpq,pq->x', s1[:,p0:p1], im0[p0:p1]) e1 -= numpy.einsum('xqp,pq->x', s1[:,p0:p1], im0[:,p0:p1]) e1 += numpy.einsum('xij,ij->x', veff1[1,:,p0:p1], oo0[p0:p1]) e1 += numpy.einsum('xij,ij->x', veff1[2,:,p0:p1], dmxpy[p0:p1,:]) * 2 e1 += numpy.einsum('xij,ij->x', veff1[3,:,p0:p1], dmxmy[p0:p1,:]) * 2 e1 += numpy.einsum('xji,ij->x', veff1[2,:,p0:p1], dmxpy[:,p0:p1]) * 2 e1 -= numpy.einsum('xji,ij->x', veff1[3,:,p0:p1], dmxmy[:,p0:p1]) * 2 de[k] = e1 log.timer('TDDFT nuclear gradients', *time0) return de
def _ao2mo_ovov(mp, orbo, orbv, feri, max_memory=2000, verbose=None): time0 = (time.clock(), time.time()) log = logger.new_logger(mp, verbose) mol = mp.mol int2e = mol._add_suffix('int2e') ao2mopt = _ao2mo.AO2MOpt(mol, int2e, 'CVHFnr_schwarz_cond', 'CVHFsetnr_direct_scf') nao, nocc = orbo.shape nvir = orbv.shape[1] nbas = mol.nbas assert (nvir <= nao) ao_loc = mol.ao_loc_nr() dmax = max( 4, min(nao / 3, numpy.sqrt(max_memory * .95e6 / 8 / (nao + nocc)**2))) sh_ranges = ao2mo.outcore.balance_partition(ao_loc, dmax) dmax = max(x[2] for x in sh_ranges) eribuf = numpy.empty((nao, dmax, dmax, nao)) ftmp = lib.H5TmpFile() log.debug('max_memory %s MB (dmax = %s) required disk space %g MB', max_memory, dmax, nocc**2 * (nao * (nao + dmax) / 2 + nvir**2) * 8 / 1e6) buf_i = numpy.empty((nocc * dmax**2 * nao)) buf_li = numpy.empty((nocc**2 * dmax**2)) buf1 = numpy.empty_like(buf_li) fint = gto.moleintor.getints4c jk_blk_slices = [] count = 0 time1 = time0 with lib.call_in_background(ftmp.__setitem__) as save: for ip, (ish0, ish1, ni) in enumerate(sh_ranges): for jsh0, jsh1, nj in sh_ranges[:ip + 1]: i0, i1 = ao_loc[ish0], ao_loc[ish1] j0, j1 = ao_loc[jsh0], ao_loc[jsh1] jk_blk_slices.append((i0, i1, j0, j1)) eri = fint(int2e, mol._atm, mol._bas, mol._env, shls_slice=(0, nbas, ish0, ish1, jsh0, jsh1, 0, nbas), aosym='s1', ao_loc=ao_loc, cintopt=ao2mopt._cintopt, out=eribuf) tmp_i = numpy.ndarray((nocc, (i1 - i0) * (j1 - j0) * nao), buffer=buf_i) tmp_li = numpy.ndarray((nocc, nocc * (i1 - i0) * (j1 - j0)), buffer=buf_li) lib.ddot(orbo.T, eri.reshape(nao, (i1 - i0) * (j1 - j0) * nao), c=tmp_i) lib.ddot(orbo.T, tmp_i.reshape(nocc * (i1 - i0) * (j1 - j0), nao).T, c=tmp_li) tmp_li = tmp_li.reshape(nocc, nocc, (i1 - i0), (j1 - j0)) save(str(count), tmp_li.transpose(1, 0, 2, 3)) buf_li, buf1 = buf1, buf_li count += 1 time1 = log.timer_debug1( 'partial ao2mo [%d:%d,%d:%d]' % (ish0, ish1, jsh0, jsh1), *time1) time1 = time0 = log.timer('mp2 ao2mo_ovov pass1', *time0) eri = eribuf = tmp_i = tmp_li = buf_i = buf_li = buf1 = None h5dat = feri.create_dataset('ovov', (nocc * nvir, nocc * nvir), 'f8', chunks=(nvir, nvir)) occblk = int( min(nocc, max(4, 250 / nocc, max_memory * .9e6 / 8 / (nao**2 * nocc) / 5))) def load(i0, eri): if i0 < nocc: i1 = min(i0 + occblk, nocc) for k, (p0, p1, q0, q1) in enumerate(jk_blk_slices): eri[:i1 - i0, :, p0:p1, q0:q1] = ftmp[str(k)][i0:i1] if p0 != q0: dat = numpy.asarray(ftmp[str(k)][:, i0:i1]) eri[:i1 - i0, :, q0:q1, p0:p1] = dat.transpose(1, 0, 3, 2) def save(i0, i1, dat): for i in range(i0, i1): h5dat[i * nvir:(i + 1) * nvir] = dat[i - i0].reshape( nvir, nocc * nvir) orbv = numpy.asarray(orbv, order='F') buf_prefecth = numpy.empty((occblk, nocc, nao, nao)) buf = numpy.empty_like(buf_prefecth) bufw = numpy.empty((occblk * nocc, nvir**2)) bufw1 = numpy.empty_like(bufw) with lib.call_in_background(load) as prefetch: with lib.call_in_background(save) as bsave: load(0, buf_prefecth) for i0, i1 in lib.prange(0, nocc, occblk): buf, buf_prefecth = buf_prefecth, buf prefetch(i1, buf_prefecth) eri = buf[:i1 - i0].reshape((i1 - i0) * nocc, nao, nao) dat = _ao2mo.nr_e2(eri, orbv, (0, nvir, 0, nvir), 's1', 's1', out=bufw) bsave( i0, i1, dat.reshape(i1 - i0, nocc, nvir, nvir).transpose(0, 2, 1, 3)) bufw, bufw1 = bufw1, bufw time1 = log.timer_debug1('pass2 ao2mo [%d:%d]' % (i0, i1), *time1) time0 = log.timer('mp2 ao2mo_ovov pass2', *time0) return h5dat
def get_nto(tdobj, state=1, threshold=OUTPUT_THRESHOLD, verbose=None): r''' Natural transition orbital analysis. The natural transition density matrix between ground state and excited state :math:`Tia = \langle \Psi_{ex} | i a^\dagger | \Psi_0 \rangle` can be transformed to diagonal form through SVD :math:`T = O \sqrt{\lambda} V^\dagger`. O and V are occupied and virtual natural transition orbitals. The diagonal elements :math:`\lambda` are the weights of the occupied-virtual orbital pair in the excitation. Ref: Martin, R. L., JCP, 118, 4775-4777 Note in the TDHF/TDDFT calculations, the excitation part (X) is interpreted as the CIS coefficients and normalized to 1. The de-excitation part (Y) is ignored. Args: tdobj : TDA, or TDHF, or TDDFT object state : int Excited state ID. state = 1 means the first excited state. If state < 0, state ID is counted from the last excited state. Kwargs: threshold : float Above which the NTO coefficients will be printed in the output. Returns: A list (weights, NTOs). NTOs are natural orbitals represented in AO basis. The first N_occ NTOs are occupied NTOs and the rest are virtual NTOs. ''' if state == 0: logger.warn( tdobj, 'Excited state starts from 1. ' 'Set state=1 for first excited state.') state_id = state elif state < 0: state_id = state else: state_id = state - 1 mol = tdobj.mol mo_coeff = tdobj._scf.mo_coeff mo_occ = tdobj._scf.mo_occ orbo = mo_coeff[:, mo_occ == 2] orbv = mo_coeff[:, mo_occ == 0] nocc = orbo.shape[1] nvir = orbv.shape[1] cis_t1 = tdobj.xy[state_id][0] # TDDFT (X,Y) has X^2-Y^2=1. # Renormalizing X (X^2=1) to map it to CIS coefficients cis_t1 *= 1. / numpy.linalg.norm(cis_t1) # TODO: Comparing to the NTOs defined in JCP, 142, 244103. JCP, 142, 244103 # provides a method to incorporate the Y matrix in the transition density # matrix. However, it may break the point group symmetry of the NTO orbitals # when the system has degenerated irreducible representations. if mol.symmetry: orbsym = hf_symm.get_orbsym(mol, mo_coeff) orbsym_in_d2h = numpy.asarray(orbsym) % 10 # convert to D2h irreps o_sym = orbsym_in_d2h[mo_occ == 2] v_sym = orbsym_in_d2h[mo_occ == 0] nto_o = numpy.eye(nocc) nto_v = numpy.eye(nvir) weights_o = numpy.zeros(nocc) weights_v = numpy.zeros(nvir) for ir in set(orbsym_in_d2h): idx = numpy.where(o_sym == ir)[0] if idx.size > 0: dm_oo = numpy.dot(cis_t1[idx], cis_t1[idx].T) weights_o[idx], nto_o[idx[:, None], idx] = numpy.linalg.eigh(dm_oo) idx = numpy.where(v_sym == ir)[0] if idx.size > 0: dm_vv = numpy.dot(cis_t1[:, idx].T, cis_t1[:, idx]) weights_v[idx], nto_v[idx[:, None], idx] = numpy.linalg.eigh(dm_vv) # weights in descending order idx = numpy.argsort(-weights_o) weights_o = weights_o[idx] nto_o = nto_o[:, idx] o_sym = o_sym[idx] idx = numpy.argsort(-weights_v) weights_v = weights_v[idx] nto_v = nto_v[:, idx] v_sym = v_sym[idx] nto_orbsym = numpy.hstack((o_sym, v_sym)) if nocc < nvir: weights = weights_o else: weights = weights_v else: nto_o, w, nto_vT = numpy.linalg.svd(cis_t1) nto_v = nto_vT.conj().T weights = w**2 nto_orbsym = None idx = numpy.argmax(abs(nto_o.real), axis=0) nto_o[:, nto_o[idx, numpy.arange(nocc)].real < 0] *= -1 idx = numpy.argmax(abs(nto_v.real), axis=0) nto_v[:, nto_v[idx, numpy.arange(nvir)].real < 0] *= -1 occupied_nto = numpy.dot(orbo, nto_o) virtual_nto = numpy.dot(orbv, nto_v) nto_coeff = numpy.hstack((occupied_nto, virtual_nto)) if mol.symmetry: nto_coeff = lib.tag_array(nto_coeff, orbsym=nto_orbsym) log = logger.new_logger(tdobj, verbose) if log.verbose >= logger.INFO: log.info('State %d: %g eV NTO largest component %s', state_id + 1, tdobj.e[state_id] * nist.HARTREE2EV, weights[0]) o_idx = numpy.where(abs(nto_o[:, 0]) > threshold)[0] v_idx = numpy.where(abs(nto_v[:, 0]) > threshold)[0] fmt = '%' + str(lib.param.OUTPUT_DIGITS) + 'f (MO #%d)' log.info(' occ-NTO: ' + ' + '.join([(fmt % (nto_o[i, 0], i + MO_BASE)) for i in o_idx])) log.info(' vir-NTO: ' + ' + '.join([(fmt % (nto_v[i, 0], i + MO_BASE + nocc)) for i in v_idx])) return weights, nto_coeff
def rotate_orb_cc(mf, mo_coeff, mo_occ, fock_ao, h1e, conv_tol_grad=None, max_stepsize=None, verbose=None): log = logger.new_logger(mf, verbose) if conv_tol_grad is None: conv_tol_grad = numpy.sqrt(mf.conv_tol*.1) #TODO: dynamically adjust max_stepsize, as done in mc1step.py t2m = (time.clock(), time.time()) g_orb, h_op, h_diag = mf.gen_g_hop(mo_coeff, mo_occ, fock_ao) g_kf = g_orb norm_gkf = norm_gorb = numpy.linalg.norm(g_orb) log.debug(' |g|= %4.3g (keyframe)', norm_gorb) t3m = log.timer('gen h_op', *t2m) def precond(x, e): hdiagd = h_diag-(e-mf.ah_level_shift) hdiagd[abs(hdiagd)<1e-8] = 1e-8 x = x/hdiagd ## Because of DFT, donot norm to 1 which leads 1st DM too large. # norm_x = numpy.linalg.norm(x) # if norm_x < 1e-2: # x *= 1e-2/norm_x return x g_op = lambda: g_orb x0_guess = g_orb kf_trust_region = mf.kf_trust_region while True: ah_conv_tol = min(norm_gorb**2, mf.ah_conv_tol) # increase the AH accuracy when approach convergence #ah_start_cycle = max(mf.ah_start_cycle, int(-numpy.log10(norm_gorb))) ah_start_cycle = mf.ah_start_cycle imic = 0 dr = 0 ukf = None jkcount = 0 kfcount = 0 ikf = 0 dm0 = mf.make_rdm1(mo_coeff, mo_occ) # NOTE: vhf0 cannot be computed as (fock_ao - h1e) because mf.get_fock # may be overloaded and fock_ao != h1e + vhf0 vhf0 = mf._scf.get_veff(mf._scf.mol, dm0) for ah_end, ihop, w, dxi, hdxi, residual, seig \ in ciah.davidson_cc(h_op, g_op, precond, x0_guess, tol=ah_conv_tol, max_cycle=mf.ah_max_cycle, lindep=mf.ah_lindep, verbose=log): norm_residual = numpy.linalg.norm(residual) ah_start_tol = min(norm_gorb*5, mf.ah_start_tol) if (ah_end or ihop == mf.ah_max_cycle or # make sure to use the last step ((norm_residual < ah_start_tol) and (ihop >= ah_start_cycle)) or (seig < mf.ah_lindep)): imic += 1 dxmax = numpy.max(abs(dxi)) if dxmax > mf.max_stepsize: scale = mf.max_stepsize / dxmax log.debug1('... scale rotation size %g', scale) dxi *= scale hdxi *= scale else: scale = None dr = dr + dxi g_orb = g_orb + hdxi norm_dr = numpy.linalg.norm(dr) norm_gorb = numpy.linalg.norm(g_orb) norm_dxi = numpy.linalg.norm(dxi) log.debug(' imic %d(%d) |g|= %4.3g |dxi|= %4.3g ' 'max(|x|)= %4.3g |dr|= %4.3g eig= %4.3g seig= %4.3g', imic, ihop, norm_gorb, norm_dxi, dxmax, norm_dr, w, seig) max_cycle = max(mf.max_cycle_inner, mf.max_cycle_inner-int(numpy.log(norm_gkf+1e-9)*2)) log.debug1('Set ah_start_tol %g, ah_start_cycle %d, max_cycle %d', ah_start_tol, ah_start_cycle, max_cycle) ikf += 1 if imic > 3 and norm_gorb > norm_gkf*mf.ah_grad_trust_region: g_orb = g_orb - hdxi dr -= dxi norm_gorb = numpy.linalg.norm(g_orb) log.debug('|g| >> keyframe, Restore previouse step') break elif (imic >= max_cycle or norm_gorb < conv_tol_grad*.5): break elif (ikf > 2 and # avoid frequent keyframe #TODO: replace it with keyframe_scheduler (ikf >= max(mf.kf_interval, mf.kf_interval-numpy.log(norm_dr+1e-9)) or # Insert keyframe if the keyframe and the esitimated g_orb are too different norm_gorb < norm_gkf/kf_trust_region)): ikf = 0 u = mf.update_rotate_matrix(dr, mo_occ, mo_coeff=mo_coeff) if ukf is not None: u = mf.rotate_mo(ukf, u) ukf = u dr[:] = 0 mo1 = mf.rotate_mo(mo_coeff, u) dm = mf.make_rdm1(mo1, mo_occ) # use mf._scf.get_veff to avoid density-fit mf polluting get_veff vhf0 = mf._scf.get_veff(mf._scf.mol, dm, dm_last=dm0, vhf_last=vhf0) kfcount += 1 dm0 = dm # Use API to compute fock instead of "fock=h1e+vhf0". This is because get_fock # is the hook being overloaded in many places. fock = mf.get_fock(h1e, vhf=vhf0) g_kf1 = mf.get_grad(mo1, mo_occ, fock) norm_gkf1 = numpy.linalg.norm(g_kf1) norm_dg = numpy.linalg.norm(g_kf1-g_orb) jkcount += 1 log.debug('Adjust keyframe g_orb to |g|= %4.3g ' '|g-correction|= %4.3g', norm_gkf1, norm_dg) if (norm_dg < norm_gorb*mf.ah_grad_trust_region # kf not too diff #or norm_gkf1 < norm_gkf # grad is decaying # close to solution or norm_gkf1 < conv_tol_grad*mf.ah_grad_trust_region): kf_trust_region = min(max(norm_gorb/(norm_dg+1e-9), mf.kf_trust_region), 10) log.debug1('Set kf_trust_region = %g', kf_trust_region) g_orb = g_kf = g_kf1 norm_gorb = norm_gkf = norm_gkf1 else: g_orb = g_orb - hdxi dr -= dxi norm_gorb = numpy.linalg.norm(g_orb) log.debug('Out of trust region. Restore previouse step') break u = mf.update_rotate_matrix(dr, mo_occ, mo_coeff=mo_coeff) if ukf is not None: u = mf.rotate_mo(ukf, u) jkcount += ihop + 1 log.debug(' tot inner=%d %d JK |g|= %4.3g |u-1|= %4.3g', imic, jkcount, norm_gorb, numpy.linalg.norm(dr)) h_op = h_diag = None t3m = log.timer('aug_hess in %d inner iters' % imic, *t3m) mo_coeff, mo_occ, fock_ao = (yield u, g_kf, kfcount, jkcount) g_kf, h_op, h_diag = mf.gen_g_hop(mo_coeff, mo_occ, fock_ao) norm_gkf = numpy.linalg.norm(g_kf) norm_dg = numpy.linalg.norm(g_kf-g_orb) log.debug(' |g|= %4.3g (keyframe), |g-correction|= %4.3g', norm_gkf, norm_dg) kf_trust_region = min(max(norm_gorb/(norm_dg+1e-9), mf.kf_trust_region), 10) log.debug1('Set kf_trust_region = %g', kf_trust_region) g_orb = g_kf norm_gorb = norm_gkf if norm_dxi != 0: x0_guess = dxi else: x0_guess = g_kf
def kernel(self, mo_coeff=None, ci0=None, verbose=None): with_solvent = self.with_solvent log = logger.new_logger(self) log.info( '\n** Self-consistently update the solvent effects for %s **', oldCAS) log1 = copy.copy(log) log1.verbose -= 1 # Suppress a few output messages def casci_iter_(ci0, log): # self.e_tot, self.e_cas, and self.ci are updated in the call # to oldCAS.kernel e_tot, e_cas, ci0 = oldCAS.kernel(self, mo_coeff, ci0, log)[:3] if isinstance(self.e_cas, (float, numpy.number)): dm = self.make_rdm1(ci=ci0) else: log.debug('Computing solvent responses to DM of state %d', with_solvent.state_id) dm = self.make_rdm1(ci=ci0[with_solvent.state_id]) if with_solvent.e is not None: edup = numpy.einsum('ij,ji->', with_solvent.v, dm) self.e_tot += with_solvent.e - edup if not with_solvent.frozen: with_solvent.e, with_solvent.v = with_solvent.kernel(dm) return self.e_tot, e_cas, ci0 if with_solvent.frozen: with lib.temporary_env(self, _finalize=lambda: None): casci_iter_(ci0, log) log.note('Total energy with solvent effects') self._finalize() return self.e_tot, self.e_cas, self.ci, self.mo_coeff, self.mo_energy self.converged = False with lib.temporary_env(self, canonicalization=False): e_tot = e_last = 0 for cycle in range(self.with_solvent.max_cycle): log.info('\n** Solvent self-consistent cycle %d:', cycle) e_tot, e_cas, ci0 = casci_iter_(ci0, log1) de = e_tot - e_last if isinstance(e_cas, (float, numpy.number)): log.info( 'Sovlent cycle %d E(CASCI+solvent) = %.15g ' 'dE = %g', cycle, e_tot, de) else: for i, e in enumerate(e_tot): log.info( 'Solvent cycle %d CASCI root %d ' 'E(CASCI+solvent) = %.15g dE = %g', cycle, i, e, de[i]) if abs(e_tot - e_last).max() < with_solvent.conv_tol: self.converged = True break e_last = e_tot # An extra cycle to canonicalize CASCI orbitals with lib.temporary_env(self, _finalize=lambda: None): casci_iter_(ci0, log) if self.converged: log.info('self-consistent CASCI+solvent converged') else: log.info('self-consistent CASCI+solvent not converged') log.note('Total energy with solvent effects') self._finalize() return self.e_tot, self.e_cas, self.ci, self.mo_coeff, self.mo_energy
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, RANGE_TYPE)) 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, (list, tuple, RANGE_TYPE)): 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 grad_elec(mc_grad, mo_coeff=None, ci=None, atmlst=None, verbose=None): mc = mc_grad.base if mo_coeff is None: mo_coeff = mc.mo_coeff if ci is None: ci = mc.ci if mc.frozen is not None: raise NotImplementedError time0 = time.clock(), time.time() log = logger.new_logger(mc_grad, verbose) mol = mc_grad.mol ncore = mc.ncore ncas = mc.ncas nocc = ncore + ncas nelecas = mc.nelecas nao, nmo = mo_coeff.shape nao_pair = nao * (nao + 1) // 2 mo_occ = mo_coeff[:, :nocc] mo_core = mo_coeff[:, :ncore] mo_cas = mo_coeff[:, ncore:nocc] casdm1, casdm2 = mc.fcisolver.make_rdm12(ci, ncas, nelecas) # gfock = Generalized Fock, Adv. Chem. Phys., 69, 63 dm_core = numpy.dot(mo_core, mo_core.T) * 2 dm_cas = reduce(numpy.dot, (mo_cas, casdm1, mo_cas.T)) # MRH flag: this is one of my kludges # It would be better to just pass the ERIS object used in orbital optimization # But I am too lazy at the moment aapa = ao2mo.kernel(mol, (mo_cas, mo_cas, mo_coeff, mo_cas), compact=False) aapa = aapa.reshape(ncas, ncas, nmo, ncas) vj, vk = mc._scf.get_jk(mol, (dm_core, dm_cas)) h1 = mc.get_hcore() vhf_c = vj[0] - vk[0] * .5 vhf_a = vj[1] - vk[1] * .5 gfock = numpy.zeros((nmo, nmo)) gfock[:, :ncore] = reduce(numpy.dot, (mo_coeff.T, h1 + vhf_c + vhf_a, mo_core)) * 2 gfock[:, ncore:nocc] = reduce(numpy.dot, (mo_coeff.T, h1 + vhf_c, mo_cas, casdm1)) gfock[:, ncore:nocc] += numpy.einsum('uviw,vuwt->it', aapa, casdm2) dme0 = reduce(numpy.dot, (mo_coeff, (gfock + gfock.T) * .5, mo_coeff.T)) aapa = vj = vk = vhf_c = vhf_a = h1 = gfock = None dm1 = dm_core + dm_cas vj, vk = mc_grad.get_jk(mol, (dm_core, dm_cas)) vhf1c, vhf1a = vj - vk * .5 hcore_deriv = mc_grad.hcore_generator(mol) s1 = mc_grad.get_ovlp(mol) diag_idx = numpy.arange(nao) diag_idx = diag_idx * (diag_idx + 1) // 2 + diag_idx casdm2_cc = casdm2 + casdm2.transpose(0, 1, 3, 2) dm2buf = ao2mo._ao2mo.nr_e2(casdm2_cc.reshape(ncas**2, ncas**2), mo_cas.T, (0, nao, 0, nao)).reshape(ncas**2, nao, nao) dm2buf = lib.pack_tril(dm2buf) dm2buf[:, diag_idx] *= .5 dm2buf = dm2buf.reshape(ncas, ncas, nao_pair) casdm2 = casdm2_cc = None if atmlst is None: atmlst = range(mol.natm) aoslices = mol.aoslice_by_atom() de = numpy.zeros((len(atmlst), 3)) max_memory = mc_grad.max_memory - lib.current_memory()[0] # MRH: this originally implied that the memory footprint would be max(p1-p0) * max(q1-q0) * nao_pair # In fact, that's the size of dm2_ao AND EACH COMPONENT of the differentiated eris # So the actual memory footprint is 4 times that! blksize = int(max_memory * .9e6 / 8 / (4 * (aoslices[:, 3] - aoslices[:, 2]).max() * nao_pair)) blksize = min(nao, max(2, blksize)) for k, ia in enumerate(atmlst): shl0, shl1, p0, p1 = aoslices[ia] h1ao = hcore_deriv(ia) de[k] += numpy.einsum('xij,ij->x', h1ao, dm1) de[k] -= numpy.einsum('xij,ij->x', s1[:, p0:p1], dme0[p0:p1]) * 2 q1 = 0 for b0, b1, nf in _shell_prange(mol, 0, mol.nbas, blksize): q0, q1 = q1, q1 + nf dm2_ao = lib.einsum('ijw,pi,qj->pqw', dm2buf, mo_cas[p0:p1], mo_cas[q0:q1]) shls_slice = (shl0, shl1, b0, b1, 0, mol.nbas, 0, mol.nbas) eri1 = mol.intor('int2e_ip1', comp=3, aosym='s2kl', shls_slice=shls_slice).reshape( 3, p1 - p0, nf, nao_pair) de[k] -= numpy.einsum('xijw,ijw->x', eri1, dm2_ao) * 2 eri1 = None de[k] += numpy.einsum('xij,ij->x', vhf1c[:, p0:p1], dm1[p0:p1]) * 2 de[k] += numpy.einsum('xij,ij->x', vhf1a[:, p0:p1], dm_core[p0:p1]) * 2 log.timer('CASSCF nuclear gradients', *time0) return de
def canonicalize(mc, mo_coeff=None, ci=None, eris=None, sort=False, cas_natorb=False, casdm1=None, verbose=logger.NOTE, with_meta_lowdin=WITH_META_LOWDIN): '''Canonicalized CASCI/CASSCF orbitals of effecitve Fock matrix. Effective Fock matrix is built with one-particle density matrix (see also :func:`mcscf.casci.get_fock`). For state-average CASCI/CASSCF object, the canonicalized orbitals are based on the state-average density matrix. To obtain canonicalized orbitals for an individual state, you need to pass "casdm1" of the specific state to this function. Args: mc: a CASSCF/CASCI object or RHF object Kwargs: mo_coeff (ndarray): orbitals that span the core, active and external space. ci (ndarray): CI coefficients (or objects to represent the CI wavefunctions in DMRG/QMC-MCSCF calculations). eris: Integrals for the MCSCF object. Input this object to reduce the overhead of computing integrals. It can be generated by :func:`mc.ao2mo` method. sort (bool): Whether the canonicalized orbitals are sorted based on orbital energy (diagonal part of the effective Fock matrix) within each subspace (core, active, external). If the point group symmetry is not available in the system, the orbitals are always sorted. When the point group symmetry is available, sort=False will keep the symmetry label of input orbitals and only sort the orbitals in each symmetry block while sort=True will reorder all orbitals in each subspace and the symmetry labels may be changed. cas_natorb (bool): Whether to transform the active orbitals to natual orbitals casdm1 (ndarray): 1-particle density matrix in active space. This density matrix is used to build effective fock matrix. Without input casdm1, the density matrix is computed with the input ci coefficients/object. If neither ci nor casdm1 were given, density matrix is computed by :func:`mc.fcisolver.make_rdm1` method. For state-average CASCI/CASCF calculation, this results in a set of canonicalized orbitals of state-average effective Fock matrix. To canonicalize the orbitals for one particular state, you can assign the density matrix of that state to the kwarg casdm1. Returns: A tuple, (natural orbitals, CI coefficients, orbital energies) The orbital energies are the diagonal terms of effective Fock matrix. ''' from pyscf.lo import orth from pyscf.tools import dump_mat from pyscf.mcscf import addons log = logger.new_logger(mc, verbose) if mo_coeff is None: mo_coeff = mc.mo_coeff if ci is None: ci = mc.ci if casdm1 is None: if (isinstance(ci, (list, tuple)) and not isinstance(mc.fcisolver, addons.StateAverageFCISolver)): log.warn('Mulitple states found in CASCI solver. First state is ' 'used to compute the natural orbitals in active space.') casdm1 = mc.fcisolver.make_rdm1(ci[0], mc.ncas, mc.nelecas) else: casdm1 = mc.fcisolver.make_rdm1(ci, mc.ncas, mc.nelecas) ncore = mc.ncore nocc = ncore + mc.ncas nmo = mo_coeff.shape[1] fock_ao = mc.get_fock(mo_coeff, ci, eris, casdm1, verbose) if cas_natorb: mo_coeff1, ci, occ = mc.cas_natorb(mo_coeff, ci, eris, sort, casdm1, verbose, with_meta_lowdin) else: # Keep the active space unchanged by default. The rotation in active space # may cause problem for external CI solver eg DMRG. mo_coeff1 = mo_coeff.copy() log.info('Density matrix diagonal elements %s', casdm1.diagonal()) fock = reduce(numpy.dot, (mo_coeff1.T, fock_ao, mo_coeff1)) mo_energy = fock.diagonal().copy() mask = numpy.ones(nmo, dtype=bool) frozen = getattr(mc, 'frozen', None) if frozen is not None: if isinstance(frozen, (int, numpy.integer)): mask[:frozen] = False else: mask[frozen] = False core_idx = numpy.where(mask[:ncore])[0] vir_idx = numpy.where(mask[nocc:])[0] + nocc if hasattr(mo_coeff, 'orbsym'): orbsym = mo_coeff.orbsym else: orbsym = numpy.zeros(nmo, dtype=int) if len(core_idx) > 0: # note the last two args of ._eig for mc1step_symm # mc._eig function is called to handle symmetry adapated fock w, c1 = mc._eig(fock[core_idx[:, None], core_idx], 0, ncore, orbsym[core_idx]) if sort: idx = numpy.argsort(w.round(9), kind='mergesort') w = w[idx] c1 = c1[:, idx] orbsym[core_idx] = orbsym[core_idx][idx] mo_coeff1[:, core_idx] = numpy.dot(mo_coeff1[:, core_idx], c1) mo_energy[core_idx] = w if len(vir_idx) > 0: w, c1 = mc._eig(fock[vir_idx[:, None], vir_idx], nocc, nmo, orbsym[vir_idx]) if sort: idx = numpy.argsort(w.round(9), kind='mergesort') w = w[idx] c1 = c1[:, idx] orbsym[vir_idx] = orbsym[vir_idx][idx] mo_coeff1[:, vir_idx] = numpy.dot(mo_coeff1[:, vir_idx], c1) mo_energy[vir_idx] = w if hasattr(mo_coeff, 'orbsym'): mo_coeff1 = lib.tag_array(mo_coeff1, orbsym=orbsym) if log.verbose >= logger.DEBUG: for i in range(nmo): log.debug('i = %d <i|F|i> = %12.8f', i + 1, mo_energy[i]) # still return ci coefficients, in case the canonicalization funciton changed # cas orbitals, the ci coefficients should also be updated. return mo_coeff1, ci, mo_energy
def kernel_ms0(fci, h1e, eri, norb, nelec, ci0=None, link_index=None, tol=None, lindep=None, max_cycle=None, max_space=None, nroots=None, davidson_only=None, pspace_size=None, max_memory=None, verbose=None, ecore=0, **kwargs): if nroots is None: nroots = fci.nroots if davidson_only is None: davidson_only = fci.davidson_only if pspace_size is None: pspace_size = fci.pspace_size if max_memory is None: max_memory = fci.max_memory - lib.current_memory()[0] log = logger.new_logger(fci, verbose) assert (fci.spin is None or fci.spin == 0) assert (0 <= numpy.sum(nelec) <= norb * 2) link_index = _unpack(norb, nelec, link_index) h1e = numpy.ascontiguousarray(h1e) eri = numpy.ascontiguousarray(eri) na = link_index.shape[0] if max_memory < na**2 * 6 * 8e-6: log.warn( 'Not enough memory for FCI solver. ' 'The minimal requirement is %.0f MB', na**2 * 60e-6) hdiag = fci.make_hdiag(h1e, eri, norb, nelec) nroots = min(hdiag.size, nroots) try: addr, h0 = fci.pspace(h1e, eri, norb, nelec, hdiag, max(pspace_size, nroots)) if pspace_size > 0: pw, pv = fci.eig(h0) else: pw = pv = None if pspace_size >= na * na and ci0 is None and not davidson_only: # The degenerated wfn can break symmetry. The davidson iteration with proper # initial guess doesn't have this issue if na * na == 1: return pw[0] + ecore, pv[:, 0].reshape(1, 1) elif nroots > 1: civec = numpy.empty((nroots, na * na)) civec[:, addr] = pv[:, :nroots].T civec = civec.reshape(nroots, na, na) try: return pw[:nroots] + ecore, [_check_(ci) for ci in civec] except ValueError: pass elif abs(pw[0] - pw[1]) > 1e-12: civec = numpy.empty((na * na)) civec[addr] = pv[:, 0] civec = civec.reshape(na, na) civec = lib.transpose_sum(civec) * .5 # direct diagonalization may lead to triplet ground state ##TODO: optimize initial guess. Using pspace vector as initial guess may have ## spin problems. The 'ground state' of psapce vector may have different spin ## state to the true ground state. try: return pw[0] + ecore, _check_(civec.reshape(na, na)) except ValueError: pass except NotImplementedError: addr = [0] pw = pv = None precond = fci.make_precond(hdiag, pw, pv, addr) h2e = fci.absorb_h1e(h1e, eri, norb, nelec, .5) def hop(c): hc = fci.contract_2e(h2e, c.reshape(na, na), norb, nelec, link_index) return hc.ravel() #TODO: check spin of initial guess if ci0 is None: if callable(getattr(fci, 'get_init_guess', None)): ci0 = lambda: fci.get_init_guess(norb, nelec, nroots, hdiag) else: def ci0(): x0 = [] for i in range(nroots): x = numpy.zeros((na, na)) addra = addr[i] // na addrb = addr[i] % na if addra == addrb: x[addra, addrb] = 1 else: x[addra, addrb] = x[addrb, addra] = numpy.sqrt(.5) x0.append(x.ravel()) return x0 elif not callable(ci0): if isinstance(ci0, numpy.ndarray) and ci0.size == na * na: ci0 = [ci0.ravel()] else: ci0 = [x.ravel() for x in ci0] if tol is None: tol = fci.conv_tol if lindep is None: lindep = fci.lindep if max_cycle is None: max_cycle = fci.max_cycle if max_space is None: max_space = fci.max_space tol_residual = getattr(fci, 'conv_tol_residual', None) with lib.with_omp_threads(fci.threads): #e, c = lib.davidson(hop, ci0, precond, tol=fci.conv_tol, lindep=fci.lindep) e, c = fci.eig(hop, ci0, precond, tol=tol, lindep=lindep, max_cycle=max_cycle, max_space=max_space, nroots=nroots, max_memory=max_memory, verbose=log, follow_state=True, tol_residual=tol_residual, **kwargs) if nroots > 1: return e + ecore, [_check_(ci.reshape(na, na)) for ci in c] else: return e + ecore, _check_(c.reshape(na, na))
def grad_elec(cc_grad, t1=None, t2=None, l1=None, l2=None, eris=None, atmlst=None, d1=None, d2=None, verbose=logger.INFO): mycc = cc_grad.base if eris is not None: if (abs(eris.focka - numpy.diag(eris.focka.diagonal())).max() > 1e-3 or abs(eris.fockb - numpy.diag(eris.fockb.diagonal())).max() > 1e-3): raise RuntimeError( 'UCCSD gradients does not support NHF (non-canonical HF)') if t1 is None: t1 = mycc.t1 if t2 is None: t2 = mycc.t2 if l1 is None: l1 = mycc.l1 if l2 is None: l2 = mycc.l2 log = logger.new_logger(mycc, verbose) time0 = time.clock(), time.time() log.debug('Build uccsd rdm1 intermediates') if d1 is None: d1 = uccsd_rdm._gamma1_intermediates(mycc, t1, t2, l1, l2) time1 = log.timer_debug1('rdm1 intermediates', *time0) log.debug('Build uccsd rdm2 intermediates') fdm2 = lib.H5TmpFile() if d2 is None: d2 = uccsd_rdm._gamma2_outcore(mycc, t1, t2, l1, l2, fdm2, True) time1 = log.timer_debug1('rdm2 intermediates', *time1) mol = cc_grad.mol mo_a, mo_b = mycc.mo_coeff mo_ea, mo_eb = mycc._scf.mo_energy nao, nmoa = mo_a.shape nmob = mo_b.shape[1] nocca = numpy.count_nonzero(mycc.mo_occ[0] > 0) noccb = numpy.count_nonzero(mycc.mo_occ[1] > 0) with_frozen = not ((mycc.frozen is None) or (isinstance(mycc.frozen, (int, numpy.integer)) and mycc.frozen == 0) or (len(mycc.frozen) == 0)) moidx = mycc.get_frozen_mask() OA_a, VA_a, OF_a, VF_a = ccsd_grad._index_frozen_active( moidx[0], mycc.mo_occ[0]) OA_b, VA_b, OF_b, VF_b = ccsd_grad._index_frozen_active( moidx[1], mycc.mo_occ[1]) log.debug('symmetrized rdm2 and MO->AO transformation') # Roughly, dm2*2 is computed in _rdm2_mo2ao mo_active = (mo_a[:, numpy.hstack((OA_a, VA_a))], mo_b[:, numpy.hstack( (OA_b, VA_b))]) _rdm2_mo2ao(mycc, d2, mo_active, fdm2) # transform the active orbitals time1 = log.timer_debug1('MO->AO transformation', *time1) hf_dm1a, hf_dm1b = mycc._scf.make_rdm1(mycc.mo_coeff, mycc.mo_occ) hf_dm1 = hf_dm1a + hf_dm1b if atmlst is None: atmlst = range(mol.natm) offsetdic = mol.offset_nr_by_atom() diagidx = numpy.arange(nao) diagidx = diagidx * (diagidx + 1) // 2 + diagidx de = numpy.zeros((len(atmlst), 3)) Imata = numpy.zeros((nao, nao)) Imatb = numpy.zeros((nao, nao)) vhf1 = fdm2.create_dataset('vhf1', (len(atmlst), 2, 3, nao, nao), 'f8') # 2e AO integrals dot 2pdm max_memory = max(0, mycc.max_memory - lib.current_memory()[0]) blksize = max(1, int(max_memory * .9e6 / 8 / (nao**3 * 2.5))) for k, ia in enumerate(atmlst): shl0, shl1, p0, p1 = offsetdic[ia] ip1 = p0 vhf = numpy.zeros((2, 3, nao, nao)) for b0, b1, nf in ccsd_grad._shell_prange(mol, shl0, shl1, blksize): ip0, ip1 = ip1, ip1 + nf dm2bufa = ccsd_grad._load_block_tril(fdm2['dm2aa+ab'], ip0, ip1, nao) dm2bufb = ccsd_grad._load_block_tril(fdm2['dm2bb+ab'], ip0, ip1, nao) dm2bufa[:, :, diagidx] *= .5 dm2bufb[:, :, diagidx] *= .5 shls_slice = (b0, b1, 0, mol.nbas, 0, mol.nbas, 0, mol.nbas) eri0 = mol.intor('int2e', aosym='s2kl', shls_slice=shls_slice) Imata += lib.einsum('ipx,iqx->pq', eri0.reshape(nf, nao, -1), dm2bufa) Imatb += lib.einsum('ipx,iqx->pq', eri0.reshape(nf, nao, -1), dm2bufb) eri0 = None eri1 = mol.intor('int2e_ip1', comp=3, aosym='s2kl', shls_slice=shls_slice).reshape(3, nf, nao, -1) de[k] -= numpy.einsum('xijk,ijk->x', eri1, dm2bufa) * 2 de[k] -= numpy.einsum('xijk,ijk->x', eri1, dm2bufb) * 2 dm2bufa = dm2bufb = None # HF part for i in range(3): eri1tmp = lib.unpack_tril(eri1[i].reshape(nf * nao, -1)) eri1tmp = eri1tmp.reshape(nf, nao, nao, nao) vhf[:, i] += numpy.einsum('ijkl,ij->kl', eri1tmp, hf_dm1[ip0:ip1]) vhf[0, i] -= numpy.einsum('ijkl,il->kj', eri1tmp, hf_dm1a[ip0:ip1]) vhf[1, i] -= numpy.einsum('ijkl,il->kj', eri1tmp, hf_dm1b[ip0:ip1]) vhf[:, i, ip0:ip1] += numpy.einsum('ijkl,kl->ij', eri1tmp, hf_dm1) vhf[0, i, ip0:ip1] -= numpy.einsum('ijkl,jk->il', eri1tmp, hf_dm1a) vhf[1, i, ip0:ip1] -= numpy.einsum('ijkl,jk->il', eri1tmp, hf_dm1b) eri1 = eri1tmp = None vhf1[k] = vhf log.debug('2e-part grad of atom %d %s = %s', ia, mol.atom_symbol(ia), de[k]) time1 = log.timer_debug1('2e-part grad of atom %d' % ia, *time1) s0 = mycc._scf.get_ovlp() Imata = reduce(numpy.dot, (mo_a.T, Imata, s0, mo_a)) * -1 Imatb = reduce(numpy.dot, (mo_b.T, Imatb, s0, mo_b)) * -1 dm1a = numpy.zeros((nmoa, nmoa)) dm1b = numpy.zeros((nmob, nmob)) doo, dOO = d1[0] dov, dOV = d1[1] dvo, dVO = d1[2] dvv, dVV = d1[3] if with_frozen: dco = Imata[OF_a[:, None], OA_a] / (mo_ea[OF_a, None] - mo_ea[OA_a]) dfv = Imata[VF_a[:, None], VA_a] / (mo_ea[VF_a, None] - mo_ea[VA_a]) dm1a[OA_a[:, None], OA_a] = (doo + doo.T) * .5 dm1a[OF_a[:, None], OA_a] = dco dm1a[OA_a[:, None], OF_a] = dco.T dm1a[VA_a[:, None], VA_a] = (dvv + dvv.T) * .5 dm1a[VF_a[:, None], VA_a] = dfv dm1a[VA_a[:, None], VF_a] = dfv.T dco = Imatb[OF_b[:, None], OA_b] / (mo_eb[OF_b, None] - mo_eb[OA_b]) dfv = Imatb[VF_b[:, None], VA_b] / (mo_eb[VF_b, None] - mo_eb[VA_b]) dm1b[OA_b[:, None], OA_b] = (dOO + dOO.T) * .5 dm1b[OF_b[:, None], OA_b] = dco dm1b[OA_b[:, None], OF_b] = dco.T dm1b[VA_b[:, None], VA_b] = (dVV + dVV.T) * .5 dm1b[VF_b[:, None], VA_b] = dfv dm1b[VA_b[:, None], VF_b] = dfv.T else: dm1a[:nocca, :nocca] = (doo + doo.T) * .5 dm1a[nocca:, nocca:] = (dvv + dvv.T) * .5 dm1b[:noccb, :noccb] = (dOO + dOO.T) * .5 dm1b[noccb:, noccb:] = (dVV + dVV.T) * .5 dm1 = (reduce(numpy.dot, (mo_a, dm1a, mo_a.T)), reduce(numpy.dot, (mo_b, dm1b, mo_b.T))) vhf = mycc._scf.get_veff(mycc.mol, dm1) Xvo = reduce(numpy.dot, (mo_a[:, nocca:].T, vhf[0], mo_a[:, :nocca])) XVO = reduce(numpy.dot, (mo_b[:, noccb:].T, vhf[1], mo_b[:, :noccb])) Xvo += Imata[:nocca, nocca:].T - Imata[nocca:, :nocca] XVO += Imatb[:noccb, noccb:].T - Imatb[noccb:, :noccb] dm1_resp = _response_dm1(mycc, (Xvo, XVO), eris) dm1a += dm1_resp[0] dm1b += dm1_resp[1] time1 = log.timer_debug1('response_rdm1 intermediates', *time1) Imata[nocca:, :nocca] = Imata[:nocca, nocca:].T Imatb[noccb:, :noccb] = Imatb[:noccb, noccb:].T im1 = reduce(numpy.dot, (mo_a, Imata, mo_a.T)) im1 += reduce(numpy.dot, (mo_b, Imatb, mo_b.T)) time1 = log.timer_debug1('response_rdm1', *time1) log.debug('h1 and JK1') # Initialize hcore_deriv with the underlying SCF object because some # extensions (e.g. QM/MM, solvent) modifies the SCF object only. mf_grad = cc_grad.base._scf.nuc_grad_method() hcore_deriv = mf_grad.hcore_generator(mol) s1 = mf_grad.get_ovlp(mol) zeta = (mo_ea[:, None] + mo_ea) * .5 zeta[nocca:, :nocca] = mo_ea[:nocca] zeta[:nocca, nocca:] = mo_ea[:nocca].reshape(-1, 1) zeta_a = reduce(numpy.dot, (mo_a, zeta * dm1a, mo_a.T)) zeta = (mo_eb[:, None] + mo_eb) * .5 zeta[noccb:, :noccb] = mo_eb[:noccb] zeta[:noccb, noccb:] = mo_eb[:noccb].reshape(-1, 1) zeta_b = reduce(numpy.dot, (mo_b, zeta * dm1b, mo_b.T)) dm1 = (reduce(numpy.dot, (mo_a, dm1a, mo_a.T)), reduce(numpy.dot, (mo_b, dm1b, mo_b.T))) vhf_s1occ = mycc._scf.get_veff(mol, (dm1[0] + dm1[0].T, dm1[1] + dm1[1].T)) p1a = numpy.dot(mo_a[:, :nocca], mo_a[:, :nocca].T) p1b = numpy.dot(mo_b[:, :noccb], mo_b[:, :noccb].T) vhf_s1occ = (reduce(numpy.dot, (p1a, vhf_s1occ[0], p1a)) + reduce(numpy.dot, (p1b, vhf_s1occ[1], p1b))) * .5 time1 = log.timer_debug1('h1 and JK1', *time1) # Hartree-Fock part contribution dm1pa = hf_dm1a + dm1[0] * 2 dm1pb = hf_dm1b + dm1[1] * 2 dm1 = dm1[0] + dm1[1] + hf_dm1 zeta_a += rhf_grad.make_rdm1e(mo_ea, mo_a, mycc.mo_occ[0]) zeta_b += rhf_grad.make_rdm1e(mo_eb, mo_b, mycc.mo_occ[1]) zeta = zeta_a + zeta_b for k, ia in enumerate(atmlst): shl0, shl1, p0, p1 = offsetdic[ia] # s[1] dot I, note matrix im1 is not hermitian de[k] += numpy.einsum('xij,ij->x', s1[:, p0:p1], im1[p0:p1]) de[k] += numpy.einsum('xji,ij->x', s1[:, p0:p1], im1[:, p0:p1]) # h[1] \dot DM, contribute to f1 h1ao = hcore_deriv(ia) de[k] += numpy.einsum('xij,ji->x', h1ao, dm1) # -s[1]*e \dot DM, contribute to f1 de[k] -= numpy.einsum('xij,ij->x', s1[:, p0:p1], zeta[p0:p1]) de[k] -= numpy.einsum('xji,ij->x', s1[:, p0:p1], zeta[:, p0:p1]) # -vhf[s_ij[1]], contribute to f1, *2 for s1+s1.T de[k] -= numpy.einsum('xij,ij->x', s1[:, p0:p1], vhf_s1occ[p0:p1]) * 2 de[k] -= numpy.einsum('xij,ij->x', vhf1[k, 0], dm1pa) de[k] -= numpy.einsum('xij,ij->x', vhf1[k, 1], dm1pb) log.timer('%s gradients' % mycc.__class__.__name__, *time0) return de
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 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 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) 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]) 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 + 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 canonicalize(mc, mo_coeff=None, ci=None, eris=None, sort=False, cas_natorb=False, casdm1=None, verbose=logger.NOTE): '''Canonicalized CASCI/CASSCF orbitals of effecitve Fock matrix. Effective Fock matrix is built with one-particle density matrix (see also :func:`mcscf.casci.get_fock`) Args: mc : a CASSCF/CASCI object or RHF object Returns: A tuple, (natural orbitals, CI coefficients, orbital energies) The orbital energies are the diagonal terms of general Fock matrix. ''' from pyscf.lo import orth from pyscf.tools import dump_mat log = logger.new_logger(mc, verbose) if mo_coeff is None: mo_coeff = mc.mo_coeff if ci is None: ci = mc.ci if casdm1 is None: casdm1 = mc.fcisolver.make_rdm1(ci, mc.ncas, mc.nelecas) ncore = mc.ncore nocc = ncore + mc.ncas nmo = mo_coeff.shape[1] fock_ao = mc.get_fock(mo_coeff, ci, eris, casdm1, verbose) fock = reduce(numpy.dot, (mo_coeff.T, fock_ao, mo_coeff)) mo_energy = fock.diagonal().copy() if cas_natorb: mo_coeff1, ci, occ = mc.cas_natorb(mo_coeff, ci, eris, sort, casdm1, verbose) ma = mo_coeff1[:,ncore:nocc] mo_energy[ncore:nocc] = numpy.einsum('ji,ji->i', ma, fock_ao.dot(ma)) else: # Keep the active space unchanged by default. The rotation in active space # may cause problem for external CI solver eg DMRG. mo_coeff1 = numpy.empty_like(mo_coeff) mo_coeff1[:,ncore:nocc] = mo_coeff[:,ncore:nocc] if ncore > 0: # note the last two args of ._eig for mc1step_symm # mc._eig function is called to handle symmetry adapated fock w, c1 = mc._eig(fock[:ncore,:ncore], 0, ncore) if sort: idx = numpy.argsort(w.round(9), kind='mergesort') w = w[idx] c1 = c1[:,idx] mo_coeff1[:,:ncore] = numpy.dot(mo_coeff[:,:ncore], c1) mo_energy[:ncore] = w if nmo-nocc > 0: w, c1 = mc._eig(fock[nocc:,nocc:], nocc, nmo) if sort: idx = numpy.argsort(w.round(9), kind='mergesort') w = w[idx] c1 = c1[:,idx] mo_coeff1[:,nocc:] = numpy.dot(mo_coeff[:,nocc:], c1) mo_energy[nocc:] = w if hasattr(mo_coeff, 'orbsym'): if sort: orbsym = symm.label_orb_symm(mc.mol, mc.mol.irrep_id, mc.mol.symm_orb, mo_coeff1) else: orbsym = mo_coeff.orbsym mo_coeff1 = lib.tag_array(mo_coeff1, orbsym=orbsym) if log.verbose >= logger.DEBUG: for i in range(nmo): log.debug('i = %d <i|F|i> = %12.8f', i+1, mo_energy[i]) # still return ci coefficients, in case the canonicalization funciton changed # cas orbitals, the ci coefficients should also be updated. return mo_coeff1, ci, mo_energy
def analyze(tdobj, verbose=None): log = logger.new_logger(tdobj, verbose) mol = tdobj.mol mo_coeff = tdobj._scf.mo_coeff mo_occ = tdobj._scf.mo_occ nocc = numpy.count_nonzero(mo_occ == 2) e_ev = numpy.asarray(tdobj.e) * nist.HARTREE2EV e_wn = numpy.asarray(tdobj.e) * nist.HARTREE2WAVENUMBER wave_length = 1e7 / e_wn if tdobj.singlet: log.note( '\n** Singlet excitation energies and oscillator strengths **') else: log.note( '\n** Triplet excitation energies and oscillator strengths **') if mol.symmetry: orbsym = hf_symm.get_orbsym(mol, mo_coeff) x_sym = symm.direct_prod(orbsym[mo_occ == 2], orbsym[mo_occ == 0], mol.groupname) else: x_sym = None f_oscillator = tdobj.oscillator_strength() for i, ei in enumerate(tdobj.e): x, y = tdobj.xy[i] if x_sym is None: log.note('Excited State %3d: %12.5f eV %9.2f nm f=%.4f', i + 1, e_ev[i], wave_length[i], f_oscillator[i]) else: wfnsym = analyze_wfnsym(tdobj, x_sym, x) log.note('Excited State %3d: %4s %12.5f eV %9.2f nm f=%.4f', i + 1, wfnsym, e_ev[i], wave_length[i], f_oscillator[i]) if log.verbose >= logger.INFO: o_idx, v_idx = numpy.where(abs(x) > 0.1) for o, v in zip(o_idx, v_idx): log.info(' %4d -> %-4d %12.5f', o + MO_BASE, v + MO_BASE + nocc, x[o, v]) if log.verbose >= logger.INFO: log.info('\n** Transition electric dipole moments (AU) **') log.info( 'state X Y Z Dip. S. Osc.' ) trans_dip = tdobj.transition_dipole() for i, ei in enumerate(tdobj.e): dip = trans_dip[i] log.info('%3d %11.4f %11.4f %11.4f %11.4f %11.4f', i + 1, dip[0], dip[1], dip[2], numpy.dot(dip, dip), f_oscillator[i]) log.info( '\n** Transition velocity dipole moments (imaginary part, AU) **') log.info( 'state X Y Z Dip. S. Osc.' ) trans_v = tdobj.transition_velocity_dipole() f_v = tdobj.oscillator_strength(gauge='velocity', order=0) for i, ei in enumerate(tdobj.e): v = trans_v[i] log.info('%3d %11.4f %11.4f %11.4f %11.4f %11.4f', i + 1, v[0], v[1], v[2], numpy.dot(v, v), f_v[i]) log.info( '\n** Transition magnetic dipole moments (imaginary part, AU) **') log.info('state X Y Z') trans_m = tdobj.transition_magnetic_dipole() for i, ei in enumerate(tdobj.e): m = trans_m[i] log.info('%3d %11.4f %11.4f %11.4f', i + 1, m[0], m[1], m[2]) return tdobj