def prepare_ga(dbfile='godb.db', N=20): ''' This method creates a database with the desired number of randomly generated structures. ''' blocks = [('Si', 7)] # the building blocks l = [list(Atoms(block).numbers) * count for block, count in blocks] stoichiometry = [int(item) for sublist in l for item in sublist] atom_numbers = list(set(stoichiometry)) # This dictionary will be used to check that the shortest # Si-Si distances are above a certain threshold # (here 1.5 Angstrom): blmin = {(14, 14): 1.5} # This defines the cubic simulation cell: slab = Atoms('', positions=np.zeros((0, 3)), cell=[16] * 3) # This defines the smaller box in which the # initial coordinates are allowed to vary density = 0.12 # in atoms per cubic Angstrom aspect_ratios = np.array([1.0, 1.0, 1.0]) v = len(stoichiometry) / density l = np.cbrt(v / np.product(aspect_ratios)) cell = np.identity(3) * aspect_ratios * l p0 = 0.5 * (np.diag(slab.get_cell() - cell)) box = [p0, cell] # Seed the random number generators using the system time, # to ensure that no two runs produce the same results: np.random.seed() seed() # Generate the random structures and add them to the database: sg = StartGenerator(slab=slab, atom_numbers=stoichiometry, closest_allowed_distances=blmin, box_to_place_in=box) da = PrepareDB(db_file_name=dbfile, simulation_cell=slab, stoichiometry=stoichiometry) for i in range(N): a = sg.get_new_candidate() da.add_unrelaxed_candidate(a) return
def prepare_ga(dbfile='godb.db', N=20): """ This method creates a database with the desired number of randomly generated structures. """ Z = atomic_numbers['Si'] atom_numbers = [Z] * 7 # This dictionary will be used to check that the shortest # Si-Si distances are above a certain threshold # (here 1.5 Angstrom): blmin = {(Z, Z): 1.5} # This defines the cubic simulation cell. In case there big_cell = np.identity(3) * 16. slab = Atoms('', cell=big_cell) # This defines the smaller box in which the initial # coordinates are allowed to be generated density = 0.1 # in atoms per cubic Angstrom L = np.cbrt(len(atom_numbers) / density) small_cell = np.identity(3) * L p0 = 0.5 * np.diag(big_cell - small_cell) box = [p0, small_cell] # Seed the random number generators using the system time, # to ensure that no two runs produce the same results: np.random.seed() seed() # Generate the random structures and add them to the database: sg = StartGenerator(slab=slab, atom_numbers=atom_numbers, closest_allowed_distances=blmin, box_to_place_in=box) da = PrepareDB(db_file_name=dbfile, simulation_cell=slab, stoichiometry=atom_numbers) for i in range(N): a = sg.get_new_candidate() da.add_unrelaxed_candidate(a) return
def test_database_logic(seed, testdir): from ase.ga.data import PrepareDB from ase.ga.data import DataConnection from ase.ga.startgenerator import StartGenerator from ase.ga.utilities import closest_distances_generator from ase.ga import set_raw_score import numpy as np from ase.build import fcc111 from ase.constraints import FixAtoms # set up the random number generator rng = np.random.RandomState(seed) slab = fcc111('Au', size=(4, 4, 2), vacuum=10.0, orthogonal=True) slab.set_constraint(FixAtoms(mask=slab.positions[:, 2] <= 10.)) # define the volume in which the adsorbed cluster is optimized # the volume is defined by a corner position (p0) # and three spanning vectors (v1, v2, v3) pos = slab.get_positions() cell = slab.get_cell() p0 = np.array([0., 0., max(pos[:, 2]) + 2.]) v1 = cell[0, :] * 0.8 v2 = cell[1, :] * 0.8 v3 = cell[2, :] v3[2] = 3. # define the closest distance between two atoms of a given species blmin = closest_distances_generator(atom_numbers=[47, 79], ratio_of_covalent_radii=0.7) # Define the composition of the atoms to optimize atom_numbers = 2 * [47] + 2 * [79] # create the starting population sg = StartGenerator(slab=slab, blocks=atom_numbers, blmin=blmin, box_to_place_in=[p0, [v1, v2, v3]], rng=rng) # generate the starting population starting_population = [sg.get_new_candidate() for i in range(20)] d = PrepareDB(db_file_name=db_file, simulation_cell=slab, stoichiometry=atom_numbers) for a in starting_population: d.add_unrelaxed_candidate(a) # and now for the actual test dc = DataConnection(db_file) dc.get_slab() dc.get_atom_numbers_to_optimize() assert dc.get_number_of_unrelaxed_candidates() == 20 a1 = dc.get_an_unrelaxed_candidate() dc.mark_as_queued(a1) assert dc.get_number_of_unrelaxed_candidates() == 19 assert len(dc.get_all_candidates_in_queue()) == 1 set_raw_score(a1, 0.0) dc.add_relaxed_step(a1) assert dc.get_number_of_unrelaxed_candidates() == 19 assert len(dc.get_all_candidates_in_queue()) == 0 assert len(dc.get_all_relaxed_candidates()) == 1 a2 = dc.get_an_unrelaxed_candidate() dc.mark_as_queued(a2) confid = a2.info['confid'] assert dc.get_all_candidates_in_queue()[0] == confid dc.remove_from_queue(confid) assert len(dc.get_all_candidates_in_queue()) == 0
def test_chain_operators(seed): from ase.ga.startgenerator import StartGenerator from ase.ga.cutandsplicepairing import CutAndSplicePairing from ase.ga.standardmutations import StrainMutation from ase.ga.utilities import (closest_distances_generator, atoms_too_close, CellBounds) import numpy as np from ase import Atoms # set up the random number generator rng = np.random.RandomState(seed) slab = Atoms('', cell=(0, 16, 16), pbc=[True, False, False]) blocks = ['C'] * 8 n_top = 8 use_tags = False num_vcv = 1 box_volume = 8. * n_top blmin = closest_distances_generator(atom_numbers=[6], ratio_of_covalent_radii=0.6) cellbounds = CellBounds( bounds={ 'phi': [0.1 * 180., 0.9 * 180.], 'chi': [0.1 * 180., 0.9 * 180.], 'psi': [0.1 * 180., 0.9 * 180.], 'a': [1, 6] }) box_to_place_in = [[None, 6., 6.], [None, [0., 4., 0.], [0., 0., 4.]]] sg = StartGenerator(slab, blocks, blmin, box_volume=box_volume, splits=None, box_to_place_in=box_to_place_in, number_of_variable_cell_vectors=num_vcv, cellbounds=cellbounds, test_too_far=True, test_dist_to_slab=False, rng=rng) parents = [] for i in range(2): a = None while a is None: a = sg.get_new_candidate() a.info['confid'] = i parents.append(a) assert len(a) == n_top assert len(np.unique(a.get_tags())) == 8 assert np.allclose(a.get_pbc(), slab.get_pbc()) p = a.get_positions() assert np.min(p[:, 1:]) > 6. assert np.max(p[:, 1:]) < 6. + 4. assert not atoms_too_close(a, blmin, use_tags=use_tags) c = a.get_cell() assert np.allclose(c[1:], slab.get_cell()[1:]) assert cellbounds.is_within_bounds(c) v = a.get_volume() * (4. / 16.)**2 assert abs(v - box_volume) < 1e-5 # Test cut-and-splice pairing and strain mutation pairing = CutAndSplicePairing(slab, n_top, blmin, number_of_variable_cell_vectors=num_vcv, p1=1., p2=0., minfrac=0.15, cellbounds=cellbounds, use_tags=use_tags, rng=rng) strainmut = StrainMutation(blmin, cellbounds=cellbounds, number_of_variable_cell_vectors=num_vcv, use_tags=use_tags, rng=rng) strainmut.update_scaling_volume(parents) for operator in [pairing, strainmut]: child = None while child is None: child, desc = operator.get_new_individual(parents) assert not atoms_too_close(child, blmin, use_tags=use_tags) cell = child.get_cell() assert cellbounds.is_within_bounds(cell) assert np.allclose(cell[1:], slab.get_cell()[1:])
# (and not intramolecularly): Z = atomic_numbers['N'] blmin = closest_distances_generator(atom_numbers=[Z], ratio_of_covalent_radii=1.3) # The bounds for the randomly generated unit cells: cellbounds = CellBounds(bounds={'phi': [30, 150], 'chi': [30, 150], 'psi': [30, 150], 'a': [3, 50], 'b': [3, 50], 'c': [3, 50]}) # The familiar 'slab' object, here only providing # the PBC as there are no atoms or cell vectors # that need to be applied. slab = Atoms('', pbc=True) # create the starting population sg = StartGenerator(slab, blocks, blmin, box_volume=box_volume, cellbounds=cellbounds, splits=splits, number_of_variable_cell_vectors=3, test_too_far=False) # Initialize the database da = PrepareDB(db_file_name='gadb.db', simulation_cell=slab, stoichiometry=[Z] * 16) # Generate the new structures # and add them to the database for i in range(N): a = sg.get_new_candidate() da.add_unrelaxed_candidate(a)
v3[2] = 3. # Define the composition of the atoms to optimize atom_numbers = 2 * [47] + 2 * [79] # define the closest distance two atoms of a given species can be to each other unique_atom_types = get_all_atom_types(slab, atom_numbers) blmin = closest_distances_generator(atom_numbers=unique_atom_types, ratio_of_covalent_radii=0.7) # create the starting population sg = StartGenerator(slab, atom_numbers, blmin, box_to_place_in=[p0, [v1, v2, v3]]) # generate the starting population population_size = 20 starting_population = [sg.get_new_candidate() for i in range(population_size)] # from ase.visualize import view # uncomment these lines # view(starting_population) # to see the starting population # create the database to store information in d = PrepareDB(db_file_name=db_file, simulation_cell=slab, stoichiometry=atom_numbers) for a in starting_population: d.add_unrelaxed_candidate(a)
# Define the composition of the atoms to optimize atom_numbers = 2 * [47] + 2 * [79] # define the closest distance two atoms of a given species can be to each other unique_atom_types = get_all_atom_types(slab, atom_numbers) cd = closest_distances_generator(atom_numbers=unique_atom_types, ratio_of_covalent_radii=0.7) # create the starting population sg = StartGenerator(slab=slab, atom_numbers=atom_numbers, closest_allowed_distances=cd, box_to_place_in=[p0, [v1, v2, v3]]) # generate the starting population population_size = 5 starting_population = [sg.get_new_candidate() for i in xrange(population_size)] # from ase.visualize import view # uncomment these lines # view(starting_population) # to see the starting population # create the database to store information in d = PrepareDB(db_file_name=db_file, simulation_cell=slab, stoichiometry=atom_numbers,) # population_size=population_size) for a in starting_population: d.add_unrelaxed_candidate(a)
# define the closest distance between two atoms of a given species cd = closest_distances_generator(atom_numbers=[47, 79], ratio_of_covalent_radii=0.7) # Define the composition of the atoms to optimize atom_numbers = 2 * [47] + 2 * [79] # create the starting population sg = StartGenerator(slab=slab, atom_numbers=atom_numbers, closest_allowed_distances=cd, box_to_place_in=[p0, [v1, v2, v3]]) # generate the starting population starting_population = [sg.get_new_candidate() for i in xrange(20)] d = PrepareDB(db_file_name=db_file, simulation_cell=slab, stoichiometry=atom_numbers) for a in starting_population: d.add_unrelaxed_candidate(a) # and now for the actual test dc = DataConnection(db_file) slab_get = dc.get_slab() an_get = dc.get_atom_numbers_to_optimize() assert dc.get_number_of_unrelaxed_candidates() == 20
def test_film_operators(seed): from ase.ga.startgenerator import StartGenerator from ase.ga.cutandsplicepairing import CutAndSplicePairing from ase.ga.standardmutations import StrainMutation from ase.ga.utilities import (closest_distances_generator, atoms_too_close, CellBounds) import numpy as np from ase import Atoms from ase.build import molecule # set up the random number generator rng = np.random.RandomState(seed) slab = Atoms('', cell=(0, 0, 15), pbc=[True, True, False]) cation, anion = 'Mg', molecule('OH') d_oh = anion.get_distance(0, 1) blocks = [(cation, 4), (anion, 8)] n_top = 4 + 8 * len(anion) use_tags = True num_vcv = 2 box_volume = 8. * n_top blmin = closest_distances_generator(atom_numbers=[1, 8, 12], ratio_of_covalent_radii=0.6) cellbounds = CellBounds( bounds={ 'phi': [0.1 * 180., 0.9 * 180.], 'chi': [0.1 * 180., 0.9 * 180.], 'psi': [0.1 * 180., 0.9 * 180.], 'a': [2, 8], 'b': [2, 8] }) box_to_place_in = [[None, None, 3.], [None, None, [0., 0., 5.]]] sg = StartGenerator(slab, blocks, blmin, box_volume=box_volume, splits={(2, 1): 1}, box_to_place_in=box_to_place_in, number_of_variable_cell_vectors=num_vcv, cellbounds=cellbounds, test_too_far=True, test_dist_to_slab=False, rng=rng) parents = [] for i in range(2): a = None while a is None: a = sg.get_new_candidate() a.info['confid'] = i parents.append(a) assert len(a) == n_top assert len(np.unique(a.get_tags())) == 4 + 8 assert np.allclose(a.get_pbc(), slab.get_pbc()) p = a.get_positions() assert np.min(p[:, 2]) > 3. - 0.5 * d_oh assert np.max(p[:, 2]) < 3. + 5. + 0.5 * d_oh assert not atoms_too_close(a, blmin, use_tags=use_tags) c = a.get_cell() assert np.allclose(c[2], slab.get_cell()[2]) assert cellbounds.is_within_bounds(c) v = a.get_volume() * 5. / 15. assert abs(v - box_volume) < 1e-5 # Test cut-and-splice pairing and strain mutation pairing = CutAndSplicePairing(slab, n_top, blmin, number_of_variable_cell_vectors=num_vcv, p1=1., p2=0., minfrac=0.15, cellbounds=cellbounds, use_tags=use_tags, rng=rng) strainmut = StrainMutation(blmin, cellbounds=cellbounds, number_of_variable_cell_vectors=num_vcv, use_tags=use_tags, rng=rng) strainmut.update_scaling_volume(parents) for operator in [pairing, strainmut]: child = None while child is None: child, desc = operator.get_new_individual(parents) assert not atoms_too_close(child, blmin, use_tags=use_tags) cell = child.get_cell() assert cellbounds.is_within_bounds(cell) assert np.allclose(cell[2], slab.get_cell()[2])
def test_cutandsplicepairing(seed): from ase.ga.startgenerator import StartGenerator from ase.ga.utilities import closest_distances_generator, atoms_too_close from ase.ga.cutandsplicepairing import CutAndSplicePairing import numpy as np from ase.build import fcc111 from ase.constraints import FixAtoms # set up the random number generator rng = np.random.RandomState(seed) # first create two random starting candidates slab = fcc111('Au', size=(4, 4, 2), vacuum=10.0, orthogonal=True) slab.set_constraint(FixAtoms(mask=slab.positions[:, 2] <= 10.)) pos = slab.get_positions() cell = slab.get_cell() p0 = np.array([0., 0., max(pos[:, 2]) + 2.]) v1 = cell[0, :] * 0.8 v2 = cell[1, :] * 0.8 v3 = cell[2, :] v3[2] = 3. blmin = closest_distances_generator(atom_numbers=[47, 79], ratio_of_covalent_radii=0.7) atom_numbers = 2 * [47] + 2 * [79] sg = StartGenerator(slab=slab, blocks=atom_numbers, blmin=blmin, box_to_place_in=[p0, [v1, v2, v3]], rng=rng) c1 = sg.get_new_candidate() c1.info['confid'] = 1 c2 = sg.get_new_candidate() c2.info['confid'] = 2 n_top = len(atom_numbers) pairing = CutAndSplicePairing(slab, n_top, blmin, rng=rng) c3, desc = pairing.get_new_individual([c1, c2]) # verify that the stoichiometry is preserved assert np.all(c3.numbers == c1.numbers) top1 = c1[-n_top:] top2 = c2[-n_top:] top3 = c3[-n_top:] # verify that the positions in the new candidate come from c1 or c2 n1 = -1 * np.ones((n_top, )) n2 = -1 * np.ones((n_top, )) for i in range(n_top): for j in range(n_top): if np.allclose(top1.positions[j, :], top3.positions[i, :], 1e-12): n1[i] = j break elif np.allclose(top2.positions[j, :], top3.positions[i, :], 1e-12): n2[i] = j break assert (n1[i] > -1 and n2[i] == -1) or (n1[i] == -1 and n2[i] > -1) # verify that c3 includes atoms from both c1 and c2 assert len(n1[n1 > -1]) > 0 and len(n2[n2 > -1]) > 0 # verify no atoms too close assert not atoms_too_close(top3, blmin)
# define the closest distance between two atoms of a given species cd = closest_distances_generator(atom_numbers=[47, 79], ratio_of_covalent_radii=0.7) # Define the composition of the atoms to optimize atom_numbers = 2 * [47] + 2 * [79] # create the starting population sg = StartGenerator(slab=slab, atom_numbers=atom_numbers, closest_allowed_distances=cd, box_to_place_in=[p0, [v1, v2, v3]]) # generate the starting population starting_population = [sg.get_new_candidate() for i in range(20)] d = PrepareDB(db_file_name=db_file, simulation_cell=slab, stoichiometry=atom_numbers) for a in starting_population: d.add_unrelaxed_candidate(a) # and now for the actual test dc = DataConnection(db_file) slab_get = dc.get_slab() an_get = dc.get_atom_numbers_to_optimize() assert dc.get_number_of_unrelaxed_candidates() == 20
cell = slab.get_cell() p0 = np.array([0.0, 0.0, max(pos[:, 2]) + 2.0]) v1 = cell[0, :] * 0.8 v2 = cell[1, :] * 0.8 v3 = cell[2, :] v3[2] = 3.0 cd = closest_distances_generator(atom_numbers=[47, 79], ratio_of_covalent_radii=0.7) atom_numbers = 2 * [47] + 2 * [79] sg = StartGenerator( slab=slab, atom_numbers=atom_numbers, closest_allowed_distances=cd, box_to_place_in=[p0, [v1, v2, v3]] ) c1 = sg.get_new_candidate() c1.info["confid"] = 1 c2 = sg.get_new_candidate() c2.info["confid"] = 2 n_top = len(atom_numbers) pairing = CutAndSplicePairing(slab, n_top, cd) c3, desc = pairing.get_new_individual([c1, c2]) # verify that the stoichiometry is preserved assert np.all(c3.numbers == c1.numbers) top1 = c1[-n_top:] top2 = c2[-n_top:] top3 = c3[-n_top:]
v1 = cell[0, :] * 0.8 v2 = cell[1, :] * 0.8 v3 = cell[2, :] v3[2] = 3. cd = closest_distances_generator(atom_numbers=[47, 79], ratio_of_covalent_radii=0.7) atom_numbers = 2 * [47] + 2 * [79] sg = StartGenerator(slab=slab, atom_numbers=atom_numbers, closest_allowed_distances=cd, box_to_place_in=[p0, [v1, v2, v3]]) c1 = sg.get_new_candidate() c1.info['confid'] = 1 c2 = sg.get_new_candidate() c2.info['confid'] = 2 n_top = len(atom_numbers) pairing = CutAndSplicePairing(slab, n_top, cd) c3, desc = pairing.get_new_individual([c1, c2]) # verify that the stoichiometry is preserved assert np.all(c3.numbers == c1.numbers) top1 = c1[-n_top:] top2 = c2[-n_top:] top3 = c3[-n_top:]
def test_basic_example_main_run(seed, testdir): # set up the random number generator rng = np.random.RandomState(seed) # create the surface slab = fcc111('Au', size=(4, 4, 1), vacuum=10.0, orthogonal=True) slab.set_constraint(FixAtoms(mask=len(slab) * [True])) # define the volume in which the adsorbed cluster is optimized # the volume is defined by a corner position (p0) # and three spanning vectors (v1, v2, v3) pos = slab.get_positions() cell = slab.get_cell() p0 = np.array([0., 0., max(pos[:, 2]) + 2.]) v1 = cell[0, :] * 0.8 v2 = cell[1, :] * 0.8 v3 = cell[2, :] v3[2] = 3. # Define the composition of the atoms to optimize atom_numbers = 2 * [47] + 2 * [79] # define the closest distance two atoms of a given species can be to each other unique_atom_types = get_all_atom_types(slab, atom_numbers) blmin = closest_distances_generator(atom_numbers=unique_atom_types, ratio_of_covalent_radii=0.7) # create the starting population sg = StartGenerator(slab=slab, blocks=atom_numbers, blmin=blmin, box_to_place_in=[p0, [v1, v2, v3]], rng=rng) # generate the starting population population_size = 5 starting_population = [sg.get_new_candidate() for i in range(population_size)] # from ase.visualize import view # uncomment these lines # view(starting_population) # to see the starting population # create the database to store information in d = PrepareDB(db_file_name=db_file, simulation_cell=slab, stoichiometry=atom_numbers) for a in starting_population: d.add_unrelaxed_candidate(a) # XXXXXXXXXX This should be the beginning of a new test, # but we are using some resources from the precious part. # Maybe refactor those things as (module-level?) fixtures. # Change the following three parameters to suit your needs population_size = 5 mutation_probability = 0.3 n_to_test = 5 # Initialize the different components of the GA da = DataConnection('gadb.db') atom_numbers_to_optimize = da.get_atom_numbers_to_optimize() n_to_optimize = len(atom_numbers_to_optimize) slab = da.get_slab() all_atom_types = get_all_atom_types(slab, atom_numbers_to_optimize) blmin = closest_distances_generator(all_atom_types, ratio_of_covalent_radii=0.7) comp = InteratomicDistanceComparator(n_top=n_to_optimize, pair_cor_cum_diff=0.015, pair_cor_max=0.7, dE=0.02, mic=False) pairing = CutAndSplicePairing(slab, n_to_optimize, blmin, rng=rng) mutations = OperationSelector([1., 1., 1.], [MirrorMutation(blmin, n_to_optimize, rng=rng), RattleMutation(blmin, n_to_optimize, rng=rng), PermutationMutation(n_to_optimize, rng=rng)], rng=rng) # Relax all unrelaxed structures (e.g. the starting population) while da.get_number_of_unrelaxed_candidates() > 0: a = da.get_an_unrelaxed_candidate() a.calc = EMT() print('Relaxing starting candidate {0}'.format(a.info['confid'])) dyn = BFGS(a, trajectory=None, logfile=None) dyn.run(fmax=0.05, steps=100) set_raw_score(a, -a.get_potential_energy()) da.add_relaxed_step(a) # create the population population = Population(data_connection=da, population_size=population_size, comparator=comp, rng=rng) # test n_to_test new candidates for i in range(n_to_test): print('Now starting configuration number {0}'.format(i)) a1, a2 = population.get_two_candidates() a3, desc = pairing.get_new_individual([a1, a2]) if a3 is None: continue da.add_unrelaxed_candidate(a3, description=desc) # Check if we want to do a mutation if rng.rand() < mutation_probability: a3_mut, desc = mutations.get_new_individual([a3]) if a3_mut is not None: da.add_unrelaxed_step(a3_mut, desc) a3 = a3_mut # Relax the new candidate a3.calc = EMT() dyn = BFGS(a3, trajectory=None, logfile=None) dyn.run(fmax=0.05, steps=100) set_raw_score(a3, -a3.get_potential_energy()) da.add_relaxed_step(a3) population.update() write('all_candidates.traj', da.get_all_relaxed_candidates())
def test_mutations(seed): from ase.ga.startgenerator import StartGenerator from ase.ga.utilities import closest_distances_generator from ase.ga.standardmutations import RattleMutation, PermutationMutation import numpy as np from ase.build import fcc111 from ase.constraints import FixAtoms # set up the random number generator rng = np.random.RandomState(seed) # first create two random starting candidates slab = fcc111('Au', size=(4, 4, 2), vacuum=10.0, orthogonal=True) slab.set_constraint(FixAtoms(mask=slab.positions[:, 2] <= 10.)) pos = slab.get_positions() cell = slab.get_cell() p0 = np.array([0., 0., max(pos[:, 2]) + 2.]) v1 = cell[0, :] * 0.8 v2 = cell[1, :] * 0.8 v3 = cell[2, :] v3[2] = 3. blmin = closest_distances_generator(atom_numbers=[47, 79], ratio_of_covalent_radii=0.7) atom_numbers = 2 * [47] + 2 * [79] n_top = len(atom_numbers) sg = StartGenerator(slab=slab, blocks=atom_numbers, blmin=blmin, box_to_place_in=[p0, [v1, v2, v3]], rng=rng) c1 = sg.get_new_candidate() c1.info['confid'] = 1 # first verify that the rattle mutation works rmut = RattleMutation(blmin, n_top, rattle_strength=0.8, rattle_prop=0.4, rng=rng) c2, desc = rmut.get_new_individual([c1]) assert np.all(c1.numbers == c2.numbers) top1 = c1[-n_top:] top2 = c2[-n_top:] slab2 = c2[0:(len(c1) - n_top)] assert len(slab) == len(slab2) assert np.all(slab.get_positions() == slab2.get_positions()) dp = np.sum((top2.get_positions() - top1.get_positions())**2, axis=1)**0.5 # check that all displacements are smaller than the rattle strength we # cannot check if 40 % of the structures have been rattled since it is # probabilistic and because the probability will be lower if two atoms # get too close for p in dp: assert p < 0.8 * 3**0.5 # now we check the permutation mutation mmut = PermutationMutation(n_top, probability=0.5, rng=rng) c3, desc = mmut.get_new_individual([c1]) assert np.all(c1.numbers == c3.numbers) top1 = c1[-n_top:] top2 = c3[-n_top:] slab2 = c3[0:(len(c1) - n_top)] assert len(slab) == len(slab2) assert np.all(slab.get_positions() == slab2.get_positions()) dp = np.sum((top2.get_positions() - top1.get_positions())**2, axis=1)**0.5 # verify that two positions have been changed assert len(dp[dp > 0]) == 2
def test_bulk_operators(seed): # set up the random number generator rng = np.random.RandomState(seed) h2 = Atoms('H2', positions=[[0, 0, 0], [0, 0, 0.75]]) blocks = [('H', 4), ('H2O', 3), (h2, 2)] # the building blocks volume = 40. * sum([x[1] for x in blocks]) # cell volume in angstrom^3 splits = {(2,): 1, (1,): 1} # cell splitting scheme stoichiometry = [] for block, count in blocks: if type(block) == str: stoichiometry += list(Atoms(block).numbers) * count else: stoichiometry += list(block.numbers) * count atom_numbers = list(set(stoichiometry)) blmin = closest_distances_generator(atom_numbers=atom_numbers, ratio_of_covalent_radii=1.3) cellbounds = CellBounds(bounds={'phi': [30, 150], 'chi': [30, 150], 'psi': [30, 150], 'a': [3, 50], 'b': [3, 50], 'c': [3, 50]}) slab = Atoms('', pbc=True) sg = StartGenerator(slab, blocks, blmin, box_volume=volume, number_of_variable_cell_vectors=3, cellbounds=cellbounds, splits=splits, rng=rng) # Generate 2 candidates a1 = sg.get_new_candidate() a1.info['confid'] = 1 a2 = sg.get_new_candidate() a2.info['confid'] = 2 # Define and test genetic operators n_top = len(a1) pairing = CutAndSplicePairing(slab, n_top, blmin, p1=1., p2=0., minfrac=0.15, number_of_variable_cell_vectors=3, cellbounds=cellbounds, use_tags=True, rng=rng) a3, desc = pairing.get_new_individual([a1, a2]) cell = a3.get_cell() assert cellbounds.is_within_bounds(cell) assert not atoms_too_close(a3, blmin, use_tags=True) n_top = len(a1) strainmut = StrainMutation(blmin, stddev=0.7, cellbounds=cellbounds, number_of_variable_cell_vectors=3, use_tags=True, rng=rng) softmut = SoftMutation(blmin, bounds=[2., 5.], used_modes_file=None, use_tags=True) # no rng rotmut = RotationalMutation(blmin, fraction=0.3, min_angle=0.5 * np.pi, rng=rng) rattlemut = RattleMutation(blmin, n_top, rattle_prop=0.3, rattle_strength=0.5, use_tags=True, test_dist_to_slab=False, rng=rng) rattlerotmut = RattleRotationalMutation(rattlemut, rotmut) # no rng permut = PermutationMutation(n_top, probability=0.33, test_dist_to_slab=False, use_tags=True, blmin=blmin, rng=rng) combmut = CombinationMutation(rattlemut, rotmut, verbose=True) # no rng mutations = [strainmut, softmut, rotmut, rattlemut, rattlerotmut, permut, combmut] for i, mut in enumerate(mutations): a = [a1, a2][i % 2] a3 = None while a3 is None: a3, desc = mut.get_new_individual([a]) cell = a3.get_cell() assert cellbounds.is_within_bounds(cell) assert np.all(a3.numbers == a.numbers) assert not atoms_too_close(a3, blmin, use_tags=True) modes_file = 'modes.txt' softmut_with = SoftMutation(blmin, bounds=[2., 5.], use_tags=True, used_modes_file=modes_file) # no rng no_muts = 3 for _ in range(no_muts): softmut_with.get_new_individual([a1]) softmut_with.read_used_modes(modes_file) assert len(list(softmut_with.used_modes.values())[0]) == no_muts os.remove(modes_file) comparator = OFPComparator(recalculate=True) gold = bulk('Au') * (2, 2, 2) assert comparator.looks_like(gold, gold) # This move should not exceed the default threshold gc = gold.copy() gc[0].x += .1 assert comparator.looks_like(gold, gc) # An additional step will exceed the threshold gc[0].x += .2 assert not comparator.looks_like(gold, gc)