Skip to content

Commit

Permalink
build neighbor list with external Python program (#3046)
Browse files Browse the repository at this point in the history
Fix #2877

---------

Signed-off-by: Jinzhe Zeng <jinzhe.zeng@rutgers.edu>
  • Loading branch information
njzjz authored Dec 11, 2023
1 parent ce25ff4 commit a6f1333
Show file tree
Hide file tree
Showing 10 changed files with 326 additions and 30 deletions.
5 changes: 4 additions & 1 deletion deepmd/calculator.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ class DP(Calculator):
type_dict : Dict[str, int], optional
mapping of element types and their numbers, best left None and the calculator
will infer this information from model, by default None
neighbor_list : ase.neighborlist.NeighborList, optional
The neighbor list object. If None, then build the native neighbor list.
Examples
--------
Expand Down Expand Up @@ -83,10 +85,11 @@ def __init__(
model: Union[str, "Path"],
label: str = "DP",
type_dict: Optional[Dict[str, int]] = None,
neighbor_list=None,
**kwargs,
) -> None:
Calculator.__init__(self, label=label, **kwargs)
self.dp = DeepPotential(str(Path(model).resolve()))
self.dp = DeepPotential(str(Path(model).resolve()), neighbor_list=neighbor_list)
if type_dict:
self.type_dict = type_dict
else:
Expand Down
7 changes: 7 additions & 0 deletions deepmd/infer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ def DeepPotential(
load_prefix: str = "load",
default_tf_graph: bool = False,
input_map: Optional[dict] = None,
neighbor_list=None,
) -> Union[DeepDipole, DeepGlobalPolar, DeepPolar, DeepPot, DeepDOS, DeepWFC]:
"""Factory function that will inialize appropriate potential read from `model_file`.
Expand All @@ -71,6 +72,8 @@ def DeepPotential(
If uses the default tf graph, otherwise build a new tf graph for evaluation
input_map : dict, optional
The input map for tf.import_graph_def. Only work with default tf graph
neighbor_list : ase.neighborlist.NeighborList, optional
The neighbor list object. If None, then build the native neighbor list.
Returns
-------
Expand All @@ -97,6 +100,7 @@ def DeepPotential(
load_prefix=load_prefix,
default_tf_graph=default_tf_graph,
input_map=input_map,
neighbor_list=neighbor_list,
)
elif model_type == "dos":
dp = DeepDOS(
Expand All @@ -111,20 +115,23 @@ def DeepPotential(
load_prefix=load_prefix,
default_tf_graph=default_tf_graph,
input_map=input_map,
neighbor_list=neighbor_list,
)
elif model_type == "polar":
dp = DeepPolar(
mf,
load_prefix=load_prefix,
default_tf_graph=default_tf_graph,
input_map=input_map,
neighbor_list=neighbor_list,
)
elif model_type == "global_polar":
dp = DeepGlobalPolar(
mf,
load_prefix=load_prefix,
default_tf_graph=default_tf_graph,
input_map=input_map,
neighbor_list=neighbor_list,
)
elif model_type == "wfc":
dp = DeepWFC(
Expand Down
4 changes: 4 additions & 0 deletions deepmd/infer/deep_dipole.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ class DeepDipole(DeepTensor):
If uses the default tf graph, otherwise build a new tf graph for evaluation
input_map : dict, optional
The input map for tf.import_graph_def. Only work with default tf graph
neighbor_list : ase.neighborlist.NeighborList, optional
The neighbor list object. If None, then build the native neighbor list.
Warnings
--------
Expand All @@ -41,6 +43,7 @@ def __init__(
load_prefix: str = "load",
default_tf_graph: bool = False,
input_map: Optional[dict] = None,
neighbor_list=None,
) -> None:
# use this in favor of dict update to move attribute from class to
# instance namespace
Expand All @@ -58,6 +61,7 @@ def __init__(
load_prefix=load_prefix,
default_tf_graph=default_tf_graph,
input_map=input_map,
neighbor_list=neighbor_list,
)

def get_dim_fparam(self) -> int:
Expand Down
95 changes: 95 additions & 0 deletions deepmd/infer/deep_eval.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ class DeepEval:
as the initial batch size.
input_map : dict, optional
The input map for tf.import_graph_def. Only work with default tf graph
neighbor_list : ase.neighborlist.NewPrimitiveNeighborList, optional
The ASE neighbor list class to produce the neighbor list. If None, the
neighbor list will be built natively in the model.
"""

load_prefix: str # set by subclass
Expand All @@ -56,6 +59,7 @@ def __init__(
default_tf_graph: bool = False,
auto_batch_size: Union[bool, int, AutoBatchSize] = False,
input_map: Optional[dict] = None,
neighbor_list=None,
):
self.graph = self._load_graph(
model_file,
Expand Down Expand Up @@ -86,6 +90,8 @@ def __init__(
else:
raise TypeError("auto_batch_size should be bool, int, or AutoBatchSize")

self.neighbor_list = neighbor_list

@property
@lru_cache(maxsize=None)
def model_type(self) -> str:
Expand Down Expand Up @@ -360,3 +366,92 @@ def eval_typeebd(self) -> np.ndarray:
t_typeebd = self._get_tensor("t_typeebd:0")
[typeebd] = run_sess(self.sess, [t_typeebd], feed_dict={})
return typeebd

def build_neighbor_list(
self,
coords: np.ndarray,
cell: Optional[np.ndarray],
atype: np.ndarray,
imap: np.ndarray,
neighbor_list,
):
"""Make the mesh with neighbor list for a single frame.
Parameters
----------
coords : np.ndarray
The coordinates of atoms. Should be of shape [natoms, 3]
cell : Optional[np.ndarray]
The cell of the system. Should be of shape [3, 3]
atype : np.ndarray
The type of atoms. Should be of shape [natoms]
imap : np.ndarray
The index map of atoms. Should be of shape [natoms]
neighbor_list : ase.neighborlist.NewPrimitiveNeighborList
ASE neighbor list. The following method or attribute will be
used/set: bothways, self_interaction, update, build, first_neigh,
pair_second, offset_vec.
Returns
-------
natoms_vec : np.ndarray
The number of atoms. This tensor has the length of Ntypes + 2
natoms[0]: nloc
natoms[1]: nall
natoms[i]: 2 <= i < Ntypes+2, number of type i atoms for nloc
coords : np.ndarray
The coordinates of atoms, including ghost atoms. Should be of
shape [nframes, nall, 3]
atype : np.ndarray
The type of atoms, including ghost atoms. Should be of shape [nall]
mesh : np.ndarray
The mesh in nei_mode=4.
imap : np.ndarray
The index map of atoms. Should be of shape [nall]
ghost_map : np.ndarray
The index map of ghost atoms. Should be of shape [nghost]
"""
pbc = np.repeat(cell is not None, 3)
cell = cell.reshape(3, 3)
positions = coords.reshape(-1, 3)
neighbor_list.bothways = True
neighbor_list.self_interaction = False
if neighbor_list.update(pbc, cell, positions):
neighbor_list.build(pbc, cell, positions)
first_neigh = neighbor_list.first_neigh.copy()
pair_second = neighbor_list.pair_second.copy()
offset_vec = neighbor_list.offset_vec.copy()
# get out-of-box neighbors
out_mask = np.any(offset_vec != 0, axis=1)
out_idx = pair_second[out_mask]
out_offset = offset_vec[out_mask]
out_coords = positions[out_idx] + out_offset.dot(cell)
atype = np.array(atype, dtype=int)
out_atype = atype[out_idx]

nloc = positions.shape[0]
nghost = out_idx.size
all_coords = np.concatenate((positions, out_coords), axis=0)
all_atype = np.concatenate((atype, out_atype), axis=0)
# convert neighbor indexes
ghost_map = pair_second[out_mask]
pair_second[out_mask] = np.arange(nloc, nloc + nghost)
# get the mesh
mesh = np.zeros(16 + nloc * 2 + pair_second.size, dtype=int)
mesh[0] = nloc
# ilist
mesh[16 : 16 + nloc] = np.arange(nloc)
# numnei
mesh[16 + nloc : 16 + nloc * 2] = first_neigh[1:] - first_neigh[:-1]
# jlist
mesh[16 + nloc * 2 :] = pair_second

# natoms_vec
natoms_vec = np.zeros(self.ntypes + 2).astype(int)
natoms_vec[0] = nloc
natoms_vec[1] = nloc + nghost
for ii in range(self.ntypes):
natoms_vec[ii + 2] = np.count_nonzero(atype == ii)
# imap append ghost atoms
imap = np.concatenate((imap, np.arange(nloc, nloc + nghost)))
return natoms_vec, all_coords, all_atype, mesh, imap, ghost_map
13 changes: 12 additions & 1 deletion deepmd/infer/deep_polar.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ class DeepPolar(DeepTensor):
If uses the default tf graph, otherwise build a new tf graph for evaluation
input_map : dict, optional
The input map for tf.import_graph_def. Only work with default tf graph
neighbor_list : ase.neighborlist.NeighborList, optional
The neighbor list object. If None, then build the native neighbor list.
Warnings
--------
Expand All @@ -44,6 +46,7 @@ def __init__(
load_prefix: str = "load",
default_tf_graph: bool = False,
input_map: Optional[dict] = None,
neighbor_list=None,
) -> None:
# use this in favor of dict update to move attribute from class to
# instance namespace
Expand All @@ -61,6 +64,7 @@ def __init__(
load_prefix=load_prefix,
default_tf_graph=default_tf_graph,
input_map=input_map,
neighbor_list=neighbor_list,
)

def get_dim_fparam(self) -> int:
Expand All @@ -83,10 +87,16 @@ class DeepGlobalPolar(DeepTensor):
The prefix in the load computational graph
default_tf_graph : bool
If uses the default tf graph, otherwise build a new tf graph for evaluation
neighbor_list : ase.neighborlist.NeighborList, optional
The neighbor list object. If None, then build the native neighbor list.
"""

def __init__(
self, model_file: str, load_prefix: str = "load", default_tf_graph: bool = False
self,
model_file: str,
load_prefix: str = "load",
default_tf_graph: bool = False,
neighbor_list=None,
) -> None:
self.tensors.update(
{
Expand All @@ -101,6 +111,7 @@ def __init__(
model_file,
load_prefix=load_prefix,
default_tf_graph=default_tf_graph,
neighbor_list=None,
)

def eval(
Expand Down
Loading

0 comments on commit a6f1333

Please sign in to comment.