def modifyTransmute(self, elem, force = False, atomType=None): """ This method was originally a method of class mode and selectMode. Transmute selected atoms into <elem> and with an optional <atomType>. <elem> is an element number that selected atoms will be transmuted to. <force>: boolean variable meaning keeping existing bond or not. <atomType>: the optional hybrid bond type if the element support hybrid. --Huaicai[9/1/05] """ # now change selected atoms to the specified element # [bruce 041215: this should probably be made available for any modes # in which "selected atoms" are permitted, not just Select modes. #e] from model.elements import PeriodicTable if self.selatoms: dstElem = PeriodicTable.getElement(elem) for atm in self.selatoms.values(): atm.Transmute(dstElem, force = force, atomtype=atomType) # bruce 041215 fix bug 131 by replacing low-level mvElement call # with new higher-level method Transmute. Note that singlets # can't be selected, so the fact that Transmute does nothing to # them is not (presently) relevant. #e status message? # (Presently a.Transmute makes one per "error or refusal".) self.o.gl_update() if self.selmols: #bruce 060720 elif -> if, in case both atoms and chunks can be selected someday dstElem = PeriodicTable.getElement(elem) #bruce 060720 fix typo dstElm -> dstElem to fix bug 2149 # but we have to decide if we want the behavior this now gives us, of transmuting inside selected chunks. for mol in self.selmols[:]: for atm in mol.atoms.values(): atm.Transmute(dstElem, force = force, atomtype=atomType) # this might run on some killed singlets; should be ok self.o.gl_update() return
def update_hybrid_btngrp(self, buttonIndex = 0): """Update the buttons of the current element\'s hybridization types into hybrid_btngrp; select the specified one if provided""" elem = PeriodicTable.getElement(self.w.Element) # self.w.Element is atomic number atypes = elem.atomtypes if elem.name == 'Carbon': self.setup_C_hybrid_buttons() elif elem.name == 'Nitrogen': self.setup_N_hybrid_buttons() elif elem.name == 'Oxygen': self.setup_O_hybrid_buttons() elif elem.name == 'Sulfur': self.setup_S_hybrid_buttons() else: self.hide_hybrid_btngrp() self.elemGLPane.changeHybridType(None) return #if len(atypes) > 1: # Prequisite: w.hybridComboBox has been updated at this moment. b_name = self.bond_id2name[buttonIndex] self.elemGLPane.changeHybridType(b_name) self.elemGLPane.refreshDisplay(self.elm, self.displayMode) self.theHybridizations.button(buttonIndex).setChecked(True) self.set_hybrid_type(buttonIndex) # Added Atomic Hybrids label. Mark 2007-05-30 self.atomic_hybrids_label.setText("Atomic Hybrids for " + elem.name + " :") self.show_hybrid_btngrp()
def update_selection_filter_list(self): """Adds/removes the element selected in the MMKit to/from Atom Selection Filter based on what modifier key is pressed (if any). """ eltnum = self.w.Element if self.o.modkeys is None: self.w.filtered_elements = [] self.w.filtered_elements.append(PeriodicTable.getElement(eltnum)) if self.o.modkeys == 'Shift': if not PeriodicTable.getElement(eltnum) in self.w.filtered_elements[:]: self.w.filtered_elements.append(PeriodicTable.getElement(eltnum)) elif self.o.modkeys == 'Control': if PeriodicTable.getElement(eltnum) in self.w.filtered_elements[:]: self.w.filtered_elements.remove(PeriodicTable.getElement(eltnum)) self.update_selection_filter_list_widget()
def fix_deprecated_elements( changed_atoms): """ scan for deprecated elements, and fix them """ fix_PAM3 = pref_fix_deprecated_PAM3_atoms() fix_PAM5 = pref_fix_deprecated_PAM5_atoms() deprecated_atoms = [] for atom in changed_atoms.itervalues(): deprecated_to = atom.element.deprecated_to # an element symbol, or None, or 'remove' if deprecated_to: pam = atom.element.pam assert pam in (MODEL_PAM3, MODEL_PAM5) if pam == MODEL_PAM3: fix = fix_PAM3 elif pam == MODEL_PAM5: fix = fix_PAM5 else: fix = False if fix: deprecated_atoms.append(atom) elif debug_flags.DEBUG_DNA_UPDATER: print "dna updater: debug_pref says don't alter deprecated atom %r" % (atom,) continue for atom in deprecated_atoms: deprecated_to = atom.element.deprecated_to # an element symbol, or 'remove' if atom.display != diDEFAULT: # Atoms of deprecated elements sometimes have funny display modes # set by the DNA Duplex Generator. Remove these here. # (This may be needed even after we fix the generator, # due to old mmp files. REVIEW: can it ever cause harm?) atom.setDisplay(diDEFAULT) if deprecated_to == 'remove' or deprecated_to == 'X': # (Atom.kill might be unideal behavior for 'remove', # but that's only on Pl3 which never occurs AFAIK, so nevermind) # Kill the atom (and make sure that new bondpoints get into a good enough position). # REVIEW: does atom.kill make new bps immediately # (review whether ok if more than one needs making on one base atom) # or later # (review whether it's still going to happen in the current master_updater call)? #### if debug_flags.DEBUG_DNA_UPDATER_VERBOSE: print "dna updater: kill deprecated atom %r" % (atom,) summary_format = \ "Warning: dna updater killed [N] deprecated %s pseudoatom(s)" % \ (atom.element.symbol,) env.history.deferred_summary_message( orangemsg(summary_format) ) atom.kill() # TODO: worry about atom being a hotspot, or having a bondpoint which is a hotspot? else: # Transmute atom to a new element symbol -- assume its position, # bonds, and bondpoints are all ok and need no changes. # (Should be true of as 071119, since this is used only # to make Ax and Ss PAM atoms from variant atomtypes # used to mark them as being in special situations.) # # Use mvElement to avoid remaking existing bondpoints. elt = PeriodicTable.getElement(deprecated_to) if debug_flags.DEBUG_DNA_UPDATER_VERBOSE: print "dna updater: transmute deprecated atom %r to element %s" % \ (atom, elt.symbol) if dna_updater_warn_when_transmuting_deprecated_elements(): summary_format = \ "Warning: dna updater transmuted [N] %s to %s pseudoatom(s)" % \ (atom.element.symbol, elt.symbol ) env.history.deferred_summary_message( orangemsg(summary_format) ) # todo: refactor so orangemsg is replaced with a warning option atom.mvElement(elt) atom.make_enough_bondpoints() # REVIEW: do this later, if atom classes should be corrected first # to help this properly position bondpoints # (or perhaps, first set correct atom classes, then do this, # making sure it sets correct bondpoint classes, # or that we correct them separately afterwards) continue return # from fix_deprecated_elements
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
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 elementId(symbol): return PeriodicTable.getElement(symbol).eltnum
from utilities.exception_classes import PluginBug from utilities.constants import gensym from utilities.prefs_constants import dnaDefaultStrand1Color_prefs_key from utilities.prefs_constants import dnaDefaultStrand2Color_prefs_key from utilities.prefs_constants import dnaDefaultSegmentColor_prefs_key from dna.model.Dna_Constants import getDuplexBasesPerTurn ##from dna.updater.dna_updater_prefs import pref_dna_updater_convert_to_PAM3plus5 from simulation.sim_commandruns import adjustSinglet from model.elements import PeriodicTable from model.Line import Line from model.chem import Atom_prekill_prep Element_Ae3 = PeriodicTable.getElement('Ae3') from dna.model.Dna_Constants import basesDict, dnaDict from dna.model.dna_model_constants import LADDER_END0 basepath_ok, basepath = find_plugin_dir("DNA") if not basepath_ok: env.history.message(orangemsg("The cad/plugins/DNA directory is missing.")) RIGHT_HANDED = -1 LEFT_HANDED = 1 from geometry.VQT import V, Q, norm, cross from geometry.VQT import vlen from Numeric import dot
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
def populate(self, mol, height, width, z, bond_length, endings, position): """ Create a graphene sheet chunk. """ 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 num_atoms = len(mol.atoms) bond_dict = {} i = j = 0 y = -0.5 * height - 2 * bond_length while y < 0.5 * height + 2 * bond_length: i = 0 x = -0.5 * width - 2 * bond_length while x < 0.5 * width + 2 * bond_length: lst = [] for x1, y1 in quartet: atm = add("C", x + x1 * bond_length, y + y1 * bond_length) lst.append(atm) bond_dict[(i, j)] = lst bond_atoms(lst[0], lst[1], bond_constants.V_GRAPHITE) bond_atoms(lst[1], lst[2], bond_constants.V_GRAPHITE) bond_atoms(lst[2], lst[3], bond_constants.V_GRAPHITE) i += 1 x += 3 * bond_length j += 1 y += sqrt3 * bond_length imax, jmax = i, j for i in range(imax): for j in range(jmax - 1): lst1 = bond_dict[(i, j)] lst2 = bond_dict[(i, j + 1)] bond_atoms(lst1[0], lst2[1], bond_constants.V_GRAPHITE) bond_atoms(lst1[3], lst2[2], bond_constants.V_GRAPHITE) for i in range(imax - 1): for j in range(jmax): lst1 = bond_dict[(i, j)] lst2 = bond_dict[(i + 1, j)] bond_atoms(lst1[3], lst2[0], bond_constants.V_GRAPHITE) # trim to dimensions atoms = mol.atoms for atm in atoms.values(): x, y, z = atm.posn() xdim, ydim = width + bond_length, height + bond_length # xdim, ydim = width + 0.5 * bond_length, height + 0.5 * bond_length if (x < -0.5 * xdim or x > 0.5 * xdim or y < -0.5 * ydim or y > 0.5 * ydim): atm.kill() def trimCarbons(): """Trim all the carbons that only have one carbon neighbor. """ for i in range(2): for atm in atoms.values(): if not atm.is_singlet() and len(atm.realNeighbors()) == 1: atm.kill() if TOROIDAL: # This is for making electrical inductors. What would be # really good here would be to break the bonds that are # stretched by this and put back the bondpoints. angstromsPerTurn = 6.0 for atm in atoms.values(): x, y, z = atm.posn() r = (x**2 + y**2)**.5 if 0.25 * width <= r <= 0.5 * width: angle = atan2(y, x) zdisp = (angstromsPerTurn * angle) / (2 * pi) atm.setposn(V(x, y, z + zdisp)) else: atm.kill() if endings == 1: # hydrogen terminations trimCarbons() for atm in atoms.values(): atm.Hydrogenate() elif endings == 2: # nitrogen terminations trimCarbons() dstElem = PeriodicTable.getElement('N') atomtype = dstElem.find_atomtype('sp2') for atm in atoms.values(): if len(atm.realNeighbors()) == 2: atm.Transmute(dstElem, force=True, atomtype=atomtype) for atm in atoms.values(): atm.setposn(atm.posn() + position) if num_atoms == len(mol.atoms): raise Exception("Graphene sheet too small - no atoms added")
def populate(self, mol, height, width, z, bond_length, endings, position): """ Create a graphene sheet chunk. """ 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 num_atoms = len(mol.atoms) bond_dict = { } i = j = 0 y = -0.5 * height - 2 * bond_length while y < 0.5 * height + 2 * bond_length: i = 0 x = -0.5 * width - 2 * bond_length while x < 0.5 * width + 2 * bond_length: lst = [ ] for x1, y1 in quartet: atm = add("C", x + x1 * bond_length, y + y1 * bond_length) lst.append(atm) bond_dict[(i, j)] = lst bonds.bond_atoms(lst[0], lst[1], bond_constants.V_GRAPHITE) bonds.bond_atoms(lst[1], lst[2], bond_constants.V_GRAPHITE) bonds.bond_atoms(lst[2], lst[3], bond_constants.V_GRAPHITE) i += 1 x += 3 * bond_length j += 1 y += sqrt3 * bond_length imax, jmax = i, j for i in range(imax): for j in range(jmax - 1): lst1 = bond_dict[(i, j)] lst2 = bond_dict[(i, j+1)] bonds.bond_atoms(lst1[0], lst2[1], bond_constants.V_GRAPHITE) bonds.bond_atoms(lst1[3], lst2[2], bond_constants.V_GRAPHITE) for i in range(imax - 1): for j in range(jmax): lst1 = bond_dict[(i, j)] lst2 = bond_dict[(i+1, j)] bonds.bond_atoms(lst1[3], lst2[0], bond_constants.V_GRAPHITE) # trim to dimensions atoms = mol.atoms for atm in atoms.values(): x, y, z = atm.posn() xdim, ydim = width + bond_length, height + bond_length # xdim, ydim = width + 0.5 * bond_length, height + 0.5 * bond_length if (x < -0.5 * xdim or x > 0.5 * xdim or y < -0.5 * ydim or y > 0.5 * ydim): atm.kill() def trimCarbons(): """Trim all the carbons that only have one carbon neighbor. """ for i in range(2): for atm in atoms.values(): if not atm.is_singlet() and len(atm.realNeighbors()) == 1: atm.kill() if TOROIDAL: # This is for making electrical inductors. What would be # really good here would be to break the bonds that are # stretched by this and put back the bondpoints. angstromsPerTurn = 6.0 for atm in atoms.values(): x, y, z = atm.posn() r = (x**2 + y**2) ** .5 if 0.25 * width <= r <= 0.5 * width: angle = atan2(y, x) zdisp = (angstromsPerTurn * angle) / (2 * pi) atm.setposn(V(x, y, z + zdisp)) else: atm.kill() if endings == 1: # hydrogen terminations trimCarbons() for atm in atoms.values(): atm.Hydrogenate() elif endings == 2: # nitrogen terminations trimCarbons() dstElem = PeriodicTable.getElement('N') atomtype = dstElem.find_atomtype('sp2') for atm in atoms.values(): if len(atm.realNeighbors()) == 2: atm.Transmute(dstElem, force=True, atomtype=atomtype) for atm in atoms.values(): atm.setposn(atm.posn() + position) if num_atoms == len(mol.atoms): raise Exception("Graphene sheet too small - no atoms added")
# - Remove Crossover needs to be offered when correct to do so, not otherwise # - Pl position is wrong after either op, esp. Remove # - Undo and Feature Help cmdnames are wrong (not working) from utilities.constants import noop, average_value from model.bond_constants import V_SINGLE from model.bond_constants import atoms_are_bonded, find_bond from model.bonds import bond_atoms_faster, bond_direction ##, bond_atoms from utilities.Log import redmsg, greenmsg, quote_html ##, orangemsg ##from debug_prefs import debug_pref, Choice import foundation.env as env from utilities.GlobalPreferences import dna_updater_is_enabled from model.elements import PeriodicTable Element_Sj5 = PeriodicTable.getElement('Sj5') Element_Ss5 = PeriodicTable.getElement('Ss5') def crossover_menu_spec(atom, selatoms): """ Make a crossover-related menu_spec list for the two atoms in the selatoms dict (atom.key -> atom), both Pl, for use in atom's context menu (which must be one of the atoms in selatoms). If no menu commands are needed, return [] (a valid empty menu_spec) or None. Should be reasonably fast, but needn't be super-fast -- called once whenever we construct a context menu for exactly two selected Pl atoms. """ assert len(selatoms) == 2 atoms = selatoms.values()
def fix_deprecated_elements(changed_atoms): """ scan for deprecated elements, and fix them """ fix_PAM3 = pref_fix_deprecated_PAM3_atoms() fix_PAM5 = pref_fix_deprecated_PAM5_atoms() deprecated_atoms = [] for atom in changed_atoms.itervalues(): deprecated_to = atom.element.deprecated_to # an element symbol, or None, or 'remove' if deprecated_to: pam = atom.element.pam assert pam in (MODEL_PAM3, MODEL_PAM5) if pam == MODEL_PAM3: fix = fix_PAM3 elif pam == MODEL_PAM5: fix = fix_PAM5 else: fix = False if fix: deprecated_atoms.append(atom) elif debug_flags.DEBUG_DNA_UPDATER: print "dna updater: debug_pref says don't alter deprecated atom %r" % ( atom, ) continue for atom in deprecated_atoms: deprecated_to = atom.element.deprecated_to # an element symbol, or 'remove' if atom.display != diDEFAULT: # Atoms of deprecated elements sometimes have funny display modes # set by the DNA Duplex Generator. Remove these here. # (This may be needed even after we fix the generator, # due to old mmp files. REVIEW: can it ever cause harm?) atom.setDisplay(diDEFAULT) if deprecated_to == 'remove' or deprecated_to == 'X': # (Atom.kill might be unideal behavior for 'remove', # but that's only on Pl3 which never occurs AFAIK, so nevermind) # Kill the atom (and make sure that new bondpoints get into a good enough position). # REVIEW: does atom.kill make new bps immediately # (review whether ok if more than one needs making on one base atom) # or later # (review whether it's still going to happen in the current master_updater call)? #### if debug_flags.DEBUG_DNA_UPDATER_VERBOSE: print "dna updater: kill deprecated atom %r" % (atom, ) summary_format = \ "Warning: dna updater killed [N] deprecated %s pseudoatom(s)" % \ (atom.element.symbol,) env.history.deferred_summary_message(orangemsg(summary_format)) atom.kill() # TODO: worry about atom being a hotspot, or having a bondpoint which is a hotspot? else: # Transmute atom to a new element symbol -- assume its position, # bonds, and bondpoints are all ok and need no changes. # (Should be true of as 071119, since this is used only # to make Ax and Ss PAM atoms from variant atomtypes # used to mark them as being in special situations.) # # Use mvElement to avoid remaking existing bondpoints. elt = PeriodicTable.getElement(deprecated_to) if debug_flags.DEBUG_DNA_UPDATER_VERBOSE: print "dna updater: transmute deprecated atom %r to element %s" % \ (atom, elt.symbol) if dna_updater_warn_when_transmuting_deprecated_elements(): summary_format = \ "Warning: dna updater transmuted [N] %s to %s pseudoatom(s)" % \ (atom.element.symbol, elt.symbol ) env.history.deferred_summary_message(orangemsg(summary_format)) # todo: refactor so orangemsg is replaced with a warning option atom.mvElement(elt) atom.make_enough_bondpoints() # REVIEW: do this later, if atom classes should be corrected first # to help this properly position bondpoints # (or perhaps, first set correct atom classes, then do this, # making sure it sets correct bondpoint classes, # or that we correct them separately afterwards) continue return # from fix_deprecated_elements
def build_struct(self, name, params, position, mol=None, createPrinted=False): """ Build a nanotube from the parameters in the Property Manger dialog. """ length, n, m, bond_length, zdist, xydist, \ twist, bend, members, endings, numwalls, spacing = params # This can take a few seconds. Inform the user. # 100 is a guess on my part. Mark 051103. if not createPrinted: # If it's a multi-wall tube, only print the "Creating" message once. if length > 100.0: env.history.message(self.cmd + "This may take a moment...") self.chirality = Chirality(n, m, bond_length) PROFILE = False if PROFILE: sw = Stopwatch() sw.start() xyz = self.chirality.xyz if mol == None: mol = Chunk(self.win.assy, name) atoms = mol.atoms mlimits = self.chirality.mlimits # populate the tube with some extra carbons on the ends # so that we can trim them later self.chirality.populate(mol, length + 4 * self.chirality.maxlen, members != 0) # Apply twist and distortions. Bends probably would come # after this point because they change the direction for the # length. I'm worried about Z distortion because it will work # OK for stretching, but with compression it can fail. BTW, # "Z distortion" is a misnomer, we're stretching in the Y # direction. for atm in atoms.values(): # twist x, y, z = atm.posn() twistRadians = twist * z c, s = cos(twistRadians), sin(twistRadians) x, y = x * c + y * s, -x * s + y * c atm.setposn(V(x, y, z)) for atm in atoms.values(): # z distortion x, y, z = atm.posn() z *= (zdist + length) / length atm.setposn(V(x, y, z)) length += zdist for atm in atoms.values(): # xy distortion x, y, z = atm.posn() radius = self.chirality.R x *= (radius + 0.5 * xydist) / radius y *= (radius - 0.5 * xydist) / radius atm.setposn(V(x, y, z)) # Judgement call: because we're discarding carbons with funky # valences, we will necessarily get slightly more ragged edges # on nanotubes. This is a parameter we can fiddle with to # adjust the length. My thought is that users would prefer a # little extra length, because it's fairly easy to trim the # ends, but much harder to add new atoms on the end. LENGTH_TWEAK = bond_length # trim all the carbons that fall outside our desired length # by doing this, we are introducing new singlets for atm in atoms.values(): x, y, z = atm.posn() if (z > .5 * (length + LENGTH_TWEAK) or z < -.5 * (length + LENGTH_TWEAK)): atm.kill() # Apply bend. Equations are anomalous for zero bend. if abs(bend) > pi / 360: R = length / bend for atm in atoms.values(): x, y, z = atm.posn() theta = z / R x, z = R - (R - x) * cos(theta), (R - x) * sin(theta) atm.setposn(V(x, y, z)) def trimCarbons(): # trim all the carbons that only have one carbon neighbor for i in range(2): for atm in atoms.values(): if not atm.is_singlet() and len(atm.realNeighbors()) == 1: atm.kill() trimCarbons() # if we're not picky about endings, we don't need to trim carbons if endings == "Capped": # buckyball endcaps addEndcap(mol, length, self.chirality.R) if endings == "Hydrogen": # hydrogen terminations for atm in atoms.values(): atm.Hydrogenate() elif endings == "Nitrogen": # nitrogen terminations dstElem = PeriodicTable.getElement('N') atomtype = dstElem.find_atomtype('sp2') for atm in atoms.values(): if len(atm.realNeighbors()) == 2: atm.Transmute(dstElem, force=True, atomtype=atomtype) # Translate structure to desired position for atm in atoms.values(): v = atm.posn() atm.setposn(v + position) if PROFILE: t = sw.now() env.history.message(greenmsg("%g seconds to build %d atoms" % (t, len(atoms.values())))) if numwalls > 1: n += int(spacing * 3 + 0.5) # empirical tinkering params = (length, n, m, bond_length, zdist, xydist, twist, bend, members, endings, numwalls-1, spacing) self.build_struct(name, params, position, mol=mol, createPrinted=True) return mol
def _init(): global _is_initialized if (_is_initialized): return AMBER_AtomTypes["C"] = PeriodicTable.getElement("C").find_atomtype("sp2") AMBER_AtomTypes["CA"] = PeriodicTable.getElement("C").find_atomtype("sp2") AMBER_AtomTypes["CB"] = PeriodicTable.getElement("C").find_atomtype("sp2") AMBER_AtomTypes["CC"] = PeriodicTable.getElement("C").find_atomtype("sp2") AMBER_AtomTypes["CD"] = PeriodicTable.getElement("C").find_atomtype("sp2") AMBER_AtomTypes["CK"] = PeriodicTable.getElement("C").find_atomtype("sp2") AMBER_AtomTypes["CM"] = PeriodicTable.getElement("C").find_atomtype("sp2") AMBER_AtomTypes["CN"] = PeriodicTable.getElement("C").find_atomtype("sp2") AMBER_AtomTypes["CQ"] = PeriodicTable.getElement("C").find_atomtype("sp2") AMBER_AtomTypes["CR"] = PeriodicTable.getElement("C").find_atomtype("sp2") AMBER_AtomTypes["CT"] = PeriodicTable.getElement("C").find_atomtype("sp3") AMBER_AtomTypes["CV"] = PeriodicTable.getElement("C").find_atomtype("sp2") AMBER_AtomTypes["CW"] = PeriodicTable.getElement("C").find_atomtype("sp2") AMBER_AtomTypes["C*"] = PeriodicTable.getElement("C").find_atomtype("sp2") AMBER_AtomTypes["CY"] = PeriodicTable.getElement("C").find_atomtype("sp") AMBER_AtomTypes["CZ"] = PeriodicTable.getElement("C").find_atomtype("sp") AMBER_AtomTypes["C0"] = PeriodicTable.getElement("Ca").find_atomtype("?") AMBER_AtomTypes["H"] = PeriodicTable.getElement("H").find_atomtype("?") AMBER_AtomTypes["H0"] = PeriodicTable.getElement("H").find_atomtype("?") AMBER_AtomTypes["HC"] = PeriodicTable.getElement("H").find_atomtype("?") AMBER_AtomTypes["H1"] = PeriodicTable.getElement("H").find_atomtype("?") AMBER_AtomTypes["H2"] = PeriodicTable.getElement("H").find_atomtype("?") AMBER_AtomTypes["H3"] = PeriodicTable.getElement("H").find_atomtype("?") AMBER_AtomTypes["HA"] = PeriodicTable.getElement("H").find_atomtype("?") AMBER_AtomTypes["H4"] = PeriodicTable.getElement("H").find_atomtype("?") AMBER_AtomTypes["H5"] = PeriodicTable.getElement("H").find_atomtype("?") AMBER_AtomTypes["HO"] = PeriodicTable.getElement("H").find_atomtype("?") AMBER_AtomTypes["HS"] = PeriodicTable.getElement("H").find_atomtype("?") AMBER_AtomTypes["HW"] = PeriodicTable.getElement("H").find_atomtype("?") AMBER_AtomTypes["HP"] = PeriodicTable.getElement("H").find_atomtype("?") AMBER_AtomTypes["HZ"] = PeriodicTable.getElement("H").find_atomtype("?") AMBER_AtomTypes["F"] = PeriodicTable.getElement("F").find_atomtype("?") AMBER_AtomTypes["Cl"] = PeriodicTable.getElement("Cl").find_atomtype("?") AMBER_AtomTypes["Br"] = PeriodicTable.getElement("Br").find_atomtype("?") AMBER_AtomTypes["I"] = PeriodicTable.getElement("I").find_atomtype("?") AMBER_AtomTypes["IM"] = PeriodicTable.getElement("Cl").find_atomtype("?") AMBER_AtomTypes["IB"] = PeriodicTable.getElement("Na").find_atomtype("?") AMBER_AtomTypes["MG"] = PeriodicTable.getElement("Mg").find_atomtype("?") AMBER_AtomTypes["N"] = PeriodicTable.getElement("N").find_atomtype("sp2") AMBER_AtomTypes["NA"] = PeriodicTable.getElement("N").find_atomtype("sp2") AMBER_AtomTypes["NB"] = PeriodicTable.getElement("N").find_atomtype("sp2") AMBER_AtomTypes["NC"] = PeriodicTable.getElement("N").find_atomtype("sp2") AMBER_AtomTypes["N2"] = PeriodicTable.getElement("N").find_atomtype("sp2") AMBER_AtomTypes["N3"] = PeriodicTable.getElement("N").find_atomtype("sp3") AMBER_AtomTypes["NT"] = PeriodicTable.getElement("N").find_atomtype("sp3") AMBER_AtomTypes["N*"] = PeriodicTable.getElement("N").find_atomtype("sp2") AMBER_AtomTypes["NY"] = PeriodicTable.getElement("N").find_atomtype("sp") AMBER_AtomTypes["O"] = PeriodicTable.getElement("O").find_atomtype("sp2") AMBER_AtomTypes["O2"] = PeriodicTable.getElement("O").find_atomtype("sp2") AMBER_AtomTypes["OW"] = PeriodicTable.getElement("O").find_atomtype("sp3") AMBER_AtomTypes["OH"] = PeriodicTable.getElement("O").find_atomtype("sp3") AMBER_AtomTypes["OS"] = PeriodicTable.getElement("O").find_atomtype("sp3") AMBER_AtomTypes["P"] = PeriodicTable.getElement("P").find_atomtype("sp3(p)") #sp3(p) is 'sp3(phosphate) AMBER_AtomTypes["S"] = PeriodicTable.getElement("S").find_atomtype("sp3") # ? AMBER_AtomTypes["SH"] = PeriodicTable.getElement("S").find_atomtype("sp3") # ? AMBER_AtomTypes["CU"] = PeriodicTable.getElement("Cu").find_atomtype("?") AMBER_AtomTypes["FE"] = PeriodicTable.getElement("Fe").find_atomtype("?") AMBER_AtomTypes["Li"] = PeriodicTable.getElement("Li").find_atomtype("?") AMBER_AtomTypes["IP"] = PeriodicTable.getElement("Na").find_atomtype("?") AMBER_AtomTypes["Na"] = PeriodicTable.getElement("Na").find_atomtype("?") AMBER_AtomTypes["K"] = PeriodicTable.getElement("K").find_atomtype("?") #AMBER_AtomTypes["Rb"] = PeriodicTable.getElement("Rb").find_atomtype("?") #AMBER_AtomTypes["Cs"] = PeriodicTable.getElement("Cs").find_atomtype("?") AMBER_AtomTypes["Zn"] = PeriodicTable.getElement("Zn").find_atomtype("?") _is_initialized = True
def build_struct(self, name, params, position, mol=None, createPrinted=False): """ Build a nanotube from the parameters in the Property Manger dialog. """ length, n, m, bond_length, zdist, xydist, \ twist, bend, members, endings, numwalls, spacing = params # This can take a few seconds. Inform the user. # 100 is a guess on my part. Mark 051103. if not createPrinted: # If it's a multi-wall tube, only print the "Creating" message once. if length > 100.0: env.history.message(self.cmd + "This may take a moment...") self.chirality = Chirality(n, m, bond_length) PROFILE = False if PROFILE: sw = Stopwatch() sw.start() xyz = self.chirality.xyz if mol == None: mol = Chunk(self.win.assy, name) atoms = mol.atoms mlimits = self.chirality.mlimits # populate the tube with some extra carbons on the ends # so that we can trim them later self.chirality.populate(mol, length + 4 * self.chirality.maxlen, members != 0) # Apply twist and distortions. Bends probably would come # after this point because they change the direction for the # length. I'm worried about Z distortion because it will work # OK for stretching, but with compression it can fail. BTW, # "Z distortion" is a misnomer, we're stretching in the Y # direction. for atm in atoms.values(): # twist x, y, z = atm.posn() twistRadians = twist * z c, s = cos(twistRadians), sin(twistRadians) x, y = x * c + y * s, -x * s + y * c atm.setposn(V(x, y, z)) for atm in atoms.values(): # z distortion x, y, z = atm.posn() z *= (zdist + length) / length atm.setposn(V(x, y, z)) length += zdist for atm in atoms.values(): # xy distortion x, y, z = atm.posn() radius = self.chirality.R x *= (radius + 0.5 * xydist) / radius y *= (radius - 0.5 * xydist) / radius atm.setposn(V(x, y, z)) # Judgement call: because we're discarding carbons with funky # valences, we will necessarily get slightly more ragged edges # on nanotubes. This is a parameter we can fiddle with to # adjust the length. My thought is that users would prefer a # little extra length, because it's fairly easy to trim the # ends, but much harder to add new atoms on the end. LENGTH_TWEAK = bond_length # trim all the carbons that fall outside our desired length # by doing this, we are introducing new singlets for atm in atoms.values(): x, y, z = atm.posn() if (z > .5 * (length + LENGTH_TWEAK) or z < -.5 * (length + LENGTH_TWEAK)): atm.kill() # Apply bend. Equations are anomalous for zero bend. if abs(bend) > pi / 360: R = length / bend for atm in atoms.values(): x, y, z = atm.posn() theta = z / R x, z = R - (R - x) * cos(theta), (R - x) * sin(theta) atm.setposn(V(x, y, z)) def trimCarbons(): # trim all the carbons that only have one carbon neighbor for i in range(2): for atm in atoms.values(): if not atm.is_singlet() and len(atm.realNeighbors()) == 1: atm.kill() trimCarbons() # if we're not picky about endings, we don't need to trim carbons if endings == "Capped": # buckyball endcaps addEndcap(mol, length, self.chirality.R) if endings == "Hydrogen": # hydrogen terminations for atm in atoms.values(): atm.Hydrogenate() elif endings == "Nitrogen": # nitrogen terminations dstElem = PeriodicTable.getElement('N') atomtype = dstElem.find_atomtype('sp2') for atm in atoms.values(): if len(atm.realNeighbors()) == 2: atm.Transmute(dstElem, force=True, atomtype=atomtype) # Translate structure to desired position for atm in atoms.values(): v = atm.posn() atm.setposn(v + position) if PROFILE: t = sw.now() env.history.message( greenmsg("%g seconds to build %d atoms" % (t, len(atoms.values())))) if numwalls > 1: n += int(spacing * 3 + 0.5) # empirical tinkering params = (length, n, m, bond_length, zdist, xydist, twist, bend, members, endings, numwalls - 1, spacing) self.build_struct(name, params, position, mol=mol, createPrinted=True) return mol
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 _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 _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 build(self, name, assy, position, mol=None, createPrinted=False): """ Build a Peptide from the parameters in the Property Manger dialog. """ endPoint1, endPoint2 = self.getEndPoints() cntAxis = endPoint2 - endPoint1 length = vlen(cntAxis) # This can take a few seconds. Inform the user. # 100 is a guess. --Mark 051103. if not createPrinted: # If it's a multi-wall tube, only print the "Creating" message once. if length > 100.0: env.history.message("This may take a moment...") PROFILE = False if PROFILE: sw = Stopwatch() sw.start() xyz = self.xyz if mol == None: mol = Chunk(assy, name) atoms = mol.atoms mlimits = self.mlimits # populate the tube with some extra carbons on the ends # so that we can trim them later self.populate(mol, length + 4 * self.maxlen) # Apply twist and distortions. Bends probably would come # after this point because they change the direction for the # length. I'm worried about Z distortion because it will work # OK for stretching, but with compression it can fail. BTW, # "Z distortion" is a misnomer, we're stretching in the Y # direction. for atm in atoms.values(): # twist x, y, z = atm.posn() twistRadians = self.twist * z c, s = cos(twistRadians), sin(twistRadians) x, y = x * c + y * s, -x * s + y * c atm.setposn(V(x, y, z)) for atm in atoms.values(): # z distortion x, y, z = atm.posn() z *= (self.zdist + length) / length atm.setposn(V(x, y, z)) length += self.zdist for atm in atoms.values(): # xy distortion x, y, z = atm.posn() radius = self.getRadius() x *= (radius + 0.5 * self.xydist) / radius y *= (radius - 0.5 * self.xydist) / radius atm.setposn(V(x, y, z)) # Judgement call: because we're discarding carbons with funky # valences, we will necessarily get slightly more ragged edges # on Peptides. This is a parameter we can fiddle with to # adjust the length. My thought is that users would prefer a # little extra length, because it's fairly easy to trim the # ends, but much harder to add new atoms on the end. LENGTH_TWEAK = self.getBondLength() # trim all the carbons that fall outside our desired length # by doing this, we are introducing new singlets for atm in atoms.values(): x, y, z = atm.posn() if z > 0.5 * (length + LENGTH_TWEAK) or z < -0.5 * (length + LENGTH_TWEAK): atm.kill() # Apply bend. Equations are anomalous for zero bend. if abs(self.bend) > pi / 360: R = length / self.bend for atm in atoms.values(): x, y, z = atm.posn() theta = z / R x, z = R - (R - x) * cos(theta), (R - x) * sin(theta) atm.setposn(V(x, y, z)) def trimCarbons(): """ Trim all the carbons that only have one carbon neighbor. """ for i in range(2): for atm in atoms.values(): if not atm.is_singlet() and len(atm.realNeighbors()) == 1: atm.kill() trimCarbons() # If we're not picky about endings, we don't need to trim carbons if self.endings == "Capped": # buckyball endcaps addEndcap(mol, length, self.getRadius()) if self.endings == "Hydrogen": # hydrogen terminations for atm in atoms.values(): atm.Hydrogenate() elif self.endings == "Nitrogen": # nitrogen terminations. # This option has been removed from the "Endings" combo box # in the PM. 2008-05-02 --mark dstElem = PeriodicTable.getElement("N") atomtype = dstElem.find_atomtype("sp2") for atm in atoms.values(): if len(atm.realNeighbors()) == 2: atm.Transmute(dstElem, force=True, atomtype=atomtype) # Translate structure to desired position for atm in atoms.values(): v = atm.posn() atm.setposn(v + position) if PROFILE: t = sw.now() env.history.message(greenmsg("%g seconds to build %d atoms" % (t, len(atoms.values())))) if self.numwalls > 1: n += int(self.spacing * 3 + 0.5) # empirical tinkering self.build(name, assy, endPoint1, endPoint2, position, mol=mol, createPrinted=True) # Orient the Peptide. if self.numwalls == 1: # This condition ensures that MWCTs get oriented only once. self._orient(mol, endPoint1, endPoint2) return mol