Design - QVF container format for visualization data

Status: QVF v1 is implemented, not just proposed. The canonical machine-readable contract is python/vibeqc/output/formats/qvf_manifest.schema.json. This document is the human tech spec for that schema and for the current vibe-qc writer / vibe-view reader contract.

QVF (.qvf, “Quantum Visualization Format”) is a ZIP-based container for quantum-chemistry visualization and analysis data. It is designed to keep one calculation’s structure, scalar fields, spectra, bands, trajectories, provenance, and viewer hints in one random-access file instead of scattering them across Cube, XYZ, Molden, XSF, log, and program-specific sidecar files.

The format is intended for the whole quantum-chemistry ecosystem, not only vibe-qc. A QVF producer may be any quantum-chemistry code. A QVF consumer may be a structure viewer, a band plotter, a spectrum tool, a validator, a notebook script, or the reference GPU viewer vibe-view.

0. Changes Since The May 2026 Blog Post

The May 21, 2026 blog post “Quantum chemistry needs a modern file format” described the motivation and early direction. The implementation has moved since then:

Area

Blog-era statement

Current QVF v1 state

Reference viewer

A viewer was on the roadmap.

vibe-view/ exists and renders all v1 section kinds.

Writer

QVF writer was described as part of the output plan.

write_qvf() and qvf_bytes() are implemented in python/vibeqc/output/formats/qvf.py.

Validator

qvf-validate was part of the commitment.

validate_qvf() validates schema, refs, checksums, JSON parseability, binary byte size, duplicate ids, and selected cross-references.

Wavefunctions

Wavefunction coefficient matrices were originally scoped out.

wavefunction.gto section carries GTO basis + MO coefficients. Periodic Bloch wavefunctions remain out of scope.

Provenance

The blog used “provenance” broadly.

source is mandatory; provenance, citations, scf_history, thermochemistry, dipole_moment, and constraints are optional.

Extensibility

The blog sketched semver-by-kind.

Closed in v1.1. Formal extensions block, per-section critical flag, optional schema_uri, and a minor-version policy are now specified (see § 5).

DOS/PDOS

Not mentioned.

New in v1.1: dos.total and dos.projected canonical kinds.

Electrostatic potential

Not mentioned.

New in v1.1: volume.potential promoted from reserved to implemented.

NCI / RDG surfaces

Not mentioned.

New in v1.1: volume.rdg canonical kind.

Fermi surface

Not mentioned.

New in v1.1: fermi_surface canonical kind for periodic metals.

Phonon bands + DOS

Not mentioned.

New in v1.1: phonon_bands and phonon_dos canonical kinds.

Equation of state

Not mentioned.

New in v1.1: equation_of_state canonical kind for periodic EOS curves.

Metadata

The blog used “provenance” broadly.

New in v1.1: optional root metadata for thermochemistry, dipole_moment, constraints.

This document is therefore both a specification and a correction point for the next public write-up: QVF is no longer only a proposal, but its extension governance should still be described honestly.

1. Design Goals

QVF v1 is built around six goals:

  1. One shareable artifact. A calculation’s visualization data should travel as one .qvf file.

  2. Random access. A consumer should read only the members it needs. A structure-only viewer should not load a 500 MB density grid.

  3. Typed payloads. Every member has a declared format, dtype, shape, and checksum where applicable.

  4. Stable common vocabulary. Common data types use canonical section kinds such as structure, volume.density, bands, and spectra.ir.

  5. Partial support. A consumer can support a subset of kinds and still open the file. Unsupported vendor sections are reported rather than interpreted incorrectly.

  6. Producer neutrality. The source.program field can name any code. The core section kinds are not vibe-qc-specific.

QVF v1 is deliberately not a restart format. It does not attempt to encode integral caches, SCF internal state, grids used internally by a particular DFT engine, or every possible wavefunction representation. The optional wavefunction.gto section is a visualization-oriented Molden-like molecular orbital carrier, not a universal restart record.

2. Archive Model

A QVF file is a ZIP archive. The only mandatory ZIP member is manifest.json. Every other member is named from the manifest, so member paths are part of the manifest contract rather than hard-coded by the container itself.

A typical vibe-qc-produced archive looks like this:

example.qvf
├── manifest.json
├── structure/
│   ├── structure.json
│   └── symmetry.json
├── bonds/
│   └── connectivity.json
├── volumes/
│   ├── Electron_density.dat
│   ├── Electron_density_grid.json
│   ├── HOMO.dat
│   └── HOMO_grid.json
├── bands/
│   ├── kpath.json
│   └── eigenvalues.bin
├── spectra/
│   ├── ir.json
│   └── raman.json
├── trajectories/
│   ├── opt.json
│   └── opt_coords.bin
├── wavefunction/
│   ├── basis.json
│   ├── mo_metadata.json
│   └── mo_coefficients.bin
└── citations/
    └── references.bib

The exact paths may differ. Consumers must follow the path fields in manifest.json. Producers should use stable, readable directories for debuggability, but path layout is not semantic.

There is no separate viewer_defaults.json; viewer hints live in the root viewer_defaults object in manifest.json.

2.1 manifest.json

