示例#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
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 _insert_Pl_between_0(s1, s2):
    direct_bond = find_bond(s1, s2)
    assert direct_bond
    direction = direct_bond.bond_direction_from(s1)  # from s1 to s2

    # Figure out which atom the Pl sticks to (joins the chunk of).
    # Note: the following works even if s1 or s2 is a bondpoint,
    # which is needed for putting a Pl at the end of a newly-PAM5 strand.
    # But whether to actually put one there is up to the caller --
    # it's only correct at one end, but this function will always do it.
    if direction == Pl_STICKY_BOND_DIRECTION:
        Pl_prefers = [s2, s1]
    else:
        Pl_prefers = [s1, s2]
    # The Pl sticks to the first thing in Pl_prefers which is not a bondpoint
    # (which always exists, since two bondpoints can't be bonded):
    # (see also the related method Pl_preferred_Ss_neighbor())
    Pl_sticks_to = None  # for pylint
    for s in Pl_prefers:
        if not s.is_singlet():
            Pl_sticks_to = s
            break
        continue

    # break the old bond... wait, do this later,
    # so as not to kill s1 or s2 if one of them is a Singlet
    ## direct_bond.bust(make_bondpoints = False)

    # make the new Pl atom


##    Atom = s1.__class__
##        # kluge to avoid import of chem.py, for now
##        # (though that would probably be ok (at least as a runtime import)
##        #  even though it might expand the import cycle graph)
##        # needs revision if we introduce Atom subclasses
##        # TODO: check if this works when Atom is an extension object
##        # (from pyrex atoms)
    Atom = s1.molecule.assy.Atom  #bruce 080516
    chunk = Pl_sticks_to.molecule
    pos = (s1.posn() + s2.posn()) / 2.0
    # position will be corrected by caller before user sees it
    Pl = Atom('Pl5', pos, chunk)
    Pl._f_Pl_posn_is_definitive = False
    # tell caller it needs to, and is allowed to, correct pos

    # bond it to s1 and s2
    b1 = bond_atoms_faster(Pl, s1, V_SINGLE)
    b2 = bond_atoms_faster(Pl, s2, V_SINGLE)

    # now it should be safe to break the old bond
    direct_bond.bust(make_bondpoints=False)

    # set bond directions: s1->Pl->s2 same as s1->s2 was before
    b1.set_bond_direction_from(s1, direction)
    b2.set_bond_direction_from(Pl, direction)

    return Pl  # or None if error? caller assumes not possible, so do we
def _insert_Pl_between_0(s1, s2):
    direct_bond = find_bond(s1, s2)
    assert direct_bond
    direction = direct_bond.bond_direction_from(s1) # from s1 to s2

    # Figure out which atom the Pl sticks to (joins the chunk of).
    # Note: the following works even if s1 or s2 is a bondpoint,
    # which is needed for putting a Pl at the end of a newly-PAM5 strand.
    # But whether to actually put one there is up to the caller --
    # it's only correct at one end, but this function will always do it.
    if direction == Pl_STICKY_BOND_DIRECTION:
        Pl_prefers = [s2, s1]
    else:
        Pl_prefers = [s1, s2]
    # The Pl sticks to the first thing in Pl_prefers which is not a bondpoint
    # (which always exists, since two bondpoints can't be bonded):
    # (see also the related method Pl_preferred_Ss_neighbor())
    Pl_sticks_to = None # for pylint
    for s in Pl_prefers:
        if not s.is_singlet():
            Pl_sticks_to = s
            break
        continue

    # break the old bond... wait, do this later,
    # so as not to kill s1 or s2 if one of them is a Singlet
    ## direct_bond.bust(make_bondpoints = False)

    # make the new Pl atom
##    Atom = s1.__class__
##        # kluge to avoid import of chem.py, for now
##        # (though that would probably be ok (at least as a runtime import)
##        #  even though it might expand the import cycle graph)
##        # needs revision if we introduce Atom subclasses
##        # TODO: check if this works when Atom is an extension object
##        # (from pyrex atoms)
    Atom = s1.molecule.assy.Atom #bruce 080516
    chunk = Pl_sticks_to.molecule
    pos = (s1.posn() + s2.posn()) / 2.0
        # position will be corrected by caller before user sees it
    Pl = Atom('Pl5', pos, chunk)
    Pl._f_Pl_posn_is_definitive = False
        # tell caller it needs to, and is allowed to, correct pos

    # bond it to s1 and s2
    b1 = bond_atoms_faster(Pl, s1, V_SINGLE)
    b2 = bond_atoms_faster(Pl, s2, V_SINGLE)

    # now it should be safe to break the old bond
    direct_bond.bust(make_bondpoints = False)

    # set bond directions: s1->Pl->s2 same as s1->s2 was before
    b1.set_bond_direction_from(s1, direction)
    b2.set_bond_direction_from(Pl, direction)

    return Pl # or None if error? caller assumes not possible, so do we
