meeko/writer.py
def write_string(self, mol, remove_index_map=False, remove_smiles=False):
        """Output a PDBQT file as a string.
        
        Args:
            mol (OBMol): OBMol that was prepared with Meeko

        Returns:
            str: PDBQT string of the molecule

        """
        self._count = 1
        self._visited = []
        self._numbering = {}
        self._pdbqt_buffer = []
        self._atom_counter = {}
        self._resinfo_set = set()

        self.mol = mol
        self.model = mol.setup.flexibility_model
        # get a copy of the current setup, since it's going to be messed up by the hacks for legacy, D3R, etc...
        self.setup = mol.setup.copy()

        root = self.model['root']
        torsdof = len(self.model['rigid_body_graph']) - 1

        if 'torsions_org' in self.model:
            torsdof_org = self.model['torsions_org']
            self._pdbqt_buffer.append('REMARK Flexibility Score: %2.2f' % self.model['score'] )
            active_tors = torsdof_org
        else:
            active_tors = torsdof

        self._walk_graph_recursive(root, first=True)

        if not remove_index_map:
            for i, remark_line in enumerate(self.remark_index_map()):
                # need to use 'insert' because self._numbering is calculated
                # only after self._walk_graph_recursive
                self._pdbqt_buffer.insert(i, remark_line)

        if not remove_smiles:
            sdfstring = obutils.writeMolecule(mol, ftype='sdf')
            ob_smiles = obutils.writeMolecule(mol, ftype='smi')

            rdmol = Chem.MolFromMolBlock(sdfstring, removeHs=False)
            rdmol_noH = Chem.MolFromMolBlock(sdfstring)
            rdkit_smiles = Chem.MolToSmiles(rdmol_noH)

            # map smiles noH to smiles with H
            atomic_num_rdmol_noH = [atom.GetAtomicNum() for atom in rdmol_noH.GetAtoms()]
            noH_to_H = []
            num_H_in_noH = 0 # e.g. stereo imines [H]/N=C keep [H] after RemoveHs()
            for (index, atom) in enumerate(rdmol.GetAtoms()):
                if atom.GetAtomicNum() == 1: continue
                for i in range(len(noH_to_H), len(atomic_num_rdmol_noH)):
                    if atomic_num_rdmol_noH[i] > 1: break
                    noH_to_H.append('H')
                noH_to_H.append(index)
            extra_hydrogens = len(atomic_num_rdmol_noH) - len(noH_to_H)
            if extra_hydrogens > 0:
                assert(set(atomic_num_rdmol_noH[len(noH_to_H):]) == {1}) 
                noH_to_H.extend(['H'] * extra_hydrogens)

            # map indices of explicit hydrogens, e.g. stereo imine [H]/N=C
            for index in range(len(noH_to_H)):
                if noH_to_H[index] != 'H': continue
                h_atom = rdmol_noH.GetAtomWithIdx(index)
                assert(h_atom.GetAtomicNum() == 1)
                parents = h_atom.GetNeighbors()
                assert(len(parents) == 1)
                num_h_in_parent = len([a for a in parents[0].GetNeighbors() if a.GetAtomicNum() == 1])
                if num_h_in_parent != 1:
                    msg = "Can't handle %d explicit H for each heavy atomin noH mol.\n" % num_h_in_parent
                    msg += "Was expecting only imines [H]N=\n"
                    raise RuntimeError(msg)
                parent_index_in_mol_with_H = noH_to_H[parents[0].GetIdx()]
                parent_in_mol_with_H = rdmol.GetAtomWithIdx(parent_index_in_mol_with_H)
                h_in_mol_with_H = [a for a in parent_in_mol_with_H.GetNeighbors() if a.GetAtomicNum() == 1]  
                if len(h_in_mol_with_H) != 1:
                    msg = "Can't handle %d explicit H for each heavy atomin noH mol.\n" % len(h_in_mol_with_H)
                    msg += "Was expecting only imines [H]N=\n"
                    raise RuntimeError(msg)
                noH_to_H[index] = h_in_mol_with_H[0].GetIdx()

            # notably, 3D SDF files written by other toolkits (OEChem, ChemAxon)
            # seem to not include the chiral flag in the bonds block, only in
            # the atoms block. RDKit ignores the atoms chiral flag as per the
            # spec. Since OpenBabel writes 3D SDF files with chiral flags on the bonds
            # we are good here. Otherwise, when reading SDF (e.g. from PubChem/PDB),
            # we may need to have RDKit assign stereo from coordinates, see:
            # https://sourceforge.net/p/rdkit/mailman/message/34399371/
            order_string = rdmol_noH.GetProp("_smilesAtomOutputOrder")
            order_string = order_string.replace(',]', ']') # remove trailing comma
            order = json.loads(order_string) # rdmol_noH to smiles
            order = list(np.argsort(order))
            order = {noH_to_H[i]+1: order[i]+1 for i in range(len(order))} # 1-index

            # identify polar hydrogens, which are not in the smiles
            missing_h = []
            strings_h_parent = []
            for key in self._numbering:
                if key in self.setup.atom_pseudo: continue
                if key not in order:
                    atom = mol.GetAtom(key)
                    if atom.GetAtomicNum() != 1:
                        raise RuntimeError("non-Hydrogen atom unexpectedely missing from RDKit smiles!?")
                    missing_h.append(key)
                    parents = [a for a in ob.OBAtomAtomIter(atom)]
                    if len(parents) != 1:
                        raise RuntimeError("expected hydrogen to have exactly one parent")
                    parent_idx = order[parents[0].GetIdx()]
                    string = ' %d %d' % (parent_idx, self._numbering[key])
                    strings_h_parent.append(string)
            remarks_h_parent = self.strings_to_remarks(strings_h_parent, "REMARK H PARENT")
            remark_prefix = "REMARK SMILES IDX"
            remark_idxmap = self.remark_index_map(order, remark_prefix, missing_h)
            remarks = []
            remarks.append("REMARK SMILES %s" % rdkit_smiles) # break line at 79 chars?
            remarks.extend(remark_idxmap)
            remarks.extend(remarks_h_parent)

            for i, remark_line in enumerate(remarks):
                # need to use 'insert' because self._numbering is calculated
                # only after self._walk_graph_recursive
                self._pdbqt_buffer.insert(i, remark_line)

        if self.setup.is_protein_sidechain:
            if len(self._resinfo_set) > 1:
                print("Warning: more than a single resName, resNum, chain in flexres", file=sys.stderr)
                print(self._resinfo_set, file=sys.stderr)
            resinfo = list(self._resinfo_set)[0]
            pdbinfo = obutils.PDBAtomInfo('', resinfo.resName, resinfo.resNum, resinfo.chain)
            _, res_name, res_num, chain = self._get_pdbinfo_fitting_pdb_chars(pdbinfo)
            resinfo_string = "{:3s} {:1s}{:4d}".format(res_name, chain, res_num)
            self._pdbqt_buffer.insert(0, 'BEGIN_RES %s' % resinfo_string)
            self._pdbqt_buffer.append('END_RES %s' % resinfo_string)
        else: # no TORSDOF in flexres
            # torsdof is always going to be the one of the rigid, non-macrocyclic one
            self._pdbqt_buffer.append('TORSDOF %d' % active_tors)


        return '\n'.join(self._pdbqt_buffer) + '\n'