Minimal manifest shape:

{
  "qvf_version": 1,
  "source": {
    "program": "vibe-qc",
    "version": "0.9.0",
    "calculation": "h2o_rhf_sto3g"
  },
  "sections": [
    {
      "id": "structure",
      "kind": "structure",
      "members": {
        "structure": {
          "path": "structure/structure.json",
          "format": "json",
          "sha256": "..."
        }
      }
    }
  ]
}

Richer manifests may add:

{
  "schema_uri": "https://vibe-qc.org/spec/qvf/1/manifest.schema.json",
  "provenance": {
    "method": "RKS",
    "functional": "PBE",
    "basis": "def2-SVP",
    "charge": 0,
    "multiplicity": 1,
    "scf_converged": true,
    "scf_energy": {"value": -76.3371, "units": "Eh"}
  },
  "thermochemistry": {
    "zpve_eh": 0.0294,
    "enthalpy_eh": -76.3077,
    "entropy_cal_mol_k": 52.3,
    "gibbs_free_energy_eh": -76.3321,
    "temperature_k": 298.15,
    "pressure_atm": 1.0
  },
  "dipole_moment": {
    "total_debye": 1.85,
    "vector_debye": [0.0, 0.0, 1.85],
    "origin": "center_of_mass"
  },
  "constraints": {
    "frozen_atoms": [3, 4],
    "distance_constraints": [
      {"atoms": [0, 1], "target_angstrom": 1.5}
    ]
  },
  "extensions": {
    "x_vendor_ecp": {
      "version": "1.0",
      "schema_uri": "https://vendor.example.org/qvf/ecp.schema.json",
      "critical": false
    }
  },
  "viewer_defaults": {
    "auto_open": ["vol_dens_0"],
    "vol_dens_0": {"isovalue": 0.05, "colormap": "viridis", "opacity": 0.6},
    "bookmarks": [
      {
        "name": "front",
        "camera": {
          "position": [0.0, 0.0, 12.0],
          "focal_point": [0.0, 0.0, 0.0],
          "view_up": [0.0, 1.0, 0.0],
          "view_angle": 30.0
        }
      }
    ]
  }
}

schema_uri is optional. The schema’s $id is still the canonical identifier for the machine contract.

The manifest root may also carry optional metadata blocks:

  • thermochemistry — Thermodynamic corrections computed from the Hessian / frequency calculation. Fields: zpve_eh (zero-point vibrational energy in Hartree), enthalpy_eh, entropy_cal_mol_k, gibbs_free_energy_eh, temperature_k, pressure_atm. All fields are optional; a producer may emit only what it computed.

  • dipole_moment — Electric dipole moment. Fields: total_debye, vector_debye (3-element array in Debye), origin (string naming the reference point: "center_of_mass", "center_of_nuclear_charge", or "origin").

  • constraints — Geometry optimization constraints. May carry frozen_atoms (array of zero-based atom indices), frozen_lattice (bool), and constraint arrays: distance_constraints, angle_constraints, torsion_constraints. Each constraint has atoms (indices) and a target_* field with the target value.

  • extensions — Extension governance block (see § 5). A mapping from vendor namespace to {"version": "...", "schema_uri": "...", "critical": true|false}. If a section with a matching x_<vendor>.* kind has critical: true, a consumer must either support it or refuse to open the file.

2.2 Section Objects

Every section has:

Field

Required

Meaning

id

yes

Unique section identifier within the archive.

kind

yes

Section kind from the canonical registry or x_<vendor>.*.

members

yes

Mapping from member role to JSON or binary member spec.

label

no

Human-readable display label.

component

no

Component hint for sections such as volume.orbital.

critical

no

If true, consumers that do not support this kind MUST refuse to open the archive (see § 5). Default false.

schema_uri

no

URI of a JSON Schema for this section’s members, overriding the kind’s canonical contract for vendor sections.

JSON member spec:

{
  "path": "structure/structure.json",
  "format": "json",
  "sha256": "64 lowercase hex characters"
}

Binary member spec:

{
  "path": "volumes/rho.dat",
  "format": "binary",
  "dtype": "float32",
  "shape": [80, 80, 80],
  "sha256": "64 lowercase hex characters"
}

Binary members are raw C-contiguous NumPy-style arrays. The dtype is a NumPy dtype name from the schema enum. Current writer/reader behavior assumes ordinary little-endian platforms; adding an explicit endian tag is an open portability hardening item.

2.3 Validation

The canonical validator checks:

  • ZIP readability and a per-member uncompressed-size cap.

  • manifest.json existence and JSON parseability.

  • manifest conformance to the JSON Schema.

  • section id uniqueness.

  • declared member paths exist in the archive.

  • every declared member checksum matches the bytes on disk.

  • every JSON member parses as UTF-8 JSON.

  • every binary member’s byte length equals dtype.itemsize * prod(shape).

  • volume.difference.operand_a / operand_b resolve when present.

  • reaction.waypoints.trajectory_ref resolves to a trajectory section.

The schema catches structural errors. validate_qvf() adds semantic checks that JSON Schema cannot express cleanly.

3. Units And Numeric Conventions

