Quickstart
Coulomb matrix computation
This example demonstrates the complete workflow: building a molecule with PySCF, converting bases to libintx structures, constructing the DF metric (P|Q), and computing the Coulomb matrix J.
import numpy as np
from pyscf import gto, scf, df
import libintx
from libintx.tools import pyscf_to_libintx_basis
# Build a PySCF molecule
mol = gto.Mole()
mol.atom = '''
O 0.000000 0.000000 0.000000
H 0.000000 -0.757160 0.586260
H 0.000000 0.757160 0.586260
'''
mol.basis = 'def2-svp'
# Spherical harmonic GTOs are not yet supported:
mol.cart = True
mol.build()
# Build an auxiliary basis and its two-center metric (P|Q)
auxmol = df.addons.make_auxmol(mol) # set up the default auxiliary basis
V = auxmol.intor('int2c2e', hermi=1) # 2-center 2-electron integrals matrix
# Convert PySCF bases to libintx Basis
basis = pyscf_to_libintx_basis(mol)
aux_basis = pyscf_to_libintx_basis(auxmol)
# Create the J-engine and compute J
jeng = libintx.Jengine(basis, aux_basis, V, vinv_type='cholesky', vinv_device='cuda')
D = np.eye(mol.nao)
J = jeng.compute_j(D, scale=0.5)
# Check the results
mf = scf.RHF(mol).density_fit()
J_ref = mf.get_j(dm=D)
print(f'|J_ref - J| = {np.linalg.norm(J_ref-J)}')
Schwarz Screening
Schwarz screening can dramatically reduce compute time, especially for large systems. You can enable screening using per-shell two-center two-electron \((P|P)\) and four-center two-electron \((ij|ij)\) estimates.
Computation:
\((P|P)\) can be computed from the diagonal AO blocks of (P|Q) aggregated per auxiliary shell. It should be a vector of length nsh_aux, where nsh_aux is the number of shells in the auxiliary basis (len(aux_basis)).
\((ij|ij)\) can be initialized via PySCF’s RHF helper. It should be a matrix of shape (nsh, nsh), where nsh is the number of shells in the primary basis (len(basis)).
Note
Some PySCF bases are general-contracted; convert them to segmented-contraction variants before computing screening quantities.
An example code:
import numpy as np
from pyscf import scf
import libintx
# Compute screening quantities
pp = np.sqrt(np.array([np.max(np.abs(V[i:j,i:j])) for i,j in aux_basis.aos_by_shell()]))
ijij = scf.RHF(mol).init_direct_scf().q_cond
# Enable screening
jeng.set_screening(pp, ijij, cutoff=1e-12)
Solver Options
To use CUDA-based solver, pass vinv_device=’cuda’. Optionally adjust vinv_mem_fraction (default 0.5) to limit the maximum fraction of GPU memory for the solver.
jeng = libintx.Jengine(
basis, aux_basis, V,
vinv_device='cuda',
vinv_mem_fraction=0.5
)
If memory limits are exceeded, the Cholesky/LU factors will be stored on the host and loaded to the GPU when needed. Consider distributed Cholesky solver (cholesky_mp) for very large molecules (requires MPI).