def list_matching_torsions(smi_file, forcefield): from fragmenter import chemi # chemi.file_to_oemols # generate oemols from smi file oemols = chemi.file_to_oemols(smi_file) # list of torsion parameters ff_torsion_param_list = forcefield.get_parameter_handler( 'ProperTorsions').parameters # tid_molecules_list[tid] = [{'mol_index': mol_index, 'indices': indices, 'covered_tids':covered_tids}, ...] tid_molecules_list = {} failed_smi = [] for torsion_param in ff_torsion_param_list: tid_molecules_list[torsion_param.id] = [] for oemol in tqdm(oemols): try: off_mol, mol_index, center_bond = gen_canonical_isomeric_smiles( oemol) oemol = Molecule.to_openeye(off_mol) except: failed_smi.append(oechem.OEMolToSmiles(oemol)) continue torsions_coverage = defaultdict(list) off_top = Topology.from_molecules(off_mol) center_tids = defaultdict(set) dihedrals = [] for torsion_indices, torsion_param in forcefield.label_molecules( off_top)[0]['ProperTorsions'].items(): i, j, k, l = torsion_indices if set([j, k]) == center_bond: center_tids[tuple(sorted([j, k]))].add(torsion_param.id) torsions_coverage[torsion_param].append(torsion_indices) dihedrals.append(torsion_indices) if not check_connectivity(dihedrals, oemol): print(f'## {mol_index} has diff bond info in oemol and offmol...') continue filtered_torsions_coverage = filter_torsions_coverage( torsions_coverage, oemol) # check connectivity for idx, (tid, indices_list) in enumerate( filtered_torsions_coverage.items()): for idxx, indices in enumerate(indices_list): if idxx == 0: # count once covered_tids = [] i, j, k, l = indices tids = center_tids[tuple(sorted([j, k]))] for i in tids: if i not in covered_tids: covered_tids.append(i) tid_molecules_list[tid].append({ 'mol_index': mol_index, 'indices': indices, 'covered_tids': covered_tids }) print("\n## Torsion parameter: matched molecules ##\n" + '-' * 90) print( f"{'idx':<7} {'ID':7s} {'SMIRKS Pattern':70s} {'Number of molecules matched'}" ) for idx, (tid, molecules_list) in enumerate(tid_molecules_list.items()): torsion_param = get_torsion_definition(ff_torsion_param_list, tid) print( f'{idx:<7} {torsion_param.id:7s} {torsion_param.smirks:70s} {len(molecules_list)}' ) print('-' * 90) return tid_molecules_list, failed_smi
def gen_pdf( tid_clusters_list: list, output_path: str, cols: int = 8, cell_width: int = 200, cell_height: int = 200, ): from openeye import oechem, oedepict from openforcefield.topology import Molecule itf = oechem.OEInterface() PageByPage = True suppress_h = True n = sum([len(clusters) for tid, clusters in tid_clusters_list.items()]) rows = math.ceil(n / cols) image = oedepict.OEImage(cell_width * cols, cell_height * rows) grid = oedepict.OEImageGrid(image, rows, cols) opts = oedepict.OE2DMolDisplayOptions(grid.GetCellWidth(), grid.GetCellHeight(), oedepict.OEScale_AutoScale) opts.SetAromaticStyle(oedepict.OEAromaticStyle_Circle) opts.SetTitleLocation(oedepict.OETitleLocation_Bottom) count = 0 for tid, clusters in tid_clusters_list.items(): for cluster in clusters: torsions = cluster['torsions'] label = cluster['cluster_label'] torsions = cluster['torsions'] for torsion in torsions: cell = grid.GetCell(count // cols + 1, count % cols + 1) smi = torsion['mol_index'] atom_indices = torsion['indices'] # mol = oechem.OEGraphMol() # oechem.OESmilesToMol(mol, smi) off_mol = Molecule.from_smiles(smi, allow_undefined_stereo=True) off_mol = off_mol.canonical_order_atoms() mol = Molecule.to_openeye(off_mol) title = '{} ({})'.format(tid, set(torsion['covered_tids'])) mol.SetTitle(title) oedepict.OEPrepareDepiction(mol, False, suppress_h) disp = oedepict.OE2DMolDisplay(mol, opts) # Highlight element of interest class NoAtom(oechem.OEUnaryAtomPred): def __call__(self, atom): return False class AtomInTorsion(oechem.OEUnaryAtomPred): def __call__(self, atom): return atom.GetIdx() in atom_indices class NoBond(oechem.OEUnaryBondPred): def __call__(self, bond): return False class CentralBondInTorsion(oechem.OEUnaryBondPred): def __call__(self, bond): return (bond.GetBgn().GetIdx() in atom_indices[1:3] ) and (bond.GetEnd().GetIdx() in atom_indices[1:3]) atoms = mol.GetAtoms(AtomInTorsion()) bonds = mol.GetBonds(NoBond()) abset = oechem.OEAtomBondSet(atoms, bonds) oedepict.OEAddHighlighting( disp, oechem.OEColor(oechem.OEYellow), oedepict.OEHighlightStyle_BallAndStick, abset) atoms = mol.GetAtoms(NoAtom()) bonds = mol.GetBonds(CentralBondInTorsion()) abset = oechem.OEAtomBondSet(atoms, bonds) oedepict.OEAddHighlighting( disp, oechem.OEColor(oechem.OEMandarin), oedepict.OEHighlightStyle_BallAndStick, abset) oedepict.OERenderMolecule(cell, disp) count += 1 oedepict.OEWriteImage(output_path, image)
def fragment(self, molecule: Molecule) -> List[FragmentData]: """ Fragment the molecule using the WBOFragmenter. Parameters: molecule: The openff molecule to be fragmented using the provided class settings Returns: A list of FragmentData schema which details how a parent molecule is related to a fragment and which bond we fragmented around. Raises: FragmenterError: If the molecule can not be fragmented. """ from fragmenter import fragment # make sure the molecule has at least one conformer as this can cause issues if molecule.n_conformers == 0: molecule.generate_conformers(n_conformers=1) # set up the fragmenter fragment_factory = fragment.WBOFragmenter( molecule=molecule.to_openeye(), verbose=False) fragments: List[FragmentData] = [] try: # fragment the molecule fragment_factory.fragment( threshold=self.wbo_threshold, keep_non_rotor_ring_substituents=self. keep_non_rotor_ring_substituents, ) # now we work out the relation between the fragment and the parent fragments_data = fragment_factory.to_torsiondrive_json() # now store the data for data in fragments_data.values(): off_frag = Molecule.from_mapped_smiles( data["identifiers"] ["canonical_isomeric_explicit_hydrogen_mapped_smiles"]) # get the fragment parent mapping frag_dihedral = data["dihedral"][0][1:3] # in some cases we get one fragment back which is the parent molecule # we should not work out a mapping if not molecule.is_isomorphic_with(off_frag): mapping = self._get_fragment_parent_mapping( fragment=off_frag, parent=molecule) # get the parent torsion parent_dihedral = tuple( [mapping[i] for i in frag_dihedral]) parent_molecule = molecule else: # reuse the current fragment data as dummy parent data mapping = dict((i, i) for i in range(molecule.n_atoms)) parent_dihedral = frag_dihedral parent_molecule = off_frag # this is the data we need so make the fragmnetdata frag_data = FragmentData( parent_molecule=parent_molecule, parent_torsion=parent_dihedral, fragment_molecule=off_frag, fragment_torsion=frag_dihedral, fragment_attributes=data["identifiers"], fragment_parent_mapping=mapping, ) fragments.append(frag_data) return fragments except RuntimeError: raise FragmenterError( f"The molecule {molecule} could not be fragmented so no fitting target was made." )