Coverage for /builds/kinetik161/ase/ase/io/turbomole.py: 49.54%
109 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-12-10 11:04 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-12-10 11:04 +0000
1from ase.units import Bohr
4def read_turbomole(fd):
5 """Method to read turbomole coord file
7 coords in bohr, atom types in lowercase, format:
8 $coord
9 x y z atomtype
10 x y z atomtype f
11 $end
12 Above 'f' means a fixed atom.
13 """
14 from ase import Atoms
15 from ase.constraints import FixAtoms
17 lines = fd.readlines()
18 atoms_pos = []
19 atom_symbols = []
20 myconstraints = []
22 # find $coord section;
23 # does not necessarily have to be the first $<something> in file...
24 for i, l in enumerate(lines):
25 if l.strip().startswith('$coord'):
26 start = i
27 break
28 for line in lines[start + 1:]:
29 if line.startswith('$'): # start of new section
30 break
31 else:
32 x, y, z, symbolraw = line.split()[:4]
33 symbolshort = symbolraw.strip()
34 symbol = symbolshort[0].upper() + symbolshort[1:].lower()
35 # print(symbol)
36 atom_symbols.append(symbol)
37 atoms_pos.append(
38 [float(x) * Bohr, float(y) * Bohr, float(z) * Bohr]
39 )
40 cols = line.split()
41 if (len(cols) == 5):
42 fixedstr = line.split()[4].strip()
43 if (fixedstr == "f"):
44 myconstraints.append(True)
45 else:
46 myconstraints.append(False)
47 else:
48 myconstraints.append(False)
50 # convert Turbomole ghost atom Q to X
51 atom_symbols = [element if element !=
52 'Q' else 'X' for element in atom_symbols]
53 atoms = Atoms(positions=atoms_pos, symbols=atom_symbols, pbc=False)
54 c = FixAtoms(mask=myconstraints)
55 atoms.set_constraint(c)
56 return atoms
59class TurbomoleFormatError(ValueError):
60 default_message = ('Data format in file does not correspond to known '
61 'Turbomole gradient format')
63 def __init__(self, *args, **kwargs):
64 if args or kwargs:
65 ValueError.__init__(self, *args, **kwargs)
66 else:
67 ValueError.__init__(self, self.default_message)
70def read_turbomole_gradient(fd, index=-1):
71 """ Method to read turbomole gradient file """
73 # read entire file
74 lines = [x.strip() for x in fd.readlines()]
76 # find $grad section
77 start = end = -1
78 for i, line in enumerate(lines):
79 if not line.startswith('$'):
80 continue
81 if line.split()[0] == '$grad':
82 start = i
83 elif start >= 0:
84 end = i
85 break
87 if end <= start:
88 raise RuntimeError('File does not contain a valid \'$grad\' section')
90 # trim lines to $grad
91 del lines[:start + 1]
92 del lines[end - 1 - start:]
94 # Interpret $grad section
95 from ase import Atom, Atoms
96 from ase.calculators.singlepoint import SinglePointCalculator
97 from ase.units import Bohr, Hartree
98 images = []
99 while lines: # loop over optimization cycles
100 # header line
101 # cycle = 1 SCF energy = -267.6666811409 |dE/dxyz| = 0.157112 # noqa: E501
102 fields = lines[0].split('=')
103 try:
104 # cycle = int(fields[1].split()[0])
105 energy = float(fields[2].split()[0]) * Hartree
106 # gradient = float(fields[3].split()[0])
107 except (IndexError, ValueError) as e:
108 raise TurbomoleFormatError() from e
110 # coordinates/gradient
111 atoms = Atoms()
112 forces = []
113 for line in lines[1:]:
114 fields = line.split()
115 if len(fields) == 4: # coordinates
116 # 0.00000000000000 0.00000000000000 0.00000000000000 c # noqa: E501
117 try:
118 symbol = fields[3].lower().capitalize()
119 # if dummy atom specified, substitute 'Q' with 'X'
120 if symbol == 'Q':
121 symbol = 'X'
122 position = tuple([Bohr * float(x) for x in fields[0:3]])
123 except ValueError as e:
124 raise TurbomoleFormatError() from e
125 atoms.append(Atom(symbol, position))
126 elif len(fields) == 3: # gradients
127 # -.51654903354681D-07 -.51654903206651D-07 0.51654903169644D-07 # noqa: E501
128 grad = []
129 for val in fields[:3]:
130 try:
131 grad.append(
132 -float(val.replace('D', 'E')) * Hartree / Bohr
133 )
134 except ValueError as e:
135 raise TurbomoleFormatError() from e
136 forces.append(grad)
137 else: # next cycle
138 break
140 # calculator
141 calc = SinglePointCalculator(atoms, energy=energy, forces=forces)
142 atoms.calc = calc
144 # save frame
145 images.append(atoms)
147 # delete this frame from data to be handled
148 del lines[:2 * len(atoms) + 1]
150 return images[index]
153def write_turbomole(fd, atoms):
154 """ Method to write turbomole coord file
155 """
156 from ase.constraints import FixAtoms
158 coord = atoms.get_positions()
159 symbols = atoms.get_chemical_symbols()
161 # convert X to Q for Turbomole ghost atoms
162 symbols = [element if element != 'X' else 'Q' for element in symbols]
164 fix_indices = set()
165 if atoms.constraints:
166 for constr in atoms.constraints:
167 if isinstance(constr, FixAtoms):
168 fix_indices.update(constr.get_indices())
170 fix_str = []
171 for i in range(len(atoms)):
172 if i in fix_indices:
173 fix_str.append('f')
174 else:
175 fix_str.append('')
177 fd.write('$coord\n')
178 for (x, y, z), s, fix in zip(coord, symbols, fix_str):
179 fd.write('%20.14f %20.14f %20.14f %2s %2s \n'
180 % (x / Bohr, y / Bohr, z / Bohr, s.lower(), fix))
182 fd.write('$end\n')