43 — XSF and BXSF: periodic volumetric visualisation¶
Visualise crystal structures, electron densities, Bloch orbitals, and Fermi surfaces with XCrySDen’s native formats — directly from vibe-qc.
You’ll learn: how to write XSF files for crystal structures, electron densities, and single Bloch orbitals; how to write BXSF files for Fermi-surface visualisation; and how to open them in moltui, VESTA, and XCrySDen.
Why: XSF is the de-facto format for periodic volumetric data. Unlike Gaussian cube — which has no concept of a lattice — XSF stores the unit-cell vectors and tells the viewer to tile the cell. The result is correct periodicity with exactly one set of voxels, no supercell hacks. BXSF extends this to reciprocal space so XCrySDen can render Fermi surfaces directly.
Prerequisites: a working vibe-qc install; optional moltui for terminal viewing.
# Install moltui as a vibe-qc extra:
.venv/bin/pip install '.[viewer]'
The three verbs: structure, volumetric, bands¶
vibe-qc ships four writers in the XSF family:
Writer |
What it writes |
Best viewer |
|---|---|---|
|
Crystal-only XSF (PRIMVEC + PRIMCOORD) |
VESTA, XCrySDen, moltui |
|
One Bloch orbital on the primitive cell |
VESTA, XCrySDen |
|
Total SCF density on the primitive cell |
VESTA, XCrySDen, moltui |
|
Band energies on a regular k-mesh (Fermi surface) |
XCrySDen, moltui |
The first three work in real space; the last one works in
reciprocal (k) space. All are callable as free functions from
vibeqc; write_xsf_density is also wired into
run_periodic_job(write_density=True) for one-line access.
Tip
For molecular volumetric output — cube files, molden, orbital visualisation — see tutorial 11 and the volumetric data user guide.
1. XSF crystal structure¶
The simplest XSF file carries only the crystal — lattice vectors and
atom positions — with no volumetric data. Useful as a quick visual
check on your PeriodicSystem before running the SCF.
import vibeqc as vq
import numpy as np
# NaCl rocksalt primitive cell.
a = 5.640 / 0.529177210903 # Å → bohr
system = vq.PeriodicSystem(
3,
[[a/2, a/2, 0], [a/2, 0, a/2], [0, a/2, a/2]], # fcc primitive
[vq.Atom(11, [0.0, 0.0, 0.0]), # Na at origin
vq.Atom(17, [a/2, a/2, a/2])], # Cl at body-centre
)
vq.write_xsf_structure("nacl_primitive.xsf", system)
# → nacl_primitive.xsf — open in VESTA, XCrySDen, or moltui
The file contains a CRYSTAL block with PRIMVEC (lattice
vectors in ångström) and PRIMCOORD (atom positions in ångström).
vibe-qc converts from bohr automatically; XSF’s native unit is
ångström.
For the one-call workflow, run_periodic_job writes a
{stem}.xsf alongside the other output files:
basis = vq.BasisSet(system.unit_cell_molecule(), "sto-3g")
vq.run_periodic_job(
system, basis,
method="RHF",
output="nacl",
write_xsf_structure_file=True, # default: True
write_density=False,
)
# → nacl.out, nacl.system, nacl.molden, nacl.xsf (.structure.xsf
# when write_density=True), nacl.POSCAR, nacl.cif
When write_density=True (see next section), the density lands
in {stem}.xsf and the structure-only XSF is routed to
{stem}.structure.xsf to avoid a suffix collision.
2. XSF density¶
Pass write_density=True to run_periodic_job and the job emits
a {stem}.xsf with the SCF total electron density sampled on the
primitive cell:
vq.run_periodic_job(
system, basis,
method="RHF",
output="nacl",
write_density=True,
density_spacing_bohr=0.2, # default
)
# → nacl.xsf — CRYSTAL block + DATAGRID_3D_density
The density ρ(r) = Σ D_{μν} χ_μ(r) χ_ν(r) is evaluated on a regular
fractional-coordinate grid spanning one primitive cell. The
BEGIN_DATAGRID_3D_density block holds the voxel values; VESTA
renders them as a scalar isosurface, moltui as a raycast isosurface.
When both write_density=True and write_xsf_structure_file=True,
the density lands in {stem}.xsf and the structure-only file is
routed to {stem}.structure.xsf to avoid a suffix collision.
For programmatic access, use the standalone writer:
from vibeqc import write_xsf_density
# After a converged SCF, D_real is the real-space density.
write_xsf_density("nacl_density.xsf", system, basis, result.density_real)
3. XSF Bloch orbitals¶
To visualise a single crystalline (Bloch) orbital —
ψ_{n,k}(r) — on the primitive cell, use write_xsf_mo. This is
the periodic counterpart of write_cube_mo for molecules.
# Assumes a Γ-only SCF converged above. At Γ the coefficients are
# real (up to an arbitrary sign) and k = (0, 0, 0).
homo = system.n_electrons() // 2 - 1
vq.write_xsf_mo(
"nacl_homo.xsf", system, basis,
np.asarray(result.mo_coeffs), # C(k) — at Γ this is real
np.array([0.0, 0.0, 0.0]), # k = Γ
band_index=homo,
spacing_bohr=0.2,
component="real", # Re ψ — the standard "MO picture"
)
write_xsf_mo constructs a primitive-cell grid, evaluates the Bloch
orbital
at every voxel, and writes the result as a DATAGRID_3D block.
The component= keyword selects what scalar to write:
Component |
Scalar |
Use when… |
|---|---|---|
|
Re ψ |
Orbitals at Γ or time-reversal-invariant k (standard MO picture) |
|
Im ψ |
Gauge-paired view at general k |
|
|ψ| |
Gauge-invariant magnitude |
|
|ψ|² |
Cross-k comparison; no phase ambiguity |
For a full walk-through of Bloch-orbital evaluation — the
evaluate_bloch_orbital kernel, the PrimitiveCellGrid factory,
the cube-file workaround for molecular viewers, and the gauge-
invariance considerations — see
tutorial 22: periodic orbital cubes.
4. BXSF Fermi surfaces¶
BXSF (“band XSF”) stores band energies sampled on a regular 3D k-mesh across the Brillouin zone. XCrySDen / moltui render an isosurface at the Fermi energy — the Fermi surface — showing which bands cross the Fermi level and where in k-space.
What you need¶
write_bxsf expects:
A
PeriodicSystem(for the reciprocal-lattice vectors),A 4D
energiesarray of shape(n_kx, n_ky, n_kz, n_bands)in Hartree (the writer converts to eV automatically),An optional
e_fermiin Hartree.
The k-mesh must be a regular fractional-coordinate grid over
[0, 1) along each reciprocal-lattice direction — exactly what
np.linspace(0, 1, nk, endpoint=False) produces.
End-to-end: NaCl rocksalt at pob-TZVP¶
This example runs a multi-k Hcore diagonalisation on a dense k-mesh and writes a BXSF file. NaCl is a wide-gap insulator, so no bands cross the Fermi level — the resulting “Fermi surface” will be empty. The workflow is identical for metals; just substitute a metallic system (e.g. an H chain at half-filling from tutorial 4) and use SCF-converged Fock matrices for production-quality surfaces.
import numpy as np
import vibeqc as vq
from vibeqc import (
Atom, BasisSet, PeriodicSystem, LatticeSumOptions,
compute_overlap_lattice, compute_kinetic_lattice,
compute_nuclear_lattice, bloch_sum, diagonalize_bloch,
write_bxsf,
)
# ---- 1. Build NaCl rocksalt -----------------------------------------
a = 5.640 / 0.529177210903 # Å → bohr
system = vq.PeriodicSystem(
3,
[[a/2, a/2, 0], [a/2, 0, a/2], [0, a/2, a/2]],
[Atom(11, [0.0, 0.0, 0.0]),
Atom(17, [a/2, a/2, a/2])],
)
basis = BasisSet(system.unit_cell_molecule(), "pob-tzvp")
# ---- 2. Build real-space lattice matrices (Hcore) -------------------
opts = LatticeSumOptions()
opts.cutoff_bohr = 12.0
opts.nuclear_cutoff_bohr = 25.0
S_lat = compute_overlap_lattice(basis, system, opts)
T_lat = compute_kinetic_lattice(basis, system, opts)
V_lat = compute_nuclear_lattice(basis, system, opts)
# ---- 3. Run a multi-k SCF for the Fermi energy ----------------------
# (For a production-quality BXSF you'd use the converged Fock
# matrices here; this example uses Hcore to keep the recipe short.)
km_scf = vq.KPoints.monkhorst_pack(system, [4, 4, 4], symmetry=True)
opts_scf = vq.PeriodicSCFOptions()
opts_scf.lattice_opts.cutoff_bohr = 12.0
opts_scf.conv_tol_energy = 1e-6
result = vq.run_rhf_periodic_scf(system, basis, km_scf, opts_scf)
# Fermi energy: midpoint between the HOMO maximum and LUMO minimum
# across all k-points in the SCF mesh.
n_occ = system.n_electrons() // 2
homo = max(float(eps[n_occ - 1]) for eps in result.mo_energies)
lumo = min(float(eps[n_occ]) for eps in result.mo_energies)
e_fermi = (homo + lumo) / 2.0
# ---- 4. Sample band energies on a dense regular k-mesh --------------
nk = 16 # 16×16×16 = 4096 k-points
kx = np.linspace(0, 1, nk, endpoint=False)
ky = np.linspace(0, 1, nk, endpoint=False)
kz = np.linspace(0, 1, nk, endpoint=False)
KX, KY, KZ = np.meshgrid(kx, ky, kz, indexing="ij")
n_bands = basis.nbasis
energies = np.zeros((nk, nk, nk, n_bands))
for i in range(nk):
for j in range(nk):
for k in range(nk):
# Convert fractional k → Cartesian k (bohr⁻¹).
rec_lat = system.reciprocal_lattice() # (3, 3), columns = b_i
k_cart = KX[i, j, k] * rec_lat[:, 0] \
+ KY[i, j, k] * rec_lat[:, 1] \
+ KZ[i, j, k] * rec_lat[:, 2]
H_k = bloch_sum(T_lat, k_cart) + bloch_sum(V_lat, k_cart)
S_k = bloch_sum(S_lat, k_cart)
sol = diagonalize_bloch(H_k, S_k)
energies[i, j, k, :] = sol.eigenvalues
# ---- 5. Write BXSF --------------------------------------------------
write_bxsf("nacl_bands.bxsf", system, energies, e_fermi=e_fermi)
# → nacl_bands.bxsf — open in XCrySDen or moltui
The loop evaluates H(k) = T(k) + V(k) at every k-point of the dense 16³ mesh and diagonalises. For a converged SCF, replace the Hcore matrices with the real-space Fock lattice set from the SCF result; the sampling loop is identical.
Tip
For metals, the Fermi surface is non-empty. Substitute a metallic system — an H₂ chain at half-filling, or bulk Al — and the BXSF file will contain one or more bands that cross E_F. XCrySDen / moltui will render a surface where those bands equal E_F, exactly the textbook Fermi-surface picture.
Performance notes¶
A 16³ dense mesh diagonalising n_bands at each k-point takes O(nk³ · n_bands³) operations. For pob-TZVP on NaCl (42 basis functions, 2 atoms), a 16³ mesh completes in under a minute on a laptop.
File sizes scale as nk³ × n_bands × 6 bytes (ASCII float). 16³ × 42 bands ≈ 11 MB.
For production Fermi surfaces, 32³ or 48³ meshes are common; file sizes reach 100–300 MB.
5. Viewing in moltui¶
moltui reads XSF and BXSF natively. Launch it on any of the files written above:
moltui nacl_primitive.xsf # crystal structure
moltui nacl.xsf # density isosurface
moltui nacl_homo.xsf # Bloch orbital isosurface
moltui nacl_bands.bxsf # Fermi surface / band isosurfaces
Keyboard controls (from the geometry / isosurface view):
Key |
Action |
|---|---|
|
Rotate the structure |
|
Zoom in / out |
|
Toggle periodic replication (XSF only) |
|
Cycle view mode (geometry / orbitals / normal modes) |
|
Toggle isosurface visibility |
|
Navigate grids / bands (when multiple are present) |
|
Cycle through bands (BXSF) |
|
Decrease / increase isovalue |
|
Open visual panel (isovalue, style, lighting) |
|
Export PNG screenshot |
|
Quit |
Tip
Periodic replication (key b) tiles the primitive cell across
the display. This is particularly useful for XSF structure and
density files — you see the full crystal, not just one cell. The
replication respects the lattice vectors, so off-diagonal cells
(monoclinic, triclinic) tile correctly.
For more on moltui — including molecular-orbital viewing, normal-mode animation, and trajectory playback — see tutorial 27: viewing with moltui.
6. Viewing in VESTA / XCrySDen¶
VESTA¶
VESTA (free, cross-platform GUI) reads XSF files directly:
# macOS:
open -a VESTA nacl.xsf
# Linux:
vesta nacl.xsf
Structure: File → Open → select the
.xsf. VESTA displays atoms + bonds with the unit-cell wireframe. Right-click → Style to switch to polyhedral / ball-and-stick / space-filling.Volumetric data: VESTA automatically detects the
DATAGRID_3Dblock. Edit → Volumetric Data → adjust the isosurface level and colour map.Periodic replication: Objects → Boundary → set the number of replicated cells along each axis.
XCrySDen¶
XCrySDen (free, Linux/macOS) is the reference viewer for the format:
# XSF structure + volumetric data:
xcrysden --xsf nacl.xsf
# BXSF Fermi surface:
xcrysden --bxsf nacl_bands.bxsf
XCrySDen’s --bxsf mode opens the Brillouin-zone wireframe and
renders an isosurface for every band that crosses the Fermi level.
The wireframe is the reciprocal-lattice bounding box; the surfaces
inside it are the Fermi sheets. Use Tools → k-path to overlay
a band-structure path on the same display.
Tip
moltui vs VESTA vs XCrySDen. moltui is the terminal-native triage tool — fast, runs over SSH, no GUI. VESTA is the standard crystallographic viewer — publication-quality figures, polyhedral rendering, diffraction patterns. XCrySDen is the specialist for Fermi surfaces and band-structure overlay; its BXSF reader is the reference implementation. Pick the right viewer for the job.
Theory¶
XSF format conventions¶
The XSF format (Kokalj 2003) was designed for periodic crystals.
A file consists of a CRYSTAL block containing:
PRIMVEC — three row vectors (in ångström) defining the primitive-cell lattice. vibe-qc converts from its internal bohr representation at write time.
PRIMCOORD — atom count, a format flag (
1for fractional coordinates), and one line per atom (Z x y zin ångström).DATAGRID_3D_
(optional) — a 3D scalar grid spanning a chosen real-space volume. The volume is specified by an origin(ångström) and threespanvectors (ångström); for a primitive-cell density these are the lattice vectors. Voxel(0, 0, 0)is at the origin; voxel(n_a − 1, n_b − 1, n_c − 1)is one step short of the opposite corner. The voxel at the far corner — which coincides with the periodic image of(0, 0, 0)— is not stored. XSF / VESTA treat it as implicit, and including it introduces a spurious stripe at every cell boundary (the “off-by-one trap”).
The grid data runs with the first index varying fastest, then the
second, then the third — the Fortran-order convention that vibe-qc
honours via np.transpose(data, (2, 1, 0)).ravel().
BXSF format conventions¶
BXSF (“band XSF”) uses the same block-structured syntax but in reciprocal space:
The spanning vectors are the reciprocal-lattice vectors \(\mathbf{b}_1, \mathbf{b}_2, \mathbf{b}_3\) in 1/Å.
The origin is \((0, 0, 0)\) — the Γ point.
Band energies are in eV (vibe-qc converts from Hartree internally using 1 Ha = 27.211386 eV).
Each band is one
BAND:block; the band index counts from 1.The
Fermi Energyin theBEGIN_INFOblock is in eV.
XCrySDen renders the band structure as \(N_\text{bands}\) isosurfaces inside the BZ wireframe, one per band, at the Fermi energy. Bands that never reach \(E_F\) produce no visible isosurface; bands that cross \(E_F\) produce Fermi sheets — the textbook Fermi surface.
Real-space grids (XSF) vs k-space grids (BXSF)¶
XSF |
BXSF |
|
|---|---|---|
Domain |
Real space \(\mathbf{r} \in\) unit cell |
Reciprocal space \(\mathbf{k} \in\) BZ |
Grid |
Primitive-cell fractional coordinates |
Monkhorst–Pack fractional coordinates |
Scalar |
ρ(r), ψ_{n,k}(r), or any f(r) |
ε_n(k) — band energy |
Isosurface meaning |
Shape of the density / orbital |
Shape of the Fermi surface |
Span vectors |
Lattice \(\mathbf{a}_1, \mathbf{a}_2, \mathbf{a}_3\) |
Reciprocal lattice \(\mathbf{b}_1, \mathbf{b}_2, \mathbf{b}_3\) |
Units |
Å for coordinates, a.u. for data |
1/Å for reciprocal vectors, eV for energies |
The real-space grid tells you where the electrons are; the k-space grid tells you which electrons cross the Fermi level and at what crystal momentum. Together they give the complete one-electron picture of a periodic system.
Fermi surfaces and the BZ¶
A Fermi surface is the set of k-points where
for some band \(n\). In an insulator, no band satisfies this condition — the occupied bands all lie below \(E_F\) and the virtual bands all lie above it, so the BXSF isosurface is empty. In a metal, at least one band crosses \(E_F\), and the resulting isosurface encloses the occupied region of k-space — the Fermi sea. The shape of the Fermi surface determines transport properties (conductivity, thermopower, magnetoresistance) and is the central object of metallic electronic structure.
The mesh density nk controls the smoothness of the isosurface.
For publication-quality figures, 32³ or denser is recommended;
16³ is enough for orientation and triage.
References¶
Kokalj, A. “Computer graphics and graphical user interfaces as tools in simulations of matter at the atomic scale,” Comput. Mater. Sci. 28, 155 (2003). The XSF format specification. Updated at http://www.xcrysden.org/doc/XSF.html.
Bloch, F. “Über die Quantenmechanik der Elektronen in Kristallgittern,” Z. Phys. 52, 555 (1928). Bloch’s theorem — the foundation of crystalline orbitals and band theory.
Ashcroft, N. W.; Mermin, N. D. Solid State Physics. Holt, Rinehart and Winston (1976), Chapters 8–10 and 15. The standard textbook on Bloch states, band structures, and Fermi surfaces. Chapter 15 is the canonical introduction to Fermi-surface topology and measurement.
Next¶
Tutorial 22: Periodic orbital cubes — the
write_xsf_modeep-dive: gauge invariance, component selection, and theevaluate_bloch_orbitalkernel.Tutorial 27: Viewing with moltui — full moltui reference: geometries, molecular orbitals, normal modes, and trajectory animations in the terminal.
Volumetric data user guide — the complete reference for cube, XSF, and BXSF writers, including molecular cube files and the
run_periodic_jobintegration.Tutorial 4: Periodic HF — the foundation for periodic calculations, including k-mesh setup and the Monkhorst–Pack scheme.
Tutorial 12: Band structure — the energy-vs-kpath context for the Fermi-surface view BXSF provides.