summaryrefslogtreecommitdiff
path: root/cad/src/model/Elem.py
blob: 6dbd71e11f05607ed22566cd502327ad7f5ffa4d (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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# Copyright 2004-2008 Nanorex, Inc.  See LICENSE file for details.
"""
Elem.py -- provides class Elem, which represents one element
in NE1's periodic table.

Note that some Elem instances are used only with "pseudoatoms" or
bondpoints, whereas others correspond to actual chemical elements.

@author: Josh
@version: $Id$
@copyright: 2004-2008 Nanorex, Inc.  See LICENSE file for details.

History:

Bruce 071101 split class Elem out of elements.py into its own module.

TODO:

In elements.py and Elem.py,
modularize the creation of different kinds of elements,
to help permit specialized modules and Elem/Atom subclasses
for PAM3 and PAM5 (etc). (Should we define new Elem subclasses for them?)
"""

from foundation.state_utils import IdentityCopyMixin

class Elem(IdentityCopyMixin):
    """
    There is exactly one of these objects for each supported element in the periodic table.
    Its identity (as a python object) never changes during the run.
    Instead, if prefs changes are made in color, radius, or perhaps bonding pattern,
    this object's contents will be modified accordingly.
    """
    # bruce 050510 renamed this from 'elem'
    # (not using 'Element' since too common in strings/comments)

    # default values of per-instance constants
    bonds_can_be_directional = False #bruce 071015

    # attributes for classifying elements -- tentative in name, meaning, and value-encoding [bruce 071106]
    # (warning: these default values are never used, since __init__ always sets these attrs in self)
    pam = None # name of pseudo-atom model (e.g. MODEL_PAM3 == 'PAM3'), or None;
        # not sure if Singlet and regular elems have same .pam
        # REVIEW: it might be simplest if Singlet had None here, and all others had a true value, e.g. 'PAM3' or 'PAM5' or 'Chem'.
        # If we use that scheme, then we certainly need to rename this. It is an "element class"? "element model"??
    role = None # element role in its pseudo-atom model; for DNA PAM atoms
        # this can be 'strand' or 'axis' or 'unpaired-base'; not sure about Singlet
    deprecated_to = None # symbol of an element to transmute this one to, when reading mmp files; or None, or 'remove' (??)
        # (used for deprecated elements, including simulation-only elements no longer needed when modeling)

    def __init__(self, eltnum, sym, name, mass, rvdw, color, bn,
                 pam = None,
                 role = None,
                 deprecated_to = None ):
        """
        Note: this should only be called by class _ElementPeriodicTable
        in elements.py.

        eltnum = atomic number (e.g. H is 1, C is 6); for Singlet this is 0
        sym = (e.g.) "H"
        name = (e.g.) "Hydrogen"
        mass = atomic mass in e-27 kg
        rvdw = van der Waals radius
            [warning: vdw radius is used for display, and is changeable as a display preference!
             If we ever need to use it for chemical purposes, we'll need a separate unchanging copy
             for that!]
        color = color (RGB, 0-1)
        bn = bonding info: list of triples:
             # of bonds in this form
             covalent radius (units of 0.01 Angstrom)
             info about angle between bonds, as an array of vectors
             optional 4th item in the "triple": name of this bonding pattern, if it has one

        Note: self.bonds_can_be_directional is set for some elements
        by the caller. [In the future it may depend on the role option, or be its own option.]
        """
        # bruce 041216 and 050510 modified the above docstring
        self.eltnum = eltnum
        self.symbol = sym
        self.symbol_for_printing = sym #bruce 071106
        if sym[-1].isdigit():
            # separate symbol digits from numeric key
            self.symbol_for_printing += '-'
        self.name = name
        self.color = color
        self.mass = mass
        self.rvdw = rvdw
        # option values
        self.pam = pam
        self.role = role
        self.deprecated_to = deprecated_to
        if not deprecated_to and deprecated_to is not None:
            print "WARNING: we don't yet know what element %r should be deprecated_to" % sym
        self.atomtypes = []
        ## self.bonds = bn # not needed anymore, I hope
#         if not bn: # e.g. Helium
#             bn = [[0, 0, None]]
#         valence = bn[0][0]
#         assert type(valence) == type(1)
#         assert valence in [0,1,2,3,4,5,6,7] # in fact only up to 4 is properly supported
#         self.atomtypes = map( lambda bn_triple: AtomType( self, bn_triple, valence ), bn ) # creates cyclic refs, that's ok
#             # This is a public attr. Client code should not generally modify it!
#             # But if we someday have add_atomtype method, it can append or insert,
#             # as long as it remembers that client code treats index 0 as the default atomtype for this element.
#             # Client code is not allowed to assume a given atomtype's position in this list remains constant!
        return

    def addAtomType(self, aType):
        self.atomtypes += [aType]

    def find_atomtype(self, atomtype_name): #bruce 050511
        """
        Given an atomtype name or fullname (or an atomtype object itself)
        for this element, return the atomtype object.

        @param atomTypeName: The atomtype name or fullname (or an atomtype
                             object itself) for this element. Given None,
                             return this element's default atomtype object.
        @type  atomTypeName: str or L{AtomType}

        @return: The atomtype object.
        @rtype:  L{AtomType}

        @raise: Raise an exception (various exception types are possible)
                if no atomtype for this element matches the name (or equals
                the passed object).

        """
        if not atomtype_name: # permit None or "" for now
            return self.atomtypes[0]
        for atomtype in self.atomtypes: # in order from [0], though this should not matter since at most one should match
            if atomtype.name == atomtype_name or atomtype.fullname == atomtype_name or atomtype == atomtype_name:
                return atomtype # we're not bothering to optimize for atomtype_name being the same obj we return
        assert 0, "%r is not a valid atomtype name or object for %s" % (atomtype_name, self.name)

    def findAtomType(self, atomTypeName):
        """
        Given an atomtype name or fullname (or an atomtype object itself)
        for this element, return the atomtype object.

        Same as L{find_atomtype()}, provided for convenience.

        @param atomTypeName: The atomtype name or fullname (or an atomtype
                             object itself) for this element. Given None,
                             return this element's default atomtype object.
        @type  atomTypeName: str or L{AtomType}

        @return: The atomtype object.
        @rtype:  L{AtomType}

        @raise: Raise an exception (various exception types are possible)
                if no atomtype for this element matches the name (or equals
                the passed object).
        """
        return self.find_atomtype(atomTypeName)

    def __repr__(self):
        return "<Element: " + self.symbol + "(" + self.name + ")>"

    pass

# end