5
$\begingroup$

Background

In PySCF, one can use from_chkfile() to Read SCF results from PySCF chkfile and transform 1e, 2e integrals using the SCF orbitals. The transformed integrals are then written to FCIDUMP file. It seems that the code supports writing the FCIDUMP file for the case where the calculation is RHF, but not for UHF. For instance, in the following input of a UHF calculation on O++:

mol = pyscf.M(
    atom = '''
        O
    ''',
    unit = 'angstrom',
    basis = {
            'O' : parse_gaussian.load('STO-3G.gbs', 'O')
    },
    charge = 2,
    spin = 2,
    verbose = 9,
    symmetry = True,
    output = name +'.txt',
    symmetry_subgroup = 'D2h',
    max_memory = 4000,
)
mf = mol.UHF().set(conv_tol=1e-10,max_cycle=999,direct_scf_tol=1e-14,chkfile=name+'.chk',init_guess='atom',irrep_nelec={'Ag': 4, 'B3u':1 , 'B2u':1 ,'B1u':0 })
mf.kernel()
pyscf.tools.fcidump.from_chkfile('fcidump', name+'.chk', tol=1e-18, float_format=' %.16g')

calling from_chkfile() with UHF calculation would give the following error:

s = reduce(numpy.dot, (mo_coeff.conj().T, mol.intor_symmetric('int1e_ovlp'), mo_coeff))
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: shapes (5,5,2) and (5,5) not aligned: 2 (dim 2) != 5 (dim 0)

Obviously that is because in this case, mo_coeff is a tuple of two ndarrays, which I think corresponds to Alpha and Beta spins.

My Approach

Now I am trying to directly use from_integrals() to convert the given 1e, 2e integrals to FCIDUMP. I think that there would be two sets of integrals each coming from a spin channel. I try to access that with the following:

mol, scf_rec = scf.chkfile.load_scf(name+'.chk')
mo_coeff = numpy.array(scf_rec['mo_coeff'])
orbsym_alpha = symm.label_orb_symm(mol, mol.irrep_id,mol.symm_orb, mo_coeff[0], check=False)
orbsym_beta = symm.label_orb_symm(mol, mol.irrep_id,mol.symm_orb, mo_coeff[1], check=False)
h1ao = scf.hf.get_hcore(mol)
h1e_alpha = reduce(numpy.dot, (mo_coeff[0].T, h1ao, mo_coeff[0]))
h1e_beta = reduce(numpy.dot, (mo_coeff[1].T, h1ao, mo_coeff[1]))
eri_alpha = ao2mo.full(mol, mo_coeff[0], verbose=0)
eri_beta = ao2mo.full(mol, mo_coeff[1], verbose=0)
nuc = mol.energy_nuc()
ms=mol.spin
from_integrals('fcidump_alpha', h1e_alpha, eri_alpha, h1e_alpha.shape[0], mol.nelec, nuc, ms, orbsym_alpha, tol=1e-18, float_format=' %.16g')
from_integrals('fcidump_beta', h1e_beta, eri_beta, h1e_beta.shape[0], mol.nelec, nuc, ms, orbsym_beta, tol=1e-18, float_format=' %.16g')

The full input and output files can be found in Input/Output

Question

I should mention that I am not sure about the approach I am following, I suspect that orbsym should be coming from only one of the spin channels as I have seen in some of the example files found in FCIdump. Thus, my question: How should I handle the orbital symmetry and the converted integrals in the case of UHF calculation so that the final integrals are written into a single FCIDUMP file?

Any suggestions or corrections are appreciated. Thanks!

$\endgroup$

2 Answers 2

2
$\begingroup$

The FCIDUMP "standard" originates from Peter Knowles' full configuration interaction (FCI) program, published in Comput. Phys. Commun. 54, 75 (1989).

The orbital active space for FCI is defined in terms of spin-restricted orbitals, since the exact FCI wave function is able to describe spin polarization of the density.

Extremely few programs support FCI calculations with spin-unrestricted orbitals, exactly since it is hard to make sense of an active space which differs for spin-up and spin-down electrons. There is simply no FCIDUMP standard for UHF orbitals. You don't need UHF orbitals, as RHF or ROHF orbitals suffice to get all states in exact theory.

Addendum: Many programs have introduced their own extensions to FCIDUMP, thus breaking compliance with the standard. Introduced for FCI calculations in 1989, the FCIDUMP standard is not optimal for e.g. coupled-cluster calculations which typically employ spin-unrestricted orbitals. FCIDUMP is also ASCII only, while binary i/o is orders of magnitude faster and requires less storage. An alternative is TREXIO, which is a new library designed for passing data like basis sets, orbitals, and one- and two-electron integrals between programs.

