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 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
def getHBond(bgf_file, trj_file, selection='', silent=False): # init timestep = 0 l_timestep = [] line = [] n_header = 0 t1 = 0 t2 = 0 # clock hbond_dat = dict() d_crit = 3.5 a_crit = 30.0 myBGF = bgf.BgfFile(bgf_file) myTRJ = open(trj_file) myTRJ.seek(0) myPickle_file = bgf_file[:-4] + ".hbond.pickle" myDAT = open(bgf_file[:-4] + ".hbond.count.dat", "w") myDAT.write(str(sys.argv) + "\n") # how many steps to go? wc_trj_file = popen("grep TIMESTEP " + trj_file + " | wc -l ").read() n_timestep = int(wc_trj_file.split()[0]) 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 * (n_timestep - 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(' ') ### 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 #nu.warn("Crystal information error: is this file not periodic?") pbc = myBGF.CRYSTX[:3] # apply periodic condition myBGF = bgftools.periodicMoleculeSort(myBGF, myBGF.CRYSTX, silent=True) ### myBGF update complete! ### # get donor and acceptor # donors: O in ACT A = [] D = [] 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 (especially O atoms)!" ) ### find hydrogen bonds # for all pairs of OC-HW sys.stdout.write('Hbonds.. ') sys.stdout.flush() hbonds = [] donors = [] acceptors = [] hydrogens = [] angles = [] distances = [] 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]) dist = nu.dist(d, a) #dist = bgf.distance(d_atom, a_atom) if 0.001 < dist < d_crit: # check H angle 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)) #theta = bgf.angle(h_atom, d_atom, a_atom, radians=False) if theta < a_crit: hbonds.append([d_atom.aNo, a_atom.aNo]) donors.append(d_atom.aNo) acceptors.append(a_atom.aNo) hydrogens.append(h_atom.aNo) distances.append(dist) angles.append(theta) #n_hbond += 1 #print("%s %s" % (d_atom.rNo, a_atom.rNo)) hbond_dat[timestep] = hbonds sys.stdout.write( 'Done ') sys.stdout.flush() print("") for i in zip(donors, acceptors, hydrogens, distances, angles): print i sys.exit(0) myDAT.write("%d %d\n" % (timestep, len(hbonds))) # write output #myBGF.saveBGF(bgf_file[:-4] + "." + str(timestep) + ".bgf") #myBGF2.saveBGF(bgf_file[:-4] + "." + str(timestep) + ".bgf") t2 = time.time() # time mark elapsed_time = t2 - t1 pickle_f = open(myPickle_file, 'w') pickle.dump(hbond_dat, pickle_f) print('') myDAT.close() return 1
for atom in tqdm.tqdm(moslayer.a, ncols=120, desc='Assigning ffTypes'): 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]
def analyze(bgf_file, trj_file, ff_file='', name='', selection='', d_crit=3.5): '''analyze something within a timestep in a series of lammps trajectory. ''' # variables result_angles = dict() result_diheds = dict() print( "Using neighboring water distance criteria %4.1f A (should be consistent with the coordination number)" % d_crit) # inner functions def get_line(file): with open(file, 'r') as f: for line in f: yield line # 1. Load BGF mybgf = bgf.BgfFile(bgf_file) N_BGF_ATOMS = len(mybgf.a) # 2. Read LAMMPS Trajectory #timesteps, N_HEADER, N_ATOMS = lt.getTrjInfo(trj_file) mytrj = lt.lammpstrj(trj_file) timesteps = mytrj.load() N_HEADER = mytrj.nheader N_ATOMS = mytrj.natoms[0] N_BUFFER = N_HEADER + N_ATOMS if N_BGF_ATOMS != N_ATOMS: nu.die( "Number of atoms in trajectory file does not match with BGF file.") # 3. Determine dump style dump_keywords = mytrj.dumpstyle yes_scale = False if 'xs' in dump_keywords: yes_scale = True # 4. Update coordinates from the snapshot dump = get_line(trj_file) for t in tqdm.tqdm(timesteps, ncols=120, desc="Analyzing"): chunk = [next(dump) for i in range(N_BUFFER)] t = int(chunk[1]) mybgf.CRYSTX = mytrj.pbc[t] + [90.0, 90.0, 90.0] coords = chunk[9:] for c in coords: c = c.split(' ') atom = mybgf.getAtom(int(c[0])) if yes_scale: atom.x = float(c[2]) * pbc[0] atom.y = float(c[3]) * pbc[1] atom.z = float(c[4]) * pbc[2] else: atom.x = float(c[2]) atom.y = float(c[3]) atom.z = float(c[4]) mybgf = bt.periodicMoleculeSort(mybgf, mybgf.CRYSTX, ff_file=ff_file, silent=True) # Calculate Angles and Dihedrals for Water Structure O = [] for atom in mybgf.a: if selection: if "O" in atom.ffType and eval(selection): O.append(atom) else: if "O" in atom.ffType: O.append(atom) if not len(O): nu.warn("There are no O atoms which satisfies %s!" % selection) coords = [] anos = [] for atom in O: coords.append([atom.x, atom.y, atom.z]) anos.append(atom.aNo) coords = np.array(coords) #tree = pkdtree.PeriodicKDTree(mytrj.pbc[t], coords) # KDTree with pbc tree = scipy.spatial.KDTree( coords, leafsize=len(coords) + 1) # REMARK: normal KDTree -- do not consider over pbc angles = [] diheds = [] for atom in O: # Find neighbors coord = np.array([atom.x, atom.y, atom.z]) neighbor_O = [] # list of O atoms near centerO d, ndx = tree.query(coord, k=5) index = np.where((d >= 1e-4) & (d <= d_crit)) # discard self for i in ndx[index]: neighbor_O.append(mybgf.getAtom(anos[i])) # Angles for i, j in itertools.combinations(neighbor_O, 2): x = [i.x, i.y, i.z] y = [j.x, j.y, j.z] angle = nu.angle(x, coord, y, radians=False) angles.append([angle, i.z, atom.z, j.z]) # angles: result_angle data structure # Dihedrals: farthest H-O...O-H for all neighboring O-O pairs for atom2 in neighbor_O: # are they have an hydrogen bond? if bt.is_hbonded(mybgf, atom, atom2): hO_anos = atom.CONECT # H connected to center O hO2_anos = atom2.CONECT # H connected to neighboring O max_dist_pair = [] max_dist = 0.0 for k, l in itertools.product(hO_anos, hO2_anos): h1 = mybgf.getAtom(k) h2 = mybgf.getAtom(l) dist = nu.dist([h1.x, h1.y, h1.z], [h2.x, h2.y, h2.z]) if dist > max_dist: max_dist = dist max_dist_pair = [h1, h2] phi = bgf.dihedral(max_dist_pair[0], atom, atom2, max_dist_pair[1], radians=False) diheds.append([phi, atom.z, atom2.z]) # diheds: result_dihed data structure result_angles[t] = angles result_diheds[t] = diheds # Write-ups if name: out_file = name + '.aop' else: out_file = 'aop' angle_out_file = out_file + ".angle.pickle" dihed_out_file = out_file + ".dihed.pickle" with open(angle_out_file, 'wb') as f: pickle.dump(result_angles, f, protocol=pickle.HIGHEST_PROTOCOL) print("Success to save the angle result to a pickle file %s" % angle_out_file) with open(dihed_out_file, 'wb') as f: pickle.dump(result_diheds, f, protocol=pickle.HIGHEST_PROTOCOL) print("Success to save the dihed result to a pickle file %s" % dihed_out_file) print("Done.")