Slabs and adsorbates¶
vibeqc.build is the native (no-ASE) helper for surface-catalysis
inputs. It generates :class:PeriodicSystem objects with a vacuum gap
along the third lattice vector — the standard layout for slab + vacuum
calculations.
The submodule is deliberately small: closed-form surface unit cells for
the most common low-index facets of fcc, bcc, and hcp metals. For
exotic facets or non-metals, build a PeriodicSystem directly from
coordinates (see crystal_lattices).
A 5-layer Fe(100) slab with N₂ adsorbed¶
import vibeqc as vq
# 5-layer Fe(100), 2×2 lateral cell, 12 Å vacuum. The default lattice
# constant (2.866 Å) comes from vq.BULK_LATTICE_CONSTANTS; override
# with a=...
slab_sys, info = vq.slab(
"Fe", facet=(1, 0, 0), n_layers=5,
vacuum=12.0, supercell=(2, 2),
multiplicity=21, # 5×4 = 20 atoms × 4 unpaired e-/Fe ≈ FM start guess
)
# Side-on N₂ at a bridge site, 1.9 Å above the top Fe layer.
slab_with_n2 = vq.place_adsorbate(
slab_sys, "N2", info=info,
site="bridge", orientation="side-on", height=1.9,
bond_length=1.10,
)
The info object (a :class:SlabInfo) carries the per-atom layer
index, which feeds the frozen-substrate relaxation pattern below.
Built-in adsorbate library¶
vq.build_molecule(name, bond_length=...) returns a small library of
common adsorbate geometries (in Å):
Name |
Atoms |
Default geometry |
|---|---|---|
|
1–2 |
linear along z |
|
2 |
linear along z |
|
3–5 |
gas-phase neutral geometry |
For anything else, pass an explicit ((symbol, (x, y, z)), …) sequence
in Å.
Adsorption sites¶
place_adsorbate(slab, ads, site=..., info=info) resolves named sites
against the primitive surface cell:
Site |
fcc(100), bcc(100) |
fcc(111), hcp(0001) |
bcc(110) |
|---|---|---|---|
|
atop a metal |
atop a metal |
atop a metal |
|
2-fold bridge |
2-fold bridge |
short bridge |
|
4-fold hollow |
(degenerate w/ fcc/hcp) |
quasi-3-fold |
|
— |
hcp(110) absent, fcc-only |
— |
|
— |
offset 3-fold hollow |
— |
For full control, pass position=(x, y) in Å (relative to the lattice
origin) instead of site=.
Frozen-substrate relaxation¶
The standard pattern: freeze the bottom layers and relax only the top
layers + adsorbate. :func:vibeqc.relax_atoms takes a
freeze_indices= argument; :meth:SlabInfo.bottom_layer_indices
produces the list:
freeze = info.bottom_layer_indices(3) # bottom 3 of 5 layers
opt = vq.relax_atoms(
slab_with_n2,
basis_name="sto-3g",
kmesh=vq.monkhorst_pack(slab_with_n2, [2, 2, 1]),
method="UKS",
functional="pbe",
freeze_indices=freeze,
)
Internally the optimizer pins the fractional coordinates of the frozen
atoms via L-BFGS-B box bounds and zeros their gradient components, so
the reported |grad| reflects only the free degrees of freedom.
Animate the relaxation in vibe-view¶
Pass output_trajectory="stem" and relax_atoms writes a
vibe-view-renderable QVF archive on exit — one frame per accepted
L-BFGS-B step, with the initial geometry as frame 0 and the
converged geometry as the last frame:
opt = vq.relax_atoms(
slab_with_n2,
basis_name="sto-3g",
kmesh=vq.monkhorst_pack(slab_with_n2, [2, 2, 1]),
method="UKS",
functional="pbe",
freeze_indices=freeze,
output_trajectory="slab_n2_relax", # → slab_n2_relax.qvf
)
The archive ships as QVF v2 (per-frame lattice + dim) for any
PeriodicSystem input, so vibe-view’s renderer (per the
“Periodic reaction paths (QVF v2)” section)
draws the unit cell and wraps atoms across in-plane periodic
boundaries automatically. Default output_trajectory=None is a
no-op — no per-step capture overhead.
Open-shell + multi-k for metallic slabs¶
Periodic UKS / UHF dispatches via the BIPOLE J/K backend. Metallic slabs
typically need a finite k-mesh in the surface plane — pass kpoints= to
:func:vibeqc.run_periodic_job:
vq.run_periodic_job(
slab_with_n2,
basis=vq.BasisSet(slab_with_n2.unit_cell_molecule(), "sto-3g"),
method="UKS",
functional="pbe",
jk_method="bipole", # required for UHF / UKS today
kpoints=[4, 4, 1], # k-mesh in surface plane, 1 along z
)
The GDF path is RHF/RKS-only at present; selecting jk_method="gdf"
together with method="UKS" raises NotImplementedError and steers
you to jk_method="bipole".
Dispersion correction for periodic systems¶
vq.compute_d3bj_periodic returns the D3-BJ dispersion energy per
unit cell. Two backends:
backend="dftd3"(recommended) — Grimme’s reference Fortran library via the optionaldftd3Python package. Bit-exact periodic D3-BJ. Install withpip install dftd3orpip install -e '.[dispersion]'.backend="builtin"— vibe-qc’s native C++ D3-BJ on a supercell expansion of the unit cell. Approximate (CN values near the supercell boundary are wrong); the per-cell energy converges with larger supercell. Use for elements not yet covered by the dftd3 install or to stay external-dependency-free.backend="auto"(default) — picks dftd3 when available.
Standalone call:
import vibeqc as vq
slab_sys, info = vq.slab("Fe", facet=(1, 0, 0), n_layers=5, vacuum=12.0,
supercell=(2, 2), multiplicity=21)
res = vq.compute_d3bj_periodic(slab_sys, "pbe", with_gradient=True)
print(res.energy, "Ha per cell;", res.backend, res.supercell)
Or inline with run_periodic_job via the new dispersion= keyword:
vq.run_periodic_job(
slab_with_n2,
basis=vq.BasisSet(slab_with_n2.unit_cell_molecule(), "sto-3g"),
method="UKS",
functional="pbe",
jk_method="bipole",
kpoints=[4, 4, 1],
dispersion="pbe", # or True (uses the current functional)
dispersion_backend="auto",
)
The dispersion piece is logged as a separate block in the .out
file and the SCF result is wrapped with a _DispersionAugmented
proxy that exposes .energy_total = E_SCF + E_disp (the bare
.energy is unchanged).
For a slab with vacuum, the cutoff is naturally bounded along the
vacuum direction — pass the slab PeriodicSystem unchanged. The
dftd3 backend’s periodic=[True, True, True] flag handles it
correctly.
What’s not here yet¶
The following surface-catalysis features are tracked in
docs/roadmap.md but not yet shipped:
Relaxed bond-length / angle scans on the surface.
Nudged elastic band (NEB / CI-NEB).
Hubbard-U correction (
+U) for strongly correlated d/f electrons.Periodic D4 (the molecular D4 path already exists; periodic generalisation pending).
If you need any of these urgently, file an issue.