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
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
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
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
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
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
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()
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
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 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
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