From ae9b453e47954a1aeed7ed4ef7f563363a70dfdd Mon Sep 17 00:00:00 2001 From: xubwa Date: Thu, 30 May 2019 00:03:49 -0600 Subject: [PATCH 01/26] mv soc supports to a standalone socutils.py file --- pyscf/shciscf/shci.py | 232 --------------- pyscf/shciscf/socutils.py | 577 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 577 insertions(+), 232 deletions(-) create mode 100644 pyscf/shciscf/socutils.py diff --git a/pyscf/shciscf/shci.py b/pyscf/shciscf/shci.py index cf893d0ac4..38804bbb55 100644 --- a/pyscf/shciscf/shci.py +++ b/pyscf/shciscf/shci.py @@ -1306,177 +1306,6 @@ def get_p(dm, x, rp): return pLL, pLS, pSS -def get_fso2e_withkint(kint, x, rp, pLL, pLS, pSS): - nb = x.shape[0] - fso2e = numpy.zeros((3, nb, nb)) - for ic in range(3): - gsoLL = -2.0 * numpy.einsum('lmkn,lk->mn', kint[ic], pSS) - gsoLS = -numpy.einsum('mlkn,lk->mn',kint[ic],pLS) \ - -numpy.einsum('lmkn,lk->mn',kint[ic],pLS) - gsoSS = -2.0*numpy.einsum('mnkl,lk',kint[ic],pLL) \ - -2.0*numpy.einsum('mnlk,lk',kint[ic],pLL) \ - +2.0*numpy.einsum('mlnk,lk',kint[ic],pLL) - fso2e[ic] = gsoLL + gsoLS.dot(x) + x.T.dot(-gsoLS.T) \ - + x.T.dot(gsoSS.dot(x)) - fso2e[ic] = reduce(numpy.dot, (rp.T, fso2e[ic], rp)) - return fso2e - - -def get_kint2(mol): - nb = mol.nao_nr() - kint = mol.intor('int2e_spv1spv2_spinor', comp=3) - return kint.reshape(3, nb, nb, nb, nb) - - -def get_fso2e(mol, x, rp, pLL, pLS, pSS): - nb = mol.nao_nr() - np = nb * nb - nq = np * np - ddint = mol.intor('int2e_ip1ip2_sph', 9).reshape(3, 3, nq) - fso2e = numpy.zeros((3, nb, nb)) - - ddint[0, 0] = ddint[1, 2] - ddint[2, 1] - kint = ddint[0, 0].reshape(nb, nb, nb, nb) - gsoLL = -2.0 * numpy.einsum('lmkn,lk->mn', kint, pSS) - gsoLS = -numpy.einsum('mlkn,lk->mn',kint,pLS) \ - -numpy.einsum('lmkn,lk->mn',kint,pLS) - gsoSS = -2.0*numpy.einsum('mnkl,lk',kint,pLL) \ - -2.0*numpy.einsum('mnlk,lk',kint,pLL) \ - +2.0*numpy.einsum('mlnk,lk',kint,pLL) - fso2e[0] = gsoLL + gsoLS.dot(x) + x.T.dot(-gsoLS.T) \ - + x.T.dot(gsoSS.dot(x)) - fso2e[0] = reduce(numpy.dot, (rp.T, fso2e[0], rp)) - - ddint[0, 0] = ddint[2, 0] - ddint[2, 1] - kint = ddint[0, 0].reshape(nb, nb, nb, nb) - gsoLL = -2.0 * numpy.einsum('lmkn,lk->mn', kint, pSS) - gsoLS = -numpy.einsum('mlkn,lk->mn',kint,pLS) \ - -numpy.einsum('lmkn,lk->mn',kint,pLS) - gsoSS = -2.0*numpy.einsum('mnkl,lk',kint,pLL) \ - -2.0*numpy.einsum('mnlk,lk',kint,pLL) \ - +2.0*numpy.einsum('mlnk,lk',kint,pLL) - fso2e[1] = gsoLL + gsoLS.dot(x) + x.T.dot(-gsoLS.T) \ - + x.T.dot(gsoSS.dot(x)) - fso2e[1] = reduce(numpy.dot, (rp.T, fso2e[1], rp)) - - ddint[0, 0] = ddint[0, 1] - ddint[1, 0] - kint = ddint[0, 0].reshape(nb, nb, nb, nb) - gsoLL = -2.0 * numpy.einsum('lmkn,lk->mn', kint, pSS) - gsoLS = -numpy.einsum('mlkn,lk->mn',kint,pLS) \ - -numpy.einsum('lmkn,lk->mn',kint,pLS) - gsoSS = -2.0*numpy.einsum('mnkl,lk',kint,pLL) \ - -2.0*numpy.einsum('mnlk,lk',kint,pLL) \ - +2.0*numpy.einsum('mlnk,lk',kint,pLL) - fso2e[2] = gsoLL + gsoLS.dot(x) + x.T.dot(-gsoLS.T) \ - + x.T.dot(gsoSS.dot(x)) - fso2e[2] = reduce(numpy.dot, (rp.T, fso2e[2], rp)) - return fso2e - - -def get_kint(mol): - nb = mol.nao_nr() - np = nb * nb - nq = np * np - ddint = mol.intor('int2e_ip1ip2_sph', 9).reshape(3, 3, nq) - - kint = numpy.zeros((3, nq)) - kint[0] = ddint[1, 2] - ddint[2, 1] # x = yz - zy - kint[1] = ddint[2, 0] - ddint[0, 2] # y = zx - xz - kint[2] = ddint[0, 1] - ddint[1, 0] # z = xy - yx - return kint.reshape(3, nb, nb, nb, nb) - - -def writeSOCIntegrals(mc, - ncasorbs=None, - rdm1=None, - pictureChange1e="bp", - pictureChange2e="bp", - uncontract=True): - from pyscf.x2c import x2c, sfx2c1e - from pyscf.lib.parameters import LIGHT_SPEED - LIGHT_SPEED = 137.0359895000 - alpha = 1.0 / LIGHT_SPEED - - if (uncontract): - xmol, contr_coeff = x2c.X2C().get_xmol(mc.mol) - else: - xmol, contr_coeff = mc.mol, numpy.eye(mc.mo_coeff.shape[0]) - - rdm1ao = rdm1 - if (rdm1 is None): - rdm1ao = 1. * mc.make_rdm1() - if len(rdm1ao.shape) > 2: rdm1ao = (rdm1ao[0] + rdm1ao[1]) - - if (uncontract): - dm = reduce(numpy.dot, (contr_coeff, rdm1ao, contr_coeff.T)) - else: - dm = 1. * rdm1ao - np, nc = contr_coeff.shape[0], contr_coeff.shape[1] - - hso1e = numpy.zeros((3, np, np)) - h1e_1c, x, rp = sfx2c1e.SpinFreeX2C(mc.mol).get_hxr( - mc.mol, uncontract=uncontract) - - #two electron terms - if (pictureChange2e == "bp"): - h2ao = -(alpha)**2 * 0.5 * xmol.intor( - 'cint2e_p1vxp1_sph', comp=3, aosym='s1') - h2ao = h2ao.reshape(3, np, np, np, np) - hso1e += 1. * (numpy.einsum('ijklm,lm->ijk', h2ao, dm) - 1.5 * - (numpy.einsum('ijklm, kl->ijm', h2ao, dm) + - numpy.einsum('ijklm,mj->ilk', h2ao, dm))) - elif (pictureChange2e == "x2c"): - dm1 = dm / 2. - pLL, pLS, pSS = get_p(dm1, x, rp) - #kint = get_kint(xmol) - #hso1e += -(alpha)**2*0.5*get_fso2e_withkint(kint,x,rp,pLL,pLS,pSS) - hso1e += -(alpha)**2 * 0.5 * get_fso2e(xmol, x, rp, pLL, pLS, pSS) - elif (pictureChange2e == "none"): - hso1e *= 0.0 - else: - print(pictureChane2e, "not a valid option") - exit(0) - - #MF 1 electron term - if (pictureChange1e == "bp"): - hso1e += (alpha)**2 * 0.5 * get_wso(xmol) - elif (pictureChange1e == "x2c1"): - dm /= 2. - pLL, pLS, pSS = get_p(dm, x, rp) - wso = (alpha)**2 * 0.5 * get_wso(xmol) - hso1e += get_hso1e(wso, x, rp) - elif (pictureChange1e == "x2cn"): - h1e_2c = x2c.get_hcore(xmol) - - for i in range(np): - for j in range(np): - if (abs(h1e_2c[2 * i, 2 * j + 1].imag) > 1.e-8): - hso1e[0][i, j] -= h1e_2c[2 * i, 2 * j + 1].imag * 2. - if (abs(h1e_2c[2 * i, 2 * j + 1].real) > 1.e-8): - hso1e[1][i, j] -= h1e_2c[2 * i, 2 * j + 1].real * 2. - if (abs(h1e_2c[2 * i, 2 * j].imag) > 1.e-8): - hso1e[2][i, j] -= h1e_2c[2 * i, 2 * j].imag * 2. - else: - print(pictureChane1e, "not a valid option") - exit(0) - - h1ao = numpy.zeros((3, nc, nc)) - if (uncontract): - for ic in range(3): - h1ao[ic] = reduce(numpy.dot, - (contr_coeff.T, hso1e[ic], contr_coeff)) - else: - h1ao = 1. * hso1e - - ncore, ncas = mc.ncore, mc.ncas - if (ncasorbs is not None): - ncas = ncasorbs - mo_coeff = mc.mo_coeff - h1 = numpy.einsum('xpq,pi,qj->xij', h1ao, mo_coeff, - mo_coeff)[:, ncore:ncore + ncas, ncore:ncore + ncas] - print1Int(h1, 'SOC') - - def dryrun(mc, mo_coeff=None): '''Generate FCIDUMP and SHCI config file''' if mo_coeff is None: @@ -1486,67 +1315,6 @@ def dryrun(mc, mo_coeff=None): #mc.fcisolver.onlywriteIntegral = bak -#mc is the CASSCF object -#nroots is the number of roots that will be used to calcualte the SOC matrix -def runQDPT(mc, gtensor): - if mc.fcisolver.__class__.__name__ == 'FakeCISolver': - SHCI = mc.fcisolver.fcisolvers[0] - outFile = os.path.join(SHCI.runtimeDir, SHCI.outputFile) - writeSHCIConfFile(SHCI, mc.nelecas, False) - confFile = os.path.join(SHCI.runtimeDir, SHCI.configFile) - f = open(confFile, 'a') - for SHCI2 in mc.fcisolver.fcisolvers[1:]: - if (SHCI2.scratchDirectory != ""): - f.write('prefix %s\n' % (SHCI2.scratchDirectory)) - if (gtensor): - f.write("dogtensor\n") - f.close() - try: - cmd = ' '.join((SHCI.mpiprefix, SHCI.QDPTexecutable, confFile)) - cmd = "%s > %s 2>&1" % (cmd, outFile) - check_call(cmd, shell=True) - check_call('cat %s|grep -v "#"' % (outFile), shell=True) - except CalledProcessError as err: - logger.error(mc.fcisolver, cmd) - raise err - - else: - writeSHCIConfFile(mc.fcisolver, mc.nelecas, False) - confFile = os.path.join(mc.fcisolver.runtimeDir, - mc.fcisolver.configFile) - outFile = os.path.join(mc.fcisolver.runtimeDir, - mc.fcisolver.outputFile) - if (gtensor): - f = open(confFile, 'a') - f.write("dogtensor\n") - f.close() - try: - cmd = ' '.join((mc.fcisolver.mpiprefix, - mc.fcisolver.QDPTexecutable, confFile)) - cmd = "%s > %s 2>&1" % (cmd, outFile) - check_call(cmd, shell=True) - check_call('cat %s|grep -v "#"' % (outFile), shell=True) - except CalledProcessError as err: - logger.error(mc.fcisolver, cmd) - raise err - - -def doSOC(mc, gtensor=False, pictureChange="bp"): - writeSOCIntegrals(mc, pictureChange=pictureChange) - dryrun(mc) - if (gtensor or True): - ncore, ncas = mc.ncore, mc.ncas - charge_center = numpy.einsum('z,zx->x', mc.mol.atom_charges(), - mc.mol.atom_coords()) - h1ao = mc.mol.intor('cint1e_cg_irxp_sph', comp=3) - h1 = numpy.einsum( - 'xpq,pi,qj->xij', h1ao, mc.mo_coeff, - mc.mo_coeff)[:, ncore:ncore + ncas, ncore:ncore + ncas] - print1Int(h1, 'GTensor') - - runQDPT(mc, gtensor) - - if __name__ == '__main__': from pyscf import gto, scf, mcscf, dmrgscf from pyscf.shciscf import shci diff --git a/pyscf/shciscf/socutils.py b/pyscf/shciscf/socutils.py new file mode 100644 index 0000000000..4f0e6df4ca --- /dev/null +++ b/pyscf/shciscf/socutils.py @@ -0,0 +1,577 @@ +import time +from functools import reduce + +import numpy +import scipy.linalg + +from pyscf import gto, lib +from pyscf.lib.parameters import LIGHT_SPEED +from pyscf.shciscf import shci +from pyscf.x2c import x2c + + +def print1Int(h1, name): + with open('%s.X' % (name), 'w') as fout: + fout.write('%d\n' % h1[0].shape[0]) + for i in range(h1[0].shape[0]): + for j in range(h1[0].shape[0]): + if (abs(h1[0, i, j]) > 1.e-8): + fout.write('%16.10g %4d %4d\n' % (h1[0, i, j], i + 1, j + 1)) + + with open('%s.Y' % (name), 'w') as fout: + fout.write('%d\n' % h1[1].shape[0]) + for i in range(h1[1].shape[0]): + for j in range(h1[1].shape[0]): + if (abs(h1[1, i, j]) > 1.e-8): + fout.write('%16.10g %4d %4d\n' % (h1[1, i, j], i + 1, j + 1)) + + with open('%s.Z' % (name), 'w') as fout: + fout.write('%d\n' % h1[2].shape[0]) + for i in range(h1[2].shape[0]): + for j in range(h1[2].shape[0]): + if (abs(h1[2, i, j]) > 1.e-8): + fout.write('%16.10g %4d %4d\n' % (h1[2, i, j], i + 1, j + 1)) + + with open('%sZ' % (name), 'w') as fout: + fout.write('%d\n' % h1[2].shape[0]) + for i in range(h1[2].shape[0]): + for j in range(h1[2].shape[0]): + if (abs(h1[2, i, j]) > 1.e-8): + fout.write('%16.10g %4d %4d\n' % (h1[2, i, j], i + 1, j + 1)) + + +#by default h is returned in the contracted basis +#x and r in the uncontracted basis +def get_hxr(mc, uncontract=True): + if (uncontract): + xmol, contr_coeff = x2c.X2C().get_xmol(mc.mol) + else: + xmol, contr_coeff = mc.mol, numpy.eye(mc.mo_coeff.shape[0]) + + c = lib.param.LIGHT_SPEED + #assert('1E' in self.approx.upper()) + t = xmol.intor_symmetric('int1e_kin') + v = xmol.intor_symmetric('int1e_nuc') + s = xmol.intor_symmetric('int1e_ovlp') + w = xmol.intor_symmetric('int1e_pnucp') + + h1, x, r = _x2c1e_hxrmat(t, v, w, s, c) + if (uncontract): + h1 = reduce(numpy.dot, (contr_coeff.T, h1, contr_coeff)) + + return h1, x, r + + +def _x2c1e_hxrmat(t, v, w, s, c): + nao = s.shape[0] + n2 = nao * 2 + h = numpy.zeros((n2, n2), dtype=v.dtype) + m = numpy.zeros((n2, n2), dtype=v.dtype) + h[:nao, :nao] = v + h[:nao, nao:] = t + h[nao:, :nao] = t + h[nao:, nao:] = w * (.25 / c**2) - t + m[:nao, :nao] = s + m[nao:, nao:] = t * (.5 / c**2) + + e, a = scipy.linalg.eigh(h, m) + cl = a[:nao, nao:] + cs = a[nao:, nao:] + + b = numpy.dot(cl, cl.T.conj()) + x = reduce(numpy.dot, (cs, cl.T.conj(), numpy.linalg.inv(b))) + + s1 = s + reduce(numpy.dot, (x.T.conj(), t, x)) * (.5 / c**2) + tx = reduce(numpy.dot, (t, x)) + h1 = (h[:nao, :nao] + h[:nao, nao:].dot(x) + x.T.conj().dot(h[nao:, :nao]) + + reduce(numpy.dot, (x.T.conj(), h[nao:, nao:], x))) + + sa = _invsqrt(s) + sb = _invsqrt(reduce(numpy.dot, (sa, s1, sa))) + r = reduce(numpy.dot, (sa, sb, sa, s)) + h1out = reduce(numpy.dot, (r.T.conj(), h1, r)) + return h1out, x, r + + +def _invsqrt(a, tol=1e-14): + e, v = numpy.linalg.eigh(a) + idx = e > tol + return numpy.dot(v[:, idx] / numpy.sqrt(e[idx]), v[:, idx].T.conj()) + + +def get_hso1e(wso, x, rp): + nb = x.shape[0] + hso1e = numpy.zeros((3, nb, nb)) + for ic in range(3): + hso1e[ic] = reduce(numpy.dot, (rp.T, x.T, wso[ic], x, rp)) + return hso1e + + +def get_wso(mol): + nb = mol.nao_nr() + wso = numpy.zeros((3, nb, nb)) + for iatom in range(mol.natm): + zA = mol.atom_charge(iatom) + xyz = mol.atom_coord(iatom) + mol.set_rinv_orig(xyz) + wso += zA * mol.intor('cint1e_prinvxp_sph', 3) # sign due to integration by part + return wso + + +def get_wso_1c(mol, atomlist): + nb = mol.nao_nr() + wso = numpy.zeros((3, nb, nb)) + aoslice = mol.aoslice_by_atom() + for iatom in atomlist: + zA = mol.atom_charge(iatom) + xyz = mol.atom_coord(iatom) + mol.set_rinv_orig(xyz) + ao_start = aoslice[iatom, 2] + ao_end = aoslice[iatom, 3] + shl_start = aoslice[iatom, 0] + shl_end = aoslice[iatom, 1] + wso[:, ao_start : ao_end, ao_start : ao_end]\ + = zA*mol.intor('cint1e_prinvxp_sph', 3, shls_slice=[shl_start, shl_end, shl_start, shl_end]).reshape(3, ao_end-ao_start, ao_end-ao_start) + return wso + + +def get_p(dm, x, rp): + pLL = rp.dot(dm.dot(rp.T)) + pLS = pLL.dot(x.T) + pSS = x.dot(pLL.dot(x.T)) + return pLL, pLS, pSS + + +def get_fso2e_0(mol, x, rp, pLL, pLS, pSS): + ''' Function for x2c Hamiltonian without any memory saving ''' + nb = mol.nao_nr() + np = nb * nb + nq = np * np + + ddint = mol.intor('int2e_ip1ip2_sph', 9).reshape(3, 3, nq) + fso2e = numpy.zeros((3, nb, nb)) + + ddint[0, 0] = ddint[1, 2] - ddint[2, 1] + kint = ddint[0, 0].reshape(nb, nb, nb, nb) + gsoLL = -2.0 * numpy.einsum('lmkn,lk->mn', kint, pSS) + gsoLS = -1.0*numpy.einsum('mlkn,lk->mn',kint,pLS) \ + -1.0*numpy.einsum('lmkn,lk->mn',kint,pLS) + gsoSS = -2.0*numpy.einsum('mnkl,lk',kint,pLL) \ + -2.0*numpy.einsum('mnlk,lk',kint,pLL) \ + +2.0*numpy.einsum('mlnk,lk',kint,pLL) + fso2e[0] = gsoLL + gsoLS.dot(x) + x.T.dot(-gsoLS.T) \ + + x.T.dot(gsoSS.dot(x)) + fso2e[0] = reduce(numpy.dot, (rp.T, fso2e[0], rp)) + + ddint[0, 0] = ddint[2, 0] - ddint[2, 1] + kint = ddint[0, 0].reshape(nb, nb, nb, nb) + gsoLL = -2.0 * numpy.einsum('lmkn,lk->mn', kint, pSS) + gsoLS = -1.0*numpy.einsum('mlkn,lk->mn',kint,pLS) \ + -1.0*numpy.einsum('lmkn,lk->mn',kint,pLS) + gsoSS = -2.0*numpy.einsum('mnkl,lk',kint,pLL) \ + -2.0*numpy.einsum('mnlk,lk',kint,pLL) \ + +2.0*numpy.einsum('mlnk,lk',kint,pLL) + fso2e[1] = gsoLL + gsoLS.dot(x) + x.T.dot(-gsoLS.T) \ + + x.T.dot(gsoSS.dot(x)) + fso2e[1] = reduce(numpy.dot, (rp.T, fso2e[1], rp)) + + ddint[0, 0] = ddint[0, 1] - ddint[1, 0] + kint = ddint[0, 0].reshape(nb, nb, nb, nb) + gsoLL = -2.0 * numpy.einsum('lmkn,lk->mn', kint, pSS) + gsoLS = -1.0*numpy.einsum('mlkn,lk->mn',kint,pLS) \ + -1.0*numpy.einsum('lmkn,lk->mn',kint,pLS) + gsoSS = -2.0*numpy.einsum('mnkl,lk',kint,pLL) \ + -2.0*numpy.einsum('mnlk,lk',kint,pLL) \ + +2.0*numpy.einsum('mlnk,lk',kint,pLL) + fso2e[2] = gsoLL + gsoLS.dot(x) + x.T.dot(-gsoLS.T) \ + + x.T.dot(gsoSS.dot(x)) + fso2e[2] = reduce(numpy.dot, (rp.T, fso2e[2], rp)) + return fso2e + + +def get_fso2e_2(mol, x, rp, pLL, pLS, pSS): + ''' Two-electron x2c operator with memory saving strategy ''' + nb = mol.nao_nr() + np = nb * nb + + fso2e = numpy.zeros((3, nb, nb)) + gsoLL = numpy.zeros((3, nb, nb)) + gsoLS = numpy.zeros((3, nb, nb)) + gsoSS = numpy.zeros((3, nb, nb)) + + from pyscf.gto import moleintor + nbas = mol.nbas + max_double = mol.max_memory / 8.0 * 1.0e6 + max_basis = pow(max_double / 9., 1. / 4.) + ao_loc_orig = moleintor.make_loc(mol._bas, 'int2e_ip1_ip2_sph') + shl_size = [] + shl_slice = [0] + ao_loc = [0] + print(nb, nbas) + print(ao_loc_orig) + if nb > max_basis: + for i in range(0, nbas - 1): + if (ao_loc_orig[i + 1] - ao_loc[-1] > max_basis and ao_loc_orig[i] - ao_loc[-1]): + ao_loc.append(ao_loc_orig[i]) + shl_size.append(ao_loc[-1] - ao_loc[-2]) + shl_slice.append(i) + if ao_loc[-1] is not ao_loc_orig[-1]: + ao_loc.append(ao_loc_orig[-1]) + shl_size.append(ao_loc[-1] - ao_loc[-2]) + shl_slice.append(nbas) + print(ao_loc, shl_size, shl_slice) + nbas = len(shl_size) + + for i in range(0, nbas): + for j in range(0, nbas): + for k in range(0, nbas): + for l in range(0, nbas): + ii = shl_slice[i] + iii = shl_slice[i + 1] + jj = shl_slice[j] + jjj = shl_slice[j + 1] + kk = shl_slice[k] + kkk = shl_slice[k + 1] + ll = shl_slice[l] + lll = shl_slice[l + 1] + start = time.clock() + ddint = mol.intor('int2e_ip1ip2_sph', comp=9, shls_slice=[ii, iii, jj, jjj, kk, kkk, ll, + lll]).reshape(3, 3, -1) + kint = numpy.zeros(3 * shl_size[i] * shl_size[j] * shl_size[k] * shl_size[l]).reshape( + 3, shl_size[i], shl_size[j], shl_size[k], shl_size[l]) + kint[0] = (ddint[1, 2] - ddint[2, 1]).reshape(shl_size[i], shl_size[j], shl_size[k], shl_size[l]) + kint[1] = (ddint[2, 0] - ddint[2, 1]).reshape(shl_size[i], shl_size[j], shl_size[k], shl_size[l]) + kint[2] = (ddint[0, 1] - ddint[1, 0]).reshape(shl_size[i], shl_size[j], shl_size[k], shl_size[l]) + end = time.clock() + print("Time elapsed for integral calculation:", end - start, i, j, k, l, nbas) + + start = time.clock() + gsoLL[:, ao_loc[j]:ao_loc[j+1], ao_loc[l]:ao_loc[l+1]] \ + +=-2.0*numpy.einsum('ilmkn,lk->imn', kint, \ + pSS[ao_loc[i]:ao_loc[i+1], ao_loc[k]:ao_loc[k+1]]) + gsoLS[:, ao_loc[i]:ao_loc[i+1], ao_loc[l]:ao_loc[l+1]] \ + +=-1.0*numpy.einsum('imlkn,lk->imn', kint, \ + pLS[ao_loc[j]:ao_loc[j+1], ao_loc[k]:ao_loc[k+1]]) + gsoLS[:, ao_loc[j]:ao_loc[j+1], ao_loc[l]:ao_loc[l+1]] \ + +=-1.0*numpy.einsum('ilmkn,lk->imn', kint, \ + pLS[ao_loc[i]:ao_loc[i+1], ao_loc[k]:ao_loc[k+1]]) + gsoSS[:, ao_loc[i]:ao_loc[i+1], ao_loc[j]:ao_loc[j+1]] \ + +=-2.0*numpy.einsum('imnkl,lk->imn', kint, \ + pLL[ao_loc[l]:ao_loc[l+1], ao_loc[k]:ao_loc[k+1]])\ + -2.0*numpy.einsum('imnlk,lk->imn', kint, \ + pLL[ao_loc[k]:ao_loc[k+1], ao_loc[l]:ao_loc[l+1]]) + gsoSS[:, ao_loc[i]:ao_loc[i+1], ao_loc[k]:ao_loc[k+1]] \ + += 2.0*numpy.einsum('imlnk,lk->imn', kint, \ + pLL[ao_loc[j]:ao_loc[j+1], ao_loc[l]:ao_loc[l+1]]) + print(" Time elapsed for einsum:", time.clock() - start) + + for comp in range(0, 3): + fso2e[comp] = gsoLL[comp] + gsoLS[comp].dot(x) + x.T.dot(-gsoLS[comp].T) + x.T.dot(gsoSS[comp].dot(x)) + fso2e[comp] = reduce(numpy.dot, (rp.T, fso2e[comp], rp)) + + return fso2e + + +def get_fso2e_1c(mol, x, rp, pLL, pLS, pSS): + ''' Function for x2c Hamiltonian with one-center approximation ''' + nb = mol.nao_nr() + np = nb * nb + nq = np * np + fso2e = numpy.zeros((3, nb, nb)) + gsoLL = numpy.zeros((3, nb, nb)) + gsoLS = numpy.zeros((3, nb, nb)) + gsoSS = numpy.zeros((3, nb, nb)) + + from pyscf.gto import moleintor + nbas = mol.nbas + max_double = mol.max_memory / 8.0 * 1.0e6 + max_basis = pow(max_double / 9., 1. / 4.) + ao_loc_orig = moleintor.make_loc(mol._bas, 'int2e_ip1_ip2_sph') + shl_size = [] + shl_slice = [0] + ao_loc = [0] + + ao_slice_by_atom = mol.aoslice_by_atom() + for slice in ao_slice_by_atom: + shl_slice.append(slice[1]) + shl_size.append(slice[3] - slice[2]) + ao_loc.append(slice[3]) + natom = len(ao_slice_by_atom) + + for iatom in range(0, natom): + ibegin = shl_slice[iatom] + iend = shl_slice[iatom + 1] + start = time.clock() + ddint = mol.intor('int2e_ip1ip2_sph', + comp=9, + shls_slice=[ibegin, iend, ibegin, iend, ibegin, iend, ibegin, iend]).reshape(3, 3, -1) + kint = numpy.zeros(3 * shl_size[iatom] * shl_size[iatom] * shl_size[iatom] * shl_size[iatom]).reshape( + 3, shl_size[iatom], shl_size[iatom], shl_size[iatom], shl_size[iatom]) + kint[0] = (ddint[1, 2] - ddint[2, 1]).reshape(shl_size[iatom], shl_size[iatom], shl_size[iatom], + shl_size[iatom]) + kint[1] = (ddint[2, 0] - ddint[2, 1]).reshape(shl_size[iatom], shl_size[iatom], shl_size[iatom], + shl_size[iatom]) + kint[2] = (ddint[0, 1] - ddint[1, 0]).reshape(shl_size[iatom], shl_size[iatom], shl_size[iatom], + shl_size[iatom]) + start = time.clock() + gsoLL[:, ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]] \ + +=-2.0*numpy.einsum('ilmkn,lk->imn', kint, \ + pSS[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]]) + gsoLS[:, ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]] \ + +=-1.0*numpy.einsum('imlkn,lk->imn', kint, \ + pLS[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]]) + gsoLS[:, ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]] \ + +=-1.0*numpy.einsum('ilmkn,lk->imn', kint, \ + pLS[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]]) + gsoSS[:, ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]] \ + +=-2.0*numpy.einsum('imnkl,lk->imn', kint, \ + pLL[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]])\ + -2.0*numpy.einsum('imnlk,lk->imn', kint, \ + pLL[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]]) + gsoSS[:, ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]] \ + += 2.0*numpy.einsum('imlnk,lk->imn', kint, \ + pLL[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]]) + print(" Time elapsed for einsum:", time.clock() - start) + for comp in range(0, 3): + fso2e[comp] = gsoLL[comp] + gsoLS[comp].dot(x) + x.T.dot(-gsoLS[comp].T) + x.T.dot(gsoSS[comp].dot(x)) + fso2e[comp] = reduce(numpy.dot, (rp.T, fso2e[comp], rp)) + + return fso2e + + +def get_hso1e_bp(mol, dm): + ''' Two-electron bp operator ''' + nb = mol.nao_nr() + np = nb * nb + nq = np * np + + hso1e = numpy.zeros(3 * nb * nb).reshape(3, nb, nb) + from pyscf.gto import moleintor + nbas = mol.nbas + max_double = mol.max_memory / 8.0 * 1.0e6 + max_basis = pow(max_double / 3., 1. / 4.) + ao_loc_orig = moleintor.make_loc(mol._bas, 'cint2e_p1vxp1_sph') + shl_size = [] + shl_slice = [0] + ao_loc = [0] + if nb > max_basis: + for i in range(0, nbas - 1): + if (ao_loc_orig[i + 1] - ao_loc[-1] > max_basis and ao_loc_orig[i] - ao_loc[-1]): + ao_loc.append(ao_loc_orig[i]) + shl_size.append(ao_loc[-1] - ao_loc[-2]) + shl_slice.append(i) + if ao_loc[-1] is not ao_loc_orig[-1]: + ao_loc.append(ao_loc_orig[-1]) + shl_size.append(ao_loc[-1] - ao_loc[-2]) + shl_slice.append(nbas) + nshell = nbas + nbas = len(shl_size) + + for i in range(0, nbas): + for j in range(0, nbas): + for k in range(0, nbas): + for l in range(0, nbas): + ii = shl_slice[i] + iii = shl_slice[i + 1] + jj = shl_slice[j] + jjj = shl_slice[j + 1] + kk = shl_slice[k] + kkk = shl_slice[k + 1] + ll = shl_slice[l] + lll = shl_slice[l + 1] + start = time.clock() + h2ao = mol.intor('cint2e_p1vxp1_sph', + comp=3, + aosym='s1', + shls_slice=[ii, iii, jj, jjj, kk, kkk, ll, + lll]).reshape(3, shl_size[i], shl_size[j], shl_size[k], shl_size[l]) + end = time.clock() + print("Time elapsed for integral calculation:", end - start, i, j, k, l, nbas) + start = time.clock() + hso1e[:, ao_loc[i]:ao_loc[i+1], ao_loc[j]:ao_loc[j+1]] \ + += 1.0*numpy.einsum('ijklm, lm->ijk', h2ao, \ + dm[ao_loc[k]:ao_loc[k+1], ao_loc[l]:ao_loc[l+1]]) + hso1e[:, ao_loc[i]:ao_loc[i+1], ao_loc[l]:ao_loc[l+1]] \ + +=-1.5*numpy.einsum('ijklm, kl->ijm', h2ao, \ + dm[ao_loc[j]:ao_loc[j+1], ao_loc[k]:ao_loc[k+1]]) + hso1e[:, ao_loc[k]:ao_loc[k+1], ao_loc[j]:ao_loc[j+1]] \ + += -1.5*numpy.einsum('ijklm, mj->ilk', h2ao, \ + dm[ao_loc[l]:ao_loc[l+1], ao_loc[i]:ao_loc[i+1]]) + return hso1e + + +def get_hso1e_bp1c(mol, dm, atomlist): + ''' Two electron bp operator with one-center approximation ''' + nb = mol.nao_nr() + np = nb * nb + + hso1e = numpy.zeros(3 * nb * nb).reshape(3, nb, nb) + from pyscf.gto import moleintor + nbas = mol.nbas + max_double = mol.max_memory / 8.0 * 1.0e6 + max_basis = pow(max_double / 3., 1. / 4.) + shl_size = [] + shl_slice = [0] + ao_loc = [0] + ao_slice_by_atom = mol.aoslice_by_atom() + for slice in ao_slice_by_atom: + shl_slice.append(slice[1]) + shl_size.append(slice[3] - slice[2]) + ao_loc.append(slice[3]) + + for iatom in atomlist: + if shl_size[iatom] > max_basis: + print("TODO: split the basis with atom to deal with very heavy element") + else: + ibegin = shl_slice[iatom] + iend = shl_slice[iatom + 1] + start = time.clock() + h2ao = mol.intor('cint2e_p1vxp1_sph', + comp=3, + aosym='s1', + shls_slice=[ibegin, iend, ibegin, iend, ibegin, iend, ibegin, + iend]).reshape(3, shl_size[iatom], shl_size[iatom], shl_size[iatom], + shl_size[iatom]) + end = time.clock() + print("Time elapsed for integral calculation:", end - start, iatom, nbas) + start = time.clock() + hso1e[:, ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]] \ + += 1.0*numpy.einsum('ijklm, lm->ijk', h2ao, \ + dm[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]]) + hso1e[:, ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]] \ + +=-1.5*numpy.einsum('ijklm, kl->ijm', h2ao, \ + dm[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]]) + hso1e[:, ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]] \ + += -1.5*numpy.einsum('ijklm, mj->ilk', h2ao, \ + dm[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]]) + return hso1e + + +def writeGTensorIntegrals(mc, atomlist=None): + mol = mc.mol + ncore, ncas = mc.ncore, mc.ncas + nb = mol.nao_nr() + if atomlist is None: + h1ao = mol.intor('cint1e_cg_irxp_sph', comp=3) + else: + h1ao = numpy.zeros((3, nb, nb)) + aoslice = mc.mol.aoslice_by_atom() + for iatom in atomlist: + for jatom in atomlist: + iao_start = aoslice[iatom, 2] + jao_start = aoslice[jatom, 2] + iao_end = aoslice[iatom, 3] + jao_end = aoslice[jatom, 3] + ishl_start = aoslice[iatom, 0] + jshl_start = aoslice[jatom, 0] + ishl_end = aoslice[iatom, 1] + jshl_end = aoslice[jatom, 1] + h1ao[:, iao_start : iao_end, jao_start : jao_end]\ + += mol.intor('cint1e_cg_irxp_sph', 3, shls_slice=[ishl_start, ishl_end, jshl_start, jshl_end]).reshape(3, iao_end-iao_start, jao_end-jao_start) + + h1 = numpy.einsum('xpq, pi, qj->xij', h1ao, mc.mo_coeff, mc.mo_coeff) + print1Int(h1[:, ncore:ncore + ncas, ncore:ncore + ncas], 'GTensor') + + +def writeSOCIntegrals(mc, + ncasorbs=None, + rdm1=None, + pictureChange1e="bp", + pictureChange2e="bp", + uncontract=True, + atomlist=None): + + LIGHT_SPEED = 137.0359895000 + alpha = 1.0 / LIGHT_SPEED + + if ("bp" in pictureChange1e and "bp" in pictureChange2e): + uncontract = False + + if (uncontract): + xmol, contr_coeff = x2c.X2C().get_xmol(mc.mol) + else: + xmol, contr_coeff = mc.mol, numpy.eye(mc.mo_coeff.shape[0]) + + if (atomlist is None): + atomlist = numpy.linspace(0, xmol.natm - 1, xmol.natm, dtype=int) + print(atomlist) + rdm1ao = rdm1 + if (rdm1 is None): + rdm1ao = 1. * mc.make_rdm1() + if len(rdm1ao.shape) > 2: rdm1ao = (rdm1ao[0] + rdm1ao[1]) + + if (uncontract): + dm = reduce(numpy.dot, (contr_coeff, rdm1ao, contr_coeff.T)) + else: + dm = 1. * rdm1ao + np, nc = contr_coeff.shape[0], contr_coeff.shape[1] + + hso1e = numpy.zeros((3, np, np)) + + if ("x2c" in pictureChange1e or "x2c" in pictureChange2e): + h1e_1c, x, rp = get_hxr(mc, uncontract=uncontract) + pLL, pLS, pSS = get_p(dm / 2.0, x, rp) + + #two electron terms + if (pictureChange2e == "bp"): + hso1e += -(alpha)**2 * 0.5 * get_hso1e_bp(xmol, dm) + elif (pictureChange2e == "bp1c"): + hso1e += -(alpha)**2 * 0.5 * get_hso1e_bp1c(xmol, dm, atomlist) + elif (pictureChange2e == "x2c"): + dm1 = dm * 0.5 + pLL, pLS, pSS = get_p(dm1, x, rp) + hso1e += -(alpha)**2 * 0.5 * get_fso2e_2(xmol, x, rp, pLL, pLS, pSS) + elif (pictureChange2e == "x2c1c"): + pLL, pLS, pSS = get_p(dm / 2.0, x, rp) + hso1e += -(alpha)**2 * 0.5 * get_fso2e_1c(xmol, x, rp, pLL, pLS, pSS) + elif (pictureChange2e == "none"): + hso1e += 0.0 + else: + print(pictureChange2e, "not a valid option") + exit(0) + + #MF 1 electron term + if (pictureChange1e == "bp"): + hso1e += (alpha)**2 * 0.5 * get_wso(xmol) + elif (pictureChange1e == "bp1c"): + hso1e += (alpha)**2 * 0.5 * get_wso_1c(xmol, atomlist) + elif (pictureChange1e == "x2c1"): + wso = (alpha)**2 * 0.5 * get_wso(xmol) + hso1e += get_hso1e(wso, x, rp) + elif (pictureChange1e == "x2cn"): + h1e_2c = (alpha)**2 * 0.5 * x2c.get_hcore(xmol) + + for i in range(np): + for j in range(np): + hso1e[0][i, j] -= h1e_2c[2 * i, 2 * j + 1].imag * 2. + hso1e[1][i, j] -= h1e_2c[2 * i, 2 * j + 1].real * 2. + hso1e[2][i, j] -= h1e_2c[2 * i, 2 * j].imag * 2. + elif (pictureChange1e == "none"): + hso1e += 0.0 + else: + print(pictureChange1e, "not a valid option") + exit(0) + + h1ao = numpy.zeros((3, nc, nc)) + if (uncontract): + for ic in range(3): + h1ao[ic] = reduce(numpy.dot, (contr_coeff.T, hso1e[ic], contr_coeff)) + else: + h1ao = 1. * hso1e + + ncore, ncas = mc.ncore, mc.ncas + if (ncasorbs is not None): + ncas = ncasorbs + mo_coeff = mc.mo_coeff + h1 = numpy.einsum('xpq,pi,qj->xij', h1ao, mo_coeff, mo_coeff)[:, ncore:ncore + ncas, ncore:ncore + ncas] + print1Int(h1, 'SOC') + + +def doSOC(mc, pictureChange1e = "bp", pictureChange2e = "bp1c", uncontract = False, atomlist = None, FCIDUMP = True): + if (FCIDUMP): + mch = shci.SHCISCF(mc.mol, mc.norb, mc.nelec) + mch.fcisolver.onlywriteIntegral = False + shci.dryrun(mch, mc.mo_coeff) + writeGTensorIntegrals(mc, atomlist = atomlist) + writeSOCIntegrals(mc, pictureChange1e = pictureChange1e, pictureChange2e = pictureChange2e, uncontract = uncontract, atomlist = atomlist) + From 4765d1d35f9594d766781c64de7ad47dde4d1453 Mon Sep 17 00:00:00 2001 From: Nikolay Bogdanov Date: Thu, 15 Aug 2019 14:37:46 +0200 Subject: [PATCH 02/26] Fix typo in kint[1], better print1Int and use LIGHT_SPEED from lib --- pyscf/shciscf/socutils.py | 96 ++++++++++++--------------------------- 1 file changed, 28 insertions(+), 68 deletions(-) diff --git a/pyscf/shciscf/socutils.py b/pyscf/shciscf/socutils.py index 4f0e6df4ca..b9839d41c7 100644 --- a/pyscf/shciscf/socutils.py +++ b/pyscf/shciscf/socutils.py @@ -11,33 +11,15 @@ def print1Int(h1, name): - with open('%s.X' % (name), 'w') as fout: - fout.write('%d\n' % h1[0].shape[0]) - for i in range(h1[0].shape[0]): - for j in range(h1[0].shape[0]): - if (abs(h1[0, i, j]) > 1.e-8): - fout.write('%16.10g %4d %4d\n' % (h1[0, i, j], i + 1, j + 1)) - - with open('%s.Y' % (name), 'w') as fout: - fout.write('%d\n' % h1[1].shape[0]) - for i in range(h1[1].shape[0]): - for j in range(h1[1].shape[0]): - if (abs(h1[1, i, j]) > 1.e-8): - fout.write('%16.10g %4d %4d\n' % (h1[1, i, j], i + 1, j + 1)) - - with open('%s.Z' % (name), 'w') as fout: - fout.write('%d\n' % h1[2].shape[0]) - for i in range(h1[2].shape[0]): - for j in range(h1[2].shape[0]): - if (abs(h1[2, i, j]) > 1.e-8): - fout.write('%16.10g %4d %4d\n' % (h1[2, i, j], i + 1, j + 1)) - - with open('%sZ' % (name), 'w') as fout: - fout.write('%d\n' % h1[2].shape[0]) - for i in range(h1[2].shape[0]): - for j in range(h1[2].shape[0]): - if (abs(h1[2, i, j]) > 1.e-8): - fout.write('%16.10g %4d %4d\n' % (h1[2, i, j], i + 1, j + 1)) + xyz = ["X", "Y", "Z"] + for k in range(3): + with open('%s.' % (name) + xyz[k], 'w') as fout: + fout.write('%d\n' % h1[k].shape[0]) + for i in range(h1[k].shape[0]): + for j in range(h1[k].shape[0]): + if (abs(h1[k, i, j]) > 1.e-8): + fout.write( + '%16.10g %4d %4d\n' % (h1[k, i, j], i + 1, j + 1)) #by default h is returned in the contracted basis @@ -151,41 +133,21 @@ def get_fso2e_0(mol, x, rp, pLL, pLS, pSS): ddint = mol.intor('int2e_ip1ip2_sph', 9).reshape(3, 3, nq) fso2e = numpy.zeros((3, nb, nb)) - ddint[0, 0] = ddint[1, 2] - ddint[2, 1] - kint = ddint[0, 0].reshape(nb, nb, nb, nb) - gsoLL = -2.0 * numpy.einsum('lmkn,lk->mn', kint, pSS) - gsoLS = -1.0*numpy.einsum('mlkn,lk->mn',kint,pLS) \ - -1.0*numpy.einsum('lmkn,lk->mn',kint,pLS) - gsoSS = -2.0*numpy.einsum('mnkl,lk',kint,pLL) \ - -2.0*numpy.einsum('mnlk,lk',kint,pLL) \ - +2.0*numpy.einsum('mlnk,lk',kint,pLL) - fso2e[0] = gsoLL + gsoLS.dot(x) + x.T.dot(-gsoLS.T) \ - + x.T.dot(gsoSS.dot(x)) - fso2e[0] = reduce(numpy.dot, (rp.T, fso2e[0], rp)) - - ddint[0, 0] = ddint[2, 0] - ddint[2, 1] - kint = ddint[0, 0].reshape(nb, nb, nb, nb) - gsoLL = -2.0 * numpy.einsum('lmkn,lk->mn', kint, pSS) - gsoLS = -1.0*numpy.einsum('mlkn,lk->mn',kint,pLS) \ - -1.0*numpy.einsum('lmkn,lk->mn',kint,pLS) - gsoSS = -2.0*numpy.einsum('mnkl,lk',kint,pLL) \ - -2.0*numpy.einsum('mnlk,lk',kint,pLL) \ - +2.0*numpy.einsum('mlnk,lk',kint,pLL) - fso2e[1] = gsoLL + gsoLS.dot(x) + x.T.dot(-gsoLS.T) \ - + x.T.dot(gsoSS.dot(x)) - fso2e[1] = reduce(numpy.dot, (rp.T, fso2e[1], rp)) - - ddint[0, 0] = ddint[0, 1] - ddint[1, 0] - kint = ddint[0, 0].reshape(nb, nb, nb, nb) - gsoLL = -2.0 * numpy.einsum('lmkn,lk->mn', kint, pSS) - gsoLS = -1.0*numpy.einsum('mlkn,lk->mn',kint,pLS) \ - -1.0*numpy.einsum('lmkn,lk->mn',kint,pLS) - gsoSS = -2.0*numpy.einsum('mnkl,lk',kint,pLL) \ - -2.0*numpy.einsum('mnlk,lk',kint,pLL) \ - +2.0*numpy.einsum('mlnk,lk',kint,pLL) - fso2e[2] = gsoLL + gsoLS.dot(x) + x.T.dot(-gsoLS.T) \ - + x.T.dot(gsoSS.dot(x)) - fso2e[2] = reduce(numpy.dot, (rp.T, fso2e[2], rp)) + xyz = [0, 1, 2] + for i_x in xyz: + i_y = xyz[i_x - 2] + i_z = xyz[i_x - 1] + ddint[0, 0] = ddint[i_y, i_z] - ddint[i_z, i_y] # x = yz - zy etc + kint = ddint[0, 0].reshape(nb, nb, nb, nb) + gsoLL = -2.0 * numpy.einsum('lmkn,lk->mn', kint, pSS) + gsoLS = -numpy.einsum('mlkn,lk->mn',kint,pLS) \ + -numpy.einsum('lmkn,lk->mn',kint,pLS) + gsoSS = -2.0*numpy.einsum('mnkl,lk',kint,pLL) \ + -2.0*numpy.einsum('mnlk,lk',kint,pLL) \ + +2.0*numpy.einsum('mlnk,lk',kint,pLL) + fso2e[i_x] = gsoLL + gsoLS.dot(x) + x.T.dot(-gsoLS.T) \ + + x.T.dot(gsoSS.dot(x)) + fso2e[i_x] = reduce(numpy.dot, (rp.T, fso2e[i_x], rp)) return fso2e @@ -240,7 +202,7 @@ def get_fso2e_2(mol, x, rp, pLL, pLS, pSS): kint = numpy.zeros(3 * shl_size[i] * shl_size[j] * shl_size[k] * shl_size[l]).reshape( 3, shl_size[i], shl_size[j], shl_size[k], shl_size[l]) kint[0] = (ddint[1, 2] - ddint[2, 1]).reshape(shl_size[i], shl_size[j], shl_size[k], shl_size[l]) - kint[1] = (ddint[2, 0] - ddint[2, 1]).reshape(shl_size[i], shl_size[j], shl_size[k], shl_size[l]) + kint[1] = (ddint[2, 0] - ddint[0, 2]).reshape(shl_size[i], shl_size[j], shl_size[k], shl_size[l]) kint[2] = (ddint[0, 1] - ddint[1, 0]).reshape(shl_size[i], shl_size[j], shl_size[k], shl_size[l]) end = time.clock() print("Time elapsed for integral calculation:", end - start, i, j, k, l, nbas) @@ -309,7 +271,7 @@ def get_fso2e_1c(mol, x, rp, pLL, pLS, pSS): 3, shl_size[iatom], shl_size[iatom], shl_size[iatom], shl_size[iatom]) kint[0] = (ddint[1, 2] - ddint[2, 1]).reshape(shl_size[iatom], shl_size[iatom], shl_size[iatom], shl_size[iatom]) - kint[1] = (ddint[2, 0] - ddint[2, 1]).reshape(shl_size[iatom], shl_size[iatom], shl_size[iatom], + kint[1] = (ddint[2, 0] - ddint[0, 2]).reshape(shl_size[iatom], shl_size[iatom], shl_size[iatom], shl_size[iatom]) kint[2] = (ddint[0, 1] - ddint[1, 0]).reshape(shl_size[iatom], shl_size[iatom], shl_size[iatom], shl_size[iatom]) @@ -481,7 +443,7 @@ def writeSOCIntegrals(mc, uncontract=True, atomlist=None): - LIGHT_SPEED = 137.0359895000 + from pyscf.lib.parameters import LIGHT_SPEED alpha = 1.0 / LIGHT_SPEED if ("bp" in pictureChange1e and "bp" in pictureChange2e): @@ -510,7 +472,6 @@ def writeSOCIntegrals(mc, if ("x2c" in pictureChange1e or "x2c" in pictureChange2e): h1e_1c, x, rp = get_hxr(mc, uncontract=uncontract) - pLL, pLS, pSS = get_p(dm / 2.0, x, rp) #two electron terms if (pictureChange2e == "bp"): @@ -518,8 +479,7 @@ def writeSOCIntegrals(mc, elif (pictureChange2e == "bp1c"): hso1e += -(alpha)**2 * 0.5 * get_hso1e_bp1c(xmol, dm, atomlist) elif (pictureChange2e == "x2c"): - dm1 = dm * 0.5 - pLL, pLS, pSS = get_p(dm1, x, rp) + pLL, pLS, pSS = get_p(dm / 2.0, x, rp) hso1e += -(alpha)**2 * 0.5 * get_fso2e_2(xmol, x, rp, pLL, pLS, pSS) elif (pictureChange2e == "x2c1c"): pLL, pLS, pSS = get_p(dm / 2.0, x, rp) From 3864a5ce206874c1a0d7d33a5b097963b6bfbda5 Mon Sep 17 00:00:00 2001 From: xubwa Date: Wed, 11 Sep 2019 00:03:19 -0600 Subject: [PATCH 03/26] decorative change --- pyscf/shciscf/shci.py | 88 ++++-------- pyscf/shciscf/socutils.py | 292 ++++++++++++++++++-------------------- 2 files changed, 166 insertions(+), 214 deletions(-) diff --git a/pyscf/shciscf/shci.py b/pyscf/shciscf/shci.py index 38804bbb55..7135fc2839 100644 --- a/pyscf/shciscf/shci.py +++ b/pyscf/shciscf/shci.py @@ -135,6 +135,7 @@ class SHCI(pyscf.lib.StreamObject): nroots: int nPTiter: int DoRDM: bool + DoSOC: bool sweep_iter: [int] sweep_epsilon: [float] initialStates: [[int]] @@ -162,6 +163,7 @@ def __init__(self, mol=None, maxM=None, tol=None, num_thrds=1, self.outputlevel = 2 self.executable = settings.SHCIEXE + self.executableZDice2 = settings.ZSHCIEXE self.scratchDirectory = settings.SHCISCRATCHDIR self.mpiprefix = settings.MPIPREFIX self.memory = memory @@ -196,6 +198,7 @@ def __init__(self, mol=None, maxM=None, tol=None, num_thrds=1, self.nroots = 1 self.nPTiter = 0 self.DoRDM = True + self.DoSOC = False self.sweep_iter = [] self.sweep_epsilon = [] self.maxIter = 6 @@ -809,8 +812,10 @@ def kernel(self, h1e, eri, norb, nelec, fciRestart=None, ecore=0, return calc_e, roots if self.returnInt: return h1e, eri - - executeSHCI(self) + if self.DoSOC: + executeZSHCI(self) + else: + executeSHCI(self) if self.verbose >= logger.DEBUG1: outFile = os.path.join(self.runtimeDir, self.outputFile) logger.debug1(self, open(outFile).read()) @@ -881,40 +886,6 @@ def cleanup_dice_files(self): os.remove("FCIDUMP") -def print1Int(h1, name): - with open('%s.X' % (name), 'w') as fout: - fout.write('%d\n' % h1[0].shape[0]) - for i in range(h1[0].shape[0]): - for j in range(h1[0].shape[0]): - if (abs(h1[0, i, j]) > 1.e-8): - fout.write( - '%16.10g %4d %4d\n' % (h1[0, i, j], i + 1, j + 1)) - - with open('%s.Y' % (name), 'w') as fout: - fout.write('%d\n' % h1[1].shape[0]) - for i in range(h1[1].shape[0]): - for j in range(h1[1].shape[0]): - if (abs(h1[1, i, j]) > 1.e-8): - fout.write( - '%16.10g %4d %4d\n' % (h1[1, i, j], i + 1, j + 1)) - - with open('%s.Z' % (name), 'w') as fout: - fout.write('%d\n' % h1[2].shape[0]) - for i in range(h1[2].shape[0]): - for j in range(h1[2].shape[0]): - if (abs(h1[2, i, j]) > 1.e-8): - fout.write( - '%16.10g %4d %4d\n' % (h1[2, i, j], i + 1, j + 1)) - - with open('%sZ' % (name), 'w') as fout: - fout.write('%d\n' % h1[2].shape[0]) - for i in range(h1[2].shape[0]): - for j in range(h1[2].shape[0]): - if (abs(h1[2, i, j]) > 1.e-8): - fout.write( - '%16.10g %4d %4d\n' % (h1[2, i, j], i + 1, j + 1)) - - def make_sched(SHCI): nIter = len(SHCI.sweep_iter) @@ -1039,6 +1010,8 @@ def writeSHCIConfFile(SHCI, nelec, Restart): f.write('prefix %s\n' % (SHCI.scratchDirectory)) if (SHCI.DoRDM): f.write('DoRDM\n') + if(SHCI.DoSOC): + f.write('DoSOC\n') for line in SHCI.extraline: f.write('%s\n' % line) @@ -1227,6 +1200,22 @@ def executeSHCI(SHCI): raise err +def executeZSHCI(SHCI): + file1 = os.path.join(SHCI.runtimeDir, "%s/shci.e" % (SHCI.scratchDirectory))#what? + if os.path.exists(file1): #what? + os.remove(file1) #what? + inFile = os.path.join(SHCI.runtimeDir, SHCI.configFile) + outFile = os.path.join(SHCI.runtimeDir, SHCI.outputFile) + try: + cmd = ' '.join((SHCI.mpiprefix, SHCI.executableZDice2, inFile)) + cmd = "%s > %s 2>&1" % (cmd, outFile) + check_call(cmd, shell=True) + #save_output(SHCI) + except CalledProcessError as err: + logger.error(SHCI, cmd) + raise err + + #def save_output(SHCI): # for i in range(50): # if os.path.exists(os.path.join(SHCI.runtimeDir, "output%02d.dat"%(i))): @@ -1279,33 +1268,6 @@ def SHCISCF(mf, norb, nelec, maxM=1000, tol=1.e-8, *args, **kwargs): return mc -def get_hso1e(wso, x, rp): - nb = x.shape[0] - hso1e = numpy.zeros((3, nb, nb)) - for ic in range(3): - hso1e[ic] = reduce(numpy.dot, (rp.T, x.T, wso[ic], x, rp)) - return hso1e - - -def get_wso(mol): - nb = mol.nao_nr() - wso = numpy.zeros((3, nb, nb)) - for iatom in range(mol.natm): - zA = mol.atom_charge(iatom) - xyz = mol.atom_coord(iatom) - mol.set_rinv_orig(xyz) - wso += zA * mol.intor('cint1e_prinvxp_sph', - 3) # sign due to integration by part - return wso - - -def get_p(dm, x, rp): - pLL = rp.dot(dm.dot(rp.T)) - pLS = pLL.dot(x.T) - pSS = x.dot(pLL.dot(x.T)) - return pLL, pLS, pSS - - def dryrun(mc, mo_coeff=None): '''Generate FCIDUMP and SHCI config file''' if mo_coeff is None: diff --git a/pyscf/shciscf/socutils.py b/pyscf/shciscf/socutils.py index b9839d41c7..74c1022a71 100644 --- a/pyscf/shciscf/socutils.py +++ b/pyscf/shciscf/socutils.py @@ -22,8 +22,8 @@ def print1Int(h1, name): '%16.10g %4d %4d\n' % (h1[k, i, j], i + 1, j + 1)) -#by default h is returned in the contracted basis -#x and r in the uncontracted basis +# by default h is returned in the contracted basis +# x and r in the uncontracted basis def get_hxr(mc, uncontract=True): if (uncontract): xmol, contr_coeff = x2c.X2C().get_xmol(mc.mol) @@ -31,7 +31,6 @@ def get_hxr(mc, uncontract=True): xmol, contr_coeff = mc.mol, numpy.eye(mc.mo_coeff.shape[0]) c = lib.param.LIGHT_SPEED - #assert('1E' in self.approx.upper()) t = xmol.intor_symmetric('int1e_kin') v = xmol.intor_symmetric('int1e_nuc') s = xmol.intor_symmetric('int1e_ovlp') @@ -96,7 +95,8 @@ def get_wso(mol): zA = mol.atom_charge(iatom) xyz = mol.atom_coord(iatom) mol.set_rinv_orig(xyz) - wso += zA * mol.intor('cint1e_prinvxp_sph', 3) # sign due to integration by part + # sign due to integration by part + wso += zA * mol.intor('cint1e_prinvxp_sph', 3) return wso @@ -112,8 +112,8 @@ def get_wso_1c(mol, atomlist): ao_end = aoslice[iatom, 3] shl_start = aoslice[iatom, 0] shl_end = aoslice[iatom, 1] - wso[:, ao_start : ao_end, ao_start : ao_end]\ - = zA*mol.intor('cint1e_prinvxp_sph', 3, shls_slice=[shl_start, shl_end, shl_start, shl_end]).reshape(3, ao_end-ao_start, ao_end-ao_start) + wso[:, ao_start: ao_end, ao_start: ao_end]\ + = zA*mol.intor('cint1e_prinvxp_sph', 3, shls_slice=[shl_start, shl_end, shl_start, shl_end]).reshape(3, ao_end-ao_start, ao_end-ao_start) return wso @@ -124,8 +124,9 @@ def get_p(dm, x, rp): return pLL, pLS, pSS -def get_fso2e_0(mol, x, rp, pLL, pLS, pSS): +def get_fso2e_x2c_original(mol, x, rp, pLL, pLS, pSS): ''' Function for x2c Hamiltonian without any memory saving ''' + # although this function is not used but just keep here for reference nb = mol.nao_nr() np = nb * nb nq = np * np @@ -137,24 +138,23 @@ def get_fso2e_0(mol, x, rp, pLL, pLS, pSS): for i_x in xyz: i_y = xyz[i_x - 2] i_z = xyz[i_x - 1] - ddint[0, 0] = ddint[i_y, i_z] - ddint[i_z, i_y] # x = yz - zy etc + ddint[0, 0] = ddint[i_y, i_z] - ddint[i_z, i_y] # x = yz - zy etc kint = ddint[0, 0].reshape(nb, nb, nb, nb) gsoLL = -2.0 * numpy.einsum('lmkn,lk->mn', kint, pSS) - gsoLS = -numpy.einsum('mlkn,lk->mn',kint,pLS) \ - -numpy.einsum('lmkn,lk->mn',kint,pLS) - gsoSS = -2.0*numpy.einsum('mnkl,lk',kint,pLL) \ - -2.0*numpy.einsum('mnlk,lk',kint,pLL) \ - +2.0*numpy.einsum('mlnk,lk',kint,pLL) + gsoLS = -1.0 * numpy.einsum('mlkn,lk->mn', kint, pLS) \ + - 1.0 * numpy.einsum('lmkn,lk->mn', kint, pLS) + gsoSS = -2.0 * numpy.einsum('mnkl,lk', kint, pLL) \ + - 2.0 * numpy.einsum('mnlk,lk', kint, pLL) \ + + 2.0 * numpy.einsum('mlnk,lk', kint, pLL) fso2e[i_x] = gsoLL + gsoLS.dot(x) + x.T.dot(-gsoLS.T) \ - + x.T.dot(gsoSS.dot(x)) + + x.T.dot(gsoSS.dot(x)) fso2e[i_x] = reduce(numpy.dot, (rp.T, fso2e[i_x], rp)) return fso2e -def get_fso2e_2(mol, x, rp, pLL, pLS, pSS): +def get_fso2e_x2c(mol, x, rp, pLL, pLS, pSS): ''' Two-electron x2c operator with memory saving strategy ''' nb = mol.nao_nr() - np = nb * nb fso2e = numpy.zeros((3, nb, nb)) gsoLL = numpy.zeros((3, nb, nb)) @@ -188,67 +188,69 @@ def get_fso2e_2(mol, x, rp, pLL, pLS, pSS): for j in range(0, nbas): for k in range(0, nbas): for l in range(0, nbas): - ii = shl_slice[i] - iii = shl_slice[i + 1] - jj = shl_slice[j] - jjj = shl_slice[j + 1] - kk = shl_slice[k] - kkk = shl_slice[k + 1] - ll = shl_slice[l] - lll = shl_slice[l + 1] - start = time.clock() - ddint = mol.intor('int2e_ip1ip2_sph', comp=9, shls_slice=[ii, iii, jj, jjj, kk, kkk, ll, - lll]).reshape(3, 3, -1) - kint = numpy.zeros(3 * shl_size[i] * shl_size[j] * shl_size[k] * shl_size[l]).reshape( - 3, shl_size[i], shl_size[j], shl_size[k], shl_size[l]) - kint[0] = (ddint[1, 2] - ddint[2, 1]).reshape(shl_size[i], shl_size[j], shl_size[k], shl_size[l]) - kint[1] = (ddint[2, 0] - ddint[0, 2]).reshape(shl_size[i], shl_size[j], shl_size[k], shl_size[l]) - kint[2] = (ddint[0, 1] - ddint[1, 0]).reshape(shl_size[i], shl_size[j], shl_size[k], shl_size[l]) - end = time.clock() - print("Time elapsed for integral calculation:", end - start, i, j, k, l, nbas) - - start = time.clock() + #start = time.clock() + ddint = mol.intor('int2e_ip1ip2_sph', comp=9, + shls_slice=[shl_slice[i], shl_slice[i+1], + shl_slice[j], shl_slice[j+1], + shl_slice[k], shl_slice[k+1], + shl_slice[l], shl_slice[l+1]]).reshape(3, 3, -1) + kint = numpy.zeros( + 3 * shl_size[i] * shl_size[j] * shl_size[k] * shl_size[l]).reshape( + 3, shl_size[i], shl_size[j], shl_size[k], shl_size[l]) + kint[0] = (ddint[1, 2] - ddint[2, 1]).reshape(shl_size[i], + shl_size[j], shl_size[k], shl_size[l]) + kint[1] = (ddint[2, 0] - ddint[0, 2]).reshape(shl_size[i], + shl_size[j], shl_size[k], shl_size[l]) + kint[2] = (ddint[0, 1] - ddint[1, 0]).reshape(shl_size[i], + shl_size[j], shl_size[k], shl_size[l]) + #end = time.clock() + # print("Time elapsed for integral calculation:", + # end - start, i, j, k, l, nbas) + + #start = time.clock() gsoLL[:, ao_loc[j]:ao_loc[j+1], ao_loc[l]:ao_loc[l+1]] \ - +=-2.0*numpy.einsum('ilmkn,lk->imn', kint, \ - pSS[ao_loc[i]:ao_loc[i+1], ao_loc[k]:ao_loc[k+1]]) + += -2.0*numpy.einsum('ilmkn,lk->imn', kint, + pSS[ao_loc[i]:ao_loc[i+1], ao_loc[k]:ao_loc[k+1]]) gsoLS[:, ao_loc[i]:ao_loc[i+1], ao_loc[l]:ao_loc[l+1]] \ - +=-1.0*numpy.einsum('imlkn,lk->imn', kint, \ - pLS[ao_loc[j]:ao_loc[j+1], ao_loc[k]:ao_loc[k+1]]) + += -1.0*numpy.einsum('imlkn,lk->imn', kint, + pLS[ao_loc[j]:ao_loc[j+1], ao_loc[k]:ao_loc[k+1]]) gsoLS[:, ao_loc[j]:ao_loc[j+1], ao_loc[l]:ao_loc[l+1]] \ - +=-1.0*numpy.einsum('ilmkn,lk->imn', kint, \ - pLS[ao_loc[i]:ao_loc[i+1], ao_loc[k]:ao_loc[k+1]]) + += -1.0*numpy.einsum('ilmkn,lk->imn', kint, + pLS[ao_loc[i]:ao_loc[i+1], ao_loc[k]:ao_loc[k+1]]) gsoSS[:, ao_loc[i]:ao_loc[i+1], ao_loc[j]:ao_loc[j+1]] \ - +=-2.0*numpy.einsum('imnkl,lk->imn', kint, \ - pLL[ao_loc[l]:ao_loc[l+1], ao_loc[k]:ao_loc[k+1]])\ - -2.0*numpy.einsum('imnlk,lk->imn', kint, \ - pLL[ao_loc[k]:ao_loc[k+1], ao_loc[l]:ao_loc[l+1]]) + += -2.0*numpy.einsum('imnkl,lk->imn', kint, + pLL[ao_loc[l]:ao_loc[l+1], ao_loc[k]:ao_loc[k+1]])\ + - 2.0*numpy.einsum('imnlk,lk->imn', kint, + pLL[ao_loc[k]:ao_loc[k+1], ao_loc[l]:ao_loc[l+1]]) gsoSS[:, ao_loc[i]:ao_loc[i+1], ao_loc[k]:ao_loc[k+1]] \ - += 2.0*numpy.einsum('imlnk,lk->imn', kint, \ - pLL[ao_loc[j]:ao_loc[j+1], ao_loc[l]:ao_loc[l+1]]) - print(" Time elapsed for einsum:", time.clock() - start) + += 2.0*numpy.einsum('imlnk,lk->imn', kint, + pLL[ao_loc[j]:ao_loc[j+1], ao_loc[l]:ao_loc[l+1]]) + #print(" Time elapsed for einsum:", time.clock() - start) for comp in range(0, 3): - fso2e[comp] = gsoLL[comp] + gsoLS[comp].dot(x) + x.T.dot(-gsoLS[comp].T) + x.T.dot(gsoSS[comp].dot(x)) + fso2e[comp] = gsoLL[comp] + + gsoLS[comp].dot(x) + + x.T.dot(-gsoLS[comp].T) + + x.T.dot(gsoSS[comp].dot(x)) fso2e[comp] = reduce(numpy.dot, (rp.T, fso2e[comp], rp)) return fso2e -def get_fso2e_1c(mol, x, rp, pLL, pLS, pSS): +def get_fso2e_x2c1c(mol, x, rp, pLL, pLS, pSS): ''' Function for x2c Hamiltonian with one-center approximation ''' nb = mol.nao_nr() - np = nb * nb - nq = np * np + #np = nb * nb fso2e = numpy.zeros((3, nb, nb)) gsoLL = numpy.zeros((3, nb, nb)) gsoLS = numpy.zeros((3, nb, nb)) gsoSS = numpy.zeros((3, nb, nb)) from pyscf.gto import moleintor - nbas = mol.nbas - max_double = mol.max_memory / 8.0 * 1.0e6 - max_basis = pow(max_double / 9., 1. / 4.) - ao_loc_orig = moleintor.make_loc(mol._bas, 'int2e_ip1_ip2_sph') + #nbas = mol.nbas + #max_double = mol.max_memory / 8.0 * 1.0e6 + #max_basis = pow(max_double / 9., 1. / 4.) + #ao_loc_orig = moleintor.make_loc(mol._bas, 'int2e_ip1_ip2_sph') shl_size = [] shl_slice = [0] ao_loc = [0] @@ -263,7 +265,7 @@ def get_fso2e_1c(mol, x, rp, pLL, pLS, pSS): for iatom in range(0, natom): ibegin = shl_slice[iatom] iend = shl_slice[iatom + 1] - start = time.clock() + #start = time.clock() ddint = mol.intor('int2e_ip1ip2_sph', comp=9, shls_slice=[ibegin, iend, ibegin, iend, ibegin, iend, ibegin, iend]).reshape(3, 3, -1) @@ -275,37 +277,37 @@ def get_fso2e_1c(mol, x, rp, pLL, pLS, pSS): shl_size[iatom]) kint[2] = (ddint[0, 1] - ddint[1, 0]).reshape(shl_size[iatom], shl_size[iatom], shl_size[iatom], shl_size[iatom]) - start = time.clock() + #start = time.clock() gsoLL[:, ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]] \ - +=-2.0*numpy.einsum('ilmkn,lk->imn', kint, \ - pSS[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]]) + += -2.0*numpy.einsum('ilmkn,lk->imn', kint, + pSS[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]]) gsoLS[:, ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]] \ - +=-1.0*numpy.einsum('imlkn,lk->imn', kint, \ - pLS[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]]) + += -1.0*numpy.einsum('imlkn,lk->imn', kint, + pLS[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]]) gsoLS[:, ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]] \ - +=-1.0*numpy.einsum('ilmkn,lk->imn', kint, \ - pLS[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]]) + += -1.0*numpy.einsum('ilmkn,lk->imn', kint, + pLS[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]]) gsoSS[:, ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]] \ - +=-2.0*numpy.einsum('imnkl,lk->imn', kint, \ - pLL[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]])\ - -2.0*numpy.einsum('imnlk,lk->imn', kint, \ - pLL[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]]) + += -2.0*numpy.einsum('imnkl,lk->imn', kint, + pLL[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]])\ + - 2.0*numpy.einsum('imnlk,lk->imn', kint, + pLL[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]]) gsoSS[:, ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]] \ - += 2.0*numpy.einsum('imlnk,lk->imn', kint, \ - pLL[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]]) - print(" Time elapsed for einsum:", time.clock() - start) + += 2.0*numpy.einsum('imlnk,lk->imn', kint, + pLL[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]]) + #print(" Time elapsed for einsum:", time.clock() - start) for comp in range(0, 3): - fso2e[comp] = gsoLL[comp] + gsoLS[comp].dot(x) + x.T.dot(-gsoLS[comp].T) + x.T.dot(gsoSS[comp].dot(x)) + fso2e[comp] = gsoLL[comp] + \ + gsoLS[comp].dot(x) + x.T.dot(-gsoLS[comp].T) + \ + x.T.dot(gsoSS[comp].dot(x)) fso2e[comp] = reduce(numpy.dot, (rp.T, fso2e[comp], rp)) return fso2e -def get_hso1e_bp(mol, dm): +def get_fso2e_bp(mol, dm): ''' Two-electron bp operator ''' nb = mol.nao_nr() - np = nb * nb - nq = np * np hso1e = numpy.zeros(3 * nb * nb).reshape(3, nb, nb) from pyscf.gto import moleintor @@ -326,50 +328,36 @@ def get_hso1e_bp(mol, dm): ao_loc.append(ao_loc_orig[-1]) shl_size.append(ao_loc[-1] - ao_loc[-2]) shl_slice.append(nbas) - nshell = nbas nbas = len(shl_size) for i in range(0, nbas): for j in range(0, nbas): for k in range(0, nbas): for l in range(0, nbas): - ii = shl_slice[i] - iii = shl_slice[i + 1] - jj = shl_slice[j] - jjj = shl_slice[j + 1] - kk = shl_slice[k] - kkk = shl_slice[k + 1] - ll = shl_slice[l] - lll = shl_slice[l + 1] - start = time.clock() - h2ao = mol.intor('cint2e_p1vxp1_sph', - comp=3, - aosym='s1', - shls_slice=[ii, iii, jj, jjj, kk, kkk, ll, - lll]).reshape(3, shl_size[i], shl_size[j], shl_size[k], shl_size[l]) - end = time.clock() - print("Time elapsed for integral calculation:", end - start, i, j, k, l, nbas) - start = time.clock() + h2ao = mol.intor('cint2e_p1vxp1_sph', comp=3, aosym='s1', + shls_slice=[shl_slice[i], shl_slice[i+1], + shl_slice[j], shl_slice[j+1], + shl_slice[k], shl_slice[k+1], + shl_slice[l], shl_slice[l+1]]).reshape( + 3, shl_size[i], shl_size[j], shl_size[k], shl_size[l]) hso1e[:, ao_loc[i]:ao_loc[i+1], ao_loc[j]:ao_loc[j+1]] \ - += 1.0*numpy.einsum('ijklm, lm->ijk', h2ao, \ - dm[ao_loc[k]:ao_loc[k+1], ao_loc[l]:ao_loc[l+1]]) + += 1.0*numpy.einsum('ijklm, lm->ijk', h2ao, + dm[ao_loc[k]:ao_loc[k+1], ao_loc[l]:ao_loc[l+1]]) hso1e[:, ao_loc[i]:ao_loc[i+1], ao_loc[l]:ao_loc[l+1]] \ - +=-1.5*numpy.einsum('ijklm, kl->ijm', h2ao, \ - dm[ao_loc[j]:ao_loc[j+1], ao_loc[k]:ao_loc[k+1]]) + += -1.5*numpy.einsum('ijklm, kl->ijm', h2ao, + dm[ao_loc[j]:ao_loc[j+1], ao_loc[k]:ao_loc[k+1]]) hso1e[:, ao_loc[k]:ao_loc[k+1], ao_loc[j]:ao_loc[j+1]] \ - += -1.5*numpy.einsum('ijklm, mj->ilk', h2ao, \ - dm[ao_loc[l]:ao_loc[l+1], ao_loc[i]:ao_loc[i+1]]) + += -1.5*numpy.einsum('ijklm, mj->ilk', h2ao, + dm[ao_loc[l]:ao_loc[l+1], ao_loc[i]:ao_loc[i+1]]) return hso1e -def get_hso1e_bp1c(mol, dm, atomlist): +def get_fso2e_bp1c(mol, dm, atomlist): ''' Two electron bp operator with one-center approximation ''' nb = mol.nao_nr() - np = nb * nb hso1e = numpy.zeros(3 * nb * nb).reshape(3, nb, nb) from pyscf.gto import moleintor - nbas = mol.nbas max_double = mol.max_memory / 8.0 * 1.0e6 max_basis = pow(max_double / 3., 1. / 4.) shl_size = [] @@ -387,25 +375,21 @@ def get_hso1e_bp1c(mol, dm, atomlist): else: ibegin = shl_slice[iatom] iend = shl_slice[iatom + 1] - start = time.clock() - h2ao = mol.intor('cint2e_p1vxp1_sph', - comp=3, - aosym='s1', - shls_slice=[ibegin, iend, ibegin, iend, ibegin, iend, ibegin, - iend]).reshape(3, shl_size[iatom], shl_size[iatom], shl_size[iatom], - shl_size[iatom]) - end = time.clock() - print("Time elapsed for integral calculation:", end - start, iatom, nbas) - start = time.clock() + #start = time.clock() + h2ao = mol.intor('cint2e_p1vxp1_sph', comp=3, aosym='s1', shls_slice=[ibegin, iend, ibegin, iend, ibegin, iend, ibegin, iend]).reshape( + 3, shl_size[iatom], shl_size[iatom], shl_size[iatom], shl_size[iatom]) + #end = time.clock() + # print("Time elapsed for integral calculation:", + # end - start, iatom, nbas) hso1e[:, ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]] \ - += 1.0*numpy.einsum('ijklm, lm->ijk', h2ao, \ - dm[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]]) + += 1.0*numpy.einsum('ijklm, lm->ijk', h2ao, + dm[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]]) hso1e[:, ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]] \ - +=-1.5*numpy.einsum('ijklm, kl->ijm', h2ao, \ - dm[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]]) + += -1.5*numpy.einsum('ijklm, kl->ijm', h2ao, + dm[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]]) hso1e[:, ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]] \ - += -1.5*numpy.einsum('ijklm, mj->ilk', h2ao, \ - dm[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]]) + += -1.5*numpy.einsum('ijklm, mj->ilk', h2ao, + dm[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]]) return hso1e @@ -428,8 +412,8 @@ def writeGTensorIntegrals(mc, atomlist=None): jshl_start = aoslice[jatom, 0] ishl_end = aoslice[iatom, 1] jshl_end = aoslice[jatom, 1] - h1ao[:, iao_start : iao_end, jao_start : jao_end]\ - += mol.intor('cint1e_cg_irxp_sph', 3, shls_slice=[ishl_start, ishl_end, jshl_start, jshl_end]).reshape(3, iao_end-iao_start, jao_end-jao_start) + h1ao[:, iao_start: iao_end, jao_start: jao_end]\ + += mol.intor('cint1e_cg_irxp_sph', 3, shls_slice=[ishl_start, ishl_end, jshl_start, jshl_end]).reshape(3, iao_end-iao_start, jao_end-jao_start) h1 = numpy.einsum('xpq, pi, qj->xij', h1ao, mc.mo_coeff, mc.mo_coeff) print1Int(h1[:, ncore:ncore + ncas, ncore:ncore + ncas], 'GTensor') @@ -446,6 +430,12 @@ def writeSOCIntegrals(mc, from pyscf.lib.parameters import LIGHT_SPEED alpha = 1.0 / LIGHT_SPEED + # \alpha^2/4 factor before SOC Hamiltonian in the paper + # not applicable to ECP terms + factor = alpha**2 * 0.25 + + has_ecp = mc.mol.has_ecp() + if ("bp" in pictureChange1e and "bp" in pictureChange2e): uncontract = False @@ -456,11 +446,12 @@ def writeSOCIntegrals(mc, if (atomlist is None): atomlist = numpy.linspace(0, xmol.natm - 1, xmol.natm, dtype=int) - print(atomlist) + rdm1ao = rdm1 if (rdm1 is None): rdm1ao = 1. * mc.make_rdm1() - if len(rdm1ao.shape) > 2: rdm1ao = (rdm1ao[0] + rdm1ao[1]) + if len(rdm1ao.shape) > 2: + rdm1ao = (rdm1ao[0] + rdm1ao[1]) if (uncontract): dm = reduce(numpy.dot, (contr_coeff, rdm1ao, contr_coeff.T)) @@ -472,40 +463,37 @@ def writeSOCIntegrals(mc, if ("x2c" in pictureChange1e or "x2c" in pictureChange2e): h1e_1c, x, rp = get_hxr(mc, uncontract=uncontract) + if (has_ecp): + print("X2C Hamiltonian shouldn't be used with ECPs, switch to BP.") + exit(0) + + # ECPso terms + if (has_ecp): + hso1e += xmol.intor('ECPso') - #two electron terms + # two electron terms if (pictureChange2e == "bp"): - hso1e += -(alpha)**2 * 0.5 * get_hso1e_bp(xmol, dm) + hso1e += -factor * get_fso2e_bp(xmol, dm) elif (pictureChange2e == "bp1c"): - hso1e += -(alpha)**2 * 0.5 * get_hso1e_bp1c(xmol, dm, atomlist) + hso1e += -factor * get_fso2e_bp1c(xmol, dm, atomlist) elif (pictureChange2e == "x2c"): pLL, pLS, pSS = get_p(dm / 2.0, x, rp) - hso1e += -(alpha)**2 * 0.5 * get_fso2e_2(xmol, x, rp, pLL, pLS, pSS) + hso1e += -factor * get_fso2e_x2c(xmol, x, rp, pLL, pLS, pSS) elif (pictureChange2e == "x2c1c"): pLL, pLS, pSS = get_p(dm / 2.0, x, rp) - hso1e += -(alpha)**2 * 0.5 * get_fso2e_1c(xmol, x, rp, pLL, pLS, pSS) + hso1e += -factor * get_fso2e_x2c1c(xmol, x, rp, pLL, pLS, pSS) elif (pictureChange2e == "none"): hso1e += 0.0 else: print(pictureChange2e, "not a valid option") exit(0) - #MF 1 electron term + # MF 1 electron term if (pictureChange1e == "bp"): - hso1e += (alpha)**2 * 0.5 * get_wso(xmol) - elif (pictureChange1e == "bp1c"): - hso1e += (alpha)**2 * 0.5 * get_wso_1c(xmol, atomlist) + hso1e += factor * get_wso(xmol) elif (pictureChange1e == "x2c1"): - wso = (alpha)**2 * 0.5 * get_wso(xmol) + wso = factor * get_wso(xmol) hso1e += get_hso1e(wso, x, rp) - elif (pictureChange1e == "x2cn"): - h1e_2c = (alpha)**2 * 0.5 * x2c.get_hcore(xmol) - - for i in range(np): - for j in range(np): - hso1e[0][i, j] -= h1e_2c[2 * i, 2 * j + 1].imag * 2. - hso1e[1][i, j] -= h1e_2c[2 * i, 2 * j + 1].real * 2. - hso1e[2][i, j] -= h1e_2c[2 * i, 2 * j].imag * 2. elif (pictureChange1e == "none"): hso1e += 0.0 else: @@ -515,7 +503,8 @@ def writeSOCIntegrals(mc, h1ao = numpy.zeros((3, nc, nc)) if (uncontract): for ic in range(3): - h1ao[ic] = reduce(numpy.dot, (contr_coeff.T, hso1e[ic], contr_coeff)) + h1ao[ic] = reduce( + numpy.dot, (contr_coeff.T, hso1e[ic], contr_coeff)) else: h1ao = 1. * hso1e @@ -523,15 +512,16 @@ def writeSOCIntegrals(mc, if (ncasorbs is not None): ncas = ncasorbs mo_coeff = mc.mo_coeff - h1 = numpy.einsum('xpq,pi,qj->xij', h1ao, mo_coeff, mo_coeff)[:, ncore:ncore + ncas, ncore:ncore + ncas] + h1 = numpy.einsum('xpq,pi,qj->xij', h1ao, mo_coeff, + mo_coeff)[:, ncore:ncore + ncas, ncore:ncore + ncas] print1Int(h1, 'SOC') -def doSOC(mc, pictureChange1e = "bp", pictureChange2e = "bp1c", uncontract = False, atomlist = None, FCIDUMP = True): - if (FCIDUMP): - mch = shci.SHCISCF(mc.mol, mc.norb, mc.nelec) - mch.fcisolver.onlywriteIntegral = False - shci.dryrun(mch, mc.mo_coeff) - writeGTensorIntegrals(mc, atomlist = atomlist) - writeSOCIntegrals(mc, pictureChange1e = pictureChange1e, pictureChange2e = pictureChange2e, uncontract = uncontract, atomlist = atomlist) - +def doSOC(mc, pictureChange1e="bp", pictureChange2e="bp", uncontract=False, atomlist=None): + writeGTensorIntegrals(mc, atomlist=atomlist) + writeSOCIntegrals(mc, pictureChange1e=pictureChange1e, + pictureChange2e=pictureChange2e, uncontract=uncontract, atomlist=atomlist) + mch = shci.SHCISCF(mc.mol, mc.norb, mc.nelec) + mch.fcisolver.DoSOC = True + mch.fcisolver.DoRDM = False + shci.dryrun(mch, mc.mo_coeff) From 9602395cb0d1291d5b1c4ba317aa6d40e7106980 Mon Sep 17 00:00:00 2001 From: xubwa Date: Wed, 11 Sep 2019 21:15:23 -0600 Subject: [PATCH 04/26] add example for soc calculation, enable to call ZDice2 from shci.py directly --- pyscf/shciscf/examples/06_I_soc.py | 117 +++++++++++++++++++++++++++++ pyscf/shciscf/shci.py | 117 +++++++++++++++++------------ pyscf/shciscf/socutils.py | 4 +- 3 files changed, 186 insertions(+), 52 deletions(-) create mode 100644 pyscf/shciscf/examples/06_I_soc.py diff --git a/pyscf/shciscf/examples/06_I_soc.py b/pyscf/shciscf/examples/06_I_soc.py new file mode 100644 index 0000000000..6b82508fd7 --- /dev/null +++ b/pyscf/shciscf/examples/06_I_soc.py @@ -0,0 +1,117 @@ +from pyscf import gto, scf, mcscf +from pyscf.shciscf import shci, socutils +import numpy + +# molecule object +# to carry out ecp calculation, spin-orbit pseudo potential has to be provided +# crenbl and crenbs ecp are currently the only available in pyscf, for more ecps +# visit http://www.tc.uni-koeln.de/PP/index.en.html it is only available in molpro format. +# or http://www.cosmologic-services.de/basis-sets/basissets.php the dhf-()-2c basis sets also have spin-orbit ecps. +mol = gto.M( + verbose = 4, + atom = "I 0 0 0", + basis = 'cc-pvdz-pp', + ecp = ''' +I nelec 28 +I ul +2 1.00000000 0.00000000 +I S +2 40.03337600 49.98964900 +2 17.30057600 281.00655600 +2 8.85172000 61.41673900 +I P +2 15.72014100 67.41623900 -134.832478 +2 15.20822200 134.80769600 134.807696 +2 8.29418600 14.56654800 -29.133096 +2 7.75394900 28.96842200 28.968422 +I D +2 13.81775100 35.53875600 -35.538756 +2 13.58780500 53.33975900 35.559839 +2 6.94763000 9.71646600 -9.716466 +2 6.96009900 14.97750000 9.985000 +I F +2 18.52295000 -20.17661800 13.451079 +2 18.25103500 -26.08807700 -13.044039 +2 7.55790100 -0.22043400 0.146956 +2 7.59740400 -0.22164600 -0.110823 + ''', + spin = 1) + +# mean-field object +mf = scf.RHF(mol) +mf.kernel() + +# state-average mcscf calculation with valence active space +mc = mcscf.CASSCF(mf, 4, 7).state_average_(numpy.ones(3)/3.0) +mc.internal_rotation = True +mc.mc1step() + +# writes the SOC integrals, by default picture change effects are +# considered using breit-pauli Hamiltonian +socutils.writeSOCIntegrals(mc) + +# perform a one-step SHCI calculation +mch = shci.SHCISCF(mf, 4, 7).state_average_(numpy.ones(6)/6.0) +mch.fcisolver.DoSOC = True +mch.fcisolver.DoRDM = False +# the first three doublet states are degenerate at non-relativistic calculations +# due to spin-orbit coupling, it splits into a four-fold degenerate state and +# a two-fold degenerate state +mch.fcisolver.nroots = 6 +mch.fcisolver.sweep_iter = [0] +mch.fcisolver.sweep_epsilon = [1e-5] +mch.fcisolver.dets = [[0,1,2,3,4,5,6],[0,1,2,3,4,5,7]] +shci.dryrun(mch, mc.mo_coeff) + +e_ecp = shci.readEnergy(mch.fcisolver) + +# then do a very small all electron calculation for reference +mol_all_elec = gto.M( + verbose = 4, + atom = 'I 0 0 0', + basis = 'ano@6s5p3d1f', + max_memory = 2000, + spin = 1) +mf_nr = scf.RHF(mol_all_elec) +mf_nr.kernel() + +mc_nr = mcscf.CASSCF(mf_nr, 4, 7).state_average_(numpy.ones(3)/3.0) +mc_nr.internal_rotation = True +mc_nr.mc2step() + +socutils.writeSOCIntegrals(mc_nr, pictureChange1e = 'bp', pictureChange2e = 'bp') + +mch_nr = shci.SHCISCF(mf_nr, 4, 7).state_average_(numpy.ones(6)/6.0) +mch_nr.fcisolver.DoSOC = True +mch_nr.fcisolver.DoRDM = False +mch_nr.fcisolver.nroots = 6 +mch_nr.fcisolver.sweep_iter = [0] +mch_nr.fcisolver.sweep_epsilon = [1e-5] +mch_nr.fcisolver.dets = [[0,1,2,3,4,5,6],[0,1,2,3,4,5,7]] +shci.dryrun(mch_nr, mc_nr.mo_coeff) + +e_bp = shci.readEnergy(mch_nr.fcisolver) + +mf_x2c = scf.sfx2c1e(scf.RHF(mol_all_elec)) +mf_x2c.kernel() +mc_x2c = mcscf.CASSCF(mf_x2c, 4, 7).state_average_(numpy.ones(3)/3.0) +mc_x2c.mc2step() + +socutils.writeSOCIntegrals(mc_x2c, pictureChange1e = 'x2c1', pictureChange2e = 'x2c') +mch_x2c = shci.SHCISCF(mf_x2c, 4, 7).state_average_(numpy.ones(6)/6.0) +mch_x2c.fcisolver.DoSOC = True +mch_x2c.fcisolver.DoRDM = False +mch_x2c.fcisolver.nroots = 6 +mch_x2c.fcisolver.sweep_iter = [0] +mch_x2c.fcisolver.sweep_epsilon = [1e-5] +mch_x2c.fcisolver.dets = [[0,1,2,3,4,5,6],[0,1,2,3,4,5,7]] +shci.dryrun(mch_x2c, mc.mo_coeff) + +e_x2c = shci.readEnergy(mch_x2c.fcisolver) + +print( "%11s%20s %12s%11s%12s %11s \n" %("", "SOECP(Har) (cm^-1)", " BP(Har) "," (cm^-1)", " X2C(Har)", "cm^-1")) +for i in range(6): + print("State %i : %10.4f %10.1f %12.4f %10.1f %12.4f %10.1f\n" %(i, + e_ecp[i], (e_ecp[i]-e_ecp[0]) * 219470., + e_bp[i], (e_bp[i] -e_bp[0] ) * 219470., + e_x2c[i], (e_x2c[i]-e_x2c[0]) * 219470.)) diff --git a/pyscf/shciscf/shci.py b/pyscf/shciscf/shci.py index 6634369471..b5aaf79d52 100644 --- a/pyscf/shciscf/shci.py +++ b/pyscf/shciscf/shci.py @@ -179,6 +179,7 @@ def __init__(self, mol=None, maxM=None, tol=None, num_thrds=1, # TODO: Organize into pyscf and SHCI parameters # Standard SHCI Input parameters + self.dets = None self.davidsonTol = 5.e-5 self.epsilon2 = 1.e-7 self.epsilon2Large = 1000. @@ -913,60 +914,76 @@ def writeSHCIConfFile(SHCI, nelec, Restart): # Reference determinant section f.write('#system\n') f.write('nocc %i\n' % (nelec[0] + nelec[1])) - if SHCI.__class__.__name__ == 'FakeCISolver': - for i in range(nelec[0]): - f.write('%i ' % (2 * i)) - for i in range(nelec[1]): - f.write('%i ' % (2 * i + 1)) - else: - if SHCI.initialStates is not None: - for i in range(len(SHCI.initialStates)): - for j in SHCI.initialStates[i]: - f.write('%i ' % (j)) - if (i != len(SHCI.initialStates) - 1): - f.write('\n') - elif SHCI.irrep_nelec is None: - for i in range(int(nelec[0])): + if SHCI.dets is None: + if SHCI.__class__.__name__ == 'FakeCISolver': + for i in range(nelec[0]): f.write('%i ' % (2 * i)) - for i in range(int(nelec[1])): + for i in range(nelec[1]): f.write('%i ' % (2 * i + 1)) else: - from pyscf import symm - from pyscf.dmrgscf import dmrg_sym - from pyscf.symm.basis import DOOH_IRREP_ID_TABLE - if SHCI.groupname is not None and SHCI.orbsym is not []: - orbsym = dmrg_sym.convert_orbsym(SHCI.groupname, SHCI.orbsym) + if SHCI.initialStates is not None: + for i in range(len(SHCI.initialStates)): + for j in SHCI.initialStates[i]: + f.write('%i ' % (j)) + if (i != len(SHCI.initialStates) - 1): + f.write('\n') + elif SHCI.irrep_nelec is None: + for i in range(int(nelec[0])): + f.write('%i ' % (2 * i)) + for i in range(int(nelec[1])): + f.write('%i ' % (2 * i + 1)) + else: + from pyscf import symm + from pyscf.dmrgscf import dmrg_sym + from pyscf.symm.basis import DOOH_IRREP_ID_TABLE + if SHCI.groupname is not None and SHCI.orbsym is not []: + orbsym = dmrg_sym.convert_orbsym(SHCI.groupname, SHCI.orbsym) + else: + orbsym = [1] * norb + done = [] + for k, v in SHCI.irrep_nelec.items(): + + irrep, nalpha, nbeta = [dmrg_sym.irrep_name2id(SHCI.groupname, k)],\ + v[0], v[1] + + for i in range(len(orbsym)): #loop over alpha electrons + if (orbsym[i] == irrep[0] and nalpha != 0 + and i * 2 not in done): + done.append(i * 2) + f.write('%i ' % (i * 2)) + nalpha -= 1 + if (orbsym[i] == irrep[0] and nbeta != 0 + and i * 2 + 1 not in done): + done.append(i * 2 + 1) + f.write('%i ' % (i * 2 + 1)) + nbeta -= 1 + if (nalpha != 0): + print("number of irreps %s in active space = %d" % + (k, v[0] - nalpha)) + print( + "number of irreps %s alpha electrons = %d" % (k, v[0])) + exit(1) + if (nbeta != 0): + print("number of irreps %s in active space = %d" % + (k, v[1] - nbeta)) + print( + "number of irreps %s beta electrons = %d" % (k, v[1])) + exit(1) + else: + nelec_all = nelec[0] + nelec[1] + for det in SHCI.dets: + if len(det) is not nelec_all: + if len(det) is 1: + print("SHCI.dets should be a 2d array.") + else: + print("number of electrons in SHCI.dets %d does not equal to nelecas %d" % (len(det), nelec_all)) + exit(1) else: - orbsym = [1] * norb - done = [] - for k, v in SHCI.irrep_nelec.items(): - - irrep, nalpha, nbeta = [dmrg_sym.irrep_name2id(SHCI.groupname, k)],\ - v[0], v[1] - - for i in range(len(orbsym)): #loop over alpha electrons - if (orbsym[i] == irrep[0] and nalpha != 0 - and i * 2 not in done): - done.append(i * 2) - f.write('%i ' % (i * 2)) - nalpha -= 1 - if (orbsym[i] == irrep[0] and nbeta != 0 - and i * 2 + 1 not in done): - done.append(i * 2 + 1) - f.write('%i ' % (i * 2 + 1)) - nbeta -= 1 - if (nalpha != 0): - print("number of irreps %s in active space = %d" % - (k, v[0] - nalpha)) - print( - "number of irreps %s alpha electrons = %d" % (k, v[0])) - exit(1) - if (nbeta != 0): - print("number of irreps %s in active space = %d" % - (k, v[1] - nbeta)) - print( - "number of irreps %s beta electrons = %d" % (k, v[1])) - exit(1) + for i in det: + f.write('%i '%i) + if det is not SHCI.dets[-1]: + f.write('\n') + f.write('\nend\n') f.write('nroots %r\n' % SHCI.nroots) diff --git a/pyscf/shciscf/socutils.py b/pyscf/shciscf/socutils.py index 74c1022a71..d3cb3e974c 100644 --- a/pyscf/shciscf/socutils.py +++ b/pyscf/shciscf/socutils.py @@ -26,7 +26,7 @@ def print1Int(h1, name): # x and r in the uncontracted basis def get_hxr(mc, uncontract=True): if (uncontract): - xmol, contr_coeff = x2c.X2C().get_xmol(mc.mol) + xmol, contr_coeff = x2c.X2C(mc.mol).get_xmol() else: xmol, contr_coeff = mc.mol, numpy.eye(mc.mo_coeff.shape[0]) @@ -440,7 +440,7 @@ def writeSOCIntegrals(mc, uncontract = False if (uncontract): - xmol, contr_coeff = x2c.X2C().get_xmol(mc.mol) + xmol, contr_coeff = x2c.X2C(mc.mol).get_xmol() else: xmol, contr_coeff = mc.mol, numpy.eye(mc.mo_coeff.shape[0]) From a90c341f1a6b6438418c336dc3c88d8f9ce36122 Mon Sep 17 00:00:00 2001 From: xubwa Date: Fri, 6 Dec 2019 15:41:05 -0700 Subject: [PATCH 05/26] fix minor bug in get_fso2e_x2c, mostly reverting to a previous version --- pyscf/shciscf/socutils.py | 64 +++++++++++++++------------------------ 1 file changed, 25 insertions(+), 39 deletions(-) diff --git a/pyscf/shciscf/socutils.py b/pyscf/shciscf/socutils.py index d3cb3e974c..628796f3e4 100644 --- a/pyscf/shciscf/socutils.py +++ b/pyscf/shciscf/socutils.py @@ -169,8 +169,6 @@ def get_fso2e_x2c(mol, x, rp, pLL, pLS, pSS): shl_size = [] shl_slice = [0] ao_loc = [0] - print(nb, nbas) - print(ao_loc_orig) if nb > max_basis: for i in range(0, nbas - 1): if (ao_loc_orig[i + 1] - ao_loc[-1] > max_basis and ao_loc_orig[i] - ao_loc[-1]): @@ -181,57 +179,42 @@ def get_fso2e_x2c(mol, x, rp, pLL, pLS, pSS): ao_loc.append(ao_loc_orig[-1]) shl_size.append(ao_loc[-1] - ao_loc[-2]) shl_slice.append(nbas) - print(ao_loc, shl_size, shl_slice) + #print(ao_loc, shl_size, shl_slice) nbas = len(shl_size) for i in range(0, nbas): for j in range(0, nbas): for k in range(0, nbas): for l in range(0, nbas): - #start = time.clock() - ddint = mol.intor('int2e_ip1ip2_sph', comp=9, - shls_slice=[shl_slice[i], shl_slice[i+1], - shl_slice[j], shl_slice[j+1], - shl_slice[k], shl_slice[k+1], - shl_slice[l], shl_slice[l+1]]).reshape(3, 3, -1) - kint = numpy.zeros( - 3 * shl_size[i] * shl_size[j] * shl_size[k] * shl_size[l]).reshape( - 3, shl_size[i], shl_size[j], shl_size[k], shl_size[l]) - kint[0] = (ddint[1, 2] - ddint[2, 1]).reshape(shl_size[i], - shl_size[j], shl_size[k], shl_size[l]) - kint[1] = (ddint[2, 0] - ddint[0, 2]).reshape(shl_size[i], - shl_size[j], shl_size[k], shl_size[l]) - kint[2] = (ddint[0, 1] - ddint[1, 0]).reshape(shl_size[i], - shl_size[j], shl_size[k], shl_size[l]) - #end = time.clock() - # print("Time elapsed for integral calculation:", - # end - start, i, j, k, l, nbas) - - #start = time.clock() + ddint = mol.intor('int2e_ip1ip2_sph', comp=9, shls_slice=[shl_slice[i], shl_slice[i+1], shl_slice[j], shl_slice[j+1], shl_slice[k], shl_slice[k+1], shl_slice[l], shl_slice[l+1]]).reshape(3, 3, -1) + kint = numpy.zeros(3 * shl_size[i] * shl_size[j] * shl_size[k] * shl_size[l]).reshape( + 3, shl_size[i], shl_size[j], shl_size[k], shl_size[l]) + kint[0] = (ddint[1, 2] - ddint[2, 1]).reshape(shl_size[i], shl_size[j], shl_size[k], shl_size[l]) + kint[1] = (ddint[2, 0] - ddint[0, 2]).reshape(shl_size[i], shl_size[j], shl_size[k], shl_size[l]) + kint[2] = (ddint[0, 1] - ddint[1, 0]).reshape(shl_size[i], shl_size[j], shl_size[k], shl_size[l]) + #print("Time elapsed for integral calculation:", end - start, i, j, k, l, nbas) + gsoLL[:, ao_loc[j]:ao_loc[j+1], ao_loc[l]:ao_loc[l+1]] \ - += -2.0*numpy.einsum('ilmkn,lk->imn', kint, - pSS[ao_loc[i]:ao_loc[i+1], ao_loc[k]:ao_loc[k+1]]) + +=-2.0*numpy.einsum('ilmkn,lk->imn', kint, \ + pSS[ao_loc[i]:ao_loc[i+1], ao_loc[k]:ao_loc[k+1]]) gsoLS[:, ao_loc[i]:ao_loc[i+1], ao_loc[l]:ao_loc[l+1]] \ - += -1.0*numpy.einsum('imlkn,lk->imn', kint, - pLS[ao_loc[j]:ao_loc[j+1], ao_loc[k]:ao_loc[k+1]]) + +=-1.0*numpy.einsum('imlkn,lk->imn', kint, \ + pLS[ao_loc[j]:ao_loc[j+1], ao_loc[k]:ao_loc[k+1]]) gsoLS[:, ao_loc[j]:ao_loc[j+1], ao_loc[l]:ao_loc[l+1]] \ - += -1.0*numpy.einsum('ilmkn,lk->imn', kint, - pLS[ao_loc[i]:ao_loc[i+1], ao_loc[k]:ao_loc[k+1]]) + +=-1.0*numpy.einsum('ilmkn,lk->imn', kint, \ + pLS[ao_loc[i]:ao_loc[i+1], ao_loc[k]:ao_loc[k+1]]) gsoSS[:, ao_loc[i]:ao_loc[i+1], ao_loc[j]:ao_loc[j+1]] \ - += -2.0*numpy.einsum('imnkl,lk->imn', kint, - pLL[ao_loc[l]:ao_loc[l+1], ao_loc[k]:ao_loc[k+1]])\ - - 2.0*numpy.einsum('imnlk,lk->imn', kint, - pLL[ao_loc[k]:ao_loc[k+1], ao_loc[l]:ao_loc[l+1]]) + +=-2.0*numpy.einsum('imnkl,lk->imn', kint, \ + pLL[ao_loc[l]:ao_loc[l+1], ao_loc[k]:ao_loc[k+1]])\ + -2.0*numpy.einsum('imnlk,lk->imn', kint, \ + pLL[ao_loc[k]:ao_loc[k+1], ao_loc[l]:ao_loc[l+1]]) gsoSS[:, ao_loc[i]:ao_loc[i+1], ao_loc[k]:ao_loc[k+1]] \ - += 2.0*numpy.einsum('imlnk,lk->imn', kint, - pLL[ao_loc[j]:ao_loc[j+1], ao_loc[l]:ao_loc[l+1]]) + += 2.0*numpy.einsum('imlnk,lk->imn', kint, \ + pLL[ao_loc[j]:ao_loc[j+1], ao_loc[l]:ao_loc[l+1]]) #print(" Time elapsed for einsum:", time.clock() - start) for comp in range(0, 3): - fso2e[comp] = gsoLL[comp] - + gsoLS[comp].dot(x) - + x.T.dot(-gsoLS[comp].T) - + x.T.dot(gsoSS[comp].dot(x)) + fso2e[comp] = gsoLL[comp] + gsoLS[comp].dot(x) + x.T.dot(-gsoLS[comp].T) + x.T.dot(gsoSS[comp].dot(x)) fso2e[comp] = reduce(numpy.dot, (rp.T, fso2e[comp], rp)) return fso2e @@ -476,6 +459,9 @@ def writeSOCIntegrals(mc, hso1e += -factor * get_fso2e_bp(xmol, dm) elif (pictureChange2e == "bp1c"): hso1e += -factor * get_fso2e_bp1c(xmol, dm, atomlist) + elif (pictureChange2e == "x2c_old"): + pLL, pLS, pSS = get_p(dm / 2.0, x, rp) + hso1e += -factor * get_fso2e_x2c_original(xmol, x, rp, pLL, pLS, pSS) elif (pictureChange2e == "x2c"): pLL, pLS, pSS = get_p(dm / 2.0, x, rp) hso1e += -factor * get_fso2e_x2c(xmol, x, rp, pLL, pLS, pSS) From 7550007501f2e9a4fcc4fafcc652fe29f66b00d6 Mon Sep 17 00:00:00 2001 From: xubwa Date: Sun, 2 Feb 2020 21:36:34 -0700 Subject: [PATCH 06/26] switch from numpy.einsum to lib.einsum --- pyscf/shciscf/socutils.py | 54 +++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/pyscf/shciscf/socutils.py b/pyscf/shciscf/socutils.py index 628796f3e4..086bf7054e 100644 --- a/pyscf/shciscf/socutils.py +++ b/pyscf/shciscf/socutils.py @@ -140,12 +140,12 @@ def get_fso2e_x2c_original(mol, x, rp, pLL, pLS, pSS): i_z = xyz[i_x - 1] ddint[0, 0] = ddint[i_y, i_z] - ddint[i_z, i_y] # x = yz - zy etc kint = ddint[0, 0].reshape(nb, nb, nb, nb) - gsoLL = -2.0 * numpy.einsum('lmkn,lk->mn', kint, pSS) - gsoLS = -1.0 * numpy.einsum('mlkn,lk->mn', kint, pLS) \ - - 1.0 * numpy.einsum('lmkn,lk->mn', kint, pLS) - gsoSS = -2.0 * numpy.einsum('mnkl,lk', kint, pLL) \ - - 2.0 * numpy.einsum('mnlk,lk', kint, pLL) \ - + 2.0 * numpy.einsum('mlnk,lk', kint, pLL) + gsoLL = -2.0 * lib.einsum('lmkn,lk->mn', kint, pSS) + gsoLS = -1.0 * lib.einsum('mlkn,lk->mn', kint, pLS) \ + - 1.0 * lib.einsum('lmkn,lk->mn', kint, pLS) + gsoSS = -2.0 * lib.einsum('mnkl,lk', kint, pLL) \ + - 2.0 * lib.einsum('mnlk,lk', kint, pLL) \ + + 2.0 * lib.einsum('mlnk,lk', kint, pLL) fso2e[i_x] = gsoLL + gsoLS.dot(x) + x.T.dot(-gsoLS.T) \ + x.T.dot(gsoSS.dot(x)) fso2e[i_x] = reduce(numpy.dot, (rp.T, fso2e[i_x], rp)) @@ -179,7 +179,7 @@ def get_fso2e_x2c(mol, x, rp, pLL, pLS, pSS): ao_loc.append(ao_loc_orig[-1]) shl_size.append(ao_loc[-1] - ao_loc[-2]) shl_slice.append(nbas) - #print(ao_loc, shl_size, shl_slice) + print(ao_loc, shl_size, shl_slice) nbas = len(shl_size) for i in range(0, nbas): @@ -195,21 +195,21 @@ def get_fso2e_x2c(mol, x, rp, pLL, pLS, pSS): #print("Time elapsed for integral calculation:", end - start, i, j, k, l, nbas) gsoLL[:, ao_loc[j]:ao_loc[j+1], ao_loc[l]:ao_loc[l+1]] \ - +=-2.0*numpy.einsum('ilmkn,lk->imn', kint, \ + +=-2.0*lib.einsum('ilmkn,lk->imn', kint, \ pSS[ao_loc[i]:ao_loc[i+1], ao_loc[k]:ao_loc[k+1]]) gsoLS[:, ao_loc[i]:ao_loc[i+1], ao_loc[l]:ao_loc[l+1]] \ - +=-1.0*numpy.einsum('imlkn,lk->imn', kint, \ + +=-1.0*lib.einsum('imlkn,lk->imn', kint, \ pLS[ao_loc[j]:ao_loc[j+1], ao_loc[k]:ao_loc[k+1]]) gsoLS[:, ao_loc[j]:ao_loc[j+1], ao_loc[l]:ao_loc[l+1]] \ - +=-1.0*numpy.einsum('ilmkn,lk->imn', kint, \ + +=-1.0*lib.einsum('ilmkn,lk->imn', kint, \ pLS[ao_loc[i]:ao_loc[i+1], ao_loc[k]:ao_loc[k+1]]) gsoSS[:, ao_loc[i]:ao_loc[i+1], ao_loc[j]:ao_loc[j+1]] \ - +=-2.0*numpy.einsum('imnkl,lk->imn', kint, \ + +=-2.0*lib.einsum('imnkl,lk->imn', kint, \ pLL[ao_loc[l]:ao_loc[l+1], ao_loc[k]:ao_loc[k+1]])\ - -2.0*numpy.einsum('imnlk,lk->imn', kint, \ + -2.0*lib.einsum('imnlk,lk->imn', kint, \ pLL[ao_loc[k]:ao_loc[k+1], ao_loc[l]:ao_loc[l+1]]) gsoSS[:, ao_loc[i]:ao_loc[i+1], ao_loc[k]:ao_loc[k+1]] \ - += 2.0*numpy.einsum('imlnk,lk->imn', kint, \ + += 2.0*lib.einsum('imlnk,lk->imn', kint, \ pLL[ao_loc[j]:ao_loc[j+1], ao_loc[l]:ao_loc[l+1]]) #print(" Time elapsed for einsum:", time.clock() - start) @@ -262,21 +262,21 @@ def get_fso2e_x2c1c(mol, x, rp, pLL, pLS, pSS): shl_size[iatom]) #start = time.clock() gsoLL[:, ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]] \ - += -2.0*numpy.einsum('ilmkn,lk->imn', kint, + += -2.0*lib.einsum('ilmkn,lk->imn', kint, pSS[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]]) gsoLS[:, ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]] \ - += -1.0*numpy.einsum('imlkn,lk->imn', kint, + += -1.0*lib.einsum('imlkn,lk->imn', kint, pLS[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]]) gsoLS[:, ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]] \ - += -1.0*numpy.einsum('ilmkn,lk->imn', kint, + += -1.0*lib.einsum('ilmkn,lk->imn', kint, pLS[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]]) gsoSS[:, ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]] \ - += -2.0*numpy.einsum('imnkl,lk->imn', kint, + += -2.0*lib.einsum('imnkl,lk->imn', kint, pLL[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]])\ - - 2.0*numpy.einsum('imnlk,lk->imn', kint, + - 2.0*lib.einsum('imnlk,lk->imn', kint, pLL[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]]) gsoSS[:, ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]] \ - += 2.0*numpy.einsum('imlnk,lk->imn', kint, + += 2.0*lib.einsum('imlnk,lk->imn', kint, pLL[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]]) #print(" Time elapsed for einsum:", time.clock() - start) for comp in range(0, 3): @@ -324,13 +324,13 @@ def get_fso2e_bp(mol, dm): shl_slice[l], shl_slice[l+1]]).reshape( 3, shl_size[i], shl_size[j], shl_size[k], shl_size[l]) hso1e[:, ao_loc[i]:ao_loc[i+1], ao_loc[j]:ao_loc[j+1]] \ - += 1.0*numpy.einsum('ijklm, lm->ijk', h2ao, + += 1.0*lib.einsum('ijklm, lm->ijk', h2ao, dm[ao_loc[k]:ao_loc[k+1], ao_loc[l]:ao_loc[l+1]]) hso1e[:, ao_loc[i]:ao_loc[i+1], ao_loc[l]:ao_loc[l+1]] \ - += -1.5*numpy.einsum('ijklm, kl->ijm', h2ao, + += -1.5*lib.einsum('ijklm, kl->ijm', h2ao, dm[ao_loc[j]:ao_loc[j+1], ao_loc[k]:ao_loc[k+1]]) hso1e[:, ao_loc[k]:ao_loc[k+1], ao_loc[j]:ao_loc[j+1]] \ - += -1.5*numpy.einsum('ijklm, mj->ilk', h2ao, + += -1.5*lib.einsum('ijklm, mj->ilk', h2ao, dm[ao_loc[l]:ao_loc[l+1], ao_loc[i]:ao_loc[i+1]]) return hso1e @@ -365,13 +365,13 @@ def get_fso2e_bp1c(mol, dm, atomlist): # print("Time elapsed for integral calculation:", # end - start, iatom, nbas) hso1e[:, ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]] \ - += 1.0*numpy.einsum('ijklm, lm->ijk', h2ao, + += 1.0*lib.einsum('ijklm, lm->ijk', h2ao, dm[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]]) hso1e[:, ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]] \ - += -1.5*numpy.einsum('ijklm, kl->ijm', h2ao, + += -1.5*lib.einsum('ijklm, kl->ijm', h2ao, dm[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]]) hso1e[:, ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]] \ - += -1.5*numpy.einsum('ijklm, mj->ilk', h2ao, + += -1.5*lib.einsum('ijklm, mj->ilk', h2ao, dm[ao_loc[iatom]:ao_loc[iatom+1], ao_loc[iatom]:ao_loc[iatom+1]]) return hso1e @@ -398,7 +398,7 @@ def writeGTensorIntegrals(mc, atomlist=None): h1ao[:, iao_start: iao_end, jao_start: jao_end]\ += mol.intor('cint1e_cg_irxp_sph', 3, shls_slice=[ishl_start, ishl_end, jshl_start, jshl_end]).reshape(3, iao_end-iao_start, jao_end-jao_start) - h1 = numpy.einsum('xpq, pi, qj->xij', h1ao, mc.mo_coeff, mc.mo_coeff) + h1 = lib.einsum('xpq, pi, qj->xij', h1ao, mc.mo_coeff, mc.mo_coeff) print1Int(h1[:, ncore:ncore + ncas, ncore:ncore + ncas], 'GTensor') @@ -498,7 +498,7 @@ def writeSOCIntegrals(mc, if (ncasorbs is not None): ncas = ncasorbs mo_coeff = mc.mo_coeff - h1 = numpy.einsum('xpq,pi,qj->xij', h1ao, mo_coeff, + h1 = lib.einsum('xpq,pi,qj->xij', h1ao, mo_coeff, mo_coeff)[:, ncore:ncore + ncas, ncore:ncore + ncas] print1Int(h1, 'SOC') From 0fd86edddb0e19f63cb20c5f3faf56ed9dd020a4 Mon Sep 17 00:00:00 2001 From: xubwa Date: Fri, 21 Feb 2020 15:45:30 -0700 Subject: [PATCH 07/26] add __init__.py, add functions to compute photophospherece lifetime, fix the doSOC function, add functions to call ZDice2. --- pyscf/shciscf/__init__.py | 3 + pyscf/shciscf/shci.py | 309 +++++++++++++++++++++++--------------- pyscf/shciscf/socutils.py | 12 +- 3 files changed, 198 insertions(+), 126 deletions(-) diff --git a/pyscf/shciscf/__init__.py b/pyscf/shciscf/__init__.py index 31982cc7a5..47528a364b 100644 --- a/pyscf/shciscf/__init__.py +++ b/pyscf/shciscf/__init__.py @@ -16,3 +16,6 @@ # Author: Sandeep Sharma # James Smith # + +from . import shci +from . import socutils diff --git a/pyscf/shciscf/shci.py b/pyscf/shciscf/shci.py index b5aaf79d52..7e494054f3 100644 --- a/pyscf/shciscf/shci.py +++ b/pyscf/shciscf/shci.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# Copyright 2014-2018 The PySCF Developers. All Rights Reserved. +# Copyright 2014-2019 The PySCF Developers. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ import ctypes import os import sys +import re import struct import time import tempfile @@ -46,14 +47,14 @@ from pyscf import __config__ settings = lambda: None settings.SHCIEXE = getattr(__config__, 'shci_SHCIEXE', None) + settings.ZSHCIEXE = getattr(__config__, 'shci_ZSHCIEXE', None) settings.SHCISCRATCHDIR = getattr(__config__, 'shci_SHCISCRATCHDIR', None) settings.SHCIRUNTIMEDIR = getattr(__config__, 'shci_SHCIRUNTIMEDIR', None) settings.MPIPREFIX = getattr(__config__, 'shci_MPIPREFIX', None) if (settings.SHCIEXE is None or settings.SHCISCRATCHDIR is None): import sys - sys.stderr.write( - 'settings.py not found. Please create %s\n' % os.path.join( - os.path.dirname(__file__), 'settings.py')) + sys.stderr.write('settings.py not found for module shciscf. Please create %s\n' + % os.path.join(os.path.dirname(__file__), 'settings.py')) raise ImportError('settings.py not found') # Libraries @@ -199,6 +200,7 @@ def __init__(self, mol=None, maxM=None, tol=None, num_thrds=1, self.nroots = 1 self.nPTiter = 0 self.DoRDM = True + self.DoTRDM = False self.DoSOC = False self.sweep_iter = [] self.sweep_epsilon = [] @@ -246,9 +248,7 @@ def threads(self, x): self.num_thrds = x def dump_flags(self, verbose=None): - if verbose is None: - verbose = self.verbose - log = logger.Logger(self.stdout, verbose) + log = logger.new_logger(self, verbose) log.info('') log.info('******** SHCI flags ********') log.info('executable = %s', self.executable) @@ -287,7 +287,33 @@ def dump_flags(self, verbose=None): # # --> See various remarks in the pertinent functions below. # ----------------------------------------------------------------------------------------------- - + + def trans_rdm1(self, state_i, state_j, norb, nelec, link_index=None, **kwargs): + if self.DoSOC: + trdm1 = numpy.zeros((norb, norb), dtype=complex) + else: + trdm1 = numpy.zeros((norb, norb)) + # assume Dice prints only i < j transition rdm. + if state_i > state_j: + tmp = state_i + state_i = state_j + state_j = tmp + + filetrdm1 = os.path.join(self.scratchDirectory, "transition1RDM.%d.%d.txt" % (state_i, state_j)) + with open(filetrdm1) as f: + line = f.readline() + file_orb = int(line.split()[0]) + for line in f: + orb1 = int(line.split()[0]) + orb2 = int(line.split()[1]) + if self.DoSOC: + val = re.split("[(,)]", line.split()[2]) + val = complex(float(val[1]), float(val[2])) + else: + val = float(line.split()[2]) + trdm1[orb1][orb2] = val + return trdm1 + def make_rdm1(self, state, norb, nelec, link_index=None, **kwargs): # Avoid calling self.make_rdm12 because it may be overloaded return self.make_rdm12(state, norb, nelec, link_index, **kwargs)[0] @@ -328,16 +354,6 @@ def make_rdm12(self, state, norb, nelec, link_index=None, **kwargs): onepdm /= (nelectrons - 1) return onepdm, twopdm - def trans_rdm1(self, - statebra, - stateket, - norb, - nelec, - link_index=None, - **kwargs): - return self.trans_rdm12(statebra, stateket, norb, nelec, link_index, - **kwargs)[0] - def trans_rdm12(self, statebra, stateket, @@ -471,7 +487,7 @@ def make_rdm3(self, state, norb, nelec, - dt=numpy.dtype('Float64'), + dt=numpy.float64, filetype="binary", link_index=None, bypass=False, @@ -559,7 +575,7 @@ def make_rdm4(self, state, norb, nelec, - dt=numpy.dtype('Float64'), + dt=numpy.float64, filetype="binary", link_index=None, bypass=False, @@ -813,10 +829,7 @@ def kernel(self, h1e, eri, norb, nelec, fciRestart=None, ecore=0, return calc_e, roots if self.returnInt: return h1e, eri - if self.DoSOC: - executeZSHCI(self) - else: - executeSHCI(self) + executeSHCI(self) if self.verbose >= logger.DEBUG1: outFile = os.path.join(self.runtimeDir, self.outputFile) logger.debug1(self, open(outFile).read()) @@ -882,10 +895,42 @@ def cleanup_dice_files(self): """ Remove the files used for Dice communication. """ - os.remove("input.dat") - os.remove("output.dat") - os.remove("FCIDUMP") - + + os.remove(os.path.join(self.runtimeDir, self.configFile)) + os.remove(os.path.join(self.runtimeDir, self.outputFile)) + os.remove(os.path.join(self.runtimeDir, self.integralFile)) + + +def transition_dipole(mc, state_i, state_j): + t_dm1 = mc.fcisolver.trans_rdm1(state_i, state_j, mc.ncas, mc.nelecas) + ncore = mc.ncore + ncasorb = mc.ncas + mol = mc.mol + mo_cas = mc.mo_coeff[:, ncore:ncore+ncasorb] + t_dm1 = pyscf.lib.einsum('pi, ij, qj->pq', mo_cas, t_dm1, mo_cas) + #print(t_dm1) + charge_center = (numpy.einsum('z,zx->x', mol.atom_charges(), mol.atom_coords()) / mol.atom_charges().sum()) + with mol.with_common_origin(charge_center): + t_dip = numpy.einsum('xij,ji->x', mol.intor('int1e_r'), t_dm1) + return t_dip + +def oscillator_strength(mc, state_i, state_j): + t_dip = transition_dipole(mc, state_i, state_j) + calc_e = readEnergy(mc.fcisolver) + delta_e = abs(calc_e[state_i] - calc_e[state_j]) + return 2./3.*delta_e*sum(abs(t_dip)**2), 2./3.*delta_e*abs(t_dip)**2 + +def phospherescence_lifetime(mc, s0=0, triplet_list=[1,2,3]): + au2wavenumber = 219470. + tau = numpy.zeros(len(triplet_list)) + calc_e = readEnergy(mc.fcisolver) + for state in triplet_list: + delta_e = calc_e[state] - calc_e[s0] + fosc = oscillator_strength(mc, s0, state) + tau[triplet_list.index(state)] = 1.5/(fosc*(delta_e*au2wavenumber)**2) + tau_av = 3./(sum(1/tau)) + return tau_av, tau + def make_sched(SHCI): @@ -914,81 +959,78 @@ def writeSHCIConfFile(SHCI, nelec, Restart): # Reference determinant section f.write('#system\n') f.write('nocc %i\n' % (nelec[0] + nelec[1])) - if SHCI.dets is None: - if SHCI.__class__.__name__ == 'FakeCISolver': + if SHCI.__class__.__name__ == 'FakeCISolver': + if SHCI.initialStates is not None: + print("write determinants") + for i in range(len(SHCI.initialStates)): + for j in SHCI.initialStates[i]: + f.write('%i ' % (j)) + if (i != len(SHCI.initialStates) - 1): + f.write('\n') + else: for i in range(nelec[0]): f.write('%i ' % (2 * i)) for i in range(nelec[1]): f.write('%i ' % (2 * i + 1)) - else: - if SHCI.initialStates is not None: - for i in range(len(SHCI.initialStates)): - for j in SHCI.initialStates[i]: - f.write('%i ' % (j)) - if (i != len(SHCI.initialStates) - 1): - f.write('\n') - elif SHCI.irrep_nelec is None: - for i in range(int(nelec[0])): - f.write('%i ' % (2 * i)) - for i in range(int(nelec[1])): - f.write('%i ' % (2 * i + 1)) - else: - from pyscf import symm - from pyscf.dmrgscf import dmrg_sym - from pyscf.symm.basis import DOOH_IRREP_ID_TABLE - if SHCI.groupname is not None and SHCI.orbsym is not []: - orbsym = dmrg_sym.convert_orbsym(SHCI.groupname, SHCI.orbsym) - else: - orbsym = [1] * norb - done = [] - for k, v in SHCI.irrep_nelec.items(): - - irrep, nalpha, nbeta = [dmrg_sym.irrep_name2id(SHCI.groupname, k)],\ - v[0], v[1] - - for i in range(len(orbsym)): #loop over alpha electrons - if (orbsym[i] == irrep[0] and nalpha != 0 - and i * 2 not in done): - done.append(i * 2) - f.write('%i ' % (i * 2)) - nalpha -= 1 - if (orbsym[i] == irrep[0] and nbeta != 0 - and i * 2 + 1 not in done): - done.append(i * 2 + 1) - f.write('%i ' % (i * 2 + 1)) - nbeta -= 1 - if (nalpha != 0): - print("number of irreps %s in active space = %d" % - (k, v[0] - nalpha)) - print( - "number of irreps %s alpha electrons = %d" % (k, v[0])) - exit(1) - if (nbeta != 0): - print("number of irreps %s in active space = %d" % - (k, v[1] - nbeta)) - print( - "number of irreps %s beta electrons = %d" % (k, v[1])) - exit(1) else: - nelec_all = nelec[0] + nelec[1] - for det in SHCI.dets: - if len(det) is not nelec_all: - if len(det) is 1: - print("SHCI.dets should be a 2d array.") - else: - print("number of electrons in SHCI.dets %d does not equal to nelecas %d" % (len(det), nelec_all)) - exit(1) - else: - for i in det: - f.write('%i '%i) - if det is not SHCI.dets[-1]: + if SHCI.initialStates is not None: + print("write determinants") + for i in range(len(SHCI.initialStates)): + for j in SHCI.initialStates[i]: + f.write('%i ' % (j)) + if (i != len(SHCI.initialStates) - 1): f.write('\n') - + elif SHCI.irrep_nelec is None: + for i in range(int(nelec[0])): + f.write('%i ' % (2 * i)) + for i in range(int(nelec[1])): + f.write('%i ' % (2 * i + 1)) + else: + from pyscf import symm + from pyscf.dmrgscf import dmrg_sym + from pyscf.symm.basis import DOOH_IRREP_ID_TABLE + if SHCI.groupname is not None and SHCI.orbsym is not []: + orbsym = dmrg_sym.convert_orbsym(SHCI.groupname, SHCI.orbsym) + else: + orbsym = [1] * norb + done = [] + for k, v in SHCI.irrep_nelec.items(): + + irrep, nalpha, nbeta = [dmrg_sym.irrep_name2id(SHCI.groupname, k)],\ + v[0], v[1] + + for i in range(len(orbsym)): #loop over alpha electrons + if (orbsym[i] == irrep[0] and nalpha != 0 + and i * 2 not in done): + done.append(i * 2) + f.write('%i ' % (i * 2)) + nalpha -= 1 + if (orbsym[i] == irrep[0] and nbeta != 0 + and i * 2 + 1 not in done): + done.append(i * 2 + 1) + f.write('%i ' % (i * 2 + 1)) + nbeta -= 1 + if (nalpha != 0): + print("number of irreps %s in active space = %d" % + (k, v[0] - nalpha)) + print( + "number of irreps %s alpha electrons = %d" % (k, v[0])) + exit(1) + if (nbeta != 0): + print("number of irreps %s in active space = %d" % + (k, v[1] - nbeta)) + print( + "number of irreps %s beta electrons = %d" % (k, v[1])) + exit(1) f.write('\nend\n') + + # Handle different cases for FCIDUMP file names/paths + f.write("orbitals {}\n".format(os.path.join(SHCI.runtimeDir, SHCI.integralFile))) + f.write('nroots %r\n' % SHCI.nroots) # Variational Keyword Section - f.write('#variational\n') + f.write('\n#variational\n') if (not Restart): schedStr = make_sched(SHCI) f.write(schedStr) @@ -1008,7 +1050,7 @@ def writeSHCIConfFile(SHCI, nelec, Restart): f.write('fullrestart\n') # Perturbative Keyword Section - f.write('#pt\n') + f.write('\n#pt\n') if (SHCI.stochastic == False): f.write('deterministic \n') else: @@ -1019,16 +1061,19 @@ def writeSHCIConfFile(SHCI, nelec, Restart): f.write('sampleN %i\n' % SHCI.sampleN) # Miscellaneous Keywords - f.write('#misc\n') - f.write('noio \n') + f.write('\n#misc\n') + f.write('io \n') if (SHCI.scratchDirectory != ""): if not os.path.exists(SHCI.scratchDirectory): os.makedirs(SHCI.scratchDirectory) f.write('prefix %s\n' % (SHCI.scratchDirectory)) + if (SHCI.DoSOC): + f.write('DoSOC\n') + SHCI.DoRDM = False if (SHCI.DoRDM): f.write('DoRDM\n') - if(SHCI.DoSOC): - f.write('DoSOC\n') + if (SHCI.nroots > 1 and (SHCI.DoRDM or SHCI.DoTRDM)): + f.write('DoTRDM\n') for line in SHCI.extraline: f.write('%s\n' % line) @@ -1208,26 +1253,15 @@ def executeSHCI(SHCI): inFile = os.path.join(SHCI.runtimeDir, SHCI.configFile) outFile = os.path.join(SHCI.runtimeDir, SHCI.outputFile) try: - cmd = ' '.join((SHCI.mpiprefix, SHCI.executable, inFile)) - cmd = "%s > %s 2>&1" % (cmd, outFile) - check_call(cmd, shell=True) - #save_output(SHCI) - except CalledProcessError as err: - logger.error(SHCI, cmd) - raise err - - -def executeZSHCI(SHCI): - file1 = os.path.join(SHCI.runtimeDir, "%s/shci.e" % (SHCI.scratchDirectory))#what? - if os.path.exists(file1): #what? - os.remove(file1) #what? - inFile = os.path.join(SHCI.runtimeDir, SHCI.configFile) - outFile = os.path.join(SHCI.runtimeDir, SHCI.outputFile) - try: - cmd = ' '.join((SHCI.mpiprefix, SHCI.executableZDice2, inFile)) - cmd = "%s > %s 2>&1" % (cmd, outFile) - check_call(cmd, shell=True) - #save_output(SHCI) + if SHCI.DoSOC: + cmd = ' '.join((SHCI.mpiprefix, SHCI.zdiceExecutable, inFile)) + cmd = "%s > %s 2>&1" % (cmd, outFile) + check_call(cmd, shell=True) + else: + cmd = ' '.join((SHCI.mpiprefix, SHCI.executable, inFile)) + cmd = "%s > %s 2>&1" % (cmd, outFile) + check_call(cmd, shell=True) + #save_output(SHCI) except CalledProcessError as err: logger.error(SHCI, cmd) raise err @@ -1286,12 +1320,43 @@ def SHCISCF(mf, norb, nelec, maxM=1000, tol=1.e-8, *args, **kwargs): def dryrun(mc, mo_coeff=None): - '''Generate FCIDUMP and SHCI config file''' + ''' + Generate FCIDUMP and SHCI input.dat file for a give multiconfigurational + object. This method DOES NOT run a single iteration using Dice it just + generates all the inputs you'd need to do so. + + Args: + mc: a CASSCF/CASCI object (or RHF object) + + Kwargs: + mo_coeff: `np.ndarray` of the molecular orbital coefficients. + Default is None. + + Examples: + + >>> from pyscf import gto, scf, mcscf + >>> from pyscf.shciscf import shci + >>> mol = gto.M(atom="H 0 0 0; H 0 0 2; O 0 1 1;", symmetry=True) + >>> mf = scf.RHF(mol).run() + >>> mc = mcscf.CASCI(mf, 3, 4) + >>> mc.fcisolver = shci.SHCI(mol) + >>> shci.dryrun(mc) + + ''' if mo_coeff is None: mo_coeff = mc.mo_coeff - #bak, mc.fcisolver.onlywriteIntegral = mc.fcisolver.onlywriteIntegral, True - mc.casci(mo_coeff) - #mc.fcisolver.onlywriteIntegral = bak + + # Set orbsym b/c it's needed in `writeIntegralFile()` + if mc.fcisolver.orbsym is not []: + mc.fcisolver.orbsym = getattr(mo_coeff, "orbsym", []) + + #mc.kernel(mo_coeff) # Works, but runs full CASCI/CASSCF + h1e, ecore = mc.get_h1eff(mo_coeff) + h2e = mc.get_h2eff(mo_coeff) + writeIntegralFile(mc.fcisolver, h1e, h2e, mc.ncas, mc.nelecas, ecore) + writeSHCIConfFile(mc.fcisolver, mc.nelecas, False) + + if __name__ == '__main__': diff --git a/pyscf/shciscf/socutils.py b/pyscf/shciscf/socutils.py index 086bf7054e..7ee6d6d741 100644 --- a/pyscf/shciscf/socutils.py +++ b/pyscf/shciscf/socutils.py @@ -6,6 +6,7 @@ from pyscf import gto, lib from pyscf.lib.parameters import LIGHT_SPEED +from pyscf.lib import logger from pyscf.shciscf import shci from pyscf.x2c import x2c @@ -179,20 +180,21 @@ def get_fso2e_x2c(mol, x, rp, pLL, pLS, pSS): ao_loc.append(ao_loc_orig[-1]) shl_size.append(ao_loc[-1] - ao_loc[-2]) shl_slice.append(nbas) - print(ao_loc, shl_size, shl_slice) nbas = len(shl_size) + logger.info(mol, "Cutting basis functions into %d batches, need to calculate %d integrals batches.", nbas, nbas**4) + start = time.clock() for i in range(0, nbas): for j in range(0, nbas): for k in range(0, nbas): for l in range(0, nbas): + start_this = time.clock() ddint = mol.intor('int2e_ip1ip2_sph', comp=9, shls_slice=[shl_slice[i], shl_slice[i+1], shl_slice[j], shl_slice[j+1], shl_slice[k], shl_slice[k+1], shl_slice[l], shl_slice[l+1]]).reshape(3, 3, -1) kint = numpy.zeros(3 * shl_size[i] * shl_size[j] * shl_size[k] * shl_size[l]).reshape( 3, shl_size[i], shl_size[j], shl_size[k], shl_size[l]) kint[0] = (ddint[1, 2] - ddint[2, 1]).reshape(shl_size[i], shl_size[j], shl_size[k], shl_size[l]) kint[1] = (ddint[2, 0] - ddint[0, 2]).reshape(shl_size[i], shl_size[j], shl_size[k], shl_size[l]) kint[2] = (ddint[0, 1] - ddint[1, 0]).reshape(shl_size[i], shl_size[j], shl_size[k], shl_size[l]) - #print("Time elapsed for integral calculation:", end - start, i, j, k, l, nbas) gsoLL[:, ao_loc[j]:ao_loc[j+1], ao_loc[l]:ao_loc[l+1]] \ +=-2.0*lib.einsum('ilmkn,lk->imn', kint, \ @@ -211,8 +213,8 @@ def get_fso2e_x2c(mol, x, rp, pLL, pLS, pSS): gsoSS[:, ao_loc[i]:ao_loc[i+1], ao_loc[k]:ao_loc[k+1]] \ += 2.0*lib.einsum('imlnk,lk->imn', kint, \ pLL[ao_loc[j]:ao_loc[j+1], ao_loc[l]:ao_loc[l+1]]) - #print(" Time elapsed for einsum:", time.clock() - start) - + logger.info(mol, "Time elapsed for %dth batch in %d batches is %g, cumulates time is %g.", \ + i*nbas**3+j*nbas**2+k*nbas+l+1, nbas*4, time.clock()-start_this, time.clock()-start) for comp in range(0, 3): fso2e[comp] = gsoLL[comp] + gsoLS[comp].dot(x) + x.T.dot(-gsoLS[comp].T) + x.T.dot(gsoSS[comp].dot(x)) fso2e[comp] = reduce(numpy.dot, (rp.T, fso2e[comp], rp)) @@ -455,6 +457,7 @@ def writeSOCIntegrals(mc, hso1e += xmol.intor('ECPso') # two electron terms + logger.note(xmol, "Start calculating two-electron contribution to SOC integrals, be patient.") if (pictureChange2e == "bp"): hso1e += -factor * get_fso2e_bp(xmol, dm) elif (pictureChange2e == "bp1c"): @@ -511,3 +514,4 @@ def doSOC(mc, pictureChange1e="bp", pictureChange2e="bp", uncontract=False, atom mch.fcisolver.DoSOC = True mch.fcisolver.DoRDM = False shci.dryrun(mch, mc.mo_coeff) + shci.executeSHCI(mc.fcisolver) From 0459b78ceb19317fa6d9874ee409edd6f9021d67 Mon Sep 17 00:00:00 2001 From: Sandeep Sharma Date: Sun, 23 Feb 2020 20:57:17 -0700 Subject: [PATCH 08/26] vmcscf added. --- pyscf/gto/__init__.py | 1 + pyscf/vmcscf/__init__.py | 18 + pyscf/vmcscf/vmc.py | 620 +++++++++++++++++++++++++ pyscf/vmcscf/vmcActiveActive.py | 779 ++++++++++++++++++++++++++++++++ pyscf/x2c/sfx2c1e.py | 21 + 5 files changed, 1439 insertions(+) create mode 100644 pyscf/vmcscf/__init__.py create mode 100644 pyscf/vmcscf/vmc.py create mode 100644 pyscf/vmcscf/vmcActiveActive.py diff --git a/pyscf/gto/__init__.py b/pyscf/gto/__init__.py index 60be5e5d35..9741f2e061 100644 --- a/pyscf/gto/__init__.py +++ b/pyscf/gto/__init__.py @@ -22,6 +22,7 @@ from pyscf.gto.mole import * from pyscf.gto.moleintor import getints, getints_by_shell from pyscf.gto.eval_gto import eval_gto +from pyscf.gto.write_gto import write_gto from pyscf.gto import ecp parse = basis.parse diff --git a/pyscf/vmcscf/__init__.py b/pyscf/vmcscf/__init__.py new file mode 100644 index 0000000000..31982cc7a5 --- /dev/null +++ b/pyscf/vmcscf/__init__.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python +# Copyright 2014-2018 The PySCF Developers. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: Sandeep Sharma +# James Smith +# diff --git a/pyscf/vmcscf/vmc.py b/pyscf/vmcscf/vmc.py new file mode 100644 index 0000000000..9812d58a27 --- /dev/null +++ b/pyscf/vmcscf/vmc.py @@ -0,0 +1,620 @@ +#!/usr/bin/env python +# Copyright 2014-2018 The PySCF Developers. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: Sandeep Sharma +# +''' +VMC solver for CASCI and CASSCF. +''' + +from functools import reduce +import ctypes +import os +import sys +import struct +import time +import tempfile +import warnings +from subprocess import check_call +from subprocess import CalledProcessError + +from pyscf.lo import pipek, boys, edmiston, iao, ibo +from pyscf import ao2mo, tools, scf, mcscf, lo, gto +import numpy +import scipy +import pyscf.tools +import pyscf.lib +from pyscf.lib import logger +from pyscf.lib import chkfile +from pyscf import mcscf +ndpointer = numpy.ctypeslib.ndpointer + +# Settings +try: + from pyscf.vmcscf import settings +except ImportError: + from pyscf import __config__ + settings = lambda: None + settings.VMCEXE = getattr(__config__, 'vmc_VMCEXE', None) + settings.VMCSCRATCHDIR = getattr(__config__, 'vmc_VMCSCRATCHDIR', None) + settings.VMCRUNTIMEDIR = getattr(__config__, 'vmc_VMCRUNTIMEDIR', None) + settings.MPIPREFIX = getattr(__config__, 'vmc_MPIPREFIX', None) + if (settings.VMCEXE is None or settings.VMCSCRATCHDIR is None): + import sys + sys.stderr.write('settings.py not found for module vmcscf. Please create %s\n' + % os.path.join(os.path.dirname(__file__), 'settings.py')) + raise ImportError('settings.py not found') + +# Libraries +from pyscf.lib import load_library +libE3unpack = load_library('libicmpspt') +# TODO: Organize this better. +vmcLib = load_library('libshciscf') + + +writeIntNoSymm = vmcLib.writeIntNoSymm +writeIntNoSymm.argtypes = [ + ctypes.c_int, + ndpointer(ctypes.c_double), + ndpointer(ctypes.c_double), ctypes.c_double, ctypes.c_int, + ndpointer(ctypes.c_int) +] + +fcidumpFromIntegral = vmcLib.fcidumpFromIntegral +fcidumpFromIntegral.restype = None +fcidumpFromIntegral.argtypes = [ + ctypes.c_char_p, + ndpointer(ctypes.c_double, flags="C_CONTIGUOUS"), + ndpointer(ctypes.c_double, flags="C_CONTIGUOUS"), ctypes.c_size_t, + ctypes.c_size_t, ctypes.c_double, + ndpointer(ctypes.c_int32, flags="C_CONTIGUOUS"), ctypes.c_size_t +] + +r2RDM = vmcLib.r2RDM +r2RDM.restype = None +r2RDM.argtypes = [ + ndpointer(ctypes.c_double, flags="C_CONTIGUOUS"), ctypes.c_size_t, + ctypes.c_char_p +] + + +class VMC(pyscf.lib.StreamObject): + r'''VMC program interface and object to hold VMC program input parameters. + + Attributes: + initialStates: [[int]] + groupname : str + groupname, orbsym together can control whether to employ symmetry in + the calculation. "groupname = None and orbsym = []" requires the + VMC program using C1 symmetry. + useExtraSymm : False + if the symmetry of the molecule is Dooh or Cooh, then this keyword uses + complex orbitals to make full use of this symmetry + + Examples: + + ''' + + def __init__(self, mol=None): + self.mol = mol + if mol is None: + self.stdout = sys.stdout + self.verbose = logger.NOTE + else: + self.stdout = mol.stdout + self.verbose = mol.verbose + self.outputlevel = 2 + + self.executable = settings.VMCEXE + self.scratchDirectory = settings.VMCSCRATCHDIR + self.mpiprefix = settings.MPIPREFIX + + self.wavefunction = "jastrowslater" + self.slater = "ghf" + self.maxIter = 100 + self.stochasticIter = 1000 + self.stochasticIterTight = 4*self.stochasticIter + + self.integralFile = "FCIDUMP" + self.configFile = "vmc.dat" + self.rdmconfigFile = "rdmvmc.dat" + self.outputFile = "vmc.out" + self.rdmoutputFile = "rdmvmc.out" + if getattr(settings, 'VMCRUNTIMEDIR', None): + self.runtimeDir = settings.VMCRUNTIMEDIR + else: + self.runtimeDir = '.' + self.extraline = [] + + if mol is None: + self.groupname = None + else: + if mol.symmetry: + self.groupname = mol.groupname + else: + self.groupname = None + + + def dump_flags(self, verbose=None): + if verbose is None: + verbose = self.verbose + log = logger.Logger(self.stdout, verbose) + log.info('') + log.info('******** VMC flags ********') + log.info('executable = %s', self.executable) + log.info('mpiprefix = %s', self.mpiprefix) + log.info('scratchDirectory = %s', self.scratchDirectory) + log.info('integralFile = %s', + os.path.join(self.runtimeDir, self.integralFile)) + log.info('configFile = %s', + os.path.join(self.runtimeDir, self.configFile)) + log.info('outputFile = %s', + os.path.join(self.runtimeDir, self.outputFile)) + log.info('maxIter = %d', self.maxIter) + log.info('') + return self + + # ABOUT RDMs AND INDEXES: ----------------------------------------------------------------------- + # There is two ways to stored an RDM + # (the numbers help keep track of creation/annihilation that go together): + # E3[i1,j2,k3,l3,m2,n1] is the way DICE outputs text and bin files + # E3[i1,j2,k3,l1,m2,n3] is the way the tensors need to be written for SQA and ICPT + # + # --> See various remarks in the pertinent functions below. + # ----------------------------------------------------------------------------------------------- + + def make_rdm12(self, state, norb, nelec, link_index=None, **kwargs): + nelectrons = 0 + if isinstance(nelec, (int, numpy.integer)): + nelectrons = nelec + else: + nelectrons = nelec[0] + nelec[1] + + # The 2RDMs written by "VMCrdm::saveRDM" in DICE + # are written as E2[i1,j2,k1,l2] + # and stored here as E2[i1,k1,j2,l2] (for PySCF purposes) + # This is NOT done with SQA in mind. + twopdm = numpy.zeros((norb, norb, norb, norb)) + file2pdm = "spatialRDM.%d.%d.txt" % (state, state) + # file2pdm = file2pdm.encode() # .encode for python3 compatibility + r2RDM(twopdm, norb, + os.path.join(file2pdm).encode()) + + # (This is coherent with previous statement about indexes) + onepdm = numpy.einsum('ikjj->ki', twopdm) + onepdm /= (nelectrons - 1) + return onepdm, twopdm + + + def make_rdm12_forSQA(self, state, norb, nelec, link_index=None, **kwargs): + nelectrons = 0 + if isinstance(nelec, (int, numpy.integer)): + nelectrons = nelec + else: + nelectrons = nelec[0]+nelec[1] + + # The 2RDMs written by "VMCrdm::saveRDM" in DICE + # are written as E2[i1,j2,k1,l2] + # and stored here as E2[i1,k1,j2,l2] (for PySCF purposes) + # This is NOT done with SQA in mind. + twopdm = numpy.zeros((norb, norb, norb, norb)) + file2pdm = "spatialRDM.%d.%d.txt" % (state,state) + r2RDM(twopdm, norb, + os.path.join(self.scratchDirectory, file2pdm).endcode()) + twopdm=twopdm.transpose(0,2,1,3) + + # (This is coherent with previous statement about indexes) + onepdm = numpy.einsum('ijkj->ki', twopdm) + onepdm /= (nelectrons-1) + return onepdm, twopdm + + + def kernel(self, h1e, eri, norb, nelec, fciRestart=None, ecore=0, + **kwargs): + """ + Approximately solve CI problem for the specified active space. + """ + writeIntegralFile(self, h1e, eri, norb, nelec, ecore) + self.writeConfig() + executeVMC(self) + + #onerdm, twordm = make_rdm12(self, 0, norb, nelec) + + outFile = os.path.join(self.runtimeDir, self.outputFile) + f = open(outFile, 'r') + l = f.readlines() + calc_e = float(l[-1].split()[1]) + roots = 0 + return calc_e, roots + + + def writeConfig(self, restart=True, readBestDeterminant=False): + confFile = os.path.join(self.runtimeDir, self.configFile) + + f = open(confFile, 'w') + f.write("%s\n" %self.wavefunction) + f.write("complex\n") + f.write("%s\n" %self.slater) + f.write("maxiter %d\n" %self.maxIter) + f.write("stochasticIter %d\n" % self.stochasticIter) + #if (restart) : + #f.write("fullrestart\n") + if (readBestDeterminant) : + f.write("determinants bestDet") + f.close() + + + confFile = os.path.join(self.runtimeDir, self.rdmconfigFile) + + f = open(confFile, 'w') + f.write("slatertwordm\n") + f.write("complex\n") + f.write("%s\n" %self.slater) + f.write("maxiter %d\n" %self.maxIter) + f.write("stochasticIter %d\n" % self.stochasticIterTight) + f.close() + + + def spin_square(self, civec, norb, nelec): + if isinstance(nelec, (int, numpy.integer)): + nelecb = nelec // 2 + neleca = nelec - nelecb + else: + neleca, nelecb = nelec + s = (neleca - nelecb) * .5 + ss = s * (s + 1) + if isinstance(civec, int): + return ss, s * 2 + 1 + else: + return [ss] * len(civec), [s * 2 + 1] * len(civec) + + def cleanup_dice_files(self): + """ + Remove the files used for Dice communication. + """ + os.remove("input.dat") + os.remove("output.dat") + os.remove("FCIDUMP") + + +def print1Int(h1, name): + with open('%s.X' % (name), 'w') as fout: + fout.write('%d\n' % h1[0].shape[0]) + for i in range(h1[0].shape[0]): + for j in range(h1[0].shape[0]): + if (abs(h1[0, i, j]) > 1.e-8): + fout.write( + '%16.10g %4d %4d\n' % (h1[0, i, j], i + 1, j + 1)) + + with open('%s.Y' % (name), 'w') as fout: + fout.write('%d\n' % h1[1].shape[0]) + for i in range(h1[1].shape[0]): + for j in range(h1[1].shape[0]): + if (abs(h1[1, i, j]) > 1.e-8): + fout.write( + '%16.10g %4d %4d\n' % (h1[1, i, j], i + 1, j + 1)) + + with open('%s.Z' % (name), 'w') as fout: + fout.write('%d\n' % h1[2].shape[0]) + for i in range(h1[2].shape[0]): + for j in range(h1[2].shape[0]): + if (abs(h1[2, i, j]) > 1.e-8): + fout.write( + '%16.10g %4d %4d\n' % (h1[2, i, j], i + 1, j + 1)) + + with open('%sZ' % (name), 'w') as fout: + fout.write('%d\n' % h1[2].shape[0]) + for i in range(h1[2].shape[0]): + for j in range(h1[2].shape[0]): + if (abs(h1[2, i, j]) > 1.e-8): + fout.write( + '%16.10g %4d %4d\n' % (h1[2, i, j], i + 1, j + 1)) + + + +def writeIntegralFile(VMC, h1eff, eri_cas, norb, nelec, ecore=0): + if isinstance(nelec, (int, numpy.integer)): + neleca = nelec // 2 + nelec % 2 + nelecb = nelec - neleca + else: + neleca, nelecb = nelec + + # The name of the FCIDUMP file, default is "FCIDUMP". + integralFile = os.path.join(VMC.runtimeDir, VMC.integralFile) + + if not os.path.exists(VMC.scratchDirectory): + os.makedirs(VMC.scratchDirectory) + + from pyscf import symm + from pyscf.dmrgscf import dmrg_sym + + if (VMC.groupname == 'Dooh' + or VMC.groupname == 'Coov') and VMC.useExtraSymm: + coeffs, nRows, rowIndex, rowCoeffs, orbsym = D2htoDinfh( + VMC, norb, nelec) + + newintt = numpy.tensordot(coeffs.conj(), h1eff, axes=([1], [0])) + newint1 = numpy.tensordot(newintt, coeffs, axes=([1], [1])) + newint1r = numpy.zeros(shape=(norb, norb), order='C') + for i in range(norb): + for j in range(norb): + newint1r[i, j] = newint1[i, j].real + int2 = pyscf.ao2mo.restore(1, eri_cas, norb) + eri_cas = numpy.zeros_like(int2) + + transformDinfh(norb, numpy.ascontiguousarray(nRows, numpy.int32), + numpy.ascontiguousarray(rowIndex, numpy.int32), + numpy.ascontiguousarray(rowCoeffs, numpy.float64), + numpy.ascontiguousarray(int2, numpy.float64), + numpy.ascontiguousarray(eri_cas, numpy.float64)) + + writeIntNoSymm(norb, numpy.ascontiguousarray(newint1r, numpy.float64), + numpy.ascontiguousarray(eri_cas, numpy.float64), + ecore, neleca + nelecb, + numpy.asarray(orbsym, dtype=numpy.int32)) + + else: + if VMC.groupname is not None and VMC.orbsym is not []: + orbsym = dmrg_sym.convert_orbsym(VMC.groupname, VMC.orbsym) + else: + orbsym = [1] * norb + + eri_cas = pyscf.ao2mo.restore(8, eri_cas, norb) + # Writes the FCIDUMP file using functions in VMC_tools.cpp. + integralFile = integralFile.encode() # .encode for python3 compatibility + fcidumpFromIntegral(integralFile, h1eff, eri_cas, norb, + neleca + nelecb, ecore, + numpy.asarray(orbsym, dtype=numpy.int32), + abs(neleca - nelecb)) + + +def executeVMC(VMC): + inFile = os.path.join(VMC.runtimeDir, VMC.configFile) + outFile = os.path.join(VMC.runtimeDir, VMC.outputFile) + try: + cmd = ' '.join((VMC.mpiprefix, VMC.executable, inFile)) + cmd = "%s > %s 2>&1" % (cmd, outFile) + check_call(cmd, shell=True) + #save_output(VMC) + except CalledProcessError as err: + logger.error(VMC, cmd) + raise err + + inFile = os.path.join(VMC.runtimeDir, VMC.rdmconfigFile) + outFile = os.path.join(VMC.runtimeDir, VMC.rdmoutputFile) + try: + cmd = ' '.join((VMC.mpiprefix, VMC.executable, inFile)) + cmd = "%s > %s 2>&1" % (cmd, outFile) + check_call(cmd, shell=True) + #save_output(VMC) + except CalledProcessError as err: + logger.error(VMC, cmd) + raise err + + +#def save_output(VMC): +# for i in range(50): +# if os.path.exists(os.path.join(VMC.runtimeDir, "output%02d.dat"%(i))): +# continue +# else: +# import shutil +# shutil.copy2(os.path.join(VMC.runtimeDir, "output.dat"),os.path.join(VMC.runtimeDir, "output%02d.dat"%(i))) +# shutil.copy2(os.path.join(VMC.runtimeDir, "%s/vmc.e"%(VMC.scratchDirectory)), os.path.join(VMC.runtimeDir, "vmc%02d.e"%(i))) +# #print('BM copied into "output%02d.dat"'%(i)) +# #print('BM copied into "vmc%02d.e"'%(i)) +# break + + +def readEnergy(VMC): + file1 = open( + os.path.join(VMC.runtimeDir, "%s/vmc.e" % (VMC.scratchDirectory)), + "rb") + format = ['d'] * VMC.nroots + format = ''.join(format) + calc_e = struct.unpack(format, file1.read()) + file1.close() + if VMC.nroots == 1: + return calc_e[0] + else: + return list(calc_e) + +def localizeValence(mf, mo_coeff, method="iao"): + if (method == "iao"): + return iao.iao(mf.mol, mo_coeff) + elif (method == "ibo"): + a = iao.iao(mf.mol, mo_coeff) + a = lo.vec_lowdin(a, mf.get_ovlp()) + return ibo.ibo(mf.mol, mo_coeff, iaos=a) + elif (method == "boys"): + return boys.Boys(mf.mol).kernel(mo_coeff) + elif (method == "er"): + return edmiston.ER(mf.mol).kernel(mo_coeff) + +# can be used for all electron, but not recommended +def bestDetValence(mol, lmo, occ, eri, writeToFile=True): + + # index of the ao contributing the most to an lmo + maxLMOContributers = [ numpy.argmax(numpy.abs(lmo[::,i])) for i in range(lmo.shape[1]) ] + + # end AO index for each atom in ascending order + atomNumAOs = [ i[1][3] - 1 for i in enumerate(mol.aoslice_nr_by_atom()) ] + + lmoSites = [ [] for i in range(mol.natm) ] #lmo's cetered on each atom + for i in enumerate(maxLMOContributers): + lmoSites[numpy.searchsorted(numpy.array(atomNumAOs), i[1])].append(i[0]) + + bestDet = [0 for i in range(lmo.shape[1])] + def pair(i): + return i*(i+1)//2+i + for i in enumerate(occ): + if eri.ndim == 2: + onSiteIntegrals = [ (j, eri[pair(j),pair(j)]) for (n,j) in enumerate(lmoSites[i[0]]) ] + elif eri.ndim == 1: + onSiteIntegrals = [ (j, eri[pair(pair(j))]) for (n,j) in enumerate(lmoSites[i[0]]) ] + onSiteIntegrals.sort(key = lambda tup : tup[1], reverse=True) + for k in range(i[1][0]): + bestDet[onSiteIntegrals[k][0]] = '2' + for l in range(i[1][1]): + bestDet[onSiteIntegrals[i[1][0] + l][0]] = 'a' + for m in range(i[1][2]): + bestDet[onSiteIntegrals[i[1][0] + i[1][1] + m][0]] = 'b' + + bestDetStr = ' '.join(bestDet) + print('bestDet: ' + bestDetStr) + if writeToFile: + fileh = open("bestDet", 'w') + fileh.write('1. ' + bestDetStr + '\n') + fileh.close() + + return bestDetStr + +def writeMat(mat, fileName, isComplex): + fileh = open(fileName, 'w') + for i in range(mat.shape[0]): + for j in range(mat.shape[1]): + if (isComplex): + fileh.write('(%16.10e, %16.10e) '%(mat[i,j].real, mat[i,j].imag)) + else: + fileh.write('%16.10e '%(mat[i,j])) + fileh.write('\n') + fileh.close() + +def VMCSCF(mf, ncore, nact, occ=None, frozen = None, loc="iao", proc=None,*args, **kwargs): + + mol = mf.mol + mo = mf.mo_coeff + nelec = mol.nelectron - 2 * ncore + mc = mcscf.CASSCF(mf, nact, nelec) + + if loc is not None: + lmo = localizeValence(mf, mo[:, ncore:ncore+nact], loc) + tools.molden.from_mo(mf.mol, 'valenceOrbs.molden', lmo) + + h1cas, energy_core = mcscf.casci.h1e_for_cas(mc, mf.mo_coeff, nact, ncore) + mo_core = mc.mo_coeff[:,:ncore] + core_dm = 2 * mo_core.dot(mo_core.T) + corevhf = mc.get_veff(mol, core_dm) + h1eff = lmo.T.dot(mc.get_hcore() + corevhf).dot(lmo) + eri = ao2mo.kernel(mol, lmo) + else: + lmo = numpy.eye(nact) + h1eff = mf.get_hcore() + eri = mf._eri + energy_core = 0 + if occ is not None: + bestDetValence(mol, lmo, occ, eri, True) + + + #prepare initial guess for the HF orbitals + norb = nact + molA = gto.M() + molA.nelectron = nelec + molA.verbose = 4 + molA.incore_anyway = True + gmf = scf.GHF(molA) + gmf.get_hcore = lambda *args: scipy.linalg.block_diag(h1eff, h1eff) + gmf.get_ovlp = lambda *args: numpy.identity(2*norb) + gmf.energy_nuc = lambda *args: energy_core + gmf._eri = eri + + dm = gmf.get_init_guess() + dm = dm + 2 * numpy.random.rand(2*norb, 2*norb) + gmf.level_shift = 0.1 + gmf.max_cycle = 500 + print(gmf.kernel(dm0 = dm)) + mocoeff = numpy.zeros((2*norb, 2*norb), dtype=complex) + mocoeff = 1.*gmf.mo_coeff + writeMat(gmf.mo_coeff, "hf.txt", True) + + #run an initial very short turn + mc.fcisolver = VMC(mf.mol) + mc.fcisolver.maxIter = 5 + writeIntegralFile(mc.fcisolver, h1eff, eri, nact, nelec, energy_core) + mc.fcisolver.writeConfig(restart=False, readBestDeterminant= (occ is not None)) + mc.fcisolver.mpiprefix = "mpirun" + if (proc is not None): + mc.fcisolver.mpiprefix = ("mpirun -np %d" %(proc)) + executeVMC(mc.fcisolver) + + + mc.fcisolver.maxIter = 100 + mf.mo_coeff[:,ncore:ncore+nact] = lmo + mc.mo_coeff = 1.*mf.mo_coeff + mc.internal_rotation = True + + if frozen is not None: + mc.frozen = frozen + + if mc.chkfile == mc._scf._chkfile.name: + # Do not delete chkfile after mcscf + mc.chkfile = tempfile.mktemp(dir=settings.VMCSCRATCHDIR) + if not os.path.exists(settings.VMCSCRATCHDIR): + os.makedirs(settings.VMCSCRATCHDIR) + return mc + + +if __name__ == '__main__': + from pyscf import gto, scf, mcscf, dmrgscf + from pyscf.vmcscf import vmc + + # Initialize benzene molecule + atomstring = ''' + C 0.000517 0.000000 0.000299 + C 0.000517 0.000000 1.394692 + C 1.208097 0.000000 2.091889 + C 2.415677 0.000000 1.394692 + C 2.415677 0.000000 0.000299 + C 1.208097 0.000000 -0.696898 + H -0.939430 0.000000 -0.542380 + H -0.939430 0.000000 1.937371 + H 1.208097 0.000000 3.177246 + H 3.355625 0.000000 1.937371 + H 3.355625 0.000000 -0.542380 + H 1.208097 0.000000 -1.782255 + ''' + mol = gto.M( + atom = atomstring, + unit = 'angstrom', + basis = 'sto-6g', + verbose = 4, + symmetry= 0, + spin = 0) + + # Create HF molecule + mf = scf.RHF(mol) + mf.conv_tol = 1e-9 + mf.scf() + + + ncore, nact = 6, 30 + occ = [] + configC = [[0,4,0], [0,0,4]] # no double occ, 4 up or 4 dn + configH = [[0,1,0], [0,0,1]] # no double occ, 1 up or 1 dn + for i in range(6): + occ.append(configC[i%2]) + for i in range(6): + occ.append(configH[(i+1)%2]) + + + frozen = list(range(0,ncore)) + list (range(36, mf.mo_coeff.shape[0])) + print (frozen) + # Create VMC molecule for just variational opt. + # Active spaces chosen to reflect valence active space. + mch = vmc.VMCSCF(mf, ncore, nact, occ=occ, loc="ibo",\ + frozen = frozen) + mch.fcisolver.stochasticIter = 400 + mch.fcisolver.maxIter = 50 + e_noPT = mch.mc2step()[0] + diff --git a/pyscf/vmcscf/vmcActiveActive.py b/pyscf/vmcscf/vmcActiveActive.py new file mode 100644 index 0000000000..64317c31c0 --- /dev/null +++ b/pyscf/vmcscf/vmcActiveActive.py @@ -0,0 +1,779 @@ +#!/usr/bin/env python +# Copyright 2014-2018 The PySCF Developers. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: Sandeep Sharma +# +''' +VMC solver for CASCI and CASSCF. +''' + +from functools import reduce +import ctypes +import os +import sys +import struct +import time +import tempfile +import warnings +from subprocess import check_call +from subprocess import CalledProcessError + +from pyscf.lo import pipek, boys, edmiston, iao, ibo +from pyscf import ao2mo, tools, scf, mcscf, lo, gto +from pyscf.shciscf import shci +import numpy +import scipy +from scipy.linalg import fractional_matrix_power +import pyscf.tools +import pyscf.lib +from pyscf import lib +from pyscf.lib import logger +from pyscf.lib import chkfile +from pyscf import mcscf +from scipy.linalg import expm +ndpointer = numpy.ctypeslib.ndpointer + +# Settings +try: + from pyscf.vmcscf import settings +except ImportError: + from pyscf import __config__ + settings = lambda: None + settings.VMCEXE = getattr(__config__, 'vmc_VMCEXE', None) + settings.VMCSCRATCHDIR = getattr(__config__, 'vmc_VMCSCRATCHDIR', None) + settings.VMCRUNTIMEDIR = getattr(__config__, 'vmc_VMCRUNTIMEDIR', None) + settings.MPIPREFIX = getattr(__config__, 'vmc_MPIPREFIX', None) + if (settings.VMCEXE is None or settings.VMCSCRATCHDIR is None): + import sys + sys.stderr.write('settings.py not found for module vmcscf. Please create %s\n' + % os.path.join(os.path.dirname(__file__), 'settings.py')) + raise ImportError('settings.py not found') + +# Libraries +from pyscf.lib import load_library +libE3unpack = load_library('libicmpspt') +# TODO: Organize this better. +vmcLib = load_library('libshciscf') + + +writeIntNoSymm = vmcLib.writeIntNoSymm +writeIntNoSymm.argtypes = [ + ctypes.c_int, + ndpointer(ctypes.c_double), + ndpointer(ctypes.c_double), ctypes.c_double, ctypes.c_int, + ndpointer(ctypes.c_int) +] + +fcidumpFromIntegral = vmcLib.fcidumpFromIntegral +fcidumpFromIntegral.restype = None +fcidumpFromIntegral.argtypes = [ + ctypes.c_char_p, + ndpointer(ctypes.c_double, flags="C_CONTIGUOUS"), + ndpointer(ctypes.c_double, flags="C_CONTIGUOUS"), ctypes.c_size_t, + ctypes.c_size_t, ctypes.c_double, + ndpointer(ctypes.c_int32, flags="C_CONTIGUOUS"), ctypes.c_size_t +] + +r2RDM = vmcLib.r2RDM +r2RDM.restype = None +r2RDM.argtypes = [ + ndpointer(ctypes.c_double, flags="C_CONTIGUOUS"), ctypes.c_size_t, + ctypes.c_char_p +] + + +class VMC(pyscf.lib.StreamObject): + r'''VMC program interface and object to hold VMC program input parameters. + + Attributes: + initialStates: [[int]] + groupname : str + groupname, orbsym together can control whether to employ symmetry in + the calculation. "groupname = None and orbsym = []" requires the + VMC program using C1 symmetry. + useExtraSymm : False + if the symmetry of the molecule is Dooh or Cooh, then this keyword uses + complex orbitals to make full use of this symmetry + + Examples: + + ''' + + def __init__(self, mol=None): + self.mol = mol + if mol is None: + self.stdout = sys.stdout + self.verbose = logger.NOTE + else: + self.stdout = mol.stdout + self.verbose = mol.verbose + self.outputlevel = 2 + + self.executable = settings.VMCEXE + self.scratchDirectory = settings.VMCSCRATCHDIR + self.mpiprefix = settings.MPIPREFIX + + self.wavefunction = "jastrowslater" + self.slater = "ghf" + self.maxIter = 100 + self.stochasticIter = 1000 + self.stochasticIterTight = 4*self.stochasticIter + + self.integralFile = "FCIDUMP" + self.configFile = "vmc.dat" + self.rdmconfigFile = "rdmvmc.dat" + self.outputFile = "vmc.out" + self.rdmoutputFile = "rdmvmc.out" + if getattr(settings, 'VMCRUNTIMEDIR', None): + self.runtimeDir = settings.VMCRUNTIMEDIR + else: + self.runtimeDir = '.' + self.extraline = [] + + if mol is None: + self.groupname = None + else: + if mol.symmetry: + self.groupname = mol.groupname + else: + self.groupname = None + + + def dump_flags(self, verbose=None): + if verbose is None: + verbose = self.verbose + log = logger.Logger(self.stdout, verbose) + log.info('') + log.info('******** VMC flags ********') + log.info('executable = %s', self.executable) + log.info('mpiprefix = %s', self.mpiprefix) + log.info('scratchDirectory = %s', self.scratchDirectory) + log.info('integralFile = %s', + os.path.join(self.runtimeDir, self.integralFile)) + log.info('configFile = %s', + os.path.join(self.runtimeDir, self.configFile)) + log.info('outputFile = %s', + os.path.join(self.runtimeDir, self.outputFile)) + log.info('maxIter = %d', self.maxIter) + log.info('') + return self + + # ABOUT RDMs AND INDEXES: ----------------------------------------------------------------------- + # There is two ways to stored an RDM + # (the numbers help keep track of creation/annihilation that go together): + # E3[i1,j2,k3,l3,m2,n1] is the way DICE outputs text and bin files + # E3[i1,j2,k3,l1,m2,n3] is the way the tensors need to be written for SQA and ICPT + # + # --> See various remarks in the pertinent functions below. + # ----------------------------------------------------------------------------------------------- + + def make_rdm12(self, state, norb, nelec, link_index=None, **kwargs): + nelectrons = 0 + if isinstance(nelec, (int, numpy.integer)): + nelectrons = nelec + else: + nelectrons = nelec[0] + nelec[1] + + # The 2RDMs written by "VMCrdm::saveRDM" in DICE + # are written as E2[i1,j2,k1,l2] + # and stored here as E2[i1,k1,j2,l2] (for PySCF purposes) + # This is NOT done with SQA in mind. + twopdm = numpy.zeros((norb, norb, norb, norb)) + file2pdm = "spatialRDM.%d.%d.txt" % (state, state) + # file2pdm = file2pdm.encode() # .encode for python3 compatibility + r2RDM(twopdm, norb, + os.path.join(file2pdm).encode()) + + # (This is coherent with previous statement about indexes) + onepdm = lib.einsum('ikjj->ki', twopdm) + onepdm /= (nelectrons - 1) + return onepdm, twopdm + + + def make_rdm12_forSQA(self, state, norb, nelec, link_index=None, **kwargs): + nelectrons = 0 + if isinstance(nelec, (int, numpy.integer)): + nelectrons = nelec + else: + nelectrons = nelec[0]+nelec[1] + + # The 2RDMs written by "VMCrdm::saveRDM" in DICE + # are written as E2[i1,j2,k1,l2] + # and stored here as E2[i1,k1,j2,l2] (for PySCF purposes) + # This is NOT done with SQA in mind. + twopdm = numpy.zeros((norb, norb, norb, norb)) + file2pdm = "spatialRDM.%d.%d.txt" % (state,state) + r2RDM(twopdm, norb, + os.path.join(self.scratchDirectory, file2pdm).endcode()) + twopdm=twopdm.transpose(0,2,1,3) + + # (This is coherent with previous statement about indexes) + onepdm = numpy.einsum('ijkj->ki', twopdm) + onepdm /= (nelectrons-1) + return onepdm, twopdm + + + def kernel(self, h1e, eri, norb, nelec, fciRestart=None, ecore=0, + **kwargs): + """ + Approximately solve CI problem for the specified active space. + """ + writeIntegralFile(self, h1e, eri, norb, nelec, ecore) + self.writeConfig() + executeVMC(self) + + #onerdm, twordm = make_rdm12(self, 0, norb, nelec) + + outFile = os.path.join(self.runtimeDir, self.outputFile) + f = open(outFile, 'r') + l = f.readlines() + calc_e = float(l[-1].split()[1]) + roots = 0 + return calc_e, roots + + + def writeConfig(self, restart=True, readBestDeterminant=False): + confFile = os.path.join(self.runtimeDir, self.configFile) + + f = open(confFile, 'w') + f.write("%s\n" %self.wavefunction) + f.write("complex\n") + f.write("%s\n" %self.slater) + f.write("maxiter %d\n" %self.maxIter) + f.write("stochasticIter %d\n" % self.stochasticIter) + #f.write("deterministic\n") + if (restart) : + f.write("fullrestart\n") + if (readBestDeterminant) : + f.write("determinants bestDet") + f.close() + + + confFile = os.path.join(self.runtimeDir, self.rdmconfigFile) + + f = open(confFile, 'w') + f.write("slatertwordm\n") + f.write("complex\n") + f.write("%s\n" %self.slater) + f.write("maxiter %d\n" %self.maxIter) + f.write("stochasticIter %d\n" % self.stochasticIterTight) + #f.write("deterministic\n") + f.close() + + + +def writeIntegralFile(VMC, h1eff, eri_cas, norb, nelec, ecore=0): + if isinstance(nelec, (int, numpy.integer)): + neleca = nelec // 2 + nelec % 2 + nelecb = nelec - neleca + else: + neleca, nelecb = nelec + + # The name of the FCIDUMP file, default is "FCIDUMP". + integralFile = os.path.join(VMC.runtimeDir, VMC.integralFile) + + if not os.path.exists(VMC.scratchDirectory): + os.makedirs(VMC.scratchDirectory) + + from pyscf import symm + from pyscf.dmrgscf import dmrg_sym + + if (VMC.groupname == 'Dooh' + or VMC.groupname == 'Coov') and VMC.useExtraSymm: + coeffs, nRows, rowIndex, rowCoeffs, orbsym = D2htoDinfh( + VMC, norb, nelec) + + newintt = numpy.tensordot(coeffs.conj(), h1eff, axes=([1], [0])) + newint1 = numpy.tensordot(newintt, coeffs, axes=([1], [1])) + newint1r = numpy.zeros(shape=(norb, norb), order='C') + for i in range(norb): + for j in range(norb): + newint1r[i, j] = newint1[i, j].real + int2 = pyscf.ao2mo.restore(1, eri_cas, norb) + eri_cas = numpy.zeros_like(int2) + + transformDinfh(norb, numpy.ascontiguousarray(nRows, numpy.int32), + numpy.ascontiguousarray(rowIndex, numpy.int32), + numpy.ascontiguousarray(rowCoeffs, numpy.float64), + numpy.ascontiguousarray(int2, numpy.float64), + numpy.ascontiguousarray(eri_cas, numpy.float64)) + + writeIntNoSymm(norb, numpy.ascontiguousarray(newint1r, numpy.float64), + numpy.ascontiguousarray(eri_cas, numpy.float64), + ecore, neleca + nelecb, + numpy.asarray(orbsym, dtype=numpy.int32)) + + else: + if VMC.groupname is not None and VMC.orbsym is not []: + orbsym = dmrg_sym.convert_orbsym(VMC.groupname, VMC.orbsym) + else: + orbsym = [1] * norb + + eri_cas = pyscf.ao2mo.restore(8, eri_cas, norb) + # Writes the FCIDUMP file using functions in VMC_tools.cpp. + integralFile = integralFile.encode() # .encode for python3 compatibility + fcidumpFromIntegral(integralFile, h1eff, eri_cas, norb, + neleca + nelecb, ecore, + numpy.asarray(orbsym, dtype=numpy.int32), + abs(neleca - nelecb)) + + +def executeVMC(VMC): + inFile = os.path.join(VMC.runtimeDir, VMC.configFile) + outFile = os.path.join(VMC.runtimeDir, VMC.outputFile) + try: + cmd = ' '.join((VMC.mpiprefix, VMC.executable, inFile)) + cmd = "%s > %s 2>&1" % (cmd, outFile) + check_call(cmd, shell=True) + #save_output(VMC) + except CalledProcessError as err: + logger.error(VMC, cmd) + raise err + + inFile = os.path.join(VMC.runtimeDir, VMC.rdmconfigFile) + outFile = os.path.join(VMC.runtimeDir, VMC.rdmoutputFile) + try: + cmd = ' '.join((VMC.mpiprefix, VMC.executable, inFile)) + cmd = "%s > %s 2>&1" % (cmd, outFile) + check_call(cmd, shell=True) + #save_output(VMC) + except CalledProcessError as err: + logger.error(VMC, cmd) + raise err + + +#def save_output(VMC): +# for i in range(50): +# if os.path.exists(os.path.join(VMC.runtimeDir, "output%02d.dat"%(i))): +# continue +# else: +# import shutil +# shutil.copy2(os.path.join(VMC.runtimeDir, "output.dat"),os.path.join(VMC.runtimeDir, "output%02d.dat"%(i))) +# shutil.copy2(os.path.join(VMC.runtimeDir, "%s/vmc.e"%(VMC.scratchDirectory)), os.path.join(VMC.runtimeDir, "vmc%02d.e"%(i))) +# #print('BM copied into "output%02d.dat"'%(i)) +# #print('BM copied into "vmc%02d.e"'%(i)) +# break + + +def localizeValence(mf, mo_coeff, method="iao"): + if (method == "iao"): + return iao.iao(mf.mol, mo_coeff) + elif (method == "ibo"): + return ibo.PM(mf.mol, mo_coeff).kernel() + + #a = iao.iao(mf.mol, mo_coeff) + #a = lo.vec_lowdin(a, mf.get_ovlp()) + #return ibo.ibo(mf.mol, mo_coeff, iaos=a) + elif (method == "lowdin"): + return fractional_matrix_power(mf.get_ovlp(mf.mol), -0.5).T + elif (method == "pipek"): + return pipek.PM(mf.mol).kernel(mo_coeff) + elif (method == "boys"): + return boys.Boys(mf.mol).kernel(mo_coeff) + elif (method == "er"): + return edmiston.ER(mf.mol).kernel(mo_coeff) + + + +# can be used for all electron, but not recommended +def bestDetValence(mol, lmo, occ, eri, writeToFile=True): + + # index of the ao contributing the most to an lmo + maxLMOContributers = [ numpy.argmax(numpy.abs(lmo[::,i])) for i in range(lmo.shape[1]) ] + + # end AO index for each atom in ascending order + atomNumAOs = [ i[1][3] - 1 for i in enumerate(mol.aoslice_nr_by_atom()) ] + + lmoSites = [ [] for i in range(mol.natm) ] #lmo's cetered on each atom + for i in enumerate(maxLMOContributers): + lmoSites[numpy.searchsorted(numpy.array(atomNumAOs), i[1])].append(i[0]) + + bestDet = [0 for i in range(lmo.shape[1])] + def pair(i): + return i*(i+1)//2+i + for i in enumerate(occ): + if eri.ndim == 2: + onSiteIntegrals = [ (j, eri[pair(j),pair(j)]) for (n,j) in enumerate(lmoSites[i[0]]) ] + elif eri.ndim == 1: + onSiteIntegrals = [ (j, eri[pair(pair(j))]) for (n,j) in enumerate(lmoSites[i[0]]) ] + onSiteIntegrals.sort(key = lambda tup : tup[1], reverse=True) + for k in range(i[1][0]): + bestDet[onSiteIntegrals[k][0]] = '2' + for l in range(i[1][1]): + bestDet[onSiteIntegrals[i[1][0] + l][0]] = 'a' + for m in range(i[1][2]): + bestDet[onSiteIntegrals[i[1][0] + i[1][1] + m][0]] = 'b' + + bestDetStr = ' '.join(bestDet) + print('bestDet: ' + bestDetStr) + if writeToFile: + fileh = open("bestDet", 'w') + fileh.write('1. ' + bestDetStr + '\n') + fileh.close() + + return bestDetStr + +def getGradient(t, E1, V, E2): + nact = E1.shape[0] + gradient =-1. * lib.einsum('Pa,Qa->PQ', t, E1) + gradient +=1. * lib.einsum('Qa,Pa->PQ', t, E1) + gradient +=-1. * lib.einsum('aP,aQ->PQ', t, E1) + gradient +=1. * lib.einsum('aQ,aP->PQ', t, E1) + gradient +=-0.5 * lib.einsum('Pabc,Qabc->PQ', V, E2) + gradient +=-0.5 * lib.einsum('Pabc,abcQ->PQ', V, E2) + gradient +=0.5 * lib.einsum('Qabc,Pabc->PQ', V, E2) + gradient +=0.5 * lib.einsum('Qabc,abcP->PQ', V, E2) + gradient +=-0.5 * lib.einsum('aPbc,Qacb->PQ', V, E2) + gradient +=-0.5 * lib.einsum('aPbc,acbQ->PQ', V, E2) + gradient +=0.5 * lib.einsum('aQbc,Pacb->PQ', V, E2) + gradient +=0.5 * lib.einsum('aQbc,acbP->PQ', V, E2) + ''' + gradient = ( -1.00000) *lib.einsum('Pa,Qa->PQ', h1eff, E1) + gradient += ( 1.00000) *lib.einsum('Qa,Pa->PQ', h1eff, E1) + gradient += ( -1.00000) *lib.einsum('aP,aQ->PQ', h1eff, E1) + gradient += ( 1.00000) *lib.einsum('aQ,aP->PQ', h1eff, E1) + gradient += ( -1.00000) *lib.einsum('Pabc,Qabc->PQ', eri, E2) + gradient += ( -1.00000) *lib.einsum('Pabc,abcQ->PQ', eri, E2) + gradient += ( 1.00000) *lib.einsum('Qabc,Pabc->PQ', eri, E2) + gradient += ( 1.00000) *lib.einsum('Qabc,abcP->PQ', eri, E2) + gradient += ( -1.00000) *lib.einsum('aPbc,Qacb->PQ', eri, E2) + gradient += ( -1.00000) *lib.einsum('aPbc,acbQ->PQ', eri, E2) + gradient += ( 1.00000) *lib.einsum('aQbc,Pacb->PQ', eri, E2) + gradient += ( 1.00000) *lib.einsum('aQbc,acbP->PQ', eri, E2) + ''' + return gradient + +def lowerTriangle(gradient): + nact = gradient.shape[0] + Grad = numpy.zeros(((nact*(nact-1))//2,)) + index = 0 + for p in range(nact): + for q in range(p): + Grad[index] = gradient[p, q] + index+=1 + return Grad + +def fullTriangle(gradient, nact): + Grad = numpy.zeros((nact,nact)) + index = 0 + for p in range(nact): + for q in range(p): + Grad[p,q] = gradient[index] + Grad[q,p] = -gradient[index] + index+=1 + return Grad + +def getHessian(h1eff, E1, eri, E2): + nact = E1.shape[0] + norb = nact + Hessian = 1. * lib.einsum('PR,QS->PQRS', h1eff, E1) + Hessian +=-1. * lib.einsum('PS,QR->PQRS', h1eff, E1) + Hessian +=-1. * lib.einsum('QR,PS->PQRS', h1eff, E1) + Hessian +=1. * lib.einsum('QS,PR->PQRS', h1eff, E1) + Hessian +=1. * lib.einsum('RP,SQ->PQRS', h1eff, E1) + Hessian +=-1. * lib.einsum('RQ,SP->PQRS', h1eff, E1) + Hessian +=-1. * lib.einsum('SP,RQ->PQRS', h1eff, E1) + Hessian +=1. * lib.einsum('SQ,RP->PQRS', h1eff, E1) + Hessian +=-1. * lib.einsum('PR,Qa,Sa->PQRS', numpy.eye(norb), h1eff, E1) + Hessian +=-1. * lib.einsum('PR,aQ,aS->PQRS', numpy.eye(norb), h1eff, E1) + Hessian +=1. * lib.einsum('PS,Qa,Ra->PQRS', numpy.eye(norb), h1eff, E1) + Hessian +=1. * lib.einsum('PS,aQ,aR->PQRS', numpy.eye(norb), h1eff, E1) + Hessian +=1. * lib.einsum('QR,Pa,Sa->PQRS', numpy.eye(norb), h1eff, E1) + Hessian +=1. * lib.einsum('QR,aP,aS->PQRS', numpy.eye(norb), h1eff, E1) + Hessian +=-1. * lib.einsum('QS,Pa,Ra->PQRS', numpy.eye(norb), h1eff, E1) + Hessian +=-1. * lib.einsum('QS,aP,aR->PQRS', numpy.eye(norb), h1eff, E1) + Hessian +=0.5 * lib.einsum('PRab,QSab->PQRS', eri, E2) + Hessian +=0.5 * lib.einsum('PRab,QbaS->PQRS', eri, E2) + Hessian +=0.5 * lib.einsum('PRab,SabQ->PQRS', eri, E2) + Hessian +=0.5 * lib.einsum('PRab,abQS->PQRS', eri, E2) + Hessian +=-0.5 * lib.einsum('PSab,QRab->PQRS', eri, E2) + Hessian +=-0.5 * lib.einsum('PSab,QbaR->PQRS', eri, E2) + Hessian +=-0.5 * lib.einsum('PSab,RabQ->PQRS', eri, E2) + Hessian +=-0.5 * lib.einsum('PSab,abQR->PQRS', eri, E2) + Hessian +=0.5 * lib.einsum('PaRb,QaSb->PQRS', eri, E2) + Hessian +=0.5 * lib.einsum('PaRb,SaQb->PQRS', eri, E2) + Hessian +=-0.5 * lib.einsum('PaSb,QaRb->PQRS', eri, E2) + Hessian +=-0.5 * lib.einsum('PaSb,RaQb->PQRS', eri, E2) + Hessian +=-0.5 * lib.einsum('QRab,PSab->PQRS', eri, E2) + Hessian +=-0.5 * lib.einsum('QRab,PbaS->PQRS', eri, E2) + Hessian +=-0.5 * lib.einsum('QRab,SabP->PQRS', eri, E2) + Hessian +=-0.5 * lib.einsum('QRab,abPS->PQRS', eri, E2) + Hessian +=0.5 * lib.einsum('QSab,PRab->PQRS', eri, E2) + Hessian +=0.5 * lib.einsum('QSab,PbaR->PQRS', eri, E2) + Hessian +=0.5 * lib.einsum('QSab,RabP->PQRS', eri, E2) + Hessian +=0.5 * lib.einsum('QSab,abPR->PQRS', eri, E2) + Hessian +=-0.5 * lib.einsum('QaRb,PaSb->PQRS', eri, E2) + Hessian +=-0.5 * lib.einsum('QaRb,SaPb->PQRS', eri, E2) + Hessian +=0.5 * lib.einsum('QaSb,PaRb->PQRS', eri, E2) + Hessian +=0.5 * lib.einsum('QaSb,RaPb->PQRS', eri, E2) + Hessian +=0.5 * lib.einsum('RPab,QSba->PQRS', eri, E2) + Hessian +=0.5 * lib.einsum('RPab,QabS->PQRS', eri, E2) + Hessian +=0.5 * lib.einsum('RPab,SbaQ->PQRS', eri, E2) + Hessian +=0.5 * lib.einsum('RPab,abSQ->PQRS', eri, E2) + Hessian +=-0.5 * lib.einsum('RQab,PSba->PQRS', eri, E2) + Hessian +=-0.5 * lib.einsum('RQab,PabS->PQRS', eri, E2) + Hessian +=-0.5 * lib.einsum('RQab,SbaP->PQRS', eri, E2) + Hessian +=-0.5 * lib.einsum('RQab,abSP->PQRS', eri, E2) + Hessian +=-0.5 * lib.einsum('SPab,QRba->PQRS', eri, E2) + Hessian +=-0.5 * lib.einsum('SPab,QabR->PQRS', eri, E2) + Hessian +=-0.5 * lib.einsum('SPab,RbaQ->PQRS', eri, E2) + Hessian +=-0.5 * lib.einsum('SPab,abRQ->PQRS', eri, E2) + Hessian +=0.5 * lib.einsum('SQab,PRba->PQRS', eri, E2) + Hessian +=0.5 * lib.einsum('SQab,PabR->PQRS', eri, E2) + Hessian +=0.5 * lib.einsum('SQab,RbaP->PQRS', eri, E2) + Hessian +=0.5 * lib.einsum('SQab,abRP->PQRS', eri, E2) + Hessian +=0.5 * lib.einsum('aPbR,QaSb->PQRS', eri, E2) + Hessian +=0.5 * lib.einsum('aPbR,SaQb->PQRS', eri, E2) + Hessian +=-0.5 * lib.einsum('aPbS,QaRb->PQRS', eri, E2) + Hessian +=-0.5 * lib.einsum('aPbS,RaQb->PQRS', eri, E2) + Hessian +=-0.5 * lib.einsum('aQbR,PaSb->PQRS', eri, E2) + Hessian +=-0.5 * lib.einsum('aQbR,SaPb->PQRS', eri, E2) + Hessian +=0.5 * lib.einsum('aQbS,PaRb->PQRS', eri, E2) + Hessian +=0.5 * lib.einsum('aQbS,RaPb->PQRS', eri, E2) + Hessian +=0.5 * lib.einsum('Pabc,QR,Sabc->PQRS', eri, numpy.eye(norb), E2) + Hessian +=0.5 * lib.einsum('Pabc,QR,abcS->PQRS', eri, numpy.eye(norb), E2) + Hessian +=-0.5 * lib.einsum('Pabc,QS,Rabc->PQRS', eri, numpy.eye(norb), E2) + Hessian +=-0.5 * lib.einsum('Pabc,QS,abcR->PQRS', eri, numpy.eye(norb), E2) + Hessian +=-0.5 * lib.einsum('Qabc,PR,Sabc->PQRS', eri, numpy.eye(norb), E2) + Hessian +=-0.5 * lib.einsum('Qabc,PR,abcS->PQRS', eri, numpy.eye(norb), E2) + Hessian +=0.5 * lib.einsum('Qabc,PS,Rabc->PQRS', eri, numpy.eye(norb), E2) + Hessian +=0.5 * lib.einsum('Qabc,PS,abcR->PQRS', eri, numpy.eye(norb), E2) + Hessian +=0.5 * lib.einsum('aPbc,QR,Sacb->PQRS', eri, numpy.eye(norb), E2) + Hessian +=0.5 * lib.einsum('aPbc,QR,acbS->PQRS', eri, numpy.eye(norb), E2) + Hessian +=-0.5 * lib.einsum('aPbc,QS,Racb->PQRS', eri, numpy.eye(norb), E2) + Hessian +=-0.5 * lib.einsum('aPbc,QS,acbR->PQRS', eri, numpy.eye(norb), E2) + Hessian +=-0.5 * lib.einsum('aQbc,PR,Sacb->PQRS', eri, numpy.eye(norb), E2) + Hessian +=-0.5 * lib.einsum('aQbc,PR,acbS->PQRS', eri, numpy.eye(norb), E2) + Hessian +=0.5 * lib.einsum('aQbc,PS,Racb->PQRS', eri, numpy.eye(norb), E2) + Hessian +=0.5 * lib.einsum('aQbc,PS,acbR->PQRS', eri, numpy.eye(norb), E2) + + Hess = 0.5*(Hessian + lib.einsum('pqrs->rspq', Hessian)) + + npair = (nact*(nact-1))//2 + Hessian = numpy.zeros( (npair,npair)) + for p in range(nact): + for q in range(p): + for r in range(nact): + for s in range(r): + Hessian[ (p*(p-1))//2 + q , (r*(r-1))//2+s] = Hess[p,q,r,s] + return Hessian + +def writeMat(mat, fileName, isComplex): + fileh = open(fileName, 'w') + for i in range(mat.shape[0]): + for j in range(mat.shape[1]): + if (isComplex): + fileh.write('(%16.10e, %16.10e) '%(mat[i,j].real, mat[i,j].imag)) + else: + fileh.write('%16.10e '%(mat[i,j])) + fileh.write('\n') + fileh.close() + +def randomHermitian(nact): + b = numpy.zeros((nact, nact), dtype=complex) + for i in range(nact): + for j in range(nact): + b[i,j] = numpy.random.random() + numpy.random.random()*1j + antiHermi = (b - b.T.conjugate())/2 + return expm(0.01*antiHermi) + +def VMCSCF(mf, ncore, nact, occ=None, frozen = None, loc="iao", proc=None, + stochasticIter = 1000, maxIter = 100, *args, **kwargs): + + mol = mf.mol + mo = mf.mo_coeff + nelec = mol.nelectron - 2 * ncore + mc = mcscf.CASSCF(mf, nact, nelec) + + if loc is not None: + lmo = localizeValence(mf, mo[:, ncore:ncore+nact], loc) + tools.molden.from_mo(mf.mol, 'valenceOrbs.molden', lmo) + + #performing unitary transformation to get rid of symmetries + b = numpy.random.rand(nact,nact) + antiHermi = (b - b.T)/2 + U = expm(0.01*antiHermi) + lmo = lib.einsum('mi, ia->ma', lmo, U) + + h1cas, energy_core = mcscf.casci.h1e_for_cas(mc, mf.mo_coeff, nact, ncore) + mo_core = mc.mo_coeff[:,:ncore] + core_dm = 2 * mo_core.dot(mo_core.T) + corevhf = mc.get_veff(mol, core_dm) + h1eff = lmo.T.dot(mc.get_hcore() + corevhf).dot(lmo) + eri = ao2mo.kernel(mol, lmo) + else: + lmo = numpy.eye(nact) + + b = numpy.random.rand(nact,nact) + antiHermi = (b - b.T)/2 + U = expm(0.01*antiHermi) + lmo = lib.einsum('mi, ia->ma', lmo, U) + + h1eff = mf.get_hcore() + corevhf = 0.*h1eff + eri = mf._eri + energy_core = 0 + if occ is not None: + bestDetValence(mol, lmo, occ, eri, True) + + + #prepare initial guess for the HF orbitals + norb = nact + molA = gto.M() + molA.nelectron = nelec + molA.verbose = 4 + molA.incore_anyway = True + gmf = scf.GHF(molA) + gmf.get_hcore = lambda *args: scipy.linalg.block_diag(h1eff, h1eff) + gmf.get_ovlp = lambda *args: numpy.identity(2*norb) + gmf.energy_nuc = lambda *args: energy_core + gmf._eri = eri + + dm = gmf.get_init_guess() + dm = dm + 2 * numpy.random.rand(2*norb, 2*norb) + gmf.level_shift = 0.1 + gmf.max_cycle = 500 + print(gmf.kernel(dm0 = dm)) + mocoeff = numpy.zeros((2*norb, 2*norb), dtype=complex) + mocoeff = (1.+0j)*gmf.mo_coeff + + #performing unitary transformation to get rid of symmetries + U = randomHermitian(nact) + mocoeff[:,:nact] = numpy.dot(mocoeff[:,:nact], U) + + writeMat(mocoeff, "hf.txt", True) + + + #now start running the orbital optimization + mc.fcisolver = VMC(mf.mol) + mc.fcisolver.stochasticIter = stochasticIter + mc.fcisolver.maxIter = maxIter + + maxMicro, maxMacro = 100, 5 + for i in range(maxMacro): + print ("\nMacroIter: %4d"%i) + #execute VMC + writeIntegralFile(mc.fcisolver, h1eff, eri, nact, nelec, energy_core) + mc.fcisolver.writeConfig(restart= i is not 0, readBestDeterminant= (occ is not None and i is 0)) + + mc.fcisolver.mpiprefix = "mpirun" + if (proc is not None): + mc.fcisolver.mpiprefix = ("mpirun -np %d" %(proc)) + executeVMC(mc.fcisolver) + + #read RDM + E1, E2 = mc.fcisolver.make_rdm12(0, nact, nelec) + E2 = lib.einsum('abcd->acbd', E2) + + #optimize the orbitals and generate new integrals + for j in range(maxMicro): + eri = ao2mo.restore(1, eri, nact) + eri.shape= (nact, nact, nact, nact) + eri = lib.einsum('abcd->acbd', eri) + + gradient = getGradient(h1eff, E1, eri, E2) + Grad = -1.*lowerTriangle(gradient) + + print ("MicorIter: %4d %18.10f %18.10f"% (j, lib.einsum('ij,ij',h1eff, E1) + 0.5*lib.einsum('ijkl,ijkl', eri, E2)+energy_core, numpy.linalg.norm(Grad)), flush=True) + + + Hess = getHessian(h1eff, E1, eri, E2) + Hess += 1.e-2 * numpy.eye(Hess.shape[0]) + step = numpy.linalg.solve(Hess, Grad) + K = fullTriangle(step, nact) + U = expm(-K) + + lmo = lib.einsum('mi, ia->ma', lmo, U) + h1eff = lmo.T.dot(mc.get_hcore() + corevhf).dot(lmo) + + if (loc is not None) : + eri = ao2mo.kernel(mol, lmo) + else: #Hubbard model + eri = ao2mo.incore.full(eri, lmo) + if (numpy.linalg.norm(Grad) < 1.e-4): + break + + + + exit(0) + mc.fcisolver.maxIter = 100 + mf.mo_coeff[:,ncore:ncore+nact] = lmo + mc.mo_coeff = 1.*mf.mo_coeff + mc.internal_rotation = True + + if frozen is not None: + mc.frozen = frozen + + if mc.chkfile == mc._scf._chkfile.name: + # Do not delete chkfile after mcscf + mc.chkfile = tempfile.mktemp(dir=settings.VMCSCRATCHDIR) + if not os.path.exists(settings.VMCSCRATCHDIR): + os.makedirs(settings.VMCSCRATCHDIR) + return mc + + +if __name__ == '__main__': + from pyscf import gto, scf, mcscf, dmrgscf + from pyscf.vmcscf import vmc + + # Initialize benzene molecule + atomstring = ''' + C 0.000517 0.000000 0.000299 + C 0.000517 0.000000 1.394692 + C 1.208097 0.000000 2.091889 + C 2.415677 0.000000 1.394692 + C 2.415677 0.000000 0.000299 + C 1.208097 0.000000 -0.696898 + H -0.939430 0.000000 -0.542380 + H -0.939430 0.000000 1.937371 + H 1.208097 0.000000 3.177246 + H 3.355625 0.000000 1.937371 + H 3.355625 0.000000 -0.542380 + H 1.208097 0.000000 -1.782255 + ''' + mol = gto.M( + atom = atomstring, + unit = 'angstrom', + basis = 'sto-6g', + verbose = 4, + symmetry= 0, + spin = 0) + + # Create HF molecule + mf = scf.RHF(mol) + mf.conv_tol = 1e-9 + mf.scf() + + + ncore, nact = 6, 30 + occ = [] + configC = [[0,4,0], [0,0,4]] # no double occ, 4 up or 4 dn + configH = [[0,1,0], [0,0,1]] # no double occ, 1 up or 1 dn + for i in range(6): + occ.append(configC[i%2]) + for i in range(6): + occ.append(configH[(i+1)%2]) + + + frozen = list(range(0,ncore)) + list (range(36, mf.mo_coeff.shape[0])) + print (frozen) + # Create VMC molecule for just variational opt. + # Active spaces chosen to reflect valence active space. + mch = vmc.VMCSCF(mf, ncore, nact, occ=occ, loc="ibo",\ + frozen = frozen) + mch.fcisolver.stochasticIter = 400 + mch.fcisolver.maxIter = 50 + e_noPT = mch.mc2step()[0] + diff --git a/pyscf/x2c/sfx2c1e.py b/pyscf/x2c/sfx2c1e.py index 0dae9b0173..b84c2f7bf8 100644 --- a/pyscf/x2c/sfx2c1e.py +++ b/pyscf/x2c/sfx2c1e.py @@ -228,6 +228,27 @@ def picture_change(self, even_operator=(None, None), odd_operator=None): else: return lib.einsum('pi,xpq,qj->xij', c, pc_mat, c) + #by default h is returned in the contracted basis + #x and r in the uncontracted basis + def get_hxr(self, mol=None, uncontract = True): + if mol is None: mol = self.mol + if (uncontract): + xmol, contr_coeff = self.get_xmol(mol) + else: + xmol, contr_coeff = mol, None + + c = lib.param.LIGHT_SPEED + assert('1E' in self.approx.upper()) + t = xmol.intor_symmetric('int1e_kin') + v = xmol.intor_symmetric('int1e_nuc') + s = xmol.intor_symmetric('int1e_ovlp') + w = xmol.intor_symmetric('int1e_pnucp') + + h1, x, r = x2c._x2c1e_hxrmat(t, v, w, s, c) + if (uncontract): + h1 = reduce(numpy.dot, (contr_coeff.T, h1, contr_coeff)) + return h1, x, r + def get_xmat(self, mol=None): if mol is None: xmol = self.get_xmol(mol)[0] From e2712323d76702c49cc65edc55ce954678dac3e2 Mon Sep 17 00:00:00 2001 From: xubwa Date: Wed, 4 Mar 2020 15:56:03 -0700 Subject: [PATCH 09/26] write integrals for spinor orbitals, both x2c wfn and dhf wfn supported --- pyscf/shciscf/fcidump_rel.py | 106 +++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 pyscf/shciscf/fcidump_rel.py diff --git a/pyscf/shciscf/fcidump_rel.py b/pyscf/shciscf/fcidump_rel.py new file mode 100644 index 0000000000..d934a90005 --- /dev/null +++ b/pyscf/shciscf/fcidump_rel.py @@ -0,0 +1,106 @@ +import pyscf, h5py, numpy +from pyscf import scf, gto, ao2mo, tools +from functools import reduce + +TOL=1e-16 +DEFAULT_FLOAT_FORMAT='(%16.12e, %16.12e)' + +def write_hcore(fout, h, nmo, tol=TOL, float_format=DEFAULT_FLOAT_FORMAT): + h = h.reshape(nmo,nmo) + output_format = float_format + ' %4d %4d 0 0\n' + for i in range(nmo): + for j in range(0, i+1): + if abs(h[i,j]) > tol: + fout.write(output_format % (h[i,j].real, h[i,j].imag, i+1, j+1)) + +def write_eri(fout, eri, nmo, tol=TOL, float_format=DEFAULT_FLOAT_FORMAT): + npair = nmo*(nmo+1)//2 + output_format = float_format + ' %4d %4d %4d %4d\n' + for i in range(nmo): + for j in range(0, nmo): + ij = i*nmo+j + for k in range(0, nmo): + for l in range(0, nmo): + kl = k*nmo+l + if ij >= kl: + if abs(eri[ij][kl]) > tol: + fout.write(output_format % (eri[ij][kl].real, eri[ij][kl].imag, i+1, j+1, k+1, l+1)) +def from_integrals(filename, h1e, h2e, nmo, nelec, nuc=0, ms=0, orbsym=None, + tol=TOL, float_format=DEFAULT_FLOAT_FORMAT): + '''Convert the given 1-electron and 2-electron integrals to FCIDUMP format''' + with open(filename, 'w') as fout: + write_head(fout, nmo, nelec, ms, orbsym) + write_eri(fout, h2e, nmo, tol=tol, float_format=float_format) + write_hcore(fout, h1e, nmo, tol=tol, float_format=float_format) + output_format = float_format + ' 0 0 0 0\n' + fout.write(output_format % (nuc, 0.0)) + +def view(h5file, dataname='eri_mo'): + with h5py.File(h5file, 'r') as f5: + print('dataset %s, shape %s' % (str(f5.keys()), str(f5[dataname].shape))) + +def write_head(fout, nmo, nelec, ms, orbsym=None): + if not isinstance(nelec, (int, numpy.number)): + ms = abs(nelec[0] - nelec[1]) + nelec = nelec[0] + nelec[1] + fout.write(' &FCI NORB=%4d,NELEC=%2d,MS2=%d,\n' % (nmo, nelec, ms)) + if orbsym is not None and len(orbsym) > 0: + fout.write(' ORBSYM=%s\n' % ','.join([str(x) for x in orbsym])) + else: + fout.write(' ORBSYM=%s\n' % ('1,' * nmo)) + fout.write(' ISYM=0,\n') + fout.write(' &END\n') + +def from_x2c(mf, ncore, nact, filename = 'FCIDUMP', tol=1e-10, intor='int2e_spinor'): + ncore = ncore*2 + nact = nact*2 + mo_coeff = mf.mo_coeff[:,ncore:ncore+nact] + + assert mo_coeff.dtype == numpy.complex + + h1e = reduce(numpy.dot, (mf.mo_coeff.T, mf.get_hcore(), mf.mo_coeff))[ncore:ncore+nact, ncore:ncore+nact] + if mf._eri is None: + eri = ao2mo.kernel(mf.mol, mo_coeff, intor=intor) + else: + eri = ao2mo.kernel(mf._eri, mo_coeff, intor=intor) + nuc = mf.energy_nuc() + from_integrals(filename = filename, h1e=h1e, h2e=eri, nmo=nact, nelec=sum(mol.nelec)-ncore, nuc=nuc) + +def from_dhf(mf, ncore, nact, filename = 'FCIDUMP', tol=1e-10, intor='int2e_spinor'): + ncore = ncore*2 + nact = nact*2 + n4c, nmo = mf.mo_coeff.shape + n2c = n4c // 2 + nNeg = nmo // 2 + ncore += nNeg + mo_coeff = mf.mo_coeff[n2c:,ncore:ncore+nact] + + assert mo_coeff.dtype == numpy.complex + + h1e = reduce(numpy.dot, (mf.mo_coeff.T, mf.get_hcore(), mf.mo_coeff))[ncore:ncore+nact, ncore:ncore+nact] + if mf._eri is None: + eri = ao2mo.kernel(mf.mol, mo_coeff, intor=intor) + else: + eri = ao2mo.kernel(mf._eri, mo_coeff, intor=intor) + nuc = mf.energy_nuc() + from_integrals(filename = filename, h1e=h1e, h2e=eri, nmo=nact, nelec=sum(mol.nelec)-ncore, nuc=nuc) + return + +if __name__ == '__main__': + mol = gto.M( + atom = + ''' + O 0. 0. 0. + H 0. -0.757 0.587 + H 0. 0.757 0.587 + ''', + basis = 'sto3g', + verbose = 3, + spin = 0) + mf_x2c = scf.X2C(mol) + mf_x2c.kernel() + from_x2c(mf_x2c, 0, 7, filename = 'FCIDUMP_x2c') + + mf_dirac = scf.DHF(mol) + mf_dirac.kernel() + from_dhf(mf_dirac, 0, 7, filename = 'FCIDUMP_dhf') \ No newline at end of file From 1a482e9d66f75d6110f883fbebd3b09c5bf1b762 Mon Sep 17 00:00:00 2001 From: xubwa Date: Thu, 5 Mar 2020 10:42:08 -0700 Subject: [PATCH 10/26] change print to log in socutils, change 2c-x2c into ro kind of fashion --- pyscf/shciscf/shci.py | 3 ++- pyscf/shciscf/socutils.py | 10 +++++----- pyscf/x2c/x2c.py | 7 ++++++- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/pyscf/shciscf/shci.py b/pyscf/shciscf/shci.py index b5aaf79d52..fc68a1e5a4 100644 --- a/pyscf/shciscf/shci.py +++ b/pyscf/shciscf/shci.py @@ -1020,7 +1020,8 @@ def writeSHCIConfFile(SHCI, nelec, Restart): # Miscellaneous Keywords f.write('#misc\n') - f.write('noio \n') + if Restart is True: + f.write('noio \n') if (SHCI.scratchDirectory != ""): if not os.path.exists(SHCI.scratchDirectory): os.makedirs(SHCI.scratchDirectory) diff --git a/pyscf/shciscf/socutils.py b/pyscf/shciscf/socutils.py index 086bf7054e..6401937e73 100644 --- a/pyscf/shciscf/socutils.py +++ b/pyscf/shciscf/socutils.py @@ -8,7 +8,7 @@ from pyscf.lib.parameters import LIGHT_SPEED from pyscf.shciscf import shci from pyscf.x2c import x2c - +from pyscf.lib import logger def print1Int(h1, name): xyz = ["X", "Y", "Z"] @@ -160,7 +160,6 @@ def get_fso2e_x2c(mol, x, rp, pLL, pLS, pSS): gsoLL = numpy.zeros((3, nb, nb)) gsoLS = numpy.zeros((3, nb, nb)) gsoSS = numpy.zeros((3, nb, nb)) - from pyscf.gto import moleintor nbas = mol.nbas max_double = mol.max_memory / 8.0 * 1.0e6 @@ -179,20 +178,20 @@ def get_fso2e_x2c(mol, x, rp, pLL, pLS, pSS): ao_loc.append(ao_loc_orig[-1]) shl_size.append(ao_loc[-1] - ao_loc[-2]) shl_slice.append(nbas) - print(ao_loc, shl_size, shl_slice) nbas = len(shl_size) for i in range(0, nbas): for j in range(0, nbas): for k in range(0, nbas): for l in range(0, nbas): + start = time.clock() ddint = mol.intor('int2e_ip1ip2_sph', comp=9, shls_slice=[shl_slice[i], shl_slice[i+1], shl_slice[j], shl_slice[j+1], shl_slice[k], shl_slice[k+1], shl_slice[l], shl_slice[l+1]]).reshape(3, 3, -1) kint = numpy.zeros(3 * shl_size[i] * shl_size[j] * shl_size[k] * shl_size[l]).reshape( 3, shl_size[i], shl_size[j], shl_size[k], shl_size[l]) kint[0] = (ddint[1, 2] - ddint[2, 1]).reshape(shl_size[i], shl_size[j], shl_size[k], shl_size[l]) kint[1] = (ddint[2, 0] - ddint[0, 2]).reshape(shl_size[i], shl_size[j], shl_size[k], shl_size[l]) kint[2] = (ddint[0, 1] - ddint[1, 0]).reshape(shl_size[i], shl_size[j], shl_size[k], shl_size[l]) - #print("Time elapsed for integral calculation:", end - start, i, j, k, l, nbas) + logger.info(mol, "Time elapsed for integral calculation: %g, %d slice of total %d slice.", time.clock() - start, i*nbas**3+j*nbas**2+k*nbas+l+1, nbas**4) gsoLL[:, ao_loc[j]:ao_loc[j+1], ao_loc[l]:ao_loc[l+1]] \ +=-2.0*lib.einsum('ilmkn,lk->imn', kint, \ @@ -211,12 +210,13 @@ def get_fso2e_x2c(mol, x, rp, pLL, pLS, pSS): gsoSS[:, ao_loc[i]:ao_loc[i+1], ao_loc[k]:ao_loc[k+1]] \ += 2.0*lib.einsum('imlnk,lk->imn', kint, \ pLL[ao_loc[j]:ao_loc[j+1], ao_loc[l]:ao_loc[l+1]]) - #print(" Time elapsed for einsum:", time.clock() - start) + logger.info(mol, 'Time elapsed for integral contraction: %g, %d slice of total %d slice', time.clock() - start, i*nbas**3+j*nbas**2+k*nbas+l+1, nbas**4) for comp in range(0, 3): fso2e[comp] = gsoLL[comp] + gsoLS[comp].dot(x) + x.T.dot(-gsoLS[comp].T) + x.T.dot(gsoSS[comp].dot(x)) fso2e[comp] = reduce(numpy.dot, (rp.T, fso2e[comp], rp)) + logger.info(mol, 'Two electron part of SOC integral is done') return fso2e diff --git a/pyscf/x2c/x2c.py b/pyscf/x2c/x2c.py index 759057f3d2..d8dd5685cf 100644 --- a/pyscf/x2c/x2c.py +++ b/pyscf/x2c/x2c.py @@ -252,7 +252,12 @@ def get_occ(self, mo_energy=None, mo_coeff=None): if mo_energy is None: mo_energy = self.mo_energy mol = self.mol mo_occ = numpy.zeros_like(mo_energy) - mo_occ[:mol.nelectron] = 1 + if mol.nelectron%2 is 0: + mo_occ[:mol.nelectron] = 1 + else: + mo_occ[:mol.nelectron-1]=1 + mo_occ[mol.nelectron-1]=0.5 + mo_occ[mol.nelectron]=0.5 if mol.nelectron < len(mo_energy): logger.info(self, 'nocc = %d HOMO = %.12g LUMO = %.12g', \ mol.nelectron, mo_energy[mol.nelectron-1], From e46ed72b309175d4d407f02062bad3f20c4bdd40 Mon Sep 17 00:00:00 2001 From: xubwa Date: Sat, 28 Mar 2020 18:29:12 -0600 Subject: [PATCH 11/26] "add aochf and socamf integrals" --- pyscf/scf/__init__.py | 8 ++- pyscf/scf/aochf.py | 115 +++++++++++++++++++++++++++++++++++ pyscf/scf/hf.py | 13 ++++ pyscf/shciscf/fcidump_rel.py | 1 + pyscf/shciscf/socutils.py | 57 ++++++++++++++++- pyscf/x2c/x2c.py | 43 +++++++++++-- 6 files changed, 228 insertions(+), 9 deletions(-) create mode 100644 pyscf/scf/aochf.py diff --git a/pyscf/scf/__init__.py b/pyscf/scf/__init__.py index b155869a01..1d933dee92 100644 --- a/pyscf/scf/__init__.py +++ b/pyscf/scf/__init__.py @@ -118,6 +118,7 @@ from pyscf.scf.uhf import spin_square from pyscf.scf.hf import get_init_guess from pyscf.scf.addons import * +from pyscf.scf import aochf def HF(mol, *args): @@ -185,10 +186,13 @@ def DHF(mol, *args): return dhf.UHF(mol, *args) -def X2C(mol, *args): +def X2C(mol, nopen=None, nact=None, *kwargs): '''X2C UHF (in testing)''' from pyscf.x2c import x2c - return x2c.UHF(mol, *args) + return x2c.UHF(mol, nopen=nopen, nact=nact, *kwargs) + +def AOCHF(mol, nopen=None, nact=None): + return aochf.AOCHF(mol, nopen=nopen, nact=nact) def sfx2c1e(mf): return mf.sfx2c1e() diff --git a/pyscf/scf/aochf.py b/pyscf/scf/aochf.py new file mode 100644 index 0000000000..e568026a33 --- /dev/null +++ b/pyscf/scf/aochf.py @@ -0,0 +1,115 @@ +''' +Average of configuration Hartree-Fock +as described in DIRAC by Timo Fleig +current plan is to use this to generate spherical symmetric reference atom. +''' + +from functools import reduce +import numpy +import pyscf.gto +from pyscf import lib +from pyscf.lib import logger +from pyscf.scf import hf, rohf, uhf +import pyscf.scf.chkfile +from pyscf import __config__ + +def get_occ(mf, mo_energy=None, mo_coeff=None, nclose=None, nact=None, nopen=None): + '''Label occupancies for each orbitals + + Kwargs: + mo_energy : 1D ndarray + Obital energies + + mo_coeff : 2D ndarray + Obital coefficients + + nclose : int + Number of core orbitals + + nact : int + Number of active electrons + + nopen : int + Number of active orbitals + ''' + if mo_energy is None: mo_energy = mf.mo_energy + if nclose is None : nclose = mf.nclose + if nact is None : nact = mf.nact + if nopen is None : nopen = mf.nopen + + if mo_energy is None: mo_energy = mf.mo_energy + e_idx_a = numpy.argsort(mo_energy[0]) + e_idx_b = numpy.argsort(mo_energy[1]) + e_sort_a = mo_energy[0][e_idx_a] + e_sort_b = mo_energy[1][e_idx_b] + nmo = mo_energy[0].size + n_a, n_b = nclose+nopen, nclose+nopen + mo_occ = numpy.zeros_like(mo_energy) + mo_occ[0,e_idx_a[:nclose]] = 1 + mo_occ[1,e_idx_b[:nclose]] = 1 + mo_occ[0,e_idx_a[nclose:nclose+nopen]] = nact/nopen/2. + mo_occ[1,e_idx_b[nclose:nclose+nopen]] = nact/nopen/2. + if mf.verbose >= logger.INFO and n_a < nmo and n_b > 0 and n_b < nmo: + if e_sort_a[n_a-1]+1e-3 > e_sort_a[n_a]: + logger.warn(mf, 'alpha nocc = %d HOMO %.15g >= LUMO %.15g', + n_a, e_sort_a[n_a-1], e_sort_a[n_a]) + else: + logger.info(mf, ' alpha nocc = %d HOMO = %.15g LUMO = %.15g', + n_a, e_sort_a[n_a-1], e_sort_a[n_a]) + + if e_sort_b[n_b-1]+1e-3 > e_sort_b[n_b]: + logger.warn(mf, 'beta nocc = %d HOMO %.15g >= LUMO %.15g', + n_b, e_sort_b[n_b-1], e_sort_b[n_b]) + else: + logger.info(mf, ' beta nocc = %d HOMO = %.15g LUMO = %.15g', + n_b, e_sort_b[n_b-1], e_sort_b[n_b]) + + if e_sort_a[n_a-1]+1e-3 > e_sort_b[n_b]: + logger.warn(mf, 'system HOMO %.15g >= system LUMO %.15g', + e_sort_b[n_a-1], e_sort_b[n_b]) + + numpy.set_printoptions(threshold=nmo) + logger.debug(mf, ' alpha mo_energy =\n%s', mo_energy[0]) + logger.debug(mf, ' beta mo_energy =\n%s', mo_energy[1]) + numpy.set_printoptions(threshold=1000) + + if mo_coeff is not None and mf.verbose >= logger.DEBUG: + ss, s = mf.spin_square((mo_coeff[0][:,mo_occ[0]>0], + mo_coeff[1][:,mo_occ[1]>0]), mf.get_ovlp()) + logger.debug(mf, 'multiplicity = %.8g 2S+1 = %.8g', ss, s) + return mo_occ + +def make_rdm1(mo_coeff, mo_occ, **kwargs): + '''One-particle densit matrix. mo_occ is a 1D array, with occupancy 1 or 2. + ''' + print(mo_occ) + mo_a = mo_coeff[:,mo_occ>0] + mo_b = mo_coeff[:,mo_occ==2] + occ_a = numpy.zeros(mo_occ.size) + occ_a[mo_occ==2] = 1 + occ_a = mo_occ-occ_a + print(occ_a) + dm_a = numpy.dot(mo_a*occ_a[:5],mo_a.conj().T) + #dm_a = reduce(numpy.dot, (mo_a, mo_a.conj().T)) + dm_b = numpy.dot(mo_b, mo_b.conj().T) + #print(dm_a-numpy.dot(mo_a, mo_a.conj().T)) + return numpy.array((dm_a, dm_b)) + +class AOCHF(uhf.UHF): + __doc__ = hf.SCF.__doc__ + + def __init__(self, mol, nact=None, nopen=None): + hf.SCF.__init__(self, mol) + if nact is not None : self.nact=nact + if nopen is not None : self.nopen=nopen + self.nclose=(mol.nelectron-self.nact)//2 + print(self.nact, self.nopen, self.nclose) + self.nelec=None + + get_occ = get_occ + #@lib.with_doc(make_rdm1.__doc__) + #def make_rdm1(self, mo_coeff=None, mo_occ=None, **kwargs): + # if mo_coeff is None: mo_coeff = self.mo_coeff + # if mo_occ is None: mo_occ = self.mo_occ + # return make_rdm1(mo_coeff, mo_occ, **kwargs) + diff --git a/pyscf/scf/hf.py b/pyscf/scf/hf.py index f7c559c2ec..5a78990867 100644 --- a/pyscf/scf/hf.py +++ b/pyscf/scf/hf.py @@ -35,6 +35,7 @@ from pyscf.scf import chkfile from pyscf.data import nist from pyscf import __config__ +from pyscf import x2c WITH_META_LOWDIN = getattr(__config__, 'scf_analyze_with_meta_lowdin', True) PRE_ORTH_METHOD = getattr(__config__, 'scf_analyze_pre_orth_method', 'ANO') @@ -163,6 +164,17 @@ def kernel(mf, conv_tol=1e-10, conv_tol_grad=None, fock = mf.get_fock(h1e, s1e, vhf, dm, cycle, mf_diis) mo_energy, mo_coeff = mf.eig(fock, s1e) mo_occ = mf.get_occ(mo_energy, mo_coeff) + if type(mf) is x2c.x2c.X2C_UHF and cycle > 5: + mo_tmp = numpy.zeros((mo_coeff.shape),dtype=complex) + mo_tmp[0::2,0::2]=numpy.sign(mo_coeff[0::2,0::2])*(abs(mo_coeff[0::2,0::2]) + abs(mo_coeff[1::2,1::2]))/2 + mo_tmp[1::2,1::2]=numpy.sign(mo_coeff[1::2,1::2])*(abs(mo_coeff[0::2,0::2]) + abs(mo_coeff[1::2,1::2]))/2 + mo_tmp[1::2,0::2]=numpy.sign(mo_coeff[1::2,0::2])*(abs(mo_coeff[1::2,0::2]) + abs(mo_coeff[0::2,1::2])) / 2 + mo_tmp[0::2,1::2]=numpy.sign(mo_coeff[0::2,1::2])*(abs(mo_coeff[0::2,1::2]) + abs(mo_coeff[1::2,0::2])) / 2 + #mo_coeff=mo_tmp + #print(numpy.isclose(mo_coeff, mo_tmp)) + for i,j in zip(mo_coeff, mo_tmp): + print(i) + print(j) dm = mf.make_rdm1(mo_coeff, mo_occ) # attach mo_coeff and mo_occ to dm to improve DFT get_veff efficiency dm = lib.tag_array(dm, mo_coeff=mo_coeff, mo_occ=mo_occ) @@ -199,6 +211,7 @@ def kernel(mf, conv_tol=1e-10, conv_tol_grad=None, #fock = mf.get_fock(h1e, s1e, vhf, dm) # = h1e + vhf mo_energy, mo_coeff = mf.eig(fock, s1e) mo_occ = mf.get_occ(mo_energy, mo_coeff) + dm, dm_last = mf.make_rdm1(mo_coeff, mo_occ), dm dm = lib.tag_array(dm, mo_coeff=mo_coeff, mo_occ=mo_occ) vhf = mf.get_veff(mol, dm, dm_last, vhf) diff --git a/pyscf/shciscf/fcidump_rel.py b/pyscf/shciscf/fcidump_rel.py index d934a90005..8f56c9dd0c 100644 --- a/pyscf/shciscf/fcidump_rel.py +++ b/pyscf/shciscf/fcidump_rel.py @@ -55,6 +55,7 @@ def from_x2c(mf, ncore, nact, filename = 'FCIDUMP', tol=1e-10, intor='int2e_spin ncore = ncore*2 nact = nact*2 mo_coeff = mf.mo_coeff[:,ncore:ncore+nact] + mol=mf.mol assert mo_coeff.dtype == numpy.complex diff --git a/pyscf/shciscf/socutils.py b/pyscf/shciscf/socutils.py index 7ee6d6d741..6d0b635adb 100644 --- a/pyscf/shciscf/socutils.py +++ b/pyscf/shciscf/socutils.py @@ -10,6 +10,61 @@ from pyscf.shciscf import shci from pyscf.x2c import x2c +alpha = 1./LIGHT_SPEED + +def get_socamf(atoms, basis): + hso1e_atoms = {} + factor = -alpha**2 * 0.25 + for atom in atoms: + from pyscf import gto + atm_id=gto.elements.charge(atom) + spin=atm_id%2 + mol_atom = gto.M(atom=[[atom,[0,0,0]]], basis=basis, spin=spin) + conf = gto.elements.CONFIGURATION[atm_id] + # generate configuration for spherical symmetry atom + if conf[0] % 2==0: + if conf[1]%6 == 0: + if conf[2]%10 == 0: + if conf[3]%14 == 0: + nopen = 0 + nact = 0 + else: + nopen = 7 + nact = conf[3]%14 + else: + nopen = 5 + nact = conf[2]%10 + else: + nopen = 3 + nact = conf[1]%6 + else: + nopen = 1 + nact = 1 + from pyscf import scf + mf_atom = scf.sfx2c1e(scf.AOCHF(mol_atom, nopen=nopen, nact=nact)) + xatm, contr_coeff = x2c.X2C(mol_atom).get_xmol() + np, nc = contr_coeff.shape + mf_atom.kernel() + h1e, x, rp = get_hxr(mf_atom, uncontract=True) + dm = mf_atom.make_rdm1() + dm = dm[0]+dm[1] + dm = reduce(numpy.dot, (contr_coeff, dm, contr_coeff.T)) + pLL, pLS, pSS = get_p(dm/2, x, rp) + h1ao_uncontract = numpy.zeros((3, np, np),dtype=complex) + h1ao = numpy.zeros((3, nc, nc),dtype=complex) + h1ao_uncontract += factor*get_fso2e_x2c(xatm, x, rp, pLL, pLS, pSS) + for ic in range(3): + h1ao[ic] = reduce( + numpy.dot, (contr_coeff.T, h1ao_uncontract[ic], contr_coeff)) + hso1e = numpy.zeros((nc*2, nc*2),dtype=complex) + hso1e[1::2,0::2]+=h1ao[0] + hso1e[0::2,1::2]+=h1ao[0] + hso1e[1::2,0::2]+=-1.j*h1ao[1] + hso1e[0::2,1::2]+= 1.j*h1ao[1] + hso1e[0::2,0::2]+=h1ao[2] + hso1e[1::2,1::2]-=h1ao[2] + hso1e_atoms[atom]=hso1e + return hso1e_atoms def print1Int(h1, name): xyz = ["X", "Y", "Z"] @@ -146,7 +201,7 @@ def get_fso2e_x2c_original(mol, x, rp, pLL, pLS, pSS): - 1.0 * lib.einsum('lmkn,lk->mn', kint, pLS) gsoSS = -2.0 * lib.einsum('mnkl,lk', kint, pLL) \ - 2.0 * lib.einsum('mnlk,lk', kint, pLL) \ - + 2.0 * lib.einsum('mlnk,lk', kint, pLL) + + 2.0 * lib.einsum('mlnk,lk', kint, pLL) fso2e[i_x] = gsoLL + gsoLS.dot(x) + x.T.dot(-gsoLS.T) \ + x.T.dot(gsoSS.dot(x)) fso2e[i_x] = reduce(numpy.dot, (rp.T, fso2e[i_x], rp)) diff --git a/pyscf/x2c/x2c.py b/pyscf/x2c/x2c.py index 759057f3d2..7a8f7dc1db 100644 --- a/pyscf/x2c/x2c.py +++ b/pyscf/x2c/x2c.py @@ -26,6 +26,7 @@ from pyscf.scf import dhf from pyscf.scf import _vhf from pyscf import __config__ +from pyscf.shciscf import socutils class X2C(lib.StreamObject): '''2-component X2c (including spin-free and spin-dependent terms) in @@ -109,7 +110,23 @@ def get_hcore(self, mol=None): contr_coeff[0::2,0::2] = contr_coeff_nr contr_coeff[1::2,1::2] = contr_coeff_nr h1 = reduce(numpy.dot, (contr_coeff.T.conj(), h1, contr_coeff)) - return h1 + print (self.approx.upper()) + hso1e = numpy.zeros((nc*2, nc*2), dtype=complex) + if 'SOCAMF' in self.approx.upper(): + # incorporate X2CAMF approximation. + # since 1e SOC terms are already included. + hso1e_atoms = socutils.get_socamf(list(set(xmol.elements)), xmol.basis) + atom_slices = self.mol.aoslice_2c_by_atom() + for ia in range(xmol.natm): + ish0, ish1, c0, c1 = atom_slices[ia] + hso1e[c0:c1,c0:c1] += hso1e_atoms[self.mol.elements[ia]] + #i,j,k,l=atom_slices[0] + #N0=hso1e[k:l,k:l] + #i,j,k,l=atom_slices[1] + #N1=hso1e[k:l,k:l] + #print(N0-N1) + #exit(0) + return h1+hso1e def get_xmat(self, mol=None): if mol is None: @@ -204,12 +221,17 @@ def get_init_guess(mol, key='minao'): class X2C_UHF(hf.SCF): - def __init__(self, mol): + def __init__(self, mol, nact=None, nopen=None): hf.SCF.__init__(self, mol) self.with_x2c = X2C(mol) #self.with_x2c.xuncontract = False self._keys = self._keys.union(['with_x2c']) - + if nact is not None : self.nact=nact + else : self.nact=0 + if nopen is not None : self.nopen=nopen + else : self.nopen=0 + self.nclose=(mol.nelectron-self.nact)//2 + def build(self, mol=None): if self.verbose >= logger.WARN: self.check_sanity() @@ -248,12 +270,21 @@ def get_ovlp(self, mol=None): if mol is None: mol = self.mol return mol.intor_symmetric('int1e_ovlp_spinor') - def get_occ(self, mo_energy=None, mo_coeff=None): + def get_occ(self, mo_energy=None, mo_coeff=None, nclose=None, nact=None, nopen=None): if mo_energy is None: mo_energy = self.mo_energy + if nclose is None : nclose = self.nclose + if nact is None : nact = self.nact + if nopen is None : nopen = self.nopen mol = self.mol + assert(mol.nelectron==nclose*2+nact) mo_occ = numpy.zeros_like(mo_energy) - mo_occ[:mol.nelectron] = 1 - if mol.nelectron < len(mo_energy): + if nopen is 0: + mo_occ[:mol.nelectron] = 1 + else: + mo_occ[:2*nclose]=1 + average_occ=nact/(nopen*2.) + mo_occ[2*nclose:2*(nclose+nopen)]=average_occ + if 2*(nopen+nclose) < len(mo_energy): logger.info(self, 'nocc = %d HOMO = %.12g LUMO = %.12g', \ mol.nelectron, mo_energy[mol.nelectron-1], mo_energy[mol.nelectron]) From 68051c6c25bc36e8d7d8903e538676da96283277 Mon Sep 17 00:00:00 2001 From: xubwa Date: Sun, 29 Mar 2020 19:51:32 -0600 Subject: [PATCH 12/26] "add approx option in generating x2c fcidump file" --- pyscf/scf/aochf.py | 4 +++- pyscf/shciscf/fcidump_rel.py | 8 +++++--- pyscf/shciscf/socutils.py | 32 +++++++++++++++++++++----------- pyscf/x2c/x2c.py | 6 ------ 4 files changed, 29 insertions(+), 21 deletions(-) diff --git a/pyscf/scf/aochf.py b/pyscf/scf/aochf.py index e568026a33..14e4547533 100644 --- a/pyscf/scf/aochf.py +++ b/pyscf/scf/aochf.py @@ -97,7 +97,9 @@ def make_rdm1(mo_coeff, mo_occ, **kwargs): class AOCHF(uhf.UHF): __doc__ = hf.SCF.__doc__ - + nclose = getattr(__config__, 'nclose', 0) + nact = getattr(__config__, 'nact', 0) + nopen = getattr(__config__, 'nopen', 0) def __init__(self, mol, nact=None, nopen=None): hf.SCF.__init__(self, mol) if nact is not None : self.nact=nact diff --git a/pyscf/shciscf/fcidump_rel.py b/pyscf/shciscf/fcidump_rel.py index b1fc42b29f..5ace831d48 100644 --- a/pyscf/shciscf/fcidump_rel.py +++ b/pyscf/shciscf/fcidump_rel.py @@ -50,7 +50,7 @@ def write_head(fout, nmo, nelec, ms, orbsym=None): fout.write(' ISYM=0,\n') fout.write(' &END\n') -def from_x2c(mf, ncore, nact, filename = 'FCIDUMP', tol=1e-16, intor='int2e_spinor', h1e = None): +def from_x2c(mf, ncore, nact, filename = 'FCIDUMP', tol=1e-8, intor='int2e_spinor', h1e = None, approx = '1e'): ncore = ncore*2 nact = nact*2 mo_coeff = mf.mo_coeff[:,ncore:ncore+nact] @@ -58,6 +58,7 @@ def from_x2c(mf, ncore, nact, filename = 'FCIDUMP', tol=1e-16, intor='int2e_spin assert mo_coeff.dtype == numpy.complex + mf.with_x2c.approx = approx hcore = mf.get_hcore() core_occ = numpy.zeros(len(mf.mo_energy)) core_occ[:ncore]=1.0 @@ -66,7 +67,7 @@ def from_x2c(mf, ncore, nact, filename = 'FCIDUMP', tol=1e-16, intor='int2e_spin energy_core = mf.energy_nuc() energy_core += numpy.einsum('ij,ji', core_dm, hcore) energy_core += numpy.einsum('ij,ji', core_dm, corevhf) * .5 - h1eff = reduce(numpy.dot, (mo_coeff.T, hcore+corevhf, mo_coeff)) + h1eff = reduce(numpy.dot, (mo_coeff.T.conjugate(), hcore+corevhf, mo_coeff)) #print(h1eff, energy_core) #reduce(numpy.dot, (mf.mo_coeff.T, mf.get_hcore(), mf.mo_coeff))[ncore:ncore+nact, ncore:ncore+nact] if mf._eri is None: @@ -78,7 +79,7 @@ def from_x2c(mf, ncore, nact, filename = 'FCIDUMP', tol=1e-16, intor='int2e_spin #dm = mf.make_rdm1(mo_occ = core_occ) #core_energy = scf.hf.energy_elec(mf, dm=dm) print(mf.energy_nuc(), energy_core) - from_integrals(filename = filename, h1e=h1eff, h2e=eri, nmo=nact, nelec=sum(mf.mol.nelec)-ncore, nuc=energy_core.real) + from_integrals(filename = filename, h1e=h1eff, h2e=eri, nmo=nact, nelec=sum(mf.mol.nelec)-ncore, nuc=energy_core.real, tol=tol) def from_dhf(mf, ncore, nact, filename = 'FCIDUMP', tol=1e-10, intor='int2e_spinor'): ncore = ncore*2 @@ -122,3 +123,4 @@ def from_dhf(mf, ncore, nact, filename = 'FCIDUMP', tol=1e-10, intor='int2e_spin mf_dirac = scf.DHF(mol) mf_dirac.kernel() from_dhf(mf_dirac, 0, 7, filename = 'FCIDUMP_dhf') + diff --git a/pyscf/shciscf/socutils.py b/pyscf/shciscf/socutils.py index 765bf3701a..6496db169f 100644 --- a/pyscf/shciscf/socutils.py +++ b/pyscf/shciscf/socutils.py @@ -42,7 +42,7 @@ def get_socamf(atoms, basis): nopen = 1 nact = 1 from pyscf import scf - mf_atom = scf.sfx2c1e(scf.AOCHF(mol_atom, nopen=nopen, nact=nact)) + mf_atom = scf.sfx2c1e(scf.AOCHF(mol_atom, nopen=nopen, nact=nact)) xatm, contr_coeff = x2c.X2C(mol_atom).get_xmol() np, nc = contr_coeff.shape mf_atom.kernel() @@ -55,16 +55,23 @@ def get_socamf(atoms, basis): h1ao = numpy.zeros((3, nc, nc),dtype=complex) h1ao_uncontract += factor*get_fso2e_x2c(xatm, x, rp, pLL, pLS, pSS) for ic in range(3): - h1ao[ic] = reduce( - numpy.dot, (contr_coeff.T, h1ao_uncontract[ic], contr_coeff)) + h1ao[ic] = reduce(numpy.dot, (contr_coeff.T, h1ao_uncontract[ic], contr_coeff)) hso1e = numpy.zeros((nc*2, nc*2),dtype=complex) - hso1e[1::2,0::2]+=h1ao[0] - hso1e[0::2,1::2]+=h1ao[0] - hso1e[1::2,0::2]+=-1.j*h1ao[1] - hso1e[0::2,1::2]+= 1.j*h1ao[1] - hso1e[0::2,0::2]+=h1ao[2] - hso1e[1::2,1::2]-=h1ao[2] - hso1e_atoms[atom]=hso1e + #hso1e[1::2,0::2]+=h1ao[0,:,:] + #hso1e[0::2,1::2]+=h1ao[0,:,:] + #hso1e[1::2,0::2]+= 1.j*h1ao[1,:,:] + #hso1e[0::2,1::2]+=-1.j*h1ao[1,:,:] + #hso1e[0::2,0::2]+=h1ao[2,:,:] + #hso1e[1::2,1::2]-=h1ao[2,:,:] + for i in range(nc): + for j in range(nc): + hso1e[2*i ,2*j+1]+=1.j*h1ao[0,i,j] + hso1e[2*i+1,2*j ]+=1.j*h1ao[0,i,j] + hso1e[2*i ,2*j+1]+= 1.*h1ao[1,i,j] + hso1e[2*i+1,2*j ]+=-1.*h1ao[1,i,j] + hso1e[2*i ,2*j ]+= 1.j*h1ao[2,i,j] + hso1e[2*i+1,2*j+1]+=-1.j*h1ao[2,i,j] + hso1e_atoms[atom]=1.*hso1e return hso1e_atoms def print1Int(h1, name): @@ -524,7 +531,10 @@ def writeSOCIntegrals(mc, hso1e += -factor * get_fso2e_x2c_original(xmol, x, rp, pLL, pLS, pSS) elif (pictureChange2e == "x2c"): pLL, pLS, pSS = get_p(dm / 2.0, x, rp) - hso1e += -factor * get_fso2e_x2c(xmol, x, rp, pLL, pLS, pSS) + #hso1e += -factor * get_fso2e_x2c(xmol, x, rp, pLL, pLS, pSS) + hso1e[0] += -factor * get_fso2e_x2c(xmol, x, rp, pLL, pLS, pSS)[0] + hso1e[1] += -factor * get_fso2e_x2c(xmol, x, rp, pLL, pLS, pSS)[1] + hso1e[2] += -factor * get_fso2e_x2c(xmol, x, rp, pLL, pLS, pSS)[2] elif (pictureChange2e == "x2c1c"): pLL, pLS, pSS = get_p(dm / 2.0, x, rp) hso1e += -factor * get_fso2e_x2c1c(xmol, x, rp, pLL, pLS, pSS) diff --git a/pyscf/x2c/x2c.py b/pyscf/x2c/x2c.py index 7a8f7dc1db..3cbddd72fd 100644 --- a/pyscf/x2c/x2c.py +++ b/pyscf/x2c/x2c.py @@ -120,12 +120,6 @@ def get_hcore(self, mol=None): for ia in range(xmol.natm): ish0, ish1, c0, c1 = atom_slices[ia] hso1e[c0:c1,c0:c1] += hso1e_atoms[self.mol.elements[ia]] - #i,j,k,l=atom_slices[0] - #N0=hso1e[k:l,k:l] - #i,j,k,l=atom_slices[1] - #N1=hso1e[k:l,k:l] - #print(N0-N1) - #exit(0) return h1+hso1e def get_xmat(self, mol=None): From 14619dcb3d011b40589a1723b57b2acdd9648eb3 Mon Sep 17 00:00:00 2001 From: xubwa Date: Mon, 30 Mar 2020 14:55:50 -0600 Subject: [PATCH 13/26] add boettger factor to x2c spin-orbit integrals --- pyscf/x2c/x2c.py | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/pyscf/x2c/x2c.py b/pyscf/x2c/x2c.py index 3cbddd72fd..93ab577a44 100644 --- a/pyscf/x2c/x2c.py +++ b/pyscf/x2c/x2c.py @@ -81,6 +81,50 @@ def get_hcore(self, mol=None): t = xmol.intor_symmetric('int1e_spsp_spinor') * .5 v = xmol.intor_symmetric('int1e_nuc_spinor') w = xmol.intor_symmetric('int1e_spnucsp_spinor') + + # an approximate one-electron spin-orbit integral to account for two-electron effect + # by adding an scaling factor + # Q = [0,2,10,28,60,110] for [s,p,d,f,g,h] seperately + # + if 'BOETTGER' in self.approx.upper(): + # first construct the array sqrt(Q(l)/Z) + fudge_factor = numpy.zeros(xmol.nao_2c()) + spinor_labels = xmol.spinor_labels(0) + atom_slices = xmol.offset_2c_by_atom() + for ia in range(xmol.natm): + Z_ia = xmol.atom_charge(ia) + #print(Z_ia) + for iorb in range(atom_slices[ia][2], atom_slices[ia][3]): + label = spinor_labels[iorb] + if 'S' in label[2].upper(): + fudge_factor[iorb] = 0./Z_ia + #print('s', label) + elif 'P' in label[2].upper(): + #if 2 < Z_ia: + fudge_factor[iorb] = 2./Z_ia + #print('p', label) + elif 'D' in label[2].upper(): + #if 10 < Z_ia: + fudge_factor[iorb] = 10./Z_ia + #print('d', label) + elif 'F' in label[2].upper(): + #if 28 < Z_ia: + fudge_factor[iorb] = 28./Z_ia + #print('f', label) + elif 'G' in label[2].upper(): + #if 60 < Z_ia: + fudge_factor[iorb] = 60./Z_ia + #print('g', label) + elif 'H' in label[2].upper: + #if 110 < Z_ia: + #print('h', label) + fudge_factor[iorb] = 110./Z_ia + #fudge_factor = numpy.sqrt(fudge_factor) + #print(fudge_factor) + for iorb in range(xmol.nao_2c()): + for jorb in range(xmol.nao_2c()): + w[iorb, jorb]*=(1.-numpy.sqrt(fudge_factor[iorb]*fudge_factor[jorb])) + if 'ATOM' in self.approx.upper(): atom_slices = xmol.offset_2c_by_atom() n2c = xmol.nao_2c() @@ -110,7 +154,6 @@ def get_hcore(self, mol=None): contr_coeff[0::2,0::2] = contr_coeff_nr contr_coeff[1::2,1::2] = contr_coeff_nr h1 = reduce(numpy.dot, (contr_coeff.T.conj(), h1, contr_coeff)) - print (self.approx.upper()) hso1e = numpy.zeros((nc*2, nc*2), dtype=complex) if 'SOCAMF' in self.approx.upper(): # incorporate X2CAMF approximation. From a911d5bd16d54ba0c1727c49f155f806b25f8167 Mon Sep 17 00:00:00 2001 From: Sandeep Sharma Date: Tue, 31 Mar 2020 09:25:05 -0600 Subject: [PATCH 14/26] aded the ECPscalar to the one electron integrals before dumping it to fcidump. --- pyscf/dft/radi.py | 21 +++++++++++++++++++++ pyscf/tools/fcidump.py | 2 ++ 2 files changed, 23 insertions(+) diff --git a/pyscf/dft/radi.py b/pyscf/dft/radi.py index d5ed0ee0f9..c5b835a0d2 100644 --- a/pyscf/dft/radi.py +++ b/pyscf/dft/radi.py @@ -66,6 +66,17 @@ def delley(n, *args, **kwargs): return r, dr gauss_legendre = delley +def mura_knowles_inputFar(n, far, charge=None, *args, **kwargs): + '''Mura-Knowles (JCP, 104, 9848) log3 quadrature radial grids''' + far = 50 + r = numpy.empty(n) + dr = numpy.empty(n) + for i in range(n): + x = (i+.5) / n + r[i] = -far * numpy.log(1-x**3) + dr[i] = far * 3*x*x/((1-x**3)*n) + return r, dr + def mura_knowles(n, charge=None, *args, **kwargs): '''Mura-Knowles (JCP, 104, 9848) log3 quadrature radial grids''' r = numpy.empty(n) @@ -84,6 +95,16 @@ def mura_knowles(n, charge=None, *args, **kwargs): # Gauss-Chebyshev of the second kind, and the transformed interval [0,\infty) # Ref Matthias Krack and Andreas M. Koster, J. Chem. Phys. 108 (1998), 3226 def gauss_chebyshev(n, *args, **kwargs): + '''Mura-Knowles (JCP, 104, 9848) log3 quadrature radial grids''' + far = 50 + r = numpy.empty(n) + dr = numpy.empty(n) + for i in range(n): + x = (i+.5) / n + r[i] = -far * numpy.log(1-x**3) + dr[i] = far * 3*x*x/((1-x**3)*n) + return r, dr + '''Gauss-Chebyshev (JCP, 108, 3226) radial grids''' ln2 = 1 / numpy.log(2) fac = 16./3 / (n+1) diff --git a/pyscf/tools/fcidump.py b/pyscf/tools/fcidump.py index 13f4d7d402..03c79d5417 100644 --- a/pyscf/tools/fcidump.py +++ b/pyscf/tools/fcidump.py @@ -135,6 +135,8 @@ def from_mo(mol, filename, mo_coeff, orbsym=None, orbsym = getattr(mo_coeff, 'orbsym', None) t = mol.intor_symmetric('int1e_kin') v = mol.intor_symmetric('int1e_nuc') + if len(mol._ecpbas) > 0: + v += mol.intor_symmetric('ECPscalar') h1e = reduce(numpy.dot, (mo_coeff.T, t+v, mo_coeff)) eri = ao2mo.full(mol, mo_coeff, verbose=0) nuc = mol.energy_nuc() From f0ab3fd17b5621fba02dd7bb6f8429d6aff70afe Mon Sep 17 00:00:00 2001 From: Sandeep Sharma Date: Tue, 31 Mar 2020 09:32:10 -0600 Subject: [PATCH 15/26] small changes to vmcscf. --- pyscf/vmcscf/vmc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyscf/vmcscf/vmc.py b/pyscf/vmcscf/vmc.py index 9812d58a27..dc3c0129d7 100644 --- a/pyscf/vmcscf/vmc.py +++ b/pyscf/vmcscf/vmc.py @@ -455,7 +455,7 @@ def bestDetValence(mol, lmo, occ, eri, writeToFile=True): for i in enumerate(maxLMOContributers): lmoSites[numpy.searchsorted(numpy.array(atomNumAOs), i[1])].append(i[0]) - bestDet = [0 for i in range(lmo.shape[1])] + bestDet = ['0' for i in range(lmo.shape[1])] def pair(i): return i*(i+1)//2+i for i in enumerate(occ): From df56f9fb26760c3c7e1ce7dad00629f806a42204 Mon Sep 17 00:00:00 2001 From: xubwa Date: Sun, 5 Apr 2020 02:42:22 -0600 Subject: [PATCH 16/26] "fix amf implementation" --- pyscf/scf/__init__.py | 8 ++-- pyscf/scf/aochf.py | 11 ++--- pyscf/scf/dhf.py | 99 +++++++++++++++++++++++++++++++++++++-- pyscf/scf/hf.py | 11 ----- pyscf/shciscf/socutils.py | 52 +++++++++----------- pyscf/x2c/x2c.py | 53 +++++++++++---------- 6 files changed, 155 insertions(+), 79 deletions(-) diff --git a/pyscf/scf/__init__.py b/pyscf/scf/__init__.py index 1d933dee92..9d52cffe31 100644 --- a/pyscf/scf/__init__.py +++ b/pyscf/scf/__init__.py @@ -186,13 +186,13 @@ def DHF(mol, *args): return dhf.UHF(mol, *args) -def X2C(mol, nopen=None, nact=None, *kwargs): +def X2C(mol, *kwargs): '''X2C UHF (in testing)''' from pyscf.x2c import x2c - return x2c.UHF(mol, nopen=nopen, nact=nact, *kwargs) + return x2c.UHF(mol, *kwargs) -def AOCHF(mol, nopen=None, nact=None): - return aochf.AOCHF(mol, nopen=nopen, nact=nact) +def AOCHF(mol, *kwargs): + return aochf.AOCHF(mol, *kwargs) def sfx2c1e(mf): return mf.sfx2c1e() diff --git a/pyscf/scf/aochf.py b/pyscf/scf/aochf.py index 14e4547533..cc4a581cdd 100644 --- a/pyscf/scf/aochf.py +++ b/pyscf/scf/aochf.py @@ -33,10 +33,10 @@ def get_occ(mf, mo_energy=None, mo_coeff=None, nclose=None, nact=None, nopen=Non Number of active orbitals ''' if mo_energy is None: mo_energy = mf.mo_energy - if nclose is None : nclose = mf.nclose if nact is None : nact = mf.nact - if nopen is None : nopen = mf.nopen + if nopen is None : nopen = mf.nopen // 2 + nclose = (mf.mol.nelectron - nact) // 2 if mo_energy is None: mo_energy = mf.mo_energy e_idx_a = numpy.argsort(mo_energy[0]) e_idx_b = numpy.argsort(mo_energy[1]) @@ -100,12 +100,9 @@ class AOCHF(uhf.UHF): nclose = getattr(__config__, 'nclose', 0) nact = getattr(__config__, 'nact', 0) nopen = getattr(__config__, 'nopen', 0) - def __init__(self, mol, nact=None, nopen=None): + + def __init__(self, mol): hf.SCF.__init__(self, mol) - if nact is not None : self.nact=nact - if nopen is not None : self.nopen=nopen - self.nclose=(mol.nelectron-self.nact)//2 - print(self.nact, self.nopen, self.nclose) self.nelec=None get_occ = get_occ diff --git a/pyscf/scf/dhf.py b/pyscf/scf/dhf.py index fb497ab4b9..8d1a08a952 100644 --- a/pyscf/scf/dhf.py +++ b/pyscf/scf/dhf.py @@ -108,6 +108,40 @@ def get_jk_coulomb(mol, dm, hermi=1, coulomb_allow='SSSS', vk[...,n2c:,n2c:] += k1 return vj, vk + +def get_jk_sf_coulomb(mol, dm, hermi=1, coulomb_allow='SSSS', + opt_llll=None, opt_ssll=None, opt_ssss=None, omega=None, verbose=None): + log = logger.new_logger(mol, verbose) + with mol.with_range_coulomb(omega): + 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_sf_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_sf_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_sf_ssss(mol, dm, hermi, opt_ssss) + vj[...,n2c:,n2c:] += j1 + vk[...,n2c:,n2c:] += k1 + + return vj, vk + get_jk = get_jk_coulomb @@ -371,6 +405,9 @@ class UHF(hf.SCF): with_ssss = getattr(__config__, 'scf_dhf_SCF_with_ssss', True) with_gaunt = getattr(__config__, 'scf_dhf_SCF_with_gaunt', False) with_breit = getattr(__config__, 'scf_dhf_SCF_with_breit', False) + nopen = getattr(__config__, 'scf_dhf_SCF_nopen', 0) + nact = getattr(__config__, 'scf_dhf_SCF_nopen', 0) + nopen = getattr(__config__, 'scf_dhf_SCF_nopen', 0) def __init__(self, mol): hf.SCF.__init__(self, mol) @@ -426,20 +463,31 @@ def build(self, mol=None): def get_occ(self, mo_energy=None, mo_coeff=None): if mo_energy is None: mo_energy = self.mo_energy mol = self.mol + nopen = self.nopen + nact = self.nact + nclose = mol.nelectron - nact c = lib.param.LIGHT_SPEED n4c = len(mo_energy) n2c = n4c // 2 mo_occ = numpy.zeros(n2c * 2) if mo_energy[n2c] > -1.999 * c**2: - mo_occ[n2c:n2c+mol.nelectron] = 1 + if nopen is 0: + mo_occ[n2c:n2c+mol.nelectron] = 1 + else: + mo_occ[n2c:n2c+nclose] = 1 + mo_occ[n2c+nclose:n2c+nclose+nopen] = 1.*nact/nopen else: lumo = mo_energy[mo_energy > -1.999 * c**2][mol.nelectron] mo_occ[mo_energy > -1.999 * c**2] = 1 mo_occ[mo_energy >= lumo] = 0 if self.verbose >= logger.INFO: + if nopen is 0: + homo_ndx = mol.nelectron + else: + homo_ndx = nclose + nopen logger.info(self, 'HOMO %d = %.12g LUMO %d = %.12g', - n2c+mol.nelectron, mo_energy[n2c+mol.nelectron-1], - n2c+mol.nelectron+1, mo_energy[n2c+mol.nelectron]) + n2c+homo_ndx, mo_energy[n2c+homo_ndx-1], + n2c+homo_ndx+1, mo_energy[n2c+homo_ndx]) logger.debug1(self, 'NES mo_energy = %s', mo_energy[:n2c]) logger.debug(self, 'PES mo_energy = %s', mo_energy[n2c:]) return mo_occ @@ -669,6 +717,36 @@ def _call_veff_ssll(mol, dm, hermi=1, mf_opt=None): vk = vk.reshape(vk.shape[1:]) return _jk_triu_(vj, vk, hermi) +def _call_veff_sf_ssll(mol, dm, hermi=1, mf_opt=None): + if isinstance(dm, numpy.ndarray) and dm.ndim == 2: + n_dm = 1 + n2c = dm.shape[0] // 2 + dmll = dm[:n2c,:n2c].copy() + dmsl = dm[n2c:,:n2c].copy() + dmss = dm[n2c:,n2c:].copy() + dms = (dmll, dmss, dmsl) + else: + n_dm = len(dm) + n2c = dm[0].shape[0] // 2 + dms = [dmi[:n2c,:n2c].copy() for dmi in dm] \ + + [dmi[n2c:,n2c:].copy() for dmi in dm] \ + + [dmi[n2c:,:n2c].copy() for dmi in dm] + jks = ('lk->s2ij',) * n_dm \ + + ('ji->s2kl',) * n_dm \ + + ('jk->s1il',) * n_dm + c1 = .5 / lib.param.LIGHT_SPEED + vx = _vhf.rdirect_bindm('int2e_pp1_spinor', 's4', jks, dms, 1, + mol._atm, mol._bas, mol._env, mf_opt) * c1**2 + vj = numpy.zeros((n_dm,n2c*2,n2c*2), dtype=numpy.complex) + vk = numpy.zeros((n_dm,n2c*2,n2c*2), dtype=numpy.complex) + vj[:,n2c:,n2c:] = vx[ :n_dm ,:,:] + vj[:,:n2c,:n2c] = vx[n_dm :n_dm*2,:,:] + vk[:,n2c:,:n2c] = vx[n_dm*2: ,:,:] + if n_dm == 1: + vj = vj.reshape(vj.shape[1:]) + vk = vk.reshape(vk.shape[1:]) + return _jk_triu_(vj, vk, hermi) + def _call_veff_ssss(mol, dm, hermi=1, mf_opt=None): c1 = .5 / lib.param.LIGHT_SPEED if isinstance(dm, numpy.ndarray) and dm.ndim == 2: @@ -684,6 +762,21 @@ def _call_veff_ssss(mol, dm, hermi=1, mf_opt=None): mol._atm, mol._bas, mol._env, mf_opt) * c1**4 return _jk_triu_(vj, vk, hermi) +def _call_veff_sf_ssss(mol, dm, hermi=1, mf_opt=None): + c1 = .5 / lib.param.LIGHT_SPEED + if isinstance(dm, numpy.ndarray) and dm.ndim == 2: + n2c = dm.shape[0] // 2 + dms = dm[n2c:,n2c:].copy() + else: + n2c = dm[0].shape[0] // 2 + dms = [] + for dmi in dm: + dms.append(dmi[n2c:,n2c:].copy()) + vj, vk = _vhf.rdirect_mapdm('int2e_pp1pp2_spinor', 's8', + ('ji->s2kl', 'jk->s1il'), dms, 1, + mol._atm, mol._bas, mol._env, mf_opt) * c1**4 + return _jk_triu_(vj, vk, hermi) + def _call_veff_gaunt_breit(mol, dm, hermi=1, mf_opt=None, with_breit=False): if with_breit: intor_prefix = 'int2e_breit_' diff --git a/pyscf/scf/hf.py b/pyscf/scf/hf.py index 5a78990867..ac2dac9244 100644 --- a/pyscf/scf/hf.py +++ b/pyscf/scf/hf.py @@ -164,17 +164,6 @@ def kernel(mf, conv_tol=1e-10, conv_tol_grad=None, fock = mf.get_fock(h1e, s1e, vhf, dm, cycle, mf_diis) mo_energy, mo_coeff = mf.eig(fock, s1e) mo_occ = mf.get_occ(mo_energy, mo_coeff) - if type(mf) is x2c.x2c.X2C_UHF and cycle > 5: - mo_tmp = numpy.zeros((mo_coeff.shape),dtype=complex) - mo_tmp[0::2,0::2]=numpy.sign(mo_coeff[0::2,0::2])*(abs(mo_coeff[0::2,0::2]) + abs(mo_coeff[1::2,1::2]))/2 - mo_tmp[1::2,1::2]=numpy.sign(mo_coeff[1::2,1::2])*(abs(mo_coeff[0::2,0::2]) + abs(mo_coeff[1::2,1::2]))/2 - mo_tmp[1::2,0::2]=numpy.sign(mo_coeff[1::2,0::2])*(abs(mo_coeff[1::2,0::2]) + abs(mo_coeff[0::2,1::2])) / 2 - mo_tmp[0::2,1::2]=numpy.sign(mo_coeff[0::2,1::2])*(abs(mo_coeff[0::2,1::2]) + abs(mo_coeff[1::2,0::2])) / 2 - #mo_coeff=mo_tmp - #print(numpy.isclose(mo_coeff, mo_tmp)) - for i,j in zip(mo_coeff, mo_tmp): - print(i) - print(j) dm = mf.make_rdm1(mo_coeff, mo_occ) # attach mo_coeff and mo_occ to dm to improve DFT get_veff efficiency dm = lib.tag_array(dm, mo_coeff=mo_coeff, mo_occ=mo_occ) diff --git a/pyscf/shciscf/socutils.py b/pyscf/shciscf/socutils.py index 6496db169f..6dbc9e5c89 100644 --- a/pyscf/shciscf/socutils.py +++ b/pyscf/shciscf/socutils.py @@ -12,7 +12,7 @@ from pyscf.lib import logger alpha = 1./LIGHT_SPEED - +c = LIGHT_SPEED def get_socamf(atoms, basis): hso1e_atoms = {} factor = -alpha**2 * 0.25 @@ -42,36 +42,30 @@ def get_socamf(atoms, basis): nopen = 1 nact = 1 from pyscf import scf - mf_atom = scf.sfx2c1e(scf.AOCHF(mol_atom, nopen=nopen, nact=nact)) - xatm, contr_coeff = x2c.X2C(mol_atom).get_xmol() - np, nc = contr_coeff.shape + mf_atom = scf.DHF(mol_atom) + mf_atom.nopen = nopen*2 + mf_atom.nact = nact mf_atom.kernel() - h1e, x, rp = get_hxr(mf_atom, uncontract=True) dm = mf_atom.make_rdm1() - dm = dm[0]+dm[1] - dm = reduce(numpy.dot, (contr_coeff, dm, contr_coeff.T)) - pLL, pLS, pSS = get_p(dm/2, x, rp) - h1ao_uncontract = numpy.zeros((3, np, np),dtype=complex) - h1ao = numpy.zeros((3, nc, nc),dtype=complex) - h1ao_uncontract += factor*get_fso2e_x2c(xatm, x, rp, pLL, pLS, pSS) - for ic in range(3): - h1ao[ic] = reduce(numpy.dot, (contr_coeff.T, h1ao_uncontract[ic], contr_coeff)) - hso1e = numpy.zeros((nc*2, nc*2),dtype=complex) - #hso1e[1::2,0::2]+=h1ao[0,:,:] - #hso1e[0::2,1::2]+=h1ao[0,:,:] - #hso1e[1::2,0::2]+= 1.j*h1ao[1,:,:] - #hso1e[0::2,1::2]+=-1.j*h1ao[1,:,:] - #hso1e[0::2,0::2]+=h1ao[2,:,:] - #hso1e[1::2,1::2]-=h1ao[2,:,:] - for i in range(nc): - for j in range(nc): - hso1e[2*i ,2*j+1]+=1.j*h1ao[0,i,j] - hso1e[2*i+1,2*j ]+=1.j*h1ao[0,i,j] - hso1e[2*i ,2*j+1]+= 1.*h1ao[1,i,j] - hso1e[2*i+1,2*j ]+=-1.*h1ao[1,i,j] - hso1e[2*i ,2*j ]+= 1.j*h1ao[2,i,j] - hso1e[2*i+1,2*j+1]+=-1.j*h1ao[2,i,j] - hso1e_atoms[atom]=1.*hso1e + from pyscf.scf import dhf + x2c_obj = x2c.X2C(mol_atom) + x2c_obj.xuncontract = False + x = x2c_obj.get_xmat() + s = mol_atom.intor_symmetric('int1e_ovlp_spinor') + t = mol_atom.intor_symmetric('int1e_spsp_spinor')*.5 + s1 = s + reduce(numpy.dot, (x.T.conj(), t, x)) * (.5/c**2) + r = x2c._get_r(s, s1) + vj, vk = dhf.get_jk_coulomb(mol_atom, dm, 1, mf_atom._coulomb_now) + vj_sf, vk_sf = dhf.get_jk_sf_coulomb(mol_atom, dm, 1, mf_atom._coulomb_now) + + veff_sd = (vj-vk)-(vj_sf-vk_sf) + n2c=mol_atom.nao_2c() + g_ll = veff_sd[:n2c, :n2c] + g_ls = veff_sd[:n2c, n2c:] + g_sl = veff_sd[n2c:, :n2c] + g_ss = veff_sd[n2c:, n2c:] + g_so_mf = reduce(numpy.dot,(r.T.conj(), (g_ll+reduce(numpy.dot, (g_ls, x))+reduce(numpy.dot, (x.T.conj(), g_sl))+reduce(numpy.dot, (x.T.conj(), g_ss, x))),r)) + hso1e_atoms[atom]=g_so_mf return hso1e_atoms def print1Int(h1, name): diff --git a/pyscf/x2c/x2c.py b/pyscf/x2c/x2c.py index 93ab577a44..e53d60fa15 100644 --- a/pyscf/x2c/x2c.py +++ b/pyscf/x2c/x2c.py @@ -98,29 +98,21 @@ def get_hcore(self, mol=None): label = spinor_labels[iorb] if 'S' in label[2].upper(): fudge_factor[iorb] = 0./Z_ia - #print('s', label) elif 'P' in label[2].upper(): #if 2 < Z_ia: fudge_factor[iorb] = 2./Z_ia - #print('p', label) elif 'D' in label[2].upper(): #if 10 < Z_ia: fudge_factor[iorb] = 10./Z_ia - #print('d', label) elif 'F' in label[2].upper(): #if 28 < Z_ia: fudge_factor[iorb] = 28./Z_ia - #print('f', label) elif 'G' in label[2].upper(): #if 60 < Z_ia: fudge_factor[iorb] = 60./Z_ia - #print('g', label) elif 'H' in label[2].upper: #if 110 < Z_ia: - #print('h', label) fudge_factor[iorb] = 110./Z_ia - #fudge_factor = numpy.sqrt(fudge_factor) - #print(fudge_factor) for iorb in range(xmol.nao_2c()): for jorb in range(xmol.nao_2c()): w[iorb, jorb]*=(1.-numpy.sqrt(fudge_factor[iorb]*fudge_factor[jorb])) @@ -155,7 +147,7 @@ def get_hcore(self, mol=None): contr_coeff[1::2,1::2] = contr_coeff_nr h1 = reduce(numpy.dot, (contr_coeff.T.conj(), h1, contr_coeff)) hso1e = numpy.zeros((nc*2, nc*2), dtype=complex) - if 'SOCAMF' in self.approx.upper(): + if 'AMF' in self.approx.upper(): # incorporate X2CAMF approximation. # since 1e SOC terms are already included. hso1e_atoms = socutils.get_socamf(list(set(xmol.elements)), xmol.basis) @@ -258,17 +250,17 @@ def get_init_guess(mol, key='minao'): class X2C_UHF(hf.SCF): - def __init__(self, mol, nact=None, nopen=None): + nopen = getattr(__config__, 'scf_dhf_SCF_nopen', 0) + nact = getattr(__config__, 'scf_dhf_SCF_nact', 0) + nclose = getattr(__config__, 'scf_dhf_SCF_nclose', 0) + h1_somf = getattr(__config__, 'scf_dhf_SCF_h1_somf', None) + + def __init__(self, mol): hf.SCF.__init__(self, mol) self.with_x2c = X2C(mol) #self.with_x2c.xuncontract = False self._keys = self._keys.union(['with_x2c']) - if nact is not None : self.nact=nact - else : self.nact=0 - if nopen is not None : self.nopen=nopen - else : self.nopen=0 - self.nclose=(mol.nelectron-self.nact)//2 - + def build(self, mol=None): if self.verbose >= logger.WARN: self.check_sanity() @@ -302,26 +294,27 @@ def _eigh(self, h, s): def get_hcore(self, mol=None): if mol is None: mol = self.mol return self.with_x2c.get_hcore(mol) + def get_ovlp(self, mol=None): if mol is None: mol = self.mol return mol.intor_symmetric('int1e_ovlp_spinor') - def get_occ(self, mo_energy=None, mo_coeff=None, nclose=None, nact=None, nopen=None): + def get_occ(self, mo_energy=None, mo_coeff=None): if mo_energy is None: mo_energy = self.mo_energy - if nclose is None : nclose = self.nclose - if nact is None : nact = self.nact - if nopen is None : nopen = self.nopen + nact = self.nact + nopen = self.nopen mol = self.mol - assert(mol.nelectron==nclose*2+nact) + nclose = mol.nelectron-nact + assert(mol.nelectron==nclose+nact) mo_occ = numpy.zeros_like(mo_energy) if nopen is 0: mo_occ[:mol.nelectron] = 1 else: - mo_occ[:2*nclose]=1 - average_occ=nact/(nopen*2.) - mo_occ[2*nclose:2*(nclose+nopen)]=average_occ - if 2*(nopen+nclose) < len(mo_energy): + mo_occ[:nclose]=1 + average_occ=nact/nopen + mo_occ[nclose:nclose+nopen]=average_occ + if nopen+nclose < len(mo_energy): logger.info(self, 'nocc = %d HOMO = %.12g LUMO = %.12g', \ mol.nelectron, mo_energy[mol.nelectron-1], mo_energy[mol.nelectron]) @@ -544,6 +537,16 @@ def _x2c1e_xmatrix(t, v, w, s, c): x = numpy.linalg.solve(cl.T, cs.T).T # B = XA return x +def _x2cmf_xmatrix(h, s, c): + nao = s.shape[0] // 2 + n2 = nao * 2 + m = s + e, a = scipy.linalg.eigh(h, m) + cl = a[:nao,nao:] + cs = a[nao:,nao:] + x = numpy.linalg.solve(cl.T, cs.T).T # B = XA + return x + def _x2c1e_get_hcore(t, v, w, s, c): nao = s.shape[0] n2 = nao * 2 From 2564402ee98cbbea7418dd9506ee910d8c91bf9b Mon Sep 17 00:00:00 2001 From: Sandeep Sharma Date: Mon, 13 Apr 2020 08:37:43 -0600 Subject: [PATCH 17/26] write gto was added. --- pyscf/gto/write_gto.py | 108 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 pyscf/gto/write_gto.py diff --git a/pyscf/gto/write_gto.py b/pyscf/gto/write_gto.py new file mode 100644 index 0000000000..d491b9d5fd --- /dev/null +++ b/pyscf/gto/write_gto.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python +# Copyright 2014-2018 The PySCF Developers. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: Qiming Sun +# + +import warnings +import ctypes +import numpy +from pyscf import lib +from pyscf.gto.moleintor import make_loc + +BLKSIZE = 128 # needs to be the same to lib/gto/grid_ao_drv.c + +libcgto = lib.load_library('libcgto') + +def write_gto(mol): + atm = numpy.asarray(mol._atm, dtype=numpy.int32, order='C') + bas = numpy.asarray(mol._bas, dtype=numpy.int32, order='C') + env = numpy.asarray(mol._env, dtype=numpy.double, order='C') + natm = atm.shape[0] + nbas = bas.shape[0] + + eval_name, comp = _get_intor_and_comp(mol, "GTOval") + ao_loc = make_loc(bas, eval_name) + + shls_slice = (0, nbas) + sh0, sh1 = shls_slice + nao = ao_loc[sh1] - ao_loc[sh0] + + ngrids = 1 + non0tab = numpy.ones(((ngrids+BLKSIZE-1)//BLKSIZE,nbas), + dtype=numpy.int8) + + basisInfo = open("basisInfo.txt", "w") + if ("cart" in eval_name): + basisInfo.write("cart\n") + else: + basisInfo.write("sph\n") + basisInfo.write("%i\n"%(nao)) + basisInfo.write("%i\n%i \n"%(shls_slice[0], shls_slice[1])) + basisInfo.write("%i\n"%(len(ao_loc))) + for a in ao_loc: + basisInfo.write("%i "%(a)) + basisInfo.write("\n%i %i\n"%(atm.shape[0], atm.shape[1])) + for i in range(atm.shape[0]): + for j in range(atm.shape[1]): + basisInfo.write("%i "%(atm[i][j])) + basisInfo.write("\n%i %i\n"%(bas.shape[0], bas.shape[1])) + for i in range(bas.shape[0]): + for j in range(bas.shape[1]): + basisInfo.write("%i "%(bas[i][j])) + basisInfo.write("\n%i\n"%(len(env))) + for e in env: + basisInfo.write("%35.18e\n"%(e)) + + basisInfo.write("%i\n"%(len(non0tab[0]))) + for e in non0tab[0]: + basisInfo.write("%i "%(e)) + + basisInfo.close() + +def _get_intor_and_comp(mol, eval_name, comp=None): + if not ('_sph' in eval_name or '_cart' in eval_name or + '_spinor' in eval_name): + if mol.cart: + eval_name = eval_name + '_cart' + else: + eval_name = eval_name + '_sph' + + if comp is None: + if '_spinor' in eval_name: + fname = eval_name.replace('_spinor', '') + comp = _GTO_EVAL_FUNCTIONS.get(fname, (None,None))[1] + else: + fname = eval_name.replace('_sph', '').replace('_cart', '') + comp = _GTO_EVAL_FUNCTIONS.get(fname, (None,None))[0] + if comp is None: + warnings.warn('Function %s not found. Set its comp to 1' % eval_name) + comp = 1 + return eval_name, comp + +_GTO_EVAL_FUNCTIONS = { +# Functiona name : (comp-for-scalar, comp-for-spinor) + 'GTOval' : (1, 1 ), + 'GTOval_ip' : (3, 3 ), + 'GTOval_ig' : (3, 3 ), + 'GTOval_ipig' : (3, 3 ), + 'GTOval_deriv0' : (1, 1 ), + 'GTOval_deriv1' : (4, 4 ), + 'GTOval_deriv2' : (10,10), + 'GTOval_deriv3' : (20,20), + 'GTOval_deriv4' : (35,35), + 'GTOval_sp' : (4, 1 ), + 'GTOval_ipsp' : (12,3 ), +} From 02acf3f64b8ac17d337d178a3b14f8e18e373ec4 Mon Sep 17 00:00:00 2001 From: Sandeep Sharma Date: Sat, 5 Sep 2020 20:15:13 -0600 Subject: [PATCH 18/26] started work on 2-component CASSCF --- pyscf/gto/basis/__init__.py | 1 + pyscf/vmcscf/vmcActiveActive.py | 8 +- pyscf/zmcscf/__init__.py | 42 ++ pyscf/zmcscf/gzcasci.py | 1076 +++++++++++++++++++++++++++++++ pyscf/zmcscf/zmc2step.py | 514 +++++++++++++++ 5 files changed, 1638 insertions(+), 3 deletions(-) create mode 100644 pyscf/zmcscf/__init__.py create mode 100644 pyscf/zmcscf/gzcasci.py create mode 100644 pyscf/zmcscf/zmc2step.py diff --git a/pyscf/gto/basis/__init__.py b/pyscf/gto/basis/__init__.py index 2d6a2aae42..1d219522e4 100644 --- a/pyscf/gto/basis/__init__.py +++ b/pyscf/gto/basis/__init__.py @@ -152,6 +152,7 @@ 'def2qzvppd' : 'def2-qzvppd.dat', 'def2qzvpp' : 'def2-qzvpp.dat' , 'def2qzvp' : 'def2-qzvp.dat' , + 'def2univjkfit' : 'def2-univ-jkfit.dat' , 'def2svpjfit' : 'def2-svp-jfit.dat' , 'def2svpjkfit' : 'def2-svp-jkfit.dat' , 'def2tzvpjfit' : 'def2-tzvp-jfit.dat' , diff --git a/pyscf/vmcscf/vmcActiveActive.py b/pyscf/vmcscf/vmcActiveActive.py index 64317c31c0..f36d259925 100644 --- a/pyscf/vmcscf/vmcActiveActive.py +++ b/pyscf/vmcscf/vmcActiveActive.py @@ -371,7 +371,9 @@ def localizeValence(mf, mo_coeff, method="iao"): if (method == "iao"): return iao.iao(mf.mol, mo_coeff) elif (method == "ibo"): - return ibo.PM(mf.mol, mo_coeff).kernel() + iaos = lo.iao.iao(mf.mol, mo_coeff) + return lo.ibo.ibo(mf.mol, mo_coeff, locmethod='PM', iaos=iaos).kernel() + #return ibo.PM(mf.mol, mo_coeff).kernel() #a = iao.iao(mf.mol, mo_coeff) #a = lo.vec_lowdin(a, mf.get_ovlp()) @@ -400,7 +402,7 @@ def bestDetValence(mol, lmo, occ, eri, writeToFile=True): for i in enumerate(maxLMOContributers): lmoSites[numpy.searchsorted(numpy.array(atomNumAOs), i[1])].append(i[0]) - bestDet = [0 for i in range(lmo.shape[1])] + bestDet = ['0' for i in range(lmo.shape[1])] def pair(i): return i*(i+1)//2+i for i in enumerate(occ): @@ -417,7 +419,7 @@ def pair(i): bestDet[onSiteIntegrals[i[1][0] + i[1][1] + m][0]] = 'b' bestDetStr = ' '.join(bestDet) - print('bestDet: ' + bestDetStr) + if writeToFile: fileh = open("bestDet", 'w') fileh.write('1. ' + bestDetStr + '\n') diff --git a/pyscf/zmcscf/__init__.py b/pyscf/zmcscf/__init__.py new file mode 100644 index 0000000000..3b0044a5aa --- /dev/null +++ b/pyscf/zmcscf/__init__.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python +# Copyright 2014-2018 The PySCF Developers. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from pyscf.zmcscf import zmc2step +from pyscf.zmcscf import gzcasci +from pyscf.mcscf import casci +from pyscf.mcscf import addons + + +def ZCASSCF(mf_or_mol, ncas, nelecas, ncore=None, frozen=None): + from pyscf import gto + from pyscf import scf + if isinstance(mf_or_mol, gto.Mole): + mf = scf.RHF(mf_or_mol) + else: + mf = mf_or_mol + + if isinstance(mf, scf.uhf.UHF): + mf = scf.addons.convert_to_rhf(mf) +# if getattr(mf, 'with_df', None): +# return DFCASSCF(mf, ncas, nelecas, ncore, frozen) + +# if mf.mol.symmetry: +# mc = mc1step_symm.CASSCF(mf, ncas, nelecas, ncore, frozen) +# else: + mc = zmc2step.ZCASSCF(mf, ncas, nelecas, ncore, frozen) + return mc + +RCASSCF = CASSCF + diff --git a/pyscf/zmcscf/gzcasci.py b/pyscf/zmcscf/gzcasci.py new file mode 100644 index 0000000000..29f7f868c9 --- /dev/null +++ b/pyscf/zmcscf/gzcasci.py @@ -0,0 +1,1076 @@ +#!/usr/bin/env python +# Copyright 2014-2020 The PySCF Developers. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: Qiming Sun +# + +import sys +import time +from functools import reduce +import numpy +from pyscf import lib +from pyscf.lib import logger +from pyscf import gto +from pyscf import scf +from pyscf import ao2mo +from pyscf import fci +from pyscf.mcscf import addons +from pyscf import symm +from pyscf import __config__ +import h5py + +WITH_META_LOWDIN = getattr(__config__, 'mcscf_analyze_with_meta_lowdin', True) +LARGE_CI_TOL = getattr(__config__, 'mcscf_analyze_large_ci_tol', 0.1) +PENALTY = getattr(__config__, 'mcscf_casci_CASCI_fix_spin_shift', 0.2) + +if sys.version_info < (3,): + RANGE_TYPE = list +else: + RANGE_TYPE = range + + +def h1e_for_cas(casci, mo_coeff=None, ncas=None, ncore=None): + '''CAS sapce one-electron hamiltonian + + Args: + casci : a CASSCF/CASCI object or RHF object + + Returns: + A tuple, the first is the effective one-electron hamiltonian defined in CAS space, + the second is the electronic energy from core. + ''' + if mo_coeff is None: mo_coeff = casci.mo_coeff + if ncas is None: ncas = casci.ncas + if ncore is None: ncore = casci.ncore + mo_core = mo_coeff[:,:ncore] + mo_cas = mo_coeff[:,ncore:ncore+ncas] + + hcore = (1+0j)*casci.get_hcore() + energy_core = casci.energy_nuc() + if mo_core.size == 0: + corevhf = 0 + else: + core_dm = numpy.dot(mo_core, mo_core.T) * 2 + corevhf = casci.get_veff(casci.mol, core_dm) + energy_core += numpy.einsum('ij,ji', core_dm, hcore) + energy_core += numpy.einsum('ij,ji', core_dm, corevhf) * .5 + h1eff = reduce(numpy.dot, (mo_cas.T, hcore+corevhf, mo_cas)) + return h1eff, energy_core + +def analyze(casscf, mo_coeff=None, ci=None, verbose=None, + large_ci_tol=LARGE_CI_TOL, with_meta_lowdin=WITH_META_LOWDIN, + **kwargs): + from pyscf.lo import orth + from pyscf.tools import dump_mat + from pyscf.mcscf import addons + log = logger.new_logger(casscf, verbose) + + if mo_coeff is None: mo_coeff = casscf.mo_coeff + if ci is None: ci = casscf.ci + nelecas = casscf.nelecas + ncas = casscf.ncas + ncore = casscf.ncore + nocc = ncore + ncas + mocore = mo_coeff[:,:ncore] + mocas = mo_coeff[:,ncore:nocc] + + label = casscf.mol.ao_labels() + if (isinstance(ci, (list, tuple, RANGE_TYPE)) and + not isinstance(casscf.fcisolver, addons.StateAverageFCISolver)): + log.warn('Mulitple states found in CASCI/CASSCF solver. Density ' + 'matrix of the first state is generated in .analyze() function.') + civec = ci[0] + else: + civec = ci + if getattr(casscf.fcisolver, 'make_rdm1s', None): + casdm1a, casdm1b = casscf.fcisolver.make_rdm1s(civec, ncas, nelecas) + casdm1 = casdm1a + casdm1b + dm1b = numpy.dot(mocore, mocore.T) + dm1a = dm1b + reduce(numpy.dot, (mocas, casdm1a, mocas.T)) + dm1b += reduce(numpy.dot, (mocas, casdm1b, mocas.T)) + dm1 = dm1a + dm1b + if log.verbose >= logger.DEBUG2: + log.info('alpha density matrix (on AO)') + dump_mat.dump_tri(log.stdout, dm1a, label, **kwargs) + log.info('beta density matrix (on AO)') + dump_mat.dump_tri(log.stdout, dm1b, label, **kwargs) + else: + casdm1 = casscf.fcisolver.make_rdm1(civec, ncas, nelecas) + dm1a =(numpy.dot(mocore, mocore.T) * 2 + + reduce(numpy.dot, (mocas, casdm1, mocas.T))) + dm1b = None + dm1 = dm1a + + if log.verbose >= logger.INFO: + ovlp_ao = casscf._scf.get_ovlp() + # note the last two args of ._eig for mc1step_symm + occ, ucas = casscf._eig(-casdm1, ncore, nocc) + log.info('Natural occ %s', str(-occ)) + mocas = numpy.dot(mocas, ucas) + if with_meta_lowdin: + log.info('Natural orbital (expansion on meta-Lowdin AOs) in CAS space') + orth_coeff = orth.orth_ao(casscf.mol, 'meta_lowdin', s=ovlp_ao) + mocas = reduce(numpy.dot, (orth_coeff.T, ovlp_ao, mocas)) + else: + log.info('Natural orbital (expansion on AOs) in CAS space') + dump_mat.dump_rec(log.stdout, mocas, label, start=1, **kwargs) + if log.verbose >= logger.DEBUG2: + if not casscf.natorb: + log.debug2('NOTE: mc.mo_coeff in active space is different to ' + 'the natural orbital coefficients printed in above.') + if with_meta_lowdin: + c = reduce(numpy.dot, (orth_coeff.T, ovlp_ao, mo_coeff)) + log.debug2('MCSCF orbital (expansion on meta-Lowdin AOs)') + else: + c = mo_coeff + log.debug2('MCSCF orbital (expansion on AOs)') + dump_mat.dump_rec(log.stdout, c, label, start=1, **kwargs) + + if casscf._scf.mo_coeff is not None: + addons.map2hf(casscf, casscf._scf.mo_coeff) + + if (ci is not None and + (getattr(casscf.fcisolver, 'large_ci', None) or + getattr(casscf.fcisolver, 'states_large_ci', None))): + log.info('** Largest CI components **') + if isinstance(ci, (list, tuple, RANGE_TYPE)): + if hasattr(casscf.fcisolver, 'states_large_ci'): + # defined in state_average_mix_ mcscf object + res = casscf.fcisolver.states_large_ci(ci, casscf.ncas, casscf.nelecas, + large_ci_tol, return_strs=False) + else: + res = [casscf.fcisolver.large_ci(civec, casscf.ncas, casscf.nelecas, + large_ci_tol, return_strs=False) + for civec in ci] + for i, civec in enumerate(ci): + log.info(' [alpha occ-orbitals] [beta occ-orbitals] state %-3d CI coefficient', i) + for c,ia,ib in res[i]: + log.info(' %-20s %-30s %.12f', ia, ib, c) + else: + log.info(' [alpha occ-orbitals] [beta occ-orbitals] CI coefficient') + res = casscf.fcisolver.large_ci(ci, casscf.ncas, casscf.nelecas, + large_ci_tol, return_strs=False) + for c,ia,ib in res: + log.info(' %-20s %-30s %.12f', ia, ib, c) + + if with_meta_lowdin: + casscf._scf.mulliken_meta(casscf.mol, dm1, s=ovlp_ao, verbose=log) + else: + casscf._scf.mulliken_pop(casscf.mol, dm1, s=ovlp_ao, verbose=log) + return dm1a, dm1b + +def get_fock(mc, mo_coeff=None, ci=None, eris=None, casdm1=None, verbose=None): + r''' + Effective one-electron Fock matrix in AO representation + f = \sum_{pq} E_{pq} F_{pq} + F_{pq} = h_{pq} + \sum_{rs} [(pq|rs)-(ps|rq)] DM_{sr} + + Ref. + Theor. Chim. Acta., 91, 31 + Chem. Phys. 48, 157 + + For state-average CASCI/CASSCF object, the effective fock matrix is based + on the state-average density matrix. To obtain Fock matrix of a specific + state in the state-average calculations, you can 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. + casdm1 (ndarray): 1-particle density matrix in active space. 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 the + effective Fock matrix based on the state-average density matrix. + To obtain the effective Fock matrix for one particular state, you + can assign the density matrix of that state to the kwarg casdm1. + + Returns: + Fock matrix + ''' + + if ci is None: ci = mc.ci + if mo_coeff is None: mo_coeff = mc.mo_coeff + nmo = mo_coeff.shape[1] + ncore = mc.ncore + ncas = mc.ncas + nocc = ncore + ncas + nelecas = mc.nelecas + + if casdm1 is None: + casdm1 = mc.fcisolver.make_rdm1(ci, ncas, nelecas) + if getattr(eris, 'ppaa', None) is not None: + vj = numpy.empty((nmo,nmo)) + vk = numpy.empty((nmo,nmo)) + for i in range(nmo): + vj[i] = numpy.einsum('ij,qij->q', casdm1, eris.ppaa[i]) + vk[i] = numpy.einsum('ij,iqj->q', casdm1, eris.papa[i]) + mo_inv = numpy.dot(mo_coeff.T, mc._scf.get_ovlp()) + fock =(mc.get_hcore() + + reduce(numpy.dot, (mo_inv.T, eris.vhf_c+vj-vk*.5, mo_inv))) + else: + dm_core = numpy.dot(mo_coeff[:,:ncore]*2, mo_coeff[:,:ncore].T) + mocas = mo_coeff[:,ncore:nocc] + dm = dm_core + reduce(numpy.dot, (mocas, casdm1, mocas.T)) + vj, vk = mc._scf.get_jk(mc.mol, dm) + fock = mc.get_hcore() + vj-vk*.5 + return fock + +def cas_natorb(mc, mo_coeff=None, ci=None, eris=None, sort=False, + casdm1=None, verbose=None, with_meta_lowdin=WITH_META_LOWDIN): + '''Transform active orbitals to natrual orbitals, and update the CI wfn + accordingly + + Args: + mc : a CASSCF/CASCI object or RHF object + + Kwargs: + sort : bool + Sort natural orbitals wrt the occupancy. + + Returns: + A tuple, the first item is natural orbitals, the second is updated CI + coefficients, the third is the natural occupancy associated to the + natural orbitals. + ''' + from pyscf.lo import orth + from pyscf.tools import dump_mat + from pyscf.tools.mo_mapping import mo_1to1map + if mo_coeff is None: mo_coeff = mc.mo_coeff + if ci is None: ci = mc.ci + log = logger.new_logger(mc, verbose) + ncore = mc.ncore + ncas = mc.ncas + nocc = ncore + ncas + nelecas = mc.nelecas + if casdm1 is None: + casdm1 = mc.fcisolver.make_rdm1(ci, ncas, nelecas) + # orbital symmetry is reserved in this _eig call + occ, ucas = mc._eig(-casdm1, ncore, nocc) + if sort: + casorb_idx = numpy.argsort(occ.round(9), kind='mergesort') + occ = occ[casorb_idx] + ucas = ucas[:,casorb_idx] + + occ = -occ + mo_occ = numpy.zeros(mo_coeff.shape[1]) + mo_occ[:ncore] = 2 + mo_occ[ncore:nocc] = occ + + mo_coeff1 = mo_coeff.copy() + mo_coeff1[:,ncore:nocc] = numpy.dot(mo_coeff[:,ncore:nocc], ucas) + if getattr(mo_coeff, 'orbsym', None) is not None: + orbsym = numpy.copy(mo_coeff.orbsym) + if sort: + orbsym[ncore:nocc] = orbsym[ncore:nocc][casorb_idx] + mo_coeff1 = lib.tag_array(mo_coeff1, orbsym=orbsym) + + fcivec = None + if getattr(mc.fcisolver, 'transform_ci_for_orbital_rotation', None): + if isinstance(ci, numpy.ndarray): + fcivec = mc.fcisolver.transform_ci_for_orbital_rotation(ci, ncas, nelecas, ucas) + elif (isinstance(ci, (list, tuple)) and + all(isinstance(x[0], numpy.ndarray) for x in ci)): + fcivec = [mc.fcisolver.transform_ci_for_orbital_rotation(x, ncas, nelecas, ucas) + for x in ci] + elif getattr(mc.fcisolver, 'states_transform_ci_for_orbital_rotation', None): + fcivec = mc.fcisolver.states_transform_ci_for_orbital_rotation(ci, ncas, nelecas, ucas) + + if fcivec is None: + log.info('FCI vector not available, call CASCI to update wavefunction') + mocas = mo_coeff1[:,ncore:nocc] + hcore = mc.get_hcore() + dm_core = numpy.dot(mo_coeff1[:,:ncore]*2, mo_coeff1[:,:ncore].T) + ecore = mc.energy_nuc() + ecore+= numpy.einsum('ij,ji', hcore, dm_core) + h1eff = reduce(numpy.dot, (mocas.T, hcore, mocas)) + if getattr(eris, 'ppaa', None) is not None: + ecore += eris.vhf_c[:ncore,:ncore].trace() + h1eff += reduce(numpy.dot, (ucas.T, eris.vhf_c[ncore:nocc,ncore:nocc], ucas)) + aaaa = ao2mo.restore(4, eris.ppaa[ncore:nocc,ncore:nocc,:,:], ncas) + aaaa = ao2mo.incore.full(aaaa, ucas) + else: + if getattr(mc, 'with_df', None): + raise NotImplementedError('cas_natorb for DFCASCI/DFCASSCF') + corevhf = mc.get_veff(mc.mol, dm_core) + ecore += numpy.einsum('ij,ji', dm_core, corevhf) * .5 + h1eff += reduce(numpy.dot, (mocas.T, corevhf, mocas)) + aaaa = ao2mo.kernel(mc.mol, mocas) + + # See label_symmetry_ function in casci_symm.py which initialize the + # orbital symmetry information in fcisolver. This orbital symmetry + # labels should be reordered to match the sorted active space orbitals. + if sort and getattr(mo_coeff1, 'orbsym', None) is not None: + mc.fcisolver.orbsym = mo_coeff1.orbsym[ncore:nocc] + + max_memory = max(400, mc.max_memory-lib.current_memory()[0]) + e, fcivec = mc.fcisolver.kernel(h1eff, aaaa, ncas, nelecas, ecore=ecore, + max_memory=max_memory, verbose=log) + log.debug('In Natural orbital, CASCI energy = %s', e) + + if log.verbose >= logger.INFO: + ovlp_ao = mc._scf.get_ovlp() + # where_natorb gives the new locations of the natural orbitals + where_natorb = mo_1to1map(ucas) + log.debug('where_natorb %s', str(where_natorb)) + log.info('Natural occ %s', str(occ)) + if with_meta_lowdin: + log.info('Natural orbital (expansion on meta-Lowdin AOs) in CAS space') + label = mc.mol.ao_labels() + orth_coeff = orth.orth_ao(mc.mol, 'meta_lowdin', s=ovlp_ao) + mo_cas = reduce(numpy.dot, (orth_coeff.T, ovlp_ao, mo_coeff1[:,ncore:nocc])) + else: + log.info('Natural orbital (expansion on AOs) in CAS space') + label = mc.mol.ao_labels() + mo_cas = mo_coeff1[:,ncore:nocc] + dump_mat.dump_rec(log.stdout, mo_cas, label, start=1) + + if mc._scf.mo_coeff is not None: + s = reduce(numpy.dot, (mo_coeff1[:,ncore:nocc].T, + mc._scf.get_ovlp(), mc._scf.mo_coeff)) + idx = numpy.argwhere(abs(s)>.4) + for i,j in idx: + log.info(' %d %d %12.8f', + ncore+i+1, j+1, s[i,j]) + return mo_coeff1, fcivec, mo_occ + +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 effecitive Fock matrix and + update CI coefficients accordingly. + + 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 + the orbital energy (diagonal part of the effective Fock matrix) + within each subspace (core, active, external). If point group + symmetry is not available in the system, orbitals are always + sorted. When point group symmetry is available, sort=False will + preserve the symmetry label of input orbitals and only sort the + orbitals in each symmetry sector. sort=True will reorder all + orbitals over all symmetry sectors in each subspace and the + symmetry labels may be changed. + cas_natorb (bool): Whether to transform active orbitals to natual + orbitals. If enabled, the output orbitals in active space are + transformed to natural orbitals and CI coefficients are updated + accordingly. + 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, RANGE_TYPE)) 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 getattr(mo_coeff, 'orbsym', None) is not None: + 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 getattr(mo_coeff, 'orbsym', None) is not None: + mo_coeff1 = lib.tag_array(mo_coeff1, orbsym=orbsym) + + if log.verbose >= logger.DEBUG: + for i in range(nmo): + log.debug('i = %d = %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(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 + import pdb + pdb.set_trace() + max_memory = max(400, casci.max_memory-lib.current_memory()[0]) + e_tot, fcivec = casci.fcisolver.kernel(h1eff, eri_cas, ncas, (nelecas,0), + 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 as_scanner(mc): + '''Generating a scanner for CASCI PES. + + The returned solver is a function. This function requires one argument + "mol" as input and returns total CASCI energy. + + The solver will automatically use the results of last calculation as the + initial guess of the new calculation. All parameters of MCSCF object + are automatically applied in the solver. + + Note scanner has side effects. It may change many underlying objects + (_scf, with_df, with_x2c, ...) during calculation. + + Examples: + + >>> from pyscf import gto, scf, mcscf + >>> mf = scf.RHF(gto.Mole().set(verbose=0)) + >>> mc_scanner = mcscf.CASCI(mf, 4, 4).as_scanner() + >>> mc_scanner(gto.M(atom='N 0 0 0; N 0 0 1.1')) + >>> mc_scanner(gto.M(atom='N 0 0 0; N 0 0 1.5')) + ''' + if isinstance(mc, lib.SinglePointScanner): + return mc + + logger.info(mc, 'Create scanner for %s', mc.__class__) + + class CASCI_Scanner(mc.__class__, lib.SinglePointScanner): + def __init__(self, mc): + self.__dict__.update(mc.__dict__) + self._scf = mc._scf.as_scanner() + def __call__(self, mol_or_geom, mo_coeff=None, ci0=None): + if isinstance(mol_or_geom, gto.Mole): + mol = mol_or_geom + else: + mol = self.mol.set_geom_(mol_or_geom, inplace=False) + + # These properties can be updated when calling mf_scanner(mol) if + # they are shared with mc._scf. In certain scenario the properties + # may be created for mc separately, e.g. when mcscf.approx_hessian is + # called. For safety, the code below explicitly resets these + # properties. + for key in ('with_df', 'with_x2c', 'with_solvent', 'with_dftd3'): + sub_mod = getattr(self, key, None) + if sub_mod: + sub_mod.reset(mol) + + if mo_coeff is None: + mf_scanner = self._scf + mf_scanner(mol) + mo_coeff = mf_scanner.mo_coeff + if ci0 is None: + ci0 = self.ci + self.mol = mol + e_tot = self.kernel(mo_coeff, ci0)[0] + return e_tot + return CASCI_Scanner(mc) + + +class GZCASCI(lib.StreamObject): + '''CASCI + + Args: + mf_or_mol : SCF object or Mole object + SCF or Mole to define the problem size. + ncas : int + Number of active orbitals. + nelecas : int + Number of electrons in active space. + + Kwargs: + ncore : int + Number of core orbitals. If not presented, this + parameter can be automatically determined. + + Attributes: + verbose : int + Print level. Default value equals to :class:`Mole.verbose`. + max_memory : float or int + Allowed memory in MB. Default value equals to :class:`Mole.max_memory`. + ncas : int + Active space size. + nelecas : int + Active electrons + ncore : int + Core electron number. + natorb : bool + Whether to transform natural orbital in active space. Be cautious + of this parameter when CASCI/CASSCF are combined with DMRG solver + or selected CI solver because DMRG and selected CI are not invariant + to the rotation in active space. + False by default. + canonicalization : bool + Whether to canonicalize orbitals. Note that canonicalization does + not change the orbitals in active space by default. It only + diagonalizes core and external space of the general Fock matirx. + To get the natural orbitals in active space, attribute natorb + need to be enabled. + True by default. + sorting_mo_energy : bool + Whether to sort the orbitals based on the diagonal elements of the + general Fock matrix. Default is False. + fcisolver : an instance of :class:`FCISolver` + The pyscf.fci module provides several FCISolver for different scenario. Generally, + fci.direct_spin1.FCISolver can be used for all RHF-CASSCF. However, a proper FCISolver + can provide better performance and better numerical stability. One can either use + :func:`fci.solver` function to pick the FCISolver by the program or manually assigen + the FCISolver to this attribute, e.g. + + >>> from pyscf import fci + >>> mc = mcscf.CASSCF(mf, 4, 4) + >>> mc.fcisolver = fci.solver(mol, singlet=True) + >>> mc.fcisolver = fci.direct_spin1.FCISolver(mol) + + You can control FCISolver by setting e.g.:: + + >>> mc.fcisolver.max_cycle = 30 + >>> mc.fcisolver.conv_tol = 1e-7 + + For more details of the parameter for FCISolver, See :mod:`fci`. + + Saved results + + e_tot : float + Total MCSCF energy (electronic energy plus nuclear repulsion) + e_cas : float + CAS space FCI energy + ci : ndarray + CAS space FCI coefficients + mo_coeff : ndarray + When canonicalization is specified, the orbitals are canonical + orbitals which make the general Fock matrix (Fock operator on top + of MCSCF 1-particle density matrix) diagonalized within each + subspace (core, active, external). If natorb (natural orbitals in + active space) is specified, the active segment of the mo_coeff is + natural orbitls. + mo_energy : ndarray + Diagonal elements of general Fock matrix (in mo_coeff + representation). + + Examples: + + >>> from pyscf import gto, scf, mcscf + >>> mol = gto.M(atom='N 0 0 0; N 0 0 1', basis='ccpvdz', verbose=0) + >>> mf = scf.RHF(mol) + >>> mf.scf() + >>> mc = mcscf.CASCI(mf, 6, 6) + >>> mc.kernel()[0] + -108.980200816243354 + ''' + + natorb = getattr(__config__, 'zmcscf_gzcasci_CASCI_natorb', False) + canonicalization = getattr(__config__, 'zmcscf_gzcasci_CASCI_canonicalization', True) + sorting_mo_energy = getattr(__config__, 'zmcscf_gzcasci_CASCI_sorting_mo_energy', False) + + def __init__(self, mf_or_mol, ncas, nelecas, ncore=None): + if isinstance(mf_or_mol, gto.Mole): + mf = scf.GHF(mf_or_mol) + elif isinstance(mf_or_mol, scf.ghf.GHF): + mf = mf_or_mol + else: + print("we need the mean field object to be of GHF type") + + mol = mf.mol + self.mol = mol + self._scf = mf + self.verbose = mol.verbose + self.stdout = mol.stdout + self.max_memory = mf.max_memory + self.ncas = ncas + self.nelecas = nelecas + self.ncore = ncore + self.fcisolver = fci.solver(mol, symm=False) +# CI solver parameters are set in fcisolver object + self.fcisolver.max_cycle = getattr(__config__, + 'mcscf_casci_CASCI_fcisolver_max_cycle', 200) + self.fcisolver.conv_tol = getattr(__config__, + 'mcscf_casci_CASCI_fcisolver_conv_tol', 1e-8) + +################################################## +# don't modify the following attributes, they are not input options + self.e_tot = 0 + self.e_cas = None + self.ci = None + self.mo_coeff = (1.+0j)*mf.mo_coeff #complex orbitals + self.mo_energy = mf.mo_energy + self.converged = False + + keys = set(('natorb', 'canonicalization', 'sorting_mo_energy')) + self._keys = set(self.__dict__.keys()).union(keys) + + @property + def ncore(self): + if self._ncore is None: + ncorelec = self.mol.nelectron - self.nelecas + return ncorelec + else: + return self._ncore + @ncore.setter + def ncore(self, x): + assert(x is None or isinstance(x, (int))) + self._ncore = x + + 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, %do), ncore = %d, nvir = %d', \ + self.nelecas, 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 at SCF level but not applied to the CASCI object. +The SCF solvent model will not be applied to the current CASCI calculation. +To enable the solvent model for CASCI, the following code needs to be called + from pyscf import solvent + mc = mcscf.CASCI(...) + mc = solvent.ddCOSMO(mc) +''', + self._scf.with_solvent.__class__) + return self + + def reset(self, mol=None): + if mol is not None: + self.mol = mol + self.fcisolver.mol = mol + self._scf.reset(mol) + return self + + def energy_nuc(self): + return self._scf.energy_nuc() + + def get_hcore(self, mol=None): + return self._scf.get_hcore(mol) + + def get_veff(self, mol=None, dm=None, hermi=1): + if mol is None: mol = self.mol + if dm is None: + mocore = self.mo_coeff[:,:self.ncore] + dm = numpy.dot(mocore, mocore.T) * 2 + + nao = dm.shape[0]//2 + vj, vk = 0.*dm, 0.*dm + eri_ao = (1.+0j)*mol.intor('int2e_sph', aosym='s1') + vj[:nao,:nao] = lib.einsum('pqrs,rs->pq', eri_ao, (dm[:nao,:nao]+dm[nao:,nao:])) + vj[nao:,nao:] = 1.*vj[:nao,:nao] + + vk[:nao,:nao] = lib.einsum('pqrs,rs->pq', eri_ao, dm[:nao,:nao]) + vk[:nao,nao:] = lib.einsum('pqrs,rs->pq', eri_ao, dm[nao:,:nao]) + vk[nao:,nao:] = lib.einsum('pqrs,rs->pq', eri_ao, dm[nao:,nao:]) + vk[nao:,:nao] = lib.einsum('pqrs,rs->pq', eri_ao, dm[:nao,nao:]) + +# don't call self._scf.get_veff because _scf might be DFT object + + #vj, vk = self._scf.get_jk(mol, dm, hermi=hermi) + return vj - vk * .5 + + def _eig(self, h, *args): + return scf.hf.eig(h, None) + + def get_h2cas(self, mo_coeff=None): + '''Computing active space two-particle Hamiltonian. + + Note It is different to get_h2eff when df.approx_hessian is applied, + in which get_h2eff function returns the DF integrals while get_h2cas + returns the regular 2-electron integrals. + ''' + return self.ao2mo(mo_coeff) + def get_h2eff(self, mo_coeff=None): + '''Computing active space two-particle Hamiltonian. + + Note It is different to get_h2cas when df.approx_hessian is applied. + in which get_h2eff function returns the DF integrals while get_h2cas + returns the regular 2-electron integrals. + ''' + return self.ao2mo(mo_coeff) + + ####THIS SHOULD BE CHECKED### + def ao2mo(self, mo_coeff=None): + ncore = self.ncore + ncas = self.ncas + nocc = ncore + ncas + nao = self.mo_coeff.shape[0]//2 + if mo_coeff is None: + ncore = self.ncore + mo_coeffa = self.mo_coeff[:nao,ncore:nocc] + mo_coeffb = self.mo_coeff[nao:,ncore:nocc] + + elif mo_coeff.shape[1] != ncas: + mo_coeffa = mo_coeff[:nao,ncore:nocc] + mo_coeffb = mo_coeff[nao:,ncore:nocc] + + eri = numpy.zeros((ncas,ncas,ncas,ncas), dtype=numpy.complex) + eri_ao = (1.+0j)*mol.intor('int2e_sph', aosym='s1') + ##the mo eri will need to be constructed in 4 steps + if self._scf._eri is not None: + eri += lib.einsum('pqrs,pi,qj,rk,sl->ijkl', eri_ao.reshape([nao]*4), + mo_coeffa.conj(), mo_coeffa, mo_coeffa.conj(), mo_coeffa) + eri += lib.einsum('pqrs,pi,qj,rk,sl->ijkl', eri_ao.reshape([nao]*4), + mo_coeffb.conj(), mo_coeffb, mo_coeffa.conj(), mo_coeffa) + eri += lib.einsum('pqrs,pi,qj,rk,sl->ijkl', eri_ao.reshape([nao]*4), + mo_coeffa.conj(), mo_coeffa, mo_coeffb.conj(), mo_coeffb) + eri += lib.einsum('pqrs,pi,qj,rk,sl->ijkl', eri_ao.reshape([nao]*4), + mo_coeffb.conj(), mo_coeffb, mo_coeffb.conj(), mo_coeffb) + else: + eri = ao2mo.full(self.mol, mo_coeff, verbose=self.verbose, + max_memory=self.max_memory) + import pdb + pdb.set_trace() + return eri + + get_h1cas = h1e_for_cas = h1e_for_cas + + def get_h1eff(self, mo_coeff=None, ncas=None, ncore=None): + return self.h1e_for_cas(mo_coeff, ncas, ncore) + get_h1eff.__doc__ = h1e_for_cas.__doc__ + + def casci(self, mo_coeff=None, ci0=None, verbose=None): + return self.kernel(mo_coeff, ci0, verbose) + def kernel(self, mo_coeff=None, ci0=None, verbose=None): + ''' + Returns: + Five elements, they are + total energy, + active space CI energy, + the active space FCI wavefunction coefficients or DMRG wavefunction ID, + the MCSCF canonical orbital coefficients, + the MCSCF canonical orbital coefficients. + + They are attributes of mcscf object, which can be accessed by + .e_tot, .e_cas, .ci, .mo_coeff, .mo_energy + ''' + if mo_coeff is None: + mo_coeff = self.mo_coeff + else: + self.mo_coeff = mo_coeff + if ci0 is None: + ci0 = self.ci + log = logger.new_logger(self, verbose) + + if self.verbose >= logger.WARN: + self.check_sanity() + self.dump_flags(log) + + self.e_tot, self.e_cas, self.ci = \ + kernel(self, mo_coeff, ci0=ci0, verbose=log) + + if self.canonicalization: + self.canonicalize_(mo_coeff, self.ci, + sort=self.sorting_mo_energy, + cas_natorb=self.natorb, verbose=log) + + if getattr(self.fcisolver, 'converged', None) is not None: + self.converged = numpy.all(self.fcisolver.converged) + if self.converged: + log.info('CASCI converged') + else: + log.info('CASCI not converged') + else: + self.converged = True + self._finalize() + return self.e_tot, self.e_cas, self.ci, self.mo_coeff, self.mo_energy + + def _finalize(self): + log = logger.Logger(self.stdout, self.verbose) + if log.verbose >= logger.NOTE and getattr(self.fcisolver, 'spin_square', None): + if isinstance(self.e_cas, (float, numpy.number)): + ss = self.fcisolver.spin_square(self.ci, self.ncas, self.nelecas) + log.note('CASCI E = %.15g E(CI) = %.15g S^2 = %.7f', + self.e_tot, self.e_cas, ss[0]) + else: + for i, e in enumerate(self.e_cas): + ss = self.fcisolver.spin_square(self.ci[i], self.ncas, self.nelecas) + log.note('CASCI state %d E = %.15g E(CI) = %.15g S^2 = %.7f', + i, self.e_tot[i], e, ss[0]) + else: + if isinstance(self.e_cas, (float, numpy.number)): + log.note('CASCI E = %.15g E(CI) = %.15g', self.e_tot, self.e_cas) + else: + for i, e in enumerate(self.e_cas): + log.note('CASCI state %d E = %.15g E(CI) = %.15g', + i, self.e_tot[i], e) + return self + + as_scanner = as_scanner + + @lib.with_doc(cas_natorb.__doc__) + def cas_natorb(self, mo_coeff=None, ci=None, eris=None, sort=False, + casdm1=None, verbose=None, with_meta_lowdin=WITH_META_LOWDIN): + return cas_natorb(self, mo_coeff, ci, eris, sort, casdm1, verbose, + with_meta_lowdin) + @lib.with_doc(cas_natorb.__doc__) + def cas_natorb_(self, mo_coeff=None, ci=None, eris=None, sort=False, + casdm1=None, verbose=None, with_meta_lowdin=WITH_META_LOWDIN): + self.mo_coeff, self.ci, occ = cas_natorb(self, mo_coeff, ci, eris, + sort, casdm1, verbose) + return self.mo_coeff, self.ci, occ + + def get_fock(self, mo_coeff=None, ci=None, eris=None, casdm1=None, + verbose=None): + return get_fock(self, mo_coeff, ci, eris, casdm1, verbose) + + canonicalize = canonicalize + @lib.with_doc(canonicalize.__doc__) + def canonicalize_(self, mo_coeff=None, ci=None, eris=None, sort=False, + cas_natorb=False, casdm1=None, verbose=None, + with_meta_lowdin=WITH_META_LOWDIN): + self.mo_coeff, ci, self.mo_energy = \ + canonicalize(self, mo_coeff, ci, eris, + sort, cas_natorb, casdm1, verbose, with_meta_lowdin) + if cas_natorb: # When active space is changed, the ci solution needs to be updated + self.ci = ci + return self.mo_coeff, ci, self.mo_energy + + analyze = analyze + + @lib.with_doc(addons.sort_mo.__doc__) + def sort_mo(self, caslst, mo_coeff=None, base=1): + if mo_coeff is None: mo_coeff = self.mo_coeff + return addons.sort_mo(self, mo_coeff, caslst, base) + + @lib.with_doc(addons.state_average.__doc__) + def state_average_(self, weights=(0.5,0.5)): + addons.state_average_(self, weights) + return self + @lib.with_doc(addons.state_average.__doc__) + def state_average(self, weights=(0.5,0.5)): + return addons.state_average(self, weights) + + @lib.with_doc(addons.state_specific_.__doc__) + def state_specific_(self, state=1): + addons.state_specific(self, state) + return self + + def make_rdm1(self, mo_coeff=None, ci=None, ncas=None, nelecas=None, + ncore=None, **kwargs): + '''One-particle density matrix in AO representation + ''' + if mo_coeff is None: mo_coeff = self.mo_coeff + if ci is None: ci = self.ci + if ncas is None: ncas = self.ncas + if nelecas is None: nelecas = self.nelecas + if ncore is None: ncore = self.ncore + + casdm1 = self.fcisolver.make_rdm1(ci, ncas, nelecas) + mocore = mo_coeff[:,:ncore] + mocas = mo_coeff[:,ncore:ncore+ncas] + dm1 = numpy.dot(mocore, mocore.T) * 2 + dm1 = dm1 + reduce(numpy.dot, (mocas, casdm1, mocas.T)) + return dm1 + + + def density_fit(self, auxbasis=None, with_df=None): + from pyscf.mcscf import df + return df.density_fit(self, auxbasis, with_df) + + #####THIS NEEDS TO BE CHANGED TO X2C### + def sfx2c1e(self): + from pyscf.x2c import sfx2c1e + self._scf = sfx2c1e.sfx2c1e(self._scf).run() + self.mo_coeff = self._scf.mo_coeff + self.mo_energy = self._scf.mo_energy + return self + x2c = x2c1e = sfx2c1e + + def nuc_grad_method(self): + from pyscf.grad import casci + return casci.Gradients(self) + + + +if __name__ == '__main__': + from pyscf import mcscf + mol = gto.Mole() + mol.verbose = 4 + mol.output = None#"out_h2o" + mol.atom = [ + ['O', ( 0., 0. , 0. )], + ['H', ( 0., -0.757, 0.587)], + ['H', ( 0., 0.757 , 0.587)],] + + mol.basis = {'H': 'sto-3g', + 'O': '6-31g',} + mol.build() + + m = scf.GHF(mol) + ehf = m.scf() + mc = GZCASCI(m, 8, 4) + from pyscf.shciscf import shci + mc.fcisolver = shci.SHCI(mol, maxM=1.e-3) + mc.natorb = 1 + emc = mc.kernel()[0] + print(ehf, emc, emc-ehf) + #-75.9577817425 -75.9624554777 -0.00467373522233 + print(emc+75.9624554777) + +# mc = CASCI(m, 4, (3,1)) +# #mc.fcisolver = fci.direct_spin1 +# mc.fcisolver = fci.solver(mol, False) +# emc = mc.casci()[0] +# print(emc - -75.439016172976) +# +# mol = gto.Mole() +# mol.verbose = 0 +# mol.output = "out_casci" +# mol.atom = [ +# ["C", (-0.65830719, 0.61123287, -0.00800148)], +# ["C", ( 0.73685281, 0.61123287, -0.00800148)], +# ["C", ( 1.43439081, 1.81898387, -0.00800148)], +# ["C", ( 0.73673681, 3.02749287, -0.00920048)], +# ["C", (-0.65808819, 3.02741487, -0.00967948)], +# ["C", (-1.35568919, 1.81920887, -0.00868348)], +# ["H", (-1.20806619, -0.34108413, -0.00755148)], +# ["H", ( 1.28636081, -0.34128013, -0.00668648)], +# ["H", ( 2.53407081, 1.81906387, -0.00736748)], +# ["H", ( 1.28693681, 3.97963587, -0.00925948)], +# ["H", (-1.20821019, 3.97969587, -0.01063248)], +# ["H", (-2.45529319, 1.81939187, -0.00886348)],] +# +# mol.basis = {'H': 'sto-3g', +# 'C': 'sto-3g',} +# mol.build() +# +# m = scf.RHF(mol) +# ehf = m.scf() +# mc = CASCI(m, 9, 8) +# mc.fcisolver = fci.solver(mol) +# emc = mc.casci()[0] +# print(ehf, emc, emc-ehf) +# print(emc - -227.948912536) +# +# mc = CASCI(m, 9, (5,3)) +# #mc.fcisolver = fci.direct_spin1 +# mc.fcisolver = fci.solver(mol, False) +# mc.fcisolver.nroots = 3 +# emc = mc.casci()[0] +# print(emc[0] - -227.7674519720) diff --git a/pyscf/zmcscf/zmc2step.py b/pyscf/zmcscf/zmc2step.py new file mode 100644 index 0000000000..3654efd19c --- /dev/null +++ b/pyscf/zmcscf/zmc2step.py @@ -0,0 +1,514 @@ +#!/usr/bin/env python +# Copyright 2014-2018 The PySCF Developers. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: Qiming Sun +# + +import copy +import time +from functools import reduce +import numpy +import pyscf.lib.logger as logger +from pyscf.mcscf import mc1step +from pyscf.mcscf import casci +from pyscf import __config__ +from pyscf.mcscf.casci import get_fock, cas_natorb, canonicalize +from pyscf.mcscf import mc_ao2mo +from pyscf.mcscf import chkfile + +def kernel(casscf, mo_coeff, tol=1e-7, conv_tol_grad=None, + ci0=None, callback=None, verbose=None, dump_chk=True): + if verbose is None: + verbose = casscf.verbose + if callback is None: + callback = casscf.callback + + log = logger.Logger(casscf.stdout, verbose) + cput0 = (time.clock(), time.time()) + log.debug('Start 2-step ZCASSCF') + + mo = mo_coeff + nmo = mo.shape[1] + ncore = casscf.ncore + ncas = casscf.ncas + nocc = ncore + ncas + import pdb + pdb.set_trace() + eris = casscf.ao2mo(mo) + e_tot, e_cas, fcivec = casscf.casci(mo, ci0, eris, log, locals()) + + if ncas == nmo and not casscf.internal_rotation: + if casscf.canonicalization: + log.debug('CASSCF canonicalization') + mo, fcivec, mo_energy = casscf.canonicalize(mo, fcivec, eris, + casscf.sorting_mo_energy, + casscf.natorb, verbose=log) + else: + mo_energy = None + return True, e_tot, e_cas, fcivec, mo, mo_energy + + if conv_tol_grad is None: + conv_tol_grad = numpy.sqrt(tol) + logger.info(casscf, 'Set conv_tol_grad to %g', conv_tol_grad) + conv_tol_ddm = conv_tol_grad * 3 + conv = False + de, elast = e_tot, e_tot + totmicro = totinner = 0 + casdm1 = 0 + r0 = None + + t2m = t1m = log.timer('Initializing 2-step CASSCF', *cput0) + imacro = 0 + while not conv and imacro < casscf.max_cycle_macro: + imacro += 1 + njk = 0 + t3m = t2m + casdm1_old = casdm1 + casdm1, casdm2 = casscf.fcisolver.make_rdm12(fcivec, ncas, casscf.nelecas) + norm_ddm = numpy.linalg.norm(casdm1 - casdm1_old) + t3m = log.timer('update CAS DM', *t3m) + max_cycle_micro = 1 # casscf.micro_cycle_scheduler(locals()) + + casscf.optimizeOrbs(mo, lambda:casdm1, lambda:casdm2, eris, r0, conv_tol_grad*0.3, log) + for imicro in range(max_cycle_micro): + rota = casscf.rotate_orb_cc(mo, lambda:fcivec, lambda:casdm1, lambda:casdm2, + eris, r0, conv_tol_grad*.3, max_stepsize, log) + u, g_orb, njk1, r0 = next(rota) + rota.close() + njk += njk1 + norm_t = numpy.linalg.norm(u-numpy.eye(nmo)) + norm_gorb = numpy.linalg.norm(g_orb) + if imicro == 0: + norm_gorb0 = norm_gorb + de = numpy.dot(casscf.pack_uniq_var(u), g_orb) + t3m = log.timer('orbital rotation', *t3m) + + eris = None + u = u.copy() + g_orb = g_orb.copy() + mo = casscf.rotate_mo(mo, u, log) + eris = casscf.ao2mo(mo) + t3m = log.timer('update eri', *t3m) + + log.debug('micro %d ~dE=%5.3g |u-1|=%5.3g |g[o]|=%5.3g |dm1|=%5.3g', + imicro, de, norm_t, norm_gorb, norm_ddm) + + if callable(callback): + callback(locals()) + + t2m = log.timer('micro iter %d'%imicro, *t2m) + if norm_t < 1e-4 or abs(de) < tol*.4 or norm_gorb < conv_tol_grad*.2: + break + + totinner += njk + totmicro += imicro + 1 + + e_tot, e_cas, fcivec = casscf.casci(mo, fcivec, eris, log, locals()) + log.timer('CASCI solver', *t3m) + t2m = t1m = log.timer('macro iter %d'%imacro, *t1m) + + de, elast = e_tot - elast, e_tot + if (abs(de) < tol and + norm_gorb < conv_tol_grad and norm_ddm < conv_tol_ddm): + conv = True + else: + elast = e_tot + + if dump_chk: + casscf.dump_chk(locals()) + + if callable(callback): + callback(locals()) + + if conv: + log.info('2-step CASSCF converged in %d macro (%d JK %d micro) steps', + imacro, totinner, totmicro) + else: + log.info('2-step CASSCF not converged, %d macro (%d JK %d micro) steps', + imacro, totinner, totmicro) + + if casscf.canonicalization: + log.info('CASSCF canonicalization') + mo, fcivec, mo_energy = \ + casscf.canonicalize(mo, fcivec, eris, casscf.sorting_mo_energy, + casscf.natorb, casdm1, log) + if casscf.natorb and dump_chk: # dump_chk may save casdm1 + occ, ucas = casscf._eig(-casdm1, ncore, nocc) + casdm1 = numpy.diag(-occ) + + if dump_chk: + casscf.dump_chk(locals()) + + log.timer('2-step CASSCF', *cput0) + return conv, e_tot, e_cas, fcivec, mo, mo_energy + + +class ZCASSCF(casci.CASCI): + __doc__ = casci.CASCI.__doc__ + '''CASSCF + + Extra attributes for CASSCF: + + conv_tol : float + Converge threshold. Default is 1e-7 + conv_tol_grad : float + Converge threshold for CI gradients and orbital rotation gradients. + Default is 1e-4 + max_cycle_macro : int + Max number of macro iterations. Default is 50. + + bb_conv_tol : float, for Barzelai-Browein (BB) solver. + converge threshold for BB solver. Default is 1e-6. + bb_max_cycle : float, for BB solver. + Max number of iterations allowd in BB solver. Default is 1000. + bb_gauss_noise : float, for BB solver. Default is 0.1 + Add some random noise before starting a new BB iteration + + internal_rotation: bool. + if the CI solver is not FCI then active-active rotations are not redundant. + Default(True) + chkfile : str + Checkpoint file to save the intermediate orbitals during the CASSCF optimization. + Default is the checkpoint file of mean field object. + callback : function(envs_dict) => None + callback function takes one dict as the argument which is + generated by the builtin function :func:`locals`, so that the + callback function can access all local variables in the current + envrionment. + + Saved results + + e_tot : float + Total MCSCF energy (electronic energy plus nuclear repulsion) + e_cas : float + CAS space FCI energy + ci : ndarray + CAS space FCI coefficients + mo_coeff : ndarray (MxM, but the number of active variables is BB are just first N(=ncore+nact) columns) + Optimized CASSCF orbitals coefficients. When canonicalization is + specified, the returned orbitals make the general Fock matrix + (Fock operator on top of MCSCF 1-particle density matrix) + diagonalized within each subspace (core, active, external). + If natorb (natural orbitals in active space) is specified, + the active segment of the mo_coeff is natural orbitls. + mo_energy : ndarray + Diagonal elements of general Fock matrix (in mo_coeff + representation). + + Examples: + ********CHANGE THIS EXAMPLE*********** + >>> from pyscf import gto, scf, mcscf + >>> mol = gto.M(atom='N 0 0 0; N 0 0 1', basis='ccpvdz', verbose=0) + >>> mf = scf.RHF(mol) + >>> mf.scf() + >>> mc = mcscf.CASSCF(mf, 6, 6) + >>> mc.kernel()[0] + -109.044401882238134 + ''' + +# the max orbital rotation and CI increment, prefer small step size + max_cycle_macro = getattr(__config__, 'zmcscf_zmc2step_ZCASSCF_max_cycle_macro', 50) + conv_tol = getattr(__config__, 'zmcscf_zmc2step_ZCASSCF_conv_tol', 1e-7) + conv_tol_grad = getattr(__config__, 'mcscf_zmc2step_ZCASSCF_conv_tol_grad', None) + + # for BB Solver + bb_conv_tol = getattr(__config__, 'zmcscf_zmc2step_ZCASSCF_bb_conv_tol', 1e-6) + bb_max_cycle = getattr(__config__, 'zmcscf_zmc2step_ZCASSCF_bb_max_cycle', 1000) + bb_gauss_noise = getattr(__config__, 'zmcscf_zmc2step_ZCASSCF_bb_gauss_noise', 0.1) + + internal_rotation = getattr(__config__, 'zmcscf_zmc2step_ZCASSCF_internal_rotation', True) + + ao2mo_level = getattr(__config__, 'zmcscf_zmc2step_ZCASSCF_ao2mo_level', 2) + natorb = getattr(__config__, 'zmcscf_zmc2step_ZCASSCF_natorb', False) + canonicalization = getattr(__config__, 'zmcscf_zmc2step_ZCASSCF_canonicalization', True) + sorting_mo_energy = getattr(__config__, 'zmcscf_zmc2step_ZCASSCF_sorting_mo_energy', False) + + def __init__(self, mf_or_mol, ncas, nelecas, ncore=None, frozen=None): + casci.CASCI.__init__(self, mf_or_mol, ncas, nelecas, ncore) + self.frozen = frozen + + self.callback = None + self.chkfile = self._scf.chkfile + + self.fcisolver.max_cycle = getattr(__config__, + 'zmcscf_zmc2step_ZCASSCF_fcisolver_max_cycle', 50) + self.fcisolver.conv_tol = getattr(__config__, + 'zmcscf_zmc2step_ZCASSCF_fcisolver_conv_tol', 1e-8) + +################################################## +# don't modify the following attributes, they are not input options + self.e_tot = None + self.e_cas = None + self.ci = None + self.mo_coeff = self._scf.mo_coeff + self.mo_energy = self._scf.mo_energy + self.converged = False + self._max_stepsize = None + + keys = set(('max_cycle_macro', + 'conv_tol', 'conv_tol_grad', + 'bb_conv_tol', 'bb_max_cycle', + 'internal_rotation', + 'fcisolver_max_cycle', + 'fcisolver_conv_tol', 'natorb', 'canonicalization', + 'sorting_mo_energy', 'scale_restoration')) + self._keys = set(self.__dict__.keys()).union(keys) + + def dump_flags(self, verbose=None): + log = logger.new_logger(self, verbose) + log.info('') + log.info('******** %s ********', self.__class__) + 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(nvir >= 0 and ncore >= 0 and ncas >= 0) + if self.frozen is not None: + log.info('frozen orbitals %s', str(self.frozen)) + log.info('max_cycle_macro = %d', self.max_cycle_macro) + log.info('conv_tol = %g', self.conv_tol) + log.info('conv_tol_grad = %s', self.conv_tol_grad) + log.info('Barzilai-Borwein bb_max_cycle = %d', self.bb_max_cycle) + log.info('Barzilai-Borwein bb_conv_tol = %g', self.bb_conv_tol) + log.info('Barzilai-Borwein bb_gauss_noise = %g', self.bb_gauss_noise) + log.info('natorb = %s', self.natorb) + log.info('canonicalization = %s', self.canonicalization) + log.info('sorting_mo_energy = %s', self.sorting_mo_energy) + log.info('ao2mo_level = %d', self.ao2mo_level) + log.info('chkfile = %s', self.chkfile) + log.info('max_memory %d MB (current use %d MB)', + self.max_memory, lib.current_memory()[0]) + log.info('internal_rotation = %s', self.internal_rotation) + if getattr(self.fcisolver, 'dump_flags', None): + self.fcisolver.dump_flags(self.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 at SCF level but not applied to the CASSCF object. +The SCF solvent model will not be applied to the current CASSCF calculation. +To enable the solvent model for CASSCF, the following code needs to be called + from pyscf import solvent + mc = mcscf.CASSCF(...) + mc = solvent.ddCOSMO(mc) +''', + self._scf.with_solvent.__class__) + return self + + def kernel(self, mo_coeff=None, ci0=None, callback=None, _kern=kernel): + ''' + Returns: + Five elements, they are + total energy, + active space CI energy, + the active space FCI wavefunction coefficients or DMRG wavefunction ID, + the MCSCF canonical orbital coefficients, + the MCSCF canonical orbital coefficients. + + They are attributes of mcscf object, which can be accessed by + .e_tot, .e_cas, .ci, .mo_coeff, .mo_energy + ''' + if mo_coeff is None: + mo_coeff = self.mo_coeff + else: # overwrite self.mo_coeff because it is needed in many methods of this class + self.mo_coeff = mo_coeff + if callback is None: callback = self.callback + + if self.verbose >= logger.WARN: + self.check_sanity() + self.dump_flags() + + self.converged, self.e_tot, self.e_cas, self.ci, \ + self.mo_coeff, self.mo_energy = \ + _kern(self, mo_coeff, + tol=self.conv_tol, conv_tol_grad=self.conv_tol_grad, + ci0=ci0, callback=callback, verbose=self.verbose) + logger.note(self, 'CASSCF energy = %.15g', self.e_tot) + self._finalize() + return self.e_tot, self.e_cas, self.ci, self.mo_coeff, self.mo_energy + + def mc1step(self, mo_coeff=None, ci0=None, callback=None): + return self.kernel(mo_coeff, ci0, callback) + + def mc2step(self, mo_coeff=None, ci0=None, callback=None): + from pyscf.mcscf import mc2step + return self.kernel(mo_coeff, ci0, callback, mc2step.kernel) + + + def casci(self, mo_coeff, ci0=None, eris=None, verbose=None, envs=None): + log = logger.new_logger(self, verbose) + if eris is None: + fcasci = copy.copy(self) + fcasci.ao2mo = self.get_h2cas + else: + fcasci = _fake_h_for_fast_casci(self, mo_coeff, eris) + + e_tot, e_cas, fcivec = casci.kernel(fcasci, mo_coeff, ci0, log) + if not isinstance(e_cas, (float, numpy.number)): + raise RuntimeError('Multiple roots are detected in fcisolver. ' + 'CASSCF does not know which state to optimize.\n' + 'See also mcscf.state_average or mcscf.state_specific for excited states.') + elif numpy.ndim(e_cas) != 0: + # This is a workaround for external CI solver compatibility. + e_cas = e_cas[0] + + if envs is not None and log.verbose >= logger.INFO: + log.debug('CAS space CI energy = %.15g', e_cas) + + if getattr(self.fcisolver, 'spin_square', None): + ss = self.fcisolver.spin_square(fcivec, self.ncas, self.nelecas) + else: + ss = None + + if 'imicro' in envs: # Within CASSCF iteration + if ss is None: + log.info('macro iter %d (%d JK %d micro), ' + 'CASSCF E = %.15g dE = %.8g', + envs['imacro'], envs['njk'], envs['imicro'], + e_tot, e_tot-envs['elast']) + else: + log.info('macro iter %d (%d JK %d micro), ' + 'CASSCF E = %.15g dE = %.8g S^2 = %.7f', + envs['imacro'], envs['njk'], envs['imicro'], + e_tot, e_tot-envs['elast'], ss[0]) + 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 + if ss is None: + log.info('CASCI E = %.15g', e_tot) + else: + log.info('CASCI E = %.15g S^2 = %.7f', e_tot, ss[0]) + return e_tot, e_cas, fcivec + + def ao2mo(self, mo_coeff=None): + if mo_coeff is None: mo_coeff = self.mo_coeff +# nmo = mo.shape[1] +# ncore = self.ncore +# ncas = self.ncas +# nocc = ncore + ncas +# eri = pyscf.ao2mo.incore.full(self._scf._eri, mo) +# eri = pyscf.ao2mo.restore(1, eri, nmo) +# eris = lambda:None +# eris.j_cp = numpy.einsum('iipp->ip', eri[:ncore,:ncore,:,:]) +# eris.k_cp = numpy.einsum('ippi->ip', eri[:ncore,:,:,:ncore]) +# eris.vhf_c =(numpy.einsum('iipq->pq', eri[:ncore,:ncore,:,:])*2 +# -numpy.einsum('ipqi->pq', eri[:ncore,:,:,:ncore])) +# eris.ppaa = numpy.asarray(eri[:,:,ncore:nocc,ncore:nocc], order='C') +# eris.papa = numpy.asarray(eri[:,ncore:nocc,:,ncore:nocc], order='C') +# return eris + + return mc_ao2mo._ERIS(self, mo_coeff, method='incore', + level=self.ao2mo_level) + + def getGrad(self, mo, casdm1, casdm2, eris): + nmo, ncore, nact = mo.shape[0], self.ncore, self.ncas + nocc = ncore+nact + + mocopy = 1j*mo + grad = 0.*mo[:,:nocc] + + import pdb + pdb.set_trace() + hcore = self.get_hcore() + + #Ec = numpy.einsum('ij,ji', + #contribution from the core energy + grad[:,:ncore] = numpy.dot(numpy.transpose(hcore), numpy.conjugate(mo[:,:ncore])) + + + def optimizeOrbs(self, mo, casdm1, casdm2, eris, r0, conv_tol, log): + #the part of mo that is relevant + + grad = self.getGrad(mo, casdm1, casdm2, eris) + +# to avoid calculating AO integrals +def _fake_h_for_fast_casci(casscf, mo, eris): + mc = copy.copy(casscf) + mc.mo_coeff = mo + ncore = casscf.ncore + nocc = ncore + casscf.ncas + + mo_core = mo[:,:ncore] + mo_cas = mo[:,ncore:nocc] + core_dm = numpy.dot(mo_core, mo_core.T) * 2 + hcore = casscf.get_hcore() + energy_core = casscf.energy_nuc() + energy_core += numpy.einsum('ij,ji', core_dm, hcore) + energy_core += eris.vhf_c[:ncore,:ncore].trace() + h1eff = reduce(numpy.dot, (mo_cas.T, hcore, mo_cas)) + h1eff += eris.vhf_c[ncore:nocc,ncore:nocc] + mc.get_h1eff = lambda *args: (h1eff, energy_core) + + eri_cas = eris.ppaa[ncore:nocc,ncore:nocc,:,:].copy() + mc.get_h2eff = lambda *args: eri_cas + return mc + + +if __name__ == '__main__': + from pyscf import gto + from pyscf import scf + + mol = gto.Mole() + mol.verbose = 0 + mol.output = None#"out_h2o" + mol.atom = [ + ['H', ( 1.,-1. , 0. )], + ['H', ( 0.,-1. ,-1. )], + ['H', ( 1.,-0.5 ,-1. )], + ['H', ( 0.,-0.5 ,-1. )], + ['H', ( 0.,-0.5 ,-0. )], + ['H', ( 0.,-0. ,-1. )], + ['H', ( 1.,-0.5 , 0. )], + ['H', ( 0., 1. , 1. )], + ] + + mol.basis = {'H': 'sto-3g', + 'O': '6-31g',} + mol.build() + + m = scf.GHF(mol) + ehf = m.scf() + emc = kernel(ZCASSCF(m, 4, 4), m.mo_coeff, verbose=4)[1] + print(ehf, emc, emc-ehf) + print(emc - -3.22013929407) + exit(0) + + mol.atom = [ + ['O', ( 0., 0. , 0. )], + ['H', ( 0., -0.757, 0.587)], + ['H', ( 0., 0.757 , 0.587)],] + mol.basis = {'H': 'cc-pvdz', + 'O': 'cc-pvdz',} + mol.build() + + m = scf.RHF(mol) + ehf = m.scf() + mc = mc1step.CASSCF(m, 6, 4) + mc.verbose = 4 + mo = m.mo_coeff.copy() + mo[:,2:5] = m.mo_coeff[:,[4,2,3]] + emc = mc.mc2step(mo)[0] + print(ehf, emc, emc-ehf) + #-76.0267656731 -76.0873922924 -0.0606266193028 + print(emc - -76.0873923174, emc - -76.0926176464) + From b403090d0397608b75ded8a340a08731a16ad370 Mon Sep 17 00:00:00 2001 From: Sandeep Sharma Date: Tue, 29 Sep 2020 16:58:16 -0600 Subject: [PATCH 19/26] Gradient descent casscf is working but is expensive. --- pyscf/zmcscf/zmc2step.py | 473 +++++++++++++------------------------- pyscf/zmcscf/zmc_ao2mo.py | 19 +- 2 files changed, 159 insertions(+), 333 deletions(-) diff --git a/pyscf/zmcscf/zmc2step.py b/pyscf/zmcscf/zmc2step.py index 27a632290f..bca76749ad 100644 --- a/pyscf/zmcscf/zmc2step.py +++ b/pyscf/zmcscf/zmc2step.py @@ -26,7 +26,8 @@ from pyscf import __config__, lib, ao2mo #from pyscf.mcscf.casci import get_fock, cas_natorb, canonicalize from pyscf.mcscf import mc_ao2mo -#from pyscf.mcscf import chkfile +from pyscf.mcscf import chkfile +from scipy.linalg import expm as expmat from pyscf import mcscf def kernel(casscf, mo_coeff, tol=1e-7, conv_tol_grad=None, @@ -80,41 +81,12 @@ def kernel(casscf, mo_coeff, tol=1e-7, conv_tol_grad=None, casdm1, casdm2 = casscf.fcisolver.make_rdm12Frombin(fcivec, ncas, casscf.nelecas) norm_ddm = numpy.linalg.norm(casdm1 - casdm1_old) t3m = log.timer('update CAS DM', *t3m) - max_cycle_micro = 1 # casscf.micro_cycle_scheduler(locals()) - - casscf.optimizeOrbs(mo, lambda:casdm1, lambda:casdm2, eris, r0, conv_tol_grad*0.3, log) - for imicro in range(max_cycle_micro): - rota = casscf.rotate_orb_cc(mo, lambda:fcivec, lambda:casdm1, lambda:casdm2, - eris, r0, conv_tol_grad*.3, max_stepsize, log) - u, g_orb, njk1, r0 = next(rota) - rota.close() - njk += njk1 - norm_t = numpy.linalg.norm(u-numpy.eye(nmo)) - norm_gorb = numpy.linalg.norm(g_orb) - if imicro == 0: - norm_gorb0 = norm_gorb - de = numpy.dot(casscf.pack_uniq_var(u), g_orb) - t3m = log.timer('orbital rotation', *t3m) - - eris = None - u = u.copy() - g_orb = g_orb.copy() - mo = casscf.rotate_mo(mo, u, log) - eris = casscf.ao2mo(mo) - t3m = log.timer('update eri', *t3m) - - log.debug('micro %d ~dE=%5.3g |u-1|=%5.3g |g[o]|=%5.3g |dm1|=%5.3g', - imicro, de, norm_t, norm_gorb, norm_ddm) - - if callable(callback): - callback(locals()) - - t2m = log.timer('micro iter %d'%imicro, *t2m) - if norm_t < 1e-4 or abs(de) < tol*.4 or norm_gorb < conv_tol_grad*.2: - break + mo, gorb, njk, norm_gorb0 = casscf.optimizeOrbs(mo, lambda:casdm1, lambda:casdm2, imacro <= 2, + eris, r0, conv_tol_grad*0.3, log) + norm_gorb = numpy.linalg.norm(gorb) + totinner += njk - totmicro += imicro + 1 e_tot, e_cas, fcivec = casscf.casci(mo, fcivec, eris, log, locals()) log.timer('CASCI solver', *t3m) @@ -127,8 +99,9 @@ def kernel(casscf, mo_coeff, tol=1e-7, conv_tol_grad=None, else: elast = e_tot - if dump_chk: - casscf.dump_chk(locals()) + ###FIX THIS### + #if dump_chk: + #casscf.dump_chk(locals()) if callable(callback): callback(locals()) @@ -155,7 +128,6 @@ def kernel(casscf, mo_coeff, tol=1e-7, conv_tol_grad=None, log.timer('2-step CASSCF', *cput0) return conv, e_tot, e_cas, fcivec, mo, mo_energy - class ZCASSCF(gzcasci.GZCASCI): __doc__ = gzcasci.GZCASCI.__doc__ + '''CASSCF @@ -310,6 +282,7 @@ def dump_flags(self, verbose=None): self._scf.with_solvent.__class__) return self + def kernel(self, mo_coeff=None, ci0=None, callback=None, _kern=kernel): ''' Returns: @@ -405,20 +378,41 @@ def casci(self, mo_coeff, ci0=None, eris=None, verbose=None, envs=None): log.info('CASCI E = %.15g S^2 = %.7f', e_tot, ss[0]) return e_tot, e_cas, fcivec + def dump_chk(self, envs): + if not self.chkfile: + return self - def getGrad(self, mo, casdm1, casdm2, eris): - nmo, ncore, nact = mo.shape[0], self.ncore, self.ncas - nocc = ncore+nact - - mocopy = 1j*mo - grad = 0.*mo[:,:nocc] - - hcore = self.get_hcore() + ncore = self.ncore + nocc = ncore + self.ncas + if 'mo' in envs: + mo_coeff = envs['mo'] + else: + mo_coeff = envs['mo_coeff'] + mo_occ = numpy.zeros(mo_coeff.shape[1]) + mo_occ[:ncore] = 2 + if self.natorb: + occ = self._eig(-envs['casdm1'], ncore, nocc)[0] + mo_occ[ncore:nocc] = -occ + else: + mo_occ[ncore:nocc] = envs['casdm1'].diagonal().real +# Note: mo_energy in active space =/= F_{ii} (F is general Fock) + if 'mo_energy' in envs: + mo_energy = envs['mo_energy'] + else: + mo_energy = 'None' + chkfile.dump_mcscf(self, self.chkfile, 'mcscf', envs['e_tot'], + mo_coeff, ncore, self.ncas, mo_occ, + mo_energy, envs['e_cas'], None, envs['casdm1'], + overwrite_mol=False) + return self - #contribution from the core energy - grad[:,:ncore] = numpy.dot(numpy.transpose(hcore), numpy.conjugate(mo[:,:ncore])) + def update_from_chk(self, chkfile=None): + if chkfile is None: chkfile = self.chkfile + self.__dict__.update(lib.chkfile.load(chkfile, 'mcscf')) + return self + update = update_from_chk - ''' + ''' def calcGrad(self, mo, casdm1, casdm2, ERIS=None): #if ERIS is None: ERIS = self.ao2mo(mo) hcore = self._scf.get_hcore() @@ -473,33 +467,45 @@ def calcGrad(self, mo, casdm1, casdm2, ERIS): hcore = self._scf.get_hcore() nmo, ncore, nact = mo.shape[0], self.ncore, self.ncas nocc = ncore+nact - nuc_energy = self.energy_nuc() Grad = numpy.zeros((nmo, nocc), dtype=complex) moc, moa = mo[:,:ncore], mo[:,ncore:nocc] - dmcore = numpy.dot(moc, moc.conj().T) - dmcas = reduce(numpy.dot, (moa, casdm1(), moa.conj().T)) + dmcore = numpy.dot(moc, moc.T.conj()) + dmcas = reduce(numpy.dot, (moa.conj(), casdm1(), moa.T)).conj() j,k = self._scf.get_jk(self.mol, dm=dmcore) ja,ka = self._scf.get_jk(self.mol, dm=dmcas) hcore = reduce(numpy.dot, (mo.conj().T, hcore , mo)) + #print (hcore.diagonal()) Fc = (hcore + reduce(numpy.dot, (mo.conj().T, (j-k), mo))) Grad[:,:ncore] = hcore[:,:ncore] + reduce(numpy.dot, (mo.conj().T, j + ja - k - ka, moc)) Grad[:,ncore:nocc] = numpy.einsum('sp,qp->sq', Fc[:,ncore:nocc], casdm1()) - Grad[:,ncore:nocc]+= numpy.einsum('ruvw,tvuw->rt', ERIS.ppaa[:, ncore:nocc], casdm2()) - return 2.*Grad + Grad[:,ncore:nocc]+= numpy.einsum('ruvw,tvuw->rt', ERIS.paaa, casdm2()) + return 2*Grad + + def calcH_op(self, Grad, mo, x, casdm1, casdm2, ERIS): + x1 = casscf.unpack_uniq_var(x) + eps = 1.e-5 + moxp = mo + eps*numpy.dot(mo, x) + Gdxp = self.calcGrad(mox, casdm1, casdm2, ERIS) + + moxm = mo - eps*numpy.dot(mo, x) + Gdxm = self.calcGrad(mox, casdm1, casdm2, ERIS) + + Hx = (Gdxp - Gdxm)/eps/2. + return Hx def calcE(self, mo, casdm1, casdm2, ERIS=None): if ERIS is None: ERIS = self.ao2mo(mo) hcore = self._scf.get_hcore() - nmo, ncore, nact = mo.shape[0], self.ncore, self.ncas + ncore, nact = self.ncore, self.ncas nocc = ncore+nact nuc_energy = self.energy_nuc() - moc, moa = mo[:,:ncore], mo[:,ncore:nocc] + moc = mo[:,:ncore] dmcore = numpy.dot(moc, moc.conj().T) j,k = self._scf.get_jk(self.mol, dm=dmcore) @@ -508,286 +514,108 @@ def calcE(self, mo, casdm1, casdm2, ERIS=None): Fc = ( hcore + reduce(numpy.dot, (mo.conj().T, (j-k), mo))) Ecore = 0.5*numpy.sum((hcore+Fc).diagonal()[:ncore]) - - Ecas1 = numpy.einsum('tu, ut', Fc[ncore:nocc, ncore:nocc], casdm1()) - Ecas2 = 0.5*numpy.einsum('tuvw, tvuw', ERIS.ppaa[ncore:nocc, ncore:nocc], casdm2()) + Ecas1 = numpy.einsum('tu, tu', Fc[ncore:nocc, ncore:nocc], casdm1()) + Ecas2 = 0.5*numpy.einsum('tuvw, tvuw', ERIS.paaa[ncore:nocc], casdm2()) #print (Ecore, Ecas1, Ecas2, nuc_energy, "mo") return Ecore+Ecas1+Ecas2+nuc_energy - def calcEAO(self, mo, casdm1, casdm2, ERIS=None): - #if ERIS is None: ERIS = self.ao2mo(mo) - hcore = self._scf.get_hcore() - nmo, ncore, nact = mo.shape[0], self.ncore, self.ncas - nocc = ncore+nact - - moc = mo[:,:ncore] - - #ecore - dmcore = numpy.dot(moc, moc.conj().T) - j,k = self._scf.get_jk(self.mol, dm=dmcore) - Ecore = numpy.einsum('xy, yx', hcore+0.5*(j-k), dmcore) ###hcore - - - moa = mo[:,ncore:nocc] - dmcas = reduce(numpy.dot, (moa, casdm1(), moa.conj().T)) - - - - ###THIS IS THE BIT WE NEED - eri_ao_sp = mol.intor('int2e_spinor', aosym='s1') - j1 = lib.einsum('wxyz, wp->pxyz', eri_ao_sp, moa.conj()) - jaapp = lib.einsum('pxyz, xq->pqyz', j1, moa) - jaapp = lib.einsum('pqyz, prqs->rsyz', jaapp, casdm2()) - ####### FOR GRADIENT - - - Ecas2 = 0.5*numpy.einsum('pqwx,wp,xq', jaapp, moa.conj(), moa) - Ecas1 = numpy.einsum('xy, yx', hcore+ (j-k), dmcas) - nuc_energy = self.energy_nuc() - - #print (Ecore, Ecas1, Ecas2, nuc_energy, "ao") - return Ecore+Ecas1+Ecas2+nuc_energy - - def optimizeOrbs(self, mo, casdm1, casdm2, eris, r0, conv_tol, log): + def optimizeOrbs(self, mo, casdm1, casdm2, addnoise, eris, r0, conv_tol, log): #the part of mo that is relevant nmo, ncore, nact = mo.shape[0], self.ncore, self.ncas nocc = ncore+nact - ERIS = self.ao2mo() - - ##TEST IF THE GRADIENT IS WORKING - - Uk = 1.*mo - E = self.calcE(Uk, casdm1, casdm2, ERIS) - Grad = self.calcGrad(Uk, casdm1, casdm2, ERIS) - eps = 1.e-4 - - print (E, self.calcEAO(Uk, casdm1, casdm2)) - T = numpy.zeros((nmo, nocc), dtype=complex) - for i in range(nmo): - for j in range(nocc): - T[i,j] = 1*eps - Uk[:,:nocc] = mo[:,:nocc] + numpy.dot(mo, T) - Ep = self.calcE(Uk, casdm1, casdm2) - - T[i,j] = -1*eps - Uk[:,:nocc] = mo[:,:nocc] + numpy.dot(mo, T) - Em = self.calcE(Uk, casdm1, casdm2) - g = (Ep - Em)/2./eps - - #if ( abs(g - Grad[i,j].real) > 1.e-8): - print (i, j, Grad[i,j], g, Ep, Em) - T[i,j] = 0.0 - #exit(0) - print ("I am done \n") - exit(0) - - #### - - S = self._scf.get_ovlp(self.mol) - Sinv = numpy.linalg.inv(S) - - - - Uk = 1.*mo - print (self.calcE(Uk, casdm1, casdm2, ERIS)) - #Uk[:,:nocc] = 1.*orthogonalize(mo[:,:nocc] + 0.001*numpy.random.random((nmo, nocc))) - print (self.calcE(Uk, casdm1, casdm2)) - #print (self.calcE(Uk, casdm1, casdm2)) - - def update(Uk, Grad, tau): - nmo, nocc = Uk.shape[0], Uk.shape[1] - A = reduce(numpy.dot, (Grad, Uk.conj().T, S)) - reduce(numpy.dot, (S, Uk, Grad.conj().T)) - Q = numpy.dot(numpy.linalg.inv(numpy.eye(nmo) + tau * numpy.dot(A, S)) , numpy.eye(nmo)-tau*numpy.dot(A, S)) - Uk = numpy.dot(Q, Uk) - #print (numpy.linalg.norm( reduce(numpy.dot, (Uk.conj().T, S, Uk)) - numpy.eye(nocc))) - return Uk + + def GD(casscf, mo, nocc, addnoise): + + #add random noise + if (addnoise): + noise = numpy.zeros(mo.shape, dtype=complex) + noise = numpy.random.random(mo.shape) +\ + numpy.random.random(mo.shape)*1.j + mo = numpy.dot(mo, expmat(-0.01*(noise - noise.T.conj()))) + S = casscf._scf.get_ovlp() + + '''###TEST GRAD#### + Grad, Gradnew = 0.*mo, 0.*mo + ERIS = casscf.ao2mo(mo) + E = casscf.calcE(mo, casdm1, casdm2, ERIS).real + Grad[:,:nocc] = casscf.calcGrad(mo, casdm1, casdm2, ERIS) + T = numpy.zeros((nmo, nmo),dtype=complex) + eps = 1.e-5 + for i in range(nmo): + for j in range(nocc): + T[i,j] = eps + U = mo + numpy.dot(mo, T) + Ep = casscf.calcE(U, casdm1, casdm2).real + + T[i,j] = -eps + U = mo + numpy.dot(mo, T) + Em = casscf.calcE(U, casdm1, casdm2).real + + #if (abs((Ep-Em)/2./eps - Grad[i,j].imag) > 1.e-9): + print (i,j,Grad[i,j], (Ep-Em)/2./eps, (Ep-2*E+Em)/eps/eps) + T[i,j] = 0.0 + exit(0) + exit(0) + '''############# + - def BBOptimize(Uk, Sinv, nocc): tau = 0.01 - Gold = self.calcGrad(Uk, casdm1, casdm2) - Uk1 = 1.*update(Uk, Gold, tau) - dU = 1.*(Uk1 - Uk) - - for i in range(1000): - print (i, tau, self.calcE(Uk1, casdm1, casdm2).real, numpy.linalg.norm(Gold)) + Grad, Gradnew = 0.*mo, 0.*mo + ERIS = casscf.ao2mo(mo) + Eold = casscf.calcE(mo, casdm1, casdm2, ERIS).real + Grad[:,:nocc] = casscf.calcGrad(mo, casdm1, casdm2, ERIS) + monew = 1.*numpy.dot(mo, expmat(-tau*(Grad-Grad.T.conj()))) + Enew = casscf.calcE(monew, casdm1, casdm2, ERIS).real + ERIS = casscf.ao2mo(monew) + + for iter in range(1000): + G = Grad - Grad.conj().T + if (iter == 0): + normG0 = numpy.linalg.norm(G) + print ("%d %6.3e %13.7e %13.6e g=%6.2e"\ + %(iter, tau, Enew, Enew-Eold, numpy.linalg.norm(G))) + #print (numpy.linalg.norm(reduce(numpy.dot, (mo.conj().T, S, mo))-numpy.eye(mo.shape[0]))) + + if (iter == 90): + Enew = casscf.calcE(monew, casdm1, casdm2, ERIS).real + Eold = Enew + + Gradnew[:,:nocc] = casscf.calcGrad(monew, casdm1, casdm2, ERIS) - Gradk = self.calcGrad(Uk1, casdm1, casdm2) - - Uk1 = 1.*update(Uk1, Gradk, tau) + tau = 1.0 + mo, Grad = 1.*monew, 1.*Gradnew + monew = numpy.dot(mo, expmat(-tau*(Grad-Grad.T.conj()))) + Gnorm = numpy.linalg.norm(Grad-Grad.conj().T) - ''' - dG = 1.*(Gradk - Gold) - Gold = 1.*Gradk - - dUr, dUi = dU.real, dU.imag - dGr, dGi = dG.real, dG.imag - if (i%2 == 1 ): - tau = (numpy.einsum('ij,ij', dUr, dUr)+numpy.einsum('ij,ij', dUi, dUi)) \ - /abs(numpy.einsum('ij,ij', dGr,dUr)+numpy.einsum('ij,ij', dGi,dUi)) - else : - tau = abs(numpy.einsum('ij,ij', dUr, dGr)+numpy.einsum('ij,ij', dUi, dGi)) \ - /(numpy.einsum('ij,ij', dGr,dGr)+numpy.einsum('ij,ij', dGi,dGi)) - - #tau = 0.01 - Uk = Uk1 - Uk1 = update(Uk, Gradk, tau/2.) - dU = Uk1 - Uk - ''' - BBOptimize(Uk[:,:nocc], Sinv, nocc) - - ''' - def optimizeOrbs(self, mo, casdm1, casdm2, eris, r0, conv_tol, log): - #the part of mo that is relevant - nmo, ncore, nact = mo.shape[0], self.ncore, self.ncas - nocc = ncore+nact - - eri_ao_sp = mol.intor('int2e_spinor', aosym='s1') - hcore = self.get_hcore() - - moc, moa = mo[:,:ncore], mo[:,ncore:nocc] - ###CORE ENERGY### - nuc_energy = self.energy_nuc() - h1mo = reduce(numpy.dot, (mo.T.conj(), hcore, mo)) - eri_core = ao2mo.kernel(eri_ao_sp, moc, intor='int2e_spinor') - print (nuc_energy + numpy.sum(h1mo.diagonal()[:ncore]) + 0.5*(numpy.einsum('iijj', eri_core) - numpy.einsum('ijji', eri_core))) - - ###CAS ENERGY#### - h1cas = hcore + numpy.einsum('wxyz, yi, zi', eri_ao_sp, moc.conj(), moc) - \ - numpy.einsum('wzyx, yi, zi', eri_ao_sp, moc.conj(), moc) - eri_cas = ao2mo.kernel(eri_ao_sp, moa, intor='int2e_spinor') - h1casmo = reduce(numpy.dot, (moa.T.conj(), h1cas, moa)) - print (numpy.einsum('ij,ji', h1casmo, casdm1()), 0.5*numpy.einsum('ijkl, ikjl', eri_cas, casdm2())) - - - moc = mo[:, :nocc] - ###GRADIENT Uxi ### (core orbitals) - gradC = numpy.einsum('yz, yi->zi', hcore, moc.conj()) #numpy.dot(hcore.T, moc.conj()) - j,k = numpy.einsum('wxyz,wi,xi->yz', eri_ao_sp, moc.conj(), moc), numpy.einsum('wxyz,wi,zi->yx', eri_ao_sp, moc.conj(), moc) - gradC += numpy.einsum('yz, yi->zi', j, moc.conj()) - numpy.einsum('yz, yi->zi', k, moc.conj()) - - gradC2 = numpy.einsum('yz, zi->yi', hcore, moc) #numpy.dot(hcore.T, moc.conj()) - j,k = numpy.einsum('wxyz,wi,xi->yz', eri_ao_sp, moc.conj(), moc), numpy.einsum('wxyz,wi,zi->yx', eri_ao_sp, moc.conj(), moc) - gradC2 += numpy.einsum('yz, zi->yi', j, moc) - numpy.einsum('yz, zi->yi', k, moc) - - def calcE(moc, moconj, eri_ao_sp, nocc): - h1mo = reduce(numpy.dot, (moconj.conj().T, hcore, moc)) - eri_core = ao2mo.kernel(eri_ao_sp, (moconj, moc, moconj, moc), intor='int2e_spinor') - E0 = numpy.sum(h1mo.diagonal()[:nocc]) + 0.5*(numpy.einsum('iijj', eri_core) - numpy.einsum('ijji', eri_core)) - return E0 - - import pdb - pdb.set_trace() - moc, moconj = (1.+0j)*mo[:,:nocc], (1.+0j)*mo[:,:nocc] - E0 = calcE(moc, moconj, eri_ao_sp, nocc) - - GradReal, GradImag = (gradC + gradC2).real, (1.j*(gradC - gradC2)).real - Grad = 0.*gradC - Grad.real, Grad.imag = GradReal, GradImag - import scipy - moc = moc - 0.01 * Grad - S = self._scf.get_ovlp(self.mol) - Shalf = scipy.linalg.sqrtm(S) - moc = numpy.dot(Shalf, moc) - - [d,v] = numpy.linalg.eigh(numpy.dot(moc.conj().T, moc)) - idx = d > 1.e-10 - dinvsqrt = 1./numpy.sqrt(d[idx]) - from pyscf.lo import orth - Sortho = orth.lowdin(S) - + while tau > 1e-3: + monew = numpy.dot(mo, expmat(-tau*(Grad-Grad.T.conj()))) + ERIS = casscf.ao2mo(monew) + Enew = casscf.calcE(monew, casdm1, casdm2, ERIS).real + + if (Enew < Eold - tau*1.e-4*Gnorm): + break + else: + tau *= 0.5 + if (tau < 1.e-3 or Gnorm < conv_tol or abs(Enew-Eold)<1.e-9): + print ("%d %6.3e %13.7e %13.6e g=%6.2e"\ + %(iter, tau, Enew, Enew-Eold, numpy.linalg.norm(G))) + #print ("encountered small step size") + break + return mo, Grad-Grad.T.conj(), iter, normG0 - orthoU = reduce(numpy.dot, (Sortho, moc, v, numpy.diag(dinvsqrt))) - - import pdb - pdb.set_trace() - print (numpy.linalg.norm(reduce(numpy.dot, (orthoU.conj().T, S, orthoU)) - numpy.eye(nocc))) - - moc = mo[:,:nocc] - print (calcE(orthoU, orthoU, eri_ao_sp, nocc)+nuc_energy, calcE(moc, moc, eri_ao_sp, nocc)+nuc_energy) - exit(0) - - - print (numpy.linalg.norm(gradC)) - ERIS = self.ao2mo(mo) - hxy = self.get_hcore() - S = self._scf.get_ovlp(self.mol) - Sinv = numpy.linalg.inv(S) + return GD(self, mo, nocc, addnoise) - #moinvL, moinvR = numpy.dot(mo.T, S), - T_mo_mo = reduce(numpy.dot, (mo.T.conj(), hxy, mo)) - import pdb - pdb.set_trace() - #core energy - ecore = numpy.sum(hcc.diagonal()[:ncore]) \ - + 0.5 *numpy.einsum('iijj', ERIS.ppcc[:ncore, :ncore]) \ - - 0.5 * numpy.einsum('ijji', ERIS.ppcc[:ncore, :ncore]) \ - + nuc_energy - - heff = T_mo_mo[ncore:nocc, ncore:nocc] \ - + numpy.einsum('xyii', ERIS.ppaa[:ncore, :ncore]) \ - #- numpy.einsum('xiiy', ERIS.ppaa[ - ej = 0.0 - for i in range(ncore): - for j in range(ncore): - ej = ej + ERIS.ppcc[i,i,j,j] - print (ej) - print ( numpy.sum(hcc.diagonal()[:ncore]) , - + numpy.einsum('iijj', ERIS.ppcc[:ncore, :ncore]) , - - numpy.einsum('ijji', ERIS.ppcc[:ncore, :ncore])) - h1eff, energy_core = self.get_h1eff(mo) - - print (ecore, energy_core) - grad = self.getGrad(mo, casdm1, casdm2, eris) - ''' - - ''' - def optimizeOrbs(self, mo, casdm1, casdm2, eris, r0, conv_tol, log): - #the part of mo that is relevant - nmo, ncore, nact = mo.shape[0], self.ncore, self.ncas - nocc = ncore+nact - ERIS = self.ao2mo(mo) - hxy = self.get_hcore() - S = self._scf.get_ovlp(self.mol) - Sinv = numpy.linalg.inv(S) - - #moinvL, moinvR = numpy.dot(mo.T, S), - T_mo_mo = reduce(numpy.dot, (mo.T.conj(), hxy, mo)) - import pdb - pdb.set_trace() - #core energy - nuc_energy = self.energy_nuc() - ecore = numpy.sum(hcc.diagonal()[:ncore]) \ - + 0.5 *numpy.einsum('iijj', ERIS.ppcc[:ncore, :ncore]) \ - - 0.5 * numpy.einsum('ijji', ERIS.ppcc[:ncore, :ncore]) \ - + nuc_energy - - heff = T_mo_mo[ncore:nocc, ncore:nocc] \ - + numpy.einsum('xyii', ERIS.ppaa[:ncore, :ncore]) \ - #- numpy.einsum('xiiy', ERIS.ppaa[ - ej = 0.0 - for i in range(ncore): - for j in range(ncore): - ej = ej + ERIS.ppcc[i,i,j,j] - print (ej) - print ( numpy.sum(hcc.diagonal()[:ncore]) , - + numpy.einsum('iijj', ERIS.ppcc[:ncore, :ncore]) , - - numpy.einsum('ijji', ERIS.ppcc[:ncore, :ncore])) - h1eff, energy_core = self.get_h1eff(mo) - - print (ecore, energy_core) - grad = self.getGrad(mo, casdm1, casdm2, eris) - ''' if __name__ == '__main__': from pyscf import gto from pyscf import scf mol = gto.Mole() - mol.verbose = 1 + mol.verbose = 4 mol.output = None#"out_h2o" mol.atom = [ ['H', ( 1.,-1. , 0. )], @@ -800,7 +628,8 @@ def optimizeOrbs(self, mo, casdm1, casdm2, eris, r0, conv_tol, log): ['H', ( 0., 1. , 1. )], ] - mol.basis = '6-31g' + #mol.basis = '6-31g' + mol.basis = 'sto-3g' mol.build() ''' @@ -815,9 +644,19 @@ def optimizeOrbs(self, mo, casdm1, casdm2, eris, r0, conv_tol, log): #m = scf.GHF(mol) ehf = m.kernel() print (ehf) - mc = ZCASSCF(m, 4, 2) - mc.fcisolver = shci.SHCI(mol, maxM=1.e-6) - mc.kernel() + #mc = ZCASSCF(m, 16, 8) + mc = ZCASSCF(m, 8, 4) + mc.fcisolver = shci.SHCI(mol) + mc.fcisolver.sweep_epsilon=[1.e-5] + mc.fcisolver.sweep_iter=[0] + mc.fcisolver.davidsonTol = 1.e-6 + + mo = 1.*m.mo_coeff + noise = numpy.zeros(mo.shape, dtype=complex) + noise = numpy.random.random(mo.shape) +\ + numpy.random.random(mo.shape)*1.j + mo = numpy.dot(mo, expmat(-0.01*(noise - noise.T.conj()))) + emc = mc.kernel(mo)[0] print(ehf, emc, emc-ehf) print(emc - -3.22013929407) diff --git a/pyscf/zmcscf/zmc_ao2mo.py b/pyscf/zmcscf/zmc_ao2mo.py index f9acc3bd39..a92f314552 100644 --- a/pyscf/zmcscf/zmc_ao2mo.py +++ b/pyscf/zmcscf/zmc_ao2mo.py @@ -30,12 +30,8 @@ def __init__(self, zcasscf, mo, method='incore', level=1): if eri is None: eri = mol.intor('int2e_spinor', aosym='s8') - self.ppaa = ao2mo.kernel(eri, (mo, mo, moa, moa), + self.paaa = ao2mo.kernel(eri, (mo, moa, moa, moa), intor="int2e_spinor") - #self.papa = ao2mo.kernel(eri, (mo, moa, mo, moa), - #intor="int2e_spinor") - #self.j_pc, self.k_pc, self.ppaa, self.papa = \ - #mc_ao2mo.trans_e1_incore(eri, mo, ncore, ncas) else: import gc gc.collect() @@ -45,16 +41,7 @@ def __init__(self, zcasscf, mo, method='incore', level=1): if max_memory < mem_basic: log.warn('Calculation needs %d MB memory, over CASSCF.max_memory (%d MB) limit', (mem_basic+mem_now)/.9, zcasscf.max_memory) - self.ppaa = ao2mo.kernel(mol, (mo, mo, moa, moa), + self.paaa = ao2mo.kernel(mol, (mo, moa, moa, moa), intor="int2e_spinor") - self.papa = ao2mo.kernel(mol, (mo, moa, mo, moa), - intor="int2e_spinor") - #self.ppcc = ao2mo.kernel(mol, (mo, mo, moc, moc), - #intor="int2e_spinor") - #self.pcpc = ao2mo.kernel(mol, (mo, moc, mo, moc), - #intor="int2e_spinor") - self.ppaa.shape = (nmo, nmo, ncas, ncas) - self.papa.shape = (nmo, ncas, nmo, ncas) - #self.ppcc.shape = (nmo, nmo, ncore, ncore) - #self.pcpc.shape = (nmo, ncore, nmo, ncore) + self.paaa.shape = (nmo, ncas, ncas, ncas) From 8a5350cb5c8153cb23b7b5b7e85feb2c82edc5c4 Mon Sep 17 00:00:00 2001 From: Sandeep Sharma Date: Thu, 8 Oct 2020 20:33:34 -0600 Subject: [PATCH 20/26] basic zmc2step is working, but not efficient --- pyscf/zmcscf/gzcasci.py | 2 +- pyscf/zmcscf/zmc2step.py | 521 +++++++++++++++++++++++++++++++------- pyscf/zmcscf/zmc_ao2mo.py | 36 ++- 3 files changed, 457 insertions(+), 102 deletions(-) diff --git a/pyscf/zmcscf/gzcasci.py b/pyscf/zmcscf/gzcasci.py index e90e559bc9..8d3cf41eba 100644 --- a/pyscf/zmcscf/gzcasci.py +++ b/pyscf/zmcscf/gzcasci.py @@ -60,7 +60,7 @@ def h1e_for_cas(casci, mo_coeff=None, ncas=None, ncore=None): corevhf = casci.get_veff(casci.mol, core_dm) j,k = casci._scf.get_jk(casci.mol, core_dm) - print (energy_core, numpy.einsum('ij,ji', core_dm, hcore), numpy.einsum('ij,ji', j, corevhf), numpy.einsum('ij,ji', k, corevhf) ) + #print (energy_core, numpy.einsum('ij,ji', core_dm, hcore), numpy.einsum('ij,ji', j, corevhf), numpy.einsum('ij,ji', k, corevhf) ) energy_core += numpy.einsum('ij,ji', core_dm, hcore) energy_core += numpy.einsum('ij,ji', core_dm, corevhf) * .5 h1eff = reduce(numpy.dot, (mo_cas.T.conj(), hcore+corevhf, mo_cas)) diff --git a/pyscf/zmcscf/zmc2step.py b/pyscf/zmcscf/zmc2step.py index bca76749ad..19d94713c1 100644 --- a/pyscf/zmcscf/zmc2step.py +++ b/pyscf/zmcscf/zmc2step.py @@ -28,8 +28,33 @@ from pyscf.mcscf import mc_ao2mo from pyscf.mcscf import chkfile from scipy.linalg import expm as expmat - +from scipy.sparse.linalg import LinearOperator, minres, gmres +from pyscf.soscf import ciah +import pyscf.df from pyscf import mcscf + +def bracket(A,B): + return numpy.dot(A,B)-numpy.dot(B,A) +def derivOfExp(a,dA, maxT=50): + fact = 1.0 + deriv = 1.*dA + bra = 1.*dA + for i in range(maxT): + bra = bracket(a, bra) + fact *= -1./(i+2.) + deriv += fact*bra + + return deriv + +def get_jk_df(cderi, dm, with_j=True, with_k=True): + if (with_j): + j1 = lib.einsum('Lij,ji->L', cderi, dm) + j = lib.einsum('Lij,L->ij', cderi, j1) + if (with_k): + kint = lib.einsum('Lij,ja->Lia', cderi, dm) + k = lib.einsum('Laj,Lia->ij', cderi, kint) + return j,k + def kernel(casscf, mo_coeff, tol=1e-7, conv_tol_grad=None, ci0=None, callback=None, verbose=None, dump_chk=True): if verbose is None: @@ -82,6 +107,10 @@ def kernel(casscf, mo_coeff, tol=1e-7, conv_tol_grad=None, norm_ddm = numpy.linalg.norm(casdm1 - casdm1_old) t3m = log.timer('update CAS DM', *t3m) + max_cycle_micro = casscf.micro_cycle_scheduler(locals()) + #max_stepsize = casscf.max_stepsize_scheduler(locals()) + + mo, gorb, njk, norm_gorb0 = casscf.optimizeOrbs(mo, lambda:casdm1, lambda:casdm2, imacro <= 2, eris, r0, conv_tol_grad*0.3, log) norm_gorb = numpy.linalg.norm(gorb) @@ -141,13 +170,6 @@ class ZCASSCF(gzcasci.GZCASCI): max_cycle_macro : int Max number of macro iterations. Default is 50. - bb_conv_tol : float, for Barzelai-Browein (BB) solver. - converge threshold for BB solver. Default is 1e-6. - bb_max_cycle : float, for BB solver. - Max number of iterations allowd in BB solver. Default is 1000. - bb_gauss_noise : float, for BB solver. Default is 0.1 - Add some random noise before starting a new BB iteration - internal_rotation: bool. if the CI solver is not FCI then active-active rotations are not redundant. Default(True) @@ -191,16 +213,42 @@ class ZCASSCF(gzcasci.GZCASCI): ''' # the max orbital rotation and CI increment, prefer small step size - max_cycle_macro = getattr(__config__, 'zmcscf_zmc2step_ZCASSCF_max_cycle_macro', 50) + max_cycle_macro = getattr(__config__, 'mcscf_mc1step_CASSCF_max_cycle_macro', 50) + max_cycle_micro = getattr(__config__, 'mcscf_mc1step_CASSCF_max_cycle_micro', 4) conv_tol = getattr(__config__, 'zmcscf_zmc2step_ZCASSCF_conv_tol', 1e-7) conv_tol_grad = getattr(__config__, 'mcscf_zmc2step_ZCASSCF_conv_tol_grad', None) - # for BB Solver - bb_conv_tol = getattr(__config__, 'zmcscf_zmc2step_ZCASSCF_bb_conv_tol', 1e-6) - bb_max_cycle = getattr(__config__, 'zmcscf_zmc2step_ZCASSCF_bb_max_cycle', 1000) - bb_gauss_noise = getattr(__config__, 'zmcscf_zmc2step_ZCASSCF_bb_gauss_noise', 0.1) - + ah_level_shift = getattr(__config__, 'mcscf_mc1step_CASSCF_ah_level_shift', 1e-8) + ah_conv_tol = getattr(__config__, 'mcscf_mc1step_CASSCF_ah_conv_tol', 1e-12) + ah_max_cycle = getattr(__config__, 'mcscf_mc1step_CASSCF_ah_max_cycle', 30) + ah_lindep = getattr(__config__, 'mcscf_mc1step_CASSCF_ah_lindep', 1e-14) +# * ah_start_tol and ah_start_cycle control the start point to use AH step. +# In function rotate_orb_cc, the orbital rotation is carried out with the +# approximate aug_hessian step after a few davidson updates of the AH eigen +# problem. Reducing ah_start_tol or increasing ah_start_cycle will delay +# the start point of orbital rotation. +# * We can do early ah_start since it only affect the first few iterations. +# The start tol will be reduced when approach the convergence point. +# * Be careful with the SYMMETRY BROKEN caused by ah_start_tol/ah_start_cycle. +# ah_start_tol/ah_start_cycle actually approximates the hessian to reduce +# the J/K evaluation required by AH. When the system symmetry is higher +# than the one given by mol.symmetry/mol.groupname, symmetry broken might +# occur due to this approximation, e.g. with the default ah_start_tol, +# C2 (16o, 8e) under D2h symmetry might break the degeneracy between +# pi_x, pi_y orbitals since pi_x, pi_y belong to different irreps. It can +# be fixed by increasing the accuracy of AH solver, e.g. +# ah_start_tol = 1e-8; ah_conv_tol = 1e-10 +# * Classic AH can be simulated by setting eg +# ah_start_tol = 1e-7 +# max_stepsize = 1.5 +# ah_grad_trust_region = 1e6 +# ah_grad_trust_region allow gradients being increased in AH optimization + ah_start_tol = getattr(__config__, 'mcscf_mc1step_CASSCF_ah_start_tol', 2.5) + ah_start_cycle = getattr(__config__, 'mcscf_mc1step_CASSCF_ah_start_cycle', 3) + ah_grad_trust_region = getattr(__config__, 'mcscf_mc1step_CASSCF_ah_grad_trust_region', 3.0) internal_rotation = getattr(__config__, 'zmcscf_zmc2step_ZCASSCF_internal_rotation', True) + kf_interval = getattr(__config__, 'mcscf_mc1step_CASSCF_kf_interval', 4) + kf_trust_region = getattr(__config__, 'mcscf_mc1step_CASSCF_kf_trust_region', 3.0) ao2mo_level = getattr(__config__, 'zmcscf_zmc2step_ZCASSCF_ao2mo_level', 2) natorb = getattr(__config__, 'zmcscf_zmc2step_ZCASSCF_natorb', False) @@ -229,6 +277,10 @@ def __init__(self, mf_or_mol, ncas, nelecas, ncore=None, frozen=None): self.converged = False self._max_stepsize = None + #calculate the integrals + self.cderi = pyscf.df.r_incore.cholesky_eri(mol, int3c='int3c2e_spinor') + self.cderi.shape = (self.cderi.shape[0], self.mo_coeff.shape[0], self.mo_coeff.shape[1]) + keys = set(('max_cycle_macro', 'conv_tol', 'conv_tol_grad', 'bb_conv_tol', 'bb_max_cycle', @@ -253,9 +305,6 @@ def dump_flags(self, verbose=None): log.info('max_cycle_macro = %d', self.max_cycle_macro) log.info('conv_tol = %g', self.conv_tol) log.info('conv_tol_grad = %s', self.conv_tol_grad) - log.info('Barzilai-Borwein bb_max_cycle = %d', self.bb_max_cycle) - log.info('Barzilai-Borwein bb_conv_tol = %g', self.bb_conv_tol) - log.info('Barzilai-Borwein bb_gauss_noise = %g', self.bb_gauss_noise) log.info('natorb = %s', self.natorb) log.info('canonicalization = %s', self.canonicalization) log.info('sorting_mo_energy = %s', self.sorting_mo_energy) @@ -322,12 +371,17 @@ def mc2step(self, mo_coeff=None, ci0=None, callback=None): from pyscf.mcscf import mc2step return self.kernel(mo_coeff, ci0, callback, mc2step.kernel) + def micro_cycle_scheduler(self, envs): + return self.max_cycle_micro - def ao2mo(self, mo_coeff=None): + #log_norm_ddm = numpy.log(envs['norm_ddm']) + #return max(self.max_cycle_micro, int(self.max_cycle_micro-1-log_norm_ddm)) + + def ao2mo(self, mo_coeff=None, level=None): if mo_coeff is None: mo_coeff = self.mo_coeff - + if level is None: level=self.ao2mo_level return zmc_ao2mo._ERIS(self, mo_coeff, method='incore', - level=self.ao2mo_level) + level=level) def casci(self, mo_coeff, ci0=None, eris=None, verbose=None, envs=None): log = logger.new_logger(self, verbose) @@ -412,63 +466,123 @@ def update_from_chk(self, chkfile=None): return self update = update_from_chk - ''' - def calcGrad(self, mo, casdm1, casdm2, ERIS=None): - #if ERIS is None: ERIS = self.ao2mo(mo) + + #Fully uses AO and is currently not efficient because AO + #integrals are assumed to be available cheaply + def calcGradAO(self, mo, casdm1, casdm2): hcore = self._scf.get_hcore() nmo, ncore, nact = mo.shape[0], self.ncore, self.ncas nocc = ncore+nact + gradC2 = 0*mo moc = mo[:,:ncore] moa = mo[:,ncore:nocc] - gradC = 0*mo #for z - gradC2 = 0*mo #for z.conj #ecore - dmcas = reduce(numpy.dot, (moa, casdm1(), moa.conj().T)) + dmcas = reduce(numpy.dot, (moa.conj(), casdm1(), moa.T)).conj() dmcore = numpy.dot(moc, moc.conj().T) jc,kc = self._scf.get_jk(self.mol, dm=dmcore) ja,ka = self._scf.get_jk(self.mol, dm=dmcas) - #gradC [:,:ncore] = numpy.dot( (hcore + (jc-kc)+ja-ka).T, moc.conj()) gradC2[:,:ncore] = numpy.dot( (hcore + (jc-kc)+ja-ka) , moc) - #Ecore = numpy.einsum('xy, yx', hcore+0.5*(j-k), dmcore) ###hcore + gradC2[:,ncore:nocc] = reduce(numpy.dot, ( (hcore + jc - kc) , moa, casdm1().T)) ###THIS IS THE BIT WE NEED + ''' eri_ao_sp = mol.intor('int2e_spinor', aosym='s1') j1 = lib.einsum('wxyz, wp->pxyz', eri_ao_sp, moa.conj()) jaapp = lib.einsum('pxyz, xq->pqyz', j1, moa) - jaapp = lib.einsum('pqyz, prqs->rsyz', jaapp, casdm2()) + jaaap = lib.einsum('pqyz, zs->pqys', jaapp, moa) + gradC2[:,ncore:nocc] += lib.einsum('pqys,prqs->yr', jaaap, casdm2()) + ''' ####### FOR GRADIENT + ###THIS BIT WILL BE EXPENSIVE + eripaaa = numpy.zeros((nmo, nact, nact,nact), dtype = complex) + for i in range(nact): + for j in range(i+1): + dm = lib.einsum('x,y->xy',moa[:,i], moa[:,j].conj()) + j1 = self._scf.get_j(self.mol, dm = dm, hermi=0) + j1 = numpy.triu(j1) + j1 = j1 + j1.T - numpy.diag(numpy.diag(j1)) + eripaaa[:,:,i,j] = numpy.dot(j1,moa) + if (i != j): + eripaaa[:,:,j,i] = numpy.dot(j1.conj().T,moa) + gradC2[:,ncore:nocc] += lib.einsum('ypqr,sqpr->ys', eripaaa, casdm2()) + + gradC2 = numpy.dot(mo.conj().T, gradC2) + return 2*gradC2 + + + def calcGradDF(self, mo, casdm1, casdm2): + hcore = self._scf.get_hcore() + + nmo, ncore, nact = mo.shape[0], self.ncore, self.ncas + nocc = ncore+nact + + Grad = numpy.zeros((nmo, nmo), dtype=complex) + moc, moa = mo[:,:ncore], mo[:,ncore:nocc] + dmcore = numpy.dot(moc, moc.T.conj()) + dmcas = reduce(numpy.dot, (moa.conj(), casdm1(), moa.T)).conj() + j,k=get_jk_df(self.cderi, dmcore) + ja,ka=get_jk_df(self.cderi, dmcas) - #gradC [:,ncore:nocc] = reduce(numpy.dot, ( (hcore + jc - kc).T, moa.conj(), casdm1().T)) - gradC2[:,ncore:nocc] = reduce(numpy.dot, ( (hcore + jc - kc) , moa, casdm1())) + hcore = reduce(numpy.dot, (mo.conj().T, hcore , mo)) + Fc = (hcore + reduce(numpy.dot, (mo.conj().T, (j-k), mo))) + Grad[:,:ncore] = hcore[:,:ncore] + reduce(numpy.dot, (mo.conj().T, j + ja - k - ka, moc)) + + Grad[:,ncore:nocc] = numpy.einsum('sp,qp->sq', Fc[:,ncore:nocc], casdm1()) + Lrq = lib.einsum('Lxy,ya->Lxa',self.cderi, moa) + Lpq = lib.einsum('Lxa,xb->Lba', Lrq, moa.conj()) + Lrq = lib.einsum('Lxa,xy->Lya', Lrq, mo.conj()) + paaa = lib.einsum('Lxa,Lcd->xacd', Lrq, Lpq) + Grad[:,ncore:nocc]+= lib.einsum('ruvw,tvuw->rt', paaa, casdm2()) - #gradC [:,ncore:nocc] = gradC [:, ncore:nocc] + numpy.einsum('pqwx, wp->xq', jaapp, moa.conj()) - gradC2[:,ncore:nocc] = gradC2[:, ncore:nocc] + numpy.einsum('pqwx, xq->wp', jaapp, moa) + return 2*Grad - return gradC2 + def calcGrad(self, mo, casdm1, casdm2, ERIS=None): + if ERIS is None: ERIS = self.ao2mo(mo,level=2) + hcore = self._scf.get_hcore() + nmo, ncore, nact = mo.shape[0], self.ncore, self.ncas + nocc = ncore+nact + Grad = numpy.zeros((nmo, nmo), dtype=complex) - GradReal, GradImag = (gradC + gradC2).real, (1.j*(gradC - gradC2)).real - Grad = 0.*gradC - Grad.real, Grad.imag = GradReal, GradImag - print (numpy.linalg.norm(gradC - Grad.conj()/2.)) - return Grad - ''' - #return gradC + moc, moa = mo[:,:ncore], mo[:,ncore:nocc] + dmcore = numpy.dot(moc, moc.T.conj()) + dmcas = reduce(numpy.dot, (moa.conj(), casdm1(), moa.T)).conj() + j,k = self._scf.get_jk(self.mol, dm=dmcore) + ja,ka = self._scf.get_jk(self.mol, dm=dmcas) + + + hcore = reduce(numpy.dot, (mo.conj().T, hcore , mo)) + #print (hcore.diagonal()) + Fc = (hcore + reduce(numpy.dot, (mo.conj().T, (j-k), mo))) + Grad[:,:ncore] = hcore[:,:ncore] + reduce(numpy.dot, (mo.conj().T, j + ja - k - ka, moc)) + Grad[:,ncore:nocc] = numpy.einsum('sp,qp->sq', Fc[:,ncore:nocc], casdm1()) + Grad[:,ncore:nocc]+= numpy.einsum('ruvw,tvuw->rt', ERIS.paaa, casdm2()) - def calcGrad(self, mo, casdm1, casdm2, ERIS): + ###Make the lower traingular zero + #for i in range(ncore,nocc): + # for j in range(i+1,nocc): + # Grad[i,j] = 0.0 + #Grad = Grad-Grad.conj().T + + + return 2*Grad + + + def calcGradOld(self, mo, casdm1, casdm2, ERIS=None): + if ERIS is None: ERIS = self.ao2mo(mo,level=2) hcore = self._scf.get_hcore() nmo, ncore, nact = mo.shape[0], self.ncore, self.ncas nocc = ncore+nact - Grad = numpy.zeros((nmo, nocc), dtype=complex) + Grad = numpy.zeros((nmo, nmo), dtype=complex) moc, moa = mo[:,:ncore], mo[:,ncore:nocc] dmcore = numpy.dot(moc, moc.T.conj()) @@ -486,20 +600,51 @@ def calcGrad(self, mo, casdm1, casdm2, ERIS): Grad[:,ncore:nocc]+= numpy.einsum('ruvw,tvuw->rt', ERIS.paaa, casdm2()) return 2*Grad - def calcH_op(self, Grad, mo, x, casdm1, casdm2, ERIS): - x1 = casscf.unpack_uniq_var(x) - eps = 1.e-5 - moxp = mo + eps*numpy.dot(mo, x) - Gdxp = self.calcGrad(mox, casdm1, casdm2, ERIS) + ###IT IS NOT CORRECT, maybe some day i will fix it### + def calcH(self, mo, x, casdm1, casdm2, ERIS): + hcore = self._scf.get_hcore() + nmo, ncore, nact = mo.shape[0], self.ncore, self.ncas + nocc = ncore+nact - moxm = mo - eps*numpy.dot(mo, x) - Gdxm = self.calcGrad(mox, casdm1, casdm2, ERIS) + moc, moa = mo[:,:ncore], mo[:,ncore:nocc] + dmcore = numpy.dot(moc, moc.T.conj()) + dmcas = reduce(numpy.dot, (moa.conj(), casdm1(), moa.T)).conj() + j,k = self._scf.get_jk(self.mol, dm=dmcore) + ja,ka = self._scf.get_jk(self.mol, dm=dmcas) - Hx = (Gdxp - Gdxm)/eps/2. - return Hx + hcore = reduce(numpy.dot, (mo.conj().T, hcore , mo)) + Fc = (hcore + reduce(numpy.dot, (mo.conj().T, (j-k), mo))) + Fa = reduce(numpy.dot, (mo.conj().T, (ja-ka), mo)) + + Hrr = numpy.zeros((nmo,nocc, nmo, nocc)) + for i in range(ncore): + Hrr[:,i,:,i] = (Fc+Fa).real + + print (Hrr[2,2,3,3]) + Hrr[:,:ncore,:,:ncore] += \ + (numpy.einsum('xijy->xiyj',ERIS.poop[:,:ncore,:ncore])\ + - numpy.einsum('xyji->xiyj',ERIS.ppoo[:,:,:ncore,:ncore])\ + + numpy.einsum('xiyj->xiyj', ERIS.popo[:,:ncore,:,:ncore])\ + - numpy.einsum('xjyi->xiyj', ERIS.popo[:,:ncore,:,:ncore])).real + + Hrr[:,:ncore, :,ncore:nocc] +=\ + (numpy.einsum('xqyi,pq->yixp',ERIS.popo[:,ncore:nocc,:,:ncore], casdm1())\ + +numpy.einsum('xqiy,pq->yixp',ERIS.poop[:,ncore:nocc,:ncore,:], casdm1())\ + -numpy.einsum('xiyq,pq->yixp',ERIS.popo[:,:ncore,:,ncore:nocc], casdm1())\ + -numpy.einsum('yxpi,pq->yixq',ERIS.ppoo[:,:,ncore:nocc,:ncore], casdm1())).real + + Hrr[:,ncore:nocc,:,ncore:nocc] +=\ + (0*numpy.einsum('xy,pq->xpyq',Fc,casdm1())\ + + 1*numpy.einsum('xyrs,prqs->xpyq',ERIS.ppoo[:,:,ncore:nocc,ncore:nocc].real, casdm2())\ + + 0.5*numpy.einsum('xsry,rpqs->xpyq',ERIS.poop[:,ncore:nocc,ncore:nocc,:], casdm2())\ + + 0.5*numpy.einsum('xsry,prsq->xpyq',ERIS.poop[:,ncore:nocc,ncore:nocc,:], casdm2())\ + + 0.5*numpy.einsum('ysxr,pqrs->xpyq',ERIS.popo[:,ncore:nocc,:,ncore:nocc], casdm2())\ + + 0.5*numpy.einsum('yrxs,srpq->xpyq',ERIS.popo[:,ncore:nocc,:,ncore:nocc].conj(), casdm2())).real + + return 2*Hrr def calcE(self, mo, casdm1, casdm2, ERIS=None): - if ERIS is None: ERIS = self.ao2mo(mo) + if ERIS is None: ERIS = self.ao2mo(mo, level=1) hcore = self._scf.get_hcore() ncore, nact = self.ncore, self.ncas nocc = ncore+nact @@ -515,53 +660,167 @@ def calcE(self, mo, casdm1, casdm2, ERIS=None): Ecore = 0.5*numpy.sum((hcore+Fc).diagonal()[:ncore]) Ecas1 = numpy.einsum('tu, tu', Fc[ncore:nocc, ncore:nocc], casdm1()) - Ecas2 = 0.5*numpy.einsum('tuvw, tvuw', ERIS.paaa[ncore:nocc], casdm2()) + Ecas2 = 0.5*numpy.einsum('tuvw, tvuw', ERIS.aaaa, casdm2()) - #print (Ecore, Ecas1, Ecas2, nuc_energy, "mo") return Ecore+Ecas1+Ecas2+nuc_energy + def uniq_var_indices(self, nmo, ncore, ncas, frozen=None): + nocc = ncore + ncas + mask = numpy.zeros((nmo,nmo),dtype=bool) + mask[ncore:nocc,:ncore] = True + mask[nocc:,:nocc] = True + if self.internal_rotation: + mask[ncore:nocc,ncore:nocc][numpy.tril_indices(ncas,-1)] = True + if frozen is not None: + if isinstance(frozen, (int, numpy.integer)): + mask[:frozen] = mask[:,:frozen] = False + else: + frozen = numpy.asarray(frozen) + mask[frozen] = mask[:,frozen] = False + return mask + + def pack_vars(self, mat): + nmo = self.mo_coeff.shape[1] + idx = self.uniq_var_indices(nmo, self.ncore, self.ncas, self.frozen) + + vec1 = mat[idx].real + vec2 = mat[idx].imag + vec = numpy.zeros((2*vec1.shape[0],)) + vec[:vec1.shape[0]] = 1*vec1 + vec[vec1.shape[0]:] = 1*vec2 + return vec + + # to anti symmetric matrix + def unpack_vars(self, v): + nmo = self.mo_coeff.shape[1] + idx = self.uniq_var_indices(nmo, self.ncore, self.ncas, self.frozen) + mat = numpy.zeros((nmo,nmo), dtype=complex) + nvars = v.shape[0]//2 + mat[idx] += v[:nvars] + mat[idx] += 1j*v[nvars:] + return mat - mat.T.conj() + + def optimizeOrbs(self, mo, casdm1, casdm2, addnoise, eris, r0, conv_tol, log): #the part of mo that is relevant nmo, ncore, nact = mo.shape[0], self.ncore, self.ncas nocc = ncore+nact - - def GD(casscf, mo, nocc, addnoise): - - #add random noise - if (addnoise): - noise = numpy.zeros(mo.shape, dtype=complex) - noise = numpy.random.random(mo.shape) +\ - numpy.random.random(mo.shape)*1.j - mo = numpy.dot(mo, expmat(-0.01*(noise - noise.T.conj()))) - S = casscf._scf.get_ovlp() - - '''###TEST GRAD#### - Grad, Gradnew = 0.*mo, 0.*mo - ERIS = casscf.ao2mo(mo) - E = casscf.calcE(mo, casdm1, casdm2, ERIS).real - Grad[:,:nocc] = casscf.calcGrad(mo, casdm1, casdm2, ERIS) - T = numpy.zeros((nmo, nmo),dtype=complex) - eps = 1.e-5 - for i in range(nmo): - for j in range(nocc): - T[i,j] = eps - U = mo + numpy.dot(mo, T) - Ep = casscf.calcE(U, casdm1, casdm2).real - - T[i,j] = -eps - U = mo + numpy.dot(mo, T) - Em = casscf.calcE(U, casdm1, casdm2).real - - #if (abs((Ep-Em)/2./eps - Grad[i,j].imag) > 1.e-9): - print (i,j,Grad[i,j], (Ep-Em)/2./eps, (Ep-2*E+Em)/eps/eps) - T[i,j] = 0.0 + + Grad, Gradnew = 0.*mo, 0.*mo + ERIS = self.ao2mo(mo) + E = self.calcE(mo, casdm1, casdm2).real + Grad = self.calcGrad(mo, casdm1, casdm2) + + T = numpy.zeros((nmo, nmo),dtype=complex) + + + def NewtonStep(casscf, mo, nocc, Grad): + Gradnewp, Gradnewm, T, monew = 0.*mo, 0.*mo, 0.*mo, 0.*mo + + nvars = casscf.pack_vars(mo).shape[0] + G = casscf.pack_vars(Grad-Grad.conj().T) + + T, monew = 0*mo, 0.*mo + def hop(x): + Gradnewp, Gradnewm = 0.*mo, 0.*mo + + eps = 1.e-5 + Kappa = eps*casscf.unpack_vars(x) + + monew = numpy.dot(mo, expmat(Kappa)) + Gradnewp = casscf.calcGradDF(monew, casdm1, casdm2) + #print (numpy.linalg.norm(Gradnewp-Grad)) + + f = numpy.dot(Kappa.conj().T, Gradnewp) + h = numpy.dot(Gradnewp, Kappa.conj().T) + Gradnewp = Gradnewp -0.5*(f-h) #- Gtemp.conj().T - 0.5*(f-f.conj().T+h-h.conj().T) + Gradnewp = Gradnewp - Gradnewp.conj().T + Gnewp= casscf.pack_vars(Gradnewp) + + + #Hx = (Gnewp - G)/eps + monew = numpy.dot(mo, expmat(-Kappa)) + Gradnewm = casscf.calcGradDF(monew, casdm1, casdm2) + f = numpy.dot(Kappa.conj().T, Gradnewm) + h = numpy.dot(Gradnewm, Kappa.conj().T) + Gradnewm = Gradnewm -0.5*(f-h) #- Gtemp.conj().T - 0.5*(f-f.conj().T+h-h.conj().T) + Gradnewm = Gradnewm - Gradnewm.conj().T + Gnewm= casscf.pack_vars(Gradnewm) + + Hx = (Gnewp - Gnewm)/2./eps + + return Hx + + ##we don't have diagonal elements + def precond(x, y): + return x + + + x = 0*G + x0 = 1.*G + index = 0 + for ah_end, ihop, w, dxi, hdxi, residual, seig \ + in ciah.davidson_cc(hop, lambda: G, precond, x0, + tol=casscf.ah_conv_tol, max_cycle=casscf.ah_max_cycle, + lindep=casscf.ah_lindep, verbose=log): + index += 1 + if (index >=casscf.ah_start_cycle): + norm_residual = numpy.linalg.norm(residual) + x += dxi + #print ("doing ah ", norm_residual, w) + + return x, numpy.linalg.norm(G) + + + imicro, nmicro, T, Grad = 0, 10, numpy.zeros_like(mo), 0.*mo + Enew = 0. + Eold = self.calcE(mo, casdm1, casdm2).real + while True: + Grad = self.calcGrad(mo, casdm1, casdm2) + gnorm = numpy.linalg.norm(Grad-Grad.conj().T) + + #if gradient is converged then exit + if ( gnorm < conv_tol or imicro >= nmicro ): exit(0) - exit(0) - '''############# - + return mo, Grad, imicro, gnorm + + + #find the newton step direction + x, gnorm = NewtonStep(self, mo, nocc, Grad) + T = self.unpack_vars(x) + #T[:nocc,nocc:] = -1.*T[nocc:,:nocc].conj().T + + ###do line search along the AH direction + tau = 0.1 + while tau > 1e-3: + monew = numpy.dot(mo, expmat(tau*(T) )) + Enew = self.calcE(monew, casdm1, casdm2).real + + if (Enew < Eold):# - tau * 1e-4*gnorm): + print ("%d %6.3e %13.7e %13.6e g=%6.2e"\ + %(imicro, tau, Enew, Enew-Eold, gnorm)) + Eold = Enew + mo = 1.*monew + break + tau = tau/2. + + imicro += 1 + exit(0) + + + + def optimizeOrbsDep1(self, mo, casdm1, casdm2, addnoise, eris, r0, conv_tol, log): + #the part of mo that is relevant + nmo, ncore, nact = mo.shape[0], self.ncore, self.ncas + nocc = ncore+nact + + ERIS = casscf.ao2mo(mo) + Eold = casscf.calcE(mo, casdm1, casdm2, ERIS).real + Grad[:,:nocc] = casscf.calcGrad(mo, casdm1, casdm2, ERIS) + def GD(casscf, mo, nocc, addnoise, ERIS): tau = 0.01 Grad, Gradnew = 0.*mo, 0.*mo ERIS = casscf.ao2mo(mo) @@ -608,7 +867,72 @@ def GD(casscf, mo, nocc, addnoise): return GD(self, mo, nocc, addnoise) + def optimizeOrbsDep1B(self, mo, casdm1, casdm2, addnoise, eris, r0, conv_tol, log): + #the part of mo that is relevant + nmo, ncore, nact = mo.shape[0], self.ncore, self.ncas + nocc = ncore+nact + + Grad = 0*mo + ERIS = self.ao2mo(mo) + Eold = self.calcE(mo, casdm1, casdm2, ERIS).real + Grad = self.calcGrad(mo, casdm1, casdm2, ERIS) + S = self._scf.get_ovlp() + + def GD(casscf, mo, nocc, addnoise, ERIS=None): + tau = -0.01 + Grad, Gradnew = 0.*mo, 0.*mo + ERIS = casscf.ao2mo(mo) + Eold = casscf.calcE(mo, casdm1, casdm2, ERIS).real + Grad = casscf.calcGrad(mo, casdm1, casdm2, ERIS) + + #first chec + Kappa = reduce(numpy.dot, (mo.conj().T, S, Grad)) + Kappa = Kappa - Kappa.T.conj() + monew = 1.*numpy.dot(mo, expmat(-tau*(Kappa))) + + Enew = casscf.calcE(monew, casdm1, casdm2, ERIS).real + ERIS = casscf.ao2mo(monew) + + for iter in range(1000): + G = Grad - Grad.conj().T + if (iter == 0): + normG0 = numpy.linalg.norm(G) + print ("%d %6.3e %13.7e %13.6e g=%6.2e"\ + %(iter, tau, Enew, Enew-Eold, numpy.linalg.norm(G))) + #print (numpy.linalg.norm(reduce(numpy.dot, (mo.conj().T, S, mo))-numpy.eye(mo.shape[0]))) + + if (iter == 90): + Enew = casscf.calcE(monew, casdm1, casdm2, ERIS).real + Eold = Enew + Gradnew = casscf.calcGradAO(monew, casdm1, casdm2, ERIS) + + tau = 1.0 + mo, Grad = 1.*monew, 1.*Gradnew + + Kappa = reduce(numpy.dot, (mo.conj().T, S, Grad)) + Kappa = Kappa - Kappa.T.conj() + + #monew = numpy.dot(mo, expmat(-tau*(Grad-Grad.T.conj()))) + Gnorm = numpy.linalg.norm(Kappa) + + while tau > 1e-5: + monew = numpy.dot(mo, expmat(-tau*(Kappa))) + ERIS = casscf.ao2mo(monew) + Enew = casscf.calcE(monew, casdm1, casdm2, ERIS).real + + if (Enew < Eold): + break + else: + tau *= 0.5 + if (tau < 1.e-3 or Gnorm < conv_tol or abs(Enew-Eold)<1.e-9): + print ("%d %6.3e %13.7e %13.6e g=%6.2e"\ + %(iter, tau, Enew, Enew-Eold, numpy.linalg.norm(G))) + #print ("encountered small step size") + break + return mo, Grad-Grad.T.conj(), iter, normG0 + + return GD(self, mo, nocc, addnoise) if __name__ == '__main__': from pyscf import gto @@ -616,6 +940,7 @@ def GD(casscf, mo, nocc, addnoise): mol = gto.Mole() mol.verbose = 4 + mol.memory=200 mol.output = None#"out_h2o" mol.atom = [ ['H', ( 1.,-1. , 0. )], @@ -628,8 +953,8 @@ def GD(casscf, mo, nocc, addnoise): ['H', ( 0., 1. , 1. )], ] - #mol.basis = '6-31g' - mol.basis = 'sto-3g' + mol.basis = '6-31g' + #mol.basis = 'sto-3g' mol.build() ''' @@ -652,12 +977,18 @@ def GD(casscf, mo, nocc, addnoise): mc.fcisolver.davidsonTol = 1.e-6 mo = 1.*m.mo_coeff + + numpy.random.seed(5) noise = numpy.zeros(mo.shape, dtype=complex) noise = numpy.random.random(mo.shape) +\ numpy.random.random(mo.shape)*1.j mo = numpy.dot(mo, expmat(-0.01*(noise - noise.T.conj()))) + + import cProfile + cProfile.run('mc.kernel(mo)') + exit(0) emc = mc.kernel(mo)[0] - + exit(0) print(ehf, emc, emc-ehf) print(emc - -3.22013929407) exit(0) @@ -673,7 +1004,7 @@ def GD(casscf, mo, nocc, addnoise): m = scf.RHF(mol) ehf = m.scf() mc = mc1step.CASSCF(m, 6, 4) - mc.verbose = 4 + mc.verbose = 5 mo = m.mo_coeff.copy() mo[:,2:5] = m.mo_coeff[:,[4,2,3]] emc = mc.mc2step(mo)[0] @@ -681,3 +1012,7 @@ def GD(casscf, mo, nocc, addnoise): #-76.0267656731 -76.0873922924 -0.0606266193028 print(emc - -76.0873923174, emc - -76.0926176464) + + + + \ No newline at end of file diff --git a/pyscf/zmcscf/zmc_ao2mo.py b/pyscf/zmcscf/zmc_ao2mo.py index a92f314552..01b37b0967 100644 --- a/pyscf/zmcscf/zmc_ao2mo.py +++ b/pyscf/zmcscf/zmc_ao2mo.py @@ -9,8 +9,9 @@ -# level = 1: ppaa, papa and vhf, jpc, kpc -# level = 2: ppaa, papa, vhf, jpc=0, kpc=0 + +# level = 1: aaaa +# level = 2: paaa class _ERIS(object): def __init__(self, zcasscf, mo, method='incore', level=1): mol = zcasscf.mol @@ -23,15 +24,26 @@ def __init__(self, zcasscf, mo, method='incore', level=1): mem_incore, mem_outcore, mem_basic = mc_ao2mo._mem_usage(ncore, ncas, nmo) mem_now = lib.current_memory()[0] eri = zcasscf._scf._eri - moc, moa = mo[:,:ncore], mo[:,ncore:nocc] + moc, moa, moo = mo[:,:ncore], mo[:,ncore:nocc], mo[:,:nocc] if (method == 'incore' and eri is not None and (mem_incore+mem_now < zcasscf.max_memory*.9) or mol.incore_anyway): if eri is None: eri = mol.intor('int2e_spinor', aosym='s8') - self.paaa = ao2mo.kernel(eri, (mo, moa, moa, moa), - intor="int2e_spinor") + if level == 1: + self.aaaa = ao2mo.kernel(eri, moa, intor="int2e_spinor") + elif level == 2: + self.paaa = ao2mo.kernel(eri, (mo, moa, moa, moa), + intor="int2e_spinor") + elif level == 3: + self.ppoo = ao2mo.kernel(eri, (mo, moa, moa, moa), + intor="int2e_spinor") + self.papa = ao2mo.kernel(eri, (mo, moa, moa, moa), + intor="int2e_spinor") + self.paaa = ao2mo.kernel(eri, (mo, moa, moa, moa), + intor="int2e_spinor") + else: import gc gc.collect() @@ -41,7 +53,15 @@ def __init__(self, zcasscf, mo, method='incore', level=1): if max_memory < mem_basic: log.warn('Calculation needs %d MB memory, over CASSCF.max_memory (%d MB) limit', (mem_basic+mem_now)/.9, zcasscf.max_memory) - self.paaa = ao2mo.kernel(mol, (mo, moa, moa, moa), - intor="int2e_spinor") + if level == 1: + self.aaaa = ao2mo.kernel(mol, moa, intor="int2e_spinor") + else: + self.paaa = ao2mo.kernel(mol, (mo, moa, moa, moa), + intor="int2e_spinor") - self.paaa.shape = (nmo, ncas, ncas, ncas) + + if (level == 1): + self.aaaa.shape = (ncas, ncas, ncas, ncas) + else: + self.paaa.shape = (nmo, ncas, ncas, ncas) + self.aaaa = self.paaa[ncore:nocc] From f19e818204de88f398a44f6a634b6f8b40e7e57a Mon Sep 17 00:00:00 2001 From: Sandeep Sharma Date: Thu, 8 Oct 2020 23:30:59 -0600 Subject: [PATCH 21/26] small fixes to the orbital optimizer --- pyscf/zmcscf/zmc2step.py | 278 +++++++++++++++++---------------------- 1 file changed, 119 insertions(+), 159 deletions(-) diff --git a/pyscf/zmcscf/zmc2step.py b/pyscf/zmcscf/zmc2step.py index 19d94713c1..e1649d046a 100644 --- a/pyscf/zmcscf/zmc2step.py +++ b/pyscf/zmcscf/zmc2step.py @@ -521,6 +521,7 @@ def calcGradDF(self, mo, casdm1, casdm2): nmo, ncore, nact = mo.shape[0], self.ncore, self.ncas nocc = ncore+nact + nuc_energy = self.energy_nuc() Grad = numpy.zeros((nmo, nmo), dtype=complex) moc, moa = mo[:,:ncore], mo[:,ncore:nocc] @@ -541,7 +542,37 @@ def calcGradDF(self, mo, casdm1, casdm2): paaa = lib.einsum('Lxa,Lcd->xacd', Lrq, Lpq) Grad[:,ncore:nocc]+= lib.einsum('ruvw,tvuw->rt', paaa, casdm2()) - return 2*Grad + E = nuc_energy + 0.5*numpy.sum((hcore+Fc).diagonal()[:ncore]) + E += numpy.einsum('tu, tu', Fc[ncore:nocc, ncore:nocc], casdm1()) + E += 0.5*lib.einsum('ruvw,rvuw', paaa[ncore:nocc], casdm2()) + return 2*Grad, E + + def calcEDF(self, mo, casdm1, casdm2): + hcore = self._scf.get_hcore() + + nmo, ncore, nact = mo.shape[0], self.ncore, self.ncas + nocc = ncore+nact + nuc_energy = self.energy_nuc() + + Grad = numpy.zeros((nmo, nmo), dtype=complex) + moc, moa = mo[:,:ncore], mo[:,ncore:nocc] + dmcore = numpy.dot(moc, moc.T.conj()) + dmcas = reduce(numpy.dot, (moa.conj(), casdm1(), moa.T)).conj() + j,k=get_jk_df(self.cderi, dmcore) + ja,ka=get_jk_df(self.cderi, dmcas) + + hcore = reduce(numpy.dot, (mo.conj().T, hcore , mo)) + Fc = (hcore + reduce(numpy.dot, (mo.conj().T, (j-k), mo))) + E = nuc_energy + 0.5*numpy.sum((hcore+Fc).diagonal()[:ncore]) + E += numpy.einsum('tu, tu', Fc[ncore:nocc, ncore:nocc], casdm1()) + + Lrq = lib.einsum('Lxy,ya->Lxa',self.cderi, moa) + Lpq = lib.einsum('Lxa,xb->Lba', Lrq, moa.conj()) + Lrq = lib.einsum('Lxa,xy->Lya', Lrq, moa.conj()) + paaa = lib.einsum('Lxa,Lcd->xacd', Lrq, Lpq) + E += 0.5*lib.einsum('ruvw,rvuw', paaa, casdm2()) + + return E def calcGrad(self, mo, casdm1, casdm2, ERIS=None): if ERIS is None: ERIS = self.ao2mo(mo,level=2) @@ -549,6 +580,7 @@ def calcGrad(self, mo, casdm1, casdm2, ERIS=None): nmo, ncore, nact = mo.shape[0], self.ncore, self.ncas nocc = ncore+nact + nuc_energy = self.energy_nuc() Grad = numpy.zeros((nmo, nmo), dtype=complex) moc, moa = mo[:,:ncore], mo[:,ncore:nocc] @@ -571,9 +603,11 @@ def calcGrad(self, mo, casdm1, casdm2, ERIS=None): # for j in range(i+1,nocc): # Grad[i,j] = 0.0 #Grad = Grad-Grad.conj().T + Ecore = 0.5*numpy.sum((hcore+Fc).diagonal()[:ncore]) + Ecas1 = numpy.einsum('tu, tu', Fc[ncore:nocc, ncore:nocc], casdm1()) + Ecas2 = 0.5*numpy.einsum('tuvw, tvuw', ERIS.paaa[ncore:nocc], casdm2()) - - return 2*Grad + return 2*Grad, (Ecore+Ecas1+Ecas2+nuc_energy).real def calcGradOld(self, mo, casdm1, casdm2, ERIS=None): @@ -709,10 +743,6 @@ def optimizeOrbs(self, mo, casdm1, casdm2, addnoise, eris, r0, conv_tol, log): Grad, Gradnew = 0.*mo, 0.*mo - ERIS = self.ao2mo(mo) - E = self.calcE(mo, casdm1, casdm2).real - Grad = self.calcGrad(mo, casdm1, casdm2) - T = numpy.zeros((nmo, nmo),dtype=complex) @@ -723,6 +753,10 @@ def NewtonStep(casscf, mo, nocc, Grad): G = casscf.pack_vars(Grad-Grad.conj().T) T, monew = 0*mo, 0.*mo + + Grad0, e = casscf.calcGradDF(mo, casdm1, casdm2) + G0 = casscf.pack_vars(Grad0-Grad0.conj().T) + def hop(x): Gradnewp, Gradnewm = 0.*mo, 0.*mo @@ -730,7 +764,7 @@ def hop(x): Kappa = eps*casscf.unpack_vars(x) monew = numpy.dot(mo, expmat(Kappa)) - Gradnewp = casscf.calcGradDF(monew, casdm1, casdm2) + Gradnewp, e = casscf.calcGradDF(monew, casdm1, casdm2) #print (numpy.linalg.norm(Gradnewp-Grad)) f = numpy.dot(Kappa.conj().T, Gradnewp) @@ -740,7 +774,8 @@ def hop(x): Gnewp= casscf.pack_vars(Gradnewp) - #Hx = (Gnewp - G)/eps + Hx = (Gnewp - G0)/eps + ''' monew = numpy.dot(mo, expmat(-Kappa)) Gradnewm = casscf.calcGradDF(monew, casdm1, casdm2) f = numpy.dot(Kappa.conj().T, Gradnewm) @@ -750,7 +785,8 @@ def hop(x): Gnewm= casscf.pack_vars(Gradnewm) Hx = (Gnewp - Gnewm)/2./eps - + ''' + #print ("hop") return Hx ##we don't have diagonal elements @@ -761,47 +797,92 @@ def precond(x, y): x = 0*G x0 = 1.*G index = 0 + imic = 0 + ikf = 0 + g_op = lambda : G + g_orb = 1.*G + max_stepsize = 0.02 + norm_gkf = norm_gorb = numpy.linalg.norm(g_orb) + for ah_end, ihop, w, dxi, hdxi, residual, seig \ - in ciah.davidson_cc(hop, lambda: G, precond, x0, - tol=casscf.ah_conv_tol, max_cycle=casscf.ah_max_cycle, - lindep=casscf.ah_lindep, verbose=log): - index += 1 - if (index >=casscf.ah_start_cycle): - norm_residual = numpy.linalg.norm(residual) - x += dxi - #print ("doing ah ", norm_residual, w) + in ciah.davidson_cc(hop, g_op, precond, x0, + tol=casscf.ah_conv_tol, max_cycle=casscf.ah_max_cycle, + lindep=casscf.ah_lindep, verbose=log): + # residual = v[0] * (g+(h-e)x) ~ v[0] * grad + norm_residual = numpy.linalg.norm(residual) + + if (ah_end or ihop == casscf.ah_max_cycle or # make sure to use the last step + ((norm_residual < casscf.ah_start_tol) and (ihop >= casscf.ah_start_cycle)) or + (seig < casscf.ah_lindep)): + imic += 1 + dxmax = numpy.max(abs(dxi)) + if dxmax > max_stepsize: + scale = max_stepsize / dxmax + log.debug1('... scale rotation size %g', scale) + dxi *= scale + hdxi *= scale + else: + scale = None + + g_orb = g_orb + hdxi + x = x + dxi + norm_gorb = numpy.linalg.norm(g_orb) + norm_dxi = numpy.linalg.norm(dxi) + norm_dr = numpy.linalg.norm(x) + log.debug(' imic %d(%d) |g[o]|=%5.3g |dxi|=%5.3g ' + 'max(|x|)=%5.3g |dr|=%5.3g eig=%5.3g seig=%5.3g', + imic, ihop, norm_gorb, norm_dxi, + dxmax, norm_dr, w, seig) + + ikf += 1 + if ikf > 1 and norm_gorb > norm_gkf*casscf.ah_grad_trust_region: + g_orb = g_orb - hdxi + x -= dxi + #norm_gorb = numpy.linalg.norm(g_orb) + log.debug('|g| >> keyframe, Restore previouse step') + break + + elif (norm_gorb < 1e-4*.3): + break + + elif (ikf >= max(casscf.kf_interval, -numpy.log(norm_dr+1e-7)) or + # Insert keyframe if the keyframe and the esitimated grad are too different + norm_gorb < norm_gkf/casscf.kf_trust_region): + ikf = 0 + return x, norm_gorb - return x, numpy.linalg.norm(G) + return x, norm_gorb #numpy.linalg.norm(G) - imicro, nmicro, T, Grad = 0, 10, numpy.zeros_like(mo), 0.*mo + imicro, nmicro, T, Grad = 0, 2, numpy.zeros_like(mo), 0.*mo Enew = 0. - Eold = self.calcE(mo, casdm1, casdm2).real + #Eold = self.calcE(mo, casdm1, casdm2).real + Grad, Eold = self.calcGrad(mo, casdm1, casdm2) + Eolddf = self.calcEDF(mo, casdm1, casdm2).real while True: - Grad = self.calcGrad(mo, casdm1, casdm2) gnorm = numpy.linalg.norm(Grad-Grad.conj().T) #if gradient is converged then exit - if ( gnorm < conv_tol or imicro >= nmicro ): - exit(0) + if ( gnorm < conv_tol or imicro >= nmicro ): return mo, Grad, imicro, gnorm #find the newton step direction x, gnorm = NewtonStep(self, mo, nocc, Grad) T = self.unpack_vars(x) - #T[:nocc,nocc:] = -1.*T[nocc:,:nocc].conj().T ###do line search along the AH direction - tau = 0.1 + tau = 1.0 while tau > 1e-3: monew = numpy.dot(mo, expmat(tau*(T) )) - Enew = self.calcE(monew, casdm1, casdm2).real + Enewdf = self.calcEDF(monew, casdm1, casdm2).real - if (Enew < Eold):# - tau * 1e-4*gnorm): + if (Enewdf < Eolddf):# - tau * 1e-4*gnorm): + Grad, Enew = self.calcGrad(monew, casdm1, casdm2) print ("%d %6.3e %13.7e %13.6e g=%6.2e"\ %(imicro, tau, Enew, Enew-Eold, gnorm)) Eold = Enew + Eolddf = Enewdf mo = 1.*monew break tau = tau/2. @@ -811,128 +892,7 @@ def precond(x, y): - def optimizeOrbsDep1(self, mo, casdm1, casdm2, addnoise, eris, r0, conv_tol, log): - #the part of mo that is relevant - nmo, ncore, nact = mo.shape[0], self.ncore, self.ncas - nocc = ncore+nact - - ERIS = casscf.ao2mo(mo) - Eold = casscf.calcE(mo, casdm1, casdm2, ERIS).real - Grad[:,:nocc] = casscf.calcGrad(mo, casdm1, casdm2, ERIS) - - def GD(casscf, mo, nocc, addnoise, ERIS): - tau = 0.01 - Grad, Gradnew = 0.*mo, 0.*mo - ERIS = casscf.ao2mo(mo) - Eold = casscf.calcE(mo, casdm1, casdm2, ERIS).real - Grad[:,:nocc] = casscf.calcGrad(mo, casdm1, casdm2, ERIS) - monew = 1.*numpy.dot(mo, expmat(-tau*(Grad-Grad.T.conj()))) - Enew = casscf.calcE(monew, casdm1, casdm2, ERIS).real - ERIS = casscf.ao2mo(monew) - - for iter in range(1000): - G = Grad - Grad.conj().T - if (iter == 0): - normG0 = numpy.linalg.norm(G) - print ("%d %6.3e %13.7e %13.6e g=%6.2e"\ - %(iter, tau, Enew, Enew-Eold, numpy.linalg.norm(G))) - #print (numpy.linalg.norm(reduce(numpy.dot, (mo.conj().T, S, mo))-numpy.eye(mo.shape[0]))) - - if (iter == 90): - Enew = casscf.calcE(monew, casdm1, casdm2, ERIS).real - Eold = Enew - - Gradnew[:,:nocc] = casscf.calcGrad(monew, casdm1, casdm2, ERIS) - - tau = 1.0 - mo, Grad = 1.*monew, 1.*Gradnew - monew = numpy.dot(mo, expmat(-tau*(Grad-Grad.T.conj()))) - Gnorm = numpy.linalg.norm(Grad-Grad.conj().T) - - while tau > 1e-3: - monew = numpy.dot(mo, expmat(-tau*(Grad-Grad.T.conj()))) - ERIS = casscf.ao2mo(monew) - Enew = casscf.calcE(monew, casdm1, casdm2, ERIS).real - - if (Enew < Eold - tau*1.e-4*Gnorm): - break - else: - tau *= 0.5 - if (tau < 1.e-3 or Gnorm < conv_tol or abs(Enew-Eold)<1.e-9): - print ("%d %6.3e %13.7e %13.6e g=%6.2e"\ - %(iter, tau, Enew, Enew-Eold, numpy.linalg.norm(G))) - #print ("encountered small step size") - break - return mo, Grad-Grad.T.conj(), iter, normG0 - return GD(self, mo, nocc, addnoise) - - def optimizeOrbsDep1B(self, mo, casdm1, casdm2, addnoise, eris, r0, conv_tol, log): - #the part of mo that is relevant - nmo, ncore, nact = mo.shape[0], self.ncore, self.ncas - nocc = ncore+nact - - Grad = 0*mo - ERIS = self.ao2mo(mo) - Eold = self.calcE(mo, casdm1, casdm2, ERIS).real - Grad = self.calcGrad(mo, casdm1, casdm2, ERIS) - S = self._scf.get_ovlp() - - def GD(casscf, mo, nocc, addnoise, ERIS=None): - tau = -0.01 - Grad, Gradnew = 0.*mo, 0.*mo - ERIS = casscf.ao2mo(mo) - Eold = casscf.calcE(mo, casdm1, casdm2, ERIS).real - Grad = casscf.calcGrad(mo, casdm1, casdm2, ERIS) - - #first chec - Kappa = reduce(numpy.dot, (mo.conj().T, S, Grad)) - Kappa = Kappa - Kappa.T.conj() - monew = 1.*numpy.dot(mo, expmat(-tau*(Kappa))) - - Enew = casscf.calcE(monew, casdm1, casdm2, ERIS).real - ERIS = casscf.ao2mo(monew) - - for iter in range(1000): - G = Grad - Grad.conj().T - if (iter == 0): - normG0 = numpy.linalg.norm(G) - print ("%d %6.3e %13.7e %13.6e g=%6.2e"\ - %(iter, tau, Enew, Enew-Eold, numpy.linalg.norm(G))) - #print (numpy.linalg.norm(reduce(numpy.dot, (mo.conj().T, S, mo))-numpy.eye(mo.shape[0]))) - - if (iter == 90): - Enew = casscf.calcE(monew, casdm1, casdm2, ERIS).real - Eold = Enew - - Gradnew = casscf.calcGradAO(monew, casdm1, casdm2, ERIS) - - tau = 1.0 - mo, Grad = 1.*monew, 1.*Gradnew - - Kappa = reduce(numpy.dot, (mo.conj().T, S, Grad)) - Kappa = Kappa - Kappa.T.conj() - - #monew = numpy.dot(mo, expmat(-tau*(Grad-Grad.T.conj()))) - Gnorm = numpy.linalg.norm(Kappa) - - while tau > 1e-5: - monew = numpy.dot(mo, expmat(-tau*(Kappa))) - ERIS = casscf.ao2mo(monew) - Enew = casscf.calcE(monew, casdm1, casdm2, ERIS).real - - if (Enew < Eold): - break - else: - tau *= 0.5 - if (tau < 1.e-3 or Gnorm < conv_tol or abs(Enew-Eold)<1.e-9): - print ("%d %6.3e %13.7e %13.6e g=%6.2e"\ - %(iter, tau, Enew, Enew-Eold, numpy.linalg.norm(G))) - #print ("encountered small step size") - break - return mo, Grad-Grad.T.conj(), iter, normG0 - - return GD(self, mo, nocc, addnoise) if __name__ == '__main__': from pyscf import gto @@ -940,7 +900,7 @@ def GD(casscf, mo, nocc, addnoise, ERIS=None): mol = gto.Mole() mol.verbose = 4 - mol.memory=200 + mol.memory=20000 mol.output = None#"out_h2o" mol.atom = [ ['H', ( 1.,-1. , 0. )], @@ -953,7 +913,7 @@ def GD(casscf, mo, nocc, addnoise, ERIS=None): ['H', ( 0., 1. , 1. )], ] - mol.basis = '6-31g' + mol.basis = 'cc-pvtz' #mol.basis = 'sto-3g' mol.build() @@ -978,15 +938,15 @@ def GD(casscf, mo, nocc, addnoise, ERIS=None): mo = 1.*m.mo_coeff - numpy.random.seed(5) - noise = numpy.zeros(mo.shape, dtype=complex) - noise = numpy.random.random(mo.shape) +\ - numpy.random.random(mo.shape)*1.j - mo = numpy.dot(mo, expmat(-0.01*(noise - noise.T.conj()))) + #numpy.random.seed(5) + #noise = numpy.zeros(mo.shape, dtype=complex) + #noise = numpy.random.random(mo.shape) +\ + # numpy.random.random(mo.shape)*1.j + #mo = numpy.dot(mo, expmat(-0.01*(noise - noise.T.conj()))) - import cProfile - cProfile.run('mc.kernel(mo)') - exit(0) + #import cProfile + #cProfile.run('mc.kernel(mo)') + #exit(0) emc = mc.kernel(mo)[0] exit(0) print(ehf, emc, emc-ehf) From 85cc57e1def639598f8def6bb040f0c563162cb6 Mon Sep 17 00:00:00 2001 From: Sandeep Sharma Date: Sun, 11 Oct 2020 13:32:48 -0600 Subject: [PATCH 22/26] implemented conjugate gradient to solve newton step. --- pyscf/zmcscf/zmc2step.py | 77 +++++++++++++++++++++++++++++++--------- 1 file changed, 60 insertions(+), 17 deletions(-) diff --git a/pyscf/zmcscf/zmc2step.py b/pyscf/zmcscf/zmc2step.py index e1649d046a..d61acf0607 100644 --- a/pyscf/zmcscf/zmc2step.py +++ b/pyscf/zmcscf/zmc2step.py @@ -46,7 +46,30 @@ def derivOfExp(a,dA, maxT=50): return deriv -def get_jk_df(cderi, dm, with_j=True, with_k=True): +def conjugateGradient(A, b, x, max_iter=5, conv=1.e-4): + r = b - A(x) + p = 1*r + rsold = numpy.dot(r,r) + + for i in range(max_iter): + Ap = A(p) + alpha = rsold / numpy.dot(p, Ap) + x += alpha * p + r -= alpha * Ap + rsnew = numpy.dot(r,r) + if (rsnew)**0.5 < conv: + break + p = r + (rsnew / rsold) * p + #print ("cg, ", i, rsnew**0.5) + if (i == 5): + #reset it + r = b - A(x) + p = 1*r + rsnew = numpy.dot(r,r) + rsold = rsnew + return x, rsnew**0.5 + +def get_jk_df(mol, cderi, dm, with_j=True, with_k=True): if (with_j): j1 = lib.einsum('Lij,ji->L', cderi, dm) j = lib.einsum('Lij,L->ij', cderi, j1) @@ -796,6 +819,9 @@ def precond(x, y): x = 0*G x0 = 1.*G + x, norm = conjugateGradient(hop, -G, x0) + return x, norm + ''' index = 0 imic = 0 ikf = 0 @@ -850,15 +876,15 @@ def precond(x, y): norm_gorb < norm_gkf/casscf.kf_trust_region): ikf = 0 return x, norm_gorb - + ''' return x, norm_gorb #numpy.linalg.norm(G) + - - imicro, nmicro, T, Grad = 0, 2, numpy.zeros_like(mo), 0.*mo + imicro, nmicro, T, Grad = 0, 5, numpy.zeros_like(mo), 0.*mo Enew = 0. #Eold = self.calcE(mo, casdm1, casdm2).real Grad, Eold = self.calcGrad(mo, casdm1, casdm2) - Eolddf = self.calcEDF(mo, casdm1, casdm2).real + Eolddf = self.calcE(mo, casdm1, casdm2).real while True: gnorm = numpy.linalg.norm(Grad-Grad.conj().T) @@ -873,20 +899,27 @@ def precond(x, y): ###do line search along the AH direction tau = 1.0 + ''' + monew = numpy.dot(mo, expmat(tau*(T) )) + Grad, Enew = self.calcGrad(monew, casdm1, casdm2) + print ("%d %6.3e %18.12g %13.6e g=%6.2e"\ + %(imicro, tau, Enew, Enew-Eold, gnorm)) + Eold = Enew + mo = 1.*monew + ''' while tau > 1e-3: monew = numpy.dot(mo, expmat(tau*(T) )) - Enewdf = self.calcEDF(monew, casdm1, casdm2).real - + Enewdf = self.calcE(monew, casdm1, casdm2).real + #print ("line search ", Enewdf, Eolddf) if (Enewdf < Eolddf):# - tau * 1e-4*gnorm): Grad, Enew = self.calcGrad(monew, casdm1, casdm2) - print ("%d %6.3e %13.7e %13.6e g=%6.2e"\ + print ("%d %6.3e %18.12g %13.6e g=%6.2e"\ %(imicro, tau, Enew, Enew-Eold, gnorm)) Eold = Enew Eolddf = Enewdf mo = 1.*monew break tau = tau/2. - imicro += 1 exit(0) @@ -913,8 +946,8 @@ def precond(x, y): ['H', ( 0., 1. , 1. )], ] - mol.basis = 'cc-pvtz' - #mol.basis = 'sto-3g' + #mol.basis = 'cc-pvtz' + mol.basis = '6-31g' mol.build() ''' @@ -925,6 +958,7 @@ def precond(x, y): emc = mc.kernel()[0] print (emc) ''' + ''' m = scf.X2C(mol) #m = scf.GHF(mol) ehf = m.kernel() @@ -937,7 +971,7 @@ def precond(x, y): mc.fcisolver.davidsonTol = 1.e-6 mo = 1.*m.mo_coeff - + ''' #numpy.random.seed(5) #noise = numpy.zeros(mo.shape, dtype=complex) #noise = numpy.random.random(mo.shape) +\ @@ -947,22 +981,31 @@ def precond(x, y): #import cProfile #cProfile.run('mc.kernel(mo)') #exit(0) + + ''' emc = mc.kernel(mo)[0] exit(0) print(ehf, emc, emc-ehf) print(emc - -3.22013929407) exit(0) - + ''' mol.atom = [ ['O', ( 0., 0. , 0. )], ['H', ( 0., -0.757, 0.587)], ['H', ( 0., 0.757 , 0.587)],] - mol.basis = {'H': 'cc-pvdz', - 'O': 'cc-pvdz',} + mol.basis = {'H': 'cc-pvtz', + 'O': 'cc-pvtz',} mol.build() - m = scf.RHF(mol) + m = scf.DHF(mol) ehf = m.scf() + + from pyscf.df import density_fit + m2 = density_fit(m, "cc-pvtz-jkfit") + energy = m2.scf() + print (ehf, energy) + exit(0) + mc = mc1step.CASSCF(m, 6, 4) mc.verbose = 5 mo = m.mo_coeff.copy() @@ -975,4 +1018,4 @@ def precond(x, y): - \ No newline at end of file + From 803f3d624d4c33b9298cec8d0b4fa0c1f48fe26d Mon Sep 17 00:00:00 2001 From: Sandeep Sharma Date: Wed, 21 Oct 2020 13:43:46 -0600 Subject: [PATCH 23/26] small bug fix to zmc2step. --- pyscf/zmcscf/zmc2step.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pyscf/zmcscf/zmc2step.py b/pyscf/zmcscf/zmc2step.py index d61acf0607..ef3c603eca 100644 --- a/pyscf/zmcscf/zmc2step.py +++ b/pyscf/zmcscf/zmc2step.py @@ -69,7 +69,7 @@ def conjugateGradient(A, b, x, max_iter=5, conv=1.e-4): rsold = rsnew return x, rsnew**0.5 -def get_jk_df(mol, cderi, dm, with_j=True, with_k=True): +def get_jk_df(cderi, dm, with_j=True, with_k=True): if (with_j): j1 = lib.einsum('Lij,ji->L', cderi, dm) j = lib.einsum('Lij,L->ij', cderi, j1) @@ -505,8 +505,8 @@ def calcGradAO(self, mo, casdm1, casdm2): dmcas = reduce(numpy.dot, (moa.conj(), casdm1(), moa.T)).conj() dmcore = numpy.dot(moc, moc.conj().T) - jc,kc = self._scf.get_jk(self.mol, dm=dmcore) - ja,ka = self._scf.get_jk(self.mol, dm=dmcas) + jc,kc = self._scf.get_jk(self.cderi, dm=dmcore) + ja,ka = self._scf.get_jk(self.cderi, dm=dmcas) gradC2[:,:ncore] = numpy.dot( (hcore + (jc-kc)+ja-ka) , moc) @@ -958,7 +958,7 @@ def precond(x, y): emc = mc.kernel()[0] print (emc) ''' - ''' + m = scf.X2C(mol) #m = scf.GHF(mol) ehf = m.kernel() @@ -971,13 +971,13 @@ def precond(x, y): mc.fcisolver.davidsonTol = 1.e-6 mo = 1.*m.mo_coeff - ''' - #numpy.random.seed(5) - #noise = numpy.zeros(mo.shape, dtype=complex) - #noise = numpy.random.random(mo.shape) +\ - # numpy.random.random(mo.shape)*1.j - #mo = numpy.dot(mo, expmat(-0.01*(noise - noise.T.conj()))) + numpy.random.seed(5) + noise = numpy.zeros(mo.shape, dtype=complex) + noise = numpy.random.random(mo.shape) +\ + numpy.random.random(mo.shape)*1.j + mo = numpy.dot(mo, expmat(-0.01*(noise - noise.T.conj()))) + mc.kernel(mo) #import cProfile #cProfile.run('mc.kernel(mo)') #exit(0) From cafd1a16de34ad72cf7de1e30acea3676bb7f2df Mon Sep 17 00:00:00 2001 From: Sandeep Sharma Date: Wed, 21 Oct 2020 14:49:10 -0600 Subject: [PATCH 24/26] twordm->onerdm obained from the binary file (relativisitic) is changed --- pyscf/shciscf/shci.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyscf/shciscf/shci.py b/pyscf/shciscf/shci.py index 54bbd5fc8c..81b2cf5f2c 100644 --- a/pyscf/shciscf/shci.py +++ b/pyscf/shciscf/shci.py @@ -330,7 +330,7 @@ def make_rdm12Frombin(self, state, norb, nelec): twopdm = numpy.fromfile(f, dtype=complex) #twopdm = numpy.reshape(twopdm, (norb, norb, norb, norb)) twopdm = numpy.reshape(twopdm, (norb, norb, norb, norb), order='F') - onepdm = numpy.einsum('ijkj->ki', twopdm)/(nelectrons-1) + onepdm = numpy.einsum('ijkj->ik', twopdm)/(nelectrons-1) return onepdm, twopdm def make_rdm12(self, state, norb, nelec, link_index=None, **kwargs): From f621257771da20361b2035ad568ce78829346adf Mon Sep 17 00:00:00 2001 From: Sandeep Sharma Date: Sun, 22 Nov 2020 14:59:45 -0700 Subject: [PATCH 25/26] now using gmres instead of conjugate gradient because hessian is not positive definite. --- pyscf/zmcscf/__init__.py | 1 - pyscf/zmcscf/zmc2step.py | 206 +++++++++++++++++++++------------------ 2 files changed, 110 insertions(+), 97 deletions(-) diff --git a/pyscf/zmcscf/__init__.py b/pyscf/zmcscf/__init__.py index 3b0044a5aa..34c291e954 100644 --- a/pyscf/zmcscf/__init__.py +++ b/pyscf/zmcscf/__init__.py @@ -38,5 +38,4 @@ def ZCASSCF(mf_or_mol, ncas, nelecas, ncore=None, frozen=None): mc = zmc2step.ZCASSCF(mf, ncas, nelecas, ncore, frozen) return mc -RCASSCF = CASSCF diff --git a/pyscf/zmcscf/zmc2step.py b/pyscf/zmcscf/zmc2step.py index ef3c603eca..0aba37eb8b 100644 --- a/pyscf/zmcscf/zmc2step.py +++ b/pyscf/zmcscf/zmc2step.py @@ -46,7 +46,43 @@ def derivOfExp(a,dA, maxT=50): return deriv -def conjugateGradient(A, b, x, max_iter=5, conv=1.e-4): +def arnoldi(A, Q, H, k): + Q[:,k+1] = A(Q[:,k]) + for i in range(k+1): + H[i, k] = numpy.dot(Q[:,i], Q[:,k+1]) + Q[:,k+1] = Q[:,k+1] - H[i,k] * Q[:,i] + + H[k+1,k] = numpy.linalg.norm(Q[:,k+1]) + Q[:,k+1] = Q[:,k+1]/H[k+1,k] + +def GMRES(A, b, x, max_iter=20, conv=1.e-5): + from numpy.linalg import norm + m, n = max_iter, b.shape[0] + + r = b - A(x) + b_norm, r_norm = norm(b), norm(r) + error = r_norm/b_norm + + e1 = numpy.zeros((m+1,)) + e1[0] = r_norm + + Q, H = numpy.zeros((n,m)), numpy.zeros((m+1,m)) + Q[:,0] = r/r_norm + beta = r_norm * e1 + + for k in range(max_iter-1): + arnoldi(A, Q, H, k) + + result = numpy.linalg.lstsq(H, e1, rcond=None)[0] + err = norm(numpy.dot(H,result)-e1) + #print (k, err) + x = numpy.dot(Q, result) + + #print (norm(A(x)-b)) + return x, err + + +def conjugateGradient(A, b, x, max_iter=10, conv=1.e-4): r = b - A(x) p = 1*r rsold = numpy.dot(r,r) @@ -60,7 +96,7 @@ def conjugateGradient(A, b, x, max_iter=5, conv=1.e-4): if (rsnew)**0.5 < conv: break p = r + (rsnew / rsold) * p - #print ("cg, ", i, rsnew**0.5) + print ("cg, ", i, rsnew**0.5) if (i == 5): #reset it r = b - A(x) @@ -68,8 +104,30 @@ def conjugateGradient(A, b, x, max_iter=5, conv=1.e-4): rsnew = numpy.dot(r,r) rsold = rsnew return x, rsnew**0.5 - -def get_jk_df(cderi, dm, with_j=True, with_k=True): +def _sqrt(a, tol=1e-14): + e, v = numpy.linalg.eigh(a) + idx = e > tol + return numpy.dot(v[:,idx]*numpy.sqrt(e[idx]), v[:,idx].T.conj()) + +def get_jk_df(cderi, mo_coeff,dm=None, with_j=True, with_k=True): + norb = mo_coeff.shape[1] + mo = mo_coeff + + if dm is None: + dm_ao = numpy.dot(mo, mo.T.conj()) + else: + dm_sqrt = _sqrt(dm) + dm_ao = reduce(numpy.dot, (mo.conj(), dm, mo.T)).conj() + mo = lib.einsum('ij,jk->ik', mo_coeff, dm_sqrt) + + if (with_j): + j1 = lib.einsum('Lij,ji->L', cderi, dm_ao) + j = lib.einsum('Lij,L->ij', cderi, j1) + if (with_k): + cderi_half_trans = lib.einsum('pij,jk->pik', cderi, mo) + k = lib.einsum('pik,pkj->ij', cderi_half_trans, cderi_half_trans.transpose(0,2,1).conj()) + return j,k + ''' if (with_j): j1 = lib.einsum('Lij,ji->L', cderi, dm) j = lib.einsum('Lij,L->ij', cderi, j1) @@ -77,7 +135,7 @@ def get_jk_df(cderi, dm, with_j=True, with_k=True): kint = lib.einsum('Lij,ja->Lia', cderi, dm) k = lib.einsum('Laj,Lia->ij', cderi, kint) return j,k - + ''' def kernel(casscf, mo_coeff, tol=1e-7, conv_tol_grad=None, ci0=None, callback=None, verbose=None, dump_chk=True): if verbose is None: @@ -278,7 +336,7 @@ class ZCASSCF(gzcasci.GZCASCI): canonicalization = getattr(__config__, 'zmcscf_zmc2step_ZCASSCF_canonicalization', True) sorting_mo_energy = getattr(__config__, 'zmcscf_zmc2step_ZCASSCF_sorting_mo_energy', False) - def __init__(self, mf_or_mol, ncas, nelecas, ncore=None, frozen=None): + def __init__(self, mf_or_mol, ncas, nelecas, auxbasis = 'weigend+etb', ncore=None, frozen=None): gzcasci.GZCASCI.__init__(self, mf_or_mol, ncas, nelecas, ncore) self.frozen = frozen @@ -301,9 +359,9 @@ def __init__(self, mf_or_mol, ncas, nelecas, ncore=None, frozen=None): self._max_stepsize = None #calculate the integrals - self.cderi = pyscf.df.r_incore.cholesky_eri(mol, int3c='int3c2e_spinor') + self.cderi = pyscf.df.r_incore.cholesky_eri(self.mol, auxbasis=auxbasis, int3c='int3c2e_spinor') self.cderi.shape = (self.cderi.shape[0], self.mo_coeff.shape[0], self.mo_coeff.shape[1]) - + print ("shape", self.cderi.shape) keys = set(('max_cycle_macro', 'conv_tol', 'conv_tol_grad', 'bb_conv_tol', 'bb_max_cycle', @@ -540,6 +598,7 @@ def calcGradAO(self, mo, casdm1, casdm2): def calcGradDF(self, mo, casdm1, casdm2): + #return self.calcGrad(mo, casdm1, casdm2) hcore = self._scf.get_hcore() nmo, ncore, nact = mo.shape[0], self.ncore, self.ncas @@ -550,8 +609,8 @@ def calcGradDF(self, mo, casdm1, casdm2): moc, moa = mo[:,:ncore], mo[:,ncore:nocc] dmcore = numpy.dot(moc, moc.T.conj()) dmcas = reduce(numpy.dot, (moa.conj(), casdm1(), moa.T)).conj() - j,k=get_jk_df(self.cderi, dmcore) - ja,ka=get_jk_df(self.cderi, dmcas) + j,k=get_jk_df(self.cderi, moc) + ja,ka=get_jk_df(self.cderi, moa, dm= casdm1()) hcore = reduce(numpy.dot, (mo.conj().T, hcore , mo)) Fc = (hcore + reduce(numpy.dot, (mo.conj().T, (j-k), mo))) @@ -568,6 +627,7 @@ def calcGradDF(self, mo, casdm1, casdm2): E = nuc_energy + 0.5*numpy.sum((hcore+Fc).diagonal()[:ncore]) E += numpy.einsum('tu, tu', Fc[ncore:nocc, ncore:nocc], casdm1()) E += 0.5*lib.einsum('ruvw,rvuw', paaa[ncore:nocc], casdm2()) + return 2*Grad, E def calcEDF(self, mo, casdm1, casdm2): @@ -581,8 +641,7 @@ def calcEDF(self, mo, casdm1, casdm2): moc, moa = mo[:,:ncore], mo[:,ncore:nocc] dmcore = numpy.dot(moc, moc.T.conj()) dmcas = reduce(numpy.dot, (moa.conj(), casdm1(), moa.T)).conj() - j,k=get_jk_df(self.cderi, dmcore) - ja,ka=get_jk_df(self.cderi, dmcas) + j,k=get_jk_df(self.cderi, moc) hcore = reduce(numpy.dot, (mo.conj().T, hcore , mo)) Fc = (hcore + reduce(numpy.dot, (mo.conj().T, (j-k), mo))) @@ -621,15 +680,11 @@ def calcGrad(self, mo, casdm1, casdm2, ERIS=None): Grad[:,ncore:nocc] = numpy.einsum('sp,qp->sq', Fc[:,ncore:nocc], casdm1()) Grad[:,ncore:nocc]+= numpy.einsum('ruvw,tvuw->rt', ERIS.paaa, casdm2()) - ###Make the lower traingular zero - #for i in range(ncore,nocc): - # for j in range(i+1,nocc): - # Grad[i,j] = 0.0 - #Grad = Grad-Grad.conj().T Ecore = 0.5*numpy.sum((hcore+Fc).diagonal()[:ncore]) Ecas1 = numpy.einsum('tu, tu', Fc[ncore:nocc, ncore:nocc], casdm1()) Ecas2 = 0.5*numpy.einsum('tuvw, tvuw', ERIS.paaa[ncore:nocc], casdm2()) + return 2*Grad, (Ecore+Ecas1+Ecas2+nuc_energy).real @@ -800,7 +855,7 @@ def hop(x): Hx = (Gnewp - G0)/eps ''' monew = numpy.dot(mo, expmat(-Kappa)) - Gradnewm = casscf.calcGradDF(monew, casdm1, casdm2) + Gradnewm,e = casscf.calcGradDF(monew, casdm1, casdm2) f = numpy.dot(Kappa.conj().T, Gradnewm) h = numpy.dot(Gradnewm, Kappa.conj().T) Gradnewm = Gradnewm -0.5*(f-h) #- Gtemp.conj().T - 0.5*(f-f.conj().T+h-h.conj().T) @@ -812,85 +867,27 @@ def hop(x): #print ("hop") return Hx - ##we don't have diagonal elements - def precond(x, y): - return x x = 0*G - x0 = 1.*G - x, norm = conjugateGradient(hop, -G, x0) + x0 = 0.*G + x, norm = GMRES(hop, -G, x0) + #x, stat = scipy.sparse.linalg.gmres(hop, -G, x0,maxiter = 10) return x, norm - ''' - index = 0 - imic = 0 - ikf = 0 - g_op = lambda : G - g_orb = 1.*G - max_stepsize = 0.02 - norm_gkf = norm_gorb = numpy.linalg.norm(g_orb) - - for ah_end, ihop, w, dxi, hdxi, residual, seig \ - in ciah.davidson_cc(hop, g_op, precond, x0, - tol=casscf.ah_conv_tol, max_cycle=casscf.ah_max_cycle, - lindep=casscf.ah_lindep, verbose=log): - # residual = v[0] * (g+(h-e)x) ~ v[0] * grad - norm_residual = numpy.linalg.norm(residual) - - if (ah_end or ihop == casscf.ah_max_cycle or # make sure to use the last step - ((norm_residual < casscf.ah_start_tol) and (ihop >= casscf.ah_start_cycle)) or - (seig < casscf.ah_lindep)): - imic += 1 - dxmax = numpy.max(abs(dxi)) - if dxmax > max_stepsize: - scale = max_stepsize / dxmax - log.debug1('... scale rotation size %g', scale) - dxi *= scale - hdxi *= scale - else: - scale = None - - g_orb = g_orb + hdxi - x = x + dxi - norm_gorb = numpy.linalg.norm(g_orb) - norm_dxi = numpy.linalg.norm(dxi) - norm_dr = numpy.linalg.norm(x) - log.debug(' imic %d(%d) |g[o]|=%5.3g |dxi|=%5.3g ' - 'max(|x|)=%5.3g |dr|=%5.3g eig=%5.3g seig=%5.3g', - imic, ihop, norm_gorb, norm_dxi, - dxmax, norm_dr, w, seig) - - ikf += 1 - if ikf > 1 and norm_gorb > norm_gkf*casscf.ah_grad_trust_region: - g_orb = g_orb - hdxi - x -= dxi - #norm_gorb = numpy.linalg.norm(g_orb) - log.debug('|g| >> keyframe, Restore previouse step') - break - - elif (norm_gorb < 1e-4*.3): - break - - elif (ikf >= max(casscf.kf_interval, -numpy.log(norm_dr+1e-7)) or - # Insert keyframe if the keyframe and the esitimated grad are too different - norm_gorb < norm_gkf/casscf.kf_trust_region): - ikf = 0 - return x, norm_gorb - ''' - return x, norm_gorb #numpy.linalg.norm(G) imicro, nmicro, T, Grad = 0, 5, numpy.zeros_like(mo), 0.*mo Enew = 0. #Eold = self.calcE(mo, casdm1, casdm2).real Grad, Eold = self.calcGrad(mo, casdm1, casdm2) - Eolddf = self.calcE(mo, casdm1, casdm2).real + Eolddf = self.calcEDF(mo, casdm1, casdm2).real while True: + tau = 1.0 gnorm = numpy.linalg.norm(Grad-Grad.conj().T) #if gradient is converged then exit - if ( gnorm < conv_tol or imicro >= nmicro ): - return mo, Grad, imicro, gnorm + if ( gnorm < conv_tol or imicro >= nmicro or tau <= 1.e-2): + return mo, Grad-Grad.conj().T, imicro, gnorm #find the newton step direction @@ -898,20 +895,11 @@ def precond(x, y): T = self.unpack_vars(x) ###do line search along the AH direction - tau = 1.0 - ''' - monew = numpy.dot(mo, expmat(tau*(T) )) - Grad, Enew = self.calcGrad(monew, casdm1, casdm2) - print ("%d %6.3e %18.12g %13.6e g=%6.2e"\ - %(imicro, tau, Enew, Enew-Eold, gnorm)) - Eold = Enew - mo = 1.*monew - ''' - while tau > 1e-3: + while tau > 1e-2: monew = numpy.dot(mo, expmat(tau*(T) )) - Enewdf = self.calcE(monew, casdm1, casdm2).real + Enewdf = self.calcEDF(monew, casdm1, casdm2).real #print ("line search ", Enewdf, Eolddf) - if (Enewdf < Eolddf):# - tau * 1e-4*gnorm): + if (Enewdf < Eolddf or tau/2 <= 1.e-3):# - tau * 1e-4*gnorm): Grad, Enew = self.calcGrad(monew, casdm1, casdm2) print ("%d %6.3e %18.12g %13.6e g=%6.2e"\ %(imicro, tau, Enew, Enew-Eold, gnorm)) @@ -921,7 +909,7 @@ def precond(x, y): break tau = tau/2. imicro += 1 - exit(0) + @@ -931,6 +919,32 @@ def precond(x, y): from pyscf import gto from pyscf import scf + + mol = gto.Mole() + mol = gto.M( + atom = [["N" , (0. , 0. , 0.)], + ["N" , (0. , 0. , 1.2)] ], + basis = "cc-pvtz", + verbose = 4) + mf = scf.X2C(mol)#.density_fit(auxbasis='cc-pvtz-jkfit') + #mf.max_cycle = 10 + mf.kernel() + + from pyscf import mcscf, zmcscf + from pyscf.shciscf import shci + mf2c = scf.X2C(mol) #.density_fit(auxbasis='cc-pvtz-jkfit') + #mf2c.get_jk = functools.partial(get_jk, mf2c) + #mf2c.max_cycle = 5 + mf2c.chkfile = "scf.chk" + #mf2c.init_guess = 'chkfile' + mf2c.kernel() + + mc2c = zmcscf.zmc2step.ZCASSCF(mf2c, 16, 10, auxbasis='cc-pvtz-jkfit') + mc2c.fcisolver = shci.SHCI(mol) + mc2c.fcisolver.sweep_epsilon = [1e-5] + mc2c.fcisolver.sweep_iter = [0] + mc2c.kernel() + exit(0) mol = gto.Mole() mol.verbose = 4 mol.memory=20000 From 1bda546ab6a2cff2cf16a2555e39ca8686883701 Mon Sep 17 00:00:00 2001 From: Sandeep Sharma Date: Sun, 10 Jan 2021 12:07:30 -0700 Subject: [PATCH 26/26] the flag dospinrdm is explicitly added in shci and when ecp is used then soc integrals do not require the 2-electron part --- pyscf/shciscf/shci.py | 1 + pyscf/shciscf/socutils.py | 81 +++++++++++++++++++++------------------ 2 files changed, 45 insertions(+), 37 deletions(-) diff --git a/pyscf/shciscf/shci.py b/pyscf/shciscf/shci.py index 81b2cf5f2c..91bb11d2b2 100644 --- a/pyscf/shciscf/shci.py +++ b/pyscf/shciscf/shci.py @@ -200,6 +200,7 @@ def __init__(self, mol=None, maxM=None, tol=None, num_thrds=1, self.io = True self.nroots = 1 self.nPTiter = 0 + self.DoSpinRDM = False self.DoRDM = True self.DoTRDM = False self.DoSOC = False diff --git a/pyscf/shciscf/socutils.py b/pyscf/shciscf/socutils.py index 6dbc9e5c89..20e1ad4840 100644 --- a/pyscf/shciscf/socutils.py +++ b/pyscf/shciscf/socutils.py @@ -436,12 +436,16 @@ def get_fso2e_bp1c(mol, dm, atomlist): return hso1e -def writeGTensorIntegrals(mc, atomlist=None): +def writeGTensorIntegrals(mc, atomlist=None, origin=None): mol = mc.mol ncore, ncas = mc.ncore, mc.ncas nb = mol.nao_nr() + + if (origin is not None): + mol.set_common_origin(origin) + if atomlist is None: - h1ao = mol.intor('cint1e_cg_irxp_sph', comp=3) + h1ao = mol.intor('int1e_cg_irxp_sph', comp=3, hermi=2) else: h1ao = numpy.zeros((3, nb, nb)) aoslice = mc.mol.aoslice_by_atom() @@ -455,6 +459,7 @@ def writeGTensorIntegrals(mc, atomlist=None): jshl_start = aoslice[jatom, 0] ishl_end = aoslice[iatom, 1] jshl_end = aoslice[jatom, 1] + h1ao[:, iao_start: iao_end, jao_start: jao_end]\ += mol.intor('cint1e_cg_irxp_sph', 3, shls_slice=[ishl_start, ishl_end, jshl_start, jshl_end]).reshape(3, iao_end-iao_start, jao_end-jao_start) @@ -514,41 +519,43 @@ def writeSOCIntegrals(mc, if (has_ecp): hso1e += xmol.intor('ECPso') - # two electron terms - logger.note(xmol, "Start calculating two-electron contribution to SOC integrals, be patient.") - if (pictureChange2e == "bp"): - hso1e += -factor * get_fso2e_bp(xmol, dm) - elif (pictureChange2e == "bp1c"): - hso1e += -factor * get_fso2e_bp1c(xmol, dm, atomlist) - elif (pictureChange2e == "x2c_old"): - pLL, pLS, pSS = get_p(dm / 2.0, x, rp) - hso1e += -factor * get_fso2e_x2c_original(xmol, x, rp, pLL, pLS, pSS) - elif (pictureChange2e == "x2c"): - pLL, pLS, pSS = get_p(dm / 2.0, x, rp) - #hso1e += -factor * get_fso2e_x2c(xmol, x, rp, pLL, pLS, pSS) - hso1e[0] += -factor * get_fso2e_x2c(xmol, x, rp, pLL, pLS, pSS)[0] - hso1e[1] += -factor * get_fso2e_x2c(xmol, x, rp, pLL, pLS, pSS)[1] - hso1e[2] += -factor * get_fso2e_x2c(xmol, x, rp, pLL, pLS, pSS)[2] - elif (pictureChange2e == "x2c1c"): - pLL, pLS, pSS = get_p(dm / 2.0, x, rp) - hso1e += -factor * get_fso2e_x2c1c(xmol, x, rp, pLL, pLS, pSS) - elif (pictureChange2e == "none"): - hso1e += 0.0 - else: - print(pictureChange2e, "not a valid option") - exit(0) - - # MF 1 electron term - if (pictureChange1e == "bp"): - hso1e += factor * get_wso(xmol) - elif (pictureChange1e == "x2c1"): - wso = factor * get_wso(xmol) - hso1e += get_hso1e(wso, x, rp) - elif (pictureChange1e == "none"): - hso1e += 0.0 - else: - print(pictureChange1e, "not a valid option") - exit(0) + + if (not has_ecp): + # two electron terms + logger.note(xmol, "Start calculating two-electron contribution to SOC integrals, be patient.") + if (pictureChange2e == "bp"): + hso1e += -factor * get_fso2e_bp(xmol, dm) + elif (pictureChange2e == "bp1c"): + hso1e += -factor * get_fso2e_bp1c(xmol, dm, atomlist) + elif (pictureChange2e == "x2c_old"): + pLL, pLS, pSS = get_p(dm / 2.0, x, rp) + hso1e += -factor * get_fso2e_x2c_original(xmol, x, rp, pLL, pLS, pSS) + elif (pictureChange2e == "x2c"): + pLL, pLS, pSS = get_p(dm / 2.0, x, rp) + #hso1e += -factor * get_fso2e_x2c(xmol, x, rp, pLL, pLS, pSS) + hso1e[0] += -factor * get_fso2e_x2c(xmol, x, rp, pLL, pLS, pSS)[0] + hso1e[1] += -factor * get_fso2e_x2c(xmol, x, rp, pLL, pLS, pSS)[1] + hso1e[2] += -factor * get_fso2e_x2c(xmol, x, rp, pLL, pLS, pSS)[2] + elif (pictureChange2e == "x2c1c"): + pLL, pLS, pSS = get_p(dm / 2.0, x, rp) + hso1e += -factor * get_fso2e_x2c1c(xmol, x, rp, pLL, pLS, pSS) + elif (pictureChange2e == "none"): + hso1e += 0.0 + else: + print(pictureChange2e, "not a valid option") + exit(0) + + # MF 1 electron term + if (pictureChange1e == "bp"): + hso1e += factor * get_wso(xmol) + elif (pictureChange1e == "x2c1"): + wso = factor * get_wso(xmol) + hso1e += get_hso1e(wso, x, rp) + elif (pictureChange1e == "none"): + hso1e += 0.0 + else: + print(pictureChange1e, "not a valid option") + exit(0) h1ao = numpy.zeros((3, nc, nc)) if (uncontract):