def make_Atom_and_bondpoints(self, elem, pos, atomtype=None, Chunk_class=None): """ Create one unbonded atom, of element elem and (if supplied) the given atomtype (otherwise the default atomtype for elem), at position pos, in its own new chunk, with enough bondpoints to have no valence error. @param Chunk_class: constructor for the returned atom's new chunk (self.assy.Chunk by default) @return: one newly created Atom object, already placed into a new chunk which has been added to the model using addnode """ # bruce 041215 moved this from chunk.py to chem.py, and split part of it # into the new atom method make_bondpoints_when_no_bonds, to help fix bug 131. # bruce 050510 added atomtype option # bruce 080520 added Chunk_class option # bruce 090112 renamed oneUnbonded function and turned it into this method assy = self.assy if Chunk_class is None: Chunk_class = assy.Chunk chunk = Chunk_class(assy, "bug") # name is reset below! atom = Atom(elem.symbol, pos, chunk) # bruce 041124 revised name of new chunk, was gensym('Chunk.'); # no need for gensym since atom key makes the name unique, e.g. C1. atom.set_atomtype_but_dont_revise_singlets(atomtype) # ok to pass None, type name, or type object; this verifies no change in elem # note, atomtype might well already be the value we're setting; # if it is, this should do nothing ## chunk.name = "Chunk-%s" % str(atom) chunk.name = gensym("Chunk", assy) # bruce 080407 per Mark NFR desire atom.make_bondpoints_when_no_bonds() # notices atomtype assy.addnode(chunk) # REVIEW: same as self.addnode? return atom
def constructModel(self, elm, pos, dispMode): """ This is to try to repeat what 'make_Atom_and_bondpoints()' method does, but hope to remove some stuff not needed here. The main purpose is to build the geometry model for element display. @param elm: An object of class Elem @param elm: L{Elem} @param dispMode: the display mode of the atom @type dispMode: int @return: the Chunk which contains the geometry model. @rtype: L{Chunk} """ assy = Assembly(None, run_updaters = False) assy.set_glpane(self) # sets .o and .glpane mol = Chunk(assy, 'dummy') atm = Atom(elm.symbol, pos, mol) atm.display = dispMode ## bruce 050510 comment: this is approximately how you should change the atom type (e.g. to sp2) for this new atom: ## atm.set_atomtype_but_dont_revise_singlets('sp2') ## see also atm.element.atomtypes -> a list of available atomtype objects for that element ## (which can be passed to set_atomtype_but_dont_revise_singlets) atm.make_bondpoints_when_no_bonds() return mol
def build_struct( self, name, parameters, position ): """ Build an Atom (as a chunk) according to the given parameters. @param name: The name which should be given to the toplevel Node of the generated structure. The name is also passed in self.name. @type name: str @param parameters: The parameter tuple returned from L{gather_parameters()}. @type parameters: tuple @param position: Unused. The xyz position is obtained from the I{parameters} tuple. @type position: position @return: The new structure, i.e. some flavor of a Node, which has not yet been added to the model. Its structure should depend only on the values of the passed parameters, since if the user asks to build twice, this method may not be called if the parameterss have not changed. @rtype: Node """ x, y, z, theElement, theAtomType = parameters # Create new chunk to contain the atom. outMolecule = Chunk( self.win.assy, self.name ) theAtom = Atom( theElement, V(x, y, z), outMolecule ) theAtom.set_atomtype( theAtomType ) theAtom.make_enough_bondpoints() return outMolecule
def constructModel(self, elm, pos, dispMode): """ This is to try to repeat what 'make_Atom_and_bondpoints()' method does, but hope to remove some stuff not needed here. The main purpose is to build the geometry model for element display. @param elm: An object of class Elem @param elm: L{Elem} @param dispMode: the display mode of the atom @type dispMode: int @return: the Chunk which contains the geometry model. @rtype: L{Chunk} """ assy = Assembly(None, run_updaters=False) assy.set_glpane(self) # sets .o and .glpane mol = Chunk(assy, 'dummy') atm = Atom(elm.symbol, pos, mol) atm.display = dispMode ## bruce 050510 comment: this is approximately how you should change the atom type (e.g. to sp2) for this new atom: ## atm.set_atomtype_but_dont_revise_singlets('sp2') ## see also atm.element.atomtypes -> a list of available atomtype objects for that element ## (which can be passed to set_atomtype_but_dont_revise_singlets) atm.make_bondpoints_when_no_bonds() return mol
def insertgms_new(assy, filename): """ Reads a GAMESS DAT file and inserts it into the existing model. Returns: 0 = Success 1 = Failed """ gmsAtomList = _get_atomlist_from_gms_outfile(assy, filename) if not gmsAtomList: return 1 # No atoms read. dir, nodename = os.path.split(filename) mol = Chunk(assy, nodename) ndix = {} n = 0 for a in gmsAtomList: print a pos = a.posn() fpos = (float(pos[0]), float(pos[1]), float(pos[2])) na = Atom(a.element.symbol, fpos, mol) ndix[n] = na n += 1 if mol is not None: assy.addmol(mol) return 0 else: return 1
def _new_bonded_n(self, lis): """ [private method] Make and return an atom (of self.atomtype) bonded to the base atoms of the n bondpoints in lis, in place of those bondpoints, which is a list of n pairs (singlet, pos), where each pos is the ideal position for a new atom bonded to its singlet alone. The new atom will always have n real bonds and no bondpoints, and be positioned at the average of the positions passed as pos. We don't check whether n is too many bonds for self.atomtype, nor do we care what kind of bond positions it would prefer. (This is up to the caller, if it matters; since the bondpoints typically already existed, there's not a lot that could be done about the bonding pattern, anyway, though we could imagine finding a position that better matched it. #e) """ # bruce 041215 made this from the first parts of the older methods bond1 # through bond4; the rest of each of those have become atom methods like # make_bondpoints_when_2_bonds. # The caller (self.attach [now renamed self.attach_to]) has been revised # to use these, and the result is (I think) equivalent to the old code, # except when el.numbonds > 4 [later: caller's new subrs now use atomtype not el], # when it does what it can rather than # doing nothing. The purpose was to fix bug 131 by using the new atom # methods by themselves. s1, p1 = lis[0] mol = s1.molecule # (same as its realneighbor's mol) totpos = +p1 # (copy it, so += can be safely used below) for sk, pk in lis[1:]: # 0 or more pairs after the first totpos += pk # warning: += can modify a mutable totpos pos = totpos / (0.0 + len(lis)) # use average of ideal positions atm = Atom(self.atomtype, pos, mol) for sk, pk in lis: sk.bonds[0].rebond(sk, atm) return atm
def _finish_molecule(): """ Perform some operations after reading entire PDB chain: - rebuild (infer) bonds - rename molecule to reflect a chain ID - delete protein object if this is not a protein - append the molecule to the molecule list """ if mol == water: # Skip water, to be added explicitly at the end. return if mol.atoms: ###print "READING PDB ", (mol, numconects, chainId) mol.name = pdbid.lower() + chainId ###idzialprint "SEQ = ", mol.protein.get_sequence_string() ###print "SEC = ", mol.protein.get_secondary_structure_string() if mol.protein.count_c_alpha_atoms() == 0: # If there is no C-alpha atoms, consider the chunk # as a non-protein. But! Split it into individual # hetero groups. res_list = mol.protein.get_amino_acids() assy.part.ensure_toplevel_group() hetgroup = Group("Heteroatoms", assy, assy.part.topnode) for res in res_list: hetmol = Chunk(assy, res.get_three_letter_code().replace(" ", "") + \ "[" + str(res.get_id()) + "]") for atom in res.get_atom_list(): newatom = Atom(atom.element.symbol, atom.posn(), hetmol) # New chunk - infer the bonds anyway (this is not # correct, should first check connectivity read from # the PDB file CONECT records). inferBonds(hetmol) hetgroup.addchild(hetmol) mollist.append(hetgroup) else: #if numconects == 0: # msg = orangemsg("PDB file has no bond info; inferring bonds") # env.history.message(msg) # # let user see message right away (bond inference can take significant # # time) [bruce 060620] # env.history.h_update() # For protein - infer the bonds anyway. inferBonds(mol) mol.protein.set_chain_id(chainId) mol.protein.set_pdb_id(pdbid) if mol.atoms: mollist.append(mol) else: env.history.message( redmsg( "Warning: Pdb file contained no atoms")) env.history.h_update()
def create_methane_test(self, params, name): # example: build some methanes print "create_methane_test" assy = self.win.assy from geometry.VQT import V from model.chunk import Chunk from model.chem import Atom mol = Chunk(assy, 'bug') # name is reset below! n = max(params[0],1) for x in range(n): for y in range(2): ## build methane, much like make_Atom_and_bondpoints method does it pos = V(x,y,0) atm = Atom('C', pos, mol) atm.make_bondpoints_when_no_bonds() # notices atomtype mol.name = name ## assy.addmol(mol) return mol
def add_to_mol(self, mol): maxradius = 1.5 * self.bondlength positions = self.carbons() atoms = [ ] for newpos in positions: newguy = Atom('C', newpos, mol) atoms.append(newguy) newguy.set_atomtype('sp2') ngen = NeighborhoodGenerator(atoms, maxradius) for atm1 in atoms: p1 = atm1.posn() for atm2 in ngen.region(p1): if atm2.key < atm1.key: bond_atoms(atm1, atm2, V_GRAPHITE) # clean up singlets for atm in atoms: for s in atm.singNeighbors(): s.kill() atm.make_enough_bondpoints()
def add_to_mol(self, mol): maxradius = 1.5 * self.bondlength positions = self.carbons() atoms = [] for newpos in positions: newguy = Atom('C', newpos, mol) atoms.append(newguy) newguy.set_atomtype('sp2') ngen = NeighborhoodGenerator(atoms, maxradius) for atm1 in atoms: p1 = atm1.posn() for atm2 in ngen.region(p1): if atm2.key < atm1.key: bond_atoms(atm1, atm2, V_GRAPHITE) # clean up singlets for atm in atoms: for s in atm.singNeighbors(): s.kill() atm.make_enough_bondpoints()
def insertin(assy, filename): _init() dir, nodename = os.path.split(filename) mol = Chunk(assy, nodename) mol.showOverlayText = True file = open(filename) lines = file.readlines() atoms = {} transform = InternalCoordinatesToCartesian(len(lines), None) for line in lines: columns = line.strip().split() index = int(columns[0]) name = columns[1] type = columns[2] na = int(columns[4]) nb = int(columns[5]) nc = int(columns[6]) r = float(columns[7]) theta = float(columns[8]) phi = float(columns[9]) transform.addInternal(index, na, nb, nc, r, theta, phi) xyz = transform.getCartesian(index) if (index > 3): if (AMBER_AtomTypes.has_key(type)): sym = AMBER_AtomTypes[type] else: print "unknown AMBER atom type, substituting Carbon: %s" % type sym = "C" a = Atom(sym, A(xyz), mol) atoms[index] = a a.setOverlayText(type) if (na > 3): a2 = atoms[na] bond_atoms(a, a2) assy.addmol(mol)
def build_struct(self, name, parameters, position): """ Build an Atom (as a chunk) according to the given parameters. @param name: The name which should be given to the toplevel Node of the generated structure. The name is also passed in self.name. @type name: str @param parameters: The parameter tuple returned from L{gather_parameters()}. @type parameters: tuple @param position: Unused. The xyz position is obtained from the I{parameters} tuple. @type position: position @return: The new structure, i.e. some flavor of a Node, which has not yet been added to the model. Its structure should depend only on the values of the passed parameters, since if the user asks to build twice, this method may not be called if the parameterss have not changed. @rtype: Node """ x, y, z, theElement, theAtomType = parameters # Create new chunk to contain the atom. outMolecule = Chunk(self.win.assy, self.name) theAtom = Atom(theElement, V(x, y, z), outMolecule) theAtom.set_atomtype(theAtomType) theAtom.make_enough_bondpoints() return outMolecule
def make_Atom_and_bondpoints(self, elem, pos, atomtype = None, Chunk_class = None ): """ Create one unbonded atom, of element elem and (if supplied) the given atomtype (otherwise the default atomtype for elem), at position pos, in its own new chunk, with enough bondpoints to have no valence error. @param Chunk_class: constructor for the returned atom's new chunk (self.assy.Chunk by default) @return: one newly created Atom object, already placed into a new chunk which has been added to the model using addnode """ #bruce 041215 moved this from chunk.py to chem.py, and split part of it # into the new atom method make_bondpoints_when_no_bonds, to help fix bug 131. #bruce 050510 added atomtype option #bruce 080520 added Chunk_class option #bruce 090112 renamed oneUnbonded function and turned it into this method assy = self.assy if Chunk_class is None: Chunk_class = assy.Chunk chunk = Chunk_class(assy, 'bug') # name is reset below! atom = Atom(elem.symbol, pos, chunk) # bruce 041124 revised name of new chunk, was gensym('Chunk.'); # no need for gensym since atom key makes the name unique, e.g. C1. atom.set_atomtype_but_dont_revise_singlets(atomtype) # ok to pass None, type name, or type object; this verifies no change in elem # note, atomtype might well already be the value we're setting; # if it is, this should do nothing ## chunk.name = "Chunk-%s" % str(atom) chunk.name = gensym("Chunk", assy) #bruce 080407 per Mark NFR desire atom.make_bondpoints_when_no_bonds() # notices atomtype assy.addnode(chunk) # REVIEW: same as self.addnode? return atom
def add(element, x, y, atomtype='sp2'): atm = Atom(element, V(x, y, z), mol) atm.set_atomtype_but_dont_revise_singlets(atomtype) return atm
def _buildResiduum(self, mol, zmatrix, n_atoms, phi, psi, init_pos, symbol): """ Builds cartesian coordinates for an amino acid from the internal coordinates table. mol is a chunk to which the amino acid will be added. zmatrix is an internal coordinates array corresponding to a given amino acid. n_atoms is a number of atoms to be build + 3 dummy atoms. phi is a peptide bond PHI angle. psi is a peptide bond PSI angle. init_pos are optional postions of previous CA, C and O atoms. symbol is a current amino acid symbol (used for proline case) Note: currently, it doesn't rebuild bonds, so inferBonds has to be called after. Unfortunately, the proper bond order can not be correctly recognized this way. """ if mol == None: return if not init_pos: # assign three previous atom positions for i in range(0, 3): self.coords[i][0] = self.prev_coords[i][0] self.coords[i][1] = self.prev_coords[i][1] self.coords[i][2] = self.prev_coords[i][2] else: # if no prev_coords are given, compute the first three atom positions num, name, atom_name, atom_type, \ atom_c, atom_b, atom_a, r, a, t = zmatrix[1] self.coords[0][0] = 0.0 self.coords[0][1] = 0.0 self.coords[0][2] = 0.0 self.coords[1][0] = r self.coords[1][1] = 0.0 self.coords[1][2] = 0.0 ccos = cos(DEG2RAD * a) num, name, atom_name, atom_type, \ atom_c, atom_b, atom_a, r, a, t = zmatrix[2] if atom_c == 1: self.coords[2][0] = self.coords[0][0] + r * ccos else: self.coords[2][0] = self.coords[0][0] - r * ccos self.coords[2][1] = r * sin(DEG2RAD * a) self.coords[2][2] = 0.0 for i in range(0, 3): self.prev_coords[i][0] = self.coords[i][0] + init_pos[0] self.prev_coords[i][1] = self.coords[i][1] + init_pos[1] self.prev_coords[i][2] = self.coords[i][2] + init_pos[2] for n in range(3, n_atoms): # Generate all coordinates using three previous atoms # as a frame of reference, num, name, atom_name, atom_type, \ atom_c, atom_b, atom_a, r, a, t = zmatrix[n] cosa = cos(DEG2RAD * a) xb = self.coords[atom_b][0] - self.coords[atom_c][0] yb = self.coords[atom_b][1] - self.coords[atom_c][1] zb = self.coords[atom_b][2] - self.coords[atom_c][2] rbc = 1.0 / sqrt(xb * xb + yb * yb + zb * zb) if abs(cosa) >= 0.999: # Linear bond case # Skip angles, just extend along the bond. rbc = r * rbc * cosa self.coords[n][0] = self.coords[atom_c][0] + xb * rbc self.coords[n][1] = self.coords[atom_c][1] + yb * rbc self.coords[n][2] = self.coords[atom_c][2] + zb * rbc else: xa = self.coords[atom_a][0] - self.coords[atom_c][0] ya = self.coords[atom_a][1] - self.coords[atom_c][1] za = self.coords[atom_a][2] - self.coords[atom_c][2] xyb = sqrt(xb * xb + yb * yb) inv = False if xyb < 0.001: xpa = za za = -xa xa = xpa xpb = zb zb = -xb xb = xpb xyb = sqrt(xb * xb + yb * yb) inv = True costh = xb / xyb sinth = yb / xyb xpa = xa * costh + ya * sinth ypa = ya * costh - xa * sinth sinph = zb * rbc cosph = sqrt(abs(1.0 - sinph * sinph)) xqa = xpa * cosph + za * sinph zqa = za * cosph - xpa * sinph yza = sqrt(ypa * ypa + zqa * zqa) if yza < 1e-8: coskh = 1.0 sinkh = 0.0 else: coskh = ypa / yza sinkh = zqa / yza # Apply the peptide bond conformation if symbol != "P": if name == "N " and not init_pos: t = self.prev_psi + 0.0 if name == "O ": t = psi + 180.0 if name == "HA " or name == "HA2": t = 120.0 + phi if name == "CB " or name == "HA3": t = 240.0 + phi if name == "C ": t = phi else: # proline if name == "N " and not init_pos: t = self.prev_psi + 0.0 if name == "O ": t = psi + 180.0 if name == "CA ": t = phi - 120.0 if name == "CD ": t = phi + 60.0 sina = sin(DEG2RAD * a) sind = -sin(DEG2RAD * t) cosd = cos(DEG2RAD * t) # Apply the bond length. xd = r * cosa yd = r * sina * cosd zd = r * sina * sind # Compute the atom position using bond and torsional angles. ypd = yd * coskh - zd * sinkh zpd = zd * coskh + yd * sinkh xpd = xd * cosph - zpd * sinph zqd = zpd * cosph + xd * sinph xqd = xpd * costh - ypd * sinth yqd = ypd * costh + xpd * sinth if inv: tmp = -zqd zqd = xqd xqd = tmp self.coords[n][0] = xqd + self.coords[atom_c][0] self.coords[n][1] = yqd + self.coords[atom_c][1] self.coords[n][2] = zqd + self.coords[atom_c][2] if self.nterm_hydrogen: # It is a hack for the first hydrogen atom # to make sure the bond length is correct. self.nterm_hydrogen.setposn(self.nterm_hydrogen.posn() + 0.325 * norm(V(xqd, yqd, zqd))) self.nterm_hydrogen = None ax = self.coords[n][0] ay = self.coords[n][1] az = self.coords[n][2] # Store previous coordinates for the next building step if not init_pos: if name == "N ": self.prev_coords[0][0] = self.coords[n][0] self.prev_coords[0][1] = self.coords[n][1] self.prev_coords[0][2] = self.coords[n][2] if name == "CA ": self.prev_coords[1][0] = self.coords[n][0] self.prev_coords[1][1] = self.coords[n][1] self.prev_coords[1][2] = self.coords[n][2] if name == "C ": self.prev_coords[2][0] = self.coords[n][0] self.prev_coords[2][1] = self.coords[n][1] self.prev_coords[2][2] = self.coords[n][2] # Add a new atom to the molecule atom = Atom( atom_name, V(self.coords[n][0], self.coords[n][1], self.coords[n][2]), mol) # Create temporary attributes for proper bond assignment. atom._is_aromatic = False atom._is_single = False if atom_type == "sp2a": atom_type = "sp2" atom._is_aromatic = True if atom_type == "sp2s": atom_type = "sp2" atom._is_single = True atom.set_atomtype_but_dont_revise_singlets(atom_type) if name == "CA ": # Set c-alpha flag for protein main chain visualization. atom._protein_ca = True else: atom._protein_ca = False if name == "CB ": # Set c-alpha flag for protein main chain visualization. atom._protein_cb = True else: atom._protein_cb = False if name == "N ": # Set c-alpha flag for protein main chain visualization. atom._protein_n = True else: atom._protein_n = False if name == "C ": # Set c-alpha flag for protein main chain visualization. atom._protein_c = True else: atom._protein_c = False if name == "O ": # Set c-alpha flag for protein main chain visualization. atom._protein_o = True else: atom._protein_o = False # debug - output in PDB format # print "ATOM %5d %-3s %3s %c%4d %8.3f%8.3f%8.3f" % ( n, name, "ALA", ' ', res_num, coords[n][0], coords[n][1], coords[n][2]) self.prev_psi = psi # Remember previous psi angle. self.length += 1 # Increase the amino acid counter. return
def add(element, x, y, z, atomtype='sp2'): atm = Atom(element, V(x, y, z), mol) atm.set_atomtype_but_dont_revise_singlets(atomtype) return atm
def build_struct(self, name, params, position, mol=None, createPrinted=False): """ Build a peptide from a sequence entered through the Property Manager dialog. """ if len(self.peptide_cache) == 0: return None # Create a molecule mol = Chunk(self.win.assy, name) # Generate dummy atoms positions self.prev_coords[0][0] = position[0] - 1.499 self.prev_coords[0][1] = position[1] + 1.539 self.prev_coords[0][2] = position[2] self.prev_coords[1][0] = position[0] - 1.499 self.prev_coords[1][1] = position[1] self.prev_coords[1][2] = position[2] self.prev_coords[2][0] = position[0] self.prev_coords[2][1] = position[1] self.prev_coords[2][2] = position[2] # Add a N-terminal hydrogen atom = Atom("H", position, mol) atom._is_aromatic = False atom._is_single = False self.nterm_hydrogen = atom # Generate the peptide chain. self.length = 1 for index, phi, psi in self.peptide_cache: name, short_name, symbol, zmatrix, size = AMINO_ACIDS[index] self._buildResiduum(mol, zmatrix, size, phi, psi, None, symbol) # Add a C-terminal OH group self._buildResiduum(mol, CTERM_ZMATRIX, 5, 0.0, 0.0, None, symbol) # Compute bonds (slow!) # This should be replaced by a proper bond assignment. inferBonds(mol) mol._protein_helix = [] mol._protein_sheet = [] # Assign proper bond orders. i = 1 for atom in mol.atoms.itervalues(): if self.ss_idx == 1: mol._protein_helix.append(i) elif self.ss_idx == 2: mol._protein_sheet.append(i) if atom.bonds: for bond in atom.bonds: if bond.atom1.getAtomTypeName()=="sp2" and \ bond.atom2.getAtomTypeName()=="sp2": if (bond.atom1._is_aromatic and bond.atom2._is_aromatic): bond.set_v6(V_AROMATIC) elif ((bond.atom1._is_aromatic == False and bond.atom1._is_aromatic == False) and not (bond.atom1._is_single and bond.atom2._is_single)): bond.set_v6(V_DOUBLE) i += 1 # Remove temporary attributes. for atom in mol.atoms.itervalues(): del atom._is_aromatic del atom._is_single return mol
def build_struct(self, name, params, position, mol=None, createPrinted=False): """ Build a peptide from a sequence entered through the Property Manager dialog. """ if len(self.peptide_cache) == 0: return None # Create a molecule mol = Chunk(self.win.assy,name) # Generate dummy atoms positions self.prev_coords[0][0] = position[0] - 1.499 self.prev_coords[0][1] = position[1] + 1.539 self.prev_coords[0][2] = position[2] self.prev_coords[1][0] = position[0] - 1.499 self.prev_coords[1][1] = position[1] self.prev_coords[1][2] = position[2] self.prev_coords[2][0] = position[0] self.prev_coords[2][1] = position[1] self.prev_coords[2][2] = position[2] # Add a N-terminal hydrogen atom = Atom("H", position, mol) atom._is_aromatic = False atom._is_single = False self.nterm_hydrogen = atom # Generate the peptide chain. self.length = 1 for index, phi, psi in self.peptide_cache: name, short_name, symbol, zmatrix, size = AMINO_ACIDS[index] self._buildResiduum(mol, zmatrix, size, phi, psi, None, symbol) # Add a C-terminal OH group self._buildResiduum(mol, CTERM_ZMATRIX, 5, 0.0, 0.0, None, symbol) # Compute bonds (slow!) # This should be replaced by a proper bond assignment. inferBonds(mol) mol._protein_helix = [] mol._protein_sheet = [] # Assign proper bond orders. i = 1 for atom in mol.atoms.itervalues(): if self.ss_idx == 1: mol._protein_helix.append(i) elif self.ss_idx == 2: mol._protein_sheet.append(i) if atom.bonds: for bond in atom.bonds: if bond.atom1.getAtomTypeName()=="sp2" and \ bond.atom2.getAtomTypeName()=="sp2": if (bond.atom1._is_aromatic and bond.atom2._is_aromatic): bond.set_v6(V_AROMATIC) elif ((bond.atom1._is_aromatic == False and bond.atom1._is_aromatic == False) and not (bond.atom1._is_single and bond.atom2._is_single)): bond.set_v6(V_DOUBLE) i += 1 # Remove temporary attributes. for atom in mol.atoms.itervalues(): del atom._is_aromatic del atom._is_single return mol
def addCarbons(chopSpace, R=radius): # a buckyball has no more than about 6*r**2 atoms, r in angstroms # each cap is ideally a half-buckyball for i in range(int(3.0 * R**2)): regional_singlets = filter(lambda atm: chopSpace(atm) and atm.is_singlet(), mol.atoms.values()) for s in regional_singlets: s.setposn(projectOntoSphere(s.posn())) if len(regional_singlets) < 3: # there won't be anything to bond to anyway, let the user # manually adjust the geometry return singlet_pair = None try: for s1 in regional_singlets: s1p = s1.posn() for s2 in regional_singlets: if s2.key > s1.key and \ vlen(s2.posn() - s1p) < bondlength: singlet_pair = (s1, s2) # break out of both for-loops raise Exception except: pass if singlet_pair is not None: # if there is an existing pair of singlets that's close than one bond # length, use those to make the newguy, so he'll have one open bond left sing1, sing2 = singlet_pair owner1, owner2 = sing1.realNeighbors()[0], sing2.realNeighbors()[0] newpos1 = walk_great_circle(owner1.posn(), sing1.posn(), bondlength) newpos2 = walk_great_circle(owner2.posn(), sing2.posn(), bondlength) newpos = 0.5 * (newpos1 + newpos2) regional_singlets.remove(sing1) regional_singlets.remove(sing2) else: # otherwise choose any pre-existing bond and stick the newguy on him # prefer a bond whose real atom already has two real neighbors preferred = filter(lambda atm: len(atm.realNeighbors()[0].realNeighbors()) == 2, regional_singlets) if preferred: sing = preferred[0] else: sing = regional_singlets[0] owner = sing.realNeighbors()[0] newpos = walk_great_circle(owner.posn(), sing.posn(), bondlength) regional_singlets.remove(sing) ngen = NeighborhoodGenerator(mol.atoms.values(), 1.1 * bondlength) # do not include new guy in neighborhood, add him afterwards newguy = Atom('C', newpos, mol) newguy.set_atomtype('sp2') # if the new atom is close to an older atom, merge them: kill the newer # atom, give the older one its neighbors, nudge the older one to the midpoint for oldguy in ngen.region(newpos): if vlen(oldguy.posn() - newpos) < 0.4: newpos = 0.5 * (newguy.posn() + oldguy.posn()) newguy.setposn(newpos) ngen.remove(oldguy) oldguy.kill() break # Bond with anybody close enough. The newer make_bonds # code doesn't seem to handle this usage very well. for oldguy in ngen.region(newpos): r = oldguy.posn() - newpos rlen = vlen(r) if (len(newguy.realNeighbors()) < 3 and rlen < 1.1 * bondlength): if rlen < 0.7 * bondlength: # nudge them apart nudge = ((0.7 * bondlength - rlen) / rlen) * r oldguy.setposn(oldguy.posn() + 0.5 * r) newguy.setposn(newguy.posn() - 0.5 * r) bond_atoms(newguy, oldguy, V_GRAPHITE) cleanupSinglets(newguy) cleanupSinglets(oldguy) if len(newguy.realNeighbors()) > 3: print 'warning: too many bonds on newguy' # Try moving the new guy around to make his bonds closer to bondlength but # keep him on or near the surface of the sphere. Use Newton's method in # three dimensions. def error(posn): e = (vlen(posn - sphere_center) - radius) ** 2 for atm in newguy.realNeighbors(): e += (vlen(atm.posn() - posn) - bondlength)**2 return e p = newguy.posn() for i in range(2): h = 1.0e-4 e0 = error(p) gradient = V((error(p + V(h, 0, 0)) - e0) / h, (error(p + V(0, h, 0)) - e0) / h, (error(p + V(0, 0, h)) - e0) / h) p = p - (e0 / vlen(gradient)**2) * gradient newguy.setposn(p) # we may need to reposition singlets for atm in ngen.region(newguy.posn()): cleanupSinglets(atm) cleanupSinglets(newguy)
def _buildResidue(self, mol, zmatrix, n_atoms, idx, phi, psi, secondary, init_pos, residue_name, fake_chain=False): """ Builds cartesian coordinates for an amino acid from the internal coordinates table. @param mol: a chunk to which the amino acid will be added. @type mol: Chunk @param zmatrix: is an internal coordinates array corresponding to a given amino acid. @type zmatrix: list @param n_atoms: size of z-matrix (a number of atoms to be build + 3 dummy atoms) @type n_atoms: int @param idx: is a residue index (1..length). @type idx: integer @param phi, psi: peptide bond phi and psi angles @type phi, psi: float @param init_pos: optional postions of previous CA, C and O atoms. @type init_pos: V @param symbol: current amino acid symbol (used to derermine proline case) @type symbol: string """ # note: currently, it doesn't rebuild bonds, so inferBonds has to be # called after this method. Unfortunately, the proper bond order can # not be correctly recognized this way. Therefore, temporary atom flags # _is_aromatic and _is_single are used. #this code was re-factored by EricM and internal-to-cartesian # conversion method was moved to geometry.InternalCoordinatesToCartesian if mol is None: return if not init_pos: # assign three previous atom positions coords = self.prev_coords else: # if no prev_coords are given, compute the first three atom positions coords = zeros([3,3], Float) num, name, atom_name, atom_type, \ atom_c, atom_b, atom_a, r, a, t = zmatrix[1] coords[0][0] = 0.0; coords[0][1] = 0.0; coords[0][2] = 0.0; coords[1][0] = r; coords[1][1] = 0.0; coords[1][2] = 0.0; ccos = cos(DEG2RAD*a) num, name, atom_name, atom_type, \ atom_c, atom_b, atom_a, r, a, t = zmatrix[2] if atom_c == 1: coords[2][0] = coords[0][0] + r*ccos else: coords[2][0] = coords[0][0] - r*ccos coords[2][1] = r * sin(DEG2RAD*a) coords[2][2] = 0.0 for i in range (0, 3): self.prev_coords[i][0] = coords[i][0] + init_pos[0] self.prev_coords[i][1] = coords[i][1] + init_pos[1] self.prev_coords[i][2] = coords[i][2] + init_pos[2] translator = InternalCoordinatesToCartesian(n_atoms, coords) for n in range (3, n_atoms): # Generate all coordinates using three previous atoms # as a frame of reference, num, name, atom_name, atom_type, \ atom_c, atom_b, atom_a, r, a, t = zmatrix[n] # Apply the peptide bond conformation if residue_name != "PRO": if name == "N " and not init_pos: t = self.prev_psi + 0.0 if name == "O ": t = psi + 180.0 if name == "HA " or name == "HA2": t = 120.0 + phi if name == "CB " or name == "HA3": t = 240.0 + phi if name == "C ": t = phi else: # proline if name == "N " and not init_pos: t = self.prev_psi + 0.0 if name == "O ": t = psi + 180.0 if name == "CA ": t = phi - 120.0 if name == "CD ": t = phi + 60.0 translator.addInternal(n+1, atom_c+1, atom_b+1, atom_a+1, r, a, t) xyz = translator.getCartesian(n+1) if self.nterm_hydrogen: # This is a hack for the first hydrogen atom # to make sure the bond length is correct. self.nterm_hydrogen.setposn( self.nterm_hydrogen.posn() + \ 0.325 * norm(xyz)) self.nterm_hydrogen = None # Store previous coordinates for the next building step if not init_pos: if name=="N ": self.prev_coords[0][0] = xyz[0] self.prev_coords[0][1] = xyz[1] self.prev_coords[0][2] = xyz[2] if name=="CA ": self.prev_coords[1][0] = xyz[0] self.prev_coords[1][1] = xyz[1] self.prev_coords[1][2] = xyz[2] if name=="C ": self.prev_coords[2][0] = xyz[0] self.prev_coords[2][1] = xyz[1] self.prev_coords[2][2] = xyz[2] # Add a new atom to the molecule if not fake_chain or \ name == "CA ": atom = Atom( atom_name, xyz, mol) if not self.init_ca and \ name == "CA ": self.init_ca = atom if mol.protein: aa = mol.protein.add_pdb_atom(atom, name.replace(' ',''), idx, AA_3_TO_1[residue_name]) atom.pdb_info = {} atom.pdb_info['atom_name'] = name.replace(' ','') atom.pdb_info['residue_name'] = residue_name residue_id = "%3d " % idx atom.pdb_info['residue_id'] = residue_id atom.pdb_info['standard_atom'] = True atom.pdb_info['chain_id'] = True if aa: aa.set_secondary_structure(secondary) # Create temporary attributes for proper bond assignment. atom._is_aromatic = False atom._is_single = False if atom_type == "sp2a": atom_type = "sp2" atom._is_aromatic = True if atom_type == "sp2s": atom_type = "sp2" atom._is_single = True atom.set_atomtype_but_dont_revise_singlets(atom_type) ### debug - output in PDB format ### print "ATOM %5d %-3s %3s %c%4d %8.3f%8.3f%8.3f" % ( n, name, "ALA", ' ', res_num, xyz[0], xyz[1], xyz[2]) self.prev_psi = psi # Remember previous psi angle. return
def _buildResidue(self, mol, zmatrix, n_atoms, idx, phi, psi, secondary, init_pos, residue_name, fake_chain=False): """ Builds cartesian coordinates for an amino acid from the internal coordinates table. @param mol: a chunk to which the amino acid will be added. @type mol: Chunk @param zmatrix: is an internal coordinates array corresponding to a given amino acid. @type zmatrix: list @param n_atoms: size of z-matrix (a number of atoms to be build + 3 dummy atoms) @type n_atoms: int @param idx: is a residue index (1..length). @type idx: integer @param phi, psi: peptide bond phi and psi angles @type phi, psi: float @param init_pos: optional postions of previous CA, C and O atoms. @type init_pos: V @param symbol: current amino acid symbol (used to derermine proline case) @type symbol: string """ # note: currently, it doesn't rebuild bonds, so inferBonds has to be # called after this method. Unfortunately, the proper bond order can # not be correctly recognized this way. Therefore, temporary atom flags # _is_aromatic and _is_single are used. #this code was re-factored by EricM and internal-to-cartesian # conversion method was moved to geometry.InternalCoordinatesToCartesian if mol is None: return if not init_pos: # assign three previous atom positions coords = self.prev_coords else: # if no prev_coords are given, compute the first three atom positions coords = zeros([3, 3], Float) num, name, atom_name, atom_type, \ atom_c, atom_b, atom_a, r, a, t = zmatrix[1] coords[0][0] = 0.0 coords[0][1] = 0.0 coords[0][2] = 0.0 coords[1][0] = r coords[1][1] = 0.0 coords[1][2] = 0.0 ccos = cos(DEG2RAD * a) num, name, atom_name, atom_type, \ atom_c, atom_b, atom_a, r, a, t = zmatrix[2] if atom_c == 1: coords[2][0] = coords[0][0] + r * ccos else: coords[2][0] = coords[0][0] - r * ccos coords[2][1] = r * sin(DEG2RAD * a) coords[2][2] = 0.0 for i in range(0, 3): self.prev_coords[i][0] = coords[i][0] + init_pos[0] self.prev_coords[i][1] = coords[i][1] + init_pos[1] self.prev_coords[i][2] = coords[i][2] + init_pos[2] translator = InternalCoordinatesToCartesian(n_atoms, coords) for n in range(3, n_atoms): # Generate all coordinates using three previous atoms # as a frame of reference, num, name, atom_name, atom_type, \ atom_c, atom_b, atom_a, r, a, t = zmatrix[n] # Apply the peptide bond conformation if residue_name != "PRO": if name == "N " and not init_pos: t = self.prev_psi + 0.0 if name == "O ": t = psi + 180.0 if name == "HA " or name == "HA2": t = 120.0 + phi if name == "CB " or name == "HA3": t = 240.0 + phi if name == "C ": t = phi else: # proline if name == "N " and not init_pos: t = self.prev_psi + 0.0 if name == "O ": t = psi + 180.0 if name == "CA ": t = phi - 120.0 if name == "CD ": t = phi + 60.0 translator.addInternal(n + 1, atom_c + 1, atom_b + 1, atom_a + 1, r, a, t) xyz = translator.getCartesian(n + 1) if self.nterm_hydrogen: # This is a hack for the first hydrogen atom # to make sure the bond length is correct. self.nterm_hydrogen.setposn( self.nterm_hydrogen.posn() + \ 0.325 * norm(xyz)) self.nterm_hydrogen = None # Store previous coordinates for the next building step if not init_pos: if name == "N ": self.prev_coords[0][0] = xyz[0] self.prev_coords[0][1] = xyz[1] self.prev_coords[0][2] = xyz[2] if name == "CA ": self.prev_coords[1][0] = xyz[0] self.prev_coords[1][1] = xyz[1] self.prev_coords[1][2] = xyz[2] if name == "C ": self.prev_coords[2][0] = xyz[0] self.prev_coords[2][1] = xyz[1] self.prev_coords[2][2] = xyz[2] # Add a new atom to the molecule if not fake_chain or \ name == "CA ": atom = Atom(atom_name, xyz, mol) if not self.init_ca and \ name == "CA ": self.init_ca = atom if mol.protein: aa = mol.protein.add_pdb_atom(atom, name.replace(' ', ''), idx, AA_3_TO_1[residue_name]) atom.pdb_info = {} atom.pdb_info['atom_name'] = name.replace(' ', '') atom.pdb_info['residue_name'] = residue_name residue_id = "%3d " % idx atom.pdb_info['residue_id'] = residue_id atom.pdb_info['standard_atom'] = True atom.pdb_info['chain_id'] = True if aa: aa.set_secondary_structure(secondary) # Create temporary attributes for proper bond assignment. atom._is_aromatic = False atom._is_single = False if atom_type == "sp2a": atom_type = "sp2" atom._is_aromatic = True if atom_type == "sp2s": atom_type = "sp2" atom._is_single = True atom.set_atomtype_but_dont_revise_singlets(atom_type) ### debug - output in PDB format ### print "ATOM %5d %-3s %3s %c%4d %8.3f%8.3f%8.3f" % ( n, name, "ALA", ' ', res_num, xyz[0], xyz[1], xyz[2]) self.prev_psi = psi # Remember previous psi angle. return
def _get_atomlist_from_gms_outfile(assy, filename): """ Read the atoms from a GAMESS OUT file into an atom list, which is returned, unless there are no atoms in the file, in which case a warning is printed and None is returned. """ fi = open(filename, "rU") lines = fi.readlines() fi.close() dir, nodename = os.path.split(filename) mol = Chunk(assy, nodename) newAtomList = [] countdown = 0 equilibruim_found = False atoms_found = False for card in lines: if failpat.search( card): # GAMESS Aborted. No atom data will be found. print card env.history.message(redmsg(card)) break if noconvpat.search(card): # Geometry search is not converged. print card env.history.message(redmsg(card)) break # If this card is found: # "1 ***** EQUILIBRIUM GEOMETRY LOCATED *****\n" # we know we have a successfully optimized structure/set of atoms. # If this card is not found, the optimization failed for some reason. # Atom positions begin soon after this card. if card == "1 ***** EQUILIBRIUM GEOMETRY LOCATED *****\n": equilibruim_found = True continue # The atom positions we want ALWAYS begin 2 lines after this card: # " COORDINATES OF ALL ATOMS ARE (ANGS)\n" # which follows the previous card. # This is one way to fix the problem mentioned above. # I've commented the code below out since it needs further work to do what # we need, and there is a chance we will not need this if GAMESS-US has # the same number of lines (6) after the "EQUILIBRIUM" card above. # # P.S. The reason we do not just look for this card by itself is that there # can be many of them. There is only one "EQUILIBRIUM" card, and the # good atoms follow that card. # 050624 Mark if equilibruim_found: if card == " COORDINATES OF ALL ATOMS ARE (ANGS)\n": atoms_found = True reading_atoms = True countdown = 2 continue if not equilibruim_found or not atoms_found: continue if countdown: countdown -= 1 # print countdown, card # for debugging only. continue # The current card contains atom type and position. n = 0 if reading_atoms: # print "_get_atomlist_from_gms_outfile:", card if len(card) < 10: reading_atoms = False # Finished reading atoms. break m = irecpat.match(card) sym = capitalize(m.group(1)) try: PeriodicTable.getElement(sym) except: env.history.message( redmsg( "Warning: GAMESS OUT file: unknown element %s in: %s" % (sym, card))) else: xyz = map(float, (m.group(2), m.group(3), m.group(4))) a = Atom(sym, A(xyz), mol) newAtomList += [a] # Let caller handle history msgs. Mark 050712 # if not newAtomList: # msg = "Warning: GAMESS file contains no equilibrium geometry. No atoms read into part." # env.history.message( redmsg(msg)) # return None return newAtomList
def _readgms(assy, filename, isInsert=False): """ Read the atoms from a GAMESS DAT file into a single new chunk, which is returned, unless there are no atoms in the file, in which case a warning is printed and None is returned. (The new chunk (if returned) is in assy, but is not yet added into any Group or Part in assy -- caller must do that.) """ fi = open(filename, "rU") lines = fi.readlines() fi.close() dir, nodename = os.path.split(filename) ndix = {} mol = Chunk(assy, nodename) countdown = 0 equilibruim_found = False atoms_found = False for card in lines: if failpat.search( card): # GAMESS Aborted. No atom data will be found. print card break # If this card is found: # "1 ***** EQUILIBRIUM GEOMETRY LOCATED *****\n" # we know we have a successfully optimized structure/set of atoms. # If this card is not found, the optimization failed for some reason. # Atom positions begin soon after this card. if card == "1 ***** EQUILIBRIUM GEOMETRY LOCATED *****\n": equilibruim_found = True continue # The atom positions we want ALWAYS begin 2 lines after this card: # " COORDINATES OF ALL ATOMS ARE (ANGS)\n" # which follows the previous card. # This is one way to fix the problem mentioned above. # I've commented the code below out since it needs further work to do what # we need, and there is a chance we will not need this if GAMESS-US has # the same number of lines (6) after the "EQUILIBRIUM" card above. # # P.S. The reason we do not just look for this card by itself is that there # can be many of them. There is only one "EQUILIBRIUM" card, and the # good atoms follow that card. # 050624 Mark if equilibruim_found: if card == " COORDINATES OF ALL ATOMS ARE (ANGS)\n": atoms_found = True reading_atoms = True countdown = 2 continue if not equilibruim_found or not atoms_found: continue if countdown: countdown -= 1 # print countdown, card # for debugging only. continue # The current card contains atom type and position. n = 0 if reading_atoms: if len(card) < 10: reading_atoms = False # Finished reading atoms. break m = irecpat.match(card) sym = capitalize(m.group(1)) try: PeriodicTable.getElement(sym) except: env.history.message( redmsg( "Warning: GAMESS DAT file: unknown element %s in: %s" % (sym, card))) else: xyz = map(float, (m.group(2), m.group(3), m.group(4))) a = Atom(sym, A(xyz), mol) ndix[n] = a n += 1 # Don't return an empty chunk. if not mol.atoms: msg = "Warning: GAMESS file contains no equilibrium geometry. No atoms read into part." env.history.message(redmsg(msg)) return None # Need to compute and add bonds for this chunk. I'll ask Bruce how to best accomplish this. # In the meantime, let's warn the user that no bonds have been formed since it # is impossible to see this in vdW display mode. # Mark 050623. msg = "Warning: Equilibrium geometry found. Atoms read into part, but there are no bonds." env.history.message(orangemsg(msg)) return mol
# note: defining __eq__ is sufficient, but only because we inherit # from DataMixin, which defines __ne__ based on __eq__ return self.__class__ is other.__class__ and \ (self.mirrorQ, self.rot) == (other.mirrorQ, other.rot) pass # end of class image_mod_record # == test code if __name__ == '__main__': nopos = V(0,0,0) #bruce 060308 replaced 'no' with nopos (w/o knowing if it was correct #in the first place) alist = [Atom('C', nopos, None), Atom('C', nopos, None), Atom('H', nopos, None), Atom('O', nopos, None), ] assert getMultiplicity(alist) == 2 alist += [Atom('N', nopos, None), ] assert getMultiplicity(alist) == 1 print "Test succeed, no assertion error." #end
def _readpdb(assy, filename, isInsert = False, showProgressDialog = False, chainId = None): """ Read a Protein DataBank-format file into a single new chunk, which is returned unless there are no atoms in the file, in which case a warning is printed and None is returned. (The new chunk (if returned) is in assy, but is not yet added into any Group or Part in assy -- caller must do that.) Unless isInsert = True, set assy.filename to match the file we read, even if we return None. @param assy: The assembly. @type assy: L{assembly} @param filename: The PDB filename to read. @type filename: string @param isInsert: If True, the PDB file will be inserted into the current assembly. If False (default), the PDB is opened as the assembly. @param isInsert: boolean @param showProgressDialog: if True, display a progress dialog while reading a file. @type showProgressDialog: boolean @return: A chunk containing the contents of the PDB file. @rtype: L{Chunk} @see: U{B{PDB File Format}<http://www.wwpdb.org/documentation/format23/v2.3.html>} """ fi = open(filename,"rU") lines = fi.readlines() fi.close() dir, nodename = os.path.split(filename) if not isInsert: assy.filename = filename ndix = {} mol = Chunk(assy, nodename) numconects = 0 atomname_exceptions = { "HB":"H", #k these are all guesses -- I can't find this documented # anywhere [bruce 070410] ## "HE":"H", ### REVIEW: I'm not sure about this one -- ### leaving it out means it's read as Helium, # but including it erroneously might prevent reading an actual Helium # if that was intended. # Guess for now: include it for ATOM but not HETATM. (So it's # specialcased below, rather than being included in this table.) # (Later: can't we use the case of the 'E' to distinguish it from He?) "HN":"H", } # Create and display a Progress dialog while reading the MMP file. # One issue with this implem is that QProgressDialog always displays # a "Cancel" button, which is not hooked up. I think this is OK for now, # but later we should either hook it up or create our own progress # dialog that doesn't include a "Cancel" button. --mark 2007-12-06 if showProgressDialog: _progressValue = 0 _progressFinishValue = len(lines) win = env.mainwindow() win.progressDialog.setLabelText("Reading file...") win.progressDialog.setRange(0, _progressFinishValue) _progressDialogDisplayed = False _timerStart = time.time() for card in lines: key = card[:6].lower().replace(" ", "") if key in ["atom", "hetatm"]: ## sym = capitalize(card[12:14].replace(" ", "").replace("_", "")) # bruce 080508 revision (guess at a bugfix for reading NE1-saved # pdb files): # get a list of atomnames to try; use the first one we recognize. # Note that full atom name is in columns 13-16 i.e. card[12:16]; # see http://www.wwpdb.org/documentation/format2.3-0108-us.pdf, # page 156. The old code only looked at two characters, # card[12:14] == columns 13-14, and discarded ' ' and '_', # and capitalized (the first character only). The code as I revised # it on 070410 also discarded digits, and handled HB, HE, HN # (guesses) using the atomname_exceptions dict. name4 = card[12:16].replace(" ", "").replace("_", "") name3 = card[12:15].replace(" ", "").replace("_", "") name2 = card[12:14].replace(" ", "").replace("_", "") def nodigits(name): for bad in "0123456789": name = name.replace(bad, "") return name atomnames_to_try = [ name4, # as seems best according to documentation name3, name2, # like old code nodigits(name4), nodigits(name3), nodigits(name2) # like code as revised on 070410 ] foundit = False for atomname in atomnames_to_try: atomname = atomname_exceptions.get(atomname, atomname) if atomname == "HE" and key == "atom": atomname = "H" # see comment in atomname_exceptions sym = capitalize(atomname) # turns either 'he' or 'HE' into 'He' try: PeriodicTable.getElement(sym) except: # note: this typically fails with AssertionError # (not e.g. KeyError) [bruce 050322] continue else: foundit = True break pass if not foundit: msg = "Warning: Pdb file: will use Carbon in place of unknown element %s in: %s" \ % (name4, card) print msg #bruce 070410 added this print env.history.message( redmsg( msg )) ##e It would probably be better to create a fake atom, so the # CONECT records would still work. #bruce 080508 let's do that: sym = "C" # Better still might be to create a fake element, # so we could write out the pdb file again # (albeit missing lots of info). [bruce 070410 comment] # Note: an advisor tells us: # PDB files sometimes encode atomtypes, # using C_R instead of C, for example, to represent sp2 # carbons. # That particular case won't trigger this exception, since we # only look at 2 characters [eventually, after trying more, as of 080508], # i.e. C_ in that case. It would be better to realize this means # sp2 and set the atomtype here (and perhaps then use it when # inferring bonds, which we do later if the file doesn't have # any bonds). [bruce 060614/070410 comment] # Now the element name is in sym. xyz = map(float, [card[30:38], card[38:46], card[46:54]] ) n = int(card[6:11]) a = Atom(sym, A(xyz), mol) ndix[n] = a elif key == "conect": try: a1 = ndix[int(card[6:11])] except: #bruce 050322 added this level of try/except and its message; # see code below for at least two kinds of errors this might # catch, but we don't try to distinguish these here. BTW this # also happens as a consequence of not finding the element # symbol, above, since atoms with unknown elements are not # created. env.history.message( redmsg( "Warning: Pdb file: can't find first atom in CONECT record: %s" % (card,) )) else: for i in range(11, 70, 5): try: a2 = ndix[int(card[i:i+5])] except ValueError: # bruce 050323 comment: # we assume this is from int('') or int(' ') etc; # this is the usual way of ending this loop. break except KeyError: #bruce 050322-23 added history warning for this, # assuming it comes from ndix[] lookup. env.history.message( redmsg( "Warning: Pdb file: can't find atom %s in: %s" % (card[i:i+5], card) )) continue bond_atoms(a1, a2) numconects += 1 if showProgressDialog: # Update the progress dialog. _progressValue += 1 if _progressValue >= _progressFinishValue: win.progressDialog.setLabelText("Building model...") elif _progressDialogDisplayed: win.progressDialog.setValue(_progressValue) else: _timerDuration = time.time() - _timerStart if _timerDuration > 0.25: # Display progress dialog after 0.25 seconds win.progressDialog.setValue(_progressValue) _progressDialogDisplayed = True if showProgressDialog: # Make the progress dialog go away. win.progressDialog.setValue(_progressFinishValue) #bruce 050322 part of fix for bug 433: don't return an empty chunk if not mol.atoms: env.history.message( redmsg( "Warning: Pdb file contained no atoms")) return None if numconects == 0: msg = orangemsg("PDB file has no bond info; inferring bonds") env.history.message(msg) # let user see message right away (bond inference can take significant # time) [bruce 060620] env.history.h_update() inferBonds(mol) return mol
def addCarbons(chopSpace, R=radius): # a buckyball has no more than about 6*r**2 atoms, r in angstroms # each cap is ideally a half-buckyball for i in range(int(3.0 * R**2)): regional_singlets = filter( lambda atm: chopSpace(atm) and atm.is_singlet(), mol.atoms.values()) for s in regional_singlets: s.setposn(projectOntoSphere(s.posn())) if len(regional_singlets) < 3: # there won't be anything to bond to anyway, let the user # manually adjust the geometry return singlet_pair = None try: for s1 in regional_singlets: s1p = s1.posn() for s2 in regional_singlets: if s2.key > s1.key and \ vlen(s2.posn() - s1p) < bondlength: singlet_pair = (s1, s2) # break out of both for-loops raise Exception except: pass if singlet_pair is not None: # if there is an existing pair of singlets that's close than one bond # length, use those to make the newguy, so he'll have one open bond left sing1, sing2 = singlet_pair owner1, owner2 = sing1.realNeighbors()[0], sing2.realNeighbors( )[0] newpos1 = walk_great_circle(owner1.posn(), sing1.posn(), bondlength) newpos2 = walk_great_circle(owner2.posn(), sing2.posn(), bondlength) newpos = 0.5 * (newpos1 + newpos2) regional_singlets.remove(sing1) regional_singlets.remove(sing2) else: # otherwise choose any pre-existing bond and stick the newguy on him # prefer a bond whose real atom already has two real neighbors preferred = filter( lambda atm: len(atm.realNeighbors()[0].realNeighbors()) == 2, regional_singlets) if preferred: sing = preferred[0] else: sing = regional_singlets[0] owner = sing.realNeighbors()[0] newpos = walk_great_circle(owner.posn(), sing.posn(), bondlength) regional_singlets.remove(sing) ngen = NeighborhoodGenerator(mol.atoms.values(), 1.1 * bondlength) # do not include new guy in neighborhood, add him afterwards newguy = Atom('C', newpos, mol) newguy.set_atomtype('sp2') # if the new atom is close to an older atom, merge them: kill the newer # atom, give the older one its neighbors, nudge the older one to the midpoint for oldguy in ngen.region(newpos): if vlen(oldguy.posn() - newpos) < 0.4: newpos = 0.5 * (newguy.posn() + oldguy.posn()) newguy.setposn(newpos) ngen.remove(oldguy) oldguy.kill() break # Bond with anybody close enough. The newer make_bonds # code doesn't seem to handle this usage very well. for oldguy in ngen.region(newpos): r = oldguy.posn() - newpos rlen = vlen(r) if (len(newguy.realNeighbors()) < 3 and rlen < 1.1 * bondlength): if rlen < 0.7 * bondlength: # nudge them apart nudge = ((0.7 * bondlength - rlen) / rlen) * r oldguy.setposn(oldguy.posn() + 0.5 * r) newguy.setposn(newguy.posn() - 0.5 * r) bond_atoms(newguy, oldguy, V_GRAPHITE) cleanupSinglets(newguy) cleanupSinglets(oldguy) if len(newguy.realNeighbors()) > 3: print 'warning: too many bonds on newguy' # Try moving the new guy around to make his bonds closer to bondlength but # keep him on or near the surface of the sphere. Use Newton's method in # three dimensions. def error(posn): e = (vlen(posn - sphere_center) - radius)**2 for atm in newguy.realNeighbors(): e += (vlen(atm.posn() - posn) - bondlength)**2 return e p = newguy.posn() for i in range(2): h = 1.0e-4 e0 = error(p) gradient = V((error(p + V(h, 0, 0)) - e0) / h, (error(p + V(0, h, 0)) - e0) / h, (error(p + V(0, 0, h)) - e0) / h) p = p - (e0 / vlen(gradient)**2) * gradient newguy.setposn(p) # we may need to reposition singlets for atm in ngen.region(newguy.posn()): cleanupSinglets(atm) cleanupSinglets(newguy)
return self.__class__ is other.__class__ and \ (self.mirrorQ, self.rot) == (other.mirrorQ, other.rot) pass # end of class image_mod_record # == test code if __name__ == '__main__': nopos = V( 0, 0, 0 ) #bruce 060308 replaced 'no' with nopos (w/o knowing if it was correct in the first place) alist = [ Atom('C', nopos, None), Atom('C', nopos, None), Atom('H', nopos, None), Atom('O', nopos, None), ] assert getMultiplicity(alist) == 2 alist += [ Atom('N', nopos, None), ] assert getMultiplicity(alist) == 1 print "Test succeed, no assertion error." #end
def _buildResiduum(self, mol, zmatrix, n_atoms, phi, psi, init_pos, symbol): """ Builds cartesian coordinates for an amino acid from the internal coordinates table. mol is a chunk to which the amino acid will be added. zmatrix is an internal coordinates array corresponding to a given amino acid. n_atoms is a number of atoms to be build + 3 dummy atoms. phi is a peptide bond PHI angle. psi is a peptide bond PSI angle. init_pos are optional postions of previous CA, C and O atoms. symbol is a current amino acid symbol (used for proline case) Note: currently, it doesn't rebuild bonds, so inferBonds has to be called after. Unfortunately, the proper bond order can not be correctly recognized this way. """ if mol == None: return if not init_pos: # assign three previous atom positions for i in range (0,3): self.coords[i][0] = self.prev_coords[i][0] self.coords[i][1] = self.prev_coords[i][1] self.coords[i][2] = self.prev_coords[i][2] else: # if no prev_coords are given, compute the first three atom positions num, name, atom_name, atom_type, \ atom_c, atom_b, atom_a, r, a, t = zmatrix[1] self.coords[0][0] = 0.0; self.coords[0][1] = 0.0; self.coords[0][2] = 0.0; self.coords[1][0] = r; self.coords[1][1] = 0.0; self.coords[1][2] = 0.0; ccos = cos(DEG2RAD*a) num, name, atom_name, atom_type, \ atom_c, atom_b, atom_a, r, a, t = zmatrix[2] if atom_c == 1: self.coords[2][0] = self.coords[0][0] + r*ccos else: self.coords[2][0] = self.coords[0][0] - r*ccos self.coords[2][1] = r * sin(DEG2RAD*a) self.coords[2][2] = 0.0 for i in range (0, 3): self.prev_coords[i][0] = self.coords[i][0] + init_pos[0] self.prev_coords[i][1] = self.coords[i][1] + init_pos[1] self.prev_coords[i][2] = self.coords[i][2] + init_pos[2] for n in range (3, n_atoms): # Generate all coordinates using three previous atoms # as a frame of reference, num, name, atom_name, atom_type, \ atom_c, atom_b, atom_a, r, a, t = zmatrix[n] cosa = cos(DEG2RAD * a) xb = self.coords[atom_b][0] - self.coords[atom_c][0] yb = self.coords[atom_b][1] - self.coords[atom_c][1] zb = self.coords[atom_b][2] - self.coords[atom_c][2] rbc = 1.0 / sqrt(xb*xb + yb*yb + zb*zb) if abs(cosa) >= 0.999: # Linear bond case # Skip angles, just extend along the bond. rbc = r * rbc * cosa self.coords[n][0] = self.coords[atom_c][0] + xb*rbc self.coords[n][1] = self.coords[atom_c][1] + yb*rbc self.coords[n][2] = self.coords[atom_c][2] + zb*rbc else: xa = self.coords[atom_a][0] - self.coords[atom_c][0] ya = self.coords[atom_a][1] - self.coords[atom_c][1] za = self.coords[atom_a][2] - self.coords[atom_c][2] xyb = sqrt(xb*xb + yb*yb) inv = False if xyb < 0.001: xpa = za za = -xa xa = xpa xpb = zb zb = -xb xb = xpb xyb = sqrt(xb*xb + yb*yb) inv = True costh = xb / xyb sinth = yb / xyb xpa = xa * costh + ya * sinth ypa = ya * costh - xa * sinth sinph = zb * rbc cosph = sqrt(abs(1.0- sinph * sinph)) xqa = xpa * cosph + za * sinph zqa = za * cosph - xpa * sinph yza = sqrt(ypa * ypa + zqa * zqa) if yza < 1e-8: coskh = 1.0 sinkh = 0.0 else: coskh = ypa / yza sinkh = zqa / yza # Apply the peptide bond conformation if symbol != "P": if name == "N " and not init_pos: t = self.prev_psi + 0.0 if name == "O ": t = psi + 180.0 if name == "HA " or name == "HA2": t = 120.0 + phi if name == "CB " or name == "HA3": t = 240.0 + phi if name == "C ": t = phi else: # proline if name == "N " and not init_pos: t = self.prev_psi + 0.0 if name == "O ": t = psi + 180.0 if name == "CA ": t = phi - 120.0 if name == "CD ": t = phi + 60.0 sina = sin(DEG2RAD * a) sind = -sin(DEG2RAD * t) cosd = cos(DEG2RAD * t) # Apply the bond length. xd = r * cosa yd = r * sina * cosd zd = r * sina * sind # Compute the atom position using bond and torsional angles. ypd = yd * coskh - zd * sinkh zpd = zd * coskh + yd * sinkh xpd = xd * cosph - zpd * sinph zqd = zpd * cosph + xd * sinph xqd = xpd * costh - ypd * sinth yqd = ypd * costh + xpd * sinth if inv: tmp = -zqd zqd = xqd xqd = tmp self.coords[n][0] = xqd + self.coords[atom_c][0] self.coords[n][1] = yqd + self.coords[atom_c][1] self.coords[n][2] = zqd + self.coords[atom_c][2] if self.nterm_hydrogen: # It is a hack for the first hydrogen atom # to make sure the bond length is correct. self.nterm_hydrogen.setposn( self.nterm_hydrogen.posn() + 0.325 * norm(V(xqd, yqd, zqd))) self.nterm_hydrogen = None ax = self.coords[n][0] ay = self.coords[n][1] az = self.coords[n][2] # Store previous coordinates for the next building step if not init_pos: if name=="N ": self.prev_coords[0][0] = self.coords[n][0] self.prev_coords[0][1] = self.coords[n][1] self.prev_coords[0][2] = self.coords[n][2] if name=="CA ": self.prev_coords[1][0] = self.coords[n][0] self.prev_coords[1][1] = self.coords[n][1] self.prev_coords[1][2] = self.coords[n][2] if name=="C ": self.prev_coords[2][0] = self.coords[n][0] self.prev_coords[2][1] = self.coords[n][1] self.prev_coords[2][2] = self.coords[n][2] # Add a new atom to the molecule atom = Atom( atom_name, V(self.coords[n][0], self.coords[n][1], self.coords[n][2]), mol) # Create temporary attributes for proper bond assignment. atom._is_aromatic = False atom._is_single = False if atom_type == "sp2a": atom_type = "sp2" atom._is_aromatic = True if atom_type == "sp2s": atom_type = "sp2" atom._is_single = True atom.set_atomtype_but_dont_revise_singlets(atom_type) if name == "CA ": # Set c-alpha flag for protein main chain visualization. atom._protein_ca = True else: atom._protein_ca = False if name == "CB ": # Set c-alpha flag for protein main chain visualization. atom._protein_cb = True else: atom._protein_cb = False if name == "N ": # Set c-alpha flag for protein main chain visualization. atom._protein_n = True else: atom._protein_n = False if name == "C ": # Set c-alpha flag for protein main chain visualization. atom._protein_c = True else: atom._protein_c = False if name == "O ": # Set c-alpha flag for protein main chain visualization. atom._protein_o = True else: atom._protein_o = False # debug - output in PDB format # print "ATOM %5d %-3s %3s %c%4d %8.3f%8.3f%8.3f" % ( n, name, "ALA", ' ', res_num, coords[n][0], coords[n][1], coords[n][2]) self.prev_psi = psi # Remember previous psi angle. self.length += 1 # Increase the amino acid counter. return
def _readpdb_new(assy, filename, isInsert = False, showProgressDialog = False, chainId = None): """ Read a Protein DataBank-format file into a single new chunk, which is returned unless there are no atoms in the file, in which case a warning is printed and None is returned. (The new chunk (if returned) is in assy, but is not yet added into any Group or Part in assy -- caller must do that.) Unless isInsert = True, set assy.filename to match the file we read, even if we return None. @param assy: The assembly. @type assy: L{assembly} @param filename: The PDB filename to read. @type filename: string @param isInsert: If True, the PDB file will be inserted into the current assembly. If False (default), the PDB is opened as the assembly. @param isInsert: boolean @param showProgressDialog: if True, display a progress dialog while reading a file. @type showProgressDialog: boolean @return: A chunk containing the contents of the PDB file. @rtype: L{Chunk} @see: U{B{PDB File Format}<http://www.wwpdb.org/documentation/format23/v2.3.html>} """ from protein.model.Protein import is_water def _finish_molecule(): """ Perform some operations after reading entire PDB chain: - rebuild (infer) bonds - rename molecule to reflect a chain ID - delete protein object if this is not a protein - append the molecule to the molecule list """ if mol == water: # Skip water, to be added explicitly at the end. return if mol.atoms: ###print "READING PDB ", (mol, numconects, chainId) mol.name = pdbid.lower() + chainId ###idzialprint "SEQ = ", mol.protein.get_sequence_string() ###print "SEC = ", mol.protein.get_secondary_structure_string() if mol.protein.count_c_alpha_atoms() == 0: # If there is no C-alpha atoms, consider the chunk # as a non-protein. But! Split it into individual # hetero groups. res_list = mol.protein.get_amino_acids() assy.part.ensure_toplevel_group() hetgroup = Group("Heteroatoms", assy, assy.part.topnode) for res in res_list: hetmol = Chunk(assy, res.get_three_letter_code().replace(" ", "") + \ "[" + str(res.get_id()) + "]") for atom in res.get_atom_list(): newatom = Atom(atom.element.symbol, atom.posn(), hetmol) # New chunk - infer the bonds anyway (this is not # correct, should first check connectivity read from # the PDB file CONECT records). inferBonds(hetmol) hetgroup.addchild(hetmol) mollist.append(hetgroup) else: #if numconects == 0: # msg = orangemsg("PDB file has no bond info; inferring bonds") # env.history.message(msg) # # let user see message right away (bond inference can take significant # # time) [bruce 060620] # env.history.h_update() # For protein - infer the bonds anyway. inferBonds(mol) mol.protein.set_chain_id(chainId) mol.protein.set_pdb_id(pdbid) if mol.atoms: mollist.append(mol) else: env.history.message( redmsg( "Warning: Pdb file contained no atoms")) env.history.h_update() fi = open(filename,"rU") lines = fi.readlines() fi.close() mollist = [] # Lists of secondary structure tuples (res_id, chain_id) helix = [] sheet = [] turn = [] dir, nodename = os.path.split(filename) if not isInsert: assy.filename = filename ndix = {} mol = Chunk(assy, nodename) mol.protein = Protein() # Create a chunk for water molecules. water = Chunk(assy, nodename) numconects = 0 comment_text = "" _read_rosetta_info = False # Create a temporary PDB ID - it should be later extracted from the # file header. pdbid = nodename.replace(".pdb","").lower() atomname_exceptions = { "HB":"H", #k these are all guesses -- I can't find this documented # anywhere [bruce 070410] "CA":"C", #k these are all guesses -- I can't find this documented ## "HE":"H", ### REVIEW: I'm not sure about this one -- ### leaving it out means it's read as Helium, # but including it erroneously might prevent reading an actual Helium # if that was intended. # Guess for now: include it for ATOM but not HETATM. (So it's # specialcased below, rather than being included in this table.) # (Later: can't we use the case of the 'E' to distinguish it from He?) "HN":"H", } # Create and display a Progress dialog while reading the MMP file. # One issue with this implem is that QProgressDialog always displays # a "Cancel" button, which is not hooked up. I think this is OK for now, # but later we should either hook it up or create our own progress # dialog that doesn't include a "Cancel" button. --mark 2007-12-06 if showProgressDialog: _progressValue = 0 _progressFinishValue = len(lines) win = env.mainwindow() win.progressDialog.setLabelText("Reading file...") win.progressDialog.setRange(0, _progressFinishValue) _progressDialogDisplayed = False _timerStart = time.time() for card in lines: key = card[:6].lower().replace(" ", "") if key in ["atom", "hetatm"]: ## sym = capitalize(card[12:14].replace(" ", "").replace("_", "")) # bruce 080508 revision (guess at a bugfix for reading NE1-saved # pdb files): # get a list of atomnames to try; use the first one we recognize. # Note that full atom name is in columns 13-16 i.e. card[12:16]; # see http://www.wwpdb.org/documentation/format2.3-0108-us.pdf, # page 156. The old code only looked at two characters, # card[12:14] == columns 13-14, and discarded ' ' and '_', # and capitalized (the first character only). The code as I revised # it on 070410 also discarded digits, and handled HB, HE, HN # (guesses) using the atomname_exceptions dict. name4 = card[12:16].replace(" ", "").replace("_", "") name3 = card[12:15].replace(" ", "").replace("_", "") name2 = card[12:14].replace(" ", "").replace("_", "") chainId = card[21] resIdStr = card[22:26].replace(" ", "") if resIdStr != "": resId = int(resIdStr) else: resId = 0 resName = card[17:20] sym = card[77:78] alt = card[16] # Alternate location indicator if alt != ' ' and \ alt != 'A': # Skip non-standard alternate location # This is not very safe test, it should preserve # the remaining atoms. piotr 080715 continue ###ATOM 131 CB ARG A 18 104.359 32.924 58.573 1.00 36.93 C def nodigits(name): for bad in "0123456789": name = name.replace(bad, "") return name atomnames_to_try = [ name4, # as seems best according to documentation name3, name2, # like old code nodigits(name4), nodigits(name3), nodigits(name2) # like code as revised on 070410 ] # First, look at 77-78 field - it should include an element symbol. foundit = False try: PeriodicTable.getElement(sym) except: pass else: foundit = True if not foundit: for atomname in atomnames_to_try: atomname = atomname_exceptions.get(atomname, atomname) if atomname[0] == 'H' and key == "atom": atomname = "H" # see comment in atomname_exceptions sym = capitalize(atomname) # turns either 'he' or 'HE' into 'He' try: PeriodicTable.getElement(sym) except: # note: this typically fails with AssertionError # (not e.g. KeyError) [bruce 050322] continue else: foundit = True break pass if not foundit: msg = "Warning: Pdb file: will use Carbon in place of unknown element %s in: %s" \ % (name4, card) print msg #bruce 070410 added this print env.history.message( redmsg( msg )) ##e It would probably be better to create a fake atom, so the # CONECT records would still work. #bruce 080508 let's do that: sym = "C" # Better still might be to create a fake element, # so we could write out the pdb file again # (albeit missing lots of info). [bruce 070410 comment] # Note: an advisor tells us: # PDB files sometimes encode atomtypes, # using C_R instead of C, for example, to represent sp2 # carbons. # That particular case won't trigger this exception, since we # only look at 2 characters [eventually, after trying more, as of 080508], # i.e. C_ in that case. It would be better to realize this means # sp2 and set the atomtype here (and perhaps then use it when # inferring bonds, which we do later if the file doesn't have # any bonds). [bruce 060614/070410 comment] _is_water = is_water(resName, name4) if _is_water: tmpmol = mol mol = water # Now the element name is in sym. xyz = map(float, [card[30:38], card[38:46], card[46:54]] ) n = int(card[6:11]) a = Atom(sym, A(xyz), mol) ndix[n] = a if not _is_water: mol.protein.add_pdb_atom(a, name4, resId, resName) # Assign secondary structure. if (resId, chainId) in helix: mol.protein.assign_helix(resId) if (resId, chainId) in sheet: mol.protein.assign_strand(resId) if (resId, chainId) in turn: mol.protein.assign_turn(resId) if mol == water: mol = tmpmol elif key == "conect": try: a1 = ndix[int(card[6:11])] except: #bruce 050322 added this level of try/except and its message; # see code below for at least two kinds of errors this might # catch, but we don't try to distinguish these here. BTW this # also happens as a consequence of not finding the element # symbol, above, since atoms with unknown elements are not # created. env.history.message( redmsg( "Warning: Pdb file: can't find first atom in CONECT record: %s" % (card,) )) else: for i in range(11, 70, 5): try: a2 = ndix[int(card[i:i+5])] except ValueError: # bruce 050323 comment: # we assume this is from int('') or int(' ') etc; # this is the usual way of ending this loop. break except KeyError: #bruce 050322-23 added history warning for this, # assuming it comes from ndix[] lookup. env.history.message( redmsg( "Warning: Pdb file: can't find atom %s in: %s" % (card[i:i+5], card) )) continue bond_atoms(a1, a2) numconects += 1 elif key == "ter": # Finish the current molecule. _finish_molecule() # Discard the original molecule and create a new one. mol = Chunk(assy, nodename) mol.protein = Protein() numconects = 0 elif key == "header": # Extract PDB ID from the header string. pdbid = card[62:66].lower() comment_text += card elif key == "compnd": comment_text += card elif key == "remark": comment_text += card elif key == "model": # Check out the MODEL record, ignore everything other than MODEL 1. # This behavior has to be optional and set via User Preference. # piotr 080714 model_id = int(card[6:20]) if model_id > 1: # Skip remaining part of the file. break elif key in ["helix", "sheet", "turn"]: # Read secondary structure information. if key == "helix": begin = int(card[22:25]) end = int(card[34:37]) chainId = card[19] for s in range(begin, end+1): helix.append((s, chainId)) elif key == "sheet": begin = int(card[23:26]) end = int(card[34:37]) chainId = card[21] for s in range(begin, end+1): sheet.append((s, chainId)) elif key == "turn": begin = int(card[23:26]) end = int(card[34:37]) chainId = card[19] for s in range(begin, end+1): turn.append((s, chainId)) else: if card[7:15] == "ntrials:": _read_rosetta_info = True comment_text += "Rosetta Scoring Analysis\n" if _read_rosetta_info: comment_text += card if showProgressDialog: # Update the progress dialog. _progressValue += 1 if _progressValue >= _progressFinishValue: win.progressDialog.setLabelText("Building model...") elif _progressDialogDisplayed: win.progressDialog.setValue(_progressValue) else: _timerDuration = time.time() - _timerStart if _timerDuration > 0.25: # Display progress dialog after 0.25 seconds win.progressDialog.setValue(_progressValue) _progressDialogDisplayed = True if showProgressDialog: # Make the progress dialog go away. win.progressDialog.setValue(_progressFinishValue) _finish_molecule() if water.atoms: # Check if there are any water molecules water.name = "Solvent" # The water should be hidden by default. water.hide() mollist.append(water) return (mollist, comment_text)
def buildChunk(self, assy): """ Build Chunk for the cookies. First, combine bonds from all layers together, which may fuse some half bonds to full bonds. """ from model.chunk import Chunk from model.chem import Atom from utilities.constants import gensym numLayers = len(self.bondLayers) if numLayers: allBonds = {} allCarbons = {} #Copy the bonds, carbons and hedron from the first layer for ii in range(numLayers): if self.bondLayers.has_key(ii): for bKey, bValue in self.bondLayers[ii].items(): allBonds[bKey] = bValue del self.bondLayers[ii] break for carbons in self.carbonPosDict.values(): for cKey, cValue in carbons.items(): allCarbons[cKey] = cValue for hedrons in self.hedroPosDict.values(): for hKey, hValue in hedrons.items(): allCarbons[hKey] = hValue for bonds in self.bondLayers.values(): for bKey, bValues in bonds.items(): if bKey in allBonds: existValues = allBonds[bKey] for bValue in bValues: if type(bValue) == type((1, 1)): if bValue[1]: ctValue = (bValue[0], 0) else: ctValue = (bValue[0], 1) if ctValue in existValues: idex = existValues.index(ctValue) existValues[idex] = bValue[0] else: existValues += [bValue] else: existValues += [bValue] allBonds[bKey] = existValues else: allBonds[bKey] = bValues #print "allbonds: ", allBonds #print "allCarbons: ", allCarbons carbonAtoms = {} mol = Chunk(assy, gensym("Crystal", assy)) for bKey, bBonds in allBonds.items(): keyHedron = True if len(bBonds): for bond in bBonds: if keyHedron: if type(bBonds[0]) == type(1) or (not bBonds[0][1]): if not bKey in carbonAtoms: keyAtom = Atom("C", allCarbons[bKey], mol) carbonAtoms[bKey] = keyAtom else: keyAtom = carbonAtoms[bKey] keyHedron = False if keyHedron: if type(bond) != type((1, 1)): raise ValueError, (bKey, bond, bBonds) else: xp = (allCarbons[bKey] + allCarbons[bond[0]])/2.0 keyAtom = Atom("X", xp, mol) if type(bond) == type(1) or bond[1]: if type(bond) == type(1): bvKey = bond else: bvKey = bond[0] if not bvKey in carbonAtoms: bondAtom = Atom("C", allCarbons[bvKey], mol) carbonAtoms[bvKey] = bondAtom else: bondAtom = carbonAtoms[bvKey] else: xp = (allCarbons[bKey] + allCarbons[bond[0]])/2.0 bondAtom = Atom("X", xp, mol) bond_atoms(keyAtom, bondAtom) if len(mol.atoms) > 0: #bruce 050222 comment: much of this is not needed, since mol.pick() does it. # Note: this method is similar to one in BuildCrystal_Command.py. assy.addmol(mol) assy.unpickall_in_GLPane() # was unpickparts; not sure _in_GLPane is best (or that # this is needed at all) [bruce 060721] mol.pick() assy.mt.mt_update() return # from buildChunk