Example #1
0
File: view.py Project: gjuhasz/ase
    def bind(self, frame):
        if not self.ui.get_widget('/MenuBar/ViewMenu/ShowBonds'
                                  ).get_active():
            self.bonds = np.empty((0, 5), int)
            return
        
        from ase.atoms import Atoms
        from ase.calculators.neighborlist import NeighborList
        nl = NeighborList(self.images.r * 1.5, skin=0, self_interaction=False)
        nl.update(Atoms(positions=self.images.P[frame],
                        cell=(self.images.repeat[:, np.newaxis] *
                              self.images.A[frame]),
                        pbc=self.images.pbc))
        nb = nl.nneighbors + nl.npbcneighbors
        self.bonds = np.empty((nb, 5), int)
        if nb == 0:
            return
        
        n1 = 0
        for a in range(self.images.natoms):
            indices, offsets = nl.get_neighbors(a)
            n2 = n1 + len(indices)
            self.bonds[n1:n2, 0] = a
            self.bonds[n1:n2, 1] = indices
            self.bonds[n1:n2, 2:] = offsets
            n1 = n2

        i = self.bonds[:n2, 2:].any(1)
        self.bonds[n2:, 0] = self.bonds[i, 1]
        self.bonds[n2:, 1] = self.bonds[i, 0]
        self.bonds[n2:, 2:] = -self.bonds[i, 2:]
def get_fingerprints(atoms):
    elements =['O','Ru']
    Gs = make_symmetry_functions(elements)
    fp = np.zeros([2, len(Gs['O'])])
    cutoff ={'name': 'Cosine', 'kwargs': {'Rc': 6.5}}
    cutoff_globle = 6.5
    _nl = NeighborList(cutoffs=([cutoff_globle / 2.] *
                                len(atoms)),
                       self_interaction=False,
                       bothways=True,
                       skin=0.)
    _nl.update(atoms)

    amp_obj = FingerprintCalculator(neighborlist=_nl, Gs=Gs, cutoff=cutoff,fortran=False)
    amp_obj.initialize(fortran=False,atoms=atoms)
    n=0
    fingerprint = []
    for index in range(12,14):
        symbol = atoms[index].symbol
        neighborindices, neighboroffsets = _nl.get_neighbors(index)
        neighborsymbols = [atoms[_].symbol for _ in neighborindices]
        neighborpositions = [atoms.positions[neighbor] + np.dot(offset, atoms.cell)
                             for (neighbor, offset) in zip(neighborindices,
                                                           neighboroffsets)]
        indexfp = amp_obj.get_fingerprint(
            index, symbol, neighborsymbols, neighborpositions)
        fp[n] = indexfp[1]
        n = n+1
    return fp
Example #3
0
def find_tip_coordination(a, bondlength=2.6, bulk_nn=4):
    """
    Find position of tip in crack cluster from coordination
    """
    nl = NeighborList([bondlength / 2.0] * len(a),
                      skin=0.0,
                      self_interaction=False,
                      bothways=True)
    nl.update(a)
    nn = np.array([len(nl.get_neighbors(i)[0]) for i in range(len(a))])
    a.set_array('n_neighb', nn)
    g = a.get_array('groups')

    y = a.positions[:, 1]
    above = (nn < bulk_nn) & (g != 0) & (y > a.cell[1, 1] / 2.0)
    below = (nn < bulk_nn) & (g != 0) & (y < a.cell[1, 1] / 2.0)

    a.set_array('above', above)
    a.set_array('below', below)

    bond1 = np.asscalar(above.nonzero()[0][a.positions[above, 0].argmax()])
    bond2 = np.asscalar(below.nonzero()[0][a.positions[below, 0].argmax()])

    # These need to be ints, otherwise they are no JSON serializable.
    a.info['bond1'] = bond1
    a.info['bond2'] = bond2

    return bond1, bond2
Example #4
0
File: pov.py Project: jboes/ase
def get_bondpairs(atoms, radius=1.1):
    """Get all pairs of bonding atoms

    Return all pairs of atoms which are closer than radius times the
    sum of their respective covalent radii.  The pairs are returned as
    tuples::

      (a, b, (i1, i2, i3))

    so that atoms a bonds to atom b displaced by the vector::

        _     _     _
      i c + i c + i c ,
       1 1   2 2   3 3

    where c1, c2 and c3 are the unit cell vectors and i1, i2, i3 are
    integers."""
    
    from ase.data import covalent_radii
    from ase.calculators.neighborlist import NeighborList
    cutoffs = radius * covalent_radii[atoms.numbers]
    nl = NeighborList(cutoffs=cutoffs, self_interaction=False)
    nl.update(atoms)
    bondpairs = []
    for a in range(len(atoms)):
        indices, offsets = nl.get_neighbors(a)
        bondpairs.extend([(a, a2, offset)
                          for a2, offset in zip(indices, offsets)])
    return bondpairs
Example #5
0
File: pov.py Project: lqcata/ase
def get_bondpairs(atoms, radius=1.1):
    """Get all pairs of bonding atoms

    Return all pairs of atoms which are closer than radius times the
    sum of their respective covalent radii.  The pairs are returned as
    tuples::

      (a, b, (i1, i2, i3))

    so that atoms a bonds to atom b displaced by the vector::

        _     _     _
      i c + i c + i c ,
       1 1   2 2   3 3

    where c1, c2 and c3 are the unit cell vectors and i1, i2, i3 are
    integers."""
    
    from ase.data import covalent_radii
    from ase.calculators.neighborlist import NeighborList
    cutoffs = radius * covalent_radii[atoms.numbers]
    nl = NeighborList(cutoffs=cutoffs, self_interaction=False)
    nl.update(atoms)
    bondpairs = []
    for a in range(len(atoms)):
        indices, offsets = nl.get_neighbors(a)
        bondpairs.extend([(a, a2, offset)
                          for a2, offset in zip(indices, offsets)])
    return bondpairs
def calculate_image_center(atoms, watchindex, pdirs):
    """Calculates the center of the image in the pdir basis coordinates."""

    # The first moment of the image around the center atom is calculated.
    atoms = atoms.copy()
    from ase.calculators.neighborlist import NeighborList
    _nl = NeighborList(cutoffs=([6.5 / 2.] * len(atoms)),
                       self_interaction=False,
                       bothways=True,
                       skin=0.)
    _nl.update(atoms)

    position = atoms.positions[watchindex]

    # Step 1: Calculating neighbors of atom.
    n_indices, n_offsets = _nl.get_neighbors(watchindex)
    Rs = [atoms.positions[n_index] +
          np.dot(n_offset, atoms.get_cell()) - position
          for n_index, n_offset in zip(n_indices, n_offsets)]

    xtilde = 0.
    ytilde = 0.
    ztilde = 0.
    for rs in Rs:
        xtilde += rs[0] * np.dot([1, 0, 0], pdirs[0])
        xtilde += rs[1] * np.dot([0, 1, 0], pdirs[0])
        xtilde += rs[2] * np.dot([0, 0, 1], pdirs[0])
        ytilde += rs[0] * np.dot([1, 0, 0], pdirs[1])
        ytilde += rs[1] * np.dot([0, 1, 0], pdirs[1])
        ytilde += rs[2] * np.dot([0, 0, 1], pdirs[1])
        ztilde += rs[0] * np.dot([1, 0, 0], pdirs[2])
        ztilde += rs[1] * np.dot([0, 1, 0], pdirs[2])
        ztilde += rs[2] * np.dot([0, 0, 1], pdirs[2])

    return xtilde, ytilde, ztilde
 def __init__(self, filename):
     atoms = ase.io.read(filename, format='vasp')
     cell = atoms.get_cell()
     symb = atoms.get_atomic_numbers()
     
     cutoffs = np.ones((len(atoms))) * 1.35  # radius around each atom (half the max bondlength)
     nl = NeighborList(cutoffs, 0., self_interaction = False, bothways = False)
     nl.update(atoms)
     
     std_len = 2.35805097505
     
     # Loop over atoms
     bond_no = 0
     bonds = np.zeros((nl.nneighbors,4))
     
     for atom1idx, (indices, offsets, atom1pos) in enumerate(zip(nl.neighbors, nl.displacements, nl.positions)):
         # Loop over bonds
         for atom2idx, offset in zip(indices, offsets):
             #if symb[atom1idx] == symb[atom2idx] == 'Si': 
             atom2pos = nl.positions[atom2idx] + np.dot(offset, cell)
             #print "pos[%i] = %.2f %.2f %.2f" % (atom1idx, atom1pos[0],atom1pos[1],atom1pos[2])
             #print "pos[%i] = %.2f %.2f %.2f" % (atom2idx, atom2pos[0],atom2pos[1],atom2pos[2])
             bond = atom2pos - atom1pos
             bond_center = atom1pos + bond/2.
             bond_z = bond_center[2]
             bondlength = np.sqrt(np.dot(bond,bond))    
             bonds[bond_no] = [bond_z, bondlength, symb[atom1idx], symb[atom2idx]]
             bond_no += 1
     self.bonds = bonds
     print bonds
Example #8
0
def find_tip_coordination(a, bondlength=2.6, bulk_nn=4):
    """
    Find position of tip in crack cluster from coordination
    """
    nl = NeighborList([bondlength/2.0]*len(a),
                      skin=0.0,
                      self_interaction=False,
                      bothways=True)
    nl.update(a)
    nn = np.array([len(nl.get_neighbors(i)[0]) for i in range(len(a))])
    a.set_array('n_neighb', nn)
    g = a.get_array('groups')

    y = a.positions[:, 1]
    above = (nn < bulk_nn) & (g != 0) & (y > a.cell[1,1]/2.0)
    below = (nn < bulk_nn) & (g != 0) & (y < a.cell[1,1]/2.0)

    a.set_array('above', above)
    a.set_array('below', below)

    bond1 = np.asscalar(above.nonzero()[0][a.positions[above, 0].argmax()])
    bond2 = np.asscalar(below.nonzero()[0][a.positions[below, 0].argmax()])

    # These need to be ints, otherwise they are no JSON serializable.
    a.info['bond1'] = bond1
    a.info['bond2'] = bond2

    return bond1, bond2
Example #9
0
    def update(self, atoms):
        # check all the elements are available in the potential
        self.Nelements = len(self.elements)
        elements = np.unique(atoms.get_chemical_symbols())
        unavailable = np.logical_not(
            np.array([item in self.elements for item in elements]))

        if np.any(unavailable):
            raise RuntimeError('These elements are not in the potential: %s' %
                               elements[unavailable])

        # cutoffs need to be a vector for NeighborList
        cutoffs = self.cutoff * np.ones(len(atoms))

        # convert the elements to an index of the position
        # in the eam format
        self.index = np.array(
            [self.elements.index(el) for el in atoms.get_chemical_symbols()])
        self.pbc = atoms.get_pbc()

        # since we need the contribution of all neighbors to the
        # local electron density we cannot just calculate and use
        # one way neighbors
        self.neighbors = NeighborList(cutoffs,
                                      skin=self.parameters.skin,
                                      self_interaction=False,
                                      bothways=True)
        self.neighbors.update(atoms)
Example #10
0
    def bind(self, frame):
        if not self.ui.get_widget('/MenuBar/ViewMenu/ShowBonds').get_active():
            self.bonds = np.empty((0, 5), int)
            return

        from ase.atoms import Atoms
        from ase.calculators.neighborlist import NeighborList
        nl = NeighborList(self.images.r * 1.5, skin=0, self_interaction=False)
        nl.update(
            Atoms(positions=self.images.P[frame],
                  cell=(self.images.repeat[:, np.newaxis] *
                        self.images.A[frame]),
                  pbc=self.images.pbc))
        nb = nl.nneighbors + nl.npbcneighbors
        self.bonds = np.empty((nb, 5), int)
        if nb == 0:
            return

        n1 = 0
        for a in range(self.images.natoms):
            indices, offsets = nl.get_neighbors(a)
            n2 = n1 + len(indices)
            self.bonds[n1:n2, 0] = a
            self.bonds[n1:n2, 1] = indices
            self.bonds[n1:n2, 2:] = offsets
            n1 = n2

        i = self.bonds[:n2, 2:].any(1)
        self.bonds[n2:, 0] = self.bonds[i, 1]
        self.bonds[n2:, 1] = self.bonds[i, 0]
        self.bonds[n2:, 2:] = -self.bonds[i, 2:]
Example #11
0
    def __init__(self, filename):
        atoms = ase.io.read(filename, format='vasp')
        cell = atoms.get_cell()
        symb = atoms.get_atomic_numbers()

        cutoffs = np.ones(
            (len(atoms)
             )) * 1.35  # radius around each atom (half the max bondlength)
        nl = NeighborList(cutoffs, 0., self_interaction=False, bothways=False)
        nl.update(atoms)

        std_len = 2.35805097505

        # Loop over atoms
        bond_no = 0
        bonds = np.zeros((nl.nneighbors, 4))

        for atom1idx, (indices, offsets, atom1pos) in enumerate(
                zip(nl.neighbors, nl.displacements, nl.positions)):
            # Loop over bonds
            for atom2idx, offset in zip(indices, offsets):
                #if symb[atom1idx] == symb[atom2idx] == 'Si':
                atom2pos = nl.positions[atom2idx] + np.dot(offset, cell)
                #print "pos[%i] = %.2f %.2f %.2f" % (atom1idx, atom1pos[0],atom1pos[1],atom1pos[2])
                #print "pos[%i] = %.2f %.2f %.2f" % (atom2idx, atom2pos[0],atom2pos[1],atom2pos[2])
                bond = atom2pos - atom1pos
                bond_center = atom1pos + bond / 2.
                bond_z = bond_center[2]
                bondlength = np.sqrt(np.dot(bond, bond))
                bonds[bond_no] = [
                    bond_z, bondlength, symb[atom1idx], symb[atom2idx]
                ]
                bond_no += 1
        self.bonds = bonds
        print bonds
 def calculate(self, image, key):
     cutoff = self.globals.cutoff
     n = NeighborList(cutoffs=[cutoff / 2.] * len(image),
                      self_interaction=False,
                      bothways=True,
                      skin=0.)
     n.update(image)
     return [n.get_neighbors(index) for index in xrange(len(image))]
Example #13
0
File: opls.py Project: grhawk/ASE
 def update_neighbor_list(self, atoms):
     cut = 0.5 * max(self.data['cutoffs'].values())
     self.nl = NeighborList([cut] * len(atoms),
                            skin=0,
                            bothways=True,
                            self_interaction=False)
     self.nl.update(atoms)
     self.atoms = atoms
Example #14
0
    def update(self, atoms):

        if (self.neighbors is None) or (len(self.neighbors.cutoffs) !=
                                        len(atoms)):
            cutoffs = self.cutoff * np.ones(len(atoms))
            self.neighbors = NeighborList(cutoffs,
                                          self_interaction=False,
                                          bothways=True)
        self.neighbors.update(atoms)
Example #15
0
    def initialize(self, atoms):
        self.par = {}
        self.rc = 0.0
        self.numbers = atoms.get_atomic_numbers()
        maxseq = 0.0
        seen = {}
        for Z in self.numbers:
            if Z not in seen:
                seen[Z] = True
                ss = parameters[chemical_symbols[Z]][1] * Bohr
                if maxseq < ss:
                    maxseq = ss
        rc = self.rc = beta * maxseq * 0.5 * (sqrt(3) + sqrt(4))
        rr = rc * 2 * sqrt(4) / (sqrt(3) + sqrt(4))
        self.acut = np.log(9999.0) / (rr - rc)
        for Z in self.numbers:
            if Z not in self.par:
                p = parameters[chemical_symbols[Z]]
                s0 = p[1] * Bohr
                eta2 = p[3] / Bohr
                kappa = p[4] / Bohr
                x = eta2 * beta * s0
                gamma1 = 0.0
                gamma2 = 0.0
                for i, n in enumerate([12, 6, 24]):
                    r = s0 * beta * sqrt(i + 1)
                    x = n / (12 * (1.0 + exp(self.acut * (r - rc))))
                    gamma1 += x * exp(-eta2 * (r - beta * s0))
                    gamma2 += x * exp(-kappa / beta * (r - beta * s0))

                self.par[Z] = {'E0': p[0],
                               's0': s0,
                               'V0': p[2],
                               'eta2': eta2,
                               'kappa': kappa,
                               'lambda': p[5] / Bohr,
                               'n0': p[6] / Bohr**3,
                               'rc': rc,
                               'gamma1': gamma1,
                               'gamma2': gamma2}
                #if rc + 0.5 > self.rc:
                #    self.rc = rc + 0.5

        self.ksi = {}
        for s1, p1 in self.par.items():
            self.ksi[s1] = {}
            for s2, p2 in self.par.items():
                #self.ksi[s1][s2] = (p2['n0'] / p1['n0'] *
                #                    exp(eta1 * (p1['s0'] - p2['s0'])))
                self.ksi[s1][s2] = p2['n0'] / p1['n0']
                
        self.forces = np.empty((len(atoms), 3))
        self.sigma1 = np.empty(len(atoms))
        self.deds = np.empty(len(atoms))
                    
        self.nl = NeighborList([0.5 * self.rc + 0.25] * len(atoms),
                               self_interaction=False)
Example #16
0
    def calculate(self,
                  atoms=None,
                  properties=['energy'],
                  system_changes=all_changes):
        Calculator.calculate(self, atoms, properties, system_changes)

        natoms = len(self.atoms)

        sigma = self.parameters.sigma
        epsilon = self.parameters.epsilon
        rc = self.parameters.rc
        if rc is None:
            rc = 3 * sigma

        if 'numbers' in system_changes:
            self.nl = NeighborList([rc / 2] * natoms, self_interaction=False)

        self.nl.update(self.atoms)

        positions = self.atoms.positions
        cell = self.atoms.cell

        e0 = 4 * epsilon * ((sigma / rc)**12 - (sigma / rc)**6)

        energy = 0.0
        forces = np.zeros((natoms, 3))
        stress = np.zeros((3, 3))

        for a1 in range(natoms):
            neighbors, offsets = self.nl.get_neighbors(a1)
            cells = np.dot(offsets, cell)
            d = positions[neighbors] + cells - positions[a1]
            r2 = (d**2).sum(1)
            c6 = (sigma**2 / r2)**3
            c6[r2 > rc**2] = 0.0
            energy -= e0 * (c6 != 0.0).sum()
            c12 = c6**2
            energy += 4 * epsilon * (c12 - c6).sum()
            f = (24 * epsilon * (2 * c12 - c6) / r2)[:, np.newaxis] * d
            #print d
            #print r2**.5
            #print offsets
            #print f
            #print neighbors
            forces[a1] -= f.sum(axis=0)
            for a2, f2 in zip(neighbors, f):
                forces[a2] += f2
            stress += np.dot(f.T, d)

        #stress = np.dot(stress, cell)
        stress += stress.T.copy()
        stress *= -0.5 / self.atoms.get_volume()

        self.results['energy'] = energy
        self.results['forces'] = forces
        self.results['stress'] = stress.flat[[0, 4, 8, 5, 2, 1]]
Example #17
0
def vacancy_energy(bulk, remove_index=0):
    Nat = bulk.get_number_of_atoms()
    vac = bulk.copy()
    vac.set_calculator(bulk.get_calculator())

    nl = NeighborList([a0*sqrt(3.0)/4*0.6]*len(bulk), self_interaction=False, bothways=True)
    nl.update(bulk)
    indices, offsets = nl.get_neighbors(remove_index)
    offset_factor=0.13
    for i, offset in zip(indices, offsets):
       ri = vac.positions[remove_index] - (vac.positions[i] + dot(offset, vac.get_cell()))
       vac.positions[i] += offset_factor*ri
       offset_factor += 0.01

    del vac[remove_index] # remove an atom to introduce a vacancy

    # perturb positions
    vac.rattle(0.1)

    ##
    try:
        model.calculator.set(local_gap_error='local_gap_error')
        vac.get_potential_energy()
        unrelaxed_local_gap_error_max = amax(model.calculator.results['local_gap_error'])
        unrelaxed_local_gap_error_sum = sum(model.calculator.results['local_gap_error'])
    except:
        unrelaxed_local_gap_error_max = None
        unrelaxed_local_gap_error_sum = None
    if isinstance(model, Potential):
        model.calculator.set(local_gap_error='')

    # relax atom positions, holding cell fixed
    vac = relax_atoms(vac, tol=fmax, traj_file="model-"+model.name+"-test-vacancy-energy.opt.xyz")

    vac.write("model-"+model.name+"test-vacancy-energy-relaxed.xyz")

    ##
    try:
        model.calculator.set(local_gap_error='local_gap_error')
        vac.get_potential_energy()
        relaxed_local_gap_error_max = amax(model.calculator.results['local_gap_error'])
        relaxed_local_gap_error_sum = sum(model.calculator.results['local_gap_error'])
    except:
        relaxed_local_gap_error_max = None
        relaxed_local_gap_error_sum = None
    if isinstance(model, Potential):
        model.calculator.set(local_gap_error='')
    ##

    # compute vacancy formation energy as difference of bulk and vac energies
    print 'bulk cell energy', bulk_energy
    print 'vacancy cell energy', vac.get_potential_energy()
    e_form = vac.get_potential_energy() - bulk_energy*vac.get_number_of_atoms()/bulk.get_number_of_atoms()
    print 'vacancy formation energy', e_form
    return (e_form, unrelaxed_local_gap_error_max, unrelaxed_local_gap_error_sum, relaxed_local_gap_error_max, relaxed_local_gap_error_sum)
Example #18
0
 def buildNeighborList(self):
     atoms = self.atoms
     nl = NeighborList([0.8 for atom in atoms],
                       self_interaction=False, bothways=True)
     nl.update(atoms)
     self.nl = nl
     neigh = []
     for i in range(self.natom):
         neigh.append(self.sort_nei(i))
         # print neigh[i]
     self.neigh = neigh
Example #19
0
 def buildNeighborList(self):
     atoms = self.atoms
     nl = NeighborList([0.8 for atom in atoms],
                       self_interaction=False,
                       bothways=True)
     nl.update(atoms)
     self.nl = nl
     neigh = []
     for i in range(self.natom):
         neigh.append(self.sort_nei(i))
         # print neigh[i]
     self.neigh = neigh
Example #20
0
	def testNeighborlist(self):
		atoms=read('range',format='lammps')
		atoms. set_pbc((1, 1, 1))
		nl = NeighborList([0.8 for atom in atoms],self_interaction=False,bothways=True)
		nl.update(atoms)
		ang=[]		
		for i in xrange(3):
			indices, offsets = nl. get_neighbors(i)
			angs=[]
			for j, offset in zip(indices, offsets):
				pos= atoms. positions[j] + dot(offset, atoms. get_cell())-atoms.positions[i]
				ang1=atan2(pos[1],pos[0])+pi
				angs.append((j,ang1))
			newangs=sorted(angs,key=lambda d:d[1])
			print newangs
Example #21
0
    def update(self, atoms):
        # check all the elements are available in the potential
        elements = np.unique(atoms.get_chemical_symbols())
        unavailable = np.logical_not(
            np.array([item in self.elements for item in elements]))
        if np.any(unavailable):
            raise RuntimeError('These elements are not in the potential: %s' %
                               elements[unavailable])
                    
        # check if anything has changed to require re-calculation
        if (self.positions is None or
            len(self.positions) != len(atoms.get_positions()) or
            (self.positions != atoms.get_positions()).any() or
            (self.cell != atoms.get_cell()).any() or
            (self.pbc != atoms.get_pbc()).any()):

            # cutoffs need to be a vector for NeighborList
            cutoffs = self.cutoff * np.ones(len(atoms))

            # convert the elements to an index of the position
            # in the eam format
            self.index = np.array([atoms.calc.elements.index(el)
                                   for el in atoms.get_chemical_symbols()])
            self.pbc = atoms.get_pbc()

            # since we need the contribution of all neighbors to the
            # local electron density we cannot just calculate and use
            # one way neighbors
            self.neighbors = NeighborList(cutoffs, skin=self.skin,
                                          self_interaction=False,
                                          bothways=True)
            self.neighbors.update(atoms)
            self.calculate(atoms)
Example #22
0
    def find_connected(self, index, dmax=None, scale=1.5):
        """Find the atoms connected to self[index] and return them.

        If dmax is not None:
        Atoms are defined to be connected if they are nearer than dmax
        to each other.

        If dmax is None:
        Atoms are defined to be connected if they are nearer than the
        sum of their covalent radii * scale to each other.

        """

        # set neighbor lists
        neighborlist = []
        if dmax is None:
            # define neighbors according to covalent radii
            radii = scale * covalent_radii[self.get_atomic_numbers()]
            for atom in self:
                positions = self.positions - atom.position
                distances = np.sqrt(np.sum(positions**2, axis=1))
                radius = scale * covalent_radii[atom.get_atomic_number()]
                neighborlist.append(np.where(distances < radii + radius)[0])
        else:
            # define neighbors according to distance
            nl = NeighborList([0.5 * dmax] * len(self), skin=0)
            nl.update(self)
            for i, atom in enumerate(self):
                neighborlist.append(list(nl.get_neighbors(i)[0]))

        connected = list(neighborlist[index])
        isolated = False
        while not isolated:
            isolated = True
            for i in connected:
                for j in neighborlist[i]:
                    if j in connected:
                        pass
                    else:
                        connected.append(j)
                        isolated = False 
 
        atoms = Cluster()
        for i in connected:
            atoms.append(self[i])

        return atoms
Example #23
0
    def relax(self, individual):
        """Relaxes the individual using a hard-sphere cutoff method.
        Args:
            individual (Individual):  the individual to relax
        """
        rank = gparameters.mpi.rank
        print("Relaxing individual {} on rank {} with hard-sphere cutoff method".format(individual.id, rank))
        radii = [2.0 for atom in individual]
        nl = NeighborList(radii, bothways=True, self_interaction=False)
        nl.update(individual)

        ntries = 0
        modified = True
        while modified and ntries < 100:
            modified = False
            for atom in individual:
                indices, offsets = nl.get_neighbors(atom.index)
                for neigh in indices:
                    if individual.get_distance(atom.index, neigh) < self.cutoff:
                        individual.set_distance(atom.index, neigh, self.cutoff, fix=0.5)
                        modified = True
            nl.update(individual)
            individual.wrap()
            ntries += 1
        if ntries == 100:
            print("WARNING! Iterated through the hard-sphere cutoff relaxation 100 times and it still did not converge!")
Example #24
0
 def testNeighborlist(self):
     atoms = read('range', format='lammps')
     atoms.set_pbc((1, 1, 1))
     nl = NeighborList([0.8 for atom in atoms],
                       self_interaction=False,
                       bothways=True)
     nl.update(atoms)
     ang = []
     for i in xrange(3):
         indices, offsets = nl.get_neighbors(i)
         angs = []
         for j, offset in zip(indices, offsets):
             pos = atoms.positions[j] + dot(
                 offset, atoms.get_cell()) - atoms.positions[i]
             ang1 = atan2(pos[1], pos[0]) + pi
             angs.append((j, ang1))
         newangs = sorted(angs, key=lambda d: d[1])
         print newangs
Example #25
0
File: lj.py Project: PHOTOX/fuase
    def calculate(self, atoms=None,
                  properties=['energy'],
                  system_changes=all_changes):
        Calculator.calculate(self, atoms, properties, system_changes)

        natoms = len(self.atoms)

        sigma = self.parameters.sigma
        epsilon = self.parameters.epsilon
        rc = self.parameters.rc
        if rc is None:
            rc = 3 * sigma
        
        if 'numbers' in system_changes:
            self.nl = NeighborList([rc / 2] * natoms, self_interaction=False)

        self.nl.update(self.atoms)
        
        positions = self.atoms.positions
        cell = self.atoms.cell
        
        e0 = 4 * epsilon * ((sigma / rc)**12 - (sigma / rc)**6)
        
        energy = 0.0
        forces = np.zeros((natoms, 3))
        stress = np.zeros((3, 3))

        for a1 in range(natoms):
            neighbors, offsets = self.nl.get_neighbors(a1)
            cells = np.dot(offsets, cell)
            d = positions[neighbors] + cells - positions[a1]
            r2 = (d**2).sum(1)
            c6 = (sigma**2 / r2)**3
            c6[r2 > rc**2] = 0.0
            energy -= e0 * (c6 != 0.0).sum()
            c12 = c6**2
            energy += 4 * epsilon * (c12 - c6).sum()
            f = (24 * epsilon * (2 * c12 - c6) / r2)[:, np.newaxis] * d
            #print d
            #print r2**.5
            #print offsets
            #print f
            #print neighbors
            forces[a1] -= f.sum(axis=0)
            for a2, f2 in zip(neighbors, f):
                forces[a2] += f2
            stress += np.dot(f.T, d)
        
        #stress = np.dot(stress, cell)
        stress += stress.T.copy()
        stress *= -0.5 / self.atoms.get_volume()
        
        self.results['energy'] = energy
        self.results['forces'] = forces
        self.results['stress'] = stress.flat[[0, 4, 8, 5, 2, 1]]
Example #26
0
class NeighborPairs:
    """Class for looping over pairs of atoms using a neighbor list."""
    def __init__(self, cutoff_a, cell_cv, pbc_c, self_interaction):
        self.neighbors = NeighborList(cutoff_a, skin=0, sorted=True, 
                                      self_interaction=self_interaction)
        self.atoms = Atoms('X%d' % len(cutoff_a), cell=cell_cv, pbc=pbc_c)
        # Warning: never use self.atoms.get_scaled_positions() for
        # anything.  Those positions suffer from roundoff errors!
        
    def set_positions(self, spos_ac):
        self.spos_ac = spos_ac
        self.atoms.set_scaled_positions(spos_ac)
        self.neighbors.update(self.atoms)

    def iter(self):
        cell_cv = self.atoms.cell
        for a1, spos1_c in enumerate(self.spos_ac):
            a2_a, offsets = self.neighbors.get_neighbors(a1)
            for a2, offset in zip(a2_a, offsets):
                spos2_c = self.spos_ac[a2] + offset
                R_c = np.dot(spos2_c - spos1_c, cell_cv)
                yield a1, a2, R_c, offset
Example #27
0
File: emt.py Project: PHOTOX/fuase
    def initialize(self, atoms):
        self.par = {}
        self.rc = 0.0
        self.numbers = atoms.get_atomic_numbers()
        maxseq = max(par[1] for par in parameters.values()) * Bohr
        rc = self.rc = beta * maxseq * 0.5 * (sqrt(3) + sqrt(4))
        rr = rc * 2 * sqrt(4) / (sqrt(3) + sqrt(4))
        self.acut = np.log(9999.0) / (rr - rc)
        for Z in self.numbers:
            if Z not in self.par:
                p = parameters[chemical_symbols[Z]]
                s0 = p[1] * Bohr
                eta2 = p[3] / Bohr
                kappa = p[4] / Bohr
                x = eta2 * beta * s0
                gamma1 = 0.0
                gamma2 = 0.0
                for i, n in enumerate([12, 6, 24]):
                    r = s0 * beta * sqrt(i + 1)
                    x = n / (12 * (1.0 + exp(self.acut * (r - rc))))
                    gamma1 += x * exp(-eta2 * (r - beta * s0))
                    gamma2 += x * exp(-kappa / beta * (r - beta * s0))

                self.par[Z] = {'E0': p[0],
                               's0': s0,
                               'V0': p[2],
                               'eta2': eta2,
                               'kappa': kappa,
                               'lambda': p[5] / Bohr,
                               'n0': p[6] / Bohr**3,
                               'rc': rc,
                               'gamma1': gamma1,
                               'gamma2': gamma2}
                #if rc + 0.5 > self.rc:
                #    self.rc = rc + 0.5

        self.ksi = {}
        for s1, p1 in self.par.items():
            self.ksi[s1] = {}
            for s2, p2 in self.par.items():
                #self.ksi[s1][s2] = (p2['n0'] / p1['n0'] *
                #                    exp(eta1 * (p1['s0'] - p2['s0'])))
                self.ksi[s1][s2] = p2['n0'] / p1['n0']
                
        self.forces = np.empty((len(atoms), 3))
        self.sigma1 = np.empty(len(atoms))
        self.deds = np.empty(len(atoms))
                    
        self.nl = NeighborList([0.5 * self.rc + 0.25] * len(atoms),
                               self_interaction=False)
