Moving from CRYSTAL

vibe-qc’s roadmap targets feature-for-feature parity with CRYSTAL on the v1.0 / v2.0 timeline, the Peintinger-Vilela Oliveira-Bredow pob-* basis-set family is bundled, and the periodic SCF gauge convention is being aligned with CRYSTAL’s BIPOLE construction (see user_guide/bipole). For CRYSTAL users, the physics maps directly; the input idiom is different — CRYSTAL takes a fixed-grammar .d12 deck while vibe-qc takes a Python script. This page is a keyword-by-keyword crosswalk.

Input file shape

CRYSTAL .d12

TiO2 rutile
CRYSTAL
0 0 0
136
4.5937  2.9587
2
22  0.0     0.0     0.0
8   0.30530 0.30530 0.0
END
BASISSET
POB-TZVP
END
DFT
EXCHANGE
PBE
CORRELAT
PBE
END
SHRINK
8 8
TOLINTEG
7 7 7 7 14
FMIXING
30
LEVSHIFT
2 1
END

vibe-qc Python

import numpy as np
import vibeqc as vq

# Rutile lattice — fetched from a CIF / Materials Project / hand-built.
a, c = 4.5937 * 1.8897259886, 2.9587 * 1.8897259886    # → bohr

sysp = vq.PeriodicSystem(
    dim=3,
    lattice=np.diag([a, a, c]),         # tetragonal P4_2/mnm
    unit_cell=[
        vq.Atom(22, [0.00000 * a, 0.00000 * a, 0.0 * c]),     # Ti
        vq.Atom(22, [0.50000 * a, 0.50000 * a, 0.5 * c]),
        vq.Atom( 8, [0.30530 * a, 0.30530 * a, 0.0 * c]),     # O
        vq.Atom( 8, [0.69470 * a, 0.69470 * a, 0.0 * c]),
        vq.Atom( 8, [0.80530 * a, 0.19470 * a, 0.5 * c]),
        vq.Atom( 8, [0.19470 * a, 0.80530 * a, 0.5 * c]),
    ],
)
basis = vq.BasisSet(sysp.unit_cell_molecule(), "pob-tzvp")
kmesh = vq.monkhorst_pack(sysp, [8, 8, 8])              # SHRINK 8 8

opts = vq.PeriodicRKSOptions()
opts.functional   = "PBE"
opts.lattice_opts.cutoff_bohr = 12.0                    # ≈ TOLINTEG row 5 control
opts.fock_mixing  = 0.30                                # FMIXING 30
opts.level_shift  = 0.2                                 # LEVSHIFT 2 1 — vibe-qc uses Ha
                                                        # → 0.2 Ha ≈ 5 eV ≈ "2"
                                                        # in CRYSTAL's units (×10⁻¹ Ha)

result = vq.run_rks_periodic(sysp, basis, kmesh, opts)
print(result.energy)

The structural difference is that vibe-qc’s input is executable Python rather than declarative text. That trades CRYSTAL’s grammar guard-rails for the full flexibility of a programming language — you can sweep over functionals in a for loop, generate geometries from a CIF, splice in custom basis sets, etc., without input-file pre-processing.

For high-volume CRYSTAL ports the vq submit -d <dir> directory-submit pattern lets you keep one Python “driver” plus several geometry files alongside it, mimicking CRYSTAL’s .d12 + INPUT layout.

Keyword crosswalk

Important

Many CRYSTAL keywords accept integer values that map to scaled SI / atomic units (the LEVSHIFT 2 1 means \(0.2\) Ha applied for 1 iteration). vibe-qc uses Hartree directly for every energy-like field. Multiplying CRYSTAL’s integer by \(10^{-1}\) generally lands the same number.

Geometry + symmetry

CRYSTAL

vibe-qc

CRYSTAL (3D), SLAB (2D), POLYMER (1D), MOLECULE (0D)

PeriodicSystem(dim=3 / 2 / 1) or just Molecule(...)

Space-group number + lattice + asymmetric unit

Full lattice matrix + full unit-cell atom list (vibe-qc reconstructs symmetry via spglib)

KEEPSYMM

(default — vibe-qc keeps the input cell)

EXTPRT (print extended geometry)

Manifested as .xyz / .POSCAR / .xsf siblings by run_periodic_job, see output_files.

SUPERCEL

Build the supercell in Python before calling PeriodicSystem(...) (e.g. via ASE make_supercell).

ATOMSPIN (initial spin per atom)

The UHF / UKS drivers carry multiplicity on the Molecule; per-atom spin density isn’t a CRYSTAL-only concept and lands via the SAD initial guess.

OPTGEOM (geometry optimisation)

