def make_rdms_mcpdft (mc, ot=None, mo_coeff=None, ci=None): ''' Build the necessary density matrices for an MC-PDFT calculation Args: mc : an instance of CASSCF or CASCI class Note: this function does not currently run the CASSCF or CASCI calculation itself Kwargs: ot : an instance of on-top density functional class - see otfnal.py mo_coeff : ndarray of shape (nao, nmo) Molecular orbital coefficients ci : ndarray or list CI vector or vectors. If a list of many CI vectors, mc must be a state-average object with the correct nroots Returns: dm1s : ndarray of shape (2,nao,nao) Spin-separated 1-RDM adm : (adm1s, adm2s) adm1s : ndarray of shape (2,ncas,ncas) Spin-separated 1-RDM for the active orbitals adm2s : 3 ndarrays of shape (ncas,ncas,ncas,ncas) First ndarray is spin-summed casdm2 Second ndarray is casdm2_aa + casdm2_bb Third ndarray is casdm2_ab ''' if ci is None: ci = mc.ci if ot is None: ot = mc.otfnal if mo_coeff is None: mo_coeff = mc.mo_coeff ncore, ncas, nelecas = mc.ncore, mc.ncas, mc.nelecas nocc = ncore + ncas # figure out the correct RDMs to build (state-average or state-specific?) nelecas = _unpack_nelec (nelecas) ndet = [cistring.num_strings (ncas, n) for n in nelecas] ci = np.asarray (ci).reshape (-1, ndet[0], ndet[1]) nroots = ci.shape[0] if nroots > 1: _casdms = mc.fcisolver else: ci = ci[0] _casdms = fci.solver (mc._scf.mol, singlet=False, symm=False) # Make the rdms # make_rdm12s returns (a, b), (aa, ab, bb) mo_cas = mo_coeff[:,ncore:nocc] moH_cas = mo_cas.conj ().T mo_core = mo_coeff[:,:ncore] moH_core = mo_core.conj ().T adm1s = np.stack (_casdms.make_rdm1s (ci, ncas, nelecas), axis=0) adm2s = _casdms.make_rdm12s (ci, ncas, nelecas)[1] adm2s = get_2CDMs_from_2RDMs (adm2s, adm1s) adm2_ss = adm2s[0] + adm2s[2] adm2_os = adm2s[1] adm2 = adm2_ss + adm2_os + adm2_os.transpose (2,3,0,1) dm1s = np.dot (adm1s, moH_cas) dm1s = np.dot (mo_cas, dm1s).transpose (1,0,2) dm1s += np.dot (mo_core, moH_core)[None,:,:] return dm1s, (adm1s, (adm2, adm2_ss, adm2_os))
def _get_e_decomp(mc, ot, mo_coeff, ci, e_mcscf): if ot is not None: xfnal, cfnal = ot.split_x_c() ncore, ncas, nelecas = mc.ncore, mc.ncas, mc.nelecas if isinstance(mc, mcscf.casci.CASCI): _rdms = mc _casdms = mc.fcisolver else: _rdms = mcscf.CASCI(mc._scf, ncas, nelecas) _rdms.fcisolver = fci.solver(mc._scf.mol, singlet=False, symm=False) _rdms.mo_coeff = mo_coeff _rdms.ci = ci _casdms = _rdms.fcisolver _scf = _rdms._scf.to_uhf() dm1s = np.stack(_rdms.make_rdm1s(), axis=0) dm1 = dm1s[0] + dm1s[1] h = _scf.get_hcore() j, k = _scf.get_jk(dm=dm1s) e_nn = _scf.energy_nuc() e_core = np.tensordot(h, dm1, axes=2) #print(j.shape, k.shape, dm1.shape) e_coul = np.tensordot(j[0] + j[1], dm1, axes=2) / 2 e_x = -(np.tensordot(k[0], dm1s[0]) + np.tensordot(k[1], dm1s[1])) / 2 e_otx = 0.0 e_otc = 0.0 if ot is not None: adm1s = np.stack(_casdms.make_rdm1s(ci, ncas, nelecas), axis=0) adm2s = _casdms.make_rdm12s(_rdms.ci, ncas, nelecas)[1] #print(adm2s) adm2s = get_2CDMs_from_2RDMs(adm2s, adm1s) adm2_ss = adm2s[0] + adm2s[2] adm2_os = adm2s[1] adm2 = adm2_ss + adm2_os + adm2_os.transpose(2, 3, 0, 1) mo_cas = mo_coeff[:, ncore:][:, :ncas] e_otx = get_E_ot(xfnal, dm1s, adm2, mo_cas, max_memory=mc.max_memory) e_otc = get_E_ot(cfnal, dm1s, adm2, mo_cas, max_memory=mc.max_memory) e_c = e_mcscf - e_nn - e_core - e_coul - e_x return e_nn, e_core, e_coul, e_x, e_otx, e_otc, e_c
def kernel(mc, ot, root=-1): ''' Calculate MC-PDFT total energy Args: mc : an instance of CASSCF or CASCI class Note: this function does not currently run the CASSCF or CASCI calculation itself prior to calculating the MC-PDFT energy. Call mc.kernel () before passing to this function! ot : an instance of on-top density functional class - see otfnal.py Kwargs: root : int If mc describes a state-averaged calculation, select the root (0-indexed) Negative number requests state-averaged MC-PDFT results (i.e., using state-averaged density matrices) Returns: Total MC-PDFT energy including nuclear repulsion energy. ''' t0 = (time.clock(), time.time()) amo = mc.mo_coeff[:, mc.ncore:mc.ncore + mc.ncas] # make_rdm12s returns (a, b), (aa, ab, bb) mc_1root = mc if isinstance(mc.ci, list) and root >= 0: mc_1root = mcscf.CASCI(mc._scf, mc.ncas, mc.nelecas) mc_1root.fcisolver = fci.solver(mc._scf.mol, singlet=False, symm=False) mc_1root.mo_coeff = mc.mo_coeff mc_1root.ci = mc.ci[root] mc_1root.e_tot = mc.e_tot dm1s = np.asarray(mc_1root.make_rdm1s()) adm1s = np.stack(mc_1root.fcisolver.make_rdm1s(mc_1root.ci, mc.ncas, mc.nelecas), axis=0) adm2 = get_2CDM_from_2RDM( mc_1root.fcisolver.make_rdm12(mc_1root.ci, mc.ncas, mc.nelecas)[1], adm1s) if ot.verbose >= logger.DEBUG: adm2s = get_2CDMs_from_2RDMs( mc_1root.fcisolver.make_rdm12s(mc_1root.ci, mc.ncas, mc.nelecas)[1], adm1s) adm2s_ss = adm2s[0] + adm2s[2] adm2s_os = adm2s[1] spin = abs(mc.nelecas[0] - mc.nelecas[1]) t0 = logger.timer(ot, 'rdms', *t0) omega, alpha, hyb = ot._numint.rsh_and_hybrid_coeff(ot.otxc, spin=spin) Vnn = mc._scf.energy_nuc() h = mc._scf.get_hcore() dm1 = dm1s[0] + dm1s[1] if ot.verbose >= logger.DEBUG or abs(hyb) > 1e-10: vj, vk = mc._scf.get_jk(dm=dm1s) vj = vj[0] + vj[1] else: vj = mc._scf.get_j(dm=dm1) Te_Vne = np.tensordot(h, dm1) # (vj_a + vj_b) * (dm_a + dm_b) E_j = np.tensordot(vj, dm1) / 2 # (vk_a * dm_a) + (vk_b * dm_b) Mind the difference! if ot.verbose >= logger.DEBUG or abs(hyb) > 1e-10: E_x = -(np.tensordot(vk[0], dm1s[0]) + np.tensordot(vk[1], dm1s[1])) / 2 else: E_x = 0 logger.debug(ot, 'CAS energy decomposition:') logger.debug(ot, 'Vnn = %s', Vnn) logger.debug(ot, 'Te + Vne = %s', Te_Vne) logger.debug(ot, 'E_j = %s', E_j) logger.debug(ot, 'E_x = %s', E_x) if ot.verbose >= logger.DEBUG: # g_pqrs * l_pqrs / 2 #if ot.verbose >= logger.DEBUG: aeri = ao2mo.restore(1, mc.get_h2eff(mc.mo_coeff), mc.ncas) E_c = np.tensordot(aeri, adm2, axes=4) / 2 E_c_ss = np.tensordot(aeri, adm2s_ss, axes=4) / 2 E_c_os = np.tensordot(aeri, adm2s_os, axes=4) # ab + ba -> factor of 2 logger.info(ot, 'E_c = %s', E_c) logger.info(ot, 'E_c (SS) = %s', E_c_ss) logger.info(ot, 'E_c (OS) = %s', E_c_os) e_err = E_c_ss + E_c_os - E_c assert (abs(e_err) < 1e-8), e_err if isinstance(mc_1root.e_tot, float): e_err = mc_1root.e_tot - (Vnn + Te_Vne + E_j + E_x + E_c) assert (abs(e_err) < 1e-8), e_err if abs(hyb) > 1e-10: logger.debug(ot, 'Adding %s * %s CAS exchange to E_ot', hyb, E_x) t0 = logger.timer(ot, 'Vnn, Te, Vne, E_j, E_x', *t0) E_ot = get_E_ot(ot, dm1s, adm2, amo) t0 = logger.timer(ot, 'E_ot', *t0) e_tot = Vnn + Te_Vne + E_j + (hyb * E_x) + E_ot logger.info(ot, 'MC-PDFT E = %s, Eot(%s) = %s', e_tot, ot.otxc, E_ot) return e_tot, E_ot