Example #1
0
 def __init__(self, struc, new_sequence=None, \
             keep=keep_nothing, strict=True):
     self.struc = struc
     self.new_sequence = new_sequence
     self.sup = ModernaSuperimposer()
     self.rmsd = None
     self._keep_func = keep
     if strict: 
         self.check()
     self._prepared_anchors = []
Example #2
0
    def merge_region(self):
        """Merges the optimized residues with the memorized ones."""
        residues = [r for r in self.model]
        if self.model_passive:
            # apply old residue numbers
            print '\nRestoring original numeration in the optimized region.'
            for num, resi in zip(self.model_resnumbers, residues):
                #print resi, num
                #self.model.renumber_residue(resi.identifier, num)
                resi.change_number(num)
            self.model = ModernaStructure('residues',residues)
            # do the superposition
            print '\nSuperimposing the optimized part onto the rest of the model.'
            all_atoms = self.model.get_all_atoms()
            sup = ModernaSuperimposer(moved_atoms = all_atoms)
            sup.get_atoms([self.stem5, self.stem3], BACKBONE_ATOMS, 'fixed')
            resi5, resi3 = self.model[self.residues[0]], self.model[self.residues[1]]
            sup.get_atoms([resi5, resi3], BACKBONE_ATOMS, 'moved')
            sup.superimpose()

            # merge residues
            print '\nMerging the optimized part with the rest of the model.'
            resi = [r for r in self.model]
            for r in self.model_passive:
                if r.identifier not in self.residues:
                    resi.append(r)
            self.model = RnaModel(None, None, self.options.chain_name, 'residues', resi)
Example #3
0
    def force_superimpose(self, old_bp, new_bp):
        """
        Superimposes base pairs if there is no interaction between old_bp and force mode is True
        """
        min_rmsd = 100.0
        pdb_name = ''
        base_pair = None
        path = DATA_PATH+'base_pairs/'

        for pdb in os.listdir(path):
            if new_bp in pdb:
                moved_residues = ModernaStructure('file', path+pdb).moderna_residues
                moved, fixed, to_move = self.create_atoms_lists(moved_residues)
                rmsd = ModernaSuperimposer(fixed, moved, to_move).rmsd
                if rmsd < min_rmsd:
                    pdb_name = pdb      
                    min_rmsd = rmsd
                    base_pair = moved_residues

        log.write_message("The best RMSD ("+str(min_rmsd)+") with structure: "+pdb_name)  
        if min_rmsd <= self.cutoff:
            log.write_message("RMSD <= cutoff (" + str(self.cutoff) + ")")
            self.assign_residue_numbers(base_pair['1'], base_pair['2'])
        else:
            log.write_message("RMSD > cutoff (" + str(self.cutoff) + ") - starting backbone builder")
            self.run_backbone_builder()
Example #4
0
 def __init__(self, old_bp=None, new_bp=None, cutoff=1.0, orientation="cis", force=False):
     """
     Arguments:
     - old_bp - single tuple/list: (moderna_residue1, moderna_residue2)
                where moderna_residue is ModernaResidue object from model 
     - new_bp - new base pair description in format 'BP'
                where BP is base pair shortcut
                example: 'AC' - 'Adenine-Cytosine'
     - cutoff - maximum allowed RMSD
     - orientation - cis or trans
     - force  - change base pair even if there is no interaction between old_bp 
 
     Starts immediately if finds input data 
     """
     self.old_bp = old_bp
     self.new_bp = new_bp        
     self.cutoff = cutoff
     self.orientation = orientation[0].lower()
     self.force = force
     self.interaction = ''
     self.rmsd = None
     self.matrices = IsostericityMatrices()
     self.superimposer = ModernaSuperimposer()
     #self.pyrimidine_atoms = ["C5'", "C4'", "O4'", "C3'", "O3'", "C2'", "O2'", "N1"]
    # self.purine_atoms = ["C5'", "C4'", "O4'", "C3'", "O3'", "C2'", "O2'", "N9"]
     #self.pyrimidine_atoms = ["C5'", "C4'", "O4'", "C3'", "O3'", "C2'", "N*"]
     #self.purine_atoms = ["C5'", "C4'", "O4'", "C3'", "O3'", "C2'", "N*"]
     #self.pyrimidine_atoms = ["P", "O3'"]
     #self.purine_atoms = ["P", "O3'"]
     #self.pyrimidine_atoms = ["P", "O5'",  "C5'", "C4'", "C3'", "O3'"]
     #self.purine_atoms = ["P", "O5'",  "C5'", "C4'", "C3'", "O3'"]
     self.pyrimidine_atoms = ["C1'", "N*"]
     self.purine_atoms = ["C1'", "N*"]
     self.result_bp = None #contains result base pair after modelling
     if None not in (self.old_bp, self.new_bp): self.check_input_data()
