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-Na and the composition Cl1Na1. 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: DONE is a finished material, EXPANDED is a parent that fanned out, and FAILED at screen is 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.