Example #28
0
class NeighborPairs:
    """Class for looping over pairs of atoms using a neighbor list."""
    def __init__(self, cutoff_a, cell_cv, pbc_c, self_interaction):
        self.neighbors = NeighborList(cutoff_a,
                                      skin=0,
                                      sorted=True,
                                      self_interaction=self_interaction)
        self.atoms = Atoms('X%d' % len(cutoff_a), cell=cell_cv, pbc=pbc_c)
        # Warning: never use self.atoms.get_scaled_positions() for
        # anything.  Those positions suffer from roundoff errors!

    def set_positions(self, spos_ac):
        self.spos_ac = spos_ac
        self.atoms.set_scaled_positions(spos_ac)
        self.neighbors.update(self.atoms)

    def iter(self):
        cell_cv = self.atoms.cell
        for a1, spos1_c in enumerate(self.spos_ac):
            a2_a, offsets = self.neighbors.get_neighbors(a1)
            for a2, offset in zip(a2_a, offsets):
                spos2_c = self.spos_ac[a2] + offset
                R_c = np.dot(spos2_c - spos1_c, cell_cv)
                yield a1, a2, R_c, offset
Example #29
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
Example #30
0
    def find_connected(self, index, dmax=None, scale=1.5):
        """Find the atoms connected to self[index] and return them.

        If dmax is not None:
        Atoms are defined to be connected if they are nearer than dmax
        to each other.

        If dmax is None:
        Atoms are defined to be connected if they are nearer than the
        sum of their covalent radii * scale to each other.

        """

        if index < 0:
            index = len(self) + index

        # set neighbor lists
        if dmax is None:
            # define neighbors according to covalent radii
            radii = scale * covalent_radii[self.get_atomic_numbers()]
        else:
            # define neighbors according to distance
            radii = [0.5 * dmax] * len(self)
        nl = NeighborList(radii, skin=0, self_interaction=False, bothways=True)
        nl.update(self)

        connected = [index] + list(nl.get_neighbors(index)[0])
        isolated = False
        while not isolated:
            isolated = True
            for i in connected:
                for j in nl.get_neighbors(i)[0]:
                    if j in connected:
                        pass
                    else:
                        connected.append(j)
                        isolated = False

        atoms = Cluster()
        for i in connected:
            atoms.append(self[i])

        return atoms
Example #31
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
Example #32
0
def lattice_alteration_nn(indiv, Optimizer):
    """Move function to perform random move along random axis for nearest neighbor distance to random atoms
    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, swaps, stro = find_defects(
                indiv[0], Optimizer.solidbulk, 0)
            ind = indc.copy()
            ind.extend(indb)
        else:
            ind = indiv[0].copy()
            indc = indiv[0].copy()
    else:
        ind = indiv[0].copy()
        indc = indiv[0].copy()
    if len(indc) != 0:
        ctoff1 = [1.0 for one in ind]
        nl = NeighborList(ctoff1, bothways=True, self_interaction=False)
        nl.update(ind)
        try:
            natomsmove = random.randint(1, len(indc) / 2)
        except ValueError:
            natomsmove = 1
        passn = 0
        for count in range(natomsmove):
            try:
                indexmv = random.choice([i for i in range(len(indc))])
                indices, offsets = nl.get_neighbors(indexmv)
                nns = Atoms()
                nns.append(ind[indexmv])
                for index, d in zip(indices, offsets):
                    index = int(index)
                    pos = ind[index].position + numpy.dot(d, ind.get_cell())
                    nns.append(Atom(symbol=ind[index].symbol, position=pos))
                dist = [nns.get_distance(0, i) for i in range(1, len(nns))]
                r = sum(dist) / len(dist)
                dir = random.choice([[1, 0, 0], [-1, 0, 0], [0, 1, 0],
                                     [0, -1, 0], [0, 0, 1], [0, 0, -1]])
                ind[indexmv].position += [i * r for i in dir]
            except:
                passn += 1
        indiv[0] = ind.copy()
    else:
        natomsmove = 0
        passn = 0
    Optimizer.output.write(
        'Lattice Alteration NN Mutation performed on individual\n')
    Optimizer.output.write('Index = ' + repr(indiv.index) + '\n')
    natomsmove -= passn
    Optimizer.output.write('Number of atoms moved = ' + repr(natomsmove) +
                           '\n')
    Optimizer.output.write(repr(indiv[0]) + '\n')
    muttype = 'LANN' + 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
Example #33
0
def find_defects(solid, bulko, rcutoff, atomlistcheck = False, trackvacs = False, trackswaps = False, debug = False, dcheck = 0.6):
    """Function to find interstitials, vacancies, and substitutional atoms (swaps) in a defected structure.
    Identifies species by comparison to perfect structure.
    Inputs:
        solid = ASE atoms class for defected structure
        bulko = ASE atoms class for perfect structure
        rcutoff = float value of distance to surrounding atoms to include
        atomlistcheck = False/list of atom types and concentrations according to atomlist format
        trackvacs = True/False whether or not to identify vacancies in defect
        trackswaps = True/False whether or not to identify substitutional defects
        debug = False/file object to write debug structures"""

    # Combine perfect and defect structures together
    b = bulko.copy()
    b.extend(solid)
    b.set_pbc(True)

    # Debug: Write solid and bulko to file
    if debug:
        print len(bulko)
        write_xyz(debug, b, 'Find Ints: Solid and Bulko')

    # Identify nearest neighbor atoms for each atom in perfect structure
    ntot = len(bulko)
    ctoff1 = [1.2 for one in b]
    nl = NeighborList(ctoff1, bothways = True, self_interaction = False)
    nl.update(b)
    slist = []
    blist = []
    wlist = []

    # Loop over each atom in perfect structure
    for one in range(ntot):
        indices, offsets = nl.get_neighbors(one)
        for index, d in zip(indices, offsets):
            index = int(index)
            if index >= ntot:
                pos = b[index].position + numpy.dot(d, bulko.get_cell())
                natm1 = Atom(position = pos)
                dist, dx, dy, dz = calc_dist(b[one], natm1)
                if dist <= dcheck:
                    # Assume atoms closer than 0.6 Angstroms to be equivalent
                    slist.append(index-ntot)
                    blist.append(one)
                    if b[one].symbol == b[index].symbol:
                        wlist.append(index-ntot)

    # Identify those atoms corresponding to interstitials, vacancies, and substitutions
    oslist = [atm.index for atm in solid if atm.index not in slist]
    vlist = [atm.index for atm in bulko if atm.index not in blist]
    swlist = [atm.index for atm in solid if atm.index not in wlist and atm.index not in oslist]

    # Create Atoms objects for each identified defect
    ntot = len(solid)
    cluster = Atoms()
    for one in oslist:
        cluster.append(solid[one])
    vacant = Atoms()
    if trackvacs == True:
        for one in vlist:
            vacant.append(Atom(symbol = bulko[one].symbol, position = bulko[one].position))
            solid.append(Atom(symbol = 'X', position = bulko[one].position))
            oslist.append(len(solid)-1)
        stro = 'Cluster Identified with length = {0}\nIdentified {1} vacancies\n'.format(len(cluster), len(vlist))
    swaps = Atoms()
    if trackswaps == True:
        for one in swlist:
            swaps.append(solid[one])
            oslist.append(one)
        stro = 'Cluster Identified with length = {0}\nIdentified {1} swaps\n'.format(len(cluster), len(swlist))
    else:
        stro = 'Cluster Identified with length = {0}\n'.format(len(cluster))

    # Debug: write cluster to file
    if debug:
        b = cluster.copy()
        write_xyz(debug, b, 'Find Ints: Cluster')
        debug.flush()
        print('Found cluster size = ', len(b))

    # Identify atoms surrounding the identified defects in the defected structure
    box = Atoms()
    bulki = Atoms()
    if rcutoff != 0:
        if rcutoff > 2.0:
            cutoffs = [rcutoff for one in solid]
        else:
            cutoffs = [2.0 for one in solid]
        solid.set_pbc(True)
        nl = NeighborList(cutoffs, bothways = True, self_interaction = False)
        nl.update(solid)
        nbatmsd = []
        repinds = []
        for one in oslist:
            if one not in repinds:
                if one < ntot:
                    nbatmsd.append((0, one))
                    repinds.append(one)
        for one in oslist:
            indices, offsets = nl.get_neighbors(one)
            for index, d in zip(indices, offsets):
                index = int(index)
                if index not in repinds and index < ntot:
                    opos = copy.copy(solid[index].position)
                    solid[index].position = solid[index].position + numpy.dot(d, solid.get_cell())
                    dist = solid.get_distance(one, index)
                    solid[index].position = opos
                    if dist <= rcutoff:
                        nbatmsd.append((dist, index))
                        repinds.append(index)
    else:
        nbatmsd = []
        repinds = []
        for one in oslist:
            if one not in repinds:
                if one < ntot:
                    nbatmsd.append((0, one))
                    repinds.append(one)
    nbatmsd = sorted(nbatmsd, key = lambda one:one[0], reverse = True)
    indices = []
    natomsbox = 0

    # Select only atoms closest to defects that satisfy concentrations specified by atomlist given in atomlistcheck
    if atomlistcheck:
        for sym, c, m, u in atomlistcheck:
            i = 0
            nbsym = [one for one in nbatmsd if solid[one[1]].symbol == sym]
            if len(nbsym) > c:
                while i < c:
                    a = nbsym.pop()
                    box.append(solid[a[1]])
                    indices.append(a[1])
                    i += 1
            else:
                for a in nbsym:
                    box.append(solid[a[1]])
                    indices.append(a[1])
                    i += 1
                if len(box)-natomsbox < c:
                    try:
                        while True:
                            for n in range(len(nbatmsd)-1, -1, -1):
                                inds, offsets = nl.get_neighbors(nbatmsd[n][1])
                                for one, d in zip(inds, offsets):
                                    if len(box)-natomsbox < c:
                                        if one not in indices and one < ntot and solid[one].symbol == sym:
                                            opos = copy.copy(solid[one].position)
                                            solid[one].position = solid[one].position + numpy.dot(d, solid.get_cell())
                                            dist = solid.get_distance(nbatmsd[n][1], one)
                                            solid[one].position = opos
                                            if dist <= rcutoff*5.0:
                                                box.append(solid[one])
                                                indices.append(one)
                                    else:
                                        raise StopIteration()
                                for one, d in zip(inds, offsets):
                                    if len(box)-natomsbox < c:
                                        if one not in indices and one < ntot and solid[one].symbol == sym:
                                            opos = copy.copy(solid[one].position)
                                            solid[one].position = solid[one].position + numpy.dot(d, solid.get_cell())
                                            dist = solid.get_distance(nbatmsd[n][1], one)
                                            solid[one].position = opos
                                            box.append(solid[one])
                                            indices.append(one)
                                    else:
                                        raise StopIteration()
                    except StopIteration:
                        pass
            natomsbox = len(box)
        # Double check for sanity
        for sym, c, m, u in atomlistcheck:
            symsbox = [one for one in box if one.symbol == sym]
            if len(symsbox) != c:
                stro += 'WARNING!!!! : FAILURE IN FIND_DEFECTS TO MATCH PROVIDED ATOMLIST. DEBUG!!!!\n'

    # If atomlistcheck is False then use all the atoms in the given cutoff distance
    else:
        for a in nbatmsd:
            box.append(solid[a[1]])
            indices.append(a[1])

    # Add remaining atoms in defect to defected bulk atoms object
    for one in range(len(solid)):
        if one not in indices and one < ntot:
            bulki.append(solid[one])

    # Check for accidental vacancy admissions
    #checklist = [atm for atm in box if atm.symbol == 'X']
    #checklist.extend([atm for atm in bulki if atm.symbol == 'X'])

    # Set up new individual
    indiv = box.copy()
    bulki.set_cell(bulko.get_cell())
    indiv.set_cell(bulko.get_cell())
    bulki.set_pbc(True)
    indiv.set_pbc(True)
    stro += 'Atomlist check = {0}\n'.format(atomlistcheck)
    stro += 'Bulko = {0}\n'.format(bulko)
    stro += 'New individual ({0} atoms) : {1}\n'.format(len(indiv), indiv)
    stro += 'New bulki ({0} atoms) : {1}\n'.format(len(bulki), bulki)

    # Debug: write new indiv to file
    if debug:
        b = indiv.copy()
        write_xyz(debug, b, 'Find Ints: New Individual')
        # Debug: write new bulki to file
        b = bulki.copy()
        write_xyz(debug, b, 'Find Ints: New Bulki')
        debug.flush()
        print(len(bulko))

    return indiv, bulki, vacant, swaps, stro
Example #34
0
 def update_neighbor_list(self, atoms):
     cut = 0.5 * max(self.data['cutoffs'].values())
     self.nl = NeighborList([cut] * len(atoms), skin=0, bothways=True)
     self.nl.update(atoms)
     self.atoms = atoms
Example #35
0
class ANNCalculator(Calculator):
    def __init__(self, potentials, **kwargs):
        Calculator.__init__(self, **kwargs)
        self.ann = ANNPotentials(potentials)
        self.cutoff = self.ann.Rc_max
        self.cutoff2 = self.cutoff**2
        self.neighbors = None
        self.implemented_properties = {
            'energy': self.calculate_energy,
            'forces': self.calculate_energy_and_forces,
        }
        self.results = {}

    def __del__(self):
        try:
            del self.ann
        except (AttributeError, NameError):
            pass

    def release(self):
        del self.ann

    def update(self, atoms):

        if (self.neighbors is None) or (len(self.neighbors.cutoffs) !=
                                        len(atoms)):
            cutoffs = self.cutoff * np.ones(len(atoms))
            self.neighbors = NeighborList(cutoffs,
                                          self_interaction=False,
                                          bothways=True)
        self.neighbors.update(atoms)

    def calculate(self,
                  atoms=None,
                  properties=['energy'],
                  system_changes=all_changes):

        # Calculator essentially does: self.atoms = atoms
        Calculator.calculate(self, atoms, properties, system_changes)

        has_results = [p in self.results for p in properties]
        if (len(system_changes) > 0) or (not np.all(has_results)):
            self.update(self.atoms)
            if ('energy' in properties) and ('forces' in properties):
                # Forces evaluation requires energy. No need to compute
                # energy twice.
                del properties[properties.index('energy')]
            for p in properties:
                if p in self.implemented_properties:
                    self.implemented_properties[p](self.atoms)
                else:
                    raise NotImplementedError(
                        "Property not implemented: {}".format(p))

    def calculate_energy(self, atoms):
        energy = 0.0
        atom_types = atoms.get_chemical_symbols()
        for i in range(len(atoms)):
            indices, offsets = self.neighbors.get_neighbors(i)
            type_i = atom_types[i]
            coords_i = atoms.positions[i]
            coords_j = np.empty((len(indices), 3), dtype=np.double)
            types_j = []
            inb = 0
            for j, offset in zip(indices, offsets):
                coo = (atoms.positions[j] + np.dot(offset, atoms.get_cell()))
                d2 = np.sum((coo - coords_i)**2)
                if d2 <= self.cutoff2:
                    coords_j[inb] = coo
                    types_j.append(atom_types[j])
                    inb += 1
            energy += self.ann.atomic_energy(coords_i, type_i, coords_j[:inb],
                                             types_j)
        self.results['energy'] = energy

    def calculate_energy_and_forces(self, atoms):
        energy = 0.0
        atom_types = atoms.get_chemical_symbols()
        forces = np.zeros((len(atoms), 3), dtype=np.double)
        for i in range(len(atoms)):
            indices, offsets = self.neighbors.get_neighbors(i)
            type_i = atom_types[i]
            index_i = i + 1
            index_j = np.empty(indices.size, dtype=np.intc)
            coords_i = atoms.positions[i]
            coords_j = np.empty((indices.size, 3), dtype=np.double)
            types_j = []
            inb = 0
            for j, offset in zip(indices, offsets):
                coo = (atoms.positions[j] + np.dot(offset, atoms.get_cell()))
                d2 = np.sum((coo - coords_i)**2)
                if d2 <= self.cutoff2:
                    coords_j[inb] = coo
                    index_j[inb] = j + 1
                    types_j.append(atom_types[j])
                    inb += 1
            energy += self.ann.atomic_energy_and_forces(
                coords_i, type_i, index_i, coords_j[:inb], types_j,
                index_j[:inb], forces)
        self.results['energy'] = energy
        self.results['forces'] = forces
Example #36
0
    d = 0.0
    for a in range(len(atoms)):
        i, offsets = nl.get_neighbors(a)
        for j in i:
            c[j] += 1
        c[a] += len(i)
        d += (((R[i] + np.dot(offsets, cell) - R[a])**2).sum(1)**0.5).sum()
    return d, c

for sorted in [False, True]:
    for p1 in range(2):
        for p2 in range(2):
            for p3 in range(2):
                print p1, p2, p3
                atoms.set_pbc((p1, p2, p3))
                nl = NeighborList(atoms.numbers * 0.2 + 0.5,
                                  skin=0.0, sorted=sorted)
                nl.update(atoms)
                d, c = count(nl, atoms)
                atoms2 = atoms.repeat((p1 + 1, p2 + 1, p3 + 1))
                nl2 = NeighborList(atoms2.numbers * 0.2 + 0.5,
                                   skin=0.0, sorted=sorted)
                nl2.update(atoms2)
                d2, c2 = count(nl2, atoms2)
                c2.shape = (-1, 10)
                dd = d * (p1 + 1) * (p2 + 1) * (p3 + 1) - d2
                print dd
                print c2 - c
                assert abs(dd) < 1e-10
                assert not (c2 - c).any()

h2 = Atoms('H2', positions=[(0, 0, 0), (0, 0, 1)])
Example #37
0
def find_defects(solid,
                 bulko,
                 rcutoff,
                 atomlistcheck=False,
                 trackvacs=False,
                 trackswaps=False,
                 debug=False,
                 dcheck=0.6):
    """Function to find interstitials, vacancies, and substitutional atoms (swaps) in a defected structure.
    Identifies species by comparison to perfect structure.
    Inputs:
        solid = ASE atoms class for defected structure
        bulko = ASE atoms class for perfect structure
        rcutoff = float value of distance to surrounding atoms to include
        atomlistcheck = False/list of atom types and concentrations according to atomlist format
        trackvacs = True/False whether or not to identify vacancies in defect
        trackswaps = True/False whether or not to identify substitutional defects
        debug = False/file object to write debug structures"""
    # Combine perfect and defect structures together
    b = bulko.copy()
    b.extend(solid)
    b.set_pbc(True)
    #Debug: Write solid and bulko to file
    if debug:
        print len(bulko)
        write_xyz(debug, b, 'Find Ints: Solid and Bulko')
    # Identify nearest neighbor atoms for each atom in perfect structure
    ntot = len(bulko)
    ctoff1 = [1.2 for one in b]
    nl = NeighborList(ctoff1, bothways=True, self_interaction=False)
    nl.update(b)
    slist = []
    blist = []
    wlist = []
    #Loop over each atom in perfect structure
    for one in range(ntot):
        indices, offsets = nl.get_neighbors(one)
        for index, d in zip(indices, offsets):
            index = int(index)
            if index >= ntot:
                pos = b[index].position + numpy.dot(d, bulko.get_cell())
                natm1 = Atom(position=pos)
                dist, dx, dy, dz = calc_dist(b[one], natm1)
                if dist <= dcheck:
                    #Assume atoms closer than 0.6 Angstroms to be equivalent
                    slist.append(index - ntot)
                    blist.append(one)
                    if b[one].symbol == b[index].symbol:
                        wlist.append(index - ntot)
    #Identify those atoms corresponding to interstitials, vacancies, and substitutions
    oslist = [atm.index for atm in solid if atm.index not in slist]
    vlist = [atm.index for atm in bulko if atm.index not in blist]
    swlist = [
        atm.index for atm in solid
        if atm.index not in wlist and atm.index not in oslist
    ]
    # Create Atoms objects for each identified defect
    ntot = len(solid)
    cluster = Atoms()
    for one in oslist:
        cluster.append(solid[one])
    vacant = Atoms()
    if trackvacs == True:
        for one in vlist:
            vacant.append(
                Atom(symbol=bulko[one].symbol, position=bulko[one].position))
            solid.append(Atom(symbol='X', position=bulko[one].position))
            oslist.append(len(solid) - 1)
        stro = 'Cluster Identified with length = {0}\nIdentified {1} vacancies\n'.format(
            len(cluster), len(vlist))
    swaps = Atoms()
    if trackswaps == True:
        for one in swlist:
            swaps.append(solid[one])
            oslist.append(one)
        stro = 'Cluster Identified with length = {0}\nIdentified {1} swaps\n'.format(
            len(cluster), len(swlist))
    else:
        stro = 'Cluster Identified with length = {0}\n'.format(len(cluster))
    #Debug: write cluster to file
    if debug:
        b = cluster.copy()
        write_xyz(debug, b, 'Find Ints: Cluster')
        debug.flush()
        print 'Found cluster size = ', len(b)
    # Identify atoms surrounding the identified defects in the defected structure
    box = Atoms()
    bulki = Atoms()
    if rcutoff != 0:
        if rcutoff > 2.0:
            cutoffs = [rcutoff for one in solid]
        else:
            cutoffs = [2.0 for one in solid]
        solid.set_pbc(True)
        nl = NeighborList(cutoffs, bothways=True, self_interaction=False)
        nl.update(solid)
        nbatmsd = []
        repinds = []
        for one in oslist:
            if one not in repinds:
                if one < ntot:
                    nbatmsd.append((0, one))
                    repinds.append(one)
        for one in oslist:
            indices, offsets = nl.get_neighbors(one)
            for index, d in zip(indices, offsets):
                index = int(index)
                if index not in repinds and index < ntot:
                    opos = copy.copy(solid[index].position)
                    solid[index].position = solid[index].position + numpy.dot(
                        d, solid.get_cell())
                    dist = solid.get_distance(one, index)
                    solid[index].position = opos
                    if dist <= rcutoff:
                        nbatmsd.append((dist, index))
                        repinds.append(index)
    else:
        nbatmsd = []
        repinds = []
        for one in oslist:
            if one not in repinds:
                if one < ntot:
                    nbatmsd.append((0, one))
                    repinds.append(one)
    nbatmsd = sorted(nbatmsd, key=lambda one: one[0], reverse=True)
    indices = []
    natomsbox = 0
    # Select only atoms closest to defects that satisfy concentrations specified by atomlist given in atomlistcheck
    if atomlistcheck:
        for sym, c, m, u in atomlistcheck:
            i = 0
            nbsym = [one for one in nbatmsd if solid[one[1]].symbol == sym]
            if len(nbsym) > c:
                while i < c:
                    a = nbsym.pop()
                    box.append(solid[a[1]])
                    indices.append(a[1])
                    i += 1
            else:
                for a in nbsym:
                    box.append(solid[a[1]])
                    indices.append(a[1])
                    i += 1
                if len(box) - natomsbox < c:
                    try:
                        while True:
                            for n in range(len(nbatmsd) - 1, -1, -1):
                                inds, offsets = nl.get_neighbors(nbatmsd[n][1])
                                for one, d in zip(inds, offsets):
                                    if len(box) - natomsbox < c:
                                        if one not in indices and one < ntot and solid[
                                                one].symbol == sym:
                                            opos = copy.copy(
                                                solid[one].position)
                                            solid[one].position = solid[
                                                one].position + numpy.dot(
                                                    d, solid.get_cell())
                                            dist = solid.get_distance(
                                                nbatmsd[n][1], one)
                                            solid[one].position = opos
                                            if dist <= rcutoff * 5.0:
                                                box.append(solid[one])
                                                indices.append(one)
                                    else:
                                        raise StopIteration()
                                for one, d in zip(inds, offsets):
                                    if len(box) - natomsbox < c:
                                        if one not in indices and one < ntot and solid[
                                                one].symbol == sym:
                                            opos = copy.copy(
                                                solid[one].position)
                                            solid[one].position = solid[
                                                one].position + numpy.dot(
                                                    d, solid.get_cell())
                                            dist = solid.get_distance(
                                                nbatmsd[n][1], one)
                                            solid[one].position = opos
                                            box.append(solid[one])
                                            indices.append(one)
                                    else:
                                        raise StopIteration()
                    except StopIteration:
                        pass
            natomsbox = len(box)
        #Double check for sanity
        for sym, c, m, u in atomlistcheck:
            symsbox = [one for one in box if one.symbol == sym]
            if len(symsbox) != c:
                stro += 'WARNING!!!! : FAILURE IN FIND_DEFECTS TO MATCH PROVIDED ATOMLIST. DEBUG!!!!\n'
    # If atomlistcheck is False then use all the atoms in the given cutoff distance
    else:
        for a in nbatmsd:
            box.append(solid[a[1]])
            indices.append(a[1])
    # Add remaining atoms in defect to defected bulk atoms object
    for one in range(len(solid)):
        if one not in indices and one < ntot:
            bulki.append(solid[one])
    #Check for accidental vacancy admissions
    #checklist=[atm for atm in box if atm.symbol=='X']
    #checklist.extend([atm for atm in bulki if atm.symbol=='X'])
    #Set up new individual
    indiv = box.copy()
    bulki.set_cell(bulko.get_cell())
    indiv.set_cell(bulko.get_cell())
    bulki.set_pbc(True)
    indiv.set_pbc(True)
    stro += 'Atomlist check = {0}\n'.format(atomlistcheck)
    stro += 'Bulko = {0}\n'.format(bulko)
    stro += 'New individual ({0} atoms) : {1}\n'.format(len(indiv), indiv)
    stro += 'New bulki ({0} atoms) : {1}\n'.format(len(bulki), bulki)
    #Debug: write new indiv to file
    if debug:
        b = indiv.copy()
        write_xyz(debug, b, 'Find Ints: New Individual')
        #Debug: write new bulki to file
        b = bulki.copy()
        write_xyz(debug, b, 'Find Ints: New Bulki')
        debug.flush()
        print len(bulko)

    return indiv, bulki, vacant, swaps, stro
Example #38
0
File: opls.py Project: grhawk/ASE
class OPLSff:
    def __init__(self, fileobj=None, warnings=0):
        self.warnings = warnings
        self.data = {}
        if fileobj is not None:
            self.read(fileobj)

    def read(self, fileobj, comments='#'):
        if isinstance(fileobj, str):
            fileobj = open(fileobj)

        def read_block(name, symlen, nvalues):
            """Read a data block.

            name: name of the block to store in self.data
            symlen: length of the symbol
            nvalues: number of values expected
            """

            if name not in self.data:
                self.data[name] = {}
            data = self.data[name]

            def add_line():
                line = fileobj.readline().strip()
                if not len(line):  # end of the block
                    return False
                line = line.split('#')[0]  # get rid of comments
                if len(line) > symlen:
                    symbol = line[:symlen]
                    words = line[symlen:].split()
                    if len(words) >= nvalues:
                        if nvalues == 1:
                            data[symbol] = float(words[0])
                        else:
                            data[symbol] = [
                                float(word) for word in words[:nvalues]
                            ]
                return True

            while add_line():
                pass

        read_block('one', 2, 3)
        read_block('bonds', 5, 2)
        read_block('angles', 8, 2)
        read_block('dihedrals', 11, 4)
        read_block('cutoffs', 5, 1)

        self.bonds = BondData(self.data['bonds'])
        self.angles = AnglesData(self.data['angles'])
        self.dihedrals = DihedralsData(self.data['dihedrals'])
        self.cutoffs = CutoffList(self.data['cutoffs'])

    def write_lammps(self, atoms, prefix='lammps'):
        """Write input for a LAMMPS calculation."""
        self.prefix = prefix
        btypes, atypes, dtypes = self.write_lammps_atoms(atoms)
        self.write_lammps_definitions(atoms, btypes, atypes, dtypes)
        self.write_lammps_in()

    def write_lammps_in(self):
        # XXX change this
        # XXX some input file for syntax checks
        # XXX change this
        fileobj = self.prefix + '_in'
        if isinstance(fileobj, str):
            fileobj = open(fileobj, 'w')
        fileobj.write("""
# (written by ASE)
clear
variable dump_file string "dump_traj"
variable data_file string "dump_data"
units metal
boundary p p f

atom_style full
""")
        fileobj.write('read_data ' + self.prefix + '_atoms\n')
        fileobj.write('include  ' + self.prefix + '_opls\n')
        fileobj.write("""
