Coverage for /builds/ase/ase/ase/io/bader.py: 83.33%
42 statements
« prev ^ index » next coverage.py v7.5.3, created at 2025-08-02 00:12 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2025-08-02 00:12 +0000
1# fmt: off
3import numpy as np
5from ase.data import atomic_numbers
6from ase.units import Bohr
9def attach_charges(atoms, fileobj='ACF.dat', displacement=1e-4):
10 """Attach the charges from the fileobj to the Atoms."""
11 if isinstance(fileobj, str):
12 with open(fileobj) as fd:
13 lines = fd.readlines()
14 else:
15 lines = fileobj
17 sep = '---------------'
18 i = 0 # Counter for the lines
19 k = 0 # Counter of sep
20 assume6columns = False
21 for line in lines:
22 if line[0] == '\n': # check if there is an empty line in the
23 i -= 1 # head of ACF.dat file
24 if i == 0:
25 headings = line
26 if 'BADER' in headings.split():
27 j = headings.split().index('BADER')
28 elif 'CHARGE' in headings.split():
29 j = headings.split().index('CHARGE')
30 else:
31 print('Can\'t find keyword "BADER" or "CHARGE".'
32 ' Assuming the ACF.dat file has 6 columns.')
33 j = 4
34 assume6columns = True
35 if sep in line: # Stop at last separator line
36 if k == 1:
37 break
38 k += 1
39 if i <= 1:
40 pass
41 else:
42 words = line.split()
43 if assume6columns is True:
44 if len(words) != 6:
45 raise OSError('Number of columns in ACF file incorrect!\n'
46 'Check that Bader program version >= 0.25')
48 atom = atoms[int(words[0]) - 1]
49 atom.charge = atomic_numbers[atom.symbol] - float(words[j])
50 if displacement is not None: # check if the atom positions match
51 xyz = np.array([float(w) for w in words[1:4]])
52 # ACF.dat units could be Bohr or Angstrom
53 norm1 = np.linalg.norm(atom.position - xyz)
54 norm2 = np.linalg.norm(atom.position - xyz * Bohr)
55 assert norm1 < displacement or norm2 < displacement
56 i += 1