コード例 #1
0
def add_basepair_handles_to_atoms(atoms): #bruce 080515
    """
    """
    goodcount, badcount = 0, 0
    for atom in atoms:
        atom.unpick()
        if atom.element is Gv5 and len(atom.strand_neighbors()) == 2:

            goodcount += 1

            # Figure out the position from the Gv5 and its presumed-to-be Ss5
            # neighbors. [Fixed per Eric D spec, bruce 080516]
            sn = atom.strand_neighbors()
            ss_midpoint = average_value([a.posn() for a in sn])
            towards_Gv = norm(atom.posn() - ss_midpoint)
            newpos = ss_midpoint + \
                     BASEPAIR_HANDLE_DISTANCE_FROM_SS_MIDPOINT * towards_Gv
##            if 0: # stub for computing new position
##                oldposns = [a.posn() for a in ([atom] + sn)]
##                newpos = average_value(oldposns)

            Atom = atom.molecule.assy.Atom # (avoid model.chem import cycle)

            newatom = Atom( 'Ah5', newpos, atom.molecule ) # PAM5-Axis-handle
            bond_atoms_faster( newatom, atom, V_SINGLE)
                # note: no bondpoints need creation or removal

            newatom.pick()
            pass
        else:
            badcount += 1
        continue

    return goodcount, badcount
コード例 #2
0
def add_basepair_handles_to_atoms(atoms):  #bruce 080515
    """
    """
    goodcount, badcount = 0, 0
    for atom in atoms:
        atom.unpick()
        if atom.element is Gv5 and len(atom.strand_neighbors()) == 2:

            goodcount += 1

            # Figure out the position from the Gv5 and its presumed-to-be Ss5
            # neighbors. [Fixed per Eric D spec, bruce 080516]
            sn = atom.strand_neighbors()
            ss_midpoint = average_value([a.posn() for a in sn])
            towards_Gv = norm(atom.posn() - ss_midpoint)
            newpos = ss_midpoint + \
                     BASEPAIR_HANDLE_DISTANCE_FROM_SS_MIDPOINT * towards_Gv
            ##            if 0: # stub for computing new position
            ##                oldposns = [a.posn() for a in ([atom] + sn)]
            ##                newpos = average_value(oldposns)

            Atom = atom.molecule.assy.Atom  # (avoid model.chem import cycle)

            newatom = Atom('Ah5', newpos, atom.molecule)  # PAM5-Axis-handle
            bond_atoms_faster(newatom, atom, V_SINGLE)
            # note: no bondpoints need creation or removal

            newatom.pick()
            pass
        else:
            badcount += 1
        continue

    return goodcount, badcount
コード例 #3
0
 def posn(
     self
 ):  # stub - average posn of Ss neighbors (plus offset in case only one!)
     print "should use Pl_pos_from_neighbor_PAM3plus5_data for %r" % self  #####
     # note: average_value seems to work here
     res = average_value([n.posn() for n in self.neighbors()], V(0, 0, 0))
     return res + V(0, 2, 0)  # offset (kluge, wrong)
コード例 #4
0
def Gv_pos_from_neighbor_PAM3plus5_data(
        neighbors,
        remove_data_from_neighbors = False
     ):
    #bruce 080409, modified from Pl_pos_from_neighbor_PAM3plus5_data
    """
    Figure out where a new Gv atom should be located
    based on the PAM3plus5_data (related to its position)
    in its neighbor Ss3 or Ss5 atoms (whose positions are assumed correct).
    (If the neighbors are Ss5 it's because they got converted from Ss3
     recently, since this info is normally only present on Ss3 atoms.)

    Assume the neighbors are in the same basepair in a DnaLadder
    with up to date baseframe info already computed and stored.
    (See warning in Pl_pos_from_neighbor_PAM3plus5_data about atom.
     molecule.ladder perhaps being wrong or missing.)
    This up to dateness may not be checked, and out of date
    info would cause hard-to-notice bugs.

    @return: new absolute position, or None if we can't compute one (error).

    @see: related method (stores data in the other direction),
          _f_Gv_store_position_into_Ss3plus5_data

    @see: analogous function for Pl: Pl_pos_from_neighbor_PAM3plus5_data
    """
    proposed_posns = []

    for ss in neighbors:
        assert ss.element.role == 'strand'
            # (avoid bondpoints or (erroneous) non-PAM or axis atoms)
        pos = ss._f_recommend_PAM3plus5_Gv_abs_position(
                remove_data = remove_data_from_neighbors,
                make_up_position_if_necessary = True # doesn't prevent all returns of None
         )
        if pos is None:
            # see comment in Pl_pos_from_neighbor_PAM3plus5_data
            print "fyi: _f_recommend_PAM3plus5_Gv_abs_position returned None for %r" % ss
                # remove when works if routine; leave in if never seen, to notice bugs
        else:
            proposed_posns.append(pos)
        continue

    if not proposed_posns:
        # neither neighbor was able to make up a position -- error.
        # caller might have ways of handling this, but we don't...
        print "bug: Gv_pos_from_neighbor_PAM3plus5_data can't compute pos " \
              "for Gv between these neighbors:", neighbors

        return None

    if len(proposed_posns) == 1:
        # optimization
        return proposed_posns[0]

    return average_value( proposed_posns)
