def _get_pam5_strand_atoms_in_bond_direction(self, inputAtomList = ()): """ Return a list of sugar atoms in a fixed direction -- from 5' to 3' @param inputAtomList: An optional argument. If its not provided, this method will return a list of all atoms within the strand, in the strand's bond direction. Otherwise, it will just return the list <inputAtomList> whose atoms are ordered in the strand's bond direction. @type inputAtomList: list (with default value as an empty tuple) @note: this is a stub and we can modify it so that it can accept other direction i.e. 3' to 5' , as an argument. [I think at most one atom at each end can be a bondpoint, so we could revise this code to remove them before returning. bruce 080205] piotr 080411: This is a helper method for 'get_strand_atoms_in_bond_direction'. It is called for PAM5 models and should be replaced by a properly modified caller method. Only bondpoints ('X') and sugar atoms ('Ss3', Ss5') are preserved. @warning: for a ring, this uses an arbitrary start atom in self (so it is not yet useful in that case). ### VERIFY @note: this would return all atoms from an entire strand (chain or ring) even if it spanned multiple chunks. @TODO: THIS method is copied over from chunk class. with a minor modification To be revised. See self.getStrandSequence() for a comment. """ startAtom = None atomList = [] rawAtomList = [] if inputAtomList: rawAtomList = inputAtomList else: for c in self.members: if isinstance(c, DnaStrandChunk): rawAtomList.extend(c.atoms.itervalues()) #Choose startAtom randomly (make sure that it's a Sugar atom # and not a bondpoint) for atm in rawAtomList: if atm.element.symbol == 'Ss3' or \ atm.element.symbol == 'Ss5': startAtom = atm break if startAtom is None: print_compact_stack("bug: no Sugar atom (Ss3 or Ss5) found: " ) return [] #Build one list in each direction, detecting a ring too #ringQ decides whether the first returned list forms a ring. #This needs a better name in bond_chains.grow_directional_bond_chain ringQ = False atomList_direction_1 = [] atomList_direction_2 = [] b = None bond_direction = 0 for bnd in startAtom.directional_bonds(): if not bnd.is_open_bond(): # (this assumes strand length > 1) #Determine the bond_direction from the 'startAtom' direction = bnd.bond_direction_from(startAtom) if direction in (1, -1): b = bnd bond_direction = direction break if b is None or bond_direction == 0: return [] #Find out the list of new atoms and bonds in the direction #from bond b towards 'startAtom' . This can either be 3' to 5' direction #(i.e. bond_direction = -1 OR the reverse direction # Later, we will check the bond direction and do appropriate things. #(things that will decide which list (atomList_direction_1 or #atomList_direction_2) should be prepended in atomList so that it has #atoms ordered from 5' to 3' end. # 'atomList_direction_1' does NOT include 'startAtom'. # See a detailed explanation below on how atomList_direction_a will be # used, based on bond_direction ringQ, listb, atomList_direction_1 = grow_directional_bond_chain(b, startAtom) del listb # don't need list of bonds if ringQ: # The 'ringQ' returns True So its it's a 'ring'. #First add 'startAtom' (as its not included in atomList_direction_1) atomList.append(startAtom) #extend atomList with remaining atoms atomList.extend(atomList_direction_1) else: #Its not a ring. Now we need to make sure to include atoms in the #direction_2 (if any) from the 'startAtom' . i.e. we need to grow #the directional bond chain in the opposite direction. other_atom = b.other(startAtom) if not other_atom.is_singlet(): ringQ, listb, atomList_direction_2 = grow_directional_bond_chain(b, other_atom) assert not ringQ #bruce 080205 del listb #See a detailed explanation below on how #atomList_direction_2 will be used based on 'bond_direction' atomList_direction_2.insert(0, other_atom) atomList = [] # not needed but just to be on a safer side. if bond_direction == 1: # 'bond_direction' is the direction *away from* startAtom and # along the bond 'b' declared above. . # This can be represented by the following sketch -- # (3'end) <--1 <-- 2 <-- 3 <-- 4 <-- (5' end) # Let startAtom be '2' and bond 'b' be directional bond between # 1 and 2. In this case, the direction of bond *away* from # '2' and along 2 = bond direction of bond 'b' and thus # atoms traversed along bond_direction = 1 lead us to 3' end. # Now, 'atomList_direction_1' is computed by 'growing' (expanding) # a bond chain in the direction that goes from bond b # *towards* startAtom. That is, in this case it is the opposite # direction of one specified by 'bond_direction'. The last atom # in atomList_direction_1 is the (5' end) atom. # Note that atomList_direction_1 doesn't include 'startAtom' # Therefore, to get atomList ordered from 5'to 3' end we must #reverse atomList_direction_1 , then append startAtom to the #atomList (as its not included in atomList_direction_1) and then #extend atoms from atomList_direction_2. #What is atomList_direction_2 ? It is the list of atoms #obtained by growing bond chain from bond b, in the direction of #atom 1 (atom 1 is the 'other atom' of the bond) . In this case #these are the atoms in the direction same as 'bond_direction' #starting from atom 1. Thus the atoms in the list are already #arranged from 5' to 3' end. (also note that after computing #the atomList_direction_2, we also prepend 'atom 1' as the #first atom in that list. See the code above that does that. atomList_direction_1.reverse() atomList.extend(atomList_direction_1) atomList.append(startAtom) atomList.extend(atomList_direction_2) else: #See a detailed explanation above. #Here, bond_direction == -1. # This can be represented by the following sketch -- # (5'end) --> 1 --> 2 --> 3 --> 4 --> (3' end) #bond b is the bond betweern atoms 1 and 2. #startAtom remains the same ..i.e. atom 2. #As you can notice from the sketch, the bond_direction is #direction *away* from 2, along bond b and it leads us to # 5' end. #based on how atomList_direction_2 (explained earlier), it now #includes atoms begining at 1 and ending at 5' end. So #we must reverse atomList_direction_2 now to arrange them #from 5' to 3' end. atomList_direction_2.reverse() atomList.extend(atomList_direction_2) atomList.append(startAtom) atomList.extend(atomList_direction_1) # Note: the bondpoint atoms are NOT included. # ONLY consecutive sugar stoms are returned. # piotr 080411 # extract only sugar atoms or bondpoints # the bondpoints are extracted to make the method compatible # with get_strand_atoms_in_bond_direction def filter_sugars(atm): return atm.element.symbol == 'Ss3' or \ atm.element.symbol == 'Ss5' or \ atm.element.symbol == 'X' atomList = filter(filter_sugars, atomList) return atomList
def _get_pam5_strand_atoms_in_bond_direction(self, inputAtomList=()): """ Return a list of sugar atoms in a fixed direction -- from 5' to 3' @param inputAtomList: An optional argument. If its not provided, this method will return a list of all atoms within the strand, in the strand's bond direction. Otherwise, it will just return the list <inputAtomList> whose atoms are ordered in the strand's bond direction. @type inputAtomList: list (with default value as an empty tuple) @note: this is a stub and we can modify it so that it can accept other direction i.e. 3' to 5' , as an argument. [I think at most one atom at each end can be a bondpoint, so we could revise this code to remove them before returning. bruce 080205] piotr 080411: This is a helper method for 'get_strand_atoms_in_bond_direction'. It is called for PAM5 models and should be replaced by a properly modified caller method. Only bondpoints ('X') and sugar atoms ('Ss3', Ss5') are preserved. @warning: for a ring, this uses an arbitrary start atom in self (so it is not yet useful in that case). ### VERIFY @note: this would return all atoms from an entire strand (chain or ring) even if it spanned multiple chunks. @TODO: THIS method is copied over from chunk class. with a minor modification To be revised. See self.getStrandSequence() for a comment. """ startAtom = None atomList = [] rawAtomList = [] if inputAtomList: rawAtomList = inputAtomList else: for c in self.members: if isinstance(c, DnaStrandChunk): rawAtomList.extend(c.atoms.itervalues()) #Choose startAtom randomly (make sure that it's a Sugar atom # and not a bondpoint) for atm in rawAtomList: if atm.element.symbol == 'Ss3' or \ atm.element.symbol == 'Ss5': startAtom = atm break if startAtom is None: print_compact_stack("bug: no Sugar atom (Ss3 or Ss5) found: ") return [] #Build one list in each direction, detecting a ring too #ringQ decides whether the first returned list forms a ring. #This needs a better name in bond_chains.grow_directional_bond_chain ringQ = False atomList_direction_1 = [] atomList_direction_2 = [] b = None bond_direction = 0 for bnd in startAtom.directional_bonds(): if not bnd.is_open_bond(): # (this assumes strand length > 1) #Determine the bond_direction from the 'startAtom' direction = bnd.bond_direction_from(startAtom) if direction in (1, -1): b = bnd bond_direction = direction break if b is None or bond_direction == 0: return [] #Find out the list of new atoms and bonds in the direction #from bond b towards 'startAtom' . This can either be 3' to 5' direction #(i.e. bond_direction = -1 OR the reverse direction # Later, we will check the bond direction and do appropriate things. #(things that will decide which list (atomList_direction_1 or #atomList_direction_2) should be prepended in atomList so that it has #atoms ordered from 5' to 3' end. # 'atomList_direction_1' does NOT include 'startAtom'. # See a detailed explanation below on how atomList_direction_a will be # used, based on bond_direction ringQ, listb, atomList_direction_1 = grow_directional_bond_chain( b, startAtom) del listb # don't need list of bonds if ringQ: # The 'ringQ' returns True So its it's a 'ring'. #First add 'startAtom' (as its not included in atomList_direction_1) atomList.append(startAtom) #extend atomList with remaining atoms atomList.extend(atomList_direction_1) else: #Its not a ring. Now we need to make sure to include atoms in the #direction_2 (if any) from the 'startAtom' . i.e. we need to grow #the directional bond chain in the opposite direction. other_atom = b.other(startAtom) if not other_atom.is_singlet(): ringQ, listb, atomList_direction_2 = grow_directional_bond_chain( b, other_atom) assert not ringQ #bruce 080205 del listb #See a detailed explanation below on how #atomList_direction_2 will be used based on 'bond_direction' atomList_direction_2.insert(0, other_atom) atomList = [] # not needed but just to be on a safer side. if bond_direction == 1: # 'bond_direction' is the direction *away from* startAtom and # along the bond 'b' declared above. . # This can be represented by the following sketch -- # (3'end) <--1 <-- 2 <-- 3 <-- 4 <-- (5' end) # Let startAtom be '2' and bond 'b' be directional bond between # 1 and 2. In this case, the direction of bond *away* from # '2' and along 2 = bond direction of bond 'b' and thus # atoms traversed along bond_direction = 1 lead us to 3' end. # Now, 'atomList_direction_1' is computed by 'growing' (expanding) # a bond chain in the direction that goes from bond b # *towards* startAtom. That is, in this case it is the opposite # direction of one specified by 'bond_direction'. The last atom # in atomList_direction_1 is the (5' end) atom. # Note that atomList_direction_1 doesn't include 'startAtom' # Therefore, to get atomList ordered from 5'to 3' end we must #reverse atomList_direction_1 , then append startAtom to the #atomList (as its not included in atomList_direction_1) and then #extend atoms from atomList_direction_2. #What is atomList_direction_2 ? It is the list of atoms #obtained by growing bond chain from bond b, in the direction of #atom 1 (atom 1 is the 'other atom' of the bond) . In this case #these are the atoms in the direction same as 'bond_direction' #starting from atom 1. Thus the atoms in the list are already #arranged from 5' to 3' end. (also note that after computing #the atomList_direction_2, we also prepend 'atom 1' as the #first atom in that list. See the code above that does that. atomList_direction_1.reverse() atomList.extend(atomList_direction_1) atomList.append(startAtom) atomList.extend(atomList_direction_2) else: #See a detailed explanation above. #Here, bond_direction == -1. # This can be represented by the following sketch -- # (5'end) --> 1 --> 2 --> 3 --> 4 --> (3' end) #bond b is the bond betweern atoms 1 and 2. #startAtom remains the same ..i.e. atom 2. #As you can notice from the sketch, the bond_direction is #direction *away* from 2, along bond b and it leads us to # 5' end. #based on how atomList_direction_2 (explained earlier), it now #includes atoms begining at 1 and ending at 5' end. So #we must reverse atomList_direction_2 now to arrange them #from 5' to 3' end. atomList_direction_2.reverse() atomList.extend(atomList_direction_2) atomList.append(startAtom) atomList.extend(atomList_direction_1) # Note: the bondpoint atoms are NOT included. # ONLY consecutive sugar stoms are returned. # piotr 080411 # extract only sugar atoms or bondpoints # the bondpoints are extracted to make the method compatible # with get_strand_atoms_in_bond_direction def filter_sugars(atm): return atm.element.symbol == 'Ss3' or \ atm.element.symbol == 'Ss5' or \ atm.element.symbol == 'X' atomList = filter(filter_sugars, atomList) return atomList