# 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 "" pass # end