Example #1
0
def update_PAM_atoms_and_bonds(changed_atoms):
    """
    Update PAM atoms and bonds.

    @param changed_atoms: an atom.key -> atom dict of all changed atoms
                          that this update function needs to consider,
                          which includes no killed atoms. THIS WILL BE
                          MODIFIED to include all atoms changed herein,
                          and to remove any newly killed atoms.

    @return: None
    """
    # ==

    # fix atom & bond classes, and break locally-illegal bonds

    # (Note that the dna updater only records changed bonds
    #  by recording both their atoms as changed -- it has no
    #  list of "changed bonds" themselves.)

    # note: these fix_ functions are called again below, on new atoms.

    fix_atom_classes( changed_atoms)

    fix_bond_classes( changed_atoms)
        # Fixes (or breaks if locally-illegal) all bonds of those atoms.
        # ("Locally" means "just considering that bond and its two atoms,
        #  not worrying about other bonds on those atoms. ### REVIEW: true now? desired?)

        # NOTE: new bondpoints must be given correct classes by bond.bust,
        # since we don't fix them in this outer method!
        # (Can this be done incrementally? ### REVIEW)
        # IMPLEM THAT ###

    # depending on implem, fixing classes might record more atom changes;
    # if so, those functions also fixed the changed_atoms dict we passed in,
    # and we can ignore whatever additional changes were recorded:

    ignore_new_changes( "from fixing atom & bond classes")

    # ==

    # fix deprecated elements, and the classes of any new objects this creates
    # (covering all new atoms, and all their bonds)

    # (note: we might also extend this to do PAM 3/3+5/5 conversions. not sure.)

    fix_deprecated_elements( changed_atoms) # changes more atoms;
        # implem is allowed to depend on atom & bond classes being correct

    # NOTE: this may kill some atoms that remain in changed_atoms
    # or get added to it below. Subroutines must tolerate killed atoms
    # until we remove them again.

    # Grab new atoms the prior step made, to include in subsequent steps.
    # (Note also that atoms already in changed_atoms might have been killed
    #  and/or transmuted during the prior step.)

    new_atoms = get_changes_and_clear()

    if new_atoms:

        fix_atom_classes( new_atoms)
            # must tolerate killed atoms
        fix_bond_classes( new_atoms)
            # sufficient, since any changed bonds (if alive) must be new bonds.

            # @@@@ did that break illegal bonds, or do we do that somewhere else? [080117 Q]

        # Note: do changed_atoms.update only after fixing classes on new_atoms,
        # so any new atoms that replace old ones in new_atoms also make it
        # into changed_atoms. Note that this effectively replaces all old atoms
        # in changed_atoms. (Note that this only works because new atoms have
        # the same keys as old ones. Also note that none of this matters as of
        # 071120, since fixing classes doesn't make new objects in the present
        # implem.)

        changed_atoms.update( new_atoms )

        ignore_new_changes( "from fixing classes after fixing deprecated elements")

    # ==

    # delete bare atoms (axis atoms without strand atoms, or vice versa).

    if not pref_permit_bare_axis_atoms():
        delete_bare_atoms( changed_atoms)
        # must tolerate killed atoms; can kill more atoms and break bonds,
        # but we can ignore those changes; BUT it can change neighbor atom
        # structure, and those changes are needed by subsequent steps
        # (though no need to fix their classes or look for bareness again,
        #  as explained inside that function)

        # Grab newly changed atoms from that step (neighbors of deleted atoms),
        # to include in subsequent steps. (But no need to fix their classes
        # or again call delete_bare_atoms, as explained inside that function.)
        # (Note also that atoms already in changed_atoms might have been killed
        #  during that step.)

        new_atoms = get_changes_and_clear()

        if new_atoms:
            if debug_flags.DEBUG_DNA_UPDATER:
                print "dna_updater: will scan %d new changes from delete_bare_atoms" % len(new_atoms)
            changed_atoms.update( new_atoms )

    # ==

    # Fix local directional bond issues:
    # - directional bond chain branches (illegal)
    # - missing bond directions (when fixable locally -- most are not)
    # - inconsistent bond directions
    #
    # The changes caused by these fixes include only:
    # - setting atom._dna_updater__error to a constant error code string on some atoms
    # - setting or unsetting bond direction on open bonds (changes from this could be ignored here)

    # Tentative conclusion: no need to do anything to new changed atoms
    # except scan them later; need to ignore atoms with _dna_updater__error set
    # when encountered in changed_atoms (remove them now? or in that function?)
    # and to stop on them when finding chains or rings.

    # [Note: geometric warnings (left-handed DNA, major groove on wrong side)
    # are not yet implemented. REVIEW whether they belong here or elsewhere --
    # guess: later, once chains and ladders are known. @@@@]

    fix_local_bond_directions( changed_atoms)

    new_atoms = get_changes_and_clear()

    if new_atoms:
        if debug_flags.DEBUG_DNA_UPDATER:
            print "dna_updater: will scan %d new changes from fix_local_bond_directions" % len(new_atoms)
        changed_atoms.update( new_atoms )

    # ==

    remove_killed_atoms( changed_atoms)

    remove_error_atoms( changed_atoms)

    # replaced with code at a later stage [bruce 080408];
    # we might revive this to detect Pl5 atoms that are bridging Ss3 atoms
    # (an error to fix early to prevent bugs)
    # or to make a note of ladders that need automatic conversion
    # due to displaying PAM5 in PAM35 form by default
    #
