예제 #1
0
def read_trajectory(filename, r_cut):
    atom_number_list = [atoms.get_atomic_numbers() for atoms in traj]
    flat_atom_number = np.concatenate(atom_number_list)
    elements = np.unique(flat_atom_number, return_counts=False)
    all_distances = []
    for i, atoms in enumerate(traj):
        atoms.set_cell([[100, 0, 0], [0, 100, 0], [0, 0, 100]])
        nl = FullNeighborList(r_cut, atoms=atoms)
        for i in np.arange(len(atoms)):
            indices, positions, distances = nl.get_neighbors(i)
            all_distances.extend(distances**0.5)
    return all_distances
예제 #2
0
    def make_neighborlist(self):
        """Generate a lists with nearest neighbors, types and coordinations"""
        neighbor_cutoff = data.lattice[self.symmetry]['neighbor_cutoff']
        neighbor_cutoff *= self.lattice_constant
        neighbor_numbers = data.lattice[self.symmetry]['neighbor_numbers']
        neighbor_count = data.lattice[self.symmetry]['neighbor_count']

        get_neighbors = FullNeighborList(neighbor_cutoff, self)

        positions = self.get_positions()
        neighbors = []
        coordinations = []
        types = []

        for i, pos in enumerate(positions):
            nl = get_neighbors[i]
            dl = (positions[nl] - pos) / self.lattice_constant

            neighbors.append([-1] * neighbor_count)
            for n, d in zip(nl, dl):
                name = tuple(d.round(1))
                if name in neighbor_numbers:
                    neighbors[i][neighbor_numbers[name]] = n

            coordinations.append(self.get_atom_coordination(neighbors[i]))
            types.append(self.get_atom_type(neighbors[i]))

        self.set_neighbors(neighbors)
        self.set_coordinations(coordinations)
        self.set_types(types)
예제 #3
0
def GetLayerNumbers(atoms, rNearest=None, maxlayers=-1):
    if not rNearest:
        rNearest = GuessLatticeConstant(atoms) * 0.707
    c_nb = rNearest * 1.17  #Nearest neighbor cutoff
    c_sh = rNearest * 1.50  #Surface atoms cutoff

    nb = FullNeighborList(c_nb, atoms)
    cn = CoordinationNumbers(atoms, c_nb)

    rad = GetDiameter(atoms) / 2.0
    pos = atoms.get_positions()
    cop = pos.mean(axis=0)
    r = np.sqrt(np.sum((pos - cop)**2, axis=1))

    tags = ((cn < 11) & (r > (rad - c_sh))).astype(int)
    left = len(atoms) - tags.sum()

    n = 1
    while left:
        for i in np.arange(len(tags))[tags == n]:
            nbi = nb[i]
            ids = nbi[tags[nbi] == 0]
            tags[ids] = n + 1
            left -= len(ids)
        n += 1
        if n > maxlayers and maxlayers > 0:
            tags[tags == 0] = n
            break

    return tags
예제 #4
0
def cn_generator (filename, r_cut_cn):
    global agcn, agcn_tot_1D, agcn_tot_2D
    agcn_tot_1D=[]
    agcn_tot_2D=[]
    agcn=[] #containing arrays of aGCN at every time step

    for i, atoms in enumerate(traj):
        #frame by frame
        cn_frame=[]
        agcn_frame=[]
        agcn_tot_frame=[]
        atoms.set_cell([[100, 0, 0], [0, 100, 0], [0, 0, 100]])
        ind=[]
        for j in np.arange(len(atoms)):
            nl = FullNeighborList(r_cut_cn, atoms=atoms)
            indices, positions, distances = nl.get_neighbors(j)
            ind.append([int(k) for k in indices])
            distancej=[]    
            distancej.extend(distances**0.5)
            cnj=len(distancej)
            cn_frame.append(cnj)
        for l in np.arange(len(atoms)):
            cc=ind[l][:]
            list=[]
            for m in range(len(cc)):
                list.append(cn_frame[ind[l][m]])
                sm=sum(list)/12
            agcn_tot_1D.append(sm)
            agcn_tot_frame.append(sm)
                #we only want surface atoms, whihc have an atop GCN smaller than 11, from the freezing graphs
            if sm<=9:
                agcn_frame.append(sm)
        agcn_frame=np.asarray(agcn_frame)
        agcn_tot_frame=np.asarray(agcn_tot_frame)
        #rounding it to first decimal figure 
        agcn_frame=np.around(agcn_frame, 2)
        agcn_tot_frame=np.around(agcn_tot_frame, 2)
        agcn.append(agcn_frame)
        agcn_tot_2D.append(agcn_tot_frame)
    agcn=np.asarray(agcn)  #2D array: only surface atop GCN rounded at first decimal figure, each subarray corresponding to one time frame
    return (agcn, agcn_tot_1D, agcn_tot_2D)
예제 #5
0
파일: calculators.py 프로젝트: kcl-tscm/mff
    def initialize(self, atoms):

        logger.info('initialize')
        self.nl = FullNeighborList(self.r_cut, atoms=atoms, driftfactor=0.)
예제 #6
0
    def set_atoms(self, atoms):
        Move.set_atoms(self, atoms)

        if not isinstance(atoms, Cluster):
            raise Warning('The cluster is not a valid Cluster instance.')

        if atoms.symmetry != 'fcc':
            raise Warning('Can only work with fcc structures.')

        self.natoms = len(atoms)

        # Get the structual parameters
        sym = atoms.symmetry
        self.neighbor_positions = (latticedata[sym]['neighbor_positions'] *
                                   atoms.lattice_basis[0, 0])
        self.neighbor_cutoff = (latticedata[sym]['neighbor_cutoff'] *
                                atoms.lattice_basis[0, 0])
        self.neighbor_numbers = latticedata[sym]['neighbor_numbers']
        self.neighbor_mapping = latticedata[sym]['neighbor_mapping']
        self.neighbor_count = latticedata[sym]['neighbor_count']
        self.type_count = latticedata[sym]['type_count']
        self.type_names = latticedata[sym]['type_names']
        self.type_numbers = latticedata[sym]['type_numbers']
        self.type_data = latticedata[sym]['type_data']

        # Set neighbors, coordination and type for all atoms
        get_neighbors = FullNeighborList(self.neighbor_cutoff, atoms)

        positions = atoms.get_positions()
        neighbors = []
        coordinations = []
        types = []

        for i, pos in enumerate(positions):
            nl = get_neighbors[i]
            dl = (positions[nl] - pos) / atoms.lattice_basis[0, 0]

            neighbors.append([-1] * self.neighbor_count)
            for n, d in zip(nl, dl):
                name = tuple(d.round(1))
                if name in self.neighbor_numbers:
                    neighbors[i][self.neighbor_numbers[name]] = n

            coordinations.append(self.get_atom_coordination(neighbors[i]))
            types.append(self.get_atom_type(neighbors[i]))

        self.positions = positions
        self.neighbors = np.array(neighbors, dtype=int)
        self.coordinations = np.array(coordinations, dtype=int)
        self.types = np.array(types, dtype=int)

        # Generate vacancies (position, neighbors, coordination and type)
        self.vacant_positions = np.zeros((0, 3), dtype=float)
        self.vacant_neighbors = np.zeros((0, self.neighbor_count), dtype=int)
        self.vacant_coordinations = np.zeros(0, dtype=int)
        self.vacant_types = np.zeros(0, dtype=int)

        for i in self.surface_indexes():
            self.add_vacancies(self.positions[i], self.neighbors[i], i)

        if self.debug > 2:
            if self.check_vacancies():
                raise Warning('Something is wrong, read the message above')
예제 #7
0
 def __init__(self, cutoff=3.0):
     self.nblist = FullNeighborList(cutoff)
예제 #8
0
class ClusterCenter:
    def __init__(self, cutoff=3.0):
        self.nblist = FullNeighborList(cutoff)
        
    def calculate_center(self, atoms, startatom=None):
        """Calculate the center of mass position of a cluster of atoms.
        
        An atom belonging to the cluster can optionally be specified, if
        not specified one of the highest coordinated atoms is used.
        """
        natoms = len(atoms)
        self.nblist.check_and_update(atoms)
        if startatom is None:
            coordnum = [len(self.nblist[i]) for i in range(natoms)]
            startatom = np.argmax(coordnum)
        self.cluster = []
        self.sumpos = np.zeros(3)
        self.summass = 0
        self.isincluster = np.zeros(natoms, bool)
        self.masses = atoms.get_masses()
        self.add_atom_to_cluster(startatom, atoms[startatom].position)
        com = self.sumpos / self.summass
        return com
    
    def add_atom_to_cluster(self, n, pos):
        "Add an atom and all its neighbors to the cluster."
        self.isincluster[n] = True
        self.sumpos += pos * self.masses[n]
        self.summass += self.masses[n]
        neighbors, reldists, sqdists = self.nblist.get_neighbors(n)
        for i, relpos in zip(neighbors, reldists):
            if not self.isincluster[i]:
                # Add neighboring atom to cluster, using the relative
                # position so periodic boundaries are handled correctly.
                self.add_atom_to_cluster(i, pos + relpos)
            
    def calculate_from_trajectory(self, traj, startatom=None, selector=None):
        """Calculate the center of mass for a cluster in a trajectory file.
        
        traj: The trajectory object, or a file name.
        
        startatom (optional): Specifies an atom guaranteed to be in the cluster.
            If not specified, the atom with the highest coordination number is
            used (if there is only one cluster this should work).
            
        selector (optional): A function defining which atoms should be 
            considered.  The function is called with one argument, the atoms
            object, and should either return an array of booleans, one per
            atom, indicating if they should be included, or return an array
            of integers interpreted as the indices of the atoms to be included.
            This can e.g. be used to select a cluster sitting on a substrate.
            
        This method returns an array of center-of-mass positions, one for each
        frame in the trajectory.
        """
        if isinstance(traj, str):
            if traj.endswith('.traj'):
                traj = PickleTrajectory(traj)
            elif traj.endswith('.bundle'):
                traj = BundleTrajectory(traj)
            else:
                raise ValueError("Cannot handle a file name not ending in .traj or .bundle: " + traj)
        result = []
        for atoms in traj:
            if selector is not None:
                idx = selector(atoms)
                atoms = atoms[idx]
            result.append(self.calculate_center(atoms, startatom))
        return result
