Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
120fe0d
feat(long-range): latent-charge long-range module for DPA4C
OutisLi Oct 24, 2026
8264c3a
perf(long-range): accelerate long-range DPA4C inference in LAMMPS
OutisLi Oct 25, 2026
0289a0d
perf(long-range): fuse the charge network and harden the LAMMPS path
OutisLi Oct 25, 2026
4a5eaea
fix(long-range): keep the self term of a periodic frame without wave …
OutisLi Oct 30, 2026
5f4dd24
fix(long-range): reject a composition without the NeighborGraph lower
OutisLi Oct 30, 2026
6fcbb74
test(long-range): check the pair virial independently and integrate w…
OutisLi Oct 30, 2026
ed9e304
fix(long-range): form the mesh virial from the derivative of the kern…
OutisLi Oct 30, 2026
72cf6c3
fix(api): bump the C API version for the cell argument and check empt…
OutisLi Oct 30, 2026
b09c79a
fix(api): keep the C API at version 31
OutisLi Oct 30, 2026
3751d3b
fix(long-range): fail fast where PyTorch breaks the mesh evaluation
OutisLi Oct 30, 2026
ee23658
test(long-range): check the orientation of the pair virial
OutisLi Oct 30, 2026
ec436bc
Merge remote-tracking branch 'upstream/master' into pr/long-range
OutisLi Oct 30, 2026
50cb290
fix(pt_expt): keep the lower of a frozen model through change-bias
OutisLi Oct 31, 2026
7c57294
test(long-range): cover change-bias, atomic parameters and the C++ me…
OutisLi Oct 31, 2026
92b471b
Merge remote-tracking branch 'upstream/master' into pr/long-range
OutisLi Oct 31, 2026
b46418a
Merge remote-tracking branch 'upstream/master' into pr/long-range
OutisLi Oct 31, 2026
e890b20
fix(pt_expt): carry the per-atom virial through change-bias and re-se…
OutisLi Oct 31, 2026
8407bdc
test(long-range): cover the deployed options and the change-bias forms
OutisLi Oct 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 85 additions & 1 deletion deepmd/dpmodel/atomic_model/base_atomic_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,67 @@ def fused_decomposition(self) -> "tuple[Any, Any] | None":
"""
return None

def has_long_range(self) -> bool:
"""Whether the atomic energy carries a long-range module beside a fitting.

Generic capability (concrete default ``False``): a model wrapping the
atomic model asks it whether the energy holds a long-range interaction,
e.g. before combining it with a scheme defined for a learned local
energy alone.
"""
return False

def required_topologies(self) -> tuple[str, ...]:
"""Names of the topologies the graph forward reads beside the neighbor graph.

Generic capability (concrete default: none) in the sense of
:meth:`uses_graph_lower`: a long-range module, whose interaction has no
cutoff, declares the topology it is evaluated on (see
:func:`~deepmd.dpmodel.utils.neighbor_graph.build_topologies`), and
every caller of the graph forward builds what is declared.
"""
return ()

def topology_params(self) -> dict[str, dict[str, Any]]:
"""The build parameters of the topologies of :meth:`required_topologies`.

Generic capability (concrete default: none) in the sense of
:meth:`required_topologies`: a module whose topology depends on its
own state, such as the wave-number cutoff of a periodic long-range
module, which follows its kernel, gives it here, by topology name.
"""
return {}

def build_topologies(
self,
coord: Any,
n_node: Any,
box: Any,
*,
atype: Any,
node_mask: Any = None,
pair_excl: Any = None,
) -> dict[str, Any]:
"""Build the topologies of :meth:`required_topologies` for one batch.

See :func:`~deepmd.dpmodel.utils.neighbor_graph.build_topologies`;
the build parameters are those of :meth:`topology_params`.
"""
from deepmd.dpmodel.utils.neighbor_graph import (
build_topologies,
)

return build_topologies(
self.required_topologies(),
coord,
n_node,
box,
node_mask,
atype=atype,
pair_excl=pair_excl,
topology_params=self.topology_params(),
)

def supports_native_spin(self) -> bool:
"""Returns whether this atomic model consumes a per-atom spin input.

Expand Down Expand Up @@ -494,6 +555,8 @@ def forward_common_atomic_graph(
charge_spin: Array | None = None,
spin: Array | None = None,
comm_dict: dict | None = None,
topologies: dict[str, Any] | None = None,
node_mask: Array | None = None,
) -> dict:
"""Graph analogue of :meth:`forward_common_atomic` on the flat node axis.

Expand Down Expand Up @@ -530,6 +593,13 @@ def forward_common_atomic_graph(
(and, from there, the descriptor's ``call_graph``). ``None`` for
non-parallel inference (default). Mirrors :meth:`forward_common_atomic`'s
``comm_dict`` on the dense route.
topologies
the topologies of :meth:`required_topologies` by name, built on the
node axis of ``graph``; ``None`` when none is required.
node_mask
flat mask of the nodes an enclosing model counts (real, owned and
not excluded there), combined with this model's own; ``None``
imposes nothing further.

Returns
-------
Expand All @@ -538,6 +608,8 @@ def forward_common_atomic_graph(

"""
graph, atype_clamped, output_mask = self._prepare_graph_inputs(graph, atype)
if node_mask is not None:
output_mask = output_mask & node_mask
ret_dict = self.forward_atomic_graph(
graph,
atype_clamped,
Expand All @@ -546,6 +618,8 @@ def forward_common_atomic_graph(
charge_spin=charge_spin,
spin=spin,
comm_dict=comm_dict,
topologies=topologies,
node_mask=output_mask,
)
return self._finalize_atomic_ret(ret_dict, output_mask, atype)

Expand Down Expand Up @@ -1014,9 +1088,18 @@ def model_forward(
self.get_rcut(),
pair_excl=self.pair_excl,
)
atype_flat = xp.reshape(atype, (-1,))
topologies = self.build_topologies(
xp.reshape(coord, (-1, 3)),
graph.n_node,
box,
atype=atype_flat,
node_mask=atype_flat >= 0,
pair_excl=self.pair_excl,
)
atomic_ret = self.forward_common_atomic_graph(
graph,
xp.reshape(atype, (-1,)),
atype_flat,
fparam=fparam,
aparam=(
xp.reshape(
Expand All @@ -1028,6 +1111,7 @@ def model_forward(
),
charge_spin=charge_spin,
spin=None if spin is None else xp.reshape(spin, (-1, 3)),
topologies=topologies,
)
# The graph route works on a flat node axis; restore the
# per-frame layout the dense route returns.
Expand Down
Loading
Loading