@@ -217,20 +217,18 @@ def formula_to_npy(
217217 csv_path : str ,
218218 output_dir : str ,
219219 poscar : str ,
220- formula_col : int | str = 0 ,
221- property_col : int | str = 1 ,
222- property_name : str = "property " ,
220+ formula_col : str = "formula" ,
221+ property_col : str = "Property" ,
222+ property_name : str = "Property " ,
223223 base_element : str | None = None ,
224224 sets : int = 1 ,
225225 seed : int = 42 ,
226226) -> list [str ]:
227227 """Convert a formula CSV + template POSCAR to ``deepmd/npy`` systems.
228228
229- CSV format: two or more columns. The formula column holds composition
229+ CSV format: two or more named columns. The formula column holds composition
230230 strings (e.g. ``Ni0.65Gd0.15Fe0.10Co0.05Yb0.05O2H1``); the property
231- column holds the scalar target value. Header auto-detected: if the first
232- data row's property column cannot be parsed as ``float``, that row is
233- skipped as a header.
231+ column holds the scalar target value.
234232
235233 For each CSV row, *sets* random doped structures are generated. Each
236234 structure is written as a ``deepmd/npy`` system under
@@ -244,13 +242,13 @@ def formula_to_npy(
244242 Destination directory for ``deepmd/npy`` output.
245243 poscar : str
246244 Path to template POSCAR (VASP format).
247- formula_col : int | str
248- Column index (0-based) or column name for the formula. Default: 0 .
249- property_col : int | str
250- Column index (0-based) or column name for the property value. Default: 1 .
245+ formula_col : str
246+ Column name for the formula. Default: ``"formula"`` .
247+ property_col : str
248+ Column name for the property value. Default: ``"Property"`` .
251249 property_name : str
252250 Label key written into each system (``set.000/{property_name}.npy``).
253- Default: ``"property "``.
251+ Default: ``"Property "``.
254252 base_element : str | None
255253 Host element for random substitution. Auto-inferred from the template
256254 POSCAR when ``None``.
@@ -288,21 +286,25 @@ def formula_to_npy(
288286 break
289287 delimiter = "\t " if "\t " in first_line else ","
290288 fh .seek (0 )
291- reader = csv .reader (fh , delimiter = delimiter )
289+ reader = csv .DictReader (fh , delimiter = delimiter )
290+ if reader .fieldnames is None :
291+ raise ValueError (f"No header row found in formula CSV: { csv_path !r} " )
292+ formula_header = _resolve_col (formula_col , reader .fieldnames )
293+ property_header = _resolve_col (property_col , reader .fieldnames )
292294 for raw_row in reader :
293- if not raw_row or all (c .strip () == "" for c in raw_row ):
295+ if raw_row is None or all (( v or "" ) .strip () == "" for v in raw_row . values () ):
294296 continue
295- row_values = [c .strip () for c in raw_row ]
296- # Resolve column indices from names if needed.
297- fidx = _resolve_col (formula_col , row_values , allow_name = True )
298- pidx = _resolve_col (property_col , row_values , allow_name = True )
299- formula_str = row_values [fidx ]
300- prop_str = row_values [pidx ]
297+ formula_str = (raw_row .get (formula_header ) or "" ).strip ()
298+ prop_str = (raw_row .get (property_header ) or "" ).strip ()
299+ if not formula_str :
300+ raise ValueError (f"Empty formula value in column { formula_header !r} " )
301301 try :
302302 prop_val = float (prop_str )
303303 except ValueError :
304- # Likely a header row — skip.
305- continue
304+ raise ValueError (
305+ f"Could not parse property value { prop_str !r} "
306+ f"from column { property_header !r} "
307+ ) from None
306308 rows .append ((formula_str , prop_val ))
307309
308310 if not rows :
@@ -367,21 +369,12 @@ def formula_to_npy(
367369
368370
369371def _resolve_col (
370- spec : int | str ,
371- row_values : list [str ],
372- allow_name : bool = False ,
373- ) -> int :
374- """Resolve a column specifier to an integer index.
375-
376- - *int* → used directly.
377- - *str* + ``allow_name=True`` → looks up the column name in *row_values*
378- (case-insensitive), falling back to ``int(spec)``.
379- """
380- if isinstance (spec , int ):
381- return spec
382- if allow_name :
383- lower_map = {v .lower (): i for i , v in enumerate (row_values )}
384- key = spec .lower ()
385- if key in lower_map :
386- return lower_map [key ]
387- return int (spec )
372+ spec : str ,
373+ fieldnames : list [str ],
374+ ) -> str :
375+ """Resolve a case-insensitive column name to the exact CSV header."""
376+ lower_map = {name .lower (): name for name in fieldnames if name is not None }
377+ key = str (spec ).lower ()
378+ if key in lower_map :
379+ return lower_map [key ]
380+ raise KeyError (f"Column { spec !r} not found in CSV header { fieldnames } " )
0 commit comments