ASE integration

ASE (Atomic Simulation Environment) is the de-facto standard Python toolbox for atomic-scale simulation: geometry optimization, molecular dynamics, vibrational analysis, NEB, basin hopping, structure I/O, periodic-cell handling. ASE’s Calculator interface is calculator-agnostic — anything that can return energy + forces (and optionally stress / dipole / Hessian / polarizability / magmoms) plugs in directly.

vibeqc.ase.VibeQC is vibe-qc’s ASE Calculator. With it, every ASE workflow that works for VASP / GPAW / ORCA / PySCF works for vibe-qc by changing one line:

atoms.calc = VibeQC(basis="6-31g*", functional="PBE")

This page is the reference for what the calculator exposes, how to use it, and which ASE workflows are validated against vibe-qc as of v0.5. For the cross-validation idiom (running the same calculation through vibe-qc + ORCA + PySCF in a single comparison table), see the cross-validation tutorial and the external codes setup guide.

Quickstart

from ase.build import molecule
from ase.optimize import BFGS
from vibeqc.ase import VibeQC

atoms = molecule("H2O")
atoms.calc = VibeQC(basis="6-31g*", functional="PBE")

BFGS(atoms).run(fmax=0.01)        # ASE's BFGS, vibe-qc forces
print(atoms.get_potential_energy())   # eV
print(atoms.get_dipole_moment())       # eÅ — Phase A (v0.5)

Supported methods

The driver is selected automatically from the calculator’s functional and the molecule’s multiplicity:

functional=

multiplicity

Driver

None (default)

1

RHF (run_rhf)

None

> 1

UHF (run_uhf)

set

1

RKS (run_rks) — closed-shell DFT

set

> 1

UKS (run_uks) — open-shell DFT

You don’t pass a method= argument — the calculator infers it. This keeps every RHF / UHF / RKS / UKS run on the same Molecule consistent (the multiplicity is the single source of truth).

# Closed-shell HF
VibeQC(basis="6-31g*")

# Open-shell HF — multiplicity comes from atoms.info["multiplicity"]
# or atoms.get_initial_magnetic_moments(); set explicitly via the
# `multiplicity=` kwarg to be unambiguous.
VibeQC(basis="6-31g*", multiplicity=2)

# Closed-shell DFT
VibeQC(basis="def2-tzvp", functional="B3LYP")

# Open-shell DFT
VibeQC(basis="def2-tzvp", functional="B3LYP", multiplicity=3)

Property surface

As of v0.5, VibeQC implements the following ASE properties. The ASE accessor (left column) triggers the calculator only on first call; subsequent calls return the cached result until the geometry or other input changes.

ASE accessor

Property

Method coverage

Notes

atoms.get_potential_energy()

energy (eV)

RHF / UHF / RKS / UKS

+ D3(BJ) when dispersion= set

atoms.get_potential_energy(force_consistent=True)

free_energy

All

Alias for energy (vibe-qc has no smearing entropy at the molecular SCF level).

atoms.get_forces()

forces (eV/Å)

All

Analytic nuclear gradients.

atoms.get_dipole_moment()

dipole (eÅ)

All

3-vector. Phase A (v0.5).

atoms.calc.get_property("hessian", atoms)

hessian (eV/Ų)

RHF / UHF / RKS analytic; UKS via FD

3N×3N matrix. RHF uses Phase 17b-3 CPHF; UHF uses Phase 17c per-spin CPHF; RKS uses 17c+; UKS falls back to compute_hessian_fd.

atoms.calc.get_property("polarizability", atoms)

polarizability (ų)

RHF only (CPHF, Phase 17b-1)

3×3 tensor. UHF / KS variants on the roadmap.

Properties not yet implemented (raise PropertyNotImplementedError):

  • magmoms — per-atom spin density. The data exists on the result (density_alpha density_beta) but vibe-qc has no exposed Mulliken-spin-population API yet; tracked as a tiny engineering ask.

  • stress (periodic) — gated on G2 (stress tensor, slated for v0.7).

  • Periodic energy / forces — gated on G1 (periodic gradients, slated for v0.6). The molecular calculator above does not yet drive run_*_periodic_scf from an ase.Atoms with pbc=True. Translate to a PeriodicSystem manually for periodic work; see tutorial 4.

