コード例 #1
0
def rich2poor(atoms, surf_CN=11):
    '''Moves an atom from a rich region to a poor region'''

    syms = np.asarray(atoms.get_chemical_symbols())
    NN_list = NeighborList(atoms)
    NNs = [list(syms[i]) for i in NN_list]
    max_CN = max([len(NN) for NN in NNs])

    # Pick an atom in a rich environment
    ns = np.array([NNs[i].count(sym) for i, sym in enumerate(syms)])

    unique_ns = np.array(list(set(ns)))
    prob_rich = 2.0 ** unique_ns
    prob_rich /= np.sum(prob_rich)
    n = np.random.choice(unique_ns, p=prob_rich)
    indices_rich = np.where(ns == n)[0]
    index_rich = np.random.choice(indices_rich)
    symbol_rich = syms[index_rich]

    # Pick another atom in a poor environment
    indices_other = [i for i, sym in enumerate(syms) if sym != symbol_rich]
    ns_to_rich = np.array([max_CN - NNs[i].count(symbol_rich) for i in indices_other])
    unique_ns = np.array(list(set(ns_to_rich)))
    prob_poor = 2.0 ** unique_ns
    prob_poor /= np.sum(prob_poor)
    n_to_rich = np.random.choice(unique_ns, p=prob_poor)
    indices_poor = np.where(ns_to_rich == n_to_rich)[0]
    index_poor = np.random.choice(indices_poor)
    index_poor = indices_other[index_poor]
    symbol_poor = syms[index_poor]

    atoms[index_poor].symbol = symbol_rich
    atoms[index_rich].symbol = symbol_poor

    return
コード例 #2
0
def move_atoms(individual, max_natoms=0.20):
    """Randomly moves atoms within a cluster.
    
    Parameters
    ----------
    individual : Individual 
        An individual
    max_natoms : float or int
        if float, the maximum number of atoms that will be moved is 
        max_natoms*len(individual). if int, the maximum number of atoms 
        that will be moved is max_natoms default: 0.20
    """

    if not len(individual):
        return False

    if isinstance(max_natoms, float):
        assert max_natoms <= 1
        max_natoms = int(len(individual) * max_natoms)

    NNs = NeighborList(individual)
    CNs = [len(NN) for NN in NNs]

    # Get the surface atoms. These provide bounds for the moves
    # First get unit vectors and mags of all surface atoms
    positions = individual.get_positions()
    com = np.sum(positions.T, axis=1) / len(individual)
    surf_indices = [i for i, CN in enumerate(CNs) if CN < 11]
    surf_positions = np.array([positions[i] for i in surf_indices])
    surf_magnitudes = np.linalg.norm(surf_positions - com, axis=1)
    surf_vectors = (surf_positions - com) / np.array([surf_magnitudes]).T

    #Weight probability of choosing a vector by its length from the center
    surf_probabilities = surf_magnitudes / sum(surf_magnitudes)

    move_vectors_magnitudes_indices = np.random.choice(list(
        range(len(surf_indices))),
                                                       replace=True,
                                                       size=max_natoms,
                                                       p=surf_probabilities)

    # Move the atoms
    max_natoms = max(max_natoms, 1)
    natoms_to_move = random.randint(1, max_natoms)
    atom_indices = list(range(len(individual)))
    random.shuffle(
        atom_indices
    )  # Using random.shuffle on the indices guarantees no duplicates
    atom_indices = atom_indices[:natoms_to_move]
    for atom_index, move_index in zip(atom_indices,
                                      move_vectors_magnitudes_indices):
        vec, mag = surf_vectors[move_index], surf_magnitudes[move_index]
        atom = individual[atom_index]
        atom.x, atom.y, atom.z = com + vec * random.random() * mag

    return None
