def rotate_atoms(individual, max_natoms=0.20): """Randomly rotates a number of random atoms within the individual (in place). Args: individual (Individual): an individual max_natoms (float or int): if float, the maximum number of atoms that will be rotated is max_natoms*len(individual) if int, the maximum number of atoms that will be rotated is max_natoms default: 0.20 """ if len(individual): if isinstance(max_natoms, float): max_natoms = int(len(individual)*max_natoms) max_natoms = max(max_natoms, 1) natoms_to_rotate = random.randint(1, max_natoms) atom_indices = list(range(len(individual))) random.shuffle(atom_indices) # Using random.shuffle on the indices guarantees no duplicates atom_indices = atom_indices[:natoms_to_rotate] # Extract out the atoms to be rotated atom_indices.sort(reverse=True) atoms = Atoms() for ind in atom_indices: atoms.append(individual.pop(ind)) axis = random.choice(['x', '-x', 'y', '-y', 'z', '-z']) angle = random.uniform(30, 180) atoms.rotate(axis, a=angle, center='COM', rotate_cell=False) individual.extend(atoms) return None
def bcc100(symbol, a, layers, L): """Build a bcc(100) surface symbol: chemical symbol ('H', 'Li', ...) a : lattice constant layers: number of layers L : height of unit cell""" a = float(a) # Distance between layers: z = a / 2 assert L > layers * z, 'Unit cell too small!' # Start with an empty Atoms object with an orthorhombic unit cell: atoms = Atoms(pbc=(True, True, False), cell=(a, a, L)) # Fill in the atoms: for n in range(layers): position = [a / 2 * (n % 2), a / 2 * (n % 2), n * z] atoms.append(Atom(symbol, position)) atoms.center(axis=2) return atoms
def rotate_cluster(individual, max_natoms=0.20): """Chooses a random number of atoms nearest to a random point in the cluster. These atoms are then rotated randomly around this point Parameters ---------- individual : Individual An individual object max_natoms : float The fraction of the total atoms to rotate """ if not len(individual): return None if isinstance(max_natoms, float): assert max_natoms <= 1 max_natoms = int(len(individual)*max_natoms) NNs = NeighborList(individual) CNs = [len(NN) for NN in NNs] # Get the surface atoms. These provide bounds for the moves # First get unit vectors and mags of all surface atoms positions = individual.get_positions() com = np.sum(positions.T, axis=1) / len(individual) surf_indices = [i for i, CN in enumerate(CNs) if CN < 11] surf_positions = np.array([positions[i] for i in surf_indices]) surf_magnitudes = np.linalg.norm(surf_positions - com, axis=1) surf_vectors = (surf_positions - com) / np.array([surf_magnitudes]).T # Weight probability of choosing a vector by its length from the center surf_probabilities = surf_magnitudes / sum(surf_magnitudes) # Choose a random point inside the particle and find nearest neighbors to that point i = np.random.choice(list(range(len(surf_vectors))), p=surf_probabilities) point = com + surf_vectors[i] * surf_magnitudes[i] * random.random() atom = Atom('Si', point) nearest_indices = individual.get_nearest_atom_indices(atom_index=atom.index, count=max_natoms) # Extract out the atoms to be rotated. Popping changes the indices # so pop them out highest to lowest indice nearest_indices = np.sort(nearest_indices)[::-1] atoms = Atoms() for ind in nearest_indices: atoms.append(individual.pop(ind)) axis = random.choice(['x', '-x', 'y', '-y', 'z', '-z']) angle = random.uniform(30, 180) atoms.rotate(axis, a=angle, center='COM', rotate_cell=False) individual.extend(atoms) return None
def get_defect_restart_indiv(Optimizer, indiv): """ Function to generate an structopt Individual class object containing a defect structure from a previously existing structure Inputs: Optimizer = structopt Optimizer class indiv = ASE Atoms object containing the previously existing structure Outputs: individ = structopt Individual class object containing defect structure data """ if not Optimizer.solidbulk: #Initialize Bulk - Generate or load positions of bulk solid try: rank = MPI.COMM_WORLD.Get_rank() except: rank = 0 outfilename = os.path.join(os.path.join(os.getcwd(),Optimizer.filename+'-rank'+repr(rank)),'Bulkfile.xyz') if Optimizer.evalsolid: bulk1, PureBulkEnpa, stro = gen_solid(Optimizer.solidfile, Optimizer.solidcell,outfilename,Optimizer.calc,Optimizer.calc_method) Optimizer.output.write(stro) else: bulk1 = gen_solid(Optimizer.solidfile,Optimizer.solidcell,outfilename) PureBulkEnpa = 0 natomsbulk = len(bulk1) Optimizer.solidbulk = bulk1.copy() Optimizer.summary.write('CIBS Run Pure Bulk Energy per Atom:'+ repr(PureBulkEnpa)+'\n') Optimizer.purebulkenpa = PureBulkEnpa Optimizer.natomsbulk = natomsbulk indiv.set_cell(Optimizer.solidcell) indiv.set_pbc(True) if Optimizer.restart_ints == 0: outt = find_defects(indiv,Optimizer.solidbulk,Optimizer.sf) else: indicop = [atm for atm in indiv if atm.symbol != 'X'] indiv = Atoms(cell=Optimizer.solidcell, pbc=True) for atm in indicop: indiv.append(atm) outt=[indiv[0:Optimizer.restart_ints],indiv[Optimizer.restart_ints::], Atoms(), Atoms(),'Assuming first '+repr(Optimizer.restart_ints)+' are interstitials\n'] indi = outt[0].copy() bulki = outt[1].copy() individ = Individual(indi) individ.bulko = bulki.copy() individ.bulki = bulki.copy() individ.purebulkenpa = Optimizer.purebulkenpa individ.natomsbulk = Optimizer.natomsbulk individ.vacancies = outt[2].copy() individ.swaps = outt[3].copy() Optimizer.output.write(outt[4]) return individ
def get_fingerprint(Optimizer, indiv, binsize, cutoffdist): """Function to calculate the fingerprint of a structure""" rs = numpy.linspace(0.0, cutoffdist, cutoffdist/binsize) indi = indiv[0] Vuc = indi.get_volume() if Optimizer.structure == 'Defect': solid = Atoms() solid.extend(indi) solid.extend(indiv.bulki) elif Optimizer.structure == 'Crystal': solid = indi.repeat([3, 3, 3]) else: solid = indi.copy() syms = sorted(list(set([atm.symbol for atm in solid]))) fingerprints = [] for i in range(len(syms)): for j in range(i, len(syms)): indl = [atm for atm in indi if atm.symbol == syms[i]] ind = Atoms() for one in indl: ind.append(one) soll = [atm for atm in solid if atm.symbol == syms[j]] sol = Atoms() for one in soll: sol.append(one) soli = [atm for atm in solid if atm.symbol == syms[i]] value = [] for R in rs: value2 = [] for k in range(len(ind)): value1 = [] for m in range(len(sol)): if k != m: rij = sol.get_distance(k, m, mic = True) if rij == 0: pass #pdb.set_trace() value1.append(dirac(R, a=rij, sig=0.02) * 1. / (4*math.pi * rij** 2*binsize * len(soli) * len(sol) / Vuc)) value2.append(sum(value1)) value.append(sum(value2)) fingerprints.append(value) fpt = [] for one in fingerprints: fpt.extend(one) return fpt
def read_xyz(fileobj,n=-1,data=False): if isinstance(fileobj, str): fileobj = open(fileobj,'r') lines = fileobj.readlines() fileobj.close() natmslist = [] atmslist = [] datalist = [] readnatoms = 0 while readnatoms<len(lines): natoms = int(lines[readnatoms]) natmslist.append(natoms) readnatoms += natoms+2 if n != 'All': if n < 0: n = len(natmslist)+n lines = lines[sum(natmslist[0:n])+2*n::] atm1 = Atoms() size = lines[1].split() if len(size) > 1: atm1.set_cell([size[1],size[2],size[3]]) for i in range(natmslist[n]): a = lines[i+2].split() sym = a[0] position = [float(a[1]),float(a[2]),float(a[3])] atm1.append(Atom(symbol=sym,position=position)) if data == True: return atm1,lines[1] else: return atm1 return atm1 elif n=='All': while len(lines)>0: natoms = int(lines[0]) datalist.append(lines[1]) atm1 = Atoms() i =- 1 for i in range(natoms): a = lines[i+2].split() sym = a[0] position = [float(a[1]),float(a[2]),float(a[3])] atm1.append(Atom(symbol=sym,position=position)) atmslist.append(atm1) lines = lines[i+3::] if data == True: return atmslist, datalist else: return atmslist
def update_structfile(ind, structfile, Optimizer): if Optimizer.structure == 'Defect' or Optimizer.structure == 'Surface': sols = Atoms() sols.extend(ind[0]) sols.extend(ind.bulki) elif Optimizer.structure == 'Crystal': sols = ind[0].repeat((3,3,3)) else: sols = ind[0].copy() positions = sols.get_positions() if Optimizer.vacancy_output: for one in ind.vacancies: sols.append(Atom(symbol='X',position=one.position)) #Optimizer.output.write('Number of positions = {0}\n'.format(len(positions))) write_xyz(structfile, sols, ind.energy) return positions
def generate_dumbbells(ndumbbells,dumbbellsym,nindiv,solid,symbol=None,size=None): """Function to generate initial dumbbells Inputs: ndumbbells = number of dumbbells to generate dumbbellsym = symbol of atom for dumbell nidiv = number of individuals to generate solid = ASE Atoms class object with initial bulk structure for dumbbells to reside symbol = dumbbell symbol matching size = restrict location of dumbbells Outputs: indivs = list of ASE atoms class objects with dumbbells *** Note: function is still in development. Has not been implemented *** """ if size==None: size = ndumbbells**0.333*3.0 bulk = solid.copy() bulkcom = bulk.get_center_of_mass() bulk.translate(-bulkcom) bulk.append(Atom(position=[0,0,0])) nr2 = Atoms(pbc=True, cell=bulk.get_cell()) for i in range(len(bulk)-1): dist = bulk.get_distance(-1,i) if dist <= size: nr2.append(bulk[i]) indivs = [] for one in range(nindiv): indices = [] if symbol == None: opts = [atm for atm in nr2] else: opts = [atm for atm in nr2 if atm.symbol==symbol] indiv = Atoms() if len(opts) <= ndumbbells: ndum = len(opts) flag = True else: ndum = ndumbbells flag = False for one in range(ndum): while True: atm = random.choice(opts) if atm.index not in indices: break datm = Atom(symbol=dumbbellsym, position=atm.position) indiv.append(datm) if flag==True: lack = ndumbbells-ndum for one in range(lack): indiv.append(Atom(symbol=dumbbellsym,position=random.choice(opts).position)) indiv.translate(bulkcom) indiv.rattle(stdev=0.5) indivs.append(indiv) return indivs
def populate_new_cell(self, unit_cell, new_cell, max_coeff): """ Fill up an orthorhombic cell wiht the atoms from a unit cell. Each atom is translated by a multiple of the old lattice vectors, and accepted atoms are added to the new object until the atom density matches that of the unit cell. """ super_cell = Atoms() super_cell.set_cell(new_cell) # setup storage for rejected atoms in case we need them rejects = Atoms() volume = unit_cell.get_volume() new_volume = super_cell.get_volume() atoms = int(round(float(len(unit_cell)) * new_volume / volume)) # quick check to see if the new cell will have too many atoms if (atoms > int(self.input.dict['max_atoms'])): raise Exception("too many atoms in supercell") vectors = np.asarray(unit_cell.cell) spiral = Hyperspiral(max_coeff) atom_positions = unit_cell.get_positions() # have to zero out infinitesimal values in atom_positions # =====Debug===== if self.input.dict['print_debug'] != "False": printx("old cell = " + str(unit_cell.cell)) printx("new cell = " + str(new_cell)) printx("max_coeff = " + str(max_coeff)) printx("atoms = " + str(atoms)) # move through the representations of the initial unit cell along a # spiral pattern on a grid of integer values. first the spiral on # a plane is completed and then the spiral is shifted down and then up # along the third coordinate. while True: shift = np.matmul(spiral.position, vectors) for i in range(len(unit_cell)): atom_prime = np.add(shift, atom_positions[i]) if self.in_new_cell(atom_prime, new_cell, 1e-7): new_atom = unit_cell[i] new_atom.position = atom_prime super_cell.append(new_atom) atoms -= 1 # satisfying this condition means success if atoms == 0: return super_cell else: new_atom = unit_cell[i] new_atom.position = atom_prime rejects.append(new_atom) # if we get to the end of the spirals then we check # the edges for barely rejected atoms to add in try: spiral.tick() except Exception as err: [printx(x) for x in err.args] if self.input.dict['print_debug'] != 'False': print_exc() try: super_cell = self.check_edges( rejects, new_cell, super_cell, atoms) except Exception as err: raise Exception(err.args[0]) return super_cell return super_cell
def rotct_defect(ind1, ind2, Optimizer): """Rotate atoms cut and splice Translates atoms to center of positions first Rotates atoms randomly around center of mass and cuts with xy plane Maintains number of atoms Maintains concentration of atoms Returns individuals to standard positions at end (un-rotates) """ if 'CX' in Optimizer.debug: debug = True else: debug = False Optimizer.output.write( 'Rotate Cut/Splice Cx for defects between individual ' + repr(ind1.index) + ' and individual ' + repr(ind2.index) + '\n') #Perserve starting conditions of individual indi1 = ind1[0].copy() indi2 = ind2[0].copy() #Translate individuals so COP is at (0,0,0) if Optimizer.structure == 'Defect': #Identify center of positions for defect structure indi1c, indi1b, vacant1, swap1, stro1 = find_defects( indi1, Optimizer.solidbulk, 0) com1 = position_average(indi1c) indi1 = shift_atoms(indi1, com1) trans = [-p for p in numpy.maximum.reduce(indi1.get_cell())] indi1.translate(trans) #Do the same for second individual indi2c, indi2b, vacant2, swap2, stro2 = find_defects( indi2, Optimizer.solidbulk, 0) com2 = position_average(indi2c) indi2 = shift_atoms(indi2, com2) indi2.translate(trans) else: com1 = indi1.get_center_of_mass() indi1.translate(-1 * com1) com2 = indi2.get_center_of_mass() indi2.translate(-1 * com2) #Select random axis, random angle, and random position and rotate individuals n = 0 while n < 10: rax = random.choice(['x', '-x', 'y', '-y', 'z', '-z']) rang = random.random() * 90 indi1.rotate(rax, a=rang, center=[0, 0, 0], rotate_cell=False) #Search for atoms in individual 1 that are above the xy plane group1 = Atoms(cell=ind1[0].get_cell(), pbc=ind1[0].get_pbc()) indices1 = [] for one in indi1: if one.position[2] >= 0: group1.append(one) indices1.append(one.index) if len(group1) > 2 and len(group1) < len(indi1): break else: n += 1 indi1.rotate(rax, a=-1 * rang, center=[0, 0, 0], rotate_cell=False) indi2.rotate(rax, a=rang, center=[0, 0, 0], rotate_cell=False) if debug: print 'Group1 size = ', len(group1) print 'Position = ', [0, 0, 0] print 'Angle = ', rang print 'Axis = ', rax print 'Number of tries = ', n + 1 if len(group1) != 0: #Apply concentration forcing if needed group2 = Atoms(cell=ind2[0].get_cell(), pbc=ind2[0].get_pbc()) indices2 = [] dellist = [] for one in indi2: if one.position[2] >= 0: group2.append(one) indices2.append(one.index) if Optimizer.forcing == 'Concentration': symlist = list(set(indi1.get_chemical_symbols())) seplist = [[atm for atm in group2 if atm.symbol == sym] for sym in symlist] group2n = Atoms(cell=group2.get_cell(), pbc=group2.get_pbc()) indices2n = [] dellist = [] for one in group1: sym1 = one.symbol listpos = [i for i, s in enumerate(symlist) if s == sym1][0] if len(seplist[listpos]) > 0: pos = random.choice(range(len(seplist[listpos]))) group2n.append(seplist[listpos][pos]) indices2n.append(indices2[seplist[listpos][pos].index]) del seplist[listpos][pos] else: dellist.append(one.index) if len(dellist) != 0: dellist.sort(reverse=True) for one in dellist: del group1[one] del indices1[one] indices2 = indices2n group2 = group2n.copy() else: dellist = [] while len(group2) < len(group1) - len(dellist): #Too many atoms in group 1 dellist.append(random.choice(group1).index) if len(dellist) != 0: dellist.sort(reverse=True) for one in dellist: del group1[one] del indices1[one] dellist = [] while len(group1) < len(group2) - len(dellist): #Too many atoms in group 2 dellist.append(random.choice(group2).index) if len(dellist) != 0: dellist.sort(reverse=True) for one in dellist: del group2[one] del indices2[one] other2 = Atoms(cell=ind2[0].get_cell(), pbc=ind2[0].get_pbc()) for one in indi2: if one.index not in indices2: other2.append(one) other1 = Atoms(cell=ind1[0].get_cell(), pbc=ind1[0].get_pbc()) for one in indi1: if one.index not in indices1: other1.append(one) indi1 = group2.copy() indi1.extend(other1) indi2 = group1.copy() indi2.extend(other2) #DEBUG: Write crossover to file if debug: write_xyz(Optimizer.debugfile, group1, 'group1') write_xyz(Optimizer.debugfile, other1, 'other1') write_xyz(Optimizer.debugfile, group2, 'group2') write_xyz(Optimizer.debugfile, other2, 'other2') print 'Length of group1 = ', len(group1), 'Length of group2', len( group2) #DEBUG: Check structure of atoms exchanged for sym, c, m, u in Optimizer.atomlist: nc = len([atm for atm in indi1 if atm.symbol == sym]) Optimizer.output.write('CX ROTCT_Defect: Individual 1 contains ' + repr(nc) + ' ' + repr(sym) + ' atoms\n') nc = len([atm for atm in indi2 if atm.symbol == sym]) Optimizer.output.write('CX ROTCT_Defect: Individual 2 contains ' + repr(nc) + ' ' + repr(sym) + ' atoms\n') if Optimizer.forcing != 'Concentration': for i in range(len(Optimizer.atomlist)): atms1 = [ inds for inds in indi1 if inds.symbol == Optimizer.atomlist[i][0] ] atms2 = [ inds for inds in indi2 if inds.symbol == Optimizer.atomlist[i][0] ] if len(atms1) == 0: if len(atms2) == 0: indi1[random.randint( 0, len(indi1) - 1)].symbol == Optimizer.atomlist[i][0] indi2[random.randint( 0, len(indi2) - 1)].symbol == Optimizer.atomlist[i][0] else: indi1.append(atms2[random.randint(0, len(atms2) - 1)]) indi1.pop(random.randint(0, len(indi1) - 2)) else: if len(atms2) == 0: indi2.append(atms1[random.randint(0, len(atms1) - 1)]) indi2.pop(random.randint(0, len(indi2) - 2)) indi1.rotate(rax, a=-1 * rang, center=[0, 0, 0], rotate_cell=False) indi2.rotate(rax, a=-1 * rang, center=[0, 0, 0], rotate_cell=False) if Optimizer.structure == 'Defect': trans = [-p for p in trans] indi1.translate(trans) indi2.translate(trans) indi1 = shift_atoms(indi1, [-p for p in com1]) indi2 = shift_atoms(indi2, [-p for p in com2]) else: indi1.translate(com1) indi2.translate(com2) #DEBUG: Check structure and number of atoms in crystal if Optimizer.structure == 'Defect': solid1 = Atoms() solid1.extend(indi1) solid1.extend(ind1.bulki) solid2 = Atoms() solid2.extend(indi2) solid2.extend(ind2.bulki) for sym, c, m, u in Optimizer.atomlist: nc = len([atm for atm in solid1 if atm.symbol == sym]) Optimizer.output.write( 'CX ROTCT_Defect: Defect 1 configuration contains ' + repr(nc) + ' ' + repr(sym) + ' atoms\n') nc = len([atm for atm in solid2 if atm.symbol == sym]) Optimizer.output.write( 'CX ROTCT_Defect: Defect 2 configuration contains ' + repr(nc) + ' ' + repr(sym) + ' atoms\n') if debug: Optimizer.output.flush() #pdb.set_trace() ind1[0] = indi1 ind2[0] = indi2 return ind1, ind2
def gen_pop_sphere(atomlist, size, crystal=False): """Function to generate a random structure of atoms within a sphere of given size. Inputs: atomlist = List of tuples with structure of atoms and quantity [('Sym1',int(concentration1), float(mass1),float(chempotential1)), ('Sym2',int(concentration2), float(mass2),float(chempotential2)),...] size = Float of length of side of cube within which to generate atoms crystal = False/List of crystal cell shape options list('cubic','orthorhombic','tetragonal','hexagonal','monoclinic','triclinic') cell shape will be adjusted accordingly Outputs: Returns individual of class Atoms (see ase manual for info on Atoms class) and if crystal list provided also outputs combined string with output information """ size = float(size) indiv = Atoms() indiv.set_cell([size, size, size]) # Get list of atom types for all atoms in cluster for s, c, m, u in atomlist: if c > 0: for i in range(c): r = random.random() * size / 2.0 d = [random.uniform(-1.0, 1.0) for j in range(3)] u = [d[j] / numpy.linalg.norm(d) for j in range(3)] pos = [r * u[j] + size / 2.0 for j in range(3)] at = Atom(symbol=s, position=pos) indiv.append(at) if crystal: stro = '' natoms = sum([c for s, c, m, u in atomlist]) pos = indiv.get_scaled_positions() structure = random.choice(crystal) cello = indiv.get_cell() if structure == 'cubic': #Set to cubic shape an, bn, cn = [numpy.linalg.norm(v) for v in cello] a = (an + bn + cn) / 3.0 celln = numpy.array([[a, 0, 0], [0, a, 0], [0, 0, a]]) stro += 'Setting cell to cubic\n' elif structure == 'orthorhombic': #Set to orthorhombic a = random.uniform(2, natoms**0.3333 * size) b = random.uniform(2, natoms**0.3333 * size) c = random.uniform(2, natoms**0.3333 * size) celln = numpy.array([[a, 0, 0], [0, b, 0], [0, 0, c]]) stro += 'Setting cell to orthorhombic\n' elif structure == 'tetragonal': #Set to tetragonal shape an, bn, cn = [numpy.linalg.norm(v) for v in cello] a = (an + bn) / 2.0 c = cn if c == a: c = random.uniform(1, natoms**0.3333 * size) celln = numpy.array([[a, 0, 0], [0, a, 0], [0, 0, c]]) stro += 'Setting cell to tetragonal\n' elif structure == 'hexagonal': #Set to hexagonal shape an, bn, cn = [numpy.linalg.norm(v) for v in cello] a = (an + bn) / 2.0 c = cn if c <= a: c = random.uniform(a + 1, natoms**0.3333 * size) trans = numpy.array([[1, 0, 0], [-0.5, (3.0**0.5) / 2.0, 0], [0, 0, 1]]) trans[0] = [a * i for i in trans[0]] trans[1] = [a * i for i in trans[1]] trans[2] = [c * i for i in trans[2]] celln = trans stro += 'Setting cell to Hexagonal\n' elif structure == 'monoclinic': #Set to monoclinic a, b, c = [numpy.linalg.norm(v) for v in cello] if a == b: b = random.uniform(1, natoms**0.3333 * size) trans = numpy.array([(1 + random.random()) * c, 0, (1 + random.random()) * c]) celln = numpy.array([[a, 0, 0], [0, b, 0], [0, 0, 0]]) celln[2] = trans stro += 'Setting cell to monoclinic\n' elif structure == 'triclinic': #Set to triclinic a, b, c = [numpy.linalg.norm(v) for v in cello] celln = numpy.array([[a, 0, 0], [(1 + random.random()) * b, (1 + random.random()) * b, 0], [(1 + random.random()) * c, 0, (1 + random.random()) * c]]) stro += 'Setting cell to triclinic\n' indiv.set_cell(celln) indiv.set_scaled_positions(pos) stro += repr(indiv.get_cell()) + '\n' return indiv, stro return indiv
def get_defect_indiv(Optimizer): """ Function to generate a structopt Individual class structure with a defect structure. Inputs: Optimizer = structopt Optimizer class object Outputs: individ = structopt Individual class object containing defect structure data """ if not Optimizer.solidbulk: #Initialize Bulk - Generate or load positions of bulk solid try: rank = MPI.COMM_WORLD.Get_rank() except: rank = 0 outfilename = os.path.join( os.path.join(os.getcwd(), Optimizer.filename + '-rank' + repr(rank)), 'Bulkfile.xyz') if Optimizer.evalsolid: if Optimizer.parallel: from MAST.structopt.tools.setup_calculator import setup_calculator Optimizer.calc = setup_calculator(Optimizer) bulk1, PureBulkEnpa, stro = gen_solid(Optimizer.solidfile, Optimizer.solidcell, outfilename, Optimizer.calc, Optimizer.calc_method) Optimizer.output.write(stro) else: bulk1 = gen_solid(Optimizer.solidfile, Optimizer.solidcell, outfilename) PureBulkEnpa = 0 natomsbulk = len(bulk1) Optimizer.solidbulk = bulk1.copy() Optimizer.purebulkenpa = PureBulkEnpa Optimizer.natomsbulk = natomsbulk # Identify nearby atoms for region 2 inclusion bulk = Optimizer.solidbulk.copy() if not Optimizer.random_loc_start: bulkcom = bulk.get_center_of_mass() else: cellsize = numpy.maximum.reduce(bulk.get_cell()) dsize = max([Optimizer.size, Optimizer.sf]) bulkcom = numpy.array( [random.uniform(dsize, cellsize[one] - dsize) for one in range(3)]) bulk.translate(-bulkcom) if Optimizer.sf != 0: bulk.append(Atom(position=[0, 0, 0])) nbulk = Atoms(pbc=True, cell=bulk.get_cell()) nr2 = Atoms(pbc=True, cell=bulk.get_cell()) for i in range(len(bulk) - 1): dist = bulk.get_distance(-1, i) if dist <= Optimizer.sf: nr2.append(bulk[i]) else: nbulk.append(bulk[i]) else: nbulk = bulk.copy() nr2 = Atoms(pbc=True, cell=bulk.get_cell()) # Generate random individual if 'sphere' in Optimizer.generate_flag: ind = gen_pop_sphere(Optimizer.atomlist, Optimizer.size) elif 'dumbbell' in Optimizer.generate_flag: ind = Atoms(cell=[Optimizer.size for i in range(3)], pbc=True) for sym, c, m, u in Optimizer.atomlist: if c > 0: dums = generate_dumbbells(c, dumbbellsym=sym, nindiv=1, solid=Optimizer.solidbulk, size=Optimizer.size)[0] ind.extend(dums) ## HKK: adding plate elif 'plate' in Optimizer.generate_flag: #print 'HKK:: " plate" as Generate_flag' #print type(gen_pop_plate) #print gen_pop_plate.__file__ ind = gen_pop_plate(Optimizer.atomlist, Optimizer.size) ## HKK: adding plate else: #print 'HKK:: "box" as Generte flag' ind = gen_pop_box(Optimizer.atomlist, Optimizer.size) if 'random' in Optimizer.generate_flag: #Update atom list with atoms in region 2 natlist = [] for sym, c, m, u in Optimizer.atomlist: atsym = [atm for atm in nr2 if atm.symbol == sym] natlist.append((sym, len(atsym), m, u)) nnr2 = gen_pop_sphere(natlist, Optimizer.sf * 2.0) nnr2.translate([-Optimizer.sf, -Optimizer.sf, -Optimizer.sf]) nnr2.set_pbc(True) nnr2.set_cell(bulk.get_cell()) nr2 = nnr2.copy() # Initialize class individual with known values individ = Individual(ind) individ.purebulkenpa = Optimizer.purebulkenpa individ.natomsbulk = Optimizer.natomsbulk # Combine individual with R2 if Optimizer.natoms > 1: ## ZS: We will not recenter the atoms if natoms=1 icom = ind.get_center_of_mass() ind.translate(-icom) else: pass ind.extend(nr2) ind.set_pbc(True) ind.set_cell(bulk.get_cell()) # Recenter structure nbulk.translate(bulkcom) ind.translate(bulkcom) individ[0] = ind.copy() individ.bulki = nbulk.copy() individ.bulko = nbulk.copy() bulk = nbulk.copy() bul = bulk.copy() for atm in individ[0]: bul.append(atm) indices = [] for sym, c, m, u in Optimizer.atomlist: if c < 0: if Optimizer.random_vac_start: alist = [one for one in bul if one.symbol == sym] count = abs(c) while count > 0: indices.append(random.choice(alist).index) count -= 1 else: pos = individ[0][0:Optimizer.natoms].get_center_of_mass() count = abs(c) bul.append(Atom(position=pos)) alist = [one for one in bul if one.symbol == sym] alistd = [(bul.get_distance(len(bul) - 1, one.index), one.index) for one in alist] alistd.sort(reverse=True) bul.pop() while count > 0: idx = alistd.pop()[1] indices.append(idx) count -= 1 if len(indices) != 0: nbulklist = [ at for at in bul if at.index not in indices and at.index < len(bulk) ] nalist = [ at for at in bul if at.index not in indices and at.index >= len(bulk) ] bulkn = Atoms(cell=bulk.get_cell(), pbc=True) for atm in nbulklist: bulkn.append(atm) individ.bulki = bulkn.copy() individ.bulko = bulkn.copy() newind = Atoms() for atm in nalist: newind.append(atm) newind.set_cell(individ[0].get_cell()) newind.set_pbc(True) individ[0] = newind return individ
def getUnitCell(self): sideLen=self.sideLen edge=Atoms() nAtom=2*sideLen+1 for i in range(nAtom): if i%2==0: label='C' y=0.5 else: label='N' if len(self.elements)==1:label='C' y=1.0 x=(-sideLen+i)*0.5*sqrt(3) y-=(sideLen+1)*1.5 atom=Atom(label,(x,y,0.0)) edge.append(atom) unitCell=Atoms() for i in range(6): newEdge=edge.copy() newEdge.rotate('z',i*2*pi/6.0) unitCell.extend(newEdge) #get cell dist=(self.sideLen+1)*3.0 #the distance between 2 hole center if self.cubic: newAtoms=unitCell.copy() newAtoms.translate([dist*sqrt(3)/2,dist/2.0,0]) unitCell.extend(newAtoms) unitCell.set_cell([dist*sqrt(3),dist,10.0]) else: cell=np.diag([dist*sqrt(3)/2,dist,10]) cell[0,1]=dist/2.0 unitCell.set_cell(cell) return unitCell
def initialize_subsystem(self, info): """Initializes a SubsystemInternal from the given Subsystem. Parameters: info: SubSystem """ name = info.name real_indices = self.generate_subsystem_indices(name) # Create a copy of the subsystem temp_atoms = Atoms() index_map = {} reverse_index_map = {} counter = 0 for index in real_indices: atom = self.atoms[index] temp_atoms.append(atom) index_map[index] = counter reverse_index_map[counter] = index counter += 1 atoms = temp_atoms.copy() atoms.set_pbc(self.atoms.get_pbc()) atoms.set_cell(self.atoms.get_cell()) # Create the SubSystem subsystem = SubSystemInternal( atoms, info, index_map, reverse_index_map, len(self.atoms)) self.subsystems[name] = subsystem
def set_src(self, filename): a = data(filename) bond = False if "bonds" in a.headers: bond = True if bond: a.map(1, 'id', 2, 'type', 3, 'mtype', 4, 'x', 5, 'y', 6, 'z') else: a.map(1, 'id', 2, 'type', 3, 'x', 4, 'y', 5, 'z') ats = a.viz(0)[2] # print ats atoms = Atoms() for at in ats: id, type, x, y, z = at if len(self.types) == 0: atoms.append(Atom(type, position=[x, y, z])) else: atoms.append(Atom(self.types[type - 1], position=[x, y, z])) cell = np.zeros([3, 3]) xlo, xhi = a.headers["xlo xhi"] ylo, yhi = a.headers["ylo yhi"] zlo, zhi = a.headers["zlo zhi"] cell[0, 0] = xhi - xlo cell[1, 1] = yhi - ylo cell[2, 2] = zhi - zlo if "xy xz yz" in a.headers: xy, xz, yz = a.headers["xy xz yz"] cell[1, 0], cell[2, 0], cell[2, 1] = xy, xz, yz atoms.set_cell(cell) self.atoms = atoms return atoms
def prototype_tilt(self, latx, laty): unit = Atoms() unit.append(Atom('C', [1.0 / 2, 0, 0])) unit.append(Atom('C', [0, sqrt(3) / 2, 0])) unit.set_cell((3.0 / 2, sqrt(3), 10.0)) unit.cell[0, 1] = sqrt(3) / 2 col = unit.repeat((latx, laty, 1)) return col
def zhexian(self,n,p,type): m=n*2+1; atoms=Atoms() for i in range(m): y=i*sqrt(3)/2 x=0.5*(1+(type*2-1)*(i%2)-type)+p*1.5 atoms.append(Atom('C',(x,y,0.0))) return atoms
def atomsfromlist(atomslist): """Takes in a list of atomic symbols and coordinates, as in [atom1, atom2, ...] where atomX = (symbol, (x,y,z)), and symbol is the atomic symbol (e.g. "Na") and x,y,z is the position, in Angstroms, of the atom. Returns an ASE atoms object.""" atoms = Atoms() for atom in atomslist: atoms.append(Atom(atom[0], atom[1])) return atoms
def mutate(self, atoms): """ Does the actual mutation. """ tbm = random.choice(range(len(atoms))) indi = Atoms() for a in atoms: if a.index == tbm: a.position += self.random_vector(self.length) indi.append(a) return indi
def read_xyz(fileobj,n=-1,data=False): """ Function to read multi-atom xyz file with data Inputs: fileobj = String containing file name or file object n = Integer indicating number for structure from xyz file to read. Default is last structure in file. Or String=='All' which will output all structures in file data = True/False boolean indicating whether or not to output any data strings read from xyz file Outputs: atmslist = ASE Atoms class object containing structure from file or list of Atoms objects if n=='All' datalist(Optional) = String of data read from xyz file or list of data strings read from xyz file """ try: from pymatgen.io.vaspio import Poscar mystructure = Poscar.from_file(fileobj).structure from pymatgen.io.aseio import AseAtomsAdaptor myatoms = AseAtomsAdaptor.get_atoms(mystructure) return myatoms except: if isinstance(fileobj, str): fileobj = open(fileobj,'r') lines = fileobj.readlines() atmslist = [] datalist = [] while len(lines)>0: natoms = int(lines[0]) datalist.append(lines[1]) atm1 = Atoms() i =- 1 for i in range(natoms): a = lines[i+2].split() sym = a[0] position = [float(a[1]),float(a[2]),float(a[3])] atm1.append(Atom(symbol=sym,position=position)) atmslist.append(atm1) lines = lines[i+3::] if n == 'All': if data == True: return atmslist, datalist else: return atmslist else: if data == True: return atmslist[n],datalist[n] else: return atmslist[n]
def find_top_layer(surf,topthick): """Development function for identifying viable surface atoms. *** needs development *** """ top=Atoms() bulk=Atoms() zs=[z for x,y,z in surf.get_positions()] thick=max(zs)-min(zs) for one in surf: x,y,z=one.position if z>=max(zs)-topthick: top.append(one) else: bulk.append(one) return top, bulk
def unitcell(self,latx,laty): unit=Atoms('C2N',[(1/2*sqrt(3),0.5,0.0), (1/2*sqrt(3),-0.5,0), (1/2*sqrt(3),1.5,0)]) atoms=Atoms() for i in range(3): a=unit.copy() a.rotate('z',pi*2/3*i) atoms.extend(a) if self.centerN: atoms.append(Atom('N',(0,0,0))) atoms.set_cell([6*sqrt(3)/2,6,10.0]) col=unit.repeat((latx,laty,1)) return col
def get_unique_atoms(atoms, mic=True): n = len(atoms) u = atoms.copy() if (mic): u.set_pbc([True] * 3) dis = u.get_all_distances(mic=mic) clusters = get_clusters(n, dis) newatoms = Atoms() for i in range(n): if clusters[i]: newatoms.append(atoms[i]) cell = atoms.get_cell() pbc = atoms.get_pbc() newatoms.set_pbc(pbc) newatoms.set_cell(cell) # newatoms.center() return newatoms
def rattle(indiv): """Function to slightly alter atoms in structure. Intended for use in defect function. """ atms,indb,vacant,swap,stro = find_defects(indiv[0],self.solidbulk,0.0) atmsl,indbl,vacantl,swapl,strol = find_defects(indiv[0],self.solidbulk,2.0) atmslist = [] for atm1 in atmsl: for atm2 in atms: if atm1.symbol==atm2.symbol: if atm1.position[0]==atm2.position[0] and atm1.position[1]==atm2.position[1] and atm1.position[2]==atm2.position[2]: atmslist.append(atm1.index) atmolist=[atm for atm in atmsl if atm.index not in atmslist] rat=Atoms(cell=atms.get_cell(), pbc=atms.get_pbc) for one in atmolist: rat.append(one) rat.rattle(stdev=0.2) ind=atms.copy() ind.extend(rat) ind.extend(indbl) return ind
def rotate_cluster(individual, max_natoms=0.20): """Randomly rotates a random cluster of atoms within the individual (in place). Args: individual (Individual): an individual max_natoms (float or int): if float, the maximum number of atoms that will be rotated is max_natoms*len(individual) if int, the maximum number of atoms that will be rotated is max_natoms default: 0.20 """ if len(individual): cell_max = np.maximum.reduce(individual.get_cell()) cell_min = np.minimum.reduce(individual.get_cell()) if isinstance(max_natoms, float): max_natoms = int(len(individual)*max_natoms) max_natoms = max(max_natoms, 1) natoms_to_rotate = random.randint(1, max_natoms) point = (random.uniform(cell_min, cell_max), random.uniform(cell_min, cell_max), random.uniform(cell_min, cell_max)) atom = Atom('Si', point) nearest_indices = individual.get_nearest_atom_indices(atom_index=atom.index, count=natoms_to_rotate) # Extract out the atoms to be rotated. Popping changes the indices # so pop them out highest to lowest indice nearest_indices = np.sort(nearest_indices)[::-1] atoms = Atoms() for ind in nearest_indices: atoms.append(individual.pop(ind)) axis = random.choice(['x', '-x', 'y', '-y', 'z', '-z']) angle = random.uniform(30, 180) atoms.rotate(axis, a=angle, center='COM', rotate_cell=False) individual.extend(atoms) return None
def prototype_tilt(self,latx,laty): unit=Atoms() b=3.134/self.bond/2 unit.append(Atom('Mo',[1.0/2,0,0])) unit.append(Atom('S',[0,sqrt(3)/2,b])) unit.append(Atom('S',[0,sqrt(3)/2,-b])) unit.set_cell((3.0/2,sqrt(3),10.0)) unit.cell[0,1]=sqrt(3)/2 col=unit.repeat((latx,laty,1)) return col
def read_lammps_data(filename, atomlist=False, ratomlist=False, writefile=False): """Function to read a Lammps data file and output an xyz and atoms object Input: Filename = string for data file to read atomlist = List of strings corresponding to Lammps atom type ['Cr','Fe','He',...] ratomlist = True/False - will return atomlist with structure writefile = True/False - will output an xyz file of the Lammps data file """ f=open(filename,'r') for i in range(4): ln=f.readline() box = [] for i in range(3): ln=f.readline().split() box.append(float(ln[1])) for i in range(4): ln=f.readline() a=Atoms(cell=box) if atomlist: atlist=[pair for pair in enumerate(atomlist)] for line in f.readlines(): sp=line.split() for i,sym in atlist: if sp[1]==str(i+1): at=Atom(symbol=sym,position=[float(sp[2]),float(sp[3]),float(sp[4])]) a.append(at) else: atnum = [] attype = [] for line in f.readlines(): sp=line.split() if sp[1] in atnum: sym = attype[[i for i,num in enumerate(atnum) if num==sp[1]][0]] at=Atom(symbol=sym,position=[float(sp[2]),float(sp[3]),float(sp[4])]) a.append(at) else: symn = random.choice(range(1,100)) at=Atom(symbol=symn,position=[float(sp[2]),float(sp[3]),float(sp[4])]) a.append(at) atnum.append(sp[1]) attype.append(at.symbol) atomlist=list(np.zeros(len(attype))) for i in range(len(atnum)): atomlist[int(atnum[i])-1]=attype[i] f.close() if writefile==True: write_xyz(filename+'.xyz',a,'xyz') if ratomlist: return a, atomlist else: return a
def fss_bcc(indiv, Optimizer): defected = indiv[0].copy() defected.extend(indiv.bulki) #Identify nearest neighbor cutoff distance nndists = [] for i in range(5): one = random.choice(defected) distances = [defected.get_distance(one.index, j) for j in range(len(defected)) if j != one.index] distances.sort() nndists.extend(distances[0:3]) nndist = sum(nndists)/len(nndists) cutoff = nndist*0.6 #Create nearest neighbor list from cutoff distance ctflist = [cutoff for one in defected] nl = NeighborList(ctflist, bothways=True, self_interaction=False) nl.update(defected) #Identify most common number of nearest neighbors for each atom nneigh = [] for one in defected: indices, offsets = nl.get_neighbors(one.index) nneigh.append(len(indices)) avn = mode(nneigh) #Identify those atoms that have a different number of nearest neighbors defs = [i for i in range(len(nneigh)) if nneigh[i] != avn[0][0]] #Create new structure from translated defected atoms defsat = Atoms(pbc = defected.get_pbc(), cell=defected.get_cell()) for i in defs: defsat.append(defected[i]) #Identify center of mass of defected group and translate to center of cell cop = position_average(defsat) ndefsat = shift_atoms(defsat, cop) ndefected = shift_atoms(defected, cop) #Identify bounds of defected portion of structure maxpos = max(numpy.maximum.reduce(ndefsat.positions)) minpos = min(numpy.minimum.reduce(ndefsat.positions)) #Identify size of structure that will encompass defected structure osc = copy.deepcopy(Optimizer.supercell) latcont = numpy.maximum.reduce(ndefsat.get_cell())[0]/osc[0] deltapos = abs(maxpos-minpos) newsupercell = round(deltapos/latcont)+1 bxlen = newsupercell*latcont #Identify those atoms within a box of encompassing length centered at the center of the cell tol = .1 cell = numpy.maximum.reduce(ndefsat.get_cell()) boxlow = [cell[i]/2.0-bxlen/2.0-tol for i in range(3)] boxhigh = [cell[i]/2.0+bxlen/2.0+tol for i in range(3)] atlist = [] otherlist = [] for one in ndefected: pos = one.position if boxlow[0] < pos[0] < boxhigh[0]: if boxlow[1] < pos[1] < boxhigh[1]: if boxlow[2] < pos[2] < boxhigh[2]: atlist.append(one) else: otherlist.append(one) else: otherlist.append(one) else: otherlist.append(one) ncell = [bxlen,bxlen,bxlen] #Create a new atoms object from the atoms within the box ssats = Atoms(pbc = True, cell=ncell) for one in atlist: ssats.append(one) #Attach a calculator for the atoms ssats.set_calculator(Optimizer.calc) #Calculate the energy out = REE(ssats)
def get_new_individual(self, parents): f, m = parents if self.fix_coverage: # Count number of adsorbates adsorbates_in_parents = len(self.get_all_adsorbate_indices(f)) indi = self.initialize_individual(f) indi.info['data']['parents'] = [i.info['confid'] for i in parents] fna = self.get_atoms_without_adsorbates(f) mna = self.get_atoms_without_adsorbates(m) fna_geo_mid = np.average(fna.get_positions(), 0) mna_geo_mid = np.average(mna.get_positions(), 0) if self.rvecs is not None: if not isinstance(self.rvecs, list): print('rotation vectors are not a list, skipping rotation') else: vec = random.choice(self.rvecs) try: angle = random.choice(self.rangs) except TypeError: angle = self.rangs f.rotate(vec, angle, center=fna_geo_mid) vec = random.choice(self.rvecs) try: angle = random.choice(self.rangs) except TypeError: angle = self.rangs m.rotate(vec, angle, center=mna_geo_mid) theta = random.random() * 2 * np.pi # 0,2pi phi = random.random() * np.pi # 0,pi e = np.array((np.sin(phi) * np.cos(theta), np.sin(theta) * np.sin(phi), np.cos(phi))) eps = 0.0001 # Move each particle to origo with their respective geometrical # centers, without adsorbates common_mid = (fna_geo_mid + mna_geo_mid) / 2. f.translate(-common_mid) m.translate(-common_mid) off = 1 while off != 0: fna = self.get_atoms_without_adsorbates(f) mna = self.get_atoms_without_adsorbates(m) # Get the signed distance to the cutting plane # We want one side from f and the other side from m fmap = [np.dot(x, e) for x in fna.get_positions()] mmap = [-np.dot(x, e) for x in mna.get_positions()] ain = sorted([i for i in chain(fmap, mmap) if i > 0], reverse=True) aout = sorted([i for i in chain(fmap, mmap) if i < 0], reverse=True) off = len(ain) - len(fna) # Translating f and m to get the correct number of atoms # in the offspring if off < 0: # too few # move f and m away from the plane dist = abs(aout[abs(off) - 1]) + eps f.translate(e * dist) m.translate(-e * dist) elif off > 0: # too many # move f and m towards the plane dist = abs(ain[-abs(off)]) + eps f.translate(-e * dist) m.translate(e * dist) eps /= 5. fna = self.get_atoms_without_adsorbates(f) mna = self.get_atoms_without_adsorbates(m) # Determine the contributing parts from f and m tmpf, tmpm = Atoms(), Atoms() for atom in fna: if np.dot(atom.position, e) > 0: atom.tag = 1 tmpf.append(atom) for atom in mna: if np.dot(atom.position, e) < 0: atom.tag = 2 tmpm.append(atom) # Place adsorbates from f and m in tmpf and tmpm f_ads = self.get_all_adsorbate_indices(f) m_ads = self.get_all_adsorbate_indices(m) for ads in f_ads: if np.dot(f[ads[0]].position, e) > 0: for i in ads: f[i].tag = 1 tmpf.append(f[i]) for ads in m_ads: pos = m[ads[0]].position if np.dot(pos, e) < 0: # If the adsorbate will sit too close to another adsorbate # (below self.min_adsorbate_distance) do not add it. dists = [ np.linalg.norm(pos - a.position) for a in tmpf if a.tag == 1 ] for d in dists: if d < self.min_adsorbate_distance: break else: for i in ads: m[i].tag = 2 tmpm.append(m[i]) tmpfna = self.get_atoms_without_adsorbates(tmpf) tmpmna = self.get_atoms_without_adsorbates(tmpm) # Check that the correct composition is employed if self.keep_composition: opt_sm = sorted(fna.numbers) tmpf_numbers = list(tmpfna.numbers) tmpm_numbers = list(tmpmna.numbers) cur_sm = sorted(tmpf_numbers + tmpm_numbers) # correct_by: dictionary that specifies how many # of the atom_numbers should be removed (a negative number) # or added (a positive number) correct_by = dict([(j, opt_sm.count(j)) for j in set(opt_sm)]) for n in cur_sm: correct_by[n] -= 1 correct_in = random.choice([tmpf, tmpm]) to_add, to_rem = [], [] for num, amount in correct_by.items(): if amount > 0: to_add.extend([num] * amount) elif amount < 0: to_rem.extend([num] * abs(amount)) for add, rem in zip(to_add, to_rem): tbc = [a.index for a in correct_in if a.number == rem] if len(tbc) == 0: pass ai = random.choice(tbc) correct_in[ai].number = add # Move the contributing apart if any distance is below blmin maxl = 0. for sv, min_dist in self.get_vectors_below_min_dist(tmpf + tmpm): lsv = np.linalg.norm(sv) # length of shortest vector d = [-np.dot(e, sv)] * 2 d[0] += np.sqrt(np.dot(e, sv)**2 - lsv**2 + min_dist**2) d[1] -= np.sqrt(np.dot(e, sv)**2 - lsv**2 + min_dist**2) l = sorted([abs(i) for i in d])[0] / 2. + eps if l > maxl: maxl = l tmpf.translate(e * maxl) tmpm.translate(-e * maxl) # Translate particles halves back to the center tmpf.translate(common_mid) tmpm.translate(common_mid) # Put the two parts together for atom in chain(tmpf, tmpm): indi.append(atom) if self.fix_coverage: # Remove or add adsorbates as needed adsorbates_in_child = self.get_all_adsorbate_indices(indi) diff = len(adsorbates_in_child) - adsorbates_in_parents if diff < 0: # Add adsorbates for _ in range(abs(diff)): self.add_adsorbate(indi, self.adsorption_sites, self.min_adsorbate_distance) elif diff > 0: # Remove adsorbates tbr = random.sample(adsorbates_in_child, diff) # to be removed for adsorbate_indices in sorted(tbr, reverse=True): for i in adsorbate_indices[::-1]: indi.pop(i) return (self.finalize_individual(indi), self.descriptor + ': {0} {1}'.format(f.info['confid'], m.info['confid']))
os.remove('1.nc') co[0].number = 6 co.pbc = True t.write(co) co.pbc = False o = co.pop(1) try: t.write(co) except ValueError: pass else: assert False co.append(o) co.pbc = True t.write(co) del t # append to a nonexisting file fname = '2.nc' if os.path.isfile(fname): os.remove(fname) t = NetCDFTrajectory(fname, 'a', co) del t fname = '3.nc' t = NetCDFTrajectory(fname, 'w', co) # File is not created before first write co.set_pbc([True, False, False])
def add_monomer(self, monomer, sites, plane_atom=None): """Add a monomer (e.g. CH3-S-Au-S-CH3) to the anchored sites. Parameters ---------- monomer : Instance See class monomer, which defines the generated monomer. sites : list A list of two integers indicating the anchored surface atoms. This defines x-axis vector on the anchored surface. plane_atom : int, optional (default=None) Specifies the index of the neighboring atom used to determine the anchored surface. Together with 'sites', this defines the xy plane, and we will generate a vector perpendicular to this plane, that will then define the xz plane. If None, we use the center of mass as a reference to define the xz plane. Thereafter, we will generate a vector perpendicular to this xz plane, which will define the xy plane. Notes ----- Update the self.avail to mark the occupied sites, if the ligand is created. """ # Check whether the provided monomer class is valid if not hasattr(monomer, 'SR'): raise AttributeError("monomer instance does not have 'SR'.") if not hasattr(monomer, 'delta'): raise AttributeError("monomer instance does not have 'delta'.") if not hasattr(monomer, 'std'): raise AttributeError("monomer instance does not have 'std'.") if not hasattr(monomer, 'symbol'): raise AttributeError("monomer instance does not have 'symbol'.") if not hasattr(monomer, 'side_group'): raise AttributeError( "monomer instance does not have 'side_group'.") if not hasattr(monomer, 'over_r'): raise AttributeError("monomer instance does not have 'over_r'.") assert isinstance(sites[0], int), 'sites[0] must be an integer' assert isinstance(sites[1], int), 'sites[1] must be an integer' assert len(sites) == 2, 'sites must be a list of two integers' # Warn the users if the provided sites have been occupied if sites[0] not in self.avail or sites[1] not in self.avail: message = """The provided sites %s, %s have been occupied! Are you sure that do you like to continue?""" % (sites[0], sites[1]) input(message) ligand = Atoms() # Create the random value (Norm dist.) for SR noise_SR = stats.norm(loc=monomer.SR, scale=monomer.std[0]) # Attach S atom pos = tuple(np.array([0, 0, noise_SR.rvs()])) ligand.append(Atom(monomer.symbol[0], position=pos)) # Distance between the two sites diff = self[sites[1]].position - self[sites[0]].position disp = np.sqrt(np.vdot(diff, diff)) # Attach second S atom pos = tuple(np.array([disp, 0, noise_SR.rvs()])) ligand.append(Atom(monomer.symbol[0], position=pos)) # Attach Au atom, mid point between the S atoms pos = tuple(0.5 * (ligand[0].position + ligand[1].position)) ligand.append(Atom(monomer.symbol[1], position=pos)) # If there is a defined side_group, such as methyl, append it if monomer.side_group: num = monomer.side_group.get_number_of_atoms() # Decide the ligand orientation, disturb the symmetry if np.random.rand() >= 0.5: orientation_1st = monomer.side_group.positions else: # Refletion through xz plane orientation_1st = reflect(monomer.side_group.positions, symmetry='xz') orientation_2nd = rotate_vec(theta=math.pi, vec_axis=(0, 0, 1), vec=orientation_1st.T) orientation_2nd = orientation_2nd.T # Attach side_group to the first S atom pos = orientation_1st + ligand[0].position ligand.extend(monomer.side_group) # Update the side_group's positions for (i, x) in enumerate(ligand[-num:]): ligand[-num + i].position = pos[i] # Attach side_group to the second S atom pos = orientation_2nd + ligand[1].position ligand.extend(monomer.side_group) for (i, x) in enumerate(ligand[-num:]): ligand[-num + i].position = pos[i] # Create a temporary copy of ligand, used to generate a ligand # with the opposite orientaiton if the initial orientation fails # This is relevant only if user suggests a side group ligand2 = copy.deepcopy(ligand) pos = reflect(ligand2.positions, symmetry='xz') ligand2.set_positions(pos) # Create a random value (Norm dist.) for noise_delta noise_delta = stats.norm(loc=monomer.delta, scale=monomer.std[1]) deg_delta = noise_delta.rvs() # Create the sign which guides how the ligands to be displaced side way. if np.random.rand() >= 0.5: sign = 1 else: sign = -1 # Rotate the ligand group with respect to xz plane, at noise_delta deg ligand.rotate(v=(0, 0, 1), a=sign * deg_delta) # Add the ligand success, _ = self.add_ligand(ligand, sites=sites, over_r=monomer.over_r, plane_atom=plane_atom, debug=monomer.debug) if success: self.avail.remove(sites[0]) self.avail.remove(sites[1]) # Repeat the effort of creating the ligand with the opposite # orientation defined by ligand2 if ligand2 and success == False: # Rotate the whole side groups with respect to xz plane ligand2.rotate(v=(0, 0, 1), a=sign * deg_delta) # Add the ligand success, _ = self.add_ligand(ligand2, sites=sites, over_r=monomer.over_r, plane_atom=plane_atom, debug=monomer.debug) if success: self.avail.remove(sites[0]) self.avail.remove(sites[1]) return success
def lattice_alteration_group(indiv, Optimizer): """Move function to perform Lattice Alteration of group of atoms based on location Inputs: indiv = Individual class object to be altered Optimizer = Optimizer class object with needed parameters Outputs: indiv = Altered Individual class object """ if 'MU' in Optimizer.debug: debug = True else: debug = False if Optimizer.structure == 'Defect': if Optimizer.isolate_mutation: indc, indb, vacant, swap, stro = find_defects( indiv[0], Optimizer.solidbulk, 0) atms = indc.copy() else: atms = indiv[0].copy() else: atms = indiv[0].copy() if len(atms) != 0: try: natomsmove = random.randint(1, len(atms) / 5) except ValueError: natomsmove = 1 #Select random position in cluster cellx = numpy.maximum.reduce(atms.get_positions()) cellm = numpy.minimum.reduce(atms.get_positions()) pt = [ random.uniform(cellm[0], cellx[0]), random.uniform(cellm[1], cellx[1]), random.uniform(cellm[2], cellx[2]) ] #Get distance of atoms from random point atpt = Atom(position=pt) atms.append(atpt) dist = [] for i in range(len(atms) - 1): dist.append(atms.get_distance(i, len(atms) - 1)) atms.pop() dlist = zip(dist, atms) dlist = sorted(dlist, key=lambda one: one[0], reverse=True) # Select atoms closest to random point atmst = Atoms() indexlist = [] for i in range(natomsmove): atmst.append(dlist[i][1]) indexlist.append(dlist[i][1].index) trans = (random.uniform(0, cellx[0] - cellm[0]), random.uniform(0, cellx[1] - cellm[1]), random.uniform(0, cellx[2] - cellm[2])) atmst.translate(trans) for i in range(len(indexlist)): index = indexlist[i] atms[index].position = atmst[i].position if Optimizer.structure == 'Defect': if Optimizer.isolate_mutation: indiv[0] = atms.copy() indiv[0].extend(indb) else: indiv[0] = atms.copy() else: indiv[0] = atms.copy() else: natomsmove = 0 trans = 0 Optimizer.output.write( 'Group Lattice Alteration Mutation performed on individual\n') Optimizer.output.write('Index = ' + repr(indiv.index) + '\n') Optimizer.output.write('Number of atoms moved = ' + repr(natomsmove) + '\n') Optimizer.output.write(repr(trans) + '\n') Optimizer.output.write(repr(indiv[0]) + '\n') muttype = 'LAGC' + repr(natomsmove) if indiv.energy == 0: indiv.history_index = indiv.history_index + 'm' + muttype else: indiv.history_index = repr(indiv.index) + 'm' + muttype return indiv
def reasonable_random_structure_maker(elements, composition_generator, cut_off_radius = 5.0, fill_factor_max = 0.55, fill_factor_min = 0.2, insert_attempt_max = 2000, max_build_failures = 20, element_radii = default_element_radii, hard_radii = default_hard_radii, magmom_generator = None, verbose = False): #seed = 312 from ase import Atoms, Atom from ase.data import atomic_numbers, chemical_symbols from ase.geometry import find_mic import numpy as np from numpy.random import rand from random import shuffle #z_numbers = [atomic_numbers[el] for el in elements ] #print('Not tested with non-orthrombic systems!') def compute_nspecies(atomic_fractions, fill_factor = 1.0): atomic_volumes=[4/3.*np.pi*(element_radii[el])**3 for el in elements] average_atomic_volume = np.array(atomic_fractions).dot(atomic_volumes) volume = (2*cut_off_radius)**3 natoms = fill_factor * volume/average_atomic_volume nspecies = np.ceil(natoms * atomic_fractions).astype(int ) return nspecies def compute_actual_fill_factor(atomic_fractions, nspecies): volume = (2*cut_off_radius)**3 atomic_volumes=[4/3.*np.pi*(element_radii[el])**3 for el in elements] total_atom_volume = np.array(atomic_volumes).dot(nspecies) return total_atom_volume/volume def safe_insertion_test(test_atoms, new_species, position): if len(test_atoms)>0: safe = True vec = test_atoms.get_positions() - position vec_min_image, vec_min_image_mag = find_mic(vec, cell = test_atoms.get_cell()) symbols = test_atoms.get_chemical_symbols() existing_atom_index = 0 while existing_atom_index < len(test_atoms) and safe: distance_cut = hard_radii[new_species] + hard_radii[symbols[existing_atom_index]] if vec_min_image_mag[existing_atom_index] < distance_cut: safe = False existing_atom_index += 1 return safe else: return True if verbose: print("Pure Compositions Possible:") for i in range(len(elements)): atomic_fractions = np.zeros(len(elements)) atomic_fractions[i] = 1.0 nspecies = compute_nspecies(atomic_fractions) print(elements[i].ljust(2), ':', int(nspecies.sum())) print('-------------------------------') composition_failed = True while composition_failed: fill_factor = (fill_factor_max - fill_factor_min) * rand() + fill_factor_min atomic_fractions = composition_generator(elements) nspecies = compute_nspecies(atomic_fractions, fill_factor = fill_factor ) actual_fill_factor = compute_actual_fill_factor(atomic_fractions, nspecies) symbol_list = [] for species_index in range(len(nspecies)): for i in range(nspecies[species_index]): symbol_list.append(elements[species_index]) #print(symbol_list) output_positions = np.zeros((sum(nspecies),3)) # random test order test_order = list(range(sum(nspecies))) shuffle(test_order) last_prefix = 'Atom Count '+ ('(%i) '%sum(nspecies)) print('Fill Factor'.ljust(len(last_prefix))+': %.4f [%.4f]'% (actual_fill_factor,fill_factor)) print('Composition'.ljust(len(last_prefix))+':', elements) print('Fractions'.ljust(len(last_prefix)) +':', atomic_fractions) print(last_prefix +':', nspecies) if True: test_symbols = '' for test_index in test_order: test_symbols = test_symbols + ' ' + symbol_list[test_index] print('Test Order'.ljust(len(last_prefix)) +':'+test_symbols) build_failed = True n_build_failures = 0 while build_failed and n_build_failures < max_build_failures: # i use this to store only the safe atoms for tests, I rebuild so that element order is preserved later test_atoms = Atoms(cell=[2*cut_off_radius, 2*cut_off_radius, 2*cut_off_radius], pbc = True) safe_inserts = 0 i = 0 attempt_no = 0 while attempt_no < insert_attempt_max and i < sum(nspecies): test_index = test_order[i] ### multiple attempts per atom safe_insert = False el = symbol_list[test_index] attempt_no = 0 while safe_insert==False and attempt_no < insert_attempt_max: position = test_atoms.get_cell().dot(rand(3)) safe_insert = safe_insertion_test(test_atoms, el, position) attempt_no +=1 #print(attempt_no) if safe_insert: safe_inserts += 1 test_atoms.append(Atom( el, position) ) output_positions[test_index] = position i+=1 #print(len(atoms), sum(nspecies)) if safe_inserts == sum(nspecies): composition_failed = False build_failed = False atoms = Atoms(cell=[2*cut_off_radius, 2*cut_off_radius, 2*cut_off_radius], pbc = True) for i in range(sum(nspecies)): atoms.append(Atom( symbol_list[i], output_positions[i]) ) else: n_build_failures += 1 print('%i Build Failures'%n_build_failures) if callable(magmom_generator): magmoms = magmom_generator(atoms) #should return random spins for the atoms input atoms.set_initial_magnetic_moments(magmoms) print('Net Magnetic Moment:', magmoms.sum()) #print(structure_direct, 'Structure Successfully Made.') print('Structure Successfully Made.') return atoms
def rotate(individual1, individual2, conserve_composition=True): """Rotates the two individuals around their centers of mass, splits them in half at the xy-plane, then splices them together. Maintains number of atoms. Args: individual1 (Individual): The first parent individual2 (Individual): The second parent conserve_composition (bool): default True. If True, conserves composition. Returns: Individual: The first child Individual: The second child The children are returned without indicies. """ # Preserve starting conditions of individual ind1c = individual1.copy() ind2c = individual2.copy() # Translate individuals so COM is at (0, 0, 0) com1 = ind1c.get_center_of_mass() ind1c.translate(-1 * com1) com2 = ind2c.get_center_of_mass() ind2c.translate(-1 * com2) # Select random axis and random angle and rotate individuals for _ in range(0, 10): rax = random.choice(['x', '-x', 'y', '-y', 'z', '-z']) rang = random.random() * 90 ind1c.rotate(rax, a=rang, center='COM', rotate_cell=False) # Search for atoms in individual 1 that are above the xy plane above_xy_plane1 = Atoms(cell=individual1.get_cell(), pbc=individual1.get_pbc()) indices1 = [] for atom in ind1c: if atom.z >= 0: above_xy_plane1.append(atom) indices1.append(atom.index) if len(above_xy_plane1) < 2 or len(above_xy_plane1) > len(ind1c): # Try again; unrotate ind1c ind1c.rotate(rax, a=-1 * rang, center='COM', rotate_cell=False) else: break ind2c.rotate(rax, a=rang, center='COM', rotate_cell=False) # Generate `above_xy_plane2`, with the same concentration as `above_xy_plane1` if needed above_xy_plane2 = Atoms(cell=individual2.get_cell(), pbc=individual2.get_pbc()) indices2 = [] dellist = [] if conserve_composition: symbols = list(set(above_xy_plane1.get_chemical_symbols())) # The below list contains atoms from ind2c, whereas `above_xy_plane1` contains atoms from ind1c atoms_by_symbol = { sym: [atm for atm in ind2c if atm.symbol == sym] for sym in symbols } for atom in above_xy_plane1: if len(atoms_by_symbol[atom.symbol]) > 0: # Get the atom from `atoms_by_symbol` that has the same type as `atom` and the largest `z` value dist = [atom.z for atom in atoms_by_symbol[atom.symbol]] pos = dist.index(max(dist)) above_xy_plane2.append(atoms_by_symbol[atom.symbol][pos]) indices2.append(atoms_by_symbol[atom.symbol][pos].index) del atoms_by_symbol[atom.symbol][pos] else: dellist.append(atom.index) if dellist: dellist.sort(reverse=True) for atom_index in dellist: del above_xy_plane1[atom_index] del indices1[atom_index] else: for atom in ind2c: if atom.z >= 0: above_xy_plane2.append(atom) indices2.append(atom.index) while len(above_xy_plane2) < len(above_xy_plane1) - len(dellist): # Too many atoms in above_xy_plane1 dellist.append(random.choice(above_xy_plane1).index) if dellist: dellist.sort(reverse=True) for atom in dellist: del above_xy_plane1[atom] del indices1[atom] dellist = [] while len(above_xy_plane1) < len(above_xy_plane2) - len(dellist): # Too many atoms in above_xy_plane2 dellist.append(random.choice(above_xy_plane2).index) if dellist: dellist.sort(reverse=True) for atom in dellist: del above_xy_plane2[atom] del indices2[atom] below_xy_plane1 = Atoms() below_xy_plane2 = Atoms() below_xy_plane2 = Atoms(cell=individual2.get_cell(), pbc=individual2.get_pbc()) for atom in ind2c: if atom.index not in indices2: below_xy_plane2.append(atom) below_xy_plane1 = Atoms(cell=individual1.get_cell(), pbc=individual1.get_pbc()) for atom in ind1c: if atom.index not in indices1: below_xy_plane1.append(atom) child1 = above_xy_plane2.copy() child1.extend(below_xy_plane1) child2 = above_xy_plane1.copy() child2.extend(below_xy_plane2) # Need to have at least one atom of each specie in atomlist to prevent LAMMPS from erroring if not conserve_composition: for i in range(len(individual1)): atoms1 = [atom for atom in child1 if atom.symbol == individual1[i]] atoms2 = [atom for atom in child2 if atom.symbol == individual1[i]] if len(atoms1) == 0 and len(atoms2) == 0: random.choice(child1).symbol = individual1[i].symbol random.choice(child2).symbol = individual1[i].symbol elif len(atoms1) == 0 and len(atoms2) != 0: del child1[random.randint(0, len(child1))] child1.append(random.choice(atoms2)) elif len(atoms1) != 0 and len(atoms2) == 0: del child2[random.randint(0, len(child2))] child2.append(random.choice(atoms1)) # Unrotate and untranslate the children child1.rotate(rax, a=-1 * rang, center='COM', rotate_cell=False) child2.rotate(rax, a=-1 * rang, center='COM', rotate_cell=False) child1.translate(com1) child2.translate(com2) full_child1 = individual1.copy() full_child1.clear() full_child1.extend(child1) full_child2 = individual2.copy() full_child2.clear() full_child2.extend(child2) return full_child1, full_child2
def get_defect_restart_indiv(Optimizer, indiv): """ Function to generate an structopt Individual class object containing a defect structure from a previously existing structure Inputs: Optimizer = structopt Optimizer class indiv = ASE Atoms object containing the previously existing structure Outputs: individ = structopt Individual class object containing defect structure data """ if not Optimizer.solidbulk: #Initialize Bulk - Generate or load positions of bulk solid try: rank = MPI.COMM_WORLD.Get_rank() except: rank = 0 outfilename = os.path.join( os.path.join(os.getcwd(), Optimizer.filename + '-rank' + repr(rank)), 'Bulkfile.xyz') if Optimizer.evalsolid: bulk1, PureBulkEnpa, stro = gen_solid(Optimizer.solidfile, Optimizer.solidcell, outfilename, Optimizer.calc, Optimizer.calc_method) Optimizer.output.write(stro) else: bulk1 = gen_solid(Optimizer.solidfile, Optimizer.solidcell, outfilename) PureBulkEnpa = 0 natomsbulk = len(bulk1) Optimizer.solidbulk = bulk1.copy() Optimizer.summary.write('CIBS Run Pure Bulk Energy per Atom:' + repr(PureBulkEnpa) + '\n') Optimizer.purebulkenpa = PureBulkEnpa Optimizer.natomsbulk = natomsbulk indiv.set_cell(Optimizer.solidcell) indiv.set_pbc(True) if Optimizer.restart_ints == 0: outt = find_defects(indiv, Optimizer.solidbulk, Optimizer.sf) else: indicop = [atm for atm in indiv if atm.symbol != 'X'] indiv = Atoms(cell=Optimizer.solidcell, pbc=True) for atm in indicop: indiv.append(atm) outt = [ indiv[0:Optimizer.restart_ints], indiv[Optimizer.restart_ints::], Atoms(), Atoms(), 'Assuming first ' + repr(Optimizer.restart_ints) + ' are interstitials\n' ] indi = outt[0].copy() bulki = outt[1].copy() individ = Individual(indi) individ.bulko = bulki.copy() individ.bulki = bulki.copy() individ.purebulkenpa = Optimizer.purebulkenpa individ.natomsbulk = Optimizer.natomsbulk individ.vacancies = outt[2].copy() individ.swaps = outt[3].copy() Optimizer.output.write(outt[4]) return individ
def snrmaster(Nanoparticle, metal, subsys100, subsys111, temparams, nmean, indexed_surfaces, plot, debug_box, prnt): #Produce the Nanoparticle SNR reference cell Nanoparticlesnr = Atoms() for i in Nanoparticle: Nanoparticlesnr.append(i) Nanoparticlesnr.set_cell(Nanoparticle.cell) Nanoparticlesnr.center() #Same procedure for the SNR reference cell Referencesnr = Atoms() for i in Nanoparticle: if i.symbol == metal: Referencesnr.append(i) Referencesnr.set_cell(Nanoparticle.cell) Referencesnr.center() #Read the TEM parameters tilt, plane_grid, num_slices, resample, dose, v0, defocus, Cs, df, MTF_param, blur, temsurface, rotation = temparams SNRparams = [ 0, plane_grid, num_slices, resample, dose, v0, defocus, Cs, df, MTF_param, blur, [0, 1, 0], 0 ] qstem = PyQSTEM('TEM') #Rotate systems to the surface we wish to investigate. Referencesnr.rotate(temsurface, [0, 1, 0], center='COU') Nanoparticlesnr.rotate(temsurface, [0, 1, 0], center='COU') #Rotate the system according to rotation parameter: Referencesnr.rotate(rotation, [0, 1, 0], center='COU') Nanoparticlesnr.rotate(rotation, [0, 1, 0], center='COU') #Tilt NP and reference S/N in order to match the system we visualise and the system we calculate S/N from. Nanoparticlesnr.rotate(tilt, [1, 0, 0], center="COU") Referencesnr.rotate(tilt, [1, 0, 0], center="COU") # First setup of the TEM objects for the NP and the reference system. # The setup happens before the loop designed to get an average since we need an img_wave object to define the S/N box. img_snr, img_wave_snr, defocus_value = IMG_sim(Nanoparticlesnr, SNRparams) img_reference_snr, img_wave_reference_snr, defocus_value = IMG_sim( Referencesnr, SNRparams) #Find the height and width of the box within which to calculate the value of the signal. def heightbox(): dire = abs(temsurface[0]) + abs(temsurface[1]) + abs(temsurface[2]) if (dire == 1): subsys = subsys100 if (dire == 3): subsys = subsys111 zmax = 0 xmax = 0 ymax = 0 zmaxmetal = 0 xmaxmetal = 0 ymaxmetal = 0 #Maximum x, y and z values are found for the subsystem: xmin = 0 ymin = 0 for i in subsys: if (i.position[2] > zmax): zmax = i.position[2] if (i.position[2] > zmaxmetal and i.symbol == metal): zmaxmetal = i.position[2] if abs(i.position[0]) > xmax: xmax = abs(i.position[0]) if abs(i.position[0]) > xmaxmetal and i.symbol == metal: xmaxmetal = abs(i.position[0]) if abs(i.position[1]) > ymax: ymax = abs(i.position[1]) if abs(i.position[1]) > ymax and i.symbol == metal: ymaxmetal = abs(i.position[1]) if i.position[0] < xmin and i.symbol != metal: xmin = i.position[0] if i.position[1] < ymin and i.symbol != metal: ymin = i.position[1] #Determine if adsorbate atoms have x-y-coordinates outside the subsystem unit cell. xyoffsets = [0] if xmax > xmaxmetal: xyoffsets.append(xmax - xmaxmetal) if ymax > ymaxmetal: xyoffsets.append(ymax - ymaxmetal) if ymin < 0: xyoffsets.append(abs(ymin)) if xmin < 0: xyoffsets.append(abs(xmin)) #Determine the offset in x,y,z: zoffset = zmax - zmaxmetal xyoffset = max(xyoffsets) return zoffset, xyoffset #Define a function to determine x,y and z coordinates for the SNR box in the nanoparticle unit cell: def coordinatesbox(): xmax = 0 height = heightbox()[0] ind = indexed_surfaces[tuple(temsurface)][0] zmax = 0 for i in indexed_surfaces[tuple(temsurface)]: pos = Nanoparticlesnr[i].position[1] if (pos > zmax): zmax = pos zmin = zmax for i in indexed_surfaces[tuple(temsurface)]: pos = Nanoparticlesnr[i].position[1] if (pos < zmin): zmin = pos zdif = zmax - zmin z = zmax height = (height + zdif) * 1.2 for i in indexed_surfaces[tuple(temsurface)]: pos = Nanoparticlesnr[i].position[0] if (pos > xmax): xmax = pos xmin = xmax for i in indexed_surfaces[tuple(temsurface)]: pos = Nanoparticlesnr[i].position[0] if (pos < xmin): xmin = pos xmax = xmax + heightbox()[1] xmin = xmin - heightbox()[1] return (xmin, xmax, z, z + height) xmin, xmax, zmin, zmax = coordinatesbox() xmin, xmax, zmin, zmax = coordinatesbox() #Conversion between number of data points and length scale dpl = len(img_snr.T[0]) / img_wave_snr.get_extent()[1] #Convert from lengths to corresponding index values in image array xmindata = math.floor(xmin * dpl) xmaxdata = math.floor(xmax * dpl) zmindata = math.floor(zmin * dpl) zmaxdata = math.floor(zmax * dpl) #Define RMS function. def rms(img): rms = np.sqrt(np.sum(img * img) / len(img.flat)) return rms #Define SNR function. def snrfunc(signal, noise): #We find the SNR of both the image and the corresponding inverted image. We return the maximum value. s1 = signal s2 = util.invert(signal) n1 = noise n2 = util.invert(noise) snr1 = rms(s1) / rms(n1) snr2 = rms(s2) / rms(n2) return max(snr1, snr2) zlen = zmaxdata - zmindata #Define arrays to contain SNR values. snrarr = [] nnrarr = [] # Make first data point of the average imgdif = img_snr - img_reference_snr #Select the data points where signal and noise is calculated from: imgcut = imgdif[xmindata:xmaxdata, zmindata:zmaxdata] imgcutnoise = imgdif[0:20, 0:20] #If debug_box=True, set the pixel values corresponding to the SNR boxes to 0. if debug_box: imgdif[xmindata:xmaxdata, zmindata:zmaxdata] = 0 imgdif[0:20, 0:20] = 0 imgcut_reference_snr = img_reference_snr[xmindata:xmaxdata, -1 - zlen:-1] imgcutnoise_reference_snr = img_reference_snr[0:20, 0:20] #For debugging. extentcut = [xmindata, xmaxdata, zmindata, zmaxdata] snr = snrfunc(imgcut, imgcutnoise) snrref = snrfunc(imgcut_reference_snr, imgcutnoise_reference_snr) snrarr.append(snr) nnrarr.append(snrref) #Loop over nmean for i in range(1, nmean): #Construct qstem objects again for use in the averaging img_snr, img_wave_snr, defocus_value = IMG_sim(Nanoparticlesnr, SNRparams) img_reference_snr, img_wave_reference_snr, defocus_value = IMG_sim( Referencesnr, SNRparams) #calculate SNR and NNR and append to array imgdif = img_snr - img_reference_snr imgcut = imgdif[xmindata:xmaxdata, zmindata:zmaxdata] imgcutnoise = imgdif[0:20, 0:20] #imgcut_reference_snr=img_reference_snr[xmindata:xmaxdata,zmindata:zmaxdata] imgcut_reference_snr = img_reference_snr[xmindata:xmaxdata, -1 - zlen:-1] imgcutnoise_reference_snr = img_reference_snr[0:20, 0:20] extentcut = [xmindata, xmaxdata, zmindata, zmaxdata] snr = snrfunc(imgcut, imgcutnoise) snrref = snrfunc(imgcut_reference_snr, imgcutnoise_reference_snr) snrarr.append(snr) nnrarr.append(snrref) #Rotate and tilt back Nanoparticlesnr.rotate(-tilt, [1, 0, 0], center="COU") Referencesnr.rotate(-tilt, [1, 0, 0], center="COU") Referencesnr.rotate(-rotation, [0, 1, 0], center='COU') Nanoparticlesnr.rotate(-rotation, [0, 1, 0], center='COU') Nanoparticlesnr.rotate([0, 1, 0], temsurface, center='COU') Referencesnr.rotate([0, 1, 0], temsurface, center='COU') finalsnr = np.average(snrarr) finalsnrdb = 10 * math.log10(finalsnr) finalnnr = np.average(nnrarr) finalnnrdb = 10 * math.log10(finalnnr) if (prnt): print('Average SNR: ' + str(finalsnr)) print('Average SNR in dB:' + str(finalsnrdb)) print('Average reference NNR: ' + str(finalnnr)) print('Average reference NNR in dB: ' + str(finalnnrdb) + '\n') #If plot=True, plot the different pixel arrays used in the calculation of SNR: if (plot): figimgsnr, axs = plt.subplots(1, 3, figsize=(10, 7), dpi=300) axs[0].imshow(img_snr.T, extent=img_wave_snr.get_extent(), cmap='gray', interpolation='nearest', origin='lower') axs[1].imshow(img_reference_snr.T, extent=img_wave_reference_snr.get_extent(), cmap='gray', interpolation='nearest', origin='lower') axs[2].imshow(imgdif.T, extent=img_wave_reference_snr.get_extent(), cmap='gray', interpolation='nearest', origin='lower') figsnr = plt.figure(figsize=(5, 4), dpi=200) x = np.arange(nmean) plt.plot(x, snrarr, label='S/N ratio', linewidth=2, marker='s', color='blue') plt.plot(x, nnrarr, label='N/N ratio', linewidth=2, marker='o', color='red') plt.axhline(y=np.average(snrarr), xmin=0, xmax=nmean, color='green', linestyle='--', label='Average S/N:\n {:.3} dB'.format(finalsnrdb)) plt.axhline(y=np.average(nnrarr), xmin=0, xmax=nmean, color='black', linestyle='--', label='Average N/N:\n {:.3} dB'.format(finalnnrdb)) plt.rcParams['mathtext.fontset'] = 'cm' plt.rcParams['font.family'] = 'serif' plt.xlabel('Data points') plt.ylabel('Ratio value') plt.title('Average value of S/N-ratio') plt.legend() #Depending on what is needed, return the following objects: return snrarr, nnrarr, finalsnr, finalsnrdb, img_snr, img_reference_snr, img_wave_snr, img_wave_reference_snr
from ase.constraints import FixAtoms from ase.optimize import QuasiNewton from ase.calculators.emt import EMT from ase.dimer import DimerControl, MinModeAtoms, MinModeTranslate from catlearn.optimize.mldimer import MLDimer # Setting up the initial image: a = 4.0614 b = a / sqrt(2) h = b / 2 initial = Atoms('Al2', positions=[(0, 0, 0), (a / 2, b / 2, -h)], cell=(a, b, 2 * h), pbc=(1, 1, 0)) initial *= (2, 2, 2) initial.append(Atom('Al', (a / 2, b / 2, 3 * h))) initial.center(vacuum=4.0, axis=2) N = len(initial) # number of atoms # Make a mask of zeros and ones that select fixed atoms - the two # bottom layers: mask = initial.positions[:, 2] - min(initial.positions[:, 2]) < 1.5 * h constraint = FixAtoms(mask=mask) initial.set_constraint(constraint) # Calculate using EMT: initial.set_calculator(EMT()) # Relax the initial state: QuasiNewton(initial, trajectory='initial.traj').run(fmax=0.05)
def scan(self): super(QboxWavefunctionLoader, self).scan() if self.xmlfile is None: xmllist = sorted(glob("*xml"), key=lambda f: os.path.getsize(f)) if len(xmllist) == 0: raise IOError( "No xml file found in current directory: {}".format( os.getcwd())) elif len(xmllist) == 1: self.xmlfile = xmllist[0] else: self.xmlfile = xmllist[-1] if mpiroot: print("More than one xml files found: {}".format(xmllist)) print( "Assume wavefunction is in the largest xml file: {} ({} MB)" .format(self.xmlfile, os.path.getsize(self.xmlfile) / 1024**2)) if mpiroot: print("Reading wavefunction from file {}".format(self.xmlfile)) iterxml = etree.iterparse(self.xmlfile, huge_tree=True, events=("start", "end")) for event, leaf in iterxml: if event == "end" and leaf.tag == "unit_cell": R1 = np.fromstring(leaf.attrib["a"], sep=" ", dtype=np.float_) * bohr_to_angstrom R2 = np.fromstring(leaf.attrib["b"], sep=" ", dtype=np.float_) * bohr_to_angstrom R3 = np.fromstring(leaf.attrib["c"], sep=" ", dtype=np.float_) * bohr_to_angstrom lattice = np.array([R1, R2, R3]) ase_cell = Atoms(cell=lattice, pbc=True) if event == "end" and leaf.tag == "atom": species = leaf.attrib["species"] position = np.array( parse_many_values(3, float, leaf.find("position").text)) ase_cell.append( Atom(symbol=species, position=position * bohr_to_angstrom)) if event == "start" and leaf.tag == "wavefunction": nspin = int(leaf.attrib["nspin"]) assert nspin == 2 iorb_sb_map = list() sb_psir_map = dict() if event == "end" and leaf.tag == "grid": n1, n2, n3 = int(leaf.attrib["nx"]), int( leaf.attrib["ny"]), int(leaf.attrib["nz"]) if event == "start" and leaf.tag == "slater_determinant": spin = leaf.attrib["spin"] if event == "end" and leaf.tag == "density_matrix": if spin == "up": uoccs = np.fromstring(leaf.text, sep=" ", dtype=np.float_) iuorbs = np.where(uoccs > 0.8)[0] + 1 nuorbs = len(iuorbs) iorb_sb_map.extend( ("up", iuorbs[iorb]) for iorb in range(nuorbs)) elif spin == "down": doccs = np.fromstring(leaf.text, sep=" ", dtype=np.float_) idorbs = np.where(doccs > 0.8)[0] + 1 ndorbs = len(idorbs) iorb_sb_map.extend( ("down", idorbs[iorb]) for iorb in range(ndorbs)) else: raise ValueError if event == "end" and leaf.tag == "grid_function": leaf.clear() if event == "start" and leaf.tag == "wavefunction_velocity": break norbs = nuorbs + ndorbs iorb_fname_map = [self.xmlfile] * norbs cell = Cell(ase_cell) ft = FourierTransform(n1, n2, n3) self.wfc = Wavefunction(cell=cell, ft=ft, nuorbs=nuorbs, ndorbs=ndorbs, iorb_sb_map=iorb_sb_map, iorb_fname_map=iorb_fname_map) for (band, spin), psir in sb_psir_map.items(): iorb = self.wfc.sb_iorb_map[band, spin] psir = sb_psir_map[band, spin] self.wfc.set_psir(iorb, psir)
def newclus(ind1, ind2, Optimizer): """Select a box in the cluster configuration""" if 'CX' in Optimizer.debug: debug = True else: debug = False Optimizer.output.write('Box Cluster Cx between individual ' + repr(ind1.index) + ' and individual ' + repr(ind2.index) + '\n') #Perserve starting conditions of individual solid1 = ind1[0].copy() solid2 = ind2[0].copy() cello1 = ind1[0].get_cell() cello2 = ind2[0].get_cell() cell1 = numpy.maximum.reduce(solid1.get_positions()) cell1m = numpy.minimum.reduce(solid1.get_positions()) cell2 = numpy.maximum.reduce(solid2.get_positions()) cell2m = numpy.minimum.reduce(solid2.get_positions()) cell = numpy.minimum(cell1, cell2) pbc1 = solid1.get_pbc() pbc2 = solid2.get_pbc() #Get starting concentrations and number of atoms nat1 = len(solid1) nat2 = len(solid2) # Pick a origin point for box in the cell pt1 = random.choice(solid1) pt1f = [(pt1.position[i] - cell1m[i]) / cell1[i] for i in range(3)] pt2 = [pt1f[i] * cell2[i] + cell2m[i] for i in range(3)] solid2.append(Atom(position=pt2)) pt2 = solid2[len(solid2) - 1] #Find max neighborsize of circle cut r = random.uniform(0, min(nat1, nat2) / 5.0) if debug: print 'DEBUG CX: Point one =', pt1.position print 'DEBUG CX: Point two =', pt2.position #Find atoms within sphere of neighborsize r for both individuals #Make sure that crossover is only selection of atoms not all while True: ctoff = [r for on in solid1] nl = NeighborList(ctoff, bothways=True, self_interaction=False) nl.update(solid1) indices1, offsets = nl.get_neighbors(pt1.index) if len(indices1) == 0: r = r * 1.2 elif len(indices1) < nat1 * .75: break else: r = r * 0.8 if debug: print 'Neighborsize of box = ' + repr( r) + '\nPosition in solid1 = ' + repr( pt1.position) + '\nPosition in solid2 = ' + repr(pt2.position) group1 = Atoms(cell=solid1.get_cell(), pbc=solid1.get_pbc()) group1.append(pt1) indices1a = [pt1.index] for index, d in zip(indices1, offsets): if index not in indices1a: index = int(index) pos = solid1[index].position + numpy.dot(d, solid1.get_cell()) group1.append(Atom(symbol=solid1[index].symbol, position=pos)) indices1a.append(index) indices1 = indices1a ctoff = [r for on in solid2] nl = NeighborList(ctoff, bothways=True, self_interaction=False) nl.update(solid2) indices2, offsets = nl.get_neighbors(pt2.index) group2 = Atoms(cell=solid2.get_cell(), pbc=solid2.get_pbc()) indices2a = [] for index, d in zip(indices2, offsets): if index not in indices2a: index = int(index) pos = solid2[index].position + numpy.dot(d, solid2.get_cell()) group2.append(Atom(symbol=solid2[index].symbol, position=pos)) indices2a.append(index) indices2 = indices2a if len(indices2) == 0: for one in group1: while True: sel = random.choice(solid2) if sel.symbol == one.symbol: if sel.index not in indices2: group2.append(sel) indices2.append(sel.index) break if Optimizer.forcing == 'Concentration': symlist = list(set(group1.get_chemical_symbols())) seplist = [[atm for atm in group2 if atm.symbol == sym] for sym in symlist] group2n = Atoms(cell=group2.get_cell(), pbc=group2.get_pbc()) indices2n = [] dellist = [] for one in group1: sym1 = one.symbol listpos = [i for i, s in enumerate(symlist) if s == sym1][0] if len(seplist[listpos]) > 0: pos = random.choice(range(len(seplist[listpos]))) group2n.append(seplist[listpos][pos]) indices2n.append(indices2[seplist[listpos][pos].index]) del seplist[listpos][pos] else: dellist.append(one.index) if len(dellist) != 0: dellist.sort(reverse=True) for one in dellist: del group1[one] del indices1[one] indices2n.append(pt2.index) indices2 = indices2n group2 = group2n.copy() else: dellist = [] while len(group2) < len(group1) - len(dellist): #Too many atoms in group 1 dellist.append(random.choice(group1).index) if len(dellist) != 0: dellist.sort(reverse=True) for one in dellist: del group1[one] del indices1[one] dellist = [] while len(group1) < len(group2) - len(dellist): #Too many atoms in group 2 dellist.append(random.choice(group2).index) if len(dellist) != 0: dellist.sort(reverse=True) for one in dellist: del group2[one] del indices2[one] other2 = Atoms(cell=solid2.get_cell(), pbc=solid2.get_pbc()) for one in solid2: if one.index not in indices2: other2.append(one) other1 = Atoms(cell=solid1.get_cell(), pbc=solid1.get_pbc()) for one in solid1: if one.index not in indices1: other1.append(one) #Exchange atoms in sphere and build new solids nsolid1 = other1.copy() nsolid1.extend(group2.copy()) nsolid2 = other2.copy() nsolid2.extend(group1.copy()) #DEBUG: Write crossover to file if debug: write_xyz(Optimizer.debugfile, nsolid1, 'CX(randalloybx):nsolid1') write_xyz(Optimizer.debugfile, nsolid2, 'CX(randalloybx):nsolid2') #DEBUG: Check structure of atoms exchanged for sym, c, m, u in Optimizer.atomlist: if Optimizer.structure == 'Defect': nc = len([atm for atm in nsolid1 if atm.symbol == sym]) nc += len([atm for atm in ind1.bulki if atm.symbol == sym]) oc = len([atm for atm in solid1 if atm.symbol == sym]) oc += len([atm for atm in ind1.bulki if atm.symbol == sym]) else: nc = len([atm for atm in nsolid1 if atm.symbol == sym]) oc = len([atm for atm in solid1 if atm.symbol == sym]) Optimizer.output.write('CX(clustbx):New solid1 contains ' + repr(nc) + ' ' + repr(sym) + ' atoms\n') if debug: print 'DEBUG CX: New solid1 contains ' + repr(nc) + ' ' + repr( sym) + ' atoms' if oc != nc: #pdb.set_trace() print 'CX: Issue in maintaining atom concentration\n Dropping new individual' Optimizer.output.write( 'CX: Issue in maintaining atom concentration\n Dropping new individual 1\n' ) nsolid1 = solid1 for sym, c, m, u in Optimizer.atomlist: if Optimizer.structure == 'Defect': nc = len([atm for atm in nsolid2 if atm.symbol == sym]) nc += len([atm for atm in ind2.bulki if atm.symbol == sym]) oc = len([atm for atm in solid2 if atm.symbol == sym]) oc += len([atm for atm in ind2.bulki if atm.symbol == sym]) else: nc = len([atm for atm in nsolid2 if atm.symbol == sym]) oc = len([atm for atm in solid2 if atm.symbol == sym]) Optimizer.output.write('CX(clustbx):New solid2 contains ' + repr(nc) + ' ' + repr(sym) + ' atoms\n') if debug: print 'DEBUG CX: New solid2 contains ' + repr(nc) + ' ' + repr( sym) + ' atoms' if oc != nc: #pdb.set_trace() print 'CX: Issue in maintaining atom concentration\n Dropping new individual' Optimizer.output.write( 'CX: Issue in maintaining atom concentration\n Dropping new individual 2\n' ) solid2.pop() nsolid2 = solid2 if Optimizer.forcing != 'Concentration': for i in range(len(Optimizer.atomlist)): atms1 = [ inds for inds in nsolid1 if inds.symbol == Optimizer.atomlist[i][0] ] atms2 = [ inds for inds in nsolid2 if inds.symbol == Optimizer.atomlist[i][0] ] if len(atms1) == 0: if len(atms2) == 0: nsolid1[random.randint( 0, len(indi1) - 1)].symbol == Optimizer.atomlist[i][0] nsolid2[random.randint( 0, len(indi2) - 1)].symbol == Optimizer.atomlist[i][0] else: nsolid1.append(atms2[random.randint(0, len(atms2) - 1)]) nsolid1.pop(random.randint(0, len(nsolid1) - 2)) else: if len(atms2) == 0: nsolid2.append(atms1[random.randint(0, len(atms1) - 1)]) nsolid2.pop(random.randint(0, len(nsolid2) - 2)) nsolid1.set_cell(cello1) nsolid2.set_cell(cello2) nsolid1.set_pbc(pbc1) nsolid2.set_pbc(pbc2) ind1[0] = nsolid1.copy() ind2[0] = nsolid2.copy() return ind1, ind2
def unitcell(self, latx, laty): pos2 = np.array([ 0.34109, 1.18154, 0, 7.16273, 4.33233, 0, 4.77515, 3.74156, 0, 5.11624, 2.36309, 0, 2.38758, 3.15078, 0, 5.45733, 6.10465, 0, 7.50382, 2.95387, 0, 3.41082, 4.13541, 0, 2.72866, 1.77231, 0, 0.68216, 4.92311, 0, 8.18599, 5.31696, 0, 4.09299, 1.37846, 0, 8.18599, 0.19692, 0, 6.82166, 0.59078, 0, 2.04649, 14.76931, 0, 3.41082, 14.37546, 0, 5.45733, 11.22468, 0, 1.36433, 12.40622, 0, 0.68216, 10.04313, 0, 0.34109, 11.42161, 0, 6.13949, 13.58776, 0, 3.06975, 10.6339, 0, 2.72866, 12.01237, 0, 7.8449, 11.81546, 0, 5.11624, 12.60315, 0, 7.50382, 13.19392, 0, 8.52706, 9.0585, 0, 6.82166, 10.83083, 0, 6.48057, 7.08926, 0, 3.75191, 7.87696, 0, 4.77515, 8.86159, 0, 6.13949, 8.46774, 0, 1.70542, 5.90772, 0, 1.36433, 7.2862, 0, 4.09299, 6.4985, 0, 2.04649, 9.64928, 0, 0, 2.56002, 0, 0, 7.68004, 0, 9.8914, 3.54463, 0, 14.66656, 4.72618, 0, 13.6433, 3.74156, 0, 14.32548, 6.10465, 0, 16.37197, 2.95387, 0, 11.9379, 5.51387, 0, 12.27897, 4.13541, 0, 11.59681, 1.77231, 0, 9.55032, 4.92311, 0, 17.05414, 5.31696, 0, 16.71305, 6.69542, 0, 12.96114, 1.37846, 0, 10.57357, 0.7877, 0, 17.05414, 0.19692, 0, 15.34872, 1.96924, 0, 15.68981, 0.59078, 0, 10.91464, 14.76931, 0, 17.39521, 14.17854, 0, 12.27897, 14.37546, 0, 14.32548, 11.22468, 0, 12.62006, 12.997, 0, 10.23248, 12.40622, 0, 9.8914, 13.7847, 0, 9.55032, 10.04313, 0, 15.00764, 13.58776, 0, 11.59681, 12.01237, 0, 14.66656, 14.96624, 0, 16.37197, 13.19392, 0, 15.68981, 10.83083, 0, 13.6433, 8.86159, 0, 15.00764, 8.46774, 0, 16.03088, 9.45235, 0, 10.23248, 7.2862, 0, 12.96114, 6.4985, 0, 11.25573, 8.27081, 0, 10.91464, 9.64928, 0, 8.86815, 2.56002, 0, 8.86815, 7.68004, 0, 13.30223, 0, 0, 13.30223, 10.24006, 0, 1.02325, 18.90472, 0, 5.7984, 20.08626, 0, 4.77515, 19.10165, 0, 5.45733, 21.46474, 0, 7.50382, 18.31395, 0, 3.06975, 20.87396, 0, 3.41082, 19.4955, 0, 2.72866, 17.1324, 0, 0.68216, 20.28319, 0, 8.18599, 20.67704, 0, 7.8449, 22.0555, 0, 4.09299, 16.73855, 0, 1.70542, 16.14778, 0, 8.18599, 15.557, 0, 6.48057, 17.32933, 0, 6.82166, 15.95087, 0, 2.04649, 30.12939, 0, 8.52706, 29.53863, 0, 3.41082, 29.73555, 0, 5.45733, 26.58476, 0, 3.75191, 28.35709, 0, 1.36433, 27.76631, 0, 1.02325, 29.14478, 0, 0.68216, 25.40322, 0, 6.13949, 28.94785, 0, 2.72866, 27.37246, 0, 5.7984, 30.32633, 0, 7.50382, 28.554, 0, 6.82166, 26.19091, 0, 4.77515, 24.22167, 0, 6.13949, 23.82783, 0, 7.16273, 24.81244, 0, 1.36433, 22.64628, 0, 4.09299, 21.85859, 0, 2.38758, 23.6309, 0, 2.04649, 25.00937, 0, 0, 17.92011, 0, 0, 23.04013, 0, 4.43408, 15.36009, 0, 4.43408, 25.60015, 0, 9.20924, 16.54163, 0, 16.03088, 19.69242, 0, 13.6433, 19.10165, 0, 13.98439, 17.72318, 0, 11.25573, 18.51087, 0, 14.32548, 21.46474, 0, 16.37197, 18.31395, 0, 12.27897, 19.4955, 0, 11.59681, 17.1324, 0, 9.55032, 20.28319, 0, 17.05414, 20.67704, 0, 12.96114, 16.73855, 0, 17.05414, 15.557, 0, 15.68981, 15.95087, 0, 10.91464, 30.12939, 0, 12.27897, 29.73555, 0, 14.32548, 26.58476, 0, 10.23248, 27.76631, 0, 9.55032, 25.40322, 0, 9.20924, 26.78169, 0, 15.00764, 28.94785, 0, 11.9379, 25.99398, 0, 11.59681, 27.37246, 0, 16.71305, 27.17554, 0, 13.98439, 27.96324, 0, 16.37197, 28.554, 0, 17.39521, 24.41859, 0, 15.68981, 26.19091, 0, 15.34872, 22.44935, 0, 12.62006, 23.23705, 0, 13.6433, 24.22167, 0, 15.00764, 23.82783, 0, 10.57357, 21.26781, 0, 10.23248, 22.64628, 0, 12.96114, 21.85859, 0, 10.91464, 25.00937, 0, 8.86815, 17.92011, 0, 8.86815, 23.04013, 0, 1.02325, 3.54463, 0, 5.7984, 4.72618, 0, 3.06975, 5.51387, 0, 7.8449, 6.69542, 0, 1.70542, 0.7877, 0, 6.48057, 1.96924, 0, 8.52706, 14.17854, 0, 3.75191, 12.997, 0, 1.02325, 13.7847, 0, 5.7984, 14.96624, 0, 7.16273, 9.45235, 0, 2.38758, 8.27081, 0, 4.43408, 0, 0, 4.43408, 10.24006, 0, 9.20924, 1.18154, 0, 16.03088, 4.33233, 0, 13.98439, 2.36309, 0, 11.25573, 3.15078, 0, 9.20924, 11.42161, 0, 11.9379, 10.6339, 0, 16.71305, 11.81546, 0, 13.98439, 12.60315, 0, 17.39521, 9.0585, 0, 15.34872, 7.08926, 0, 12.62006, 7.87696, 0, 10.57357, 5.90772, 0, 0.34109, 16.54163, 0, 7.16273, 19.69242, 0, 5.11624, 17.72318, 0, 2.38758, 18.51087, 0, 0.34109, 26.78169, 0, 3.06975, 25.99398, 0, 7.8449, 27.17554, 0, 5.11624, 27.96324, 0, 8.52706, 24.41859, 0, 6.48057, 22.44935, 0, 3.75191, 23.23705, 0, 1.70542, 21.26781, 0, 9.8914, 18.90472, 0, 14.66656, 20.08626, 0, 11.9379, 20.87396, 0, 16.71305, 22.0555, 0, 10.57357, 16.14778, 0, 15.34872, 17.32933, 0, 17.39521, 29.53863, 0, 12.62006, 28.35709, 0, 9.8914, 29.14478, 0, 14.66656, 30.32633, 0, 16.03088, 24.81244, 0, 11.25573, 23.6309, 0, 13.30223, 15.36009, 0, 13.30223, 25.60015, 0 ]).reshape(-1, 3) / 1.42 pos1 = [6.928400, 13.000369, 0.000000, 7.794450, 16.500469, 0.000000] phi = pi / 2 - atan((pos1[4] - pos1[1]) / (pos1[3] - pos1[0])) cbond = np.linalg.norm((pos1[4] - pos1[1], pos1[3] - pos1[0], 0)) dx = sqrt(3) * cbond * 2 dy = 3 * cbond * 2 atoms = Atoms() for i, coord in enumerate(pos2): ele = ['C', 'N'][i < 156] atom = Atom(ele, coord) atoms.append(atom) #atoms.rotate('z',phi) atoms.set_cell([dx, dy, 10.0]) col = atoms.repeat((latx, laty, 1)) return col
def get_new_individual(self, parents): f, m = parents indi = self.initialize_individual(f) indi.info['data']['parents'] = [i.info['confid'] for i in parents] theta = random.random() * 2 * np.pi # 0,2pi phi = random.random() * np.pi # 0,pi e = np.array((np.sin(phi) * np.cos(theta), np.sin(theta) * np.sin(phi), np.cos(phi))) eps = 0.0001 f.translate(-f.get_center_of_mass()) m.translate(-m.get_center_of_mass()) # Get the signed distance to the cutting plane # We want one side from f and the other side from m fmap = [np.dot(x, e) for x in f.get_positions()] mmap = [-np.dot(x, e) for x in m.get_positions()] ain = sorted([i for i in chain(fmap, mmap) if i > 0], reverse=True) aout = sorted([i for i in chain(fmap, mmap) if i < 0], reverse=True) off = len(ain) - len(f) # Translating f and m to get the correct number of atoms # in the offspring if off < 0: # too few # move f and m away from the plane dist = (abs(aout[abs(off) - 1]) + abs(aout[abs(off)])) * .5 f.translate(e * dist) m.translate(-e * dist) elif off > 0: # too many # move f and m towards the plane dist = (abs(ain[-off - 1]) + abs(ain[-off])) * .5 f.translate(-e * dist) m.translate(e * dist) if off != 0 and dist == 0: # Exactly same position => we continue with the wrong number # of atoms. What should be done? Fail or return None or # remove one of the two atoms with exactly the same position. pass # Determine the contributing parts from f and m tmpf, tmpm = Atoms(), Atoms() for atom in f: if np.dot(atom.position, e) > 0: atom.tag = 1 tmpf.append(atom) for atom in m: if np.dot(atom.position, e) < 0: atom.tag = 2 tmpm.append(atom) # Check that the correct composition is employed if self.keep_composition: opt_sm = sorted(f.numbers) tmpf_numbers = list(tmpf.numbers) tmpm_numbers = list(tmpm.numbers) cur_sm = sorted(tmpf_numbers + tmpm_numbers) # correct_by: dictionary that specifies how many # of the atom_numbers should be removed (a negative number) # or added (a positive number) correct_by = dict([(j, opt_sm.count(j)) for j in set(opt_sm)]) for n in cur_sm: correct_by[n] -= 1 correct_in = random.choice([tmpf, tmpm]) to_add, to_rem = [], [] for num, amount in correct_by.items(): if amount > 0: to_add.extend([num] * amount) elif amount < 0: to_rem.extend([num] * abs(amount)) for add, rem in zip(to_add, to_rem): tbc = [a.index for a in correct_in if a.number == rem] if len(tbc) == 0: pass ai = random.choice(tbc) correct_in[ai].number = add # Move the contributing apart if any distance is below blmin maxl = 0. for sv, min_dist in self.get_vectors_below_min_dist(tmpf + tmpm): lsv = np.linalg.norm(sv) # length of shortest vector d = [-np.dot(e, sv)] * 2 d[0] += np.sqrt(np.dot(e, sv)**2 - lsv**2 + min_dist**2) d[1] -= np.sqrt(np.dot(e, sv)**2 - lsv**2 + min_dist**2) l = sorted([abs(i) for i in d])[0] / 2. + eps if l > maxl: maxl = l tmpf.translate(e * maxl) tmpm.translate(-e * maxl) # Put the two parts together for atom in chain(tmpf, tmpm): indi.append(atom) parent_message = ':Parents {0} {1}'.format(f.info['confid'], m.info['confid']) return (self.finalize_individual(indi), self.descriptor + parent_message)
def Nanoparticle_Builder(metal, lc, surfaces, layers, adsorbate, site, coverage, cell_height, subsys_height, unmasked_layers, E_cut, kpoints, ads_cutoff, ads111, ads100, subsys111_relaxed, subsys100_relaxed, relax): ### ### Function to set up the Nanoparticle base ### #Input pparameters # metal: The metal comprising the substrate/nanoparticle. For example 'Au'. # lc: Lattice constant. For example 4.0800. # surfaces: The surfaces in the NP. For example [(1, 0, 0),(1, 1, 1)]. # layers: The number of layers from the cetner of the NP to each surface. For example: [6,5] # adsorbate: The molecule to be adsorbed on top. Should be an object which ase can read. For example: molecule('CO') # site: Either 'OnTop' or 'Hollow'. Should be 'OnTop' for 100% coverages. # coverage: Desired coverage. Must be 1/n where n is whole number. # cell_height: The height of the subsystem unit cell. For example 6*lc. # subsys_height: Height of subsystem - no. of atom layers. For example: 4. If reading the relaxed subsystem from a traj file MAKE SURE THE HEIGHTS MATCH. # unmasked_layers: The number of layers in the NP subsystem (from the top) that is allowed to move during DFT relaxation. # E_cut: Cutoff energy for DFT PW. # kpoints: k-point grid in the first Brillouin zone. # ads_cutoff: The cutoff for adsorption energy. This determines whether adsorption has occurred for a surface or not. # ads111: If providing a relaxed subsystem, this is the adsorption energy for the 111 surface. # ads100: If providing a relaxed subsystem, this is the adsorption energy for the 100 surface. # subsys111_relaxed: This is an ASE object containing the relaxed subsystem for 111. Necessary to skip DFT. # subsys100_relaxed: This is an ASE object containing the relaxed subsystem for 100. Necessary to skip DFT. # relax: A boolean to determine whether or not DFT calculations have to be run or not. If relax=False, make sure to provide subsys111_relaxed and subsys100_relaxed along with ads111 and ads100. if site == None: site = 'OnTop' if cell_height == None: cell_height = 6 * lc if subsys_height == None: subsys_height = 3 if unmasked_layers == None: unmasked_layers = 1 if E_cut == None: E_cut = 200 if kpoints == None: kpoints = (6, 6, 1) if ads_cutoff == None: ads_cutoff = 0 #The bulk structure is created. Nanoparticle = FaceCenteredCubic(metal, surfaces, layers, latticeconstant=lc) Reference = FaceCenteredCubic( metal, surfaces, layers, latticeconstant=lc ) #A reference is made for TEM imaging without adsorbates. ##Alternative wulff_construction-based setup. This requires N_atoms and surf_energies list to be set up (for gold, use surf_energies=[1.23,1]). #Nanoparticle = wulff_construction(metal, surfaces, surf_energies,size=N_atoms, 'fcc',rounding='above', latticeconstant=lc) #Reference=wulff_construction(metal, surfaces, surf_energies,size=N_atoms, 'fcc',rounding='above', latticeconstant=lc) surf_atoms = [ ] #This list will contain indeces for each atom that is on a surface of the nanoparticle. #Each surface is rotated to the top of the unit cell. The corresponding surface atoms are then the atoms with highest y-position. for i in Nanoparticle.get_surfaces(): Nanoparticle.rotate(i, [0, 1, 0], center="COU") y_max = 0 for j in Nanoparticle: if j.position[1] > y_max: y_max = j.position[1] for j in Nanoparticle: if round(j.position[1], 2) == round(y_max, 2): surf_atoms.append(j.index) Nanoparticle.rotate([0, 1, 0], i, center="COU") #Now we need to identify the edge atoms. These are the atoms that are part of 2 or more surfaces in the nanoparticle. #Therefore, they will be the atoms that appear more than once in surf_atoms: marked = [] edge_atoms = [] for i in surf_atoms: if i in marked: edge_atoms.append(i) else: marked.append(i) #A subsystem of the bulk is defined. This will be the basis of the DFT calculation. We also define relevant functions to #translate between bulk atom coordinates and the subsystem coordinates. def sizesetup(coverage ): #Define the function to set up the size of the unit cell. invconverage = math.floor(1 / coverage) for i in range(1, invconverage + 1): for j in range(1, invconverage + 1): if j / i**2 == coverage: return (i, j) break #This function wraps lattice coordinates (i,j) back into the corresponding coordinate in the unit cell. def coordreference(i, j, ucx, ucy, direction): if direction == 1: ri = i % ucx ry = j % ucy if direction == 3: #If the unit cell is not orthogonal: if ucx % 2 != 0 and ucy % 2 != 0: ri = i % ucx ry = j % ucy #If the unit cell is orthogonal: else: i = i + j / 2 - ( j % ucy ) / 2 #Moving along j also corresponds to movement along i. ri = i % ucx ry = j % ucy return (ri, ry) ss = sizesetup(coverage) ucx = ss[0] ucy = ss[0] height = subsys_height n_adsorbates = ss[1] #Check if the requirement for orthogonal unit cell is met: if ss[0] % 2 == 0: orthogonal = True else: orthogonal = False size = [ucx, ucy, height] #Size of the FCC bulk structure. #Set up subsystems for the 111 and 100 directions. subsys111 = fcc111(symbol=metal, size=size, a=lc, vacuum=None, orthogonal=orthogonal) subsys100 = fcc100(symbol=metal, size=size, a=lc, vacuum=None, orthogonal=True) # Give all atoms in top layer a coordinate atomorigo = ucx * ucy * (height - 1) n = ucx * ucy * height subsys_coordinates = {} i = 0 j = 0 for q in range(atomorigo, n): if (i == ucx): j += 1 i = 0 subsys_coordinates[q] = [i, j, 0] i += 1 #Now we have to find a set of basis vectors describing the subsystem surface. if ss[0] > 1: #For 111: v1_111 = subsys111[atomorigo + 1].position - subsys111[atomorigo].position v1_111 = np.array([v1_111[0], v1_111[1], 0]) v2_111 = subsys111[atomorigo + ss[0]].position - subsys111[atomorigo].position v2_111 = np.array([v2_111[0], v2_111[1], 0]) #For 100: v1_100 = subsys100[atomorigo + 1].position - subsys100[atomorigo].position v1_100 = np.array([v1_100[0], v1_100[1], 0]) v2_100 = subsys100[atomorigo + ss[0]].position - subsys100[atomorigo].position v2_100 = np.array([v2_100[0], v2_100[1], 0]) else: v1_111 = np.array([0, 0, 0]) v2_111 = np.array([0, 0, 0]) v1_100 = np.array([0, 0, 0]) v2_100 = np.array([0, 0, 0]) #Now we add adsorbates matching the coverage. They are added along the diagonal in the unit cell. if site == 'OnTop': position100 = [0, 0, 0] position111 = [0, 0, 0] else: position100 = 1 / 2 * v1_100 + 1 / 2 * v2_100 position111 = 1 / 2 * v1_111 + [0, 1 / 2 * v2_111[1], 0] zig = True #If zig is false, then the 111 adsorbate won't zag. zags = 0 adsorbate_atom_links = [ ] #This list will link adsorbates to an atom in the surface. for i in range(0, n_adsorbates): zig = not zig if zig and i > 1: zags = zags + 1 for j in adsorbate: j.tag = i scale = ss[0] / n_adsorbates adsorbate_atom_links.append([i * scale, i * scale]) pos111 = position111 + i * scale * v1_111 + i * scale * v2_111 - zags * v1_111 pos100 = position100 + i * scale * v1_100 + i * scale * v2_100 add_adsorbate(subsys111, adsorbate, height=1 * lc, position=(pos111[0], pos111[1]), mol_index=0) add_adsorbate(subsys100, adsorbate, height=1 * lc, position=(pos100[0], pos100[1]), mol_index=0) subsys111.set_cell( [subsys111.cell[0], subsys111.cell[1], ([0., 0., cell_height])]) subsys100.set_cell( [subsys100.cell[0], subsys100.cell[1], ([0., 0., cell_height])]) #Create vectors with initial coordinates for each atom. Offsets from these coordinates are found after the DFT is run. x111_i = [] y111_i = [] z111_i = [] x100_i = [] y100_i = [] z100_i = [] for i in range(0, n): x111_i.append(subsys111[i].position[0]) y111_i.append(subsys111[i].position[1]) z111_i.append(subsys111[i].position[2]) x100_i.append(subsys100[i].position[0]) y100_i.append(subsys100[i].position[1]) z100_i.append(subsys100[i].position[2]) if relax: #A subsystem of the bulk is defined. This will be the basis of the DFT calculation. We also define relevant functions to #translate between bulk atom coordinates and the subsystem coordinates. ss = sizesetup(coverage) ucx = ss[0] ucy = ss[0] height = subsys_height n_adsorbates = ss[1] #Check if the requirement for orthogonal unit cell is met: if ss[0] % 2 == 0: orthogonal = True else: orthogonal = False size = [ucx, ucy, height ] #Size of the FCC bulk structure. Preferably size_z is odd. #Redefine subsystem for energy calculations: subsys111 = fcc111(symbol=metal, size=size, a=lc, vacuum=None, orthogonal=orthogonal) subsys100 = fcc100(symbol=metal, size=size, a=lc, vacuum=None, orthogonal=True) # Give all atoms in top layer a coordinate atomorigo = ucx * ucy * (height - 1) n = ucx * ucy * height subsys_coordinates = {} i = 0 j = 0 for q in range(atomorigo, n): if (i == ucx): j += 1 i = 0 subsys_coordinates[q] = [i, j, 0] i += 1 # Calculate system energies: energyfile = open( 'energies-%s-%s.txt' % (str(coverage), "butanethiolate_hollow"), 'w') energies = {} for i in ['adsorbate', 'subsys111', 'subsys100']: system = globals()[i].copy() if i == 'adsorbate': system.center(vacuum=5) system.set_pbc((1, 1, 0)) else: system.set_cell( [system.cell[0], system.cell[1], ([0., 0., cell_height])]) calc = GPAW(mode=PW(E_cut), kpts=kpoints, xc='BEEF-vdW', txt='energy-%s.txt' % i) system.set_calculator(calc) energy = system.get_potential_energy() energies[i] = energy #Now we have to find a set of basis vectors describing the subsystem surface. if ss[0] > 1: #For 111: v1_111 = subsys111[atomorigo + 1].position - subsys111[atomorigo].position v1_111 = np.array([v1_111[0], v1_111[1], 0]) v2_111 = subsys111[atomorigo + ss[0]].position - subsys111[atomorigo].position v2_111 = np.array([v2_111[0], v2_111[1], 0]) #For 100: v1_100 = subsys100[atomorigo + 1].position - subsys100[atomorigo].position v1_100 = np.array([v1_100[0], v1_100[1], 0]) v2_100 = subsys100[atomorigo + ss[0]].position - subsys100[atomorigo].position v2_100 = np.array([v2_100[0], v2_100[1], 0]) else: v1_111 = np.array([0, 0, 0]) v2_111 = np.array([0, 0, 0]) v1_100 = np.array([0, 0, 0]) v2_100 = np.array([0, 0, 0]) #Now we add adsorbates matching the coverage. They are added along the diagonal in the unit cell. if site == 'OnTop': position100 = [0, 0, 0] position111 = [0, 0, 0] else: position100 = 1 / 2 * v1_100 + 1 / 2 * v2_100 position111 = 1 / 2 * v1_111 + [0, 1 / 2 * v2_111[1], 0] zig = True #If zig is false, then the 111 adsorbate won't zag. zags = 0 #This is the number of zags that have been performed. adsorbate_atom_links = [ ] #This list will link adsorbates to an atom in the surface. for i in range(0, n_adsorbates): zig = not zig if zig and i > 1: zags = zags + 1 for j in adsorbate: j.tag = i scale = ss[0] / n_adsorbates adsorbate_atom_links.append([i * scale, i * scale]) #pos111=position111+((i*scale)/2)*v1_111+[0,i*scale*v2_111[1],0] pos111 = position111 + i * scale * v1_111 + i * scale * v2_111 - zags * v1_111 pos100 = position100 + i * scale * v1_100 + i * scale * v2_100 add_adsorbate(subsys111, adsorbate, height=1 * lc, position=(pos111[0], pos111[1]), mol_index=0) add_adsorbate(subsys100, adsorbate, height=1 * lc, position=(pos100[0], pos100[1]), mol_index=0) subsys111.set_cell( [subsys111.cell[0], subsys111.cell[1], ([0., 0., cell_height])]) #subsys110.set_cell([subsys110.cell[0],subsys110.cell[1],([0.,0.,cell_height])]) subsys100.set_cell( [subsys100.cell[0], subsys100.cell[1], ([0., 0., cell_height])]) #Create vectors with initial coordinates for each atom. Offsets from these coordinates are found after the DFT is run. x111_i = [] y111_i = [] z111_i = [] x100_i = [] y100_i = [] z100_i = [] for i in range(0, n): x111_i.append(subsys111[i].position[0]) y111_i.append(subsys111[i].position[1]) z111_i.append(subsys111[i].position[2]) x100_i.append(subsys100[i].position[0]) y100_i.append(subsys100[i].position[1]) z100_i.append(subsys100[i].position[2]) #The calculator is set. calc111 = GPAW(mode=PW(E_cut), kpts=kpoints, xc='BEEF-vdW', txt='calc111.out') subsys111.set_calculator(calc111) #The bottom layers of the subsystem are masked such that these atoms do not move during QuasiNewton minimization/relaxation. mask = [ i.tag > unmasked_layers and i.symbol == metal for i in subsys100 ] fixedatoms = FixAtoms(mask=mask) subsys111.set_constraint(fixedatoms) #The subsystem is relaxed until all forces on atoms are below fmax=0.02. relax = QuasiNewton(subsys111, trajectory='relax111.traj') relax.run(fmax=0.02) #The calculator is set. calc100 = GPAW(mode=PW(E_cut), kpts=kpoints, xc='BEEF-vdW', txt='calc100.out') subsys100.set_calculator(calc100) #The bottom layer of the subsystem is masked such that these atoms do not move during QuasiNewton minimization/relaxation. subsys100.set_constraint(fixedatoms) #The subsystem is relaxed until all forces on atoms are below fmax=0.02. relax = QuasiNewton(subsys100, trajectory='relax100.traj') relax.run(fmax=0.02) ## Calculate new energies for i in ['subsys111', 'subsys100']: system = globals()[i] energy = system.get_potential_energy() energies["relax" + i[6:]] = energy e_bond = {} e_bond['subsys111'] = energies['relax111'] - energies[ 'subsys111'] - n_adsorbates * energies['adsorbate'] e_bond['subsys100'] = energies['relax100'] - energies[ 'subsys100'] - n_adsorbates * energies['adsorbate'] print(energies, e_bond, file=energyfile) energyfile.close() ads111 = e_bond['subsys111'] ads100 = e_bond['subsys100'] if subsys100_relaxed != None: subsys100 = subsys100_relaxed if subsys111_relaxed != None: subsys111 = subsys111_relaxed #Check if adsorption has occurred. If not, remove adsorbates from corresponding subsystem. if ads111 >= ads_cutoff: subsys111_prov = subsys111.copy() subsys111 = Atoms() for i in subsys111_prov: if i.symbol == metal: subsys111.append(i) if ads100 >= ads_cutoff: subsys100_prov = subsys100.copy() subsys100 = Atoms() for i in subsys100_prov: if i.symbol == metal: subsys100.append(i) #Now to find offsets for each atom in the subsystems after DFT: x111_offset = [] y111_offset = [] z111_offset = [] x100_offset = [] y100_offset = [] z100_offset = [] for i in range(0, n): x111_offset.append(subsys111[i].position[0] - x111_i[i]) y111_offset.append(subsys111[i].position[1] - y111_i[i]) z111_offset.append(subsys111[i].position[2] - z111_i[i]) x100_offset.append(subsys100[i].position[0] - x100_i[i]) y100_offset.append(subsys100[i].position[1] - y100_i[i]) z100_offset.append(subsys100[i].position[2] - z100_i[i]) # Define dictionary of indexed surfaces for use in TEM indexed_surfaces = {} #Sadly, we now have to do all the rotations again: surface_coordinates = { } #For convenience this dictionary will contain v1,v2 coordinates for all the surfaces - indexed by their #respective origo atom's index. for i in Nanoparticle.get_surfaces(): surface = [ ] #This list will contain the atoms on the specific surface at the top. Nanoparticle.rotate(i, [0, 1, 0], center="COU") y_max = 0 for j in Nanoparticle: if j.position[1] > y_max and j.symbol == metal: y_max = j.position[1] for j in Nanoparticle: if round(j.position[1], 2) == round(y_max, 2) and j.symbol == metal: surface.append(j.index) #Now surface contains the indeces of atoms of the specific surface that is at the top in this rotation. direction = abs(i[0]) + abs(i[1]) + abs( i[2] ) #This number determines the surface direction family - 100, 110, 111. #Define a dictionary with all indeces of atoms of surface i indexed_surfaces[tuple(i)] = surface #Find maximum z and x values for the surface: x_max = 0 z_max = 0 for k in surface: if Nanoparticle[k].position[0] > x_max: x_max = Nanoparticle[k].position[0] if Nanoparticle[k].position[2] > z_max: z_max = Nanoparticle[k].position[2] x_min = x_max z_min = z_max for k in surface: if Nanoparticle[k].position[0] < x_min: x_min = Nanoparticle[k].position[0] if Nanoparticle[k].position[2] < z_min: z_min = Nanoparticle[k].position[2] bot_row = [ ] #This will contain the indeces of the bottom (low z) row of atoms. #Find the atoms in the bottom row: for k in surface: if round(Nanoparticle[k].position[2], 1) == round(z_min, 1): bot_row.append(k) #Find the atom in the corner of the surface: corner_atom = bot_row[0] for k in bot_row: if Nanoparticle[k].position[0] < Nanoparticle[ corner_atom].position[0]: corner_atom = k distance_1 = 2 * lc #The distance between corner_atom and the nearest neighbour is at least smaller than this. neighbour_1 = "d" #placeholder for neighbour 1. neighbour_2 = "d" #placeholder for neighbour 2. ## Find the unit cell neighbours to corner_atom. v1 = [] v2 = [] for k in surface: if k != corner_atom and k in edge_atoms: #The v1-axis should lie along some edge. if np.linalg.norm( Nanoparticle[k].position - Nanoparticle[corner_atom].position) < distance_1: distance_1 = np.linalg.norm( Nanoparticle[k].position - Nanoparticle[corner_atom].position) neighbour_1 = k #Construct the first basis vector for the surface using the first nearest neighbour coordinate. v1 = Nanoparticle[neighbour_1].position - Nanoparticle[ corner_atom].position v1[1] = 0 #The y-coordinate of the surface basis vector is set to 0. # To find the second neighbour, we have to align the v1 vector with the x-axis: Nanoparticle.rotate(v1, [1, 0, 0], center='COU') for k in surface: if k != corner_atom and k != neighbour_1: dist_vector = Nanoparticle[k].position - Nanoparticle[ corner_atom].position # We require that the angle between dist_vector and v1 is <=90. if math.acos( round( np.dot(dist_vector, v1) / (np.linalg.norm(dist_vector) * np.linalg.norm(v1)), 5)) <= 90: # We check for a dist_vector which corresponds to one of the lattice vectors defined for the subsystem. if direction == 1: if round(dist_vector[0], 5) == round(v2_100[0], 5) and round( dist_vector[2], 5) == round(v2_100[1], 5): neighbour_2 = k if round(dist_vector[0], 5) == round( v2_100[0], 5) and round( dist_vector[2], 5) == -round(v2_100[1], 5): neighbour_2 = k if direction == 3: if round(dist_vector[0], 5) == round(v2_111[0], 5) and round( dist_vector[2], 5) == round(v2_111[1], 5): neighbour_2 = k if round(dist_vector[0], 5) == round( v2_111[0], 5) and round( dist_vector[2], 5) == -round(v2_111[1], 5): neighbour_2 = k # Rotate the system back after finding the second neighbour. Nanoparticle.rotate([1, 0, 0], v1, center='COU') #Construct the second basis vector for the surface using the nearest neighbour coordinate. v2 = Nanoparticle[neighbour_2].position - Nanoparticle[ corner_atom].position v2[1] = 0 #The y-coordinate of the surface basis vector is set to 0. Transform_matrix = np.linalg.inv( np.array([ v1, v2, [0, 1, 0] ]).transpose()) #This transforms x-y-z coordinates to v1-v2-y. #Now to find v1,v2-coordinates for all the atoms in the surface and replace them with atoms from DFT subsystem accordingly: surface.sort for k in surface: if k not in edge_atoms: flag = False #Flag to determine wether the z-axis was flipped. #Find the coordinate of the atom in v1,v2-basis. coordinate = np.round( Transform_matrix.dot(Nanoparticle[k].position - Nanoparticle[corner_atom].position), 0) #We want the origo of the surface system to be off the surface edge. Therefore, we translate the coordinates: coordinate[0] = coordinate[0] - 1 coordinate[1] = coordinate[1] - 1 reference = coordreference( coordinate[0], coordinate[1], ucx, ucy, direction ) #This references the matching atom in the subsystem #The system is rotated again such that the bottom row of atoms lie along the x-direction. Nanoparticle.rotate(v1, [1, 0, 0], center="COU") #Check if v2 is in the positive z-direction. If not, rotate the system 180 degrees: if Nanoparticle[neighbour_2].position[2] - Nanoparticle[ corner_atom].position[2] < 0: Nanoparticle.rotate([0, 0, -1], [0, 0, 1], center='COU') flag = True #Flag is set indicating the system has been flipped. for l in subsys_coordinates: if subsys_coordinates[l] == [ reference[0], reference[1], 0 ]: #This atom in the subsystem matches the atom in the Nanoparticle surface. if direction == 1: #Apply the corresponding offset from the 100 list: Nanoparticle[ k].position = Nanoparticle[k].position + [ x100_offset[l], z100_offset[l], y100_offset[l] ] if direction == 3: #Apply the corresponding offset from the 111 list: Nanoparticle[ k].position = Nanoparticle[k].position + [ x111_offset[l], z111_offset[l], y111_offset[l] ] if [ reference[0], reference[1] ] in adsorbate_atom_links: #Checks if the subsystem reference atom is linked to an adsorbate molecule. for u in range( 0, len(adsorbate_atom_links) ): #Find the linked adsorbate molecule tag (it's the index of adsorbate_atom_links) if adsorbate_atom_links[u] == [ reference[0], reference[1] ]: #Check if the reference coordinates exists in the list of linked atoms coordinates. adsorbate_tag = u #This is the tag assigned to the adsorbate that is linked to the reference atom. #Calculate the reference atom's index in the subsystem (from the v1/v2-coordinates). tagged_atom_index = int( atomorigo + ((adsorbate_atom_links[adsorbate_tag][1]) % ucy) * ucx + adsorbate_atom_links[adsorbate_tag][0] % ucx) if direction == 1: for m in subsys100: if m.symbol != metal and m.tag == adsorbate_tag: #Single out the adsorbate with the correct tag. m_old_position = m.position.copy( ) #Save the adsorbate's original position. m.position = m.position - subsys100[ tagged_atom_index].position #Calculate the vector from the reference atom to the adsorbate. #Now calculate and set the position to that which the adsorbate atom should have in the Nanoparticle system: m.position = [ m.position[0] + Nanoparticle[k].position[0], m.position[2] + Nanoparticle[k].position[1], m.position[1] + Nanoparticle[k].position[2] ] Nanoparticle.append( m) #Finally, add the adsorbate. m.position = m_old_position #Reset the subsystem (set the adsorbate position to it's old, saved value). if direction == 3: #Do exactly the same below as for the 100 surface above: for m in subsys111: if m.symbol != metal and m.tag == adsorbate_tag: m_old_position = m.position.copy() m.position = m.position - subsys111[ tagged_atom_index].position m.position = [ m.position[0] + Nanoparticle[k].position[0], m.position[2] + Nanoparticle[k].position[1], m.position[1] + Nanoparticle[k].position[2] ] Nanoparticle.append(m) m.position = m_old_position #Check if the z-axis was flipped. If it was, flip it back: if flag: Nanoparticle.rotate([0, 0, 1], [0, 0, -1], center='COU') #The system is then rotated back. Nanoparticle.rotate([1, 0, 0], v1, center="COU") #First the x-axis alignment. Nanoparticle.rotate( [0, 1, 0], i, center="COU" ) #Now rotate the surface back to it's original direction. ## Find the actual coverage of the Nanoparticle: ## This procedure is outdated, but it still works. #First we need to count the number of surface atoms (including edges). First we turn surf_atoms into a dictionary and back into # a list in order to remove duplicate values (edge atoms should only be counted once): surf_atoms = list(dict.fromkeys(surf_atoms)) #The number of surf atoms is then: n_surf_atoms = len(surf_atoms) #Now to count the number of adsorbed molecules. First we count the number of non-gold atoms: non_gold_atoms = 0 for i in Nanoparticle: if i.symbol != metal: non_gold_atoms += 1 #Then we count the number of atoms in the adsorbate molecules: adsorbate_size = len(adsorbate) #The number of adsorbed molecules: n_adsorbate = non_gold_atoms / adsorbate_size #The actual coverage is then: actual_coverage = n_adsorbate / n_surf_atoms Nanoparticle.center(vacuum=4) #Center the nanoparticle for TEM imaging. return Nanoparticle, indexed_surfaces, subsys111, subsys100 #Depending on what is needed, return the different objects.
class MC: basis_factor = 0.5 int_basis = np.array([[0, 1, 1], [1, 0, 1], [1, 1, 0]]) # последняя ось печатается слева направо, # предпоследняя — сверху вниз, # и оставшиеся — также сверху вниз, разделяя пустой строкй. # convolution with this array yields number of neigbors for FCC neib_matrix = np.array([ [[0, 0, 0], [0, 1, 1], [0, 1, 0]], [[0, 1, 1], [1, 0, 1], [1, 1, 0]], [[0, 1, 0], [1, 1, 0], [0, 0, 0]] ]) def __init__(self, log='-', chems=[]): self.moves = [] # available move types self.moves_weight = [] # some random moves are more random self.move = None # current move self.nsteps = {} # dict-counter self.naccept = {} # dict-counter self.chems = chems # chemical symbols self.CNs = [] # coordination numbers self.E = 1e32 # potential energy per atom # a-la-Lagrange coefficients (weights) self.penalty_weight_CN = 1000 self.penalty_weight_E = 1 # self.penalty_weight_X = 100 # Concentration contrib. self.penalty_weight_S = 200 # Surface atom type penalty for a1 in self.chems: for a2 in self.chems: self.CNs.append(0) if isinstance(log, str): if log == '-': self.logfile = sys.stdout else: self.logfile = open(log, 'a') else: self.logfile = None def init_grid(self, GRID_SIZE): self.L = int(GRID_SIZE) self.GRID = np.zeros((self.L, self.L, self.L)) self.NEIB = np.zeros((self.L, self.L, self.L)) def set_targets(self, target_CNs, target_conc=[1], temperature=1000, surface_atom_type = 0): self.temp = temperature self.surface_atom_type = surface_atom_type self.targetCNs = np.array(target_CNs) # linked? #self.CNs = target_CNs[:] # make a copy self.target_conc = target_conc if self.logfile is not None: self.logfile.write('='*20 + ' Targets ' + '='*20+'\n') self.logfile.write('Temperature %f\n' % self.temp) self.logfile.write('Coordination numbers:\n') i = 0 for B in self.chems: for A in self.chems: self.logfile.write(' CN[%i-%i] = %f\n' % (A, B, self.targetCNs[i])) i += 1 self.logfile.write('Concentrations:\n') i = 0 for B in self.chems: self.logfile.write(' conc[%i] = %f\n' % (B, self.target_conc[i])) i += 1 if (self.penalty_weight_X>0)and(len(self.chems)>0): self.logfile.write('Energy: ASAP3.EMT per atom -> minimum\n') if (self.surface_atom_type > 0)and(len(self.chems)>0): self.logfile.write('Surface atom type: %i\n'%self.surface_atom_type) self.logfile.write('='*49+'\n') self.logfile.flush() def attach_move(self, move, weight=1.0): if not hasattr(move, '__call__'): raise ValueError("Attached move is not callable.") #if hasattr(move, 'set_atoms'): # move.set_atoms(self.atoms) #if hasattr(move, 'set_optimizer'): # move.set_optimizer(self) self.moves.append(move) self.moves_weight.append(weight) #~ print('Weights: ',self.moves_weight) self.nsteps[move.get_name()] = 0 self.naccept[move.get_name()] = 0 def random_move(self): self.calc_neighbors() # choose move: #return self.moves[(np.random.uniform() < self.moves_weight).argmax()] self.move = self.weightedChoice(self.moves, self.moves_weight) # setup move: if isinstance(self.move, MoveChange): found = False while not found: # find non-emty position n1, n2, n3 = np.random.random_integers(0, self.L-1, 3) found = self.GRID[n1, n2, n3] > 0 # find chemical element to swap B = filter(lambda A: A != self.GRID[n1, n2, n3], self.chems)[0] self.move.setup(self.GRID, n1, n2, n3, B) elif isinstance(self.move, MoveShuffle): found = False while not found: # find non-empty position n1, n2, n3 = np.random.random_integers(0, self.L-1, 3) found = (self.GRID[n1, n2, n3] > 0) #TODO: check if selected atom is not unique found = False while not found: # find non-empty position m1, m2, m3 = np.random.random_integers(0, self.L-1, 3) found = (self.GRID[m1, m2, m3] > 0) and (self.GRID[m1, m2, m3] != self.GRID[n1, n2, n3]) self.move.setup(self.GRID, n1, n2, n3, m1, m2, m3) elif isinstance(self.move, MoveDestroy): found = False while not found: # find non-empty position n1, n2, n3 = np.random.random_integers(0, self.L-1, 3) found = self.GRID[n1, n2, n3] > 0 self.move.setup(self.GRID, n1, n2, n3) elif isinstance(self.move, MoveCreate): found = False while not found: # find empty position bounded to non-empty n1, n2, n3 = np.random.random_integers(0, self.L-1, 3) found = (self.GRID[n1, n2, n3] == 0)and(self.NEIB[n1, n2, n3] > 0) A = self.weightedChoice(self.chems, self.target_conc) self.move.setup(self.GRID, n1, n2, n3, A) return self.move #print [np.random.uniform() < self.moves_weight].any() def get_atoms(self): if True: #self.atoms == None: self.atoms = Atoms() self.atoms.set_cell( self.int_basis*self.basis_factor*self.a*self.L ) for n1 in xrange(self.L): for n2 in xrange(self.L): for n3 in xrange(self.L): A = self.GRID[n1, n2, n3] if (A > 0): pos = np.empty(3) #for i in range(3): pos[0] = 0.5*self.a*(n2+n3) pos[1] = 0.5*self.a*(n1+n3) pos[2] = 0.5*self.a*(n1+n2) atom = Atom(A, position=pos) self.atoms.append(atom) return self.atoms def set_atoms(self, atoms, margin = 5): """ set atoms position as initial values of GRID This function will alter the size of the GRID atoms - ASE.Atoms object with atomic system margin - the extra space from the borders of GRID array Example: mc = MC() atoms = read('initial.cube') mc.set_lattice_constant(2.772*sqrt(2)) mc.set_atoms(atoms, margin=1) from ase.visualize import view view(mc.get_atoms())""" x = atoms.positions[:,0] y = atoms.positions[:,1] z = atoms.positions[:,2] n1 = np.round(1/self.a*(-x + y + z )) n2 = np.round(1/self.a*( x - y + z )) n3 = np.round(1/self.a*( x + y - z )) # change GRID array size to fit all the data + margin space min1 = n1.min() min2 = n2.min() min3 = n3.min() max1 = n1.max() max2 = n2.max() max3 = n3.max() L = max(max1-min1, max2-min2, max3-min3) + 1 # +1 is required for correct treatment of margin=0 case L += 2*margin print('L = %i\n' % L) self.init_grid( L ) for i_atom in xrange(len(atoms)): in1 = margin + n1[i_atom] - min1 in2 = margin + n2[i_atom] - min2 in3 = margin + n3[i_atom] - min3 self.GRID[in1, in2, in3] = atoms[i_atom].number if not(atoms[i_atom].number in self.chems): self.chems.append(atoms[i_atom].number) print('WARNING: Added atom with Z=%i'%atoms[i_atom].number) return L def calc_neighbors(self): """ To fill array of neighbors numbers. 3x3x3 array neib_matrix is specific for FCC structure""" #self.NEIB = ndimage.convolve((self.GRID>1), self.neib_matrix, mode='constant', cval=0.0) self.NEIB = convolve(1*(self.GRID>1), self.neib_matrix, mode='same') return self.NEIB def get_N(self, chem=-1): """ Number of atoms. If chem == -1 returns total number of atoms """ if chem > 0: return (self.GRID == chem).sum() else: return (self.GRID > 0).sum() def calc_CNs(self): """ To fill coordination numbers. Should be called after calc_neighbors""" # total CN: #self.CNs[0] = (self.NEIB[(self.NEIB>0) & (self.GRID>0)]).sum() #self.CNs[0] = self.CNs[0] * 1.0 / self.get_N() # partial CN: i = 0 for B in self.chems: NEIB_AB = convolve(1*(self.GRID==B), self.neib_matrix, mode='same') for A in self.chems: # calc number of B around A self.CNs[i] = (NEIB_AB[(self.NEIB>0) & (self.GRID==A)]).sum() self.CNs[i] = self.CNs[i] * 1.0 / self.get_N(A) i += 1 #print nnn #print sum(nnn)*1.0 / len(atoms) def calc_conc(self): if len(self.chems) == 1: return 1 # only one chemical element N = (self.GRID > 0).sum() N_A = (self.GRID == self.chems[0]).sum() N_B = (self.GRID == self.chems[1]).sum() return [1.0*N_A/N, 1.0*N_B/N] def calc_energy(self): atoms = self.get_atoms() atoms.set_calculator(EMT()) self.E = atoms.get_potential_energy() #/ self.get_N() # energy per atom! return self.E def run(self, nsteps=10): """ Run Monte-Carlo simulation for nsteps moves """ for step in xrange(nsteps): move = self.random_move() if self.logfile is not None: #self.logfile.write('* Move: %s \t' % move.get_name()) #self.logfile.write(' Pos.: ['+str(move.n1)+','+str(move.n2)+','+str(move.n3)+'] \t') self.logfile.write('* '+str(move)) # perform and evaluate move if self.evaluate_move(): self.accept_move() if self.logfile is not None: self.logfile.write(' Acc!\n') else: self.reject_move() if self.logfile is not None: self.logfile.write(' Rej.\n') self.log_stats() # show stats #if self.logfile is not None: # self.logfile.write( # '* Move accepted: %7i / %7i \t Total accepted: %7i / %7i\n' % # (self.get_naccept(move.get_name()), self.get_nsteps(move.get_name()), # self.get_naccept(), self.get_nsteps() ) # ) def log_stats(self): if self.logfile is not None: self.logfile.write('='*60) self.logfile.write('\n%-13s %-15s %-15s\n' % ('Move', 'Steps', 'Accepts')) self.logfile.write('-' * 60 + '\n') for m in self.moves: name = m.get_name() ns = self.get_nsteps(name) fs = 1.0 * ns / self.get_nsteps() na = self.get_naccept(name) if ns != 0: fa = 1.0 * na / ns else: fa = -1 self.logfile.write('%-13s %-7i (%5.3f) %-7i (%5.3f)\n' % (name, ns, fs, na, fa)) self.logfile.write('-' * 60 + '\n') ns = self.get_nsteps() na = self.get_naccept() self.logfile.write('%-13s %-7i (%5.3f) %-7i (%5.3f)\n' % ('Total', ns, 1.0, na, 1.0 * na / ns)) self.logfile.write('=' * 60 + '\n') self.logfile.write('Target CN : '+str(self.targetCNs) +'\nAchieved CN: '+str(self.CNs) +'\nTarget conc: '+str(self.target_conc) +'\nAchieved conc: '+str(self.calc_conc()) +'\nPenalty function: '+str(self.penalty_function()) +'\n') self.logfile.write('Natoms = %i\n' % self.get_N()) self.logfile.write('='*60+'\n') # for comparison with list version #~ from qsar import QSAR #~ q = QSAR(self.get_atoms()) #~ q.monoatomic() #~ self.logfile.write('qsar> N = %i\n' % q.N ) #~ self.logfile.write('qsar> CN = %f\n' % q.CN) self.logfile.flush() #else: # raise "Called log stats without logfile setted" def clear_stats(self): for key in self.nsteps: self.naccept[key] = 0 self.nsteps[key] = 0 def set_lattice_constant(self, lattice_constant): self.a = lattice_constant def penalty_function(self): self.calc_neighbors() self.calc_CNs() #E = self.calc_energy() #result = wCN * sum((np.array(self.targetCNs)-np.array(self.CNs))**2) # result = wCN * sum((self.targetCNs-self.CNs)**2) # -- last working version. Changed to treat skipping result = 0 if self.penalty_weight_CN > 0: for i in range(len(self.targetCNs)): if self.targetCNs[i] > 0: result += (self.targetCNs[i]-self.CNs[i])**2 result *= self.penalty_weight_CN if (self.penalty_weight_E > 0): result += self.penalty_weight_E * self.calc_energy() if (self.penalty_weight_X > 0)and(len(self.chems)>1): curr_conc = self.calc_conc(); #print(N, N_A, N_B) result += self.penalty_weight_X * ( (self.target_conc[0] - curr_conc[0])**2 + (self.target_conc[1] - curr_conc[1])**2 ) if (self.penalty_weight_S > 0)and(self.surface_atom_type>0): # calc surface atoms ratio A = self.surface_atom_type NA_surf = (self.NEIB[(self.NEIB<12) & (self.GRID==A)]>0).sum() # number of surface atoms type A N_surf = (self.NEIB[(self.NEIB<12) & (self.GRID>0)]>0).sum() # number of all surface atoms print('[ Surface: %i/%i ]'%(NA_surf, N_surf)) result += self.penalty_weight_S * (1-NA_surf/N_surf) return result def evaluate_move(self): #oldCNs = self.CNs[:] Eold = self.penalty_function() #if self.logfile is not None: # self.logfile.write('\nOld: E %f \t CN %f \t Energy %f' % (Eold, self.CNs[0], self.E)) self.move() newCNs = self.CNs Enew = self.penalty_function() #if self.logfile is not None: # self.logfile.write('New: E %f \t CN %f \t Energy %f\n' % (Enew, self.CNs[0], self.E)) if Enew < Eold: if self.logfile is not None: self.logfile.write(' P: 1+ \t') return True else: prob = np.exp( (Eold - Enew) / (self.temp * kB)) if self.logfile is not None: self.logfile.write(' P: %.3f\t' % prob) return prob > np.random.uniform() def accept_move(self): self.move.accept() self.naccept[self.move.get_name()] += 1 self.nsteps[self.move.get_name()] += 1 def reject_move(self): self.move.reject() self.nsteps[self.move.get_name()] += 1 def get_nsteps(self, name='total'): if name == 'total': return sum(self.nsteps.values()) else: return self.nsteps[name] def get_naccept(self, name='total'): if name == 'total': return sum(self.naccept.values()) else: return self.naccept[name] def weightedChoice(self, objects, weights): """Return a random item from objects, with the weighting defined by weights (which must sum to 1). http://stackoverflow.com/questions/10803135/weighted-choice-short-and-simple""" # should be replaced by np.random.choice() # awaliable in newer versions of numpy.. cs = np.cumsum(weights) # An array of the weights, cumulatively summed. idx = sum(cs < np.random.uniform()) # Find the index of the first weight over a random value. return objects[idx]
from ase import Atoms, Atom from ase.io import write from ase.data.colors import jmol_colors a = 4.0614 b = a / sqrt(2) h = b / 2 atoms = Atoms('Al2', positions=[(0, 0, 0), (a / 2, b / 2, -h)], cell=(a, b, 2 * h), pbc=(1, 1, 0)) atoms *= (2, 2, 2) atoms.append(Atom('Al', (a / 2, b / 2, 3 * h))) atoms.center(vacuum=4., axis=2) atoms *= (2, 3, 1) atoms.cell /= [2, 3, 1] rotation = '-60x, 10y' radii = 1.2 # single float specifies a uniform scaling of the covalent radii colors = jmol_colors[atoms.numbers] colors[16::17] = [1, 0, 0] write('Al110slab.pov', atoms, rotation=rotation, colors=colors, radii=radii, show_unit_cell=2,
class AbstractMolecularEnvironment(gym.Env, abc.ABC): def __init__( self, reward: InteractionReward, observation_space: ObservationSpace, action_space: ActionSpace, bag_refills: int, initial_formula, min_atomic_distance=0.6, # Angstrom max_h_distance=2.0, # Angstrom min_reward=-0.6, # Hartree ): self.reward = reward self.observation_space = observation_space self.action_space = action_space self.random_state = np.random.RandomState() self.min_atomic_distance = min_atomic_distance self.max_h_distance = max_h_distance self.min_reward = min_reward self.current_atoms = Atoms() self.current_formula = ase.formula.Formula() self.bag_refills = bag_refills self.initial_formula = initial_formula # Negative reward should be on the same order of magnitude as the positive ones. # Memory agent on QM9: mean 0.26, std 0.13, min -0.54, max 1.23 (negative reward indeed possible # but avoidable and probably due to PM6) @abc.abstractmethod def reset(self) -> ObservationType: raise NotImplementedError def step(self, action: ActionType) -> Tuple[ObservationType, float, bool, dict]: new_atom = self.action_space.to_atom(action) done = new_atom.symbol == NULL_SYMBOL if done: return self.observation_space.build(self.current_atoms, self.current_formula), 0.0, done, {} if not self._is_valid_action(current_atoms=self.current_atoms, new_atom=new_atom): return ( self.observation_space.build(self.current_atoms, self.current_formula), self.min_reward, True, {}, ) reward, info = self.reward.calculate(self.current_atoms, new_atom, self.min_reward, len(self.current_formula), self.bag_refills) if reward < self.min_reward: done = True reward = self.min_reward self.current_atoms.append(new_atom) self.current_formula = util.remove_from_formula(self.current_formula, new_atom.symbol) if len(self.current_formula) == 0 and self.bag_refills > 0: self.current_formula = self.initial_formula[-1] self.bag_refills -= 1 # Check if state is terminal if self._is_terminal(): done = True return self.observation_space.build(self.current_atoms, self.current_formula), reward, done, info def _is_terminal(self) -> bool: return len(self.current_atoms) == self.observation_space.canvas_space.size or len(self.current_formula) == 0 def _is_valid_action(self, current_atoms: Atoms, new_atom: Atom) -> bool: if self._is_too_close(current_atoms, new_atom): return False return self._all_h_covered(current_atoms, new_atom) def _is_too_close(self, existing_atoms: Atoms, new_atom: Atom) -> bool: # Check distances between new and old atoms for existing_atom in existing_atoms: if np.linalg.norm(existing_atom.position - new_atom.position) < self.min_atomic_distance: return True return False def _all_h_covered(self, existing_atoms: Atoms, new_atom: Atom) -> bool: # Ensure that H atoms are not too far away from the nearest heavy atom if len(existing_atoms) == 0 or new_atom.symbol != 'H': return True for existing_atom in existing_atoms: if existing_atom.symbol == 'H': continue distance = np.linalg.norm(existing_atom.position - new_atom.position) if distance < self.max_h_distance: return True return False def render(self, mode='human'): pass def seed(self, seed=None) -> int: seed = seed or np.random.randint(int(1e5)) self.random_state = np.random.RandomState(seed) return seed
co[0].number = 1 with must_raise(ValueError): t.write(co) co[0].number = 6 co.pbc = True with must_raise(ValueError): t.write(co) co.pbc = False o = co.pop(1) with must_raise(ValueError): t.write(co) co.append(o) t.write(co) # append to a nonexisting file: fname = '2.traj' if os.path.isfile(fname): os.remove(fname) t = Trajectory(fname, 'a', co) os.remove(fname) t = Trajectory('empty.traj', 'w') t.close() assert os.path.getsize('empty.traj') == 0 t = Trajectory('fake.traj', 'w') t.write(Atoms('H'), energy=-42.0, forces=[[1, 2, 3]])
'Reorder atoms that QM atoms at the beginning and MM atoms at the end' 'create atoms object to save new data' ordered_atoms = Atoms('Au', positions=[[0, 0, 0]]) with open("dir_data.json", "r") as read_file: data = json.load(read_file) atoms = io.read('input.xyz') 'new atoms numbers' d = 0 new_qm = [] for i in range(0,len(atoms)): if i in data['qm_region']: ordered_atoms.append(atoms[i]) new_qm.append(d) d += 1 for i in range(0,len(atoms)): if i not in data['qm_region']: ordered_atoms.append(atoms[i]) d += 1 data['qm_region'] = new_qm with open("dir_data_qmmm_order.json", "w") as write_file: json.dump(data, write_file, indent=4) 'del Au atom' del ordered_atoms[0]
for rot in [20, 200]: new = org.copy() new.translate(-new.get_center_of_mass()) new.rotate(axis, np.pi * rot / 180) dist = distance(org, new, True) dist2 = distance(org, new, False) print('rotation', axis, ', angle', rot, '-> distance', dist) assert dist < maxdist assert dist == dist2 if 0: # reflect new = Atoms() cm = org.get_center_of_mass() for a in org: new.append(Atom(a.symbol, -(a.position - cm))) dist = distance(org, new) print('reflected -> distance', dist) # permute for i, a in enumerate(org): if i < 3: a.symbol = 'H' if hasattr(itertools, 'permutations'): for indxs in itertools.permutations(range(3)): new = org.copy() for c in range(3): new[c].position = org[indxs[c]].position dist = distance(org, new) print('permutation', indxs, '-> distance', dist)
def CCP_Oct_Tet(ind1, ind2, Optimizer): """CX Function underdevelopment for preferentially exchanging octahedral and tetrahedral defect sites Non-Functional """ if 'CX' in Optimizer.debug: debug = True else: debug = False uc = numpy.maximum.reduce(Optimizer.solidbulk.get_cell()/Optimizer.supercell)[0] interstitials = [[0.0,0.5,0.0],[0.5,0.0,0.0],[0.0,0.0,0.5],[0.5,0.5,0.5], [0.25,0.25,0.25],[0.25,0.75,0.25],[0.75,0.25,0.25],[0.75,0.75,0.25], [0.25,0.25,0.75],[0.25,0.75,0.75],[0.75,0.25,0.75],[0.75,0.75,0.75]] sites=[] for one in interstitials: pos = [uc*p for p in one] npos = [0,0,0] for i in range(Optimizer.supercell[0]): npos[0]=pos[0]+i*uc for j in range(Optimizer.supercell[1]): npos[1]=pos[1]+j*uc for k in range(Optimizer.supercell[2]): npos[2]=pos[2]+k*uc sites.append(copy.copy(npos)) solidsites = Atoms() for one in sites: solidsites.append(Atom(position=one)) return ind1, ind2
def check_min_dist(totalsol, type='Defect', nat=None, min_len=0.7, STR=''): if type=='Defect' or type=='Crystal' or type=='Surface': if nat==None: nat=len(totalsol) cutoffs=[2.0 for one in totalsol] nl=NeighborList(cutoffs,bothways=True,self_interaction=False) nl.update(totalsol) for one in totalsol[0:nat]: nbatoms=Atoms() nbatoms.append(one) indices, offsets=nl.get_neighbors(one.index) for index, d in zip(indices,offsets): index = int(index) sym=totalsol[index].symbol pos=totalsol[index].position + numpy.dot(d,totalsol.get_cell()) at=Atom(symbol=sym,position=pos) nbatoms.append(at) while True: dflag=False for i in range(1,len(nbatoms)): d=nbatoms.get_distance(0,i) if d < min_len: nbatoms.set_distance(0,i,min_len+.01,fix=0.5) STR+='--- WARNING: Atoms too close (<0.7A) - Implement Move ---\n' dflag=True if dflag==False: break for i in range(len(indices)): totalsol[indices[i]].position=nbatoms[i+1].position totalsol[one.index].position=nbatoms[0].position nl.update(totalsol) elif type=='Cluster': for i in range(len(totalsol)): for j in range(len(totalsol)): if i != j: d=totalsol.get_distance(i,j) if d < min_len: totalsol.set_distance(i,j,min_len,fix=0.5) STR+='--- WARNING: Atoms too close (<0.7A) - Implement Move ---\n' else: print 'WARNING: In Check_Min_Dist in EvalEnergy: Structure Type not recognized' return totalsol, STR
def fcc100(symbol, a, layers, L): """Build an fcc(100) surface Parameters ---------- symbol: string Chemical symbol ('H', 'Li', ...). a: float Lattice constant. layers: int Number of layers. L: float Height of unit cell. """ # Distance between atoms: d = a / sqrt(2) # Distance between layers: z = a / 2. assert L > layers * z, 'Unit cell too small!' # Start with an empty Atoms object: atoms = Atoms(cell=(d, d, L), pbc=(True, True, False)) # Fill in the atoms: for n in range(layers): position = [d / 2 * (n % 2), d / 2 * (n % 2), n * z] atoms.append(Atom(symbol, position)) atoms.center(axis=2) return atoms
def obmol_to_atoms(mol, return_bonds=False): """Convert an OBMol object to an Atoms object. Parameters ========== mol: OBMol return_bonds: bool If True, a list of list of 3xint describing the bonds will be returned. """ atoms = Atoms() for i in range(mol.NumAtoms()): obatom = mol.GetAtom(i + 1) atoms.append(Atom(obatom.GetAtomicNum(), [obatom.GetX(), obatom.GetY(), obatom.GetZ()] ) ) if return_bonds: return atoms, get_bonds(mol) else: return atoms
def read_aims_output(fd, index=-1): """Import FHI-aims output files with all data available, i.e. relaxations, MD information, force information etc etc etc.""" from ase import Atoms, Atom from ase.calculators.singlepoint import SinglePointCalculator from ase.constraints import FixAtoms, FixCartesian molecular_dynamics = False cell = [] images = [] fix = [] fix_cart = [] f = None pbc = False found_aims_calculator = False stress = None for line in fd: # if "List of parameters used to initialize the calculator:" in line: # next(fd) # calc = read_aims_calculator(fd) # calc.out = filename # found_aims_calculator = True if "| Number of atoms :" in line: inp = line.split() n_atoms = int(inp[5]) if "| Unit cell:" in line: if not pbc: pbc = True for i in range(3): inp = next(fd).split() cell.append([inp[1], inp[2], inp[3]]) if "Found relaxation constraint for atom" in line: xyz = [0, 0, 0] ind = int(line.split()[5][:-1]) - 1 if "All coordinates fixed" in line: if ind not in fix: fix.append(ind) if "coordinate fixed" in line: coord = line.split()[6] if coord == "x": xyz[0] = 1 elif coord == "y": xyz[1] = 1 elif coord == "z": xyz[2] = 1 keep = True for n, c in enumerate(fix_cart): if ind == c.a: keep = False if keep: fix_cart.append(FixCartesian(ind, xyz)) else: fix_cart[n].mask[xyz.index(1)] = 0 if "Atomic structure:" in line and not molecular_dynamics: next(fd) atoms = Atoms() for _ in range(n_atoms): inp = next(fd).split() atoms.append(Atom(inp[3], (inp[4], inp[5], inp[6]))) if "Complete information for previous time-step:" in line: molecular_dynamics = True if "Updated atomic structure:" in line and not molecular_dynamics: atoms = _parse_atoms(fd, n_atoms=n_atoms) elif "Atomic structure (and velocities)" in line: next(fd) atoms = Atoms() velocities = [] for i in range(n_atoms): inp = next(fd).split() atoms.append(Atom(inp[4], (inp[1], inp[2], inp[3]))) inp = next(fd).split() floatvect = [v_unit * float(l) for l in inp[1:4]] velocities.append(floatvect) atoms.set_velocities(velocities) if len(fix): atoms.set_constraint([FixAtoms(indices=fix)] + fix_cart) else: atoms.set_constraint(fix_cart) images.append(atoms) # if we enter here, the SocketIO/PIMD Wrapper was used elif ("Atomic structure that " "was used in the preceding time step of the wrapper") in line: # parse atoms and add calculator information, i.e., the energies # and forces that were already collected atoms = _parse_atoms(fd, n_atoms=n_atoms) results = images[-1].calc.results atoms.calc = SinglePointCalculator(atoms, **results) # replace last image with updated atoms images[-1] = atoms # make sure `atoms` does not point to `images[-1` later on atoms = atoms.copy() # FlK: add analytical stress and replace stress=None if "Analytical stress tensor - Symmetrized" in line: # scroll to significant lines for _ in range(4): next(fd) stress = [] for _ in range(3): inp = next(fd) stress.append([float(i) for i in inp.split()[2:5]]) if "Total atomic forces" in line: f = [] for i in range(n_atoms): inp = next(fd).split() # FlK: use inp[-3:] instead of inp[1:4] to make sure this works # when atom number is not preceded by a space. f.append([float(i) for i in inp[-3:]]) if not found_aims_calculator: e = images[-1].get_potential_energy() # FlK: Add the stress if it has been computed if stress is None: calc = SinglePointCalculator(atoms, energy=e, forces=f) else: calc = SinglePointCalculator(atoms, energy=e, forces=f, stress=stress) images[-1].calc = calc e = None f = None if "Total energy corrected" in line: e = float(line.split()[5]) if pbc: atoms.set_cell(cell) atoms.pbc = True if not found_aims_calculator: atoms.calc = SinglePointCalculator(atoms, energy=e) if not molecular_dynamics: if len(fix): atoms.set_constraint([FixAtoms(indices=fix)] + fix_cart) else: atoms.set_constraint(fix_cart) images.append(atoms) e = None # if found_aims_calculator: # calc.set_results(images[-1]) # images[-1].calc = calc # FlK: add stress per atom if "Per atom stress (eV) used for heat flux calculation" in line: # scroll to boundary next(l for l in fd if "-------------" in l) stresses = [] for l in [next(fd) for _ in range(n_atoms)]: # Read stresses xx, yy, zz, xy, xz, yz = [float(d) for d in l.split()[2:8]] stresses.append([[xx, xy, xz], [xy, yy, yz], [xz, yz, zz]]) if not found_aims_calculator: e = images[-1].get_potential_energy() f = images[-1].get_forces() stress = images[-1].get_stress(voigt=False) calc = SinglePointCalculator(atoms, energy=e, forces=f, stress=stress, stresses=stresses) images[-1].calc = calc fd.close() if molecular_dynamics: images = images[1:] # return requested images, code borrowed from ase/io/trajectory.py if isinstance(index, int): return images[index] else: step = index.step or 1 if step > 0: start = index.start or 0 if start < 0: start += len(images) stop = index.stop or len(images) if stop < 0: stop += len(images) else: if index.start is None: start = len(images) - 1 else: start = index.start if start < 0: start += len(images) if index.stop is None: stop = -1 else: stop = index.stop if stop < 0: stop += len(images) return [images[i] for i in range(start, stop, step)]