Example #5
0
 def test_superimposition(self):
     """
     """
     a = PDBParser().get_structure('A',BASE_PATH+'A.ent')[0]['C'][(' ',54,' ')]
     c = PDBParser().get_structure('C',BASE_PATH+'C.ent')[0]['C'][(' ',54,' ')] 
     original_coord = a['N6'].coord[1]
     ModernaSuperimposer([c['N1'],c['C2'],c['C6']],[a['N9'],a['C4'],a['C8']],a.child_list)
    
     self.failIfEqual(a['N6'].coord[1],original_coord)
Example #6
0
 def add_op12(self):
     """Adds OP1 OP2 atoms"""
     self.remove_op1_op2()
     phos = RNAChain('file', DATA_PATH + 'phosphate_group.pdb')['1']
     #TODO: replace by Bio.PDB --> faster
     mobile = [phos['OP3'], phos['P'], phos["O5'"]]
     fixed = [self.r1["O3'"], self.r2['P'], self.r2["O5'"]]
     s = ModernaSuperimposer(fixed, mobile, phos.child_list)
     self.r2.add(phos['OP1'])
     self.r2.add(phos['OP2'])
 def test_rmsd(self):
     """The RMS of the superposition should be close to manual superimp. in PyMOL."""
     a = PDBParser().get_structure('A',
                                   BASE_PATH + 'A.ent')[0]['C'][(' ', 54,
                                                                 ' ')]
     g = PDBParser().get_structure('G',
                                   BASE_PATH + 'G.ent')[0]['C'][(' ', 54,
                                                                 ' ')]
     m = ModernaSuperimposer([a['C4'], a['N9'], a['C8']],
                             [g['C4'], g['N9'], g['C8']], a.child_list)
     self.assertTrue(m.rmsd < 0.004)
Example #8
0
 def test_superimposition2(self):
     """
     """
     a = PDBParser().get_structure('A',BASE_PATH+'A.ent')[0]['C'][(' ',54,' ')]
     c = PDBParser().get_structure('C',BASE_PATH+'C.ent')[0]['C'][(' ',54,' ')] 
     #original_coord = a['N6'].coord[1]
     ModernaSuperimposer([c['N1'],c['C2'],c['C6']],[a['N9'],a['C4'],a['C8']],a.child_list)
     for at in a:
         self.assertAlmostEqual(at.coord[0], A_SUPERIMPOSED_ON_C[at.id.strip()][0],5)
         self.assertAlmostEqual(at.coord[1], A_SUPERIMPOSED_ON_C[at.id.strip()][1],5)
         self.assertAlmostEqual(at.coord[2], A_SUPERIMPOSED_ON_C[at.id.strip()][2],5)
Example #9
0
class ResidueEditor:
    """
    Deals with residue objects.
    Supplements RNAResidue object with functions to
    - exchange bases
    - add modifications
    - remove modifications
    - rotat chi angle
    The type of each ModernaResidue is automatically recognized, and has a couple
    of long and short names as attributes:
    - long_abbrev
    - short_abbrev (one letter abbreviation)
    - original_base
    - full_name
    - modified (True/False)
    """
    parse = PDBParser()
    superimpose = ModernaSuperimposer()

    def add_single_fragment(self, resi, rule):
        """
        Adds a fragment to a residue.

        Arguments:
        - an adding rule dict (prepared by get_modification_rules())
        """
        try:
            fragment = self.parse.get_structure(
                'fragment', MODIFICATION_FRAGMENTS_PATH +
                rule['fragment_file_name'])[0]['A'][('H_UNK', 1, ' ')]
        except IOError:
            raise ModernaResidueError('File does not exist: %s' %
                                      MODIFICATION_FRAGMENTS_PATH)

        self.superimpose.get_atoms([resi], rule['fixed_link_atoms'], 'fixed')
        self.superimpose.get_atoms([fragment], rule['moved_link_atoms'],
                                   'moved')
        self.superimpose.moved_atoms = fragment.child_list
        self.superimpose.superimpose()

        if rule['remove']:
            delete_atoms = rule['remove'].split(',') + rule['moved_link_atoms']
        else:
            delete_atoms = rule['moved_link_atoms']
        try:
            for atom_name in delete_atoms:
                resi.detach_child(atom_name)
            for atom in fragment:
                resi.add(atom)
        except ModernaResidueError, e:
            raise e
        except:
