def print_imag_vector(self, mode_number=6, name=None): """Print a .xyz file with multiple structures visualising the largest magnitude imaginary mode Keyword Arguments: mode_number (int): Number of the normal mode to visualise, 6 (default) is the lowest frequency vibration i.e. largest magnitude imaginary, if present name (str): """ assert self.optts_calc is not None name = self.name if name is None else name disp = -0.5 for i in range(40): atoms = get_displaced_atoms_along_mode( calc=self.optts_calc, mode_number=int(mode_number), disp_magnitude=disp, atoms=self.atoms) atoms_to_xyz_file(atoms=atoms, filename=f'{name}.xyz', append=True) # Add displacement so the final set of atoms are +0.5 Å displaced # along the mode, then displaced back again sign = 1 if i < 20 else -1 disp += sign * 1.0 / 20.0 return None
def print_geometries(self, name): """Print an xyz trajectory of the geometries in the path""" # Empty the file open(f'{name}.xyz', 'w').close() for i, image in enumerate(self): assert image.species is not None energy = image.energy if image.energy is not None else 'none' atoms_to_xyz_file(image.species.atoms, f'{name}.xyz', title_line=f'autodE path point {i}. E = {energy}', append=True) return None
def print_xyz_file(self, title_line='', filename=None): """Print a standard xyz file from the Molecule's atoms""" if filename is None: filename = f'{self.name}.xyz' return atoms_to_xyz_file(self.atoms, filename, title_line=title_line)
def test_making_xyz_file(): atoms = [Atom('H'), Atom('H')] atoms_to_xyz_file(atoms, filename='test.xyz') atoms_to_xyz_file(atoms, filename='test.xyz', append=False) xyz_lines = open('test.xyz', 'r').readlines() assert len(xyz_lines) == 4 # With append should add the next set of atoms to the same file atoms_to_xyz_file(atoms, filename='test.xyz', append=True) xyz_lines = open('test.xyz', 'r').readlines() assert len(xyz_lines) == 8 with pytest.raises(AssertionError): # Requires some atoms atoms_to_xyz_file(atoms=None, filename='test.xyz') # Needs .xyz extension atoms_to_xyz_file(atoms, filename='test') os.remove('test.xyz')
def get_simanl_atoms(species, dist_consts=None, conf_n=0, save_xyz=True): """ Use a bonded + repulsive force field to generate 3D structure for a species. If the initial coordinates are reasonable e.g. from a previously generated 3D structure then add random displacement vectors and minimise to generate a conformer. Otherwise add atoms to the box sequentially until all atoms have been added, which generates a qualitatively reasonable 3D geometry which should be optimised using a electronic structure method V(x) = Σ_bonds k(d - d0)^2 + Σ_ij c/d^n Arguments: species (autode.species.Species): Keyword Arguments: dist_consts (dict): Key = tuple of atom indexes, Value = distance conf_n (int): Number of this conformer save_xyz (bool): Whether or not to save a .xyz file of the structure for fast reloading Returns: (list(autode.atoms.Atom)): Atoms """ xyz_filename = f'{species.name}_conf{conf_n}_siman.xyz' saved_atoms = get_atoms_from_generated_file(species, xyz_filename) if saved_atoms is not None: return saved_atoms # To generate the potential requires bonds between atoms defined in a # molecular graph if species.graph is None: raise ex.NoMolecularGraph # Initialise a new random seed and make a copy of the species' atoms. # RandomState is thread safe rand = np.random.RandomState() atoms = get_atoms_rotated_stereocentres(species=species, atoms=deepcopy(species.atoms), rand=rand) # Add the distance constraints as fixed bonds d0 = get_ideal_bond_length_matrix(atoms=species.atoms, bonds=species.graph.edges()) # Add distance constraints across stereocentres e.g. for a Z double bond # then modify d0 appropriately dist_consts = add_dist_consts_for_stereocentres( species=species, dist_consts={} if dist_consts is None else dist_consts) constrained_bonds = [] for bond, length in dist_consts.items(): i, j = bond d0[i, j] = length d0[j, i] = length constrained_bonds.append(bond) # Randomise coordinates that aren't fixed by shifting a maximum of # autode.Config.max_atom_displacement in x, y, z fixed_atom_indexes = get_non_random_atoms(species=species) # Shift by a factor defined in the config file if the coordinates are # reasonable but otherwise init in a 10 A cube reasonable_init_coords = ade.geom.are_coords_reasonable( species.coordinates) if reasonable_init_coords: factor = ade.Config.max_atom_displacement / np.sqrt(3) for i, atom in enumerate(atoms): if i not in fixed_atom_indexes: atom.translate(vec=factor * rand.uniform(-1, 1, 3)) else: # Randomise in a 10 Å cubic box [atom.translate(vec=rand.uniform(-5, 5, 3)) for atom in atoms] logger.info('Minimising species...') st = time() if reasonable_init_coords: coords = get_coords_minimised_v(coords=np.array( [atom.coord for atom in atoms]), bonds=species.graph.edges, k=1.0, c=0.01, d0=d0, tol=1E-5, fixed_bonds=constrained_bonds) else: coords = get_coords_no_init_strucutre(atoms, species, d0, constrained_bonds) logger.info(f' ... ({time()-st:.3f} s)') # Set the coordinates of the new atoms for i, atom in enumerate(atoms): atom.coord = coords[i] # Print an xyz file so rerunning will read the file if save_xyz: atoms_to_xyz_file(atoms=atoms, filename=xyz_filename) return atoms