コード例 #3
0
def rotate_cluster(individual, max_natoms=0.20):
    """Chooses a random number of atoms nearest to a random point in
    the cluster. These atoms are then rotated randomly around this point

    Parameters
    ----------
    individual : Individual
        An individual object
    max_natoms : float
        The fraction of the total atoms to rotate
    """

    if not len(individual):
        return None

    if isinstance(max_natoms, float):
        assert max_natoms <= 1
        max_natoms = int(len(individual) * max_natoms)

    NNs = NeighborList(individual)
    CNs = [len(NN) for NN in NNs]

    # Get the surface atoms. These provide bounds for the moves
    # First get unit vectors and mags of all surface atoms
    positions = individual.get_positions()
    com = np.sum(positions.T, axis=1) / len(individual)
    surf_indices = [i for i, CN in enumerate(CNs) if CN < 11]
    surf_positions = np.array([positions[i] for i in surf_indices])
    surf_magnitudes = np.linalg.norm(surf_positions - com, axis=1)
    surf_vectors = (surf_positions - com) / np.array([surf_magnitudes]).T

    # Weight probability of choosing a vector by its length from the center
    surf_probabilities = surf_magnitudes / sum(surf_magnitudes)

    # Choose a random point inside the particle and find nearest neighbors to that point
    i = np.random.choice(list(range(len(surf_vectors))), p=surf_probabilities)
    point = com + surf_vectors[i] * surf_magnitudes[i] * random.random()
    atom = Atom('Si', point)
    nearest_indices = individual.get_nearest_atom_indices(
        atom_index=atom.index, count=max_natoms)

    # Extract out the atoms to be rotated. Popping changes the indices
    # so pop them out highest to lowest indice
    nearest_indices = np.sort(nearest_indices)[::-1]
    atoms = Atoms()
    for ind in nearest_indices:
        atoms.append(individual.pop(ind))

    axis = random.choice(['x', '-x', 'y', '-y', 'z', '-z'])
    angle = random.uniform(30, 180)
    atoms.rotate(axis, a=angle, center='COM', rotate_cell=False)
    individual.extend(atoms)

    return None
コード例 #4
0
def move_atoms_group(individual, max_natoms=0.20):
    """Randomly moves atoms within a cluster.
    
    Args:
        individual (Individual): an individual
        max_natoms (float or int): if float, the maximum number of atoms that will be moved is max_natoms*len(individual)
                                   if int, the maximum number of atoms that will be moved is max_natoms
                                   default: 0.20
    """

    if not len(individual):
        return None

    if isinstance(max_natoms, float):
        assert max_natoms <= 1
        max_natoms = int(len(individual) * max_natoms)

    NNs = NeighborList(individual)
    CNs = [len(NN) for NN in NNs]

    # Get the surface atoms. These provide bounds for the moves
    # First get unit vectors and mags of all surface atoms
    positions = individual.get_positions()
    com = np.sum(positions.T, axis=1) / len(individual)
    surf_indices = [i for i, CN in enumerate(CNs) if CN < 11]
    surf_positions = np.array([positions[i] for i in surf_indices])
    surf_magnitudes = np.linalg.norm(surf_positions - com, axis=1)
    surf_vectors = (surf_positions - com) / np.array([surf_magnitudes]).T

    # Weight probability of choosing a vector by its length from the center
    surf_probabilities = surf_magnitudes / sum(surf_magnitudes)

    # Choose a random point inside the particle and find nearest neighbors to that point
    i = np.random.choice(list(range(len(surf_vectors))), p=surf_probabilities)
    center = com + surf_vectors[i] * surf_magnitudes[i] * random.random()
    dists = np.linalg.norm(positions - center, axis=1)
    indices_dists = [[i, dist] for i, dist in enumerate(dists)]
    indices_dists.sort(key=lambda i: i[1])
    max_natoms = max(max_natoms, 1)
    natoms_to_move = random.randint(1, max_natoms)

    # Choose another random point inside the particle and move cluster to that point
    i = np.random.choice(list(range(len(surf_vectors))), p=surf_probabilities)
    move = com + surf_vectors[i] * surf_magnitudes[i] * random.random() - center
    for index, dist in indices_dists[:natoms_to_move]:
        positions[index] += move

    individual.set_positions(positions)

    return None
コード例 #5
0
    def get_bulk_bonds(self, individual):
        """Chooses a random atom to orient around. The probability
        of choosing the atom is proportional to the atom's distance
        from the center of mass"""

        # Get a bulk atom near the center of the particle
        pos = individual.get_positions()
        com = individual.get_center_of_mass()
        dists_from_com = np.linalg.norm(pos - com, axis=1)
        prob = dists_from_com / sum(dists_from_com)
        bulk_atom_index = np.random.choice(list(range(len(individual))), p=prob)
        bulk_atom_pos = pos[bulk_atom_index]

        # Get the neighbors of the bulk atom
        NNs = NeighborList(individual)
        bonds = np.asarray([pos[i] for i in NNs[bulk_atom_index]]) - bulk_atom_pos

        return bonds
コード例 #6
0
def poor2rich(individual):
    '''Used for multi-component systems. Swaps atoms A and B
    so that atom A moves from a region with a low number of A-A
    bonds to a high number of A-A bonds.

    Parameters
    ----------
    individual : Individual
        An individual
    '''

    syms = np.asarray(individual.get_chemical_symbols())
    NN_list = NeighborList(individual)
    NNs = [list(syms[i]) for i in NN_list]
    max_CN = max([len(NN) for NN in NNs])

    # Pick an atom in a poor environment
    ns_to_rich = np.array(
        [max_CN - NNs[i].count(sym) for i, sym in enumerate(syms)])

    unique_ns = np.array(list(set(ns_to_rich)))
    prob_poor = 2.0**unique_ns
    prob_poor /= np.sum(prob_poor)
    n_to_rich = np.random.choice(unique_ns, p=prob_poor)
    indices_poor = np.where(ns_to_rich == n_to_rich)[0]
    index_poor = np.random.choice(indices_poor)
    symbol_poor = syms[index_poor]

    # Pick another atom in a rich environment
    indices_other = [i for i, sym in enumerate(syms) if sym != symbol_poor]
    ns = np.array([NNs[i].count(symbol_poor) for i in indices_other])
    unique_ns = np.array(list(set(ns)))
    prob_rich = 2.0**unique_ns
    prob_rich /= np.sum(prob_rich)
    n = np.random.choice(unique_ns, p=prob_rich)
    indices_rich = np.where(ns == n)[0]
    index_rich = np.random.choice(indices_rich)
    index_rich = indices_other[index_rich]
    symbol_rich = syms[index_rich]

    individual[index_poor].symbol = symbol_rich
    individual[index_rich].symbol = symbol_poor

    return
コード例 #7
0
def poor2rich_column(individual, STEM_parameters, filter_size=0.5,
                       column_cutoff=0.5, species=None, surf_CN=11):
    """This mutation randomly does an atom swap within a column of atoms"""

    NN_list = NeighborList(individual)
    
    module = STEM(STEM_parameters)
    module.generate_target()

    image = module.get_image(individual)

    # Find the xy coordinates of the columns in the image
    avg_bond_length = get_avg_radii(individual) * 2
    cutoff = avg_bond_length * 1.1
    column_cutoff *= cutoff
    resolution = module.parameters['resolution']        
    size = cutoff * resolution * filter_size

    image_max = filters.maximum_filter(image, size=size)
    columns = ((image == image_max) & (image > 0.01))
    column_coords = np.argwhere(columns)
    column_xys = column_coords[:, ::-1] / resolution

    if len(column_xys) < 2:
        return False

    ##########################################################
    ### This code is for checking the local maximum finder ###
    ##########################################################
    # import matplotlib.pyplot as plt
    # import matplotlib.cm as cm
    # fig, ax = plt.subplots(num=1)
    # fig.colorbar(ax.pcolormesh(image_max, cmap=cm.viridis))
    # ax.set_aspect('equal', 'box')

    # fig, ax = plt.subplots(num=2)
    # fig.colorbar(ax.pcolormesh(columns, cmap=cm.viridis))
    # ax.set_aspect('equal', 'box')
    
    # plt.show()
    # import sys; sys.exit()

    # Get the symbols in each column with > 1 type of atom and a non-species site
    # at the surface available to be switched
    xys = individual.get_positions()[:, :2]
    dists_to_columns = np.expand_dims(xys, 0) - np.transpose(np.expand_dims(column_xys, 0), (1, 0, 2))
    dists_to_columns = np.linalg.norm(dists_to_columns, axis=2)
    all_column_indices = [np.where(dists < column_cutoff)[0] for dists in dists_to_columns]
    syms = individual.get_chemical_symbols()
    if species is None:
        unique_syms = np.unique(syms)
        counts = [syms.count(sym) for sym in unique_syms]
        species = unique_syms[np.argmin(counts)]

    syms = np.asarray(syms)
    
    # In this case, CNs refers to coordination to the species, not all atoms
    CNs = np.asarray([list(syms[i]).count(species) for i in NN_list])
    
    column_indices = []
    for indices in all_column_indices:
        column_syms = syms[indices]
        unique_syms = np.unique(column_syms)
        column_CNs = CNs[indices]
        species_CNs = [CN for sym, CN in zip(column_syms, column_CNs) if sym == species]
        other_CNs = [CN for sym, CN in zip(column_syms, column_CNs) if sym != species]
        diff_CNs = np.expand_dims(species_CNs, 0) - np.expand_dims(other_CNs, 0).T
        if len(unique_syms) > 1 and not (diff_CNs >= 0).all():
            column_indices.append(indices)

    if len(column_indices) == 0:
        return False
    
    # Pick a random column and calculate coordination numbers
    column_indices = column_indices[random.randint(0, len(column_indices) - 1)]
    column_CNs = CNs[column_indices]
    species_indices_CNs = [[index, CN] for index, CN in zip(column_indices, column_CNs) if syms[index] == species]
    other_indices_CNs = [[index, CN] for index, CN in zip(column_indices, column_CNs) if syms[index] != species]

    species_indices, species_CNs = zip(*species_indices_CNs)
    other_indices, other_CNs = zip(*other_indices_CNs)

    # Probabilistically select moves that decrease the CN of the enriched species
    diff_CNs = np.expand_dims(species_CNs, 0) - np.expand_dims(other_CNs, 0).T
    moves = np.argwhere(diff_CNs < 0)
    diffs = [diff_CNs[i, j] for i, j in moves]
    diff_counts = {diff: diffs.count(diff) for diff in set(diffs)}
    moves_p = 2.0 ** (np.max(diffs) - np.array(diffs) + 1)
    moves_p /= np.array([diff_counts[diff] for diff in diffs])
    moves_p /= np.sum(moves_p)
    move = moves[np.random.choice(range(len(moves)), p=moves_p)]
    species_index, other_index = species_indices[move[1]], other_indices[move[0]]
    other_symbol = syms[other_index]

    # Apply the mutation
    individual[species_index].symbol = other_symbol
    individual[other_index].symbol = species

    return
