Source code for promod3.rawmodel._closedels

'''Single function to be 'injected' into the RawModellingResult class in the
__init__.py file.
'''

import ost
#pylint: disable=no-name-in-module
from . import _rawmodel as rawmodel
from promod3 import loop

def _CloseSmallDeletions(self, scorer, extension_steps=9, clash_thresh=1.0,
                         e_thresh=200):
    '''Close small deletions by relaxing neighbouring residues.

    Small deletions in the template from the target-template alignment have a
    good chance to be bridged just by relaxing neighbours around a tiny gap.
    Before diving into the more demanding tasks in modeling, those may be closed
    already in the raw-model. After closure some checks are done to see if the
    solution is stereochemically sensible.

    Closed gaps are removed from :attr:`self.gaps`.

    .. testcode:: closesmalldel
      :hide:

      from promod3 import rawmodel
      from promod3 import loop

      tpl = ost.io.LoadPDB('../tests/rawmodel/data/raw-modeling/gly.pdb')
      aln = ost.seq.CreateAlignment(ost.seq.CreateSequence('trg', 'GGG-GGG'),
                                    ost.seq.CreateSequence('tpl', 'GGGAGGG'))
      aln.AttachView(1, tpl.CreateFullView())
      rmodel = rawmodel.BuildRawModel(aln)
      assert len(rmodel.gaps) == 1
      scorer = loop.SetupBackboneScorer(rmodel)
      rmodel.CloseSmallDeletions(scorer)
      assert len(rmodel.gaps) == 0

    .. doctest:: closesmalldel

      import ost
      from promod3 import rawmodel
      from promod3 import loop

      tpl = ost.io.LoadPDB('gly.pdb')
      aln = ost.io.LoadAlignment('seq.fasta')
      aln.AttachView(1, tpl.CreateFullView())
      rmodel = rawmodel.BuildRawModel(aln)
      scorer = loop.SetupBackboneScorer(rmodel)
      rmodel.CloseSmallDeletions(scorer)

    :param scorer: A scorer dedicated to this raw model.
    :type scorer: :class:`~promod3.loop.BackboneLoopScorer`

    :param extension_steps: Iterations allowed for gap extension. This does not
                            directly mean 'extended size of gap' but no. of
                            attempts to extend. Extension works like trying to
                            increase left, then right, then try more on the
                            left side again...
    :type extension_steps: :class:`int`

    :param clash_thresh: Threshold for the clash score, acceptance means being
                         lower than this.
    :type clash_thresh: :class:`float`

    :param e_thresh: Potential energy should be lower than this.
    :type e_thresh: :class:`float`

    :return: If gaps are deleted, the scorer needs to be updated and is
             returned.
    :rtype: :class:`~promod3.loop.BackboneLoopScorer`
    '''
    current_gap_index = 0

    # iterating self.gaps. The number of gaps may change during the process,
    # hence we run by 'while', breaking out once we've seen all gaps. If a gap
    # gets closed, it is deleted from self.gaps, otherwise, current_gap_index
    # as a counter is increased.
    while True:
        # check if all gaps were visited
        if current_gap_index >= len(self.gaps):
            break

        # in the end this determines if a gap was closed or not
        success = False

        # A deletion is a gap of size 0 in the template, this means that to
        # transform the template sequence into the target sequence, aa's vanish,
        # so the target sequence has gap caracters, the template not.
        # If we are not looking at a deletion, do nothing.
        if len(self.gaps[current_gap_index].seq) == 0:
            # work on a copy of the gap, if not closed in the end, no harm done
            current_gap = self.gaps[current_gap_index].Copy()
            current_chain = current_gap.GetChain()
            current_chain_index = current_gap.GetChainIndex()

            # Try to close gap by relaxation: by checking how far we may extend
            # the gap, we get the backbone to be stretched. If no more extension
            # is possible, break out. On first successful relaxation for an
            # extension, we successfully stop.
            extender = rawmodel.GapExtender(current_gap)
            for _ in range(extension_steps):
                if not extender.Extend():
                    break
                # gather residues for backbone relaxation, check that we get a
                # bunch of actual residues, otherwise jump to next extension
                res_list = list()
                current_resnum = current_gap.before.GetNumber()
                after_resnum = current_gap.after.GetNumber()
                found_residues = True
                while True:
                    if current_resnum > after_resnum:
                        break
                    res = current_chain.FindResidue(current_resnum)
                    if not res.IsValid():
                        found_residues = False
                        break
                    res_list.append(res)
                    current_resnum += 1
                if not found_residues:
                    continue
                # backbone relaxation, for now we allow 300 steps or stop at
                # max. force of 0.1
                bb_list = loop.BackboneList(current_gap.full_seq, res_list)
                bb_relaxer = loop.BackboneRelaxer(bb_list)
                potential_e = bb_relaxer.Run(bb_list, 300, 0.1)
                # check for clashes
                score = scorer.CalculateClashScore(\
                                        bb_list,
                                        current_gap.before.GetNumber().GetNum(),
                                        current_chain_index)
                # if there is no clash and potential energy is low enough we
                # just solved a gap, delete it and update the scorer for the
                # changed model
                if score < clash_thresh and \
                   potential_e < e_thresh and \
                   scorer.TransOmegaTorsions(bb_list):
                    ost.LogVerbose("Closed: %s by relaxing %s" % \
                                   (self.gaps[current_gap_index], current_gap))
                    chain = current_gap.before.GetChain()
                    bb_list.InsertInto(chain, current_gap.before.GetNumber(),
                                       current_gap.after.GetNumber())
                    scorer.SetEnvironment(\
                                        bb_list,
                                        current_gap.before.GetNumber().GetNum())
                    self.ClearGaps(current_gap)
                    success = True
                    break
        # On closed gap, it is removed so the no. of gaps goes down by itself.
        # In case of no success, counter needs to be increased to jump to the
        # next gap.
        if not success:
            current_gap_index += 1

    return scorer

__all__ = ['_CloseSmallDeletions']

#  LocalWords:  modeling stereochemically param