vermouth/processors/canonicalize_modifications.py
def fix_ptm(molecule):
    '''
    Canonizes all PTM atoms in molecule, and labels the relevant residues with
    which PTMs were recognized. Modifies ``molecule`` such that atomnames of
    PTM atoms are corrected, and the relevant residues have been labeled with
    which PTMs were recognized.

    Parameters
    ----------
    molecule : networkx.Graph
        Must not have missing atoms, and atomnames must be correct. Atoms which
        could not be recognized must be labeled with the attribute
        PTM_atom=True.
    '''
    PTM_atoms = find_PTM_atoms(molecule)

    def key_func(ptm_atoms):
        node_idxs = ptm_atoms[-1]  # The anchors
        return sorted(molecule.nodes[idx]['resid'] for idx in node_idxs)

    ptm_atoms = sorted(PTM_atoms, key=key_func)

    resid_to_idxs = defaultdict(list)
    for n_idx in molecule:
        residx = molecule.nodes[n_idx]['resid']
        resid_to_idxs[residx].append(n_idx)
    resid_to_idxs = dict(resid_to_idxs)

    known_PTMs = molecule.force_field.modifications

    for resids, res_ptms in itertools.groupby(ptm_atoms, key_func):
        # How to solve this graph covering problem
        # Filter known_ptms, such that
        #   element_count(known_ptm) <= element_count(found)
        # Filter known_ptms, such that known_ptm <= found (subgraph of).
        #   Note that this does mean that the PTMs in the PDB *must* be
        #   complete. So no missing atoms.
        # Find all the exactly covering combinations.
        # Pick the best solution, such that the maximum size of the applied
        # PTMs is maximal. (3, 2) > (3, 1, 1) > (2, 2, 1)
            # Numbers are sizes of applied PTMs

        # The last two steps are combined by recursively trying the largest
        # option in identify_ptms

        res_ptms = list(res_ptms)
        n_idxs = set()
        for resid in resids:
            n_idxs.update(resid_to_idxs[resid])
        # TODO: Maybe use graph_utils.make_residue_graph? Or rewrite that
        #       function?
        residue = molecule.subgraph(n_idxs)
        options = allowed_ptms(residue, res_ptms, known_PTMs)
        # TODO/FIXME: This includes anchors in sorting by size.
        options = sorted(options, key=lambda opt: len(opt[0]), reverse=True)
        try:
            identified = identify_ptms(residue, res_ptms, options)
        except KeyError:
            LOGGER.exception('Could not identify the modifications for'
                             ' residues {}, involving atoms {}', 
                             ['{resname}{resid}'.format(**molecule.nodes[resid_to_idxs[resid][0]])
                              for resid in resids],
                             ['{atomid}-{atomname}'.format(**molecule.nodes[idx])
                              for idxs in res_ptms for idx in idxs[0]],
                             type='unknown-input')
            raise
        # Why this mess? There can be multiple PTMs for a single (set of)
        # residue(s); and a single PTM can span multiple residues.
        LOGGER.info("Identified the modifications {} on residues {}",
                    [out[0].graph['name'] for out in identified],
                    ['{resname}{resid}'.format(**molecule.nodes[resid_to_idxs[resid][0]])
                     for resid in resids])
        for ptm, match in identified:
            for mol_idx, ptm_idx in match.items():
                ptm_node = ptm.nodes[ptm_idx]
                mol_node = molecule.nodes[mol_idx]
                # Names of PTM atoms still need to be corrected, and for some
                # non PTM atoms attributes need to change.
                # Nodes with 'replace': {'atomname': None} will be removed.
                if ptm_node['PTM_atom'] or 'replace' in ptm_node:
                    mol_node['graph'] = molecule.subgraph([mol_idx]).copy()
                    to_replace = ptm_node.copy()
                    if 'replace' in to_replace:
                        del to_replace['replace']
                    to_replace.update(ptm_node.get('replace', dict()))
                    for attr_name, val in to_replace.items():
                        if attr_name == 'atomname' and val is None:
                            LOGGER.debug('Removing atom {}',
                                         format_atom_string(mol_node),
                                         type='remove-atom')
                            molecule.remove_node(mol_idx)
                            n_idxs.remove(mol_idx)
                            break
                        if mol_node.get(attr_name) != val:
                            fmt = 'Changing attribute {} from {} to {} for atom {}'
                            LOGGER.debug(fmt, attr_name, mol_node[attr_name],
                                         val, format_atom_string(mol_node),
                                         type='change-atom')
                            mol_node[attr_name] = val
            for n_idx in n_idxs:
                molecule.nodes[n_idx]['modifications'] = molecule.nodes[n_idx].get('modifications', [])
                molecule.nodes[n_idx]['modifications'].append(ptm)
    return None

    Canonizes all PTM atoms in molecule, and labels the relevant residues with
    which PTMs were recognized. Modifies ``molecule`` such that atomnames of
    PTM atoms are corrected, and the relevant residues have been labeled with
    which PTMs were recognized.

    Parameters
    ----------
    molecule : networkx.Graph
        Must not have missing atoms, and atomnames must be correct. Atoms which
        could not be recognized must be labeled with the attribute
        PTM_atom=True.
    
