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)
Example #2
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)