예제 #1
0
def len_supercell(atoms):
    tags = atoms.get_tags()
    if not tags.any():
        logger.warning(
            'atoms object appears to have only 1 layer or tags are missing')

    bottom_layer = atoms[tags == 0]
    x_tags, x_positions = get_layers(bottom_layer, (1, 0, 0), 0.3)
    x = len(x_positions)

    y_tags, y_positions = get_layers(bottom_layer, (0, 1, 0), 0.3)
    y = len(y_positions)

    z_tags, z_positions = get_layers(atoms, (0, 0, 1), 0.3)
    z = len(z_positions)
    return x, y, z
예제 #2
0
def create_surface(sc_shape):
    from scipy.spatial import cKDTree as KDTree
    atoms = bulk("Al") * sc_shape
    if sc_shape[2] % 2 != 0:
        raise ValueError("The third direction has to be divisible by 2!")
    num_layers = int(sc_shape[2] / 2)

    # Create a cut such that a3 vector is the normal vector
    slab = cut(atoms,
               a=(1, 0, 0),
               b=(0, 1, 0),
               c=(0, 0, 1),
               nlayers=num_layers)
    tags, _ = get_layers(slab, (1, -1, 0))
    for tag, atom in zip(tags, slab):
        if tag % 2 == 0:
            atom.symbol = "Mg"
        else:
            atom.symbol = "Si"
    tree = KDTree(atoms.get_positions())
    used_indices = []
    for atom in slab:
        _, closest_indx = tree.query(atom.position)
        if closest_indx in used_indices:
            raise RuntimeError("Two atoms are mapped onto the same index!")
        atoms[closest_indx].symbol = atom.symbol
        used_indices.append(closest_indx)
    return atoms
예제 #3
0
def from_ase(atoms, zsubs):
    def get_layer(atoms, atom):
        atomlayers = geometry.get_layers(atoms, (0, 0, 1), tolerance=0.01)
        return atomlayers[0][atom.index]

    sublattices = {}
    layer_by_atom = geometry.get_layers(atoms, (0, 0, 1), tolerance=0.01)[0]
    layers = list(set(layer_by_atom))
    if collections.Counter(zsubs[0]) == collections.Counter(layers):
        vectors = np.array([v for v in atoms.get_cell()])
        sites = np.array(atoms.get_scaled_positions())
        numbers = atoms.get_atomic_numbers()
        sublattices[0] = [at.index for at in atoms]

    else:
        vectors = np.array([v for v in atoms.get_cell()])
        sites = np.array(atoms.get_scaled_positions())
        numbers = atoms.get_atomic_numbers()
        for ind, lat in enumerate(zsubs):
            sublat = []
            for at in atoms:
                if get_layer(atoms, at) in sublat:
                    sublat.append(at.index)
            sublattices[ind] = sublat

    return (vectors, sites, numbers, sublattices)
예제 #4
0
    def test_layered(self):
        if not available:
            self.skipTest(reason)

        atoms = bulk("Al", crystalstructure="sc", a=5.0)
        atoms *= (10, 10, 10)
        layers, dist = get_layers(atoms, (1, 0, 0))
        for atom in atoms:
            if layers[atom.index] % 2 == 0:
                atom.symbol = "Al"
            else:
                atom.symbol = "Mg"

        k = 2.0 * np.pi / 10.0
        updater = DiffractionUpdater(atoms=atoms,
                                     k_vector=[k, 0, 0],
                                     active_symbols=["Mg"],
                                     all_symbols=["Al", "Mg"])
        self.assertAlmostEqual(np.abs(updater.value), 0.5)

        updater = DiffractionUpdater(atoms=atoms,
                                     k_vector=[0, k, 0],
                                     active_symbols=["Mg"],
                                     all_symbols=["Al", "Mg"])
        self.assertAlmostEqual(np.abs(updater.value), 0.0)
예제 #5
0
 def plot_pdos_layer(self,
                     atoms=None,
                     layers=None,
                     miller=(0, 0, 1),
                     dz=0.3,
                     Emin=-5,
                     Emax=3,
                     total=False,
                     select=None,
                     fill=True,
                     smearing=None,
                     output=None):
     '''
     
     '''
     atoms = self.atoms
     if not layers:
         layers = get_layers(atoms, miller, dz)[0]
     nlayers = max(layers) + 1
     fig, axs = plt.subplots(nlayers,
                             1,
                             figsize=(10, nlayers * 2),
                             sharex=True)
     indexs = range(len(atoms))
     images = []
     xindex = (self.pdos_energies > Emin) & (self.pdos_energies < Emax)
     for ilayer in range(nlayers):
         iax = nlayers - ilayer - 1
         # axs[iax].set_yticks([])
         index = [j for j in indexs if layers[j] == ilayer]
         images.append(atoms[index])
         pdos_kinds = self.merge_kinds(index)
         self.plot_pdos(energies=self.pdos_energies,
                        pdos_kinds=pdos_kinds,
                        select=select,
                        Emin=Emin,
                        Emax=Emax,
                        ax=axs[iax],
                        legend=False,
                        xylabel=False,
                        fill=fill,
                        smearing=smearing)
         axs[iax].axvline(0, color='b')
         # print(iax, ilayer)
         # print(index)
         # axs[iax].set_xlim([Emin, Emax])
     plt.xlabel('E - E$_{Fermi}$ (eV)', size='16')
     plt.subplots_adjust(hspace=0)
     fig.text(0.05,
              0.5,
              'PDOS (a.u.)',
              size='16',
              va='center',
              rotation='vertical')
     if output is not None:
         # output = '{0}-pdos.png'.format(self.prefix)
         plt.savefig('%s' % output)
     # view(images)
     return axs, images
