Deep, code-cited reference. See the readable summary for the public version. This page is the full provenance: exactly what the GB kernel computes per (potential × elemental cubic crystal), which file and line produces each number, how the streaming DB sinks + walltime/CUDA/atom-count caps keep a 30-relaxation study durable, how the coverage clamp is defined, and how the site turns model-vs-model agreement into the "Consensus dev" column.

No DFT reference. This track has no accuracy metric. The leaderboard default-sorts by coverage (a clamp metric, §8); the only cross-model signal is consensus — the median gb_energy over the 15 curated KEEP potentials (§9), which measures "most typical", not "most correct" (11 of 15 KEEP are GRACE-family, so it leans GRACE) and is coverage-confounded. Refer to /grain-boundary for the numbers; this page describes only the method.

Grain-Boundary Energetics — Full Method & Provenance

Benchmark: mlip-elemental-benchmark, Track GB (mlip_gb / mlip_gb_bulk) · github.com/ligerzero-ai/mlip-elemental-benchmark Question: per (potential × elemental cubic crystal): relax bulk → equilibrium a0 (EOS) → enumerate the deterministic CSL grain-boundary set at a0pure_gb_study each boundary → gb_energy (J/m²), excess volume, GB-plane surface energy, rigid & relaxed work of separation, full decohesion profile. One mlip_gb row per boundary; one mlip_gb_bulk row per (potential, structure) carrying n_gb_total. Method family: GRACE pure_gb_evaluation (calculate_single_gb_sequential) ported to a generic ASE engine via pyiron_workflow_atomistics.physics.grain_boundary.pure_gb_study. Curated scope: 15 KEEP potentials × 36 industrial structures (deliverable); 51 potentials have some GB data.

