@@ -991,6 +991,69 @@ def world_size(self) -> int:
991991 return self ._world_size
992992
993993
994+ def make_neighbor_stat_data (
995+ lmdb_path : str ,
996+ type_map : list [str ] | None ,
997+ max_frames : int = 2000 ,
998+ ) -> Any :
999+ """Create a duck-typed DeepmdDataSystem-like object for neighbor stat from LMDB.
1000+
1001+ Samples up to *max_frames* frames, groups them by nloc, and returns an
1002+ object whose attributes satisfy the interface expected by
1003+ ``NeighborStat.iterator()`` and ``UpdateSel.get_nbor_stat()``.
1004+ """
1005+ from types import (
1006+ SimpleNamespace ,
1007+ )
1008+
1009+ reader = LmdbDataReader (lmdb_path , type_map = type_map )
1010+ nframes = len (reader )
1011+ rng = np .random .RandomState (42 )
1012+ if nframes > max_frames :
1013+ indices = np .sort (rng .choice (nframes , max_frames , replace = False ))
1014+ else :
1015+ indices = np .arange (nframes , dtype = np .int64 )
1016+
1017+ # Read sampled frames, group by nloc
1018+ nloc_frames : dict [int , list [tuple [np .ndarray , np .ndarray , np .ndarray | None ]]] = {}
1019+ for idx in indices :
1020+ frame = reader [int (idx )]
1021+ atype = frame ["atype" ]
1022+ nloc = len (atype )
1023+ nloc_frames .setdefault (nloc , []).append (
1024+ (frame ["coord" ], atype , frame .get ("box" ))
1025+ )
1026+
1027+ # Build per-nloc data_system proxies
1028+ data_systems = []
1029+ system_dirs : list [str ] = []
1030+ for nloc , frames in nloc_frames .items ():
1031+ coords = np .stack ([c .reshape (nloc * 3 ) for c , _ , _ in frames ])
1032+ types = np .stack ([a .reshape (nloc ) for _ , a , _ in frames ])
1033+ has_box = frames [0 ][2 ] is not None
1034+ boxes = np .stack ([b .reshape (9 ) for _ , _ , b in frames ]) if has_box else None
1035+ set_data = {"coord" : coords , "type" : types , "box" : boxes }
1036+ label = f"lmdb:{ nloc } atoms"
1037+ proxy = SimpleNamespace (
1038+ dirs = [label ],
1039+ pbc = has_box ,
1040+ mixed_type = True ,
1041+ get_natoms = lambda _nloc = nloc : _nloc ,
1042+ _load_set = lambda _d , _sd = set_data : _sd ,
1043+ )
1044+ data_systems .append (proxy )
1045+ system_dirs .append (label )
1046+
1047+ ntypes = len (type_map ) if type_map else reader ._ntypes
1048+ return SimpleNamespace (
1049+ system_dirs = system_dirs ,
1050+ data_systems = data_systems ,
1051+ get_batch = lambda : None ,
1052+ get_ntypes = lambda : ntypes ,
1053+ mixed_type = True ,
1054+ )
1055+
1056+
9941057class LmdbTestData :
9951058 """LMDB-backed data reader for dp test.
9961059
0 commit comments