$\endgroup$
4
  • $\begingroup$ FCIDUMP files are now used for more than just FCI though, and many programs can print FCIDUMP files for more than just RHF/ROHF orbitals. The OP is trying to extend PySCF so that it can print UHF integrals, which I think is a good thing because at least three people including me have asked for it in this GitHub issue (the OP of that issue was Sai Ramadugu in 2019, then Yingzhou Li asked for an update in 2023, and I asked to re-open the issue in 2024). MRCC needs UHF or semi-canonical ROHF orbitals when reading integrals. $\endgroup$ Commented Jun 28 at 13:44
  • $\begingroup$ Yes, you can use FCIDUMP also for any other theory. Yet, the FCIDUMP standard is inherently limited to ROHF orbitals, which means it is fundamentally incompatible with UHF orbitals. $\endgroup$ Commented Jun 29 at 14:14
  • 1
    $\begingroup$ For about 25 years, the only available software for arbitrary-order coupled cluster calculations, has been MRCC. It might take years before MRCC becomes compatible with TREXIO, or before a TREXIO-compatible software is able to compete with MRCC for arbitrary-order coupled cluster. MRCC is so useful that it has been interfaced to at least ORCA, Psi4, Molpro, CFOUR, COLUMBUS, and Dirac via an FCIDUMP-like interface, and all of those programs can print the ASCII integrals from a UHF calculation. It would be nice if PySCF could too, since those programs can't easily do high-$\zeta$ integrals. $\endgroup$ Commented Jun 29 at 15:56
  • $\begingroup$ +1 @SusiLehtola, thank you for your answer, as mentioned in the comments by Nike Dattani, some other programs supports printing ASCII integrals for UHF calculations, so I think this should be technically possible, however, I don't know where to start and that's what my question is about. $\endgroup$ Commented Jun 30 at 7:54
1
$\begingroup$

The code for this function

For NECI, a Python function was made for writing an FCIDUMP file after a UHF calculation, and it can be found at line 308 of this file (the resulting FCIDUMP file can be used for calculations with NECI, or with any other software that understands an FCIDUMP file in this format):

def write_uhf_integrals_neci(fciqmcci,scf_obj,nmo,nelec,orbs,orbsym,tol=1e-15):
    ''' nmo is number of MO orbitals per spin channel
        note that ordering is abababa...   '''

    eri_aaaa = pyscf.ao2mo.restore(8,pyscf.ao2mo.incore.general(scf_obj._eri, (orbs[0],orbs[0],orbs[0],orbs[0]), compact=False),nmo)
    eri_bbbb = pyscf.ao2mo.restore(8,pyscf.ao2mo.incore.general(scf_obj._eri, (orbs[1],orbs[1],orbs[1],orbs[1]), compact=False),nmo)
    eri_aabb = pyscf.ao2mo.restore(8,pyscf.ao2mo.incore.general(scf_obj._eri, (orbs[0],orbs[0],orbs[1],orbs[1]), compact=False),nmo)
    eri_bbaa = pyscf.ao2mo.restore(8,pyscf.ao2mo.incore.general(scf_obj._eri, (orbs[1],orbs[1],orbs[0],orbs[0]), compact=False),nmo)
    h_core = scf_obj.get_hcore(fciqmcci.mol)
