Verifying an RI-MP2 auxiliary basis

When you reach for RI-MP2 in production, you trade O(N⁵) canonical AO→MO work for an O(N⁴) auxiliary-basis fit, and you inherit the fit’s residual on top of the basis-set-incompleteness error you’d have had anyway. For most workflows the per-zeta RIfit family (cc-pvNz-ri, def2-Nzv-rifit) is tight enough that the fit residual stays well below ~µHa per atom. But when you move to a new system class, switch zeta, or accidentally pair an orbital basis with the wrong aux family, the residual can balloon by an order of magnitude, and the silent failure mode is a wrong SCS / SOS / double-hybrid number, not a crash.

This tutorial walks through MP2Options.report_ri_residual, the opt-in diagnostic added in #mp2-ri-residual, on water with two aux choices. It complements the double-hybrid B2PLYP tutorial (which is where you’d actually consume this diagnostic in anger, the post-SCF MP2 step in a B2PLYP run inherits the same RI fit error).

The structural fingerprint of a Coulomb-metric Dunlap fit

RI-MP2 builds the four-index ERI from the Coulomb-metric fit

\[(ia|jb)_{\mathrm{RI}} = \sum_{PQ} (ia|P)\, [V^{-1}]_{PQ}\, (Q|jb), \quad V_{PQ} = (P|Q).\]

The fit residual has a definite sign pattern in the Grimme decomposition: \(E_{\mathrm{os}} = \sum (ia|jb)^2 / \Delta\) is over-estimated (less negative than canonical), and \(E_{\mathrm{ss}} = \sum [(ia|jb)^2 - (ia|jb)(ib|ja)] / \Delta\) is under-estimated (more negative than canonical). The two errors partially cancel in the unscaled \(E_{\mathrm{corr}} = E_{\mathrm{os}} + E_{\mathrm{ss}}\) but survive SCS / SOS scaling, because SCS weights the OS bucket (c_os = 6/5) more than three times as heavily as the SS bucket (c_ss = 1/3).

A wrong-direction signature in the diagnostic (ΔE_os < 0, ΔE_ss > 0, or both with the same sign) almost certainly indicates a numerical bug in the fit pipeline, not a fit-basis issue. The diagnostic is exactly the right tool to catch that quickly.

Running the diagnostic

The example runs RHF on water/cc-pVTZ, then an SCS-MP2 with cc-pvtz-ri and report_ri_residual=True, which reports the per-bucket fit error alongside the OS and SS correlation energies:

import vibeqc as vq

mol = vq.Molecule(
    [
        vq.Atom(8, [0.0,  0.00,  0.00]),
        vq.Atom(1, [0.0,  1.43, -0.98]),
        vq.Atom(1, [0.0, -1.43, -0.98]),
    ]
)
basis = vq.BasisSet(mol, "cc-pvtz")

rhf_opts = vq.RHFOptions(); rhf_opts.conv_tol_energy = 1e-10
hf = vq.run_rhf(mol, basis, rhf_opts)

opts = vq.MP2Options()
opts.density_fit = True
opts.aux_basis = "cc-pvtz-ri"
opts.c_os = 6.0 / 5.0          # SCS-MP2 (Grimme 2003)
opts.c_ss = 1.0 / 3.0
opts.report_ri_residual = True  # ← opt-in flag

mp2 = vq.run_mp2(mol, basis, hf, opts)

assert mp2.ri_residual_reported
print(f"E_os = {mp2.e_os:+.8f} Ha   "
      f"ΔE_os = {mp2.e_os_ri_residual:+.2e}")
print(f"E_ss = {mp2.e_ss:+.8f} Ha   "
      f"ΔE_ss = {mp2.e_ss_ri_residual:+.2e}")

Output:

E_os = -0.20613056 Ha   ΔE_os = +5.20e-05
E_ss = -0.06591474 Ha   ΔE_ss = -2.43e-05

ΔE_os > 0 and ΔE_ss < 0, the expected Coulomb-metric Dunlap fingerprint. With c_os = 6/5 and c_ss = 1/3:

\[ \Delta E_{\mathrm{SCS}} \approx 1.2 \cdot (+5.20 \times 10^{-5}) + 0.333 \cdot (-2.43 \times 10^{-5}) = +5.4 \times 10^{-5} \mathrm{\,Ha} \approx 0.034 \mathrm{\,kcal/mol}. \]

