def __init__(self, inputfile, *arg): self.inputfile = inputfile d = readdump(self.inputfile) d.read_onefile() self.ParticleNumber = d.ParticleNumber[0] if d.ParticleNumber[0] != d.ParticleNumber[-1]: raise ValueError('************* Paticle Number Changes **************') self.ParticleType = d.ParticleType self.Positions = d.Positions self.SnapshotNumber = d.SnapshotNumber self.Boxlength = d.Boxlength[0][:2] if not (d.Boxlength[0][:2] == d.Boxlength[-1][:2]).all(): raise ValueError('*********Box Length Changed from Dump***********') self.Boxbounds = d.Boxbounds[0][:2] self.Volume = self.Boxlength[0] * self.Boxlength[1] self.rhototal = self.ParticleNumber / self.Volume #number density of the total system self.typecounts = np.unique(self.ParticleType[0], return_counts = True) self.Type = self.typecounts[0] self.TypeNumber = self.typecounts[1] self.rhotype = self.TypeNumber / self.Volume print ('self.Type:', self.Type) print ('self.TypeNumber:', self.TypeNumber) if np.sum(self.TypeNumber) != self.ParticleNumber: raise ValueError('****** Sum of Indivdual Types is Not the Total Amount*******')
def __init__(self, dumpfile, Neighborfile, *arg): self.dumpfile = dumpfile d = readdump(self.dumpfile, 2) #only at two dimension self.Neighborfile = Neighborfile d.read_onefile() self.TimeStep = d.TimeStep[1] - d.TimeStep[0] if self.TimeStep != d.TimeStep[-1] - d.TimeStep[-2]: raise ValueError( '*********** dump interval changes **************') self.ParticleNumber = d.ParticleNumber[0] if d.ParticleNumber[0] != d.ParticleNumber[-1]: raise ValueError( '************* Paticle Number Changes **************') self.ParticleType = d.ParticleType self.Positions = np.array(d.Positions) self.SnapshotNumber = d.SnapshotNumber self.Boxlength = d.Boxlength[0] if not (d.Boxlength[0] == d.Boxlength[-1]).all(): raise ValueError( '*********Box Length Changed from Dump***********') self.rhototal = self.ParticleNumber / np.prod(self.Boxlength) self.hmatrix = d.hmatrix self.typecounts = np.unique(self.ParticleType[0], return_counts=True) self.Type = self.typecounts[0] self.TypeNumber = self.typecounts[1] print('Particle Type:', self.Type) print('Particle TypeNumber:', self.TypeNumber) if np.sum(self.TypeNumber) != self.ParticleNumber: raise ValueError( '****** Sum of Indivdual Types is Not the Total Amount*******')
def get_input(inputfile, ndim, radii, filetype = 'lammps', moltypes = ''): """ Design input file for Voro++ by considering particle radii radii must be a dict like {1 : 1.28, 2 : 1.60} if you do not want to consider radii, set the radii the same The keyword filetype is used for different MD engines It has four choices: 'lammps' (default) 'lammpscenter' (lammps molecular dump with known atom type of each molecule center) moltypes is a dict mapping center atomic type to molecular type moltypes is also used to select the center atom such as moltypes = {3: 1, 5: 2} 'gsd' (HOOMD-blue standard output for static properties) 'gsd_dcd' (HOOMD-blue outputs for static and dynamic properties) """ d = readdump(inputfile, ndim, filetype, moltypes) d.read_onefile() results = [] for n in range(d.SnapshotNumber): ParticleRadii = np.array(pd.Series(d.ParticleType[n]).map(radii)) PositionRadii = np.column_stack((d.Positions[n], ParticleRadii)) voroinput = np.column_stack((np.arange(d.ParticleNumber[n]) + 1, PositionRadii)) results.append(voroinput) return (results, d.Boxbounds)
def __init__(self, inputfile, ndim, *arg): self.inputfile = inputfile self.ndim = ndim d = readdump(self.inputfile, self.ndim) d.read_onefile() self.TimeStep = d.TimeStep[1] - d.TimeStep[0] if self.TimeStep != d.TimeStep[-1] - d.TimeStep[-2]: raise ValueError( '*********** dump interval changes **************') self.ParticleNumber = d.ParticleNumber[0] if d.ParticleNumber[0] != d.ParticleNumber[-1]: raise ValueError( '************* Paticle Number Changes **************') self.ParticleType = d.ParticleType self.Positions = d.Positions self.SnapshotNumber = d.SnapshotNumber self.Boxlength = d.Boxlength[0] if not (d.Boxlength[0] == d.Boxlength[-1]).all(): raise ValueError( '*********Box Length Changed from Dump***********') self.Boxbounds = d.Boxbounds[0] self.typecounts = np.unique(self.ParticleType[0], return_counts=True) self.Type = self.typecounts[0] self.TypeNumber = self.typecounts[1] print('Particle Type:', self.Type) print('Particle TypeNumber:', self.TypeNumber) if np.sum(self.TypeNumber) != self.ParticleNumber: raise ValueError( '****** Sum of Indivdual Types is Not the Total Amount*******')
def __init__(self, inputfile, *arg): self.inputfile = inputfile d = readdump(self.inputfile) d.read_onefile() self.ParticleNumber = d.ParticleNumber[0] if d.ParticleNumber[0] != d.ParticleNumber[-1]: raise ValueError('************* Paticle Number Changes **************') self.ParticleType = d.ParticleType self.Positions = d.Positions self.SnapshotNumber = d.SnapshotNumber self.Boxlength = d.Boxlength[0][:2] if not (d.Boxlength[0] == d.Boxlength[-1]).all(): raise ValueError('*********Box Length Changed from Dump***********') if len(np.unique(self.Boxlength)) != 1: raise ValueError('*********Box is not Cubic***********************') self.twopidl = 2 * pi / self.Boxlength[0] self.Boxbounds = d.Boxbounds[0][:2] self.typecounts = np.unique(self.ParticleType[0], return_counts = True) self.Type = self.typecounts[0] self.TypeNumber = self.typecounts[1] print ('self.Type:', self.Type) print ('self.TypeNumber:', self.TypeNumber) if np.sum(self.TypeNumber) != self.ParticleNumber: raise ValueError('****** Sum of Indivdual Types is Not the Total Amount*******')
def logOrderlife(dumpfile, orderings, ndim = 3, dt = 0.002, outputfile = ''): """Calculate the time decay of structural ordering with the initial configuration as reference orders is the numpy array of structural ordering its shape is [atom_number, snapshot_number] this is the standard output format of this analysis package: READ: orders = np.loadtxt(orderfile, skiprows = 1)[:, 1:] dumpfile is used to get the time scale """ print ('------------Calculate LOG Order time self-correlation---------') d = readdump(dumpfile, ndim) d.read_onefile() if d.SnapshotNumber != orderings.shape[1]: errorinfo = '***inconsistent number of configurations and orderings***' raise ValueError results = np.zeros((d.SnapshotNumber - 1, 2)) names = 't St' results[:, 0] = (np.array(d.TimeStep[1:]) - np.array(d.TimeStep[0])) * dt CII = orderings[:, 1:] * orderings[:, 0][:, np.newaxis] aveS = np.square(orderings.mean()) aveS2 = np.square(orderings).mean() results[:, 1] = (CII.mean(axis = 0) - aveS) / (aveS2 - aveS) if outputfile: np.savetxt(outputfile, results, fmt = '%.6f', header = names, comments = '') print ('------------Calculate LOG Order time self-correlation Over---------') return results, names
def total_type(inputfile, ndim, filetype='lammps', moltypes='', typeid=1, qmax=0, a=1.0, dt=0.002, ppp=[1, 1, 1], PBC=True, outputfile=''): """ Compute self-intermediate scattering functions ISF, dynamic susceptibility ISFX4 based on ISF Overlap function Qt and its corresponding dynamic susceptibility QtX4 Mean-square displacements msd; non-Gaussion parameter alpha2 qmax is the wavenumber corresponding to the first peak of structure factor a is the cutoff for the overlap function, default is 1.0 (EAM) and 0.3(LJ) (0.3<d>) dt is the timestep of MD simulations ppp is the periodic boundary conditions typeid is the atom type to calculate the properties PBC is to determine whether we need to remove periodic boundary conditions """ print( '-----------------Compute Overall Dynamics of log output--------------------' ) d = readdump(inputfile, ndim, filetype, moltypes) d.read_onefile() conditions = d.ParticleType[0] == typeid results = np.zeros(((d.SnapshotNumber - 1), 5)) names = 't ISF Qt msd alpha2' results[:, 0] = (np.array(d.TimeStep[1:]) - d.TimeStep[0]) * dt RII = [i[conditions] - d.Positions[0][conditions] for i in d.Positions[1:]] RII = np.array(RII) if PBC: #remove PBC hmatrixinv = np.linalg.inv(d.hmatrix[0]) for ii in range(RII.shape[0]): matrixij = np.dot(RII[ii], hmatrixinv) RII[ii] = np.dot(matrixij - np.rint(matrixij) * ppp, d.hmatrix[0]) results[:, 1] = (np.cos(RII * qmax).mean(axis=2)).mean(axis=1) distance = np.square(RII).sum(axis=2) results[:, 2] = (np.sqrt(distance) <= a).sum(axis=1) / conditions.sum() results[:, 3] = distance.mean(axis=1) distance2 = np.square(distance).mean(axis=1) results[:, 4] = alpha2factor(ndim) * distance2 / np.square( results[:, 3]) - 1.0 if outputfile: np.savetxt(outputfile, results, fmt='%.6f', header=names, comments='') print( '-----------------Compute Type Dynamics of log output OVER--------------------' ) return results, names
def __init__(self, inputfile, ndim, filetype='lammps', moltypes='', ppp=[1, 1, 1], PBC=True, *arg): """ The keyword filetype is used for different MD engines It has four choices: 'lammps' (default) 'lammpscenter' (lammps molecular dump with known atom type of each molecule center) moltypes is a dict mapping center atomic type to molecular type moltypes is also used to select the center atom such as moltypes = {3: 1, 5: 2} 'gsd' (HOOMD-blue standard output for static properties) 'gsd_dcd' (HOOMD-blue outputs for static and dynamic properties) """ self.inputfile = inputfile self.ndim = ndim self.filetype = filetype self.moltypes = moltypes self.ppp = ppp self.PBC = PBC d = readdump(self.inputfile, self.ndim, self.filetype, self.moltypes) d.read_onefile() if len(d.TimeStep) > 1: self.TimeStep = d.TimeStep[1] - d.TimeStep[0] if self.TimeStep != d.TimeStep[-1] - d.TimeStep[-2]: print( 'Warning: *********** dump interval changes **************' ) self.ParticleNumber = d.ParticleNumber[0] if d.ParticleNumber[0] != d.ParticleNumber[-1]: print( 'Warning: ************* Paticle Number Changes **************') self.ParticleType = d.ParticleType self.Positions = d.Positions self.SnapshotNumber = d.SnapshotNumber self.Boxlength = d.Boxlength[0] if not (d.Boxlength[0] == d.Boxlength[-1]).all(): print('Warning: *********Box Length Changed from Dump***********') self.Boxbounds = d.Boxbounds[0] self.typecounts = np.unique(self.ParticleType[0], return_counts=True) self.Type = self.typecounts[0].astype(np.int32) self.TypeNumber = self.typecounts[1] print('Particle Type:', self.Type) print('Particle TypeNumber:', self.TypeNumber) if np.sum(self.TypeNumber) != self.ParticleNumber: print( 'Warning: ****** Sum of Indivdual Types is Not the Total Amount*******' ) self.hmatrix = d.hmatrix
def local_tetrahedral_order(dumpfile, filetype='lammps', moltypes='', ppp=[1, 1, 1], outputfile=''): """Calcultae local tetrahedral order parameter The keyword filetype is used for different MD engines It has four choices: 'lammps' (default) 'lammpscenter' (lammps molecular dump with known atom type of each molecule center) moltypes is a dict mapping center atomic type to molecular type moltypes is also used to select the center atom such as moltypes = {3: 1, 5: 2} 'gsd' (HOOMD-blue standard output for static properties) 'gsd_dcd' (HOOMD-blue outputs for static and dynamic properties) """ d = readdump(dumpfile, 3, filetype, moltypes) d.read_onefile() num_nearest = 4 #number of selected nearest neighbors results = np.zeros((max(d.ParticleNumber), d.SnapshotNumber)) for n in range(d.SnapshotNumber): hmatrixinv = np.linalg.inv(d.hmatrix[n]) Positions = d.Positions[n] for i in range(d.ParticleNumber[n]): RIJ = Positions - Positions[i] matrixij = np.dot(RIJ, hmatrixinv) RIJ = np.dot(matrixij - np.rint(matrixij) * ppp, d.hmatrix[n]) #remove PBC RIJ_norm = np.linalg.norm(RIJ, axis=1) nearests = np.argpartition(RIJ_norm, num_nearest + 1)[:num_nearest + 1] nearests = [j for j in nearests if j != i] for j in range(num_nearest - 1): for k in range(j + 1, num_nearest): medium1 = np.dot(RIJ[nearests[j]], RIJ[nearests[k]]) medium2 = RIJ_norm[nearests[j]] * RIJ_norm[nearests[k]] results[i, n] += (medium1 / medium2 + 1.0 / 3)**2 results = np.column_stack( (np.arange(d.ParticleNumber[0]) + 1, 1.0 - 3.0 / 8 * results)) names = 'id q_tetra' if outputfile: numformat = '%d ' + '%.6f ' * (results.shape[1] - 1) np.savetxt(outputfile, results, fmt=numformat, header=names, comments='') print('---------calculate local tetrahedral order over---------') return results, names
def partialSq(inputfile, selection, ndim=3, filetype='lammps', moltypes='', ppp=[1, 1, 1], qrange=10, outputfile=''): """ calculate the structure factor of a specific group of atoms identified by 'selection' of bool type The shape of selection must be [num_of_atom, num_of_snapshot] """ print('--------Calculate Conditional S(q)-----------') from WaveVector import choosewavevector #(Numofq, ndim) d = readdump(inputfile, ndim, filetype, moltypes) d.read_onefile() if d.SnapshotNumber != selection.shape[1]: errorinfo = '***inconsistent number of configurations and atom selection***' raise ValueError(errorinfo) twopidl = 2 * np.pi / d.Boxlength[0] Numofq = int(qrange / twopidl.max()) wavevector = choosewavevector(Numofq, ndim)[:, 1:] wavevector = wavevector.astype(np.float64) * twopidl[np.newaxis, :] wavenumber = np.linalg.norm(wavevector, axis=1) sqresults = np.zeros((wavevector.shape[0], 2)) sqresults[:, 0] = wavenumber for n in range(d.SnapshotNumber): sqtotal = np.zeros_like(sqresults) condition = selection[:, n] for i in range(d.ParticleNumber[n]): if condition[i]: thetas = (d.Positions[n][i][np.newaxis, :] * wavevector).sum(axis=1) sqtotal += np.column_stack((np.sin(thetas), np.cos(thetas))) sqresults[:, 1] += np.square(sqtotal).sum( axis=1) / d.ParticleNumber[n] #condition.sum() # sqresults[:, 1] /= d.SnapshotNumber sqresults = pd.DataFrame(sqresults).round(6) results = sqresults.groupby(sqresults[0]).mean().reset_index().values names = 'q S(q)' if outputfile: np.savetxt(outputfile, results, fmt='%.6f', header=names, comments='') print('--------Calculate Conditional S(q) OVER-----------') return results, names
def __init__(self, dumpfile, Neighborfile, edgelengthfile='', filetype = 'lammps', moltypes = '', *arg): """ The keyword filetype is used for different MD engines It has four choices: 'lammps' (default) 'lammpscenter' (lammps molecular dump with known atom type of each molecule center) moltypes is a dict mapping center atomic type to molecular type moltypes is also used to select the center atom such as moltypes = {3: 1, 5: 2} 'gsd' (HOOMD-blue standard output for static properties) 'gsd_dcd' (HOOMD-blue outputs for static and dynamic properties) edgelengthfile: filename of voronoi cell bonds lengths for each particle w.r.t its neighbors, like the face area in 3D. This can be used to weight the order parameter; provide False if do not want to weight. (default) """ self.dumpfile = dumpfile self.filetype = filetype self.moltypes = moltypes d = readdump(self.dumpfile, 2, self.filetype, self.moltypes) #only at two dimension d.read_onefile() self.Neighborfile = Neighborfile self.edgelengthfile = edgelengthfile if len(d.TimeStep) > 1: self.TimeStep = d.TimeStep[1] - d.TimeStep[0] if self.TimeStep != d.TimeStep[-1] - d.TimeStep[-2]: print ('Warning: *********** dump interval changes **************') self.ParticleNumber = d.ParticleNumber[0] if d.ParticleNumber[0] != d.ParticleNumber[-1]: print ('Warning: ************* Paticle Number Changes **************') self.ParticleType = d.ParticleType self.Positions = np.array(d.Positions) self.SnapshotNumber = d.SnapshotNumber self.Boxlength = d.Boxlength[0] if not (d.Boxlength[0] == d.Boxlength[-1]).all(): print ('Warning: *********Box Length Changed from Dump***********') self.rhototal = self.ParticleNumber / np.prod(self.Boxlength) self.hmatrix = d.hmatrix self.typecounts = np.unique(self.ParticleType[0], return_counts = True) self.Type = self.typecounts[0] self.TypeNumber = self.typecounts[1] print ('Particle Type:', self.Type) print ('Particle TypeNumber:', self.TypeNumber) if np.sum(self.TypeNumber) != self.ParticleNumber: print ('Warning: ****** Sum of Indivdual Types is Not the Total Amount*******') if self.edgelengthfile: print ('----Bond orientational order will be weighted by Voronoi bond lengths----') else: print ('----Non-weighting is using----')
def partialgr(inputfile, selection, ndim=3, filetype='lammps', moltypes='', rdelta=0.01, ppp=[1, 1, 1], outputfile=''): """ calculate the pair correlation function of a specific group of atoms identified by 'selection' of bool type The shape of selection must be [num_of_atom, num_of_snapshot] """ print('--------Calculate Conditional g(r)-----------') d = readdump(inputfile, ndim, filetype, moltypes) d.read_onefile() if d.SnapshotNumber != selection.shape[1]: errorinfo = '***inconsistent number of configurations and atom selection***' raise ValueError(errorinfo) MAXBIN = int(d.Boxlength[0].min() / 2.0 / rdelta) grresults = np.zeros(MAXBIN) for n in range(d.SnapshotNumber): hmatrixinv = np.linalg.inv(d.hmatrix[n]) condition = selection[:, n] for i in range(d.ParticleNumber[n] - 1): RIJ = d.Positions[n][i + 1:] - d.Positions[n][i] matrixij = np.dot(RIJ, hmatrixinv) RIJ = np.dot(matrixij - np.rint(matrixij) * ppp, d.hmatrix[n]) #remove PBC distance = np.linalg.norm(RIJ, axis=1) SIJ = condition[i + 1:] * condition[i] Countvalue, BinEdge = np.histogram(distance, bins=MAXBIN, range=(0, MAXBIN * rdelta), weights=SIJ) grresults += Countvalue binleft = BinEdge[:-1] #real value of each bin edge, not index binright = BinEdge[1:] #len(Countvalue) = len(BinEdge) - 1 Nideal = Nidealfac(ndim) * np.pi * (binright**ndim - binleft**ndim) rhototal = d.ParticleNumber[0] / np.prod(d.Boxlength[0]) grresults = grresults * 2 / selection.sum() / (Nideal * rhototal) binright = binright - 0.5 * rdelta #middle of each bin results = np.column_stack((binright, grresults)) names = 'r g_c(r)' if outputfile: np.savetxt(outputfile, results, fmt='%.6f', header=names, comments='') print('--------Calculate Conditional g(r) OVER-----------') return results, names
def logdynamics(inputfile, selection, ndim=3, filetype='lammps', moltypes='', qmax=0, a=0.3, dt=0.002, ppp=[1, 1, 1], PBC=True, outputfile=''): """ calculate the dynamical properties of a specific group of atoms identified by 'selection' of bool type The shape of selection must be [num_of_atom, num_of_snapshot] """ print('--------Calculate LOG Conditional Dynamics-----------') d = readdump(inputfile, ndim, filetype, moltypes) d.read_onefile() if d.SnapshotNumber != selection.shape[1]: print(selection.shape) print( '--------Warning: selection must be from the initial state---------' ) results = np.zeros(((d.SnapshotNumber - 1), 5)) names = 't ISF Qt msd alpha2' results[:, 0] = (np.array(d.TimeStep[1:]) - d.TimeStep[0]) * dt condition = selection[:, 0] RII = [i[condition] - d.Positions[0][condition] for i in d.Positions[1:]] if PBC: #remove PBC hmatrixinv = np.linalg.inv(d.hmatrix[0]) for ii in range(len(RII)): matrixij = np.dot(RII[ii], hmatrixinv) RII[ii] = np.dot(matrixij - np.rint(matrixij) * ppp, d.hmatrix[0]) RII = np.array(RII) results[:, 1] = (np.cos(RII * qmax).mean(axis=2)).mean(axis=1) distance = np.square(RII).sum(axis=2) results[:, 2] = (np.sqrt(distance) <= a).mean(axis=1) results[:, 3] = distance.mean(axis=1) distance2 = np.square(distance).mean(axis=1) results[:, 4] = alpha2factor(ndim) * distance2 / np.square( results[:, 3]) - 1.0 if outputfile: np.savetxt(outputfile, results, fmt='%.6f', header=names, comments='') print('--------Calculate LOG Conditional Dynamics-----------') return results, names
def Nnearests(dumpfile, ndim=3, filetype='lammps', moltypes='', N=12, ppp=[1, 1, 1], fnfile='neighborlist.dat'): """Get the N nearest neighbors around a particle The keyword filetype is used for different MD engines It has four choices: 'lammps' (default) 'lammpscenter' (lammps molecular dump with known atom type of each molecule center) moltypes is a dict mapping center atomic type to molecular type moltypes is also used to select the center atom such as moltypes = {3: 1, 5: 2} 'gsd' (HOOMD-blue standard output for static properties) 'gsd_dcd' (HOOMD-blue outputs for static and dynamic properties) """ from dump import readdump import re d = readdump(dumpfile, ndim, filetype, moltypes) d.read_onefile() fneighbor = open(fnfile, 'w') for n in range(d.SnapshotNumber): hmatrixinv = np.linalg.inv(d.hmatrix[n]) Positions = d.Positions[n] neighbor = np.zeros((d.ParticleNumber[n], 2 + N), dtype=np.int) neighbor[:, 0] = np.arange(d.ParticleNumber[n]) + 1 neighbor[:, 1] = N for i in range(d.ParticleNumber[n]): RIJ = Positions - Positions[i] matrixij = np.dot(RIJ, hmatrixinv) RIJ = np.dot(matrixij - np.rint(matrixij) * ppp, d.hmatrix[n]) #remove PBC RIJ_norm = np.linalg.norm(RIJ, axis=1) nearests = np.argpartition(RIJ_norm, N + 1)[:N + 1] nearests = nearests[RIJ_norm[nearests].argsort()] neighbor[i, 2:] = nearests[1:] + 1 np.set_printoptions(threshold=np.inf, linewidth=np.inf) fneighbor.write('id cn neighborlist\n') fneighbor.write(re.sub('[\[\]]', ' ', np.array2string(neighbor) + '\n')) fneighbor.close() print('---Calculate %d nearest neighbors done---' % N)
def cutoffneighbors(dumpfile, r_cut, ndim=3, filetype='lammps', moltypes='', ppp=[1, 1, 1], fnfile='neighborlist.dat'): """Get the nearest neighbors around a particle by setting a cutoff distance r_cut The keyword filetype is used for different MD engines It has four choices: 'lammps' (default) 'lammpscenter' (lammps molecular dump with known atom type of each molecule center) moltypes is a dict mapping center atomic type to molecular type moltypes is also used to select the center atom such as moltypes = {3: 1, 5: 2} 'gsd' (HOOMD-blue standard output for static properties) 'gsd_dcd' (HOOMD-blue outputs for static and dynamic properties) """ from dump import readdump import re d = readdump(dumpfile, ndim, filetype, moltypes) d.read_onefile() fneighbor = open(fnfile, 'w') for n in range(d.SnapshotNumber): hmatrixinv = np.linalg.inv(d.hmatrix[n]) Positions = d.Positions[n] neighbor = np.arange(d.ParticleNumber[n]).astype(np.int32) fneighbor.write('id cn neighborlist\n') for i in range(d.ParticleNumber[n]): RIJ = Positions - Positions[i] matrixij = np.dot(RIJ, hmatrixinv) RIJ = np.dot(matrixij - np.rint(matrixij) * ppp, d.hmatrix[n]) #remove PBC RIJ_norm = np.linalg.norm(RIJ, axis=1) nearests = neighbor[RIJ_norm <= r_cut] nearests = nearests[RIJ_norm[nearests].argsort()] CN = nearests.shape[0] - 1 fneighbor.write('%d %d ' % (i + 1, CN)) fneighbor.write(' '.join(map(str, nearests[1:] + 1))) fneighbor.write('\n') # np.set_printoptions(threshold = np.inf, linewidth = np.inf) # fneighbor.write(re.sub('[\[\]]', ' ', np.array2string(neighbor) + '\n')) fneighbor.close() print('---Calculate nearest neighbors with r_cut = %.6f done---' % r_cut)
def partialSq(inputfile, selection, ndim = 3, filetype = 'lammps', moltypes = '', ppp = [1,1,1], qrange = 10, outputfile = ''): """ calculate the structure factor of a specific group of atoms identified by 'selection' of bool type The shape of selection must be [num_of_atom, num_of_snapshot] """ print ('--------Calculate Conditional S(q)-----------') from structurefactors import choosewavevector #(Numofq, ndim) d = readdump(inputfile, ndim, filetype, moltypes) d.read_onefile() if d.SnapshotNumber != selection.shape[1]: errorinfo = '***inconsistent number of configurations and atom selection***' raise ValueError(errorinfo) twopidl = 2 * np.pi / d.Boxlength[0][0] Numofq = int(qrange / twopidl) wavevector = choosewavevector(Numofq, ndim) qvalue, qcount = np.unique(wavevector[:, 0], return_counts = True) sqresults = np.zeros((len(wavevector[:, 0]), 2)) #the first row accouants for wavenumber for n in range(d.SnapshotNumber): sqtotal = np.zeros((len(wavevector[:, 0]), 2)) condition = selection[:, n] for i in range(d.ParticleNumber[n]): #medium = twopidl * (d.Positions[n][i] * wavevector[:, 1:]).sum(axis = 1) #sqtotal += np.column_stack((np.sin(medium)*condition[i], np.cos(medium)*condition[i])) if condition[i]: medium = twopidl * (d.Positions[n][i] * wavevector[:, 1:]).sum(axis = 1) sqtotal += np.column_stack((np.sin(medium), np.cos(medium))) sqresults[:, 1] += np.square(sqtotal).sum(axis = 1) / condition.sum() #d.ParticleNumber[n] sqresults[:, 0] = wavevector[:, 0] sqresults[:, 1] = sqresults[:, 1] / d.SnapshotNumber sqresults = pd.DataFrame(sqresults) results = np.array(sqresults.groupby(sqresults[0]).mean()) qvalue = twopidl * np.sqrt(qvalue) results = np.column_stack((qvalue, results)) names = 'q S(q)' if outputfile: np.savetxt(outputfile, results, fmt='%.6f', header = names, comments = '') print ('--------Calculate Conditional S(q) OVER-----------') return results, names
def get_input(inputfile, ndim, radii): """ Design input file for Voro++ by considering particle radii radii must be a dict like {1 : 1.28, 2 : 1.60} if you do not want to consider radii, set the radii the same """ d = readdump(inputfile, ndim) d.read_onefile() results = [] for n in range(d.SnapshotNumber): ParticleRadii = np.array(pd.Series(d.ParticleType[n]).map(radii)) PositionRadii = np.column_stack((d.Positions[n], ParticleRadii)) voroinput = np.column_stack( (np.arange(d.ParticleNumber[n]) + 1, PositionRadii)) results.append(voroinput) return (results, d.Boxbounds)
def total(inputfile, ndim, filetype='lammps', moltypes='', qmax=0, a=1.0, dt=0.002, outputfile=''): """ Compute self-intermediate scattering functions ISF, dynamic susceptibility ISFX4 based on ISF Overlap function Qt and its corresponding dynamic susceptibility QtX4 Mean-square displacements msd; non-Gaussion parameter alpha2 qmax is the wavenumber corresponding to the first peak of structure factor a is the cutoff for the overlap function, default is 1.0 (EAM) and 0.3(LJ) (0.3<d>) dt is the timestep of MD simulations """ print( '-----------------Compute Overall Dynamics of log output--------------------' ) d = readdump(inputfile, ndim, filetype, moltypes) d.read_onefile() results = np.zeros(((d.SnapshotNumber - 1), 5)) names = 't ISF Qt msd alpha2' results[:, 0] = (np.array(d.TimeStep[1:]) - d.TimeStep[0]) * dt RII = d.Positions[1:] - d.Positions[ 0] #only use the first configuration as reference results[:, 1] = (np.cos(RII * qmax).mean(axis=2)).mean(axis=1) distance = np.square(RII).sum(axis=2) results[:, 2] = (np.sqrt(distance) <= a).sum(axis=1) / d.ParticleNumber[0] results[:, 3] = distance.mean(axis=1) distance2 = np.square(distance).mean(axis=1) results[:, 4] = alpha2factor(ndim) * distance2 / np.square( results[:, 3]) - 1.0 if outputfile: np.savetxt(outputfile, results, fmt='%.6f', header=names, comments='') print( '-----------------Compute Overall Dynamics of log output OVER--------------------' ) return results, names
def ConfigOverlap_Single(inputfile, ndim, binsize, outputfile, filetype = 'lammps', moltypes = ''): """ Calculate the overlap of configurations to get the similarity of configurations The keyword filetype is used for different MD engines It has four choices: 'lammps' (default) 'lammpscenter' (lammps molecular dump with known atom type of each molecule center) moltypes is a dict mapping center atomic type to molecular type moltypes is also used to select the center atom such as moltypes = {3: 1, 5: 2} 'gsd' (HOOMD-blue standard output for static properties) 'gsd_dcd' (HOOMD-blue outputs for static and dynamic properties) """ d = readdump(inputfile, ndim, filetype, moltypes) d.read_onefile() positions = d.Positions particlenumber = d.ParticleNumber[0] snapshotnumber = d.SnapshotNumber boxlength = d.Boxlength[0] #configurations box should be the same to be compared CellNumber = int( 1.25 * boxlength.max() / binsize) #to embrace the whole box Cell = np.zeros((CellNumber, CellNumber, CellNumber, snapshotnumber)) iatom = np.zeros((particlenumber, ndim, snapshotnumber), dtype = np.int) f = open(outputfile, 'w') f.write('overall selfpart \n') for i in range(snapshotnumber): iatom[:, :, i] = np.rint(positions[i] / binsize) for j in range(particlenumber): Cell[iatom[j, 0, i], iatom[j, 1, i], iatom[j, 2, i], i] += 1 if (Cell > 1).any(): print ('Warning: More than one atom in a cell, please reduce binsize') for i in range(snapshotnumber - 1): for j in range(1, snapshotnumber - i): overall = (Cell[iatom[:, 0, i], iatom[:, 1, i], iatom[:, 2, i], i] * Cell[iatom[:, 0, i], iatom[:, 1, i], iatom[:, 2, i], i + j]).sum() #/ particlenumber selfpart = (Cell[iatom[:, 0, i], iatom[:, 1, i], iatom[:, 2, i], i] == Cell[iatom[:, 0, i], iatom[:, 1, i], iatom[:, 2, i], i + j]).sum() #/ particlenumber f.write('{:.6f} {:.6f} \n'.format(overall, selfpart)) f.close()
def MsdInstant(inputfile, ndim, filetype='lammps', moltypes='', everyn=1, ppp=[1, 1, 1], PBC=True, outputfile=''): """ calculate the instantaneous mean squared displacement by referencing to the previous 'everyn' configuration """ print('------Compute Instant MSD------') d = readdump(inputfile, ndim, filetype, moltypes) d.read_onefile() results = np.zeros((d.SnapshotNumber - everyn, 2)) names = 'n imsd' results[:, 0] = np.arange(results.shape[0]) + 1 for n in range(d.SnapshotNumber - everyn): RII = d.Positions[n + everyn] - d.Positions[n] if PBC: hmatrixinv = np.linalg.inv(d.hmatrix[n]) matrixij = np.dot(RII, hmatrixinv) RII = np.dot(matrixij - np.rint(matrixij) * ppp, d.hmatrix[n]) #remove PBC results[n, 1] = np.square(RII).sum(axis=1).mean() if outputfile: unitstep = d.TimeStep[everyn] - d.TimeStep[0] np.savetxt(outputfile, results, fmt='%d %.6f', header=names, comments='TimeStep interval:%d\n' % unitstep) print('------Compute Instant MSD Over------') return results, names
def Orderlife(dumpfile, orderings, ndim = 3, dt = 0.002, outputfile = ''): """Calculate the time decay of structural ordering orders is the numpy array of structural ordering its shape is [atom_number, snapshot_number] this is the standard output format of this analysis package: READ: orders = np.loadtxt(orderfile, skiprows = 1)[:, 1:] dumpfile is used to get the time scale """ print ('------------Calculate Order time self-correlation---------') d = readdump(dumpfile, ndim) d.read_onefile() TimeStep = d.TimeStep[1] - d.TimeStep[0] if TimeStep != d.TimeStep[-1] - d.TimeStep[-2]: print ('Warning: ********time interval changes************') num_config = orderings.shape[1] results = np.zeros((num_config - 1, 2)) names = 't St' cal_SIt = pd.DataFrame(np.zeros(num_config - 1)[np.newaxis, :]) #time correlation deltat = np.zeros((num_config - 1, 2), dtype = np.int) for n in range(num_config - 1):#time interval CII = orderings[:, n+1:] * orderings[:, n][:, np.newaxis] CII_SIt = CII.mean(axis = 0) cal_SIt = pd.concat([cal_SIt, pd.DataFrame(CII_SIt[np.newaxis, :])]) cal_SIt = cal_SIt.iloc[1:] deltat[:, 0] = np.array(cal_SIt.columns) + 1 #time interval deltat[:, 1] = np.array(cal_SIt.count()) #time interval frequency aveS = np.square(orderings.mean()) aveS2 = np.square(orderings).mean() results[:, 0] = deltat[:, 0] * TimeStep * dt results[:, 1] = (cal_SIt.mean() - aveS) / (aveS2 - aveS) if outputfile: np.savetxt(outputfile, results, fmt = '%.6f', header = names, comments = '') print ('------------Calculate Order time self-correlation Over---------') return results, names
def cavitation(inputfile, ndim=3, nbin=5, filetype='lammps', moltypes=''): """Count atom number in a small bin to see whether cavitation occurs The keyword filetype is used for different MD engines It has four choices: 'lammps' (default) 'lammpscenter' (lammps molecular dump with known atom type of each molecule center) moltypes is a dict mapping center atomic type to molecular type moltypes is also used to select the center atom such as moltypes = {3: 1, 5: 2} 'gsd' (HOOMD-blue standard output for static properties) 'gsd_dcd' (HOOMD-blue outputs for static and dynamic properties) """ #get the coordinate information d = readdump(inputfile, ndim, filetype, moltypes) d.read_onefile() if ndim == 3: results = np.zeros((nbin + 1, nbin + 1, nbin + 1)) for i in range(d.SnapshotNumber): #remove the original point to be (0, 0, 0) newpositions = d.Positions[i] - d.Boxbounds[i][:, 0] binsize = (d.Boxlength[i] / nbin)[np.newaxis, :] if i == 0: binvolume = np.prod(binsize) indices, counts = np.unique(np.rint(newpositions / binsize), axis=0, return_counts=True) indices = indices.astype(np.int) for j in range(indices.shape[0]): results[indices[j][0], indices[j][1], indices[j][2]] += counts[j] results = results / d.SnapshotNumber #-----return empty bin number------------------ return results.size - np.count_nonzero(results)
def cal_vector(filename, num_patch=12, ndim=3, ppp=[1, 1, 1]): """ calculate the rotational vectors of each particle considering each patch """ #-----get configuration information----- d = readdump(filename, ndim) d.read_onefile() num_atom = [int(i / (num_patch + 1)) for i in d.ParticleNumber] #-----get vector information------- fdump = open(filename, 'r') pos_all = [] #list of center and patch positions for n in range(d.SnapshotNumber): medium = np.zeros((num_atom[n], int(1 + num_patch), ndim)) #three dimensional array for both center and patch positions #the first is the center for i in range(9): fdump.readline() for i in range(num_atom[n]): #also the number of blocks for j in range(1 + num_patch): medium[i, j] = [ float(ii) for ii in fdump.readline().split()[2:2 + ndim] ] #-----remove PBC------- halfbox = (d.Boxlength[n] / 2.0)[np.newaxis, :] RIJ = medium[i, 0][np.newaxis, :] - medium[i, 1:] periodic = np.where(np.abs(RIJ) > halfbox, np.sign(RIJ), 0).astype(np.int) medium[i, 1:] += periodic * d.Boxlength[n][np.newaxis, :] pos_all.append(medium) print('--------prepare positions done-------') return pos_all, d.SnapshotNumber, num_atom, d.hmatrix
def PatchVector(filename, num_patch=12, ndim=3, filetype='lammps', outputvec='', outputdump=''): """get the vector of each particle based on its patches Two files will be generated: 1) patch-particle vector for each particle 2) coordinates of each particle and its first patch as LAMMPS dump format """ #-----get the first patch of each particle----- f = open(filename, 'r') for i in range(3): f.readline() num_total = int(f.readline()) for i in range(5): f.readline() num_atom = int(num_total / (num_patch + 1)) print('Atom Number: %d' % num_atom) hosts = [] firstpatch = [] for i in range(num_atom): hosts.append(int(f.readline().split()[0])) firstpatch.append(int(f.readline().split()[0])) for j in range(num_patch - 1): f.readline() f.close() if hosts[-1] + 1 != firstpatch[0]: print('Warning: possible ERROR about particle-patch relation') #-----get positions------ hosts = np.array(hosts) - 1 firstpatch = np.array(firstpatch) - 1 d = readdump(filename, ndim, filetype) d.read_onefile() pos_centers = [i[hosts] for i in d.Positions] pos_patches = [i[firstpatch] for i in d.Positions] #------move particles inside of box (PBC)-------- for n in range(d.SnapshotNumber): halfbox = d.Boxlength[n].min() / 2.0 for i in range(num_atom): RIJ = pos_centers[n][i] - pos_patches[n][i] periodic = np.where(np.abs(RIJ) > halfbox, np.sign(RIJ), 0).astype(np.int) pos_patches[n][i] += periodic * d.Boxlength[n] #-----calculate vectors------------------ vectors = [] for n in range(d.SnapshotNumber): vectors.append(pos_patches[n] - pos_centers[n]) print('-------Particle-Patch vector done------') #-------output information on vectors-------------- fvec = open(outputvec, 'w') for n in range(d.SnapshotNumber): fvec.write('Step %d\n' % d.TimeStep[n]) fvec.write('Natom %d\n' % num_atom) fvec.write('id type vx vy vz\n') for j, i in enumerate(hosts): fvec.write('%d %d ' % (j + 1, d.ParticleType[n][i])) fvec.write('%.6f %.6f %.6f\n' % tuple(vectors[n][j])) fvec.close() #-------output information on paritcle-patch------- if outputdump: fdump = open(outputdump, 'w') totalnum = num_atom + len(firstpatch) for n in range(d.SnapshotNumber): header = lammps(d.TimeStep[n], totalnum, d.Boxbounds[n]) fdump.write(header) for j, i in enumerate(hosts): fdump.write('%d %d ' % (j + 1, d.ParticleType[n][i])) fdump.write('%.6f %.6f %.6f\n' % tuple(d.Positions[n][i])) for j, i in enumerate(firstpatch): fdump.write('%d %d ' % (num_atom + j + 1, d.ParticleType[n][i])) fdump.write('%.6f %.6f %.6f\n' % tuple(d.Positions[n][i])) fdump.close() print('--------wrting configuration information over--------') return None
def dynamics(inputfile, selection, ndim = 3, filetype = 'lammps', moltypes = '', qmax = 0, a = 0.3, dt = 0.002, ppp = [1,1,1], PBC = True, outputfile = ''): """ calculate the dynamical properties of a specific group of atoms identified by 'selection' of bool type The shape of selection must be [num_of_atom, num_of_snapshot] """ print ('--------Calculate Conditional Dynamics-----------') d = readdump(inputfile, ndim, filetype, moltypes) d.read_onefile() if d.SnapshotNumber != selection.shape[1]: errorinfo = '***inconsistent number of configurations and atom selection***' raise ValueError(errorinfo) TimeStep = d.TimeStep[1] - d.TimeStep[0] if TimeStep != (d.TimeStep[-1] - d.TimeStep[-2]): print ('-------Warning: dump interval changes-------') results = np.zeros(((d.SnapshotNumber - 1), 5)) names = 't ISF Qt msd alpha2' cal_isf = pd.DataFrame(np.zeros((d.SnapshotNumber-1))[np.newaxis, :]) cal_Qt = pd.DataFrame(np.zeros((d.SnapshotNumber-1))[np.newaxis, :]) cal_msd = pd.DataFrame(np.zeros((d.SnapshotNumber-1))[np.newaxis, :]) cal_alp = pd.DataFrame(np.zeros((d.SnapshotNumber-1))[np.newaxis, :]) deltat = np.zeros(((d.SnapshotNumber - 1), 2), dtype = np.int) #deltat, deltatcounts for n in range(d.SnapshotNumber - 1): #time interval condition = selection[:, n] RII = [i[condition] - d.Positions[n][condition] for i in d.Positions[n+1:]] if PBC:#remove PBC hmatrixinv = np.linalg.inv(d.hmatrix[n]) for ii in range(len(RII)): matrixij = np.dot(RII[ii], hmatrixinv) RII[ii] = np.dot(matrixij - np.rint(matrixij) * ppp, d.hmatrix[n]) RII = np.array(RII) #shape [deltat, Natom_selected, ndim] RII_isf = (np.cos(RII * qmax).mean(axis = 2)).mean(axis = 1) #index is timeinterval -1 cal_isf = pd.concat([cal_isf, pd.DataFrame(RII_isf[np.newaxis, :])]) distance = np.square(RII).sum(axis = 2) RII_Qt = (np.sqrt(distance) <= a).mean(axis = 1) cal_Qt = pd.concat([cal_Qt, pd.DataFrame(RII_Qt[np.newaxis, :])]) cal_msd = pd.concat([cal_msd, pd.DataFrame(distance.mean(axis = 1)[np.newaxis, :])]) distance2 = np.square(distance).mean(axis = 1) cal_alp = pd.concat([cal_alp, pd.DataFrame(distance2[np.newaxis, :])]) cal_isf = cal_isf.iloc[1:] cal_Qt = cal_Qt.iloc[1:] cal_msd = cal_msd.iloc[1:] cal_alp = cal_alp.iloc[1:] deltat[:, 0] = np.array(cal_isf.columns) + 1 #Timeinterval deltat[:, 1] = np.array(cal_isf.count()) #Timeinterval frequency results[:, 0] = deltat[:, 0] * TimeStep * dt results[:, 1] = cal_isf.mean() results[:, 2] = cal_Qt.mean() results[:, 3] = cal_msd.mean() results[:, 4] = cal_alp.mean() results[:, 5] = alpha2factor(ndim) * results[:, 4] / np.square(results[:, 3]) - 1.0 if outputfile: np.savetxt(outputfile, results, fmt='%.6f', header = names, comments = '') print ('--------Calculate Conditional Dynamics OVER-----------') return results, names
def S4(file_positions, file_orientations, ndim, X4time, filetype='lammps', moltypes='', dt=0.002, phi=0.2, qrange=10, outputfile=''): """ Compute four-point dynamic structure factor at peak timescale of dynamic susceptibility Based on dynamics overlap function CRtotal and its corresponding dynamic susceptibility X4 file_positions: atomic positions file_orientations: atomic orientations phi is the cutoff for the dynamics overlap function X4time is the peaktime scale of X4 dt is the timestep in MD simulations Dynamics should be calculated before computing S4 Only considered the particles which are rotationally slow """ print('-----Compute dynamic S4(q) of rotational slow particles-----') from WaveVector import choosewavevector from dump import readdump from math import pi #read positions d1 = readdump(file_positions, ndim, filetype, moltypes) d1.read_onefile() #read orientations d2 = readangular(file_orientations, ndim) d2.read_onefile() #get unit vector velocity = [ u / np.linalg.norm(u, axis=1)[:, np.newaxis] for u in d2.velocity ] #check files if d1.SnapshotNumber != d2.SnapshotNumber: print('warning: ******check configurations*****') if d1.ParticleNumber[0] != d2.ParticleNumber[0]: print('warning: ******check configurations*****') if d1.TimeStep[0] != d2.TimeStep[0]: print('warning: ******check configurations*****') TimeStep = d1.TimeStep[1] - d1.TimeStep[0] if TimeStep != d1.TimeStep[-1] - d1.TimeStep[-2]: print('Warning: *****time interval changes*****') ParticleNumber = d1.ParticleNumber[0] if ParticleNumber != d1.ParticleNumber[-1]: print('Warning: *****particle number changes*****') #calculate dynamics and structure factor X4time = int(X4time / dt / TimeStep) twopidl = 2 * pi / d1.Boxlength[0] #list over dimensions Numofq = int(qrange / twopidl.max()) wavevector = choosewavevector( Numofq, ndim)[:, 1:] #Only S4(q) at low wavenumber range is interested wavevector = wavevector.astype( np.float64) * twopidl[np.newaxis, :] #considering non-cubic box wavenumber = np.linalg.norm(wavevector, axis=1) sqresults = np.zeros((wavevector.shape[0], 2)) sqresults[:, 0] = wavenumber for n in range(d1.SnapshotNumber - X4time): RII = (velocity[n + X4time] * velocity[n]).sum(axis=1) RII = np.where(RII >= phi, 1, 0) sqtotal = np.zeros_like(sqresults) for i in range(ParticleNumber): if RII[i]: thetas = (d1.Positions[n][i][np.newaxis, :] * wavevector).sum(axis=1) sqtotal[:, 0] += np.sin(thetas) sqtotal[:, 1] += np.cos(thetas) sqresults[:, 1] += np.square(sqtotal).sum(axis=1) / ParticleNumber sqresults[:, 1] /= (d1.SnapshotNumber - X4time) sqresults = pd.DataFrame(sqresults).round(6) results = sqresults.groupby(sqresults[0]).mean().reset_index().values names = 'q S4' if outputfile: np.savetxt(outputfile, results, fmt='%.6f', header=names, comments='') print('--------- Compute S4(q) of slow particles over ------') return results, names
def Vonmises(inputfile, Neighborfile, ndim, strainrate, outputfile, ppp=[1, 1, 1], dt=0.002, results_path='../../analysis/Strain/'): """ Calculate Non-Affine Von-Mises Strain (local shear invariant) With the first snapshot of inputfile as reference The unit of strianrate should in align with the intrinsic time unit (i.e with dt) The code accounts for both orthogonal and triclinic boxes """ if not os.path.exists(results_path): os.makedirs(results_path) d = readdump(inputfile, ndim) d.read_onefile() positions = d.Positions particlenumber = d.ParticleNumber[0] snapshotnumber = d.SnapshotNumber boxlength = d.Boxlength hmatrix = d.hmatrix timestep = d.TimeStep[1] - d.TimeStep[0] PI = np.eye(ndim, dtype=int) #identity matrix along diag results = np.zeros((particlenumber, snapshotnumber - 1)) fneighbor = open(Neighborfile, 'r') Neighborlist = Voropp(fneighbor, particlenumber) #neighbor list [number, list...] fneighbor.close() for i in range(particlenumber): neighbors = Neighborlist[i, 1:Neighborlist[i, 0] + 1] RIJ0 = positions[0][neighbors] - positions[0][ i] #reference snapshot: the initial one #periodic = np.where(np.abs(RIJ0 / boxlength[0]) > 0.50, np.sign(RIJ0), 0) #RIJ0 -= boxlength[0] * periodic * ppp #remove periodic boundary conditions matrixij = np.dot(RIJ0, np.linalg.inv(hmatrix[0])) RIJ0 = np.dot(matrixij - np.rint(matrixij) * ppp, hmatrix[0]) for j in range(snapshotnumber - 1): RIJ1 = positions[j + 1][neighbors] - positions[j + 1][ i] #deformed snapshot matrixij = np.dot(RIJ1, np.linalg.inv(hmatrix[j + 1])) RIJ1 = np.dot(matrixij - np.rint(matrixij) * ppp, hmatrix[j + 1]) PJ = np.dot(np.linalg.inv(np.dot(RIJ0.T, RIJ0)), np.dot(RIJ0.T, RIJ1)) etaij = 0.5 * (np.dot(PJ, PJ.T) - PI) results[i, j] = np.sqrt(0.5 * np.trace( np.linalg.matrix_power( etaij - (1 / ndim) * np.trace(etaij) * PI, 2))) results = np.column_stack((np.arange(particlenumber) + 1, results)) strain = np.arange(snapshotnumber) * timestep * dt * strainrate results = np.vstack((strain, results)) names = 'id The_first_row_is_the_strain.0isNAN' fformat = '%d ' + '%.6f ' * (snapshotnumber - 1) np.savetxt(results_path + outputfile, results, fmt=fformat, header=names, comments='') print('------ Calculate Von Mises Strain Over -------') return results
The keyword filetype is used for different MD engines It has four choices: 'lammps' (default) 'lammpscenter' (lammps molecular dump with known atom type of each molecule center) moltypes is a dict mapping center atomic type to molecular type moltypes is also used to select the center atom such as moltypes = {3: 1, 5: 2} 'gsd' (HOOMD-blue standard output for static properties) 'gsd_dcd' (HOOMD-blue outputs for static and dynamic properties) """ #get the coordinate information d = readdump(inputfile, ndim, filetype, moltypes) d.read_onefile() if ndim == 3: results = np.zeros((nbin + 1, nbin + 1, nbin + 1)) for i in range(d.SnapshotNumber): #remove the original point to be (0, 0, 0) newpositions = d.Positions[i] - d.Boxbounds[i][:, 0] binsize = (d.Boxlength[i] / nbin)[np.newaxis, :] if i == 0: binvolume = np.prod(binsize) indices, counts = np.unique(np.rint(newpositions / binsize), axis = 0, return_counts = True) indices = indices.astype(np.int) for j in range(indices.shape[0]): results[indices[j][0], indices[j][1], indices[j][2]] += counts[j] results = results / d.SnapshotNumber
def Vonmises(inputfile, Neighborfile, ndim, strainrate, ppp=[1, 1, 1], dt=0.002, filetype='lammps', moltypes='', outputfile=''): """ Calculate Non-Affine Von-Mises Strain (local shear invariant) With the first snapshot of inputfile as reference The unit of strianrate should in align with the intrinsic time unit (i.e with dt) The code accounts for both orthogonal and triclinic boxes The keyword filetype is used for different MD engines It has four choices: 'lammps' (default) 'lammpscenter' (lammps molecular dump with known atom type of each molecule center) moltypes is a dict mapping center atomic type to molecular type moltypes is also used to select the center atom such as moltypes = {3: 1, 5: 2} 'gsd' (HOOMD-blue standard output for static properties) 'gsd_dcd' (HOOMD-blue outputs for static and dynamic properties) """ d = readdump(inputfile, ndim, filetype, moltypes) d.read_onefile() positions = d.Positions particlenumber = d.ParticleNumber[0] snapshotnumber = d.SnapshotNumber boxlength = d.Boxlength hmatrix = d.hmatrix timestep = d.TimeStep[1] - d.TimeStep[0] PI = np.eye(ndim, dtype=int) #identity matrix along diag results = np.zeros((particlenumber, snapshotnumber - 1)) fneighbor = open(Neighborfile, 'r') Neighborlist = Voropp(fneighbor, particlenumber) #neighbor list [number, list...] fneighbor.close() for i in range(particlenumber): neighbors = Neighborlist[i, 1:Neighborlist[i, 0] + 1] RIJ0 = positions[0][neighbors] - positions[0][ i] #reference snapshot: the initial one #periodic = np.where(np.abs(RIJ0 / boxlength[0]) > 0.50, np.sign(RIJ0), 0) #RIJ0 -= boxlength[0] * periodic * ppp #remove periodic boundary conditions matrixij = np.dot(RIJ0, np.linalg.inv(hmatrix[0])) RIJ0 = np.dot(matrixij - np.rint(matrixij) * ppp, hmatrix[0]) for j in range(snapshotnumber - 1): RIJ1 = positions[j + 1][neighbors] - positions[j + 1][ i] #deformed snapshot matrixij = np.dot(RIJ1, np.linalg.inv(hmatrix[j + 1])) RIJ1 = np.dot(matrixij - np.rint(matrixij) * ppp, hmatrix[j + 1]) PJ = np.dot(np.linalg.inv(np.dot(RIJ0.T, RIJ0)), np.dot(RIJ0.T, RIJ1)) etaij = 0.5 * (np.dot(PJ, PJ.T) - PI) results[i, j] = np.sqrt(0.5 * np.trace( np.linalg.matrix_power( etaij - (1 / ndim) * np.trace(etaij) * PI, 2))) results = np.column_stack((np.arange(particlenumber) + 1, results)) strain = np.arange(snapshotnumber) * timestep * dt * strainrate results = np.vstack((strain, results)) names = 'id The_first_row_is_the_strain.0isNAN' fformat = '%d ' + '%.6f ' * (snapshotnumber - 1) if outputfile: np.savetxt(outputfile, results, fmt=fformat, header=names, comments='') print('------ Calculate Von Mises Strain Over -------') return results, names
def neighbortypes(inputfile, ndim, neighborfile, filetype='lammps', moltypes='', outputfile=''): """Analysis the fractions of atom A in the first neighbor of atom B The keyword filetype is used for different MD engines It has four choices: 'lammps' (default) 'lammpscenter' (lammps molecular dump with known atom type of each molecule center) moltypes is a dict mapping center atomic type to molecular type moltypes is also used to select the center atom such as moltypes = {3: 1, 5: 2} 'gsd' (HOOMD-blue standard output for static properties) 'gsd_dcd' (HOOMD-blue outputs for static and dynamic properties) """ #get the coordinate information d = readdump(inputfile, ndim, filetype, moltypes) d.read_onefile() #get the neighbor list from voronoi analysis fneighbor = open(neighborfile, 'r') results = np.zeros((d.SnapshotNumber, 3)) #for binary system 11 12/21 22 for i in range(d.SnapshotNumber): neighborlist = Voropp( fneighbor, d.ParticleNumber[i]) #neighbor list [number, list....] neighbortype = d.ParticleType[i] medium = np.zeros(6) for j in range(d.ParticleNumber[i]): neighborsij = neighborlist[j, 1:(neighborlist[j, 0] + 1)] data11 = (neighbortype[j] + neighbortype[neighborsij] == 2).sum() if data11 > 0: medium[0] += neighborlist[j, 0] medium[1] += data11 data12 = (neighbortype[j] + neighbortype[neighborsij] == 3).sum() if data12 > 0: medium[2] += neighborlist[j, 0] medium[3] += data12 data22 = (neighbortype[j] + neighbortype[neighborsij] == 4).sum() if data22 > 0: medium[4] += neighborlist[j, 0] medium[5] += data22 results[i, 0] = medium[1] / medium[0] results[i, 1] = medium[3] / medium[2] results[i, 2] = medium[5] / medium[4] fneighbor.close() if outputfile: names = '11 12/21 22' np.savetxt(outputfile, results, fmt=3 * ' %.6f', header=names, comments='') print('-------demix checking over------') return results, names