Source code for ase.io.turbomole

from ase.units import Bohr


[docs]def read_turbomole(fd): """Method to read turbomole coord file coords in bohr, atom types in lowercase, format: $coord x y z atomtype x y z atomtype f $end Above 'f' means a fixed atom. """ from ase import Atoms from ase.constraints import FixAtoms lines = fd.readlines() atoms_pos = [] atom_symbols = [] myconstraints = [] # find $coord section; # does not necessarily have to be the first $<something> in file... for i, l in enumerate(lines): if l.strip().startswith('$coord'): start = i break for line in lines[start + 1:]: if line.startswith('$'): # start of new section break else: x, y, z, symbolraw = line.split()[:4] symbolshort = symbolraw.strip() symbol = symbolshort[0].upper() + symbolshort[1:].lower() # print(symbol) atom_symbols.append(symbol) atoms_pos.append( [float(x) * Bohr, float(y) * Bohr, float(z) * Bohr] ) cols = line.split() if (len(cols) == 5): fixedstr = line.split()[4].strip() if (fixedstr == "f"): myconstraints.append(True) else: myconstraints.append(False) else: myconstraints.append(False) # convert Turbomole ghost atom Q to X atom_symbols = [element if element != 'Q' else 'X' for element in atom_symbols] atoms = Atoms(positions=atoms_pos, symbols=atom_symbols, pbc=False) c = FixAtoms(mask=myconstraints) atoms.set_constraint(c) return atoms
class TurbomoleFormatError(ValueError): default_message = ('Data format in file does not correspond to known ' 'Turbomole gradient format') def __init__(self, *args, **kwargs): if args or kwargs: ValueError.__init__(self, *args, **kwargs) else: ValueError.__init__(self, self.default_message)
[docs]def read_turbomole_gradient(fd, index=-1): """ Method to read turbomole gradient file """ # read entire file lines = [x.strip() for x in fd.readlines()] # find $grad section start = end = -1 for i, line in enumerate(lines): if not line.startswith('$'): continue if line.split()[0] == '$grad': start = i elif start >= 0: end = i break if end <= start: raise RuntimeError('File does not contain a valid \'$grad\' section') # trim lines to $grad del lines[:start + 1] del lines[end - 1 - start:] # Interpret $grad section from ase import Atoms, Atom from ase.calculators.singlepoint import SinglePointCalculator from ase.units import Bohr, Hartree images = [] while lines: # loop over optimization cycles # header line # cycle = 1 SCF energy = -267.6666811409 |dE/dxyz| = 0.157112 # noqa: E501 fields = lines[0].split('=') try: # cycle = int(fields[1].split()[0]) energy = float(fields[2].split()[0]) * Hartree # gradient = float(fields[3].split()[0]) except (IndexError, ValueError) as e: raise TurbomoleFormatError() from e # coordinates/gradient atoms = Atoms() forces = [] for line in lines[1:]: fields = line.split() if len(fields) == 4: # coordinates # 0.00000000000000 0.00000000000000 0.00000000000000 c # noqa: E501 try: symbol = fields[3].lower().capitalize() # if dummy atom specified, substitute 'Q' with 'X' if symbol == 'Q': symbol = 'X' position = tuple([Bohr * float(x) for x in fields[0:3]]) except ValueError as e: raise TurbomoleFormatError() from e atoms.append(Atom(symbol, position)) elif len(fields) == 3: # gradients # -.51654903354681D-07 -.51654903206651D-07 0.51654903169644D-07 # noqa: E501 grad = [] for val in fields[:3]: try: grad.append( -float(val.replace('D', 'E')) * Hartree / Bohr ) except ValueError as e: raise TurbomoleFormatError() from e forces.append(grad) else: # next cycle break # calculator calc = SinglePointCalculator(atoms, energy=energy, forces=forces) atoms.calc = calc # save frame images.append(atoms) # delete this frame from data to be handled del lines[:2 * len(atoms) + 1] return images[index]
[docs]def write_turbomole(fd, atoms): """ Method to write turbomole coord file """ from ase.constraints import FixAtoms coord = atoms.get_positions() symbols = atoms.get_chemical_symbols() # convert X to Q for Turbomole ghost atoms symbols = [element if element != 'X' else 'Q' for element in symbols] fix_indices = set() if atoms.constraints: for constr in atoms.constraints: if isinstance(constr, FixAtoms): fix_indices.update(constr.get_indices()) fix_str = [] for i in range(len(atoms)): if i in fix_indices: fix_str.append('f') else: fix_str.append('') fd.write('$coord\n') for (x, y, z), s, fix in zip(coord, symbols, fix_str): fd.write('%20.14f %20.14f %20.14f %2s %2s \n' % (x / Bohr, y / Bohr, z / Bohr, s.lower(), fix)) fd.write('$end\n')