QVF consumer reference: reading a .qvf file in Python¶
The manifest shape matches the vibe-view consumer (vibe-view/src/vibeview/qvf.py).
Important
Viewer support tracks writer support, with one rule. vibe-view
renders every implemented writer kind it has a renderer for
(see vibe-view/src/vibeview/kinds.py::SUPPORTED_KINDS). Unknown
or vendor-namespace (x_<vendor>.*) sections, and any reserved
kinds not yet wired into a renderer, are classified as “skipped,
unsupported” by the viewer and do not prevent the archive from
opening (§ 2.5 Rule 2 of design_qvf_format.md). The full
writer / viewer matrix is in the content-kinds table of the
design doc.
Use the manifest as the single source of truth for “what is in this archive”. The viewer’s status panel reports which of those sections are actually being rendered in the current session.
import zipfile, json, hashlib
import numpy as np
# ── Open ───────────────────────────────────────────────────────────────
path = "h2o.qvf"
zf = zipfile.ZipFile(path, "r")
manifest = json.loads(zf.read("manifest.json"))
print(f"QVF v{manifest['qvf_version']} from {manifest['source']['program']}")
# ── Verify sha256 of every member ──────────────────────────────────────
for section in manifest["sections"]:
for _key, member in section.get("members", {}).items():
sha = member.get("sha256")
if sha is not None:
got = hashlib.sha256(zf.read(member["path"])).hexdigest()
assert got == sha, f"sha256 mismatch for {member['path']}"
# ── Structure → atoms ──────────────────────────────────────────────────
for s in manifest["sections"]:
if s["kind"] == "structure":
struct = json.loads(zf.read(s["members"]["structure"]["path"]))
for a in struct["atoms"]:
print(f" {a['symbol']} at {a['position']}")
print(f" pbc={struct['pbc']}")
if "lattice_vectors" in struct:
print(f" lattice={struct['lattice_vectors']}")
# ── Volume.density → numpy array ──────────────────────────────────────
for s in manifest["sections"]:
if s["kind"] == "volume.density":
dm = s["members"]["data"]
raw = zf.read(dm["path"])
grid_data = np.frombuffer(raw, dtype=np.float32).reshape(dm["shape"])
# Grid descriptor is a JSON member
g = json.loads(zf.read(s["members"]["grid"]["path"]))
print(f"Density: {dm['shape']}, origin={g['origin']}")
# ── Vibrations ────────────────────────────────────────────────────────
for s in manifest["sections"]:
if s["kind"] == "vibrations":
meta = json.loads(zf.read(s["members"]["metadata"]["path"]))
print(f"Frequencies: {len(meta['frequencies'])} modes")
zf.close()
Consumer contract (v1)¶
The canonical contract is the JSON Schema at
python/vibeqc/output/formats/qvf_manifest.schema.json.
Both the producer (vibe-qc) and the consumer (vibe-view) load the
same file — the latter via a symlink — so this list is a human-readable
summary, never a normative spec on its own.
Kind strings must come from the registered v1 set (§ 1.4 of
design_qvf_format.md) or thex_<vendor>.*namespace.Every member in
membershaspath,format("json"|"binary"), andsha256. Binary members additionally havedtype(a numpy dtype name) andshape(rank-N integer array).Structure has a JSON member
"structure"; periodic structures also havelattice_vectorsandpbc=[true,true,true].Volumes (
volume.density,volume.orbital,volume.spin,volume.elf,volume.difference,volume.generic) have binary"data"+ JSON"grid"members. The grid JSON carriesorigin,voxel_vectors,shape(bohr units per design § 1.3a; vibe-view converts to Å at its PyVista boundary).volume.differencemay additionally carryoperand_aandoperand_b(string section ids that must resolve, perdependentRequired).volume.genericis an escape hatch for fields that don’t fit the purpose-built kinds — producers should prefer a more specific kind when one applies.Spectra (
spectra.ir,spectra.raman,spectra.uvvis,spectra.ecd,spectra.vcd,spectra.nmr,spectra.generic) have a JSON"spectrum"member withfrequenciesandintensities.Vibrations have JSON
"metadata"(withatomsandfrequencies) + binary"displacements"(float64,[n_modes, n_atoms, 3]).Trajectory has JSON
"metadata"(withatomsandenergies)binary
"coords"(float64,[n_frames, n_atoms, 3], Å).
reaction.pathhas the same binary layout astrajectory; the metadata JSON additionally carrieswaypoints(each withframe_index,label,kind ∈ {reactant, transition_state, intermediate, product, point}, optionalenergy_eh) and an optionalreaction_coordinatearray.reaction.waypointscarries one JSON"waypoints"member plus a section-leveltrajectory_refstring naming thetrajectorysection it annotates (validator-checked).Bands has JSON
"kpath"(withfermikey) + binary"eigenvalues"(float64,[n_spin, n_kpoints, n_bands], eV).atom_propertiescarries one or more ofmulliken_charge,loewdin_charge,spin_population, eachfloat64 [n_atoms].citationscarries a binary"references"member (BibTeX bytes, UTF-8).bondscarries one JSON"bonds"member with{"pairs": [{"i", "j", "order"}, ...]}.scf_historycarries one JSON"iterations"member with{"iterations": [{"iter", "energy_eh", ...}, ...]}.structure.symmetrycarries one JSON"data"member (spglib output).Wavefunction (
wavefunction.gto) has JSON"basis"(withstructure_ref,pure,n_ao,shells) + JSON"mo_metadata"(withspin,orbital_kind, energies / occupations either at top level forrestrictedor underalpha/betaforunrestricted) + binary"mo_coefficients"(restricted) or"mo_coefficients_alpha"+"mo_coefficients_beta"(unrestricted), each row-majorfloat64of shape[n_mo, n_ao]. Molecular and Gamma-point periodic in v1. Shell coefficients apply to normalized primitive Gaussians (see design doc Sec. 4.6 for the formula).Manifest root may carry
viewer_defaultswithauto_open(list of section ids), per-section render hints (isovalue, colormap, opacity, replication), andbookmarks(ordered list of{name, camera}using the VTK camera model).Unknown / vendor sections (Rule 2): consumers list them as “skipped, unsupported” and continue. They don’t crash the open.