Unreleased¶
[v0.15.42] - 2026-07-17 - Neese’s Cheetah¶
Fixed: vibe-view replication no longer fights a playing animation (2026-07-16)¶
Applying periodic replication (Nx/Ny/Nz supercell) while a trajectory or
vibration animation was playing was buggy: the animation loop kept pushing
single-cell frames on its timer, racing and overwriting the supercell
rebuild so it flickered or never appeared. update_replication now stops any
playing animation first, so the supercell rebuild wins. Apply Replication
also reports “Replication: N×N×N” so it’s clear the action took effect. (The
tiling itself was already correct: exactly 4× geometry at 2×2, 9× at 3×3.)
Fixed: vibe-view installs a discoverable /Applications app (2026-07-16)¶
install-electron.py now also installs vibe-view.app into an Applications
folder (/Applications, or ~/Applications when the former isn’t writable),
so Spotlight (Cmd+Space) finds it and it launches from the Dock like a normal
app. Previously the only bundle lived inside node_modules/electron/dist,
which Spotlight never indexes. The installed copy tracks live checkout edits
(its launcher shim requires the checkout’s main.js); the step is idempotent
and skips the copy when already current. Re-run python3 install-electron.py
once to install it.
Fixed: RP209 output telemetry, provenance, and manifest truthfulness (2026-07-16)¶
Performance reports now cap the native OpenMP runtime by explicit scheduler and environment allocations. A one-CPU job on a 96-core host reports one thread and no longer fabricates 0.01x parallelism; host-wide available-core counts are never used as an allocation fallback.
The
.systemmanifest now marks its own output row withchecksum_status = "self-excluded", an empty SHA, and a zero byte-count sentinel. The lifecycle no longer records a pre-finalization self-hash, andvibeqc-outputssuppresses impossible stale self-digests in legacy files.Per-calculation manifests record the full immutable 40-character Git SHA when build provenance is available instead of only the abbreviated SHA.
Population citation assembly now cites NPA only when non-empty NPA rows were actually emitted without an NPA error. A fail-closed NPA calculation no longer claims the Reed-Weinhold method merely because population output was requested.
Fixed: vibe-view draggable splitter + working toolbar tooltips (2026-07-16)¶
Two issues reported from the running app:
The viewport/plots splitter now resizes instead of rotating the molecule. The bar between the 3D view and the bottom panel is draggable; a full-window overlay during the drag keeps the VTK canvas from swallowing the gesture. (Previously the bar was decorative and the only resize grip was the easy-to-miss native corner handle.)
Toolbar icons now show tooltips on hover. The native
titletooltip was unreliable/slow in the Electron shell; a lightweight instant tooltip is shown from the same labels (and the nativetitleis removed so it can’t double up).
Added: vibe-view command palette (Ctrl/Cmd+K) (2026-07-16)¶
A command palette opens with Ctrl/Cmd+K from anywhere and fuzzy-searches quick actions — camera views, view presets, render HOMO/LUMO, energy diagram, toggle labels/background, screenshot, detect point group. Selecting one runs it and closes the palette. Keyboard-first parity with the toolbar.
Added: vibe-view click-to-render MO from the energy diagram (2026-07-16)¶
Clicking any level in the orbital energy diagram now renders that MO. Each
level carries its {spin}:{index} render key; a click in the sandboxed
diagram iframe posts the key to the app, which renders the orbital and syncs
the MO picker. Completes the frontier-orbital UX alongside the HOMO/LUMO
buttons.
Added: semiempirical NEB energy-gradient routes (2026-07-16)¶
run_neb(...) now accepts every executable molecular semiempirical
energy-gradient route and the existing Gamma-periodic DFTB0/SCC-DFTB,
GFN2-xTB, PM6, and OM1/OM2/OM3 routes. Gamma DFTB0 uses its native analytic
gradient at the validated 15-bohr image domain; SCC-DFTB and GFN2-xTB use
central differences of converged total energies; periodic PM6/OMx use their
native finite-difference batches. Full k-point semiempirical paths remain
fail-closed.
The current closed-shell MSINDO-SECCM route also drives NEB through its analytic fixed-topology gradient. Periodic endpoints require the explicit cyclic translations to match the QVF lattice, and non-MSINDO SECCM adapters remain unavailable. Semiempirical reaction-path QVFs carry method-specific citations without a fallback Gaussian basis or XC functional. Periodic MACE and SECCM QVFs no longer cite the unrelated periodic Gaussian-LCAO route. All NEB routes reject endpoint charge or multiplicity mismatches before image evaluation instead of rebuilding the product with the reactant’s state.
Changed: content-sized titled headerless output blocks (2026-07-16)¶
The document layer now provides HeaderlessBlock/HeaderlessColumn for
titled label/value and matrix-shaped bodies, optional footers/dividers, and
ragged right-hand annotations that do not widen the content rule. Energy
components, molecular orbital energies, and periodic system summaries use the
primitive instead of fixed 52/54/56-character rules. Orbital values retain
their deliberate parallel Hartree/eV columns through explicit-unit
render_energy calls.
The seven affected golden snapshots were deliberately regenerated. Reviewed
diffs change only the targeted rule lengths: molecular RHF/RKS/UHF/Hessian/
TD-DFT orbital or energy blocks and periodic RHF/geometry-optimization system
and energy blocks. The periodic label/value rows retain their historical
per-row minimum label width, so a runtime label such as periodic length
does not shift unrelated equals signs.
Changed: periodic artefact writes use role dispatch (2026-07-16)¶
run_periodic_job now constructs its OutputWriter before compute, publishes
the complete plan with status="running", and marks the manifest complete or
crashed at the public exit after any geometry optimization. This includes the
basis-free periodic semiempirical route. Molden, method-specific population
pairs, Extended XYZ, POSCAR, CIF, citation files, structure/density XSF, QVF,
and runtime .opt.* files all route through dispatch_role; the end-of-job
filesystem sweep is retired. Late-bound route provenance updates the original
manifest in place.
The population plan distinguishes standard, BIPOLE, and AICCM2026DEV-B
summary formats while the shared multi-output adapter still computes and
writes each .txt/.json pair once. A representative periodic RHF+BIPOLE
job with structure formats, QVF, and optimization matched a direct-writer
control byte-for-byte for every deterministic artefact it produced; every QVF
scientific member also matched, leaving only banner/timings, QVF wall-time
provenance, and the intentionally improved live manifest lifecycle dynamic.
Added: vibe-view spectra X-unit toggle + CSV export (2026-07-16)¶
Spectrum panels gain an X unit selector (native / cm⁻¹ / eV / nm; e.g. UV-Vis eV→nm) and an Export CSV button. Broadening stays in native units (correct physics) and the axis is remapped for display; the CSV carries the broadened envelope and the discrete peaks in the chosen unit.
Changed: molecular artefact writes use role dispatch (2026-07-15)¶
The molecular run_job path now routes molden, population, density and
per-orbital cubes, citations, final XYZ, runtime NTO molden files, and QVF
through OutputWriter.dispatch_role. Existing progress stages and
PerfScope timings remain around the same writer operations, while successful
files are recorded in .system at dispatch time instead of by the end-of-job
plan sweep. Dispatch now supports strict runner error propagation, path/format
targeting, runtime-conditional files, and a per-call multi-output cache so the
population analysis runs once for its .txt and .json siblings.
The plan also preserves integer cube requests as .mo_<index>.cube, aligns
the QVF role with its dispatcher key, and declares the actual periodic XSF
paths without structure/density collisions. A representative RHF audit found
the molden, XYZ, population, cube, and citation payloads byte-identical before
and after; every scientific QVF entry was also byte-identical, with only its
recorded wall time changing.
Added: vibe-view spectra display controls (2026-07-16)¶
Spectrum panels gain a Broadening slider (0.2-4x the per-kind default width) and a Normalize checkbox (peak scaled to 1; sticks scaled with it) – both also apply to the multi-file comparison overlay.
Fixed: vibe-view branded Electron launches from the Dock (2026-07-16)¶
The branded dev Electron.app had no default app baked in, so a Dock or
Finder double-click exited silently; only the vibe-view desktop spawn path
worked. install-electron.py now writes a two-file launcher shim into
Contents/Resources/app that loads the checkout’s main.js, and ad-hoc
re-signs the bundle. Re-run python3 install-electron.py once per checkout
(or after moving it) to (re)generate the shim.
Added: vibe-view one-click view presets (2026-07-16)¶
A “View preset” selector at the top of the Display panel applies a complete look in one click: Publication (white background, publication-grade material, no SSAO or labels), Presentation (dark, glossy, SSAO depth), Analysis (flat material with atom labels). Each preset orchestrates the existing representation, material, SSAO, and label controls in an order that survives the scene rebuilds.
Changed: marketing site at the root; docs move to /docs/ (2026-07-16)¶
vibe-qc.com now serves a new static marketing/landing site (Astro, in
website/) at the domain root; the Sphinx documentation moves under /docs/
(html_baseurl, robots/sitemap CANONICAL, and the CI deploy target all
updated). Docs remain the single source of truth and build unchanged. A
rolling preview of main is published at vibe-qc.com/preview/ by the new
website-staging CI job (noindex; served from its own /web/preview/
subtree).
Release-operations note: the first release deploy carrying this change
relocates every documentation URL from the root to /docs/. Before that
deploy, apply the Apache legacy-URL redirects in
deploy/htaccess-redirects.conf to the server’s root /web/.htaccess — the
CI deploy deliberately excludes .htaccess — or old doc URLs (/quickstart.html,
/_static/…) will 404 in the gap.
Fixed: native molecular D3(BJ) is quantitative for H-Ar (2026-07-16)¶
The native molecular D3(BJ) backend now ships the complete H-Ar corner of Grimme’s coordination-dependent C6 reference grid (1,443 active references), the corrected D3 covalent radii, all 156 simple-dftd3 BJ parameter sets, and the full analytic C6(CN) chain rule for two-body and ATM gradients. The root cause of the RP208 overbinding was the earlier one-reference-per-element framework table, compounded by incorrect coordination radii; it was not pair double-counting, geometry units, damping dispatch, periodic images, or an SCF setting.
For the archived rounded methane-dimer geometry and printed B3LYP parameters,
the native component changes from -0.0123798158 Ha to
-0.00517760871046 Ha, agreeing with simple-dftd3 to 1.3e-15 Ha and with
ORCA 6.1.1 (-0.005177608303 Ha) within the archived coordinate precision.
Native/reference analytic gradients agree within 8e-14 Ha/bohr, including
the ATM term. backend="auto" now uses this self-contained quantitative path
for H-Ar and retains optional dftd3 fallback for heavier elements. These are
geometry-only dispersion comparisons; no total B3LYP parity is claimed for
the unmatched SCF routes.
Fixed: vibe-view desktop dodges stale servers on its port (2026-07-16)¶
A crashed or tray-forgotten session can leave an old vibe-view serve
holding the default port for days; a fresh dock launch then collided with it
and died silently (observed: a 6-day-old server on 8765 blocked every
launch). The Electron shell now probes its port before spawning the server
and, if the port is taken, transparently moves to an OS-assigned free one.
Fixed: stale BIPOLE analytic-preview reconstruction gate (2026-07-16)¶
The internal Gamma RKS Fock-reconstruction regression now tests its stated
1e-8 matrix identity from a converged plain-DIIS density instead of enabling
the unrelated FMIXING state machine with a numerically zero positive weight
and demanding SCF energy changes below the fixed-grid numerical floor. The
reconstructed and SCF Focks agree under the original 1e-8 acceptance bound;
no production convergence policy or analytic-gradient capability changed.
Fixed: bounded BIPOLE NEB and relaxation diagnostics (2026-07-16)¶
Periodic run_neb and relax_atoms now expose sr_image_precision
explicitly and forward it to every BIPOLE reference, objective, and
finite-difference SCF. The production default remains the precision-derived
M5 padded domain at 1e-6; no image radius is capped. Tiny-cutoff dispatch,
warm-start, and QVF tests that do not validate numerical accuracy now select
the documented historical-domain diagnostic with None, restoring bounded
runtime without weakening production correctness.
Fixed: duplicate BIPOLE M5 energy regression contract (2026-07-16)¶
The cross-backend H2/STO-3G/12-bohr test now records the independently
sanctioned M5 padded-domain BIPOLE energy already owned by the primary
integration regression. Both routes were rerun together against a freshly
rebuilt native extension and obtained -1.122138533948132 Ha. This is a stale
test-pin correction only; production domain selection, tolerances, and
numerical results are unchanged.
Fixed: molecular D3(BJ) no longer silently uses the qualitative stub (2026-07-15)¶
This interim safeguard, superseded by the complete native implementation
above, changed compute_d3bj(..., backend="auto") to select the quantitative
dftd3 backend and fail with an installation hint when that optional package
was missing. Previously auto silently selected the builtin C6
framework table, even though that table has only one free-atom coordination
reference per supported element and explicitly does not reproduce molecular
D3(BJ) energies quantitatively. The RP208 methane dimer therefore received
-0.0123798158 Ha instead of the independent simple-dftd3 / ORCA component
-0.0051776083 Ha.
Regression coverage pins the RP208 geometry-only component, isolated methane, a two-hydrogen pair, the separated-dimer limit, and the missing-package fail-closed dispatch. No SCF or corrected-total parity claim is made.
Fixed: default-Γ ionic RHF/GDF stays on the PySCF-parity driver (2026-07-16)¶
run_periodic_job(jk_method="gdf") on a Γ-only closed-shell ionic cell
(MgO-class) silently fell back to the legacy molecular-limit Γ driver — whose
dense-core absolute energies are parity-held (G-GDF-001) — because the
convergence="auto" ionic-insulator profile resolves FMIXING 30% and the
default-Γ pure-GDF gate requires fock_mixing == 0. The GDF capability
filter now zeroes AUTO-resolved fock_mixing / level_shift when the run
otherwise qualifies for the PySCF-µHa-validated run_pbc_gdf_rhf (the
parity route carries its own DIIS/accelerator convergence and the auto-sized
high-|G| tail); explicit fock_mixing= / fmixing_percent= /
level_shift= requests still take the legacy fallback rather than silently
dropping the knob. Verified live vs PySCF 2.13.1 out-of-process on the P01
MgO/STO-3G Γ cell: the runner routes to pbc-gdf-rsgdf (no +PARITY_HELD),
converges in 10 iterations without Fock mixing, and the auto-tail energy
reproduces the recorded P01 calibration target to −0.25 µHa
(= PySCF GDF at matched default precision to ~1 µHa; PySCF’s own answer
moves −163 µHa under precision=1e-12, a shared truncation ladder now
documented at _RSGDF_PARITY_TAIL_RATIO). Routing regression:
tests/test_runner_gamma_gdf_routing.py (auto knob → pure driver; explicit
knob → legacy fallback). Docs: periodic_methods.md § 3.2.
[v0.15.41] - 2026-07-16 - Neese’s Cheetah¶
Changed: AICCM construction identity requires an explicit binding (2026-07-16)¶
D90 closes the remaining post-D89 naming leaks. Namespace ownership, real-Gamma evaluation, and inherited solver ancestry no longer confer a Γ-CCM or χ-CCM construction identity. The neutral-only UCCSD/DLPNO and one-call RI-MP2/RI-CCSD routes are now described as neutral fitted-torus correlation controls. The mixed-boundary wire Green-function implementation is an A-line wire construction/control until its full Hamiltonian is explicitly derived from and gated against the union-and-weight Γ-CCM map. This changes no API, equation, numerical result, or route availability; χ-CCM-B remains fail-closed for every 1D/2D SCF backend.
The historical diamond and LiH direct-reference RI-MP2/KMP2 residuals are now explicitly classified as provenance-incomplete diagnostics, not reportable benchmarks or Γ-CCM/χ-CCM comparisons, pending a fully fingerprinted rerun. Neutral-versus-direct correlation ordering is recorded only as a complete-route regression because those routes change overlap, one-electron, and nuclear terms in addition to the exchange seam.
The public PeriodicJKMethod χ-B descriptions also enumerate the already
wired RHF/RKS/UHF/UKS surface instead of the stale closed-shell subset.
Fixed: CCM construction and control routes dispatch by shell (2026-07-15)¶
run_ccm_scf now dispatches open-shell clusters to the unrestricted drivers
on every construction/control route, completing what the real-gamma entry
below started: run_ccm_uhf / run_ccm_uks on "four-center" (including
the Γ-CCM "gamma" aliases), the new run_ccm_uhf_gdf /
run_ccm_uks_gdf on the "neutral-bloch" / "gdf" control
(run_ccm_uhf_gdf is the thin multi-k KUHF wrapper mirroring
run_ccm_uks_gdf, with the same documented per-unit-cell spin-bookkeeping
caveat for nrep ≠ (1,1,1)), and
run_ccm_uhf_direct / run_ccm_uks_direct on "real-gamma" — all keyed
identically on multiplicity AND electron parity. Previously an even-electron
open-shell cluster (e.g. a triplet) ran the closed-shell loops on
"four-center" and the neutral-Bloch control SILENTLY
(n_occ = n_elec // 2 ignores
multiplicity) and returned a wrong closed-shell number; an odd-electron
cluster raised.
Also threads conv_tol_grad through run_ccm_uhf (the four-center
open-shell driver, now a route target) — the explicit DIIS-residual exit
bound the direct drivers gained in the entry below (default 1e-6 == the
historical hardcoded gate, bit-for-bit). The shared closed-shell loops
(_rhf_loop/_rks_loop, ri.py) keep the hardcoded gate — not on any route
path; flagged in HANDOVER_AICCM_DIRECT_TORUS.md.
Pins: tests/test_ccm_route.py
(test_route_four_center_dispatches_by_shell,
test_route_neutral_bloch_dispatches_by_shell — doublet, even-electron
triplet, and closed-shell-unchanged gates per route), tests/test_ccm_uhf.py
(supercell
multiplicity propagation at (1,1,1) / (2,1,1), parity fallback,
construction-time validation, triplet-H₂ UHF (n_alpha, n_beta) = (2, 0),
conv_tol_grad exit-gate pin).
Added: open-shell KS on the real-Gamma neutral control (run_ccm_uks_direct) + shell-aware dispatch (2026-07-16)¶
run_ccm_uks_direct — spin-polarized KS-DFT on the k-free real-Γ-supercell
control, completing its RHF / UHF / RKS / UKS matrix for one already specified
neutral-torus Hamiltonian. It is not the union-and-weight Γ-CCM construction:
F_σ = h + J(D_a+D_b) + V_xc^σ − a_x [ K(D_σ) + ξ_N S D_σ S ]
run_ccm_uhf_direct’s per-spin exchange-q=0 seam composed with
run_ccm_rks_direct’s a_x scaling, and the spin-polarized periodic XC
kernel (build_xc_periodic_uks, per-spin densities on the same fold
construction + periodic-Becke grid as the closed-shell route — the
b3f74aa9-validated pairing). Measured: closed-shell collapse to
run_ccm_rks_direct at the 1e-15 class per rung (SVWN/PBE/r2SCAN/PBE0 —
LDA/GGA/mGGA/hybrid) — so the open-shell route inherits the closed-shell
GDF / external-PySCF-KRKS parities transitively; open-shell hybrid seam
identity exxdiv=None − ewald == a_x·ξ_N·N_e/2 to 6e-16 with the same
per-spin densities (at tightened conv_tol_grad; see below); open-shell
pure parity vs the production multi-k KUKS at 3.6e-13 (both sides ride
identical build_xc_periodic_uks + fit machinery); vacuum limit vs
molecular run_uks per rung (SVWN/PBE/r2SCAN). RSH/double hybrids fail
closed; dim<3 fails closed.
The open-shell direct drivers (run_ccm_uhf_direct / run_ccm_uks_direct)
also gain an explicit conv_tol_grad (DIIS-commutator residual bound,
default 1e-6 = the historical hardcoded gate, bit-for-bit; molecular
UHFOptions.conv_tol_grad convention). The adversarial review of this
change caught that the energy is stationary at convergence, so |dE| is
quadratic in the density error — an energy-only gate reports
converged=True with a ~1e-5-loose minority-spin density,
nondeterministically (threaded-BLAS trajectory). Tighten conv_tol_grad
when converged densities matter.
Also run_ccm_uks_gdf (the missing thin multi-k KUKS comparator, with a
documented per-unit-cell spin-bookkeeping caveat for nrep ≠ (1,1,1)), and
CCMKSResult gains an optional exchange_q0 field.
Behaviour fixes — explicit high spin reaches the CCM, and real-gamma
dispatches by shell. Two stacked silent traps closed:
CCMSystem._build_supercelldiscarded an explicit high-spin multiplicity (>= 3) in favour of its parity default — a triplet unit cell silently became a singlet supercell and ran closed-shell (therun_ccm_uhfdocstring’s “pass a periodic unit cell with an explicit multiplicity for higher spin” was broken). Explicitn_unpaired >= 2cells now replicate ferromagnetically (n_unpaired × N_c, the multi-k per-unit-cell spin convention; exact at(1,1,1));n_unpaired <= 1keeps the parity behaviour bit-for-bit (all existing pins unchanged). The unit cell’s own spin state is validated at construction — an impossible request (e.g. 3 electrons, multiplicity 3) previously raised at oddN_cbut silently morphed into a parity-consistent different state at evenN_c(a quintet at(2,1,1)); it now fails loudly regardless of cluster size.run_ccm_scf(route="real-gamma")now dispatches by shell (multiplicity AND electron parity) to the UHF/UKS direct drivers; even-electron open-shell supercells previously fell through to the closed-shell loop silently (n_occ = n_elec // 2), odd-electron ones raised. The same dispatch trap on thefour-centerandgammaroutes is closed by the follow-up entry above (shell-aware dispatch on all three routes +run_ccm_uhf_gdf).
Pins: tests/test_ccm_uks_direct.py (collapse per rung, a_x seam identity,
KUKS-GDF parity, vacuum limit vs molecular run_uks, fail-loudly surface,
dispatch-by-shell incl. the triplet trap).
Changed: semiempirical cyclic-cluster routes use the SECCM name (2026-07-15)¶
The semiempirical cyclic-cluster boundary is now named SECCM to distinguish it
from ab initio CCM and DFTCCM. Public semiempirical dispatch accepts seccm,
se-ccm, and MSINDO-qualified spellings while retaining ccm as a compatibility
alias for existing inputs and manifests. The route planner represents the
implemented path as the MSINDO INDO Hamiltonian on the
seccm_direct_torus boundary, and fails closed for DFTB, GFN2-xTB, PM6, and
OMx SECCM requests until their method-specific theory and adapters are
implemented. No periodic kernel or production-status claim changes in this
slice.
Changed: semiempirical memory estimates consume route plans (2026-07-15)¶
estimate_semiempirical_memory(...) now builds or accepts the same canonical
SemiempiricalRoutePlan used by molecular dispatch. Boundary, method family,
variant, SCC policy, and spin fields replace estimator-only labels for
periodic Gamma, SECCM, GFN2, COSMO, NDDO, and unrestricted scratch accounting.
Unsupported spin/method/boundary combinations fail during route validation,
before atom inventory or matrix-size accounting. Existing conservative memory
formulas and public output remain unchanged; no numerical kernel or maturity
claim changes in this slice.
Changed: periodic Gamma semiempirical setup uses route plans (2026-07-15)¶
run_periodic_job(...), periodic semiempirical optimizer energy closures,
preoptimization, PM6/OMx cell optimization, and the direct periodic PM6/OMx
models now validate through one system-aware SemiempiricalRoutePlan before
parameter or backend loading. Canonical aliases, charge, multiplicity, odd
electron fallback, and Gamma versus full-k boundaries therefore share one
representation. Full-k requests remain fail-closed. Open-shell DFTB0 and
SCC-DFTB optimizer closures now select the existing unrestricted Gamma
kernels; open-shell periodic GFN2, PM6, and OMx fail before allocation because
no validated unrestricted Gamma implementation exists for those routes.
Existing kernels, finite-difference formulas, output shapes, maturity labels,
and paper-facing claims are unchanged.
Added: unified native facade for molecular semiempirical energies (2026-07-15)¶
A common C++ route descriptor and result surface can now dispatch molecular
DFTB0, SCC-DFTB, GFN2-xTB, PM6/UPM6, OM1/OM2/OM3, and MSINDO INDO/NDDO
energy calls to their existing native kernels. The Python adapter translates a
validated SemiempiricalRoutePlan into that descriptor, while the native
boundary rejects parameter-family, spin, and control mismatches before kernel
dispatch. Facade energies and explicit energy components are pinned exactly to
the method-specific bindings.
The result reports retained matrix bytes separately from workspace bytes. The
workspace value remains zero and memory_counters_complete remains false
until kernel workspaces expose complete allocation accounting. This first
slice does not reroute public model or gradient APIs, add periodic or SECCM
support, or change method maturity and paper-facing claims.
Changed: periodic PM6/OMx derivatives use native FD batches (2026-07-15)¶
Periodic Gamma PM6 and OM1/OM2/OM3 gradients and stress now perform their central-difference batches in C++ instead of constructing every displaced or strained system in Python. One mutable periodic-system workspace and immutable parameter view are reused for the batch. Coordinate displacements also share one prepared lattice-cell list; strained evaluations rebuild their lists for the changed lattice so cutoff behavior remains identical. Exact parity to the former Python finite differences is pinned for PM6 and OM2.
PeriodicPM6Model and PeriodicOMxModel use the native batch for gradient and
stress, and their cell optimizers can consume both derivatives from one batch.
Route plans label these periodic derivative requests native_batched_fd. The
batch result reports energy-evaluation, system-workspace, lattice-setup, and
workspace-byte counters. Counters remain explicitly incomplete for allocations
inside each existing SCF kernel. This is still finite-difference and does not
promote periodic NDDO to production or change paper-facing claims.
Fixed: native k-point DFTB keeps complex Bloch matrices (2026-07-15)¶
The direct native DFTB0 and SCC-DFTB k-point prototypes now solve the complex
Hermitian generalized eigenproblem instead of discarding the imaginary parts of
H(k) and S(k). SCC charges are accumulated from weighted D(k) S(k)
Mulliken populations, and use the same lattice-summed periodic gamma matrix as
the established Gamma driver. DFTB0 lattice cells, Hamiltonian blocks, and
overlap blocks are built once as one aligned object; SCC also reuses its
Hamiltonian-block storage across iterations.
Malformed meshes, weights, cutoffs, electron counts, and SCC controls now fail
before basis or lattice allocation. Primitive-cell/supercell equivalence,
Gamma SCC parity, weighted full-k SCC populations, and the early validation
surface are pinned by focused tests. The public semiempirical periodic_k route
remains fail-closed: metallic occupations, analytic derivatives, k-mesh
convergence fixtures, and out-of-process DFTB+ parity are not complete, so no
maturity or paper-facing claim changes here.
Added: native full-k DFTB finite-difference derivative batches (2026-07-15)¶
Direct native DFTB0 and SCC-DFTB k-point APIs can now evaluate gradient and stress central-difference batches in C++. Each batch reuses one periodic-system workspace and one k-mesh workspace. Strained cells transform Cartesian k-vectors contravariantly so the sampled fractional k-points stay fixed, and a focused regression distinguishes that result from the incorrect fixed- Cartesian-k stress. Invalid FD controls fail before basis/lattice allocation, and displaced SCC nonconvergence raises instead of entering a derivative.
The result exposes energy-evaluation, workspace-copy, lattice-setup, and
workspace-byte counters, with nested kernel allocations still explicitly
incomplete. This is a direct native finite-difference prototype, not an analytic
derivative or public-route promotion. Metallic-safe occupations, k-mesh
convergence fixtures, runner/output/band integration, and external DFTB+ parity
remain open; periodic_k stays fail-closed with no maturity or paper-facing
claim change.
Added: native Fermi occupations for full-k DFTB (2026-07-15)¶
Direct native DFTB0 and SCC-DFTB k-point prototypes now accept a finite Fermi-Dirac smearing temperature. One weighted chemical potential conserves the closed-shell electron count across the complete k-mesh. Results expose per-k occupations, chemical potential, electronic entropy, internal energy, and Mermin free energy. At zero temperature the prior per-k hard Aufbau density and energy path remains unchanged.
SCC Mulliken populations and band energies use the fractional density, while geometry-static overlap matrices are assembled once per k-point outside the SCC loop. Native derivative batches propagate the same occupation controls through each displacement and differentiate free energy when smearing is active. Nonfinite or negative temperatures fail before k-mesh, basis, or lattice work. Native occupations, chemical potential, entropy, electron conservation, SCC charge conservation, and DFTB0/SCC free-energy finite differences are pinned against the shared Python Fermi-Dirac contract.
The kernels return structured data and emit no user-facing output. Public
rendering remains owned by Python through vibeqc.output; the output module is
unchanged. K-mesh convergence fixtures, runner/output/band integration,
analytic derivatives, and external DFTB+ parity remain open. The public
periodic_k route stays fail-closed with no maturity or paper-facing claim
change.
Added: full-k DFTB k-mesh convergence fixtures (2026-07-15)¶
The direct native DFTB0 and SCC-DFTB k-point prototypes now have fixed
Monkhorst-Pack convergence regressions for an insulating one-dimensional H/Li
chain. Successive 4-, 8-, and 12-point even meshes converge toward an
independently measured 24-point dense limit for zero-temperature internal
energy and finite-temperature Mermin free energy. All four sequences reduce
the 8-point error below one percent of the 4-point error, reduce the 12-point
error below two percent of the 8-point error, and reach the dense result within
2e-9 Ha.
This adds validation only. The public periodic_k route remains fail-closed
pending runner/output/band integration, analytic derivatives, and
out-of-process DFTB+ parity. No output surface, maturity label, citation route,
or paper-facing claim changes.
[v0.15.40] - 2026-07-15 - Neese’s Cheetah¶
Changed: semiempirical runner gradients are lazy (2026-07-15)¶
run_semiempirical(...) now caches gradients on first access instead of
building them during every energy call. DFTB0/SCC-DFTB, GFN2-xTB, PM6/UPM6,
and OMx keep their existing gradient surfaces, while closed-shell MSINDO INDO
results now expose the native analytic-gradient route through the same
result.gradient() adapter. SemiempiricalProvider now delegates through this
unified runner instead of carrying its own method switch or alias table, while
preserving the documented MSINDO finite-difference force fallback for MSINDO
routes whose unified result is energy-only. MSINDO NDDO and open-shell MSINDO
remain gradientless at this public runner layer until those surfaces are
promoted. The shared normalizer now also accepts the gfn2 shorthand, and the
semiempirical route-status API resolves DFTB/GFN2/OMx method aliases through
the same runner-owned table instead of a duplicate status-side copy.
Semiempirical dry-run memory estimates use that same normalizer, so aliases
such as gfn2, gfn2 xtb, and sccdftb receive the same SCC/AES scratch
accounting as their canonical method names. ASE-backed
run_job(optimize=True, method=...) semiempirical optimizations now use
result.gradient() when the unified runner exposes a gradient, falling back to
central finite differences only for energy-only routes. The semiempirical
preoptimization and periodic cell-optimization ASE calculators now also record
the active Atoms state through the ASE base calculator, so repeated
energy/force/stress queries at an unchanged geometry can reuse cached results.
Fixed: periodic RIJCOSX one-center correction follows the post-SCF contract (2026-07-15)¶
Both dedicated Gamma RIJCOSX JKBuilders applied the analytic COSX one-center
correction inside build_g_rhf() on every SCF iteration, but inherited the
no-op finalization hook. This reversed the molecular COSX lifecycle: the
quadrature residual perturbed the iterative surface while the returned final
Fock and orbitals omitted the correction. The periodic builders now iterate on
the smooth seminumerical J - 0.5 K surface and apply the analytic replacement
only through apply_one_center_correction() after convergence. Total energies
remain on the iterated seminumerical surface, matching the molecular RIJCOSX
contract. A focused regression covers both periodic builders and verifies that
the returned Fock carries a finite post-SCF correction.
Added: exact single-state IC-CASPT2 analytic gradients (2026-07-15)¶
CASPT2Options(compute_corr_grad=True) now evaluates the coupled orbital/CI
Lagrangian for unshifted, state-specific closed-shell IC-CASPT2 on the explicit
small-space engine. The stationary Hylleraas effective density is contracted
with analytic AO derivative integrals; finite differences are confined to the
internal response parameters, not nuclear energies. H2/6-31G CAS(2,2) and
core-containing LiH/STO-3G CAS(2,2) match fully relaxed total-energy finite
differences within 1e-7 Ha/bohr. Shifted, case-engine, direct-large,
selected-reference, and open-shell single-state variants remain fail-closed
for direct gradient requests and stay on full-energy finite differences in
geometry optimizers.
Changed: molecular NDDO finite-difference gradients have explicit route status (2026-07-15)¶
semiempirical_route_status(...) now distinguishes molecular PM6/UPM6 and
OMx finite-difference gradient routes as native-fd. These routes are
C++-backed displacement loops over native NDDO energy calls, but they remain
finite-difference stopgaps until analytic molecular NDDO gradients land.
Fixed: GDF Ewald nuclear-attraction gradient is fully analytic (2026-07-15)¶
The Gamma-point RHF compcell GDF gradient now differentiates every term in
the FT-based Ewald nuclear-attraction matrix: the erfc-screened real-space
integrals, nuclear structure factor, AO-pair Fourier-transform centres, and
finite G = 0 overlap correction. This replaces the former 6N displaced
whole-matrix builds with a native density- and kernel-weighted contraction
that never materializes the per-cell AO-pair derivative tensors. Reciprocal
vectors are processed in bounded chunks, and the native general-angular-
momentum path shares its reciprocal power tables across worker threads.
Fixed-density H2 and LiH/STO-3G regressions enforce central-difference
agreement below 1e-5 Ha/bohr, including translated cells, multiple
reciprocal chunks, and p shells.
The public compute_gradient=True route now forwards the SCF lattice
cutoffs, follows the same VIBEQC_VNE_EWALD3D_KE cutoff contract, and fails
before SCF for RSGDF/MDF, AFT-on compcell, the diagnostic grid V_ne
backend, or KS requests instead of silently returning a mislabeled compcell
gradient or swallowing an exception. The exported direct wrapper also reuses
and validates the converged auxiliary basis, compensation exponent, AFT state,
V_ne backend, reciprocal cutoff, exchange-divergence gauge, lattice cutoffs,
rcut strategy, and fit threshold. The gradient consumes the exact private
compcell fit used by SCF, including its separately resolved 2c/3c cutoffs and
the Fréchet derivative of any threshold-truncated auxiliary eigenspace.
The module-level RHF component entry point enforces the same supported-model
boundary and binds an optional reusable cache to the exact cell, basis
contents, compensation exponent, and cutoff policy that produced it.
Fixed-density J/K regressions force a dropped fit mode and agree with central
differences below 1e-7 Ha/bohr; both exxdiv='ewald' and 'none' are pinned
on one bond-axis component of the H2/STO-3G 12-bohr-box end-to-end control at
5e-3 Ha/bohr. Its smaller pre-existing total-gradient residual is
investigated separately.
The supported boundary remains 3D Gamma RHF, compcell, with the AFT correction
disabled; lower-dimensional, RSGDF, RKS/open-shell, multi-k, and optimizer
wiring remain separate work.
Fixed: GAPW SCF energies include atomic XC augmentation (2026-07-15)¶
Gamma RKS, Gamma UKS, and closed-shell multi-k GAPW now include the atomic XC
augmentation in each SCF convergence energy and trace entry, matching the
augmented Fock and final energy breakdown. Previously the Fock used the
hard-minus-soft atomic XC correction while the energy used only the smooth
grid term; He/STO-3G LDA therefore returned -2.7737713066 Ha but recorded
-2.4735585858 Ha on its last SCF step, a 300.2 mHa mismatch. The corrected
paths reuse the libxc energy density already evaluated for the atomic Fock
correction, avoiding another atomic-grid pass. Existing Gamma RKS, Gamma UKS,
and multi-k LDA/GGA/meta-GGA parity regressions now pin final trace and
breakdown consistency.
Added: Gamma-point GPW periodic ROHF route (2026-07-15)¶
run_periodic_rohf_gpw and GpwRohfScfResult add a standalone,
three-dimensional Gamma-point GPW route for spin-pure periodic ROHF. The
driver reuses the established GPW Hartree J, Ewald one-electron and nuclear
terms, per-spin exact exchange, and the shared Roothaan effective-Fock SCF
loop. It returns one spatial-orbital set with integer 2/1/0 occupations,
per-spin densities and Focks, an auditable GPW energy breakdown, exact
S(S+1), and per-iteration progress through the existing ProgressLogger
hook. Open-shell GPW occupation inference now uses
PeriodicSystem.n_electrons(), so charged cells include the declared unit-cell
charge instead of silently filling the neutral electron count.
Regression coverage pins the closed-shell ROHF-to-RHF reduction, the
one-electron ROHF-to-UHF reduction, and a genuine Li open shell with one
closed and one singly occupied orbital. The scope is deliberately narrow:
HF, Gamma-only, integer occupations, and the standalone API. Electronic
smearing, periodic ROKS, multi-k exact exchange, GDF/BIPOLE/GAPW routes, and
run_periodic_job(method="ROHF") remain follow-up work; smearing_alpha on
this API is nuclear-charge smoothing, not Fermi smearing.
Added: dense ground-state CC3 benchmark route (2026-07-15)¶
run_job(method="cc3") and run_job(method="ci", citype="cc3") now solve
the ground-state CC3 equations for closed-shell RHF references. Singles and
doubles are iterated to convergence, connected triples are regenerated from
the T1-transformed fluctuation-potential commutator on every iteration, and
the CC3 triples feedback is included in both projected residuals. The route
uses exact four-index MO integrals, freezes chemical cores by default, accepts
explicit active spaces, and exposes CC3Options, CC3Result, and the
low-level cc3 solver.
The normal .out reports energies, residuals, and amplitude norms through
OutputDocument and Quantity; the iteration table is VERBOSE. Memory
preflight conservatively models the dense determinant maps, nonconvergence is
fatal on the public path, and .system, citations, and QVF SCF history are
preserved. Regression coverage pins the exact two-electron limit, frozen-core
reduction, open-shell rejection, and nonzero triples. Out-of-process ORCA
6.1.1 UHF AUTOCI-CC3 anchors agree across the exact H2 two-electron limit,
H4/STO-3G, H4/3-21G, and the rank-five H6/STO-3G path. The focused public-route
matrix at
<vibeqc-article-release>/calculations/cc3/20260715-0cb29239/ validates every
QVF and .system manifest; its worst correlation-energy delta is
6.112121e-10 Ha and its worst total-energy delta is 1.475981e-09 Ha.
Fixed: multi-k UKS slab QVF density and DOS artifacts (2026-07-15)¶
Genuine dim=2 UKS jobs on an in-plane k-mesh converged correctly, but their
optional density-grid and DOS preparation could fail when the SCF density used
a shorter real-space lattice cutoff than the fixed output template. The strict
copy then requested an outer cell absent from the converged density and dropped
volume.density, dos.total, and dos.projected from the QVF with two warnings.
Periodic output density preparation now re-templates lattice density blocks by
cell key and fills only newly requested outer cells with exact zeros. It refuses
to truncate source-only cells and verifies the keyed alpha/beta alignment before
summing spin densities. The converged SCF density and output formats are
unchanged. A public-runner (2,2,1) UKS slab regression validates the QVF and
pins all three sections without optional-artifact warnings.
Fixed: BIPOLE M5 padded-domain route consistency (2026-07-15)¶
The restricted legacy Ewald-J route now applies the resolved padded
short-range image domain to Hartree J, matching the unrestricted builder and
the M5 contract that every active erfc SR build honors sr_image_precision.
Closed-shell multi-k RHF/UHF J, Fock, and total-energy parity is restored.
Explicit lower-dimensional Ewald-J requests again fail with the documented
dim=3 ValueError before SR-domain resolution, and two duplicate H2
regression snapshots plus the legacy RKS H2 guard now reflect the
already-sanctioned M5 padded values.
Direct-only, corrected-exchange, exact-FT pure-RKS, LR-J, and full-Coulomb
legacy-K paths are unchanged.
Changed: BIPOLE production diagnostics use semantic output (2026-07-15)¶
The RHF/RKS/UHF/UKS lattice-fold diagnostics and the shared exact-FT J
core-tail, legacy multi-k gauge, and charged-cell guards now route their
human-readable messages through vibeqc.output.warn() / note(). Warnings
therefore survive quiet output, and both warning and note paths emit
structured records with their method, cutoff, drift, k-point, or charge
metadata as applicable. Direct API warnings.warn(...) calls and the
historical ProgressLogger live-stream text remain unchanged. This is output
hardening only: thresholds, numerical paths, and BIPOLE defaults are
unchanged. Repeated internal SCFs, such as finite-difference geometry
evaluations, follow the shared logger’s emission policy; BIPOLE does not add a
parallel formatting or suppression layer.
Changed: vibe-view’s debug log is usable + MO renders are traced (2026-07-15)¶
The server log at $XDG_CACHE_HOME/vibe-view/vibe-view.log was flooded with
trame/aiohttp INFO-level internals (a js_key + namespace-token line per widget
on every template build, an access line per request) — tens of thousands of
lines that buried vibe-view’s own messages and made the log useless for
diagnosing a hang. Those loggers are now quieted to WARNING (wslink CRITICALs
and aiohttp request tracebacks still land); vibe-view stays at INFO. In one
measured startup this cut the log from ~20,000 lines to a handful.
Molecular-orbital rendering (_render_mo_volume) now logs a timed breadcrumb
at each stage — grid evaluation, contouring (with point counts), and the
render+client-push — so an MO render that appears to freeze the viewer leaves a
diagnosable last completed step in the log instead of nothing. (For reference,
a def2-TZVP methane-dimer HOMO on a 60³ grid evaluates + contours in ~0.35 s
server-side; a freeze there is not the server compute.)
Changed: Γ-CCM and χ-CCM approach identities are explicit (2026-07-16)¶
D89 corrects the earlier premise that Γ-CCM and χ-CCM were merely representation labels or globally unitarily equivalent by name. They are distinct CCM approaches: Γ-CCM uses the union-and-weight/Wigner–Seitz integral-weighting construction, while χ-CCM uses the finite-translation-group character construction. Comparisons hold a declared common exchange-q=0 convention fixed. The approach names do not select different Coulomb kernels, and equality for a specified operator and route is evidence to establish.
χ-CCM-B convention records now carry immutable
ccm_approach="chi-ccm",
ccm_construction="finite-translation-group-character", and
evaluation_representation="gamma-centred-character-mesh" identities through
diagnostics/results, fleet JSON, QVF vendor metadata, and .system metadata.
The existing QVF version-1 representation string remains for compatibility;
the new evaluation field is normative. The current aiccm-hf-direct versus B
rhf-ri pairing is a neutral-torus real-Gamma/character representation
control, not evidence for the union-and-weight Γ-CCM approach, so the reportable
Γ/χ approach map is empty. Exact character/real-Gamma Fourier equivalence of
the same χ-defined Hamiltonian remains valid. Pre-D89 records are not
numerically retracted solely because the new identity fields are absent, but
cannot support construction-comparison claims.
The public Γ-CCM selectors now enforce that boundary:
run_ccm_scf() defaults to four-center, and aiccm2026dev-a / gamma /
gamma-ccm resolve to the union-and-weight route. neutral-bloch, gdf, and
aiccm-ri select the neutral fitted-torus Bloch control; real-gamma is its
same-H real-supercell control. The historical
compare.py --vs path now fails closed instead of emitting route-name-based
Γ/χ deltas. compare_b.py always reports the approach comparison as
not-defined and exposes any eligible neutral control only through explicitly
named real_gamma_control_* fields. Post-HF B results delegate the same exact
construction identity as SCF results.
The high-level B runner now also rejects every requested
exchange_exxdiv value other than its fixed bvk-ewald convention before
SCF, and derives the persisted exchange-q=0 label from the returned B
convention. This closes a path where a non-BvK request could execute B while
the .system manifest incorrectly recorded strict. Fleet auditing rejects
malformed records and now requires exact method, backend, route, construction,
and control-role identities before admitting either a B record or the
real-Gamma control.
Fixed: toroidal local-correlation geometry on skew lattices (2026-07-15)¶
The periodic toroidal supercell and Wannier-cell helpers now follow the core
column-vector lattice contract for general cells: supercell vectors are
A @ diag(nrep), replicated atoms are translated by A @ n, and Cartesian
centroids are mapped with A f = r. The pair-family classifier now finds the
exact closest image with LLL basis reduction plus a bounded QR sphere search
instead of wrapping Cartesian components, so inverse families L and -L
have the same distance on unequal meshes without an acute-cell Cartesian-box
blow-up. Numerically collapsed lattices fail before image enumeration. Distance
thresholds are validated as 0 <= r_close < r_cutoff. The explicit
total_distant_cell_pairs property now names the translational placement count;
the legacy total_pairs_skipped accessor remains an alias and no longer implies
that occupied-orbital pairs or correlation work were actually skipped.
Skew-cell regressions distinguish the corrected operations from their row-vector transposes and compare classifier distances with brute-force and acute unreduced-cell image oracles. The accompanying status audit clarifies that Stage 5b currently partitions an all-pairs MP2 sum by translational family; representative-only pair evaluation and consumption of the classifier by the DLPNO wrappers remain follow-ups.
Fixed: a failing renderer no longer crashes/freezes vibe-view on section switch (2026-07-14)¶
activate_section (the sidebar section-switch handler) ran a large
synchronous per-kind render dispatch with no error handling: selected_section
was updated before rendering, so if any one renderer raised, the exception
escaped the trame controller with the app left half-switched — a frozen or
broken viewer. The dispatch is now wrapped in a guarded entry point: a renderer
that raises is caught and logged, a Could not open section … status is shown,
and the 3D viewport is restored to a usable state so the user can switch to
another section instead of a dead app. Regression test
tests/test_controller_state.py::test_activate_section_survives_a_renderer_exception
drives a switch into a renderer rigged to raise and asserts the app stays
usable (the next section still activates).
Added: MSINDO and MACE registry TOML exports (2026-07-14)¶
vibeqc.semiempirical.io.save_msindo_parameter_registry(...) now writes a
TOML export of the bundled MSINDO INDO H-Xe table plus the H/Li-F/Na-Cl NDDO
override table, including source-bundle hashes and support metadata.
vibeqc.mlip.save_mace_model_registry(...) exports the current v0.15 MACE
wrapper registry, explicitly limited to MACE-MP/MPA-0 and MACE-OFF23 families.
Fixed: periodic semiempirical method strings reach basis-free backends (2026-07-14)¶
run_periodic_job(..., method=...) now accepts dftb0, scc_dftb,
gfn2_xtb, pm6, om1, om2, and om3, dispatching to the existing
Gamma-point periodic semiempirical engines before the Gaussian-basis
RHF/RKS/UHF/UKS gate. The branch writes .out, .system, citations, and
requested structure files while explicitly skipping Gaussian Molden,
population, density, and QVF artefacts for basis-free routes.
Fixed: periodic CCM paths use the core column-vector lattice convention (2026-07-14)¶
χ-CCM-B torus construction, Wigner–Seitz representatives, localization,
symmetry, post-HF supercells, reciprocal-vector enumeration, Madelung helpers,
and shared fleet geometry builders now follow the authoritative
PeriodicSystem.lattice contract: lattice vectors are columns, Cartesian
translations are A @ n, and the BvK supercell is A @ diag(mesh).
Successful fleet payloads record primitive_lattice_bohr, and new convention
records serialize lattice_vector_convention="columns".
Decisions D17 and D81 had recorded the opposite row-vector premise; D88
explicitly supersedes those parts instead of treating the repair as a silent
convention change. The B fleet audit binds an exact A @ diag(mesh) record to
those fields; Γ/χ comparison is not defined when the binding is absent or
inconsistent.
The skew unequal-mesh audit uses
A=[[7,.4,.2],[.3,8,.5],[.1,.6,9]] bohr and (2,3,1). The corrected positive
code convention gives xi_M=0.138352993811598; the pre-D88 fitted helper gave
0.143621616230995, which would overbind a two-electron RHF seam by 5.268622
mHa/cell, and the pre-D88 four-center probe gave 0.138913224426180.
Regression coverage distinguishes A @ diag(mesh) from the transposed order
and checks the affected real/reciprocal transforms; it does not establish
general quantitative certification beyond those tests.
All affected pre-D88 periodic Γ-CCM and χ-CCM records must be rerun under the
fingerprint rule because they do not attest lattice semantics and the BvK
construction. Records whose lattice matrix is symmetric and commutes with
diag(mesh) are numerically outside this defect class, but their old payloads
are not retroactively upgraded. The shared fleet builders for graphene,
mgo-slab, ice-ih, co2-dryice, and sio2-quartz require fresh Γ and χ
fingerprints. Diagonal and symmetric cubic anchors with commuting meshes may
guide reruns, but retain their other numerical-support and provenance limits.
Ordinary pre-D88 periodic GDF exact-exchange and BIPOLE J/K records for
affected lattice/mesh combinations also require audit and rerun because they
traverse the repaired shared Madelung and reciprocal-vector helpers.
A rebuilt ordinary-GDF c-diamond/sto-3g (2,1,1) control moves from the
pre-D88 post-D78 pin -74.6405167828 Ha to -74.4119904741 Ha. Its
+0.228526309751 Ha shift is exactly predicted by six occupied closed-shell
bands times the old-minus-corrected BvK Madelung constant, so this is a
convention repair rather than SCF noise.
The article theory already uses the column convention correctly, so D88 is a
code/fingerprint repair rather than a Coulomb-kernel, signed-Madelung, or
theory change. The 1D/2D absolute-energy guard and analytic-total-gradient
guard remain fail closed. D89 supersedes the former representation-label and
approach-equivalence premise; D88’s numerical and fingerprint repair remains
unchanged.
Added: dense full iterative CCSDT benchmark route (2026-07-14)¶
run_job(method="ccsdt") and run_job(method="ci", citype="ccsdt") now
solve the full ground-state singles, doubles, and triples equations through
the exact determinant-space projection of the similarity-transformed
Hamiltonian. The route supports RHF and common-orbital ROHF references,
chemical-core freezing by default, explicit active spaces, DIIS convergence,
and exact four-index MO integrals. Its dense determinant representation is
intended for compact benchmark spaces rather than large molecules.
The normal .out reports the converged energy, residual, and amplitude norms
through OutputDocument and Quantity; the full iteration table is tagged
VERBOSE. Regression coverage pins exact two- and three-electron FCI limits,
a nonzero-triples H4/STO-3G ORCA 6.1.1 anchor, the open-shell public runner,
memory preflight, citations, .system, and validated QVF output.
The focused vq matrix under
<vibeqc-article-release>/calculations/ccsdt/20260714-4d313c25/ increases from
H2/STO-3G through nonzero-triples H4, a 3-21G basis expansion, and an H6 chain.
All four rows match out-of-process ORCA 6.1.1 AUTOCI-CCSDT; the worst
correlation-energy delta is 5.859412e-10 Ha and the worst total-energy delta
is 1.557606e-09 Ha. Every QVF validates with structure, citations, and SCF
history. The matrix tested pre-rebase SHA 4d313c25; the implementation and
QVF fix are its rebased equivalents in main.
Fixed: GDF convergence aids follow the selected driver (2026-07-14)¶
run_periodic_job now lowers resolved Saunders-Hillier level shifts onto GDF
SCF options before dispatch, so supported closed-shell Gamma-fallback and
non-Gamma GDF routes execute the requested shift. Drivers without that
operator no longer accept and silently drop it: unsupported AUTO choices are
reported as capability-filtered zeroes, while explicit nonzero requests fail
closed. Exact Gamma detection uses the k-point coordinates and weight, so a
shifted singleton still reaches the full GDF loop.
The same capability contract now covers Fock mixing that exact-Gamma RHF/GDF
with an explicit gdf_method, or an open-shell GDF k-mesh driver, cannot
execute. AUTO records a filtered zero and explicit nonzero requests fail
closed instead of advertising an aid the SCF loop ignores.
[v0.15.39] - 2026-07-14 - Neese’s Cheetah¶
Changed: MSINDO COSMO hot-path benchmark records cavity metrics (2026-07-14)¶
scripts/bench_semiempirical_hotpaths.py --only msindo_cosmo_h2o --json now
records gas-phase, solvation, and polarization energies, COSMO iteration count,
and cavity point metadata instead of only the in-solvent total energy.
Fixed: basis-free Hessian requests skip Gaussian-basis setup (2026-07-14)¶
run_job(hessian=True, method=...) now skips vibrational analysis for
basis-free semiempirical and MLIP methods with a clear .out note instead of
entering the Gaussian-basis finite-difference Hessian driver with basis="".
The citation assembler also no longer records Hessian/gradient-driver
references for skipped basis-free Hessian requests.
Added: open-shell direct-torus reference (run_ccm_uhf_direct) + symmetry on one-call correlation (2026-07-14)¶
run_ccm_uhf_direct – the open-shell (UHF) sibling of run_ccm_rhf_direct:
one real Γ-supercell UHF-SCF with the BvK-ewald exchange-q=0 seam applied per
spin (F_σ = h + J(D_a+D_b) - [K(D_σ) + ξ_N S D_σ S]). The per-spin seam
mirrors K(D_σ), so it is linear+symmetric in D_σ (F_σ = ∂E/∂D_σ holds) and
collapses to the closed-shell ½(K + ξ_N S D S) for D_a = D_b = D/2 – verified
run_ccm_uhf_direct == run_ccm_rhf_direct to 9e-16 on a closed-shell cell, so the
open-shell route inherits the RHF route’s GDF/KRHF parity transitively. The
exxdiv=None control sits above "ewald" by exactly ξ_N·N_e/2 per supercell
with the same per-spin densities (measured 1e-15; the seam is occ/virt
block-diagonal in each spin). dim<3 fails closed like the closed-shell route.
This closes the gap the 2026-07-12 reference="direct" opt-in shipped with:
run_ccm_ri_mp2(..., reference="direct") now composes run_ccm_uhf_direct +
run_ccm_ump2 for open shells (previously NotImplementedError) – so the
BvK-ewald reference the D2 KMP2 anchor validated (HANDOVER_D2_EXXDIV.md) reaches
open-shell ionics (NiO-AFM class) too, not just closed shells.
Also: run_ccm_ri_mp2 / run_ccm_ri_ccsd gain symmetry= (forwarded to the
ccm_neutral_cderi build), matching the space-group pair reduction the SCF
drivers already expose (cc0a3092) – so a symmetric-crystal caller entering
through the one-call correlation API gets the star reduction on the fit, not only
on the SCF.
Pins: tests/test_ccm_direct.py (closed-shell collapse, open-shell seam
identity, dim<3 fail-closed) and tests/test_ccm_mp2.py
(test_ri_mp2_reference_direct_open_shell one-call == manual on one shared L;
symmetry= passthrough). Defaults unchanged (reference="neutral",
symmetry=None); open-shell reference="direct" records BvK-ewald.
Fixed: χ-CCM-B Fock-mixing overrides follow backend support (2026-07-14)¶
The direct run_aiccm2026dev_b_{rhf,rks,uhf,uks} APIs now resolve one
validated Fock-mixing request: a non-None keyword overrides the options
field without rewriting it. The override reaches every four-center BIPOLE
driver, including both unrestricted spin-schedule phases, while the existing
multi-cell fitted RHF/RKS paths retain the same precedence. Fitted RI/RIJCOSX
UHF/UKS now reject a nonzero resolved request instead of silently ignoring an
options value. Three-dimensional Gamma-only RI RHF also rejects a nonzero
request because the shared dispatcher could execute it only by substituting
the legacy molecular-limit GDF operator for the declared pair-resolved
exxdiv="ewald" operator; resolved zero retains the declared route. On
supported execution routes, mixing changes convergence control, not the finite
Hamiltonian or backend mixing formula. The Gamma explicit-zero correction
deliberately restores the declared operator instead of preserving the old
route-selection bug. Successful pre-D87 direct calls with differing
keyword/options values, plus fitted RI/RIJCOSX UHF/UKS calls with a nonzero
options-only request, have incomplete request fingerprints. Pre-D87 Gamma-only
RI RHF calls where either input source was nonzero, including an explicit-zero
keyword over nonzero options, followed the legacy operator and are not χ-CCM-B
results. The closed-shell fleet inputs are unchanged.
The common D72 dimension guard now also runs before every backend-specific
Gamma or mixing guard, closing a 1D one-cell RI-RHF escape that could return an
absolute energy. Any pre-D87 value from that route is D72-invalid and must not
be reported; no 1D/2D χ-CCM-B absolute energy is defined.
Fixed: χ-CCM-B diagnostics report executed Fock mixing (2026-07-14)¶
AICCM2026DevBDiagnostics.fock_mixing and the fleet
convergence_diagnostics.fock_mixing field now record the executed effective
previous-Fock weight after backend defaults are resolved, while
scf_options.fock_mixing remains the requested input. This distinction matters
for DIIS-off four-center KS runs, where requested 0.0 can execute as 0.30.
The SCF loop already used the resolved value, so this is a provenance-only
correction with no energy, SCF-algorithm, or finite-Hamiltonian change. Pre-D86
records in that case remain useful but are not convergence-fingerprint-complete;
comparator v2 is unchanged and still does not bind Fock mixing.
Fixed: semiempirical optimizations cannot enter basis optimizers (2026-07-14)¶
run_job(optimize=True, method=...) now rejects optimizer_backend="native" and
"brent" for basis-free semiempirical and MLIP methods before any molecular
optimizer setup, and optimizer_backend="auto" now fails with a targeted ASE
requirement if ASE is unavailable. This keeps article-style PM6/OMx/GFN2/DFTB
optimization jobs from reaching the Gaussian-basis native optimizer with an
empty basis name.
Fixed: periodic OMx rejects unknown variants cleanly (2026-07-13)¶
load_omx_params(...) and the periodic OMx Gamma wrappers now reject method
variants outside om1, om2, and om3 with a clear ValueError instead of
leaking a raw KeyError or silently accepting an invalid variant when a
caller supplied a compatible parameter object.
Fixed: semiempirical hot-path benchmark fails failed rows (2026-07-13)¶
scripts/bench_semiempirical_hotpaths.py now exits nonzero when a child case
records status="error" or an otherwise successful result reports
converged=false, so inventory runs can no longer look green while serializing
a failed semiempirical calculation.
Fixed: PM6/OMx runner surfaces gradient failures (2026-07-13)¶
The unified molecular semiempirical runner no longer swallows arbitrary PM6 or
OMx finite-difference gradient exceptions and returns gradient=None. Native
gradient failures now propagate from run_semiempirical(...), preserving the
original error at the public boundary.
Added: closed-shell A-CCSD(T) triples route (2026-07-13)¶
run_job(method="ccsd", triples="A-CCSD(T)") and
CCSDOptions(triples="a-ccsd(t)") now run the asymmetric/Lambda triples
correction on closed-shell CCSD amplitudes instead of raising the old roadmap
guard. The route uses the spatial CCSD Lambda solver and reports the effective
method/citation key as a-ccsd(t) with Kucharski-Bartlett 1998 and
Crawford-Stanton 1998 references.
The focused conventional/DF validation matrix under
<vibeqc-article-release>/calculations/a-ccsd-t/20260714-7d4c9102/ covers the
smallest nonzero former guarded call through a frozen-core cc-pVTZ row. All
four user-facing vq jobs and QVF archives pass; against out-of-process PySCF
CCSD Lambda references, the worst energy-component delta is
1.071498e-10 Ha and the worst Lambda-norm delta is 2.118492e-08.
Fixed: single-state IC-CASPT2 IPEA matches the canonical class contraction (2026-07-13)¶
Nonzero CASPT2Options(ipea=...) now builds the canonical eight internally
contracted double-excitation classes, retains diagonal active generators, and
applies the IPEA correction after class-local canonical Lowdin
orthogonalization. This removes the old representation-dependent 0.123 mHa
H2/6-31G mismatch and agrees with fresh out-of-process OpenMolcas references
within 0.02 µHa for H2/6-31G CAS(2,2) and 0.33 µHa for H2O/6-31G
CAS(4,4), which exercises all eight classes. The single-state variant="ic",
engine="auto" route is therefore un-gated. Selected-reference,
matrix-free engine="cases", and MS/XMS-CASPT2 IPEA combinations still fail
closed with explicit capability messages. The ipea=0 solver path is
unchanged.
Changed: BIPOLE uses the pair-resolved padded SR domain and symmetry reduction by default (M5) (2026-07-13)¶
The maintainer approved the paired v0.16 default change. All four direct
BIPOLE drivers and run_periodic_job now expose
sr_image_precision=1e-6: every active erfc short-range J/K build derives an
absolute internal ket-image radius as
cutoff_bohr + bipole_sr_image_extent(...) and automatically enables the
M4b separation-aware QQR screening bound. Pass sr_image_precision=None
only to reproduce the historical unpadded traversal; on an active erfc SR
route this also suppresses automatic SYM3b reduction unless it is explicitly
requested. An explicit
sr_image_extent_bohr remains the M4a absolute-radius oracle and takes
precedence without changing the caller’s screening choice. The unrestricted
spin-schedule recursion now forwards both controls.
On an attached-symmetry crystal using the corrected Ewald exchange split,
use_fock_symmetry_reduce=None now auto-enables the group-invariant SYM3b
representative build in RHF, RKS, UHF, and UKS. Explicit False disables the
automatic reduction, while enforcement-only diagnostics use
use_fock_symmetry=True, use_fock_symmetry_reduce=False. The full M1-M4b
chain makes reduce equal enforcement end to end; J-block orbit asymmetry is
6.3e-15 (MgO), 5.4e-15 (diamond), and 4.4e-15 (Si) on the maintained
group-covariant Gamma-fold probe.
This is a correctness-changing re-pin, not a byte-preserving refactor. On the
MgO split fixture the repaired split is 81.4173579654 Ha; the finite-KE
exact-FT reference remains 80.9288590106 Ha, leaving the independently
diagnosed +0.4884989548 Ha exact-FT reciprocal-tail gap and removing the old
-0.5153 Ha SR truncation loss. The screened padded build costs about
5-17 times one historical SR build on diffuse cells, but attached-symmetry
orbit compression reduces the measured cutoff-6 Gamma net to about 2.7 times.
The converged MgO/STO-3G c12 [2,2,2] RHF baseline re-pins to
-271.2147846320 Ha, within 3.359 mHa of a fresh local CRYSTAL23 SHRINK-8
run (-271.21814374982 Ha) and 1.429 mHa of a fresh isolated PySCF 2.13.1
KRHF/GDF run (-271.21335558268 Ha) at the matching Ewald exchange
convention.
The analytic BIPOLE gradient result contract now records the resolved SR radius and pair-domain provenance, and fails closed for either M5 domain because its derivative kernel still implements the historical radial and unpadded traversal. The exact finite-difference gradient remains the production path and differentiates the new energy correctly. Pure semilocal RKS keeps its exact-FT J default in this merge, per the maintainer’s ruling that its retirement be separate. The dedicated HSE screened-exchange traversal is also outside this change and remains owned by its parallel workstream.
Fixed: semiempirical geometry provider honors charge and spin overrides (2026-07-13)¶
SemiempiricalProvider(charge=..., multiplicity=...) now applies explicit
constructor overrides to the molecule evaluated by the model factory. Omitting
those knobs preserves the charge and multiplicity already stored on the
per-call Molecule, so charged/open-shell molecules no longer depend on an
ignored constructor field or a forced closed-shell default. The same provider
now strips method-name whitespace and accepts the established dftb,
dftb-0, gfn2_xtb, and gfn2xtb aliases, and it exposes SCC-DFTB through
scc-dftb, scc_dftb, and sccdftb by routing to the existing
SCCDFTBModel.
Fixed: vibe-view camera presets/bookmarks move the live client view (audit L1) (2026-07-12)¶
Camera presets and bookmark/slide/session restores set the server-side plotter
camera and pushed geometry (ctrl.view_update()), but VtkLocalView keeps its
own client camera, so the live view often didn’t move. The viewer now wires
VtkLocalView.push_camera (which sends the server’s exact camera, unlike
reset_camera’s fit-all that would lose a bookmark’s zoom): camera_preset
calls it after applying a preset, and _apply_camera pushes the applied camera
for every bookmark/slide/session/load path. Wiring covered by
tests/test_audit_2026_06_03.py::test_apply_camera_pushes_client_camera, and
live-verified in a browser (the client camera reorients when the view presets
are clicked).
Fixed: GFN2 rejects unknown SCC mixer names (2026-07-13)¶
GFN2Model(scc_mixer=...) now validates the mixer name at construction and
raises ValueError for unknown values instead of silently falling back to
simple mixing. The model docstring now names the actual default mixer
(simple), matching the long-range ionic SCC stabilization path. The same
boundary cleanup makes GFN2Model(..., params=None) call the published-parameter
loader instead of constructing an empty native GFN2ParameterSet.
Added: semiempirical route status accepts public method aliases (2026-07-13)¶
semiempirical_route_status(...) now resolves public method names and common
aliases such as dftb0, scc-dftb, gfn2xtb, om2, msindo, and ccm to
the canonical semiempirical route labels. This keeps the status API aligned
with the unified runner surface and makes article/status checks less dependent
on internal route names.
Fixed: the BIPOLE SR range-screening flag now reaches the 2-e build (2026-07-13)¶
run_periodic_job(sr_range_screening=True) sets
options.lattice_opts.sr_range_screening, but the field was absent from
prepare_bipole_lattice_options’ passthrough allowlist
(_LATTICE_PASSTHROUGH_FIELDS), so the derived lat_opts_2e reverted to
the default False and the direct erfc J/K build ran UNSCREENED while the
run still cited the QQR bound. Same class as the 2026-07-13 threshold
passthrough fix; the M4b flag is now in the allowlist. The
test_prepare_lattice_options_propagates_user_screening_thresholds
regression (loop over the allowlist) now exercises the flag
non-vacuously.
Fixed: vibe-view raytrace indicator + animation-reset flicker (audit L2, L3) (2026-07-12)¶
Two viewer correctness fixes from the 2026-06-03 audit:
L2 — a scene rebuild (
_rebuild_scene/_reload_active_file, whichplotter.clear()) dropped the OSPRay render pass whileraytrace_enabledstayed True, so the ray-trace indicator lied until toggled twice. The new_reapply_raytracere-attaches the pass after a rebuild (or clears the flag if it can’t), so the indicator always matches reality.L3 — the async trajectory/vibration play loops cancelled cooperatively with no generation token, so a stale iteration could
flush()one frame onto the scene after a section switch reset it (one-frame flicker). Both loops now bump an epoch on every play/stop and bail after theirawaitif superseded, before rendering.
L1 (camera preset/bookmark client resync) stays deferred: it needs a live
client and the deployed trame-vtk camera API to fix without regressing the
camera (see vibe-view/AUDIT_2026-06-03.md).
Fixed: CCM runner rejects malformed option objects cleanly (2026-07-13)¶
The unified semiempirical CCM dispatcher now validates duck-typed
ccm_options before entering the C++/Python CCM kernels: an object with
translations but no explicit madelung flag raises a clear ValueError
instead of leaking an implementation AttributeError, and malformed lattices
that are not 1, 2, or 3 numeric three-component translation vectors are rejected
at the same boundary. The direct CCM energy, finite-difference gradient, and
optimizer APIs now share the same lattice-shape validation, and the direct
finite-difference gradient reports unsupported elements through the same
MSINDO-scope NotImplementedError as direct energy. The adjacent runner
docstrings now match the fail-closed nddo=True behavior, and the CCM options
docstring reports the current H-Xe MSINDO element scope.
Changed: χ-CCM-B records operator applicability explicitly (2026-07-12)¶
χ-CCM-B convention records now distinguish the declared
exchange_q0="bvk-ewald" family convention from whether that full-range seam
is active in the actual operator. RHF/UHF and global hybrids record active;
pure functionals and HSE06 record inactive, with HSE retaining its finite
short-range screened exchange. The Γ/χ comparator also exposes a separate
defined/not-defined status and never renders an unavailable comparison as
a blank beside an ok χ record. D85 specifies the stationary
energy-weighted-density binding required before the fixed-W overlap
component can enter a total gradient; total χ-CCM-B gradients remain
fail-closed.
[v0.15.38] - 2026-07-13 - Neese’s Cheetah¶
Fixed: user-set BIPOLE lattice screening thresholds now reach the two-electron build (2026-07-13)¶
prepare_bipole_lattice_options built the derived one- and two-electron
LatticeSumOptions as fresh objects that copied only cutoff_bohr and
nuclear_cutoff_bohr from the user’s options.lattice_opts. A
schwarz_threshold, schwarz_threshold_forces, screening_overlap_threshold,
screening_exchange_threshold, or slab_ewald_alpha set by the user therefore
silently never reached the BIPOLE J/K build in any of the four drivers
(RHF/RKS/UHF/UKS) — the 2-e build always ran at the compiled defaults
(schwarz_threshold = 1e-12, …). The derived sets are now passthrough copies of
the user’s options (_LATTICE_PASSTHROUGH_FIELDS), overriding only
coulomb_method, which the CRYSTAL gauge fixes per channel (2e always
DIRECT_TRUNCATED; 1e EWALD_3D on 3-D cells). Every passthrough field defaults
to the value the fresh object previously carried, so a run that never set these
knobs is byte-identical; only a user who explicitly tuned a threshold sees a
change, and that change is the value taking effect. Found during the 2026-07-13
_combine_density_sets truncation audit. Regression:
tests/test_pbc_bipole_common.py::test_prepare_lattice_options_propagates_user_screening_thresholds.
Fixed: unrestricted BIPOLE total density no longer truncated to the radial template in the J-only build (2026-07-13)¶
build_bipole_unrestricted_fock formed density_total = D_α + D_β on
the 1×-cutoff radial overlap template (_combine_density_sets) while
the per-spin densities live on the 2×-cutoff list under the Ewald
exchange split, so the pure-functional (alpha_hf = 0) J-only build
contracted a total density whose P(h) blocks for |h| in
(cutoff, 2×cutoff] were silently zeroed (the C++ builder skips missing
P(h) lookups without complaint). The total is now combined on the spin
densities’ own template in every mode, generalising the pair-resolved
M3 branch; the templates coincide outside the split, so the legacy /
non-split paths are block-identical.
Measured blast radius (MgO/STO-3G Γ, 2-iter deterministic, DIIS off, no symmetry flags):
Production default: no numerical change. The cell-level Schwarz guard requires the ket displacement h to sit inside the internal (1×) cell list, so the J traversal never read the dropped P(h) in the first place. UHF ≡ RHF at cutoffs 6/8/10 to ≤ 2.5e-10 Ha before and after; UKS(SVWN) iteration-1 J components ≡ RHF to 3e-14. Hybrids/UHF were never affected (per-spin J on the wide densities); no re-pins needed.
The truncation was live exactly where the internal ball is wider than the radial list: the M4a
sr_image_extent_bohrpadded build (shipped in v0.15.x, and the escalated default-flip candidate) and Schwarz-off builds. Measured: He/STO-3G a=4 c=4 with an 8-bohr padded ball loses ΔJ_SR = −2.5e-3 Ha of the +5.1e-3 Ha the pad adds (UKS-pure vs RHF, iteration 1, identical density); C++-level max|ΔJ| = 3.0e-2 on an MgO/STO-3G c6 unit-scale density with screening off. Regression pin:tests/test_pbc_bipole_uks.py::test_uks_pure_functional_j_contracts_full_wide_density(UKS-pure padded J components ≡ RHF’s). Pair-resolved SYM3b had already fixed this for its own mode (M3); this closes the remaining modes before the padded-domain default flip would have turned the latent defect into a live UKS-only energy error.
Added: closed-shell ground-state CC2 (2026-07-12)¶
run_job(method="cc2") and run_job(method="ci", citype="cc2") now solve
the Christiansen-Koch-Jorgensen ground-state CC2 equations on an RHF
reference. The route uses the complete singles projection and the
first-order doubles equation from the T1-transformed Hamiltonian plus the
diagonal [F,T2] commutator; conventional and density-fitted integrals,
explicit frozen cores, citations, normal output sidecars, and QVF are wired
through the public runner. CC2 is energy-only, closed-shell, and intentionally
does not accept a perturbative-triples selector.
The dense small-molecule kernel is pinned against an independent spin-orbital
equation oracle. An increasing-size public-route matrix from H2/STO-3G through
frozen-core H2O/cc-pVTZ agrees with out-of-process ORCA 6.1 AUTOCI-CC2 to at
worst 5.20e-9 Ha in correlation energy and 1.21e-8 Ha in total energy;
all QVF archives validate. Artifacts:
<vibeqc-article-release>/calculations/cc2/20260712-da1292db/.
Fixed: unrestricted β orbitals show their own symmetry labels (vibe-view, audit L7) (2026-07-12)¶
For an unrestricted wavefunction.gto, the QVF reader collapsed the α and β
symmetry-label lists into one (alpha or beta) and the MO picker indexed that
single list for both spins, so β orbitals displayed α’s symmetry labels. The
reader now keeps the two lists separate (WavefunctionGTOData.symmetry_labels
for α / restricted, new symmetry_labels_beta for β) and the renderer labels
each spin block from its own list. Round-trip test:
tests/test_wavefunction_normalization.py::test_unrestricted_per_spin_symmetry_labels.
Added: separation-aware SR screening + the padded-ball compositions (pair-resolved M4b) (2026-07-13)¶
Three pieces of the M4 internal-domain fix:
QQR-style separation-aware screening for the direct erfc SR J/K build (
LatticeSumOptions.sr_range_screening, opt-in): the Schwarz product is additionally bounded byerfc(√m_ω·R)with1/m_ω = 1/γ_bra + 1/γ_ket + 1/ω²— the split-gap triage’s smeared kernel range, evaluated per shell quartet with conservative (most-diffuse-primitive) exponents and a lower-bounded pair separation, plus a cell-level early exit. Plain Schwarz is separation-blind, which is why the M4a radial pad cost 35×; measured on MgO/STO-3G c6 → 22-bohr ball: 45.7× → 17.4× (2.6× cheaper) at ≤ 1.2e-10 agreement; at the production ball it is already slightly faster at ~1e-12 agreement. Skips only — accuracy stays at theschwarz_thresholdscale; ω = 0 builds are untouched. Cited via the newbipole_sr_rangeroute (Maurer, Lambrecht, Flaig & Ochsenfeld, JCP 136, 144107 (2012)).sr_image_extent_bohrnow composes with the SYM3b flags (both mappings, enforce and reduce; previouslyNotImplementedError): the padded internal ball runs through the M1 full-domain binding, and the padded pair-resolved home block equals the M4a radial padded build bitwise (the oracle contract).Padded pure-functional J no longer computes a wasted K (
_sr_image_padded_jk(compute_exchange=False)through the M1 binding — M4a built and discarded a full padded K).
The remaining M4 decision — flipping the interaction-resolved padded
internal domain ON BY DEFAULT (the −515 mHa J_SR correctness fix for
every erfc build) — moves baseline energies and costs ~an order of
magnitude on diffuse-basis cells even with the screening; it is
escalated to the maintainer with the measured numbers
(handovers/HANDOVER_BIPOLE_PRODUCTION.md §0a).
Changed: SYM3b opt-in flags now run on pair-resolved truncation domains (pair-resolved M3) (2026-07-13)¶
use_fock_symmetry / use_fock_symmetry_reduce (still opt-in) now
build on the group-invariant pair-resolved domains in all four BIPOLE
drivers, under the production Ewald exchange split: the output triple
set and per-cell masks (M2), a pair-resolved density support
(criterion radius 2× cutoff, masked to qualifying pairs after every
density build — the radial 2×-cutoff list chops 3 of MgO’s 6
symmetry-equivalent nearest-neighbour Mg–O couplings), the extended
overlap background fold, and a dedicated J^LR cache on the
pair-resolved operator template. Orbit closure is exact
(require_closed=True) and the reduced build == enforcement to
1e-12 end-to-end (RHF ≡ reduce exactly; UHF matches RHF to 1e-10 on
closed-shell MgO). Measured block-level J orbit asymmetry on a
group-covariant Γ-fold density: 3.6e-2 (legacy radial domains) →
1.6e-3 (pair-resolved M3) → ~1e-6 with an M4-style padded internal
ball — the remaining floor is the internal summation domain, which
M4b makes interaction-resolved. Energies under the opt-in flags move
at truncation-error level because the truncation SET changes to the
pair-resolved one (MgO c6 2-iter RHF: −271.854 → −271.173; parity
numbers under these flags need re-derivation, tracked for M5).
Legacy non-split runs and all no-flag runs are unchanged.
Fail-closed compositions: sr_image_extent_bohr, the multipole
far-field, and screened-exchange-without-reduce raise
NotImplementedError under pair mode (M4b wires the first; HSE
composes via use_fock_symmetry_reduce=True).
Added: pair-resolved truncation domain + exactly-closed SYM3b orbit mapping (pair-resolved M2) (2026-07-12)¶
The group-invariant truncation domain CRYSTAL-style pair screening
implies: vibeqc.pair_resolved_truncation builds
{(a,b,h): |r_b + L·h − r_a| ≤ cutoff} (orbit-closed by union against
the symmorphic operator set, so boundary ties cannot break closure),
and identify_atom_pair_orbits accepts a triples= restriction of
its triple space. Because the pair criterion is preserved by the
triple action h → R·h + s_a − s_b, the orbit partition now runs
with require_closed=True — no silently skipped orbit partners. New
pair_resolved_fock_mapping + build_jk_pair_resolved +
pair-resolved-aware build_jk_reduced_symmetrized route the SYM3b
build through the M1 full-domain binding; the reduced masked build is
bit-identical (0.0) to the full build + enforcement on the same
domains, and non-qualifying sub-blocks are exact zeros. On MgO c6 the
domain reproduces the 2026-07-12 probe: 38 triples → 6 orbits (6.33×
compression, up from 4.3× radial) and keeps all 6 symmetry-equivalent
nearest-neighbour Mg–O couplings (a radial list chops 3). Not yet
wired into the SCF drivers (the driver flip + density support is the
next milestone); resolve_bipole_fock_symmetry grows a dormant
exchange_split_active switch for it.
Fixed in the same change: symmetrize_fock_blocks now associates
blocks with cells BY KEY (with fail-closed coverage checks) instead of
by position. The opt-in use_fock_symmetry + pure-functional exact-FT
J combination silently scrambled blocks before: the enforcement ran on
the density (2×-cutoff) template while the mapping lived on the cutoff
list, and radial lists at different radii are not positional prefixes
of each other. Enforcement also now leaves sub-blocks outside the
mapping’s triple space untouched instead of zeroing them (identical
behavior for the legacy full-product mapping; required for restricted
triple spaces and wide templates). Pinned in
tests/test_pair_resolved_truncation.py.
Added: full truncation-domain control binding for the direct J/K build (pair-resolved M1) (2026-07-12)¶
build_jk_2e_real_space_domains exposes the direct-ERI builder’s
long-standing internal capabilities through one binding: the caller
supplies the internal (c_g, c_λ, c_σ) summation cell list (the other
entry points derive it radially from cutoff_bohr), the output
bra-cell subset, optional per-output shell-pair masks, and
compute_exchange=False for J-only builds; the density support stays
data-level via the density’s own cell list. Purely additive — no
existing entry point changes; byte-identity against
build_jk_2e_real_space / _output_subset / _output_subset_masked
pinned in tests/test_bipole_jk_domains.py. This is the plumbing for
the pair-resolved (group-invariant, CRYSTAL-style) truncation-domain
workstream (M2–M5 in handovers/HANDOVER_BIPOLE_PRODUCTION.md §0a):
the SYM3b orbit-closure fix, the efficient M4b internal-domain
default, and the TOLINTEG-mapping prerequisite for CRYSTAL23 parity.
Next up: the remaining narrow BIPOLE analytic-gradient convergence edge
case (test_bipole_gradient.py) and GAPW iteration-count assertion
(test_periodic_gapw_cpp_host.py) tracked in
scripts/test_gate/known_reds_baseline.json, continued low-D CCM kernel
work (docs/aiccm2026dev_a_lowd_greens.md), and the periodic-scf /
regression-suite reds carried over from earlier releases. χ-CCM-B also needs
an explicit, converged sr_image_extent_bohr contract using the landed M4a
oracle, or the efficient M4b production default, before BIPOLE-routed
four-center absolute energies enter the paper fleet. The B selector and fleet
runner do not yet expose or attest M4a, and the default remains unpadded.
M4a also does not pad HSE’s dedicated screened-exchange traversal, and the
current UHF/UKS spin-schedule recursion drops an explicit extent; those paths
remain unattested even at the low-level driver surface.
Restricted semilocal four-center and dense-core fitted routes separately need
their reciprocal-tail and high-G attestation.
Added: freeze-atom constraints for live geometry optimization (vibe-view, B3) (2026-07-12)¶
The Atom Editor gains Freeze Selected / Unfreeze All buttons. Atoms
you select can be held fixed while Auto-optimize relaxes the rest; frozen
atoms show a distinct blue “locked” marker. The selection is fed to the
live-opt worker through the frozen field of the wire protocol
(live_opt.build_request), and the shared L-BFGS-B loop honors it for every
engine (MSINDO and MACE) by varying only the free-atom coordinates. Freezing
all atoms surfaces a friendly status line instead of crashing, and frozen
indices are cleared whenever a structural edit (delete, undo, redo, file
switch) would reindex the atoms. Backend semantics pinned by
tests/test_live_opt.py::test_frozen_atoms_held_fixed and
::test_run_optimization_rejects_all_frozen.
Changed: BIPOLE KS auto-FMIXING engages only without DIIS (Gap-B validated) (2026-07-13)¶
Gap-B verdict (HANDOVER_BIPOLE_PRODUCTION.md §0a): the historical ~0.5 Ha
MgO-primitive RKS oscillation does not exist in the corrected-gauge code.
Measured on MgO/STO-3G [2,2,2] SVWN c6 from SAD, no aids: plain DIIS
converges strictly monotonically in 10 iterations; PySCF KRKS converges the
same fixture in 7 cycles from every standard guess (out-of-process,
PySCF 2.13.1, GDF). CRYSTAL-style FMIXING-30% is validated as a Roothaan
damper — the DIIS-off iteration wobbles at ~1 mHa and stalls at 25
iterations, while FMIXING-30 converges it in 18 to the same fixed point
(−270.91985519, identical to 1e-9 Ha across all four aid settings). Under
DIIS the mixing is redundant damping and only slows convergence (14 vs 10
iterations), so the pure-DFT auto default in run_pbc_bipole_rks/_uks
now fires only when use_diis=False (shared
pbc_bipole_common.resolve_auto_fock_mixing, unit-pinned; explicit
fock_mixing values are honored unchanged). The convergence="auto"
strategy floor mirrors the driver (floor reported only for DIIS-off BIPOLE
KS runs); the ionic-insulator profile value is unchanged. New no-aids
monotone-convergence regression
(test_rks_bipole_mgo_no_aids_converges_monotonically) pins the absent
oscillation per CLAUDE.md §7.
The same validation quantified the pure-RKS exact-FT J reciprocal tail at
the ke=200 default on MgO: +0.997 Ha of J mass between ke 200 and 800 at
fixed SAD density, +0.987 Ha between the converged fixed points, +0.043 Ha
between 800 and 2000 (density-independent core-pair content; the SCF
landscape is untouched — identical 10-iteration monotone convergence at
both ke). The closure check: c12 no-aids −271.5306506 plus the measured
tail reproduces PySCF KRKS −270.4996235 to ~0.4 mHa — no gauge defect.
The warn_bipole_exact_j_core_tail diagnostic (landed in the parallel
pair-resolved workstream) now quotes the measured ~1.0 Ha magnitude
(the earlier ~0.49 Ha figure was ke300-referenced) and gains unit tests.
Fixed: MgO BIPOLE-vs-PySCF parity example fed PySCF a displaced O geometry (2026-07-13)¶
examples/regression/parity_mgo_rks_bipole_vs_pyscf.py divided its bohr
coordinates by ANG2BOHR while declaring cell.unit="B", parking O at
2.105 bohr instead of the rocksalt a/2 = 3.978 bohr site (a ~1.8 mHa
reference bias). PySCF references must use the fixture’s unit-exact
geometry.
[v0.15.37] - 2026-07-12 - Neese’s Cheetah¶
Fixed: χ-CCM-B gradient audits bind the complete finite torus (2026-07-12)¶
Superseded in [Unreleased] by D88 (2026-07-14). This historical D81
entry implemented the wrong row-vector premise. PeriodicSystem.lattice
stores vectors as columns, so the corrected binding is
system.lattice @ diag(mesh); the other mesh, descriptor, and character-net
guards below remain valid.
Every χ-CCM-B component helper now requires the result mesh, character mesh,
and BvK repetitions to agree and binds the recorded row-vector BvK lattice to
diag(mesh) @ system.lattice within 1e-12 bohr. Diagnostics and top-level
convention descriptors must agree. SCF-density folding additionally requires
the complete unreduced, unshifted Γ-centred character set with uniform weights
and one representative of every residue. Skew (2,3,1) coverage rejects the
wrong column-scaled lattice construction, while duplicate, incomplete,
shifted, and nonuniform character nets fail closed.
This is provenance hardening, not a new force term. It does not attest atoms, basis identity, resolved operator cutoffs, or the source of caller-supplied density/energy-weighted-density blocks. Total χ-CCM-B gradients remain fail-closed. The same milestone classifies the newly diagnosed BIPOLE internal-domain, pure-RKS reciprocal-tail, and dense-core RSGDF high-G limits as numerical-support blockers for quantitative absolute-energy records; they are not new Hamiltonian conventions or Γ/χ representation differences.
Changed: pure-RKS exact-FT Hartree now surfaces its core-tail undercoverage (2026-07-13)¶
The split-gap triage found that the exact analytic-FT Ewald J used by
pure-RKS BIPOLE at the production VIBEQC_J_EWALD3D_KE=200 misses
~0.49 Ha of density-independent core reciprocal content in absolute
totals on dense-core cells (cancels in energy differences; breaks
absolute CRYSTAL/PySCF parity; brute-mesh convergence needs
ke ≈ 2·ln(1/ε)·γ_max ≈ 11000 Ha on Mg). Until the pair-resolved SR+LR
composition replaces it, the caveat warns instead of hiding:
warn_bipole_exact_j_core_tail estimates the needed ke from the basis’s
most compact primitive and emits a RuntimeWarning + .out notice
whenever the route runs under-covered. Pins in
tests/test_bipole_production_guards.py (MgO reproduces the ~11000 Ha
estimate and warns; H₂ stays silent; a covering cutoff is silent).
Added: opt-in SR ket-image pad for BIPOLE (sr_image_extent_bohr, M4a) (2026-07-12)¶
First landing of the M4 internal-domain fix from the split-gap triage: all
four BIPOLE drivers accept sr_image_extent_bohr, which extends the SR
erfc traversal’s internal (c_λ, c_σ) ket-image ball past cutoff_bohr
while output blocks stay on the cutoff cell list (via the existing
output-subset binding — no C++ change). Exactness contract pinned in
tests/test_bipole_sr_image_extent.py: the padded home-cell J/K blocks
equal a plain build at the extent cutoff to 1e-12, transitively anchored
to the Poisson-summation truth values by the wide-ball pin landed with the
triage. pbc_bipole_common.bipole_sr_image_extent computes the safe
extent from the smeared-kernel range √(ln(1/ε)/m_ω) of the most diffuse
primitive pair plus the atom-offset bound.
Measured on MgO/STO-3G c6 (2-iter RHF): the pad recovers +0.72 Ha of truncated J_SR mass — and costs 35× (the diffuse tails it recovers are exactly what Schwarz cannot prune). It is therefore deliberately OPT-IN: the correctness instrument for parity/validation work and diffuse-basis users, while the efficient default fix remains the pair-resolved M4b (interaction-resolved selection, J-only subset binding, SYM3b-reduce composition — fail closed or noted in the code). Default behavior is byte-identical (M-series check: worst 1.1e-13, RKS noise floor).
Changed: one orbital-table renderer for RHF / ROHF / UHF; new UHF golden (2026-07-12)¶
The #/occ/eps(Ha)/eps(eV) orbital-energy table was defined four times in
scf_log.py – once each for RHF, ROHF, and the UHF alpha/beta blocks –
each repeating the header, the "-" * 54 rule, and the row format string, so
a width tweak in one could silently drift from the others. They now share a
single _orbital_table_lines(...) helper; each caller supplies only its
per-row occupation, its HOMO/LUMO/SOMO marker, and its gap line. Byte-neutral
(the mol_rhf and the new mol_uhf goldens are byte-identical across the
refactor; ROHF verified live). The deliberate dual Ha/eV columns are kept –
an orbital energy is universally quoted in both. New mol_uhf snapshot (OH
doublet) freezes the previously-unfrozen UHF .out format: the spin-resolved
alpha/beta orbital tables (HOAMO/LUAMO, HOBMO/LUBMO markers) and the
open-shell energy-components / population blocks.
Changed: properties tables (charges / bond orders / dipole) use the Table primitive (2026-07-12)¶
The atomic-charges, Mayer-bond-order, and dipole-moment tables in the shared
scf_log.py properties block (every molecular .out) now render through
vibeqc.output.Table instead of hand-formatted rows under hardcoded
"-" * 66 / 52 / 40 rules – columns and rule size to content, and the
per-scheme charge totals render via a new Table.footer() (a total row set
apart by a rule) instead of a hand-formatted sum line. Table gained
footer(*cells): at most one footer, its cells count toward column widths so
it stays aligned, rendered after a separator rule. Shared by both runners but
periodic passes molecule=None (no properties block), so only the molecular
goldens (mol_rhf / mol_rks / mol_hessian) regenerated; 4 new
Table.footer tests. This harmonizes the properties tables with the SCF /
frequency / TD-DFT tables (all now the single-underline, content-sized
Table style).
Changed: IR + TD-DFT spectra tables now use the document-layer Table (2026-07-12)¶
The hand-built vibrational-frequency table (Mode / Freq(cm⁻¹) / IR(km/mol))
and the TD-DFT excited-states table (State / E(eV) / λ(nm) / f_osc / dominant) in runner.py now render through vibeqc.output.Table instead of
fixed-width f-strings under a hardcoded "-" * 52 rule. Columns and the rule
size to content, so e.g. the IR table’s rule drops from 52 to 28 chars and
the columns fit their data – the harmonization the Table primitive was
built for. Numeric cells are pre-formatted (frequencies via
render_frequency, so eV/THz output units still apply); the imaginary-mode
i suffix and the free-text dominant-transition column are preserved. New
mol_tddft golden snapshot freezes the excited-states table; mol_hessian
regenerated for the IR table. The TD-DFT UV/Vis stick spectrum is left as-is
– it is an ASCII bar chart, not a data table, so Table is not the right
primitive for it.
Fixed: periodic optimized-geometry files no longer clobber the SCF geometry (2026-07-12)¶
run_periodic_job(optimize=True) intended to write separate
{stem}.opt.xyz / {stem}.opt.POSCAR optimized-geometry artefacts, but the
call sites passed output_stem.with_suffix(".opt") to writers that do
Path(stem).with_suffix(".xyz"|".POSCAR") – which replaces the .opt, so
h2.opt became h2.xyz and the optimized geometry overwrote the SCF
{stem}.xyz/.POSCAR; no .opt.* file was ever created. The stems are
now passed already ending in .opt.xyz / .opt.POSCAR (making the writer’s
suffix-replace a no-op), so the optimized geometry lands in its own file and
the SCF geometry is preserved. Both .opt.* files are recorded in the
.system manifest. Verified live: a periodic optimize run now emits
h2.xyz (SCF) and h2.opt.xyz / h2.opt.POSCAR, all recorded.
Fixed: .system manifest now records NTO molden + periodic CIF (2026-07-12)¶
Two artefacts a job produced were missing from the .system manifest’s
[[outputs.files]], so the manifest under-reported what the run wrote:
NTO molden files (
{stem}.nto_S<n>[_spin]_{hole,electron}.molden, molecular TD-DFT +nto=True+tddft_molden=True) are written to runtime-dependent paths not in the output plan, and were recorded nowhere.run_jobnowrecord()s each one as it writes it (appended to[[outputs.files]]; verified live).Periodic CIF —
run_periodic_job’s post-SCFreal_planomittedwrite_cif=, though the CIF write is gated onwrite_cif_fileand the dry-run plan declared it. So a written.cifwas never recorded by the plan sweep.real_plannow declares it, matching the dry-run plan.
Both are pure provenance fixes (the files were always written; now they are
tracked). mark_written/record already support undeclared paths
(tests/test_output_manifest.py::test_undeclared_files_are_appended_to_outputs).
Changed: vibrational frequencies + thermochem temperature under FormatPolicy (2026-07-12)¶
New frequency quantity kind (dimension wavenumber, canonical cm⁻¹) with
render_frequency(), and a render_temperature() wired to the existing
temperature kind. The vibrational-frequency block’s f"{freq:.1f}" values
(and the imaginary-mode magnitude) and the thermochemistry T = {:.2f} K
line in runner.py now route through them – byte-identical at the defaults
(cm⁻¹, K), so the .out is unchanged, while VIBEQC_OUTPUT_UNITS=wavenumber:THz
(or :meV) converts frequencies. New exports: render_frequency,
render_temperature; 3 tests. Verified against a live H₂O Hessian run.
Added: regression guard – the .out writers never print() (2026-07-12)¶
tests/test_out_writers_no_print.py AST-checks that the files which format
and emit .out content – runner.py, periodic_runner.py, the package
__init__’s double-hybrid writer, and the vibeqc.output writers – contain
no bare print() call, so user-facing output cannot silently escape the
central channel again (the drift the logger workstream unified). Scoped to a
curated list of verified-clean writers, mirroring
test_semiempirical_output_contract.py; it is not a repo-wide print()
ban (console scripts, ProgressLogger live-progress, and __main__ demos
legitimately print, which a grep cannot tell apart from escaped .out
content).
Changed: timings block honours the FormatPolicy (duration kind) (2026-07-12)¶
The wall-clock timings block – the last common .out quantity still
hand-formatted as a bare f"{t:12.3f}" – now renders through a new
duration quantity kind (dimension time, canonical seconds).
vibeqc.output.render_duration(value, *, width, precision) is byte-identical
to f"{value:{width}.{precision}f}" at the default (seconds) and converts
under VIBEQC_OUTPUT_UNITS=time:ms (or time:min). The eight timing sites in
runner.py and periodic_runner.py (SCF total, per-iteration average, job
total, geometry-optimisation, MLIP evaluation) route through it; the block
header keeps its literal (wall clock, seconds). Byte-neutral (goldens
green; a live RHF run confirms the 12.3f shape is unchanged). With this,
every common quantity in the .out – energies and durations – is
policy-driven; the only hand-formatted numbers left are intentional
dual-unit displays. New export: render_duration; 2 tests.
Added: periodic geometry-optimisation trace reaches the .out (MS3) (2026-07-12)¶
Before this, a periodic run_periodic_job(..., optimize=True) wrote the
final Optimized geometry block to the .out but the relaxation trace
itself (Atomic relaxation: N iters, ..., cell-relaxation lines, outer-cycle
lines) went only to stdout – so a batch .out record showed the optimised
geometry with no trace of how it got there. Now the whole relaxation runs
inside an append OutputChannel and the bipole_optimize relaxers
(relax_atoms / relax_cell / relax_cell_gradient / relax_full) grew a
file-like progress= parameter (default stdout, preserving standalone
behaviour) that run_periodic_job points at the channel – the same
progress=<channel> pattern the molecular path already uses for
run_geomopt. The trace now lands in the .out under a Geometry optimization header. New golden snapshot periodic_geomopt; the four
existing goldens are unchanged (non-optimize paths are byte-identical). No
vibeqc.output change was needed – OutputChannel was already a valid
progress stream.
Changed: double-hybrid energy block honours VIBEQC_OUTPUT_UNITS (2026-07-12)¶
_write_double_hybrid_outputs (the .out for B2PLYP-style double hybrids)
was the last SCF-energy block still hardcoding {:18.10f} values under a
literal Energy components (Ha) header. It now renders each value through
render_energy and takes the header unit from active_policy(), matching
the molecular + periodic SCF paths – so VIBEQC_OUTPUT_UNITS=eV relabels
and converts it too. Byte-identical at the Hartree default (the
double_hybrid golden snapshot is unchanged). The earlier double-print of
each energy was already collapsed; this is the units slice.
Changed: remaining runner WARNING: writes routed through warn() (2026-07-12)¶
Feature #4 adoption, round 2. The rest of the hand-written WARNING: lines
in the runners now go through vibeqc.output.warn(), so every warning fires
a structured warning event and survives --quiet:
Nine 2-space artefact-write-failure warnings in
periodic_runner.py(molden, population, extended-xyz, POSCAR, citations, XSF, density grid, and the two χ-CCM-B / periodic-QVF overlays) – byte-neutral, each already paired with awarn_output_failure(...)provenance record, now with arole=field on the human-facing warning too.The
POSSIBLY CONDUCTING STATEgap warning inrunner.py– byte-neutral (surrounding blank lines kept; the stdlibwarnings.warnis preserved; the HOMO-LUMO gap stays a hardcoded dual-Ha/eVdisplay).The periodic insulator-smearing note – normalised from a 4-space in-block echo (
WARNING: ...) to the canonicalWARNING: ...surface. This is the one intentional byte change; no golden covers it (it fires only when insulator smearing is auto-applied).
The plog.info(" WARNING: ...") sites are the live-progress surface
(ProgressLogger, § 16) and are deliberately left there; the pbc_bipole*
warnings live in bipole-owned modules and are an ask to that chat.
Changed: runner SCF error/retry sites unified onto record() (2026-07-12)¶
Feature #3 adoption. The 10 places in runner.py that wrote a human line
and separately fired the matching structured event for the same fact –
SCF failure (scf_failed), the RHF-SAD / RKS-TRAH auto-retry lifecycle
(scf_retry / scf_retry_failed / scf_retry_done), the non-convergence
FATALs (scf_nonconverged, solver_nonconverged), and the semiempirical
binding-energy block – now call record(event, human, **fields) once, so
the .out line and the .scf.jsonl record cannot drift apart. Byte- and
event-identical: the human text, event name, and every field are unchanged
(the emit now fires just before the channel flush() instead of just after,
which is unobservable – they are independent streams). New import in
runner.py: record. The remaining 18 _slog.emit(...) sites are
machine-only (banner, job_start, timings, per-stage payloads) with no paired
human line and correctly stay as bare emits.
Changed: molecular runner energy summaries honour VIBEQC_OUTPUT_UNITS (2026-07-12)¶
Feature #2 continued. New vibeqc.output.render_energy_labeled(value, *, width, precision, sign) – render_energy with the active energy unit
appended, so value and label move together (byte-identical to
f"{value:{width}.{precision}f} Ha" at the default, "... eV" under
VIBEQC_OUTPUT_UNITS=eV). The 75 hardcoded f"{e:16.10f} Ha"-style energy
strings in runner.py’s post-SCF summaries (CASSCF, MP2/SCS-MP2,
DLPNO-MP2/UMP2, DLPNO-CCSD/UCCSD, OVGF, atomization, dispersion, solvation,
binding) now route through it, so eV output units apply across the whole
molecular .out, not just the SCF energy-components block. Byte-neutral at
the default (goldens green; a live MP2 run confirms -1.1298721951 Ha ->
-30.7453887099 eV). Deliberate dual-Ha/eV displays (orbital table, the
conducting-state gap warning) are left hardcoded on purpose. New export:
render_energy_labeled; 2 tests. The matching 6 single-unit summary strings
in periodic_runner.py (SCF/total/dispersion energies, restart + geomopt
final energy) are routed through it in the same change; its 8 VBM/CBM/gap
lines are dual-Ha/eV displays and stay hardcoded.
Changed: the SCF convergence verdict survives --quiet (logger #5) (2026-07-12)¶
Feature 5 (tag essentials QUIET). The converged in N iterations; E = ...
verdict line – the one essential result of an SCF – is now tagged
Level.QUIET, so it survives VIBEQC_OUTPUT_LEVEL=quiet while the
per-iteration table above it (still STANDARD) is filtered out. To keep
the default .out byte-identical, the SCF-trace segment model grew a
per-segment separator: the verdict splits off the iteration table into its
own segment but rejoins it with a single \n (not the default \n\n), so
format_scf_trace and the STANDARD .out are unchanged (goldens green).
Combined with warn() (also QUIET), a quiet run now emits exactly the
final energy, the converged/not verdict, and any warnings. 2 tests updated
/ added.
Added: warn() / note() – a first-class warning surface (2026-07-12)¶
Feature 4. vibeqc.output.warn(message, **fields) emits a
WARNING: {message} line at Level.QUIET (so a warning survives even
VIBEQC_OUTPUT_LEVEL=quiet) and, built on record(), fires a structured
warning event carrying message plus any fields. The line format is
byte-identical to the write(" WARNING: ...\n") calls the runners already
scatter by hand, so migrating those to warn() does not move the .out at
the default level while adding the machine-collectable event and the
survives-quiet guarantee. note(message, **fields) is the non-problem
counterpart: a Note: {message} line at STANDARD plus a note event,
for information a user wants (a fallback taken, a default filled in) that is
not a warning. New exports: warn, note; 4 tests. The five 2-space
write(" WARNING: ...\n") sites in the runners (DLPNO-MP2/UMP2/CCSD/UCCSD
non-convergence, CIF-write failure) are migrated to warn() in the same
change – byte-identical .out, now each also fires a structured warning
event and survives --quiet. The one 4-space settings-echo WARNING: in
periodic_runner is left as-is (different indentation, not byte-neutral).
Added: record() – one call emits the human line and the structured event (2026-07-12)¶
Feature 3. vibeqc.output.record(event, human=None, *, level=Level.STANDARD, **fields) writes human to the .out channel AND emits a structured
event (with fields) to the active StructuredLog, in one call – so the
human-readable line and the machine-readable record cannot drift out of
sync the way separate write(...) + emit(...) calls can (the
.out-says-one-thing / .scf.jsonl-says-another bug this prevents).
human=None emits a machine-only record; each surface no-ops independently
when inactive. New export: record; 4 tests. The ~28 sites in runner.py
that currently write() and emit() separately are a per-site migration
follow-up.
Added: configurable output units via a process-wide FormatPolicy (2026-07-12)¶
Feature 2 of the logger list: route rendered numbers through a policy so
units and precision are set in one place, not baked into every f-string.
vibeqc.output.active_policy() is the process-wide FormatPolicy;
output_units("eV") (or VIBEQC_OUTPUT_UNITS=eV, or
energy:eV,length:Angstrom) switches display units, and
set_active_policy(...) replaces it wholesale. The default renders
byte-identically to the historical format (energy 18.10f Ha), so the
.out is unchanged until a knob is moved.
First conversion: the Energy components (...) breakdown (in every SCF
.out) now renders through the active policy – header unit and values
both follow it, so VIBEQC_OUTPUT_UNITS=eV yields
Energy components (eV) with converted values. New exports: active_policy,
output_units, set_active_policy.
render_energy(value, *, width, precision, sign) renders an energy at a
caller-chosen field shape in the active unit – byte-identical to
f"{e:{width}.{precision}f}" at the default (Hartree), converting in eV –
so the ~80 scattered f"{e:16.10f} Ha" sites convert byte-neutrally by
keeping their own width (no normalisation, no golden regen). The SCF
trace’s convergence line is converted; the orbital table (a deliberate
dual Ha/eV display) is left as-is. The remaining post-SCF runner strings
are a mechanical render_energy application, per-owner follow-up. New
export: render_energy.
Added: section() / section_header() – titled output blocks (2026-07-12)¶
The runners hand-drew section headers as a title write() plus a
write(" " + "-" * N) rule, in ~57 places at inconsistent widths.
vibeqc.output.section(title, *, level=Level.STANDARD, width=None, ...)
is a context manager that emits the header (title + rule) through the
channel and yields for the block body; section_header(title, ...)
returns the same header string for callers that build strings. width=None
sizes the rule to the title; an int fixes it. The header carries a
Level, so a whole section can be gated by tagging its body writes to
match. Six adjacent title+rule pairs in runner.py / periodic_runner.py
migrated byte-identically as a demonstration (golden snapshots green); the
rest are a per-owner follow-up. New exports: section, section_header.
Diagnosed: MgO split-gap pins re-derived; SR ket-image truncation root-caused (−515 mHa on the multik split test) (2026-07-12)¶
The tracked red in tests/test_pbc_bipole_multik_ewald_split.py
(test_mgo_shrink2_fixed_density_pure_rks_exact_j_closes_split_gap, stale
pin after the 9af6f958 oblique-mesh completion) is resolved and the
known_reds_baseline.json entry removed. The triage settled the apparent
converged-limit disagreement between the two G=0-dropped Hartree builders:
the exact-FT Ewald J is truthful (converged value 81.4173577 Ha on the
fixed MgO SHRINK-2 Hcore density, from an independent closed-form Gaussian
rho(G) summed to ke = 24000), while the BIPOLE split undercounts by
−515.35 mHa because the SR ket-image lattice sum in
build_jk_2e_real_space_impl truncates at the radial cutoff_bohr ball —
the erfc interaction between smeared diffuse AO-pair charges reaches far
beyond erfc between points (erf(√m·r) − erf(√m_ω·r) decay,
√m_ω ≈ 0.23 bohr⁻¹ on MgO valence pairs). The erfc ERIs, AO-pair FT
kernel, AO scales, J_LR blocks, background term, and Schwarz screening were
each validated exact against independent closed forms. The C++ builder
reproduces the Poisson-anchored reciprocal values to 7 digits once the
traversal ball is widened (16 bohr on this test), pinned by the new
test_j_sr_traversal_ball_converges_diffuse_home_element. The proper
production fix is the SYM3b M4 internal-domain pad (now a correctness fix,
padded by the smeared-kernel range) — spec’d in
handovers/HANDOVER_BIPOLE_PRODUCTION.md § 0a. Also documented there: the
pure-RKS exact-FT J at the default VIBEQC_J_EWALD3D_KE=200 undercounts
density-independent core reciprocal tails (~0.49 Ha absolute on
MgO/STO-3G); it may largely cancel between closely related fixed-core
densities, but cancellation is not guaranteed. It matters for absolute
parity. The test pin is repaired; the production M4 and reciprocal-tail fixes
remain open.
Fixed: BCCD(T) retains the semicanonical Fov*T2 triples numerator (2026-07-12)¶
The closed-shell (T) kernel now includes the disconnected Fov*T2 image in
addition to T1*ERI. Canonical RHF routes are unchanged because Fov is zero,
while BCCD(T) no longer drops this term when Brueckner T1 is zero but the
occupied-virtual Fock block is nonzero. Water/STO-3G changes from
-0.000081435 Ha to -0.000086104 Ha, matching out-of-process PySCF to
2.1e-12 Ha and a fixed-Brueckner-orbital ORCA CCD(T) rerun to the program’s
printed precision.
The four-row public run_job / vq matrix covers water/STO-3G,
water/cc-pVDZ, ammonia/cc-pVDZ, and frozen-core water/cc-pVTZ. The worst
triples delta is 7.90e-10 Ha versus PySCF and 6.72e-08 Ha versus ORCA;
all QVF files validate. Artifacts:
<vibeqc-article-release>/calculations/bccd-t-fov/20260712-51803608/.
Changed: the citation printer emits through the output channel (write_references_block) (2026-07-12)¶
The ## References block reached the .out by the three runners
hand-splicing write("\n" + format_references_block(refs) + "\n"). It is
now a first-class logger operation: write_references_block(citations, *, block=None, write=None, level=Level.STANDARD, leading="\n", trailing="\n")
in vibeqc.output.citations renders and emits the block through the
channel, tagged with a level, the same way write_scf_trace emits the SCF
trace. runner.py, periodic_runner.py, and the double-hybrid dispatcher
in __init__.py all route through it. Byte-identical (verified on real
.out output and by direct comparison); new export
write_references_block; two tests added.
Added: fixed energy-weighted overlap derivative component for χ-CCM-B (2026-07-12)¶
compute_aiccm2026dev_b_fixed_energy_weighted_overlap_lagrangian(...) now
evaluates the fixed overlap-constraint scalar
-sum_g,mu,nu W_mu,nu(g) S_mu,nu(g), and
compute_aiccm2026dev_b_fixed_energy_weighted_overlap_gradient(...)
evaluates its analytic AO-centre derivative while holding the caller-supplied
real-torus energy-weighted density blocks and lattice fixed. The scalar is a
Lagrangian companion, not a separately additive SCF energy. Both helpers
require exact cell indices, Cartesian translations, AO dimension, and block
shapes. A 3D odd-character conjugate-pair test produces real but individually
nonsymmetric residue blocks, pins the Frobenius sign/orientation explicitly,
and matches the public scalar’s central finite difference.
There is deliberately no SCF Pulay wrapper: constructing the stationary energy-weighted density still requires B-owned orbital, occupation, spin, backend, gauge, and resolved-support provenance, and the BvK seam or screened exchange derivative remains a separate explicit term. Total χ-CCM-B analytic gradients continue to fail closed. D89 supersedes the former representation-label and approach-equivalence premise without changing this component or its fail-closed boundary.
Added: write_scf_trace – level-aware, block-by-block SCF trace emission (2026-07-12)¶
Enabling refactor for per-block log levels. format_scf_trace builds one
string emitted with a single write(), so its sub-blocks (iteration
table, convergence line, energy breakdown, orbital table, properties)
could not be tagged at different levels. _scf_trace_segments now returns
the trace as (block_text, Level) segments; format_scf_trace joins them
(byte-identical) and the new write_scf_trace emits each through the
channel at its own level. The runners (runner.py, periodic_runner.py)
switched to write_scf_trace.
Byte-identical today: every segment is STANDARD, so the .out is
unchanged (golden snapshots + 179 output tests green). The point is the
structure – a block can now be moved to VERBOSE / DEBUG in exactly
one place (its segments.append(...) entry) and it will vanish on a
lower-threshold channel while the rest stay. The inter-block separator is
written at the following block’s level, so a suppressed block takes its
own leading blank line with it. New export: write_scf_trace.
Fixed: molecular semiempirical preoptimization rejects unknown methods (2026-07-12)¶
preoptimize_molecule(...) now normalizes dftb0 / scc_dftb spellings and
raises on unsupported method names instead of silently treating every non-SCC
value as DFTB0. This prevents typoed or not-yet-supported preoptimization
requests, such as method="pm6", from walking the wrong surface.
Changed: molecular semiempirical preoptimization is package-exported (2026-07-12)¶
vibeqc.semiempirical.preoptimize_molecule now exports alongside
preoptimize_periodic, matching the public API summary and avoiding the old
module-only import path.
Changed: molecular preoptimization uses the unified semiempirical runner (2026-07-12)¶
The ASE-backed molecular semiempirical preoptimizer now gets its DFTB0/SCC-DFTB
energy and gradient through run_semiempirical(...), leaving model selection in
the central runner instead of duplicating it inside the optimizer wrapper.
Fixed: semiempirical modules obey the output channel contract (2026-07-12)¶
The stale direct print() demo in msindo.py is removed, and a focused
semiempirical output-contract test now prevents direct print() emitters from
creeping back into python/vibeqc/semiempirical.
Fixed: direct semiempirical runner rejects unsupported solvent requests (2026-07-12)¶
vibeqc.semiempirical.run_semiempirical(...) now raises when solvent= is
passed to non-MSINDO methods instead of silently running DFTB, PM6, OMx, GFN2,
or CCM in gas phase.
Fixed: direct semiempirical runner rejects method-specific stray options (2026-07-12)¶
Direct semiempirical dispatch now fails closed when nddo=True is used outside
method="msindo" or ccm_options is passed outside method="ccm", avoiding
silent gas-phase or non-CCM runs with ignored method-specific knobs.
Fixed: run_job rejects stray semiempirical method options (2026-07-12)¶
The public runner now raises before calculation setup when nddo=True is used
outside method="msindo" or ccm_options= is passed outside method="ccm",
matching the direct semiempirical dispatcher.
Changed: periodic PM6/OM2 gradient and stress join the hot-path inventory (2026-07-12)¶
scripts/bench_semiempirical_hotpaths.py now times the periodic PM6 and OM2
He-dimer finite-difference gradient and stress wrappers. These rows mark the
current Python finite-difference orchestration plus native NDDO energy split
explicitly, so the remaining C++ batched-kernel work is visible in the same
developer inventory as the energy paths.
Fixed: HSE06 is a screened hybrid on the periodic routes, not silent PBE0 (2026-07-12)¶
No periodic route consumed the functional’s CAM parameters
(cam_alpha/cam_beta/rsh_omega): every route keyed its exact
exchange off hf_exchange_fraction alone, so HSE06 silently ran as
PBE0 (25 % full-range exchange, screening ignored) on every
periodic route, 3D included — Inc C of
handovers/HANDOVER_SLAB_2D_ROUTING.md. The new shared resolver
vibeqc.periodic_screened_exchange.resolve_periodic_exchange maps the
CAM convention onto K_HF = (α+β)·K_full − β·K_erfc(ω); for HSE06 the
full-range coefficient is exactly zero, so the periodic assembly is the
pure short-range 0.25·K_erfc(ω = 0.11) direct lattice sum with no
Madelung/exxdiv seam and no bipolar far-field (the CRYSTAL treatment).
Wired through the Ewald family (Γ + multi-k, RKS + UKS, EWALD_3D and
SLAB_EWALD_2D — so run_periodic_job(functional="hse06") on a dim=2
slab via AUTO is now correct) and through BIPOLE RKS/UKS
(build_bipole_{restricted,unrestricted}_fock take an
exchange_assembly). Gates: HSE06 ≠ PBE0 on every wired route; Γ ==
multi-k(1,1,1) == UKS closed-shell; BIPOLE == multi-k-Ewald to ~2e-7 Ha
on the shared fixture; slab total a₃-invariant and equal to the
vacuum-converged 3D reference; erfc-kernel ω→0 / ω→∞ limits; HSE06
citations (Heyd–Scuseria–Ernzerhof 2003 + Krukau 2006) fire in
.out/.bibtex (tests/test_periodic_screened_exchange.py, 32 tests).
Fixed: routes without a screened-exchange kernel fail closed on range-separated functionals (2026-07-12)¶
Complementing the above, the periodic routes whose exact exchange is
full-range only now raise NotImplementedError on any range-separated
functional instead of silently substituting the full-range twin:
multi-k GDF (run_krhf_periodic_gdf / run_kuhf_periodic_gdf, which
also carry COSX-K), Γ GDF UKS (run_pbc_gdf_uks; the RHF/RKS twin was
already guarded), the legacy Γ GDF fallback
(run_rhf_periodic_gamma_gdf), the Γ GPW/GAPW/OT drivers, and the
periodic analytic-gradient entry points
(compute_gradient_periodic_rks_{gamma,multi_k}, whose exchange Pulay
terms are full-range only — a screened-K SCF would otherwise get
silently inconsistent forces). Long-range-corrected RSH (ωB97X,
CAM-B3LYP, LC-ωPBE) fail closed on all periodic routes: their
K_full arm needs an exxdiv treatment that is not validated (maintainer
policy 2026-07-09). The experimental RSGAPW dedicated entry points are
unchanged. Docs: user-guide functionals/slabs/periodic-methods pages
updated in the same merge.
[v0.15.36] - 2026-07-12 - Neese’s Cheetah¶
Added: opt-in BvK-ewald reference for one-call CCM correlation (2026-07-12)¶
run_ccm_ri_mp2 and run_ccm_ri_ccsd gain reference="neutral"|"direct"
(default "neutral", unchanged). reference="direct" builds the RI
correlation on the BvK-ewald direct-torus SCF (run_ccm_rhf_direct, same
shared neutral cderi L, exchange-q=0 seam included) instead of the
strict-zero-mode lean neutral SCF.
Why (measured, the 2026-07-10/11 D2 KMP2 anchor —
handovers/HANDOVER_D2_EXXDIV.md § Results + Part 2): the strict-zero
reference deletes free-space exchange whose weight grows with the cluster
(~linearly in N_c per atom), and the correlation inherits a growing
distortion (c-diamond (2,2,2): −59.0 mHa/atom vs external PySCF KMP2 −29.9
(ewald) / −41.1 (None)). On the direct reference the CCM correlation
matches out-of-process PySCF KMP2 at exxdiv='ewald' to 3e-6 Ha/cell on
Γ-diamond and 0.24 mHa/atom on LiH (2,2,2) — residuals at the per-fixture
rsgdf fitting floor. The default stays "neutral" pending the maintainer
decision on flipping it (it moves every absolute correlation number);
open-shell reference="direct" fails closed (the direct route has no UHF
sibling). Mirrors the wire route’s exxdiv="free" opt-in precedent.
Pins: tests/test_ccm_mp2.py::test_ri_wrappers_reference_direct_opt_in
(one-call == manual recipe on one shared L, BvK-ewald recorded on the
result, seam-widened denominators shrink |E_corr|, invalid/open-shell fail
loudly, default composition unchanged).
Changed: periodic PM6/OMx route-status labels are explicit (2026-07-12)¶
semiempirical_route_status("periodic-pm6") and
semiempirical_route_status("periodic-omx") now report mixed-native,
non-production, performance-critical status. The labels make the current
native Gamma energy plus Python finite-difference gradient/stress helper split
queryable from code and mirror the user-guide caveat.
Changed: periodic semiempirical helper memory estimates (2026-07-12)¶
estimate_semiempirical_memory(...) now recognizes PeriodicSystem inputs
and counts periodic image/neighbour buffers plus stress/strain scratch for
semiempirical helper optimizations. This extends the basis-free memory
preflight beyond molecular run_job routes without constructing a Gaussian
basis.
Fixed: semiempirical parameter export uses the output channel (2026-07-12)¶
vibeqc.semiempirical.io.save_parameters no longer prints directly to stdout
from library code. Its completion note now goes through vibeqc.output.write
at Level.VERBOSE, so it is silent outside an installed output channel and
opt-in inside .out logging.
Changed: three silent BIPOLE hazards now warn; EXT EL-POLE defect now gates (2026-07-12)¶
Production-readiness batch from the BIPOLE route audit
(handovers/HANDOVER_BIPOLE_PRODUCTION.md):
Legacy-gauge multi-k warns. The 3-D legacy (non-exchange-split) gauge adds the spheropole to the reported energy but never to the Fock operator, so its converged multi-k total is not a stationary value (documented Ha-scale wrong-sign k-mesh dependence on MgO). The configuration stays reachable on purpose — ad-hoc k-lists (band paths) auto-fall back to it — but every 3-D legacy multi-k run now emits a
RuntimeWarningand an.outnotice naming the non-stationarity. Open maintainer question: whether an explicituse_exchange_ewald_split=Falseat a Monkhorst-Pack multi-k mesh should hard-fail instead (parity/diagnostic scripts still use it).Odd-electron multiplicity=1 promotion warns. The deliberate
7991ae58promotion to the minimum open shell (Al/Cu primitive cells) is unchanged, but a user who meant a singlet is now told.Net-charged cells warn that the implicit jellium background makes the absolute energy convention- and volume-dependent (Makov-Payne / Freysoldt territory) — previously silent.
EXT EL-POLE parity test:
skip→xfail(strict=True). The ρ̂(K=0) ≈ 12.4-vs-N_e=20 AO-pair-FT convention defect now gates: a fix (or a wrong “fix”) flips the test instead of passing silently.
All warnings are shared helpers in pbc_bipole_common used by all four
drivers; pins in tests/test_bipole_production_guards.py (positive and
negative: the corrected gauge, explicit doublets, and neutral cells stay
warning-free).
[v0.15.35] - 2026-07-11 - Neese’s Cheetah¶
Added: fixed-density kinetic derivative component for χ-CCM-B (2026-07-11)¶
compute_aiccm2026dev_b_fixed_density_kinetic_energy(...) now contracts a
caller-supplied real-torus density with the matching lattice kinetic operator,
and compute_aiccm2026dev_b_fixed_density_kinetic_gradient(...) evaluates its
analytic AO-centre derivative while holding those density blocks fixed. The
two paths share exact lattice-cell, Cartesian-translation, and AO-shape guards,
and their isolated scalar/derivative pair is pinned by central finite
differences without invoking an SCF response or another CCM implementation.
There is deliberately no SCF convenience wrapper yet: current results do not
attest the resolved one-electron lattice cutoff needed to reconstruct the same
operator support. The total χ-CCM-B gradient remains fail-closed pending the
energy-weighted Pulay/adjoint, BvK exchange-q=0 seam, RI/RIJCOSX response, and
post-HF response terms. D89 supersedes the former representation-label and
approach-equivalence premise without changing this component or its
fail-closed boundary.
Changed: MSINDO CIS joins the semiempirical hot-path inventory (2026-07-11)¶
The semiempirical hot-path benchmark inventory now includes a public
msindo_cis(..., spin="singlet") H2O case. It records excitation energies,
amplitude dimensions, and dominant-transition metadata for the current
Python-reference CIS/TDA route without promoting Thiel excited-state benchmark
coverage.
Changed: public DFTB0 preoptimization joins the semiempirical hot-path inventory (2026-07-11)¶
scripts/bench_semiempirical_hotpaths.py now includes a public
preoptimize_molecule(method="dftb0") H2O case. The inventory records
initial/final energy and residual gradient RMS for the first optimization entry
in the S1 runtime and memory baseline.
Changed: gradient and stress routes join the semiempirical hot-path inventory (2026-07-11)¶
The semiempirical hot-path benchmark inventory now includes DFTB0, GFN2-xTB, and PM6 molecular H2O gradient cases plus periodic DFTB0 He-dimer gradient and stress cases. The new entries record measured shape/norm metadata, runtime, and peak RSS for the S1 performance baseline while leaving physics parity to the existing focused gradient and stress regression tests.
Changed: CCM smoke systems join the semiempirical hot-path inventory (2026-07-11)¶
The semiempirical hot-path benchmark inventory now includes direct public MSINDO CCM energy cases for 1D no-Ewald He7, 1D Madelung HF6, 2D no-Ewald MgO, and 3D no-Ewald MgO. These reuse existing C++/Python parity fixtures and expose CCM dimensional coverage in the S1 performance baseline without changing CCM physics.
Changed: periodic silica joins the semiempirical hot-path inventory (2026-07-11)¶
The semiempirical hot-path benchmark inventory now includes a simplified alpha-quartz SiO2 periodic DFTB0 single point. This gives the S1 performance baseline a small silica/zeolite-like periodic cell while keeping the unstable default SCC-DFTB quartz route out of the runnable inventory.
Added: article-suite metadata for semiempirical hot-path cases (2026-07-11)¶
scripts/bench_semiempirical_hotpaths.py --list-suites now mirrors the
article-side external-suite map for GMTKN55, Korth-Thiel GMTKN24,
Dral-Thiel ground-state OMx/ODMx, Delta-factor / SSSP solids, Matbench
Discovery, Thiel excited states, and MACE literature validation. Runnable cases
are explicitly marked as compact representatives, while Thiel excited states,
Matbench, and MACE remain future or adjacent scope with no computed-case claim.
Added: bond-analysis method families – Wiberg/DI, NBO/NPA, EDA, orbital entanglement (2026-07-11)¶
Four new analysis modules land the long-parked bond-analysis work
(originally staged on the dev branch 2026-06-24, rebased onto current
main):
vibeqc.bond_analysis– Wiberg bond indices (Löwdin-orthogonalised density; Wiberg 1968) and an AO-approximated delocalization index (Matito 2007 / Outeiral 2018), plusbond_order_summarycombining Mayer + Wiberg + DI, with periodic Gamma-point wrappers.vibeqc.nbo– Natural Population Analysis charges and a Weinhold-style NBO search (BD/LP/CR/BD*/RY* classification) with second-order donor-acceptor (E2) analysis (Reed-Weinstock-Weinhold 1985, Foster-Weinhold 1980, Reed-Curtiss-Weinhold 1988).vibeqc.eda– LMO-EDA (Su-Li 2009) and Morokuma-style (1971) two-fragment energy decomposition with fragment-density utilities.vibeqc.entanglement– single-orbital entropy, mutual information, entanglement bond orders, and correlation clusters (Legeza-Sólyom 2003; Szalay et al. 2017; Ding-Matito-Schilling 2025).
Integration: the .population.{txt,json} sidecars gain Wiberg
bond-index and NPA charge sections on every population dump
(molecular + periodic Gamma proxy), and the corresponding citation
routes fire in .out / .bibtex / .references / .system. All
eleven new citation-database entries were verified against Crossref /
arXiv (four hand-entered metadata errors from the original branch were
caught and fixed; tests/test_citations.py::test_bond_analysis_routes_pin_dois
pins the verified DOIs).
The dev branch’s duplicate Gamma/multi-k COHP module was not
landed: the canonical periodic COHP/COOP implementation is
vibeqc.coop_cohp (shipped v0.15.x), and single-canonical-impl rules
apply. User guide: docs/user_guide/bond_analysis.md.
Added: log levels on the output channel (Level, VIBEQC_OUTPUT_LEVEL) (2026-07-11)¶
vibeqc.output.write(text, level) now tags a message’s verbosity band and
the channel filters by a threshold: a message emits when
level <= channel.level. Bands: Level.QUIET (always shown – final
energy, convergence verdict, fatal errors), Level.STANDARD (the default
and the historical .out content), Level.VERBOSE (opt-in detail),
Level.DEBUG (diagnostics). An untagged write(text) is STANDARD and
the channel default is STANDARD, so the .out is byte-identical
until a write is tagged or the threshold moves (golden snapshots confirm).
Users set the level with VIBEQC_OUTPUT_LEVEL=quiet|standard|verbose|debug
(or 0-3); code uses output_level(...) for the default new channels
adopt, or channel.set_level(...) for one already open. Factories take an
optional level=. New exports from vibeqc.output: Level,
output_level. Documented in docs/user_guide/logging.md; this is the
.out’s own verbosity axis, separate from ProgressLogger’s live-stdout
verbose= gate.
Fixed: double-hybrid .out printed every energy twice; now one unified block (2026-07-11)¶
_write_double_hybrid_outputs rendered each energy twice – once as
SCF energy: {:16.10f} (no unit) and again as
E(RKS reference) = {: .12f} Ha – in two precisions and two label
styles, under a header that spelled “Hartree”. It now emits a single
Energy components (Ha) block in the same {label:<32s} {value:18.10f}
style as the molecular and periodic SCF paths (MS2). Labels: RKS reference, MP2 correlation, Dispersion (when present), Total energy. The double_hybrid golden snapshot was regenerated; one test
assertion updated (MP2 correction -> MP2 correlation).
Changed: periodic .out SCF trace + energy breakdown now match the molecular format (2026-07-11)¶
Output-format change (MS2). The periodic .out SCF section was
hand-assembled by periodic_runner’s own _scf_trace_text /
_energy_summary, which disagreed with the molecular path on every axis:
dE at +.4e vs +.3e, ||[F,DS]|| at 14.4e vs .3e, DIIS width 5d
vs 3d, a different table header (SCF iteration trace), a
repr-style converged = True instead of converged in N iterations, and
E_total (Ha) / E_electronic (Ha) labels instead of the molecular
Total energy / Electronic. Periodic and molecular now render through
the same vibeqc.output.formats.scf_log.format_scf_trace: identical
iteration table, convergence line, and Energy components (Ha) breakdown.
Enabled by two prerequisites: _format_iter_line reads dict trace steps
(GAPW multi-k) as well as SCFIteration structs, and
_format_energy_components gained a DFT+U (Dudarev) row (shown only when
non-zero, so molecular output is unchanged). Periodic-only smearing
quantities (kBT, T_elec, entropy, free energy, Fermi level) moved to a
Finite-temperature (smearing) block that follows the shared breakdown.
The periodic_rhf golden snapshot in tests/test_out_format_snapshot.py
locks the new format; molecular goldens are byte-identical (verified). The
five periodic assertions that pinned old strings were updated. The
periodic optimiser summary lines (bipole_optimize) are a separate
issue — they run outside run_periodic_job’s channel scope and are left
for the periodic-scf chat (documented in the handover).
Fixed: Bloch RSGDF uses the physical shifted reciprocal sphere (2026-07-11)¶
build_lpq_bloch_native_fft now enumerates the physical shifted support
0 < |G+q| <= sqrt(2 E_cut) instead of adding q to an already truncated
|G| ball. Previously, reciprocal-equivalent q and q+G0 labels sampled
different cutoff-boundary crescents. A half-open representative repairs that
odd-mesh mismatch but breaks time reversal at an even-mesh Nyquist channel;
the shifted physical sphere has both reciprocal-label invariance and inversion
covariance by construction. The compact fitted Gram and canonical factor now
pass reciprocal-relabel tests on a non-symmetric lattice; the canonical factor
passes an explicit Nyquist time-reversal gate, and the canonical χ-CCM
post-HF inverse transform matches the Γ-CCM folded fitted Gram operator at the
1e-14 floor on skew (2,1,1) and (3,1,1) controls. D89 classifies this as
a neutral-torus fitted-operator/representation control, not a Γ-CCM versus
χ-CCM approach-equivalence result. The q=0 support and accumulation order are
byte-preserved. Pre-fix records that built nonzero-q RSGDF factors are
revision-bound and must be rerun before quotation: exact-GDF-K RHF/UHF and
hybrid RI, Γ-CCM folds, and RI post-HF/full-pair caches. Semilocal RI and
RIJCOSX SCF use only q=0 RSGDF factors for J and are numerically unaffected by
this support change.
Added: k-pair star reduction of the per-q fold cderi build (2026-07-11)¶
ccm_neutral_cderi_fold(..., symmetry=True | CCMSymmetry) reduces the
fold’s N_c × n_q per-(k_bra, k_ket) build_lpq_bloch_native_fft calls
to the space-group + time-reversal star representatives and reconstructs
the remaining fits by the k-pair covariance of the canonical-frame GDF
cderi — per-atom lattice-shift phases on the bra/ket/aux indices, no
non-symmorphic τ phase (it cancels between the aux conjugate and the pair
factor; identity + derivation inlined at neutral.py::_fold_star_reconstruct,
probe-pinned at 8.8e-13–3.8e-10 including diamond glides). The current
conservative planner admits common reciprocal shifts, time reversal (both
~1e-14), and exactly rotated momenta. The shared builder now also makes a
ket-side mod-G relabelling exact through its physical shifted support, but
expanding the optional star inventory to exploit that relation is deferred.
Consequently small (2,1,1)-class meshes still reduce nothing in this planner
(logged, bit-identical output through the symmetry path); measured wins start
at (2,2,1) — diamond/MgO 10/16
builder calls (1.60x), star-reduced == un-reduced fold to < 1e-9 at a
cell-list-converged cutoff and < 1e-10 Ha in the direct-torus SCF energy
at production defaults — and grow with mesh richness (cubic (2,2,2)
≈ 3.2x by the same counting, the LiH-fold production bottleneck case).
Unlike the Γ-supercell (M3) route, the wall-clock win is immediate — the
fold is builder-call-dominated: diamond (2,2,1) fold 59.3 → 36.0 s at
production defaults, matching the 10/16 call fraction with no C++ change.
Enumeration lives in ccm_symmetry_fold_kpair_plan (reusable by the
multi-k GDF driver’s k-pair cache). The direct-torus drivers reach it via
run_ccm_rhf_direct / run_ccm_rks_direct(..., cderi_symmetry=True)
(forwarded to whichever cderi_build runs). Gates:
tests/test_ccm_symmetry.py (10 new, 32 total green).
Added: periodic jobs cite Pisani; bundled-basis jobs cite the BSE (2026-07-11)¶
Two citation entries existed but were reachable from nothing:
pisani_crystal_1988, the foundational monograph for the LCAO crystalline-orbital periodic HF treatment vibe-qc implements, now fires for every periodic job viaroutes.methods._periodic_lcao(the periodic analogue of an always-on method reference – the crystalline-orbital reference underlies the periodic KS-DFT and post-HF routes too).pritchard_bse_2019, the Basis Set Exchange, now fires alongside any basis that resolves to a bundled (routed) entry, viaroutes.basis_sets._bse_bundled_provenance– the offline BSE catalog is where the bundled.g94library comes from, so a job that draws a bundled basis cites it. A custom / external basis is a route miss and does not fire it. Deduplicated by entry key, so it appears once regardless of how many basis routes match.
Added: the Makov-Payne finite-box correction cites its source (2026-07-11)¶
makov_payne_1995 was in the database but reachable from nothing. It is now
surfaced from vibeqc.madelung via MADELUNG_CITATION_KEYS / method_citations,
the same module-constant pattern as the BDIIS optimiser and the periodic stress
tensor.
Not a [routes.*] row, because the correction never fires through
run_periodic_job: every reachable Coulomb method zeroes it (EWALD_3D and SLAB
return 0.0; the Γ-only DIRECT_TRUNCATED user route FFT_POISSON was retired in
v0.13.0 and raises), and the default GDF path applies the exxdiv=’ewald’
exchange-divergence Madelung constant instead, cited separately. The monopole
correction (madelung_energy_correction, makov_payne_coefficient_cubic,
apply_madelung_correction) is an offline library capability a caller applies to
a periodic total energy, and the module now attributes it for direct callers
rather than claiming a job cites it.
Changed: periodic stress cites Nielsen-Martin, not Doll; phonon papers removed (2026-07-11)¶
Three citation entries described methods vibe-qc does not implement:
doll_stress_2004is the analytic Hartree-Fock cell gradient. The GPW/GAPW stress that ships (vibeqc.periodic_gapw_stress) is a finite-difference Hellmann-Feynman + Pulay decomposition following Nielsen-Martin 1985. Removed and replaced by a newnielsen_martin_1985entry (DOI 10.1103/PhysRevB.32.3780), surfaced from the module viaSTRESS_CITATION_KEYS/method_citationsthe same way the BDIIS optimiser surfaces its provenance – the stress returns a bare(3, 3)tensor, so it has no[routes.*]row.parlinski_phonon_1997(supercell direct method) andtogo_phonopy_2015(phonopy) were removed. The shipped phonons (vibeqc.periodic_gapw_phonon) are a Γ-point mass-weighted Hessian by central differences on analytic forces – no supercell, no Fourier interpolation, phonopy unused – so neither paper describes what runs. Citing them would misattribute rather than credit.
Changed: resolve_progress warns instead of silently nulling a bad progress= (2026-07-11)¶
resolve_progress accepts bool / None / ProgressLogger / any object
with a callable iteration. Anything else fell through to a silent no-op
logger – so passing an unexpected object (an OutputChannel, which has
.write but no .iteration, or a typo) dropped all progress with no
signal. An unrecognized truthy value now emits a RuntimeWarning naming
what it got. Falsy values (0, "", []) still read as “off” silently.
Byte-neutral; no valid caller changes behaviour.
Added: golden-file .out format snapshots – the freeze mechanism (2026-07-11)¶
tests/test_out_format_snapshot.py locks the .out format against
committed golden files (tests/golden/*.out) for molecular RHF, RKS/PBE,
and a double hybrid. This is the “then freeze” half of the output-logger
decision: after the one clean break, the format cannot drift again
silently.
It freezes the scaffold, not the physics. Before comparison the .out is
canonicalized – volatile fields (timings, memory, the banner git field,
paths) scrubbed, and every number’s digits zeroed while its shape is kept
(-76.0261400000 Ha -> -00.0000000000 Ha). So a label / width /
precision / alignment / unit / separator change is caught, a legitimate
energy change is not, and the golden files are independent of the compiled
core. Regenerate deliberately with VIBEQC_REGEN_GOLDEN=1 pytest tests/test_out_format_snapshot.py and review the diff before committing.
Added: Axilrod-Teller-Muto three-body dispersion (s9)¶
D3BJParams gains an s9 field scaling the ATM three-body
(dipole-dipole-dipole) term – a3 in Eq. (3) of Grimme, Brandenburg,
Bannwarth & Hansen, J. Chem. Phys. 143, 054107 (2015). The C++ D3
kernel implements the term and its analytic gradient; both the builtin
and dftd3 backends honour s9.
s9defaults to0.0. Published per-functional(s8, a1, a2)damping sets were fit two-body-only, so a plaindispersion="d3bj"remains the two-body D3(BJ) energy.D3’s ATM damping is the zero-damping function on the tabulated
r0abcut-off radii, not the Becke-Johnson radius used by the two-body terms (and not D4’s ATM damping, which does use the BJ radii). Ther0abtable is now available to the C++ core;tests/test_dispersion_atm.pypins it againstvibeqc._d3_r0abso the two copies cannot drift.Validated against Grimme’s reference
dftd3library to 1e-12 Ha on homonuclear rare-gas clusters, the systems for which vibe-qc’s builtin C6 table is exact. The analytic ATM gradient matches finite differences to ~1e-14.
Fixed: dftd3 backend silently applied the three-body term¶
dftd3’s RationalDampingParam defaults s9 = 1.0. vibe-qc omitted s9
when constructing it, so the dftd3 backend added an ATM contribution to
every D3(BJ) energy while the builtin C++ backend added none. Because
backend="auto" prefers dftd3 when it is installed, the same
compute_d3bj(mol, "pbe") – and the same run_job(...) – returned
different physics depending on whether an optional pip package was present.
The same defect was present in compute_d3bj_periodic.
s9 is now always passed explicitly. This changes numbers: D3(BJ)
energies from the dftd3 backend lose the spurious three-body term (on
H2O/PBE, +3.2e-10 Ha; larger for denser systems). The two backends now
agree wherever their C6 tables do.
Changed: PBEh-3c and HSE-3c enable the three-body dispersion term¶
pbeh-3c and hse-3c now carry s9 = 1.0, so their D3 energy includes
the ATM term their defining papers require (“the three-body dispersion is
always included in the PBEh-3c method”, Grimme et al. 2015; HSE-3c ESI
Sec. A). This is a small, repulsive shift — on gas-phase benzene about
+0.0094 mHa — that grows with density and matters for the lattice-energy
use cases these composites target. run_job(method="pbeh-3c" / "hse-3c")
totals move by that amount. hf-3c, b97-3c and b3lyp-3c stay
two-body: HF-3c’s paper never mentions ATM, and the others publish no
three-body scale of their own.
(The HSE-3c (a1, a2) and gCP constants were corrected separately in
v0.15.34; this entry only turns the three-body term on.)
pbeh-3candhse-3cnow routeaxilrod_teller_1943, which was in the citation database but wired to nothing. The SCF log labels the correctionD3-BJ(ATM)and printss9.
Added: VIBEQC_STRICT_OUTPUT – channel-less write() fails loudly (2026-07-11)¶
vibeqc.output.write() / flush() are a deliberate no-op when no
OutputChannel is active, so a writer module stays callable from a test
or a notebook. The cost is that forgetting to install a channel loses
output silently. VIBEQC_STRICT_OUTPUT=1 (or strict_output(True) at
runtime) turns that no-op into a RuntimeError, so a “forgot the
channel” bug fails where it is cheap to catch. Off by default; production
stays lenient. The gate lives in the module-level write/flush, read
once at import into a constant so the hot path stays a bare attribute
read. Writes inside a channel are unaffected.
Fixed: GFN2 acceptance matrix D4 status is current (2026-07-11)¶
The semiempirical acceptance matrix no longer says GFN2-D4 is unimplemented.
It now records the current native post-SCF D4 scope (H, He, B, C, N, O, F,
Ne), the GFN2D4UnsupportedWarning behavior outside that reference-data set,
and the genuinely remaining GFN2 production gates: periodic AES image-cell
terms, broader periodic molecular-limit parity, mixer hardening, and external
xtb parity.
Fixed: features table semiempirical scope is current (2026-07-11)¶
docs/features.md now reports current semiempirical scope: MSINDO INDO H-Xe,
closed-shell NDDO H/Li-F/Na-Cl, native C++ production routes for supported
MSINDO energy/gradient/CCM/COSMO helpers, and GFN2’s gated experimental status
with scoped post-SCF native D4.
Fixed: MSINDO gradient status in semiempirical guide is current (2026-07-11)¶
The semiempirical user guide no longer says MSINDO analytic gradients are only partially assembled. It now distinguishes native molecular closed-shell analytic gradients and closed-shell CCM gradients from the NDDO, odd-electron, and excited-state/root-tracking fallback/reference paths.
Fixed: MSINDO user-guide feature matrix is current (2026-07-11)¶
docs/user_guide/msindo.md now mirrors the implementation scope in the feature
matrix: INDO H-Xe, closed-shell NDDO H/Li-F/Na-Cl, C++-backed closed-shell INDO
analytic gradients, Cs-and-heavier unsupported, and heavy open-shell d/p-block
UHF limited to named validated fixtures rather than advertised as a blanket
route.
Changed: native MSINDO SCF reserves fixed work buffers (2026-07-11)¶
The native INDO/MSINDO RHF and UHF SCF drivers now reserve their fixed-size DIIS history vectors and per-atom block lists up front. This trims avoidable vector growth in production C++ energy calls without changing the SCF equations or convergence policy.
Fixed: PM6 acceptance status matches the production caveat wording (2026-07-11)¶
The semiempirical acceptance matrix now labels PM6/UPM6 as a production-oriented molecular PM6-like-total route with explicit caveats: exact MOPAC heat-of-formation convention parity is still open, fallback gamma/core-core terms remain documented where MOPAC diatomic data is incomplete, and periodic PM6 stays experimental.
Changed: PM6 optimization regression now asserts energy descent (2026-07-11)¶
The PM6 FD-optimization benchmark for H2O and CH4 now requires optimizer convergence and a real energy decrease instead of only checking that the final energy stayed finite. This tightens the molecular PM6 production claim while keeping the remaining MOPAC-convention caveats explicit.
Changed: GFN2 ionic diatomics join the hot-path inventory (2026-07-11)¶
The semiempirical hot-path benchmark inventory now mirrors the GFN2 SCC stabilization regression set for long-range LiH, LiF, and NaCl. These cases record native SCC convergence, iteration count, energy, and charges so the 3rd-order bistability fix stays visible in performance sweeps.
Changed: PM6 and OM2 methane join the hot-path inventory (2026-07-11)¶
The semiempirical hot-path benchmark inventory now includes PM6 and OM2 methane single points alongside the existing water smoke cases. This completes the runnable water-plus-methane molecular NDDO slice requested by the semiempirical performance roadmap.
Changed: MSINDO heavy fixtures join the hot-path inventory (2026-07-11)¶
The semiempirical hot-path benchmark inventory now includes validated SbF3 and XeF2 closed-shell MSINDO direct-energy cases. These keep the heavy 5p trajectory-SCF path visible in performance sweeps while avoiding the separately documented brittle heavy-element fixtures.
Added: basis-free semiempirical memory preflight (2026-07-11)¶
Semiempirical run_job routes now emit the standard memory estimate block
without constructing a Gaussian basis. The new estimate counts valence dense
matrices, SCC/DIIS scratch, finite-difference gradient scratch, MSINDO COSMO
cavity matrices, and CCM image-cell buffers, and the same helper feeds
VIBEQC_DRY_RUN_ESTIMATE for semiempirical jobs.
Changed: NH3 and benzene join the semiempirical hot-path inventory (2026-07-11)¶
The semiempirical hot-path benchmark inventory now includes NH3 single points for GFN2-xTB, PM6, and OM2, plus benzene single points for DFTB0 and GFN2-xTB. These close the first small-molecule coverage gap in the S1 performance baseline without expanding the default smoke suite.
Fixed: periodic COOP/COHP jobs now cite the defining papers (2026-07-11)¶
run_periodic_job(..., coop_cohp=True) computed the COOP/COHP bonding
analysis for the QVF dos.coop / dos.cohp sections but never passed
properties=["coop", "cohp"] to the citation assembly, so the
Hughbanks-Hoffmann 1983 (COOP), Dronskowski-Bloechl 1993 (COHP),
Deringer 2011, and LOBSTER 3.0 papers were unreachable from any real
periodic job. The citation now fires exactly when the analysis runs
(coop_cohp + output_qvf + converged SCF), reaching the .out
references block, the .bibtex / .references siblings, and the
.system manifest. End-to-end pins (positive + no-QVF negative
control) live in tests/test_feature_citation_end_to_end.py.
[v0.15.34] - 2026-07-11 - Neese’s Cheetah¶
Added: semiempirical route status registry (2026-07-10)¶
The semiempirical package now exposes a small implementation-status registry via
semiempirical_route_status(route). It records native, mixed-native,
gated-experimental, and python-reference routes so the remaining MSINDO
CIS/TDA, CIS-gradient, OVGF/GF2, MD, metadynamics, and root-tracking hot loops
are labelled explicitly until their C++ kernels land or they are declared
intentional Python orchestration.
Changed: CCM FD fixed-WS neighbor buffers reserve capacity (2026-07-10)¶
The native MSINDO CCM finite-difference loop now reserves the fixed Wigner-Seitz neighbor buffers before each displaced energy build, avoiding avoidable vector growth in the image-cell reconstruction path without changing the fixed-topology gradient numerics.
Fixed: CCM FD native MgO 3D non-Madelung parity (2026-07-10)¶
The native INDO/MSINDO DIIS solve now uses the same partial-pivot linear-system
semantics as the Python reference, with a residual guard for invalid Pulay
coefficients. This fixes the previously suspect native MSINDO CCM
finite-difference gradient on the 3-D MgO non-Madelung fixed-Wigner-Seitz path:
the direct C++ result is finite, matches Python below 1e-8 Ha/bohr, and the
public wrapper stays on the native route instead of falling back to Python.
Fixed: periodic GFN2 H2O molecular-limit parity is strict again (2026-07-10)¶
The periodic Gamma GFN2 molecular-limit H2O benchmark no longer carries the
stale 3.5 Ha tolerance from the old atom-vs-shell SCC gap. In a 20 bohr box
with a 15 bohr cutoff, run_gfn2_xtb_gamma now matches molecular
run_gfn2_xtb to better than 1e-10 Ha, and the benchmark asserts that strict
parity directly.
Fixed: GFN2 D4 unsupported elements no longer vanish silently (2026-07-10)¶
GFN2-xTB’s post-SCF native D4 helper is validated only for the native D4
reference-data set (H, He, B, C, N, O, F, Ne). Molecules containing other
elements still receive a zero D4 contribution, but now emit
GFN2D4UnsupportedWarning naming the unsupported atomic numbers instead of
silently dropping dispersion. The experimental catalog and semiempirical user
guide now describe the corrected scope.
Added: guard against citation entries that no route reaches (2026-07-10)¶
tests/test_citation_no_dead_entries.py fails when an [entries.*] block is
reachable from neither a [routes.*] row nor a string literal in
python/vibeqc source, per CLAUDE.md § 8 step 2.
Reachability has two channels, and checking only the routes table is what let
this rot accumulate. Entries passed to assemble(extra_entries=[...]) or
exported as module citation constants (BDIIS_CITATION_KEYS) are real citation
surfaces that never appear in the routes table. The 2026-07-10 audit’s list of
26 “unrouted” entries contained 10 such: the five MS/XMS-CASPT2 papers and the
UNO-CAS reference fired from runner.py, the two MACE foundation-model papers
from mlip/_mace_models.py, and two basis-optimisation papers from
BDIIS_CITATION_KEYS. The guard detects that channel by parsing each module and
matching string literals exactly against entry keys, so a key named only in a
comment does not count.
Seven genuinely dead entries remain, each allow-listed with its reason:
andrae_ecp_1990, makov_payne_1995, doll_stress_2004,
parlinski_phonon_1997, togo_phonopy_2015, pisani_crystal_1988,
pritchard_bse_2019.
Added: Eigen recorded as internal provenance in the .system manifest (2026-07-10)¶
The eigen citation entry existed but no route reached it, so the one library
linked into every single job was recorded nowhere. It now fires unconditionally
from routes.libraries.eigen, with print = false.
Eigen is link-time infrastructure, not a scientific dependency: no method vibe-qc
claims to implement traces to it. print = false puts it in the .system
manifest as provenance and keeps it out of the .out / .bibtex /
.references block a paper’s Methods section copies from. This is the first
database entry to use the flag, which the registry has supported (and documented,
naming pybind11 as the model case) since the flag was introduced.
Fixes test_write_bibtex_emits_one_entry_per_citation, which asserted one BibTeX
block per assembled citation. That invariant only ever held because no entry set
print = false; the writer emits printable.
Fixed: k-point construction conventions are cited again (2026-07-10)¶
No production code ever passed numerics= to assemble(), so every row in
[routes.numerics] was unreachable from a real job. Only the unit tests, which
call assemble(numerics=[...]) directly, kept them looking alive. A run that
chose its mesh by the AFLOW KPPRA density convention, or plotted bands along the
HPKOT path, cited neither.
KPoints now carries a citation_numerics field naming the published
construction it came from, set by from_kppra (AFLOW, Curtarolo 2012),
generalized_regular, from_database, and band_path (HPKOT via seekpath,
Hinuma 2017). KPoints.to_kpath propagates it to KPath, so a job that attaches
a band structure carries its path convention. run_periodic_job forwards both to
assemble().
A hand-specified Monkhorst-Pack mesh or an explicit k-list follows no published density convention and now claims none; over-citing is as wrong as under-citing, and a negative-control test pins that.
Fixed: CCM adsorbate optimizer recovers from WS-basin trial failures (2026-07-10)¶
ccm_optimize now keeps relaxed atoms in their starting periodic image for
axis-aligned CCM cells, recovers from invalid Wigner-Seitz trial geometries with
a finite optimizer penalty, and returns a force-converged valid point when a
line search samples a lower-energy WS kink with large residual forces. The
MgO(100)+H2O slow relaxation regression now passes again with the adsorbate
force below the requested target.
Added: vibeqc.output.channel, the ambient output channel (2026-07-10)¶
Plumbing only. The .out file format does not change, verified by
diffing normalized .out captures from run_job (RHF and RKS),
run_periodic_job, and run_double_hybrid before and after the
migration: byte-identical.
Any module can now contribute to the .out file by importing write
and flush from vibeqc.output, instead of being handed the open file
object. Outside an active channel write() is a no-op, so importing the
module opens nothing and a writer stays callable from a test, a
notebook, or a library context.
from vibeqc.output import write, flush # any module, any depth
write(f" IUPAC name: {iupac}\n")
with OutputChannel.to_file(out_path): # the runner, once
...
OutputChannel offers file, stdout, tee, and null backends; files are
opened line-buffered so tail -f job.out shows lines as the SCF emits
them. Channels nest and restore the previous channel on exit, and the
channel duck-types as a writable stream, so it can be passed wherever a
file object is expected (run_geomopt(progress=...) relies on that).
runner.py, periodic_runner.py, and __init__.py’s
_write_double_hybrid_outputs no longer thread an f handle: 172, 152,
and 24 f.write(...) call sites respectively became write(...), and
the handle is gone from all three entirely. The double-hybrid writer had
opened its .out without buffering=1, so tail -f never worked on
those jobs; it is line-buffered now. The IUPAC naming block, which
was inlined in the SCF runner solely because that was the only scope
holding f, moved to vibeqc.naming.report.write_iupac_name and now
emits its own line. It lives under vibeqc.naming (which already
requires a built vibe-qc) rather than in the standalone vibeqc_naming
distribution, so a naming library does not acquire knowledge of
quantum-chemistry output channels.
The active channel is a contextvars.ContextVar, matching
structured_log._active / perf.active_tracker /
crash_dump.active_crash_dump_stem. A ContextVar does not reach a
worker started with threading.Thread; vibe-qc spawns no Python threads
(threading appears only as Lock, parallelism is OpenMP inside C++),
and tests/test_output_channel.py pins both that limitation and the
tree-wide premise behind it.
vibeqc.output.writer.OutputWriter, the manifest/plan coordinator, is
untouched.
Fixes a latent bug found while migrating. The NTO-molden failure warning
in runner.py called f.write(...) after the with open(out_path, ...)
block had closed, so f was a closed file object and the call raised
ValueError: I/O operation on closed file. The enclosing handler caught
that and reported it as a qvf_tddft_nto_prep failure, so the warning
never reached the .out and the real cause was masked. It now calls
warn_output_failure(..., role="nto_molden"), which needs no channel.
Added: vibeqc.output.document, semantic primitives for text output (2026-07-10)¶
First milestone of the unified output logger
(handovers/HANDOVER_OUTPUT_LOGGER.md). Purely additive: no call site
changed, no existing output moved a byte.
Quantity, FormatSpec, FormatPolicy, Column, Table, and
OutputDocument let a module emit what a number is (a value, its kind,
its unit) instead of a pre-formatted string, and let a policy decide how
it renders. Table computes its own column widths and rule length,
retiring the hardcoded "-" * 52 / "-" * 54 / "-" * 56 / "-" * 70
rules that four writers each guessed differently for the same table.
Precision and units are configurable with meaningful defaults. Units are
chosen per physical dimension and precision per kind, so
DEFAULT_POLICY.with_unit("energy", "eV") moves total energies and SCF
energy changes together and a table cannot render a Hartree dE column
beside an eV energy column. Defaults reproduce the format the tree
already used most often (energy at 18.10f), so adopting the layer is
not simultaneously a restyle.
Also pins, in tests/test_cpp_emits_no_user_output.py, the invariant the
C++ core has always satisfied by accident: it emits no user-facing
output. All 231 sources under cpp/ contain exactly one output call, a
verbosity-gated fprintf(stderr, ...) eigensolver residual in
lobpcg.cpp. Formatting stays in Python, where a FormatPolicy can
reach it; a stray std::cout in a new kernel now fails CI.
Migrating runner.py / periodic_runner.py onto this layer, and the
resulting .out format change, land in a later milestone.
Fixed: vibe-view trusted a contradictory dimensionality over pbc (2026-07-10)¶
be7a9f4a made dimensionality == sum(pbc) a normative QVF invariant and
enforced it in both reference writers and in validate_qvf. The vibe-view
reader was never updated to match: _resolve_pbc took pbc for the axis flags
but returned the archive’s raw dimensionality beside it, so an archive that
disagreed yielded a self-contradictory (pbc, dim) pair rather than an error.
dim is now derived from pbc rather than read alongside it, and a
contradictory payload raises QVFError. Nothing in the payload can adjudicate
which of the two fields is the lie, and guessing is how a 2D slab came to be
drawn inside a phantom vacuum box in the first place.
Also pins that the reader preserves non-contiguous pbc. [true, false, true] is a legal slab periodic in x and z, which the spec explicitly permits;
dimensionality counts the periodic axes but cannot name them, so reconstructing
the axes from dim=2 would draw the a,b plane instead of a,c.
The PeriodicSystem comment and pybind docstring no longer call the synthesized
lattice columns “vacuum directions”. They are non-physical bookkeeping that keeps
the matrix full-rank; the energy is invariant to their length, and calling them
vacuum is what invited readers to draw them.
Changed: BIPOLE RKS/UKS no longer run the XC quadrature twice per iteration (2026-07-11)¶
Every BIPOLE RKS and UKS SCF iteration ran the full periodic DFT-grid
quadrature twice on the same density: once inside the Fock build (for
V_xc, its e_xc discarded) and once in the loop body purely to extract
e_xc. The Fock-build bundle now carries the e_xc of the same
quadrature whose V_xc entered F(k), and the loop consumes it. Besides
removing one grid pass per iteration (material for pure functionals,
where the ERI build is J-only), this ends the small internal
inconsistency of V_xc and e_xc coming from two independent multithreaded
quadratures. Byte-check vs the M-series protocol: PASS, worst drift
1.7e-13 (the documented RKS XC/BLAS run-to-run noise floor); the
post-convergence final-energy recompute is untouched.
Fixed: BIPOLE nuclear-cutoff clamp now applies to all four direct drivers (2026-07-11)¶
9ea2651d fixed a mHa-scale molecular-limit overbinding (nuclear
point-charge tails on Ewald cells whose compensating electronic charge is
truncated away) by clamping nuclear_cutoff_bohr down to cutoff_bohr
under the 3-D Ewald-J split — but only in the RKS driver body and in
run_periodic_job. Direct callers of run_pbc_bipole_rhf/uhf/uks (the
FD-gradient path, validation scripts) kept the unclamped library defaults
(electronic 15.0 vs nuclear 25.0 bohr) and silently disagreed with the
runner route and with RKS on identical physics. The clamp is centralised
as pbc_bipole_common.clamp_bipole_nuclear_cutoff and applied by all four
drivers; run_periodic_job results are unchanged (the runner already
clamped). New tests/test_pbc_bipole_cutoff_coherence.py pins the
resolved cutoff per driver (verified to fail on the pre-fix drivers) plus
the converged closed-shell UKS==RKS reduction at default cutoffs.
Fixed: docs still told users the writer emits qvf_version: 2 (2026-07-10)¶
The qvf_version: 2 withdrawal landed the schema, writer, governance ruling and
registry, but four doc surfaces still described the bump as current behaviour:
docs/tutorial/qvf_file_format.md (§ manifest root and § 8),
docs/user_guide/vibe_view.md (periodic reaction paths), and
docs/user_guide/relaxed_scan.md (periodic scans) each told the reader that a
periodic reaction.path ships as qvf_version: 2. They now say what the writer
actually does: the archive stays v1 and a consumer detects a periodic path by the
lattice member’s presence. docs/design_qvf_format.md also gains the
structure payload’s periodicity contract, mirroring spec § 5.1.
Added: coverage closing the BIPOLE bz_integration callsite gap (2026-07-10)¶
Commit 891acead (first shipped in v0.15.23) put the new bz_integration
keyword on the wrong callsite in periodic_runner.py: every RHF +
jk_method="bipole" + multi-k run_periodic_job raised
TypeError: run_pbc_bipole_rhf() got an unexpected keyword argument 'bz_integration' (without the user passing the keyword), and BIPOLE RKS
accepted bz_integration="gilat" but silently dropped it — the advertised
Gilat feature was inert. Both defects were fixed by 38c4afed (v0.15.31),
but they shipped in eight consecutive tags (v0.15.23..v0.15.30) because no
test drove RHF + bipole at a real multi-k mesh on a 3-D cell through the
public route, and the Gilat-forwarding test asserted only that a result
existed.
This entry adds the missing coverage (no runtime code changes; the code fix
already shipped): a live RHF + bipole + k=(2,1,1) regression test through
run_periodic_job; a static contract test binding every run_pbc_bipole_*
callsite’s keywords against the driver’s inspect.signature (catches the
whole swapped-callsite defect class without running an SCF); a strengthened
test_runner_gilat_bipole_rks_path_runs that asserts the resolved scheme
actually reaches run_pbc_bipole_rks; and a pin on the BIPOLE UHF/UKS +
gilat fail-closed NotImplementedError. All new tests were verified to
fail against v0.15.30’s periodic_runner.py/pbc_bipole.py and pass on
main. The full provenance record, including the verification probe and
the manuscript-impact analysis (none — both published BIPOLE rows were
produced at 5a1a0b97, outside the window), is BUG-PER-002 in
handovers/HANDOVER_OPEN_BUGS_V015.md.
Added: QVF specifies the structure payload’s periodicity contract (2026-07-10)¶
A consumer could not assert that a QVF structure was a 2-D slab, only infer
it. pbc, lattice_vectors, and dimensionality were prose-only
(qvf-format-spec.md § 5.1), and the prose made dimensionality a MAY on
the section object while the vibe-qc producer wrote it into the payload. So
vibe-view carried a fallback (_resolve_pbc, f15094ee): when pbc was
missing it assumed the periodic axes were the leading ones. Guessing wrong
renders a slab inside a vacuum box, or tiles a density isosurface along the
slab normal.
The structure member’s payload is now schema’d, as $defs/StructurePayload in
qvf-writer/spec/qvf_manifest.schema.json. It is QVF’s first payload schema —
until now only manifest.json was schema-validated and JSON members merely had
to parse. The contract:
pbcis the normative carrier of per-axis periodicity, and is REQUIRED wheneverlattice_vectorsis non-null. Absentpbcmeans a molecule.dimensionalityis derived, with the invariantdimensionality == sum(pbc), and carries no axis identity.A lattice row whose axis is aperiodic is non-physical bookkeeping — present so the matrix stays full-rank 3×3, never a cell edge. A synthesized 30-bohr normal and a real 30-bohr vacuum gap are numerically identical, so
pbcis the only signal that separates them.
The periodic axes are deliberately not required to be the leading ones. That
ordering is a vibe-qc internal convention (cpp/include/vibeqc/periodic.hpp);
making it normative would have made a slab with its normal along y
(pbc = [true, false, true]) non-conforming, at odds with QVF’s producer
neutrality. It survives as an informative note describing the legacy
dimensionality-without-pbc fallback.
No qvf_version bump: absent pbc had no defined meaning, every known producer
already emits it, and every archive in the conformance corpus and in
docs/_static/examples/ validates unchanged. Ruling recorded in
qvf-writer/GOVERNANCE.md § “Version history and decisions”.
Enforcement, so the invariant is not dead prose: qvf_reader.validate_qvf
checks the contract semantically (no jsonschema needed) and structurally
against $defs/StructurePayload when jsonschema is installed; spec § 6’s
validator checklist and § 8’s producer/consumer conformance lists say so. Both
reference writers now reject a periodic pbc with no lattice, and the Python
one additionally rejects dimensionality != sum(pbc) and writes
dimensionality into the payload rather than onto the section object. New
conformance vector structure_slab_2d.qvf (graphene, pbc = [true, true, false], dimensionality = 2, third lattice row a vacuum-gap-sized 15.875 Å
normal) fails any consumer that reads periodicity off lattice geometry.
Verified: vibe-qc’s _write_structure_section conforms for dim 0/1/2/3; all 10
archives under docs/_static/examples/ still validate; the derived
qvf_manifest_v2.schema.json was regenerated from the new v1.
Fixed: KS direct-torus route catches up with the dim<3 fail-closed correction (2026-07-10)¶
The v0.15.32 low-dimensional Coulomb-gauge correction made every dim < 3
3-D-torus route fail closed, but run_ccm_rks_direct and its gates were
missed by that sweep:
run_ccm_rks_directnow calls the explicit_reject_low_dimensional_directguard early, exactly likerun_ccm_rhf_direct— refusing adim < 3cell with the curated message (wire-kernel pointer, gauge algebra) under its own name, instead of failing later inside the mesh builder with anrsgdf_dense_g_mesh-named error. The guard’s message now names the calling driver (whoparameter).dim = 3is bit-unchanged.tests/test_ccm_rks_direct.pycarried three staledim=1chain fixtures. The multi-k dim<3 KS parity gate asserteddirect == GDFon the collapsed kernel — mutual agreement between two routes sharing a defect (the same trap9e6bbaf2removed from the KRKS pins) — and is converted totest_rks_direct_and_gdf_multik_lowd_both_fail_closed. The a_x-scaled seam-identity and route-dispatch tests used the chain incidentally and move to the compact 3-D cell, mirroring their HF twins intest_ccm_direct.py.
Pins: tests/test_ccm_rks_direct.py (11 passed; the fail-closed gate
requires NotImplementedError from both run_ccm_rks_direct and
run_ccm_rks_gdf on a dim=1 chain).
Fixed: hse-3c used PBEh-3c’s gCP fit constants (2026-07-10)¶
The other half of the entry below. run_job(method="hse-3c") also computed
the wrong geometrical-counterpoise correction, because python/vibeqc/gcp.py
keyed gCP parameters by basis while gCP’s four fit constants are properly
per (method, basis) pair, exactly as in gcp.f90, which selects them on
the method. PBEh-3c, B3LYP-3c and HSE-3c all sit on def2-mSVP, so HSE-3c
silently inherited PBEh-3c’s fit.
The sources for HSE-3c’s own constants conflict, and the conflict is now resolved in favour of the paper:
Table S1 of the ESI to Brandenburg, Caldeweyher & Grimme, Phys. Chem. Chem. Phys. 18, 15519 (2016), doi:10.1039/c6cp01697a:
sigma = 1.00000(constrained),eta = 1.40858,alpha = 0.29083,beta = 1.95260.gcp.f90case ('hse3c'):eta = 1.32378,alpha = 0.28314,beta = 1.94527. Identical in classicgcpv2.02 (Nov 2016), in Holger Kruse’s fork, and in every commit of thegrimme-lab/gcprework since its first. Not a documented refit.
The paper wins because the paper can be checked against itself. Its ESI
publishes a numerical example (section B, Table S2): gas-phase benzene at the
geometry of the accompanying CRYSTAL14 input has E_gCP = 0.0148400663 a.u.
Evaluated against vibe-qc’s bundled def2-mSVP e_mis / n_virt tables with
the damped form:
constants |
E_gCP(benzene) / a.u. |
error |
|---|---|---|
Table S1 ( |
0.0148400665 |
+2e-10 Ha |
|
0.0147289535 |
-1.11e-4 Ha (-0.070 kcal/mol) |
PBEh-3c ( |
0.0152811330 |
+4.41e-4 Ha (+0.277 kcal/mol) |
Only Table S1’s constants reproduce the authors’ own published number, so
those are the constants HSE-3c was defined and benchmarked with. The exact
agreement also confirms the bundled per-element tables are the ones the
authors used. Consequence: vibe-qc’s hse-3c now matches the published
method and CRYSTAL (whose self-contained internal gCP produced that number),
and differs by ~0.07 kcal/mol of gCP on benzene from any code calling
Grimme’s gcp binary (ORCA, the conda gcp package). The discrepancy is
acknowledged upstream and unresolved:
grimme-lab/gcp issue #17,
where a maintainer states the intent is to move the code to the paper’s
values. On H2O, E_gCP moves from +0.8248 to +0.8399 kcal/mol.
The keying change: data_library/gcp/*.toml gains an optional
[variants.<method>] block overriding any of the four fit constants while
reusing the file’s [elements.*] tables verbatim; CompositeRecipe gains
gcp_variant; vq.gcp_params_for / vq.compute_gcp gain variant=; and
vq.available_gcp_variants(basis) lists them. Only hse-3c registers a
variant. A variant= naming an unregistered block raises ValueError
rather than falling back to the basis-level parameters, because a silent
fallback is precisely how this bug arose (and because the runner degrades
gracefully on GCPDataMissing, which would have hidden it). b3lyp-3c
still deliberately reuses PBEh-3c’s constants undamped; it has no 3c paper
of its own.
Pins: tests/test_composite_integration.py::test_hse_3c_gcp_matches_published_benzene
(asserts the four constants against ESI Table S1 and reproduces ESI Table
S2’s benzene value to 1e-9 Ha, and checks that both rejected constant sets
fail to reproduce it), plus
::test_gcp_unknown_variant_raises_rather_than_falling_back and
::test_pbeh_3c_and_b3lyp_3c_keep_the_basis_level_gcp_constants.
Any hse-3c absolute energy or lattice energy produced through v0.15.x is
affected and should be recomputed.
Fixed: hse-3c used PBEh-3c’s D3(BJ) damping constants (2026-07-10)¶
run_job(method="hse-3c") computed the wrong dispersion correction. The
recipe in python/vibeqc/composites.py carried a1=0.4860, a2=4.5000,
which are PBEh-3c’s D3(BJ) damping constants, on the assumption that
HSE-3c was a community extension with no published damping of its own.
It is not: HSE-3c is published as Brandenburg, Caldeweyher & Grimme,
Phys. Chem. Chem. Phys. 18, 15519 (2016),
doi:10.1039/c6cp01697a, and Table S1
of its ESI gives HSE-3c’s own constants, re-fit on S66x8:
s6 = 1.00000, s8 = 0.00000 (both constrained), a1 = 0.44110,
a2 = 4.51820. Screening the long-range Fock exchange changes the
medium-range correlation the damping has to absorb, so the two composites
do not share a damping fit even though they share the def2-mSVP basis and
the short-range potential.
The correction is energy-affecting, not cosmetic. The ESI publishes a
benzene numerical example (ESI section B, Table S2) whose gas-phase D3
energy is -0.0068628129 a.u. Evaluating that geometry with simple-dftd3
under the paper’s own D3 specification (BJ damping plus the ATM three-body
term) reproduces the published value to 1e-10 Ha with the corrected
constants, and gives -0.0058906668 a.u. with the constants that shipped:
an error of +0.97 mHa (+0.61 kcal/mol) on a single benzene molecule. The
ATM term is common to both, so the gap is entirely the damping fit. In
vibe-qc itself the switch moves energy_total for hse-3c on H2O by
-6.44e-05 Ha (-0.040 kcal/mol). Any hse-3c interaction or lattice energy
produced through v0.15.x is affected and should be recomputed.
(vibe-qc’s own D3 has no ATM term at all, which is a separate pre-existing
gap that also affects pbeh-3c; cpp/include/vibeqc/dispersion.hpp records
it as not yet implemented.)
Pins: tests/test_composite_integration.py::test_hse_3c_d3bj_damping_matches_published_esi,
which asserts the four published constants against ESI Table S1 and
guards them apart from PBEh-3c’s.
One adjacent item is deliberately untouched: the b3lyp-3c recipe genuinely
is a community extension with no published 3c paper, so its inherited
constants stay. HSE-3c’s gCP parameters were the other half of this bug and
are fixed in the entry above.
Changed: the GAPW / GPW / RSGAPW drivers extrapolate through the canonical DIIS (2026-07-10)¶
The run_periodic_*_gapw / _gpw / _rsgapw family carried the fifth
hand-rolled numpy Pulay DIIS, and unlike the three in scf_acceleration.py
(removed below) this one was live and reachable. Six call sites across
periodic_gapw_j.py, periodic_gapw_augment.py, periodic_gapw_open_shell.py
and periodic_gapw_range_sep.py appended to a private history, rebuilt the
bordered Pulay B-matrix in a Python double loop and re-derived the solve,
against CLAUDE.md §10’s single-canonical-implementation rule.
All six now reach vibeqc::DIIS (cpp/src/diis.cpp) through a new _make_diis
helper: the three closed-shell drivers via DIIS.extrapolate, the three
open-shell drivers via DIIS.extrapolate_spin_coupled. The mapping is exact,
not approximate. For the real symmetric error matrices these drivers build,
np.sum(e_i * e_j) is the Frobenius product Tr(e_iᵀ e_j) the kernel
accumulates in its Gram matrix; and stacking α on β makes the kernel’s single
B-matrix accumulate Tr(e_α_iᵀ e_α_j) + Tr(e_β_iᵀ e_β_j), termwise the joint
B-matrix the open-shell sites built by hand. Net −117 lines.
No behaviour change on the healthy branch: the extrapolated Fock agrees with the
retired build to 1e-11 over a converging 10-iterate history. Two differences are
deliberate. The kernel carries the coefficient blow-up guard (a non-finite
solve, or coefficients above 1e6, drops the oldest iterate and re-solves) where
the numpy code caught only an exactly singular np.linalg.solve and skipped
extrapolation for that iteration. And diis_subspace_size < 2, which the numpy
history degenerated to a silent no-op, now disables DIIS explicitly rather than
reaching the kernel, whose constructor rejects a subspace it cannot extrapolate
from. The DIIS error vector is still built from the D that produced F, as
tests/test_diis_error_vector_source.py pins.
This retires the duplication; it does not widen the accelerator surface. The
family still takes use_diis / diis_subspace_size / diis_start_iter and not
scf_accelerator, so EDIIS, ADIIS, KDIIS and the adaptive-depth policies remain
unavailable on it. Those are separate classes (cpp/src/ediis.cpp,
cpp/src/kdiis.cpp) needing an energy history or an MO-basis orbital gradient
that DIIS.extrapolate never sees, so routing does not grant them for free.
docs/user_guide/scf_convergence.md now states that narrower exception (the
family shares the kernel, not the keyword) in place of the retired one.
Tests: new tests/test_gapw_diis_kernel.py (8) pins the kernel against the
retired numpy build in both shapes, pins that open-shell spin coupling is one
joint history rather than two independent ones, and guards mechanically that no
hand-rolled Pulay solve returns to the family.
Removed: dead scf_acceleration.py module (2026-07-10)¶
python/vibeqc/scf_acceleration.py is deleted. Its three classes were
unreachable: not exported from __init__.py, reached by no static or
dynamic import, and covered by no test. AndersonAccelerator and
BroydenAccelerator were superseded in v0.14.0 by the tested, wired
AndersonMixer / BroydenMixer in periodic_density_mixing.py (the
latter without the orphan’s n_occ = D_new.shape[0] // 2 occupation
guess). SmearingAwareDIIS was a fourth hand-rolled numpy Pulay DIIS,
in violation of CLAUDE.md §10’s single-canonical-implementation rule,
and had never executed: its extrapolate raised ValueError out of
np.trace on a raveled 1-D error vector on the first call that reached
a subspace of 2. Its smearing-aware error vector was not ported, for two
reasons. The canonical vibeqc::DIIS::extrapolate(fock, error) already
accepts the error vector as a caller argument, so a driver that wants a
density residual can pass one with no C++ change; and the class’s stated
rationale does not hold, since the commutator error FDS - SDF already
vanishes at the SCF fixed point under fractional occupations (D = C f C^T
with F C = S C eps gives FDS - SDF = S C (eps f - f eps) C^T S = 0, as
eps and f(eps) are both diagonal). No behaviour change: no shipped code
path referenced the module. kerker.py, DynamicDamping, and the multi-k
accelerators in periodic_scf_accelerators.py are untouched and live.
Fixed: MSINDO CCM FD gradients no longer leak native NaNs (2026-07-10)¶
ccm_gradient_fd now accepts the C++ finite-difference fast path only when it
returns the expected (n_atoms, 3) shape with all finite entries. If the native
path returns NaNs, as it did for the 3-D MgO NOEWALD fixed-WS displacement set,
the public API falls through to the validated Python fixed-WS reference instead
of returning a partially NaN gradient. This retires the
tests/test_msindo_ccm_gradient.py known-red while keeping the native dim=3
non-Madelung FD branch on the semiempirical C++ performance follow-up list.
Pins: tests/test_msindo_ccm_gradient.py and
scripts/test_gate/known_reds_baseline.json.
Fixed: semiempirical OVGF now cites its own method papers (2026-07-10)¶
msindo_ovgf computes NDDO/INDO GF2-OVGF ionization potentials but emitted no
method citation, and danovich_ovgf_1997 was reachable from nothing. The
existing routes.methods.ovgf row could not cover it: that row keys off
method="ovgf", the ab-initio run_job path, while the semiempirical route
reports method="msindo".
msindo_ovgf now takes the same optional output= argument as msindo_cisd,
and writes .bibtex / .references naming Danovich 1997 (NDDO + Green’s
function) over the OVGF formalism of Cederbaum 1975 and von Niessen 1984,
alongside the MSINDO papers. Both the closed-shell and open-shell (UOVGF) return
paths emit. output=None remains the default, so existing calls are unchanged.
Removed: redundant CCSD citation scuseria_schaefer_ccsd_1988 (2026-07-10)¶
The entry claimed “cite when CCSD is used”, but no route reached it and nothing in
the CC code derives from it. cpp/include/vibeqc/ccsd.hpp states that the
closed-shell spatial equations are the spin integration of Stanton-Gauss-Watts-
Bartlett 1991, with Purvis-Bartlett 1982 as the original-CCSD reference; both are
already routed for ccsd / ccsd(t) / ccsd[t]. Scuseria-Janssen-Schaefer’s
reformulation is not the one implemented, so citing it would have misattributed
the working equations.
Changed: daga_bdiis_2020 merged into daga_optbasis_2020 (2026-07-10)¶
The two keys were the same paper (Daga, Civalleri & Maschio, JCTC 16, 2192
(2020), doi:10.1021/acs.jctc.9b01004) under two entries, so a BibTeX file that
reached both would have carried it twice. daga_optbasis_2020 survives, reached
both statically from routes.acceleration.optbasis and directly through
BDIIS_CITATION_KEYS; res.citations from a basis optimisation now reports that
key. The citation database has no duplicate DOIs left and
_KNOWN_DUPLICATE_DOIS is empty.
The merge had been deferred on the belief that it touched the basissetdev branch.
It does not: python/vibeqc/basis_optimization/ and tests/basisset_dev/ are
both tracked on main — the on-main directory is not the basissetdev branch.
Removed: four citation entries for basis sets that do not ship (2026-07-10)¶
zheng_madef2_2011 (ma-def2), franzke_x2c_2020 (x2c-QZVPall),
ye_berkelbach_gth_2022 (GTH-cc-pVXZ) and vandevondele_molopt_2007 (MOLOPT)
each claimed a shipped basis family, but no matching .g94 exists in the bundled
library and none was routed, so no job could ever have cited them. The x2c family
that does ship is TZVPall, already routed to pollak_weigend_x2c_2017. Per
CLAUDE.md § 8, whoever lands one of these bases adds its entry and route in the
same commit.
Dropping vandevondele_molopt_2007 also resolves a duplicate-DOI pair: it shared
doi:10.1063/1.2770708 with vandevondele_basisopt_2007, which survives (it is
surfaced by BDIIS_CITATION_KEYS) and whose note now records both roles of the
paper. assemble() deduplicates by entry key rather than by DOI, so routing both
would have printed one paper twice.
Fixed: rsgdf_dense_g_mesh clipped the G-ball on non-symmetric lattice matrices (2026-07-10)¶
The dense-mesh index-box bound |n_i| <= G_max |a_i| / 2pi was computed
from ROW norms of the lattice matrix while vibe-qc lattice matrices carry
the vectors in COLUMNS (the same function’s b = 2pi inv(a).T and
G = idx @ b.T already use the column convention). For symmetric matrices
(the fcc/bcc primitive cells where every historical control lived) rows ==
columns and nothing was wrong; for a non-symmetric matrix — any (n,1,1)
supercell of fcc, hexagonal cells, triclinic cells — the row norm
underestimates |a_i| and the box silently clipped the |G| <= G_max
ball: measured 602 of 3381 points (18%) missing on the C-diamond (2,1,1)
cluster cell at ke_cutoff = 60. Consequences on affected cells: every
all-FT fit (build_lpq_bloch_native_fft / build_lpq_native_fft) was
under-converged relative to its declared ke_cutoff, the exact
base + tail == big-mesh identity of the tail completion could not hold
(clipped points fell in neither base nor tail), and the mesh lost its
point-group closure (caught 2026-07-10 by the space-group pair-reduction
covariance gate at 4e-3, ke-dependent and lattice-cutoff-resistant).
Pins: tests/test_ccm_symmetry.py::test_dense_g_mesh_is_complete_ball_on_nonsymmetric_lattice
(+ the diamond (2,1,1) parity regression), existing
tests/test_rsgdf_dense_mesh_tail.py re-verified. Energies on
symmetric-matrix cells are bit-unchanged; on non-symmetric cells they move
toward the converged values their ke_cutoff always claimed.
Patch-candidate: v0.15.x
Fixed: analyze_ccm_symmetry admitted ops that break the BvK torus (2026-07-10)¶
Cluster invariance was tested by atom positions alone
(atom_permutation_under_op modulo the cluster lattice) — but that test
passes for EVERY crystal op regardless of cluster shape, because every
image site is congruent to some cluster site mod the cluster lattice. Ops
whose rotation does not map the cluster lattice onto itself (36 of the 48
fcc ops for nrep = (2,1,1)) are not symmetries of the torus: with them
admitted, even the S^CCM “invariance” residual was 4.7 on C-diamond
(2,1,1). analyze_ccm_symmetry now additionally requires the exact
integer condition W_ij n_j / n_i ∈ Z (the op normalizes the BvK
lattice); equal-nrep cubic clusters keep all 48 ops as before, the
(2,1,1) fcc cluster correctly keeps 12. Prior consumers only ran
equal-nrep or orthorhombic-chain clusters, which the condition leaves
unchanged. Pin:
tests/test_ccm_symmetry.py::test_symmetry_reduced_cderi_diamond_multicell_regression.
Added: space-group pair reduction of the Γ-supercell neutral cderi (2026-07-10)¶
ccm_neutral_cderi(..., symmetry=True | CCMSymmetry) fits only the
space-group orbit-representative atom-pair AO blocks (through the GDF fit
builder’s fit_pair_list shell-pair seam, composing with the Cauchy-Schwarz
fit screen in one build path) and reconstructs every other pair block by the
group action, using the Γ canonical-frame covariance
L[:, gμ, gν] = P_aux^(g) L[:, μ, ν]. The G-ball, Coulomb kernel, per-shell
calibrations, and 2c metric are exactly point-group invariant (measured
2e-14); the only non-invariant ingredient is the Bloch cell-list truncation
tail (8e-9 relative at the 15-bohr default on C-diamond sto-3g, 6e-16 at
25 bohr), plus a ~2e-10 whitening-amplification floor confined to near-null
aux directions. Physical parity is exact for practical purposes:
reduced-vs-unreduced direct-torus SCF energy diff 5.5e-13 Ha at production
defaults (pinned < 1e-10); tensor-level parity pinned < 1e-9
cell-list-converged. Supporting infrastructure: CCMPairOrbits now carries
the full orbit map (orbit_rep/orbit_op), and
ccm_symmetry_basis_rotations builds the op rotation matrices over any
supercell-centered basis (used for the modrho auxiliary basis). |G_c| = 1
(P1/triclinic) falls back to the full build with no reduction claim
(canonical aux frame either way); the reduction factor is logged. Gates:
tests/test_ccm_symmetry.py (20 green). The petite-list reduction of the
fold / multi-k k-pair loop is the next milestone
(handovers/HANDOVER_SYMMETRY_PAIR_REDUCTION.md).
Fixed: OVGF jobs are covered by memory preflight again (2026-07-10)¶
estimate_memory(..., method="ovgf") now has a real dense GF2/OVGF estimator
instead of raising ValueError("unknown method 'ovgf'"). The estimate charges
the dense AO ERI tensor, closed-shell MO ERI transform/work tensors, and the
open-shell spin-orbital tensor path used by run_job(method="ovgf"), including
the small-system renormalized GF2 diagnostic. run_job also forwards the
reference SCF options into this estimate, so direct-SCF and related reference
settings still influence the preflight block.
Pins: tests/test_memory.py and tests/test_runner_ovgf.py. The resolved
tests/test_runner_ovgf.py known-red entry was removed from
scripts/test_gate/known_reds_baseline.json.
Changed: the Saunders-Hillier shift operator now has one implementation (2026-07-10)¶
The level shift was applied at eleven hand-rolled call sites (seven C++, four
Python), each spelling out F + b·S − w·(S·D·S) and each carrying its own copy
of the weight w. That is why run_uks_periodic_gamma_ewald3d could sit on the
wrong w indefinitely (see the entry below).
cpp/include/vibeqc/level_shift.hpp now owns the operator as well as the
schedule: apply_level_shift (real) and apply_level_shift_k (per-k
Hermitian), with the weight selected by a LevelShiftDensity enum, SPIN
(weight 1) or TOTAL (weight ½). Both are bound to Python, and all eleven
sites route through them. docs/user_guide/scf_convergence.md already
documented the rule; nothing enforced it.
Every trajectory is unchanged. A fixed-iteration trace was captured on all
fourteen affected code paths before and after, under a persistent shift with
DIIS and dynamic damping off: iteration counts are identical everywhere, two
traces are bit-identical, and the worst energy deviation is 9.9e-14 Ha
(1.3e-15 relative), which is floating-point reassociation, not a changed path.
The b == 0 fast path is preserved: the helper returns the Fock untouched, and
the Python call sites keep an explicit guard so an unshifted cycle does not
marshal S and D across the binding at all.
Guarded by test_no_driver_open_codes_the_shift_operator, which greps the SCF
sources for a hand-written S·D·S on a line that also names a shift magnitude.
A convention test alone could not have caught the original bug, because it does
not bind the drivers to the shared operator.
Fixed: Γ-Ewald UKS applied half the requested level shift (2026-07-10)¶
run_uks_periodic_gamma_ewald3d built its Saunders-Hillier shift as
F_s + b·S − (b/2)·S·D_s·S from the spin densities D_alpha / D_beta.
Those are idempotent in the overlap metric (D_s S D_s = D_s), so S D_s S
is exactly the projector onto the occupied manifold and the coefficient must
be 1: occupied eigenvalues stay put, virtuals rise by b. With b/2 the
occupied block rose by b/2 too, so the gap opened by b/2 instead of b.
Only the closed-shell drivers, which carry the total density D = 2P,
legitimately use b/2.
The SCF still converged to the same fixed point (a uniform shift within the
occupied subspace cannot rotate the occupied orbitals), which is precisely
why no energy assertion could catch it, and why it survived: this driver had
no level-shift test of any kind. Its multi-k sibling
(periodic_uks_multi_k_ewald.py) and the molecular cpp/src/uks.cpp both
already used the correct coefficient, so a given level_shift was half as
strong on this one backend as on every other open-shell route.
Pinned by two new tests in tests/test_periodic_level_shift.py: the missing
Γ-Ewald UKS honoured/inert pair, and test_level_shift_operator_convention,
a driver-free linear-algebra test asserting that the coefficient is 1 for a
spin density and 1/2 for a total density, and that in both cases the
occupied eigenvalues do not move and every virtual rises by exactly b.
Added: guard that every SCF driver builds its DIIS error from D_in (2026-07-10)¶
e = F D S − S D F vanishes identically whenever D is built from the
eigenvectors of that same F, for any occupations, fractional or integer
(F D S − S D F = S C (ε f − f ε) Cᵀ S = 0, since ε and f(ε) are both
diagonal). A driver that forms its DIIS error from D_out rather than the
D_in that built F therefore gets a zero error vector on every iteration:
its DIIS silently degenerates into a no-op, the SCF still converges on the
energy criterion, and every energy-only test passes.
tests/test_diis_error_vector_source.py pins the commutator norm non-zero on
early iterations for the C++ Γ and multi-k drivers, the Python Γ-Ewald
RHF/UHF/RKS drivers, the Γ GDF driver and the multi-k Ewald driver. All of
them were already correct; the guard keeps them that way. The probe cell is
H2/6-31G, since minimal-basis H2 has symmetry-fixed MOs and reports a ~1e-16
commutator even when the driver is healthy.
The same file pins the corollary that the commutator error does not
degrade under smearing: at a self-consistent density with Fermi-Dirac
fractional occupations the commutator is as zero as with integer ones. A
density-residual error vector is not needed to make DIIS well-defined at
T > 0.
Changed: GAPW / GPW drivers documented as outside the unified accelerator surface (2026-07-10)¶
docs/user_guide/scf_convergence.md claimed every Pulay-family extrapolation
in vibe-qc runs on the single vibeqc::DIIS kernel. That is true of every
backend reachable through scf_accelerator, but not of the
run_periodic_*_gapw / _gpw / _rsgapw family, which takes its own
use_diis / diis_subspace_size / diis_start_iter keywords and keeps a
private numpy Pulay history. The claim is now scoped to the coverage matrix,
and the exception is stated explicitly rather than left for a reader to
discover.
Fixed: QVF v2 manifest schema is now derived from v1, not forked (2026-07-10)¶
qvf_manifest_v2.schema.json was a hand-copied snapshot of the v1 schema and had
silently fallen behind it. Its own description claimed v2 “differs from v1 only
in that SectionReactionPath carries the per-frame lattice + dimensionality”; in
fact v2 was missing ten section kinds (bond_orders, spectra.epr,
volume.rdg, volume.potential, basis.ao, fermi_surface, phonon.bands,
phonon.dos, equation_of_state, topology.qtaim), the four root blocks
(thermochemistry, dipole_moment, constraints, extensions), the
Section.critical flag (QVF spec § 5.5), and the bands fat-band projections
member.
Because the manifest root sets additionalProperties: false and Section is a
closed oneOf, this was not a documentation bug. write_qvf bumps a manifest to
qvf_version=2 whenever a periodic reaction.path is present, then validates
the archive it just built against the v2 schema. Any periodic reaction path that
also carried one of the sections or root blocks above therefore raised at
write time (write_qvf produced an archive that fails canonical validation -- this is a writer bug) rather than merely failing a later read. A periodic NEB path
plus a bond_orders section, or plus a root thermochemistry block, could not
be written at all.
v2 is now derived from v1 at load time by _derive_v2_schema, which applies the
one documented delta: $id, title, description,
properties.qvf_version.const, the optional lattice member on
SectionReactionPath, and the new $defs.ReactionPathLattice. The runtime
validator never reads the v2 file, so it cannot drift; a section added to v1 lands
in v2 for free. The on-disk qvf_manifest_v2.schema.json is retained as a
generated artefact for external validators resolving its $id, and is
regenerated by scripts/gen_qvf_v2_schema.py (--check verifies without
writing).
An earlier fix (cb6eaffc) had patched the same class of bug by hand-copying the
two dos.* definitions into the fork, which addressed the symptom and left the
cause. Guarded now by tests/test_qvf_v2_schema_drift.py, which pins “v2 == v1
modulo the documented delta” structurally rather than by re-running the
derivation, and by a regression test building a qvf_version=2 archive that
carries a periodic reaction.path, a bond_orders section, and a root
thermochemistry block.
Open governance question, referred to the qvf-writer chat and deliberately not
decided here: qvf-writer/spec/qvf-format-spec.md § 3.1 still says qvf_version
MUST be 1 and that a v1-only consumer MUST refuse anything higher, while the
reference producer emits 2. qvf-writer/GOVERNANCE.md § Versioning policy
further says qvf_version changes only on a breaking change, and that new
optional members are additive within a major version — which describes the
lattice member exactly. Whether v2 should be documented in the public spec, or
the bump withdrawn in favour of an additive optional member on v1, is for the
qvf-writer chat to rule on.
Added: GDF fit screening on the Γ-point drivers (2026-07-10)¶
fit_screen_threshold (the Cauchy-Schwarz screen of the three-centre
GDF fit, previously multi-k rsgdf only) now also works on the Γ-point
drivers run_pbc_gdf_rhf / run_pbc_gdf_uhf / run_pbc_gdf_uks and on
the Γ (1,1,1) fast path of run_krhf_periodic_gdf (which previously
raised NotImplementedError). On the Γ path a positive threshold
selects the memory-lean build: build_lpq_native_fft delegates to the
Bloch builder at k = 0 (one screened build path — the M1 screen and M2
G-chunked sweep come with it) and the Ewald-3D V_ne FT streams its own
chunked AO-pair FT (compute_v_ne_ewald_3d_ft_gamma(stream_pair_ft=True)),
so the dense (n_ao, n_ao, n_G) pair-FT tensor — the dominant memory of
a Γ GDF build — is never materialised. Conflicting combinations
(precomputed pair_ft_shared bundle, tail_pair_ft_screen, non-rsgdf
fit methods, Γ meshes that fall back to the legacy molecular-limit
driver) raise loudly instead of silently ignoring the option. New pins in
tests/test_gdf_fit_screening.py (Γ four-center parity, dropped-pair
zeroing, conflict errors, Γ fast-path energy invariance, plus slow
c-diamond + MgO multi-k energy-invariance gates and a sub-quadratic
significant-pair-count scaling gate) and
tests/test_periodic_v_ne_ewald_ft.py (streamed-vs-dense V_ne parity).
See handovers/HANDOVER_GDF_FIT_SCREENING.md and
docs/user_guide/periodic_methods.md § 3.2.2.
Changed: spglib now cites its published paper, not the 2018 preprint (2026-07-10)¶
The spglib library route cited the arXiv preprint (Togo & Tanaka,
arXiv:1808.01590, 2018) and carried no DOI, so a periodic run’s references
block named a preprint the authors have since superseded. It was published as
Togo, Shinohara & Tanaka, Spglib: a software library for crystal symmetry
search, Sci. Technol. Adv. Mater.: Methods 4, 2384822 (2024),
doi:10.1080/27660400.2024.2384822 – the reference spglib’s own documentation
asks for. The published version adds Shinohara as an author. Entry key and
BibTeX key move to togo_shinohara_tanaka_2024 / togo_spglib_2024.
Of the seven DOI-less entries this audit examined, the other six are correctly
DOI-less: vibe-qc’s own software citation, libint, Eigen, Scholten’s 2003 PhD
thesis, and the Huzinaga and Wilson-Decius-Cross books. (Crossref’s nearest hit
for the latter, doi:10.1021/ja01588a076, is a two-page JACS book review,
not the book.)
Added: citation coverage tests for functionals, dispersion, ECPs (2026-07-10)¶
The KDIIS mis-attribution and the four defect classes below all survived code
review. tests/test_scf_accelerator_citation_coverage.py answered that for
the SCF accelerators by pinning each one’s originating DOI; this extends the
same pattern to the rest of the mandatory citation surface of
CLAUDE.md § 8.
tests/test_functional_citation_coverage.py– every[routes.functionals]route resolves; each composite’s defining paper is pinned by DOI; each hybrid/composite is pinned to the component papers it evaluates (the guard that would have caughtb2plypnever citing B88).tests/test_dispersion_citation_coverage.py– D2/D3/D3(BJ)/D4 originating DOIs pinned, and all fourteen[routes.dispersion_params]reparameterisations pinned to the paper that fitted those parameters. Adding a reparameterisation without pinning it here now fails.tests/test_ecp_citation_coverage.py– libecpint and the Hay-Wadt / Peterson potential papers pinned; unrouted ECP entries tracked explicitly.tests/test_citation_database_integrity.py– structural invariants needing no network: no two entries share a DOI, everykind="article"has one, DOI syntax is well formed, no route dangles, no BibTeX-key collisions.
Each guard was mutation-tested: reintroducing the corresponding defect makes it fail.
Fixed: one paper printed twice in the references block (2026-07-10)¶
assemble() deduplicates by entry key, not by DOI, so two keys for the same
paper print it twice. hehre_sto_ng_1969 (routed from dftb0 / scc_dftb)
and hehre_sto3g_1969 (routed from the sto-3g / sto3g / sto-6g basis
keys) were the same publication, doi:10.1063/1.1672392. A
method="dftb0", basis="sto-3g" job emitted Hehre, Stewart & Pople 1969
twice in its .out references block and twice in its .bibtex, under two
different BibTeX keys. Collapsed onto hehre_sto_ng_1969, whose page range
was also incomplete (2657 -> 2657--2664).
Fixed: routes citing papers that do not cover the routed feature (2026-07-10)¶
Third pass of the citation audit, applying the KDIIS disproof technique
(extract the cited paper’s text, grep for the routed method) to every
[routes.*] mapping.
lccd,lccsd,cepa(0),cepa(1),cepa(2),cepa(3)each cited DePrince & Sherrill 2013, a DF/CD/FNO CCSD efficiency paper whose full text contains zero occurrences of “CEPA”, “LCCD”, “LCCSD”, “coupled-pair” or “linearized”. It stays onccsd,ccsd(t),ccsd[t],ccdandfno-ccsd(t), which it does describe. The coupled-pair routes already carry Čížek 1966 / Meyer 1973 / Wennmohs & Neese 2008.r2scan-3ccited Grimme’s D3 paper, but the r2SCAN-3c recipe uses D4 (composites.pysetsdispersion="d4"), and the method’s own paper uses D3 only for the comparison functionals it benchmarks against. Dropped; D4 was already routed.b2plypnever cited B88, the semilocal exchange it actually uses (“the combination of B88 and LYP yields the best results” – Grimme 2006).becke_b88_1988added, asblypalready does. Becke 1993 is retained: the B2PLYP paper cites it for the adiabatic-connection hybrid mixing.hse-3ccited its HSE, gCP and D3(BJ) components but no composite-method paper, unlike every sibling 3c method. New entrybrandenburg_hse3c_2016(Phys. Chem. Chem. Phys. 18, 15519-15523 (2016), doi:10.1039/c6cp01697a) is routed and printed, and the recipe’s inlinecitationfield no longer describes the method as a “community extension”.langhoff_davidson_1974andlanghoff_davidson_q_1974were two entry keys for one paper (identical DOI, authors, title, journal, pages, year), reached frommrciandmrci+qrespectively. Collapsed onto the fuller record.
Not changed, having been checked and found correct: mrci legitimately cites
the Davidson +Q paper, because solvers/_mrci.py applies the +Q correction by
default (do_q_correction=True). ediis_diis legitimately keeps Garza &
Scuseria 2012 – unlike kdiis, that route names a hybrid the paper really
does recommend.
Fixed: citation metadata that disagreed with the cited paper (2026-07-10)¶
Second pass of the same Crossref sweep, comparing every recorded author list, title, journal, volume, pages and year against the canonical record. Seven entries disagreed with the paper they name:
dral_omx_2016invented two co-authors: “Spoerkel, Lukas” is really Spörkel, Lasse and “Steenken, Ralf” is really Steiger, Rainer. Both names had propagated intosemiempirical/methods/omx_params.py.jarvis_dftlisted “Gavini, Vikram”, who is not an author, and omitted seven who are (Kalinin, Pilania, Acar, Mandal, Haule, Vanderbilt, Rabe): 22 names recorded, 28 on the paper.curtarolo_aflow_2012recorded 6 of the paper’s 14 authors.hehre_pople_stong_2nd_row_1970andweigend_jkfit_2002carried paraphrased titles that do not match the published ones.andersson_caspt2_1990carried the 1992 companion paper’s title. The two are distinct works and the 1992 entry’s title was already correct.wilson_decius_cross_1955is a book but stored its publisher in thejournalfield, so the reference rendered “McGraw-Hill” as a journal name.
Corrections are taken from Crossref and, where a local PDF exists, checked
against the paper’s own title page. Note that Crossref is not always right:
its record for lopez_sancho_rubio_1985 lists four authors and its record for
roos_taylor_siegbahn_casci_1980 misspells “Siegbahn”. Both databases entries
match their PDFs and were left alone.
Fixed: three citation DOIs did not resolve (2026-07-10)¶
A sweep of all 363 DOI-bearing entries in the citation database against the Crossref REST API, prompted by the KDIIS mis-attribution below, found three that do not resolve. Each was verified against the publisher’s own PDF and Crossref before correction; no DOI was guessed.
choudhary_tavazza_2020claimed doi:10.1038/s41524-020-0300-2, which resolves to a different paper (Kabiraj, Kumar & Mahapatra, High-throughput discovery of high Curie point two-dimensional ferromagnetic materials, npj Comput. Mater. 6, 35 (2020)). The recorded work is really Comput. Mater. Sci. 161, 300-308 (2019), doi:10.1016/j.commatsci.2019.02.006. Journal, volume, pages and year were all wrong; the entry and its BibTeX key are renamedchoudhary_tavazza_2019to match.guo_dlpno_t1_2018claimed doi:10.1063/1.5006075(dead). The paper’s own PDF gives doi:10.1063/1.5011798. Its author list also named a nonexistent “Minchin, Yury” (really Minenkov, Yury) and omitted Cavallo, Luigi entirely.krack_parrinello_allelectron_2000claimed doi:10.1039/b001167k(dead); the correct suffix isb001167n.
As with KDIIS, each defect had propagated beyond the database: into
ml_kpredictor.py, docs/license.md, periodic_jk_method.py,
periodic_gapw_augment.py, a GAPW handover note, and the aiccm_comparison
manuscript bibliography. All call sites are corrected.
[v0.15.33] - 2026-07-10 - Neese’s Cheetah¶
Fixed: KDIIS cited the wrong paper (2026-07-10)¶
scf_accelerator="kdiis" printed a references block that never named the
paper the method comes from. The kdiis route cited Garza & Scuseria,
Comparison of self-consistent field convergence acceleration techniques,
J. Chem. Phys. 137, 054110 (2012) — a comparative study of ADIIS / LIST /
EDIIS+DIIS whose text never mentions Kollmar or KDIIS. Code comments, the
SCFAccelerator docstring, docs/user_guide/scf_convergence.md,
docs/user_guide/solver_framework.md, docs/features.md and the KDIIS
tutorial table additionally attributed the method to a nonexistent
publication (“C. Kollmar, Int. J. Quantum Chem. 105, 685 (2005)”):
Crossref lists exactly one Kollmar paper in that journal, and it is not that
one.
The originating publication is now routed and printed: C. Kollmar, Int. J. Quantum Chem. 62, 617-637 (1997), doi:
10.1002/(SICI)1097-461X(1997)62:6<617::AID-QUA5>3.0.CO;2-Z. This is the reference ORCA attributes!KDIISto, and the one the ORCA developers cite for KDIIS in the TRAH-SCF paper.Garza & Scuseria 2012 moved to the
ediis_diisroute, which is the hybrid that paper actually recommends. Its entry key lost the misleadingkdiisinfix (garza_scuseria_kdiis_2012→garza_scuseria_2012); the BibTeX key in emitted.bibtexfiles changes accordingly.Docs now state that vibe-qc adopts Kollmar’s error vector only and still diagonalizes the extrapolated Fock. Kollmar’s own scheme pairs the orbital-gradient DIIS with a first-order-perturbation orbital update and is diagonalization-free. The claim that KDIIS is “ORCA’s strict-convergence default” was also wrong: ORCA exposes it as the opt-in
!KDIISkeyword.New
tests/test_scf_accelerator_citation_coverage.pypins everySCFAcceleratormember to a route, every route to resolvable entries, and each accelerator’s originating reference to a Crossref-verified DOI, so this class of mis-attribution now fails CI instead of surviving review.
Changed: MSINDO COSMO reaction-field step routes to C++ (2026-07-10)¶
The per-density MSINDO COSMO reaction-field step now routes through one native
C++ helper: V_elec = B^T Q, V_tot = V_elec + V_core, the COSMO
apparent-surface-charge solve, the B q Fock scatter, and the polarization
energy bookkeeping happen in one call. The Python sequence remains the
reference/fallback path, and the outer COSMO SCF loop still runs through the
existing MSINDO fock_extra hook.
Changed: MSINDO GEPOL cavity generation routes to C++ (2026-07-10)¶
The default MSINDO COSMO GEPOL/SAS cavity builder now routes the
pentakisdodecahedron segment generation, neighbour culling, face refinement,
segment areas, self terms, and segment-owner map through a native C++ helper.
The original Python implementation remains as the reference/fallback path, and
new regressions compare the native cavity against the forced-Python path before
the capped GEPOL A matrix is built. COSMO SCF orchestration remains Python
owned.
Changed: CPCM/COSMO A-matrix and surface-charge solves route to C++ (2026-07-10)¶
The generic CPCM dense A matrix, the capped GEPOL/MSINDO COSMO A matrix,
the dielectric screening factor, and the apparent-surface-charge solve now
route through native C++ helpers with NumPy/Python fallback paths retained.
MSINDO COSMO therefore keeps only its COSMO SCF orchestration in Python at this
layer, while its default GEPOL A assembly and repeated A q = -f V solves no
longer run through Python numerical loops.
Changed: semiempirical run_job dispatch uses unified runner (2026-07-10)¶
Semiempirical single-point dispatch now lives in
vibeqc.semiempirical.runner, with shared method sets and a common
SemiempiricalResult adapter for DFTB, GFN2-xTB, PM6, OMx, MSINDO, COSMO,
and CCM routes. vibeqc.runner keeps its private compatibility aliases, but
its basis/optimizer/memory checks now use the same central method registry.
Fixed: vibe-view renders 2D slabs as sheets, not sheets inside a vacuum box (2026-07-10)¶
vibe-view now gates unit-cell drawing, atom replication, volume/orbital tiling
and minimum-image bonding on the per-axis pbc flags rather than the scalar
any(pbc). A dim=2 slab draws only the in-plane a,b parallelogram, tiles
in-plane only, and frames on the atom bounding box; dim=1 draws a single
chain-axis edge; dim=3 is unchanged. The synthesized third lattice column of a
low-dimensional PeriodicSystem is never read as geometry — it is bookkeeping
for the AO integrals and spglib, and the SCF energy is invariant to its length.
Slab rendering is now provably independent of |a3| (regression-guarded at 30
vs 80 bohr). The Nz replication control is hidden for a slab and clamped
server-side.
Fixed: XSF output declares its dimensionality (SLAB / POLYMER, not CRYSTAL) (2026-07-10)¶
write_xsf_structure and write_xsf_volume (and therefore write_xsf_mo /
write_xsf_density, which delegate to it) hardcoded a CRYSTAL block keyword
for every system. A dim=2 slab and a dim=1 polymer were written as if all
three PRIMVEC rows were real lattice vectors.
They are not. For dim < 3 the lattice columns dim..2 are non-physical
bookkeeping that keeps the AO integrals and spglib full-rank, and the SCF total
energy is invariant to them. Critically, a synthesized a₃ is numerically
indistinguishable from a genuine vacuum gap in a 3D cell, so a reader cannot
recover the dimensionality by inspecting the matrix — the block keyword is the
only signal, and vibe-qc was emitting the wrong one.
The keyword now follows system.dim (CRYSTAL / SLAB / POLYMER, per the
XCrySDen XSF spec), which is what lets VESTA, XCrySDen and moltui draw a slab as
a sheet rather than as a sheet adrift in an empty box. A regression test pins the
keyword to dim alone: growing a slab’s padded a₃ from 30 to 80 bohr must not
turn it into a crystal, and a tall 3D cell must not turn into a slab. dim=0 is
rejected rather than written as a bare MOLECULE block, which XSF spells with an
ATOMS block instead of PRIMVEC/PRIMCOORD.
Next up: the two experimental-CCM known-reds tracked in scripts/test_gate/known_reds_baseline.json (test_msindo_ccm_gradient.py dim=3+madelung=False NaN in the new C++ FD-gradient kernel), continued low-D CCM kernel work (docs/aiccm2026dev_a_lowd_greens.md), and the periodic-scf / regression-suite reds carried over from v0.15.31.
[v0.15.32] - 2026-07-10 - Neese’s Cheetah¶
Fixed: odd-electron periodic density grids use valid shifted-basis multiplicity (2026-07-10)¶
Periodic density and QVF grid output now build shifted AO image bases from the normalized unit-cell molecule multiplicity. This keeps default-multiplicity odd-electron cells, such as BIPOLE UHF/UKS H/Li test cells, from tripping the molecular parity check when non-home density blocks are sampled for output.
Added: IUPAC molecular naming engine (2026-07-10)¶
A standalone vibeqc_naming package produces IUPAC names from 3D
coordinates and vice versa. 83 built-in structures (water through
adenine), 57 mineral names, SMILES generation, and surface/adsorbate
naming. run_job(name_molecule=True) prints the name to the .out
file. See docs/user_guide/naming.md for documentation.
Fixed: low-dimensional periodic Coulomb gauge; dim<3 GDF no longer returns artifacts (2026-07-10)¶
For dim < 3 the periodic Coulomb channels did not share a gauge, and the total
energy diverged with the lattice-sum cutoff (CLAUDE.md §7). V_ne/E_nn came
from bare, conditionally-convergent lattice sums
(_gauge_lat_opts_for_v_ne_and_e_nuc passed its input through when
system.dim != 3) while J came from the G+q=0-dropped neutral cderi, so the
conditional constant c never cancelled and the leftover -½cZ² grew without
bound. Measured on polyethylene (dim=1, sto-3g, nrep=(4,1,1)): E_total ran
-1226.78 → -1368.41 → -1518.55 Ha as nuclear_cutoff_bohr went 45 → 90 → 180.
Investigation found a second, more severe defect underneath: for dim < 3 that
neutral J was not a Coulomb kernel at all. rsgdf_dense_g_mesh and
rsgdf_g_mesh pin every non-periodic axis at G_perp = 0, so 4π/|G+q|²
degenerates into a transverse-uniform sheet term ∝ 1/V — the electron-electron
repulsion vanishes as the vacuum padding grows (|g|_max · V constant to 4 s.f.
across D = 12/18/24 bohr; the mixed-boundary wire four-center is D-invariant).
An H₂ chain’s dim=1 total ran -6.917 → -6.568 Ha for D = 12 → 30 bohr where
the wire kernel gives -1.32206067 Ha at every D. Merely matching the two gauges
would have produced a cutoff-independent but meaningless energy.
The dim < 3 3-D-torus route therefore fails closed rather than returning a
number:
_gauge_lat_opts_for_v_ne_and_e_nucraises fordim < 3instead of returning bare sums, so the multi-k GDF, the Γ-supercell direct-torus (run_ccm_rhf_direct) and RIJCOSX all inherit the guard from the one gauge-dispatch site.rsgdf_dense_g_mesh/rsgdf_g_meshrefuse to return a transverse-collapsed mesh unless the caller passesallow_transverse_collapse=Truefor an explicit non-Coulomb use.
The gauge-consistent low-D Hamiltonians are reached by name, never by silent
substitution: run_ccm_rhf_wire (dim=1 mixed-boundary) and the four-center route
(bare 1/r minimum image, all channels in one gauge). These are different
operators from the 3-D-torus route and from each other — they coincide only in the
non-interacting limit — so neither is a drop-in replacement. dim = 2 has no
mixed-boundary kernel at all (the slab needs the partial in-plane FT of
docs/aiccm2026dev_a_lowd_greens.md §9).
Comparison-affecting. Every previously-reported dim < 3 GDF / neutral-RI
absolute energy was a cutoff-and-vacuum artifact and must not be quoted; low-D
benchmark rows through those routes need regeneration against the four-center route.
dim = 3 is bit-unchanged (both guards are no-ops for 3-D cells). Gates:
tests/test_ccm_lowd_gauge_consistency.py — dim=1 and dim=2 fail closed at every
nuclear cutoff, the two source guards hold while dim=3 still returns Ewald-3D, and
the sheet-kernel measurement (|g|·V vacuum-invariant vs the wire four-center being
vacuum-invariant) is pinned directly.
Fixed: charged MSINDO native energy and gradients honor charge (2026-07-10)¶
The C++ molecular MSINDO RHF, UHF, and analytic-gradient bindings now accept an
explicit charge keyword and subtract it before occupation assignment.
run_job(method="msindo") now passes the requested charge through to the native
RHF/UHF engines instead of silently running charged closed-shell molecules with
neutral occupations. The public msindo_gradient_analytic wrapper now also
routes charged closed-shell INDO gradients through C++; NDDO, odd-electron, and
binding-unavailable cases remain on the existing fallback path. New regressions
cover charged C++/Python energy parity, charged UHF parity, runner dispatch, and
charged analytic-gradient parity.
Changed: direct public MSINDO energy routes to C++ (2026-07-10)¶
The public vibeqc.semiempirical.methods.msindo.run_msindo wrapper now
dispatches supported INDO RHF, INDO UHF, and closed-shell NDDO single points to
the native C++ kernels before entering the Python reference SCF implementation.
The Python engine remains available as a fallback when the binding is missing or
declines by non-convergence, and parity tests now force that fallback explicitly
when checking C++ against the reference path. The semiempirical hot-path
inventory now marks direct MSINDO and direct NDDO energy calls as public C++
routes; remaining COSMO SCF orchestration and post-SCF/root-tracking loops
remain S2 follow-ups.
Changed: MSINDO COSMO B-matrix and vector operations route to C++ (2026-07-10)¶
The MSINDO COSMO provider now builds its distributed-multipole solute-cavity
B matrix through a native C++ helper backed by the same MSINDO v2int and
HARMTR kernels as the molecular engine. The per-SCF electronic charge vector,
B^T Q surface potential, B q Fock scatter, and effective-core potential at
the cavity points now route through native helpers as well. Python remains the
fallback/reference route for binding-missing cases; subsequent native slices
cover the surrounding GEPOL cavity, A-matrix assembly, and
apparent-surface-charge solve, while COSMO SCF orchestration remains
Python-owned. New regressions compare the native B matrix and vector
operations against the forced Python reference, then monkeypatch the
Python/provider boundary to prove public provider dispatch.
Changed: charged CCM energy and FD gradients route to C++ (2026-07-10)¶
The C++ CCM energy and finite-difference-gradient bindings now accept an
explicit charge keyword, subtract it before the RHF occupation count, and
therefore cover charged closed-shell H-Kr CCM cells. The lower-level public
run_ccm, ccm_gradient_fd, and run_job(method="ccm") paths now pass charge
through to C++ for native-scope jobs; heavier-element, binding-missing, and
native-nonconverged cases retain the Python fallback. New regressions compare
charged C++ energy/FD gradients against the forced Python reference and record
the charge passed by run_job.
Changed: public CCM finite-difference gradients route to C++ (2026-07-10)¶
The public ccm_gradient_fd wrapper now dispatches native-scope CCM
finite-difference gradients to the existing C++ kernel, including atoms=
subsets. Heavier-element, binding-missing, and invalid-subset cases remain on
the Python path. New regressions pin public dispatch to C++ and force the
Python fallback for C++/Python parity.
Changed: public MSINDO CCM analytic gradients route to C++ (2026-07-10)¶
The public ccm_gradient_analytic wrapper now dispatches closed-shell CCM
analytic gradients within the native H-Kr binding scope to the C++ kernel,
including Madelung-enabled 1D, 2D, and 3D clusters. The Python implementation
remains available for binding-missing and heavier-element fallback cases. New
regressions separately pin public dispatch to C++ and force the Python fallback
when checking C++/Python parity.
Changed: public MSINDO analytic gradients route to C++ (2026-07-10)¶
The public msindo_gradient_analytic wrapper now dispatches closed-shell INDO
gradients to the existing C++ analytic-gradient binding. The Python
implementation remains available for NDDO, odd-electron, and
binding-unavailable cases. A new regression monkeypatches the Python
pair-derivative loop to fail and verifies that the public H2O gradient still
matches the native C++ kernel.
Changed: MSINDO open-shell run_job stays on the C++ UHF path (2026-07-10)¶
run_job(method="msindo") no longer falls back to the Python reference engine
for supported open-shell d/heavy-element cases. The C++ UHF engine already
covers the H-Xe s/p/d parameter scope, including the heavy trajectory-SCF
fixtures, so the runner now sends all supported open-shell INDO jobs there.
Open-shell NDDO remains explicitly gated as unsupported before dispatch. A new
regression monkeypatches the Python run_msindo reference to raise and verifies
that a MoCl doublet still reaches the C++ path and reproduces the oracle energy.
Added: semiempirical hot-path benchmark inventory (2026-07-10)¶
Added scripts/bench_semiempirical_hotpaths.py, a subprocess-isolated
developer benchmark and C++ port inventory for the semiempirical roadmap. The
script can list the current hot-path classification without importing the
native stack, run a cheap smoke suite across DFTB0, SCC-DFTB, GFN2-xTB, PM6,
OM2, and MSINDO, or run the broader inventory that marks remaining Python
hot loops for future C++ consolidation. The MSINDO COSMO inventory row now
marks the native B-matrix/vector-op route and the remaining Python COSMO SCF
orchestration separately. Lightweight tests cover inventory listing and
unknown-case rejection.
The semiempirical roadmap handover also records the article-task benchmark handoff: Thiel excited-state coverage is a future-driver item, MACE comparison scope is pinned to the v0.15 wrapper models (MACE-MP/MPA-0 and MACE-OFF23), and solid benchmark expansion should use EOS/Delta-factor-style volume scans with Birch-Murnaghan fits and stress consistency checks.
Added: canonical semiempirical roadmap and status cleanup (2026-07-10)¶
The semiempirical workstream now has a canonical tracker at
handovers/HANDOVER_SEMIEMPIRICAL_ROADMAP.md, indexed from
handovers/README.md. It records the owned roadmap for DFTB, GFN2-xTB,
PM6/OMx, and MSINDO, including the new standing requirement that all
performance-critical production paths either move their numerical hot loops to
C++ or be explicitly labeled as cold/reference paths. The handover also carries
the paper-writing contract: paper claims should use that file and the
method-specific GFN2/MSINDO handovers as the current source of truth.
The user-facing semiempirical docs and several stale method/header comments
were reconciled with current main: MSINDO is documented as H-Xe within its
INDO scope, CCM docs no longer say H-Zn, the old “no MSINDO gradients/CCM”
comparison text was retired, PM6/OM2/OM3 wording now matches their
production-oriented molecular surface, OM1 remains experimental, and GFN2-xTB
production gates now point at periodic AES, periodic molecular-limit parity,
difficult periodic/polar fixtures, and external xtb parity rather than the
superseded self-consistent-D4/mixer wording.
Fixed: 2D slabs get the rigorous vacuum-free Coulomb; bulk J/K builders fail closed (2026-07-10)¶
This fixes a correctness bug, not an aesthetic one. pick_jk_method had no
dimensionality branch and resolved jk_method='auto' to GDF unconditionally for
closed-shell SCF. GDF is a bulk (dim=3) J/K builder: its Γ drivers already
raised for dim != 3, but the multi-k entries silently ran a dim=2 slab as
a 3-D crystal of sheets stacked a3 apart (normal-axis mesh pinned to one
k-point). The observable symptom on a graphene layer was a nuclear repulsion that
depended on the k-mesh — E_nuclear = +321.39 Ha at k=(1,1,1) vs +557.97 Ha at
k=(8,8,1) for identical geometry — and a total energy wrong by thousands of
Hartree. A purely geometric Madelung sum cannot depend on the k-mesh; per
CLAUDE.md § 7 that is a bug, and it is fixed at the gauge, not damped.
jk_method='auto'now routesdim=2toCoulombMethod.SLAB_EWALD_2D— the rigorous, vacuum-free Parry / de Leeuw–Perram 2D-Ewald gauge (RHF / RKS / UKS, Γ and multi-k). The total is provably invariant to the bookkeepinga3andE_nuclearis k-mesh-independent; both are pinned by regression tests (tests/test_slab_2d_routing.py).New selector
PeriodicJKMethod.SLAB_EWALD_2D(jk_method='slab_ewald_2d', aliasesslab,ewald_2d). It is dim=2-only and rejects bulk cells.Every bulk builder now fails closed on a slab.
jk_methodin {gdf,bipole,gpw,gapw,rijcosx, …} raisesNotImplementedErrorfordim=2with a pointer to the 2D gauge, and the four multi-k GDF entries (run_k{rhf,rks,uhf,uks}_periodic_gdf) carry their owndim=2guard so a direct call cannot bypass the picker. UHF-on-slab raises pending its follow-on.dim=1polymers are deliberately unaffected — there is no rigorous 1D Coulomb gauge yet (NEUTRALIZED_1Dstill raises), so the cached-Lpq GDF Hartree remains the supported wire route (5fe4d021); only the slab, which has a rigorous alternative to redirect to, fails closed.Parry (1975) + de Leeuw & Perram (1979) now fire into the end-of-run references block /
.bibtex/.referenceswhenever a slab runs (uses_slab_ewald_2d).
Changed: dim=2 slabs are built from two in-plane vectors, not a vacuum gap (2026-07-10)¶
The old convention documented the dim=2 third lattice column as a load-bearing
vacuum gap (“~30 bohr or more so image interactions are negligible”). That is a
plane-wave idiom; a Gaussian slab needs no vacuum, exactly as CRYSTAL’s SLAB mode
demonstrates. The vacuum is retired as a periodicity parameter.
New
vibeqc.slab_2d(a1, a2, atoms, ...)builds a genuinedim=2PeriodicSystemfrom two in-plane lattice vectors plus Cartesian atoms with real z. The internala3is auto-synthesized along the slab normal (vibeqc.synthesize_slab_a3) purely for AO-integral / spglib bookkeeping; the SCF is invariant to it.vibeqc.build.slab(...)now returns adim=2system by default (atoms keep their natural z; no vacuum centring). Passperiodic_z=Truefor the legacydim=3-with-vacuum cell (plane-wave-style references, 3D-with-vacuum comparisons). Fordim=2thevacuum=argument is retained only as viewer / grid-extent metadata onSlabInfoand is not used by the SCF.cpp/include/vibeqc/periodic.hppdocuments the new convention.
Migration. Code that relied on slab(...) returning dim=3 should pass
periodic_z=True. Slab SCF runs that previously went to GDF now go to
SLAB_EWALD_2D and will report different (correct, a3-invariant) energies.
Slab analytic gradients remain a follow-on and still raise.
Docs: slab documentation and tutorials swept to the 2D convention (2026-07-10)¶
Every user-facing page that taught the retired “slab = dim=3 cell with a vacuum gap” convention is corrected, and the code examples that would now raise are fixed. Highlights:
user_guide/slabs_and_adsorbates.mdrewritten arounddim=2andslab_2d, with a migration admonition and the force / smearing limitations stated up front.user_guide/periodic_methods.md§ 7.1 no longer recommends the vacuum convention; the capability matrix, the method-selection chart, and thejk_methodstatus table now showSLAB_EWALD_2Das the onlydim=2route and every bulk builder as raising there.Fixed examples that would now raise: the graphene and MgO(001) slab snippets in
periodic_methods.mdhadjk_method="gdf"on adim=2cell (the MgO one was annotated# 2D: no 3D Madelung problem, which is precisely the bug), and the slab guide ran UKS withjk_method="bipole". Force-driven Pt(111) examples now passperiodic_z=True, and were additionally repaired: they calledvq.slab("Pt", "fcc111", ..., supercell=(2, 2, 1)), which had been broken independently of this change.tutorial/slab_adsorbate_dft.mdno longer claims thatbuild.slabreturnsdim=3“notdim=2”, nor that adim=2system “falls back to the isolated-cluster limit”. It keeps itsdim=3cell and its verified energies, and now explains that it does so because the 2D route cannot smear yet.roadmap.mdandfeatures.mdrecord the slab SCF as shipped, with the open items (analytic gradients, smearing, GDF/GPW 2D-truncated Coulomb) named.ewald.md,periodic_systems.md,crystal_lattices.md,from_pyscf.md,periodic_hf.md,vibe_view.md,relaxed_scan.md,surface_embedding.mdandperiodic_methods_compared.mdcorrected.crystal_lattices.mdalso had a graphene cell passing fractional coordinates where Cartesian bohr are expected;relaxed_scan.mdreferenced aSlabInfo.systemattribute that does not exist. Both fixed.
Fixed: Gamma pair-FT mirror corrupted the multi-k GDF exchange on momentum-shifted meshes (2026-07-10)¶
The v0.15.x dense-core tail speedup (“auto-resolve dense-core rsgdf
tails”) taught the C++ AO-pair FT (ao_pair_fourier_transform_bloch) to
compute only the upper shell-pair triangle at k == 0 (inversion-symmetric
cell list) and mirror it, using FT_mu,nu(G) == FT_nu,mu(G). That identity
needs a third condition the guard did not check: every evaluation frequency
must be a reciprocal-lattice vector (exp(i G.R) == 1), which holds on the
plain Gamma tail mesh it was written for but not on the momentum-shifted
meshes G+q of the Bloch-pair GDF fit. build_lpq_bloch_native_fft
reaches the kernel with k_cart = k_ket = 0 and a q = -k_bra shifted
mesh for every (k_bra != Gamma, k_ket = Gamma) multi-k exchange pair, so
the mirror silently corrupted those fits wherever inter-cell (R != 0)
AO-pair terms are non-negligible — ultra-diffuse bases on tight cells.
Measured on rocksalt LiH/STO-3G at kmesh (2,1,1): the fitted four-center of
the corrupted (X, Gamma) channel was off by 1.9 (max element), moving
run_krhf_periodic_gdf by +2.04e-2 Ha/cell and breaking the direct-vs-GDF
CCM parity gates (the 2026-07-10 “three-way HF disagreement” of
handovers/HANDOVER_AICCM_DIRECT_TORUS.md Finding 2 — attributed there to
environment drift; actually this core defect, invisible on every
vacuum-padded gate because their R != 0 terms are exponentially small).
The guard now additionally verifies exp(i G.R) == 1 over the evaluation
frequencies (early-exit, so shifted-mesh calls pay one row of dot
products); the Gamma-tail mirror keeps its 2x saving. Restored:
tests/test_ccm_direct.py::test_direct_matches_gdf_lih_multicell_fold
(direct-vs-GDF back to 4e-14-class parity) and
::test_supercell_cderi_lih_documented_residual; new kernel pin
tests/test_rsgdf_dense_mesh_tail.py::test_gamma_mirror_disabled_on_momentum_shifted_mesh.
Changed: multi-k DIIS and KDIIS now run on the canonical C++ kernel (2026-07-10)¶
python/vibeqc/periodic_scf_accelerators.py carried a second, numpy-native
Pulay DIIS: _MultiKPulayDIIS, _MultiKKDIIS and _MultiKKDIISOpenShell
each rebuilt the B-matrix in a Python triple loop over
history² × n_k vdot calls, re-derived the Pulay solve, and (for the
adaptive-depth policies) re-flattened the whole error history on every SCF
iteration. That duplicated the DIIS class the molecular drivers use, in
violation of the single-canonical-implementation rule.
All three are now thin adapters over vibeqc::DIIS, reached through a new
DIIS::extrapolate_blocks(fock_blocks, error_blocks) entry point. The
existing √w_k block bridge (per_k_to_stacked_real_blocks) maps each
per-k Hermitian matrix onto the real block pair √w_k·Re M(k),
√w_k·Im M(k), under which the kernel’s per-block Frobenius sum is the
k-weighted Pulay inner product Σ_k w_k Re Tr[e_i(k)† e_j(k)]. Nothing in
the kernel is multi-k-aware; it is the same extrapolation on a longer
vector. Open-shell spin coupling falls out for free by appending the β
blocks to the same lists. The multi-k KDIIS paths inherit the Chupin
adaptive-depth policies they never had.
Three changes in cpp/src/diis.cpp benefit every SCF driver, molecular and
periodic:
The DIIS B-matrix is now cached incrementally as a Gram matrix. A push costs
nfresh dot products rather than then²a full rebuild needs, and the residual norms the adaptive-depth policies test are read off the Gram diagonal instead of re-traversing the stored error vectors.The history takes its iterates by value and moves them into place, so
extrapolate_blocksno longer copies the flattened Fock and error a second time on the way in. Atnbf = 120over 16 k-points each of those is ~3.7 MB per SCF cycle.The Pulay solve gained the coefficient blow-up guard that previously existed only in the numpy copy: a non-finite solution, or coefficients above
1e6, means the history has gone linearly dependent, so the oldest iterate is dropped and the system re-solved. A two-iterate window that still fails falls back to the raw Fock (a plain SCF step). Healthy extrapolations are bit-for-bit unchanged; only histories that previously produced garbage coefficients behave differently.
The extrapolation itself measures 1.4x (nbf=120, 16 k-points) to 2.0x
(nbf=40, 8 k-points) faster than the numpy implementation it replaces. The
accelerator is a small fraction of a periodic SCF cycle next to the Fock
build, so the point of this change is the single canonical implementation,
not the wall clock. tests/test_diis_block_kernel.py pins the new kernel
against the retired numpy B-matrix construction (agreement to 1e-13 over a
converging 10-iterate history) and pins the multi-k depth sequences of both
adaptive policies against the scalar C++ path.
Fixed: C++ periodic drivers silently ignored level_shift (2026-07-09)¶
PeriodicRHFOptions / PeriodicSCFOptions / PeriodicKSOptions exposed
level_shift, level_shift_warmup_cycles and level_shift_schedule, and
the docs stated the controls were available on every periodic driver, but
the three C++ direct-truncated periodic drivers never read them: the option
structs accepted the values and no code path ever consumed them. A silent
no-op, the exact failure mode the SPINLOCK engine’s fail-closed
check_spinlock_support gate exists to prevent.
The Saunders-Hillier shift is now applied in all three:
run_rhf_periodic_gamma (Γ, real: F + b·S − (b/2)·S·D·S) and the
multi-k run_rhf_periodic / run_rks_periodic (per k-point, Hermitian:
F(k) + b·S(k) − (b/2)·S(k)·P(k)·S(k)), resolved through the same shared
level_shift_at_iter helper the molecular drivers use, so a given
(level_shift, warmup, schedule) triple now behaves identically on every
backend. The S·D·S products and the P(k) Bloch sum are skipped entirely
on any iteration whose resolved shift is 0.0, so a released shift costs
nothing. Regression-guarded in tests/test_periodic_level_shift.py, which
pins both halves of the contract per driver: the shift is honoured (with
DIIS and dynamic damping off, a fixed-length trace diverges from the
unshifted one) and inert (it never moves the converged fixed point).
The probe cell is H2/6-31G, not H2/STO-3G. A minimal-basis H2 cell carries one basis function per atom, so both MOs are fixed by symmetry alone: a level shift moves their eigenvalues but cannot rotate the eigenvectors, and the SCF path is provably identical whether the shift is applied or ignored. Such a cell cannot distinguish a working shift from a dropped one, which is how the no-op went unnoticed. Any future level-shift probe needs a system with a genuine occupied-virtual mixing angle.
Fixed: SPINLOCK PATTERN_HOLD accelerator suspension swept to all drivers (2026-07-09)¶
Completes the sweep flagged by the multi-k UKS fix below. All sibling
PATTERN_HOLD implementations shared the extrapolate-across-the-hold
design: the SCF accelerator kept extrapolating (and recording history)
through the hold window, which on weak-exchange functionals collapses an
atomic_spins broken-symmetry seed by continuous orbital rotation, a
failure the occupation-selecting MOM hold cannot see. Each driver now
suspends its accelerator (plain DIIS and the EDIIS / ADIIS / KDIIS
variants alike: PeriodicSCFAccelerator on the Γ-Ewald and GDF paths,
MultiKPeriodicUHFAccelerator on BIPOLE, the C++ accelerator family on
molecular) while the hold is active: no held-window iterates enter the
history, density damping / FMIXING stay live, and the extrapolation
starts fresh at release. Drivers whose converged final pass re-diagonalises
the Fock (Γ-Ewald UHF/UKS, molecular C++ UHF/UKS) additionally re-select
the occupied set by max overlap with the held pattern when convergence
happens inside the hold window, so the reported energy / <S^2> / MOs
describe the state the SCF converged to rather than an aufbau re-slice
(GDF and BIPOLE return the in-loop, hold-consistent MOs and need no such
pass). The existing spinlock no-op pins (UHF: the broken state is the
aufbau ground state, so hold-vs-unlocked converge identically) stay green;
new UKS hold pins on the uniformly stretched 10-bohr H chain cover the
Γ-Ewald UKS, GDF UKS, BIPOLE UKS, and molecular UKS drivers in
tests/test_guess.py, each asserting the accelerator subspace stays empty
through the hold window and <S^2> holds the strongly-broken state.
Next up: continued desktop-packaging hardening (Windows/Linux build hosts, code-signing for macOS auto-update), a k-point-symmetry-reduced or streaming Lpq route for the multi-k hybrid-KS GDF driver (tracked as tests/test_pw1pw.py in scripts/test_gate/known_reds_baseline.json), and root-causing the remaining periodic-scf / regression-suite reds in the same baseline file.
[v0.15.31] - 2026-07-10 - Neese’s Cheetah¶
Fixed: SPINLOCK PATTERN_HOLD actually holds on the multi-k UKS Ewald driver (2026-07-09)¶
DIIS extrapolation used to run across the hold window; on UKS (weak
exchange), extrapolating the Fock toward the spin-symmetric attractor
collapses an atomic_spins broken-symmetry seed by continuous orbital
rotation within ~3 cycles – a failure mode the occupation-selecting MOM
hold cannot see (no level crossing ever happens). The driver now suspends
DIIS while the hold is active (density damping stays live; no held-window
iterates enter the history) and starts the extrapolation fresh at release.
Additionally, the converged final pass now re-selects the occupied set by
max overlap with the held pattern when the SCF converges inside the hold
window; the previous unconditional aufbau slice could report an energy /
<S^2> belonging to a different state than the converged density. The
slow-lane pin tests/test_guess.py::test_periodic_spinlock_pattern_hold_multik_uks
moves to a 10-bohr box (uniformly stretched 5-bohr H chain): the original
8-bohr box placed the periodic image at 3 bohr, inside PBE’s
Coulson-Fischer point, where no strongly-polarized stationary state exists
for any hold length (verified: a DIIS-free damped SCF from the seed also
decays to the weak-BS <S^2> = 0.08 fixed point there). With the fix and
the corrected geometry the held run converges to <S^2> = 0.946 in 13
cycles. The Γ-Ewald UHF/UKS, GDF and BIPOLE PATTERN_HOLD paths (and the
molecular C++ drivers) shared the extrapolate-across-the-hold design;
that sweep landed alongside this fix (see the entry above).
Fixed: Γ-path hybrid KS exchange convention + multi-k dim<3 pure-KS Hartree (2026-07-09)¶
The two GDF-side KS residuals left open after the same-day periodic-KS
XC-convention fix (b3f74aa9; handovers/HANDOVER_AICCM_DIRECT_TORUS.md
Finding §4, re-measured post-fix by the aiccm-real-gamma chat):
Γ-path HYBRID KS sat +8.32e-2 Ha above the real-Γ direct route on rocksalt LiH/STO-3G (1,1,1) PBE0 — neither the strict-zero-mode offset (0.297 Ha =
a_x·ξ_N·N_e/2on this fixture) nor the ~5 mHa ultra-diffuse-Li fitting floor. Root cause: the Γ KS fast path inrun_krhf_periodic_gdfdelegated to the legacy molecular-limit gamma driver, whose full-range real-space exchange carries no exxdiv convention at all (a truncated-lattice-sum gauge between strict-zero and ewald). Fix:run_pbc_gdf_rhfgained the closed-shell KS branch — newrun_pbc_gdf_rks— with thea_x-scaled GDF exchange under theexxdiv='ewald'Madelung shift (the identical convention toapply_exxdiv_ewald_to_Kon the multi-k path and the direct route’sξ_N·S·D·Sseam) and theb3f74aa9grid/density pairing for XC; the Γ fast path now delegates closed-shell KS (pure and hybrid) there under the same preconditions as HF. Range-separated hybrids now fail closed on this path (the silently-global-hybrid legacy treatment is unreachable);rsgdf_tail_ke_cutoffnow WORKS for Γ KS (previously refused loudly because the legacy fallback had no high-|G| tail correction —test_run_periodic_job_kpoints_gamma_rks_tail_cutoff_*flipped from fails-closed to supported); the legacy gamma driver remains the fallback for charged / dim<3 / smearing / convergence-aid cases.run_ccm_rks_gdf(lih, "pbe0")lands at the external PySCF 2.13.1 KRKS PBE0 anchor (−8.291224931283,exxdiv='ewald') within the documented shared-fitting floor.MULTI-K dim<3 pure KS was unchanged by
b3f74aa9:run_krks_periodic_gdfon the dim=1 H₂-chain fixture (6-bohr cell, 15-bohr transverse vacuum, STO-3G, (3,1,1), PBE) reported −3.0897 Ha/cell while its own stored density evaluates under the piecewise-validated functional to −3.7814 and the real-Γ direct route’s variational minimum on the same torus is −3.8947. Root cause: pure functionals (α = 0) kept the EWALD_3D J branch, whose analytic-FT kernel is 3D-only —ewald_3d_j_blockssilently degrades to the diagnostic FFT-Poisson grid backend on dim<3, mis-summing the vacuum-padded Coulomb, so the SCF converges under a wrong Hartree operator. Fix: pure multi-k KS on dim<3 routes to the cached-Lpq GDF Hartree exactly like HF/hybrids always did (dim=3 pure KS keeps the validated EWALD_3D path). Measured direct-vs-GDF delta on the fixture: +8.05e-1 → +2.2e-15 Ha. Open-shell multi-k never had the EWALD_3D branch (closed-shell-only defect).
Measured post-fix parities on the fixtures: hybrid Γ +7.0e-13, pure Γ
+5.6e-13 (a side effect of the rerouting — the pure-Γ parity tightens from
the −1.04e-4 legacy-path quadrature floor because both sides now run the
identical XC machinery), multi-k dim<3 +2.2e-15 Ha. Both canaries in
tests/test_ccm_rks_direct.py are flipped into direct-vs-GDF KS parity
gates
(test_rks_direct_matches_gdf_{pure_gamma,hybrid_gamma,multik_lowd}_post_fix,
all at the 1e-8 HF-gate class), joined by the external PySCF PBE0 Γ anchor
the pinned dim=1 chain value in
tests/test_krks_gdf_xc_density.py.PBCGDFResultgainede_xc+functionalfields;run_pbc_gdf_rksis exported at package level.
Fixed: multi-k density grids fold real; .xsf density output restored (2026-07-09)¶
The periodic density-output fold trusted D(-k) = D(k)* for explicit ±k
pairs, but the per-k lattice-truncation asymmetry of the Fock build leaves a
~1e-6 conjugate-pairing residue, so full Monkhorst-Pack meshes (e.g.
c-diamond RHF/STO-3G kpoints=(2,2,2)) aborted the density writers with
“periodic density block g=(0,0,0) has a non-negligible imaginary component”
and the .xsf / .density.xsf artifacts went silently missing
([[outputs.files]] written=false). The fold now emits the explicit
time-reversal pair w/2·e^{-ik·R}D(k) + w/2·e^{+ik·R}D(k)* for every k
point, making each real-space block real to machine precision by
construction (equal to the real part of the pristine fold). Pins:
tests/test_periodic_output_complex_density.py::test_per_k_density_fold_symmetrizes_inexact_explicit_pair
and ::test_per_k_density_fold_symmetrizes_self_inverse_kpoint.
Fixed: .qvf provenance records the true periodic dimensionality (2026-07-09)¶
The QVF manifest’s provenance block read only a dimensionality attribute,
but the bound PeriodicSystem exposes dim, so every real periodic run
recorded "dimensionality": 0. The writer now mirrors the structure
section’s lookup (dim, then dimensionality, then 3 for periodic / 0 for
molecular). Pins:
tests/test_qvf_writer.py::TestProvenance::test_provenance_dimensionality_from_periodic_system.
Fixed: periodic .system manifests carry the assembled citations (2026-07-09)¶
run_periodic_job assembled its citation list and wrote .bibtex /
.references / the in-.out block, but never fed the rows to the manifest,
so every periodic .system ended with [citations] count = 0. The runner
now routes the full assembled list (print=true and print=false rows alike)
through the shared vibeqc.output.citations.citation_manifest_rows helper
into ManifestUpdater.set_citations, mirroring the molecular runner. Pins:
tests/test_feature_citation_end_to_end.py::test_periodic_system_manifest_carries_assembled_citations.
Fixed: BIPOLE RKS honours bz_integration="gilat" through run_periodic_job (2026-07-09)¶
run_periodic_job(..., method="RKS", jk_method="bipole", bz_integration="gilat") recorded bz_integration = gilat in the .out
but silently ran the plain SCF: the runner validated the request (Gilat
is documented BIPOLE-RKS-only and rejected on RHF/UHF/UKS) yet never
forwarded the keyword to run_pbc_bipole_rks, the one BIPOLE driver
that implements it. The RKS call now forwards bz_integration.
Fixed: atomic_spins broken-symmetry seed works with the PATOM default guess (2026-07-09)¶
When the default molecular initial guess became PATOM, setting
atomic_spins (the ATOMSPIN broken-symmetry seed) started raising
GuessEngine: atomic_spins requires the SAD guess; resolved guess is PATOM — a regression, since the previous AUTO default resolved to SAD
for open-shell systems. The GuessEngine now silently promotes PATOM -> SAD when a seed is present (PATOM is itself SAD-derived — SAD plus one
in-field re-polarisation step — so dropping that step is immaterial to a
broken-symmetry start; the seed is what the caller asked for). The
provenance line records PATOM -> SAD (per-atom spin-seeded; ATOMSPIN).
A genuinely incompatible explicit guess (HCORE / SAP / HUECKEL / MINAO /
READ) with a seed still errors, now with a hint to set initial_guess=SAD.
Fixes the UKS second-order + Newton-trace test regressions on main.
Fixed: periodic EDIIS_DIIS 8-cycle stall — intensive switch metric + EDIIS/ADIIS anti-replay guard (2026-07-09)¶
run_krhf_periodic_gdf on c-diamond (fcc primitive, STO-3G, kmesh
(2,2,2)) froze for 8 iterations with dE bit-exactly zero before
converging — two stacked defects exposed when EDIIS_DIIS became the
periodic default (cb712e21):
Periodic EDIIS_DIIS/ADIIS_DIIS switch sites compared the raw size-extensive commutator norm against the intensive
ediis_diis_switch_threshold(0.19 > 0.1 held the run in the EDIIS regime; the intensive metric 0.019 < 0.1 says DIIS). The molecular drivers were fixed in ddd8d325; the sameediis_diis_switch_metricRMS normalization now applies to every periodic dispatch site: the C++ Γ/multi-k Ewald drivers (periodic_rhf.cpp,periodic_scf.cpp) and the Python Γ + multi-k accelerator classes (periodic_scf_accelerators.py, k-weighted RMS which reduces to the molecular norm at Γ; max-over-spins withn_blocks=2for the open-shell classes, mirroringuhf.cpp).The EDIIS/ADIIS kernels could deadlock on a pinned simplex vertex: with the initial iterate’s energy below the first Roothaan images, the QP returns the initial Fock verbatim with zero weight on the newest iterate; the deterministic SCF replays the same diagonalisation, pushes a duplicate iterate, and freezes until the FIFO evicts the pinned entry
max_subspace - 1cycles later. The kernels now drop a fully-exploited pinned vertex (its Roothaan image is already in the history) and re-solve — the same eviction the FIFO would do later, minus the dead cycles. Never triggers while the extrapolation carries new information, so converging trajectories are bit-unchanged.
Converged energies are unaffected (the accelerator changes the path,
not the fixed point). Regression coverage: EDIIS/ADIIS anti-replay
kernel tests (test_ediis.py), multi-k intensive-switch test
(test_periodic_multi_k_accelerator.py), and the c-diamond KRHF-GDF
stall reproducer (test_bug_repros.py).
Changed: qvf_version: 2 withdrawn — reaction.path gains an optional lattice member under v1 (2026-07-10)¶
Governance ruling (maintainer-ratified; recorded in
qvf-writer/GOVERNANCE.md § Version history and qvf-writer/registry.json).
vibe-qc v0.10.0–v0.15.x stamped qvf_version: 2 on archives containing a
periodic reaction.path. The entire schema-visible delta was a single
optional member (lattice); the companion dim field is not schema-visible
at all. Under the QVF versioning policy an optional member is the additive
case and must not bump qvf_version. The bump also created a real interop
break: the spec requires a v1-only consumer to refuse a higher major version, so
conforming third-party consumers were obliged to reject archives the reference
producer routinely wrote — and it would have spent the format’s first-ever major
bump on a backward-compatible field.
Schema:
reaction.pathnow accepts an optionallatticemember underqvf_version: 1— float64[3, 3](fixed cell) or[n_frames, 3, 3](variable cell), columns a/b/c in bohr. Its presence marks the path periodic; a path without it is unambiguously molecular. New$defs.ReactionPathLattice. Canonical schema + the byte-identicalqvf-writer/and pip-package copies.Reference writers: both
add_reaction_path()implementations (Python and C++) take an optionallattice, with shape/dtype/frame-count guardrails.Docs: spec § 3.1 (historical note), § 5.5, and the kind table;
docs/design_qvf_format.md§ 4.5 + § 5.1.Compatibility: producers MUST NOT emit
qvf_version: 2; consumers SHOULD accept it and treat it as1(archives exist in the wild).qvf_manifest_v2.schema.jsonis retained as a frozen artifact resolving its$id.
Follow-up (vibe-qc producer side, sequenced after the in-flight v2 schema
de-fork lands): stop stamping qvf_version: 2 in write_qvf, teach
validate_qvf to accept 2 as a deprecated alias for 1, and retarget the
tests that currently assert v2 emission.
Added: open governance model + registry for QVF (2026-07-09)¶
Establishes QVF as an open standard — so adopters (ORCA, PySCF, …) can trust
it as a multi-vendor interchange format — while keeping vibe-qc’s origin explicit.
qvf-writer/GOVERNANCE.md documents the stewardship model (created and stewarded
by vibe-qc; producer-neutral by design; Apache-2.0 reference impls), the
versioning policy, the change process, the vendor→canonical promotion process,
and a path to broaden stewardship as independent producers adopt it.
qvf-writer/registry.json is the machine-readable registry of canonical section
kinds (drift-guarded against the schema, test_registry.py) and registered
vendor namespaces (x_vibeqc, x_orca). Surfaced in the spec (§ 10) and on the
website (docs/qvf/governance.md).
Added: pip-installable packaging for the qvf-writer Python reference (2026-07-09)¶
qvf-writer/python/ is now a release-ready pip package (pyproject.toml):
pip install . gives the qvf_writer / qvf_reader modules (deps: numpy), a
qvf-validate console script, and a bundled JSON Schema so an installed wheel
does full validation; pip install ".[validate]" adds jsonschema. Verified by a
clean-venv install (import + write + validate + CLI). The bundled schema copy is
drift-guarded against the canonical spec (test_packaging_schema.py;
make_archive.sh refreshes it). Not published to PyPI — this only makes the
package release-ready for a maintainer go/no-go.
Fixed: NEVPT2 options reach the ASE wavefunction optimizer (2026-07-09)¶
run_job(..., method="casscf", optimize=True, optimizer_backend="ase") no
longer crashes with NameError: name 'nevpt2_options' is not defined when the
ASE wavefunction calculator samples a CASSCF/CASCI optimization step. The ASE
geometry-optimization helper now binds and forwards nevpt2_options alongside
the other wavefunction option structs, matching the native optimizer path.
Regression coverage pins both the default reproducer and explicit
NEVPT2Options(compute_corr_grad=True) forwarding. The former known-red
tests/test_runner_optimize_wavefunction_options.py is removed from the gate
baseline after a four-row OpenMolcas-backed validation matrix.
Added: reference C++ reader for the qvf-writer library (2026-07-09)¶
Completes the round-trip: qvf-writer/cpp/include/qvf/qvf_reader.hpp
(qvf::QvfReader) opens a .qvf, parses manifest.json, verifies each member’s
sha256, and decodes JSON + binary members. Still zero third-party code — adds an
original INFLATE decompressor (qvf_inflate.hpp, all three block types:
stored / fixed / dynamic Huffman), a ZIP reader (qvf_unzip.hpp), and a
JSON parser (qvf::Json::parse + read accessors). Reads any producer’s
output, verified by reading the Python-written golden corpus (dynamic-Huffman
DEFLATE) from C++. Included in the single-header amalgamation, so qvf_single.hpp
is now a complete writer and reader in one file.
Added: QVF conformance suite + golden corpus (2026-07-09)¶
A language-agnostic way to certify a QVF implementation, under
qvf-writer/conformance/: nine frozen reference archives (corpus/*.qvf) each
paired with a *.expected.json describing the values a conforming consumer must
decode (structure, the wavefunction normalization contract, spectra incl.
spectra.epr, volumes, bands, analysis, vendor extensions, root metadata). The
runner run_conformance.py validates each archive (schema + sha256 + semantic
checks), confirms its section kinds, and decodes members against the expected
values; the expected.json check format (manifest / JSON / binary pointers) is
reimplementable in any language, so ORCA, PySCF, or any consumer can self-certify.
The reference writer + reader are certified both directions in CI
(test_conformance.py: the committed corpus and a freshly regenerated one).
Fixed: BIPOLE RHF runner accepts BZ-integration metadata (2026-07-09)¶
run_periodic_job(..., method="RHF", jk_method="bipole") no longer fails
before SCF when the runner forwards its resolved bz_integration keyword to
the closed-shell BIPOLE driver. The RHF driver now accepts None and the
neutral "smearing" metadata value while keeping parameter-free Gilat
integration refused for RHF; Gilat remains a BIPOLE RKS route. This removes the
paired known-reds for tests/test_output_population.py and
tests/test_periodic_convergence_auto_runner.py.
Added: CMake install/export for the qvf-writer C++ library (2026-07-09)¶
The C++ QVF writer now installs a CMake package, so external projects can
consume it with find_package(qvf CONFIG REQUIRED) +
target_link_libraries(app PRIVATE qvf::qvf), or pull it from source via
FetchContent. Ships qvfConfig.cmake / qvfConfigVersion.cmake / exported
qvfTargets, the public headers, and the single-header amalgamation. Verified
end to end by installing to a prefix and building a separate consumer project
against it.
Added: single-header amalgamation for the qvf-writer C++ library (2026-07-09)¶
The zero-dependency C++ QVF writer is now also available as one drop-in header,
qvf-writer/cpp/qvf_single.hpp (STB-style): define QVF_IMPLEMENTATION in one
translation unit and #include it — no CMake, no separate compile, no
include-path setup. Generated from the cpp/ sources by
qvf-writer/scripts/amalgamate.py (regenerated by make_archive.sh; a drift
test, test_single_header.py, keeps the committed copy current). A
write_single_header example compiles against only the amalgamated header
(not the library) to prove it is fully self-contained.
Fixed: DLPNO-MP2 local density fitting now builds direct local 3c integrals (2026-07-09)¶
DLPNOMP2Options(local_df=True) no longer slices a precomputed global raw
three-centre DF tensor. The closed-shell DLPNO-MP2 driver now constructs a
restricted auxiliary sub-basis for each unique fit atom domain and evaluates the
2c/3c integral tensors directly on that sub-basis, while the default
local_df=False path still uses the original global RI half-transform. The
public run_job(method="dlpno-mp2", dlpno_options=DLPNOMP2Options(local_df=True, ...)) route also avoids constructing the full global DensityFitting object
and passes only auxiliary-basis metadata into the direct local path. Pins:
tests/test_dlpno_local_df.py::TestLocalDFExactness::test_local_df_builds_integrals_directly
and
tests/test_runner_dlpno_mp2.py::TestRunJobDLPNOMP2::test_local_df_run_job_avoids_global_density_fitting.
Fixed: multi-k KUKS XC now uses the per-spin real-space fold (2026-07-09)¶
Closes the open half of the 2026-07-09 periodic-KS XC-convention work
(the “do not quote multi-k KUKS numbers on tight cells” limitation, and
HANDOVER_AICCM_DIRECT_TORUS.md Finding §4’s open-shell sibling):
run_kuhf_periodic_gdf’s KS branch fed build_xc_periodic_uks the
BZ-averaged density in the home-cell block only — the C++ kernel’s
molecular-limit mode, one k-independent V_xc(Γ) added to every k; exact
only in the vacuum limit, wrong by the image density on tight cells
(+3.2e-4 Ha on the dim=3 H₂ chain (3,1,1) PBE fixture; the same class
as the fixed Γ finding). The branch now mirrors the closed-shell multi-k
path: per-spin inverse-Bloch fold of the per-k densities to the
real-space finite-torus set (_build_xc_k_from_density_uks),
spin-polarised periodic XC on the pair, per-k Bloch-folded V_a/V_b.
Why the first fold attempt failed by −2.4 Ha (the recorded blocker):
KS runs through run_kuhf_periodic_gdf defaulted options to
PeriodicRHFOptions, which carries no use_periodic_becke — so KUKS
silently built the molecular Becke grid while the run_krks_periodic_gdf
wrapper defaulted to PeriodicKSOptions (periodic-Becke grid), and a
cross-cell density fold on the molecular grid over-counts by the
replicated image density (the b3f74aa9 grid/density pairing, in
reverse). KS runs now default to PeriodicKSOptions like the
closed-shell wrapper; UHF keeps PeriodicRHFOptions.
Gates (tests/test_kuks_gdf_xc_fold.py): (a) the algebraic spin
identity of the fold helpers at complex k (no SCF, 1e-10); (b) the
KUKS(mult=1)==KRKS SCF identity on the chain at (3,1,1) PBE at the
like-for-like J backend, gated 1e-12 (measured 4.4e-16; the default
closed-shell pure-DFT route keeps the Ewald-3D real-space J while the
open-shell driver always contracts the cached GDF Lpq, a legitimate
−4.4e-5 Ha fitting-scale backend difference); (c) external out-of-process
PySCF 2.13.1 KUKS(GDF) PBE anchors — fully-spin-polarised H₂ chain
(3,1,1) with mf.nelec=(6,0) matching vibe-qc’s per-cell mult=3
(measured −1.26e-4 Ha, the XC-quadrature-scheme floor; the xc-free KUHF
twin agrees to 1.1e-9 Ha, so J/K/exxdiv are exact) and rocksalt LiH
(2,1,1) mult=1 (+2.1 mHa, the documented ultra-diffuse-Li shared-fitting
floor) with the KUKS==KRKS identity pinned on the same tight ionic cell
(2.3e-14). NB: PySCF’s cell.spin distributes the spin excess over the
whole BvK supercell (nelec (4,2) at (3,1,1)), a different state 0.40 Ha
below the per-cell-polarised one — open-shell multi-k anchors must pin
mf.nelec per cell, which is why no direct KUKS anchor existed before.
Multi-k KUKS numbers on tight cells produced before this fix should be
re-run; the loud in-code limitation markers are removed.
Changed: Γ open-shell GDF drivers default to gdf_method=’rsgdf’ + KS options (2026-07-09)¶
Completes the Γ-only compcell UHF/UKS follow-up recorded in the sanity-guard
entry below. The Γ-only compcell Hartree is vacuum-box-only by
construction (its cderi is q-only and cannot resolve overlapping AO-pair
images on tight ionic cells; the “fix” for those cells is rsgdf), so
rather than papering over the +1172.63 Ha class with convergence aids
(CLAUDE.md §7), run_pbc_gdf_uhf / run_pbc_gdf_uks now default to
gdf_method='rsgdf' — the production method the runner already dispatched
— and a KS run with options=None defaults to PeriodicKSOptions, so the
b3f74aa9 periodic-Becke-grid/torus-density XC pairing engages by default
(closing the recorded “sub-Ha class” note: plain PeriodicRHFOptions
evaluated XC in the v0.8.x molecular-grid convention, −7.9638 Ha on the
LiH fixture). Measured on the previously-broken rocksalt LiH/STO-3G Γ
fixture with pure defaults: UKS-PBE −8.233910336 (identical to the
independent real-Γ direct route −8.233910335588; 5.0 mHa from external
PySCF KRKS(GDF) PBE, the documented diffuse-Li fitting floor) and UHF
−8.330292528 (4.9 mHa class). gdf_method='compcell' stays selectable
for development and keeps the tight-ionic warning + energy-sanity guard;
run_pbc_gdf_rhf keeps its pinned compcell warn-and-tag default. The
compcell guard pins are also made robust to a machine-dependent detail:
whether the broken-Hartree SCF converges to its garbage fixed point
within max_iter varies with BLAS/threading, so the tests now assert the
§7 contract that holds either way (converged → raise; non-converged →
warn + +SANITY_FAILED tag; never a silently plausible result). Pins:
tests/test_pbc_gdf_compcell.py::test_pbc_gdf_uks_lih_ionic_default_rsgdf_is_sane
(+ the guard tests now pass gdf_method='compcell' explicitly). Docs:
docs/user_guide/periodic_methods.md § 3.4.
Added: KS-DFT on the real-Γ (direct-torus) CCM route (2026-07-09)¶
run_ccm_rks_direct (python/vibeqc/periodic/ccm/direct.py): closed-shell
KS-DFT — pure LDA/GGA/meta-GGA and global hybrids — on the k-free real
Γ-supercell route, closing the run_ccm_scf(route="real-gamma", functional=...) NotImplementedError gap. XC is built periodically by the
same fold construction as the one-electron matrices (supercell density →
cell-averaged unit-cell lattice blocks → the production build_xc_periodic
kernel on the periodic-Becke grid → V_xc(g) folded onto the torus); hybrids
apply the exchange-q=0 seam ξ_N·S·D·S on the exact-exchange channel scaled
by the functional’s a_x, the identical exxdiv="ewald" convention as the
multi-k driver, with the exxdiv=None control sitting above by exactly
a_x·ξ_N·N_e/2 per supercell (pinned). Range-separated and double hybrids
fail closed. Gates (tests/test_ccm_rks_direct.py): vacuum limit vs
molecular run_rks per XC rung, external out-of-process PySCF KRKS anchors
on ionic LiH (PBE −8.238879724246, PBE0 −8.291224931283; agreement at the
documented ~5 mHa ultra-diffuse-Li shared-fitting floor, XC-attributable
≤0.5 mHa), the seam identity, and — after the 2026-07-09 periodic-KS
XC-convention fix (b3f74aa9) plus the same-day Γ-hybrid + multi-k-dim<3
fixes (entry above) restored the KRKS reference — the three direct-vs-GDF
KS parity gates that replaced the discrepancy canaries.
Found: production KRKS XC-convention inconsistency (2026-07-09; fixed same day)¶
While gating the above: run_krks_periodic_gdf disagreed with external PySCF
KRKS by 308 mHa on rocksalt LiH/STO-3G (1,1,1) at pure PBE (no
exchange-convention freedom), and its stored density evaluated under the
piecewise-validated functional to 0.24 Ha below its reported energy (HF was
unaffected; the real-Γ KS route agrees with PySCF to the ~5 mHa HF-level
shared-fitting deviation). Escalated to the periodic-SCF/GDF chat
(handovers/HANDOVER_AICCM_DIRECT_TORUS.md Finding §4) and fixed the same
day in three parts: the XC-density/grid-convention pairing (b3f74aa9,
entry below) for the Γ-path pure KS, plus the Γ-hybrid exchange-convention
and multi-k dim<3 Hartree fixes (entry above); the discrepancy canaries in
tests/test_ccm_rks_direct.py were flipped to three direct-vs-GDF KS parity
gates.
Fixed: Γ-only GDF UHF/UKS no longer return converged non-physical energies (2026-07-09)¶
run_pbc_gdf_uks (PBE, default options/aux) returned +1172.63 Ha with
converged=True on rocksalt LiH/STO-3G (fcc a = 7.72 bohr) — external
PySCF 2.13.1 KRKS(GDF) PBE gives -8.2389 Ha. Root cause is not the KS/XC
path: run_pbc_gdf_uhf/run_pbc_gdf_rhf land at +579.83 Ha on the same
cell with no XC at all, and the Γ rsgdf lane is sane (-7.96 Ha). It is
the Γ-only gdf_method='compcell' (the API default) Hartree on tight
ionic cells — the Γ-only J (G=0 dropped) cannot resolve overlapping
AO-pair images, and the SCF converges to a fixed point of that broken
Fock (e_coulomb = +1177 Ha) — the documented limitation the RHF driver
already warns about and tags (+SANITY_FAILED,
test_pbc_gdf_rhf_production_defaults_lih_ionic); the open-shell/KS
siblings had neither the warning nor the guard and returned the garbage
silently (CLAUDE.md §7). Both drivers now (a) emit the shared
tight-ionic-cell warning (_warn_gamma_compcell_tight_ionic_cell) and
(b) run the energy-sanity guard with KRHF/KRKS semantics: a converged
non-physical energy (runaway |E| > max(10·ΣZ², 100) Ha, or unbound
E > +1 Ha) raises RuntimeError steering to the multi-k route /
gdf_method='rsgdf'; check_energy_sanity=False bypasses for
diagnostics; a non-converged run warns + tags. The shared guard also
gains the unbound criterion for the RHF caller (warn+tag mode there —
pinned behaviour unchanged). The production runner is unaffected (it
dispatches these drivers with gdf_method='rsgdf'); direct API callers
were exposed. Not fixed here (separate, pre-existing, sub-Ha class): on
the sane rsgdf lane this driver still evaluates XC in the
molecular-grid/molecular-limit-density convention when given plain
PeriodicRHFOptions (default options=None), -7.9638 vs KRKS(1,1,1)
-8.2338 on this fixture — the use_periodic_becke torus pairing of the
2026-07-09 periodic-KS XC-convention fix engages only when the options
object carries the flag (e.g. PeriodicKSOptions). [Both follow-ups
landed same day: see “Γ open-shell GDF drivers default to
gdf_method=’rsgdf’ + KS options” above — the defaults now avoid this
class entirely.] Pins:
tests/test_pbc_gdf_compcell.py::test_pbc_gdf_{uks,uhf}_lih_ionic_sanity_guard_raises,
::test_pbc_gdf_energy_sanity_guard_semantics.
Fixed: multi-k GDF no longer builds the unused EWALD_3D J cache (2026-07-09)¶
run_krhf_periodic_gdf built the iteration-invariant EWALD_3D Hartree-J
cache (make_ewald_3d_lattice_j_cache — a per-cell AO-pair FT of shape
(n_cells, nbf, nbf, n_G) on its own VIBEQC_J_EWALD3D_KE mesh)
unconditionally, although the cached-Lpq GDF branch — every multi-k
HF/hybrid run after the 2026-06-04 auto-routing — never consumes it. On
c-diamond fcc primitive / sto-3g / kmesh (2,1,1) that cache was 2.95 GB,
transiently ~5.9 GB through an out-of-place pair-scale multiply — 5.9 of
the measured 6.13 GB peak RSS, previously misattributed to the GDF fit
tensors (the four (k_i,k_j) fit builds peak at 0.09 GB each; corrected
in handovers/HANDOVER_GDF_FIT_SCREENING.md). The cache is now built
only when the EWALD_3D branch runs, and the pair-scale multiplies in
build_j_ewald_3d_ft_lattice_cache / _rsgdf_dense_pair_ft /
build_lpq_bloch_native_fft are in-place. Pin:
tests/test_periodic_k_gdf.py::test_multik_gdf_branch_skips_ewald_j_cache.
Added: Cauchy-Schwarz screening of the three-centre GDF fit (2026-07-09)¶
fit_screen_threshold on run_krhf_periodic_gdf / run_krks_periodic_gdf
/ run_kuhf_periodic_gdf (rsgdf method) and on the shared fit primitive
build_lpq_bloch_native_fft: AO pairs whose Schwarz bound
max_P sqrt((P|P))·sqrt((μν|μν)) on the fit mesh stays below the
threshold are dropped from every per-(k_i,k_j) three-centre fit tensor
(Neese et al., Chem. Phys. 356, 98 (2009) §3.1; Häser & Ahlrichs 1989;
Ochsenfeld et al. 1998 — routed as gdf_fit_screen in the citation
database). The pair-norm factor is an analytic upper bound (never
underestimates); kept/dropped pair counts are logged (no silent
truncation). Default off (0.0, exact); non-rsgdf methods and the Γ-only
fast path raise NotImplementedError rather than silently ignoring the
threshold. Every consumer of the multi-k drivers (CCM run_ccm_rhf_gdf /
run_ccm_rks_gdf via **driver_kwargs, RIJCOSX diagonal-J) inherits the
option. Also fit_pair_list on the fit primitive — the shell-pair-list
seam the space-group pair-reduction workstream composes with
(handovers/HANDOVER_SYMMETRY_PAIR_REDUCTION.md). This milestone pins
semantics + energetics (screened == unscreened to <1e-8 Ha/cell at 1e-10
on the H₂ anchor; tests/test_gdf_fit_screening.py); the compacted
pair-list kernel that converts the screen into the peak-memory reduction
is the next milestone (handovers/HANDOVER_GDF_FIT_SCREENING.md).
Changed: multi-k GDF fit build is G-chunked (bounded peak memory) (2026-07-09)¶
build_lpq_bloch_native_fft now accumulates the 2c metric and the
three-centre tensor in tail_chunk_g-point G-chunks (the same chunking
the high-|G| tail already used), so the builder’s peak transient is the
(nbf, nbf, tail_chunk_g) pair-FT block — never the full
(nbf, nbf, n_G) tensor — and the Schwarz screen
(fit_screen_threshold, above) zeroes dropped pairs per chunk. The
default chunk (65536) reproduces the previous one-pass build
bit-for-bit for meshes that fit one chunk; larger meshes log their
chunk count (no silent memory caps). M2 of
handovers/HANDOVER_GDF_FIT_SCREENING.md; pin:
tests/test_gdf_fit_screening.py::test_chunked_accumulation_matches_single_pass.
Added: vibe-view live-opt MACE engine + hardened no-vibe-qc gating (2026-07-09)¶
The Auto-optimize worker protocol now carries an engine: MSINDO (default,
unchanged) or MACE via vibeqc.mlip (the CLAUDE.md §10 MLIP exception) —
offered in a new Engine picker when the probe finds vibe-qc’s [mace] stack
(torch + mace + ase) importable. Live-opt uses only the MIT-licensed
MACE-MPA-0 default on CPU (ASL-gated academic models are not selectable), a
note protocol event surfaces model loading / first-use weight download in
the status line, and switching engines mid-session re-relaxes with the new
one. Attribution per the MLIP rule: the note and picker name the model, and
the docs state the energy is the model’s reference-shifted DFT-surface value.
The no-vibe-qc path is now pinned by tests that block the vibeqc import in
a subprocess (probe reports unavailable + zero engines; a direct optimize
request yields one parseable error event), the probe never imports torch
(find_spec only), and LiveOptService.schedule without a running event
loop reports through on_error instead of raising. MACE-path tests are
mock-based (MACE caps at Python 3.13; the repo venv is 3.14). Docs: user
guide § Building & editing (engines paragraph).
Fixed: periodic Γ-point KS drivers evaluated XC on a molecular-limit density (2026-07-09)¶
Resolves the escalated “production KRKS internally inconsistent” finding
(the aiccm-real-gamma chat, 2026-07-09; the drop-box/handover Finding §4
class): run_krks_periodic_gdf at a Γ-only mesh disagreed with external
PySCF 2.13.1 KRKS(GDF) by +308 mHa on rocksalt LiH/STO-3G (1,1,1) at
pure PBE, and its own stored density evaluated 0.24 Ha below the energy it
reported. Root cause: every Γ-point KS driver fed
build_xc_periodic(_uks) a home-cell-only density set, which the C++
kernel deliberately treats as a molecular-limit density
(ρ = χ₀Dχ₀, no image-cell AO products) — the convention that pairs with
the molecular Becke grid of the v0.8.x era. Once use_periodic_becke
became the default, Γ KS silently evaluated the isolated-molecule density
on the one-cell periodic grid: exact in a vacuum box (every vacuum-limit
gate passed), wrong by the image density on tight crystals. The fix pairs
the density-set convention with the grid: on the periodic Becke grid the
drivers now feed the Γ-torus set (D in every lattice block — the
kernel’s validated cross-cell mode and the N_k = 1 limit of the multi-k
fold); use_periodic_becke=False keeps the historical pairing. Fixed:
run_rhf_periodic_gamma_gdf (the KRKS Γ route — post-fix LiH lands at
−8.2338, 0.10 mHa from the independent real-Γ route and 5.07 mHa from
PySCF, i.e. the documented ultra-diffuse-Li shared-fitting floor;
pre-fix −7.930753392700 reproduced exactly),
run_rks_periodic_gamma_ewald3d, run_uks_periodic_gamma_ewald3d
(UKS==RKS closed-shell identity now exact to 2e-16 on the dim=3 chain
box), and the use_periodic_becke=True branch of run_pbc_gdf_uks
(NB: that driver’s default path — PeriodicRHFOptions, molecular
grid — has a separate pre-existing pathology on dense ionic cells,
+1172 Ha converged=True on LiH pre- and post-fix with no sanity
guard; flagged as its own follow-up, out of scope here). Gates:
tests/test_krks_gdf_xc_density.py (torus-set plumbing, the
Γ-vs-multi-k-fold XC identity, the external PySCF KRKS(GDF) PBE anchors
on LiH (1,1,1) and on the non-TRIM (3,1,1) dim=3-declared H₂ chain, and
the Γ-Ewald UKS==RKS closed-shell identity). KS numbers produced by the
Γ paths above on non-vacuum cells before this fix should be re-run. The
test_rks_gdf_ks_reference_discrepancy_canary pinned by the real-Γ CCM
chat trips once both changes are in a tree and is to be flipped to a
≤1e-8 Ha/cell direct-vs-GDF KS parity gate per its docstring.
Two clarifications from the same investigation. (1) The multi-k
closed-shell KS branch is not affected: it already fed the full
real-space fold, its reported energy reconstructs from its own stored
density to 5e-15, and it now carries an external anchor — vibe-qc multi-k
KRKS(PBE) on the dim=3-declared H₂ chain at the non-TRIM (3,1,1) mesh
agrees with PySCF 2.13.1 KRKS(GDF) to 29 µHa (KRHF: 5e-10). The
handover’s multi-k line item (“reported −3.0897 vs stored-density
−3.7814” on the dim=1 chain) is a cross-convention comparison: the
multi-k HF baseline on that fixture carries the exxdiv='ewald'
Madelung exchange shift that a pure-PBE KS legitimately lacks, so “KS
above HF” is the expected convention there (PySCF LiH shows the same
0.096 Ha pattern). (2) The multi-k UKS KS branch retains its
documented BZ-averaged home-cell XC shortcut (same defect class,
vacuum-limit-only): a first attempt at the closed-shell-style fold fix
failed its KUKS(mult=1)==KRKS identity gate by −2.4 Ha and was not
landed; the limitation is now marked loudly in the code — do not quote
multi-k KUKS numbers on tight cells until that follow-up lands. [That
follow-up landed same day: see “multi-k KUKS XC now uses the per-spin
real-space fold” above — the −2.4 Ha blocker was the KUKS grid-options
default, not the fold.]
Added: unified auto-reducing SCF level shift (molecular + periodic) (2026-07-09)¶
The Saunders-Hillier level shift now has one representation shared by
every molecular and periodic SCF driver. A new C++ helper
level_shift_at_iter (cpp/include/vibeqc/level_shift.hpp, exposed to
Python as vibeqc.level_shift_at_iter) is the single source of truth
for the per-iteration shift; it resolves both an auto/warm-up step
function and an explicit per-iteration decay schedule with zero
allocation and no Python round-trips inside the SCF loop.
Molecular drivers gained auto-reduction. RHF / UHF / RKS / UKS previously applied a constant
level_shiftat every iteration. They now honourlevel_shift_warmup_cycles(-1auto: shift a few startup cycles then release with an unshifted tail;0persistent, the legacy behaviour;Nexplicit) and an explicitlevel_shift_schedule, matching the periodic drivers. The default flips from persistent to-1(auto). The shift matmuls (S·D·S) are skipped entirely on iterations where the resolved shift is 0.Option-struct parity.
level_shift_warmup_cyclesandlevel_shift_scheduleare now exposed (with pybind11 bindings) on all four molecular and all three periodic options structs, with identical names and semantics.Periodic drivers share the helper. The Γ-Ewald, multi-k Ewald and GDF periodic drivers route their per-iteration shift through the same
level_shift_at_iterand now honouropts.level_shift_schedulein addition to the existing warm-up. Behaviour is byte-identical when no schedule is set.Ergonomics.
LevelShiftSchedule(now a top-levelvibeqcexport) lowers onto the C++ vector via.as_list()/.apply_to(opts);crystal_default()/crystal_aggressive()/constant()classmethods unchanged.
Converged energies are bit-identical to the no-shift result (the shift
is inert at the SCF fixed point). Pins:
tests/test_molecular_level_shift.py, tests/test_periodic_level_shift.py,
tests/test_level_shift_schedule.py.
Fixed: GFN2-xTB long-range ionic diatomics now converge (2026-07-09)¶
Long-range closed-shell ionic diatomics (LiH, LiF, NaCl) could exhaust the
GFN2-xTB SCC loop with the third-order on-site term active: the hard T=0
Aufbau occupation map bounced between neutral and charge-transfer fixed
points, so the C++ driver returned converged=False with an empty charge
vector. The default path still tries the unchanged T=0 SCC first, but
run_gfn2_xtb now has an auto_stabilize retry ladder for gamma3 cases:
smaller simple-mixing steps first, then finite electronic-temperature
occupations (electronic_temperature, Ha) as a stabilizer if the cold map
does not settle. A successful retry reports the total SCC cycles consumed
across failed attempts plus the converged attempt. Pins:
tests/test_gfn2_xtb.py::TestGFN2SCCStabilization.
Fixed: GFN2-xTB gamma-derivative force was halved (2026-07-09)¶
The same ordered-pair halving defect repaired for SCC-DFTB on 2026-07-09
was present in both GFN2-xTB analytic gradients
(compute_gfn2_gradient, compute_periodic_gfn2_gradient): the
per-atom charge-electrostatics force loop visits every ordered pair
(a,b) and deposits into grad(a) only, so the 0.5 prefactor from
E2 = ½ ΣΔqγΔq double-counted the ½ and halved the gamma force. The
derivative kernel itself (∂γ/∂R = −R·γ³) was verified correct for the
Klopman–Ohno form 1/sqrt(R² + η⁻²) used by build_gfn2_gamma —
against central finite differences of ½ Δq·γ(R)·Δq at fixed charges the
corrected term matches to ~1e-9 Ha/bohr while the pre-fix form is off
by exactly a factor 2. The stress-tensor gamma terms accumulate both
loop orders into one global tensor; their 0.5 is correct and untouched.
Pins: tests/test_gfn2_xtb.py::TestGFN2GammaGradientFD (gamma-term FD
test + implementation value pin on water).
Caveat found while validating (resolved the same day by the full
analytic gradient below): the full GFN2 gradient was then dominated by
the knowingly omitted H⁰ shape-term derivatives (CN self-energy /
EN-scaling / shell polynomial) and disagreed with central finite
differences of the SCC energy by ~0.3 Ha/bohr with the wrong sign on
water. Those derivatives have since landed (see the next entry) and the
molecular gradient is now FD-consistent. This fix repaired the one term
that was mathematically wrong within its own model rather than approximate.
Added: full GFN2-xTB analytic gradient — H⁰ shape + shell-ES + 3rd-order + AES (2026-07-09)¶
compute_gfn2_gradient now differentiates every term of the GFN2-xTB
energy functional assembled by run_gfn2_xtb (Bannwarth, Ehlert &
Grimme, JCTC 2019, doi:10.1021/acs.jctc.8b01176), closing the caveat from
the gamma-halving fix above. Added:
H⁰ shape-term derivatives (the previously-omitted piece): the CN-dependent self-energy chain Σ_A (∂E/∂CN’_A)·dCN’_A/dR, the full ∂H⁰/∂S shape factor (Wolfsberg–Helmholtz K, Slater-exponent compactness, EN scaling) folded into the M·dS/dR overlap-derivative contraction, and the shell distance-polynomial pair force. Implemented in
gfn2_h0_image_derivative_terms/gfn2_cn_chain_gradient, which differentiatebuild_gfn2_hamiltonian_zero_imagefactor-for-factor.Shell-resolved second-order electrostatics: the ES force now uses
build_gfn2_shell_gamma(matching the energy) rather than the atom-resolvedbuild_gfn2_gammastand-in.Third-order on-site term: enters through the converged Fock shell potential (no explicit geometry dependence).
Anisotropic electrostatics (AES): the kernel geometry force at fixed moments plus a Z-vector response correction required because the shipped ad-hoc AES is not variational w.r.t. the SCC Fock. The response also carries the moment-integral Pulay variation (its ∂X/∂R rebuilds the multipole integrals at the displaced geometry), so no separate Pulay term is added; doing so double-counted and broke FD agreement by the exact Pulay magnitude (~6e-4 Ha/bohr on water).
Validated against central finite differences of the total SCC energy to
< 1e-5 Ha/bohr on H₂O / CH₄ / NH₃. The periodic Γ-point gradient
compute_periodic_gfn2_gradient gains the H⁰ shape + shell-ES + AES
kernel/Pulay derivatives too but remains a fixed-charge approximation
(no AES response correction). Pins:
tests/test_gfn2_xtb.py::TestGFN2FullGradientFD (FD gate on H₂O/CH₄/NH₃
translation invariance). The
preoptimizepaths that used finite-difference GFN2 gradients are unaffected; callers may now use the analytic gradient for molecular geometry work.
Added: ADIIS_DIIS hybrid SCF accelerator (2026-07-09)¶
SCFAccelerator.ADIIS_DIIS (keyword "adiis_diis" / "adiis+diis") is the
ADIIS analogue of the EDIIS_DIIS production hybrid: it uses ADIIS (Hu &
Yang 2010) while the commutator-error norm is above
ediis_diis_switch_threshold (robust far from convergence), then switches
to plain DIIS for the asymptotic regime that ADIIS, like EDIIS, cannot
reach past the convex hull under its positive-simplex constraint. Wired
across all four molecular drivers (RHF/UHF/RKS/UKS), the C++ periodic
drivers (Γ + multi-k), and the Python periodic accelerators (Γ-Ewald +
multi-k). Reuses the existing ediis_diis_switch_threshold; reaches the
same fixed point as plain DIIS. Citation routed to hu_yang_adiis_2010.
Added: vibe-view live geometry optimization while building (2026-07-08)¶
Avogadro-parity milestone M3 (roadmap §4, B1+B2): an Auto-optimize switch
in the editor’s Atom Editor card relaxes the sketch after every edit pause —
a debounced background subprocess (python -m vibeview.live_opt) runs a
vibe-qc MSINDO semi-empirical optimization (analytic gradients, H–Xe;
scipy L-BFGS-B mirroring msindo_optimize) and streams each optimizer step
back over a JSON-lines protocol, easing the atoms toward the relaxed geometry
live in the viewport. Cancel-on-edit (a new edit kills the running worker and
reschedules), one undo entry per relax run, convergence feedback
(step / energy / max gradient, then “relaxed in N steps”), and a clean
degrade: without vibeqc importable the switch snaps back off with the
reason. Engine survey recorded in vibeview/live_opt.py: SCC-DFTB’s default
parameter set was rejected — its PES has no repulsive wall (water
dissociates downhill) and its analytic gradient disagrees with finite
differences; GFN2/PM6 have correct minima but no analytic gradient. Pins:
vibe-view/tests/test_live_opt.py (protocol, service debounce/cancel via a
fake worker, real MSINDO worker end-to-end, app wiring); trusted-event
Playwright QA vibe-view/docs/qa_live_opt.py (7/7). Docs:
user guide § Building & editing.
Fixed: vibe-view editor edits now compound (2026-07-08)¶
Sequential editor operations in vibe-view (add / delete / change-element /
fragment insert / add-hydrogens / supercell) previously each re-read the
pristine archive, so every edit silently discarded all earlier ones — adding
two atoms left only the second — and export / input generation used the
un-edited structure. QVFReader now carries a viewer-side edit overlay
(set_edit_overlay / clear_edit_overlay), written at the single
scene-rebuild point and returned by read_structure(), so every consumer
sees the same current geometry; a fresh reader (file switch, hot reload)
drops it. Explicit file bonds are dropped while an overlay is active (atom
indices are stale; auto-bonding applies). Also fixed in passing: the
change-element status message always reported “0 atom(s)”, and the
spectra.epr renderer-dispatch test was missing its synthetic fixture
(vibe-view suite red since the EPR kind landed). Pins:
vibe-view/tests/test_edit_overlay.py.
Added: C ABI + roadmap for the qvf-writer library (2026-07-09)¶
The zero-dependency C++ QVF writer now exposes a stable C ABI
(qvf-writer/cpp/include/qvf/qvf_c.h + src/qvf_c.cpp) so C, Fortran (via
ISO_C_BINDING), Rust, Julia, and other languages can emit .qvf archives
without a C++ interface. All state lives in one opaque qvf_writer handle;
nested payloads (provenance, NMR/EPR spectra, vendor sections) pass as JSON
strings, emitted verbatim via a new qvf::Json::raw node, so no JSON parser
crosses the boundary. Ships a runnable pure-C example (write_h2o_c.c), a
C-ABI unit smoke test, and CMake wiring. Also adds qvf-writer/ROADMAP.md
(conformance corpus, packaging, endianness, governance, second producer) and a
“C and Fortran” section to the library guide.
Added: code-agnostic QVF integration guide (2026-07-09)¶
New qvf-writer/docs/integration_guide.md (surfaced on the website under the
QVF section): a producer-neutral how-to for adopting QVF in any
quantum-chemistry code — the two integration models (in-process vs
post-processor), the data-category → section-kind mapping table, the
wavefunction normalization/AO-ordering contract, periodic notes, the vendor →
canonical promotion path, and how a code becomes an officially supported
producer (with vibe-view support). The ORCA guide is reframed as the concrete
worked example of it; includes a PySCF-oriented note.
Added: DEFLATE compression in the qvf-writer C++ library (2026-07-08)¶
The zero-dependency C++ QVF writer now DEFLATE-compresses archive members
(original RFC-1951 codec in qvf-writer/cpp/src/qvf_deflate.hpp: LZ77
hash-chain match finder + a fixed-Huffman block), falling back to STORE when a
member does not shrink and always STORE-ing manifest.json for cheap
table-of-contents reads. Still zero third-party code. Verified decompressible
by unzip, Python zipfile, and the sha256 integrity check in vibe-qc’s
validate_qvf (byte-exact inflate); binary grids compress to a few percent.
Added: vibe-view desktop auto-update plumbing (2026-07-08)¶
The vibe-view Electron app now bundles electron-updater and reads a real
update feed at https://vibe-qc.com/updates/vibe-view/ (electron-builder
publish config, baked into app-update.yml). build-desktop.sh produces the
dmg/zip/blockmaps + latest-mac.yml, verifies the updater is in the app.asar,
and can rsync the feed to the (ISPConfig-managed) host via
VIBEVIEW_UPDATE_RSYNC_TARGET. “Check for Updates” now performs a real check.
See vibe-view/electron/PUBLISHING.md.
macOS self-install is gated on code signing: unsigned builds can detect, download, and notify, but Squirrel.Mac will not apply an update without an Apple Developer ID cert + notarization (a pending maintainer-infra decision). Scope: mac-arm64 built + published today; Linux feed config is wired but needs a Linux build host; Windows is a follow-up.
Added: vibe-view desktop first-run setup screen (2026-07-08)¶
The vibe-view desktop app (vibe-view/electron/) now shows an in-app
setup screen when it can’t find a Python capable of running the viewer
server, instead of a dead-end error dialog followed by the app quitting.
The screen classifies the machine into one of three cases — no Python, a
Python without vibe-view, or vibe-view without the [viewer] extra — names
exactly what to install (with a Copy button and a python.org pointer when no
Python exists), and offers a recheck button that re-scans and launches the
server in place once the user has installed it, with no restart. The install
command creates a dedicated virtualenv rather than a bare
pip install, so it works on Homebrew/Debian/Fedora system Pythons that
reject pip with externally-managed-environment (PEP 668); the app
discovers that managed venv (at $XDG_DATA_HOME/vibe-view/venv)
automatically, so recheck finds it with no extra step. New files: preload.js
(a minimal
contextBridge IPC surface) and setup.html (the onboarding page); both
are added to the electron-builder files whitelist so they ship in the
packaged app. Interpreter discovery is platform-aware (the py launcher +
.venv\Scripts\python.exe on Windows, python3 + bin/python3 on
macOS/Linux). Windows is in scope but shipped untested — the maintainers
have no Windows machine, and the nsis installer is unsigned (SmartScreen
warns); macOS and Linux are the verified targets. On its first launch on
Windows the app shows a one-time notice that the build is untested and asks
for feedback (a “Send feedback” button opens the issue tracker). This is the
graceful-onboarding half of the “downloadable,
no-heavy-prereq” desktop packaging effort; a full self-contained Python
freeze was evaluated and deferred to the prompt-to-install model (see
vibe-view/docs/desktop_packaging_design.md). Documented in
docs/user_guide/vibe_view_desktop.md § First run.
Added: canonical spectra.epr QVF kind (EPR g/A/D tensors) (2026-07-08)¶
New canonical QVF section kind spectra.epr, paralleling spectra.nmr: a
single object-shaped spectrum JSON member carrying the EPR g-tensor,
per-nucleus hyperfine (A) tensors, and the zero-field-splitting (D) tensor
(g dimensionless; A / D in MHz; payload shape intentionally loose in v1).
Promoted to canonical by maintainer decision ahead of the usual two-producer
threshold (design_qvf_format.md § 5.7) to give ORCA a canonical target for its
EPR output instead of a vendor section. Wired end to end:
Schema: new
SectionSpectraEPRbranch inqvf_manifest.schema.json(canonical + the byte-identicalqvf-writer/copy); documented indocs/design_qvf_format.md§ 4.4.Writer:
spectra.eprin_IMPLEMENTED_KINDS+ a dormant_write_spectra_epr_sectiondriven by theepr_datacontext key (vibe-qc does not compute EPR yet; same pattern as the phonon / EOS writers).vibe-view:
spectra.eprrenderer (renderers/epr.py) with a g-tensor / hyperfine / zero-field-splitting panel; registered inSUPPORTED_KINDS, the renderer map, and the app activation path.Toolkit: both reference writers accept
spectra.epr; the ORCA-integration guide and adoption tutorial now present EPR as canonical.
Added: qvf-writer/ — standalone QVF writer toolkit for external adopters (2026-07-08)¶
New top-level qvf-writer/ directory: a self-contained, Apache-2.0
distribution of the QVF (Quantum Visualization Format) producer contract, so
third-party quantum-chemistry codes (initial adopter: ORCA) can emit .qvf
archives without depending on vibe-qc. Landed incrementally; this milestone
(M1) ships the scaffold, licensing, and the standalone normative spec:
qvf-writer/spec/qvf-format-spec.md— normative, self-contained QVF v1 specification (archive model, manifest, section-kind catalog, units, extension governance, producer/consumer conformance, and an AO-ordering + primitive-normalization appendix forwavefunction.gto).qvf-writer/spec/qvf_manifest.schema.json— byte-identical copy of the canonical schema (drift-guarded).Apache-2.0
LICENSE+NOTICE; the subtree license is recorded indocs/license.md§ 3h. The C++ library has zero third-party dependencies — ZIP (STORE), CRC-32, SHA-256, and JSON emission are original Apache-2.0 code, so there is no vendored-code licensing surface.
Reference writers (Python + zero-dependency C++17 library), library docs, an
ORCA-integration guide, a “how to adapt QVF + implement a writer” tutorial, a
dedicated website section, and a downloadable archive follow in later
milestones on this branch. Nothing under python/vibeqc/ or cpp/ imports the
subtree; it is standalone.
Fixed: SOSCF is now a proper quasi-Newton (L-BFGS) accelerator (2026-07-08)¶
The Phase D2d SOSCF step (cpp/src/soscf.cpp) was a memoryless single
diagonal augmented-Hessian step (κ = −g/(ε_a − ε_i)): it built no
curvature and, for Kohn-Sham DFT, oscillated near convergence because the
diagonal Hessian ignores the (large) XC-kernel response — an SOSCF-enabled
RKS/PBE H2O run failed to converge in 100 iterations. SOSCF is now the
quasi-Newton method Neese (2000) actually describes: the diagonal orbital
Hessian is only the initial preconditioner H0, and an L-BFGS two-loop
recursion accumulates the true curvature (including the XC coupling) from
lbfgs_history (default 10) pairs of (orbital-rotation step, gradient
difference). New stateful SOSCF / UHFSOSCF classes replace the
stateless per-iteration step in all four molecular drivers. With an empty
history the first step reduces exactly to the old diagonal step, so it is
a strict generalisation. Result: SOSCF-enabled RHF/UHF/RKS/UKS all
converge to the correct energy (RKS/PBE 100→10 iterations). SOSCF remains
opt-in (soscf_threshold = 0.0 by default): even after the L-BFGS
fix it does not robustly accelerate every system (it still stalls on
porphine RHF/STO-3G), so it is offered as a much-improved opt-in finalizer
rather than a default.
Changed: molecular + periodic SCF defaults tuned toward ORCA/CRYSTAL (2026-07-08)¶
SCF option defaults now track the out-of-the-box convergence profile of ORCA (molecular) and CRYSTAL (periodic), without changing any converged energy:
Molecular (
RHFOptions/UHFOptions/RKSOptions/UKSOptions):dynamic_dampingnow defaults totrue(Zerner-Hehenberger adaptive damping;dampingstill starts at 0.5 and relaxes toward 0.0 on monotonic descent, so easy systems pay nothing and oscillating ones are stabilised). Verified benign: iteration counts are unchanged on well-behaved cases and it only acts when the SCF oscillates.Periodic (
PeriodicRHFOptions/PeriodicSCFOptions/PeriodicKSOptions):scf_acceleratornow defaults toEDIIS_DIIS(the production hybrid, wasDIIS) anddynamic_dampingnow defaults totrue(charge-sloshing-prone periodic SCF benefits most).
Unchanged: molecular scf_accelerator (EDIIS_DIIS), soscf_threshold
(0.0, opt-in), damping (0.5), molecular guess (PATOM), periodic
guess (SAD). Periodic SOSCF is not added (the periodic structs have no
soscf_threshold and the multi-k loop lacks the orbital-rotation
machinery). Level shift and smearing stay at 0.0 (opt-in per system).
ROHF/ROKS inherit RHFOptions / RKSOptions.
The benchmark generator (qc-input-library/scripts/gen_inputs.py) no
longer overrides damping = 0.0, which had defeated the adaptive-damping
default.
Added: explicit aux_basis override for DLPNO methods (2026-07-08)¶
The DLPNO-CCSD / CCSD(T) option dataclasses (LocalCCSDOptions,
DLPNOCCSDPilotOptions, DLPNOUCCSDPilotOptions) gained an aux_basis
field. When set, the runner uses that RI fitting basis instead of
auto-resolving one from the orbital basis. This unblocks DLPNO on orbital
bases that ship no default RI aux (minimal / Pople bases such as sto-3g,
6-31g*): previously such jobs raised NotImplementedError from
default_aux_basis_for(..., kind="ri") with no way to supply an aux on the
DLPNO path (only the plain CCSD path exposed the knob). aux_basis=None
keeps the existing auto-resolution behaviour, so default runs are
unchanged. The default_aux_basis_for error message now names the cause
(minimal / Pople bases have no standard fitting aux) and points at the
option-object override.
Added: adaptive-depth commutator-DIIS accelerators (R-CDIIS + AD-CDIIS) (2026-07-08)¶
Two new scf_accelerator choices implementing the adaptive-depth DIIS
variants of Chupin, Dupuy, Legendre & Séré, Convergence analysis of
adaptive DIIS algorithms with application to electronic ground state
calculations, ESAIM: M2AN 55, 2785 (2021),
doi:10.1051/m2an/2021069:
SCFAccelerator.R_CDIIS(keyword"r_cdiis"): restarted CDIIS (their Algorithm 3): the history depth grows until the stored error differences become nearly linearly dependent, then restarts. Restart aggressiveness is the new optiondiis_restart_tau(τ ∈ (0,1), default 1e-4).SCFAccelerator.AD_CDIIS(keyword"ad_cdiis"): adaptive-depth CDIIS (their Algorithm 4): the window retains only recent iterates whose residual is within1/δof the current one, so the depth shrinks smoothly near convergence. Controlled by the new optiondiis_adaptive_delta(δ > 0, default 1e-4).
Both reuse Pulay’s commutator-DIIS extrapolation unchanged; only the
retained (F, error) window differs, implemented as a DIISDepthPolicy
on the single canonical C++ DIIS class (cpp/src/diis.cpp). Available
on every backend that supports DIIS: molecular (RHF/UHF/RKS/UKS),
C++ periodic (Γ + multi-k), the Python Γ-Ewald accelerator, and the
Python multi-k accelerators (_MultiKPulayDIIS carries the policy via a
√wₖ-scaled per-k error flattening so the restart projection / residual
tests reuse the C++ Euclidean formulas). Citation wired
(chupin_adaptive_diis_2021); user guide + keyword index updated.
Fixed¶
vibe-view desktop “Check for Updates” no longer silently does nothing. The menu item ran
autoUpdater && autoUpdater.checkForUpdatesAndNotify(), a no-op becauseelectron-updateris not installed (autoUpdateris null). It now shows an actionable dialog explaining how to update by hand (git pull + rebuild, or reinstall the dmg), and still triggers a real check once electron-updater is wired.vibe-view desktop tray icon no longer claims the whole menu-bar area. The macOS
Traywas constructed from the full 512x512 appicon.png, so it rendered oversized in the status bar.main.jsnow resizes it to a 22px menu-bar image (nativeImage.resize), with an empty-icon fallback.
Fixed¶
SCC-DFTB no longer dissociates molecules downhill; DFTB band filling now counts valence electrons only. Two independent defects in the DFTB family (found 2026-07-08 by the vibe-view live-optimization chat: water’s SCC-DFTB energy fell monotonically with O–H stretch — no repulsive wall, no minimum — and the analytic gradient disagreed with finite differences in sign and magnitude):
The SCC Hamiltonian correction was applied with the wrong sign for the code’s Mulliken-charge convention (Δq > 0 = cation), turning charge screening into self-amplifying anti-screening: water converged to O = −1.36 e / H = +1.7 e and the second-order energy grew without bound with stretching. The correction is now
H¹ = −½S(V_A+V_B)(Elstner et al., PRB 58, 7260 (1998), Eq. 14, sign-adapted), and the total energy is assembled variationally asE = tr(D H⁰) + ½ΣΔqγΔq + E_rep. With the variational assembly the fixed-charge analytic SCC gradient is exact at convergence;compute_scc_dftb_gradient_responsenow delegates to it and the CP-SCC finite-difference Z-vector machinery is retired.Band filling used
mol.n_electrons()/2, counting CORE electrons into a valence-minimal basis (H₂O: 5 pairs instead of 4). This over-filled every DFTB variant, broke the Mulliken neutrality reference (ΣΔq = −n_core, hidden by an unconditional recentre), and produced symmetry-broken charges on equivalent atoms. All DFTB0 / SCC-DFTB runners (molecular, unrestricted, periodic Γ, k-point) now fill Σ n_val(A) − charge electrons; the Mulliken recentre is charge-aware.SCCDFTBResult.e_electronicis nowtr(D H⁰)(was Σnε) andenergy = e_electronic + e_scc + e_repulsive(e_scc was previously subtracted); reference energies re-pinned. Water now has an interior O–H minimum near 1.8 bohr with a repulsive wall for both DFTB0 and SCC-DFTB, andpreoptimize_moleculeactually relaxes distorted geometries. Pins:tests/test_semiempirical_pes.py::TestDFTBWellShapes,tests/test_semiempirical_dftb0.py::TestPreoptimization::test_preoptimize_improves_distorted_water, tightenedTestSCCDFTB::test_scc_analytic_vs_fd.
[v0.15.30] - 2026-07-07 - Neese’s Cheetah¶
Changed¶
ROHF/ROKS Roothaan SCF now supports the accelerator family. The Python Roothaan loop accepts EDIIS, ADIIS, EDIIS+DIIS, and KDIIS-style accelerator selections through
ROHFOptions.scf_accelerator, matching the convergence-control surface used by the production RHF/UHF/UKS drivers. The porphine RHF default-convergence regression is also pinned so the large default-SCF lane keeps its current basin.
[v0.15.29] - 2026-07-07 - Neese’s Cheetah¶
Changed¶
vq programs can gate managed venv commit identity.
vq programs --require-sha NAME=SHAnow fails when a program’scurrent_git_shadoes not match the requested commit or short SHA prefix.vq programs --require-version NAME=VERSIONsimilarly gates the imported runtime version. Pair both with--require-branchand--require-cleanfor fleet update and docs artifact preflights.vq cleanup reclaims scheduler-side workspaces. Auto-cleanup now removes duplicate remote scheduler workspaces at archive age after terminal results have been copied back locally, stamps
scheduler_remote_workspace_cleaned_aton the spec, retries archived specs whose remote cleanup failed, and skips final delete if the scheduler-side workspace cannot be cleaned.
Fixed¶
ROKS/ROHF frontier degeneracies now get averaged occupations. Open-shell restricted references now assign fractional occupation across degenerate frontier manifolds instead of selecting an arbitrary component from a symmetry-equivalent set. This keeps spin-pure ROHF/ROKS starts symmetric for cases such as atomic p shells and avoids artificial anisotropy in the SCF log and downstream ROKS functional routes. Pins:
tests/test_rohf.pyandtests/test_roks.py.CASPT2/NEVPT2 correlation gradients now differentiate the reported PT2 energy. For single-state CASSCF-referenced CASPT2/NEVPT2,
compute_corr_grad=Truenow returns a relaxed full-energy central finite-difference gradient through the runner instead of adding the historical semi-analytic Z-vector shortcut to a frozen correlation derivative. This removes the mHa/bohr-scale mismatch seen in the v0.15 validation matrix. Pins:tests/test_casscf_gradient.py::TestCASPT2ZVectorRunner::test_corr_gradient_matches_relaxed_fdandtests/test_casscf_gradient.py::TestNEVPT2ZVectorRunner::test_corr_gradient_matches_relaxed_fd.vibe-view desktop app: double-click on a
.qvfnow starts the server. Two bugs invibe-view/electron/main.js. (1)findPython()picked the firstpython3on PATH that answered--version, even whenvibeviewwas only installed in a virtualenv, so the spawned server died withModuleNotFoundErrorand the app showed “The vibe-view server did not start.” It now verifiespython -c "import vibeview"and reads a persistent interpreter record thevibe-view desktopcommand writes to the shared cache (<XDG_CACHE_HOME>/vibe-view/interpreter.json), so a plain double-click (which sets noVIBEVIEW_DESKTOP_CONFIG) reuses the venv vibe-view lives in; when no capable interpreter exists it shows an actionable dialog instead of a doomed spawn. (2) A separate stale-build crash on launch (Cannot find module 'electron-updater') was resolved by rebuilding from the current source, whose require is already guarded; regenerated the 2.1.0 dmg/zip and bumpedelectron/package.jsonto 2.1.0.Gamma RSGDF auto-tails sparse tight-core boxes. The production Gamma RSGDF tail selector now keys off the steepest AO primitive, not only compact cell density, so molecular-limit O/Ne/Mg-style STO-3G boxes get the same high-|G| AO-pair support as compact ionic cells. This fixes the OH/STO-3G open-shell UKS/GDF Coulomb under-resolution seen in the periodic open-shell QVF validation matrix. Pins:
tests/test_rsgdf_dense_mesh_tail.py::test_sparse_tight_core_rsgdf_auto_tail_lifts_default_hold.
[v0.15.28] - 2026-07-05 - Neese’s Cheetah¶
Changed¶
vq programs reports venv checkout branch and dirty state. Venv program JSON records now include
current_git_branchandcurrent_git_dirty, and the human detail line notes branch plus dirty checkouts. Fleet update chats can now tell whether a managed checkout is on the expected branch and whether local modifications may explain update drift.vq programs --require-clean NAMEturns that dirty-state probe into a pass/fail gate for managed venv programs, andvq programs --require-branch NAME=BRANCHfails when the live checkout is on the wrong branch.
Fixed¶
ROKS now supports meta-GGA tau and range-separated hybrid exchange. The pure-Python ROKS Fock builder now mirrors the native UKS/RKS conventions for tau-dependent XC, VV10 nonlocal correlation, and CAM/RSH exact exchange (
cam_alpha*K + cam_beta*K_erf). This lifts the global ROKS meta-GGA/RSH gate: TPSS/r2SCAN-style routes, omega-B97X, and omega-B97M-V-style routes run as spin-pure ROKS, while direct ROKS double-hybrid calls still point torun_double_hybridbecause the SCF energy alone is incomplete. Open-shell PWPB95 now reaches the dispatcher path (ROKS SCF + semicanonical ROHF-MP2). Pins:tests/test_roks.pyandtests/test_pwpb95.py::test_run_pwpb95_open_shell_roks_mgga.BIPOLE runner cutoff controls now reach the BIPOLE drivers.
run_periodic_job(..., jk_method="bipole")acceptsbipole_cutoff_bohrandbipole_nuclear_cutoff_bohr, forwarding them to the direct-lattice electronic and nuclear BIPOLE sums instead of leaving validation scripts stuck on driver defaults. The default BIPOLE RKS path also keeps the nuclear/Ewald real cutoff coherent with the electronic J/K cell set, removing the water/PBE0/6-31G molecular-limit overbinding from the v0.15 validation artifact.cutoff_haremains the GPW/GAPW plane-wave grid cutoff. Pins:tests/test_periodic_feature_input_guards.py::test_run_periodic_job_forwards_bipole_cutoffs,tests/test_bipole_fock_ewald_exchange.py::test_run_periodic_job_bipole_rks_h2o_pbe0_default_cutoffs_match_pyscf.v2RDM now returns quantitative small-active-space energies.
solve_v2rdmno longer uses the fragile P-only augmented-Lagrangian projector that could converge far above FCI while reporting success. The public route now diagonalises the in-tree determinant Hamiltonian and contracts the exact N-representable 1-/2-RDM, so H2/STO-3G matches FCI to roundoff. Requests containing Q/G constraints still raise explicitly until real SDP feedback is implemented. Pins:tests/test_solvers_v2rdm_tc.py,tests/test_solvers_v2rdm_constraints.py, andtests/test_bug_repros.py::test_v2rdm_run_job_exact_small_space_converges.
[v0.15.27] - 2026-07-05 - Neese’s Cheetah¶
Added¶
Closed-shell QCISD / QCISD(T) now runs through the CC variant ladder.
run_job(method="qcisd"),run_job(method="qcisd(t)"), andmethod="ci", citype="qcisd"/"qcisd(t)"route through the RHF canonical CC kernel withcc_variant="qcisd". The residual is selected by exact two-parameter monomial projection from the validated CCSD equations, and QCISD(T) uses the original Pople-Head-Gordon-RaghavachariE[T] + 2*E_STtriples convention. Pins:tests/test_cc_variants.py,tests/test_runner_ccsd.py, andtests/test_runjob_cc_ci_surface.py.
Changed¶
vq programs fleet JSON is now parseable as one document.
vq programs --all --jsonnow emits a single top-level object keyed by host, matchingvq daemon ping --all --jsonandvq doctor --all --json, instead of bannered per-host JSON fragments. Fleet managers can now auditvibeview-devor runtime drift across the queue with ordinary JSON tooling.vq programs can enforce required managed tools.
vq programs --require NAMEnow exits non-zero whenNAMEis absent or notOK; with--allit checks every configured host. Usevq programs --all --require vibeview-devas the human gate for a queue-wide vibe-view capture install, or combine it with--jsonfor a parseable host-keyed inventory on stdout plus failures on stderr. During managed-program renames,--require-any vibeview-dev,vibe-viewaccepts either OK handle on each host while the fleet converges on the standard name.
[v0.15.26] - 2026-07-05 - Neese’s Cheetah¶
Added¶
vq venv programs expose stable runtime handles to payloads. Jobs submitted with
--program NAMEnow receiveVQ_PROGRAM_BIN,VQ_PROGRAM_PYTHON,VQ_PROGRAM_GIT_DIR, and optionalVQ_PROGRAM_BRANCHwhenNAMEis akind = "venv"program. This lets docs and vibe-view jobs call managed tools such as"$VQ_PROGRAM_BIN/vibe-view"without hard-coding per-host install paths, while keeping--programfrom rewriting commands orPATH. Pins:vibe-queue/tests/test_daemon.pyandvibe-queue/tests/test_daemon_scheduler.py.Open-shell Python periodic SAP/PATOM/HUECKEL/MINAO initial guesses.
InitialGuess.SAP,InitialGuess.PATOM,InitialGuess.HUECKEL, andInitialGuess.MINAOnow reach the Python periodic UHF/UKS drivers. Gamma-Ewald, multi-k Ewald, GDF, and BIPOLE pass the periodic system and lattice options into the shared helper for SAP/HUECKEL/MINAO; PATOM seeds from SAD and performs one route-local HF-like in-field J/K step before SCF. Pins:tests/test_periodic_open_shell_guesses.py.Multi-k RHF/RKS/UKS
SLAB_EWALD_2DSCF is now active. Dense slab meshes route through rigorous per-k slabV_ne(k)and per-cell slab HartreeJ(g)kernels sharinglattice_opts.slab_ewald_alpha; the Gamma-only slab routes remain on their dedicated drivers. Pins:tests/test_periodic_rhf_dispatch.py::test_slab_ewald_2d_multi_k_routes_through_dispatcher,tests/test_periodic_ks_dispatch.py::test_slab_ewald_2d_multi_k_routes_through_dispatcher,tests/test_periodic_uks_multi_k_ewald.py::test_slab_ewald_2d_multi_k_closed_shell_uks_matches_rks, plus the multi-k slab V_ne/J parity checks.vq scheduler hosts can apply per-program qsub hooks/wrappers.
[hosts.HOST.scheduler_program_hooks.NAME]adds trusted prologue/epilogue shell lines and an optionalcommand_wrapperargv prefix to generated scheduler scripts only for jobs submitted with--program NAME, ordered inside the host-level scheduler hooks and around the wrapped user command while preserving the existing exit-marker wrapper. Pins:vibe-queue/tests/test_config.py,vibe-queue/tests/test_scheduler_dispatch.py, andvibe-queue/tests/test_daemon_scheduler.py.vq venv programs can declare runtime healthchecks.
[programs.NAME].healthcheck_commandletsvq programsrun a short command from the program checkout after the basic interpreter/import probes, so headless tools such asvibeview-devcan verifyvibe-view capture-selftestbefore accepting capture jobs. The newscripts/update_vibeview_capture_env.shprovisions the lean capture-only vibe-view venv through the normalvq admin updatepath (pip install -e vibe-view/, without the optional trame web stack). The recommended healthcheck isxvfb-run -a vibe-view capture-selftest; the managed venv can provide a localxvfb-runshim that falls back toPYVISTA_OFF_SCREEN=Truedirect VTK/OSMesa rendering on hosts where no systemxvfb-runis available in the queuePATH. Pins:vibe-queue/tests/test_programs.py.vq’s ORCA contributed wrapper is regression-pinned and documented.
contrib/run-orca.shnow has fake-binary coverage forORCA_BIN, PATH fallback, stdout-to-.outredirection, success scratch cleanup,--keep-scratch, custom output filenames, and missing-binary diagnostics. Queue onboarding recipes now submit ORCA through the wrapper so fetched workspaces carry the expected output file. Pins:vibe-queue/tests/test_run_orca_sh.py.
Fixed¶
vq scheduler host config now fails fast on path/scope mistakes. Scheduler-only fields such as
scratch_root,node_scratch_dir,remote_scheduler_host,scheduler_driver, andsubmit_extraare rejected on normal local hosts; scheduler hosts now require an absolutescratch_rootand non-empty single-line scheduler path/command strings. The sample TOML also keeps scheduler update keys under[hosts.HOST]instead of below a nested program-hook table. Pin:vibe-queue/tests/test_config.py.vq programshealthcheck timeouts now keep the last probe line. Timed-out[programs.NAME].healthcheck_commandruns report the last captured stdout/stderr line, so headless capture failures keep their most useful diagnostic instead of only sayingtimed out after 60s. Pin:vibe-queue/tests/test_programs.py.Removed an unsafe orphaned PT2 finite-difference mapper. The private
gradient._parallel_helpers.parallel_dE2dkprocess-pool path was unused by current CASPT2/NEVPT2 code and copied fullnmo**4tensors once per orbital pair if called. The module now exposes only the memory-capped PT2 worker sizing helpers used by the live Z-vector gradient routes. Pins:tests/test_pt2_parallel_limits.py.Dense-core Gamma RSGDF now auto-resolves its high-G Coulomb tail. The Γ-only pure-GDF drivers choose a parity-sized
rsgdf_tail_ke_cutofffor dense-core STO-3G-class ionic cells when the caller leaves the cutoff unset, so the default regression-suite solid lane proceeds through the resolved RSGDF tail instead of being statically held. Explicit undersized tails, including0, remain available as diagnostic held runs. The C++ general-L AO-pair FT also mirrors symmetric Gamma shell-pair blocks for the high-G tail, avoiding duplicate work. Pins:tests/test_rsgdf_dense_mesh_tail.py,tests/test_regression_runner_vibeqc.py, andtests/test_regression_suite_output.py.NEB dry-run preflight now charges image parallelism and warm-start storage.
run_neb(..., dry_run=True)andVIBEQC_DRY_RUN=1now write a minimaljob_kind="neb"manifest, andVIBEQC_DRY_RUN_ESTIMATE=1records a NEB peak-memory estimate for Gaussian SCF routes. The estimate includesn_jobs * per_image_peak, retained warm-start density matrices, band coordinate/force arrays, and the periodic6N+1finite-difference gradient scratch sovq submit autocan place NEB jobs by RAM. Pins:tests/test_memory.py,tests/test_neb_driver.py, andtests/test_neb_periodic.py.Periodic DFT-gradient dry-run preflight now carries the XC-gradient peak.
run_periodic_job(..., method="RKS"|"UKS", optimize=True)records a periodic analytic XC-gradient estimate underVIBEQC_DRY_RUN_ESTIMATE=1, including full-grid AO/gradient/Hessian tables, density blocks, per-thread pair scratch, and grid-vector workspace for the current unbatched kernel. Pins:tests/test_memory.pyandtests/test_periodic_feature_input_guards.py.GPW/GAPW dry-run preflight now charges FFT-grid cache peaks. Explicit
run_periodic_job(..., jk_method="gpw"|"gapw")dry-runs record the GPW full collocation cache, reciprocal mesh, density/Poisson/XC workspaces, compact multi-k Bloch AO tables, and GAPW soft/analytic augmentation caches underVIBEQC_DRY_RUN_ESTIMATE=1. Pins:tests/test_memory.pyandtests/test_periodic_feature_input_guards.py.TDDFT/CIS memory preflight now includes the dense response solve. Molecular
run_job(..., tddft=True)dry-run and live estimates charge the AO ERI tensor, ovov/oovv spin blocks, dense A/B response matrices, eigensolver workspace, retained state vectors, and f_xc kernel blocks beforevq submit autoplaces the job by RAM. Pins:tests/test_memory.pyand the dry-run producer tests intests/test_runner.py.Scheduler-host admin updates now keep the live marker fresh.
vq admin update <scheduler-host>refreshes the admin-update marker while a long remote provisioning/update command is still running, using the same heartbeat cadence as venv builds. Operators pollingvq admin statusnow see a recent heartbeat for cluster-side update commands instead of only the phase transition timestamp.vq overviewnow carries admin-update marker diagnosis. Fleet scans show the marker status, PID diagnosis, summary, and heartbeat line in text output, andvq overview --jsonserializes those host-side diagnosis fields so fleet/twin management chats do not need to re-probe remote PIDs.Remote
vq admin updatewaits through long compiles. Delegated venv and scheduler-host updates now use a maintenance-sized outer SSH timeout (default 7200 s, configurable withVQ_REMOTE_ADMIN_UPDATE_TIMEOUT) instead of the generic 600 s remote-vq cap, so the local wrapper does not report a timeout while the remote update marker is still live and heartbeating.vq submit --expected-shanow snapshots the validated checkout. The per-job docs-artifact guard requires at least 7 hex characters and stores the host-validated canonical 12-character SHA inprogram_runtime_pin, so dispatch-time drift diagnostics identify the exact checkout even when the operator supplied a shorter prefix.vq doctornow catches scheduler hook registry drift. Scheduler-host preflights fail when[hosts.HOST.scheduler_program_hooks.NAME]names do not match a configured[programs.NAME]entry, so typoed Twin/Fleet qsub hooks are found before a job is submitted. Pin:vibe-queue/tests/test_scheduler_admin_update.py.The ORCA wrapper now rejects unsafe argument shapes.
contrib/run-orca.shfails before launching ORCA when extra positional arguments are present or when the requested output path would overwrite the input file. Pins:vibe-queue/tests/test_run_orca_sh.py.The CRYSTAL wrapper now rejects unsafe argument shapes.
contrib/run-crystal.shfails before launching CRYSTAL/PROPERTIES when extra positional arguments are present or when the requested output path would overwrite the input file. Parallel mode now also accepts a user input file already namedINPUTwithout moving it aside as a temporary staging file. Pins:vibe-queue/tests/test_run_crystal_sh.py.Dense-core Gamma GDF parity holds now reach RKS/UKS validation rows. UHF/UKS GDF results carry the same
+PARITY_HELDbackend marker as RHF when the tight-cell dense-core Gamma route lacks enough high-G support, and the regression harness reports those rows, plus the known STO-3G Gamma RKS-LDA solid validation lane, as unavailable instead of treating whole-Hartree absolute-energy offsets as ordinary parity failures. Pins:tests/test_periodic_rhf_gdf.pyandtests/test_regression_runner_vibeqc.py.
[v0.15.25] - 2026-07-04 - Neese’s Cheetah¶
Added¶
Gamma-only RHF/RKS/UKS
SLAB_EWALD_2DSCF is now active.run_rhf_periodic_gamma_scf(..., CoulombMethod.SLAB_EWALD_2D)andrun_rks_periodic_gamma_scf(..., CoulombMethod.SLAB_EWALD_2D)route to the new slab drivers; UKS is available directly throughrun_uks_periodic_gamma_ewald2d. All three use one sharedlattice_opts.slab_ewald_alphasplit for the bare 2D-Ewald nuclear block, slabV_ne, and Ewald-split slab HartreeJ; HF and hybrid exchange remain the full-range Gamma molecular-limit build. In v0.15.25, multi-k slab routes and analytic slab gradients still raised cleanly; multi-k slab SCF is un-gated in[Unreleased]. Pins:tests/test_periodic_rhf_dispatch.py(test_dispatcher_routes_slab_ewald_2d_gamma,test_slab_ewald_2d_gamma_polar_slab_vacuum_invariant),tests/test_periodic_ks_dispatch.py(test_slab_ewald_2d_gamma_routes_through_dispatcher),tests/test_periodic_uks_ewald.py(test_slab_ewald_2d_h_atom_converges_with_correct_spin,test_slab_ewald_2d_closed_shell_uks_matches_rks), plus the slab E_nn, V_ne, and J suites.
[v0.15.24] - 2026-07-04 - Neese’s Cheetah¶
Added¶
Closed-shell Python periodic SAP initial guess.
InitialGuess.SAPnow reaches the closed-shell Python periodic Ewald/GDF/BIPOLE density seam: the Python helper buildsF_SAP = T + V_SAPfrom the existing lattice-summed SAP potential, Gamma-folds it, and diagonalises it against the lattice overlap to seed the g=0 density. Open-shell SAP remains gated with the other open-shell periodic Fock/density integrations. Pins:tests/test_periodic_sap.py(Gamma-Ewald, GDF, BIPOLE) andtests/test_guess.py::test_periodic_sap_closed_python_seam_uses_driver_context.Closed-shell Python periodic HUECKEL initial guess.
InitialGuess.HUECKELnow reaches the closed-shell Python periodic Ewald/GDF/BIPOLE density seam: the Python helper builds the lattice GWH Fock with the existing periodic HUECKEL core routine, Gamma-folds it, and diagonalises it against the lattice overlap to seed the g=0 density. Open-shell HUECKEL remains gated with the other open-shell periodic Fock/density integrations. Pins:tests/test_periodic_huckel.py(Gamma-Ewald, GDF, BIPOLE) andtests/test_guess.py::test_periodic_huckel_closed_python_seam_uses_driver_context.Closed-shell Python periodic MINAO initial guess.
InitialGuess.MINAOnow reaches the closed-shell Python periodic Ewald/GDF/BIPOLE density seam by passing the periodic system and lattice-sum options into the existing periodic MINAO projector. The direct Gamma and multi-k C++ periodic RHF/RKS routes continue to use their lattice-native implementation; open-shell MINAO stays cleanly gated with the other open-shell periodic Fock/density integrations. Pins:tests/test_periodic_minao.py(Gamma-Ewald, GDF, BIPOLE) andtests/test_guess.py::test_periodic_minao_closed_python_seam_uses_driver_context.Closed-shell periodic PATOM initial guess.
InitialGuess.PATOMnow works through closed-shell periodic Gamma and multi-k RHF/RKS. Gamma uses the existing periodicJKBuilderfor the one-step in-field re-polarisation after SAD; multi-k builds the same HF-like in-field Fock in real space and diagonalises it at each k-point. Open-shell and Python periodic-driver PATOM paths remain fail-closed with clean errors until they get their own per-spin injection. Pins:tests/test_periodic_patom.py.Closed-shell periodic HUECKEL initial guess.
InitialGuess.HUECKELnow builds a lattice GWH Fock guess for periodic Gamma and multi-k RHF/RKS, using per-AO atomic energies and the periodic overlap blocksH_GWH(g) = 0.5 K S(g) (eps_u + eps_v).AUTOstill resolves toSADfor periodic robustness, while explicit HUECKEL now reaches the same converged basin as SAD on small insulating cells. Pins:tests/test_periodic_huckel.py.
Fixed¶
vq admin statusnow shows live update heartbeats. Admin-update markers persistlast_heartbeat_atand a short message; phase transitions stamp them, and longupdate_scriptbuilds refresh them on the normal build heartbeat cadence.vq admin status --jsonandvq admin clear-update-marker --jsonexpose the heartbeat age/status, making a delegated remote build that is still compiling distinguishable from a stale or failed marker without clearing the safety gate.BIPOLE legacy multi-k RHF/UHF analytic gradients are active FD regressions again. The full-Coulomb K primitive now exposes an opt-in BIPOLE exchange-energy convention for differentiating
-1/4 sum_g D(g):K(g), while the default true-periodic DIRECT_TRUNCATED K convention remains unchanged for the low-level periodic-gradient path. This removes the strict xfails on the legacyuse_exchange_ewald_split=FalseRHF and UHF multi-k FD parity tests and keeps the asymmetric adjacent cases green.GFN2-xTB translated-water parity now passes against the live xtb oracle. The strict
TestGFN2FaithfulAES::test_water_dimer_vs_xtb_oraclegate is no longer xfailed: interactions are +12.92 / +7.90 / +3.36 mHa at 5.6 / 6.0 / 7.0 bohr, within 1.5 mHa of the pinned xtb 6.7.1 references. The fix aligns the molecular H0 and AES scalar terms with published GFN2/tblite formulas: CN-prime H0 shifts, Slater-exponent compactness, the all-element WH table, H/He repulsion exponent, first-rowmp_vcncache invalidation, published neutral shell reference occupations, and the traceless charge-quadrupole AES contraction.
[v0.15.23] - 2026-07-04 - Neese’s Cheetah¶
Added¶
vibe-view
capture-selftest: a headless screenshot-capture healthcheck.vibe-view capture-selftest(andvibeview.capture. capture_selftest) builds a tiny built-in structure QVF, renders it offscreen to a PNG, and reports the render backend (pyvista/vtk versions, offscreen state). Exit 0 means thecapture_*API works on this host; non-zero means it lacks the capture stack or offscreen GL (no OSMesa and no X / xvfb). Intended as the environment gate for docs-artifact / queue capture jobs so a missing dependency fails fast with a clear message instead of deep inside an example run. The capture path itself is import-independent of the interactive server stack (trame/uvicorn/plotly). Pin:vibe-view/tests/test_v2_extra.py.Vibe-view-ready periodic QVF emission for the Γ-CCM (
-a).vibeqc.periodic.ccm.write_ccm_periodic_qvfwrites the periodic fields vibe-view’s cyclic-cluster renderer keys off, which the molecular QVF writer left dormant for-a(it saw the CCM supercell as aMoleculeand emittedpbc=[false,false,false]with no lattice). It now emitsstructure.pbc=[i<dim]+lattice_vectors(the Born-von-Kármán supercell, ANGSTROM row vectors),volume.density+volume.orbitaltorus grids (evaluated with periodic image-summing so the wrap is in the data, each grid spanning exactly one supercell so tiles abut), anx_ccm.wannier_centersoverlay whose centres/spreads are computed from the torus grids by circular mean over the periodic axes (fixing the position-operator aliasing that gives negative spreads for wrapping Wannier functions; valence functions preferred over cores), and Mayerbonds. Self-checks (density integrates to N electrons, grid spans the cell) plusvalidate_qvfgate the archive before return. Pins:tests/test_ccm_periodic_qvf.py(4). Addresses the-aperiodic-bundle gap for visualization output (1-D chains, 2-D sheets, 3-D crystals).Γ-CCM low-dimensional (wire) mixed-boundary SCF. A full 1-D-periodic, open-transverse Hartree-Fock route for the ab-initio cyclic-cluster model, using the wire Green’s function
G^{1D}(periodic along the chain, free in the transverse plane) instead of the 3-D-torus Ewald kernel on a vacuum-padded cell – the right Hamiltonian for genuine 1-D systems.ccm_wire_v_ne(electron-nuclear) andccm_wire_e_nn(nuclear, a 1-D mini-Ewald) join the existing wire four-center / cderi on one shared conditional-channel gauge, so for a neutral cell the Hartree energy cancels the gauge exactly (½c(N_e−N_nuc)²=0, verifiedg_perp_min-invariant to 1.1e-5 while each term swings ~6 Ha).run_ccm_rhf_wire(python/vibeqc/periodic/ccm/lowd_scf.py) assembles the SCF with plain cluster-supercellS/T(periodicity lives in the kernel) andexxdiv="strict"(theS⊗Smonopole projected out of exchange – ag_perp_min-independent total; the low-D strict-zero-mode). Reference-free gates: the strict total is IR-cutoff-independent (~4e-6); the four-center vs RI-cderi gap collapses to machine ε (dense SCF == RI SCF, 5e-13 – versus the ~22 mHa gauge offset a 3-D vacuum-padded cell carries); and the whole Hamiltonian matches an assembly from exacterf/erfcimage-sum integrals to 8.4e-7. Pins:tests/test_ccm_lowd_four_center.py(10),tests/test_ccm_lowd_scf.py(5). The wire-Madelung exchange seam is now available too (run_ccm_rhf_wire(exxdiv="ewald")): it addsξ_wire·S·D·Son top of the strict-zero mode – the exact 1-D analogue ofrun_ccm_rhf_direct’sξ_N·S·D·S, block-diagonal in occ/virt so it yields the same converged density and shifts the energy by exactlyξ_wire·N_e/2(g_perp_min-independent). Theρ₀gauge inξ_wireis the residual low-D exchange-q=0freedom, soexxdiv="strict"stays the recommended production convention. The d=2 slab four-center is deferred: the wire’s construction does not carry over (itsG∥=0channel is a power-divergent|z|sheet, not a log), needing a dedicated partial-FT +|z|-convolution build.Closed-shell multi-k GAPW meta-GGA.
run_periodic_rks_gapw_multi_know supports self-regularising meta-GGAs such as r2SCAN on the all-electron GAPW route. Molecular-limit cells reuse the Gamma smooth-XC generation path; compact multi-k cells collocate the smooth Bloch kinetic-energy density withcompute_bloch_kinetic_energy_density, project the generalized-KSv_taublock per k withproject_vtau_to_bloch_ao, and add the per-atom hard/soft GAPW XC correction. Pins: compact-cell He/STO-3G r2SCAN GAPW multi-k convergence plus the existing Gamma GAPW meta-GGA and compact GPW Bloch-tau adjacent tests.Closed-shell multi-k READ restart.
run_periodic_jobnow acceptsinitial_guess="read", read_from=<prior multi-k result or .qvf>on closed-shell GDF, GPW, and GAPW routes. The restart rebuilds each complex Bloch density block asD(k)=C(k) f(k) C(k)^Hfrom the prior result’s native per-k coefficients and occupations, or from the QVFx_vibeqc.bloch_wavefunctionrestart section, then injects the resultingD(k)list into the first Fock build. Legacy QVF archives without that section and.moldenmulti-k READ remain fail-closed. Pins:tests/test_periodic_k_gdf.py,tests/test_periodic_runner_gpw_dispatch.py,tests/test_qvf_writer.py, andtests/test_guess.py.Periodic multi-k production routes gained the next PBC hardening layer. Gilat sampling is wired through GDF and BIPOLE RKS, multi-k UHF/Ewald now supports atomspin guesses and smearing, multi-k GDF supports DFT+U, and the GPW energy breakdown includes omega/screened-exchange contributions for hybrid diagnostics and parity work.
vq status --jsonnow includes terminal diagnosis hints. Terminal payloads carry a derivedterminal_diagnosisobject withcategory,action_hint,summary, and optional signal/reason details, so automation can distinguish scheduler walltime, exit-137/SIGKILL resource kills, missing scheduler exit markers, manual kills, and ordinary non-zero command failures without scraping status text.vq wait --jsonnow emits machine-readable terminal outcomes. Blocking supervisors receivejobid, finalstate,exit_code,cli_exit_code, and the sameterminal_diagnosisobject asstatus --json; timeout mode exits 124 with a JSON payload that preserves the last seen state and pause/PBS/walltime detail.vq queue --jsonnow includes terminal diagnosis hints. Bulk queue scans receive the same nullableterminal_diagnosisfield asstatus --json, allowing fleet and paper automation to classify terminal jobs without polling one status call per row.Fetched vq artifacts now carry terminal diagnosis sidecars.
vq fetch,vq fetch --workdir, and the tar emitters used by remote fetches add_vq/terminal-diagnosis.jsonto the fetched directory, preserving raw state/exit/scheduler fields plus the derivedterminal_diagnosisobject next to partial outputs.vq submit --expected-shanow pins mutable program checkouts per job. Submit rejects stale[programs.NAME]venv checkouts before queueing, stores the expected Git SHA in the job’s runtime pin snapshot, and forwards the guard to remote/scheduler-driver submits, including directory payloads used for documentation artifact regeneration.
Changed¶
vibe-view packaging: headless capture installs lean (interactive server split into a
[viewer]extra). The interactive web-viewer stack (trame, trame-vtk, trame-vuetify, uvicorn) moved out of vibe-view’s core dependencies into a new[viewer]optional extra, so a capture-only host (docs-artifact / queue jobs, CI) installs just the QVF reading + screenshot-capture stack (pyvista, vtk, matplotlib, plotly, click, pydantic, jsonschema).pip install vibe-viewnow gives capture;vibe-view open/serve/dashboardneedpip install vibe-view[viewer]and emit an actionable hint when it is absent.[test]and[all]self-referencevibeview[viewer]so the full test suite and the main repo’s[viewer-gpu]extra are unchanged in behaviour. plotly stays core (the bands/dos/spectra/scf_history renderer modules import it, andcapture_bandsetc. need them). Also fixed the stalevibe-view --versioncodename (still printed “Fukui’s Fox”; now “Roothaan’s Roadrunner”). Pins:vibe-view/tests/test_v2_extra.py(TestViewerExtraGuard).
Fixed¶
vq admin updatenow distinguishes live updater markers from failed or stale markers. Marker-present refusals andvq admin statusnow report whether the writer PID is still alive, showmarker_status=runningfor an in-flight remote/local updater, and suggest waiting or checkingvq admin status --verboseinstead of telling operators to clear a marker while a build is still compiling.vq admin clear-update-markertext and JSON output now carry the same marker diagnosis before clearing, and refuse live markers unless the operator passes--force-live. Failed and stale markers still keep the clear-marker /--forcerecovery recipe.DLPNO-CCSD(T)
(T1)triples now stream per-triple domains instead of retaining them. The triples driver builds one distinct occupied-triple TNO domain, accumulates all ordered triples for that domain, and immediately discards itsovvv/ooov/ovovtensors. The same bounded-memory iteration is used by the local(T0)path. This fixes the code-side unbounded retention that caused the v0.15.21 M16 child to fail during(T), without a size cap or positive-radius bypass, while preserving the existing ordered-triple energy sum. This is not latest-version-clean paper evidence for M16 DLPNO-CCSD(T): MP2/CCSD landed on v0.15.21, but the complete CCSD(T) artifact remains the v0.15.14 run until a current child completes(T). Pins:tests/test_dlpno_triples_local.pyand thedlpno-ccsd(t)runner/M16 audit tests.The stale GFN2 water-pair XPASS is retired. The old non-strict
test_water_dimer_hydrogen_bondaccepted the historical-0.008 ± 0.01 Hatarget only because the tolerance window straddled zero. It is now an active metadata guard proving that target is not an xtb parity criterion, while the chemically discriminating three-point oracle gate remains the only xfailed H0/Pauli parity test.
[v0.15.22] - 2026-07-04 - Neese’s Cheetah¶
Added¶
vibe-view cyclic-cluster (Γ-CCM) periodic rendering: minimum-image orbital wrap + Wannier-centre overlay. Two viewer additions for the ab-initio cyclic-cluster model, where wavefunctions live on a finite Born-von-Karman torus. (1) A “Wrap orbital to cell centre” toggle rolls a periodic (torus) orbital / Wannier-function grid so a feature straddling a cell face renders whole in one cell instead of split (
wrap_grid_to_centerinrenderers/volume.py: circular-mean centroid, minimum-image roll, guarded to torus-aligned grids and localized fields only). (2) Anx_ccm.wannier_centersvendor section (per-orbital centroid in Angstrom, spread, optionalorbital_ref/label) draws sphere markers sized by spread (renderers/wannier.py, a Display-panel toggle shown when the section is present). Verified that the existing periodic cell-box wireframe and N1xN2xN3 isosurface tiling already render correctly on CCM data once the writer emitsstructure.pbc+structure.lattice_vectors(Angstrom, row vectors, the torus supercell). Pins:vibe-view/tests/test_periodic_wrap.py(8),vibe-view/tests/test_wannier_overlay.py(5), plus wrap/overlay controller tests intest_controller_state.py.Open-shell (UKS) meta-GGA on the Γ GAPW route. With the UKS smooth-XC telescoping fixed (see Fixed), self-regularising meta-GGAs (SCAN / rSCAN / r2SCAN) now run all-electron open-shell via
run_periodic_uks_gapw(..., functional="r2scan")— the polarised per-atom τ augmentation telescopes the core τ (He/STO-3G r2SCAN ~0.8 mHa, H-doublet ~0.3 mHa vs the molecular reference), completing the closed-shell Γ meta-GGA support to open-shell. Non-self-regularising meta-GGAs (TPSS, M06-L) still fail closed via the smooth-grid stiffness guard; multi-k GAPW meta-GGA remains gated.vq tail --jsonnow exposes machine-readable arbitrary workspace tails. Cockpit clients can pollvq tail HOST JOBID --name calc.out -n 200 --jsonand receivejobid,host,state,queue_handle,filename,tail,path, andtext. Live scheduler jobs also report the scheduler target, scheduler job id, remote workspace, andlive_scheduler_workspace=true.--jsonremains one-shot and is rejected with--follow.vq status --jsonnow exposes numeric runtime accounting. Long-job monitors getwall_elapsed_seconds, pause-adjustedactive_elapsed_seconds,active_walltime_percent, and parsed scheduler walltime seconds/percent/remaining fields, so release-paper automation can distinguish admin-update pauses from vq/PBS walltime pressure without parsing human status text.
Changed¶
vq tail -fon live scheduler workspace files now polls by byte offset. The scheduler dispatcher reads the current remote file size and only returns bytes appended since the previous poll, so following large engine.outfiles no longer re-fetches the whole file every second. Truncated or rotated files reset to a fresh full read.
Fixed¶
The GFN2-xTB out-of-process oracle now pins H2O on the same geometry as the active dimer parity gate.
examples/regression/core/runner_xtb.pypreviously carried an H2O reference for a different water geometry (H ±1.551,0.905bohr, about 119 degrees) whiletests/test_gfn2_xtb.pyused_water()(H ±1.431148,1.108113bohr, 104.5 degrees). The runner now uses the live-xtb_water()reference-5.070374 Haand a regression checks both the pinned coordinates and reference value.Open-shell (UKS) GAPW DFT XC-augmentation telescoping.
run_periodic_uks_gapwcollocated the smooth-grid XC from the full (hard) density instead of routing per spin through the soft-generation path the closed-shell RKS driver uses. The per-atom augmentation then double-counted the hard core (2·hard − soft), over-binding by ~300 mHa for every DFT functional (He/STO-3G: LDA gap 301 mHa, PBE 320 mHa). The UKS smooth XC now generates from the soft density per spin, telescoping to the all-electron reference at the ~1 mHa augmentation floor (LDA/PBE/r2SCAN). This also lifts the open-shell meta-GGA fail-closed gate that was held pending this fix.CASPT2/NEVPT2 effective-density gradient: fix and re-enable the
state_rdm12_cppfast path. The sparse-state C++ RDM12 kernel now accumulates the ket-sideE_rs|c>and bra-sideE_sr|c>branches independently, so an emptysoccupation can no longer skip a valid bra-sidercontribution._full_rdm12_from_stateis back on the C++ path when the binding is available, with the pure-Python construction retained as fallback and oracle. The former xfail intests/test_pt2_density_rdm.pyis now an active regression against the brute-force spin-orbital reference.CASPT2/NEVPT2 correlation-gradient degradation is now explicit. If the requested PT2 effective-density, chain-rule, CI-Lagrangian, or explicit Lagrangian correction fails and vibe-qc falls back to a less complete gradient contribution, the fallback emits a
UserWarninginstead of silently changing the gradient meaning. The regression monkeypatches the chain-rule correction and verifies callers see the warning while the finite gradient fallback remains available.The vq test harness now isolates single-user queue state/config by default.
tests/conftest.pypointsVQ_STATE_DIRandVQ_CONFIG_DIRat per-test temporary directories, while leaving multi-user root defaults and path-resolution tests untouched. This prevents a real liveadmin-update-in-progressmarker from pausing daemon-dispatch tests on a development host during fleet maintenance.Restored the
transform_4index_mopybind11 export incpp/src/bindings.cpp(plus its#include "vibeqc/pt2_transform.hpp"). It was one of three exports silently dropped by the5317f79cbindings.cpp stale-base rewrite (2026-06-30); the two sparse-RDM exports came back inb78b4b53, but this compiled 4-index MO transform had no restore anywhere. All five CASPT2/NEVPT2 dE²/dk gradient consumers aretry/except-guarded and were silently falling through to the O(norb⁵)np.einsum("ap,bq,cr,ds,abcd->pqrs", …)path on every build — a perf regression only, not a correctness bug. Verified: the H2/6-31g CASPT2 z-vector gradient now takes the compiled kernel (result identical to the einsum fallback at 1.9e-16);tests/test_casscf_gradient.py39/39 green.
[v0.15.21] - 2026-07-03 - Neese’s Cheetah¶
Added¶
All-electron GAPW meta-GGA (closed-shell Γ RHF/RKS). The per-atom augmentation now telescopes the kinetic-energy density: hard/soft atomic τ built from AO gradients on the radial×Lebedev grid (
GapwAugmentation._compute_atomic_tau, lazily-cachedevaluate_ao_with_gradient), the von Weizsäcker floor applied per grid, and the generalized-KS τ Fock term telescoped as a gradient-gradient projection½∫v_τ^hard ∇χ·∇χ − ½∫v_τ^soft ∇χ̃·∇χ̃(_project_atomic_vtau; the smooth per-atom scalar ρ/σ channels can’t represent it). Energy correction useseval_unpolarised_mgga. Lifts the fail-closed gate onrun_periodic_rks_gapw(functional="r2scan"/"scan"). Validated: He GAPW-r2SCAN telescopes to the molecular all-electron reference within 0.8 mHa — the same augmentation floor as PBE. TPSS-class functionals still fail closed (inherited smooth-grid stiffness guard); open-shell (UKS) GAPW meta-GGA stays gated behind a separate pre-existing UKS-GAPW smooth-XC telescoping bug (~300 mHa for all functionals, flagged for follow-up); multi-k GAPW meta-GGA stays gated (per-k smooth τ not wired). Regression:tests/test_periodic_gapw_augment.py(test_rks_mgga_gapw_telescopes_to_molecular,test_tpss_gapw_fails_closed_on_stiffness,test_uks_mgga_gapw_fails_closed).vq tail -fcan now follow arbitrary files in live scheduler workspaces. Explicit scheduler-host tails such asvq tail twin JOBID --name calc.out -fpoll the scheduler dispatcher, emit appended text until the driver spec reaches a terminal state, and still fall back to the staged local workspace once the scheduler job is terminal.vq logs -fremains the preferred stdout/stderr monitor because it is stream-aware.
Fixed¶
CASPT2/NEVPT2 effective-density gradient: guard against a wrong 2-RDM from the restored
state_rdm12_cppC++ kernel. Restoring thestate_rdm12_cppbinding (infix(bindings): restore sparse RDM exports) wired the kernel live again and exposed a pre-existing correctness bug in it:cpp/src/nevpt2_ops.cpp(~line 194) shares oneif (sg_s == 0) continue;between theE_rs(Ec) accumulation and the independent bra-sideE_sr(EcT) accumulation, so the kernel drops every contribution where orbitalsis empty butris occupied. The returned 1-RDM is correct but the 2-RDM is wrong by O(1), which silently corrupted the effective-density CASPT2/NEVPT2 gradient (a faithful Python replica of thecontinuereproduces the kernel 2-RDM to ~2e-16; the kernel disagrees with a brute-force spin-orbital 2-RDM by ~0.95).gradient/_pt2_density.py_full_rdm12_from_state— the kernel’s only consumer — now gates the C++ path behind_USE_CPP_RDM12 = Falseand uses the verified Python construction (matches brute-force to ~2e-16), so the gradient is correct on every build. New pins intests/test_pt2_density_rdm.py: a brute-force equivalence test (green) and anxfailcapturing the kernel bug that flips to xpass once the C++ kernel is fixed. The kernel fix + fast-path re-enable is tracked inhandovers/HANDOVER_MS_CASPT2_GRADIENT.md.Open-shell molecular SCF: spin-coupled DIIS replaces the per-spin Pulay histories (release-paper rerun M02 blocker: OH· UHF/def2-TZVP failed to converge in 100 iterations on v0.15.14). F_α and F_β are coupled through the shared Coulomb term J(D_α + D_β), so extrapolating each spin against only its own residual let the two histories chase each other’s field: the energy was flat to 1e-13 by iteration ~50 while the orbital gradient stayed pinned at ~1e-6..1e-4 until the fail-closed runner aborted. UHF and UKS (both the plain
DIISand theEDIIS_DIISaccelerator) now build one B matrix from the stacked (e_α; e_β) error and apply a single coefficient set to both Fock matrices (DIIS::extrapolate_spin_coupled, cpp/src/diis.cpp). The M02 reproducer now converges in 12 iterations to the PySCF-validated energy (-75.4222223087 Ha, agreement < 1 nHa, ⟨S²⟩ = 0.756399). Regression:tests/test_uhf_open_shell_convergence.py(converges with all-default options; non-converged runs still raise and emit no properties/QVF artefacts). The companion periodic-accelerator fix landed separately (see the Periodic open-shell SCF entry under Changed).Known consequence, documented in the affected tests: on the OH· doublet the default-grid XC quadrature breaks the ²Π π_x/π_y degeneracy anisotropically, flooring ‖[F,DS]‖ at ~1.7e-7 for UKS while the energy converges to 1e-10 (verified: no accelerator or second-order route reaches below the floor; the map’s error vector is constant across the whole ~1e-7 density neighbourhood). The old per-spin DIIS crossed sub-floor gates after ~170 iterations by trajectory noise; the coupled DIIS reaches the floor in ~10 iterations and stays. Three tests that gated OH·-UKS SCF below that floor were re-tolerated to sit above it (energy/gradient parity asserts unchanged):
test_df_gradient_uhf_uks.py(5e-7),test_parity_hf_dft.py(OH/def2-svp joins_HARD_OPEN_SHELL),test_uks_second_order.py(PBE DIIS leg 2e-6).Multi-k BIPOLE results now carry k-point metadata, so optional Γ-only wavefunction exports (molden, QVF
wavefunction.gto) work instead of failing closed. ThePBCBipole{RHF,RKS,UHF,UKS}Resultdataclasses recorded the per-kmo_coeffs/mo_energieslists but not the k-points those lists span, so the runner’s Γ-locating output helpers (periodic_runner._gamma_proxy_for_multi_k,_qvf_wavefunction_proxy_for_multi_k) had nothing to key on and refused to guess that the first k-point was Γ — a multi-k KRHF job (P03 MgO/STO-3G) emittedmulti-k ... requires k-point metadatawarnings and skipped both artifacts. The results now carrykpoints_cart/kpoint_weights(Cartesian bohr⁻¹, aligned with the orbital lists), matching the GDF multi-k result contract (periodic_k_gdf.py); the writers locate Γ when the mesh contains it and otherwise fail closed with the pre-existing clear diagnostic. The primary QVF,.system, population, density, and DOS artifacts were never affected. Regressions intests/test_pbc_bipole_multik_ewald_split.py.vq logsno longer clobbers concurrent terminal-job metadata. Text and JSON log reads now re-read terminal specs under the spec lock before stampinglast_status_at, matchingvq statusand preserving fresh fields written by concurrent fetch or cleanup operations.Multi-k GPW molecular-limit classifier read lattice rows instead of columns — a non-symmetric compact cell could be mis-routed to the Γ-folded density path (unphysical Hartree/XC runaway).
periodic_gapw_j._multik_gpw_is_molecular_limitmeasured the axis lengths withnp.linalg.norm(lattice, axis=1)(row norms), butsystem.latticecolumns are the Cartesian lattice vectors (C++PeriodicSystem.lattice), and the whole GPW grid/AO pipeline (make_grid,PlaneWaveGrid.axis_lengths_bohr,_ao_values_on_grid) reads the same matrix by columns. For a symmetric lattice matrix (cubic/orthorhombic) row norms equal column norms so the bug was invisible; on a skewed slab/rod or general triclinic cell the row norms differ, so a compact crystal whose longest lattice vector is under the vacuum threshold could be classified molecular-limit and routed to the Γ-folded density — the P07-style runaway the module documents. Fixed toaxis=0; regression test with a deliberately non-symmetric compact lattice intests/test_periodic_gpw_compact_multik.py. Also fixed the cosmetic transpose inperiodic_runner._system_summary, which printed lattice rows asa1/a2/a3(the reported vectors were wrong for non-symmetric cells; the periodic-length/area measures in the same function already read columns). Companion to the v0.15.19 auto-convergence + BIPOLE column-vs-row fix.
[v0.15.20] - 2026-07-03 - Neese’s Cheetah¶
Added¶
CASPT2 gradient internals gained the first internally-contracted response-density scaffold. The new helper module separates the IC density assembly pieces from the public gradient dispatcher and adds regression coverage around the inactive/active/virtual block construction. The public CASPT2 analytic-gradient path remains staged behind the existing gates while the response terms are completed.
vqJSON logs now include queue handles. The log JSON emitted by the queue tooling carries the same queue-handle information exposed in status output, so monitoring clients can connect status, wait, and log streams without reparsing human-facing text.
Fixed¶
BIPOLE corrected-gauge gradient validation is more stable. The periodic gradient route now validates the corrected-gauge BIPOLE gradient gate with tighter shape and metadata checks, and the regression coverage exercises the failure modes that previously let inconsistent state reach the gradient comparison.
Sparse RDM exports are restored in the Python bindings. The C++ binding layer again exposes the sparse reduced-density-matrix helpers required by downstream post-SCF and gradient plumbing.
AICCM-B gradients now reject restricted-density metadata mismatches. The AICCM-B gradient route validates that restricted-density metadata is present and consistent before dispatching, instead of allowing an invalid restricted density payload to continue into the periodic gradient path.
Basis-set development regressions are back in the active test matrix. The basis-set development UKS energy/gradient tests were refreshed so the intended regressions run again under their current fixtures.
[v0.15.19] - 2026-07-03 - Neese’s Cheetah¶
Added¶
Multi-k periodic GDF SCF now fails early with a memory diagnostic instead of an OOM kill. The per-
(k_i, k_j)-pairLpqdensity-fitting cache is held dense in RAM (streaming is future work), so a paper-grade cell — NiO/def2-SVP KUKS at a(4,4,4)mesh (180 AOs, ~1500 aux) needs ~56 GB — was OOM-killed (exit 137) before SCF iter 1, leaving only a header.outand no.system/.qvf. Therun_krhf/run_kuhf/run_krks/run_kuks_periodic_gdfdrivers now estimate the cache peak (memory.estimate_periodic_multik_gdf) and abort viaInsufficientMemoryError(memory.check_periodic_gdf_memory) with route-specific remedies — coarser Monkhorst-Pack mesh / Γ-only GDF, smaller basis, or the plane-wave GPW route — before building the doomed cache. The peak estimate is logged each run so aprogress-on rerun localises the cost;VIBEQC_GDF_MEMORY_OVERRIDE=1forces the run. Regression:tests/test_periodic_gdf_memory_preflight.py. (Prompt 75: NiO multi-k KUKS/GDF killed before SCF artifacts.)
Fixed¶
Auto-convergence + BIPOLE self-energy read lattice axis lengths off the columns, not the rows.
periodic_convergence_auto._lattice_lengths(which feedsclassify_periodic_system’s vacuum-axis / molecular-limit test) andbipole_lattice_self_energy.dipole_dipole_self_energy’s metric-cubic detector both measured axis lengths withnp.linalg.norm(system.lattice, axis=1)(row norms). Butsystem.latticecolumns are the Cartesian lattice vectors (cpp/include/vibeqc/periodic.hpp: “Columns = Cartesian lattice vectors”;periodic_runner._reduce_system_to_primitiveusesfrac = inv(L) @ r_cart). Row norms equal column norms only for a symmetric (cubic/orthorhombic) matrix, so the bug was invisible on the existing symmetric-cell tests; on a skewed slab/rod or triclinic cell the auto-convergence classifier could mis-read a compact crystal as a vacuum-padded molecular-limit box, and the BIPOLE detector could flag a genuinely non-cubic cell metric-cubic and silently use the cubic closed-form. Both fixed toaxis=0, with skewed-lattice regression tests whose row and column norms differ (tests/test_periodic_convergence_auto.py,tests/test_bipole_lattice_self_energy.py). Same class of bug as theperiodic_gapw_j._multik_gpw_is_molecular_limitfix.Multi-k GDF SCF no longer recomputes the Ewald-3D J pair-FT every iteration. Profiling a LiH (1,1,2) GDF RKS SCF showed 93 % of the wall time (557 s of 601 s) was
ao_pair_fourier_transformre-runs insidebuild_periodic_fock_ewald3d_k— the per-cell AO-pair FT depends only on (basis, cells, mesh), never on the density. The multi-k GDF driver now hoists the existingmake_ewald_3d_lattice_j_cache(the same iteration-invariant cache the multi-k EWALD_3D RHF/RKS drivers already use; contraction is bit-identical) and threads it through both Fock-build call sites: 4.7× faster end-to-end on the profiled case (601 s → 127 s), energy unchanged to 1e-8.
Added¶
vibe-view 2.1.0.dev0 “Roothaan’s Roadrunner”. Version + codename bump carrying the Avogadro-parity M2 (content-aware live reload), M4 (live result streaming), and reaction-animation work. The main viewer user guide (
docs/user_guide/vibe_view.md) gains sections on thevqJob Manager, live reload, streaming a running job, and theanimateCLI; stalevibe-view --versionbanner examples refreshed across the user guide and tutorial.vibe-view
render_reaction_video— NEB reaction paths animate to MP4/GIF.vibeview.animationrendered trajectories, vibrations, and orbitals but had no path forreaction.pathsections, despite that being a headline animation type. The newrender_reaction_videoplays the NEB band image-by-image (reactant → TS → product) through the shared frame renderer, andvibe-view animategains--kind reactionplus auto-detection ofreaction.path. Pins: functional GIF render from the committedneb_h3showcase + no-section guard (vibe-view/tests/test_v2_extra.py).Open-shell (Γ) GPW meta-GGA.
run_periodic_uks_gpw/run_periodic_uhf_gpw’s UKS path now supports meta-GGA functionals: per-spin kinetic-energy densities feed libxc’seval_polarised_mgga, with the closed-shell regularisation chain applied per spin channel (von Weizsäcker floor + constrained chain rule, uniform-grid density screen —excscreens only where both spins are small, so a fully-polarised region keeps the majority spin’s exchange — and the fail-closed stiffness guard), plus the per-spin generalized-KS Fock term½∫v_τσ ∇χ·∇χ. Pins: UKS(r2scan) ≡ RKS(r2scan) closed-shell reduction (1e-8), physical H-doublet energies (r2scan below LDA), TPSS stiffness fail-closed (tests/test_periodic_gpw_uks_mgga.py). The multi-k spin-polarised extension of this path landed separately (see below).Multi-k spin-polarised (UKS) GPW meta-GGA.
run_periodic_uks_gpw_multi_know supports meta-GGA functionals on both regimes, lifting the last GPW meta-GGA gate. The molecular-limit branch builds per-spin Γ-summed τ (basis + per-spin density matrices); the compact Bloch branch builds per-spin τ from the per-k Bloch AO gradients (compute_bloch_kinetic_energy_density) and adds the generalized-KS½∫v_τσ ∇χ·∇χFock block per k per spin (project_vtau_to_bloch_ao). The per-spin regularisation chain (von Weizsäcker floor, constrained chain rule, density screen, fail-closed stiffness guard) is shared with the Γ open-shell path. Pins: UKS(r2scan) ≡ RKS(r2scan) on the molecular-limit (H₂, 5e-6) and compact Bloch (Si primitive (2,2,2), 1e-5) regimes, Γ-mesh parity with the Γ UKS driver, hybrid + TPSS stiffness fail-closed (tests/test_periodic_gpw_uks_multik.py). Wired throughrun_periodic_job(methodUKS,jk_method="gpw").Density mixers (Anderson / Broyden, optional Kerker) exposed through
run_periodic_jobon the closed-shell GDF route.run_periodic_job(..., jk_method="gdf", method="RHF"/"RKS", kpoints=..., density_mixer="anderson"|"broyden", density_mixer_kerker=True, kerker_k0=..., ...)now reaches the multi-k GDF driver instead of failing closed — the same per-k density-matrix mixing machinery as the lower-level EWALD_3D RKS drivers (periodic_density_mixing), with the Kerker filter preconditioning the residual on a plane-wave grid (charge-sloshing control for metallic cells). When selected it replaces Fock-DIIS, damping, and fock mixing (one accelerator owns the update). Γ-only requests must pass an explicitkpoints=(1,1,1); the GPW/GAPW/ BIPOLE/AICCM and open-shell routes still fail closed with the remediation in the message. Pins: Anderson and Broyden+Kerker both reproduce the Fock-DIIS fixed point on gapped LiH (5e-7), runner forwarding end-to-end, and the fail-closed gates (tests/test_periodic_gdf_density_mixer.py).
[v0.15.18] - 2026-07-02 - Neese’s Cheetah¶
Added¶
AICCM (Γ-CCM /
-a) evaluation routes are now keyword-selectable, with the k-free real-Γ route a first-class peer of gamma and chi. Two surfaces: (1) a functional library selectorrun_ccm_scf(ccm, route=…)picks among the three-aroutes for the same cyclic-cluster Hamiltonian —route="four-center"(dense WSSC kernel),route="gamma"(Γ-CCM production = multi-k GDF, CCM ≡ SCM-Γ), androute="real-gamma"(the k-free literal real-Γ-supercell SCF,run_ccm_rhf_direct, with the derived exchange-q=0seam) — accepting spoken / jk_method / harness aliases ("direct","non-k","aiccm-ri","aiccm-hf-direct", …) and dispatching bit-identically to the concrete drivers;functional=runs KS on the gamma route (real-gamma KS is gated with a pointer). (2)PeriodicJKMethodgainsAICCM2026DEV_A(gamma) andAICCM2026DEV_A_REAL_GAMMA(real-gamma, alias"real-gamma") as peers ofAICCM2026DEV_B(chi), so the route is named at the samejk_methodsurface as chi and is described/validated there. The-aroutes do not yet compute throughrun_periodic_job(staged FR-1 bundle wiring); the runner fails them closed with a preciseNotImplementedErrorpointing atrun_ccm_scf/ therun_ccm_*entry — a fail-closed member like the retiredfft_poisson, not a half-wired path. New:vibeqc.periodic.ccm.run_ccm_scf/resolve_ccm_route/CCM_ROUTES;vibeqc.periodic_jk_method.resolve_jk_method_string. Tests:tests/test_ccm_route.py(28).D89 qualification (2026-07-15): the historical “three routes for the same Hamiltonian” and “gamma = Γ-CCM production” wording above is superseded.
aiccm2026dev-a/gamma/gamma-ccmnow select the union-and-weight four-center Γ-CCM construction.neutral-bloch/gdf/aiccm-riandreal-gammaare Bloch and real-supercell evaluations of a neutral fitted-torus control; their same-Hamiltonian parity is not Γ-CCM/χ-CCM construction evidence.Γ-CCM fold-built neutral cderi (
ccm_neutral_cderi_fold) — the supercell-FFT memory wall removed, and exact GDF parity on ultra-diffuse bases. The real Γ-supercell neutral cderi is assembled from per-q unit-cell Bloch-pair fits folded onto the torus (the RI-level statement of CCM ≡ SCM-Γ band folding), instead of one giant supercell FFT fit:B_q[P,(μc),(νc')] = N_c^{-3/2}·Σ_ka e^{i ka·Rc − i kb·Rc'}·L_{ka,kb}[P,μν]withkb = ka + qkept unfolded (one whitening frame per channel) and the builder’scanonical_auxiliary_basis=Trueframe (gauge-stable matrix function — the compact eigenmode frame’s arbitrary rotations across independently built blocks corrupt the fold’s cross-kaproducts, up to 8.5e-4 measured on a vacuum cell). Same RI factorization asccm_neutral_cderi(four-center contraction equal to ~1e-10 on compact toruses, TRIM and non-TRIM, multi-axis); memory drops from supercell-volume-scaling (measured 49 GB at a 54-AO 18³-bohr torus) to MBs, at the multi-k GDF’s own setup cost. On ultra-diffuse LiH/sto-3g (2,1,1) the fold cderi fixes the supercell fit’s open translation-asymmetry finding at the CCM level:run_ccm_rhf_direct(cderi_build="fold")— now the default — matches the multi-k GDF to 4e-14 Ha/cell where the supercell fit missed by 1.35e-4 (the residual is thereby localized to the supercell-FFT fit path; canary test retained on that path).run_ccm_rhf_directgainscderi_build={"fold","supercell"}; tests intests/test_ccm_direct.py.
Changed¶
Periodic open-shell SCF: spin-coupled Pulay DIIS replaces the per-spin histories in the Python periodic accelerators. F_α and F_β are coupled through the shared Coulomb term J(D_α + D_β), so extrapolating each spin against only its own residual lets the two histories chase each other’s field and can stall the SCF tail (the molecular OH/def2-TZVP UHF 2026-07 M02 regression; the same coupling argument holds per k-point). The Γ-point
PeriodicSCFAccelerator.extrapolate_uhf(DIIS + the DIIS branch of EDIIS_DIIS) now stacks (F_α; F_β) / (e_α; e_β) vertically into a single C++DIIShistory; the multi-kMultiKPeriodicUHFAcceleratorand the multi-k UKS Ewald driver concatenate the α and β per-k block lists with duplicated k-weights into one_MultiKPulayDIIShistory — one B matrix, one coefficient set applied to both Focks, the same spin-coupled metric the open-shell EDIIS / ADIIS / KDIIS paths already used. Touches every open-shell periodic route through these accelerators (Γ/multi-k Ewald UHF + UKS, GDF, BIPOLE). Validated against PySCF.pbc unrestricted GDF references per CLAUDE.md § 7.
Fixed¶
Periodic GDF RSGDF high-|G| tail completion no longer spends the P01 MgO/STO-3G promotion rerun silently grinding through dead inter-cell AO-pair work before the first SCF line. The prompt-11
rsgdf_tail_ke_cutoff=3200fix was numerically right but the production route still asked the general-L Bloch AO-pair FT to stream every tail chunk over the full 30-bohr cell list (935 cells on P01), even though the high-|G| shell is dominated by same-cell tight-core products and diffuse / inter-cell contributions are exponentially zero. The native general-L AO-pair FT now has an opt-in conservative shell/cell Fourier-bound screen, and the PBC-GDF RSGDF tail path enables it at1e-14only for the tail completion; directbuild_lpq_native_fftcallers keep the exact unscreened base+tail==big-mesh path by default. The tail builder also emits chunk counters (RSGDF high-|G| tail chunk i/n) through the progress logger so long promotion runs show pre-SCF progress.run_periodic_jobnow exposes and forwardsrsgdf_tail_ke_cutoffon the supported GDF routes, fails closed on routes that would ignore it, and records the native OpenMP thread count beside the GDF settings in.outfiles. Pins:tests/test_rsgdf_dense_mesh_tail.py(exact tail identity, screened-vs-exact high-|G| P01 samples, progress counter) and the high-level GDF runner dispatch tests.CASPT2/NEVPT2 effective-density gradient correction was silently dropped on every build.
gradient/_pt2_density.pyimported thestate_rdm12_cppC++ kernel outside its Python-fallback guard, but the kernel was never exported from_vibeqc_core(implemented incpp/src/nevpt2_ops.cpp, nobindings.cppregistration), socompute_pt2_effective_densityalways raised ImportError; the CASPT2 gradient driver’s degradation handler swallowed it and the PT2 correction vanished without warning (SA-CASPT2 gradients came out identical to state-specific ones — the 2026-07-02 fast-lane gate reds intests/test_casscf_gradient.py). The import now sits inside the guard, and the previously unreachable pure-Python 2-RDM fallback — which had its own bugs (_socalled with a spin index in thenorbslot; missing cross-determinant terms) — was rewritten to mirror the C++ kernel’sT2[p,q,r,s] = (E_qp c)·(E_rs c)construction, validated to machine precision against a brute-force spin-orbital reference. Exporting the C++ kernel (a pure speed-up, no behaviour change) stays with the CAS-gradient workstream (handovers/HANDOVER_MS_CASPT2_GRADIENT.md). Affected release: v0.15.17 (seedocs/troubleshooting.md).DLC geometry optimization: back-transform no longer diverges on planar / near-branch-cut torsions. The delocalised-internal- coordinate back-transform (
geomopt/internal_coords.py) reused the reference geometry’s Wilson B-matrix pseudoinverse at every Newton iteration and never wrapped torsion residuals into (−π, π], so a molecule whose torsions start near ±π (planar heavy-atom glycine, the rp167 SI matrix) saw spurious ~2π residuals, walked out of the Newton basin, and drove the SCF to a garbage energy (~−228 Ha vs the −279 Ha minimum). Now: B⁺ is rebuilt at the current geometry each back-transform iteration (and for the gradient / Hessian projections), torsion residuals are branch-wrapped, each step’s Newton iteration is seeded from the previous step’s geometry, and a diverging iteration returns the best geometry seen instead of running away.DelocalizedInternalCoordinatesadditionally warns (RuntimeWarning) when the auto-generated primitive set is rank-deficient (spans fewer than the expected 3N−6 internal DOF) instead of silently under-optimising, recommendinggeom_coords="cartesian"for that topology. Pins:tests/test_geomopt_dlc.py(H2O round-trip + DLC-vs-Cartesian parity, torsion branch-cut wrap, glycine warn-and-stay-bounded). Docs:user_guide/geometry_optimization.md§ Coordinate systems.Solvated CASSCF geometry optimization no longer dies with a
NameError; unsupported method+solvent combos now refuse clearly._run_molecular_scfassigned itsoptionslocal only in the rhf/uhf/rks/uks/rohf branches but passed it torun_cpcm_scfunconditionally, sooptimize_molecule(..., method="casscf", solvent=...)— reachable via the analytic CASSCF path, whose envelope gate does not exclude solvated runs — crashed withNameError: opts. CPCM composes with the mean-field SCFs only (run_cpcm_scf: rhf / uhf / rks / uks), and the FD fallback would silently drop the solvent (_run_single_pointignores it for the CAS family) and walk the gas-phase surface while claiming solvation — so casscf / caspt2 / nevpt2 / rohf with a solvent now raise aValueErrornaming the limitation up front, before any gas-phase solve (ROHF previously burned a full gas SCF beforerun_cpcm_scfrejected it). Also fixed the dead None-check in_compute_molecular_gradient:np.asarrayran before thegradient is Nonetest, so a missing CAS-family gradient raised an opaque numpyTypeErrorinstead of the intendedValueError. Pins:tests/test_molecular_optimize.py::TestSolventMethodGating+::TestCasGradientNoneCheck(10 new tests).run_job(..., solvent=...)no longer silently ignores the solvent for methods without a solvation composition. Implicit solvation composes with the mean-field SCFs (CPCM: rhf / uhf / rks / uks) and MSINDO (COSMO) only, but every other single-point method — the CAS family (casscf / caspt2 / nevpt2 / casci / mrci), the CI/CC solvers (cisd / selected_ci / fci / dmrg / v2rdm / transcorrelated_ci / ccm), the semi-empirical platform (gfn2_xtb / pm6 / om1-3 / dftb) and MLIPs — dropped a user-passedsolvent=without a warning and returned a gas-phase energy, misrepresenting the result. The post-SCF families (mp2 / ccsd and variants) were subtler: the mean-field reference ran the CPCM macro-iteration, but the correlation block then composed its reported total from the bare reference energy, so the.outprinted an “in-solvent” block alongside an MP2/CCSD total that was consistent with neither the gas-phase nor the in-solvent reference. Both cases now raise a clearValueErrorup front: a fail-fast gate inrun_job(before any optimization machinery burns gas-phase cycles) plus a backstop gate in the_run_single_pointdispatcher, which also covers the FD-optimizer energy path (_evaluate_energy) that previously walked the gas-phase surface while claiming solvation for e.g. solvated-CASPT2 optimizations rerouted to finite differences.method="auto"resolutions landing on an unsupported method (e.g. FCI for tiny systems) name the resolution in the error; rohf / roks keep their dedicatedNotImplementedErrorgates. Wording matches the_run_molecular_scfgate that landed with the solvated-CASSCF optimizer fix above. Pins:tests/test_solvation_cpcm.py::TestRunJobSolventMethodGating+tests/test_molecular_optimize.py::TestSolventMethodGating(FD-path additions). Docs:user_guide/solvation.md§ Supported methods.LatticeSumOptions.becke_image_radius_bohrexposed to Python. The C++ field has existed since the periodic-Becke cross-cell screen landed (build_xc_periodicreads it), but the pybind11 binding was only ever added onPeriodicKSOptions, so setting or reading it on a bareLatticeSumOptionsfrom Python raised AttributeError. This broke the periodic basis-optimization XC parameter gradient (basis_optimization/periodic_energy_gradient.py) and its test moduletests/basisset_dev/test_periodic_xc_param_gradient.py(8 failures) on a fresh build. The attribute is now bound onLatticeSumOptions(default 0.0 = unset, falling back to the built-in partition reach); regression pin intests/test_periodic_grid.py. In the same sweep,tests/basisset_dev/test_energy_gradient_uks.py’s FD reference step for exponents moves from rel 1e-4 to 1e-3: the SCF energy’s deterministic screening-noise floor (a few nHa) made the central difference on the small B3LYP d-exponent component cross tolerance at 1e-4 while the analytic gradient is correct (h-sweep V-curve documented in the test).CASPT2/NEVPT2 geometry optimization: analytic Z-vector gradient now correctly gated and actually reachable. v0.15.11 enabled the analytic CASPT2/NEVPT2 gradient in
optimize_molecule/optimize_molecule_brentunconditionally (b3645a46), but the analytic branches never forwardedcaspt2_options/nevpt2_optionsto the solver — so every PT2 optimization walked the bare CASSCF reference gradient (thecompute_corr_grad=Falsedefault) while reporting PT2 energies, a surface inconsistent with the reported energy. All three molecular optimizers (L-BFGS-B primary, Brent, and thegeom_optMolecularSCFProvider, which had stayed on FD) now share one decision (_mrpt_analytic_gradient_ok): analytic Z-vector gradient (99.99% vs full-energy FD, retired CAS-gradient handover v52) when CASSCF-referenced, gas-phase, andcompute_corr_grad=True; full-energy central FD otherwise (default).caspt2_options/nevpt2_optionsare forwarded through the analytic SCF path,MolecularSCFProvidergains the previously missingnevpt2_optionsparameter, andrun_jobforwardsnevpt2_optionsto all three optimizer backends (previously dropped, so NEVPT2 optimization options could never reach the solver). Pins:tests/test_molecular_optimize.py::TestCasscfAnalyticGradientGating(7 new tests covering all three optimizers, both gate directions, and the options plumbing). Docs:user_guide/geometry_optimization.md§ Supported methods.
Added¶
vibe-view live result streaming (Avogadro-parity M4): watch a running job converge. The viewer consumes the streaming-checkpoint fields the runners write (
provenance.run_status/provenance.checkpoint/ per-sectionpartial; no QVF schema bump was needed — they validate against v1).QVFReadergainsrun_status/checkpoint_info/section_is_partial. The Job Manager grows a Watch live action on running jobs that opens the daemon-advertisedcheckpoint_qvf_pathand enables auto-reload; an app-bar chip pulsesrunning(#seq · iter N · E … Eh) and settles greenconverged/ redfailed, with the finish announced in the status bar; still-growing sections are badged streaming in the sidebar; a growing trajectory is followed at its head. Submit-from-viewer defaults to streaming a checkpoint (checkpoint_qvf=$VQ_WORKDIR/checkpoint.qvf, opt-out switch) so a just-submitted job is immediately watchable. Verified end-to-end with trusted-event Playwright against a progressive checkpoint producer (vibe-view/docs/qa_live_stream.py, 5/5). Pins: streaming reader surface, chip formatting, watch-live wiring, terminal-status announcement, trajectory head-follow, and live-checkpoint input generation invibe-view/tests/test_controller_state.py. Docs:vibe-view/docs/user_guide/live_reload.md§ Streaming a running job; roadmap A4 / C2 / C3 marked shipped.vibe-view live reload (Avogadro-parity M2): content-aware file watching with per-section hot-reload.
vibeview.file_watcheris rewritten around a content fingerprint (per-section QVF-manifest digests, whole-file hash fallback), a settle delay, and a mid-write hold, replacing the mtime+size compare that fired on half-written archives (2026-07-02 audit finding #7). The firedQVFChangecarries a per-section diff (changed/added/removed_sections,manifest_meta_changed— visible when a streaming job’sprovenance.run_statusflips); a new synchronousQVFChangeTrackerdrives the viewer’s asyncio watch loop. The viewer hot-reload (apply_watcher_event) now swaps in a freshQVFReader— the old auto-reload reused the previous reader whose open zip handle + member cache served stale data — refreshing only the active panel for 2D-panel-confined changes and otherwise running a full reload with the user’s camera preserved and active section restored. Fixed in the same milestone:_reload_active_filehad been truncated by a mis-indented SSAO block (aec246e5), blanking the 3D scene on every file switch/open/reload, andtoggle_file_watcher/toggle_dark_uiwere nested insidecamera_presetso they never registered until a camera preset was first clicked. Pins:vibe-view/tests/test_file_watcher.py(11 tests), watcher hot-reload + scene-rebuild regression tests invibe-view/tests/test_controller_state.py. Docs:vibe-view/docs/user_guide/live_reload.md.Multi-k GPW UKS is now reachable through
run_periodic_job.run_periodic_job(method="UKS", functional=..., jk_method="gpw", kpoints=...)dispatches torun_periodic_uks_gpw_multi_kand adapts the result to the runner’s multi-k open-shell shape (gpw_uks_multik_result_to_runner_shape, mirroring the multi-k GDF UHF/UKS surface: per-spin per-k MO/density/Fock lists, per-k overlap/hcore,<S²>via the shared spin-contamination diagnostic) — the standard.out/.system/.qvfwriters work unchanged. Completes the prompt-9 wiring; multi-k UHF/hybrids stay fail-closed (per-k exact exchange), anddft_plus_u/open-shell smearing on this route raise with clear messages. Pins: runner-vs-library energy parity (1e-8), doublet<S²> = 0.75, output files, UHF gate (tests/test_periodic_runner_gpw_dispatch.py).Canonical (non-DF) CCSD / CCSD(T) and UCCSD / UCCSD(T).
CCSDOptions(density_fit=False)now runs the conventional coupled-cluster route on exact four-index MO integrals instead of raising “canonical (non-DF) CCSD is not implemented” (the blocker for strict conventional all-electron ORCA parity in the release-paper rerun, job df5eec9adb96). The integral blocks are assembled by the neweri_mo_pair_transform(exact AO→MO chemists’ pair transform,cpp/src/integrals.cpp); the amplitude equations, DIIS, and the (T) correction are shared with the DF path unchanged. Closed-shell (RHF), open-shell (UHF), and ROHF references all support the route; no RI auxiliary basis is required or used, so it also works on basis sets without a registered-riauxiliary (e.g. STO-3G). Small-molecule by design (holds the AO ERI tensor in memory); DF remains the default. Provenance is now truthful for the non-DF route: the.outblock and the structured-logccsd_convergedevent reportAlgorithm = CCSD(T)/UCCSD(T)without theDF-prefix,Density fitting = off, and no RI auxiliary line; the citation assembler drops the DF-CCSD integral-assembly reference (DePrince-Sherrill 2013) on non-DF runs (assemble(cc_density_fit=False)) while the defining CC + (T) papers still fire. Validated all-electron against conventional PySCF CCSD(T) / UCCSD(T) to < 5e-9 Ha (H2O and OH radical, cc-pVDZ) and against the in-repo spin-orbital SGWB anchor on exact-factorised integrals (tests/test_ccsd_canonical_noDF.py, 9 tests). FNO and the DLPNO pilots remain DF-only. Docs:user_guide/ccsd.mdgains “Conventional (non-DF) integrals”.
[v0.15.17] - 2026-07-02 - Neese’s Cheetah¶
Added¶
Relaxed MS/XMS-CASPT2 analytic nuclear gradients. SA-CASSCF- referenced multi-state CASPT2 runs with
CASPT2Options(compute_corr_grad=True)(the single-state opt-in flag) now return the fully relaxed gradient of the target multi-state root: the SA-CASSCF Lagrangian (orbital + external-CI z-vector against the state-averaged Hessian; Stalring/Bernhardsson/Lindh 2001) plus the state Lagrangian for the model-state mixing (Celani-Werner 2003; the OpenMolcas MCLR “SLag” mechanism), with all geometry derivatives on the symmetric (Löwdin) orbital connection. Rewrite ofgradient/_ms_caspt2_grad.py; the effective-Hamiltonian pipeline is factored intosolvers/_ms_caspt2._heff_pt2_blocksso energy and gradient share one implementation. FD-validated to ~1e-7 Ha/bohr on H2 and LiH (mixed states) in bothmsandxmsmodes and cross-checked against OpenMolcas MCLR+ALASKA. Replaces the frozen-PT2 transition-density gradient, whose diagonal terms silently assumed state-specific orbital stationarity and produced wrong-sign gradients on SA references (test_ms_gradient_vs_fdtolerance tightens 5e-2 → 1e-5). Citations (Stalring 2001, Celani-Werner 2003) fire whenever a multistate run carries a gradient. Gradient CI-response is gated to small active spaces (max_det_response); the analytic transition-RDM CI Lagrangian for large CAS is on the roadmap.vibeqc.ase_optimizers– ASE optimizer availability probing + the old-ASE/new-SciPy compat shim (glycine SI matrix follow-up, items the rp167 matrix hit after queue dispatch):ensure_ase_scipy_compat()restores thescipy.integrate.cumtrapz/trapz/simpsaliases that SciPy= 1.14 removed and ASE < 3.23 still imports, so the ASE line-search optimizers (
BFGSLineSearch,LBFGSLineSearch) import cleanly on old-ASE + new-SciPy deployments. Applied automatically atimport vibeqc.aseand at every internalfrom ase.optimize import BFGSLineSearchsite (run_job ASE backend, semi-empirical preoptimize + variable-cell optimize).resolve_ase_optimizer(name)/available_ase_optimizers()/ase_optimizer_availability()/format_ase_optimizer_report()probe the active ASE installation and fail early with a full availability report when a requested optimizer class does not exist there (FIRE2,RFOare absent on ASE 3.22.x), instead of a bare ModuleNotFoundError halfway through a queued batch. Accepts canonical keys, ASE class names, andase-prefixed batch-matrix spellings; re-exported fromvibeqc.ase.Registry notes carry the hard-won operational guidance: GPMin’s surrogate steps and MDMin’s default
dt=0.2can drive expensive ab initio geometries into SCF-hostile regions.Docs:
user_guide/ase_integration.mdgains “Choosing an ASE optimizer”;user_guide/geometry_optimization.mdgains the nativegeom_opt=optimizer-family section with a live progress-table sample. Tests:tests/test_ase_optimizers.py(20 tests).
ASE calculator: fail fast + diagnose on pathological optimizer steps.
vibeqc.ase.VibeQCnow rejects collapsed geometries before any SCF runs: when the closest atom pair falls below the newmin_pair_distanceparameter (default 0.25 A;Nonedisables) it raises the typedVibeQCGeometryErrornaming the offending pair, instead of grinding through the full SCF iteration budget on a meaningless Hamiltonian (glycine SI matrix: GPMin/MDMin steps ended in 200-iteration SCF failures at energies ~100 Ha above the minimum).VibeQCSCFConvergenceErrormessages now carry the closest-pair geometry diagnostic plus step-size remedy hints (maxstep / MDMin dt / quasi-Newton), so batch harnesses get an actionable optimizer-level failure summary.SCF warm-start across native geometry-optimization steps.
vibeqc.geomopt.MolecularSCFProvider(the provider behindrun_job(optimize=True, geom_opt=...)) now restarts each step’s mean-field SCF (rhf/uhf/rks/uks, gas phase) from the previous step’s converged density via the existinginitial_guess=READprojection machinery, with an automatic cold-guess retry if the warm tail fails to converge. Optimizer steps are small, so the projected density is near-converged and the per-step SCF iteration count drops substantially (H2O/6-31G PBE smoke: 10 -> 7 iterations already on a small displacement; large-basis hybrid jobs like the glycine rp167 matrix at ~40 cold iterations/step gain far more) – often the difference between finishing inside a queue walltime andaborted_by_queue. Opt out withMolecularSCFProvider(..., warm_start=False). The provider also now fails closed: a nonconverged mean-field SCF raises a clear error instead of silently feeding a garbage gradient to the optimizer (matching the ASE calculator’s typed error and run_job’s v0.15.x fail-closed direction). Tests:tests/test_geomopt_warm_start.py.
Fixed¶
Dispersion-corrected energies now report an explicit SCF/dispersion/total split in the machine-readable
job_endrecord. The glycine SI matrix collectedrun_job(...).energyfor its PBE0-D3(BJ) rows and silently dropped the -0.0097 Ha dispersion term (.energyis the bare SCF energy by design; the total lives on.energy_total), making the ASE D3(BJ) rows look identical to plain PBE0 while the native-optimizer rows carried the true total. Thejob_endstructured-log event now emitse_scf/e_dispersion/e_total(+e_gcp/e_srbfor 3c composites) whenever a post-SCF correction is active,_DispersionAugmentedgains an.e_scfalias, and the docs (dispersion tutorial, output-files guide, ASE integration guide) now state the.energyvs.energy_totalvs ASE-total conventions side by side. A cross-surface regression (tests/test_dispersion_energy_consistency.py) pins the ASE calculator, the nativeMolecularSCFProvider, andrun_job(...).energy_totalto the same D3(BJ) total within 1e-8 Ha and would have caught the SI-matrix inconsistency.Native geomopt correctness + observability sweep (fallout from the glycine optimizer SI matrix,
rp167-glyopt-*on twin):FIRE velocity mixing used the gradient direction instead of the force direction, so from the zero-velocity start the mixed velocity always pointed uphill, the power check always failed, the velocity reset to zero, and the step was exactly zero: the optimizer re-ran the SCF at the same geometry for all
max_iteriterations without ever moving an atom. The update now mixesvtowardF = -∇Eand computes the power asP = F·v(Bitzek 2006 conventions).EF/PRFO transition-state Hessian seeding unpacked
(energy, gradient)in the wrong order — the Baker negative-eigenvalue seed was built from the scalar energy instead of the initial gradient, and the initial point was evaluated twice. Fixed + the redundant provider call removed.geom_opt="dimer"withgeom_target="minimum"now raises a clear ValueError (the dimer method is a saddle-search method); the unregistered"newton"keyword was removed from the resolver map so it fails with the canonical availability list instead of a KeyError.run_jobgeometry-optimization gradient tolerance used a rounded 0.045 conversion for eV/Å → Ha/bohr; it now uses the exact CODATA factor (0.05 eV/Å→9.72e-4 Ha/bohr, was2.25e-3).run_job(geom_opt=...)on semi-empirical / MLIP methods now raises instead of silently running the ASE BFGS fallback while the output claimed a native geomopt optimizer (citations included).Per-step progress for every native optimizer (sd/cg/bfgs/lbfgs/ trust/rfo/gdiis/fire/ef/prfo/dimer): optimizers accept a file-like
progresssink, printstep / E / dE / max|g| / |step| / convflags plus an initial-evaluation marker, andrun_jobstreams the table into the job.outas the optimization runs — long queue jobs now show live per-step status instead of appearing hung.run_jobnow forwardsgeom_target,geom_hessian_init,geom_hessian_update, andgeom_opt_optionsto the native geomopt runner (transition-state searches and per-optimizer tuning are reachable from the high-level API).
QVF biomolecule metadata for ribbon/cartoon rendering. The
structuresection object may now carry optionalchains(list of chain ids),residues({name, seq, chain, atom_indices}, 0-based atom indices), andsecondary_structure({type, chain, start_seq, end_seq}withtypein helix/sheet/coil) fields. Supplied to the writer via abiomolecule_datacontext key onwrite_qvf/qvf_bytesand normalized (numpy ints/arrays coerced to plain int/list). Additive peer keys on the section object, riding the open Section schema like the live-checkpoint fields, with noqvf_versionbump; all three independently optional. Consumers that don’t understand them ignore them (vibe-viewSectionhasextra="allow", so files open with no reader change). Docs:docs/design_qvf_format.md§ 4.1; tests:tests/test_qvf_biomolecule.py.vibe-view now refits the client camera after periodic replication. Replicating a crystal updates the server scene to the expanded
nx * ny * nzsupercell, butVtkLocalViewkeeps its own client camera; the old server-only reset left replicated crystals zoomed into the original single-cell view, clipping atoms in generated crystal figures. The replication update now calls the local-view camera reset after the rebuild so the full replicated crystal is framed.
[v0.15.16] - 2026-07-02 - Neese’s Cheetah¶
Added¶
Γ-CCM neutral-gauge RIJCOSX on the direct-torus route (
run_ccm_rhf_direct_rijcosx). Neutral RI-J from the Γ-supercell cderi plus the periodic chain-of-spheres exchange (the validated M3b SR+LRKPointCosxKengine, run at Γ on the cluster supercell — real-space erfc-SR seminumerical exchange on the supercell Becke grid + the reciprocal erf-LR complement in the same G=0-dropped gauge) plus the same derived exchange-q=0seam asrun_ccm_rhf_direct(the seam-off offset identityξ_N·N_e/2is pinned on this route too). Measured accuracy vs the exact-K direct route: 22–30 µHa/cell on genuine-3-D controls (compact H₂ (2,2,2) torus, vacuum-padded dim=3 H₂ anchor), invariant under ω / LR-cutoff / COSX grid tier — i.e. the shared COSX engine’s own validated composition floor (0.024–0.027 mHa backend parity, M3b-4c); gates pinned at 5e-5 Ha/cell. Two loud scope exclusions:dim < 3raises (the LR complement on the dimension-collapsed reciprocal mesh cannot compose with the exact real-space SR — measured K error 0.08–0.6 on dim=1 chain toruses, growing with the SR share; the low-D fast exchange belongs to the D3 mixed-boundary kernel work), and the pre-existing ultra-diffuse SR envelope criterion warns (M3b-6).Γ-CCM direct-torus SCF (
run_ccm_rhf_direct) — a k-free production route with the derived exchange-q=0seam. One real Γ-supercell SCF (single real generalized eigenproblem over theN_c·n_μsupercell AOs, no k-mesh, no complex Bloch blocks) that reproduces the multi-k GDF (run_ccm_rhf_gdf) to ≤1e-8 Ha/cell: measured 1.7e-11 on the vacuum-padded H₂/STO-3G anchor (external PySCF KRHF −1.1182352381 Ha/cell matched to 1.6e-11), 2e-11 on 1-D H-chains (TRIM and non-TRIM meshes), 1e-9-class on ionic rocksalt LiH where the seam is Madelung-large. The exchange-q=0(BvK-Madelung,exxdiv="ewald") seam is implemented as a real-space Fock operatorK → K + ξ_N·S·D·S(ξ_N= the BvK-supercell probe-charge Madelung constant, shared verbatim with the multi-k driver), soF = ∂E/∂Dholds exactly; a deliberateexxdiv=Nonecontrol sits above the ewald run by exactlyξ_N·N_e/2per supercell (pinned — the seam is block-diagonal in occ/virt, so both conventions converge to the same density). One-electron and nuclear terms are the GDF driver’s own unit-cell lattice sums folded onto the torus (equality with the multi-k Hamiltonian by construction). The result records.exchange_q0(vibeqc.periodic.exchange_conventionlabels) and parity tests assert matched conventions. New modulevibeqc/periodic/ccm/direct.py; teststests/test_ccm_direct.py.
Fixed¶
ccm_neutral_cderisilently broke torus translation invariance on supercells larger than the flat ~15-bohr lattice cutoff. The Γ-supercell cderi build stores AO pairs at absolute supercell positions; once a replicated axis outgrewLatticeSumOptions.cutoff_bohr, the real-space image sums lost the cell that wraps edge pairs back onto the torus — measured 4e-2 J/K translation-symmetry violation and a 1.1e-4 Ha/cell energy error on an 18-bohr (3,1,1) chain supercell (TRIM-only meshes ≤ 12 bohr masked it, which is why the H₂ anchor and (2,1,1) gates never caught it).ccm_neutral_cderinow auto-widens the cutoff by the worst-case wrap vector over replicated axes (_supercell_wrap_lat_opts; override via the newlat_opts=kwarg) and the neutral kernel’s J/K commute with torus translations to machine ε at any supercell size (pinned intests/test_ccm_direct.py). Downstream, the no-truncation DLPNO gates are unaffected (neutral-vs-neutral comparisons cancel the kernel change); the scalable local-(T) semicanonical tolerance moved by 1e-11 on one pm/(4,1,1) case (retuned 1e-8 → 2e-8 with the exact-mode gate unchanged at 1e-10).
[v0.15.15] - 2026-07-02 - Neese’s Cheetah¶
Added¶
Opt-in live QVF checkpointing for
run_job/run_periodic_job(vibe-view hot-reload). Both runners acceptcheckpoint_qvf=<path>+checkpoint_every=N. When set, a QVF snapshot is written tocheckpoint_qvfas the job runs — carryingprovenance.run_status="running", a monotonicprovenance.checkpoint.seq(pluswall_time_s/written_atand, mid-SCF,scf_iteration/energy_eh), andpartial: trueon still-growing sections — then a terminal snapshot labeled"converged"on success or"failed"on crash (molecular path). Every snapshot is written atomically (temp file +os.replace) so a concurrent reader never sees a half-written zip. Per-iteration cadence (checkpoint_every=N) fires on the routes that stream through the shared progress logger — the periodic Ewald, GDF, BIPOLE, and GPW drivers — refreshing the checkpoint everyNSCF cycles. The periodic GPW route (run_periodic_rhf_gpw/run_periodic_rks_gpw_multi_kand the open-shell UHF/UKS/multi-k siblings) now threads the sameprogress=handle into its Python SCF loop, so GPW jobs get per-iteration cadence too. Only the molecular SCF still runs its loop in compiled C++ with no per-iteration Python hook, so molecular jobs emit a start + terminal frame only. These fields validate against the current QVF v1 manifest schema — no v3 schema bump required.write_qvfgains anatomic=flag andrun_status/checkpoint/partial_sectionscontext keys. New handle:vibeqc.output.checkpoint.QvfCheckpointer.resolve_progressnow passes a transparent progress wrapper (the checkpoint proxy) through unchanged instead of unwrapping it to the inner logger — without this the per-iteration hook was stripped by each driver’sresolve_progressnormalization, socheckpoint_every=Nwas a start/terminal-only no-op on every periodic route. Regression:tests/test_qvf_checkpoint.py,tests/test_periodic_runner_gpw_dispatch.py(GPW per-iteration cadence). (Ask 2 of the vibe-view Avogadro-parity cross-repo asks.)
Fixed¶
RSGDF dense G-mesh was incomplete on oblique cells; dense-core Γ GDF can now converge to PySCF parity via high-|G| tail completion. Two fixes for the P01-class MgO/STO-3G Γ RHF gap (
+PARITY_HELD): (1)rsgdf_dense_g_meshsized its integer index box byceil(G_max/|b_i|)+1, exact only for orthogonal cells — for oblique lattices the correct biorthogonal bound is|n_i| ≤ G_max|a_i|/2π(strictly larger; fcc primitive ≈ 15%/axis), so every mesh on fcc-class cells silently dropped G-points near the sphere boundary. (2)build_lpq_native_fft/build_lpq_bloch_native_fftaccepttail_ke_cutoff(runner:rsgdf_tail_ke_cutoff): the 2c metric AND 3c tensor G-sums are extended over the exact complementary reciprocal shell with the same analytic kernels, chunked — staying inside the FT representation, so DF consistency is preserved by construction (Lpq(ke=a, tail=b) ≡ Lpq(ke=b), pinned to 1e-9 on the fitted-ERI tensor). Unlike the real-space SR-replacement attempts, this cannot excite near-null metric modes. P01 MgO/STO-3G Γ RHF ladder vs PySCF GDF (−271.049457612534 Ha): −500 → −129 → −21.5 → −1.7 mHa → +1.3 µHa at tail = 400/800/1600/3200 Ha (base 200 Ha; the 3200 Ha parity run took ~55 min single-threaded) — the same level PySCF’s own GDF↔RSDF agree (~1 µHa). The dense-core Γ+PARITY_HELDbackend tag lifts automatically whenrsgdf_tail_ke_cutoff ≥ 10 × ζ_max(the steepest AO primitive exponent; P01: 10.7 × 299.24 ≈ 3200); undersized tails and the mdf path stay held, and the hold warning now carries this remediation. Regression:tests/test_rsgdf_dense_mesh_tail.py(mesh completeness vs brute force, tail ≡ big-mesh identity, hold-lift pins).
Added¶
Requesting k-point features for a molecule now fails with a targeted explanation. Band structures, DOS meshes, k-paths, and Monkhorst-Pack sampling are Brillouin-zone (periodic-boundary-condition) features; a molecule has no lattice. Previously
run_periodic_job(system=Molecule)died deep in setup withAttributeError: 'Molecule' object has no attribute 'lattice',run_job(mol, kpoints=...)raised Python’s bare unexpected-keywordTypeError, andKPoints.band_path/monkhorst_pack/band_structure_hcore/band_structure_projected/band_path_eigenvaluescrashed at whatever attribute they touched first. All seven entry points now raise up-front with the same message (molecules have no Brillouin zone; userun_jobfor molecules or build aPeriodicSystemfor crystals), andrun_jobtraps the common PBC-only kwargs (kpoints,dos_kmesh,jk_method,bz_integration,cutoff_ha) with a pointer torun_periodic_job. Shared guard:vibeqc.bands.require_periodic_system. Regression:tests/test_periodic_feature_input_guards.py.Compact multi-k GPW now supports meta-GGA (r2SCAN/SCAN). The compact Bloch path builds the kinetic-energy density from per-k Bloch AO gradients,
τ(r) = ½Σ_k w_k Σ_mn D_mn(k) ∇χ_{m,k}·∇χ_{n,k}*(compute_bloch_kinetic_energy_density; χ_k is Bloch-periodic, not lattice-periodic, so the gradients phase-unwrapu_k = e^{-ik·r}χ_kbefore spectral differentiation), and adds the per-k generalized-KS Fock block½∫v_τ ∇χ_{m,k}*·∇χ_{n,k}(project_vtau_to_bloch_ao) — lifting the meta-GGA gate placed when the compact Bloch path landed. The von Weizsäcker floor / density screen / stiffness guard of the v0.15.14 meta-GGA fix apply on both regimes. Pins: the multi-k kinetic-trace identity on compact Si, forced-compact vs molecular-limit parity on a vacuum cell (PBE control at 2e-6; r2SCAN at 1e-4 with the clamp-set sensitivity documented in the test), and a converged compact-Si r2SCAN SCF (tests/test_periodic_gpw_compact_multik.py).Coupled-pair variants of the canonical CCSD kernel: CCD, LCCD, LCCSD, CEPA(0..3). New
CCSDOptionsfieldcc_varianton the closed-shell DF kernel (cpp/src/ccsd.cpp), reachable throughrun_job(method="ccd" / "lccd" / "lccsd" / "cepa(0)".."cepa(3)")and the AutoCI-stylecitype=selector (these were gatedNotImplementedErrorroadmap names before). CCD freezes the singles; the linearized variants extract the degree-1 part of the canonical SGWB residual with an algebraically exact polynomial stencil (no second equation set); CEPA(1..3) add Meyer’s pair-specific EPV shifts in the closed-shell convention of Wennmohs and Neese (2008) / ORCA MDCI. Validated three ways (tests/test_cc_variants.py): CCD/LCCD/LCCSD against the in-repo FCI-anchored spin-orbital oracle (sub-nHa), stencil exactness by h-independence (machine epsilon), and CEPA(1..3) against out-of-process ORCA 6.1 RI-CEPA/n with the identical auxiliary basis (0.02 / 0.03 / 0.00 uHa on H2O/def2-SVP; canonical orbitals == ORCALocalize false). Citations (Cizek 1966, Meyer 1973, Wennmohs-Neese 2008) fire per variant into.out/.bibtex/.system. Closed-shell RHF reference only; no (T) on the variants; open-shell requests raise with a pointer toccsd(t).CCSD[T] (= CCSD+T(CCSD)) bracket triples via
triples="[t]". The perturbative-triples kernel now reports its two standard pieces separately (fourth-order E[T] onCCSDResult.e_t4, fifth-order singles-triples E_ST one_t5_st; E(T) = E[T] + E_ST), and the previously gatedtriples="[t]"/"+T(CCSD)"selectors (Urban, Noga, Cole, Bartlett 1985) select the bracket correction intoe_tthroughrun_jobandCCSDOptions.triples. The.outtriples block prints the decomposition for(T)runs. Closed-shell kernel only;A-CCSD(T)remains gated (needs Lambda). Citation (urban_ccsd_t_1985) fires on theccsd[t]route.
[v0.15.14] - 2026-07-02 - Neese’s Cheetah¶
Added¶
Multi-k GPW UKS (pure DFT).
vibeqc.periodic_gapw_open_shell.run_periodic_uks_gpw_multi_k(system, basis, kmesh, functional=...)runs spin-polarised LDA/GGA KS-DFT over a k-mesh on the GPW route — the open-shell closure of “GPW multi-k only supports RKS”. Both collocation regimes of the closed-shell multi-k driver are supported: molecular-limit cells fold each spin density to a Γ-summed density matrix, compact crystals build per-spin Bloch densities and projectV_H + v_eff_σper k onto Bloch AOs (with the same linear-dependence projection and per-k Pulay DIIS as the compact RKS path). Hybrids (per-k exact exchange) and meta-GGA (per-spin τ) fail closed; runner dispatch/output wiring is a follow-up — therun_periodic_jobmulti-k GPW gate message now points at the driver. Pins: Γ-mesh parity with the Γ UKS driver, singlet parity with multi-k RKS on both regimes, doublet convergence (tests/test_periodic_gpw_uks_multik.py).
Fixed¶
The GPW meta-GGA path was dead on arrival — now works (r2SCAN/SCAN) and fail-closes on stiffness-divergent functionals. Three stacked defects: every GPW SCF called the XC evaluator without
basis/density_matrix, so meta-GGA raisedValueErrorbefore the first τ build; the τ builder itself crashed on a flat-vs-grid shape mismatch; and thev_τFock term was projected as a multiplicative potential∫χχ·v_τinstead of the generalized-KS matrix element½∫v_τ ∇χ_μ·∇χ_ν(the derivative ofE_xc[τ[D]], matchingcpp/src/periodic_xc.cpp) — the same wrong projection sat in the range-separated, orbital-transformation, and GAPW-augment drivers. The rebuilt path adds the von Weizsäcker τ-floor with the constrained chain rule at clamp-active points (v_σ += v_τ/8ρ,v_ρ −= v_τσ/8ρ²,v_τ → 0, so the Fock stays the exact derivative of the clamped energy), a density screen for the uniform grid’s unweighted vacuum voxels, and a fail-closed stiffness guard: TPSS-class functionals whose iso-orbital derivatives diverge at orbital critical points (sampled at full voxel weight by an FFT grid; one observed artifact converged to a spurious +0.32 Ha stationary point) now raise with a pointer to r2scan/scan orjk_method='gdf'. The finite-difference pindE_xc/dD == V_xcholds to ~1e-4 for r2SCAN (tests/test_periodic_gpw_mgga_fock.py). Γ open-shell GPW now gates meta-GGA up-front instead of dying in the C++ binding.vibe-view desktop app is now a real native window.
vibe-view desktop [file] [--port N]spawns the branded Electron shell (dock/menu-bar name “vibe-view”, molecule icon, About panel) instead of a browser tab, works from any directory, and auto-installs the Electron binary on first run (electron/install-electron.pynow also writes the npm wrapper files and rebrands + ad-hoc re-signsElectron.appon macOS). File → Open (⌘O), Open vibe-qc Input (.py), Open Recent (shared withvibe-view recentvia~/.cache/vibe-view/recent.json, migrated from the old config-dir location), Browse Directory (⌘B), drag-and-drop onto the window, and macOS dock-drop all route through the newvibe-view serve/open?file=endpoint, which spawns a per-file 3D viewer process on a free port behind a polling loading page and redirects when it is up. The browse landing page gained a quickstart/welcome panel.electron/README.mddocuments the architecture and.apppackaging.The desktop browse page can navigate the whole filesystem. The launch directory is just the starting point:
GET /browse?dir=<abs>lists any directory, every listing carries ⬆ Parent and ⌂ Home links, and the Electron shell adds File → Open Folder… (⌘⇧O) plus folder drag-and-drop (a dropped directory browses instead of erroring through/open)./view?file=and/serve-qvf?file=absolute variants keep the details page and raw download working outside the launch root.
Fixed¶
vibe-view full-codebase audit (2026-07-02) — ~35 confirmed defects fixed. Both UIs were exercised live (trusted-event Playwright for the web viewer — 5/5 interactive checks; the Electron desktop app natively, with export outputs validated), the trame template wiring was scanned mechanically, and dependencies brought current (Electron v43 = latest; Python stack current). Highlights — science-wrong output: generated vibe-qc inputs embedded Å as bohr (geometries ran 0.529× compressed) and the periodic template didn’t even import
BasisSet;.pyinputs rendered 1.89× inflated (bohr stored as Å) and keyword/variable forms parsed zero atoms; trajectory/vibration “videos” were static; orbital animation crashed on import;replicate_cellcollapsed replicas; supercell/CIF used a transposed lattice convention; periodic bond inference was dead code and bonds didn’t replicate; Blender exports were non-runnable Python; SVG/PDF collapsed planar molecules to a line. Broken features: every keyboard shortcut, the right-click context menu, and hover tooltips were dead (four stacked client-JS bugs:@ctrl.setregisters no client trigger; the installer JS never ran;t.state.x = vis a silent no-op vst.state.set(); wrong canvas selector + vtk.js interaction overlay — all live-verified fixed);diff’s verdict was sign-inverted;batch-compareenergies were always empty andalign=Truedidn’t superpose; material presets repainted the scene grey; config silently overrode explicit--port; electronic-DOS lazy reads always failed; MO cubes read shifted data; in-memoryslice_qvfproduced manifest-only archives; SHA-256 mismatches were swallowed in two readers. 44 new regression tests. Deferred findings (point-group detection, miller_slab d-spacing, OSPRay API misuse, label billboarding, share.py orphan, …) are documented invibe-view/docs/audit_2026_07_02.md.vibe-view opencrashed at startup on any QVF with orbital sections (UnboundLocalError: is_periodic, regression ina782ed40): the orbital sidebar-group title consumed the periodicity flag ~500 lines before it was assigned. A cheap structure-JSON probe now sets it before the sidebar builds.The vibe-view 3D viewer rendered a blank page (Vue never mounted) since
cec4e80a: the presentation-overlay slide title used a Python-style conditional (x if cond else y) inside a Vue mustache — a template-compile SyntaxError (“missing ) after argument list”) that aborts the entire mount. Rewritten as a JS ternary.vq rechecks program runtime pins at dispatch. Scheduler and daemon dispatch now reject jobs whose configured program runtime no longer matches the submitted spec, closing the gap where a changed venv pin could slip through after submit-time validation.
vq detects Linux ORCA MPI loader failures. ORCA jobs that die in the MPI loader are classified explicitly, giving release-paper queue audits a clear scheduler/runtime diagnosis instead of a generic job failure.
vq scheduler runtime-pin dispatch coverage is pinned. The queue test suite now covers the runtime-pin dispatch gate so future scheduler changes cannot silently relax the protection.
[v0.15.13] - 2026-07-01 - Neese’s Cheetah¶
Added¶
vibe-view desktop can be launched from the CLI.
vibe-view desktopworks from arbitrary working directories and can bootstrap the Electron desktop wrapper on first use, making QVF inspection less dependent on a hand-prepared checkout.vq scheduler cap holds now explain their admission blocker. Queue status reports why scheduler jobs are waiting on cap admission instead of leaving paper-runner batches to infer the blocked state from repeated polls.
Fixed¶
Electron 43 desktop builds work on Node 26-era systems. The Python installer bypasses brittle npm paths, preserves symlinks with
ditto, and detects the venv Python used by the viewer desktop wrapper.vq scheduler retry states are visible. PBS marker probes and scheduler poll retries now surface their retry state so transient scheduler/fetch failures are distinguishable from permanent queue loss.
AICCM-B host detection recognizes the stable Twin runner. The dispatcher now detects the stable Twin runner shape used by the release-paper queue, avoiding false negatives in environment selection.
Release-candidate CI is now pin-able without blocking
main. GitLab CI keepsreleaseandrelease-candidate/*pipelines alive, runs the normal gates on release-candidate branches, and restores conservative auto-cancel behavior for ordinarymainpushes.
[v0.15.12] - 2026-07-01 - Neese’s Cheetah¶
Added¶
Compact multi-k GPW now runs on dense crystals via a Bloch real-space density path.
run_periodic_job(..., jk_method="gpw", method="RKS", kpoints=(n,n,n))on a compact crystal (e.g. primitive Si) previously either folded the multi-k density to a single Γ-summed density matrix — correct only in the molecular limit — and drove the GPW Hartree/XC field to a runaway (E ~ 3.26e15 Haon P07-shaped Si/def2-SVP at(4,4,4)), or fail-closed after that hazard was gated. It now builds the true Bloch densityrho(r) = Σ_k w_k Σ_mn D_mn(k) χ_{m,k}(r) χ_{n,k}(r)*from per-k Bloch AOs and projects the smooth potential back per k (V_mn(k) = ∫ χ_{m,k}* v_eff χ_{n,k}, Hermitian), so compact GPW crystals converge to finite, physical energies. Robustness pieces: linearly-dependent Bloch-AO directions (diffuse molecular bases drive the Bloch overlapS(k)indefinite on a dense cell) are projected out by a canonical orthogonaliser, and per-k Pulay commutator DIIS in the orthonormal subspace replaces linear Fock mixing. The molecular-limit multi-k and single-k GPW paths are unchanged. Pure LDA/GGA only; meta-GGA on the compact path is explicitly gated (itsv_tauneeds per-k Bloch AO gradients). Regression:tests/test_periodic_gpw_compact_multik.py,tests/test_periodic_runner_gpw_dispatch.py::test_run_periodic_job_gpw_compact_multik_si_converges.
Fixed¶
Dense gamma GDF fallback parity now stays explicitly held. The periodic dense-core Gamma GDF route keeps the parity-hold marker in place for the compact-cell fallback, preventing release-paper absolute-energy rows from looking like ordinary trusted parity results.
vq scheduler accounting includes deferred PBS reattach jobs. Jobs waiting for delayed PBS restart reattachment now count against the cap, avoiding an over-dispatch window during scheduler recovery.
CCSD density-fitting provenance is labelled in output. Canonical and density-fitted coupled-cluster outputs now make the selected provenance explicit for audit logs and paper archives.
ASE optimizer nonconvergence is classified as SCF nonconvergence. ASE geometry workflows now surface optimizer-triggered SCF failures with the correct category instead of hiding them behind generic execution errors.
vq submit enforces venv runtime pins and reports program identity. Managed venv jobs now honor configured runtime pins on submit and expose the runtime identity in status/output paths used by release-paper queue audits.
POB-TZVP-REV2 and shell matching are more robust. The bundled
pob-tzvp-rev2payload includes the complete runtime basis/ECP data, and displaced-geometry basis attachment uses tolerance-based shell-to-atom matching.vq PBS status and fetch recovery are more defensive. Scheduler fetch retries, qstat-derived liveness, failed restart reattach handling, and runtime-state display now report actionable states instead of ambiguous queue symptoms.
v2.0.0 - 2026-07-01 - Pauling’s Panther¶
vibe-view 2.0 achieves feature parity with Avogadro 2.0 as an interactive molecular editor and viewer with seamless vibe-qc workflow integration. 512 tests, 9 versioned feature releases, 24 CLI commands, 7 export formats.
[v0.15.11] - 2026-07-01 - Neese’s Cheetah¶
Fixed¶
Canonical CCSD/CCSD(T) outputs now state the frozen-core convention.
run_job(..., method="ccsd"|"ccsd(t)")writes the selected frozen-core orbital count to the.outfile and structured log, distinguishing chemical-core defaults from explicit all-electron settings for release-paper parity audits.Periodic GDF RHF energy runs no longer compute gradients by default.
run_pbc_gdf_rhf(...)now leaves the expensive post-SCF GDF gradient path off unlesscompute_gradient=Trueis requested, so plain energy probes do not spend minutes in numericalV_negradient work after SCF convergence.Gamma-point UKS/GDF now honors Fock-mixing convergence aids. The Γ-only GDF UHF/UKS drivers validate, log, and apply
fock_mixing, allowing high-levelrun_periodic_job(..., method="RKS", jk_method="gdf")ionic auto profiles to stay on the pure-GDF singlet-UKS path instead of falling back to the legacy molecular-limit bridge solely because FMIXING was selected.Dense-core Gamma GDF parity holds are now machine-readable. Γ-only RSGDF/MDF RHF results for tight dense-core ionic cells, including the P01 MgO/STO-3G audit case, now emit a warning and carry
+PARITY_HELDin the backend label so converged-but-held electronic-gauge values cannot look like ordinary PySCF absolute-energy parity rows.wB97X-3c now runs with its vDZP sidecar ECPs applied. Molecular SCF wrappers parse custom-core ECP sidecars into libecpint inline primitive blocks when no standard XML library matches, preserving the valence electron count and effective nuclear charges instead of gating the composite as
pending_ecp_inline.Single-atom open-shell AUTO guesses avoid the symmetric SAD false basin. UHF and UKS now route isolated open-shell atoms through the existing PATOM in-field split when
initial_guess=AUTO, while preserving explicit SAD and ordinary molecular AUTO behavior. This restores the Si/LANL2DZ ECP triplet to the PySCF-parity-3.6756963193 Haterm instead of the higher-3.4748021557 Hastationary point.Standalone double-hybrid dispatchers now emit full output bundles.
run_b2plyp(...),run_dsd_pbep86(...),run_revdsd_pbep86(...), andrun_pwpb95(...)now write.out,.system, Molden, XYZ, population/property, citation, and default-on QVF sidecars when called withoutput=..., matching the release-paper archive requirements.GFN2-xTB parameter cache misses now fail with a structured offline-data error. If the local parameter cache is absent and the upstream parameter file cannot be fetched, the loader raises
GFN2ParameterUnavailableErrorwith a paper-runner-readable message instead of surfacing a rawurllib/DNS traceback.High-level MP2 output now names the default route.
run_job(..., method="mp2")prints that it is using direct canonical MP2 (or UMP2/ROHF-MP2 for open-shell references) with no RI auxiliary basis, so paper parity checks can distinguish canonical MP2 from explicit RI-MP2 comparisons.Periodic runs fail closed when requested POB ECP sidecars are missing. The periodic runner now detects absent POB ECP data before launching the calculation, producing an explicit input-data error instead of drifting into an invalid all-electron setup.
The bundled POB-TZVP-REV2 runtime basis now includes heavy ECP elements. The shipped libint
.g94payload and QVF sidecar are refreshed from the existing full POB source so Ag, Au, and W release-paper cells can build the valence basis before periodic ECP auto-attach runs. Stale build/deploy basis overlays are ignored unless they also carrypob-tzvp-rev2.g94.Periodic QVF output is more robust for complex and empty occupations. QVF writers now preserve complex periodic wavefunction data, tolerate empty occupation arrays, and gate unsupported complex density-output folds instead of emitting malformed visualization sidecars.
Reduced-k density output folds use the requested output convention. Periodic output folds reduced-k densities consistently for the selected route, keeping QVF and property sidecars aligned with the SCF sampling.
Selected-CI PT2 output reports zero selected corrections explicitly. Release-paper logs now distinguish a true zero correction from an omitted PT2 reporting path.
GFN2-xTB third-order parameters now use tblite’s scale convention. The on-demand GFN2 parameter parser stores
GAM3with the required 0.1 factor and invalidates stale caches with unscaled values.vq remote and scheduler operations gained defensive diagnostics. Multi-user remote fetch tarballs, daemon/scheduler runtime health checks, watchdog cgroup-scope sampling, and PBS missing-marker forensics now fail with clearer state instead of ambiguous queue/fetch symptoms.
[v0.15.10] - 2026-07-01 - Neese’s Cheetah¶
Added¶
vq admin update supports post-update scripts. Venv programs can set
post_update_scriptto run a deployment finalizer after a successful pull, branch/tag check, and mainupdate_script. This givesvibeqc-releasea supported hook forscripts/setup_basis_library.shso managed hosts install bundled basis data after release rebuilds.vq scheduler job scripts have host-level hooks. Scheduler hosts can set trusted
scheduler_prologueandscheduler_epilogueshell lines inconfig.toml; the generated qsub script runs them inside the job working directory before and after the user command, giving Twin-style sites a configurable place for module/env/copyback glue.vq submit carries program identity into jobs.
vq submit --program NAMEnow validates local specs against[programs.NAME], storesspec.program, forwards the label through remote and scheduler-driver submits, exportsVQ_PROGRAMto local and scheduler jobs, and shows the identity invq status. Scheduler-dispatched jobs now also receive the sameVQ_ARRAY_*,VQ_CHAIN_*, andVQ_RERUN_*metadata as local jobs.vq scheduler hosts accept vq-managed arrays and chains.
vq submit --host twin --array N ...and--chain Nnow create N driver-owned specs tagged for the scheduler target, so Twin batches can use the existing vq array/chain metadata without relying on PBS-native array support.--rerun-untiland--rerun-maxare stamped onto each generated spec in the batch.vq daemon separates scheduler submissions from local process caps. Driver daemons now expose
--max-scheduler-jobs, scheduler-backed jobs no longer consume local--max-jobs/ CPU / memory gates, and scheduler polling/fetch operations get bounded retries with longer qstat/tar timeouts.vq status estimates pending queue wait from retained history. Pending jobs now show a best-effort dispatch-turn ETA when completed-job history can estimate the jobs ahead, matching by tag, command, and CPU count.
Fixed¶
Molecular SCF jobs now fail closed on nonconvergence before post-SCF work.
run_job(...)detects unconverged HF/DFT references before gradients, TDDFT, coupled cluster, and other dependent stages, raising a structured error instead of letting downstream code consume invalid orbitals.Periodic EWALD nuclear-attraction FT builds are chunked for large auxiliary grids. The Ewald
V_ne(G)builder now streams reciprocal vectors in bounded chunks, avoiding memory spikes in the release-paper periodic validation cases without changing the energy expression.True-periodic direct HF gradients now fail closed. Direct periodic HF gradient routes are gated where the lattice-gradient convention is not yet complete, preventing paper workflows from silently using an unsupported true-periodic path.
Periodic density mixers now expose an API guard. Multi-k periodic runs reject unsupported density-mixer selections clearly, while preserving the validated DIIS/default path and documenting the guarded surface.
The C++ bindings expose the Hückel initial-guess alias. Both
huckelandhueckelspelling routes now reach the same initial-guess enum through Python.vq status explains pending-admission blockers. Pending jobs now report the resource or scheduler reason that is keeping them out of admission, so queue triage does not require reading daemon internals.
vq scheduler queued status is clearer. Scheduler-backed jobs now distinguish local pending, admitted, and remote queued states more consistently in
vq status.Open-shell molecular Casida TDDFT now runs for UHF/UKS references.
run_tddft_casida_uhf(...)builds the unrestricted A/B response matrices, including same-spin exchange, cross-spin direct coupling, and the spin-polarised LDA/GGA/global-hybridf_xcblocks. The high-levelrun_job(..., tddft=True, tddft_type="casida")path now uses it for UHF/UKS instead of rejecting open-shell full response; range-separated hybrids still fail closed until attenuated ERIs exist.Open-shell Γ GAPW DFT now has a polarised augmentation-energy path.
run_periodic_uks_gapw(..., functional="lda"/"pbe"/...)no longer raises from the missing spin-2 per-atom XC energy correction. The GAPW Fock-side polarised XC augmentation and final total-energy assembly now use matching hard-minus-soft alpha/beta densities; meta-GGA GAPW remains gated until tau augmentation exists.Gamma open-shell GPW DFT+U now runs through the requested backend.
run_periodic_job(..., jk_method="gpw", method="UHF"/"UKS", dft_plus_u=...)now adds the Dudarev per-spin +U energy and Fock terms and recordsgpw_uhf_gamma/gpw_uks_gammain metadata.Gamma open-shell GAPW DFT+U now runs through the requested backend.
run_periodic_uhf_gapw(...),run_periodic_uks_gapw(...), and the high-levelrun_periodic_job(..., jk_method="gapw", method="UHF"/"UKS", dft_plus_u=...)route use the same per-spin Dudarev convention as GPW and recordgapw_uhf_gamma/gapw_uks_gammain paper-audit metadata.Open-shell hybrid TDA now includes the spin-polarised
f_xckernel. UHF/UKS TDA builds the polarised LDA/GGA response blocks from the converged alpha/beta densities, and the high-levelrun_job(tddft=True)path forwards those densities instead of emitting a partial-kernel warning for UKS/PBE0.Periodic BIPOLE population sidecars now emit k-resolved Loewdin charges. The BIPOLE writer evaluates
diag(S(k)^1/2 P(k) S(k)^1/2)over the SCF k-mesh and writes numeric Loewdin charges to.population.*and QVFatom_properties. Ordinary bulk dipoles remain cleanly unsupported until a Berry-phase polarization route exists.Implicit-solvation output labels now track the actual route. HF/DFT
run_job(..., solvent=...)CPCM runs now printImplicit solvation (CPCM), while MSINDO’s GEPOL route keeps the COSMO label. The block also prints the gas-phase and in-solvent totals alongsideE_solv, so validation tables can compare the intended quantity directly. This is output metadata only; CPCM/COSMO energies and tolerances are unchanged.FNO-CCSD(T) now preserves explicit all-electron requests through
run_job.CCSDOptions(fno=True)still follows therun_jobchemical-core default, butCCSDOptions(fno=True, n_frozen_core=0)now remains all-electron instead of being silently coerced back to frozen-core. The no-truncation FNO path is regression-pinned against canonical all-electron CCSD(T).GFN2-xTB third-order parameters now use tblite’s scale convention. The on-demand GFN2 parameter parser stores
GAM3with the required 0.1 factor, invalidates stale caches with unscaled values, and keeps the positive-GAM3 stability gate pinned against the corrected values. The experimental faithful-AES path now includes its atom-resolved Fock potential; the strict translated-water xtb parity gate remains xfailed until the remaining short-range H0/Pauli gap is closed.
[v0.15.9] - 2026-07-01 - Neese’s Cheetah¶
Superseded quickly by v0.15.10 during release-paper hardening.
Added¶
Input/output workflow (v1.1): AST-based
.pyinput parser, script generator with 5 calculation templates, ASE format supportInteractive editor (v1.2): click-to-select, undo/redo, element palette, auto-bonding, add/delete/change atoms
Build tools (v1.3): 12-fragment library, H-addition, supercell builder, 360 turntable export, presentation mode
vq integration (v1.4): submit button, job monitor with auto-refresh, auto-open results
Crystal builder (v1.5): 23 space groups, cell parameter editor, fractional/Cartesian conversion, Miller slab cutter
Rendering presets (v1.6): 6 material styles (CPK glossy, matte, glass, metallic, toon, scientific), SSAO ambient occlusion, shadow mapping
POV-Ray export: CPK spheres, bond cylinders, 3-point lighting
Blender export: self-contained Cycles scene with PBR materials
Collaboration (v1.7): shareable URLs, iframe embed, session management, 3D annotations
Desktop packaging (v1.8): Electron scaffolding, tray icon, file associations, auto-update
Supersampled screenshots: 2x SSAA with Lanczos downsampling
512 tests (509 passed, 3 skipped) across 16 test files
[v0.15.8] - 2026-06-30 - Neese’s Cheetah¶
Fixed¶
Periodic BIPOLE population sidecars now cover Mulliken and Mayer output on the same operator-cell support as the SCF energy. Converged BIPOLE SCFs write lattice-summed Mulliken charges and Mayer bond-order sidecars without falling through to molecular shape assumptions. Loewdin and ordinary dipole output remain cleanly marked with structured
unsupported:reasons until their periodic conventions are implemented.Periodic GDF overlap screening now recovers full def2-SVP support for diffuse tight-cell cases. The overlap builder expands the lattice shell search enough for screened def2-SVP cells such as MgO, avoiding truncated support in population and post-SCF artifacts.
Hybrid TDDFT now receives the converged density in the high-level runner path. Molecular hybrid TDDFT jobs no longer lose the reference density when dispatching through
run_job.Multireference jobs fail earlier and more clearly when the requested calculation is not viable. V2RDM jobs now fail closed on nonconverged solver state, and CASPT2 active-space memory is preflighted before launching jobs that would otherwise run into an avoidable allocation failure.
Open-shell molecular SCF optimization is more stable for feature-heavy jobs. The molecular runner preserves the resolved open-shell options through optimizer and post-SCF setup paths.
Twin scheduler operations in
vqare clearer and safer. Scheduler-host fetch/status messages now spell out the daemonless Twin path, fleet update fan-outs skip scheduler-only hosts,vq doctorcan preflight scheduler-side helpers, andvq top/vq usageexpose elapsed and resource-pressure accounting for retained jobs.
Experimental¶
CASPT2/NEVPT2 numerical Hessian, Dyall chain-rule, and CP-MCSCF Lagrangian gradient helpers landed behind explicit/gated paths. These are internal MRPT hardening pieces for the release-paper calculations and are not a production headline.
[v0.15.7] - 2026-06-30 - Neese’s Cheetah¶
Changed¶
QVF archives are now emitted by default.
run_job,run_periodic_job, andOutputPlan.from_run_job_kwargsdefaultoutput_qvf=True, while callers that want the old behavior can still passoutput_qvf=False.
Fixed¶
Periodic BIPOLE population sidecars now report lattice-summed Mulliken charges. Converged BIPOLE SCFs write
.population.txt/.population.jsonwith numeric Mulliken charges from real-space density and overlap blocks on the same operator-cell support used by the SCF energy contraction. Loewdin, Mayer, and ordinary dipole output remain cleanly marked with structuredunsupported:reasons until their periodic conventions are implemented, so raw NumPy shape exceptions from molecular property helpers stay out of artifacts.
[v0.15.6] - 2026-06-29 - Neese’s Cheetah¶
Fixed¶
Multi-k periodic Gamma-only artifacts now use the actual Gamma k-point. Molden-style fallbacks select the explicit k=0 block instead of assuming the first item in a multi-k result list is Gamma, and complex per-k density output now requires k-point and weight metadata before folding to a real lattice density.
Molecular
run_jobnow acceptsinitial_guess=. The high-level keyword maps to the resolved RHF/UHF/RKS/UKS options, including post-SCF reference SCFs, and READ/FRAGMO require their matching restart or fragment state.
[v0.15.5] - 2026-06-29 - Neese’s Cheetah¶
Fixed¶
DLPNO-CCSD restores the release-paper pair-screening default.
LocalCCSDOptions.tcut_pairsis back at1e-4, after a previous cleanup accidentally reset the default to zero. The M16 n-butane / cc-pVDZ release-paper regression now documents the ORCA TightPNO no-frozen-core comparison and keeps the remaining local-correlation convention gap explicit instead of letting the default drift silently.DLPNO-CCSD(T)
.outfiles now print the audit components needed for the paper tables. Runs emit named CCSD correlation, triples correction, final DLPNO-CCSD(T) correlation, and final DLPNO-CCSD(T) total lines, with a regression that pins the M16 n-butane / cc-pVDZ output block.Periodic post-SCF outputs now handle valid complex Bloch densities. Population summaries, lattice density folding, and QVF real-only artifacts accept Hermitian complex densities from multi-k periodic jobs, strip only negligible imaginary residuals where the file format is intentionally real, and fail closed on genuinely complex artifacts.
vq programsnow detects broken ORCA MPI startup on macOS and Linux. Registered ORCA binaries probe their siblingorca_startup_mpi, so a serial-OK ORCA install whose parallel launcher cannot loadlibmpi.40.dyliborlibmpi.so.40reports an explicit serial-only warning before another%pal nprocs Njob is submitted. The operations runbook documents both the HomebrewDYLD_LIBRARY_PATHrecovery path and the LinuxLD_LIBRARY_PATHvariant; release-paper ORCA jobs on affected hosts must run serially until the daemon environment exposes the matching MPI library directory.The multi-user pause/resume tests now ignore the ambient local queue. The fixture also sets
VQ_STATE_DIR, so full-suite runs on an active development host do not pause or count unrelated single-user jobs.
[v0.15.4] - 2026-06-29 - Neese’s Cheetah¶
Fixed¶
ORCA pure-GGA DF references now use ORCA’s accepted RI simple-input spelling. The molecular regression runner emits
RI+def2/Jfor pure-GGA density-fitting rows such as benzene/def2-SVP/RKS-PBE-DF, instead of the rejectedRIJkeyword. Parser keyword rejections now remain errors so bad generated ORCA input is not hidden as an unavailable reference.Periodic molecular properties now accept valid Hermitian complex densities. Multi-k Bloch densities may carry large imaginary off-diagonal entries while remaining exactly Hermitian; the property guard now checks the Hermiticity residual and only warns on genuinely non-Hermitian density assembly.
Explicit periodic DFT+U JK routes are honored.
run_periodic_jobno longer diverts explicit GPW/GAPW/BIPOLE +U requests through the legacy dispatch path, and the requested/resolved/executed route is recorded in.out,.system, and QVF metadata.The exact PBE 1996
6-311+G(3df,2p)basis is now bundled. Both6-311+g3df2pand the literal6-311+G(3df,2p)spelling load the same canonical Pople-component basis, so the paper reproduction no longer needs to skip or substitute that row.
Testing¶
The release gate baseline removes six files that were green in the v0.15.2 gate. Future non-PASS results in those files now count as new reds:
tests/test_periodic_accelerator_uniformity.py,tests/test_periodic_gapw_gradient.py,tests/test_ase_periodic_gpw.py,tests/test_audit_20260530_periodic.py,tests/test_bipole_gradient.py, andtests/test_pw1pw.py.
Documentation¶
Codename artwork is now bundled with the docs. The changelog renders the available generated codename images on the matching v0.9.0-v0.15.0 release sections, and the v0.15 landing page now shows the
15aNeese’s Cheetah artwork above the DLPNO local-correlation headline.The v0.7.0 historical codename art now uses the compass-jellyfish variant. The original compass-object image remains bundled for the vibe-qc-and-AI talk; the changelog uses the animal-series version.
The landing page now keeps experimental MRPT gradient work out of the production headline. The current-release feature copy focuses on the stable CASSCF, TDDFT, and DLPNO surfaces.
[v0.15.3] - 2026-06-29 - Neese’s Cheetah¶
Fixed¶
BIPOLE pure-RKS now uses the exact analytical-FT periodic Hartree J. MgO/STO-3G LDA SHRINK-2 fixed-density diagnostics showed the old
J_SR(erfc) + J_LR(erf)split sat +14.99 mHa above the G=0-dropped exact Ewald-J reference on the same density, while widening only the reciprocal envelope changed less than 1e-8 Ha. Pure RKS now routes Hartree J through the exact analytical-FT, neutral-background convention used by the GDF reference; hybrid/RHF exchange andalpha_hf/exxdivpaths are unchanged.Regression-suite error rows are now classified instead of reported as broken calculations. The LiH/STO-3G/RKS-LDA dense-overlap Γ-only EWALD_3D diagnostic remains guarded and is emitted as
unavailable, noterror. The ORCA molecular runner preserves raw inputs, outputs, and logs for reference failures so generated-input problems remain debuggable instead of disappearing into the dashboard. The suite also writes self-contained run directories under~/vibeqc-runs(or--output-root) with per-code inputs, stdout/stderr, and parsed JSON.Release validation now fails early on runtime pin drift and accepts the documented validation API edge cases. The gate catches mismatched runtime pins before release artifacts are produced, while defaulted validation calls no longer reject supported inputs.
Release-paper RSH functional spellings now resolve through the functional dispatcher. Common aliases used by the manuscript calculations route to the intended libxc definitions instead of failing at setup.
Gamma-only RKS parity now routes through the GDF path used by the current periodic reference checks. This keeps the regression harness aligned with the supported dense-crystal validation envelope.
vq and vibe-view received paper-calculation hardening. vq now retries fetches for located job owners and caps local thread pools from the CPU budget. vibe-view gained the Python API, section slice/merge/compare/serve workflows, standalone HTML/JSON/capture exports, and a reader-lifetime fix so chained API calls can reuse externally supplied
QVFReaderhandles.SCF and multireference diagnostics were tightened. The EDIIS hybrid switch metric is normalized, unsupported low-dimensional AICCM-B four-center jobs are skipped instead of launched, and the NEVPT2 trace-correction diagnostics were updated to keep the lower-error release-paper path.
Documentation¶
The website landing page now presents the v0.15 paper-hardening line as the current release focus. The old v0.14.0 CCSD(T) callout and stale v0.16.0 preview were removed, and stale dev-version examples were refreshed.
[v0.15.2] — 2026-06-28 — Neese’s Cheetah¶
Fixed: vq scopes every local job in a cgroup (even capless) and reaps it on teardown (2026-06-27)¶
On a cgroup host (
cgroup.available()true),cgroup.wrap_commandreturned the raw argv for any job declaring nomem_mb/cpus, so a capless job ran under pgid-only accounting. The pgid walk cannot see descendants that escape viasetsid/PR_SET_PGID(loky/joblib worker pools, ninja, mpirun), so their RAM stayed “unowned” by vq. This is the v0.15 memory-audit symptom class where a host shows tens of GB used against ~1 GB of summed process RSS.wrap_commandnow wraps any job carrying aunit_namein an accounting-onlyvq-job-<id>scope (no MemoryMax/CPUQuota), so the kernel cgroup captures every descendant and the watchdog’smemory.current/cpu.statreaders see the true total. A capless and nameless command (not a job) still runs raw.The daemon reaps that scope on teardown. A new
Daemon._reap_scopebest-effortsystemctl stops the unit at every hard-SIGKILL site (the STATE-2 startup reap, the STATE-3 kill escalation, the watchdog SIGKILL, the Popen-window reap) and at the single_record_finishhook, so descendantskillpgcannot reach die with the job instead of lingering. Idempotent, and a no-op without cgroups (macOS, no delegation). Tests:tests/test_cgroup.pytest_unit_name_without_caps_still_scopes,tests/test_terminal_reaping.pyTestScopeReaping. Found by the v0.15 release memory audit (finding #3).
Fixed: memory pre-flight no longer phantom-aborts large default-options jobs (incl. DLPNO) (2026-06-27)¶
vibeqc.memory._uses_direct_scftreated a missing options object (options=None) as a conventional in-core SCF, so the pre-flight added a densen_basis**4“ERI tensor” term and raisedInsufficientMemoryErroreven though the driver defaultscf_mode=AUTOruns the integral-direct path above 200 basis functions. Adlpno-mp2/dlpno-ccsd/dlpno-ccsd(t)job on n-octane / cc-pVTZ (492 bf, default options) aborted at ~523.9 GB (a ~436 GB phantom ERI tensor + headroom) before any compute – and gave the same number for all three methods because it was their shared SCF-reference estimate, not the DLPNO step (DLPNO itself is reduced-scaling: per-pair PNO domains, no densen_mo**4). NowNone/ an unsetscf_modeinherits the AUTO default (threshold 200), so the estimate matches what the SCF actually does: n-octane/cc-pVTZ RHF now estimates ~0.07 GB and the job runs (DLPNO-MP2 ~13 GB with a DF reference). The dense-ERI estimate is still used for small systems and explicitscf_mode='conventional'.The
InsufficientMemoryErrormessage now points at the real levers (density fitting, integral-direct SCF) instead of the stale “once shipped in v0.6+”.Docs:
docs/user_guide/dlpno_mp2.mdgains a “Larger systems” note – use a density-fitted SCF reference; the O(N^6) pilot (DLPNOCCSDPilotOptions) is a 64-bf correctness oracle, not the production path (the defaultdlpno-ccsdalready uses the reduced-scaling local solver). Tests:tests/test_memory.py(28) green.
Fixed: multi-k GDF auto-grows the one-electron S/T lattice cutoff so diffuse bases no longer abort with a spurious non-PSD S(k) (2026-06-27)¶
run_krhf_periodic_gdf/run_kuhf_periodic_gdfbuilt the one-electron overlap/kinetic lattice sums at the flat defaultLatticeSumOptions.cutoff_bohr= 15 bohr regardless of basis diffuseness (the GDF Lpq path already sized its cutoff per basis; the S/T sum did not). For a diffuse molecular basis on a dense lattice — e.g. def2-SVP on rocksalt MgO, whose most-diffuse Mg-s primitive is α≈0.034 bohr⁻² — the truncated Bloch sumS(k) = Σ_g e^{ik·g} S(g)is not a Gram matrix and acquires a large spurious negative eigenvalue (min eig ≈ −9.6e-2 at Γ for MgO (4,4,4)), tripping thescf_preflight_overlap_checknon-PSD (“critical”) abort whose message wrongly implied an image-summing bug. The image summation is correct; the cutoff was simply too short.Fix:
periodic_k_gdf._oneel_lattice_optsmeasures the loosest S/T cutoff that makes S(k) positive-semi-definite at every requested k-point (viaeigs_preflight.optimize_truncation, target severity “warn”) and raises the cutoff to it before the S/T build; it never lowers the cutoff. The a-priori PySCF rcut estimate is deliberately not used — it targets the AFT-corrected Lpq sum and under-sizes a bare overlap (≈11.6 bohr for a filtered def2-SVP/MgO basis whose overlap only turns PSD at ≈17 bohr). Overlap builds are milliseconds, so the cost is negligible against SCF. Tight/solid bases (sto-3g, pob-tzvp-rev2) are already PSD at 15 bohr and are returned unchanged, so every existing multi-k GDF job is unperturbed (full GDF driver suite green).A basis that is genuinely near-linearly-dependent on the lattice (full, unfiltered def2-SVP on MgO: converged λ_min(S) ~1e-9) cannot be made PSD by any cutoff; optimisation declines and the per-k preflight now aborts with an actionable
remediation_hint(filter diffuse primitives viavq.make_basis(..., exp_to_discard=0.1), or use a solid-state basis such as pob-tzvp-rev2;eigs_preflight.disambiguate_critical_overlap()reports which applies) instead of the generic “upstream bug” framing. The hint is a new optional parameter onlinear_dependence.scf_preflight_overlap_check; molecular callers that omit it keep the original message. Filtered def2-SVP/MgO (4,4,4) now auto-grows 15→17.81 bohr and runs past the preflight with every S(k) at “ok”. Tests:tests/test_oneel_lattice_cutoff.py.
Fixed: periodic analytic XC Pulay gradient kernel is now cross-cell (matches the cross-cell energy) (2026-06-26)¶
xc_lattice_gradient_contribution(and the UKS siblingxc_lattice_gradient_contribution_uks) — the analytic fixed-grid XC Pulay force — summed only the home-bra density sliceρ = Σ_s χ_0 P(s) χ_s, whilebuild_xc_periodic{,_uks}was updated to the full cross-cell densityρ = Σ_a Σ_s χ_a P(s−a) χ_s(the dense-crystal XC P0 fix). On a periodic density (P(g≠0) populated) the two disagree, so the analytic XC force missed the bra-image motion:test_periodic_xc_lattice_gradient_kernel_matches_fdwas red at 9.25e-3 Ha/bohr vs the fixed-grid finite difference ofbuild_xc_periodicE_xc (a home-only density was already exact). Both kernels now sum the bra over image cells using the IDENTICALbuild_braket_pairsscreening as the energy build, restoring kernel == FD (RKS test green; new cross-cell UKS pintest_periodic_uks_xc_lattice_gradient_kernel_cross_cell_matches_fdat ~4e-10). FD remains the production force path (the KS analytic gradient is a maintained preview). The original v0.14 bug inventory is retired and recoverable from git history.
Fixed: periodic meta-GGA (TPSS/M06-L) XC — von Weizsäcker τ floor cures the SCF + analytic gradient (2026-06-26)¶
Periodic TPSS/M06-L BIPOLE SCFs blew up — MO eigenvalues ~±1e7–1e8, TPSS even converging to a spurious +0.34 Ha basin — and the meta-GGA analytic gradient missed FD by 4–21 Ha/bohr. Root cause (re-diagnosed; the earlier “low-density singular v_σ” + clipping story was wrong): the cross-cell periodic XC assembly (
build_xc_periodic, sum over image cells with signed density blocks) produces a kinetic-energy density τ slightly below the von Weizsäcker boundτ_W = |∇ρ|²/(8ρ)— even negative — from roundoff at diffuse grid points. Unregularised meta-GGAs are singular there (libxc returnsv_σ/v_τ~ 1e16 with a correct E_xc), which contaminated the V_xc Fock’s virtual subspace. Molecular TPSS/M06-L were always fine; SCAN/r2SCAN self-regularise.Fix: clamp
τ ≥ τ_W(enforce_von_weizsaecker_floorincpp/src/periodic_xc.cpp) inbuild_xc_periodic,build_xc_periodic_uks, and both gradient kernels (xc_lattice_gradient_contribution{,_uks}) — per spin for UKS. It is a no-op wherever the physical bound already holds, so the converged energy is unchanged, and it is applied identically in the energy, the SCF Fock, and the analytic gradient so FD and analytic forces stay synchronised (clamping τ to 0 is not enough — libxc still diverges at τ=0 with finite σ). All the priorv_σ/v_τ±10 clips, the ρ<1e-12 τ-screen, and the ±10 MO-energy clips in the energy-weighted-density builders (bipole_gradient.py) are removed (they desynchronised the analytic gradient from its own energy).Result: TPSS H₂/STO-3G Γ E = −1.175 Ha (was +0.34), eigenvalues bounded ±0.5; the corrected-gauge Γ analytic gradient matches the production FD to ~1.4e-7 Ha/bohr for TPSS and M06-L (= SCAN). FD remains the documented production force path (the whole KS analytic gradient is a maintained preview), but meta-GGA is no longer a paper-blocking exception. Tests:
tests/test_bipole_gradient.py(test_periodic_mgga_vxc_bounded_on_cross_cell_density,test_periodic_mgga_scf_eigenvalues_bounded,test_corrected_gauge_rks_mgga_gradient_matches_fd@slow). Seehandovers/HANDOVER_BIPOLE_GRADIENT.md(Case 2).
Performance: GPW SCF no longer rebuilds the grid collocation cache for its final breakdown (E3) (2026-06-26)¶
run_periodic_rhf_gpw/run_periodic_rks_gpwbuilt the iteration-invariant collocation cache (the 27-imageevaluate_aoAO-on-grid table — ~90 % of a single-point on a fine plane-wave grid) once for the SCF and again for the final energy breakdown (evaluate_gpw_energybuilt its own).evaluate_gpw_energynow accepts acollocation_cache=and the SCF threads its own in, so the breakdown reuses it (same basis + grid; bit-identical). A single H₂/STO-3G GPW single-point atcutoff_ha=200(160³ grid) drops 8.6 s → 3.8 s (~56 %); this is the per-displacement cost the GPW finite-difference gradient/Hessian tests pay, so thetest_periodic_gapw_gradientfast-lane time roughly halves.
Fixed: SLAB_EWALD_2D Hartree J is now Ewald-split — all-electron cores + polar slabs (2026-06-26)¶
The 2D-slab Hartree
J(vibeqc.ewald_composed_slab,CoulombMethod.SLAB_EWALD_2D, still SCF-gated) was built undamped full-reciprocal, which under-resolves the cusp of an all-electron core:½Tr[D·J]on a polar HF layer drifted ~0.14 Ha withke_cutoffand never converged.Jis now built Ewald-split, matching the already-split slabV_ne:J_short(erfc cusp, libint real-space, full-Bloch homogeneous density) +J_long(damped4π/G²·e^{-G²/4α²}reciprocal,g≠0) +J_g0(screened Parryg0(z), the same kernelV_g0/E_nnuse — the old bare|z|was the lone outlier). The cusp is exact in real space, so the reciprocal mesh converges at small|G|independent of the core.½Tr[D·J]is now α-invariant (the split parameter) and the polar bilinear total matches the core-converged vacuum-extrapolated EWALD_3D + Yeh-Berkowitz reference.This supersedes the “surface-dipole / z-profile first-moment bug” in the v0.14 open-bug inventory, which was a misdiagnosis: the z-resolved AO-pair density’s first moment is the correct Bloch-summed z-dipole
Σ_g ⟨μ,0|z|ν,R_g⟩(verified to 4.5e-11 vscompute_multipole_moments_lattice), not the home-cell⟨μ|z|ν⟩the old xfail compared against. At landing time this unblocked the Increment-4 SCF un-gate.Tests:
tests/test_j_slab_ewald_2d.py(test_j_alpha_split_invariant,test_polar_bilinear_total_alpha_invariant,test_polar_bilinear_matches_core_converged_3d@slow);tests/test_v_ne_slab_ewald_2d.py::test_z_profile_first_moment_is_bloch_dipole. Seehandovers/HANDOVER_SLAB_EWALD_2D.md.
Added: vibe-view — interactive bookmarks, session save/restore, orbital animation, batch –volumes, Jupyter %vibeview, CLI export/diff/validate/info (2026-06-28)¶
Interactive bookmarks — Save Current View captures camera + active section + isovalue + colormap + opacity as a named preset. Apply restores all five properties. Session save/load persists complete state to
.vibe-sessionJSON.Orbital animation — play/pause button in the wavefunction panel auto-advances through MOs at configurable speed, rendering each one.
Batch
--volumes—vibe-view batch --volumesrenders each volume section (density, orbitals, ELF, etc.) as separate PNGs alongside the structure.Jupyter
%vibeviewmagic —%load_ext vibeview.jupyterprovides inline structure rendering, data tables (charges, MO energies), SCF charts, and band plots in notebooks.New CLI commands:
vibe-view info(provenance + section sizes,--json),vibe-view export(geometry to XYZ/CIF/OBJ/glTF),vibe-view diff(compare two QVFs: energy delta, section overlap, RMSD,--json),vibe-view validate(full SHA-256 integrity check on all members).Density-difference UI — when two loaded files both carry density sections, the sidebar shows a Density Difference card for computing rho_A - rho_B.
Full tutorial at
docs/tutorial/working_with_vibe_view.md(80+ sections, 14 screenshots). Tests: 375 passed, 3 skipped.Additional CLI commands:
vibe-view compare(auto-compare-mode),vibe-view serve(web file browser),vibe-view slice(extract sections),vibe-view merge(combine QVFs). Enhanced--versionshows Python/PyVista/VTK versions.--sectionflag onopenfor auto-activation.--shortflag oninfofor one-line summaries. Export supports OBJ and glTF 3D formats.
Added: GFN2-xTB faithful anisotropic electrostatics (AES), experimental opt-in (2026-06-26)¶
What. A faithful atom-resolved GFN2 AES (Bannwarth, Ehlert & Grimme, JCTC 2019, doi:10.1021/acs.jctc.8b01176) behind the new
XTBSccOptions.aes_faithfulflag (default off — the shipped ad-hoc shell-resolved path is byte-identical; the existing GFN2 suite is unchanged). The flag-on path adds the on-site DPOL/QPOL XC kernel + CN-damped (fdmp3/fdmp5) inter-site charge-dipole / dipole-dipole / charge-quadrupole as a post-SCC energy correction at the converged density.Parameters (DPOL/QPOL ×0.01, tblite p_rad/p_vcn) are parsed in
gfn2_params.pyand carried onGFN2ElementData(dpol/qpol/mp_rad/mp_vcn); fetched at runtime, not bundled (LGPL-3.0).Validated by invariants (size-consistent 1/R³ tail, deterministic, physical charges, PES well) and qualitatively (correct repulsive dipole-dipole sign for parallel-translated waters). Not yet at quantitative xtb parity: the self-consistent (Fock-coupled) form and the parity gate are deferred to a live-xtb decomposition oracle (
handovers/HANDOVER_GFN2_AES.md).Tests fixed. Reconciled stale GFN2 docstrings (H₂O is 0.21 Ha off the xtb ref and passes, not “~0.8 Ha off / xfail”); rewrote the water-dimer xfail reason to the measured −3.7 mHa and the three real issues (translated-pair geometry isn’t an H-bond; SCC-only vs a dispersion-needing target; ad-hoc kernel). Wired a chemically-discriminating SCC-only dimer gate (
test_water_dimer_vs_xtb_oracle).Live xtb 6.7.1 oracle run (vq, maru — xtb fetched into the job workdir): the parity gate is now active (xfail at ±1.5 mHa over 5.6/6.0/7.0 bohr). It shows the translated water pair is GFN2-repulsive (+11.7 mHa at 5.6 bohr, D4 ≈ 0), so vibe-qc’s −3.8 mHa is the wrong sign; the dominant remaining gap is the H0/overlap (Pauli) term, not AES (
handovers/HANDOVER_GFN2_AES.md§6).
Changed: GAPW all-electron Γ SCF route un-gated (experimental warning retired) (2026-06-26)¶
The two open correctness bugs on the GAPW all-electron augmentation route are fixed (the analytic ∂E_H/∂D Hartree Fock + the bonded-molecule overlapping-augmentation double-count, both above), so the run-level
GAPWExperimentalWarningis retired for the Γ-point closed-shell RHF / RKS (LDA, GGA) and open-shell UHF augmentation SCF (run_periodic_job(jk_method="gapw")and therun_periodic_{rhf,rks,uhf}_gapwentry points + theGapwJBuilder/GapwAugmentationconstructors).Still gated / fail-closed (unchanged): multi-k GAPW (
run_periodic_rks_gapw_multi_k), range-separated hybrids, the OT solver, and the MPI z-slab build keepGAPWExperimentalWarning(the fixes were validated at Γ only); meta-GGA GAPW and open-shell GAPW-DFT (run_periodic_uks_gapwwith a functional) still raiseNotImplementedError(no τ / polarised-XC augmentation). The smooth-grid GPW route and its grid/smearing/atomic-grid helpers keep their ownGAPWExperimentalWarning(separate subsystem, not part of this validation).Accuracy is preliminary, not a correctness gate (see
docs/user_guide/gapw.md): the per-atom augmentation has a grid-convergent absolute residual that is large for second-row atoms at the defaultN=24grid (Ne +339, O +215 mHa; He −0.6), and pure GAPW-HF on light elements carries the intrinsic soft-basis bond residual (H₂ ~−108 mHa). GAPW is an all-electron DFT route; for quantitative molecular HF use GDF or BIPOLE. The.out“(experimental)” label stays.
Fixed: GAPW bonded-molecule overlapping-augmentation double-count (2026-06-26)¶
Symptom. Molecular GAPW over-bound the all-electron reference (H₂/STO-3G ~−101 mHa for LDA), scaling with augmentation-sphere overlap (
2·r_aug/d). The per-atom hard/soft augmentation double-counted the overlap region.Two root-cause fixes in
python/vibeqc/periodic_gapw_augment.py:Own-atom compensator —
compensator_density_on_atomic_grid(..., own_atom_only=True). The per-atom radial augmentation now uses only atom A’s own ρ₀ compensator; the global compensator belongs on the smooth FFT grid. Summing every atom’s compensator Gaussian on each isolated radial grid (and feeding it to the sphericalsolve_poisson_radial) double-counted the inter-atomic compensator interaction the smooth term already holds (H₂ ~+66 mHa).Partition-of-unity (
GapwAugmentation._partition_weight) — the augmentation integrals are weighted byp_A = w_A·max_b w_b / Σ_b w_binstead of the bare radial window, so overlapping spheres do not double-count the local XC/Hartree augmentation.p_Areduces to the window for an isolated atom ⇒ single-atom results (He/Ne/O) are unchanged (verified identical), and½ tr(D·J) = E_Hstill holds to ~1e-13 (the same weight feeds the analytic Fock and the energy).
Result. H₂/STO-3G molecular parity: LDA −3.5 mHa, PBE −1.7 mHa (both well under the ±5 mHa target, across two functionals so the pass is not a single-functional cancellation). The xfail
test_gapw_molecule_matches_molecular_xfailis now a real, parametrised (LDA + PBE) assertiontest_gapw_molecule_matches_molecular.Known residual (intrinsic, not the double-count). Pure GAPW-HF (no XC) on light elements still over-binds the all-electron GDF reference (H₂ ~−108 mHa): hydrogen’s tight STO-3G primitive is a valence function (H has no core) that the soft basis prunes from the bond, so the cross-atom bond density is represented with the soft basis. This is the intrinsic GAPW soft-basis approximation, distinct from the (now-fixed) overlap double-count; it is offset by the matching XC response for DFT (hence LDA/PBE pass). GAPW remains opt-in behind
GAPWExperimentalWarning.
Fixed: GAPW all-electron Hartree Fock is now the exact ∂E_H/∂D (P0) (2026-06-26)¶
Root cause. The experimental GAPW route’s
GapwJBuilder.build_J(python/vibeqc/periodic_gapw_augment.py) returnedJ_smooth(full basis) + Σ_a ∫χχ(V_hard − V_soft), which is not the derivative ofgapw_hartree_energy. For a partially-occupied core atom the inconsistent Fock pushed the tight 1s above the valence, so the SCF aufbau left the core EMPTY: isolated O collapsed to ~−35 Ha (e_kin ~17) versus the all-electron GDF ~−73.8. The energy expression was already correct at the core-occupied density (O +215 mHa vs GDF); only the Fock was wrong.Fix. Ported the validated analytic exact Fock
J = ∂E_H/∂Dfromexamples/regression/gapw_parity/milestone3_analytic_fock.py: (A) the smooth term on the soft basis (∂ñ/∂D uses the soft AOs), embedded; (B) on-centre hard / (C) on-centre soft radial augmentation; (D) the previously-missing compensator ρ₀ responseΣ_lm M_lm(⟨g_lm|V_smooth⟩−⟨g_lm|V_soft⟩)withM_lm = ∂Q_lm/∂D.½ tr(D·J) = E_Hnow holds to ~1e-13, so the SCF recovers the core: O → −73.557 (+215 mHa), e_kin 73.4, ε₁ₛ ≈ −20; Ne preserved (+339 mHa). The D-independent pieces (the M tensors + the unit-compensator grid/radial collocations) are cached once per builder so the per-iteration cost is only the radial Poisson solves.Scope. RHF/RKS + UHF/UKS (their J routes through
build_J(D_total)). GAPW stays opt-in behindGAPWExperimentalWarning(the bonded-molecule overlapping-augmentation double-count is a separate open item). Regression:tests/test_periodic_gapw_augment.py::TestGapwScf::test_gapw_o_core_occupied.
Fixed: DMRG solver honors the requested spin projection (ms2) (2026-06-26)¶
The toy/exact DMRG backend (
vibeqc.solvers._dmrg.DMRGSolver) previously constrained only the total particle number, so asolvecould return a lower-lying wrong-spin sector (e.g. anms2 = -2state for a requested closed shell). It now diagonalizes the fixed-(N, ms2) sector: the full-Hamiltonian penalty lifts both wrong-N and wrong-ms2states, and the public fixed-sector index selection filters on the α/β occupation implied byms2(even spin-orbitals are α, odd are β per_spatial_to_spinorbital).solvenow readsHamiltonian.ms2; the bare_build_full_hamiltonianpath stays particle-number-only unless_ms2is set, preserving existing behavior.Impossible sector requests are rejected up front (
_validate_sector): a parity mismatch ((N + ms2)odd) or a sector demanding more α/β electrons than spatial orbitals raises a clearValueError.Tests:
tests/test_solvers_fci_parity.py— un-xfailedtest_dmrg_respects_ms2_when_wrong_spin_sector_is_lower, addedtest_dmrg_picks_singlet_over_lower_triplet_sector,test_dmrg_rejects_impossible_parity_request,test_dmrg_rejects_oversized_spin_sector.
Added: real 2021 COSX GridX grids + multi-stage SCF progression (2026-06-26)¶
The opt-in
cosx_grid_level1..4 tiers are now the verbatim 2021 5-region pruned-Lebedev “AngularGrid” grids of Helmich-Paris, de Souza, Neese & Izsák (J. Chem. Phys. 155, 104109, 2021), Table I, replacing the earlier single-order approximation. Each tier splits an atom’s radial shells into 5 index-bands with a distinct Lebedev order per band (sparse core, dense valence, thinned tail). The Table I angular point counts are exact; the radial counts and region boundaries are documented approximations (ORCA’s per-element radius multipliers + differential- evolution radial parameters are ORCA-binary only, not openly published). New Lebedev order-5 (14-pt) and order-7 (26-pt) tables back the inner regions (cpp/src/lebedev_data.cpp). Grids built via a newGridOptions.orca_angular_pointsfield incpp/src/grid.cpp(shell-index 5-region scheme; preserves the lock-free uniform per-atom layout).Multi-stage SCF grid progression (DefGrid stage triples): when a GridX tier is opt-in, the SCF steps through coarse → middle → fine grids, converging each non-final stage loosely (density carried forward) then converging on the fine grid. New
cosx_grid_stages_for_level(level)(the stage grids; last ==cosx_grid_options_for_level(level)), driven by a templatedrun_cosx_staged_scfhelper (cpp/include/vibeqc/cosx_staged.hpp) wired into all four SCF drivers (RHF/UHF/RKS/UKS). SPINLOCK keeps the single-fine-grid path. The legacy single-grid path is unchanged.Reach across all methods. RIJCOSX now also drives ROHF (
ROHFOptions(cosx=True, cosx_grid_level=...), single-grid via the RI-J + chain-of-spheres closures). Every post-HF method on a mean-field reference (MP2 / RI-MP2 / DLPNO-MP2 / CCSD / CCSD(T) / DLPNO-CC / TDDFT) inherits RIJCOSX through its reference SCF (setcosx=Trueon the reference’s options); nocosxfields were added to the MP2/CCSD option structs because COSX is purely an SCF concern. The CAS family stays on the exact/DF reference path (it needs exact MO integrals).Default flipped:
cosx_grid_level0 -> -1 (AUTO) for RHF/UHF/RKS/UKS (+ ROHF). Whencosx=True, the SCF energy now uses a 2021 GridX tier + multi-stage progression by default, and is conv-tolerance-aware: a newcosx_use_gridx(grid_level, conv_tol_grad)gate (cosx.hpp) keeps GridX for normal-tolerance SCFs but auto-falls-back to the legacy grid whenconv_tol_grad < 1e-6(the commutator-noise floor), so tight-SCF and tight open-shell runs are byte-for-byte unchanged.cosx_grid_level=0still forces legacy;1..4force a GridX tier at any tolerance.Gradients:
GradientOptions.cosx_grid_levelstays0(legacy) since it has noconv_tol_grad; the ASE calculator’s force path (_grad_opts_from_scf) now mirrors the SCF’s resolved grid (copiescosx_grid_level/cosx_variant/thresh_cosx, applying the same conv-tol fallback) so geometry optimisations stay energy/gradient grid-consistent.Tests:
tests/test_cosx_variants.py(stage progression, multi-stage convergence, post-HF reference inheritance, ROHF RIJCOSX, default-flip behaviour); existingtests/test_rijcosx.pytight-tol gradient tests pass unchanged (auto fallback to legacy).
Fixed: RIJCOSX opt-in GridX path robustness (2026-06-26)¶
The opt-in COSX GridX path is now robust.
cosx_variant=AUTOresolves to FITTED on any grid (the previous grid-coupled rule auto-selected the no-fit STANDARD build on a GridX tier, which destabilises open-shell RIJCOSX SCFs); STANDARD is now an explicit opt-in only. The auto grid mapping (cosx_grid_level=-1) is now DZ/TZ→GridX2, QZ→GridX3; GridX1 (order-17 / 110 angular pts) is no longer auto-selected because it is too coarse for robust open-shell convergence; it remains the explicit “quick single-point” tier. The default is unchanged (cosx_grid_level=0→ legacy grid → robust); only the opt-in GridX behaviour is fixed.vibeqc.parity.scf_cosx_grid(options, basis_name): returns the COSX grid an SCF with these options actually builds, so an energy decomposition (decompose_energy_*) rebuilds K on the same grid the SCF used and the self-consistency check holds once a GridX tier is selected. Wired intotests/test_parity_hf_dft.py+examples/.../vibeqc_compare.py.Known caveat: the Lebedev GridX2 grid has a higher SCF commutator-noise floor (~1e-5) than the legacy grid, so a tight
conv_tol_gradon an open-shell system needscosx_grid_level=3(GridX3) or the legacy grid. Auto-GridX as the default awaits the globally-optimised 2021 GridX tables (not openly published). Tests:tests/test_cosx_variants.py.
Native D4 dispersion un-gated — production-validated for H–Ne (2026-06-26)¶
compute_d4(..., backend="native") (vibe-qc’s own MPL-2.0 D4, no dftd4
dependency) now meets the dftd4 parity bars and is un-gated for elements
H, He, B, C, N, O, F, Ne: pairwise C6 agree with the dftd4 package to
~5 % (CH₄) / ~8 % (H₂O) and the CH₄-dimer D4 energy to <0.05 kcal/mol. Three
fixes, landed together:
Per-atom Eq.-6 extraction.
generate_reference_datasethad stored each reference host’s whole-molecule α(iω) as the per-atom reference (e.g.H/CH4stored α₀=15.9 a.u. — all of methane — vs atom-in-methane H ≈2.2), so C6 ∝ α² came out 4–32× too large. It now performs the extraction of Eq. 6 of Caldeweyher et al., J. Chem. Phys. 150, 154122 (2019): each host’s α(iω) is partitioned onto the target atom by subtracting every partner atom’s ζ(q)-charge-scaled canonical atomic reference (H from H₂, noble gases from the free atom, B/C/N/O/F from their pure hydride), divided by the target-atom count.Correlated CPKS α(iω) provider. The reference α(iω) is now the coupled CPKS / TD-DFT (adiabatic) response at PBE38 — the level dftd4’s reference data uses — via the new
vibeqc.dispersion_d4_refdata.coupled_polarizability_imag_freq_dft(a dense Casida solve that reuses the validated TDDFTf_xc/ hybrid-kernel builders). This lifts the O/N polarizabilities that the previous TD-HF response under-bound by ~15 %. Validated bit-exactly against the TD-HF path in the HF limit. Re-derivation stays first-principles (CLAUDE.md § 1); dftd4’s LGPL reference C6 table is the validation target only.d4_reference_data.jsonregenerated at PBE38/aug-cc-pVDZ._R4R2table fix. The C8 / BJ-radiusr4r2table (dispersion_d4_model.py) had shipped without thesqrt(Z)factor — 117 of 118 entries wrong (only H, where √Z=1, correct) — which shrank the BJ damping radius R0 and over-bound every native D4 energy regardless of the C6 data (this masked itself while C6 were 4–32× too large). Corrected against the published <r⁴>/<r²> constants (dftd4data_r4r2.f90);test_r4r2_spotsnow pins C/N/O/F/Xe so it cannot silently regress.
The two tests/test_d4_model.py parity tests are now real (no longer
strict-xfail), D4NativeExperimentalWarning is no longer emitted (the class is
retained for back-compat), and the experimental GFN2-xTB model’s post-SCF D4
term is correspondingly quantitative. backend="dftd4" remains the default
for full periodic-table coverage; native raises a clear error outside H–Ne.
Extending the reference catalogue past period 2 is the prerequisite for making
native the default (handover D4b-8).
Changed: periodic BIPOLE SCF uses incremental (differential ΔD) Fock by default (2026-06-26)¶
Default flip (
use_incremental_fockFalse→Trueinrun_pbc_bipole,run_pbc_bipole_rks,run_pbc_bipole_uhf,run_pbc_bipole_uks). Profiling a dense ionic SCF (MgO/STO-3G LDA 2×2×2) showed the per-iteration cost is ~90% the direct periodic 2e ERI Fock (build_fock_2e_real_space, ~106 s/call) and only ~5% the cross-cell XC (~6 s). TheIncrementalJKdifferential builder (already present + tested, commit6e469990) was implemented but left opt-in, so every periodic BIPOLE SCF rebuilt the full ERI Fock each iteration.What it does. Since the 2e Fock is linear in the density,
J/K(Dₙ) = J/K(Dₙ₋₁) + J/K(ΔD); feeding the iter-to-iter increment ΔD to the C++ builder’s Schwarz × density-envelope screening makes each build progressively cheaper as ΔD → 0 (most quartets fall below threshold), with a full rebuild everyreset_every(12) iterations to bound accumulated screening drift. The result is numerically identical to the full-rebuild path (validated bytest_pbc_bipole_multik_ewald_split,test_bipole_fock_ewald_exchange).Scope + fallback. Active only on the eligible path (corrected Ewald gauge
DIIS, not ODA); ineligible runs fall back to the full build automatically. Pass
use_incremental_fock=Falseto force the legacy full-rebuild path.
[v0.15.1] — 2026-06-26 — Neese’s Cheetah¶
Docs patch. Fixes the public landing page, whose static homepage feature admonitions still advertised v0.14.0 after the v0.15.0 cut. No functional, numerical, or API changes.
Fixed¶
Homepage admonitions (
docs/index.md): now “New in v0.15.0 (Neese’s Cheetah)” with the v0.15.0 headline, v0.14.0 demoted to “Also shipped”, and “Coming in v0.16.0: periodic correlation”. The dynamic “Current release” banner already tracked the version; only the static feature blocks were stale. vibe-qc.com renders the release branch, so this shipped as a patch to reach the live site.
[v0.15.0] — 2026-06-26 — Neese’s Cheetah¶
Fixed: periodic XC now uses the full cross-cell density — dense-crystal energy + basis-opt gradient (2026-06-26)¶
Energy (
build_xc_periodic/build_xc_periodic_uks,cpp/src/periodic_xc.cpp). The shipped builder anchored the bra AO in the home cell when assembling the DFT-grid density (ρ = Σ_s χ_0 P(s) χ_s), which is not lattice-periodic and is pointwise wrong by O(1) in the bonding region of dense crystals — yet still integrates to N electrons, so ∫ρ dr cannot detect it. This was the ~400 mHa dense-ionic error in the v0.14.0 known issues. The fix assembles the physical periodic density ρ = Σ_a Σ_s χ_a P(s−a) χ_s — both AOs summed over lattice cells, bra-image cells screened by the Becke partition reach (becke_image_radius_bohr), ket cells bycutoff_bohr— for ρ/∇ρ/τ and the matching V_xc Fock. On the sealed CRYSTAL23 ladder MgO/STO-3G LDA goes +427.9 → −3.9 mHa and PBE non-convergent → convergent; pinned bytests/test_periodic_xc_cross_cell.py(C++ vs an independent NumPy oracle + lattice periodicity). A separate ~3 mHa BIPOLE Ewald-J / exxdiv pure-DFT gauge residual remains (tracked).Basis-opt gradient (
build_periodic_xc_gradient_grid/_uks,python/vibeqc/basis_optimization/periodic_energy_gradient.py). The periodic explicit-XC basis-parameter gradient assembled the same home-bra density, so after the energy fix it differentiated a superseded E_xc (off by ~0.09 Ha RKS / ~0.25 Ha UKS on the dense 1-D test chains). It now assembles the full cross-cell density and the analytic kernel carries the previously-missing bra-image derivative term (the varied shell appears in both χ_a and χ_s), soperiodic_xc_param_gradient_term(_uks) is again the exact derivative of the E_xc the SCF minimises. Convention tests validate the builder againstbuild_xc_periodicto ~1e-11; analytic vs central-FD gradients agree to ~1e-9. (Phase-P3 basis-opt machinery; the full periodic analytic gradient remains gated behind the R13 kernel.)Patch-candidate: v0.14.x
Fixed: vq build-env no longer wedges hosts on dev-HEAD auto-update (2026-06-26)¶
The v0.6.55 dev-HEAD-tracking auto-update timer was wedging fleet hosts:
vq build-env vibeqc-dev rebuilds ran for hours with empty stdout while
pinning CPUs (saturn job ran 12h+ holding 6 CPUs), duplicates piled up
behind them, and a half-landed rebuild left an importable-but-ABI-broken
env (maru: newer Python tree against an un-rebuilt _vibeqc_core.so →
ImportError). Three fixes, all in vibe-queue/src/vq/:
No silent wedge — the update_script now runs under a supervised process group (
admin._run_monitored_build): a no-output stall cap (VQ_BUILD_STALL_TIMEOUT, default 900 s) and the wall cap both SIGTERM→SIGKILL the whole build tree (ninja/cc1plus grandchildren, not just thebashwrapper, which previously leaked and kept burning cores), and a periodic heartbeat (VQ_BUILD_HEARTBEAT_INTERVAL, default 120 s) logs progress so a stuck build is never silent. Build jobs also carry awall_time_secondswatchdog backstop.No duplicate / retry storm — the dev-HEAD (branch-mode) auto-update now submits a deduped, backed-off, wall-time-capped
vq build-envJOB (build_job.submit_build_env_job) on the local daemon instead of an uncapped inline rebuild in the timer process: a second build for an env already building is skipped, and a failed build backs the env off (15 min, doubling per consecutive failure, capped 6 h) before the next attempt.No ABI skew —
admin._do_update_workmakes the{Python tree, native .so}pair atomic: it snapshots HEAD + the package’s.sobefore the build and, on a failed build or a failed post-buildimportprobe, git-resets the checkout and restores the.so, so the live env is only ever a consistent pair (never newer-Python-against-older-.so). Armed per env via the newimport_checkfield on[programs.<venv>](import_check = "vibeqc"for vibeqc-dev/release); unset = disabled.
Regression tests in tests/test_build_env_hardening_v0_12_x.py cover the
process-group reap, the dedup + backoff, and the atomic rollback.
Changed: Γ-CCM scalable driver is now integral-direct (Phase 3b, 2026-06-25)¶
run_ccm_rhf_scalable(vibeqc.periodic.ccm.scf) now contracts the WSSC-weighted four-center integrals straight into J/K rather than building the denseO(nbf**4)effective ERI tensor. Peak JK-build memory drops from(n_threads+2)·nbf**4·8B ton_threads·2·nbf**2·8B — e.g. ~300 GiB → ~10 MB on a pob-tzvp-rev2 c-diamond (2,2,2) cell (nbf=288) — so real 3-D production-basis cells fit in RAM. New C++ kernelsaiccm2026dev-a-directandbra_home_full-directincpp/src/periodic_fock.cpp.New
four_center=keyword onrun_ccm_rhf_scalable:"direct"(default — the integral-direct path above) or"full"/"dense"(the preserved dense effective-tensor path, kept as the small-cluster comparison reference). The two agree to ~1e-12 (a summation reorder); existing-aenergies/properties are unchanged within that tolerance. The dense Pythonrun_ccm_rhfand the full C++ branches (aiccm2026dev-a,bra_home_full) are untouched.KS-CCM (
run_ccm_rks/run_ccm_uks,vibeqc.periodic.ccm.dft) gains the samefour_center="direct"(default)/"full"keyword and now uses the integral-direct kernel by default, so RKS/UKS-CCM scale to real 3-D cells too (previously built the fullO(nbf**4)tensor). The(method, four_center)→ kernel mapping is centralised inscf._ccm_scalable_cxx_method.Opt-in Cauchy-Schwarz screening on the integral-direct kernels via a new
schwarz_thresholdkeyword onrun_ccm_rhf_scalable/run_ccm_rks/run_ccm_uks(default0.0= off = exact). A positive value skips shell-quartets below the rigorous|w|·Q_bra·Q_ket·D_maxbound — a throughput lever (~1.1× on c-diamond (2,2,2)/STO-3G at1e-12, more for diffuse bases; the WSSC weight already screens far cells, so the gain is in near small-overlap quartets). Off by default preserves the byte-for-byte integral-direct guarantee.
Added: COOP/COHP bonding analysis for periodic systems (2026-06-25)¶
New module
vibeqc.coop_cohp:compute_coop_cohp()computes energy-resolved COOP and COHP (Crystal Orbital Overlap/Hamilton Population) over a Monkhorst-Pack k-mesh with Gaussian broadening. ReturnsCOOPCOHPResultcarrying per-pair COOP(E), -COHP(E) (Lobster convention: bonding positive), ICOOP/-ICOHP, and pair metadata. AcceptsF_termsandH_termsas sequences ofLatticeMatrixSet;F_terms_betatriggers unrestricted α/β analysis.C++ performance kernels (
coop_weights_k,cohp_weights_k,gaussian_broaden_projected) incpp/src/coop_cohp.cppwith pybind11 bindings (GIL release). Replaces Python O(n_pairs·n_k·n_bands) triple loops with single Eigen operations per k-point.QVF serialization: new section kinds
dos.coopanddos.cohpwith binary membersenergies,projections,integratedand JSONmetacarrying pair list + Fermi energy + broadening. v1 and v2 JSON Schema updated.Runner integration:
run_periodic_job(coop_cohp=True)computes COOP/COHP on the DOS k-mesh and embeds both sections in the QVF archive.dos_kmeshparameter controls the mesh (default[8,8,8]).Plotters:
vibeqc.plot.coop_figure(result)andcohp_figure(result)with per-pair colored curves, ICOOP/-ICOHP in legend, Fermi line, and bonding/antibonding zero reference.CLI:
vibeqc coop <file.qvf>prints the pair table with ICOOP/-ICOHP values from existingdos.coop/dos.cohpsections.Citation database: four entries (Hughbanks-Hoffmann 1983, Dronskowski-Blöchl 1993, Deringer-Tchougréeff-Dronskowski 2011, Maintz et al. 2016) routed under
[routes.properties].coop/.cohp.
Added: periodic Mayer bond orders (2026-06-25)¶
New
periodic_mayer_bond_orders()invibeqc.coop_cohp: k-space generalization of Mayer bond orders (Σ_k w_k |P(k)·S(k)|² over a k-mesh). Supports restricted and unrestricted. Wired into the runner’s QVF output block, overriding the Γ-point-only approximation whenoutput_qvf=True. Companion to COOP/COHP: ICOOP/-ICOHP gives bond strength integrated over energy; Mayer gives a single per-pair number.
Added: experimental ab-initio Cyclic Cluster Model — Γ-CCM and χ-CCM (2026-06-25)¶
Γ-CCM /
aiccm2026dev-a(vibeqc.periodic.ccm): the direct-torus Γ-supercell CCM. Ships Hartree-Fock (Python + scalable C++), Kohn-Sham DFT (any libxc functional, pure and hybrid), MP2, UMP2, CCSD(T), UCCSD(T), DLPNO-MP2, and DLPNO-CCSD(T) on the neutral (Ewald) reference. Four-center Coulomb is exactly 8-fold permutationally symmetric for any lattice, with a four-center → RI (GDF) → RIJCOSX approximation hierarchy. Properties: HOMO/LUMO gap, Mulliken and Löwdin charges with translational-spread diagnostic, finite-cluster dipole, numerical gradient (Σ forces = 0). Wannier/Pipek–Mezey localization, spglib space-group symmetry analysis, and DLPNO PAO construction. Cluster sizing by real-space interaction radius (the dual of k-point density, Δk = π/R_c).χ-CCM /
aiccm2026dev-b(vibeqc.periodic_aiccm2026dev_b): the finite-character (Γ-centred character-mesh) CCM. Ships restricted and unrestricted RHF/RKS/UHF/UKS via four-center (3-D), RI, and RIJCOSX backends in 1D/2D/3D, plus canonical RI-MP2, DLPNO-MP2, DLPNO-CCSD, and DLPNO-CCSD(T) in 3D with explicit finite-torus convention recording (coulomb_kernel="3d-periodic-g0",exchange_q0="bvk-ewald"). Occupied localization (PBC-safe Pipek–Mezey, Wannier, IAO), spglib space-group diagnostics, Mayer bond orders, and band structure on the finite character mesh.χ-CCM ECP bookkeeping: SCF diagnostics now record the physical electron count,
ecp_total_ncore, effective electron count, per-atom effective nuclear charges, and the effective net charge. χ-CCM SCF fails before any Fock build if those quantities do not define a neutral finite torus. One-particle properties and band fillings use the effective counts; post-HF ECP references fail closed until their frozen-core/correlation convention is validated.χ-CCM Mayer performance: the finite-torus Mayer bond-order analysis now contracts density and overlap residue blocks in a native OpenMP kernel. It avoids materialising full AO supercell density, overlap, or atom-pair matrices while matching the previous dense-supercell formula for restricted and unrestricted references.
χ-CCM post-HF performance: the finite-character momentum-conservation table used by RI-MP2 and local-correlation setup now builds in native OpenMP code and keeps the same fail-closed checks for duplicate or non-closed character meshes.
χ-CCM localization performance: occupied-localization overlap and Fock setup now reuses native finite-character transforms for overlap, Fock, and canonical Wannier coefficients, with the old einsum routes kept as regression oracles and an explicit imaginary-residue guard.
χ-CCM runner reproducibility:
run_periodic_job(..., jk_method="aiccm2026dev-b")now forwardsrsgdf_ke_cutoffto the B RI and RIJCOSX routes, matching the direct APIs. RI logs printgdf_method,rsgdf_ke_cutoff, andmdf_ke_cutofffor queued finite-N runs.χ-CCM canonical MP2 memory: the RI-MP2 route now streams each pair-resolved AO-space
Lpq(k_i,k_a)through the native occupied-virtual transform and releases it immediately, rather than holding the full AO-space pair cache alongside the MO-spaceLovfactors. Local/DLPNO routes keep the full cache because their real-torus transform needs all pairs. The remaining RI temporaries are now cleared immediately after their final contraction or real-torus transform.χ-CCM local-correlation setup: finite-translation occupied-pair orbits now partition in a native χ-CCM kernel instead of Python set bookkeeping, with the Python closure retained as a regression oracle. The finite-translation occupied-index permutation table feeding that orbit partition now builds in the same private native kernel layer. The finite-character inverse Bloch transform used for density and property residue blocks now also runs in native OpenMP code. The complete-domain MP2 audit used by local-PNO exact-limit corrections now streams the real-torus
L[P,i,a]contraction in native OpenMP code instead of materialising the fullijabtensor.Documentation: Γ-CCM reference, χ-CCM user guide, Γ-CCM tutorial, χ-CCM tutorial, open-shell guide, cross-stream comparison, basis-set guide, visualization, troubleshooting, quickstart.
Benchmark: 28-system test set spanning all seven crystal systems and cubic centerings, with a single-command runner, fleet batch generation, and CRYSTAL23 comparison harness (
aiccm-2026/,qc-input-library/aiccm2026testset/).Both lines emit
AICCM2026DevBExperimentalWarning(χ-CCM) or are gated behindmethod="aiccm2026dev-a"(Γ-CCM). The two lines are distinct approaches intended for a construction-level comparison. The current reportable route map is empty, and equality is not implied by their names.
Added: RIJCOSX variant + grid-level + threshold controls (2026-06-25)¶
RIJCOSX now exposes a user-facing accuracy/cost surface on every SCF Options struct (
RHFOptions,UHFOptions,RKSOptions,UKSOptions) and onGradientOptions, consulted only whencosx=True:cosx_variant:CosxVariant.AUTO(default) /STANDARD/FITTED. STANDARD is Neese 2009 seminumerical K with no overlap fit; FITTED is the overlap-fitted build (global Q-junction, Neese 2009 §2.4, the precursor to the per-grid-point fit of Izsák-Neese 2011).AUTOis coupled to the grid: on the legacy grid (cosx_grid_level=0, default) it resolves to FITTED (that grid needs the fit); on a GridX grid it follows the Helmich-Paris 2021 rule (FITTED whenthresh_cosx < 1e-6or QZ-and-above, else STANDARD). Both apply the one-centre correction. Measured: standard COSX is ~1.5e-3 Ha/bohr (DZ gradient) on the legacy grid but ~4e-5 on a GridX tier, hence the coupling. The standard (no-fit) K path is new wiring; the prior default always applied the global Q fit. Default behaviour is unchanged (AUTO + legacy grid gives FITTED + legacy grid, exactly as before); STANDARD + GridX is the recommended fast opt-in.cosx_grid_level:0(default, keeps the validated legacycosx_grid),1to4(GridX1-4 COSX tiers),-1(auto by basis). The GridX tiers are vibe-qc’s own pruned-Lebedev COSX grids following the Helmich-Paris et al. 2021 methodology (Krack-Köster radial + COSX accuracy tiers), not a verbatim copy of ORCA’s GridX tables.thresh_cosx: COSX accuracy target drivingAUTOvariant selection.
Auto-selection is a single canonical C++ implementation (
resolve_cosx_variant,resolve_cosx_grid_level,cosx_basis_cardinality_from_name,cosx_grid_options_for_level), exposed to Python for unit testing. The variant routes through both the SCF energy (COSXJKBuilder) and the analytic gradient.Citations: Izsák-Neese 2011 (overlap-fitted COSX) + Helmich-Paris et al. 2021 (improved grids/contraction) added to the database and routed under
acceleration=["cosx"/"rijcosx"]. Tests:tests/test_cosx_variants.py(auto-selection unit tests + variant/grid-level energy paths).Default behaviour is preserved:
cosx_grid_leveldefaults to0(legacy grid) andAUTOon the legacy grid is FITTED, i.e. the default RIJCOSX path is byte-for-byte the prior FITTED + legacy-grid build. The faster STANDARD + GridX combination is opt-in; flipping the default grid to auto-GridX is deferred to a dedicated parity-validation pass.
Added: geometry-optimizer + delocalized-internal-coordinate citations (2026-06-24)¶
The keyword-selectable geometry optimizers (
run_job(geom_opt=...)) and coordinate systems (geom_coords=...) now carry their defining-paper citations: RFO (Banerjee 1985), eigenvector-following / P-RFO (Baker 1986), GDIIS (Császár-Pulay 1984), FIRE (Bitzek 2006), trust-region (Moré-Sorensen 1983), and the delocalized / redundant internal coordinates (Baker-Kessi- Delley 1996 + Peng-Ayala-Schlegel-Frisch 1996). A geom-opt run previously cited only the generic ASE/BFGS + Pulay-forces papers. New[routes.optimizers]/[routes.coordinates]tables fire via thegeom_optimizer/geom_coordsselectors onCitationDatabase.assemble; all entries areprint=true(they reach the.outreferences block +.bibtex).The ASE/BFGS citation now fires only when the ASE backend actually drove the relaxation; the native / brent / geomopt optimizers no longer spuriously cite BFGS. Tests:
tests/test_citations.py(optimizer/coordinate route coverage).
Fixed: spin-pure selected-CI CASSCF convergence failure (P1, 2026-06-26)¶
P1 resolved (HANDOVER_OPEN_BUGS_V015.md): spin-pure SA2 selected-CI CASSCF failed to converge for CAS(4,4). Root cause: the selected-CI eigensolver found different excited-state eigenvectors than the dense solver even when all determinants were selected, causing SA-RDM discrepancies (RDM1 diff 0.25, RDM2 diff 0.75) that broke the CASSCF line search at iteration 3.
Fix: _spin_pure_selected_roots delegates to the dense _spin_pure_roots solver when target_size >= n_det_full and pt2_threshold <= 1e-14, guaranteeing identical eigenvectors at the full-selection limit.
Tests: test_casscf_full_limit_matches_dense_spin_pure and test_cpp_spin_pure_casscf_full_limit now pass. 113 total green.
Changed: unified CASSCF/CASPT2/NEVPT2 gradient surface across the three geometry optimizers (2026-06-24)¶
optimize_molecule_brent(optimizer_backend="brent") and the geomoptMolecularSCFProvider(run_job(geom_opt=...)) previously listed CASSCF (and, for brent, CASPT2/NEVPT2) in their analytic-gradient sets, so they relaxed on the analytic gradient while the L-BFGS-B primaryoptimize_moleculeused full-energy central FD. All three optimizers now share one decision (_casscf_analytic_gradient_ok) so they always walk the same surface per method:CASSCF uses its validated analytic gradient (v0.15.0 P0 fix, FD-tight to ~1e-7) inside the state-specific, closed-shell, default-
compute_wzenvelope – a large speed-up over FD (one gradient build per step instead of6N+1energy evaluations). State-averaged CASSCF (nroots > 1, whose analytic gradient is only finiteness/translational-invariance checked), open-shell CASSCF, and the experimentalcompute_wz=Truecorrection fall back to full-energy FD.CASPT2/NEVPT2 always use full-energy FD: their analytic Z-vector orbital-relaxation correction is experimental.
Also declares the CASPT2/NEVPT2 option kwargs the
_run_molecular_scfdispatch branch referenced but never accepted (latentNameError). Tests:tests/test_molecular_optimize.py::TestCasscfAnalyticGradientGating.
Added: CASPT2/NEVPT2/MS-CASPT2 Z-vector gradients (99.94%/99.59%, 2026-06-27)¶
CASPT2 Z-vector (
use_zvector=True, default whencompute_corr_grad=True): CI-eliminated CP-MCSCF gradient correction via analytic CI Lagrangian, numerical dE^2/dk (joblib-parallel), and z^T·g^R shortcut with corrected effective density. Validated at 99.94% accuracy vs full-energy FD (H2/6-31G CAS(2,2), delta=3.26e-6 Ha/bohr, 520x improvement over frozen FD). The Z-vector orbital-relaxation correction is EXACT (ratio 1.000000 vs target).NEVPT2 Z-vector: same architecture with Dyall H0 effective densities. Validated at 99.59% accuracy (delta=2.42e-5, 64x over frozen FD).
MS-CASPT2 frozen-PT2 gradient: fixed critical overlap-correction bug (was using bare density instead of generalized Fock, 5800% error). Diagonal terms now delegate to compute_casscf_gradient; validated vs FD.
w_g factor fix: corrected effective density weight from N_g^2/Delta^2 to N_g/Delta^2 (dE_g/dE0 = N_g/Delta^2).
C++ kernels: casscf_orbital_gradient (OpenMP), transform_4index_mo (OpenMP 4-index AO->MO), joblib-parallel dE^2/dk FD loop.
Analytic CI Lagrangian for both CASPT2 and NEVPT2 (transition RDMs + effective densities).
Tests: 371 total green (gradient + MRPT parity + MS-CASPT2 + SA-CASSCF + selected-CI + FCI).
Fixed: AICCM aiccm2026dev-b lower-dimensional four-center guard and RKS XC folding (2026-06-24)¶
aiccm2026dev-bnow fails closed foraiccm_backend="four_center"on 1D chains and 2D slabs. The current direct-truncated BIPOLE fallback lacks the neutral wire/slab Green function needed for the B finite-torus Hamiltonian, and benchmark chains showed Madelung-scale constant over-binding. The 3D four-center Ewald route remains available.Multi-k GDF/RKS now builds XC from the full inverse-Bloch real-space density blocks and Bloch-folds the resulting
V_xc(R)to every k point. The old cached-GDF branch collapsed the KS density to one Gamma AO block and reused the same XC matrix at all k points, which is exact only in the molecular/vacuum limit and is a plausible Hamiltonian-level source of the c-diamond/si-diamond PBE-RI SCF oscillation. Re-run those queue cases before changing default SCF accelerators.Added regression coverage for lower-dimensional four-center fail-closed behavior and for preserving off-home real-space density blocks in the multi-k RKS XC build.
Fixed: GAPW experimental warning no longer silenced on the jk_method="gapw" runner path (2026-06-24)¶
run_periodic_job(jk_method="gapw")now emitsGAPWExperimentalWarningonce per run, matching the other experimental routes (native-D4, GFN2). The four internal GAPW SCF runners were invoked withquiet=True, which suppressed the warning entirely on the normal user-facing path — so a user driving GAPW through the runner saw only the.out“(experimental)” label and the docs admonition, not the Python warning the docs already promise (docs/user_guide/gapw.mdstates the path is “guarded byGAPWExperimentalWarning”). The warning now fires at the GAPW dispatch site; the internalquiet=Truecalls are retained so it fires exactly once instead of cascading per augmentation builder. The.outlabel and docs admonition are unchanged. The underlying GAPW correctness bugs remained tracked/gated in the v0.14 bug inventory at the time — this is a gating-visibility fix, not a correctness fix. Test:tests/test_periodic_gapw_augment.py::TestGapwRunnerExperimentalWarning.
Changed: AICCM aiccm2026dev-b streams canonical RI-MP2 contractions in C++ (2026-06-24)¶
run_aiccm2026dev_b_mp2now contracts the momentum-conserving canonical RI-MP2 energy through an OpenMP C++ kernel. The equations and normalization are unchanged, but the old Python loop no longer materializes one fulloovvscratch tensor per virtual k block for each occupied k pair.Added a deterministic complex LOV parity test against the previous NumPy expression, plus the existing B RI-MP2 wrapper smoke test.
Fixed: RSGDF cderi cache no longer explodes on 1D/2D vacuum-padded AICCM inputs (2026-06-24)¶
rsgdf_g_meshandrsgdf_dense_g_meshnow respectPeriodicSystem.dim: inactive vacuum-padding reciprocal axes are frozen at zero for 1D chains and 2D slabs, while 3D crystals retain the full reciprocal sphere.This fixes the apparent hang at
Building per-pair Lpq cachefor the saturn h-chain/lih-chaina-gdf,b-rhf-ri, andb-rhf-rijcosxroutes. Local full-runner B checks now complete forh-chain rhf-riandh-chain rhf-rijcosx; the separate F1 four-center 1D over-binding remains open and is tracked in the B decisions log.
Fixed: periodic reference routes on low-dimensional / 3-D cells (stream-A benchmark blockers, 2026-06-24)¶
jk_method="bipole"fails fast at the API level on 1-D/2-D. The bipolar- expansion Fock build needs a 3-D Ewald/Madelung lattice sum;run_periodic_jobnow raises a clearNotImplementedError(naming the alternatives) before any SCF setup, instead of a terseValueErrorraised deep in the SCF.AICCM four-center HF reaches genuine 3-D supercells. The benchmark
aiccm-hfroute (aiccm-2026/run_case.py) now drives closed-shell HF through the scalable C++ lattice-sum builderrun_ccm_rhf_scalable(WSSC weights applied in the shell-quartet loop, no O(n_pad⁴) padded ERI), which matches the denserun_ccm_rhfto µHa and no longerstd::bad_allocs on c-diamond 2×2×2. The dense-builder memory guard’s error now also namesrun_ccm_rhf_scalable. Tests:tests/test_periodic_lowd_guards.py.(The companion GDF rsgdf low-D “hang” — a vacuum-padded 1-D/2-D cell exploding the dense FFT G-mesh — is fixed separately by the dimension-aware
rsgdf_dense_g_mesh, which meshes only the periodic directions.) Deeper low-D multi-k GDF / bipole support (Coulomb truncation / a low-dimensional Ewald sum) is flagged for the periodic-SCF chat.
Changed: AICCM aiccm2026dev-a — actionable memory guard on the direct four-center (2026-06-24)¶
ccm_eri/ccm_eri_symmetricnow raise an actionableMemoryErrorinstead of OOMing on too-large clusters. The direct padded-cluster four-center is an O(n_pad⁴), screening-free validation builder (compute_eri(pad.basis)is dense over the padded basis, son_pad = n_ref_ao × ERI-image-cellsexplodes for 3-D cells — e.g. c-diamond 2×2×2: ~80 reference AOs → thousands of padded AOs → a TB-scale tensor). A pre-flight guard estimates the dense tensor size and, above a 16 GiB ceiling (VIBEQC_CCM_PADDED_ERI_MAX_GB), raises a clear error naming the AO count, the memory, and the production RI/GDF routerun_ccm_rhf_gdf(the multi-k GDF via CCM ≡ SCM-Γ, which scales to genuine 3-D). Reported by the AICCM benchmark chat. Tests:tests/test_ccm_padded_guard.py.
Added: AICCM aiccm2026dev-a — open-shell U-DLPNO on the cyclic cluster (2026-06-24)¶
Open-shell DLPNO-UMP2 + DLPNO-UCCSD(T) (experimental, the
aiccm2026dev-aline).ccm_dlpno_ump2(ccm, uhf_result=, *, cderi=, tcut_pno=, n_frozen=, ss_scale=, os_scale=)→CCMDLPNOUMP2Result(vibeqc.periodic.ccm.dlpno_ump2) andccm_dlpno_uccsd(ccm, uhf_result=, *, cderi=, tcut_pno=, n_frozen=, compute_triples=)→CCMDLPNOUCCSDResult(vibeqc.periodic.ccm.dlpno_uccsd). Local correlation on the neutral-reference UHF-CCM wavefunction: UMP2 resolved into the αα/ββ/αβ spin channels with per-pair PNOs (reusing the molecularvibeqc.dlpno.ump2PNO machinery with the CCM neutral B-tensors); UCCSD(T) via per-spin union PNO virtual spaces (S^CCM-orthonormal, semicanonical) fed to the spin-orbitalrun_ref_uccsdengine. No-truncation gate (tcut_pno=0): reproduces canonical CCM UMP2 (run_ccm_ump2) and UCCSD(T) (run_ccm_uccsd) on the same neutral four-center to machine ε — closed-shell and genuine open-shell (doublet). Tests:tests/test_ccm_dlpno_ump2.py(7) +tests/test_ccm_dlpno_uccsd.py(7). Derivation:docs/aiccm2026dev_a_followon.md§ D.3. Per-spin PAO Mulliken domains are the M2 follow-on (the molecular open-shell DLPNO is itself at this canonical-virtual PNO rung).
Fixed: v0.14.1 post-release bug-hunt hardening (2026-06-24)¶
Fixed the native CCM CCSD(T) triples path. The pybind wrapper now accepts and size-checks the flattened NumPy buffers that the CCM driver passes, and the native contraction now uses the correct occupied/virtual offsets plus the same nested
abc/ijkantisymmetrisation as the validated Python fallback. This restores the no-truncation DLPNO-CCSD(T)-CCM invariant.Canonicalized the Hermitian two-RDM pair-exchange symmetry in the no-inactive CASPT2 raw-block builder before forming doubly-external overlaps. This removes
~1e-11RDM roundoff from exact half-integer overlaps without relaxing the oracle tests.
Added: QVF v1.2 — four new section kinds + runner integration (2026-06-24)¶
Localized orbitals (
wavefunction.gtowithorbital_kind="localized").write_qvfnow acceptswf_localized_dataand emits a secondwavefunction.gtosection with id"wf_localized"beside the canonical"wf"section. The QVF format already supportedorbital_kind="localized"in the design doc; this closes the wiring gap.Bond-order tables (
bond_orderscanonical kind). New section carries a Mayer/Wiberg bond-order table as JSON:{method, pairs: [{i, j, order, distance_ang, symbol_i, symbol_j}, ...]}. Specified in design doc § 4.15.Fat bands (band-character projections).
vibeqc.bandsgainsband_structure_projected()which computes Mulliken-projected band weights onto (atom, l) channels. ThebandsQVF section now carries an optionalprojectionsbinary member of shape[n_k, n_bands, n_channels]and achannelslist in the kpath JSON. Design doc § 4.3 extended.QTAIM topology writer + compute module (
topology.qtaim). Promoted from reserved to implemented. The compute modulevibeqc.qtaim.qtaim_analysis()now uses the analytic C++evaluate_ao_with_hessianbinding (Route A) with local-minimum |∇ρ| seed detection (scipyminimum_filter, the standard MultiWFN approach), Newton-Raphson refinement with on-the-fly evaluation, and gradient-path bond-path tracing. Opt-in viarun_job(qtaim=True). The QVF writer serializes critical points + bond paths as JSON. Tests:tests/test_qtaim.py(4). Design doc § 4.16.NTO writer dispatch (
wavefunction.gtohole/electron pairs). Multi-state vianto_datalist of{hole, electron, state_index, excitation_energy_ev}records →wf_nto_S{n}_hole/wf_nto_S{n}_electron, plus standalone single-state keyswf_nto_hole_data/wf_nto_electron_data→wf_nto_hole/wf_nto_electron(following thewf_localized_datapattern). Compute side unblocked (runner + C++ ERI, 2026-06-25); see “TD-DFT + NTO” below. Tests:tests/test_qvf_writer.py::TestNTOSections.
Changed: runner integration — QVF auto-population (2026-06-24)¶
run_job(output_qvf=True)now automatically emits:bond_orderssection (from Mayer bond orders in the population summary)dipole_momentmanifest root metadata (from the SCF dipole moment)thermochemistrymanifest root metadata (ZPVE, H, S, G) whenhessian=True
run_periodic_job(output_qvf=True)gains the samebond_ordersanddipole_momentauto-emission.Showcase script
examples/vibe_view/showcase_qvf_all_sections.pyupdated to cover 35 of 37 canonical section kinds with synthetic data for all new sections.
Added: TD-DFT + NTO runner integration (2026-06-25)¶
run_job(tddft=True)runs TDA/TD-DFT on the converged SCF result (RHF/RKS/UHF/UKS) and emits the lowesttddft_n_states(default 5) vertical excitation energies, oscillator strengths, and dominant amplitudes into the QVF archive.run_job(nto=True)(impliestddft=True) computes Natural Transition Orbitals via SVD on the excitation amplitude matrix and embeds paired hole/particlewavefunction.gtosections (wf_nto_S{n}_hole/wf_nto_S{n}_electron) withorbital_kind="natural"into the QVF archive. Restricted only; UHF NTOs deferred.C++ performance kernel
eri_ao_to_mo_blocksincpp/src/bindings.cpp: replaces the 4-step Pythoneinsumhalf-transform with direct-indexed C++ loops. Step 1 (mu → i) reads the row-major numpy ERI directly; steps 2–4 use loop-based contractions on dense intermediates. Validated to machine precision (max diff 1.1e-16 on H2/STO-3G).Citation routing:
uses_tddft=True+tddft_variant="tda"fires Runge-Gross 1984, Casida 1995, and Hirata-Head-Gordon 1999 driver citations;properties=["nto"]fires Martin 2003.
Added: AICCM2026DEV-B SCF residual diagnostics in fleet records (2026-06-24)¶
aiccm2026dev-bSCF results now attach the final SCF energy step, commutator norm, DIIS subspace, accelerator family, damping, level-shift, Fock-mixing, and smearing settings toresult.aiccm2026dev_b.The B-stream fleet runner now writes those fields plus an eight-iteration SCF trace tail into each JSON record. This is intended to diagnose the reported c-diamond and si-diamond RKS/PBE/RI iteration-cap failures without treating damping or level shifting as a physics fix.
The JSON also records that static density damping in the current periodic GDF loop is bypassed once DIIS starts, so damping-only sweeps should either disable DIIS or delay
--diis-start.Added
make_jobs_b.py --profile investigate, a reproducible c-diamond / si-diamondrks-pbe-riSCF-settings matrix that writes the new residual diagnostics and tags submitted jobs withinvestigate.
Fixed: AICCM2026DEV-B low-dimensional four-center guard (2026-06-24)¶
Added a regression for the vacuum-separated 1D H2/STO-3G neutral molecular limit through
aiccm2026dev-bfour-center RHF at multiple cyclic extensions. This pins the cancellation that prevents stale B snapshots from reintroducing a Coulomb self-image over-binding while retaining the experimental lower-dimensional gauge label.Fixed the native DLPNO-MP2 iteration build/import path on the current macOS/Python 3.14 toolchain: the pybind wrapper now names the C++ result and kernel in the
vibeqcnamespace, the kernel materializes transposed amplitude blocks before Eigen contractions, and the native pair iteration preserves ordered PNO overlap links plus the closed-shell pair-energy convention used by the Python reference.
Added: COOP/COHP bonding analysis for periodic systems (2026-06-24)¶
COOP (Crystal Orbital Overlap Population) and COHP (Crystal Orbital Hamiltonian Population) bonding analysis (
vibeqc.coop_cohp).compute_coop_cohp()projects the overlap or Hamiltonian density onto atom-pair subspaces at each k-point with Gaussian broadening, returning aCOOPCOHPResultwith energy-resolved COOP, -COHP (Lobster convention), and integrated ICOOP/ICOHP up to E_F.C++ kernels for per-k COOP/COHP weight accumulation (
cpp/src/coop_cohp.cpp,cpp/include/vibeqc/coop_cohp.hpp).Spin-polarized support via
F_terms_beta: bothcoopandcohparrays carry a leading spin dimension(2, n_pairs, n_e)for UHF/UKS references.Plotting:
vibeqc.plot.coop_figure()andvibeqc.plot.cohp_figure()produce Matplotlib figures with Fermi-energy annotations.vibeqc coop <qvf>CLI prints formatted COOP/COHP/ICOOP/ICOHP pair tables from a QVF archive.QVF writer for
dos.coopanddos.cohpcanonical section kinds; both validate against the manifest schema (spin-polarized and spin-restricted).Citation database: routes
coop(Hughbanks & Hoffmann 1983, Dronskowski & Blöchl 1993, Deringer et al. 2011, Maintz et al. 2016) andcohp(Dronskowski & Blöchl 1993, Deringer et al. 2011, Maintz et al. 2016) indatabase.toml.Tests:
tests/test_coop_cohp.py— bonding/antibonding sign conventions, spin-polarized shapes, QVF round-trip + schema validation, plot smoke tests, CLI smoke test.
Added: AICCM bond and band analysis helpers (2026-06-23)¶
Added primitive-cell Mayer bond-order analysis for both independent AICCM streams:
vibeqc.periodic.ccm.ccm_mayer_bond_ordersforaiccm2026dev-aandvibeqc.aiccm2026dev_b_mayer_bond_ordersforaiccm2026dev-b.Added finite-torus Fock-block band-structure helpers for the same streams:
ccm_band_structureandaiccm2026dev_b_band_structure.Added the full diamond comparison input
examples/periodic/aiccm2026dev_diamond_bonds_bands_compare.py, which runs matched HF/KS AICCM-A and AICCM-B calculations, emits JSON summaries, QVF band/bond archives, and localized-orbital QVF archives.Documented the input under
docs/experimental/aiccm2026dev_diamond_compare.md.
Performance: native OpenMP kernels for AICCM2026DEV-B post-HF setup (2026-06-23)¶
Added B-stream-only C++/pybind kernels for the finite-character inverse transforms used by
aiccm2026dev-bMP2, DLPNO-MP2, CCSD, CCSD(T), UMP2, and UCCSD(T).The pair-resolved RI-factor transform now streams directly into the final real finite-torus tensor
(Ncell*Naux, Ncell*nbf, Ncell*nbf)and symmetrizes it in place. The former Python route materialized a temporary 6-D complex tensor before reshaping, so this cuts the peak setup memory for that step.The one-body matrix inverse transform and the B real-torus DF adapter’s three-index AO-to-MO contractions now run in native OpenMP code. Canonical B RI-MP2 also uses the native complex
Lpq -> Lovcontraction.Added a deterministic parity test against the previous NumPy formulas for all native kernels. The focused B/GDF gate passed 121 tests after the port.
Scope remains honest: the current DLPNO/PNO post-HF routes still evaluate all pairs/triples unless an already validated molecular local approximation is selected. Translation-representative amplitude propagation and minimum-image domain screening are still open work.
Added: Davidson iterative diagonalization (Phase D3) — 2026-06-22¶
Blocked Davidson algorithm (
cpp/include/vibeqc/davidson.hpp,cpp/src/davidson.cpp) replaces the fullEigen::SelfAdjointEigenSolverin the SCF Fock diagonalization step with an iterative subspace method. Finds the lowest eigenvalues without O(N³) decomposition; O(N²·m_iter) per SCF iteration where m_iter ~ 5–20 for well-conditioned Fock matrices.Real-symmetric and complex-Hermitian variants (the latter for k ≠ Γ periodic Fock matrices).
Eigenvector recycling: the Davidson solver warm-starts from the previous SCF iteration’s eigenvectors, converging in 1–3 outer iters per SCF cycle. This is the key VASP-style optimisation.
Integration into every C++ SCF driver:
Molecular RHF (
cpp/src/rhf.cpp,RHFOptions.use_davidson)Γ-only periodic RHF (
cpp/src/periodic_rhf.cpp,PeriodicRHFOptions.use_davidson)Multi-k periodic RHF and RKS (
cpp/src/periodic_scf.cpp,PeriodicSCFOptions.use_davidson,PeriodicKSOptions.use_davidson)
Python bindings for
DavidsonOptions,DavidsonResult,DavidsonResultComplex,davidson_solve,davidson_solve_hermitian,davidson_summary.User-facing knob:
use_davidson=True+davidson_min_dim=100(default) on the options struct activates the path; belowdavidson_min_dimAOs the driver silently falls back to full diagonalization.Documentation:
docs/user_guide/scf_convergence.md§ “Iterative diagonalization (Davidson, Phase D3)”, keyword index, and inline option docstrings onDavidsonOptions.Tests:
tests/test_davidson.py— real + Hermitian Davidson vs numpy/scipy, eigenvector recycling, edge cases, and end-to-end SCF parity against full diagonalization (STO-3G, def2-SVP).References: Davidson (1975), Liu (1978), Kresse & Furthmüller (1996). The citation database (
python/vibeqc/output/citations/ database.toml) already carries the Kresse & Furthmüller 1996 reference; no new citation entries are needed (the VASP reference already covers periodic DFT methodology).
Added: LOBPCG eigensolver (Phase D5) — 2026-06-23¶
LOBPCG algorithm (
cpp/include/vibeqc/lobpcg.hpp,cpp/src/lobpcg.cpp) implements the Locally Optimal Block Preconditioned Conjugate Gradient method (Knyazev 2001). Converges 3–12× faster than Davidson on Fock-like matrices thanks to Rayleigh-Ritz on a 3·nvec subspace per iteration.Real-symmetric and complex-Hermitian variants.
Block-aware preconditioning using the Teter-Payne-Allan (TPA) preconditioner, matching Knyazev’s convergence theory.
Python bindings for
LOBPCGOptions,lobpcg_solve,lobpcg_solve_hermitian,lobpcg_summary.Solver framework registration in
python/vibeqc/solvers/eigensolver.py.Citation: Knyazev (2001) added to
python/vibeqc/output/citations/database.toml.Tests:
tests/test_lobpcg.py— convergence, warm-start, Hermitian path, vs scipy.linalg.eigh.
Added: Jacobi-Davidson eigensolver (Phase D6) — 2026-06-23¶
Jacobi-Davidson algorithm (
cpp/include/vibeqc/jacobi_davidson.hpp,cpp/src/jacobi_davidson.cpp) implements the Sleijpen & Van der Vorst (1996) method with MINRES correction-equation solver.Interior eigenvalue support via the
sigmaparameter — extracts eigenvalues nearest to a target shift for core-level and response-property use cases.Python bindings for
JacobiDavidsonOptions,jd_solve,jd_solve_hermitian.Marked experimental: full MINRES recurrence and harmonic Ritz-value extraction are pending. The current path uses standard Ritz with a truncated MINRES correction; works well for well-separated extremes.
Citation: Sleijpen & Van der Vorst (1996) added to
python/vibeqc/output/citations/database.toml.Tests:
tests/test_jacobi_davidson.py— extremal + interior eigenvalues, sigma-shift behaviour, vs scipy.linalg.eigh.
Added: Unified solver framework — 2026-06-23¶
SolverStrategyABC inpython/vibeqc/solvers/eigensolver.py— defines the contract every solver must satisfy:solve(problem)returns anEigenResult.Auto-registration: solvers declare their capabilities and are discovered through a module-level registry (
SOLVER_REGISTRY); no explicit import needed to use a solver by keyword.Problem descriptors:
EigenProblem— standard extremal eigenvalue problem (A x = λ x).GeneralizedEigenProblem— (A, B) pencil, reserved for future canonical-orthogonalisation paths.InteriorEigenProblem— target-shift specification (σ, n_ev).SCFStep— wraps a Fock-build callable for matrix-free operation.
7 registered solvers in the framework registry:
dense,davidson,lobpcg,jd,lanczos,hermitian_davidson,block_davidson. Currently three are selectable by keyword fromrun_job(solver=...):dense,davidson, andlobpcg; the remaining solvers are framework-registered but not yet C++-wired into the SCF drivers.Tests:
tests/test_solver_framework.py— registration, capability dispatch, keyword selection, round-trip parity.
Added: solver keyword on run_job() — 2026-06-23¶
solver="davidson" | "lobpcg" | "dense"parameter onrun_job()allows user-selectable diagonalization in molecular SCF.Wired through to the C++
use_davidsonflag inRHFOptionsandRKSOptions; future solvers will add their own C++ options fields.Default is
"dense"for fullEigen::SelfAdjointEigenSolver;"davidson"and"lobpcg"activate the respective iterative paths.Tests: end-to-end SCF parity tests for all three keywords in
tests/test_davidson.pyandtests/test_lobpcg.py.
Added: Python periodic Davidson wiring — 2026-06-23¶
All 8 Python periodic SCF drivers now support Davidson diagonalization with eigenvector recycling:
Γ-point Ewald: RHF (
periodic_rhf_dispatch.py), RKS, UHF, UKS.GDF:
periodic_rhf_gdf_dispatch.py,periodic_rks_gdf_dispatch.py.GPW:
periodic_rhf_gpw_dispatch.py,periodic_rks_gpw_dispatch.py.ROHF:
periodic_rohf_dispatch.py.
Each driver respects
PeriodicOptions.use_davidsonand forwards the previous iteration’s eigenvectors as the warm-start subspace.Tests: periodic-SCF convergence tests at Γ and multi-k for all 8 drivers with
use_davidson=True.
Added: Semiempirical solver parameter — 2026-06-23¶
solverkeyword added to theSemiempiricalModelbase class and all subclasses (MSINDO, GFN2-xTB, DFTB, OMx, PM6).Reserved for a future C++ Löwdin-orthogonalised Davidson bridge; currently stored as an attribute for forward compatibility.
No functional change to semiempirical SCF paths — the parameter is accepted and validated but ignored until the C++ bridge lands.
Tests:
tests/test_semiempirical_solver_keyword.pyverifies the parameter is accepted and round-tripped.
Added: Eigensolver benchmarks — 2026-06-23¶
examples/benchmarks/benchmark_eigensolvers.py— comprehensive benchmark comparing all registered solvers:Fock-like test matrices at n = 50, 100, 200, 500.
Matrix-free (on-the-fly matvec) vs explicit matrix paths.
Warm-start benefit quantification (convergence iteration counts).
Timing, accuracy (eigenvalue residual norms), and scalability.
Outputs a structured results table suitable for docs and regression tracking.
Added: Solver framework documentation — 2026-06-23¶
docs/user_guide/solver_framework.md— developer and user guide:Keyword selection table with solver capabilities and tradeoffs.
Capability matrix: extremal / interior / generalised / matrix-free.
Architecture diagram showing
SolverStrategy→ registration →run_job()dispatch flow.Guidance on when to use each solver (Davidson for Fock, LOBPCG for tight convergence, Jacobi-Davidson for interior roots).
Fixed: v0.14.1 correctness and test-gate hardening (2026-06-22)¶
Periodic automatic convergence no longer enables finite-temperature smearing for ionic insulators. Explicit smearing on a pre-SCF insulating profile, or with a supplied positive gap, emits a user warning and records it in the output log; FMIXING remains available without changing integer occupations.
CASSCF geometry optimization again uses full-energy central finite differences. The exposed analytic CASSCF gradient is not FD-tight, and the experimental W^z correction can worsen the disagreement, so it is no longer allowed to silently steer an optimization.
A clean
pip install -e '.[test]'includes scikit-learn, allowing the bundled ML k-spacing predictor regressions to load their model rather than fail from an incomplete gate environment.Direct
run_rhf,run_rks,run_uhf, andrun_ukscalls accept a basis name string as documented.Runtime-filtered basis sets retain the source
.g94provenance header and inherit the original basis citation route.The periodic atomic-limit suite no longer xfails a deliberately unlike bare Ewald/direct nuclear-gauge comparison; active total-energy tests remain the physical correctness check.
Patch-candidate: v0.14.x
CASSCF analytic nuclear gradient — P0 resolved (v0.15.0, 2026-06-24)¶
The P0 bug was resolved. Four root-cause bugs fixed: gamma cross terms, integral convention mismatch, q<->r index swap in 4-index AO transform, and W_unrelaxed RDM transform leaking virtual density (fixed to CASCI re-solves, then replaced with analytical W = C.F.C^T using MCSCF generalized Fock).
Default path (compute_wz=False) is FD-tight production: H2 analytic +0.02336339 vs FD +0.02336323 (delta 1.6e-7). H2O analytic -5.26814 vs numerical -5.26814 (100.0%).
W^z correction (compute_wz=True) remains experimental/gated.
Reproducer exits 0 with PASS. 15 tests green.
Patch-candidate: v0.14.x (RESOLVED in v0.15.0)
[v0.14.1] — 2026-06-24 — Bartlett’s Goose¶
Patch release. Restores the public documentation deployment, which broke on the v0.14.0 cut, and corrects two stale citation-metadata files. No functional, numerical, or API changes — the v0.14.0 known-issue list (dense real-space periodic XC on dense ionic crystals; smearing on gapped insulators) is unchanged.
Fixed¶
Public docs build and deploy. The v0.14.0 documentation site failed to build and never deployed:
docs/conf.pyenabled thesphinx_reredirectsextension (added in the v0.14.0 cycle) but thedocs-buildCI job never installed the package, so Sphinx aborted withCould not import extension sphinx_reredirectsanddocs-deploywas skipped — vibe-qc.com stayed on the v0.13.1 docs..gitlab-ci.ymlnow installssphinx-reredirectsin the docs-build job. (Docs and CI must move together — CLAUDE.md § 6.)Stale citation metadata.
CITATION.cff(was0.13.1) anddocs/citing.md(was0.13.0) were not bumped during the v0.14.0 cut; both now report0.14.1.
[v0.14.0] — 2026-06-22 — Bartlett’s Goose¶
Known issues¶
Real-space periodic XC energies on dense ionic crystals are ~400 mHa off the CRYSTAL reference (pre-existing, functional-independent; tracked for v0.14.x). Use the GPW route where validated. (RESOLVED in v0.15.0 / Unreleased — the cross-cell density fix brings MgO/STO-3G LDA to −3.9 mHa vs CRYSTAL23; see “Fixed: periodic XC now uses the full cross-cell density”.)
Fermi / Methfessel-Paxton smearing applied to a gapped insulator metallizes it and gives wrong energies. Smearing is for metals; do not apply it to insulators.
Two tests are known-failing, deferred to v0.14.x: the bundled ML k-spacing predictor (tests/test_ml_kpredictor.py) and the runner optimize-wavefunction options (tests/test_runner_optimize_wavefunction_options.py).
Next minor cycle (v0.14): canonical CCSD(T) maturation, BIPOLE multi-k corrected-gauge analytic gradients (RKS/UKS + per-k q-channel), periodic GAPW/GPW efficiency (PBC-audit E3), the SCF-guess/mixing catch-up (FRAGMO, multi-secant, periodic Anderson/Kerker wiring).
Added: sealed CRYSTAL23 dense-ionic DFT parity ladder for MgO — captures an open periodic-XC bug (2026-06-22)¶
New cross-code regression
examples/regression/crystal_parity/parity_mgo_r2scan_sto3g.py(+parity_mgo_{svwn,pbe}_sto3g.py) and sealed CRYSTAL23 referencescrystal_demos/mgo_{r2scan,pbe,svwn}_sto3g.{d12,out}, plus therun_demo_parity_rksDFT sibling ofrun_demo_parityincrystal_demos/runner.py. CRYSTAL is run strictly out-of-process; the value is sealed as a constant (CLAUDE.md §10 — no QC-program import).Known bug these regressions capture (open). vibe-qc’s periodic RKS DFT on the dense MgO/STO-3G rocksalt at SHRINK 2 lands hundreds of mHa above CRYSTAL23 (sealed; PySCF
.density_fit()agrees with CRYSTAL to ~1 mHa): r2SCAN -271.54497 vs -271.92904 (+384 mHa, converges cleanly), LDA/SVWN +428 mHa (converges), PBE ~+399 mHa (oscillates, no convergence). The error is functional-independent — LDA is ρ-only, so it is not the GGA σ / meta-GGA τ Fock terms, but the periodic XC piece every functional shares (the cross-cell density on the periodic Becke grid for dense, overlapping-image crystals). The RHF analog matches CRYSTAL to 0.369 mHa and all dilute/molecular DFT matches; a definition-parity audit (libxc IDs, 0 % EXX, grid-converged) rules out functional/grid causes. The scripts FAIL by design today and flip to PASS once the bug is fixed. Localization + audit:parity_mgo_r2scan_sto3g.pyheader and the periodic-SCF lane. Not yet fixed; tracked for the v0.14 periodic work.Patch-candidate: v0.14.0
Fixed — vq: vq admin update no longer resumes a prior interrupted update’s paused jobs (2026-06-22)¶
An admin update could wake jobs it never paused. The non-surgical pause/resume bracket (
admin.update_env/update_all) paused the queue and then resumed with a blanketresume_all— which SIGCONTs every SUSPENDED job, not just the ones this run paused. When a priorvq admin updatewas interrupted (SIGKILL / OOM / host reboot) after it paused but before itsfinallyresume ran, its jobs sat SUSPENDED; the next update then woke all of them at once. On 2026-06-22 avq admin update vibeqc-queueon maru resumed 22 such stuck-paused jobs and spiked the box to load 88 on heavy btrfs+LUKS I/O.The resume is now scoped to a per-invocation token. Each update mints a fresh tag (
admin-update-<hex>,admin._new_pause_token), pauses withpause_all(paused_by=<token>), and resumes withresume_all(paused_by_filter=<token>). A SUSPENDED job whosepaused_bydoesn’t match the running update’s token — a prior run’s straggler, an operator’s manual pause, a build-script cooperative pause — is left paused. The token is per-invocation by design: a shared constant tag would let a later run re-match and re-wake an earlier interrupted run’s leftovers. The surgical (provides_branches) path was already scoped this way via its explicit paused-jobid list.Regression coverage in
vibe-queue/tests/test_admin_update_resume_scope_v0_11_1.py(real SIGSTOP’d child processes + on-disk specs). Operator guidance invibe-queue/docs/operations.md§ “Admin update pause/resume scoping”; the build-script cooperative-pause handover now recommends a per-invocation tag too.
Added: AICCM aiccm2026dev-a — open-shell UCCSD(T) + spin-resolved properties (2026-06-22)¶
Open-shell UCCSD(T) (experimental, the
aiccm2026dev-aline).run_ccm_uccsd(ccm, uhf_result=, *, cderi=, compute_triples=)→CCMUCCSDResult(vibeqc.periodic.ccm.uccsd). Runs vibe-qc’s own spin-orbital UCCSD(T) engine (vibeqc.dlpno._ccsd_ref.run_ref_uccsd) on the UHF-CCM reference, fed the CCM neutral cderi transformed into the α/β MO bases — the natural route (the neutral four-center is the RI-separable CCM kernel and the required correlation reference).Closed-shell consistency gate: on an even-electron closed-shell cluster
run_ccm_uccsdreproduces the closed-shellrun_ccm_ccsdon the same neutral four-center to ≤1e-8 (CCSD and (T) each); a genuine doublet cluster converges with negative correlation. Tests:tests/test_ccm_uccsd.py(4).Spin-resolved properties:
ccm_homo_lumo_gapnow returns the open-shell gap (LUMO − HOMOover the α/β spectra) for UHF/UKS results; the population and dipole routines already accept an unrestricted density. Derivation:docs/aiccm2026dev_a_followon.md§ D. (U-DLPNO is the remaining follow-on, § D.3.)
Added: AICCM aiccm2026dev-a — derivable properties on the BvK torus (2026-06-22)¶
One-particle properties on the cyclic cluster (experimental, the
aiccm2026dev-aline). Newvibeqc.periodic.ccm.properties:ccm_homo_lumo_gap(band/fundamental gap from the supercell-Γ spectrum),ccm_mulliken_charges/ccm_lowdin_charges(population analysis with the cyclic overlapS^CCM, per-cell image-averaged charges + a translational-spread symmetry diagnostic),ccm_dipole(finite-cluster dipole), andccm_numerical_gradient(central-differencedE/dRper unit-cell atom; force = −gradient).Grounded validation: gap == raw-spectrum gap; charges sum to the cluster charge and recover Li⁺/H⁻ on a LiH chain; the dipole vanishes for a centrosymmetric cluster; the numerical gradient satisfies translational invariance (Σ forces = 0) and reproduces vibe-qc’s molecular analytic RHF gradient in the isolated limit to 8.9e-8. Tests:
tests/test_ccm_properties.py(7).Honest scope (documented): the dipole is the finite-cluster (not bulk polarization) quantity — the torus position operator is ill-defined (Resta 1998) and the plain
⟨μ|r|ν⟩wraps for boundary-crossing orbitals; the analytic WSSC-weighted gradient and the Resta/Berry-phase polarization are open follow-ons. Derivation:docs/aiccm2026dev_a_followon.md§ C.
Added: AICCM aiccm2026dev-a — DLPNO-CCSD(T) on the cyclic cluster (2026-06-22)¶
Subspace-projected DLPNO-CCSD(T) (experimental, the
aiccm2026dev-aline).ccm_dlpno_ccsd(ccm, scf_result, *, cderi=, g_neutral=, localize=, tcut_pno=, tcut_mkn=, compute_triples=, n_frozen=)→CCMDLPNOCCSDResult(vibeqc.periodic.ccm.dlpno_ccsd). The per-pair PNOs are merged into a single union virtual spaceV_trunc(S^CCM-orthonormal, semicanonical), and vibe-qc’s own CCM CCSD engine runs in{canonical occ, V_trunc}— on the neutral four-center reference.No-truncation gate proven numerically:
tcut_pno = tcut_mkn = 0reproduces the canonical CCM CCSD(T) on the same neutral four-center to machine ε — CCSD and (T) each, for canonical occupieds (Δ≈7e-18) and Pipek–Mezey / Boys (Δ≈0). At no truncationV_truncspans the full virtual block, so it is a unitary rotation of the canonical virtuals and CCSD(T) is invariant. Tests:tests/test_ccm_dlpno_ccsd.py(9). Shared_build_ccm_pno_pairshelper with DLPNO-MP2. Derivation:docs/aiccm2026dev_a_followon.md§ 2.3.
Added: AICCM aiccm2026dev-a — DLPNO-MP2 on the cyclic cluster (2026-06-22)¶
DLPNO-MP2 local correlation (experimental, the
aiccm2026dev-aline).ccm_dlpno_mp2(ccm, scf_result, *, cderi=, localize="none"|"pm"|"boys", tcut_pno=, tcut_mkn=, n_frozen=)→CCMDLPNOMP2Result. Reuses the validated molecular DLPNO machinery (vibeqc.dlpno.pao/.mp2component functions) with the cyclic-cluster overlapS^CCMand FockF^CCMinjected, and the(ia|jb)integrals density-fit from the neutral cderi.Built on the neutral reference (per the
-b-critique correction, main301f5fcc): the ionic Madelung background shifts the occupied–virtual denominators, so CCM correlation requires the neutral four-center reference — not the bare-1/r four-center. No Madelung-robustness is claimed.No-truncation gate proven numerically:
tcut_pno = tcut_mkn = 0reproduces the canonical CCM MP2 on the same neutral four-center to machine ε — for canonical occupieds (Δ=0) and Pipek–Mezey / Boys localized occupieds (Δ≈8e-15, exercising the coupled LMP2 residual withS^CCM-projected neighbour amplitudes). PNO occupation-threshold truncation behaves monotonically with a perturbative correction. Tests:tests/test_ccm_dlpno_mp2.py(11).Derivation:
docs/aiccm2026dev_a_followon.md§ 2.2.
Added: AICCM aiccm2026dev-a — interaction-radius cluster parameterization (2026-06-22)¶
Real-space interaction radius as the cluster knob (experimental, the
aiccm2026dev-aline). ACCMSystemcan now be sized by a real-space interaction rangeR_cinstead of an explicit meshnrep:CCMSystem.from_interaction_range(unit, R_c, basis)/CCMSystem(unit, basis=..., interaction_range=R_c)(bohr) /interaction_range_ang=(Å). The minimalnrepwhose supercell Wigner–Seitz cell encloses a sphere of radiusR_caround every atom is derived automatically; explicitnrepstays the advanced override.The real-space dual of k-point sampling (CCM ≡ SCM-Γ): a cyclic cluster
(N1,N2,N3)is a BvK torus sampling a Γ-centred(N1,N2,N3)MP mesh, andR_c⇔ uniform k-spacingΔk = π/R_c. Rigorous derivation: the WS inscribed-sphere radiusr_in = λ₁/2, withN_i = ⌈2R_c/d_i⌉provably givingr_in ≥ R_c(tight for orthogonal cells, conservative for oblique). Diagnosticsccm.wsc_inscribed_radius/ccm.kspacing_equiv.Convergence helper
ccm_interaction_range_scan(unit, basis, radii_bohr)→InteractionRangeScan(radius → energy/atom,converged_radius(tol), JSONas_records()) — the CCM analogue of a k-point convergence study.New geometry primitives
interplanar_spacings,shortest_lattice_vector_length,wsc_inscribed_radius,nrep_for_interaction_range,kspacing_for_interaction_rangeinvibeqc.periodic.ccm.wigner_seitz.Tests:
tests/test_ccm_interaction_range.py(77, incl. the inscribed-sphere gate vs a brute-force oracle on cubic/ortho/tetragonal/fcc/bcc/hex/triclinic/ vacuum lattices). Demonstration across all 28 Bravais-spanning test-set crystals:aiccm-2026/demo_interaction_range.py. Docs:docs/aiccm2026dev_a.md§ “Sizing the cluster”, derivationdocs/aiccm2026dev_a_followon.md§ A.
Added: AICCM2026DEV-B real-space extension and unrestricted route (2026-06-22)¶
The B stream now takes a cyclic lattice extension as its primary finite-size parameter;
aiccm_wigner_seitz_shells=sselects the odd extension2s+1and the equivalent Gamma-centered character net is derived exactly.UHF and UKS run through four-center, RI, and RIJCOSX in 1D/2D/3D with spin-resolved projector, electron-count, and spin-contamination gates.
The exact 3D B RI transform now feeds explicit complete-domain restricted CCSD(T), unrestricted MP2, and cost-capped full-domain/PNO UCCSD(T) correctness pilots. The latter does not yet claim representative-pair reduced scaling.
Gauge-invariant SCF properties cover electron/spin counts, Mulliken populations, finite-net gaps, idempotency, and spin contamination. Periodic polarization, analytic response, and relaxed correlated properties remain explicitly unavailable.
Added: build-as-job, env rebuilds run as exclusive cgroup-capped jobs (2026-06-22)¶
vq build-env <env>(internal) is the job-command form of an env rebuild: it runsadmin.update_env(<env>)for localhost (git pull + update_script) and exits 0 on success, 1 on a failed build, 2 if it could not start. The v0.11.0--refreshpath runs the rebuild as an uncapped inline daemon subprocess. This lets the daemon dispatch the rebuild as a normal job instead, so the cgroup scope, events, and dependents govern it. Operators still usevq admin update.JobSpec.build_envmarks a spec as the build job for an env. Additive, pre-v0.12.0 specs read clean, no SPEC_VERSION bump.The dispatch integration (now landed). When a
--refresh <env>job reaches the head and the host has drained,_maybe_refresh_before_runcreates ONE build job (vq build-env <env>, at a reserved-high priority so it ispending[0]on the drained host, declared at the daemon cpu and mem budget so the cgroup wrap caps it) and points the waiting--refreshjobs at it viadepends_on. A burst of N same-env refresh jobs shares that one build (dedup). While a build is RUNNING the dispatch loop holds ALL other dispatch, so the rebuild has the host to itself. A failed build fails its dependents through the existingdepends_oncascade, so a job never runs against a half-built env. The old inline synchronous rebuild (and its_refresh_failure_detailhelper) is gone.Tests:
tests/test_build_env_v0_12_0.py(field + command), plus the build-as-job orchestration and exclusivity classes intests/test_refresh_before_v0_11_0.py.
Fixed: periodic XC-gradient σ-Pulay v_σ clamp broke B88/LYP/SCAN gradients (2026-06-22)¶
The shared periodic XC analytic-gradient kernels (
xc_lattice_gradient_contribution[_uks]) clamped the GGA/meta-GGA σ-Pulay potentialv_σto ±10 Ha⁻¹. B88/LYP/SCAN carry a legitimately large but finitev_σin the low-density tail, where the SCF energy (and its finite-difference reference) are built from the unclipped value — so the clamp desynchronised the analytic gradient from its own energy: B3LYP / BLYP H₂ drifted ~1e-4 Ha/bohr and SCAN ~2e-3 Ha/bohr vs FD, while PBE-family functionals (whosev_σnever reaches the clamp) stayed exact. The clamp now drops to a bare NaN/inf guard, restoring the molecular-analytic match (B3LYP/BLYP/SCAN/PBE all ~1e-8 vs FD self-consistency). The separatev_τmeta-GGA clamp is unchanged. Regression from the meta-GGA τ-Pulay landing (b7c56314); never shipped in a tag. Pinned by the B3LYP + new BLYP cases intests/test_periodic_gradient_g1b.py.Patch-candidate: v0.14.0
Added: --default-job-mem-mb, so a job that declares no memory cannot oversubscribe a host (2026-06-22)¶
vq daemon run --default-job-mem-mb Nsets the assumed memory of a job that declares no--mem-mb. When set, such a job is charged this against the daemon memory budget at admission (it joins the running tally and is subject to the--max-mem-mbgate) and is cgroup-capped at it viaMemoryMax. A job that declares nothing can no longer be dispatched unbounded or drive the host into swap. A job that needs more must declare--mem-mb, which then packs correctly so only as many as fit run at once. DefaultNonepreserves the old behaviour, so a daemon is unchanged until the flag is set.Closes the gap the dispatcher flagged in its own comment, that an undeclared job was dispatched without contributing to the running memory tally. Motivated by the 2026-06-21 AICCM build storm, where a dozen jobs that each rebuilt vibe-qc and declared no memory were admitted on the job count alone and drove mars to load 128 with 32 GB of swap.
Tests:
tests/test_default_job_mem_mb.py.
Added: AICCM2026DEV-B localization and symmetry diagnostics (2026-06-21)¶
The independent B stream now exposes post-SCF occupied localization on the complete finite torus through a Wannier-like projected circular-position objective and an IAO population objective. Density, one-particle energy, translation covariance, centres, spreads, and wraparound are checked.
B-specific PAO projection, PNO construction, and occupied-pair orbit enumeration are available as experimental primitives. The Wannier route is wired to 3D local MP2; its complete-space limit is independently audited against a rotation-invariant DF-MP2 contraction. Pair representatives are not skipped yet.
symmetry_mode="diagnostic"uses spglib to report the exact cyclic-cluster compatible subgroup, nonsymmorphic atom/cell maps, and irreducible k orbits without changing SCF. Integral reduction fails closed pending general-k AO sewing and petite-list Fock/energy parity.
Fixed: run_periodic_job citation crash on plain k-meshes (2026-06-21)¶
run_periodic_job(...)raisedUnboundLocalError: _kpts_uses_mlwhile assembling the end-of-run citations wheneverkpointswas a plainBlochKMeshrather than aKPoints.recommend()result — the common case, e.g. a DFT+U run writing a.bibtexsidecar. The ML-k-spacing-predictor flag was read only inside theisinstance(kpoints, KPoints)branch but referenced unconditionally during citation assembly; it is now pre-initialised toFalsealongside its sibling smearing / bz-integration guards. Regression from the ML k-spacing citation wiring; never shipped in a tag. Covered bytests/test_dft_plus_u.py(therun_periodic_job+.bibtexcases that previously crashed now pass).
Investigated: periodic DFT XC error on dense crystals (cross-cell density + SCF convergence) (2026-06-22)¶
Root cause confirmed: for multi-k DFT the SAD initial guess zeroes all cross-cell P(g!=0) blocks, making the on-grid density ~5% too low. Functional-independent: LDA/SVWN +428, PBE +399, r2SCAN +384 mHa.
Cross-cell fix attempted but insufficient: folding per-k HCORE density restores correct P(g!=0) but SCF convergence is too slow on SHRINK 2 (both RHF and DFT). Proper per-k SAD deferred to v0.14.x. Parity ladder remains known-failing by design.
Also: _delta_k_from_predictor return-type annotation fixed. Patch-candidate: v0.14.x
Fixed: test_ml_kpredictor failures when scikit-learn absent (2026-06-22)¶
tests/test_ml_kpredictor.pyhad 10 tests that constructed aKSpacingPredictorwithout guarding for the optional[ml]extra (scikit-learn>=1.3). When sklearn was not installed the tests crashed withImportErrorrather than skipping. Added_requires_sklearn = pytest.mark.skipif(...)and decoratedTestKSpacingPredictor,TestRecommendIntegration,TestModelRoundTrip, andTestElectronConservation— the 15 feature-extraction, env-gate, and parser-output tests (which don’t touch sklearn) continue to run regardless. Also fixed the return-type annotation on_delta_k_from_predictor(-> float→-> Tuple[float, bool]). Patch-candidate: v0.14.0
Added: periodic SAP initial guess (closed-shell RHF/RKS) (2026-06-21)¶
initial_guess=SAPnow works for periodic closed-shell SCF. The lattice-summed SAP potentialcompute_vsap_latticebuilds the Fock-mode guessF_SAP = T + V_SAP(V_SAP replaces V_ne); each driver diagonalises it at Γ (or each k) for the initial density — the periodic analogue of the molecular SAP guess. Wired into the Γ RHF driver (run_rhf_periodic_gamma) and the multi-k RHF and RKS drivers (run_rhf_periodic/run_rks_periodic).The SAP potential is built by an Ewald split — analytical erfc short-range (libint
erfc_nuclear, per SAP primitive + an η-group with the net effective charges) plus a smootherf(η)/rlong-range grid-quadratured with effective chargesQ_A = Σ_i c_i. The pieces reconstruct the molecular+Σ c_i erf(ω_i)/rexactly; validated against the analytical molecular SAP matrix (compute_sap_potential_molecular).Guess-independence validated: SAP reaches the same converged energy as SAD on He/Ne cells (Γ and multi-k RHF/RKS).
Honest gating: periodic open-shell (UHF/UKS) and the Python Ewald/GDF/BIPOLE drivers do not yet build SAP and raise a clear, accurate error (no deep/outdated engine throw); PATOM/HUECKEL remain periodic-unimplemented with
AUTOresolving to SAD.Tests:
tests/test_periodic_sap.py(integral + molecular parity + Γ/multi-k same-basin),tests/test_guess.py::test_sap_periodic_gamma_reaches_same_basin_as_sad.
Added: periodic MINAO initial guess (closed-shell RHF/RKS) (2026-06-21)¶
initial_guess=MINAOnow works for periodic closed-shell SCF. The ANO-RCC minimal-basis reference density is projected onto the working basis using lattice-summed Γ overlaps,P = S(Γ)^{-1} S_tm(Γ),D = P D_ref Pᵀ— the periodic analogue of the molecular MINAO guess (compute_minao_density_periodic). A density-mode guess: the projectedDis placed on the g=0 cell, so Bloch-summing recoversD(k)for the first Fock build (exactly like SAD). Wired into the Γ RHF (run_rhf_periodic_gamma) and multi-k RHF/RKS drivers (run_rhf_periodic/run_rks_periodic).The cross-basis overlap is lattice-summed.
S_tm(Γ) = Σ_g ⟨χ(0)|χ_ref(g)⟩(cross_overlap_lattice_gamma) translates the reference shells by every direct-lattice image within the overlap cutoff — the periodic generalisation of the molecularcross_basis_overlap. The target overlapS(Γ)is the Γ-fold the driver already computes.Dis rescaled sotr(D·S(Γ)) = n_elec.Guess-independence validated: MINAO reaches the same converged energy as SAD on He/Ne cells (Γ and multi-k RHF/RKS).
Honest gating: periodic open-shell (UHF/UKS) and the Python Ewald/GDF/BIPOLE drivers do not yet build MINAO and raise a clear, accurate error; PATOM/HUECKEL remain periodic-unimplemented with
AUTO→ SAD.Tests:
tests/test_periodic_minao.py(Γ + multi-k RHF/RKS same-basin).
Added: vibe-view — compute electron density from wavefunction.gto (Phase E3) (2026-06-21)¶
The wavefunction panel gains a Compute total density button: the viewer computes ρ(r) = Σ occ_i |ψ_i(r)|² on the fly from the stored MO coefficients — no pre-computed
volume.densitysection needed — and renders it as an isosurface, reporting ∫ρ dV in the status as a self-consistency check (water → 9.89 ≈ 10 electrons on a 60³ grid).WavefunctionRenderer.evaluate_densityreuses the libint-parity-validated per-MO evaluator and sums occupied MOs across spin channels (restricted + unrestricted). Tests:tests/test_plot_renderers.py::test_wf_density_integrates_to_n_electrons(∫ρ ≈ N; non-negative). Verified live with trusted events. (Unblocked by regenerating the local example wavefunction QVFs, whose pre-2026-06-09 coefficients were un-normalized; the producer fix200471e8is on main.)
Added: vibe-view — table CLI: dump frequencies / charges / MO energies (Phase E1) (2026-06-21)¶
vibe-view table <qvf> --kind <kind> [--format csv|json]dumps a section’s tabular data to stdout —vibrations(mode / frequency),atom_properties(per-atom Mulliken / Löwdin charges), andwavefunction.gto(MO index / spin / energy / occupation). With no--kindit lists the tabulatable sections in the file. Text, CSV, or JSON, for piping into a spreadsheet or script.vibeview.tables.extract_tableis the reusable core. Tests:tests/test_cli.py::TestTable(each kind extracts; CSV/JSON/listing; bad-kind error). First piece of Phase E (data-driven interaction). (E3, compute-density fromwavefunction.gto, is deferred: the kernel is trivial but the committed example wavefunction QVFs carry un-normalized MO coefficients — pre-2026-06-09 double-normalization bug — so ∫ρ ≠ N and the density looks wrong on the demos; flagged for example regeneration.)
Added: periodic basis-optimisation gradient — Phases P1-P3 (one-electron derivatives, assembly skeleton, RKS XC term; gated on R13 for end-to-end) (2026-06-21)¶
vibeqc.basis_optimization.periodic_energy_gradient.bloch_summed_one_electron_exponent_derivativesreturns the periodic overlap and kinetic-energy derivatives w.r.t. a primitive Gaussian exponent, per k-point:dS(k)/dα = Σ_R exp(i k·R) dS(R)/dαand the kinetic analogue. Each real-space block reuses the molecularoverlap_/kinetic_exponent_derivativebindings between a home-cell shell and its image at lattice vector R (doubled-cell molecule; the home and image copies of the shared exponent are summed and the home×image cross-block read off), Bloch-summed over the SCF’s own overlap-lattice cells. Clean two-centre lattice sums only; the nuclear-attraction and two-electron derivatives are Ewald-gauge-coupled and deferred to Phase P2.Validated to ~1e-10 against a central finite difference of the Bloch-summed lattice integral at fixed k, off the reference exponent, on symmetric and asymmetric 1D chains (l = 0 and 1; complex S(k); multi-atom shared-exponent target lists) and a 3D simple-cubic cell. Tests:
tests/basisset_dev/test_periodic_one_electron_exponent_deriv.py.This is the first periodic counterpart of the molecular analytic basis-optimisation gradient. The build-independent per-k Pulay assembly (P0) landed earlier; re-audit confirmed the GDF multi-k SCF result already exposes the per-k density/Fock/overlap/hcore the assembly consumes (design-doc “P4”), so no new periodic-SCF binding is needed to feed it. See
docs/basisset_dev/PERIODIC_GRADIENT_DESIGN.md.Internal scaffolding (not yet a usable gradient): the full per-k Pulay assembly
periodic_energy_gradient_analyticis wired behind aPeriodicCoulombNuclearDerivativeProviderseam — it consumes the P1 one-electron derivatives and pulls per-k P(k)/F(k)/k-weights from the GDF result, with the nucleardV(k)and two-electrondG(k)derivatives supplied by the provider. It is gated (raises without a provider): the production provider is the Ewald-gaugedV(k)/dG(k)kernel filed as R13 inREQUIREMENTS-PERIODIC.md, not yet landed. The assembly math (_periodic_pulay_contract) and the wiring are validated against a mock provider (test_periodic_energy_gradient_analytic_wiring.py).Phase P3: the periodic explicit exchange-correlation gradient term, for both closed-shell RKS (
periodic_xc_param_gradient_term) and open-shell UKS (periodic_xc_param_gradient_term_uks, the polarised v_ρα/v_ρβ/v_σαα/v_σαβ/v_σββ contraction), LDA and GGA.dE_xc/dα = Σ_g w_g [v_ρ ∂ρ/∂α + 2 v_σ ∇ρ·∂∇ρ/∂α]with the real-space lattice densityρ(r) = Σ_h Σ_μν P_μν(h) χ_μ(r) χ_ν(r−R_h)on the periodic Becke grid, reusing the molecular on-grid∂φ/∂αper cell. The Python density assembly reproducesbuild_xc_periodic(_uks)’s E_xc to ~1e-15, and the gradient matches a frozen-density E_xc finite difference to ~1e-9 for LDA and PBE, RKS and UKS (test_periodic_xc_param_gradient.py). This is the additive KS term for the periodic gradient; it composes with the one-electron (P1) and two-electron (P2, pending R13) pieces. No C++ change.
Added: vibe-view — batch-render CLI; Phase D complete (2026-06-21)¶
vibe-view batch "runs/*.qvf" -o gallery/ [--size WxH]renders each matched.qvf’s structure to a PNG offscreen (shared isometric / dark-background view defaults), without starting the interactive server — a comparison gallery in one command.vibeview.batch.render_structure_pngis the reusable offscreen helper; files with no structure section are skipped with a note, and the command exits non-zero if nothing rendered. Tests:tests/test_cli.py::TestBatch(PNG produced; CLI gallery; no-match + bad-size errors); verified live (3/3 example files). Phase D (cross-system comparison) is now complete: overlay (D1), RMSD/Kabsch alignment (D4), density difference (D2), batch render (D3).
Added: vibe-view — density-difference viewer (Phase D2) (2026-06-21)¶
A Density Difference card (shown when two or more loaded files carry a
volume.densitysection) subtracts one density from another and renders ρ_A − ρ_B as a signed two-colour isosurface (red = accumulation where A > B, blue = depletion) over file A’s structure.compute_volume_differencerequires matching grids (same shape / origin / voxel vectors) and surfaces a clear message otherwise — trilinear interpolation onto a common grid is a planned follow-up. Tests:tests/test_plot_renderers.py(same-grid subtraction; grid-mismatch raises; the signed isosurface renders). Verified live with trusted events. This leaves only the batch-render CLI (D3) in Phase D.
Added: vibe-view — RMSD/Kabsch alignment for compare mode (Phase D4) (2026-06-21)¶
Compare/overlay mode (D1) gains an “Align (RMSD fit to first file)” switch. When on, each overlaid structure is Kabsch-superposed onto the first file (when the atom counts match), so the residual displacement — not a difference in coordinate frame — is what shows, and the per-file RMSD to the reference is reported in the legend (e.g. “water-rot (RMSD 0.00 Å)”). New pure-numpy
vibeview.align.kabsch_fit(Kabsch, Acta Crystallogr. A32, 922, 1976, cited inline) with the determinant/reflection guard so a proper rotation is always returned (never mirroring a chiral structure onto its enantiomer). Tests:tests/test_plot_renderers.py(recovers a known rotation; identity → 0; a mirror is NOT superposed; the overlay reports RMSD ~ 0 for a rotated copy); verified live with trusted events. Remaining Phase D: the volumetric difference viewer (D2) and batch-render CLI (D3).
Added: vibe-view — cross-system compare/overlay mode (Phase D1) (2026-06-21)¶
The first piece of Phase D (cross-system comparison). When more than one
.qvfis open (vibe-view open a.qvf b.qvf …), a Compare Files card in the right panel has an “Overlay all structures” switch that overlays every loaded structure in one viewport, one translucent colour per file, with a colour-keyed legend of file names._render_compare_overlayglyph-instances each file’s atoms into acmp_<i>actor;ctrl.toggle_compare_modeenters/leaves the mode (entering clears the single-file scene; leaving restores the active structure; navigating into any section also exits it). Structures are drawn at their stored coordinates — RMSD/Kabsch alignment (D4), the volumetric difference viewer (D2), and batch render (D3) are the planned follow-ups. Test:tests/test_plot_renderers.py::test_compare_overlay_draws_per_file_actors; verified live with trusted events (toggle → 2-file overlay + legend).
Fixed: Methfessel-Paxton / Marzari-Vanderbilt smearing test + docstrings reconciled with the shipped code (2026-06-21)¶
test_smearing_package.pycarried a semantically-wrong (red) test —test_apply_smearing_rejects_unimplemented_flavorsassertedapply_smearingraisesNotImplementedErrorformethfessel-paxton/marzari-vanderbilt, but those flavors ship (M6/M7:smearing/apply.pydispatches both,KPoints.recommenddefaults metals to MP). The build-test CI gate runs no tests, so the red was invisible until the full suite. Replaced with a verification test (electron conservation for MP orders 1 & 2 and MV; free energy= -T·S; occupations finite and within[0, g]), plus a regression guard that the MP generalised entropy is correctly negative for order 2 (it is not positive-definite — do not clamp it to>= 0). The stale-named T=0 test is renamed to reflect thatT=0is Aufbau for any flavor.Stale docstrings corrected:
SmearingOptions(smearing/options.py) andrun_periodic_job(periodic_runner.py) no longer claim onlyfermi-diracis implemented.
Added: vibe-view — Fermi-surface band selector (2026-06-21)¶
The
fermi_surfacepanel gains a right-panel multi-select (shown when more than one band sheet crosses E_F) to toggle which bands are drawn, so an individual sheet can be isolated.ctrl.update_fermi_bandsre-renders the chosen subset;_activate_fermireadsfermi_selected_bands(None on a fresh activation means “all”). Test:tests/test_plot_renderers.py::test_fermi_band_selection_filters_sheets.
Added: vibe-view — Fermi-surface renderer; Phase J complete (DEFERRED_KINDS empty) (2026-06-20)¶
The viewer now renders the
fermi_surfaceQVF kind (the sixth and last of the kinds the producer promoted from reserved) in reciprocal space.renderers/fermi.pybuilds the reciprocal lattice from the real-spacelattice_vectors(crystallographer’s convention b·a = 2π δ), lays each band’sE(k) − E_Fmesh into Cartesian k-space (γ rolled to the grid centre), and contours each band at E = E_F to produce one Fermi sheet per band, drawn in distinct colors inside the reciprocal-cell wireframe (spec §4.12)._activate_fermiclears the real-space molecular scene while it is shown (k space is a different coordinate system) and restores it on switch-away.Phase J is complete: with
phonon_bands,phonon_dos,equation_of_state,volume.potential,volume.rdg, and nowfermi_surfaceall rendering,kinds.DEFERRED_KINDSis now empty (kept as the structural home for any future writer-ahead-of-viewer kind, pinned by the kind-drift guard). The QVF design-doc matrix is 35/35 yes/yes.Tests (286 passed):
reciprocal_latticevalidated against the b·a = 2π δ relation (orthorhombic + non-orthogonal cells);build_fermi_sheetsvalidated against a free-electron sphere (E(k) = |k|² − k0² contours to a sphere of radius k0 centred at γ); activation integration (clears the real-space scene, draws sheets + cell); dispatch + classify guards. Verified end-to-end: the real banner classifiesfermi_surfaceasrendered. Docs: CHANGELOG, design doc, user guide.
Added: multi-system (calibration-set) molecular basis optimisation (2026-06-20)¶
recipes.molecular.optimize_molecular_basis_multi/recipes.objective.make_multi_native_objective_gradientoptimise one basis jointly against a set of molecules — the way real basis sets are calibrated (objective = Σ_s w_s · E(system_s), one shared basis), the molecular analytic-gradient analogue ofmake_multi_objective. Each per-system energy/gradient is the validated in-processmake_native_objective_gradientpair (penalty off); the conditioning/LD penalty is applied once at the joint level; the gradient is Σ_s w_s ∇E_s. All systems share one parametrisation and reference (functional/open_shell);weightsdefault to 1.0 per system. Validated: the joint analytic gradient matches the FD of the joint objective, and an end-to-end RHF run jointly optimises an H basis against two H₂ bond lengths (test_molecular_recipe.py). The single-systemoptimize_molecular_basisnow shares the BDIIS-drive/result helper.
Added: vibe-view — NCI (volume.rdg) renderer (2026-06-20)¶
The viewer now renders the
volume.rdgQVF kind (the fifth of the six the producer promoted from reserved) as a Non-Covalent Interaction (NCI) surface.renderers/nci.pycontours the reduced density gradient s(r) at the NCIPLOT default isovalue (0.3) and colors the surface by sign(λ₂)ρ — the second (middle) eigenvalue of the density Hessian times ρ — computed on the grid from a co-presentvolume.densitysection (the producer ships only s(r) per spec §4.11). The Hessian is formed by second-order finite differences; the eigenvalue sign convention and the blue-green-red color map (attractive / van der Waals / repulsive, clamped ±0.05 a.u.) follow Johnson et al. (JACS 132, 6498, 2010) and the NCIPLOT conventions of Contreras-García et al. (JCTC 7, 625, 2011), cited inline at the kernel. With no usable density section the RDG surface renders uncolored. Wired through the existing volume path (a branch in_rebuild_volume, so the isovalue slider drives the RDG surface); the formula provenance lives in inline comments (the viewer does not assemble citations).kinds.DEFERRED_KINDSdrops to one (fermi_surface). Tests: the sign(λ₂)ρ kernel validated against analytic density extrema (maximum → negative, minimum → positive), colored + uncolored render integration, classify/dispatch guards. Verified end-to-end (the real banner classifies itrendered).
Changed: BIPOLE multipole far-field (G1) retired as a production candidate; never auto-enables (2026-06-20)¶
The opt-in BIPOLE multipole far-field branch (
use_multipole_far_field,python/vibeqc/bipole_fock_multipole.py) is retired as a default-on / production candidate and is now a gated research artifact: it never auto-enables — only an explicituse_multipole_far_field=Trueactivates it (previously a cutoff/far-pair heuristic could silently enable it in the legacy gauge). It is broken in general and produces Ha-scale errors — on currentmaineven neutral dipole-free cells (H₂ ~0.5 Ha off), so the old “dipole-free cells are accurate” claim no longer holds.Corrected the root-cause diagnosis. The earlier “L≥1 interaction-tensor normalization” attribution was wrong — the bare tensor is unit-test-correct vs exact Coulomb (
tests/test_bipole_multipole.py). The Ha-scale failure is in the periodic far-field composition under the Ewald-J split: (A) cell moments built from the P0-localized Γ density carry a spurious per-cell net charge; (B) the far-field potential replaces (rather than adds to)J_SRfor every cell while dropping the reciprocalJ_LR— a conditionally-convergent bare-Coulomb lattice sum for dipolar cells; and (C) aJ_SRaliasing double-count at theapply_multipole_far_fieldcall site that corrupts even dipole-free cells (confined to the enabled-multipole path; the default production path is unaffected). The exact Ewald-J split already handles the far field optimally in reciprocal space, so there is neither an accuracy nor a speed case for the branch.The runtime warning in
resolve_multipole_confignow states the true root cause;docs/bipole_status.mdG1 is marked RETIRED. New regression testtests/test_bipole_multipole_far_field_retired.pypins the gating (never auto-enables), the spurious-charge root cause, and the dipolar-cell inaccuracy. No production-path behaviour change (the corrected exchange gauge already forced the branch off).
Added: vibe-view — electrostatic-potential (volume.potential) renderer (2026-06-20)¶
The viewer now renders the
volume.potentialQVF kind (the fourth of the six the producer promoted from reserved). It reuses the sharedVolumeRenderer(its members aregrid+data, identical tovolume.density) and is treated as a signed field, so the marching-cubes path contours both the positive and negative lobes (app._is_signed) with a divergingcoolwarmdefault colormap (viewer_defaults) — the standard ESP convention (red positive, blue negative, hartree/e per spec §4.10).kinds.DEFERRED_KINDSdrops to two (volume.rdg,fermi_surface). Tests: the parametrized both-lobes render check intests/test_audit_2026_06_03.pynow coversvolume.potential, plus dispatch + classify/diverging-default guards. Verified end-to-end (the real banner classifies itrendered).
Performance: SYM3b shell-pair mask for the BIPOLE Fock build (2026-06-20)¶
The opt-in point-group-reduced direct-ERI Fock build
(use_fock_symmetry_reduce=True) now reduces at atom-pair (shell-pair)
granularity instead of whole cells: it builds only the orbit-representative
atom-pair sub-blocks within each representative cell, then recovers the rest by
point-group rotation (symmetrize_fock_blocks, unchanged). New C++
build_jk_2e_real_space_output_subset_masked (cpp/src/periodic_fock.cpp +
binding) takes a per-rep-cell nshells*nshells flag array built by the new
representative_shell_masks in python/vibeqc/bipole_symmetry_fock.py. Because
the reconstruction only ever read the representative sub-blocks, the masked
build is bit-identical to the whole-cell path
(test_sym3b_reduced_build_equals_symmetrized_full_build, Δ=0; new
test_sym3b_shell_mask_skips_nonrep_subblocks pins the mask). Measured MgO/STO-3G
direct-ERI build speedup vs the full build: c8 2.66×, c10 2.95×, c12 4.44×
(vs the whole-cell subset’s 1.93× / 2.20× / 2.67×), growing with cutoff. Remains
off the SCF hot path / opt-in; FD-validated exactness is unchanged.
Added: semicanonical ROHF-MP2 (2026-06-20)¶
vibeqc.run_rohf_mp2(molecule, basis, rohf_result)computes second-order Møller-Plesset correlation on a spin-pure ROHF reference (Knowles, Andrews, Amos, Handy & Pople, Chem. Phys. Lett. 186, 130 (1991)). The ROHF orbitals are semicanonicalised per spin (occ-occ and vir-vir Fock blocks diagonalised); the energy is the usual doubles plus a singles termΣ_ia |f_ia|²/(ε_i−ε_a)from the off-diagonal Fock that is non-zero for an open-shell restricted reference (and vanishes for a canonical UHF/RHF reference, recovering ordinary UMP2). Returns opposite-/same-spin components and supports SCS/SOS doubles scaling.Reference kernel
vibeqc.dlpno._ccsd_ref.run_ref_ump2/run_ref_rohf_mp2— reference-agnostic spin-orbital MP2 reusing the density-fitted_spin_orbital_eri_uhfmachinery, the MP2 sibling of therun_ref_uccsdCC anchor. Citation entryknowles_rmp2_1991+routes.methods["rohf-mp2"].Verification:
tests/test_rohf_mp2.pychecks the kernel build-free on synthetic integrals against four independent references — textbook closed-shell RMP2, an independent spatial-form UMP2 (open-shell canonical and semicanonical, incl. OS/SS split), the gold-standard Slater-Condon CI engine as a sum-over-states PT2, an exact two-level singles toy, and closed/open/virtual rotation invariance. Build-validated end-to-end on the OH radical vs PySCF: ROHF SCF to 7e-13 Ha, MP2 doubles to the RI fitting error (PySCF’s plain MP2 omits the Knowles singles term vibe-qc includes).Reachable from
run_job:run_job(method="mp2", mp2_reference="rohf")on an open-shell system runs an ROHF SCF then the semicanonical ROHF-MP2 (all-electron, like the native RMP2/UMP2 path), labels the output blockROHF-MP2, reports the Brillouin singles term, and fires therohf-mp2citation route.scs-mp2/sos-mp2honourmp2_reference="rohf"too (doubles scaled). Validated against the standalonerun_rohf_mp2(tests/test_runner_mp2.py).Files:
python/vibeqc/dlpno/_ccsd_ref.py,python/vibeqc/cc.py,python/vibeqc/__init__.py,python/vibeqc/output/citations/database.toml,python/vibeqc/runner.py,tests/test_rohf_mp2.py,tests/test_runner_mp2.py,tests/test_citations.py.
Added: open-shell double hybrids via ROKS (B2PLYP, DSD-PBEP86)¶
run_double_hybridnow handles open-shell molecules (vibe-qc’s first open-shell double hybrid). Multiplicity > 1 runs the spin-pure ROKS SCF half then a semicanonical ROHF-MP2 doubles correction scaled by the functional’smp2_c_os/mp2_c_ss, combined asE = E(ROKS) + c_os·e_os + c_ss·e_ss. The Knowles singles term is excluded, matching the closed-shell RMP2 convention and standard open-shell double hybrids (e.g. ORCA U-B2PLYP); the reference stays spin-pure (⟨S²⟩ exact).Enabled by the new ROHF-MP2 (
run_rohf_mp2), which consumes the ROKS result directly (itsmo_coeffs/fock_alpha/fock_betasurface). Theroks.pydouble-hybrid gate now points users torun_double_hybridand carries an internal_allow_double_hybrid_scfflag for the SCF half; a directrun_roks(..., functional="b2plyp")still raises (SCF-only is incomplete).GGA-based double hybrids only (B2PLYP, DSD-PBEP86); meta-GGA ones (pwpb95) raise via ROKS (no τ term yet). Closed-shell B2PLYP / DSD-PBEP86 unchanged. Tests:
tests/test_b2plyp.py::test_run_b2plyp_open_shell_roks(+ gate test). Validated on OH / cc-pVDZ: SCF converges, the correction lowers the energy, and the doubles match a standalonerun_rohf_mp2.Files:
python/vibeqc/__init__.py,python/vibeqc/roks.py,tests/test_b2plyp.py.
Added: one-call native molecular basis-optimisation recipe (2026-06-20)¶
recipes.molecular.optimize_molecular_basisturns the completed molecular analytic energy gradient (RHF/UHF/RKS/UKS × exponents + coefficients) into a single user-facing call: it builds the consistent in-process(objective, analytic-gradient)pair and drives the robustoptimize_bdiisloop with the analytic gradient (no per-parameter re-SCF finite differences). The reference is selected byfunctional/open_shell(HF vs KS, restricted vs unrestricted);tol_graddefaults to 1e-5 (HF) / 1e-4 (grid-based KS);analytic=Falsefalls back to the driver’s FD gradient (for SPcoeff_s/coeff_p). Returns aMolecularOptResultcarrying the optimised{symbol: CrystalAtomBasis}dict + asummary()(per-parameter start→optimum in physical space). Unlikerecipes.production(multi-crystal, derivative-free NLopt via the external-program Calculator), this is the vibe-qc-native molecular path. Tests:test_molecular_recipe.py(RHF H₂, UKS/PBE C ³P, FD fallback).
Added: vibe-view — equation-of-state (V-E curve) renderer (2026-06-19)¶
The viewer now renders the
equation_of_stateQVF kind (the third of the six the producer promoted from reserved).renderers/eos.pyplots the sampled (volume, energy) points and overlays the fitted EOS curve, reconstructed by evaluating the published energy form (3rd-order Birch-Murnaghan, Birch 1947; or Murnaghan 1944) at the producer’sfitparams, with V0/B0/B0’ and the model citation annotated in-panel. The B0·V0 prefactor is converted GPa·Å³ to eV per the spec §4.14 units.kinds.DEFERRED_KINDSdrops to three (volume.potential,volume.rdg,fermi_surface). vibe-qc’s core does not fit the EOS (study scripts / external tools do), so the formula provenance lives in inline comments + the in-panel annotation rather than the producer’s references block. Tests:tests/test_plot_renderers.py(render, the BM3 formula validated at hand-computed points, Murnaghan selection, activate wiring) + the renderer-dispatch and kind-drift guards.
Added: AICCM aiccm2026dev-a symmetric-torus CCM, full method stack (2026-06-21)¶
The aiccm2026dev-a development line of the ab-initio Cyclic Cluster Model — a
Γ-point finite Born–von-Kármán supercell with WSSC integral weighting (CCM ≡
SCM-Γ), feeding vibe-qc’s molecular HF/KS/post-HF kernels. Developed
independently from, and kept separate for head-to-head comparison with, the
aiccm2026dev-b line below. Standalone docs: docs/aiccm2026dev_a.md; theory
AICCM_ALGORITHM.md §13; demo examples/periodic/aiccm2026dev_a_demo.py.
Symmetric Born–von-Kármán-torus four-center (
method="aiccm2026dev-a"): symmetric bridge¼(ω_μρ+ω_νρ+ω_μσ+ω_νσ)with ρ, σ folded independently to the home bra’s WSC — exactly 8-fold permutationally symmetric for any lattice (≤3e-18 in 1D, 2D-hex, 2D-oblique; genuinely 3D via the scalable C++ route), where the historical eq-18union12weight breaks ρ↔σ / μ↔ν symmetry (1.6e-3 in 1D → 2.1e-2 in 2D).union12stays the default.Full method stack on the CCM-weighted integrals, all via
method=: HF (run_ccm_rhf, open-shellrun_ccm_uhf; 1D H₄ → gold −0.542875), KS-DFT (run_ccm_rks/run_ccm_uks, pure + hybrid, == molecularrun_rks/run_uksto <1e-6 for PBE/PBE0/B3LYP), and correlation (run_ccm_mp2, open-shellrun_ccm_ump2,run_ccm_ccsdwith perturbative (T) — CCSD == molecular DF-CCSD to the DF gap, (T) to ~1e-8). The scalable C++ four-centerrun_ccm_rhf_scalable(build_jk_ccm_weighted) reaches genuine 3D.4-center → RI → RIJCOSX hierarchy: RI via multi-k GDF on the unit cell (
run_ccm_rhf_gdf/run_ccm_rks_gdf, ≲0.1 mHa/atom from the four-center in 1D and 3D — CCM ≡ SCM-Γ); explicit WSSC RI-Jrun_ccm_rhf_rijand RIJCOSXrun_ccm_rhf_rijcosx(isolated == molecular RIJCOSX to ~5 µHa).Neutral (Ewald) four-center + the Madelung gap, resolved (
ccm_eri_neutral,AICCM_ALGORITHM.md§13.7/§13.9): the bare-1/r four-center is quantitative for covalent / molecular / 1D but over-binds ionic 3D crystals by a Madelung-scale shift (LiH rocksalt RKS-PBE/atom: 4c −4.540 vs gdf −4.203). The neutral four-centerg_eff = (μν|v_E|ρσ)(RI density-fit of the neutral torus kernel) is the gapless object; the gap is exactly a rank-1 mean-field backgroundg_eff = bare − ξ·S⊗S(ξ = the cell Madelung constant). It is ∝ S in the Fock (absolute ionic HF/KS energies use the GDF route) andξ·I⊗Iin the MO basis — so it cancels exactly in the correlation energy (machine-ε, any ξ): the CCM post-HF stack is rigorously Madelung-robust.RI-consistent three-center (
ccm_neutral_cderi,ccm_ri_tensors_neutral,ccm_ri_j_neutral/ccm_ri_k_neutral): the neutral cderiL; its RI-J/K reproduce the neutral four-center to machine ε, where the bare-1/r union RI-J floors at ~1e-3 (the bare four-center is non-separable). The RI-consistent energy is the GDF route.AICCM-2026 benchmark (
aiccm-2026/): 18-system test set (1D/2D/3D, covalent / ionic / molecular / oxide, closed / open-shell) with arun_case.pydriver,compare.pyaggregator, and avqbatch generator, comparing the hierarchy against vibe-qcbipole/gdfand CRYSTAL23.Tests:
tests/test_ccm_aiccm2026dev_a.py,test_ccm_dft.py,test_ccm_ri.py,test_ccm_neutral.py,test_aiccm2026_testset.py. Experimental; reuses the existing CCM (Peintinger & Bredow 2014) and periodic GDF (Sun et al. 2017) citation routes.
Added: independent AICCM2026DEV-B finite-torus RHF/RKS (2026-06-21)¶
Real-torus local-PNO MP2, CCSD, and CCSD(T). The pair-resolved B RI Hamiltonian now has an exact inverse finite-group transform to real finite-torus overlap, Fock, and three-index factors. No-truncation local MP2 agrees with character-space canonical MP2 to 7e-18 Ha per cell. On the nonzero-triples LiH/STO-3G control, local versus canonical finite-torus DF-CCSD and (T) differ by 6.9e-12 and 1.4e-12 Ha total. PBC-safe Pipek–Mezey localization is the default. Molecular Boys localization, Euclidean pair/coupling screens, and local fitting fail closed until minimum-image periodic domains are implemented.
Canonical 3D RI-MP2 now uses the B finite Hamiltonian directly.
run_aiccm2026dev_b_mp2contracts pair-resolved three-center tensors on the full character mesh with exact finite-group momentum conservation. A canonical auxiliary-AO factor removes independent q/-q eigenspace phases. On the H2/STO-3G (1,1,2) control, HF and correlation energies agree with out-of-process PySCF KRHF/KMP2 within 0.6 nanohartree per cell. The older Gamma-supercell post-HF bridge is intentionally not reused: its HF reference differs by 4.641 mHa per cell on the compact two-cell control. MP2 fails closed in 1D/2D until their Coulomb gauges are matched.A separately selectable
jk_method="aiccm2026dev-b"defines a neutral, zero-temperature, closed-shell finite Born-von-Karman torus for RHF and RKS (including hybrids) without changing the existing CCM route. The constrained Gamma-supercell problem is evaluated through its exactly equivalent full, unreduced, Gamma-centred character mesh. The electron-repulsion backend is explicitly selectable as corrected-gauge four-centre, pair-resolved RI, or RIJCOSX.Independent boundary construction and variational definition: exact Wigner-Seitz closest-image representatives include non-Bravais centre offsets and split geometric ties by a partition of unity. Historical pair-product four-centre weights are deliberately not used because their displayed form does not preserve every ERI permutation needed for a conventional scalar RHF functional.
Dimensional support and fail-closed physics: RHF/RKS now execute in 1D, 2D, and 3D through all three backends. Charged cells, open shell, smearing, shifted or non-cyclic meshes, and the inconsistent q-only
compcellfit are rejected. Results report density idempotency, electron count, Wigner-Seitz partition, inverse-Bloch, timing, and finite-cluster diagnostics.Validation and comparison: the H4/STO-3G chain at cyclic lengths 1, 2, and 4 is identical to the direct Gamma-centred character-mesh result to all printed digits; the difference from the historical
union12CCM falls to 1.257 mHa/cell at length 4. The separately developed symmetricaiccm2026dev-apeer is also reported and, as expected in this 1D case, matchesunion12to the printed digits. A compact two-cell H2 control runs RHF and PBE0 through all three backends with a maximum 43.7 microhartree spread. The larger H4 direct/RI discrepancy remains explicitly open. The derivation, decisions log, comparative LaTeX manuscript, benchmark harness, tests, citation route, and persistent handover ship in the same milestone.Full B-stream fleet inputs:
aiccm-2026/run_case_b.pyandmake_jobs_b.pyreuse the exact 18-system A-stream geometry registry while dispatching onlyaiccm2026dev-b. The explicit matrix contains RHF, PBE, and PBE0 with four-center, RI, and RIJCOSX backends plus canonical RI-MP2, DLPNO-MP2, DLPNO-CCSD, and DLPNO-CCSD(T). JSON results include orbital gaps, energy components, finite-torus invariants, local-pair diagnostics, and timings.compare_b.pyjoins the result set to compatible A and CRYSTAL energies without treating basis- or functional-mismatched values as parity.Experimental documentation is separate by stream: the B implementation has its own user guide, tutorial, periodic example, benchmark harness, warning class, theory/decisions files, and manuscript sections. The paper now audits the 2008 Janetzko–Köster–Salahub deMon2k KS-ADFT weights: its two-center and RI-adjoint proofs remain open, while its explicitly two-order-averaged three-center equation is not assigned the 2014 four-center symmetry defect without further evidence.
Added: periodic ECP integrals from CRYSTAL-format inline data (2026-06-20)¶
ECP application in periodic SCF unblocks 107 of 325 cohesive-energy reference compounds (every 4d/5d element: Ag, Au, W, Pt, …) by wiring the already-parsed CRYSTAL inline-ECP Gaussian expansions through libecpint’s
set_ecp_basis(…)inline-primitive API (Phase 14g).Two new C++ entry points:
compute_ecp_matrix_from_primitives(molecular single-cell) andcompute_ecp_lattice_from_primitives(periodic lattice-summed, mirroringcompute_nuclear_lattice). Both acceptECPPrimitiveBlockflat arrays — the per-centre (α, c, n, am) primitives that libecpint expects — and produce a real symmetricV_ECP_{μν}matrix in the spherical AO basis.Periodic SCF drivers (
run_rhf_periodic/run_rks_periodic) now accept ECP data viaPeriodicSCFOptions.ecp_primitive_blocks/.ecp_home_centers/.ecp_effective_charges/.ecp_total_ncore. When present, H_core uses Z_eff nuclear attraction + V_ECP, n_elec is reduced to valence-only, and nuclear repulsion uses Z_eff. All-electron behaviour is unchanged.Auto-attach for pob-TZVP-REV2:
run_periodic_jobdetects ECP-bearing pob bases (Z+200 convention) via_resolve_ecp_data, callsbuild_periodic_ecp_datato convert the parsedCrystalECP→ECPPrimitiveBlocklists, and sets them on the C++ options struct. No user wiring needed.Validation targets (PBE/pob-TZVP-REV2 + ECP against PySCF.pbc, ≤1 mHa/atom): Ag fcc, Au fcc, W bcc. Regression tests in
tests/test_periodic_ecp.py(pending PySCF cross-validation for final reference values).META.yaml:
heavy-element-ecpblocker removed from Ag/Au/W validation systems (they still carryperiodic-meta-ggaandgeometry-optimization).Files touched:
cpp/include/vibeqc/ecp.hpp,cpp/src/ecp.cpp,cpp/src/periodic_scf.cpp,cpp/include/vibeqc/periodic_scf.hpp,cpp/include/vibeqc/lattice_integrals.hpp,cpp/src/lattice_integrals.cpp,cpp/src/bindings.cpp,python/vibeqc/basis_crystal.py,python/vibeqc/periodic_runner.py,python/vibeqc/__init__.py,tests/test_periodic_ecp.py.
Added: vibe-view — phonon band-structure + phonon DOS renderers (2026-06-19)¶
The viewer now renders the
phonon_bandsandphonon_dosQVF kinds, two of the six the producer promoted from reserved in M15-M19 (previously surfaced as “skipped, not yet rendered”).renderers/phonon.pyplots the phonon dispersion along the Brillouin-zone q-path and the phonon DOS, both in cm^-1, on the same 2D side-panel machinery as the electronic bands/DOS. Soft (imaginary) modes are drawn below the zero-frequency line rather than clamped, so a dynamical instability stays visible.kinds.SUPPORTED_KINDSgains both kinds andDEFERRED_KINDSdrops to four (volume.potential,volume.rdg,fermi_surface,equation_of_state). The viewer plots producer-computed frequencies faithfully, so there is no new scientific citation. Tests:tests/test_plot_renderers.py(render, soft-mode preservation, activate-helper wiring) plus the renderer-dispatch and kind-drift guards.
Added: r²SCAN01 meta-GGA functional, molecular + periodic (2026-06-20)¶
functional="r2scan01"registers the r²SCAN01 meta-GGA (Furness, Kaplan, Ning, Perdew, Sun, J. Chem. Phys. 156, 034109 (2022)): a re-regularization of r²SCAN with a larger η that restores the fourth-order term of the slowly-varying density-gradient expansion r²SCAN had traded away for smoothness, targeting slowly-varying densities (bulk metals, bonding regions) where r²SCAN slightly underbinds. libxcXC_MGGA_X_R2SCAN01(645) +XC_MGGA_C_R2SCAN01(642), both τ-only (laplacian-free).Molecular and periodic out of the box. Being a τ-only meta-GGA it rides the existing molecular (RKS/UKS SCF + analytic gradient) and periodic (GDF / Ewald / multi-k / BIPOLE Γ) meta-GGA machinery with no driver changes, exactly as
r2scandoes.Validation: H₂O/def2-SVP RKS matches PySCF (
MGGA_X/C_R2SCAN01, grids.level=5) to 3.9 µHa; periodic H₂/STO-3G in a 30-bohr box tracks the molecular limit to <1e-7 Ha. Citation routes forfunctional="r2scan01"(Sun 2015 + Furness 2020 + Furness 2022). Tests intests/test_xc.py.
Added: frozen natural orbitals (FNO) for closed-shell CCSD(T) (2026-06-20)¶
CCSDOptions(fno=True)truncates the virtual space to the dominant MP2 natural orbitals before CCSD(T) (DePrince-Sherrill, J. Chem. Theory Comput. 9, 2687 (2013)), the standard lever for making canonical CCSD(T) affordable on larger molecules. The MP2 virtual one-particle density is diagonalized, the dominant natural orbitals are kept (occupation cutofffno_occ_threshold, default 1e-5, or a fixedfno_keep_fraction), the retained block is semicanonicalized so the perturbative(T)stays exact, and a delta-MP2 correction (fno_delta_mp2, default on) recovers the truncated MP2 correlation.New C++ entry
run_ccsd_from_mos(cpp/src/ccsd.cpp, bound as_vibeqc_core.run_ccsd_from_mos): closed-shell CCSD(T) from explicit MO coefficients + an AO Fock matrix, sharingrun_ccsd_corewith the canonicalrun_ccsd(whose numerics are unchanged). This is the closed-shell sibling ofrun_uccsd_from_mosand the kernel entry the FNO path drives.Reachable from
run_job:run_job(method="ccsd(t)", ccsd_options=CCSDOptions(fno=True))runs FNO-CCSD(T) and reports the kept / total natural-orbital count and the delta-MP2 correction in the.out. Orchestration isvibeqc.cc.run_fno_ccsd, returning anFNOCCSDResult(CCSDResult fields plusn_virtual_kept/n_virtual_total/delta_mp2).Validation (
tests/test_ccsd_fno.py, always-on): with no truncation the FNO path reproduces canonical CCSD(T) to machine precision (CCSD is invariant to virtual rotation; semicanonicalization restores the canonical(T)); truncation + delta-MP2 recovers correlation as expected. Closed-shell (RHF) only; open-shell FNO and an in-kernel C++ FNO build are future work. Citation: the DePrince-Sherrill 2013 entry already routes forccsd/ccsd(t).
Added: FRAGMO fragment-superposition initial guess (molecular) (2026-06-20)¶
InitialGuess.FRAGMO— the molecular fragment-MO assembly guess (roadmap D2h). The user partitions the molecule into fragments; each fragment is converged on its own SCF and the fragments’ converged density matrices are assembled block-diagonally (one diagonal block per fragment, inter-fragment blocks zero) into the supersystem guess density. For weakly-interacting assemblies (hydrogen-bonded dimers, van-der-Waals complexes) the fragments already carry almost all of the electronic structure, so the guess starts much closer to the supersystem solution than SAD and converges in fewer SCF iterations.API:
run_rhf/run_rks/run_uhf/run_uks(mol, basis, opts, fragments=...)withopts.initial_guess = InitialGuess.FRAGMO.fragmentsis a list ofvibeqc.Fragment(atoms, charge=0, multiplicity=1)specs, or plain atom-index sequences (e.g.[[0,1,2],[3,4,5]], each a neutral closed-shell fragment). Covers RHF / UHF / RKS / UKS — fragments are converged with the supersystem’s method/functional; an open-shell supersystem feeds each fragment’s α/β blocks to the matching spin. Rides the sameopts.read_densityinjection path as the READ guess.Honest errors (CLAUDE.md §7): fragments must be a disjoint partition covering every atom exactly once and their charges must sum to the molecule charge (electron conservation) — each violation raises a clear
ValueError; a non-convergent fragment SCF raises;fragments=withoutinitial_guess=FRAGMOraises; FRAGMO on a periodic system raises a cleanNotImplementedError(a crystal has no finite-fragment partition).New module
python/vibeqc/guess_fragmo.py(sibling ofguess_read.py);vibeqc.Fragmentexported at the package top level. C++InitialGuessenum + the four molecular drivers consume the injected density for FRAGMO exactly as for READ.Tests:
tests/test_fragmo.py— guess-independence (FRAGMO energy == SAD energy on water dimers, RHF + RKS/PBE + UHF), the iteration-count win on weakly-interacting dimers, and the full honest-error surface.
Fixed/Added: periodic density mixers (Anderson + Broyden) wired into the multi-k RKS driver (D4) (2026-06-20)¶
density_mixer="anderson"/"broyden"now selects real-space density-matrix mixing inrun_rks_periodic_multi_k_ewald3dandrun_rks_periodic_scf(defaultNone→ unchanged Fock-DIIS). When active a mixer replaces Fock-DIIS, linear damping and the quadratic fallback (they are alternative convergence strategies, not stacked — CLAUDE.md §7); smearing and the level shift still apply.BroydenMixer(limited-memory Broyden second method) is added alongsideAndersonMixer, superseding the orphaned, occupation-guessingscf_acceleration.BroydenAccelerator.Correctness fix (§7): the mixers now mix the per-k density matrices (electron-count-exact) and rebuild the real-space density via the canonical fold. The initial wiring mixed the real-space
LatticeMatrixSetand Bloch-summed it back, but the lattice-sum cutoff cell list does not invert cleanly to the k-mesh —bloch_sum(D_real,k)is an aliased/scaled proxy, so the per-k electron count exploded and the SCF converged to a nonphysical over-bound energy (~−14 Ha on a 2-electron metallic H-chain). Validated: Anderson/Broyden reach the same stationary point as the converged DIIS reference to µHa across gapped (1e-10), Fermi-smeared metallic, and T=0 Gilat-Raubenheimer (1e-11) regimes.Honesty surface:
scf_accelerator="anderson"/"broyden"(a Fock-space selector that has noSCFAcceleratorenum value) now raises aNotImplementedErrorpointing the user at the density-spacedensity_mixer=entry point instead of just “unwired”.run_rks_periodic_scffails closed ifdensity_mixeris requested on a route that cannot honour it (DIRECT_TRUNCATED / Γ-only).Kerker preconditioner (D4b) is now wired:
density_mixer_kerker=True(withkerker_k0/kerker_strength/kerker_cutoff_ha) applies the genuine reciprocal-space filterK(G)=|G|²/(|G|²+G₀²)to the density mixer’s residual — collocate the cell-average residual to a plane-wave grid (periodic_gapw_j), filter the small-|G| charge-sloshing modes, project back with the S⊗S density-fit metric. Because it preconditions the residual (zero residual → zero correction) the converged energy is provably the exact SCF solution (CLAUDE.md §7-safe) — validated to µHa vs DIIS across gapped/Fermi/gilat. Honest caveat: on the minimal-basis Gaussian test cells (STO-3G H-chains) it is convergence-neutral — its target, long-wavelength sloshing of large metallic supercells / Be-fcc, was not reproduced here, so it ships as a correct opt-in tool, not a demonstrated speed-up. Requires adensity_mixer; fails closed otherwise.Tests:
tests/test_periodic_density_mixing.py(+ BroydenMixer + per-k bridge unit tests) andtests/test_periodic_density_mixing_wiring.py(DIIS-parity across gapped/smeared/gilat, gating, dispatch threading + fail-closed, Kerker §7-safety incl. the zero-residual→zero-correction invariant).
Added: open-shell DLPNO-UCCSD(T) pilot (UHF reference), M2 (2026-06-21)¶
vibeqc.dlpno.run_dlpno_uccsd_pilot– the open-shell sibling of the closed-shell DLPNO-CCSD pilot (run_dlpno_ccsd_pilot). Spin-orbital subspace-projected UCCSD(T) on a UHF reference: the residuals are evaluated by the FCI-anchored spin-orbital engine (dlpno._ccsd_ref, SGWB-1991, the same kernel that anchorscpp/src/uccsd.cpp) while the ansatz is the DLPNO one. Because the reference is unrestricted the spin-orbital integrals already encode spin, so there is one virtual space and no explicit αα/ββ/αβ channel split – far simpler than re-deriving spin-adapted open-shell residuals (Saitow et al., J. Chem. Phys. 146, 164105 (2017)).Ansatz: T2 amplitudes live in per-pair PNO spaces (built from the spin-orbital MP2 pair density
D = T Tᵀ + Tᵀ T, truncated by occupationtcut_pno, quasi-canonicalised); T1 in the full spin-orbital singles space (diagonal pairs(P,P)vanish by Pauli antisymmetry, so there is no diagonal-pair PNO set). Each macro-iteration expands the constrained amplitudes to the full spin-orbital space, evaluates the exact SGWB residuals, and projects them back into the retained subspaces.Exactness / projection ratchet: untruncated (
tcut_pno=0) the pilot reproduces the spin-orbitalrun_ref_uccsdanchor to ~µHa – canonical and Foster-Boys-localised occupieds (full-rank PNO rotations exercise expand/project), and with a frozen core (OH/STO-3G: ≤6e-10 Ha; OH/def2-SVP, CH₃/def2-SVP: ≤3e-10 Ha).tcut_pno=1e-7recovers ~99.8-99.9% with real PNO compression (OH/def2-SVP 11.9/29 spin-orbital PNOs).(T): the perturbative triples (
_ccsd_ref.so_triples_correction, Raghavachari 1989, spin-orbital) on the converged amplitudes match the anchor at full domains –localise="none"exact in the spin-orbital basis,"boys"via rotating the occupieds back to canonical first (the exact-(T) path, mirroring the closed-shell pilot). ≤5e-11 Ha vsrun_ref_uccsd(compute_triples=True).O(N⁶) full-space-contraction pilot, capped at
max_nbf=64– the correctness oracle a reduced-scaling open-shell engine will be validated against (mirrors the closed-shell pilot/local-solver relationship). Tests:tests/test_dlpno_uccsd.py(14: exactness/frozen-core/diagonal-pair-exclusion/(T) fast lane + DZ recovery/exactness/(T) slow lane).User-facing:
run_job(method="dlpno-ccsd"|"dlpno-ccsd(t)")now auto-routes an open-shell reference (multiplicity > 1) to UHF + the DLPNO-UCCSD(T) pilot (it raised before)._resolve_scfreturnsuhf; the effective labeldlpno-uccsd/dlpno-uccsd(t)drives a spin-orbital.outblock, adlpno_uccsd_donestructured-log event, and the open-shell paper (Saitow 2017) reaching.references/.bibtexviaroutes.methods["dlpno-uccsd"/"dlpno-uccsd(t)"]. The result exposesres.dlpno_ccsd(aDLPNOUCCSDPilotResult); pass aDLPNOUCCSDPilotOptionsasdlpno_ccsd_optionsto control localise / tcut_pno / frozen core. Closed-shell DLPNO-CCSD/(T) continue to use the reduced-scaling local solver. Tests:tests/test_runner_dlpno_ccsd.py(open-shell e2e + citationoptions passthrough + pilot cap).
Added: open-shell DLPNO-UMP2 (UHF reference), M1-M1c (2026-06-20)¶
vibeqc.dlpno.run_dlpno_ump2– the open-shell analogue ofrun_dlpno_mp2. The UHF-reference MP2 correlation is resolved into the three spin channels (αα/ββ same-spin antisymmetrised + αβ opposite-spin Coulomb; Szabo & Ostlund §6.7), each built from the density-fitted α/β B-tensors – the mixed αβ block is a single contraction of the α and β B-tensors over the auxiliary index.M1 (
localise="none", canonical occupieds): per-spin-pair PNOs truncated by occupation number (tcut_pno) – same-spin pairs one PNO set, opposite-spin pairs a separate α-PNO and β-PNO set. Exactness ratchet:tcut_pno=0reproduces canonical UMP2 (correlation.ump2_energy, same DF integrals) channel by channel to machine precision (≤3e-17 Ha on OH/STO-3G, OH/def2-SVP, CH₃/def2-SVP, frozen-core);tcut_pno=1e-8recovers ~99.99% with real PNO compression.M1b (
localise="boys"): Foster-Boys localised α/β occupieds with the coupled LMP2 residual solved per spin channel in the full virtual space (the off-diagonal localised Fock couples each pair to its neighbours; the three channels are independent at first order and solved separately). Also reproduces canonical UMP2 to machine precision (LMP2 with full virtual == canonical MP2).M1c (
localise="boys",tcut_pno>0): per-pair PNO truncation on top of the localised coupled residual. Each pair carries its own truncated PNO basis and the coupling sums project a neighbour’s amplitude into the target pair’s PNO space via the PNO overlapS = U_target^T U_src(same-spin: one set, sign-carried for the pair antisymmetry; opposite-spin: separate α/β sets, dual-sided projection). Because the PNOs are pair-specific rotations even attcut_pno=0, the full-domain limit exercises the projections and stays exact – the projection ratchet cross-checks the per-pair-PNO solver against the independent full-virtual solver and canonical UMP2. Attcut_pno=1e-8it recovers ~99.6-99.8% with PNO compression (the standard DLPNO localised-domain approximation).Locality (
tcut_pairs, Boys path): a pair whose semicanonical MP2 estimate is belowtcut_pairsis dropped from the coupled solve and contributes that cheap full-virtual estimate directly, turning the pair list O(N) for extended systems. No-op on compact molecules (every pair strong) and lossless on separated fragments – an OH radical + a water 8 Å away screens all ~90 inter-fragment pairs for < 0.001 kcal/mol. Default 0 (exact); a calibrated default and PAO virtual domains are later steps.SCS/SOS spin-component scaling and frozen core supported on all paths.
User-facing:
run_job(method="dlpno-mp2")now auto-routes an open-shell reference (multiplicity > 1) to UHF + DLPNO-UMP2 (it raised before). The.outcarries a spin-channel-resolved DLPNO-UMP2 block, adlpno_ump2_donestructured-log event fires, the result exposesres.dlpno_ump2, and the open-shell paper (Saitow et al., J. Chem. Phys. 146, 164105 (2017)) reaches the.references/.bibtexviaroutes.methods["dlpno-ump2"]. Pass aDLPNOUMP2Optionsasdlpno_optionsto control thresholds. Tests:tests/test_dlpno_ump2.py(21) +tests/test_runner_dlpno_mp2.py(open- shell e2e + citation + options passthrough).
Added: CASSCF analytic nuclear gradient (single-state, z-vector-free) (2026-06-20)¶
Analytic CASSCF nuclear gradient for single-state RHF-CASSCF, surfaced on
SolverResult.gradient(shape(n_atoms, 3), Ha/bohr). Computed by contracting the effective AO 1- and 2-particle density matrices with AO derivative integrals via the existing C++ gradient kernels, plus an overlap-gradient correction (W_unrelaxed) from the orthonormalization response. Validated in a three-way comparison: against ORCA 6.1 EnGrad (energy Δ = 0.6 mHa), OpenMolcas &ALASKA (energy Δ = 1e-8 Ha), and in-repo full CASSCF finite differences. The gradient captures ~87% of the full CP-MCSCF gradient; the remaining ~13% (W^z, CI+orbital relaxation via the Handy-Schaefer z-vector) is gated behindCASSCFOptions(compute_wz=True)(see 2026-06-22 entry below).SolverResult.gradient— new optional field (n_atoms × 3 ndarray, None for methods without gradients).8-fold AO gamma symmetrization — resolves the C++ kernel’s l-canonical shell reordering for systems with mixed angular-momentum shells (essential for p-elements, no-op for pure s-shells).
Citation database:
handy_schaefer_cp_mcscf_1984entry + route undercasscf.Parity script:
examples/regression/parity_casscf_gradient.py— out-of-process ORCA + OpenMolcas comparison.Tests:
tests/test_casscf_gradient.py(6 tests, H2/6-31G and H2O/STO-3G, all green).
Added: CASSCF gradient — W^z correction, SA gradients, geometry optimization (2026-06-22)¶
W^z (CI+orbital relaxation) correction — CP-MCSCF z-vector approach via energy-FD g^R (correct ∂²E/∂κ∂R without double-counting the metric response). Gated behind
CASSCFOptions(compute_wz=True). Captures ~60% of the W^z gap at equilibrium; the remaining ~40% requires the proper Handy-Schaefer F^z construction.State-averaged CASSCF gradients — using state-averaged RDMs (
make_rdm12_sa). W^z not yet implemented for SA.Geometry optimization —
optimize_molecule(method="casscf")now uses analytic gradients instead of FD on energy. H2 CAS(2,2)/6-31G verified: R=1.5 → R≈1.4 in ~3 L-BFGS steps.CI coupling infrastructure —
_build_ci_hamiltonian(H_det),_build_ci_orbital_coupling_energy_fd(H_oc), CI-eliminated Hessian (H_eff). Makes <3% difference for H2O; H_eff can have negative eigenvalues (saddle-point character in full orbital+CI space).Tests:
tests/test_casscf_gradient.py(6→10 tests, +SA +optimization).Fixed:
_run_molecular_scfand analytic-path closures silently droppedcasci_options/cas_referenceon the casscf branch — an SA-CASSCF optimization withcas_reference="uno"or acasci_optionssolver override would have walked the default surface per geometry step while the final single-point used the requested options.
Added: VV10 nonlocal correlation (catch-up Tier 3 #5; un-gates ωB97X-V) (2026-06-20)¶
The VV10 nonlocal-correlation functional (Vydrov & Van Voorhis, J. Chem. Phys. 133, 244103 (2010)) is now evaluated — previously it existed only as a comment in
cpp/src/xc.cppand was never computed. libxc supplies the semilocal part of a VV10-paired functional and the(b, C)parameters viaxc_nlc_coef, but not the nonlocal double-grid integral;cpp/src/vv10.cpp(compute_vv10) is that evaluator — energy + self-consistentv_ρ/v_σpotential, folded into the RKS and UKSV_xcon the same grid each SCF iteration.xc="wb97x-v"is now the complete ωB97X-V functional. It previously silently omitted VV10 (resolved to a libxc id but returned the semilocal+RSH energy only — an ~8 mHa error on H₂O).Functionalnow exposesneeds_vv10/vv10_b/vv10_C, and any functional libxc flags withXC_FLAGS_VV10gets the nonlocal term automatically.New
xc="vv10"alias — the original standalone VV10 functional (libxcXC_GGA_XC_VV10, b = 5.9, C = 0.0093).vibeqc.compute_vv10(points, weights, rho, sigma, b, C)exposes the kernel for direct validation.Validated three ways (
tests/test_vv10.py): the kernel reproduces PySCF’s reference_vv10nlc(energy + both potential channels) on a shared grid to ~1e-8; the analytic potential is the finite-difference derivative of the energy; and the end-to-end self-consistent ωB97X-V total matches PySCF (nlc="VV10") to sub-µHa on H₂O / cc-pVDZ. Citation route[routes.functionals]."vv10"added (vydrov_vanvoorhis_vv10_2010).Note: the nonlocal integral is O(N_grid²) on the main XC grid and runs each SCF iteration; fine for molecular work, a dedicated coarse VV10 grid is a documented future optimisation.
Added: ωB97M-V meta-GGA hybrid; ωB97M(2) gated (catch-up Tier 3 #6) (2026-06-20)¶
xc="wb97m-v"(ωB97M-V; Mardirossian & Head-Gordon, J. Chem. Phys. 144, 214110 (2016)) — the range-separated meta-GGA hybrid with VV10 nonlocal correlation, the most accurate ωB97-V functional. It composes all three advanced XC machineries at once: the erf-attenuated range-separated exchange, the τ-dependent meta-GGA Kohn-Sham path, and the VV10 nonlocal term (now that VV10 has landed). Validated end-to-end against PySCF (nlc="VV10") to grid accuracy on H2O / cc-pVDZ (tests/test_wb97m_v.py); citation route added (mardirossian_wb97mv_2016+vydrov_vanvoorhis_vv10_2010).ωB97M(2) is gated, not shipped. Unlike the other double hybrids, ωB97M(2) (Mardirossian-Head-Gordon 2018) is not a composition of stock libxc components: its SCF base is a re-optimised B97M power-series functional that libxc 7.0.0 does not provide.
xc="wb97m(2)"raises a specific message pointing to ωB97M-V and explaining that the double hybrid needs a dedicated from-scratch meta-GGA kernel — rather than the generic “unknown name”.ωB97M(2) building blocks (infrastructure, no user-facing functional yet). The from-scratch generic B97M-form semilocal kernel landed and is validated to machine precision against libxc’s ωB97M-V (feed ωB97M-V’s coefficients → reproduce its energy density to 4.4e-16 and its v_ρ/v_σ/v_τ potential to ~1e-9):
vibeqc.compute_b97m_semilocal_exc(energy) andcompute_b97m_semilocal_vxc(potential),cpp/src/b97m_energy.cpp,tests/test_b97m_kernel.py. These are the correctness-critical pieces of the self-consistent ωB97M(2) variant (maintainer’s choice over the published xDH form). The remaining work is the self-consistent SCF driverPT2 on the converged orbitals; until it lands,
xc="wb97m(2)"stays gated and these kernels have no SCF consumer (tested building blocks only). Full spec + recipe:HANDOVER_WB97M2.md. Tracked as a follow-up.
Added: revDSD-PBEP86-D4 double hybrid (catch-up Tier 1 #5 / Tier 3 #7) (2026-06-20)¶
xc="revdsd-pbep86"now resolves and computes (previously threwFunctional: unknown name— the surface existed only as a D4-parameter row + citation route). It is the GMTKN55-retrained revision of DSD-PBEP86 (Santra, Sylvetsky & Martin, J. Phys. Chem. A 123, 5129 (2019)): the D4 linear fit — 0.69·HF + 0.31·PBE-X + 0.4210·P86-C for the SCF piece, c_os = 0.5922, c_ss = 0.0636 for the MP2 correction (Table 4). The no-dash aliasrevdsdpbep86resolves identically. The -D3BJ member uses a different XC/MP2 fit and is deliberately not reachable through this alias.run_revdsd_pbep86(mol, basis, ...)is the named dispatcher (thin wrapper overrun_double_hybrid);dispersion="d4"folds in the Caldeweyher-Bannwarth-Grimme D4 term (damping s6 = 0.5132, a1 = 0.44, a2 = 3.60, read fromdispersion_d4_parametersviarevdsdpbep86) for the published revDSD-PBEP86-D4 total.Validated against PySCF (hand-rolled hybrid SCF + scaled DF-MP2 from the identical primitive XC pieces) to grid accuracy on H2O / cc-pVDZ (
tests/test_revdsd_pbep86.py). Citation routes added under[routes.functionals]+[routes.methods](santra_martin_revdsd_2019).
Changed: DLPNO-CCSD(T) pair screening (tcut_pairs) default-on (2026-06-20)¶
LocalCCSDOptions.tcut_pairsnow defaults to1e-4(was0), matching ORCA’sTCutPairs. A pair whose full-virtual MP2 energy is below the threshold is treated at MP2 level (its exact pair energy added directly) instead of being iterated at truncated-PNO CCSD. For a genuinely weak pair the CCSD correction is negligible and full-virtual MP2 has no PNO-truncation error, so screening is accuracy-neutral while removing that pair’s CCSD cost.Calibrated on the 7-mol def2-SVP benchmark (all-electron, vs canonical CCSD(T)): a no-op on the 5-occupied molecules (H₂O/NH₃/CH₄/HF screen 0 pairs) and the weak ~10-14 % tail on larger ones (CO/N₂ 10.7 %, H₂CO 13.9 %); aggregate MAE 0.369 vs 0.368 for
tcut_pairs=0(identical, max error unchanged at 0.84 kcal/mol). On extended systems it is the linear-scaling lever: two waters 8 Å apart screen all 25 inter-fragment pairs (45 %) for a-0.0006kcal/mol change and ~2x speedup.3e-4begins to cost accuracy (MAE 0.379),1e-3cuts real short-range pairs (MAE 0.496). Settcut_pairs=0for the no-screening exactness reference (the parity ratchets pin it).Coverage:
tests/test_dlpno_ccsd_solver.py::TestPairScreening(separated-fragment losslessness, monotone screening count) +::TestPairScreeningAccuracy(N₂ default accuracy-neutral). Exactness ratchets pintcut_pairs=0explicitly, mirroring thecoupling_radiusdefault-on precedent.Docs corrected to the honest shipped default. The benchmark, the DLPNO tutorial, and the user guide claimed
tcut_pno=1e-8/ MAE 0.17 as the default; the actual default istcut_pno=1e-7/ MAE 0.37 (the1e-8value was reverted in an earlier fix without updating the prose). The ORCA-matching 0.17 recipe (tcut_pno=1e-8) is documented as the high-accuracy option.
Added: rigorous 2D (slab) Ewald energy primitive — foundation for CoulombMethod.SLAB_EWALD_2D (2026-06-20)¶
vibeqc.ewald_2d_point_charge_energy(lattice, positions_cart, charges, options)— the exact 2D-periodic (slab) Madelung energy of Parry (Surf. Sci. 49, 433 (1975)) and de Leeuw & Perram (Mol. Phys. 37, 1313 (1979)): real-space + reciprocal (g≠0) + the specialg=0slab term + self. The true 2D lattice sum — no vacuum-gap parameter and no spurious inter-image dipole field along the normal — for a system periodic in lattice columns 0,1 and finite along the normal. Requires a charge-neutral cell (a net-charged periodic plane diverges); raisesValueErrorotherwise.cpp/src/ewald.cpp+cpp/include/vibeqc/ewald.hpp.vibeqc.ewald_2d_point_charge_potential(...)(M2a) — the matching 2D (slab) Ewald electrostatic potential at arbitrary points (real-space + 2D reciprocalg≠0+g=0term,include_short_rangeflag). The long-range building block for the electron-nuclear attraction in a slab SCF. Validated by the exact functional-partner identityE = ½ Σ_i q_i V(r_i) + E_selfagainst the energy summer, plus α-invariance and 3D-vacuum agreement of potential differences.The SCF dispatch is still unchanged (gated). Selecting
CoulombMethod.SLAB_EWALD_2Don the periodic SCF drivers still raisesNotImplementedError; the electronic 2D-Ewald J/K coupling that un-gates a self-consistent slab run is the next milestone (HANDOVER_SLAB_EWALD_2D.md).NEUTRALIZED_1D(1D wire) is unchanged. Workaround for 1D/2D systems today:CoulombMethod.DIRECT_TRUNCATED.Closes roadmap-catchup audit Tier 1 #2 / Tier 4 #6 (
docs/roadmap_catchup_audit_2026_06.md): the maintainer chose implement the rigorous method over removing the enum.Citations:
parry_2d_ewald_1975+de_leeuw_perram_2d_ewald_1979added to the database with aroutes.methods.slab_ewald_2droute. Tests:tests/test_ewald_2d_slab.py(α-invariance, 3D-vacuum limit, 2D square Madelung constant 1.6155426267, neutrality guard, geometric invariances, citation route).
Fixed — vq: vq overview daemon health no longer false-FAILs on macOS (2026-06-20)¶
A healthy local daemon on macOS read as
daemon: FAIL (pidfile).vq overview/vq daemon healthderive the daemon verdict fromlifecycle.verify_user_systemd_contract(), whose four-source check is Linux-systemd-specific (loginctl,pgrep 'systemd --user',systemctl --user, the pidfile). On macOS none of those exist, so two probes returnedFAILand flipped the verdict toFAILeven though the daemon was demonstrably serving —vq queue/vq programsanswered over the RPC socket and jobs dispatched normally. The muspelheim guarded node (a documented exception precisely because those controls are Linux-only) hit this every time.The verdict now falls back to RPC-socket liveness when no systemd-user manager is present. When all three systemd signals are absent (no
loginctl, nosystemd --user,systemctl --userunreachable),verify_user_systemd_contract()skips the inapplicable four-source contract and reportsok=Trueiff the daemon answerspingon<state_root>/daemon.sock— the same signalvq queue/ dispatch use — carrying the pidfile pid through for display. A healthy macOS node now reportsdaemon: OK ... (pidfile). Linux is unchanged: a real host always has aloginctlState + manager PID, so it keeps the full contract, and the orphan/zombie incidents stillFAILcorrectly. Mirrors the STATUS-1 (v0.8.18) precedent of trusting daemon liveness — not a Linux-only mechanism — as the source of truth.Regression coverage in
tests/test_lifecycle.py(TestVerifyContractRpcFallback: responsive→OK, unresponsive→FAIL, complete verdict surface, drain pass-through) +tests/test_overview.py(thedaemon: OK ... (pidfile)render). Docs:docs/lifecycle.md§ “Hosts without systemd-user”.
Fixed — vq: a stale admin-update marker no longer wedges dispatch (2026-06-18)¶
A dead
vq admin updatemarker silently parked the queue. The in-progress marker (<state_root>/admin-update-in-progress) gates the daemon’s dispatch loop, but nothing checked that the writing process was still alive: a marker left by a killed update — or one whose host rebooted mid-update, since the marker persists in the state dir — held every submitted job atpendingindefinitely while the daemon was otherwise healthy and the host reported plainup/OK. Hit saturn and maru on 2026-06-18 (apid=66408marker, 5 days stale, survived a reboot;vq admin clear-update-marker -yun-wedged it instantly).The daemon now reaps a stale marker — at startup and on every dispatch tick — when the recorded
pidis gone, the pid was recycled (a newpid_start_time/procfingerprint catches the post-reboot case), or the marker is older than 24h; it then resumes dispatch. A live update (pid alive + young, including the brief daemon-restart window) is never reaped and still correctly holds the queue.admin.admin_update_marker_stale_reasonis the shared predicate.vq overviewsurfaces a lingering stale marker with a loud⚠ STALEline + anadmin_marker_stale_reasonJSON field — it only lingers when a host’s daemon is down and so can’t self-reap — instead of reporting plainOK.Regression coverage in
tests/test_admin_stale_marker_v0_11_0.py(dead / recycled / aged predicate, daemon reap-and-dispatch, live-marker-kept, overview surfacing) +tests/test_daemon.py::TestMarkerPidLivenessCheckupdated to the reap behaviour. Operator guidance:docs/operations.md§ “Stale markers are auto-reaped”.
Added: AUTO k-point recommender auto-read in periodic SCF drivers (Phase K8-E) (2026-06-19)¶
run_periodic_jobandrun_{rhf,rks}_periodic_scfnow auto-read.smearingand.bz_integrationfrom aKPoints.recommend()result when the user passes the recommended spec askpoints=. Explicit user args (smearing=,bz_integration=) always win — mirrors theperiodic_convergence_auto“explicit wins” contract.New
bz_integrationparameter onrun_periodic_job,run_rhf_periodic_scf, andrun_rks_periodic_scf:None/"smearing"(temperature broadening, the default) or"gilat"(parameter-free Gilat-Raubenheimer net, T=0). Validated early; forwarded to the multi-k Ewald drivers. GDF / BIPOLE paths raiseNotImplementedErrorfor"gilat"until downstream wiring lands.Tests:
tests/test_kpoints_recommend.py(+10 cases) — smearing / bz_integration metadata population, dispatch-wire acceptance, bad-value rejection, gilat-on-GDF guard, recommended-KPoints passthrough throughrun_periodic_job.
Added: AUTO k-point recommender classifies from prior SCF result (Phase K8-B) (2026-06-19)¶
New
scf_resultparameter onKPoints.recommend()— passes a converged periodic SCF result (duck-typedmo_energiesper-k eigenvalues). The indirect HOMO-LUMO gap is computed viaconverged_gap_hartree(reuses the existing function fromperiodic_convergence_auto) and classified by the same gap-cutoff thresholds. Resolution priority step 3 — fires only when neitherband_gapnoris_metalis given, and before theclassifierhook. No extra SCF cost.Lazy-imports the gap helper to avoid a module-load cycle.
Tests:
tests/test_kpoints_recommend.py(+6 cases) — insulator / metal / small-gap classification, explicit-hints-win, unusable-result fallback, multi-k indirect gap.
Added: AUTO k-point recommender pre-SCF classifier (Phase K8-C) (2026-06-19)¶
New built-in
classifier="pre-scf"option onKPoints.recommend()— runs a cheap few-cycle Gamma-only GDF SCF with the user-suppliedbasis, extracts the HOMO-LUMO gap, and classifies by the same thresholds. Explicitly gated behind the string opt-in (spends an SCF — never the silent default). Requiresbasis=; warns and falls back to SAFE metal when not supplied. Lazy-imports the GDF driver to avoid couplingkpoints.pyto the SCF stack.Tests:
tests/test_kpoints_recommend.py(+4 cases) — insulator classification, missing-basis warning, explicit-hints-win, bad-string guard.
Added: Methfessel-Paxton & Marzari-Vanderbilt smearing (M6/M7); AUTO defaults metals to MP (Phase K8-D) (2026-06-20)¶
Implemented
methfessel_paxton_occupations_per_k(orders N=1,2) andmarzari_vanderbilt_occupations_per_k(cold smearing) invibeqc.smearing— same contract as the Fermi-Dirac module ((occ_per_k, mu, entropy), supportsspin_degeneracy).Wired into
apply_smearingandapply_smearing_open_shellvia flavor dispatch — no driver-side changes needed.KPoints.recommend()now defaults metals and small-gap systems to"methfessel-paxton"(previously"fermi-dirac"). MP N=1 accelerates k-point convergence for metals by cancelling the leading integration error term with negative-occupation tails.marzari-vanderbiltis selectable viasmearing_method=.run_periodic_jobnow accepts all three smearing flavors.Tests:
tests/test_kpoints_recommend.py(+4 cases) — MP/MV construction + application + electron conservation, mp_order validation, AUTO-metal-defaults-to-MP.
Added: bundled ML k-spacing predictor for AUTO mode (Phase K8-F) (2026-06-20)¶
KPoints.recommend(predictor="ml")now auto-loads a bundled random-forest regressor whenVIBEQC_ML_KPOINTS=1is set (the model is never auto-loaded silently – the gate env var is mandatory). Without the gate,predictor="ml"raisesNotImplementedErroras before;ml_predictor=<callable>user-supplied models bypass the gate entirely.Model: 100-tree scikit-learn
RandomForestRegressor, 11 features (n_atoms, cell_volume, reciprocal lengths, band_gap, character, n_electrons, dim), trained on 600 synthetic points encoding the Choudhary-Tavazza 2020 scaling laws (metals -> denser, insulators -> coarser, larger cells -> coarser). R2=0.987, MAE=0.016 A-1, ensemble sigma=0.025 A-1. Shipped aspython/vibeqc/data_library/k8f_predictor.pkl(1.4 MB).python/vibeqc/data_library/ml_kpredictor.py–KSpacingPredictorclass (lazy-loads scikit-learn),build_ml_predictor()factory,is_ml_predictor_enabled()gate, and a CLI training script for regenerating the.pkl.Optional dependency:
[ml]extra inpyproject.toml->scikit-learn>=1.3. The model training data is derived from JARVIS-DFT heuristics (CC-BY-4.0, Choudhary et al., npj Comput. Mater. 6, 173, 2020).Citations:
choudhary_tavazza_2020+jarvis_dftdatabase entries withroutes.softwareandroutes.methodswiring.Citation routing:
uses_ml_kpredictorflag onKPoints(stamped byrecommend(predictor="ml")when the bundled model fires) is read by the periodic runner and forwarded toCitationDatabase.assemble(), firingroutes.methods.ml_kpoints->choudhary_tavazza_2020in the.bibtex/.referencesoutput.Tests:
tests/test_ml_kpredictor.py(30 tests) – feature extraction, env gate, prediction shape/range, metal-vs-insulator ordering, integration smoke throughKPoints.recommend(), round-trip serialisation.
Added: DLPNO-(T1), the scaling-preserving exact (T) (tasks #20 + #21) (2026-06-19)¶
DLPNO-(T1) (
vibeqc.dlpno.triples_local.t1_triples_correction): the off-diagonal localised occupied Fock is restored iteratively via Jacobi diagonalisation off_oo; the eigenvalues replacef_ddin the triples denominators and the eigenvectors rotate the amplitudes from the LMO to the canonical occupied basis, removing the ~0.1 kcal/mol (T0) semicanonical error while keeping TNO-domain scaling (Guo, Riplinger et al., J. Chem. Phys. 148, 011101 (2018)). Full-domain (T1) ==exact_triples_correctionto 1.6e-17 Ha (the (T1) parity ratchet). WireLocalCCSDOptions.triples_mode="t1".Default
tcut_pnotightened 1e-7 → 1e-8 — removes the CCSD over-recovery at the loose threshold (e.g. CH4 −0.61 → −0.07 kcal).Default
triples_modechanged"local"→"t1"— the ORCA-matching recipe is now the zero-knob default: MAE 0.17 kcal/mol vs canonical CCSD(T) on a 7-molecule def2-SVP set (ORCA 6.1: 0.16). The old 1e-7 / (T0) defaults remain available via explicitLocalCCSDOptions(tcut_pno=1e-7, triples_mode="local").Citation database:
guo_dlpno_t1_2018entry + route underdlpno-ccsd(t).Tests:
TestT1(4 ratchet tests) intests/test_dlpno_triples_local.py; all 42 DLPNO tests green.Docs + examples updated for the new defaults.
Added: periodic basis-gradient Pulay assembly (build-independent, Phase P0) (2026-06-18)¶
periodic_energy_gradient_fd— the build-independent per-k Pulay assembly for the eventual periodic (k-point) analytic basis-optimisation gradient, the CRYSTAL OPTBASIS target. The BZ sum of the molecular frozen-density expression with complex per-k matrices:dE/dη = Σ_k w_k Re[tr(P(k)·∂Hcore(k)) + ½ tr(P(k)·∂G(k)) − tr(W(k)·∂S(k))], W(k)=½P(k)F(k)P(k). The integral source is an injectedPeriodicIntegralProvider(the Bloch-summed integral derivatives / GDF-Ewald Coulomb derivative / per-k P(k),F(k) exposure are the later phases). Validated build-free against a multi-k mock (per-k Hermitian generalized eigenproblem total-energy FD):test_periodic_energy_gradient_assembly.py.Scoping/design in
docs/basisset_dev/PERIODIC_GRADIENT_DESIGN.md— reusable molecular pieces, the existing periodic nuclear-gradient machinery to build on, the hard parts (P1 Bloch-summed 1e derivatives, P2 GDF/Ewald Coulomb derivative, P3 periodic XC, P4 per-k matrix exposure), and the validation plan.
Added: periodic GDF analytic gradient — C++ 2c/3c lattice gradient kernels + DF J/K component gradients (2026-06-18)¶
cpp/include/vibeqc/aux_eri.hpp/cpp/src/aux_eri.cpp— newcompute_2c_eri_lattice_gradient_weightedandcompute_3c_eri_lattice_gradient_weighted: lattice-summed 2-centre metric and 3-centre ERI nuclear-derivative kernels (libintderiv_order=1with outer loop over direct-lattice cells; image-centre derivatives mapped back onto unit-cell atoms).cpp/src/bindings.cpp— Python bindings for both kernels.python/vibeqc/periodic_gdf_gradient.py— new GDF analytic gradient module withcompute_gdf_gradient_rhf_gamma. Ships the 1-electron (nuclear / kinetic / V_ne Pulay + overlap-Lagrangian) and 2-electron DF J/K gradient for the compcell GDF path at Γ. J and K gradient formulas validated against component-level FD to machine precision (1e-7 Ha/bohr on H2/STO-3G/12-bohr).tests/test_periodic_gdf_gradient.py— 7 tests: C++ kernel shape + Newton’s-3rd-law sanity, DF J gradient vs FD (all 6 atom×dir components), DF K gradient vs FD (one component).Status: the C++ kernels and J/K component gradients are validated; the full SCF gradient (including orbital relaxation / overlap Lagrangian) carries a residual and is tracked for a follow-up increment (Increment 2 / 3 — rsgdf + multi-k generalisation). The gradient module is NOT wired into
run_pbc_gdf_rhfor the runner yet.
Fixed: GDF gradient gauge mismatch — full SCF gradient now matches FD (2026-06-20)¶
Root cause: the C++
nuclear_lattice_gradient_contributionignoresEWALD_3Dand usesDIRECT_TRUNCATED, while the SCF usescompute_nuclear_lattice_dispatch→ FT-based Ewald V_ne. This caused a 0.618 Ha/bohr gauge mismatch in the V_ne gradient.Fix: replaced
nuclear_lattice_gradient_contributionwith numerical FD of the SCF’s own FT-based Ewald V_ne (compute_v_ne_ewald_3d_ft_gamma). The full-SCF gradient residual dropped from ~0.6 to 0.003 Ha/bohr on H₂/STO-3G/12-bohr.python/vibeqc/periodic_v_ne_gradient.py— partial analytic FT-based V_ne gradient (V_short vianuclear_erfc_lattice_gradient_contribution+ V_long structure-factor derivative). AO-pair FT derivative tracked for the next increment.python/vibeqc/periodic_gdf_gradient.py— addedcompute_gdf_gradient()convenience function (auto gauge/cache/Madelung setup). Gradient auto-computed afterrun_pbc_gdf_rhfconvergence and stored onresult.gradient.tests/test_periodic_gdf_gradient.py— 8 tests including new end-to-endtest_full_scf_gradient_vs_fd_h2(analytic vs central-difference FD, 10 mHa/bohr tolerance). All green.vibeqc.compute_gdf_gradientexported at the top level.
Extended: GPAW PW-limit reference — main-group benchmark set + cutoff-convergence (2026-06-18)¶
examples/regression/pw_limit_atomization/systems.py— added 19 non-magnetic main-group cubic compounds (MAIN_GROUP_SET): AgCl, AlAs, AlN, AlP, BaO, BaS, CaF₂, CaO, CdSe, CsCl, GaAs, GaN, GaP, InAs, KBr, KCl, Li₂O, SiC, ZnS. All geometries from the r2SCAN-optimised CRYSTAL.d12inputs inreferences-cohesive/. System selector--systemsnow acceptsmain-groupandnonmagnetic(validation + main-group). Hund magmoms added for Ag, Cd, Zn, In, Cs, Ba.BY_IDcovers 28 compounds.pw_reference.py— addedcheck_element_convergence(): per-element free-atom cutoff-convergence audit (compares two highest cutoffs, reports Δ in kJ/mol).run_pw_reference.py— added--converge-cutoffflag: runs element audit before atomization and warns if any element needs higher cutoff._run_external()now explicitly inheritsos.environsoPYTHONPATHand threading controls reach the GPAW worker subprocess.compare_pwref.py— three-way comparison script: reads GPAW JSON sidecar +r2scan.dat, produces GPAW-vs-pob-vs-VASP table with per-system outlier identification (pairwise agreement method).PwSystemnow supports non-cubic structures via optionalcfield andlattice_kwargsproperty; GPAW worker updated to passcto ASE bulk builder. Placeholder TM prototype (TiO₂-r rutile) inTM_PROTOTYPES. Added Ti (Z=22) toHUND_MAGMOM(magmom=2, S=1) — marked experimental (3d TMs not in vibe-qc’s_GROUND_STATE_MULTIPLICITY).Tests updated for expanded system set.
Added: UKS (open-shell DFT) analytic basis-optimisation energy gradient (2026-06-18)¶
energy_gradient_analytic_uksextends the analytic basis-parameter gradient to open-shell Kohn-Sham — the spin-polarised counterpart ofenergy_gradient_analytic_rks. Combines the UHF per-spin frozen-density Pulay assembly (W_σ = P_σ·F_σ·P_σ,G_σ = J(P_tot) − a·K(P_σ)) with the polarised grid XC termΣ_g w_g [v_ρα ∂ρ_α + v_ρβ ∂ρ_β + 2 v_σαα ∇ρ_α·∂∇ρ_α + 2 v_σββ ∇ρ_β·∂∇ρ_β + v_σαβ(∇ρ_α·∂∇ρ_β + ∇ρ_β·∂∇ρ_α)]. Reaches the open-shell atoms (C, N, O, …) at DFT level — the combination the pob recipe needs. The spin-independent on-grid∂φ_μ/∂ηis shared with the RKS path (_param_dphi_on_grid). LDA/GGA/hybrid; meta-GGA / range-sep / double-hybrid raise.make_uks_native_objective_gradient(andmake_native_objective_gradient(functional=…, open_shell=True)) wire it into BDIIS.This completes the molecular analytic basis-optimisation gradient: RHF, UHF, RKS, UKS × exponents + coefficients, + κ/ln-condition penalty, all wired into
optimize_bdiis. Validated vs full-energy FD on C ³P / N ⁴S (PBE, B3LYP), off-reference and valence params; end-to-end PBE-UKS BDIIS converges on the C ³P atom:test_energy_gradient_uks.py,test_bdiis_analytic_optimize.py.
Fixed: multi-k GDF Coulomb-J over-binding on tight cells (2026-06-18)¶
The multi-k GDF Coulomb (Hartree) build conjugated the diagonal density-fit
tensor L(k,k) when assembling the BZ-summed fitted density
ρ_P = Σ_k w_k tr(L(k,k)·D(k)) (run_krhf/kuhf_periodic_gdf._build_j_from_lpq).
The conjugate is a no-op for the real cderi of vacuum-box / cubic cells — so
every cubic insulator, including the H₂ (2,1,1) = −1.12013988 gate, was correct
— but it mis-contracts the Coulomb for the genuinely complex diagonal cderi of
tight cells with inter-cell (R≠0) overlap, over-binding E_J by Madelung scale.
On a uniform H-chain metal (sto-3g, 14×14×2.8 bohr, (1,1,8) + Fermi smearing)
the free energy was 0.54 Ha too low (−1.507 vs PySCF −0.963); a gapped
(dimerized) chain in the same cell was 0.16 Ha too low — so it was neither
metal- nor exxdiv-specific (exxdiv-K, the fractional-occupation energy
expression, E_1e and E_nuc all matched PySCF to <0.3 mHa; the cderi itself
matched PySCF’s to ~1e-6). Dropping the spurious .conj() reproduces PySCF
get_j_kpts exactly: metal (1,1,8) free energy now −0.96320158 (= PySCF to
3e-9), H₂ (2,1,1) gate unchanged (2e-9). The strict-xfail
test_metallic_chain_krhf_smear_matches_pyscf is flipped to a passing
regression. (Coarse non-converged metal meshes that sample an exact
zone-boundary degeneracy — e.g. (1,1,4) — can still pick a different stationary
point than PySCF; that is a k-sampling artifact, not the J build, which is now
machine-exact. Use a converged mesh for metals.)
Added: fractional-occupation (smeared) BIPOLE analytic gradient — corrected gauge (2026-06-18)¶
Finite-temperature / Fermi-Dirac-smeared KS analytic gradients now work in the
corrected (Ewald-exchange-split) gauge for compute_bipole_gradient_rks and
compute_bipole_gradient_uks, at Γ and multi-k. At the smeared SCF minimum the
energy is the Mermin free energy A = E − T·S, which is stationary w.r.t. the
occupations, so dA/dR is the standard Hellmann–Feynman + Pulay expression with
the fractional density D and the fractional energy-weighted density
W = Σ_i f_i ε_i C_iC_i† — no occupation-response term, and (the corrected gauge
being variational) no CPHF. FD-validated against compute_bipole_gradient_fd on
genuinely-fractional smeared H₂/STO-3G (worst occupation ~0.11 from integer):
RKS Γ 1.9e-8, multi-k RKS 4.2e-9 Ha/bohr. The legacy
(use_exchange_ewald_split=False) gauge still rejects fractional occupations.
Production forces remain the exact FD path.
Added: meta-GGA τ-Pulay in periodic XC gradient — RKS + UKS (2026-06-20)¶
The meta-GGA τ-Pulay atomic-gradient term — the analytic derivative of the
kinetic-energy-density contribution to V_xc — is landed in the shared periodic
XC gradient kernels xc_lattice_gradient_contribution and
xc_lattice_gradient_contribution_uks (cpp/src/periodic_xc.cpp). The
term is validated to 1e-9 against an independent Python frozen-potential
reimplementation. SCAN and r2SCAN produce physically-reasonable gradients;
TPSS and M06-L carry known residual blowup (~4–20 Ha/bohr on H₂) from
pre-existing SCF garbage MO eigenvalues — the singular v_xc potentials
contaminate the Fock matrix’s virtual subspace, and the MO-energy clipping
(±10 Ha) in the energy-weighted-density builder only bounds, not cures, the
problem. FD remains the production force path.
Still gated in the analytic preview: multi-k KS-CPHF — legacy-gauge-only
(the corrected-gauge multi-k KS path is variational and needs no CPHF).
Deferral ratified 2026-06-20 (no production need — FD is exact and the
corrected gauge covers multi-k KS analytically; a full multi-k KS-CPHF would
be the first complex k-space coupled-perturbed solver in the codebase, for a
deprecated gauge). See HANDOVER_BIPOLE_GRADIENT.md Case 3 for the scoping.
Performance: restricted-triple loop in closed-shell (T) (2026-06-20)¶
Closed-shell perturbative (T) now sums occupied triples over
i <= j <= kinstead of all ordered(i, j, k)(cpp/src/ccsd.cppcompute_triples). The per-ordered-triple density is not permutation-symmetric in the classwise spin formulation (theaabpattern pins beta to the third slot, so an ordered triple carries only the “beta on k” configuration), but two sub-symmetries survive: the same-spinaaablock is fully symmetric in(i, j, k)and theaabblock is symmetric under exchange of its two alpha slots. Folding the orderings analytically does 1 (aaa) + up to 3 (aab) intermediate builds per unordered triple instead of 2 per ordered triple.~2.6x faster (T) measured on C2H4/def2-SVP (no=8): the (T) step drops 1.21 s to 0.47 s, approaching 3x for larger occupied spaces. E(T) is reproduced bit-for-bit against the ordered loop. Closed-shell only; the spin-orbital UHF / ROHF kernel (
cpp/src/uccsd.cpp) is unchanged.Pinned by the existing always-on anchors (
tests/test_ccsd_anchor.py, which carry occupied triples of every multiplicity class) and the PySCF cross-check (tests/test_ccsd.py).
Added: open-shell UCCSD / UCCSD(T) on a UHF reference (2026-06-17)¶
New spin-orbital unrestricted coupled-cluster kernel (
cpp/src/uccsd.cpp,cpp/include/vibeqc/uccsd.hpp, bound asrun_uccsd/vibeqc.cc.run_uccsd): DF-UCCSD and perturbative (T) on a converged UHF reference. It evaluates the Stanton-Gauss-Watts-Bartlett (1991) working equations directly over spin orbitals (the same equation set the closed-shell kernel spin-integrates), with integrals built from a block-diagonal spin-orbital DF B-tensor (DePrince-Sherrill 2013) and the Raghavachari (1989) (T) evaluated triple-by-triple. Frozen occupied cores supported (same chemical-core count per spin channel).run_job(method="ccsd"/"ccsd(t)")now auto-routes by shell: closed-shell molecules use RHF + the spin-adapted kernel; open-shell molecules (multiplicity > 1) use UHF + the spin-orbital kernel. No new keyword; the result is attached asresult.ccsdwith the sameCCSDResultfields.vq.run_uccsdis exported for the low-level path.Validation (CLAUDE.md §7/§10). A new in-repo spin-orbital anchor
vibeqc.dlpno._ccsd_ref.run_ref_uccsd(the open-shell sibling of the FCI-anchoredrun_ref_ccsd, reusing its residual + (T) code verbatim) matches PySCFcc.UCCSD/UCCSD(T)to < 1e-3 µHa on CH3/NH2/O2 (sto-3g + cc-pVDZ, exact integrals). The C++ kernel is pinned to that anchor (tests/test_uccsd_anchor.py, always-on) and cross-checked out of process vs PySCF DF-UCCSD/(T) (tests/test_uccsd.py, all-electron and frozen-core sub-µHa) and vs cached conventional ORCA 6.1 CCSD(T) on CH3/NH2.Citations (
tests/test_citations.py): theccsd/ccsd(t)routes (Purvis-Bartlett 1982, SGWB 1991, Raghavachari 1989, DePrince-Sherrill 2013) fire for the open-shell path too and are now DOI-pinned mechanically.Docs:
docs/user_guide/ccsd.md+handovers/HANDOVER_CCSD.mddocument the open-shell surface, scope, and the ROHF-reference roadmap item.
Added: ROHF-reference CCSD / CCSD(T) (2026-06-19)¶
ROHF-CCSD wired through the spin-orbital kernel. The C++ UCCSD(T) kernel (
cpp/src/uccsd.cpp) is reference-agnostic — it accepts per-spin MO coefficient and Fock matrices directly, with the occupied/virtual partition following frommolecule.multiplicity. ROHF supplies a single set of spatial orbitals (C_alpha == C_beta) with distinct per-spin Fock matrices (F_alpha != F_beta, different exchange terms), so the same kernel computes ROHF-CCSD with no new equations.New entry points:
vibeqc.cc.run_rohf_ccsd(mol, basis, rohf_result)(Python) andrun_uccsd_from_mos(mol, basis, C_a, C_b, F_a, F_b, e_hf)(C++, bound as_vibeqc_core.run_uccsd_from_mos). The low-levelrun_uccsd_from_mosaccepts explicit MO-coefficient and Fock arrays for any non-UHF reference (ROHF, semicanonical orbitals from other sources).run_job(method="ccsd(t)", ccsd_reference="rohf")selects an ROHF SCF + ROHF-CCSD(T) calculation — spin-pure restricted-open-shell reference, no contamination. The default (ccsd_reference="uhf") keeps the existing UHF + UCCSD(T) path.Validation. Anchor tests (
test_rohf_ccsd_anchor.py, always-on) pin the C++ kernel to the in-repo spin-orbital reference (run_ref_rohf_ccsd) at < 5 µHa on OH / NH2 / O2 (sto-3g). PySCF cross-check (test_rohf_ccsd.py, importorskip) confirms CCSD correlation to < 1 mHa (DF vs conventional + different ROHF implementations).Bug fix. The ROHF density-fitting J/K path (
vibeqc.rohf) carried a broken import (_vibeqc_core.DensityFitting— the class is invibeqc.density_fitting), fixed en passant.Docs:
handovers/HANDOVER_CCSD.mdanddocs/user_guide/ccsd.mdupdated.
Fixed: honest errors for unwired SCF-accelerator / periodic-guess selectors (2026-06-18)¶
Tier-1 surface fixes from the v0.14 roadmap catch-up audit
(docs/roadmap_catchup_audit_2026_06.md) — exposed selectors that raised
deep, inconsistently, or (worst) silently did the wrong thing:
scf_accelerator="anderson"/="broyden"no longer resolve silently to DIIS. They name real-space density mixers not yet wired into the accelerator path, so a user asking for Anderson/Broyden was quietly handed DIIS with no indication.scf_accelerator_from_stringnow raises a clearNotImplementedError— distinct from theValueErrortypo path — naming the usable accelerators (DIIS / KDIIS / EDIIS / EDIIS_DIIS / ADIIS). The periodicAndersonMixerwiring tracked for v0.14 (roadmap D4a) is the follow-up that will make the name resolve to a real mixer.The Python periodic-SCF driver seam (
guess.initial_density_closed_shell/initial_densities_open_shell, which every Python periodic driver routes through) now raises a cleanNotImplementedErrorwhen a molecular-only initial guess (SAP/PATOM/HUECKEL/MINAO) is selected, naming the supported periodic guesses (SAD / HCORE / READ; AUTO resolves to SAD) — dispatch-layer parity with how the unimplementedCoulombMethodvalues already signal. The lower-level C++ Γ drivers continue to raise the GuessEngine’s clearRuntimeError; sinceNotImplementedErrorsubclassesRuntimeError, existingexcept RuntimeErrorhandlers still catch both.For completeness: the exposed-but-unimplemented
CoulombMethod.SLAB_EWALD_2D/NEUTRALIZED_1Dalready raise a clearNotImplementedErrorat the dispatch edge (audit Tier-1 #2/#3) and are unchanged.
Fixed: vibe-view — open fails loudly on a busy port instead of opening the browser at a stale server (2026-06-19)¶
vibe-view openraced on an occupied port: the announce thread connected to whatever was already listening, printed “ready”, and opened the browser at that stale server, while the freshly launched server died withEADDRINUSEburied in the log. The user saw the previous session’s last rendered frame — visible but unresponsive to the mouse (“I can see the molecule but can’t rotate it”).open(andfrom-vq) now pre-check the port and abort with an actionable message (stop the other server, or pass a different--port) before announcing or launching the browser. Tests:tests/test_cli.py::TestPortCollision(3).
CI: vibe-view — install xauth so the viewer test job runs (2026-06-18)¶
The
vibe-view-testjob (pip-only,allow_failure, scoped tovibe-view/**) installedxvfbbut notxauth, soxvfb-run -afailed with “xauth command not found” before pytest ran — the viewer suite had never actually executed in CI (which is why the kind-drift above reachedmainunnoticed). Addedxauthto the job’s apt install.
Fixed: vibe-view — producer↔viewer QVF kind drift + forward-compat reader tests (2026-06-18)¶
The producer promoted six QVF kinds —
volume.potential,volume.rdg,fermi_surface,phonon_bands,phonon_dos,equation_of_state— from reserved to implemented (with schema branches) without matching viewer renderers, so the viewer’s kind-drift guard broke and a file carrying one would have been mislabeled. The viewer now declares them invibeview.kinds.DEFERRED_KINDSand surfaces them as “skipped, not yet rendered” (honest, distinct from “unsupported”); such a file opens and the section is skipped rather than rejected. Dedicated renderers for the six remain planned follow-up work.tests/test_kind_drift.pynow accounts for deferred kinds (a new unaccounted-for writer kind still trips the guard), andtests/test_reader_robustness.pyderives a still-schema-unknown kind for its forward-compat checks instead of the now-promotedfermi_surface/phonon_bands.
Added: vibe-view — OSPRay path-traced rendering for publication output (2026-06-09)¶
vibe-view/src/vibeview/raytrace.py: aRaytraceEngineover VTK’s OSPRay path tracer (quality presets, PBR materials, environment lighting, optional depth-of-field, progress callback) with arender_high_quality(...)entry point and graceful fallback when the local VTK build has no OSPRay. Wired into the viewer’s render/export path; covered byvibe-view/tests/test_raytrace.py.
Added: vibe-view — interaction + export polish (2026-06-09)¶
Browser-native
.qvffile picker (hidden<input type=file>+ FileReader, reliable across Trame versions), screenshot and OBJ/glTF export saved next to the QVF and offered as a browser download, an MO show/hide toggle on the wavefunction card, a resizable bottom (chart) panel, app-bar tooltips, and camera/export state-initialisation fixes.
Fixed: GAPW DFT XC-augmentation double-counting (300 mHa over-binding) + GGA crash (2026-06-18)¶
The crash (any GGA/meta-GGA all-electron GAPW).
run_periodic_rks_gapw(..., functional="pbe")raisedValueError: operands could not be broadcast together with shapes (n_r,n_ang) (n_r,n_ang,3)inGapwAugmentation.compute_xc_correction: the scalar GGAv_sigmawas multiplied by ∇ρ without a broadcast axis. Fixed withv_sigma[..., None]in both the restricted and the polarised σ-flux terms, mirroring the GPW path (periodic_gapw_j.py:_project_vxc_to_ao).The deeper bug (every GAPW DFT functional, LDA and GGA). Once the crash was cleared, GAPW DFT over-bound by ~300 mHa on He/STO-3G (LDA −301, PBE −318 mHa), independent of grid. Root cause: the smooth-grid XC energy/potential was generated from the full AO density while the per-atom augmentation subtracts a soft (pseudo) local term — so the soft core was double-counted inside each augmentation sphere (the smooth term already held ε_xc[full], then the augmentation added ε_xc[hard]−ε_xc[soft] on top). The soft/hard partition landed for the Hartree J in
1ec5d914but the XC smooth term was never switched over.The fix. The smooth-grid XC term is now generated from the soft density (new
GapwJBuilder._smooth_xc_generation), exactly asbuild_Jdoes for the Hartree term, so the per-atom XC augmentation telescopes to the all-electron XC; the potential is still projected onto the full AO basis. He/STO-3G now matches vibe-qc’s molecular all-electron reference to <1 mHa (LDA −0.78, PBE −0.94 — the residual is the Hartree augmentation’s pre-existing accuracy floor), is grid-stable to µHa, and the augmentation correctly rescues a coarse grid (He 24³: smooth GPW +150 mHa → GAPW +0.03 mHa). Regression tests:tests/test_periodic_gapw_augment.py::TestGapwDFTEnergy.Gated (fail-closed) — not yet correct.
meta-GGA GAPW raises
NotImplementedError: the augmentation has no kinetic-energy-density (τ) correction, so the core τ would be silently wrong. Use LDA/GGA with GAPW, or run meta-GGA on the smooth GPW route.Open-shell GAPW DFT (
run_periodic_uks_gapwwith a functional) raisesNotImplementedError: the polarised per-atom XC augmentation energy is unimplemented (the post-loop assembly would call the unpolarised libxc path on a spin-2 functional and crash). Use closed-shellrun_periodic_rks_gapw, open-shellrun_periodic_uhf_gapw(Hartree-Fock), or the smoothrun_periodic_uks_gpwfor open-shell DFT.
Known limitation (tracked, not fixed here). GAPW augmentation spheres overlap for bonded atoms, so the per-atom hard/soft correction double-counts the bond region: H2/STO-3G over-binds ~100 mHa at d=1.4 bohr, scaling with the overlap ratio 2·r_aug/d (→ ~28 mHa at d=6). This is a pre-existing multi-centre defect that also affects shipped GAPW-RHF on molecules (it lives in the Hartree augmentation, not the XC fix above) and needs a partition-of-unity across spheres. Captured as an xfail:
tests/test_periodic_gapw_augment.py::test_gapw_molecule_matches_molecular_xfail.
Added: RKS (DFT) analytic basis-optimisation energy gradient (2026-06-18)¶
energy_gradient_analytic_rksextends the analytic basis-parameter gradient to closed-shell Kohn-Sham — LDA, GGA, and global hybrids. The Coulomb (and hybrid exact-exchange fraction) two-electron term + the Pulay −tr(W·∂S) term reuse the closed-form integral derivatives; the new piece is the explicit exchange-correlation term on the DFT grid,∂E_xc/∂η = Σ_g w_g [v_ρ ∂ρ/∂η + 2 v_σ ∇ρ·∂∇ρ/∂η], with ∂φ_μ/∂η evaluated on the grid (prim-q collocation for coefficients;(∂lnN_c/∂α)·φ_μ + c̃_q·((2l+3)/4α − r²)·G_qfor exponents). The grid matchesrun_rks’s (build_grid(mol, opts.grid)), so the gradient is consistent with the SCF energy the optimiser minimises. Meta-GGA / range-separated / double-hybrid raise.make_rks_native_objective_gradient(andmake_native_objective_gradient(functional=…)) wire it into BDIIS.Validated vs full-energy FD on LDA/PBE/B3LYP × exp/coeff (Be), a diffuse exponent evaluated away from the reference basis, and H₂O (multi-element + O d-shell); end-to-end PBE BDIIS converges on Be:
test_energy_gradient_rks.py,test_bdiis_analytic_optimize.py. The exponent ∂lnN_c/∂α uses the current (unpacked-at-x) exponent, not the reference — the reference-vs-current slip is only visible once the optimiser moves x off x0 (passing at x0, wrong for diffuse exponents during optimisation).
Fixed: ASE → PeriodicSystem lattice-vector convention (non-orthogonal cells) (2026-06-17)¶
ASE stores lattice vectors as the rows of
atoms.cell; vibe-qc’sPeriodicSystemstores them as the columns of its lattice matrix (cpp/include/vibeqc/periodic.hpp:32). The ASE→engine boundary helpers passed the cell without transposing, so every non-orthogonal cell entered via ASE was silently transposed — wrong geometry, hence wrong k-paths, bands, forces and energies (VibeQCPeriodic,periodic_forces, and the GPW/GAPW calculators). Orthogonal/cubic cells — and the symmetric FCC/BCC primitive cells used in the test-suite — are transpose-invariant, which is why it went unnoticed (cell volume|det|is transpose-invariant too).Fixed at all three one-way boundaries (
atoms_to_periodic_system,VibeqcGPW/VibeqcGAPW._atoms_to_periodic_system) by transposing the cell to columns. The MACE relaxation path round-trips throughatoms_to_periodic_system, so its input side (MACEModel._ase_atoms) was co-fixed in the same change to avoid double-flipping the relaxed cell (and so MACE sees the correct geometry on non-orthogonal cells). The fix is a no-op on orthogonal/symmetric cells, so existing periodic tests are unchanged.Validated out-of-process against PySCF KRHF/GDF on a hexagonal cell (identical
lattice_vectors(); agreement to 3.05 mHa on the same geometry in the dilute limit —examples/regression/parity_hexagonal_lattice_vs_pyscf.py, CLAUDE.md §10). Regression:tests/test_ase_periodic_lattice_convention.py(column convention at all three boundaries + ASE-vs-engine-native SCF self-parity + rotation invariance).
Fixed: GAPW molecular blow-up — one-centre density restricted to on-centre AOs (2026-06-17)¶
The bug (every multi-atom system). The GAPW per-atom augmentation built the one-centre density n_A¹ from the full AO set on atom A’s unbounded radial grid, so a neighbour’s AO — large on A’s far grid points and amplified by the rˡ multipole weight — detonated the ρ₀ compensator. LiH/STO-3G at d = 11 bohr gave E ≈ +9.8×10⁷ Ha (H |Q|max ≈ 2.3×10⁴); even bonded LiH was +44 Ha. (Isolated atoms — He/Ne/Be — were unaffected: a single centre has no off-centre AOs, which is why the foundational fix’s atom-only validation missed this.)
The fix (Lippert–Hutter–Parrinello 1999, Eq 53; on-centre n_A¹). The one-centre density that sources the augmentation is now restricted to atom-A-centred AOs (new
on_centermasks +_ao_atom_index), and the ρ₀ compensator multipoles are the on-centre (hard−soft) moments computed on the augmentation grid (replacing the unboundedcompute_atom_local_multipoles). The ΔJ / ΔV_xc projection keeps the full AO set (the Fock is full-dimensional).Validation (vs all-electron GDF, same gauge). Bonded LiH/STO-3G: E_GAPW = −7.854 vs E_GDF = −7.862 (+8 mHa); compensator |Q|max 2.3×10⁴ → 0.08. Isolated He/Ne/Be unchanged. Regression-pinned by
tests/test_periodic_gapw_parity.py::test_gapw_molecular_no_blowup_lih.Still experimental / known-limited. Partially-occupied core-bearing atoms (O/C/N/F and most molecules with virtual orbitals) still suffer a core aufbau failure: the augmented Fock pushes the tight 1s orbital above the valence (O: ε₁ₛ ≈ +68 Ha), so the SCF leaves the core unoccupied (O total −35 vs −74 Ha). Filled-(sub)shell atoms (He/Ne) and Li-core molecules are immune. GAPW stays opt-in behind
GAPWExperimentalWarning; GDF/BIPOLE remain the production all-electron routes. (Tracked inHANDOVER_GAPW_PRODUCTION.md.)
Added: MSINDO analytic CIS (TDA) excited-state nuclear gradient (2026-06-17)¶
vibeqc.semiempirical.methods.msindo_cis.cis_gradientnow computes the analytic CIS excited-state gradient (Ha/bohr) via the CPHF Z-vector relaxed density + the CIS two-particle density, contracting the per-pair integral derivatives — the same engine as the ground-state analytic gradient plus the orbital-relaxation response (A·Z = −L). Reproduces the validated finite-differencecis_gradient_fdto ~1e-6 Ha/bohr for singlet and triplet non-degenerate states (closed-shell; open-shell raises). The previous_experimentalgate (the gradient mis-scaled the forces) is removed.Root cause of the prior mis-scaling (milestone M2 / Phase 6): the CIS two-particle density is non-symmetric (its
2·D_mm·P_nnand2·T_mn²terms), so the pair-block gradient contraction must sum the[L_j,K_i]and[K_i,L_j]elements (faithfulkloop.f+lloop.f), not use the2×shortcut that is valid only for the symmetric ground-state density. The transition density is one-sided (cisgrad.f) and the Z-vector Lagrangian was re-derived directly in the MO basis (cphf_solver.f; the triplet drops the transition-density Coulomb terms percislagrange_trip.f).cis_gradient_fdremains the documented, robust fallback (and the state-tracking / conical-intersection paths keep using it, since the per-state analytic gradient is ill-defined at degeneracies). Citation: Foresman–Head-Gordon–Pople–Frisch, J. Phys. Chem. 96, 135 (1992), already wired viaroutes.uses_cis. Tests:tests/test_cis_gradient.py(analytic-vs-FD parity, S1/S2 × singlet/triplet × H₂O/NH₃/H₂CO/HF; translational invariance; open-shell guard).
Added: analytic contraction-coefficient energy gradient for basis optimisation (2026-06-17)¶
The analytic basis-optimisation energy gradient now covers contraction coefficients, not just exponents —
energy_gradient_analytic/energy_gradient_analytic_uhf(and the BDIIS factories) handlefield="coeff"analytically instead of raising. A contracted integral is linear in its primitive coefficients and the SCF energy is invariant to a shell’s contracted normalisation, so only the direct term survives:∂I_μν/∂d_q = (c̃_q/d_q)·⟨G_q|Ô|φ_ν⟩. The primitive-q integral rows⟨G_q|Ô|φ_ν⟩are read off an augmented basis (the full basis plus one extra shell = primitive q alone,coefficients_pre_normalized=True), so no new integral kernel or C++ is needed;c̃_q/d_qis the frozen-renormalisation chain factor. Validated vs full-energy FD (relative step — tiny core coefficients need it) on H₂ and H₂O incl. O’s d shell:test_energy_gradient_analytic.py,test_bdiis_analytic_optimize.py.SP
coeff_s/coeff_premain FD-only (documented; raise in the analytic path).
Added: UHF (open-shell) analytic energy gradient for basis optimisation (2026-06-17)¶
energy_gradient_analytic_uhfextends the analytic basis-parameter energy gradient to unrestricted HF via the spin-resolved frozen-density Pulay assemblydE/dη = tr(P_tot·∂Hcore) + ½[tr(P_α·∂G_α) + tr(P_β·∂G_β)] − tr((W_α+W_β)·∂S), withG_σ = J(P_tot) − K(P_σ)andW_σ = P_σ·F_σ·P_σ(occupation 1 per spin). The four closed-form integral exponent derivatives are spin-independent and reused unchanged; only the density/Fock contraction differs. This reaches the open-shell atoms (C, N, O, …) the pob recipe needs. Validated to ≤1e-7 Ha vs full-energy UHF FD on C (³P, s/p/d), O (³P), N (⁴S):tests/basisset_dev/test_energy_gradient_uhf.py.make_uhf_native_objective_gradient(and the sharedmake_native_objective_gradientwith anopen_shell=switch) gives the in-process UHF(objective, gradient)pair foroptimize_bdiis; end-to-end on the C ³P atom the analytic-gradient loop converges to a stationary, lower-energy basis (test_bdiis_analytic_optimize.py).build_j/build_kare split out ofbuild_gfor the per-spin Coulomb/exchange contractions.
Added: MSINDO CCM analytic nuclear gradient (1-D / 2-D / 3-D, closed-shell) (2026-06-17)¶
vibeqc.semiempirical.methods.msindo_ccm_gradient_analytic.ccm_gradient_analytic(...)— the analytic nuclear gradient (Ha/bohr) of the periodic Cyclic Cluster Model total energy, completing the MSINDO analytic-gradient programme (Phase 5; the molecular analytic gradient, Phase 4, already shipped). The Wigner-Seitz topology is held fixed at the reference geometry (matchingccm_gradient_fd); pair derivatives reusemsindo_pair_deriv; the analytic Madelung/Ewald gradient covers 1-D (finite ±T,±2T point-charge lattice sum), 2-D (Parry/Heyes slab Ewald: reciprocal + K=0 + direct) and 3-D (Ewald bulk). Closed-shell (RHF) only — an open-shell cell raisesNotImplementedError.The Madelung gradient uses a direct, per-neighbour assembly — the exact fixed-charge derivative of the CCM energy — in place of the MSINDO Fortran’s central-atom-only form (
dedmadelsum.f:EDX(IATOM) += DMADEL·q_I), which is exact only for mirror-symmetric Wigner-Seitz cells. For asymmetric ionic Madelung cells (an H-F chain with weight-½ boundary atoms; a distorted MgO(100) slab) the MSINDO oracle’s own analytic gradient deviates from the finite difference of its own energy by up to ~2e-2 Ha/bohr, whereas vibe-qc’s gradient stays exact (matches the FD of its energy to ~1e-7 Ha/bohr).C++-backed (added 2026-06-18):
ccm_gradient_analyticis also in the C++ engine (m_indo.ccm_gradient_analytic,ccm_engine.hpp) — byte-parity with the Python path to ~1e-10 Ha/bohr across 1-D/2-D/3-D × {NOEWALD, Madelung}, reusing the molecular pair-derivative engine (pair_blocks_deriv) and the same direct-assembly Madelung gradient (_dmadkonst_2d/_dmadkonst_3d).Tests: analytic == fixed-WS finite difference to <1e-6 Ha/bohr for 1-D/2-D/3-D × {NOEWALD, Madelung}; analytic == MSINDO oracle (
CARTOPT ANALY GRADONLY, frozen inccm_reference.json) to <2e-5 Ha/bohr on symmetric cells (H-F NOEWALD chain, MgO(100) Madelung slab, distorted MgO Madelung bulk); closed-shell-only and invalid-cluster guards (tests/test_msindo_ccm_gradient.py); and C++↔Python parity for every dim × Madelung intests/test_msindo_cpp.py.
Performance: cache the GPW/GAPW grid collocation across SCF iterations (PBC-audit E3) (2026-06-17)¶
The GPW/GAPW Hartree-J + XC build collocates the AO basis on the FFT grid (
χ_μ(r_g) = Σ_R χ_μ^mol(r_g − R), 27 periodic-imageevaluate_aocalls over the whole grid). Profiling shows this χ build is 95–100 % of a singlecollocate/project— the density contraction and the FFT-Poisson solve are ~1 %. χ depends only on(basis, grid), never on the density, yet the per-iteration XC path rebuilt it every iteration (RKS-GGA ~3×, UKS ~4×).New
GpwCollocationCache(the AO-on-grid table χ + the reciprocal-space G-mesh) built once per SCF viabuild_gpw_collocation_cache(basis, grid)and contracted per iteration — mirroring the EWALD E2 analytic-FT J cache (ewald_composed.EwaldJFTGammaCache) and the in-builderGpwJBuilder._chi_cache. Threaded throughcollocate_density_on_grid,project_potential_to_ao, the XC projectors, and all GPW/GAPW SCF drivers (Γ + multi-k RHF/RKS, UHF/UKS, the GAPWGapwJBuilderand its four drivers).GapwJBuildercaches both the full-basis and the soft-basis χ.Pure caching, no physics change. The cached χ is bit-identical to the inline build (
np.array_equal);cache=Noneeverywhere falls back to the pre-cache path. Pure-DFT energies/densities are bit-identical before↔after; HF/hybrid agree to within the pre-existing ~1e-15 run-to-run floor of the non-associative parallel exact-exchange reduction (unchanged by this work).Measured per-SCF speedups (STO-3G H-chains): GPW RKS-PBE 3.6×, GAPW RKS-LDA 3.0×, UKS-PBE 19× — scaling with how χ-dominated (vs O(n⁴)-exchange-bound) the route is. File-level on the gate’s slow GPW/GAPW targets is 1.1–2.1× (
gapw_open_shell2.1×,gapw_gradient1.5×,ase_periodic_gpw1.4×; the HF-onlygapw_hessianis unaffected — HF skips the XC-collocation path). This reduces the per-file CPU that drove the v0.13.0 contention false reds, but these HF/multi-k/FD-overhead-dominated files do not clear the OMP=2 fast-lane bound, so they stay inknown_slowpending the gate chat’s--jobs 6re-verification (deliberately not de-listed here — see5453f9a9).
Validated: megacell periodic-MP2 thermodynamic limit == PySCF.pbc KMP2 (k-mesh → ∞) (2026-06-19)¶
The headline cross-check for the megacell periodic-correlation route (
python/vibeqc/periodic_megacell_mp2.py): the per-cell MP2 correlation energy frommegacell_mp2_tdl(megacell → ∞, an open-boundary supercell extrapolation) agrees with periodic PySCF.pbc KMP2 (k-mesh → ∞, a BvK torus) — two independent routes, two independent codes, one per-cell thermodynamic limit. On the H₂/STO-3G chain (20-bohr transverse vacuum, 6-bohr spacing) the megacell TDL bulk is −0.013275 Ha/cell, the KMP2 k-mesh series (nk=2,4,6,8 along z) converges to −0.013322 Ha/cell — agreement ~0.05 mHa (< 0.4 % of e_corr), an honest combined finite-size truncation error (megacell ~1/N surface residual vs KMP2 finite-k-mesh convergence, plus ~sub-µHa inter-chain coupling through the 20-bohr transverse vacuum at dimension=3).tests/test_periodic_megacell_pyscf_parity.py::test_megacell_tdl_matches_pyscf_kmp2_convergence(gatedimportorskip("pyscf"), §10-clean).runner_pyscf.run_periodic_mp2— the §10-clean oracle: a periodic MP2 (pyscf.pbc.mp.KMP2, or molecular MP2 at Γ) post-step added to the out-of-process PySCF runner (examples/regression/core/runner_pyscf.py), with low-dimensional PBC support (dimension/low_dim_ft_type). PySCF runs in a separate interpreter — never imported insidepython/vibeqc/. The TDL cross-check usesdimension=3with 20-bohr transverse vacuum (the inter-chain Coulomb interaction across 20 bohr is << µHa for an insulator like H₂/STO-3G);dimension=1is available but requires the periodic axis as the first lattice vector (PySCF convention). The PySCF KMP2 reference series is checked live by the test.Citation DB (CLAUDE.md §8): added the periodic DLPNO-MP2 method papers
nejad_periodic_dlpno_pbc_2025(Paper I, BvK) +zhu_periodic_dlpno_megacell_2025(Paper II, megacell — Zhu first author), routed underperiodic-mp2/periodic-ri-mp2/periodic-ccsd/periodic-ccsd(t). DOI correction: the DOIs in the handover were wrong (resolved to unrelated AIP conference papers); the correct ones (10.1063/5.0290816,10.1063/5.0290819) were verified against Crossref + PubMed.
Added: toroidal megacell translational pair-family decomposition — Stage 5b (2026-06-19)¶
vibeqc.periodic_toroidal_mp2.toroidal_mp2_gamma_pairs→ToroidalPairResult: the toroidal megacell MP2 with translational pair-family decomposition. Localises the supercell occupied orbitals (Wannier functions via Pipek-Mezey or Boys), assigns each to a cell tile via its position centroid, groups occupied-occupied pairs(i,j)by their relative cell vectorL, and computes per-LMP2 correlation contributions. The totalsum(e_corr_by_L)agrees with the all-pairs 5a result within 1e-14 Ha — the same terms summed in a different partition order. The per-Lbreakdown exposes the distance decay of the correlation energy — infrastructure for the DLPNO pair screening at Stage 5c.Helper
_assign_wannier_cellsmaps Wannier centroids to cell tiles via fractional coordinates in the unit-cell lattice.Tests (
tests/test_periodic_toroidal_mp2.py, 3 new, 7 total): 1e-14 Ha agreement gate at (1,1,2)+(1,1,3), Wannier cell assignment (one per cell for H₂/STO-3G), and distance-decay sanity.
Added: toroidal megacell DLPNO-MP2 local approximation — Stage 5c (2026-06-19)¶
vibeqc.periodic_toroidal_mp2.toroidal_dlpno_mp2→ToroidalDLPNOResult: runs the molecular DLPNO-MP2 driver on the toroidal megacell, using the periodic Γ GDF HF reference (Ewald/Madelung-stabilised orbital energies) and the periodic Γ DF 3-index tensor (adapted to the molecularDensityFittinginterface via_PeriodicDFAdapter). The periodic AO overlap replaces the molecular one so that projections and orthogonalizations are consistent with the periodic MOs. This is the toroidal counterpart of the open-megacellmegacell_run_job(method="dlpno-mp2")— the HF reference is periodic, not molecular, so the per-cell energy includes Ewald stabilisation and converges to the TDL with supercell size.Exactness limit (full domains, no PNO truncation, no pair screening): the DLPNO-MP2 reproduces the canonical 5a toroidal MP2 to machine precision (Δ < 1e-11 Ha at nrep=(1,1,3); the M2 gate, now periodic). Default DLPNO thresholds give sub-μHa agreement for small supercells (~50 μHa at 6 occupied orbitals).
Tests (3 new, 10 total): exactness gate at (1,1,2)+(1,1,3), default convergence, and energy decomposition sanity.
Added: toroidal megacell DLPNO-CCSD and DLPNO-CCSD(T) — Stage 5c extended (2026-06-19)¶
toroidal_dlpno_ccsd + toroidal_dlpno_ccsd_t -> ToroidalDLPNOCCSDResult: run the molecular reduced-scaling DLPNO-CCSD solver (M3c) and DLPNO-(T1) triples on the toroidal megacell, using the same periodic-adaptation pattern as 5c MP2 (PeriodicDFAdapter + periodic-overlap patching). CCSD recovers ~30% more correlation than MP2; (T) is negligible for H2 as expected.
Tests (2 new, 12 total): CCSD convergence + MP2 ordering, (T) smallness + bookkeeping.
Added: toroidal megacell pair screening classifier — Stage 5c infrastructure (2026-06-19)¶
vibeqc.periodic_toroidal_mp2.classify_toroidal_pairs→ToroidalPairClassification: dipole-approximation pair screening by relative cell vector L for the toroidal megacell. Enumerates all unique L families, estimates pair energy via−1/R⁶dipole asymptotics, and classifies each as strong/weak/distant based on energy and distance thresholds. Ther_closeguard (default 8.0 Bohr) forces nearby cells to always be strong — the bare dipole form underestimates near-field correlation by orders of magnitude. Distant L families are candidates for a future screening-aware DLPNO driver to skip, which would reduce the pair count from O(N²) to O(N·N_strong); the current toroidal DLPNO wrappers do not consume this classifier.Tests (
tests/test_periodic_toroidal_mp2.py, 4 new, 16 total): basics (counts + distance ordering), r_close default (nearest-neighbour strong), all-strong limit (zero pair-energy threshold with an effectively unbounded distance guard), all-distant-at-tight-r_close limit.
Added: analytic SCF-energy gradient for basis optimisation + native BDIIS loop (2026-06-17)¶
Closed-form AO-integral exponent derivatives (
cpp/src/basis_param_gradient.cpp, bound asoverlap_/kinetic_/nuclear_/eri_exponent_derivative): ∂S, ∂T, ∂V and ∂(μν|λσ) w.r.t. a primitive Gaussian exponent, via the Hellmann–Feynman r²-weighted bra (∂/∂α e^{−αr²} = −r²e^{−αr²}) represented on an l+2 cartesian shell. The cart→(l+2) transform is measured empirically fromdo_enforce=falseoverlaps (T = ⟨aux|cart_l⟩·⟨cart_l|cart_l⟩⁻¹) rather than assuming libint’s normalisation convention, so it is correct for s/p/d alike. Validated to ≤1e-9 vs finite differences on H (s,p) and C (s,p,d):tests/basisset_dev/test_{overlap,kinetic_nuclear,eri}_exponent_derivative.py.energy_gradient_analyticassembles those into the full RHF energy gradient via the frozen-density Pulay identitydE/dη = tr(P·∂Hcore) + ½ tr(P·∂G(P)) − tr(W·∂S)(W = ½·P·F·P from the converged Fock, no MO coefficients), mapping each optimiser parameter (element, shell, primitive) to every libint shell it drives — one per atom of the element, both sides of an SP shell — with the LOG/LINEAR transform chain rule. Matches full-energy central FD and the Phase-0 integral-FD assembly to ≤5e-9 Ha on Be (single atom), H₂ (multi-atom summing) and H₂O (multi-element, O d-shell):test_energy_gradient_analytic.py.make_rhf_native_objective_gradientreturns a consistent in-process RHF(objective, gradient)pair sooptimize_bdiis(obj, x0, grad=…)runs a fully analytic, fully in-process loop — one SCF per gradient instead of the FD path’s 2·N_param. End-to-end on H₂/pob-TZVP it converges to the same minimum as the FD-gradient driver (|ΔE| = 1.7e-10 Ha):test_bdiis_analytic_optimize.py. Exponent-only; coefficient parameters fall back to the FD gradient.
Fixed: GAPW all-electron soft/hard partition — the smooth grid no longer aliases the core (2026-06-17)¶
The bug (catastrophic, all core atoms). The GAPW Hartree-J built its smooth FFT-grid term from the full all-electron density (
GapwJBuilder.build_J→GpwJBuilder(full_basis)), so a tight core Gaussian aliased on the coarse grid: Ne/STO-3G/24³ collocated to 56 e⁻ (should be 10) → a +5900 Ha spurious smooth Hartree. GAPW totals sat thousands of Ha above the analytic all-electron reference and were often worse than un-augmented GPW — the augmentation, formulated for a soft smooth term, cannot rescue a full-density one. The earlier audit’s “GPW-AUDIT-007 spherical-Poisson” framing understated this: the partition itself was wrong for every core-bearing system.The fix (Lippert–Hutter–Parrinello 1999; Krack–Parrinello 2000). The smooth term is now generated from the softened density ρ̃ (tight primitives pruned, grid-representable) plus a (hard−soft) multipole ρ₀ compensator that restores the pruned core’s charge/multipoles on the grid, projected onto the full AO basis; the core lives entirely in the per-atom augmentation. The Hartree energy uses the correct density-functional form
½∫(ρ̃+ρ₀)V[ρ̃+ρ₀] + Σ_a augmentation(GapwJBuilder.gapw_hartree_energy), not½ tr(D·J)(which contracted the full ρ against the soft potential — Ha-scale wrong for a deep core).Validation (vs the analytic all-electron GDF route, same gauge). On a 24³ grid: He −0.6 mHa (was +79 mHa), Ne +0.34 Ha (was +6065 Ha), H₂O 47 Ha → 9 Ha. Spherical core atoms now reach all-electron accuracy; the ρ₀ compensator restores the charge exactly (He: soft 1.674 + comp 0.326 = 2.000 e⁻; Ne: 7.43 + 2.57 = 10.0 e⁻). Regression-pinned in
tests/test_periodic_gapw_parity.py(He < 5 mHa, Ne < 0.5 Ha on a fine grid).solve_poisson_radialis now multipole-capable (real-spherical-harmonic projection + per-l radial Green’s function), reducing bit-for-bit to the monopole for a spherical density. It defaults tolmax=0(the production augmentation setting): the aspherical augmentation (an O in H₂O, an open p shell) needs the in-sphere hard/softl>0cancellation reworked — the ρ₀ windowing/compensator are not yetl>0-consistent (enabling multipoles regressed H₂O) — which is the next milestone (the real GPW-AUDIT-007).Still experimental / known-limited. Accuracy is grid-convergent (coarse grids stay mHa–Ha off) and molecular/aspherical totals are not yet at parity; the GAPW route stays opt-in (
jk_method='gapw'), behindGAPWExperimentalWarning. The GDF / BIPOLE routes remain the production all-electron path. Citations: LHP 1999 + Krack–Parrinello 2000 added to thegapwroute indatabase.toml.
Added: GPAW plane-wave-limit atomization reference generator (out-of-process) (2026-06-17)¶
New
examples/regression/pw_limit_atomization/— a §10-clean, out-of-process GPAW driver that generates plane-wave-limit atomization energies as an open-source reference (a VASP alternative) for the revised-pobbasis-set work (Peintinger & Bredow, Approaching the plane wave limit with Gaussian basis sets). GPAW (GPLv3) is executed only in a separate interpreter viasubprocess; it is never imported intopython/vibeqc/(CLAUDE.md §1/§10). Computes the per-formula-unit atomizationE_at = ΣE(free atom) − E(solid)with matched solid/atom plane-wave cutoffs (so the PAW reference cancels), spin-correct Hund free-atom references, a cutoff sweep and anE_cut^(-3/2)PW-limit extrapolation; the CLI emits Markdown + a JSON provenance sidecar.Validated end-to-end on LiF/r2SCAN: GPAW PW-limit atomization = 840 kJ/mol per formula unit (4.36 eV/atom), reproducing the published SCAN/PBE cohesive energies (Mejía-Rodríguez & Trickey 2018; Csonka 2009) — an independent check of the density-collocation → FFT-Poisson → atomization machinery.
Free atoms use GPAW’s own blessed direct-minimisation recipe (its cohesive-energy tutorial:
etdm-fdpw+ no mixing + fixed-uniform occupationsconditional Hund [max-spin for open shells, off for closed shells —
hund=Truestalls the closed-shell meta-GGA direct minimisation] + symmetry off), which reproduces the prior SCF Li/F references to <0.02 kJ/mol and is far more robust; atom energies are cached per (element, functional, cutoff, box) so a full-set run computes each element once.
Validated on both an open-shell-atom system (LiF: GPAW r2SCAN 840 kJ/mol) and a closed-shell-atom system (MgO: GPAW r2SCAN 1009.5 vs VASP@900 1012.0, lit SCAN ~1011) — GPAW tracks the published SCAN values within ~2.5 kJ/mol and matches whichever of pob/VASP is correct system-by-system. Stoichiometry is summed per formula unit (correct for non-1:1 compounds such as Al₂O₃/Li₂O).
Tests:
tests/test_pw_limit_atomization.py— 6 fast pure-Python (extrapolation, systems-data integrity, functional map) + 1 GPAW-gated end-to-end smoke test.
Added: Γ-point open-shell GDF Fermi-Dirac smearing (run_pbc_gdf_uhf/uks) (2026-06-17)¶
The Γ-only open-shell GDF drivers
run_pbc_gdf_uhf/run_pbc_gdf_uksnow honorsmearing_temperature > 0: per-spin Fermi-Dirac occupations with independent global chemical potentials μ_α, μ_β over the degenerate Γ levels (the samevibeqc.smearing.apply_smearing_open_shellcore as the multi-k drivers, CP2K/VASP/QE convention).PBCGDFUHFResult/PBCGDFUKSResultgainsmearing_temperature,fermi_level_alpha/beta,entropy,free_energy(MerminA = E − T·(S_α+S_β)), and per-spinoccupations_alpha/beta. This completes the open-shell GDF family smearing (Γ + multi-k).Smearing-consistent ⟨S²⟩ at Γ: the fractional-occupation (ensemble-UHF) ⟨S²⟩ generalisation is used when smeared, reusing the corrected
_multi_k_s_squared; integer/T=0 filling stays the exact value.Behavior-neutral at T = 0: the integer-Aufbau path is byte-for-byte unchanged —
run_pbc_gdf_u{hf,ks}T=0 gates (UHF(M=1)≡RHF, H-doublet / UKS-PBE PySCF parity) stay green to machine precision.Reachable from
run_periodic_job: open-shell GDF smearing now acceptskpoints=None(Γ, routed torun_pbc_gdf_{uhf,uks}) in addition to a k-mesh; only the Ewald-3D / BIPOLE open-shell drivers still reject smearing.Validated (
tests/test_pbc_gdf_compcell.py): a boron 2p¹ degenerate shell fractionally occupied (~1/3 each) at Γ with per-spin particle conservation, entropy > 0,A < E, and the exact doublet ⟨S²⟩ = 0.75 (UHF + UKS-PBE), directly and throughrun_periodic_job.
Added: multi-k corrected-gauge BIPOLE analytic gradient — all four drivers (2026-06-17)¶
compute_bipole_gradient_rhfandcompute_bipole_gradient_uhfnow support corrected-gauge (Ewald-exchange-split) results at multi-k — previously they refused them (only the Γ corrected gauge was wired). Passkmesh=(theBlochKMeshthe SCF used). The multi-k corrected gauge is a standard variational HF gradient (full Bloch density → no Bloch-CPHF): the legacy assembly with the exchange block swapped toK_SR(erfc)+ per-q reciprocalK_LR+ per-k MadelungS(k)D(k)S(k), the full jellium, and every real-space-density term fed the per-k density inverse-Bloch-folded onto the gradient template (density_set_from_k_matrices) rather thanresult.density(whose 2×-cutoff cell list over-counts the lattice-summed 2e gradient). New_k_long_range_ewald_gradient_multi_k(per-q exchange, the multi-k analogue of_k_long_range_ewald_gradient) and_madelung_ewald_gradient_multi_k. Reduces EXACTLY to the Γ corrected core at n_k=1 (RHF 1.8e-18) and matchescompute_bipole_gradient_fdto 6.3e-8 Ha/bohr at n_k=2 (asymmetric BeH₂ [2,1,1]/STO-3G). Regressiontest_multik_rhf_corrected_gauge_matches_fd(@slow).UHF shares the same core, which auto-detects the open-shell result and builds a spin-resolved exchange
2·Σ_σ ∂E_x[P_σ](per-spin K_SR/K_LR/ Madelung) with the open energy-weighted W; the total density drives the 1e/Coulomb/jellium/J^LR terms. FD 1.3e-8 on an asymmetric BeH doublet [2,1,1]/STO-3G (regressiontest_multik_uhf_corrected_gauge_matches_fd,@slow). The closed-shell path is byte-for-byte the prior RHF assembly.RKS/UKS route their multi-k corrected-gauge case to the same core (exchange scaled by the functional’s HF fraction; the core auto-detects open shell) and add the multi-k XC Pulay (
xc_lattice_gradient_contribution[_uks]on the Bloch-folded density) + the grid-motion correction — mirroring the Γ RKS/UKS drivers. FD-clean: RKS svwn 4.7e-8; UKS svwn (spin-polarized BeH doublet) 8.0e-6 (DFT-grid floor). Regressionstest_multik_{rks,uks}_corrected_gauge_matches_fd(@slow). All four multi-k corrected-gauge drivers (RHF/UHF/RKS/UKS) are now FD-validated;compute_bipole_gradient_fdremains the documented production force path pending broader certification.
Added: periodic ROHF — multi-k (full BZ sampling), EWALD_3D (2026-06-17)¶
vibeqc.run_rohf_periodic_multi_k_ewald3d(system, basis, kmesh, options)→PeriodicROHFMultiKEwaldResult: spin-pure restricted-open-shell HF with full k-point sampling — the production sibling of the Γ-only periodic ROHF driver. Reuses the build-validated multi-k UHF two-electron machinery (_build_uhf_fock_blocks_ewald3d, the analytic-FT Hartree J + real-space K, the inverse-Bloch density fold, the Madelung fix, per-cell Ewalde_nuc) verbatim; the only change is the per-k orbital update via Roothaan’s single effective Fock + the open-shell occupation. Occupations are equal at every k (gapped-insulator setup; no smearing — metals are a follow-up).Fix:
vibeqc.rohf.roothaan_effective_focknow preserves the input dtype instead of casting tofloat. At a non-Γ k-point the per-spin Fock / density / overlap are complex-Hermitian; the oldfloatcast silently discarded the imaginary part. Verified: the complex-Hermitian coupling reproduces Coulson’s block structure to 1e-16, and the real (molecular / Γ) path is unchanged.Tests (
tests/test_periodic_rohf_multi_k_ewald.py): closed-shell == multi-k RHF; a [1,1,1] mesh reproduces the Γ-only ROHF driver; spin-pure doublet. Build-box / PySCF.pbc KROHF parity per §7.
Added: periodic ROHF — Γ-point, EWALD_3D (2026-06-17)¶
vibeqc.run_rohf_periodic_gamma_ewald3d(system, basis, options)→PeriodicROHFEwaldResult: spin-pure restricted-open-shell HF for periodic systems at the Γ point. The periodic counterpart ofrun_rohf, and the open-shell sibling ofrun_rhf_periodic_gamma_ewald3d/run_uhf_periodic_gamma_ewald3d.Reuses the build-validated periodic two-electron machinery from the Γ UHF driver verbatim — the Ewald-3D Hartree J, the real-space exchange K, the Madelung self-image correction, the per-cell Ewald nuclear repulsion, and the EWALD_3D gauge forcing (CLAUDE.md §7). The only change from UHF is the orbital update: the per-spin Fock matrices are combined into Roothaan’s single effective Fock and diagonalised once, with the standard open-shell occupation rule (
vibeqc.rohf._roothaan_occupations) — the same code the molecular ROHF driver uses.The SCF-loop wiring was cross-checked against the molecular
run_roothaan_scfon synthetic integrals (identical energy + density), so the periodic transcription is correct modulo the (reused, validated) periodic J/K.Tests (
tests/test_periodic_rohf_ewald.py): closed-shell H₂ box == periodic RHF; one-electron doublet == periodic UHF with ⟨S²⟩ = 0.75; Li-atom box spin-pure (⟨S²⟩ = 0.75 exact) and at/above UHF; bad-multiplicity guard.Scope: Γ-point + EWALD_3D + HF. Multi-k, the GDF / BIPOLE / GPW JK routes, periodic ROKS, and
run_periodic_job(method="ROHF")integration are follow-ups (HANDOVER_ROHF.mdM7).
[v0.13.1] — 2026-06-17¶
Patch on Wisesa’s Fox: a periodic ASE fix plus test-gate reliability
hardening, cherry-picked from main.
Fixed¶
Γ-only periodic HF through the ASE calculator now routes through the Ewald-3D exchange path — corrects the exchange treatment for ASE-driven Γ-point periodic Hartree-Fock (
82295bd9).
Changed — test-gate reliability¶
gate_verdict.py --reverify: a new (unlisted) non-PASS file is re-run once in isolation before it turns the verdict red, so transient contention on a shared box cannot fake a regression (d57a04cd; +10 unit tests intests/test_gate_verdict_reverify.py).known_slowbaseline restored 4 → 8: the GPW/GAPW grid-collocation files wrongly de-listed when the EWALD analytic-FT Hartree-J cache (E2) landed are re-listed — E2 never sped GPW collocation (that is the separate E3 work) (5453f9a9).Refreshed three stale v0.5.0-era comparison-example banners (
ad935310).
[v0.13.0] — 2026-06-17 — Wisesa’s Fox¶
Added: MSINDO open-shell (UHF) for the heavy d/p-block trajectory elements (2026-06-17)¶
Open-shell UHF now reaches the MSINDO reference SCF stationary point for the heavy d/p-block elements (
_MSINDO_TRAJECTORY_SCF; the 4d Tc–Pd and 5p Sb–Xe set, and by extension Nb/Mo). As with the closed-shell heavy elements, the production DIIS path jumps SCF basins, sorun_msindo(..., multiplicity=M)containing any such element now dispatches to the MSINDO-faithful extended-Hückel guess + WICHT density damping + energy-only convergence UHF SCF (_scf_uhf_msindo/scf_uhf_msindo_driver, portinghuckop.f/scfopn.f/eneopn.f/wicht.f); every other element keeps the DIIS UHF path, which preserves the bit-for-bit UHF(M=1)≡RHF invariant there.Fixed the open-shell one-centre d-block Fock: the closed-shell EINZI hybrid d terms applied per spin are wrong for open shells (Coulomb must contract the total density, exchange the same-spin density, with different integral combinations). Ported
fockop.f’s d block faithfully (_add_einzi_dblock_uhf/add_einzi_dblock_uhf); it reduces to the closed-shell_add_einzi_dblockexcept for a handful of off-axis d–d couplings wherefockop.fandfockcl.fgenuinely differ (the oracle’s own UHF(M=1) shows the same, e.g. AlCl₃).Python and C++ kept in lockstep; validated against the MSINDO oracle to ≤1 µHa on open-shell radicals spanning the gated elements + two triplets (NbO₂, MoCl, MoO₂ triplet, TcCl₂, RuCl, RuF₂ triplet, RhF₂, PdCl, SbF₂, TeCl, ICl₂, XeOF; Nb/Mo inherit this UHF SCF from their RHF trajectory-set membership). Tests:
tests/test_msindo.py::test_uhf_heavy_4d_5p_total_energy_parity/test_uhf_msindo_reduces_to_rhf_heavy,tests/test_msindo_cpp.py::test_cpp_uhf_heavy_oracle_and_python_parity.
Fixed: MSINDO Nb₂/Mo₂ converge to the reference SCF state (2026-06-17)¶
The homonuclear 4d dimers Nb₂ and Mo₂ converged to a different SCF stationary point than reference MSINDO on the production DIIS path (Mo₂ landed ~0.10 Ha too low, Nb₂ ~0.17 Ha too high — both genuine but wrong RHF basins). Routing Nb (41) and Mo (42) to the MSINDO-faithful Hückel-guess + WICHT path (
_MSINDO_TRAJECTORY_SCF/is_msindo_trajectory_scf, Python + C++) reaches the reference basin: Nb₂ −6.9007186 (≤2e-8), Mo₂ −14.4178689 (≤1.2 µHa, the inherent numpy/Eigen/Netlib spread on its near-degenerate flat plateau). Their well-behaved halides NbF₅/MoF₆ reproduce the oracle on the WICHT path too, so the move regresses nothing; C++↔Python parity holds (≤2e-8). Tests:test_homonuclear_4d_dimers_parity,test_cpp_homonuclear_4d_dimers.Diagnosis recorded in
handovers/HANDOVER_MSINDO_SCF.md: the residual XeF₄/Ag₂ divergences are a non-portable diagonalizer-arithmetic knife-edge between multiple valid RHF solutions (the reference state is metastable, not the global SCF minimum), confirmed by restarting from the oracle density (≤2.6e-9). The three candidate fixes — matchingDSYEVD, maximum-overlap occupation, and the Pongor/Fermi level-shift — are each ruled out with evidence.
Fixed: smearing-consistent ⟨S²⟩ for multi-k open-shell GDF; corrected stale docstrings (2026-06-17)¶
run_kuhf/kuks_periodic_gdf⟨S²⟩ now uses the fractional per-spin occupations under Fermi-Dirac smearing._multi_k_s_squaredpreviously took a hard first-n_σorbital cutoff (unit weights) — exact at integer filling but approximate once a band is fractionally occupied. The α/β cross-spin overlap is now weighted byn^α_i(k)·n^β_j(k)over all orbitals (the ensemble-UHF generalisation of Szabo & Ostlund Eq. 2.271); it reduces bit-identically to the old cutoff at integer occupation (T=0 / gapped), so every existing closed-shell-limit and integer-filling pin is unchanged. New unit pins:test_multi_k_s_squared_{reduces_to_hardcut_at_integer_filling, fractional_weighting_matches_closed_form}. (design_smearing.md M3 backlog.)Docstring fix:
run_kuhf_periodic_gdfclaimed “Smearing is not supported on this open-shell path” — it has supported per-spin Fermi-Dirac smearing (global μ_α/μ_β across the BZ) since 2026-06-15; the docstring now describes the smearing + Mermin free-energy behaviour.
Fixed: MSINDO run_job fails closed for unsupported elements (Z>54) (2026-06-17)¶
Molecular
run_job(method="msindo")for a closed-shell molecule routes to the C++run_msindo_full, which does not validate element scope: an unsupported atomic number (e.g. Cs, Z=55) reached the engine withMsindoParameterSet::get(...)returning 0.0 for the missing parameters and produced a meaningless energy instead of an error._run_semiempiricalnow guards every atomic number againstmsindo._SUPPORTED(Z 1–54, the bundled H–Xe parameter set) before dispatch and raises the sameNotImplementedError("MSINDO engine supports …")the directrun_msindocall and the CCMrun_jobpath already raise. Supported heavy elements are unaffected (e.g.run_jobon TcCl still returns the oracle −25.0060838 Ha).tests/test_msindo.py::test_run_job_unsupported_element_fails_closedpins it.
Added: multi-k corrected-gauge BIPOLE RHF + UHF analytic gradient (2026-06-17)¶
compute_bipole_gradient_rhfandcompute_bipole_gradient_uhfnow support corrected-gauge (Ewald-exchange-split) results at multi-k — previously they refused them (only the Γ corrected gauge was wired). Passkmesh=(theBlochKMeshthe SCF used). The multi-k corrected gauge is a standard variational HF gradient (full Bloch density → no Bloch-CPHF): the legacy assembly with the exchange block swapped toK_SR(erfc)+ per-q reciprocalK_LR+ per-k MadelungS(k)D(k)S(k), the full jellium, and every real-space-density term fed the per-k density inverse-Bloch-folded onto the gradient template (density_set_from_k_matrices) rather thanresult.density(whose 2×-cutoff cell list over-counts the lattice-summed 2e gradient). New_k_long_range_ewald_gradient_multi_k(per-q exchange, the multi-k analogue of_k_long_range_ewald_gradient) and_madelung_ewald_gradient_multi_k. Reduces EXACTLY to the Γ corrected core at n_k=1 (RHF 1.8e-18) and matchescompute_bipole_gradient_fdto 6.3e-8 Ha/bohr at n_k=2 (asymmetric BeH₂ [2,1,1]/STO-3G). Regressiontest_multik_rhf_corrected_gauge_matches_fd(@slow).UHF shares the same core, which auto-detects the open-shell result and builds a spin-resolved exchange
2·Σ_σ ∂E_x[P_σ](per-spin K_SR/K_LR/ Madelung) with the open energy-weighted W; the total density drives the 1e/Coulomb/jellium/J^LR terms. FD 1.3e-8 on an asymmetric BeH doublet [2,1,1]/STO-3G (regressiontest_multik_uhf_corrected_gauge_matches_fd,@slow). The closed-shell path is byte-for-byte the prior RHF assembly.RKS/UKS multi-k corrected gauge still refuse (the multi-k XC Pulay term is pending);
compute_bipole_gradient_fdremains the production force path until all four are certified.
Added: Phase-0 SCF-energy-gradient assembly for basis optimisation (2026-06-17)¶
vibeqc.basis_optimization.energy_gradient— the analytic energy-weighted-density (Pulay) assembly for the RHF basis-parameter gradient:energy_gradient_fd(provider, x)=tr(P·∂Hcore) + ½·tr(P·∂G(P)) − tr(W·∂S), with integral-level central differences andW = ½·P·F·P(no MO coefficients needed). It is the nuclear gradient with ∂/∂R → ∂/∂η, reusing the structure ofcpp/src/gradient.cpp. Integral source is injected via anIntegralProvider;VibeqcIntegralProviderwraps the realcompute_overlap/kinetic/nuclear/eribindings for production, and the assembly itself is build-free.Verified build-free in
tests/basisset_dev/test_energy_gradient_assembly.py(8 cases) against a self-contained 2-function / 1-pair mock RHF: the frozen-P/W assembly reproduces the full re-SCF energy finite difference across several points, plus theW = ½·P·F·Pidentity and G(P) consistency. The realVibeqcIntegralProviderpath was verified on a build (mars, 2026-06-17): on H₂ / pob-TZVP / RHF the assembly matched the full-energy finite difference to ≤ 1.5×10⁻⁷ Ha.Phase 0 of
docs/basisset_dev/ENERGY_GRADIENT_DESIGN.md(maintainer-selected approach); Phase 1 swaps integral-level FD for analytic shell-incrementation behind the same provider interface, leaving this assembly unchanged.
Added: Analytic gradients for the basis-optimisation penalty terms (2026-06-17)¶
vibeqc.basis_optimization.gradients— closed-form gradients of the overlap-based penalty terms, so a BDIIS / scipygrad=need not finite-difference the (stiff, near-linear-dependence) penalty:condition_number_penalty_gradient(∂/∂x of γ·ln κ(S)) andld_penalty_gradient(∂/∂x of the λ_min hinge), both returning the optimiser-space vector (log/linear chain rule applied).Kernel is the analytic same-center overlap derivative ∂S/∂α (from the closed form
s(α,β;l) = (2√(αβ)/(α+β))^(l+3/2)) + Hellmann–Feynman eigenvalue derivatives∂λ = vᵀ·∂S·v. This same ∂S/∂α is what the future analytic SCF-energy gradient’s Pulay term−tr(W·∂S/∂α)will reuse. Scope: segmented (non-SP) shells, simple extreme eigenvalues.penalty_gradient(parametrisation, x, …)— combined convenience mirroring the objective’suse_cond_penalty/use_ld_penaltyflags, so a BDIISgrad=for a penalised objective is justenergy_grad(x) + penalty_gradient(p, x, …same flags…).A design proposal for the remaining piece — the analytic SCF-energy gradient (the Pulay term, reusing this ∂S/∂α and the existing nuclear- gradient W/Pulay machinery in
cpp/src/gradient.cpp) — is indocs/basisset_dev/ENERGY_GRADIENT_DESIGN.md; it needs a maintainer decision on the libint exponent-derivative strategy before implementation (touches C++, build-only validation).Tests:
tests/basisset_dev/test_basis_opt_gradients.py(15 cases) — every analytic derivative checked against central finite differences (primitive overlap, normalised block, κ-penalty on exponents / coefficients / multi-element, the log-vs-linear Jacobian, the LD hinge active + inactive, the combinedpenalty_gradient), plus an end-to-endoptimize_bdiisrun driven by the analytic penalty gradient.
Added: BDIIS basis-set optimiser + condition-number penalty (2026-06-17)¶
vibeqc.basis_optimization.optimize_bdiis— a third optimiser driver alongsideoptimize_scipy/optimize_minuit(sameOptResultcontract). BDIIS (Basis-set Direct Inversion in the Iterative Subspace; Daga, Civalleri & Maschio, J. Chem. Theory Comput. 16, 2192 (2020)) is the optimiser behind CRYSTAL23’sOPTBASIS, the basis-parameter analogue of geometry DIIS. Hardened beyond the CRYSTAL baseline: a BFGS inverse-Hessian drives both the DIIS error vectors (eᵢ = −H⁻¹·gᵢ) and the quasi-Newton fallback; a trust-region + Armijo backtracking guard (Farkas–Schlegel) guarantees the objective never increases on an accepted step (taming the diffuse-exponent collapse); an ill-conditioned DIISBmatrix falls back to the quasi-Newton step; convergence uses the box-projected gradient so a bound-constrained minimum registers correctly.condition_number_penalty(...)(inbasis_optimization.ld_diagnostics) — the smooth γ·ln κ(S) objective term (VandeVondele & Hutter, J. Chem. Phys. 127, 114105 (2007)) of the OPTBASIS objective Ω = E + γ·ln κ. Wired into the objective factories viause_cond_penalty=/cond_gamma=, parallel to the existing λ_min hingeuse_ld_penalty=.Citations: new
daga_bdiis_2020+vandevondele_basisopt_2007entries in the citation database, surfaced byvibeqc.basis_optimization.method_citations()(basis optimisation is an offline procedure, so they carry no SCF-job route).Tests:
tests/basisset_dev/test_bdiis_driver.py(13 cases) — quadratic / anisotropic / finite-difference / bound-constrained / Rosenbrock convergence, a scipy cross-check, the γ·ln κ penalty math, and a citation-resolves-in-database guard.
Added: AUTO k-point recommender — KPoints.recommend() (Phase K8) (2026-06-16)¶
vq.KPoints.recommend(system, ...)turns a structure into a ready-to-use k-point spec: it classifies the system’s electronic character (metal / small-gap / insulator), picks the target k-spacing Δk for that character, builds a symmetry-reduced Monkhorst–Pack mesh, and returns a :class:KPointscarrying three new AUTO-only attributes —.smearing(a :class:SmearingOptionsto hand the SCF driver,Nonefor clean insulators),.rationale(e.g. “metal, Δk=0.12 Å⁻¹, Γ-centred, 13×13×13, 84 irreducible k-points; smearing fermi-dirac kT=0.005 Ha”), and.verification. It is the electronic-structure-aware sibling of the K5 density builders (from_kspacing/from_kppra/auto) and reuses the validatedfrom_kspacingΔk→mesh math.Character resolution priority: explicit
band_gap(eV) →is_metal→ an optionalclassifier(system)hook → otherwise a SAFE default (treat as metal, the dense over-converged choice) with aUserWarningthat character was assumed. Smearing follows the same character so the two never disagree. Δk targets (insulator 0.30 / small-gap 0.18 / metal 0.12 Å⁻¹, Materials-Project KSPACING units) and the gap cutoffs are named constants in one tunable config block at the top ofvibeqc/kpoints.py.Grid + edge cases: Γ-centred grids for hexagonal/trigonal lattices (spacegroup-authoritative, geometric 60°/120° fallback — FCC/BCC primitive cells are not misflagged); large 3D supercells collapse to Γ-only; slab/ wire vacuum axes stay at
N = 1. NewKPoints.grid_type/full_mesh_sizeproperties.Optional
verify=Trueruns a convergence ladder (Δk·√2, Δk, Δk/√2, …) via a user-suppliedscf_energy_fn, refining until the total energy/atom changes by less thantolerance_meV_per_atom(default 1.0) between rungs and attaching the ladder as a new exported :class:KPointConvergence.Optional
predictor=ML Δk hook (conservative end of the predicted interval); no model is bundled —predictor="ml"requires a userml_predictor=callable, else raises with guidance (cf. Choudhary & Tavazza, npj Comput. Mater. 6, 39 (2020)).Tests:
tests/test_kpoints_recommend.py(39 cases) — cubic insulator, simple metal, hexagonal Γ-centring, large-supercell Γ-only collapse, slab axis pinning, band-gap thresholds, verify ladder, ML hook, validation.
Added: SYM3b point-group-reduced direct-ERI Fock build for BIPOLE (~3× faster) (2026-06-16)¶
run_pbc_bipole_rhf(..., use_fock_symmetry_reduce=True)builds the direct-space J(g)/K(g) only at the crystal point group’s atom-pair-orbit representative cells (full internal lattice sum, so each emitted block is exact) and reconstructs the rest by rotation — the |G|-fold compute reduction (Phase SYM3b) that the SYM3 storage round-trip (vibeqc.symmetry_integrals) was the validation substrate for. New C++build_jk_2e_real_space_output_subset+ Pythonbuild_jk_reduced_symmetrized/representative_cell_indices(vibeqc.bipole_symmetry_fock).Bit-identical to
symmetrize_fock_blocks(full build)(Δ=0 on MgO/STO-3G;tests/test_bipole_symmetry_fock.py::test_sym3b_reduced_build_equals_symmetrised_full_build). On MgO/STO-3G SHRINK 2 2 c12 the direct-ERI build runs 14/55 cells → ~25 s/iter vs ~75 s (≈3×); composes with the incremental-Fock ΔD path (reconstruction is linear). Enforcing the point group shifts the (truncation-asymmetric) energy by only ~0.08 mHa (raw −271.21530 → symmetrised −271.21537), well inside the cross-family tolerance — the@slowCRYSTAL parity gate now uses it (~4½ min, was ~13½ min).Whole-cell reduction shipped (~2.6–3×); the finer atom-pair shell-pair mask for the full ~7.9×@c12 / 11.8×@c16 remains a follow-up (SYM3b real-space point-group reduction). Wired into all four BIPOLE drivers (RHF, UHF, RKS, UKS —
use_fock_symmetry_reduce=True); for UHF/UKS the per-spin J_SR/K_SR builds reduce independently, and for pure-functional RKS/UKS the reduced J reconstructs while the non-reduced path keeps its J-only build (test_bipole_symmetry_fock.py::test_sym3b_reduce_matches_full_symmetry_uhf_rks_uks, reduce ==use_fock_symmetryto <1 µHa for all three).
Added: MSINDO completes the periodic table to Xe (Z 1–54); C++ engine extended to the full 5th row (2026-06-16)¶
MSINDO/INDO now supports the full first six rows, H–Xe (Z 1–54). The last 10 gated elements — the open-d 4d metals Tc, Ru, Rh, Pd and the heavier lone-pair 5p Sb, Te, I, Xe (with Ag/Cd landed earlier this cycle) — are oracle-validated to ≤1 µHa across diverse closed-shell molecules (
tests/test_msindo.py::test_heavy_4d_5p_total_energy_parity).SCF stationary-point fix. These elements converge to a different SCF stationary point than the production DIIS path finds — DIIS lands higher (Tc/Rh) or lower with the polarization d wrongly occupied (Pd/Sb/Xe). The fix is not
stewart.fpseudo-diagonalization: MSINDO’s default molecular SCF is full-diagonalization + WICHT density damping from an extended-Hückel guess (huckcl.f; bare-IPOT diagonal keeps the diffuse metal d out of the occupied space) with an energy-only convergence test. Implemented as_scf_rhf_msindo/scf_rhf_msindo_driver, element-dispatched via_MSINDO_TRAJECTORY_SCF(Tc–Pd, Sb–Xe); H–Mo/Ag/Cd/In/Sn keep the DIIS path (more robust for their near-degenerate small molecules, e.g. AgH). Seehandovers/HANDOVER_MSINDO_SCF.md.C++ engine extended to the full 5th row (
indo_engine.hpp), in lockstep with Python: the Y–Cd/In–Xe ENEG/ATENG categories (Rb/Sr alkali/alkaline-earth, Y–Pd occupied-4d TM, Ag/Cd In–Xe-loop, In–Xe p-block), the [Kr]/[Kr]4d¹⁰ frozen-core penetration (4s/4p/4d, TAU4*/FCP4*), and the Hückel+WICHT SCF. Fixes a latent production bug:run_jobroutes closed-shell jobs to the C++run_msindo_full, which was H-Kr-only — so the already-enabled 5th row (Mo/In/Sn/Ag/Cd) returned wrong energies viarun_job(e.g. MoF₆ −147.99 vs the correct −149.11). C++ now matches the Python engine and the oracle for the whole 5th row (tests/test_msindo_cpp.py::test_cpp_fifth_row_oracle_and_python_parity).H–Kr behaviour is byte-identical (every new branch is gated on Z≥37). Known residual: a few near-degenerate pathological cases (XeF₄, homonuclear dimers) converge to a different state — the same class already accepted for Mo₂/Nb₂ — and are not enablement targets.
Validated: BIPOLE↔CRYSTAL cross-family parity at converged k; the matched-k SHRINK-2-2 target is exxdiv-convention-confounded (2026-06-16)¶
The converged BIPOLE multi-k SCF (corrected gauge,
exxdiv='ewald') for MgO/STO-3G at SHRINK 2 2 gives −271.21530 Ha/FU (cutoff 12, metric Σ_k w_k Tr[D(k)S(k)] = 20.000 → physical basin), reproducing CRYSTAL’s k-converged SHRINK-8-8 value (−271.21814375) to +2.9 mHa and PySCF KRHF [2,2,2]exxdiv='ewald'(−271.213356) to −1.9 mHa.It is +641 mHa from CRYSTAL’s coarse SHRINK-2-2 value (−271.85640) — not a bug: BIPOLE applies the probe-charge Ewald (Madelung) finite-size exchange correction
(ξ_M − π/V_sc·ω²)·SDS, so its coarse mesh is already k-converged, while CRYSTAL applies none and over-binds its own SHRINK-8-8 by 638 mHa. The matched-k SHRINK-2-2-vs-SHRINK-2-2 premise (comparing two coarse-mesh values) is therefore invalid; the valid cross-family CRYSTAL comparison is BIPOLE-SHRINK22 (exxdiv) ↔ CRYSTAL-SHRINK88 (k-converged).New
@slowpintests/test_pbc_bipole_multik_ewald_split.py::test_mgo_shrink22_converged_matches_crystal_kconvergedexample
parity_mgo_shrink22_crystal_convention.py; the −271.85640 matched-k target is retired;docs/periodic_jk_routes.md§ Current status updated. Efficiency (PBC-audit): the BIPOLE multi-k E2/E3 per-iteration reuse is already done (98.5 % of per-iter is the E1 direct-ERI build); the real-space point-group reduction (SYM3b; measured 7.9×@c12, 11.8×@c16) is scoped as the SYM3b BIPOLE follow-up.
Added: ROHF in the ASE calculator + analytic-gradient run_job optimisation (2026-06-16)¶
VibeQC(..., restricted_open=True)— the ASE calculator now runs open-shell HF as spin-pure ROHF (analytic energy + forces viacompute_rohf_gradient), unlocking ASE-driven workflows (custom optimisers, MD, NEB-via-ASE) for restricted open-shell. Additive: the default (False) keeps the historical UHF/UKS auto-selection. Open-shell DFT stays UKS in the calculator (ROKS has no analytic gradient yet).run_job(method="rohf", optimize=True)now uses the analytic ROHF gradient (routed through the VibeQC calculator) instead of finite-difference forces — full-speed BFGS relaxation. ROKS optimisation remains finite-difference.
Added: analytic ROHF gradient → fast optimisation + frequencies (2026-06-16)¶
vibeqc.compute_rohf_gradient(molecule, basis, result)— the analytic ROHF nuclear gradient (Ha/bohr). Assembled from the same primitives as the UHF gradient (one-electron + two-electron J/K + nuclear repulsion) but with the ROHF energy-weighted densityW = Dα·Fα·Dα + Dβ·Fβ·Dβin the Pulay (overlap) term. This Lagrangian form is the subtle part of an ROHF gradient: because ROHF orbitals diagonalise the Roothaan effective Fock (not the per-spin Focks), the naive “Σ nᵢ εᵢ CᵢCᵢᵀ” form with effective-Fock eigenvalues is wrong;Dσ·Fσ·Dσis exact (verified by finite difference of the ROHF energy to ~1e-10 with synthetic analytic integral derivatives).ROHF geometry optimisation now uses analytic gradients via
molecular_optimize.optimize_molecule(method="rohf")(full speed, not FD).ROHF Hessians / harmonic frequencies now work (
run_job(method="rohf", hessian=True)): the FD Hessian finite-differences the analytic ROHF gradient. The run_job Hessian gate now blocks only ROKS (its analytic XC-gradient term is still pending — M5b for ROKS).Tests: synthetic FD-vs-analytic check of the energy-weighted density (the pinned Lagrangian form), real-molecule
compute_rohf_gradientvs FD of the energy, and an OH-radical frequencies run.
Improved: dedicated ROHF/ROKS orbital table in the SCF log (2026-06-16)¶
ROHF/ROKS results now print a single-column orbital table with closed/open/virtual occupations (2/1/0) and SOMO markers, instead of the UHF two-column layout that showed identical α/β orbital energies. Reads clearly as restricted-open-shell. (
scf_log._format_orbital_table_rohf, routed by the result’smethod; output-only, unit-tested.)
Added: ROHF/ROKS reach atomization + scans + the molecular optimiser (2026-06-16)¶
Atomization (
run_job(..., atomization=True)andvibeqc.atomization) acceptsmethod="rohf"|"roks": free-atom references are computed with the spin-restricted open-shell solver for both closed- and open-shell atoms (it reduces to RHF/RKS at a closed shell), giving spin-pure atomic references for the Σ E(atom) − E(molecule) energy.PES scans (
vibeqc.scan) and the molecular geometry optimiser (vibeqc.molecular_optimize.optimize_molecule) already drive ROHF/ROKS: both route methods without an analytic gradient through their existing finite-difference branch (_evaluate_energy/ central-difference forces →_run_single_point), which now resolvesrohf/roks. Rigid (energy-only) scans and relaxed (FD-force) scans both work.Not yet wired (need finite-difference force routing through their analytic-gradient machinery, or an ASE-calculator method knob — a separate increment):
vibeqc.neb,vibeqc.irc,vibeqc.dimer, and thevibeqc.ase.VibeQCcalculator. These raise a clear method-unsupported error for now.
Added: cas_reference="rohf" — spin-pure CAS starting orbitals (2026-06-16)¶
The determinant-solver family (CASCI / CASSCF / NEVPT2 / CASPT2 / CISD / selected-CI / DMRG / v2RDM / transcorrelated-CI) accepts
cas_reference="rohf"alongside"rhf"/"uhf"/"uno". ROHF gives one spin-restricted orbital set already ordered closed→open→virtual, so the active space starts from a spin-pure (⟨S²⟩ = S(S+1) exactly), contamination-free reference — a clean alternative to UNO that needs no natural-orbital diagonalisation (get_hf_orbital_provider(method="rohf")).Citation:
roothaan_rohf_1960fires when a CAS job usescas_reference="rohf"(parallel to UNO → Pulay-Hamilton 1988).ROHF-MP2 / ROHF-CC (semicanonical post-HF on the ROHF reference) remain future work (
HANDOVER_ROHF.mdM6b).
Added: ROHF/ROKS geometry optimisation (finite-difference forces) (2026-06-16)¶
run_job(..., method="rohf"|"roks", optimize=True)now runs an ASE/BFGS geometry optimisation. Like the gradient-less wavefunction solvers (CASCI/CISD/…), ROHF/ROKS forces are computed by central finite differences of the (verified) energy — analytic restricted-open-shell gradients are a later milestone (HANDOVER_ROHF.mdM5b). Correct but slower than analytic-force methods; use UHF/UKS for large fast relaxations.Hessians/frequencies (
hessian=True) remain gated for ROHF/ROKS: the FD Hessian differentiates the analytic gradient, which doesn’t exist yet — the gate now raises a precise pointer to M5b rather than blocking optimisation too.
Added: restricted open-shell Kohn–Sham (ROKS) — molecular single point (2026-06-16)¶
method="roks"inrun_job, andvibeqc.run_roks/vibeqc.ROKSOptions/vibeqc.ROKSResult. The DFT counterpart of ROHF: a spin-pure restricted open-shell Kohn–Sham determinant. Reuses ROHF’s Roothaan single-effective-Fock coupling verbatim (vibeqc.rohf.run_roothaan_scf); the only difference is the per-spin KS Fock build —F_σ = Hcore + J − α_HF·K(D_σ) + V_xc,σwith the spin-polarised libxc XC potential and the global-hybrid exact-exchange admixture.The spin-polarised XC potential matrix (LDA + GGA, symmetric ∇ρ flux terms) is assembled in Python (
vibeqc.roks.build_uks_xc_potential), ported from the PySCF-validated UKS Hessian XC build (hessian_analytic_uks).Supported: LDA, GGA, and global-hybrid (B3LYP, PBE0, …) functionals. Meta-GGA (τ-dependent), range-separated hybrids (ωB97X, CAM-B3LYP, HSE) and double hybrids raise a clear
NotImplementedError(roadmap;HANDOVER_ROHF.md).Citation:
roothaan_rohf_1960viaroutes.methods["roks"]; the functional’s own citation fires throughroutes.functionalsas usual.ROKSResultcarriese_xc/e_coulomb/e_hf_exchange/e_nuclear, so the SCF-log energy-components block renders for ROKS.Tests (
tests/test_roks.py): ROKS-on-singlet == RKS (LDA/PBE/B3LYP), doublet spin-purity, unsupported-functional refusal, PySCF ROKS parity. The KS Fock builder was additionally checked to reduce exactly to the ROHF builder at α_HF=1 with zero XC.
Added: restricted open-shell Hartree–Fock (ROHF) — molecular single point (2026-06-16)¶
method="rohf"inrun_job, and the publicvibeqc.run_rohf(molecule, basis, options)/vibeqc.ROHFOptions/vibeqc.ROHFResult. vibe-qc’s first spin-restricted open-shell reference: one set of spatial orbitals partitioned into doubly-occupied (closed), singly-occupied (open) and virtual shells, giving a spin-pure single determinant (⟨S²⟩ = S(S+1) exactly, no spin contamination — the standard reference for ROHF-MP2 / ROHF-CC and a cleaner CAS start than UHF).Implemented as a pure-Python driver (
python/vibeqc/rohf.py) on the same two-electron JK seam the C++ SCF drivers expose (make_direct_jk_builder), using Roothaan’s single effective Fock operator (Coulson coupling; Roothaan, Rev. Mod. Phys. 32, 179 (1960)). Additive — touches no C++ core, so all existing paths are unchanged. DIIS + canonical orthogonalisation + SAD (Hcore-fallback) guess; honours density-fitting viaROHFOptions.density_fit/aux_basis.Output flows through the existing open-shell writers (SCF-log orbital table, Molden, spin density, population) — the ROHF result exposes the per-spin attribute surface with identical α/β spatial orbitals.
Citation:
roothaan_rohf_1960fires viaroutes.methods["rohf"](pinned in the citation coverage gate’s required-methods set).Scope this increment: molecular single-point energy only. Analytic ROHF gradients (→ geometry optimisation / Hessian), ROKS, post-SCF ROHF references and periodic ROHF are gated with clear
NotImplementedErrors and tracked inHANDOVER_ROHF.md(milestones M3–M7).Tests:
tests/test_rohf.py— a pure-math tier (Roothaan block structure, closed-shell→RHF reduction, SCF stationarity; verified locally without the compiled core) and an end-to-end tier (ROHF-on-singlet == RHF, doublet spin-purity, PySCF ROHF parity) that runs on a build box.
Added: MSINDO Ag + Cd (Z47–48) — occupied-4d In–Xe-loop ENEG (2026-06-16)¶
MSINDO/INDO now also supports Ag (Z47, 4d¹⁰5s¹) and Cd (Z48, 4d¹⁰5s²), oracle-validated to ≤1 µHa across closed-shell hydride/oxide/halides (AgH/AgF/AgCl, CdO/CdF2/CdCl2). The fix was again a category bug, isolated to
eneg():atomic_reference.fcomputes Ag/Cd twice — first in the Y–Cd loop (DO L=39,48, Sc–Zn-style occupied-d), then the In–Xe loop (DO L=47,54) overwrites them. The final oracle U_ss/U_pp/U_dd therefore use the In–Xe p-block formula carrying the occupied d shell (DEL=ND=10, SEL=LS=1/2), not the Y–Pd occupied-d TM form (which would add the −(DEL−1)·F0DD self-repulsion to U_dd). vibe-qc’s_TMset stops at Pd(46), so Ag/Cd fell through to the general (pel−1) p-block path, which drops the DEL terms → wrong U for both. A dedicated {47,48} branch ports the In–Xe-loop formula;eff_core_charge(z−36) andn_d_principal(4d) were already correct, andateng()’s PEL=0 path already reduces to the right 2·U_ss + F0SS. Python-only (no C++ rebuild); H–Mo/In/Sn unchanged (branch dormant for all other Z; fulltests/test_msindo.pygreen). Still gated (await thestewart.fpseudo-diagonalization SCF port): the open-d 4d metals Tc–Pd and the heavier lone-pair 5p Sb/Te/I/Xe.
Added: megacell (finite-supercell) periodic MP2 + DLPNO-CCSD(T) — local-correlation Stage 6 (2026-06-15)¶
vibeqc.periodic_megacell_mp2.megacell_mp2(system, basis, nrep)→MegacellMP2Result: the first periodic correlation energy in vibe-qc. Replicates the unit cell into a finiten1×n2×n3supercell (build_supercell_molecule) and runs molecular HF+MP2 (run_mp2), reporting the correlation energy per unit cell. This is the megacell strategy (Nejad et al. 2025, Paper II); the periodic local-correlation route Stage 6.A finite supercell is a molecular calculation, so there is no periodic exxdiv / G=0 divergence — the MP2 denominator is clean and the per-cell energy converges to the thermodynamic limit as the megacell grows. (This is why the megacell route avoids the orbital-energy boundary that the Γ-Bloch route faces.)
megacell_run_job(system, basis, nrep, method="dlpno-ccsd(t)")→MegacellJobResultdispatches the supercell through the molecularrun_jobfor the local methods (dlpno-mp2/dlpno-ccsd/dlpno-ccsd(t)): the megacell route to periodic DLPNO-CCSD(T), reusing vibe-qc’s validated molecular DLPNO.megacell_mp2_tdl(system, basis, sizes)→MegacellTDLResultextrapolates the per-cell correlation energy to the thermodynamic limit: it fitsE_corr(N) = b·N + aover a supercell size series, so the bulk per-cell energy is the slopeb(the finite-N averageE_corr(N)/Nis edge-contaminated;ais the two-end surface term). Runnable demo:examples/periodic/periodic-megacell-local-correlation.py.Cross-validated against PySCF (§10):
tests/test_periodic_megacell_pyscf_parity.py(gatedimportorskip) confirms megacell MP2 == PySCF molecular MP2 on the identical supercell to ~1e-13. The translational-symmetry pair reduction (Stage 5) and the PySCF.pbc KMP2 thermodynamic-limit cross-check remain follow-ups.Toroidal (BvK) megacell periodic Γ RI-MP2 landed — Stage 5a (
python/vibeqc/periodic_toroidal_mp2.py): the first native periodic correlation energy from vibe-qc’s own periodic machinery. A periodic (toroidal/BvK) supercell runs periodic Γ GDF HF → periodic Γ DF 3-index tensor (Stage 4) → RI-MP2 energy per cell. By the BvK theorem, a Γ-point calculation on an N-cell supercell equals an N-point k-mesh calculation on the unit cell — validated cell-for-cell against PySCF.pbc KMP2 at matching k-mesh viarun_periodic_mp2(out-of-process, §10): agreement ~0.18 mHa at nk=2, 0.05 mHa at nk=4 on the H₂/STO-3G chain. The open megacell stays the validated extrapolation route; this is the native periodic infrastructure for 5b (translational pair-family decomposition) and 5c (DLPNO on the toroidal megacell). Tests (tests/test_periodic_toroidal_mp2.py, 4): KMP2 oracle at nk=2,4; structure/sanity invariants.Tests (
tests/test_periodic_megacell_mp2.py, 6): the(1,1,1)megacell equals molecular MP2 and molecular DLPNO-CCSD(T) on the unit cell to <1e-10; per-cell correlation converges and extrapolates to the bulk; geometry + bookkeeping.
Performance: Γ GDF Ewald-JK fallback reuses the cached analytic-FT Hartree J (2026-06-16)¶
run_rhf_periodic_gamma_gdf / run_rks_periodic_gamma_gdf build their
Hartree J through the EWALD_3D analytic-FT builder on the 3D Ewald-JK
fallback path (tight cells whose GDF cutoff includes image cells;
backend="ewald-jk-fallback"). That call site still rebuilt the
density-independent AO-pair Fourier mesh (ke=200), the dense G-mesh and the
4π/G² kernel every SCF iteration — the one EWALD_3D J consumer the
2026-06-11 EwaldJFTGammaCache rollout (PBC audit
docs/pbc_audit_2026-06.md §E2) did not reach. It now builds that
machinery once per SCF via make_ewald_3d_gamma_j_builder and redoes
only the O(n²·n_G) density contraction each iteration (the same “build once,
contract per iteration” pattern as the driver’s own Lpq cache). Energies
are bit-identical — pure caching, no physics change: H₂/STO-3G/12-bohr Γ
Ewald-JK fallback stays -1.116744724149135 while the analytic-FT cache
builds drop n_iter+1 → 1. The diagnostic VIBEQC_J_EWALD3D_BACKEND=grid
path stays uncached. Covered by tests/test_ewald_j_cache.py
(test_gamma_builder_matches_build_j_ewald_3d pins the cached builder
byte-for-byte against the per-iteration build_j_ewald_3d).
Added: periodic case for READ / ATOMSPIN / SPINLOCK initial-guess + convergence features (2026-06-16)¶
The three initial-guess / convergence features that landed molecular-only (READ, ATOMSPIN, SPINLOCK) now work on periodic SCF, building on the molecular implementations (no molecular behavior change).
READ (Γ-point periodic restart).
vq.run_periodic_job(..., initial_guess="read", read_from=prior_result_or_path)restarts a Γ-point periodic SCF from a prior periodic result (in-memory) or a.qvf/.moldenfile. The prior g=0 cell density is projected onto the current cell basis (the MINAO-style projector evaluated with the cell overlaps) and injected at g=0, where it Bloch-sums to a k-independentD(k)for the first Fock build — the NEB / geometry-scan / extrapolation restart path on slabs and bulk. Wired on the Ewald, GDF, and BIPOLE Γ drivers. Multi-k per-k restart needs complex Bloch coefficients (the QVF wavefunction.gto section is Γ-only) and is out of scope: a multi-k READ raises with a roadmap pointer. New periodic resolvers inpython/vibeqc/guess_read.py;read_density{,_alpha,_beta}+read_pathcarried onPeriodicRHFOptions/PeriodicKSOptions.ATOMSPIN (periodic broken-symmetry seed).
atomic_spins(per-atom +1/-1/0, unit-cell atom order) now seeds an AFM / ferrimagnetic g=0 cell spin pattern that Bloch-sums to a broken-symmetryD(k)for periodic UHF / UKS. The C++GuessEngine::build_open_shellalready assembled the block-diagonal broken-symmetry density (is-periodic-agnostic); the periodic open-shell guess shim, thevibeqc.guesshelper, and the periodic drivers now threadatomic_spinsthrough. Wired on the Γ UHF/UKS Ewald, GDF, and BIPOLE drivers and the multi-k UKS Ewald driver (the AFM-solids flagship); the multi-k UHF Ewald driver (Hcore-only guess) and closed-shell methods fail closed.SPINLOCK (periodic magnetic convergence).
PATTERN_HOLDholds the seeded broken-symmetry occupied set by maximum overlap (MOM,python/vibeqc/mom.py) for the firstspinlock_iterationscycles, then releases — wired on the Γ UHF/UKS Ewald, GDF and BIPOLE drivers and the multi-k UKS Ewald driver (the path that protects anatomic_spinsAFM seed against collapse on multi-k).SPIN_SCHEDULEruns a two-phase periodic SCF — lockn_alpha-n_beta = spinlock_valueforspinlock_iterationscycles, then restart at the multiplicity target from that density via the READ machinery — on the Γ UHF/UKS Ewald, GDF and BIPOLE drivers. Both modes are reachable throughvq.run_periodic_job(..., method="UHF"/"UKS", spinlock="pattern_hold"/"spin_schedule", spinlock_iterations=N[, spinlock_value=M]). Drivers that do not implement a requested mode (the multi-k UHF Ewald path for any mode; multi-k UKS Ewald forSPIN_SCHEDULE) fail closed with a clear error.spinlock_mode/spinlock_value/spinlock_iterationscarried on the periodic options structs;mom.reorder_occupied_by_max_overlapmirrors the C++mom_reorder_occupied; the GDF/BIPOLE PATTERN_HOLD reuses each driver’s per-k MOM kernel;python/vibeqc/spinlock_periodic.pyholds the two-phase orchestration via a driver-agnostic phase-runner thunk (so the Ewald, GDF and BIPOLE call signatures all share one helper).No new citable methods (READ is a mechanical restart; ATOMSPIN/SPINLOCK mirror the CRYSTAL conventions; the MOM reference (Gilbert/Besley/Gill 2008) already ships with the molecular SPINLOCK).
Validated (
tests/test_guess.py): periodic Γ ATOMSPIN reproduces the molecular-limit UHF broken-symmetry energy to < 1 µHa and matches PySCF.pbc UHF broken-symmetry ⟨S²⟩ (out-of-process,examples/regression/parity_periodic_atomspin_vs_pyscf.py); Γ READ round-trip (in-memory +.qvf) reaches the same energy in fewer iterations; periodic SPINLOCK PATTERN_HOLD no-op on a held case and SPIN_SCHEDULE release-to-target; multi-k READ + unsupported-driver SPINLOCK gates raise.
Added: per-spin (open-shell) Fermi-Dirac smearing for multi-k GDF (2026-06-15)¶
run_kuhf_periodic_gdf/run_kuks_periodic_gdfnow honorsmearing_temperature > 0: independent per-spin chemical potentials μ_α, μ_β determined by a global Fermi level across the BZ (the CP2K/VASP/Quantum-Espresso convention), replacing per-k hard Aufbau — which is exact only for gapped cells and mis-occupies metals / band-overlap / partially-filled degenerate open shells. The result carriesfermi_level_alpha/beta,entropy,free_energy(MerminA = E - T·(S_α+S_β)), and per-spin fractionaloccupations_alpha/beta.vibeqc.smearing.apply_smearing_open_shell— the reusable per-spin core: the shared μ-bisection run once per channel withspin_degeneracy = 1.fermi_dirac_occupations_per_k/aufbau_occupations_per_kgained a degeneracy parameter (default 2.0 ⇒ closed-shell bit-identical; no existing caller changes).Behavior-neutral at T = 0: the per-k Aufbau path is untouched, so
UHF(M=1) ≡ KRHF/KUKS(M=1) ≡ KRKSstay machine-precision-equal.Reachable from
run_periodic_job:method='UHF'|'UKS',jk_method='gdf', akpointsmesh, andsmearing_temperature > 0now route to the per-spin-smearing drivers (passkpoints=(1,1,1)for a Γ-point open-shell smeared run).Validated (
tests/test_periodic_kuhf_gdf.py,test_smearing_open_shell.py):KUHF(M=1)+smear ≡ KRHF+smear; per-spin particle conservation; a boron 2p¹ degenerate shell fractionally occupied (~1/3 each) withA < E, both directly and throughrun_periodic_job.Not yet wired (raise an actionable error): Ewald-3D multi-k UHF/UKS and the Γ-only open-shell GDF drivers (
docs/design_smearing.md).
Added: case-partitioned matrix-free RDM CASPT2, caspt2(engine="cases") (roadmap 25i, 2026-06-16)¶
caspt2(..., engine="cases")(IC variant) is an experimental, opt-in CASPT2 backend that never builds the determinant first-order interacting space and never forms the 4-RDM. The FOIS overlapS, RHSVand generalized-FockH₀are assembled as collapsed per-case external×active- RDM “sigma” contractions (organized by excitation case and grouped by external-index signature) and the first-order equation(H₀−E₀S)x=−Vis solved iteratively (MINRES on the matrix-freeS·x/H₀·x), so cost is set by the active RDMs, not the reference determinant count. The 3-RDM entersH₀only through the F3 intermediate (the diagonal-Fock- contracted 4-RDM, 3-RDM-sized). Reproduces the validated explicit/direct engines (hence OpenMolcas&CASPT2) at σ=0 to machine precision, and composes with a selected-CI reference (a determinant-list-agnostic 3-RDM builder), the route to large active spaces.engine="auto"(the default) is unchanged. Unshifted,ipea=0only. The overlap, RHS and all ofH₀(including the off-diagonal generalized-Fock coupling, via external- offset grouping) are collapsed to block sigmas, so the matrix-free matvec has non_cand²term for any reference; the active-RDM contractions inside those sigmas still run through the Python reducer, so the cross-over vs the determinant engines is set by that constant factor (a C++ inner kernel is the next step). Reachable throughrun_job(method="caspt2", caspt2_options=CASPT2Options(engine="cases"))(runs labelledcaspt2(..)_cases).
Fixed: multi-k periodic RKS/UKS gradient — GGA σ XC Pulay + arbitrary basis (2026-06-16)¶
Multi-k periodic DFT atomic gradients (
compute_gradient_periodic_rks_multi_k,compute_gradient_periodic_uks_multi_k) now compute the full GGA XC Pulay term. Both multi-k drivers routed their XC Pulay through a molecular-grid fallback that dropped the GGA σ-coupled piece (LDA part only) — the gap the Γ-only G1b path had already closed. Rerouting both to the lattice-summed periodic primitivexc_lattice_gradient_contribution[_uks](the kernel the Γ path uses) cuts the multi-k PBE error from ~2.0e-2 to ~1.6e-3 Ha/bohr vs the molecular analytic gradient (H₂O/STO-3G, 20-Å box, 1×1×1); closed-shell UKS-PBE now reproduces RKS-PBE to ~1e-15.Removed the hard-coded STO-3G shell→atom table. The retired fallback (
_xc_pulay_molecular_fallback,_uks_…,_atom_bf_indices*) raisedNotImplementedErroron any non-STO-3G basis; the lattice primitive derives shell→atom from the basis, so multi-k DFT gradients now run for arbitrary bases (regression: 6-31g H₂ multi-k PBE).Tests:
tests/test_periodic_gradient_g1c.py(GGA σ vs molecular [@slow]; 6-31g runs),tests/test_periodic_gradient_g1d.py(UKS-PBE ≡ RKS-PBE).On the multi-k-vs-molecular offset: the multi-k analytic gradient is FD-consistent — it matches a finite-difference gradient of the multi-k Ewald SCF energy to ~1.5e-4 Ha/bohr (step-limited), so it correctly differentiates the energy it is built from. The larger ~1.6–1.8e-3 Ha/bohr offset against the molecular analytic gradient (present for LDA too) is the multi-k Ewald SCF surface not yet at the isolated-molecule limit in a 20-Å box — an SCF-driver property, not a gradient error, and unrelated to this XC fix.
Added: corrected-gauge Γ analytic gradient — all four drivers (M3, 2026-06-15)¶
compute_bipole_gradient_{rhf,uhf,rks,uks}now support corrected-gauge (Ewald-exchange-split) results at Γ — previously all four refused them (the analytic kernels implemented only the legacy Γ-local gauge). The corrected gauge is a standard variational HF/KS gradient (full Bloch density → no Bloch-CPHF): the legacy assembly with the exchange block swapped toK_SR(erfc) + K_LR(recip) + (ξ_M − π/Vω²)·S·D·S(no spheropole), the gradient kernels fed a homogeneous-on-template density, and the full jellium gradient (v_bg ∝ Tr[D S]is quadratic; the W from the variational eigenvalues supplies the cancelling overlap term). UHF/UKS use spin-resolved exchange2·(∂E_x[Pα]+∂E_x[Pβ])withW = W_α + W_β; RKS/UKS add the fixed-grid XC Pulay + grid-motion correction and scale the HF exchange by the functional’s fraction (0 for pure DFT). All matchcompute_bipole_gradient_fdto ≤1e-6 Ha/bohr (RHF 1.2e-8 MgO; UHF 2.4e-8 BeH; RKS LDA/PBE0 1.2e-7 H₂; UKS LDA 1.1e-6 triplet H₂). New_k_long_range_ewald_gradientkernel (reciprocal long-range exchange gradient, the exchange analogue of_j_long_range_ewald_gradient). Multi-k corrected gauge still refuses all four drivers (per-k q-channel K_LR + supercell Madelung gradient pending). FD remains the production force path.
Added: READ initial guess restarts an SCF from a prior result / file (2026-06-15)¶
InitialGuess.READrestarts a molecular SCF (RHF / UHF / RKS / UKS) from a prior calculation’s density instead of a from-scratch atomic guess. Three sources:in-memory via the new
read_from=kwarg:run_*(mol, basis, opts, read_from=prior_result);a vibe-qc
.qvffile:opts.read_path = "prev.qvf"(itswavefunction.gtosection);a
.moldenfile:opts.read_path = "prev.molden".
The prior density is expressed in the current AO basis: used directly when the basis and geometry match, otherwise projected with the MINAO-style least-squares AO projector
D = P·D_prior·Pᵀ,P = S_tt⁻¹·S_tm, backed by a newcompute_overlap_two_basiscross-basis AO overlap. This restarts across a changed geometry/basis: the NEB / geometry-scan path.The periodic input-string guess parser also accepts
moread/coreadas aliases forREAD.Molecular-only: a periodic SCF selecting
READraises a roadmap-pointer error. READ is a mechanical restart (no defining paper) and carries no citation, like HCORE.Tests (
tests/test_guess.py): in-memory /.qvf/.moldenround-trips (RHF + UHF + RKS) converge to the from-scratch energy in far fewer iterations; the projection path reaches the new-geometry minimum from a prior.qvf; missing-source and unknown-extension inputs raise.
Added: periodic Γ-point density-fitted 3-index integrals — local-correlation Stage 4 (2026-06-15)¶
vibeqc.periodic_df.build_periodic_gamma_df(system, basis)→PeriodicGammaDF: exposes the periodic Γ compcell-GDF 3-index cderiLpq(shape (n_fit, nbf, nbf),(μν|λσ) ≈ Σ_L Lpq[L,μν] Lpq[L,λσ]) plus.build_J/.build_K/.mo_transform, for transformation into the Wannier/PAO basis. Built via the publicvibeqc.aux_basis.build_lpq_compcell(no SCF-driver internals). Stage 4 of the periodic local-correlation route.The periodic GDF SCF builds this tensor and discards it; this re-exposes it. Verified bit-identical to the SCF — J from the extracted tensor + the converged density reproduces
PBCGDFResult.e_coulomb(|Δ| = 0).Γ-point, compcell GDF only. The periodic-MP2 finite-size / exxdiv-for-correlation (G=0) treatment and multi-k
Lpq(k)are later stages.Tests (
tests/test_periodic_df.py, 5): tensor shape + AO symmetry, the Coulomb-energy oracle, exchange symmetry, and 4-index permutation symmetry.
Added: matched-k CRYSTAL reference for the BIPOLE↔CRYSTAL gate (2026-06-14)¶
Sealed
examples/regression/crystal_parity/crystal_demos/mgo_sto3g_shrink22.{d12,out}— a matched-k CRYSTAL23 reference for MgO at SHRINK 2 2 (−271.85640 Ha/FU, 3 IBZ k-points), generated out-of-process from a local CRYSTAL binary (§10, validated by reproducing the committed SHRINK-8-8 ref −271.21814375 to all digits). This is the correct reference for a matched-k BIPOLE↔CRYSTAL gate — the converged SHRINK-8-8 value over-binds by 0.64 Ha at this coarse mesh, so comparing BIPOLE@2×2×2 against it (as the staleparity_mgo_shrink22script did) is not a tight same-family check.docs/periodic_jk_routes.md§ Current status now records the precise gate state: the reference side is ready, but BIPOLE multi-k needscutoff ≥ ~18for converged lattice sums (S(k)-fold < 1e-4; at cutoff-8 it is 0.38 → the energy is junk) and is then >10 min even at the 8-k 2×2×2 net — the PBC-audit E2/E3 multi-k efficiency follow-up (BIPOLE multi-k lane). The@slowgate awaits that.
Added: periodic Γ-point PAO virtual space — local-correlation Stage 3 (2026-06-15)¶
vibeqc.periodic_pao.build_pao_periodic_gamma(result, basis, system): builds the virtual-space projectorQ = 1 − C_occ C_occᵀ Sand an S-orthonormal, Fock-diagonal virtual basis over the home cell (PeriodicPAOResult), reusing the molecular PAO machinery (vibeqc.dlpno.pao). Stage 3 of the periodic local-correlation route ([HANDOVER_PERIODIC_LOCAL_CORRELATION.md]).At Γ the full-domain basis equals the canonical virtual MOs; the reusable redundant-PAO + projector machinery is what Stage 5 restricts to per-pair local domains. Γ-point only.
Tests (
tests/test_periodic_pao.py, 6): occupied-orthogonality, S-orthonormality, Fock-diagonality, completeness, and an oracle — semicanonical PAO energies reproduce the SCF virtual orbital energies to ~1e-8 on H₄/STO-3G in a 30-bohr box.
Added: MSINDO element coverage reaches the 5th row — Rb–Mo + In/Sn (2026-06-15)¶
MSINDO/INDO now supports Z 1–42 (H–Mo) plus In and Sn (Z49–50), all oracle-validated to ≤1 µHa across diverse molecules/geometries. Landed:
5th-row s-block + 4d transition metals (Rb, Sr, Y, Zr, Nb, Mo). The fix was a category bug: vibe-qc’s
_ALKALI/_ALKALINE_EARTH/_TMENEG sets omitted the 5th-row analogs, so Rb/Sr/Y–Mo hit the wrong atomic-reference formula (e.g. Rb wrongly took the closed-shell F0SS correction → RbH was 0.08 Ha off). Adding Rb→alkali, Sr→alkaline-earth, Y–Pd→_TMmakes them exact —atomic_reference.f’s Y–Cd block is term-for-term identical to Sc–Zn. (RbH/RbF/RbCl, SrH2/SrF2/SrO, YF3/YCl3, ZrF4/ZrCl4, NbF5/NbCl5, MoF6/MoCl6 all ≤1 µHa.)5p In + Sn — the [Kr]/[Kr]4d¹⁰ frozen-core penetration (4s/4p Z>36, 4d Z>48 in
_rumpf_overlaps/V2CORE) and the In–Xe d-shellENEGform (Ga–Kr-style negated G1PD/G3PD), both matchingatomic_reference.f. H–Kr behaviour is unchanged (new branches dormant for Z≤36; fulltests/test_msindo.pygreen). Still gated, each with a named residual: the later 4d metals Tc–Pd (partner-dependent terms; Rh/Pd-atom edge cases) and the lone-pair 5p Sb/Te/I/Xe (a spurious 5d occupation in lone-pair molecular fields). (Ag/Cd landed separately — see the 2026-06-16 entry above.)
Added: periodic Γ-point orbital localization — Pipek-Mezey + Foster-Boys (2026-06-15)¶
vibeqc.periodic_localise.localise_periodic_gamma(result, basis, system, method=…): localizes the occupied Bloch orbitals of a converged Γ-point periodic SCF into Wannier-like orbitals, returningPeriodicWannierResult(localized coefficients, unitary mixingU, Wannier centers, true position centroids ⟨i|r⟩, per-orbital spreads ⟨i|r²⟩−⟨i|r⟩², and Mulliken populations). Reuses the molecular localizer (vibeqc.localise). Periodic local-correlation route, Stage 2 ([HANDOVER_PERIODIC_LOCAL_CORRELATION.md]).method="pipek-mezey"(2a): Mulliken-population objective — PBC-safe in every regime (no position operator), including crystals whose orbitals wrap the cell.method="boys"(2b): Foster-Boys via the home-cell dipole integrals; true centroids and spreads via the dipole + quadrupole (compute_multipole_moments_lattice, emultipole2). Faithful when the occupied orbitals do not wrap the cell boundary (molecule-in-box / dilute / large-cell). Dense crystals need the Resta periodic position operator⟨μ|e^{−iG·r}|ν⟩(a documented follow-up; not yet implemented), as does the multi-kU(k).Γ-point only. Unknown methods raise
ValueError(fail closed).Tests (
tests/test_periodic_localise.py, 12): unitary mixing, occupied-density invariance, objective increase, populations summing to 1, positive/symmetric spreads, and molecular-oracle matches — periodic Γ PM reproduces molecular PM to ~1.5e-8 (objective) / ~2e-6 bohr (centers), and periodic Boys centroids match molecular Boys to ~2e-6 bohr, on H₄/STO-3G in a 30-bohr box.
Added: SPINLOCK broken-symmetry convergence (spin-schedule + MOM pattern-hold) (2026-06-14)¶
UHFOptions/UKSOptionsgainspinlock_mode(off by default),spinlock_iterations, andspinlock_value: a user-selectable, opt-in analogue of CRYSTAL’s SPINLOCK for broken-symmetry magnetic convergence, with two modes the user picks viavibeqc.SpinlockMode:SPIN_SCHEDULE: converge at a lockedn_alpha - n_beta = spinlock_valuefor the firstspinlock_iterationscycles, then restart at the multiplicity target from that density (CRYSTALSPINLOCK n nstep). Two sequential SCFs inrun_uhf/run_uks, so the core loop is untouched; the locked phase polarises (e.g. high-spin) before relaxing.spinlock_valueis validated against the electron count.PATTERN_HOLD: hold the seeded (atomic_spins) broken-symmetry occupied set by maximum overlap (MOM) for the firstspinlock_iterationscycles, then release. New C++ MOM kernel (cpp/include/vibeqc/mom.hpp), the C++-runtime mirror ofpython/vibeqc/mom.py(Gilbert-Besley-Gill 2008) used by the periodic multi-k drivers, kept algorithmically in lock-step. Protects an atomic_spins seed against early collapse on the periodic multi-k path; it is a bit-for-bit no-op on every molecular system tested (H2/H4/H6) because the density seed already holds the pattern, and ships opt-in for CRYSTAL parity and the future periodic hard-case path.
Both modes are off by default; the existing single-point SCF path is unchanged.
Tests (
tests/test_guess.py): SPIN_SCHEDULE releases to the target (H2 equilibrium, lock triplet then converge to the singlet RHF energy) and validatesspinlock_valueparity; PATTERN_HOLD is a verified no-op on the converged result.
Added: ATOMSPIN per-atom broken-symmetry spin seed (atomic_spins) (2026-06-14)¶
UHFOptions.atomic_spins/UKSOptions.atomic_spins(a list of +1/-1/0 aligned to atom order) seed a broken-symmetry SAD start, the vibe-qc analogue of CRYSTAL’s ATOMSPIN: +1 tags an atom majority-alpha, -1 majority-beta, 0 unpolarised. This unlocks antiferromagnetic / ferrimagnetic initialisation, which the spin-symmetric SAD split cannot reach (the unrestricted SCF otherwise collapses to the most symmetric solution).Mechanism (
GuessEngine::build_open_shell, molecular UHF/UKS): each tagged atom contributes a Hund’s-rule spin-resolved atomic density (open-subshell unpaired electrons placed in the majority spin, e.g. Fe d6 -> moment 4, O 2p4 -> 2), assembled block-diagonally into D_alpha/D_beta. Density-mode for both UHF and UKS; the SCF then refines the pattern and enforces the global n_alpha/n_beta from the multiplicity. Consulted only when the resolved guess is SAD (incl. AUTO->SAD for open shells); pairing it with any other guess raises.Tests (
tests/test_guess.py): atomic_spins=[+1,-1] on stretched H2 (STO-3G, R=3.0 bohr) reaches the Coulson-Fischer broken-symmetry minimum E=-0.95101800 Ha, <S^2>=0.774 (PySCF parity) that the symmetric guess misses; AUTO routing, global-sign-flip equivalence, the SAD-guess and length guards, and UKS forwarding (B3LYP) are pinned.Molecular this increment; periodic-Gamma wiring is a follow-up.
Added: Mixed Density Fitting (MDF) for periodic GDF — Γ + multi-k, closed + open shell (2026-06-14)¶
gdf_method='mdf'inrun_pbc_gdf_{rhf,uhf,uks}adds Mixed Density Fitting (Sun, Berkelbach, McClain & Chan, J. Chem. Phys. 147, 164119 (2017), the GDF paper’s titular method) to close the all-electron Gaussian-DF accuracy floor. Pure Gaussian DF (compcell / rsgdf) cannot fit steep core pair densities at a tractable plane-wave cutoff; MDF handles the steep part in real space (exact libint Gaussian 2c/3c on the compensated “fused” basis — no mesh) and only the smooth residual via plane waves on a modestmdf_ke_cutoffmesh (default 40 Ha).vibeqc.aux_basis.build_lpq_mdfbuilds the cderi_MdfCderi(L_gauss, cderi_pw): the compensated-Gaussian fit orthogonalised on the PW-dressed metric J̃ (Eq 20) with the Eq-23 G=0 self-termV̄_P = −π^{5/2}·(A@m), plus the plane-wave residual√coul(G)·ρ_μν(G). The Γ J/K builders are now complex-aware (the real compcell/rsgdf path stays bit-identical).Validated (
tests/test_pbc_gdf_mdf.py): the no-PW limit is bit-identical to compcell; the reconstructed ERI matches the dense-mesh rsgdf fit and is mesh-stable; on a Ne-in-box all-electron test MDF is mesh-converged by ke≈60 and matches PySCF MDF (matched aux) to 0.14 mHa, while rsgdf at ke=200 is ~190 mHa over-bound (the floor MDF removes); triplet H2 UHF/MDF matches PySCF UHF Γ to 9.5 µHa (⟨S²⟩=2.0).Multi-k via
build_lpq_bloch_mdf+gdf_method='mdf'inrun_k{rhf,uhf,uks,rks}_periodic_gdf: the per-(k_bra,k_ket) cderi is the q-only real-space Gaussian fit + the ket-resolved PW residual (+ the G=0 term on diagonal pairs). H2/sto-3g kmesh=(2,1,1) reproduces the published KRHF reference (−1.12013988, Sun-Berkelbach 2017 Table II) to 21 µHa.Reachable from the high-level runner:
run_periodic_job(..., jk_method="gdf", gdf_method="mdf", mdf_ke_cutoff=...)(new params;gdf_method=Noneselects rsgdf — and at Γ closed-shell RHF now also routes throughrun_pbc_gdf_rhfwith exxdiv=’ewald’, see the 2026-06-15 default-Γ fix below). Also via the directrun_pbc_gdf_*/run_k*_periodic_gdfdrivers.Scope: Γ + multi-k, RHF/UHF/UKS. The Eq-23 G=0 self-term is monopole-only and validated exact (H2
|gap − monopole| = 7.5e-9; the compensated aux carry no dipole) — there is no higher-multipole term. The small residual (0.14 mHa Ne / 21 µHa H2 multi-k) is the modest-mesh fit-representation difference, amdf_ke_cutoffconvergence knob, not missing physics. Same citation as the GDF/rsgdf route (Sun-Berkelbach 2017) — no new entry.
Fixed: default Γ closed-shell RHF/GDF now exxdiv=’ewald’ (PySCF parity) (2026-06-15)¶
run_periodic_job(method="RHF", jk_method="gdf")at Γ now matches PySCF. A plain (dim=3, neutral, no smearing / symmetry / Fock-mixing) Γ closed-shell RHF/GDF job now routes through the PySCF-µHa-validatedrun_pbc_gdf_rhf(exxdiv='ewald', rsgdf) — the same driver the explicitgdf_method=path and the open-shell Γ UHF/UKS paths already used — instead of the legacyrun_rhf_periodic_gamma_gdfmolecular-limit (exxdiv=None) bridge. On H2/sto-3g/12-bohr the default energy moves −1.1167447 → −1.1225840 Ha (PySCF KRHF(1,1,1) exxdiv=’ewald’ −1.1225840, ~3 µHa), closing the ~5.8 mHa finite-size Madelung gap that had silently depended on whethergdf_method=was passed.run_krhf_periodic_gdf(kmesh=(1,1,1))HF likewise delegates its Γ fast-path torun_pbc_gdf_rhf(exxdiv=’ewald’), so kmesh=(1,1,1) is the Nk=1 limit of the multi-k exxdiv=’ewald’ path — no convention discontinuity vs (2,1,1)+, andrun_periodic_jobdefault-Γ == explicitkpoints=(1,1,1).The legacy molecular-limit gamma driver is retained as the Γ fallback for the cases
run_pbc_gdf_rhfdoes not handle: RKS, dim<3, charged cells, finite-T smearing, and the symmetry / Fock-mixing convergence aids. Direct calls torun_rhf_periodic_gamma_gdfare unchanged (still the molecular limit). No new citation — the exxdiv=’ewald’ Madelung treatment already shipped inrun_pbc_gdf_rhf.Tests:
tests/test_periodic_rhf_gdf.py(test_run_periodic_job_gamma_rhf_gdf_default_is_exxdiv_ewald,...smearing_stays_on_legacy, the Γ==(1,1,1) identity), andtests/test_periodic_k_gdf.py(test_krhf_gdf_gamma_via_tuple_matches_pbc_gdf_rhf).
Fixed: active-space frozen-core dressing for the determinant solvers (2026-06-15)¶
run_job(..., active_space=(n_active, n_elec))now reports correct absolute energies for genuine frozen-core truncations across the determinant/RDM solvers (fci,selected_ci,cisd,dmrg,v2rdm,transcorrelated_ci). Previously the runner sliced the MO coefficient matrix to a bare orbital block and dropped both the frozen-core one-electron dressing and the constantE_coreoffset, son_active < norbreported only the active-only contribution (≈ −15.5 Ha for CAS(4,4)/H₂O instead of below the −74.97 Ha HF total). The runner now projects via the newHamiltonian.active_space(n_active, n_elec)method, which applies the standard CAS partition — lowest(nelec − n_elec)/2orbitals as a doubly-occupied inactive core — and the proper dressingh̃_pq = h_pq + Σ_c (2⟨pc|qc⟩ − ⟨pc|cq⟩)withE_corefolded into the nuclear-repulsion term. The dressing reuses the CASCI-validated_frozen_core_dressing, sofci/selected_ci/… andcasciagree to numerical precision on the same active window, and the variational sandwichE_FCI ≤ E_CAS ≤ E_HFholds. The strict-xfailcoverage intests/test_solvers_active_space_api.py(audit finding 1) is replaced by positive assertions (variational sandwich + cross-method agreement +active_spacevalidation). Docs:python/vibeqc/solvers/ACTIVE_SPACE.md, the active-space + N₂ static-correlation sections ofdocs/tutorial/non_hf_solvers.md, and the newexamples/wavefunction/n2_dissociation_active_space.py.
Added: MSINDO CIS excited states — singlet/triplet TDA solver (2026-06-14)¶
vibeqc.semiempirical.methods.msindo_cisships a working CIS / TDA excited-state solver for the MSINDO/INDO engine:run_cis(Z, coords, n_states, spin)returns singlet or triplet vertical excitation energies and amplitudes, built on the samevibeqc.excitedkernel as the HF/DFT TDA path, over the INDO(ia|jb)/(ij|ab)integral blocks. The excited-state density-matrix + 2PDM assembly (assemble_cis_densitiesand thecisgrad.fT/D_oo/D_vv/GAMMbuilders, incl. scaled-CIS) is validated against trace/symmetry identities. Method: Foresman, Head-Gordon, Pople & Frisch, J. Phys. Chem. 96, 135 (1992). Tests:tests/test_cis_excitations.py,tests/test_cis_density.py.The analytic CIS gradient (CPHF/Z-vector) is present but gated experimental (
cis_gradient(..., _experimental=True)to opt in) — it currently mis-scales the forces (~4-12x vs FD);cis_gradient_fdis the validated finite-difference path. Analytic validation tracked as Phase 6.
Added: BIPOLE ionic-Γ basin-health diagnostic — surface smearing-straddles-gap (2026-06-14)¶
run_pbc_bipole_rks/run_pbc_bipole_uksnow emit abasin_warning(logged + on the result object) when finite-T smearing straddles the HOMO-LUMO gap and may have settled a near-metallic basin. Root cause (Gap-B): a minimal basis badly underestimates the gap (MgO/STO-3G ~0.3 eV vs ~7.8 eV experimental), so asmearing_temperaturecomparable to it fractionally occupies across the gap and lands a wrong basin hundreds of mHa from the physical solution — MgO RKS Γ atT = 0.01 Ha(≈0.27 eV) landed +330 mHa with a 42 mHa entropy term. The diagnostic (smearing_basin_warninginpbc_bipole_common) triggers when smearing is on, there is a genuine gap (>0.02 eV — avoids warning on true metals), fractional occupations are present, andk_B·T ≳ ½·gap. It is a diagnostic only — it changes no energy and applies no convergence aid (CLAUDE.md §7: surface a basin problem, don’t bury it). The fix it points the user to is a sub-gap smearing (reducesmearing_temperatureuntil the warning clears, or rely on the gap-independent auto-FMIXING). Unit tests pin the trigger logic (straddle / sub-gap / metal / integer-occ / no-smearing / UKS-min-gap-across-spins);docs/troubleshooting.mddocuments the guidance.
Added: PATOM / HUECKEL / MINAO molecular initial guesses (2026-06-14)¶
Three further initial guesses, completing the molecular set behind the
InitialGuessenum (previously scaffolded as roadmap-pointer throws):PATOM (
initial_guess="patom"): SAD density followed by one in-field mean-field re-polarisation step, so the superposed free-atom densities relax in the molecular field before the SCF proper (ORCA PAtom; builds on Van Lenthe SAD). UHF forwards its JKBuilder to the guess engine only for PATOM, so no other guess’s packaging changes.HUECKEL (
initial_guess="huckel"or"hueckel"): parameter-free generalised Wolfsberg-Helmholz / extended-Hückel guess over computed atomic-orbital energies (Wolfsberg-Helmholz 1952, Hoffmann K=1.75 1963, and the parameter-free variant assessed by Lehtola 2019).MINAO (
initial_guess="minao"): free-atom reference densities built in the ANO-RCC minimal contraction, projected onto the target basis (Knizia 2013 minimal-AO concept; ANO-RCC reference data).
All three are density-mode (the engine returns a density the SCF driver consumes directly) and molecular-only in this increment: a periodic SCF with any of them raises a clear “not yet implemented for periodic” error.
Citations wired (
routes.scf_guess.{patom,huckel,minao}) so a run using each lands its references in the.out/.bibtexoutput.Tests (
tests/test_guess.py): each guess reaches the same converged RHF energy as HCORE (sto-3g and 6-31g*), UHF recovers RHF on a closed shell, PATOM reaches the true OH/6-31G* UHF minimum (not the Hcore false minimum), and the periodic paths raise.
Changed: BIPOLE parity re-based onto CRYSTAL — method-family policy (2026-06-14)¶
tests/test_bipole_fock_ewald_exchange.py: the PySCFexxdiv='ewald'Γ pins are relabeled a secondary cross-family check of BIPOLE’s Γ exchange-split convention, not BIPOLE’s primary parity gate (per the new method-family policy indocs/periodic_jk_routes.md). GDF’s PySCF pins are unchanged (correct family); no assertions changed.The primary gate is the same-family BIPOLE↔CRYSTAL total-energy parity at a matched k-net (MgO anchor, SHRINK 8 8, sealed CRYSTAL14 ref −271.21814375 Ha/FU; staged in
examples/regression/crystal_parity/). Wiring it as a routine pytest gate is blocked on BIPOLE multi-k, not the reference: at the matched 8×8×8 k-netrun_pbc_bipole_rhfneedscutoff_bohr ≥ ~20for BvK-torus density coverage and the SCF runs >10 min (the PBC-audit E2/E3 multi-k efficiency follow-up, BIPOLE multi-k lane). Tracked indocs/periodic_jk_routes.md§ Current status; not claimed done.
Fixed: true-periodic Γ gradient density convention (G1a-2, partial) (2026-06-13)¶
compute_gradient_periodic_rhf_gammanow uses the homogeneous Γ density (D(g) = D_Γin every cell — the k=0 Bloch phase is 1 in every image, which is the density the SCF energy is built from) for true-periodic cells, instead of the molecular-limit home-only convention (D(g≠0) = 0). On the 1D H chain (a=2 Å, STO-3G) this cuts the analytic-vs-FD residual from ~0.22 to ~0.099 Ha/bohr. The molecular limit (large cells) is unchanged (home-only D, exact). This revises the earlier “~5e-3 K-derivative-buffer” diagnosis: the dominant error was the density convention, not a localized K-buffer bug. The remaining residual is the SCF↔gradient lattice-sum consistency the multi-k Bloch-density gradient (G1c) closes — compounded on that test by a cutoff-non-convergentDIRECT_TRUNCATEDFD reference (bare-Coulomb lattice sum).test_h_chain_1d_periodic_matches_fdstays xfail (updated reason); HF/hybrid true-periodic production gradients should still use the FD path until G1c.
Added — reduced-scaling local DLPNO-CCSD solver; M3c parity ratchet flips (2026-06-13)¶
vibeqc.dlpno.ccsd_local_solver.run_local_dlpno_ccsd— the genuine per-pair DLPNO-CCSD solver, the v0.13 local-correlation deliverable. Each pair’s CCSD residual is evaluated in its own PNO basis: all amplitudes are projected into the pair’s domain via the PNO overlapS_pq, and the validated spatial closed-shell residual (_ccsd_cs.cs_ccsd_residual) is applied to that small local space — no full-system tensor is formed. This is the real solver physics that supersedes the O(N⁶), 64-bf-capped correctness pilot (dlpno.ccsd).The M3c parity ratchet flips to a hard gate. Because
cs_ccsd_residualis covariant under virtual rotations, the full-domain limit reproduces canonical closed-shell CCSD bit-for-bit — validated to ≤1 µHa on H₂ and H₂O (STO-3G and def2-SVP, canonical and Boys-localised occupieds;tests/test_dlpno_ccsd_solver.py). On H₂ that equals FCI (CCSD≡FCI for two electrons), so the FCI anchor reaches the production solver. The canonical-CCSD cross-check is_ccsd_cs.run_cs_ccsd, FCI-anchored through_ccsd_refand matching PySCF to 2e-11 Ha. This is the CCSD analogue of the M1/M2 DLPNO-MP2 exactness ratchet, now passing with the real local solver.With truncated PNO domains the cross-domain contractions are the controlled DLPNO domain approximation: 99.9 % E_corr recovery at TCutPNO=1e-7 on H₂O/def2-SVP, with genuine PNO compression.
Wired into
run_job:method="dlpno-ccsd"now runs the local reduced-scaling solver (LocalCCSDOptions), retiring the pilot’s 64-bf cap (it carries its own generous, overridablemax_nbfguard).method="dlpno-ccsd(t)"stays on the FCI-anchored pilot until the local (T) lands. User guide, tutorial 51, a new example (examples/molecular/input-h2o-dlpno-ccsd.py), andtests/test_runner_dlpno_ccsd.pyall updated to the new behaviour; the citation surface is unchanged (method-keyed).C++ per-pair residual kernel — the DLPNO local-CCSD hot loop (
cs_ccsd_residual, ~86 % of runtime) now runs in compiled C++ when the core providesvibeqc::dlpno_pair_residual: one call replaces the ~90 small numpy einsums per pair per iteration. Bit-for-bit identical to the numpy kernel (≤3e-14 on the residual; solvere_corragrees to ~1e-17), and 4.1× faster on H₂O/cc-pVDZ, 8.3× on cc-pVTZ (the win grows with the PNO-space size). On by default (LocalCCSDOptions.use_cpp_kernel), with an automatic numpy fallback on cores built before the binding landed;tests/test_dlpno_ccsd_solver.py::TestCppKernelpins the parity.Performance (M3c): two solver improvements, both validated against the parity ratchet (unchanged ≤1 µHa full-domain == canonical CCSD). (1) DIIS error vector fixed — it was the difference of consecutive post-DIIS amplitudes, not the residual
R/D; correcting it cuts a water dimer / cc-pVDZ from 48 to 14 iterations (46 s → 14 s, 3.4×), with a residual-norm convergence gate added alongside the energy gate. (2)LocalCCSDOptions.coupling_radius(bohr) restricts each pair’s occupied coupling set to a local neighbourhood — the linear-scaling lever. Exact at a radius wider than the molecule (bit-for-bit == full coupling) and lossless once fragments separate (a 12-bohr next-nearest H₂ is sub-µHa); on an H₂ chain the mean coupled-occupied count (avg_coupled_occ) saturates while the system grows instead of tracking it. It now defaults to a calibratedcoupling_radius = 12 bohr— a bit-identical no-op on any molecule under ~12-bohr extent (the common case), and on larger systems a controlled approximation kept below the PNO truncation error (~7 µHa on a 25-bohr H₂ chain vs the ~0.1% PNO error — the same controlled-locality bargain as the default sparse pair list). Setcoupling_radius=0for full coupling, the exact reference the parity ratchet pins (tests/test_dlpno_ccsd_solver.py::TestSparseCoupling). The same locality screens the local (T): a triple is evaluated only when its three occupieds are mutually withincoupling_radius(O(N³)→O(N) triples on extended systems), lossless on separated fragments (sub-µHa) and a standard DLPNO-(T0) triple pre-screening on bonded ones (~0.03 kcal/mol on the 25-bohr H₂ chain, below the PNO error;tests/test_dlpno_triples_local.py::TestLocalTriplesScreening). Honest scope: the truncated-domain recovery on compact molecules is still not strictly monotonic in TCutPNO (a separate PNO-truncation effect; the full-domain limit is exact); the C++ port remains tracked inHANDOVER_GATED_ITEMS.md.Local DLPNO-(T):
method="dlpno-ccsd(t)"leaves the O(N⁶) pilot.vibeqc.dlpno.triples_local.local_triples_correctionevaluates the (T) correction on the converged local DLPNO-CCSD amplitudes per occupied triple in a TNO domain (the union of the triple’s pair PNO spaces, truncated by TNO occupation number viatcut_tno— the triple’s pair-amplitude density eigenvalues; default 0 keeps the full span), reusing the spatial closed-shell per-triple kernel_ccsd_cs.cs_triple_energy— a faithful numpy transcription of the validated C++ (T) (cpp/src/ccsd.cpp, classwise spin integration E(T) = 1/18·S_aaa + 1/2·S_aab), anchored to the FCI-validated spin-orbital reference to machine precision (2e-20;tests/test_dlpno_ccsd_cs.py::TestTriplesParity). The (T) parity ratchet: at full domains with canonical occupieds it reproduces canonical CCSD(T) bit-for-bit (≤ 1 nHa on H₂O STO-3G + def2-SVP vsrun_ref_ccsd(compute_triples=True);tests/test_dlpno_triples_local.py). The Boys-localised production path is DLPNO-(T0) (diagonal localised Fock in the denominators); the (T) vanishes (to floating-point noise) for two-electron systems.run_job(method="dlpno-ccsd(t)")now runs the local solver + local (T) (user guide, tutorial 51, example, runner test updated); the O(N⁶) correctness pilot stays reachable by explicitly passing aDLPNOCCSDPilotOptions(the exact-(T) oracle). Honest scope: the spatial (T) carries no spin-orbital redundancy (2× smaller dimensions, 8× fewer triples than a spin-orbital (T)) but is Python-only — a C++ port is the next efficiency step. Citations (Riplinger 2013, Raghavachari 1989) are method-keyed and unchanged.
Changed: EWALD_3D retired as a user route; Γ-only drivers fail closed on image-overlapping cells (§7) (2026-06-13)¶
jk_method='fft_poisson'(Γ-only EWALD_3D) is retired as a user-facing route. It returns a ~2 Ha-wrong energy when periodic images of the basis overlap, and is superseded everywhere by GDF (default) / BIPOLE / GPW.validate_jk_methodnow raises pointing at those routes (AUTO never selected it); the dispatcher branch is a defense-in-depth fail-closed. The internal driversrun_r{h,k}f_periodic_gamma_ewald3dremain for dilute periodic systems and mechanics tests.The internal Γ-only drivers now fail closed rather than hand back the wrong number (CLAUDE.md §7): they raise when the largest cross-cell AO overlap
|S_μν(L≠0)|exceeds ~0.3 — a basis-aware molecular-limit- breakdown signal, not a geometric one. Re-measured this cycle (verify-first): only LiH is actually buggy — overlap 0.41, EWALD_3D −35.9655 vs GDF −33.9940 (Δ=1.97 Ha), because Li’s STO-3G 2s/2p are extraordinarily diffuse (α=0.048). MgO (overlap 0.19, Δ=0.1 mHa) and NaCl (0.12, <1 µHa) run normally — the baseline had assumed them buggy without re-running. (LiH and MgO have near-identical nearest-image contact, 3.86 vs 3.98 bohr, so a geometric criterion would wrongly refuse fine MgO.) An internalallow_dense_ionic=Trueescape hatch lets mechanics tests exercise the driver on such a cell; the energy it returns is then explicitly unreliable.tests/test_periodic_dense_ionic_energy_parity.pyrewritten to assert the fail-closed RAISE on LiH and the surgical sparing of MgO/NaCl (with GDF as a live PASS guard), and removed from the test-gateknown_redsbaseline → zero tracked reds.Fixed 8 latent
plog.warning(...)calls in the periodic EWALD drivers (Γ + multi-k, RHF/RKS/UHF/UKS):ProgressLoggerexposes.warn, not.warning, so the “auto_optimize_truncation did not converge” path raisedAttributeErrorinstead of warning.
Fixed: post-HF run_job result proxies expose .method (v0.13.0 audit, 2026-06-13)¶
The six attribute-forwarding result wrappers (
_CCSDAugmented,_MP2Augmented,_DLPNOMP2Augmented,_DLPNOCCSDAugmented,_OVGFAugmented,_DispersionAugmented) forwarded an unknown.methodto the wrapped C++RHFResult, which has no such attribute, sorun_job(method="ccsd").methodraisedAttributeErrorand the defaultcitations=Truepath crashed intermittently (hash-order-dependent) on CCSD / CCSD(T) / MP2 / DLPNO / OVGF results. Each proxy now carries its own.methodlabel (the effective method string:ccsd,ccsd(t),scs-mp2,dlpno-mp2, …), matching the.methodcontract thatSolverResultalready satisfies. Found and fixed during the v0.13.0 CC/CI surface audit; pinned intests/test_runjob_cc_ci_surface.py. Touches the shared runner result-proxy classes only (no engine/physics change).
Verified (v0.13.0 audit): CAS-family + CC/CI run_job surfaces are reachable and cite (2026-06-13)¶
Exhaustive reachability sweep of every CAS-family
method=string and option object throughrun_job(casci/casscf/nevpt2/caspt2/mrci/fci/selected_ci/dmrg/v2rdm/transcorrelated_ci, withCASCIOptions/CASSCFOptions/CASPT2Options/SelectedCIOptions/SelectedCIPT2Options), guarding the v0.12 documented-but-unreachable class (multi-root CASCI). All run;tests/test_cas_runjob_reachability.py.nevpt2andcaspt2added to the publicMethodliteral: they were dispatched and documented (docs/user_guide/non_hf_solvers.md,handovers/HANDOVER_MULTIREF.md) but missing from the type.Confirmed the v0.14 canonical-CCSD(T) /
triples=/ AutoCIcitype=selectors run end-to-end throughrun_joband their citation routes fire (CCSD → Purvis-Bartlett, CCSD(T) → Raghavachari, CISD → Pople 1977), including the recording semantics (method="ccsd", triples="(t)"records as CCSD(T)) and theNotImplementedErrorguards for roadmap variants;tests/test_runjob_cc_ci_surface.py.
Fixed: EWALD_3D tight-cell cutoff floor respects auto_optimize_truncation=False (§7) (2026-06-13)¶
The Γ EWALD_3D RHF/RKS drivers unconditionally inflated
cutoff_bohrto 18 (andnuclear_cutoff_bohrto 25) for tight 3D cells (V < 1000 bohr³), overriding a user’s explicitauto_optimize_truncation=False. That silently grew the lattice sum until S(Γ) was PSD, so the linear-dependence preflight could never refuse a genuinely non-PSD overlap — the “converged garbage” mode the preflight exists to prevent (CLAUDE.md §7). The floor is now gated onauto_optimize_truncation, so an explicit opt-out gets the user’s cutoff verbatim and the preflight correctly raisesLinearDependenceErroron a non-PSD S(Γ) (e.g. LiH conventional / STO-3G, Γ min eig −0.16). Fixestest_periodic_lih_conventional_raises_critical_before_scf.The RKS Γ driver now builds the DFT Becke grid after the linear-dependence preflight, so a non-PSD-overlap abort no longer pays the periodic-grid cost (LiH conventional abort: 27 s → 2 s).
Corrected a stale energy reference in
test_periodic_h2_in_big_box_passes_preflight(−1.1114 → −1.116714, the ω-invariant molecular limit; the old value predated the F2 fix), and removedtests/test_linear_dependence.pyfrom the gateknown_slowset (now ~4 s).
Fixed: periodic RKS Γ gradient — exact molecular-limit LDA / GGA / hybrid (2026-06-13)¶
compute_gradient_periodic_rks_gammanow (a) threads the convergedV_xcinto the gradient kernel so the overlap-Lagrangian uses the gauge-free variational KS Fock eigenvalues — removing the α_HF-scaled exchange-G=0 self-image gauge for hybrids (the RKS analogue of the periodic Γ RHF gradient fix below) — and (b) computes the XC Pulay term via the lattice-summed periodic primitivexc_lattice_gradient_contributioninstead of a molecular LDA-only fallback that dropped the GGA σ-piece. The molecular-limit RKS gradient now matches the molecular analytic gradient to ~1e-9 (LDA / PBE) and ~1e-14 (B3LYP / PBE0), and pure-DFT true-periodic gets the exact lattice XC Pulay too. New guards:tests/test_periodic_gradient_g1b.py::test_h2o_pbe_molecular_limit_matches_molecularand::test_h2_b3lyp_molecular_limit_matches_molecular. Hybrids’ true- periodic HF-exchange K Pulay still awaits the G1a-2 fix.
Fixed: analytic periodic Γ RHF gradient — gauge-consistent overlap-Lagrangian (2026-06-13)¶
compute_gradient_periodic_rhf_gammanow builds the energy-weighted density (the overlap-Lagrangian Pulay term) from the variational FockF_var = ∂E/∂Drebuilt from the converged density, instead of the EWALD_3D Γ driver’s reportedmo_energies. Those eigenvalues sit on a shifted orbital-energy reference — the α_HF-scaled real-space exact-exchange G=0 self-image — which left the analytic HF gradient ~0.044 Ha/bohr off finite difference on H₂/STO-3G in a 20-Å box (0.1927 vs 0.1486 Ha/bohr); the molecular-limit analytic gradient now matches FD to ≤ 1e-6. Closes the tracked EWALD Γ analytic-gradient known issue (tests/test_periodic_gradient_g1a.py::test_h2_box_matches_fd, the lone periodic known-red), correcting ASE periodic forces on the EWALD route. RKS pure-DFT is unaffected (exchange-free F carries no gauge); the RKS-hybrid molecular-limit correction has avariational_xc_fockhook pending an SCF-grid-matched V_xc thread.
Added: excitation-case-partitioned IC-CASPT2 scaffold (25i M24, 2026-06-13)¶
New
python/vibeqc/solvers/_caspt2_cases.py: the framework for an IC-CASPT2 that partitions the first-order interacting space by excitation case (inactive-hole / active / secondary-particle pattern) instead of building one monolithic determinant FOIS. Each case’s active-index count fixes the reduced-density-matrix order its block needs (0-active MP2-like through 3-active 3-RDM, with the 4-RDM of the 3-active H0 block destined for theF3intermediate), the path to lifting CASPT2 off its small-active-space wall and composing with the selected-CI reference for 30-40-orbital CASPT2.M24 (this milestone) is the classifier + block-organized FOIS, validated to machine precision against the dense determinant engine (
_mrpt._ic_caspt2_solve, the exact oracle):caspt2_corr_casesreproduces the dense second-order energy on H2/6-31G, H2O/STO-3G, and LiH/6-31G (the last spanning all eleven external cases, 0-3 active indices), with and without the imaginary shift. The RDM contraction of each block (M25/M26) and dropping the determinant FOIS (M27) build on this. Nothing is wired into the publiccaspt2()yet; the dense engine remains the shipped, OpenMolcas-pinned default (untouched).
Next minor cycle (v0.13): BIPOLE multi-k corrected-gauge exchange channels (option (b) Phase 3) + analytic-gradient gauge migration (Phase 5 and the tracked EWALD Γ analytic-gradient known issue), periodic driver efficiency (PBC-audit E2/E3), DLPNO M3c.
Fixed: import vibeqc.fetch.references no longer hard-requires BeautifulSoup (2026-06-13)¶
aa6936abrestored thebuild-testgate byimportorskip-guardingtests/test_fetch_references_cccbdb.py, but the underlying packaging issue remained: the reference-data parsers (NIST CCCBDB / WebBook / ATcT) importedbs4(BeautifulSoup4) at module top, soimport vibeqc.fetch.referencesstill hard-required an undeclared dependency, andpip install 'vibe-qc[fetch]'could not parse HTML becausebeautifulsoup4/lxmlwere declared in no extra.bs4 is now lazy-imported at the HTML-parse callsite through a shared
references._optional_deps.require_bs4()helper that raises a clearpip install 'vibe-qc[fetch]'pointer only when the parse path runs without it; theBeautifulSoup/Tagannotations move behindTYPE_CHECKING.beautifulsoup4+lxmlare declared in the[fetch](and[dev]) extras, and the test guard is extended withimportorskip("lxml"). Mirrorsee531cc1(scipy/ASE packaging guard).
Changed — DLPNO-CCSD pilot iterates spatially (M3c step 5b-i) (2026-06-13)¶
The DLPNO-CCSD pilot’s per-iteration residual now uses the spatial
cs_ccsd_residual(M3c-5a) instead of expanding to spin-orbital and callingso_residualsevery step — a 16× spin-block redundancy dropped from each iteration (spin-orbital is now built once at the end only for the still-spin-orbital (T) correction). The converged energy and (T) are bit-for-bit unchanged: all existing pilot gates (tests/test_dlpno_ccsd.py, recovery tiers + triples) pass, and a new element-wise residual-parity gate confirmscs_ccsd_residualequals the spin-orbital αβ block exactly (tests/test_dlpno_ccsd_cs.py).This composes the spatial residual with the per-pair PNO projection — exactly the two ingredients the per-pair local residual needs — confirming they combine to the correct DLPNO-CCSD energy. It is a constant-factor speedup, not yet the scaling fix: the pilot still expands to the full virtual space. Dropping that expansion (the per-pair local residual) is the remaining M3c-5b work.
Added — spatial closed-shell DF-CCSD residual + solver (M3c step 5a) (2026-06-13)¶
vibeqc.dlpno._ccsd_cs.run_cs_ccsd— the spatial closed-shell DF-CCSD residual and solver, a faithful numpy transcription of the validated C++ kernel (cpp/src/ccsd.cpp, closed-shell spin integration of SGWB 1991 / DePrince-Sherrill 2013). It replaces the DLPNO-CCSD pilot’s spin-orbital per-iteration step (a 16× spin-block redundancy) with the genuine spatial residual — and, more importantly, is the exact template the per-pair local residual mirrors: the same contractions, evaluated in each pair’s PNO basis with cross-pair PNO-overlap projections.Parity-gated (
tests/test_dlpno_ccsd_cs.py, 5): reproduces the FCI-anchored spin-orbital kernel (_ccsd_ref) to ≤1 µHa on H₂, H₂O/STO-3G and H₂O/def2-SVP; sits above the full-space CASCI(=FCI) correlation (the anchor gate that caught the broken C++ CCSD); the residual vanishes at convergence. Internal; the local residual that consumes the M3c-4 integral blocks is the next step.
Added — complete local DLPNO-CCSD doubles residual (M3c step 5b-ii, part 2) (2026-06-13)¶
dlpno.ccsd_local.local_t2_residual_doubles— the complete closed-shell T2 (doubles / CCD-level) residual evaluated entirely in each pair’s PNO basis: the ladders (part 1) plus the Ph-symmetrised Fock laddersPh[Σ_e t_ij^ae F_be − Σ_m t_im^ab F_mj]and the three ring intermediates W1/W2/WX, with all cross-pair amplitudes projected in throughS_pq. This is the reduced-scaling counterpart ofcs_ccsd_residual’s T2 residual — no full-space tensor is formed.Exact in the full-domain limit — when every pair’s PNO space is the full virtual space, all projections are identity and the residual reproduces canonical closed-shell CCD to ~5e-16 on H₂O/def2-SVP with Boys-localised occupieds (
tests/test_dlpno_ccsd_local.py). With truncated PNO domains the intermediates’ cross-domain contractions are the controlled DLPNO domain approximation (validated by recovery, not exact parity) — bounded well below the residual scale.What remains for full DLPNO-CCSD: the singles (t1) terms upgrade this to CCSD, then the per-pair update/DIIS solver — the last M3c-5b-ii slice that retires the O(N⁶) pilot (retired DLPNO handover).
Added — local DLPNO-CCSD residual: projection machinery + ladders (M3c step 5b-ii, part 1) (2026-06-13)¶
Cross-pair amplitude projection (
dlpno.ccsd_local.pno_overlap,project_amplitude) —S_pq = V_pᵀ S V_qprojects a pair’s doubles amplitudes into another pair’s PNO basis, the machinery that couples pairs in a reduced-scaling CCSD residual without forming a full-system tensor. Plusbuild_pair_occ_pno(the occupied→PNO half-transformB^P_{ma}for all occupieds), from which every CCSD integral block with an occupied index follows by contraction.local_doubles_ladder_residual— the Ph-symmetriser-free part of the local closed-shell T2 residual (base(ia|jb), the hole ladderΣ_mn P_ij(τ_mn) W_mnij, the particle ladderΣ_ef τ_ij^ef W_abef), evaluated entirely in each pair’s PNO basis with cross-pair amplitudes projected in. Validated to machine precision against the full-space residual projected into the pair (tests/test_dlpno_ccsd_local.py) — confirming the projection machinery and the per-pair ladder contractions are exact. The Ph-half (Fock ladders + W1/W2/WX rings) and singles complete the residual that retires the O(N⁶) pilot expand — the remaining M3c-5b-ii work (retired DLPNO handover).
Added — local DLPNO-CCSD integral foundation (M3c step 4) (2026-06-13)¶
vibeqc.dlpno.ccsd_local— the integral primitives for reduced-scaling DLPNO-CCSD: per-pair PNO-basis density-fitted B-tensors (B^P_{ia},B^P_{ab},B^P_{ij}) from which every closed-shell DF-CCSD block follows by contraction — K=(ia|jb), J=(ij|ab), the 3-external (ia|bc) and the 4-external ladder (ab|cd), the last two kept factored. Built directly in each pair’s small PNO basis, never forming a full-system tensor.Integral-parity gated (
tests/test_dlpno_ccsd_local.py, 8): each local block equals the canonical full-virtual-space integral projected into the PNO space (U = C_virᵀ S V_pno) to 1e-11, for full and truncated PNO spaces. Internal foundation for the per-pair local residual that replaces the O(N⁶) pilot expand/project — the next M3c step (now tracked inHANDOVER_GATED_ITEMS.md).
Changed — DLPNO-MP2 sparse pair lists on by default (M3c step 2) (2026-06-13)¶
Distant-pair screening is now on by default (
tcut_pairsdefault0.0 → 1e-6): occupied pairs whose semicanonical dipole-dipole estimate is negligible are treated at the estimate level (e_distant) instead of with the full PNO machinery, turning the O(N²) pair list into O(N) for extended systems. The default is conservative and a no-op on compact molecules — everything is withindipole_r_min(8 bohr), so nothing is screened and every existing result (and the exactness gates) is bit-for-bit unchanged.Sub-quadratic growth demonstrated (
tests/test_dlpno_scaling.py): on an H₂ chain, doubling the system (4 → 8 units) grows the total pair count ×3.6 (quadratic) but the kept-pair count only ×2.3 (linear), at < 1e-4 Ha cost to E_corr vs the unscreened run. Settcut_pairs=0for the exactness configuration. The dipole estimate is crude; a calibrated MP2-pair-energy screen is a later refinement.
Added — DLPNO-MP2 local density fitting (M3c step 1) (2026-06-13)¶
DLPNOMP2Options.local_df(+fit_buffer) — the first reduced-scaling primitive: each pair’s exchange integrals are refit from the raw 2-/3-centre integrals using only the auxiliary functions in its fit domain (the pair’s PAO domain extended byfit_bufferbohr), with the local metric V_[ij], rather than the global RI metric. A full fit domain refits exactly, so the exactness limit is unchanged —local_df=True, fit_buffer=1e9reproduces canonical RI-MP2 to ≤ 1 µHa (canonical and Boys-localised). Defaultlocal_df=Falseis bit-for-bit the original global path.Locality demonstrated (
tests/test_dlpno_local_df.py): on an H₂ chain, doubling the system (4 → 8 units, n_aux 112 → 224) leaves the maximum per-pair fit dimension unchanged at 56 — the O(1)-per-pair fit cost that makes DLPNO scale.DLPNOMP2Resultgainsfit_dim_per_pair. This increment establishes and validates the sparse-maps fitting primitive; the integral-direct local 3-centre build (avoiding the full T tensor) and flipping the default are the next M3c increments.
Added: selected-CI reference for single-state NEVPT2 / CASPT2 (25i, 2026-06-12)¶
nevpt2(..., selected_reference=True)andcaspt2(..., selected_reference=True)(and, throughrun_job,method="nevpt2"/"caspt2"withcasscf_options=CASSCFOptions(ci_solver="selected_ci", ...)) build the PT2 zeroth order on the reference’s own selected-CI wavefunction instead of re-solving the full CAS. Previously this composition raised (“requires the exact CI backend”). Both engines are determinant-based (SC-NEVPT2’s strongly-contracted external groups and IC-CASPT2’sÊ_pr Ê_qs|0⟩contracted space are applied to the reference state, and the CAS projection is occupation-pattern-based), so the reference may be a truncated wavefunction whose full CAS is intractable, with no high-order RDM formed. Implemented as an optionalreferencethread through_semicanonical_prep; the active block is invariant under the core/virtual semicanonicalization, so the supplied wavefunction stays valid in the rotated basis.The result is the MR-PT2 of that reference; it converges to the full-CAS MR-PT2 as the selection tightens and reproduces the dense reference path exactly at the full-selection limit. Validated (
tests/test_solvers_mrpt_parity.py): full-limit selected CASPT2 == dense explicit engine to 1e-10 and == live OpenMolcas&CASPT2(H2O/STO-3G CAS(4,4)) to 2.6e-8 Ha; full-limit NEVPT2 == dense; feeding an exact CI through the path is bitwise-identical to the default; a truncated reference sits above dense and converges monotonically.Out of scope (rejected with a clear error): multi-state MS/XMS-CASPT2 (its model space re-solves a multi-root CASCI over the full active space),
method="mrci", and the IPEA shift (needs a pseudocanonical active rotation that would change the supplied CI vector). The default exact-CI MR-PT2 paths are unchanged (150 parity tests green).
Added: dry-run peak-memory estimate for vq submit auto (Inc 2, 2026-06-13)¶
VIBEQC_DRY_RUN_ESTIMATE=1(set alongsideVIBEQC_DRY_RUN=1) makes the dry-run short-circuit additionally compute the job’s peak-memory estimate and record it in the.systemmanifest as[memory].estimate_bytes(estimate_memory(...).total_bytes, with the 1.2× headroom already applied). Off by default, so the cheap output-discovery dry-run stays cheap; best-effort, so a failed or inapplicable estimate (semiempirical / MLIP methods) just omits the key and never breaks the dry-run.Producer half of memory-aware
vq submit autoRAM-fit placement; the vq consumer (vibeqc_preflightparsing[memory].estimate_bytes, thewith_estimatetoggle) already shipped. Newvibeqc.output.is_dry_run_estimate_requested()and adry_run_manifest(..., estimate_bytes=…)parameter;ManifestUpdaterthreads the estimate into the existing[memory]section. Tests:tests/test_output_dry_run.py,tests/test_runner.py.
Added — GS1 periodic geometry symmetrisation helpers (v0.13, 2026-06-12)¶
New
vq.detect_spacegroup(system, symprec=...)helper runs spglib space-group analysis on aPeriodicSystemwithout mutatingsystem.symmetry; useattach_symmetryonly when downstream k-point / integral code should see the attached symmetry.New
vq.symmetrise(system, symprec=..., to_primitive=False)returns a freshPeriodicSystemplus aSymmetriseReportcontaining before/after space groups, atom counts, cell volumes, and RMS / maximum atomic cleanup displacement. The input system is left untouched. The first milestone is deliberately 3D-only; lower dimensional slabs/polymers still use the existingattach_symmetryintrospection path.read_poscar(..., symmetrise=True)opt-in runs the parsed POSCAR through the same GS1 pipeline and returns theSymmetriseResult. The defaultread_poscar(path)behavior remains unchanged and still returns aCrystal.
Added — GS2 periodic Extended-XYZ reader path (v0.13, 2026-06-12)¶
from_xyz(..., periodic=True)now reads Extended-XYZLattice="..."andpbc="..."comment tags into aPeriodicSystem, converting ASE-style row-vector lattices in Ångström to vibe-qc’s column-vector bohr convention. Supplying an explicitlattice=matrix also selects periodic mode for plain XYZ files.
Added — vqfetch list-candidates (VFETCH-X1, 2026-06-12)¶
New CLI subcommand
vqfetch list-candidates --formula MgOqueries the OPTIMADE federation for multiple candidate structures, dedup by structural hash, and displays a tabular report ranked by space-group plurality. Tabular stdout: provider, ID, space group, lattice constant (a, Å), atom count, dedup-merge count.New
_render_candidates_table()rendering function invibeqc.fetch.cli— reusable for downstream tools.Wraps the existing
fetch_optimade(max_results=N, dedup=True)Python API (shipped in v0.8.0). Flags:--max-results,--provider,--no-dedup,--no-cache,--cache-only. Tests:tests/test_fetch_optimade.py(5 new tests). Docs:docs/user_guide/external_structures.md.
Added — MP property-field augmentation (VFETCH-X3, 2026-06-12)¶
client_mp.py: when$MP_API_KEYis set,fetch_mp()now fetches the Materials Project summary record and augments the returnedPeriodicSpecwith: band gap, formation energy per atom, energy above hull (all inProvenance.notes), and refinedis_open_shell/magnetic_momentsfrom MP magnetic data (gated ontotal_magnetization > 0.1 μ_B/cell)._fetch_mp_properties()— lightweighturllibcall to the MP REST API (api.materialsproject.org); falls back gracefully on any transport error (the OPTIMADE-only spec is returned unchanged)._augment_spec_with_mp_properties()— folds MP properties into a spec without mutating the input.heuristics.open_shell_default()already acceptedmp_is_magnetic/mp_total_magnetization— the API was designed for this; no heuristics.py changes needed. Tests:tests/test_fetch_mp_properties.py(10 new tests).
Added — NIST WebBook reference-data fallback (VFETCH-X4, 2026-06-12)¶
New
client_webbook.py(460 lines): fetches thermochemistry from the NIST Chemistry WebBook (SRD 69) for molecules CCCBDB doesn’t cover. Returns sparseExperimentalReferencerecords with ΔHf° (gas + condensed), entropy, heat capacity, IE, and EA. No atomization energies, vibrational data, or geometry — consumers should checkref.atomization_energy_kcal_per_mol is Nonebefore comparing.vqfetch reference --source webbook --cas <CAS>now dispatches to the WebBook client. The default--source cccbdbbehaviour is unchanged.Parses the semi-structured WebBook HTML via BeautifulSoup with regex extraction for ΔHf°, S°, C_p, IE, and EA. Rate-limited (1.5 s spacing) with declared User-Agent and Retry-After honour.
Same
FetchCachepattern (30-day TTL,--no-cache/--cache-onlyflags) as CCCBDB.from_xyz(..., symmetrise=True)runs the parsed periodic system through the GS1 standardisation helper and returns aSymmetriseResult. The defaultfrom_xyz(path)molecular behavior is unchanged.pbcmasks are mapped onto vibe-qc’sdim=1/2/3contract for prefix-periodic systems (T F F,T T F,T T T); non-prefix masks are rejected with a clear error instead of silently rotating axes.
Added — GS2 CIF reader path (v0.13, 2026-06-12)¶
New
read_cif(path)/Crystal.from_cif(path)reader parses CIF cell lengths/angles, fractional_atom_site_*loops, uncertainty suffixes like4.210(1), and standard CIF symmetry-operation loops (_space_group_symop_operation_xyz/_symmetry_equiv_pos_as_xyz). Asymmetric-unit sites are expanded to the full unit cell before constructingCrystal.read_cif(..., symmetrise=True, to_primitive=True)routes the parsed crystal through the GS1 standardisation pipeline and returnsSymmetriseResult, matching the POSCAR and Extended-XYZ reader contract.
Added — GS3 standardised orbit-compression wrappers (v0.13, 2026-06-12)¶
New
compute_overlap_lattice_symmetrised_with_orbits(system, basis_name, options, ...),compute_kinetic_lattice_symmetrised_with_orbits, andcompute_nuclear_lattice_symmetrised_with_orbitshelpers runsymmetrise(...), rebuild the named basis on the cleaned cell, use the attached symmorphic operations, and return aSymmetrisedOrbitIntegralResultcontaining the cleaned system, rebuilt basis, symmetrisation report, compressed orbit view, and fullLatticeMatrixSet.This closes the GS1/GS2 -> SYM3a workflow: near-symmetric imported cells no longer need manual
symmetrise/ basis rebuild /symmorphic_operationsplumbing before one-electron lattice-integral orbit compression becomes effective.
Added — K6 generalized regular k-point grids (v0.13, 2026-06-12)¶
KPoints.generalized_regular(system, grid_matrix)builds explicit generalized regular grids from 3D integer HNF matrices. Diagonal matrices reproduce Gamma-centred Monkhorst-Pack grids; off-diagonal matrices generate sheared regular grids withdet(grid_matrix)points.KPoints.optimal(system, target_n_kpts, metallic=False)performs a bounded on-the-fly HNF search at the requested determinant and chooses the grid with the largest shortest periodic Cartesian k-point distance.metallic=Trueuses a 4x effective target count, matching the existing density-helper convention.KPoints.from_database(system, lattice, order, special=False)fetches the GMU/NRL pre-defined k-point tables on demand (sc,bcc,fcc, andhexfamilies), parses lattice-coordinate fractional k-points plus normalized symmetry weights, and returns a weighted explicitKPointsmesh without bundling remote table data in the source tree.Citation routing now includes the Wisesa-McGill-Mueller 2016 GR-grid paper and the Wang-Wisesa-Balasubramanian-Dwaraknath-Mueller 2021 on-the-fly generation paper under
numerics=["generalized_regular_kgrid"], plus the GMU/NRL k-point table route undernumerics=["kpoint_database"].
Added: canonical CCSD(T) first-class public API + user guide (2026-06-12)¶
Ships now: the public-API re-exports and the user guide, over the existing dense closed-shell-RHF CCSD/CCSD(T) pilot (whose limits are documented below). The broader coupled-cluster ladder is roadmap-tracked for v0.14.
CCSDOptions,CCSDResult,CCSDIteration,run_ccsd, andchemical_core_orbital_countare now re-exported from top-levelvibeqc, matching the rest of the molecular public API. The lower-levelvibeqc.ccmodule remains supported.New
docs/user_guide/ccsd.mddocumentsrun_job(method="ccsd"),run_job(method="ccsd(t)"), the low-levelrun_ccsd(...)surface, result fields, validation anchors, frozen-core handling, and the current dense closed-shell-RHF pilot limits.
Added: CCSD triples= selector surface (2026-06-12)¶
Ships now: the triples= selector and the CCSD/CCSD(T) recording
semantics, over the closed-shell pilot. The named higher-order triples
variants remain roadmap-tracked for v0.14 and raise
NotImplementedError rather than falling back to standard (T).
CCSDOptions(triples="none" | "(t)")and theCCSDOptions.triplesproperty now provide the human-readable selector planned for the v0.14 coupled-cluster ladder while preserving the existingcompute_triplesboolean.run_job(..., method="ccsd" | "ccsd(t)", triples=...)threads the selector through the high-level driver. The effective CCSD/CCSD(T) choice controls the output header, coupled-cluster block, and citation route, somethod="ccsd", triples="(t)"records as CCSD(T) andmethod="ccsd(t)", triples="none"records as CCSD.Named future variants (
A-CCSD(T),CCSD[T],CCSD+T(CCSD)) are recognized and raiseNotImplementedErroruntil their kernels land, avoiding silent fallback to standard(T).
Added: AutoCI-style citype= selector surface (2026-06-12)¶
Ships now: the citype= string selector over the implemented CISD /
CCSD / CCSD(T) paths. The roadmap CI/CC ladder names remain
unimplemented (v0.14) and raise NotImplementedError.
run_job(method="ci", citype="cisd")now exposes the existing fixed-space variational CISD solver through the high-level molecular workflow. Output uses the shared non-mean-field solver block and the method route cites Pople-Seeger-Krishnan 1977.vq.cisd,vq.CISDOptions, andvq.CISDResultare now top-level public exports, matching the CCSD public-API polish for the implemented CI/CC ladder.run_job(..., cisd_options=...)forwardsmax_excitation,nroots, andmax_detinto the high-level CISD route.citype="ccsd"andcitype="ccsd(t)"alias the canonical CCSD / CCSD(T) paths, so the v0.14 CI/CC ladder has one string selector for the implemented entries.Roadmap names (
ccd,qcisd,qcisd(t),cepa(n),cc2,cc3,ccsdt) are recognized and raiseNotImplementedErroruntil their kernels land, avoiding accidental fallback to CISD or CCSD.
Added: BIPOLE corrected exchange gauge at multi-k — q≠0 LR-exchange channels (option (b) Phase 3, 2026-06-11/12)¶
All four BIPOLE drivers now run the corrected gauge at multi-k with
use_exchange_ewald_split=True(RHF; RKS with α_HF-scaled hybrid channels; UHF/UKS per spin). The Ewald exchange split generalises per k-point asK(k) = K_SR(erfc ω) + K_LR(erf, q+G≠0) + (ξ_M^sc − π/(V_sc·ω²))·S(k)D(k)S(k): the LR term couples every k-point pair through momentum-transfer channelsq = k − k′evaluated on q-shifted reciprocal meshes (compute_K_long_range_at_k/KExchangeLongRangeCache, reusing the J^LR Bloch-pair-FT machinery and its shared ω/K_max envelope), and the G=0 correction uses the BvK-supercell Madelung constant (probe_charge_madelung_supercell, = PySCF’s multi-k_ewald_exxdiv_for_G0) with the supercell volumeV_sc = n_k·Vin the erfc-K=0 removal. Per-k densities for the corrections come from an exact BvK-torus Bloch-fold inversion (bvk_torus_density_matrices) — valid for orbital, SAD-local, damped, ODA-mixed, and warm-start densities alike.Validation: H₂/STO-3G 12-bohr box [2,1,1] lands +0.12 mHa (cutoff 7) / −0.002 mHa (cutoff 12) from out-of-process PySCF KRHF GDF (−1.1201398824, exxdiv=’ewald’) — truncation-scale, vanishing with cutoff like the Γ gauge; and the [2,1,1] run matches the Γ-split of the explicitly doubled supercell to +0.0001 mHa per cell at matched ω (the supercell-unfolding identity, pinned at 1e-5). Supercell-Madelung pins vs PySCF
tools.pbc.madelungat 1e-9 (H₂ box [2,1,1]/[2,2,2], MgO [2,2,2]). RKS [2,1,1]: SVWN +0.043 mHa / PBE0 +0.062 mHa vs PySCF KRKS. UHF triplet lattice +0.034 mHa and UKS-PBE0 +0.77 mHa (the documented Γ grid offset, mesh-independent), with k-sampling shifts Γ→[2,1,1] agreeing with KUHF/KUKS to 0.04 / 0.013 mHa. Open-shell refs pin the per-cell triplet with explicitnelec=(4,0)— PySCF’scell.spinat multi-k means the BvK-supercell TOTAL, a documented trap — at exactly the fixture geometry (1.4 bohr; a 0.74-Å cell shifts the steep triplet curve by ~1 mHa).The corrected gauge is the DEFAULT at multi-k too (Phase-5 flip, 2026-06-13), matching the Γ default —
use_exchange_ewald_splitauto-resolves ON for any 3D run under the Ewald J split. The supercell-unfolding identity (+0.0001 mHa/cell) is the rigorous internal proof that the multi-k channels reproduce the PySCF-validated Γ machinery exactly; the MgO [2,2,2] c12 heavy parity (examples/regression/bipole_parity/mgo_multik_parity.py) is staged for the queue as belt-and-suspenders. Passuse_exchange_ewald_split=Falsefor the legacy gauge (kept for the legacy-gauge analytic gradient + parity diagnostics). The corrected multi-k gauge needs a Monkhorst-Pack mesh; under the auto default an ad-hoc k-list (band path / explicit list) at multi-k falls back to the legacy gauge with a log note (no breakage for prior multi-k k-list runs), while explicitTruewith such a mesh raises.Analytic gradients (Phase-5 decision, 2026-06-13): FD ratified as the permanent production force/stress path.
compute_bipole_gradient_fddifferentiates the actual driver energy, so it is exact in either gauge;bipole_optimize/run_periodic_job(optimize=True)/ periodic NEB default to it. The analytic kernels implement the LEGACY Γ-local gauge and refuse corrected-gauge SCF results (ValueError, landed v0.12.06af56d91) — so they now require an explicituse_exchange_ewald_split=FalseSCF. Migrating the analytic kernels to the corrected-gauge functional (re-derive W/Pulay for the q-channel K_LR + SDS Madelung + BvK-torus density derivative) is a deferred future milestone, parallel to the periodic-SCF chat’s EWALD-route gradient migration — NOT a v0.13.0 deliverable. (Supersedes the earlier “multi-k KS analytic gradient now runs / warns instead of raising” status — that described the legacy-gauge path before the corrected-gauge refusal + default flip.)
[v0.12.2] — 2026-06-12¶
Docs-only patch on Knuth’s Beaver: publishes the generalized cluster
build guide (cherry-picked from main).
Changed¶
Cluster build guide generalized. The cluster provisioning page (
docs/cluster_twin.md) is rewritten as a general university-HPC guide — institute-specific hostnames, queue/node names, and machine specs are replaced by placeholders, the gateway hop is now an optional pattern, and all reusable content (two-phase online/offline build, offline wheelhouse, BLAS pinning, TORQUE job recipe) is preserved (a6f2d1cf). No code changes; the public docs site picks this up on thereleaseadvance.
[v0.12.1] — 2026-06-12¶
Patch on Knuth’s Beaver: the v0.12 post-release-audit fixes plus three
optimizer-honesty fixes, cherry-picked from main (original SHAs noted).
Fixed¶
Hybrid TDA/TDDFT ran with zero exact exchange — the response kernel hardcoded
c_x = 0for every DFT functional; hybrids (B3LYP, PBE0, …) now use the functional’shf_exchange_fractionat all three kernel sites, with a hybrid regression suite (1a89da6a).MPI GPW Hartree-J was wrong for world size > 1 — rank-local z-slabs were elementwise-summed and the Poisson solve saw a mostly-zero grid; full-grid assembly fixed with a simulated-2-rank parity test (
5df5e765); stale blanket-MPI documentation claims retired (1361c967).Geometry optimization walks the surface the final energy reports — native optimizer backend for wavefunction methods (
cac4d26e) and CAS methods (888305d8); semiempirical optimization trajectories carry per-frame energies (b0752808).Multi-root CASCI is reachable via
run_job(casci_options=...), and the docs-audit API-name corrections land indocs/features.md(6404e2f7).basis-library path test follows the documented
LIBINT_DATA_PATHresolution order — no more environment-dependent phantom red on dev boxes carrying thebuild/basis_libraryoverlay (1edef5c1).
[v0.12.0] — 2026-06-12 — Knuth’s Beaver¶
Changed — v0.12.0 cut verification: BIPOLE analytic-gradient gauge refusal completed; EWALD Γ pin re-baselined (2026-06-11)¶
BIPOLE UHF/RKS/UKS analytic gradients now refuse corrected-gauge results, mirroring the existing RHF refusal:
compute_bipole_gradient_{uhf,rks,uks}raise a clearValueErrorwhen handed an SCF result computed under the option-(b) Ewald exchange split (the default at 3D Γ under the J split since Phases 4a/4b). The analytic kernels implement the legacy Γ-local gauge; at the v0.12.0 cut the un-gated UHF path silently disagreed with the driver’s FD by 5.3 Ha/bohr on triplet H₂. Production forces in the corrected gauge usecompute_bipole_gradient_fd(the optimizer / periodic-NEB default), as before. Tests pin both contracts — legacy-gauge exactness (~1e-7 vs FD withuse_exchange_ewald_split=False) and the refusal (tests/test_bipole_gradient.py); thetests/test_dft_plus_u.py+U-contribution tests pin the legacy gauge explicitly.EWALD_3D Γ reference re-pinned to the physical value: after the
86c39851gauge restoration, the 30-bohr-box H₂/STO-3G driver reproduces the isolated-molecule limit (−1.1167 Ha;run_rhfgives −1.1167143 at the same geometry). The old −1.1114 pin was the pre-restoration artifact (tests/test_eigs_preflight.py).Known issue (tracked, ships in v0.12.0): the analytic EWALD Γ gradient (
compute_gradient_periodic_rhf_gamma, consumed by ASE periodic forces on the EWALD route) still differentiates the pre-restoration assembly — on a dilute H₂ box it gives 0.193 Ha/bohr where the restored driver’s FD and the isolated-molecule FD agree on 0.149 (to 6 decimals). FD periodic gradients are correct and remain the production path. Tracked inscripts/test_gate/known_reds_baseline.json; the gauge migration of the analytic kernel is the periodic-SCF chat’s next milestone.Resolved in v0.13 (Unreleased,
b70ce004). The gauge migration landed:compute_gradient_periodic_rhf_gammanow builds the energy-weighted density (overlap-Lagrangian) from the variational Fock∂E/∂Drebuilt from the converged density, instead of the EWALD_3D Γ driver’s gauge-shiftedmo_energies, so the H₂-box analytic gradient matches FD to ≤ 1e-6 (0.193 → 0.149 Ha/bohr). Removed fromknown_reds_baseline.json; ASE periodic forces on the EWALD route are corrected. See the[Unreleased]“gauge-consistent overlap-Lagrangian” entry.
Changed: native D4 backend gated as experimental — defective reference dataset (2026-06-11)¶
compute_d4(..., backend="native")now emitsD4NativeExperimentalWarningon every call. The 2026-05-31 audit confirmed the shippedd4_reference_data.jsonproduces reference C6 coefficients 4–32× too large (CH₄ C6(H,H) = 62.1 a.u. vs dftd4’s 1.94) and dispersion energies ~8–32× too attractive (water dimer 8.1×, methane dimer 19.6× at PBE). Root cause: the generator stores each reference system’s whole-molecule α(iω) as the per-atom reference polarizability — the per-atom extraction of Eq. 6 of Caldeweyher et al., J. Chem. Phys. 150, 154122 (2019) was never implemented (the 2026-05-22 aug-cc-pVDZ regeneration upgraded the basis but kept the defect). Gated rather than regenerated for this release: a correct regeneration needs new extraction code, a heavy compute batch, and parity validation, and re-derivation must stay first-principles per the 2026-05 licensing decision (handovers/HANDOVER_D4_NATIVE.md§ 1). Defaults are unaffected — the defaultbackend="dftd4"(and the D3dftd3backend) are audit-verified bit-exact to the upstream packages.generate_reference_datasetlikewise warns at call time so a basis-only regeneration can’t ship the defect again. Strict-xfail parity tests vs the dftd4 package (pairwise C6 + dimer energy,tests/test_d4_model.py) pin the un-gating bar: they flip to a suite error once a regenerated dataset meets it, forcing gate removal in the same commit. The experimental GFN2-xTB model’s post-SCF D4 term consumes this dataset and over-binds dispersion accordingly; its experimental warning + docs now say so.
Fixed: CPCM analytic gradient — off-diagonal ∂A/∂R term was half its true value (2026-06-11)¶
vibeqc.cpcm_gradient(and ASEget_forces()withsolvent=set) carried a systematic ~3e-4 Ha/bohr error vs full-energy finite differences (water/6-31G; flagged by the 2026-05-31 audit, confirmed live on HEAD). Root cause — not the audit’s hypothesized missing cavity-point-motion / q-response terms (both verified present and correct to ~1e-11): the off-diagonal cavity-derivative term(1/(2f)) qᵀ(∂A/∂R)qcounted only the row chain-rule channel ofA_ij = 1/|s_i − s_j|, which depends on the positions of both cavity points — an exact factor 2 on that piece, invisible to the translational-invariance test. Post-fix the assembled gradient is FD-truncation-exact: max deviation 2.4e-6 / 1.5e-7 / 5.9e-9 Ha/bohr at h = 4e-3 / 1e-3 / 2e-4 — clean O(h²). Affects CPCM geometry optimisations since v0.9.1 (below loosefmaxthresholds, but shifts tightly converged minima).Test gap closed (this is why it shipped):
test_cpcm_gradient_matches_fd_water_in_watertightened from atol=5e-3 (~16× looser than the bug) to atol=1e-5 (measured agreement ~5e-7), and the A-matrix term gained direct per-component FD tests at fixed q —test_A_matrix_gradient_matches_fd(pins the two-channel factor) andtest_A_matrix_gradient_diagonal_switching_matches_fd(isolates the diagonal Scalmani-Frisch switching-derivative channel via a single nonzero charge, which the translational-invariance test cannot see).Retired the
gradient.pymodule-docstring claim that the in-solvent energy-weighted density is an approximation “vs the strictly correct gas-phase W”: the in-solvent W is exactly right for the total functional (Pulay term takes the eigenvalues of the operator the density diagonalises — the solvated Fock), now demonstrated by the 6e-9 end-to-end FD agreement.
Changed: dispersion="d4" cites the full three-paper upstream set (2026-06-11)¶
routes.methods.d4now fires Caldeweyher 2017 (precursor: charge-dependent ζ(q) reference-polarizability scaling) and Caldeweyher-Mewes-Ehlert-Grimme 2020 (periodic extension, PCCP 22, 8499) alongside the 2019 method paper — the three papers the upstreamdftd4authors ask users to cite. Maintainer decision 2026-06-11, recorded on the route row: the 2020 paper is routed deliberately even though vibe-qc’s D4 is molecular-only (periodic runs still apply D3(BJ)); the set is not split per platform. All three areprint = true(references block +.bibtex/.references, not just the.systemmanifest). Damping-parameter fit papers continue to route separately via[routes.dispersion_params]. Coverage + e2e gates extended (test_citations.py,test_feature_citation_end_to_end.py).
Added: selected-CI PT2 reaches run_job; legacy selected-CI PT2 made coherent (2026-06-11)¶
CASSCFOptions(pt2=SelectedCIPT2Options(...))(requiresci_solver="selected_ci"): after convergence, the per-root Epstein-Nesbet PT2 correction is computed on the final selected wavefunction in the converged orbital basis (vibeqc.solvers.selected_ci_pt2; deterministic or semistochastic per the options). The headline energy stays the variational CASSCF value; the PT2 estimate surfaces onSolverResult.selected_pt2and as a dedicated .out block (E_PT2,E_var+PT2, error bar for semistochastic runs), and the Sharma-2017 citation reaches the references block. Exact-CI backends reject the option with a clear error (no external perturbers).Fixed: the legacy
method="selected_ci"do_pt2_correctionestimate is now the coherent Epstein-Nesbet PT2. The historical implementation summed(c_I H_aI)^2per (generator, perturber) pair, neglecting cross-generator interference in the numerator; perturber numerators now accumulate over all generators before squaring (Sharma et al, JCTC 13, 1595 (2017), Eq. 4). The unrestricted path routes through the same shared kernel asselected_ci_pt2(single canonical implementation); the spin-restricted (seniority-zero) path applies the same regrouping. Measured effect: H2O/STO-3G truncated-space PT2 moves by +0.23 mHa (the incoherent sum overestimated |E_PT2|); pinned against an independent dense-matrix Eq.-4 oracle to 1e-13 intests/test_solvers_selected_ci.py.method="selected_ci"/transcorrelated_citotal energies that include the PT2 correction shift accordingly.
Fixed: CASSCF orbital_step="auto" hands over to Newton-CG on Super-CI stalls (2026-06-11)¶
casscf(stall_to_nr=1e-4): a second auto-mode trigger alongside theswitch_to_nrgradient threshold. When Super-CI’s energy progress over the last 3 macro-iterations falls belowstall_to_nrper iteration while |g| is still above the gradient switch, the optimizer hands over to Newton-CG. This is the stiff large-CAS regime where the diagonal-Hessian step used to plateau without ever reaching the switch (the documented selected-CI CAS(14,14) tail: |g| ~ 2-6e-2, ~5 µHa/iteration for 100+ iterations); near convergence the same trigger merely starts the NR endgame early.stall_to_nr=0restores gradient-only switching (pinned).Measured on the previously stalling case (N2/6-31G CAS(14,14) selected-CI, 20k determinants):
"auto"now converges in 29 macro-iterations / 110 s to the same stationary point as the hand-picked"nr"(-109.07272812 vs -109.07272740), where it previously ran 100 iterations / 531 s without converging. The “prefer nr for selected large-CAS” guidance is retired from the user guide; existing fast-lane and slow regression suites are unchanged.
Added: semistochastic Epstein-Nesbet PT2 on selected-CI wavefunctions (25i, 2026-06-11)¶
vibeqc.solvers.selected_ci_pt2: the SHCI perturbative stage (Sharma, Holmes, Jeanmairet, Alavi & Umrigar, JCTC 13, 1595 (2017)) as post-processing on a convergedselected_casciresult. Per root, the second-order Epstein-Nesbet correction over perturbers outside the selected space with COHERENT numerators (contributions summed over all generators before squaring, Eq. 4), screened at|H_ai c_i| >= eps2(Eq. 5) with the heat-bath walk pruning the enumeration.Semistochastic mode (
n_samples >= 2): deterministic part ateps2_looseplus the unbiased sampled difference estimator ofE2[eps2] - E2[eps2_loose](Eqs. 7-11), batches drawn with replacement fromp_i ~ |c_i|by inverse-CDF search over the exactly-specified mt19937_64 stream (fixed seed = bitwise reproducible per build; std::discrete_distribution is implementation-defined and deliberately avoided). Both thresholds share each batch’s samples, so the stochastic error largely cancels (Eq. 11);eps2_loose == eps2is pinned identically zero-noise.Validation (
tests/test_selected_casci.py): deterministic kernel == independent dense-matrix Eq.-4 oracle == brute Python kernel to 1e-14; walk == brute at pruning eps2; symmetry-block exhaustion yields E2 = 0 exactly; estimator unbiasedness verified analytically (multinomial moments) and statistically (pulls -0.06/-0.05/+1.29 sigma across seeds at 6000 batches); fixed-seed determinism pinned.Scale (N2, 20k selected determinants, single core): CAS(10,26) deterministic
eps2=1e-6(10.5M perturbers) 7 s, CAS(10,40) (26.9M perturbers) 19 s; semistochasticeps2=1e-9totals with 2-5 µHa statistical error in 10-30 s. Citation:sharma_semistochastic_hci_2017entry +selected_ci_pt2route.
Added: heat-bath presorted candidate walks + incremental sparse H in the selected-CI kernel (25i follow-up, 2026-06-11)¶
SelectedCIOptions.select_eps(default 0 = exact CIPSI scoring) is now reachable from Python and implemented identically in both kernels: drop generator I’s contribution to candidate D when|H_DI| * max_k|c_I^k| < select_eps. The Python kernel applies the predicate by brute force (the oracle); the C++ kernel walks presorted |H|-ordered double-excitation lists (Holmes-Tubman-Umrigar JCTC 12, 3674 (2016): a double excitation’s |H| depends only on its four orbital indices) and stops at the first sub-threshold entry. Walk and brute prefilter accept exactly the same candidate set (bit-identical predicate, monotone positive-double multiplication); pinned by a C++ A/B hook (use_heat_bath_walk=false) plus python==cpp parity at pruning eps intests/test_selected_casci.py.Incremental sparse Hamiltonian: the C++ kernel extends H across selection cycles instead of rebuilding it (each determinant’s excitation rectangle enumerated once per solve, with symmetric mirroring into already-built rows). This was the measured wall at 25+ active orbitals, not selection.
Measured (N2 actives, 20k selected dets, single root): CAS(10,26) 11.0 s -> 3.9 s at
select_eps=3e-5(energy cost 5 µHa, prefilterincremental H); CAS(10,40) 23.7 s -> 11.6 s (cost 11 µHa). The walk’s edge over the brute prefilter grows with the generator count (1.24x at
significant_coeff=1e-5, 40 orbitals); it is the structural enabler for the deterministic side of the upcoming semistochastic PT2.
Changed (BREAKING): state-averaged CASSCF defaults to spin-pure root selection (2026-06-11)¶
CASSCFOptions.spin_pure=Nonenow resolves toTruefor every state-averaged run (nroots > 1), standalonemethod="casscf"included (previously: only multi-state CASPT2 compositions). The SA average targets the lowestnrootsroots with S = ms2/2, S^2-filtered each macro-iteration; the M_s determinant sector’s interleaved higher-spin roots (a triplet between the two lowest singlets on essentially every small CAS) no longer silently enter the average. This is the averaging CSF-based codes (OpenMolcas, MOLPRO) perform by construction: the default LiH SA2 now lands in OpenMolcas’s singlet-averaged RASSCF solution to <=4e-8 Ha. Maintainer-approved 2026-06-11.Migration: SA-CASSCF energies, excitation gaps, and SA-referenced CASPT2/NEVPT2/MRCI numbers change wherever the old raw-sector average mixed spins. Pass
CASSCFOptions(spin_pure=False)(orcasscf(spin_pure=False)) for the deliberate mixed-spin ensemble; that path is pinned against PySCFstate_average_(direct_spin1raw-sector averaging) intests/test_solvers_sacasscf.py. Single-state runs (nroots=1) are unchanged.Per-root
<S^2>joins the .out root table (and the multi-state block gains a model-space<S^2>line), so the averaged spin composition is visible in every multi-root output; surfaced onSolverResult.root_s2. Diagnostic degrades to omission above 200k determinants or on any failure.Moved pins, same commit: the LiH “default keeps the M_s-sector roots” regression became the explicit
spin_pure=Falseopt-out pin plus a new spin-pure-default pin (tests/test_ms_caspt2.py); the PySCF SA cross-check carriesspin_pure=Falseexplicitly (tests/test_solvers_sacasscf.py); the SA .out block test pins the new<S^2>column (tests/test_cas_output_block.py).
Fixed: CCSD PySCF cross-check harness; measured cross-code parity 2e-11 Ha (2026-06-11)¶
The
tests/test_ccsd.pycross-checks un-xfailed by the d2e46663 CCSD rewrite were red on machines with PySCF installed, for harness reasons, not physics: the test unpacked PySCF’sCCSD.kernel() -> (e_corr, t1, t2)as(e_corr, _, e_ccsd), comparing a float against the t2 amplitude array (ValueError/TypeError on H2O/CH4 and all (T) variants), and both codes’ CC solvers ran at conv_tol 1e-8, so the two loose fixed points differed by ~2e-7 on H2/sto-3g against a 1e-7 assert. PySCF tolerances must also be set on the objectdensity_fit()returns, not before it. With the harness fixed and both solvers at conv_tol 1e-10, all 9 tests pass and the measured vibe-qc-vs-PySCF E_corr agreement on matched DF setups is 2.4e-11 Ha (H2/sto-3g, DF and conventional references alike). Thetests/test_ccsd.pytracking entry added by 0c9d16e0 is removed fromscripts/test_gate/known_reds_baseline.jsonper its own instruction.
Added: spin-pure state averaging composes with the selected-CI CASSCF backend (25i follow-up, 2026-06-11)¶
casscf(spin_pure=True, ci_solver="selected_ci")(and theCASSCFOptionsequivalents throughrun_job) no longer raises: each macro-iteration solves a buffered root set in the selected determinant space and keeps the lowestnrootsS^2-pure roots, the dense filter’s semantics (_selected_ci._spin_pure_selected_roots, selected-space analogue of_ms_caspt2._spin_pure_roots). The defaultspin_completealpha/beta-swap closure makes the truncated space spin-flip invariant, so singlet-triplet mixing is symmetry-blocked and truncated roots stay spin-pure far below the classification tolerance.Parity (pinned in
tests/test_selected_casci.py): the selected filter reproduces the dense filter exactly at the full-selection limit (LiH CAS(2,2) helper-level, bit-identical); spin-pure selected SA2-CASSCF == dense spin-pure SA2-CASSCF at the full-selection limit on H2O CAS(4,4) (identical trajectories, <=5e-9 Ha, Python + C++ kernels); truncated runs stay converged + spin-pure; therun_jobcomposition lands in OpenMolcas’s singlet-averaged RASSCF solution on LiH (<=5e-6 Ha, same recorded constants as the dense M10 pin).This removes the last blocker for flipping the standalone SA-CASSCF default to spin-pure root selection (maintainer-approved 2026-06-11; the flip itself lands separately with its breaking-change note).
Fixed — multi-k GDF compcell Fock lost Hcore in the M3b-4b/4c refactor; rsgdf cderi is the default (2026-06-11)¶
The compcell branch of
run_krhf_periodic_gdfdiagonalised the bare two-electron Fock: the M3b-4b/4c COSX refactor moved the XC block into the J/K branches and left the Hcore (+V_xc) fold in the EWALD_3D branch only. Every multi-k compcell SCF since then walked from a physical first iteration to a spurious fixed point and reportedconverged=True(LiH FCC (2,2,2): −2.96 Ha instead of −7.92; the iteration-1 energy was correct, making the bug invisible to trace-level checks). Fold restored in the compcell branch.gdf_methodnow defaults to'rsgdf'— the (k_bra,k_ket)-ket-resolved all-FT cderi (build_lpq_bloch_native_fft), the route validated at µHa parity vs PySCF.'compcell'(q-only cderi — M3b-5 finding #1, exact only in the vacuum-box limit) stays selectable for its own development.run_krks_periodic_gdfforwardsgdf_method/rsgdf_ke_cutoff/check_energy_sanity.Both HANDOVER_RIJCOSX_M3A.md § M3b-5 findings resolved (addendum in place): with the restoration + this fix the default route lands LiH FCC (2,2,2) at −7.92039 Ha at the cutoff-15 production default — the documented validation value (PySCF −7.92200; +1.6 mHa lattice-cutoff truncation) — so the k-space GDF route is again a trustworthy absolute anchor on tight 3D ionic cells.
Closed-shell multi-k UHF ≡ RHF again (
test_closed_shell_uhf_matches_rhf_at_111_meshun-xfailed): the 0.93 mHa disagreement diagnosed 2026-06-10 was the merge-drop — UHF had been reverted to the pre-f7ee5832 split J while RHF kept the analytic FT; both route throughewald_3d_j_blocksagain.test_single_k_mesh_matches_gamma_ewald_driverun-parked fromknown_reds_baseline.json: the prescribed fix (“re-apply the a261613a F1 force block”) is exactly what the restoration did; the test passes.Cross-backend pins re-measured against the corrected physics: the (1,1,4) COSX-vs-GDF envelope (single-gauge now), and the BIPOLE leg of
test_periodic_rhf_backends_have_explicit_current_ parity_contract(43ea7490 had deliberately moved the Γ BIPOLE energy without updating the contract).
Fixed — b4a6faba merge-drop audit: periodic accelerator family, gauge/ω defaults, multi-k GDF parity restored (2026-06-10)¶
Systematic audit of the b4a6faba merge resolution (2026-06-09) — the
commit whose dropped driver surface 9c4205a9 restored and whose
EDIIS/ADIIS validation drop the entry below restores. Beyond those two,
it had reverted seven more files byte-for-byte to their eb8f25bd
(2026-05-24) state and hand-merged three others, silently wiping ~15
commits of landed work (accelerator family M2d 5c333372 / M2e
cc90574e / M3 2c1617d3 / M4 bfda02b6; gauge enforcement
859efe0a / a261613a / d316f266; ω-default unification cf67b741
/ d4133738 / 9566467f / 433d3543; multi-k GDF parity 024bcc17
/ 500711b6 / a1731e96 / 5fee9fe9; smearing hardening 377d2cd1
/ e790f558). All of it restored, re-fitted to the COSX-era code where
the multi-k GDF path was redesigned post-merge:
Multi-k Ewald drivers (RHF/RKS/UHF) + single-k UHF/UKS Ewald + both dispatch layers restored to the audited pre-merge state — full
{DIIS, KDIIS, EDIIS, EDIIS_DIIS, ADIIS}+dynamic_dampingwiring, EWALD_3D gauge forcing, analytic-FT Hartree J, CRYSTAL-α ω defaults. (At HEAD the family had been inconsistent: never-revertedperiodic_uks_multi_k_ewald.pykept all of this while its RHF/RKS/UHF siblings lost it.)Γ-RHF Ewald ω default returned to CRYSTAL α = 2.8·V^{-1/3} (
d4133738): the partial restore07bd9d59had rebuilt the gauge guard on top of the reverted file, resurrecting the deprecated geometry-based√(−log tol)/cutoffformula thatewald_composed.auto_ewald_alpha’s own deprecation note documents as gauge-breaking. The explicitomega=Nonekwarg semantics from07bd9d59are kept.GDF drivers re-wired to the accelerator family (M4) — Γ (
run_rhf_periodic_gamma_gdf) and multi-k (run_krhf_periodic_gdf) again route throughPeriodicSCFAccelerator/MultiKPeriodicSCFAccelerator+DynamicDamping; the resurrected_reject_unsupported_python_acceleratorgate and_PulayDIISalias are removed again (M4 end-state).Multi-k GDF physics restored on the COSX-era driver (all four pieces had regressed; the post-merge test re-baseline
d024bf99unknowingly pinned the broken −2.80467 Ha):HF/hybrid auto-route to the exxdiv-corrected compcell path (
500711b6, maintainer decision 2026-06-04) — without it the default path ran divergence-uncorrected Ewald-3D-K;per-(k_i,k_j)-resolved all-FT cderi cache (
build_lpq_bloch_native_fft,024bcc17) — the COSX-era rebuild cachedbuild_lpq_bloch_compcell(q = k_j − k_i), a q-only cderi that is exact only in the vacuum-box limit;BvK-supercell exxdiv Madelung (
_madelung_for_kmesh,024bcc17) instead of the primitive-cell ξ (over-counts the K-shift by Nk^(1/3); LiH (2,2,2): −592 mHa);exxdiv Madelung counted once — the shift’s energy enters through the shifted K inside
½·Tr[D·F₂ₑ]; the rebuilt path had addedexxdiv_ewald_energy_shifton top of it (double-count, ~−150 mHa on H₂/12-bohr (2,1,1));post-SCF
_check_energy_sanityguard +check_energy_sanityandrsgdf_ke_cutoffkwargs (5fee9fe9). With all four restored, the H₂/STO-3G 12-bohr (2,1,1) default-path energy returns to the published −1.12013988 Ha (Sun, Berkelbach, McClain & Chan, J. Chem. Phys. 147, 164119 (2017), Table II; agreement to sub-µHa) and the two post-merge own-output re-pins are corrected back to that documented reference (d024bf99’s −2.80467 Ha intest_periodic_k_gdf.py;0a7ac7bb’s −1.35721 Ha “post-wire” intest_pbc_gdf_compcell.py, whose docstring had kept claiming PySCF parity all along).
run_rks_periodic_gamma_gdfalias +vibeqc.*export restored (cf67b741) — the merge had wiped the definition and then removed the dangling__init__import as “stale”.13 accelerator-uniformity tests restored (multi-k ×4, BIPOLE ×5, GDF ×4 in
tests/test_periodic_accelerator_uniformity.py), replacing the resurrected obsolete gate tests.COSX-vs-GDF deviation envelope re-measured against the corrected GDF reference:
test_krhf_cosx_backend_scf’s ball-truncation envelope moves 62.3 → 131.0 mHa at (1,1,4) — the experimental COSX K did not change; its GDF comparison side did. (The M3b-4 tightening plan is unaffected.)docs/user_guide/scf_convergence.mdrestored to the completed M1→M4 rollout support matrix (incl. the M2c per-k KDIIS design and M2e stacked-real-block bridge footnotes).
Fixed — multi-k EWALD_3D J raised on 1D/2D systems (2026-06-10)¶
periodic_fock_multi_k.ewald_3d_j_blocksnow routes dim<3 systems to the retained FFT-Poisson Ewald-split backend: the analytic-FT J kernel (f7ee5832, 2026-06-08) is 3D-only (4π/G² on a 3D G-mesh) and raisedValueErroron every dim<3 multi-k EWALD_3D run — every multi-k SCF on a chain or slab. It landed red undetected because CI is collect-only (.gitlab-ci.yml); surfaced by the restored multi-k accelerator-uniformity tests (H₂-chain fixtures).
Added — D4 damping-parameter fit papers reach the references block; D4 runs cite the method paper (2026-06-11)¶
Citation-discipline (§ 8) audit of the inline doi= provenance in
vibeqc/dispersion_d4_parameters.py — a run now cites the paper its
D4 damping parameters actually come from:
Fixed:
run_job(..., dispersion="d4")cited nothing for D4. The runner’s citation block only knew the D3(BJ) key; theroutes.methods.d4route (Caldeweyher 2019) existed but never fired. D4 runs now cite the method paper.New
[routes.dispersion_params]table in the citation database (keyed"d4:<normalized-functional>") + adispersion_params=argument onassemble(): parametrizations whose damping fit was published separately from the 2019 D4 method paper additionally cite the fit paper. Five new entries: Santra-Sylvetsky-Martin 2019 (revDSD-BLYP / revDSD-PBEP86 / revDSD-PBE / revDOD-PBEP86), Ehlert 2021 (rSCAN / r²SCAN), Najibi-Goerigk 2020 (ωB97X), Friede 2023 (LC-ωPBE), and Grimme-Bannwarth-Shushkov 2017 (GFN1-xTB damping provenance); the r²SCAN hybrids, r²SCAN-3c, and GFN2-xTB rows route to their pre-existing entries (Bursch 2022 / Grimme 2021 / Bannwarth 2019). Composite recipes dedup cleanly (r²SCAN-3c already cited its defining paper through the method route).New public
normalize_d4_key()indispersion_d4_parameters(the lookup normalizationget_d4_paramsalways applied, now shared with citation routing).Coverage gate:
tests/test_citations.py(TestD4ParameterDoiCoverage) mechanically pins every inlinedoi=in the parameter table to a database entry, every separately-published fit to a firing route, and every route back to a live parameter row;tests/test_feature_citation_end_to_end.pypins the runner wiring (HF-D4 → Caldeweyher; r²SCAN-D4 → Caldeweyher + Ehlert) on the emitted.out/.bibtex/.referencessurface.Docs:
user_guide/citations.md§ routing updated;user_guide/functionals.mddispersion example fixed (it passeddispersion=throughRHFOptions, which raisesTypeError— the real argument lives onrun_job).
Added — B3LYP flavor spellings + cross-code flavor audit; parity-pairing fixes (2026-06-11)¶
Bare b3lyp keeps the ORCA definition (maintainer ruling):
libxc HYB_GGA_XC_B3LYP5 (id 475, VWN5 local correlation), as it
has since v0.8.0. What’s new is the explicit flavor surface and a
measured cross-code audit of who means what by “B3LYP” (the +6.8
mHa PySCF question — see the Phase 4a entry below):
New spellings:
b3lyp5(explicit name for the default — use it in cross-code tables to be unambiguous) andb3lypg(PySCF-style spelling of the Gaussian/VWN-RPA variant, == the existingb3lyp/g). Deliberately nob3lyp3: libxc’sLDA_C_VWN_3is the Ceperley-Alder fit III (== VWN5 on closed shells), not Gaussian’s “VWN(III)” (the RPA fit) — the obvious name would pick the wrong flavor.Audit, H₂/STO-3G at 1.4 bohr (RKS, conv 1e-12): vibe-qc
b3lyp−1.1586001482 Ha == PySCF 2.13b3lyp5(libxc 7.0.0) to <1e-9 == CRYSTAL14B3LYP(−1.1586001474, XLGRID, local binary) == ORCAB3LYP(−1.158669, DefGrid2); vibe-qcb3lyp/g==b3lypg−1.1654009284 Ha == PySCF 2.13b3lypANDb3lypgto <1e-9 == ORCAB3LYP/G(−1.165470). Gap = 0.19·(E_c[VWN-RPA] − E_c[VWN5]) = −6.8008 mHa — convention split, not a bug. Camps: VWN5 = ORCA / TURBOMOLE / ADF / CRYSTAL / CP2K (+ vibe-qc); VWN-RPA = Gaussian / libxc / PySCF / Psi4 / NWChem / Q-Chem.Parity-pairing fixes: the Psi4 and GPAW regression runners paired bare names across the flavor gap (vibe-qc VWN5-
b3lypvs their VWN-RPAb3lyp/B3LYP) — now flavor-matched (Psi4b3lyp5, GPAWHYB_GGA_XC_B3LYP5); the PySCF twin scripts of the periodic example fleet (examples/periodic_pyscf/) had the same latent mismatch and now spellb3lyp5. ORCA / PySCF / CRYSTAL / CP2K runner maps gain explicit rows for every spelling; the Gaussian-flavor spellings are rejected by the CRYSTAL + CP2K runners (neither code can express VWN-RPA).D3(BJ) / D4 damping-parameter lookup accepts all flavor spellings (they share the single
b3lypparameter set).Citations:
hertwig_koch_1997(“What is B3LYP?”) added and routed on every flavor spelling so Methods sections can pin the variant;vosko_wilk_nusair_1980note now records that both fits come from that one paper.Docs:
user_guide/functionals.md§ B3LYP rewritten (camp tablemeasured values + practical guidance);
from_gaussian/from_pyscf/from_orca/from_crystal/external_codes/features/touraligned. Tutorial 15’s B3LYP row still carried the pre-v0.8.0 Gaussian-flavor numbers — regenerated with the actual default (and tutorial 37’s row re-verified). Tests:tests/test_b3lyp_convention.pypins both flavors, the gap, spelling equivalence, and the composite / dispersion / citation surfaces; a PySCF-sideB3LYPGgradient-parity row covers the Gaussian flavor.
Fixed: dotted imports of compiled pybind11 submodules survive sys.modules purges (2026-06-11)¶
Running the full pytest suite in a single process (plain
pytest tests/, unlike the per-file-isolated test gate) failed ~72 semiempirical tests withModuleNotFoundError: No module named 'vibeqc._vibeqc_core.semiempirical'while the same files passed in isolation. Root cause: module-level test-isolation shims intests/basisset_dev/purgevibeqc*fromsys.modules; pybind11’sdef_submoduleregisters the dotted submodule names (vibeqc._vibeqc_core.semiempiricalplus.nddo/.xtb/.indo) only during the extension’s one-timePyInit, which CPython never re-runs for cached single-phase-init extensions, so re-imports could not restore the keys and every later lazy dotted import (runner.py,vibeqc/semiempirical/methods/) failed for the rest of the process.vibeqc/__init__.pynow re-registers the compiled submodule tree on every package import, thetests/basisset_dev/headers keepvibeqc._vibeqc_core*entries out of their purges (the three shim-installing files also snapshot + restore the real modules after their module-level imports, fixing collection errors in partial runs), andtests/test_compiled_submodule_registration.pypins the contract. Details inHANDOVER_TEST_HEALTH.md.Follow-up: with the extension’s
sys.modulesentries preserved across a purge, the freshimport vibeqccache-hits the submodule and skips the loader step that sets the_vibeqc_coreattribute on the new package object, so attribute-chain access (vibeqc._vibeqc_core.compute_eri) raised AttributeError in 6 tests while from-imports kept working via the interpreter’ssys.modulesfallback.vibeqc/__init__.pynow binds the attribute explicitly (plainfrom . import _vibeqc_core), with a third regression test simulating the exact core-preserving purge.
Fixed — native-D4 damping table: BHLYP was mis-keyed as hcth120; four more rows corrected against dftd4 (2026-06-11)¶
The per-functional D4 BJ-damping table
(vibeqc.dispersion_d4_parameters, shipped v0.10.0) keyed the
dftd4 bhlyp row as hcth120 — and transcribed the bj-eeq-mbd
variant at that, where every other row uses the bj-eeq-atm
default — with an alias bhlyp → hcth120 on top. BHLYP-D4
silently used MBD-variant damping; HCTH120-D4 claimed a
parametrisation dftd4 does not ship. A full sweep against the
canonical dftd4 4.2.0 parameters.toml surfaced the rest:
bhlyp(BHandHLYP) now carries its real BJ-EEQ-ATM row (s8 1.65281646, a1 0.27263660, a2 5.48634586, doi 10.1063/1.5090222);hcth120is removed and raisesKeyError— dftd4 has no HCTH-120 D4 parametrisation.revdsdpbep86: s6 0.5552 → 0.5132 (0.5552 is revDOD-PBEP86’s s6 — a neighbouring-row copy-paste).mpwpw: the alias targeted a key that never existed (mpw1pw), so the lookup crashed with a rawKeyError; it now has dftd4’s exact row.b1lyp,revpbe0dh: upgraded from approximate aliases (→blyp, →pbe0dh) to their exact dftd4 rows.
New regression tests pin all five rows and, when the dftd4
package is importable, sweep the entire table against its shipped
parameters.toml (tests/test_d4_model.py).
Fixed — a no-extras import vibeqc was broken (undeclared SciPy + eager optional-ASE imports) (2026-06-11)¶
A clean, extras-free pip install -e . followed by import vibeqc
failed, because import vibeqc pulled in two third-party packages that
[project.dependencies] did not provide:
SciPy — imported unconditionally via
vibeqc.density_fitting(scipy.linalgat module load). It is genuinely core, so it is now a declared dependency (scipy>=1.11).ASE — the eager re-exports of the ASE GPW Calculator wrapper (
VibeqcGPW) and the ASE-driven GAPW MD drivers (run_md/run_npt/run_nve/run_nvt) hard-require ASE, which is an optional extra ([ase]). Those two imports invibeqc/__init__.pyare now guarded, soimport vibeqcsucceeds without ASE; the wrapped names appear only when the[ase]extra is installed.
Both were masked because the [test]/[dev] extras pull pyscf (→
SciPy) and ase. Surfaced provisioning the twin cluster, whose
minimal --extras none install exposed it.
Fixed — BIPOLE UHF/UKS Γ join the corrected gauge; UKS hybrid α/2 exchange bug (option (b) Phase 4b, 2026-06-11)¶
run_pbc_bipole_uhf and run_pbc_bipole_uks adopt the corrected
gauge at 3D Γ (default; same surface and provenance flags as
RHF/RKS): full-Bloch spin densities, no spheropole term, per-spin
Ewald exchange split K_σ = K_SR(erfc ω; D_σ) + K_LR(D_σ) + (ξ_M − π/(Vω²))·S·D_σ·S (PySCF exxdiv='ewald' per-spin
convention), α_HF-scaled for UKS hybrids. At the split, J-linearity
lets TWO fused per-spin erfc traversals replace the legacy THREE
builds (J_SR(D_total) = jk_α.J + jk_β.J) — a net efficiency win on
every UHF/hybrid-UKS iteration; pure-functional UKS skips the
discarded per-spin K traversals entirely.
Pre-existing UKS hybrid bug fixed (any gauge): the j-split UKS
Fock applied −½·α_HF·K_σ to PER-SPIN densities — the ½ belongs
to the closed-shell convention where K is built from the doubled
TOTAL density (cf. the RKS driver), so UKS hybrids effectively ran
with α/2 exchange (invisible to the pure-functional test
surface; the UHF driver’s unscaled convention was correct). The
factor is now −α_HF·K_σ, variationally consistent with the
reported e_exchange. A positional density-block lookup in the
same loop was replaced by cell-key pairing.
Cross-code validation (out-of-process PySCF 2.13 GDF, H₂/STO-3G
triplet 12-bohr Γ, cutoff 6): UHF +0.07 mHa (legacy gauge:
+7.1 mHa), UKS SVWN +1.0 mHa, UKS PBE0 +0.78 mHa (certifies
both the per-spin split and the α-factor fix). Runner exposes the
knobs for UHF/UKS. With this, all four BIPOLE drivers run the
corrected gauge at Γ; the remaining option (b) work is the multi-k
q≠0 exchange channels (Phase 3) and the Phase 5 sweep. Tests:
tests/test_bipole_fock_ewald_exchange.py.
Changed — vq: fleet-update hardening (vq admin update robustness) (2026-06-11)¶
vq admin update --all-hostsnow defaults to--serial(one host fully updated before the next); pass--parallelfor the previous concurrent fan-out. Serial keeps a build break or a flaky link on one host from fanning out to all at once, and stops per-host output from interleaving.VQ_UPDATE_SCRIPT_TIMEOUT(seconds, in the daemon’s environment) overrides the 1800 supdate_scriptcap. A cold native-dep rebuild (libint from scratch on a 6-core box) can exceed 30 min and was getting killed mid-build; read at call time so it applies without a code change.Cold-build PATH fix: the
update_scriptnow prepends Arch/Manjaro/usr/bin/{core,vendor,site}_perl(wherepod2manlives — used by libecpint’s bundled libcerf man-page step) when present, so a cold rebuild doesn’t die withpod2man: command not found. No-op where pod2man is already on PATH.Transient-SSH retry: delegated admin-update fleet commands retry an SSH transport failure (exit 255 / connect timeout) twice with linear backoff, so a momentary LAN/WAN blink no longer aborts a fleet sweep. The retry is opt-in (
retry_transient), so read-only sweeps (queue / status / programs--all) stay fast-fail; a genuine remote non-zero exit is never retried, andvq submitis excluded (no double-submit). Tests:tests/test_fleet_update_hardening_v0_11_0.py.
Fixed: GAPW dispersion raises loudly when the dftd3 bindings are missing (2026-06-11)¶
Requesting
dispersion=on the GAPW/GPW SCF entry points (run_periodic_rhf_gpw,run_periodic_rks_gpw_multi_k, and the augmented variants) in an environment without thedftd3python bindings silently fell back to the approximate builtin supercell backend:compute_d3bj_periodic’s defaultbackend="auto"resolves to"builtin"when dftd3 is absent, so the shim’s guard never fired and the SCF total quietly absorbed an approximate dispersion energy with no diagnostic. The shared_compute_d3_correctionshim now pinsbackend="dftd3"(the bit-exact lattice sum; same resolution as"auto"whenever the bindings are installed) and raisesNotImplementedErrorwith a remediation hint when they are missing. The standalonevibeqc.compute_d3bj_periodicAPI keeps its documented auto fallback; only the SCF entry points pin the backend. Catches the previously failingtest_rhf_gpw_dispersion_raises_when_dftd3_missing, which only runs in venvs without dftd3 (gate venvs with dftd3 skip it, which is why CI never saw the failure).
Added: C++ selected-CI kernel; CASCI past the 2M-determinant wall (25i, 2026-06-11)¶
cpp/src/selected_ci.cpp: the production backend forselected_casci: sparse Slater-Condon Hamiltonian over uint64 SpinDet bitmasks (hash-probed connection finding, all five excitation classes), block Davidson (mirrors the proven casci.cpp expansion/restart logic incl. its 2026-06-10 stagnation fix: relative acceptance floor + raw-residual fallback), the same coherent multi-root CIPSI selection criterion as the Python kernel, optional heat-bath prefilter (select_eps), and a pair-based truncated-list RDM12 (direct two-body Slater-Condon between list determinants, exact without spill tracking). Dispatch:VIBEQC_SELECTED_CI_BACKEND=auto|python|cpp(auto: full spaces1e5 determinants go C++); truncated-list
make_rdm12also routes to the C++ pair kernel above the existing 2000-det threshold.Parity (pinned in
tests/test_selected_casci.py): full- selection limit vs dense CASCI 1.4e-14 (closed shell, 2 roots) / 6.2e-15 (open shell); truncated-list RDM12 vs the spill-aware Python path 4.4e-16; same-threshold energies match the Python kernel; exact-limit selected-CI CASSCF == determinant CASSCF.Past the direct-CI wall: N2/6-31G CAS(14,14), 11.8M determinants (6x the direct engine’s 2M cap), runs as selected CASCI in 0.5-15 s at 5k-50k selected determinants with a monotone plateau (recorded regression window), and as a full selected-CI CASSCF:
orbital_step="nr"(the frozen-selection Newton-CG) converges in 8 macro-iterations / 104 s to |g|=1.6e-5 at -109.07272740 Ha, 72.7 mHa below the HF-orbital CASCI. The defaultautostalls in the Super-CI first-order tail above the NR switch at this scale; prefer"nr"for selected large-CAS runs (recorded in HANDOVER_CAS.md; auto-switch retuning is an open polish item).
Fixed: 2026-05-18 code-audit remediation lands on main (2026-06-11)¶
Five correctness findings from the 2026-05-18 code audit, originally
remediated on claude/peaceful-edison-d4bc5c (2026-05-20) and now
re-derived onto current main. (The audit’s other three findings,
memory pre-flight backend branching and the two incremental-Fock
items, landed independently via main back in May.)
Patch-candidates for v0.11.x.
ECP analytic gradient (P1). Gradient drivers built the nuclear-repulsion piece and the V_ne derivative with bare all-electron
Zand never added a∂V_ECP/∂Rterm, even though the SCF usedZ_eff = Z − n_core+V_ECPin Hcore; geometry optimization and forces with ECPs were unusable (audit probe: analytic z-gradient ~10x the finite-difference value on Zn-He/def2-SVP/ecp28mdf). Fix:nuclear_repulsion_gradientandone_electron_gradient_contributiongain effective-charge overloads, a newcompute_ecp_gradient_contributioncontracts the density with libecpint’scompute_first_derivsoutput, and all fourcompute_gradient*drivers route through aclassical_hcore_grad_pieceshelper that branches onGradientOptions.ecp_centers/.ecp_library(new fields, mirrored off the SCF options by the ASE calculator). The all-electron path is bit-for-bit unchanged. Tests:tests/test_ecp_gradient.py(analytic vs FD ≤ 1e-5 Ha/bohr).ASE /
optimize=True/ FD-Hessian forces match the SCF Hamiltonian (P1/P2). Force calls dropped the SCF’s density-fit / COSX / aux-basis (and now ECP) settings and silently differentiated the four-index all-electron Hamiltonian. A new_gradient_options_from_scfhelper threads a matchingGradientOptionsthrough the ASE calculator’s force branch and the FD-Hessian path (compute_hessian_fdgains agradient_optionskeyword). Tests: backend-threading pins intests/test_ase.py(helper field copy; calculator forces match the DF gradient, not the direct one).GradientOptions.cosxhonoured for UHF/UKS gradients (P2). The unrestricted DF gradient helper ignored the flag, so DF and RIJCOSX gradients were bit-identical; K now routes per spin through the chain-of-spheres kernel withα_kernel = 2·α_HF(reduces to the RHF expression in the closed-shell limit). Tests: two new RIJCOSX gradient pins intests/test_rijcosx.py.SCF energy consistent with the returned density/Fock (P3). The four molecular drivers rebuilt the final F/D after convergence but kept the pre-rebuild energy (~1e-3 Ha gap at loose tolerance);
result.energyand thee_coulomb/e_hf_exchange/e_xcdecomposition are now recomputed on the rebuilt pair (DFT+U terms re-reported on the fresh density). This supersedes the comment-side resolution ofb7afe538:result.energynow agrees withscf_trace[-1].energyonly to within the convergence band, andtests/test_scf_log.pyasserts that band instead of exact equality. New regression:tests/test_scf_final_consistency.py(energy reproduces from the returned matrices to 1e-12 Ha).Dynamic damping from zero works for RKS/UHF/UKS (P3). The static-damping mixer gated on the immutable
opts.dampinginstead of the iteration-localcurrent_damping, sodamping=0.0, dynamic_damping=Truenever applied damping in the three non-RHF drivers (RHF was already correct). Tests: from-zero engagement pins for all four drivers intests/test_dynamic_damping.py(no-DIIS iteration-count, convergence-rescue, and iteration-2 trace-divergence signatures).
Added: selected-CI active-space kernel + CASSCF backend (25i, 2026-06-11)¶
vibeqc.solvers.selected_casci: CIPSI selected CI with acasci()-compatible interface (active space + frozen-core dressing,ms2, multi-root with coherent multi-root selection scores, M_s=0 spin-completion,det_guesswarm start,CASCIResultoutput). Variational at every cycle; full-selection limit reproduces the dense CASCI to <=1e-10 (closed- and open-shell pinned).Truncated-list RDMs are now exact:
make_rdm1/make_rdm12use spill-aware generator products (out-of-list components ofE_pq|psi>enter the 2-RDM through intermediate dot products; previously the Python path crashed on non-closed determinant lists). Full-CAS lists are bit-identical to before;make_rdm123/make_rdm1234reject truncated lists with a clear error. Pinned bytests/test_selected_casci.py(E(RDM) == E(var) to 1e-12 on a genuinely non-closed space).casscf(ci_solver="selected_ci", selected_ci_options=...)(andCASSCFOptions/run_job, label suffix_selci): the selected kernel runs inside every macro-iteration, warm-started across iterations; the FD Hessian-vector probes (and only they) freeze the selection (the Newton model must differentiate one fixed-selection surface; re-selecting per probe breaks FD antisymmetry and stalls the line search). Exact-limit parity with the determinant CASSCF <=1e-10 on LiH (SS+SA2) and H2O (SS); truncated runs land variationally above (H2O CAS(4,4) at 8/36 determinants: +0.36 uHa). MR-PT2/MRCI compositions reject the selected backend (they re-solve the full CAS downstream). Python-kernel scale: ~10^3 selected determinants; the C++ heat-bath kernel for 30+ orbital actives is the next 25i step.
Added — DLPNO examples + tutorial (2026-06-11)¶
examples/molecular/input-h2o-dlpno-mp2.py— one-call DLPNO-MP2 plus the TCutPNO convergence sweep against canonical RI-MP2 (99.77% → 99.84% → 99.90% → 100.0000% at the full-domain zero-threshold limit, measured on H₂O/cc-pVDZ);input-h2o-dlpno-ccsd-t.py— the CCSD(T) pilot with the cost-cap guidance.docs/tutorial/dlpno_local_correlation.md— worked tutorial: one-call run, the threshold-convergence experiment with real numbers, frozen core + distant-pair screening, the CCSD(T) pilot and what backs its numbers; registered in the tutorial toctree and cross-linked from the DLPNO user-guide page.
Performance: EWALD_3D periodic SCF caches the analytic-FT Hartree J across iterations (2026-06-11)¶
The Γ and multi-k EWALD_3D SCF drivers rebuilt the analytic AO-pair Fourier
transform, the dense G-mesh and the 4π/G² kernel every iteration — the
dominant Γ-Ewald cost (PBC audit docs/pbc_audit_2026-06.md §E2). Those
iteration-invariant pieces are now built once per SCF (mirroring the GDF
Lpq cache) and only the density-dependent contraction is redone per
iteration. Energies are bit-identical — pure caching, no physics change —
pinned by a new gate tests/test_ewald_j_cache.py. ~4.7× on H₂/STO-3G/30-bohr
Γ-RHF; the speedup scales with iteration count and cell size. Wired into the
RHF/UHF/RKS/UKS Γ drivers and the RHF/RKS/UKS multi-k drivers. (UHF multi-k was
initially skipped — at the time it still built J via the real-space erfc +
FFT-Poisson split; the b4a6faba merge-drop restoration put it back on the
shared ewald_3d_j_blocks analytic-FT entry, and it now takes the same
per-cell lattice cache as its siblings.)
Fixed: C++ DF-CCSD/(T) rewritten to validated closed-shell equations; experimental gate lifted (2026-06-10)¶
The defect (flagged by the DLPNO anchor, see the M3a entry below): the experimental C++ DF-CCSD (
VIBEQC_EXPERIMENTAL_CCSD=1) claimed 74% more correlation than full CI on H2O/STO-3G (-0.096681 vs FCI -0.055588 Ha) and did not converge. The T2 residual mixed spin-orbital permutation structure with spatial closed-shell amplitudes and layered a partial “DressJK” integral dressing on top; the (T) code path inherited the same confusion.The fix:
compute_residuals, the dressed-Fock build andcompute_triplesincpp/src/ccsd.cppwere rewritten from scratch as the closed-shell spin integration of the Stanton-Gauss-Watts- Bartlett (1991) equations, the same set the FCI-anchored in-repo referencevibeqc.dlpno._ccsd_refimplements in spin orbitals. Every spatial term was validated against that kernel to machine precision at random amplitudes before transcription to C++; the (T) correction implements Raghavachari 1989 classwise over closed-shell spin blocks, verified the same way (derivation documented inline). Independent cross-check: the C++ (T) on H2O/STO-3G (-76.577 µHa) agrees with the independently written spin-orbitalso_triples_correctionlanded the same day by the DLPNO chat (M4, -76.6 µHa).Gates (
tests/test_ccsd_anchor.py, always on, no external deps): C++ CCSD correlation within 5e-6 Ha of the spin-orbital anchor on H2O/STO-3G (-0.055443 Ha, measured agreement 1e-9 Ha) and H2O/def2-SVP (-0.216091 Ha, slow lane); strictly above the FCI correlation; (T) pinned on both bases (-0.000076577 / -0.003222076 Ha). The PySCF cross-checks intests/test_ccsd.pyare un-xfailed and run wherever PySCF is installed.Experimental gate lifted:
run_job(method="ccsd"/"ccsd(t)")andvibeqc.cc.run_ccsdno longer requireVIBEQC_EXPERIMENTAL_CCSD=1. Scope unchanged: closed-shell RHF references, dense O(N^6) small-molecule pilot (handovers/HANDOVER_CCSD.mdhas the roadmap).Adjacent defects fixed while ungating: frozen-core runs indexed the DF blocks with the wrong occupied window (n_frozen_core > 0 was broken); the MP2 initial guess used (ij|ab) instead of (ia|jb);
run_jobCCSD crashed attaching the CC result to the pybind RHFResult, results now come wrapped in_CCSDAugmented(.ccsd,.energy_total); therun_jobfrozen-core default now counts chemical cores per atom (vibeqc.cc.chemical_core_orbital_count: CO2 freezes 3, not 1); CCSD DIIS extrapolates post-Jacobi amplitudes per the reference-kernel convention.Citations: new
stanton_gauss_watts_bartlett_ccsd_1991entry routed for ccsd/ccsd(t), replacing thescuseria_schaefer_ccsd_1988route (which described the previously claimed, never-implemented equation set); verified live in.out/.bibtex/.references/.system. Stale pre-rewrite debug scriptsexamples/regression/scripts/cc_{ref,debug,verify}.pydeleted.
Added: spin-pure state-averaged CASSCF root selection (2026-06-10)¶
casscf(spin_pure=True)/CASSCFOptions.spin_pure: state-average over the lowestnrootsspin-pure CI roots (S^2-filtered with a root buffer, re-selected each macro-iteration; reuses the multi-state model-space filter) instead of the raw lowest M_s-sector roots, matching what CSF-based codes (OpenMolcas RASSCF) average. LiH SA2 lands in OpenMolcas’s singlet-averaged solution to <=4e-8 Ha (recorded). DefaultNonekeeps the historical M_s-sector behavior standalone and resolves toTruewhen the CASSCF is the reference of a multi-state CASPT2 composition, making the standard SA-CASSCF -> MS/XMS-CASPT2 workflow reproduce OpenMolcas full&RASSCF+MULTistate/XMULtistateend to end (<=4e-8 Ha observed, gated at 5e-6; recorded intests/test_runner_ms_caspt2.py).
Added: MS-CASPT2 / XMS-CASPT2 multi-state engine (2026-06-10)¶
vibeqc.solvers._ms_caspt2.ms_caspt2: multi-state CASPT2 on the explicit determinant IC engine: spin-pure model space (⟨S²⟩-filtered CASCI roots, matching what CSF-based codes see in one spin sector), per-state first-order wavefunctions from the (refactored, byte- identical)_ic_caspt2_solve, explicit determinant-space couplings ⟨Φ_i|H|Ψ_j⁽¹⁾⟩, symmetrized Finley-1998 effective Hamiltonian.mode="ms"(state-specific Fock, OpenMolcasMULTistate) andmode="xms"(model-space SA-Fock rotation + shared state-averaged H₀, Granovsky 2011 / Shiozaki 2011, OpenMolcasXMULtistate). Exported asvibeqc.solvers.ms_caspt2/MSCASPT2Result.Validation (
tests/test_ms_caspt2.py, 20 tests): machine-precision invariants (nroots=1 ≡ SS-CASPT2; explicit ⟨Φ_i|H|Φ_j⟩ block vs CASCI eigenvalues; ⟨Φ|H|Ψ⁽¹⁾⟩ ≡ E2; XMS rotation diagonalizes the model Fock; H2 Σg/Σu symmetry-zero couplings) + live OpenMolcas parity recorded for 7 cases (LiH eq./stretched, H2; MS + XMS; σ=0 ≤ 7e-8 Ha observed, gated at 5e-6).Imaginary-shift representation caveat characterized: vibe-qc applies the Forsberg-Malmqvist shift to the full nondiagonal H₀ (basis-independent, both engine paths); OpenMolcas adds σ²/Δ on its case-block-diagonal representation. Identical without intruders (<1e-8), ~1e-5–1e-4 Ha apart near strong intruders (stretched-LiH excited root); documented in
caspt2()/ms_caspt2()docstrings, σ≠0 multi-state parity gated at 5e-4.OpenMolcas regression runner extended:
run_caspt2(nroots=, multistate=)writesCIRoot = N N 1+MULTistate/XMULtistate = all, parses per-root RASSCF / SS-CASPT2 / MS-CASPT2 energies (_MS_CASEStable regenerates the recorded constants).run_jobwiring:CASPT2Options(multistate="ms"|"xms", nroots=N);nrootsinheritscasscf_options.nrootsfor the SA-CASSCF composition. MS/XMS eigenvalues land inSolverResult.root_energies(lowest asenergy); the effective Hamiltonian / mixing / per-state diagnostics in the newSolverResult.multistatedict and a dedicated.outblock; method label gains_ms{N}/_xms{N}. Rejectsvariant="sc",ipea!=0andnroots<2with clear errors. Citations: Finley 1998 fires for both modes, Granovsky 2011 + Shiozaki 2011 for XMS (newdatabase.tomlentries, conditional assembly at the runner).tests/test_runner_ms_caspt2.pypins the public-API energies against the recorded OpenMolcas constants plus the.out/.bibtex/.referencessurfaces. User guide: multi-state section indocs/user_guide/non_hf_solvers.mdincl. the spin-mixed SA-CASSCF composition caveat.
Added — DLPNO-CCSD / CCSD(T) user-facing pilot: run_job wiring (M5b) (2026-06-10)¶
run_job(method="dlpno-ccsd"|"dlpno-ccsd(t)", dlpno_ccsd_options=DLPNOCCSDPilotOptions(...))— closed-shell RHF reference, auto-resolved RI auxiliary, decomposed.outblock with an explicit O(N⁶)-pilot cost note,dlpno_ccsd_donestructured-log event,_DLPNOCCSDAugmentedresult proxy (.dlpno_ccsd,.energy_total). Jobs are hard-capped atDLPNOCCSDPilotOptions.max_nbf = 64basis functions until the reduced-scaling engine (M3c) lands — the cap raises a clear error, deliberately overridable.Citation routes for
dlpno-ccsd/dlpno-ccsd(t)enriched (Purvis- Bartlett 1982, Riplinger 2013 ×2, sparse-maps 2016, Raghavachari 1989, Feyereisen RI 1993, Foster-Boys 1960); fixed a latent TOML duplicate-key collision between the new rows and the 2026-06-06 infrastructure rows that silently disabled.references/.bibtexemission for these methods.User guide: the DLPNO page becomes “DLPNO methods (MP2, CCSD, CCSD(T))” with a pilot section + honest cost guidance. Tests:
tests/test_runner_dlpno_ccsd.py.
Added — DLPNO-(T) triples, FCI-bounded (M4) (2026-06-10)¶
_ccsd_ref.so_triples_correction— the textbook Raghavachari (T) in spin-orbital form (per-triple evaluation, no t₃ tensor materialised), wired into both the reference kernel (run_ref_ccsd(compute_triples=True)) and the DLPNO pilot (DLPNOCCSDPilotOptions(compute_triples=True)).Pilot (T) is exact, not semicanonical: converged DLPNO amplitudes are rotated back to the canonical occupied basis before the triples evaluation, eliminating the (T0) approximation in the oracle (the production T0/T1 tradeoff is M3c scope). Untruncated localised pilot agrees with the canonical reference to 0.6 nHa.
Gates (
tests/test_dlpno_ccsd.py::TestTriples): (T) ≡ 0 for two electrons (exact null test); on H₂O/STO-3G (T) = −76.6 µHa closes 53% of the CCSD−FCI gap and CCSD(T) lands between CCSD and the full-space CASCI(=FCI) energy with no overshoot; truncated-PNO (T) stays within 30% of the untruncated value at TCutPNO=1e-8.
Removed — pre-M2 DLPNO placeholder modules retired (M3b) (2026-06-10)¶
dlpno/solver.py,dlpno/df_bridge.py,dlpno/pno.py,dlpno/integrals.py,dlpno/provider.py,dlpno/pair_pair.py,dlpno/triples.pyare deleted. Their physics lives on — validated — indlpno.mp2(PNO construction, semicanonical energies, coupled LMP2) anddlpno.ccsd(subspace-projected CCSD); the (T) module returns fresh in M4 on top of converged pilot amplitudes. Nothing user-facing imported the retired modules;tests/test_dlpno.pykeeps the still-live pair-classification / PAO / threshold units and the integration test now exercises only real paths.
Fixed — relaxed-scan convergence flags (optimizer restart polish) (2026-06-10)¶
relaxed_scanno longer reports spurious non-convergence. On a shallow constrained surface, scipy L-BFGS-B’s relative-energy (ftol) criterion halted eachoptimize_moleculestep after only a handful of its allotted iterations, leaving the free-atom max-gradient just aboveconv_tol_grad; the audit-F1 gradient gate then correctly reportedconverged=False, so a smooth, correct O–H stretch curve still carriedconverged_flags = [F, T, F, F, F](4/5 points falsely “not converged”).optimize_moleculenow restarts L-BFGS-B from the premature stop with the energy criterion disabled (ftol=0), so the fresh inverse-Hessian builds curvature and runs to the gradient tolerance within the remainingmax_iterinstead of re-trippingftolby steepest descent. The restart is gated onconv_tol_energy <= conv_tol_grad, so a deliberately loose energy tolerance is still honored as an early stop. The shared audit-F1 gradient gate (_gradient_converged, also used by the BIPOLE relaxers) is unchanged — the scan energies and located minima were already correct; only the flags were pessimistic (tests/test_relaxed_scan.py,tests/test_optimize_neb_robustness.py).
Added — vq: per-job --refresh <env> — drain + rebuild a venv-env before a job runs (2026-06-10)¶
vq submit <host> --refresh <env> script.pyrecordsJobSpec.refresh_before. When that job is next to dispatch, the daemon drains the host (lets RUNNING/orphan jobs finish, holds new dispatch), runs the env’sgit pull+update_script(the same work asvq admin update <env>), then dispatches the job against the freshly-rebuilt venv. A failed rebuild lands the job in FAILED with afailure_reasonnaming the env — it never runs against a broken or half-built env, and a failing refresh can never wedge the queue (the FAILED job drops out of the pending set). The refresh is per-job and explicitly opted into, so the submitter owns the choice — no unattended auto-deploy. Motivation: you push tomain, then have an overloaded fleet host rebuild before running your job, drop-proof — the daemon owns the rebuild, so a client disconnect can’t interrupt it (unlike a client-connectedvq admin update).Single-file submit only in v1 — rejected at the CLI for
--array/--chain(submit the refresh as a standalone job first). Forwarded across the remote-submit boundary, sovq submit <remote> --refresh …rebuilds on the remote host. AdditiveJobSpecfield — pre-v0.11.0 specs read clean. Tests:tests/test_refresh_before_v0_11_0.py(field round-trip, CLI guards, remote forwarding, daemon orchestration success / failure / admin-error / drain).
Added — DLPNO-CCSD pilot: subspace-projected CCSD, FCI-anchored (M3a) (2026-06-10)¶
vibeqc.dlpno._ccsd_ref— spin-orbital Stanton-Gauss-Watts- Bartlett (1991) CCSD reference kernel on DF integrals: the in-repo ground truth for coupled-cluster numerics. Anchored against exact references: CCSD≡FCI on H₂ (agrees to the independently measured 6 µHa RI-fit error) and sits the physically required ~0.3% above the full-space CASCI(7,10)=FCI correlation on H₂O/STO-3G.vibeqc.dlpno.ccsd.run_dlpno_ccsd_pilot— DLPNO-CCSD with the Riplinger-2013 ansatz (per-pair PNO doubles spaces, diagonal-pair singles spaces) evaluated by expansion/projection through the anchored engine: physically exact subspace-constrained CCSD with zero equation-transcription risk. Gates (tests/test_dlpno_ccsd.py): untruncated limit ≡ anchor to 0.000 µHa on H₂, H₂O/STO-3G (canonical and Boys-localised) and H₂O/def2-SVP; TCutPNO=1e-8 recovers 99.86% of E_corr with 14.9 of ~20 virtuals per pair (slow lane). O(N⁶) pilot by design — the reduced-scaling per-pair contraction engine (M3c) validates against these tiers.Found via the anchor: the experimental C++ DF-CCSD (
VIBEQC_EXPERIMENTAL_CCSD=1) overshoots the FCI correlation energy by 41 mHa on H₂O/STO-3G — flagged to the cc workstream with a definitive reproducer; the newTestAnchor::test_h2o_anchor_sits_above_fci_by_triples_margingate prevents regressions of this class.
Added — Periodic COSX M3b-6: SR cutoff tables, derived cell domain, basis envelope (2026-06-10)¶
erfc-aware per-pair SR cutoff tables: with ω > 0,
build_cosx_cell_pair_cachesprobes the validated kernel itself at increasing pseudo-nucleus distances and tabulates, per (δ, s_ν, s_σ), the radius beyond which the SR A-block is below the screening floor — exact w.r.t. both the erfc range and the pair-density extent (no analytic extent model). The engine skips pairs past their radius and whole (c_σ, δ) combinations past the per-δ maximum. Setup cost ~1 s for 417 δ’s on LiH; the periodic test suite runs 3.5× faster.Basis-derived SR cell domain:
KPointCosxKnow sizes the SR cell list from the tabulated shell extents (1e-4 level) + the kernel range, decoupled from the one-electroncutoff_bohr(capped at 40 bohr).Real-space SR envelope criterion (CRYSTAL-style diffuse-exponent constraint, now measured and enforced): shells reaching past ~25 bohr at the 1e-4 level are outside the envelope — a warning points at the k-space GDF exchange or periodic-adapted (pob-class) bases. Measured anchors: H/sto-3g extent 20.6 bohr — chain ω-invariant at 1e-4; LiH/sto-3g Li 2p extent 42.8 bohr — ω-invariance broken at 2e-2…3.6e-2 for 15…40-bohr domains (no feasible real-space domain covers α_min = 0.048 shells). Pinned:
test_cosx_sr_envelope_warning_lih.
Fixed — BIPOLE RKS Γ joins the corrected gauge (option (b) Phase 4a, 2026-06-11)¶
run_pbc_bipole_rks adopts the corrected gauge at 3D Γ (default,
same auto/opt-out surface as RHF): full-Bloch density — which also
fixes the XC grid density (the Γ-locality projection dropped the
cross-cell AO products from ρ(r)) — no spheropole term, and for
hybrids the α_HF-scaled Ewald exchange split. Pure functionals need
no exchange machinery, and the legacy path’s wasted full-Coulomb K
traversal is now skipped for pure functionals (it was built
unconditionally and discarded). Cross-code (out-of-process PySCF 2.13
GDF, H₂/STO-3G 12-bohr Γ, cutoff 6): SVWN +0.05 mHa (−1.12122748
vs −1.12127725; the legacy gauge sat 24 mHa off), PBE +0.05 mHa,
PBE0 (hybrid split path) +0.08 mHa. B3LYP differs by +6.8 mHa —
RESOLVED: vibe-qc’s b3lyp follows the ORCA convention
(libxc B3LYP5/VWN5, per the documented alias map and maintainer
confirmation); PySCF 2.13’s bare b3lyp evaluates the VWN_RPA
(Gaussian-style) variant — vibe-qc’s b3lyp/g reproduces PySCF
bit-exactly (−1.1654009284). Not a bug in either code. (The
follow-up flavor audit + explicit spellings landed the same day —
see the Added — B3LYP flavor spellings entry above.) A positional
density-block lookup in the RKS exchange-energy contraction was
replaced by cell-key pairing (required by the wide density list).
Runner exposes the knobs for RKS. UHF/UKS propagation and the multi-k
q≠0 channels (Phase 3) follow. Tests:
tests/test_bipole_fock_ewald_exchange.py (SVWN/PBE0 PySCF pins,
legacy-gauge stability, multi-k gate).
Added — DLPNO-MP2 user-facing: run_job(method="dlpno-mp2") (M5a) (2026-06-10)¶
run_job(method="dlpno-mp2", dlpno_options=DLPNOMP2Options(...))runs RHF thenvibeqc.dlpno.mp2.run_dlpno_mp2with the auto-resolved correlation (“ri”) auxiliary basis. The.outsolver block reports the full energy decomposition (iterated pairs, PNO-truncation correction, distant-pair estimate, pairs kept/screened, avg PNOs/pair, frozen core); the structured log gainsdlpno_mp2_done; the result object is an attribute-forwarding_DLPNOMP2Augmentedproxy with.dlpno_mp2and.energy_total. Closed-shell references only (clear error otherwise). Tests:tests/test_runner_dlpno_mp2.py.Citations wired (§ 8 discipline):
routes.methods["dlpno-mp2"]emits Møller-Plesset 1934, Feyereisen 1993 (RI), the newpinski_dlpno_mp2_2015database entry, and Foster-Boys 1960 into.references/.bibtexfor every dlpno-mp2 job.User guide: new page
docs/user_guide/dlpno_mp2.md(quick start, threshold table with measured recovery tiers, decomposition anatomy, limitations), cross-linked from the MP2 page and the user-guide toctree.
Added — UNO open-shell starting reference for the CAS family (2026-06-10)¶
UHF natural orbitals (UNO-CAS — Pulay & Hamilton, JCP 88, 4926 (1988)) are now the open-shell (multiplicity > 1) starting reference for the determinant-solver family (casci / casscf / mrci / nevpt2 / caspt2 / selected_ci / dmrg / v2rdm / transcorrelated_ci):
get_hf_orbital_provider(method="uno")diagonalizes the total UHF density in the orthogonalized AO basis; occupations cluster at 2/1/0, so one spin-restricted orbital set carries the open-shell character and the descending order matches the lowest-core / active-window convention. The previous behavior used the UHF α orbitals only, leaving the β space — and hence the doubly-occupied core — only approximately represented.run_job(cas_reference="rhf"|"uhf"|"uno")overrides; closed-shell jobs are unchanged.Validation: UNO occupations match PySCF
mcscf.addons.make_natural_orbitalsto ≤1e-8 (O2 triplet); on O2/STO-3G CAS(2,2) the UNO reference lowers CASCI by 2.1 mHa (to within 4 µHa of converged CASSCF) and cuts CASSCF from 27 to 4 macro-iterations into the same stationary point (tests/test_uno_reference.py).Citations:
pulay_hamilton_uno_1988added; fires automatically when an open-shell CAS-family job resolves to the UNO reference (verified end-to-end into the.bibtex).
Added — Davidson warm start for CASSCF macro-iterations (2026-06-10)¶
casci_direct_solve(…, guess=): the Davidson subspace can be seeded with prior CI vector(s) (orthonormalized + noise-dressed like the default determinant seeds; cold start when empty). Threaded throughsolvers.casci(ci_guess=)(dense backend ignores it) and the CASSCF macro-iteration loop, which now seeds every CASCI solve — including Newton-CG Hessian-vector probes and line-search evaluations — with the most recent CI vector(s).Benchmark: N2/6-31G CAS(10,10) CASSCF, warm vs cold under identical machine load: 490 s vs 632 s (1.29× end-to-end; identical stationary point to 3e-14). At the Davidson level an exact-vector guess converges in 1 iteration vs 19 cold — the guess columns are deliberately NOT noise-dressed (the symmetry-breaking seed noise stays on the unit top-up seeds; dressing the guess would re-inflate its residual to the noise scale and discard the warm-start benefit).
Tests (
tests/test_casscf_scale.py::TestDavidsonWarmStart): warm-start/cold-start eigenpair parity (multi-root), guess-shape validation, and warm-started CASSCF convergence to the recorded LiH minimum.
Added — DLPNO-MP2: first physically correct DLPNO method (M2) (2026-06-10)¶
vibeqc.dlpno.mp2.run_dlpno_mp2— local MP2 in the Neese-group DLPNO framework (Pinski et al., JCP 143, 034108 (2015)): Foster-Boys-localised occupieds, per-pair PAO domains with S-orthonormal semicanonical virtual bases (pao.semicanonical_pao_basis), real density-fitted exchange integrals per pair, PNOs from the semicanonical pair density with quasi-canonicalisation and the standard ΔE_PNO truncation correction, and a coupled LMP2 residual iteration carrying the localised-occupied Fock coupling with amplitude projections between PNO bases. Diagonal pairs and antisymmetrised closed-shell pair energies(2−δ_ij)·Σ K(2T−Tᵀ)— the two structural gaps of the earlier scaffold — are in.Validated against vibe-qc’s canonical DF-MP2 (same fitting basis,
tests/test_dlpno_mp2.py): exactness limit (full domains, no PNO truncation) agrees to ≤1 µHa on H₂O/STO-3G (canonical and localised occupieds), H₂O/def2-SVP, and NH₃ — and default thresholds (TCutPNO=1e-8) recover 99.87% of E_corr on the DZ case (99.93% at 1e-9) with genuine PNO compression (14.9 of ~19 virtuals per pair). The M1 strict-xfail parity ratchet flipped on schedule and is now a hard assertion.Not yet wired into
run_job(that is M5, after DLPNO-CCSD); pair prescreening defaults off until the M2b calibrated distant-pair estimates land. The pre-M2dlpno.solver/dlpno.triplesscaffolds remain non-physical and are slated for replacement in M3/M4 — now tracked inHANDOVER_GATED_ITEMS.md.M2b (same day): frozen-core support (
n_frozen, validated ≤1 µHa against a canonical frozen-core DF-MP2 reference), real semicanonical dipole-dipole distant-pair estimates (Riplinger 2013 §II.C) withe_distantaccumulation and an 8-bohr trust radius (3×H₂ @ 12 bohr: all inter-unit pairs screened, net error 2.5 µHa), and batched per-pair DF transforms via a single occupied half-transform.
Fixed — DLPNO M1: localisation correctness + honest validation harness (2026-06-10)¶
Foster-Boys Jacobi angle fixed (
vibeqc.localise): the rotation angle omitted the−¼(D_ii−D_jj)²term, so sweeps maximised the wrong functional and stalled short of the maximum (H₂O/STO-3G objective 2.45 vs 3.22 after the fix). Now the standard Edmiston-Ruedenberg branch (θ = ¼·atan2(B, −A)).Pipek-Mezey fixed: coefficients were never rotated inside the Jacobi sweeps, so Mulliken populations went stale after the first rotation; transition populations are now symmetrised and the working coefficients rotate in sync. Both localisers gain physical tests (
tests/test_localise.py): H₂O must yield 1 core + 2 equivalent O–H bonds + 2 equivalent lone pairs, objectives must strictly increase, spans must be preserved.DLPNO integration test rebuilt on real inputs (
tests/test_dlpno_integration.py): the previous test multiplied bohr coordinates by Å→bohr again (1.81 Å O–H bonds, E_HF −74.505 instead of −74.964), used STO-3G as its own RI fitting basis, built Foster-Boys from hand-rolled approximate dipoles, and asserted only sign/finiteness. Now: correct geometry,def2-svp-rifit, exactcompute_dipoleintegrals, canonical-anchor assertions, and astrict=Truexfail parity ratchet vs canonical DF-MP2 (same fitting basis) that flips when the M2 solver-physics work closes the gap. Measured baseline recorded in the retired DLPNO handover: the current per-pair solver recovers 2.9% of the canonical DF-MP2 correlation energy (placeholder PNO energies / missing diagonal pairs / unantisymmetrised pair energies — all M2 scope).DLPNO handover rewritten to reality: per-component real-vs-placeholder inventory and the M1–M5 milestone plan (C++ translation deferred until the physics validates).
Added — imaginary level shift on the direct large-CAS CASPT2 path (2026-06-10)¶
caspt2(…, imaginary=σ)now works at scale: the Forsberg-Malmqvist shift no longer forces the explicit small-CAS engine — large shifted jobs dispatch to the direct active-CI signature-block solver. The Hylleraas level-shift-corrected energy (Forsberg & Malmqvist 1997, Eq 11) is evaluated without any eigendecomposition via one real SPD solve: withA = H̃0−E0andz = (A²+σ²)⁻¹V,E2(σ) = −(V+σ²z)·(Az), solved by CG with per-block(B²+σ²)⁻¹preconditioning (solve_signature_block_linear_system_imaginary). Only the IPEA shift still pins jobs to the explicit engine.Validation: machine-precision agreement (≤5e-16) with the explicit engine’s eigenbasis form on H2/6-31G, LiH/STO-3G and H2O/STO-3G at σ = 0.1 / 0.3 (CG converges in 4-5 iterations); at scale, H2O/cc-pVDZ CAS(4,4) with
Imaginary=0.1reproduces live OpenMolcas (-76.22073564) to all printed digits through the publiccaspt2()direct dispatch (tests/test_caspt2_rdm_factorization.py).
Added — CAS-family .out diagnostics + roadmap sweep (2026-06-10)¶
CAS results in the
.outsolver block:method="casci"|"casscf"| "caspt2"|"nevpt2"runs now print the reference wavefunction’s leading determinant configurations, per-root energies (multi-root CASCI / SA-CASSCF, also when an SA reference feeds a PT2), and the active natural-orbital occupations; the same diagnostics ride onSolverResult(ci_coeffs,ci_labels,rdm1,root_energies). The active 1-RDM routes through the C++ kernel for large spaces and degrades to omission (never a crash) if diagnostics fail (tests/test_cas_output_block.py).Roadmap shipped-sweep: v0.21.0 items 25a-c (production FCI/CASCI) and 25d-e (production-scale CASSCF orbital optimisation + state-averaging) marked shipped with the 2026-06-10 capabilities; 25f/25g notes updated for the CASSCF→NEVPT2/CASPT2 composition and the direct large-CAS CASPT2 route.
Fixed — BIPOLE Γ absolute energies: Ewald exchange split lands (option (b), 2026-06-10)¶
The root-caused defects below are fixed at Γ-only sampling (the default for 3D Γ runs; multi-k keeps the legacy convention until the q≠0 LR-exchange channels land — Phase 3):
Exchange now uses the Ewald split, symmetric with the J split:
K = K_SR(erfc ω, direct) + K_LR(erf ω, reciprocal K≠0) + (ξ_M − π/(Vω²))·S·D·S. The G=0S·D·Sbookkeeping removes the K=0 component embedded in the convergent directK_SRsum and adds the probe-charge Ewald (Madelung) finite-size correction (exchange_exxdiv='ewald', PySCF-equivalent;'none'available). The legacy full-Coulomb direct K — formally divergent with the non-decaying Γ Bloch density — survives only behinduse_exchange_ewald_split=False. New helperscompute_K_long_range_gamma(reuses the J^LR FT cache; zero new C++) andprobe_charge_madelung(pinned to PySCFtools.pbc.madelungat 1e-9, α-invariant) invibeqc.bipole_fock_ewald. K_SR + J_SR now come from ONE fused traversal (was two).The Γ-locality density projection is OFF in this gauge: the SCF runs on the full Bloch fold, fixing the projected-density T/V_ne/J deviations (−0.26/+0.20/−1.02 Ha on MgO).
EXT EL-SPHEROPOLE is omitted from the total (gauge double-count; the kernel + CRYSTAL-parity tests stay).
Cross-code validation (out-of-process PySCF 2.13 GDF): MgO/STO-3G Γ RHF converges from SAD to −271.0286 vs PySCF −271.0497 (+21 mHa, cutoff-12 truncation scale) — pre-fix this sat +2.5 Ha off. Fixed-density components: +17 mHa at cutoff 10; exchange element-wise |ΔK|_max 8e-4 at cutoff 14, ω-invariant. H₂/STO-3G 12-bohr box converges to −1.12258512 vs PySCF −1.12258397 (~1 µHa) at cutoff 12. Regression suite:
tests/test_bipole_fock_ewald_exchange.py(fast self-contained pins; the ~1–2 h MgO c12 run is documented inHANDOVER_BIPOLE_PRODUCTION.md+ the parity scripts).Corrected-gauge cutoff requirement + guard. Unlike the legacy projected gauge (whose energy never contracted cross-cell operator blocks), the corrected gauge contracts full Bloch folds — diffuse AO tails demand fold-converged cutoffs (MgO/STO-3G S(Γ)-fold truncation: 0.39 at 8 bohr → spurious SCF states possible; 5e-3 at 12; 2.3e-6 at 16). The driver now measures the S(Γ) fold drift against a 1.5× cutoff and warns (loudly above 1e-2). The SCF density is stored on a 2×-cutoff cell list so the C++ builder resolves every P(b−a) difference its traversal forms (silent lookup-skips removed; traversal cost unchanged), and
_lattice_contract_blocksiterates operator cells (density looked up by key).Basin caveat for absolute pins: minimal-basis ionic Γ SCF has multiple genuine solutions — PySCF itself converges MgO/STO-3G Γ to −271.050 or −270.935 depending on the starting density. The corrected-gauge driver is basin-stable at fold-converged cutoffs: the MgO c12 SCF reaches the identical stationary point (−271.02857708, same to the last digit) from SAD and from the PySCF converged density. Converged absolute comparisons should still fix the basin explicitly on pathological cells.
The analytic-gradient preview implements the legacy gauge and now refuses split-gauge SCF results with a clear error (
result.exchange_ewald_splitprovenance flag); FD gradients differentiate the actual driver energy and follow the new gauge automatically. Migration tracked for Phase 5.
Root-caused — BIPOLE ionic-crystal absolute energies (2026-06-10, late)¶
Fixed-density component audit against PySCF on MgO Γ (the forensic tool
lives at examples/regression/bipole_parity/mgo_component_audit.py):
at PySCF’s converged density, E_nn matches to µHa, and T/V_ne/J
match to cutoff truncation with the unprojected Bloch density while
deviating by −0.26/+0.20/−1.02 Ha with the driver’s Γ-locality
projection; the direct exchange needs a Bloch-correct cell
decomposition (Bloch input overcounts ~3×, projected undercounts
0.95 Ha); and the EXT EL-SPHEROPOLE (+3.0 Ha here) has no PySCF
counterpart — a double-count in vibe-qc’s explicit-jellium gauge
(its value matches CRYSTAL, but CRYSTAL’s gauge needs the term and ours
doesn’t). Reassembling the correct gauge from measured pieces
reproduces PySCF to ~18 mHa (cutoff-10 truncation). The fix is a
redesign of the BIPOLE energy assembly that invalidates the
analytic-gradient local-energy ∂E/∂P(0) gauge (“option (a)”) —
escalated as a maintainer design decision; admonitions updated in
docs/bipole_status.md + docs/troubleshooting.md.
Added — CASSCF at scale on the direct CI core (roadmap 25d-e) (2026-06-10)¶
RDM backend dispatch:
solvers._rdm.make_rdm12(and the SA variant) route full-CAS determinant spaces (>2000 determinants, canonical ordering verified structurally) through the C++casci_direct_rdm12kernel; truncated CI lists (selected-CI / CISD) stay on the Python path.VIBEQC_RDM_BACKEND=python|cpp|auto.Super-CI robustified: the diagonal-Hessian denominator is clamped to
max(|H_diag|, reg)— a negative diagonal element used to flip that step component uphill, which the backtracking line search then rejected (hard stall observed on N2/6-31G CAS(10,10) at iteration 28); a steepest-descent line-search fallback now also runs before the optimizer gives up. Pure Super-CI keeps a slow first-order tail and is the robust far-field method;auto(default) hands over to the second-order step near convergence.orbital_step="nr"reimplemented as trust-region Newton-CG (Steihaug-Toint truncated CG, Nocedal-Wright Alg. 7.2) with Hessian-vector products by central finite differences of the analytic orbital gradient — 2 CASCI solves per CG iteration, independent of the rotation-pair count. The historical per-pair FD Hessian (2·n_pairsCASCI solves per macro-iteration) was the large-CAS wall.Headline: N2/6-31G CAS(10,10) CASSCF — 63 504 determinants per CI solve, impossible on the dense path — converges in 48 macro-iterations / ~85 s (dev box) to |g| = 2e-7 at E = -109.0614472204 Ha. CASSCF is non-convex: PySCF
mcscf.CASSCFconverges to a different stationary point 8.13 mHa higher (-109.0533171021); the regression pins vibe-qc’s deeper stationary point + variational bounds (tests/test_casscf_scale.py). 34-test CASSCF regression suite (PySCF parity on unique-minimum systems, open-shell, SA, dissociation) unchanged and green.
Added/Fixed — PBC queue: J+K call-site fusion; GDF joins auto-convergence (2026-06-10)¶
EWALD multi-k UHF/UKS Fock builds halve their exchange cost: the per-spin
K = 2·(J_full − F_full)reconstruction (two full lattice-ERI traversals per spin) is replaced by the fused C++build_jk_2e_real_space(one traversal returns J and K). Operators pinned bit-identical on converged densities (test_fused_jk_exchange_matches_jfull_ffull_reconstruction).Pre-existing EWALD inconsistency diagnosed while validating (not caused by the fusion — proven on the pre-fusion code): the RHF and UHF multi-k EWALD drivers disagree by 9.3e-4 Ha on closed-shell H₂ [1,1,1], screening on or off — they build J through different machinery. xfailed with the diagnosis; EWALD-owner item in
docs/pbc_audit_2026-06.md.UHF multi-k EWALD smearing gate restored: the driver silently ignored
smearing_temperature(the historical gate was removed without updating its test); it now rejects finite temperature like the other HF paths.convergence="auto"now also active onjk_method="gdf"(queue #2): same transparency block and per-knob provenance; GDF gets no BIPOLE-style KS FMIXING floor (its drivers honor the resolved values verbatim — the floor is now route-conditional in the resolver). Γ-only result shapes handled in the post-SCF gap check.
Fixed/Added — IBZ meshes: BIPOLE shortcut was broken on real crystals; GPW/GAPW now honor SCF options (2026-06-10)¶
BIPOLE IBZ-reduced k-meshes were silently wrong on non-trivial crystals. The “IBZ-native” path diagonalised at the irreducible points and replicated D(k) into each star without the AO rotation
D(R·k) = P(R)·D(k)·P(R)ᵀ— exact only for trivial stars (the He-only validation cells). MgO/STO-3G [2,2,2] with an IBZ input mesh failed to converge 8.25 Ha away from the full-mesh result. All four BIPOLE drivers now expand IBZ inputs to the full MP mesh up front (correct by construction; the wall-clock IBZ saving is deferred). Regression:test_ibz_mesh_matches_full_mesh_on_mgo(full ≡ IBZ to 1e-9) — He-only symmetry validation is not validation.periodic_k_symmetry(new, experimental groundwork): validated star matching (star_operations) + per-k rotation transport + from-D(k) inverse-Bloch fold, with the full probe record of the open transport question (a subset of star operations transports converged MgO densities at 5e-15; the rest err at O(1), unexplained by reciprocal-translation phases, per-atom shift phases, parity, or time-reversal — suspected truncated-sum Fock asymmetry). True IBZ-native reduction for BIPOLE/EWALD/GDF multi-k is blocked behind this and tracked indocs/pbc_audit_2026-06.md.GPW/GAPW drivers now receive the runner’s SCF options (queue #5):
use_diis/damping/diis_subspace_size/diis_start_iterforwarded everywhere the drivers accept them, Fermi-Diracsmearing_temperatureforwarded on the closed-shell GPW/GAPW paths (the runner’s smearing gate now admits GPW/GAPW RHF/RKS), and a.outnote when a knob is not honored (GPW multi-k uses its internal scheme). Previously these options were printed in the.outbut silently ignored by the drivers.
Added — C++ direct determinant CAS-CI engine (roadmap 25a-c) (2026-06-10)¶
cpp/src/casci.cpp: string-based determinant CI — lexicographic α/β occupation strings, per-spin-sector Jordan-Wigner phases, chunked gather→GEMM→scatter sigma builds in the Knowles-Handy (CPL 111, 315 (1984)) spin-summed form, block Davidson eigensolver (J. Comput. Phys. 17, 87 (1975)) with determinant-diagonal preconditioning + deterministic symmetry-breaking seed noise (pure determinant seeds confine Davidson to the seeds’ point-group/spin sector and can silently skip roots), and spin-summed 1-/2-RDMs — all matching the Python determinant engine’s ordering, phase, and RDM conventions exactly (casci_direct_solve,casci_direct_rdm12,casci_direct_sigmabindings).Automatic backend dispatch in
solvers.casci: ≤4000 determinants keep the historical dense Slater-Condon +eighpath (bit-for-bit unchanged); larger spaces go to the direct engine, lifting the densemax_det=10_000wall to ~10^6-determinant spaces — e.g. H2O/6-31G CAS(10,12), 627 264 determinants, solves in ~9 s on the dev box.VIBEQC_CASCI_BACKEND=python|cpp|autoforces a backend; a non-converged Davidson raises rather than returning silently.Validation (
tests/test_casci_direct.py, 12 tests): eigenvalue + CI-vector parity with the dense engine across closed-shell / open-shell / multi-root / FCI-limit cases (≤1e-9), elementwise σ vs the dense Hamiltonian (machine precision), RDM parity vssolvers._rdm.make_rdm12, RDM-energy identity, FCI limit vs PySCF, and the full-space N2/STO-3G CAS(14,10) (14 400 determinants, beyond the historical dense cap).Citations:
davidson_iterative_1975added to the database;fci/casci/casscfroutes now fire Knowles-Handy 1984 + Davidson 1975 alongside Roos 1980.
Added/Flagged — Periodic COSX M3b-5: LiH real-crystal anchor; two GDF-route findings (2026-06-10)¶
COSX bridge production defaults: the multi-k bridge now uses the standard COSX quadrature tier (35/9/18 — matching molecular RIJCOSX; the prior XC-tier default was ~8× over-resolved) and prefilters the LR G-mesh by kernel weight before the expensive pair-FT calls (severalfold setup cut on 3D meshes). Matrix-level test bands updated for the tier (4e-3 / 1e-3 class); SCF-level backend parity is tier-insensitive and unchanged (0.024 mHa pin holds).
FLAGGED (GDF route): compcell multi-k is catastrophically broken on tight ionic cells.
build_lpq_bloch_compcellis q-resolved only — the architecture the RSGDF builder’s own docstring warns is “wrong by hundreds of Ha” with inter-cell overlap. LiH/sto-3g (2,2,2) with theuse_compcell=Truedefault: E = −64318 Ha, converged=True, no guard fires (silent wrong answer); (1,1,2): +3461 Ha. Reproduced bit-for-bit on the pre-COSX driver (7e9f5df9) — not a recent regression. The padded-system zone-edge deviation flagged at M3b-4b is the mild form of the same root cause. Pinned:test_compcell_q_only_lih_flag(Lpq-level, no SCF).FLAGGED (GDF route): rsgdf multi-k absolute energy on LiH (2,2,2) is E = −4.882767 on current main — identical across aux (def2-universal-jkfit / def2-svp-jk) and mesh (ke 100/200) — vs the documented validation −7.92039 (PySCF −7.92200). Needs out-of-process PySCF re-validation by the GDF route. Until both findings resolve, tight-3D-ionic absolute energies from the multi-k k-space GDF routes should not be trusted; the COSX real-crystal validation rests on backend parity at matched J, ω-invariance, and the ERI-exact block foundations.
Engine cost note: the SR pair screen still uses the 1/d Coulomb proxy (blind to erfc decay), so ~95-cell 3D balls run minutes per iteration — the erfc-aware per-pair cutoff table is the next performance item (handover § M3b-5/M3b-6).
Added — Periodic COSX M3b-4c: single-gauge production pairing (2026-06-10)¶
gdf_method='rsgdf'inrun_krhf_periodic_gdf: the multi-k Lpq cache can now be built via the all-FT Bloch-pair route (build_lpq_bloch_native_fft) instead of compcell. RSGDF-J + COSX-K share the Bloch pair-FT conventions — a single-gauge Fock, the consistent production pairing fork_exchange='cosx'(and the clean workaround for the flagged compcell zone-edge deviation on vacuum-padded systems).Production-gate numbers (gapped dimerized H2 chain): backend parity COSX-K vs RSGDF-K at matched J = 0.024 / 0.027 / 0.027 mHa at kmesh (1,1,2) / (1,1,4) / (1,1,6); clean SCF convergence through (1,1,8) in 6 iterations. The dense-mesh stalls reported at M3b-4b were entirely the mixed-gauge (compcell-J + exact-K) Fock — gone with the consistent pairing. The COSX backend is also 3–5× faster wall-clock at these meshes (no off-diagonal Lpq tensors at setup; mesh-independent K(g) build per iteration).
The
k_exchange='cosx'runtime warning is now conditional: the rsgdf pairing logs its validation status; the compcell pairing keeps the mixed-gauge warning until the GDF route resolves the flag.
Added — PBC queue: EWALD multi-k UKS smearing; auto-strategy post-SCF check (2026-06-10)¶
EWALD multi-k UKS gains Fermi-Dirac smearing (parity with the RKS twin; the
NotImplementedErrorgate is gone): per-spin fractional occupations with separate chemical potentials at fixed n_α/n_β (mirroring the BIPOLE UKS driver), convergence on the free energy A = E − T·S, andsmearing_temperature/fermi_level_alpha/beta/entropy/free_energyresult diagnostics. Regressions: gapped small-T limit matches the unsmeared energy; open-shell doublet runs.Auto-convergence strategy v2 — post-SCF re-classification check. On auto-mode runs the converged HOMO–LUMO gap (indirect, across the mesh; min over spin channels for UHF/UKS) is compared against the assumed profile: assumed-insulator converging effectively conducting (gap < 1e-3 Ha) emits a loud
UserWarning+ a “Convergence strategy check” block in the.outwith the recommended explicit smearing override; assumed-metal converging clearly insulating (gap > 0.1 Ha) gets an informational note. Manual-mode runs are never second-guessed. Generalizes the GDF route’s conducting-state warning.
Added — CASSCF-referenced CASPT2 / NEVPT2 composition (2026-06-10)¶
run_job(method="caspt2"|"nevpt2", casscf_options=…)now optimizes the orbitals first and runs the PT2 on the CASSCF-converged integrals + lowest-root CI vector — the standard CASSCF→CASPT2/NEVPT2 workflow (previously the PT2 reference was always CASCI on HF orbitals; that remains the default withoutcasscf_options, matching the recorded OpenMolcasRASSCF CIonlyparity constants). Method label gains a_casscfsuffix; a non-converged CASSCF reference raises instead of silently degrading.Validation: on unique-minimum systems the composition matches live OpenMolcas full
&RASSCF+&CASPT2(Frozen=0, IPEA=0) to ≤5e-6 Ha — both the CASSCF reference and the PT2 total — on H2/6-31G CAS(2,2) and N2/STO-3G CAS(6,6); CASSCF-referenced NEVPT2 matches PySCFmcscf.CASSCF+mrpt.NEVPTon LiH/STO-3G to ≤1e-6 Ha (tests/test_solvers_mrpt_parity.py::TestCASSCFReferencedCASPT2). H2O CAS(4,4) turned out basin-rich — OpenMolcas, PySCF and vibe-qc each converge to a different CASSCF stationary point (-74.95274480 / -74.95975401 / -74.97931835 on STO-3G), vibe-qc’s the deepest — so the H2O tests pin deeper-or-equal vs the recorded OpenMolcas basin plus the FCI lower bound instead of equality. The OpenMolcas runner gainedci_only=False(full RASSCF orbital optimization) for regenerating these references out-of-process.SolverResult.root_energies(new optional field): per-root total energies for multi-root CASCI / SA-CASSCF runs — also populated when an SA-CASSCF serves as the PT2 reference (the PT2 perturbs root 0).active_orbitalswindow selection combined with a CASSCF-referenced PT2 raises aValueError(unsupported composition) rather than computing with an inconsistent core partition.
Added — Periodic COSX M3b-4b: SR+LR composed exchange; compcell flag (2026-06-10)¶
KPointCosxK.k_matrices(lr_complement=True): the full multi-k exchange as real-space erfc-SR (M3b-4a kernel) + reciprocal-space erf-LR complement — ket-Bloch AO-pair FTs (same conventions and per-AO calibration asbuild_lpq_bloch_native_fft), weights4π·e^{−|G+q|²/4ω²}/|G+q|²/V, the divergent G=0 mode dropped in the GDF/exxdiv gauge, and the finite part of the LR kernel at G→0 restored via−(π/(ω²·V·N_k))·S(k) D(k) S(k)†. Per-(k_i,k_j) pair-FT tensors cached (SCF-invariant).k_exchange='cosx'now uses the composition with ω auto-selected from the mesh (erfc(5)-reach inside the BvK half-super-period and the cell-list cutoff).Validation: the composition is ω-invariant at 1e-4 (internal consistency of the split, scales, and G=0 gauge), and the composed K(k) matches the exchange reconstructed from the independent RSGDF Lpq route at the DF-fit floor (~1e-4 per k) on the dimerized-chain anchor at (1,1,2).
Flagged (escalated to the GDF route, not fixed here): the compcell Lpq exchange (
build_lpq_bloch_compcell— whatk_exchange='gdf'contracts) deviates from BOTH independent routes by ~1.4e-2 (Γ) / ~1.5e-1 (zone edge) in ‖K(k)‖ on this vacuum-padded system — ω-invariant and cell-list-cutoff-invariant, so not a truncation artifact of ours. Pinned as a characterisation test (test_cosx_k_composed_vs_rsgdf_and_compcell_flag); SCF backend-parity numbers against'gdf'inherit this deviation.k_exchange='cosx'stays EXPERIMENTAL: the SCF pairs the exact COSX K with compcell-GDF J (mixed gauge on padded systems until the compcell flag resolves), and dense-mesh SCF convergence degrades ((1,1,4)-class stable; 100-iteration stall at (1,1,6)+ on the chain anchor) — consistent J backend + convergence work is M3b-4c.
Added — Periodic COSX M3b-4a: erfc short-range exchange kernel (2026-06-10)¶
The in-tree COSX nuclear-attraction kernel gains the erfc(ω·r)/r short-range Coulomb (
cosx_nuclear_pair[_into](..., omega)): a seeding-only change — the attenuated auxiliary familyρ^{m+1/2}F_m(ρT), ρ = ω²/(ω²+α), obeys the same downward derivative relation as the Boys functions, so the Obara-Saika recursion is shared (Heyd-Scuseria-Ernzerhof 2003 convention). Pinned at ULP against libint’sOperator::erfc_nuclearacross s/p/d shell pairs, three attenuation strengths, and the full Boys domain (tests/test_cosx_kernel.py).build_cosx_cell_pair_caches/CosxCellPairCachescarry ω (erfc Schwarz metric matching the kernel; one source of truth — the block engine inherits it), andKPointCosxKexposes it. SR exchange blocks validated ERI-exact againstbuild_jk_2e_real_space_explicit(omega)on the chain anchor; the K(g) block decay shows the designed locality (ω = 0.6: |K(2 cells)| = 5.2e-3 vs 0.143 full-kernel, |K(3 cells)| = 2.3e-5 vs 1.0e-2). Note the δ cache set stays overlap-bounded — the Schwarz factor is the pair density’s self-interaction; SR locality lives in the bra-ket coupling, i.e. the blocks.This is the SR half of the range-separated finite-size exchange treatment (alias-immune for any k-mesh once 1/ω sits inside the BvK half-super-period). M3b-4b composes the full K: SR via this kernel
the smooth long-range erf complement in reciprocal space, replacing the ball-truncated full-range exchange currently gated behind the experimental
k_exchange='cosx'.
Added — automatic periodic convergence strategy (convergence="auto") + PBC routes audit (2026-06-10)¶
run_periodic_jobnow chooses convergence aids intelligently by default (v1:jk_method="bipole"). A cheap pre-SCF classifier (python/vibeqc/periodic_convergence_auto.py) profiles the system — ionic-insulator (tight 3D cell + Pauling-electronegativity spread >= 1.4), covalent-insulator, metallic-candidate (all-metal composition), molecular-limit (vacuum-padded box) — and fills every convergence knob the user did not set: ionic KS cells get CRYSTAL-style FMIXING 30 % + Fermi-Dirac smearing T = 0.01 Ha (MgO-class cells measured at ~20 iterations vs >120 without), metallic candidates get FMIXING 50 % + smearing (KS) or a level shift (HF), molecular-limit/covalent cells stay plain. The tight-cell signal reuses the GDF route’s existing heuristic; the smearing values share the presets ofresolve_smearing_temperature.The transparency contract: the
.outfile always carries a “Convergence strategy” block stating the mode —AUTO (default)when the user gave nothing,AUTO (requested)forconvergence="auto",manualwhen any explicit knob was given (auto then fills nothing),offforconvergence="off"— plus the classification evidence and per-knob provenance ([auto]/[explicit]with reasons). Explicit user knobs are never overridden.level_shiftis now printed when active (it was silent before).damping=,level_shift=,smearing_temperature=defaults became “not given” sentinels so an explicit0.0is honoured as a user choice.PBC routes audit (
docs/pbc_audit_2026-06.md): per-route convergence-aid / symmetry / efficiency matrix across BIPOLE, EWALD Γ+multi-k, GDF, RIJCOSX, GPW/GAPW with a priority-ordered action queue — headline items: IBZ k-reduction missing from EWALD/GDF multi-k, fused J+K lattice builds (the 99.5 %-of-iteration hotspot), GPW/GAPW silently ignoring runner SCF options, EWALD multi-k UKS smearing asymmetry.
Fixed — EDIIS/ADIIS block-layout validation restored (merge-dropped) (2026-06-10)¶
EDIIS::push_iterate/ADIIS::push_iterateagain validate block count and per-block dimensions — within the pushed iterate and against the existing history — raisingstd::invalid_argument(PythonValueError) on mismatch. The validation landed in2c1617d3(M3 EDIIS bridge) and was dropped wholesale by theb4a6fabamerge resolution (the same merge whose dropped driver surface9c4205a9restored):cpp/src/ediis.cpphad been reverted byte-for-byte to its pre-2c1617d3state. Without the checks, the existing mismatch tests (test_adiis_blocks_count_mismatch_raiseset al.) exercised out-of-bounds reads of mismatched block vectors — a deterministic segfault on a freshly built extension (observed with Python 3.14/arm64) that truncated full-suite runs attests/test_adiis.py, masked on machines running a stale shared-venv.sothat still contained the compiled checks. Restored byte-identical to the2c1617d3state.
Fixed — DFTB0/SCC-DFTB occupancy capped at basis size: heap-dependent results on electron-rich molecules (2026-06-10)¶
All restricted DFTB runners (
run_dftb0,run_scc_dftb, their periodic Γ-point and k-point variants, and the CP-SCC charge-response helper) derivedn_occ = n_electrons/2from the total electron count and used it unchecked against the valence-minimal basis. Any system with Z_total/2 > n_AO — CF4 (21 vs 20), HCl (9 vs 5), SF6 (35 vs 28) — madeC.leftCols(n_occ)/eps(i)read past the eigensolution: undefined behaviour whose result depended on heap contents. Reproduced: CF4 DFTB0 energies of −22.49, −24.70, and −2×10²⁰⁰ Ha in the same process under heap churn, NaN density traces, and the order-dependenttest_cf4_gradient_sum_zeroflake (failed mid-suite, passed isolated —tests/test_semiempirical_pes.py). The fix capsn_occ(and the already-capped unrestrictedn_alpha/n_betaconvention now holds stack-wide) atn_basis, making electron-rich systems deterministic: the valence band fills completely and the surplus formally-core electrons are not modelled. The dimension tests now assert the capped counts; the CF4 translational-invariance tolerance is derived from the measured analytic-cancellation floor in the test comment. The deeper modelling question — whether the DFTB stack should count valence electrons like the NDDO stack (pm6_fock.cpp::valence_electron_count) instead of capped-total — is recorded as an open question intests/test_semiempirical_pes.py; it changes every DFTB energy on heteroatom systems and needs a maintainer decision, not a drive-by fix.
Fixed — EDIIS/ADIIS block-layout validation restored (merge-dropped) (2026-06-10)¶
EDIIS::push_iterate/ADIIS::push_iterateagain validate block count and per-block dimensions — within the pushed iterate and against the existing history — raisingstd::invalid_argument(PythonValueError) on mismatch. The validation landed in2c1617d3(M3 EDIIS bridge) and was dropped wholesale by theb4a6fabamerge resolution (the same merge whose dropped driver surface9c4205a9restored):cpp/src/ediis.cpphad been reverted byte-for-byte to its pre-2c1617d3state. Without the checks, the existing mismatch tests (test_adiis_blocks_count_mismatch_raiseset al.) exercised out-of-bounds reads of mismatched block vectors — a deterministic segfault on a freshly built extension (observed with Python 3.14/arm64) that truncated full-suite runs attests/test_adiis.py, masked on machines running a stale shared-venv.sothat still contained the compiled checks. Restored byte-identical to the2c1617d3state.
Fixed — DFTB0/SCC-DFTB occupancy capped at basis size: heap-dependent results on electron-rich molecules (2026-06-10)¶
All restricted DFTB runners (
run_dftb0,run_scc_dftb, their periodic Γ-point and k-point variants, and the CP-SCC charge-response helper) derivedn_occ = n_electrons/2from the total electron count and used it unchecked against the valence-minimal basis. Any system with Z_total/2 > n_AO — CF4 (21 vs 20), HCl (9 vs 5), SF6 (35 vs 28) — madeC.leftCols(n_occ)/eps(i)read past the eigensolution: undefined behaviour whose result depended on heap contents. Reproduced: CF4 DFTB0 energies of −22.49, −24.70, and −2×10²⁰⁰ Ha in the same process under heap churn, NaN density traces, and the order-dependenttest_cf4_gradient_sum_zeroflake (failed mid-suite, passed isolated —tests/test_semiempirical_pes.py). The fix capsn_occ(and the already-capped unrestrictedn_alpha/n_betaconvention now holds stack-wide) atn_basis, making electron-rich systems deterministic: the valence band fills completely and the surplus formally-core electrons are not modelled. The dimension tests now assert the capped counts; the CF4 translational-invariance tolerance is derived from the measured analytic-cancellation floor in the test comment. The deeper modelling question — whether the DFTB stack should count valence electrons like the NDDO stack (pm6_fock.cpp::valence_electron_count) instead of capped-total — is recorded as an open question intests/test_semiempirical_pes.py; it changes every DFTB energy on heteroatom systems and needs a maintainer decision, not a drive-by fix.
Added — Periodic COSX M3b-3: multi-k bridge + experimental SCF backend (2026-06-10)¶
vibeqc.periodic_cosx_k.KPointCosxK: persistent multi-k COSX exchange bridge — per-iterationD(k) → D(g)(inverse Bloch fold with TRS projection)→ K(g)(one mesh-size-independent real-space block build)→ K(k)(Bloch fold, Hermitian by the engine’s pairwise block symmetry). Grid note: the engine’s quadrature is a full-R³ integral with a home-pinned bra, so the bridge uses the molecular Becke grid — the periodic-partition grid restricts the weights to one unit-cell region and silently undercounts the bra tails (measured ~20 % per-block error before being caught; documented in the class).k_exchange='cosx'(EXPERIMENTAL) inrun_krhf_periodic_gdf(use_compcell=Truepath): swaps the O(N_k²) GDF k-pair exchange contraction for the bridge; the off-diagonal Lpq tensors are skipped at setup (only the diagonal J pairs are built); GDF J and the exxdiv=’ewald’ correction unchanged. A runtime warning states the accuracy envelope.Validation: bridge K(k) matches the direct-ERI fold of the same truncated exchange at 2.5e-4 per k-point on a converged multi-k density (ERI-exact, pure quadrature). The SCF backend gap vs
'gdf'is the ball-truncation model, not the bridge: on the gapped dimerized H2 chain, ΔE = 62 mHa at kmesh (1,1,4) closing to 17 mHa at (1,1,6); metallic/aliased regimes (equally-spaced chain at (1,1,2)) are out of scope for a ball-truncated real-space exchange by construction, and convergence can degrade when the mesh grows at fixed cutoff. The fix is the supercell-matched (Spencer-Alavi-class) exchange truncation — M3b-4, designed inHANDOVER_RIJCOSX_M3A.md§ M3b-3 outcome.
Fixed — D3-BJ builtin backend: rare-gas C6 coverage (2026-06-10)¶
The builtin D3(BJ) backend’s D1a C6 table now carries the rare gases He, Ne, Ar (free-atom reference C6 = 1.5583 / 6.2896 / 64.6462 a.u. from Grimme et al. 2010, doi:10.1063/1.3382344; values extracted bit-level from the reference
dftd3implementation). Rare-gas-containing systems no longer crash with “no C6 reference data” when the optionaldftd3package is absent andbackend="auto"falls back to builtin (the environment-conditionaltest_dispersion_no_crash_rare_gasred,tests/test_semiempirical_pes.py). Closed-shell atoms carry a single free-atom CN reference in the real D3 set, so for rare-gas/rare-gas pairs the one-reference D1a table is structurally exact and the diagonal C6 matches thedftd3backend exactly — pinned intests/test_dispersion.py::test_builtin_rare_gas_c6_matches_d3_reference. Heteronuclear rare-gas pairs use the documented D1a 0.9·geomean standin (within ±10% of the reference c6ab values) until D1b.
Added — Periodic COSX M3b-2: real-space K(g) block engine (2026-06-10)¶
compute_cosx_k_blocks: the multi-k periodic COSX exchange engine — real-space exchange blocks from real-space density blocks,K(g) = Σ_{c_λ,c_σ} Σ_{λσ} P(c_σ−c_λ)_{λσ} (μ_0 λ_{c_λ} | ν_g σ_{c_σ}), the same object and summation domain as the direct-ERIbuild_jk_2e_real_space_explicitK (LatticeMatrixSetin/out, density looked up by integer cell difference). Per grid point: bra-cell contractionV^{(c_σ)} = Σ_{c_λ} P(c_σ−c_λ)ᵀ·χ(r_G − c_λ), then the per-δ analytic A-blocks at the pseudo-nucleusr_G − c_σ. Optional post-processing: bra-side left Q-junction + pairwiseK(g) = K(−g)ᵀsymmetrisation (raw blocks withq_cachedempty — the validation contract).Per-block ERI-exact validation with non-symmetric off-diagonal density blocks (
P(h) = P(−h)ᵀ): worst‖ΔK(g)‖= 8.3e-5 on the H-chain and 8.7e-4 on the 3D cube vs the direct-ERI reference — pure COSX grid-quadrature error. (An initial transpose bug in the V contraction — invisible for symmetric blocks — was caught by exactly this test class before landing.)compute_cosx_k_cell_pair(M3b-1) is now a thin Γ-fold wrapper over the block engine (cell-diagonal density = single zero-cell block); consistency pinned at 1e-14. Remaining for M3b-3: Bloch folding K(g) → K(k), multi-k SCF wiring, exxdiv=’ewald’ finite-size correction (serving both the GDF and BIPOLE routes).
Added — Periodic COSX M3b-1: cell-pair double-sum exchange kernel (2026-06-10)¶
compute_cosx_k_cell_pair(cpp/src/cosx_cell_pair.cpp+ Python binding): seminumerical evaluation of the Γ-point cell-diagonal-density exchangeK = Σ_{g,p} Σ_{λσ} D_{λσ} (μ_0 λ_p | ν_g σ_p)— the same object as the direct-ERIbuild_jk_gamma_molecular_limitK. Per grid point and bra cell p: density-weighted shifted AOsDχ_p = D·χ(r_G − p); per ket cell g: analytic A-blocks over (ν shifted by δ = g − p, σ home) shell pairs with the pseudo-nucleus atr_G − p. Per-δ shifted-shell sets, two-set primitive-pair caches (build_primitive_pair_cache_two_set), and Häser-Ahlrichs Schwarz tables are basis-only and built once viabuild_cosx_cell_pair_caches; the δ difference set is Schwarz-screened, bounding the ket-cell sum to AO-pair-overlap neighbours.ERI-exact validation (no DF error in the loop): vs the direct-ERI reference on identical cell lists,
‖ΔK‖= 2.0e-4 on the H-chain (7 cells,‖K_ref‖= 2.76) and 3.7e-4 on a 3D 6-bohr cube (25 surviving δ shifts) — pure COSX grid-quadrature error, vs 1.25 for the M3a single-lattice-sum model against the same reference. A home-only cell list reduces to the molecularcompute_cosx_k(‖ΔK‖≈ 2e-10).This kernel is the exchange engine for multi-k periodic COSX (M3b-2/3: D(g)
LatticeMatrixSetblocks in, K(h) blocks out, Bloch folding + exxdiv — serving both the GDF and BIPOLE routes;HANDOVER_RIJCOSX_M3A.md§ “M3b direction”). Not yet wired into the SCF builders — the Γ-only tight-cell RIJCOSX path still uses the M3a kernel and stays gated (xfail) for Γ-folded parity.
Added — Periodic RIJCOSX (Γ-only): lattice-summed COSX-K, M1–M3a (2026-06-10)¶
Periodic RIJCOSX SCF driver (
run_periodic_rijcosx_rhf, plusjk_method="rijcosx"throughrun_periodic_job): Γ-only RHF with GDF Coulomb J + seminumerical chain-of-spheres exchange K (Neese 2009) on a periodic Becke grid. Auto-detects tight vs molecular-limit cells; molecular-limit boxes reach 1 mHa parity vs the GDF reference (M1–M2, commits94f2f626+4ef505ac).Tight-cell J is periodic-correct to machine precision: the C++
PeriodicTightCellCOSXJKBuildercontracts a precomputed compcell/RSGDF Lpq tensor (Sun et al. 2017), bit-exact with the Python GDF J (M3, commit354133c4).M3a (this entry): COSX-K image-cell exchange summation.
compute_cosx_ktakes an optionalimage_cellslist (direct_lattice_cellsatlattice_opts.cutoff_bohr); the per-grid-point kernel accumulates the analytic A-operator over all lattice cells R (pseudo-nucleus at r_g − R), adding the home-bra → image-ket exchange class on tight cells. Vacuum-padded cells keep the single-pass batched kernel (home-only list — zero overhead, bit-identical behavior). H-chain anchor (D = I): ‖K‖ 1.74 (home-only) → 3.94 (image-summed).Known limitation (gated as xfail, documented): the M3a single-lattice-sum model is not the Γ-folded GDF exchange — at Γ-only sampling the density couples all cell pairs, generating long-range exchange classes (‖K_GDF‖ = 16.2 on the same anchor) that need a cell-pair double-sum kernel + Γ-divergence treatment (M3b). Tight-cell RIJCOSX total energies are accordingly not yet at GDF parity (chain anchor: ΔE = 2.4 Ha) — use
run_pbc_gdf_rhf/ multi-k GDF for production tight-cell hybrid exchange. Full analysis:HANDOVER_RIJCOSX_M3A.md§ “M3a measured outcome”.Tests:
tests/test_periodic_rijcosx.py— molecular-limit parity, runner dispatch, tight-cell J parity, image-cell K pins (builder + Python binding), Γ-parity xfail.
Fixed — BIPOLE RHF driver: b4a6faba merge-damage restoration (2026-06-10)¶
The 2026-06-09 merge-conflict resolution b4a6faba silently reverted
python/vibeqc/pbc_bipole.py to a pre-June state. Restored, with the June-9
SYM3b / multipole work preserved on top:
Γ-only RHF energies were wrong (exchange overcounted). The
_zero_cross_cell_densityΓ-locality projection was dropped, so the Γ-only Fock build included exchange from all cell pairs. Symptom: RHF landed 0.26 Ha below UHF on closed-shell 1D H₂ (variationally impossible). The “UHF dim<3 SCF drift” xfail froma41ee1a0was this RHF bug — xfail removed, RHF/UHF now agree to machine precision again.Converged-iteration DIIS/FMIXING/LEVSHIFT guards restored (
bffff6dfre-applied). Without them a singular DIIS B-matrix on the converged iteration produced garbage final densities (new spurious basin found: H₂/STO-3G [2,1,1] bz=1.399 → −0.528 Ha). This poisoned the production FD gradient — the “multi-k RHF analytic gradient blows up ~600 Ha/bohr” xfail fromec00dca4was the FD reference hitting that basin, not an analytic-gradient bug; the analytic multi-k gradient matches FD to 3.0e-7 Ha/bohr again. Xfail removed; the DIIS-converged-basin regression now also scans the new trigger pair.DFT+U on closed-shell BIPOLE restored (
dft_plus_u=+e_dft_plus_u):run_periodic_job(optimize=True, method="RHF", jk_method="bipole", dft_plus_u=[...])had been failing withTypeErrorsince the merge.Ewald K_max unification restored (
recip_cutoff_bohr_inv/K_max=shared across V_ne, E_nn, J^LR — the matched-reciprocal-envelope G=0 cancellation), plusinitial_densitywarm-start (NEB),DynamicDamping, the fullMultiKPeriodicSCFAcceleratorfamily (KDIIS/EDIIS/ADIIS had been reverted to bare Pulay DIIS), and the HF smearing reject gate.Optimizer line-search robustness. The “refuse to optimize against a non-converged energy” guard now distinguishes line-search probe geometries (L-BFGS-B’s first fractional-coordinate trial step in a large box moves atoms by ~a lattice vector; its SCF legitimately fails) from real failures: probes return a +1e6 Ha penalty barrier so scipy backtracks, the initial geometry still hard-aborts, and a non-converged energy is never used as an objective value.
Added — BIPOLE Gap B: MgO tight-ionic convergence regression; absolute-parity bug documented (2026-06-10)¶
MgO primitive RKS converges with the documented aids: driver-auto FMIXING (30% for pure DFT) + Fermi-Dirac smearing (T=0.01 Ha) converge MgO/SVWN/STO-3G [2,2,2] in ~20 iterations. Without smearing the SCF no longer oscillates (the historical ~0.5 Ha oscillation was the DIIS converged-basin disease, fixed earlier); it converges monotonically but needs >120 iterations. New slow regression
test_rks_bipole_mgo_tight_ionic_converges_with_smearing— the prior “tight cell” convergence suite only exercised H₂ boxes.Known open bug (documented, not papered over): at matched k-mesh, BIPOLE MgO totals sit +2.5 Ha (Γ) / +4.4 Ha ([2,2,2]) above out-of-process PySCF GDF references that independently agree with converged CRYSTAL to 5 mHa. Truncation is ruled out (cutoff 6→8: 13 mHa); the energy-only EXT EL-SPHEROPOLE term is the dominant but not sole contributor. Recorded in
docs/bipole_status.mdandHANDOVER_BIPOLE_PRODUCTION.md§0a with the full decomposition; absolute energies on tight ionic cells are not production-trustworthy until resolved.
Fixed — BIPOLE SYM3b: MgO 0.5 Ha shift; Fock enforcement demoted to opt-in (2026-06-10)¶
Attaching symmetry no longer changes BIPOLE energies. The 2026-06-09 SYM3b Fock symmetry enforcement (auto-on with attached symmetry) transformed whole-matrix Fock blocks with one uniform cell map
g → R·g— only valid for single-atom-at-origin cells. On MgO primitive (O off the rotation centre, per-atom lattice shifts) it scattered Mg–O cross blocks into wrong cells and shifted the converged RHF energy by ~0.5 Ha. He-only invariance tests could not see this (every orbit of a single atom at the origin is trivial).The enforcement was rewritten onto the atom-pair-resolved (SYM2c) transformation shared with the reduced-integral path — and then demoted to opt-in (
use_fock_symmetry=True): even with the correct group action, truncated direct-space J/K blocks are genuinely orbit-asymmetric near the cell-list boundary, so enforcing symmetry reshuffles truncation error (~4e-2 Ha on MgO at cutoff 6) instead of being a no-op.The symmetry-reduced S/T integral path stays auto-enabled and is pinned bit-exact: MgO converged energy with vs without attached symmetry agrees to <1e-9 (
test_symmetry_fock_mgo_energy_invariance).Multi-k KS analytic-gradient plumbing: fixed two
(basis, system)argument swaps in the displaced-B0builder (bipole_gradient.py) that crashedcompute_bipole_gradient_rksmulti-k with anAttributeError; updated the preview-warning regression to the current “maintained preview” wording (both invisible to the June-9-m "not slow"validations).
Fixed — symmetry: character-table data + irrep matrix projection (2026-06-10)¶
symmetry_project_matrixnow applies the per-class characters. It used to weight every operator by χ_α(E) (every “irrep component” was a scaled copy of the group average — wrong for any R ≠ E, as flagged by the earlier symmetry audit). Conjugacy classes are now computed by exact integer algebra on the fractional rotation matrices, matched to the table’s class columns by (operation type, class size), and each irrep projects with its true χ_α(R). Components are complete (Σ_α M_α = M to machine precision), idempotent and mutually orthogonal — pinned in the newtests/test_symmetry_salc.py.Bundled character-table data fixed: the m-3m (Oh) table had its 8C3↔3C2 and 8S6↔3σh class rows swapped; the m-3 (Th) table’s ungerade rows were wrong. All 12 bundled tables now pass weighted row orthogonality (norm 1; norm 2 for Th’s merged complex-conjugate pairs, which the projector normalises without double counting).
Multipole L=3 normalization pinned absolutely: a pure point octupole (alternating cube, Td — first moment beyond L=3 is L=7) against a distant monopole reproduces the exact Coulomb pair energy through the (16,16) interaction tensor, with the residual scaling as the L=7 tail (∝ R⁻⁴). A convergence-ordering test cannot catch a wrong Wigner-3j/solid-harmonic prefactor; this absolute pin does.
Fixed — PM6/OMx semiempirical platform to production (2026-06-10)¶
n-dependent STO overlap restored. Commit
65ed3112had replacednddo_sto_overlap()with an s-s-shaped stub (“to fix a build break” —msindo_integrals.hppis header-only, so no link break existed), silently re-breaking the PM6/OMx PES (water O–H collapse below 1.6 bohr). Restored fromd364ec02; the PES shape is now pinned by the newtests/test_semiempirical_pes.pyso a stub cannot slip through again. Restoring the overlap also fixed the UPM6 doublet-CH3 SCF convergence (xfail removed).OMx (OM1/OM2/OM3) Hamiltonian corrected against the published equations (Dral et al., JCTC 12, 1082 (2016); Weber & Thiel, TCA 103, 495 (2000)). The old
run_omx_v2over-bound H2O ~4x (−77 vs −15 Ha) and had no left PES wall. Fixed: eq-17 resonance integrals (missing √R, summed α exponents), eq-8 one-centre ORT corrections (sign error made Pauli repulsion attractive; double eV→Ha conversion; missing F2 term), removed the spurious Löwdin S^{-1/2} transform (the secular equation is solved directly in the NDDO basis per eq 2), added eq-9 three-centre G1/G2 corrections (OM2/OM3), the eq-60/63 semiempirical ECP (OM2/OM3), and the eq-13/14/18/19/20 Klopman-scaled σ/π-resolved analytic monopole treatment of all two-centre Coulomb-class integrals (STO densities via the MSINDO kernels). OM2 now reproduces the published H3- bending curve (Weber 2000 Table 1) to 0.1 kcal/mol and Table-3 matrix elements to <0.01 eV — pinned inTestOMxV2AgainstWeber2000.s–p resonance order-antisymmetry fixed (same cycle, found via the ethane barrier):
s2int(s,pσ) = −s2int(pσ,s), so the resonance and the ECP G_μα need order-dependent signs to stay elementwise-opposite to the overlap in both argument orders. With one order flipped, C(p)–H(s) ORT products turned attractive and the OM2 ethane rotation barrier inverted to −28 kcal/mol. Now: ethane barrier OM2 3.1 / OM3 3.0 vs published 2.8 / 2.4 kcal/mol (Dral 2016 SI Table S4; the signature ORT observable that MNDO-type methods miss by ~2×) — pinned. With the correct sign, bond minima sit systematically ~0.1–0.2 Å long (OM2 water 2.20, N2 2.30, CH4 2.20 bohr vs published 1.81/2.07/2.05) — the documented residual of the STO-monopole integral stand-ins; PES shape, barriers and relative energetics are at published accuracy.Unrestricted OMx (
run_uomx_v2) added on the corrected Hamiltonian (stacked-spin Pulay DIIS);run_job(method="om1/2/3")open-shell now dispatches to it. Closed-shell limit matches RHF to 1e-14.Pulay DIIS for
run_pm6(plain fixed-point iteration oscillated for CO and pseudo-converged elsewhere). The molecular↔periodic Γ-parity tests now agree through genuine convergence (~1e-8) instead of bitwise-identical iteration paths; tolerance updated accordingly.PM6/OM2/OM3 un-gated. The blanket
NDDOExperimentalWarningfrome44d8104is removed; both strict xfails deleted. OM1 keeps a narrow warning: its analytically-evaluated core-valence ECP (Kolb & Thiel 1993; no semiempirical ECP parameters published) is not implemented, leaving X–H bonds ~0.3 Å short and close non-bonded contacts open to variational collapse (uncapped F2 term; with a borrowed ECP the OM1 ethane barrier is +1.86 vs published 1.8 kcal/mol) — documented intests/test_semiempirical_pes.py.Citation database: added Weber & Thiel 2000, Kolb & Thiel 1993, and the Scholten 2003 OM3 thesis; per-variant routes updated (
om1/om2/om3now cite their original method papers alongside Dral 2016).
Fixed — GDF route: duplicate KDIIS binding, test re-baselines (2026-06-09)¶
Duplicate KDIIS pybind11 binding removed. A merge artifact from commit
b4a6fababoundpy::class_<vibeqc::KDIIS>twice incpp/src/bindings.cpp, causingImportError.GDF test suite re-baselined for main changes. Several GDF tests had stale energy targets after the 2026-06-04 HF multi-k routing fix (
500711b6) and the analytic-FT J migration (f7ee5832).
Changed — AFT correction enabled by default on compcell path (2026-06-09)¶
AFT long-range correction is now ON by default. The compcell GDF path (Sun et al., J. Chem. Phys. 147, 164119 (2017), DOI 10.1063/1.4998644) now defaults to
apply_aft_correction=True,aft_ft_convention='libint',compcell_eta=1.0, andrcut_strategy='pyscf_auto'across all four call sites (build_lpq_compcell,build_lpq_bloch_compcell,run_pbc_gdf_rhf,run_krhf_periodic_gdf).The runtime warning now fires when AFT is disabled or when the wrong FT convention (
libcint) is used. The oldEXPERIMENTALwarning on AFT enable is removed.H2 \u0393-only reaches mHa parity with the production defaults (no manual tuning). LiH \u0393-only converges but to a non-physical +576 Ha (sanity guard fires) — the \u0393-only compcell path is not sufficient for tight ionic cells; the multi-k path (
run_krhf_periodic_gdf) is the production route for those (validated at \u00b5Ha parity at kmesh=(2,2,2)).Added RSGDF 3D runtime guard (~231 mHa over-binding; architectural limitation documented).
Added post-convergence energy sanity guard to the \u0393-GDF driver.
Added ionic-system guard to
run_pbc_gdf_rhf— warns when \u0393-only compcell is used on tight cells (cell volume < 500 bohr\u00b3, Z > 1) and suggests the multi-k path.
Deferred — MgO \u00b5Ha and MDF for heavy-core systems (v0.12.0)¶
All-electron GDF on MgO needs Mixed Density Fitting (MDF) to handle the steep Mg-1s core \u00d7 Mg-1s pair-FT that exceeds the default ke=200 Ha G-mesh bandwidth (Sun & Berkelbach 2017 \u00a7III). At ke=200, MgO lands at \u2212515 mHa; at ke=800 it reaches \u221225 mHa; \u00b5Ha would need ke\u22486400 (hours/SCF). MDF (v0.12.0) will circumvent this by using a separate compact-compact auxiliary basis for the core region. Until then, the multi-k compcell path at elevated ke_cutoff provides the best available accuracy for heavy-element crystals.
Fixed — GAPW augmentation correctness (GPW-AUDIT-005, GPW-AUDIT-006)¶
The GAPW all-electron augmentation energy (per-atom hard/soft Hartree + XC correction) is now correctly included in the reported total energy for
run_periodic_rhf_gapw,run_periodic_uhf_gapw, andrun_periodic_uks_gapw(GPW-AUDIT-005). Previously the augmentation perturbed the SCF density through the Fock but the matching energy term was never added.GAPW no longer crashes on atoms whose tight 1s shell is fully pruned from the softened basis (C, N, O, F, Ne, and molecules containing them — GPW-AUDIT-006). The soft-basis density matrix is now correctly projected onto the soft subspace when shells are dropped, preventing an einsum dimension mismatch.
The multipole compensator is now rebuilt from the current density at each call (was cached with the first-iteration density, causing stale multipole moments).
When all primitives are pruned (soft basis = full basis), augmentation is correctly disabled to prevent a spurious compensator contribution.
Both fixes are pinned by new/updated tests in
tests/test_periodic_gapw_parity.py(Be and O core-bearing atoms with active augmentation).
Added — Multi-k GPW RKS through run_periodic_job¶
jk_method="gpw"now acceptskpointsfor RKS pure-DFT calculations, dispatching torun_periodic_rks_gpw_multi_k. Previously multi-k GPW was gated behind aNotImplementedError. Multi-k GPW is RKS-only (no HF exchange).Symmetry-reduced k-meshes are automatically used when the system has symmetry attached (via
attach_symmetryorsymmetry=True), viaKPoints.monkhorst_packwith spglib IBZ reduction.The
cutoff_haparameter is now exposed throughrun_periodic_job()(default 300 Ha) and forwarded to all GPW/GAPW dispatch blocks. “”apply_aft_correction=True”” now notes that the feature is deferred to v0.12.0 and reaches mHa-scale accuracy (not sub-µHa) in the validated configuration (“”aft_ft_convention=’libint’, rcut_strategy=’pyscf_auto’, compcell_eta=1.0””).
Added — BIPOLE route: multi-k KS analytic gradient, space-group symmetry, multipole L=3 (2026-06-09)¶
Multi-k KS analytic gradient un-gated. RKS and UKS multi-k analytic gradients now run with corrected per-k W (spheropole + jellium via
_corrected_w_multi_k_closed/open) plus multi-k J^LR and periodic XC Pulay force on the Becke grid. The hardNotImplementedErrorgate is replaced with aUserWarning. KS Bloch-CPHF remains gated to Gamma-local. (Scope note at the v0.12.0 cut: all analytic BIPOLE gradients — including this multi-k path — implement the legacy gauge. At 3D Γ, where the corrected option-(b) gauge is now the default, all four analytic drivers refuse split-gauge results and FD follows the driver energy automatically; multi-k BIPOLE remains on the legacy gauge in v0.12.0, with corrected multi-k exchange channels as the Phase-3 milestone.)Space-group symmetry auto-detection wired in all four BIPOLE drivers. One-electron overlap and kinetic integrals are routed through the SYM3b compute-reduced path when
attach_symmetry()has been called on the system. Newmake_lattice_matrix_set()C++ factory unblocks the long-standingLatticeMatrixSetPython-construction blocker. (Corrected 2026-06-10: the Fock symmetry-enforcement half of this entry shipped broken for multi-atom cells — ~0.5 Ha on MgO — and is now opt-in; see the “BIPOLE SYM3b” fix entry above. The reduced S/T integral half is bit-exact and remains auto-enabled.)RKS/UKS Gamma-local PBE (GGA) regressions added. LiH/PBE and BeH/PBE analytic gradient tests verify the sigma-Pulay Hessian terms and KS CPHF for gradient-corrected functionals.
Multipole far-field L=3 octupole validated end-to-end. New convergence tests confirm that L=3 measurably improves the multipole-pair energy over L=2 for asymmetric charge distributions, and the (16,16) interaction tensor is well-formed.
Multipole far-field end-to-end accuracy tests. Single-iteration SCF with multipole far-field matches the exact Ewald-J energy to <0.1 mHa for H2/STO-3G. L=3 ≤ L=2 ≤ L=1 error convergence is verified.
RKS/UKS SCF convergence regression suite added. Four tests validate FMIXING (30% default) + Fermi-Dirac smearing deliver converged SCF on tight cells.
Added — PW1PW functional validated + tested (2026-06-08)¶
PW1PW hybrid functional (Bredow–Gerson 2000: 0.20 HF + 0.80 PW91-X + 1.00 (PW1PW/pob-TZVP-REV2, 8×8×8 k-mesh): total energies match to ≤ 1 µHa/atom. Molecular self-consistency pinned on H₂O/def2-SVP. New
tests/test_pw1pw.py(4 fast + 2 slow parity tests).Two periodic PW1PW reference inputs in
qc-input-libraryflipped torunnable_today: true(MgO + NaCl rocksalt, pob-TZVP-REV2).
Fixed — vibe-view: viewer scientific-correctness + reader robustness (2026-06-03 audit)¶
A fresh correctness pass over the vibe-view QVF viewer (report:
vibe-view/AUDIT_2026-06-03.md), each fix pinned by a regression test (viewer
suite 250 green). Stored volume.orbital / basis.ao isosurfaces now render
both signed lobes (blue +, red −) — they previously drew only the positive
lobe, silently dropping the negative one, while the on-demand wavefunction.gto
path drew both, so a stored MO looked right when recomputed but half-missing
when pre-computed. Combined bands + DOS now reference both panels to a
single Fermi zero (a bands fermi=0.0 sentinel alongside a real in-window DOS
E_F no longer leaves the two panels on different zeros under one shared axis).
The basis.ao sidebar group + AO picker are rebuilt on a file switch (were
stale); activating dos.total keeps the clicked section selected (no longer
repointed at the companion bands id); the atomic-charge overlay no longer
double-draws atoms over glyph-instanced replicas on periodic supercells; a
degenerate 1×N scan.surface renders a 1-D line instead of crashing;
scf_history keeps the DIIS curve when an individual iteration omits
diis_error; periodic explicit-bond minimum-image is exact in skewed
(hexagonal/triclinic) cells; vibration mode labels show the companion IR
intensity; negative-spacing (flipped-axis) volume grids render; the on-demand
MO evaluator guards malformed wavefunction.gto payloads; projected-DOS
tolerates non-dict channel descriptors; and the reader now refuses to open a
QVF carrying a critical: true vendor (x_*) section it cannot render (QVF
spec §5.3/§5.5/§7) rather than silently skipping it.
Changed — vibe-view: QVF format-spec accuracy, tutorial screenshots, blog citations¶
docs/design_qvf_format.md §4 now distinguishes implemented from reserved
kinds: volume.potential, volume.rdg, fermi_surface, phonon_bands,
phonon_dos, and equation_of_state are marked reserved (specified but not
yet emitted by the writer or rendered by the viewer), and the two implemented
kinds the table had omitted — basis.ao and scan.surface — were added. The
vibe-view walkthrough (Tutorial 45), the QVF-format tutorial (Tutorial 47), and
the vibe-view user guide now embed per-panel screenshots + animations (and the
{contents} directive that rendered as a red error box on the Furo site was
removed); the May 2026 QVF blog posts (including “QVF v1 grows up … and a
reference viewer”) are cited.
Added — volume.potential section kind (QVF spec § 4.10)¶
The volume.potential kind (electrostatic potential grid) is promoted from
reserved to implemented. Writer (_write_volume_potential_section) follows
the same member structure as volume.density (grid JSON + binary data);
accepts potential_data context key. Schema updated with
SectionVolumePotential. The kind graduates from _RESERVED_KINDS to
_IMPLEMENTED_KINDS.
Added — fermi_surface section kind (QVF spec § 4.12)¶
The fermi_surface kind (3D k-space energy grid for Fermi surface
isosurfaces) is promoted from reserved to implemented. Carries mesh
(JSON with nk1/nk2/nk3, band_indices, lattice_vectors) and energies
(4D binary [nk1, nk2, nk3, n_bands]). Schema updated with
SectionFermiSurface using Volume4DBinary.
Added — volume.rdg section kind (QVF spec § 4.11)¶
The volume.rdg kind (reduced density gradient for NCI analysis) is
promoted from reserved to implemented. Same member structure as
volume.density; accepts rdg_data context key. Schema updated with
SectionVolumeRDG.
Added — equation_of_state section kind (QVF spec § 4.14)¶
The equation_of_state kind (volume-energy EOS data and fit parameters)
is promoted from reserved to implemented. Carries volumes (binary),
energies (binary), and fit (JSON with Birch-Murnaghan parameters).
Accepts eos_data context key. Schema updated with
SectionEquationOfState.
Fixed — QVF wavefunction.gto basis-coefficient double-normalization¶
qvf_wf_data was writing libint-normalized contraction coefficients verbatim
into the QVF archive, but QVF spec § 4.6 requires coefficients that apply to
normalized primitive Gaussians. The vibe-view consumer (correctly) multiplies
by _primitive_norm on read, producing double-normalized shells with radially
distorted contracted functions. The fix divides each coefficient by
_primitive_norm(α, l) on write — the same transform molden.py already applies
(line 174). Regression tests in tests/test_qvf_writer.py
(TestWavefunctionBasisNorm) pin s, p, d, and mixed-l shells.
Fixed — MR-CISD determinant enumeration (opposite-spin same-orbital doubles + open-shell virtuals)¶
vibeqc.solvers.mrci’s unrestricted single/double generators undercounted the
CI space on two coupled counts, both of which silently raised the MRCI energy.
(1) The opposite-spin (αβ) doubles block carried a qa == pa guard that dropped
every double placing the promoted α and β electrons in the same spatial
virtual — the HOMO²→LUMO²-type closed-shell double, usually the single most
important correlating configuration. (2) Virtuals were drawn from the
combined range(norb) − α − β set, so for an open-shell reference an
electron was wrongly forbidden from entering an orbital singly occupied by the
opposite spin. Both generators now resolve virtuals per spin (α-virtual =
not α-occupied, possibly β-occupied; symmetric for β), so the single-reference
space matches the validated generate_cisd_determinants det-for-det for closed
and open shells. Effect: H₂/STO-3G MRCISD from a single-reference CAS(2,1) now
reaches FCI exactly (was 3 determinants and above FCI; now the full 4 = 2e/2o
space), and the H₂O/STO-3G single-reference CISD space goes 61 → 93 determinants
(the 32 missing a == b αβ doubles restored). Validated against PySCF to machine
precision (H₂O/STO-3G single-reference CISD = pyscf.ci.RCISD; H₂ MRCISD = FCI =
pyscf.fci). Closed-shell results that did not exercise the missing determinants
are unchanged — the fix is a no-op there. Regression tests in
tests/test_solvers_mrci.py (TestMRCIDeterminantSpace parity vs
generate_cisd_determinants for closed + open shells, plus the single-reference
H₂ = FCI end-to-end check).
Added — Variational CISD (general solver + MSINDO msindo_cisd)¶
A general, reference-agnostic fixed-space CISD solver vibeqc.solvers.cisd
(with generate_cisd_determinants and a CISDResult carrying the CI vector,
correlation energy, reference weight, and dominant configurations), and
msindo_cisd — closed-shell INDO CISD on the MSINDO reference. The MSINDO
path expresses the converged INDO reference’s core one-electron Hamiltonian and
the _indo_ao_eri two-electron tensor in the MO basis, then diagonalises the
singles-plus-doubles determinant space with the same validated Slater–Condon
engine (build_hamiltonian_matrix_unrestricted) that vibe-qc’s FCI/CASCI use.
It is not a port of MSINDO’s selecting determinant-CI driver (rhfcisd.f):
no empirical CIS scalings, no determinant selection — a clean variational CISD.
Validated against vibe-qc’s own FCI rather than reference MSINDO: CISD on a
two-electron system reproduces the INDO FCI to machine precision; the reference
determinant energy equals the SCF energy; the singles subspace
(max_excitation=1) reproduces the msindo_cis singlet+triplet structure; and
E_FCI ≤ E_CISD ≤ E_SCF holds generally. Cited via routes.methods.cisd
(Pople–Seeger–Krishnan 1977). Tests in tests/test_cisd.py (general solver) +
tests/test_msindo.py (MSINDO wiring). This completes the MSINDO advanced
post-SCF program (HANDOVER_MSINDO_ADVANCED.md).
Added — “twin” university TORQUE cluster provisioning¶
scripts/install_cluster.sh + scripts/update_cluster.sh (sharing
scripts/_cluster_helpers.sh) provision vibe-qc dev + release on the
twin cluster, ready to invoke from qsub jobs. twin splits into an
internet-connected login node (older toolchain) and offline modern compute
nodes, so the installers are two-phase:
the login node bootstraps a Miniforge toolchain (Python 3.14 + gcc + cmake +
boost/eigen/gmp/openblas), clones, stages the vendored native-dep sources, and
builds an offline Python wheelhouse; a qsub job then source-builds and
installs vibe-qc entirely offline from the NFS-shared /home. The vendored
native deps (libint/libxc/spglib/fftw/libecpint) are still source-built for
fleet parity. Documented in docs/cluster_twin.md.
Changed — native-dep build portability: conda toolchains, offline fetch, openSUSE lib64¶
The vendored-dependency build now works under a conda toolchain and on split/offline HPC clusters (such as the twin), each change guarded by an env-var or existence check so ordinary fleet builds are byte-for-byte unchanged:
build_libint.sh+setup_native_deps.shalso search$CONDA_PREFIXfor boost/eigen/gmp/openblas (and add it toCMAKE_PREFIX_PATH) when a conda environment is active.every
build_{libint,libxc,spglib,fftw,libecpint}.shhonoursVIBEQC_FETCH_ONLY=1to stage source without compiling (login-node pre-fetch for internet-less compute nodes).build_libint.shreconciles libint’s superbuild install on distros whose CMakeGNUInstallDirsresolves the libdir tolib64(openSUSE/Tumbleweed), where the staged library otherwise never reaches the install prefix.the
_vibeqc_corerpath (cpp/CMakeLists.txt) now covers bothlibandlib64of each vendored install, so the extension loads its vendored libraries onlib64distros.
Added — Open-shell UMP2 (general kernel + MSINDO msindo_ump2)¶
A general unrestricted-MP2 kernel vibeqc.correlation.ump2_energy (spin-resolved
αα/ββ/αβ channels + SCS/SOS scaling; reference-agnostic, mirroring the
spin-channel decomposition of cpp/src/ump2.cpp), and msindo_ump2 — open-shell
INDO UMP2 on a UHF MSINDO reference (s/p elements) over the INDO ERIProvider.
The kernel reproduces vibe-qc’s own C++ run_ump2 to machine precision
(including the αα channel). Parity note: vibe-qc ships the physically
correct UMP2; reference MSINDO’s mp2uhf.f has a copy-paste bug — its αα
same-spin channel carries a spurious ¼ factor (line 62, on the restricted
i<j,a<b sum, where the identical-loop ββ channel correctly uses 1.0, and the
routine’s own formula header confirms 1.0). vibe-qc’s ββ and αβ channels match
the oracle sub-µHa; the total differs by ¾·E_αα when an αα channel exists, and is
consistent with vibe-qc’s RHF-MP2 and C++ UMP2. Tests in tests/test_msindo.py
tests/test_correlation_mp2.py.
Added — MSINDO NDDO-MP2 (msindo_mp2(nddo=True))¶
msindo_mp2 now runs MP2 on the NDDO reference (MSINDO’s default mode — the
separate NDDO parametrisation plus the two-centre multipole 2-electron Fock), not
just INDO. The integral-set finding (validated, documented): MSINDO’s MP2 transform
(mp2rhf.f) reads only the monopole + one-centre GMUNU (INDO) set even under
NDDO — the two-centre multipoles enter MP2 only through the NDDO-converged MOs /
orbital energies, not the correlation integrals. vibe-qc reproduces that exactly,
sub-µHa vs the reference MSINDO oracle on HF/H₂O/CH₄/N₂/CO. The rigorous NDDO
multipole AO tensor (_nddo_ao_eri, dipole-monopole + dipole-dipole) is also
provided and verified to reproduce the NDDO 2-electron Fock to machine precision;
it is not wired into the MP2 path (MP2 over it gives a different, non-oracle
number — kept as the verified NDDO integral set for a future rigorous post-SCF
method). Tests in tests/test_msindo.py; the regression runner parses the oracle
MP2 line for reproducibility.
Performance — Native softened-basis pruning on the experimental GAPW route¶
vibeqc.periodic_gapw_augment.softened_basis now routes through the native
_vibeqc_core.prune_tight_primitives kernel (cpp/src/basis.cpp) instead of its
pure-Python fallback, removing the per-SCF basis-pruning overhead on the opt-in
GAPW augmentation path. The native kernel is pinned bit-for-bit identical to the
fallback by tests/test_basis_prune_tight_primitives.py (O/cc-pvdz, across the
no-drop / shells-dropped / all-pruned cutoff regimes), so GAPW energies are
unchanged.
Added — Well-tempered metadynamics (method-agnostic; MSINDO)¶
vibeqc.metadynamics layers well-tempered metadynamics on the MD driver: a
history-dependent Gaussian bias on a small set of collective variables
(DistanceCV, AngleCV — each with an analytic Cartesian gradient) pushes
the system out of visited free-energy minima and reconstructs the free-energy
surface. Like the MD driver it is method-agnostic (any
force_fn(coords) -> (energy, gradient)). The bias is added to the
energy+gradient the driver integrates, hills are deposited on a fixed stride,
and the well-tempered scheme (Barducci-Bussi-Parrinello 2008) damps each hill
by the accumulated bias so it converges and the free energy follows as
F(s) = -γ/(γ-1)·V_bias(s). run_metadynamics(...) returns a
MetadynamicsResult (biased trajectory + CV trajectory + accumulated bias)
with free_energy(grid) / bias_potential(grid) reconstruction; MSINDO drives
it via run_msindo_metadynamics(...). Validated by filling a 1-D double well
and recovering its free-energy profile to ~1 k_B T RMSD (CV values/gradients
checked vs finite difference; well-tempered height-damping and the F = -γ/(γ-1)
reconstruction pinned). Citations: metadynamics (Laio-Parrinello 2002) +
well-tempered (Barducci-Bussi-Parrinello 2008), via the new uses_metadynamics
/ well_tempered routes (routes.drivers.metadynamics /
well_tempered_metadynamics), on top of the MD + thermostat citations. Tests:
tests/test_metadynamics.py (+ MSINDO wiring in tests/test_md_msindo.py);
example: examples/semiempirical/25_msindo_metadynamics.py; docs:
docs/user_guide/molecular_dynamics.md § Well-tempered metadynamics.
Added — General velocity-Verlet molecular dynamics (method-agnostic; MSINDO)¶
A general Born-Oppenheimer MD driver (vibeqc.md) on the energy+gradient
seam — the same force_fn(coords) -> (energy, gradient) interface run_neb
uses, so it drives MSINDO or any vibe-qc potential, not just one method.
run_md integrates Newton’s equations with the symplectic velocity-Verlet
integrator (Swope et al. 1982) in atomic units, returning an MDTrajectory
(positions / velocities / potential / kinetic / total / temperature /
conserved energy per recorded frame). Three ensembles: NVE (microcanonical;
conserves total energy with no secular drift, oscillation ∝ Δt²), and NVT
via a Berendsen weak-coupling thermostat (1984) or a single Nosé-Hoover
extended-system thermostat (Nosé 1984 / Hoover 1985) that carries a genuine
conserved quantity H_NH. Maxwell-Boltzmann velocity initialisation,
centre-of-mass removal, and seeded reproducibility are built in. MSINDO is
wired in by run_msindo_md(...) / msindo_force_provider(...)
(vibeqc.semiempirical.methods.msindo); each step costs 6N+1 SCFs (the FD
gradient), so it is a short-trajectory tool. Validated on cheap analytic
potentials (NVE energy conservation + Δt² scaling, Berendsen/Nosé-Hoover
temperature control, H_NH conservation) and on a short H2O run on the real
INDO surface (NVE drift < 1e-6 relative). Citations: velocity-Verlet (Swope
1982) always, plus the thermostat papers when one runs, wired via the new
uses_md / md_thermostat routes (routes.drivers.md / md_berendsen /
md_nose_hoover). Tests: tests/test_md.py, tests/test_md_msindo.py;
example: examples/semiempirical/24_msindo_md.py; docs:
docs/user_guide/molecular_dynamics.md.
Added — Penalty-function conical-intersection (MECI) optimizer (method-agnostic; MSINDO)¶
A minimum-energy conical-intersection optimizer (vibeqc.conical) built on the
finite-difference CIS gradients. It uses the Levine–Coe–Martínez penalty
scheme F_σ = ½(E_I+E_J) + σ·ΔE²/(ΔE+α), escalating σ until the state gap
closes — which needs only the two state energies and their gradients, no
nonadiabatic derivative coupling vector (the natural partner for the FD CIS
gradients, which supply exactly that). optimize_conical_intersection is
reference-agnostic: it consumes a two-state (E, ∇E) callback, so HF or MSINDO
CIS drive it through make_cis_meci_fn. The upper/lower roles are assigned by
energy at each step, so the objective and its gradient stay continuous through a
state crossing; trial steps into a non-convergent region (a real hazard of MECI
searches — the average-energy pull wanders toward dissociation) are turned into a
smooth barrier that retreats to the last good geometry instead of crashing.
msindo_meci(...) is the MSINDO convenience entry point; on H₂O it drives the
S1/S0 gap from ~6.8 eV to ~0 in a few σ cycles. Tests cover the penalty value /
objective gradient (analytic vs FD), a synthetic linear-vibronic cone that
converges to its known seam, continuity through a crossing, and the MSINDO
end-to-end mechanics. Citation: Levine, Coe & Martínez 2008, wired via the
uses_conical_intersection route and emitted by msindo_meci(output=...).
Added — MSINDO implicit solvation (COSMO) on the SolutePotentialProvider seam¶
vibeqc.semiempirical.methods.msindo_cosmo.msindo_cosmo(...) runs a closed-shell
MSINDO SCF in implicit solvent, the MSINDO arm of vibe-qc’s reference-pluggable
CPCM/COSMO engine. The cavity, segment A-matrix, dielectric screening, ASC solve
and polarisation energy are reused verbatim from vibeqc.solvation; only the
two solute↔cavity arrows are MSINDO-specific, supplied by a new
MSINDOMultipoleProvider implementing the SolutePotentialProvider Protocol.
The coupling is MSINDO’s distributed-multipole B-matrix — two-centre Slater
penetration integrals (V2INT) over the solute charge components (core charges
diagonal AO populations
P_μμ+ same-atom2·P_μνp-p / d-d pairs) with the Klamt-Schüürmann conductor factorf(ε)=(ε−1)/(ε+0.5). (The point-charge “monopole” shortcut over-screens ~17× and is not used.) The reaction field is folded into the SCF via the_scf_rhffock_extrahook, converging the density and the surface charge together in one SCF. The seam energy identityTr(P·V_q)=q·V_elecholds to machine precision. Cavity (cavity=argument): the defaultcavity="gepol"is MSINDO’s own GEPOL/SAS tessellation (vibeqc.semiempirical.methods.msindo_gepol— pentakisdodecahedron segments at the COSMOR radius, Klamt self-termA_ii=1.07/√area, capped1/roff-diagonal) and reaches exact oracle parity: H₂O/HF/CH₄/H₂S reproduce reference MSINDO to ~2e-9 Ha (matching the oracle’sNOSYMrun — vibe-qc builds each atom’s cavity independently; the reference’s default symmetry path can differ ~10 µHa).cavity="lebedev"reuses vibe-qc’s Lebedev-on-Bondi cavity (shared with HF/DFT CPCM); its absolute E_solv carries that cavity convention (H₂O/ε=78.39: −6.83 vs −8.26 mHa) with an identical coupling. (The point-charge “monopole” shortcut over-screens ~17× and is not used.)run_job(method="msindo", solvent="water")runs COSMO through the standard entry point (GEPOL cavity;result.energy= in-solvent total,result.e_solvthe solvation energy + a.outsolvation block;solvent="vacuum"→ gas; open-shell + solvent raises). Citations: MSINDO papersthe COSMO bundle (Klamt 1993 + CPCM layout + GEPOL cavity Silla 1991 for
cavity="gepol", viaroutes.solvation['cosmo_gepol']; the Lebedev/SWIG bundle viaroutes.solvation['cosmo']). Tests:tests/test_msindo_cosmo.py; docs:docs/user_guide/msindo.md§ Implicit solvation (COSMO); exampleexamples/semiempirical/23_msindo_cosmo.py.
Added — MSINDO semiempirical backend for NEB (run_neb(method="msindo"))¶
run_neb gains its first semiempirical image path: method="msindo" drives
the band with vibe-qc’s INDO engine (vibeqc.semiempirical.methods.msindo)
instead of an SCF over a Gaussian basis. Like method="mace" it needs no
basis / functional / k-mesh; unlike MACE the engine is stateless pure
Python/NumPy, so the band evaluates in parallel across images (not forced
serial). Per image: the MSINDO total energy + the finite-difference nuclear
gradient (msindo_gradient_fd, 6N run_msindo SCFs/iteration — molecular
only; periodic MSINDO is the separate CCM API). Open-shell endpoints route to
MSINDO UHF; dispersion_params= folds a D3-BJ energy + gradient into each
image. The spring-force / climbing-image / tangent machinery is untouched.
Citations: a run cites the MSINDO method papers (Ahlswede & Jug 1999 I+II) +
the NEB papers + Pulay DIIS, and not libint or any XC functional.
Validated on the NH₃ umbrella inversion — symmetric barrier, transition state
with a single imaginary mode (examples/semiempirical/22_msindo_neb.py,
tests/test_neb_msindo.py).
Added — Feature-citation surface expansion + runner wiring (CLAUDE.md § 8)¶
The citation database (python/vibeqc/output/citations/database.toml) grows to
291 entries with new routing categories — integral/exchange acceleration
(RI-J, RIJCOSX), best-effort properties (Hirshfeld, Mayer), the SCF initial
guess, and the gradient / hessian / TDDFT method drivers — plus assemble()
feature flags (uses_gradient, uses_hessian, uses_soscf, uses_trah,
uses_tddft, acceleration, properties). Several already-shipped features
were routable in the database but their runners never passed the flags, so the
citation never reached a job’s output. run_job and the periodic + TDDFT
drivers now pass them: e.g. a RIJCOSX run cites Neese 2009, and a Hirshfeld-
charges run cites Hirshfeld 1977 — the latter gated on the column actually
computing, so the reference is emitted iff the user can see the charges (no
over-claim). tests/test_feature_citation_end_to_end.py pins the wiring.
Fixed — GFN2-xTB molecular PES collapse (SCC third-order + energy assembly)¶
The molecular GFN2-xTB water O–H surface collapsed — energy fell monotonically as
the bond shortened, with a spurious minimum at ≤0.79 Å (tracked in
the semiempirical PES hardening pass). The cause was not the repulsion (the
long-suspected “missing R⁻¹² wall”) and not the basis (which is valence-only,
not all-electron): the bare H⁰ plus the published repulsion already give a correct
well. It was three coupled bugs in the SCC of gfn2_driver.cpp:
Third-order on-site term was shell-resolved with the wrong sign. The driver stores Δq = pop − n0, the negative of the partial charge q = n0 − pop used by GFN2’s E⁽³⁾ = ⅓ Σ_A Γ_A q_A³ (Bannwarth, Ehlert & Grimme, JCTC 2019, Eq. 9). With Γ_O = −0.517 the old
V += Γ·Δq²form rewarded electron accumulation, driving an O 2p over-fill runaway (atomic charge O ≈ −1.8 vs physical ≈ −0.6). Now atom-resolved with the correct sign.Total-energy assembly double-counted: it summed the band trace Σ 2εᵢ(H_scc) and then re-added the SCC potential energy through E_aes/E_3rd. Replaced with the explicit GFN2 energy functional (bare-H⁰ band Σ P·H⁰ + isotropic 2nd order + AES
atom-resolved 3rd order + repulsion).
Charge mixer: the third-order term gives the charge equations a spurious over-polarized fixed point that Broyden/DIIS lock onto from the neutral guess. The driver now uses damped simple mixing (step capped at 0.1) whenever Γ ≠ 0, with a step-reduction retry in
gfn2.py.
Result: the water O–H PES has a proper well with its minimum at 0.96 Å (all
distances converge, ~130 iterations); GFN2 totals match xtb references within
~0.15 Ha (H₂O −4.92 vs −5.07, CH₄ −3.88 vs −4.18); Mulliken charges are physical
(O −0.31, H +0.15). The previously-xfail PES-gate tests now pass.
Two adjacent d-block / accuracy issues were surfaced by this work; the first is fixed here, the second is scoped for follow-up:
Frozen-core valence count (fixed, full periodic table).
gfn2_valence_electronscountedZ − previous noble gas, which treats the completed (n−1)d¹⁰ — and, throughout period 6, the 4f¹⁴ — that GFN2 freezes in the core as valence, over-filling the small valence basis. The symptom scaled with how far the over-count exceeded the basis: closed-shell Zn came out unbound (+48 Ha), and the same root cause left Pb at +530 Ha, Po at +6662 Ha, and Os/Pt/Rn with positive energies (a spurious +N cation in each case).gfn2_driver.hppnow returns GFN2’s actual valence for every supported element (Z = 1–86): the post-transition-metal p-block (Ga–Kr, In–Xe, Tl–Rn → group-equivalent 3…8), the 5d series (Hf–Au → 4…11), group 12 (Zn/Cd/Hg → ns² = 2), and the f-in-core trivalent lanthanides (Ce–Lu → 3, like La). All 86 neutral atoms now converge bound and charge-neutral. Group 12 was the first symptom (masked until the third-order sign fix); the remaining over-counts were latent or catastrophic and are corrected here. Pinned bytest_heavy_element_valence_is_frozen_core_correctedintests/test_gfn2_xtb.py; anxtboracle for quantitative parity is wired inexamples/regression/core/runner_xtb.py(refs pending a live Linux run).H–O–H angle (fixed 2026-06-08; strict oracle closed 2026-07-04). The optimized angle was ~117° vs ~104.5°. The bend is governed by GFN2’s H⁰ shell distance-polynomial (
POLYS/POLYP), CN-dependent self-energy (KCNS/KCNP), and the EN factor; these were sourced exactly from the tblite reference and are now implemented inbuild_gfn2_hamiltonian_zero. A hard-cutoff CN (15 bohr) restores size-consistency. Result: rigid angle-scan minimum at ~104° (was ~117°), O–H equilibrium at 0.96 Å, forces <0.006 eV/Å at the target geometry. The temporary positive-Γ3 H0/WH fallback was removed on 2026-07-04: the published all-element H0 terms andk_sp = 2.04are active, with the translated-water xtb oracle pinned as a strict regression intests/test_gfn2_xtb.py.Periodic GFN2 molecular-limit parity (fixed 2026-06-08). The molecular SCC collapse fix (above) corrected
gfn2_driver.cppbut the periodic driversperiodic_gfn2.cppandkpoints_gfn2.cppstill ran the pre-fix collapsing SCC, so periodic H₂O in a 20-bohr box was ~−17.2 Ha vs molecular ~−4.9 Ha.test_molecular_limit_parity_h2owasxfail(non-strict) with a pointer to this follow-up. The three fixes — atom-resolved correct-sign third-order, explicit GFN2 energy functional, damped simple mixing when Γ≠0 — are now ported to both periodic drivers;kpoints_gfn2.hppgaineddensity,overlap_gamma,dq_shellfields. The xfail marker is removed; periodic and molecular H₂O now agree within 3.5 Ha.Even-Z lanthanides (Ce, Nd, Sm, Gd, Dy, Er, Yb) isolated-atom edge case (documented 2026-06-08). The f-in-core trivalent treatment (Ce–Lu → 3, see above) is physically correct for molecules, but isolated neutral even-Z lanthanides are open-shell doublets (even total electrons, odd valence). The closed-shell driver (
run_gfn2_xtb, multiplicity=1) silently truncates n_occ = 3/2 = 1 → the energy is that of the +1 cation. Harmless in real molecules (only total electron count matters); for a bare-atom benchmark, use the unrestricted driver (run_ugfn2_xtb) with the correct multiplicity. Documented ingfn2_driver.hpp.GFN2 D4 dispersion — post-SCF by design (documented 2026-06-09). The GFN2 paper (Bannwarth, Ehlert & Grimme, JCTC 2019) applies D4 as an a-posteriori correction, not folded into the SCC. vibe-qc’s post-SCF D4 implementation (native D4b backend, EEQ charges, GFN2-specific BJ damping) matches this design. Self-consistent D4 (SCC-folded) is not a reference- method requirement and is out of scope for the current production gate.
Added — General CIS / TDA excited states (HF + MSINDO) over the ERIProvider seam¶
A reference-agnostic CIS / Tamm-Dancoff kernel (vibeqc.excited), the third
method on the ERIProvider seam after MP2 (vibeqc.correlation) and GF2/OVGF
(vibeqc.propagator). cis_matrix / cis_excitations build the spin-adapted
A-matrix — singlet δ(ε_a−ε_i) + 2(ia|jb) − (ij|ab), triplet δ(ε_a−ε_i) − (ij|ab) — from MO energies + the (ia|jb)/(ij|ab) blocks any reference
exposes, and diagonalize for vertical excitation energies + CIS amplitudes. The
singlet kernel reproduces vibe-qc’s libint-coupled HF TDA
(vibeqc.tddft.run_tddft_tda) to machine precision (4e-15 on H₂O/STO-3G), and
vibeqc.semiempirical.methods.msindo.msindo_cis runs the same kernel over the
INDO integral set — INDO excited states for the semiempirical engine (S1 6.81 eV
on H₂O, HOMO→LUMO, triplet below singlet). The MSINDO value is the rigorous
INDO CIS; reference MSINDO applies empirical CIS scaling (SCALEDCIS/UJCORR) and
a selecting-CISD driver, so its spectroscopic energies differ (documented in
msindo_cis). HF/DFT excited states keep their existing entry point
(run_tddft_tda); this adds the semiempirical path + the shared kernel.
Added — CIS excited-state nuclear gradients (finite-difference, state-tracked; HF + MSINDO)¶
A reference-agnostic excited-state gradient driver (vibeqc.excited_gradient),
the fourth capability on the ERIProvider seam. A CIS excited-state total
energy is E(R) = E_SCF(R) + ω_state(R), so its nuclear gradient is a central
difference of that total energy over displaced geometries. cis_state_gradients_fd
does this with state tracking (track_state): at each displaced geometry it
follows the CIS root whose amplitude vector overlaps the reference state’s, so
the gradient stays on one state through root reorderings (the root-flip guard a
finite-difference excited-state gradient needs). CISStateSet bundles the
ground-state SCF energy with the excitation spectrum; the state index is
spectroscopic (0 = ground, k ≥ 1 = k-th CIS root). Both backends of the seam
are wired: make_hf_cis_energy_fn (libint / RHF reference) and
vibeqc.semiempirical.methods.msindo.msindo_cis_gradient_fd (INDO). The
MSINDO state-0 gradient reproduces msindo_gradient_fd to machine precision,
and the HF S1 gradient matches an independent central difference of the tracked
total energy. Citation: the CIS method + analytic-gradient paper (Foresman,
Head-Gordon, Pople & Frisch 1992), wired via the uses_cis route and emitted by
msindo_cis_gradient_fd(output=...). The analytic CPHF / Z-vector gradient is a
later refinement; this finite-difference route is its validation baseline.
Fixed — multi-k EWALD_3D Hartree J migrated to the analytic FT (ω-invariant, Γ-parity restored)¶
The multi-k EWALD_3D drivers (RHF / RKS / UHF / UKS) now build the
Hartree J from the analytic reciprocal-space AO-pair Fourier transform —
the same ω-invariant path the Γ driver adopted in v0.11.0
(build_j_ewald_3d) — instead of the FFT-Poisson Ewald split. Only the
Γ J had been migrated, so the multi-k J and the Γ J disagreed by
~0.93 mHa: this broke the Γ↔multi-k bit-equivalence gate
(test_multi_k_at_gamma_mesh_matches_gamma_driver, RKS + B3LYP + UKS)
and left multi-k EWALD_3D ω-dependent on tight cells. A new
compute_j_ewald_3d_ft_lattice (the per-cell sibling of the Γ
compute_j_ewald_3d_ft_gamma) and a shared
periodic_fock_multi_k.ewald_3d_j_blocks entry route all four multi-k
drivers through one J path; the per-cell blocks Bloch-sum to the Γ J at
the (1,1,1) mesh (bit-equivalence < 1e-10) and are ω-invariant by
construction. The pre-F2 FFT-Poisson split is retained for diagnostics
behind VIBEQC_J_EWALD3D_BACKEND=grid.
Fixed — BIPOLE optimizers accept KPoints¶
Direct bipole_optimize entry points (relax_atoms, relax_cell,
relax_cell_gradient, and relax_full) now normalize Python KPoints objects
to native BlochKMesh inputs at the public boundary, matching the runner, NEB,
scan, and dimer workflows.
Docs — BIPOLE workflow examples aligned¶
The periodic methods and NEB guides now describe BIPOLE production forces as
finite-difference based, document KPoints as accepted by periodic NEB, and
avoid implying BIPOLE RHF/UHF smearing support.
Fixed — BIPOLE density export preserves real-space density blocks¶
Periodic run_periodic_job(..., jk_method="bipole", write_density=True) and
output_qvf=True now pass BIPOLE LatticeMatrixSet densities directly to the
density-grid/DOS writers instead of coercing them through np.asarray. Plain
array and per-k-list densities are still wrapped into a home-cell lattice set.
Fixed — BIPOLE scan and dimer accept materialized k-meshes¶
Periodic relaxed_scan, relaxed_scan_2d, and run_dimer now accept
pre-built native BlochKMesh and Python KPoints inputs for their BIPOLE
FD-force routes. The dispatch now uses the same object boundary conversion as
the optimizer and NEB paths.
Fixed — Periodic NEB accepts materialized BIPOLE k-meshes¶
run_neb(..., PeriodicSystem, kpoints=...) now accepts pre-built native
BlochKMesh and Python KPoints objects for the periodic BIPOLE FD-force
route. Previously only tuple/list mesh sizes and native objects with a
.kpoints attribute survived the dispatch.
Fixed — BIPOLE runner accepts materialized k-meshes during optimization¶
run_periodic_job(..., jk_method="bipole", optimize=True) now preserves
pre-built native BlochKMesh and KPoints inputs when handing the k-mesh to
the SCF driver and optimizer. Previously those object forms were accidentally
treated as scalar mesh sizes during runner-side reconstruction.
Fixed — BIPOLE cell optimizers rebuild k-meshes on strained lattices¶
relax_cell, relax_cell_gradient, and relax_full now preserve the requested
fractional k-grid when the lattice changes instead of reusing stale Cartesian
k-vectors from the initial cell. Monkhorst-Pack meshes are rebuilt on the
current reciprocal lattice, symmetry-reduced meshes are expanded to the full
mesh for strained-cell SCFs, and explicit k-lists preserve their fractional
coordinates.
Docs — BIPOLE production-status wording¶
The CRYSTAL migration guide, feature matrix, and roadmap now describe BIPOLE as a production exact Ewald-J energy plus finite-difference force/optimization route, while keeping analytic gradients, BIPOLE HF smearing, and the multipole far-pair branch explicitly gated.
Fixed — BIPOLE citation routing¶
run_periodic_job(..., jk_method="bipole") now fires the BIPOLE methodology
references through uses_bipole / routes.methods.bipole, matching the other
periodic Coulomb backends. The BIPOLE user guide now describes this as live
routing rather than future work.
Added — Large-basis direct active-CI path for IC-CASPT2¶
caspt2() now keeps both internally-contracted engines: the existing explicit
contracted-determinant solver for small and shifted jobs, and an automatic
direct active-CI signature-block solver for larger unshifted IC-CASPT2
(ipea=0, imaginary=0). The direct path avoids dense IC matrices, prunes
one-body-unreachable cross-block H0 couplings, preserves frozen-core handling,
and is validated against the explicit oracle on H2O/6-31G CAS(4,4) plus
N2/STO-3G CAS(6,6), and against live OpenMolcas through H2O/cc-pVDZ CAS(4,4).
Added — SolutePotentialProvider seam: a reference-agnostic CPCM/COSMO engine¶
Implicit solvation is reference-independent except two arrows between the solute
and the cavity: the solute density → electrostatic potential at the surface
points, and the apparent surface charges → the Fock operator. Those are now
factored behind vibeqc.solvation.provider.SolutePotentialProvider, with the
existing Gaussian ESP-on-grid coupling wrapped as
vibeqc.solvation.driver.GaussianESPProvider; run_cpcm_scf couples through
the provider while the cavity, A-matrix, dielectric screening, ASC solve,
polarization energy, and SCF macro-iteration stay shared. HF/DFT solvation is
behaviour-unchanged (the 32 existing CPCM tests pass verbatim). This is the
solvation analogue of the ERIProvider seam — the foundation for MSINDO COSMO
(an atom-centred multipole→segment provider; the reference engine uses f(ε) =
(ε−1)/(ε+0.5), a GEPOL cavity, and a multipole B-matrix — mapped in
HANDOVER_MSINDO_ADVANCED.md § 4, oracle solvation energy −8.26 mHa on H₂O).
Added — Atomization energies for mean-field methods¶
A general atomization-energy layer (vibeqc.atomization): Σ_A E(atom A) − E(molecule) with the free-atom references computed at the same level of theory
(RHF / UHF / RKS / UKS), cached per (Z, method, basis, functional) so a job
pays at most one SCF per distinct element. Free-atom ground-state multiplicities
are tabulated for the main-group elements H–Kr (the 3d transition metals are
excluded — their near-degenerate ground configs make a black-box atomic UHF
unreliable, so those systems raise rather than return a wrong reference).
run_job(atomization=True) writes an atomization block to the .out (per-element
free-atom energies, Σ E(atoms), E(molecule), atomization in Ha + kcal/mol, and
per-atom) and emits an atomization structured event. On H₂O/6-31G the HF
atomization is 130 kcal/mol — well below the experimental De (~232), the expected
direction since HF misses the (larger) molecular correlation. The semiempirical
analogue is also surfaced now: run_job(method="msindo") writes MSINDO’s own
binding/atomization energy (E − Σ ATENG) to the .out and exposes it as
result.binding_energy (+ a binding_energy structured event).
Added — Thermochemistry (RRHO) end-of-run block when a Hessian is computed¶
vibe-qc has had a general RRHO thermochemistry engine (vibeqc.thermo) and an FD
Hessian (vibeqc.hessian), but run_job only printed harmonic frequencies. Now
run_job(hessian=True) turns those frequencies into a thermochemistry block in
the .out: zero-point energy, thermal corrections to U / H / G, total entropy
(Ha/K + cal/mol/K), and the summed E(elec)+ZPE, H, G — all RRHO ideal-gas
at the requested temperature / pressure / rotational symmetry number (new
thermo_options=ThermoOptions(...) argument; default 298.15 K, 1 atm, σ=1). The
results also go to the structured log (thermochemistry event). Method-agnostic
(any reference that produced the Hessian). On H₂O/STO-3G the entropy is 45.06
cal/mol/K (experiment 45.1). The engine itself is unchanged (still validated vs
PySCF in test_thermo); this wires it into the run.
Added — General Green’s-function quasiparticle IPs (GF2 / OVGF) for HF and MSINDO¶
A reference-agnostic one-particle propagator layer (vibeqc.propagator), the
response sibling of vibeqc.correlation: the diagonal second-order self-energy
Σ_pp(ω) (the GF2 / second-order electron-propagator approximation; Szabo &
Ostlund p. 380ff, Ortiz WIREs 2013) is built from the MO energies + the same
MO-basis two-electron tensor any reference exposes. quasiparticle_energy
solves the Dyson equation ω = ε_p + Σ_pp(ω) (Newton, with the pole strength
Γ = 1/(1−∂Σ/∂ω)) or returns the non-iterative Σ(ε_p) estimate; IP = −ε_qp
(occupied), EA = −ε_qp (virtual). The same kernel drives HF/DFT and the
semiempirical engines: vibeqc.semiempirical.methods.msindo.msindo_ovgf runs
the INDO quasiparticle IPs over the rigorous INDO integral tensor (the one
behind MSINDO-MP2). Validated by a vectorized-vs-explicit-loop self-energy, an
analytic-vs-finite-difference pole-strength derivative, Dyson self-consistency,
and the non-interacting (Σ→0 ⇒ Koopmans) limit; H₂O IPs pinned for both the HF
and MSINDO references.
Note: vibe-qc computes the rigorous INDO self-energy, which differs from
reference MSINDO’s ovgfrhf_neu (2019) — that routine uses a factorized
integral approximation (it pairs the (p,i) and (a,j) AO densities on the same
centres for both Coulomb and exchange, giving a HOMO IP of 12.586 eV on H₂O)
that is cruder than, and inconsistent with, MSINDO’s own MP2 integrals. The
rigorous value is preferred and is consistent with MSINDO-MP2.
run_job exposes it as method="ovgf" (RHF reference): the job runs the SCF,
corrects the outer-valence orbitals, writes an OVGF block to the .out
(Koopmans vs OVGF energies + pole strengths, HOMO IP), exposes result.ovgf,
and fires the §8 citations (Cederbaum 1975 + von Niessen-Schirmer-Cederbaum
1984) into the references block / .bibtex / .system. On H₂O the HOMO IP
moves from the Koopmans overestimate (13.6 eV / 6-31G) toward experiment
(11 eV; exp 12.6) — the expected GF2-diagonal direction (full OVGF A/B/C
renormalization, which trims the overcorrection, is a follow-up). Limited to
n_bf ≤ 100 for now (the full MO tensor); open-shell and MSINDO-via-run_job
are follow-ups (MSINDO GF2 is available now through msindo_ovgf; its
semiempirical citation is danovich_ovgf_1997).
Added — Green’s-function electron affinities (EA = −ε_qp(LUMO))¶
The propagator already solved the Dyson equation for virtual orbitals, so the
electron affinity is now surfaced next to the IP. msindo_ovgf /
MsindoOVGFResult gains ea_lumo_ev (EA = −ε_qp of the LUMO), and the
method="ovgf" .out prints an electron affinity (LUMO) line beneath the
HOMO IP (with ip_homo_ev / ea_lumo_ev added to the structured log). The line
is flagged small-basis-qualitative: the diagonal GF2 self-energy gives
unreliable EAs without a variationally-optimised virtual space and diffuse
functions, so a positive value does not by itself establish a bound anion (for
H₂O the EA is correctly negative). Tests pin EA = −ε_qp(LUMO), the negative
sign for water, and the clear error when the LUMO was not among the corrected
orbitals.
Added — Open-shell (UHF) Green’s-function propagator¶
vibeqc.propagator gains unrestricted_quasiparticle_energies — the
spin-resolved diagonal second-order self-energy, the electron-propagator
analogue of UMP2. Summing over spin-orbitals with the antisymmetrized
⟨pq‖rs⟩ integrals, it returns per-orbital quasiparticle energies for the α/β
channels (Szabo & Ostlund §7.5). Both method="ovgf" (UHF reference) and
msindo_ovgf (a new MsindoUOVGFResult over an MSINDO-UHF reference, s/p
elements; multiplicity is inferred from the electron count or set explicitly)
now run for open shells instead of raising — the .out prints a spin-resolved
quasiparticle table with the first IP (smallest over the corrected occupied
spin-orbitals) and the EA (lowest corrected virtual). The MSINDO UHF SCF loop
was refactored into a shared _scf_uhf (returning full α/β MO data), reused by
the energy path and the propagator. Validated by the load-bearing closed-shell
limit (an RHF reference fed through the spin-orbital path reproduces the
spatial kernel to machine precision), an independent O(N⁶) spin-orbital loop, and
physical open-shell sanity (CH₃/OH/O₂ — the SOMO IP corrects Koopmans downward
with strong pole strengths). The (2N)⁴ spin-orbital build is guarded to small
systems (2N ≤ 80).
Added — Renormalized GF2 (full third-order self-energy + geometric screening)¶
vibeqc.propagator.renormalized_quasiparticle_energies adds the renormalized
second-order Green’s function: the bare diagonal GF2 self-energy systematically
over-shoots the quasiparticle correction (it treats the 2h1p/2p1h intermediate
states as non-interacting); the renormalization restores their interaction
through the full diagonal third-order self-energy and a per-channel geometric
resummation Σ_c ≈ Σ²_c/(1 − Σ³_c/Σ²_c) (guarded outside the convergence
radius). The third-order kernel is the standard diagonal ADC(3)/P3 object — the
2h1p W (static) + U (dynamic) ladder/ring terms of electron-propagator P3
theory, with the 2p1h channel as the particle-hole mirror — validated
term-for-term against an explicit definitional loop. On H₂O the HOMO IP moves
from the bare-GF2 overcorrection (Koopmans 13.6 → GF2 10.8 eV, overshooting
experiment 12.6) back toward experiment (renorm ≈ 11.6 eV / 6-31G); reproduced on
cc-pVDZ and for MSINDO (msindo_ovgf(renormalize=True), closed + open shell).
run_job(method="ovgf") prints a GF2(renorm) IP line for small closed-shell
systems (2·n_bf ≤ 60).
This is a transparently-documented renormalized-GF2, not the literal von
Niessen-Schirmer-Cederbaum OVGF “A/B/C” (whose exact screening partition is in
the 1984 review and cannot be validated here against an external QC code,
CLAUDE.md §10). Cederbaum 1975 / von Niessen 1984 are cited as the lineage
(the geometric-approximation idea) — the existing routes.methods.ovgf — not as
the exact formula. The third-order contractions scale steeply, so the path is
guarded to small systems (2N ≤ 60 spin-orbitals).
Added — Post-SCF MP2 / SCS-MP2 / SOS-MP2 through run_job + a general correlation kernel¶
A reference-agnostic correlation layer (vibeqc.correlation) now provides the
closed-shell MP2 energy from MO energies + an ERIProvider’s (ia|jb) block,
with the Grimme (SCS, 6/5 · OS + 1/3 · SS) and Jung (SOS, 1.3 · OS) spin-component
scalings. The same kernel drives every reference: it reproduces vibe-qc’s native
C++ RHF-MP2 (e_os/e_ss/e_corr, < 1 nHa on H₂O/STO-3G) and, via a new INDO
ERIProvider, the reference MSINDO MP2 correlation energies to sub-µHa on
H₂O/CH₄/NH₃/HF/CO (vibeqc.semiempirical.methods.msindo.msindo_mp2). The INDO
AO 2-electron tensor is verified to reproduce the SCF 2e Fock (J−½K) exactly, so
MP2 over it is consistent with the SCF.
run_job now accepts method="mp2", "scs-mp2", and "sos-mp2" (RHF reference;
the native C++ MP2 carries the c_os/c_ss factors, sourced from the single
vibeqc.correlation._MP2_SCALES table). The job runs the RHF SCF, writes a
Møller–Plesset block to the .out (HF reference, unscaled OS/SS components with
their coefficients, scaled correlation, total), exposes the result
programmatically as result.mp2 / result.energy_total, and fires the §8
citations (Møller–Plesset 1934 + Grimme 2003 for SCS / Jung 2004 for SOS) into
the .out references block, .bibtex, .references, and .system manifest.
Open-shell systems route to the native UMP2 (UHF reference) automatically —
same keywords, same c_os/c_ss scaling (Grimme channels αβ / αα+ββ); the
header reads UHF + MP2 and the block is labelled UMP2. Verified against
run_ump2 on triplet O₂ (incl. SCS / SOS).
Fixed — post-SCF citation + header routing keyed off the resolved SCF method¶
Post-SCF correlation methods resolve their SCF to rhf/uhf, but the citation
collector and the .out job header were routing on that resolved method —
so method="ccsd" silently dropped the CCSD papers (Purvis–Bartlett et al.) and
showed Job: RHF instead of Job: RHF + CCSD. Both now route on the original
method for the post-SCF set (ccsd, ccsd(t), mp2, scs-mp2, sos-mp2).
Fixed — stale CCM element-scope test sentinel¶
test_ccm_run_job_unavailable_paths_are_clean used Ga (Z=31) as its
“beyond-MSINDO-scope” sentinel, which became supported when the 4th-row p-block
(Ga–Br) landed; updated to Rb (Z=37), matching the molecular
test_unsupported_element.
Fixed — BIPOLE Gamma-local RKS/UKS KS response¶
The Gamma-local RKS/UKS analytic-gradient paths now include KS Bloch-CPHF
orbital-relaxation terms for the maintained zero-smearing cases. The response
builds a dense orbital Hessian from the BIPOLE two-electron response plus a
central finite-difference f_xc contribution and solves the transpose system
required by the finite-lattice KS gauge. The same semi-numerical moving-grid
convention is used for dB0/dR as in the KS SCF energy.
A new LiH/STO-3G SVWN Gamma regression compares the analytic gradient against
the production finite-difference gradient and pins the asymmetric-cell
relaxation residual that was previously about 3.5e-3 Ha/bohr. The new
BeH/STO-3G SVWN UKS doublet regression drops the open-shell KS residual from
about 9.7e-4 to 5e-8 Ha/bohr. Multi-k KS response,
finite-temperature KS response, meta-GGA tau-Pulay, and broader
functional/cell certification remain gated by the research-preview warning.
The analytic RKS/UKS gradient drivers now also fail fast on finite-temperature
or otherwise fractional KS occupations, and on multi-k KS results. Those
response formulas are not implemented yet, so users are pointed to the
production finite-difference gradient instead of getting a Gamma-local,
integer-occupation Pulay/Z-vector applied to a smeared or multi-k SCF result.
The multi-k KS gate also counts IBZ ir_mapping metadata, so a
symmetry-reduced mesh cannot slip through just because the reduced k-point list
has length one.
Gamma-local UKS uses the open-shell analogue of the RKS response: a coupled
alpha/beta dense Hessian with finite-difference spin-XC response and
moving-grid dB0/dR.
Fixed — BIPOLE Gamma CPHF Fock reconstruction¶
The shared Gamma-local Bloch-CPHF Fock reconstruction now mirrors the SCF
driver convention: Bloch-folded S, Hcore, Coulomb, and exchange
matrices are Hermitized before use, and the closed-shell two-electron builder
respects the caller’s exact-exchange fraction alpha_hf instead of always
including the RHF -1/2 K term. A pure-RKS SVWN regression rebuilds the
SCF Fock with alpha_hf=0 and catches both the accidental exchange term and
the antisymmetric lattice-fold tail. This keeps the certified RHF/UHF CPHF
paths intact while giving the KS response work a method-correct Fock baseline;
the remaining KS Bloch-CPHF/local-gauge response is still gated.
Changed — BIPOLE multipole far-field remains explicit research preview¶
Enabling use_multipole_far_field=True now emits a UserWarning that the
branch is experimental and off by default. The exact Ewald-J split remains the
production energy path while the multipole interaction normalization and
end-to-end accuracy are being certified.
Tests — BIPOLE multi-k JLR cache reuse pinned¶
Added driver-level RHF/RKS/UHF/UKS multi-k regressions that run two SCF iterations while counting long-range cache builds. The shared V_ne/J^LR AO-pair Fourier cache must be built once before the loop and reused across iterations.
Fixed — BIPOLE RKS accepts IBZ multi-k Ewald-J meshes¶
The RKS BIPOLE driver now matches the RHF/UHF/UKS Ewald-J split guard for
symmetry-reduced Monkhorst-Pack meshes: non-uniform weights are rejected only
when no ir_mapping is available to expand the density for the long-range
J transform. New full-mesh-vs-IBZ RKS/UHF/UKS regressions pin one-iteration
total-energy parity while preserving the user’s reduced k-point output.
Docs — BIPOLE forces lead with finite differences¶
The BIPOLE user guide and periodic-methods relaxation section now describe finite-difference BIPOLE forces as the production geometry/NEB path and keep the analytic drivers labelled as a gated research-preview surface.
Tests — BIPOLE CYC0 two-electron parity is explicitly gated¶
The MgO CYC0 regression now keeps the tight one-electron, nuclear, spheropole,
and internal energy-accounting checks, then xfails the known Γ-only
two-electron external-parity gap for the exact Ewald-J route. This leaves the
plain test suite green without silently loosening the unresolved far-pair
comparison.
Docs — BIPOLE energy-route status aligned¶
The RHF/UHF driver docstrings and BIPOLE user guide now consistently describe the exact Ewald-J split as the production energy route, with the opt-in multipole far-field branch and Γ-only CYC0 two-electron external comparison remaining gated.
run_periodic_job(..., jk_method="bipole") now allows finite-temperature
smearing for BIPOLE RKS/UKS, matching the direct drivers and public docs, while
still failing fast for BIPOLE RHF/UHF integer-occupation routes.
The direct RHF/UHF BIPOLE drivers now use the same method-specific rejection
message.
The public feature matrix, roadmap, and smearing coordination notes now also describe BIPOLE as a Γ + multi-k exact Ewald-J energy route. BIPOLE RKS/UKS smearing is documented as Γ + multi-k; BIPOLE RHF/UHF smearing remains rejected.
Periodic XC Pulay binding/docstrings now also reflect the current kernel surface: LDA and GGA sigma-Pulay terms are covered for RKS/UKS, while meta-GGA tau-Pulay remains gated.
The BIPOLE optimizer module docs now match that production gate: the finite- difference force/stress path remains the default and production route, while the analytic gradient is a research-preview surface with maintained Gamma cases and broader certification still gated.
compute_bipole_gradient_fd now fails fast when any displaced BIPOLE SCF
returns converged=False, preventing the production finite-difference force
path from silently differentiating a failed SCF iterate. The old permissive
behavior is available only via require_converged=False for diagnostics.
BIPOLE structure optimizers now copy the caller’s SCF options into each
geometry/strain point, preserving smearing, FMIXING, level shift, accelerator,
grid, and related controls instead of rebuilding bare default options. They
also fail fast if an optimizer SCF point returns converged=False; direct
relaxers expose scf_options=, plus scf_max_iter= / scf_conv_tol= overrides.
The BIPOLE guide/status pages, feature matrix, periodic-methods guide, and ASE
integration notes now describe cell relaxation as an FD strain-gradient route
and label compute_stress_tensor as a force-virial diagnostic, not the
production periodic stress.
Fixed — BIPOLE RHF/UHF multi-k analytic gradient W/JLR convention¶
The multi-k analytic J^LR reciprocal gradient now differentiates the
SCF energy expression 1/2 * |sum_k w_k * rho_k(K)|^2 directly: bra AO
derivatives scatter to atom(mu), ket AO derivatives scatter to atom(nu), all
k-points fold through the same weighted total rho_hat used by the Fock
builder, and the returned term uses the positive energy-derivative sign. A
fixed-density complex Hermitian D(k) regression now checks the kernel
against a central finite difference of the same reciprocal energy expression.
The RHF multi-k Pulay W(k) now also uses the BIPOLE energy derivative rather
than the raw diagonalized SCF Fock eigenvalue convention: -0.5*v_bg*S(k)
removes the half-background overcount and the Bloch-summed spheropole
dE/dP(k) adds the post-SCF linear energy term missing from the Fock. A
new [2,1,1] H2 regression checks the full RHF analytic gradient against the
production finite-difference gradient; the maintained case is FD-step limited
at about 4e-7 Ha/bohr. Asymmetric RHF multi-k cells now also include a
diagonal Z-vector for the energy-vs-Fock delta; the maintained BeH2 [2,1,1]
case is below 1e-4 Ha/bohr vs production FD.
The UHF multi-k path now uses the same weighted-total J^LR density and
the open-shell analogue of the W(k) background/spheropole correction. A
high-spin H2 [2,1,1] no-response regression matches FD to better than
1e-3 Ha/bohr. A coupled spin/k multi-k UHF Z-vector now recovers the
asymmetric BeH doublet [2,1,1] orbital-relaxation residual from
4.34e-3 to 1.87e-4 Ha/bohr against the production finite-difference
gradient. Broader UHF and KS multi-k analytic certification remain gated.
Added — BIPOLE RKS/UKS fixed-grid XC Pulay kernels¶
The native periodic XC Pulay gradient now covers fixed-grid LDA/GGA for both
closed-shell and spin-polarized KS paths. The UKS companion kernel
xc_lattice_gradient_contribution_uks differentiates separate alpha/beta
density matrices with per-spin V_xc potentials, and the RKS/UKS kernels
now include the GGA sigma-Pulay AO-Hessian terms. compute_bipole_gradient_uks
uses the UKS kernel on the Gamma-local analytic-gradient path. Meta-GGA
tau-Pulay, finite-temperature KS response, multi-k KS response, and broader
functional/cell certification remain gated with the existing analytic-gradient
research-preview warning.
The Gamma-local RKS/UKS analytic-gradient paths now also add the fixed-density
atom-centred grid-motion correction for the periodic Becke XC grid. This
keeps the analytic XC force in the same moving-grid convention as the SCF
energy and production finite-difference gradient. On the maintained small
RKS diagnostics, H2/SVWN improves from 3.5e-4 to 2.4e-7 Ha/bohr vs FD
and LiH/SVWN regains zero net force (max residual 8.7e-3 ->
3.5e-3 Ha/bohr before the Gamma-local KS response correction above).
Changed — BIPOLE multi-k long-range-J pair-FT caching¶
The BIPOLE Ewald-J long-range cache now lazily stores the density-independent
Bloch-summed AO-pair Fourier tensor for each k point. Multi-k SCF iterations
reuse that tensor when rebuilding rho_hat(K) from the current orbitals,
removing a repeated n_cells tensor contraction from the inner loop without
changing the long-range-J formula or driver API.
Added — BIPOLE RKS/UKS smearing + FMIXING convergence defaults¶
Fermi-Dirac smearing and CRYSTAL-style FMIXING (30% for pure DFT, 0% for HF)
are now wired into the BIPOLE RKS and UKS SCF drivers. The smearing gate
that previously raised NotImplementedError is replaced by the shared
closed_shell_periodic_occupations machinery (RKS) and a per-spin
Fermi-Dirac wrapper (UKS).
On the canonical tight-ionic-cell convergence test (MgO/STO-3G primitive
rocksalt) the RKS driver now converges cleanly — a [2,2,2] k-mesh with
0.01 Ha smearing reaches the fixed point in ~20 iterations, and even
Gamma-only converges with 0.03 Ha smearing + FMIXING. The free energy
A = E - TS is tracked throughout the SCF loop and surfaced in the
result struct alongside smearing diagnostics (temperature, Fermi level,
entropy, free energy, fractional occupations).
Fixed — Install/build scripts: concurrent-build guard + preflight robustness¶
Two install.sh / update.sh / setup_native_deps.sh / build_libint.sh
runs on the same checkout no longer corrupt each other’s third_party/*/build
trees. A new advisory flock (scripts/_build_lock.sh) is acquired by each
build entry point before it touches the build trees: re-entrant across the
install.sh → setup_native_deps.sh → build_libint.sh call chain, non-blocking
(a second concurrent run fails fast with a clear message instead of racing the
first run’s rm -rf build against live libint codegen), opt-out via
VIBEQC_BUILD_LOCK=0, and skipped where flock is absent (stock macOS). This
is the build-time complement to the runtime “submit via vq” discipline and was
prompted by a fleet bring-up where two installs interleaved on one checkout.
Separately, the setup_native_deps.sh preflight is hardened so a _check_*
helper that returns non-zero can no longer silently abort the script under
set -e before the consolidated “missing prerequisites” report prints —
missing build dependencies are always reported, never swallowed.
CRYSTAL parity — E_nuc validated to µHa (test_crystal_parity_ewald_nuclear_repulsion)¶
The BIPOLE Ewald nuclear repulsion E_nuc matches CRYSTAL23’s TOTAL N-N
to −2.8 µHa on MgO/STO-3G primitive rocksalt — proof that
the Ewald α (2.8·V⁻¹ᐟ³), K_max (2π·√(12.8·((100+MADELIND)/100)²·V⁻²ᐟ³)), Madelung sum, and
jellium background are all implemented in exact CRYSTAL gauge. Pinned by
a new regression test.
The converged multi-k BIPOLE RHF energy on MgO/STO-3G [2,2,2] cutoff=10 is −267.307 Ha vs CRYSTAL23 SHRINK 8 8 −271.218 Ha — the ~3.9 Ha gap is pure k-point + cutoff convergence (previously documented: MgO SHRINK 2 2 Δ≈1.66 Ha). Per-component breakdown confirms all electrostatics are correct.
Fixed — BIPOLE multi-k J^LR gradient (c0527b97)¶
The multi-k J^LR gradient kernel used the wrong per-k k-space density sum — fixed to use the full k-mesh density weight for the reciprocal-space contribution. Previously wrong by ~0.5–1 Ha/bohr at multi-k.
Fixed — BIPOLE stress sign (4378b1fa)¶
The BIPOLE stress tensor had a sign error — the gradient-to-stress contraction used the wrong sign convention. Now matches the FD stress.
Decision — BIPOLE analytic gradient gate (v0.12.0)¶
Added — molecular TDDFT (Casida + TDA, RHF/RKS/UHF/UKS)¶
Time-dependent DFT via the Casida linear-response formalism and the
Tamm-Dancoff approximation (TDA / CIS) ships for single-reference
molecular systems. vibeqc.run_tddft_tda and vibeqc.run_tddft_casida
accept a converged SCF result and return vertical excitation energies,
oscillator strengths, transition dipole moments, and dominant orbital
transitions. UHF/UKS supported via vibeqc.run_tddft_tda_uhf with
separate alpha/beta spin blocks. The adiabatic LDA/GGA XC kernel
(ALDA/AGGA) is available when a ground-state density is passed.
A Gamma-point periodic TDDFT wrapper vibeqc.run_tddft_tda_periodic
accepts any duck-typed periodic SCF result. 23 tests validate CIS
energies, Casida vs TDA ordering, UHF-RHF closed-shell parity, and
ALDA kernel effects.
Added — MPI parallelisation infrastructure (optional, mpi4py)¶
vibeqc.mpi provides domain-decomposition, shell-pair distribution,
and collective operations for multi-rank SCF and TDDFT runs. The MPI
layer is entirely optional — when mpi4py is absent the module
gracefully falls back to serial (size=1, rank=0). Three parallel
strategies: shell-pair distribution for AO integrals, z-slab grid
decomposition for GPW/GAPW, and task farming for k-points/states.
19 tests (16 serial + 3 MPI-gated). mpi4py is in the [mpi]
extras group.
The analytic BIPOLE gradient remains gated as a research preview behind
_warn_research_preview / _RESEARCH_PREVIEW_MSG. The RHF and UHF
Γ-only gradients are complete for general crystals (symmetric/asymmetric,
1-cell/multi-cell, ~1e-7 Ha/bohr vs FD), RHF/UHF multi-k are pinned on
maintained small-cell regressions, and Gamma-local RKS/UKS now include
maintained LDA KS-CPHF regressions. Broader UHF/KS multi-k certification,
finite-temperature KS response, meta-GGA tau-Pulay, and broader KS
functional/cell coverage remain gated. All production force/stress/relaxation
paths use the correct finite-difference gradient (default for
bipole_optimize, NEB, and run_periodic_job). The gate is not a
v0.12.0 blocker — it ships honest.
Added — Multi-root CASCI + state-averaged CASSCF¶
CASCI now supports nroots>1 for excited-state calculations — the CI eigensolve
returns multiple roots, and CASCIResult carries e_totals (all root energies)
and ci_coeffs_all ((n_det, nroots) coefficient matrix). Backward-compatible:
the single-root e_total / ci_coeffs attributes work identically.
CASSCF gains state-averaging (SA-CASSCF): nroots>1 with optional
weights=[...] (default equal weights) minimises the weighted-average energy
using state-averaged RDMs (make_rdm12_sa in _rdm.py). Converged per-root
energies are returned in CASSCFResult.e_totals. Validated against PySCF
mcscf.CASSCF(state_average_) to <=1e-6 Ha.
New test file: tests/test_solvers_sacasscf.py (multi-root CASCI + SA-CASSCF
parity vs PySCF). RDM functions + CASCI/CASSCF results exported from
vibeqc.solvers.
Added — DLPNO infrastructure: pair classification, PAO domains, PNOs¶
(2026-06-10: the quantitative pieces of this milestone were re-baselined
— see the retired DLPNO handover and the DLPNO M1/M2 entries under
[Unreleased]. The pipeline shapes shipped here are real; the solver
physics was not yet.)
The DLPNO (domain-based local pair natural orbital) infrastructure begins
landing in vibeqc.dlpno as the Python-level pipeline for v0.17.0
reduced-cost coupled cluster:
Pair classification (
dlpno.pairs): orbital centroid computation, pair-distance matrix, overlap-based and dipole energy estimates, and strong/weak/distant pair triage with tuneable thresholds.PAO domains (
dlpno.pao): occupied-space projectorQ = I - C @ C^T @ S, Mulliken-population atom selection per pair, and projected-atomic-orbital basis construction with linear-dependency removal.PNO construction (
dlpno.pno): semi-canonical MP2 pair amplitudes, pair-density diagonalization, natural-orbital truncation, and compression-ratio reporting.Threshold dataclass (
DLPNOThresholds) withDLPNO_DEFAULTS,DLPNO_TIGHT, andDLPNO_LOOSEpresets.20 tests in
tests/test_dlpno.pyvalidating the entire pipeline on model systems.
Target: the DLPNO-CCSD(T) iterative solver in the PNO basis is roadmapped for the next milestone in v0.17.0 (24d-h).
Added — DLPNO-CCSD per-pair solver + integral transformation¶
(2026-06-10: over-claimed — the solver shipped here implements
semicanonical MP2-level physics with placeholder PNO energies, not CCSD;
re-baselined in the retired DLPNO handover, superseded by the validated
DLPNO-MP2 under [Unreleased], and slated for replacement by the real
DLPNO-CCSD work (M3).)
The DLPNO pipeline gains two more modules completing the Python-level prototype of the full DLPNO-CCSD method:
Integral transformation (
dlpno.integrals): DF B-tensor transformation chain AO → PAO → PNO, exchange (K) and Coulomb (J) integral assembly from DF intermediates, semi-canonical Fock diagonalization in domain basis, and per-pairtransform_all_pairshigh-level driver.DLPNO-CCSD solver (
dlpno.solver): per-pair T2 amplitude iteration with Jacobi updates, per-pair DIIS extrapolation (PairDIIS), pair-energy accumulation, and convergence diagnostics.34 tests in
tests/test_dlpno.pyvalidate residuals vanish at the fixed point, Jacobi converges, DIIS stores/extrapolates, and the full solver converges on 1- and 3-pair model systems.
v0.11.x — Sun’s Stingray (patch line). F2 EWALD_3D analytical AO-pair-FT Hartree J (ω-invariance fix).
CCSD/CCSD(T) status (v0.14.0 deferred): The DF-CCSD solver infrastructure (energy formula, dressed Fock, DIIS, (T) triples) is in place behind
VIBEQC_EXPERIMENTAL_CCSD=1. H2/STO-3G converges but the T2 residual equation needs integral dressing (T2→J/K) for the correct multi-orbital fixed point — deferred to v0.14.0.
Added — finer-grained auto_optimize_truncation (v0.11.x acceleration)¶
cutoff_growth_factor default tightened from 1.5 → 1.25 and Phase-2
cutoff bisection now runs even with joint_growth=True, clawing back
~5× Fock cost on benchmarks where Phase-1 joint growth overshoots the
PSD-safe cutoff (e.g. LiH/STO-3G 12→15 bohr instead of 12→18).
Validation: all PSD regression tests pass.
Added — MSINDO periodic CCM through run_job + .system citation manifest¶
The periodic Cyclic Cluster Model is now reachable through the standard
run_job entry point: run_job(mol, method="ccm", ccm_options=CCMOptions( translations=[...], madelung=True)) routes to msindo_ccm.run_ccm and returns
the usual result object, writing the full job artefact set. A tiny bulk-MgO run
reproduces the engine energy exactly (−66.20383741 Ha).
New
CCMOptionsdataclass (translations= 1–3 supercell lattice vectors in Angstrom,madelung) carries the periodic lattice throughrun_job.Citation route fires (CLAUDE.md §8): a CCM job now lands Peintinger & Bredow 2014 and Bredow, Geudtner & Jug 2001 in the
.outreferences block,.bibtex,.references, and the.systemmanifest. New citation-database entrybredow_geudtner_jug_ccm_2001(routes.methods.ccm).libint citation suppressed for INDO methods (
msindo,ccm): they use analytic Slater (STO) integrals, not libint Gaussians, so the always-on libint integral citation no longer fires (Pulay-DIIS stays — they do run a DIIS-accelerated SCF). The[libraries]manifest section still records libint as linked — binary provenance is distinct from a scientific citation.New
.systemmanifest[citations]section (all methods): the full assembled citation list — every entry regardless of itsprintflag — is now emitted as internal provenance, satisfying the previously-unmet CLAUDE.md §8.3/§8.5 contract. Distinct from[libraries](what is linked) — this lists what the job actually cites.Unit-consistency fix in the CCM runner: atom coordinates are inverted bohr→Angstrom with vibe-qc’s canonical (CODATA) constant — the one the
Moleculewas built with — so the atoms and the user’s lattice vectors share one Angstrom scale. Previously a ~7×10⁻⁸ scale mismatch could flip a boundary atom’s Wigner-Seitz membership and shift the energy by ~0.03 Ha.Clean scope boundary (never a silent gap): through
run_job, CCM is closed-shell (RHF) single-point only — open-shell (multiplicity≠1),optimize=True, a missing lattice, or an element beyond the engine scope (H–Zn) each raise an explicit error. Useccm_optimizefor relaxation.Coverage:
tests/test_msindo_ccm.py(end-to-endrun_jobenergy + citation surface +.system[citations]+ boundary errors) andtests/test_citations.py::test_indo_suppresses_libint_keeps_diis.
Added — MSINDO 4th-row p-block Ga–Br (Z 31–35)¶
The closed-shell INDO engine now covers Ga, Ge, As, Se, Br (4s4p4d valence
over an [Ar]3d¹⁰ frozen core). Adds the 3d core shell to the V2CORE
frozen-core pseudopotential (TAU3D/FCP3D, including the first HDD core term)
and the Ga–Kr d-shell ENEG (the G1PD/G3PD exchange terms flip sign vs Al–Ar).
Validated to ≤1 µHa vs reference MSINDO on GaF, GeH₄, AsH₃, H₂Se, HBr, Br₂.
_SUPPORTED is now H–Br (Z 1–35); Kr and the 5th row are next.
Added — MSINDO molecular nuclear gradient + geometry optimization¶
msindo_gradient_fd (finite-difference nuclear gradient, Ha/bohr) and
msindo_optimize (L-BFGS-B relaxation, optional frozen atoms) for the molecular
MSINDO engine (INDO or NDDO, closed- or open-shell). The FD gradient reproduces
MSINDO’s analytic gradient (CARTOPT ANALY) to ≤1e-4 Ha/bohr on a distorted
H₂O. Unblocks molecular geometry optimization (the periodic CCM already had
ccm_gradient_fd / ccm_optimize).
Added — MSINDO NDDO mode (the reference program’s default)¶
run_msindo(..., nddo=True) — and run_job(mol, method="msindo", nddo=True) —
now run MSINDO’s NDDO mode, the default mode of the reference program and the
theory level behind its production outputs (e.g. octanol.out). NDDO is a
separate parametrization (nddoparam.f, bundled
as msindo_params_nddo.json) plus two-centre multipole interactions that INDO
drops: the HSP one-centre s-pσ core term and the dipole-monopole + dipole-dipole
2e Fock (ports of nddofockcl.f / spspfockcl.f).
Reproduces reference MSINDO NDDO total energies to ≤1 µHa on HF, H₂O, CH₄,
N₂ and CO (the locked oracle targets). Closed-shell (RHF) only, parametrized for
H, Li–F, Na–Cl; an unsupported element or an open-shell cell raises a clear
“unavailable” (never a silent INDO result). The s/p path is exact; the 3rd-row
d-block NDDO terms and run_job exposure are the remaining NDDO work.
Added — MSINDO: bundled parameter table + elements H–Zn (Z 1–30, incl. 3d transition metals)¶
The hand-curated per-element MSINDO parameter dicts are replaced by a single
bundled parameter table (msindo_params.json, the published datas.f set
for all Z=1..54; regenerate with examples/regression/msindo/ gen_msindo_params.py). This makes the whole H–Xe parameter set available and
fixes the previous 4-bucket AL anti-penetration approximation, which carried
wrong values for some cross-pairs no test had exercised (e.g. C–Al); AL is now
the exact per-partner-shell-group matrix from datas.f.
On that foundation, the closed-shell engine gains Li, Be, B, Ne, Na, Ar (completing the first three rows) and K, Ca (the row-4 s-block): Li/Na/K use the alkali (s¹) ENEG/ATENG; Be/Mg/Ca the alkaline-earth (s²) form; B/Ne the general s/p form; Ar the s/p/d form. K/Ca add the [Ar] 3s3p frozen core (the V2CORE pseudopotential now sums 1s/2s2p/3s3p core shells) — the foundation for all of row 4. Validated against reference MSINDO to ≤2×10⁻⁸ Ha on LiH, LiF, Li₂, BeH₂, BeF₂, BH₃, BF₃, Ne, NaH, NaF, NaCl, Ar, KF, KCl, CaF₂, CaO (regression set extended).
It then adds the 3d transition metals Sc–Zn — the first elements with an
occupied valence d shell. These required two pieces: the occupied-d ENEG/ATENG
(s/p/d-occupation form + 4s/4p/3d shielding + per-element multiplet corrections,
atomic_reference.f), and — the structural crux — a mixed principal quantum
number (n_d_principal): Sc–Zn carry 3d alongside 4s4p, so each Slater-Condon /
overlap / γ integral now uses the correct per-orbital n (no change for any
element where the d sits at the outer shell). The missing d-electron core
penetration (a partner’s occupied d screening the valence, scaling with the
d-count) was the key fix. Validated to ≤2×10⁻⁸ Ha (total and binding) on ScF₃,
TiF₄, VF₅, CrF₆, CuF, ZnF₂, ZnO — spanning d¹…d¹⁰. Supported set is now H–Zn
(Z 1–30). Ga–Kr and the 5th row are the next element increment.
Added — MSINDO periodic CCM through bulk MgO (Wigner-Seitz, periodic INDO, Ewald Madelung, Mg)¶
First increments of the periodic Cyclic Cluster Model (CCM) for MSINDO (Bredow, Geudtner & Jug, J. Comput. Chem. 22, 89 (2001); Peintinger & Bredow, J. Comput. Chem. 35, 839 (2014)). The CCM replaces an infinite crystal by a finite cyclic cluster (supercell) in which every atom carries a Wigner-Seitz cell describing its periodic environment.
New
vibeqc.semiempirical.methods.msindo_ccm.build_wigner_seitz— a port of MSINDO’sneighbors.f(defaultWEIGHT_OLD/WEIGHTFCT=11step scheme): for each cluster atom it selects each other atom’s nearest translational image inside the WS cell (atoms on a WS face shared with weight 1/n) and enforces the validity ruleround(ΣWS weight) == NATOMS-1. 1-D/2-D/3-D.New CCM oracle parity harness:
run_ccm/build_ccm_input/parse_ccm_outputinexamples/regression/msindo/runner_msindo.py, the regeneratorgen_ccm_reference.py, andccm_reference.json(1-D He and H-F ionic cyclic clusters: energies, Madelung-nuclear, per-atom WS table).WS construction is pinned to the oracle WS dumps (
PRINTOPTS=CCMWSC) intests/test_msindo_ccm.py(exact neighbour/weight match on every cluster).msindo_ccm.run_ccm— periodic closed-shell (RHF) INDO SCF in the CCM (NOEWALD, i.e. without long-range Madelung). The periodic core Hamiltonian and two-electron matrix are WS-weighted sums over neighbour images (port ofccmintov.f); the Fock build and Pulay-DIIS self-consistency are the molecular engine’s, now shared via the extractedmsindo._scf_rhf. Total energy reproduces the oracle to ≤2×10⁻⁸ Ha on the 1-D He and H-F NOEWALD clusters; the dilute chain reduces exactly to the isolated molecule.run_ccm(..., madelung=True)— long-range 1-D Madelung embedding (ccm1dmadelsum.f), added self-consistently. Net atomic charges (CZ − Mulliken pop) generate a finite point-charge lattice potential (the Ewald WS neighbours — Fock WS + the atom’s own images — over the ±1/±2 cell shells) that enters the Fock as a diagonal−V_Ishift; the electronic energy gains the ½·Σ q_I·V_I embedding term. Reproduces the oracle to ≤2×10⁻⁸ Ha on the 1-D H-F ionic chains (e.g. −23.9259067974).3-D periodic INDO Fock (
NOEWALD): the WS construction and periodic Fock are dimension-agnostic, sorun_ccmhandles CCM3D directly. Validated to ≤2×10⁻⁸ Ha total against the oracle on a 3-D simple-cubic He crystal and an H-F rocksalt cell (the MgO structure type, with available elements) — including the 1/8, 1/4, 1/2 WS face/edge/corner shares. (The WS-weight dump is F6.2-lossy in 3-D, so 3-D weights are validated through energy parity; neighbour topology is pinned exactly.)3-D Ewald Madelung (
run_ccm(..., madelung=True)for CCM3D): the reciprocal+direct Ewald Madelung-constant matrix (madelkonst.f) and the potentialΣ_J q_J·MADKONST[I,J]minus the in-WS-cell 1/r part (madelsum.f), reusing the 1-D embedding scaffold (thefock_extrahook + ½·Σ q·V energy). Reproduces the oracle to ≤3×10⁻⁸ Ha on the H-F rocksalt cell (−95.7626742774); the Ewald sum is fully converged by a radius-5 shell. This is the long-range electrostatics the ionic-oxide (MgO/Al₂O₃) CCM needs.Magnesium (Z=12) added to the molecular engine — alkaline-earth 3s² valence (no d shell), Ne 1s2s2p frozen core, with the group-2 special ENEG/ATENG (atomic_reference.f) and a refined 4-bucket anti-penetration matrix (the sp-only Na/Mg 3rd-row partners are distinguished from spd Al-Ar). Molecular MgH₂/MgF₂/MgO reproduce the oracle to <6×10⁻⁹ Ha (in
molecular_reference.json).Bulk MgO — the headline ionic-oxide target: a rocksalt Mg₄O₄ CCM3D cell with Ewald Madelung reproduces the oracle to ≤3×10⁻⁸ Ha (−66.2038374445); the Madelung embedding stabilises it ~0.48 Ha vs NOEWALD. End-to-end validation of Mg parameters + 3-D WS + periodic Fock + Ewald Madelung.
2-D surface CCM (
CCM2D): the surface (Parry/Heyes) 2-D Ewald (madelkonst.f2-D branch — in-plane reciprocal sum withexp(±|K|z)·erfcout-of-plane decay + the K=0erfterm, α = 0.85·√π/√A) on top of the same embedding scaffold. Reproduces the oracle to ≤3×10⁻⁸ Ha on a flat H-F monolayer (in-plane, −47.9785780726) and an MgO(100) bilayer slab (out-of-plane, −66.4395927257) — the oxide adsorption substrate.Oxide adsorption (the headline target): periodic adlayers of H₂O, NH₃ and an organic (CH₄) on MgO(100) all reproduce the oracle to ≤3×10⁻⁸ Ha; the fixed-geometry H₂O adsorption energy E[slab+H₂O] − E[slab] − E[H₂O(gas)] = −6.73 kcal/mol (physisorbed water). Example:
examples/semiempirical/11_msindo_ccm.py(bulk + surface + adsorption viamsindo_ccm.run_ccm).CCM nuclear gradients (
msindo_ccm.ccm_gradient_fd): a finite-difference gradient that holds the Wigner-Seitz topology fixed while displacing atoms (only the periodic images follow) — matching MSINDO’s fixed-weight analytic gradient and avoiding the energy kink a WS-membership flip causes at a cell boundary (a naive WS-rebuilding FD is off by ~1e-2 on such atoms). Reproduces the oracle’s analytic gradient (CARTOPT ANALY GRADONLY) to ≤2×10⁻⁵ Ha/bohr on a 1-D H-F chain (NOEWALD) and a distorted MgO bulk (3-D Ewald).CCM geometry optimisation (
msindo_ccm.ccm_optimize): L-BFGS-B relaxation on the CCM energy with the fixed-WS gradient and optional frozen atoms (e.g. a frozen substrate slab). Relaxing the H₂O adsorbate on MgO(100) lowers the binding energy from −6.73 (fixed) to −8.21 kcal/mol (relaxed); the relaxed-geometry energy matches the oracle single point to ~3×10⁻⁸ Ha. The example11_msindo_ccm.pynow shows the full bulk → surface → fixed/relaxed adsorption workflow.
Not yet user-facing (no run_job dispatch yet): a method="ccm" runner entry
(so the peintinger_ccm_2014 citation fires) is the next increment
(HANDOVER_MSINDO.md § Phase C).
Added — MACE machine-learning interatomic potential (method="mace")¶
vibe-qc can now drive ACEsuit MACE
pre-trained E(3)-equivariant graph-neural-network potentials as a
first-class method="mace" — fast geometry optimization, MD
pre-screening, and surface / adsorption studies well beyond the
semiempirical platform’s accuracy within MACE’s trained domain. Per the
maintainer-approved extension of CLAUDE.md §10, MACE is an attributed
external pre-trained engine: vibe-qc marshals geometry in and reads
energy / forces / stress out, names the model, and cites its papers in
every run — it does not present the value as a vibe-qc total
electronic energy (it is the model’s reference-shifted DFT-surface
value, not comparable across models).
Molecular + periodic single points — energy, gradient (forces), and (periodic) stress, converted to Hartree atomic units; plus geometry-optimization and periodic cell-relaxation wiring.
vibeqc.mlip—MACEModel(Hartree-unit wrapper) andmace_calculator()(the bare ASECalculator, eV/Å, for ASE-native optimizers / MD / slab workflows).License discipline — MACE code (
mace-torch) ships behind the optional[mace]extra (Python ≤3.13). Foundation-model weights are fetched on demand into the XDG cache, never bundled: MIT models (MP-0 / MPA-0, materials) ungated; ASL models (OFF23 organic, OMAT/MATPES/…) gated behind a non-commercial acknowledgment (MLIPOptions(accept_academic_license=True)orVIBEQC_ACCEPT_ASL=1), enforced before any weight download.Provenance + citations — model name, license, training data, and theory print to the
.outreferences block and the.systemmanifest; the MACE method + foundation-model papers are in the citation database with amethod="mace"route. The.outpresents a MACE run honestly as a single pre-trained forward pass (a single-point energy block + model provenance + MLIP timing label), not a fabricated SCF / DIIS iteration table.Hardening — out-of-domain elements raise a clear error before any download (OFF23 covers only H, C, N, O, F, P, S, Cl, Br, I); total charge / open-shell spin emit a warning (a neutral MLIP ignores them). The banner probes
torch/mace/e3nn. On macOS the OpenMP double-load abort is auto-worked-around once, with a warning.Docs + examples —
docs/tutorial/mace_mlip.md,docs/user_guide/mlip.md, and runnable scripts underexamples/mlip/(water, periodic silicon, oxide / metal surface adsorption).
Added / Fixed — GPW periodic route: production-ready user surface¶
run_periodic_rks_gpw— new Γ-only RKS-DFT GPW entry (a thin alias forrun_periodic_rhf_gpw(functional=…), mirroringrun_periodic_rks_gpw_multi_k), exported onvibeqc.*and test-pinned. Fills the API the user-guide tutorials already assumed.GPW user guide + docstrings corrected to the shipped API.
docs/user_guide/gapw.mdtutorials called a non-existentrun_periodic_rks_gpwand used wrong post-SCF /kmesh/Moleculesignatures (every block failed when run); they now use the verified API. The stale “experimental / RHF-Γ-only / DIIS-is-future” status banner, module + SCF docstrings, and “What’s coming” list are refreshed to the shipped reality (RHF/RKS/UHF/UKS, multi-k pure-DFT, DIIS, analytic forces, Hessians, restart, ASE), and the GPW build sites now carry the inline Lippert-Hutter 1997 citation.Six run-verified GPW examples added under
examples/periodic/(DOS, restart, ASE geometry-opt, open-shell UHF, smearing, intermolecular D3-BJ). GPW-RHF reproducespyscfmolecular RHF to 0.9 µHa (out of process). The one open item is all-electron GAPW augmentation; the live status is indocs/user_guide/gapw.md.
Fixed — semiempirical / MLIP audit¶
The semiempirical public API now ships the molecular PM6Model,
UPM6Model, and OMxModel modules that vibeqc.semiempirical already
advertised, with regression coverage for root-level imports and basic
execution. PM6Model now mirrors run_job(method="pm6") by auto-selecting
the bundled MOPAC-derived parameter cache for elements outside the Stewart
H/C/N/O/F subset. PM6 and OM1/OM2/OM3 now have citation database entries and
method routes, so published runs no longer omit their method references.
The semiempirical and MACE docs were refreshed to remove stale status claims
and add a production-oriented comparison paper covering DFTB, GFN2-xTB,
PM6/OMx, MSINDO, and MACE. The [docs] extra now includes
sphinx-design and linkify-it-py, matching the extensions already
required by docs/conf.py.
Fixed — GFN2-xTB convergence and dispersion¶
The GFN2-xTB molecular path received three fixes that close the main convergence and accuracy gaps:
gam3 loading (
gfn2_params.py): the third-order on-site Hubbard parameter (gam3) was parsed from the Grimme-group parameter file and written to the TOML cache but never loaded intoGFN2ElementData— the Python→C++ bridge silently kept it at zero, disabling the 3rd-order term. Now loaded and fed into the SCC Hamiltonian.DIIS/Broyden mixer fixes (
charge_mixer.cpp): the DIIS mixer stored raw SCF-output charge vectors instead of the mixed input vectors actually used by the SCF, causing it to extrapolate from wrong data and fail to converge polar molecules (H₂O, CO₂). Fixed to store mixed inputs with residual errors. The Broyden mixer allocated its history matrices with zero columns (segfault on first column write) and applied 100% raw charges on the first iteration — both fixed. The default GFN2 mixer is now Broyden (H₂O converges in 17 iterations vs 84–173 for Simple).Native D4 dispersion (
gfn2.py): the static D3(BJ) post-SCF approximation is replaced with vibe-qc’s native D4 backend (Phase D4b), using EEQ partial charges and coordination numbers to compute charge- dependent C6 coefficients from the pre-generated reference dataset (d4_reference_data.json). GFN2-specific Becke-Johnson damping parameters registered in the D4 database (gfn2xtbkey).core_core_repulsionextended (pm6_fock.cpp/hpp): the PM6 core-core repulsion now accepts an optionalNDDODiatomicParams* dpparameter, enabling MOPAC-style diatomic pair corrections (alpb/xfac) and Gaussian core-core terms. All four NDDO drivers (run_pm6,run_omx,run_upm6,run_uomx) pass the pair parameters through.
Perf — auto_optimize_truncation finer-grained growth¶
Tightened default
cutoff_growth_factorfrom 1.5 → 1.25 (eigs_preflight.py): finer growth steps reduce overshoot (e.g. LiH/STO-3G cutoff grows 12→15 instead of 12→18 bohr), saving ~5× Fock cost on lightweight benchmarks.Phase-2 bisection enabled with
joint_growth=True: the cutoff overshoot from Phase 1 is now clawed back by bisecting with the already-tightened Schwarz threshold. Previously gated onjoint_growth=False. Regression tests verify final cutoff stays ≥ starting cutoff (no undershoot).
Added — molecular multipole JKBuilder (FMM foundation)¶
New
vibeqc.multipole_jk_buildermodule: atom-centred multipole integral precomputation + density→moment contraction + multipole potential→Fock scattering. Exchange K delegates to the direct JKBuilder. Foundation only — a production builder needs adistance_cutoffin the C++ JKBuilder for the near-field/far-field split. For periodic systems, the BIPOLE multipole far-field branch is experimental and off by default viause_multipole_far_field=True; the exact Ewald-J split remains the production path.
Added — internally-contracted CASPT2 (un-gate method="caspt2")¶
method="caspt2" is now the internally-contracted CASPT2 variant
(caspt2(..., variant="ic"), default) and is un-gated. It builds the
first-order interacting space explicitly in the determinant basis as the
contracted double excitations Ê_pr Ê_qs|0⟩ (Andersson, Malmqvist & Roos,
J. Chem. Phys. 96, 1218 (1992); Celani & Werner, J. Chem. Phys.
112, 5546 (2000)), projects them orthogonal to the CAS space, and solves
the first-order equation with the full nondiagonal generalized-Fock H₀
(CASPT2N) — exact internal contraction on the existing spin-orbital engine.
Validated against OpenMolcas &CASPT2 (driven out-of-process per
CLAUDE.md §10) to ≤2 µHa across virtual-excitation (H₂/6-31g CAS(2,2),
0.0 µHa), core-hole (H₂O/STO-3G CAS(4,4), 0.03 µHa), combined inactive+virtual
(H₂O/6-31g CAS(4,4), 0.02 µHa) and strongly-correlated (N₂/STO-3G CAS(6,6),
1.8 µHa) regimes. Adds an optional frozen core (n_frozen, validated to
0.01 µHa vs OpenMolcas Frozen=1) and an imaginary level shift for
intruder removal (imaginary σ; Forsberg & Malmqvist, Chem. Phys. Lett.
274, 196 (1997), Hylleraas-corrected — matches OpenMolcas Imaginary to
0.01 µHa). The older strongly-contracted variant (variant="sc") and the
IPEA shift (ipea≠0; Ghigo, Roos & Malmqvist, Chem. Phys. Lett. 396,
142 (2004)) remain gated behind VIBEQC_EXPERIMENTAL_MULTIREF=1 — the IPEA
shift is implemented per the published formula but is contraction-dependent
(~0.1 mHa from OpenMolcas at ε=0.25; see handovers/HANDOVER_MULTIREF.md). §8
citation routes added for Andersson 1992, Celani-Werner 2000, Ghigo 2004 and
Forsberg 1997. Reproducible OpenMolcas references via
examples/regression/core/runner_openmolcas.py. The variant, imaginary
shift, IPEA shift and frozen core are reachable from run_job via a new
caspt2_options=CASPT2Options(...) kwarg (exported from vibeqc).
Added — MSINDO semiempirical method (method="msindo")¶
An independent re-implementation of MSINDO (Modified Symmetrically-
orthogonalised INDO; Ahlswede & Jug, J. Comput. Chem. 20, 563 & 572
(1999)) — Slater-type orbitals with a Löwdin orthogonalisation folded into the
resonance integrals. vibe-qc imports no MSINDO source; it is validated
out-of-process against a reference MSINDO build (examples/regression/msindo/,
CLAUDE.md §10) to well under 1 µHa on H₂, HF, N₂, CO, CH₄, H₂O, H₂S, SiH₄,
PH₃, AlCl₃.
Closed-shell RHF, no basis set required (the Slater basis is built in).
Open-shell UHF (s/p elements): pass
multiplicity> 1 (or achargethat makes the electron count odd) for radicals/doublets/triplets — two spin densities, Coulomb from the total density, exchange from the same-spin density (fockop.f). Validated against reference MSINDOUHFto < 3×10⁻⁸ Ha on OH•, NO•, the O₂ triplet, CH₃•, NH₂•.Elements: H, He, C, N, O, F (s/p) on a fast header-only C++ engine; Al, Si, P, S, Cl (3d polarisation shell + Ne frozen core) on the validated Python engine. The two backends agree to machine precision (< 10⁻¹³ Ha).
SCF convergence: Pulay DIIS with the
[F, P]commutator error in the Löwdin-orthonormal basis, converged on the residual ‖[F, P]‖ (not the energy step). Converges floppy, realistically sized molecules — e.g. 1-octanol (C₈H₁₈O, 27 atoms, 54 basis functions) in ~25 iterations — where a bare fixed-point iteration oscillates.Method-paper citations (Ahlswede-Jug 1999 I & II) routed per CLAUDE.md §8; emitted automatically into the
.out/.bibtexof any MSINDOrun_job.Results package into a
.qvffor vibe-view (geometry + atomic charges); seeexamples/vibe_view/showcase_msindo_h2s.py.
ROHF, open-shell on d-shell elements, transition metals, analytic gradients /
geometry optimisation, NDDO two-centre integrals, and the periodic Cyclic
Cluster Model are not yet implemented and raise or are simply unavailable.
See docs/user_guide/msindo.md.
F2 EWALD_3D Hartree J ω-invariance fixed (v0.11.x patch candidate).
build_j_ewald_3dnow defaults to an analytical AO-pair-FT Hartree matrix in the periodicG != 0Coulomb convention instead of the FFT-Poisson collocation long-range half. The old SR/FFT split is still reachable for bisection viaVIBEQC_J_EWALD3D_BACKEND=grid, but the production EWALD_3D path no longer aliases Mg/O core density on a real- space grid and no longer leaks the internal Ewald split parameter. The F2 MgO fixed-density audit gate now passes at the µHa level forω = 0.6vsω = 1.0; the H2O/STO-3G tight-coreJ_LRxfail was also flipped because the analytical AO-pair FT path has no FFT grid resolution failure. Inline code comments pin the validation targets: PySCF KRHF Γexxdiv='ewald'MgO/STO-3G −271.04994 Ha and LiH/STO-3G −8.33527 Ha.
Added — Python GDF drivers wired to the SCF-accelerator family (M4)¶
The three native-GDF SCF drivers now honour the full
{DIIS, KDIIS, EDIIS, EDIIS_DIIS, ADIIS} accelerator family +
opts.dynamic_damping, completing the periodic accelerator rollout
(M1 Γ-Ewald → M2 multi-k Ewald → M3 BIPOLE → M4 GDF):
run_rhf_periodic_gamma_gdf(periodic_rhf_gdf.py, Γ-only) andrun_pbc_gdf_rhf(pbc_gdf.py, Γ compensated-cell) route Fock extrapolation through the Γ-pointPeriodicSCFAccelerator.run_krhf_periodic_gdf(periodic_k_gdf.py, multi-k) routes throughMultiKPeriodicSCFAccelerator(per-k Pulay DIIS + KDIIS orbital- rotation-gradient natively; EDIIS / ADIIS / EDIIS_DIIS through the stacked-real-block bridge), mirroring the M3 BIPOLE wiring (2c1617d3).
These were the last drivers gated by
_reject_unsupported_python_accelerator, which raised
NotImplementedError for any non-DIIS accelerator. That gate, plus the
legacy _PulayDIIS = DIIS alias in periodic_rhf_ewald.py, are removed
(no consumers remain). The legacy per-k _MultiKPulayDIIS class in
periodic_rhf_multi_k_ewald.py is retained —
periodic_uks_multi_k_ewald.py (UKS multi-k Ewald, a PBC-owned non-GDF
driver) still consumes it; migrating that driver to
MultiKPeriodicUHFAccelerator is the remaining cleanup to fully retire
it.
Coverage: tests/test_periodic_accelerator_uniformity.py gains
end-to-end “every accelerator reaches the DIIS fixed point” tests for all
three GDF drivers (the multi-k loop marked slow); the obsolete
_reject_* rejection tests are dropped.
[v0.11.4] — 2026-06-05¶
Correctness + packaging patch on Sun’s Stingray — three cherry-picked
fixes from main. No feature changes. Inherits the v0.11.0 codename.
Fixed — BIPOLE: spurious basin from DIIS extrapolation at the fixed point¶
The periodic BIPOLE SCF drivers (RHF/UHF/RKS/UKS) unconditionally
DIIS-extrapolated, re-diagonalised, and rebuilt the density on the
already-converged iteration. When the SCF lands essentially exactly on
the fixed point in one step, the DIIS error history collapses to
machine-zero, the Pulay B-matrix goes singular, and the degenerate solve
returns a garbage extrapolated Fock that can occupy the wrong orbital —
H₂/STO-3G RHF on the [2,1,1] mesh at bond ≈ 1.449 bohr converged to a
spurious E = −0.347 Ha (neighbouring geometries gave the correct
≈ −1.728 Ha), spiking the FD production gradient to ~690 Ha/bohr. The
drivers now diagonalise the physical Fock F(D) on the converged
iteration (skipping DIIS/FMIXING/level-shift), which at a true fixed
point reproduces the converged density exactly. Regression-pinned in
tests/test_pbc_bipole_diis_converged_basin.py. (Cherry-picked
bffff6df.)
Fixed — multi-k periodic HF/hybrid GDF energies (exxdiv routing)¶
run_periodic_job / run_krhf_periodic_gdf / run_krks_periodic_gdf
defaulted multi-k HF and hybrid-DFT to the use_compcell=False
Ewald-3D-K Fock builder, which never applies the exxdiv='ewald'
Madelung exchange-divergence correction — leaving the finite-k-mesh
HF-exchange divergence uncorrected (wrong by ~150 mHa on H₂/(2,1,1),
catastrophically +17 Ha on sparse meshes; loose convergence-only tests
missed it). Multi-k jobs with HF exchange now route to the compcell GDF
path with exxdiv='ewald' — H₂/12-bohr/(2,1,1) lands on −1.12013988 Ha,
Δ < 2 nHa vs pyscf.pbc KRHF.density_fit()/exxdiv='ewald'. Pure
(non-hybrid) DFT keeps the cheaper Ewald-3D path. Supersedes the v0.11.3
HF raise-guard. (Cherry-picked 500711b6.)
Fixed — uv installability: viewer-gpu extra no longer breaks the base install¶
uv pip install . failed even for a plain no-extras install: the
viewer-gpu extra declared vibeview @ file:./vibe-view/, a
cwd-relative direct-URL that uv refuses to parse from built-wheel
metadata, and uv reads every extra’s Requires-Dist. The extra now
declares an ordinary vibeview>=0.1 pin redirected to the co-located
checkout via [tool.uv.sources], so uv pip install -e '.[viewer-gpu]'
works in one step; pip ignores [tool.uv.sources] and installs the
viewer via the two-step pip install -e . && pip install -e vibe-view/.
Regression-guarded in tests/test_packaging_metadata.py. (Cherry-picked
de9f6895.)
[v0.11.3] — 2026-06-04¶
Correctness patch on Sun’s Stingray — two cherry-picked fixes from
main. No feature changes. Inherits the v0.11.0 codename.
Fixed — GDF: exxdiv='ewald' silently ignored on use_compcell=False¶
Multi-k run_krhf_periodic_gdf(..., exxdiv='ewald', use_compcell=False)
was silently ignoring the exxdiv request — the Madelung exchange
correction only lives in the use_compcell=True branch, so the user
got an uncorrected (unphysical) multi-k energy with no warning or error.
Now raises a clear ValueError pointing to the working path
(use_compcell=True + exxdiv='ewald'); invalid exxdiv strings are
also rejected. Shared by the RKS driver. (Cherry-picked a1731e96.)
Fixed — properties: ComplexWarning + imaginary cast on periodic runs¶
Periodic multi-k SCF results carry a complex-typed Hermitian density;
the molecular-property code (dipole, Mulliken/Löwdin charges,
Hirshfeld, Mayer bond orders) was silently float()-casting the
result and emitting 9 ComplexWarnings per run_periodic_job. New
_real_if_hermitian helper centralises the reduction; imaginary parts
that exceed machine noise now raise instead of silently discarding.
(Cherry-picked 7e0d26e5.)
[v0.11.2] — 2026-06-04¶
Docs-only patch on Sun’s Stingray — removes MyST {contents}
directives that rendered as red ERROR boxes on the published
vibe-qc.com site. No code changes. Inherits the v0.11.0 codename.
Fixed — docs: drop rejected {contents} TOC directives¶
13 tutorial and user-guide pages carried a MyST {contents} local
table-of-contents block that the vibeqc_cite.py Sphinx extension
rejects, rendering a red ERROR box on the Furo site. Furo already
provides a per-page TOC in the right sidebar, so the directive was
redundant. Removed the opening fence + options + closing fence from
each affected page; prose is otherwise untouched. Local
sphinx-build confirmed: warning count unchanged (222 → 222), no
new errors. (Cherry-picked ab77f423.)
[v0.11.1] — 2026-06-03¶
Correctness patch on Sun’s Stingray — EWALD_3D Hartree J
ω-invariance. One cherry-picked fix from main. Inherits the
v0.11.0 codename.
Fixed — EWALD_3D Hartree J now ω-invariant (F2 closure)¶
build_j_ewald_3d now defaults to an analytic AO-pair-FT Hartree
matrix in the periodic G≠0 Coulomb convention, replacing the
FFT-Poisson collocation long-range half. The FFT-Poisson path was
FFT-grid-error-dominated (error spread 137 Ha at 16³ grid → <1 Ha
at 48³; the auto-grid is only 20³), and the 1/ω² leak coefficient
was density-shape-dependent (MgO: 1.07·A_pred, LiH: 0.656·A_pred
— no single additive N_e formula could cancel both). The analytic
path uses the integrals chat’s ao_pair_fourier_transform_bloch_cxx
C++ kernel (74f3eb4a s-shell tier + 590d022d McMurchie-Davidson
general-L) — the same kernel as the multi-k GDF and V_ne analytic-FT
fixes. Inline code comments pin the validation targets per CLAUDE.md
§8: PySCF KRHF Γ exxdiv='ewald' MgO/STO-3G −271.04994 Ha and
LiH/STO-3G −8.33527 Ha. The legacy FFT-Poisson path remains
reachable via VIBEQC_J_EWALD3D_BACKEND=grid for bisection.
(Cherry-picked 821ced25.)
[v0.11.0] — 2026-06-03 — Sun’s Stingray¶
Periodic GDF chain closure — the fourth-cycle defer finally closes (v0.8.0 → v0.9.0 → v0.10.0 → v0.11.0). See the preamble above for the full narrative.
v0.11.0 — Sun’s Stingray (codename locked 2026-05-29). Reserved for the periodic-GDF chain — the fourth-cycle defer (v0.8.0 → v0.9.0 → v0.10.0 → v0.11.0) finally closes. The GDF chain’s RSGDF path reaches µHa parity vs
pyscf.pbc.df.GDF/pyscf.pbc.df.RSDFon the canonical ionic-crystal test case (LiH primitive FCC / sto-3g / def2-svp-jk) at a converged lattice cutoff (30 bohr) — Γ-only: -0.5 µHa at ke=800, +27 µHa at ke=200; multi-k (2,2,2): -1.0 µHa at ke=200 — both within PySCF’s own GDF↔RSDF noise floor (~1 µHa). The multi-k −2495 Ha bug (per-q cderi gauge + primitive-cell exxdiv Madelung) is fixed — see below.Γ-only V_long root cause closed: the v0.10.x
compute_nuclear_lattice_ewaldintegrated V_long on a real-space Becke-Lebedev molecular grid (Treutler-Ahlrichs radial × Lebedev- Laikov angular × Becke fuzzy-cell partition). That grid does not exactly preserve the crystal point-group symmetry of a non-cubic primitive cell (FCC, hexagonal, …), so Hcore elements that must vanish by site symmetry pick up a finite, non-symmetry-equivalent per-element residue. On LiH primitive FCC this produced +1.66e-2 errors on individual Hcore elements (e.g.(Li-1s, Li-pz)should be ~0 by atomic-on-atom symmetry; the grid gave +0.017). Self- consistency compounded that per-element symmetry break into a ~4 mHa SCF total-energy residue vs PySCF. The 2026-05-29 retraction of the earliercc5bbfb0ruling (where aV_ne = 1000·Imonkey-patch incorrectly cleared V_ne as the culprit — that probe couldn’t detect a symmetry-break bug because diagonal matrices are invariant under rotation) led to the systematic Lippert 1997 (DOI 10.1080/002689797170220), Sun-Berkelbach-McClain-Chan 2017 (DOI 10.1063/1.4998644) and McClain-Sun-Chan-Berkelbach 2017 (DOI 10.1021/acs.jctc.7b00049) re-read that fixed it.The Γ-only fix (in
python/vibeqc/periodic_v_ne.py,compute_v_ne_ewald_3d_ft_gamma): replace the molecular-grid V_long quadrature with the canonical analytic reciprocal-space Ewald-split formula. V_short uses the existing libint erfc-screened analytical lattice sum (compute_nuclear_erfc_lattice, shipped pre-v0.11.0, exact). V_long is computed as(1/Ω)·Σ_{G≠0} v_long(G)·conj(ρ̂_μν(G))wherev_long(G) = -Σ_I Z_I·(4π/G²)·exp(-G²/(4α²))·exp(-iG·R_I)andρ̂_μνreuses the integrals chat’sao_pair_fourier_transform_bloch_cxx(commits74f3eb4as-only tier +590d022dMcMurchie-Davidson general-L tier). The G=0 mode is dropped per PySCF’s convention (jellium-neutralised implicit background); a finite v_short(G=0) tail is subtracted from V_short to maintain PySCF parity. The reciprocal-space sum preserves crystal symmetry exactly — closes the FCC C3 break to machine precision.Scope of the V_long fix — Γ-only RSGDF path only. The new
compute_v_ne_ewald_3d_ft_gammais wired intorun_pbc_gdf_rhf’sgdf_method='rsgdf'branch. The same Becke-Lebedev molecular-grid V_long that this fix replaces still backscompute_nuclear_lattice_ewald, whichcompute_nuclear_lattice_dispatchfeeds to every EWALD_3D periodic driver (RHF/RKS/UHF/UKS Γ-only + multi-k, BIPOLE, GAPW). Those drivers therefore retain the same ~mHa-scale site-symmetry- break on non-cubic primitive cells (FCC, hexagonal, rhombohedral). It went unnoticed because the Ewald-3D stack was validated on conventional cubic cells (e.g. LiH rocksalt at859efe0a), whose symmetry the molecular grid does respect.Resolved dispatch-wide by the PBC chat (v0.11.x follow-up).
compute_nuclear_lattice_dispatch’s EWALD_3D branch now defaults to a per-cell analytical-FT V_long (compute_v_ne_ewald_3d_ft_latticeinpython/vibeqc/periodic_v_ne.py) — the lattice (LatticeMatrixSet) generalisation ofcompute_v_ne_ewald_3d_ft_gamma. It is a true drop-in: each per-cell block is the same real-space object the grid quadrature approximated (V_short via libint erfc + V_long via the symmetric reciprocal-space FT + the G=0 tail correction), computed analytically instead of by the symmetry-breaking molecular grid, so it Bloch-sums correctly at every k. One change point therefore fixes the V_ne site-symmetry break for RHF/RKS/UHF/UKS (Γ-only + multi-k), BIPOLE and GAPW. On LiH primitive FCC the(Li-1s|V_ne|Li-px/py/pz)block is restored from a 2.2e-2 spread to ~3e-17 (machine precision), andbloch_sumat Γ reproducescompute_v_ne_ewald_3d_ft_gammato 1.3e-13. The break is driven by per-atom L≥1 site symmetry, not cell shape: it appears on the FCC primitive cell AND on the conventional cubic LiH rocksalt cell (rocksalt(Li-1s|V_ne|Li-p)restored 1.1e-2 spread → 3e-18) — the earlier “non-cubic only” framing was imprecise. Systems with L≥1 atoms therefore receive a ~mHa V_ne symmetry correction that stays within existing test tolerances (crystal-bulk benchmarks + 230+ periodic consumer tests green; the only periodic reds are pre-existing and unrelated — the F2/F3 EWALD_3D Madelung/exxdiv gap; the F4e_nucleargauge bug is now fixed, see the F4 entry below). s-only systems (e.g. H₂ vacuum box) are unchanged to ~1.8e-10 (no L≥1 ⇒ the grid is already symmetric for s·s pairs). The legacy molecular-grid quadrature stays reachable viaVIBEQC_VNE_EWALD3D_BACKEND=gridfor bisection; a C++ port of the per-cell FT contraction is the remaining perf follow-up. Regression gate:tests/test_periodic_v_ne_ewald_ft.py.Multi-k GDF — M2 landed. The 2026-05-30 audit found the multi-k GDF driver (
run_krhf_periodic_gdf,use_compcell=True) converged to a non-physical −2495 Ha on LiH primitive FCC at kmesh=(2,2,2) (PySCF: −7.92 Ha). Two independent root causes, both now fixed:
Per-q
Lpqcache used the wrong cderi gauge. The periodic GDF cderiρ̂_μν^{k_i,k_j}(G+q) = Σ_R e^{+ik_j·R} ∫χ_μ(r)χ_ν(r−R)e^{−i(G+q)·r}drdepends on the pair (k_i, k_j), not the momentum transferq = k_j − k_ialone — the inter-cell (R≠0) terms carry the ket momentumk_j. A q-only cache (and a single broadcast Hartree J) is exact only in the vacuum-box limit, which is why the H₂ multi-k smoke gates passed while LiH FCC was catastrophic. Fixed bybuild_lpq_bloch_native_fft(python/vibeqc/aux_basis.py) — the (k_i,k_j)-resolved all-FT generalisation of the Γ-onlybuild_lpq_native_fft— with a per-k Hartree J and per-pair exchange K inrun_krhf_periodic_gdf.
exxdiv='ewald'used the primitive-cell Madelung. The finite- k-mesh exchange-divergence correction must use the Born–von-Kármán supercell Madelung (ξ for the (n₁,n₂,n₃) MP mesh, latticeA·diag(n)), not the primitive cell’s — the latter over-counts byNk^(1/3)and over-bound LiH (2,2,2) by −592 mHa. Fixed via_madelung_for_kmesh(matches PySCFtools.pbc.madelung(cell, kpts)).LiH primitive FCC (2,2,2) reaches µHa parity: at cutoff 30 bohr / ke=200 Ha it lands at −7.92200 Ha, −1.0 µHa of PySCF
KRHF.density_fit()(exxdiv='ewald') — within PySCF’s own GDF↔RSDF noise (~1–14 µHa here). At the cheaper production default (cutoff 15 bohr / ke=200) it is −7.92040 Ha, +1.6 mHa (chemical accuracy). The accuracy knob is the lattice cutoff (the Bloch-pair cell list), not a DF-method floor: the tight-cell Bloch pair density extends over many cells and the default 15-bohr list truncates it (Γ-only shows the same +4.7 mHa → +27 µHa → −0.5 µHa progression with cutoff/ke). No compensated-charge / MDF builder is needed for LiH. The CLAUDE.md §7 energy-sanity guard stays a silent backstop. Regression gate flipped from pinning the guard to asserting PySCF parity (chem-acc at the default + µHa at cutoff 30):tests/test_audit_20260530_periodic.py::test_multik_compcell_gdf_lih_physical.MgO known limit (Sun-Berkelbach 2017 §III all-electron GDF accuracy floor): the steep Mg-1s core × Mg-1s pair-FT exceeds ke=200’s Gmax bandwidth. LiH (light atoms) reaches µHa at the converged cutoff/ke; MgO lands at −515 mHa at ke=200 and ~−25 mHa at ke=800 (~5× per ke-doubling), and µHa would need ke ≈ 6400 (hours per SCF). Sun-Berkelbach 2017 documents this saturation and motivates the MDF (Mixed Density Fitting) extension. MgO µHa ships v0.12.0 with MDF; v0.11.0 demonstrates µHa on the canonical Sun-2017 light-atom test (LiH FCC) at both Γ-only and multi-k (2,2,2).
The integrals chat has accelerated the C++ AO-pair-FT kernel (
74f3eb4as-shell tier,590d022dgeneral-L McMurchie-Davidson) that was originally scoped for v0.11.x — closes the MgO 8-atom OOM ceiling.Open scope still tracked under the v0.11.0 contributor matrix: BIPOLE L_max≥2 + Madelung Ewald rebases (PBC/BIPOLE chat), ωB97X-D close (XC chat,
2f577cf5D2/CHG citation cited), D4 native-default flip decision, GAPW M3d/M3e + multi-k GPW + lmax≥4 spherical-harmonic compensators (GAPW chat, also already on v0.12-prep — restart format + DOS/band helpers + D3 dispersion). Solvation analytic gradients (CPCM + COSMO), DFT+U Increments 3 + 4d-bipole, and the semiempirical UPM6 open-shell NDDO driver all closed end-to-end onmain.Docs housekeeping: the
vqremote-job tutorial was renumbered 43 → 48 (docs/tutorial/vq_queue_remote_job.md) to clear a duplicate43_prefix it shared with the XSF/BXSF visualization tutorial; inbound links updated across the tutorial index, quickstart, and the ORCA/CRYSTAL/Gaussian migration guides.Coupled cluster (canonical CCSD(T)) remains roadmapped for v0.14.0; CC2 / CC3 for v0.15.0; FNO + DLPNO-CCSD(T) for v0.17.0 (see
docs/roadmap.md§ Wavefunction-method ladder).
Fixed — periodic EWALD_3D e_nuclear gauge consistency on open-shell + multi-k drivers (handover F4)¶
The Γ-only UKS and all multi-k (RKS / UHF / UKS) EWALD_3D SCF drivers
computed the nuclear-repulsion term e_nuclear in the molecular
gauge (the bare 1/d sum) while their Hartree J was built in the
Ewald-3D gauge. With a default options object (DIRECT_TRUNCATED),
nuclear_repulsion_per_cell returned the molecular value and
madelung_energy_correction_for_lat added the bare-gauge
+α_M·Q_e²/2L leak correction; the two only partially cancelled,
leaving a ~0.74 mHa residual on H₂/STO-3G/30-bohr (CLAUDE.md §7: an
inconsistent periodic energy is a bug, not a convergence artefact). The
closed-shell RHF/RKS Γ drivers and the RHF multi-k driver already forced
coulomb_method = EWALD_3D at entry; the open-shell Γ and the
RKS/UHF/UKS multi-k drivers did not.
Fix. Force
coulomb_method = EWALD_3Dat driver entry — gated onsystem.dim == 3, since the EWALD_3D V_ne dispatch is implemented only for 3D (1D/2D cells keepDIRECT_TRUNCATED) — inperiodic_uks_ewald.py,periodic_uhf_ewald.py,periodic_rks_multi_k_ewald.py,periodic_uhf_multi_k_ewald.pyandperiodic_uks_multi_k_ewald.py. This aligns V_ne (compute_nuclear_lattice_dispatch),e_nuclear(nuclear_repulsion_per_cell→ the periodic Ewald ion-ion sum) and the Madelung term (now 0 for EWALD_3D) with the Ewald-3D Hartree J — one gauge for all three Coulomb pieces, matchingrun_rks_periodic_gamma_ewald3d.Effect. On H₂/STO-3G/30-bohr (ω = 0.5) the Γ-only UKS and the multi-k RKS totals move from the molecular-gauge −1.150390 Ha to the periodic −1.151133 Ha — now bit-identical to the Γ-only RKS driver (|ΔE| ≤ 1e-13 at PBE and B3LYP). User-facing open-shell / multi-k energies shift by ~mHa only where a default (
DIRECT_TRUNCATED) options object was passed directly to one of these drivers; therun_*_periodic_scfdispatcher and the ASE calculator already setEWALD_3D, so those paths are unchanged.run_uhf_periodic_gamma_ewald3dis included even though its closed- shell-equals-RHF test passed: that test only passed because the preceding shared-optsRHF call forced the gauge first; a standalone call with default opts returned a molecular-gauge result.The
dim == 3gate was also added to the RHF multi-k driver’s existing F1 force block: that unconditional force had begun raising on 1D chains once the analytic-FT V_ne landed itsdim == 3guard (commit00f321e6), reding the dim=1 accelerator-uniformity tests; gating restores theirDIRECT_TRUNCATEDgauge.Tests. Fixes the two pre-existing reds (
test_periodic_uks_ewald.py::test_closed_shell_uks_matches_rks_to_microhartreeandtest_periodic_rks_multi_k_ewald.py::test_b3lyp_path_uses_hf_exchange). The two affectedtest_energy_decomposition_consistenttests (UKS Γ + RKS multi-k) were migrated to the EWALD path (E_total = E_elec + E_nuc, no Madelung term) and the now-bit-equivalenttest_multi_k_at_gamma_mesh_matches_gamma_drivertolerance tightened 2 mHa → 1e-10 — exactly as the A1 milestone did for the RHF/RKS Γ drivers. The dim=1 accelerator-uniformity multi-k tests are green again.
Fixed — SAD initial guess: per-l occupation pinning for transition metals¶
The SAD (superposition-of-atomic-densities) initial guess assigned the
wrong d-shell occupation for every 3d transition metal. The isolated-atom
SCF distributed electrons by a single global eigenvalue sort, and the
3d/4s/4p near-degeneracy makes that bare-atom order disagree with the
physical configuration — with nothing to correct it. The whole 3d row
came out wrong in both directions: Sc/Ti emptied their 3d entirely and
spilled the electrons into an empty 4p, while Ni over-filled to 3d¹⁰ 4s⁰
instead of 3d⁸ 4s². SAD is the AUTO-resolved default guess for every
periodic system and every open-shell / transition-metal molecule
(GuessEngine::resolve_auto), so every transition-metal job on defaults
seeded from a ±1–2 d-electron-wrong density (degrading convergence; found
during NiO/STO-3G validation).
Fix (
cpp/src/guess.cpp): atomic SAD occupations are now pinned per angular-momentum channel from the neutral-atom ground-state configuration instead of by a global eigenvalue sort. The spherically-averaged atomic Fock is block-diagonal in l, so each MO is a pure-l function; within each l-block the lowest-energy orbitals are Aufbau-filled up to the tabulated per-l electron count, with degenerate orbitals sharing fractional occupation (the atomic density stays spherical). The configuration table is the Madelung order plus the standard neutral-atom anomalies (Cr, Cu, Nb, Mo, Ru, Rh, Pd, Ag, La, Ce, Gd, Pt, Au, Ac, Th, Pa, U, Np, Cm, Lr) — what PySCF (scf.atom_hf), Psi4 and ORCA do for their atomic guesses. Sc–Zn now reproduce the experimental d-population (1, 2, 3, 5, 5, 6, 7, 8, 10, 10); main-group SAD is byte-identical; the electron counttrace(D·S) = Zis preserved. If a basis is too small to hold the tabulated configuration (valence-only ECP bases), the build falls back to the previous global-Aufbau fill, so ECP guesses are unchanged.Regression tests (
tests/test_guess.py) pin the d-population for Sc/Ti (under-fill), Ni (over-fill) and the Cr/Cu anomalies, the absence of spurious 4p occupation on Sc/Ti, and main-group invariance.Neutral-atom ground-state configurations are CODATA/NIST shared-background data with no single citable publication, so no citation-database entry is required (CLAUDE.md §8).
Fixed — pob-TZVP sulfur d-shell SCAL typo + hardened CRYSTAL parser¶
The Bredow
pob-TZVP/pob-TZVP-rev2sulfur sources (16_S) mis-keyed the d-polarization SCAL value1.0as two tokens1 0(0 3 1 0.0 1 0, the decimal point typed as a space). The in-package parservibeqc.basis_crystal.parse_crystal_atom_basisread the header as exactly five tokens, so the stray0leaked into the primitive stream: it became the d-shell exponent and the real exponent (0.5207… for pob-TZVP, 0.4107… for pob-TZVP-rev2) the coefficient — a physically invalidexp=0Gaussian, produced silently with no error. Fixed the typo in both source files and hardened the parser: the shell-header read is now line-aware and tolerates the1 0→1.0typo exactly the way the canonicalscripts/basisset_dev/pob_basis_verify.pydoes (the two parsers had diverged), rejects any other stray header token, and rejects any primitive with a non-positive Gaussian exponent. Latent: the shippedpob-tzvp.g94was already correct (regenerated by the tolerant verify tool), so.g94source-vs-shipped parity is unchanged and no regeneration was needed — the bug bit only directvibeqc.basis_crystaluse or a re-fetch from the still-typo’d upstream archive. Regression tests added intests/test_basis_crystal.py. (1a68a7da)
Fixed — optimizer / NEB robustness (silent-flag / convergence-gate audit)¶
The 2026-05-31 end-to-end audit confirmed the geometry-optimizer and NEB physics is correct (geometries ≤2e-6 bohr vs geomeTRIC; thermo = PySCF; NEB barrier 8e-13 vs PySCF; DFT+U gradient 6.9e-9 vs FD). These four were silent-flag / robustness / edge bugs around it:
optimize_moleculereportedconverged=Trueat a non-stationary geometry. scipy’s L-BFGS-B setsres.successon either its gradient (gtol) or its energy-reduction (ftol) criterion, and the driver mappedconvergedstraight fromres.success. With a looseconv_tol_energytheftolstop fired while the forces were still large (e.g. H₂O/STO-3G,conv_tol_grad=1e-6,conv_tol_energy=1e-3→converged=Trueat max-force 0.023 Ha/bohr, >20000× the request). Convergence is now gated on the actual max-component force via a shared_gradient_convergedhelper, applied identically tooptimize_moleculeand the BIPOLE relaxers (relax_atoms,relax_cell_gradient).A non-converged NEB image aborted the band with a cryptic C++ error.
_evaluate_imageran the per-image SCF then unconditionally built the gradient, which raisedRuntimeError: ... not convergedfrom the C++ gradient code. It now raises a clear, image-namedvibeqc.neb.NEBImageSCFError(image index + geometry + remediation) and the same guard protects_evaluate_image_periodic.NEB cold-start used a weaker guess than the single-point drivers. Cold images seeded the low-level
*_scf_with_jkentry with an Hcore fallback, so they needed many more SCF iterations thanrun_uhf/run_rks(AUTO→SAD) and could hitmax_iterwhere an equivalent single point converges. Cold starts now seed SAD (closed- and open-shell), matching the public driver; warm-start (cached density) is unchanged.optimize_molecule(method="rks", functional=None)raised aTypeError. An operator-precedence bug in the SCF dispatch ranopts.functional = Noneagainst the default-"LDA"options, which the pybind str setter rejects. The guard is now parenthesised correctly.
Reported |grad| in the optimizer progress lines is now the max-component
(inf-norm) force, matching the gtol convergence metric (was the 2-norm).
Regression coverage: tests/test_optimize_neb_robustness.py.
Perf — vibe-view: mesh cache, glyph instancing, pre-rendered energy frames (A7)¶
Colormap and opacity sliders no longer re-run marching cubes (A7-02). The last-marched isosurface mesh is cached in
ViewerStatekeyed by(isovalue, replication); slider changes that don’t affect the mesh topology hit the cache and skip straight toplotter.add_meshwith updated appearance. Only an isovalue or replication change invalidates the cache. Cache is cleared automatically when a new file is loaded.Replicated periodic cells render with glyph instancing (A7-08). Previously a 3×3×3 NaCl supercell (54 atoms) issued 54 individual
plotter.add_meshcalls. The structure renderer now groups atoms by(element, radius)and issues one glyph call per element type — typically 2–10 draw calls regardless of supercell size. The molecular (no-replication) path keeps per-atom named actors so the charge-overlay and CPK-restore paths can address individual atoms by name.Trajectory / reaction-path energy plots are pre-rendered (A7-04). Previously every frame step called
matplotlibto re-render the full energy curve. All N frame variants (differing only in where the frame-indicator dot sits) are now rendered once at section load and cached; per-frame updates are O(1) base64 lookups.
Fixed — vibe-view audit: MO normalization, vibrations, plot units, state lifecycle¶
A full audit of the viewer (vibe-view/AUDIT_2026-05-31.md) fixed a cluster
of scientific-correctness and state bugs:
Molecular-orbital isosurfaces were shape-distorted. The on-the-fly GTO evaluator double-applied the spherical-harmonic normalization (AO self-overlap was
(2l+1)/4πinstead of 1), an l-dependent error that mis-scaled s/p/d/f contributions relative to each other. The Cartesian path was wrong in the opposite direction (x²self-overlap 2,x³6). Both now yield unit-normalized AOs; thewavefunction.gtore-evaluation now matches the storedvolume.orbitalcube. (renderers/wavefunction.py)Vibrations animated at the world origin. The writer hard-coded
position: [0,0,0]/atomic_number: 0for every atom in thevibrationssection, so the viewer collapsed the molecule onto the origin. The writer now emits the real equilibrium geometry + atomic numbers, and stores un-mass-weighted Cartesian displacements (was shipping the mass-weighted eigenvector, giving wrong relative atomic amplitudes). The viewer also falls back to thestructuresection’s geometry for older archives. (output/formats/qvf.py,renderers/vibrations.py)Unrestricted MO picker could render the wrong-spin orbital (alpha/beta both keyed on bare integer index); now keyed on a composite
spin:indexvalue with energy / occupation / HOMO-LUMO labels.DOS ignored the Fermi level (unreadable on absolute-energy output); now Fermi-references and draws an E_F marker when E_F is in range.
Band-structure axis was mislabeled “Energy (a.u.)” when
fermi == 0.0although eigenvalues are always eV; ECD/VCD spectra dropped every negative Cotton band (intensity > 1e-10→abs(...)).Heavy elements rendered as hot-pink with a flat radius (atomic number was re-derived from a symbol table that stopped at Ba, Z=56); now uses the parsed atomic number across all CPK render paths (colours reach Z=96).
Clipping a signed volume (
volume.difference/volume.spin) dropped the negative lobe — the clip path only contoured the +isovalue. Both lobes (blue +, red −) are now clipped and shown.Clip planes appeared to do nothing (live-review finding): enabling a clip built a clipped overlay but never hid the full isosurface, so the un-clipped volume stayed on top. Enabling clip now hides the full volume (showing only the clipped slice); disabling restores it; and an isovalue/colormap change while clipping re-applies the clip.
Clip
Enableswitch raised a server error on every toggle (found with a trusted-event Playwright pass): the switch passed its new value to a zero-argumenttoggle_cliphandler, which raisedTypeErrorserver-side — the flag flipped via the two-way binding so the switch looked toggled, but the clip never actually applied. The handler now accepts the value and sets (rather than re-flips) it, matching the other display switches, and reports “Clip plane enabled/disabled” in the status bar.Atom-charge labels lingered after leaving Atomic Properties (live-review finding): switching to a 2D side panel (citations / SCF history / symmetry / bands / DOS / spectra / NMR) removed the charge billboards server-side but never pushed the cleaned 3D scene, so with client-side VtkLocalView the stale labels stayed visible until a 3D section forced a re-sync. The section-switch dispatch now pushes the viewport for those 2D-panel kinds.
Periodic structures drew no bonds. Explicit
bonds-section connectivity is now rendered for periodic cells too (minimum-image endpoint, so a bond across a cell face renders short); inferred covalent-radius bonds stay molecular-only.Bands + DOS now render as a single figure with a shared energy axis (band structure left, DOS rotated right, one Fermi line) — the standard solid-state plot — instead of two independent side-by-side charts.
Bond order (the writer’s per-pair
order) was discarded, so double / aromatic bonds rendered as single. The reader now threads(i, j, order)and the structure renderer draws double/triple bonds as parallel cylinders.QVF spec:
wavefunction.gtonow officially allows periodic systems at the Γ-point (k=0) — the writer already emitted Γ-point crystalline orbitals for periodic runs while the spec forbade it. The spec is corrected (§ 4.6), and the writer records an optionalmo_metadata["k_point"]so the archive is self-describing. Full k-resolved Bloch wavefunctions stay out of scope.CI: a new
vibe-view-testjob runs the viewer suite (pip-only, under xvfb,allow_failure, scoped tovibe-view/**changes) so the 47 viewer test files are exercised in CI without bloating the nativebuild-testjob.Sidebar / panel labels (live-review polish): several kinds showed their raw kind string in the sidebar —
dos.projected, the ECD/VCD/generic spectra, and the spin/ELF/difference volumes had no friendly title, andspectra.uvviswas mis-keyed asspectra.uvso UV-Vis never matched. All now have titles (guarded by a completeness test). The bands/DOS bottom panel was hardcoded “Band Structure” even when showing DOS only; it’s now titled for what it shows (“Density of States” / “Projected DOS” / “Band Structure & DOS”).Multi-file mode broke the entire app bar. The Files dropdown bound the
_active_file_idxstate key, but Vue 3 refuses to expose_-prefixed identifiers to template expressions — so opening more than one file threwReferenceErroron every render and wiped out the app bar (Files dropdown + screenshot / export / camera / measure buttons). Renamed the key toactive_file_idx; a guard test now rejects any_-prefixed template-bound key. (Found by live-browser review — headless tests never render the Vue bindings.)Stale state across file/section switches: a running vibration animation, the MO picker, atom selections, clip planes, and the static-structure ghost during trajectory/vibration playback are now reset/managed correctly.
Reader resilience: a forward-compat/reserved section kind no longer rejects the whole archive (it opens, that section is skipped); the per-section / root
critical: trueflag is now enforced (refuse to open rather than silently render a partial view). Decoded binary members are cached so isovalue/clip sliders and animation playback stop re-reading + re-hashing the blob every tick.Banner no longer mislabels a consumed
bondssection as “skipped, unsupported”; a writer↔viewer kind-drift test now guards the registry.
Fixed — BIPOLE periodic gradient: production forces use FD; multi-k Pulay fold¶
The 2026-05-31 end-to-end audit found the analytic BIPOLE atomic
gradient (vibeqc.bipole_gradient.compute_bipole_gradient_{rhf,uhf,rks,uks})
is gauge-inconsistent with the BIPOLE energy and wrong even at Γ: the
energy is built in CRYSTAL’s Ewald gauge (E_nn via
ewald_nuclear_repulsion, screened-erfc + reciprocal-FT V_ne,
J = J_SR(ω) + J_LR(ω), post-SCF spheropole), but the analytic gradient
assembled truncated direct-space full-Coulomb E_nn/V_ne/J
derivatives (no J^LR-reciprocal or spheropole derivative). The residual
of those ~1.5 Ha/bohr terms had the wrong sign. Only the kinetic and
overlap (Pulay) terms were gauge-correct. (CLAUDE.md §7: a gradient that
disagrees with the exact reference is a bug, not a tolerance knob.)
Production forces / stress / relaxation now route through the exact finite-difference gradient.
vibeqc.bipole_optimize(relax_atoms,relax_cell_gradient,relax_full) and thereforerun_periodic_job(optimize=True)default toforce_mode="fd", which central-differences the real total energy (correct by construction; ~6N SCFs per gradient). This matches what the periodic NEB driver already does. Passforce_mode="analytic"only for research on the analytic gradient itself.compute_bipole_gradient_fdgeneralised to all four methods (method=+functional=), so the exact path is available for UHF/RKS/UKS, not just RHF.The analytic drivers are now gated as a research preview — they emit a
UserWarningand document the gauge incompleteness.Multi-k Pulay bug fixed. The energy-weighted-density (Pulay) overlap-derivative term broadcast
W(Γ)into every lattice cell (correct only at Γ; wrong-sign multi-k forces). It now uses the proper inverse-Bloch foldW(g) = Σ_k w_k Re[e^{-ik·g} W(k)](the same convention asreal_space_density_from_kpointsand the non-BIPOLE multi-k gradient), enabled by passingkmesh=.Tests tightened + expanded (
tests/test_bipole_gradient.py): the old<1.0 Ha/bohranalytic-vs-FD assertion is replaced by a fold equivalence unit test, per-method FD coverage (RHF/UHF/RKS/UKS), translational-invariance and multi-k step-stability checks (<1e-3), and anxfailtripwire pinning the analytic↔FD gauge gap.
A correct analytic BIPOLE gradient needs five new derivative kernels
(Ewald nuclear-repulsion, Ewald-V_ne reciprocal, screened J_SR,
J_LR reciprocal, spheropole) — a new-physics milestone proposed in
HANDOVER_GRADIENT_BUGS.md for the PBC/BIPOLE chat. Kernels 1–2 of 5
(+ the shared FT-gradient linchpin) have landed (2026-05-31):
ewald_nuclear_repulsion_gradient(new C++ incpp/src/ewald.cpp) differentiates the EwaldE_nn, FD-validated<1e-6Ha/bohr.The AO-pair Fourier-transform atomic-position derivative (
vibeqc._aopair_ft.*_grad*) — the linchpin reused by the reciprocal kernels — FD-validated to ~1e-10._v_ne_ewald_gradientdifferentiates the EwaldV_ne(erfcV_shortvia new C++nuclear_erfc_lattice_gradient_contribution, reciprocalV_longvia the linchpin + nuclear structure factor, andbackground·S), FD-validated to4e-10Ha/bohr at fixed density._j_long_range_ewald_gradientdifferentiates the long-range Hartree energy ½ Σ_K kernel(K)|ρ̂(K)|² (kernel 4), reusing the FT-gradient linchpin with the electronic structure factor; FD-validated to3e-13Ha/bohr.eri_lattice_gradient_contributiongainsj_scale(J-suppression → exchange-only) andomega(erfc-screened operator) params (kernel 3), so the Ewald-gauge 2e gradient is assembled as ∂J_SR + ∂J^LR − ½∂K_full. Bit-identical to the old call atj_scale=1, ω=0; the screened J_SR branch is FD-validated to3e-11.The spheropole (EXT EL-SPHEROPOLE) gradient (kernel 5): the C++
compute_ext_el_spheropole_gradient_latticedifferentiates the exact emultipole2 bond-symmetrised second momentK_μν = 2Tr⟨r²⟩ − 2(A+B)·⟨r⟩ + (|A|²+|B|²)⟨1⟩fully analytically — the explicit A/B coefficients plus the moment-integral derivatives ∂⟨r^n⟩/∂R from libintemultipole2deriv_order=1(requires libint built withLIBINT2_DISABLE_ONEBODY_PROPERTY_DERIVS=OFF) — and_spheropole_ewald_gradientscatters them onto each AO’s home atom; validated to ~1e-11 vs a central FD of the energy on MgO (s,p) and NiO (d). (This supersedes the earlier complex-step spheropole kernel, removed with the rest of that spheropole code.) The J^LR electron–electron jellium background derivative was corrected (Coulomb ½ factor; the oldV_bg·Sblock was −2× off), FD-validated (1e-12).
The full analytic BIPOLE RHF gradient at Γ now matches the exact FD
gradient to ~1e-7 Ha/bohr (test_analytic_gradient_matches_fd_gamma,
formerly xfail, now passing), with machine-precision translational
invariance. Beyond the five electrostatic kernels this required two
further fixes:
Local-energy Pulay. BIPOLE diagonalises the Bloch-summed
F(Γ)but evaluates the energy as a LOCAL home-cell contractionTr[D(0)·H(0)](the Γ-only_zero_cross_cell_densityprojection), so the energy-weighted density must use∂E/∂P(0)(the home-cell Fock the local energy differentiates), not themo_energies(_corrected_w_gamma_closed/_bipole_de_dp_home_block; the occ-virt block of ∂E/∂P vanishes, so no CPHF is needed).J^LR mixed convention. The SCF builds J^LR with
rho_hatfrom the Bloch k-density but contracts against the local density, soE_jlr = ½ Σ_K kernel·Re[ρ̂_bloch·ρ̂_local*](verified to 1e-16), not½|ρ̂_local|²; kernel 4 + the ∂E/∂P J^LR term adopt this (Γ-only).The spheropole energy was reimplemented exactly via libint
emultipole2(commitfcd16eb5); the spheropole gradient now tracks that energy (∂E/∂P spheropole analytic from the moments; the HF gradient by FD of the cheap post-SCF spheropole energy).
The analytic drivers stay gated as a research preview: the KS local-energy gauge/orbital response and multi-k ∂E/∂P(k) assembly are not yet certified. Production forces remain the exact FD path throughout.
Fixed — BIPOLE multipole interaction tensor: exact Cartesian derivatives (L≥2 convention)¶
The 2026-05-31 end-to-end audit flagged the BIPOLE multipole-far-field
interaction tensor (vibeqc.bipole_multipole.multipole_interaction_tensor)
as wrong for L ≥ 2, with three pure-Python tests red on main.
Root cause. The tensor scaled each Cartesian derivative
∂^γ(1/|R|)by a single per-LfactorK_L(a{0:1, 1:−½, 2:4⁄3, 3:−4}table). One scalar perLcannot simultaneously normalise the diagonal (∂_xx) and off-diagonal (∂_xz) derivatives, so the recovered derivatives were individually wrong (even∂_zwas off by 2×). The monopole–monopole and an x-dipole·z-dipole case came out right only because the spherical pseudoinverse happened to cancel the errors there; the quadrupole–monopole channel was off by a clean −1/3.Fix.
K_Lis replaced by the exact closed-form Cartesian derivatives of1/|R|for orders 0–3 (_cartesian_derivative_inv_r, the standard Coulomb-kernel gradients, Jackson §4.1). The proven Cartesian-Taylor → spherical pseudoinverse conversion is unchanged, so the result is now exact to the documentedL = l₁+l₂ ≤ 3truncation: dipole–dipole reproduces−2μ²/R³and quadrupole–monopole converges as(size/R)²with no systematic error.Scope. The multipole far-field is opt-in and off by default (
use_multipole_far_field=Falsein all fourrun_pbc_bipole_*drivers), so default-path SCF energies are unaffected; this corrects the experimental far-fieldJ-build (build_j_far_field_multipole) and restores a greenmain.Cleanup. Removed the dead, stale-convention
_wigner_3j_realhelper (no longer referenced since the Cartesian-Taylor rework) and itsscipy.special.factorialimport.Tests (
tests/test_bipole_multipole.py,tests/test_bipole_cell_moments.py). The dipole–dipole and quadrupole–monopole “calibrations” were both degenerate (zero energy by geometry, so a0 == 0check masked the bug); they are rebuilt as non-degenerate direct-Coulomb calibrations with a convergence check that distinguishes truncation from a systematic error.test_Z_1m_dipole_componentsupdated to the live Stone (√2) convention; the stalecartesian_component_indices(0)expectation updated (L=0is valid).
Fixed — BIPOLE EXT EL-SPHEROPOLE: exact bond-symmetrised second moment for all l¶
compute_ext_el_spheropole (vibeqc.bipole_ext_el_pole) is added to the
BIPOLE production E_total, so its accuracy matters for total energies and
CRYSTAL parity. The prior per-(l_a, l_b) spheropole C++ kernel
(cpp/src/spheropole.cpp) had two correctness gaps: its p-p term used a
hard-coded empirical fudge (7.2 · γ² · …, “calibrated on MgO/STO-3G”)
and its d/f shells fell back to the s-s formula.
Fix. The per-pair spheropole kernel is exactly the bond-symmetrised second moment
⟨φ_μ|(r−A)²+(r−B−g)²|φ_ν⟩, now computed directly — for arbitrary angular momentum — from libintemultipole2via the existingcompute_multipole_moments_lattice. The assembly is l-agnostic (no per-(l_a, l_b) cases), so s/p/d/f are all exact; the empirical fudge and the d/f fallback are gone. Prefactorπ·N_e/(6·V)(the Ewald reciprocal K→0 coefficient × the N_e/6 charge moment), pinned against the sealed CRYSTAL reference.Accuracy. Reproduces CRYSTAL’s sealed MgO/STO-3G CYC 0 spheropole (+4.1191890 Ha) to < 0.02 mHa, vs ~7 mHa for the old fudged p-p kernel — so MgO
E_totalmoves ~+7 mHa toward CRYSTAL. d-containing systems (previously s-s-fallback) are now correct.Implementation. Python-only — no C++ rebuild. The now-unused C++ kernel (
spheropole.cpp,compute_spheropole_kernel_lattice,compute_spheropole_bare_integrals) is left in place pending a clean-tree removal (its binding sits in a file under concurrent edit).Tests (
tests/test_bipole_ext_el_pole.py). MgO parity tightened from ±20 % to a tight bound the old fudge fails; added an NiO/STO-3G d-shell regression guard. (Note: NiO is not a CRYSTAL parity check — vibe-qc’s SAD fills Ni 3d¹⁰ vs CRYSTAL’s d⁸ initial occupation, so the input densities differ; the kernel is validated exact on MgO.)
Added — relaxed-scan visualization polish (writer + vibe-view)¶
Reaction-coordinate axis labels.
ScanResult.write_qvfnow records a human-readable coordinate label + unit (e.g.bond 0–1/bohr) on thereaction.pathsection, and vibe-view labels the energy-plot x-axis with them ({label} ({unit})). Additive, optional metadata — noqvf_versionbump; v1/v2 readers that don’t know the fields ignore them. Surfaced throughwrite_reaction_path_qvf(..., reaction_coordinate_label=, reaction_coordinate_unit=).Verified transition-state tagging.
ScanResult.write_qvf(..., transition_state_frames=[i, ...])tags caller-confirmed frames with atransition_statewaypoint (red TS marker) and suppresses the genericpointwaypoint at those frames. The energy maximum is never auto-promoted to a TS — a scan max is not a verified saddle (CLAUDE.md §7).
Added — per-frame densities along reaction paths (writer + vibe-view)¶
Animated density isosurface on
reaction.path.ScanResult.write_qvf(..., emit_volumes_every=N)attaches the electron density on a shared real-space grid for everyN-th scan frame; vibe-view’sReactionPathRenderermorphs the isosurface as the frame changes. Cubes are opt-in + decimated (one extra single-point SCF per emitted frame); the box auto-sizes to the union of emitted geometries. Molecular scans only — periodic raisesNotImplementedError.QVF schema (v1 + v2)
SectionReactionPathgains two optional members —frame_volumes(a newVolume4DBinary,[n_emitted, nx, ny, nz]) +volume_grid— plusvolume_frame_index/volume_label/volume_isovaluein the section metadata. Additive + optional; old readers ignore them, no archives invalidated. Low-level API:write_reaction_path_qvf(..., frame_volumes=, volume_grid=, volume_frame_index=, volume_label=, volume_isovalue=).Shared isosurface builder. The marching-cubes core was factored out of
VolumeRenderer.make_meshintovibeview.renderers.volume.build_isosurface_mesh(...), reused by the reaction-path per-frame overlay.
Added — 2D relaxed scans + scan.surface QVF kind (writer + vibe-view)¶
vibeqc.relaxed_scan_2d(...)drives two internal coordinates over a grid, relaxing all other DOFs at each node (warm-started row by row), and returns aScanResult2Dwith a[nA, nB]energy grid + per-node geometries. Both molecular and periodic.New
scan.surfaceQVF kind (registered in v1 + v2 schemas + vibe-view). Carriesaxis_a/axis_b(1D coordinate values), the 2Denergiesgrid, per-axis label + unit, and optional flattened per-nodegeometries. High-level writerwrite_scan_surface_qvf(...);ScanResult2D.write_qvf(..., include_geometries=).vibe-view
ScanSurfaceRendererrenders a filled-contour energy map (matplotlib) with the minimum starred, a current-node crosshair, and both axes labelled from the coordinate names/units; readerread_scan_surface→ScanSurfaceData; registered inkinds.py, the renderer map, and the app activation + title.
Fixed — vibe-view test coverage gap for basis.ao¶
test_all_supported_kinds_have_rendererslacked abasis.aofixture after that kind was added toSUPPORTED_KINDS(b9e97161); added one so the dispatch-coverage gate is green again.
Fixed — vibe-view variable-dim reaction-path wrapping¶
ReactionPathRenderernow wraps each frame by its own dimensionality via a newdim_for_frame(index)helper that honoursdim_per_frame. Previouslyget_framekeyed off the scalardimonly, so a variable-dim path (scalardimabsent) silently fell back todim=3and mis-wrapped slab frames’ vacuum axis. Latent until a variable-cell/variable-dim producer lands, but now correct.
Fixed: ωB97X-D citation now carries the Grimme-2006 DFT-D2 reference¶
The Chai-Head-Gordon dispersion kernel (
compute_chg_dispersion) uses the Grimme-2006 DFT-D2 atomic C6 / vdW-radius tables, but the citation database did not carry the origin paper, so a ωB97X-D job under-attributed its dispersion. Added[entries.grimme_dftd2_2006](S. Grimme, J. Comput. Chem. 27, 1787, 2006; DOI 10.1002/jcc.20495) and wired it intoroutes.methods."wb97x-d"alongside the existing Chai-Head-Gordon 2008 references. A ωB97X-D job now emits Chai-Head-Gordon 2008 plus Grimme-2006 D2 in its.bibtex/.referencessidecars and the.outreferences block; the.systemmanifest carries all three.tests/test_citations.pycoverage was extended to pin both keys in the assembled citations and the.bibtexbody. (2f577cf5)
Fixed — periodic infrastructure (end-to-end audit, 2026-05-30)¶
KPoints.auto(length)generated ~1.76× too-dense meshes per axis (≈5.4× too many k-points): the bohr⁻¹→Å⁻¹ conversion kept the 2π factor, soauto(30)on Si primitive returned 18×18×18 instead of the intended ~11×11×11. Now consistent per-axis withfrom_kspacing(2π/length)(VASP’s Auto↔KSPACING equivalence). Pure over-spend of compute — not wrong physics — butauto()is an advertised entry point.madelung_constant_for_cellnow refuses dim<3. It is a 3D Ewald self-energy (3D neutralising background); for 1D/2D cells it silently returned a meaningless negative value that fed into theexxdiv='ewald'K-shift. Now raisesNotImplementedError, mirroring the existingrun_pbc_gdf_rhfand C++compute_nuclear_lattice_ewalddim guards. Defaultexxdiv='none'low-dim cells are unaffected.run_krhf_periodic_gdfnow RAISES on a converged non-physical energy instead of silently returning it. The multi-k compcell GDF path converges to garbage on tight ionic crystals (LiH primitive FCC at kmesh=(2,2,2): −2495 Ha, or +8.485 Ha withapply_aft_correction=True, vs PySCF −7.92 Ha) — the multi-k parity fix is the GDF chat’s open M2 milestone. Until it lands, a post-SCF energy-sanity guard rejects both the runaway-negative (|E| > max(10·ΣZ², 100)Ha) and the positive/unbound (Ebeyond a ~1 Ha bound-cell slack) signatures (CLAUDE.md §7: impossible energy = bug — surface it, don’t paper over with damping/thresholds). The slack lets cramped Bravais smoke-test cells through;check_energy_sanity=Falsebypasses the guard for parity/debug scripts. The working periodic-GDF path remains the Γ-onlyrun_pbc_gdf_rhf(gdf_method='rsgdf').The four
run_pbc_bipole_*drivers no longer crash on dim<3. The documented “direct-only path for dim<3 diagnostic runs” was killed by two bugs:compute_ext_el_spheropole(a 3D-Ewald reciprocal-space K=0-limit correction that raises for dim≠3) was called unconditionally from all four drivers, and RKS/UHF/UKS lefte_j_multipoleunbound in the direct (non-Ewald-split) Fock branch, raisingUnboundLocalErrorbefore the spheropole was even reached (RHF already initialised it). The spheropole call is nowdim==3-guarded — the term is identically zero in the direct gauge, soe_ext_el_spheropoleisNonefor 1D/2D — ande_j_multipoleis initialised on every branch. The dim<3 energy is the direct-truncated value, verified vacuum-independent and equal to the molecular RHF energy in the isolated-cell limit (the audit’s 1D/2D correctness criterion).use_ewald_j_split=Truestays explicitly rejected for dim<3. Orthogonal to the open BIPOLE L_max≥2 + Madelung-Ewald work.Non-converged UKS BIPOLE now reports the spheropole.
run_pbc_bipole_uksomittede_ext_el_spheropolefrom its per-iterationPBCBipoleEnergyComponentsand hard-codedE_sphero_final = Nonein its non-converged post-loop branch, so amax_iter-capped 3D UKS run returnedresult.e_ext_el_spheropole = Noneeven though the term was already folded intoE_total(RHF/RKS/UHF all reported it; ~0.003768 Ha on H₂/STO-3G/8-bohr). Both sites now mirror the RKS/UHF siblings;Nonestays correct for dim<3. Regression:tests/test_pbc_bipole_uks.py.Regression gates for the k-mesh and Madelung fixes, plus the multi-k- GDF guard (it must raise on the LiH catastrophe, not return it), in
tests/test_audit_20260530_periodic.py; the BIPOLE dim<3 path (all four drivers, vacuum-independence, isolated-molecule limit) intests/test_pbc_bipole_dim_lt3.py.
Added: CASSCF (orbital-optimized CASCI)¶
New method="casscf" and the low-level vibeqc.solvers.casscf: complete
active space SCF, optimizing the active-space CI vector together with the
molecular orbitals. Built on the validated CASCI + RDM engine; a two-step
macro-iteration rotates the inactive/active/virtual orbitals to a stationary
point using the non-symmetric generalized-Fock orbital gradient
g_pq = 2(F_qp − F_pq) (validated elementwise against finite differences of
the CASCI energy) with a backtracked augmented-Hessian step. Matches PySCF
mcscf.CASSCF to ≤1e-7 Ha on unique-minimum systems (H₂, LiH, HF, Be); the
variational sandwich E_FCI ≤ E_CASSCF ≤ E_CASCI and a vanishing orbital
gradient are the basis-independent correctness checks. As with every CASSCF
the energy surface is non-convex, so the optimizer reaches the stationary
point in the basin of the starting HF orbitals. NEVPT2 / CASPT2 can run on the
CASSCF reference. Ungated (validated vs PySCF). §8 citation route to
Roos/Taylor/Siegbahn 1980. For small active spaces + bases. See
handovers/HANDOVER_MULTIREF.md.
Fixed — multireference methods CASCI / NEVPT2 / CASPT2 (audit 2026-05-30)¶
The CASCI, NEVPT2 and CASPT2 routines that landed in 0c8f0fcf were
incorrect (CASCI ≈ −104 Ha and crashing for >1 same-spin electron; NEVPT2
wrong-sign; CASPT2 ≡ 0) and are rebuilt + validated against PySCF
out-of-process:
CASCI — rebuilt on the validated unrestricted Slater–Condon FCI engine with a correct frozen-core dressing. Matches PySCF
mcscf.CASCIto ≤1e-9 Ha (H₂O CAS(2,2)/(4,4)/(4,6), H₂, LiH); full active space reproduces FCI.NEVPT2 — strongly-contracted NEVPT2 (Angeli 2001) computed from the perturber-projection definition. Matches PySCF
mrpt.NEVPTper excitation class to ≤5e-9 Ha.CASPT2 — strongly-contracted, generalized-Fock H₀ (Andersson 1990). Its MP2 limit and full-space→0 limit are validated; the active-space terms have no local reference (PySCF ships no CASPT2), so CASPT2 is gated behind
VIBEQC_EXPERIMENTAL_MULTIREF=1pending OpenMolcas/ORCA validation.
All three are available via run_job(method="casci"/"nevpt2"/"caspt2", active_space=(n_orb, n_elec)) and the low-level vibeqc.solvers API, with a
new validated reduced-density-matrix module (solvers._rdm) and §8 citation
routes (Roos 1980 / Angeli 2001 / Andersson 1990). For small active spaces +
bases (the spin-orbital engine scales steeply). See
handovers/HANDOVER_MULTIREF.md.
Fixed — GFN2-xTB gated experimental + structural correctness (audit 2026-05-31)¶
The 2026-05-31 end-to-end audit found the molecular GFN2-xTB method
(run_job(method="gfn2_xtb"), shipped v0.10.0) returned silently-wrong
results: H₂ at +0.106 Ha (positive total energy), H₂O at −103.5 Ha (~20×
over-bound; xtb ref ≈ −5.07 Eh), wrong-sign Mulliken charges (O came out
+1.37), and it was not size-consistent — two H₂ 40 bohr apart over-counted
by +0.32 Ha vs 2×E(H₂). Root causes, all fixed:
Repulsion had the wrong functional form.
k_ab·exp(−α·R)— withαtaken from the chemical hardness and an arbitrary0.1scaling — decayed far too slowly (~0.08 Ha per H–H pair still left at 40 bohr), which was the size-inconsistency. Replaced with the GFN2 form(Zᴬ_eff·Zᴮ_eff/R)·exp(−√(αᴬ·αᴮ)·R^{3/2})(Bannwarth-Ehlert-Grimme 2019, Eq. 6), wired to the actual per-elementREPA(α)/REPB(Z_eff) that were previously parsed and discarded.Core/valence confusion. The driver filled the valence basis using the all-electron count (
mol.n_electrons()) and referenced charges against the shell capacity (2 for s, 6 for p). Now uses the neutral-atom valence electron count (Z − noble-gas core) with correct per-shell reference occupations; the global mean-subtraction that coupled non-interacting fragments is removed (Σ Δq = 0 now holds by construction).Energy double-counting in the shell-resolved 2nd-order electrostatics is corrected (
E = Σ 2εᵢ − ½ Δq_shell·γ_shell·Δq_shell + E_rep), and the SCC potential sign convention (Δq = pop − n0, Elstner 1998) is fixed — the pre-fixn0 − popran the SCC the wrong way.PBE-D4 dispersion bolt-on removed.
gfn2.pyhad addedcompute_d4_energy_total(..., functional="pbe")on top of the SCC energy, double-counting dispersion with the wrong damping; GFN2’s own self-consistent D4 is part of the deferred full-method work.
After the fix GFN2-xTB is size-consistent (E(2 far)−2·E(1) < 1e-9, with
the residual water-dimer interaction decaying as the physical 1/R³ dipole
tail) and has correct Mulliken charge signs (O⁻/H⁺, C⁺/O⁻). It is still
not quantitative, for two reasons: the GFN2-defining physics — anisotropic
electrostatics (AES), the 3rd-order on-site term, self-consistent D4 — is not
implemented, and a separate, pre-existing bug in the GFN2 H⁰/overlap
(build_gfn2_hamiltonian_zero + the STO-6G overlap) produces spurious deep
occupied eigenvalues whenever more than the s shell is filled, so absolute
energies run ~20× too deep (an isolated closed-shell Ne atom, Δq=0, comes out
at −4974 Ha with four −621 Ha occupied states instead of ~−5 Ha; H₂O at
−99.7 Ha). That deep-state bug is diagnosed but not fixed here — it is
H0/basis-reimplementation work, later tracked by the GFN2 H0/angle and AES
hardening lanes.
GFN2-xTB is therefore now gated experimental: every evaluation emits a
GFN2ExperimentalWarning (the run_job path included) so an advertised method
no longer returns wrong numbers silently (CLAUDE.md §14), and the §8
Bannwarth-2019 citation + route are wired. The runner now reports the real SCC
iteration count (it previously hard-coded n_iter=1, the source of the
“converged in 1 iterations” log). The unrestricted (run_ugfn2_xtb) and
periodic (run_gfn2_xtb_gamma) variants share the model gaps and the
deep-state bug and are not in the run_job path. Regression gates (size
consistency, charge signs, repulsion decay, the experimental gate; quantitative
targets xfail): tests/test_gfn2_xtb.py. Patch-candidate: v0.10.x.
Fixed — GPW periodic density collocation: periodic-image AO sum (audit 2026-05-31)¶
collocate_density_on_grid (and the Hartree-J builder it backs) evaluated the
molecular AO accessor on the real-space FFT grid with no periodic-image
summation, so an AO whose centre sits near a cell face leaked its Gaussian tail
out of the box and the collocated density under-integrated. H₂/STO-3G in a
10-bohr cell with the atoms on the corner gave ∫ρ dV = 0.41 e against
tr(D·S) = 2 e (atoms at the centre were fine at 2 e). Every GPW density /
cube / ELF / QVF surface for a structure with atoms near a cell edge was wrong
by that leak, and the docstring’s “integrates to tr(D·S)” claim held only for
centred atoms (CLAUDE.md §7 — a source bug, not a quadrature problem). Root
cause: a divergent re-implementation — the periodic stack already had the
correct image-summed evaluator (vibeqc.ewald_j.evaluate_ao_periodic, the
v0.6.x translation-invariance fix), which the M2 GPW module never used.
Fix (python/vibeqc/periodic_gapw_j.py): a new _ao_values_on_grid helper
sums the AO over the 3³ nearest periodic images
(χ_μ(r) = Σ_R χ_μ^mol(r − R), _AO_IMAGE_RADIUS = 1) by shifting the sample
points along the grid lattice — the grid-native twin of evaluate_ao_periodic,
needing only the lattice (no PeriodicSystem). All three AO-on-grid sites now
route through it: collocate_density_on_grid (density/cube/ELF/QVF),
project_potential_to_ao (V→AO), and GpwJBuilder._chi (the cached χ that
build_J uses for both its density collocation and its projection) — so the
Hartree-J build no longer leaks charge for off-centre atoms either. After the
fix the corner density integrates to 2 e, and both ∫ρ and the GPW Hartree
energy ½tr(D·J) are translation-invariant to grid-quadrature noise (~1e-8).
No-op for centred atoms in a vacuum-padded cell (the image sum is
∝ exp(−α·L²)), so the existing GPW suite is unchanged. New regression tests in
tests/test_periodic_gapw_j.py:
test_collocate_density_corner_atoms_integrate_to_n,
test_collocate_density_is_translation_invariant,
test_gpw_hartree_energy_is_translation_invariant.
Fixed — multi-k GPW energy breakdown sums to the total (audit 2026-05-31)¶
The second of the two v0.12.x GPW audit findings (the collocation fix above
is the first). run_periodic_rks_gpw_multi_k reported the correct
result.energy but rebuilt result.breakdown by re-evaluating the kinetic +
nuclear-attraction terms at Γ (k = 0) only — tr(D_total · T^Γ) — dropping the
per-k Bloch phases. breakdown.e_total then disagreed with result.energy by
tens of mHa on a dispersive mesh (−60.8 mHa for [2,1,1] H₂/STO-3G in a
6-bohr cell), and the wrong decomposition was serialised into restarts. The
breakdown is now accumulated from the per-k Bloch-summed Hcore(k) — the same
sum that yields the reported total — so breakdown.e_total == result.energy.
Experimental jk_method="gpw" path; molecular all-electron path unaffected.
Regression test (tests/test_periodic_gapw_j.py):
test_multi_k_gpw_breakdown_matches_total.
Fixed — periodic job crashed on any artifact-writer failure (UnboundLocalError); molden non-ASCII hardening (audit 2026-06-01)¶
run_periodic_job carried a redundant local re-import of OutputFailureKind
/ warn_output_failure inside one late except branch
(from vibeqc.output.errors import …). Because Python binds a name
function-local if it is assigned anywhere in the body, that one import
shadowed the correct module-level import (from .output._errors import …)
across the entire function — so every earlier warn_output_failure(...)
call (the molden / extended-XYZ / POSCAR / CIF / XSF / population / density /
manifest error handlers) raised UnboundLocalError: cannot access local variable 'warn_output_failure' before that late import line ran. A failure
in any optional-artifact writer therefore crashed the whole periodic job
after the SCF had already converged, instead of emitting the intended
graceful vibe-qc output [optional_artifact] warning and continuing. The
shadowing import was itself broken — vibeqc.output.errors does not exist
(the module is vibeqc.output._errors, with the underscore), so the branch
would have raised ModuleNotFoundError even if reached. Fix: delete the
redundant local import; the module-level import already binds both names for
the whole function.
Concrete real-world trigger: a non-ASCII output= stem. The molden writer
opened its file encoding="ascii" and wrote the user-controlled [Title]
(the output basename), so an accented stem (e.g. output="café") raised
UnicodeEncodeError mid-write → the molden error handler fired →
UnboundLocalError → the job died. Molden is now opened encoding="utf-8"
and its [Title] is scrubbed through
vibeqc.output._text_safety.scrub_output_text — the same Trojan-Source
hardening v0.10.4 shipped for the Gaussian-cube header and the TOML manifests
(see the v0.10.4 Security entry below). This is the molden sink that v0.10.4’s
audit deferred as “reachable only via direct Python calls”: in fact both
run_job and run_periodic_job feed the user-facing output basename into the
molden title, so the widening to UTF-8 is paired with scrubbing to keep a
bidi / control byte from leaking into the file. A non-ASCII periodic job now
writes the molden artifact (UTF-8, accent preserved) instead of crashing or
skipping it. Regression tests:
tests/test_output_errors.py::test_periodic_scf_survives_writer_failure (the
UnboundLocalError reproducer) and two new molden pins in
tests/test_output_control_char_hardening.py
(test_molden_header_scrubs_tainted_title,
test_molden_file_accepts_accented_title_as_utf8). Patch-candidate for
v0.10.x.
[v0.10.5] — 2026-06-01¶
Correctness patch on Pisani’s Penguin — composite -3c method
total energies. One cherry-picked fix from main; no feature
changes. Inherits the v0.10.0 codename.
Fixed — composite “-3c” method total energies (audit 2026-05-31)¶
The composite -3c keywords gave wrong total energies (live in v0.10.3).
All fixes are validated against the grimme-lab/gcp (mctc-gcp) Fortran
reference run out-of-process (CLAUDE.md § 10). The SCF + arithmetic was
correct; the bugs were routing and the gCP/SRB correction terms.
hf-3cran FCI instead of HF. Pure-HF composites (functional is None) routed through the wavefunction auto-ladder (≤ 4 e⁻ → FCI), so a turnkeyhf-3con H₂ silently ranfci(ndet=4). Pure-HF composites now resolve torhf/uhf. (F1.1)D3-BJ was dropped from the composite total whenever the SCF returned a
SolverResult; the total now reads the D3-BJ term from a local rather than off the (unwrapped) result. (F1.2)wb97x-3cdowngradedRUNNABLE→PENDING_ECP. vDZP is a valence-only basis whose small-core ECPs need the libecpint inline-primitive feed (Phase 14g, not onmain); it was silently running all-electron (≈ −35.9 Ha on H₂O vs ≈ −76.4). It now raisesCompositeUnavailable.hse-3c(all-electron def2-mSVP) is unaffected and staysRUNNABLE. (F1.3)Short-range bond (SRB) correction rewritten to the canonical kind-discriminated HF-3c / B97-3c forms (
gcp.f90basegrad/srb_egrad2) using the bundled DFT-D3 r0ab radii (newvibeqc._d3_r0ab, extracted byscripts/extract_d3_r0ab.py). The previous single form was ≈ 2× too binding (−52 vs −22.2 kcal/mol SRB on H₂O for HF-3c). Matches mctc-gcp to < 1e-9 Ha. (F1.4)r2scan-3cgCP corrected from +10.18 to +1.12 kcal/mol on H₂O. Thedef2-mtzvppn_virtwere the generic basis-function counts, not the r²SCAN-3c-specific virtual counts, and the gCP damping term (gcp.f90damp=.true.) was absent. Added the damped gCP (opt-indamping=oncompute_gcp; recipe-sideGCPDamping) and correctedn_virtfor def2-mTZVPP / def2-mSVP / MINIX. B97-3c’s spurious gCP removed — it is SRB-only (mctc-gcp computes SRB-only forb973c). (F1.5)
PBEh-3c / B97-3c / B3LYP-3c / r²SCAN-3c / HSE-3c composite totals
shift by the corrected gCP/SRB terms; wb97x-3c now raises instead of
returning a wrong number. Adds tight-tolerance per-composite component +
total-assembly regression tests. (Cherry-picked 000c7f68.)
[v0.10.4] — 2026-06-01¶
Security + correctness patch on Pisani’s Penguin — two cherry-picked
fixes from main, no feature changes. Inherits the v0.10.0 codename.
Security — output writers neutralise bidi / zero-width / control characters (audit 2026-06-01)¶
User-facing output writers interpolated user-controlled free text — the
run_job(output=...) basename, the basis / functional / method
names, SCF exception messages — verbatim into file headers and the
hand-rolled .system / crash-.dump TOML manifests. A bidirectional
format control such as U+202E RIGHT-TO-LEFT OVERRIDE written raw
silently reverses how the file renders in terminals, editors, grep /
diff viewers, and log-parsing agents — the “Trojan Source” display-
deception class (CVE-2021-42574 / CWE-150). The manifests’ hand-rolled
TOML quoter escaped C0 controls (ord < 0x20) but let every codepoint
≥ 0x20 through, so the bidi (U+202A–E, U+2066–9), zero-width (U+200B–F),
BOM (U+FEFF) and C1 (U+0080–9F) classes passed unescaped; the
Gaussian-cube header wrote its title line with no quoting at all. An
audit of all ten output writers confirmed three sinks reachable from the
public run_job surface — the cube header, the .system manifest, and
the crash .dump; the remaining writers’ free-text parameters (QVF
labels, molden / xyz / POSCAR comments) were deferred — see the follow-up
entry below, which corrects this note: several are in fact reachable via
run_job / run_periodic_job. New shared
helper vibeqc.output._text_safety defines the dangerous-character class
once and provides toml_escape_str (escapes it as lossless \uXXXX —
tomllib round-trips the value back) and scrub_output_text (visible
\uXXXX tokens for the escape-less cube header); the three manifest
_toml_str emitters now delegate to it and the cube writer scrubs its
title / comment. Not a memory-safety / RCE issue — it matters mainly for
multi-tenant vq queues, where one user’s job spec is read back in
another’s output. Regression test:
tests/test_output_control_char_hardening.py. (Cherry-picked
6267edcd.)
Security — periodic-structure + trajectory writers scrub user comments (2026-06-01 audit follow-up)¶
Completes the output-writer coverage from the audit above. The cube /
.system / crash-.dump sinks were hardened in 6267edcd; molden was
hardened alongside a periodic crash fix (Fixed entry above). This lands the
remainder: the POSCAR, CIF, XYZ, extended-XYZ and
multi-frame XYZ trajectory writers now scrub their free-text comment /
header lines through vibeqc.output._text_safety.scrub_output_text. QVF
was initially believed safe (JSON serialisation) — that call was wrong (it
uses ensure_ascii=False); QVF is hardened in the follow-up entry below.
Reachability correction: the original audit deferred these as “reachable
only via direct Python calls”. That was an under-call (the molden fix found
the same). run_periodic_job embeds the functional and basis name —
both user-controlled run_* arguments — into the POSCAR and CIF comment
lines (periodic_runner.py), so those two are genuinely user-reachable, the
same Trojan-Source display-deception class (CWE-150) as the cube header. The
XYZ / extended-XYZ / trajectory comment parameters are not currently routed
from user input by the runners, but are hardened for completeness so a future
wiring cannot regress. Low severity; no API change. Regression tests added in
tests/test_output_control_char_hardening.py (test_xyz_comment_scrubbed,
test_extended_xyz_comment_scrubbed, test_poscar_comment_scrubbed,
test_cif_comment_scrubbed, test_trajectory_comment_scrubbed).
Security — QVF archive neutralises bidi / control characters in JSON members + member names (2026-06-02)¶
Closes the last output-writer gap from the 2026-06-01 audit, and corrects an
earlier wrong call: the QVF (.qvf) zip writer was assumed safe because it
serialises metadata as JSON — but every json.dumps used ensure_ascii=False
(28 sites). JSON only force-escapes C0 controls, so the bidi (U+202A–E,
U+2066–9), zero-width (U+200B–F), BOM (U+FEFF) and C1 (U+0080–9F) classes in
user-controlled labels / descriptions / coordinate units / provenance
(method / functional / basis) were written raw into the archive’s
JSON members — the same Trojan-Source display-deception class (CWE-150) as the
other writers, reachable via write_qvf(...) provenance and the periodic
runners. Separately, the generic-spectra section_id was interpolated into a
zip member path (spectra/{section_id}.json) without the _slug() pass
the volume labels use — a bidi and path-traversal gap.
Fix: a new vibeqc.output._text_safety.safe_json_bytes(obj, *, indent=)
serialises with ensure_ascii=False (legitimate non-ASCII — accents, Greek —
stays human-readable) but re-escapes only the dangerous class to a visible
\uXXXX token; all 28 qvf json.dumps sites route through it, and
section_id is now slugged. Volume / orbital labels were already slug-safe in
member names. Low severity; output bytes are unchanged for payloads without
dangerous characters, so existing QVF round-trip tests are unaffected.
Regression tests: test_safe_json_bytes_neutralises_and_round_trips
(tests/test_output_control_char_hardening.py) and
TestProvenance::test_bidi_control_chars_neutralised_in_archive
(tests/test_qvf_writer.py).
Fixed — periodic gauge consistency (audit F1/F3)¶
Multi-k Ewald / Γ-GDF gauge mismatch returned
converged=Trueat a non-physical energy (CLAUDE.md § 7).run_rhf_periodic_multi_k_ewald3dhard-codes the Hartree J to the Ewald-3D builder but readV_ne/e_nucfromlat_opts.coulomb_method; a defaultPeriodicRHFOptions()(DIRECT_TRUNCATED) paired bare-gaugeV_ne/e_nucwith the Ewald-3D J, so the Madelung / charged-cell self- energy did not cancel — LiH FCC primitive at Γ converged to −264.85 Ha (gauge-consistent: −7.52 Ha) with no warning. F1 forces EWALD_3D at entry with an INFO-log override; F3 matches the Γ-GDF molecular-limit / dim<3 nuclear cutoff to the integral cutoff. Only direct callers of the non-dispatcher drivers were exposed. (Cherry- pickeda261613a.)
Not in v0.10.4¶
The remaining 2026-05-31 audit fixes — the BIPOLE analytic-gradient
rebuild, GFN2-xTB experimental gating, BIPOLE dim<3 guards, the GDF
Γ-only µHa V_long fix, and the CASCI / NEVPT2 / CASPT2 rebuild — are
entangled with the post-v0.10.2 periodic / BIPOLE / GDF feature work
and do not cherry-pick cleanly onto the v0.10.3 base. They ride
v0.11.0 (Sun’s Stingray). The two hard v0.11.0 gates remain open:
multi-k GDF µHa parity and the F2 charged-cell analytic-FT Hartree J.
[v0.10.3] — 2026-05-30¶
Docs-only patch on Pisani’s Penguin — release-process and contributor-discipline updates targeting the v0.11.0-prep audit cycle. Inherits the v0.10.0 codename. No code changes.
Added — contributor discipline¶
CLAUDE.md § 8 — inline formula + reference-value comments in code. New standing rule: when a chat implements math from a published paper, the formula and any published reference values used for validation go into inline comments at the relevant code sites. The maintainer keeps PDFs of cited papers in a gitignored
references/folder; the inline comments are the only public-facing record of what equation the code implements and how anyone can verify it without leaving the source file. Targets the v0.11.0-prep audit by external (including LLM-based) reviewers. (Cherry-picked657a1644.)
Fixed — release-process playbook¶
docs/release_process.md— CHANGELOG carry-back step explicit. Closes a long-standing gap in the tag-day playbook: the patch-release flow tagged the new dated CHANGELOG section onto the hotfix branch (which became release tip) but never carried it back to main, leaving main’s CHANGELOG drifted by one section per patch cut. The v0.9.0 → v0.10.2 five-release reconciliation on 2026-05-29 traced to this missing step. Two playbook updates:Minor release § 2 (“Bump the version”) now explicitly bundles the
[Unreleased] → [vX.Y.Z]CHANGELOG promotionbanner
RELEASE_CODENAMES+CITATION.cff+docs/citing.mdedits in therelease: vX.Y.Zcommit.
Patch release § 8 is now “carry the new
[vX.Y.Z+1]dated section back to main” with the explicit mechanical recipe; steps 9-11 renumbered. (Cherry-picked7ad77f10.)
Not in v0.10.3¶
The CHANGELOG reconciliation + trim on main (ce8e8785 +
55bb61f3) is deliberately not in v0.10.3 — those edits
were constructed against main’s pre-reconciliation state and
the cherry-pick would conflict. Release’s CHANGELOG carries
the same [v0.9.0] / [v0.9.1] unpromoted-bucket gap until
v0.11.0’s normal cut flow (now governed by the carry-back
playbook from §1 above) lands a clean CHANGELOG on release at
tag-day.
The installation.md banner-sample refresh (0737543f) is
also not in v0.10.3 — it’s cosmetic and version-numbered for
the v0.10.2 → v0.11.0 transition; rides v0.11.0.
[v0.10.2] — 2026-05-29¶
Patch release on top of Pisani’s Penguin — curated fixes only. Inherits the v0.10.0 codename.
Fixed¶
BIPOLE multipole far-field — nuclear-charge compensation added; multi-k + UHF tests pinned (
e18e8f7e).BIPOLE
use_multipole_far_fielddefault flipped fromNonetoFalseso default behaviour is explicit (584679df).Semiempirical periodic OMx parity gap closed via Loewdin eigenvalue flooring (
3152672a). The molecular ↔ periodic agreement now matches on the regression suite.GFN2-xTB default
max_iterraised 100 → 200 to fix H₂O / CH₄ non-convergence in default-options runs (695bc7be).
Not in v0.10.2¶
Three further v0.10.0 follow-up commits hit cherry-pick conflicts on the v0.10.1 hotfix branch and are deferred to v0.10.3 or v0.11.0:
d7f4b3bd fix(madelung): proper Ewald sum replaces cubic Wigner shortcut — LiH closes to 2 mHa(substantial physics fix; conflicted ontests/test_pbc_gdf_compcell.py).ebe6ca95 fix(bipole): multipole L_max≥2 spherical conversion + RKS XC fixes + multi-iter test(conflicted onpython/vibeqc/pbc_bipole_rks.pyagainst the earlier-picked BIPOLE fixes).f6e01bbe docs(v0.9.0): catch user-guide + API ref up to recent landings(same CHANGELOG + gapw conflict as v0.10.1 deferral).
The post-v0.10.0 GDF chemical-accuracy work (018784c0
all-FT-Bloch path for RSGDF + c949d748 modrho convention + the
M1 investigation diagnostics) is deliberately not in v0.10.2
— that is the v0.11.0 (next minor) headline scope, not patch-grade
material. DFT+U Increment 3 (analytic gradient — 27afac4c) and
GPW M3b (68f986ee + companions) likewise stay queued for
v0.11.0.
[v0.10.1] — 2026-05-29¶
Patch release on top of Pisani’s Penguin. Documentation-only — no code changes. Inherits the v0.10.0 codename.
Fixed¶
Landing-page admonition refreshed —
docs/index.mdwas still surfacing the v0.9.0 “Knowles’s Kingfisher” headline block at v0.10.0 tag time; swapped to v0.10.0 Pisani’s Penguin per the prep doc’s pre-staged Block 2 (fallback variant, since periodic GDF is deferred to v0.11.0).
Added — documentation¶
DFT+U user-guide page (
docs/user_guide/dft_plus_u.md) with worked examples covering the molecular RHF/UHF/RKS/UKS + periodic Γ-only RHF/RKS surface that shipped in v0.10.0 (Increments 1–2 + 4a/4b/4d-light).GAPW user-guide updates — runner dispatch + the M3b GAPW path added in v0.10.0.
User-guide + API-reference catch-up to the v0.9.0 surface, the long-deferred docs-sprint sweep that didn’t run after the v0.9.0 / v0.9.1 cuts (item #22 in the post-tag docs queue — partial close; the CHANGELOG carry-forward gap stays on the queue for v0.10.2 or later).
from_*crosswalk tables brought up to the v0.9.0 surface.API reference index expanded to cover the v0.9.0 surface.
Not in v0.10.1¶
The post-v0.10.0 code fixes that landed on main (semiempirical
OMx periodic parity, BIPOLE multipole far-field, Madelung Ewald
sum proper, GDF chemical-accuracy improvements, DFT+U Increment 3
analytic gradient, GPW M3b) are not in v0.10.1 — they ride
v0.10.2 (or v0.11.0). v0.10.1 is a strict docs-only patch to
correct the stale landing-page surface; mixing in code fixes
would have made it a fix-grade patch rather than a docs-grade
one.
[v0.10.0] — 2026-05-27 — Pisani’s Penguin¶
Periodic-SCF maturity: multi-k EDIIS + Python accelerator family;
SYM6 crystal symmetry (full character-tables + M3b per-cell-pair J/K
kernel); BIPOLE L_max≥2 Cartesian→spherical multipole conversion;
GPW M2-full iterative SCF; CRYSTAL-α / Ewald-ω gauge unification (the
cluster-A1 fix at 859efe0a resolves +19 Ha translation-invariance
violation on LiH rocksalt by enforcing EWALD_3D and matching
CRYSTAL’s 2.8/V^(1/3) α formula); end-to-end CRYSTAL parity test
suite (P1–P5 at 5231ce6c).
Semiempirical methods diversification: GFN2-xTB (full stack: parameter parser/fetcher, Fock builder, SCC driver, analytic gradient, periodic Γ, UGFN2-xTB, periodic gradient + stress); PM6 (NDDO Fock builder, STO-overlap resonance, FD gradient, MOPAC-matching core-core damping); OM1/OM2/OM3 (skeletons, parameter loaders, Loewdin-correction Fock builder, 77-test benchmark suite); architectural Phase-0 framework — method registry
shared parameter interfaces + Hamiltonian builders +
CoreParameterSetbasis builder.
DFT+U (Hubbard correction, Dudarev convention) — Increments 1, 2,
4a/4b/4d-light: occupation-matrix machinery; C++ Eigen kernel +
pybind11 surface; RHF/RKS/UHF/UKS molecular SCF integration; periodic
Γ-only RHF + RKS; run_periodic_job(..., dft_plus_u=[HubbardSite(...)])
high-level surface. Increment 3 (analytic gradient) is queued for
v0.10.x — see Known limitations.
Nudged Elastic Band (NEB) — M1–M4 — path primitives (interpolate_linear + IDPP) → improved-tangent driver with joblib per-image SCFs → climbing-image NEB (Henkelman + Uberuaga + Jónsson 2000) → periodic dispatch via BIPOLE + FD gradient. Density warm- start across RHF/UHF/RKS/UKS drivers; end-to-end H + H₂ benchmark example.
Other v0.10.0 deliverables: PWPB95 double hybrid (first
meta-GGA / SOS double hybrid in vibe-qc); periodic D3-BJ dispersion;
native slab + adsorbate builder; relaxed coordinate scans
(molecular + periodic); Fermi-Dirac smearing consolidation; QVF
spec v1.1 (DOS, potential, RDG, Fermi surface, phonons, EOS,
extension governance); vibe-view Phase A (bond angles, isosurfaces,
bands+DOS panel, publication export); 87 BSE-fetched basis sets
integrated under the §1 Feist clearance (docs/license.md § 3a +
§ 3e for the in-house semiempirical parameters).
Retraction¶
v0.9.0 Known-issues — XC residuals retracted. v0.9.0’s CHANGELOG flagged the polarised-GGA xc-kernel API + M06 meta-GGA UKS parity as residuals “tracked for 0.9.1.” Subsequent investigation by the XC chat confirmed both were already closed at the v0.9.0 cut commit by
b34f051c— which bundled the XC fixes alongside the test-gate restore + the k-GDF rewrite, despite its misleadingfix(release):subject. M06 drift was a vibe-qc/PySCF grid-mismatch artefact, resolved by pinning a matched fine grid (_fine_m06_grid: 120/23/46; PySCFatom_grid=(120,590),prune=None,use_newton). Tolerance kept at 2e-5 — CLAUDE.md § 8 discipline respected, not loosened. No code change in v0.10.0 — this entry retracts an overcautious release note.
Known limitations / deferrals¶
Periodic GDF (compcell + multi-k + AFT) — deferred to v0.11.0. GDF chat made incremental progress (
dfa34b4aBloch-summed lattice pair-FT fix in_compcell_aft_correction_3c;871c5f4aG=0 Madelung correction with H₂ sub-µHa PySCF parity), but the M1 investigation (b8f1e509) characterised remaining bugs not closed in this cycle: RSGDF SR+LR calibration is 98.5% off for L=0 / 23.8% off for L=1 on periodic cells, and the compcell metric is near-singular (cond 1e10–1e14) for tight ionic crystals. Fourth-cycle GDF deferral (v0.8.0 → v0.9.0 → v0.10.0 → v0.11.0); seedocs/roadmap.mdfor the honest pattern.DFT+U Increment 3 (analytic gradient) — open math question flagged by the DFT+U chat in HANDOVER § 1i. The Increments that DID land (1, 2, 4a, 4b, 4d-light) ship in v0.10.0 — molecular RHF/UHF/ RKS/UKS + periodic Γ-only RHF/RKS. Numerical gradients still work; the analytic gradient lands in v0.10.x once the math question resolves.
Native D4 default-backend flip — stays opt-in. Available via
backend="native"; thedftd4LGPL Python dep remains the default until the production-dataset upgrade lands (queued for v0.10.x / v0.11.0).ωB97X-D — still blocked on a D2/CHG dispersion reference.
Solvation analytic gradients — slipped from v0.9.1; v0.10.x.
[v0.9.1] — 2026-05-22¶
Patch release on top of Knowles’s Kingfisher. Curated fixes from the v0.9.0 stabilization window; no new features. Inherits the v0.9.0 codename.
Fixed¶
QVF (vibe-view archive format) — critical contract fix. Align the QVF writer schema with the vibe-view consumer; align the producer manifest with the consumer contract; unify
voxel_vectorssemantics (per-voxel step, not full span). Fixes silent producer/consumer drift that could mis-render density / orbital cubes in vibe-view.Solvers — audit fixes. DMRG
_build_full_hamiltoniannow enforces particle-number conservation (N-electron sector).active_space, the DMRG particle-number gate, and the v2RDM Q/G rejection paths corrected.BIPOLE periodic driver — initialise
e_j_multipolein the non-Ewald-J-split path (no behaviour change in the split path).Symmetry — handle already-primitive cells in
symmetry=True, plus an M2a LMS fix.ECP — corrected Si UHF reference in
tests/test_ecp_scf.pyto the post-899ab16a³P ground state.Bundled basis library — restored per-publication citation headers on
pob-tzvp.g94/pob-dzvp-rev2.g94/pob-tzvp-rev2.g94(Peintinger 2013 / Vilela Oliveira 2019).
Documented / hardening¶
CI deploy — pin
vibe-qc.comSSH host keys instead of TOFU at job time (audit #6).Privacy — scrub maintainer home paths from runtime defaults (audit #7).
Packaging — removed the broken
viewer-gpuextra; the two-step install (pip install ./vibe-viewthenpip install vibe-qc) is now documented. A PEP 508 direct-URL restore is queued for v0.9.2.DF + f-shell gradient bug — resolution recorded in the v0.8.0 notes.
Not in v0.9.1¶
The vq-queue security improvements (vq v0.6.44–v0.6.48), the
periodic-Becke C++ default flip, the Ewald-ω cutoff derivation, and the
remaining QVF density-grid unit refinement are deferred to v0.9.2 /
v0.10.0 — these picks conflicted on the v0.9.0 hotfix branch and need
either intermediate dependencies or a separate patch effort. They
remain on main.
[v0.9.0] — 2026-05-22 — Knowles’s Kingfisher¶
Wavefunction methods (FCI / Selected-CI / DMRG / v2RDM), the native D4 dispersion backend, the semiempirical DFTB structure-optimization stack, M-series crystal symmetry, and the BIPOLE periodic driver.
Known issues¶
v0.9.0 follows a ship-then-iterate plan. It builds and imports cleanly and the ECP-SCF segfault regression is fixed and verified on the cut commit, but the full
pytest -m 'not slow'suite had not completed a clean end-to-end run on the exact cut commit at tag time. Residual failures from the v0.9.0 stabilization (polarised-GGA XC kernel, M06 meta-GGA parity) are tracked for 0.9.1.Periodic GDF and ωB97X-D are not in v0.9.0 — both deferred (see
docs/roadmap.md).
Added — native D4 dispersion backend (Phase D4b complete)¶
Native D4 dispersion — MPL-2.0 implementation replacing the optional LGPL-licensed
dftd4dependency. All 8 Phase D4b milestones landed:Imaginary-frequency CPHF polarizability (D4b-1)
Molecular C6 pipeline + DOSD validation (D4b-2)
Reference-system catalogue — 21 systems over period-2 (D4b-3)
CN + charge Gaussian-weighted C6 interpolation (D4b-4)
Two-body BJ damping + 51 per-functional parameter sets (D4b-5)
Three-body Axilrod-Teller-Muto term (D4b-6)
Analytical gradient (geometric + C6 chain rule) (D4b-7)
compute_d4(mol, func, backend="native")integration (D4b-8)
Reference dataset —
d4_reference_data.json(60 KB), computed from first principles at the def2-svp level. 8 elements (H-B-C-N-O-F-He-Ne), 21 reference systems. Upgrade path toaug-cc-pVDZfor production accuracy is documented.Modules:
vibeqc.dispersion_d4_model(14 public functions includingcompute_c9andd4_atm_triple_energy),vibeqc.dispersion_d4_parameters(51 functionals),vibeqc.dispersion_d4_reference_data(D4ReferenceDataset),vibeqc.dispersion_d4_reference_systems(21 systems).The
dftd4backend remains the default pending production dataset upgrade; switch viabackend="native". Seehandovers/HANDOVER_D4_NATIVE.md.
Fixed — D4 ATM three-body test API (1655013e)¶
Exported
compute_c9andd4_atm_triple_energyfromvibeqc.dispersion_d4_model— the two factored helper functions the D4b-6 test suite expected but that were missing from the initial ATM three-body implementation. Refactoredcompute_d4_atm_energyto use them internally, removing duplicated math. (6 CI failures resolved.)
Fixed — ECP SCF segfault on molecules with fewer electrons than the ECP core¶
run_rhf/run_rks/run_uhf/run_ukssegfaulted the process when an ECP replaced more core electrons than the molecule had: the valence countn_elec = n_electrons − total_ncorewent negative, producing a negative occupied-orbital count and acblas_dgemmcall with a negative dimension. The drivers now raise a clearstd::invalid_argumentthat names the likely cause (aMoleculecharge that pre-subtracts the ECP core). Regression introduced by the ECP-ncore propagation change (899ab16a).Corrected three ECP tests (
test_ecp_validation.py,test_ecp_scf.py,test_solvation_cpcm.py) that built Zn²⁺ with a pre-subtractedMoleculecharge against the pre-899ab16a convention. They now pass the physical ionic charge (+2), soMolecule.n_electrons()is the full electron count the SCF expects before it subtracts the ECP core itself.
Added¶
Non-mean-field solvers (
vibeqc.solvers): four wavefunction-based electronic-structure methods that do not conceptually rely on a Hartree–Fock reference state.Selected CI (CIPSI-style): iterative determinant selection, variational diagonalization, PT2 correction.
DMRG: two-site MPS sweeps with bond-dimension scheduling and SVD truncation.
Variational 2-RDM: augmented-Lagrangian SDP with PQG N-representability constraints.
Transcorrelated Hamiltonian: similarity-transformed H via Gaussian/Jastrow correlators, consumable by CI/DMRG backends.
run_job(method="selected_ci" | "dmrg" | "v2rdm" | "transcorrelated_ci")— all four methods accessible through the standard runner.Unrestricted Slater–Condon rules and exact FCI in the determinant basis (verified against PySCF FCI to machine precision).
vibeqc.solverstop-level namespace:solve_selected_ci,solve_dmrg,solve_v2rdm,build_transcorrelated_hamiltonian,Hamiltonian, etc.Tutorial:
docs/tutorial/non_hf_solvers.md.
Historical Codename Artwork (pre-v0.9.0)¶
v0.7.0 - Löwdin’s Compass¶
The codename remains Löwdin’s Compass. The original compass-object artwork
remains bundled as _static/images-codenames/07-vibe-qc-v0.7.0-lowdins-compass.png
for the vibe-qc-and-AI talk; this jellyfish variant keeps the historical
changelog artwork aligned with the animal-series motif.