npx skills add ...
npx skills add nvidia/nvalchemi-toolkit --skill nvalchemi-model-wrapping
How to wrap an arbitrary MLIP (Machine Learning Interatomic Potential) using the BaseModelMixin interface to standardize inputs, outputs, and embeddings. Use when integrating a model such as MACE or AIMNet2 (e.g. MACEWrapper, loading pretrained checkpoints) so dynamics, training, or fine-tuning stages can call it, or when exposing energies, forces, or embeddings from a custom PyTorch model.
npx skills add nvidia/nvalchemi-toolkit --skill nvalchemi-model-wrapping
To use an arbitrary MLIP (Machine Learning Interatomic Potential) within nvalchemi,
pair it with the BaseModelMixin interface. This standardizes how models receive
AtomicData/Batch inputs and produce ModelOutputs.
A wrapper subclasses nn.Module and BaseModelMixin, and holds the
underlying model by composition (self.model = ...). This is the pattern
used by every built-in wrapper (DemoModelWrapper, MACEWrapper,
AIMNet2Wrapper, LennardJonesModelWrapper).
nn.Module must come first in the bases so PyTorch initializes correctly.
model_config in __init__ (capabilities & runtime control)ModelConfig unifies two kinds of fields:
frozenset/bool at construction) describe what
the checkpoint can do: outputs, autograd_outputs, autograd_inputs,
required_inputs, optional_inputs, supports_pbc, needs_pbc,
neighbor_config.active_outputs (defaults to outputs) and gradient_keys.BaseModelMixin enforces that every wrapper sets self.model_config in
__init__ (a missing one raises TypeError at construction).
Well-known output keys: energy, forces, stress, hessians, dipoles,
charges, embeddings. outputs/required_inputs are free-form strings, so
new properties can be added without changing ModelConfig.
embedding_shapesadapt_inputConverts AtomicData/Batch to a dict of keyword arguments for the underlying model's forward().
Always call super().adapt_input() first — it enables requires_grad on
autograd_inputs (when an autograd output is active) plus any gradient_keys,
and collects the keys declared by input_data().
adapt_outputConverts the model's raw output to ModelOutputs (an OrderedDict[str, Tensor | None]).
Always call super().adapt_output() first — it returns an OrderedDict
pre-filled with the output_data() keys (set to None) and auto-maps matching
key names (unsqueezing a 1-D energy to [B, 1]).
Standard output keys and shapes:
| Key | Shape | Notes |
|---|---|---|
energy | [B, 1] | Per-graph energy (eV) |
forces | [V, 3] | Per-node forces |
stress | [B, 3, 3] | Per-graph stress tensor |
hessian | [V, 3, 3] | Energy Hessian |
dipole | [B, 3] | Dipole moment |
charges | [V] | Partial charges |
compute_embeddingsembedding_shapes and compute_embeddings are abstract on BaseModelMixin, so
every wrapper must define them (raise NotImplementedError if the model has no
embeddings). compute_embeddings writes embeddings to the data structure
in-place and returns it.
forwardThe main entry point. Adapts input, calls the underlying model, adapts output.
export_model / add_output_headBaseModelMixin.export_model and add_output_head default to raising
NotImplementedError. Override them if your model needs to be exported without
the mixin (e.g. for ASE calculators) or supports extra output heads.
active_outputsactive_outputs selects what to compute on each forward pass. Change it with
set_config, which validates that the field exists and is mutable:
set_config(key, value) is equivalent to model.model_config.<key> = value.
output_data() returns active_outputs & outputs and warns if you request a
key the model does not support.
| Method | Returns | Description |
|---|---|---|
input_data() | set[str] | Required input keys from model_config (positions, atomic_numbers, neighbor-list keys, pbc, required_inputs) |
output_data() | set[str] | active_outputs & outputs (warns on unsupported requests) |
set_config(key, value) | None | Set a mutable ModelConfig field with validation |
direct_derivative_keys() | set[str] | Outputs computed analytically alongside an autograd energy (pipeline autograd); default empty |
add_output_head(prefix) | None | Override to add an MLP output head; default raises NotImplementedError |
export_model(path, as_state_dict=False) | None | Override to export the raw model; default raises NotImplementedError |