Material states#
Every candidate material carries exactly one state, and that state is what the
orchestrator claims work from. There are nine, and knowing which three are
terminal explains most of what status shows you.
The nine#
State |
Set by |
Terminal |
Meaning |
|---|---|---|---|
|
seeding, or fan-out adoption |
no |
An element-combination seed, or a freshly adopted child, waiting for its first stage. |
|
|
no |
A charge-balanced composition exists. |
|
|
no |
A candidate crystal structure exists. |
|
|
no |
The MACE relaxation converged and the verdict was |
|
|
no |
The DFTB+ WorkChain converged. Runs without the optional stage never park here. |
|
|
no |
|
|
finalize |
yes |
The material completed the configured funnel. |
|
a verdict or an exhausted budget |
yes |
Terminally rejected or terminally faulted; |
|
fan-out settlement |
yes |
A parent whose children were all adopted. Not a failure. |
The six non-terminal states form one chain, in funnel order:
NEW → COMPOSED → STRUCTURED → SCREENED → CALCULATED → EXTRACTED → DONE
A stage claims materials in the state before it and completes them into the state after it. Movement along the chain is strictly forward — nothing regresses, and a completion that has already been recorded is a no-op, so an interrupted run replays safely.
A skipped stage skips its state. With no calculate: in your config,
screen completes a material straight from SCREENED to EXTRACTED; nothing
ever parks in CALCULATED. The orchestrator derives each stage’s input state
from the funnel you configured, not from a fixed position in the chain.
The three terminal states, and why they differ#
DONE is retirement, and it is deliberate. A material becomes DONE only
when the run executes through the configured last stage. A run stopped
mid-funnel — by pipeline.stop_after or by run --only <stage> — leaves its
materials resting at that stage’s output state, live and claimable, so a
later run continues them in the same workspace. This is why a stop_after: structure run leaves everything at STRUCTURED rather than retiring it: those
materials still have a funnel ahead of them.
FAILED covers two different things, and the difference is visible in the
attempt count:
Cause |
Attempts |
Example |
|---|---|---|
A deterministic science verdict |
|
MACE relaxed the cell and the result is unphysical; a DFTB+ WorkChain exhausted the plugin’s own healing ladders. |
A fault that spent |
|
A missing input file, a crashed job, an unreachable backend. |
A verdict spends no budget, because re-running reproduces it. A fault might not
be reproducible, so it is retried — and when in doubt, the tool treats an
outcome as a fault rather than a verdict. Both end in FAILED, and matmelt retry brings either back.
EXPANDED is a success. compose and structure are fan-out stages: one
seed becomes several compositions, one composition becomes several candidate
crystals. The parent has no further work of its own, so it retires as EXPANDED
once all its children are adopted. Its children ride the funnel individually,
each addressable by id, and run --materials <parent-id> reaches them through
lineage.
Seeing where materials are#
$ matmelt status
$ matmelt status --state STRUCTURED
$ matmelt status --json
status alone gives the tally per state, the failures with their stage and
reason, and any jobs in flight. --state <STATE> lists the material ids in
one state — the picking list for run --materials. monitor shows the same
information as a live dashboard.
Moving materials back#
Terminal is terminal: no stage claims a DONE, FAILED or EXPANDED
material. One command deliberately reverses that, and only for failures:
$ matmelt retry --dry-run
$ matmelt retry --stage structure --yes
retry resets FAILED materials to the state they should resume from, derived
per material from its own completion timeline — the output state of the last
stage it actually completed, or NEW if it never completed one. That derivation
uses no config, so it is right even if you have since changed the funnel. The
reset clears the lease, the attempt count and the recorded error, and
preserves the completions timeline, so the material’s history stays intact.
Nothing resets DONE or EXPANDED. A finished material is re-run by pointing a
stage at it explicitly (run --only <stage> --materials <id>), which is a
different operation with a different meaning.
Durability#
State lives in a single SQLite database under run.work_dir, with the
write-ahead log on and every read-then-write in an immediate transaction.
Claiming is atomic, so a material is never worked twice; a claim that outlives
pipeline.lease_timeout_s returns to its queue.
Alongside state, an in-flight ledger records every submitted job’s durable handle — a PID or a scheduler job id — at the moment of submission. That is what makes an interrupted run resumable: a fresh process re-polls the jobs the old one started instead of launching duplicates, so work already running on a cluster is picked back up rather than repeated.