예제 #9
0
파일: EMT2011_py.py 프로젝트: auag92/n2dm
    def initialize(self, atoms):
        """ Method which initializes the EMT calculator by defining all needed values used in the
            calculations. """
        # A list, Z, of the element type for each atom in the system is defined:
        self.Z = atoms.get_atomic_numbers()
        # The number of atoms are calculated: 
        self.N = len(self.Z)        
        # Lists of the values for eta2, kappa, Seq, E0, V0, n0 and L (lambda) for the element types Z are 
        # defined:
        # The "largest" element number is corrected with regards to the method of counting in python.
        self.eta2 = numpy.zeros(NumElle)
        self.kappa = numpy.zeros(NumElle)
        self.Seq = numpy.zeros(NumElle)
        self.E0 = numpy.zeros(NumElle)
        self.V0 = numpy.zeros(NumElle)
        self.L = numpy.zeros(NumElle)
        self.n0 = numpy.zeros(NumElle)
        for i in range(NumElle):
            if chemical_symbols[i] in self.parameters:
                self.eta2[i] = self.parameters[chemical_symbols[i]][3] / Bohr
                self.kappa[i] = self.parameters[chemical_symbols[i]][4] / Bohr
                self.Seq[i] = self.parameters[chemical_symbols[i]][1] * Bohr
                self.E0[i] = self.parameters[chemical_symbols[i]][0]
                self.V0[i] = self.parameters[chemical_symbols[i]][2]
                self.L[i] = self.parameters[chemical_symbols[i]][5] / Bohr
                self.n0[i] = self.parameters[chemical_symbols[i]][6] / (Bohr**3)
        
        # Calculation of the X*X-arrays:    
        # r_cut; X*X-array of vaules for the cutoff length for the atompair of type (Z,Z') 
        # sigmaaRCUT; X*X-array of values for sigmaa evaluated in r_cut[Z,Z']
        # sigmabRCUT; X*X-array of values for sigmab evaluated in r_cut[Z,Z']
        # dsigmaadrRCUT; X*X-array of values for the deriviative of sigmaa evaluated in r_cut[Z,Z']
        # dsigmabdrRCUT; X*X-array of values for the deriviative of sigmab evaluated in r_cut[Z,Z']
        # chi; X*X-array of values for chi for the atompair of type (Z,Z')

        # The cutoff distances are calculated using that the lattice constant, a0 = sqrt(2)*beta*s0:
        self.r_cut = numpy.zeros([NumElle,NumElle])

        for i in range(NumElle):
            for j in range(NumElle):
                # Check to see if the two elements considered are defined or not. Only calculating an 
                # r_cut if both are. r_cut[Z,Z'] is calculated as the cutoff distance for the the one of 
                # elements Z,Z' which has the largest a0.
                if self.Seq[i] and self.Seq[j] != 0:
                    self.r_cut[i,j] = (1./2. * (sqrt(3. / 2.) + sqrt(4. / 2.)) * (sqrt(2) * beta) * 
                                      max(self.Seq[i],self.Seq[j]))

        
        ### Calculations for sigmaaRCUT, sigmabRCUT, d_sigmaadrRCUT and d_sigmabdrRCUT ###

        self.dsigmaadrRCUT = numpy.zeros([NumElle,NumElle])
        self.dsigmabdrRCUT = numpy.zeros([NumElle,NumElle])
        self.sigmaaRCUT = numpy.zeros([NumElle,NumElle])
        self.sigmabRCUT = numpy.zeros([NumElle,NumElle])

        for i in range(NumElle):
            for j in range(NumElle):
                # Check to see if r_cut[i,j] is defined for this pair of elements. r_cut[i,j] == 0 means 
                # that it is not defined.
                if self.r_cut[i,j] != 0:
                    self.sigmaaRCUT[i,j] = (numpy.exp(self.eta2[j] * 
                                           (-self.r_cut[i,j] + self.Seq[j] * beta)))
                    self.sigmabRCUT[i,j] = (numpy.exp(self.kappa[j] * 
                                           (-self.r_cut[i,j] / beta + self.Seq[j])))
                    self.dsigmaadrRCUT[i,j] = -self.eta2[j] * self.sigmaaRCUT[i,j]
                    self.dsigmabdrRCUT[i,j] = -self.kappa[j] / beta * self.sigmabRCUT[i,j]
        
        
        ### Calculations for chi[Z,Z'] ###

        self.chi = numpy.zeros([NumElle,NumElle])

        for i in range(NumElle):
            for j in range(NumElle):
                # Check to see if the elements i,j are defined.
                if self.n0[i] and self.n0[j] != 0:
                    self.chi[i,j] = self.n0[i] / self.n0[j]


        ### Calculations of gamma1 and gamma2 ###

	# Four (3 x NumElle)-arrays for lambda_1,2 (named L_1,2) and sigmaa_1,2 are calculated with the distance, 
        # r_ij = (beta * Seq, sqrt(2) * beta * Seq, sqrt(3) * beta * Seq) for all supportet elements. 
        # where Z = Z'.
        
        # The NumberNearestNeighbours variable is set to the number of nearest neighbors included in the model.
	NumberNearestNeighbours = 3
        # arrays for lambda and sigmaa are initialized 
	L_1_Z = numpy.zeros([NumberNearestNeighbours,NumElle])
	L_2_Z = numpy.zeros([NumberNearestNeighbours,NumElle])
	sigmaa_Z = numpy.zeros([NumberNearestNeighbours,NumElle])
	sigmab_Z = numpy.zeros([NumberNearestNeighbours,NumElle])
        # The values for each are calculated for each neighbour distance
        for i in range(NumberNearestNeighbours):
            L_1_Z[i] = (self.dsigmaadrRCUT[range(NumElle),range(NumElle)] *
                       ((sqrt(1 + i) * beta * self.Seq) - self.r_cut[range(NumElle),range(NumElle)]) +
                        self.sigmaaRCUT[range(NumElle),range(NumElle)])

            L_2_Z[i] = (self.dsigmabdrRCUT[range(NumElle),range(NumElle)] *
                       ((sqrt(1 + i) * beta * self.Seq) - self.r_cut[range(NumElle),range(NumElle)]) +
                        self.sigmabRCUT[range(NumElle),range(NumElle)])
              
            sigmaa_Z[i] = numpy.exp(self.eta2 * (-(sqrt(1 + i) * beta * self.Seq) + self.Seq * beta))
            sigmab_Z[i] = numpy.exp(self.kappa * (-(sqrt(1 + i) * self.Seq) + self.Seq))

        
        # The factor (self.Seq/self.Seq) is an array of zeros and ones and is only used to secure that only 
        # the elements which are actually defined in "parameters" gives a gamma_1,2 different from zero.
        self.gamma1 = ((self.Seq/self.Seq) * 
                      (12 * (sigmaa_Z[0] - L_1_Z[0]) + 
                       6 * (sigmaa_Z[1] - L_1_Z[1]) + 
                       24 * (sigmaa_Z[2] - L_1_Z[2]) ))
        self.gamma2 = ((self.Seq/self.Seq) * 
                      (12 * (sigmab_Z[0] - L_2_Z[0]) + 
                       6 * (sigmab_Z[1] - L_2_Z[1]) + 
                       24 * (sigmab_Z[2] - L_2_Z[2]) ))

        ### Construction of a Full Neighborlist for the system of atoms, 
        self.nbList = FullNeighborList(self.r_cut.max(),atoms)

        ### Initialization of the variables holding the forces on and energy of the atoms ###
        self.forces = None
        self.energy = None