Example #10
0
 def __init__(self,
              template=None,
              alignment=None,
              model_chain_name='A',
              data_type=None,
              data=None,
              seq=None):
     ModernaStructure.__init__(self,
                               data_type=data_type,
                               data=data,
                               chain_name=model_chain_name,
                               seq=seq)
     #TODO: order of arguments is inconsistent.
     #TODO: template + alignment should be obligatory
     #TODO: replace alignment by recipe
     #TODO: rename to SingleTemplateModeling and/or refactor classes for tasks out.
     self.template = template
     if template:
         self.template.set_template_numeration()
     if alignment:
         self.recipe = RecipeMaker(alignment).recipe
     self.alignment = alignment
     self.s = ModernaSuperimposer()
Example #11
0
    def test_insert_in_helix(self):
        """Evaluates how a helical single strand is handled by LIR"""
        helix = load_model(SINGLE_STRAND, 'A')
        m = load_model(SINGLE_STRAND, 'A')
        lc = find_fragment(m, '1475', '1485', Sequence('GCCCGUGAC'), 10)

        # calculate all-atom-rmsds
        result = []
        i = 1
        for cand in lc:
            insert_fragment(m, cand)
            fixed = self.get_atoms(helix)
            moved = self.get_atoms(m)
            s = ModernaSuperimposer(fixed, moved, m.get_all_atoms())
            result.append(s.rmsd)
            i += 1

        self.assertAlmostEqual(result[0], 0.000, 3)
        avg_rms = sum(result) / (1.0 * len(result))
        self.assertTrue(avg_rms < 3.25)
Example #12
0
    def superimpose_bp(self):
        """
        Superimpose two base pairs
        Uses self.moved_atoms and self.standard_atoms
        """
        filename = self.interaction+'_'+self.new_bp+'.pdb'
        log.write_message("File used for superposition: "+filename)

        moved_residues = ModernaStructure('file', DATA_PATH+'base_pairs/'+filename).moderna_residues
        moved, fixed, to_move = self.create_atoms_lists(moved_residues)

        self.rmsd = ModernaSuperimposer(fixed, moved, to_move).rmsd

        log.write_message("Moved atoms: "+str(moved))
        log.write_message("Fixed atoms: "+str(fixed))
        log.write_message("RMSD: "+str(self.rmsd))

        if self.rmsd <= self.cutoff:
            log.write_message("\nRMSD: " + str(self.rmsd) + "\n")
            self.assign_residue_numbers(moved_residues['1'], moved_residues['2'])
        else:
            log.write_message("\nRMSD bigger than offset " + str(self.rmsd) + ">" + str(self.cutoff) + " - starting backbone builder\n")
            self.run_backbone_builder()        
