---
description: Scaffold a config, generate candidate structures, and screen one into a finished record.
---

# Your first run

This walkthrough goes from an empty directory to a screened crystal structure
with an OPTIMADE record. It takes about a minute of compute. Every output below
is real — only absolute paths have been shortened.

It assumes `matmelt --version` works ({doc}`installation`) and that SMACT,
PyXtal and MACE are importable by some interpreter ({doc}`dependencies`).

## 1. Scaffold a config

```console
$ mkdir matmelt-demo && cd matmelt-demo
$ matmelt init
```

`init` writes a commented `matmelt.yaml` and immediately runs the environment
check against it, so you find out at once whether this machine can do the work:

```text
Wrote starter config: matmelt.yaml
Edit run.elements and the backends/stages blocks for your machines; re-run
'matmelt doctor' after any change.
Checking the environment against it now:
...
│ compose   │ smact       │ python3     │ local │ ok     │ smact importable    │
│ structure │ pyxtal      │ python3     │ local │ ok     │ pyxtal importable   │
│ screen    │ mace        │ python3     │ local │ ok     │ mace importable     │
│ extract   │ stdlib only │ python3     │ local │ ok     │ standard library    │
│           │             │             │       │        │ only -- always      │
│           │             │             │       │        │ ready               │
└───────────┴─────────────┴─────────────┴───────┴────────┴─────────────────────┘
Seed enumeration: 2 element(s) -> 3 combination(s) (size 1: 2, size 2: 1).
doctor: all checks passed.
```

If any stage says `MISSING`, fix it before continuing — the command to run is
printed under the table.

## 2. Make three edits

Open `matmelt.yaml`. The scaffold already targets sodium and chlorine, which is
what we want. Change three things:

```yaml
run:
  name: my-first-run
  work_dir: ./materials-work
  elements: [Na, Cl]
  combination_sizes: [2]          # binaries only, so this run stays small

pipeline:
  stop_after: structure           # stop after generating candidates
```

```yaml
stages:
  screen:
    backend: laptop
    options:
      python: python3
      device: cpu
      model_size: small           # ~32 MB; the default 'medium' is better science
```

`combination_sizes: [2]` narrows the seeds from three (Na, Cl, and Na–Cl) to
just the binary. `stop_after: structure` deliberately stops the run before
screening, so you can look at what was generated before spending compute on it.
`model_size: small` keeps the first model download small.

Check the edits:

```console
$ matmelt doctor
doctor: all checks passed.
```

## 3. Generate candidate structures

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

```text
Run my-first-run: compose -> structure
Seed enumeration: 2 element(s) -> 1 combination(s) (size 2: 1).
Seeded 1 combination(s); starting the drain loop.
pass 1: submitted=1
pass 2: expanded=1, children=1, submitted=1
pass 8: expanded=1, children=37
  Materials by state
┏━━━━━━━━━━━━┳━━━━━━━┓
┃ State      ┃ Count ┃
┡━━━━━━━━━━━━╇━━━━━━━┩
│ EXPANDED   │     2 │
│ STRUCTURED │    37 │
└────────────┴───────┘
stop_after structure: stage drained; materials rest at its output, ready for the
next stage -- raise or remove stop_after (or use --only) to continue them.
```

About ten seconds. Read that summary carefully, because it shows the funnel's
shape:

- One seed, `Cl-Na`, went into `compose`, which asked SMACT which
  charge-balanced compositions are plausible and produced one: `Cl1Na1`.
- That composition went into `structure`, which asked PyXtal for a candidate
  crystal in each 3D space group and got **37** valid ones.
- The two parents are `EXPANDED` — a terminal state meaning "this fanned out
  into children", not a failure.
- The 37 candidates **rest** at `STRUCTURED`. A mid-funnel `stop_after` is a
  pause, not a retirement: they are live and claimable by a later run.

## 4. Look at what you have

```console
$ matmelt status --state STRUCTURED
```

```text
Run my-first-run: 37 material(s) at STRUCTURED.
 Materials at STRUCTURED
┏━━━━━━━━━━━━━━━┳━━━━━━━━┓
┃ Material      ┃ Parent ┃
┡━━━━━━━━━━━━━━━╇━━━━━━━━┩
│ Cl1Na1_3D-1   │ Cl1Na1 │
│ Cl1Na1_3D-2   │ Cl1Na1 │
│ Cl1Na1_3D-3   │ Cl1Na1 │
│ Cl1Na1_3D-6   │ Cl1Na1 │
│ Cl1Na1_3D-10  │ Cl1Na1 │
...
```