コード例 #8
0
def twist(individual, max_radius=0.90):
    """Twists a random section of the particle.
    
    Parameters
    ----------
    individual : structopt.Individual object
        Individual to be mutated
    max_natoms : float
        That maximum relative distance from the center of the particle 
        the twist is initiated
    """

    if not len(individual):
        return None

    NNs = NeighborList(individual)
    CNs = [len(NN) for NN in NNs]

    # Get the surface atoms. These provide bounds for the moves
    # First get unit vectors and mags of all surface atoms
    positions = individual.get_positions()
    com = np.sum(positions.T, axis=1) / len(individual)
    surf_indices = [i for i, CN in enumerate(CNs) if CN < 11]
    surf_positions = np.array([positions[i] for i in surf_indices])
    surf_magnitudes = np.linalg.norm(surf_positions - com, axis=1)
    surf_vectors = (surf_positions - com) / np.array([surf_magnitudes]).T

    # Weight probability of choosing a vector by its length from the center
    surf_probabilities = surf_magnitudes / sum(surf_magnitudes)

    # Choose a random point inside the particle
    i = np.random.choice(list(range(len(surf_vectors))), p=surf_probabilities)
    center = com + surf_vectors[i] * surf_magnitudes[i] * random.random(
    ) * max_radius

    atoms = individual.copy()
    atoms.translate(-center)
    v = random_three_vector()
    a = random.uniform(30, 180) * np.pi / 180
    atoms.rotate(v=v, a=a, center=(0, 0, 0))
    new_pos_indices = []
    top_atoms = Atoms()
    for i, atom in enumerate(atoms):
        if atom.z > 0:
            new_pos_indices.append(i)
            top_atoms.extend(atom)

    xs, ys, zs = top_atoms.get_positions().T
    x = (max(xs) + min(xs)) * 0.5
    y = (max(ys) + min(ys)) * 0.5

    top_atoms.rotate('z', random.uniform(0, np.pi), center=(x, y, 0))
    top_atoms.rotate(v=v, a=-a, center=(0, 0, 0))
    top_atoms.translate(center)
    rotated_positions = top_atoms.get_positions()
    positions = individual.get_positions()
    for i, index in enumerate(new_pos_indices):
        positions[index] = rotated_positions[i]

    individual.set_positions(positions)

    return None
コード例 #9
0
def move_surface_atoms(individual, max_natoms=0.2, move_CN=11, surf_CN=11):
    """Randomly moves atoms at the surface to other surface sites

    Parameters
    ----------
    individual : structopt.Individual object
        The individual object to be modified in place
    max_natoms : float or int
        if float, the maximum number of atoms that will be moved is 
        max_natoms*len(individual)
        if int, the maximum number of atoms that will be moved is max_natoms
        default: 0.20
    max_CN : int
        The coordination number cutoff for determining which atoms are surface atoms
        Any atoms with coordnation number at or above CN will not be considered as 
        surface.

    Output
    ------
    out : None
        Modifies individual in-place
    """

    if len(individual) == 0:
        return False

    # Analyze the individual
    NNs = NeighborList(individual)
    CNs = [len(NN) for NN in NNs]

    # Get indices of atoms considered to be moved
    move_indices_CNs = [[i, CN] for i, CN in enumerate(CNs) if CN <= move_CN]
    if len(move_indices_CNs) == 0:
        return False
    move_indices_CNs.sort(key=lambda i: i[1])
    move_indices = list(zip(*move_indices_CNs))[0]

    # Get surface sites to move atoms to
    # First get all surface atoms
    positions = individual.get_positions()
    surf_indices_CNs = [[i, CN] for i, CN in enumerate(CNs)
                        if CN <= surf_CN and CN > 2]
    surf_indices_CNs.sort(key=lambda i: i[1])
    surf_indices = list(zip(*surf_indices_CNs))[0]
    surf_positions = np.array([positions[i] for i in surf_indices])

    # Get the average bond length of the particle
    chemical_symbols = individual.get_chemical_symbols()
    unique_symbols = set(chemical_symbols)
    atomlist = [[symbol, chemical_symbols.count(symbol)]
                for symbol in unique_symbols]
    avg_bond_length = get_avg_radii(atomlist) * 2

    # Choose sites as projections one bond length away from COM
    COM = surf_positions.mean(axis=0)
    vec = surf_positions - COM
    vec /= np.array([np.linalg.norm(vec, axis=1)]).T
    add_positions = surf_positions + vec * avg_bond_length * 0.5

    # Set positions of a fraction of the surface atoms
    if type(max_natoms) is float:
        max_natoms = int(max_natoms * len(move_indices))
    move_natoms = random.randint(0, max_natoms)
    move_indices = move_indices[:move_natoms]
    add_indices = np.random.choice(len(add_positions),
                                   len(move_indices),
                                   replace=False)
    for move_index, add_index in zip(move_indices, add_indices):
        positions[move_index] = add_positions[add_index]

    individual.set_positions(positions)

    return