Generated Fort.55 File in PySCF for MRCC, how to handle symmetry from UHF reference calculation?
Background
I am trying to build an interface between PySCF and MRCC using UHF reference. The basic idea of this interface is to generate 1e, 2e integrals using PySCF, save those integrals with ASCII format in what it is known as fort.55 file in MRCC or FCIDUMP file in PySCF, the format of this file follows the UHF standard designed by Knowles and Handy, in which after the header part, separate 4-index and 2-index integrals are written for alpha-alpha, beta-beta, and alph-beta orbitals, with a line of zeroes separating them. Unfortunately, this format and despite being used in some major quantum chemistry codes like MRCC or CFOUR, is not natively supported in PySCF.
Progress
I have implied this format to PySCF FCIDUMP file while taking care of the header part such that the user can generate and save the designated fort.55 file which can be later used as an input for MRCC in order to perform state-of-the-art post-HF calculations at the level of FCI or CC(n).
Code
The following are details about the script I am using to build the fort.55 file. Suppose that we have defined a Mol object for open-shell system, followed by running a UHF calculation. In the next step, I convert the 2e integrals using 4-fold permutation symmetry for alph-alpha, beta-beta, and alpha-beta orbitals which is saved in corresponding eri_aaaa, eri_bbbb, and eri_aabb, respectively. Moreover, the transformed 1e core Hamiltonian for alpha and beta are also calculated and stored in h_aa and h_bb, respectively.
orbs = mf.mo_coeff
nmo = orbs[0].shape[0]
eri_aaaa = pyscf.ao2mo.restore(4,pyscf.ao2mo.incore.general(mf._eri, (orbs[0],orbs[0],orbs[0],orbs[0]), compact=False),nmo)
eri_bbbb = pyscf.ao2mo.restore(4,pyscf.ao2mo.incore.general(mf._eri, (orbs[1],orbs[1],orbs[1],orbs[1]), compact=False),nmo)
eri_aabb = pyscf.ao2mo.restore(4,pyscf.ao2mo.incore.general(mf._eri, (orbs[0],orbs[0],orbs[1],orbs[1]), compact=False),nmo)
h_core = mf.get_hcore(mol)
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 = mol.energy_nuc()
In the next section of the code, I try to handle the symmetry of the orbitals in a way that will match the format of fort.55, I also define the range of values for integral indices.
if mol.symmetry:
groupname = mol.groupname
if groupname in ('SO3', 'Dooh'):
logger.info(mol, 'Lower symmetry from %s to D2h', groupname)
raise RuntimeError('Lower symmetry from %s to D2h' % groupname)
elif groupname == 'Coov':
logger.info(mol, 'Lower symmetry from Coov to C2v')
raise RuntimeError('''Lower symmetry from Coov to C2v''')
orbsym = pyscf.symm.label_orb_symm(mol,mol.irrep_name,mol.symm_orb,orbs[0])
orbsym = numpy.array(orbsym)
orbsym = [param.IRREP_ID_TABLE[groupname][i]+1 for i in orbsym]
a_inds = [i+1 for i in range(orbs[0].shape[0])]
b_inds = [i+1 for i in range(orbs[1].shape[1])]
nelec = mol.nelec
tol=1e-18
The last part is considered the main part of the script, which is supposed to handle the header of the generated file in a way that will match the fort.55 format, it is supposed to deal with the 4-index, and 2-index integrals in which I am assuming that we are using a 4-fold permutation symmetry. The sections after '4-fold symmetry' are in the sequence of alpha-alpha, beta-beta, alpha-beta 2e integrals, followed by alpha, beta 1e integrals, and finally the nuclear repulsion energy.
with open('fort.55', '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(f"{nmo:1d} {nelec:1d}\n")
if orbsym is not None and len(orbsym) > 0:
fout.write(f"{' '.join([str(x) for x in orbsym])}\n")
else:
fout.write(f"{' 1' * nmo}\n")
fout.write(' 150000\n')
output_format = float_format + ' %5d %5d %5d %5d\n'
#4-fold symmetry
kl = 0
for l in range(nmo):
for k in range(0, l+1):
ij = 0
for i in range(0, nmo):
for j in range(0, i+1):
if i >= k:
if abs(eri_aaaa[ij,kl]) > tol:
fout.write(output_format % (eri_aaaa[ij,kl], a_inds[i], a_inds[j], a_inds[k], a_inds[l]))
ij += 1
kl += 1
fout.write(' 0.00000000000000000000E+00' + ' 0 0 0 0\n')
kl = 0
for l in range(nmo):
for k in range(0, l+1):
ij = 0
for i in range(0, nmo):
for j in range(0, i+1):
if i >= k:
if abs(eri_bbbb[ij,kl]) > tol:
fout.write(output_format % (eri_bbbb[ij,kl], b_inds[i], b_inds[j], b_inds[k], b_inds[l]))
ij += 1
kl += 1
fout.write(' 0.00000000000000000000E+00' + ' 0 0 0 0\n')
ij = 0
for j in range(nmo):
for i in range(0, j+1):
kl = 0
for k in range(nmo):
for l in range(0, k+1):
if abs(eri_aabb[ij,kl]) > tol:
fout.write(output_format % (eri_aabb[ij,kl], a_inds[i], a_inds[j], b_inds[k], b_inds[l]))
kl += 1
ij +=1
fout.write(' 0.00000000000000000000E+00' + ' 0 0 0 0\n')
h_aa = h_aa.reshape(nmo,nmo)
h_bb = h_bb.reshape(nmo,nmo)
output_format = float_format + ' %5d %5d 0 0\n'
for i in range(nmo):
for j in range(nmo):
fout.write(output_format % (h_aa[i,j], a_inds[i], a_inds[j]))
fout.write(' 0.00000000000000000000E+00' + ' 0 0 0 0\n')
for i in range(nmo):
for j in range(nmo):
fout.write(output_format % (h_bb[i,j], b_inds[i], b_inds[j]))
fout.write(' 0.00000000000000000000E+00' + ' 0 0 0 0\n')
output_format = float_format + ' 0 0 0 0\n'
fout.write(output_format % nuc)
Test & Debugging
In order to test the above method and further arrive at my question, I will consider two open-shell systems, namely Be(+) and Be(-). The former having 3 electrons occupying s-type orbitals, while the latter having 5 electrons with s- and p-type orbitals being occupied. by using the generated fort.55 file as described above with the following input file in MRCC:
basis=STO-XG-EMSL
iface=cfour
uncontract=off
calc=CC(5)
core=corr
itol=18
scftol=13
cctol=7
ccmaxit=999
scfmaxit=9999
scfiguess=ao
scftype=UHF
rohftype=semicanonical
rest=2
charge=-1
refdet=serialno
1,2
3
symm=0
mult=2
occ=2,0,0,0,0,0,0,1/2,0,0,0,0,0,0,0
geom
BE
tprint=0.01
verbosity=3
You can see that I have turned off the symmetry 'symm=0', upon which, the calculation will run normally, to confirm that, I compare the energies I get with those generated using standalone MRCC, and I can confirm that the energies are identical in that case for both Be(-) and Be(+).
Those values are for Be(-) using STO-XG
mrcc-mrcc:
CCSD -14.274723492321
CCSD[T] -14.274740253256
CCSD(T) -14.274739121789
CCSDT -14.274741793166
CCSDT[Q] -14.274743492727
CCSDT(Q)/A -14.274743594299
CCSDT(Q)/B -14.274743590678
CCSDTQ -14.274743599645
CCSDTQ[P] -14.274743599645
CCSDTQ(P)/A -14.274743599645
CCSDTQ(P)/B -14.274743599645
CCSDTQP -14.274743599073
pyscf-mrcc (symmetry is off):
CCSD -14.274723492321
CCSD[T] -14.274740253256
CCSD(T) -14.274739121789
CCSDT -14.274741793166
CCSDT[Q] -14.274743492727
CCSDT(Q)/A -14.274743594299
CCSDT(Q)/B -14.274743590678
CCSDTQ -14.274743599645
CCSDTQ[P] -14.274743599645
CCSDTQ(P)/A -14.274743599645
CCSDTQ(P)/B -14.274743599645
CCSDTQP -14.274743599073
The Problem
Running the above calculation without using symmetry is affordable in terms of computational power and time for small systems with small basis sets, however, my attempt for such interface is to perform higher-order coupled cluster calculations such as CCSDT(Q), CCSDTQ, CCSDTQ(P), CCSDTQP, etc. with large basis sets such as aCV9Z. Thus, symmetry shall be applied as well.
I will start with Be(+) which has 3 electrons occupying s-type orbitals, using the generated fort.55 file in PySCF with STO-XG basis set, which in this case has the following ORBSYM: 'Ag' 'Ag' 'B1u' 'B2u' 'B3u' 'Ag', I should be using 'symm=1' in the input file of MRCC, I find that energies are identical to the energies generated with standalone MRCC.
Be+ (STO-XG)
pyscf-mrcc (symmetry is on):
CCSD -14.101017848371
CCSD[T] -14.101018583402
CCSD(T) -14.101018579146
CCSDT -14.101018557319
mrcc-mrcc
CCSD -14.101017848371
CCSD[T] -14.101018583402
CCSD(T) -14.101018579146
CCSDT -14.101018557319
On the other hand, in the case of Be(-) which has 5 electrons occupying s- and p-type orbitals, the generated fort.55 file in PySCF would have the following ORBSYM: Ag' 'Ag' 'B3u' 'B1u' 'B2u' 'Ag', I should be using 'symm=8' in this case. However, the final energies are found to be different (lower) than the energies generated with standalone MRCC.
pyscf-mrcc (symmetry is on):
CCSD -14.256521081042
CCSD[T] -14.256521081042
CCSD(T) -14.256521081042
CCSDT -14.256521081190
CCSDT[Q] -14.256521081190
CCSDT(Q)/A -14.256521081190
CCSDT(Q)/B -14.256521081190
CCSDTQ -14.256521081240
CCSDTQ[P] -14.256521081240
CCSDTQ(P)/A -14.256521081240
CCSDTQ(P)/B -14.256521081240
CCSDTQP -14.256521081257
You can find full files for the inputs and outputs generated with PySCF and MRCC in the following Input_Output_Files.
The Question
Based on the above description, what is the reason that Be(+) works correctly with symmetry while not for Be(-)?
What could possibly be wrong/missing in my code in order to correctly handle the symmetry of spatial orbitals in systems where p-type orbitals are occupied?