Troubleshooting¶
When a vibe-qc run fails the error message is almost always concrete and actionable, but knowing which knob to turn requires a bit of context. This page collects the errors users actually hit during a first month with the code, with the canonical fix for each. The homepage admonition carries the current known-issues list (the things that are bugs in vibe-qc rather than misconfigurations on your end); this page covers the recoverable situations.
For “I think I found a bug” cases see CONTRIBUTING.md § Filing a bug.
SCF didn’t converge¶
Symptom, molecular¶
iter 85 -75.94731234 3.4e-04 2.1e-03 8
iter 86 -75.94731201 3.3e-04 2.1e-03 8
iter 87 -75.94731267 3.4e-04 2.1e-03 8
WARNING: SCF did NOT converge after 100 iterations
RuntimeError: SCF failed: max_iter (100) reached without convergence
The third column (dE) is bouncing instead of decreasing. The
fourth column (\(\lVert\mathbf{e}\rVert_F\)) is plateaued. The DIIS
subspace (the trailing integer) is full at 8.
Cause¶
DIIS is oscillating between two near-degenerate density solutions. Common triggers:
Stretched bond / dissociation-limit geometry (closed-shell reference becomes a poor description of the multireference state).
Transition-metal complex with ligand-field-degenerate d orbitals.
Broken-symmetry singlet biradical.
Wrong initial guess (e.g. AUTO picked SAP on a system where SAD starts closer to the true minimum).
Fix¶
In v0.8.0 the default scf_accelerator is already EDIIS_DIIS
(see the stiff-convergence tutorial),
if you’re seeing this on a v0.8.x build, the default already fired.
The remaining levers, in order of bang-for-buck:
opts.level_shift = 0.2 # Saunders-Hillier shift
opts.newton_threshold = 1.0 # Phase D2c Newton finalizer
opts.initial_guess = vq.InitialGuess.SAD # if AUTO picked SAP
opts.max_iter = 200 # last resort - buys time
If you specifically need the deprecated v0.7.x-style vanilla DIIS behaviour for parity work:
opts.scf_accelerator = vq.SCFAccelerator.DIIS
The full decision tree is in user_guide/scf_convergence.md § SCF accelerators.
Symptom, periodic¶
iter 18 -114.821342 1.2e-05 4.7e-04
iter 19 -114.821339 1.1e-05 4.8e-04
iter 20 -114.821345 1.2e-05 4.7e-04
For periodic SCF, oscillation in the last two digits is usually a sign of a convergence aid problem (band crossing the Fermi level at a coarse k-mesh) rather than a hard SCF instability. The fix is Fermi-Dirac smearing:
opts.smearing_temperature = "metal" # 0.005 Ha - see
# the `fermi_dirac_smearing` tutorial
If the periodic SCF lands at an impossible energy (over-bound by Madelung-scale shifts of ~0.5 Ha on H₂/STO-3G/30-bohr-box) the problem is something more fundamental, see CLAUDE.md § 7: that’s a bug, not a convergence-aid problem. File an issue with the input that reproduces it.
“Canonical orth dropped too many basis directions”¶
Symptom¶
RuntimeError: Canonical orthogonalisation dropped 17 of 234 basis
directions (overlap eigenvalues below 1e-7). The basis set is
nearly linearly dependent on this geometry/cell. See
docs/user_guide/linear_dependence.md.
Cause¶
The overlap matrix \(\mathbf{S}\) has near-zero eigenvalues, two or more basis-function combinations are nearly identical at the input geometry. Most common triggers:
Using a molecular basis set in a periodic calculation (e.g.
def2-tzvpfor solid NaCl). Diffuse functions overlap with periodic images. Use the pob- family for periodic systems*, see Why solid-state calculations usepob-TZVP.A vDZP / dhf- / x2c- basis** without an explicit ECP center (the heavy-element basis is short by Z core electrons; without the ECP the SCF reaches for them via diffuse-function overlap).
Atoms placed at or very near the same coordinates by an upstream geometry-conversion step (CIF → POSCAR rounding, duplicated atoms from a faulty supercell expansion).
Fix¶
Pick the appropriate fix for the trigger:
# 1. Use a solid-state basis instead.
basis = vq.BasisSet(sysp.unit_cell_molecule(), "pob-tzvp")
# 2. Filter out diffuse primitives at the BasisSet stage.
basis = vq.make_basis(sysp.unit_cell_molecule(), "def2-tzvp",
exp_to_discard=0.05)
# 3. Wire ECP centers explicitly for vDZP / dhf-* / x2c-*
# (see docs/user_guide/ecp.md § ECPCenter recipe).
opts.ecp_centers = [
vq.ECPCenter(Z=78, xyz=[0.0, 0.0, 0.0]), # Pt
...
]
opts.ecp_library = "ecp60mdf"
# 4. Auto-optimise periodic cutoffs jointly with screening.
opts.auto_optimize_truncation = True # default on v0.7+
user_guide/linear_dependence.md
walks through the v0.7 diagnostic stack
(vq.eigs_preflight, vq.disambiguate_critical_overlap,
auto_optimize_truncation) and the matching fix recipe in detail.
Memory abort before the SCF starts¶
Symptom¶
vibe-qc estimates this calculation will require ~218.4 GB of memory:
ERI tensor 186.0 GB
...
Available on this machine: 7.2 GB. ABORTING.
InsufficientMemoryError: Set `options.memory_override = True` (or
pass `memory_override=True` to `run_job`) to proceed anyway.
Cause¶
The pre-flight estimator (see user_guide/memory.md) sized the dense four-index ERI tensor or the MO transformation buffers and added the configured headroom (1.5x by default); that number exceeds the machine’s available RAM.
Fix, in order of severity¶
# 1. Smaller basis (always start here).
run_job(mol, basis="def2-svp", ...)
# 2. Density fitting - orders-of-magnitude smaller working set.
opts.density_fit = True
opts.aux_basis = vq.default_aux_basis_for(basis_name, kind="jk")
# 3. RIJCOSX - even smaller working set for large hybrid-DFT.
opts.density_fit = True
opts.cosx = True
# 4. Override the check (you accept the risk of swap-thrashing).
run_job(mol, ..., memory_override=True)
density_fit=True is the right answer for ~250-1000 basis
functions; cosx=True is the right answer above ~1000. See
user_guide/density_fitting.md.
Note
The old v0.7.3 DF integral-kernel SIGSEGV on auxiliary bases with
l >= 1 shells is fixed in current builds. The active DF-RHF suite runs
def2-universal-jkfit and cc-pvdz-jkfit and checks PySCF parity. If an
older checkout still segfaults below the Python boundary, it usually has a
stale libint install built without the required ERI2/ERI3 flags; follow the
clean rebuild recipe in updating.
KeyError: 'Ne' (or any other element) on basis load¶
Symptom¶
KeyError: "basis set 'pob-tzvp' has no entry for element 'Ne'"
Cause¶
The bundled .g94 for that basis set covers a subset of the
periodic table that doesn’t include the requested element. Known
gaps:
Basis |
Missing |
Mitigation |
|---|---|---|
|
Ne |
Use the def2-TZVP Ne block, or switch the whole calculation to def2-TZVP / cc-pVTZ |
|
Si, Fe, and the transition metals (only the 19 lightest elements are parameterised) |
Use |
Fix¶
Either:
Switch to a basis that covers the element (
def2-tzvp/cc-pvtzfor molecular work,pob-tzvp-rev2for periodic).Add the element by hand: copy the matching block from BSE into a
custom/*.g94file (see user_guide/basis_sets.md § Custom basis sets) and rebuild the library with./scripts/setup_basis_library.sh.
“DF gradient disagrees by ~115 mHa/bohr”¶
Symptom¶
Geometry optimisation drifts away from the true minimum when
density_fit=True is on; or hand-written FD gradients disagree
with compute_gradient by ~100 mHa/bohr on glycine / def2-TZVP.
Cause¶
Fixed in v0.8.0. A libint engine-state leak in the 3c-ERI
gradient kernel (compute_3c_eri_gradient_weighted in
cpp/src/df.cpp). Two adjacent same-l heavy atoms (e.g. the
carboxyl O=C-O-H oxygens in glycine and formic acid) caused the
engine to leak derivative-buffer state across compute() calls.
Fix¶
Upgrade to vibe-qc ≥ v0.8.0. The regression guard is
tests/test_df_gradient.py::test_df_rhf_gradient_hcooh_def2_tzvp_matches_direct.
density_fit=True gradients are now safe at def2-TZVP-class
basis sets.
“Analytic RHF gradient is wrong”¶
Symptom¶
Geometry optimisation walks to the wrong minimum on a system with f-shells AND ≥2 different second-row elements (e.g. CO, CH₂O, glycine + def2-TZVP). Magnitude can reach 161 mHa on glycine with a recent libint build.
Cause¶
Fixed in v0.8.0. The two_electron_gradient_contribution
(direct 4-index ERI gradient) kernel relied on libint’s internal
DerivMapGenerator unscramble path, which had a buggy
derivative-to-atom routing for high-l mixed-l shell quartets.
Fix C rewrites the kernel as a canonical 1/8 shell-quartet loop
with explicit l-canonical reorder before engine.compute().
Fix¶
Upgrade to vibe-qc ≥ v0.8.0. The regression guard is
tests/test_gradient_f_bug.py::test_h2co_def2_tzvp_gradient_matches_pyscf.
Direct analytic gradients with f-shells are now correct
(post-fix H2CO/def2-tzvp matches PySCF to ~5e-11 Ha/bohr).
“Periodic (BIPOLE) forces / geometry optimisation look wrong”¶
Symptom¶
A periodic geometry optimisation with jk_method="bipole" walks the
wrong way, or compute_bipole_gradient_rhf/uhf/rks/uks disagrees with a
finite-difference of the BIPOLE energy, including at Γ, and emits a
UserWarning about a “RESEARCH PREVIEW” gradient.
Cause¶
The analytic BIPOLE gradient is still a gated research preview. The maintained RHF/UHF Gamma cases, maintained RHF/UHF multi-k cases, and Gamma-local RKS/UKS LDA response regressions now track finite differences, but broader KS functional/cell coverage, meta-GGA tau-Pulay, and multipole far-field certification remain open. RKS/UKS finite-temperature, fractional-occupation, and multi-k analytic calls now fail fast to the finite-difference path. If a system sits outside the maintained surface, the analytic driver can still disagree with the real BIPOLE total-energy finite difference.
Since M5, erfc short-range energies use a larger precision-derived internal
ket-image domain and attached-symmetry crystals use a pair-resolved Fock
domain by default. The analytic derivative kernel does not yet differentiate
either traversal, so analytic gradient calls now fail closed when the SCF
result records one of them. Use the finite-difference path for the production
M5 energy. Setting sr_image_precision=None and
use_fock_symmetry_reduce=False is only a historical-domain diagnostic, not
the recommended force workflow.
Fix¶
Use the exact finite-difference gradient, it is the default for all production paths as of the 2026-05-31 fix:
# Geometry optimisation (FD forces by default - force_mode="fd")
from vibeqc.bipole_optimize import relax_atoms
result = relax_atoms(system, "sto-3g", kmesh, method="RHF")
# Standalone exact gradient (any method)
from vibeqc.bipole_gradient import compute_bipole_gradient_fd
g = compute_bipole_gradient_fd(system, "sto-3g", kmesh, opts,
method="RKS", functional="pbe")
run_periodic_job(optimize=True) and the periodic NEB driver already use
this path. If a displaced SCF point does not converge, the FD helper raises
instead of differentiating a failed iterate; pass
require_converged=False only when deliberately diagnosing a failed surface.
The analytic drivers remain available for research (force_mode="analytic");
stay on the maintained regression surface if you compare them directly. The
full analytic gradient certification is tracked in handovers/HANDOVER_BIPOLE_GRADIENT.md;
the regression guards are in tests/test_bipole_gradient.py.
CASPT2/NEVPT2 compute_corr_grad=True gradient has no PT2 correction (v0.15.17)¶
Symptom¶
On v0.15.17, a CASPT2 or NEVPT2 analytic gradient requested with
caspt2_options.compute_corr_grad=True (or the NEVPT2 equivalent)
silently returns a gradient without the effective-density PT2
correction: an SA-CASPT2 gradient comes out identical to the
state-specific one. No warning or error is raised.
Cause¶
The effective-density builder imported the state_rdm12_cpp C++
kernel outside its Python-fallback guard, and that kernel was never
exported from the compiled core, so the import always raised, and the
gradient driver’s graceful-degradation handler silently dropped the
correction on every build.
Fix¶
Fixed on main (2026-07-02): the import moved inside the fallback
guard and the pure-Python 2-RDM fallback was repaired (validated to
machine precision against a brute-force spin-orbital reference), so
the correction is always computed. Fixed again on current main
(2026-07-03): the restored C++ state_rdm12_cpp fast path now splits
the ket-side E_rs|c> and bra-side E_sr|c> branches correctly and
is re-enabled. Current main also warns if a requested PT2
effective-density, chain-rule, CI-Lagrangian, or explicit Lagrangian
correction has to fall back to a less complete gradient contribution.
On v0.15.17, treat compute_corr_grad=True gradients as CASSCF-level
only; regression guards live in tests/test_casscf_gradient.py
(TestDyallChainRule, TestPT2Lagrangian, TestSASpecificity, and
the degradation-warning regression) and tests/test_pt2_density_rdm.py.
Multi-k GDF HF / hybrid-KS exchange wrong on diffuse bases in tight cells (v0.15.26 to v0.15.31)¶
Symptom¶
On v0.15.26 through v0.15.31, run_krhf_periodic_gdf (and hybrid
run_krks_periodic_gdf) on a true multi-k mesh returns a wrong total
energy when the AO basis carries functions whose inter-cell overlap is
large, for example the ultra-diffuse Li 2sp shells of STO-3G on the
rocksalt LiH primitive cell. Measured on LiH/STO-3G at kmesh (2,1,1):
+2.04e-2 Ha/cell versus the same Hamiltonian’s real-Gamma direct route
and versus pre-v0.15.26 releases. Gamma-only runs, pure (non-hybrid)
KS, and vacuum-padded cells (molecules in boxes, chains with vacuum)
are unaffected at the 1e-8 gate level.
Cause¶
The v0.15.26 dense-core tail speedup mirrored shell-pair blocks of the
AO-pair Fourier transform at k = 0, using the transpose identity
FT_mu,nu(G) == FT_nu,mu(G). That identity additionally requires
every evaluation frequency to be a reciprocal-lattice vector, which is
false for the momentum-shifted meshes G+q that the Bloch-pair GDF
fit evaluates for every (k_bra != Gamma, k_ket = Gamma) exchange
pair. The mirrored fit is silently wrong wherever inter-cell AO-pair
terms are non-negligible.
Fix¶
Fixed on main (2026-07-10): the mirror is disabled unless the
evaluation frequencies are verified reciprocal-lattice vectors; the
Gamma-tail speedup itself is kept. Flagged for the next v0.15.x patch
release. On affected releases, cross-check any multi-k HF or hybrid-KS
number on diffuse-basis tight cells against run_ccm_rhf_direct
(real-Gamma route, unaffected) or upgrade to main. Regression pins:
tests/test_rsgdf_dense_mesh_tail.py::test_gamma_mirror_disabled_on_momentum_shifted_mesh
and the restored LiH (2,1,1) parity gates in tests/test_ccm_direct.py.
Multi-k BIPOLE RHF raises TypeError instantly; BIPOLE RKS ignores bz_integration="gilat" (v0.15.23 to v0.15.30)¶
Symptom¶
On v0.15.23 through v0.15.30, any run_periodic_job(..., method="RHF", jk_method="bipole", kpoints=...) run at a multi-k mesh
fails in 0.0 s with
TypeError: run_pbc_bipole_rhf() got an unexpected keyword argument 'bz_integration'
even though you never passed bz_integration. On the same releases,
method="RKS" with jk_method="bipole" accepts
bz_integration="gilat" and records it in the .out, but the SCF
silently runs with default Aufbau occupations - the Gilat-Raubenheimer
integration announced in those releases is inert on the BIPOLE RKS
route. (BIPOLE UHF/UKS + gilat correctly fail closed with
NotImplementedError on all releases; GDF routes are unaffected.)
Cause¶
The commit wiring Gilat through GDF and BIPOLE RKS (891acead, first
in v0.15.23) put the new keyword on the wrong callsite in
periodic_runner.py: it passed bz_integration= unconditionally to
run_pbc_bipole_rhf (which did not accept it yet) and never passed it
to run_pbc_bipole_rks (which did).
Fix¶
Fixed by 38c4afed, shipped in v0.15.31 - upgrade, or pin a tag
outside the window. No energy computed on the affected tags is wrong:
the RHF route fails before any integral work, and the RKS route
computes a correct default-occupation result (only the requested
Gilat scheme is dropped). Regression pins:
tests/test_periodic_runner_bipole_callsites.py (live RHF multi-k
route + a static callsite/signature contract over every
run_pbc_bipole_* dispatch) and the strengthened forwarding assertion
in tests/test_bz_integration_gilat_scf.py. Full provenance:
BUG-PER-002 in handovers/HANDOVER_OPEN_BUGS_V015.md.
ImportError: cannot import name 'EEQOptions' from 'vibeqc._vibeqc_core'¶
Symptom¶
ImportError: cannot import name 'EEQOptions' from
'vibeqc._vibeqc_core' (/path/to/_vibeqc_core.cpython-...so)
Cause¶
You have a Python source tree from a newer commit but the
compiled _vibeqc_core.so is from an older one. Symbol mismatch.
Fix¶
pip install -e . --no-build-isolation --force-reinstall
This rebuilds the C++ extension against the current Python sources. On macOS the rebuild takes ~30-90 s; on Linux similar. If you’re on a worktree, memory dictates a per-worktree venv so the parent venv doesn’t get clobbered.
ModuleNotFoundError: No module named 'vibeqc'¶
Symptom¶
ModuleNotFoundError: No module named 'vibeqc'
Cause¶
You ran the script with the wrong Python, the system python3
instead of the venv-installed one.
Fix¶
Either give the full path to the venv’s interpreter:
~/path/to/vibeqc/.venv/bin/python my_input.py
or activate the venv first:
source ~/path/to/vibeqc/.venv/bin/activate
python my_input.py
“Cell-list construction returned 0 cells”¶
Symptom¶
RuntimeError: cell-list construction returned 0 cells for cutoff
12.0 bohr on this lattice - check that the lattice matrix is full
rank and the cutoff is positive.
Cause¶
Either a degenerate lattice (zero-rank column) or a non-positive
lattice_opts.cutoff_bohr. Common when a 1D-chain input has the
two vacuum axes accidentally set to 0 instead of a wide separation:
sysp = vq.PeriodicSystem(
dim=1,
lattice=np.diag([4.0, 0.0, 0.0]), # <- bug - vacuum axes are 0
unit_cell=[...],
)
Fix¶
Set the non-periodic axes to a generous vacuum separation:
sysp = vq.PeriodicSystem(
dim=1,
lattice=np.diag([4.0, 30.0, 30.0]), # 30 bohr vacuum decouples images
unit_cell=[...],
)
vibeqc-cite: manifest missing after a job¶
Symptom¶
$ vibeqc-cite output-h2o
error: manifest file not found: output-h2o.system
Cause¶
Either the job hasn’t finished writing the manifest (look for
output-h2o.system.tmp), or the path is wrong, or the run was
killed before the initial manifest landed.
Fix¶
Check
output-h2o.systemactually exists:ls output-h2o.*.Try the stem with the
.outsuffix,vibeqc-cite output-h2o.outalso works (the CLI normalises via.with_suffix(".system")).For pre-v0.8.x runs (no manifest at all), use
vibeqc-cite’s inability to find the manifest as a diagnostic, the run predates the citation surface. Re-run the SCF on a current build to get the bibliography.
Native D4 dispersion (backend="native") scope + accuracy¶
What it is¶
compute_d4(..., backend="native") uses vibe-qc’s own MPL-2.0 D4
implementation (no dftd4 dependency). As of 2026-06-26 it is
parity-validated for elements H-Ne: its reference C6 (per-atom
Eq.-6 extraction over a correlated CPKS/PBE38 polarizability) agree
with the dftd4 package to ~5 % (CH₄) / ~8 % (H₂O) and the CH₄-dimer
D4 energy to <0.05 kcal/mol. No warning is emitted. (The
D4NativeExperimentalWarning class is retained for back-compat but is
no longer raised – earlier versions emitted it while the reference
data was being developed.)
Limitation¶
The native reference catalogue covers H, He, B, C, N, O, F, Ne
only. backend="native" on a molecule containing any other element
raises a clear error (no reference data). For full periodic-table
coverage use the default backend="dftd4" (optional dftd4 package,
bit-exact to upstream) – every built-in dispersion="d4" route does.
Tracked in HANDOVER_D4_NATIVE.md;
extending the catalogue past period 2 is the prerequisite for making
native the default backend.
Things that are not errors¶
A handful of things look alarming but aren’t:
vibe-qc estimates this calculation will require ~218.4 GB,Proceeding (override), you passedmemory_override=Trueand the run is going through despite the estimate. Wait and see; the estimate’s configured headroom plus the dense-ERI-tensor estimate is conservative.canonical_orth dropped 2 of 234 basis directionswith a small number, vibe-qc reports any drop; 1-3 dropped basis directions out of hundreds is typical of a tight cell or large basis. Only worry if the drop exceeds ~5% of the basis count.# no citation route for basis 'foo'in.references, your basis isn’t in the routing table. The job ran fine; the bibliography is just missing one entry that you’ll need to add by hand. The fix is to extenddatabase.tomlin your next PR.Used 4 OpenMP threadseven though you setOMP_NUM_THREADS=16the system’s actual core count (or the cgroup limit on a cluster node) wins over the env var. Sanity-check with
nprocorsysctl hw.physicalcpu(macOS).
BIPOLE absolute energies on tight ionic crystals (fixed, corrected gauge is the default at Γ and multi-k, all four drivers)¶
Symptom¶
jk_method="bipole" total energies on dense ionic cells (MgO primitive
is the reference case) sit several hartree off converged references at
the same k-mesh, e.g. MgO/STO-3G RKS [2,2,2]: BIPOLE −266.1 Ha vs
PySCF-GDF −270.5 Ha (which independently agrees with converged CRYSTAL
to 5 mHa). The SCF converges cleanly, the stationary point’s
absolute energy is what’s off.
Cause + status¶
Root-caused (2026-06-10, fixed-density component audit vs PySCF): (1) the EXT EL-SPHEROPOLE term is a double-count in vibe-qc’s explicit-jellium gauge; (2) the Γ-locality density projection drops real cross-cell contributions to T/V_ne/J on tight cells; (3) the full-Coulomb direct exchange series is formally divergent with the non-decaying finite-k-mesh Bloch density (any finite value is a cutoff artefact).
Fixed at Γ for all four drivers (RHF/RKS/UHF/UKS, on by default;
2026-06-10/11) by the Ewald exchange split: K = K_SR(erfc ω, direct) + K_LR(erf ω, reciprocal K≠0) + (ξ_M − π/(Vω²))·S·D·S
(probe-charge Ewald / Madelung G=0 correction,
exchange_exxdiv='ewald', PySCF-equivalent), full-Bloch density,
spheropole omitted. Validated out-of-process against PySCF GDF:
exchange element-wise to 8e-4 at the converged MgO density; H₂
12-bohr Γ to ~1 µHa (RHF) / ≤1 mHa (KS + open-shell variants). At
multi-k the corrected gauge is now the DEFAULT for all four
drivers too (option (b) Phase 3 + the Phase-5 flip, 2026-06-13):
the q = k−k′ LR-exchange channels + BvK-supercell Madelung
correction (α_HF-scaled for hybrid RKS/UKS, per spin for UHF/UKS),
validated on the H₂ box [2,1,1] vs PySCF (RHF +0.12 mHa at cutoff 7
/ −0.002 at 12, RKS SVWN/PBE0 +0.04/+0.06, UHF +0.03, UKS k-shift
0.013) and to +0.0001 mHa/cell against the explicitly-doubled
supercell at Γ. Pass use_exchange_ewald_split=False for the
legacy gauge (kept for the legacy-gauge analytic gradient + parity
diagnostics; it mis-states ionic absolute energies). The corrected
multi-k gauge needs a Monkhorst-Pack mesh; an ad-hoc k-list at
multi-k falls back to the legacy gauge under the auto default.
If your Γ ionic-crystal energies still look wrong¶
Heed the S(Γ) fold-truncation line in the output. The corrected gauge contracts full Bloch folds; diffuse AO tails (STO-3G Mg 3sp, outermost exponent ≈ 0.046) keep cross-cell overlaps alive past 16 bohr. MgO/STO-3G needs
cutoff_bohr ≥ 12(≈5e-3 fold truncation) and ≥16 for tight work; at 8 bohr the fold error is 0.39 and the SCF can land in spurious metric-artifact states.Check the SCF basin. Minimal-basis ionic Γ SCF can have multiple solutions when the density is corrupted by an under-converged operator (the pre-v0.14 cross-cell XC bug era produced such basins). At a healthy density the fixture is benign: PySCF KRKS converges MgO/STO-3G to one basin from every standard guess (minao/1e/atom, Γ and [2,2,2], re-verified 2026-07-13), and the corrected-gauge BIPOLE RKS does the same. For absolute claims, warm-start from a reference density (
initial_density=) or compare stationary points explicitly.Keep KS smearing well BELOW the gap. A minimal basis badly underestimates the gap (MgO/STO-3G: ~0.3 eV vs the ~7.8 eV experimental gap). A
smearing_temperaturewhose window is comparable to that small gap fractionally occupies states across it and settles a near-metallic basin hundreds of mHa off the physical (gapped) solution, e.g. MgO RKS Γ atT = 0.01 Ha(≈0.27 eV) landed +330 mHa with a 42 mHa entropy term (2026-06-13). The RKS/UKS drivers now emit abasin_warning(logged + on the result object) when finite-T smearing straddles the gap. The fix is a sub-gap smearing: reducesmearing_temperatureuntil thebasin_warningclears, or drop smearing once converged. For a gap as small as MgO/STO-3G’s (where even0.002 Ha≈ 0.05 eV still straddles, the converged gap there is only ~0.1-0.3 eV depending on the basin) that can mean≤ 0.001 Haor no smearing at all. Usually no aid is needed at all: plain DIIS converges the MgO-class tight ionic cells monotonically in ~10 iterations (Gap-B validation 2026-07-13), so the BIPOLE KS auto-FMIXING (30%) now engages only for DIIS-off runs, where it damps the bare Roothaan iteration (CRYSTAL-style). Smearing is for near-degeneracy, not as a blanket ionic-cell aid.Keep the M5 SR image-domain default for absolute energies. Every erfc short-range build now derives its absolute internal ket-image radius from
sr_image_precision=1e-6and enables separation-aware QQR screening. This repairs the 515 mHa short-range Hartree loss measured on the MgO split fixture.sr_image_precision=Nonerestores the old truncated behavior for reproducibility only and suppresses automatic Fock reduction on an active erfc SR route; it is not an accuracy setting. An explicitsr_image_extent_bohroverrides the precision-derived radius.Pure semilocal RKS still uses exact-FT Hartree by default. Retiring that route in favor of the repaired SR+LR composition is a separate change. Its dense-core reciprocal-tail warning remains relevant until that change lands. The M5 image-domain default applies whenever an erfc SR build is actually present.
Remaining reference choices¶
Energy differences at fixed cell/density class (geometry scans, relative energies on covalent/molecular-limit cells) are much less affected, and H₂-class validation cells agree with references to sub-mHa. For an independent implementation comparison, use the GDF route and a converged reciprocal cutoff. Historical BIPOLE values produced with the unpadded SR domain must be regenerated before absolute comparison.
Still stuck?¶
gitlab.peintinger.com/mpei/vibeqc/-/issues
When filing an issue, include:
The full error traceback.
The minimal Python script that reproduces it.
The
output-*.systemmanifest (carries the hardware + library versions vibe-qc needs to reproduce).The vibe-qc version (
python -c "from vibeqc import VIBEQC_VERSION; print(VIBEQC_VERSION)").
The maintainer or release chat will triage. For security vulnerabilities follow SECURITY.md instead of opening a public issue.