Canonizes all PTM atoms in molecule, and labels the relevant residues with
which PTMs were recognized. Modifies ``molecule`` such that atomnames of
PTM atoms are corrected, and the relevant residues have been labeled with
which PTMs were recognized.

Parameters

molecule : networkx.Graph
Must not have missing atoms, and atomnames must be correct. Atoms which
could not be recognized must be labeled with the attribute
PTM_atom=True.
Canonizes all PTM atoms in molecule and labels the relevant residues with which PTMs were recognized . Modifies ` ` molecule ` ` such that atomnames of PTM atoms are corrected and the relevant residues have been labeled with which PTMs were recognized . Parameters molecule : networkx . Graph Must not have missing atoms and atomnames must be correct . Atoms which could not be recognized must be labeled with the attribute PTM_atom = True .
def fix_ptm(molecule):
    PTM_atoms = find_PTM_atoms(molecule)
    def key_func(ptm_atoms):
        node_idxs = ptm_atoms[-1]  
        return sorted(molecule.nodes[idx]['resid'] for idx in node_idxs)
    ptm_atoms = sorted(PTM_atoms, key=key_func)
    resid_to_idxs = defaultdict(list)
    for n_idx in molecule:
        residx = molecule.nodes[n_idx]['resid']
        resid_to_idxs[residx].append(n_idx)
    resid_to_idxs = dict(resid_to_idxs)
    known_PTMs = molecule.force_field.modifications
    for resids, res_ptms in itertools.groupby(ptm_atoms, key_func):
         known_ptm <= found (subgraph of).
        res_ptms = list(res_ptms)
        n_idxs = set()
        for resid in resids:
            n_idxs.update(resid_to_idxs[resid])
        residue = molecule.subgraph(n_idxs)
        options = allowed_ptms(residue, res_ptms, known_PTMs)
        options = sorted(options, key=lambda opt: len(opt[0]), reverse=True)
        try:
            identified = identify_ptms(residue, res_ptms, options)
        except KeyError:
            LOGGER.exception('Could not identify the modifications for'
                             ' residues {}, involving atoms {}', 
                             ['{resname}{resid}'.format(**molecule.nodes[resid_to_idxs[resid][0]])
                              for resid in resids],
                             ['{atomid}-{atomname}'.format(**molecule.nodes[idx])
                              for idxs in res_ptms for idx in idxs[0]],
                             type='unknown-input')
            raise
        LOGGER.info("Identified the modifications {} on residues {}",
                    [out[0].graph['name'] for out in identified],
                    ['{resname}{resid}'.format(**molecule.nodes[resid_to_idxs[resid][0]])
                     for resid in resids])
        for ptm, match in identified:
            for mol_idx, ptm_idx in match.items():
                ptm_node = ptm.nodes[ptm_idx]
                mol_node = molecule.nodes[mol_idx]
                if ptm_node['PTM_atom'] or 'replace' in ptm_node:
                    mol_node['graph'] = molecule.subgraph([mol_idx]).copy()
                    to_replace = ptm_node.copy()
                    if 'replace' in to_replace:
                        del to_replace['replace']
                    to_replace.update(ptm_node.get('replace', dict()))
                    for attr_name, val in to_replace.items():
                        if attr_name == 'atomname' and val is None:
                            LOGGER.debug('Removing atom {}',
                                         format_atom_string(mol_node),
                                         type='remove-atom')
                            molecule.remove_node(mol_idx)
                            n_idxs.remove(mol_idx)
                            break
                        if mol_node.get(attr_name) != val:
                            fmt = 'Changing attribute {} from {} to {} for atom {}'
                            LOGGER.debug(fmt, attr_name, mol_node[attr_name],
                                         val, format_atom_string(mol_node),
                                         type='change-atom')
                            mol_node[attr_name] = val
            for n_idx in n_idxs:
                molecule.nodes[n_idx]['modifications'] = molecule.nodes[n_idx].get('modifications', [])
                molecule.nodes[n_idx]['modifications'].append(ptm)
    return None