예제 #10
0
파일: EMT2011_py.py 프로젝트: auag92/n2dm
class EMT:
    """ This class is an implementation of the revised edition of the Effective Medium Theory approach of calculating the energy of a given FCC crystal system. The functional form of the equations used can be found in ******* """
    def __init__(self,Params=None,ZeroPoint=1):
        """ Initializes the EMT object. The input Params is used to specify userdefined parameters and the input 
            Zeropoint is used to specify whether the potential energy measured realtive to E0 = Ecoh (ZeroPoint = 1) 
            or E0 = 0 (ZeroPoint = 0). """
        # Secures that the calculator is initialized correctly the first time it is used.
        self.energy = None
        self.ZeroPoint = ZeroPoint
        # If no parameters have been specified when creating the EMT object the default parameters are used.
        if Params is None:
            self.parameters = parameters
        else:
            self.parameters = Params

    def initialize(self, atoms):
        """ Method which initializes the EMT calculator by defining all needed values used in the
            calculations. """
        # A list, Z, of the element type for each atom in the system is defined:
        self.Z = atoms.get_atomic_numbers()
        # The number of atoms are calculated: 
        self.N = len(self.Z)        
        # Lists of the values for eta2, kappa, Seq, E0, V0, n0 and L (lambda) for the element types Z are 
        # defined:
        # The "largest" element number is corrected with regards to the method of counting in python.
        self.eta2 = numpy.zeros(NumElle)
        self.kappa = numpy.zeros(NumElle)
        self.Seq = numpy.zeros(NumElle)
        self.E0 = numpy.zeros(NumElle)
        self.V0 = numpy.zeros(NumElle)
        self.L = numpy.zeros(NumElle)
        self.n0 = numpy.zeros(NumElle)
        for i in range(NumElle):
            if chemical_symbols[i] in self.parameters:
                self.eta2[i] = self.parameters[chemical_symbols[i]][3] / Bohr
                self.kappa[i] = self.parameters[chemical_symbols[i]][4] / Bohr
                self.Seq[i] = self.parameters[chemical_symbols[i]][1] * Bohr
                self.E0[i] = self.parameters[chemical_symbols[i]][0]
                self.V0[i] = self.parameters[chemical_symbols[i]][2]
                self.L[i] = self.parameters[chemical_symbols[i]][5] / Bohr
                self.n0[i] = self.parameters[chemical_symbols[i]][6] / (Bohr**3)
        
        # Calculation of the X*X-arrays:    
        # r_cut; X*X-array of vaules for the cutoff length for the atompair of type (Z,Z') 
        # sigmaaRCUT; X*X-array of values for sigmaa evaluated in r_cut[Z,Z']
        # sigmabRCUT; X*X-array of values for sigmab evaluated in r_cut[Z,Z']
        # dsigmaadrRCUT; X*X-array of values for the deriviative of sigmaa evaluated in r_cut[Z,Z']
        # dsigmabdrRCUT; X*X-array of values for the deriviative of sigmab evaluated in r_cut[Z,Z']
        # chi; X*X-array of values for chi for the atompair of type (Z,Z')

        # The cutoff distances are calculated using that the lattice constant, a0 = sqrt(2)*beta*s0:
        self.r_cut = numpy.zeros([NumElle,NumElle])

        for i in range(NumElle):
            for j in range(NumElle):
                # Check to see if the two elements considered are defined or not. Only calculating an 
                # r_cut if both are. r_cut[Z,Z'] is calculated as the cutoff distance for the the one of 
                # elements Z,Z' which has the largest a0.
                if self.Seq[i] and self.Seq[j] != 0:
                    self.r_cut[i,j] = (1./2. * (sqrt(3. / 2.) + sqrt(4. / 2.)) * (sqrt(2) * beta) * 
                                      max(self.Seq[i],self.Seq[j]))

        
        ### Calculations for sigmaaRCUT, sigmabRCUT, d_sigmaadrRCUT and d_sigmabdrRCUT ###

        self.dsigmaadrRCUT = numpy.zeros([NumElle,NumElle])
        self.dsigmabdrRCUT = numpy.zeros([NumElle,NumElle])
        self.sigmaaRCUT = numpy.zeros([NumElle,NumElle])
        self.sigmabRCUT = numpy.zeros([NumElle,NumElle])

        for i in range(NumElle):
            for j in range(NumElle):
                # Check to see if r_cut[i,j] is defined for this pair of elements. r_cut[i,j] == 0 means 
                # that it is not defined.
                if self.r_cut[i,j] != 0:
                    self.sigmaaRCUT[i,j] = (numpy.exp(self.eta2[j] * 
                                           (-self.r_cut[i,j] + self.Seq[j] * beta)))
                    self.sigmabRCUT[i,j] = (numpy.exp(self.kappa[j] * 
                                           (-self.r_cut[i,j] / beta + self.Seq[j])))
                    self.dsigmaadrRCUT[i,j] = -self.eta2[j] * self.sigmaaRCUT[i,j]
                    self.dsigmabdrRCUT[i,j] = -self.kappa[j] / beta * self.sigmabRCUT[i,j]
        
        
        ### Calculations for chi[Z,Z'] ###

        self.chi = numpy.zeros([NumElle,NumElle])

        for i in range(NumElle):
            for j in range(NumElle):
                # Check to see if the elements i,j are defined.
                if self.n0[i] and self.n0[j] != 0:
                    self.chi[i,j] = self.n0[i] / self.n0[j]


        ### Calculations of gamma1 and gamma2 ###

	# Four (3 x NumElle)-arrays for lambda_1,2 (named L_1,2) and sigmaa_1,2 are calculated with the distance, 
        # r_ij = (beta * Seq, sqrt(2) * beta * Seq, sqrt(3) * beta * Seq) for all supportet elements. 
        # where Z = Z'.
        
        # The NumberNearestNeighbours variable is set to the number of nearest neighbors included in the model.
	NumberNearestNeighbours = 3
        # arrays for lambda and sigmaa are initialized 
	L_1_Z = numpy.zeros([NumberNearestNeighbours,NumElle])
	L_2_Z = numpy.zeros([NumberNearestNeighbours,NumElle])
	sigmaa_Z = numpy.zeros([NumberNearestNeighbours,NumElle])
	sigmab_Z = numpy.zeros([NumberNearestNeighbours,NumElle])
        # The values for each are calculated for each neighbour distance
        for i in range(NumberNearestNeighbours):
            L_1_Z[i] = (self.dsigmaadrRCUT[range(NumElle),range(NumElle)] *
                       ((sqrt(1 + i) * beta * self.Seq) - self.r_cut[range(NumElle),range(NumElle)]) +
                        self.sigmaaRCUT[range(NumElle),range(NumElle)])

            L_2_Z[i] = (self.dsigmabdrRCUT[range(NumElle),range(NumElle)] *
                       ((sqrt(1 + i) * beta * self.Seq) - self.r_cut[range(NumElle),range(NumElle)]) +
                        self.sigmabRCUT[range(NumElle),range(NumElle)])
              
            sigmaa_Z[i] = numpy.exp(self.eta2 * (-(sqrt(1 + i) * beta * self.Seq) + self.Seq * beta))
            sigmab_Z[i] = numpy.exp(self.kappa * (-(sqrt(1 + i) * self.Seq) + self.Seq))

        
        # The factor (self.Seq/self.Seq) is an array of zeros and ones and is only used to secure that only 
        # the elements which are actually defined in "parameters" gives a gamma_1,2 different from zero.
        self.gamma1 = ((self.Seq/self.Seq) * 
                      (12 * (sigmaa_Z[0] - L_1_Z[0]) + 
                       6 * (sigmaa_Z[1] - L_1_Z[1]) + 
                       24 * (sigmaa_Z[2] - L_1_Z[2]) ))
        self.gamma2 = ((self.Seq/self.Seq) * 
                      (12 * (sigmab_Z[0] - L_2_Z[0]) + 
                       6 * (sigmab_Z[1] - L_2_Z[1]) + 
                       24 * (sigmab_Z[2] - L_2_Z[2]) ))

        ### Construction of a Full Neighborlist for the system of atoms, 
        self.nbList = FullNeighborList(self.r_cut.max(),atoms)

        ### Initialization of the variables holding the forces on and energy of the atoms ###
        self.forces = None
        self.energy = None
        

    def NeighborList_rcutReduced(self,i):
        """ Method which makes sure that only the neighboratoms within the correct cutoff for the involved 
        element types Z,Z' are included in the calculations by modifying the output of the FullNeighborList
        function. """

        # Relavant data about the neighbor atom, j, for atom i which can possible give a contribution are 
        # selected
        (other_j,r_ij,rsq) = self.nbList.get_neighbors(i)

        # The neighbor atoms which will actually give a contribution to the energy, based on the individual 
        # cutoff distances between atom i of type Z and atom j of type Z', are selected.

        # The neighbor atoms which fullfill the condition are chosen
        keep = numpy.sqrt(rsq) <= self.r_cut[self.Z[i],self.Z[other_j]]
	
        # The lists of data about the neighbor atoms are updated
        if len(keep) != 0:
            return (other_j[keep],r_ij[keep],rsq[keep])
        else:
            # nbList returned empty lists, but we cannot index a shape (0,3) array (r_ij)
            # with an empty list (bug in numpy?).
            return (other_j,r_ij,rsq)

    
    def update(self, atoms):
        """ This method is called by the atoms object to which the calculator is attached, it secures that the 
            energy (and/or force) of the system is recalculated if this is required. """
        need_calc = False
        if (self.energy is None or len(self.Z) != len(atoms) or (self.Z != atoms.get_atomic_numbers()).any()):
            # The calculator is initialized with regards to the atoms object.
            self.initialize(atoms)
            need_calc = True
        elif (self.positions != atoms.get_positions()).any():
            # The atoms object has not changed enough for the calculator to need a reinitialization but a 
            # new calculation of the value for the energies are still needed.
            need_calc = True
        if need_calc:
            self.positions = atoms.get_positions()
            self.nbList.check_and_update(atoms)
            self.energy = self.calculate_Energy()
            self.forces = self.calculate_Force()


    # Returns the energy of the atoms (the method calculates the energy first if needed be)
    def get_potential_energy(self, atoms):
         self.update(atoms)
         return self.energy

    # Returns the forces on the atoms (the method calculates the forces first if needed be)
    def get_forces(self, atoms):
        self.update(atoms)
        return self.forces.copy()

    def get_stress(self, atoms):
        raise NotImplementedError('No stresses implemented')


    ########## ENERGY Calculations ##########

    ### sigma_1,2 ###

    
    def calculate_sigma12(self):
        """ Calculates and returns sigma_1 and sigma_2. """
        # The N-arrays for sigma_1,2 are initialized
        sigma_1 = numpy.zeros(self.N)
        sigma_2 = numpy.zeros(self.N)

       
        for i in range(self.N):
            # The numbers of the neighbor atoms, the relative position vectors and length of the position 
            # vectors squared between atom i and the neighbor atoms j are defined in three arrays. 
            (other_j,r_ij,rsq) = self.NeighborList_rcutReduced(i)
            
            
            # The values for the linear subtracktion functions evaluated at norm(r_ij,2) for all the atom 
            # pairs, [i,other_j], are calculated.
            L_1_i = (self.dsigmaadrRCUT[self.Z[i],self.Z[other_j]] *
                    (numpy.sqrt(rsq) - self.r_cut[self.Z[i],self.Z[other_j]]) +
                     self.sigmaaRCUT[self.Z[i],self.Z[other_j]])
            L_2_i = (self.dsigmabdrRCUT[self.Z[i],self.Z[other_j]] *
                    (numpy.sqrt(rsq) - self.r_cut[self.Z[i],self.Z[other_j]]) +
                     self.sigmabRCUT[self.Z[i],self.Z[other_j]])
                       
            # sigmaa_i and sigmab_i are evaluated at norm(r_ij,2) for all the atom pairs, [i,other_j]. 
            sigmaa_i = (numpy.exp(self.eta2[self.Z[other_j]] * 
                       (-numpy.sqrt(rsq) + self.Seq[self.Z[other_j]] * beta)))
            sigmab_i = (numpy.exp(self.kappa[self.Z[other_j]] * 
                       (-numpy.sqrt(rsq) / beta + self.Seq[self.Z[other_j]])))

            #if (i == 1):
            #    print sigmaa_i

            # The values of sigma_1_i and sigma_2_i are calculated for
            # the atom i. Where max(a,b) is introduced in order to
            # secure a none zero minimumvalue for sigma_1 so the
            # following calculations wont result in an error.
            sigma_1[i] = max( pow(10,-9) , (self.chi[self.Z[i],self.Z[other_j]] * (sigmaa_i - L_1_i)).sum() )
            sigma_2[i] = (self.chi[self.Z[i],self.Z[other_j]] * (sigmab_i - L_2_i)).sum()

        #TESTER#
        #print sigma_1[:10]
        
        
        return (sigma_1,sigma_2)


    ### s_i ###

    def calculate_s(self,sigma_1):
        """ Calculates and returns an N-array containing the neutrality sphere radii, s, for the atoms of the 
        system is calculated."""    
        return self.Seq[self.Z] - numpy.log(sigma_1 / self.gamma1[self.Z]) / (beta * self.eta2[self.Z])
 

    ### E_tot ###

    
    def calculate_Energy_function(self,s,sigma_2):
        """ Calculates and returns the total energy of the system using s and sigma_2. """
        # Calculation of the N-array containing the cohesive energy for each of the N atoms.
        E_c = ( self.E0[self.Z] * (self.L[self.Z] * (s - self.Seq[self.Z]) + 1) * 
                numpy.exp(-self.L[self.Z] * (s - self.Seq[self.Z])) ) - self.E0[self.Z] * self.ZeroPoint

        # Calculation of the N-array containing the atomic sphere correction energy for each of the N atoms.
        E_as = (6 * ( self.V0[self.Z] * numpy.exp(-self.kappa[self.Z] * (s - self.Seq[self.Z])) 
                -self.V0[self.Z] * sigma_2 / self.gamma2[self.Z] ) )

        # Calculation of the total energy
        return (E_c + E_as).sum()


    ### Final Energy Calculator ###

    def calculate_Energy(self):
        """ Calculates and returnes the energy of the atoms in the atom object to which the 
            EMT calculator is attached. The calculations are done using the following methods, 
            also defined in EMT.py: calculate_sigma12(self), calculate_s(self,sigma_1), 
            calculate_Energy_function(self,s,sigma_2). """
        (sigma_1,sigma_2) = self.calculate_sigma12()
        s = self.calculate_s(sigma_1)
    
        # The total energy is calculated and returned
        return self.calculate_Energy_function(s,sigma_2)




    ########## FORCE Calculations ##########


    ### dsdsigma_1 ###

    
    def calculate_dsdsigma_1(self,sigma_1):
        """ Calculates and returns dsdsigma_1 using sigma_1. """
        # An N-array containing the the deriviative of neutrality sphere radii, s, with regards to sigma_1 for 
        # the atoms of the system is calculated.    
        dsdsigma_1 = -1 / (beta * self.eta2[self.Z] * sigma_1)

        return dsdsigma_1


    ### dE_cids, dE_asds, dE_asdsigma_2 ###

    def calculate_Deriviative_of_Energy(self,s):
        """ Calculates and returns the deriviatives of E_cs and E_as with regards to s and sigma_2. """
        # Calculation of the N-array containing the deriviative of the cohesive energy with regards to s for 
        # each of the N atoms.
        dE_cds = -( self.E0[self.Z] * self.L[self.Z] * self.L[self.Z] * 
                    numpy.exp(-self.L[self.Z] * (s - self.Seq[self.Z])) * (s - self.Seq[self.Z]) )

        # Calculation of the N-array containing the deriviative of the atomic sphere correction energy with 
        # regards to s for each of the N atoms.
        dE_asds = -6 * self.kappa[self.Z] * self.V0[self.Z] * numpy.exp(-self.kappa[self.Z] * (s - self.Seq[self.Z]))

        # Calculation of the N-array containing the deriviative of the atomic sphere correction energy with 
        # regards to sigma_2 for each of the N atoms.
        dE_asdsigma_2 = -6 * self.V0[self.Z] / (self.gamma2[self.Z])

        return (dE_cds,dE_asds,dE_asdsigma_2)


    ### F_kalpha ###
    def calculate_Force_function(self,dE_cds,dE_asds,dE_asdsigma_2,dsdsigma_1):
        """ Calculates the force on all k atoms in the three directions {x,y,z} representet by alpha. """ 
            
        # An array for the force is initialized
        F = numpy.zeros([self.N,3])

        for k in range(self.N):
            # The atoms interacting with atom k are selected.
            (other_i,r_ki,rsq) = self.NeighborList_rcutReduced(k)
            #print other_i
            #print r_ki
            #print numpy.sqrt(rsq)
            
            # The values for dr_ijdr_kalpha are calculated for the relevant atoms, k and other_i.
            dr_kidr_kalpha = r_ki / numpy.sqrt(rsq)[:,numpy.newaxis]
            
            ## The force on the k'th atom caused by the atoms, other_i's, interactions with k are calculated ##

            # The values for dsigmaa_idr_ij and dsigmab_idr_ij are calculated with regards to the k'th atom
            sigmaa_k = numpy.exp(self.eta2[self.Z[other_i]] * 
                       (-numpy.sqrt(rsq) + self.Seq[self.Z[other_i]] * beta))
            sigmab_k = numpy.exp(self.kappa[self.Z[other_i]] * 
                       (-numpy.sqrt(rsq) / beta + self.Seq[self.Z[other_i]]))
            
            dsigmaa_kdr_ki = (-self.eta2[self.Z[other_i]] * sigmaa_k)[:,numpy.newaxis]
            dsigmab_kdr_ki = (-self.kappa[self.Z[other_i]] / beta * sigmab_k)[:,numpy.newaxis]
            
            # Values for dL_1idr_ij and dL_2idr_ij are calculated with regards to the k'th atom
            dL_1kdr_ki = self.dsigmaadrRCUT[self.Z[k],self.Z[other_i]][:,numpy.newaxis]
            dL_2kdr_ki = self.dsigmabdrRCUT[self.Z[k],self.Z[other_i]][:,numpy.newaxis]
            
            # First the value of dsigma1_idr_kaplha and dsigma2_idr_kaplha are calculated for the k'th atom
            dsigma1_kdr_kalpha = ( self.chi[self.Z[k],self.Z[other_i]][:,numpy.newaxis] * 
                                   (dsigmaa_kdr_ki - dL_1kdr_ki) * dr_kidr_kalpha ).sum(axis=0)
            dsigma2_kdr_kalpha = ( self.chi[self.Z[k],self.Z[other_i]][:,numpy.newaxis] * 
                                   (dsigmab_kdr_ki - dL_2kdr_ki) * dr_kidr_kalpha ).sum(axis=0)
            
            """ TJEK DER SKAL FJERNES SENERE """
            assert len(dsigma1_kdr_kalpha) == 3
            """ TJEK DER SKAL FJERNES SENERE """

            # The contribution to the force on atom k from the k'th atoms interaction with the other_i atoms is 
            # calculated
            F[k] = (dE_cds[k] * dsdsigma_1[k] * dsigma1_kdr_kalpha +
                    dE_asds[k] * dsdsigma_1[k] * dsigma1_kdr_kalpha + 
                    dE_asdsigma_2[k] * dsigma2_kdr_kalpha)
            

            # The values for dsigmaa_idr_ij and dsigmab_idr_ij are calculated with regards to the atoms other_i 
            # where j = k for all other_i (thus we only need one value of dsigmaa_idr_ij).
            sigmaa_i = numpy.exp(self.eta2[self.Z[k]] * (-numpy.sqrt(rsq) + self.Seq[self.Z[k]] * beta))
            sigmab_i = numpy.exp(self.kappa[self.Z[k]] * (-numpy.sqrt(rsq) / beta + self.Seq[self.Z[k]]))
            
            dsigmaa_idr_ik = (-self.eta2[self.Z[k]] * sigmaa_i)[:,numpy.newaxis]
            dsigmab_idr_ik = (-self.kappa[self.Z[k]] / beta * sigmab_i)[:,numpy.newaxis]
            
            # Values for dL_1idr_ij and dL_2idr_ij are calculated with regards to the atoms other_i 
            # where j = k for all other_i.
            dL_1idr_ik = self.dsigmaadrRCUT[self.Z[other_i],self.Z[k]][:,numpy.newaxis]
            dL_2idr_ik = self.dsigmabdrRCUT[self.Z[other_i],self.Z[k]][:,numpy.newaxis]
            
            # First the value of dsigma1_idr_kaplha and dsigma2_idr_kaplha are calculated with regards to the atoms 
            # other_i where j are only the atom k for all other_i. (thus the sum only has one element for all other_i.
            # which results in the calculations leading to an [other_i,3]-array. 
            dsigma1_idr_kalpha = (self.chi[self.Z[other_i],self.Z[k]][:,numpy.newaxis] * 
                                  (dsigmaa_idr_ik - dL_1idr_ik) * (dr_kidr_kalpha) )
            dsigma2_idr_kalpha = (self.chi[self.Z[other_i],self.Z[k]][:,numpy.newaxis] * 
                                  (dsigmab_idr_ik - dL_2idr_ik) * (dr_kidr_kalpha) )
            
            # The contribution to the force on atom k from the other_i atoms interaction with the k'th atom is now 
            # calculated
            F[k] += (dE_cds[other_i][:,numpy.newaxis] * dsdsigma_1[other_i][:,numpy.newaxis] * dsigma1_idr_kalpha +
                     dE_asds[other_i][:,numpy.newaxis] * dsdsigma_1[other_i][:,numpy.newaxis] * dsigma1_idr_kalpha + 
                     dE_asdsigma_2[other_i][:,numpy.newaxis] * dsigma2_idr_kalpha).sum(axis=0)

            """ TJEK DER SKAL FJERNES SENERE """
            assert len(F[k]) == 3
            """ TJEK DER SKAL FJERNES SENERE """

        return F


    ### Final Force Calculator ###

    def calculate_Force(self):
        """ Calculates and returnes the force acting on each of the atoms in the atoms object to which the 
            EMT calculator is attached. These calculations are done using the following methods, 
            also defined in EMT.py: calculate_sigma12(self), calculate_s(self,sigma_1), calculate_dsdsigma_1
            (self,sigma_1), calculate_Deriviative_of_Energy(self,s) and calculate_Force_function
            (self,dE_cds,dE_asds,dE_asdsigma_2,dsdsigma_1) """
        (sigma_1,sigma_2) = self.calculate_sigma12()
        s = self.calculate_s(sigma_1)
        dsdsigma_1 = self.calculate_dsdsigma_1(sigma_1)
        (dE_cds,dE_asds,dE_asdsigma_2) = self.calculate_Deriviative_of_Energy(s)
    
        # The force is calculated and returned
        return self.calculate_Force_function(dE_cds,dE_asds,dE_asdsigma_2,dsdsigma_1)
예제 #11
0
def carve_from_snapshot(atoms,
                        r_cut,
                        forces_label=None,
                        energy_label=None,
                        atoms_ind=None):
    """Extract atomic configurations, the forces acting on the central atoms
    os said configurations, and the local energy values associated to a single atoms object.

    Args:
        atoms (ase atoms object): Ase atoms file, opened with ase.io.read
        atoms_ind (list): indexes of the atoms for which a conf is created
        r_cut (float): Cutoff to use when carving out atomic environments
        forces_label (str): Name of the force label in the trajectory file, if None default is "forces"
        energy_label (str): Name of the energy label in the trajectory file, if None default is "energy"

    Returns:
        confs (list of arrays): List of M by 5 numpy arrays, where M is the number of atoms within
            r_cut from the central one. The first 3 components are positions w.r.t
            the central atom in Angstroms, the fourth is the atomic number of the 
            central atom, the fifth the atomic number of each atom.
        forces (array): x,y,z components of the force acting on the central atom in eV/Angstrom
        energies (array): value of the local atomic energy in eV

    """

    if atoms_ind is None:
        atoms_ind = np.arange(len(atoms))

    if forces_label:
        forces = atoms.arrays.get(forces_label)
    else:
        try:
            forces = atoms.get_forces()
        except:
            forces = None
    if energy_label and energy_label != 'energy':
        energy = atoms.arrays.get(energy_label)
    else:
        energy_label = 'energy'
        try:
            energy = atoms.get_potential_energy()
        except:
            energy = None

    if forces is None and energy is None:
        raise MissingData(
            'Cannot find energy or force values in the xyz file, shutting down'
        )

    if forces is not None:
        forces = forces[atoms_ind]
    else:
        logger.info(
            'Forces in the xyz file are not present, or are not called %s' %
            (forces_label))

    if energy is None:
        logger.info(
            'Energy in the xyz file is not present, or is not called %s' %
            (energy_label))

    # See if there are forces and energies, get them for the chosen atoms
    if (atoms.get_cell() == np.zeros((3, 3))).all():
        atoms.set_cell(100.0 * np.identity(3))
        logger.info('No cell values found, setting to a 100 x 100 x 100 cube')

    # Build local configurations for every indexed atom
    nl = FullNeighborList(r_cut, atoms=atoms)
    confs = []
    for i in atoms_ind:
        indices, positions, distances = nl.get_neighbors(i)

        atomic_numbers_i = np.ones(
            (len(indices), 1)) * atoms.get_atomic_numbers()[i]
        atomic_numbers_j = atoms.get_atomic_numbers()[indices].reshape(-1, 1)
        confs.append(np.hstack([positions, atomic_numbers_i,
                                atomic_numbers_j]))

    return confs, forces, energy