### run
fix fix_nve all nve
dump dump_all all custom 1 trj_lammps id type x y z vx vy vz fx fy fz
thermo_style custom step temp press cpu pxx pyy pzz pxy pxz pyz ke pe etotal vol lx ly lz atoms
thermo_modify flush yes
thermo 1
run 0
print "__end_of_ase_invoked_calculation__"
log /dev/stdout
""")
        fileobj.close()

    def write_lammps_atoms(self, atoms):
        """Write atoms infor for LAMMPS"""

        fileobj = self.prefix + '_atoms'
        if isinstance(fileobj, str):
            fileobj = open(fileobj, 'w')

        # header
        fileobj.write(fileobj.name + ' (by ' + str(self.__class__) + ')\n\n')
        fileobj.write(str(len(atoms)) + ' atoms\n')
        fileobj.write(str(len(atoms.types)) + ' atom types\n')
        btypes, blist = self.get_bonds(atoms)
        if len(blist):
            fileobj.write(str(len(blist)) + ' bonds\n')
            fileobj.write(str(len(btypes)) + ' bond types\n')
        atypes, alist = self.get_angles()
        if len(alist):
            fileobj.write(str(len(alist)) + ' angles\n')
            fileobj.write(str(len(atypes)) + ' angle types\n')
        dtypes, dlist = self.get_dihedrals(alist, atypes)
        if len(dlist):
            fileobj.write(str(len(dlist)) + ' dihedrals\n')
            fileobj.write(str(len(dtypes)) + ' dihedral types\n')

        # cell
        p = prism(atoms.get_cell())
        xhi, yhi, zhi, xy, xz, yz = p.get_lammps_prism_str()
        fileobj.write('\n0.0 %s  xlo xhi\n' % xhi)
        fileobj.write('0.0 %s  ylo yhi\n' % yhi)
        fileobj.write('0.0 %s  zlo zhi\n' % zhi)

        # atoms
        fileobj.write('\nAtoms\n\n')
        tag = atoms.get_tags()
        for i, r in enumerate(map(p.pos_to_lammps_str, atoms.get_positions())):
            q = 0  # charge will be overwritten
            fileobj.write('%6d %3d %3d %s %s %s %s' %
                          ((i + 1, 1, tag[i] + 1, q) + tuple(r)))
            fileobj.write(' # ' + atoms.types[tag[i]] + '\n')

        # velocities
        velocities = atoms.get_velocities()
        if velocities is not None:
            fileobj.write('\nVelocities\n\n')
            for i, v in enumerate(velocities):
                fileobj.write('%6d %g %g %g\n' % (i + 1, v[0], v[1], v[2]))

        # masses
        fileobj.write('\nMasses\n\n')
        for i, typ in enumerate(atoms.types):
            cs = atoms.split_symbol(typ)[0]
            fileobj.write(
                '%6d %g # %s -> %s\n' %
                (i + 1, atomic_masses[chemical_symbols.index(cs)], typ, cs))

        # bonds
        if len(blist):
            fileobj.write('\nBonds\n\n')
            for ib, bvals in enumerate(blist):
                fileobj.write(
                    '%8d %6d %6d %6d ' %
                    (ib + 1, bvals[0] + 1, bvals[1] + 1, bvals[2] + 1))
                fileobj.write('# ' + btypes[bvals[0]] + '\n')

        # angles
        if len(alist):
            fileobj.write('\nAngles\n\n')
            for ia, avals in enumerate(alist):
                fileobj.write('%8d %6d %6d %6d %6d ' %
                              (ia + 1, avals[0] + 1, avals[1] + 1,
                               avals[2] + 1, avals[3] + 1))
                fileobj.write('# ' + atypes[avals[0]] + '\n')

        # dihedrals
        if len(dlist):
            fileobj.write('\nDihedrals\n\n')
            for i, dvals in enumerate(dlist):
                fileobj.write('%8d %6d %6d %6d %6d %6d ' %
                              (i + 1, dvals[0] + 1, dvals[1] + 1, dvals[2] + 1,
                               dvals[3] + 1, dvals[4] + 1))
                fileobj.write('# ' + dtypes[dvals[0]] + '\n')

        return btypes, atypes, dtypes

    def update_neighbor_list(self, atoms):
        cut = 0.5 * max(self.data['cutoffs'].values())
        self.nl = NeighborList([cut] * len(atoms),
                               skin=0,
                               bothways=True,
                               self_interaction=False)
        self.nl.update(atoms)
        self.atoms = atoms

    def get_bonds(self, atoms):
        """Find bonds and return them and their types"""
        cutoffs = CutoffList(self.data['cutoffs'])
        self.update_neighbor_list(atoms)

        types = atoms.get_types()
        tags = atoms.get_tags()
        cell = atoms.get_cell()
        positions = atoms.get_positions()
        bond_list = []
        bond_types = []
        for i, atom in enumerate(atoms):
            iname = types[tags[i]]
            indices, offsets = self.nl.get_neighbors(i)
            for j, offset in zip(indices, offsets):
                if j <= i:
                    continue  # do not double count
                jname = types[tags[j]]
                cut = cutoffs.value(iname, jname)
                if cut is None:
                    if self.warnings > 1:
                        print('Warning: cutoff %s-%s not found' %
                              (iname, jname))
                    continue  # don't have it
                dist = np.linalg.norm(atom.position - atoms[j].position -
                                      np.dot(offset, cell))
                if dist > cut:
                    continue  # too far away
                name, val = self.bonds.name_value(iname, jname)
                if name is None:
                    if self.warnings:
                        print('Warning: potential %s-%s not found' %
                              (iname, jname))
                    continue  # don't have it
                if name not in bond_types:
                    bond_types.append(name)
                bond_list.append([bond_types.index(name), i, j])
        return bond_types, bond_list

    def get_angles(self, atoms=None):
        cutoffs = CutoffList(self.data['cutoffs'])
        if atoms is not None:
            self.update_neighbor_list(atoms)
        else:
            atoms = self.atoms

        types = atoms.get_types()
        tags = atoms.get_tags()
        cell = atoms.get_cell()
        positions = atoms.get_positions()
        ang_list = []
        ang_types = []

        # center atom *-i-*
        for i, atom in enumerate(atoms):
            iname = types[tags[i]]
            indicesi, offsetsi = self.nl.get_neighbors(i)

            # search for first neighbor j-i-*
            for j, offsetj in zip(indicesi, offsetsi):
                jname = types[tags[j]]
                cut = cutoffs.value(iname, jname)
                if cut is None:
                    continue  # don't have it
                dist = np.linalg.norm(atom.position - atoms[j].position -
                                      np.dot(offsetj, cell))
                if dist > cut:
                    continue  # too far away

                # search for second neighbor j-i-k
                for k, offsetk in zip(indicesi, offsetsi):
                    if k <= j:
                        continue  # avoid double count
                    kname = types[tags[k]]
                    cut = cutoffs.value(iname, kname)
                    if cut is None:
                        continue  # don't have it
                    dist = np.linalg.norm(atom.position -
                                          np.dot(offsetk, cell) -
                                          atoms[k].position)
                    if dist > cut:
                        continue  # too far away
                    name, val = self.angles.name_value(jname, iname, kname)
                    if name is None:
                        continue  # don't have it
                    if name not in ang_types:
                        ang_types.append(name)
                    ang_list.append([ang_types.index(name), j, i, k])

        return ang_types, ang_list

    def get_dihedrals(self, ang_types, ang_list):
        'Dihedrals derived from angles.'

        cutoffs = CutoffList(self.data['cutoffs'])

        atoms = self.atoms
        types = atoms.get_types()
        tags = atoms.get_tags()
        cell = atoms.get_cell()

        dih_list = []
        dih_types = []

        def append(name, i, j, k, l):
            if name not in dih_types:
                dih_types.append(name)
            index = dih_types.index(name)
            if (([index, i, j, k, l] not in dih_list)
                    and ([index, l, k, j, i] not in dih_list)):
                dih_list.append([index, i, j, k, l])

        for angle in ang_types:
            l, i, j, k = angle
            iname = types[tags[i]]
            jname = types[tags[j]]
            kname = types[tags[k]]

            # search for l-i-j-k
            indicesi, offsetsi = self.nl.get_neighbors(i)
            for l, offsetl in zip(indicesi, offsetsi):
                if l == j:
                    continue  # avoid double count
                lname = types[tags[l]]
                cut = cutoffs.value(iname, lname)
                if cut is None:
                    continue  # don't have it
                dist = np.linalg.norm(atoms[i].position - atoms[l].position -
                                      np.dot(offsetl, cell))
                if dist > cut:
                    continue  # too far away
                name, val = self.dihedrals.name_value(lname, iname, jname,
                                                      kname)
                if name is None:
                    continue  # don't have it
                append(name, l, i, j, k)

            # search for i-j-k-l
            indicesk, offsetsk = self.nl.get_neighbors(k)
            for l, offsetl in zip(indicesk, offsetsk):
                if l == j:
                    continue  # avoid double count
                lname = types[tags[l]]
                cut = cutoffs.value(kname, lname)
                if cut is None:
                    continue  # don't have it
                dist = np.linalg.norm(atoms[k].position - atoms[l].position -
                                      np.dot(offsetl, cell))
                if dist > cut:
                    continue  # too far away
                name, val = self.dihedrals.name_value(iname, jname, kname,
                                                      lname)
                if name is None:
                    continue  # don't have it
                append(name, i, j, k, l)

        return dih_types, dih_list

    def write_lammps_definitions(self, atoms, btypes, atypes, dtypes):
        """Write force field definitions for LAMMPS."""

        fileobj = self.prefix + '_opls'
        if isinstance(fileobj, str):
            fileobj = open(fileobj, 'w')

        fileobj.write('# OPLS potential\n')
        fileobj.write('# write_lammps' +
                      str(time.asctime(time.localtime(time.time()))))

        # bonds
        if len(btypes):
            fileobj.write('\n# bonds\n')
            fileobj.write('bond_style      harmonic\n')
            for ib, btype in enumerate(btypes):
                fileobj.write('bond_coeff %6d' % (ib + 1))
                for value in self.bonds.nvh[btype]:
                    fileobj.write(' ' + str(value))
                fileobj.write(' # ' + btype + '\n')

        # angles
        if len(atypes):
            fileobj.write('\n# angles\n')
            fileobj.write('angle_style      harmonic\n')
            for ia, atype in enumerate(atypes):
                fileobj.write('angle_coeff %6d' % (ia + 1))
                for value in self.angles.nvh[atype]:
                    fileobj.write(' ' + str(value))
                fileobj.write(' # ' + atype + '\n')

        # dihedrals
        if len(dtypes):
            fileobj.write('\n# dihedrals\n')
            fileobj.write('dihedral_style      opls\n')
            for i, dtype in enumerate(dtypes):
                fileobj.write('dihedral_coeff %6d' % (i + 1))
                for value in self.dihedrals.nvh[dtype]:
                    fileobj.write(' ' + str(value))
                fileobj.write(' # ' + dtype + '\n')

        # Lennard Jones settings
        fileobj.write('\n# L-J parameters\n')
        fileobj.write('pair_style lj/cut/coul/long 10.0 7.4' +
                      ' # consider changing these parameters\n')
        fileobj.write('special_bonds lj/coul 0.0 0.0 0.5\n')
        data = self.data['one']
        for ia, atype in enumerate(atoms.types):
            if len(atype) < 2:
                atype = atype + ' '
            fileobj.write('pair_coeff ' + str(ia + 1) + ' ' + str(ia + 1))
            for value in data[atype][:2]:
                fileobj.write(' ' + str(value))
            fileobj.write(' # ' + atype + '\n')
        fileobj.write('pair_modify shift yes mix geometric\n')

        # Coulomb
        fileobj.write("""
# Coulomb
kspace_style pppm 1e-5
kspace_modify slab 3.0
""")

        # Charges
        fileobj.write('\n# charges\n')
        for ia, atype in enumerate(atoms.types):
            if len(atype) < 2:
                atype = atype + ' '
            fileobj.write('set type ' + str(ia + 1))
            fileobj.write(' charge ' + str(data[atype][2]))
            fileobj.write(' # ' + atype + '\n')
Example #39
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 {} and individual {}'.format(repr(ind1.index), repr(ind2.index)))

    # 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 = {}'.format(pt1.position))
        print('DEBUG CX: Point two = {}'.format(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 = {}'.format(repr(r)))
        print('Position in solid1 = {}'.format(repr(pt1.position)))
        print('Position in solid2 = {}'.format(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 {} {} atoms'.format(repr(nc), repr(sym)))
        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 {} and {} atoms'.format(repr(nc), repr(sym)))
        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
Example #40
0
def eval_energy(input):
    """Function to evaluate energy of an individual
    Inputs:
        input = [Optimizer class object with parameters, Individual class structure to be evaluated]
    Outputs:
        energy, bul, individ, signal
        energy = energy of Individual evaluated
        bul = bulk structure of Individual if simulation structure is Defect
        individ = Individual class structure evaluated
        signal = string of information about evaluation
    """
    if input[0] == None:
        energy = 0
        bul = 0
        individ = 0
        rank = MPI.COMM_WORLD.Get_rank()
        signal = 'Evaluated none individual on ' + repr(rank) + '\n'
    else:
        [Optimizer, individ] = input
    if Optimizer.calc_method == 'MAST':
        energy = individ.energy
        bul = individ.energy
        signal = 'Recieved MAST structure\n'
    else:
        if Optimizer.parallel: rank = MPI.COMM_WORLD.Get_rank()
        if not Optimizer.genealogy:
            STR = '----Individual ' + str(
                individ.index) + ' Optimization----\n'
        else:
            STR = '----Individual ' + str(
                individ.history_index) + ' Optimization----\n'
        indiv = individ[0]
        if 'EE' in Optimizer.debug:
            debug = True
        else:
            debug = False
        if debug:
            write_xyz(Optimizer.debugfile, indiv, 'Recieved by eval_energy')
            Optimizer.debugfile.flush()
        if Optimizer.structure == 'Defect':
            indi = indiv.copy()
            if Optimizer.alloy == True:
                bulk = individ.bulki
            else:
                bulk = individ.bulko
            nat = indi.get_number_of_atoms()
            csize = bulk.get_cell()
            totalsol = Atoms(cell=csize, pbc=True)
            totalsol.extend(indi)
            totalsol.extend(bulk)
            for sym, c, m, u in Optimizer.atomlist:
                nc = len([atm for atm in totalsol if atm.symbol == sym])
                STR += 'Defect configuration contains ' + repr(
                    nc) + ' ' + repr(sym) + ' atoms\n'

        elif Optimizer.structure == 'Surface':
            totalsol = Atoms()
            totalsol.extend(indiv)
            nat = indiv.get_number_of_atoms()
            totalsol.extend(individ.bulki)
            for sym, c, m, u in Optimizer.atomlist:
                nc = len([atm for atm in totalsol if atm.symbol == sym])
                STR += 'Surface-Bulk configuration contains ' + repr(
                    nc) + ' ' + repr(sym) + ' atoms\n'
            cell = numpy.maximum.reduce(indiv.get_cell())
            totalsol.set_cell([cell[0], cell[1], 500])
            totalsol.set_pbc([True, True, False])

        if Optimizer.constrain_position:
            ts = totalsol.copy()
            indc, indb, vacant, swap, stro = find_defects(
                ts, Optimizer.solidbulk, 0)
            sbulk = Optimizer.solidbulk.copy()
            bcom = sbulk.get_center_of_mass()
            #totalsol.translate(-bulkcom)
            #indc.translate(-bulkcom)
            #totalsol.append(Atom(position=[0,0,0]))
            # 			for one in indc:
            # 				index = [atm.index for atm in totalsol if atm.position[0]==one.position[0] and atm.position[1]==one.position[1] and atm.position[2]==one.position[2]][0]
            # 				if totalsol.get_distance(-1,index) > Optimizer.sf:
            # 					r = random.random()
            # 					totalsol.set_distance(-1,index,Optimizer.sf*r,fix=0)
            # 			totalsol.pop()
            # 			totalsol.translate(bulkcom)
            com = indc.get_center_of_mass()
            dist = (sum((bcom[i] - com[i])**2 for i in range(3)))**0.5
            if dist > Optimizer.sf:
                STR += 'Shifting structure to within region\n'
                r = random.random() * Optimizer.sf
                comv = numpy.linalg.norm(com)
                ncom = [one * r / comv for one in com]
                trans = [ncom[i] - com[i] for i in range(3)]
                indices = []
                for one in indc:
                    id = [
                        atm.index for atm in totalsol
                        if atm.position[0] == one.position[0]
                        and atm.position[1] == one.position[1]
                        and atm.position[2] == one.position[2]
                    ][0]
                    totalsol[id].position += trans

        # Check for atoms that are too close
        min_len = 0.7
        #pdb.set_trace()
        if not Optimizer.fixed_region:
            if Optimizer.structure == 'Defect' or Optimizer.structure == 'Surface':
                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)
                if debug:
                    write_xyz(Optimizer.debugfile, totalsol,
                              'After minlength check')
                    Optimizer.debugfile.flush()
            else:
                for i in range(len(indiv)):
                    for j in range(len(indiv)):
                        if i != j:
                            d = indiv.get_distance(i, j)
                            if d < min_len:
                                indiv.set_distance(i, j, min_len, fix=0.5)
                                STR += '--- WARNING: Atoms too close (<0.7A) - Implement Move ---\n'
                if debug:
                    write_xyz(Optimizer.debugfile, indiv,
                              'After minlength check')
                    Optimizer.debugfile.flush()

        # Set calculator to use to get forces/energies
        if Optimizer.parallel:
            calc = setup_calculator(Optimizer)
            if Optimizer.fixed_region:
                pms = copy.deepcopy(calc.parameters)
                try:
                    pms['mass'][
                        len(pms['mass']) - 1] += '\ngroup RO id >= ' + repr(
                            nat) + '\nfix freeze RO setforce 0.0 0.0 0.0\n'
                except KeyError:
                    pms['pair_coeff'][0] += '\ngroup RO id >= ' + repr(
                        nat) + '\nfix freeze RO setforce 0.0 0.0 0.0\n'
                calc = LAMMPS(parameters=pms,
                              files=calc.files,
                              keep_tmp_files=calc.keep_tmp_files,
                              tmp_dir=calc.tmp_dir)
                lmin = copy.copy(Optimizer.lammps_min)
                Optimizer.lammps_min = None
                Optimizer.static_calc = setup_calculator(Optimizer)
                Optimizer.lammps_min = lmin
        else:
            calc = Optimizer.calc
        if Optimizer.structure == 'Defect' or Optimizer.structure == 'Surface':
            totalsol.set_calculator(calc)
            totalsol.set_pbc(True)
        else:
            indiv.set_calculator(calc)
            indiv.set_pbc(
                True)  #Current bug in ASE optimizer-Lammps prevents pbc=false
            if Optimizer.structure == 'Cluster':
                indiv.set_cell([500, 500, 500])
                indiv.translate([250, 250, 250])

        cwd = os.getcwd()
        # Perform Energy Minimization
        if not Optimizer.parallel:
            Optimizer.output.flush()
        if Optimizer.ase_min == True:
            try:
                if Optimizer.structure == 'Defect' or Optimizer.structure == 'Surface':
                    dyn = BFGS(totalsol)
                else:
                    dyn = BFGS(indiv)
                dyn.run(fmax=Optimizer.ase_min_fmax,
                        steps=Optimizer.ase_min_maxsteps)
            except OverflowError:
                STR += '--- Error: Infinite Energy Calculated - Implement Random ---\n'
                box = Atoms()
                indiv = gen_pop_box(Optimizer.natoms, Optimizer.atomlist,
                                    Optimizer.size)
                indiv.set_calculator(calc)
                dyn = BFGS(indiv)
                dyn.run(fmax=fmax, steps=steps)
            except numpy.linalg.linalg.LinAlgError:
                STR += '--- Error: Singular Matrix - Implement Random ---\n'
                indiv = gen_pop_box(Optimizer.natoms, Optimizer.atomlist,
                                    Optimizer.size)
                indiv.set_calculator(calc)
                dyn = BFGS(indiv)
                dyn.run(fmax=fmax, steps=steps)
            # Get Energy of Minimized Structure
            if Optimizer.structure == 'Defect' or Optimizer.structure == 'Surface':
                en = totalsol.get_potential_energy()
                #force=numpy.maximum.reduce(abs(totalsol.get_forces()))
                if Optimizer.fitness_scheme == 'enthalpyfit':
                    pressure = totalsol.get_isotropic_pressure(
                        totalsol.get_stress())
                    cell_max = numpy.maximum.reduce(totalsol.get_positions())
                    cell_min = numpy.minimum.reduce(totalsol.get_positions())
                    cell = cell_max - cell_min
                    volume = cell[0] * cell[1] * cell[2]
                else:
                    pressure = 0
                    volume = 0
                na = totalsol.get_number_of_atoms()
                ena = en / na
                energy = en
                individ[0] = totalsol[0:nat]
                bul = totalsol[(nat):len(totalsol)]
                STR += 'Number of positions = ' + repr(
                    len(bul) + len(individ[0])) + '\n'
                individ[0].set_cell(csize)
                indiv = individ[0]
            else:
                en = indiv.get_potential_energy()
                if Optimizer.fitness_scheme == 'enthalpyfit':
                    pressure = indiv.get_isotropic_pressure(indiv.get_stress())
                    cell_max = numpy.maximum.reduce(indiv.get_positions())
                    cell_min = numpy.minimum.reduce(indiv.get_positions())
                    cell = cell_max - cell_min
                    volume = cell[0] * cell[1] * cell[2]
                else:
                    pressure = 0
                    volume = 0
                na = indiv.get_number_of_atoms()
                ena = en / na
                energy = ena
                individ[0] = indiv
                bul = 0
        else:
            if Optimizer.structure == 'Defect' or Optimizer.structure == 'Surface':
                if Optimizer.calc_method == 'VASP':
                    en = totalsol.get_potential_energy()
                    calcb = Vasp(restart=True)
                    totalsol = calcb.get_atoms()
                    stress = calcb.read_stress()
                else:
                    try:
                        totcop = totalsol.copy()
                        if debug:
                            write_xyz(Optimizer.debugfile, totcop,
                                      'Individual sent to lammps')
                        OUT = totalsol.calc.calculate(totalsol)
                        totalsol = OUT['atoms']
                        totalsol.set_pbc(True)
                        if Optimizer.fixed_region:
                            if debug:
                                print 'Energy of fixed region calc = ', OUT[
                                    'thermo'][-1]['pe']
                            totalsol.set_calculator(Optimizer.static_calc)
                            OUT = totalsol.calc.calculate(totalsol)
                            totalsol = OUT['atoms']
                            totalsol.set_pbc(True)
                            if debug:
                                print 'Energy of static calc = ', OUT[
                                    'thermo'][-1]['pe']
                        en = OUT['thermo'][-1]['pe']
                        stress = numpy.array([
                            OUT['thermo'][-1][i]
                            for i in ('pxx', 'pyy', 'pzz', 'pyz', 'pxz', 'pxy')
                        ]) * (-1e-4 * GPa)
                        #force=numpy.maximum.reduce(abs(totalsol.get_forces()))
                        if debug:
                            write_xyz(Optimizer.debugfile, totalsol,
                                      'After Lammps Minimization')
                            Optimizer.debugfile.flush()
                    except Exception, e:
                        os.chdir(cwd)
                        STR += 'WARNING: Exception during energy eval:\n' + repr(
                            e) + '\n'
                        f = open('problem-structures.xyz', 'a')
                        write_xyz(f,
                                  totcop,
                                  data='Starting structure hindex=' +
                                  individ.history_index)
                        write_xyz(f, totalsol, data='Lammps Min structure')
                        en = 10
                        stress = 0
                        f.close()
                if Optimizer.fitness_scheme == 'enthalpyfit':
                    pressure = totalsol.get_isotropic_pressure(stress)
                    cell_max = numpy.maximum.reduce(totalsol.get_positions())
                    cell_min = numpy.minimum.reduce(totalsol.get_positions())
                    cell = cell_max - cell_min
                    volume = cell[0] * cell[1] * cell[2]
                else:
                    pressure = totalsol.get_isotropic_pressure(stress)
                    volume = 0
                na = totalsol.get_number_of_atoms()
                ena = en / na
                energy = en
                if Optimizer.structure == 'Defect':
                    if Optimizer.fixed_region == True or Optimizer.finddefects == False:
                        individ[0] = totalsol[0:nat]
                        bul = totalsol[(nat):len(totalsol)]
                        individ[0].set_cell(csize)
                    else:
                        if 'FI' in Optimizer.debug:
                            outt = find_defects(
                                totalsol,
                                Optimizer.solidbulk,
                                Optimizer.sf,
                                atomlistcheck=Optimizer.atomlist,
                                trackvacs=Optimizer.trackvacs,
                                trackswaps=Optimizer.trackswaps,
                                debug=Optimizer.debugfile)
                        else:
                            outt = find_defects(
                                totalsol,
                                Optimizer.solidbulk,
                                Optimizer.sf,
                                atomlistcheck=Optimizer.atomlist,
                                trackvacs=Optimizer.trackvacs,
                                trackswaps=Optimizer.trackswaps,
                                debug=False)
                        individ[0] = outt[0]
                        bul = outt[1]
                        individ.vacancies = outt[2]
                        individ.swaps = outt[3]
                        STR += outt[4]
                    indiv = individ[0]
                else:
                    top, bul = find_top_layer(totalsol, Optimizer.surftopthick)
                    indiv = top.copy()
                    individ[0] = top.copy()
            else:
Example #41
0
class EAM(Calculator):
    r"""

    EAM Interface Documentation

Introduction
============

The Embedded Atom Method (EAM) [1]_ is a classical potential which is
good for modelling metals, particularly fcc materials. Because it is
an equiaxial potential the EAM does not model directional bonds
well. However, the Angular Dependent Potential (ADP) [2]_ which is an
extended version of EAM is able to model directional bonds and is also
included in the EAM calculator.

Generally all that is required to use this calculator is to supply a
potential file or as a set of functions that describe the potential.
The files containing the potentials for this calculator are not
included but many suitable potentials can be downloaded from The
Interatomic Potentials Repository Project at
http://www.ctcms.nist.gov/potentials/

Theory
======

A single element EAM potential is defined by three functions: the
embedded energy, electron density and the pair potential.  A two
element alloy contains the individual three functions for each element
plus cross pair interactions.  The ADP potential has two additional
sets of data to define the dipole and quadrupole directional terms for
each alloy and their cross interactions.

The total energy `E_{\rm tot}` of an arbitrary arrangement of atoms is
given by the EAM potential as

.. math::
   E_{\rm tot} = \sum_i F(\bar\rho_i) + \frac{1}{2}\sum_{i\ne j} \phi(r_{ij})

and

.. math::
   \bar\rho_i = \sum_j \rho(r_{ij})

where `F` is an embedding function, namely the energy to embed an atom `i` in
the combined electron density `\bar\rho_i` which is contributed from
each of its neighbouring atoms `j` by an amount `\rho(r_{ij})`,
`\phi(r_{ij})` is the pair potential function representing the energy
in bond `ij` which is due to the short-range electro-static
interaction between atoms, and `r_{ij}` is the distance between an
atom and its neighbour for that bond.

The ADP potential is defined as

.. math::
   E_{\rm tot} = \sum_i F(\bar\rho_i) + \frac{1}{2}\sum_{i\ne j} \phi(r_{ij})
   + {1\over 2} \sum_{i,\alpha} (\mu_i^\alpha)^2
   + {1\over 2} \sum_{i,\alpha,\beta} (\lambda_i^{\alpha\beta})^2
   - {1 \over 6} \sum_i \nu_i^2

where `\mu_i^\alpha` is the dipole vector, `\lambda_i^{\alpha\beta}`
is the quadrupole tensor and `\nu_i` is the trace of
`\lambda_i^{\alpha\beta}`.

Running the Calculator
======================

EAM calculates the cohesive atom energy and forces. Internally the
potential functions are defined by splines which may be directly
supplied or created by reading the spline points from a data file from
which a spline function is created.  The LAMMPS compatible ``.alloy``
and ``.adp`` formats are supported. The LAMMPS ``.eam`` format is
slightly different from the ``.alloy`` format and is currently not
supported.

For example::

    from ase.calculators.eam import EAM

    mishin = EAM(potential='Al99.eam.alloy')
    mishin.write_potential('new.eam.alloy')
    mishin.plot()

    slab.set_calculator(mishin)
    slab.get_potential_energy()
    slab.get_forces()

The breakdown of energy contribution from the indvidual components are
stored in the calculator instance ``.results['energy_components']``

Arguments
=========

=========================  ====================================================
Keyword                    Description
=========================  ====================================================
``potential``              file of potential in ``.alloy`` or ``.adp`` format
                           (This is generally all you need to supply)

``elements[N]``            array of N element abbreviations

``embedded_energy[N]``     arrays of embedded energy functions

``electron_density[N]``    arrays of electron density functions

``phi[N,N]``               arrays of pair potential functions

``d_embedded_energy[N]``   arrays of derivative embedded energy functions

``d_electron_density[N]``  arrays of derivative electron density functions

``d_phi[N,N]``             arrays of derivative pair potentials functions

``d[N,N], q[N,N]``         ADP dipole and quadrupole function

``d_d[N,N], d_q[N,N]``     ADP dipole and quadrupole derivative functions

``skin``                   skin distance passed to NeighborList(). If no atom
                           has moved more than the skin-distance since the last
                           call to the ``update()`` method then the neighbor
                           list can be reused. Defaults to 1.0.

``form``                   the form of the potential ``alloy`` or ``adp``. This
                           will be determined from the file suffix or must be
                           set if using equations

=========================  ====================================================


Additional parameters for writing potential files
=================================================

The following parameters are only required for writing a potential in
``.alloy`` or ``.adp`` format file.

=========================  ====================================================
Keyword                    Description
=========================  ====================================================
``header``                 Three line text header. Default is standard message.

``Z[N]``                   Array of atomic number of each element

``mass[N]``                Atomic mass of each element

``a[N]``                   Array of lattice parameters for each element

``lattice[N]``             Lattice type

``nrho``                   No. of rho samples along embedded energy curve

``drho``                   Increment for sampling density

``nr``                     No. of radial points along density and pair
                           potential curves

``dr``                     Increment for sampling radius

=========================  ====================================================

Special features
================

``.plot()``
  Plots the individual functions. This may be called from multiple EAM
  potentials to compare the shape of the individual curves. This
  function requires the installation of the Matplotlib libraries.

Notes/Issues
=============

* Although currently not fast, this calculator can be good for trying
  small calculations or for creating new potentials by matching baseline
  data such as from DFT results. The format for these potentials is
  compatible with LAMMPS_ and so can be used either directly by LAMMPS or
  with the ASE LAMMPS calculator interface.

* Supported formats are the LAMMPS_ ``.alloy`` and ``.adp``. The
  ``.eam`` format is currently not supported. The form of the
  potential will be determined from the file suffix.

* Any supplied values will override values read from the file.

* The derivative functions, if supplied, are only used to calculate
  forces.

* There is a bug in early versions of scipy that will cause eam.py to
  crash when trying to evaluate splines of a potential with one
  neighbor such as caused by evaluating a dimer.

.. _LAMMPS: http://lammps.sandia.gov/

.. [1] M.S. Daw and M.I. Baskes, Phys. Rev. Letters 50 (1983)
       1285.

.. [2] Y. Mishin, M.J. Mehl, and D.A. Papaconstantopoulos,
       Acta Materialia 53 2005 4029--4041.


