def get_positions(self, coords='direct'): if self.direct_coords == True and coords[0].lower() == 'c': return oppvasp.direct_to_cartesian(self.positions, self.basis) elif self.direct_coords == False and coords[0].lower() == 'd': return oppvasp.cartesian_to_direct(self.positions, self.basis) else: return self.positions.copy()
def get_positions(self, coords = 'direct'): if self.direct_coords == True and coords[0].lower() == 'c': return oppvasp.direct_to_cartesian(self.positions, self.basis) elif self.direct_coords == False and coords[0].lower() == 'd': return oppvasp.cartesian_to_direct(self.positions, self.basis) else: return self.positions.copy()
def get_forces(self, coords): """ (n,3) array """ if not self.has_forces(): return None if coords[0].lower() == 'd': return self._forces elif coords[0].lower() == 'c': return direct_to_cartesian(self._forces, self._cell)
def get_supercell_positions(self, sx, sy, sz, coords = 'cart'): """ Returns a (sx,sy,sz) supercell in the form of a (sx*sy*sz,3) numpy array """ nat = self.get_num_atoms() sup = np.zeros((nat*sx*sy*sz,3)) c = np.diag((1,1,1)) # since we use direct coordinates for i in range(sx): for j in range(sy): for k in range(sz): m = i*sy*sz + j*sz + k #print m sup[m*nat:(m+1)*nat] = self._positions + np.dot(c,[i, j, k]) if coords[0].lower() == 'c': return direct_to_cartesian(sup, self._cell) else: return sup
def get_supercell_positions(self, sx, sy, sz, coords='cart'): """ Returns a (sx,sy,sz) supercell in the form of a (sx*sy*sz,3) numpy array """ nat = self.get_num_atoms() sup = np.zeros((nat * sx * sy * sz, 3)) c = np.diag((1, 1, 1)) # since we use direct coordinates for i in range(sx): for j in range(sy): for k in range(sz): m = i * sy * sz + j * sz + k #print m sup[m * nat:(m + 1) * nat] = self._positions + np.dot(c, [i, j, k]) if coords[0].lower() == 'c': return direct_to_cartesian(sup, self._cell) else: return sup
def get_positions(self, coords = 'direct', atom = -1): """ Returns positions for one (atom_no>=0) or all (atom_no=-1) atoms in direct or cartesian coordinates as a numpy array of dimension (steps, atoms, 3). Parameters: - coords : either 'direct' or 'cartesian' - atom : (int) Set this to only return positions for a single atom. """ if atom == -1: p = self.positions.copy() else: p = self.positions[:,atom,:].copy() if coords[0].lower() == 'd': return p elif coords[0].lower() == 'c': return direct_to_cartesian(p, self.basis)
def get_shortest_bond(self, include_self = False, safe_mode = True, rmax = 3.0): """ Returns a tupple containing the shortest bond length in Angstrom, followed by the indices of the two atoms making up the bond Parameters: include_self : bool (default False) Whether to check for bonds between an atom and its _own_ periodic image. This is only needed for very small unit cells, typically with a single atom. safe_mode : bool (default True) Much slower, but safer for non-cubic cells rmax : float (default 3.0) Maximum bond length (in Angstrom) to check for (only used in safe_mode) """ a = self._cell[0] b = self._cell[1] c = self._cell[2] pos = self._positions natoms = pos.shape[0] if natoms == 1: print "Note from get_shortest_bond(): include_self is enabled since only a single atom was found!" include_self = True # Limit to sphere of radius Rc if safe_mode: #if include_self: # print "include_self and safe_mode are not currently compatible. sorry" # return natoms = self.get_num_atoms() a = self._cell[0] b = self._cell[1] c = self._cell[2] # Find distance between opposing faces of the cell: wa = abs(np.dot(a,np.cross(b,c))) / np.linalg.norm(np.cross(b,c)) wb = abs(np.dot(b,np.cross(c,a))) / np.linalg.norm(np.cross(c,a)) wc = abs(np.dot(c,np.cross(a,b))) / np.linalg.norm(np.cross(a,b)) # If half the max radius exceeds one of the face-to-face distances (wa, wb or wc), # we add more atoms in the given direction(s) as necessary. la = 1 + int(2*rmax/wa) lb = 1 + int(2*rmax/wb) lc = 1 + int(2*rmax/wc) print "dim:",la,lb,lc #p2 = self.get_supercell_positions(la,lb,lc, coords='d') pos = np.zeros((natoms*la*lb*lc,3)) #offsets = np.zeros((natoms*la*lb*lc,3), dtype=np.int) #c = np.diag((1,1,1)) # since we use direct coordinates for i in range(la): for j in range(lb): for k in range(lc): m = i*lb*lc + j*lc + k pos[m*natoms:(m+1)*natoms] = self._positions + np.array((i, j, k)) #offsets[m*natoms:(m+1)*natoms] = [i, j, k] indices = np.tile(np.arange(natoms), la*lb*lc) paired = np.zeros(natoms, dtype=np.int) minr = np.zeros(natoms) for j in np.arange(natoms): # Center at atom <index>: pos -= pos[j] # Minimum image convention with coordinates in range lb*[-0.5,0.5> pos[:,0] -= la*(pos[:,0]*2/la).astype('int') pos[:,1] -= lb*(pos[:,1]*2/lb).astype('int') pos[:,2] -= lc*(pos[:,2]*2/lc).astype('int') cart = direct_to_cartesian(pos,self._cell) dr = np.sqrt(np.sum(cart**2,1)) cond = np.logical_and(dr < rmax, dr > 0.0) dr = dr[cond] if dr.shape[0] == 0: print "[Warning]: No neighbours found within %.2f Angstrom of atom %d" % (rmax,j) minr[j] = 1001.0 continue so = np.argsort(dr) #pos = pos[cond] #offsets = (2*pos).astype('int') ##indices = indices[cond] ## sort: #dr = dr[so] #pos = pos[so] #offsets = offsets[so] #indices = indices[so] #indices, dr, offsets, pos = self.get_neighbours(j) paired[j] = indices[cond][so][0] minr[j] = dr[so][0] #pairs[j,nearest] = #minpair = pairs[j,nearest] #minr = dr[0] #print nearest,shortest minidx = np.argmin(minr) minr = minr[minidx] if minr > 1000.0: print "[Error]: No bonds shorter than %.2f Angstrom found. Please increase rmax" % rmax return (minr,minidx,paired[minidx]) else: # Build array of pair indices: pairs = get_pairs(natoms, include_self=include_self) npairs = pairs.shape[0] # Find displacement vectors for all pairs: x = pos[pairs[:,0]] - pos[pairs[:,1]] # Use minimum image convention to threat bonds over PBCs # Note: use safe_mode with tilted unit cells in which the # shortest bond is close to half of one of the cell dimensions x = x - (2*x).astype('int') X = direct_to_cartesian(x, self._cell) r2 = (X**2).sum(axis=1) r = np.sqrt(r2) meanr2 = np.mean(r2,axis=0) meanr = np.mean(r,axis=0) minidx = np.argmin(r) minpair = pairs[minidx] minr = r[minidx] return (minr,minpair[0],minpair[1])
def get_neighbours(self, atom_no, rmax = 3.0): """ Returns a list of all atoms within <rmax> of atom <atom_no>, including atoms across periodic boundary conditions. """ natoms = self.get_num_atoms() a = self._cell[0] b = self._cell[1] c = self._cell[2] # Find distance between opposing faces of the cell: wa = abs(np.dot(a,np.cross(b,c))) / np.linalg.norm(np.cross(b,c)) wb = abs(np.dot(b,np.cross(c,a))) / np.linalg.norm(np.cross(c,a)) wc = abs(np.dot(c,np.cross(a,b))) / np.linalg.norm(np.cross(a,b)) # If half the max radius exceeds one of the face-to-face distances (wa, wb or wc), # we add more atoms in the given direction(s) as necessary. la = 1 + int(2*rmax/wa) lb = 1 + int(2*rmax/wb) lc = 1 + int(2*rmax/wc) #print "dim:",la,lb,lc #p2 = self.get_supercell_positions(la,lb,lc, coords='d') pos = np.zeros((natoms*la*lb*lc,3)) offsets = np.zeros((natoms*la*lb*lc,3), dtype=np.int) #c = np.diag((1,1,1)) # since we use direct coordinates for i in range(la): for j in range(lb): for k in range(lc): m = i*lb*lc + j*lc + k pos[m*natoms:(m+1)*natoms] = self._positions + np.array((i, j, k)) offsets[m*natoms:(m+1)*natoms] = [i, j, k] indices = np.tile(np.arange(0,natoms), la*lb*lc) # Center at atom <index>: pos -= pos[atom_no] # Minimum image convention with coordinates in range lb*[-0.5,0.5> pos[:,0] -= la*(pos[:,0]*2/la).astype('int') pos[:,1] -= lb*(pos[:,1]*2/lb).astype('int') pos[:,2] -= lc*(pos[:,2]*2/lc).astype('int') cart = direct_to_cartesian(pos, self._cell) dr = np.sqrt(np.sum(cart**2, axis = 1)) cond = np.logical_and(dr < rmax, dr > 0.0) dr = dr[cond] pos = pos[cond] offsets = (2*pos).astype('int') indices = indices[cond] if len(dr) == 0: q = np.sqrt(np.sum(cart**2, axis = 1)) q = q[q>0.] print u'No neighbours within %.2f Å. Nearest neighbour found at %.2f Å' % (rmax, np.sort(q)[0]) return None # sort: so = np.argsort(dr) dr = dr[so] pos = pos[so] offsets = offsets[so] indices = indices[so] return indices, dr, offsets, pos
def get_velocities(self, coords): """ (n,3) array """ if coords[0].lower() == 'd': return self._velocities elif coords[0].lower() == 'c': return direct_to_cartesian(self._velocities, self._cell)
pos = poscar1.get_positions(coords='direct') natoms = pos.shape[0] # Build array of pair indices: pairs = get_pairs(natoms) npairs = pairs.shape[0] print "%d atoms, %d pairs" % (natoms, npairs) # Find displacement vectors for all atoms: x = pos[pairs[:, 0]] - pos[pairs[:, 1]] # Use minimum image convention to threat bonds over PBCs # Note: This will not work with *very* tilted unit cells x = x - (2 * x).astype('int') X = direct_to_cartesian(x, poscar1.get_cell()) r2 = (X**2).sum(axis=1) r = np.sqrt(r2) meanr2 = np.mean(r2, axis=0) meanr = np.mean(r, axis=0) minidx = np.argmin(r) minpair = pairs[minidx] minr = r[minidx] print "Shortest bond is: %.3f Angstrom (between atoms %d and %d)" % ( minr, minpair[0], minpair[1])
def get_shortest_bond(self, include_self=False, safe_mode=True, rmax=3.0): """ Returns a tupple containing the shortest bond length in Angstrom, followed by the indices of the two atoms making up the bond Parameters: include_self : bool (default False) Whether to check for bonds between an atom and its _own_ periodic image. This is only needed for very small unit cells, typically with a single atom. safe_mode : bool (default True) Much slower, but safer for non-cubic cells rmax : float (default 3.0) Maximum bond length (in Angstrom) to check for (only used in safe_mode) """ a = self._cell[0] b = self._cell[1] c = self._cell[2] pos = self._positions natoms = pos.shape[0] if natoms == 1: print "Note from get_shortest_bond(): include_self is enabled since only a single atom was found!" include_self = True # Limit to sphere of radius Rc if safe_mode: #if include_self: # print "include_self and safe_mode are not currently compatible. sorry" # return natoms = self.get_num_atoms() a = self._cell[0] b = self._cell[1] c = self._cell[2] # Find distance between opposing faces of the cell: wa = abs(np.dot(a, np.cross(b, c))) / np.linalg.norm(np.cross( b, c)) wb = abs(np.dot(b, np.cross(c, a))) / np.linalg.norm(np.cross( c, a)) wc = abs(np.dot(c, np.cross(a, b))) / np.linalg.norm(np.cross( a, b)) # If half the max radius exceeds one of the face-to-face distances (wa, wb or wc), # we add more atoms in the given direction(s) as necessary. la = 1 + int(2 * rmax / wa) lb = 1 + int(2 * rmax / wb) lc = 1 + int(2 * rmax / wc) print "dim:", la, lb, lc #p2 = self.get_supercell_positions(la,lb,lc, coords='d') pos = np.zeros((natoms * la * lb * lc, 3)) #offsets = np.zeros((natoms*la*lb*lc,3), dtype=np.int) #c = np.diag((1,1,1)) # since we use direct coordinates for i in range(la): for j in range(lb): for k in range(lc): m = i * lb * lc + j * lc + k pos[m * natoms:(m + 1) * natoms] = self._positions + np.array((i, j, k)) #offsets[m*natoms:(m+1)*natoms] = [i, j, k] indices = np.tile(np.arange(natoms), la * lb * lc) paired = np.zeros(natoms, dtype=np.int) minr = np.zeros(natoms) for j in np.arange(natoms): # Center at atom <index>: pos -= pos[j] # Minimum image convention with coordinates in range lb*[-0.5,0.5> pos[:, 0] -= la * (pos[:, 0] * 2 / la).astype('int') pos[:, 1] -= lb * (pos[:, 1] * 2 / lb).astype('int') pos[:, 2] -= lc * (pos[:, 2] * 2 / lc).astype('int') cart = direct_to_cartesian(pos, self._cell) dr = np.sqrt(np.sum(cart**2, 1)) cond = np.logical_and(dr < rmax, dr > 0.0) dr = dr[cond] if dr.shape[0] == 0: print "[Warning]: No neighbours found within %.2f Angstrom of atom %d" % ( rmax, j) minr[j] = 1001.0 continue so = np.argsort(dr) #pos = pos[cond] #offsets = (2*pos).astype('int') ##indices = indices[cond] ## sort: #dr = dr[so] #pos = pos[so] #offsets = offsets[so] #indices = indices[so] #indices, dr, offsets, pos = self.get_neighbours(j) paired[j] = indices[cond][so][0] minr[j] = dr[so][0] #pairs[j,nearest] = #minpair = pairs[j,nearest] #minr = dr[0] #print nearest,shortest minidx = np.argmin(minr) minr = minr[minidx] if minr > 1000.0: print "[Error]: No bonds shorter than %.2f Angstrom found. Please increase rmax" % rmax return (minr, minidx, paired[minidx]) else: # Build array of pair indices: pairs = get_pairs(natoms, include_self=include_self) npairs = pairs.shape[0] # Find displacement vectors for all pairs: x = pos[pairs[:, 0]] - pos[pairs[:, 1]] # Use minimum image convention to threat bonds over PBCs # Note: use safe_mode with tilted unit cells in which the # shortest bond is close to half of one of the cell dimensions x = x - (2 * x).astype('int') X = direct_to_cartesian(x, self._cell) r2 = (X**2).sum(axis=1) r = np.sqrt(r2) meanr2 = np.mean(r2, axis=0) meanr = np.mean(r, axis=0) minidx = np.argmin(r) minpair = pairs[minidx] minr = r[minidx] return (minr, minpair[0], minpair[1])
def get_neighbours(self, atom_no, rmax=3.0): """ Returns a list of all atoms within <rmax> of atom <atom_no>, including atoms across periodic boundary conditions. """ natoms = self.get_num_atoms() a = self._cell[0] b = self._cell[1] c = self._cell[2] # Find distance between opposing faces of the cell: wa = abs(np.dot(a, np.cross(b, c))) / np.linalg.norm(np.cross(b, c)) wb = abs(np.dot(b, np.cross(c, a))) / np.linalg.norm(np.cross(c, a)) wc = abs(np.dot(c, np.cross(a, b))) / np.linalg.norm(np.cross(a, b)) # If half the max radius exceeds one of the face-to-face distances (wa, wb or wc), # we add more atoms in the given direction(s) as necessary. la = 1 + int(2 * rmax / wa) lb = 1 + int(2 * rmax / wb) lc = 1 + int(2 * rmax / wc) #print "dim:",la,lb,lc #p2 = self.get_supercell_positions(la,lb,lc, coords='d') pos = np.zeros((natoms * la * lb * lc, 3)) offsets = np.zeros((natoms * la * lb * lc, 3), dtype=np.int) #c = np.diag((1,1,1)) # since we use direct coordinates for i in range(la): for j in range(lb): for k in range(lc): m = i * lb * lc + j * lc + k pos[m * natoms:(m + 1) * natoms] = self._positions + np.array((i, j, k)) offsets[m * natoms:(m + 1) * natoms] = [i, j, k] indices = np.tile(np.arange(0, natoms), la * lb * lc) # Center at atom <index>: pos -= pos[atom_no] # Minimum image convention with coordinates in range lb*[-0.5,0.5> pos[:, 0] -= la * (pos[:, 0] * 2 / la).astype('int') pos[:, 1] -= lb * (pos[:, 1] * 2 / lb).astype('int') pos[:, 2] -= lc * (pos[:, 2] * 2 / lc).astype('int') cart = direct_to_cartesian(pos, self._cell) dr = np.sqrt(np.sum(cart**2, axis=1)) cond = np.logical_and(dr < rmax, dr > 0.0) dr = dr[cond] pos = pos[cond] offsets = (2 * pos).astype('int') indices = indices[cond] if len(dr) == 0: q = np.sqrt(np.sum(cart**2, axis=1)) q = q[q > 0.] print u'No neighbours within %.2f Å. Nearest neighbour found at %.2f Å' % ( rmax, np.sort(q)[0]) return None # sort: so = np.argsort(dr) dr = dr[so] pos = pos[so] offsets = offsets[so] indices = indices[so] return indices, dr, offsets, pos
def get_positions(self, coords): if coords[0].lower() == 'd': return self._positions.copy() elif coords[0].lower() == 'c': return direct_to_cartesian(self._positions, self._cell)
def __init__(self, trajectory, n_bins = 10, atoms = [10], smin = 1000, smax = 20000, cache = None, step = 5): struc = trajectory.get_snapshot(0) vol = struc.get_volume() s = sqrt(np.sum(struc.get_cell()[0]**2)) # sidelength a #max_dist = sqrt(3*(s/2.)**2) # max distance between atoms in 3D self.max_dist = s / 2. # the largest sphere that can be inscribed in a cube of side L print "Sidelength: %.2f Å, volume: %.2f Å^3, max r: %.2f Å" % (s, vol, self.max_dist) if n_bins == 'auto': rr = 0. # mean atom-atom distance for i in range(trajectory.natoms): rr += struc.get_neighbours(i)[1].mean() rr /= trajectory.natoms n_bins = int(self.max_dist / (0.025 * rr)) # the value 0.025 rr from J. M. Haile. Molecular dynamics simulation : elementary methods. Wiley, 1992, p. 263 print "Average atom-atom distance: %.2f Angstrom => setting n_bins = %d" % (rr, n_bins) self.dr = self.max_dist / n_bins print "Number of bins: %d, makes dr = %.2f Å" % (n_bins, self.dr) nsteps = trajectory.length natoms = trajectory.num_atoms self.density = natoms / vol if cache != None: hasher = hashlib.sha1() f = open(trajectory.filename, 'rb') try: hasher.update(f.read()) finally: f.close() trajhash = hasher.hexdigest() print "Trajectory hash:",trajhash if os.path.isfile(cache): pkl_file = open(cache, 'rb') data1 = pickle.load(pkl_file) params = data1['params'] if params['hash'] != trajhash: print 'Note: Trajectory file hash differs from value in cache file "%s". Bypassing cache.' % cache elif params['n_bins'] != n_bins: print 'Note: n_bins value differs from value in cache file "%s". Bypassing cache.' % cache elif params['atoms'] != atoms: print 'Note: atoms value differs from value in cache file "%s". Bypassing cache.' % cache elif params['smin'] != smin: print 'Note: smin value differs from value in cache file "%s". Bypassing cache.' % cache elif params['smax'] != smax: print 'Note: smax value differs from value in cache file "%s". Bypassing cache.' % cache else: # Hooray, we shall use the cache self.x = data1['r'] self.y = data1['g'] pkl_file.close() print 'Read data from cache "%s", trajectory has is "%s"' % (cache, trajhash) return pkl_file.close() #d = 2 # super cell size #fac = 2 * np.pi * d # 2pi/0.125 = 2pi*8 (0.125 is the smallest distance between atoms in direct coordinates for the 2x2x2 Si cell) pos = trajectory.get_positions( coords = 'direct' ) frames = np.arange(smin, np.min([nsteps,smax]), step) hist = np.zeros((frames.shape[0], n_bins)) print hist.shape[0],"steps" for i, probe_at in enumerate(atoms): print "Probing atom %d of %d" % (i+1, len(atoms)) for i, n in enumerate(frames): cpos = pos[n] ccell = trajectory.basis[n] r = cpos[[x for x in range(cpos.shape[0]) if x != probe_at]] - cpos[probe_at] # use probe_at as center # Use minimum image convention to threat bonds over PBCs # Note: This will not work with *very* tilted unit cells r = r - (2*r).astype('int') r = direct_to_cartesian(r, ccell) r2 = (r**2).sum(axis=1) r = np.sqrt(r2) cbin = np.floor(r / self.dr).astype('int') # could we vectorise this somehow? for x in cbin: if x < n_bins: hist[i,x] += 1 # Normalize hist /= len(atoms) #norm = 2.0 * np.pi * dr * density self.x = np.zeros(n_bins) self.y = np.zeros(n_bins) for i in np.arange(0, n_bins): shell_r = i * self.dr # distance to beginning of shell shell_vol = 4./3. * np.pi * ((shell_r + self.dr)**3 - shell_r**3) # volume of the shell time_avg_n = np.sum(hist[:,i]) / hist.shape[0] # time average val = time_avg_n / shell_vol / self.density # local density to system density ratio #natoms / norm / ((rr**2) + (dr**2) / 12.0) self.x[i] = shell_r + 0.5*self.dr # use middle of the shell self.y[i] = val #self.x = np.arange(n_bins) * dr #self.y = np.mean(hist[0:10000], axis = 0) ##self.y = np.mean(hist, axis = 0) #vmd_r = [0.05, 0.15000000000000002, 0.25, 0.35000000000000003, 0.45, 0.55, 0.65, 0.75, 0.8500000000000001, 0.9500000000000001, 1.05, 1.1500000000000001, 1.25, 1.35, 1.4500000000000002, 1.55, 1.6500000000000001, 1.75, 1.85, 1.9500000000000002, 2.0500000000000003, 2.15, 2.25, 2.35, 2.45, 2.5500000000000003, 2.6500000000000004, 2.75, 2.85, 2.95, 3.0500000000000003, 3.1500000000000004, 3.25, 3.35, 3.45, 3.5500000000000003, 3.6500000000000004, 3.75, 3.85, 3.95, 4.05, 4.15, 4.25, 4.3500000000000005, 4.45, 4.55, 4.65, 4.75, 4.8500000000000005, 4.95, 5.050000000000001, 5.15, 5.25, 5.3500000000000005, 5.45, 5.550000000000001, 5.65, 5.75, 5.8500000000000005, 5.95] #vmd_g = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.007519646352273967, 0.16803063700833396, 0.8705159618606413, 1.758370840475422, 2.283940404874828, 1.983953361507034, 1.4766080953857323, 1.1769551771463873, 0.9292292714634547, 0.8134830099213072, 0.7275062820612057, 0.7369909416061019, 0.7738143781341961, 0.8264792577807392, 0.9459870991621928, 1.008875524678367, 1.0764976921218232, 1.1637934824221774, 1.1568744636400938, 1.1417407274585984, 1.1471446817553927, 1.12710440892153, 1.081071932165155, 1.0372008450448278, 0.9681381745490185, 0.9209937945818178, 0.9260629623139609, 0.9387623185735751, 0.9248015478764121, 0.9193931865534244, 0.9593854795073877, 0.969338021724002, 1.0044774942407746, 1.0028481628862973, 1.0456729324756333, 1.0467550666063061, 1.0812861053364635, 1.0873968724468983, 1.102965726490678, 1.0960581045516773, 1.0686904640968937] #plt.plot(vmd_r, vmd_g, 'x-') #plt.show() if cache != None: # should we ask to overwrite? pkl_file = open(cache, 'wb') pickle.dump({ 'params': { 'hash': trajhash, 'n_bins': n_bins, 'atoms': atoms, 'smin': smin, 'smax': smax }, 'r': self.x, 'g': self.y }, pkl_file) pkl_file.close() print 'Wrote cache "%s", trajectory has is "%s"' % (cache, trajhash)
def __init__(self, trajectory, n_bins=10, atoms=[10], smin=1000, smax=20000, cache=None, step=5): struc = trajectory.get_snapshot(0) vol = struc.get_volume() s = sqrt(np.sum(struc.get_cell()[0]**2)) # sidelength a #max_dist = sqrt(3*(s/2.)**2) # max distance between atoms in 3D self.max_dist = s / 2. # the largest sphere that can be inscribed in a cube of side L print "Sidelength: %.2f Å, volume: %.2f Å^3, max r: %.2f Å" % ( s, vol, self.max_dist) if n_bins == 'auto': rr = 0. # mean atom-atom distance for i in range(trajectory.natoms): rr += struc.get_neighbours(i)[1].mean() rr /= trajectory.natoms n_bins = int(self.max_dist / (0.025 * rr)) # the value 0.025 rr from J. M. Haile. Molecular dynamics simulation : elementary methods. Wiley, 1992, p. 263 print "Average atom-atom distance: %.2f Angstrom => setting n_bins = %d" % ( rr, n_bins) self.dr = self.max_dist / n_bins print "Number of bins: %d, makes dr = %.2f Å" % (n_bins, self.dr) nsteps = trajectory.length natoms = trajectory.num_atoms self.density = natoms / vol if cache != None: hasher = hashlib.sha1() f = open(trajectory.filename, 'rb') try: hasher.update(f.read()) finally: f.close() trajhash = hasher.hexdigest() print "Trajectory hash:", trajhash if os.path.isfile(cache): pkl_file = open(cache, 'rb') data1 = pickle.load(pkl_file) params = data1['params'] if params['hash'] != trajhash: print 'Note: Trajectory file hash differs from value in cache file "%s". Bypassing cache.' % cache elif params['n_bins'] != n_bins: print 'Note: n_bins value differs from value in cache file "%s". Bypassing cache.' % cache elif params['atoms'] != atoms: print 'Note: atoms value differs from value in cache file "%s". Bypassing cache.' % cache elif params['smin'] != smin: print 'Note: smin value differs from value in cache file "%s". Bypassing cache.' % cache elif params['smax'] != smax: print 'Note: smax value differs from value in cache file "%s". Bypassing cache.' % cache else: # Hooray, we shall use the cache self.x = data1['r'] self.y = data1['g'] pkl_file.close() print 'Read data from cache "%s", trajectory has is "%s"' % ( cache, trajhash) return pkl_file.close() #d = 2 # super cell size #fac = 2 * np.pi * d # 2pi/0.125 = 2pi*8 (0.125 is the smallest distance between atoms in direct coordinates for the 2x2x2 Si cell) pos = trajectory.get_positions(coords='direct') frames = np.arange(smin, np.min([nsteps, smax]), step) hist = np.zeros((frames.shape[0], n_bins)) print hist.shape[0], "steps" for i, probe_at in enumerate(atoms): print "Probing atom %d of %d" % (i + 1, len(atoms)) for i, n in enumerate(frames): cpos = pos[n] ccell = trajectory.basis[n] r = cpos[[x for x in range(cpos.shape[0]) if x != probe_at ]] - cpos[probe_at] # use probe_at as center # Use minimum image convention to threat bonds over PBCs # Note: This will not work with *very* tilted unit cells r = r - (2 * r).astype('int') r = direct_to_cartesian(r, ccell) r2 = (r**2).sum(axis=1) r = np.sqrt(r2) cbin = np.floor(r / self.dr).astype('int') # could we vectorise this somehow? for x in cbin: if x < n_bins: hist[i, x] += 1 # Normalize hist /= len(atoms) #norm = 2.0 * np.pi * dr * density self.x = np.zeros(n_bins) self.y = np.zeros(n_bins) for i in np.arange(0, n_bins): shell_r = i * self.dr # distance to beginning of shell shell_vol = 4. / 3. * np.pi * ( (shell_r + self.dr)**3 - shell_r**3) # volume of the shell time_avg_n = np.sum(hist[:, i]) / hist.shape[0] # time average val = time_avg_n / shell_vol / self.density # local density to system density ratio #natoms / norm / ((rr**2) + (dr**2) / 12.0) self.x[i] = shell_r + 0.5 * self.dr # use middle of the shell self.y[i] = val #self.x = np.arange(n_bins) * dr #self.y = np.mean(hist[0:10000], axis = 0) ##self.y = np.mean(hist, axis = 0) #vmd_r = [0.05, 0.15000000000000002, 0.25, 0.35000000000000003, 0.45, 0.55, 0.65, 0.75, 0.8500000000000001, 0.9500000000000001, 1.05, 1.1500000000000001, 1.25, 1.35, 1.4500000000000002, 1.55, 1.6500000000000001, 1.75, 1.85, 1.9500000000000002, 2.0500000000000003, 2.15, 2.25, 2.35, 2.45, 2.5500000000000003, 2.6500000000000004, 2.75, 2.85, 2.95, 3.0500000000000003, 3.1500000000000004, 3.25, 3.35, 3.45, 3.5500000000000003, 3.6500000000000004, 3.75, 3.85, 3.95, 4.05, 4.15, 4.25, 4.3500000000000005, 4.45, 4.55, 4.65, 4.75, 4.8500000000000005, 4.95, 5.050000000000001, 5.15, 5.25, 5.3500000000000005, 5.45, 5.550000000000001, 5.65, 5.75, 5.8500000000000005, 5.95] #vmd_g = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.007519646352273967, 0.16803063700833396, 0.8705159618606413, 1.758370840475422, 2.283940404874828, 1.983953361507034, 1.4766080953857323, 1.1769551771463873, 0.9292292714634547, 0.8134830099213072, 0.7275062820612057, 0.7369909416061019, 0.7738143781341961, 0.8264792577807392, 0.9459870991621928, 1.008875524678367, 1.0764976921218232, 1.1637934824221774, 1.1568744636400938, 1.1417407274585984, 1.1471446817553927, 1.12710440892153, 1.081071932165155, 1.0372008450448278, 0.9681381745490185, 0.9209937945818178, 0.9260629623139609, 0.9387623185735751, 0.9248015478764121, 0.9193931865534244, 0.9593854795073877, 0.969338021724002, 1.0044774942407746, 1.0028481628862973, 1.0456729324756333, 1.0467550666063061, 1.0812861053364635, 1.0873968724468983, 1.102965726490678, 1.0960581045516773, 1.0686904640968937] #plt.plot(vmd_r, vmd_g, 'x-') #plt.show() if cache != None: # should we ask to overwrite? pkl_file = open(cache, 'wb') pickle.dump( { 'params': { 'hash': trajhash, 'n_bins': n_bins, 'atoms': atoms, 'smin': smin, 'smax': smax }, 'r': self.x, 'g': self.y }, pkl_file) pkl_file.close() print 'Wrote cache "%s", trajectory has is "%s"' % (cache, trajhash)
poscar1 = PoscarParser(args.infile).get_structure() pos = poscar1.get_positions( coords = 'direct' ) natoms = pos.shape[0] # Build array of pair indices: pairs = get_pairs(natoms) npairs = pairs.shape[0] print "%d atoms, %d pairs" % (natoms, npairs) # Find displacement vectors for all atoms: x = pos[pairs[:,0]] - pos[pairs[:,1]] # Use minimum image convention to threat bonds over PBCs # Note: This will not work with *very* tilted unit cells x = x - (2*x).astype('int') X = direct_to_cartesian(x, poscar1.get_cell()) r2 = (X**2).sum(axis=1) r = np.sqrt(r2) meanr2 = np.mean(r2,axis=0) meanr = np.mean(r,axis=0) minidx = np.argmin(r) minpair = pairs[minidx] minr = r[minidx] print "Shortest bond is: %.3f Angstrom (between atoms %d and %d)" % (minr,minpair[0],minpair[1])
def __init__(self, trajectory, lattice, atom = 0, nn = 8, step = 50, remove_lattice_drift = True, cache = True): """ DistanceFromLatticeSites class Parameters: - trajectory : oppvasp.Trajectory object - lattice : oppvasp.Structure object describing the lattice sites - atom : atom to follow - nn : number of nearest sites - step : step size """ if trajectory.pbc_unwrapped: print "----" print "DistanceFromLatticeSites: Probably best to use a trajectory with PBCs (no unwrapping)" print "----" self.entered_site = Event() self.left_site = Event() self.bottombar_labels = {} self.bottombar_height = .5 self.bottombar_colors = {} self.valid = True self.lattice = lattice self.traj = trajectory.get_selection(step = step) if self.traj.length <= 1: print "[DistanceFromLatticeSites ERROR] Trajectory selection is too short. Aborting" self.valid = False return ts = self.traj.time[1] - self.traj.time[0] print "[DistanceFromLatticeSites] Timestamp for trajectory selection is %.f fs" % ts p = trajectory.path.split('/') basename = p.pop() cache_file = '/'.join(p) + '/' + basename.rsplit('.',1)[0] + '_dist_from_lattice.pkl' print "cache file:",cache_file if cache and os.path.isfile(cache_file): pkl_file = open(cache_file, 'rb') data = pickle.load(pkl_file) pkl_file.close() self.x = data['x'] self.y = data['y'] self.sites = data['sites'] print "[DistanceFromLatticeSites] Loaded from cache %s" % cache_file return #checks? if remove_lattice_drift: print "[DistanceFromLatticeSites] Calculating drift using AvgDistPlotter" avg = AvgDistPlotter(trajectory = self.traj, lattice = self.lattice, step = 1) drift = avg.y_smoothed pos = self.traj.get_positions(coords = 'direct') print "Min/max: %.2f / %.2f" % (np.min(pos), np.max(pos)) print "[DistanceFromLatticeSites] Unwrapping PBCs before smoothing" self.traj.unwrap_pbc() pos = self.traj.get_positions(coords = 'direct') self.traj.wrap_pbc() print "Min/max: %.2f / %.2f" % (np.min(pos), np.max(pos)) print "[DistanceFromLatticeSites] Generating symmetric running mean..." atpos = pos[:,atom] atpos = symmetric_running_mean(atpos, 250/ts) atpos = symmetric_running_mean(atpos, 500/ts) # Wrap back into cell atpos = atpos - np.floor(atpos) print "Min/max: %.2f / %.2f" % (np.min(atpos), np.max(atpos)) np.save('temp.npz', atpos) cell = self.traj.basis latpos = lattice.get_positions(coords = 'direct') natoms = pos.shape[1] nsteps = pos.shape[0] nsites = latpos.shape[0] #print "[DistanceFromLatticeSites] Reference lattice contains %d lattice sites" % (nsites) dists = np.zeros((nsteps, nn), dtype=np.float) sites = np.zeros((nsteps, nn), dtype=np.int) pbar = ProgressBar(widgets=['[DistanceFromLatticeSites] Analysing step ',SimpleProgress(),'...'], maxval = nsteps).start() for i in np.arange(0, nsteps): pbar.update(i) # Find displacement vectors between atom and all sites: r = atpos[i] - latpos # Use minimum image convention to threat bonds over PBCs # Note: This will not work with *very* tilted unit cells r = r - (2*r).astype('int') #r = r - np.floor(r) r = direct_to_cartesian(r, cell[i]) if remove_lattice_drift: r -= drift[i] # cartesian? yes r2 = (r**2).sum(axis=1) r = np.sqrt(r2) idx = np.argsort(r) dists[i] = r[idx[0:nn]] sites[i] = idx[0:nn] #print "Shortest bond is: %.3f Angstrom (between atoms %d and %d)" % (minr,minpair[0],minpair[1]) #break pbar.finish() #dists = symmetric_running_median(dists,6) #dists = symmetric_running_mean(dists,2) self.x = self.traj.time/1000. self.y = dists self.sites = sites pkl_file = open(cache_file, 'wb') pickle.dump({'x': self.x, 'y': self.y, 'sites': self.sites, 'trajectory': self.traj.path, 'lattice': lattice }, pkl_file) pkl_file.close() print "[DistanceFromLatticeSites] Wrote cache file: %s" % cache_file