예제 #12
0
    def initialize(self, atoms):
        """ Method which initializes the EMT calculator by defining all needed values used in the
            calculations. """
        # A list, Z, of the element type for each atom in the system is defined:
        self.Z = atoms.get_atomic_numbers()
        # The number of atoms are calculated:
        self.N = len(self.Z)
        # Lists of the values for eta2, kappa, Seq, E0, V0, n0 and L (lambda) for the element types Z are
        # defined:
        # The "largest" element number is corrected with regards to the method of counting in python.
        self.eta2 = numpy.zeros(NumElle)
        self.kappa = numpy.zeros(NumElle)
        self.Seq = numpy.zeros(NumElle)
        self.E0 = numpy.zeros(NumElle)
        self.V0 = numpy.zeros(NumElle)
        self.L = numpy.zeros(NumElle)
        self.n0 = numpy.zeros(NumElle)
        for i in range(NumElle):
            if chemical_symbols[i] in self.parameters:
                self.eta2[i] = self.parameters[chemical_symbols[i]][3] / Bohr
                self.kappa[i] = self.parameters[chemical_symbols[i]][4] / Bohr
                self.Seq[i] = self.parameters[chemical_symbols[i]][1] * Bohr
                self.E0[i] = self.parameters[chemical_symbols[i]][0]
                self.V0[i] = self.parameters[chemical_symbols[i]][2]
                self.L[i] = self.parameters[chemical_symbols[i]][5] / Bohr
                self.n0[i] = self.parameters[chemical_symbols[i]][6] / (Bohr**
                                                                        3)

        # Calculation of the X*X-arrays:
        # r_cut; X*X-array of vaules for the cutoff length for the atompair of type (Z,Z')
        # sigmaaRCUT; X*X-array of values for sigmaa evaluated in r_cut[Z,Z']
        # sigmabRCUT; X*X-array of values for sigmab evaluated in r_cut[Z,Z']
        # dsigmaadrRCUT; X*X-array of values for the deriviative of sigmaa evaluated in r_cut[Z,Z']
        # dsigmabdrRCUT; X*X-array of values for the deriviative of sigmab evaluated in r_cut[Z,Z']
        # chi; X*X-array of values for chi for the atompair of type (Z,Z')

        # The cutoff distances are calculated using that the lattice constant, a0 = sqrt(2)*beta*s0:
        self.r_cut = numpy.zeros([NumElle, NumElle])

        for i in range(NumElle):
            for j in range(NumElle):
                # Check to see if the two elements considered are defined or not. Only calculating an
                # r_cut if both are. r_cut[Z,Z'] is calculated as the cutoff distance for the the one of
                # elements Z,Z' which has the largest a0.
                if self.Seq[i] and self.Seq[j] != 0:
                    self.r_cut[i, j] = (1. / 2. *
                                        (sqrt(3. / 2.) + sqrt(4. / 2.)) *
                                        (sqrt(2) * beta) *
                                        max(self.Seq[i], self.Seq[j]))

        ### Calculations for sigmaaRCUT, sigmabRCUT, d_sigmaadrRCUT and d_sigmabdrRCUT ###

        self.dsigmaadrRCUT = numpy.zeros([NumElle, NumElle])
        self.dsigmabdrRCUT = numpy.zeros([NumElle, NumElle])
        self.sigmaaRCUT = numpy.zeros([NumElle, NumElle])
        self.sigmabRCUT = numpy.zeros([NumElle, NumElle])

        for i in range(NumElle):
            for j in range(NumElle):
                # Check to see if r_cut[i,j] is defined for this pair of elements. r_cut[i,j] == 0 means
                # that it is not defined.
                if self.r_cut[i, j] != 0:
                    self.sigmaaRCUT[i, j] = (numpy.exp(
                        self.eta2[j] *
                        (-self.r_cut[i, j] + self.Seq[j] * beta)))
                    self.sigmabRCUT[i, j] = (numpy.exp(
                        self.kappa[j] *
                        (-self.r_cut[i, j] / beta + self.Seq[j])))
                    self.dsigmaadrRCUT[i,
                                       j] = -self.eta2[j] * self.sigmaaRCUT[i,
                                                                            j]
                    self.dsigmabdrRCUT[
                        i, j] = -self.kappa[j] / beta * self.sigmabRCUT[i, j]

        ### Calculations for chi[Z,Z'] ###

        self.chi = numpy.zeros([NumElle, NumElle])

        for i in range(NumElle):
            for j in range(NumElle):
                # Check to see if the elements i,j are defined.
                if self.n0[i] and self.n0[j] != 0:
                    self.chi[i, j] = self.n0[i] / self.n0[j]

        ### Calculations of gamma1 and gamma2 ###