コード例 #5
0
def Gv_pos_from_neighbor_PAM3plus5_data(neighbors,
                                        remove_data_from_neighbors=False):
    #bruce 080409, modified from Pl_pos_from_neighbor_PAM3plus5_data
    """
    Figure out where a new Gv atom should be located
    based on the PAM3plus5_data (related to its position)
    in its neighbor Ss3 or Ss5 atoms (whose positions are assumed correct).
    (If the neighbors are Ss5 it's because they got converted from Ss3
     recently, since this info is normally only present on Ss3 atoms.)

    Assume the neighbors are in the same basepair in a DnaLadder
    with up to date baseframe info already computed and stored.
    (See warning in Pl_pos_from_neighbor_PAM3plus5_data about atom.
     molecule.ladder perhaps being wrong or missing.)
    This up to dateness may not be checked, and out of date
    info would cause hard-to-notice bugs.

    @return: new absolute position, or None if we can't compute one (error).
    
    @see: related method (stores data in the other direction),
          _f_Gv_store_position_into_Ss3plus5_data

    @see: analogous function for Pl: Pl_pos_from_neighbor_PAM3plus5_data
    """
    proposed_posns = []

    for ss in neighbors:
        assert ss.element.role == 'strand'
        # (avoid bondpoints or (erroneous) non-PAM or axis atoms)
        pos = ss._f_recommend_PAM3plus5_Gv_abs_position(
            remove_data=remove_data_from_neighbors,
            make_up_position_if_necessary=
            True  # doesn't prevent all returns of None
        )
        if pos is None:
            # see comment in Pl_pos_from_neighbor_PAM3plus5_data
            print "fyi: _f_recommend_PAM3plus5_Gv_abs_position returned None for %r" % ss
            # remove when works if routine; leave in if never seen, to notice bugs
        else:
            proposed_posns.append(pos)
        continue

    if not proposed_posns:
        # neither neighbor was able to make up a position -- error.
        # caller might have ways of handling this, but we don't...
        print "bug: Gv_pos_from_neighbor_PAM3plus5_data can't compute pos " \
              "for Gv between these neighbors:", neighbors

        return None

    if len(proposed_posns) == 1:
        # optimization
        return proposed_posns[0]

    return average_value(proposed_posns)
コード例 #6
0
 def site_position(self):
     ## return average_value( [a.posn() for a in self.parent_atoms()] )
     ##     # STUB, actually use self._function_id and _x and _y, or self._props
     self._update_props()
     if self._function_id == 1 and len(self.parent_atoms()) == 3:
         # the only supported kind so far
         parentID1, parentID2, parentID3 = self.parent_atoms()
         A = self._x
         B = self._y
         #   Multiply the vector (parentID2 - parentID1) * A
         #   Multiply the vector (parentID3 - parentID1) * B
         #   Add the above two vectors to parentID1
         pos1 = parentID1.posn()
         pos2 = parentID2.posn()
         pos3 = parentID3.posn()
         return pos1 + (pos2 - pos1) * A + (pos3 - pos1) * B
     else:
         print "bug: unsupported kind of virtual site:", self._props
         return average_value( [a.posn() for a in self.parent_atoms()] )
     pass
