def gcn(ts_path, ): """ Generates a TS guess using the updated graph convolutional network originally published by Pattanaik et al. https://chemrxiv.org/articles/Genereting_Transition_States_of_Isomerization_Reactions_with_Deep_Learning/12302084 This function writes the TS guess as an xyz file to the corresponding TS directory. Args: ts_path (str): Path to the inputs for the network. Returns: ts_xyz_dict (dict): ARC xyz dictionary of TS guess """ # run the GCN as a subprocess p = subprocess.run( f'{TS_GCN_PYTHON} {os.path.join(TS_GCN_PATH, "inference.py")} ' f'--r_sdf_path {os.path.join(ts_path, "reactant.sdf")} ' f'--p_sdf_path {os.path.join(ts_path, "product.sdf")} ' f'--ts_xyz_path {os.path.join(ts_path, "TS.xyz")} ', shell=True) # if subprocess ran successfully, read the TS structure into ARC's dictionary format if p.returncode == 0: ts_xyz_dict = str_to_xyz(os.path.join(ts_path, "TS.xyz")) # otherwise, log the error else: logger.error(f'GCN subprocess did not give a successful return code:\n' f'Got return code: {p.returncode}\n' f'stdout: {p.stdout}\n' f'stderr: {p.stderr}') ts_xyz_dict = None return ts_xyz_dict
def get_mapped_product_xyz(self): """ Uses the mapping from product onto reactant to create a new ARC Species for the mapped product. For now, only functional for A <=> B reactions. Returns: Tuple[dict, ARCSpecies]: dict: Mapped product atoms in ARC dictionary format. ARCSpecies: ARCSpecies object created from mapped coordinates. """ product = self.p_species[0] mapping = self.atom_map original_xyz_str = xyz_to_str(product.get_xyz()) original_xyz = np.array(original_xyz_str.split('\n')) mapped_xyz = '\n'.join(original_xyz[mapping].tolist()) # create another product with the mapped atoms since ARCSpecies objects preserve this ordering # in RDKit objects and in RMG-Py Molecule objects mapped_product = ARCSpecies( label=product.label, smiles=product.mol.smiles, multiplicity=product.multiplicity, charge=product.charge, xyz=mapped_xyz, ) mapped_xyz = str_to_xyz(mapped_xyz) return mapped_xyz, mapped_product
def test_get_lp_vector(self): """Test the lone pair vector""" xyz1 = converter.str_to_xyz( """O 1.13971727 -0.35763357 -0.91809799 N -0.16022228 -0.63832421 -0.32863338 C -0.42909096 0.49864538 0.54457751 H -1.36471297 0.33135829 1.08632108 H 0.37059419 0.63632068 1.27966893 H -0.53867601 1.41749835 -0.03987146 H 0.03832076 -1.45968957 0.24914206 H 0.94407000 -0.42817536 -1.87310674""") spc1 = ARCSpecies(label='tst1', smiles='CN(O)', xyz=xyz1) vector = vectors.get_lp_vector(label='tst1', mol=spc1.mol, xyz=xyz1, pivot=1) self.assertAlmostEqual(vector[0], -0.7582151013592212, 5) self.assertAlmostEqual(vector[1], -0.14276808320949216, 5) self.assertAlmostEqual(vector[2], -0.6361816835523585, 5) self.assertAlmostEqual((sum([vi**2 for vi in vector]))**0.5, 1) # puts the following dummy atom in xyz1: 'Cl -0.91844 -0.78109 -0.96482' xyz2 = converter.str_to_xyz( """N -0.70735114 0.81971647 0.24999886 C 0.58016992 0.65919122 -0.42405305 C 1.44721132 -0.43727777 0.17945348 C -1.63900905 -0.25796649 -0.04936095 H 1.11974047 1.60931343 -0.33768790 H 0.43764604 0.48458543 -1.49689220 H 1.00255021 -1.42757899 0.04242741 H 2.42947502 -0.44523307 -0.30432399 H 1.60341053 -0.27376799 1.25093890 H -1.81252045 -0.34624671 -1.12667881 H -2.60396918 -0.04100469 0.41960198 H -1.29274859 -1.22036999 0.33877281 H -0.56460509 0.87663914 1.25780346""") spc2 = ARCSpecies(label='tst2', smiles='CNCC', xyz=xyz2) vector = vectors.get_lp_vector(label='tst2', mol=spc2.mol, xyz=xyz2, pivot=0) self.assertAlmostEqual(vector[0], -0.40585301456248446, 5) self.assertAlmostEqual(vector[1], 0.8470158636326891, 5) self.assertAlmostEqual(vector[2], -0.34328917449449764, 5) self.assertAlmostEqual((sum([vi**2 for vi in vector]))**0.5, 1)
def test_save_geo(self): """Test saving the geometry files for a species""" spc = ARCSpecies(label='methylamine', smiles='CN', multiplicity=1, charge=0) spc.final_xyz = str_to_xyz( """N -0.74566988 -0.11773792 0.00000000 C 0.70395487 0.03951260 0.00000000 H 1.12173564 -0.45689176 -0.87930074 H 1.06080468 1.07995075 0.00000000 H 1.12173564 -0.45689176 0.87930074 H -1.16115119 0.31478894 0.81506145 H -1.16115119 0.31478894 -0.81506145""") spc.opt_level = 'opt/level' project = 'arc_project_for_testing_delete_after_usage' project_directory = os.path.join(arc_path, 'Projects', project) xyz_path = os.path.join(project_directory, 'output', 'Species', spc.label, 'geometry', 'methylamine.xyz') gjf_path = os.path.join(project_directory, 'output', 'Species', spc.label, 'geometry', 'methylamine.gjf') plotter.save_geo(species=spc, project_directory=project_directory) xyz_data = """7 methylamine optimized at opt/level N -0.74566988 -0.11773792 0.00000000 C 0.70395487 0.03951260 0.00000000 H 1.12173564 -0.45689176 -0.87930074 H 1.06080468 1.07995075 0.00000000 H 1.12173564 -0.45689176 0.87930074 H -1.16115119 0.31478894 0.81506145 H -1.16115119 0.31478894 -0.81506145 """ gjf_data = """# hf/3-21g methylamine optimized at opt/level 0 1 N -0.74566988 -0.11773792 0.00000000 C 0.70395487 0.03951260 0.00000000 H 1.12173564 -0.45689176 -0.87930074 H 1.06080468 1.07995075 0.00000000 H 1.12173564 -0.45689176 0.87930074 H -1.16115119 0.31478894 0.81506145 H -1.16115119 0.31478894 -0.81506145 """ with open(xyz_path, 'r') as f: data = f.read() self.assertEqual(data, xyz_data) with open(gjf_path, 'r') as f: data = f.read() self.assertEqual(data, gjf_data)
def test_get_vector(self): """Test getting a vector between two atoms in the molecule""" xyz1 = converter.str_to_xyz("""O 0.0 0.0 0.0 N 1.0 0.0 0.0""") # trivial vector = vectors.get_vector(pivot=0, anchor=1, xyz=xyz1) self.assertAlmostEqual(vector[0], 1.0, 5) self.assertAlmostEqual(vector[1], 0.0, 5) self.assertAlmostEqual(vector[2], 0.0, 5) xyz2 = converter.str_to_xyz("""O -0.39141517 -1.49218505 0.23537907 N -1.29594218 0.36660772 -0.33360920 C -0.24369399 -0.21522785 0.47237314 C 1.11876670 0.24246665 -0.06138419 H -0.34055624 0.19728442 1.48423848 H 1.27917500 -0.02124533 -1.11576163 H 1.93896021 -0.20110894 0.51754953 H 1.21599040 1.33219465 0.01900272 H -2.12405283 -0.11420423 0.01492411 H -1.15723190 -0.09458204 -1.23271202""") # smiles='NC([O])(C)' vector = vectors.get_vector(pivot=1, anchor=2, xyz=xyz2) self.assertAlmostEqual(vector[0], 1.052248, 5) self.assertAlmostEqual(vector[1], -0.581836, 5) self.assertAlmostEqual(vector[2], 0.805982, 5)
def test_calculate_angle(self): """Test calculating an angle from xyz coordinates""" co2 = converter.str_to_xyz("""O -1.40465894 -0.03095532 0.00000000 C -0.00000000 0.00000004 0.00000000 O 1.40465895 0.03095528 0.00000000""") angle = vectors.calculate_angle(coords=co2['coords'], atoms=[0, 1, 2], index=0, units='degs') self.assertEqual(angle, 180.0) angle = vectors.calculate_angle(coords=co2['coords'], atoms=[1, 2, 3], index=1, units='degs') self.assertEqual(angle, 180.0) angle = vectors.calculate_angle(coords=co2['coords'], atoms=[0, 1, 2], index=0, units='rads') self.assertEqual(angle, math.pi) with self.assertRaises(VectorsError): angle = vectors.calculate_angle(coords=co2['coords'], atoms=[1, 2, 1], index=1, units='degs') fake_co2 = converter.str_to_xyz("""O -1.40465894 -0.03095532 0.00000000 C -0.00000000 0.00000004 0.00000000 O -1.40465894 -0.03095532 0.00000000""") angle = vectors.calculate_angle(coords=fake_co2['coords'], atoms=[0, 1, 2], index=0, units='degs') self.assertEqual(angle, 0.0) propene = converter.str_to_xyz("""C 1.22905000 -0.16449200 0.00000000 C -0.13529200 0.45314000 0.00000000 C -1.27957200 -0.21983000 0.00000000 H 1.17363000 -1.25551200 0.00000000 H 1.79909600 0.15138400 0.87934300 H 1.79909600 0.15138400 -0.87934300 H -0.16831500 1.54137600 0.00000000 H -2.23664600 0.28960500 0.00000000 H -1.29848800 -1.30626200 0.00000000""") angle = vectors.calculate_angle(coords=propene['coords'], atoms=[8, 3, 9], index=1, units='degs') self.assertAlmostEqual(angle, 117.02817, 4) angle = vectors.calculate_angle(coords=propene['coords'], atoms=[9, 3, 8], index=1, units='degs') self.assertAlmostEqual(angle, 117.02817, 4) angle = vectors.calculate_angle(coords=propene['coords'], atoms=[1, 2, 3], index=1, units='degs') self.assertAlmostEqual(angle, 125.18344, 4) angle = vectors.calculate_angle(coords=propene['coords'], atoms=[5, 1, 2], index=1, units='degs') self.assertAlmostEqual(angle, 110.82078, 4)
def test_calculate_distance(self): """Test calculating a distance between two atoms""" propene = converter.str_to_xyz("""C 1.22905000 -0.16449200 0.00000000 C -0.13529200 0.45314000 0.00000000 C -1.27957200 -0.21983000 0.00000000 H 1.17363000 -1.25551200 0.00000000 H 1.79909600 0.15138400 0.87934300 H 1.79909600 0.15138400 -0.87934300 H -0.16831500 1.54137600 0.00000000 H -2.23664600 0.28960500 0.00000000 H -1.29848800 -1.30626200 0.00000000""") distance = vectors.calculate_distance(coords=propene['coords'], atoms=[1, 4], index=1) self.assertAlmostEqual(distance, 1.092426698) distance = vectors.calculate_distance(coords=propene['coords'], atoms=[1, 2], index=1) self.assertAlmostEqual(distance, 1.49763087) distance = vectors.calculate_distance(coords=propene['coords'], atoms=[2, 3], index=1) self.assertAlmostEqual(distance, 1.32750337)
def process_conformers_file( conformers_path: str) -> Tuple[List[Dict[str, tuple]], List[float]]: """ Parse coordinates and energies from an ARC conformers file of either species or TSs. Args: conformers_path (str): The path to an ARC conformers file (either a "conformers_before_optimization" or a "conformers_after_optimization" file). Raises: InputError: If the file could not be found. Returns: Tuple[List[Dict[str, tuple]], List[float]] Conformer coordinates in a dict format, the respective energies in kJ/mol. """ if not os.path.isfile(conformers_path): raise InputError( 'Conformers file {0} could not be found'.format(conformers_path)) with open(conformers_path, 'r') as f: lines = f.readlines() xyzs, energies = list(), list() line_index = 0 while line_index < len(lines): if 'conformer' in lines[line_index] and ':' in lines[ line_index] and lines[line_index].strip()[-2].isdigit(): xyz, energy = '', None line_index += 1 while len(lines) and line_index < len(lines) and lines[line_index].strip() \ and 'SMILES' not in lines[line_index] \ and 'energy' not in lines[line_index].lower() \ and 'guess method' not in lines[line_index].lower(): xyz += lines[line_index] line_index += 1 while len(lines) and line_index < len( lines) and 'conformer' not in lines[line_index]: if 'relative energy:' in lines[line_index].lower(): energy = float(lines[line_index].split()[2]) line_index += 1 xyzs.append(str_to_xyz(xyz)) energies.append(energy) else: line_index += 1 return xyzs, energies
def autotst(reaction_label=None, rmg_reaction=None, reaction_family=None): """ Run AutoTST to generate a TS guess. Currently only works for H Abstraction reactions. Either `reaction_label` or `rmg_reaction` has to be given (ARC sends rmg_reaction). If reaction_family isn't specified, it is assumed to be H_Abstraction. Args: reaction_label (str): AutoTST's string format reaction, e.g., CCCC+[O]O_[CH2]CCC+OO rmg_reaction (Reaction): An RMG Reaction object. reaction_family (str): The RMG family corresponding to the RMG Reaction object Returns: xyz (dict): The TS guess. Raises: TSError: if neither ``rmg_reaction`` nor ``reaction_family`` were specified. """ xyz_str = '' xyz_path = os.path.join(ARC_PATH, 'arc', 'ts', 'auto_tst.xyz') run_autotst_path = os.path.join(ARC_PATH, 'arc', 'ts', 'run_autotst.py') reaction_family = str('H_Abstraction') if reaction_family is None else str( reaction_family) if os.path.isfile(xyz_path): os.remove(xyz_path) if rmg_reaction is not None and reaction_label is None: reaction_label = get_reaction_label(rmg_reaction) elif reaction_label is None: raise TSError('Must get either reaction_label or rmg_reaction') os.system( 'python {run_autotst_path} {reaction_label} {reaction_family}'.format( run_autotst_path=run_autotst_path, reaction_label=reaction_label, reaction_family=reaction_family)) if os.path.isfile(xyz_path): with open(xyz_path, 'r') as f: lines = f.readlines() xyz_str = ''.join([str(line) for line in lines]) os.remove(xyz_path) if not xyz_str or xyz_str == '\n': return None return str_to_xyz(xyz_str)
def parse_1d_scan_coords(path: str) -> List[Dict[str, tuple]]: """ Parse the 1D torsion scan coordinates from an ESS log file. Args: path (str): The ESS log file to parse from. Returns: list The Cartesian coordinates. """ lines = _get_lines_from_file(path) log = ess_factory(fullpath=path, check_for_errors=False) if not isinstance(log, GaussianLog): raise NotImplementedError( f'Currently parse_1d_scan_coords only supports Gaussian files, got {type(log)}' ) traj = list() done = False i = 0 while not done: if i >= len(lines) or 'Normal termination of Gaussian' in lines[ i] or 'Error termination via' in lines[i]: done = True elif 'Optimization completed' in lines[i]: while len(lines) and 'Input orientation:' not in lines[i]: i += 1 if 'Error termination via' in lines[i]: return traj i += 5 xyz_str = '' while len( lines ) and '--------------------------------------------' not in lines[ i]: splits = lines[i].split() xyz_str += f'{qcel.periodictable.to_E(int(splits[1]))} {splits[3]} {splits[4]} {splits[5]}\n' i += 1 traj.append(str_to_xyz(xyz_str)) i += 1 return traj
def parse_geometry(path: str) -> Optional[Dict[str, tuple]]: """ Parse the xyz geometry from an ESS log file. Args: path (str): The ESS log file to parse from. Returns: Optional[Dict[str, tuple]] The cartesian geometry. """ log = ess_factory(fullpath=path) try: coords, number, _ = log.load_geometry() except LogError: logger.debug(f'Could not parse xyz from {path}') # try parsing Gaussian standard orientation instead of the input orientation parsed by Arkane lines = _get_lines_from_file(path) xyz_str = '' for i in range(len(lines)): if 'Standard orientation:' in lines[i]: xyz_str = '' j = i while len(lines) and not lines[j].split()[0].isdigit(): j += 1 while len(lines) and '-------------------' not in lines[j]: splits = lines[j].split() xyz_str += f'{qcel.periodictable.to_E(int(splits[1]))} {splits[3]} {splits[4]} {splits[5]}\n' j += 1 break if xyz_str: return str_to_xyz(xyz_str) return None return xyz_from_data(coords=coords, numbers=number)
def parse_trajectory(path: str) -> List[Dict[str, tuple]]: """ Parse all geometries from an xyz trajectory file or an ESS output file. Args: path (str): The file path. Raises: ParserError: If the trajectory could not be read. Returns: List[Dict[str, tuple]] Entries are xyz's on the trajectory. """ lines = _get_lines_from_file(path) ess_file = False if path.split('.')[-1] != 'xyz': try: log = ess_factory(fullpath=path) ess_file = True except InputError: ess_file = False if ess_file: if not isinstance(log, GaussianLog): raise NotImplementedError( f'Currently parse_trajectory only supports Gaussian files, got {type(log)}' ) traj = list() done = False i = 0 while not done: if i >= len(lines) or 'Normal termination of Gaussian' in lines[ i] or 'Error termination via' in lines[i]: done = True elif 'Input orientation:' in lines[i]: i += 5 xyz_str = '' while len( lines ) and '--------------------------------------------' not in lines[ i]: splits = lines[i].split() xyz_str += f'{qcel.periodictable.to_E(int(splits[1]))} {splits[3]} {splits[4]} {splits[5]}\n' i += 1 traj.append(str_to_xyz(xyz_str)) i += 1 else: # this is not an ESS output file, probably an XYZ format file with several Cartesian coordinates skip_line = False num_of_atoms = 0 traj, xyz_lines = list(), list() for line in lines: splits = line.strip().split() if len(splits) == 1 and all([c.isdigit() for c in splits[0]]): if len(xyz_lines): if len(xyz_lines) != num_of_atoms: raise ParserError( f'Could not parse trajectory, expected {num_of_atoms} atoms, ' f'but got {len(xyz_lines)} for point {len(traj) + 1} in the trajectory.' ) traj.append( str_to_xyz(''.join( [xyz_line for xyz_line in xyz_lines]))) num_of_atoms = int(splits[0]) skip_line = True xyz_lines = list() elif skip_line: # skip the comment line skip_line = False continue else: xyz_lines.append(line) if len(xyz_lines): # add the last point in the trajectory if len(xyz_lines) != num_of_atoms: raise ParserError( f'Could not parse trajectory, expected {num_of_atoms} atoms, ' f'but got {len(xyz_lines)} for point {len(traj) + 1} in the trajectory.' ) traj.append( str_to_xyz(''.join([xyz_line for xyz_line in xyz_lines]))) if not len(traj): raise ParserError(f'Could not parse trajectory from {path}') return traj
def parse_xyz_from_file(path: str) -> Optional[Dict[str, tuple]]: """ Parse xyz coordinated from: - .xyz: XYZ file - .gjf: Gaussian input file - .out or .log: ESS output file (Gaussian, Molpro, Orca, QChem, TeraChem) - calls parse_geometry() - other: Molpro or QChem input file Args: path (str): The file path. Raises: ParserError: If the coordinates could not be parsed. Returns: Optional[Dict[str, tuple]] The parsed cartesian coordinates. """ lines = _get_lines_from_file(path) file_extension = os.path.splitext(path)[1] xyz = None relevant_lines = list() if file_extension == '.xyz': for i, line in enumerate(reversed(lines)): splits = line.strip().split() if len(splits) == 1 and all([c.isdigit() for c in splits[0]]): # this is the last number of atoms line (important when parsing trajectories) num_of_atoms = int(splits[0]) break else: raise ParserError( f'Could not identify the number of atoms line in the xyz file {path}' ) index = len(lines) - i - 1 relevant_lines = lines[index + 2:index + 2 + num_of_atoms] elif file_extension == '.gjf': start_parsing = False for line in lines: if start_parsing and line and line != '\n' and line != '\r\n': relevant_lines.append(line) elif start_parsing: break else: splits = line.split() if len(splits) == 2 and all([s.isdigit() for s in splits]): start_parsing = True elif 'out' in file_extension or 'log' in file_extension: xyz = parse_geometry(path) else: record = False for line in lines: if '$end' in line or '}' in line: break if record and len(line.split()) == 4: relevant_lines.append(line) elif '$molecule' in line: record = True elif 'geometry={' in line: record = True if not relevant_lines: raise ParserError( f'Could not parse xyz coordinates from file {path}') if xyz is None and relevant_lines: xyz = str_to_xyz(''.join([line for line in relevant_lines if line])) return xyz
def test_calculate_dihedral_angle(self): """Test calculating a dihedral angle from xyz coordinates""" propene = converter.str_to_xyz("""C 1.22905000 -0.16449200 0.00000000 C -0.13529200 0.45314000 0.00000000 C -1.27957200 -0.21983000 0.00000000 H 1.17363000 -1.25551200 0.00000000 H 1.79909600 0.15138400 0.87934300 H 1.79909600 0.15138400 -0.87934300 H -0.16831500 1.54137600 0.00000000 H -2.23664600 0.28960500 0.00000000 H -1.29848800 -1.30626200 0.00000000""") hydrazine = converter.str_to_xyz("""N 0.70683700 -0.07371000 -0.21400700 N -0.70683700 0.07371000 -0.21400700 H 1.11984200 0.81113900 -0.47587600 H 1.07456200 -0.35127300 0.68988300 H -1.11984200 -0.81113900 -0.47587600 H -1.07456200 0.35127300 0.68988300""") cj_11974 = converter.str_to_xyz("""C 5.675 2.182 1.81 O 4.408 1.923 1.256 C 4.269 0.813 0.479 C 5.303 -0.068 0.178 C 5.056 -1.172 -0.639 C 3.794 -1.414 -1.169 C 2.77 -0.511 -0.851 C 2.977 0.59 -0.032 C 1.872 1.556 0.318 N 0.557 1.029 -0.009 C -0.537 1.879 0.448 C -0.535 3.231 -0.298 C -1.831 3.983 0.033 C -3.003 3.199 -0.61 N -2.577 1.854 -0.99 C -1.64 1.962 -2.111 C -0.501 2.962 -1.805 C -1.939 1.236 0.178 C -1.971 -0.305 0.069 C -3.385 -0.794 -0.209 C -4.336 -0.893 0.81 C -5.631 -1.324 0.539 C -5.997 -1.673 -0.759 C -5.056 -1.584 -1.781 C -3.764 -1.147 -1.505 C -1.375 -1.024 1.269 C -1.405 -0.508 2.569 C -0.871 -1.226 3.638 C -0.296 -2.475 3.429 C -0.259 -3.003 2.14 C -0.794 -2.285 1.078 C 3.533 -2.614 -2.056 C 2.521 -3.574 -1.424 C 3.087 -2.199 -3.461 H 5.569 3.097 2.395 H 6.433 2.338 1.031 H 6.003 1.368 2.47 H 6.302 0.091 0.57 H 5.874 -1.854 -0.864 H 1.772 -0.654 -1.257 H 1.963 1.832 1.384 H 2.033 2.489 -0.239 H 0.469 0.13 0.461 H -0.445 2.089 1.532 H 0.328 3.83 0.012 H -1.953 4.059 1.122 H -1.779 5.008 -0.352 H -3.365 3.702 -1.515 H -3.856 3.118 0.074 H -1.226 0.969 -2.31 H -2.211 2.259 -2.999 H -0.639 3.906 -2.348 H 0.466 2.546 -2.105 H -2.586 1.501 1.025 H -1.36 -0.582 -0.799 H -4.057 -0.647 1.831 H -6.355 -1.396 1.347 H -7.006 -2.015 -0.97 H -5.329 -1.854 -2.798 H -3.038 -1.07 -2.311 H -1.843 0.468 2.759 H -0.904 -0.802 4.638 H 0.125 -3.032 4.262 H 0.189 -3.977 1.961 H -0.772 -2.708 0.075 H 4.484 -3.155 -2.156 H 1.543 -3.093 -1.308 H 2.383 -4.464 -2.049 H 2.851 -3.899 -0.431 H 3.826 -1.542 -3.932 H 2.134 -1.659 -3.429 H 2.951 -3.078 -4.102""") dihedral0 = vectors.calculate_dihedral_angle(coords=propene['coords'], torsion=[9, 3, 2, 7], index=1) dihedral1 = vectors.calculate_dihedral_angle(coords=propene['coords'], torsion=[5, 1, 2, 7], index=1) self.assertAlmostEqual(dihedral0, 180, 2) self.assertAlmostEqual(dihedral1, 59.26447, 2) dihedral2 = vectors.calculate_dihedral_angle(coords=propene['coords'], torsion=[8, 2, 1, 6], index=0) self.assertEqual(dihedral0, dihedral2) dihedral3 = vectors.calculate_dihedral_angle(coords=hydrazine['coords'], torsion=[3, 1, 2, 5], index=1) self.assertAlmostEqual(dihedral3, 148.31829, 2) dihedral2 = vectors.calculate_dihedral_angle(coords=cj_11974['coords'], torsion=[15, 18, 19, 20], index=1) self.assertAlmostEqual(dihedral2, 308.04758, 2)
def test_colliding_atoms(self): """Check that we correctly determine when atoms collide in xyz""" xyz_no_0 = """C 0.0000000 0.0000000 0.6505570""" # monoatomic xyz_no_1 = """C -0.84339557 -0.03079260 -0.13110478 N 0.53015060 0.44534713 -0.25006000 O 1.33245258 -0.55134720 0.44204567 H -1.12632103 -0.17824612 0.91628291 H -1.52529493 0.70480833 -0.56787044 H -0.97406455 -0.97317212 -0.67214713 H 0.64789210 1.26863944 0.34677470 H 1.98414750 -0.79355889 -0.24492049""" # no colliding atoms xyz_no_2 = """C 0.0 0.0 0.0 H 0.0 0.0 1.09""" # no colliding atoms xyz_no_3 = """N -0.29070308 0.26322835 0.48770927 N 0.29070351 -0.26323281 -0.48771096 N -2.61741263 1.38275080 2.63428181 N 2.61742270 -1.38276006 -2.63427425 C -1.77086206 0.18100754 0.43957605 C 1.77086254 -0.18101028 -0.43957552 C -2.22486176 -1.28143567 0.45202312 C -2.30707039 0.92407663 -0.78734681 C 2.30707074 -0.92407071 0.78735246 C 2.22485929 1.28143406 -0.45203080 C -2.23868798 0.85547218 1.67084736 C 2.23869247 -0.85548109 -1.67084185 H -1.90398693 -1.81060764 -0.45229645 H -3.31681639 -1.35858536 0.51240600 H -1.80714051 -1.81980551 1.31137107 H -3.40300863 0.95379538 -0.78701415 H -1.98806037 0.44494681 -1.71978670 H -1.94802915 1.96005927 -0.81269573 H 1.98805486 -0.44493850 1.71978893 H 1.94803425 -1.96005464 0.81270509 H 3.40300902 -0.95378386 0.78702431 H 1.90398036 1.81061002 0.45228426 H 3.31681405 1.35858667 -0.51241516 H 1.80713611 1.81979843 -1.31138136""" # check that N=N and C#N do not collide self.assertFalse(common.colliding_atoms( converter.str_to_xyz(xyz_no_0))) self.assertFalse(common.colliding_atoms( converter.str_to_xyz(xyz_no_1))) self.assertFalse(common.colliding_atoms( converter.str_to_xyz(xyz_no_2))) self.assertFalse(common.colliding_atoms( converter.str_to_xyz(xyz_no_3))) xyz_0 = """C 0.0 0.0 0.0 H 0.0 0.0 0.5""" # colliding atoms xyz_1 = """C -0.84339557 -0.03079260 -0.13110478 N 0.53015060 0.44534713 -0.25006000 O 1.33245258 -0.55134720 0.44204567 H -1.12632103 -0.17824612 0.91628291 H -1.52529493 0.70480833 -0.56787044 H -0.97406455 -0.97317212 -0.67214713 H 1.33245258 -0.55134720 0.48204567 H 1.98414750 -0.79355889 -0.24492049""" # colliding atoms xyz_2 = """ N -0.29070308 0.26322835 0.48770927 N 0.29070351 -0.26323281 -0.48771096 N -2.48318439 1.19587180 2.29281971 N 2.61742270 -1.38276006 -2.63427425 C -1.77086206 0.18100754 0.43957605 C 1.77086254 -0.18101028 -0.43957552 C -2.22486176 -1.28143567 0.45202312 C -2.30707039 0.92407663 -0.78734681 C 2.30707074 -0.92407071 0.78735246 C 2.22485929 1.28143406 -0.45203080 C -2.23868798 0.85547218 1.67084736 C 2.23869247 -0.85548109 -1.67084185 H -1.90398693 -1.81060764 -0.45229645 H -3.31681639 -1.35858536 0.51240600 H -1.80714051 -1.81980551 1.31137107 H -3.40300863 0.95379538 -0.78701415 H -1.98806037 0.44494681 -1.71978670 H -1.94802915 1.96005927 -0.81269573 H 1.98805486 -0.44493850 1.71978893 H 1.94803425 -1.96005464 0.81270509 H 3.40300902 -0.95378386 0.78702431 H 1.90398036 1.81061002 0.45228426 H 3.31681405 1.35858667 -0.51241516 H 1.80713611 1.81979843 -1.31138136""" # check that C-N collide xyz_3 = """ N -0.29070308 0.26322835 0.48770927 N 0.29070351 -0.26323281 -0.48771096 N -2.61741263 1.38275080 2.63428181 N 2.61742270 -1.38276006 -2.63427425 C -1.77086206 0.18100754 0.43957605 C 1.77086254 -0.18101028 -0.43957552 C -2.22486176 -1.28143567 0.45202312 C -2.30707039 0.92407663 -0.78734681 C 2.30707074 -0.92407071 0.78735246 C 2.22485929 1.28143406 -0.45203080 C -2.23868798 0.85547218 1.67084736 C 2.23869247 -0.85548109 -1.67084185 H -1.90398693 -1.81060764 -0.45229645 H -2.77266137 -1.32013927 0.48231533 H -1.80714051 -1.81980551 1.31137107 H -3.40300863 0.95379538 -0.78701415 H -1.98806037 0.44494681 -1.71978670 H -1.94802915 1.96005927 -0.81269573 H 1.98805486 -0.44493850 1.71978893 H 1.94803425 -1.96005464 0.81270509 H 3.40300902 -0.95378386 0.78702431 H 1.90398036 1.81061002 0.45228426 H 3.31681405 1.35858667 -0.51241516 H 1.80713611 1.81979843 -1.31138136""" # check that C-H collide self.assertTrue(common.colliding_atoms(converter.str_to_xyz(xyz_0))) self.assertTrue(common.colliding_atoms(converter.str_to_xyz(xyz_1))) self.assertTrue(common.colliding_atoms(converter.str_to_xyz(xyz_2))) self.assertTrue(common.colliding_atoms(converter.str_to_xyz(xyz_3)))