def make_bonds(atmlist, bondtyp=V_SINGLE):
    """
    Make some bonds between the given atoms. At any moment make the cheapest permitted unmade bond;
    stop only when no more bonds are permitted (i.e. all potential bonds have infinite cost).
       Assume that newly made bonds can never decrease the cost of potential bonds.
    (This is needed to justify the algorithm, which moves potential bonds later in an ordered list
    when their cost has increased since last checked;
    it's true since the bond cost (as defined elsewhere in this module) is a sum of terms,
    and adding a bond can add new terms but doesn't change the value of any existing terms.)
       Return the number of bonds created.
    """
    # Implementation note: the only way I know of to do this efficiently is to use a linked list
    # (as the Lisp code did), even though this is less natural in Python.
    bondlst0 = list_potential_bonds(
        atmlist)  # a list of triples (cost, atm1, atm2)
    bondlst = linked_list(
        bondlst0, list
    )  # arg2 (list) is a function to turn the triple (cost, atm1, atm2) into a list.
    # value is a list [[cost, atm1, atm2], next]; needs to be mutable in next and cost elements
    # (Note: Lisp code used a Lisp linked list of triples, (cost atm1 atm2 . next), but all Lisp lists are mutable.)
    res = 0
    while bondlst:
        entry, bondlst = bondlst
        # original code assumed this was too early to change bondlst -- we might insert a new element right after entry;
        # but I think that's not possible, so we can move forward now [bruce 050906]
        oldcostjunk, atm1, atm2 = entry
        cost = bond_cost(atm1,
                         atm2)  # might be different than last recorded cost
        #e optim: could invalidate changed costs, avoid recomputing some of them, incrementally adjust others
        if cost is not None:
            if (bondlst is None) or bondlst[0][0] >= cost:
                # if there's no next-best bond, or its cost is no better than this one's, make this bond
                bond_atoms_faster(
                    atm1, atm2, bondtyp
                )  # optimized bond_atoms, and doesn't make any open bonds
                res += 1
            else:
                # cost has increased beyond next bond in list -- update entry and move it down list
                entry[0] = cost
                curr = bondlst  # loop variable - next possible list element after which we might insert entry
                while 1:
                    # (at this point, we know curr is not None, and we already compared cost to curr[0][0])
                    junk, next = curr
                    if (next is None) or next[0][0] >= cost:
                        break  # found insertion point: right after curr, before next (next might be None)
                    curr = next
                assert curr[1] is next  #e remove when works
                # insert entry after curr, before next
                curr[1] = [entry, next]
            pass
        pass
    return res
def make_bonds(atmlist, bondtyp=V_SINGLE):
    """
    Make some bonds between the given atoms. At any moment make the cheapest permitted unmade bond;
    stop only when no more bonds are permitted (i.e. all potential bonds have infinite cost).
       Assume that newly made bonds can never decrease the cost of potential bonds.
    (This is needed to justify the algorithm, which moves potential bonds later in an ordered list
    when their cost has increased since last checked;
    it's true since the bond cost (as defined elsewhere in this module) is a sum of terms,
    and adding a bond can add new terms but doesn't change the value of any existing terms.)
       Return the number of bonds created.
    """
    # Implementation note: the only way I know of to do this efficiently is to use a linked list
    # (as the Lisp code did), even though this is less natural in Python.
    bondlst0 = list_potential_bonds(atmlist)  # a list of triples (cost, atm1, atm2)
    bondlst = linked_list(
        bondlst0, list
    )  # arg2 (list) is a function to turn the triple (cost, atm1, atm2) into a list.
    # value is a list [[cost, atm1, atm2], next]; needs to be mutable in next and cost elements
    # (Note: Lisp code used a Lisp linked list of triples, (cost atm1 atm2 . next), but all Lisp lists are mutable.)
    res = 0
    while bondlst:
        entry, bondlst = bondlst
        # original code assumed this was too early to change bondlst -- we might insert a new element right after entry;
        # but I think that's not possible, so we can move forward now [bruce 050906]
        oldcostjunk, atm1, atm2 = entry
        cost = bond_cost(atm1, atm2)  # might be different than last recorded cost
        # e optim: could invalidate changed costs, avoid recomputing some of them, incrementally adjust others
        if cost is not None:
            if (bondlst is None) or bondlst[0][0] >= cost:
                # if there's no next-best bond, or its cost is no better than this one's, make this bond
                bond_atoms_faster(atm1, atm2, bondtyp)  # optimized bond_atoms, and doesn't make any open bonds
                res += 1
            else:
                # cost has increased beyond next bond in list -- update entry and move it down list
                entry[0] = cost
                curr = bondlst  # loop variable - next possible list element after which we might insert entry
                while 1:
                    # (at this point, we know curr is not None, and we already compared cost to curr[0][0])
                    junk, next = curr
                    if (next is None) or next[0][0] >= cost:
                        break  # found insertion point: right after curr, before next (next might be None)
                    curr = next
                assert curr[1] is next  # e remove when works
                # insert entry after curr, before next
                curr[1] = [entry, next]
            pass
        pass
    return res
示例#7
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
示例#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