End EAM Interface Documentation
    """

    implemented_properties = ['energy', 'forces']

    default_parameters = dict(
        skin=1.0,
        potential=None,
        header="""EAM/ADP potential file\nGenerated from eam.py\nblank\n""")

    def __init__(self,
                 restart=None,
                 ignore_bad_restart_file=False,
                 label=os.curdir,
                 atoms=None,
                 **kwargs):

        if 'potential' in kwargs:
            self.read_potential(kwargs['potential'])

        Calculator.__init__(self, restart, ignore_bad_restart_file, label,
                            atoms, **kwargs)

        valid_args = (
            'potential',
            'elements',
            'header',
            'drho',
            'dr',
            'cutoff',
            'atomic_number',
            'mass',
            'a',
            'lattice',
            'embedded_energy',
            'electron_density',
            'phi',
            # derivatives
            'd_embedded_energy',
            'd_electron_density',
            'd_phi',
            'd',
            'q',
            'd_d',
            'd_q',  # adp terms
            'skin',
            'form',
            'Z',
            'nr',
            'nrho',
            'mass')

        # set any additional keyword arguments
        for arg, val in self.parameters.iteritems():
            if arg in valid_args:
                setattr(self, arg, val)
            else:
                raise RuntimeError('unknown keyword arg "%s" : not in %s' %
                                   (arg, valid_args))

    def set_form(self, fileobj):
        """set the form variable based on the file name suffix"""
        extension = os.path.splitext(fileobj)[1]

        if extension == '.eam':
            self.form = 'eam'
            raise NotImplementedError
        elif extension == '.alloy':
            self.form = 'alloy'
        elif extension == '.adp':
            self.form = 'adp'
        else:
            raise RuntimeError('unknown file extension type: %s' % extension)

    def read_potential(self, fileobj):
        """Reads a LAMMPS EAM file in alloy or adp format
        and creates the interpolation functions from the data
        """

        if isinstance(fileobj, str):
            f = open(fileobj)
            self.set_form(fileobj)
        else:
            f = fileobj

        lines = f.readlines()
        self.header = lines[:3]
        i = 3

        # make the data one long line so as not to care how its formatted
        data = []
        for line in lines[i:]:
            data.extend(line.split())

        self.Nelements = int(data[0])
        d = 1
        self.elements = data[d:(d + self.Nelements)]
        d += self.Nelements

        self.nrho = int(data[d])
        self.drho = float(data[d + 1])
        self.nr = int(data[d + 2])
        self.dr = float(data[d + 3])
        self.cutoff = float(data[d + 4])

        self.embedded_data = np.zeros([self.Nelements, self.nrho])
        self.density_data = np.zeros([self.Nelements, self.nr])
        self.Z = np.zeros([self.Nelements], dtype=int)
        self.mass = np.zeros([self.Nelements])
        self.a = np.zeros([self.Nelements])
        self.lattice = []
        d += 5

        # reads in the part of the eam file for each element
        for elem in range(self.Nelements):
            self.Z[elem] = int(data[d])
            self.mass[elem] = float(data[d + 1])
            self.a[elem] = float(data[d + 2])
            self.lattice.append(data[d + 3])
            d += 4

            self.embedded_data[elem] = np.float_(data[d:(d + self.nrho)])
            d += self.nrho
            self.density_data[elem] = np.float_(data[d:(d + self.nr)])
            d += self.nr

        # reads in the r*phi data for each interaction between elements
        self.rphi_data = np.zeros([self.Nelements, self.Nelements, self.nr])

        for i in range(self.Nelements):
            for j in range(i + 1):
                self.rphi_data[j, i] = np.float_(data[d:(d + self.nr)])
                d += self.nr

        self.r = np.arange(0, self.nr) * self.dr
        self.rho = np.arange(0, self.nrho) * self.drho

        self.set_splines()

        if (self.form == 'adp'):
            self.read_adp_data(data, d)
            self.set_adp_splines()

    def set_splines(self):
        # this section turns the file data into three functions (and
        # derivative functions) that define the potential
        self.embedded_energy = np.empty(self.Nelements, object)
        self.electron_density = np.empty(self.Nelements, object)
        self.d_embedded_energy = np.empty(self.Nelements, object)
        self.d_electron_density = np.empty(self.Nelements, object)

        for i in range(self.Nelements):
            self.embedded_energy[i] = spline(self.rho,
                                             self.embedded_data[i],
                                             k=3)
            self.electron_density[i] = spline(self.r,
                                              self.density_data[i],
                                              k=3)
            self.d_embedded_energy[i] = self.deriv(self.embedded_energy[i])
            self.d_electron_density[i] = self.deriv(self.electron_density[i])

        self.phi = np.empty([self.Nelements, self.Nelements], object)
        self.d_phi = np.empty([self.Nelements, self.Nelements], object)

        # ignore the first point of the phi data because it is forced
        # to go through zero due to the r*phi format in alloy and adp
        for i in range(self.Nelements):
            for j in range(i, self.Nelements):
                if self.form == 'eam':  # not stored as rphi
                    # should we ignore the first point for eam ?
                    raise RuntimeError('.eam format not yet supported')

                    self.phi[i, j] = spline(self.r[1:],
                                            self.rphi_data[i, j][1:],
                                            k=3)
                else:
                    self.phi[i,
                             j] = spline(self.r[1:],
                                         self.rphi_data[i, j][1:] / self.r[1:],
                                         k=3)

                self.d_phi[i, j] = self.deriv(self.phi[i, j])

                if j != i:
                    self.phi[j, i] = self.phi[i, j]
                    self.d_phi[j, i] = self.d_phi[i, j]

    def set_adp_splines(self):
        self.d = np.empty([self.Nelements, self.Nelements], object)
        self.d_d = np.empty([self.Nelements, self.Nelements], object)
        self.q = np.empty([self.Nelements, self.Nelements], object)
        self.d_q = np.empty([self.Nelements, self.Nelements], object)

        for i in range(self.Nelements):
            for j in range(i, self.Nelements):
                self.d[i, j] = spline(self.r[1:], self.d_data[i, j][1:], k=3)
                self.d_d[i, j] = self.deriv(self.d[i, j])
                self.q[i, j] = spline(self.r[1:], self.q_data[i, j][1:], k=3)
                self.d_q[i, j] = self.deriv(self.q[i, j])

                # make symmetrical
                if j != i:
                    self.d[j, i] = self.d[i, j]
                    self.d_d[j, i] = self.d_d[i, j]
                    self.q[j, i] = self.q[i, j]
                    self.d_q[j, i] = self.d_q[i, j]

    def read_adp_data(self, data, d):
        """read in the extra adp data from the potential file"""

        self.d_data = np.zeros([self.Nelements, self.Nelements, self.nr])
        # should be non symetrical combinations of 2
        for i in range(self.Nelements):
            for j in range(i + 1):
                self.d_data[j, i] = data[d:d + self.nr]
                d += self.nr

        self.q_data = np.zeros([self.Nelements, self.Nelements, self.nr])
        # should be non symetrical combinations of 2
        for i in range(self.Nelements):
            for j in range(i + 1):
                self.q_data[j, i] = data[d:d + self.nr]
                d += self.nr

    def write_potential(self, filename, nc=1, numformat='%.8e'):
        """Writes out the potential in the format given by the form
        variable to 'filename' with a data format that is nc columns
        wide.  Note: array lengths need to be an exact multiple of nc
        """

        f = open(filename, 'w')

        assert self.nr % nc == 0
        assert self.nrho % nc == 0

        for line in self.header:
            f.write(line)

        f.write('%d ' % self.Nelements)
        for i in range(self.Nelements):
            f.write('%s ' % str(self.elements[i]))
        f.write('\n')

        f.write('%d %f %d %f %f \n' %
                (self.nrho, self.drho, self.nr, self.dr, self.cutoff))

        # start of each section for each element
        #        rs = np.linspace(0, self.nr * self.dr, self.nr)
        #        rhos = np.linspace(0, self.nrho * self.drho, self.nrho)

        rs = np.arange(0, self.nr) * self.dr
        rhos = np.arange(0, self.nrho) * self.drho

        for i in range(self.Nelements):
            f.write('%d %f %f %s\n' %
                    (self.Z[i], self.mass[i], self.a[i], str(self.lattice[i])))
            np.savetxt(f,
                       self.embedded_energy[i](rhos).reshape(
                           self.nrho / nc, nc),
                       fmt=nc * [numformat])
            np.savetxt(f,
                       self.electron_density[i](rs).reshape(self.nr / nc, nc),
                       fmt=nc * [numformat])

        # write out the pair potentials in Lammps DYNAMO setfl format
        # as r*phi for alloy format
        for i in range(self.Nelements):
            for j in range(i, self.Nelements):
                np.savetxt(f,
                           (rs * self.phi[i, j](rs)).reshape(self.nr / nc, nc),
                           fmt=nc * [numformat])

        if self.form == 'adp':
            # these are the u(r) or dipole values
            for i in range(self.Nelements):
                for j in range(i + 1):
                    np.savetxt(f, self.d_data[i, j])

            # these are the w(r) or quadrupole values
            for i in range(self.Nelements):
                for j in range(i + 1):
                    np.savetxt(f, self.q_data[i, j])

        f.close()

    def update(self, atoms):
        # check all the elements are available in the potential
        self.Nelements = len(self.elements)
        elements = np.unique(atoms.get_chemical_symbols())
        unavailable = np.logical_not(
            np.array([item in self.elements for item in elements]))

        if np.any(unavailable):
            raise RuntimeError('These elements are not in the potential: %s' %
                               elements[unavailable])

        # cutoffs need to be a vector for NeighborList
        cutoffs = self.cutoff * np.ones(len(atoms))

        # convert the elements to an index of the position
        # in the eam format
        self.index = np.array(
            [self.elements.index(el) for el in atoms.get_chemical_symbols()])
        self.pbc = atoms.get_pbc()

        # since we need the contribution of all neighbors to the
        # local electron density we cannot just calculate and use
        # one way neighbors
        self.neighbors = NeighborList(cutoffs,
                                      skin=self.parameters.skin,
                                      self_interaction=False,
                                      bothways=True)
        self.neighbors.update(atoms)

    def calculate(self,
                  atoms=None,
                  properties=['energy'],
                  system_changes=all_changes):
        """EAM Calculator

        atoms: Atoms object
            Contains positions, unit-cell, ...
        properties: list of str
            List of what needs to be calculated.  Can be any combination
            of 'energy', 'forces'
        system_changes: list of str
            List of what has changed since last calculation.  Can be
            any combination of these five: 'positions', 'numbers', 'cell',
            'pbc', 'initial_charges' and 'initial_magmoms'.
            """

        Calculator.calculate(self, atoms, properties, system_changes)

        # we shouldn't really recalc if charges or magmos change
        if len(system_changes) > 0:  # something wrong with this way
            self.update(self.atoms)
            self.calculate_energy(self.atoms)

            if 'forces' in properties:
                self.calculate_forces(self.atoms)

        # check we have all the properties requested
        for property in properties:
            if property not in self.results:
                if property is 'energy':
                    self.calculate_energy(self.atoms)

                if property is 'forces':
                    self.calculate_forces(self.atoms)

        # we need to remember the previous state of parameters
#        if 'potential' in parameter_changes and potential != None:
#                self.read_potential(potential)

    def calculate_energy(self, atoms):
        """Calculate the energy
        the energy is made up of the ionic or pair interaction and
        the embedding energy of each atom into the electron cloud
        generated by its neighbors
        """

        pair_energy = 0.0
        embedding_energy = 0.0
        mu_energy = 0.0
        lam_energy = 0.0
        trace_energy = 0.0

        self.total_density = np.zeros(len(atoms))
        if (self.form == 'adp'):
            self.mu = np.zeros([len(atoms), 3])
            self.lam = np.zeros([len(atoms), 3, 3])

        for i in range(len(atoms)):  # this is the atom to be embedded
            neighbors, offsets = self.neighbors.get_neighbors(i)
            offset = np.dot(offsets, atoms.get_cell())

            rvec = (atoms.positions[neighbors] + offset - atoms.positions[i])

            ## calculate the distance to the nearest neighbors
            r = np.sqrt(np.sum(np.square(rvec), axis=1))  # fast
            #            r = np.apply_along_axis(np.linalg.norm, 1, rvec)  # sloow

            nearest = np.arange(len(r))[r <= self.cutoff]
            for j_index in range(self.Nelements):
                use = self.index[neighbors[nearest]] == j_index
                if not use.any():
                    continue
                pair_energy += np.sum(self.phi[self.index[i], j_index](
                    r[nearest][use])) / 2.

                density = np.sum(self.electron_density[j_index](
                    r[nearest][use]))
                self.total_density[i] += density

                if self.form == 'adp':
                    self.mu[i] += self.adp_dipole(
                        r[nearest][use], rvec[nearest][use],
                        self.d[self.index[i], j_index])

                    self.lam[i] += self.adp_quadrupole(
                        r[nearest][use], rvec[nearest][use],
                        self.q[self.index[i], j_index])

            # add in the electron embedding energy
            embedding_energy += self.embedded_energy[self.index[i]](
                self.total_density[i])

        components = dict(pair=pair_energy, embedding=embedding_energy)

        if self.form == 'adp':
            mu_energy += np.sum(self.mu**2) / 2.
            lam_energy += np.sum(self.lam**2) / 2.

            for i in range(len(atoms)):  # this is the atom to be embedded
                trace_energy -= np.sum(self.lam[i].trace()**2) / 6.

            adp_result = dict(adp_mu=mu_energy,
                              adp_lam=lam_energy,
                              adp_trace=trace_energy)
            components.update(adp_result)

        self.positions = atoms.positions.copy()
        self.cell = atoms.get_cell().copy()

        energy = 0.0
        for i in components.keys():
            energy += components[i]

        self.energy_free = energy
        self.energy_zero = energy

        self.results['energy_components'] = components
        self.results['energy'] = energy

    def calculate_forces(self, atoms):
        # calculate the forces based on derivatives of the three EAM functions

        self.update(atoms)
        self.results['forces'] = np.zeros((len(atoms), 3))

        for i in range(len(atoms)):  # this is the atom to be embedded
            neighbors, offsets = self.neighbors.get_neighbors(i)
            offset = np.dot(offsets, atoms.get_cell())
            # create a vector of relative positions of neighbors
            rvec = atoms.positions[neighbors] + offset - atoms.positions[i]
            r = np.sqrt(np.sum(np.square(rvec), axis=1))
            nearest = np.arange(len(r))[r < self.cutoff]

            d_embedded_energy_i = self.d_embedded_energy[self.index[i]](
                self.total_density[i])
            urvec = rvec.copy()  # unit directional vector

            for j in np.arange(len(neighbors)):
                urvec[j] = urvec[j] / r[j]

            for j_index in range(self.Nelements):
                use = self.index[neighbors[nearest]] == j_index
                if not use.any():
                    continue
                rnuse = r[nearest][use]
                density_j = self.total_density[neighbors[nearest][use]]
                scale = (self.d_phi[self.index[i], j_index](rnuse) +
                         (d_embedded_energy_i *
                          self.d_electron_density[j_index](rnuse)) +
                         (self.d_embedded_energy[j_index](density_j) *
                          self.d_electron_density[self.index[i]](rnuse)))

                self.results['forces'][i] += np.dot(scale, urvec[nearest][use])

                if (self.form == 'adp'):
                    adp_forces = self.angular_forces(
                        self.mu[i], self.mu[neighbors[nearest][use]],
                        self.lam[i], self.lam[neighbors[nearest][use]], rnuse,
                        rvec[nearest][use], self.index[i], j_index)

                    self.results['forces'][i] += adp_forces

    def angular_forces(self, mu_i, mu, lam_i, lam, r, rvec, form1, form2):
        # calculate the extra components for the adp forces
        # rvec are the relative positions to atom i
        psi = np.zeros(mu.shape)
        for gamma in range(3):
            term1 = (mu_i[gamma] - mu[:, gamma]) * self.d[form1][form2](r)

            term2 = np.sum(
                (mu_i - mu) * self.d_d[form1][form2](r)[:, np.newaxis] *
                (rvec * rvec[:, gamma][:, np.newaxis] / r[:, np.newaxis]),
                axis=1)

            term3 = 2 * np.sum((lam_i[:, gamma] + lam[:, :, gamma]) * rvec *
                               self.q[form1][form2](r)[:, np.newaxis],
                               axis=1)
            term4 = 0.0
            for alpha in range(3):
                for beta in range(3):
                    rs = rvec[:, alpha] * rvec[:, beta] * rvec[:, gamma]
                    term4 += ((lam_i[alpha, beta] + lam[:, alpha, beta]) *
                              self.d_q[form1][form2](r) * rs) / r

            term5 = ((lam_i.trace() + lam.trace(axis1=1, axis2=2)) *
                     (self.d_q[form1][form2](r) * r + 2 * self.q[form1][form2]
                      (r)) * rvec[:, gamma]) / 3.

            # the minus for term5 is a correction on the adp
            # formulation given in the 2005 Mishin Paper and is posted
            # on the NIST website with the AlH potential
            psi[:, gamma] = term1 + term2 + term3 + term4 - term5

        return np.sum(psi, axis=0)

    def adp_dipole(self, r, rvec, d):
        # calculate the dipole contribution
        mu = np.sum((rvec * d(r)[:, np.newaxis]), axis=0)

        return mu  # sign to agree with lammps

    def adp_quadrupole(self, r, rvec, q):
        # slow way of calculating the quadrupole contribution
        r = np.sqrt(np.sum(rvec**2, axis=1))

        lam = np.zeros([rvec.shape[0], 3, 3])
        qr = q(r)
        for alpha in range(3):
            for beta in range(3):
                lam[:, alpha, beta] += qr * rvec[:, alpha] * rvec[:, beta]

        return np.sum(lam, axis=0)

    def deriv(self, spline):
        """Wrapper for extracting the derivative from a spline"""
        def d_spline(aspline):
            return spline(aspline, 1)

        return d_spline

    def plot(self, name=''):
        """Plot the individual curves"""

        try:
            import matplotlib.pyplot as plt

        except ImportError:
            raise NotAvailable('This needs matplotlib module.')

        if self.form == 'eam' or self.form == 'alloy':
            nrow = 2
        elif self.form == 'adp':
            nrow = 3
        else:
            raise RuntimeError('Unknown form of potential: %s' % self.form)

        if hasattr(self, 'r'):
            r = self.r
        else:
            r = np.linspace(0, self.cutoff, 50)

        if hasattr(self, 'rho'):
            rho = self.rho
        else:
            rho = np.linspace(0, 10.0, 50)

        plt.subplot(nrow, 2, 1)
        self.elem_subplot(rho, self.embedded_energy, r'$\rho$',
                          r'Embedding Energy $F(\bar\rho)$', name, plt)

        plt.subplot(nrow, 2, 2)
        self.elem_subplot(r, self.electron_density, r'$r$',
                          r'Electron Density $\rho(r)$', name, plt)

        plt.subplot(nrow, 2, 3)
        self.multielem_subplot(r, self.phi, r'$r$',
                               r'Pair Potential $\phi(r)$', name, plt)
        plt.ylim(-1.0, 1.0)  # need reasonable values

        if self.form == 'adp':
            plt.subplot(nrow, 2, 5)
            self.multielem_subplot(r, self.d, r'$r$', r'Dipole Energy', name,
                                   plt)

            plt.subplot(nrow, 2, 6)
            self.multielem_subplot(r, self.q, r'$r$', r'Quadrupole Energy',
                                   name, plt)

        plt.plot()

    def elem_subplot(self, curvex, curvey, xlabel, ylabel, name, plt):
        plt.xlabel(xlabel)
        plt.ylabel(ylabel)
        for i in np.arange(self.Nelements):
            label = name + ' ' + self.elements[i]
            plt.plot(curvex, curvey[i](curvex), label=label)
        plt.legend()

    def multielem_subplot(self, curvex, curvey, xlabel, ylabel, name, plt):
        plt.xlabel(xlabel)
        plt.ylabel(ylabel)
        for i in np.arange(self.Nelements):
            for j in np.arange(i + 1):
                label = name + ' ' + self.elements[i] + '-' + self.elements[j]
                plt.plot(curvex, curvey[i, j](curvex), label=label)
        plt.legend()
Example #42
0
class EMT:
    disabled = False  # Set to True to disable (asap does this).
    
    def __init__(self, fakestress=False):
        self.energy = None
        self.name = 'EMT'
        self.version = '1.0'
        # fakestress is needed to fake some stress value for the testsuite
        # in order to test the filter functionality.
        self.fakestress = fakestress
        if self.disabled:
            print >> sys.stderr, """
            ase.EMT has been disabled by Asap.  Most likely, you
            intended to use Asap's EMT calculator, but accidentally
            imported ase's EMT calculator after Asap's.  This could
            happen if your script contains the lines

              from asap3 import *
              from ase.calculators.emt import EMT
            Swap the two lines to solve the problem.

            (or 'from ase import *' in older versions of ASE.) Swap 
            the two lines to solve the problem.

            In the UNLIKELY event that you actually wanted to use
            ase.calculators.emt.EMT although asap3 is loaded into memory,
            please reactivate it with the command
              ase.calculators.emt.EMT.disabled = False
            """
            raise RuntimeError('ase.EMT has been disabled.  ' +
                               'See message printed above.')
        
    def get_name(self):
        return self.name

    def get_version(self):
        return self.version

    def get_spin_polarized(self):
        return False
    
    def initialize(self, atoms):
        self.par = {}
        self.rc = 0.0
        self.numbers = atoms.get_atomic_numbers()
        maxseq = max(par[1] for par in parameters.values()) * Bohr
        rc = self.rc = beta * maxseq * 0.5 * (sqrt(3) + sqrt(4))
        rr = rc * 2 * sqrt(4) / (sqrt(3) + sqrt(4))
        self.acut = np.log(9999.0) / (rr - rc)
        for Z in self.numbers:
            if Z not in self.par:
                p = parameters[chemical_symbols[Z]]
                s0 = p[1] * Bohr
                eta2 = p[3] / Bohr
                kappa = p[4] / Bohr
                x = eta2 * beta * s0
                gamma1 = 0.0
                gamma2 = 0.0
                for i, n in enumerate([12, 6, 24]):
                    r = s0 * beta * sqrt(i + 1)
                    x = n / (12 * (1.0 + exp(self.acut * (r - rc))))
                    gamma1 += x * exp(-eta2 * (r - beta * s0))
                    gamma2 += x * exp(-kappa / beta * (r - beta * s0))

                self.par[Z] = {'E0': p[0],
                               's0': s0,
                               'V0': p[2],
                               'eta2': eta2,
                               'kappa': kappa,
                               'lambda': p[5] / Bohr,
                               'n0': p[6] / Bohr**3,
                               'rc': rc,
                               'gamma1': gamma1,
                               'gamma2': gamma2}
                #if rc + 0.5 > self.rc:
                #    self.rc = rc + 0.5

        self.ksi = {}
        for s1, p1 in self.par.items():
            self.ksi[s1] = {}
            for s2, p2 in self.par.items():
                #self.ksi[s1][s2] = (p2['n0'] / p1['n0'] *
                #                    exp(eta1 * (p1['s0'] - p2['s0'])))
                self.ksi[s1][s2] = p2['n0'] / p1['n0']
                
        self.forces = np.empty((len(atoms), 3))
        self.sigma1 = np.empty(len(atoms))
        self.deds = np.empty(len(atoms))
                    
        self.nl = NeighborList([0.5 * self.rc + 0.25] * len(atoms),
                               self_interaction=False)

    def update(self, atoms):
        if (self.energy is None or
            len(self.numbers) != len(atoms) or
            (self.numbers != atoms.get_atomic_numbers()).any()):
            self.initialize(atoms)
            self.calculate(atoms)
        elif ((self.positions != atoms.get_positions()).any() or
              (self.pbc != atoms.get_pbc()).any() or
              (self.cell != atoms.get_cell()).any()):
            self.calculate(atoms)

    def calculation_required(self, atoms, quantities):
        if len(quantities) == 0:
            return False

        return (self.energy is None or
                len(self.numbers) != len(atoms) or
                (self.numbers != atoms.get_atomic_numbers()).any() or
                (self.positions != atoms.get_positions()).any() or
                (self.pbc != atoms.get_pbc()).any() or
                (self.cell != atoms.get_cell()).any())
                
    def get_number_of_iterations(self):
        return 0

    def get_potential_energy(self, atoms):
        self.update(atoms)
        return self.energy

    def get_numeric_forces(self, atoms):
        self.update(atoms)
        p = atoms.positions
        p0 = p.copy()
        forces = np.empty_like(p)
        eps = 0.0001
        for a in range(len(p)):
            for c in range(3):
                p[a, c] += eps
                self.calculate(atoms)
                de = self.energy
                p[a, c] -= 2 * eps
                self.calculate(atoms)
                de -= self.energy
                p[a, c] += eps
                forces[a, c] = -de / (2 * eps)
        p[:] = p0
        return forces

    def get_forces(self, atoms):
        self.update(atoms)
        return self.forces.copy()
    
    def get_stress(self, atoms):
        if self.fakestress:
            return np.zeros((6))
        else:
            raise NotImplementedError
    
    def calculate(self, atoms):
        self.positions = atoms.get_positions().copy()
        self.cell = atoms.get_cell().copy()
        self.pbc = atoms.get_pbc().copy()
        
        self.nl.update(atoms)
        
        self.energy = 0.0
        self.sigma1[:] = 0.0
        self.forces[:] = 0.0
        
        natoms = len(atoms)

        for a1 in range(natoms):
            Z1 = self.numbers[a1]
            p1 = self.par[Z1]
            ksi = self.ksi[Z1]
            neighbors, offsets = self.nl.get_neighbors(a1)
            offsets = np.dot(offsets, atoms.cell)
            for a2, offset in zip(neighbors, offsets):
                d = self.positions[a2] + offset - self.positions[a1]
                r = sqrt(np.dot(d, d))
                if r < self.rc + 0.5:
                    Z2 = self.numbers[a2]
                    p2 = self.par[Z2]
                    self.interact1(a1, a2, d, r, p1, p2, ksi[Z2])
                                
        for a in range(natoms):
            Z = self.numbers[a]
            p = self.par[Z]
            try:
                ds = -log(self.sigma1[a] / 12) / (beta * p['eta2'])
            except (OverflowError, ValueError):
                self.deds[a] = 0.0
                self.energy -= p['E0']
                continue
            x = p['lambda'] * ds
            y = exp(-x)
            z = 6 * p['V0'] * exp(-p['kappa'] * ds)
            self.deds[a] = ((x * y * p['E0'] * p['lambda'] + p['kappa'] * z) /
                            (self.sigma1[a] * beta * p['eta2']))
            e = p['E0'] * ((1 + x) * y - 1) + z
            self.energy += p['E0'] * ((1 + x) * y - 1) + z

        for a1 in range(natoms):
            Z1 = self.numbers[a1]
            p1 = self.par[Z1]
            ksi = self.ksi[Z1]
            neighbors, offsets = self.nl.get_neighbors(a1)
            offsets = np.dot(offsets, atoms.cell)
            for a2, offset in zip(neighbors, offsets):
                d = self.positions[a2] + offset - self.positions[a1]
                r = sqrt(np.dot(d, d))
                if r < self.rc + 0.5:
                    Z2 = self.numbers[a2]
                    p2 = self.par[Z2]
                    self.interact2(a1, a2, d, r, p1, p2, ksi[Z2])

    def interact1(self, a1, a2, d, r, p1, p2, ksi):
        x = exp(self.acut * (r - self.rc))
        theta = 1.0 / (1.0 + x)
        y1 = (0.5 * p1['V0'] * exp(-p2['kappa'] * (r / beta - p2['s0'])) *
              ksi / p1['gamma2'] * theta)
        y2 = (0.5 * p2['V0'] * exp(-p1['kappa'] * (r / beta - p1['s0'])) /
              ksi / p2['gamma2'] * theta)
        self.energy -= y1 + y2
        f = ((y1 * p2['kappa'] + y2 * p1['kappa']) / beta +
             (y1 + y2) * self.acut * theta * x) * d / r
        self.forces[a1] += f
        self.forces[a2] -= f
        self.sigma1[a1] += (exp(-p2['eta2'] * (r - beta * p2['s0'])) *
                            ksi * theta / p1['gamma1'])
        self.sigma1[a2] += (exp(-p1['eta2'] * (r - beta * p1['s0'])) /
                            ksi * theta / p2['gamma1'])

    def interact2(self, a1, a2, d, r, p1, p2, ksi):
        x = exp(self.acut * (r - self.rc))
        theta = 1.0 / (1.0 + x)
        y1 = (exp(-p2['eta2'] * (r - beta * p2['s0'])) *
              ksi / p1['gamma1'] * theta * self.deds[a1])
        y2 = (exp(-p1['eta2'] * (r - beta * p1['s0'])) /
              ksi / p2['gamma1'] * theta * self.deds[a2])
        f = ((y1 * p2['eta2'] + y2 * p1['eta2']) +
             (y1 + y2) * self.acut * theta * x) * d / r
        self.forces[a1] -= f
        self.forces[a2] += f

    def set_atoms(self,*args,**kwargs):
        'empty function for compatibility with other calculators and tests'
        pass
Example #43
0
class EMT:
    disabled = False  # Set to True to disable (asap does this).
    
    def __init__(self):
        self.energy = None
        if self.disabled:
            print >> sys.stderr, """
            ase.EMT has been disabled by Asap.  Most likely, you
            intended to use Asap's EMT calculator, but accidentally
            imported ase's EMT calculator after Asap's.  This could
            happen if your script contains the lines

              from asap3 import *
              from ase.calculators.emt import EMT
            Swap the two lines to solve the problem.

            (or 'from ase import *' in older versions of ASE.) Swap 
            the two lines to solve the problem.

            In the UNLIKELY event that you actually wanted to use
            ase.calculators.emt.EMT although asap3 is loaded into memory,
            please reactivate it with the command
              ase.calculators.emt.EMT.disabled = False
            """
            raise RuntimeError('ase.EMT has been disabled.  ' +
                               'See message printed above.')
        
    def get_spin_polarized(self):
        return False
    
    def initialize(self, atoms):
        self.par = {}
        self.rc = 0.0
        self.numbers = atoms.get_atomic_numbers()
        maxseq = 0.0
        seen = {}
        for Z in self.numbers:
            if Z not in seen:
                seen[Z] = True
                ss = parameters[chemical_symbols[Z]][1] * Bohr
                if maxseq < ss:
                    maxseq = ss
        rc = self.rc = beta * maxseq * 0.5 * (sqrt(3) + sqrt(4))
        rr = rc * 2 * sqrt(4) / (sqrt(3) + sqrt(4))
        self.acut = np.log(9999.0) / (rr - rc)
        for Z in self.numbers:
            if Z not in self.par:
                p = parameters[chemical_symbols[Z]]
                s0 = p[1] * Bohr
                eta2 = p[3] / Bohr
                kappa = p[4] / Bohr
                x = eta2 * beta * s0
                gamma1 = 0.0
                gamma2 = 0.0
                for i, n in enumerate([12, 6, 24]):
                    r = s0 * beta * sqrt(i + 1)
                    x = n / (12 * (1.0 + exp(self.acut * (r - rc))))
                    gamma1 += x * exp(-eta2 * (r - beta * s0))
                    gamma2 += x * exp(-kappa / beta * (r - beta * s0))

                self.par[Z] = {'E0': p[0],
                               's0': s0,
                               'V0': p[2],
                               'eta2': eta2,
                               'kappa': kappa,
                               'lambda': p[5] / Bohr,
                               'n0': p[6] / Bohr**3,
                               'rc': rc,
                               'gamma1': gamma1,
                               'gamma2': gamma2}
                #if rc + 0.5 > self.rc:
                #    self.rc = rc + 0.5

        self.ksi = {}
        for s1, p1 in self.par.items():
            self.ksi[s1] = {}
            for s2, p2 in self.par.items():
                #self.ksi[s1][s2] = (p2['n0'] / p1['n0'] *
                #                    exp(eta1 * (p1['s0'] - p2['s0'])))
                self.ksi[s1][s2] = p2['n0'] / p1['n0']
                
        self.forces = np.empty((len(atoms), 3))
        self.sigma1 = np.empty(len(atoms))
        self.deds = np.empty(len(atoms))
                    
        self.nl = NeighborList([0.5 * self.rc + 0.25] * len(atoms),
                               self_interaction=False)

    def update(self, atoms):
        if (self.energy is None or
            len(self.numbers) != len(atoms) or
            (self.numbers != atoms.get_atomic_numbers()).any()):
            self.initialize(atoms)
            self.calculate(atoms)
        elif ((self.positions != atoms.get_positions()).any() or
              (self.pbc != atoms.get_pbc()).any() or
              (self.cell != atoms.get_cell()).any()):
            self.calculate(atoms)

    def calculation_required(self, atoms, quantities):
        if len(quantities) == 0:
            return False

        return (self.energy is None or
                len(self.numbers) != len(atoms) or
                (self.numbers != atoms.get_atomic_numbers()).any() or
                (self.positions != atoms.get_positions()).any() or
                (self.pbc != atoms.get_pbc()).any() or
                (self.cell != atoms.get_cell()).any())
                
    def get_potential_energy(self, atoms):
        self.update(atoms)
        return self.energy

    def get_numeric_forces(self, atoms):
        self.update(atoms)
        p = atoms.positions
        p0 = p.copy()
        forces = np.empty_like(p)
        eps = 0.0001
        for a in range(len(p)):
            for c in range(3):
                p[a, c] += eps
                self.calculate(atoms)
                de = self.energy
                p[a, c] -= 2 * eps
                self.calculate(atoms)
                de -= self.energy
                p[a, c] += eps
                forces[a, c] = -de / (2 * eps)
        p[:] = p0
        return forces

    def get_forces(self, atoms):
        self.update(atoms)
        return self.forces.copy()
    
    def get_stress(self, atoms):
        raise NotImplementedError
    
    def calculate(self, atoms):
        self.positions = atoms.get_positions().copy()
        self.cell = atoms.get_cell().copy()
        self.pbc = atoms.get_pbc().copy()
        
        self.nl.update(atoms)
        
        self.energy = 0.0
        self.sigma1[:] = 0.0
        self.forces[:] = 0.0
        
        natoms = len(atoms)

        for a1 in range(natoms):
            Z1 = self.numbers[a1]
            p1 = self.par[Z1]
            ksi = self.ksi[Z1]
            neighbors, offsets = self.nl.get_neighbors(a1)
            offsets = np.dot(offsets, atoms.cell)
            for a2, offset in zip(neighbors, offsets):
                d = self.positions[a2] + offset - self.positions[a1]
                r = sqrt(np.dot(d, d))
                if r < self.rc + 0.5:
                    Z2 = self.numbers[a2]
                    p2 = self.par[Z2]
                    self.interact1(a1, a2, d, r, p1, p2, ksi[Z2])
                                
        for a in range(natoms):
            Z = self.numbers[a]
            p = self.par[Z]
            try:
                ds = -log(self.sigma1[a] / 12) / (beta * p['eta2'])
            except (OverflowError, ValueError):
                self.deds[a] = 0.0
                self.energy -= p['E0']
                continue
            x = p['lambda'] * ds
            y = exp(-x)
            z = 6 * p['V0'] * exp(-p['kappa'] * ds)
            self.deds[a] = ((x * y * p['E0'] * p['lambda'] + p['kappa'] * z) /
                            (self.sigma1[a] * beta * p['eta2']))
            e = p['E0'] * ((1 + x) * y - 1) + z
            self.energy += p['E0'] * ((1 + x) * y - 1) + z

        for a1 in range(natoms):
            Z1 = self.numbers[a1]
            p1 = self.par[Z1]
            ksi = self.ksi[Z1]
            neighbors, offsets = self.nl.get_neighbors(a1)
            offsets = np.dot(offsets, atoms.cell)
            for a2, offset in zip(neighbors, offsets):
                d = self.positions[a2] + offset - self.positions[a1]
                r = sqrt(np.dot(d, d))
                if r < self.rc + 0.5:
                    Z2 = self.numbers[a2]
                    p2 = self.par[Z2]
                    self.interact2(a1, a2, d, r, p1, p2, ksi[Z2])

    def interact1(self, a1, a2, d, r, p1, p2, ksi):
        x = exp(self.acut * (r - self.rc))
        theta = 1.0 / (1.0 + x)
        y1 = (0.5 * p1['V0'] * exp(-p2['kappa'] * (r / beta - p2['s0'])) *
              ksi / p1['gamma2'] * theta)
        y2 = (0.5 * p2['V0'] * exp(-p1['kappa'] * (r / beta - p1['s0'])) /
              ksi / p2['gamma2'] * theta)
        self.energy -= y1 + y2
        f = ((y1 * p2['kappa'] + y2 * p1['kappa']) / beta +
             (y1 + y2) * self.acut * theta * x) * d / r
        self.forces[a1] += f
        self.forces[a2] -= f
        self.sigma1[a1] += (exp(-p2['eta2'] * (r - beta * p2['s0'])) *
                            ksi * theta / p1['gamma1'])
        self.sigma1[a2] += (exp(-p1['eta2'] * (r - beta * p1['s0'])) /
                            ksi * theta / p2['gamma1'])

    def interact2(self, a1, a2, d, r, p1, p2, ksi):
        x = exp(self.acut * (r - self.rc))
        theta = 1.0 / (1.0 + x)
        y1 = (exp(-p2['eta2'] * (r - beta * p2['s0'])) *
              ksi / p1['gamma1'] * theta * self.deds[a1])
        y2 = (exp(-p1['eta2'] * (r - beta * p1['s0'])) /
              ksi / p2['gamma1'] * theta * self.deds[a2])
        f = ((y1 * p2['eta2'] + y2 * p1['eta2']) +
             (y1 + y2) * self.acut * theta * x) * d / r
        self.forces[a1] -= f
        self.forces[a2] += f

    def set_atoms(self,*args,**kwargs):
        'empty function for compatibility with other calculators and tests'
        pass
Example #44
0
class OPLSff:
    def __init__(self, fileobj=None, warnings=0):
        self.warnings = warnings
        self.data = {}
        if fileobj is not None:
            self.read(fileobj)

    def read(self, fileobj, comments='#'):
        if isinstance(fileobj, str):
            fileobj = open(fileobj)

        def read_block(name, symlen, nvalues):
            """Read a data block.

            name: name of the block to store in self.data
            symlen: length of the symbol
            nvalues: number of values expected
            """

            if name not in self.data:
                self.data[name] = {}
            data = self.data[name]

            def add_line():
                line = fileobj.readline().strip()
                if not len(line):  # end of the block
                    return False
                line = line.split('#')[0]  # get rid of comments
                if len(line) > symlen:
                    symbol = line[:symlen]
                    words = line[symlen:].split()
                    if len(words) >= nvalues:
                        if nvalues == 1:
                            data[symbol] = float(words[0])
                        else:
                            data[symbol] = [float(word)
                                            for word in words[:nvalues]]
                return True

            while add_line():
                pass
 
        read_block('one', 2, 3)
        read_block('bonds', 5, 2)
        read_block('angles', 8, 2)
        read_block('dihedrals', 11, 4)
        read_block('cutoffs', 5, 1)

        self.bonds = BondData(self.data['bonds'])
        self.angles = AnglesData(self.data['angles'])
        self.dihedrals = DihedralsData(self.data['dihedrals'])
        self.cutoffs = CutoffList(self.data['cutoffs'])

    def write_lammps(self, atoms, prefix='lammps'):
        """Write input for a LAMMPS calculation."""
        self.prefix = prefix

        if hasattr(atoms, 'connectivities'):
            connectivities = atoms.connectivities
        else:
            btypes, blist = self.get_bonds(atoms)
            atypes, alist = self.get_angles()
            dtypes, dlist = self.get_dihedrals(alist, atypes)
            connectivities = {
                'bonds': blist,
                'bond types': btypes,
                'angles': alist,
                'angle types': atypes,
                'dihedrals': dlist,
                'dihedral types': dtypes,
                }
            self.write_lammps_definitions(atoms, btypes, atypes, dtypes)
            self.write_lammps_in()
            
        self.write_lammps_atoms(atoms, connectivities)

    def write_lammps_in(self):
        fileobj = self.prefix + '_in'
        if isinstance(fileobj, str):
            fileobj = open(fileobj, 'w')
        fileobj.write("""# LAMMPS relaxation (written by ASE)

