|
| 1 | +# MatGL → LAMMPS pair_style |
| 2 | + |
| 3 | +`pair_matgl` is a LAMMPS pair style that loads a TorchScript-compiled |
| 4 | +**MatGL TensorNet** PES (PyG backend, no-Warp, extensive head) and uses |
| 5 | +LibTorch to evaluate energies, forces, and the virial tensor on every |
| 6 | +timestep. |
| 7 | + |
| 8 | +This directory ships: |
| 9 | + |
| 10 | +- `src/ML-MATGL/pair_matgl.{cpp,h}` — the CPU/serial pair style. |
| 11 | +- `src/KOKKOS/pair_matgl_kokkos.{cpp,h}` — the Kokkos GPU/host variant |
| 12 | + (`pair_style matgl/kk`). |
| 13 | +- `cmake/ML-MATGL.cmake` and `cmake/ML-MATGL-KOKKOS.cmake` — drop-in |
| 14 | + CMake snippets. |
| 15 | +- `tests/in.matgl_si` — sample input deck for a single-point parity check. |
| 16 | + |
| 17 | +The Python side (one repo up) ships `mgl create-lammps-model`, which |
| 18 | +produces the `.pt` artifact these pair styles consume. |
| 19 | + |
| 20 | +> **Status — Phases 2 + 3 of the MatGL LAMMPS-Kokkos plugin.** CPU CI |
| 21 | +> exists (`.github/workflows/lammps-build.yml`); GPU runs require a |
| 22 | +> CUDA-capable runner (not in CI yet). |
| 23 | +
|
| 24 | +## Building |
| 25 | + |
| 26 | +### 1. Export a LAMMPS-loadable model |
| 27 | + |
| 28 | +```bash |
| 29 | +# From your matgl checkout: |
| 30 | +uv run mgl create-lammps-model \ |
| 31 | + -m materialyze/TensorNet-MatPES-r2SCAN \ |
| 32 | + -o tensornet_matpes_r2scan.pt \ |
| 33 | + --dtype float32 |
| 34 | +``` |
| 35 | + |
| 36 | +The CLI prints `r_max`, `n_species`, the dtype, and the species list — all |
| 37 | +of which you'll need for `pair_coeff`. |
| 38 | + |
| 39 | +### 2. Build LAMMPS with the package |
| 40 | + |
| 41 | +Drop the package into a stock LAMMPS source tree and configure: |
| 42 | + |
| 43 | +```bash |
| 44 | +# 1) Copy or symlink the source files. |
| 45 | +ln -s /path/to/matgl/lammps/src/ML-MATGL <lammps>/src/ML-MATGL |
| 46 | + |
| 47 | +# 2) Tell LAMMPS' CMake about the package. |
| 48 | +echo 'include(/path/to/matgl/lammps/cmake/ML-MATGL.cmake)' \ |
| 49 | + >> <lammps>/cmake/CMakeLists.txt |
| 50 | + |
| 51 | +# 3) Configure + build. Match libtorch's CXX11 ABI to LAMMPS'. |
| 52 | +cmake -B build -S <lammps>/cmake \ |
| 53 | + -D PKG_ML-MATGL=ON \ |
| 54 | + -D CMAKE_PREFIX_PATH=/path/to/libtorch \ |
| 55 | + -D CMAKE_BUILD_TYPE=Release \ |
| 56 | + -D BUILD_MPI=ON |
| 57 | +cmake --build build -j 8 |
| 58 | +``` |
| 59 | + |
| 60 | +### 2b. Build the Kokkos GPU variant |
| 61 | + |
| 62 | +To get the `matgl/kk` pair style, also enable Kokkos and append the |
| 63 | +matching snippet to LAMMPS' CMake. CUDA example for an Ampere card |
| 64 | +(A100/A30): |
| 65 | + |
| 66 | +```bash |
| 67 | +echo 'include(/path/to/matgl/lammps/cmake/ML-MATGL-KOKKOS.cmake)' \ |
| 68 | + >> <lammps>/cmake/CMakeLists.txt |
| 69 | + |
| 70 | +cmake -B build -S <lammps>/cmake \ |
| 71 | + -D PKG_ML-MATGL=ON \ |
| 72 | + -D PKG_KOKKOS=ON \ |
| 73 | + -D Kokkos_ENABLE_CUDA=ON \ |
| 74 | + -D Kokkos_ARCH_AMPERE80=ON \ |
| 75 | + -D CMAKE_PREFIX_PATH=/path/to/libtorch \ |
| 76 | + -D CMAKE_CXX_COMPILER=<lammps>/lib/kokkos/bin/nvcc_wrapper \ |
| 77 | + -D CMAKE_BUILD_TYPE=Release |
| 78 | +cmake --build build -j 8 |
| 79 | +``` |
| 80 | + |
| 81 | +Run with: |
| 82 | + |
| 83 | +```bash |
| 84 | +mpirun -n 1 build/lmp -k on g 1 -sf kk -in in.matgl_si |
| 85 | +``` |
| 86 | + |
| 87 | +`-sf kk` makes LAMMPS prefer Kokkos pair styles, so `pair_style matgl` |
| 88 | +in your input deck dispatches to `matgl/kk` automatically. If you'd |
| 89 | +rather force it explicitly, write `pair_style matgl/kk` instead. |
| 90 | + |
| 91 | +**Single-GPU only.** Multi-rank Kokkos with libtorch is unreliable |
| 92 | +(MACE issues #1294 and #322); the package emits a CMake message making |
| 93 | +this explicit. |
| 94 | + |
| 95 | +Tested with: |
| 96 | + |
| 97 | +- LibTorch 2.2.x – 2.5.x (CXX11 ABI, CPU build). |
| 98 | +- LAMMPS develop branch (Aug 2024 or newer for the `add_request` / |
| 99 | + `REQ_GHOST` neighbor-list API). |
| 100 | +- C++17, MPI optional. |
| 101 | + |
| 102 | +## LAMMPS input syntax |
| 103 | + |
| 104 | +```lammps |
| 105 | +units metal |
| 106 | +atom_style atomic |
| 107 | +atom_modify map yes # required: pair_matgl needs the atom map |
| 108 | +newton on # required: ghost contributions |
| 109 | +
|
| 110 | +pair_style matgl |
| 111 | +pair_coeff * * tensornet_matpes_r2scan.pt Si C O |
| 112 | +``` |
| 113 | + |
| 114 | +`pair_coeff` arguments after the `.pt` path are **species symbols** in |
| 115 | +LAMMPS atom-type order: type 1 = first symbol, type 2 = second, … |
| 116 | + |
| 117 | +The cutoff (`r_max`) is read from the model — you don't pass it. |
| 118 | + |
| 119 | +### Optional pair_style flags |
| 120 | + |
| 121 | +```lammps |
| 122 | +pair_style matgl no_domain_decomposition |
| 123 | +``` |
| 124 | + |
| 125 | +Reserved for future single-rank optimisations (mirrors the MACE flag). |
| 126 | +Currently a no-op. |
| 127 | + |
| 128 | +## Limitations |
| 129 | + |
| 130 | +- **No per-atom energies / virials.** `eflag_atom`, `vflag_atom`, and |
| 131 | + `compute … pe/atom` will error. The model returns a single |
| 132 | + `total_energy_local` scalar plus a 3×3 virial tensor; per-atom |
| 133 | + decompositions would require a different export. |
| 134 | +- **`atom_style atomic` only** for now. Charged systems aren't supported |
| 135 | + (the model has no charge head). |
| 136 | +- **TorchScript artifacts are dtype-specific.** Re-run |
| 137 | + `mgl create-lammps-model --dtype float64` to get a double-precision |
| 138 | + model; mixing dtypes between LAMMPS and the model will error at load |
| 139 | + time. |
| 140 | +- **Multi-rank**: works for CPU MPI, but each rank loads the model |
| 141 | + independently (memory adds up). The `data_mean` buffer baked into the |
| 142 | + TorchScript is added once per rank — keep `data_mean = 0` (the default |
| 143 | + for trained MatGL PES models). Non-zero `data_mean` will over-count |
| 144 | + proportionally to the number of ranks. |
| 145 | +- **No restart support.** The model lives on disk; `restart` files don't |
| 146 | + capture the path. Re-issue `pair_style` / `pair_coeff` after a restart. |
| 147 | +- **TensorNet only.** M3GNet, CHGNet, MEGNet, SO3Net, QET are DGL-only |
| 148 | + in the matgl repo and would need PyG ports first. |
| 149 | + |
| 150 | +## Continuous integration |
| 151 | + |
| 152 | +`.github/workflows/lammps-build.yml` builds the **CPU** pair style on |
| 153 | +every push that touches the `lammps/` tree, the Python wrapper, or the |
| 154 | +workflow itself. The job runs inside the `lammps/lammps-build:ubuntu_latest` |
| 155 | +public Docker image, downloads a CXX11-ABI libtorch, clones LAMMPS at a |
| 156 | +pinned tag, builds with `PKG_ML-MATGL=ON`, exports a tiny in-tree model |
| 157 | +through `LAMMPSMatGLModel`, runs the `in.matgl_si` deck, and diffs the |
| 158 | +LAMMPS energy against the Python reference. |
| 159 | + |
| 160 | +The Kokkos variant is **not** exercised in CI today — GitHub-hosted |
| 161 | +runners have no GPU. Hardware-accelerated CI is on the Phase-3 follow-up |
| 162 | +list and likely lives on a self-hosted CUDA runner. |
| 163 | + |
| 164 | +## Verifying a build |
| 165 | + |
| 166 | +```bash |
| 167 | +cd lammps/tests |
| 168 | +<lammps>/build/lmp -in in.matgl_si |
| 169 | +``` |
| 170 | + |
| 171 | +The test deck prints energy, forces, and stress on a small Si supercell. |
| 172 | +Compare against the Python reference: |
| 173 | + |
| 174 | +```bash |
| 175 | +uv run python tests/python_reference.py # in this directory |
| 176 | +``` |
| 177 | + |
| 178 | +Energies should match within `1e-5 eV`, forces within `1e-4 eV/Å`, and |
| 179 | +stresses (when nonzero) within `1e-3 GPa`. |
| 180 | + |
| 181 | +## Implementation notes |
| 182 | + |
| 183 | +- The pair style requests a **full neighbor list with ghost atoms** |
| 184 | + (`REQ_FULL | REQ_GHOST`). The model expects edge indices that span both |
| 185 | + owned and ghost atoms. |
| 186 | +- Every edge is folded back onto the *local* row of the atom it represents |
| 187 | + (via `atom->map(atom->tag[j])`) rather than pointing at the ghost row |
| 188 | + directly. TensorNet's message-passing layers need one consistent row |
| 189 | + per physical atom — a ghost row never propagates outgoing messages back |
| 190 | + to the atom it duplicates. The periodic image is recovered explicitly as |
| 191 | + an integer `unit_shifts` (the ghost/local position difference, |
| 192 | + transformed through the box's inverse deformation matrix and rounded to |
| 193 | + the nearest integer), rather than relying on LAMMPS' already-imaged |
| 194 | + ghost positions with `unit_shifts = 0`. Single-rank only: ghost atoms |
| 195 | + can be owned by a different MPI rank, so there is no local row to fold |
| 196 | + onto in a multi-rank run. |
| 197 | +- Forces are accumulated for **all** atoms (owned + ghost). LAMMPS' usual |
| 198 | + `comm->reverse_comm` step then sums ghost contributions back to the |
| 199 | + rank that owns each atom. This requires `newton on`. |
| 200 | +- Virials are written into the global `virial[6]` array directly as |
| 201 | + `virial -= va` (the model returns `virials = dE/dstrain = -W`, while |
| 202 | + LAMMPS' convention is `W = sum_i r_i ⊗ f_i`). We set |
| 203 | + `no_virial_fdotr_compute = 1` in the constructor so LAMMPS doesn't |
| 204 | + recompute the virial from forces. |
| 205 | + |
| 206 | +## Reference |
| 207 | + |
| 208 | +Plan and design notes: |
| 209 | +[`develop-a-kokkos-plugin-eventual-hare.md`](https://github.com/materialyzeai/matgl/tree/lammps). |
| 210 | + |
| 211 | +The Python wrapper is documented inline at |
| 212 | +`src/matgl/ext/_lammps.py` in the matgl repo. |
0 commit comments