run_periodic_job(..., optimize=True) is roadmap (periodic gradients are not yet shipped); molecular optimisation is run_job(..., optimize=True) today.

Note

Lattice direction matters. CRYSTAL stores lattice vectors as rows; vibe-qc as columns. The conversion is one transpose: lattice_vibeqc = lattice_crystal.T. Both are in the same units once you convert Å → bohr.

Basis sets

CRYSTAL

vibe-qc

BASISSET / POB-TZVP / END block

BasisSet(mol, "pob-tzvp") — same name

Per-element CRYSTAL-format basis lines

Drop the file under basis_library/custom/ and re-run ./scripts/setup_basis_library.sh. The CRYSTAL-format parser (vibeqc.basis_crystal) handles the per-element layout.

Mixed BSE + custom basis

Same pattern — drop in custom/, rebuild. See basis_sets § “Custom basis sets”.

INPUT block with explicit primitive coefficients

Same custom/ mechanism.

The bundled pob-* family on main covers H-Br (the same coverage as the original Peintinger 2013 paper). The 87 BSE-fetched basis sets that ship on the basissetdev branch are kept off main per CLAUDE.md § 4.

ECPs

CRYSTAL

vibe-qc

ECP keyword inside the basis block

opts.ecp_centers = [vq.ECPCenter(Z=..., xyz=...)] + opts.ecp_library = "lanl2dz" or "ecp46mdf" etc.

HAYWLC / HAYWSC

ecp_library="lanl2dz"

Stuttgart MDF/MWB ECPs from external libraries

One of the bundled ecp{10,28,46,60,78}mdf libraries, picked by element row (see ecp § Library-selection table).

See tutorial 32 for the ECPCenter recipe in detail.

k-mesh

CRYSTAL

vibe-qc

SHRINK n n

vq.monkhorst_pack(sysp, [n, n, n])

SHRINK n_HF n_K (two different meshes)

Not directly supported; vibe-qc uses one MP mesh per call. For HF-K convergence work, sweep monkhorst_pack(sysp, [n, n, n]) over n and report the convergence trail.

KNETOUT (k-net diagnostic)

The result object carries result.kmesh.kpoints_cart and result.kmesh.weights.

GAMMA (Γ-only)

vq.monkhorst_pack(sysp, [1, 1, 1]) or use the Γ-only drivers run_*_periodic_gamma*.

Coulomb / exchange truncation (TOLINTEG)

CRYSTAL’s TOLINTEG T1 T2 T3 T4 T5 controls five different screening tolerances. vibe-qc collapses these into two knobs plus the automatic optimiser:

CRYSTAL TOLINTEG row

vibe-qc analogue

T1 (Coulomb overlap, bipolar)

opts.lattice_opts.cutoff_bohr (real-space cutoff)

T2 (Coulomb penetration)

(automatic — Cauchy-Schwarz screening at the C++ level)

T3 (exchange overlap)

(automatic — same as T2)

T4 (exchange pseudo-overlap)

(automatic — same)

T5 (pseudopotential)

(handled inside libecpint)

PeriodicRHFOptions.auto_optimize_truncation = True (the default on v0.7+) jointly optimises the lattice cutoff and Schwarz threshold so you typically don’t touch them. See linear_dependence for the diagnostic stack when they need tightening.

SCF convergence — direct keyword map

CRYSTAL

vibe-qc

MAXCYCLE n

opts.max_iter = n

TOLDEE n (energy threshold = \(10^{-n}\) Ha)

opts.conv_tol_energy = 10**(-n)

TOLDEG n (gradient threshold)

opts.conv_tol_grad = 10**(-n)

FMIXING p (Fock-matrix mixing, p in %)

opts.fock_mixing = p / 100 — same Pulay mixing

LEVSHIFT n 1 (level shift = n * 0.1 Ha for one iter)

opts.level_shift = n / 10 (in Hartree)

LEVSHIFT n 0 (persistent shift, never removed)

opts.level_shift = n / 10; opts.level_shift_warmup_cycles = 0

BROYDEN

Not implemented in vibe-qc; vibe-qc uses Pulay DIIS / EDIIS by default.

ANDERSON

Not implemented — same comment.

SMEAR width

opts.smearing_temperature = width (Ha; named presets "metal", "small-gap", "auto" are also accepted). See tutorial 42.

DIIS

Default on (opts.scf_accelerator = vq.SCFAccelerator.EDIIS_DIIS in v0.8.0; DIIS available as the alternative). See tutorial 41.

SPINLOCK n_alpha n_beta (fix occupations)

Set Molecule.multiplicity for the spin; no per-spin lock yet.

GUESSP (read previous density)