Example #13
0
class ResidueEditor:
    """
    Deals with residue objects.
    Supplements RNAResidue object with functions to
    - exchange bases
    - add modifications
    - remove modifications
    - rotat chi angle
    The type of each ModernaResidue is automatically recognized, and has a couple
    of long and short names as attributes:
    - long_abbrev
    - short_abbrev (one letter abbreviation)
    - original_base
    - full_name
    - modified (True/False)
    """
    parse = PDBParser()
    superimpose = ModernaSuperimposer()

    def add_single_fragment(self, resi, rule):
        """
        Adds a fragment to a residue.

        Arguments:
        - an adding rule dict (prepared by get_modification_rules())
        """
        try:
            fragment = self.parse.get_structure(
                'fragment', MODIFICATION_FRAGMENTS_PATH +
                rule['fragment_file_name'])[0]['A'][('H_UNK', 1, ' ')]
        except IOError:
            raise ModernaResidueError('File does not exist: %s' %
                                      MODIFICATION_FRAGMENTS_PATH)

        self.superimpose.get_atoms([resi], rule['fixed_link_atoms'], 'fixed')
        self.superimpose.get_atoms([fragment], rule['moved_link_atoms'],
                                   'moved')
        self.superimpose.moved_atoms = fragment.child_list
        self.superimpose.superimpose()

        if rule['remove']:
            delete_atoms = rule['remove'].split(',') + rule['moved_link_atoms']
        else:
            delete_atoms = rule['moved_link_atoms']
        try:
            for atom_name in delete_atoms:
                resi.detach_child(atom_name)
            for atom in fragment:
                resi.add(atom)
        except ModernaResidueError as e:
            raise e
        except:
            raise ModernaResidueError(
                'Residue %s: could not remove unnecessary and add proper atoms'
                % resi.identifier)
            #TODO: remove except:

    def mutate_unknown_residue(self, resi):
        """
        Makes a Cytosine out of unknown residue (X, .) on ribose and N* (N9,N1) atoms.
        When ribose and N* are nor present raises an error.
        C can be then changed into any modification / standard residues.
        """
        for x in RIBOSE:
            if not resi.has_id(x):
                raise ModernaResidueError(
                    'Residue %s: cannot mutate unknown residue. Missing ribose atom %s'
                    % (resi.identifier, x))
        if not resi.has_id('N9') and not resi.has_id('N1'):
            raise ModernaResidueError(
                'Residue %s: cannot mutate unknown residue. Missing ribose atom N* (N1 or N9)'
                % (resi.identifier))

        try:
            fragment = self.parse.get_structure(
                'fragment',
                MODIFICATION_FRAGMENTS_PATH + 'ribose_C.pdb')[0]['A'][(' ', 1,
                                                                       ' ')]
        except IOError:
            raise ModernaResidueError('File does not exist: %s' %
                                      MODIFICATION_FRAGMENTS_PATH +
                                      'ribose_C.pdb')

        self.superimpose.get_atoms([resi], ["O4'", "C1'", "C2'"], 'fixed')
        self.superimpose.fixed.append(resi["N*"])
        self.superimpose.get_atoms([fragment], ["O4'", "C1'", "C2'", 'N1'],
                                   'moved')
        self.superimpose.moved_atoms = fragment.child_list
        self.superimpose.superimpose()

        for x in list(resi):  # must copy items before deleting.
            if x.name not in BACKBONE_RIBOSE_ATOMS:
                resi.detach_child(x.name)

        for x in fragment:
            if x.name not in ["O4'", "C1'", "C2'"]:
                resi.add(x)

        resi.change_name('C')
        log.write_message(
            'Residue %s: patched to construct base atoms from scratch.' %
            (resi.identifier))

    def set_bfactor(self, resi, b_value):
        """Sets the same b factor for all atoms in the residue"""
        for atom in resi:
            atom.set_bfactor(b_value)
