Good practices¶
A short catalog of working conventions that aren’t obvious if you’ve never operated a quantum-chemistry program from a clone. None of these are vibe-qc-specific — they apply to running Gaussian / ORCA / NWChem / PySCF / CP2K just as much — but nobody tells you, and everybody learns the hard way. Read this once before you start.
File organisation¶
One project = one directory, outside the repo. Vibe-qc writes
its outputs (.out, .molden, .traj, .cube, .xsf) into the
current working directory. If you run inside the cloned source
tree, those files land next to cpp/ and python/ — at best
clutter, at worst a git clean away from being deleted.
The convention used throughout these docs:
~/vibeqc-runs/
├── water-pbe/
│ ├── water.py
│ ├── water.out
│ ├── water.molden
│ └── water.traj
├── h2o-trimer-mp2/
│ └── ...
└── lih-bulk-bands/
└── ...
Geometries in a sibling directory if you reuse them across
methods — saves you from cp h2o.xyz ../next-project/ every
time you start a comparison:
~/vibeqc-runs/
├── geometries/
│ ├── h2o.xyz
│ ├── glycine.xyz
│ └── lih_4.5bohr.cif
├── water-pbe/
└── water-b3lyp/
The .out file is the record. It carries the runtime banner:
vibe-qc version, git revision, dirty-tree flag, native-library
versions. Don’t delete .out files for runs you might cite later
— they’re your provenance trail.
Naming¶
Convention: <system>_<method>_<basis>.py. Six months
later you can ls | grep glycine and find every variant you
ran:
glycine_rhf_631gss.py
glycine_rks_pbe_def2tzvp.py
glycine_uks_b3lyp_def2tzvp.py
glycine_mp2_ccpvdz.py
Match the output= argument so the side-effect files line up:
run_job(mol, basis="def2-tzvp", method="rks", functional="PBE",
output="glycine_rks_pbe_def2tzvp")
# produces: glycine_rks_pbe_def2tzvp.out
# glycine_rks_pbe_def2tzvp.molden
For periodic runs, encode the k-mesh too:
lih_rks_pbe_sto3g_k4x4x4.py.
Reproducibility¶
Pin a tagged version for any calculation going into a paper.
git checkout v0.4.6 (or whichever release you’re targeting)
before running setup_native_deps.sh and pip install.
The banner will then read Release v0.4.6 instead of the
moving main target, and your numbers stay reproducible if a
future commit changes a default. See
picking a build to test against
for the full workflow.
Version-control the input script alongside the manuscript draft.
Treat glycine_mp2_ccpvdz.py like Methods-section text — it
is the methods section, in executable form.
Save the banner. Every .out file starts with a labeled
box recording vibe-qc version, codename, git revision, dirty-tree
flag, and linked native-library versions:
╔════════════════════════════════════════════════════════════════════════════════╗
║ Release v0.5.0 "Wilson's Otter" — Quantum chemistry for molecules and solids ║
║ © Michael F. Peintinger · MPL 2.0 · https://vibe-qc.com ║
║ linked: libint 2.13.1 · libxc 7.0.0 · spglib 2.7.0 ║
╚════════════════════════════════════════════════════════════════════════════════╝
That single block is your provenance line — don’t strip it when copy-pasting into a SI appendix. The version + git revision + linked library versions together pin the exact binary that produced the numbers, down to the libint / libxc / spglib commits.
If you used uncommitted local changes, the banner will read
dirty (between the SHA and the closing parenthesis). Don’t
ship dirty-tree numbers in a paper. Either commit + tag and
re-run, or document the diff in your SI.
See example_outputs for full reference outputs from canonical calculations — the banner above is excerpted from the H₂O RHF reference run.
Performance hygiene¶
Always set OMP_NUM_THREADS explicitly. OpenMP defaults to
“every core on the machine,” which is rude on shared nodes and
pessimal on workstations with hyperthreaded cores (you usually
want physical cores, not logical):
OMP_NUM_THREADS=4 ~/path/to/vibeqc/.venv/bin/python water.py
Estimate memory before a large run. Vibe-qc has a pre-flight memory budget estimator — it’ll tell you the integral-storage footprint before the SCF starts, so you don’t OOM 30 minutes in. See the memory user guide.
Smallest basis that answers the question. DZ before TZ before QZ. Run a basis-convergence sweep on a small representative system (5-10 minutes) before committing to a 12-hour QZ production run. Same for k-meshes (3×3×3 → 6×6×6 → 8×8×8) and DFT grids — each parameter has a noise floor, find it once per project.
Trusting a number¶
Converge each parameter separately, in order. Basis → integration grid → k-mesh → SCF threshold. Verify each is at the noise floor (energy change < 1 mEh per step-up) before trusting the next parameter up. Skip this and you have no idea which parameter your final number is sensitive to.
Cross-check against an established code. Vibe-qc’s molecular stack is validated against PySCF to machine precision in CI — you can do the same in your science. Pick a small variant of your real system, run it through both, and check agreement before extending vibe-qc to a regime where PySCF can’t follow (periodic, large-system, novel methods).
Sanity-check the symmetry. If you set up a high-symmetry system but the SCF result has a tiny dipole or a spurious splitting, your input geometry probably has noise in the last few decimals. Snap to the symmetry first, then run.
Long-running calculations¶
Always capture stdout to a logfile. SSH connections drop;
terminal scrollback is finite. tee keeps a copy on disk
alongside the run:
~/path/to/vibeqc/.venv/bin/python water.py 2>&1 | tee water.log
For anything over 10 minutes, detach. nohup is the
minimum:
nohup ~/path/to/vibeqc/.venv/bin/python water.py > water.log 2>&1 &
For multi-day runs use screen or tmux so you can
reattach and see the live output:
screen -dmS mycalc ~/path/to/vibeqc/.venv/bin/python water.py
screen -r mycalc # re-attach later
# Ctrl-A, D to detach again
See the running guide for the SSH workflow and the eventual JQ1 personal-job-queue story.
When SCF diverges¶
The first instinct is to crank tolerances. Almost always wrong. The right instinct is simplify until something converges, then add complexity back one step at a time:
Halve the basis (TZ → DZ, or DZ → STO-3G). If DZ converges and TZ doesn’t, you have a basis-set problem (linear dependency, diffuse-function instability) — try tighter canonical-orthogonalisation thresholds.
Simplify the system. Drop a substituent. Shrink the cell. Get something converging at all, then re-introduce complexity.
Try the SAD initial guess before the core-Hamiltonian guess. SAD (superposition of atomic densities) is much closer to the SCF solution for most molecules.
Bump
level_shiftto 0.4 Ha for small-gap systems — stabilises occ/vir ordering during early iterations.Switch on Fermi-Dirac smearing for metals or near-metallic systems.
smearing_temperature_K = 300is a reasonable default.Engage the quadratic SCF fallback (Phase C1c, v0.5+). Set
quadratic_fallback_iter = 20to switch from diagonalize-F to a Newton step in MO space after 20 stalled standard iterations. See the SCF convergence user guide.
The order matters: try (1)-(2) before (3)-(6). A method-side fix on a fundamentally pathological input just buries the problem.
Common gotchas¶
Symptom |
Likely cause |
Fix |
|---|---|---|
|
Ran the wrong Python (system, not venv) |
Use |
|
Ran from outside the repo with the relative-path shorthand |
Use the absolute path; the venv lives where you cloned. |
Wildly wrong energy (factor-of-2-off, or sign-flipped) |
Bohr vs Ångström unit confusion in coordinates |
Vibe-qc internals are bohr; pass Ångström via |
Wrong number of electrons |
Charge / multiplicity mismatch |
|
|
File is 0 bytes (disk filled during write) |
|
|
Vendored install missing or interrupted |
Re-run |
SCF energy oscillates between two values |
DIIS pathology on near-degenerate occ/vir |
Bump |
|
Local uncommitted changes |
Either commit + tag, or document the diff in your SI before publishing. |
Backups¶
The boring rule that everyone learns the hard way: back up
~/vibeqc-runs/ like you back up everything else. The
input scripts cost minutes to rewrite; the converged
calculations cost hours-to-days. Keep them.
A throwaway one-liner for nightly tarball + offsite rsync:
tar czf ~/backup/vibeqc-runs-$(date +%Y%m%d).tar.gz ~/vibeqc-runs
rsync -av ~/backup/ user@backup-host:~/vibeqc-backup/
Or use whatever your group’s existing backup story is — but have one.