# Four (3 x NumElle)-arrays for lambda_1,2 (named L_1,2) and sigmaa_1,2 are calculated with the distance,
# r_ij = (beta * Seq, sqrt(2) * beta * Seq, sqrt(3) * beta * Seq) for all supportet elements.
# where Z = Z'.

# The NumberNearestNeighbours variable is set to the number of nearest neighbors included in the model.
        NumberNearestNeighbours = 3
        # arrays for lambda and sigmaa are initialized
        L_1_Z = numpy.zeros([NumberNearestNeighbours, NumElle])
        L_2_Z = numpy.zeros([NumberNearestNeighbours, NumElle])
        sigmaa_Z = numpy.zeros([NumberNearestNeighbours, NumElle])
        sigmab_Z = numpy.zeros([NumberNearestNeighbours, NumElle])
        # The values for each are calculated for each neighbour distance
        for i in range(NumberNearestNeighbours):
            L_1_Z[i] = (
                self.dsigmaadrRCUT[range(NumElle),
                                   range(NumElle)] *
                ((sqrt(1 + i) * beta * self.Seq) -
                 self.r_cut[range(NumElle), range(NumElle)]) +
                self.sigmaaRCUT[range(NumElle), range(NumElle)])

            L_2_Z[i] = (
                self.dsigmabdrRCUT[range(NumElle),
                                   range(NumElle)] *
                ((sqrt(1 + i) * beta * self.Seq) -
                 self.r_cut[range(NumElle), range(NumElle)]) +
                self.sigmabRCUT[range(NumElle), range(NumElle)])

            sigmaa_Z[i] = numpy.exp(
                self.eta2 *
                (-(sqrt(1 + i) * beta * self.Seq) + self.Seq * beta))
            sigmab_Z[i] = numpy.exp(self.kappa *
                                    (-(sqrt(1 + i) * self.Seq) + self.Seq))

        # The factor (self.Seq/self.Seq) is an array of zeros and ones and is only used to secure that only
        # the elements which are actually defined in "parameters" gives a gamma_1,2 different from zero.
        self.gamma1 = ((self.Seq / self.Seq) *
                       (12 * (sigmaa_Z[0] - L_1_Z[0]) + 6 *
                        (sigmaa_Z[1] - L_1_Z[1]) + 24 *
                        (sigmaa_Z[2] - L_1_Z[2])))
        self.gamma2 = ((self.Seq / self.Seq) *
                       (12 * (sigmab_Z[0] - L_2_Z[0]) + 6 *
                        (sigmab_Z[1] - L_2_Z[1]) + 24 *
                        (sigmab_Z[2] - L_2_Z[2])))

        ### Construction of a Full Neighborlist for the system of atoms,
        self.nbList = FullNeighborList(self.r_cut.max(), atoms)

        ### Initialization of the variables holding the forces on and energy of the atoms ###
        self.forces = None
        self.energy = None
예제 #13
0
class EMT:
    """ This class is an implementation of the revised edition of the Effective Medium Theory approach of calculating the energy of a given FCC crystal system. The functional form of the equations used can be found in ******* """
    def __init__(self, Params=None, ZeroPoint=1):
        """ Initializes the EMT object. The input Params is used to specify userdefined parameters and the input 
            Zeropoint is used to specify whether the potential energy measured realtive to E0 = Ecoh (ZeroPoint = 1) 
            or E0 = 0 (ZeroPoint = 0). """
        # Secures that the calculator is initialized correctly the first time it is used.
        self.energy = None
        self.ZeroPoint = ZeroPoint
        # If no parameters have been specified when creating the EMT object the default parameters are used.
        if Params is None:
            self.parameters = parameters
        else:
            self.parameters = Params

    def initialize(self, atoms):
        """ Method which initializes the EMT calculator by defining all needed values used in the
            calculations. """
        # A list, Z, of the element type for each atom in the system is defined:
        self.Z = atoms.get_atomic_numbers()
        # The number of atoms are calculated:
        self.N = len(self.Z)
        # Lists of the values for eta2, kappa, Seq, E0, V0, n0 and L (lambda) for the element types Z are
        # defined:
        # The "largest" element number is corrected with regards to the method of counting in python.
        self.eta2 = numpy.zeros(NumElle)
        self.kappa = numpy.zeros(NumElle)
        self.Seq = numpy.zeros(NumElle)
        self.E0 = numpy.zeros(NumElle)
        self.V0 = numpy.zeros(NumElle)
        self.L = numpy.zeros(NumElle)
        self.n0 = numpy.zeros(NumElle)
        for i in range(NumElle):
            if chemical_symbols[i] in self.parameters:
                self.eta2[i] = self.parameters[chemical_symbols[i]][3] / Bohr
                self.kappa[i] = self.parameters[chemical_symbols[i]][4] / Bohr
                self.Seq[i] = self.parameters[chemical_symbols[i]][1] * Bohr
                self.E0[i] = self.parameters[chemical_symbols[i]][0]
                self.V0[i] = self.parameters[chemical_symbols[i]][2]
                self.L[i] = self.parameters[chemical_symbols[i]][5] / Bohr
                self.n0[i] = self.parameters[chemical_symbols[i]][6] / (Bohr**
                                                                        3)

        # Calculation of the X*X-arrays:
        # r_cut; X*X-array of vaules for the cutoff length for the atompair of type (Z,Z')
        # sigmaaRCUT; X*X-array of values for sigmaa evaluated in r_cut[Z,Z']
        # sigmabRCUT; X*X-array of values for sigmab evaluated in r_cut[Z,Z']
        # dsigmaadrRCUT; X*X-array of values for the deriviative of sigmaa evaluated in r_cut[Z,Z']
        # dsigmabdrRCUT; X*X-array of values for the deriviative of sigmab evaluated in r_cut[Z,Z']
        # chi; X*X-array of values for chi for the atompair of type (Z,Z')

        # The cutoff distances are calculated using that the lattice constant, a0 = sqrt(2)*beta*s0:
        self.r_cut = numpy.zeros([NumElle, NumElle])

        for i in range(NumElle):
            for j in range(NumElle):
                # Check to see if the two elements considered are defined or not. Only calculating an
                # r_cut if both are. r_cut[Z,Z'] is calculated as the cutoff distance for the the one of
                # elements Z,Z' which has the largest a0.
                if self.Seq[i] and self.Seq[j] != 0:
                    self.r_cut[i, j] = (1. / 2. *
                                        (sqrt(3. / 2.) + sqrt(4. / 2.)) *
                                        (sqrt(2) * beta) *
                                        max(self.Seq[i], self.Seq[j]))

        ### Calculations for sigmaaRCUT, sigmabRCUT, d_sigmaadrRCUT and d_sigmabdrRCUT ###

        self.dsigmaadrRCUT = numpy.zeros([NumElle, NumElle])
        self.dsigmabdrRCUT = numpy.zeros([NumElle, NumElle])
        self.sigmaaRCUT = numpy.zeros([NumElle, NumElle])
        self.sigmabRCUT = numpy.zeros([NumElle, NumElle])

        for i in range(NumElle):
            for j in range(NumElle):
                # Check to see if r_cut[i,j] is defined for this pair of elements. r_cut[i,j] == 0 means
                # that it is not defined.
                if self.r_cut[i, j] != 0:
                    self.sigmaaRCUT[i, j] = (numpy.exp(
                        self.eta2[j] *
                        (-self.r_cut[i, j] + self.Seq[j] * beta)))
                    self.sigmabRCUT[i, j] = (numpy.exp(
                        self.kappa[j] *
                        (-self.r_cut[i, j] / beta + self.Seq[j])))
                    self.dsigmaadrRCUT[i,
                                       j] = -self.eta2[j] * self.sigmaaRCUT[i,
                                                                            j]
                    self.dsigmabdrRCUT[
                        i, j] = -self.kappa[j] / beta * self.sigmabRCUT[i, j]

        ### Calculations for chi[Z,Z'] ###

        self.chi = numpy.zeros([NumElle, NumElle])

        for i in range(NumElle):
            for j in range(NumElle):
                # Check to see if the elements i,j are defined.
                if self.n0[i] and self.n0[j] != 0:
                    self.chi[i, j] = self.n0[i] / self.n0[j]

        ### Calculations of gamma1 and gamma2 ###

