@@ -170,7 +170,7 @@ def convert(
170170 validate : bool = True ,
171171 strict : bool = False ,
172172) -> str :
173- """Convert a structure/trajectory file to ``deepmd/npy`` format.
173+ """Convert one or more structure files to ``deepmd/npy`` format.
174174
175175 Thin wrapper over ``dpdata``. When *fmt* is ``None`` (or ``"auto"``),
176176 dpdata auto-detects the format from the file extension or content.
@@ -180,7 +180,17 @@ def convert(
180180 Parameters
181181 ----------
182182 input_path : str
183- Path to the input file or directory.
183+ Path or glob pattern to the input file(s) (e.g. ``"calcs/**/OUTCAR"``,
184+ ``"raw/*.sdf"``). Wildcards (``*``, ``?``, ``[``) are expanded via
185+ :func:`glob.glob` with ``recursive=True``:
186+
187+ - **No wildcards** — treated as a literal path; output goes directly
188+ into *output_dir*.
189+ - **Glob matches 1 file** — same as literal path (output → *output_dir*).
190+ - **Glob matches N > 1 files** — each match is converted into a numbered
191+ subdirectory ``{output_dir}/sys_{i:04d}/`` (zero-indexed, sorted).
192+ - **Glob matches nothing** — raises ``FileNotFoundError``.
193+
184194 output_dir : str
185195 Destination directory for the deepmd/npy output.
186196 fmt : str, optional
@@ -198,6 +208,54 @@ def convert(
198208 str
199209 Resolved path to the output directory.
200210 """
211+ # --- glob expansion ---
212+ input_str = str (input_path )
213+ if any (ch in input_str for ch in "*?[" ):
214+ matches = sorted (_glob .glob (input_str , recursive = True ))
215+ if not matches :
216+ raise FileNotFoundError (f"No files matched pattern: { input_str } " )
217+ if len (matches ) == 1 :
218+ # Single match — behave identically to literal path.
219+ input_files = [(matches [0 ], str (Path (output_dir ).resolve ()))]
220+ else :
221+ output_root = str (Path (output_dir ).resolve ())
222+ input_files = [
223+ (m , str (Path (output_root ) / f"sys_{ i :04d} " ))
224+ for i , m in enumerate (matches )
225+ ]
226+ else :
227+ input_files = [(input_str , str (Path (output_dir ).resolve ()))]
228+
229+ for _in_path , _out_dir in input_files :
230+ _convert_one (
231+ input_path = _in_path ,
232+ output_dir = _out_dir ,
233+ fmt = fmt ,
234+ type_map = type_map ,
235+ validate = validate ,
236+ strict = strict ,
237+ )
238+
239+ return str (Path (output_dir ).resolve ())
240+
241+
242+ # ---------------------------------------------------------------------------
243+ # _convert_one() — single-file dpdata conversion (internal helper)
244+ # ---------------------------------------------------------------------------
245+
246+
247+ def _convert_one (
248+ input_path : str ,
249+ output_dir : str ,
250+ fmt : str | None = None ,
251+ type_map : list [str ] = None ,
252+ validate : bool = True ,
253+ strict : bool = False ,
254+ ) -> str :
255+ """Convert a single structure file to ``deepmd/npy`` format.
256+
257+ Internal helper called by :func:`convert` — do not use directly.
258+ """
201259 try :
202260 import dpdata
203261 except ImportError as e :
0 commit comments