예제 #6
0
def mgsi(initial=None, comment=None):
    if initial is None:
        pureAl = bulk('Al', cubic=True) * (N, N, N)
        pureAl = wrap_and_sort_by_position(pureAl)
        tags, _ = get_layers(pureAl, (0, 0, 1))
        success = False
        while not success:
            print("Generating new initial precipitate")
            initial, success = create_precipitate(pureAl.copy(), tags,
                                                  normal_radius)

    db = dataset.connect(DB_NAME)
    ref_tbl = db[REF]
    vac_tbl = db[VAC]
    sol_tbl = db[SOL]
    comment_tbl = db[COMMENT]

    runID = hex(random.randint(0, 2**32 - 1))
    if comment is not None:
        comment_tbl.insert({'runID': runID, 'comment': comment})

    eci = {}
    with open("data/almgsix_normal_ce.json", 'r') as infile:
        data = json.load(infile)
        eci = data['eci']

    settings = settingsFromJSON("data/settings_almgsiX_voldev.json")
    settings.basis_func_type = "binary_linear"
    atoms = attach_calculator(settings, initial.copy(), eci)
    atoms.numbers = initial.numbers
    ref_energy = atoms.get_potential_energy()
    ref_tbl.insert({'runID': runID, 'energy': ref_energy})

    for atom in atoms:
        if atom.symbol in ['Mg', 'Si']:
            pos = atom.position
            sol_tbl.insert({
                'runID': runID,
                'symbol': atom.symbol,
                'X': pos[0],
                'Y': pos[1],
                'Z': pos[2]
            })

    ref, neighbors = neighbor_list('ij', atoms, 3.0)
    for ref, nb in zip(ref, neighbors):
        if atoms[ref].symbol == 'Al' and atoms[nb].symbol in ['Mg', 'Si']:
            atoms[ref].symbol = 'X'
            e = atoms.get_potential_energy()
            atoms[ref].symbol = 'Al'
            pos = atoms[ref].position
            vac_tbl.insert({
                'runID': runID,
                'X': pos[0],
                'Y': pos[1],
                'Z': pos[2],
                'energy': e
            })
    atoms = removeAl(atoms)
예제 #7
0
def fix_layers(atoms, miller = (0, 0, 1), tol = 1.0, n = [0, 4]):
    '''
    '''
    layers = get_layers(atoms, miller, tol)[0]
    index = [j for j in range(len(atoms)) if layers[j] in range(n[0], n[1])]
    constraint = FixAtoms(indices=index)
    atoms.set_constraint(constraint)
    return atoms
예제 #8
0
def create_matsudada():
    atoms = bulk("Al", cubic=True, a=4.05)
    atoms = atoms*(4, 1, 4)

    # Construct Matsudada structure
    layer_100, _ = get_layers(atoms, (1, 0, 0))
    indices = [atom.index for atom in atoms if layer_100[atom.index] == 0]
    layer_010, _ = get_layers(atoms, (0, 1, 0))
    for indx in indices:
        if layer_010[indx]%2 == 0:
            atoms[indx].symbol = "Mg"
        else:
            atoms[indx].symbol = "Si"
    
    fname = "data/mgsi_matsudada.xyz"
    write(fname, atoms)
    print("Structure written to {}".format(fname))
예제 #9
0
def closestPackLabeling( atoms ):
    """
    Gives each atom a label A,B or C depending on which layer they are in
    """
    atomLabels = []
    layerIndx, dist = geometry.get_layers( atoms, (1,1,1), tolerance=0.8 )
    print (layerIndx,dist)
    exit()
def mgsi(interface):
    atoms = bulk('Al', cubic=True)
    mgsi = atoms.copy()

    miller = (0, 0, 1)
    if interface == SILICON or interface == MAGNESIUM:
        miller = (1, 0, 0)

    tags, levels = get_layers(mgsi, miller)
    for i, atom in enumerate(mgsi):
        if interface == SILICON or interface == ALTERNATING:
            if tags[i] % 2 == 0:
                atom.symbol = 'Mg'
            else:
                atom.symbol = 'Si'
        elif interface == MAGNESIUM:
            if tags[i] % 2 == 0:
                atom.symbol = 'Si'
            else:
                atom.symbol = 'Mg'

    # Tag all atoms to -1
    for atom in mgsi:
        atom.tag = -1

    mgsi = mgsi * (4, 4, 4)
    matrix = atoms.copy()
    for atom in matrix:
        atom.tag = -1
    matrix = matrix * (4, 4, 4)
    active_area = atoms.copy()
    active_area = active_area * (4, 4, 4)
    tags, layers = get_layers(active_area, (1, 0, 0))
    for t, atom in zip(tags, active_area):
        atom.tag = t

    # Stack togeter
    atoms = stack(mgsi, active_area, axis=0)
    atoms = stack(atoms, matrix, axis=0)
    atoms = wrap_and_sort_by_position(atoms)
    write(f"data/mgsi_active_matrix_{interface}_interface.xyz", atoms)
예제 #11
0
def get_top_struct(atoms):
    atoms_lst = []
    layers = geometry.get_layers(atoms, (0, 0, 1), tolerance=0.1)
    lays = list(set(layers[0]))
    for atom in atoms:
        if get_layer(atoms, atom) in lays[-5:]:
            atoms_lst.append(atom)
    new_atoms = Atoms(atoms_lst)
    new_atoms.set_cell(np.add(atoms.get_cell(), [0, 0, 0.]))
    new_atoms.center(axis=2)
    new_atoms.set_pbc([True, True, True])
    return new_atoms
예제 #12
0
def get_cubic_nano_particle(layer=0):
    from ase.cluster.cubic import FaceCenteredCubic
    from ase.geometry import get_layers
    surfaces = [(1, 0, 0), (0, 1, 0), (0, 0, 1)]
    layers = cubic_np_layer[layer]
    lc = 4.05
    atoms = FaceCenteredCubic('Si', surfaces, layers, latticeconstant=lc)
    tags, array = get_layers(atoms, (1, 0, 0))
    for t, atom in zip(tags, atoms):
        if t % 2 == 0:
            atom.symbol = "Mg"
    print(atoms.get_chemical_formula())
    return atoms
def get_layer_frame(structure, miller_index):
    layer_indexes, layer_distances = get_layers(structure, miller_index)
    repeating_layer_distances = []
    for i in range(len(layer_indexes)):
        layer_distance = layer_distances[layer_indexes[i]]
        repeating_layer_distances.append(layer_distance)
    repeating_layer_distances = np.array(repeating_layer_distances)

    layer_dict = {'layer_index': layer_indexes,
                  'layer_distance': repeating_layer_distances}
    layer_frame = pd.DataFrame(data=layer_dict)
    layer_frame.index.name = "structure_index"
    return layer_frame
예제 #14
0
def gen_midpoints(slab: Atoms,
                  cutoff_dist: float = 3.5,
                  heights: Iterable[float] = None,
                  **kw):
    """

    :param slab:
    :param cutoff_dist:
    :param heights:
    :param kw:
    :return:
    """
    # default heights
    h = heights or [(0, 0, i) for i in (0, 2, 1.8, 1.5, 1.3)]
    tags, layer_pos = get_layers(slab, (0, 0, 1), 0.3)
    surface_atoms = slab[tags == max(tags)]
    # n_atoms = len(surface_atoms)
    atoms = surface_atoms.repeat([3, 3, 1])
    n_points = len(atoms)
    # TODO: check if atoms moved
    atoms.wrap(center=(1 / 6, 1 / 6, 0))

    mem = set()

    def memo(pos: Tuple[float, float]) -> bool:
        label = f'{pos[0]:.2f} {pos[1]:.2f}'
        res = label in mem
        if not res:
            mem.add(label)
        return res

    for i, pos in enumerate(surface_atoms.positions):
        memo(pos)
        yield (f'1_{i}', pos + h[1])

    for i in range(2, len(h)):
        ix = 0
        for indices in combinations(range(n_points), i):
            if not valid_comb(indices, surface_atoms, atoms, cutoff_dist):
                continue
            positions = atoms.positions[list(indices)]
            # TODO:
            if i == 2:
                mean = np.mean(positions, axis=0)
            else:
                mean = get_incenter(positions[0], positions[1], positions[2])
            mean += h[i]
            if not in_cell(surface_atoms, position=mean) or memo(tuple(mean)):
                continue
            yield (f'{i}_{ix}', mean)
            ix += 1
예제 #15
0
def get_nanoparticle():
    from ase.cluster.cubic import FaceCenteredCubic
    from ase.geometry import get_layers
    surfaces = [(1, 0, 0), (1, 1, 0), (1, 1, 1)]
    layers = [10, 13, 9]
    lc = 4.05
    atoms = FaceCenteredCubic('Si', surfaces, layers, latticeconstant=lc)
    tags, array = get_layers(atoms, (1, 0, 0))
    for t, atom in zip(tags, atoms):
        if t % 2 == 0:
            atom.symbol = "Mg"
    print(atoms.get_chemical_formula())
    atoms.rotate(90, 'z', rotate_cell=True)
    return atoms
예제 #16
0
def create_linear_elastic():
    atoms = bulk("Al", cubic=True, a=4.05)
    atoms = atoms*(4, 1, 4)

    # Construct structure predicted by linear elasticity
    layer_101, _ = get_layers(atoms, (1, 0, 1))
    mg_indices = [atom.index for atom in atoms if layer_101[atom.index] == 6]
    si_indices = [atom.index for atom in atoms if layer_101[atom.index] == 7]
    for indx in mg_indices:
        atoms[indx].symbol = "Mg"
    for indx in si_indices:
        atoms[indx].symbol = "Si"

    fname = "data/mgsi_linear_elastic.xyz"
    write(fname, atoms)
    print("Structure written to {}".format(fname))
