diff --git a/dpgen/generator/arginfo.py b/dpgen/generator/arginfo.py index 0f8abb865..fe0e389ad 100644 --- a/dpgen/generator/arginfo.py +++ b/dpgen/generator/arginfo.py @@ -654,15 +654,17 @@ def fp_style_vasp_args() -> list[Argument]: "Skip configurations with unreasonable simulation box geometries before " "first-principles calculations. This parameter accepts a semicolon-separated " "string of colon-separated key-value pairs defining geometric criteria. " - "Example: 'length_ratio:3;height_ratio:3;wrap_ratio:0.5;tilt_ratio:0.5'. " + "Example: 'length_ratio:3;height_ratio:3;min_distance:1.0'. " "Available criteria: " "(1) 'length_ratio': maximum ratio of cell edge lengths (max/min); " "(2) 'height_ratio': ratio of maximum cell edge length to minimum " "face-to-face distance; " "(3) 'wrap_ratio': maximum absolute ratio of off-diagonal to diagonal " "cell matrix elements, controlling triclinic wrapping; " - "(4) 'tilt_ratio': maximum absolute tilt ratio for triclinic cells. " - "Configurations exceeding any specified threshold are skipped." + "(4) 'tilt_ratio': maximum absolute tilt ratio for triclinic cells; " + "(5) 'min_distance' (alias 'min_dist'): minimum allowed periodic " + "interatomic distance in Angstrom. Configurations violating any " + "specified threshold are skipped." ) return [ diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index 0ce513a96..bf4a504ca 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -2319,6 +2319,7 @@ def check_cluster(conf_name, fp_cluster_vacuum, fmt="lammps/dump"): def check_bad_box(conf_name, criteria, fmt="lammps/dump"): + """Return whether a configuration violates any FP screening criterion.""" all_c = criteria.split(";") sys = dpdata.System(conf_name, fmt) assert sys.get_nframes() == 1 @@ -2353,6 +2354,18 @@ def check_bad_box(conf_name, criteria, fmt="lammps/dump"): ] if np.max(np.abs(ratio)) > float(value): is_bad = True + elif key in {"min_distance", "min_dist"}: + from ase.geometry import find_mic + + natoms = sys.get_natoms() + if natoms > 1: + atom_pairs = np.triu_indices(natoms, k=1) + vectors = ( + sys["coords"][0][atom_pairs[0]] - sys["coords"][0][atom_pairs[1]] + ) + _, distances = find_mic(vectors, sys["cells"][0], pbc=True) + if np.min(distances) < float(value): + is_bad = True else: raise RuntimeError("unknow key", key) return is_bad diff --git a/tests/generator/test_check_bad_box.py b/tests/generator/test_check_bad_box.py index 7de9070bd..ecbf33052 100644 --- a/tests/generator/test_check_bad_box.py +++ b/tests/generator/test_check_bad_box.py @@ -23,3 +23,10 @@ def test_height_ratio(self): conf_bad = os.path.join(dirname, "check_bad_box", "bad.height.POSCAR") self.assertTrue(check_bad_box(conf_bad, "height_ratio:5", fmt="vasp/POSCAR")) self.assertFalse(check_bad_box(conf_bad, "length_ratio:5", fmt="vasp/POSCAR")) + + def test_min_distance(self): + dirname = os.path.dirname(__file__) + conf = os.path.join(dirname, "check_bad_box", "good.lammpstrj") + self.assertFalse(check_bad_box(conf, "min_distance:2.0")) + self.assertTrue(check_bad_box(conf, "min_distance:2.2")) + self.assertTrue(check_bad_box(conf, "min_dist:2.2"))