예제 #1
0
 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()
예제 #2
0
 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()
예제 #3
0
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)
예제 #4
0
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)
예제 #5
0
    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")
예제 #6
0
    def populate(self, mol, length, bn_members = False):

        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

        evenAtomDict = { }
        oddAtomDict = { }
        bondDict = { }
        mfirst = [ ]
        mlast = [ ]

        for n in range(self.n):
            mmin, mmax = self.mlimits(-.5 * length, .5 * length, n)
            mfirst.append(mmin)
            mlast.append(mmax)
            for m in range(mmin, mmax+1):
                x, y, z = self.xyz(n, m)
                if bn_members:
                    atm = add("B", x, y, z)
                else:
                    atm = add("C", x, y, z)
                evenAtomDict[(n,m)] = atm
                bondDict[atm] = [(n,m)]
                x, y, z = self.xyz(n+1./3, m+1./3)
                if bn_members:
                    atm = add("N", x, y, z, 'sp3')
                else:
                    atm = add("C", x, y, z)
                oddAtomDict[(n,m)] = atm
                bondDict[atm] = [(n+1, m), (n, m+1)]

        # m goes axially along the nanotube, n spirals around the tube
        # like a barber pole, with slope depending on chirality. If we
        # stopped making bonds now, there'd be a spiral strip of
        # missing bonds between the n=self.n-1 row and the n=0 row.
        # So we need to connect those. We don't know how the m values
        # will line up, so the first time, we need to just hunt for the
        # m offset. But then we can apply that constant m offset to the
        # remaining atoms along the strip.
        n = self.n - 1
        mmid = (mfirst[n] + mlast[n]) / 2
        atm = oddAtomDict[(n, mmid)]
        class FoundMOffset(Exception): pass
        try:
            for m2 in range(mfirst[0], mlast[0] + 1):
                atm2 = evenAtomDict[(0, m2)]
                diff = atm.posn() - atm2.posn()
                if dot(diff, diff) < self.maxlensq:
                    moffset = m2 - mmid
                    # Given the offset, zipping up the rows is easy.
                    for m in range(mfirst[n], mlast[n]+1):
                        atm = oddAtomDict[(n, m)]
                        bondDict[atm].append((0, m + moffset))
                    raise FoundMOffset()
            # If we get to this point, we never found m offset.
            # If this ever happens, it indicates a bug.
            raise Exception, "can't find m offset"
        except FoundMOffset:
            pass

        # Use the bond information to bond the atoms
        for (dict1, dict2) in [(evenAtomDict, oddAtomDict),
                               (oddAtomDict, evenAtomDict)]:
            for n, m in dict1.keys():
                atm = dict1[(n, m)]
                for n2, m2 in bondDict[atm]:
                    try:
                        atm2 = dict2[(n2, m2)]
                        if not atoms_are_bonded(atm, atm2):
                            if bn_members:
                                bond_atoms(atm, atm2, V_SINGLE)
                            else:
                                bond_atoms(atm, atm2, V_GRAPHITE)
                    except KeyError:
                        pass
예제 #7
0
 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)
예제 #8
0
    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
예제 #9
0
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
예제 #10
0
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)
예제 #11
0
    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
예제 #12
0
    def populate(self, mol, length, bn_members=False):
        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

        evenAtomDict = {}
        oddAtomDict = {}
        bondDict = {}
        mfirst = []
        mlast = []

        for n in range(self.n):
            mmin, mmax = self.mlimits(-.5 * length, .5 * length, n)
            mfirst.append(mmin)
            mlast.append(mmax)
            for m in range(mmin, mmax + 1):
                x, y, z = self.xyz(n, m)
                if bn_members:
                    atm = add("B", x, y, z)
                else:
                    atm = add("C", x, y, z)
                evenAtomDict[(n, m)] = atm
                bondDict[atm] = [(n, m)]
                x, y, z = self.xyz(n + 1. / 3, m + 1. / 3)
                if bn_members:
                    atm = add("N", x, y, z, 'sp3')
                else:
                    atm = add("C", x, y, z)
                oddAtomDict[(n, m)] = atm
                bondDict[atm] = [(n + 1, m), (n, m + 1)]

        # m goes axially along the nanotube, n spirals around the tube
        # like a barber pole, with slope depending on chirality. If we
        # stopped making bonds now, there'd be a spiral strip of
        # missing bonds between the n=self.n-1 row and the n=0 row.
        # So we need to connect those. We don't know how the m values
        # will line up, so the first time, we need to just hunt for the
        # m offset. But then we can apply that constant m offset to the
        # remaining atoms along the strip.
        n = self.n - 1
        mmid = (mfirst[n] + mlast[n]) / 2
        atm = oddAtomDict[(n, mmid)]

        class FoundMOffset(Exception):
            pass

        try:
            for m2 in range(mfirst[0], mlast[0] + 1):
                atm2 = evenAtomDict[(0, m2)]
                diff = atm.posn() - atm2.posn()
                if dot(diff, diff) < self.maxlensq:
                    moffset = m2 - mmid
                    # Given the offset, zipping up the rows is easy.
                    for m in range(mfirst[n], mlast[n] + 1):
                        atm = oddAtomDict[(n, m)]
                        bondDict[atm].append((0, m + moffset))
                    raise FoundMOffset()
            # If we get to this point, we never found m offset.
            # If this ever happens, it indicates a bug.
            raise Exception, "can't find m offset"
        except FoundMOffset:
            pass

        # Use the bond information to bond the atoms
        for (dict1, dict2) in [(evenAtomDict, oddAtomDict),
                               (oddAtomDict, evenAtomDict)]:
            for n, m in dict1.keys():
                atm = dict1[(n, m)]
                for n2, m2 in bondDict[atm]:
                    try:
                        atm2 = dict2[(n2, m2)]
                        if not atoms_are_bonded(atm, atm2):
                            if bn_members:
                                bond_atoms(atm, atm2, V_SINGLE)
                            else:
                                bond_atoms(atm, atm2, V_GRAPHITE)
                    except KeyError:
                        pass
예제 #13
0
    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)
예제 #14
0
    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")