Skip to content

Commit 17c87b0

Browse files
authored
Merge branch 'deepmodeling:master' into master
2 parents a625981 + 2e3117e commit 17c87b0

26 files changed

Lines changed: 1650 additions & 53 deletions

deepmd/calculator.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,15 @@ def __init__(
9191
type_dict: dict[str, int] | None = None,
9292
neighbor_list: Optional["NeighborList"] = None,
9393
head: str | None = None,
94+
nlist_backend: str = "auto",
9495
**kwargs: Any,
9596
) -> None:
9697
Calculator.__init__(self, label=label, **kwargs)
9798
self.dp = DeepPot(
9899
str(Path(model).resolve()),
99100
neighbor_list=neighbor_list,
100101
head=head,
102+
nlist_backend=nlist_backend,
101103
)
102104
if type_dict:
103105
self.type_dict = type_dict

deepmd/dpmodel/model/ener_model.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
from deepmd.dpmodel.output_def import (
2222
FittingOutputDef,
2323
)
24+
from deepmd.dpmodel.utils.neighbor_list import (
25+
NeighborList,
26+
)
2427

2528
from .dp_model import (
2629
DPModelCommon,
@@ -88,7 +91,23 @@ def call(
8891
aparam: Array | None = None,
8992
do_atomic_virial: bool = False,
9093
charge_spin: Array | None = None,
94+
neighbor_list: NeighborList | None = None,
9195
) -> dict[str, Array]:
96+
"""Evaluate the energy model.
97+
98+
Most arguments share the meaning of :meth:`call_common`.
99+
100+
Parameters
101+
----------
102+
neighbor_list
103+
The neighbor-list construction strategy forwarded to
104+
:meth:`call_common`. ``None`` uses the default all-pairs builder
105+
(:class:`~deepmd.dpmodel.utils.neighbor_list.NeighborList`
106+
subclass :class:`~deepmd.dpmodel.utils.default_neighbor_list.DefaultNeighborList`),
107+
reproducing the historical behavior; an alternative strategy may be
108+
injected to accelerate neighbor-list construction without changing
109+
the model outputs.
110+
"""
92111
model_ret = self.call_common(
93112
coord,
94113
atype,
@@ -97,6 +116,7 @@ def call(
97116
aparam=aparam,
98117
charge_spin=charge_spin,
99118
do_atomic_virial=do_atomic_virial,
119+
neighbor_list=neighbor_list,
100120
)
101121
model_predict = {}
102122
model_predict["atom_energy"] = model_ret["energy"]

deepmd/dpmodel/model/make_model.py

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,9 @@
3838
check_operation_applied,
3939
)
4040
from deepmd.dpmodel.utils import (
41-
build_neighbor_list,
42-
extend_coord_with_ghosts,
41+
DefaultNeighborList,
42+
NeighborList,
4343
nlist_distinguish_types,
44-
normalize_coord,
4544
)
4645
from deepmd.utils.path import (
4746
DPPath,
@@ -78,6 +77,7 @@ def model_call_from_call_lower(
7877
do_atomic_virial: bool = False,
7978
coord_corr_for_virial: Array | None = None,
8079
charge_spin: Array | None = None,
80+
neighbor_list: NeighborList | None = None,
8181
) -> dict[str, Array]:
8282
"""Return model prediction from lower interface.
8383
@@ -96,6 +96,12 @@ def model_call_from_call_lower(
9696
atomic parameter. nf x nloc x nda
9797
do_atomic_virial
9898
If calculate the atomic virial.
99+
neighbor_list
100+
The neighbor-list construction strategy. ``None`` uses the default
101+
all-pairs builder (:class:`DefaultNeighborList`), reproducing the
102+
historical behavior. An alternative strategy (e.g. an O(N) cell list)
103+
may be injected to speed up neighbor-list construction; it returns the
104+
same extended representation, so model outputs are unchanged.
99105
100106
Returns
101107
-------
@@ -107,26 +113,9 @@ def model_call_from_call_lower(
107113
nframes, nloc = atype.shape[:2]
108114
cc, bb, fp, ap = coord, box, fparam, aparam
109115
del coord, box, fparam, aparam
110-
if bb is not None:
111-
coord_normalized = normalize_coord(
112-
cc.reshape(nframes, nloc, 3),
113-
bb.reshape(nframes, 3, 3),
114-
)
115-
else:
116-
xp = array_api_compat.array_namespace(cc)
117-
coord_normalized = xp.reshape(cc, (nframes, nloc, 3))
118-
extended_coord, extended_atype, mapping = extend_coord_with_ghosts(
119-
coord_normalized, atype, bb, rcut
120-
)
121-
nlist = build_neighbor_list(
122-
extended_coord,
123-
extended_atype,
124-
nloc,
125-
rcut,
126-
sel,
127-
# types will be distinguished in the lower interface,
128-
# so it doesn't need to be distinguished here
129-
distinguish_types=False,
116+
builder = neighbor_list if neighbor_list is not None else DefaultNeighborList()
117+
extended_coord, extended_atype, nlist, mapping = builder.build(
118+
cc, atype, bb, rcut, sel
130119
)
131120
extended_coord = extended_coord.reshape(nframes, -1, 3)
132121
if coord_corr_for_virial is not None:
@@ -269,6 +258,7 @@ def call_common(
269258
do_atomic_virial: bool = False,
270259
coord_corr_for_virial: Array | None = None,
271260
charge_spin: Array | None = None,
261+
neighbor_list: NeighborList | None = None,
272262
) -> dict[str, Array]:
273263
"""Return model prediction.
274264
@@ -290,6 +280,11 @@ def call_common(
290280
coord_corr_for_virial
291281
The coordinates correction for virial.
292282
shape: nf x (nloc x 3)
283+
neighbor_list
284+
The neighbor-list construction strategy. ``None`` uses the
285+
default all-pairs builder; an alternative strategy (e.g. an O(N)
286+
cell list) may be injected to speed up neighbor-list construction
287+
without changing model outputs.
293288
294289
Returns
295290
-------
@@ -316,6 +311,7 @@ def call_common(
316311
do_atomic_virial=do_atomic_virial,
317312
coord_corr_for_virial=coord_corr_for_virial,
318313
charge_spin=cs,
314+
neighbor_list=neighbor_list,
319315
)
320316
model_predict = self._output_type_cast(model_predict, input_prec)
321317
return model_predict

deepmd/dpmodel/utils/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
# SPDX-License-Identifier: LGPL-3.0-or-later
2+
from .default_neighbor_list import (
3+
DefaultNeighborList,
4+
)
25
from .env_mat import (
36
EnvMat,
47
)
@@ -15,6 +18,9 @@
1518
is_lmdb,
1619
make_neighbor_stat_data,
1720
)
21+
from .neighbor_list import (
22+
NeighborList,
23+
)
1824
from .network import (
1925
EmbeddingNet,
2026
FittingNet,
@@ -53,6 +59,7 @@
5359

5460
__all__ = [
5561
"AtomExcludeMask",
62+
"DefaultNeighborList",
5663
"DistributedSameNlocBatchSampler",
5764
"EmbeddingNet",
5865
"EnvMat",
@@ -62,6 +69,7 @@
6269
"LmdbTestDataNlocView",
6370
"NativeLayer",
6471
"NativeNet",
72+
"NeighborList",
6573
"NetworkCollection",
6674
"PairExcludeMask",
6775
"SameNlocBatchSampler",
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# SPDX-License-Identifier: LGPL-3.0-or-later
2+
"""Default all-pairs neighbor-list builder (historical deepmd behavior)."""
3+
4+
import array_api_compat
5+
6+
from deepmd.dpmodel.array_api import (
7+
Array,
8+
)
9+
10+
from .neighbor_list import (
11+
NeighborList,
12+
)
13+
from .nlist import (
14+
build_neighbor_list,
15+
extend_coord_with_ghosts,
16+
)
17+
from .region import (
18+
normalize_coord,
19+
)
20+
21+
22+
class DefaultNeighborList(NeighborList):
23+
"""All-pairs builder: replicate the cell into periodic images and rank by
24+
distance (:func:`~deepmd.dpmodel.utils.nlist.extend_coord_with_ghosts` +
25+
:func:`~deepmd.dpmodel.utils.nlist.build_neighbor_list`). This is the
26+
default when no strategy is supplied, so results are unchanged.
27+
"""
28+
29+
def build(
30+
self,
31+
coord: Array,
32+
atype: Array,
33+
box: Array | None,
34+
rcut: float,
35+
sel: list[int],
36+
) -> tuple[Array, Array, Array, Array]:
37+
xp = array_api_compat.array_namespace(coord, atype)
38+
nframes, nloc = atype.shape[:2]
39+
if box is not None:
40+
coord_normalized = normalize_coord(
41+
xp.reshape(coord, (nframes, nloc, 3)),
42+
xp.reshape(box, (nframes, 3, 3)),
43+
)
44+
else:
45+
coord_normalized = xp.reshape(coord, (nframes, nloc, 3))
46+
extended_coord, extended_atype, mapping = extend_coord_with_ghosts(
47+
coord_normalized, atype, box, rcut
48+
)
49+
# types are distinguished in the lower interface, so keep them merged here
50+
nlist = build_neighbor_list(
51+
extended_coord,
52+
extended_atype,
53+
nloc,
54+
rcut,
55+
sel,
56+
distinguish_types=False,
57+
)
58+
extended_coord = xp.reshape(extended_coord, (nframes, -1, 3))
59+
return extended_coord, extended_atype, nlist, mapping

deepmd/dpmodel/utils/lmdb_data.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -751,11 +751,17 @@ def set_noise(self, noise_settings: dict[str, Any]) -> None:
751751
@property
752752
def index(self) -> list[int]:
753753
"""Number of batches per system (single system)."""
754-
return [max(1, self.nframes // self.batch_size)]
754+
return [self.total_batch]
755755

756756
@property
757757
def total_batch(self) -> int:
758-
return self.index[0]
758+
if self.mixed_batch:
759+
return math.ceil(self.nframes / self.batch_size) if self.nframes else 0
760+
total = 0
761+
for nloc, indices in self._nloc_groups.items():
762+
bs = self.get_batch_size_for_nloc(nloc)
763+
total += (len(indices) + bs - 1) // bs
764+
return total
759765

760766
@property
761767
def batch_sizes(self) -> list[int]:
@@ -1269,6 +1275,13 @@ def __init__(
12691275
self._seed = seed if seed is not None else 0
12701276
self._epoch = 0
12711277
self._block_targets = block_targets
1278+
self._total_batches = len(
1279+
SameNlocBatchSampler(
1280+
self._reader,
1281+
shuffle=False,
1282+
block_targets=self._block_targets,
1283+
)
1284+
)
12721285

12731286
def set_epoch(self, epoch: int) -> None:
12741287
"""Set epoch for deterministic cross-rank shuffling.
@@ -1304,11 +1317,11 @@ def _partition_batches(self, all_batches: list[list[int]]) -> list[list[int]]:
13041317

13051318
def __len__(self) -> int:
13061319
"""Number of batches for this rank."""
1307-
total = 0
1308-
for nloc, indices in self._reader.nloc_groups.items():
1309-
bs = self._reader.get_batch_size_for_nloc(nloc)
1310-
total += (len(indices) + bs - 1) // bs
1311-
return math.ceil(total / self._world_size)
1320+
return max(
1321+
0,
1322+
(self._total_batches + self._world_size - 1 - self._rank)
1323+
// self._world_size,
1324+
)
13121325

13131326
@property
13141327
def rank(self) -> int:
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# SPDX-License-Identifier: LGPL-3.0-or-later
2+
"""Pluggable neighbor-list construction strategies.
3+
4+
A :class:`NeighborList` turns local coordinates (and an optional cell) into the
5+
*extended* representation consumed by the model's lower interface. The default
6+
all-pairs builder lives in :mod:`deepmd.dpmodel.utils.default_neighbor_list`;
7+
backend-specific O(N) builders (e.g. the ``vesin``-based one in
8+
``deepmd.pt_expt.utils.vesin_neighbor_list``) subclass :class:`NeighborList`
9+
and are injected into the model, so the rest of the model is agnostic to how the
10+
neighbor list was built.
11+
"""
12+
13+
from deepmd.dpmodel.array_api import (
14+
Array,
15+
)
16+
17+
18+
class NeighborList:
19+
"""Strategy that builds the extended neighbor environment from local atoms.
20+
21+
Implementations turn local coordinates into the extended representation: the
22+
coordinates and atom types of local-plus-ghost (periodic-image) atoms, a
23+
candidate neighbor list indexing the extended atoms, and a mapping from each
24+
extended atom to its local owner. Implementations are stateless --
25+
``rcut``/``sel`` are supplied by the model at call time.
26+
"""
27+
28+
def build(
29+
self,
30+
coord: Array,
31+
atype: Array,
32+
box: Array | None,
33+
rcut: float,
34+
sel: list[int],
35+
) -> tuple[Array, Array, Array, Array]:
36+
"""Build the extended system and a candidate neighbor list.
37+
38+
Parameters
39+
----------
40+
coord
41+
local coordinates, shape (nf, nloc, 3) or (nf, nloc*3).
42+
atype
43+
local atom types, shape (nf, nloc).
44+
box
45+
simulation cell, shape (nf, 3, 3) or (nf, 9); ``None`` for non-periodic.
46+
rcut
47+
cutoff radius.
48+
sel
49+
number of selected neighbors per type.
50+
51+
Returns
52+
-------
53+
extended_coord
54+
shape (nf, nall, 3).
55+
extended_atype
56+
shape (nf, nall).
57+
nlist
58+
shape (nf, nloc, nnei), type-undistinguished candidate neighbors
59+
indexing the extended atoms (the lower interface re-formats it:
60+
distance sort, truncate to ``sel``, split by type).
61+
mapping
62+
shape (nf, nall), mapping each extended atom to its local owner.
63+
"""
64+
raise NotImplementedError

0 commit comments

Comments
 (0)