def addCarbons(chopSpace, R=radius): # a buckyball has no more than about 6*r**2 atoms, r in angstroms # each cap is ideally a half-buckyball for i in range(int(3.0 * R**2)): regional_singlets = filter(lambda atm: chopSpace(atm) and atm.is_singlet(), mol.atoms.values()) for s in regional_singlets: s.setposn(projectOntoSphere(s.posn())) if len(regional_singlets) < 3: # there won't be anything to bond to anyway, let the user # manually adjust the geometry return singlet_pair = None try: for s1 in regional_singlets: s1p = s1.posn() for s2 in regional_singlets: if s2.key > s1.key and \ vlen(s2.posn() - s1p) < bondlength: singlet_pair = (s1, s2) # break out of both for-loops raise Exception except: pass if singlet_pair is not None: # if there is an existing pair of singlets that's close than one bond # length, use those to make the newguy, so he'll have one open bond left sing1, sing2 = singlet_pair owner1, owner2 = sing1.realNeighbors()[0], sing2.realNeighbors()[0] newpos1 = walk_great_circle(owner1.posn(), sing1.posn(), bondlength) newpos2 = walk_great_circle(owner2.posn(), sing2.posn(), bondlength) newpos = 0.5 * (newpos1 + newpos2) regional_singlets.remove(sing1) regional_singlets.remove(sing2) else: # otherwise choose any pre-existing bond and stick the newguy on him # prefer a bond whose real atom already has two real neighbors preferred = filter(lambda atm: len(atm.realNeighbors()[0].realNeighbors()) == 2, regional_singlets) if preferred: sing = preferred[0] else: sing = regional_singlets[0] owner = sing.realNeighbors()[0] newpos = walk_great_circle(owner.posn(), sing.posn(), bondlength) regional_singlets.remove(sing) ngen = NeighborhoodGenerator(mol.atoms.values(), 1.1 * bondlength) # do not include new guy in neighborhood, add him afterwards newguy = Atom('C', newpos, mol) newguy.set_atomtype('sp2') # if the new atom is close to an older atom, merge them: kill the newer # atom, give the older one its neighbors, nudge the older one to the midpoint for oldguy in ngen.region(newpos): if vlen(oldguy.posn() - newpos) < 0.4: newpos = 0.5 * (newguy.posn() + oldguy.posn()) newguy.setposn(newpos) ngen.remove(oldguy) oldguy.kill() break # Bond with anybody close enough. The newer make_bonds # code doesn't seem to handle this usage very well. for oldguy in ngen.region(newpos): r = oldguy.posn() - newpos rlen = vlen(r) if (len(newguy.realNeighbors()) < 3 and rlen < 1.1 * bondlength): if rlen < 0.7 * bondlength: # nudge them apart nudge = ((0.7 * bondlength - rlen) / rlen) * r oldguy.setposn(oldguy.posn() + 0.5 * r) newguy.setposn(newguy.posn() - 0.5 * r) bond_atoms(newguy, oldguy, V_GRAPHITE) cleanupSinglets(newguy) cleanupSinglets(oldguy) if len(newguy.realNeighbors()) > 3: print 'warning: too many bonds on newguy' # Try moving the new guy around to make his bonds closer to bondlength but # keep him on or near the surface of the sphere. Use Newton's method in # three dimensions. def error(posn): e = (vlen(posn - sphere_center) - radius) ** 2 for atm in newguy.realNeighbors(): e += (vlen(atm.posn() - posn) - bondlength)**2 return e p = newguy.posn() for i in range(2): h = 1.0e-4 e0 = error(p) gradient = V((error(p + V(h, 0, 0)) - e0) / h, (error(p + V(0, h, 0)) - e0) / h, (error(p + V(0, 0, h)) - e0) / h) p = p - (e0 / vlen(gradient)**2) * gradient newguy.setposn(p) # we may need to reposition singlets for atm in ngen.region(newguy.posn()): cleanupSinglets(atm) cleanupSinglets(newguy)
def 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)