Example #14
0
class ModernaFragment(object):
    """
    A piece of structure that can be attached to a model.
    """
    def __init__(self, struc, new_sequence=None, \
                keep=keep_nothing, strict=True):
        self.struc = struc
        self.new_sequence = new_sequence
        self.sup = ModernaSuperimposer()
        self.rmsd = None
        self._keep_func = keep
        if strict: 
            self.check()
        self._prepared_anchors = []
        
    @property
    def anchor_residues(self):
        """Return all anchor residues."""
        return []

    @property
    def nonanchor_residues(self): 
        """Returns all non-anchor residues."""
        return list(self.struc)

    def __str__(self):
        fr_str = '%s (length %i)\n' % (self.__class__.__name__, len(self.struc))
        fr_str += '  anchor residues: %s\n' % str(self.anchor_residues)
        fr_str += '  sequence       : %s\n' % str(self.struc.get_sequence())
        fr_str += '  sec structure  : %s\n' % str(self.struc.get_secstruc())
        return fr_str
 
    def check(self):
        """Checks if the sequence given is OK."""
        if not is_chain_continuous(self.struc):
            raise ModernaFragmentError('Cannot create a ModernaFragment \
instance. The backbone is not continuous')
        if '.' in self.struc.get_sequence().seq_with_modifications: 
            raise ModernaFragmentError('Cannot create a ModernaFragment. \
Unknown residue is present in the chain.')
 
    def _superimpose_anchors(self, anchors, atoms):
        """Helper function that allows to select some anchors."""
        at_model = []
        at_frag = []
        for anchor in anchors:
            at_model += anchor.fixed_superposition_atoms
            at_frag += anchor.mobile_superposition_atoms
        self.sup.fixed = at_model
        self.sup.moved = at_frag
        self.sup.moved_atoms = atoms
        rmsd = self.sup.superimpose()
        return rmsd
                
    def superimpose(self): 
        """Superimpose the fragment using all anchor residues. Returns RMSD."""
        atoms = self.struc.get_all_atoms()
        self.rmsd = self._superimpose_anchors(self.anchor_residues, atoms)
        return self.rmsd
        
    def _get_resis_to_apply_seq(self):
        """Returns residues whose sequence should change."""
        return self.nonanchor_residues

    def apply_seq(self):
        """Changes seq of fr residues between anchors (NOT anchors)"""
        if self.new_sequence:
            resis = self._get_resis_to_apply_seq()
            if len(resis) != len(self.new_sequence):  
                raise ModernaFragmentError('Length of sequence (%i) does not \
match number of fragment residues to change (%i).'\
                %(len(self.new_sequence), len(resis))) 
            for resi, letter in zip(resis, self.new_sequence):
                modify_residue(resi, letter.long_abbrev)

    def get_resi_to_remove(self, struc):
        """
        returns residue ids from struct to be replaced 
        by the fragment plus anchors.
        """
        return []

    def fix_backbone(self): 
        """Repairs backbone breaks at all anchors."""
        pass

    def get_original_anchors(self):
        """Returns a list of unmodified anchors from fragment."""
        return [anchor.mobile_resi for anchor in self.anchor_residues]

    def prepare_anchor_residues(self):
        """
        takes care about right atoms in anchor, 
        applys model sequence for anchors, 
        does NOT change anchor numeration
        """
        self._prepared_anchors = []
        self.superimpose()
        for anchor in self.anchor_residues:
            self._prepared_anchors.append(anchor.get_anchored_residue())
      
    def _get_resis_to_renumber(self):
        """Returns a list of residues to be renumbered."""
        return self._prepared_anchors + self.nonanchor_residues

    def _get_numbers_to_renumber(self, struc):
        """Returns a list of kept ids or Nones for renumbering."""
        new_ids = [a.identifier for a in self._prepared_anchors]
        new_ids += [None] * len(self.nonanchor_residues)
        return new_ids

    def renumber(self, struc=None):
        """
        changes anchor and all numeration of all residues, 
        takes care about numbers that should be kept from model.
        """
        new_resi = self._get_resis_to_renumber()
        new_ids = self._get_numbers_to_renumber(struc)
        new_ids = self._keep_func(self, new_ids, struc)
        # remove all residues
        for resi in list(self.struc):
            self.struc.remove_residue(resi.identifier)
        # renumerate
        renumerator = Renumerator(new_resi, new_ids)
        new_ids = renumerator.get_identifiers_list()
        for new_id, resi in zip(new_ids, new_resi):
            self.struc.add_residue(resi, number=new_id)
        
    def has_clashes(self, resi_list):
        """
        Finds out whether the fragment clashes with the given list of residues.
        """
        clashrec = ClashRecognizer()
        clashes = clashrec.find_clashes_in_residues(self.nonanchor_residues+resi_list)
        return clashes
Example #15
0
class RnaModel(ModernaStructure):
    """
    Collects preliminary structure model of RNA molecule.

    Arguments:
    * template - structure of template as a Template object (must corespond with template sequence in alignment)
    * alignment - template-target sequence alignment as a Alignment object
    """
    def __init__(self,
                 template=None,
                 alignment=None,
                 model_chain_name='A',
                 data_type=None,
                 data=None,
                 seq=None):
        ModernaStructure.__init__(self,
                                  data_type=data_type,
                                  data=data,
                                  chain_name=model_chain_name,
                                  seq=seq)
        #TODO: order of arguments is inconsistent.
        #TODO: template + alignment should be obligatory
        #TODO: replace alignment by recipe
        #TODO: rename to SingleTemplateModeling and/or refactor classes for tasks out.
        self.template = template
        if template:
            self.template.set_template_numeration()
        if alignment:
            self.recipe = RecipeMaker(alignment).recipe
        self.alignment = alignment
        self.s = ModernaSuperimposer()

