Skip to content

Commit b035453

Browse files
authored
Merge branch 'main' into fix-alchmtk-compute-distances
2 parents f979a26 + 161f5d9 commit b035453

5 files changed

Lines changed: 200 additions & 31 deletions

File tree

.github/workflows/lammps-build.yml

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -169,9 +169,9 @@ jobs:
169169
import torch
170170
from pymatgen.core import Lattice, Structure
171171
from pymatgen.optimization.neighbors import find_points_in_spheres
172-
from matgl.apps._pes_pyg import Potential
173-
from matgl.ext._lammps import LAMMPSMatGLModel
174-
from matgl.models._tensornet_pyg import TensorNet
172+
from matgl.apps.pes import Potential
173+
from matgl.ext.lammps import LAMMPSMatGLModel
174+
from matgl.models._tensornet import TensorNet
175175
176176
torch.manual_seed(0)
177177
m = TensorNet(
@@ -310,13 +310,28 @@ jobs:
310310
sys.exit(1)
311311
PY
312312
313+
- name: Validate pressure sign by finite differences
314+
shell: bash
315+
env:
316+
LIBTORCH: ${{ steps.libtorch.outputs.path }}
317+
run: |
318+
export LD_LIBRARY_PATH="${LIBTORCH}/lib:${LD_LIBRARY_PATH:-}"
319+
cp lammps_artifacts/model.pt matgl/lammps/tests/model.pt
320+
cd matgl/lammps/tests
321+
LMP="${GITHUB_WORKSPACE}/lammps_build/lmp"
322+
"${LMP}" -var scale 0.99 -log log.strain-minus.lammps -in in.matgl_finite_strain
323+
"${LMP}" -var scale 1.00 -log log.strain-zero.lammps -in in.matgl_finite_strain
324+
"${LMP}" -var scale 1.01 -log log.strain-plus.lammps -in in.matgl_finite_strain
325+
python3 check_finite_difference_pressure.py \
326+
log.strain-minus.lammps log.strain-zero.lammps log.strain-plus.lammps
327+
313328
- name: Upload artifacts on failure
314329
if: failure()
315330
uses: actions/upload-artifact@v4
316331
with:
317332
name: lammps-debug
318333
path: |
319-
matgl/lammps/tests/log.lammps
334+
matgl/lammps/tests/log*.lammps
320335
lammps_artifacts/
321336
lammps_build/CMakeFiles/CMakeOutput.log
322337
lammps_build/CMakeFiles/CMakeError.log
@@ -443,9 +458,9 @@ jobs:
443458
import torch
444459
from pymatgen.core import Lattice, Structure
445460
from pymatgen.optimization.neighbors import find_points_in_spheres
446-
from matgl.apps._pes_pyg import Potential
447-
from matgl.ext._lammps import LAMMPSMatGLModel
448-
from matgl.models._tensornet_pyg import TensorNet
461+
from matgl.apps.pes import Potential
462+
from matgl.ext.lammps import LAMMPSMatGLModel
463+
from matgl.models._tensornet import TensorNet
449464
450465
torch.manual_seed(0)
451466
m = TensorNet(
@@ -557,7 +572,9 @@ jobs:
557572
export LD_LIBRARY_PATH="${LIBTORCH}/lib:${LD_LIBRARY_PATH:-}"
558573
cp lammps_artifacts/model.pt matgl/lammps/tests/model.pt
559574
cd matgl/lammps/tests
560-
"${GITHUB_WORKSPACE}/lammps_build/lmp" -k on g 1 -sf kk -in in.matgl_si | tee log.lammps
575+
"${GITHUB_WORKSPACE}/lammps_build/lmp" \
576+
-k on g 1 -sf kk -pk kokkos neigh half newton on \
577+
-in in.matgl_si | tee log.lammps
561578
562579
- name: Diff LAMMPS energy against Python reference
563580
shell: bash
@@ -589,13 +606,31 @@ jobs:
589606
sys.exit(1)
590607
PY
591608
609+
- name: Validate Kokkos pressure sign by finite differences
610+
shell: bash
611+
env:
612+
LIBTORCH: ${{ steps.libtorch.outputs.path }}
613+
run: |
614+
export LD_LIBRARY_PATH="${LIBTORCH}/lib:${LD_LIBRARY_PATH:-}"
615+
cp lammps_artifacts/model.pt matgl/lammps/tests/model.pt
616+
cd matgl/lammps/tests
617+
LMP="${GITHUB_WORKSPACE}/lammps_build/lmp"
618+
"${LMP}" -k on g 1 -sf kk -pk kokkos neigh half newton on \
619+
-var scale 0.99 -log log.strain-minus.lammps -in in.matgl_finite_strain
620+
"${LMP}" -k on g 1 -sf kk -pk kokkos neigh half newton on \
621+
-var scale 1.00 -log log.strain-zero.lammps -in in.matgl_finite_strain
622+
"${LMP}" -k on g 1 -sf kk -pk kokkos neigh half newton on \
623+
-var scale 1.01 -log log.strain-plus.lammps -in in.matgl_finite_strain
624+
python3 check_finite_difference_pressure.py \
625+
log.strain-minus.lammps log.strain-zero.lammps log.strain-plus.lammps
626+
592627
- name: Upload artifacts on failure
593628
if: failure()
594629
uses: actions/upload-artifact@v4
595630
with:
596631
name: lammps-kokkos-debug
597632
path: |
598-
matgl/lammps/tests/log.lammps
633+
matgl/lammps/tests/log*.lammps
599634
lammps_artifacts/
600635
lammps_build/CMakeFiles/CMakeOutput.log
601636
lammps_build/CMakeFiles/CMakeError.log

lammps/src/KOKKOS/pair_matgl_kokkos.cpp

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,7 @@ void PairMATGLKokkos<DeviceType>::compute(int eflag, int vflag)
130130
atomKK->sync(execution_space, datamask_read);
131131
atomKK->modified(execution_space, datamask_modify);
132132

133-
using AT_ = typename AT::t_x_array; // (nall, 3) double on the device
134-
AT_ x = atomKK->k_x.template view<DeviceType>();
133+
auto x = atomKK->k_x.template view<DeviceType>();
135134
auto f = atomKK->k_f.template view<DeviceType>();
136135
auto type = atomKK->k_type.template view<DeviceType>();
137136

@@ -173,13 +172,15 @@ void PairMATGLKokkos<DeviceType>::compute(int eflag, int vflag)
173172

174173
// Fill Z + mask from atom type.
175174
const auto type_to_z = d_type_to_z_;
175+
const auto d_atomic_numbers = d_atomic_numbers_;
176+
const auto d_local_or_ghost = d_local_or_ghost_;
176177
Kokkos::parallel_for(
177178
"matgl_kk:fill_atoms",
178179
Kokkos::RangePolicy<DeviceType>(0, nall),
179180
KOKKOS_LAMBDA(const int i) {
180181
const int t = type(i);
181-
d_atomic_numbers_(i) = type_to_z(t);
182-
d_local_or_ghost_(i) = (i < nlocal);
182+
d_atomic_numbers(i) = type_to_z(t);
183+
d_local_or_ghost(i) = (i < nlocal);
183184
});
184185

185186
// local_row_of_(j) = the owned row representing the same physical atom as
@@ -205,6 +206,7 @@ void PairMATGLKokkos<DeviceType>::compute(int eflag, int vflag)
205206
// so initialize numneigh_short_ for ghost atoms to zero).
206207
Kokkos::deep_copy(d_numneigh_short_, 0);
207208
const double r_max_sq = r_max_squared_;
209+
const auto d_numneigh_short = d_numneigh_short_;
208210

209211
Kokkos::parallel_for(
210212
"matgl_kk:count_neigh",
@@ -224,16 +226,17 @@ void PairMATGLKokkos<DeviceType>::compute(int eflag, int vflag)
224226
const double rsq = dx * dx + dy * dy + dz * dz;
225227
if (rsq <= r_max_sq) ++nshort;
226228
}
227-
d_numneigh_short_(i) = nshort;
229+
d_numneigh_short(i) = nshort;
228230
});
229231

