def permute_column_surface(individual,
                           STEM_parameters,
                           filter_size=0.5,
                           column_cutoff=0.5):
    """Permutes a column by shifting atoms up and down and filling defects.
    """

    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()
    # fig.colorbar(ax.pcolormesh(image_max, cmap=cm.viridis))
    # ax.set_aspect('equal', 'box')
    # plt.show()
    # import sys; sys.exit()

    # Pick a random column and find atoms near the column
    i = random.randint(0, len(column_xys) - 1)
    column_xy = column_xys[i]

    xys = individual.get_positions()[:, :2]
    dists = np.linalg.norm(column_xy - xys, axis=1)
    indices = np.arange(len(individual))[dists < column_cutoff]

    if len(indices) < 2:
        return False

    z_positions = individual.get_positions()[indices, 2]
    top_atom = indices[np.argmax(z_positions)]
    bot_atom = indices[np.argmin(z_positions)]

    # Move the top atom the bottom atom or vice versa
    if random.random() < 0.5:
        individual[top_atom].z = individual[bot_atom].z - avg_bond_length
    else:
        individual[bot_atom].z = individual[top_atom].z + avg_bond_length

    return
def permute_column_surface(individual, STEM_parameters, filter_size=0.5,
                           column_cutoff=0.5):
    """Permutes a column by shifting atoms up and down and filling defects.
    """

    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()
    # fig.colorbar(ax.pcolormesh(image_max, cmap=cm.viridis))
    # ax.set_aspect('equal', 'box')
    # plt.show()
    # import sys; sys.exit()

    # Pick a random column and find atoms near the column
    i = random.randint(0, len(column_xys) - 1)
    column_xy = column_xys[i]

    xys = individual.get_positions()[:,:2]
    dists = np.linalg.norm(column_xy - xys, axis=1)
    indices = np.arange(len(individual))[dists < column_cutoff]

    if len(indices) < 2:
        return False

    z_positions = individual.get_positions()[indices, 2]
    top_atom = indices[np.argmax(z_positions)]
    bot_atom = indices[np.argmin(z_positions)]

    # Move the top atom the bottom atom or vice versa
    if random.random() < 0.5:
        individual[top_atom].z = individual[bot_atom].z - avg_bond_length
    else:
        individual[bot_atom].z = individual[top_atom].z + avg_bond_length

    return
def permute_column_bulk(individual, STEM_parameters, filter_size=0.5,
                        column_cutoff=0.5):
    """This mutation randomly does an atom swap within a column of atoms"""

    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
    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)
    column_indices = [np.where(dists < column_cutoff)[0] for dists in dists_to_columns]
    syms = np.asarray(individual.get_chemical_symbols())
    column_indices = [indices for indices in column_indices if len(np.unique((syms[indices]))) > 1]

    # Shuffle the indices in the atomic column
    column_indices = column_indices[random.randint(0, len(column_indices) - 1)]
    column_symbols = syms[column_indices]
    np.random.shuffle(column_symbols)
    for index, symbol in zip(column_indices, column_symbols):
        individual[index].symbol = symbol
    
    return
Exemplo n.º 4
0
def increase_Z_STEM(individual, STEM_parameters, filter_size=0.5,
                    move_cutoff=0.5, min_cutoff=0.5):
    """Increaes the proton count of an atom to match a STEM image

    Parameters
    ----------
    STEM_parameters : dict
        Parameters for the STEM calculation. Ideally should be the same as the ones
        used for the STEM fitness/relaxation
    filter_size : float
        Filter size for choosing local maximum in the picture. Filter size is equal
        to average_bond_length * resolution * filter_size.
    move_cutoff : float
        The search radius for selecting an atom to move near a high intensity point.
        Defaults to the average bond distance
    """

    module = STEM(STEM_parameters)
    module.generate_target()
    target = module.target

    image, x_shift, y_shift = module.cross_correlate(module.get_image(individual))
    contrast = image - target
    max_max = np.max(contrast)
    min_min = np.min(contrast)

    ###################################
    ## Code for testing the contrast ##
    ###################################
    # import matplotlib.pyplot as plt
    # import matplotlib.cm as cm
    # fig, ax = plt.subplots()
    # fig.colorbar(ax.pcolormesh(contrast, cmap=cm.viridis, linewidths=0))
    # ax.set_xlim((0, STEM_parameters['dimensions'][0] * STEM_parameters['resolution']))
    # ax.set_ylim((0, STEM_parameters['dimensions'][1] * STEM_parameters['resolution']))
    # plt.show()
    # import sys; sys.exit()

    # Find a list of local minimum
    cutoff = get_avg_radii(individual) * 2 * 1.1
    move_cutoff *= cutoff
    resolution = module.parameters['resolution']
    size = cutoff * resolution * filter_size

    data_min = filters.minimum_filter(contrast, size=size)
    minima = ((contrast == data_min) & (contrast < min_min * min_cutoff))
    if len(minima) == 0:
        return False

    min_coords = np.argwhere(minima)
    min_xys = (min_coords[:,::-1] - [x_shift, y_shift]) / resolution
    min_intensities = np.asarray([data_min[tuple(coord)] for coord in min_coords])
    min_intensities = np.absolute(min_intensities)
    min_intensities /= sum(min_intensities)

    ###################################
    ## Code for testing the min find ##
    ###################################
    # import matplotlib.pyplot as plt
    # import matplotlib.cm as cm
    # fig, ax = plt.subplots(num=1)
    # fig.colorbar(ax.pcolormesh(minima, cmap=cm.viridis, linewidths=0))
    # ax.set_xlim((0, STEM_parameters['dimensions'][0] * STEM_parameters['resolution']))
    # ax.set_ylim((0, STEM_parameters['dimensions'][1] * STEM_parameters['resolution']))

    # fig, ax = plt.subplots(num=2)
    # fig.colorbar(ax.pcolormesh(data_min, cmap=cm.viridis, linewidths=0))
    # ax.set_xlim((0, STEM_parameters['dimensions'][0] * STEM_parameters['resolution']))
    # ax.set_ylim((0, STEM_parameters['dimensions'][1] * STEM_parameters['resolution']))
    
    # plt.show()
    # print(len(min_intensities))
    # import sys; sys.exit()

    # Get atoms associated with each max and min column
    xys = individual.get_positions()[:, :2]

    min_dists = np.expand_dims(xys, 0) - np.transpose(np.expand_dims(min_xys, 0), (1, 0, 2))
    min_dists = np.linalg.norm(min_dists, axis=2)
    min_column_indices = [np.where(dists < move_cutoff)[0] for dists in min_dists]

    if np.size(min_column_indices) == 0:
        return False

    # Eliminate columns that cannot be "improved" by a permutation
    syms = np.asarray(individual.get_chemical_symbols())
    unique_syms = np.unique(syms)
    unique_nums = [atomic_numbers[sym] for sym in unique_syms]
    max_sym = unique_syms[np.argmax(unique_nums)]
    min_sym = unique_syms[np.argmin(unique_nums)]

    for i, indices in reversed(list(enumerate(min_column_indices))):
        if all(syms[indices] == max_sym):
            min_column_indices = np.delete(min_column_indices, i, axis=0)
            min_intensities = np.delete(min_intensities, i)
            min_intensities /= np.sum(min_intensities)

    # Pick a max column and min column based on their intensities 
    if np.size(min_column_indices) == 0:
        return False
    min_column_indices = min_column_indices[np.random.choice(np.arange(len(min_intensities)), p=min_intensities)]

    # Take out elements with atomic numbers that cannot be increased
    min_column_numbers = [atomic_numbers[sym] for sym in syms[min_column_indices]]
    min_column_indices = [i for i, Z in zip(min_column_indices, min_column_numbers) if Z < np.max(unique_nums)]
    min_column_numbers = [atomic_numbers[sym] for sym in syms[min_column_indices]]

    # Choose a random indice
    min_index = np.random.choice(min_column_indices)
    min_number = atomic_numbers[syms[min_index]]
    new_symbol = chemical_symbols[np.random.choice([n for n in unique_nums if n > min_number])]

    # Switch the atomic symbols
    individual[min_index].symbol = new_symbol

    return