#    t = fciqmcci.mol.intor_symmetric('cint1e_kin_sph')
#    v = fciqmcci.mol.intor_symmetric('cint1e_nuc_sph')
    h_aa = reduce(numpy.dot, (orbs[0].T, h_core, orbs[0]))
    h_bb = reduce(numpy.dot, (orbs[1].T, h_core, orbs[1]))
    nuc = fciqmcci.mol.energy_nuc()
    float_format = ' %.16g'

    # Stupidly, NECI wants its orbitals as a,b,a,b,a,b rather than aaaabbbb
    # Reorder things so this is the case
    assert(len(orbsym) % 2 == 0)
    orbsym_reorder = [i for tup in zip(orbsym[:len(orbsym)/2], orbsym[len(orbsym)/2:]) for i in tup]
    a_inds = [i*2+1 for i in range(orbs[0].shape[1])]
    b_inds = [i*2+2 for i in range(orbs[1].shape[1])]

    with open(fciqmcci.integralFile, 'w') as fout:
        if not isinstance(nelec, (int, numpy.number)):
            ms = abs(nelec[0] - nelec[1])
            nelec = nelec[0] + nelec[1]
        else: ms=0
        fout.write(' &FCI NORB=%4d,NELEC=%2d,MS2=%d,\n' % (nmo*2, nelec, ms))
        if orbsym is not None and len(orbsym_reorder) > 0:
            fout.write('  ORBSYM=%s\n' % ','.join([str(x) for x in orbsym_reorder]))
        else:
            fout.write('  ORBSYM=%s\n' % ('1,' * 2*nmo))
        fout.write('  ISYM=1, UHF=TRUE\n')
        fout.write(' &END\n')
        # Assume 8-fold symmetry
        npair = nmo*(nmo+1)//2
        output_format = float_format + ' %4d %4d %4d %4d\n'
        ij = 0
        ijkl = 0
        for i in range(nmo):
            for j in range(0, i+1):
                kl = 0
                for k in range(0, i+1):
                    for l in range(0, k+1):
                        if ij >= kl:
                            if abs(eri_aaaa[ijkl]) > tol:
                                fout.write(output_format % (eri_aaaa[ijkl], a_inds[i], a_inds[j], a_inds[k], a_inds[l]))
                            if abs(eri_bbbb[ijkl]) > tol:
                                fout.write(output_format % (eri_bbbb[ijkl], b_inds[i], b_inds[j], b_inds[k], b_inds[l]))
                            if abs(eri_aabb[ijkl]) > tol:
                                fout.write(output_format % (eri_aabb[ijkl], a_inds[i], a_inds[j], b_inds[k], b_inds[l]))
                            if abs(eri_bbaa[ijkl]) > tol:
                                fout.write(output_format % (eri_bbaa[ijkl], b_inds[i], b_inds[j], a_inds[k], a_inds[l]))
                            ijkl += 1
                        kl += 1
                ij += 1
        h_aa = h_aa.reshape(nmo,nmo)
        h_bb = h_bb.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_aa[i,j]) > tol:
                    fout.write(output_format % (h_aa[i,j], a_inds[i], a_inds[j]))
                if abs(h_bb[i,j]) > tol:
                    fout.write(output_format % (h_bb[i,j], b_inds[i], b_inds[j]))
        output_format = float_format + '  0  0  0  0\n'
        fout.write(output_format % nuc)
    return 

The following comments from the above code are repeated below so that you do not miss them:

    # Stupidly, NECI wants its orbitals as a,b,a,b,a,b rather than aaaabbbb
    # Reorder things so this is the case

Calling this function

In the above file, the calling of that function occurs in the following lines of code, and you can see that there's in IF statement that splits the program into two cases, the first for UHF wavefunctions, and the other for RHF/ROHF wavefunctions:

    # Lookup and return the relevant 1-electron integrals, and print out
    # the FCIDUMP file.
    if tUHF:
        write_uhf_integrals_neci(fciqmcci,scf_obj,nmo,nelec,orbs,orbsym,tol=tol)
    else:
        eri = pyscf.ao2mo.incore.general(scf_obj._eri, (orbs,)*4, compact=False)
        h_core = scf_obj.get_hcore(fciqmcci.mol)
#        t = fciqmcci.mol.intor_symmetric('cint1e_kin_sph')
#        v = fciqmcci.mol.intor_symmetric('cint1e_nuc_sph')
        h = reduce(numpy.dot, (orbs.T, h_core, orbs))

        pyscf.tools.fcidump.from_integrals(fciqmcci.integralFile, h, 
                pyscf.ao2mo.restore(8,eri,nmo), nmo, nelec, fciqmcci.mol.energy_nuc(),
                fciqmcci.mol.spin, orbsym, tol=tol)

Ideally this function would be in a better place

The above situation is a bit like how it is in OpenMolcas, which writes FCIDUMP files using the code that was written by the NECI developers for the NECI interface, even if you want to use the FCIDUMP for the DICE interface (there might also be a way to print the FCIDUMP file using the code for the CheMPS2 interface).

Since the FCIDUMP format is not specific to NECI or DICE or any other specialized software for post-HF calculations, and is used by many programs, ideally the FCIDUMP file would be printed in a more general part of the PySCF software, such as pyscf.tools.fcidump or pyscf.ao2mo (this is the case for RHF and ROHF references, but for some reason the code for the UHF case was written in pyscf.fciqmc instead).

A note about the FCIDUMP format for UHF

Recently in the question Which programs can read an FCIDUMP file for a UHF wavefunction?, some information about the format for a UHF FCIDUMP was provided here:

"In the design of Knowles and Handy, there is an option to use this for UHF, in which IUHF=1 is in the header, then separate 2-index and 4-index integrals are written for alpha and beta orbitals, with a line of zeroes separating them for each."

$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .