def reindex_sites(structure, lattice, tolerance=0.5): """ Reindexes atoms of structure according to lattice sites. Expects that the structure is an exact supercell of the lattice, as far cell vectors are concerned. The atoms, however, may have moved around a bit. To get an index, an atom must be clearly closer to one ideal lattice site than to any other, within a given tolerance (in units of `structure.scale`?). """ from pylada.crystal import neighbors, supercell from copy import deepcopy # haowei: should not change lattice lat = deepcopy(lattice) # first give a natural index for the sites in lattice for i, a in enumerate(lat): a.site = i # in the supercell, each atom carry the site from the lat above, and will # goes into the neighs lat = supercell(lattice=lat, supercell=structure.cell * structure.scale / lat.scale) for atom in structure: neighs_in_str = [n for n in neighbors(structure, 1, atom.pos)] d = neighs_in_str[0][2] # if two atoms from structure and lattice have exactly the same coordination # and hence dist = 0, it will be neglected by neighbors # add 1E-6 to atom.pos to avoid this, but aparrently this is not a perfect # solution, Haowei neighs = [n for n in neighbors(lat, 2, atom.pos + 1E-6)] assert abs(neighs[1][2]) > 1e-12,\ RuntimeError('Found two sites occupying the same position.') if neighs[0][2] * lat.scale > tolerance * d: atom.site = -1 else: atom.site = neighs[0][0].site
def relax_structure_v1(structure, min_dist_AA=1., min_dist_AB=1., Niter=500): from itertools import combinations from pylada.crystal import neighbors indexes = range(len(structure)) for iter in range(Niter): succes = 1 np.random.shuffle(indexes) for index_A in indexes: neighs = [ n for n in neighbors(structure, 1, structure[index_A].pos) ] index_B = get_index(structure, neighs[0][0]) if not same_kind(structure, index_A, index_B): min_dist = min_dist_AB / float(structure.scale) else: min_dist = min_dist_AA / float(structure.scale) if neighs[0][-1] < min_dist: move_by = min_dist - neighs[0][-1] if move_by * structure.scale > 0.02: succes = 0 relax_pair_cm(structure, index_A, index_B, 1.2 * move_by, min_dist) move_to_cell(structure, index_A) move_to_cell(structure, index_B) if succes: return 1 return 0
def get_cn(structure, cut_off = 2.8, index = -1, **kwargs): atom_types, N = get_types(structure) types_pdf = kwargs.get("types_pdf", get_pdf_types(atom_types)) scale = float(structure.scale) cn = {} max_coord = 15 for type in types_pdf: cn[type] = np.zeros(max_coord) for index_A in range(len(structure)): A = structure[index_A].type pos_A = structure[index_A].pos neighs = [n for n in neighbors(structure, max_coord-1, pos_A)] d2_AB_prev = 10. count = {} for i, nB in enumerate(neighs): B = nB[0].type d2_AB = (scale *nB[2]) delta_dist = d2_AB - d2_AB_prev d2_AB_prev = d2_AB if d2_AB > cut_off: break #if delta_dist>0.5: # break else: if A+B not in count.keys(): count[A+B] = 0 count[A+B] += 1 for key in count.keys(): cn[key][count[key]] += 1./N[A] return cn
def count_tot_broken_bonds(bulk=None, slab=None): """Counts total number of broken bonds""" from pylada.crystal import neighbors under_coord = sort_under_coord(bulk=bulk, slab=slab) rc = z_center(slab=slab) # Check the coordination in the bulk first shell bulk_first_shell = [] for atom in bulk: bulk_first_shell.append( [atom.type, len(neighbors(structure=bulk, nmax=1, center=atom.pos, tolerance=1e-1 / bulk.scale))]) broken_bonds = [] for i in range(len(under_coord)): atom = slab[under_coord[i][0]] coordination = under_coord[i][1] for j in range(len(bulk)): eq_site = float(atom.site - j) / float(len(bulk)) if abs(int(eq_site) - eq_site) < 1e-5: break broken_bonds.append(abs(coordination - bulk_first_shell[j][1])) return sum(array(broken_bonds))
def count_broken_bonds_per_area(bulk=None, slab=None): """Counts broken bonds per atom""" from pylada.crystal import neighbors under_coord = sort_under_coord(bulk=bulk, slab=slab) rc = z_center(slab=slab) # Check the coordination in the bulk first shell bulk_first_shell = [] for atom in bulk: bulk_first_shell.append( [atom.type, len(neighbors(structure=bulk, nmax=1, center=atom.pos, tolerance=1e-1 / bulk.scale))]) broken_bonds = [] for i in range(len(under_coord)): atom = slab[under_coord[i][0]] coordination = under_coord[i][1] for j in range(len(bulk)): eq_site = float(atom.site - j) / float(len(bulk)) if abs(int(eq_site) - eq_site) < 1e-5: break broken_bonds.append(abs(coordination - bulk_first_shell[j][1])) c = cross(slab.cell[0], slab.cell[1]) area = sqrt(dot(c, c)) * float(slab.scale)**2 return sum(array(broken_bonds)) / area / 2 # there are two surfaces
def relax_structure_v1(structure, min_dist_AA=1., min_dist_AB=1., Niter = 500): from itertools import combinations from pylada.crystal import neighbors indexes = range(len(structure)) for iter in range(Niter): succes = 1 np.random.shuffle(indexes) for index_A in indexes: neighs = [n for n in neighbors(structure, 1, structure[index_A].pos)] index_B = get_index(structure, neighs[0][0]) if not same_kind(structure, index_A, index_B): min_dist = min_dist_AB /float(structure.scale) else: min_dist = min_dist_AA /float(structure.scale) if neighs[0][-1] < min_dist: move_by = min_dist - neighs[0][-1] if move_by*structure.scale > 0.02: succes = 0 relax_pair_cm(structure, index_A, index_B, 1.2*move_by, min_dist) move_to_cell(structure, index_A) move_to_cell(structure, index_B) if succes: return 1 return 0
def get_duplicates(massextr, te_diff=0.01): """ function to identify unique intersitials Parameters massextr = Pylada mass extraction object (directory with all intersitials directories) te_diff = difference in total energy among interstitials, default = 0.01 eV Returns dict = {key='foldername', total_energy, index} Note" index = all the foldername (intersitials) with same index are effectively considered equivalent Criteria for duplicates 1. Total_energy_difference <= 0.01 eV 2. Space_group 3. comparing first neighbor list for all the sites in the structure """ dict_E = massextr.total_energies dict_Str = massextr.structure dict2 = {} g = 0 for k in dict_E.keys(): g = g + 1 dict2[k] = [dict_E[k]] dict2[k].append(g) folder_list = [l for l in massextr] folder_combs = combinations(folder_list, 2) for i in folder_combs: diff_E = abs(dict2[i[0]][0] - dict2[i[1]][0]) str1 = dict_Str[i[0]] str2 = dict_Str[i[1]] spg1 = spglib.get_spacegroup(str1) spg2 = spglib.get_spacegroup(str2) ngh_list_1 = sorted( [len(neighbors(str1, 1, atom.pos, 0.2)) for atom in str1]) ngh_list_2 = sorted( [len(neighbors(str2, 1, atom.pos, 0.2)) for atom in str2]) if diff_E <= te_diff: if spg1 == spg2: if ngh_list_1 == ngh_list_2: dict2[i[1]][1] = dict2[i[0]][1] return (dict2)
def Dz(point, structure, k=1, d0=1.5): from pylada.crystal import neighbors neighs = [n for n in neighbors(structure, k, point)] d0 /= float(structure.scale) value = 0 for i in range(k): value += (abs(neighs[k-1][-1]-d0))**2 return value
def Dz(point, structure, k=1, d0=1.5): from pylada.crystal import neighbors neighs = [n for n in neighbors(structure, k, point)] d0 /= float(structure.scale) value = 0 for i in range(k): value += (abs(neighs[k - 1][-1] - d0))**2 return value
def test_symmetrized(): """ Tests that symmetrized clusters are determined correctly. """ from numpy import all, any from pylada.ce import Cluster from pylada.crystal import binary, neighbors lattice = binary.zinc_blende() lattice[0].type = ['Si', 'Ge'] lattice[1].type = ['Si', 'Ge'] a = Cluster(lattice) a.add_spin(lattice[0].pos) a.add_spin(lattice[0].pos + [0.0, -0.5, -0.5]) a._create_symmetrized() assert len(a._symmetrized) == 2 * 12 for i, (atom, vec, d) in enumerate(neighbors(lattice, 16, lattice[0].pos)): if i < 4: continue b = Cluster(lattice) b.add_spin(lattice[0].pos) b.add_spin(vec) assert any(all(b.spins == u) for u in a._symmetrized) for i, (atom, vec, d) in enumerate(neighbors(lattice, 16, lattice[1].pos)): if i < 4: continue b = Cluster(lattice) b.add_spin(lattice[1].pos) b.add_spin(vec) assert any(all(b.spins == u) for u in a._symmetrized) lattice = binary.zinc_blende() lattice[0].type = ['Si', 'Ge'] lattice[1].type = ['Si', 'Ge', 'C'] a = Cluster(lattice) a.add_spin(lattice[1].pos) a.add_spin(lattice[1].pos + [1.0, 0, 0]) a._create_symmetrized() assert len(a._symmetrized) == 6 for i, (atom, vec, d) in enumerate(neighbors(lattice, 24, lattice[0].pos)): if i < 16: continue b = Cluster(lattice) b.add_spin(lattice[1].pos) b.add_spin(vec) assert any(all(b.spins == u) for u in a._symmetrized)
def check_against_neighbors(structure, tolerance=1e-8): from bisect import bisect_right from pylada.crystal import coordination_shells, neighbors distances = [u[-1] for u in neighbors(structure, 150, [0, 0, 0], tolerance)] shells = coordination_shells(structure, 10, [0, 0, 0], tolerance) for j in range(8): i = bisect_right(distances, max([u[-1] for u in shells[j]])) assert i == sum([len(shell) for shell in shells[:j + 1]])
def sort_under_coord(bulk=None, slab=None): """Returns indices and th coordinations of the undercoordinated atoms in a slab created from the bulk :param bulk: pylada structure :param slab: pylada structure """ from pylada.crystal import neighbors # Check the coordination in the bulk first shell bulk_first_shell = [] for atom in bulk: bulk_first_shell.append( [atom.type, len(neighbors(structure=bulk, nmax=1, center=atom.pos, tolerance=1e-1 / bulk.scale))]) del atom maxz = max([x.pos[2] for x in slab]) minz = min([x.pos[2] for x in slab]) # Find the undercoordinated atoms in the slab under_coord = [] indices = [br for br in range(len(slab)) if slab[br].pos[ 2] <= minz + 4. / float(slab.scale) or maxz - 4. / float(slab.scale) <= slab[br].pos[2]] for i in indices: atom = slab[i] coordination = len(neighbors(structure=slab, nmax=1, center=atom.pos, tolerance=1e-1 / slab.scale)) # Find the equivalent bulk atom to compare the coordination with for j in range(len(bulk)): eq_site = float(atom.site - j) / float(len(bulk)) if abs(int(eq_site) - eq_site) < 1e-2: break # Comparing coordination with the "equivalent" bulk atom if coordination != bulk_first_shell[j][1]: under_coord.append([i, coordination]) del coordination # returns the list of undercoordinated atoms, # atom index in the slab, coordination return under_coord
def calc_correlation(structure, rad, **kwargs): scale = float(structure.scale) Natoms = len(structure) cell = structure.cell.T abc = [ v for v in structure.cell] ds = [np.dot(v, v)**0.5 for v in abc] Vol = np.linalg.det(cell) atom_types, N = get_types(structure) print atom_types, N types_pdf = kwargs.get("types_pdf", get_pdf_types(atom_types)) Npdfs = len(types_pdf) pdf_cut = kwargs.get("pdf_cut", Vol**(1/3.) * scale)/scale pdf_nbins = len(rad) cut2 = 6.25 / (scale**2) N_in_rcut = kwargs.get("N_in_rcut", int(1.2 *Natoms*4*np.pi*pdf_cut**3/(3*Vol))) pdfs = {} count = {} for type in types_pdf: [A, B] = re.findall('[A-Z][^A-Z]*', type) pdfs[A+B] = np.zeros(pdf_nbins) count[A+B] = 0 if 1: A_range = range(Natoms) for index_A in A_range: A = structure[index_A].type pos_A = structure[index_A].pos neighs = [n for n in neighbors(structure, N_in_rcut, pos_A)] for i, nB in enumerate(neighs): index_B = get_index(structure, nB[0]) if index_B > index_A: B = structure[index_B].type dist = nB[2]*scale for Nbin, d in enumerate(rad[1:]): if dist < d: pdf_width = rad[Nbin-1] - d break if Nbin < pdf_nbins: pdf_width weigth0 = 4 * np.pi * pdf_width**3 / Vol val = np.zeros(pdf_nbins) val[Nbin] = weigth0 pdfs[A+B] += val pdfs[B+A] += val for type in types_pdf: [A, B] = re.findall('[A-Z][^A-Z]*', type) pdfs[A+B] /= N[B] * N[A] for i in range(pdf_nbins): pdfs[A+B][i] /= (i + 1e-5)**2 return pdfs
def first_shell(structure, pos, tolerance=0.25): """ Iterates though first neighbor shell. """ from pylada.crystal import neighbors from copy import deepcopy struct = deepcopy(structure) for i, a in enumerate(struct): a.index = i neighs = [n for n in neighbors(struct, 12, pos)] d = neighs[0][2] return [n for n in neighs if abs(n[2] - d) < tolerance * d]
def analyze_voids(zipped_voids, structure): ''' Analysis voids''' centers = [] for value, point in zipped_voids: print '######', value * structure.scale centers.append(-value * structure.scale) neighs = [n for n in neighbors(structure, 7, point)] for n in neighs: print n[0].type, n[2] * structure.scale return centers
def get_all_interstitials(prim_structure, positions): """ function to return list of all interstitial sites using Voronoi.py Parameters prim = pylada primitive structure positions = positions (numpy array) to compute interstitials for Returns Inst_list = list of list, with inner list containing ['atom type', [x,y,z]] """ ints_list = [] for site_num in range(len(positions)): site = np.array([ positions[site_num][0], positions[site_num][1], positions[site_num][2] ]) site_ngh = neighbors(prim_structure, 13, site, 0.1) points = [site] ### creating list with site and its neighbors for i in range(len(site_ngh)): a = site + site_ngh[i][1] points.append(a) ### converting list to numpy array points = np.asarray(points) ### using tess object cntr to compute voronoi cntr = compute_voronoi(points) ### Voronoi vertices ### the first position in points is the site, therefore '0' v = get_vertices(0, cntr) for i in range(len(v)): ints_list.append(['B', v[i].tolist()]) ### Voronoi face centers f = get_facecentroid(0, cntr) for j in range(len(f)): ints_list.append(['C', f[j].tolist()]) ### Voronoi edge centers e = get_edgecenter(0, cntr) for k in range(len(e)): ints_list.append(['N', e[k].tolist()]) ### return list of list ['Atom type', [x,y,z]] return ints_list
def check(structure, center, tolerance=1e-8): from numpy import abs, sqrt, all from pylada.crystal import neighbors # check we get the neighbors of zinc-blende. neighs = neighbors(structure, 46, center, tolerance) assert len(neighs) == 46 for atom, diff, dist in neighs[:4]: assert abs(dist - sqrt(3.0) * 0.25) < tolerance assert all(abs(abs(diff) - 0.25) < tolerance) for atom, diff, dist in neighs[4: 16]: assert abs(dist - sqrt(2.0) * 0.5) < tolerance assert len([0 for u in diff if abs(u) < tolerance]) == 1 assert len([0 for u in diff if abs(abs(u) - 0.5) < tolerance]) == 2 for atom, diff, dist in neighs[16: 28]: assert abs(dist - sqrt(0.75 * 0.75 + 2. * 0.25 * 0.25)) < tolerance assert len([0 for u in diff if abs(abs(u) - 0.25) < tolerance]) == 2 assert len([0 for u in diff if abs(abs(u) - 0.75) < tolerance]) == 1 for atom, diff, dist in neighs[28:34]: assert abs(dist - 1.) < tolerance assert len([0 for u in diff if abs(u) < tolerance]) == 2 assert len([0 for u in diff if abs(abs(u) - 1.) < tolerance]) == 1 for atom, diff, dist in neighs[34:]: assert abs(dist - sqrt(2 * 0.75 * 0.75 + 0.25 * 0.25)) < tolerance assert len([0 for u in diff if abs(abs(u) - 0.75) < tolerance]) == 2 assert len([0 for u in diff if abs(abs(u) - 0.25) < tolerance]) == 1 # check input using position rather than atom. neighs2 = neighbors(structure, 36, center.pos, tolerance) for (a, b, c), (d, e, f) in zip(neighs, neighs2): assert a is d assert all(abs(b - e)) < tolerance assert abs(c - f) < tolerance # check neighbor completeness. assert len(neighbors(structure, 2, center, tolerance)) == 4 assert len(neighbors(structure, 4, center, tolerance)) == 4 assert len(neighbors(structure, 6, center, tolerance)) == 16
def get_nnb(s, i, mytol): nghs = neighbors(s, 20, s[i].pos, 0.001) shortest_dist = nghs[0][-1] shortest_type = nghs[0][0].type hlp = [] for ng in nghs: if ng[0].type != shortest_type: break if ng[-1] > shortest_dist * (1 + mytol): continue hlp.append(ng) return len(hlp)
def find_ngh_indices(atomind,structure): """ function to return ngh indices from given atomind """ nghs=neighbors(structure,1,structure[atomind],0.2) ngh_indices=[] for ii in range(len(structure)): if any([ all(structure[ii].pos==ngh[0].pos) for ngh in nghs]) and any([ structure[ii].type==ngh[0].type for ngh in nghs]): ngh_indices.append(ii) return ngh_indices
def ffirst_shell(structure, pos, tolerance): """ Function to iterate through the first neighbor shell Parameters structure = pylada structure object pos = position (numpy array) of site, around which neighbor shell need to be computed tolerance = for choosing neighbors whose distance from 'pos' is within first neighbor distance, d(1+tolerance) Returns list of neighbors, same format as pylada.crystal.neigbors """ struct = deepcopy(structure) for i, a in enumerate(struct): a.index = i neighs = [n for n in neighbors(struct, 12, pos)] d = neighs[0][2] return [n for n in neighs if abs(n[2] - d) < tolerance * d]
def void_hop_type(structure,seed): point, value = void.get_rnd_void_center(structure, seed=seed) neighs = [n for n in neighbors(structure, 12, point)] n0 = neighs[0] type0 = n0[0].type n_in = 0 n_o = 0 for n in neighs[1:]: type_tmp = n[0].type if (n[2] - n0[2])*structure.scale <0.4: if type_tmp != 'O': n_in +=1 if type_tmp == 'O': n_o +=1 for n in neighs: type = n[0].type if n_in < n_o: if type != 'O': dp = point - n[0].pos ddp = structure.scale*np.dot(dp,dp)**0.5 dp = impose_pbc(structure.cell, dp) ddp1 = structure.scale*np.dot(dp,dp)**0.5 print type, ddp, ddp1, structure.scale*n[2] n[0].pos = point break else: if type == 'O': dp = point - n[0].pos ddp = structure.scale*np.dot(dp,dp)**0.5 dp = impose_pbc(structure.cell, dp) ddp1 = structure.scale*np.dot(dp,dp)**0.5 print type, ddp, ddp1, structure.scale*n[2] n[0].pos = point break #print '######moved', n_in, n_o, type, index, n[2]*structure.scale return structure
def get_2shells(s, i, mytol): nghs = neighbors(s, 50, s[i].pos, 0.001) shortest_dist = nghs[0][-1] shortest_type = nghs[0][0].type hlp = [] for ii in range(len(nghs)): ng = nghs[ii] if ng[0].type != shortest_type: break if ng[-1] > shortest_dist * (1 + mytol): continue hlp.append(ng) shortest_dist = nghs[ii][-1] shortest_type = nghs[ii][0].type hlphlp = [] for jj in range(ii, len(nghs)): ng = nghs[jj] if ng[0].type != shortest_type: break if ng[-1] > shortest_dist * (1 + mytol): continue hlphlp.append(ng) return [hlp, hlphlp]
def check_bcc(): """ Check on BCC structure. """ from pylada.crystal import Structure, neighbors structure = Structure([[-0.5, 0.5, 0.5], [0.5, -0.5, 0.5], [0.5, 0.5, -0.5]])\ .add_atom(0, 0, 0, "Mo") print(neighbors(structure, 12, [0, 0, 0]))
def calc_Dx(point, structure): '''Calculates the distnace to the nearest atom''' neighs = [n for n in neighbors(structure, 3, point)] return -neighs[0][2]
def calc_pdfs(structure, template = False, index = -1, **kwargs): scale = float(structure.scale) Natoms = len(structure) cell = structure.cell.T abc = [ v for v in structure.cell] ds = [np.dot(v, v)**0.5 for v in abc] Vol = np.linalg.det(cell) atom_types, N = get_types(structure) print atom_types, N types_pdf = kwargs.get("types_pdf", get_pdf_types(atom_types)) Npdfs = len(types_pdf) pdf_width = kwargs.get("pdf_width", 0.05) smear_flag = kwargs.get("smear_flag", True) pdf_sigma = kwargs.get("pdf_sigma", pdf_width) pdf_nsigma = pdf_sigma/pdf_width pdf_nsmear = int(3*pdf_nsigma+0.5) pdf_cut = kwargs.get("pdf_cut", Vol**(1/3.) * scale)/scale pdf_nbins = 2 * int(pdf_cut * scale / (2* pdf_width) ) + 1 pdf_width = pdf_cut / pdf_nbins cut2 = 6.25 / (scale**2) N_in_rcut = kwargs.get("N_in_rcut", int(1.2 *Natoms*4*np.pi*pdf_cut**3/(3*Vol))) weight0 = 4 * np.pi * pdf_width**3 / Vol pdfs = {} count = {} for type in types_pdf: [A, B] = re.findall('[A-Z][^A-Z]*', type) pdfs[A+B] = np.zeros(pdf_nbins) count[A+B] = 0 if 1: if index >=0: A_range = [index] weight0 = Natoms * weight0 else: A_range = range(Natoms) weight0 = weight0 for index_A in A_range: A = structure[index_A].type pos_A = structure[index_A].pos neighs = [n for n in neighbors(structure, N_in_rcut, pos_A)] for i, nB in enumerate(neighs): index_B = get_index(structure, nB[0]) if index_B > index_A: B = structure[index_B].type Nbin = np.around(nB[2]/pdf_width) if Nbin < pdf_nbins: #val = gauss1D( 1., Nbin, pdf_nbins, 0, pdf_nsigma) if smear_flag: val = gauss1D( 1., Nbin, pdf_nbins, pdf_nsmear, pdf_nsigma) else: val = np.zeros(pdf_nbins) val[Nbin] = 1 pdfs[A+B] += val pdfs[B+A] += val for type in types_pdf: [A, B] = re.findall('[A-Z][^A-Z]*', type) pdfs[A+B] /= weight0 * N[B] * N[A] for i in range(pdf_nbins): pdfs[A+B][i] /= (i + 1e-5)**2 r = pdf_width * np.arange(pdf_nbins) * scale return pdfs, r
def void_hop(structure,seed): point, value = void.get_rnd_void_center(structure, seed=seed) neighs = [n for n in neighbors(structure, 1, point)] n[0].pos = point
import numpy as np d = json.load(open("data.json", 'r')) cord = {} coulomb = {"ZiZj/d": {}, "1/d": {}} for item in d: icsdstr = "{0:06d}".format(int(item["icsdnum"])) atoms = read.icsd_cif_a("/scratch/jyan/allcifs/icsd_%s.cif"%(icsdstr)) ngh = defaultdict(float) uniqueatom = defaultdict(int) coul1 = defaultdict(float) coul2 = defaultdict(float) for i, atom in enumerate(atoms): ngh_n = neighbors(structure=atoms,nmax=1,center=atom.pos,tolerance=0.05) uniqueatom[atom.type] += 1 ngh[atom.type] += len(ngh_n) Zi = getattr(pt, atom.type).atomic_number # loop over neighbors for j in range(len(ngh_n)): Zj = getattr(pt, ngh_n[j][0].type).atomic_number coul1[atom.type] += Zi * Zj / ngh_n[j][2] coul2[atom.type] += 1. / ngh_n[j][2] for atom in ngh.keys(): ngh[atom] /= float(uniqueatom[atom]) coul1[atom] /= float(uniqueatom[atom]) coul2[atom] /= float(uniqueatom[atom])