Dispersion (D3-BJ)

Pass dispersion= to fold Grimme D3-BJ dispersion into the energy + forces:

# Use the SCF functional's D3-BJ parameters automatically.
atoms.calc = VibeQC(basis="6-31g*", functional="PBE",
                    dispersion="d3bj")

# Or specify a different functional's parameters explicitly:
atoms.calc = VibeQC(basis="6-31g*", functional="PBE",
                    dispersion="b3lyp")  # uses B3LYP's D3-BJ params

# Or pass a vibeqc.D3BJParams object directly for fine control.

The dispersion energy is logged at INFO level on the vibeqc.ase logger, and the per-component breakdown is exposed on the calculator’s results dict:

atoms.get_potential_energy()
print(atoms.calc.results["e_scf"])         # SCF energy in eV
print(atoms.calc.results["e_dispersion"])  # D3(BJ) correction in eV

See tutorial 14: dispersion for the full theory + variants.

Workflow examples

The examples/ase_workflows/ directory ships runnable scripts for the common ASE patterns, all using VibeQC as the backing calculator:

Workflow

Script

Demonstrates

Geometry optimization

optimize-via-ase-bfgs.py

ASE’s BFGS on H₂O / 6-31G* with energy + force convergence plot

Vibrational analysis

vibrations-via-ase-vibrations.py

ase.vibrations.Vibrations (FD on analytic gradients); asserts the three real frequencies fall in the HF/6-31G* band

NEB transition state

nh3-neb-via-ase.py

Climbing-image NEB on NH₃ umbrella inversion

NVE molecular dynamics

md-water-nve.py

Velocity-Verlet conservation diagnostic — drift < 10 meV / 25 fs is the force-energy consistency test

Surface adsorption setup

surface-h2-pt111-singlepoint.py

Pt(111) slab + H₂ atop placeholder; periodic forces work via Phase G1 (shipped in v0.6, pure-DFT today; HF/hybrid K-gradient deferred to v0.6.x); stress + slab builder still wait on G2 (v0.7)

Tip

For ASE’s standard tutorials, see the ASE workflow-tutorials parity matrix — most molecular tutorials work with vibe-qc as a one-line calculator swap; periodic atomic-force-driven workflows now work for pure-DFT via G1 (v0.6); stress-driven ones (EOS, lattice relaxation) wait on G2 (v0.7).

Cross-validation against external codes

The same VibeQC(...) object plugs into the :mod:vibeqc.benchmark framework alongside other ASE calculators — PySCF, ORCA, Psi4 — to run the same calculation across multiple codes and report a comparison table. This is the canonical way to validate a vibe-qc result against established QC software.

from ase.build import molecule
from vibeqc.ase import VibeQC
from vibeqc.benchmark import (
    PySCFCalculator, compare_calculators, make_orca_calculator,
)

atoms = molecule("H2O")
results = compare_calculators(
    atoms,
    [
        ("vibe-qc/RHF/6-31G*", VibeQC(basis="6-31g*")),
        ("PySCF/RHF/6-31G*",   PySCFCalculator(basis="6-31g*",
                                                method="RHF")),
        ("ORCA/RHF/6-31G*",    make_orca_calculator(
            orcasimpleinput="HF 6-31G* EnGrad")),
    ],
    properties=("energy", "forces", "dipole"),
)
results.print_table()
results.assert_agreement(tol={"energy": 1e-5, "forces": 1e-4})

Expected output for the H₂O / RHF / 6-31G* example (verified end- to-end, six-decimal agreement):

label              │ status │ wall_time_s │ energy          │ |F|max
────────────────────────────────────────────────────────────────────────
vibe-qc/RHF/6-31G* │ ok     │  0.04s      │ -2068.294643 eV │ 1.4934e+00
PySCF/RHF/6-31G*   │ ok     │  0.18s      │ -2068.294643 eV │ 1.4934e+00
ORCA/RHF/6-31G*    │ ok     │  0.72s      │ -2068.294643 eV │ 1.4934e+00

