Example #1
0
def generate_uniform_grid(structure, size=3, min_distance_from_atoms=1.0):
    """
    Generates a grid of symmetry inequivalent interstitial
    positions with a specified minimum distance from the atoms of the
    sample. Especially intended for DFT simulations.

    :param bulk_structure: A structre data.
    :param int size: The number of steps in the three lattice directions.
                     Only equispaced grids are supported at the moment.
    :param float min_distance_from_atoms: Minimum distance between a
                                          interstitial position and the
                                          atoms of the lattice.
                                          Units are Angstrom.
    :returns: A list of symmetry inequivalent positions.
    :rtype: list
    """
    #bulk_structure=Structure.from_file(bulk_structure)
    bulk_structure = structure.copy()
    tolerance = 7

    #build uniform grid
    npoints = size**3
    x_ = np.linspace(0., 1., size, endpoint=False)
    y_ = np.linspace(0., 1., size, endpoint=False)
    z_ = np.linspace(0., 1., size, endpoint=False)

    uniform_grid = np.meshgrid(x_, y_, z_, indexing='ij')
    x, y, z = uniform_grid

    equiv = np.ones_like(x) * (npoints)

    SA = SpacegroupAnalyzer(bulk_structure)
    rot, tran = SA._get_symmetry()

    alt = True
    nb_cells = np.array([[-1., -1., -1.], [-1., -1., 0.], [-1., -1., 1.],
                         [-1., 0., -1.], [-1., 0., 0.], [-1., 0., 1.],
                         [-1., 1., -1.], [-1., 1., 0.], [-1., 1., 1.],
                         [0., -1., -1.], [0., -1., 0.], [0., -1., 1.],
                         [0., 0., -1.], [0., 0., 0.], [0., 0., 1.],
                         [0., 1., -1.], [0., 1., 0.], [0., 1., 1.],
                         [1., -1., -1.], [1., -1., 0.], [1., -1., 1.],
                         [1., 0., -1.], [1., 0., 0.], [1., 0., 1.],
                         [1., 1., -1.], [1., 1., 0.], [1., 1., 1.]])

    for i in range(size):
        for j in range(size):
            for k in range(size):

                if equiv[i, j, k] < npoints:
                    #this point is equivalent to someone else!
                    continue

                for r, t in zip(rot, tran):
                    # new position for the muon
                    n = np.zeros(3)
                    # apply symmetry and bring back to unit cell
                    n = np.round(
                        np.dot(r, [x[i, j, k], y[i, j, k], z[i, j, k]]) + t,
                        decimals=tolerance) % 1
                    if (np.abs(n * size - np.rint(n * size)) < 10**
                            -(tolerance)).all():

                        #get index of point
                        ii, jj, kk = np.rint(n * size).astype(int)
                        if (ii * (size**2) + jj * size + kk > i *
                            (size**2) + j * size + k):
                            equiv[ii, jj, kk] -= 1
                            equiv[i, j, k] += 1

    reduced_lat = np.array(bulk_structure.lattice)
    scaled_pos = bulk_structure.frac_coords

    A, B, C = structure.lattice.abc[0], structure.lattice.abc[
        1], structure.lattice.abc[2]
    alpha, beta, gamma = structure.lattice.angles[0], structure.lattice.angles[
        1], structure.lattice.angles[2]

    reduced_bases = get_cell_matrix(A, B, C, alpha, beta, gamma)

    positions = []
    for i in range(size):
        for j in range(size):
            for k in range(size):
                if equiv[i, j, k] >= npoints:
                    #saves distances with all atoms
                    distances = []
                    dists = np.zeros(27 * len(bulk_structure.sites))
                    center = [x[i, j, k], y[i, j, k], z[i, j, k]]

                    #check distances form atoms (also in neighbouring cells)

                    for a in range(len(bulk_structure)):
                        dists[a * 27:(a + 1) * 27] = np.linalg.norm(np.dot(
                            scaled_pos[a] - center + nb_cells, reduced_bases),
                                                                    axis=1)
                    #
                    # old method 20 times slower!
                    #
                    #for a in range(len(sample._cell)):
                    #    for ii in (-1, 0, 1):
                    #        for jj in (-1, 0, 1):
                    #            for kk in (-1, 0, 1):
                    #                distances.append( np.linalg.norm(
                    #                        np.dot(scaled_pos[a] - center + np.array([ii,jj,kk]),
                    #                            reduced_bases) ) )
                    #
                    #print(np.allclose(dists,distances))
                    #if min(distances) > min_distance_from_atoms:

                    if dists.min() > min_distance_from_atoms:
                        positions.append([x[i, j, k], y[i, j, k], z[i, j, k]])

    return positions