This site is version 0.0.1. Most of the code on this site is drafted by an agent and needs review. I apologize for the AI-speak, we are working on rewriting it!
viv-lang.org

Notebooks · lib/hf/os_onebody

Overlap, kinetic and nuclear attraction by the Obara-Saika recurrences

This module computes the three one-electron matrices an SCF needs, S, T and V, over unit-normalized Cartesian Gaussians, by the recurrences of Obara and Saika (J. Chem. Phys. 84, 3963, 1986). It is the second of the library's one-electron families. It shares no code with the McMurchie-Davidson family (hermite.viv, md_*), so when the two agree to 10−12 the agreement says something about both.

The idea. A Cartesian Gaussian on center A is x_A^i y_A^j z_A^k exp(-a r_A^2), with x_A = x - A_x. Raising the power i by one is multiplying by x_A, and the product of two Gaussians on A and B is one Gaussian on P = (aA + bB)/p, p = a + b. So x_A = (x - P_x) + (P_x - A_x), and one integration by parts turns the (x - P_x) piece into lower powers. For the overlap along one axis that gives

S(i+1, j) = PA S(i, j) + (i S(i-1, j) + j S(i, j-1)) / (2p)
S(i, j+1) = PB S(i, j) + (i S(i-1, j) + j S(i, j-1)) / (2p)

starting from S(0, 0) = sqrt(pi/p) exp(-mu AB^2) with mu = ab/p. The 3-D overlap is the product of the three axes.

Kinetic energy. T = -1/2 <a|d^2/dx^2 + d^2/dy^2 + d^2/dz^2|b>, which is Tx Sy Sz + Sx Ty Sz + Sx Sy Tz. Along one axis the same argument gives

T(0, 0)   = mu (1 - 2 mu AB^2) S(0, 0)
T(i+1, j) = PA T(i, j) + (i T(i-1, j) + j T(i, j-1)) / (2p)
            + (b/p) (2a S(i+1, j) - i S(i-1, j))
T(i, j+1) = PB T(i, j) + (i T(i-1, j) + j T(i, j-1)) / (2p)
            + (a/p) (2b S(i, j+1) - j S(i, j-1))

(Helgaker, Jorgensen and Olsen, section 9.3.4).

Nuclear attraction does not factor by axis, because 1/r_C couples them. Obara and Saika carry an auxiliary index m: the integral Theta(e)^(m) over the Gaussian product with e on A is built from

Theta(0)^(m)    = (2 pi / p) exp(-mu AB^2) F_m(p PC^2)
Theta(e+1_i)^(m) = PA_i Theta(e)^(m) - PC_i Theta(e)^(m+1)
                   + e_i/(2p) (Theta(e-1_i)^(m) - Theta(e-1_i)^(m+1))

where F_m is the Boys function and only m = 0 is kept at the end. Rather than recur on both centers, the recurrence builds every power on A up to la + lb and the horizontal step

(a, b+1_i) = (a+1_i, b) + AB_i (a, b)

moves powers from A to B. That step holds for any operator that does not depend on the basis centers, and it is applied once per shell pair after the primitives and nuclei are summed, since it is linear.

Components. Every Cartesian component up to total power L gets one index: l(l+1)(l+2)/6 for the components below l, then PySCF's order within the shell (lx counting down, then ly). os_ctab(L) tabulates the powers and the neighbor one step up and down along each axis, so the recurrences look up indices instead of computing them.

Normalization. bas_contract gives cn[p], which carries the radial norm of each primitive and the shell's contraction scale; each component then gets bas_nc(lx, ly, lz) (see lib/hf/basis.viv). The result is PySCF's int1e_*_cart divided by F F^T.

The stage members os_overlap, os_kinetic and os_nuclear take (atoms, shells, prims) and return [nao_cart, nao_cart]. The per-pair blocks (os_overlap_block and the others) take shell indices, for tests that look at one pair.

Used with import hf.os_onebody.

Look inside this program

  • Atlas

    The program’s map: every function and which calls which.

  • There’s no recorded run of this program yet.

Shift+Enter in a cell runs it and moves to the next. Running a cell runs every cell above it too, as one program.

[1]lines 73–454