230232
// 3) Exclusive prefix-sum into d_first_edge_ (length nall+1).
233+
const auto d_first_edge = d_first_edge_;
231234
Kokkos::parallel_scan(
232235
"matgl_kk:scan_edges",
233236
Kokkos::RangePolicy<DeviceType>(0, nall + 1),
234237
KOKKOS_LAMBDA(const int i, int &update, const bool final) {
235-
const int v = (i < nall) ? d_numneigh_short_(i) : 0;
236-
if (final) d_first_edge_(i) = update;
238+
const int v = (i < nall) ? d_numneigh_short(i) : 0;
239+
if (final) d_first_edge(i) = update;
237240
update += v;
238241
});
239242

@@ -256,6 +259,8 @@ void PairMATGLKokkos<DeviceType>::compute(int eflag, int vflag)
256259
// need one consistent row per physical atom (periodicity goes through
257260
// unit_shifts, not through ghost-row duplication; see pair_matgl.cpp).
258261
const auto d_local_row_of = d_local_row_of_;
262+
const auto d_edge_index = d_edge_index_;
263+
const auto d_unit_shifts = d_unit_shifts_;
259264
const double *const h_inv_host = domain->h_inv;
260265
const double hinv0 = h_inv_host[0], hinv1 = h_inv_host[1], hinv2 = h_inv_host[2];
261266
const double hinv3 = h_inv_host[3], hinv4 = h_inv_host[4], hinv5 = h_inv_host[5];
@@ -268,7 +273,7 @@ void PairMATGLKokkos<DeviceType>::compute(int eflag, int vflag)
268273
const double yi = x(i, 1);
269274
const double zi = x(i, 2);
270275
const int jnum = d_numneigh(i);
271-
int e = d_first_edge_(i);
276+
int e = d_first_edge(i);
272277
for (int jj = 0; jj < jnum; ++jj) {
273278
const int j = d_neighbors(i, jj) & NEIGHMASK;
274279
const double dx = x(j, 0) - xi;
@@ -289,11 +294,11 @@ void PairMATGLKokkos<DeviceType>::compute(int eflag, int vflag)
289294
const double ly = hinv1 * ddy + hinv3 * ddz;
290295
const double lx = hinv0 * ddx + hinv5 * ddy + hinv4 * ddz;
291296

292-
d_edge_index_(0, e) = i;
293-
d_edge_index_(1, e) = j_local;
294-
d_unit_shifts_(e, 0) = static_cast<int64_t>(Kokkos::round(lx));
295-
d_unit_shifts_(e, 1) = static_cast<int64_t>(Kokkos::round(ly));
296-
d_unit_shifts_(e, 2) = static_cast<int64_t>(Kokkos::round(lz));
297+
d_edge_index(0, e) = i;
298+
d_edge_index(1, e) = j_local;
299+
d_unit_shifts(e, 0) = static_cast<int64_t>(Kokkos::round(lx));
300+
d_unit_shifts(e, 1) = static_cast<int64_t>(Kokkos::round(ly));
301+
d_unit_shifts(e, 2) = static_cast<int64_t>(Kokkos::round(lz));
297302
++e;
298303
}
299304
});