##################################### COPYING #################################

    def copy_residue(self, residue, number_in_model=None, strict=True):
        """
        Copies a residue to a model in the given position.

        Arguments:
        - residue to copy (as a RNAResidue or PDB.Residue.Residue instance)
        - position in model (by default position in previous structure)  
        """
        temp_res = RNAResidue(residue)
        redit = ResidueEditor()
        redit.set_bfactor(temp_res, B_FACTOR_COPY)
        num = number_in_model or temp_res.identifier
        self.add_residue(temp_res, num, strict=strict)
        log.write_message(
            'Residue %s: residue copied from template residue %s to model.' %
            (num, temp_res.identifier))

    def copy_list_of_residues(self,
                              residues,
                              numbers_in_model=None,
                              strict=True):
        """
        Copies list of given residues to a model on given positions (also a list)

        Arguments:
        - list of residues
        - list of positions
        """
        if not numbers_in_model:
            numbers_in_model = [resi.identifier for resi in residues]

        if len(residues) != len(numbers_in_model):
            raise RnaModelError(
                'Number of given residues is different than number of given positions.'
            )

        for resi, number in zip(residues, numbers_in_model):
            self.copy_residue(resi, number, strict=strict)

    def copy_all_residues(self, strict=True, modifications=True):
        """
        Copies all residues identical (according alignment) in both template and target
        """
        if self.alignment and self.template:
            for ap in self.recipe.copy:
                res = self.template.template_residues[str(
                    ap.template_position)]
                if modifications or not res.modified:
                    self.copy_residue(res, strict=strict)
        else:
            raise RnaModelError('There is no template or/and alignmnt')

    def copy_residue_backbone(self,
                              residue,
                              number_in_model=None,
                              strict=True):
        """
        """
        temp_res = RNAResidue(residue)
        num = number_in_model or str(
            temp_res.id[1]).strip() + temp_res.id[2].strip()
        make_backbone_only_residue(temp_res)
        self.add_residue(temp_res, num, strict=strict)
        log.write_message(
            'Residue %s: residues backbone atoms copied from template to model.'
            % num)

    def copy_all_residue_backbone(self, strict=True):
        """
        """
        if self.alignment and self.template:
            for ap in self.recipe.copy_backbone:
                res = self.template.template_residues[str(
                    ap.template_position)]
                self.copy_residue_backbone(res, strict=strict)
        else:
            raise RnaModelError('There is no template or/and alignmnt')

################################### MODIFICATIONS (what is left) ###############

    def remove_one_modification_copy(self, residue, number_in_model):
        """
        """
        temp_resi = RNAResidue(residue)
        num = number_in_model or temp_resi.number
        remove_modification(temp_resi)
        self.add_residue(temp_resi, str(num))
        log.write_message('Residue %s: modification removed (%s ---> %s).' %
                          (num, residue.long_abbrev, temp_resi.long_abbrev))

    def remove_all_modifications_copy(self):
        """
        Removes all unnecessary modifications from model acordong given alignment.
        Copies all this residues without modification into model.
        """
        if self.alignment and self.template:
            for ap in self.recipe.remove_modifications:
                res = self.template.template_residues[str(
                    ap.template_position)]
                temp_resi = RNAResidue(res)
                remove_modification(temp_resi)

                if temp_resi != ap.target_letter:
                    exchange_base(temp_resi, ap.target_letter.original_base)
                self.add_residue(temp_resi)
                log.write_message(
                    'Residue %s: modification removed (%s ---> %s).' %
                    (temp_resi.identifier, res.long_abbrev,
                     temp_resi.long_abbrev))
        else:
            raise RnaModelError('There is no template or/and alignmnt')

    def remove_all_modifications(self):
        """Removes all modifications from the model."""
        for resi in self:
            if resi.modified:
                remove_modification(resi)

    def add_one_modification_copy(self, residue, modification_long_abbrev,
                                  number_in_model):
        """
        """
        temp_res = RNAResidue(residue)
        num = number_in_model or temp_res.identifier
        add_modification(temp_res, modification_long_abbrev)
        self.add_residue(temp_res, num, False)
        log.write_message('Residue %s: modification added (%s ---> %s).' %
                          (num, residue.long_abbrev, modification_long_abbrev))

    def add_all_modifications_copy(self):
        """
        """
        if self.alignment and self.template:
            for ap in self.recipe.add_modifications:
                temp_resi = RNAResidue(self.template.template_residues[str(
                    ap.template_position)])
                old_name = temp_resi.long_abbrev
                add_modification(temp_resi, ap.target_letter.long_abbrev)
                self.add_residue(temp_resi)
                log.write_message(
                    'Residue %s: modification added (%s ---> %s).' %
                    (temp_resi.identifier, old_name, temp_resi.long_abbrev))
        else:
            raise RnaModelError('There is no template or/and alignmnt')

    def exchange_list_of_bases(self,
                               residues,
                               new_names,
                               numbers_in_model=None):
        """
        Exchanges bases in given residues list.

        Arguments:
        - list of residues
        - list with new names for residues
        - list with numbers that indicates new positions for residues in a model (by default old residues positions)
        """
        if not numbers_in_model:
            numbers_in_model = [resi.identifier for resi in residues]

        if len(residues) != len(new_names) or len(new_names) != len(
                numbers_in_model):
            raise RnaModelError(
                'Number of given residues is different than number of given positions.'
            )

        for resi, num, name in zip(residues, numbers_in_model, new_names):
            self.copy_residue(resi, num)
            exchange_base(self[num], name)

    def exchange_all_bases(self):
        """
        Exchanges all bases according to given alignment.
        """
        if self.alignment and self.template:
            for ap in self.recipe.exchange:
                res = self.template.template_residues[str(
                    ap.template_position)]
                name = ap.target_letter.original_base

                temp_resi = RNAResidue(
                    res)  #TODO: check whether defensive copy is neccessary
                exchange_base(temp_resi, name)
                self.add_residue(temp_resi, res.identifier)

        else:
            raise RnaModelError('There is no template or/and alignmnt')