예제 #17
0
def main():
    a = 4.05
    atoms = bulk('Al', cubic=True, a=a) * (N, N, N)
    tags, _ = get_layers(atoms, (0, 0, 1))
    pos = atoms.get_positions().copy()
    center = np.mean(pos, axis=0) + np.array([a / 4, a / 4, 0.0])
    pos -= center
    radii = np.sqrt(np.sum(pos[:, :2]**2, axis=1))
    indices = np.argsort(radii)

    num_per_layer = 5
    all_indices = []
    for layer in range(2, 12):
        symbol = 'Mg' if layer % 2 == 0 else 'Si'
        num_inserted = 0
        for i in indices:
            if tags[i] == layer and num_inserted < num_per_layer:
                atoms[i].symbol = symbol
                num_inserted += 1
                all_indices.append(i)

    # Extract all items in the 2 layers above
    indices_si = []
    indices_mg = []
    num_si = 4
    num_mg = 5
    for layer in range(12, 14):
        num_extracted = 0
        num_to_extract = num_mg if layer % 2 == 0 else num_si
        for i in indices:
            if tags[i] == layer and num_extracted < num_to_extract:
                if layer % 2 == 0:
                    indices_mg.append(i)
                else:
                    indices_si.append(i)
                num_extracted += 1

    print(len(indices_mg), len(indices_si))
    traj = TrajectoryWriter("data/specialMgSiClusters.traj")
    for symbMg in product(['Al', 'Mg'], repeat=len(indices_mg)):
        for symbSi in product(['Al', 'Si'], repeat=len(indices_si)):
            atoms_cpy = atoms.copy()
            for i in range(len(indices_mg)):
                atoms_cpy[indices_mg[i]].symbol = symbMg[i]
                for j in range(len(indices_si)):
                    atoms_cpy[indices_si[j]].symbol = symbSi[j]
            traj.write(atoms_cpy)
예제 #18
0
def auto_layers(atoms, miller=(0, 0, 1)):
    """Returns two arrays describing which layer each atom belongs
    to and the distance between the layers and origo.
    Assumes the tolerance corresponds to the average atomic radii of the slab.

    Parameters
    ----------
    atoms : object
        The atoms object must have the following keys in atoms.subsets:

        'slab_atoms' : list
            indices of atoms belonging to the slab
    """
    radii = [get_radius(z) for z in atoms.numbers[atoms.subsets['slab_atoms']]]
    radius = np.average(radii) / 2.
    lz, li = get_layers(atoms, miller=miller, tolerance=radius)
    return lz, li
def insert_np(size, atoms):
    cluster = bulk("Al", cubic=True) * (size, size, size)
    tags, array = get_layers(cluster, (1, 0, 0))

    for atom in cluster:
        if atom.index % 2 == 0:
            atom.symbol = "Si"
        else:
            atom.symbol = "Mg"

    tree = KDTree(atoms.get_positions())
    symbols = [a.symbol for a in atoms]
    for atom in cluster:
        closest = tree.query(atom.position)[1]
        symbols[closest] = atom.symbol
    atoms.get_calculator().set_symbols(symbols)
    return atoms
예제 #20
0
def center_at_origin(atoms: Atoms) -> Atoms:
    """
    Returns a copy of the atoms object centered at the lowest atoms in the z axis
    :param atoms: Atoms object
    :return: Copy of the translated Atoms object
    """
    ads = atoms.copy()
    if len(ads) == 1:
        ads[0].position = (0, 0, 0)
    else:
        tags, layer_pos = get_layers(ads, (0, 0, 1), 0.3)

        if sum(tags == 0) == 1:
            center = atoms[0].position
        else:
            lowest_atoms = ads[tags == 0]
            center = np.mean([a.position for a in lowest_atoms], axis=0)

        ads.translate(-center)
    return ads
예제 #21
0
def layers2ads_index(atoms, formula):
    """ Returns the indexes of atoms in layers exceeding the number of layers
    stored in the key value pair 'layers'.

    Parameters
    ----------
    atoms : ase atoms object.
        atoms.info must be a dictionary containing the key 'key_value_pairs',
        which is expected to contain CatMAP standard adsorbate structure
        key value pairs. See the ase db to catmap module in catmap.
        the key value pair 'species' must be the
        chemical formula of the adsorbate and 'layers' must be an integer.
    """
    composition = string2symbols(formula)
    n_ads = len(composition)
    natoms = len(atoms)
    radii = [get_radius(s) for s in composition]
    lz, li = get_layers(atoms, (0, 0, 1), tolerance=np.average(radii))
    layers = int(atoms.info['key_value_pairs']['layers'])
    ads_atoms = [a.index for a in atoms if li[a.index] > layers - 1]
    ads_atoms = list(range(natoms - n_ads, natoms))
    if len(ads_atoms) != len(composition):
        raise AssertionError("ads atoms identification by layers failed.")
    return ads_atoms
예제 #22
0
    def test_update_method(self):
        if not available:
            self.skipTest(reason)

        atoms = bulk("Al", crystalstructure="sc", a=5.0)
        atoms *= (10, 10, 10)
        layers, dist = get_layers(atoms, (1, 0, 0))
        for atom in atoms:
            if layers[atom.index] % 2 == 0:
                atom.symbol = "Al"
            else:
                atom.symbol = "Mg"

        k = 2.0 * np.pi / 10.0
        updater = DiffractionUpdater(atoms=atoms,
                                     k_vector=[k, 0, 0],
                                     active_symbols=["Mg"],
                                     all_symbols=["Al", "Mg"])

        # Remove all Al atoms
        for i in range(len(atoms)):
            if atoms[i].symbol == "Al":
                system_change = [(i, "Al", "Mg")]
                updater.update(system_change)

        # Reflection should be zero
        self.assertAlmostEqual(np.abs(updater.value), 0.0)

        # Insert Al atoms again
        for i in range(len(atoms)):
            if layers[i] % 2 == 0:
                system_change = [(i, "Mg", "Al")]
                updater.update(system_change)

        # Now value should be back to 1/2
        self.assertAlmostEqual(np.abs(updater.value), 0.5)