units           metal
atom_style      full
boundary        p p p
#boundary       p p f

""")
        fileobj.write('read_data ' + self.prefix + '_atoms\n')
        fileobj.write('include  ' + self.prefix + '_opls\n')
        fileobj.write("""
kspace_style    pppm 1e-5
#kspace_modify  slab 3.0

neighbor        1.0 bin
neigh_modify    delay 0 every 1 check yes

thermo          1000
thermo_style    custom step temp press cpu pxx pyy pzz pxy pxz pyz ke pe etotal vol lx ly lz atoms

dump            1 all xyz 1000 dump_relax.xyz
dump_modify     1 sort id

restart         100000 test_relax

min_style       fire
minimize        1.0e-14 1.0e-5 100000 100000
""")
        fileobj.close()

    def write_lammps_atoms(self, atoms, connectivities):
        """Write atoms input for LAMMPS"""
        
        fname = self.prefix + '_atoms'
        fileobj = open(fname, 'w')

        # header
        fileobj.write(fileobj.name + ' (by ' + str(self.__class__) + ')\n\n')
        fileobj.write(str(len(atoms)) + ' atoms\n')
        fileobj.write(str(len(atoms.types)) + ' atom types\n')
        blist = connectivities['bonds']
        if len(blist):
            btypes = connectivities['bond types']
            fileobj.write(str(len(blist)) + ' bonds\n')
            fileobj.write(str(len(btypes)) + ' bond types\n')
        alist = connectivities['angles']
        if len(alist):
            atypes = connectivities['angle types']
            fileobj.write(str(len(alist)) + ' angles\n')
            fileobj.write(str(len(atypes)) + ' angle types\n')
        dlist = connectivities['dihedrals']
        if len(dlist):
            dtypes = connectivities['dihedral types']
            fileobj.write(str(len(dlist)) + ' dihedrals\n')
            fileobj.write(str(len(dtypes)) + ' dihedral types\n')

        # cell
        p = prism(atoms.get_cell())
        xhi, yhi, zhi, xy, xz, yz = p.get_lammps_prism_str()
        fileobj.write('\n0.0 %s  xlo xhi\n' % xhi)
        fileobj.write('0.0 %s  ylo yhi\n' % yhi)
        fileobj.write('0.0 %s  zlo zhi\n' % zhi)
        
        # atoms
        fileobj.write('\nAtoms\n\n')
        tag = atoms.get_tags()
        if atoms.has('molid'):
            molid = atoms.get_array('molid')
        else:
            molid = [1] * len(atoms)
        for i, r in enumerate(map(p.pos_to_lammps_str,
                                  atoms.get_positions())):
            q = self.data['one'][atoms.types[tag[i]]][2]
            fileobj.write('%6d %3d %3d %s %s %s %s' % ((i + 1, molid[i],
                                                        tag[i] + 1,
                                                        q)
                                                       + tuple(r)))
            fileobj.write(' # ' + atoms.types[tag[i]] + '\n')

        # velocities
        velocities = atoms.get_velocities()
        if velocities is not None:
            fileobj.write('\nVelocities\n\n')
            for i, v in enumerate(velocities):
                fileobj.write('%6d %g %g %g\n' %
                              (i + 1, v[0], v[1], v[2]))

        # masses
        fileobj.write('\nMasses\n\n')
        for i, typ in enumerate(atoms.types):
            cs = atoms.split_symbol(typ)[0]
            fileobj.write('%6d %g # %s -> %s\n' %
                          (i + 1,
                           atomic_masses[chemical_symbols.index(cs)],
                           typ, cs))
  
        # bonds
        if len(blist):
            fileobj.write('\nBonds\n\n')
            for ib, bvals in enumerate(blist):
                fileobj.write('%8d %6d %6d %6d ' %
                              (ib + 1, bvals[0] + 1, bvals[1] + 1,
                               bvals[2] + 1))
                try:
                    fileobj.write('# ' + btypes[bvals[0]])
                except:
                    pass
                fileobj.write('\n')

        # angles
        if len(alist):
            fileobj.write('\nAngles\n\n')
            for ia, avals in enumerate(alist):
                fileobj.write('%8d %6d %6d %6d %6d ' %
                              (ia + 1, avals[0] + 1,
                               avals[1] + 1, avals[2] + 1, avals[3] + 1))
                try:
                    fileobj.write('# ' + atypes[avals[0]])
                except:
                    pass
                fileobj.write('\n')

        # dihedrals
        if len(dlist):
            fileobj.write('\nDihedrals\n\n')
            for i, dvals in enumerate(dlist):
                fileobj.write('%8d %6d %6d %6d %6d %6d ' %
                              (i + 1, dvals[0] + 1,
                               dvals[1] + 1, dvals[2] + 1,
                               dvals[3] + 1, dvals[4] + 1))
                try:
                    fileobj.write('# ' + dtypes[dvals[0]])
                except:
                    pass
                fileobj.write('\n')

    def update_neighbor_list(self, atoms):
        cut = 0.5 * max(self.data['cutoffs'].values())
        self.nl = NeighborList([cut] * len(atoms), skin=0,
                               bothways=True, self_interaction=False)
        self.nl.update(atoms)
        self.atoms = atoms
    
    def get_bonds(self, atoms):
        """Find bonds and return them and their types"""
        cutoffs = CutoffList(self.data['cutoffs'])
        self.update_neighbor_list(atoms)

        types = atoms.get_types()
        tags = atoms.get_tags()
        cell = atoms.get_cell()
        bond_list = []
        bond_types = []
        for i, atom in enumerate(atoms):
            iname = types[tags[i]]
            indices, offsets = self.nl.get_neighbors(i)
            for j, offset in zip(indices, offsets):
                if j <= i:
                    continue  # do not double count
                jname = types[tags[j]]
                cut = cutoffs.value(iname, jname)
                if cut is None:
                    if self.warnings > 1:
                        print('Warning: cutoff %s-%s not found'
                              % (iname, jname))
                    continue  # don't have it
                dist = np.linalg.norm(atom.position - atoms[j].position
                                      - np.dot(offset, cell))
                if dist > cut:
                    continue  # too far away
                name, val = self.bonds.name_value(iname, jname)
                if name is None:
                    if self.warnings:
                        print('Warning: potential %s-%s not found'
                              % (iname, jname))
                    continue  # don't have it
                if name not in bond_types:
                    bond_types.append(name)
                bond_list.append([bond_types.index(name), i, j])
        return bond_types, bond_list
                
    def get_angles(self, atoms=None):
        cutoffs = CutoffList(self.data['cutoffs'])
        if atoms is not None:
            self.update_neighbor_list(atoms)
        else:
            atoms = self.atoms
         
        types = atoms.get_types()
        tags = atoms.get_tags()
        cell = atoms.get_cell()
        ang_list = []
        ang_types = []

        # center atom *-i-*
        for i, atom in enumerate(atoms):
            iname = types[tags[i]]
            indicesi, offsetsi = self.nl.get_neighbors(i)

            # search for first neighbor j-i-*
            for j, offsetj in zip(indicesi, offsetsi):
                jname = types[tags[j]]
                cut = cutoffs.value(iname, jname)
                if cut is None:
                    continue  # don't have it
                dist = np.linalg.norm(atom.position - atoms[j].position
                                      - np.dot(offsetj, cell))
                if dist > cut:
                    continue  # too far away

                # search for second neighbor j-i-k
                for k, offsetk in zip(indicesi, offsetsi):
                    if k <= j:
                        continue  # avoid double count
                    kname = types[tags[k]]
                    cut = cutoffs.value(iname, kname)
                    if cut is None:
                        continue  # don't have it
                    dist = np.linalg.norm(atom.position -
                                          np.dot(offsetk, cell) -
                                          atoms[k].position)
                    if dist > cut:
                        continue  # too far away
                    name, val = self.angles.name_value(jname, iname,
                                                       kname)
                    if name is None:
                        if self.warnings > 1:
                            print('Warning: angles %s-%s-%s not found'
                                  % (jname, iname, kname))
                        continue  # don't have it
                    if name not in ang_types:
                        ang_types.append(name)
                    ang_list.append([ang_types.index(name), j, i, k])

        return ang_types, ang_list

    def get_dihedrals(self, ang_types, ang_list):
        'Dihedrals derived from angles.'

        cutoffs = CutoffList(self.data['cutoffs'])

        atoms = self.atoms
        types = atoms.get_types()
        tags = atoms.get_tags()
        cell = atoms.get_cell()

        dih_list = []
        dih_types = []

        def append(name, i, j, k, l):
            if name not in dih_types:
                dih_types.append(name)
            index = dih_types.index(name)
            if (([index, i, j, k, l] not in dih_list) and
                ([index, l, k, j, i] not in dih_list)):
                dih_list.append([index, i, j, k, l])

        for angle in ang_types:
            l, i, j, k = angle
            iname = types[tags[i]]
            jname = types[tags[j]]
            kname = types[tags[k]]

            # search for l-i-j-k
            indicesi, offsetsi = self.nl.get_neighbors(i)
            for l, offsetl in zip(indicesi, offsetsi):
                if l == j:
                    continue # avoid double count
                lname = types[tags[l]]
                cut = cutoffs.value(iname, lname)
                if cut is None:
                    continue # don't have it
                dist = np.linalg.norm(atoms[i].position - atoms[l].position
                                      - np.dot(offsetl, cell))
                if dist > cut:
                    continue # too far away
                name, val = self.dihedrals.name_value(lname, iname,
                                                      jname, kname)
                if name is None:
                    continue # don't have it
                append(name, l, i, j, k)
              
            # search for i-j-k-l
            indicesk, offsetsk = self.nl.get_neighbors(k)
            for l, offsetl in zip(indicesk, offsetsk):
                if l == j:
                    continue # avoid double count
                lname = types[tags[l]]
                cut = cutoffs.value(kname, lname)
                if cut is None:
                    continue # don't have it
                dist = np.linalg.norm(atoms[k].position - atoms[l].position
                                      - np.dot(offsetl, cell))
                if dist > cut:
                    continue # too far away
                name, val = self.dihedrals.name_value(iname, jname,
                                                      kname, lname)
                if name is None:
                    continue # don't have it
                append(name, i, j, k, l)

        return dih_types, dih_list

    def write_lammps_definitions(self, atoms, btypes, atypes, dtypes):
        """Write force field definitions for LAMMPS."""

        fileobj = self.prefix + '_opls'
        if isinstance(fileobj, str):
            fileobj = open(fileobj, 'w')

        fileobj.write('# OPLS potential\n')
        fileobj.write('# write_lammps' +
                      str(time.asctime(
                    time.localtime(time.time()))))

        # bonds
        if len(btypes):
            fileobj.write('\n# bonds\n')
            fileobj.write('bond_style      harmonic\n')
            for ib, btype in enumerate(btypes):
                fileobj.write('bond_coeff %6d' % (ib + 1))
                for value in self.bonds.nvh[btype]:
                    fileobj.write(' ' + str(value))
                fileobj.write(' # ' + btype + '\n')

        # angles
        if len(atypes):
            fileobj.write('\n# angles\n')
            fileobj.write('angle_style      harmonic\n')
            for ia, atype in enumerate(atypes):
                fileobj.write('angle_coeff %6d' % (ia + 1))
                for value in self.angles.nvh[atype]:
                    fileobj.write(' ' + str(value))
                fileobj.write(' # ' + atype + '\n')

        # dihedrals
        if len(dtypes):
            fileobj.write('\n# dihedrals\n')
            fileobj.write('dihedral_style      opls\n')
            for i, dtype in enumerate(dtypes):
                fileobj.write('dihedral_coeff %6d' % (i + 1))
                for value in self.dihedrals.nvh[dtype]:
                    fileobj.write(' ' + str(value))
                fileobj.write(' # ' + dtype + '\n')

        # Lennard Jones settings
        fileobj.write('\n# L-J parameters\n')
        fileobj.write('pair_style lj/cut/coul/long 10.0 7.4' +
                      ' # consider changing these parameters\n')
        fileobj.write('special_bonds lj/coul 0.0 0.0 0.5\n')
        data = self.data['one']
        for ia, atype in enumerate(atoms.types):
            if len(atype) < 2:
                atype = atype + ' '
            fileobj.write('pair_coeff ' + str(ia + 1) + ' ' + str(ia + 1))
            for value in data[atype][:2]:
                fileobj.write(' ' + str(value))
            fileobj.write(' # ' + atype + '\n')
        fileobj.write('pair_modify shift yes mix geometric\n')

        # Charges
        fileobj.write('\n# charges\n')
        for ia, atype in enumerate(atoms.types):
            if len(atype) < 2:
                atype = atype + ' '
            fileobj.write('set type ' + str(ia + 1))
            fileobj.write(' charge ' + str(data[atype][2]))
            fileobj.write(' # ' + atype + '\n')
Example #45
0
class OPLSff:
    def __init__(self, fileobj=None, warnings=0):
        self.warnings = warnings
        self.data = {}
        if fileobj is not None:
            self.read(fileobj)

    def read(self, fileobj, comments='#'):
        if isinstance(fileobj, str):
            fileobj = open(fileobj)

        def read_block(name, 
                       symlen, # length of the symbol
                       nvalues # of values expected
                       ):
            if name not in self.data:
                self.data[name] = {}
            data = self.data[name]

            def add_line():
                line = fileobj.readline()
                if len(line) <= 1: # end of the block
                    return False
                line = line.split('#')[0] # get rid of comments
                if len(line) > symlen:
                    symbol = line[:symlen]
                    words = line[symlen:].split()
                    if len(words) >=  nvalues:
                        if nvalues == 1:
                            data[symbol] = float(words[0])
                        else:
                            data[symbol] = [float(word) 
                                            for word in words[:nvalues]]
                return True

            while add_line():
                pass
 
        read_block('one',      2, 3)
        read_block('bonds',      5, 2)
        read_block('angles',     8, 2)
        read_block('dihedrals', 11, 3)

        read_block('cutoffs',      5, 1)

        self.bonds = BondData(self.data['bonds'])
        self.angles = AnglesData(self.data['angles'])
        self.cutoffs = CutoffList(self.data['cutoffs'])

    def write_lammps(self, atoms):
        btypes, atypes = self.write_lammps_atoms(atoms)
        self.write_lammps_definitions(atoms, btypes, atypes)

    def write_lammps_atoms(self, atoms):
        """Write atoms infor for LAMMPS"""
        
        fileobj = 'lammps_atoms'
        if isinstance(fileobj, str):
            fileobj = open(fileobj, 'w')
        write_lammps_data(fileobj, atoms, 
                          specorder=atoms.types,
                          speclist=(atoms.get_tags() + 1),
                          )

        # masses
        fileobj.write('\nMasses\n\n')
        for i, typ in enumerate(atoms.types):
            cs = atoms.split_symbol(typ)[0]
            fileobj.write('%6d %g # %s -> %s\n' % 
                          (i + 1, 
                           atomic_masses[chemical_symbols.index(cs)],
                           typ, cs))
  
        # bonds
        btypes, blist = self.get_bonds(atoms)
        fileobj.write('\n' + str(len(btypes)) + ' bond types\n')
        fileobj.write(str(len(blist)) + ' bonds\n')
        fileobj.write('\nBonds\n\n')
        
        for ib, bvals in enumerate(blist):
            fileobj.write('%8d %6d %6d %6d\n' %
                          (ib + 1, bvals[0] + 1, bvals[1] + 1, bvals[2] + 1))

        # angles
        atypes, alist = self.get_angles()
        fileobj.write('\n' + str(len(atypes)) + ' angle types\n')
        fileobj.write(str(len(alist)) + ' angles\n')
        fileobj.write('\nAngles\n\n')
        
        for ia, avals in enumerate(alist):
            fileobj.write('%8d %6d %6d %6d %6d\n' %
                          (ia + 1, avals[0] + 1, 
                           avals[1] + 1, avals[2] + 1, avals[3] + 1))

        return btypes, atypes

    def update_neighbor_list(self, atoms):
        cut = 0.5 * max(self.data['cutoffs'].values())
        self.nl = NeighborList([cut] * len(atoms), skin=0, bothways=True)
        self.nl.update(atoms)
        self.atoms = atoms
    
    def get_bonds(self, atoms):
        """Find bonds and return them and their types"""
        cutoffs = CutoffList(self.data['cutoffs'])
        self.update_neighbor_list(atoms)

        types = atoms.get_types()
        tags = atoms.get_tags()
        cell = atoms.get_cell()
        positions = atoms.get_positions()
        bond_list = []
        bond_types = []
        for i, atom in enumerate(atoms):
            iname = types[tags[i]]
            indices, offsets = self.nl.get_neighbors(i)
            for j, offset in zip(indices, offsets):
                if j <= i:
                    continue # do not double count
                jname = types[tags[j]]
                cut = cutoffs.value(iname, jname)
                if cut is None:
                    if self.warnings > 1:
                        print ('Warning: cutoff %s-%s not found'
                               % (iname, jname))
                    continue # don't have it
                dist = np.linalg.norm(atom.position - atoms[j].position
                                      - np.dot(offset, cell))
                if dist > cut:
                    continue # too far away
                name, val = self.bonds.name_value(iname, jname)
                if name is None:
                    if self.warnings:
                        print ('Warning: potential %s-%s not found'
                               % (iname, jname))
                    continue # don't have it
                if name not in bond_types:
                    bond_types.append(name)
                bond_list.append([bond_types.index(name), i, j])
        return bond_types, bond_list
                
    def get_angles(self, atoms=None):
        cutoffs = CutoffList(self.data['cutoffs'])
        if atoms is not None:
            self.update_neighbor_list(atoms)
        else:
            atoms = self.atoms
         
        types = atoms.get_types()
        tags = atoms.get_tags()
        cell = atoms.get_cell()
        positions = atoms.get_positions()
        ang_list = []
        ang_types = []
        for i, atom in enumerate(atoms):
            iname = types[tags[i]]
            indicesi, offsetsi = self.nl.get_neighbors(i)
            for j, offsetj in zip(indicesi, offsetsi):
                jname = types[tags[j]]
                cut = cutoffs.value(iname, jname)
                if cut is None:
                    continue # don't have it
                dist = np.linalg.norm(atom.position - atoms[j].position
                                      - np.dot(offsetj, cell))
                if dist > cut:
                    continue # too far away
                indicesj, offsetsj = self.nl.get_neighbors(j)
                for k, offsetk in zip(indicesj, offsetsj):
                    if k <= i:
                        continue # avoid double count
                    kname = types[tags[k]]
                    cut = cutoffs.value(jname, kname)
                    if cut is None:
                        continue # don't have it
                    dist = np.linalg.norm(atoms[k].position +
                                          np.dot(offsetk, cell) - 
                                          atoms[j].position)
                    if dist > cut:
                        continue # too far away
                    name, val = self.angles.name_value(iname, jname, 
                                                       kname)
                    if name is None:
                        continue # don't have it
                    if name not in ang_types:
                        ang_types.append(name)
                    ang_list.append([ang_types.index(name), i, j, k])
        return ang_types, ang_list

    def write_lammps_definitions(self, atoms, btypes, atypes):
        """Write force field definitions for LAMMPS."""

        fileobj = 'lammps_opls'
        if isinstance(fileobj, str):
            fileobj = open(fileobj, 'w')

        print >> fileobj, '# OPLS potential'
        print >> fileobj, '# write_lammps', time.asctime(
            time.localtime(time.time()))

        # bonds
        fileobj.write('\n# bonds\n')
        fileobj.write('bond_style      harmonic\n')
        for ib, btype in enumerate(btypes):
            fileobj.write('bond_coeff %6d' % (ib + 1))
            for value in self.bonds.nvh[btype]:
                fileobj.write(' ' + str(value))
            fileobj.write(' # ' + btype + '\n')

        # angles
        fileobj.write('\n# angles\n')
        fileobj.write('angle_style      harmonic\n')
        for ia, atype in enumerate(atypes):
            fileobj.write('angle_coeff %6d' % (ia + 1))
            for value in self.angles.nvh[atype]:
                fileobj.write(' ' + str(value))
            fileobj.write(' # ' + atype + '\n')

        # Lennard Jones settings
        fileobj.write('\n# L-J parameters\n')
        fileobj.write('pair_style lj/cut/coul/long 10.0 7.4' +
                      ' # consider changing these parameters\n')
        fileobj.write('special_bonds lj/coul 0.0 0.0 0.5\n')
        data = self.data['one']
        for ia, atype in enumerate(atoms.types):
            if len(atype) < 2:
                atype = atype + ' '
            fileobj.write('pair_coeff ' + str(ia + 1) + ' ' + str(ia + 1))
            for value in data[atype][:2]:
                fileobj.write(' ' + str(value))
            fileobj.write(' # ' + atype + '\n')
        fileobj.write('pair_modify shift yes mix geometric\n')

        # Charges
        fileobj.write('\n# charges\n')
        for ia, atype in enumerate(atoms.types):
            if len(atype) < 2:
                atype = atype + ' '
            fileobj.write('set type ' + str(ia + 1))
            fileobj.write(' ' + str(data[atype][2]))
            fileobj.write(' # ' + atype + '\n')
Example #46
0
def lattice_alteration_nn(indiv, Optimizer):
    """Move function to perform random move along random axis for nearest neighbor distance to random atoms
    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,swaps,stro = find_defects(indiv[0],Optimizer.solidbulk,0)
            ind = indc.copy()
            ind.extend(indb)
        else:
            ind=indiv[0].copy()
            indc=indiv[0].copy()
    else:
        ind=indiv[0].copy()
        indc=indiv[0].copy()
    if len(indc) != 0:
        ctoff1 = [1.0 for one in ind]
        nl = NeighborList(ctoff1, bothways=True, self_interaction=False)
        nl.update(ind)
        try:
            natomsmove=random.randint(1,len(indc)/2)
        except ValueError:
            natomsmove=1
        passn=0
        for count in range(natomsmove):
            try:
                indexmv = random.choice([i for i in range(len(indc))])
                indices, offsets = nl.get_neighbors(indexmv)
                nns = Atoms()
                nns.append(ind[indexmv])
                for index, d in zip(indices,offsets):
                    index = int(index)
                    pos = ind[index].position + numpy.dot(d,ind.get_cell())
                    nns.append(Atom(symbol=ind[index].symbol, position=pos))
                dist = [nns.get_distance(0,i) for i in range(1, len(nns))]
                r = sum(dist)/len(dist)
                dir = random.choice([[1,0,0],[-1,0,0],[0,1,0],[0,-1,0],[0,0,1],[0,0,-1]])
                ind[indexmv].position += [i*r for i in dir]
            except:
                passn+=1
        indiv[0]=ind.copy()
    else:
        natomsmove=0
        passn=0
    Optimizer.output.write('Lattice Alteration NN Mutation performed on individual\n')
    Optimizer.output.write('Index = '+repr(indiv.index)+'\n')
    natomsmove-=passn
    Optimizer.output.write('Number of atoms moved = '+repr(natomsmove)+'\n')
    Optimizer.output.write(repr(indiv[0])+'\n')
    muttype='LANN'+repr(natomsmove)
    if indiv.energy==0:
        indiv.history_index=indiv.history_index+'m'+muttype
    else:
        indiv.history_index=repr(indiv.index)+'m'+muttype
    return indiv
def newoverlap(wfs, spos_ac):
    assert wfs.ksl.block_comm.size == wfs.gd.comm.size * wfs.bd.comm.size
    even_part = EvenPartitioning(wfs.gd.comm, #wfs.ksl.block_comm,
                                 len(wfs.atom_partition.rank_a))
    atom_partition = even_part.as_atom_partition()
    
    tci = wfs.tci

    gd = wfs.gd
    kd = wfs.kd
    nq = len(kd.ibzk_qc)

    # New neighbor list because we want it "both ways", heh.  Or do we?
    neighbors = NeighborList(tci.cutoff_a, skin=0,
                             sorted=True, self_interaction=True, bothways=False)
    atoms = Atoms('X%d' % len(tci.cutoff_a), cell=gd.cell_cv, pbc=gd.pbc_c)
    atoms.set_scaled_positions(spos_ac)
    neighbors.update(atoms)
    
    # XXX
    pcutoff_a = []
    phicutoff_a = []
    for setup in wfs.setups:
        if setup.pt_j:
            pcutoff = max([pt.get_cutoff() for pt in setup.pt_j])
        else:
            pcutoff = 0.0
        if setup.phit_j:
            phicutoff = max([phit.get_cutoff() for phit in setup.phit_j])
        else:
            phicutoff = 0.0
        pcutoff_a.append(pcutoff)
        phicutoff_a.append(phicutoff)

    # Calculate the projector--basis function overlaps:
    #
    #    a1        ~a1
    #   P      = < p   | Phi   > ,
    #    i mu       i       mu
    #
    # i.e. projector is on a1 and basis function is on what we will call a2.

    overlapcalc = TwoCenterIntegralCalculator(wfs.kd.ibzk_qc,
                                              derivative=False)
    
    P_aaqim = {} # keys: (a1, a2).  Values: matrix blocks
    dists_and_offsets = DistsAndOffsets(neighbors, spos_ac, gd.cell_cv)

    #ng = 2**extra_parameters.get('log2ng', 10)
    #transformer = FourierTransformer(rcmax, ng)
    #tsoc = TwoSiteOverlapCalculator(transformer)
    #msoc = ManySiteOverlapCalculator(tsoc, I_a, I_a)

    msoc = wfs.tci.msoc

    phit_Ij = [setup.phit_j for setup in tci.setups_I]
    l_Ij = []
    for phit_j in phit_Ij:
        l_Ij.append([phit.get_angular_momentum_number()
                     for phit in phit_j])

    pt_l_Ij = [setup.l_j for setup in tci.setups_I]        
    pt_Ij = [setup.pt_j for setup in tci.setups_I]
    phit_Ijq = msoc.transform(phit_Ij)
    pt_Ijq = msoc.transform(pt_Ij)

    #self.Theta_expansions = msoc.calculate_expansions(l_Ij, phit_Ijq,
    #                                                  l_Ij, phit_Ijq)
    #self.T_expansions = msoc.calculate_kinetic_expansions(l_Ij, phit_Ijq)
    P_expansions = msoc.calculate_expansions(pt_l_Ij, pt_Ijq,
                                             l_Ij, phit_Ijq)
    P_neighbors_a = {}
    
    for a1 in atom_partition.my_indices:
        for a2 in range(len(wfs.setups)):
            R_ca_and_offset_a = dists_and_offsets.get(a1, a2)
            if R_ca_and_offset_a is None: # No overlap between a1 and a2
                continue

            maxdistance = pcutoff_a[a1] + phicutoff_a[a2]
            expansion = P_expansions.get(a1, a2)
            P_qim = expansion.zeros((nq,), dtype=wfs.dtype)
            disp = None
            for R_c, offset in R_ca_and_offset_a:
                r = np.linalg.norm(R_c)
                if r > maxdistance:
                    continue
                
                # Below lines are meant to make use of symmetry.  Will not
                # be relevant for P.
                #remainder = (a1 + a2) % 2
                #if a1 < a2 and not remainder:
                #    continue
                # if a1 > a2 and remainder:
                #    continue
                
                phases = overlapcalc.phaseclass(overlapcalc.ibzk_qc, offset)
                disp = AtomicDisplacement(None, a1, a2, R_c, offset, phases)
                disp.evaluate_overlap(expansion, P_qim)
            
            if disp is not None: # there was at least one non-zero overlap
                assert (a1, a2) not in P_aaqim
                P_aaqim[(a1, a2)] = P_qim
                P_neighbors_a.setdefault(a1, []).append(a2)
    
    Pkeys = P_aaqim.keys()
    Pkeys.sort()

    def get_M1M2(a):
        M1 = wfs.setups.M_a[a]
        M2 = M1 + wfs.setups[a].nao
        return M1, M2
    
    oldstyle_P_aqMi = None
    if 0:#wfs.world.size == 1:
        oldstyle_P_aqMi = {}
        for a, setup in enumerate(wfs.setups):
            oldstyle_P_aqMi[a] = np.zeros((nq, wfs.setups.nao, setup.ni),
                                          dtype=wfs.dtype)
        print([(s.ni, s.nao) for s in wfs.setups])
        for a1, a2 in Pkeys:
            M1, M2 = get_M1M2(a2)
            Pconj_qmi = P_aaqim[(a1, a2)].transpose(0, 2, 1).conjugate()
            oldstyle_P_aqMi[a1][:, M1:M2, :] = Pconj_qmi
        
    # XXX mind distribution

    return P_neighbors_a, P_aaqim, oldstyle_P_aqMi
Example #48
0
class EAM(Calculator):
    r"""

    EAM Interface Documentation

Introduction
============

The Embedded Atom Method (EAM) [1]_ is a classical potential which is
good for modelling metals, particularly fcc materials. Because it is
an equiaxial potential the EAM does not model directional bonds
well. However, the Angular Dependent Potential (ADP) [2]_ which is an
extended version of EAM is able to model directional bonds and is also
included in the EAM calculator.

Generally all that is required to use this calculator is to supply a
potential file or as a set of functions that describe the potential.
The files containing the potentials for this calculator are not
included but many suitable potentials can be downloaded from The
Interatomic Potentials Repository Project at
http://www.ctcms.nist.gov/potentials/

Theory
======

A single element EAM potential is defined by three functions: the
embedded energy, electron density and the pair potential.  A two
element alloy contains the individual three functions for each element
plus cross pair interactions.  The ADP potential has two additional
sets of data to define the dipole and quadrupole directional terms for
each alloy and their cross interactions.

The total energy `E_{\rm tot}` of an arbitrary arrangement of atoms is
given by the EAM potential as

.. math::
   E_{\rm tot} = \sum_i F(\bar\rho_i) + \frac{1}{2}\sum_{i\ne j} \phi(r_{ij})

and

.. math::
   \bar\rho_i = \sum_j \rho(r_{ij})

where `F` is an embedding function, namely the energy to embed an atom `i` in
the combined electron density `\bar\rho_i` which is contributed from
each of its neighbouring atoms `j` by an amount `\rho(r_{ij})`,
`\phi(r_{ij})` is the pair potential function representing the energy
in bond `ij` which is due to the short-range electro-static
interaction between atoms, and `r_{ij}` is the distance between an
atom and its neighbour for that bond.

The ADP potential is defined as

.. math::
   E_{\rm tot} = \sum_i F(\bar\rho_i) + \frac{1}{2}\sum_{i\ne j} \phi(r_{ij})
   + {1\over 2} \sum_{i,\alpha} (\mu_i^\alpha)^2
   + {1\over 2} \sum_{i,\alpha,\beta} (\lambda_i^{\alpha\beta})^2
   - {1 \over 6} \sum_i \nu_i^2

where `\mu_i^\alpha` is the dipole vector, `\lambda_i^{\alpha\beta}`
is the quadrupole tensor and `\nu_i` is the trace of
`\lambda_i^{\alpha\beta}`.

Running the Calculator
======================

EAM calculates the cohesive atom energy and forces. Internally the
potential functions are defined by splines which may be directly
supplied or created by reading the spline points from a data file from
which a spline function is created.  The LAMMPS compatible ``.alloy``
and ``.adp`` formats are supported. The LAMMPS ``.eam`` format is
slightly different from the ``.alloy`` format and is currently not
supported.

For example::

    from ase.calculators.eam import EAM

    mishin = EAM(potential='Al99.eam.alloy')
    mishin.write_potential('new.eam.alloy')
    mishin.plot()

    slab.set_calculator(mishin)
    slab.get_potential_energy()
    slab.get_forces()

The breakdown of energy contribution from the indvidual components are
stored in the calculator instance ``.results['energy_components']``

Arguments
=========

=========================  ====================================================
Keyword                    Description
=========================  ====================================================
``potential``              file of potential in ``.alloy`` or ``.adp`` format
                           (This is generally all you need to supply)

``elements[N]``            array of N element abbreviations

``embedded_energy[N]``     arrays of embedded energy functions

``electron_density[N]``    arrays of electron density functions

``phi[N,N]``               arrays of pair potential functions

``d_embedded_energy[N]``   arrays of derivative embedded energy functions

``d_electron_density[N]``  arrays of derivative electron density functions

``d_phi[N,N]``             arrays of derivative pair potentials functions

``d[N,N], q[N,N]``         ADP dipole and quadrupole function

``d_d[N,N], d_q[N,N]``     ADP dipole and quadrupole derivative functions

``skin``                   skin distance passed to NeighborList(). If no atom
                           has moved more than the skin-distance since the last
                           call to the ``update()`` method then the neighbor
                           list can be reused. Defaults to 1.0.

``form``                   the form of the potential ``alloy`` or ``adp``. This
                           will be determined from the file suffix or must be
                           set if using equations

=========================  ====================================================


Additional parameters for writing potential files
=================================================

The following parameters are only required for writing a potential in
``.alloy`` or ``.adp`` format file.

=========================  ====================================================
Keyword                    Description
=========================  ====================================================
``header``                 Three line text header. Default is standard message.

``Z[N]``                   Array of atomic number of each element

``mass[N]``                Atomic mass of each element

``a[N]``                   Array of lattice parameters for each element

``lattice[N]``             Lattice type

``nrho``                   No. of rho samples along embedded energy curve

``drho``                   Increment for sampling density

``nr``                     No. of radial points along density and pair
                           potential curves

``dr``                     Increment for sampling radius

=========================  ====================================================

Special features
================

``.plot()``
  Plots the individual functions. This may be called from multiple EAM
  potentials to compare the shape of the individual curves. This
  function requires the installation of the Matplotlib libraries.

Notes/Issues
=============

* Although currently not fast, this calculator can be good for trying
  small calculations or for creating new potentials by matching baseline
  data such as from DFT results. The format for these potentials is
  compatible with LAMMPS_ and so can be used either directly by LAMMPS or
  with the ASE LAMMPS calculator interface.

* Supported formats are the LAMMPS_ ``.alloy`` and ``.adp``. The
  ``.eam`` format is currently not supported. The form of the
  potential will be determined from the file suffix.

* Any supplied values will override values read from the file.

* The derivative functions, if supplied, are only used to calculate
  forces.

* There is a bug in early versions of scipy that will cause eam.py to
  crash when trying to evaluate splines of a potential with one
  neighbor such as caused by evaluating a dimer.

.. _LAMMPS: http://lammps.sandia.gov/

.. [1] M.S. Daw and M.I. Baskes, Phys. Rev. Letters 50 (1983)
       1285.

.. [2] Y. Mishin, M.J. Mehl, and D.A. Papaconstantopoulos,
       Acta Materialia 53 2005 4029--4041.


End EAM Interface Documentation
    """

    implemented_properties = ["energy", "forces"]

    default_parameters = dict(
        skin=1.0, potential=None, header="""EAM/ADP potential file\nGenerated from eam.py\nblank\n"""
    )

    def __init__(self, restart=None, ignore_bad_restart_file=False, label=os.curdir, atoms=None, **kwargs):

        if "potential" in kwargs:
            self.read_potential(kwargs["potential"])

        Calculator.__init__(self, restart, ignore_bad_restart_file, label, atoms, **kwargs)

        valid_args = (
            "potential",
            "elements",
            "header",
            "drho",
            "dr",
            "cutoff",
            "atomic_number",
            "mass",
            "a",
            "lattice",
            "embedded_energy",
            "electron_density",
            "phi",
            # derivatives
            "d_embedded_energy",
            "d_electron_density",
            "d_phi",
            "d",
            "q",
            "d_d",
            "d_q",  # adp terms
            "skin",
            "form",
            "Z",
            "nr",
            "nrho",
            "mass",
        )

        # set any additional keyword arguments
        for arg, val in self.parameters.iteritems():
            if arg in valid_args:
                setattr(self, arg, val)
            else:
                raise RuntimeError('unknown keyword arg "%s" : not in %s' % (arg, valid_args))

    def set_form(self, fileobj):
        """set the form variable based on the file name suffix"""
        extension = os.path.splitext(fileobj)[1]

        if extension == ".eam":
            self.form = "eam"
            raise NotImplementedError
        elif extension == ".alloy":
            self.form = "alloy"
        elif extension == ".adp":
            self.form = "adp"
        else:
            raise RuntimeError("unknown file extension type: %s" % extension)

    def read_potential(self, fileobj):
        """Reads a LAMMPS EAM file in alloy or adp format
        and creates the interpolation functions from the data
        """

        if isinstance(fileobj, str):
            f = open(fileobj)
            self.set_form(fileobj)
        else:
            f = fileobj

        lines = f.readlines()
        self.header = lines[:3]
        i = 3

        # make the data one long line so as not to care how its formatted
        data = []
        for line in lines[i:]:
            data.extend(line.split())

        self.Nelements = int(data[0])
        d = 1
        self.elements = data[d : (d + self.Nelements)]
        d += self.Nelements

        self.nrho = int(data[d])
        self.drho = float(data[d + 1])
        self.nr = int(data[d + 2])
        self.dr = float(data[d + 3])
        self.cutoff = float(data[d + 4])

        self.embedded_data = np.zeros([self.Nelements, self.nrho])
        self.density_data = np.zeros([self.Nelements, self.nr])
        self.Z = np.zeros([self.Nelements], dtype=int)
        self.mass = np.zeros([self.Nelements])
        self.a = np.zeros([self.Nelements])
        self.lattice = []
        d += 5

        # reads in the part of the eam file for each element
        for elem in range(self.Nelements):
            self.Z[elem] = int(data[d])
            self.mass[elem] = float(data[d + 1])
            self.a[elem] = float(data[d + 2])
            self.lattice.append(data[d + 3])
            d += 4

            self.embedded_data[elem] = np.float_(data[d : (d + self.nrho)])
            d += self.nrho
            self.density_data[elem] = np.float_(data[d : (d + self.nr)])
            d += self.nr

        # reads in the r*phi data for each interaction between elements
        self.rphi_data = np.zeros([self.Nelements, self.Nelements, self.nr])

        for i in range(self.Nelements):
            for j in range(i + 1):
                self.rphi_data[j, i] = np.float_(data[d : (d + self.nr)])
                d += self.nr

        self.r = np.arange(0, self.nr) * self.dr
        self.rho = np.arange(0, self.nrho) * self.drho

        self.set_splines()

        if self.form == "adp":
            self.read_adp_data(data, d)
            self.set_adp_splines()

    def set_splines(self):
        # this section turns the file data into three functions (and
        # derivative functions) that define the potential
        self.embedded_energy = np.empty(self.Nelements, object)
        self.electron_density = np.empty(self.Nelements, object)
        self.d_embedded_energy = np.empty(self.Nelements, object)
        self.d_electron_density = np.empty(self.Nelements, object)

        for i in range(self.Nelements):
            self.embedded_energy[i] = spline(self.rho, self.embedded_data[i], k=3)
            self.electron_density[i] = spline(self.r, self.density_data[i], k=3)
            self.d_embedded_energy[i] = self.deriv(self.embedded_energy[i])
            self.d_electron_density[i] = self.deriv(self.electron_density[i])

        self.phi = np.empty([self.Nelements, self.Nelements], object)
        self.d_phi = np.empty([self.Nelements, self.Nelements], object)

        # ignore the first point of the phi data because it is forced
        # to go through zero due to the r*phi format in alloy and adp
        for i in range(self.Nelements):
            for j in range(i, self.Nelements):
                if self.form == "eam":  # not stored as rphi
                    # should we ignore the first point for eam ?
                    raise RuntimeError(".eam format not yet supported")

                    self.phi[i, j] = spline(self.r[1:], self.rphi_data[i, j][1:], k=3)
                else:
                    self.phi[i, j] = spline(self.r[1:], self.rphi_data[i, j][1:] / self.r[1:], k=3)

                self.d_phi[i, j] = self.deriv(self.phi[i, j])

                if j != i:
                    self.phi[j, i] = self.phi[i, j]
                    self.d_phi[j, i] = self.d_phi[i, j]

    def set_adp_splines(self):
        self.d = np.empty([self.Nelements, self.Nelements], object)
        self.d_d = np.empty([self.Nelements, self.Nelements], object)
        self.q = np.empty([self.Nelements, self.Nelements], object)
        self.d_q = np.empty([self.Nelements, self.Nelements], object)

        for i in range(self.Nelements):
            for j in range(i, self.Nelements):
                self.d[i, j] = spline(self.r[1:], self.d_data[i, j][1:], k=3)
                self.d_d[i, j] = self.deriv(self.d[i, j])
                self.q[i, j] = spline(self.r[1:], self.q_data[i, j][1:], k=3)
                self.d_q[i, j] = self.deriv(self.q[i, j])

                # make symmetrical
                if j != i:
                    self.d[j, i] = self.d[i, j]
                    self.d_d[j, i] = self.d_d[i, j]
                    self.q[j, i] = self.q[i, j]
                    self.d_q[j, i] = self.d_q[i, j]

    def read_adp_data(self, data, d):
        """read in the extra adp data from the potential file"""

        self.d_data = np.zeros([self.Nelements, self.Nelements, self.nr])
        # should be non symetrical combinations of 2
        for i in range(self.Nelements):
            for j in range(i + 1):
                self.d_data[j, i] = data[d : d + self.nr]
                d += self.nr

        self.q_data = np.zeros([self.Nelements, self.Nelements, self.nr])
        # should be non symetrical combinations of 2
        for i in range(self.Nelements):
            for j in range(i + 1):
                self.q_data[j, i] = data[d : d + self.nr]
                d += self.nr

    def write_potential(self, filename, nc=1, numformat="%.8e"):
        """Writes out the potential in the format given by the form
        variable to 'filename' with a data format that is nc columns
        wide.  Note: array lengths need to be an exact multiple of nc
        """

        f = open(filename, "w")

        assert self.nr % nc == 0
        assert self.nrho % nc == 0

        for line in self.header:
            f.write(line)

        f.write("%d " % self.Nelements)
        for i in range(self.Nelements):
            f.write("%s " % str(self.elements[i]))
        f.write("\n")

        f.write("%d %f %d %f %f \n" % (self.nrho, self.drho, self.nr, self.dr, self.cutoff))

        # start of each section for each element
        #        rs = np.linspace(0, self.nr * self.dr, self.nr)
        #        rhos = np.linspace(0, self.nrho * self.drho, self.nrho)

        rs = np.arange(0, self.nr) * self.dr
        rhos = np.arange(0, self.nrho) * self.drho

        for i in range(self.Nelements):
            f.write("%d %f %f %s\n" % (self.Z[i], self.mass[i], self.a[i], str(self.lattice[i])))
            np.savetxt(f, self.embedded_energy[i](rhos).reshape(self.nrho / nc, nc), fmt=nc * [numformat])
            np.savetxt(f, self.electron_density[i](rs).reshape(self.nr / nc, nc), fmt=nc * [numformat])

        # write out the pair potentials in Lammps DYNAMO setfl format
        # as r*phi for alloy format
        for i in range(self.Nelements):
            for j in range(i, self.Nelements):
                np.savetxt(f, (rs * self.phi[i, j](rs)).reshape(self.nr / nc, nc), fmt=nc * [numformat])

        if self.form == "adp":
            # these are the u(r) or dipole values
            for i in range(self.Nelements):
                for j in range(i + 1):
                    np.savetxt(f, self.d_data[i, j])

            # these are the w(r) or quadrupole values
            for i in range(self.Nelements):
                for j in range(i + 1):
                    np.savetxt(f, self.q_data[i, j])

        f.close()

    def update(self, atoms):
        # check all the elements are available in the potential
        self.Nelements = len(self.elements)
        elements = np.unique(atoms.get_chemical_symbols())
        unavailable = np.logical_not(np.array([item in self.elements for item in elements]))

        if np.any(unavailable):
            raise RuntimeError("These elements are not in the potential: %s" % elements[unavailable])

        # cutoffs need to be a vector for NeighborList
        cutoffs = self.cutoff * np.ones(len(atoms))

        # convert the elements to an index of the position
        # in the eam format
        self.index = np.array([self.elements.index(el) for el in atoms.get_chemical_symbols()])
        self.pbc = atoms.get_pbc()

        # since we need the contribution of all neighbors to the
        # local electron density we cannot just calculate and use
        # one way neighbors
        self.neighbors = NeighborList(cutoffs, skin=self.parameters.skin, self_interaction=False, bothways=True)
        self.neighbors.update(atoms)

    def calculate(self, atoms=None, properties=["energy"], system_changes=all_changes):
        """EAM Calculator

        atoms: Atoms object
            Contains positions, unit-cell, ...
        properties: list of str
            List of what needs to be calculated.  Can be any combination
            of 'energy', 'forces'
        system_changes: list of str
            List of what has changed since last calculation.  Can be
            any combination of these five: 'positions', 'numbers', 'cell',
            'pbc', 'initial_charges' and 'initial_magmoms'.
            """

        Calculator.calculate(self, atoms, properties, system_changes)

        # we shouldn't really recalc if charges or magmos change
        if len(system_changes) > 0:  # something wrong with this way
            self.update(self.atoms)
            self.calculate_energy(self.atoms)

            if "forces" in properties:
                self.calculate_forces(self.atoms)

        # check we have all the properties requested
        for property in properties:
            if property not in self.results:
                if property is "energy":
                    self.calculate_energy(self.atoms)

                if property is "forces":
                    self.calculate_forces(self.atoms)

        # we need to remember the previous state of parameters

    #        if 'potential' in parameter_changes and potential != None:
    #                self.read_potential(potential)

    def calculate_energy(self, atoms):
        """Calculate the energy
        the energy is made up of the ionic or pair interaction and
        the embedding energy of each atom into the electron cloud
        generated by its neighbors
        """

        pair_energy = 0.0
        embedding_energy = 0.0
        mu_energy = 0.0
        lam_energy = 0.0
        trace_energy = 0.0

        self.total_density = np.zeros(len(atoms))
        if self.form == "adp":
            self.mu = np.zeros([len(atoms), 3])
            self.lam = np.zeros([len(atoms), 3, 3])

        for i in range(len(atoms)):  # this is the atom to be embedded
            neighbors, offsets = self.neighbors.get_neighbors(i)
            offset = np.dot(offsets, atoms.get_cell())

            rvec = atoms.positions[neighbors] + offset - atoms.positions[i]

            ## calculate the distance to the nearest neighbors
            r = np.sqrt(np.sum(np.square(rvec), axis=1))  # fast
            #            r = np.apply_along_axis(np.linalg.norm, 1, rvec)  # sloow

            nearest = np.arange(len(r))[r <= self.cutoff]
            for j_index in range(self.Nelements):
                use = self.index[neighbors[nearest]] == j_index
                if not use.any():
                    continue
                pair_energy += np.sum(self.phi[self.index[i], j_index](r[nearest][use])) / 2.0

                density = np.sum(self.electron_density[j_index](r[nearest][use]))
                self.total_density[i] += density

                if self.form == "adp":
                    self.mu[i] += self.adp_dipole(r[nearest][use], rvec[nearest][use], self.d[self.index[i], j_index])

                    self.lam[i] += self.adp_quadrupole(
                        r[nearest][use], rvec[nearest][use], self.q[self.index[i], j_index]
                    )

            # add in the electron embedding energy
            embedding_energy += self.embedded_energy[self.index[i]](self.total_density[i])

        components = dict(pair=pair_energy, embedding=embedding_energy)

        if self.form == "adp":
            mu_energy += np.sum(self.mu ** 2) / 2.0
            lam_energy += np.sum(self.lam ** 2) / 2.0

            for i in range(len(atoms)):  # this is the atom to be embedded
                trace_energy -= np.sum(self.lam[i].trace() ** 2) / 6.0

            adp_result = dict(adp_mu=mu_energy, adp_lam=lam_energy, adp_trace=trace_energy)
            components.update(adp_result)

        self.positions = atoms.positions.copy()
        self.cell = atoms.get_cell().copy()

        energy = 0.0
        for i in components.keys():
            energy += components[i]

        self.energy_free = energy
        self.energy_zero = energy

        self.results["energy_components"] = components
        self.results["energy"] = energy

    def calculate_forces(self, atoms):
        # calculate the forces based on derivatives of the three EAM functions

        self.update(atoms)
        self.results["forces"] = np.zeros((len(atoms), 3))

        for i in range(len(atoms)):  # this is the atom to be embedded
            neighbors, offsets = self.neighbors.get_neighbors(i)
            offset = np.dot(offsets, atoms.get_cell())
            # create a vector of relative positions of neighbors
            rvec = atoms.positions[neighbors] + offset - atoms.positions[i]
            r = np.sqrt(np.sum(np.square(rvec), axis=1))
            nearest = np.arange(len(r))[r < self.cutoff]

            d_embedded_energy_i = self.d_embedded_energy[self.index[i]](self.total_density[i])
            urvec = rvec.copy()  # unit directional vector

            for j in np.arange(len(neighbors)):
                urvec[j] = urvec[j] / r[j]

            for j_index in range(self.Nelements):
                use = self.index[neighbors[nearest]] == j_index
                if not use.any():
                    continue
                rnuse = r[nearest][use]
                density_j = self.total_density[neighbors[nearest][use]]
                scale = (
                    self.d_phi[self.index[i], j_index](rnuse)
                    + (d_embedded_energy_i * self.d_electron_density[j_index](rnuse))
                    + (self.d_embedded_energy[j_index](density_j) * self.d_electron_density[self.index[i]](rnuse))
                )

                self.results["forces"][i] += np.dot(scale, urvec[nearest][use])

                if self.form == "adp":
                    adp_forces = self.angular_forces(
                        self.mu[i],
                        self.mu[neighbors[nearest][use]],
                        self.lam[i],
                        self.lam[neighbors[nearest][use]],
                        rnuse,
                        rvec[nearest][use],
                        self.index[i],
                        j_index,
                    )

                    self.results["forces"][i] += adp_forces

    def angular_forces(self, mu_i, mu, lam_i, lam, r, rvec, form1, form2):
        # calculate the extra components for the adp forces
        # rvec are the relative positions to atom i
        psi = np.zeros(mu.shape)
        for gamma in range(3):
            term1 = (mu_i[gamma] - mu[:, gamma]) * self.d[form1][form2](r)

            term2 = np.sum(
                (mu_i - mu)
                * self.d_d[form1][form2](r)[:, np.newaxis]
                * (rvec * rvec[:, gamma][:, np.newaxis] / r[:, np.newaxis]),
                axis=1,
            )

            term3 = 2 * np.sum(
                (lam_i[:, gamma] + lam[:, :, gamma]) * rvec * self.q[form1][form2](r)[:, np.newaxis], axis=1
            )
            term4 = 0.0
            for alpha in range(3):
                for beta in range(3):
                    rs = rvec[:, alpha] * rvec[:, beta] * rvec[:, gamma]
                    term4 += ((lam_i[alpha, beta] + lam[:, alpha, beta]) * self.d_q[form1][form2](r) * rs) / r

            term5 = (
                (lam_i.trace() + lam.trace(axis1=1, axis2=2))
                * (self.d_q[form1][form2](r) * r + 2 * self.q[form1][form2](r))
                * rvec[:, gamma]
            ) / 3.0

            # the minus for term5 is a correction on the adp
            # formulation given in the 2005 Mishin Paper and is posted
            # on the NIST website with the AlH potential
            psi[:, gamma] = term1 + term2 + term3 + term4 - term5

        return np.sum(psi, axis=0)

    def adp_dipole(self, r, rvec, d):
        # calculate the dipole contribution
        mu = np.sum((rvec * d(r)[:, np.newaxis]), axis=0)

        return mu  # sign to agree with lammps

    def adp_quadrupole(self, r, rvec, q):
        # slow way of calculating the quadrupole contribution
        r = np.sqrt(np.sum(rvec ** 2, axis=1))

        lam = np.zeros([rvec.shape[0], 3, 3])
        qr = q(r)
        for alpha in range(3):
            for beta in range(3):
                lam[:, alpha, beta] += qr * rvec[:, alpha] * rvec[:, beta]

        return np.sum(lam, axis=0)

    def deriv(self, spline):
        """Wrapper for extracting the derivative from a spline"""

        def d_spline(aspline):
            return spline(aspline, 1)

        return d_spline

    def plot(self, name=""):
        """Plot the individual curves"""

        try:
            import matplotlib.pyplot as plt

        except ImportError:
            raise NotAvailable("This needs matplotlib module.")

        if self.form == "eam" or self.form == "alloy":
            nrow = 2
        elif self.form == "adp":
            nrow = 3
        else:
            raise RuntimeError("Unknown form of potential: %s" % self.form)

        if hasattr(self, "r"):
            r = self.r
        else:
            r = np.linspace(0, self.cutoff, 50)

        if hasattr(self, "rho"):
            rho = self.rho
        else:
            rho = np.linspace(0, 10.0, 50)

        plt.subplot(nrow, 2, 1)
        self.elem_subplot(rho, self.embedded_energy, r"$\rho$", r"Embedding Energy $F(\bar\rho)$", name, plt)

        plt.subplot(nrow, 2, 2)
        self.elem_subplot(r, self.electron_density, r"$r$", r"Electron Density $\rho(r)$", name, plt)

        plt.subplot(nrow, 2, 3)
        self.multielem_subplot(r, self.phi, r"$r$", r"Pair Potential $\phi(r)$", name, plt)
        plt.ylim(-1.0, 1.0)  # need reasonable values

        if self.form == "adp":
            plt.subplot(nrow, 2, 5)
            self.multielem_subplot(r, self.d, r"$r$", r"Dipole Energy", name, plt)

            plt.subplot(nrow, 2, 6)
            self.multielem_subplot(r, self.q, r"$r$", r"Quadrupole Energy", name, plt)

        plt.plot()

    def elem_subplot(self, curvex, curvey, xlabel, ylabel, name, plt):
        plt.xlabel(xlabel)
        plt.ylabel(ylabel)
        for i in np.arange(self.Nelements):
            label = name + " " + self.elements[i]
            plt.plot(curvex, curvey[i](curvex), label=label)
        plt.legend()

    def multielem_subplot(self, curvex, curvey, xlabel, ylabel, name, plt):
        plt.xlabel(xlabel)
        plt.ylabel(ylabel)
        for i in np.arange(self.Nelements):
            for j in np.arange(i + 1):
                label = name + " " + self.elements[i] + "-" + self.elements[j]
                plt.plot(curvex, curvey[i, j](curvex), label=label)
        plt.legend()