Provenance caveat. physics/* line numbers are from the source checkout at /dais/u/hmai/2026_06_12_polycrystal/_src/pyiron_workflow_atomistics/… (the package the campaign envs install) and may drift a few lines against another installed wheel; function names are stable. benchmark/* citations are exact against /u/hmai/surface_campaign_port/mlip-elemental-benchmark.


0. Source-of-truth files (every claim here is cited to these)

Layer File
GB kernel (EOS → build → study; sinks + caps) benchmark/gb_compute.py
DB worker (claim queue; streaming UPSERTs; recovery hooks) benchmark/gb_worker.py
Coverage clamp metric benchmark/gb_status.py
Parquet export (clamp + consensus scaffolding) benchmark/export_gb_to_parquet.py
Schema (mlip_gb / mlip_gb_bulk / mlip_gb_claim_queue / mlip_gb_eligible) benchmark/schema_gb.sql
a0/EOS macro pyiron_workflow_atomistics/physics/bulk.py (optimise_cubic_lattice_parameter)
CSL enumeration + construction …/physics/_grain_boundary_code/searcher.py
The study macro …/physics/grain_boundary.py (pure_gb_study)
Consensus / site CSVs tools/regen_gb_from_export.py (site repo)

1. End-to-end pipeline

structure.atoms_pickle (ASE Atoms; potential-independent MP cell)  +  element, basis (from mlip_gb_eligible)
        │
        │  gb_worker.process_one  →  gb_compute.compute_gb(element, basis, atoms, calc, sinks…)
        ▼
   ┌──────────────────────────────────────────────────────────────────────────────────────────┐
   │ (A) BULK EOS   relax_bulk → optimise_cubic_lattice_parameter → a0, E0/atom, V0/atom, B      │  CPU
   │                (or skipped via prebuilt / bulk_override cache)   →  mlip_gb_bulk row         │  (bulk_sink)
   ├──────────────────────────────────────────────────────────────────────────────────────────┤
   │ (B) CSL BUILD  build_gb_set → get_gb_code_df_with_structures at a0  → deterministic GB set   │  CPU
   │                (axes × sigma; MAX_ATOMS pre-filter; MAX_BUILT_ATOMS post-build filter)       │
   ├──────────────────────────────────────────────────────────────────────────────────────────┤
   │ (C) PER GB     study_one_gb → pure_gb_study  (≈30 relaxations):                              │  GPU
   │                grain-length opt (stage1 3-ext + stage2 5-ext, interp min) → gb_energy,       │  (gb_sink,
   │                excess volume; +20 Å vacuum relax → surface_energy; segregation supercell;    │  one commit
   │                Voronoi GB-plane ID; rigid+relaxed cleavage scan → Wsep + decohesion profile  │  per GB)
   └──────────────────────────────────────────────────────────────────────────────────────────┘
        │  streamed per-GB into mlip_gb (UPSERT, UNIQUE(structure_id, potential, gb_id))
        ▼
   gb_status.py clamp coverage   ·   export_gb_to_parquet.py   ·   tools/regen_gb_from_export.py (consensus)

Every relaxation in the kernel is ASE with fmax = 0.01 eV/Å and a 500-step cap (FMAX_RELAX, MAX_STEPS = 0.01, 500, gb_compute.py:150), wired through make_engines (gb_compute.py:171-184): a minimize engine (CalcInputMinimize(force_convergence_tolerance=FMAX_RELAX, relax_cell=False)) and a static engine.


2. Step A — bulk EOS → equilibrium a0

relax_bulk (gb_compute.py:186-209) builds a seed conventional cubic cell (ase.build.bulk(element, basis, a=a0_guess, cubic=True), gb_compute.py:196) and runs the Birch–Murnaghan EOS macro optimise_cubic_lattice_parameter (physics/bulk.py:103-145), with strain_range=(-0.02, 0.02), num_points=11 (EOS_STRAIN_RANGE, EOS_NUM_POINTS, gb_compute.py:149).

EOS-skip caches (optional). If a prebuilt dict or bulk_override lookup is supplied, the EOS is skipped and a0/V0/E0 are taken from the precomputed relax-export cell (bit-identical to the in-worker EOS, immune to its failure modes) — compute_gb branches at gb_compute.py:385-428; the worker loads these via _load_prebuilt / _bulk_override (gb_worker.py:113-142).

Only the four basis strings in BASIS_OK = {bcc, fcc, sc, diamond} (gb_compute.py:151) are supported; anything else returns status='unsupported' with no rows (gb_compute.py:381-383).


3. Step B — deterministic CSL enumeration at a0

build_gb_set (gb_compute.py:211-240) calls get_gb_code_df_with_structures (searcher.py:485+) with the potential's own a0 and V0, so the boundary geometry is built at the model's equilibrium lattice, deduplicate=True:

n_gb_total (written to mlip_gb_bulk, §7) is the denominator of the coverage clamp (§8): the canonical GB count for that (potential, structure) at the cap in force when it was built.


4. Step C — the pure_gb_study macro (≈30 relaxations per boundary)

study_one_gb (gb_compute.py:286-360) runs pure_gb_study (physics/grain_boundary.py:1273-1433) for one boundary, passing the study params fixed in the kernel (gb_compute.py:146-148): EXT_STAGE1=[-0.2, 0.3, 0.8], EXT_STAGE2=[-0.05,-0.025,0,0.025,0.05], GB_NORMAL_AXIS="c", VACUUM_LENGTH=20.0. The macro chains, in order:

4.1 Grain-length optimisation → gb_energy, excess volume

full_gb_length_optimization (grain_boundary.py:246-333) is a two-stage scan along the boundary normal, each stage gb_length_optimiser (grain_boundary.py:157-210):

  1. Extend the cell along axis c by each extension, scale_atoms=True (get_extended_struct_list, grain_boundary.py:21-30), and relax each. Stage 1 uses the 3-point coarse extensions_stage1 (:263-271); Stage 2 re-seeds from the stage-1 interpolated minimum and refines with the 5-point extensions_stage2 (:273-279).
  2. Interpolate the minimum — a degree-2 polynomial fit of energy vs. grain length picks the convex minimiser (fit_polynomial_extremum, :55-83; get_interp_min_energy_structure_from_forloop_df, :86-109), giving the converged GB and its energy at the interpolated length.
  3. gb_energy (get_GB_energy, :112-120): γ_GB = (E_gb − N·E0)/(2·A) × 16.021766208 (eV/Ų → J/m²), area A the in-plane cross-section, factor 2 for the two boundaries in the periodic bicrystal. Excess volume (get_GB_exc_volume, :123-131): (V_gb − N·V0)/A/2.

These map to stage2_opt_GBEnergy / stage2_opt_excvol (returned at grain_boundary.py:1423-1424) → gb_energy / excess_volume (gb_compute.py:313-314).

4.2 Vacuum relax → GB-plane surface energy

add_vacuum inserts vacuum_length=20 Å along c (grain_boundary.py:1331-1335); the slab is relaxed (gb_with_vacuum_rel, grain_boundary.py:1337-1341); _get_surface_energy (grain_boundary.py:1254-1259) gives σ = (E_vac − E_novac) / A × 16.021766208 / 2 (two exposed surfaces → /2). Stored as surface_energy, with the vacuum slab kept as surface_structure (gb_compute.py:316, 322).

This is the GB-plane surface energy — the free surface exposed by cleaving the boundary slab — not the clean low-index facet of the surface-energy track. Never cross-compare the two.

4.3 Segregation supercell, GB-plane identification

The relaxed GB is expanded to a segregation supercell (create_supercell_with_min_dimensions → relax, grain_boundary.py:1346-1355). The GB plane is located by a Voronoi site featuriser (find_gb_plane(featuriser=voronoi_site_featuriser), grain_boundary.py:1301, 1369-1379), whose Cartesian location (gb_cart) seeds the cleavage scan.

4.4 Rigid + relaxed cleavage → work of separation + decohesion profile

rigid_and_relaxed_cleavage_study (grain_boundary.py:1088-1123) cleaves the vacuum slab on each candidate plane around the GB and computes cleavage energy two ways: rigid (static engine, no relaxation) and relaxed (minimize engine). Per plane, get_results_df (grain_boundary.py:965-1008) computes E_cleave = (E − E_uncleaved) / A × 16.0218 J/m² — no factor of 2 here because a vacuum cell exposes a single GB (grain_boundary.py:996-998). The work of separation is the minimum cleavage energy over the scanned planes (get_min_energy_from_cleavage_study, grain_boundary.py:1436-1439), returned as work_of_separation_rigid / work_of_separation_relaxed (gb_compute.py:318-319).

The kernel derives two extra products from the cleavage dataframes (gb_compute.py:251-284):

Any exception (including a gb_code sys.exit) makes the GB converged=False with a truncated traceback in error (gb_compute.py:328-334); the row is still written (as status='error').


5. Durability: streaming sinks + walltime / CUDA / atom-count caps

A full study is many relaxations and can take an hour+, so the kernel is built to lose at most one in-flight GB on any kill.


6. The DELETE-on-claim queue + resume policy

Work is dispatched from mlip_gb_claim_queue (schema_gb.sql:52-58, PK (potential, structure_id)). process_one (gb_worker.py:209-342) claims by atomically deleting rows under SKIP LOCKED (CLAIM, gb_worker.py:41-45) — shown here in a plain (non-executed) fence:

DELETE FROM mlip_gb_claim_queue
WHERE (potential, structure_id) IN (
  SELECT potential, structure_id FROM mlip_gb_claim_queue
  WHERE potential=%s ORDER BY structure_id LIMIT %s FOR UPDATE SKIP LOCKED)
RETURNING structure_id;

Deleting on claim makes the claim its own lock: no two workers take the same (potential, structure). A CLAIM_FILTERED variant (gb_worker.py:49-53) restricts claims to the GB_ONLY_STRUCTURE_IDS allowlist (gb_worker.py:23-28), leaving every other structure parked in the queue — how a run is focused on the 36 industrial ids without trimming the queue.

Resume skip-set (gb_worker.py:34-39): by default a GB already at status ∈ {done, error, timeout} is skipped on re-run; GB_RETRY_ERRORS=1 drops error from the skip-set, GB_RETRY_TIMEOUTS=1 drops timeout, and GB_RECOMPUTE_ALL=1 recomputes everything (the UPSERT overwrites in place — no DELETE needed). Excluded elements (default noble gases, gb_worker.py:20-21) simply leave the compute set.


7. Schema & the two row types (schema_gb.sql)


8. Coverage clamp metric (gb_status.py, and the export)

The naive metric Σ(terminal rows)/Σ(n_gb_total) exceeds 100 % because the numerator is inflated by (a) legacy enumeration-wave rows and (b) duplicate gb_id representations of the same physical GB (the gb_id embeds raw GB1/GB2 plane vectors, and different waves wrote equivalent planes under different conventions) — gb_status.py:5-15. Because those duplicates don't string-match across conventions, intersecting a freshly-built canonical set is also unreliable.

The fix (clamp). A structure cannot be more than 100 % done, so clamp the distinct terminal count at n_gb_total per structure (gb_status.py:16-23):

coverage = Σ_struct min(distinct_terminal_gb_id, n_gb_total) / Σ_struct n_gb_total   (≤ 100%)

Implemented as sum(LEAST(term, n_gb_total)) / sum(n_gb_total) over terminal statuses {done, error, timeout} (gb_status.py:69-83, LEAST at :79). It is an upper bound — extras can pad up to the cap but never past it. structs_full counts structures at the cap. The same clamp restricted to the 36 industrial ids is clamp_pct_industrial, the benchmark gate, computed in the export's pot_summary_sql (export_gb_to_parquet.py:128-169, industrial LEAST(...) at :140) and surfaced per potential in gb_potential_summary.parquet. gb_status.py hard-codes the 36 IDS_DEFAULT and the 15 KEEP (gb_status.py:35-41); the export mirrors them in INDUSTRIAL_IDS / KEEP_POTENTIALS (export_gb_to_parquet.py:38-46).


9. Consensus (the only cross-model signal) — tools/regen_gb_from_export.py

With no DFT truth, the site scores each potential by deviation from the KEEP consensus. The site tool tools/regen_gb_from_export.py reads the parquet export and writes the vendored CSVs under src/assets/elemental/gb/.


10. Known caveats & provenance notes


11. Reproduce one boundary

PY=python   # env with pyiron_workflow_atomistics + a model calculator
from benchmark.db import connect, from_bytea
from benchmark.gb_compute import compute_gb
from benchmark.worker import _resolve_factory
calc, _ = _resolve_factory("mace_medium-omat-0")()
with connect() as c, c.cursor() as cur:
    cur.execute("SELECT element, basis FROM mlip_gb_eligible WHERE structure_id=%s", (6,))
    element, basis = cur.fetchone()
    cur.execute("SELECT atoms_pickle FROM structure WHERE id=%s", (6,))
    atoms = from_bytea(cur.fetchone()[0])
res = compute_gb(element, basis, atoms, calc, scratch_root="/tmp/gb_demo")  # EOS → build → study
print(res["n_gb_total"], [g["gb_energy"] for g in res["gbs"]])

Inputs that fully determine the rows: the MP cell + (element, basis), the potential's calculator, the fixed study params (gb_compute.py:146-150), and the CSL enumeration at the model's own a0. The DB sinks, caps, and queue are durability plumbing around this deterministic core.


See the leaderboard → · Readable methodology → · Repo: ligerzero-ai/mlip-elemental-benchmark → · ← Methodology overview