Beispiel #1
1
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
Beispiel #2
1
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
Beispiel #3
0
	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
Beispiel #4
0
    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
Beispiel #5
0
    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
Beispiel #6
0
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
Beispiel #7
0
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
Beispiel #8
0
 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
Beispiel #9
0
	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
Beispiel #10
0
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
Beispiel #11
0
    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
Beispiel #12
0
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
Beispiel #13
0
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
Beispiel #14
0
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]
Beispiel #15
0
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
Beispiel #16
0
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
Beispiel #17
0
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
Beispiel #18
0
	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
		
			
		
Beispiel #19
0
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
Beispiel #20
0
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
Beispiel #21
0
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
Beispiel #22
0
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
Beispiel #23
0
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
Beispiel #24
0
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
Beispiel #25
0
	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
Beispiel #26
0
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
Beispiel #27
0
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
Beispiel #28
0
    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']))
Beispiel #29
0
    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
Beispiel #30
0
    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)
Beispiel #31
0
    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
Beispiel #32
0
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
Beispiel #33
0
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
Beispiel #34
0
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.
Beispiel #35
0
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)]
Beispiel #36
0
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 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
Beispiel #38
0
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
Beispiel #39
0
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
Beispiel #40
0
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
Beispiel #41
0
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]])
Beispiel #42
0
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
Beispiel #43
0
    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
Beispiel #44
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)
Beispiel #45
0
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
Beispiel #46
0
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])
Beispiel #47
0
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,
Beispiel #48
0
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)
Beispiel #49
0
'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]
Beispiel #50
0
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]
Beispiel #51
0
    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)
Beispiel #52
0
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
Beispiel #53
0
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