def test_from_molecule_graph(self): graph = MoleculeGraph.with_empty_graph(self.mol) adaptor = BabelMolAdaptor.from_molecule_graph(graph) obmol = adaptor.openbabel_mol self.assertEqual(obmol.NumAtoms(), 5) mol = adaptor.pymatgen_mol self.assertEqual(mol.formula, "H4 C1")
def open_ring(mol_graph, bond, opt_steps): """ Function to actually open a ring using OpenBabel's local opt. Given a molecule graph and a bond, convert the molecule graph into an OpenBabel molecule, remove the given bond, perform the local opt with the number of steps determined by self.steps, and then convert the resulting structure back into a molecule graph to be returned. """ obmol = BabelMolAdaptor.from_molecule_graph(mol_graph) obmol.remove_bond(bond[0][0] + 1, bond[0][1] + 1) obmol.localopt(steps=opt_steps, forcefield='uff') return MoleculeGraph.with_local_env_strategy(obmol.pymatgen_mol, OpenBabelNN())
def open_ring(mol_graph, bond, opt_steps): """ Function to actually open a ring using OpenBabel's local opt. Given a molecule graph and a bond, convert the molecule graph into an OpenBabel molecule, remove the given bond, perform the local opt with the number of steps determined by self.steps, and then convert the resulting structure back into a molecule graph to be returned. """ obmol = BabelMolAdaptor.from_molecule_graph(mol_graph) obmol.remove_bond(bond[0][0]+1, bond[0][1]+1) obmol.localopt(steps=opt_steps) return MoleculeGraph.with_local_env_strategy(obmol.pymatgen_mol, OpenBabelNN(), reorder=False, extend_structure=False)
def open_ring(mol_graph, bond, opt_steps): """ Function to actually open a ring using OpenBabel's local opt. Given a molecule graph and a bond, convert the molecule graph into an OpenBabel molecule, remove the given bond, perform the local opt with the number of steps determined by self.steps, and then convert the resulting structure back into a molecule graph to be returned. """ obmol = BabelMolAdaptor.from_molecule_graph(mol_graph) obmol.remove_bond(bond[0][0]+1, bond[0][1]+1) obmol.localopt(steps=opt_steps) return build_MoleculeGraph(obmol.pymatgen_mol, strategy=OpenBabelNN, reorder=False, extend_structure=False)
def molecule_graph_to_smiles( molecule_graph: MoleculeGraph) -> Optional[str]: """Converts a molecule graph to SMILES string. Args: molecule_graph: A molecule graph. Returns: The SMILES representation of the molecule. """ try: bma = BabelMolAdaptor.from_molecule_graph(molecule_graph) pbmol = bma.pybel_mol return pbmol.write(str("smi")).split()[0] except RuntimeError: warnings.warn("Molecule naming requires openbabel to be installed " "with Python bindings. Please get it at " "http://openbabel.org.") return None
def fragment_and_process(self, bonds): # Try to split the principle: try: frags = self.mol_graph.split_molecule_subgraphs(bonds,allow_reverse=True) frag_success = True except MolGraphSplitError: # If split is unsuccessful, then we have encountered a ring bond if len(bonds) == 1: self.ring_bonds += bonds # So we open the ring and make sure we haven't already encountered an identically opened fragment: RO_frag = open_ring(self.mol_graph, bonds, 1000) frag_done = False for done_RO_frag in self.done_RO_frags: if RO_frag.isomorphic_to(done_RO_frag): frag_done = True if not frag_done: # If this is a new fragment, save the record and then search for relevant fragment entries: self.done_RO_frags.append(RO_frag) opened_entries = self.search_fragment_entries(RO_frag) good_entries = [] # We will start by looking at entries with no structure change for frag in opened_entries[0]: # 0 -> no structural change # Since a ring opening still yields a single molecule, it should have the same charge as the principle: if frag["initial_molecule"]["charge"] == self.molecule_entry["final_molecule"]["charge"]: good_entries.append(frag) # If we didn't find any good entries, let's also look at those that exhibit structural changes: if len(good_entries) == 0: for frag in opened_entries[1]: # 1 -> YES structural change if frag["initial_molecule"]["charge"] == self.molecule_entry["final_molecule"]["charge"]: good_entries.append(frag) # If we still have no good entries, something must have gone wrong with the calculations: if len(good_entries) == 0: bb = BabelMolAdaptor.from_molecule_graph(RO_frag) pbmol = bb.pybel_mol smiles = pbmol.write(str("smi")).split()[0] specie = nx.get_node_attributes(self.mol_graph.graph, "specie") print("Missing ring opening fragment resulting from the breakage of " + specie[bonds[0][0]] + " " + specie[bonds[0][1]] + " bond " + str(bonds[0][0]) + " " + str(bonds[0][1]) + " which would yield a molecule with this SMILES string: " + smiles) elif len(good_entries) == 1: # If we have only one good entry, format it and addd it to the list that will eventually return: self.bond_dissociation_energies += [self.build_new_entry(good_entries, bonds)] else: # We shouldn't ever encounter more than one good entry. raise RuntimeError("There should only be one valid ring opening fragment! Exiting...") elif len(bonds) == 2: if not multibreak: raise RuntimeError("Should only be trying to break two bonds if multibreak is true! Exiting...") else: print('No reason to try and break more than two bonds at once! Exiting...') raise ValueError frag_success = False if frag_success: # If the principle did successfully split, then we aren't dealing with a ring bond. # As above, we begin by making sure we haven't already encountered an identical pair of fragments: frags_done = False for frag_pair in self.done_frag_pairs: if frag_pair[0].isomorphic_to(frags[0]): if frag_pair[1].isomorphic_to(frags[1]): frags_done = True break elif frag_pair[1].isomorphic_to(frags[0]): if frag_pair[0].isomorphic_to(frags[1]): frags_done = True break if not frags_done: # If we haven't, we save this pair and search for the relevant fragment entries: self.done_frag_pairs += [frags] num_entries_for_this_frag_pair = 0 frag1_entries = self.search_fragment_entries(frags[0]) frag2_entries = self.search_fragment_entries(frags[1]) frag1_charges_found = [] frag2_charges_found = [] # We then check for our expected charges of each fragment: for frag1 in frag1_entries[0] + frag1_entries[1]: if frag1["initial_molecule"]["charge"] not in frag1_charges_found: frag1_charges_found += [frag1["initial_molecule"]["charge"]] for frag2 in frag2_entries[0] + frag2_entries[1]: if frag2["initial_molecule"]["charge"] not in frag2_charges_found: frag2_charges_found += [frag2["initial_molecule"]["charge"]] # If we're missing some of either, tell the user: if len(frag1_charges_found) < len(self.expected_charges): bb = BabelMolAdaptor(frags[0].molecule) pbmol = bb.pybel_mol smiles = pbmol.write(str("smi")).split()[0] for charge in self.expected_charges: if charge not in frag1_charges_found: print("Missing charge " + str(charge) + " for fragment " + smiles) if len(frag2_charges_found) < len(self.expected_charges): bb = BabelMolAdaptor(frags[1].molecule) pbmol = bb.pybel_mol smiles = pbmol.write(str("smi")).split()[0] for charge in self.expected_charges: if charge not in frag2_charges_found: print("Missing charge " + str(charge) + " for fragment " + smiles) # Now we attempt to pair fragments with the right total charge, starting with only fragments with no structural change: for frag1 in frag1_entries[0]: # 0 -> no structural change for frag2 in frag2_entries[0]: # 0 -> no structural change if frag1["initial_molecule"]["charge"] + frag2["initial_molecule"]["charge"] == self.molecule_entry["final_molecule"]["charge"]: self.bond_dissociation_energies += [self.build_new_entry([frag1, frag2], bonds)] num_entries_for_this_frag_pair += 1 # If we haven't found the number of fragment pairs that we expect, we expand our search to include fragments that do exhibit structural change: if num_entries_for_this_frag_pair < len(self.expected_charges): for frag1 in frag1_entries[0]: # 0 -> no structural change for frag2 in frag2_entries[1]: # 1 -> YES structural change if frag1["initial_molecule"]["charge"] + frag2["initial_molecule"]["charge"] == self.molecule_entry["final_molecule"]["charge"]: self.bond_dissociation_energies += [self.build_new_entry([frag1, frag2], bonds)] num_entries_for_this_frag_pair += 1 for frag1 in frag1_entries[1]: # 1 -> YES structural change for frag2 in frag2_entries[0]: # 0 -> no structural change if frag1["initial_molecule"]["charge"] + frag2["initial_molecule"]["charge"] == self.molecule_entry["final_molecule"]["charge"]: self.bond_dissociation_energies += [self.build_new_entry([frag1, frag2], bonds)] num_entries_for_this_frag_pair += 1