T1 — A complete local run#
Goal: run the whole funnel to completion on one machine, and be able to explain every number it reports — including the materials it rejects.
Prerequisites: MatMelt installed, and SMACT, PyXtal and MACE importable by the interpreter you point the stages at (The tool and the science stack). If you followed Your first run, you already have everything.
Time: about 15 minutes, most of it compute.
Every output on this page comes from the run described here.
1. Configure#
Start in a clean directory:
$ mkdir nacl-run && cd nacl-run
$ matmelt init
Edit matmelt.yaml so the run is small enough to finish while you watch it:
run:
name: nacl-binaries
work_dir: ./materials-work
elements: [Na, Cl]
combination_sizes: [2]
stages:
screen:
backend: laptop
options:
python: python3
device: cpu
model_size: small
Leave pipeline.stop_after: extract as scaffolded — this run goes all the way.
Confirm the scope before spending anything:
$ matmelt doctor
Seed enumeration: 2 element(s) -> 1 combination(s) (size 2: 1).
doctor: all checks passed.
One seed. That is the whole input to the funnel.
2. Run it#
$ matmelt run --yes
Run nacl-binaries: compose -> structure -> screen -> extract
Seed enumeration: 2 element(s) -> 1 combination(s) (size 2: 1).
foundation model (size=small) ready in ~/.cache/mace
Seeded 1 combination(s); starting the drain loop.
pass 1: submitted=1
pass 2: expanded=1, children=1, submitted=1
...
pass 327: completed=2, finalized=2, submitted=1
pass 328: completed=1, finalized=1
Materials by state
┏━━━━━━━━━━┳━━━━━━━┓
┃ State ┃ Count ┃
┡━━━━━━━━━━╇━━━━━━━┩
│ DONE │ 28 │
│ EXPANDED │ 2 │
│ FAILED │ 9 │
└──────────┴───────┘
run complete: every material is settled.
Around ten minutes, two jobs in flight at a time (max_parallel: 2). The pass
lines only appear when something happened, so a quiet run is a quiet screen.
Read that final table carefully — it is the point of the tutorial.
28 DONE. Structures that relaxed, passed screening and became records.
2 EXPANDED. The seed
Cl-Naand the compositionCl1Na1. Both fanned out into children and are terminal — this is success, not failure. A parent is retired once its children exist.9 FAILED. Candidates that were screened out. Not crashes.
3. Read the failures#
$ matmelt status
Failed materials
┏━━━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Material ┃ Stage ┃ Attempts ┃ Reason ┃
┡━━━━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ Cl1Na1_3D-16 │ screen │ 1 │ screened out: volume changed by +88.7% │
│ │ │ │ during relaxation (threshold +/-60.0%) │
│ │ │ │ -- structure collapsed or exploded │
│ Cl1Na1_3D-81 │ screen │ 1 │ screened out: residual stress 7.258 GPa │
│ │ │ │ > 2.0 GPa -- unit cell not mechanically │
│ │ │ │ equilibrated │
│ Cl1Na1_3D-149 │ screen │ 1 │ screened out: energy rose by 1.05398 │
│ │ │ │ eV/atom during relaxation -- the │
│ │ │ │ optimizer walked uphill, suggesting │
│ │ │ │ gradient confusion │
Three things to notice.
The reasons are physical, and quantified. A random symmetry-constrained starting guess often relaxes into something unphysical — a cell that nearly doubles in volume, or one still under 7 GPa of residual stress. Rejecting those is the screen stage doing its job.
Attempts is 1. These were never retried, because they are not faults.
Re-running the same relaxation gives the same answer, so a terminal scientific
verdict is recorded once and the budget is left for infrastructure problems.
The run’s exit code is 0. Nine rejections out of 37 is a result, not an error. Only a broken run — an unreachable backend, a missing dependency — makes the command itself fail.
4. Collect the results#
$ matmelt results
Finished materials
┏━━━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━┓
┃ Material ┃ Parent ┃ Record ┃
┡━━━━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━┩
│ Cl1Na1_3D-1 │ Cl1Na1 │ yes │
│ Cl1Na1_3D-2 │ Cl1Na1 │ yes │
...
28 DONE material(s), 28 with extracted record(s).
$ matmelt results --export records.json
Exported 28 record(s) to records.json.
records.json is a JSON array of {"id", "record"} objects, each record
OPTIMADE-shaped with MatMelt’s provenance under _mcli. It is ordinary JSON, so
ranking the survivors by relaxed energy is a few lines:
import json
records = json.load(open("records.json"))
ranked = sorted(
(r["record"]["attributes"]["_mcli"]["screen"]["final"]["e_eV_atom"], r["id"])
for r in records
)
for energy, material_id in ranked[:3]:
print(f"{material_id:<16} {energy:.4f} eV/atom")
Cl1Na1_3D-2 -3.3806 eV/atom
Cl1Na1_3D-1 -3.3805 eV/atom
Cl1Na1_3D-187 -3.3167 eV/atom
Two candidates from different starting space groups relaxed to essentially the same energy — they found the same structure by different routes, which is the normal and expected outcome of a symmetry-constrained search.
Warning
These energies come from a MACE foundation model, and the small one at that.
They are a screening signal for ranking candidates and discarding nonsense —
not converged formation energies, and not a substitute for DFT. The optional
calculate stage exists for higher-fidelity follow-up.
5. Understand what is on disk#
$ ls materials-work/
Cl1Na1 Cl1Na1_3D-1 Cl1Na1_3D-10 ... Cl1Na1_3D-99 Cl-Na state.sqlite
One directory per material — including the seed Cl-Na and the composition
Cl1Na1 that fanned out — plus the state database. A
finished material holds the whole chain of evidence:
$ ls materials-work/Cl1Na1_3D-221/
record.json relaxed.cif screen_result.json structure.cif structure.json
structure.cif is what PyXtal generated, relaxed.cif what MACE relaxed it
into, screen_result.json the verdict with its metrics, and record.json the
portable record. Nothing is hidden in a database that is not also on disk as a
file you can read.
What you learned#
A run’s three outcomes are different things:
DONEis a finished material,EXPANDEDis a parent that fanned out, andFAILEDatscreenis usually a scientific rejection with a quantified reason.Science rejections are results — attempts stay at 1, and the command exits 0.
The funnel’s shape: 1 seed → 1 composition → 37 candidates → 28 survivors.
Records export as plain JSON that ranks with a few lines of Python.
Screening energies are a ranking signal, not converged thermodynamics.
Next: T2 explains every configuration key you just used, and T3 moves the expensive stage onto another machine. To get the records out of this run, see Get the results out.