Exemplo n.º 5
0
def permutation_STEM(individual,
                     STEM_parameters,
                     filter_size=0.5,
                     move_cutoff=0.5,
                     max_cutoff=0.5,
                     min_cutoff=0.5):
    """Moves surface atoms around based on the difference in the target
    and individual STEM image

    Parameters
    ----------
    STEM_parameters : dict
        Parameters for the STEM calculation. Ideally should be the same as the ones
        used for the STEM fitness/relaxation
    filter_size : float
        Filter size for choosing local maximum in the picture. Filter size is equal
        to average_bond_length * resolution * filter_size.
    move_cutoff : float
        The search radius for selecting an atom to move near a high intensity point.
        Defaults to the average bond distance
    """

    module = STEM(STEM_parameters)
    module.generate_target()
    target = module.target

    image, x_shift, y_shift = module.cross_correlate(
        module.get_image(individual))
    contrast = image - target
    max_max = np.max(contrast)
    min_min = np.min(contrast)

    ###################################
    ## Code for testing the contrast ##
    ###################################
    # import matplotlib.pyplot as plt
    # import matplotlib.cm as cm
    # fig, ax = plt.subplots()
    # fig.colorbar(ax.pcolormesh(contrast, cmap=cm.viridis, linewidths=0))
    # ax.set_xlim((0, STEM_parameters['dimensions'][0] * STEM_parameters['resolution']))
    # ax.set_ylim((0, STEM_parameters['dimensions'][1] * STEM_parameters['resolution']))
    # plt.show()
    # import sys; sys.exit()

    # Find a list of local maximum and local minimum in the image
    cutoff = get_avg_radii(individual) * 2 * 1.1
    move_cutoff *= cutoff
    resolution = module.parameters['resolution']
    size = cutoff * resolution * filter_size

    data_max = filters.maximum_filter(contrast, size=size)
    maxima = ((contrast == data_max) & (contrast > max_max * max_cutoff))
    if len(maxima) == 0:
        return False
    max_coords = np.argwhere(maxima)
    max_xys = (max_coords[:, ::-1] -
               np.array([[x_shift, y_shift]])) / resolution
    max_intensities = np.asarray(
        [data_max[tuple(coord)] for coord in max_coords])
    max_intensities /= sum(max_intensities)

    ###################################
    ## Code for testing the max find ##
    ###################################
    # import matplotlib.pyplot as plt
    # import matplotlib.cm as cm
    # fig, ax = plt.subplots()
    # fig.colorbar(ax.pcolormesh(maxima, cmap=cm.viridis, linewidths=0))
    # ax.set_xlim((0, STEM_parameters['dimensions'][0] * STEM_parameters['resolution']))
    # ax.set_ylim((0, STEM_parameters['dimensions'][1] * STEM_parameters['resolution']))
    # print(len(max_intensities))

    # fig, ax = plt.subplots(num=2)
    # fig.colorbar(ax.pcolormesh(data_max, cmap=cm.viridis, linewidths=0))
    # ax.set_xlim((0, STEM_parameters['dimensions'][0] * STEM_parameters['resolution']))
    # ax.set_ylim((0, STEM_parameters['dimensions'][1] * STEM_parameters['resolution']))

    # plt.show()
    # print(len(max_intensities))
    # import sys; sys.exit()

    data_min = filters.minimum_filter(contrast, size=size)
    minima = ((contrast == data_min) & (contrast < min_min * min_cutoff))
    if len(minima) == 0:
        return False

    min_coords = np.argwhere(minima)
    min_xys = (min_coords[:, ::-1] - [x_shift, y_shift]) / resolution
    min_intensities = np.asarray(
        [data_min[tuple(coord)] for coord in min_coords])
    min_intensities = np.absolute(min_intensities)
    min_intensities /= sum(min_intensities)

    ###################################
    ## Code for testing the min find ##
    ###################################
    # import matplotlib.pyplot as plt
    # import matplotlib.cm as cm
    # fig, ax = plt.subplots(num=1)
    # fig.colorbar(ax.pcolormesh(minima, cmap=cm.viridis, linewidths=0))
    # ax.set_xlim((0, STEM_parameters['dimensions'][0] * STEM_parameters['resolution']))
    # ax.set_ylim((0, STEM_parameters['dimensions'][1] * STEM_parameters['resolution']))

    # fig, ax = plt.subplots(num=2)
    # fig.colorbar(ax.pcolormesh(data_min, cmap=cm.viridis, linewidths=0))
    # ax.set_xlim((0, STEM_parameters['dimensions'][0] * STEM_parameters['resolution']))
    # ax.set_ylim((0, STEM_parameters['dimensions'][1] * STEM_parameters['resolution']))

    # plt.show()
    # print(len(min_intensities))
    # import sys; sys.exit()

    # Get atoms associated with each max and min column
    xys = individual.get_positions()[:, :2]
    max_dists = np.expand_dims(xys, 0) - np.transpose(
        np.expand_dims(max_xys, 0), (1, 0, 2))
    max_dists = np.linalg.norm(max_dists, axis=2)
    max_column_indices = [
        np.where(dists < move_cutoff)[0] for dists in max_dists
    ]

    min_dists = np.expand_dims(xys, 0) - np.transpose(
        np.expand_dims(min_xys, 0), (1, 0, 2))
    min_dists = np.linalg.norm(min_dists, axis=2)
    min_column_indices = [
        np.where(dists < move_cutoff)[0] for dists in min_dists
    ]

    if np.size(min_column_indices) == 0 or np.size(max_column_indices) == 0:
        return False

    # Eliminate columns that cannot be "improved" by a permutation
    syms = np.asarray(individual.get_chemical_symbols())
    unique_syms = np.unique(syms)
    unique_nums = [atomic_numbers[sym] for sym in unique_syms]
    max_sym = unique_syms[np.argmax(unique_nums)]
    min_sym = unique_syms[np.argmin(unique_nums)]

    for i, indices in reversed(list(enumerate(max_column_indices))):
        if all(syms[indices] == min_sym):
            max_column_indices = np.delete(max_column_indices, i, axis=0)
            max_intensities = np.delete(max_intensities, i)
            max_intensities /= np.sum(max_intensities)

    for i, indices in reversed(list(enumerate(min_column_indices))):
        if all(syms[indices] == max_sym):
            min_column_indices = np.delete(min_column_indices, i, axis=0)
            min_intensities = np.delete(min_intensities, i)
            min_intensities /= np.sum(min_intensities)

    # Pick a max column and min column based on their intensities
    if np.size(min_column_indices) == 0 or np.size(max_column_indices) == 0:
        return False

    max_column_indices = max_column_indices[np.random.choice(
        np.arange(len(max_intensities)), p=max_intensities)]
    min_column_indices = min_column_indices[np.random.choice(
        np.arange(len(min_intensities)), p=min_intensities)]

    # Pick a move between the two columns based on differences in atomic numbers
    max_column_numbers = [
        atomic_numbers[sym] for sym in syms[max_column_indices]
    ]
    min_column_numbers = [
        atomic_numbers[sym] for sym in syms[min_column_indices]
    ]

    max_column_numbers = np.expand_dims(max_column_numbers, 0)
    min_column_numbers = np.expand_dims(min_column_numbers, 0).T

    min_max_pairs = np.argwhere(max_column_numbers - min_column_numbers > 0)
    min_index, max_index = min_max_pairs[random.randint(
        0,
        len(min_max_pairs) - 1)]
    min_index, max_index = min_column_indices[min_index], max_column_indices[
        max_index]
    max_symbol = syms[max_index]
    min_symbol = syms[min_index]

    # Switch the atomic symbols
    individual[max_index].symbol = min_symbol
    individual[min_index].symbol = max_symbol

    return
Exemplo n.º 6
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
Exemplo n.º 7
0
def add_atom_STEM(individual,
                  STEM_parameters,
                  add_prob=None,
                  permute=0.5,
                  filter_size=1,
                  column_cutoff=0.2,
                  surf_cutoff=0.5,
                  min_cutoff=0.5):
    """Moves surface atoms around based on the difference in the target
    and individual STEM image

    Parameters
    ----------
    STEM_parameters : dict
        Parameters for the STEM calculation. Ideally should be the same as the ones
        used for the STEM fitness/relaxation
    move_CN : int
        The maximum coordination number considered as a surface atom to move.    
    surf_CN : int
        The maximum coordination number considered as a surface atom to move an
        atom to
    filter_size : float
        Filter size for choosing local maximum in the picture. Filter size is equal
        to average_bond_length * resolution * filter_size.
    surf_cutoff : float
        The search radius for selecting a surface site near a low intensity point
        Defaults to the average bond distance
    """

    module = STEM(STEM_parameters)
    module.generate_target()
    target = module.target

    image, x_shift, y_shift = module.cross_correlate(
        module.get_image(individual))
    contrast = image - target
    min_min = np.min(contrast)

    # Determine filter size for locating local minimum
    cutoff = get_avg_radii(individual) * 2 * 1.1
    surf_cutoff *= cutoff
    column_cutoff *= cutoff
    resolution = module.parameters['resolution']
    size = cutoff * resolution * filter_size

    ###################################
    ## Code for testing the contrast ##
    ###################################
    # import matplotlib.pyplot as plt
    # import matplotlib.cm as cm
    # fig, ax = plt.subplots()
    # fig.colorbar(ax.pcolormesh(contrast, cmap=cm.viridis, linewidths=0))
    # ax.set_xlim((0, STEM_parameters['dimensions'][0] * 10))
    # ax.set_ylim((0, STEM_parameters['dimensions'][1] * 10))
    # plt.show()
    # import sys; sys.exit()

    # Get xy coordinates of the minimum intensities
    data_min = filters.minimum_filter(contrast, size=size)
    minima = ((contrast == data_min) & (contrast < min_min * min_cutoff))
    if len(minima) == 0:
        return False
    min_coords = np.argwhere(minima)
    min_xys = (min_coords[:, ::-1] - [x_shift, y_shift]) / resolution
    min_intensities = np.asarray(
        [data_min[tuple(coord)] for coord in min_coords])
    min_intensities = np.absolute(min_intensities)
    min_intensities /= sum(min_intensities)

    ###################################
    ## Code for testing the min find ##
    ###################################
    # import matplotlib.pyplot as plt
    # import matplotlib.cm as cm
    # fig, ax = plt.subplots()
    # fig.colorbar(ax.pcolormesh(data_min, cmap=cm.viridis, linewidths=0))
    # ax.set_xlim((0, STEM_parameters['dimensions'][0] * 10))
    # ax.set_ylim((0, STEM_parameters['dimensions'][1] * 10))
    # plt.show()
    # print(len(min_intensities))
    # import sys; sys.exit()

    # Randomly choose local maxima and minima locations from contrast weighted
    # by their intensity
    low_xy_index = np.random.choice(np.arange(len(min_xys)), p=min_intensities)
    low_xy = min_xys[low_xy_index]

    # Get indices of atoms considered to be moved and sites to move to
    # Organize atoms into columns
    pos = individual.get_positions()
    xys = np.expand_dims(pos[:, :2], 0)
    dists = np.linalg.norm(xys - np.transpose(xys, (1, 0, 2)), axis=2)

    NNs = np.sort(np.argwhere(dists < column_cutoff))
    column_indices = []
    atoms_to_be_sorted = list(range(len(individual)))
    while len(atoms_to_be_sorted) > 0:
        i = atoms_to_be_sorted[0]
        same_column_indices = np.unique(NNs[NNs[:, 0] == i])
        column_indices.append(same_column_indices)
        for j in reversed(sorted(same_column_indices)):
            i_del = atoms_to_be_sorted.index(j)
            atoms_to_be_sorted.pop(i_del)
            NNs = NNs[NNs[:, 0] != j]
            NNs = NNs[NNs[:, 1] != j]

    # Make a list of the top and bottom atom of each column as well
    # the average bond length of atoms in the column
    top_indices, bot_indices, avg_bond_lengths = [], [], []
    for indices in column_indices:
        zs = pos[indices][:, 2]
        top_indices.append(indices[np.argmax(zs)])
        bot_indices.append(indices[np.argmin(zs)])

        zs = np.sort(zs)
        if len(zs) == 1:
            avg_bond_lengths.append(np.nan)
        else:
            avg_bond_length = np.average(
                [zs[i + 1] - zs[i] for i in range(len(zs) - 1)])
            avg_bond_lengths.append(avg_bond_length)

    avg_bond_lengths = np.array(avg_bond_lengths)
    avg_bond_length = np.average(avg_bond_lengths[np.invert(
        np.isnan(avg_bond_lengths))])
    avg_bond_lengths[np.isnan(avg_bond_lengths)] = avg_bond_length
    avg_bond_lengths = np.append(np.zeros((len(avg_bond_lengths), 2)),
                                 np.expand_dims(avg_bond_lengths, 1),
                                 axis=1)

    # Create a list of new surface sites
    bot_new_pos = pos[np.array(bot_indices)] - avg_bond_lengths * 0.95
    top_new_pos = pos[np.array(top_indices)] + avg_bond_lengths * 0.95
    surf_positions = np.concatenate((bot_new_pos, top_new_pos), axis=0)
    surf_xys = surf_positions[:, :2]

    dists_surf_xys = np.linalg.norm(surf_xys - low_xy, axis=1)
    indices_surf_xys = [
        i for i, d in enumerate(dists_surf_xys) if d < surf_cutoff
    ]

    ########################
    ## Test atoms to move ##
    ########################
    # from ase import Atom
    # from ase.visualize import view
    # for i in indices_surf_xys:
    #     individual.append(Atom('Mo', surf_positions[i]))
    # view(individual)
    # import sys; sys.exit()

    # Pick the surface site to add the atom to
    if len(indices_surf_xys) == 0:
        surf_index = np.argmin(dists_surf_xys)
    else:
        surf_index = random.choice(indices_surf_xys)
    new_position = surf_positions[surf_index]
    new_xy = new_position[:2]

    # Choose the element to add
    if add_prob is None:
        syms = individual.get_chemical_symbols()
        elements = np.unique(syms)
        n = len(syms)
        p = [syms.count(element) / n for element in elements]
    else:
        elements = [key for key in add_prob]
        p = [add_prob[key] for key in elements]

    element = np.random.choice(elements, p=p)

    # Now maybe switch the surface element with a bulk element
    if random.random() < permute:
        individual.append(Atom(element, new_position))
        return

    xys = individual.get_positions()[:, :2]
    syms = individual.get_chemical_symbols()
    dists_xys = np.linalg.norm(xys - new_xy, axis=1)
    indices_to_switch = [
        i for i, d in enumerate(dists_xys)
        if d < column_cutoff and syms[i] != element
    ]

    if len(indices_to_switch) == 0:
        individual.append(Atom(element, new_position))
        return

    index_to_switch = random.choice(indices_to_switch)
    element_to_switch = syms[index_to_switch]
    individual[index_to_switch].symbol = element
    individual.append(Atom(element_to_switch, new_position))

    return
Exemplo n.º 8
0
def move_surface_STEM(individual, STEM_parameters, move_CN=11, surf_CN=11,
                      filter_size=1, move_cutoff=0.5, surf_cutoff=0.5,
                      max_cutoff=0.5, min_cutoff=0.5):
    """Moves surface atoms around based on the difference in the target
    and individual STEM image

    Parameters
    ----------
    STEM_parameters : dict
        Parameters for the STEM calculation. Ideally should be the same as the ones
        used for the STEM fitness/relaxation
    move_CN : int
        The maximum coordination number considered as a surface atom to move.    
    surf_CN : int
        The maximum coordination number considered as a surface atom to move an
        atom to
    filter_size : float
        Filter size for choosing local maximum in the picture. Filter size is equal
        to average_bond_length * resolution * filter_size.
    move_cutoff : float
        The search radius for selecting an atom to move near a high intensity point.
        Defaults to the average bond distance
    surf_cutoff : float
        The search radius for selecting a surface site near a low intensity point
        Defaults to the average bond distance
    """

    module = STEM({'kwargs': STEM_parameters})
    module.generate_target()
    target = module.target

    image, x_shift, y_shift = module.cross_correlate(module.get_image(individual))
    contrast = image - target
    max_max = np.max(contrast)
    min_min = np.min(contrast)

    ###################################
    ## Code for testing the contrast ##
    ###################################
    # import matplotlib.pyplot as plt
    # import matplotlib.cm as cm
    # fig, ax = plt.subplots()
    # fig.colorbar(ax.pcolormesh(contrast, cmap=cm.viridis, linewidths=0))
    # ax.set_xlim((0, STEM_parameters['dimensions'][0] * 10))
    # ax.set_ylim((0, STEM_parameters['dimensions'][1] * 10))
    # plt.show()
    # import sys; sys.exit()

    # Find a list of local maximum and local minimum in the image
    cutoff = get_avg_radii(individual) * 2 * 1.1
    move_cutoff *= cutoff
    surf_cutoff *= cutoff
    resolution = module.parameters['kwargs']['resolution']
    size = cutoff * resolution * filter_size

    data_max = filters.maximum_filter(contrast, size=size)
    maxima = ((contrast == data_max) & (contrast > max_max * max_cutoff))
    if len(maxima) == 0:
        return False
    max_coords = np.argwhere(maxima)
    max_xys = (max_coords[:,::-1] - np.array([[x_shift, y_shift]])) / resolution
    max_intensities = np.asarray([data_max[tuple(coord)] for coord in max_coords])
    max_intensities /= sum(max_intensities)

    ###################################
    ## Code for testing the max find ##
    ###################################
    # import matplotlib.pyplot as plt
    # import matplotlib.cm as cm
    # fig, ax = plt.subplots()
    # fig.colorbar(ax.pcolormesh(maxima, cmap=cm.viridis, linewidths=0))
    # ax.set_xlim((0, STEM_parameters['dimensions'][0] * 10))
    # ax.set_ylim((0, STEM_parameters['dimensions'][1] * 10))
    # plt.show()
    # print(len(max_intensities))
    # import sys; sys.exit()

    data_min = filters.minimum_filter(contrast, size=size)
    minima = ((contrast == data_min) & (contrast < min_min * min_cutoff))
    if len(minima) == 0:
        return False
    min_coords = np.argwhere(minima)
    min_xys = (min_coords[:,::-1] - [x_shift, y_shift]) / resolution
    min_intensities = np.asarray([data_min[tuple(coord)] for coord in min_coords])
    min_intensities = np.absolute(min_intensities)
    min_intensities /= sum(min_intensities)

    ###################################
    ## Code for testing the min find ##
    ###################################
    # import matplotlib.pyplot as plt
    # import matplotlib.cm as cm
    # fig, ax = plt.subplots()
    # fig.colorbar(ax.pcolormesh(data_min, cmap=cm.viridis, linewidths=0))
    # ax.set_xlim((0, STEM_parameters['dimensions'][0] * 10))
    # ax.set_ylim((0, STEM_parameters['dimensions'][1] * 10))
    # plt.show()
    # print(len(min_intensities))
    # import sys; sys.exit()

    # Get indices of atoms considered to be moved and sites to move too
    CNs = CoordinationNumbers(individual)
    positions = individual.get_positions()

    move_indices = [i for i, CN in enumerate(CNs) if CN <= move_CN]
    move_xys = positions[list(move_indices)][:, :2]

    surf_indices = [i for i, CN in enumerate(CNs) if CN <= surf_CN]
    surf_positions = positions[list(surf_indices)]
    COM = surf_positions.mean(axis=0)
    vec = surf_positions - COM
    vec /= np.array([np.linalg.norm(vec, axis=1)]).T
    epsilon = np.array([np.random.random(len(surf_positions)) * 0.5 + 0.5]).T
    surf_positions = surf_positions + vec * cutoff * epsilon
    surf_xys = surf_positions[:, :2]

    # Randomly choose local maxima and minima locations from contrast weighted
    # by their intensity
    high_xy_index = np.random.choice(np.arange(len(max_xys)), p=max_intensities)
    low_xy_index = np.random.choice(np.arange(len(min_xys)), p=min_intensities)
    high_xy = max_xys[high_xy_index]
    low_xy = min_xys[low_xy_index]

    # Choose move_atom (surf_atom) from within the move_cutoff (surf_cutoff)
    # of high_xy (low_xy)
    dists_move_xys = np.linalg.norm(move_xys - high_xy, axis=1)
    indices_move_xys = [i for i, d in zip(move_indices, dists_move_xys) if d < move_cutoff]

    ########################
    ## Test atoms to move ##
    ########################
    # from ase.visualize import view
    # for i in indices_move_xys:
    #     individual[i].symbol = 'Mo'
    # view(individual)
    # import sys; sys.exit()

    if len(indices_move_xys) == 0:
        move_index = np.argmin(dists_move_xys)
    else:
        move_index = random.choice(indices_move_xys)

    dists_surf_xys = np.linalg.norm(surf_xys - low_xy, axis=1)
    indices_surf_xys = [i for i, d in enumerate(dists_surf_xys) if d < surf_cutoff]

    ########################
    ## Test atoms to move ##
    ########################
    # from ase import Atom
    # from ase.visualize import view
    # for i in indices_surf_xys:
    #     individual.append(Atom('Mo', surf_positions[i]))
    # view(individual)
    # import sys; sys.exit()

    if len(indices_surf_xys) == 0:
        surf_index = np.argmin(dists_surf_xys)
    else:
        surf_index = random.choice(indices_surf_xys)

    new_position = surf_positions[surf_index]

    positions[move_index] = new_position
    individual.set_positions(positions)

    return