예제 #23
0
def set_layer_info(atoms, miller=(0, 0, 1), tolerance=0.001):
    tags = get_layers(atoms, miller, tolerance)
    atoms.set_tags(tags[0])
예제 #24
0
def get_layer(atoms, atom):
    atomlayers = geometry.get_layers(atoms, (0, 0, 1), tolerance=0.01)
    return atomlayers[0][atom.index]
예제 #25
0
# Cut out slab of 5 Al(001) layers
al001 = cut(al, nlayers=5)
correct_pos = np.array([[0., 0., 0.],
                        [0., 0.5, 0.2],
                        [0.5, 0., 0.2],
                        [0.5, 0.5, 0.],
                        [0., 0., 0.4],
                        [0., 0.5, 0.6],
                        [0.5, 0., 0.6],
                        [0.5, 0.5, 0.4],
                        [0., 0., 0.8],
                        [0.5, 0.5, 0.8]])
assert np.allclose(correct_pos, al001.get_scaled_positions())

# Check layers along 001
tags, levels = get_layers(al001, (0, 0, 1))
assert np.allclose(tags, [0, 1, 1, 0, 2, 3, 3, 2, 4, 4])
assert np.allclose(levels, [0., 2.025, 4.05, 6.075, 8.1])

# Check layers along 101
tags, levels = get_layers(al001, (1, 0, 1))
assert np.allclose(tags, [0, 1, 5, 3, 2, 4, 8, 7, 6, 9])
assert np.allclose(levels, [0.000, 0.752, 1.504, 1.880, 2.256, 2.632, 3.008,
                            3.384, 4.136, 4.888],
                   atol=0.001)

