User Guide#
The xnn plug-in is a provider rather than a step. It connects the machine-learned force fields trained with xnn to SEAMM’s Model Chemistry mechanism, and to the steps that drive a model chemistry over the MolSSI Driver Interface (MDI).
xnn.ini#
~/SEAMM/xnn.ini (created with defaults the first time it is needed) has a section
per executor, normally just [local]:
installationconda(the default) orlocal.conda,conda-environmentThe conda executable and the environment holding xnn and PyTorch (
seamm-xnn). A path (absolute or~/...) is accepted for the environment.codeThe command to run xnn,
xnnby default; for alocalinstallation a full path may be given.deviceThe PyTorch device the engine evaluates on:
cpu,cuda,cuda:0,mps. On Apple siliconmpsworks only for a float32 model and only without the optionalvesin-torchneighbor-list package, which insists on float64 (Apple’s GPU has none); for small molecules the CPU is as fast or faster anyway.modelsThe directories searched for checkpoints, one per line. An entry may be
personal:<subdir>(~/.seamm.d/data/Forcefields/<subdir>),local:<subdir>(~/SEAMM/data/Forcefields/<subdir>), or a plain path in which{root}expands to the SEAMM root (~/SEAMMunless--rootwas given). The default ispersonal:xnnthenlocal:xnn, a personal model shadowing a machine-wide one of the same name.patternThe glob for checkpoint files,
*.ptby default.
The [xnn-step] section of seamm.ini has one option, ncores: a cap on the CPU
threads the engine may use (OMP_NUM_THREADS), or available for PyTorch’s default.
Model chemistries#
Each checkpoint is advertised as xnn:MLFF@<name>. A checkpoint with a name of its
own is called by its file stem: water_mace.pt is xnn:MLFF@water_mace. Training
writes generic names – runs/<...>/<run>/best.pt and last.pt – so a checkpoint
named best, last, model, checkpoint or final is called after its
directory instead, with the directory above too when its own is only an index (m1,
fold2, seed0), the stem unless it is best, and the first six hex digits of
the SHA-256 of the file:
.../x013_m1/best.ptisxnn:MLFF@x013_m1-3f9a2c;.../x013_m1/last.ptisxnn:MLFF@x013_m1-last-7b1e09;.../committee_w1/m1/best.ptisxnn:MLFF@committee_w1-m1-0c44d5.
The hash makes the name that of these weights, so results from a later or retrained
checkpoint in the same place are not labelled as the same model. To choose a name,
give the checkpoint one, e.g. with a symlink. In every name the grammar’s reserved
characters (: @ / |) are replaced by -.
The models are all flagged MDI-capable and periodic-capable: the engine builds the atomic graph with the cell when one is sent and returns the stress.
The MDI engine#
A driving step asks the plug-in for the launch command, which runs:
conda run --live-stream -n seamm-xnn xnn mdi --ckpt <checkpoint> --device <device> \
-mdi "-role ENGINE -name XNN -method TCP -port <port> -hostname <host>"
The engine loads the model once and then answers <ENERGY, <FORCES and
<STRESS for every geometry the driver sends (>COORDS, and >CELL for
periodic systems), converting between MDI’s atomic units and the model’s eV/Å at the
boundary.
Steps that launch the engine and the driver together under mpirun, such as LAMMPS
running dynamics on a GPU, use -method MPI in place of a TCP port. That path needs
the MDI library itself to be linked against MPI, which is why the environment takes
pymdi from conda-forge rather than PyPI. With the PyPI build the engine stops at
Error in MDI_Init: Failed to initialize MPI.
For this the plug-in gives the engine’s command without conda run: the engine runs
in the driver’s environment, since the two must share MPI and the MDI library, so
LAMMPS’s environment must have xnn installed. LAMMPS asks for it when a Model Chemistry
step selects an xnn:MLFF model, and launches the pair itself (see the LAMMPS step’s
documentation):
xnn mdi --ckpt <checkpoint> --device cuda:0 <engine options> \
-mdi "-role ENGINE -name XNN -method MPI"
Checkpoints by path#
A Model Chemistry step can name a checkpoint by its path, or with a variable, rather
than choosing one of the models found in the directories above – e.g. one written
during training. It is offered as xnn:MLFF@<file stem> and checked as the others
are. The Model Chemistry step’s Engine options are added to the engine’s command for
that run, e.g. --eeq-reuse or --dispersion "{regime: large}".
A model whose checkpoint was pickled by an older xnns release fails to load with
No module named 'xnn'; re-save it with the current xnn.
The environment requires xnns>=0.4.0. The 0.2.1 and 0.3.0 releases added
scale_shift buffers to the MACE model without defaults for older state dicts, so every
checkpoint trained before them failed in the engine with Missing key(s) in state_dict:
"model.model.scale_shift.scale", "model.model.scale_shift.shift" (and a LAMMPS driver
then waited forever for the engine); 0.4.0 loads them again. Updating the plug-in moves
an existing environment to 0.4.0 or later, including one rolled back to 0.1.0 by hand.
xnns needs only torch>=2.0, so the torch the installer chose for the machine’s
driver (see Getting Started, torch-build) is left as it is.
Committees#
A committee – a directory of members m0/best.pt, m1/best.pt, …, each trained
on a different part of the data, as the Active Learning step’s Train Committee writes
one – is offered as a single model. In a model directory it is called by the
directory’s name (water_r2/ is xnn:MLFF@water_r2); given by path, a directory
with a generic name such as committee is called after the nearest directory above it
that says more, skipping indices such as a job’s step directories, with a short hash of
the members: Job_012130/2/1/committee is xnn:MLFF@Job_012130-committee-5c20d7.
A committee.json in the directory, e.g. {"members": ["m0", "m1",
"m3"]}, chooses the members; otherwise all are used, and a committee needs at least
two. Every member must handle the dispersion the same way.
A committee runs as the committee-mean engine of the active-learning-step package,
which must be installed with SEAMM, in the same MLFF environment and the same way as a
single model (conda run and TCP, or as an MPI pair):
env PYTHONPATH=<copy of the package> python -m active_learning_step.cli engine \
--members m0/best.pt m1/best.pt ... --device <device> \
--spreads committee_spreads.csv --every 10 <engine options> -mdi "..."
The forces, energy and stress are the members’ mean, and every ten steps the spreads
of the members – of the energy, the forces and the pressure – are appended to
committee_spreads.csv in the step’s directory, which the step keeps (the model
chemistry lists the files its engine writes as engine_files). Nothing is installed in the MLFF
environment: a copy of the package’s code, made once per version in
~/.seamm.d/active_learning/code/, is put on its PYTHONPATH. The Model Chemistry
step’s Engine options go to this engine, e.g. --every 20, --flag
rho_forces=0.2 (write the frames whose spread reaches it), --dtype float32 or
--eeq-reuse.
Dispersion and charge#
The plug-in reads the training configuration stored in each checkpoint. It does this without PyTorch: the configuration is recovered from the checkpoint’s pickle with every class replaced by an inert stand-in, so nothing in the file is executed. From it the Model Chemistry step is told the model family, the elements the model was trained on, and how dispersion enters the model. Dispersion can enter in one of two ways, recorded in the training configuration:
model.extra["dispersion"]The model was trained with xnn’s D3/D4 wrapper, so the checkpoint carries the term.
subtracted_dispersion(top level)The model was trained without the wrapper, on energies and forces from which this dispersion term was subtracted.
xnn mdiadds the identical term back, using the recorded settings, for example{name: d4, cutoff_pair: 12.0, switch_width_pair: 2.0, cutoff_triple: 8.0, tail_correction: true}.
In both cases the engine adds the dispersion by itself and the plug-in passes nothing; the Model Chemistry step shows which applies, e.g. “D4, 12 Å + tail added by the engine”. A checkpoint with both was trained with the wrapper on dispersion-subtracted targets, which removes the dispersion twice. It is not offered as a model chemistry, and asking for it by name is an error.
The D4 dispersion depends on the total charge of the system through its EEQ partial
charges. For a charged configuration the plug-in passes the charge to the engine as
--total-charge.