tm-spec

TM-Spec v0.1 — Draft

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.


Design principles

  1. Each section = one mature domain standard (CIF, Kröger-Vink, PHREEQC, Antimony, NOMAD Metainfo, AiiDA). Do not reinvent.
  2. Sanity gates are the original part of TM-Spec. Compiled from the project lessons-learned (compiled into sanity gates G01..G24). Every PR with DFT results must pass all gates with pass: true.
  3. Units alongside numbers (0.043 eV, not 0.043).
  4. paper_quotable: bool — explicit flag indicating that chemist+physicist opus review has been passed.
  5. status: PASS | PRELIMINARY | FAIL | RETRACTED — research is messy.
  6. YAML for human readability; JSONL for grep/jq/LLM-agent pipelines.
  7. Provenance as a DAG with human-readable names (parent IDs, not UUIDs).

Where each section comes from

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).


Sections

1. structure — statics

Source: 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.

2. defects — Kröger-Vink

Source: 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):

3. magnetic

Source: 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.

4. environment — PHREEQC-style

Source: 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

5. reactions — Antimony-flavoured

Source: 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

6. calculation — NOMAD-like, code-agnostic

Source: 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

7. workflow — AiiDA-like

Source: 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

8. results — Materials Project schema

Source: 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]

9. sanity — our original contribution

Source: 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.

10. provenance — AiiDA + git

provenance:
  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…

JSONL variant (for grep / jq / LLM-pipelines)

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}'

Practical benefits


Open questions [QUESTION]

TODO for v0.2

  1. JSONSchema (schema/0.1.json) for all sections.
  2. Validator: tools/tm_spec_validator.py (accepts YAML/JSONL, checks typed fields + sanity gates).
  3. Conversion from existing scripts/neb_canonical_*.py — auto-extract level-of-theory.
  4. ✅ 3 pilot artefacts — DONE 2026-05-09:
    • 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+)
  5. Auto-generated sanity_report from QE/CP2K/ABACUS logs.
  6. Mapping to NOMAD Metainfo for the calculation section.

Additional sections introduced via the w2_us_pmf pilot

US-specific sections (present only in kind: USCalculation):

Analogous sections are needed for: