def prepare_ga(dbfile='godb.db', splits={(2,): 1}, N=20): blocks = [('Pd', 4), ('OH', 8)] # the building blocks volume = 50. * 4 # volume in angstrom^3 l = [list(Atoms(block).numbers)*count for block, count in blocks] stoichiometry = [item for sublist in l for item in sublist] atom_numbers = list(set(stoichiometry)) blmin = closest_distances_generator(atom_numbers=atom_numbers, ratio_of_covalent_radii=0.6) blmin[(1, 8)] = blmin[(8, 1)] = 2.0 cellbounds = CellBounds(bounds={'phi': [0.2 * 180., 0.8 * 180.], 'chi': [0.2 * 180., 0.8 * 180.], 'psi': [0.2 *180., 0.8 * 180.], 'a': [2, 8], 'b': [2, 8], 'c': [2, 8]}) # create the starting population sg = StartGenerator(blocks, blmin, volume, cellbounds=cellbounds, splits=splits) # create the database to store information in da = PrepareDB(db_file_name=dbfile, stoichiometry=stoichiometry) for i in range(N): a = sg.get_new_candidate() a.set_initial_magnetic_moments(magmoms=None) niggli_reduce(a) da.add_unrelaxed_candidate(a) return
def __init__(self, blocks, blmin, volume, cellbounds=None, cell=None, splits={(1, ): 1}): self.volume = volume self.cellbounds = cellbounds self.cell = cell # normalize splitting probabilities: tot = sum([v for v in splits.values()]) self.splits = {k: v * 1. / tot for k, v in splits.items()} # pre-process blocks and blmin: self.blocks = [] numbers = [] for block, count in blocks: if isinstance(block, Atoms): pass elif block in atomic_numbers: block = Atoms(block) else: block = molecule(block) self.blocks.append((block, count)) numbers.extend(block.get_atomic_numbers()) numbers = np.unique(numbers) if type(blmin) == dict: self.blmin = blmin else: self.blmin = closest_distances_generator(numbers, blmin)
def prepare_startGenerator(): cell = np.array([[24, 0, 0], [0, 24, 0], [0, 0, 24]]) pbc = [False, False, False] # Define template slab = Atoms('', cell=cell, pbc=pbc) # Define the box to place the atoms within # The volume is defined by a corner position (p0) and three spanning vectors (v) v = np.array([[4, 0, 0], [0, 4, 0], [0, 0, 4]]) p0 = np.diag((cell-v)/2) # Define the composition of the atoms to optimize atom_numbers = [6]*24 # 24 carbon atoms # 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, v], elliptic = False, cluster = True) return sg
def run_ga(n_to_test): """ This method specifies how to run the GA once the initial random structures have been stored in godb.db. """ # Various initializations: population_size = 10 # maximal size of the population da = DataConnection('godb.db') atom_numbers_to_optimize = da.get_atom_numbers_to_optimize() # = [14] * 7 n_to_optimize = len(atom_numbers_to_optimize) # = 7 # This defines how close the Si atoms are allowed to get # in candidate structures generated by the genetic operators: blmin = closest_distances_generator(atom_numbers_to_optimize, ratio_of_covalent_radii=0.4) # This is our OFPComparator instance which will be # used to judge whether or not two structures are identical: comparator = OFPComparator(n_top=None, dE=1.0, cos_dist_max=1e-3, rcut=10., binwidth=0.05, pbc=[False]*3, sigma=0.1, nsigma=4, recalculate=False) # Defining a typical combination of genetic operators: pairing = CutAndSplicePairing(da.get_slab(), n_to_optimize, blmin) rattlemut = RattleMutation(blmin, n_to_optimize, rattle_prop=0.8, rattle_strength=1.5) operators = OperationSelector([2., 1.], [pairing, rattlemut]) # Relax the randomly generated initial candidates: while da.get_number_of_unrelaxed_candidates() > 0: a = da.get_an_unrelaxed_candidate() a = relax_one(a) da.add_relaxed_step(a) # Create the population population = Population(data_connection=da, population_size=population_size, comparator=comparator, logfile='log.txt') current_pop = population.get_current_population() # Test n_to_test new candidates for step in range(n_to_test): print('Starting configuration number %d' % step, flush=True) a3 = None while a3 is None: a1, a2 = population.get_two_candidates() a3, description = operators.get_new_individual([a1, a2]) da.add_unrelaxed_candidate(a3, description=description) a3 = relax_one(a3) da.add_relaxed_step(a3) population.update() best = population.get_current_population()[0] print('Highest raw score at this point: %.3f' % get_raw_score(best)) print('GA finished after step %d' % step) write('all_candidates.traj', da.get_all_relaxed_candidates()) write('current_population.traj', population.get_current_population())
def __init__(self, atoms, rcut=5, ratio_of_covalent_radii=0.7): self.rcut = rcut self.pbc = atoms.get_pbc() self.cell = atoms.get_cell() self.cell_displacements = self.__get_neighbour_cells_displacement(self.pbc, self.cell) self.Ncells = len(self.cell_displacements) num = atoms.get_atomic_numbers() self.blmin = closest_distances_generator(num, ratio_of_covalent_radii=ratio_of_covalent_radii)
def relax_one(t): ''' This method defines how to locally minimize a given atoms object 't'. ''' # The provided structure will often contain atoms separated # by relatively short distances. Pushing these atoms a bit # apart using a soft potential will reduce the number of # subsequent ionic steps and will help avoid DFTB convergence # problems. pos = t.get_positions() numbers = list(set(t.get_atomic_numbers())) blmin = closest_distances_generator(numbers, 0.5) t = push_apart(t, blmin, variable_cell=False) print('Starting relaxation', flush=True) clock = time() t.wrap() # Define the DFTB+ calculator: calc = DftbPlusCalculator(t, kpts=(1, 1, 1), use_spline=True, maximum_angular_momenta={'Si': 1}) # Start the actual relaxation using the # tango.relax_utils.relax_precon method. # This wraps around the preconditioned # optimizers in ASE and also takes care of # setting the raw score etc.: try: t = relax_precon(t, calc, fmax=1e-2, variable_cell=False, logfile=None, trajfile=None) except IOError: # the DFTB+ ASE calculator throws an IOError # in case it couldn't find the 'results.tag' # output file, which may sporadically happen # due to SCC converge issues when e.g. a Si7 # cluster is breaking into fragments. We simply # handle this by aborting the relaxation and # assigning a very high energy to the structure: finalize(t, energy=1e9, forces=None, stress=None) print('Relaxing took %.3f seconds.' % (time() - clock), flush=True) return t
def ga_init(self, ddiff, dmax, dE): self.ddiff = ddiff self.dmax = dmax self.dE = dE da = DataConnection(self.db_name) atom_numbers_to_optimize = da.get_atom_numbers_to_optimize( ) # adsorbate atom numbers to optimize n_to_optimize = len( atom_numbers_to_optimize) # number of atoms 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=.7) # closest distance atoms can be comp = InteratomicDistanceComparator( n_top=None, pair_cor_cum_diff=self.ddiff, pair_cor_max=dmax, dE=dE, mic=True) # comparator to determine if parents should make childer pairing = CutAndSplicePairing( blmin, None, use_tags=True, p1=.2 ) # how children are generated (make sure your adsorbates are uniquely tagged) population = Population(data_connection=da, population_size=self.pop, comparator=comp) for i in range(self.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]) #print(a3.info) #view(a3) if a3 is None: continue da.add_unrelaxed_candidate(a3, description=desc)
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)
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_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 relax_one(t, kptdensity=1.5): 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.5, 20], 'b': [1.5, 20], 'c': [1.5, 20] }) if not cellbounds.is_within_bounds(t.get_cell()): print('Candidate outside cellbounds -- skipping') finalize(t, energy=1e9, forces=None, stress=None) return t pos = t.get_positions() numbers = list(set(t.get_atomic_numbers())) blmin = closest_distances_generator(numbers, 0.5) t = push_apart(t, blmin, variable_cell=True) print('Starting relaxation', flush=True) clock = time() t.wrap() calc = DftbPlusCalc(t, kpts=0.66 * kptdensity, use_spline=True, read_chg=True) try: t = relax_precon(t, calc, fmax=5e-1, smax=5e-2, variable_cell=True, optimizer='LBFGS', a_min=1e-4, cellbounds=cellbounds, logfile='opt_first.log', trajfile='opt_first.traj') except IOError as err: # probably convergence problem print(err.message) except TypeError as err: if 'Cannot cast array data' in err.message: # Rare precon failure print(err.message) else: raise except RuntimeError as err: print(err.message) del t.constraints t.wrap() calc = DftbPlusCalc(t, kpts=kptdensity, use_spline=True, read_chg=True) try: t = relax_precon(t, calc, fmax=5e-1, smax=5e-2, variable_cell=True, optimizer='LBFGS', a_min=1e-4, cellbounds=cellbounds, logfile='opt.log', trajfile='opt.traj') except (IOError, TypeError, RuntimeError) as err: # probably convergence problem print(err.message) if isinstance(err, TypeError): if 'Cannot cast array data' not in err.message: raise elif isinstance(err, RuntimeError): if 'dftb_my in . returned an error:' not in err.message: raise try: t = read('opt.traj@-1') energy = t.get_potential_energy() forces = t.get_forces() stress = t.get_stress() except UnknownFileTypeError as err: print(err.message) energy, forces, stress = (1e9, None, None) finalize(t, energy=energy, forces=forces, stress=stress) print('Relaxing took %.3f seconds.' % (time() - clock), flush=True) os.system('mv opt_first.traj prev_first.traj') os.system('mv opt_first.log prev_first.log') os.system('mv opt.log prev.log') os.system('mv opt.traj prev.traj') penalize(t) return t
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:])
def construct_init_parent(self, ads_cell, ads_pos): """ construct the initial set of parents """ self.ads_cell = ads_cell self.ads_pos = ads_pos Ads_vol = ads_cell[0] * ads_cell[1] * ads_cell[2] Ads_cell = [[ads_cell[0], 0, 0], [0, ads_cell[1], 0], [0, 0, ads_cell[2]]] if self.n_ads == 1: Atom_Num = self.Ads.get_atomic_numbers( ) # get atomic numbers of your adsorbate if self.n_ads == 2: TEMP_for_num = self.Ads[0] + self.Ads[1] Atom_Num = TEMP_for_num.get_atomic_numbers() #Atom_Num.append(self.Ads[1].get_atomic_numbers()) unique_atom_types = get_all_atom_types( self.FW, Atom_Num) # get atomic numbers of your system cd = closest_distances_generator( atom_numbers=unique_atom_types, ratio_of_covalent_radii=0.7 ) # Generate a dictionary of closest distances pop = self.pop_size if self.n_ads == 1: sg = StartGenerator( [(self.Ads, self.n_ads)], # Generator Parameters cd, Ads_vol, cell=Ads_cell) starting_population = [sg.get_new_candidate() for i in range(pop)] for i in range(pop): TEMPMOL = self.FW + starting_population[ i] # Add the adsorbates to the frame work self.Start_Set.append(TEMPMOL) num = len(self.Ads) if self.n_ads == 2: sg = StartGenerator([(self.Ads[0], 1), (self.Ads[1], 1)], cd, Ads_vol, cell=Ads_cell) starting_population = [sg.get_new_candidate() for i in range(pop)] for i in range(pop): TEMPMOL = self.FW + starting_population[i] self.Start_Set.append(TEMPMOL) num = len(self.Ads[0]) + len(self.Ads[1]) Cent_Pos = self.ads_pos #num = len(self.Ads)*self.n_ads Fin_atoms = np.linspace(0, num - 1, num) for i in range(pop): for j in Fin_atoms: Coord = self.Start_Set[i].get_positions()[-( int(j) + 1 )] + Cent_Pos # Move the corner of the adsorbate cell to p.o.i. self.Start_Set[i][-(int(j) + 1)].x = Coord[0] - Ads_cell[0][ 0] / 2 # Then you have to fudge the box so its centered self.Start_Set[i][-(int(j) + 1)].y = Coord[ 1] - Ads_cell[1][1] / 2 # around the p.o.i. self.Start_Set[i][-(int(j) + 1)].z = Coord[2] - Ads_cell[2][2] / 2 self.Start_Set[i].wrap()
# 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, 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
from ase.ga.standardmutations import MirrorMutation from ase.ga.standardmutations import RattleMutation from ase.ga.standardmutations import PermutationMutation # Change the following three parameters to suit your needs population_size = 20 mutation_probability = 0.3 n_to_test = 20 # 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) 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)
def run_calc(dbfile, Calculator, kptdensity=None, relax=False, vc_relax=False, precon=True, maxsteps=20, maximum_angular_momenta=None, atomic_energies={}, referencing='atomic'): ''' Runs a calculator on each unrelaxed candidate in a database. dbfile: name of the database Calculator: a suitable calculator DFT or DFTB calculator class (see tango.main) kptdensity: the k-point density to apply (in 1/A) relax: whether to do a short relaxation or only perform a single-point calculation vc_relax: if also the cell vectors are to be varied precon: whether to use the preconditioned optimizers maxsteps: maximum number of ionic steps for the local optimizer maximum_angular_momenta: a dictionary with maximum angular momenta for each element in the structure for DFTB calculations atomic_energies: dictionary with the DFT energies of the isolated atoms. Used to calculate the reference energies in the 'atomic' referencing scheme. referencing: the referencing scheme (see main.py) ''' if vc_relax: assert precon db = connect(dbfile) relaxed_ids = set([row.gaid for row in db.select(relaxed=1)]) for row in db.select(relaxed=0): if row.gaid in relaxed_ids: continue atoms = row.toatoms() mp = get_kpts(atoms, kptdensity) if maximum_angular_momenta is None: calc = Calculator(atoms, kpts=mp) else: calc = Calculator(atoms, kpts=mp, maximum_angular_momenta=maximum_angular_momenta) atoms.set_calculator(calc) E = atoms.get_potential_energy() F = atoms.get_forces() try: S = atoms.get_stress() except PropertyNotImplementedError: S = None finalize(atoms, energy=E, forces=F, stress=S) relax = relax and maxsteps > 0 if relax: atoms2 = atoms.copy() numbers = list(set(atoms2.get_atomic_numbers())) blmin = closest_distances_generator(numbers, 0.5) atoms2 = push_apart(atoms2, blmin) atoms2.set_calculator(calc) atoms2 = do_short_relax(atoms2, index=row.gaid, precon=precon, vc_relax=vc_relax, maxsteps=maxsteps) if vc_relax: # Additional single-point run calc.exit() mp = get_kpts(atoms2, kptdensity) if maximum_angular_momenta is None: calc = Calculator(atoms2, kpts=mp) else: calc = Calculator(atoms2, kpts=mp, maximum_angular_momenta=maximum_angular_momenta) atoms2.set_calculator(calc) E = atoms2.get_potential_energy() F = atoms2.get_forces() try: S = atoms2.get_stress() except PropertyNotImplementedError: S = None finalize(atoms2, energy=E, forces=F, stress=S) # Calculate energy and force references for a in [atoms] if not relax else [atoms, atoms2]: e_ref = [] f_ref = np.zeros((len(atoms), 3)) if referencing == 'atomic': sym = a.get_chemical_symbols() e_ref.extend([atomic_energies['%s_DFT' % s] for s in sym]) else: for indices in referencing: b = a[indices] b.set_calculator(calc) e_ref.append(b.get_potential_energy()) f_ref[indices] = b.get_forces() a.info['key_value_pairs']['e_dft_ref'] = convert_array(e_ref) a.info['key_value_pairs']['f_dft_ref'] = convert_array(f_ref) # Add the structure to the database: db.write(a, relaxed=1, gaid=row.gaid, **a.info['key_value_pairs']) calc.exit() return
from ase.ga.offspring_creator import OperationSelector from ase.ga.cutandsplicepairing import CutAndSplicePairing from ase.ga.standardmutations import (RattleMutation, StrainMutation, RotationalMutation, RattleRotationalMutation) from ase.ga.soft_mutation import SoftMutation from ga_molecular_crystal_relax import relax da = DataConnection('gadb.db') # Various items needed for initializing the genetic operators slab = da.get_slab() atom_numbers_to_optimize = da.get_atom_numbers_to_optimize() n_top = len(atom_numbers_to_optimize) 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,
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 __init__(self, slab, blocks, blmin, number_of_variable_cell_vectors=0, box_to_place_in=None, box_volume=None, splits=None, cellbounds=None, test_dist_to_slab=True, test_too_far=True, rng=np.random): self.slab = slab self.blocks = [] for item in blocks: if isinstance(item, tuple) or isinstance(item, list): assert len(item) == 2, 'Item length %d != 2' % len(item) block, count = item else: block, count = item, 1 # Convert block into Atoms object if isinstance(block, Atoms): pass elif block in atomic_numbers: block = Atoms(block) elif isinstance(block, str): block = molecule(block) elif block in atomic_numbers.values(): block = Atoms(numbers=[block]) else: raise ValueError('Cannot parse this block:', block) # Add to self.blocks, taking into account that # we want to group the same blocks together. # This is important for the cell splitting. for i, (b, c) in enumerate(self.blocks): if block == b: self.blocks[i][1] += count break else: self.blocks.append([block, count]) if isinstance(blmin, dict): self.blmin = blmin else: numbers = np.unique([b.get_atomic_numbers() for b in self.blocks]) self.blmin = closest_distances_generator( numbers, ratio_of_covalent_radii=blmin) self.number_of_variable_cell_vectors = number_of_variable_cell_vectors assert self.number_of_variable_cell_vectors in range(4) if len(self.slab) > 0: msg = 'Including atoms in the slab only makes sense' msg += ' if there are no variable unit cell vectors' assert self.number_of_variable_cell_vectors == 0, msg for i in range(self.number_of_variable_cell_vectors): msg = 'Unit cell %s-vector is marked as variable ' % ('abc'[i]) msg += 'and slab must then also be periodic in this direction' assert self.slab.pbc[i], msg if box_to_place_in is None: p0 = np.array([0., 0., 0.]) cell = self.slab.get_cell() self.box_to_place_in = [p0, [cell[0, :], cell[1, :], cell[2, :]]] else: self.box_to_place_in = box_to_place_in if box_volume is None: assert self.number_of_variable_cell_vectors == 0 box_volume = abs(np.linalg.det(self.box_to_place_in[1])) else: assert self.number_of_variable_cell_vectors > 0 self.box_volume = box_volume assert self.box_volume > 0 if splits is None: splits = {(1, ): 1} tot = sum([v for v in splits.values()]) # normalization self.splits = {k: v * 1. / tot for k, v in splits.items()} self.cellbounds = cellbounds self.test_too_far = test_too_far self.test_dist_to_slab = test_dist_to_slab self.rng = rng
def test_bulk_operators(): 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]}) sg = StartGenerator(blocks, blmin, volume, cellbounds=cellbounds, splits=splits) # 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 pairing = CutAndSplicePairing(blmin, p1=1., p2=0., minfrac=0.15, cellbounds=cellbounds, use_tags=True) 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, use_tags=True) softmut = SoftMutation(blmin, bounds=[2., 5.], used_modes_file=None, use_tags=True) rotmut = RotationalMutation(blmin, fraction=0.3, min_angle=0.5 * np.pi) rattlemut = RattleMutation(blmin, n_top, rattle_prop=0.3, rattle_strength=0.5, use_tags=True, test_dist_to_slab=False) rattlerotmut = RattleRotationalMutation(rattlemut, rotmut) permut = PermutationMutation(n_top, probability=0.33, test_dist_to_slab=False, use_tags=True, blmin=blmin) combmut = CombinationMutation(rattlemut, rotmut, verbose=True) 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_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)
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 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,
def run_ga(n_to_test, kptdensity=None): ''' This method specifies how to run the GA once the initial random structures have been stored in godb.db. ''' # Various initializations: population_size = 10 da = DataConnection('godb.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.05) # Defining the mix of genetic operators: mutation_probability = 0.3333 pairing = CutAndSplicePairing(slab, n_to_optimize, blmin) rattlemut = RattleMutation(blmin, n_to_optimize, rattle_prop=0.8, rattle_strength=1.5) mirrormut = MirrorMutation(blmin, n_to_optimize) mutations = OperationSelector([1., 1.], [rattlemut, mirrormut]) if True: # Recalculate raw scores of any relaxed candidates # present in the godb.db database (only applies to # iter007). structures = da.get_all_relaxed_candidates() for atoms in structures: atoms = singlepoint(atoms) da.c.delete([atoms.info['relax_id']]) if 'data' not in atoms.info: atoms.info['data'] = {} da.add_relaxed_step(atoms) print('Finished recalculating raw scores') # Relax the randomly generated initial candidates: while da.get_number_of_unrelaxed_candidates() > 0: a = da.get_an_unrelaxed_candidate() a.wrap() a = relax_one(a) da.add_relaxed_step(a) # Create the population population = Population(data_connection=da, population_size=population_size, comparator=comparator, logfile='log.txt') current_pop = population.get_current_population() # Test n_to_test new candidates ga_raw_scores = [] step = 0 for step in range(n_to_test): print('Starting configuration number %d' % step, flush=True) clock = time() a3 = None r = random() if r > mutation_probability: while a3 is None: a1, a2 = population.get_two_candidates() a3, desc = pairing.get_new_individual([a1, a2]) else: while a3 is None: a1 = population.get_one_candidate() a3, desc = mutations.get_new_individual([a1]) dt = time() - clock op = 'pairing' if r > mutation_probability else 'mutating' print('Time for %s candidate(s): %.3f' % (op, dt), flush=True) a3.wrap() da.add_unrelaxed_candidate(a3, description=desc) a3 = relax_one(a3) da.add_relaxed_step(a3) # Various updates: population.update() current_pop = population.get_current_population() write('current_population.traj', current_pop) # Print out information for easy analysis/plotting afterwards: if r > mutation_probability: print('Step %d %s %.3f %.3f %.3f' % (step, desc,\ get_raw_score(a1), get_raw_score(a2), get_raw_score(a3))) else: print('Step %d %s %.3f %.3f' % (step, desc,\ get_raw_score(a1), get_raw_score(a3))) print('Step %d highest raw score in pop: %.3f' % \ (step, get_raw_score(current_pop[0]))) ga_raw_scores.append(get_raw_score(a3)) print('Step %d highest raw score generated by GA: %.3f' % \ (step, max(ga_raw_scores))) emin = population.pop[0].get_potential_energy() print('GA finished after step %d' % step) print('Lowest energy = %8.3f eV' % emin, flush=True) write('all_candidates.traj', da.get_all_relaxed_candidates()) write('current_population.traj', population.get_current_population())
# 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) 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
volume = 240. # Specify the 'building blocks' from which the initial structures # will be constructed. Here we take single Ag atoms as building # blocks, 24 in total. blocks = [('Ag', 24)] # From these blocks we can determine the stoichiometry # (as a list of atomic numbers) stoichiometry = [ atomic_numbers[atom] for atom, count in blocks for _ in range(count) ] # Generate a dictionary with the closest allowed interatomic distances atom_numbers = list(set(stoichiometry)) blmin = closest_distances_generator(atom_numbers=atom_numbers, ratio_of_covalent_radii=0.5) # Specify reasonable bounds on the minimal and maximal # cell vector lengths (in angstrom) and angles (in degrees) cellbounds = CellBounds( bounds={ 'phi': [35, 145], 'chi': [35, 145], 'psi': [35, 145], 'a': [3, 50], 'b': [3, 50], 'c': [3, 50] }) # Choose an (optional) 'cell splitting' scheme which basically # controls the level of translational symmetry (within the unit
from ase.ga.standardmutations import MirrorMutation from ase.ga.standardmutations import RattleMutation from ase.ga.standardmutations import PermutationMutation # 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) mutations = OperationSelector( [1.0, 1.0, 1.0], [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)
def run_ga(n_to_test, kptdensity=3.5): population_size = 20 da = DataConnection('godb.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, 0.05) # 0.5 # defining genetic operators: mutation_probability = 0.75 pairing = CutAndSplicePairing(blmin, p1=1., p2=0., minfrac=0.15, use_tags=False) cellbounds = CellBounds( bounds={ 'phi': [0.2 * 180., 0.8 * 180.], 'chi': [0.2 * 180., 0.8 * 180.], 'psi': [0.2 * 180., 0.8 * 180.] }) strainmut = StrainMutation(blmin, stddev=0.7, cellbounds=cellbounds, use_tags=False) blmin_soft = closest_distances_generator(all_atom_types, 0.1) softmut = SoftMutation(blmin_soft, bounds=[2., 5.], use_tags=False) rattlemut = RattleMutation(blmin, n_to_optimize, rattle_prop=0.8, rattle_strength=2.5, use_tags=False) mutations = OperationSelector([4., 4., 2], [softmut, strainmut, rattlemut]) if True: # recalculate raw scores structures = da.get_all_relaxed_candidates() for atoms in structures: atoms = singlepoint(atoms, kptdensity=kptdensity) da.c.delete([atoms.info['relax_id']]) if 'data' not in atoms.info: atoms.info['data'] = {} da.add_relaxed_step(atoms) print('Finished recalculating raw scores') # relaxing the initial candidates: while da.get_number_of_unrelaxed_candidates() > 0: a = da.get_an_unrelaxed_candidate() a.wrap() a = relax_one(a, kptdensity=kptdensity) da.add_relaxed_step(a) # create the population population = Population(data_connection=da, population_size=population_size, comparator=comparator, logfile='log.txt') current_pop = population.get_current_population() strainmut.update_scaling_volume(current_pop, w_adapt=0.5, n_adapt=4) pairing.update_scaling_volume(current_pop, w_adapt=0.5, n_adapt=4) # Test n_to_test new candidates ga_raw_scores = [] step = 0 for step in range(n_to_test): print('Starting configuration number %d' % step, flush=True) clock = time() a3 = None r = random() if r > mutation_probability: while a3 is None: a1, a2 = population.get_two_candidates() a3, desc = pairing.get_new_individual([a1, a2]) else: while a3 is None: a1 = population.get_one_candidate() a3, desc = mutations.get_new_individual([a1]) dt = time() - clock op = 'pairing' if r > mutation_probability else 'mutating' print('Time for %s candidate(s): %.3f' % (op, dt), flush=True) a3.wrap() da.add_unrelaxed_candidate(a3, description=desc) a3 = relax_one(a3, kptdensity=kptdensity) da.add_relaxed_step(a3) # Various updates: population.update() current_pop = population.get_current_population() if step % 10 == 0: strainmut.update_scaling_volume(current_pop, w_adapt=0.5, n_adapt=4) pairing.update_scaling_volume(current_pop, w_adapt=0.5, n_adapt=4) write('current_population.traj', current_pop) # Print out information for easy analysis/plotting afterwards: if r > mutation_probability: print('Step %d %s %.3f %.3f %.3f' % (step, desc,\ get_raw_score(a1), get_raw_score(a2), get_raw_score(a3))) else: print('Step %d %s %.3f %.3f' % (step, desc,\ get_raw_score(a1), get_raw_score(a3))) print('Step %d highest raw score in pop: %.3f' % \ (step, get_raw_score(current_pop[0]))) ga_raw_scores.append(get_raw_score(a3)) print('Step %d highest raw score generated by GA: %.3f' % \ (step, max(ga_raw_scores))) emin = population.pop[0].get_potential_energy() print('GA finished after step %d' % step) print('Lowest energy = %8.3f eV' % emin, flush=True) write('all_candidates.traj', da.get_all_relaxed_candidates()) write('current_population.traj', population.get_current_population())
# By writing 'N2', the generator will automatically # get the N2 geometry using ase.build.molecule. # A guess for the cell volume in Angstrom^3 box_volume = 30. * 8 # The cell splitting scheme: splits = {(2,):1, (1,):1} # The minimal interatomic distances which the # initial structures must satisfy. We can take these # a bit larger than usual because these minimal # distances will only be applied intermolecularly # (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,
def relax_one(t, kptdensity=3.5): 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.5, 20], 'b': [1.5, 20], 'c': [1.5, 20] }) if not cellbounds.is_within_bounds(t.get_cell()): print('Candidate outside cellbounds -- skipping') finalize(t, energy=1e9, forces=None, stress=None) return t pos = t.get_positions() numbers = list(set(t.get_atomic_numbers())) blmin = closest_distances_generator(numbers, 0.5) t = push_apart(t, blmin, variable_cell=True) print('Starting relaxation', flush=True) clock = time() t.wrap() calc = DftbPlusCalculator(t, kpts=0.66 * kptdensity, use_spline=True, read_chg=True, maximum_angular_momenta={'C': 1}) try: t = relax_precon(t, calc, fmax=2e-1, smax=1e-2, variable_cell=True, optimizer='LBFGS', a_min=1e-4, cellbounds=cellbounds, logfile='opt_first.log', trajfile='opt_first.traj') except (IOError, TypeError, RuntimeError, UnboundLocalError) as err: # SCC or geometry optimization convergence problem print(err) del t.constraints t.wrap() calc = DftbPlusCalculator(t, kpts=kptdensity, use_spline=True, read_chg=True, maximum_angular_momenta={'C': 1}) try: t = relax_precon(t, calc, fmax=1e-1, smax=5e-3, variable_cell=True, optimizer='LBFGS', a_min=1e-4, cellbounds=cellbounds, logfile='opt.log', trajfile='opt.traj') except (IOError, TypeError, RuntimeError, UnboundLocalError) as err: # SCC or geometry optimization convergence problem print(err) try: t = read('opt.traj@-1') energy = t.get_potential_energy() forces = t.get_forces() stress = t.get_stress() except (FileNotFoundError, UnknownFileTypeError) as err: print(err) energy, forces, stress = (1e9, None, None) finalize(t, energy=energy, forces=forces, stress=stress) print('Relaxing took %.3f seconds.' % (time() - clock), flush=True) os.system('mv opt_first.traj prev_first.traj') os.system('mv opt_first.log prev_first.log') os.system('mv opt.log prev.log') os.system('mv opt.traj prev.traj') penalize(t) return t
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
positions=np.concatenate((tmp0, tmp1, tmp2)), cell=[[3.93588, 0.0, 0.0], [0.0, 15.74352, 0.0], [0.0, 0.0, 21.4528356]], pbc=[True, True, False], constraint=FixAtoms(indices=range(12))) c = slab.get_cell() v1 = np.array((c[0][0], 0., 0.)) v2 = np.array((0., c[1][1], 0.)) v3 = np.array((0., 0., 6.)) p0 = np.array((0., 0., 8.)) box = [p0, [v1, v2, v3]] stoichiometry = 5 * [22] + 10 * [8] print 'slab', buildFeature(slab, 6, [8, 22]) dmin = closest_distances_generator(atom_numbers=[22, 8], ratio_of_covalent_radii=0.6) sg = StartGenerator( slab=slab, # Generator to generate initial structures atom_numbers=stoichiometry, closest_allowed_distances=dmin, box_to_place_in=box) calc = MorsePotential() test = sg.get_new_candidate() test.set_calculator(calc) test.info['confid'] = 1 view(test) cd = ClusterDistanceMutation(slab, len(stoichiometry), dmin,