Exemplo n.º 9
0
def permute_column_STEM(individual, STEM_parameters, filter_size=1,
                        column_cutoff=0.5, max_cutoff=0.5):
    """Permutes a column based on the misalignment of columns between
    the individual and target.

    """

    module = STEM(STEM_parameters)
    module.generate_target()
    target = module.target

    image, x_shift, y_shift = module.cross_correlate(module.get_image(individual))
    contrast = image - target
    max_max = np.max(contrast)
    min_min = np.min(contrast)

    # Find a list of local maximum and local minimum in the image
    cutoff = get_avg_radii(individual) * 2 * 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))
    columns = np.argwhere(columns)
    columns = (columns[:, ::-1] - x_shift, y_shift)    

    data_max = filters.maximum_filter(contrast, size=size)
    maxima = ((contrast == data_max) & (contrast > 0.01))
    if len(maxima) == 0:
        return False
    max_coords = np.argwhere(maxima)
    max_xys = (max_coords[:,::-1] - np.array([[x_shift, y_shift]])) / resolution
    max_intensities = np.asarray([data_max[tuple(coord)] for coord in max_coords])
    max_intensities /= sum(max_intensities)

    ###################################
    ## Code for testing the max find ##
    ###################################
    # import matplotlib.pyplot as plt
    # import matplotlib.cm as cm
    # fig, ax = plt.subplots()
    # fig.colorbar(ax.pcolormesh(maxima, cmap=cm.viridis, linewidths=0))
    # ax.set_xlim((0, STEM_parameters['dimensions'][0] * 10))
    # ax.set_ylim((0, STEM_parameters['dimensions'][1] * 10))
    # plt.show()
    # print(len(max_intensities))
    # import sys; sys.exit()

    data_min = filters.minimum_filter(contrast, size=size)
    minima = ((contrast == data_min) & (contrast < -0.01))
    if len(minima) == 0:
        return False
    min_coords = np.argwhere(minima)
    min_xys = (min_coords[:,::-1] - [x_shift, y_shift]) / resolution
    min_intensities = np.asarray([data_min[tuple(coord)] for coord in min_coords])
    min_intensities = np.absolute(min_intensities)
    min_intensities /= sum(min_intensities)

    ###################################
    ## Code for testing the min find ##
    ###################################
    # import matplotlib.pyplot as plt
    # import matplotlib.cm as cm
    # fig, ax = plt.subplots()
    # fig.colorbar(ax.pcolormesh(data_min, cmap=cm.viridis, linewidths=0))
    # ax.set_xlim((0, STEM_parameters['dimensions'][0] * 10))
    # ax.set_ylim((0, STEM_parameters['dimensions'][1] * 10))
    # plt.show()
    # print(len(min_intensities))
    # import sys; sys.exit()

    # Find areas where where max and min are next to each other
    mins = np.array([min_xys])
    maxs = np.transpose(np.array([max_xys]), [1, 0, 2])
    vecs = mins - maxs
    dists = np.linalg.norm(vecs, axis=2)

    bonds = (dists < column_cutoff).astype(int)
    CNs = np.sum(bonds, axis=1)
    max_xys = max_xys[CNs > 0]
    max_intensities = max_intensities[CNs > 0] ** 10
    print(max_intensities / sum(max_intensities))

    columns = np.zeros(image.shape)
    for loc in max_xys:
        x, y = loc * resolution
        columns[y][x] = 1
    
    import matplotlib.pyplot as plt
    import matplotlib.cm as cm

    fig, ax = plt.subplots(num=3)
    fig.colorbar(ax.pcolormesh(columns, cmap=cm.viridis, linewidths=0))
    ax.set_xlim((0, STEM_parameters['dimensions'][0] * 10))
    ax.set_ylim((0, STEM_parameters['dimensions'][1] * 10))    
        
    plt.show()
    print(len(min_intensities))
    import sys; sys.exit()
Exemplo n.º 10
0
def remove_atom_STEM(individual,
                     STEM_parameters,
                     permute=True,
                     remove_prob=None,
                     filter_size=1,
                     remove_CN=11,
                     remove_cutoff=0.5,
                     max_cutoff=0.5,
                     column_cutoff=0.2):
    """Moves surface atoms around based on the difference in the target
    and individual STEM image

    Parameters
    ----------
    STEM_parameters : dict
        Parameters for the STEM calculation. Ideally should be the same as the ones
        used for the STEM fitness/relaxation
    remove_CN : int
        The maximum coordination number considered as a surface atom to remove.    
    surf_CN : int
        The maximum coordination number considered as a surface atom to move an
        atom to
    filter_size : float
        Filter size for choosing local maximum in the picture. Filter size is equal
        to average_bond_length * resolution * filter_size.
    move_cutoff : float
        The search radius for selecting an atom to move near a high intensity point.
        Defaults to the average bond distance
    surf_cutoff : float
        The search radius for selecting a surface site near a low intensity point
        Defaults to the average bond distance
    """

    module = STEM(STEM_parameters)
    module.generate_target()
    target = module.target

    image, x_shift, y_shift = module.cross_correlate(
        module.get_image(individual))
    contrast = image - target
    max_max = np.max(contrast)

    # Determine filter size for locating local minimum
    cutoff = get_avg_radii(individual) * 2 * 1.1
    remove_cutoff *= cutoff
    column_cutoff *= cutoff
    resolution = module.parameters['resolution']
    size = cutoff * resolution * filter_size

    ###################################
    ## Code for testing the contrast ##
    ###################################
    # import matplotlib.pyplot as plt
    # import matplotlib.cm as cm
    # fig, ax = plt.subplots()
    # fig.colorbar(ax.pcolormesh(contrast, cmap=cm.viridis, linewidths=0))
    # ax.set_xlim((0, STEM_parameters['dimensions'][0] * 10))
    # ax.set_ylim((0, STEM_parameters['dimensions'][1] * 10))
    # plt.show()
    # import sys; sys.exit()

    data_max = filters.maximum_filter(contrast, size=size)
    maxima = ((contrast == data_max) & (contrast > max_max * max_cutoff))
    if len(maxima) == 0:
        return False
    max_coords = np.argwhere(maxima)
    max_xys = (max_coords[:, ::-1] -
               np.array([[x_shift, y_shift]])) / resolution
    max_intensities = np.asarray(
        [data_max[tuple(coord)] for coord in max_coords])
    max_intensities /= sum(max_intensities)

    ###################################
    ## Code for testing the max find ##
    ###################################
    # import matplotlib.pyplot as plt
    # import matplotlib.cm as cm
    # fig, ax = plt.subplots()
    # fig.colorbar(ax.pcolormesh(maxima, cmap=cm.viridis, linewidths=0))
    # ax.set_xlim((0, STEM_parameters['dimensions'][0] * 10))
    # ax.set_ylim((0, STEM_parameters['dimensions'][1] * 10))
    # plt.show()
    # print(len(max_intensities))
    # import sys; sys.exit()

    # Get indices of atoms considered to be moved and sites to move too
    CNs = CoordinationNumbers(individual)
    positions = individual.get_positions()

    remove_indices = [i for i, CN in enumerate(CNs) if CN <= remove_CN]
    remove_xys = positions[list(remove_indices)][:, :2]

    # Randomly choose local maxima and minima locations from contrast weighted
    # by their intensity
    high_xy_index = np.random.choice(np.arange(len(max_xys)),
                                     p=max_intensities)
    high_xy = max_xys[high_xy_index]

    # Choose move_atom (surf_atom) from within the move_cutoff (surf_cutoff)
    # of high_xy (low_xy)
    dists_remove_xys = np.linalg.norm(remove_xys - high_xy, axis=1)
    indices_remove_xys = [
        i for i, d in zip(remove_indices, dists_remove_xys)
        if d < remove_cutoff
    ]

    ########################
    ## Test atoms to move ##
    ########################
    # from ase.visualize import view
    # for i in indices_move_xys:
    #     individual[i].symbol = 'Mo'
    # view(individual)
    # import sys; sys.exit()

    if len(indices_remove_xys) == 0:
        remove_index = np.argmin(dists_remove_xys)
    else:
        remove_index = random.choice(indices_remove_xys)

    remove_xy = positions[remove_index][:2]
    syms = individual.get_chemical_symbols()
    remove_element = syms[remove_index]

    # Sometimes the element at the surface isn't what we want to remove
    # So flip a random element in the same column to the one that
    if permute == False:
        individual.pop(remove_index)
        return

    # Choose an element to remove. If its the same as the one
    # we're already removing, just remove it and return
    if remove_prob is None:
        syms = individual.get_chemical_symbols()
        elements = np.unique(syms)
        n = len(syms)
        p = [syms.count(element) / n for element in elements]
    else:
        elements = [key for key in remove_prob]
        p = [remove_prob[key] for key in elements]

    element = np.random.choice(elements, p=p)
    if element == remove_element:
        individual.pop(remove_index)
        return

    # If the element is not the same, permute the column so the surface
    # atom to removed is the element chosen to be removed. If the column
    # doesn't contain the element to remove, then just remove it
    xys = positions[:, :2]
    dists_xys = np.linalg.norm(xys - remove_xy, axis=1)
    indices_to_switch = [
        i for i, d in enumerate(dists_xys)
        if d < column_cutoff and syms[i] == element
    ]
    if len(indices_to_switch) == 0:
        individual.pop(remove_index)
        return

    index_to_switch = random.choice(indices_to_switch)
    individual[index_to_switch].symbol = remove_element
    individual.pop(remove_index)

    return
