summaryrefslogtreecommitdiff
path: root/cad/src/dna/model/PAM_atom_rules.py
blob: 349431d0cfc40e31079ffef8ef2b052e0b3a1107 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# Copyright 2008 Nanorex, Inc.  See LICENSE file for details.
"""
PAM_atom_rules.py - functions that define rules for permissible PAM structure

(note: not all such code is in this module; some of it is implicit
or hardcoded in other files)

@author: Bruce
@version: $Id$
@copyright: 2008 Nanorex, Inc.  See LICENSE file for details.
"""

from model.elements import Singlet

from dna.updater.dna_updater_globals import _f_anyatom_wants_pam

from model.bonds import find_bond

# ==

def PAM_atoms_allowed_in_same_ladder(a1, a2): #bruce 080401
    # has known bugs, see comment; in future may apply only within rails
    """
    Are a1 and a2 both PAM atoms or bondpoints which are allowed in the same
    DnaLadder, during either formation or merging of ladders?

    If not, this might be for any reason we want, including cosmetic
    (e.g. chunk colors differ), but if different atoms in one base pair
    use different criteria, problems might ensue if the dna updater does not
    explicitly handle that somehow when forming ladders (since currently
    it requires all atoms in one full or partial basepair to be in one ladder).

    Presently, we allow bondpoints to go with anything; other than that,
    we require both atoms to have the same PAM model, and to be in chunks
    with the same PAM-related properties (display_as_pam, save_as_pam).
    """
    # BUG: the following will be wrong, if the atoms in one full or partial
    # basepair disagree about this data. To fix, find the basepair here
    # (if too big, mark atoms as error, use default results), and combine
    # the properties to make a joint effective property on each atom,
    # before comparing. Not trivial to do that efficiently. Or, actually
    # modify the atoms in a basepair to agree, or mark them as errors so
    # they are excluded from chains, at earlier updater stages. ### TODO
    #
    # I'm ignoring this for now, though it means some operation bugs could
    # lead to updater exceptions. (And correct operations must change these
    # properties in sync across base pairs.) It may even mean the user can
    # cause those errors by piecing together base pairs from different
    # DnaLadders using direct bond formation. (Maybe make rung-bond-former
    # detect and fix those?)
    #
    # Review: how to treat atoms with dna_updater_errors?

    explain_false_when_printed = True

    explain_false_always = False # do not commit with true --
        # too verbose if user makes mixed PAM models on purpose

    explain_false = explain_false_when_printed or explain_false_always
        # revised all explain_false logic, bruce 080413

    print0 = [""]

    def doit():
        if a1.element is Singlet or a2.element is Singlet:
            return True
        if a1.element.pam != a2.element.pam:
            # different pam models, or one has no pam model (non-PAM element)
            if explain_false:
                print0[0] = "different pam models: %s, %s" % (a1.element.pam, a2.element.pam)
            return False
        # we don't need to check for "both non-PAM", since we're not called
        # for such atoms (and if we were, we might as well allow them together)

        # compare manual pam conversion requests
        if _f_anyatom_wants_pam(a1) != _f_anyatom_wants_pam(a2):
            if explain_false:
                print0[0] = "different requested manual pam conversion: %s, %s" % \
                      ( _f_anyatom_wants_pam(a1),
                        _f_anyatom_wants_pam(a2) )
            return False

        # compare pam-related properties of chunks
        chunk1 = a1.molecule
        chunk2 = a2.molecule
        # assume atoms not killed, so these are real chunks
        if chunk1 is chunk2:
            # optimize common case
            return True
        if (chunk1.display_as_pam or None) != (chunk2.display_as_pam or None): # "or None" is kluge bug workaround [080407 late]
            if explain_false:
                print0[0] = "different display_as_pam: %r != %r" % ( chunk1.display_as_pam, chunk2.display_as_pam)
            return False
        if (chunk1.save_as_pam or None) != (chunk2.save_as_pam or None):
            if explain_false:
                print0[0] = "different save_as_pam: %r != %r" % ( chunk1.save_as_pam, chunk2.save_as_pam)
            return False
        return True
    res = doit()
    if not res:
        # should we print this as a possible bug, and/or explain why it's false?
        bond = find_bond(a1, a2)
        weird = False
        if bond is None: # can this happen, re find_bond semantics? I think so;
            # should not happen re how we are called, tho..
            # oops, it's routine, since ladder merge is filtered by checking
            # this for arbitrary baseatoms, so nevermind. [bruce 080413 230pm]
            pass ## weird = "not bonded"
        elif bond.is_rung_bond():
            weird = "rung bond"
        if weird:
            print "might cause bugs: %r and %r not allowed in same ladder, but %s" % (a1, a2, weird)
            if explain_false:
                print " reason not allowed: ", print0[0]
        elif explain_false_always:
            print "debug fyi: PAM_atoms_allowed_in_same_ladder(%r, %r) -> %r" % (a1, a2, res)
            print " reason not allowed: ", print0[0]
    return res

# end