################################### INSERTING ##################################

    def find_fragment_candidates(
            self,
            res5,
            res3,
            sequence,
            candidates_number=NUMBER_OF_FRAGMENT_CANDIDATES,
            lir_path=PATH_TO_LIR_STRUCTURES,
            secstruc=None):
        # en: candidate_number
        """
        Looks for fragment candidates for missing fragment in a structure.
        Returns list of fragment candidates.

        Arguments:
        - anchor residue on 5' end as a ModernaResidue instance (residue preceding missing fragment)
        - anchor residue on 3' end as a ModernaResidue instance (residue fallowing missing fragment)
        - missing sequence
        """
        fragment_finder = FragmentFinder(res5, res3, sequence, self,
                                         candidates_number, lir_path, secstruc)
        return fragment_finder.find_fragment_candidates(
        )  # FragmentCandidates instance

    def write_fragment_candidates(self,
                                  candidates,
                                  directory_name='fragment_candidates',
                                  with_anchor_residues=False,
                                  with_model=False,
                                  log=True):
        """
        Writes all possible fragments to one pdb file.
        """
        candidates.write_fragment_candidates(directory_name,
                                             with_anchor_residues, with_model)

    def insert_fragment(self, fragment):
        """Inserts fragment into model."""
        finsert = FragmentInserter()
        finsert.insert_fragment(fragment, self)

