Skip to content

Commit ba52c9a

Browse files
committed
Merge branch 'lammps'
2 parents 69301c2 + 78803c7 commit ba52c9a

23 files changed

Lines changed: 3252 additions & 30 deletions

.codespellrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
[codespell]
2-
ignore-words-list = COO,Mater,ket
2+
ignore-words-list = COO,Mater,ket,nd,te

.github/workflows/lammps-build.yml

Lines changed: 602 additions & 0 deletions
Large diffs are not rendered by default.

lammps/README.md

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
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.

lammps/cmake/ML-MATGL-KOKKOS.cmake

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# ML-MATGL Kokkos variant — drop-in CMake snippet.
2+
#
3+
# Layered on top of ML-MATGL.cmake: include() this *after* the base snippet,
4+
# OR set PKG_ML-MATGL=ON and PKG_KOKKOS=ON together.
5+
#
6+
# Usage (from a stock LAMMPS source tree):
7+
# cmake -B build \
8+
# -D PKG_ML-MATGL=ON -D PKG_KOKKOS=ON \
9+
# -D Kokkos_ENABLE_CUDA=ON \
10+
# -D Kokkos_ARCH_AMPERE80=ON \
11+
# -D CMAKE_PREFIX_PATH=/path/to/libtorch \
12+
# -D CMAKE_CXX_COMPILER=$LAMMPS/lib/kokkos/bin/nvcc_wrapper \
13+
# <other flags>
14+
#
15+
# The `pair_matgl/kk` style is registered via the standard LAMMPS Kokkos
16+
# pair-style macro so users invoke it with `pair_style matgl/kk` or by
17+
# launching LAMMPS with `-sf kk -k on g 1`.
18+
19+
if(NOT PKG_ML-MATGL OR NOT PKG_KOKKOS)
20+
return()
21+
endif()
22+
23+
if(NOT DEFINED ML_MATGL_KOKKOS_DIR)
24+
get_filename_component(ML_MATGL_KOKKOS_DIR
25+
"${CMAKE_CURRENT_LIST_DIR}/../src/KOKKOS" ABSOLUTE)
26+
endif()
27+
28+
if(NOT EXISTS "${ML_MATGL_KOKKOS_DIR}/pair_matgl_kokkos.cpp")
29+
message(FATAL_ERROR
30+
"ML-MATGL-KOKKOS source not found at ${ML_MATGL_KOKKOS_DIR}. "
31+
"Set -DML_MATGL_KOKKOS_DIR=<path/to/lammps/src/KOKKOS>.")
32+
endif()
33+
34+
file(GLOB ML_MATGL_KOKKOS_SOURCES "${ML_MATGL_KOKKOS_DIR}/*.cpp")
35+
36+
target_sources(lammps PRIVATE ${ML_MATGL_KOKKOS_SOURCES})
37+
target_include_directories(lammps PRIVATE ${ML_MATGL_KOKKOS_DIR})
38+
39+
# Single-GPU only: warn loudly. MACE upstream issues #1294 and #322 cover
40+
# the multi-rank-with-libtorch breakage we inherit.
41+
message(STATUS
42+
"ML-MATGL-KOKKOS: enabled. Single-GPU runs only — multi-rank Kokkos with "
43+
"libtorch is unreliable (see MACE issues #1294, #322).")

lammps/cmake/ML-MATGL.cmake

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# ML-MATGL package — drop-in CMake snippet for a stock LAMMPS source tree.
2+
#
3+
# Usage (from a stock LAMMPS source tree):
4+
# 1. Copy or symlink lammps/src/ML-MATGL → <lammps>/src/ML-MATGL
5+
# 2. Append to <lammps>/cmake/CMakeLists.txt (anywhere after the `set(STANDARD_PACKAGES …)`
6+
# block):
7+
# include(/path/to/matgl/lammps/cmake/ML-MATGL.cmake)
8+
# 3. Configure with:
9+
# cmake -B build \
10+
# -D PKG_ML-MATGL=ON \
11+
# -D CMAKE_PREFIX_PATH=/path/to/libtorch \
12+
# -D CMAKE_BUILD_TYPE=Release \
13+
# <other flags>
14+
#
15+
# CMake variables consumed:
16+
# PKG_ML-MATGL - turn the package on/off (default OFF).
17+
# CMAKE_PREFIX_PATH - must point at a libtorch install (CXX11 ABI build).
18+
# ML_MATGL_DIR - override path to lammps/src/ML-MATGL (defaults to
19+
# ${CMAKE_CURRENT_LIST_DIR}/../src/ML-MATGL).
20+
21+
option(PKG_ML-MATGL "Build the matgl pair_style backed by libtorch" OFF)
22+
23+
if(NOT PKG_ML-MATGL)
24+
return()
25+
endif()
26+
27+
# Locate the source directory.
28+
if(NOT DEFINED ML_MATGL_DIR)
29+
get_filename_component(ML_MATGL_DIR
30+
"${CMAKE_CURRENT_LIST_DIR}/../src/ML-MATGL" ABSOLUTE)
31+
endif()
32+
33+
if(NOT EXISTS "${ML_MATGL_DIR}/pair_matgl.cpp")
34+
message(FATAL_ERROR
35+
"ML-MATGL source not found at ${ML_MATGL_DIR}. "
36+
"Set -DML_MATGL_DIR=<path/to/lammps/src/ML-MATGL>.")
37+
endif()
38+
39+
# Pull in libtorch.
40+
find_package(Torch REQUIRED)
41+
if(NOT TORCH_LIBRARIES)
42+
message(FATAL_ERROR
43+
"find_package(Torch) succeeded but TORCH_LIBRARIES is empty. "
44+
"Did you set CMAKE_PREFIX_PATH to a libtorch install?")
45+
endif()
46+
47+
# Compose the source list.
48+
file(GLOB ML_MATGL_SOURCES "${ML_MATGL_DIR}/*.cpp")
49+
50+
# Hook into the LAMMPS build. This file is included from
51+
# <lammps>/cmake/CMakeLists.txt; the `lammps` target already exists by then.
52+
target_sources(lammps PRIVATE ${ML_MATGL_SOURCES})
53+
target_include_directories(lammps PRIVATE ${ML_MATGL_DIR})
54+
target_compile_features(lammps PRIVATE cxx_std_17)
55+
target_link_libraries(lammps PRIVATE ${TORCH_LIBRARIES})
56+
57+
# Make sure libtorch's headers come ahead of any system Eigen/torch shims.
58+
target_include_directories(lammps PRIVATE ${TORCH_INCLUDE_DIRS})
59+
60+
# LibTorch ships with -D_GLIBCXX_USE_CXX11_ABI=…; propagate it so consumers
61+
# (e.g. KOKKOS in Phase 3) see the same ABI.
62+
if(DEFINED TORCH_CXX_FLAGS)
63+
set_property(TARGET lammps APPEND_STRING PROPERTY COMPILE_FLAGS " ${TORCH_CXX_FLAGS}")
64+
endif()
65+
66+
message(STATUS "ML-MATGL: enabled, sources from ${ML_MATGL_DIR}")
67+
message(STATUS "ML-MATGL: linking against TORCH_LIBRARIES=${TORCH_LIBRARIES}")

0 commit comments

Comments
 (0)