Deep, code-cited reference. See the readable summary for the public version. The As-run & site implementation section below documents what actually ran and how the leaderboard is computed; the Design Spec that follows is the original (2026-06-06) plan, preserved for provenance.
As-run & site implementation (2026-06-19; data refreshed 2026-06-21)
The campaign first ran and was exported as phonon_qha_benchmark_export_2026-06-19; the site now serves the
2026-06-21 re-export (phonon_qha_benchmark_export_2026-06-21 — parquet bundle + 66 GB zstd FC2 archive,
47,213 per-(structure, potential) force-constant files, per-file sha256 in the manifest), which extended the
set by running the GRACE potentials to completion on dais.
The campaign compute pipeline (how the phonons were run)
The export the site consumes is the output of a Postgres-backed claim-queue campaign on the dais GPU
cluster (github.com/ligerzero-ai/mlip-elemental-benchmark, branch dais-port @ 095e4bf8cf12). The
unit of work is one structure = one claim → one mlip_phonon row + N mlip_phonon_volume rows + one
mlip_qha row. Full step-by-step in the repo's
benchmark/PHONON_CAMPAIGN_STEPS.md;
the single-structure recipe (relax → 0.01 Å displacements → forces → FC2 → mesh/DOS/thermo → imaginary
flag → QHA) is on the phonon page → "Reproduce these phonons".
- Seed → env → launch.
seed_track.pyfillsmlip_phonon_claim_queue(potential × 834);build_phonon_env.shbuilds a per-potential venv (each framework'smlip-env-setupinstaller + a purely additivephonopy==4.1.0 + psycopg[binary]);launch_phonon_all_dais.shsbatches one job per potential under a two-tier packing scheme — GPU-bound heavies atgpu:h200:4pack 2, CPU-bound lights (the FIRE relax + FC2 assembly + mesh are single-thread CPU) atgpu:h200:1pack 8. - Worker.
phonon_worker.py(one process = one potential) resolves the calculator once viaworker._resolve_factory, then loops:DELETE … RETURNING … FOR UPDATE SKIP LOCKEDto claim a structure (so packed/B200 workers never collide),compute_harmonic+compute_qha, UPSERT, repeat. ASIGALRMtimeout turns a hung structure into anerrorrow;--max-structsrecycles the process to free leaked JAX/CUDA-graph memory. - Compute kernel.
benchmark/phonon_compute.pyis pure ASE + phonopy (no DB, no GPU assumptions) — the locked parameters are exactly those on the methodology page (0.01 Å, ≥ 12 Å supercell, L = 100 mesh,fmax = 1e-4bulk /1e-3QHA, Vinet EOS, −0.1 THz imaginary threshold). Each completed run also caches its relaxed V₀ cell tomlip_relaxed_structure(arelaxed.copy()that strips the unpicklable MLIP calc). - Cooperative B200 offload. dais
gpu1mixes H200 + B200 (sm_100). B200 work reuses the surface campaign's cu128 / TF 2.21 env fleet pinned viaPHONON_ENVDIR, with a non-invasive phonopyPHONON_EXTRA_PYTHONPATHoverlay and per-family HF bypasses (ESEN_CKPTfor the gated fairchem checkpoints); the shared SKIP-LOCKED queue makes B200 and H200 workers cooperative — it carried ≈ 23 % of the completed work. - Recovery.
reconcile_phonon_dais.shrestores theresults + queue = 834/potentialinvariant after kills (delete-on-claim loses in-flight rows);crash_isolate_orb.pyconverts orb's silent SIGSEGV on a few degenerate tiny cells into accountederrorrows.
The export itself comes from export/export_phonon_to_parquet.py (DB → 9 parquet tables) +
compress_fc2.sh (FC2 .phonopy.yaml → zstd-16 archive, ~12.4×, status-tagged manifest) +
recompute_fmax.py (the achieved_fmax.parquet sidecar — the DB stores only the target fmax).
Provenance of the site assets:
Source → vendored CSV → parquet
tools/regen_phonon_from_export.pyreads the export'smlip_phonon(48,372 rows, one per (potential, structure); 35,314done),mlip_qha(46,161; 15,389done),structure(834, carriesdft_k_vrh), and the derivedphonon_consensus(770, the cross-potential median/MAD), and emits one row per (potential, structure) of the raw signed deltas tosrc/assets/elemental/phonon/:phonon_rollup.csv—omega_dev_thz(= ω_max − consensus median, done-only),has_imaginary(done-only bool),b_err_gpa(= B_300K − dft_k_vrh, QHA-done + DFT-K only),mp_omega_err_thz(= ω_max − MP-DFT ω_max, done + MP-covered). Integer-free so the client engine never meets a parquet int64 → JS BigInt.phonon_potentials.csv— per-potential completion %, done-only imaginary fraction, QHA %, MP coverage, runtime/GPU (feeds the selected-potential card + cost panel).phonon_consensus.csv— the per-structure consensus (drill-in reference).
- Loaders
src/data/elemental-phonon{,-potentials,-consensus}.parquet.pyapply nullable dtypes (floats →Float64, flags →Boolean, counts →Int32not Int64).
Metric → source → aggregator (the leaderboard engine)
src/components/elemental-leaderboard-agg.js (buildRows groups by potential, aggregates each metric's
source with its agg over the per-structure rows). All robust by construction:
| Metric (key) | source | agg | dir |
|---|---|---|---|
phonon_omega_consensus_mae (headline) |
omega_dev_thz |
medianAbsSkipNull |
lower |
phonon_imag_frac |
has_imaginary |
meanBool |
lower |
phonon_qha_bulk_mae |
b_err_gpa |
medianAbsSkipNull |
lower |
phonon_qha_bulk_mbe |
b_err_gpa |
medianSkipNull |
lower |
phonon_mp_omega_mae |
mp_omega_err_thz |
medianAbsSkipNull |
lower |
Verified by node-running the real engine on the real rollup (test/phonon-track.test.js + a rollup
replay): the done-only imaginary split reproduces 9.0 % GS / 34.3 % excited / 31.3 % all exactly.
Materials-Project DFT-phonon overlay
tools/fetch_mp_phonon_reference.py (mp-api ≥ 0.46.4 — 0.46.1 cannot resolve the PHEASY S3
objects) determines coverage from summary.has_props['phonon'] (381/834, 48/88 GS) and pulls
ω_max + ω_min per mp_id via materials.phonon.get_bandstructure_from_material_id(mp_id, method) over
{pheasy, dfpt} → src/assets/elemental/phonon/mp_phonon_reference.csv (379 refs: all PHEASY/PBE
bar a few DFPT/PBEsol; 2 listed-but-S3-absent misses). regen left-joins it onto the rollup
(mp_omega_err_thz). The imaginary flag uses the campaign's min ω < −0.1 THz threshold on both sides.
Caveats (tagged, never primary): MP phonons are at MP's relaxed cell vs each MLIP's own V₀ (both
at-equilibrium ω_max); PHEASY = PBE (fair), DFPT = PBEsol (softer). The phonon collection keys its S3
objects by mp_id, not the doc's opaque identifier, and exposes no material_id on the doc — hence
the per-mp_id accessor route.
Phonon DOS drill-in (expand-to-DOS)
tools/precompute_phonon_dos.py streams the ~66 GB FC2 tar once (whole-node interactive job, 72
workers, ~45–75 min; phonopy 4.1 in the pwa_elastic venv): per (structure, potential) →
zstd-decompress → phonopy.load → run_mesh(40.0) (length-based L=40 → phonopy keeps full symmetry
and diagonalises only the irreducible q-points; an explicit [na,nb,nc] mesh would disable symmetry and
diagonalise the whole zone, ~10-50x slower — that plus a too-dense L=80 was the OOM/slowness; L=40 length
keeps it ~0.5 GB/curve, fast, and visually identical for a display DOS) + run_total_dos() (tetrahedron,
faithful — resolves Van Hove peaks, vs the σ-broadening sacrifice of Gaussian smearing) → resample onto
the fixed grid linspace(-2, 50, 1041) THz (0.05 THz pitch, via run_total_dos(freq_pitch=0.05)) → normalize /n_atoms_primitive (∫ ≈ 3) → per-element parquet
shards (BLAS pinned to 1 thread so forked workers don't oversubscribe). Coverage: all 58 potentials
— the 2026-06-21 FC2 archive (47,213 files) added the GRACE force constants after those potentials were
unblocked on dais and run to completion (+1,946 files over the 06-20 repack). DOS is computed for
done + unconverged cells (both have FC2); regen joins
achieved_fmax.parquet (residual force per unconverged cell, recomputed via the same FrechetCellFilter)
onto the rollup, the structures table shows a ⚠ chip + fmax, and the DOS panel warns on unconverged.
src/assets/elemental/phonon/dos/{element}.parquet (rows mp_id, potential, dos: list<float32>).
tools/fetch_mp_phonon_dos.py (mp-api ≥ 0.46.4) appends the MP-DFT DOS as a potential="__MP-DFT__"
row per covered structure (get_dos_from_material_id, same grid, normalized ∫=3). FC-blow-up outliers
(e.g. mace_MP-0's 42,139 THz) fall off-grid → empty curve (correct). Pure shaping + the grid constant
live in src/components/phonon-dos.js (GRID_* MUST match the tool); test/phonon-dos.test.js covers
it. Delivery: the page holds one FileAttachment literal per element and awaits .parquet() only for
the clicked element (lazy, same-origin, no CORS / 25 MiB / file-count issue). The expand panel
(structures-table.js phonon variant, now caret-expandable) renders a Plot DOS overlay — MLIP vs
MP-DFT, imaginary region shaded. Why not client-side: a DOS is a q-mesh dynamical-matrix
diagonalization needing the full FC2 (~17 MB raw each) + phonopy; reimplementing that in JS/WASM (or
phonopy-in-Pyodide, infeasible) buys nothing — the output curve is ~1–3 KB, so precompute-once + ship
curves beats shipping ~66 GB of force constants for browser recompute. (The FC2 archive itself is
downloadable from the Data page for offline recompute; it is just not used for in-browser rendering.)
Band structure (as-run)
Band structures are derived from the same FC2 archive as the DOS (no new compute). Three tools handle the full pipeline:
tools/gen_band_paths.py (login node, runs once per structure, fast): generates the q-point path
for each of the two conventions.
- Hinuma / seekpath path — calls
phonopy.auto_band_structurewith theseekpathbackend (Hinuma et al., covers all 230 space groups with time-reversal). - Setyawan–Curtarolo (SC) path — calls
pymatgen'sHighSymmKpathwithpath_type="setyawan_curtarolo". For the 379 MP-covered structures, the SC path is replaced by MP's own published q-points (fetched viamaterials.phonon.get_bandstructure_from_material_id), so the MLIP-SC and MP-DFT bands share an identical x-axis by construction.
Both path dicts (fractional q-coords + segment labels/bounds) are written to
src/assets/elemental/phonon/band_paths.parquet (one row per (mp_id, convention)).
tools/precompute_phonon_bands.py (compute node, streams the FC2 tar): per (structure, potential),
for both conventions:
- Loads FC2 via
phonopy.load, callsphonopy.run_qpoints(q_points)with the path's fractional coordinates. No reciprocal-basis transform is applied — the fractional coords are fed directly; applying a transform (e.g. multiplying by the reciprocal metric) corrupts the branch ordering and was ruled out adversarially. - Atom-count guard: before calling
run_qpoints, checks that the loaded phonopy object's primitive cell atom count matches the reference cell used to generate the path. Rows where the potential relaxed to an incompatible cell (different atom count) are skipped — no silent branch mismatch. - Results per
(potential, convention):q_dist(cumulative path length),freqs(list<list<float32>>of shape[n_q][n_bands]),seg_bounds,seg_labels,n_atoms_primitive,n_bands,has_imaginary(any mode < −0.1 THz). Written to per-structure R2 shards:phonon/bands/{mp_id}.parquet.
tools/fetch_mp_phonon_bands.py (login node): appends potential="__MP-DFT__", convention="sc"
rows to each structure's shard from the Materials-Project phonon band-structure collection
(materials.phonon.get_bandstructure_from_material_id(mp_id, method="pheasy")). MP uses the
Setyawan–Curtarolo convention (verified), so these rows are always tagged "sc". Coverage: 379
structures (the same 379 as the DOS overlay; 2 listed but S3-absent misses).
Verified facts (adversarially checked):
- MP uses Setyawan–Curtarolo — confirmed against the MP phonon API response metadata and the published PHEASY methodology.
- No q-basis transform — feeding fractional coords directly to
run_qpointsgives correct branch ordering; applying a reciprocal-metric transform does not. - Atom-count guard — implemented in
precompute_phonon_bands.pybeforerun_qpoints; rows that fail the check have no band entry in the shard. - SC uses MP's own q-points for the 379 covered structures, giving an identical x-axis to the MP-DFT overlay.
R2 shard schema (phonon/bands/{mp_id}.parquet): one row per (potential, convention).
| Column | Type | Notes |
|---|---|---|
potential |
string | e.g. mace_MP-0 or __MP-DFT__ |
convention |
string | "hinuma" or "sc" |
q_dist |
list<float32> |
cumulative path distance (one value per q-point) |
freqs |
list<list<float32>> |
shape [n_q][n_bands]; THz |
seg_bounds |
list<float32> |
q_dist values at segment boundaries |
seg_labels |
list<string> |
high-symmetry point labels at each boundary |
n_atoms_primitive |
int32 | primitive cell atom count |
n_bands |
int32 | number of phonon branches (= 3 × n_atoms_primitive) |
has_imaginary |
bool | any mode < −0.1 THz on this path |
Shards are not bundled in the site build — they are fetched at runtime per-structure (the per-atom-loader pattern), so they scale with the 834-structure corpus without contributing to Cloudflare's deployment file count or size limit.
Robustness corrections vs the export's derived tables
potential_summary.pct_imaginarydivides imaginary-over-all-statuses by done, exceeding 100 % for low-completion potentials; the site recomputes done-only (regenimag_frac_doneand themeanBoolleaderboard metric agree).omega_max_meanandqha_summarymean MAE are poisoned by single-structure FC/EOS blow-ups (e.g.mace_MP-0ω_max = 42,139 THz; QHA MAE to ~10⁵ GPa). Every site metric is a median → unaffected.
Flags
orb_v2, orb_v2-MPtrj, FM_MatBench_orb, eqV2_DeNS-31M-MP, alphanet_v1-OAM carry a phonon
flag (completion ≤ ~52 % with anomalous imaginary fractions); the cliff above is clean (≥ 59 % with
~30 % imaginary). See src/data/elemental-flags.json.
PHONON + QHA Benchmark — Design Spec
Status (original): design, 2026-06-06 (now superseded by the as-run section above). Sibling track to vacancy/elastic in
dft_surrogate_mlip_elemental (db-host:db-port). Author: assembled for Han.
Prior art: Loew, Sun, Wang, Botti, Marques, "Universal Machine-Learning Interatomic Potentials
are Ready for Phonons," npj Comput. Mater. 11, 178 (2025) (arXiv:2412.16551) — an MLIP harmonic
phonon benchmark against a PBE re-computation of Togo's MDR/PhononDB. This track extends it to
(a) the metallic / magnetic elementals that DB excludes, (b) QHA / thermal expansion (Loew is
harmonic-only), and (c) the full ~60-potential ASSYST set.
0. TL;DR
For each of ~60 MLIP potentials, over all 834 MP elemental crystals, compute:
- Harmonic phonons at the MLIP's equilibrium volume → ω_max, DOS, F/S/C_v(T), ZPE, and an imaginary-mode (dynamical-stability) flag (the headline metric, reported on the 90 ground states).
- Quasi-harmonic (QHA) thermal properties via lattice expansion → V(T), α_V(T), B(T), C_p(T), C_v(T), Gibbs G(P,T).
Reference model = MLIP-vs-MLIP spread (cross-potential median/MAD + imaginary-mode consensus) on all 834 — the vacancy-track model. A small public-DFT validation overlay (~7 non-metal ground states; see §10) is tagged, never primary. A self-computed DFT reference (same pipeline, VASP engine) is a scoped follow-on; the schema reserves the hook.
Unit of work = 1 structure = 1 claim → 1 mlip_phonon row + N mlip_phonon_volume rows + 1
mlip_qha row. Plumbing is cloned from the vacancy track; the only genuinely new code is
phonon_compute.py (composes the pyiron_workflow_atomistics phonon primitives).
precompute supercell_matrix per structure (once, potential-independent)
└─ seed per-potential claim queue ─ sbatch one job per potential
└─ SLURM job = 1 potential = 1 env, gpu:a100:4, 24h, auto-resubmit until drained
└─ flux driver fans 4 workers (1/GPU), shared DELETE-on-claim queue
└─ worker: claim a structure → phonon_compute (harmonic @V0 + QHA over volumes)
→ write 1 mlip_phonon + N mlip_phonon_volume + 1 mlip_qha row
└─ rollup: cross-potential median/MAD + imaginary consensus → CSV ; overlay public DFT
1. Scope & decisions (locked with Han)
| Decision | Choice |
|---|---|
| Corpus | All 834 structures (matches vacancy/elastic). Stability headline reported on the 90 ground states (where imaginary modes are suspicious; metastable-polymorph instabilities recorded, not penalized). |
| Methods | Harmonic + QHA (isotropic-volume lattice expansion). No phono3py κ(T) (separate costly "hard mode", deferred). |
| Reference | MLIP-vs-MLIP spread primary (all 834) + tagged public-DFT validation overlay + schema hook for a future self-computed DFT reference. Score on PBE (our MLIPs are PBE-trained); tag any PBEsol/vdW ref as functional-mismatch. |
| QHA volume grid | Fixed fractional (linear) strain grid applied around each MLIP's own V₀ (potential-independent grid, per-potential equilibrium → determinism). |
| QHA intra-volume relaxation | (c) Full: relax cell shape (c/a) + ions at fixed volume per point (constant-volume variable-shape) — captures anisotropic thermal expansion. |
| QHA observables | All; headline V(T), B(T), C_p, C_v, G(P,T) (+ α_V(T), Grüneisen γ). |
| Unstable QHA volumes | Drop from the EOS fit + flag (don't abort the structure). |
| Persistence | FC2 as a portable non-pickle sidecar (force_constants.hdf5 + phonopy.yaml) at the equilibrium volume; scalars + thermo arrays in-DB; band/DOS derived on demand. (Per-volume FC2 not persisted — see §8.) |
| Energy convention | smeared VASP energy (not energy_zero) for any DFT reference. |
2. Architecture — reuse vs new
| Reused unchanged | Cloned w/ edits | New |
|---|---|---|
db.py, worker._resolve_factory (+ gpu_mem_mb), ASEEngine/engine.calculate, env_map_elemental.sh, the structure corpus, flux-4GPU pattern, auto-resubmit launcher logic, bigcell_probe.py |
claim queue → mlip_phonon_claim_queue; vacancy_driver_4gpu.py → phonon_driver_4gpu.py (WORKER path + log names); slurm_elem_vacancy.sh → slurm_elem_phonon.sh; seed_track.py += "phonon": "mlip_phonon_claim_queue"; precompute_sites* pattern → precompute_phonon_cells.py (writes per-structure supercell_matrix, not SOAP sites) |
phonon_compute.py, phonon_worker.py, schema_phonon.sql, eval/build_phonon_rollup.py + eval/phonon_metrics.py, ingest_phonon_reference.py, smoke_phonon_emt.py, PHONON.md (this file) |
Run order: (0) psql -f schema_phonon.sql; (1) sbatch precompute_phonon_cells_node.sh →
phonon_cells.json; (2) bash launch_phonon_all.sh (seed 834×60 + sbatch 60 jobs); (3) monitor;
(4) build_phonon_rollup.py; (4b) ingest_phonon_reference.py for the overlay.
3. Method — harmonic (@ equilibrium volume)
Composed from pyiron_workflow_atomistics.physics.phonons.harmonic primitives — not the
harmonic_free_energy macro, because the macro's run_thermal_properties throws on a dynamically
unstable spectrum, and "has imaginary modes" is exactly our headline metric.
- Pre-relax bulk cell+ions to the MLIP's V₀ (
FrechetCellFilter,fmax=1e-4, §8). _generate_fc2_supercells(structure, supercell_matrix, displacement_distance=0.01, is_plusminus="auto")→ list of displaced ASE supercells (PM displacements).- Loop them through the same
engine.calculatestatic-force primitive → stack(n_disp, n_at, 3)forces. phonopy.forces = forces; phonopy.produce_force_constants(); symmetrize FC + enforce the acoustic sum rule (FC_SYMMETRY). (Handles the phonopy v3→v4 FC2-shape gotcha internally.)- Imaginary-mode detection on the supercell-commensurate q-points (exact, non-interpolated), Γ-acoustic exempt (§6).
- If stable (or always, storing NaN thermo when not):
run_mesh(length=100)+run_total_dos()(tetrahedron) +run_thermal_properties(T=0..1000 K)→ F/S/C_v; ZPE = F(0 K); ω_max from the mesh. - Persist FC2 sidecar (§8); return scalars.
status = done if the bulk pre-relax converged and FC2/mesh succeeded (imaginary modes are a result,
not a failure); unconverged if pre-relax didn't hit fmax; one error row on exception.
4. Method — QHA (lattice expansion)
Composed from physics.free_energy.quasiharmonic primitives (generate_structures,
_static_energies_per_volume, _harmonic_grid_over_volumes, _fit_qha, _pack_qha_output) — not
the quasiharmonic_free_energy macro, because the macro does not skip unstable volumes (it lets
phonopy emit garbage, then _check_qha_volume_range raises on non-finite V*(T)).
- Pre-relax bulk cell+ions → V₀ (the ε=0 grid point = the harmonic headline of §3).
- Build the linear-strain grid
cell × (1+ε), ε ∈ linspace(−0.04, +0.06, 11) → target volumes (≈ V −11.5 %…+19 %, positive-biased so V*(T) stays in range at high T). - Per volume: set the target volume, then constant-volume shape+ion relax
(
FrechetCellFilter(constant_volume=True),fmax=1e-3, §8) → static E(V); build FD supercells (disp 0.01) → forces → FC2 → harmonic F(T,V) at mesh length L=60–80 (coarser; F(V,T) is far less mesh-sensitive than DOS); record per-volume ω_max + imaginary flag. - Drop unstable volumes (any with imaginary modes) from the fit; flag in
mlip_qha. _fit_qha(..., eos_type="vinet", pressure_GPa=P)over the pressure grid {0,2,5,10} GPa →phonopy.qha.QHA→ V(T), α_V(T), B(T), C_v(T), G(P,T); C_p(T) from the qha handle (get_heat_capacity_P_polyfit).- Return arrays + 300 K / 0-GPa headline scalars; persist per-volume F(T,V) (not FC2; §8).
The integer supercell_matrix is fixed per structure (from the MP cell, §5) and applied to each
volume's relaxed cell — so all potentials and all volumes evaluate commensurate cells.
5. Determinism (the cross-potential guarantee)
supercell_matrixis computed once per structure from the original MP cell via the perpendicular-spacing helper (d_i = V/|a_j×a_k|, min ⊥ spacing 12 Å; convergence-checked on ~5 cells vs 15 Å — bump globally only if ω_max/F shift beyond MLIP scatter). Stored inphonon_cells.json(atomic temp+rename), loaded read-only by every worker. Bug carried from vacancy: size by perpendicular spacing, nevercell.lengths().- The strain grid is a global constant (fractional), applied to each MLIP's own relaxed V₀ — so the grid is potential-independent while each potential sits at its own equilibrium (mirrors the vacancy "fixed repeats on each MLIP's relaxed bulk" rule).
6. Imaginary-mode detection (the stability discriminator)
- Evaluate frequencies on the supercell-commensurate q-grid (exact, non-interpolated) — interpolated property meshes can mask or fabricate near-Γ softening. Optionally also scan the dense L=100 mesh as a secondary check; the commensurate set is authoritative for the flag.
- Exempt the 3 acoustic modes at Γ (zeroed by the acoustic sum rule).
- Store the raw minimum
most_negative_freq_thzandn_imaginary_qpoints(count below a small tol) always, so the flag can be re-thresholded later. Headlinehas_imaginary_modes= (min < −0.1 THz, the default; configurable in the rollup — Loew et al. used the more lenient ≈ −1 THz, and the raw min is stored so either cutoff can be applied post hoc).frac_imaginary= fraction of commensurate q-points with a mode below threshold.
7. Parameters (cited)
| Param | Value | Source / rationale |
|---|---|---|
| Supercell min ⊥ spacing | 12 Å (conv-check vs 15 on ~5 cells) | FC2 real-space range; cost-aware |
| Displacement (FC2) | 0.01 Å | phonopy harmonic default; matches PhononDB/Alexandria. (0.03 Å is the phono3py FC3 default — not used here.) Per-potential bump to 0.015–0.02 Å allowed + documented if a MLIP is force-noisy. |
| q-mesh — production DOS/thermo | length-based L=100 (N_i=max(1,nint(L·|b_i*|))) |
phonopy density mesh — constant q-density across the heterogeneous corpus; Togo JPCM 35 353001 (2023). |
| q-mesh — QHA per-volume | L=60–80 |
F(V,T) far less mesh-sensitive; ~halves QHA q-cost. |
| q-mesh — overlay control | fixed 20×20×20 | reference-matched subset only (Loew/MDR). |
| DOS/thermo integration | linear tetrahedron (phonopy default; no SIGMA) | Togo. |
| Temperatures | 0–1000 K, 10 K step | QHA breaks near melting anyway. |
| Pressure grid (G(P,T)) | 0, 2, 5, 10 GPa | for Gibbs(P,T). |
| Strain grid | linear ε ∈ [−0.04, +0.06], 11 pts | positive-biased for high-T V*(T). |
| EOS | Vinet (phonopy.qha) | also accepts birchmurnaghan. |
| fmax — bulk FC2 pre-relax | 1e-4 eV/Å (max cell+ion force; done iff reached within the step cap, else unconverged) |
DFT refs use 1e-8…5e-5; above MLIP noise floor yet keeps Φ clean. |
| fmax — QHA constant-V relax | 1e-3 eV/Å | E(V) forgiving; robust over 11 volumes. |
| Cell filter / optimizer | FrechetCellFilter + FIRE/LBFGS, max_steps≈500 |
as Loew et al.; cap steps so noisy MLIPs can't loop. |
| fc_calculator | phonopy FD (symmetry-reduced); symfc optional for nasty low-sym cells | |
| Acoustic sum rule / PM | ON (FC_SYMMETRY, is_plusminus=auto) | residual-force / ASR mitigation. |
8. Pre-relaxation & storage
Pre-relax: bulk cell+ions to each MLIP's equilibrium (relax_cell, fmax=1e-4); displaced
supercells are never relaxed (single-point forces only); QHA per-volume does a constant-volume
shape+ion relax (fmax=1e-3). Tightness rationale: §7 + the pre-relax consult (DFT refs go 1e-8…5e-5
eV/Å because residual forces bias the FD-derived FC2 and break the acoustic sum rule → spurious Γ
imaginaries).
FC2 storage policy (non-pickle):
- Equilibrium volume (the
mlip_phononrow): persist FC2 as a portable sidecarfc/<structure_id>__<potential>.{hdf5,phonopy.yaml}(path + SHA-256 in DB) → full dispersion / DOS / any-T thermo recompute later with no MLIP rerun (sub-second on CPU). Not a pickle. - QHA per-volume rows: store the distilled
F(T,V)array + scalars in-DB, NOT per-volume FC2. Rationale: 834×60×11 ≈ 550k FC2 files is an inode burden, andF(T,V)is the expensive distillate needed to re-fit the EOS/QHA without recompute; per-volume FC2 is regenerable in the rare case it's wanted. (Flag for Han: persist per-volume FC2 too if you want per-volume dispersion replots — costs ~550k files / ~5–25 GB.) - Never store band/DOS curves — derive on demand from FC2 (bulky + lossy + re-derivable).
9. Database schema (schema_phonon.sql)
All keyed/indexed like the vacancy tables; FK structure_id → structure(id) ON DELETE CASCADE;
aggressive autovacuum on the queue.
mlip_phonon — UNIQUE (structure_id, potential); the equilibrium-volume harmonic headline:
id, structure_id, potential, status · supercell supercell_nx/ny/nz, n_atoms_supercell, min_perp_angstrom, supercell_matrix(int[9]) · method displacement_distance, n_displacements, q_mesh_length, symprec, fmax · freqs omega_max_thz, has_imaginary_modes, n_imaginary_qpoints, most_negative_freq_thz, frac_imaginary · thermo zpe_ev_per_atom, free_energy_300k, entropy_300k, cv_300k (+ temperature_array, free_energy_array, entropy_array, cv_array as float8[] ) · fc2 fc2_path, fc2_sha256 · convergence
bulk_converged · provenance runtime_ms, gpu_mem_mb, device, worker_host, slurm_job_id, error_msg, started_at, finished_at.
mlip_phonon_volume — UNIQUE (structure_id, potential, volume_index):
strain, volume_per_atom, static_energy_ev, omega_max_thz, has_imaginary_modes, used_in_qha(bool), free_energy_tv(float8[] # F(T) at this V), relax_converged, fc2_path(NULL by policy).
mlip_qha — UNIQUE (structure_id, potential):
eos_type, n_volumes_total, n_volumes_used, n_volumes_dropped, temperature_array, pressure_grid ·
arrays volume_T(float8[]), thermal_expansion_T, bulk_modulus_T, cv_T, cp_T and gibbs_PT(float8[] per P)
· headlines v_300k, alpha_300k, b_300k, cp_300k, cv_300k, gibbs_0gpa_300k, gruneisen_300k ·
qha_status (done/partial/failed), status, provenance.
mlip_phonon_claim_queue — (potential, structure_id) PK (byte-identical clone of the vacancy queue).
mlip_phonon_reference — the DFT overlay and the self-DFT hook: UNIQUE (mp_id, source, ref_method);
mp_id, element, is_ground_state, source(text), ref_functional(text), ref_method(text # dfpt/phonopy_fd/self_vasp), omega_max_thz, stable(bool), free_energy_300k, entropy_300k, cv_300k, raw(jsonb).
10. Reference handling
- Backbone = MLIP-vs-MLIP spread on all 834 (
build_phonon_rollup.py): cross-potential median/MAD of ω_max / F / S / C_v + QHA scalars, imaginary-mode consensus (majority flag per structure; disagreement = interesting), all over converged rows only. This separates conservative/smooth potentials (eSEN, GRACE) from those that hallucinate soft acoustic branches. - Public-DFT validation overlay (tagged, never primary):
ingest_phonon_reference.pyloads the ~7 ground-state overlap (B, Si from MP-DFPT; Ar, Br, N, Ne, P + B, Si from the Loew PBE recompute hosted atalexandria.icams.rub.de/data/phonon_benchmark/) intomlip_phonon_reference, tagged withref_functional/ref_method. Used to validate our phonopy pipeline reproduces an independent DFT phonopy result; also compute that subset on the 20×20×20 mesh to match the published numbers. - Coverage reality (verified, §cross-match): the public DFT phonon corpora are one non-magnetic- semiconductor selection (~10k materials) served three ways (Togo-MDR / Loew-PBE / MP-Petretto). It covers 7/90 of our ground states, zero metals, zero Fe/Ni, and no public DFT QHA exists at all (every set is harmonic-at-one-volume, reports C_v not C_p). A meaningful DFT reference for this corpus must be self-computed.
- Self-computed DFT reference (follow-on, hook reserved): the same calculator-agnostic pipeline with a
VASP
ASEEnginepopulatesmlip_phonon_reference(ref_method='self_vasp'), prioritized to the Fe/Ni-neighborhood metals where nothing public exists. Scope TBD (focused ~20–30 vs all 90 GS). - Functional rule: score on PBE (matches OMat24 / MPtrj / sAlex training). PBEsol/vdW refs are secondary, tagged functional-mismatch (PBE → larger lattice, softer phonons, larger α; never charged to the MLIP). Thermal expansion is the most functional-sensitive observable.
11. Cost & scale
- Claims: 1 per structure → 50,040 (834×60), same granularity as vacancy.
- Rows: ≈ 50k
mlip_phonon+ ≈ 550kmlip_phonon_volume(×11 vols) + 50kmlip_qha. - FC2 sidecars: ≈ 50k files (equilibrium only), ~1–2.5 GB compact.
- Compute/structure/potential: 1 cell+ion pre-relax + N_disp static force evals (harmonic) + 11×(1 constant-V relax + N_disp static evals) (QHA). N_disp is small for high-symmetry cells, larger for low-sym.
- 40 GB A100 OOM ceiling — worse here than vacancy (phonons are many supercell force evals). Screen
every potential with
bigcell_probebefore the fleet; OOM → gracefulerrorrow + capability signal.
12. Validation (before the fleet)
- EMT smoke (
smoke_phonon_emt.py): fcc-Al QHA → positive α, V*(300 K) > V*(0) (mirrors the pwatest_quasiharmonic_free_energy_emt_al); fcc-Cu harmonic → no spurious imaginaries. - Cu + Si across all 60 potentials on gpudev (single-structure load+run sanity; correct status logic).
- Pipeline-vs-public-DFT on the 7 overlap (B, Si, P, …): our phonopy F/S/C_v/ω_max within tolerance of the Loew/MDR + MP-DFPT published values (on the 20³ control mesh).
bigcell_probeOOM screen on the largest supercell × memory-heavy potentials.
13. Install / environments
pip install phonopyinto the ~36 representativeenvs_elemental/<rep>envs (where forces run — mirrors how those envs already carrypyiron_workflow_atomistics). Framework-agnostic (works in both the torch and TensorFlow model envs).pip install symfconly if thesymfcfc_calculator backend is enabled (low-sym/large cells).pip install 'pyiron_workflow_atomistics[phonons]'(phonopy+phono3py+symfc) only if a κ(T) track is ever added. Orchestration/flux envs unchanged.
14. Gotchas (carried + new)
- Compose primitives, not the macros — both
harmonic_free_energyandquasiharmonic_free_energythrow/mishandle unstable spectra; we need the imaginary flag and "drop unstable volumes." - Min-image / supercell sizing uses perpendicular spacing, never
cell.lengths()(vacancy bug). - Pre-relax tightness matters — under-converged forces → spurious near-Γ imaginaries (mistaken for real instability). 1e-4 FC2 / 1e-3 QHA + ASR ON.
displacement = 0.01 Å(harmonic) ≠ 0.03 Å (phono3py FC3). Easy to conflate.- q-mesh = length-based L, not a fixed grid — fixed grids mis-sample a heterogeneous corpus.
- Strain is linear (
cell×(1+ε), V∝(1+ε)³); positive-bias the grid or V*(T) walks off it (phonopy raises). - 40 GB A100 OOM — accept graceful errors; screen with
bigcell_probe. - Atomic sidecar + json writes (temp+os.replace) so running workers never read half-written files.
slurm_elem_phonon.shenv fallback viaenv_map_elemental.shfor the ~25 potentials lacking an own env dir — don't hardcodeenvs_elemental/<POT>.- C_p vs C_v — the package returns C_v; C_p comes from the
phonopy.qhahandle (or C_p = C_v + V·T·α²·B_T). Han wants both.
15. Open items for Han
- Per-volume FC2 persistence (§8): default = NO (store F(T,V) only). Flip to YES if you want per-volume dispersion replots (~550k files).
- Imaginary threshold (§6): default stricter than Loew's ≈ −1 THz; raw min stored so it's re-thresholdable.
- Self-computed DFT reference scope (§10): focused ~20–30 metals vs all 90 GS — decide if/when.
- Supercell 12 Å vs 15 Å (§5/§7): start 12 Å, convergence-check; bump if needed.
16. Key references
- Loew, Sun, Wang, Botti, Marques, npj Comput. Mater. 11, 178 (2025) (arXiv:2412.16551) — MLIP harmonic phonon benchmark vs PBE-recomputed MDR/PhononDB.
- A. Togo, J. Phys. Soc. Jpn. 92, 012001 (2023) — phonopy/phono3py methodology.
- A. Togo, L. Chaput, T. Tadano, I. Tanaka, J. Phys.: Condens. Matter 35, 353001 (2023) (arXiv:2301.05784) — implementation strategies (mesh sampling, tetrahedron).
- A. Togo & I. Tanaka, Scr. Mater. 108, 1 (2015).
- G. Petretto et al., Sci. Data 5, 180065 (2018) — MP DFPT phonons (PBEsol/ABINIT).
Verification addendum (run wf_7fbcd118-848, 2026-06-08)
Track is designed but not yet run; verified the code against this design doc:
- FC2 sidecar is a single
{structure}__{potential}.phonopy.yamlwith force constants embedded (phonon_compute.py:117-125) — noforce_constants.hdf5. - QHA strain grid is the asymmetric
np.linspace(-0.04, 0.06, 11)(positive-biased), Vinet EOS (phonon_compute.py:27,224) — not ±5%. - Rollup grain is cross-potential at
(mp_id, element, is_ground_state)(median/MAD + imaginary consensus),build_phonon_rollup.py:27— per-potential is only the DB input grain. - Relaxer is
FrechetCellFilter+ FIRE only (phonon_compute.py:14,56), no LBFGS. - No 20³ control mesh exists; meshes are length-based (L=100 production, L=80 QHA). The schema has
a single
statuscolumn onmlip_qha; there are nosymprec/gruneisen_300kcolumns and no Grüneisen γ is computed.