The framework auto-detects which calculators are available (via detect_calculators() / make_orca_calculator() / make_psi4_calculator()), so the same script runs cleanly with or without ORCA / Psi4 installed (missing rows show as “unavailable”). See:

  • cross-validation tutorial — full walkthrough on H₂O, NaCl, water-dimer dispersion

  • external codes user guide — installing ORCA / Psi4 alongside vibe-qc, including licensing realities and the conda Python-version caveat

  • examples/ase_compare/ — runnable scripts shipping with the repo

ASE option translation

The VibeQC calculator accepts both ASE-style kwargs and vibe-qc options objects. The ASE-style ones map to common defaults; the vibe-qc options structs let you reach every knob.

# Common cases via kwargs:
VibeQC(basis="def2-tzvp")                                # RHF defaults
VibeQC(basis="def2-tzvp", functional="PBE")              # RKS defaults
VibeQC(basis="def2-tzvp", functional="PBE",
       charge=-1, multiplicity=2)                        # OH⁻ doublet
VibeQC(basis="def2-tzvp", dispersion="d3bj",
       functional="PBE")                                  # PBE-D3(BJ)

# Full SCF-options control:
import vibeqc as vq
opts = vq.RHFOptions()
opts.max_iter = 200
opts.level_shift = 0.4
opts.diis_history = 20
VibeQC(basis="def2-tzvp", rhf_options=opts)

Available options structs: rhf_options=, uhf_options=, rks_options=, uks_options=. Pass whichever matches the method the calculator will dispatch to.

Logging

The ASE calculator logs progress on the vibeqc.ase logger. Configure as you would any Python logger:

import logging
logging.basicConfig(level=logging.INFO)
# Now atoms.get_potential_energy() prints:
#   INFO:vibeqc.ase:RHF / sto-3g  n_atoms=3  n_electrons=10  charge=0  mult=1
#   INFO:vibeqc.scf:SCF converged in 10 iterations  E = -75.984...

The underlying SCF trace logs on vibeqc.scf (per-iteration energy, gradient norm, DIIS error vector). Set level=logging.DEBUG for additional detail (per-iteration timing, integral cache hits).

Periodic calculations (current state)

ASE’s Atoms with pbc=True and a non-trivial cell flows through VibeQC for energy + forces as of v0.6 (Phase G1a-e shipped) for pure-DFT functionals (LDA, pure GGA). HF and hybrid functionals still take the K-gradient deferred to v0.6.x; stress waits on Phase G2 (v0.7). For everything else — band structures, DOS, custom k-meshes — build a vibeqc.PeriodicSystem directly:

import numpy as np
import vibeqc as vq

a = 4.5  # bohr
sysp = vq.PeriodicSystem(
    dim=3, lattice=np.eye(3) * a,
    unit_cell=[
        vq.Atom(3, [0.05, 0.05, 0.05]),     # Li
        vq.Atom(1, [0.55, 0.55, 0.55]),     # H
    ],
)
basis = vq.BasisSet(sysp.unit_cell_molecule(), "sto-3g")
opts = vq.PeriodicSCFOptions()
opts.lattice_opts.coulomb_method = vq.CoulombMethod.EWALD_3D

result = vq.run_rhf_periodic_gamma_scf(sysp, basis, opts,
                                        omega=0.5, spacing_bohr=0.5)

For full periodic-SCF details — Ewald summation, Coulomb-method dispatch, k-mesh sampling, Becke partition for DFT — see:

When VibeQCPeriodic ships, its API will mirror the molecular calculator but accept the Atoms.cell / Atoms.pbc directly:

# Forward-looking; not yet shipping:
atoms.calc = VibeQCPeriodic(basis="pob-tzvp", functional="PBE",
                             kmesh=[6, 6, 6])
BFGS(atoms).run(fmax=0.05)   # full periodic relaxation

Versions

VibeQC requires ase>=3.22. Install via the optional extra captured in pyproject.toml:

pip install '.[ase]'           # from the repo
pip install 'vibe-qc[ase]'     # once on PyPI

The cross-validation framework in :mod:vibeqc.benchmark is part of the base install (the PySCFCalculator shim imports lazily, so PySCF only loads when you actually use it).

See also