QTAIM & ELF/LOL#
The previous chapter partitioned the density with recipes. This one lets the density partition itself. Two topological analyses — QTAIM (the quantum theory of atoms in molecules) and ELF (the electron localization function) — find atoms, bonds, and lone pairs from the shape of a real-space field, with no arbitrary basis choice. They are the heart of “Multiwfn-class” analysis.
QTAIM: atoms from the density topology#
Theory#
Bader’s QTAIM analyzes the gradient field of the electron density \(\nabla\rho(\mathbf r)\). Its critical points (where \(\nabla\rho = 0\)) classify the structure:
a nuclear critical point (NCP) sits at each nucleus (a density maximum),
a bond critical point (BCP) lies between two bonded atoms (the density’s saddle along the bond),
ring (RCP) and cage (CCP) critical points appear in cyclic and caged structures.
The bond path linking two nuclei through a BCP is QTAIM’s rigorous definition of a chemical bond, and a sanity check — the Poincaré–Hopf relation \(n_{\text{NCP}} - n_{\text{BCP}} + n_{\text{RCP}} - n_{\text{CCP}} = 1\) — must hold for a correct topology. QTAIM also carves space into atomic basins (regions bounded by zero-flux surfaces); integrating \(\rho\) over a basin gives a basis-set-robust atomic charge (the “Bader charge”).
Usage#
import qc
water = "O 0 0 0.117; H 0 0.757 -0.469; H 0 -0.757 -0.469"
m = qc.chk.new(atom=water, ao="cc-pvdz", unit="angstrom").scf(ref="r").run()
topo = qc.prop.qtaim.topology(m)
topo["counts"] # {'nuclear': 3, 'bond': 2, 'ring': 0, 'cage': 0}
topo["poincare_hopf"] # {'sum': 1, 'holds': True}
topo["critical_points"][0] # {'type': 'nuclear', 'position': [...], 'rho': 297.19, 'atoms': [0]}
For water, QTAIM finds exactly 3 nuclear + 2 bond critical points (the two O–H bonds) and no rings or cages, and the Poincaré–Hopf sum is 1 — a topologically consistent result. Integrating the atomic basins gives the Bader populations:
bader = qc.prop.qtaim.basin_integrate(m)
bader["atoms"][0] # {'atom': 0, 'label': 'O', 'integral': 9.244} -> Bader charge 8 - 9.244 = -1.24
bader["total"] # 10.023 (≈ the 10 electrons; small grid residual)
Oxygen’s basin holds ~9.24 electrons — a Bader charge of about −1.24, the most negative of all the
schemes in the charges chapter, and the most physically grounded. qc.prop.qtaim
also exposes bond-critical-point properties, basin multipoles, delocalization indices, and the source
function.
Tip
QTAIM is grid-based — expect it to be slower Topology and basin integration walk a real-space grid, so they cost more than the algebraic charges of the last chapter. It is still a single call; just do not be surprised when it takes a few seconds on a bigger molecule.
ELF: seeing electron pairs#
Theory#
The electron localization function \(\text{ELF}(\mathbf r) \in [0,1]\) measures how strongly electrons are localized — how much the Pauli principle keeps a like-spin electron away from a reference point. It reveals the electron pairs of a Lewis structure directly: cores, bonds, and lone pairs show up as separate regions of high ELF (basins), which is why ELF is the standard tool for visualizing bonding and lone pairs without invoking orbitals.
Usage#
basins = qc.prop.elf.basins(m) # the ELF basins (water: 5 — an O core, two O–H bonds, two lone pairs)
domains = qc.prop.elf.domains(m) # localization domains at an isovalue
Water’s ELF resolves into 5 basins — the oxygen core, the two O–H bonding basins, and the two oxygen
lone pairs — recovering the familiar Lewis picture from the wavefunction alone. qc.prop.elf also provides
LOL (an ELF-like localized-orbital locator) and core/valence bifurcation analysis. To see any of these,
m.view3d("elf") from the visualization chapter draws the ELF isosurface.
Worked example#
import qc
water = "O 0 0 0.117; H 0 0.757 -0.469; H 0 -0.757 -0.469"
m = qc.chk.new(atom=water, ao="cc-pvdz", unit="angstrom").scf(ref="r").run()
topo = qc.prop.qtaim.topology(m)
print("critical points:", topo["counts"], "| Poincaré–Hopf holds:", topo["poincare_hopf"]["holds"])
print("O Bader population:", round(qc.prop.qtaim.basin_integrate(m)["atoms"][0]["integral"], 3))
print("ELF basins :", len(qc.prop.elf.basins(m)))
Exercise 11
A QTAIM analysis of benzene should report how many ring critical points, and what must the Poincaré–Hopf sum equal for the topology to be consistent?
The charges chapter gave oxygen charges from −0.09 (Löwdin) to −0.91 (NPA). Where does the QTAIM (Bader) charge of ≈ −1.24 sit, and why is it considered especially well-defined?
You want to see the two lone pairs on water’s oxygen. Which ELF call, and which viewer call, do you use?
Solution to Exercise 11
One ring critical point (the center of the aromatic ring). Poincaré–Hopf must equal 1: \(n_{\text{NCP}} - n_{\text{BCP}} + n_{\text{RCP}} - n_{\text{CCP}} = 12 - 12 + 1 - 0 = 1\) for benzene.
It is the most negative — even beyond NPA. It is well-defined because the basin boundary is a zero-flux surface of the density (a property of \(\rho\) itself, not of the basis functions), so the Bader charge is essentially basis-set-independent.
Compute with
qc.prop.elf.basins(m)/qc.prop.elf.domains(m), and visualize withm.view3d("elf")— the two lone-pair basins appear as distinct lobes above the oxygen.
QTAIM and ELF analyze a single molecule’s bonding. The next chapter turns to the weak, non-covalent interactions between fragments — hydrogen bonds and van der Waals contacts — with NCI and IGM.