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:])
blmin = closest_distances_generator(atom_numbers_to_optimize, 1.0) cellbounds = CellBounds(bounds={'phi': [30, 150], 'chi': [30, 150], 'psi': [30, 150]}) # Note the "use_tags" keyword argument being used # to signal that we want to preserve molecular identity # via the tags pairing = CutAndSplicePairing(slab, n_top, blmin, p1=1., p2=0., minfrac=0.15, cellbounds=cellbounds, number_of_variable_cell_vectors=3, use_tags=True) rattlemut = RattleMutation(blmin, n_top, rattle_prop=0.3, rattle_strength=0.5, use_tags=True) strainmut = StrainMutation(blmin, stddev=0.7, cellbounds=cellbounds, use_tags=True) rotmut = RotationalMutation(blmin, fraction=0.3, min_angle=0.5 * np.pi) rattlerotmut = RattleRotationalMutation(rattlemut, rotmut) blmin_soft = closest_distances_generator(atom_numbers_to_optimize, 0.8) softmut = SoftMutation(blmin_soft, bounds=[2., 5.], use_tags=True) operators = OperationSelector([5, 1, 1, 1, 1, 1], [pairing, rattlemut, strainmut, rotmut, rattlerotmut, softmut]) # Relaxing the initial candidates while da.get_number_of_unrelaxed_candidates() > 0: a = da.get_an_unrelaxed_candidate() relax(a)
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)
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])
# contributing to at least 15% of the child's scaled coordinates pairing = CutAndSplicePairing(slab, n_top, blmin, p1=1., p2=0., minfrac=0.15, number_of_variable_cell_vectors=3, cellbounds=cellbounds, use_tags=False) # Define a strain mutation with a typical standard deviation of 0.7 # for the strain matrix elements (drawn from a normal distribution) strainmut = StrainMutation(blmin, stddev=0.7, cellbounds=cellbounds, number_of_variable_cell_vectors=3, use_tags=False) # Define a soft mutation; we need to provide a dictionary with # (typically rather short) minimal interatomic distances which # is used to determine when to stop displacing the atoms along # the chosen mode. The minimal and maximal single-atom displacement # distances (in Angstrom) for a valid mutation are provided via # the 'bounds' keyword argument. blmin_soft = closest_distances_generator(atom_numbers_to_optimize, 0.1) softmut = SoftMutation(blmin_soft, bounds=[2., 5.], use_tags=False) # By default, the operator will update a "used_modes.json" file # after every mutation, listing which modes have been used so far # for each structure in the database. The mode indices start at 3 # as the three lowest frequency modes are translational modes.