コード例 #7
0
 def site_position(self):
     ## return average_value( [a.posn() for a in self.parent_atoms()] )
     ##     # STUB, actually use self._function_id and _x and _y, or self._props
     self._update_props()
     if self._function_id == 1 and len(self.parent_atoms()) == 3:
         # the only supported kind so far
         parentID1, parentID2, parentID3 = self.parent_atoms()
         A = self._x
         B = self._y
         #   Multiply the vector (parentID2 - parentID1) * A
         #   Multiply the vector (parentID3 - parentID1) * B
         #   Add the above two vectors to parentID1
         pos1 = parentID1.posn()
         pos2 = parentID2.posn()
         pos3 = parentID3.posn()
         return pos1 + (pos2 - pos1) * A + (pos3 - pos1) * B
     else:
         print "bug: unsupported kind of virtual site:", self._props
         return average_value([a.posn() for a in self.parent_atoms()])
     pass
コード例 #8
0
def make_or_remove_crossover(twoPls, make=True, cmdname=None):
    """
    Make or Remove (according to make option) a crossover, given Pl5_recognizers for its two Pl atoms.
    """

    # What we are doing is recognizing one local structure and replacing it with another
    # made from the same atoms. It'd sure be easier if I could do the manipulation in an mmp file,
    # save that somewhere, and read those to generate the operation! I'd have two sets of atoms, before and after,
    # and see how bonds and atomtypes got changed.

    # In this case it's not too hard to hand-code... I guess only the Pl atoms and their bonds are affected.
    # We probably do have to look at strand directions -- hmm, probably we should require them to exist before saying it's ok!
    # Or maybe better to give history error message when the command is chosen, saying you need to set them (or repair them) first...
    # Then we have to move the new/moved Pl atoms into a good position...

    # Note: Pl.ordered_bases are ordered by bond direction, to make this easier...
    # but if we want to patch up the directions in the end, do we need to care exactly which ones were defined?
    # or only "per-Pl"? hmm... it's per-Pl for now

    assert cmdname

    for pl in twoPls:
        if pl.ordered_bases is None:  # should no longer be possible -- now checked before menu commands are offered [bruce 070604]
            ###BUG: this could have various causes, not only the one reported below! Somehow we need access to the
            # message supplied to the RecognizerError, for use here.
            ###REVIEW: Does that mean it should pass through compute methods (probably in a controlled way)
            # rather than making computed values None?
            # Or, should the value not be None, but a "frozen" examinable and reraisable version of the error exception??
            msg = "%s: Error: bond direction is locally undefined or inconsistent around %s" % (
                cmdname, pl.atom)  ###UNTESTED
            print "should no longer be possible:", msg  #bruce 070604
            env.history.message(redmsg(quote_html(msg)))
            return

    Pl1, Pl2 = twoPls
    a, b = Pl1.ordered_bases
    d, c = Pl2.ordered_bases  # note: we use d,c rather than c,d so that the atom arrangement is as shown in the diagram below.

    # Note: for either the Make or Remove operation, the geometric arrangement is initially:
    #
    # c <-- Pl2 <-- d
    #
    # a --> Pl1 --> b
    #
    # and it ends up being (where dots indicate arrowheads, to show bond direction):
    #
    # c        d
    #  .      /
    #   \    .
    #  Pl1  Pl2
    #   .    \
    #  /      .
    # a        b
    #
    # Note: Pl1 stays attached to a, and Pl2 to d. Which two opposite bonds to preserve like that
    # is an arbitrary choice -- as long as Make and Remove make the same choice about that,
    # they'll reverse each other's effects precisely (assuming the sugars were initially correct as Ss or Sj).

    # break the bonds we no longer want
    for obj1, obj2 in [(Pl1, b), (Pl2, c)]:
        bond = find_bond(obj1.atom, obj2.atom)
        bond.bust(make_bondpoints=False)

    # make the bonds we want and didn't already have
    for obj1, obj2 in [(Pl1, c), (Pl2, b)]:
        assert not atoms_are_bonded(obj1.atom, obj2.atom)
        ###e we should make bond_atoms do this assert itself, or maybe tolerate it (or does it already??)
        bond_atoms_faster(obj1.atom, obj2.atom, V_SINGLE)

    # set directions of all 4 bonds (even the preserved ones -- it's possible they were not set before,
    #  if some but not all bonds had directions set in the part of a strand whose directions we look at.)
    for obj1, obj2 in [(a, Pl1), (Pl1, c), (d, Pl2), (Pl2, b)]:
        bond = find_bond(obj1.atom, obj2.atom)
        bond.set_bond_direction_from(obj1.atom, 1)

    # WARNING: after that bond rearrangement, don't use our Pl5_recognizers in ways that depend on Pl bonding,
    # since it's not well defined whether they think about the old or new bonding to give their answers.
    Pl_atoms = Pl1.atom, Pl2.atom
    del Pl1, Pl2, twoPls

    # transmute base sugars to Sj or Ss as appropriate
    if dna_updater_is_enabled():
        want = Element_Ss5  #bruce 080320 bugfix
    else:
        want = make and Element_Sj5 or Element_Ss5
    for obj in (a, b, c, d):
        obj.atom.Transmute(want)
        # Note: we do this after the bond making/breaking so it doesn't add singlets which mess us up.

    # move Pl atoms into better positions
    # (someday, consider using local minimize; for now, just place them directly between their new neighbor atoms,
    #  hopefully we leave them selected so user can easily do their own local minimize.)
    for pl in Pl_atoms:
        pl.setposn(
            average_value(map(lambda neighbor: neighbor.posn(),
                              pl.neighbors())))

    env.history.message(
        greenmsg(cmdname + ": ") + quote_html("(%s - %s)" % tuple(Pl_atoms)))

    #e need assy.changed()? evidently not.

    return  # from make_or_remove_crossover
