def periodicVoronoiCell(a=1.3968418, L=numpy.array([10, 10]), N=10, cType='tr', **kwargs): """ Returns a cell of size L with N grains made according to the voronoi constructions. The positions and orientations of the grains are random. """ # Get origins if 'x0' not in kwargs: x0, y0 = ptsInBox(N, L) else: x0 = kwargs['x0'] y0 = kwargs['y0'] N = len(x0) # Get a list of axes ax = kwargs.get('axes', randomAxes(N)) z0 = numpy.zeros(N) # Repeat for double periodic geometry ax = ax * 9 # Repeat 9 times x, y, z = [], [], [] for i in [0, -1, 1]: for j in [0, -1, 1]: x.extend(x0 + L[0] * i) y.extend(y0 + L[1] * j) z.extend(z0) x, y, z = numpy.array(x), numpy.array(y), numpy.array(z) orgs = numpy.concatenate( (x.reshape(N * 9, 1), y.reshape(N * 9, 1), z.reshape(N * 9, 1)), axis=1) # Get the number of repeats needed for the crystals D = numpy.linalg.norm(L) * 3 # Diagonal of the periodic box r = (int(D / a) + 2) * 2 # repeats rc = rotatedCrystal(numpy.eye(3), size=(r, r, 1), a=a, cType=cType) rc.positions[:, 2] = 0 orgPos = rc.positions # ---- Find the grain boundaries with a voronoi tesselation # ---- we do this to mark the atoms near the grain boundary. centers = numpy.zeros((len(x), 2)) centers[:, 0], centers[:, 1] = x, y vor = Voronoi(centers) edges = vorEdges(vor, D) # positions = [] # positions of the atoms in the cell bulkPositions = numpy.ndarray( shape=(0, 3)) # positions of the bulk atoms in the cell gbPositions = numpy.ndarray( shape=(0, 3)) # positions of the grain boundary atoms in the cell unRatPositions = numpy.ndarray( shape=(0, 3)) # positions of the unrattlable atoms in the cell padPositions = numpy.ndarray( shape=(0, 3)) # positions of the pad atoms in the cell tags = numpy.ndarray( shape=(0, 1) ) # tags of the atoms 0 = bulk, 1 = grain boundary, 2 = pad region, 3 = unrattlable gn = 0 overlap = kwargs.get('overlap', 0.5) for v, x1, y1, z1 in zip(ax, x0, y0, z0): gn += 1 p0 = numpy.array([x1, y1, z1]) p = copy.copy(orgPos) p = numpy.dot(v, p.T).T p += p0 # Center the crystal at the center of the grain # Choose all in the big box p = p[numpy.logical_and(*[ numpy.logical_and(p[:, i] > -L[i], p[:, i] <= 2 * L[i]) for i in range(2) ])] # Find the grain number to which we are closest # allDist = cdist(p, orgs, 'euclidean') # gNum = numpy.argmin(allDist,axis=1) if overlap == 0: # Find gNum with c routine gNum = numpy.zeros(len(p)) _cPolyUtils.closest(p[:, 0], p[:, 1], orgs[:, 0], orgs[:, 1], gNum) gNum = gNum.astype(int) p = p[gNum == gn - 1] # Choose all that belong to the grain else: # finite overlap means we need to use voronoi edges to determine which points lie within an overlap region # voronoi region associated with the point reg = vor.regions[vor.point_region[ gn - 1]] # region associated with the point if -1 in reg: raise Exception('Open region associated with generator') nVerts = len(reg) # number of verticies in the region p = pointsInRegion(gn - 1, vor, p, overlap=overlap) # Correct for periodic boundaries for i in range(2): p[p[:, i] < 0, i] += L[i] p[p[:, i] >= L[i], i] -= L[i] # Find a strip close to the grain boundary t = numpy.zeros(len(p)).reshape((len(p), 1)) # Default tag bulk bWidth = kwargs.get('bWidth', 10.0) pad = kwargs.get('pad', 5.0) ratPad = kwargs.get('ratPad', 2.0) for edge in edges: # for edge in pt2edge[(x1,y1,z1)]: p1, p2, slope = [numpy.ndarray((1, 3)) * 0 for i in range(3)] p1[:, :2], p2[:, :2], slope[:, :2] = edge['p1'], edge['p2'], edge[ 't'] # The library sometimes returns nan due to the 2d nature of the problem. Reset it. slope[:, 2] = 0 p1[:, 2] = 0 p2[:, 2] = 0 proj1, proj2 = numpy.dot(p - p1, slope.T), numpy.dot(p - p2, slope.T) distToEdge = (numpy.sum(((p1 - p) + proj1 * slope)**2.0, axis=1)**0.5).reshape((len(p), 1)) # tag pad region t[numpy.logical_and( numpy.logical_and(t != 1, t != 3), numpy.logical_and( numpy.logical_and(distToEdge < bWidth + pad, distToEdge >= bWidth), proj1 * proj2 < 0))] = 2 # tag unrattlable t[numpy.logical_and( t != 1, numpy.logical_and( numpy.logical_and(distToEdge < bWidth, distToEdge > bWidth - ratPad), proj1 * proj2 < 0))] = 3 # tag grain boundary t[numpy.logical_and(distToEdge <= bWidth - ratPad, proj1 * proj2 < 0)] = 1 bulkPositions = numpy.concatenate((bulkPositions, p[(t == 0)[:, 0], :])) padPositions = numpy.concatenate((padPositions, p[(t == 2)[:, 0], :])) unRatPositions = numpy.concatenate((unRatPositions, p[(t == 3)[:, 0], :])) # Filter out 'close' atoms in grain boundary cutoff = kwargs.get('cutoff', 0.2) thisGB = p[(t == 1)[:, 0], :] if len(gbPositions) > 0: # gbDist = cdist(thisGB,gbPositions) # minDist = numpy.min(gbDist,axis=1) # Find minDist with c routine minInd = numpy.zeros(len(thisGB)).astype('int') _cPolyUtils.closest(thisGB[:, 0], thisGB[:, 1], gbPositions[:, 0], gbPositions[:, 1], minInd) minDist = numpy.sum((thisGB - gbPositions[minInd])**2.0, axis=1)**0.5 thisGB = numpy.delete(thisGB, numpy.where(minDist < a * cutoff), axis=0) gbPositions = numpy.concatenate((gbPositions, thisGB)) # Filter out close atoms in the gb again; possible to have anamolies due to multiple grain overlap cutoff = kwargs.get('cutoff', 0.2) if len(gbPositions) > 0: closeAtoms = True while closeAtoms: # Find minDist with c routine minInd = numpy.zeros(len(gbPositions) - 1) minDist = numpy.zeros(len(gbPositions) - 1) _cPolyUtils.selfClosest(L[0], L[1], gbPositions[:, 0], gbPositions[:, 1], minInd, minDist) gbPositions = numpy.delete(gbPositions, numpy.where(minDist < cutoff * a), axis=0) minInd = numpy.zeros(len(gbPositions) - 1) minDist = numpy.zeros(len(gbPositions) - 1) _cPolyUtils.selfClosest(L[0], L[1], gbPositions[:, 0], gbPositions[:, 1], minInd, minDist) if minDist.min() >= cutoff * a: closeAtoms = False # collect all positions positions = numpy.concatenate((bulkPositions, unRatPositions)) positions = numpy.concatenate((positions, gbPositions)) positions = numpy.concatenate((positions, padPositions)) tags = numpy.zeros(len(positions)) tags[-len(unRatPositions) - len(gbPositions) - len(padPositions):] = 3 tags[-len(gbPositions) - len(padPositions):] = 1 tags[-len(padPositions):] = 2 # Set the gbPositions as rattlable, and then merge the unRat and gb rattlable = numpy.zeros(len(tags)) rattlable[tags == 1] = 1 tags[tags == 3] = 1 cell = numpy.diag([L[0], L[1], 10]) # cr = Atoms(symbols=symbols, positions=positions, cell = cell, pbc=[True,True,True]) numbers = numpy.ones(len(positions)) * 6 symbols = ['C'] * len(positions) cr = Atoms(numbers=numbers, positions=positions, cell=cell, tags=tags, pbc=[True, True, True]) cr.ax = ax cr.centers = centers cr.orgs = orgs cr.rattlable = rattlable if sum(tags == 0) == 0 or sum(tags == 1) == 0 or sum(tags == 2) == 0: print "It seems that either the cell is too small or there are too many grains. You might want to fix this" return cr
def periodicVoronoiCell(a=1.3968418, L=numpy.array([10, 10]), N=10, cType='tr', **kwargs): """ Returns a cell of size L with N grains made according to the voronoi constructions. The positions and orientations of the grains are random. """ # Get origins if 'x0' not in kwargs: x0, y0 = ptsInBox(N, L) else: x0 = kwargs['x0'] y0 = kwargs['y0'] N = len(x0) # Get a list of axes ax = kwargs.get('axes', randomAxes(N)) z0 = numpy.zeros(N) # Repeat for double periodic geometry ax = ax * 9 # Repeat 9 times x, y, z = [], [], [] for i in [0, -1, 1]: for j in [0, -1, 1]: x.extend(x0 + L[0] * i) y.extend(y0 + L[1] * j) z.extend(z0) x, y, z = numpy.array(x), numpy.array(y), numpy.array(z) orgs = numpy.concatenate((x.reshape(N * 9, 1), y.reshape(N * 9, 1), z.reshape(N * 9, 1)), axis=1) # Get the number of repeats needed for the crystals D = numpy.linalg.norm(L) * 3 # Diagonal of the periodic box r = (int(D / a) + 2) * 2 # repeats rc = rotatedCrystal(numpy.eye(3), size=(r, r, 1), a=a, cType=cType) rc.positions[:, 2] = 0 orgPos = rc.positions # ---- Find the grain boundaries with a voronoi tesselation # ---- we do this to mark the atoms near the grain boundary. centers = numpy.zeros((len(x), 2)) centers[:, 0], centers[:, 1] = x, y vor = Voronoi(centers) edges = vorEdges(vor, D) # positions = [] # positions of the atoms in the cell bulkPositions = numpy.ndarray(shape=(0, 3)) # positions of the bulk atoms in the cell gbPositions = numpy.ndarray(shape=(0, 3)) # positions of the grain boundary atoms in the cell unRatPositions = numpy.ndarray(shape=(0, 3)) # positions of the unrattlable atoms in the cell padPositions = numpy.ndarray(shape=(0, 3)) # positions of the pad atoms in the cell tags = numpy.ndarray( shape=(0, 1)) # tags of the atoms 0 = bulk, 1 = grain boundary, 2 = pad region, 3 = unrattlable gn = 0 overlap = kwargs.get('overlap', 0.5) for v, x1, y1, z1 in zip(ax, x0, y0, z0): gn += 1 p0 = numpy.array([x1, y1, z1]) p = copy.copy(orgPos) p = numpy.dot(v, p.T).T p += p0 # Center the crystal at the center of the grain # Choose all in the big box p = p[numpy.logical_and(*[numpy.logical_and(p[:, i] > -L[i], p[:, i] <= 2 * L[i]) for i in range(2)])] # Find the grain number to which we are closest # allDist = cdist(p, orgs, 'euclidean') # gNum = numpy.argmin(allDist,axis=1) if overlap == 0: # Find gNum with c routine gNum = numpy.zeros(len(p)) _cPolyUtils.closest(p[:, 0], p[:, 1], orgs[:, 0], orgs[:, 1], gNum) gNum = gNum.astype(int) p = p[gNum == gn - 1] # Choose all that belong to the grain else: # finite overlap means we need to use voronoi edges to determine which points lie within an overlap region # voronoi region associated with the point reg = vor.regions[vor.point_region[gn - 1]] # region associated with the point if -1 in reg: raise Exception('Open region associated with generator') nVerts = len(reg) # number of verticies in the region p = pointsInRegion(gn - 1, vor, p, overlap=overlap) # Correct for periodic boundaries for i in range(2): p[p[:, i] < 0, i] += L[i] p[p[:, i] >= L[i], i] -= L[i] # Find a strip close to the grain boundary t = numpy.zeros(len(p)).reshape((len(p), 1)) # Default tag bulk bWidth = kwargs.get('bWidth', 10.0) pad = kwargs.get('pad', 5.0) ratPad = kwargs.get('ratPad', 2.0) for edge in edges: # for edge in pt2edge[(x1,y1,z1)]: p1, p2, slope = [numpy.ndarray((1, 3)) * 0 for i in range(3)] p1[:, :2], p2[:, :2], slope[:, :2] = edge['p1'], edge['p2'], edge['t'] # The library sometimes returns nan due to the 2d nature of the problem. Reset it. slope[:, 2] = 0 p1[:, 2] = 0 p2[:, 2] = 0 proj1, proj2 = numpy.dot(p - p1, slope.T), numpy.dot(p - p2, slope.T) distToEdge = (numpy.sum(((p1 - p) + proj1 * slope) ** 2.0, axis=1) ** 0.5).reshape((len(p), 1)) # tag pad region t[numpy.logical_and(numpy.logical_and(t != 1, t != 3), numpy.logical_and(numpy.logical_and(distToEdge < bWidth + pad, distToEdge >= bWidth), proj1 * proj2 < 0))] = 2 # tag unrattlable t[numpy.logical_and(t != 1, numpy.logical_and(numpy.logical_and(distToEdge < bWidth, distToEdge > bWidth - ratPad), proj1 * proj2 < 0))] = 3 # tag grain boundary t[numpy.logical_and(distToEdge <= bWidth - ratPad, proj1 * proj2 < 0)] = 1 bulkPositions = numpy.concatenate((bulkPositions, p[(t == 0)[:, 0], :])) padPositions = numpy.concatenate((padPositions, p[(t == 2)[:, 0], :])) unRatPositions = numpy.concatenate((unRatPositions, p[(t == 3)[:, 0], :])) # Filter out 'close' atoms in grain boundary cutoff = kwargs.get('cutoff', 0.2) thisGB = p[(t == 1)[:, 0], :] if len(gbPositions) > 0: # gbDist = cdist(thisGB,gbPositions) # minDist = numpy.min(gbDist,axis=1) # Find minDist with c routine minInd = numpy.zeros(len(thisGB)).astype('int') _cPolyUtils.closest(thisGB[:, 0], thisGB[:, 1], gbPositions[:, 0], gbPositions[:, 1], minInd) minDist = numpy.sum((thisGB - gbPositions[minInd]) ** 2.0, axis=1) ** 0.5 thisGB = numpy.delete(thisGB, numpy.where(minDist < a * cutoff), axis=0) gbPositions = numpy.concatenate((gbPositions, thisGB)) # Filter out close atoms in the gb again; possible to have anamolies due to multiple grain overlap cutoff = kwargs.get('cutoff', 0.2) if len(gbPositions) > 0: closeAtoms = True while closeAtoms: # Find minDist with c routine minInd = numpy.zeros(len(gbPositions) - 1) minDist = numpy.zeros(len(gbPositions) - 1) _cPolyUtils.selfClosest(L[0], L[1], gbPositions[:, 0], gbPositions[:, 1], minInd, minDist) gbPositions = numpy.delete(gbPositions, numpy.where(minDist < cutoff * a), axis=0) minInd = numpy.zeros(len(gbPositions) - 1) minDist = numpy.zeros(len(gbPositions) - 1) _cPolyUtils.selfClosest(L[0], L[1], gbPositions[:, 0], gbPositions[:, 1], minInd, minDist) if minDist.min() >= cutoff * a: closeAtoms = False # collect all positions positions = numpy.concatenate((bulkPositions, unRatPositions)) positions = numpy.concatenate((positions, gbPositions)) positions = numpy.concatenate((positions, padPositions)) tags = numpy.zeros(len(positions)) tags[-len(unRatPositions) - len(gbPositions) - len(padPositions):] = 3 tags[-len(gbPositions) - len(padPositions):] = 1 tags[-len(padPositions):] = 2 # Set the gbPositions as rattlable, and then merge the unRat and gb rattlable = numpy.zeros(len(tags)) rattlable[tags == 1] = 1 tags[tags == 3] = 1 cell = numpy.diag([L[0], L[1], 10]) # cr = Atoms(symbols=symbols, positions=positions, cell = cell, pbc=[True,True,True]) numbers = numpy.ones(len(positions)) * 6 symbols = ['C'] * len(positions) cr = Atoms(numbers=numbers, positions=positions, cell=cell, tags=tags, pbc=[True, True, True]) cr.ax = ax cr.centers = centers cr.orgs = orgs cr.rattlable = rattlable if sum(tags == 0) == 0 or sum(tags == 1) == 0 or sum(tags == 2) == 0: print "It seems that either the cell is too small or there are too many grains. You might want to fix this" return cr