lammps/src/ML-MATGL/pair_matgl.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -496,17 +496,17 @@ void PairMATGL::compute(int eflag, int vflag)
496496
f[i][2] += fa[i][2];
497497
}
498498

499-
// 6) Virial — the model returns a 3x3 tensor with the LAMMPS sign
500-
// convention (V_ij = sum r_i F_j). LAMMPS stores 6 Voigt components
501-
// in `virial`: xx, yy, zz, xy, xz, yz.
499+
// 6) Virial — the model returns dE/dstrain. LAMMPS uses
500+
// W = sum_i r_i (x) f_i = -dE/dstrain. Store the six Voigt components
501+
// in the order xx, yy, zz, xy, xz, yz.
502502
if (vflag_global) {
503503
auto vir_t = out.at("virials").toTensor().to(torch::kFloat64);
504504
auto va = vir_t.accessor<double, 2>();
505-
virial[0] += va[0][0];
506-
virial[1] += va[1][1];
507-
virial[2] += va[2][2];
508-
virial[3] += 0.5 * (va[0][1] + va[1][0]);
509-
virial[4] += 0.5 * (va[0][2] + va[2][0]);
510-
virial[5] += 0.5 * (va[1][2] + va[2][1]);
505+
virial[0] -= va[0][0];
506+
virial[1] -= va[1][1];
507+
virial[2] -= va[2][2];
508+
virial[3] -= 0.5 * (va[0][1] + va[1][0]);
509+
virial[4] -= 0.5 * (va[0][2] + va[2][0]);
510+
virial[5] -= 0.5 * (va[1][2] + va[2][1]);
511511
}
512512
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
"""Validate LAMMPS hydrostatic pressure against a finite energy derivative."""
2+
3+
from __future__ import annotations
4+
5+
import argparse
6+
import math
7+
from pathlib import Path
8+
9+
10+
EV_PER_ANGSTROM3_TO_BAR = 1.602176634e6
11+
12+
13+
def read_thermo(path: Path) -> dict[str, float]:
14+
"""Return the last thermo row containing PotEng and pressure columns."""
15+
header: list[str] | None = None
16+
result: dict[str, float] | None = None
17+
18+
for line in path.read_text().splitlines():
19+
fields = line.split()
20+
if fields and fields[0] == "Step" and "PotEng" in fields and "Press" in fields:
21+
header = fields
22+
continue
23+
if header is None or len(fields) != len(header):
24+
continue
25+
try:
26+
values = [float(value) for value in fields]
27+
except ValueError:
28+
continue
29+
result = dict(zip(header, values))
30+
31+
if result is None:
32+
raise ValueError(f"Could not find a pressure thermo row in {path}")
33+
return result
34+
35+
36+
def main() -> None:
37+
parser = argparse.ArgumentParser(description=__doc__)
38+
parser.add_argument("minus", type=Path, help="LAMMPS log at scale 1-epsilon")
39+
parser.add_argument("zero", type=Path, help="LAMMPS log at scale 1")
40+
parser.add_argument("plus", type=Path, help="LAMMPS log at scale 1+epsilon")
41+
parser.add_argument("--epsilon", type=float, default=0.01)
42+
parser.add_argument("--relative-tolerance", type=float, default=0.05)
43+
parser.add_argument("--absolute-tolerance-bar", type=float, default=10.0)
44+
args = parser.parse_args()
45+
46+
minus = read_thermo(args.minus)
47+
zero = read_thermo(args.zero)
48+
plus = read_thermo(args.plus)
49+
50+
# lambda uniformly scales all three cell vectors and fractional atomic
51+
# coordinates. At lambda=1, dE/dlambda is the trace of dE/dstrain.
52+
# LAMMPS pressure is -trace(dE/dstrain)/(3V).
53+
energy_derivative = (plus["PotEng"] - minus["PotEng"]) / (2 * args.epsilon)
54+
finite_difference_pressure = (
55+
-energy_derivative / (3 * zero["Volume"]) * EV_PER_ANGSTROM3_TO_BAR
56+
)
57+
diagonal_pressure = (zero["Pxx"] + zero["Pyy"] + zero["Pzz"]) / 3
58+
59+
print(f"E(1-epsilon) = {minus['PotEng']:.12g} eV")
60+
print(f"E(1+epsilon) = {plus['PotEng']:.12g} eV")
61+
print(f"dE/dlambda = {energy_derivative:.12g} eV")
62+
print(f"FD pressure = {finite_difference_pressure:.12g} bar")
63+
print(f"LAMMPS press = {zero['Press']:.12g} bar")
64+
65+
if not math.isclose(
66+
zero["Press"],
67+
diagonal_pressure,
68+
rel_tol=1e-10,
69+
abs_tol=1e-6,
70+
):
71+
raise AssertionError(
72+
f"Press ({zero['Press']}) does not equal mean diagonal pressure "
73+
f"({diagonal_pressure})"
74+
)
75+
76+
if not math.isclose(
77+
zero["Press"],
78+
finite_difference_pressure,
79+
rel_tol=args.relative_tolerance,
80+
abs_tol=args.absolute_tolerance_bar,
81+
):
82+
raise AssertionError(
83+
"LAMMPS pressure does not match the finite-difference energy "
84+
f"derivative: {zero['Press']} vs {finite_difference_pressure} bar"
85+
)
86+
87+
88+
if __name__ == "__main__":
89+
main()
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# in.matgl_finite_strain
2+
#
3+
# Single-point energy and pressure for a uniformly scaled version of the
4+
# four-atom Mo-S fixture in in.matgl_si. The scale must be supplied on the
5+
# command line, for example:
6+
#
7+
# lmp -var scale 0.99 -in in.matgl_finite_strain
8+
#
9+
# Running at 1-epsilon, 1, and 1+epsilon lets
10+
# check_finite_difference_pressure.py validate the pressure/virial sign from
11+
# the energy derivative without using the model-reported stress as reference.
12+
13+
units metal
14+
atom_style atomic
15+
boundary p p p
16+
17+
atom_modify map yes
18+
newton on
19+
20+
lattice custom ${scale} a1 4.5 0.0 0.0 a2 0.0 4.5 0.0 a3 0.0 0.0 4.5 &
21+
origin 0 0 0 &
22+
basis 0.00 0.00 0.00 &
23+
basis 0.50 0.50 0.50 &
24+
basis 0.50 0.00 0.25 &
25+
basis 0.00 0.50 0.75
26+
region box prism 0 1 0 1 0 1 0 0 0 units lattice
27+
create_box 2 box
28+
create_atoms 1 box basis 1 1 basis 2 2 basis 3 1 basis 4 2
29+
30+
mass 1 95.95
31+
mass 2 32.06
32+
33+
pair_style matgl
34+
pair_coeff * * model.pt Mo S
35+
36+
thermo_style custom step pe press pxx pyy pzz vol
37+
thermo_modify format float %.15g
38+
thermo 1
39+
40+
run 0

0 commit comments

Comments
 (0)