コード例 #9
0
def Pl_pos_from_neighbor_PAM3plus5_data(
        bond_directions_to_neighbors,
        remove_data_from_neighbors = False
    ): #bruce 080402
    """
    Figure out where a new Pl atom should be located
    based on the PAM3plus5_data (related to its position)
    in its neighbor Ss3 or Ss5 atoms (whose positions are assumed correct).
    (If the neighbors are Ss5 it's because they got converted from Ss3
     recently, since this info is normally only present on Ss3 atoms.)

    Assume the neighbors are in DnaLadders with up to date baseframe info
    already computed and stored (but not necessarily that these ladders
    have remade their chunks, meaning atom.molecule.ladder might not exist
    or point to the new DnaLadders). The up to dateness of the baseframe info
    may not be checked, and out of date info would cause hard-to-notice bugs.

    @return: new absolute position, or None if we can't compute one (error).

    @see: related method (stores data in the other direction),
          _f_Pl_store_position_into_Ss3plus5_data

    @see: analogous function for Gv: Gv_pos_from_neighbor_PAM3plus5_data
    """
    proposed_posns = []

    for direction_to, ss in bond_directions_to_neighbors: # neighbors of Pl
        if ss.element.role == 'strand':
            # (avoid bondpoints or (erroneous) non-PAM or axis atoms)
            try:
                #bruce 080516: protect from exceptions on each neighbor
                # (so it can still work if the other one works --
                #  can happen for bridging Pls with bugs on one ladder)
                pos = ss._f_recommend_PAM3plus5_Pl_abs_position(
                        - direction_to, # the sign makes this the Ss -> Pl direction
                        remove_data = remove_data_from_neighbors,
                        make_up_position_if_necessary = True # doesn't prevent all returns of None
                 )
            except:
                msg = "bug: exception in " \
                      "%r._f_recommend_PAM3plus5_Pl_abs_position, " \
                      "skipping it for that strand_neighbor" % (ss,)
                print_compact_traceback( msg + ": ")
                pos = None # following print is redundant; nevermind
                pass
            if pos is None:
                # can happen in theory, in spite of
                # make_up_position_if_necessary = True,
                # if ss is not a valid atom for this;
                # but the loop above tries not to call it then,
                # so this should not happen unless there are bugs...
                # oops, it can be called for ss in a single strand domain, for example.
                # todo: debug_flags for this:
                print "fyi: _f_recommend_PAM3plus5_Pl_abs_position returned None for %r" % ss
                    # remove when works if routine; leave in if never seen, to notice bugs
            else:
                proposed_posns.append(pos)
        continue

    if not proposed_posns:
        # neither neighbor was able to make up a position -- error.
        # caller might have ways of handling this, but we don't...
        print "bug: Pl_pos_from_neighbor_PAM3plus5_data can't compute pos " \
              "for Pl between these neighbors:", bond_directions_to_neighbors
        return None

    if len(proposed_posns) == 1:
        # optimization
        return proposed_posns[0]

    return average_value( proposed_posns)
