def finalize(atoms, energy=None, forces=None, stress=None): # Finalizes the atoms by attaching a SinglePointCalculator # and setting the raw score as the negative of the total energy atoms.wrap() calc = SinglePointCalculator(atoms, energy=energy, forces=forces, stress=stress) atoms.calc = calc raw_score = -atoms.get_potential_energy() set_raw_score(atoms, raw_score)
def test_standardcomparator(): from ase.ga.standard_comparators import (InteratomicDistanceComparator, EnergyComparator, RawScoreComparator, SequentialComparator) from ase import Atoms from ase.calculators.singlepoint import SinglePointCalculator from ase.ga import set_raw_score a1 = Atoms('AgAgAg', positions=[[0, 0, 0], [1.5, 0, 0], [1.5, 1.5, 0]]) a2 = Atoms('AgAgAg', positions=[[0, 0, 0], [1.4, 0, 0], [1.5, 1.5, 0]]) e1 = 1.0 e2 = 0.8 a1.set_calculator(SinglePointCalculator(a1, energy=e1)) a2.set_calculator(SinglePointCalculator(a2, energy=e2)) comp1 = InteratomicDistanceComparator(n_top=3, pair_cor_cum_diff=0.03, pair_cor_max=0.7, dE=0.3) assert comp1.looks_like(a1, a2) comp2 = InteratomicDistanceComparator(n_top=3, pair_cor_cum_diff=0.03, pair_cor_max=0.7, dE=0.15) assert not comp2.looks_like(a1, a2) comp3 = InteratomicDistanceComparator(n_top=3, pair_cor_cum_diff=0.02, pair_cor_max=0.7, dE=0.3) assert not comp3.looks_like(a1, a2) hard_E_comp = EnergyComparator(dE=1.0) assert hard_E_comp.looks_like(a1, a2) soft_E_comp = EnergyComparator(dE=.01) assert not soft_E_comp.looks_like(a1, a2) set_raw_score(a1, .1) set_raw_score(a2, .27) rs_comp = RawScoreComparator(0.15) assert not rs_comp.looks_like(a1, a2) comp1 = SequentialComparator([hard_E_comp, rs_comp], [0, 0]) assert not comp1.looks_like(a1, a2) comp2 = SequentialComparator([hard_E_comp, rs_comp], [0, 1]) assert comp2.looks_like(a1, a2)
def finalize(atoms, energy=None, forces=None, stress=None): """ Saves attributes by attaching a SinglePointCalculator and sets the default raw score (equal to the negative of the potential energy). """ atoms.wrap() calc = SinglePointCalculator(atoms, energy=energy, forces=forces, stress=stress) atoms.set_calculator(calc) raw_score = -atoms.get_potential_energy() set_raw_score(atoms, raw_score)
def test_add_candidates(): import pytest from ase.build import fcc111 from ase.ga.data import PrepareDB from ase.ga.data import DataConnection from ase.ga.offspring_creator import OffspringCreator from ase.ga import set_raw_score import os db_file = 'gadb.db' if os.path.isfile(db_file): os.remove(db_file) db = PrepareDB(db_file) slab1 = fcc111('Ag', size=(2, 2, 2)) db.add_unrelaxed_candidate(slab1) slab2 = fcc111('Cu', size=(2, 2, 2)) set_raw_score(slab2, 4) db.add_relaxed_candidate(slab2) assert slab2.info['confid'] == 3 db = DataConnection(db_file) assert db.get_number_of_unrelaxed_candidates() == 1 slab3 = db.get_an_unrelaxed_candidate() old_confid = slab3.info['confid'] slab3[0].symbol = 'Au' db.add_unrelaxed_candidate(slab3, 'mutated: Parent {0}'.format(old_confid)) new_confid = slab3.info['confid'] # confid should update when using add_unrelaxed_candidate assert old_confid != new_confid slab3[1].symbol = 'Au' db.add_unrelaxed_step(slab3, 'mutated: Parent {0}'.format(new_confid)) # confid should not change when using add_unrelaxed_step assert slab3.info['confid'] == new_confid with pytest.raises(AssertionError): db.add_relaxed_step(slab3) set_raw_score(slab3, 3) db.add_relaxed_step(slab3) slab4 = OffspringCreator.initialize_individual(slab1, fcc111('Au', size=(2, 2, 2))) set_raw_score(slab4, 67) db.add_relaxed_candidate(slab4) assert slab4.info['confid'] == 7 more_slabs = [] for m in ['Ni', 'Pd', 'Pt']: slab = fcc111(m, size=(2, 2, 2)) slab = OffspringCreator.initialize_individual(slab1, slab) set_raw_score(slab, sum(slab.get_masses())) more_slabs.append(slab) db.add_more_relaxed_candidates(more_slabs) assert more_slabs[1].info['confid'] == 9 os.remove(db_file)
# Pass parameters to the population instance # A variable_function is required to divide candidates into groups here we use # the chemical composition pop = RankFitnessPopulation(data_connection=db, population_size=pop_size, variable_function=get_comp) # Evaluate the starting population # The only requirement of the evaluation is to set the raw_score # Negative mixing energy means more stable than the pure slabs # The optimization always progress towards larger raw score, # so we take the negative mixing energy as the raw score print('Evaluating initial candidates') while db.get_number_of_unrelaxed_candidates() > 0: a = db.get_an_unrelaxed_candidate() set_raw_score(a, -get_mixing_energy(a)) db.add_relaxed_step(a) pop.update() # Below is the iterative part of the algorithm gen_num = db.get_generation_number() for i in range(num_gens): print('Creating and evaluating generation {0}'.format(gen_num + i)) new_generation = [] for _ in range(pop_size): # Select parents for a new candidate parents = pop.get_two_candidates() # Select an operator and use it op = operation_selector.get_operator() offspring, desc = op.get_new_individual(parents)
syms = a2.get_chemical_symbols() assert 'Ba' in syms assert len(set(syms)) == 3 op = MoveUpMutation(cations, 1, 1.) a3, desc = op.get_new_individual([a2]) syms = a3.get_chemical_symbols() assert 'Ba' not in syms assert len(set(syms)) == 2 cations = ['Co', 'Ni', 'Cu'] a1 = Atoms('NiNiBrBr') a1.info['confid'] = 1 op = MoveRightMutation(cations, 1, 1.) a2, desc = op.get_new_individual([a1]) a2.info['confid'] = 2 syms = a2.get_chemical_symbols() assert len(set(syms)) == 2 assert len([i for i in syms if i == 'Cu']) == 2 op = MoveLeftMutation(cations, 2, .5) a3, desc = op.get_new_individual([a2]) syms = a3.get_chemical_symbols() from ase.ga import set_raw_score, get_raw_score assert len(set(syms)) == 3 set_raw_score(a3, 5.0) assert get_raw_score(a3) == 5.0
pairing = CutAndSplicePairing(slab, n_to_optimize, blmin) mutations = OperationSelector([1., 1., 1.], [ MirrorMutation(blmin, n_to_optimize), RattleMutation(blmin, n_to_optimize), PermutationMutation(n_to_optimize) ]) # Relax all unrelaxed structures (e.g. the starting population) while da.get_number_of_unrelaxed_candidates() > 0: a = da.get_an_unrelaxed_candidate() a.set_calculator(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) # 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)
# 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 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
dE=0.15) assert not comp2.looks_like(a1, a2) comp3 = InteratomicDistanceComparator(n_top=3, pair_cor_cum_diff=0.02, pair_cor_max=0.7, dE=0.3) assert not comp3.looks_like(a1, a2) hard_E_comp = EnergyComparator(dE=1.0) assert hard_E_comp.looks_like(a1, a2) soft_E_comp = EnergyComparator(dE=.01) assert not soft_E_comp.looks_like(a1, a2) set_raw_score(a1, .1) set_raw_score(a2, .27) rs_comp = RawScoreComparator(0.15) assert not rs_comp.looks_like(a1, a2) comp1 = SequentialComparator([hard_E_comp, rs_comp], [0, 0]) assert not comp1.looks_like(a1, a2) comp2 = SequentialComparator([hard_E_comp, rs_comp], [0, 1]) assert comp2.looks_like(a1, a2)
mic=False) pairing = CutAndSplicePairing(slab, n_to_optimize, blmin) mutations = OperationSelector([1., 1., 1.], [MirrorMutation(blmin, n_to_optimize), RattleMutation(blmin, n_to_optimize), PermutationMutation(n_to_optimize)]) # Relax all unrelaxed structures (e.g. the starting population) while da.get_number_of_unrelaxed_candidates() > 0: a = da.get_an_unrelaxed_candidate() a.set_calculator(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) # 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)
from ase.ga.offspring_creator import OffspringCreator from ase.ga import set_raw_score import os db_file = 'gadb.db' if os.path.isfile(db_file): os.remove(db_file) db = PrepareDB(db_file) slab1 = fcc111('Ag', size=(2, 2, 2)) db.add_unrelaxed_candidate(slab1) slab2 = fcc111('Cu', size=(2, 2, 2)) set_raw_score(slab2, 4) db.add_relaxed_candidate(slab2) assert slab2.info['confid'] == 3 db = DataConnection(db_file) assert db.get_number_of_unrelaxed_candidates() == 1 slab3 = db.get_an_unrelaxed_candidate() old_confid = slab3.info['confid'] slab3[0].symbol = 'Au' db.add_unrelaxed_candidate(slab3, 'mutated: Parent {0}'.format(old_confid)) new_confid = slab3.info['confid'] # confid should update when using add_unrelaxed_candidate assert old_confid != new_confid slab3[1].symbol = 'Au' db.add_unrelaxed_step(slab3, 'mutated: Parent {0}'.format(new_confid))
def test_element_operators(seed): import numpy as np from ase import Atoms from ase.ga.element_crossovers import OnePointElementCrossover # set up the random number generator rng = np.random.RandomState(seed) a1 = Atoms('SrSrSrBaClClClClBrBrBrBr') a1.info['confid'] = 1 a2 = Atoms('CaCaMgBaFFFFFFFF') a2.info['confid'] = 2 cations = ['Sr', 'Ba', 'Ca', 'Mg'] anions = ['Cl', 'F', 'Br'] op = OnePointElementCrossover([cations, anions], [3, 2], [.25, .5], rng=rng) a3, desc = op.get_new_individual([a1, a2]) syms = a3.get_chemical_symbols() assert len(set([i for i in syms if i in cations])) < 4 assert len(set([i for i in syms if i in anions])) < 3 from ase.ga.element_mutations import RandomElementMutation op = RandomElementMutation([cations, anions], [3, 2], [.25, .5], rng=rng) a4, desc = op.get_new_individual([a1]) syms = a4.get_chemical_symbols() assert len(set([i for i in syms if i in cations])) < 4 assert len(set([i for i in syms if i in anions])) < 3 op = RandomElementMutation(anions, 2, .5, rng=rng) a4, desc = op.get_new_individual([a2]) syms = a4.get_chemical_symbols() assert len(set([i for i in syms if i in anions])) == 2 from ase.ga.element_mutations import MoveDownMutation from ase.ga.element_mutations import MoveUpMutation from ase.ga.element_mutations import MoveRightMutation from ase.ga.element_mutations import MoveLeftMutation a1 = Atoms('SrSrClClClCl') a1.info['confid'] = 1 op = MoveDownMutation(cations, 2, .5, rng=rng) a2, desc = op.get_new_individual([a1]) a2.info['confid'] = 2 syms = a2.get_chemical_symbols() assert 'Ba' in syms assert len(set(syms)) == 3 op = MoveUpMutation(cations, 1, 1., rng=rng) a3, desc = op.get_new_individual([a2]) syms = a3.get_chemical_symbols() assert 'Ba' not in syms assert len(set(syms)) == 2 cations = ['Co', 'Ni', 'Cu'] a1 = Atoms('NiNiBrBr') a1.info['confid'] = 1 op = MoveRightMutation(cations, 1, 1., rng=rng) a2, desc = op.get_new_individual([a1]) a2.info['confid'] = 2 syms = a2.get_chemical_symbols() assert len(set(syms)) == 2 assert len([i for i in syms if i == 'Cu']) == 2 op = MoveLeftMutation(cations, 2, .5, rng=rng) a3, desc = op.get_new_individual([a2]) syms = a3.get_chemical_symbols() from ase.ga import set_raw_score, get_raw_score assert len(set(syms)) == 3 set_raw_score(a3, 5.0) assert get_raw_score(a3) == 5.0
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())
for m in metals: slab = fcc111(m, size=(2, 4, 3), a=lattice_constants[m], vacuum=5, orthogonal=True) slab.set_calculator(EMT()) # We save the reference energy as E_A / N e = slab.get_potential_energy() e_per_atom = e / len(slab) refs[m] = e_per_atom print('{0} = {1:.3f} eV/atom'.format(m, e_per_atom)) # The mixing energy for the pure slab is 0 by definition set_raw_score(slab, 0.0) pure_slabs.append(slab) # The population size should be at least the number of different compositions pop_size = 2 * len(slab) # We prepare the db and write a few constants that we are going to use later db = PrepareDB('hull.db', population_size=pop_size, reference_energies=refs, metals=metals, lattice_constants=lattice_constants) # We add the pure slabs to the database as relaxed because we have already # set the raw_score for slab in pure_slabs:
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
comp2 = InteratomicDistanceComparator(n_top=3, pair_cor_cum_diff=0.03, pair_cor_max=0.7, dE=0.15) assert not comp2.looks_like(a1, a2) comp3 = InteratomicDistanceComparator(n_top=3, pair_cor_cum_diff=0.02, pair_cor_max=0.7, dE=0.3) assert not comp3.looks_like(a1, a2) hard_E_comp = EnergyComparator(dE=1.0) assert hard_E_comp.looks_like(a1, a2) soft_E_comp = EnergyComparator(dE=.01) assert not soft_E_comp.looks_like(a1, a2) set_raw_score(a1, .1) set_raw_score(a2, .27) rs_comp = RawScoreComparator(0.15) assert not rs_comp.looks_like(a1, a2) comp1 = SequentialComparator([hard_E_comp, rs_comp], [0, 0]) assert not comp1.looks_like(a1, a2) comp2 = SequentialComparator([hard_E_comp, rs_comp], [0, 1]) assert comp2.looks_like(a1, a2)