#############################################################################################

    def insert_best_fragment(self,
                             start,
                             stop,
                             sequence,
                             candidates_number=NUMBER_OF_FRAGMENT_CANDIDATES):
        """
        Makes a LIR search to fill a gap at a given position.
        looks for possible candidates, checks the first ~20 for clashes,
        takes the best, removes anchor residue and inserts it in the model.        

        Arguments:
        - start position as a string (residue identifier)e.g. '3' - this is an identifier of the preceding anchor residue
        - stop position as above - this is an identifier of the fallowing anchor residue
        - fragment sequence as a Sequence object
        - number of fragment candidates.
        """
        log.write_message(
            '\nSearching fragment between residue %s and residue %s.' %
            (str(start), str(stop)))
        fragment_finder = FragmentFinder(self[start], self[stop], sequence,
                                         self, candidates_number)
        best_fragment = fragment_finder.find_fragment()
        log.write_message('\nBest fragment:\n%s' % str(best_fragment))
        self.insert_fragment(best_fragment)

    def insert_lir_candidate(self, candidate):
        """
        Insert the given LirHit instance into model.    
        """
        fragment = candidate.fragment_instance
        self.insert_fragment(fragment)

    def insert_all_fragments(self):
        """
        Deals with indels in the model.
        """
        if self.alignment and self.template:  # KR: is template necessary?
            for fragment in self.recipe.add_fragment:
                seq = []
                for ap in fragment:
                    if ap.target_letter: seq.append(ap.target_letter)
                start = self.template.template_residues[str(
                    fragment[0].template_position)].identifier
                stop = self.template.template_residues[str(
                    fragment[-1].template_position)].identifier
                seq = seq[1:-1]
                self.insert_best_fragment(start, stop, Sequence(seq))
        else:
            raise RnaModelError('There is no template or/and alignmnt')

    def _elongate_strand(self, all_resis):
        """
        Enables to elongate single stranded helix.
        Used while applying missing 5' and 3' ends 
        when missing part is longer than provided SINGLE_STRAND fragment.
        Arguments:
        - list of curently existing residues from which the fragment will be created
        """
        front = [RNAResidue(resi) for resi in all_resis]
        back = [RNAResidue(resi) for resi in all_resis]
        x = 1
        for resi in front:
            resi.change_number(str(x))
            x += 1
        for resi in back:
            resi.change_number(str(x))
            x += 1

        self.s.get_atoms([front[-2], front[-1]], BACKBONE_ATOMS, 'fixed')
        self.s.get_atoms([back[0], back[1]], BACKBONE_ATOMS, 'moved')
        self.s.moved_atoms = [at for resi in back for at in resi]
        self.s.superimpose()
        return front + back[2:]

    def add_missing_5p(self):
        """ """
        if self.alignment and len(self.recipe.add_fragment_5p) > 0:
            anchor3 = [r for r in self][0]
            ap_list = self.recipe.add_fragment_5p[0]
            seq = Sequence(''.join(
                [ap.target_letter.short_abbrev for ap in ap_list]))
            all_resis = [r for r in Template(SINGLE_STRAND, 'file', 'A')]
            while len(all_resis) - 1 < len(ap_list):
                all_resis = self._elongate_strand(all_resis)
            frag_resis = all_resis[:len(ap_list) + 1]
            struc = ModernaStructure('residues', frag_resis)
            frag = ModernaFragment3(struc,
                                    anchor3=anchor3,
                                    new_sequence=seq,
                                    keep=keep_last)
            self.insert_fragment(frag)

    def add_missing_3p(self):
        """ """
        if self.alignment and len(self.recipe.add_fragment_3p) > 0:
            anchor5 = [r for r in self][-1]
            ap_list = self.recipe.add_fragment_3p[0]
            seq = Sequence(''.join(
                [ap.target_letter.short_abbrev for ap in ap_list]))
            all_resis = [r for r in Template(SINGLE_STRAND, 'file', 'A')]
            while len(all_resis) - 1 < len(ap_list):
                all_resis = self._elongate_strand(all_resis)
            frag_resis = all_resis[:len(ap_list) + 1]
            struc = ModernaStructure('residues', frag_resis)
            frag = ModernaFragment5(struc,
                                    anchor5=anchor5,
                                    new_sequence=seq,
                                    keep=keep_first)
            self.insert_fragment(frag)

########################### SECONDARY STRUCTURE MODELING ##########################

    def extend_helix(self, start5, start3, helix_seq):
        """ 
        e.g. helix_seq = 'AAAA_UUUU'
        """
        helix_fr = ModernaFragmentHelix(anchor5=start5,
                                        anchor3=start3,
                                        new_sequence=helix_seq,
                                        model=self)
        self.insert_fragment(helix_fr)

    def add_second_strand(self):
        """
        """
        pass

################################### REFINING ##################################

    def refine_model(self):
        """Sets B-factor and occupancy of each atom."""
        for resi in self:
            for at in resi:
                at.set_bfactor(0.0)
                at.set_occupancy(1.0)
        self.sort_residues()

################################### CREATE MODEL ##################################

    def apply_alignment(self):
        """
        Apply all operations from alignment
        that affect single residues (no indels)
        """
        if self.alignment and self.template:
            self.copy_all_residues()
            self.copy_all_residue_backbone()
            self.exchange_all_bases()
            self.remove_all_modifications_copy()
            self.add_all_modifications_copy()
        else:
            raise RnaModelError('There is no template or/and alignment')

    def create_model(self):
        """Automatic modeling"""
        if self.alignment and self.template:
            self.template.set_template_numeration()
            self.apply_alignment()
            self.insert_all_fragments()
            self.add_missing_5p()
            self.add_missing_3p()
            self.fix_backbone()
        else:
            raise RnaModelError('There is no template or/and alignment')