def get_template_ts_guess(reactant, product, bond_rearrangement, name, method, dist_thresh=4.0): """Get a transition state guess object by searching though the stored TS templates Arguments: reactant (autode.complex.ReactantComplex): bond_rearrangement (autode.bond_rearrangement.BondRearrangement): product (autode.complex.ProductComplex): method (autode.wrappers.base.ElectronicStructureMethod): name (str): keywords (list(str)): Keywords to use for the ElectronicStructureMethod Keyword Arguments: dist_thresh (float): distance above which a constrained optimisation probably won't work due to the initial geometry being too far away from the ideal (default: {4.0}) Returns: TSGuess object: ts guess object """ logger.info('Getting TS guess from stored TS template') active_bonds_and_dists_ts = {} # This will add edges so don't modify in place mol_graph = get_truncated_active_mol_graph(graph=reactant.graph, active_bonds=bond_rearrangement.all) ts_guess_templates = get_ts_templates() for ts_template in ts_guess_templates: if not template_matches(reactant=reactant, ts_template=ts_template, truncated_graph=mol_graph): continue # Get the mapping from the matching template mapping = get_mapping_ts_template(larger_graph=mol_graph, smaller_graph=ts_template.graph) for active_bond in bond_rearrangement.all: i, j = active_bond try: active_bonds_and_dists_ts[active_bond] = ts_template.graph.edges[mapping[i], mapping[j]]['distance'] except KeyError: logger.warning(f'Couldn\'t find a mapping for bond {i}-{j}') if len(active_bonds_and_dists_ts) == len(bond_rearrangement.all): logger.info('Found a TS guess from a template') if any([reactant.get_distance(*bond) > dist_thresh for bond in bond_rearrangement.all]): logger.info(f'TS template has => 1 active bond distance larger than {dist_thresh}. Passing') pass else: return get_ts_guess_constrained_opt(reactant, method=method, keywords=method.keywords.opt, name=name, distance_consts=active_bonds_and_dists_ts, product=product) logger.info('Could not find a TS guess from a template') return None
def test_inactive_graph(): # Should fail to get a active graph from a graph with no active edges with pytest.raises(ValueError): _ = get_truncated_active_mol_graph(ch3f.graph) template = TStemplate() assert not template.graph_has_correct_structure() template.graph = ch3f.graph.copy() assert not template.graph_has_correct_structure()
def test_truncated_active_graph(): h_c = Atom(atomic_symbol='H', x=0.0, y=0.0, z=1.4) h_d = Atom(atomic_symbol='H', x=0.0, y=0.0, z=2.1) ts = Species(name='template', charge=0, mult=1, atoms=[h_a, h_b, h_c, h_d]) mol_graphs.make_graph(species=ts, allow_invalid_valancies=True) # H--active--H--H--H should truncate by keeping only the nearest neighbours to the first two atoms truncated_graph = mol_graphs.get_truncated_active_mol_graph(ts.graph, active_bonds=[(0, 1)]) assert truncated_graph.number_of_nodes() == 3 assert truncated_graph.number_of_edges() == 2
def save_ts_template(self, folder_path=Config.ts_template_folder_path): """Save a transition state template containing the active bond lengths, solvent and charge in folder_path Keyword Arguments: folder_path (str): folder to save the TS template to (default: {None}) """ logger.info(f'Saving TS template for {self.name}') truncated_graph = get_truncated_active_mol_graph(self.graph, active_bonds=self.bond_rearrangement.all) for bond in self.bond_rearrangement.all: truncated_graph.edges[bond]['distance'] = self.get_distance(*bond) ts_template = TStemplate(truncated_graph, solvent=self.solvent, charge=self.charge, mult=self.mult) ts_template.save_object(folder_path=folder_path) logger.info('Saved TS template') return None
def test_ts_template_save(): ts_graph = reac_complex.graph.copy() # Add the F-C bond as active ts_graph.add_edge(0, 2, active=True) # Remove then re-add the C-Cl bond as active ts_graph.remove_edge(1, 2) ts_graph.add_edge(1, 2, active=True) truncated_graph = get_truncated_active_mol_graph(ts_graph) template = TStemplate(truncated_graph, species=reac_complex) template.save(folder_path=os.getcwd()) assert os.path.exists('template0.txt') # With no distances the template shouldn't be valid with pytest.raises(TemplateLoadingFailed): _ = TStemplate(filename='template0.txt') os.remove('template0.txt') truncated_graph.edges[(0, 2)]['distance'] = 1.9 truncated_graph.edges[(1, 2)]['distance'] = 2.0 template.graph = truncated_graph template.save(folder_path=os.getcwd()) loaded_template = TStemplate(filename='template_sn2.txt') assert loaded_template.solvent is None assert loaded_template.charge == -1 assert loaded_template.mult == 1 assert loaded_template.graph is not None assert loaded_template.graph.nodes == truncated_graph.nodes assert loaded_template.graph.edges == truncated_graph.edges
def has_matching_ts_templates(reactant, bond_rearr): """ See if there are any templates suitable to get a TS guess from a template Arguments: reactant (autode.complex.ReactantComplex): bond_rearr (autode.bond_rearrangement.BondRearrangement): Returns: bool: """ mol_graph = get_truncated_active_mol_graph(graph=reactant.graph, active_bonds=bond_rearr.all) ts_guess_templates = get_ts_templates() for ts_template in ts_guess_templates: if template_matches(reactant=reactant, ts_template=ts_template, truncated_graph=mol_graph): return True return False
def save_ts_template(self, folder_path=None): """Save a transition state template containing the active bond lengths, solvent and charge in folder_path Keyword Arguments: folder_path (str): folder to save the TS template to (default: {None}) """ if self.bond_rearrangement is None: raise ValueError('Cannot save a TS template without a bond ' 'rearrangement') logger.info(f'Saving TS template for {self.name}') truncated_graph = get_truncated_active_mol_graph(self.graph) for bond in self.bond_rearrangement.all: truncated_graph.edges[bond]['distance'] = self.distance(*bond) ts_template = TStemplate(truncated_graph, species=self) ts_template.save(folder_path=folder_path) logger.info('Saved TS template') return None