QVF v1 uses fixed units per field. Producers convert at write time. Consumers may convert for display but should treat the on-disk units as the contract.

Field

Unit / convention

structure.atoms[].position

Angstrom

structure.lattice_vectors

Angstrom, row vectors

volume.*.grid.origin

bohr

volume.*.grid.voxel_vectors

bohr per grid step

volume.density.data

electron density on a bohr grid, usually e / bohr^3

volume.orbital.data

orbital amplitude on a bohr grid

trajectory.coords

Angstrom

trajectory.metadata.energies

Hartree

reaction.path.coords

Angstrom

reaction.path.waypoints[].energy_eh

Hartree

bands.eigenvalues

eV

bands.kpath.fermi, fermi_energy_ev

eV

spectra.ir.frequencies

cm^-1

spectra.ir.intensities

km / mol

spectra.uvvis.frequencies / energies_ev

eV

vibrations.metadata.frequencies

cm^-1

vibrations.displacements

normal-mode displacement vectors, current writer convention

wavefunction.gto.basis.shells[].exponents

bohr^-2

wavefunction.gto.mo_metadata.energies

Hartree

dos.total.energies

eV

dos.total.dos

states / eV / cell (or spin channel)

dos.total.n_electrons

unitless (integrated count)

dos.projected.energies

eV

dos.projected.dos

states / eV / channel

dos.projected.channels[].projection

states / eV

volume.potential.data

hartree / e (atomic units of potential)

volume.rdg.data

