def is_hbonded2(mybgf, atom1, atom2, d_crit=2.2): """ returns True if two atoms are h-bonded by a geometric definition. This definition is introduced in Grishina, N., & Buch, V. (2004). J. Chem. Phys., 120(11), 5217. 20160608: Works only for O atoms. """ if not "O" in atom1.ffType or not "O" in atom2.ffType: return False d = np.array([atom1.x, atom1.y, atom1.z]) a = np.array([atom2.x, atom2.y, atom2.z]) if mybgf.CRYSTX: pbc = mybgf.CRYSTX[:3] else: pbc = 0 # A1-H ... A2 for ano in atom1.CONECT: atom = mybgf.getAtom(ano) # this should be an H atom x = np.array([atom.x, atom.y, atom.z]) if pbc: dist = nu.pbc_dist(x, a, pbc) else: dist = nu.dist(x, a) if 1e-5 < dist < d_crit: return x # A1 ... H-A2 for ano in atom2.CONECT: atom = mybgf.getAtom(ano) # this should be an H atom x = np.array([atom.x, atom.y, atom.z]) if pbc: dist = nu.pbc_dist(x, a, pbc) else: dist = nu.dist(x, a) if 1e-5 < dist < d_crit: return x return []
def calc_hbonds(): # variables A = [] D = [] hbonds = [] d_crit = 3.5 a_crit = 30.0 for atom in mybgf.a: if selection: if "O" in atom.ffType and eval(selection): A.append(atom) if "O" in atom.ffType and eval(selection): D.append(atom) else: if "O" in atom.ffType: A.append(atom) if "O" in atom.ffType: D.append(atom) if not len(A) or not len(D): nu.die( "There are no atoms which can make H_bond (O atoms so far)!" ) # find nearest neighbor from D atom # calculate hbonds for d_atom in D: d = np.array([d_atom.x, d_atom.y, d_atom.z]) for a_atom in A: a = np.array([a_atom.x, a_atom.y, a_atom.z]) if 1e-5 < nu.pbc_dist(a, d, mytrj.pbc[t]) < d_crit: for ano in d_atom.CONECT: h_atom = mybgf.getAtom(ano) h = np.array([h_atom.x, h_atom.y, h_atom.z]) u = h - d v = a - d theta = np.dot(u, v) / norm(u) / norm(v) theta = np.degrees(arccos(theta)) if theta < a_crit: hbonds.append([ d_atom.aNo, a_atom.aNo, d_atom.z, a_atom.z ]) #donors.append(d_atom.aNo) #acceptors.append(a_atom.aNo) #hydrogens.append(h_atom.aNo) #distances.append(dist) return hbonds
def calc_hbonds(): # variables A = [] D = [] hbonds = [] d_crit = 3.5 a_crit = 30.0 for atom in mybgf.a: if selection: if "O" in atom.ffType and eval(selection): A.append(atom) if "O" in atom.ffType and eval(selection): D.append(atom) else: if "O" in atom.ffType: A.append(atom) if "O" in atom.ffType: D.append(atom) if not len(A) or not len(D): nu.die( "There are no atoms which can make H_bond (O atoms so far)!" ) # calculate hbonds for d_atom in D: d = np.array([d_atom.x, d_atom.y, d_atom.z]) neigh_anos = bt.get_neighbors_aNo(A, d, r=d_crit, pbc=mytrj.pbc[t]) for ano in neigh_anos: a_atom = mybgf.getAtom(ano) h = bt.is_hbonded2(mybgf, d_atom, a_atom) # returns hbonded H coord if len(h) != 0: a = np.array([a_atom.x, a_atom.y, a_atom.z]) dist = nu.pbc_dist(a, d, mytrj.pbc[t]) u = h - d v = a - d theta = np.dot(u, v) / norm(u) / norm(v) theta = np.degrees(arccos(theta)) hbonds.append( [d_atom.aNo, a_atom.aNo, d, a, dist, theta]) return hbonds
def is_hbonded(mybgf, atom1, atom2, d_crit=3.5, a_crit=30.0): """ returns True if two atoms are h-bonded with a popular geometric definition d(O..O) = 3.5 and A(H-O-O) < 30' 20160529: Works only for O atoms. """ from numpy import arccos from numpy.linalg import norm if not "O" in atom1.ffType or not "O" in atom2.ffType: return False d = np.array([atom1.x, atom1.y, atom1.z]) a = np.array([atom2.x, atom2.y, atom2.z]) if 1e-5 < nu.pbc_dist(a, d, mybgf.CRYSTX[:3]) < d_crit: for ano in atom1.CONECT: # atom1: donor, atom2: acceptor h_atom = mybgf.getAtom(ano) h = np.array([h_atom.x, h_atom.y, h_atom.z]) u = h - d v = a - d theta = np.dot(u, v) / norm(u) / norm(v) theta = np.degrees(arccos(theta)) if theta < a_crit: return True for ano in atom2.CONECT: # atom2: donor, atom1: acceptor h_atom = mybgf.getAtom(ano) h = np.array([h_atom.x, h_atom.y, h_atom.z]) u = h - d v = a - d theta = np.dot(u, v) / norm(u) / norm(v) theta = np.degrees(arccos(theta)) if theta < a_crit: return True return False
def calc_hbonds(): hbonds = [] d_crit = 3.6 if selection: for atom in mybgf.a: if "O" in atom.ffType and eval(selection): A.append(atom) if "O" in atom.ffType and eval(selection): D.append(atom) # calculate hbonds for d_atom in D: d = np.array([d_atom.x, d_atom.y, d_atom.z]) # donor coord neigh_anos = bt.get_neighbors_aNo(A, d, r=d_crit, pbc=mytrj.pbc[t], k=6) for ano in neigh_anos: a_atom = mybgf.getAtom(ano) a = np.array([a_atom.x, a_atom.y, a_atom.z]) # acceptor coord dist = nu.pbc_dist(a, d, mytrj.pbc[t]) if dist > 2.0: for ano in d_atom.CONECT: h_atom = mybgf.getAtom(ano) h = np.array([h_atom.x, h_atom.y, h_atom.z]) u = h - d v = a - d theta = np.dot(u, v) / norm(u) / norm(v) #theta = np.degrees(arccos(theta)) if -0.17364817766693034885171662676931 < theta < 1.0: hbonds.append([dist, np.degrees(theta)]) return hbonds
def calc_hbonds(mybgf, selection=""): # variables A = [] D = [] hbonds = [] d_crit = 3.5 a_crit = 30.0 for atom in mybgf.a: if selection: if "O" in atom.ffType and eval(selection): A.append(atom) if "O" in atom.ffType and eval(selection): D.append(atom) else: if "O" in atom.ffType: A.append(atom) if "O" in atom.ffType: D.append(atom) if not len(A) or not len(D): nu.die("There are no atoms which can make H_bond (O atoms so far)!") # calculate hbonds for d_atom in D: d = np.array([d_atom.x, d_atom.y, d_atom.z]) # donor coord neigh_anos = bt.get_neighbors_aNo(A, d, r=d_crit, pbc=mytrj.pbc[t], k=6) donors = [d_atom.aNo] + d_atom.CONECT for ano in neigh_anos: a_atom = mybgf.getAtom(ano) a = np.array([a_atom.x, a_atom.y, a_atom.z]) # acceptor coord acceptors = [a_atom.aNo] + a_atom.CONECT for ano in d_atom.CONECT: h_atom = mybgf.getAtom(ano) h = np.array([h_atom.x, h_atom.y, h_atom.z]) u = h - d v = a - d theta = np.dot(u, v) / norm(u) / norm(v) theta = np.degrees(arccos(theta)) if theta < a_crit: # HBond exists dist = nu.pbc_dist(a, d, mytrj.pbc[t]) dist_ah = nu.pbc_dist(d, h, mytrj.pbc[t]) # E_vdw sigma_r = O_sigma / dist sigma_r_6 = sigma_r**6 sigma_r_12 = sigma_r**12 E_vdw = 4.0 * O_epsilon * (sigma_r_12 - sigma_r_6) # E_vdw in kcal/mol # E_coul E_coul = 0.0 for i, j in itertools.product(donors, acceptors): atom1 = mybgf.getAtom(i) atom2 = mybgf.getAtom(j) a1 = [atom1.x, atom1.y, atom1.z] a2 = [atom2.x, atom2.y, atom2.z] dist_ij = nu.pbc_dist(a1, a2, mytrj.pbc[t]) E_coul += 332.06371 * atom1.charge * atom2.charge / dist_ij # E_coul in kcal/mol # E_hbond E_hbond = E_coul + E_vdw # E_hbond = E_vdw + E_coul # update for v4 # angle between H-O-H plane and O..O vector H1 = mybgf.getAtom(d_atom.CONECT[0]) h1 = [H1.x, H1.y, H1.z] # H1 H2 = mybgf.getAtom(d_atom.CONECT[1]) h2 = [H2.x, H2.y, H2.z] # H2 p = d - h1 q = d - h2 n = np.cross(p, q) # normal vector of the H1-O-H2 plane m = a - d # O..O vector alpha = np.dot(n, m) / norm(n) / norm(m) alpha = np.degrees(arcsin( alpha)) # angle between H-O-H plane and O..O vector hbonds.append([ d_atom.aNo, a_atom.aNo, d, a, dist, theta, [E_coul, E_vdw, E_hbond], dist_ah, alpha ]) # v4 return hbonds
def make_infinite(self, *args): ''' args: filename to save ''' print("CNT.py: make_infinite()") if len(args) > 1: nu.warn("make_infinite(): Too many arguments.") if not self.isPBCadjusted: nu.warn( "make_infinite(): You are trying to make infinite NT without adjusting PBC." ) #return 0; print("\t* Found " + str(len(self.NTatoms)) + " atoms for the nanotube.") print("\t* Found " + str(len(self.WATatoms)) + " atoms for water.") if self.type == "BNT": self.detach_hydrogen() aNo_pair = [] for atom in self.NTatoms: if len(atom.CONECT) == 3: continue x = np.array([atom.x, atom.y, atom.z]) min_atom_aNo = 100000 # placeholder min_atom_d = 10000.0 for atom2 in self.NTatoms: if (not atom2.aNo in atom.CONECT) and len(atom2.CONECT) == 2: if (atom.rName == atom2.rName) and ( self.type == "CNT" or atom.ffType != atom2.ffType): y = np.array([atom2.x, atom2.y, atom2.z]) if -1.0 < ((x - y) * self.axismask).sum() < 1.0: continue # prevent adjacent atoms d = nu.pbc_dist(x, y, self.pbc) if d < min_atom_d: min_atom_aNo = atom2.aNo min_atom_d = d aNo_pair.append([atom.aNo, min_atom_aNo]) for i in aNo_pair: self.bgfmodel.connectAtoms(i[0], i[1]) self.check_connectivity() self.bgfmodel.renumber() if len(args) == 0: value = raw_input( "Do you want to save the infinite Nanotube structure to BGF file [N]? " ) if "y" in value.lower(): filename = self.model_filename[:-4] + ".infinite.bgf" value = raw_input("Filename to save [" + filename + "]? ") or filename self.bgfmodel.saveBGF(value) elif len(args) == 1: self.bgfmodel.saveBGF(args[0])
def countWaterCNT(bgf_file, trj_file, d_crit=3.5, selection='', nsample=0, silent=False): ### init # constants deg_109p47 = math.radians(109.47) # variables timestep = 0 l_timestep = [] line = [] n_header = 0 avg_angles = [] avg_diheds = [] bin = np.arange(0.0, 181.0, 1.0) t1 = 0 t2 = 0 # clock myBGF = bgf.BgfFile(bgf_file) myTRJ = open(trj_file) myTRJ.seek(0) myDAT = open(bgf_file[:-4] + ".AOP.dat", "w") myDAT.write("#timestep\tAOP\tF4\n") # how many steps to go? #mytrj = lt.lammpstrj(trj_file) mytrj = Trj(trj_file) l_timestep = mytrj.load() n_timestep = len(l_timestep) l_timestep.sort() requested_timesteps = l_timestep[-nsample:] print( "Using neighboring water distance criteria %4.1f A (should be consistent with the coordination number)" % d_crit) print("The trajectory contains " + str(n_timestep) + " timesteps.") # Find header of the trajectory file while 1: templine = myTRJ.readline() line.append(templine.strip('\n').strip('ITEM: ')) n_header += 1 if "ITEM: ATOMS" in templine: break # INITIAL trajectory information timestep = int(line[1]) natoms = int(line[3]) boxsize = [ line[5].split(' ')[0], line[5].split(' ')[1], line[6].split(' ')[0], line[6].split(' ')[1], line[7].split(' ')[0], line[7].split(' ')[1] ] boxsize = [float(i) for i in boxsize] keywords = line[8].strip('ATOMS ') # for every shot in the trajectory file update BGF and manipulate dumpatom = get_line(trj_file) processed_step = 0 t1 = t2 = 0 elapsed_time = 0 while 1: ### Show progress t1 = time.time() remaining_time = elapsed_time * (len(requested_timesteps) - processed_step) sys.stdout.write('\r' + "Reading timestep.. " + str(timestep) + " (Remaining time: " + "{0:4.1f}".format(remaining_time) + " seconds, " + "{0:4.1f} minutes".format(remaining_time / 60) + ")") sys.stdout.flush() ### Read try: chunk = [next(dumpatom) for i in range(natoms + n_header)] except StopIteration: break timestep = int(chunk[1]) natoms = int(chunk[3]) boxsize = [ chunk[5].split(' ')[0], chunk[5].split(' ')[1], chunk[6].split(' ')[0], chunk[6].split(' ')[1], chunk[7].split(' ')[0], chunk[7].split(' ')[1] ] boxsize = [float(i) for i in boxsize] boxsize = [(boxsize[1] - boxsize[0]), (boxsize[3] - boxsize[2]), (boxsize[5] - boxsize[4])] keywords = chunk[8].split('ATOMS ')[1].strip('\n').split(' ') if not timestep in requested_timesteps: continue ### update myBGF with trajectory information ### natom_bgf = len(myBGF.a) # number of atoms in BGF file if not natom_bgf == natoms: nu.die( "Number of atoms in trajectory file does not match with BGF file." ) mode = "" if 'xs' in keywords or 'ys' in keywords or 'zs' in keywords: mode = 'scaled' elif 'x' in keywords or 'y' in keywords or 'z' in keywords: mode = 'normal' elif 'xu' in keywords or 'yu' in keywords or 'zu' in keywords: mode = 'unwrapped' # actual coordinate coordinfo = chunk[9:] # assume that coordinfo is similar to ['id', 'type', 'xs', 'ys', 'zs', 'ix', 'iy', 'iz'] for atomline in coordinfo: atomcoord = atomline.split(' ') atom = myBGF.getAtom(int(atomcoord[0])) if mode == 'scaled': atom.x = float(atomcoord[2]) * boxsize[0] atom.y = float(atomcoord[3]) * boxsize[1] atom.z = float(atomcoord[4]) * boxsize[2] elif mode == 'unwrapped': atom.x = float(atomcoord[2]) atom.y = float(atomcoord[3]) atom.z = float(atomcoord[4]) elif mode == 'normal': try: ix_index = keywords.index('ix') iy_index = keywords.index('iy') iz_index = keywords.index('iz') except ValueError: nu.warn( "No image information no the trajectory file. Will be treated as unwrapped." ) atom.x = float(atomcoord[2]) atom.y = float(atomcoord[3]) atom.z = float(atomcoord[4]) else: atom.x = (int(atomcoord[ix_index]) * boxsize[0]) + float( atomcoord[2]) atom.y = (int(atomcoord[iy_index]) * boxsize[1]) + float( atomcoord[3]) atom.z = (int(atomcoord[iz_index]) * boxsize[2]) + float( atomcoord[4]) try: for i in range(0, 3): myBGF.CRYSTX[i] = boxsize[i] except: pass # apply periodic condition myBGF = bgftools.periodicMoleculeSort(myBGF, myBGF.CRYSTX, silent=True) ### myBGF update complete! ### # get water OW O = [] anos_O = [] for atom in myBGF.a: if selection: if "O" in atom.ffType and eval(selection): O.append(atom) anos_O.append(atom.aNo) else: if "O" in atom.ffType: O.append(atom) anos_O.append(atom.aNo) if not len(O): nu.die("There are no O atoms which satisfies %s!" % selection) ### calculate AOP and F4 sys.stdout.write('AOP..... ') sys.stdout.flush() coords = [] anos = [] for atom in O: coords.append([atom.x, atom.y, atom.z]) anos.append(atom.aNo) pbc = np.array(myBGF.CRYSTX[:3]) coords = np.array(coords) tree = pkdtree.PeriodicKDTree( pbc, coords) # KDtree for distance calculation # analyze local water structures for a timestep n_neighbors = [] angles = [] diheds = [] for atom in O: # local variables # find neighbors neighbors = [] # list of O atoms near centerO centerO = np.array([atom.x, atom.y, atom.z]) d, ndx = tree.query(centerO, k=6) index = np.where((d >= 1e-4) & (d <= d_crit)) # discard self for i in ndx[index]: neighbors.append(myBGF.getAtom(anos[i])) # calculate O1-O-O2 angle for i, j in itertools.combinations(neighbors, 2): if not "O" in i.ffType or not "O" in j.ffType: nu.die("Wrong atom found.") x = [i.x, i.y, i.z] y = [j.x, j.y, j.z] pbc_dist = nu.pbc_dist(x, y, pbc) dist = nu.dist(x, y) if abs(pbc_dist - dist) > 0.1: continue # discard atoms over pbc boundaries # angle angle = nu.angle(x, centerO, y, radians=False) angles.append(angle) n_neighbors.append( len(neighbors)) # number of neighboring water molecules # calculate dihedral for i in neighbors: # find aNos of H located farthest among all combinations hO_anos = atom.CONECT hi_anos = i.CONECT max_dist_pair = [] max_dist = 0.0 for k, l in itertools.product(hO_anos, hi_anos): h1 = myBGF.getAtom(k) # H atom connected with atom h2 = myBGF.getAtom(l) # H atom connected with i dist = bgf.distance(h1, h2) if dist > max_dist: max_dist = dist max_dist_pair = [ h1, h2 ] # now we have two H atoms in maximum distance connected with atom and i # dihedral angle phi = bgf.dihedral(max_dist_pair[0], atom, i, max_dist_pair[1], radians=False) diheds.append(phi) hist_angle, _ = np.histogram(angles, bins=bin, normed=True) hist_dihed, _ = np.histogram(diheds, bins=bin, normed=True) avg_angles.append(hist_angle) avg_diheds.append(hist_dihed) sys.stdout.write( 'Done ') sys.stdout.flush() # write output processed_step += 1 t2 = time.time() # time mark elapsed_time = t2 - t1 avg_angles = np.mean(np.array(avg_angles), axis=0) avg_diheds = np.mean(np.array(avg_diheds), axis=0) #print(avg_diheds) myDAT.write("#angles population\n") for index, i in enumerate(avg_angles): myDAT.write("%8.2f %8.5f\n" % (_[index], avg_angles[index])) myDAT.write("\n\n") myDAT.write("#diheds population\n") for index, i in enumerate(avg_diheds): myDAT.write("%8.2f %8.5f\n" % (_[index], avg_diheds[index])) myDAT.close() print('') return 1
if len(sys.argv) < 2: print(usage) sys.exit(0) bgffile = bgf.BgfFile(sys.argv[1]) if sys.argv[2]: delta = float(sys.argv[2]) else: delta = 0.001 c = [atom for atom in bgffile.a if "C_2G" in atom.ffType] dist = [] for atom in tqdm.tqdm(c, ncols=120, desc="Calculating"): x = [atom.x, atom.y, atom.z] for ano in atom.CONECT: atom2 = bgffile.getAtom(ano) y = [atom2.x, atom2.y, atom2.z] dist.append(nu.pbc_dist(x, y, bgffile.CRYSTX[:3])) dist = np.array(dist) if delta < (dist.max() - dist.min()): print(dist.min(), dist.max(), dist.max()-dist.min()) bin = np.arange(dist.min(), dist.max(), delta) distr, _ = np.histogram(dist, bins=bin) norm_distr = distr / float(distr.sum()) pprint(norm_distr) else: print("average: %s, stdev: %s" % (dist.mean(), dist.std()))
def calc_hbonds(): # variables A = [] D = [] hbonds = [] d_crit = 3.5 a_crit = 30.0 for atom in mybgf.a: if selection: if "O" in atom.ffType and eval(selection): A.append(atom) if "O" in atom.ffType and eval(selection): D.append(atom) else: if "O" in atom.ffType: A.append(atom) if "O" in atom.ffType: D.append(atom) if not len(A) or not len(D): nu.die( "There are no atoms which can make H_bond (O atoms so far)!" ) # calculate hbonds for d_atom in D: d = np.array([d_atom.x, d_atom.y, d_atom.z]) # donor coord neigh_anos = bt.get_neighbors_aNo(A, d, r=d_crit, pbc=mytrj.pbc[t], k=6) donors = [d_atom.aNo] + d_atom.CONECT for ano in neigh_anos: a_atom = mybgf.getAtom(ano) a = np.array([a_atom.x, a_atom.y, a_atom.z]) # acceptor coord acceptors = [a_atom.aNo] + a_atom.CONECT for ano in d_atom.CONECT: h_atom = mybgf.getAtom(ano) h = np.array([h_atom.x, h_atom.y, h_atom.z]) u = h - d v = a - d theta = np.dot(u, v) / norm(u) / norm(v) theta = np.degrees(arccos(theta)) if theta < a_crit: dist = nu.pbc_dist(a, d, mytrj.pbc[t]) dist_dh = nu.pbc_dist(d, h, mytrj.pbc[t]) # E_vdw sigma_r = O_sigma / dist sigma_r_6 = sigma_r**6 sigma_r_12 = sigma_r**12 E_vdw = 4.0 * O_epsilon * (sigma_r_12 - sigma_r_6) # Evdw in kcal/mol # E_coul E_coul = 0.0 for i, j in itertools.product(donors, acceptors): atom1 = mybgf.getAtom(i) atom2 = mybgf.getAtom(j) a1 = [atom1.x, atom1.y, atom1.z] a2 = [atom2.x, atom2.y, atom2.z] dist_ij = nu.pbc_dist(a1, a2, mytrj.pbc[t]) E_coul += 332.06371 * atom1.charge * atom2.charge / dist_ij #E_coul /= 2.0 E_hbond = E_coul + E_vdw # E_hbond = E_vdw + E_coul #print("e_coul: %f, e_coul2: %f, E_coul: %f, E_vdw: %f, E_hbond: %f, O-O dist: %f, O-H dist: %f" % (e_coul, e_coul2, E_coul, E_vdw, E_hbond, dist, dist_a_dh)) #print("E_coul: %f, E_vdw: %f, E_hbond: %f, O-O dist: %f" % (E_coul, E_vdw, E_hbond, dist)) #hbonds.append([d_atom.aNo, a_atom.aNo, d, a, dist, theta, [E_coul, E_vdw, E_hbond]]) hbonds.append([ d_atom.aNo, a_atom.aNo, d, a, dist, theta, [E_coul, E_vdw, E_hbond], dist_dh ]) return hbonds
if "S" in atom.ffType: if atom.z > avg_mo_z: atom.ffType = "S_3a" else: atom.ffType = "S_3b" # remove infinite boundary n_del = 0 for atom in tqdm.tqdm(moslayer.a, ncols=120, desc='Removing pbc bonds'): a = [atom.x, atom.y, atom.z] connected_ano = atom.CONECT for ano in connected_ano: atom2 = moslayer.getAtom(ano) a2 = [atom2.x, atom2.y, atom2.z] dist = nu.dist(a, a2) pbc_dist = nu.pbc_dist(a, a2, moslayer.CRYSTX[:3]) if dist != pbc_dist: moslayer.disconnect(moslayer.a2i[atom.aNo], moslayer.a2i[atom2.aNo]) n_del += 1 print("%d bonds are disconnected." % n_del) # remove infinite boundary n_del = 0 for atom in tqdm.tqdm(moslayer.a, ncols=120, desc='Removing pbc bonds 2'): a = [atom.x, atom.y, atom.z] connected_ano = atom.CONECT for ano in connected_ano: atom2 = moslayer.getAtom(ano) a2 = [atom2.x, atom2.y, atom2.z] dist = nu.dist(a, a2)
def make_infinite(mybgf): def calc_bond_dist(pbc=False): dist = [] for atom in mybgf.a: if not "C_2G" in atom.ffType: continue for ano in atom.CONECT: atom2 = mybgf.getAtom(ano) if not "C_2G" in atom2.ffType: continue if pbc: dist.append(bgf.pbc_distance(atom, atom2, mybgf.CRYSTX[:3])) else: dist.append(bgf.distance(atom, atom2)) return np.average(dist), np.std(dist) distance, _ = calc_bond_dist() # TODO: need to distinguish armchair and zigzag boundaries minmax_x = bgftools.atoms_minmax(mybgf, "x", selection="'C_2G' in atom.ffType") minmax_y = bgftools.atoms_minmax(mybgf, "y", selection="'C_2G' in atom.ffType") pbc_x = (minmax_x[1] - minmax_x[0]) + distance * np.cos( np.radians(30)) # in 5nm x 5nm graphene, x boundaries are armchair pbc_y = (minmax_y[1] - minmax_y[0] ) + distance # in 5nm x 5nm graphene, y boundaries are zigzag mybgf.PERIOD = "111" mybgf.AXES = "ZYX" mybgf.SGNAME = "P 1 1 1" mybgf.CELLS = [-1, 1, -1, 1, -1, 1] mybgf.CRYSTX = [pbc_x, pbc_y, 10.0, 90.0, 90.0, 90.0] print("Assigning new pbc: %s" % mybgf.CRYSTX) # init aNo_pair = [] pbc = mybgf.CRYSTX[:3] edge_atoms = [] # loop for atom in tqdm(mybgf.a, ncols=80, miniters=100, desc="Analyzing atoms"): if not "C" in atom.ffType: continue if len(atom.CONECT) == 3: continue x = np.array([atom.x, atom.y, atom.z]) min_atom_aNo = 100000 # placeholder min_atom_d = 10000.0 for atom2 in mybgf.a: if len(atom2.CONECT) == 3: continue if atom.aNo != atom2.aNo: if (not atom2.aNo in atom.CONECT) and len( atom2.CONECT) != 3 and (atom.rName == atom2.rName): y = np.array([atom2.x, atom2.y, atom2.z]) d = nu.pbc_dist(x, y, pbc) if d < min_atom_d: min_atom_aNo = atom2.aNo min_atom_d = d #aNo_pair.append([atom.aNo, min_atom_aNo]) if min_atom_aNo == 100000: nu.die("Failed to find the closest atom for atom number " + str(atom.aNo)) mybgf.connectAtoms(atom.aNo, min_atom_aNo) #for i in aNo_pair: # mybgf.connectAtoms(i[0], i[1]) check_connectivity() # check bond distance stdev _, stdev = calc_bond_dist(pbc=True) if stdev <= 1e5: print("The structure seems to have regular distance over pbc.") else: nu.die("PBC assignment does not give a regular distances over pbc.") if out_file == "": filename = bgf_file.split(".bgf")[0] + ".infinite.bgf" else: filename = out_file value = raw_input("Filename to save [" + filename + "]? ") or filename mybgf.saveBGF(value)
def hbonds(mybgf, selection = ""): ''' This function calculates Hydrogen bonds(hbonds) between O(donor) and O(acceptor), especially for water molecules. Input: - bgf.BgfFile mybgf - string selection: a region to search donor and acceptor atoms in mybgf Output: - int n_sel_atoms: number of hbond-able atoms in the selection region - list hbonds: self-descriptive ''' # variables pbc = mybgf.CRYSTX[:3] d_crit = 3.5; a_crit = 30.0 # Chandler's criteria if selection: A = [atom for atom in mybgf.a if "O" in atom.ffType and eval(selection)] D = [atom for atom in mybgf.a if "O" in atom.ffType and eval(selection)] else: A = [atom for atom in mybgf.a if "O" in atom.ffType] D = [atom for atom in mybgf.a if "O" in atom.ffType] if not len(A) or not len(D): nu.warn("There are no atoms which can make hbonds (O atoms so far)!") return # calculate hbonds hbonds = []; for d_atom in D: d = np.array([d_atom.x, d_atom.y, d_atom.z]) # donor coord neigh_anos = bt.get_neighbors_aNo(A, d, r=d_crit, pbc=pbc, k=5) donors = [d_atom.aNo] + d_atom.CONECT for ano in neigh_anos: a_atom = mybgf.getAtom(ano) a = np.array([a_atom.x, a_atom.y, a_atom.z]) # acceptor coord acceptors = [a_atom.aNo] + a_atom.CONECT for ano in d_atom.CONECT: h_atom = mybgf.getAtom(ano) h = np.array([h_atom.x, h_atom.y, h_atom.z]) u = h - d; v = a - d; theta = np.dot(u, v) / norm(u) / norm(v); theta = np.degrees(arccos(theta)) if theta < a_crit: # HBond exists # calculate pair energy as Hbond energy dist = nu.pbc_dist(a, d, pbc) dist_ah = nu.pbc_dist(d, h, pbc) # E_vdw sigma_r = O_sigma / dist; sigma_r_6 = sigma_r**6; sigma_r_12 = sigma_r**12 E_vdw = 4.0 * O_epsilon * (sigma_r_12 - sigma_r_6); # E_vdw in kcal/mol # E_coul E_coul = 0.0 for i, j in itertools.product(donors, acceptors): atom1 = mybgf.getAtom(i) atom2 = mybgf.getAtom(j) a1 = [atom1.x, atom1.y, atom1.z] a2 = [atom2.x, atom2.y, atom2.z] dist_ij = nu.pbc_dist(a1, a2, pbc) E_coul += 332.06371 * atom1.charge * atom2.charge / dist_ij # E_coul in kcal/mol # E_hbond E_hbond = E_coul + E_vdw # E_hbond = E_vdw + E_coul ''' # calculate dreiding style hbond energy: http://lammps.sandia.gov/doc/pair_hbond_dreiding.html # REMARK: tested on bulk water, ice, and confined water between MoS2 but achieved suspicious values: # 6.0 $\AA$ maximum: -7.925 kcal/mol # 8.0 $\AA$ maximum: -7.875 kcal/mol # 11.0 $\AA$ maximum: -8.025 kcal/mol # ice maximum: -8.325 kcal/mol # water maximum: -7.775 kcal/mol sigma = 2.75 # in A epsilon = 9.0 # in kcal/mol u = d - h; v = a - h; cos_theta = np.dot(u, v) / norm(u) / norm(v); #cos_theta = np.cos(theta) sigma_r = sigma / dist; sigma_r_2 = sigma_r**2; sigma_r_12 = sigma_r_2**6; sigma_r_10 = sigma_r_2**5 E_hbond = epsilon * ( 5 * sigma_r_12 - 6 * sigma_r_10 ) * cos_theta**4 ''' hbonds.append([d_atom.aNo, a_atom.aNo, dist, theta, E_hbond]) # v5 return hbonds