##    # ==
##
##    # Convert from PAM5 to PAM3+5, per-atom part. [080312, unfinished]
##
##    # Potential optimization (not known whether it'd be significant):
##    # only do this after operations that might introduce new PAM5 atoms
##    # (like mmp read, mmp insert, or perhaps Dna Generator)
##    # or that change any setting that causes them to become convertable.
##    # This is not practical now, since errors or non-whole base pairs prevent
##    # conversion, and almost any operation can remove those errors or make the
##    # base pairs whole.
##
##    convert_from_PAM5( changed_atoms)
##        # note: this replaces Pl5 with direct bonds, and may do more (undecided),
##        # but some conversion might be done later after ladders are constructed.
##        # So it might be misnamed. ###
##
##    ignore_new_changes( "from converting PAM5 to PAM3+5")
##
##    remove_killed_atoms( changed_atoms)

    return # from update_PAM_atoms_and_bonds
Example #2
0
def _full_dna_update_0( _runcount):
    """
    [private helper for full_dna_update -- do all the work]
    """

    # TODO: process _f_baseatom_wants_pam: (or maybe a bit later, after delete bare, and error finding?)
    # - extend to well-structured basepairs; drop structure error atoms (as whole basepairs)
    # - these and their baseatom neighbors in our changed atoms, maybe even real .changed_structure

    changed_atoms = get_changes_and_clear()

    debug_prints_as_dna_updater_starts( _runcount, changed_atoms)
        # note: this function should not modify changed_atoms.
        # note: the corresponding _ends call is in our caller.

    if not changed_atoms and not _f_are_there_any_homeless_dna_markers():
        # maybe: also check _f_baseatom_wants_pam, invalid ladders, here and elsewhere
        # (or it might be more efficient to officially require _changed_structure on representative atoms,
        #  which we're already doing now as a kluge workaround for the lack of testing those here)
        # [bruce 080413 comment]
        #
        # note: adding marker check (2 places) fixed bug 2673 [bruce 080317]
        return # optimization (might not be redundant with caller)

    # print debug info about the set of changed_atoms (and markers needing update)
    if debug_flags.DEBUG_DNA_UPDATER_MINIMAL:
        print "\ndna updater: %d changed atoms to scan%s" % \
              ( len(changed_atoms),
                _f_are_there_any_homeless_dna_markers() and " (and some DnaMarkers)" or ""
              )
    if debug_flags.DEBUG_DNA_UPDATER and changed_atoms:
        # someday: should be _VERBOSE, but has been useful enough to keep seeing for awhile
        items = changed_atoms.items()
        items.sort()
        atoms = [item[1] for item in items]
        NUMBER_TO_PRINT = 10
        if debug_flags.DEBUG_DNA_UPDATER_VERBOSE or len(atoms) <= NUMBER_TO_PRINT:
            print " they are: %r" % atoms
        else:
            print " the first %d of them are: %r ..." % \
                  (NUMBER_TO_PRINT, atoms[:NUMBER_TO_PRINT])

    if changed_atoms:
        remove_killed_atoms( changed_atoms) # only affects this dict, not the atoms

    if changed_atoms:
        remove_closed_or_disabled_assy_atoms( changed_atoms)
            # This should remove all remaining atoms from closed files.
            # Note: only allowed when no killed atoms are present in changed_atoms;
            # raises exceptions otherwise.

    if changed_atoms:
        update_PAM_atoms_and_bonds( changed_atoms)
            # this can invalidate DnaLadders as it changes various things
            # which call atom._changed_structure -- that's necessary to allow,
            # so we don't change dnaladder_inval_policy until below,
            # inside update_PAM_chunks [bruce 080413 comment]
            # (REVIEW: atom._changed_structure does not directly invalidate
            #  dna ladders, so I'm not sure if this comment is just wrong,
            #  or if it meant something not exactly what it said, like,
            #  this can cause more ladders to be invalidated than otherwise
            #  in an upcoming step -- though if it meant that, it seems
            #  wrong too, since the existence of that upcoming step
            #  might be enough reason to not be able to change the policy yet.
            #  [bruce 080529 addendum/Q])

    if not changed_atoms and not _f_are_there_any_homeless_dna_markers() and not _f_invalid_dna_ladders:
        return # optimization

    homeless_markers = _f_get_homeless_dna_markers() #e rename, homeless is an obs misleading term ####
        # this includes markers whose atoms got killed (which calls marker.remove_atom)
        # or got changed in structure (which calls marker.changed_structure)
        # so it should not be necessary to also add to this all markers noticed
        # on changed_atoms, even though that might include more markers than
        # we have so far (especially after we add atoms from invalid ladders below).
        #
        # NOTE: it can include fewer markers than are noticed by _f_are_there_any_homeless_dna_markers
        # since that does not check whether they are truly homeless.
    assert not _f_are_there_any_homeless_dna_markers() # since getting them cleared them

    new_chunks, new_wholechains = update_PAM_chunks( changed_atoms, homeless_markers)
        # note: at the right time during this or a subroutine, it sets
        # dnaladder_inval_policy to DNALADDER_INVAL_IS_ERROR

    # review: if not new_chunks, return? wait and see if there are also new_markers, etc...

    update_DNA_groups( new_chunks, new_wholechains)
        # review:
        # args? a list of nodes, old and new, whose parents should be ok? or just find them all, scanning MT?
        # the underlying nodes we need to place are just chunks and jigs. we can ignore old ones...
        # so we need a list of new or moved ones... chunks got made in update_PAM_chunks; jigs, in update_PAM_atoms_and_bonds...
        # maybe pass some dicts into these for them to add things to?

    ignore_new_changes("as full_dna_update returns", changes_ok = False )

    if debug_flags.DEBUG_DNA_UPDATER_MINIMAL:
        if _f_are_there_any_homeless_dna_markers():
            print "dna updater fyi: as updater returns, some DnaMarkers await processing by next run"
                # might be normal...don't know. find out, by printing it even
                # in minimal debug output. [bruce 080317]

    if _f_invalid_dna_ladders: #bruce 080413
        print "\n*** likely bug: some invalid ladders are recorded, as dna updater returns:", _f_invalid_dna_ladders
        # but don't clear them, in case this was sometimes routine and we were
        # working around bugs (unknowingly) by invalidating them next time around

    restore_dnaladder_inval_policy( DNALADDER_INVAL_IS_OK)

    return # from _full_dna_update_0