Output a PDBQT file as a string.
        
        Args:
            mol (OBMol): OBMol that was prepared with Meeko

        Returns:
            str: PDBQT string of the molecule

        
Output a PDBQT file as a string.
Output a PDBQT file as a string .
def write_string(self, mol, remove_index_map=False, remove_smiles=False):
        self._count = 1
        self._visited = []
        self._numbering = {}
        self._pdbqt_buffer = []
        self._atom_counter = {}
        self._resinfo_set = set()
        self.mol = mol
        self.model = mol.setup.flexibility_model
        self.setup = mol.setup.copy()
        root = self.model['root']
        torsdof = len(self.model['rigid_body_graph']) - 1
        if 'torsions_org' in self.model:
            torsdof_org = self.model['torsions_org']
            self._pdbqt_buffer.append('REMARK Flexibility Score: %2.2f' % self.model['score'] )
            active_tors = torsdof_org
        else:
            active_tors = torsdof
        self._walk_graph_recursive(root, first=True)
        if not remove_index_map:
            for i, remark_line in enumerate(self.remark_index_map()):
                self._pdbqt_buffer.insert(i, remark_line)
        if not remove_smiles:
            sdfstring = obutils.writeMolecule(mol, ftype='sdf')
            ob_smiles = obutils.writeMolecule(mol, ftype='smi')
            rdmol = Chem.MolFromMolBlock(sdfstring, removeHs=False)
            rdmol_noH = Chem.MolFromMolBlock(sdfstring)
            rdkit_smiles = Chem.MolToSmiles(rdmol_noH)
            atomic_num_rdmol_noH = [atom.GetAtomicNum() for atom in rdmol_noH.GetAtoms()]
            noH_to_H = []
            num_H_in_noH = 0 
            for (index, atom) in enumerate(rdmol.GetAtoms()):
                if atom.GetAtomicNum() == 1: continue
                for i in range(len(noH_to_H), len(atomic_num_rdmol_noH)):
                    if atomic_num_rdmol_noH[i] > 1: break
                    noH_to_H.append('H')
                noH_to_H.append(index)
            extra_hydrogens = len(atomic_num_rdmol_noH) - len(noH_to_H)
            if extra_hydrogens > 0:
                assert(set(atomic_num_rdmol_noH[len(noH_to_H):]) == {1}) 
                noH_to_H.extend(['H'] * extra_hydrogens)
            for index in range(len(noH_to_H)):
                if noH_to_H[index] != 'H': continue
                h_atom = rdmol_noH.GetAtomWithIdx(index)
                assert(h_atom.GetAtomicNum() == 1)
                parents = h_atom.GetNeighbors()
                assert(len(parents) == 1)
                num_h_in_parent = len([a for a in parents[0].GetNeighbors() if a.GetAtomicNum() == 1])
                if num_h_in_parent != 1:
                    msg = "Can't handle %d explicit H for each heavy atomin noH mol.\n" % num_h_in_parent
                    msg += "Was expecting only imines [H]N=\n"
                    raise RuntimeError(msg)
                parent_index_in_mol_with_H = noH_to_H[parents[0].GetIdx()]
                parent_in_mol_with_H = rdmol.GetAtomWithIdx(parent_index_in_mol_with_H)
                h_in_mol_with_H = [a for a in parent_in_mol_with_H.GetNeighbors() if a.GetAtomicNum() == 1]  
                if len(h_in_mol_with_H) != 1:
                    msg = "Can't handle %d explicit H for each heavy atomin noH mol.\n" % len(h_in_mol_with_H)
                    msg += "Was expecting only imines [H]N=\n"
                    raise RuntimeError(msg)
                noH_to_H[index] = h_in_mol_with_H[0].GetIdx()
            order_string = rdmol_noH.GetProp("_smilesAtomOutputOrder")
            order_string = order_string.replace(',]', ']') 
            order = json.loads(order_string) 
            order = list(np.argsort(order))
            order = {noH_to_H[i]+1: order[i]+1 for i in range(len(order))} 
            missing_h = []
            strings_h_parent = []
            for key in self._numbering:
                if key in self.setup.atom_pseudo: continue
                if key not in order:
                    atom = mol.GetAtom(key)
                    if atom.GetAtomicNum() != 1:
                        raise RuntimeError("non-Hydrogen atom unexpectedely missing from RDKit smiles!?")
                    missing_h.append(key)
                    parents = [a for a in ob.OBAtomAtomIter(atom)]
                    if len(parents) != 1:
                        raise RuntimeError("expected hydrogen to have exactly one parent")
                    parent_idx = order[parents[0].GetIdx()]
                    string = ' %d %d' % (parent_idx, self._numbering[key])
                    strings_h_parent.append(string)
            remarks_h_parent = self.strings_to_remarks(strings_h_parent, "REMARK H PARENT")
            remark_prefix = "REMARK SMILES IDX"
            remark_idxmap = self.remark_index_map(order, remark_prefix, missing_h)
            remarks = []
            remarks.append("REMARK SMILES %s" % rdkit_smiles) 
            remarks.extend(remark_idxmap)
            remarks.extend(remarks_h_parent)
            for i, remark_line in enumerate(remarks):
                self._pdbqt_buffer.insert(i, remark_line)
        if self.setup.is_protein_sidechain:
            if len(self._resinfo_set) > 1:
                print("Warning: more than a single resName, resNum, chain in flexres", file=sys.stderr)
                print(self._resinfo_set, file=sys.stderr)
            resinfo = list(self._resinfo_set)[0]
            pdbinfo = obutils.PDBAtomInfo('', resinfo.resName, resinfo.resNum, resinfo.chain)
            _, res_name, res_num, chain = self._get_pdbinfo_fitting_pdb_chars(pdbinfo)
            resinfo_string = "{:3s} {:1s}{:4d}".format(res_name, chain, res_num)
            self._pdbqt_buffer.insert(0, 'BEGIN_RES %s' % resinfo_string)
            self._pdbqt_buffer.append('END_RES %s' % resinfo_string)
        else: 
            self._pdbqt_buffer.append('TORSDOF %d' % active_tors)
        return '\n'.join(self._pdbqt_buffer) + '\n'
