def create(self, elems, mean, sigma, layer_height=0.1, start_height=1.0, max_height=3.5): self.elems = elems self.n = len(elems) self.atoms = np.zeros((self.n, 3)) # surface part # enlarge surface to avoid drop off surfabak = np.array(self.surf.atoms) surfebak = np.array(self.surf.elems) atomsu = np.zeros((surfabak.shape[0] * 18, 3)) elemsu = [] cellmat = to_cellmat(self.surf.cell) cellmat[2, 2] = self.surf.cellz for ix, x in enumerate([-1, 0, 1]): for iy, y in enumerate([-1, 0, 1]): for iz, z in enumerate([-1, 0]): ik = ix * 6 + iy * 2 + iz surfadd = np.array( [x * cellmat[0] + y * cellmat[1] + z * cellmat[2]]) atomsu[ik * surfabak.shape[0]:(ik + 1) * surfabak.shape[0]] = \ surfabak + surfadd elemsu += list(surfebak) elemsu = np.array(elemsu) zmax = self.surf.atoms.max(axis=0)[2] xs_core = [ ia for ia, a in enumerate(self.surf.atoms) if a[2] >= zmax - layer_height ] xs_other = [ia for ia, a in enumerate(atomsu) if ia not in xs_core] soelems, soatoms = elemsu[xs_other], atomsu[xs_other] suelems, suatoms = self.surf.elems[xs_core], self.surf.atoms[xs_core] cura = 0 # current atom while cura != self.n: if cura == 0: qua, exr = True, None else: qua, exr = False, self.ext_range xatom = self.add_atom_f(self.elems[cura], self.elems, cura, mean, sigma, suelems, \ suatoms, soelems=soelems, soatoms=soatoms, atst=[cellmat, zmax, start_height, \ max_height], quarter=qua, ext_range=exr) xatom = self.image_ex(xatom, self.surf.cell) self.atoms[cura] = np.array(xatom) agatom, agelem = self.image_cr(xatom, self.elems[cura], self.surf.cell) soatoms = np.array(list(soatoms) + list(agatom)) soelems = np.array(list(soelems) + list(agelem)) cura += 1 return self
def __init__(self, clu): self.clu = clu self.finalfactor = 1.2 super(ConnectivityCheck, self).__init__(clu.n) self.sur = [] if isinstance(clu, ClusterAtSurface): cellmat = to_cellmat(clu.surf.cell) for ix in [-1, 0, 1]: for iy in [-1, 0, 1]: self.sur.append(ix * cellmat[0] + iy * cellmat[1]) else: self.sur.append(np.array([0.0, 0.0, 0.0]))
def surface_match(a, b, dmax, dmin=1E-2, nd=20): from cluster.base import elem_num assert isinstance(a, Surface) assert isinstance(b, Surface) adir = to_direct(a.atoms, a.cell) bdir = to_direct(b.atoms, a.cell) if len(a.elems) == len(b.elems): # efficient way assert (a.elems == b.elems).all() cellmat = to_cellmat(a.cell) eles, ne = elem_num(a.elems) ww = weights(a.atoms, b.atoms, ne, eles, cellmat) v, vx = kmc_comp(ww, ne, eles) vg = np.zeros(vx.shape, dtype=int) for i, ii in enumerate(vx): vg[ii - 1] = i + 1 return v, vg else: # old way cl = np.linalg.norm(to_cellmat(a.cell), axis=1) dstep = (dmax / dmin)**(1.0 / nd) d = dmin mt = [-1] * a.n mx = range(b.n) cell = [1.0, 1.0, None] for _ in range(nd): for i in range(a.n): if mt[i] == -1: for ij, j in enumerate(mx): if check_imagex(adir[i], a.elems[i], bdir[j], b.elems[j], cell, d, cl): mt[i] = j del mx[ij] break if len(mx) == 0: break d *= dstep # diff, march indices return d, np.array(mt) + 1
def __init__(self, clu, ffactor): self.clu = clu self.finalfactor = ffactor self.steplength = 0.05 self.traj = [] self.maxiter = 1000 self.sur = [] if isinstance(clu, ClusterAtSurface): cellmat = to_cellmat(clu.surf.cell) for ix in [-1, 0, 1]: for iy in [-1, 0, 1]: if ix == 0 and iy == 0: continue self.sur.append(ix * cellmat[0] + iy * cellmat[1])
def build_surf_ref(self): cax = self.clus surf = cax[0].surf # align atoms eles, ne = elem_num(cax[0].elems) cellmat = to_cellmat(surf.cell) for i in [0, -1]: _, b = surface_compare(cax[i], self.ref_structs[i], ne, eles, 0.5) self.adjust_surf(cax[i], self.ref_structs[i], b) mdiff, _ = km_comp(cax[0].atoms, cax[-1].atoms, ne, eles, cellmat) lscax = [cax] nmax = self.nimages + 1 assert (len(self.ref_structs) == nmax + 1) uimgs = [] mishort = 0 for cax in lscax: imgs = [] for i in range(0, nmax + 1): if i == 0: cc = cax[0] elif i == nmax: cc = cax[1] else: cc = self.ref_structs[i] cc = copy.deepcopy(cc) cc.surf.forces = None if cc.mag is None: cc.mag = 0.0 cc.label = "CONN-%d-%d:%%d.%d:%.2f" % (self.endp[0], self.endp[1], i, cc.mag) imgs.append(cc) uimgs.append(imgs) return uimgs, [ self.endp[0], self.endp[1], len(uimgs), 1, mishort, 0, 0, mdiff ]
def surface_align(cc, deep=True): assert isinstance(cc, ClusterAtSurface) if cc.n == 0: return # part 1: put cluster fragments together (using supercell periodicity) # atoms having a distance less than 1/3 cell are considered connected # method: using disjointset finding all fragments # move all other atoms closer to the center of the biggest fragment if cc.n > 1: cdir = to_direct(cc.atoms, cc.surf.cell) disj = DisjointSet(len(cdir)) for ii, i in enumerate(cdir): for jj, j in enumerate(cdir[:ii]): if np.linalg.norm(i - j) < 1.0 / 3.0: disj.union(ii, jj) roots, rootn = [], [] for ii, i in enumerate(cdir): ik = disj.find(ii) if ik not in roots: roots.append(ik) rootn.append(1) else: rootn[roots.index(disj.find(ik))] += 1 rootm = max(zip(roots, rootn), key=lambda x: x[1]) rootc = np.zeros(3) for ii, i in enumerate(cdir): if disj.find(ii) == rootm[0]: rootc += i rootc /= rootm[1] for ii, i in enumerate(cdir): if disj.find(ii) == rootm[0]: continue for k in range(2): cdir[ii, k] += num_adjust(rootc[k] - cdir[ii, k], 1.0) cc.atoms = to_cartesian(cdir, cc.surf.cell) # part 2: put cluster closer to center of surface (using unitcell periodicity) # can make the surface atoms indeices disordered surf = cc.surf if deep: ucellmat = to_cellmat(surf.unit_cell) ucell = np.diag(to_direct(ucellmat, surf.cell)) cdir = to_direct(cc.atoms, surf.cell) # space group transition reference atoms must be taken from space_group_ref ctrref = surf.space_group_ref scent = np.array([0.50, 0.50]) cxdmin = [] sg = SymOpsHall[surf.space_group] sg = [[x.strip() for x in s] for s in sg if s[2].strip() == 'z'] for s in sg: cx = np.array(cdir) cx[:, 0:2] -= ctrref cx = apply_trans(cx, s, ucell) cx[:, 0:2] += ctrref # for unit-cell periodicity, cluster must be moved as a whole # for supercell periodicity, atoms can be moved singlely and dim by dim ccent = cx.mean(axis=0) for k in range(2): ccent[k] += num_adjust(scent[k] - ccent[k], ucell[k]) cxd = np.linalg.norm(scent[0:2] - ccent[0:2]) cxcar = to_cartesian(cx - cx.mean(axis=0) + ccent, surf.cell) if len(cxdmin) == 0 or cxd < cxdmin[0]: cxdmin = [cxd, cxcar, s, -cx.mean(axis=0) + ccent] cc.atoms = cxdmin[1] # part 3: move surface atoms accodingly and also make surface boundary atoms better csdir = to_direct(surf.atoms, surf.cell) if deep: csdir[:, 0:2] -= ctrref csdir = apply_trans(csdir, cxdmin[2], ucell) csdir[:, 0:2] += ctrref csdir += cxdmin[3] for ii, _ in enumerate(csdir): for k in range(2): csdir[ii, k] += num_adjust(TOLS - csdir[ii, k], 1.0) surf.atoms = to_cartesian(csdir, surf.cell)
def surface_compare(a, b, ne, eles, _, best=None): assert isinstance(a, ClusterAtSurface) assert isinstance(b, ClusterAtSurface) ufactor = 0.3 adir = to_direct(a.atoms, a.surf.cell) bdir = to_direct(b.atoms, b.surf.cell) bcent = bdir.mean(axis=0) bxcar = b.atoms surf = a.surf cellmat = to_cellmat(surf.cell) ucellmat = to_cellmat(surf.unit_cell) ucell = np.diag(to_direct(ucellmat, surf.cell)) # space group transition reference atoms must be taken from space_group_ref atrref = surf.space_group_ref xvmins = [] sg = SymOpsHall[surf.space_group] sg = [[x.strip() for x in s] for s in sg if s[2].strip() == 'z'] for s in sg: ax = np.array(adir) ax[:, 0:2] -= atrref ax = apply_trans(ax, s, ucell) ax[:, 0:2] += atrref # for unit-cell periodicity, cluster must be moved as a whole # for supercell periodicity, atoms can be moved singlely and dim by dim acentpre = ax.mean(axis=0) dss = [[0.0], [0.0]] for k in range(2): acentpre[k] += num_adjust(bcent[k] - acentpre[k], ucell[k]) if bcent[k] > acentpre[k] + ufactor * ucell[k]: dss[k].append(1.0) elif bcent[k] < acentpre[k] - ufactor * ucell[k]: dss[k].append(-1.0) for dx in dss[0]: for dy in dss[1]: dxy = np.array([dx * ucell[0], dy * ucell[1], 0.0]) acent = acentpre + dxy axcar = to_cartesian(ax - ax.mean(axis=0) + acent, surf.cell) ww = weights(axcar, bxcar, ne, eles, cellmat) if best is None: v, vx = kmc_comp(ww, ne, eles) opmin = [s, vx, -ax.mean(axis=0) + acent, axcar] xvmins.append([v, opmin]) else: v, vx = kmc_comp_best(best, ww, ne, eles) for ij in range(best): opmin = [s, vx[:, ij], -ax.mean(axis=0) + acent, axcar] xvmins.append([v[ij], opmin]) xvmins.sort(key=lambda x: x[0]) if best is not None: xvmins = xvmins[:best] else: xvmins = xvmins[:1] for v, opmin in xvmins: if opmin is not None: asdir = to_direct(a.surf.atoms, surf.cell) asdir[:, 0:2] -= atrref asdir = apply_trans(asdir, opmin[0], ucell) asdir[:, 0:2] += atrref asdir += opmin[2] for ii, _ in enumerate(asdir): for k in range(2): asdir[ii, k] += num_adjust(TOLS - asdir[ii, k], 1.0) ascar = to_cartesian(asdir, surf.cell) opmin.append(ascar) if best is not None: return xvmins else: return xvmins[0]
def interp_surf(self, ibest=None): if ibest is None: ibest = self.max_trials_surf cax = self.clus surf = cax[0].surf # align atoms eles, ne = elem_num(cax[0].elems) cellmat = to_cellmat(surf.cell) lscax = [] mdiff = None mdmax = None misinglelong = 0 if len(cax) == 2: bs = surface_compare(cax[0], cax[1], ne, eles, self.max_diff + 0.1, best=ibest) for md, b in bs: if len(lscax) != 0 and (md > self.max_diff or md > mdiff * 1.5): break cxx = copy.deepcopy(cax) self.adjust_surf(cxx[0], cxx[1], b) mdx = np.linalg.norm(cxx[0].atoms - cxx[1].atoms, axis=1).max() if len(lscax) != 0 and (mdx > self.max_diff * 4): misinglelong += 1 break lscax.append(cxx) if mdiff is None or md < mdiff: mdiff = md if mdmax is None or mdx < mdmax: mdmax = mdx else: for cai in range(len(cax) - 1, 0, -1): _, b = surface_compare(cax[cai - 1], cax[cai], ne, eles, self.max_diff + 0.1) self.adjust_surf(cax[cai - 1], cax[cai], b) mdiff, _ = km_comp(cax[0].atoms, cax[-1].atoms, ne, eles, cellmat) lscax.append(cax) # construct final images nmax = self.nimages + 1 dc = 1.0 * nmax / (len(cax) - 1) for cax in lscax: for cai in range(0, len(cax)): cax[cai].idx = cai * dc uimgs = [] mishort = 0 for cax in lscax: imgs = [] ishort = False for i in range(0, nmax + 1): cc = ClusterAtSurface(cax[0].n, copy.deepcopy(cax[0].surf)) cc.elems = cax[0].elems for cai in range(1, len(cax)): if cax[cai].idx >= i - 1E-8: break cx, cy = cax[cai - 1], cax[cai] rx = (cax[cai].idx - i) / dc cc.atoms = cx.atoms * rx + cy.atoms * (1 - rx) cc.surf.atoms = cx.surf.atoms * rx + cy.surf.atoms * (1 - rx) cc.mag = None if np.abs(rx - 1) < 1E-8: cc.mag, cc.energy = cx.mag, cx.energy cc.surf.forces = cx.surf.forces elif np.abs(rx) < 1E-8: cc.mag, cc.energy = cy.mag, cy.energy cc.surf.forces = cy.surf.forces else: cc.surf.forces = None if cc.mag is None: cc.mag = 0.0 cc.label = "CONN-%d-%d:%%d.%d:%.2f" % (self.endp[0], self.endp[1], i, cc.mag) if i != 0 and i != nmax: pcc = PreOptimization(cc, self.adjusted_short_length_factor) pcc.maxiter = 12 lpp = pcc.opt() if not lpp: ishort = True break cc = pcc.traj[-1] imgs.append(cc) if not ishort: uimgs.append(imgs) else: mishort += 1 return uimgs, [ self.endp[0], self.endp[1], len(uimgs), ibest, mishort, misinglelong, 0, mdiff ]