For chemical accuracy (1 kcal/mol) this is well inside tolerance; for chasing sub-µHa parity against a reference calculation it is not.

Aux-basis sweep, confirming a genuine fit residual

A fit residual should shrink monotonically as you enlarge the aux basis. Re-running on cc-pvqz-ri (242 functions vs 141 for cc-pvtz-ri):

Aux basis

\(n_{\mathrm{aux}}\)

\(\Delta E_{\mathrm{os}}\)

\(\Delta E_{\mathrm{ss}}\)

\(\Delta E_{\mathrm{SCS}}\)

cc-pvtz-ri

141

+5.2e-5

−2.4e-5

+5.4e-5

cc-pvqz-ri

242

+2.0e-5

−4.4e-6

+2.2e-5

Both rows show the same sign pattern; the magnitudes drop roughly 2×, the right signature for a fit-residual sweep.

If you run the same sweep and the residual stays flat or grows with aux size, that is not a fit-residual signature, it’s evidence of a code bug (Cholesky / SVD propagation, mis-routed weights), an ill-conditioned \(V_{PQ}\), or an aux family that is misaligned with the orbital basis. Open an issue with the diagnostic output and the \(V_{PQ}\) condition number (the latter is on vibeqc.density_fitting.DensityFitting(...).info.metric_condition_estimate).

When to pair this diagnostic with a code

  • Before publishing a new aux-basis recommendation. Run the diagnostic on a representative set; the per-system bucket residual is your “this aux is fit for this purpose” evidence.

  • Before cross-code parity work. If two codes’ RI-MP2 numbers disagree by more than the per-code RI residuals against canonical, one of them is doing something other than the standard Coulomb-metric fit (e.g. one might be running canonical silently, always confirm both codes are running RI as advertised).

  • As a unit-test guard. Pin the residual magnitude alongside the energy itself in your test suite, a regression in the RI pipeline shows up as a residual shift, not just an energy shift.

Cost note

The diagnostic doubles the runtime of an RI-MP2 call, it forces the O(N⁵) canonical AO→MO transform alongside the O(n_aux · …) RI build. Keep it off in production loops; only switch it on when you need the answer it gives.

opts.report_ri_residual = False   # ← default, production

References

  • RI-MP2 (the algorithm). F. Weigend, M. Häser, “RI-MP2: first derivatives and global consistency,” Theor. Chem. Acc. 97, 331 (1997). doi:10.1007/s002140050269. The original derivation of the Coulomb-metric-fitted MP2 amplitudes vibe-qc implements.

  • Dunlap-fit theory. B. I. Dunlap, “Robust and variational fitting: Removing the four-center integrals from center stage in quantum chemistry,” J. Mol. Struct. THEOCHEM 529, 37 (2000). doi:10.1016/S0166-1280(00)00528-5. The “robust” fitting framework that pins the sign pattern of the residual.

  • Aux-basis families. F. Weigend, A. Köhn, C. Hättig, “Efficient use of the correlation consistent basis sets in resolution of the identity MP2 calculations,” J. Chem. Phys. 116, 3175 (2002). doi:10.1063/1.1445115 (the cc-pVNZ-RI family); F. Weigend, “Accurate Coulomb-fitting basis sets for H to Rn,” Phys. Chem. Chem. Phys. 8, 1057 (2006). doi:10.1039/B515623H (the def2-*-rifit family).

  • SCS-MP2. S. Grimme, J. Chem. Phys. 118, 9095 (2003). doi:10.1063/1.1569242, the scaling weights c_os = 6/5, c_ss = 1/3 used in this tutorial.

All four fire automatically in the bundled citation database routes for density_fit=True MP2 / c_os / c_ss jobs.

Further reading

  • MP2 and double hybrids, Verifying the RI fit

    • reference page covering the same material plus the diagnostic’s formal definition and limitations.

  • Density fitting, the J / K / MP2 fit-aux families vibe-qc ships, autodetect helper, \(V_{PQ}\) conditioning diagnostics.

  • Double hybrid B2PLYP, the place where the post-SCF RI-MP2 step (and therefore this diagnostic) actually changes a publishable number.

  • examples/molecular/input-h2o-ri-mp2-residual.py

    • the runnable script behind this tutorial.

  • examples/molecular/mp2_benchmarks/

    • multi-system vibe-qc ↔ ORCA validation suite; the diagnostic is the right pre-flight check before regenerating its tables.