def permute_column_bulk(individual,
                        STEM_parameters,
                        filter_size=0.5,
                        column_cutoff=0.5):
    """This mutation randomly does an atom swap within a column of atoms"""

    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
    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)
    column_indices = [
        np.where(dists < column_cutoff)[0] for dists in dists_to_columns
    ]
    syms = np.asarray(individual.get_chemical_symbols())
    column_indices = [
        indices for indices in column_indices
        if len(np.unique((syms[indices]))) > 1
    ]

    # Shuffle the indices in the atomic column
    column_indices = column_indices[random.randint(0, len(column_indices) - 1)]
    column_symbols = syms[column_indices]
    np.random.shuffle(column_symbols)
    for index, symbol in zip(column_indices, column_symbols):
        individual[index].symbol = symbol

    return
Exemplo n.º 12
0
def remove_atom_STEM(individual, STEM_parameters, permute=True, remove_prob=None,
                     filter_size=1, remove_CN=11, remove_cutoff=0.5, max_cutoff=0.5,
                     column_cutoff=0.2):

    """Moves surface atoms around based on the difference in the target
    and individual STEM image

    Parameters
    ----------
    STEM_parameters : dict
        Parameters for the STEM calculation. Ideally should be the same as the ones
        used for the STEM fitness/relaxation
    remove_CN : int
        The maximum coordination number considered as a surface atom to remove.    
    surf_CN : int
        The maximum coordination number considered as a surface atom to move an
        atom to
    filter_size : float
        Filter size for choosing local maximum in the picture. Filter size is equal
        to average_bond_length * resolution * filter_size.
    move_cutoff : float
        The search radius for selecting an atom to move near a high intensity point.
        Defaults to the average bond distance
    surf_cutoff : float
        The search radius for selecting a surface site near a low intensity point
        Defaults to the average bond distance
    """

    module = STEM(STEM_parameters)
    module.generate_target()
    target = module.target

    image, x_shift, y_shift = module.cross_correlate(module.get_image(individual))
    contrast = image - target
    max_max = np.max(contrast)

    # Determine filter size for locating local minimum
    cutoff = get_avg_radii(individual) * 2 * 1.1
    remove_cutoff *= cutoff
    column_cutoff *= cutoff
    resolution = module.parameters['resolution']
    size = cutoff * resolution * filter_size    

    ###################################
    ## Code for testing the contrast ##
    ###################################
    # import matplotlib.pyplot as plt
    # import matplotlib.cm as cm
    # fig, ax = plt.subplots()
    # fig.colorbar(ax.pcolormesh(contrast, cmap=cm.viridis, linewidths=0))
    # ax.set_xlim((0, STEM_parameters['dimensions'][0] * 10))
    # ax.set_ylim((0, STEM_parameters['dimensions'][1] * 10))
    # plt.show()
    # import sys; sys.exit()

    data_max = filters.maximum_filter(contrast, size=size)
    maxima = ((contrast == data_max) & (contrast > max_max * max_cutoff))
    if len(maxima) == 0:
        return False
    max_coords = np.argwhere(maxima)
    max_xys = (max_coords[:,::-1] - np.array([[x_shift, y_shift]])) / resolution
    max_intensities = np.asarray([data_max[tuple(coord)] for coord in max_coords])
    max_intensities /= sum(max_intensities)

    ###################################
    ## Code for testing the max find ##
    ###################################
    # import matplotlib.pyplot as plt
    # import matplotlib.cm as cm
    # fig, ax = plt.subplots()
    # fig.colorbar(ax.pcolormesh(maxima, cmap=cm.viridis, linewidths=0))
    # ax.set_xlim((0, STEM_parameters['dimensions'][0] * 10))
    # ax.set_ylim((0, STEM_parameters['dimensions'][1] * 10))
    # plt.show()
    # print(len(max_intensities))
    # import sys; sys.exit()

    # Get indices of atoms considered to be moved and sites to move too
    CNs = CoordinationNumbers(individual)
    positions = individual.get_positions()

    remove_indices = [i for i, CN in enumerate(CNs) if CN <= remove_CN]
    remove_xys = positions[list(remove_indices)][:, :2]

    # Randomly choose local maxima and minima locations from contrast weighted
    # by their intensity
    high_xy_index = np.random.choice(np.arange(len(max_xys)), p=max_intensities)
    high_xy = max_xys[high_xy_index]

    # Choose move_atom (surf_atom) from within the move_cutoff (surf_cutoff)
    # of high_xy (low_xy)
    dists_remove_xys = np.linalg.norm(remove_xys - high_xy, axis=1)
    indices_remove_xys = [i for i, d in zip(remove_indices, dists_remove_xys) if d < remove_cutoff]

    ########################
    ## Test atoms to move ##
    ########################
    # from ase.visualize import view
    # for i in indices_move_xys:
    #     individual[i].symbol = 'Mo'
    # view(individual)
    # import sys; sys.exit()

    if len(indices_remove_xys) == 0:
        remove_index = np.argmin(dists_remove_xys)
    else:
        remove_index = random.choice(indices_remove_xys)

    remove_xy = positions[remove_index][:2]
    syms = individual.get_chemical_symbols()
    remove_element = syms[remove_index]

    # Sometimes the element at the surface isn't what we want to remove
    # So flip a random element in the same column to the one that
    if permute == False:
        individual.pop(remove_index)
        return

    # Choose an element to remove. If its the same as the one
    # we're already removing, just remove it and return
    if remove_prob is None:
        syms = individual.get_chemical_symbols()
        elements = np.unique(syms)
        n = len(syms)
        p = [syms.count(element) / n for element in elements]
    else:
        elements = [key for key in remove_prob]
        p = [remove_prob[key] for key in elements]

    element = np.random.choice(elements, p=p)
    if element == remove_element:
        individual.pop(remove_index)
        return

    # If the element is not the same, permute the column so the surface
    # atom to removed is the element chosen to be removed. If the column
    # doesn't contain the element to remove, then just remove it
    xys = positions[:,:2]
    dists_xys = np.linalg.norm(xys - remove_xy, axis=1)
    indices_to_switch = [i for i, d in enumerate(dists_xys) if d < column_cutoff and syms[i] == element]
    if len(indices_to_switch) == 0:
        individual.pop(remove_index)
        return

    index_to_switch = random.choice(indices_to_switch)
    individual[index_to_switch].symbol = remove_element
    individual.pop(remove_index)

    return
Exemplo n.º 13
0
def add_atom_STEM(individual, STEM_parameters, add_prob=None, permute=0.5, 
                  filter_size=1, column_cutoff=0.2, surf_cutoff=0.5, min_cutoff=0.5):

    """Moves surface atoms around based on the difference in the target
    and individual STEM image

    Parameters
    ----------
    STEM_parameters : dict
        Parameters for the STEM calculation. Ideally should be the same as the ones
        used for the STEM fitness/relaxation
    move_CN : int
        The maximum coordination number considered as a surface atom to move.    
    surf_CN : int
        The maximum coordination number considered as a surface atom to move an
        atom to
    filter_size : float
        Filter size for choosing local maximum in the picture. Filter size is equal
        to average_bond_length * resolution * filter_size.
    surf_cutoff : float
        The search radius for selecting a surface site near a low intensity point
        Defaults to the average bond distance
    """

    module = STEM({'kwargs': STEM_parameters})
    module.generate_target()
    target = module.target

    image, x_shift, y_shift = module.cross_correlate(module.get_image(individual))
    contrast = image - target
    min_min = np.min(contrast)

    # Determine filter size for locating local minimum
    avg_bond_length = get_avg_radii(individual) * 2
    cutoff = avg_bond_length * 1.1
    surf_cutoff *= cutoff
    column_cutoff *= cutoff
    resolution = module.parameters['kwargs']['resolution']
    size = cutoff * resolution * filter_size

    ###################################
    ## Code for testing the contrast ##
    ###################################
    # import matplotlib.pyplot as plt
    # import matplotlib.cm as cm
    # fig, ax = plt.subplots()
    # fig.colorbar(ax.pcolormesh(contrast, cmap=cm.viridis, linewidths=0))
    # ax.set_xlim((0, STEM_parameters['dimensions'][0]*STEM_parameters['resolution']))
    # ax.set_ylim((0, STEM_parameters['dimensions'][1]*STEM_parameters['resolution']))
    # plt.show()
    # import sys; sys.exit()

    # Get xy coordinates of the minimum intensities
    data_min = filters.minimum_filter(contrast, size=size)
    minima = ((contrast == data_min) & (contrast < min_min * min_cutoff))
    if len(minima) == 0:
        return False
    min_coords = np.argwhere(minima)
    min_xys = (min_coords[:,::-1] - [x_shift, y_shift]) / resolution
    min_intensities = np.asarray([data_min[tuple(coord)] for coord in min_coords])
    min_intensities = np.absolute(min_intensities)
    min_intensities /= sum(min_intensities)

    ###################################
    ## Code for testing the min find ##
    ###################################
    # import matplotlib.pyplot as plt
    # import matplotlib.cm as cm
    # fig, ax = plt.subplots()
    # fig.colorbar(ax.pcolormesh(minima, cmap=cm.viridis, linewidths=0))
    # ax.set_xlim((0, STEM_parameters['dimensions'][0]*STEM_parameters['resolution']))
    # ax.set_ylim((0, STEM_parameters['dimensions'][1]*STEM_parameters['resolution']))
    # plt.show()
    # print(len(min_intensities))
    # import sys; sys.exit()

    # Randomly choose local maxima and minima locations from contrast weighted
    # by their intensity
    low_xy_index = np.random.choice(np.arange(len(min_xys)), p=min_intensities)
    low_xy = min_xys[low_xy_index]

    # Get indices of atoms considered to be moved and sites to move to
    # Organize atoms into columns
    pos = individual.get_positions()
    xys = np.expand_dims(pos[:, :2], 0)
    dists = np.linalg.norm(xys - np.transpose(xys, (1, 0, 2)), axis=2)

    NNs = np.sort(np.argwhere(dists < column_cutoff))
    column_indices = []
    atoms_to_be_sorted = list(range(len(individual)))
    while len(atoms_to_be_sorted) > 0:
        i = atoms_to_be_sorted[0]
        same_column_indices = np.unique(NNs[NNs[:,0] == i])
        column_indices.append(same_column_indices)
        for j in reversed(sorted(same_column_indices)):
            i_del = atoms_to_be_sorted.index(j)
            atoms_to_be_sorted.pop(i_del)
            NNs = NNs[NNs[:,0] != j]
            NNs = NNs[NNs[:,1] != j]

    # Make a list of the top and bottom atom of each column as well
    # the average bond length of atoms in the column
    top_indices, bot_indices, avg_bond_lengths = [], [], []
    vac_new_pos = []
    for indices in column_indices:
        zs = pos[indices][:,2]
        top_indices.append(indices[np.argmax(zs)])
        bot_indices.append(indices[np.argmin(zs)])

        # Get the average bond length of each column for adding
        zs = np.sort(zs)
        if len(zs) == 1:
            avg_bond_lengths.append(np.nan)
            continue
        else:
            avg_length = np.average([zs[i+1] - zs[i] for i in range(len(zs)-1)
                                     if zs[i+1] - zs[i] < avg_bond_length * 1.7])
            avg_bond_lengths.append(avg_length)

        # Check for vacancies in the column
        for i, z in enumerate(zs[:-1]):
            diff = zs[i+1] - z
            if diff < avg_length * 1.5:
                continue
            z += avg_bond_length
            xy = pos[indices][:,:2].mean(axis=0)
            vac_new_pos.append(np.append(xy, z))

    avg_bond_lengths = np.array(avg_bond_lengths)
    avg_bond_length = np.average(avg_bond_lengths[np.invert(np.isnan(avg_bond_lengths))])
    avg_bond_lengths[np.isnan(avg_bond_lengths)] = avg_bond_length
    avg_bond_lengths = np.append(np.zeros((len(avg_bond_lengths), 2)), np.expand_dims(avg_bond_lengths, 1),  axis=1)

    # Create a list of new surface sites
    bot_new_pos = pos[np.array(bot_indices)] - avg_bond_lengths * 0.95
    top_new_pos = pos[np.array(top_indices)] + avg_bond_lengths * 0.95
    if np.size(vac_new_pos) > 0:
        surf_positions = np.concatenate((bot_new_pos, top_new_pos, vac_new_pos), axis=0)
    else:
        surf_positions = np.concatenate((bot_new_pos, top_new_pos), axis=0)

    surf_xys = surf_positions[:,:2]

    dists_surf_xys = np.linalg.norm(surf_xys - low_xy, axis=1)
    indices_surf_xys = [i for i, d in enumerate(dists_surf_xys) if d < surf_cutoff]

    ########################
    ## Test atoms to move ##
    ########################
    # from ase import Atom
    # from ase.visualize import view
    # for i in indices_surf_xys:
    #     individual.append(Atom('Mo', surf_positions[i]))
    # view(individual)
    # import sys; sys.exit()

    # Pick the surface site to add the atom to
    if len(indices_surf_xys) == 0:
        surf_index = np.argmin(dists_surf_xys)
    else:
        surf_index = random.choice(indices_surf_xys)
    new_position = surf_positions[surf_index]
    new_xy = new_position[:2]

    # Choose the element to add
    if add_prob is None:
        syms = individual.get_chemical_symbols()
        elements = np.unique(syms)
        n = len(syms)
        p = [syms.count(element) / n for element in elements]
    else:
        elements = [key for key in add_prob]
        p = [add_prob[key] for key in elements]

    element = np.random.choice(elements, p=p)

    # Now maybe switch the surface element with a bulk element
    if random.random() < permute:
        individual.append(Atom(element, new_position))
        return

    xys = individual.get_positions()[:,:2]
    syms = individual.get_chemical_symbols()
    dists_xys = np.linalg.norm(xys - new_xy, axis=1)
    indices_to_switch = [i for i, d in enumerate(dists_xys) if d < column_cutoff and syms[i] != element]

    if len(indices_to_switch) == 0:
        individual.append(Atom(element, new_position))
        return

    index_to_switch = random.choice(indices_to_switch)
    element_to_switch = syms[index_to_switch]
    individual[index_to_switch].symbol = element
    individual.extend(Atoms([Atom(element_to_switch, new_position)]))

    return