コード例 #10
0
def make_or_remove_crossover(twoPls, make = True, cmdname = None):
    """
    Make or Remove (according to make option) a crossover, given Pl5_recognizers for its two Pl atoms.
    """

    # What we are doing is recognizing one local structure and replacing it with another
    # made from the same atoms. It'd sure be easier if I could do the manipulation in an mmp file,
    # save that somewhere, and read those to generate the operation! I'd have two sets of atoms, before and after,
    # and see how bonds and atomtypes got changed.

    # In this case it's not too hard to hand-code... I guess only the Pl atoms and their bonds are affected.
    # We probably do have to look at strand directions -- hmm, probably we should require them to exist before saying it's ok!
    # Or maybe better to give history error message when the command is chosen, saying you need to set them (or repair them) first...
    # Then we have to move the new/moved Pl atoms into a good position...

    # Note: Pl.ordered_bases are ordered by bond direction, to make this easier...
    # but if we want to patch up the directions in the end, do we need to care exactly which ones were defined?
    # or only "per-Pl"? hmm... it's per-Pl for now

    assert cmdname

    for pl in twoPls:
        if pl.ordered_bases is None: # should no longer be possible -- now checked before menu commands are offered [bruce 070604]
            ###BUG: this could have various causes, not only the one reported below! Somehow we need access to the
            # message supplied to the RecognizerError, for use here.
            ###REVIEW: Does that mean it should pass through compute methods (probably in a controlled way)
            # rather than making computed values None?
            # Or, should the value not be None, but a "frozen" examinable and reraisable version of the error exception??
            msg = "%s: Error: bond direction is locally undefined or inconsistent around %s" % (cmdname, pl.atom) ###UNTESTED
            print "should no longer be possible:", msg #bruce 070604
            env.history.message( redmsg( quote_html( msg)))
            return

    Pl1, Pl2 = twoPls
    a,b = Pl1.ordered_bases
    d,c = Pl2.ordered_bases # note: we use d,c rather than c,d so that the atom arrangement is as shown in the diagram below.

    # Note: for either the Make or Remove operation, the geometric arrangement is initially:
    #
    # c <-- Pl2 <-- d
    #
    # a --> Pl1 --> b
    #
    # and it ends up being (where dots indicate arrowheads, to show bond direction):
    #
    # c        d
    #  .      /
    #   \    .
    #  Pl1  Pl2
    #   .    \
    #  /      .
    # a        b
    #
    # Note: Pl1 stays attached to a, and Pl2 to d. Which two opposite bonds to preserve like that
    # is an arbitrary choice -- as long as Make and Remove make the same choice about that,
    # they'll reverse each other's effects precisely (assuming the sugars were initially correct as Ss or Sj).

    # break the bonds we no longer want
    for obj1, obj2 in [(Pl1, b), (Pl2, c)]:
        bond = find_bond(obj1.atom, obj2.atom)
        bond.bust(make_bondpoints = False)

    # make the bonds we want and didn't already have
    for obj1, obj2 in [(Pl1, c), (Pl2, b)]:
        assert not atoms_are_bonded(obj1.atom, obj2.atom)
            ###e we should make bond_atoms do this assert itself, or maybe tolerate it (or does it already??)
        bond_atoms_faster(obj1.atom, obj2.atom, V_SINGLE)

    # set directions of all 4 bonds (even the preserved ones -- it's possible they were not set before,
    #  if some but not all bonds had directions set in the part of a strand whose directions we look at.)
    for obj1, obj2 in [(a, Pl1), (Pl1, c), (d, Pl2), (Pl2, b)]:
        bond = find_bond(obj1.atom, obj2.atom)
        bond.set_bond_direction_from(obj1.atom, 1)

    # WARNING: after that bond rearrangement, don't use our Pl5_recognizers in ways that depend on Pl bonding,
    # since it's not well defined whether they think about the old or new bonding to give their answers.
    Pl_atoms = Pl1.atom, Pl2.atom
    del Pl1, Pl2, twoPls

    # transmute base sugars to Sj or Ss as appropriate
    if dna_updater_is_enabled():
        want = Element_Ss5 #bruce 080320 bugfix
    else:
        want = make and Element_Sj5 or Element_Ss5
    for obj in (a,b,c,d):
        obj.atom.Transmute(want)
        # Note: we do this after the bond making/breaking so it doesn't add singlets which mess us up.

    # move Pl atoms into better positions
    # (someday, consider using local minimize; for now, just place them directly between their new neighbor atoms,
    #  hopefully we leave them selected so user can easily do their own local minimize.)
    for pl in Pl_atoms:
        pl.setposn( average_value( map( lambda neighbor: neighbor.posn() , pl.neighbors() )))

    env.history.message( greenmsg( cmdname + ": ") + quote_html("(%s - %s)" % tuple(Pl_atoms)))

    #e need assy.changed()? evidently not.

    return # from make_or_remove_crossover
