Effective core potentials (ECPs)¶
vibe-qc ships libecpint 1.0.7 vendored as a runtime dependency. ECP integrals, reduced-Z nuclear-attraction, and valence-electron-count accounting are wired through every molecular SCF driver (RHF, UHF, RKS, UKS). Heavy-element chemistry — including the d-block, the lanthanides, and the actinides — is reachable without constructing the all-electron basis.
The banner lists libecpint as a linked dependency:
linked: libint 2.13.1 · libxc 7.0.0 · spglib 2.7.0 · libecpint 1.0.7
What ships¶
Six ECP libraries bundled inside libecpint, MIT-licensed (see
docs/license.md):
Library |
Family |
Atomic-number coverage |
Citation family |
|---|---|---|---|
|
Stuttgart-Köln MDF, 10-electron core |
post-K (rows 4+) |
Andrae, Häußermann, Dolg, Stoll, Preuß, Theor. Chim. Acta 77, 123 (1990) and follow-ups |
|
Stuttgart-Köln MDF, 28-electron core |
post-Cd |
(ditto, per-element refs) |
|
Stuttgart-Köln MDF, 46-electron core |
post-Hg |
(ditto) |
|
Stuttgart-Köln MDF, 60-electron core |
f-block (post-Yb) |
(ditto) |
|
Stuttgart-Köln MDF, 78-electron core |
actinides |
(ditto) |
|
Hay-Wadt LANL |
post-Na (rows 3+) |
Hay, Wadt, J. Chem. Phys. 82, 270 + 299 + 284 (1985) |
The accompanying valence-only orbital basis sets (e.g. lanl2dz
orbital basis, def2-svp for Stuttgart-MDF cores) are
available in vibe-qc’s standard basis library.
The two recipes¶
There are two ways to wire an ECP into an SCF run:
Manual
ECPCenterrecipe (always-available, all versions). You explicitly construct anECPCenterper heavy atom and assign the right ECP library name. Verbose but transparent.vq.auto_ecp_centers(...)helper (basissetdev-conditional; see § Phase-14e auto-helper below). One-liner replacement: parses the bundled.ecpsidecar, picks the right library, and returns(ecp_centers, library_name)ready to drop onto any SCF Options.
If you’re writing tutorials or sample scripts and the auto-helper is available, use it. If you’re writing custom production scripts and want full control over the ECP metadata, the manual recipe is the right surface.
Manual recipe (always available)¶
import vibeqc as vq
# Pt atom at the origin.
mol = vq.Molecule([vq.Atom(78, [0.0, 0.0, 0.0])])
basis = vq.BasisSet(mol, "lanl2dz") # valence-only orbital basis
# One ECPCenter per heavy atom.
ec = vq.ECPCenter()
ec.Z = 78 # atomic number of the centre
ec.xyz = [0.0, 0.0, 0.0] # bohr (Cartesian)
# Wire it onto the SCF options.
opts = vq.UHFOptions()
opts.ecp_centers = [ec]
opts.ecp_library = "lanl2dz" # which ECP XML library
opts.multiplicity = 3 # Pt ground state ⁵D₀
result = vq.run_uhf(mol, basis, opts)
print(result.energy_ha) # −118.227 Ha (22 BFs, 125 SCF iters)
The same ecp_centers + ecp_library flags are accepted by
run_rhf, run_rks, and run_uks. Multiple heavy atoms →
build one ECPCenter per atom and pass them all in:
# Pt-Pt dimer, 10-bohr separation.
mol = vq.Molecule([
vq.Atom(78, [0.0, 0.0, -5.0]),
vq.Atom(78, [0.0, 0.0, +5.0]),
])
basis = vq.BasisSet(mol, "lanl2dz")
opts = vq.UHFOptions()
opts.ecp_centers = [
vq.ECPCenter(Z=78, xyz=[0.0, 0.0, -5.0]),
vq.ECPCenter(Z=78, xyz=[0.0, 0.0, +5.0]),
]
opts.ecp_library = "lanl2dz"
opts.multiplicity = 1 # paired
Phase-14e auto-helper (vq.auto_ecp_centers)¶
The Phase 14e helper short-circuits the boilerplate above.
Given a molecule and a basis name, it parses the bundled
.ecp sidecar, picks the matching libecpint XML library, and
returns ready-to-drop (ecp_centers, library_name):
import vibeqc as vq
mol = vq.Molecule([
vq.Atom(78, [0.0, 0.0, -5.0]),
vq.Atom(78, [0.0, 0.0, +5.0]),
])
basis = vq.BasisSet(mol, "lanl2dz")
opts = vq.UHFOptions()
opts.ecp_centers, opts.ecp_library = vq.auto_ecp_centers(mol, "lanl2dz")
opts.multiplicity = 1
result = vq.run_uhf(mol, basis, opts)
# Same −118.227 Ha as the manual recipe, but no per-atom ECPCenter
# wiring needed. Tutorial / sample-script-friendly.
The helper is the recommended path for tutorials and example scripts. Live numerics match the manual recipe: Pt/lanl2dz UHF = −118.227 Ha (singlet) at 22 BFs in 125 iters; Si/lanl2dz RHF = −3.475 Ha.
Important
Status note (2026-05-10). The vq.auto_ecp_centers helper
ships from the
basissetdev
long-lived branch. Per the user’s standing rule, basissetdev
does NOT merge into v0.8.0 — it stays a paper-writing branch.
If basissetdev does merge into a future v0.8.x maintenance release, this helper becomes available. Until then, use the manual recipe above. The manual API is stable; the auto-helper is purely a convenience layer on top of it.
The current status of the basissetdev merge decision is
tracked in .release-status/v0.8.0/basissetdev.md.
When you need it¶
ECPs are mandatory when:
You’re using a valence-only basis like
lanl2dz,lanl2tz,def2-svpfor elements ≥ K (Z ≥ 19) where the basis omits the core.You’re using
dhf-{svp,sv(p),tzvp,…}orx2c-*relativistic bases — these are valence-only by design.The basis you picked has an
.ecpsidecar inpython/vibeqc/basis_library/basis/(orthird_party/libint/install/share/libint/2.13.1/basis/).
ECPs are not needed when:
You’re using an all-electron basis (
6-31g*,def2-tzvp,cc-pvtz,pob-tzvp). The basis carries every core and valence electron explicitly.All your atoms are H–Mg (Z ≤ 12) and your basis covers them all-electron. (Most commonly: any first/second-period organic chemistry.)
If you forget the ECP wiring on a basis that needs one, vibe-qc
fails loudly at SCF with "canonical orth dropped too many basis directions" — the symptom of trying to fit valence orbitals
to nuclei that still expect core electrons. The fix is to
add the ecp_centers + ecp_library flags.
Choosing the right ECP library¶
For each heavy atom, the right ECP library is determined by the size of the core electron count the orbital basis implies:
Atomic number |
Core size |
Library |
Orbital basis examples |
|---|---|---|---|
Z = 19–36 (K–Kr) |
10 |
|
|
Z = 37–54 (Rb–Xe) |
28 |
|
|
Z = 55–86 (Cs–Rn) |
46 |
|
|
Z = 57–71 (lanthanides) |
60 |
|
dhf-tzvp / dhf-qzvp lanthanide variants |
Z = 87–88 (Fr/Ra) |
78 |
|
dhf-* actinide variants |
Z = 21–86 (alternative) |
varies |
|
|
The Stuttgart-Köln MDF family is the def2-* default. The
Hay-Wadt LANL family is the LANL2DZ orbital basis default.
Mixing-and-matching across families is possible but should be
done deliberately: auto_ecp_centers(mol, basis_name) picks
the right library when the helper is available; manually,
match library to orbital basis as the table above suggests.
Python API surface¶
import vibeqc as vq
# Version probe
vq.libecpint_version() # "libecpint 1.0.7 (vendored, MAX_L=5)"
vq.library_versions()["libecpint"] # same
# Per-atom ECP descriptor (manual path)
ec = vq.ECPCenter()
ec.Z = 78
ec.xyz = [0.0, 0.0, 0.0]
# AO matrix V_ECP_{μν} = ⟨χ_μ | V_ECP | χ_ν⟩ (spherical basis)
V_ecp = vq.compute_ecp_matrix(
basis, # vibeqc.BasisSet
ecp_centers=[ec],
library_name="lanl2dz",
) # → numpy.ndarray (n_bf, n_bf)
# Auto-helper (basissetdev-conditional; see § Phase-14e above)
ecp_centers, library_name = vq.auto_ecp_centers(mol, "lanl2dz")
# Drive any SCF with ECPs:
opts = vq.UHFOptions()
opts.ecp_centers = [ec]
opts.ecp_library = "lanl2dz"
result = vq.run_uhf(mol, basis, opts)
The same ecp_centers + ecp_library API works on every
molecular SCF driver: run_rhf, run_uhf, run_rks,
run_uks. Periodic SCF + ECP is not yet supported; queued
for v0.x.x once the periodic-Γ JKBuilder lands a periodic ECP
contribution.
Phase-14f: CRYSTAL INPUT ECP-block parser¶
basissetdev also adds a parser for CRYSTAL INPUT-format
ECP blocks, so basis sets distributed in CRYSTAL’s ECP
format (e.g. 5th-period pob bases) load without raising:
parsed = vq.basis_crystal.parse_crystal_atom_basis(crystal_input_block)
parsed.ecp # CrystalECP dataclass
parsed.ecp.terms # list of CrystalECPTerm
Same basissetdev caveat as the auto-helper above: if
basissetdev doesn’t merge, this parser is also unavailable.
Workaround: convert the CRYSTAL .basis file to BSE-format
.g94 + .ecp sidecar via the basissetdev fetch + split
scripts, then load via the standard
BasisSet(mol, "name") path.
Citations¶
For published work using ECPs:
libecpint software: R. A. Shaw, J. G. Hill, J. Chem. Phys. 147, 074108 (2017); R. A. Shaw, J. Chem. Phys. 159, 014103 (2023).
Stuttgart-Köln MDF family: Andrae, Häußermann, Dolg, Stoll, Preuß, Theor. Chim. Acta 77, 123 (1990) and per-element follow-ups in the libecpint repository documentation.
Hay-Wadt LANL family: Hay, Wadt, J. Chem. Phys. 82, 270 (1985); 299 (1985); 284 (1985).
The libecpint upstream project documents the per-element citations in its source repository.
See also¶
basis_sets.md— orbital basis selection, including which orbital bases are designed to be paired with an ECP.density_fitting.md— DF / RIJCOSX Fock build, fully ECP-aware.docs/license.md— full ECP licensing inventory.