def a1_a2_distance_dist(self, selection_type, selection_1, selection_2, outfilename="distance_dist.dat", cutoff=15.0): """ calculate the distance distribution between selection_1 and selection-2 selection_type: "atom_name" cutoff: ignore distances larger than that """ outfile = open(outfilename, "a") if selection_type not in ["atom_name"]: print "[ERROR] a1_a2_distance_dist(). Selection type", selection_type, "not recognized!" sys.exit() for idx_frame, frame in enumerate(self.traj.list_atoms): # monitor progress if int(len(self.traj.list_atoms) * 0.25) == idx_frame: print " 25 % done" if int(len(self.traj.list_atoms) * 0.50) == idx_frame: print " 50 % done" if int(len(self.traj.list_atoms) * 0.75) == idx_frame: print " 75 % done " for idx1, atom1 in enumerate(frame): for idx2, atom2 in enumerate(frame): if idx2 >= idx1: continue if atom1[self.traj.ATOMNAME] == selection_1 and atom2[ self.traj.ATOMNAME] == selection_2: d = pbc.pbc_distance( [atom1[self.traj.X_COORD], atom1[self.traj.Y_COORD], atom1[self.traj.Z_COORD]], \ [atom2[self.traj.X_COORD], atom2[self.traj.Y_COORD], atom2[self.traj.Z_COORD]], \ self.traj.lattice_vector_1, \ self.traj.lattice_vector_2, \ self.traj.lattice_vector_3 ) if d * 10.0 < cutoff: outfile.write(str(d * 10.0) + "\n") outfile.close()
########################################################################################## """ ase is a sh*t tool and makes a complete mess in the way it orders the molecules! we therefore have to explicitly find atoms that form a water molecule """ list_SOL = [] for atom_O in file_poscar.list_atoms: if atom_O[file_poscar.ATOMNAME] == "O": list_H_idx = [] list_X_idx = [] for idx, atom in enumerate(file_poscar.list_atoms): if atom[file_poscar.ATOMNAME] == "H": d = pbc_distance( [atom_O[file_poscar.X_COORD], atom_O[file_poscar.Y_COORD], atom_O[file_poscar.Z_COORD]], \ [atom[file_poscar.X_COORD], atom[file_poscar.Y_COORD], atom[file_poscar.Z_COORD]], \ file_poscar.lattice_vector_1, \ file_poscar.lattice_vector_2, \ file_poscar.lattice_vector_3 ) if d < 1.2: list_H_idx.append(idx) elif atom[file_poscar.ATOMNAME] == "X": d = pbc_distance( [atom_O[file_poscar.X_COORD], atom_O[file_poscar.Y_COORD], atom_O[file_poscar.Z_COORD]], \ [atom[file_poscar.X_COORD], atom[file_poscar.Y_COORD], atom[file_poscar.Z_COORD]], \ file_poscar.lattice_vector_1, \ file_poscar.lattice_vector_2, \ file_poscar.lattice_vector_3 ) if d < 0.5: list_X_idx.append(idx)
def MSD ( self, \ outfilename="", \ skip_frames=50, \ timestep=1.0 ) : """ calculates the MSD here the number of particles has to be constant --> we can only apply this to all water molecules in trajectory and not for example a subset (such as water in first L for more info check: http://utkstair.org/clausius/docs/che548/pdf/selfD.pdf some technical details: Np ... number of particles Nt ... number of time frames No ... = int(Nt/2): number of considererd origins this also specifies for how many future timesteps will be considered this way every delta_t has the same number of datapoints which means that all of them are equally weighted e.g. Nt: 1 2 3 4 5 6 --> Nt/2=3 calculate MSD 1-2, 1-3, 1-4, 2-3, 2-4, 2-5, 3-4, 3-5, 3-6 each delta_t has Nt/2*Np datapoints --> this is generally used to get statistics this analysis needs to be performed on one concenated trajectory it is assumed that the order of atoms does not change from frame to frame to make sure we don't do anything stupid, this is checked on the run skip_frames ... skipping this number of frames each time timestep ... timestep between different frames (in fs) """ self.check_traj() X = 1 Y = 2 Z = 3 INDEX = 4 if outfilename == "": print "[ERROR]. Please specify the output-filename." sys.exit() outfile = open(outfilename, "a") #---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---# # split traj # #---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---# # split trajectory into sub-trajectory containing each skip_frames-th frame traj_split = ff.format_xyz() traj_split.list_atoms = self.traj.list_atoms[::skip_frames] traj_split.n_frames = len(traj_split.list_atoms) print "\t(+) split trajectory from", self.traj.n_frames, "frames to", len( traj_split.list_atoms), "frames" # calculate n_origins n_origins = int(traj_split.n_frames / 2) # initialize list that saves MSD for each delta_t # we will have n_origins datapoints for each origin # the simple idea: # list_msd = [0.0] * n_origins # does not work! # because each element in list changes if you change one (weird?) # workaround: list_msd = [] for i in range(n_origins): list_msd.append([]) #---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---# # loop over all time origins # #---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---# for idx_origin in range(n_origins): # monitor progress if int(n_origins * 0.10) == idx_origin: print " 10 % done" if int(n_origins * 0.20) == idx_origin: print " 20 % done" if int(n_origins * 0.30) == idx_origin: print " 30 % done " if int(n_origins * 0.40) == idx_origin: print " 40 % done" if int(n_origins * 0.50) == idx_origin: print " 50 % done" if int(n_origins * 0.60) == idx_origin: print " 60 % done " if int(n_origins * 0.70) == idx_origin: print " 70 % done " if int(n_origins * 0.80) == idx_origin: print " 80 % done " if int(n_origins * 0.90) == idx_origin: print " 90 % done " list_reference = [] #---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---# # loop over all relevant timeframes # #---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---# for idx_frame, frame in enumerate( traj_split.list_atoms[idx_origin:idx_origin + n_origins]): msd = 0 for idx_atom, atom in enumerate(frame): if len(atom) < 5: print "[ERROR]. In MSD procedure. An atom has not the right format" print " Frame: ", idx_frame, "atom: ", idx_atom print atom sys.exit() # store x0, y0, z0 for first frame if idx_frame == 0: list_reference.append(atom) # calculate MSD for this atom else: # check if trajectory oredered correctly if list_reference[idx_atom][INDEX] != atom[INDEX]: print "[ERROR]. In MSD procedure: the trajectory is ordered incorrectly" sys.exit() p0 = [ list_reference[idx_atom][X], list_reference[idx_atom][Y], list_reference[idx_atom][Z] ] p1 = [atom[X], atom[Y], atom[Z]] d = pbc.pbc_distance( p0, \ p1, \ lattice_v_1 = self.traj.lattice_vector_1 , \ lattice_v_2 = self.traj.lattice_vector_2 , \ lattice_v_3 = self.traj.lattice_vector_3 ) msd += d**2 # normalize MSD to number of atoms if int(self.traj.n_atoms) != len(frame): print "[ERROR]. In MSD procedure: the number of atoms does not make sense" print self.traj.n_atoms, len(frame) sys.exit() msd = msd / int(self.traj.n_atoms) # store MSD for correct delta_t value # --> these values are stored in list_msd list_msd[idx_frame].append(msd) #---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---# # MSD analysis done --> write output # #---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---# # check if list_msd makes sense: if len(list_msd) != n_origins: print "[ERROR]. In MSD procedure: list_msd has wrong length" sys.exit() # write column names outfile.write("time ") for i, datapoint in enumerate(list_msd[0]): outfile.write("obs_" + str(i) + " ") outfile.write("\n") # write data for idx, datapoint in enumerate(list_msd): outfile.write(str(idx * timestep * skip_frames) + " ") for i in datapoint: outfile.write(str(i) + " ") outfile.write("\n") outfile.close()
def mobility( self, \ outfilename="", \ delta_frame=20, \ list_name_select=["O"] ) : """ calculate the mobility of selected particles mobility(t) = \frac{1}{N} \sum_{i=1}^{N} [r_i(t+\Delta t) - r_i(t)]^2 N ... number of particles (can change from frame to frame) delta_frame ... to how far in the future do we compare current position mobility is calculated for each frame if particle with same index does not exist at (t) and (t+\Delta t) --> don't calculate mobility for that frame and atom this analyser requires extra information (index of atom) """ self.check_traj() # makros NAME = 0 X = 1 Y = 2 Z = 3 INDEX = 4 if outfilename == "": print "[ERROR]. No outputfilename specified!" sys.exit() outfile = open(outfilename, "a") for idx_frame, frame in enumerate(self.traj.list_atoms): # monitor progress if int(self.traj.n_frames * 0.25) == idx_frame: print " 25 % done" if int(self.traj.n_frames * 0.50) == idx_frame: print " 50 % done" if int(self.traj.n_frames * 0.75) == idx_frame: print " 75 % done " d_sum_squared = 0 n_particles = 0 for atom in frame: if atom[NAME] in list_name_select: # store coords r_i(t) p1 = [atom[X], atom[Y], atom[Z]] p2 = [] # look for same atom in frame+delta_frame # if end of traj so that idx_frame+delta_frame does not exist --> continue if idx_frame + delta_frame > (self.traj.n_frames - 1): continue for atom_2 in self.traj.list_atoms[idx_frame + delta_frame]: if atom_2[NAME] in list_name_select: if atom_2[INDEX] == atom[INDEX]: p2 = [atom_2[X], atom_2[Y], atom_2[Z]] break # if particle p1 is not there in frame+delta_frame: move on if len(p2) == 0: continue d = pbc.pbc_distance ( p1 = p1, \ p2 = p2, \ lattice_v_1 = self.traj.lattice_vector_1 , \ lattice_v_2 = self.traj.lattice_vector_2 , \ lattice_v_3 = self.traj.lattice_vector_3 ) # collect data d_sum_squared += d**2 n_particles += 1 # --- ATOM LOOP DONE --- if n_particles > 0: outfile.write(str(d_sum_squared / n_particles) + "\n") # --- FRAME LOOP DONE --- outfile.close()
def Hbond_d_alpha(self, outfilename="", list_name_select=["O"]): """ calculate the distribution of bondlenghts and angles of selected hydrogen bonds """ self.check_traj() # makros NAME = 0 X = 1 Y = 2 Z = 3 INDEX = 4 LIST_ACCEPTORS = 5 LIST_DONORS = 6 # list_donors / list_acceptors PARTNER_TYPE = 0 O_PARTNER = 1 H_PARTNER = 2 # check if index-dictionary was created already # if not: create it! try: self.list_dict_idx except: self.create_idx_dict() if outfilename == "": print "[ERROR]. No outputfilename specified!" sys.exit() outfile = open(outfilename, "a") for idx_frame, frame in enumerate(self.traj.list_atoms): # monitor progress if int(self.traj.n_frames * 0.25) == idx_frame: print " 25 % done" if int(self.traj.n_frames * 0.50) == idx_frame: print " 50 % done" if int(self.traj.n_frames * 0.75) == idx_frame: print " 75 % done " for atom in frame: if atom[NAME] in list_name_select: #---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---# # loop over acceptors # #---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---# for partner in atom[LIST_ACCEPTORS]: #=======================================================================================# # bondlength # #=======================================================================================# p1 = [atom[X], atom[Y], atom[Z]] p2 = [ frame[self.list_dict_idx[idx_frame][int(partner[O_PARTNER])]][X] , \ frame[self.list_dict_idx[idx_frame][int(partner[O_PARTNER])]][Y] , \ frame[self.list_dict_idx[idx_frame][int(partner[O_PARTNER])]][Z] ] d = pbc.pbc_distance ( p1 = p1, \ p2 = p2, \ lattice_v_1 = self.traj.lattice_vector_1 , \ lattice_v_2 = self.traj.lattice_vector_2 , \ lattice_v_3 = self.traj.lattice_vector_3 ) #=======================================================================================# # angle # #=======================================================================================# # p0 is the atom in the middle (p0 - p1 - p2) p0 = [ frame[self.list_dict_idx[idx_frame][int(partner[H_PARTNER])]][X] , \ frame[self.list_dict_idx[idx_frame][int(partner[H_PARTNER])]][Y] , \ frame[self.list_dict_idx[idx_frame][int(partner[H_PARTNER])]][Z] ] angle = pbc.pbc_angle ( p0 = p0, \ p1 = p1, \ p2 = p2, \ lattice_v_1 = self.traj.lattice_vector_1 , \ lattice_v_2 = self.traj.lattice_vector_2 , \ lattice_v_3 = self.traj.lattice_vector_3 ) outfile.write(str(d) + " " + str(angle) + "\n") #---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---# # loop over donors # #---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---# for partner in atom[LIST_DONORS]: #=======================================================================================# # bondlength # #=======================================================================================# p1 = [atom[X], atom[Y], atom[Z]] p2 = [ frame[self.list_dict_idx[idx_frame][int(partner[O_PARTNER])]][X] , \ frame[self.list_dict_idx[idx_frame][int(partner[O_PARTNER])]][Y] , \ frame[self.list_dict_idx[idx_frame][int(partner[O_PARTNER])]][Z] ] d = pbc.pbc_distance ( p1 = p1, \ p2 = p2, \ lattice_v_1 = self.traj.lattice_vector_1 , \ lattice_v_2 = self.traj.lattice_vector_2 , \ lattice_v_3 = self.traj.lattice_vector_3 ) #=======================================================================================# # angle # #=======================================================================================# # p0 is the atom in the middle (p0 - p1 - p2) p0 = [ frame[self.list_dict_idx[idx_frame][int(partner[H_PARTNER])]][X] , \ frame[self.list_dict_idx[idx_frame][int(partner[H_PARTNER])]][Y] , \ frame[self.list_dict_idx[idx_frame][int(partner[H_PARTNER])]][Z] ] angle = pbc.pbc_angle ( p0 = p0, \ p1 = p1, \ p2 = p2, \ lattice_v_1 = self.traj.lattice_vector_1 , \ lattice_v_2 = self.traj.lattice_vector_2 , \ lattice_v_3 = self.traj.lattice_vector_3 ) outfile.write(str(d) + " " + str(angle) + "\n") outfile.close()
print " n_O, n_H and n_X do not have the correct ratio (assuming O,H,X order)" print n_O, n_H, n_X sys.exit() # consistency check #2: is idx_O, idx_O+n_O, idx_O+2*n_O and idx_O+3*n_O a watermolecules? # --> we exploit this order down the line, so make sure its OK # what can happen is that PBC separate water molecules, in that case: remove them on the fly for idx, atom in enumerate(file_poscar.list_atoms): if atom[file_poscar.ATOMNAME] == "O": # --- distance to HW1 --- d = pbc_distance( [ atom[file_poscar.X_COORD], \ atom[file_poscar.Y_COORD], \ atom[file_poscar.Z_COORD] ], \ [ file_poscar.list_atoms[idx+n_O][file_poscar.X_COORD], \ file_poscar.list_atoms[idx+n_O][file_poscar.Y_COORD], \ file_poscar.list_atoms[idx+n_O][file_poscar.Z_COORD] ], \ file_poscar.lattice_vector_1, \ file_poscar.lattice_vector_2, file_poscar.lattice_vector_3) if d > 1.5: print "[ERROR]. O ( idx:", idx, ") and H1 ( idx:", n_O + idx, ") are expected to belong to the same water molecule." print " They are too far away from each other however, d=", d sys.exit() # --- distance to HW2 --- d = pbc_distance( [ atom[file_poscar.X_COORD], \ atom[file_poscar.Y_COORD], \ atom[file_poscar.Z_COORD] ], \ [ file_poscar.list_atoms[idx+2*n_O][file_poscar.X_COORD], \