def write_string ( self , mol , remove_index_map = False , remove_smiles = False ) : self . _count = 1 self . _visited = [ ] self . _numbering = { } self . _pdbqt_buffer = [ ] self . _atom_counter = { } self . _resinfo_set = set ( ) self . mol = mol self . model = mol . setup . flexibility_model self . setup = mol . setup . copy ( ) root = self . model [ 'root' ] torsdof = len ( self . model [ 'rigid_body_graph' ] ) - 1 if 'torsions_org' in self . model : torsdof_org = self . model [ 'torsions_org' ] self . _pdbqt_buffer . append ( 'REMARK Flexibility Score: %2.2f' % self . model [ 'score' ] ) active_tors = torsdof_org else : active_tors = torsdof self . _walk_graph_recursive ( root , first = True ) if not remove_index_map : for i , remark_line in enumerate ( self . remark_index_map ( ) ) : self . _pdbqt_buffer . insert ( i , remark_line ) if not remove_smiles : sdfstring = obutils . writeMolecule ( mol , ftype = 'sdf' ) ob_smiles = obutils . writeMolecule ( mol , ftype = 'smi' ) rdmol = Chem . MolFromMolBlock ( sdfstring , removeHs = False ) rdmol_noH = Chem . MolFromMolBlock ( sdfstring ) rdkit_smiles = Chem . MolToSmiles ( rdmol_noH ) atomic_num_rdmol_noH = [ atom . GetAtomicNum ( ) for atom in rdmol_noH . GetAtoms ( ) ] noH_to_H = [ ] num_H_in_noH = 0 for ( index , atom ) in enumerate ( rdmol . GetAtoms ( ) ) : if atom . GetAtomicNum ( ) == 1 : continue for i in range ( len ( noH_to_H ) , len ( atomic_num_rdmol_noH ) ) : if atomic_num_rdmol_noH [ i ] > 1 : break noH_to_H . append ( 'H' ) noH_to_H . append ( index ) extra_hydrogens = len ( atomic_num_rdmol_noH ) - len ( noH_to_H ) if extra_hydrogens > 0 : assert ( set ( atomic_num_rdmol_noH [ len ( noH_to_H ) : ] ) == { 1 } ) noH_to_H . extend ( [ 'H' ] * extra_hydrogens ) for index in range ( len ( noH_to_H ) ) : if noH_to_H [ index ] != 'H' : continue h_atom = rdmol_noH . GetAtomWithIdx ( index ) assert ( h_atom . GetAtomicNum ( ) == 1 ) parents = h_atom . GetNeighbors ( ) assert ( len ( parents ) == 1 ) num_h_in_parent = len ( [ a for a in parents [ 0 ] . GetNeighbors ( ) if a . GetAtomicNum ( ) == 1 ] ) if num_h_in_parent != 1 : msg = "Can't handle %d explicit H for each heavy atomin noH mol.\n" % num_h_in_parent msg += "Was expecting only imines [H]N=\n" raise RuntimeError ( msg ) parent_index_in_mol_with_H = noH_to_H [ parents [ 0 ] . GetIdx ( ) ] parent_in_mol_with_H = rdmol . GetAtomWithIdx ( parent_index_in_mol_with_H ) h_in_mol_with_H = [ a for a in parent_in_mol_with_H . GetNeighbors ( ) if a . GetAtomicNum ( ) == 1 ] if len ( h_in_mol_with_H ) != 1 : msg = "Can't handle %d explicit H for each heavy atomin noH mol.\n" % len ( h_in_mol_with_H ) msg += "Was expecting only imines [H]N=\n" raise RuntimeError ( msg ) noH_to_H [ index ] = h_in_mol_with_H [ 0 ] . GetIdx ( ) order_string = rdmol_noH . GetProp ( "_smilesAtomOutputOrder" ) order_string = order_string . replace ( ',]' , ']' ) order = json . loads ( order_string ) order = list ( np . argsort ( order ) ) order = { noH_to_H [ i ] + 1 : order [ i ] + 1 for i in range ( len ( order ) ) } missing_h = [ ] strings_h_parent = [ ] for key in self . _numbering : if key in self . setup . atom_pseudo : continue if key not in order : atom = mol . GetAtom ( key ) if atom . GetAtomicNum ( ) != 1 : raise RuntimeError ( "non-Hydrogen atom unexpectedely missing from RDKit smiles!?" ) missing_h . append ( key ) parents = [ a for a in ob . OBAtomAtomIter ( atom ) ] if len ( parents ) != 1 : raise RuntimeError ( "expected hydrogen to have exactly one parent" ) parent_idx = order [ parents [ 0 ] . GetIdx ( ) ] string = ' %d %d' % ( parent_idx , self . _numbering [ key ] ) strings_h_parent . append ( string ) remarks_h_parent = self . strings_to_remarks ( strings_h_parent , "REMARK H PARENT" ) remark_prefix = "REMARK SMILES IDX" remark_idxmap = self . remark_index_map ( order , remark_prefix , missing_h ) remarks = [ ] remarks . append ( "REMARK SMILES %s" % rdkit_smiles ) remarks . extend ( remark_idxmap ) remarks . extend ( remarks_h_parent ) for i , remark_line in enumerate ( remarks ) : self . _pdbqt_buffer . insert ( i , remark_line ) if self . setup . is_protein_sidechain : if len ( self . _resinfo_set ) > 1 : print ( "Warning: more than a single resName, resNum, chain in flexres" , file = sys . stderr ) print ( self . _resinfo_set , file = sys . stderr ) resinfo = list ( self . _resinfo_set ) [ 0 ] pdbinfo = obutils . PDBAtomInfo ( '' , resinfo . resName , resinfo . resNum , resinfo . chain ) _ , res_name , res_num , chain = self . _get_pdbinfo_fitting_pdb_chars ( pdbinfo ) resinfo_string = "{:3s} {:1s}{:4d}" . format ( res_name , chain , res_num ) self . _pdbqt_buffer . insert ( 0 , 'BEGIN_RES %s' % resinfo_string ) self . _pdbqt_buffer . append ( 'END_RES %s' % resinfo_string ) else : self . _pdbqt_buffer . append ( 'TORSDOF %d' % active_tors ) return '\n' . join ( self . _pdbqt_buffer ) + '\n'
Output a PDBQT file as a string.
Output a PDBQT file as a string .
"""Output a PDBQT file as a string.
        
        Args:
            mol (OBMol): OBMol that was prepared with Meeko

        Returns:
            str: PDBQT string of the molecule

        """ # get a copy of the current setup, since it's going to be messed up by the hacks for legacy, D3R, etc... # need to use 'insert' because self._numbering is calculated # only after self._walk_graph_recursive # map smiles noH to smiles with H # e.g. stereo imines [H]/N=C keep [H] after RemoveHs() # map indices of explicit hydrogens, e.g. stereo imine [H]/N=C # notably, 3D SDF files written by other toolkits (OEChem, ChemAxon) # seem to not include the chiral flag in the bonds block, only in # the atoms block. RDKit ignores the atoms chiral flag as per the # spec. Since OpenBabel writes 3D SDF files with chiral flags on the bonds # we are good here. Otherwise, when reading SDF (e.g. from PubChem/PDB), # we may need to have RDKit assign stereo from coordinates, see: # https://sourceforge.net/p/rdkit/mailman/message/34399371/ # remove trailing comma # rdmol_noH to smiles # 1-index # identify polar hydrogens, which are not in the smiles # break line at 79 chars? # need to use 'insert' because self._numbering is calculated # only after self._walk_graph_recursive # no TORSDOF in flexres # torsdof is always going to be the one of the rigid, non-macrocyclic one
PDBQT string of the molecule
PDBQT string of the molecule
str
self
mol
OBMol that was prepared with Meeko
OBMol that was prepared with Meeko
remove_index_map
remove_smiles