Each id says what the material is: composition, dimensionality, and space-group
number. `Cl1Na1_3D-221` is the candidate in space group 221, *Pm-3m*.

On disk, each material has its own directory:

```console
$ ls materials-work/Cl1Na1_3D-221/
structure.cif  structure.json
```

## 5. Screen one candidate

Screening all 37 would take a few minutes. Push a single one through instead —
`--materials` restricts a run to the materials you name (and anything they fan
out into). First raise the stop stage so the run may go all the way:

```yaml
pipeline:
  stop_after: extract
```

```console
$ matmelt run --materials Cl1Na1_3D-221 --yes
```

```text
Run my-first-run: compose -> structure -> screen -> extract
Seed enumeration: 2 element(s) -> 1 combination(s) (size 2: 1).
foundation model (size=small) ready in ~/.cache/mace
--materials: no seed combination matched -- relying on materials already in the
store.
pass 1: submitted=1
pass 8: completed=1, submitted=1
pass 9: completed=1, finalized=1
  Materials by state
┏━━━━━━━━━━━━┳━━━━━━━┓
┃ State      ┃ Count ┃
┡━━━━━━━━━━━━╇━━━━━━━┩
│ DONE       │     1 │
│ EXPANDED   │     2 │
│ STRUCTURED │    36 │
└────────────┴───────┘
--materials: the selected materials are settled; unselected materials were not
run.
```

About fifteen seconds: one MACE relaxation, then extraction into a record. The
other 36 candidates are untouched and still waiting.

:::{note}
`--yes` answers the model-download consent prompt in advance. Run without it and
MatMelt asks first, disclosing the size and the cache location; declining stops
the run cleanly rather than failing partway.
:::

## 6. Read the result

```console
$ matmelt results
```

```text
Results of run my-first-run:
        Finished materials
┏━━━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━┓
┃ Material      ┃ Parent ┃ Record ┃
┡━━━━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━┩
│ Cl1Na1_3D-221 │ Cl1Na1 │ yes    │
└───────────────┴────────┴────────┘
1 DONE material(s), 1 with extracted record(s).
```

The material's directory now holds the whole story:

```console
$ ls materials-work/Cl1Na1_3D-221/
record.json  relaxed.cif  screen_result.json  structure.cif  structure.json
```

`screen_result.json` is the screening verdict — what was decided, and on what
evidence:

```json
{
  "category": "RELAXED",
  "converged": true,
  "n_steps": 5,
  "final": {
    "e_eV_atom": -3.2325740511589527,
    "fmax_eV_A": 3.4855789502953297e-15,
    "stress_max_GPa": 0.09507366185438959,
    "volume_A3": 41.89601877227525,
    "min_bond_A": 3.007841059579302
  }
}
```

`RELAXED` means the relaxation converged and the relaxed cell passed the
physical sanity checks — bond lengths, residual stress, volume change and energy
all within bounds. A candidate that fails those gets `FAILED` with the reasons
listed, which is a *result*, not an error: the run continues and its exit code
stays 0.

`relaxed.cif` is the relaxed structure. `record.json` is the OPTIMADE-shaped
record — composition fields, lattice vectors, sites, and MatMelt's own
provenance under `_mcli`:

```json
{
  "dimension": "3D",
  "space_group": 221,
  "space_group_symbol": "Pm-3m",
  "lattice_type": "cubic",
  "formula": "Cl1Na1"
}
```

To collect every finished record into one file:

```console
$ matmelt results --export records.json
Exported 1 record(s) to records.json.
```

## What you just did

You ran a four-stage pipeline: SMACT proposed a composition, PyXtal generated 37
symmetry-constrained candidates, MACE relaxed one of them and judged it, and the
extract stage turned the survivor into a portable record. Everything ran on your
own machine, in a workspace you can inspect, resume or throw away.

The 36 untouched candidates are the natural next step: remove `--materials` and
run again, and MatMelt picks up exactly where it left off.

Next: {doc}`verify` — a short checklist proving the install is sound. Then
the tutorials, which widen the element set
({doc}`T1 <../tutorials/t1-first-local-run>`), explain every config key
({doc}`T2 <../tutorials/t2-config-in-depth>`), and move the expensive stage
onto another machine ({doc}`T3 <../tutorials/t3-offload-a-stage>`).
