Third Matter Specification — a single human-readable metalanguage for describing structures, defects, environments, reactions, DFT calculations, sanity gates, and provenance in the Third Matter project.
Date: 2026-05-09. Status: [DRAFT]. Pilot artefact: see
examples/mack_vfe_neb.tm.yaml.
pass: true.0.043 eV, not 0.043).paper_quotable: bool — explicit flag indicating that chemist+physicist opus review has been passed.status: PASS | PRELIMINARY | FAIL | RETRACTED — research is messy.| Section | Source | What we borrow |
|---|---|---|
structure |
AFLOW prototype + extxyz + CIF | Compact label + explicit Wyckoff positions |
defects |
Kröger-Vink + pymatgen-defects | Equation as code |
magnetic |
magCIF + Bilbao MAGNDATA | propagation vector + magmoms |
environment |
PHREEQC keyword blocks | speciation + kinetics in one place |
reactions |
Antimony + BNGL | text rules + rate laws |
calculation |
NOMAD Metainfo + QCSchema | code-agnostic level-of-theory |
workflow |
AiiDA + atomate2 | endpoints + provenance graph |
results |
Materials Project schema | typed values + uncertainty |
sanity |
our project lessons-learned | gates as tests in code |
provenance |
AiiDA + git | parents/hashes/cost |
spec: tm-spec/0.1
kind: NEBCalculation # Structure | Defects | Magnetic | Environment |
# Reaction | NEBCalculation | MDCalculation |
# USCalculation | MetaDynCalculation |
# MLIPBenchmark | SanityReport | Provenance
id: tm.<system>.<setup>.<method>.<date>
schema_url: https://exopoiesis.github.io/tm-spec/0.1.json # [HYPOTHESIS] published
Kind convention (Q-TMSPEC-1): each methodological sampling family gets its own kind. NEB ≠ US ≠ MetaDyn ≠ plain MD, because each has unique structural blocks (endpoints / windows / hills / trajectory).
structure — staticsSource: AFLOW prototype + extxyz + CIF.
structure:
formula: Fe16S16 # supercell composition (post-defect)
prototype: AB_tP4_129_a_c # AFLOW: mackinawite tetragonal
space_group: { number: 129, symbol: P4/nmm }
cell:
a: 3.687 Å
c: 5.029 Å
gamma: 90 deg
wyckoff:
Fe@2a: [0.00, 0.00, 0.00000]
S@2c: [0.00, 0.50, 0.23420]
supercell: [3, 3, 1]
pbc: [true, true, true]
ref: structures/mack_136at_pristine.extxyz
Required fields: formula, cell, pbc.
Recommended: prototype, space_group, wyckoff, ref.
defects — Kröger-VinkSource: Kröger-Vink notation + pymatgen-defects.
defects:
reactions:
- "Fe_Fe^× → V_Fe'' + Fe(removed)" # iron vacancy
- "nil → H_i^• + e'" # interstitial proton + electron
charge_balance: |
V_Fe'' + 2·H_i^• → neutral
population:
V_Fe: { count: 1, conc: 1/144 }
H_int: { count: 2, conc: 2/144, site: tetrahedral_cavity }
Notation (Kröger-Vink):
• (positive), ' (negative), × (neutral), relative to ideal.Fe_Fe, V_S, H_i).magneticSource: magCIF + Bilbao MAGNDATA.
magnetic:
state: AFM-G # FM | AFM-{A,C,G} | NM | PM-itinerant | ferri
collinear: true
propagation_vector: [0, 0, 0]
magmoms_uB:
Fe[top]: +3.5
Fe[bottom]: -3.5
surrogate_warning: "PBE FM as PM/itinerant proxy at 363 K"
surrogate_warning — critical for this project: at 298–363 K
mackinawite is paramagnetic, but in DFT AFM-G is used as a surrogate.
environment — PHREEQC-styleSource: PHREEQC keyword blocks.
environment:
phase: aqueous_at_solid_interface # bulk_aqueous | gas | vacuum | slab_water
T: 363.15 K
P: 100 bar
pH: 6.0
redox: { Eh: -0.40 V_SHE, pe: -6.8 }
composition_molal:
Fe(2): 1.0e-3
S(-2): 5.0e-4
Na: 0.5
Cl: 0.5
database: phreeqc/llnl.dat
saturation_targets:
mackinawite: equilibrium
pyrite: undersaturated
reactions — Antimony-flavouredSource: Antimony + BNGL.
reactions:
- id: R1
label: Fe-chemisorption
rule: "H_aq^+ + Fe_Fe^× → (H–Fe)_Fe^×"
rate: "k1 * [H+] * θ_Fe_free"
params:
k1: { value: 1.0e-4, units: M^-1·s^-1, source: dft_neb@per-project-dft-protocol }
Ea_eV: 0.043
- id: R2
label: Grotthuss-shuttle
rule: "H2O···H+ ⇌ H+···H2O"
rate: "k2 * θ_H2O_chain"
params:
k2: { value: 1.2e+11, units: s^-1, source: aimd@a-prior-run }
Ea_eV: 0.18
calculation — NOMAD-like, code-agnosticSource: NOMAD Metainfo + QCSchema.
calculation:
method: DFT # DFT | AIMD | MLIP | classical_MD | tight_binding
level:
xc: PBE+D3(BJ)
basis: { kind: plane_waves, cutoff_Ry: 80, rho_cutoff_Ry: 640 } # NOMAD enum (D-02)
pseudopotential: GBRV-1.5 USPP
smearing: { kind: marzari-vanderbilt, width_Ry: 0.02 } # lowercase enum (D-01)
# Optional NOMAD/LibXC interop layer (D-03):
xc_libxc: [GGA_X_PBE, GGA_C_PBE]
vdw: D3-BJ
spin: collinear # none | collinear | non-collinear
hubbard:
Fe-3d: { U_eV: 4.0, formulation: Dudarev }
k_points: { mesh: [3, 3, 4], shift: [0, 0, 0] }
convergence:
scf_Ry: 1.0e-8
fmax_eV_per_A: 0.05
code:
name: QuantumESPRESSO # | CP2K | ABACUS | GPAW | JDFTx | VASP
version: 7.3.1
image: infra-qe-gpu:cc80
mpirun: "mpirun --bind-to none -np 1 pw.x"
npool: 4
workflow — AiiDA-likeSource: AiiDA + atomate2 + our deploy idiom.
workflow:
kind: NEB # NEB | relax | scf | aimd | us_pmf | mlip_bench
endpoints:
A: { ref: artifacts/endA.extxyz, E_eV: -1234.567, fmax: 0.039 }
B: { ref: artifacts/endB.extxyz, E_eV: -1234.530, fmax: 0.041 }
n_images: 7
optimizer: BFGS
fmax_eV_per_A: 0.05
k_spring_eV_per_A2: 0.10
reuse_relaxed: true
prewrap: idpp # per the project DFT protocol, erratum #5: MANDATORY
paired_script: scripts/neb_canonical_mack_136at_qe_VFe.py
results — Materials Project schemaSource: Materials Project schema.
results:
Ea_forward_eV: { value: 0.043, ci95: [0.030, 0.060] }
Ea_reverse_eV: 0.020
ΔE_rxn_eV: 0.023
attempt_freq_Hz: 1.2e13
rate_at_T_per_s: 4.5e10
status: PASS # PASS | PRELIMINARY | FAIL | RETRACTED
paper_quotable: true
validated_by: [chemist@opus, physicist@opus]
sanity — our original contributionSource: the project lessons-learned (compiled into sanity gates G01..G24). Each entry is compiled into a gate.
sanity:
- { id: G01_FeS_bond, rule: "min(Fe–S) > 2.00 Å", observed: 2.27, pass: true }
- { id: G02_AFM_not_collapsed, rule: "|Δμ_Fe| < 0.5 μB", observed: 0.02, pass: true }
- { id: G03_endpoints_distinct, rule: "ΔR_H > 0.5 Å OR Δnearest_Fe identity", observed: "ΔR=1.83 Å + identity flip", pass: true }
- { id: G04_fmax_endpoints, rule: "fmax(A,B) ≤ fmax_target", observed: [0.039, 0.041], pass: true }
- { id: G05_scf_converged, rule: "all images SCF OK", pass: true }
- { id: G06_ascii_safe, rule: "no em-dash in script (ASCII-only container)", pass: true }
- { id: G07_singleton_lock, rule: "lockfile present, no race", pass: true }
[DECISION-???] A PR with a DFT result must supply sanity[] with
all pass: true; otherwise paper_quotable is forced to false. — TODO: formalise.
provenance — AiiDA + gitprovenance:
date: 2026-05-04
author: igor@exopoiesis.space
parents:
- tm.mack.vfe.hint.smoke@2026-05-03 # smoke prerequisite
- project-dft-protocol@2026-04-28 # protocol authority
literature: [Vaughan1971, Wang2025_pyrite, Qiu2025_neb]
compute:
host: cloud-gpu
gpu: 2×Blackwell
cost_usd: 47.30
walltime_h: 18.4
hash:
inputs: sha256:5f3c…
outputs: sha256:e2b1…
One document = one entity with kind:, linked via parent/id.
{"spec":"tm-spec/0.1","kind":"Structure","id":"tm.mack.pristine","prototype":"AB_tP4_129_a_c","cell":{"a":3.687,"c":5.029,"sg":129},"supercell":[3,3,1]}
{"spec":"tm-spec/0.1","kind":"Defects","parent":"tm.mack.pristine","kv":["Fe_Fe^× → V_Fe''","nil → H_i^• + e'"],"population":{"V_Fe":1,"H_int":2}}
{"spec":"tm-spec/0.1","kind":"Magnetic","parent":"tm.mack.pristine","state":"AFM-G","magmoms_uB":{"Fe_top":3.5,"Fe_bot":-3.5}}
{"spec":"tm-spec/0.1","kind":"Environment","T_K":363.15,"pH":6.0,"Eh_V":-0.40,"composition_molal":{"Fe(2)":1e-3,"S(-2)":5e-4}}
{"spec":"tm-spec/0.1","kind":"Reaction","id":"R1","rule":"H_aq^+ + Fe_Fe^× → (H-Fe)_Fe^×","Ea_eV":0.043,"k":{"value":1e-4,"units":"M^-1 s^-1"}}
{"spec":"tm-spec/0.1","kind":"NEBCalculation","id":"tm.mack.vfe.hint.neb.2026-05-04","level":"PBE+D3+U(Fe=4)","code":"QE@7.3.1","images":7,"fmax":0.05,"Ea_eV":0.043,"status":"PASS"}
{"spec":"tm-spec/0.1","kind":"SanityReport","parent":"tm.mack.vfe.hint.neb.2026-05-04","gates":[{"id":"G01","pass":true},{"id":"G03","pass":true,"obs":"ΔR=1.83Å+identityflip"}]}
{"spec":"tm-spec/0.1","kind":"Provenance","parent":"tm.mack.vfe.hint.neb.2026-05-04","cost_usd":47.30,"hash":"sha256:e2b1…"}
jq idioms:
# All paper_quotable Ea values
cat *.tm.jsonl | jq 'select(.kind=="NEBCalculation" and .status=="PASS") | .Ea_eV'
# Sanity reports with failures
cat *.tm.jsonl | jq 'select(.kind=="SanityReport") | .gates[] | select(.pass==false)'
# Cost summary
cat *.tm.jsonl | jq 'select(.kind=="Provenance") | .cost_usd' | awk '{s+=$1} END {print s}'
paper/SI/mack_neb.tm.yaml. The reviewer
sees structure + level of theory + gates + provenance in one place.results/dft_datasets/ become JSONL —
cat *.tm.jsonl | jq replaces our ad-hoc Python parsers.kind extension for US/PMF be separate from MDCalculation with enhanced_sampling?
Answer: YES, a separate kind: USCalculation. Introduced via the pilot examples/w2_us_pmf.tm.yaml. Reasons:
cv_definition, sampling, pmf_analysis) that do not fit into workflow or results of the existing schema.MetaDynCalculation and MDCalculation will be analogous separate kinds (v0.2).
Applied convention: each methodological sampling family gets its own kind.tm-spec → qcschema exporter for compatibility with the MolSSI stack?schema/0.1.json) for all sections.tools/tm_spec_validator.py (accepts YAML/JSONL, checks typed fields + sanity gates).scripts/neb_canonical_*.py — auto-extract level-of-theory.examples/mack_vfe_neb.tm.yaml (NEBCalculation, mack)examples/pyr_smoke.tm.yaml (NEBCalculation smoke, pyr)examples/w2_us_pmf.tm.yaml (USCalculation, mack 3×3×1 + H2O+H+)sanity_report from QE/CP2K/ABACUS logs.calculation section.US-specific sections (present only in kind: USCalculation):
cv_definition — collective variable + analytical form + units + implementation reference.sampling — windows, centers, springs, per-window MD config, totals.pmf_analysis — WHAM/MBAR + convergence + reliability range.Analogous sections are needed for:
kind: MetaDynCalculation — cv_definition + metadyn_protocol (hill height/width, bias factor) + fes_analysis.kind: MDCalculation — ensemble + thermostat + trajectory_analysis.kind: MLIPBenchmark — backends[] + reference_dataset + metrics.