Roadmap (Phase 14 — checkpoint reads).

Coulomb / Exchange engine

CRYSTAL

vibe-qc

BIPOLE (default — direct + multipole far-pair)

run_pbc_bipole_rhf — research-preview (not production-ready until Phase 5 multipole-branch wiring lands)

BIPOLAR n (bipolar expansion order)

Not yet exposed — defaults pinned in pbc_bipole.py.

Implicit Ewald-V_ne / Ewald-E_nn

Default for run_rhf_periodic_* and run_rks_periodic_* (opts.lattice_opts.coulomb_method = vq.CoulombMethod.EWALD_3D).

OPTGEOM (periodic gradient)

Roadmap (G1). Molecular optimize=True works today.

FREQCALC (periodic vibrations)

Roadmap (depends on periodic gradients + Hessian). Molecular vibrations via run_job(..., compute_hessian=True) work today.

Functionals

CRYSTAL

vibe-qc

EXCHANGE PBE / CORRELAT PBE

opts.functional = "PBE" (libxc-routed)

EXCHANGE BECKE / CORRELAT LYP / HYBRID 20

opts.functional = "B3LYP" — vibe-qc resolves the canonical 3-parameter B3 mixing automatically.

PBE0 / B3LYP

Same names, same libxc ids

PW1PW

opts.functional = "pw1pw" — Bredow’s periodic hybrid. See functionals.

DFTD3 / XLYP-D3 style suffix

Pass opts.dispersion = "d3" / "d3bj" / "d4". Independent from the functional name.

HSE06 / LC-ωPBE (range-separated)

Roadmap (RSH machinery for periodic K still pending).

What CRYSTAL does that vibe-qc doesn’t (yet)

Use CRYSTAL for these for now:

  • Production multi-k bulk SCF with the right BIPOLE / EXT EL-POLE / EXT EL-SPHEROPOLE gauge. vibe-qc’s BIPOLE driver is research-preview; Ewald-3D works at multi-k but isn’t the CRYSTAL-parity gauge target.

  • Periodic geometry optimisation (OPTGEOM). Periodic gradients are roadmap Phase G1; vibe-qc only supports molecular optimisation today.

  • Vibrational frequencies for solids (FREQCALC). Depends on periodic gradients + Hessian; molecular FREQ works via run_job(compute_hessian=True).

  • Phonon dispersion (PHONON). Roadmap.

  • Elastic constants (ELASTCON). Roadmap.

  • Symmetry-adapted SCF. vibe-qc reads symmetry via spglib but doesn’t yet exploit it inside the SCF loop. Big-symmetry cells pay full O(N) per k-point.

  • Open-shell multi-k SCF (your favourite antiferromagnetic oxide). Γ-only and closed-shell multi-k are shipped; open-shell multi-k is roadmap.

  • Coupled-cluster periodic (MP2, CCSD). vibe-qc’s molecular MP2 / RI-MP2 is solid; periodic post-HF correlation is v2.x scope.

What vibe-qc does that CRYSTAL doesn’t (or does differently)

Things you gain by switching:

  • Auto-citations. Every run_periodic_job writes a .bibtex / .references pair listing every paper to cite — including the pob-* basis-set papers, libxc, spglib, and the per-functional papers. See citations.

  • Python scripting. Sweeps over functionals / k-mesh / lattice constant are a few-line for loop, not multiple .d12 files.

  • ASE-bridged geometry I/O. Read CIF / POSCAR / Quantum Espresso .pwi directly; pass to PeriodicSystem via the ASE bridge.

  • vq queue with vibe-qc-aware preflight. vq submit --vibeqc-preflight knows which artefacts the job will write, so remote fetch is selective. See tutorial 43.

  • Open-source. MPL-2.0; no per-seat license; no proprietary binary distribution.

Validating vibe-qc against CRYSTAL

vibe-qc treats CRYSTAL14 as an out-of-process validation reference (per CLAUDE.md § 10: no QC-code imports). The vq submit runner spawns CRYSTAL14 as a subprocess on the configured remote; the parity matrix collects results across multiple codes for the 15 demo systems (commit 39c5309).

For ad-hoc validation, the typical workflow is:

  1. Write the same system as a vibe-qc Python file and a CRYSTAL14 .d12.

  2. vq submit input.py and vq submit -- bash run-crystal.sh (see queue § CRYSTAL14 workflows).

  3. Compare the two .out files’ total energies.

For 3D crystals at multi-k, expect vibe-qc multi-k Ewald-3D to match CRYSTAL’s SHRINK 8 8 to within a few mHa per cell at the same basis (the gauge convention is the same up to the Madelung self-image correction; the multipole-far-pair branch is the remaining piece).

See also