dimensionless (

fermi_surface.energies

eV (signed: E(k) − E_F)

phonon_bands.frequencies

cm^-1

phonon_bands.qpath.segments[].k_start/k_end

fractional reciprocal coordinates

phonon_dos.frequencies

cm^-1

phonon_dos.total

states / cm^-1

equation_of_state.volumes

Angstrom^3

equation_of_state.energies

eV

equation_of_state.fit.V0

Angstrom^3

equation_of_state.fit.B0

GPa

equation_of_state.fit.B0_prime

unitless

equation_of_state.fit.E0

eV

When adding fields, prefer unit-bearing names such as energy_eh, energy_ev, frequency_cm, or a {"value", "units"} object.

4. Canonical Section Kinds

The v1 registry is the set of schema Section.* branches plus the vendor namespace branch. The writer can emit the canonical kinds in the table; vibe-view renders the listed supported subset.

Kind

Payload

Writer

vibe-view

structure

Atoms, PBC flags, optional lattice

yes

yes

bonds

Explicit connectivity table

yes

via structure renderer

volume.density

Scalar density grid

yes

yes

volume.orbital

Pre-sampled orbital grid

yes

yes

volume.spin

Spin-density grid

yes

yes

volume.elf

Electron localization function grid

yes

yes

volume.difference

Difference scalar field with optional operand refs

yes

yes

volume.generic

Generic scalar field escape hatch

yes

yes

bands

k-path metadata + rank-3 eigenvalue array

yes

yes

spectra.ir

IR frequency / intensity spectrum

yes

yes

spectra.raman

Raman spectrum

yes

yes

spectra.uvvis

UV/vis spectrum

yes

yes

spectra.ecd

ECD spectrum

yes

yes

spectra.vcd

VCD spectrum

yes

yes

spectra.nmr

NMR chemical shifts / tensors / couplings

yes

yes

spectra.generic

Generic 1D spectrum

yes

yes

trajectory

Frames + optional energies

yes

yes

reaction.path

Self-contained reaction path

yes

yes

reaction.waypoints

Waypoints over an existing trajectory

yes

yes

vibrations

Frequencies + displacement tensor

yes

yes

atom_properties

Mulliken / Loewdin charges

yes

yes

structure.symmetry

spglib-style symmetry summary

yes

yes

scf_history

SCF iteration records

yes

yes

citations

BibTeX bibliography bytes

yes

yes

wavefunction.gto

Molecular GTO basis + MO coefficients

yes

yes, with limitations

dos.total

Total density of states (energy grid + DOS)

yes

yes

dos.projected

Projected DOS (per-atom, per-l-channel)

yes

yes

volume.potential

Electrostatic potential on a grid

yes

yes

volume.rdg

Reduced density gradient (NCI analysis)

yes

yes

fermi_surface

3D k-space energy grid near Fermi level

yes

yes

phonon_bands

Phonon band structure (q-path + frequencies)

yes

yes

phonon_dos

Phonon density of states

yes

yes

equation_of_state

Volume-energy curve + fitted EOS params

yes

yes

x_<vendor>.*

Vendor extension

external producers

listed, not rendered

The reserved names volume.orbital_projection, topology.qtaim, topology.elf_basins, and projections.lcao are planning names. They are not portable v1 viewer contracts yet. External producers should use x_<vendor>.* for experimental data until a kind is promoted into the schema.

4.1 structure And bonds

structure carries atoms in Angstrom and optional lattice vectors:

{
  "atoms": [
    {"symbol": "O", "position": [0.0, 0.0, 0.1173], "atomic_number": 8},
    {"symbol": "H", "position": [0.0, 0.7572, -0.4692], "atomic_number": 1}
  ],
  "pbc": [false, false, false],
  "lattice_vectors": null
}

For periodic systems, lattice_vectors is a [3, 3] array of row vectors in Angstrom and pbc marks periodic axes.

Explicit connectivity is carried as a separate bonds section:

{
  "pairs": [
    {"i": 0, "j": 1, "order": 1.0},
    {"i": 0, "j": 2, "order": 1.0}
  ]
}

If no bonds section is present, viewers may infer bonds from covalent radii. Explicit bonds take precedence.

4.2 volume.*

All volume kinds use the same member shape:

{
  "id": "vol_dens_0",
  "kind": "volume.density",
  "label": "Electron density",
  "members": {
    "grid": {"path": "volumes/Electron_density_grid.json", "format": "json", "sha256": "..."},
    "data": {
      "path": "volumes/Electron_density.dat",
      "format": "binary",
      "dtype": "float32",
      "shape": [80, 80, 80],
      "sha256": "..."
    }
  }
}

Grid JSON:

{
  "origin": [-8.0, -8.0, -8.0],
  "voxel_vectors": [
    [0.2, 0.0, 0.0],
    [0.0, 0.2, 0.0],
    [0.0, 0.0, 0.2]
  ],
  "shape": [80, 80, 80]
}

voxel_vectors is [v_i, v_j, v_k], three per-voxel step vectors in bohr. For a data index (i, j, k), the point position is:

origin + i * v_i + j * v_j + k * v_k

This is point-centered data: the number of points in the rendered grid matches shape, not shape + 1. Non-orthogonal grids are supported by using non-zero off-diagonal vector components.

volume.difference may add operand_a and operand_b section ids. If one operand is present, both are required. The sign convention is:

data = operand_a - operand_b

4.3 bands

Band eigenvalues are always rank 3:

{
  "id": "bands0",
  "kind": "bands",
  "members": {
    "kpath": {"path": "bands/kpath.json", "format": "json", "sha256": "..."},
    "eigenvalues": {
      "path": "bands/eigenvalues.bin",
      "format": "binary",
      "dtype": "float64",
      "shape": [1, 100, 26],
      "sha256": "..."
    }
  }
}

Shape is [n_spin, n_kpoints, n_bands]. Restricted calculations use n_spin = 1. Energies are stored in eV.

The kpath JSON should include:

{
  "kind": "bands",
  "version": "1.0",
  "n_spin": 1,
  "n_kpoints": 100,
  "n_bands": 26,
  "fermi": -4.71,
  "fermi_energy_ev": -4.71,
  "reciprocal_space": true,
  "segments": [
    {
      "label_start": "G",
      "label_end": "X",
      "k_start": [0.0, 0.0, 0.0],
      "k_end": [0.5, 0.0, 0.0],
      "n_points": 50
    }
  ]
}

Consumers should accept either explicit segment start / end indices or writer-style n_points segments.

4.4 Spectra

spectra.ir, spectra.raman, spectra.uvvis, spectra.ecd, spectra.vcd, and spectra.generic each carry one JSON member named spectrum. The common shape is:

{
  "frequencies": [1000.0, 1500.0],
  "intensities": [12.0, 4.5]
}

The physical meaning and units depend on kind. For example, IR uses cm^-1 and km/mol; UV/vis and ECD use eV on the x axis.

spectra.nmr is intentionally different. Its spectrum member is an object with conventional optional keys:

{
  "isotope": "1H",
  "reference": "TMS",
  "solvent": "gas",
  "chemical_shifts": [
    {"atom_index": 0, "symbol": "H", "isotropic_shift_ppm": 4.65}
  ],
  "shielding_tensors": [],
  "j_couplings": []
}

The schema intentionally keeps NMR payload shape loose in v1 while the field vocabulary settles.

4.5 Trajectories, Reactions, And Vibrations

trajectory has:

  • metadata JSON with atom identities and optional energies.

  • coords binary float64 with shape [n_frames, n_atoms, 3] in Angstrom.

reaction.path uses the same coordinate layout and adds waypoint records in metadata:

{
  "waypoints": [
    {"frame_index": 0, "label": "R", "kind": "reactant"},
    {"frame_index": 7, "label": "TS", "kind": "transition_state", "energy_eh": -75.1}
  ],
  "reaction_coordinate": [0.0, 0.2, 0.5]
}

reaction.waypoints carries only the annotations and references an existing trajectory section through section-level trajectory_ref. The validator checks that the reference resolves to a trajectory section. Frame-bound validation is a desired future tightening.

vibrations carries:

  • metadata JSON with atoms and frequencies in cm^-1.

  • displacements binary with shape [n_modes, n_atoms, 3].

4.6 wavefunction.gto

wavefunction.gto carries a molecular atom-centered Gaussian basis and MO coefficient matrices:

{
  "id": "wf",
  "kind": "wavefunction.gto",
  "members": {
    "basis": {"path": "wavefunction/basis.json", "format": "json", "sha256": "..."},
    "mo_metadata": {"path": "wavefunction/mo_metadata.json", "format": "json", "sha256": "..."},
    "mo_coefficients": {
      "path": "wavefunction/mo_coefficients.bin",
      "format": "binary",
      "dtype": "float64",
      "shape": [41, 41],
      "sha256": "..."
    }
  }
}

Unrestricted wavefunctions replace mo_coefficients with mo_coefficients_alpha and mo_coefficients_beta.

basis JSON conventions:

  • structure_ref names the referenced structure section.

  • center is a zero-based atom index.

  • l is shell angular momentum.

  • exponents are in bohr^-2.

  • coefficients apply to normalized primitive Gaussians.

  • pure: true means spherical harmonics; false means Cartesian.

  • AO order is part of the contract: spherical m = -l ... +l; Cartesian uses libint lexicographic ordering.

Limits in current reference viewer:

  • Molecular only; producers must not use this section for periodic Bloch wavefunctions.

  • vibe-view currently evaluates shells up to l = 3 for display. Higher-l basis functions need a renderer extension or pre-sampled volume.orbital sections.

4.8 dos.total — Total Density of States

Carries the total electronic density of states, typically broadened with a Gaussian or Lorentzian smearing function:

{
  "id": "dos_total",
  "kind": "dos.total",
  "members": {
    "energies": {
      "path": "dos/energies.bin",
      "format": "binary",
      "dtype": "float64",
      "shape": [500],
      "sha256": "..."
    },
    "dos": {
      "path": "dos/total.bin",
      "format": "binary",
      "dtype": "float64",
      "shape": [500],
      "sha256": "..."
    }
  }
}

Both energies and dos are rank-1 arrays of the same length. For spin-polarized calculations, dos is shape [2, n_points] (alpha, beta channels). Energies are in eV, referenced to the Fermi level (which is at 0.0 eV).

Metadata JSON (role meta) may carry:

{
  "smearing": 0.05,
  "smearing_type": "gaussian",
  "fermi_energy_ev": -4.71,
  "n_electrons": 64.0,
  "n_spin": 1
}

n_electrons is the integrated count under the DOS — useful for cross-checking and for Fermi-level interpolation.

4.9 dos.projected — Projected Density of States

Carries atom-projected and/or angular-momentum-projected DOS. The data is a flat rank-2 array where each row is one projection channel:

{
  "id": "dos_pdos",
  "kind": "dos.projected",
  "members": {
    "energies": {
      "path": "dos/energies.bin",
      "format": "binary",
      "dtype": "float64",
      "shape": [500],
      "sha256": "..."
    },
    "projections": {
      "path": "dos/projections.bin",
      "format": "binary",
      "dtype": "float64",
      "shape": [12, 500],
      "sha256": "..."
    }
  }
}

Metadata JSON (role meta) carries the channel descriptions:

{
  "energies_units": "eV",
  "n_spin": 1,
  "fermi_energy_ev": -4.71,
  "channels": [
    {"atom_index": 0, "symbol": "Mg", "l": 0, "label": "Mg-3s"},
    {"atom_index": 0, "symbol": "Mg", "l": 1, "label": "Mg-3p"},
    {"atom_index": 1, "symbol": "O",  "l": 0, "label": "O-2s"},
    {"atom_index": 1, "symbol": "O",  "l": 1, "label": "O-2p"}
  ]
}

Shape is [n_channels, n_points] for restricted or [n_spin, n_channels, n_points] for spin-polarized. Consumers should check n_spin from the metadata and interpret the leading dimension accordingly.

The dos.total and dos.projected kinds are deliberately separate (rather than a single combined kind) so that a consumer doing only total-DOS plotting can ignore the (potentially large) projection tensor.

4.10 volume.potential — Electrostatic Potential Grid

Promoted from reserved status to a canonical volume kind. Identical member structure to volume.density (grid JSON + binary data), but the data field carries the total electrostatic potential in hartree/e (atomic units of potential):

{
  "id": "esp",
  "kind": "volume.potential",
  "label": "Electrostatic potential",
  "members": {
    "grid": {"path": "volumes/esp_grid.json", "format": "json", "sha256": "..."},
    "data": {
      "path": "volumes/esp.dat",
      "format": "binary",
      "dtype": "float64",
      "shape": [80, 80, 80],
      "sha256": "..."
    }
  }
}

Visually, consumers typically render a single isosurface colored by the potential value (blue = positive, red = negative) or map the potential onto the solvent-accessible surface. vibe-view may offer both.

4.11 volume.rdg — Reduced Density Gradient (NCI Analysis)

The reduced density gradient s(r) = |∇ρ(r)| / (2(3π²)^⅓ ρ(r)^⅔) is the key scalar field for Non-Covalent Interaction (NCI) analysis. Its member structure matches volume.density:

{
  "id": "rdg",
  "kind": "volume.rdg",
  "label": "Reduced density gradient",
  "members": {
    "grid": {"path": "volumes/rdg_grid.json", "format": "json", "sha256": "..."},
    "data": {
      "path": "volumes/rdg.dat",
      "format": "binary",
      "dtype": "float32",
      "shape": [80, 80, 80],
      "sha256": "..."
    }
  }
}

The data field carries the dimensionless s(r) value. Consumers that also have a volume.density section in the archive should compute sign(λ₂)ρ from the density (where λ₂ is the second eigenvalue of the Hessian of ρ) and use it as the coloring field for the NCI isosurface (standard convention: blue = attractive, green = van der Waals, red = repulsive).

If the producer has already computed sign(λ₂)ρ, it may additionally write a volume.density section with the sign(λ₂)ρ data for consumer convenience.

4.12 fermi_surface — 3D Fermi Surface in Reciprocal Space

For metallic periodic systems, the Fermi surface is a 3D scalar field in reciprocal space: the signed distance from the Fermi level as a function of k-point for a selected band. The archive carries raw band energies on a Monkhorst–Pack mesh, and the consumer extracts isosurfaces at E = E_F:

{
  "id": "fermi0",
  "kind": "fermi_surface",
  "members": {
    "mesh": {
      "path": "fermi/mesh.json",
      "format": "json",
      "sha256": "..."
    },
    "energies": {
      "path": "fermi/energies.bin",
      "format": "binary",
      "dtype": "float64",
      "shape": [10, 10, 10, 2],
      "sha256": "..."
    }
  }
}

mesh JSON:

{
  "nk1": 10,
  "nk2": 10,
  "nk3": 10,
  "n_spin": 1,
  "fermi_energy_ev": -4.71,
  "band_indices": [3, 4],
  "lattice_vectors": [[4.2, 0.0, 0.0], [0.0, 4.2, 0.0], [0.0, 0.0, 4.2]]
}

energies shape is [nk1, nk2, nk3, n_bands_near_fermi] — only bands that cross or are within a window of ±2 eV (producer’s choice) of E_F. The consumer renders a 3D isosurface at value = 0.0 (E_F) for each band, optionally colored by band index or by spin channel.

The reciprocal-space grid is assumed gamma-centered and uniformly spaced. lattice_vectors (in Angstrom, real space) enables the consumer to draw the reciprocal cell wireframe.

4.13 phonon_bands And phonon_dos — Phonon Structure

phonon_bands

Parallel structure to electronic bands, but storing phonon frequencies along a q-path in the Brillouin zone:

{
  "id": "phonon_bands",
  "kind": "phonon_bands",
  "members": {
    "qpath": {"path": "phonons/qpath.json", "format": "json", "sha256": "..."},
    "frequencies": {
      "path": "phonons/frequencies.bin",
      "format": "binary",
      "dtype": "float64",
      "shape": [1, 100, 24],
      "sha256": "..."
    }
  }
}

frequencies shape is [n_qpts, n_modes] (3 × n_atoms modes). Frequencies are in cm⁻¹. Producers should ensure acoustic modes start near zero; consumers should not display or highlight negative (imaginary) frequencies unless explicitly requested.

qpath JSON:

{
  "n_atoms": 8,
  "n_modes": 24,
  "has_eigenvectors": false,
  "segments": [
    {
      "label_start": "G",
      "label_end": "X",
      "k_start": [0.0, 0.0, 0.0],
      "k_end": [0.5, 0.0, 0.0],
      "n_points": 50
    }
  ]
}

Optional: if eigenvectors are available, an additional eigenvectors binary member (shape [n_qpts, n_modes, n_atoms, 3], float64) carries the displacement vectors for animated display. has_eigenvectors: true in the metadata tells the consumer to load them.

phonon_dos

{
  "id": "phonon_dos",
  "kind": "phonon_dos",
  "members": {
    "meta": {"path": "phonons/dos_meta.json", "format": "json", "sha256": "..."},
    "frequencies": {
      "path": "phonons/dos_freq.bin",
      "format": "binary",
      "dtype": "float64",
      "shape": [500],
      "sha256": "..."
    },
    "dos": {
      "path": "phonons/dos_total.bin",
      "format": "binary",
      "dtype": "float64",
      "shape": [500],
      "sha256": "..."
    }
  }
}

Metadata meta may carry smearing, smearing_type, n_atoms, n_modes. Optionally, a projected binary member (shape [n_atoms, n_points]) carries atom-projected phonon DOS.

4.14 equation_of_state — EOS Curve And Fit

Carries the raw volume-energy points from a series of periodic calculations at varying unit-cell volumes, plus the fitted EOS parameters:

{
  "id": "eos",
  "kind": "equation_of_state",
  "members": {
    "volumes": {
      "path": "eos/volumes.bin",
      "format": "binary",
      "dtype": "float64",
      "shape": [15],
      "sha256": "..."
    },
    "energies": {
      "path": "eos/energies.bin",
      "format": "binary",
      "dtype": "float64",
      "shape": [15],
      "sha256": "..."
    },
    "fit": {
      "path": "eos/fit.json",
      "format": "json",
      "sha256": "..."
    }
  }
}

fit JSON:

{
  "model": "birch_murnaghan",
  "V0": 120.4,
  "E0": -5000.23,
  "B0": 74.2,
  "B0_prime": 4.1,
  "energy_unit": "eV",
  "volume_unit": "angstrom^3",
  "pressure_unit": "GPa",
  "residual_rms": 1.2e-5,
  "pressures_gpa": [null, -0.2, 0.0, 0.3]
}

The pressures_gpa array (same length as volumes) reports the pressure at each point from the EOS fit, or null if the point was excluded. residual_rms is the RMS deviation of the fit from the raw points.

Supported model values: "birch_murnaghan" (3rd order, E(V) = E0 + 9V0B0/16 · {[(V0/V)^(2/3) − 1]³ · B0’ + [(V0/V)^(2/3) − 1]² · [6 − 4(V0/V)^(2/3)]}), "murnaghan", "vinet".

4.7 Provenance, Citations, And SCF History (Updated)

Root source is mandatory and intentionally small:

{"program": "vibe-qc", "version": "0.9.0", "calculation": "job_name"}

Root provenance is optional and carries calculation-level metadata such as method, basis, functional, charge, multiplicity, convergence, and unit-tagged energies.

citations carries one binary member, references, containing UTF-8 BibTeX bytes.

scf_history carries one JSON member, iterations:

{
  "iterations": [
    {"iter": 1, "energy_eh": -75.0, "delta_e": 1.0, "diis_error": 1e-2}
  ]
}

Fields are conventional in v1. Consumers should degrade gracefully when a solver does not report DIIS error or energy deltas.

Beyond section-level records, the manifest root may carry additional metadata as documented in § 2.1:

  • thermochemistry — ZPVE, enthalpy, entropy, Gibbs free energy.

  • dipole_moment — Total and vector dipole moment in Debye.

  • constraints — Geoemtry optimization constraints (frozen atoms, distance/angle/torsion targets).

These are root-level objects, not sections, because they describe the calculation as a whole rather than carrying binary payloads. A consumer may display them in the application bar, a property panel, or omit them gracefully.

5. Extension And Versioning Model

QVF v1.1 establishes a formal extension governance model built on four mechanisms: the root extensions block, the per-section critical flag, the per-section schema_uri, and a minor-version convention for adding canonical optional kinds.

5.1 qvf_version Policy

qvf_version is an integer. v1.0 = 1. v1.1 = 1 (the extension governance and new canonical kinds are backward-compatible additions). Major version bumps (qvf_version: 2, …) indicate breaking changes: removed kinds, mandatory-become-optional inversions, or incompatible unit changes. Minor version is not encoded in the integer; instead, consumers detect capability from the extension block and supported-kind checking.

If future major versions are needed, a qvf_version value > 1 means consumers that only understand qvf_version = 1 MUST refuse to open the file.

5.2 Canonical Kinds

Canonical kinds appear in the JSON Schema and are described in § 4. The set of canonical kinds may grow in minor revisions (new kinds added, existing kinds extended with optional members). No canonical kind may be removed or have its required members changed without a major version bump.

5.3 Vendor Namespace (x_<vendor>.*)

Any section with a kind matching x_<vendor>.<specific> is a vendor extension. The <vendor> part should be a DNS-like identifier (e.g. x_vibeqc, x_pyscf, x_vasp, x_orca).

  • Vendor members must still be valid JSON or binary member specs.

  • A consumer must list vendor sections as present but unsupported, unless the extension is declared as critical: true (see § 5.5).

  • Unknown non-vendor kinds (no x_ prefix, not in the canonical registry) are not schema-valid QVF and should be reported as errors.

5.4 Root extensions Block

The manifest root may carry an extensions object that declares which vendor extensions are in use and their contracts:

{
  "extensions": {
    "x_vendor_ecp": {
      "version": "1.0",
      "schema_uri": "https://vendor.example.org/qvf/ecp.schema.json",
      "critical": false
    },
    "x_other_plugin": {
      "version": "2.1",
      "critical": true
    }
  }
}

Each key is a vendor namespace prefix (without the x_ — the x_ is implied from the section kinds). The value object has:

Field

Required

Meaning

version

yes

Vendor’s own version string for this extension.

schema_uri

no

URI of a JSON Schema that validates this extension’s section members.

critical

no

If true, any consumer that does not support this extension MUST refuse to open the archive. Default false.

5.5 Per-section critical Flag

Every section object in sections[] may carry a critical boolean (default false). If critical: true and the consumer does not recognize or support that kind, the consumer MUST refuse to open the file entirely rather than silently ignoring the section.

This prevents data loss in workflows where an extension carries information essential to correct interpretation (e.g., an ECP specification that changes the effective number of electrons, or a custom energy correction that alters displayed energies).

5.6 Minor-Version Convention

Canonical kinds may be added in minor revisions. A producer targeting qvf_version: 1 may emit any canonical kind defined in the current schema. A consumer targeting qvf_version: 1 must accept any file where it either supports the kind or the kind is non-critical.

There is no explicit minor_version field. Extensions are the mechanism for signaling non-canonical capability.

5.7 Registry Process For Promoting Vendor Kinds

To promote an x_<vendor>.* kind to canonical status:

  1. The kind is used in production by at least two independent producers for at least one minor release cycle.

  2. A specification PR adds the kind to § 4, the JSON Schema Section.oneOf branches, and updates the validator.

  3. The kind’s member contract is stable across the two producers.

  4. At least one reference consumer implements rendering support.

This process is intentionally lightweight in v1.x to encourage rapid adoption, and may be formalized in v2.

6. Producer Contract

A conforming producer must:

  • write a valid ZIP archive with manifest.json;

  • set qvf_version to 1;

  • include source.program, source.version, and source.calculation;

  • use unique section ids;

  • use canonical kinds or x_<vendor>.*;

  • include only manifest member paths that exist in the ZIP;

  • compute sha256 over the exact bytes stored in the ZIP;

  • write numeric fields in the units specified above;

  • write binary payloads matching declared dtype and shape;

  • ensure JSON members parse as UTF-8 JSON;

  • declare every x_<vendor> namespace used in sections[] in the root extensions block when critical: true is set on any section in that namespace;

  • set critical: true on a section only if the producer truly requires consumer support for correct interpretation (see § 5.5).

A producer should:

  • include provenance whenever the calculation context is known;

  • include citations when results depend on citable methods, bases, functionals, ECPs, libraries, or external data;

  • use volume.generic only when no specific volume.* kind applies;

  • use vendor namespaces for experimental payloads;

  • ship small examples and run validate_qvf() in tests.

7. Consumer Contract

A conforming consumer must:

  • read manifest.json by name rather than assuming ZIP entry order;

  • validate or at least sanity-check qvf_version, source, and sections;

  • verify sha256 before using a member’s bytes;

  • interpret only section kinds it supports;

  • report unsupported vendor sections without corrupting or misinterpreting them;

  • respect declared dtype, shape, and units;

  • check per-section critical flag: if critical: true and the kind is not supported, refuse to open the file;

  • check root extensions block for critical: true extensions that are not supported and refuse accordingly.

A consumer may require specific kinds for a particular operation. For example, a band plotter may refuse a file without bands, while a structure viewer may open the same file and ignore all scalar fields.

8. vibe-view Reference Implementation

vibe-view/ is the current reference consumer. It is a peer sub-project, not part of the lightweight vibeqc import path.

Install from a checkout:

pip install -e '.[viewer-gpu]'
vibe-view open my-calculation.qvf

Core modules:

vibe-view/src/vibeview/
├── qvf.py                  # QVFReader: zip, schema, sha256, lazy reads
├── kinds.py                # SUPPORTED_KINDS and lazy-kind registry
├── schema.json             # symlink to canonical manifest schema
├── viewer_defaults.py      # camera, volume hints, replication
├── renderers/
│   ├── structure.py
│   ├── volume.py
│   ├── bands.py
│   ├── spectra.py
│   ├── trajectory.py
│   ├── vibrations.py
│   ├── reaction.py
│   ├── wavefunction.py
│   ├── atom_properties.py
│   ├── nmr.py
│   ├── scf_history.py
│   ├── symmetry.py
│   └── citations.py
└── app.py                  # Trame / PyVista UI

vibe-view uses PyVista, VTK, Trame, Plotly, Pydantic, jsonschema, and NumPy. It lazy-loads volume binary blobs and verifies member checksums before use.

9. Implementation Pointers

Relevant tests:

  • tests/test_qvf_writer.py

  • tests/test_qvf_round_trip.py

  • tests/test_qvf_writer_to_viewer.py

  • vibe-view/tests/test_qvf.py

  • vibe-view/tests/test_renderer_behaviour.py

  • vibe-view/tests/test_qvf_schema_identity.py

10. Open Design Items

These are intentionally not hidden:

  1. Endianness. The current dtype names do not carry byte order. v1 assumes the normal little-endian scientific-Python ecosystem. A byte-order prefix in dtype (e.g., ">f4") should be added for cross-platform portability in v1.2.

  2. Periodic wavefunctions. wavefunction.gto is molecular only. Bloch coefficients need a separate section kind (e.g. wavefunction.bloch or wavefunction.kohn_sham).

  3. Topology payloads. QTAIM and ELF basin data are reserved ideas, not v1 contracts. A topology.qtaim and topology.elf_basins section kind remains to be specified.

  4. Large time-dependent volumes. v1 handles individual scalar fields well; long volumetric movies may need chunking conventions, a time axis in the grid descriptor, or an explicit frame-based volume kind.

  5. NMR schema tightening. spectra.nmr is intentionally loose in v1 while producer conventions settle. A v1.2 tightening pass should define chemical-shift tensor, J-coupling tensor, and relaxation-time structures.

  6. Fermi-surface grid conventions. fermi_surface uses a gamma-centered Monkhorst-Pack grid in v1.1. A future revision may need to support arbitrary k-mesh geometries (tetrahedron integration meshes, band-structure k-paths cross-sampled onto the FS grid).

  7. Phonon eigenvectors. The optional eigenvectors member in phonon_bands uses displacement vectors in Cartesian coordinates. The exact normalization and phase conventions should be documented explicitly in a future tightening pass.

  8. EOS fit validation. The equation_of_state section carries only the fit parameters, not the raw derivative data (pressure, bulk modulus as a function of volume). A future revision may add optional derivative arrays for interactive EOS exploration.

The format is useful now. These items define the next layer needed for QVF to become a durable multi-code standard rather than only a strong vibe-qc/vibe-view interchange format.