---
description: Take an element set through the complete funnel on your own machine, and read every outcome it produces.
---

# 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
({doc}`../getting-started/dependencies`). If you followed
{doc}`../getting-started/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:

```console
$ mkdir nacl-run && cd nacl-run
$ matmelt init
```

Edit `matmelt.yaml` so the run is small enough to finish while you watch it:

```yaml
run:
  name: nacl-binaries
  work_dir: ./materials-work
  elements: [Na, Cl]
  combination_sizes: [2]
```

```yaml
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:

```console
$ 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

```console
$ matmelt run --yes
```

```text
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

```console
$ matmelt status
```

```text
                                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

```console
$ matmelt results
```

```text
        Finished materials
┏━━━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━┓
┃ Material      ┃ Parent ┃ Record ┃
┡━━━━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━┩
│ Cl1Na1_3D-1   │ Cl1Na1 │ yes    │
│ Cl1Na1_3D-2   │ Cl1Na1 │ yes    │
...
28 DONE material(s), 28 with extracted record(s).
```

```console
$ 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:

```python
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")
```

```text
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

```console
$ 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:

```console
$ 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: {doc}`T2 <t2-config-in-depth>` explains every configuration key you just
used, and {doc}`T3 <t3-offload-a-stage>` moves the expensive stage onto another
machine. To get the records out of this run, see
{doc}`../how-to/export-results`.
