Tutorial 27: Viewing geometries, orbitals, and vibrations with MolTUI¶
You’ll learn: how to inspect vibe-qc output files — geometries, molecular orbitals, normal modes — directly in the terminal, without firing up Avogadro / Jmol / VMD.
Why: when you’re SSH’d into a remote machine, iterating quickly on a screen full of input files, or just allergic to GUI context-switches, MolTUI is the right tool. It renders 3D structures inline using Unicode block characters; you navigate with the keyboard.
Prerequisites: vibe-qc installed in a venv, plus MolTUI on top. Two install paths:
# As a vibe-qc extra (single command, captures the dependency):
.venv/bin/pip install '.[viewer]'
# Or via the interactive helper:
./scripts/install_optional_tools.sh
After install, moltui is on the venv’s PATH:
.venv/bin/moltui --version
What MolTUI reads¶
Format |
What it shows |
Where vibe-qc writes it |
|---|---|---|
|
Geometry (single frame) |
|
|
Geometry + MOs + normal modes |
|
|
Volumetric data (orbitals, density) |
|
|
Geometry + MOs + normal modes (Gaussian fchk) |
(not yet emitted by vibe-qc) |
|
ORCA’s native binary (needs |
(not emitted by vibe-qc; ORCA-specific) |
|
ORCA’s normal-modes file |
✅ |
Multi-frame |
Trajectory animations |
✅ |
|
Geometry-optimization history (multi-XYZ + per-step E) |
✅ |
The two formats vibe-qc writes today out of the box are
.molden (full molecular-orbital data, plus geometry, plus
normal modes if a Hessian was computed) and .cube (volumetric
data for individual orbitals or the total density). Both work with
MolTUI directly.
Step 1: Generate the files¶
from vibeqc import Atom, Molecule, run_job
mol = Molecule([
Atom(8, [0.0, 0.00, 0.00]),
Atom(1, [0.0, 1.43, -0.98]),
Atom(1, [0.0, -1.43, -0.98]),
])
run_job(
mol,
basis="6-31g*",
method="rks",
functional="PBE",
output="water",
)
# → water.out, water.molden
That gives you water.molden with the geometry + MOs + occupations.
Add a cube for density / orbital visualization:
import numpy as np
import vibeqc as vq
basis = vq.BasisSet(mol, "6-31g*")
result = vq.run_rks(mol, basis, vq.RKSOptions(functional="PBE"))
# HOMO orbital cube
homo = mol.n_electrons() // 2 - 1
vq.write_cube_mo("water-homo.cube",
np.asarray(result.mo_coeffs), homo, basis, mol,
spacing=0.2, padding=3.0)
# Total density cube
vq.write_cube_density("water-density.cube",
np.asarray(result.density), basis, mol,
spacing=0.2, padding=3.0)
Step 2: View geometries¶
Launch MolTUI on the .molden:
moltui water.molden
The default view shows the molecular geometry rendered inline:
┌──────── water.molden ────────┐
│ │
│ o │
│ │
│ h h │
│ │
│ │
└──────────────────────────────┘
[g] geometry [o] orbitals [v] vibrations
(rendering varies by terminal size + Unicode font; on a typical full-screen 100×40 character terminal you get a recognisable 3D structure)
Keyboard navigation (defaults — see moltui --help for the
authoritative list):
Key |
Action |
|---|---|
arrow keys / |
Rotate the structure |
|
Zoom in / out |
|
Switch to geometry view |
|
Switch to orbitals view (.molden / .cube / .fchk) |
|
Switch to vibrations view (.molden / .hess / .fchk) |
|
Cycle frame / orbital / mode |
|
Quit |
Step 3: View orbitals¶
From the geometry view, press o to switch to orbitals. MolTUI
parses the molden file’s MO-coefficient block and renders an
isosurface inline:
┌───── HOMO ε = -6.09 eV occ = 2 ─────┐
│ │
│ ▓▒░ ░▒▓ │
│ ▓▒░ ░▒▓ │
│ ▓▒░ o ░▒▓ │
│ ▒░ ░▒ │
│ h h │
│ │
│ │
└─────────────────────────────────────────┘
[Tab]/[Shift-Tab] cycle MO [+/-] zoom [q] quit
Light shading is the positive isosurface lobe, darker is the negative. The HOMO of water is the in-plane oxygen lone pair — two lobes opposite the H atoms.
Tab cycles through the MOs; the header shows the eigenvalue
and the occupation. Useful for sanity-checking that:
The HOMO has the right symmetry for your molecule (lone pairs, π systems, etc.).
The LUMO ordering is what you expect (no surprise σ* / Rydberg crossings).
The orbital energies (header) match what’s in the
.outfile’s orbital table.
For finer-grained orbital views — explicit isovalue control,
publication-quality renderings — open the .molden or one of the
cube files in Avogadro / Jmol / VMD instead. MolTUI is for
quick triage, not for paper figures.
Step 4: View cube files (volumetric data)¶
For more detailed per-orbital control:
moltui water-homo.cube
moltui water-density.cube
Cube files are dense volumetric grids — MolTUI raycasts an isosurface at a default isovalue (usually 0.05 a.u.). Adjust with:
[i]/[I] decrease/increase isovalue
[r] reset isovalue to default
A density cube (sum over occupied orbitals) shows the total
electron density — the “shape” of the molecule. A single-MO
cube shows one orbital at a time (HOMO, LUMO, anything you
asked write_cube_mo to output).
Step 5: View vibrational modes¶
If your .molden has a [FREQ] and [FR-COORD] block (which
vibe-qc emits when you compute a Hessian), MolTUI renders the
normal modes as animated displacement vectors. Press v from
any view to switch.
┌── mode 7 ω = 1751.3 cm⁻¹ IR = 67.8 km/mol ──┐
│ │
│ ↑ o ↑ │
│ ↑ ↑ │
│ h h │
│ (symmetric stretch) │
│ │
└─────────────────────────────────────────────────┘
[Tab]/[Shift-Tab] cycle mode [Space] play/pause
The arrows (or oscillating atoms, depending on the terminal + animation toggle) show the eigenvector of that normal mode. For water you’ll see three real modes — bend, symmetric stretch, antisymmetric stretch — and six near-zero translation/rotation modes that MolTUI typically hides.
To produce a .molden with vibrational data:
import vibeqc as vq
# Compute analytic Hessian + frequencies
mol = vq.Molecule.from_xyz("water-equilibrium.xyz")
basis = vq.BasisSet(mol, "6-31g*")
result = vq.run_rhf(mol, basis)
hess = vq.compute_hessian_rhf_analytic(
mol, basis, result, basis_name="6-31g*",
)
# Write a molden file with the normal modes attached
vq.write_molden("water-with-freqs.molden", mol, basis, result,
hessian=hess)
Tip
Direct ORCA-format .hess export is available via
:func:vibeqc.write_orca_hess (Phase M1). Useful when you’d
rather inspect just the vibrational data than parse the full
molden bundle — and it’s the format moltui reads natively for
ORCA-style normal-modes display:
hess = vq.compute_hessian_rhf_analytic(mol, basis, result, basis_name="6-31g*")
vq.write_orca_hess("water.hess", mol, hess)
# then: moltui water.hess
The same ASCII format means cross-code visual diff’ing works:
moltui vibeqc.hess and moltui orca.hess should show
identical modes when the underlying CPHF kernels agree.
See tutorial 26 § cross-validation: vibrations
for the cross-validation script that asserts ~0.5 cm⁻¹ agreement
on H₂O.
Tips and tricks¶
Over SSH: MolTUI runs entirely in your terminal — no X11 forwarding, no GUI windows. Just
sshin,cdto your output directory, andmoltui ....Quick triage: for a calculation that just finished, look at the
.outfile first (text — energy, convergence, warnings), thenmoltui ...moldento spot-check the geometryHOMO shape. Catches “the SCF converged to the wrong state” bugs immediately.
Animations (Phase M2): vibe-qc writes multi-XYZ trajectories natively — geometry-optimization paths, NEB images, normal-mode movies, MD snapshots — and moltui animates them frame-by-frame.
Optimization history → moltui movie:
import vibeqc as vq geoms, energies, rms_grads = run_my_optimization(mol) vq.write_opt_trajectory("h2o.opt", geoms, energies, rms_grad=rms_grads)
moltui h2o.opt # animated optimisation path with per-step E in the # comment line — Tab cycles iterates
Normal-mode movie — paired helper builds the displaced frames for one vibrational mode:
hess = vq.compute_hessian_rhf_analytic(mol, basis, result, basis_name="6-31g*") frames = vq.normal_mode_trajectory(mol, hess, mode_index=8, amplitude=0.5, n_frames=20) vq.write_xyz_trajectory("h2o-asym-stretch.xyz", frames)
moltui h2o-asym-stretch.xyz # animated symmetric-stretch movie
Falling back via ASE — convert any ASE
.trajto multi-XYZ — also still works:ase convert output-h2o-opt.traj output-h2o-opt.xyz moltui output-h2o-opt.xyz
Keep
moltuiopen in a separate terminal while you iterate on input files; reload by re-runningmoltui <file>after each calculation finishes.
Crystalline orbitals: not yet¶
MolTUI is a molecular viewer. Its supported formats —
.xyz, .molden, .cube, .fchk, .gbw, .hess —
were all designed for finite molecular systems with no periodic
boundary conditions. Specifically:
.molden has no concept of a unit cell. The format predates most periodic-DFT implementations and there’s no widely-agreed extension for periodic data.
.cube can in principle carry periodic data (the header has a “voxel grid + origin” format that maps onto a periodic cell), but most viewers — MolTUI included — don’t replicate the cell or render the lattice vectors. You’d see the unit-cell content as a free-floating cluster.
Bloch states ψ_{n,k}(r) are k-labeled and generally complex-valued (ψ = u_k(r) e^{i k·r}); MolTUI has no k-point-aware navigation.
So periodic / crystalline orbitals don’t render correctly in MolTUI today. This is a real gap — for a quick triage of a periodic SCF, you’d want the same “ssh in, look at orbital, move on” workflow that MolTUI gives molecular calculations.
What works today for periodic / crystalline visualization:
Vibe-qc’s plot scripts —
examples/plots/h-chain-bands-dos.py,examples/plots/lih-chain-bloch-density.py,examples/plots/h-chain-crystalline-orbitals.py— produce matplotlib PNGs of bands, DOS, Bloch densities, and crystalline-orbital contour plots. Run from the repo, viewable in any image viewer.VESTA (free, GUI) — designed for crystals; reads cube files with periodic-cell metadata and replicates the lattice correctly.
XCrySDen (free, GUI) — direct support for k-resolved crystalline orbitals; bands + DOS + isosurfaces.
VMD with
pbctools— script the cell replication; not as smooth as VESTA but works.
Ask the upstream: a feature request to MolTUI for periodic-cell awareness (read lattice vectors from CUBE / future periodic formats; render unit-cell box; replicate atoms across PBC) would unlock the same triage workflow for crystals. Track at github.com/kszenes/moltui.
When MolTUI isn’t enough¶
MolTUI is a triage tool — fast, terminal-native, low-resolution. For:
Publication figures — Avogadro 2 (cross-platform GUI), VMD (scriptable, scientific-grade), ChimeraX (high-quality surfaces), Jmol (web-friendly).
Detailed orbital analysis — natural-bond-orbital decompositions, ELF/NCI plots, charge-transfer maps — use Multiwfn (free, Windows/Wine on macOS+Linux) or pymol.
Crystal structures — VESTA (free), pymatgen visualizations.
vibe-qc writes formats every one of these tools reads (.molden,
.cube, .xyz, future .gbw/.hess); pick the right viewer for
the job. MolTUI’s superpower is no GUI, no setup, runs over
SSH — that’s the niche.
See also¶
installation: optional terminal viewer — install paths
output files user guide — what vibe-qc writes
tutorial 11: orbital visualization — generating cube files for visualization
tutorial 9: vibrational frequencies — generating Hessians for normal-mode analysis
MolTUI repo — authoritative docs for keybindings + supported formats