Exemplo n.º 14
0
def move_surface_STEM(individual,
                      STEM_parameters,
                      move_CN=11,
                      surf_CN=11,
                      filter_size=1,
                      move_cutoff=0.5,
                      surf_cutoff=0.5,
                      max_cutoff=0.5,
                      min_cutoff=0.5):
    """Moves surface atoms around based on the difference in the target
    and individual STEM image

    Parameters
    ----------
    STEM_parameters : dict
        Parameters for the STEM calculation. Ideally should be the same as the ones
        used for the STEM fitness/relaxation
    move_CN : int
        The maximum coordination number considered as a surface atom to move.    
    surf_CN : int
        The maximum coordination number considered as a surface atom to move an
        atom to
    filter_size : float
        Filter size for choosing local maximum in the picture. Filter size is equal
        to average_bond_length * resolution * filter_size.
    move_cutoff : float
        The search radius for selecting an atom to move near a high intensity point.
        Defaults to the average bond distance
    surf_cutoff : float
        The search radius for selecting a surface site near a low intensity point
        Defaults to the average bond distance
    """

    module = STEM(STEM_parameters)
    module.generate_target()
    target = module.target

    image, x_shift, y_shift = module.cross_correlate(
        module.get_image(individual))
    contrast = image - target
    max_max = np.max(contrast)
    min_min = np.min(contrast)

    ###################################
    ## Code for testing the contrast ##
    ###################################
    # import matplotlib.pyplot as plt
    # import matplotlib.cm as cm
    # fig, ax = plt.subplots()
    # fig.colorbar(ax.pcolormesh(contrast, cmap=cm.viridis, linewidths=0))
    # ax.set_xlim((0, STEM_parameters['dimensions'][0] * 10))
    # ax.set_ylim((0, STEM_parameters['dimensions'][1] * 10))
    # plt.show()
    # import sys; sys.exit()

    # Find a list of local maximum and local minimum in the image
    cutoff = get_avg_radii(individual) * 2 * 1.1
    move_cutoff *= cutoff
    surf_cutoff *= cutoff
    resolution = module.parameters['resolution']
    size = cutoff * resolution * filter_size

    data_max = filters.maximum_filter(contrast, size=size)
    maxima = ((contrast == data_max) & (contrast > max_max * max_cutoff))
    if len(maxima) == 0:
        return False
    max_coords = np.argwhere(maxima)
    max_xys = (max_coords[:, ::-1] -
               np.array([[x_shift, y_shift]])) / resolution
    max_intensities = np.asarray(
        [data_max[tuple(coord)] for coord in max_coords])
    max_intensities /= sum(max_intensities)

    ###################################
    ## Code for testing the max find ##
    ###################################
    # import matplotlib.pyplot as plt
    # import matplotlib.cm as cm
    # fig, ax = plt.subplots()
    # fig.colorbar(ax.pcolormesh(maxima, cmap=cm.viridis, linewidths=0))
    # ax.set_xlim((0, STEM_parameters['dimensions'][0] * 10))
    # ax.set_ylim((0, STEM_parameters['dimensions'][1] * 10))
    # plt.show()
    # print(len(max_intensities))
    # import sys; sys.exit()

    data_min = filters.minimum_filter(contrast, size=size)
    minima = ((contrast == data_min) & (contrast < min_min * min_cutoff))
    if len(minima) == 0:
        return False
    min_coords = np.argwhere(minima)
    min_xys = (min_coords[:, ::-1] - [x_shift, y_shift]) / resolution
    min_intensities = np.asarray(
        [data_min[tuple(coord)] for coord in min_coords])
    min_intensities = np.absolute(min_intensities)
    min_intensities /= sum(min_intensities)

    ###################################
    ## Code for testing the min find ##
    ###################################
    # import matplotlib.pyplot as plt
    # import matplotlib.cm as cm
    # fig, ax = plt.subplots()
    # fig.colorbar(ax.pcolormesh(data_min, cmap=cm.viridis, linewidths=0))
    # ax.set_xlim((0, STEM_parameters['dimensions'][0] * 10))
    # ax.set_ylim((0, STEM_parameters['dimensions'][1] * 10))
    # plt.show()
    # print(len(min_intensities))
    # import sys; sys.exit()

    # Get indices of atoms considered to be moved and sites to move too
    CNs = CoordinationNumbers(individual)
    positions = individual.get_positions()

    move_indices = [i for i, CN in enumerate(CNs) if CN <= move_CN]
    move_xys = positions[list(move_indices)][:, :2]

    surf_indices = [i for i, CN in enumerate(CNs) if CN <= surf_CN]
    surf_positions = positions[list(surf_indices)]
    COM = surf_positions.mean(axis=0)
    vec = surf_positions - COM
    vec /= np.array([np.linalg.norm(vec, axis=1)]).T
    epsilon = np.array([np.random.random(len(surf_positions)) * 0.5 + 0.5]).T
    surf_positions = surf_positions + vec * cutoff * epsilon
    surf_xys = surf_positions[:, :2]

    # Randomly choose local maxima and minima locations from contrast weighted
    # by their intensity
    high_xy_index = np.random.choice(np.arange(len(max_xys)),
                                     p=max_intensities)
    low_xy_index = np.random.choice(np.arange(len(min_xys)), p=min_intensities)
    high_xy = max_xys[high_xy_index]
    low_xy = min_xys[low_xy_index]

    # Choose move_atom (surf_atom) from within the move_cutoff (surf_cutoff)
    # of high_xy (low_xy)
    dists_move_xys = np.linalg.norm(move_xys - high_xy, axis=1)
    indices_move_xys = [
        i for i, d in zip(move_indices, dists_move_xys) if d < move_cutoff
    ]

    ########################
    ## Test atoms to move ##
    ########################
    # from ase.visualize import view
    # for i in indices_move_xys:
    #     individual[i].symbol = 'Mo'
    # view(individual)
    # import sys; sys.exit()

    if len(indices_move_xys) == 0:
        move_index = np.argmin(dists_move_xys)
    else:
        move_index = random.choice(indices_move_xys)

    dists_surf_xys = np.linalg.norm(surf_xys - low_xy, axis=1)
    indices_surf_xys = [
        i for i, d in enumerate(dists_surf_xys) if d < surf_cutoff
    ]

    ########################
    ## Test atoms to move ##
    ########################
    # from ase import Atom
    # from ase.visualize import view
    # for i in indices_surf_xys:
    #     individual.append(Atom('Mo', surf_positions[i]))
    # view(individual)
    # import sys; sys.exit()

    if len(indices_surf_xys) == 0:
        surf_index = np.argmin(dists_surf_xys)
    else:
        surf_index = random.choice(indices_surf_xys)

    new_position = surf_positions[surf_index]

    positions[move_index] = new_position
    individual.set_positions(positions)

    return