コード例 #11
0
 def posn(self): # stub - average posn of Ss neighbors (plus offset in case only one!)
     print "should use Pl_pos_from_neighbor_PAM3plus5_data for %r" % self #####
     # note: average_value seems to work here
     res = average_value( [n.posn() for n in self.neighbors()], V(0, 0, 0) )
     return res + V(0, 2, 0) # offset (kluge, wrong)
コード例 #12
0
def Pl_pos_from_neighbor_PAM3plus5_data(
        bond_directions_to_neighbors,
        remove_data_from_neighbors=False):  #bruce 080402
    """
    Figure out where a new Pl atom should be located
    based on the PAM3plus5_data (related to its position)
    in its neighbor Ss3 or Ss5 atoms (whose positions are assumed correct).
    (If the neighbors are Ss5 it's because they got converted from Ss3
     recently, since this info is normally only present on Ss3 atoms.)

    Assume the neighbors are in DnaLadders with up to date baseframe info
    already computed and stored (but not necessarily that these ladders
    have remade their chunks, meaning atom.molecule.ladder might not exist
    or point to the new DnaLadders). The up to dateness of the baseframe info
    may not be checked, and out of date info would cause hard-to-notice bugs.

    @return: new absolute position, or None if we can't compute one (error).

    @see: related method (stores data in the other direction),
          _f_Pl_store_position_into_Ss3plus5_data

    @see: analogous function for Gv: Gv_pos_from_neighbor_PAM3plus5_data
    """
    proposed_posns = []

    for direction_to, ss in bond_directions_to_neighbors:  # neighbors of Pl
        if ss.element.role == 'strand':
            # (avoid bondpoints or (erroneous) non-PAM or axis atoms)
            try:
                #bruce 080516: protect from exceptions on each neighbor
                # (so it can still work if the other one works --
                #  can happen for bridging Pls with bugs on one ladder)
                pos = ss._f_recommend_PAM3plus5_Pl_abs_position(
                    -direction_to,  # the sign makes this the Ss -> Pl direction
                    remove_data=remove_data_from_neighbors,
                    make_up_position_if_necessary=
                    True  # doesn't prevent all returns of None
                )
            except:
                msg = "bug: exception in " \
                      "%r._f_recommend_PAM3plus5_Pl_abs_position, " \
                      "skipping it for that strand_neighbor" % (ss,)
                print_compact_traceback(msg + ": ")
                pos = None  # following print is redundant; nevermind
                pass
            if pos is None:
                # can happen in theory, in spite of
                # make_up_position_if_necessary = True,
                # if ss is not a valid atom for this;
                # but the loop above tries not to call it then,
                # so this should not happen unless there are bugs...
                # oops, it can be called for ss in a single strand domain, for example.
                # todo: debug_flags for this:
                print "fyi: _f_recommend_PAM3plus5_Pl_abs_position returned None for %r" % ss
                # remove when works if routine; leave in if never seen, to notice bugs
            else:
                proposed_posns.append(pos)
        continue

    if not proposed_posns:
        # neither neighbor was able to make up a position -- error.
        # caller might have ways of handling this, but we don't...
        print "bug: Pl_pos_from_neighbor_PAM3plus5_data can't compute pos " \
              "for Pl between these neighbors:", bond_directions_to_neighbors
        return None

    if len(proposed_posns) == 1:
        # optimization
        return proposed_posns[0]

    return average_value(proposed_posns)