# Four (3 x NumElle)-arrays for lambda_1,2 (named L_1,2) and sigmaa_1,2 are calculated with the distance,
# r_ij = (beta * Seq, sqrt(2) * beta * Seq, sqrt(3) * beta * Seq) for all supportet elements.
# where Z = Z'.

# The NumberNearestNeighbours variable is set to the number of nearest neighbors included in the model.
        NumberNearestNeighbours = 3
        # arrays for lambda and sigmaa are initialized
        L_1_Z = numpy.zeros([NumberNearestNeighbours, NumElle])
        L_2_Z = numpy.zeros([NumberNearestNeighbours, NumElle])
        sigmaa_Z = numpy.zeros([NumberNearestNeighbours, NumElle])
        sigmab_Z = numpy.zeros([NumberNearestNeighbours, NumElle])
        # The values for each are calculated for each neighbour distance
        for i in range(NumberNearestNeighbours):
            L_1_Z[i] = (
                self.dsigmaadrRCUT[range(NumElle),
                                   range(NumElle)] *
                ((sqrt(1 + i) * beta * self.Seq) -
                 self.r_cut[range(NumElle), range(NumElle)]) +
                self.sigmaaRCUT[range(NumElle), range(NumElle)])

            L_2_Z[i] = (
                self.dsigmabdrRCUT[range(NumElle),
                                   range(NumElle)] *
                ((sqrt(1 + i) * beta * self.Seq) -
                 self.r_cut[range(NumElle), range(NumElle)]) +
                self.sigmabRCUT[range(NumElle), range(NumElle)])

            sigmaa_Z[i] = numpy.exp(
                self.eta2 *
                (-(sqrt(1 + i) * beta * self.Seq) + self.Seq * beta))
            sigmab_Z[i] = numpy.exp(self.kappa *
                                    (-(sqrt(1 + i) * self.Seq) + self.Seq))

        # The factor (self.Seq/self.Seq) is an array of zeros and ones and is only used to secure that only
        # the elements which are actually defined in "parameters" gives a gamma_1,2 different from zero.
        self.gamma1 = ((self.Seq / self.Seq) *
                       (12 * (sigmaa_Z[0] - L_1_Z[0]) + 6 *
                        (sigmaa_Z[1] - L_1_Z[1]) + 24 *
                        (sigmaa_Z[2] - L_1_Z[2])))
        self.gamma2 = ((self.Seq / self.Seq) *
                       (12 * (sigmab_Z[0] - L_2_Z[0]) + 6 *
                        (sigmab_Z[1] - L_2_Z[1]) + 24 *
                        (sigmab_Z[2] - L_2_Z[2])))

        ### Construction of a Full Neighborlist for the system of atoms,
        self.nbList = FullNeighborList(self.r_cut.max(), atoms)

        ### Initialization of the variables holding the forces on and energy of the atoms ###
        self.forces = None
        self.energy = None

    def NeighborList_rcutReduced(self, i):
        """ Method which makes sure that only the neighboratoms within the correct cutoff for the involved 
        element types Z,Z' are included in the calculations by modifying the output of the FullNeighborList
        function. """

        # Relavant data about the neighbor atom, j, for atom i which can possible give a contribution are
        # selected
        (other_j, r_ij, rsq) = self.nbList.get_neighbors(i)

        # The neighbor atoms which will actually give a contribution to the energy, based on the individual
        # cutoff distances between atom i of type Z and atom j of type Z', are selected.

        # The neighbor atoms which fullfill the condition are chosen
        keep = numpy.sqrt(rsq) <= self.r_cut[self.Z[i], self.Z[other_j]]

        # The lists of data about the neighbor atoms are updated
        if len(keep) != 0:
            return (other_j[keep], r_ij[keep], rsq[keep])
        else:
            # nbList returned empty lists, but we cannot index a shape (0,3) array (r_ij)
            # with an empty list (bug in numpy?).
            return (other_j, r_ij, rsq)

    def update(self, atoms):
        """ This method is called by the atoms object to which the calculator is attached, it secures that the 
            energy (and/or force) of the system is recalculated if this is required. """
        need_calc = False
        if (self.energy is None or len(self.Z) != len(atoms)
                or (self.Z != atoms.get_atomic_numbers()).any()):
            # The calculator is initialized with regards to the atoms object.
            self.initialize(atoms)
            need_calc = True
        elif (self.positions != atoms.get_positions()).any():
            # The atoms object has not changed enough for the calculator to need a reinitialization but a
            # new calculation of the value for the energies are still needed.
            need_calc = True
        if need_calc:
            self.positions = atoms.get_positions()
            self.nbList.check_and_update(atoms)
            self.energy = self.calculate_Energy()
            self.forces = self.calculate_Force()

    # Returns the energy of the atoms (the method calculates the energy first if needed be)
    def get_potential_energy(self, atoms):
        self.update(atoms)
        return self.energy

    # Returns the forces on the atoms (the method calculates the forces first if needed be)
    def get_forces(self, atoms):
        self.update(atoms)
        return self.forces.copy()

    def get_stress(self, atoms):
        raise NotImplementedError('No stresses implemented')

    ########## ENERGY Calculations ##########

    ### sigma_1,2 ###

    def calculate_sigma12(self):
        """ Calculates and returns sigma_1 and sigma_2. """
        # The N-arrays for sigma_1,2 are initialized
        sigma_1 = numpy.zeros(self.N)
        sigma_2 = numpy.zeros(self.N)

        for i in range(self.N):
            # The numbers of the neighbor atoms, the relative position vectors and length of the position
            # vectors squared between atom i and the neighbor atoms j are defined in three arrays.
            (other_j, r_ij, rsq) = self.NeighborList_rcutReduced(i)

            # The values for the linear subtracktion functions evaluated at norm(r_ij,2) for all the atom
            # pairs, [i,other_j], are calculated.
            L_1_i = (
                self.dsigmaadrRCUT[self.Z[i], self.Z[other_j]] *
                (numpy.sqrt(rsq) - self.r_cut[self.Z[i], self.Z[other_j]]) +
                self.sigmaaRCUT[self.Z[i], self.Z[other_j]])
            L_2_i = (
                self.dsigmabdrRCUT[self.Z[i], self.Z[other_j]] *
                (numpy.sqrt(rsq) - self.r_cut[self.Z[i], self.Z[other_j]]) +
                self.sigmabRCUT[self.Z[i], self.Z[other_j]])

            # sigmaa_i and sigmab_i are evaluated at norm(r_ij,2) for all the atom pairs, [i,other_j].
            sigmaa_i = (numpy.exp(
                self.eta2[self.Z[other_j]] *
                (-numpy.sqrt(rsq) + self.Seq[self.Z[other_j]] * beta)))
            sigmab_i = (numpy.exp(
                self.kappa[self.Z[other_j]] *
                (-numpy.sqrt(rsq) / beta + self.Seq[self.Z[other_j]])))

            #if (i == 1):
            #    print sigmaa_i

            # The values of sigma_1_i and sigma_2_i are calculated for
            # the atom i. Where max(a,b) is introduced in order to
            # secure a none zero minimumvalue for sigma_1 so the
            # following calculations wont result in an error.
            sigma_1[i] = max(pow(10, -9),
                             (self.chi[self.Z[i], self.Z[other_j]] *
                              (sigmaa_i - L_1_i)).sum())
            sigma_2[i] = (self.chi[self.Z[i], self.Z[other_j]] *
                          (sigmab_i - L_2_i)).sum()

        #TESTER#
        #print sigma_1[:10]

        return (sigma_1, sigma_2)

    ### s_i ###

    def calculate_s(self, sigma_1):
        """ Calculates and returns an N-array containing the neutrality sphere radii, s, for the atoms of the 
        system is calculated."""
        return self.Seq[self.Z] - numpy.log(
            sigma_1 / self.gamma1[self.Z]) / (beta * self.eta2[self.Z])

    ### E_tot ###

    def calculate_Energy_function(self, s, sigma_2):
        """ Calculates and returns the total energy of the system using s and sigma_2. """
        # Calculation of the N-array containing the cohesive energy for each of the N atoms.
        E_c = (self.E0[self.Z] *
               (self.L[self.Z] * (s - self.Seq[self.Z]) + 1) * numpy.exp(
                   -self.L[self.Z] *
                   (s - self.Seq[self.Z]))) - self.E0[self.Z] * self.ZeroPoint

        # Calculation of the N-array containing the atomic sphere correction energy for each of the N atoms.
        E_as = (6 * (self.V0[self.Z] * numpy.exp(-self.kappa[self.Z] *
                                                 (s - self.Seq[self.Z])) -
                     self.V0[self.Z] * sigma_2 / self.gamma2[self.Z]))

        # Calculation of the total energy
        return (E_c + E_as).sum()

    ### Final Energy Calculator ###

    def calculate_Energy(self):
        """ Calculates and returnes the energy of the atoms in the atom object to which the 
            EMT calculator is attached. The calculations are done using the following methods, 
            also defined in EMT.py: calculate_sigma12(self), calculate_s(self,sigma_1), 
            calculate_Energy_function(self,s,sigma_2). """
        (sigma_1, sigma_2) = self.calculate_sigma12()
        s = self.calculate_s(sigma_1)

        # The total energy is calculated and returned
        return self.calculate_Energy_function(s, sigma_2)

    ########## FORCE Calculations ##########

    ### dsdsigma_1 ###

    def calculate_dsdsigma_1(self, sigma_1):
        """ Calculates and returns dsdsigma_1 using sigma_1. """
        # An N-array containing the the deriviative of neutrality sphere radii, s, with regards to sigma_1 for
        # the atoms of the system is calculated.
        dsdsigma_1 = -1 / (beta * self.eta2[self.Z] * sigma_1)

        return dsdsigma_1

    ### dE_cids, dE_asds, dE_asdsigma_2 ###

    def calculate_Deriviative_of_Energy(self, s):
        """ Calculates and returns the deriviatives of E_cs and E_as with regards to s and sigma_2. """
        # Calculation of the N-array containing the deriviative of the cohesive energy with regards to s for
        # each of the N atoms.
        dE_cds = -(self.E0[self.Z] * self.L[self.Z] * self.L[self.Z] *
                   numpy.exp(-self.L[self.Z] *
                             (s - self.Seq[self.Z])) * (s - self.Seq[self.Z]))

        # Calculation of the N-array containing the deriviative of the atomic sphere correction energy with
        # regards to s for each of the N atoms.
        dE_asds = -6 * self.kappa[self.Z] * self.V0[self.Z] * numpy.exp(
            -self.kappa[self.Z] * (s - self.Seq[self.Z]))

        # Calculation of the N-array containing the deriviative of the atomic sphere correction energy with
        # regards to sigma_2 for each of the N atoms.
        dE_asdsigma_2 = -6 * self.V0[self.Z] / (self.gamma2[self.Z])

        return (dE_cds, dE_asds, dE_asdsigma_2)

    ### F_kalpha ###
    def calculate_Force_function(self, dE_cds, dE_asds, dE_asdsigma_2,
                                 dsdsigma_1):
        """ Calculates the force on all k atoms in the three directions {x,y,z} representet by alpha. """

        # An array for the force is initialized
        F = numpy.zeros([self.N, 3])

        for k in range(self.N):
            # The atoms interacting with atom k are selected.
            (other_i, r_ki, rsq) = self.NeighborList_rcutReduced(k)
            #print other_i
            #print r_ki
            #print numpy.sqrt(rsq)

            # The values for dr_ijdr_kalpha are calculated for the relevant atoms, k and other_i.
            dr_kidr_kalpha = r_ki / numpy.sqrt(rsq)[:, numpy.newaxis]

            ## The force on the k'th atom caused by the atoms, other_i's, interactions with k are calculated ##

            # The values for dsigmaa_idr_ij and dsigmab_idr_ij are calculated with regards to the k'th atom
            sigmaa_k = numpy.exp(
                self.eta2[self.Z[other_i]] *
                (-numpy.sqrt(rsq) + self.Seq[self.Z[other_i]] * beta))
            sigmab_k = numpy.exp(
                self.kappa[self.Z[other_i]] *
                (-numpy.sqrt(rsq) / beta + self.Seq[self.Z[other_i]]))

            dsigmaa_kdr_ki = (-self.eta2[self.Z[other_i]] *
                              sigmaa_k)[:, numpy.newaxis]
            dsigmab_kdr_ki = (-self.kappa[self.Z[other_i]] / beta *
                              sigmab_k)[:, numpy.newaxis]

            # Values for dL_1idr_ij and dL_2idr_ij are calculated with regards to the k'th atom
            dL_1kdr_ki = self.dsigmaadrRCUT[self.Z[k],
                                            self.Z[other_i]][:, numpy.newaxis]
            dL_2kdr_ki = self.dsigmabdrRCUT[self.Z[k],
                                            self.Z[other_i]][:, numpy.newaxis]

            # First the value of dsigma1_idr_kaplha and dsigma2_idr_kaplha are calculated for the k'th atom
            dsigma1_kdr_kalpha = (
                self.chi[self.Z[k], self.Z[other_i]][:, numpy.newaxis] *
                (dsigmaa_kdr_ki - dL_1kdr_ki) * dr_kidr_kalpha).sum(axis=0)
            dsigma2_kdr_kalpha = (
                self.chi[self.Z[k], self.Z[other_i]][:, numpy.newaxis] *
                (dsigmab_kdr_ki - dL_2kdr_ki) * dr_kidr_kalpha).sum(axis=0)
            """ TJEK DER SKAL FJERNES SENERE """
            assert len(dsigma1_kdr_kalpha) == 3
            """ TJEK DER SKAL FJERNES SENERE """

            # The contribution to the force on atom k from the k'th atoms interaction with the other_i atoms is
            # calculated
            F[k] = (dE_cds[k] * dsdsigma_1[k] * dsigma1_kdr_kalpha +
                    dE_asds[k] * dsdsigma_1[k] * dsigma1_kdr_kalpha +
                    dE_asdsigma_2[k] * dsigma2_kdr_kalpha)

            # The values for dsigmaa_idr_ij and dsigmab_idr_ij are calculated with regards to the atoms other_i
            # where j = k for all other_i (thus we only need one value of dsigmaa_idr_ij).
            sigmaa_i = numpy.exp(
                self.eta2[self.Z[k]] *
                (-numpy.sqrt(rsq) + self.Seq[self.Z[k]] * beta))
            sigmab_i = numpy.exp(
                self.kappa[self.Z[k]] *
                (-numpy.sqrt(rsq) / beta + self.Seq[self.Z[k]]))

            dsigmaa_idr_ik = (-self.eta2[self.Z[k]] * sigmaa_i)[:,
                                                                numpy.newaxis]
            dsigmab_idr_ik = (-self.kappa[self.Z[k]] / beta *
                              sigmab_i)[:, numpy.newaxis]

            # Values for dL_1idr_ij and dL_2idr_ij are calculated with regards to the atoms other_i
            # where j = k for all other_i.
            dL_1idr_ik = self.dsigmaadrRCUT[self.Z[other_i],
                                            self.Z[k]][:, numpy.newaxis]
            dL_2idr_ik = self.dsigmabdrRCUT[self.Z[other_i],
                                            self.Z[k]][:, numpy.newaxis]

            # First the value of dsigma1_idr_kaplha and dsigma2_idr_kaplha are calculated with regards to the atoms
            # other_i where j are only the atom k for all other_i. (thus the sum only has one element for all other_i.
            # which results in the calculations leading to an [other_i,3]-array.
            dsigma1_idr_kalpha = (
                self.chi[self.Z[other_i], self.Z[k]][:, numpy.newaxis] *
                (dsigmaa_idr_ik - dL_1idr_ik) * (dr_kidr_kalpha))
            dsigma2_idr_kalpha = (
                self.chi[self.Z[other_i], self.Z[k]][:, numpy.newaxis] *
                (dsigmab_idr_ik - dL_2idr_ik) * (dr_kidr_kalpha))

            # The contribution to the force on atom k from the other_i atoms interaction with the k'th atom is now
            # calculated
            F[k] += (
                dE_cds[other_i][:, numpy.newaxis] *
                dsdsigma_1[other_i][:, numpy.newaxis] * dsigma1_idr_kalpha +
                dE_asds[other_i][:, numpy.newaxis] *
                dsdsigma_1[other_i][:, numpy.newaxis] * dsigma1_idr_kalpha +
                dE_asdsigma_2[other_i][:, numpy.newaxis] *
                dsigma2_idr_kalpha).sum(axis=0)
            """ TJEK DER SKAL FJERNES SENERE """
            assert len(F[k]) == 3
            """ TJEK DER SKAL FJERNES SENERE """

        return F

    ### Final Force Calculator ###

    def calculate_Force(self):
        """ Calculates and returnes the force acting on each of the atoms in the atoms object to which the 
            EMT calculator is attached. These calculations are done using the following methods, 
            also defined in EMT.py: calculate_sigma12(self), calculate_s(self,sigma_1), calculate_dsdsigma_1
            (self,sigma_1), calculate_Deriviative_of_Energy(self,s) and calculate_Force_function
            (self,dE_cds,dE_asds,dE_asdsigma_2,dsdsigma_1) """
        (sigma_1, sigma_2) = self.calculate_sigma12()
        s = self.calculate_s(sigma_1)
        dsdsigma_1 = self.calculate_dsdsigma_1(sigma_1)
        (dE_cds, dE_asds,
         dE_asdsigma_2) = self.calculate_Deriviative_of_Energy(s)

        # The force is calculated and returned
        return self.calculate_Force_function(dE_cds, dE_asds, dE_asdsigma_2,
                                             dsdsigma_1)