def fix_ptm ( molecule ) : PTM_atoms = find_PTM_atoms ( molecule ) def key_func ( ptm_atoms ) : node_idxs = ptm_atoms [ - 1 ] return sorted ( molecule . nodes [ idx ] [ 'resid' ] for idx in node_idxs ) ptm_atoms = sorted ( PTM_atoms , key = key_func ) resid_to_idxs = defaultdict ( list ) for n_idx in molecule : residx = molecule . nodes [ n_idx ] [ 'resid' ] resid_to_idxs [ residx ] . append ( n_idx ) resid_to_idxs = dict ( resid_to_idxs ) known_PTMs = molecule . force_field . modifications for resids , res_ptms in itertools . groupby ( ptm_atoms , key_func ) : res_ptms = list ( res_ptms ) n_idxs = set ( ) for resid in resids : n_idxs . update ( resid_to_idxs [ resid ] ) residue = molecule . subgraph ( n_idxs ) options = allowed_ptms ( residue , res_ptms , known_PTMs ) options = sorted ( options , key = lambda opt : len ( opt [ 0 ] ) , reverse = True ) try : identified = identify_ptms ( residue , res_ptms , options ) except KeyError : LOGGER . exception ( 'Could not identify the modifications for' ' residues {}, involving atoms {}' , [ '{resname}{resid}' . format ( ** molecule . nodes [ resid_to_idxs [ resid ] [ 0 ] ] ) for resid in resids ] , [ '{atomid}-{atomname}' . format ( ** molecule . nodes [ idx ] ) for idxs in res_ptms for idx in idxs [ 0 ] ] , type = 'unknown-input' ) raise LOGGER . info ( "Identified the modifications {} on residues {}" , [ out [ 0 ] . graph [ 'name' ] for out in identified ] , [ '{resname}{resid}' . format ( ** molecule . nodes [ resid_to_idxs [ resid ] [ 0 ] ] ) for resid in resids ] ) for ptm , match in identified : for mol_idx , ptm_idx in match . items ( ) : ptm_node = ptm . nodes [ ptm_idx ] mol_node = molecule . nodes [ mol_idx ] if ptm_node [ 'PTM_atom' ] or 'replace' in ptm_node : mol_node [ 'graph' ] = molecule . subgraph ( [ mol_idx ] ) . copy ( ) to_replace = ptm_node . copy ( ) if 'replace' in to_replace : del to_replace [ 'replace' ] to_replace . update ( ptm_node . get ( 'replace' , dict ( ) ) ) for attr_name , val in to_replace . items ( ) : if attr_name == 'atomname' and val is None : LOGGER . debug ( 'Removing atom {}' , format_atom_string ( mol_node ) , type = 'remove-atom' ) molecule . remove_node ( mol_idx ) n_idxs . remove ( mol_idx ) break if mol_node . get ( attr_name ) != val : fmt = 'Changing attribute {} from {} to {} for atom {}' LOGGER . debug ( fmt , attr_name , mol_node [ attr_name ] , val , format_atom_string ( mol_node ) , type = 'change-atom' ) mol_node [ attr_name ] = val for n_idx in n_idxs : molecule . nodes [ n_idx ] [ 'modifications' ] = molecule . nodes [ n_idx ] . get ( 'modifications' , [ ] ) molecule . nodes [ n_idx ] [ 'modifications' ] . append ( ptm ) return None
Canonizes all PTM atoms in molecule, and labels the relevant residues with
which PTMs were recognized.
Canonizes all PTM atoms in molecule and labels the relevant residues with which PTMs were recognized .
'''
    Canonizes all PTM atoms in molecule, and labels the relevant residues with
    which PTMs were recognized. Modifies ``molecule`` such that atomnames of
    PTM atoms are corrected, and the relevant residues have been labeled with
    which PTMs were recognized.

    Parameters
    ----------
    molecule : networkx.Graph
        Must not have missing atoms, and atomnames must be correct. Atoms which
        could not be recognized must be labeled with the attribute
        PTM_atom=True.
    ''' # The anchors # How to solve this graph covering problem # Filter known_ptms, such that #   element_count(known_ptm) <= element_count(found) # Filter known_ptms, such that known_ptm <= found (subgraph of). #   Note that this does mean that the PTMs in the PDB *must* be #   complete. So no missing atoms. # Find all the exactly covering combinations. # Pick the best solution, such that the maximum size of the applied # PTMs is maximal. (3, 2) > (3, 1, 1) > (2, 2, 1) # Numbers are sizes of applied PTMs # The last two steps are combined by recursively trying the largest # option in identify_ptms # TODO: Maybe use graph_utils.make_residue_graph? Or rewrite that #       function? # TODO/FIXME: This includes anchors in sorting by size. # Why this mess? There can be multiple PTMs for a single (set of) # residue(s); and a single PTM can span multiple residues. # Names of PTM atoms still need to be corrected, and for some # non PTM atoms attributes need to change. # Nodes with 'replace': {'atomname': None} will be removed.
molecule