# Check layers along 111
tags, levels = get_layers(al001, (1, 1, 1))
assert np.allclose(tags, [0, 2, 2, 4, 1, 5, 5, 6, 3, 7])
assert np.allclose(levels, [0.000, 1.102, 1.929, 2.205, 2.756, 3.031, 3.858,
                            4.960],
예제 #26
0
def surfaces_with_termination(lattice,
                              indices,
                              layers,
                              vacuum=None,
                              tol=1e-10,
                              termination=None,
                              return_all=False,
                              verbose=False):
    """Create surface from a given lattice and Miller indices with a given
        termination

        Parameters
        ==========
        lattice: Atoms object or str
            Bulk lattice structure of alloy or pure metal.  Note that the
            unit-cell must be the conventional cell - not the primitive cell.
            One can also give the chemical symbol as a string, in which case the
            correct bulk lattice will be generated automatically.
        indices: sequence of three int
            Surface normal in Miller indices (h,k,l).
        layers: int
            Number of equivalent layers of the slab. (not the same as the layers
            you choose from for terminations)
        vacuum: float
            Amount of vacuum added on both sides of the slab.
        termination: str
            the atoms you wish to be in the top layer. There may be many such
            terminations, this function returns all terminations with the same
            atomic composition.
            e.g. 'O' will return oxygen terminated surfaces.
            e.g.'TiO' will return surfaces terminated with layers containing both O
            and Ti
        Returns:
        return_surfs: List
            a list of surfaces that match the specifications given

    """
    lats = translate_lattice(lattice, indices)
    return_surfs = []
    check = []
    check2 = []
    for item in lats:
        too_similar = False
        surf = surface(item, indices, layers, vacuum=vacuum, tol=tol)
        surf.wrap(pbc=[True] * 3)  # standardize slabs

        positions = surf.get_scaled_positions().flatten()
        for i, value in enumerate(positions):
            if value >= 1 - tol:  # move things closer to zero within tol
                positions[i] -= 1
        surf.set_scaled_positions(np.reshape(positions, (len(surf), 3)))
        #rep = find_z_layers(surf)
        z_layers, hs = get_layers(surf, (0, 0, 1))  # just z layers matter
        # get the indicies of the atoms in the highest layer
        top_layer = [
            i for i, val in enumerate(z_layers == max(z_layers)) if val
        ]

        if termination is not None:
            comp = [surf.get_chemical_symbols()[a] for a in top_layer]
            term = string2symbols(termination)
            # list atoms in top layer and not in requested termination
            check = [a for a in comp if a not in term]
            # list of atoms in requested termination and not in top layer
            check2 = [a for a in term if a not in comp]
        if len(return_surfs) > 0:
            pos_diff = [
                a.get_positions() - surf.get_positions() for a in return_surfs
            ]
            for i, su in enumerate(pos_diff):
                similarity_test = su.flatten() < tol * 1000
                if similarity_test.all():
                    # checks if surface is too similar to another surface
                    too_similar = True
        if too_similar:
            continue
        if return_all is True:
            pass
        elif check != [] or check2 != []:
            continue
        return_surfs.append(surf)
    return return_surfs
예제 #27
0
# Cut out slab of 5 Al(001) layers
al001 = cut(al, nlayers=5)
correct_pos = np.array([[0., 0., 0.],
                        [0., 0.5, 0.2],
                        [0.5, 0., 0.2],
                        [0.5, 0.5, 0.],
                        [0., 0., 0.4],
                        [0., 0.5, 0.6],
                        [0.5, 0., 0.6],
                        [0.5, 0.5, 0.4],
                        [0., 0., 0.8],
                        [0.5, 0.5, 0.8]])
assert np.allclose(correct_pos, al001.get_scaled_positions())

# Check layers along 001
tags, levels = get_layers(al001, (0, 0, 1))
assert np.allclose(tags, [0, 1, 1, 0, 2, 3, 3, 2, 4, 4])
assert np.allclose(levels, [0., 2.025, 4.05, 6.075, 8.1])

# Check layers along 101
tags, levels = get_layers(al001, (1, 0, 1))
assert np.allclose(tags, [0, 1, 5, 3, 2, 4, 8, 7, 6, 9])
assert np.allclose(levels, [0.000, 0.752, 1.504, 1.880, 2.256, 2.632, 3.008,
                            3.384, 4.136, 4.888],
                   atol=0.001)

# Check layers along 111
tags, levels = get_layers(al001, (1, 1, 1))
assert np.allclose(tags, [0, 2, 2, 4, 1, 5, 5, 6, 3, 7])
assert np.allclose(levels, [0.000, 1.102, 1.929, 2.205, 2.756, 3.031, 3.858,
                            4.960],
예제 #28
0
def detect_termination(atoms):
    """ Returns three lists, the first containing indices of bulk atoms and
    the second containing indices of atoms in the second outermost layer, and
    the last denotes atoms in the outermost layer or termination or the slab.

    Parameters
    ----------
    atoms : ase atoms object.
        The atoms object must have the following keys in atoms.subsets:
            - 'ads_atoms' : list
                indices of atoms belonging to the adsorbate
            - 'slab_atoms' : list
                indices of atoms belonging to the slab
    """
    max_coord = 0
    try:
        cm = atoms.connectivity.copy()
        np.fill_diagonal(cm, 0)
        # List coordination numbers of slab atoms.
        coord = np.count_nonzero(cm[atoms.subsets['slab_atoms'], :], axis=1)
        coord -= 1
        bulk = []
        term = []
        max_coord = np.max(coord)
        for j, c in enumerate(coord):
            a_s = atoms.subsets['slab_atoms'][j]
            if (c < max_coord
                    and a_s not in atoms.constraints[0].get_indices()):
                term.append(a_s)
            else:
                bulk.append(a_s)
        subsurf = []
        for a_b in bulk:
            for a_t in term:
                if cm[a_t, a_b] > 0:
                    subsurf.append(a_b)
        subsurf = list(np.unique(subsurf))
        try:
            sx, sy = atoms.info['key_value_pairs']['supercell'].split('x')
            sx = int(''.join(i for i in sx if i.isdigit()))
            sy = int(''.join(i for i in sy if i.isdigit()))
            if int(sx) * int(sy) != len(term):
                msg = str(len(term)) + ' termination atoms identified.' + \
                    'size = ' + atoms.info['key_value_pairs']['supercell'] + \
                    ' id: ' + str(atoms.info['id'])
                warnings.warn(msg)
        except KeyError:
            pass
        if len(bulk) is 0 or len(term) is 0 or len(subsurf) is 0:
            # print(il, zl)
            # msg = 'dbid: ' + str(atoms.info['id'])
            raise AssertionError()
    except (AssertionError, IndexError):
        layers = int(atoms.info['key_value_pairs']['layers'])
        radii = [
            get_radius(z) for z in atoms.numbers[atoms.subsets['slab_atoms']]
        ]
        radius = np.average(radii)
        il, zl = get_layers(atoms, (0, 0, 1), tolerance=radius)
        if len(zl) < layers:
            # msg = 'dbid: ' + str(atoms.info['id'])
            raise AssertionError()
        # bulk atoms includes subsurface atoms.
        bulk = [a.index for a in atoms if il[a.index] <= layers - 2]
        subsurf = [a.index for a in atoms if il[a.index] == layers - 2]
        term = [
            a.index for a in atoms if il[a.index] >= layers -
            1 and a.index not in atoms.subsets['ads_atoms']
        ]
    if len(bulk) is 0 or len(term) is 0 or len(subsurf) is 0:
        print(bulk, term, subsurf)
        msg = 'Detect bulk/term.'
        if 'id' in atoms.info:
            msg += ' id: ' + str(atoms.info['id'])
        print(il, zl)
        raise AssertionError(msg)
    for a_s in atoms.subsets['site_atoms']:
        if a_s not in term:
            msg = 'site not in term.'
            if 'id' in atoms.info:
                msg += str(atoms.info['id'])
            warnings.warn(msg)
            break
    return bulk, term, subsurf
예제 #29
0
def test_geometry():
    """Test the ase.geometry module and ase.build.cut() function."""

    import numpy as np

    from ase.build import cut, bulk, fcc111
    from ase.cell import Cell
    from ase.geometry import get_layers, wrap_positions
    from ase.spacegroup import crystal, get_spacegroup

    al = crystal('Al', [(0, 0, 0)], spacegroup=225, cellpar=4.05)

    # Cut out slab of 5 Al(001) layers
    al001 = cut(al, nlayers=5)
    correct_pos = np.array([[0., 0., 0.],
                            [0., 0.5, 0.2],
                            [0.5, 0., 0.2],
                            [0.5, 0.5, 0.],
                            [0., 0., 0.4],
                            [0., 0.5, 0.6],
                            [0.5, 0., 0.6],
                            [0.5, 0.5, 0.4],
                            [0., 0., 0.8],
                            [0.5, 0.5, 0.8]])
    assert np.allclose(correct_pos, al001.get_scaled_positions())

    # Check layers along 001
    tags, levels = get_layers(al001, (0, 0, 1))
    assert np.allclose(tags, [0, 1, 1, 0, 2, 3, 3, 2, 4, 4])
    assert np.allclose(levels, [0., 2.025, 4.05, 6.075, 8.1])

    # Check layers along 101
    tags, levels = get_layers(al001, (1, 0, 1))
    assert np.allclose(tags, [0, 1, 5, 3, 2, 4, 8, 7, 6, 9])
    assert np.allclose(levels, [0.000, 0.752, 1.504, 1.880, 2.256, 2.632, 3.008,
                                3.384, 4.136, 4.888],
                       atol=0.001)

    # Check layers along 111
    tags, levels = get_layers(al001, (1, 1, 1))
    assert np.allclose(tags, [0, 2, 2, 4, 1, 5, 5, 6, 3, 7])
    assert np.allclose(levels, [0.000, 1.102, 1.929, 2.205, 2.756, 3.031, 3.858,
                                4.960],
                       atol=0.001)

    # Cut out slab of three Al(111) layers
    al111 = cut(al, (1, -1, 0), (0, 1, -1), nlayers=3)
    correct_pos = np.array([[0.5, 0., 0.],
                            [0., 0.5, 0.],
                            [0.5, 0.5, 0.],
                            [0., 0., 0.],
                            [1 / 6., 1 / 3., 1 / 3.],
                            [1 / 6., 5 / 6., 1 / 3.],
                            [2 / 3., 5 / 6., 1 / 3.],
                            [2 / 3., 1 / 3., 1 / 3.],
                            [1 / 3., 1 / 6., 2 / 3.],
                            [5 / 6., 1 / 6., 2 / 3.],
                            [5 / 6., 2 / 3., 2 / 3.],
                            [1 / 3., 2 / 3., 2 / 3.]])
    assert np.allclose(correct_pos, al111.get_scaled_positions())

    # Cut out cell including all corner and edge atoms (non-periodic structure)
    al = cut(al, extend=1.1)
    correct_pos = np.array([[0., 0., 0.],
                            [0., 2.025, 2.025],
                            [2.025, 0., 2.025],
                            [2.025, 2.025, 0.],
                            [0., 0., 4.05],
                            [2.025, 2.025, 4.05],
                            [0., 4.05, 0.],
                            [2.025, 4.05, 2.025],
                            [0., 4.05, 4.05],
                            [4.05, 0., 0.],
                            [4.05, 2.025, 2.025],
                            [4.05, 0., 4.05],
                            [4.05, 4.05, 0.],
                            [4.05, 4.05, 4.05]])
    assert np.allclose(correct_pos, al.positions)

    # Create an Ag(111)/Si(111) interface
    ag = crystal(['Ag'], basis=[(0, 0, 0)], spacegroup=225, cellpar=4.09)
    si = crystal(['Si'], basis=[(0, 0, 0)], spacegroup=227, cellpar=5.43)
    try:
        assert get_spacegroup(ag).no == 225
        assert get_spacegroup(si).no == 227
    except ImportError:
        pass

    ag111 = cut(ag, a=(4, -4, 0), b=(4, 4, -8), nlayers=5)  # noqa
    si111 = cut(si, a=(3, -3, 0), b=(3, 3, -6), nlayers=5)  # noqa
    #
    # interface = stack(ag111, si111)
    # assert len(interface) == 1000
    # assert np.allclose(interface.positions[::100],
    #                   [[  4.08125   ,  -2.040625  ,   -2.040625  ],
    #                    [  8.1625    ,   6.121875  ,  -14.284375  ],
    #                    [ 10.211875  ,   0.00875   ,    2.049375  ],
    #                    [ 24.49041667,  -4.07833333,  -16.32208333],
    #                    [ 18.37145833,  14.29020833,  -24.48166667],
    #                    [ 24.49916667,  12.25541667,  -20.39458333],
    #                    [ 18.36854167,  16.32791667,  -30.60645833],
    #                    [ 19.0575    ,   0.01166667,    5.45333333],
    #                    [ 23.13388889,   6.80888889,    1.36722222],
    #                    [ 35.3825    ,   5.45333333,  -16.31333333]])
    #

    # Test the wrap_positions function.
    positions = np.array([
        [4.0725, -4.0725, -1.3575],
        [1.3575, -1.3575, -1.3575],
        [2.715, -2.715, 0.],
        [4.0725, 1.3575, -1.3575],
        [0., 0., 0.],
        [2.715, 2.715, 0.],
        [6.7875, -1.3575, -1.3575],
        [5.43, 0., 0.]])
    cell = np.array([[5.43, 5.43, 0.0], [5.43, -5.43, 0.0], [0.00, 0.00, 40.0]])
    positions += np.array([6.1, -0.1, 10.1])
    result_positions = wrap_positions(positions=positions, cell=cell)
    correct_pos = np.array([
        [4.7425, 1.2575, 8.7425],
        [7.4575, -1.4575, 8.7425],
        [3.385, 2.615, 10.1],
        [4.7425, -4.1725, 8.7425],
        [6.1, -0.1, 10.1],
        [3.385, -2.815, 10.1],
        [2.0275, -1.4575, 8.7425],
        [0.67, -0.1, 10.1]])
    assert np.allclose(correct_pos, result_positions)

    positions = wrap_positions(positions, cell, pbc=[False, True, False])
    correct_pos = np.array([
        [4.7425, 1.2575, 8.7425],
        [7.4575, -1.4575, 8.7425],
        [3.385, 2.615, 10.1],
        [10.1725, 1.2575, 8.7425],
        [6.1, -0.1, 10.1],
        [8.815, 2.615, 10.1],
        [7.4575, 3.9725, 8.7425],
        [6.1, 5.33, 10.1]])
    assert np.allclose(correct_pos, positions)

    # Test center away from values 0, 0.5
    result_positions = wrap_positions(positions, cell,
                                      pbc=[True, True, False],
                                      center=0.2)
    correct_pos = [[4.7425, 1.2575, 8.7425],
                   [2.0275, 3.9725, 8.7425],
                   [3.385, 2.615, 10.1],
                   [-0.6875, 1.2575, 8.7425],
                   [6.1, -0.1, 10.1],
                   [3.385, -2.815, 10.1],
                   [2.0275, -1.4575, 8.7425],
                   [0.67, -0.1, 10.1]]
    assert np.allclose(correct_pos, result_positions)

    # Test pretty_translation keyword
    positions = np.array([
        [0, 0, 0],
        [0, 1, 1],
        [1, 0, 1],
        [1, 1, 0.]])
    cell = np.diag([2, 2, 2])
    result = wrap_positions(positions, cell, pbc=[True, True, True],
                            pretty_translation=True)
    assert np.max(result) < 1 + 1E-10
    assert np.min(result) > -1E-10

    result = wrap_positions(positions - 5, cell, pbc=[True, True, True],
                            pretty_translation=True)
    assert np.max(result) < 1 + 1E-10

    result = wrap_positions(positions - 5, cell, pbc=[False, True, True],
                            pretty_translation=True)
    assert np.max(result[:, 0]) < -3
    assert np.max(result[:, 1:]) < 1 + 1E-10

    # Get the correct crystal structure from a range of different cells

    def checkcell(cell, name):
        cell = Cell.ascell(cell)
        lat = cell.get_bravais_lattice()
        assert lat.name == name, (lat.name, name)

    checkcell(bulk('Al').cell, 'FCC')
    checkcell(bulk('Fe').cell, 'BCC')
    checkcell(bulk('Zn').cell, 'HEX')
    checkcell(fcc111('Au', size=(1, 1, 3), periodic=True).cell, 'HEX')
    checkcell([[1, 0, 0], [0, 1, 0], [0, 0, 1]], 'CUB')
    checkcell([[1, 0, 0], [0, 1, 0], [0, 0, 2]], 'TET')
    checkcell([[1, 0, 0], [0, 2, 0], [0, 0, 3]], 'ORC')
    checkcell([[1, 0, 0], [0, 2, 0], [0.5, 0, 3]], 'ORCC')
    checkcell([[1, 0, 0], [0, 2, 0], [0.501, 0, 3]], 'MCL')
    checkcell([[1, 0, 0], [0.5, 3**0.5 / 2, 0], [0, 0, 3]], 'HEX')
예제 #30
0
def set_tags(atoms, direction=(0, 0, 1)):
    tags, positions = get_layers(atoms, direction, 0.3)
    atoms.set_tags(tags)
예제 #31
0
def free_energy_vs_layered(T, mod):
    from cemc.mcmc import AdaptiveBiasReactionPathSampler
    from cemc.mcmc import ReactionCrdRangeConstraint
    from cemc.mcmc import DiffractionObserver
    from cemc.mcmc import MinimalEnergyPath
    from ase.geometry import get_layers
    atoms = get_atoms(cubic=True)

    atoms_cpy = atoms.copy()
    layers, dist = get_layers(atoms, (0, 1, 0))
    for atom in atoms_cpy:
        if layers[atom.index] % 2 == 0:
            atom.symbol = "Mg"
        else:
            atom.symbol = "Si"
    symbols = [atom.symbol for atom in atoms_cpy]
    atoms.get_calculator().set_symbols(symbols)

    lamb = 4.05
    k = 2.0 * np.pi / lamb
    workdir = "data/diffraction"

    # Have to perform only insert moves
    mc = Montecarlo(atoms, T)
    k_vec = [k, 0, 0]
    observer = DiffractionObserver(atoms=atoms,
                                   active_symbols=["Si"],
                                   all_symbols=["Mg", "Si"],
                                   k_vector=k_vec,
                                   name="reflection")
    if T < 250:
        mc.max_attempts = 5
        nsteps = 100 * len(atoms)
        mep = MinimalEnergyPath(mc_obj=mc,
                                observer=observer,
                                value_name="reflection",
                                relax_steps=nsteps,
                                search_steps=nsteps,
                                traj_file="{}/mep_diffract{}K.traj".format(
                                    workdir, T),
                                max_reac_crd=1999.0)
        mep.run()
        mep.save(fname="{}/mep_mep_diffract_path{}.csv".format(workdir, T))
    else:
        conc_cnst = ReactionCrdRangeConstraint(observer,
                                               value_name="reflection")
        conc_cnst.update_range([0, 0.5])
        mc.add_constraint(conc_cnst)

        reac_path = AdaptiveBiasReactionPathSampler(
            mc_obj=mc,
            react_crd=[0.0, 0.5],
            observer=observer,
            n_bins=500,
            data_file="{}/layered_bias{}K.h5".format(workdir, T),
            mod_factor=mod,
            delete_db_if_exists=True,
            mpicomm=None,
            db_struct="{}/layered_bias_struct{}K.db".format(workdir, T),
            react_crd_name="reflection",
            ignore_equil_steps=False,
            smear=2000)
        reac_path.run()
        reac_path.save()