Example #49
0
def check_min_dist(Optimizer, 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':
            if not 'LAMMPS' in Optimizer.modules:
                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:
                rank = MPI.COMM_WORLD.Get_rank()
                #logger = logging.getLogger(Optimizer.loggername)
                R = totalsol.arrays['positions']
                tol = 0.01
                epsilon = 0.05
                fix = 0.5
                
                closelist = numpy.arange(len(totalsol))
                iter = 0
                while len(closelist) > 0 and iter<2:
                    iter+=1
                    closelist = []
                    dist=spatial.distance.cdist(R,R)
                    numpy.fill_diagonal(dist,1.0)
                    smalldist = numpy.where(dist < min_len-tol)
   
                    for ind in range(len(smalldist[0])):
                        i = smalldist[0][ind]
                        j = smalldist[1][ind]
                        if i < j and dist[i][j] < min_len-tol:
                            closelist.append(i)
                            closelist.append(j)
                            if dist[i][j] > epsilon:
                                x = 1.0 - min_len / dist[i][j]
                                D = R[j]-R[i]
                                R[i] += (x * fix) * D
                                R[j] -= (x * (1.0 - fix)) * D
                            else:
                                R[i] += [0.2, 0.0, 0.0]
                                R[j] -= [0.2, 0.0, 0.0]
                            R2P = [R[i],R[j]]
                            dist2P=spatial.distance.cdist(R2P,R)
                            dist[i] = dist2P[0]
                            dist[j] = dist2P[1]
                            for k in range(len(R)):
                                dist[k][i] = dist[i][k]
                                dist[k][j] = dist[j][k]
                    closelist=list(set(closelist))
                    closelist.sort()
                    #if len(closelist) != 0:
                    #    logger.info('M:iter {0}, closelist size {1}'.format(iter,len(closelist)))
        else:
            print 'WARNING: In Check_Min_Dist in EvalEnergy: Structure Type not recognized'
        return totalsol, STR
Example #50
0
def newoverlap(wfs, spos_ac):
    assert wfs.ksl.block_comm.size == wfs.gd.comm.size * wfs.bd.comm.size
    even_part = EvenPartitioning(
        wfs.gd.comm,  #wfs.ksl.block_comm,
        len(wfs.atom_partition.rank_a))
    atom_partition = even_part.as_atom_partition()

    tci = wfs.tci

    gd = wfs.gd
    kd = wfs.kd
    nq = len(kd.ibzk_qc)

    # New neighbor list because we want it "both ways", heh.  Or do we?
    neighbors = NeighborList(tci.cutoff_a,
                             skin=0,
                             sorted=True,
                             self_interaction=True,
                             bothways=False)
    atoms = Atoms('X%d' % len(tci.cutoff_a), cell=gd.cell_cv, pbc=gd.pbc_c)
    atoms.set_scaled_positions(spos_ac)
    neighbors.update(atoms)

    # XXX
    pcutoff_a = []
    phicutoff_a = []
    for setup in wfs.setups:
        if setup.pt_j:
            pcutoff = max([pt.get_cutoff() for pt in setup.pt_j])
        else:
            pcutoff = 0.0
        if setup.phit_j:
            phicutoff = max([phit.get_cutoff() for phit in setup.phit_j])
        else:
            phicutoff = 0.0
        pcutoff_a.append(pcutoff)
        phicutoff_a.append(phicutoff)

    # Calculate the projector--basis function overlaps:
    #
    #    a1        ~a1
    #   P      = < p   | Phi   > ,
    #    i mu       i       mu
    #
    # i.e. projector is on a1 and basis function is on what we will call a2.

    overlapcalc = TwoCenterIntegralCalculator(wfs.kd.ibzk_qc, derivative=False)

    P_aaqim = {}  # keys: (a1, a2).  Values: matrix blocks
    dists_and_offsets = DistsAndOffsets(neighbors, spos_ac, gd.cell_cv)

    #ng = 2**extra_parameters.get('log2ng', 10)
    #transformer = FourierTransformer(rcmax, ng)
    #tsoc = TwoSiteOverlapCalculator(transformer)
    #msoc = ManySiteOverlapCalculator(tsoc, I_a, I_a)

    msoc = wfs.tci.msoc

    phit_Ij = [setup.phit_j for setup in tci.setups_I]
    l_Ij = []
    for phit_j in phit_Ij:
        l_Ij.append([phit.get_angular_momentum_number() for phit in phit_j])

    pt_l_Ij = [setup.l_j for setup in tci.setups_I]
    pt_Ij = [setup.pt_j for setup in tci.setups_I]
    phit_Ijq = msoc.transform(phit_Ij)
    pt_Ijq = msoc.transform(pt_Ij)

    #self.Theta_expansions = msoc.calculate_expansions(l_Ij, phit_Ijq,
    #                                                  l_Ij, phit_Ijq)
    #self.T_expansions = msoc.calculate_kinetic_expansions(l_Ij, phit_Ijq)
    P_expansions = msoc.calculate_expansions(pt_l_Ij, pt_Ijq, l_Ij, phit_Ijq)
    P_neighbors_a = {}

    for a1 in atom_partition.my_indices:
        for a2 in range(len(wfs.setups)):
            R_ca_and_offset_a = dists_and_offsets.get(a1, a2)
            if R_ca_and_offset_a is None:  # No overlap between a1 and a2
                continue

            maxdistance = pcutoff_a[a1] + phicutoff_a[a2]
            expansion = P_expansions.get(a1, a2)
            P_qim = expansion.zeros((nq, ), dtype=wfs.dtype)
            disp = None
            for R_c, offset in R_ca_and_offset_a:
                r = np.linalg.norm(R_c)
                if r > maxdistance:
                    continue

                # Below lines are meant to make use of symmetry.  Will not
                # be relevant for P.
                #remainder = (a1 + a2) % 2
                #if a1 < a2 and not remainder:
                #    continue
                # if a1 > a2 and remainder:
                #    continue

                phases = overlapcalc.phaseclass(overlapcalc.ibzk_qc, offset)
                disp = AtomicDisplacement(None, a1, a2, R_c, offset, phases)
                disp.evaluate_overlap(expansion, P_qim)

            if disp is not None:  # there was at least one non-zero overlap
                assert (a1, a2) not in P_aaqim
                P_aaqim[(a1, a2)] = P_qim
                P_neighbors_a.setdefault(a1, []).append(a2)

    Pkeys = P_aaqim.keys()
    Pkeys.sort()

    def get_M1M2(a):
        M1 = wfs.setups.M_a[a]
        M2 = M1 + wfs.setups[a].nao
        return M1, M2

    oldstyle_P_aqMi = None
    if 0:  #wfs.world.size == 1:
        oldstyle_P_aqMi = {}
        for a, setup in enumerate(wfs.setups):
            oldstyle_P_aqMi[a] = np.zeros((nq, wfs.setups.nao, setup.ni),
                                          dtype=wfs.dtype)
        print([(s.ni, s.nao) for s in wfs.setups])
        for a1, a2 in Pkeys:
            M1, M2 = get_M1M2(a2)
            Pconj_qmi = P_aaqim[(a1, a2)].transpose(0, 2, 1).conjugate()
            oldstyle_P_aqMi[a1][:, M1:M2, :] = Pconj_qmi

    # XXX mind distribution

    return P_neighbors_a, P_aaqim, oldstyle_P_aqMi
Example #51
0
def create_stucture(ratio, width, edge, key = 'top', a = 1.42):
    
    vacuum  =   10
    length  =   get_length(ratio, width, edge)
    length_b=   int(length * .8)*2
    width_b =   width * 4
    
    if edge == 'zz':    
        orig    =   [np.sqrt(3)*a, 2*a] # -> AB-stacking
        b_l, b_w=   4, 3*width
        if width % 2 == 1: raise
    if edge == 'ac':    
        orig    =   [2*a, np.sqrt(3)*a] # -> AB-stacking
        b_l, b_w=   2, 3*width 
        if width % 2 == 0: raise        
    
    bottom  =   graphene_nanoribbon2(length_b, width_b, edge_type=edge, 
                                    saturated=False, 
                                    C_H=1.09,
                                    C_C=a, 
                                    vacuum=2.5, 
                                    sheet=False, 
                                    main_element='C', 
                                    saturate_element='H')
    
    top     =   graphene_nanoribbon2(length, width, edge_type=edge, 
                                    saturated=True, 
                                    C_H=1.09,
                                    C_C=a, 
                                    vacuum=2.5, 
                                    sheet=False, 
                                    main_element='C', 
                                    saturate_element='H')
    
    base    =   graphene_nanoribbon2(b_l, b_w, edge_type=edge, 
                                    saturated=True, 
                                    C_H=1.09,
                                    C_C=a, 
                                    vacuum=2.5, 
                                    sheet=False, 
                                    main_element='C', 
                                    saturate_element='H')
    
    if key == 'top_bottom':
        top.translate([orig[0],orig[1],3.4])
        atoms       =   bottom 
        atoms.extend(top)
        atoms.cell  =   [bottom.cell[0,0], bottom.cell[1,1], 2 * vacuum + 3.4]
        atoms.center()
        atoms.pbc   =   [True, True, False]
        
        #view(atoms)
        return atoms

    if key == 'top':
        atoms       =   top
        L, W        =   top.cell[0,0], top.cell[1,1] 
        atoms.cell  =   [top.cell[0,0]*1.5, 2*top.cell[0,0] + top.cell[1,1], 2 * vacuum]
        atoms.center()
        atoms.positions[:,2] = 3.4
        atoms.pbc   =   [False, False, False]
        return atoms, L, W, length, None
    
    if key == 'rib+base':

        if edge == 'zz':    base.translate([-base.cell[0,0], -base.cell[1,1]/3 ,0])
        if edge == 'ac':    base.translate([-base.cell[0,0], -base.cell[1,1]/3 + np.sqrt(3) / 2. * a,0])

        atoms       =   top + base
        
        rads    =   np.ones(len(atoms))*.3
        nl = NeighborList(rads, self_interaction=False,
                 bothways=True)
        nl.update(atoms)
        
        coll        =   []
        for i, atom in enumerate(atoms):
            if atom.number == 1:
                if 1 < len(nl.get_neighbors(i)[0]):
                    for j in nl.get_neighbors(i)[0]:
                        if atoms.numbers[j] == 1:
                            coll.append(j)
        del atoms[coll]

        atoms_base  =   []
        for i, atom in enumerate(atoms):
            if atom.position[0] < 0:
                atoms_base.append(i)
                
        rads    =   np.ones(len(atoms))*.7
        nl = NeighborList(rads, self_interaction=False,
                 bothways=True)
        nl.update(atoms)

        
        new_pos =   []
        for i, atom in enumerate(atoms):
            if len(nl.get_neighbors(i)[0]) == 2 and atom.number == 6 and i not in atoms_base:
                pos =   np.zeros(3)
                for j in nl.get_neighbors(i)[0]:
                    pos +=  atoms.positions[j] - atoms.positions[i] 
                    if atoms.numbers[j] != 6: raise
                new_pos.append(atoms.positions[i] - pos)
        
        for pos in new_pos:
            atoms   +=  Atom('C', position = pos)
        
        rads    =   np.ones(len(atoms))*.7
        nl = NeighborList(rads, self_interaction=False,
                 bothways=True)
        nl.update(atoms)
        
        h_pos =   []
        for i, atom in enumerate(atoms):
            if len(nl.get_neighbors(i)[0]) == 2 and atom.number == 6:
                pos =   np.zeros(3)
                for j in nl.get_neighbors(i)[0]:
                    pos +=  atoms.positions[j] - atoms.positions[i] 
                    if atoms.numbers[j] != 6: raise
                pos =   pos/np.linalg.norm(pos)
                
                h_pos.append(atoms.positions[i] - pos)
        
        for pos in h_pos:
            atoms   +=  Atom('H', position = pos)
        
        
        
        L, W        =   top.cell[0,0], top.cell[1,1] 
        
        
        
        atoms.cell  =   [top.cell[0,0]*1.5, 2*top.cell[0,0] + top.cell[1,1], 2 * vacuum]
        atoms.center()
        atoms.positions[:,2] = 3.4
        atoms.pbc   =   [False, False, False]
        return atoms, L, W, length, atoms_base
Example #52
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
Example #53
0
def eval_energy(input):
    """Function to evaluate energy of an individual
    Inputs:
        input = [Optimizer class object with parameters, Individual class structure to be evaluated]
    Outputs:
        energy, bul, individ, signal
        energy = energy of Individual evaluated
        bul = bulk structure of Individual if simulation structure is Defect
        individ = Individual class structure evaluated
        signal = string of information about evaluation
    """
    if input[0]==None:
        energy=0
        bul=0
        individ=0
        rank = MPI.COMM_WORLD.Get_rank()
        signal='Evaluated none individual on '+repr(rank)+'\n'
    else:
        [Optimizer, individ]=input
    if Optimizer.calc_method=='MAST':
        energy = individ.energy
        bul = individ.energy
        signal = 'Recieved MAST structure\n'
    else:
        if Optimizer.parallel: rank = MPI.COMM_WORLD.Get_rank()
        if not Optimizer.genealogy:
            STR='----Individual ' + str(individ.index)+ ' Optimization----\n'
        else:
            STR='----Individual ' + str(individ.history_index)+ ' Optimization----\n'
        indiv=individ[0]
        if 'EE' in Optimizer.debug:
            debug = True
        else:
            debug = False
        if debug: 
            write_xyz(Optimizer.debugfile,indiv,'Recieved by eval_energy')
            Optimizer.debugfile.flush()
        if Optimizer.structure=='Defect':
            indi=indiv.copy()
            if Optimizer.alloy==True:
                bulk=individ.bulki
            else:
                bulk=individ.bulko
            nat=indi.get_number_of_atoms()
            csize=bulk.get_cell()                                                                                                         
            totalsol=Atoms(cell=csize, pbc=True)
            totalsol.extend(indi)
            totalsol.extend(bulk)
            for sym,c,m,u in Optimizer.atomlist:
                nc=len([atm for atm in totalsol if atm.symbol==sym])
                STR+='Defect configuration contains '+repr(nc)+' '+repr(sym)+' atoms\n'
    
        elif Optimizer.structure=='Surface':
            totalsol=Atoms()
            totalsol.extend(indiv)
            nat=indiv.get_number_of_atoms()
            totalsol.extend(individ.bulki)
            for sym,c,m,u in Optimizer.atomlist:
                nc=len([atm for atm in totalsol if atm.symbol==sym])
                STR+='Surface-Bulk configuration contains '+repr(nc)+' '+repr(sym)+' atoms\n'
            cell=numpy.maximum.reduce(indiv.get_cell())
            totalsol.set_cell([cell[0],cell[1],500])
            totalsol.set_pbc([True,True,False])
    
        if Optimizer.constrain_position:
            ts = totalsol.copy()
            indc,indb,vacant,swap,stro = find_defects(ts,Optimizer.solidbulk,0)
            sbulk = Optimizer.solidbulk.copy()
            bcom = sbulk.get_center_of_mass()
            #totalsol.translate(-bulkcom)
            #indc.translate(-bulkcom)
            #totalsol.append(Atom(position=[0,0,0]))
    # 			for one in indc:
    # 				index = [atm.index for atm in totalsol if atm.position[0]==one.position[0] and atm.position[1]==one.position[1] and atm.position[2]==one.position[2]][0]
    # 				if totalsol.get_distance(-1,index) > Optimizer.sf:
    # 					r = random.random()
    # 					totalsol.set_distance(-1,index,Optimizer.sf*r,fix=0)
    # 			totalsol.pop()
    # 			totalsol.translate(bulkcom)
            com = indc.get_center_of_mass()
            dist = (sum((bcom[i] - com[i])**2 for i in range(3)))**0.5
            if dist > Optimizer.sf:
                STR+='Shifting structure to within region\n'
                r = random.random()*Optimizer.sf
                comv = numpy.linalg.norm(com)
                ncom = [one*r/comv for one in com]
                trans = [ncom[i]-com[i] for i in range(3)]
                indices = []
                for one in indc:
                    id = [atm.index for atm in totalsol if atm.position[0]==one.position[0] and atm.position[1]==one.position[1] and atm.position[2]==one.position[2]][0]
                    totalsol[id].position += trans
    
        # Check for atoms that are too close
        min_len=0.7
        #pdb.set_trace()
        if not Optimizer.fixed_region:
            if Optimizer.structure=='Defect' or Optimizer.structure=='Surface':
                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)
                if debug:
                    write_xyz(Optimizer.debugfile,totalsol,'After minlength check')
                    Optimizer.debugfile.flush()
            else:
                for i in range(len(indiv)):
                    for j in range(len(indiv)):
                        if i != j:
                            d=indiv.get_distance(i,j)
                            if d < min_len:
                                indiv.set_distance(i,j,min_len,fix=0.5)
                                STR+='--- WARNING: Atoms too close (<0.7A) - Implement Move ---\n'
                if debug:
                    write_xyz(Optimizer.debugfile,indiv,'After minlength check')
                    Optimizer.debugfile.flush()
    
        # Set calculator to use to get forces/energies
        if Optimizer.parallel:
            calc = setup_calculator(Optimizer)
            if Optimizer.fixed_region:
                pms=copy.deepcopy(calc.parameters)
                try:
                    pms['mass'][len(pms['mass'])-1] += '\ngroup RO id >= '+repr(nat)+'\nfix freeze RO setforce 0.0 0.0 0.0\n'
                except KeyError:
                    pms['pair_coeff'][0] += '\ngroup RO id >= '+repr(nat)+'\nfix freeze RO setforce 0.0 0.0 0.0\n'
                calc = LAMMPS(parameters=pms, files=calc.files, keep_tmp_files=calc.keep_tmp_files, tmp_dir=calc.tmp_dir)
                lmin = copy.copy(Optimizer.lammps_min)
                Optimizer.lammps_min = None
                Optimizer.static_calc = setup_calculator(Optimizer)
                Optimizer.lammps_min = lmin
        else:
            calc=Optimizer.calc
        if Optimizer.structure=='Defect' or Optimizer.structure=='Surface':
            totalsol.set_calculator(calc)
            totalsol.set_pbc(True)
        else:
            indiv.set_calculator(calc)
            indiv.set_pbc(True)	#Current bug in ASE optimizer-Lammps prevents pbc=false 
            if Optimizer.structure=='Cluster':
                indiv.set_cell([500,500,500])
                indiv.translate([250,250,250])
    
        cwd=os.getcwd()
        # Perform Energy Minimization
        if not Optimizer.parallel:
            Optimizer.output.flush()
        if Optimizer.ase_min == True:
            try:
                if Optimizer.structure=='Defect' or Optimizer.structure=='Surface':
                    dyn=BFGS(totalsol)
                else:
                    dyn=BFGS(indiv)
                dyn.run(fmax=Optimizer.ase_min_fmax, steps=Optimizer.ase_min_maxsteps)
            except OverflowError:
                STR+='--- Error: Infinite Energy Calculated - Implement Random ---\n'
                box=Atoms()
                indiv=gen_pop_box(Optimizer.natoms, Optimizer.atomlist, Optimizer.size)
                indiv.set_calculator(calc)
                dyn=BFGS(indiv)
                dyn.run(fmax=fmax, steps=steps)
            except numpy.linalg.linalg.LinAlgError:
                STR+='--- Error: Singular Matrix - Implement Random ---\n'
                indiv=gen_pop_box(Optimizer.natoms, Optimizer.atomlist, Optimizer.size)
                indiv.set_calculator(calc)
                dyn=BFGS(indiv)
                dyn.run(fmax=fmax, steps=steps)
            # Get Energy of Minimized Structure
            if Optimizer.structure=='Defect' or Optimizer.structure=='Surface':
                en=totalsol.get_potential_energy()
                #force=numpy.maximum.reduce(abs(totalsol.get_forces()))
                if Optimizer.fitness_scheme == 'enthalpyfit':
                    pressure=totalsol.get_isotropic_pressure(totalsol.get_stress())
                    cell_max=numpy.maximum.reduce(totalsol.get_positions())
                    cell_min=numpy.minimum.reduce(totalsol.get_positions())
                    cell=cell_max-cell_min
                    volume=cell[0]*cell[1]*cell[2]
                else:
                    pressure=0
                    volume=0
                na=totalsol.get_number_of_atoms()
                ena=en/na
                energy=en
                individ[0]=totalsol[0:nat]
                bul=totalsol[(nat):len(totalsol)]
                STR+='Number of positions = '+repr(len(bul)+len(individ[0]))+'\n'
                individ[0].set_cell(csize)
                indiv=individ[0]
            else:
                en=indiv.get_potential_energy()
                if Optimizer.fitness_scheme == 'enthalpyfit':
                    pressure=indiv.get_isotropic_pressure(indiv.get_stress())
                    cell_max=numpy.maximum.reduce(indiv.get_positions())
                    cell_min=numpy.minimum.reduce(indiv.get_positions())
                    cell=cell_max-cell_min
                    volume=cell[0]*cell[1]*cell[2]
                else: 
                    pressure=0
                    volume=0
                na=indiv.get_number_of_atoms()
                ena=en/na
                energy=ena
                individ[0]=indiv
                bul=0
        else:
            if Optimizer.structure=='Defect' or Optimizer.structure=='Surface':
                if Optimizer.calc_method=='VASP':
                    en=totalsol.get_potential_energy()
                    calcb=Vasp(restart=True)
                    totalsol=calcb.get_atoms()
                    stress=calcb.read_stress()
                else:
                    try:
                        totcop=totalsol.copy()
                        if debug: write_xyz(Optimizer.debugfile,totcop,'Individual sent to lammps')
                        OUT=totalsol.calc.calculate(totalsol)
                        totalsol=OUT['atoms']
                        totalsol.set_pbc(True)
                        if Optimizer.fixed_region:
                            if debug:
                                print 'Energy of fixed region calc = ', OUT['thermo'][-1]['pe']
                            totalsol.set_calculator(Optimizer.static_calc)
                            OUT=totalsol.calc.calculate(totalsol)
                            totalsol=OUT['atoms']
                            totalsol.set_pbc(True)
                            if debug:
                                print 'Energy of static calc = ', OUT['thermo'][-1]['pe']
                        en=OUT['thermo'][-1]['pe']
                        stress=numpy.array([OUT['thermo'][-1][i] for i in ('pxx','pyy','pzz','pyz','pxz','pxy')])*(-1e-4*GPa)
                        #force=numpy.maximum.reduce(abs(totalsol.get_forces()))
                        if debug:
                            write_xyz(Optimizer.debugfile,totalsol,'After Lammps Minimization')
                            Optimizer.debugfile.flush()
                    except Exception, e:
                        os.chdir(cwd)
                        STR+='WARNING: Exception during energy eval:\n'+repr(e)+'\n'
                        f=open('problem-structures.xyz','a')
                        write_xyz(f,totcop,data='Starting structure hindex='+individ.history_index)
                        write_xyz(f,totalsol,data='Lammps Min structure')
                        en=10
                        stress=0
                        f.close()
                if Optimizer.fitness_scheme == 'enthalpyfit':
                    pressure=totalsol.get_isotropic_pressure(stress)
                    cell_max=numpy.maximum.reduce(totalsol.get_positions())
                    cell_min=numpy.minimum.reduce(totalsol.get_positions())
                    cell=cell_max-cell_min
                    volume=cell[0]*cell[1]*cell[2]
                else:
                    pressure=totalsol.get_isotropic_pressure(stress)
                    volume=0
                na=totalsol.get_number_of_atoms()
                ena=en/na
                energy=en
                if Optimizer.structure=='Defect':
                    if Optimizer.fixed_region==True or Optimizer.finddefects==False:
                        individ[0]=totalsol[0:nat]
                        bul=totalsol[(nat):len(totalsol)]
                        individ[0].set_cell(csize)
                    else:
                        if 'FI' in Optimizer.debug:
                            outt=find_defects(totalsol,Optimizer.solidbulk,Optimizer.sf,atomlistcheck=Optimizer.atomlist,trackvacs=Optimizer.trackvacs,trackswaps=Optimizer.trackswaps,debug=Optimizer.debugfile)
                        else:
                            outt=find_defects(totalsol,Optimizer.solidbulk,Optimizer.sf,atomlistcheck=Optimizer.atomlist,trackvacs=Optimizer.trackvacs,trackswaps=Optimizer.trackswaps,debug=False)
                        individ[0]=outt[0]
                        bul=outt[1]
                        individ.vacancies = outt[2]
                        individ.swaps = outt[3]
                        STR += outt[4]
                    indiv=individ[0]
                else:
                    top,bul=find_top_layer(totalsol,Optimizer.surftopthick)
                    indiv=top.copy()
                    individ[0]=top.copy()
            else:
Example #54
0
File: emt.py Project: PHOTOX/fuase
class EMT(Calculator):
    implemented_properties = ['energy', 'forces']

    nolabel = True

    def __init__(self):
        Calculator.__init__(self)

    def initialize(self, atoms):
        self.par = {}
        self.rc = 0.0
        self.numbers = atoms.get_atomic_numbers()
        maxseq = max(par[1] for par in parameters.values()) * Bohr
        rc = self.rc = beta * maxseq * 0.5 * (sqrt(3) + sqrt(4))
        rr = rc * 2 * sqrt(4) / (sqrt(3) + sqrt(4))
        self.acut = np.log(9999.0) / (rr - rc)
        for Z in self.numbers:
            if Z not in self.par:
                p = parameters[chemical_symbols[Z]]
                s0 = p[1] * Bohr
                eta2 = p[3] / Bohr
                kappa = p[4] / Bohr
                x = eta2 * beta * s0
                gamma1 = 0.0
                gamma2 = 0.0
                for i, n in enumerate([12, 6, 24]):
                    r = s0 * beta * sqrt(i + 1)
                    x = n / (12 * (1.0 + exp(self.acut * (r - rc))))
                    gamma1 += x * exp(-eta2 * (r - beta * s0))
                    gamma2 += x * exp(-kappa / beta * (r - beta * s0))

                self.par[Z] = {'E0': p[0],
                               's0': s0,
                               'V0': p[2],
                               'eta2': eta2,
                               'kappa': kappa,
                               'lambda': p[5] / Bohr,
                               'n0': p[6] / Bohr**3,
                               'rc': rc,
                               'gamma1': gamma1,
                               'gamma2': gamma2}
                #if rc + 0.5 > self.rc:
                #    self.rc = rc + 0.5

        self.ksi = {}
        for s1, p1 in self.par.items():
            self.ksi[s1] = {}
            for s2, p2 in self.par.items():
                #self.ksi[s1][s2] = (p2['n0'] / p1['n0'] *
                #                    exp(eta1 * (p1['s0'] - p2['s0'])))
                self.ksi[s1][s2] = p2['n0'] / p1['n0']
                
        self.forces = np.empty((len(atoms), 3))
        self.sigma1 = np.empty(len(atoms))
        self.deds = np.empty(len(atoms))
                    
        self.nl = NeighborList([0.5 * self.rc + 0.25] * len(atoms),
                               self_interaction=False)

    def calculate(self, atoms=None, properties=['energy'],
                  system_changes=all_changes):
        Calculator.calculate(self, atoms, properties, system_changes)

        if 'numbers' in system_changes:
            self.initialize(self.atoms)

        positions = self.atoms.positions
        numbers = self.atoms.numbers
        cell = self.atoms.cell
        
        self.nl.update(self.atoms)
        
        self.energy = 0.0
        self.sigma1[:] = 0.0
        self.forces[:] = 0.0

        natoms = len(self.atoms)

        for a1 in range(natoms):
            Z1 = numbers[a1]
            p1 = self.par[Z1]
            ksi = self.ksi[Z1]
            neighbors, offsets = self.nl.get_neighbors(a1)
            offsets = np.dot(offsets, cell)
            for a2, offset in zip(neighbors, offsets):
                d = positions[a2] + offset - positions[a1]
                r = sqrt(np.dot(d, d))
                if r < self.rc + 0.5:
                    Z2 = numbers[a2]
                    p2 = self.par[Z2]
                    self.interact1(a1, a2, d, r, p1, p2, ksi[Z2])
                                
        for a in range(natoms):
            Z = numbers[a]
            p = self.par[Z]
            try:
                ds = -log(self.sigma1[a] / 12) / (beta * p['eta2'])
            except (OverflowError, ValueError):
                self.deds[a] = 0.0
                self.energy -= p['E0']
                continue
            x = p['lambda'] * ds
            y = exp(-x)
            z = 6 * p['V0'] * exp(-p['kappa'] * ds)
            self.deds[a] = ((x * y * p['E0'] * p['lambda'] + p['kappa'] * z) /
                            (self.sigma1[a] * beta * p['eta2']))
            self.energy += p['E0'] * ((1 + x) * y - 1) + z

        for a1 in range(natoms):
            Z1 = numbers[a1]
            p1 = self.par[Z1]
            ksi = self.ksi[Z1]
            neighbors, offsets = self.nl.get_neighbors(a1)
            offsets = np.dot(offsets, cell)
            for a2, offset in zip(neighbors, offsets):
                d = positions[a2] + offset - positions[a1]
                r = sqrt(np.dot(d, d))
                if r < self.rc + 0.5:
                    Z2 = numbers[a2]
                    p2 = self.par[Z2]
                    self.interact2(a1, a2, d, r, p1, p2, ksi[Z2])

        self.results['energy'] = self.energy
        self.results['forces'] = self.forces

    def interact1(self, a1, a2, d, r, p1, p2, ksi):
        x = exp(self.acut * (r - self.rc))
        theta = 1.0 / (1.0 + x)
        y1 = (0.5 * p1['V0'] * exp(-p2['kappa'] * (r / beta - p2['s0'])) *
              ksi / p1['gamma2'] * theta)
        y2 = (0.5 * p2['V0'] * exp(-p1['kappa'] * (r / beta - p1['s0'])) /
              ksi / p2['gamma2'] * theta)
        self.energy -= y1 + y2
        f = ((y1 * p2['kappa'] + y2 * p1['kappa']) / beta +
             (y1 + y2) * self.acut * theta * x) * d / r
        self.forces[a1] += f
        self.forces[a2] -= f
        self.sigma1[a1] += (exp(-p2['eta2'] * (r - beta * p2['s0'])) *
                            ksi * theta / p1['gamma1'])
        self.sigma1[a2] += (exp(-p1['eta2'] * (r - beta * p1['s0'])) /
                            ksi * theta / p2['gamma1'])

    def interact2(self, a1, a2, d, r, p1, p2, ksi):
        x = exp(self.acut * (r - self.rc))
        theta = 1.0 / (1.0 + x)
        y1 = (exp(-p2['eta2'] * (r - beta * p2['s0'])) *
              ksi / p1['gamma1'] * theta * self.deds[a1])
        y2 = (exp(-p1['eta2'] * (r - beta * p1['s0'])) /
              ksi / p2['gamma1'] * theta * self.deds[a2])
        f = ((y1 * p2['eta2'] + y2 * p1['eta2']) +
             (y1 + y2) * self.acut * theta * x) * d / r
        self.forces[a1] -= f
        self.forces[a2] += f
Example #55
0
C[5, 5] = 47.5 * GPa

# Surface energy computed with DFT (PBE-GGA)
surface_energy = 0.161 * (J / m**2) * 10

a = atoms.copy()
b = a * [nx, 1, 1]
b.center()
b.positions[:, 0] += 0.5
b.set_scaled_positions(b.get_scaled_positions())

mask = ((b.positions[:, 0] > 0.) & (b.positions[:, 0] < 2 * a.cell[0, 0]) &
        (b.positions[:, 1] < (a.cell[1, 1] / 2.0 + final_height / 2.0)) &
        (b.positions[:, 1] > (a.cell[1, 1] / 2.0 - final_height / 2.0)))

nl = NeighborList([1.0] * len(b), self_interaction=False, bothways=True)
nl.update(b)

