Coverage for dynasor/trajectory/atomic_indices.py: 100%
29 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-10 08:27 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-10 08:27 +0000
1import os
2import re
3import numpy as np
4from numpy.typing import NDArray
5from dynasor.logging_tools import logger
8def parse_gromacs_index_file(fname: str) -> dict[str, NDArray[int]]:
9 """
10 Parses a gromacs style index (ndx) file.
11 Returns a dict with key values as
12 `group-name: [1, 3, 8]`.
14 Comments (everything following a ``;``) and blank lines are ignored.
16 Note
17 ----
18 The atomic indices in gromacs-ndx file starts with 1, but the returned dict starts with 0.
19 """
21 if not os.path.isfile(fname):
22 raise ValueError('Index file not found')
24 atomic_indices = dict()
25 name = None
26 header_re = re.compile(r'^ *\[ *([a-zA-Z0-9_.-]+) *\] *$')
27 with open(fname, 'r') as fobj:
28 for line in fobj.readlines():
29 line = line.split(';')[0] # strip comments
30 match = header_re.match(line)
31 if match is not None: # get name of group
32 name = match.group(1)
33 if name in atomic_indices.keys():
34 logger.warning(f'Group name {name} appears twice in index file, '
35 'the indices of the last occurrence are used.')
36 atomic_indices[name] = []
37 else: # get indices for group
38 indices = [int(i) for i in line.split()]
39 if not indices: # blank or comment-only line
40 continue
41 if name is None:
42 raise ValueError('Index file contains indices before any group header.')
43 atomic_indices[name].extend(indices)
45 # cast to indices to numpy arrays and shift so indices start with 0
46 for name, indices in atomic_indices.items():
47 atomic_indices[name] = np.array(indices) - 1
49 return atomic_indices