Configuration¶
Atom-HiFi is config-driven (ADR-0005, local-only). A single commented YAML
template in atom_hifi.config is the source of truth for the public CLI
surface: it is written verbatim by atom-hifi init, parsed for YAML defaults,
and used as the strict allow-list for validation (unknown keys are rejected).
Expert-only controls for ND grid scan and patient/OOD are accepted through the
Python API, not YAML.
Build a config for your system¶
The first job of config.yaml is to translate your scientific question into an
Atom-HiFi run: which structures define the library, which atoms define coverage,
which descriptor measures local environments, and which clustering/search
strategy should choose the representative set. Start from the generated
template, fill in the system identity fields, then choose the backend and
multi-element search path deliberately.
Generate a starter file:
atom-hifi init config.yaml
Then edit the minimum system block:
paths:
lib_path: train_structs.xyz # ASE-readable structure library
focus_elements: [Ni, O] # elements whose local environments matter
output_dir: fr_results_NiO
descriptor:
kind: eds # eds for built-in SOAP, ace for MACE ACE
Choose these four values from your system:
Question |
Config key |
First-run choice |
|---|---|---|
Where is the structure library? |
|
Path to an ASE-readable file such as |
Which atoms should define coverage? |
|
The species whose local environments you want represented; use |
Where should this run write caches and results? |
|
A fresh directory name for this system and descriptor choice |
Which descriptor should be used? |
|
|
Applied example: multi-element adsorbate library¶
For a NOx/H adsorbate library on a surface, the scientifically relevant local states may be the adsorbate atoms rather than every support atom. A production config can therefore focus on N/O/H, use ED-SOAP, let HDBSCAN keep distinct noise environments, and use NSGA-II to pick separate c-factors for each element:
paths:
lib_path: all_structures_cycle-3.xyz
focus_elements: [N, O, H]
output_dir: hdbscan_results
descriptor:
kind: eds
eds:
species: null
lengthscale: 1.1
periodic: true
cluster:
backend: hdbscan
min_cluster_size: 2
selection:
method: mu_tiebreak
microstate:
reference: r100
eps_noise:
method: thermal
scan:
l_tol: 0.95
n_coarse: 60
n_fine: 60
nsga2:
run: true
n_gen: 50
pop_size: 60
piecing:
enabled: false
output:
extract_surroundings: true
Read this as a set of modelling choices:
Config group |
What it means in this example |
|---|---|
|
Coverage is judged on adsorbate atoms. Surface/support atoms still appear in descriptors and output structures, but they do not define the set-cover targets. |
|
Use the built-in ED-SOAP descriptor. |
|
Partition the ED-SOAP embedding with a distance-kind backend that can mark sparse/outlier environments as noise; Atom-HiFi deduplicates those noise environments and forces representatives for them. |
|
Use an ε_noise-radius KMeans reference (or |
|
For three focus elements, choose independent c-factors for N/O/H instead of forcing one common resolution scale. |
|
Write one representative environment per final group, useful for inspecting chemical motifs. |
The remaining groups control runtime, restart behaviour, and inspection output:
Config group |
Applied setting |
Meaning |
|---|---|---|
|
|
Use 60 workers for general parallel stages and descriptor computation; |
|
|
|
|
|
Keep the default Likeness floor: selected points must preserve at least 95% of full-library microstate entropy. Set |
|
|
Skip active-learning and run on the full library. All built-in backends support the loop. OOD need not decay to zero, so a loop may consume the full library. HDBSCAN’s promoted noise representatives remain outside its deployable density targets. |
|
|
Recompute the Pareto front. Set |
|
|
Treat each input structure as one selectable item. This is right for a normal many-structure adsorbate library. |
|
|
Let Atom-HiFi pick the operating point automatically from the scan/NSGA-II result. Set manually only for deliberate refits. |
|
|
Write the fitted |
|
|
Save the original periodic structure with center/neighbour/rest tags for one representative per group. |
|
|
Run the full scan. Set to a c-factor from |
Choosing the backend¶
cluster.backend decides how local atomic states are formed from the embedded
descriptor cloud. Pick it from the workflow you need, not from speed alone:
Backend |
Use when |
Avoid when |
|---|---|---|
|
You want the default, deployable mean-shift path; you may need active-learning loop or patient/OOD classification. |
Very large scans where speed is the main bottleneck and you do not need deployable classification. |
|
You want density clustering with noise representatives for selection and frozen density attachment for deployment. See prediction semantics. |
Noise representatives must be accepted deployment targets: promotion supplies no density support. NumPy 2.4+ remains refused for the positive-epsilon fitting path. |
|
You want a fast count-kind selector, compact covers, NSGA-II over per-element k-fractions, or nearest-centroid OOD/patient/loop classification. |
You need grid c-factor optimisation or ε-anchored distance-bandwidth interpretation. Its loop may consume the full library when resolution-tracking OOD remains positive. |
Distance-kind backends (decaf, hdbscan) sweep c_factor, meaning the actual
per-element clustering bandwidth is δ_el = c_el × ε_noise_el. Count-kind
backends (kmeans) sweep how many clusters to use, so c-factor logic does not
apply; NSGA-II instead optimises an independent per-element k-fraction.
Multi-element search: why NSGA-II is the default¶
With more than one focus element, one shared granularity can be too coarse for
one element and too fine for another. nsga2.run: true searches per-element
c-factors for distance-kind backends or per-element k-fractions for kmeans, then
tags two useful operating points in pareto_front.csv:
Output tag |
Meaning |
|---|---|
|
Smallest redundancy among points with |
|
Highest |
Use NSGA-II for multi-element production runs with decaf, hdbscan, or kmeans.
Set pop_size to at least 10 × N_focus_elements and at least 40; for
N/O/H, pop_size: 60 is a reasonable budget. Increase n_gen if
pareto_convergence.png has not plateaued. Use nsga2.restore: true only when
you want to reuse an existing pareto_front.csv from the same output directory
and compatible config.
Microstate reference and ε_noise¶
For a first run on an unfamiliar system, microstate.reference: n2 is the
conservative default: per element, it uses roughly two atoms per microstate.
Switch only when the physical assumption is clear:
Reference |
Meaning |
Use when |
|---|---|---|
|
Conservative k=N//2 microstate reference. |
Default for unknown, soft, mobile, or mixed systems. |
|
Smallest resolved k whose KMeans radius is within ε_noise, searched through |
Known-rigid systems, or analyses where ε_noise is a valid identity radius. |
|
Same criterion at user percentile NUM (1–100); |
Tuning strictness deliberately. Hazard: the historical r90 criterion under-selected k ~3× (+56 % force error on PdMgO) — a thermal |
|
Force-resolution reference from |
Expert MLIP-training studies where force tolerance is the reference scale. |
eps_noise.method: thermal is the default and measures descriptor motion under
QHO thermal rattles. Use precomputed only when you are carrying known ε values
from a compatible prior run. Use rdf when you deliberately want the library’s
own first-neighbour peak width to set the displacement scale.
When to use piecing¶
piecing.enabled: true is for a specific failure mode: a library made of a few
very large structures, often around 1000 atoms each, where every structure
already contains almost every microstate. In that case set-cover can return
|S| = 1, the c-factor inflates, and fidelity metrics saturate. Piecing cuts
each large periodic structure into spatial voxel pieces and runs selection over
pieces while keeping descriptors computed on the intact cells.
Use piecing when:
The library has few huge structures, not many ordinary structures.
A non-pieced run selects one or very few structures because each structure already contains all local states.
Per-element group counts collapse, especially for minority elements.
Do not use piecing for a normal molecular/surface-adsorbate library with many
separate structures, and do not combine it with loop.run: true or
loop.resume_from_iter. Tune piecing.target_atoms by checking per-element
group counts at c*: lower target_atoms until each focus element resolves more
than one meaningful local state.
If descriptor.kind: eds, the default ED-SOAP settings are meant to run
immediately. The one value worth checking early is descriptor.eds.lengthscale:
set it near a typical nearest-neighbour bond length in Å for your material. Keep
descriptor.eds.periodic: true for crystals and slabs; set it to false for
isolated molecules or clusters. If descriptor.kind: ace, set
descriptor.ace.model_path to the MACE model file and choose device: cpu or
cuda.
Run once before tuning:
atom-hifi run config.yaml 2>&1 | tee run.out
For a long run, launch with unbuffered output and record the process id:
setsid stdbuf -oL -eL atom-hifi run config.yaml > run.out 2>&1 & echo $! > run.pid
Check output artifacts first, then the log. Output files are more reliable completion signals than quiet stretches in the console output:
cat run.pid
ls -lh fr_results_NiO
test -s fr_results_NiO/representatives.xyz && echo "final representatives written"
tail -f run.out
For a standard run, expect these milestones in paths.output_dir:
Artifact |
Meaning |
|---|---|
|
Structure descriptors were computed or loaded from cache |
|
ε_noise calibration finished, unless the chosen path skips it |
|
Final L/R scan table was written |
|
Final scan diagnostic plot was written |
|
Final representative structures were selected |
|
The fitted |
Optional stages add their own completion signals: learning_curve.png and
descp_iter*.npy for loop.run: true, pareto_front.csv and Pareto plots for
nsga2.run: true, and surroundings*.xyz when
output.extract_surroundings: true.
Common failures usually point back to one config group:
Symptom |
Check |
|---|---|
|
The key is not part of the YAML surface; remove it or use the Python API for expert-only controls |
Input file error |
|
Missing QHO-table element |
Use |
Out-of-memory or killed process |
Check memory before reducing workers; descriptor work follows |
Cache unreadable or incompatible |
Atom-HiFi logs the cache problem and recomputes; keep caches only when descriptor, backend, and ε_noise settings match |
For follow-up runs, keep the same paths.output_dir: descriptor and
ε_noise caches are discovered there automatically when compatible. Use
scan.cache_file only for a compatible prior el_fit_cache.npz from the same
descriptor/backend/focus-element setup. Use loop.resume_from_iter only for
active-learning loop checkpoints; it does not resume an arbitrary interrupted
final scan. Current checkpoints include descp_iterNN_source_idx.npy and
descp_iterNN_source_hash.npy, which record and verify the exact ordered
source structures (including OOD-ranked batches); legacy checkpoints without
both files must be regenerated rather than
reconstructed by random-sampler replay.
Starter template¶
Generate a commented config.yaml with:
atom-hifi init # writes ./config.yaml
atom-hifi init my.yaml # or a chosen path
The template below is emitted by default_config_yaml() and documents every
option inline.
Loading and validation¶
- atom_hifi.config.load_config(path)[source]
Load a user YAML config, merge it under the template defaults, and validate.
Strict: any key not present in the template tree raises
ValueError; acustomdescriptor kind is rejected with a pointer to the Python API.
- atom_hifi.config.fill_defaults(config)[source]
Return
configdeep-merged under runtime defaults (no YAML validation).Used by
atom_hifi.runner.run()so callers may pass a partial dict. Python API callers may use API-only controls such asgridandpatient; those keys are deliberately rejected byload_config().
load_config() rejects YAML keys absent from the starter template, including the
API-only controls grid, patient, paths.patient_path, and
refit.grid_point. fill_defaults() is the Python API path and supplies hidden
defaults for those controls before atom_hifi.runner.run() consumes the
config.
Custom descriptor¶
descriptor.kind accepts eds (ED-SOAP) or ace (MACE ACE) in YAML. A custom
descriptor is a Python callable and cannot be expressed in YAML — call
atom_hifi.runner.run() from Python with
config['descriptor']['custom_fn'] set to fn(List[Atoms]) -> ndarray.