| name | clari |
| description | Run Clari crystal structure inference, batch sampling, ranking, and CIF export. Use for requests to sample crystal candidates from SMILES, run `sample`, `rank`, or `export-cifs`, prepare current-format inference configs, or explain the simplified inference package in `clari/inference/`. |
Clari inference
Clari predicts organic crystal structures from molecular SMILES. The workflow has three steps:
clari — samples candidate crystal structures, writes predictions.parquet, and (by default) exports .cif files
rank — scores each candidate with FairChem UMA energy, writes rankings.csv
export-cifs — (re-)exports .cif files from saved samples, optionally filtered by rank/id/index
clari exports CIFs automatically after sampling; pass --no-export-cifs to skip
it and write only predictions.parquet. The standalone export-cifs command is
still used to re-export later, or to export a ranked subset (--top-k).
Package layout
clari/inference/core.py — CLI entrypoint (clari command), argument parsing, calls sample()
clari/inference/inputs.py — SampleRequest dataclass, build_request(), config/CLI parsing, Hub checkpoint resolution
clari/inference/sample.py — ClariSampler class, model loading, OOM-safe batch sampling, shard writing, multi-GPU dispatch
clari/inference/rank.py — rank() wrapper: calls compute_energies, joins with predictions, writes rankings.csv
clari/inference/export.py — export_cifs(): reads parquet, optionally filters by rank/id/index, writes .cif files
Do not reference deleted modules: cli.py, sampler.py, runner.py, io.py.
Key concepts
--copies (Z value): Number of molecules per unit cell. Default is 4, which covers the most common organic packing motifs. Use smaller values (1–2) for large molecules or to explore low-Z polymorphs. For multi-component requests (co-crystals), copies is per-component.
SampleRequest: The core data object. Fields:
smiles: str | list[tuple[str, int]] — single-component SMILES string, or list of (smiles, copies) pairs for co-crystals
id: str | None = None — run identifier, used as subdirectory name and row key; auto-generated from SMILES if omitted
copies: int = 4 — molecules per unit cell (single-component only; ignored for co-crystal form)
samples: int = 1 — how many candidate structures to generate
Available models
| Name | Notes |
|---|
clari-m | Medium — fastest, good for exploration |
clari-l | Large |
clari-h | Huge — highest quality, slowest (default) |
Downloaded automatically from HuggingFace (the-matter-lab/clari) on first use. Pass a local .ckpt path to use a custom checkpoint.
CLI — sampling
The input grammar (one rule)
A request is a flat list of (component, copies) pairs. Dots in a SMILES always
split into components; a copies value broadcasts over the dot components of its
token; omitted copies default to 4. Hydrogens are added automatically (AddHs).
uv run clari "CCO" --samples 10
uv run clari "CCO" 1 "O" 3 --samples 8 --output-dir results/ethanol_trihydrate
uv run clari "CCO.O" 2
The --smiles/--copies flag form is a pure synonym of the positional form
(use one or the other, not both):
uv run clari --smiles "CC(=O)Oc1ccccc1C(=O)O" --copies 1 --smiles "O" --copies 3 --samples 8
uv run clari --smiles "CCO.O" --copies 1 --copies 3
Repeated SMILES in one CLI call describes one composition, not multiple
independent jobs — use --config for batches.
Writes (default --output-dir is results/<id>):
<output_dir>/predictions.parquet — one row per sample: id, sample_idx, cif
<output_dir>/config.json — full run config for reproducibility
<output_dir>/cifs/<id>/ — .cif files, exported by default (skip with --no-export-cifs)
Batch via config file
For multiple independent requests, use --config. Batch configs write one
result directory per request, not one mixed parquet:
uv run clari --config batch.json
Config schema:
{
"model": "clari-m",
"output_dir": "results/batch_run",
"requests": [
{
"id": "ethanol",
"smiles": "CCO",
"copies": 4,
"samples": 4
},
{
"id": "aspirin_trihydrate",
"smiles": [
["CC(=O)Oc1ccccc1C(=O)O", 1],
["O", 3]
],
"samples": 4
}
]
}
Top-level config keys (all optional, override CLI defaults):
model — model name (clari-m, clari-l, clari-h)
output_dir — where to write results
use_ema, use_bf16, pbar — booleans
Per-request keys: id, smiles, copies, samples, batch_size.
Output layout:
results/batch_run/
manifest.json
ethanol/
predictions.parquet
config.json
aspirin_trihydrate/
predictions.parquet
config.json
All CLI flags
| Flag | Default | Description |
|---|
| positional | — | SMILES [copies] [SMILES [copies]]... |
--smiles | — | SMILES string (repeatable; synonym of positional form) |
--copies | 4 | Molecules per unit cell (repeatable, matched by index to --smiles) |
--samples | 1 | Number of candidate structures to generate |
--id | auto | Labels every row in predictions.parquet and becomes the CIF subdirectory name. Prefer setting it explicitly — the auto-generated SMILES-based name is cryptic and can collide. Valid characters: letters, digits, ., _, -; others are replaced with _. Max 80 chars. |
--config | — | Path to batch JSON config (mutually exclusive with direct SMILES) |
--model | clari-h | Model name (clari-m, clari-l, clari-h) |
--output-dir | results/<id> | Directory to write results |
--batch-size | auto | Samples per forward pass; auto-scaled to GPU memory if unset |
--num-gpus | 1 | Number of GPUs (requires --output-dir) |
--device | auto | cuda, mps, cpu, or cuda:N |
--n-steps | 50 | Flow matching steps |
--seed | none | Random seed for reproducible sampling (multi-GPU workers use seed + rank). Config-level seed is ignored; pass --seed on the CLI. |
--torch-threads | 1 | CPU thread count |
--compile | off | Enable torch.compile (off by default; gives a meaningful speedup on CUDA after cold-start) |
--filter-clashing | off | Discard clashing structures and resample replacements |
--overwrite | off | Overwrite existing output directory |
--no-ema | off | Use raw weights instead of EMA weights |
--no-bf16 | off | Disable bfloat16 (CUDA only) |
--no-pbar | off | Suppress progress bar |
--no-export-cifs | off | Skip automatic CIF export; write only predictions.parquet |
CLI — ranking
Requires fairchem-core. Install with pip install "clari[uma]" or uv sync --extra uma.
uv run rank results/ethanol
Writes:
results/ethanol/energies.csv — sample_idx, energies (UMA energy per structure)
results/ethanol/rankings.csv — sample_idx, id, energies, rank (0-based rank within each id group)
Flags: --batch-size 32, --num-gpus 1, --torch-threads 1, --overwrite.
CLI — export
uv run export-cifs results/ethanol
uv run export-cifs results/ethanol --top-k 3
uv run export-cifs results/ethanol --sample-idx 0 --sample-idx 2
uv run export-cifs results/batch_run/ethanol
uv run export-cifs results/ethanol --output-dir my_cifs/
export-cifs works with or without rankings.csv. Without it, all samples are exported and named by index. With it, filenames include the rank and --top-k filtering becomes available.
Writes CIFs to <output_dir>/<id>/:
- Without rankings:
sample_000000.cif
- With rankings:
rank_0000_sample_000000.cif
Flags: --output-dir, --rankings-path, --top-k, --ids (repeatable), --sample-idx (repeatable), --overwrite.
Python API
from clari.inference import ClariSampler
sampler = ClariSampler("clari-m")
crystals = sampler.sample("CCO", id="ethanol", samples=8)
sampler.sample("CCO", id="ethanol", samples=8, output_dir="results/ethanol")
sampler.sample("CCO.O", id="ethanol_hydrate", copies=2, samples=4)
sampler.sample(
["CC(=O)Oc1ccccc1C(=O)O", "O"],
id="aspirin_trihydrate",
copies=[1, 3],
samples=4,
output_dir="results/aspirin_trihydrate",
)
sample() keyword arguments:
id — run identifier; auto-generated from SMILES if omitted
copies: int | list[int] = 4 — molecules per unit cell; int for uniform, list for per-component
samples: int = 1
output_dir — if set, writes to disk and returns the output Path; predictions at <output_dir>/predictions.parquet
filter_clashing: bool = False — top-level helper only; discards clashing structures and resamples replacements
ClariSampler constructor: checkpoint ("clari-m", "clari-l", or "clari-h"; default "clari-h"), device (default "auto"), use_ema (default True), use_bf16 (default True), n_steps (default 50), compile (default False), num_gpus (default 1), seed (default None; set for reproducible sampling), filter_clashing (default False; when True, discards clashing structures and resamples replacements).
Ranking and export from Python
from clari.inference import rank, export_cifs
df = rank("results/ethanol")
crystals = sampler.sample("CCO", id="ethanol", samples=8)
df = rank(crystals)
export_cifs("results/ethanol")
export_cifs("results/ethanol", top_k=3)
export_cifs("results/ethanol", sample_idx=[0, 2])
export_cifs("results/ethanol", output_dir="my_cifs/ethanol")
crystals = sampler.sample("CCO", id="ethanol", samples=8)
export_cifs(crystals, output_dir="my_cifs/", id="ethanol")
Output files reference
| File | Written by | Contents |
|---|
predictions.parquet | clari | id, sample_idx, cif |
config.json | clari | Full run config for reproducibility |
energies.csv | rank | sample_idx, energies |
rankings.csv | rank | sample_idx, id, energies, rank (0-based within id group) |
cifs/<id>/ | clari (default) / export-cifs | .cif files, named by rank and/or sample index |
Operational notes
- Use
uv run from the source checkout, or install the package and call the entry points directly.
- Without
output_dir, sample() holds everything in memory and returns list[Crystal].
- With
output_dir, results are written incrementally as shards and merged at the end; safe to interrupt and re-run with --overwrite.
- Multi-GPU sampling (
num_gpus > 1) requires output_dir and CUDA.
- OOM is handled automatically: batch size halves and retries until it fits.
--compile is off by default. Pass --compile (CLI) or compile=True (Python) to enable; gives a meaningful speedup on CUDA after cold-start compilation.
rank path requires fairchem-core (clari[uma]); sampling and export do not.