term = Atoms()

if tetra:
    for i in range(len(b)):
        if not mask[i]:
            continue
        indices, offsets = nl.get_neighbors(i)
        if b.numbers[i] == 8 and mask[indices].sum() == 0:
            mask[i] = False
        if b.numbers[i] == 14:
            # complete tetrahedra
            for (j, o) in zip(indices, offsets):
                if b.numbers[j] == 8:
Example #56
0
class EAM(Calculator):
    r"""

    EAM Interface Documentation
    
Introduction
============

The Embedded Atom Method (EAM) [1]_ is a classical potential which is
good for modelling metals particularly or fcc materials. Because it is
an equiaxial potential the EAM does not model directional bonds
well. However, the Angular Dependent Potential (ADP) [2]_ which is an
extended version of EAM is able to model directional bonds is part of
the EAM calculator.

A single element EAM potential is defined by three functions: the
embedded energy, electron density and the pair potential (phi).
A two element alloy contains the individual three functions
for each element plus cross pair interactions.
The ADP potential has additional two additional sets of data to
define the dipole and quadrupole directional terms for each alloy
and their cross interactions.

The files containing the potentials for this calculator are not
included but many suitable potentials can be downloaded from The
Interatomic Potentials Repository Project at
http://www.ctcms.nist.gov/potentials/

Running the Calculator
======================

EAM calculates the cohesive atom energy and forces. Internally the
potential functions are defined by splines which may be directly
supplied or created by reading the spline points from a data file from
which a slpline function is created.  The LAMMPS compatible ``.alloy``
and ``.adp`` formats are supported. The LAMMPS ``.eam`` format is
slightly different from the ``.alloy`` format and is currently not
supported.

For example::

    from eam import EAM

    mishin = EAM('Al99.eam.alloy')
    mishin.write_file('new.eam.alloy')
    mishin.plot()

    slab.set_calculator(mishin)
    slab.get_potential_energy()
    slab.get_forces()


Arguments
=========

=========================  ====================================================
Keyword                    Description
=========================  ====================================================
``fileobj``                file of potential in ``.alloy`` or ``.adp`` format
                           (This is generally all you need to supply)
                            
``elements[N]``            array of N element abreviations

``embedded_energy[N]``     arrays of embedded energy functions

``electron_density[N]``    arrays of electron density functions

``phi[N,N]``               arrays of pair potential funtions

``d_embedded_energy[N]``   arrays of derivative embedded energy functions

``d_electron_density[N]``  arrays of derivative electron density functions

``d_phi[N,N]``             arrays of derivative pair potentials functions

``d[N], q[N,N]``           ADP dipole and quadrupole function

``d_d[N,N], d_q[N,N]``     ADP dipole and quadrupole derivative functions

=========================  ====================================================


Additional parameters for writing potential files
=================================================

The following parameters are only required for writing a potential in
``.alloy`` or ``.adp`` format file.

=========================  ====================================================
Keyword                    Description
=========================  ====================================================
``Z[N]``                   Array of atomic number of each element

``mass[N]``                Atomic mass of each element

``a[N]``                   Array of lattice parameters for each element

``lattice[N]``             Lattice type

``nrho``                   No. of rho samples

``drho``                   Increment for sampling density

``nr``                     No. of radial points

``dr``                     Increment for sampling radius

=========================  ====================================================

Special features
================

``.plot()``
  Plots the individual funtions. This may be called from multiple EAM
  potentials to compare the shape of the individual curves. This
  function requires the installation of the Matplotlib libraries.

Notes/Issues
=============

* Although currently not fast, this calculator can be good for trying
  small calculations or for creating new potentials by matching baseline
  data such as from DFT results. The format for these potentials is
  compatable with LAMMPS_ and so can be used either directly by LAMMPS or
  with the ASE LAMMPS calculator interface.

* Supported formats are the LAMMPS_ ``.alloy`` and ``.adp``. The
  ``.eam`` format is currently not supported.

* Any supplied values will overide values read from the file.

* The derivative functions, if supplied, are only used to calculate
  forces.


.. _LAMMPS: http://lammps.sandia.gov/

.. [1] M.S. Daw and M.I. Baskes, Phys. Rev. Letters 50 (1983)
       1285.

.. [2] Y. Mishin, M.J. Mehl, and D.A. Papaconstantopoulos,
       Acta Materialia 53 2005 4029--4041.


End EAM Interface Documentation
    """

    def __init__(self, fileobj=None, **kwargs):

        if fileobj != None:
            self.read_file(fileobj)

        valid_args = ('elements', 'header', 'drho', 'dr', 'cutoff',
                      'atomic_number', 'mass', 'a', 'lattice',
                      'embedded_energy', 'electron_density', 'phi',
                      # adp terms
                      'd', 'q', 'd_d', 'd_q',
                      # derivatives
                      'd_embedded_energy', 'd_electron_density', 'd_phi',
                      'skin', 'form', 'Z', 'nr', 'nrho', 'mass')

        # these variables need to be set, but are not read in
        if 'skin' not in kwargs:
            self.skin = 1.0

        # set any additional keyword arguments
        for arg, val in kwargs.iteritems():
            if arg in valid_args:
                setattr(self, arg, val)
            else:
                raise RuntimeError('unknown keyword arg "%s" : not in %s'
                                   % (arg, valid_args))

        # initialise the state of the calculation
        self.Nelements = len(self.elements)
        self.energy = 0.0
        self.positions = None
        self.cell = None
        self.pbc = None
        
    def set_form(self, fileobj):
        extension = os.path.splitext(fileobj)[1]

        if extension == '.eam':
            self.form = 'eam'
            raise NotImplementedError
        
        elif extension == '.alloy':
            self.form = 'alloy'
                
        elif extension == '.adp':
            self.form = 'adp'

        else:
            raise RuntimeError('unknown file extension type: %s' % extension)

    def read_file(self, fileobj):
        """Reads a LAMMPS EAM file in alloy format
        and creates the interpolation functions from the data
        """

        if isinstance(fileobj, str):
            f = open(fileobj)
            self.set_form(fileobj)
        else:
            f = fileobj
         
        lines = f.readlines()
        self.header = lines[:3]
        i = 3

        # make the data one long line so as not to care how its formatted
        data = []
        for line in lines[i:]:
            data.extend(line.split())
            
        self.Nelements = int(data[0])
        d = 1
        self.elements = data[d:(d + self.Nelements)]
        d += self.Nelements

        self.nrho = int(data[d])
        self.drho = float(data[d + 1])
        self.nr = int(data[d + 2])
        self.dr = float(data[d + 3])
        self.cutoff = float(data[d + 4])
        
        self.embedded_data = np.zeros([self.Nelements, self.nrho])
        self.density_data = np.zeros([self.Nelements, self.nr])
        self.Z = np.zeros([self.Nelements], dtype=int)
        self.mass = np.zeros([self.Nelements])
        self.a = np.zeros([self.Nelements])
        self.lattice = []
        d += 5

        # reads in the part of the eam file for each element
        for elem in range(self.Nelements):
            self.Z[elem] = int(data[d])
            self.mass[elem] = float(data[d + 1])
            self.a[elem] = float(data[d + 2])
            self.lattice.append(data[d + 3])
            d += 4
            
            self.embedded_data[elem] = np.float_(data[d:(d + self.nrho)])
            d += self.nrho
            self.density_data[elem] = np.float_(data[d:(d + self.nr)])
            d += self.nr

        # reads in the r*phi data for each interaction between elements
        self.rphi_data = np.zeros([self.Nelements, self.Nelements, self.nr])

        for i in range(self.Nelements):
            for j in range(i, self.Nelements):
                self.rphi_data[i, j] = np.float_(data[d:(d + self.nr)])
                d += self.nr

        self.r = np.arange(0, self.nr) * self.dr
        self.rho = np.arange(0, self.nrho) * self.drho

        # this section turns the file data into three functions (and
        # derivative functions) that define the potential
        self.embedded_energy = np.zeros(self.Nelements, object)
        self.electron_density = np.zeros(self.Nelements, object)
        self.d_embedded_energy = np.zeros(self.Nelements, object)
        self.d_electron_density = np.zeros(self.Nelements, object)

        for i in range(self.Nelements):
            self.embedded_energy[i] = spline(self.rho,
                                             self.embedded_data[i], k=3)
            self.electron_density[i] = spline(self.r,
                                              self.density_data[i], k=3)
            self.d_embedded_energy[i] = self.deriv(self.embedded_energy[i])
            self.d_electron_density[i] = self.deriv(self.electron_density[i])

        self.phi = np.zeros([self.Nelements, self.Nelements], object)
        self.d_phi = np.zeros([self.Nelements, self.Nelements], object)

        # ignore the first point of the phi data because it is forced
        # to go through zero due to the r*phi format in alloy and adp
        for i in range(self.Nelements):
            for j in range(i, self.Nelements):
                if self.form == 'eam':  # not stored as rphi
                    # should we igore the first point for eam ?
                    raise RuntimeError('.eam format not yet supported')

                    self.phi[i, j] = spline(
                        self.r[1:], self.rphi_data[i, j][1:], k=3)
                else:
                    self.phi[i, j] = spline(
                        self.r[1:],
                        self.rphi_data[i, j][1:] / self.r[1:], k=3)

                self.d_phi[i, j] = self.deriv(self.phi[i, j])

                if j != i:
                    self.phi[j, i] = self.phi[i, j]
                    self.d_phi[j, i] = self.d_phi[i, j]

        if (self.form == 'adp'):
            self.read_adp_data(data, d)

    def read_adp_data(self, data, d):
        """reads in the extra data fro the adp format"""

        self.d_data = np.zeros([self.Nelements, self.Nelements, self.nr])
        # should be non symetrical combinations of 2
        for i in range(self.Nelements):
            for j in range(i, self.Nelements):
                self.d_data[i, j] = data[d:d + self.nr]
                d += self.nr
                
        self.q_data = np.zeros([self.Nelements, self.Nelements, self.nr])
        # should be non symetrical combinations of 2
        for i in range(self.Nelements):
            for j in range(i, self.Nelements):
                self.q_data[i, j] = data[d:d + self.nr]
                d += self.nr
    
        self.d = np.zeros([self.Nelements, self.Nelements], object)
        self.d_d = np.zeros([self.Nelements, self.Nelements], object)
        self.q = np.zeros([self.Nelements, self.Nelements], object)
        self.d_q = np.zeros([self.Nelements, self.Nelements], object)
    
        for i in range(self.Nelements):
            for j in range(i, self.Nelements):
                self.d[i, j] = spline(self.r[1:], self.d_data[i, j][1:], k=3)
                self.d_d[i, j] = self.deriv(self.d[i, j])
                self.q[i, j] = spline(self.r[1:], self.q_data[i, j][1:], k=3)
                self.d_q[i, j] = self.deriv(self.q[i, j])

                # make symmetrical
                if j != i:
                    self.d[j, i] = self.d[i, j]
                    self.d_d[j, i] = self.d_d[i, j]
                    self.q[j, i] = self.q[i, j]
                    self.d_q[j, i] = self.d_q[i, j]

    def write_file(self, filename, nc=1, form='%.8e'):
        """Writes out the model of the eam_alloy in lammps alloy
        format to file with name = 'filename' with a data format that
        is nc columns wide.  Note: array lengths need to be an exact
        multiple of nc
"""

        f = open(filename, 'w')

        assert self.nr % nc == 0
        assert self.nrho % nc == 0

        if not hasattr(self, 'header'):
            self.header = """Unknown EAM potential file
Generated from eam.py
blank
"""
        for line in self.header:
            f.write(line)

        f.write('%d ' % self.Nelements)
        for i in range(self.Nelements):
            f.write('%s ' % str(self.elements[i]))
        f.write('\n')

        f.write('%d %f %d %f %f \n' %
                (self.nrho, self.drho, self.nr, self.dr, self.cutoff))
    
        # start of each section for each element
#        rs = np.linspace(0, self.nr * self.dr, self.nr)
#        rhos = np.linspace(0, self.nrho * self.drho, self.nrho)

        rs = np.arange(0, self.nr) * self.dr
        rhos = np.arange(0, self.nrho) * self.drho

        for i in range(self.Nelements):
            f.write('%d %f %f %s\n' % (self.Z[i], self.mass[i],
                                       self.a[i], str(self.lattice[i])))
            np.savetxt(f,
                       self.embedded_energy[i](rhos).reshape(self.nrho / nc,
                                                             nc),
                       fmt=nc * [form])
            np.savetxt(f,
                       self.electron_density[i](rs).reshape(self.nr / nc,
                                                            nc),
                       fmt=nc * [form])

        # write out the pair potentials in Lammps DYNAMO setfl format
        # as r*phi for alloy format
        for i in range(self.Nelements):
            for j in range(i, self.Nelements):
                np.savetxt(f,
                           (rs * self.phi[i, j](rs)).reshape(self.nr / nc,
                                                             nc),
                           fmt=nc * [form])

        if self.form == 'adp':
            # these are the u(r) values
            for i in range(self.Nelements):
                for j in range(i + 1):
                    np.savetxt(f, self.d_data[i, j])
            
            # these are the w(r) values
            for i in range(self.Nelements):
                for j in range(i + 1):
                    np.savetxt(f, self.q_data[i, j])
                
        f.close()

    def update(self, atoms):
        # check all the elements are available in the potential
        elements = np.unique(atoms.get_chemical_symbols())
        unavailable = np.logical_not(
            np.array([item in self.elements for item in elements]))
        if np.any(unavailable):
            raise RuntimeError('These elements are not in the potential: %s' %
                               elements[unavailable])
                    
        # check if anything has changed to require re-calculation
        if (self.positions is None or
            len(self.positions) != len(atoms.get_positions()) or
            (self.positions != atoms.get_positions()).any() or
            (self.cell != atoms.get_cell()).any() or
            (self.pbc != atoms.get_pbc()).any()):

            # cutoffs need to be a vector for NeighborList
            cutoffs = self.cutoff * np.ones(len(atoms))

            # convert the elements to an index of the position
            # in the eam format
            self.index = np.array([atoms.calc.elements.index(el)
                                   for el in atoms.get_chemical_symbols()])
            self.pbc = atoms.get_pbc()

            # since we need the contribution of all neighbors to the
            # local electron density we cannot just calculate and use
            # one way neighbors
            self.neighbors = NeighborList(cutoffs, skin=self.skin,
                                          self_interaction=False,
                                          bothways=True)
            self.neighbors.update(atoms)
            self.calculate(atoms)
            
    def get_forces(self, atoms):
        # calculate the forces based on derivatives of the three EAM functions
        self.update(atoms)
        self.forces = np.zeros((len(atoms), 3))

        for i in range(len(atoms)):  # this is the atom to be embedded
            neighbors, offsets = self.neighbors.get_neighbors(i)
            offset = np.dot(offsets, atoms.get_cell())
            # create a vector of relative positions of neighbors
            rvec = atoms.positions[neighbors] + offset - atoms.positions[i]
            r = np.sqrt(np.sum(np.square(rvec), axis=1))
            nearest = np.arange(len(r))[r < self.cutoff]

            d_embedded_energy_i = self.d_embedded_energy[
                self.index[i]](self.total_density[i])
            urvec = rvec.copy()  # unit directional vector

            for j in np.arange(len(neighbors)):
                urvec[j] = urvec[j] / r[j]
    
            for j_index in range(self.Nelements):
                use = self.index[neighbors[nearest]] == j_index
                if not use.any():
                    continue
                rnuse = r[nearest][use]
                density_j = self.total_density[neighbors[nearest][use]]
                scale = (self.d_phi[self.index[i], j_index](rnuse) +
                         (d_embedded_energy_i *
                          self.d_electron_density[j_index](rnuse)) +
                         (self.d_embedded_energy[j_index](density_j) *
                          self.d_electron_density[self.index[i]](rnuse)))
            
                self.forces[i] += np.dot(scale, urvec[nearest][use])
                
                if (self.form == 'adp'):
                    adp_forces = self.angular_forces(
                        self.mu[i],
                        self.mu[neighbors[nearest][use]],
                        self.lam[i],
                        self.lam[neighbors[nearest][use]],
                        rnuse,
                        rvec[nearest][use],
                        self.index[i],
                        j_index)

                    self.forces[i] += adp_forces
                    
        return self.forces.copy()
            
    def get_stress(self, atoms):
        ## currently not implemented
        raise NotImplementedError
    
    def calculate(self, atoms):
        # calculate the energy
        # the energy is made up of the ionic or pair interaction and
        # the embedding energy of each atom into the electron cloud
        # generated by its neighbors

        energy = 0.0
        adp_energy = 0.0
        self.total_density = np.zeros(len(atoms))
        if (self.form == 'adp'):
            self.mu = np.zeros([len(atoms), 3])
            self.lam = np.zeros([len(atoms), 3, 3])

        for i in range(len(atoms)):  # this is the atom to be embedded
            neighbors, offsets = self.neighbors.get_neighbors(i)
            offset = np.dot(offsets, atoms.get_cell())
            
            rvec = (atoms.positions[neighbors] + offset -
                    atoms.positions[i])

            ## calculate the distance to the nearest neighbors
            r = np.sqrt(np.sum(np.square(rvec), axis=1))  # fast
#            r = np.apply_along_axis(np.linalg.norm, 1, rvec)  # sloow

            nearest = np.arange(len(r))[r <= self.cutoff]
            for j_index in range(self.Nelements):
                use = self.index[neighbors[nearest]] == j_index
                if not use.any():
                    continue
                pair_energy = np.sum(self.phi[self.index[i], j_index](
                        r[nearest][use])) / 2.
                energy += pair_energy

                density = np.sum(
                    self.electron_density[j_index](r[nearest][use]))
                self.total_density[i] += density

                if self.form == 'adp':
                    self.mu[i] += self.eam_dipole(
                        r[nearest][use],
                        rvec[nearest][use],
                        self.d[self.index[i], j_index])
                    
                    self.lam[i] += self.eam_quadrupole(
                        r[nearest][use],
                        rvec[nearest][use],
                        self.q[self.index[i], j_index])

            # add in the electron embedding energy
            embedding_energy = self.embedded_energy[self.index[i]](
                self.total_density[i])
#            print 'pair energy',energy
#            print 'embed energy',embedding_energy
            
            energy += embedding_energy

        if self.form == 'adp':
            mu_energy = np.sum(self.mu**2) / 2.
            adp_energy += mu_energy
#            print 'adp mu', mu_energy
            lam_energy = np.sum(self.lam**2) / 2.
            adp_energy += lam_energy
#            print 'adp lam', lam_energy

            for i in range(len(atoms)):  # this is the atom to be embedded
                adp_energy -= np.sum(self.lam[i].trace()**2) / 6.

#            print 'energy no adp', energy
            energy += adp_energy

        self.positions = atoms.positions.copy()
        self.cell = atoms.get_cell().copy()
        
        self.energy_free = energy
        self.energy_zero = energy

    def plot(self, name=''):
        """Plot the individual curves"""

        try:
            import matplotlib.pyplot as plt
        
        except ImportError:
            raise NotAvailable('This needs matplotlib module.')

        if self.form == 'eam' or self.form == 'alloy':
            nrow = 2
        elif self.form == 'adp':
            nrow = 3
        else:
            raise RuntimeError('Unknown form of potential: %s' % self.form)

        if hasattr(self, 'r'):
            r = self.r
        else:
            r = np.linspace(0, self.cutoff, 50)

        if hasattr(self, 'rho'):
            rho = self.rho
        else:
            rho = np.linspace(0, 10.0, 50)
        
        plt.subplot(nrow, 2, 1)
        self.elem_subplot(rho, self.embedded_energy,
                          r'$\rho$', r'Embedding Energy $F(\bar\rho)$',
                          name, plt)
        
        plt.subplot(nrow, 2, 2)
        self.elem_subplot(r, self.electron_density,
                          r'$r$', r'Electron Density $\rho(r)$', name, plt)

        plt.subplot(nrow, 2, 3)
        self.multielem_subplot(r, self.phi,
                               r'$r$', r'Pair Potential $\phi(r)$', name, plt)
        plt.ylim(-1.0, 1.0)  # need reasonable values

        if self.form == 'adp':
            plt.subplot(nrow, 2, 5)
            self.multielem_subplot(r, self.d,
                                   r'$r$', r'Dipole Energy', name, plt)
            
            plt.subplot(nrow, 2, 6)
            self.multielem_subplot(r, self.q,
                                   r'$r$', r'Quadrupole Energy', name, plt)

        plt.plot()

    def elem_subplot(self, curvex, curvey, xlabel, ylabel, name, plt):
        plt.xlabel(xlabel)
        plt.ylabel(ylabel)
        for i in np.arange(self.Nelements):
            label = name + ' ' + self.elements[i]
            plt.plot(curvex, curvey[i](curvex), label=label)
        plt.legend()

    def multielem_subplot(self, curvex, curvey, xlabel, ylabel, name, plt):
        plt.xlabel(xlabel)
        plt.ylabel(ylabel)
        for i in np.arange(self.Nelements):
            for j in np.arange(i + 1):
                label = name + ' ' + self.elements[i] + '-' + self.elements[j]
                plt.plot(curvex, curvey[i, j](curvex), label=label)
        plt.legend()

    def angular_forces(self, mu_i, mu, lam_i, lam, r, rvec, form1, form2):
        # calculate the extra components for the adp forces
        # rvec are the relative positions to atom i
        psi = np.zeros(mu.shape)
        for gamma in range(3):
            term1 = (mu_i[gamma] - mu[:, gamma]) * self.d[form1][form2](r)

            term2 = np.sum((mu_i - mu) * self.d_d[form1][form2](r)[:, np.newaxis]
                           * (rvec * rvec[:, gamma][:, np.newaxis]
                              / r[:, np.newaxis]), axis=1)
            
            term3 = 2 * np.sum((lam_i[:, gamma] + lam[:, :, gamma])
                               * rvec * self.q[form1][form2](r)[:, np.newaxis],
                               axis=1)
            term4 = 0.0
            for alpha in range(3):
                for beta in range(3):
                    rs = rvec[:, alpha] * rvec[:, beta] * rvec[:, gamma]
                    term4 += ((lam_i[alpha, beta] + lam[:, alpha, beta]) *
                              self.d_q[form1][form2](r) * rs) / r
                    
            term5 = ((lam_i.trace() + lam.trace(axis1=1, axis2=2)) *
                     (self.d_q[form1][form2](r) * r
                      + 2 * self.q[form1][form2](r)) * rvec[:, gamma]) / 3.

            # the minus for term5 is a correction on the adp
            # formulation given in the 2005 Mishin Paper and is posted
            # on the NIST website with the AlH potential
            psi[:, gamma] = term1 + term2 + term3 + term4 - term5

        return np.sum(psi, axis=0)

    def eam_dipole(self, r, rvec, d):
        # calculate the dipole contribution
        mu = np.sum((rvec * d(r)[:, np.newaxis]), axis=0)

        return mu  # sign to agree with lammps

    def eam_quadrupole(self, r, rvec, q):
        # slow way of calculating the quadrupole contribution
        r = np.sqrt(np.sum(rvec**2, axis=1))
    
        lam = np.zeros([rvec.shape[0], 3, 3])
        qr = q(r)
        for alpha in range(3):
            for beta in range(3):
                lam[:, alpha, beta] += qr * rvec[:, alpha] * rvec[:, beta]
            
        return np.sum(lam, axis=0)

    def deriv(self, spline):
        """Wrapper for extracting the derivative from a spline"""
        def d_spline(aspline):
            return spline(aspline, 1)

        return d_spline
Example #57
0
    d = 0.0
    for a in range(len(atoms)):
        i, offsets = nl.get_neighbors(a)
        for j in i:
            c[j] += 1
        c[a] += len(i)
        d += (((R[i] + np.dot(offsets, cell) - R[a])**2).sum(1)**0.5).sum()
    return d, c

for sorted in [False, True]:
    for p1 in range(2):
        for p2 in range(2):
            for p3 in range(2):
                print(p1, p2, p3)
                atoms.set_pbc((p1, p2, p3))
                nl = NeighborList(atoms.numbers * 0.2 + 0.5,
                                  skin=0.0, sorted=sorted)
                nl.update(atoms)
                d, c = count(nl, atoms)
                atoms2 = atoms.repeat((p1 + 1, p2 + 1, p3 + 1))
                nl2 = NeighborList(atoms2.numbers * 0.2 + 0.5,
                                   skin=0.0, sorted=sorted)
                nl2.update(atoms2)
                d2, c2 = count(nl2, atoms2)
                c2.shape = (-1, 10)
                dd = d * (p1 + 1) * (p2 + 1) * (p3 + 1) - d2
                print(dd)
                print(c2 - c)
                assert abs(dd) < 1e-10
                assert not (c2 - c).any()

h2 = Atoms('H2', positions=[(0, 0, 0), (0, 0, 1)])
Example #58
0
File: emt.py Project: grhawk/ASE
class EMT(Calculator):
    implemented_properties = ['energy', 'forces']

    nolabel = True

    def __init__(self):
        Calculator.__init__(self)

    def initialize(self, atoms):
        self.par = {}
        self.rc = 0.0
        self.numbers = atoms.get_atomic_numbers()
        maxseq = max(par[1] for par in parameters.values()) * Bohr
        rc = self.rc = beta * maxseq * 0.5 * (sqrt(3) + sqrt(4))
        rr = rc * 2 * sqrt(4) / (sqrt(3) + sqrt(4))
        self.acut = np.log(9999.0) / (rr - rc)
        for Z in self.numbers:
            if Z not in self.par:
                p = parameters[chemical_symbols[Z]]
                s0 = p[1] * Bohr
                eta2 = p[3] / Bohr
                kappa = p[4] / Bohr
                x = eta2 * beta * s0
                gamma1 = 0.0
                gamma2 = 0.0
                for i, n in enumerate([12, 6, 24]):
                    r = s0 * beta * sqrt(i + 1)
                    x = n / (12 * (1.0 + exp(self.acut * (r - rc))))
                    gamma1 += x * exp(-eta2 * (r - beta * s0))
                    gamma2 += x * exp(-kappa / beta * (r - beta * s0))

                self.par[Z] = {
                    'E0': p[0],
                    's0': s0,
                    'V0': p[2],
                    'eta2': eta2,
                    'kappa': kappa,
                    'lambda': p[5] / Bohr,
                    'n0': p[6] / Bohr**3,
                    'rc': rc,
                    'gamma1': gamma1,
                    'gamma2': gamma2
                }
                #if rc + 0.5 > self.rc:
                #    self.rc = rc + 0.5

        self.ksi = {}
        for s1, p1 in self.par.items():
            self.ksi[s1] = {}
            for s2, p2 in self.par.items():
                #self.ksi[s1][s2] = (p2['n0'] / p1['n0'] *
                #                    exp(eta1 * (p1['s0'] - p2['s0'])))
                self.ksi[s1][s2] = p2['n0'] / p1['n0']

        self.forces = np.empty((len(atoms), 3))
        self.sigma1 = np.empty(len(atoms))
        self.deds = np.empty(len(atoms))

        self.nl = NeighborList([0.5 * self.rc + 0.25] * len(atoms),
                               self_interaction=False)

    def calculate(self,
                  atoms=None,
                  properties=['energy'],
                  system_changes=[
                      'positions', 'numbers', 'cell', 'pbc', 'charges',
                      'magmoms'
                  ]):
        Calculator.calculate(self, atoms, properties, system_changes)

        if 'numbers' in system_changes:
            self.initialize(self.atoms)

        positions = self.atoms.positions
        numbers = self.atoms.numbers
        cell = self.atoms.cell

        self.nl.update(self.atoms)

        self.energy = 0.0
        self.sigma1[:] = 0.0
        self.forces[:] = 0.0

        natoms = len(self.atoms)

        for a1 in range(natoms):
            Z1 = numbers[a1]
            p1 = self.par[Z1]
            ksi = self.ksi[Z1]
            neighbors, offsets = self.nl.get_neighbors(a1)
            offsets = np.dot(offsets, cell)
            for a2, offset in zip(neighbors, offsets):
                d = positions[a2] + offset - positions[a1]
                r = sqrt(np.dot(d, d))
                if r < self.rc + 0.5:
                    Z2 = numbers[a2]
                    p2 = self.par[Z2]
                    self.interact1(a1, a2, d, r, p1, p2, ksi[Z2])

        for a in range(natoms):
            Z = numbers[a]
            p = self.par[Z]
            try:
                ds = -log(self.sigma1[a] / 12) / (beta * p['eta2'])
            except (OverflowError, ValueError):
                self.deds[a] = 0.0
                self.energy -= p['E0']
                continue
            x = p['lambda'] * ds
            y = exp(-x)
            z = 6 * p['V0'] * exp(-p['kappa'] * ds)
            self.deds[a] = ((x * y * p['E0'] * p['lambda'] + p['kappa'] * z) /
                            (self.sigma1[a] * beta * p['eta2']))
            self.energy += p['E0'] * ((1 + x) * y - 1) + z

        for a1 in range(natoms):
            Z1 = numbers[a1]
            p1 = self.par[Z1]
            ksi = self.ksi[Z1]
            neighbors, offsets = self.nl.get_neighbors(a1)
            offsets = np.dot(offsets, cell)
            for a2, offset in zip(neighbors, offsets):
                d = positions[a2] + offset - positions[a1]
                r = sqrt(np.dot(d, d))
                if r < self.rc + 0.5:
                    Z2 = numbers[a2]
                    p2 = self.par[Z2]
                    self.interact2(a1, a2, d, r, p1, p2, ksi[Z2])

        self.results['energy'] = self.energy
        self.results['forces'] = self.forces

    def interact1(self, a1, a2, d, r, p1, p2, ksi):
        x = exp(self.acut * (r - self.rc))
        theta = 1.0 / (1.0 + x)
        y1 = (0.5 * p1['V0'] * exp(-p2['kappa'] * (r / beta - p2['s0'])) *
              ksi / p1['gamma2'] * theta)
        y2 = (0.5 * p2['V0'] * exp(-p1['kappa'] * (r / beta - p1['s0'])) /
              ksi / p2['gamma2'] * theta)
        self.energy -= y1 + y2
        f = ((y1 * p2['kappa'] + y2 * p1['kappa']) / beta +
             (y1 + y2) * self.acut * theta * x) * d / r
        self.forces[a1] += f
        self.forces[a2] -= f
        self.sigma1[a1] += (exp(-p2['eta2'] * (r - beta * p2['s0'])) * ksi *
                            theta / p1['gamma1'])
        self.sigma1[a2] += (exp(-p1['eta2'] * (r - beta * p1['s0'])) / ksi *
                            theta / p2['gamma1'])

    def interact2(self, a1, a2, d, r, p1, p2, ksi):
        x = exp(self.acut * (r - self.rc))
        theta = 1.0 / (1.0 + x)
        y1 = (exp(-p2['eta2'] * (r - beta * p2['s0'])) * ksi / p1['gamma1'] *
              theta * self.deds[a1])
        y2 = (exp(-p1['eta2'] * (r - beta * p1['s0'])) / ksi / p2['gamma1'] *
              theta * self.deds[a2])
        f = ((y1 * p2['eta2'] + y2 * p1['eta2']) +
             (y1 + y2) * self.acut * theta * x) * d / r
        self.forces[a1] -= f
        self.forces[a2] += f