Esempio n. 1
0
def add_hydrogens(atoms):

    cov_radii = [covalent_radii[a.number] for a in atoms]
    nl = NeighborList(cov_radii, bothways = True, self_interaction = False)
    nl.update(atoms)

    need_a_H = []
    for a in atoms:
        nlist=nl.get_neighbors(a.index)[0]
        if len(nlist)<3:
            if a.symbol=='C':
                need_a_H.append(a.index)

    print("Added missing Hydrogen atoms: ", need_a_H)

    dCH=1.1
    for a in need_a_H:
        vec = np.zeros(3)
        indices, offsets = nl.get_neighbors(atoms[a].index)
        for i, offset in zip(indices, offsets):
            vec += -atoms[a].position +(atoms.positions[i] + np.dot(offset, atoms.get_cell()))
        vec = -vec/np.linalg.norm(vec)*dCH
        vec += atoms[a].position
        htoadd = ase.Atom('H',vec)
        atoms.append(htoadd)
Esempio n. 2
0
def add_cap_ox(clust):
    # TODO: fix bug where adds multiple oxygen's to the same place
    """
    Args:
        clust:
    """
    nl = NeighborList(natural_cutoffs(clust),
                      bothways=True,
                      self_interaction=False)
    nl.update(clust)
    new_clust = clust
    cap_inds = get_cap_si(clust)
    for ind in cap_inds:
        while len(nl.get_neighbors(ind)[0]) < 4:
            neighb = nl.get_neighbors(ind)[0][
                -1]  # last index in the list of neighbor indicies
            direction = clust.get_positions()[ind] - clust.get_positions()[
                neighb]  # vector pointing from neighbor to Si
            ox_pos = clust.get_positions(
            )[ind] + 1.6 * direction / np.linalg.norm(direction)
            new_ox = Atom('O', position=ox_pos)
            new_clust.append(new_ox)
            nl = NeighborList(natural_cutoffs(clust),
                              bothways=True,
                              self_interaction=False)
            nl.update(clust)
    return (new_clust)
Esempio n. 3
0
def test_fcc():
    x = bulk('X', 'fcc', a=2**0.5)

    nl = NeighborList([0.5], skin=0.01, bothways=True, self_interaction=False)
    nl.update(x)
    assert len(nl.get_neighbors(0)[0]) == 12

    nl = NeighborList([0.5] * 27,
                      skin=0.01,
                      bothways=True,
                      self_interaction=False)
    nl.update(x * (3, 3, 3))
    for a in range(27):
        assert len(nl.get_neighbors(a)[0]) == 12
    assert not np.any(nl.get_neighbors(13)[1])

    c = 0.0058
    for NeighborListClass in [PrimitiveNeighborList, NewPrimitiveNeighborList]:
        nl = NeighborListClass([c, c],
                               skin=0.0,
                               sorted=True,
                               self_interaction=False,
                               use_scaled_positions=True)
        nl.update([True, True, True],
                  np.eye(3) * 7.56, np.array([[0, 0, 0], [0, 0, 0.99875]]))
        n0, d0 = nl.get_neighbors(0)
        n1, d1 = nl.get_neighbors(1)
        # != is xor
        assert (np.all(n0 == [0]) and np.all(d0 == [0, 0, 1])) != \
            (np.all(n1 == [1]) and np.all(d1 == [0, 0, -1]))
Esempio n. 4
0
def test_H2():
    h2 = Atoms('H2', positions=[(0, 0, 0), (0, 0, 1)])
    nl = NeighborList([0.5, 0.5],
                      skin=0.1,
                      sorted=True,
                      self_interaction=False)
    nl2 = NeighborList([0.5, 0.5],
                       skin=0.1,
                       sorted=True,
                       self_interaction=False,
                       primitive=NewPrimitiveNeighborList)
    assert nl2.update(h2)
    assert nl.update(h2)
    assert not nl.update(h2)
    assert (nl.get_neighbors(0)[0] == [1]).all()
    m = np.zeros((2, 2))
    m[0, 1] = 1
    assert np.array_equal(nl.get_connectivity_matrix(sparse=False), m)
    assert np.array_equal(nl.get_connectivity_matrix(sparse=True).todense(), m)
    assert np.array_equal(nl.get_connectivity_matrix().todense(),
                          nl2.get_connectivity_matrix().todense())

    h2[1].z += 0.09
    assert not nl.update(h2)
    assert (nl.get_neighbors(0)[0] == [1]).all()

    h2[1].z += 0.09
    assert nl.update(h2)
    assert (nl.get_neighbors(0)[0] == []).all()
    assert nl.nupdates == 2
Esempio n. 5
0
def test_small_cell_and_large_cutoff():
    # See: https://gitlab.com/ase/ase/-/issues/441
    cutoff = 50

    atoms = bulk('Cu', 'fcc', a=3.6)
    atoms *= (2, 2, 2)
    atoms.set_pbc(False)
    radii = cutoff * np.ones(len(atoms.get_atomic_numbers()))

    neighborhood_new = NeighborList(radii,
                                    skin=0.0,
                                    self_interaction=False,
                                    bothways=True,
                                    primitive=NewPrimitiveNeighborList)
    neighborhood_old = NeighborList(radii,
                                    skin=0.0,
                                    self_interaction=False,
                                    bothways=True,
                                    primitive=PrimitiveNeighborList)

    neighborhood_new.update(atoms)
    neighborhood_old.update(atoms)

    n0, d0 = neighborhood_new.get_neighbors(0)
    n1, d1 = neighborhood_old.get_neighbors(0)

    assert np.all(n0 == n1)
    assert np.all(d0 == d1)
Esempio n. 6
0
def find_tmpo(atoms):
    """
    Args:
        atoms:
    """
    tmpo_indices = []
    p_index = None
    for a in atoms:
        if a.symbol == 'P':
            p_index = a.index
            break
    tmpo_indices.append(p_index)
    if p_index is None:
        return tmpo_indices
    nl = NeighborList(natural_cutoffs(atoms),
                      bothways=True,
                      self_interaction=False)
    nl.update(atoms)
    p_nl = nl.get_neighbors(p_index)[0]
    tmpo_indices.extend(p_nl)
    for i in p_nl:
        if atoms[i].symbol == 'C':
            tmpo_indices.extend(nl.get_neighbors(i)[0])

    return tmpo_indices
Esempio n. 7
0
def generate_site_type(atoms,
                       surface_mask,
                       normals,
                       coordination,
                       unallowed_elements=[]):
    cutoffs = natural_cutoffs(atoms)

    nl = NeighborList(cutoffs, self_interaction=False, bothways=True)
    nl.update(atoms)

    surface_mask = [
        index for index in surface_mask
        if atoms[index].symbol not in unallowed_elements
    ]

    possible = list(combinations(set(surface_mask), coordination))
    valid = []
    sites = []

    for cycle in possible:
        for start, end in combinations(cycle, 2):
            if end not in nl.get_neighbors(start)[0]:
                break
        else:  # All were valid
            valid.append(list(cycle))

    for cycle in valid:
        tracked = np.array(atoms[cycle[0]].position, dtype=float)
        known = np.zeros(shape=(coordination, 3), dtype=float)
        known[0] = tracked
        for index, (start, end) in enumerate(zip(cycle[:-1], cycle[1:])):
            for neighbor, offset in zip(*nl.get_neighbors(start)):
                if neighbor == end:
                    tracked += relative_position(
                        atoms, neighbor, offset) - atoms[start].position
                    known[index + 1] = tracked

        average = np.average(known, axis=0)

        normal = np.zeros(3)
        for index in cycle:
            neighbors = len(nl.get_neighbors(index)[0])**2
            normal += normals[index] * neighbors
        normal = normalize(normal)
        if coord == 2:
            average[2] = average[2] - 0.5
        if coord == 3:
            average[2] = average[2] - 0.7
            #print(average)
            #print(average[2])
        site_ads = Site(cycle=cycle, position=average, normal=normal)
        sites.append(site_ads)

    return sites
def test_neighborlist_initialization():

    atoms = bulk('Al', 'fcc', a=4)

    nl = NeighborList([8] * len(atoms), skin=0, self_interaction=False)
    with pytest.raises(Exception, match="Must call update"):
        nl.get_neighbors(0)

    with pytest.raises(Exception, match="Must call update"):
        nl.get_connectivity_matrix()

    nl.update(atoms)
    indices, offsets = nl.get_neighbors(0)
Esempio n. 9
0
def find_positions(atoms, n_site=0):

    nO = 10
    nl = NeighborList([3.3 / 2] * len(atoms),
                      self_interaction=False,
                      bothways=True)
    nl.update(atoms)
    com = atoms.get_center_of_mass()

    hollow = {}
    n_hollow = 0
    bridge = {}
    n_bridge = 0
    top = {}
    n_top = 0
    sites = {}
    for i in range(len(atoms)):
        i_indices, i_offsets = nl.get_neighbors(i)
        for j in i_indices:
            #if i < j:
            #   bpos = (atoms.positions[i]+atoms.positions[j]) / 2.0
            #   sites[n_site] = ['bridge',bpos, bpos-com]
            #   n_site += 1
            j_indices, j_offsets = nl.get_neighbors(j)
            c = common_member(i_indices, j_indices)
            if c is not None:
                for k in c:
                    k_indices, k_offsets = nl.get_neighbors(k)
                    if len(i_indices) < 10 and len(j_indices) < 10 and len(
                            k_indices) < 10 and i < j and j < k:
                        #n=n+1
                        pos = (atoms.positions[i] + atoms.positions[j] +
                               atoms.positions[k]) / 3.
                        v1 = atoms.positions[j] - atoms.positions[i]
                        v2 = atoms.positions[k] - atoms.positions[i]
                        vn = np.cross(v1, v2)
                        vn = vn / np.linalg.norm(vn)
                        vo = pos - com
                        d = np.dot(vo, vn)
                        sites[n_site] = ['hollow', pos, vn, d]
                        n_site += 1
    temp = copy.deepcopy(sites)
    """
    for i in range(n_site-1):
       for j in range(i+1,n_site):
          dist =np.linalg.norm(sites[i][1] - sites[j][1])
          if dist<0.5:
             print 'duplicated sites:', i, j, dist
             del temp[j]
    """
    return temp, n_site
Esempio n. 10
0
def is_connected(reference, indices, cutoff=graphene_cutoff):
    """Return True if indices in atoms are connected.

    Args:
        reference (Atoms or NeighborList): Structure containing atoms for test. The NeighborList must be constructed with bothways=True.
        indices (List[int]): Indices of the possibly connected atoms.
        cutoff (int): Radius defining neighbors in a NeighborList. Only relevent when reference is of the Atoms type.
    """
    if isinstance(reference, Atoms):
        nblist = NeighborList([cutoff for i in range(len(reference))],
                              bothways=True,
                              self_interaction=False)
        nblist.update(reference)
    else:
        nblist = reference

    if len(indices) == 0:
        return True

    connected = [indices[0]]
    for c in connected:
        neighbs = nblist.get_neighbors(c)[0]
        for n in neighbs:
            if n in indices and n not in connected:
                connected.append(n)

    return set(indices) == set(connected)
Esempio n. 11
0
    def get_all_Z_TM(self, d_Z_TM, TM_type):
        """
        :param d_Z_TM: Z-TM distance with Z being the T sites on the zeolite framework and TM being extraframework atom
        to be inserted
        :return: a dictionary of structures for each T site name
        """
        dict_Z_TM = {}
        for site_name, all_zeo_with_same_T in self.dict_1Al_replaced.items():
            atoms = copy.copy(all_zeo_with_same_T[0])
            nl = NeighborList(natural_cutoffs(atoms),
                              bothways=True,
                              self_interaction=False)
            nl.update(atoms)
            index_Al = [a.index for a in atoms if a.symbol == 'Al']
            indices, offsets = nl.get_neighbors(index_Al[0])
            assert len(indices) == 4

            traj = []
            pairs = list(itertools.combinations(indices, 2))
            for i, pair in enumerate(pairs):
                atoms = copy.copy(all_zeo_with_same_T[0])
                v = self._get_direction_of_insertion(atoms,
                                                     index_Al[0],
                                                     pair[0],
                                                     pair[1],
                                                     tag='TM')
                atoms = atoms + Atoms(
                    TM_type,
                    positions=[atoms[index_Al[0]].position] + v * d_Z_TM)
                traj.append(atoms)
            dict_Z_TM[site_name] = traj

        return dict_Z_TM
Esempio n. 12
0
    def get_Z_TM(self, atoms, d_Z_TM, TM_type):
        # todo: improve flexibility to allow Z-TM-H or Z-TM-OH insertions while avoid overlapping
        nl = NeighborList(natural_cutoffs(atoms),
                          bothways=True,
                          self_interaction=False)
        nl.update(atoms)
        index_Al = [a.index for a in atoms if a.symbol == 'Al']
        indices, offsets = nl.get_neighbors(index_Al[0])
        indices = [val for val in indices if atoms[val].symbol == 'O']
        assert len(indices) == 4

        dict_Z_TM = {}
        original_atoms = copy.copy(atoms)
        pairs = list(itertools.combinations(indices, 2))
        for i, pair in enumerate(pairs):
            atoms = copy.copy(original_atoms)
            v = self._get_direction_of_insertion(atoms,
                                                 index_Al[0],
                                                 pair[0],
                                                 pair[1],
                                                 tag='TM')
            atoms = atoms + Atoms(
                TM_type, positions=[atoms[index_Al[0]].position] + v * d_Z_TM)
            atoms.wrap()
            key_tag = 'O' + str(pair[0]) + '_O' + str(pair[1])
            dict_Z_TM[key_tag] = atoms

        return dict_Z_TM
Esempio n. 13
0
def randomize_biatom_13(atoms, type_a, type_b, ratio):
    """ replace randomly by clusters of 13 atoms
     to acheive target conc """
    n_A = 0
    n_B = 0
    for atom in atoms:
        if atom.symbol == type_a:
            n_A += 1
        elif atom.symbol == type_b:
            n_B += 1
        else:
            raise Exception('Extra chemical element %s!'%atom.symbol)
    #print n_A, n_B
    N = len(atoms)
    nl = NeighborList([1.5]*N, self_interaction=False, bothways=True)  # 2*1.5=3 Angstr. radius
    nl.build(atoms)
    #print "conc",  n_A *1.0 / N
    r = random.Random()
    while n_A < ratio*N:  # add A atoms randomly
        index = r.randint(0, N-1)
        if (atoms[index].symbol != type_a):
            #print "changing atom #"+str(index)+" to "+type_a
            #if (r.randint(0, 1000) < 500):
            atoms[index].symbol = type_a
            n_A += 1
            indeces, offsets = nl.get_neighbors(index)
            for ia in indeces :
                if (atoms[ia].symbol != type_a)&(n_A < ratio*N):
                    atoms[ia].symbol = type_a
                    n_A += 1
    return atoms
Esempio n. 14
0
    def test0(self):
        import warnings
        warnings.filterwarnings('ignore')

        a = 3.6
        Rc = 5
        atoms = bulk('Cu', 'bcc', a=a).repeat((1, 1, 1))
        atoms.rattle(0.02)
        nl = NeighborList([Rc] * len(atoms),
                          skin=0.0,
                          self_interaction=False,
                          bothways=False)
        nl.update(atoms)

        inds, dists, N = get_neighbors_oneway(atoms.positions,
                                              atoms.cell,
                                              2 * Rc,
                                              skin=0.0)

        with self.test_session() as sess:
            inds, dists, N = sess.run([inds, dists, N])

            for i in range(len(atoms)):
                ase_inds, ase_offs = nl.get_neighbors(i)

                these_inds = np.array([x[1] for x in inds if x[0] == i])
                these_offs = N[np.where(inds[:, 0] == i)]

                self.assertAllClose(ase_inds, these_inds)
                self.assertAllClose(ase_offs, these_offs)
Esempio n. 15
0
    def test_structure_repeats(self):
        'Check several structures and repeats for consistency with ase.'
        for repeat in ((1, 1, 1), (2, 1, 1), (1, 2, 1), (1, 1, 2), (1, 2, 3),
                       (4, 1, 1)):
            for structure in ('fcc', 'bcc', 'sc', 'hcp', 'diamond'):
                a = 3.6
                # Float tolerances are tricky. The 0.01 in the next line is important.
                # This test fails without it due to subtle differences in computed
                # positions.
                Rc = 2 * a + 0.01
                atoms = bulk('Cu', structure, a=a).repeat(repeat)
                nl = NeighborList([Rc] * len(atoms),
                                  skin=0.0,
                                  self_interaction=False,
                                  bothways=True)
                nl.update(atoms)
                distances = get_distances({'cutoff_radius': 2 * Rc},
                                          atoms.positions, atoms.cell,
                                          np.ones((len(atoms), 1)))

                mask = (distances <= 2 * Rc) & (distances > 0)
                tf_nneighbors = tf.reduce_sum(tf.cast(mask, tf.int32),
                                              axis=[1, 2])

                with self.test_session():
                    for i, atom in enumerate(atoms):
                        inds, disps = nl.get_neighbors(i)
                        ase_nneighbors = len(inds)
                        self.assertEqual(ase_nneighbors,
                                         tf_nneighbors.eval()[i])

                        # These are the indices of each neighbor in the atom list.
                        tf_inds = tf.where(mask[i])[:, 0].eval()
                        self.assertCountEqual(inds, tf_inds)
Esempio n. 16
0
    def test_atom_types(self):
        """Tests if the neighbor indices agree with ase.

    This is important to find the
    chemical element associated with a specific neighbor.

    """
        a = 3.6
        Rc = a / np.sqrt(2) / 2 + 0.01

        atoms = bulk('Cu', 'fcc', a=a).repeat((3, 1, 1))
        atoms[1].symbol = 'Au'

        nl = NeighborList([Rc] * len(atoms),
                          skin=0.01,
                          self_interaction=False,
                          bothways=True)
        nl.update(atoms)
        nns = [nl.get_neighbors(i) for i in range(len(atoms))]
        ase_nau = [np.sum(atoms.numbers[inds] == 79) for inds, offs in nns]

        au_mask = tf.convert_to_tensor(atoms.numbers == 79, tf.int32)

        distances = get_distances({'cutoff_radius': 2 * Rc}, atoms.positions,
                                  atoms.cell)
        mask = (distances <= (2 * Rc)) & (distances > 0)

        nau = tf.reduce_sum(tf.cast(mask, tf.int32) * au_mask[:, None], [1, 2])

        with self.test_session():
            self.assertTrue(np.all(ase_nau == nau.eval()))
Esempio n. 17
0
def main():
    arg = sys.argv
    atoms = read(filename=arg[1], format='xyz')
    cutoff = []
    coord_n = []
    n_pt = 0
    n_au = 0
    for i in range(len(atoms)):
        cutoff.append(float(arg[2]))
    nl = NeighborList(cutoff, skin=0, bothways=True, self_interaction=False)
    nl.update(atoms)
    for i in range(len(atoms)):
        indices, offsets = nl.get_neighbors(i)
        coord_n.append([i, len(indices)])
        #coord_n[i]=len(indices)
        #coord_n.append(len(indices))
        if len(indices) <= 9:
            if atoms[i].symbol == 'Pt':
                n_pt += 1
            if atoms[i].symbol == 'Au':
                n_au += 1
            atoms[i].symbol = 'Cu'
        if len(indices) == 10:
            atoms[i].symbol = 'Pd'
    write('new.xyz', images=atoms, format='xyz')
    print float(n_pt) / float(n_pt + n_au), float(n_au) / float(n_pt + n_au)
Esempio n. 18
0
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.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 test0(self):
    """check one-way neighborlist for fcc on different repeats."""
    a = 3.6
    for rep in ((1, 1, 1), (2, 1, 1), (1, 2, 1), (1, 1, 2), (1, 2, 2),
                (2, 1, 1), (2, 2, 1), (2, 2, 2), (1, 2, 3), (4, 1, 1)):
      for cutoff_radius in np.linspace(a / 2.1, 5 * a, 5):
        atoms = bulk('Cu', 'fcc', a=a).repeat(rep)
        # It is important to rattle the atoms off the lattice points.
        # Otherwise, float tolerances makes it hard to count correctly.
        atoms.rattle(0.02)
        nl = NeighborList(
            [cutoff_radius / 2] * len(atoms),
            skin=0.0,
            self_interaction=False,
            bothways=False)
        nl.update(atoms)

        neighbors, displacements = get_neighbors_oneway(
            atoms.positions, atoms.cell, cutoff_radius, skin=0.0)

        for i in range(len(atoms)):
          an, ad = nl.get_neighbors(i)
          # Check the same number of neighbors
          self.assertEqual(len(neighbors[i]), len(an))
          # Check the same indices
          self.assertCountEqual(neighbors[i], an)
Esempio n. 20
0
def main():
    imgs = read('train.traj', index='0::10', format='traj')
    natoms = len(imgs[0])
    cutoff = 3.3
    mindist = 2.7

    traj = Trajectory('traj.traj', 'w')

    for atoms in imgs:
        nl = NeighborList([cutoff / 2] * len(atoms),
                          self_interaction=False,
                          bothways=False)
        nl.update(atoms)
        neighbor_info = {}
        n_pairs = 0
        for i in range(natoms):
            i_indices, i_offsets = nl.get_neighbors(i)
            neighbor_info[i] = i_indices
            n_pairs += len(i_indices)
            print len(i_indices)
        #randomvalues=np.random.random((n_pairs,)) + mindist
        randomvalues = np.random.normal(mindist, 0.5, (n_pairs, ))

        pullcloser = {}
        pointer = 0
        for key in neighbor_info.keys():
            pullcloser[key] = randomvalues[pointer:len(neighbor_info[key]) +
                                           pointer:1]
            pointer += len(neighbor_info[key])

        atoms.set_positions(
            pull_closer(atoms.get_positions(), neighbor_info, pullcloser))
        #write('CONTCAR',atoms,format='vasp')
        traj.write(atoms)
Esempio n. 21
0
def compute_bonds(atoms, atom_radii, scale_radii=1.5):
    """Compute bonds for atoms."""
    from ase.neighborlist import NeighborList

    nl = NeighborList(atom_radii * scale_radii, skin=0, self_interaction=False)
    nl.update(atoms)
    nbonds = nl.nneighbors + nl.npbcneighbors

    bonds = np.empty((nbonds, 5), int)
    if nbonds == 0:
        return bonds

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

    i = bonds[:n2, 2:].any(1)
    pbc_bonds = bonds[:n2][i]
    bonds[n2:, 0] = pbc_bonds[:, 1]
    bonds[n2:, 1] = pbc_bonds[:, 0]
    bonds[n2:, 2:] = -pbc_bonds[:, 2:]
    return bonds
Esempio n. 22
0
def ase_neighborlist(atoms, cutoffs=None):
    """Make dict of neighboring atoms using ase function.

    This provides a wrapper for the ASE neighborlist generator. Currently
    default values are used.

    Parameters
    ----------
    atoms : object
        Target ase atoms object on which to get neighbor list.
    cutoffs : list
        A list of radii for each atom in atoms.
    rtol : float
        The tolerance factor to allow for small variation in the cutoff radii.

    Returns
    -------
    neighborlist : dict
        A dictionary containing the atom index and each neighbor index.
    """
    if cutoffs is None:
        cutoffs = [get_radius(a.number) for a in atoms]
    nl = NeighborList(
        cutoffs, skin=0., sorted=False, self_interaction=False,
        bothways=True)

    nl.update(atoms)

    neighborlist = {}
    for i, _ in enumerate(atoms):
        neighborlist[i] = sorted(list(map(int, nl.get_neighbors(i)[0])))

    return neighborlist
Esempio n. 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!")
Esempio n. 24
0
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.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 all_connected_to(id_atom, atoms, exclude):
    cov_radii = [covalent_radii[a.number] for a in atoms]

    atoms.set_pbc([False, False, False])
    nl_no_pbc = NeighborList(cov_radii, bothways=True, self_interaction=False)
    nl_no_pbc.update(atoms)
    atoms.set_pbc([True, True, True])

    tofollow = []
    followed = []
    isconnected = []
    tofollow.append(id_atom)
    isconnected.append(id_atom)
    while len(tofollow) > 0:
        indices, offsets = nl_no_pbc.get_neighbors(tofollow[0])
        indices = list(indices)
        followed.append(tofollow[0])
        for i in indices:
            if (i not in isconnected) and (atoms[i].symbol not in exclude):
                tofollow.append(i)
                isconnected.append(i)
        for i in followed:
            if i in tofollow:  ### do not remove this check
                tofollow.remove(i)
            #try:
            #    tofollow.remove(i)
            #except:
            #    pass
            #

    return isconnected
Esempio n. 26
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.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)
        self.coordination = np.zeros((self.images.natoms), dtype=int)
        if nb == 0:
            return

        n1 = 0
        for a in range(self.images.natoms):
            indices, offsets = nl.get_neighbors(a)
            self.coordination[a] += len(indices)
            for a2 in indices:
                self.coordination[a2] += 1
            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:]
Esempio n. 27
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.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)
        self.coordination = np.zeros((self.images.natoms), dtype=int)
        if nb == 0:
            return
        
        n1 = 0
        for a in range(self.images.natoms):
            indices, offsets = nl.get_neighbors(a)
            self.coordination[a] += len(indices)
            for a2 in indices:
                self.coordination[a2] += 1
            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:]
Esempio n. 28
0
def get_bonds(atoms, covalent_radii):
    from ase.neighborlist import NeighborList
    nl = NeighborList(covalent_radii * 1.5,
                      skin=0, self_interaction=False)
    nl.update(atoms)
    nbonds = nl.nneighbors + nl.npbcneighbors

    bonds = np.empty((nbonds, 5), int)
    if nbonds == 0:
        return bonds

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

    i = bonds[:n2, 2:].any(1)
    pbcbonds = bonds[:n2][i]
    bonds[n2:, 0] = pbcbonds[:, 1]
    bonds[n2:, 1] = pbcbonds[:, 0]
    bonds[n2:, 2:] = -pbcbonds[:, 2:]
    return bonds
Esempio n. 29
0
def generate_normals(atoms,
                     surface_normal=0.5,
                     normalize_final=True,
                     adsorbate_atoms=[]):
    normals = np.zeros(shape=(len(atoms), 3), dtype=float)

    atoms = atoms.copy()

    del atoms[adsorbate_atoms]

    cutoffs = natural_cutoffs(atoms)

    nl = NeighborList(cutoffs, self_interaction=False, bothways=True)
    nl.update(atoms)

    cell = atoms.get_cell()

    for index, atom in enumerate(atoms):
        normal = np.array([0, 0, 0], dtype=float)
        for neighbor, offset in zip(*nl.get_neighbors(index)):
            direction = atom.position - relative_position(
                atoms, neighbor, offset)
            normal += direction
        if norm(normal) > surface_normal:
            normals[index, :] = normalize(
                normal) if normalize_final else normal

    surface_mask = [
        index for index in range(len(atoms)) if norm(normals[index]) > 1e-5
    ]

    return normals, surface_mask
Esempio n. 30
0
def find_layers(atoms):
    cutoff = 3.2

    nlayer = 0
    layers = {}
    while True:
       if len(atoms) == 0:
          break
       nl=NeighborList([cutoff/2]*len(atoms), self_interaction=False, bothways=True)
       nl.update(atoms)
       com=atoms.get_center_of_mass()
       core = []
       for i in range(len(atoms)):
          #first_layer (most outer layer)
          i_indices, i_offsets = nl.get_neighbors(i)
          if i==13 or i==15 or i==3:
             print i, len(i_indices)
          if len(i_indices) < 10:
             if nlayer not in layers.keys():
                layers[nlayer] = [i] 
             else:
                layers[nlayer].append(i)
          else:
            core.append(i)
       #layers[nlayer].sort(reverse=True) 
       shell = atoms.copy()
       del shell[core]
       del atoms[layers[nlayer]]
       write('shell_'+str(nlayer)+'.xyz',shell,format='xyz')
       write('core_'+str(nlayer)+'.xyz',atoms,format='xyz')
       layers[nlayer].append(shell.copy())
       print nlayer
       print "     ",layers[nlayer]
       nlayer += 1
    return layers
Esempio n. 31
0
def main():
    args = sys.argv
    imgs = read(args[1], index="::40")
    #layers = find_layers(atoms.copy())
    traj = Trajectory('traj.traj','w')

    H_indices = random.sample([a.index for a in imgs[0] if a.symbol == 'H'],8)

    n_img = 0
    for atoms in imgs:
       nl=NeighborList([2.5/2]*len(atoms), self_interaction=False, bothways=True)
       nl.update(atoms)
       pair_selected = []
       for H_index in H_indices:
         nl_indices, nl_offsets = nl.get_neighbors(H_index)
         pair_selected.append([H_index, random.sample(nl_indices, 1)[0]])
       for HPd_dist in [1.0, 1.1, 1.2, 1.3, 1.4]:
          img = atoms.copy()
          for pair in pair_selected:
            H_index = pair[0]
            Pd_selected = pair[1]
            v = atoms[H_index].position - atoms[Pd_selected].position
            vn = v/np.linalg.norm(v)
            del img[H_index]
            img.append(Atom('H',atoms[Pd_selected].position + vn * HPd_dist))
          traj.write(img)
          print n_img
          n_img+=1
Esempio n. 32
0
    def get_donor_vec(self, donor_index):
        """finds direction of lone pair electrons on an adsorbate donor atom
        :return: vector direction of donor atoms

        Args:
            donor_index:
        """
        nl = NeighborList(natural_cutoffs(self),
                          self_interaction=False,
                          bothways=True)
        nl.update(self)
        # gets neighbors of donor atom and adds the vectors from neighbor to donor
        # for most donor atoms this is roughly in the proper binding direction
        donor_neighbors = nl.get_neighbors(donor_index)[0]  # neighbor's index
        donor_vec = np.array([0, 0, 0])
        for i in donor_neighbors:
            a = self.get_distance(i, donor_index, vector=True)
            donor_vec = donor_vec + a
        if np.linalg.norm(donor_vec) == 0:
            warnings.warn(
                "donor vector with magnitude 0 found, providing default  vector"
            )
            return np.array([1, 0, 0])

        donor_vec = donor_vec / np.linalg.norm(
            donor_vec)  # normalizes donor vec
        return donor_vec
Esempio n. 33
0
File: view.py Progetto: btodac/ase
def get_bonds(atoms, covalent_radii):
    from ase.neighborlist import NeighborList
    nl = NeighborList(covalent_radii * 1.5, skin=0, self_interaction=False)
    nl.update(atoms)
    nbonds = nl.nneighbors + nl.npbcneighbors

    bonds = np.empty((nbonds, 5), int)
    if nbonds == 0:
        return bonds

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

    i = bonds[:n2, 2:].any(1)
    pbcbonds = bonds[:n2][i]
    bonds[n2:, 0] = pbcbonds[:, 1]
    bonds[n2:, 1] = pbcbonds[:, 0]
    bonds[n2:, 2:] = -pbcbonds[:, 2:]
    return bonds
Esempio n. 34
0
def get_external_internal(atoms, symbols=None):

    # Atoms to include in external and internal indices
    if symbols is None:
        symbols = list(set(atoms.symbols))
        try:
            symbols.pop(symbols.index('H'))
        except ValueError as e:
            print(e)
    #
    symbols = list(symbols)

    # Define list of neighbors
    cov_radii = [covalent_radii[a.number] for a in atoms]
    nl = NeighborList(cov_radii, bothways=True, self_interaction=False)
    nl.update(atoms)

    external_i = []
    internal_i = []
    for a in atoms:
        if a.symbol not in symbols:
            continue
        nlist = nl.get_neighbors(a.index)[0]
        n_is_H = [True if atoms[n0].symbol == 'H' else False for n0 in nlist]
        if any(n_is_H):
            external_i.append(a.index)
        else:
            internal_i.append(a.index)

    return external_i, internal_i
Esempio n. 35
0
 def get_color_scalars(self, frame=None):
     if self.colormode == 'tag':
         return self.atoms.get_tags()
     if self.colormode == 'force':
         f = (self.get_forces()**2).sum(1)**0.5
         return f * self.images.get_dynamic(self.atoms)
     elif self.colormode == 'velocity':
         return (self.atoms.get_velocities()**2).sum(1)**0.5
     elif self.colormode == 'initial charge':
         return self.atoms.get_initial_charges()
     elif self.colormode == 'magmom':
         return get_magmoms(self.atoms)
     elif self.colormode == 'neighbors':
         from ase.neighborlist import NeighborList
         n = len(self.atoms)
         nl = NeighborList(self.get_covalent_radii(self.atoms) * 1.5,
                           skin=0,
                           self_interaction=False,
                           bothways=True)
         nl.update(self.atoms)
         return [len(nl.get_neighbors(i)[0]) for i in range(n)]
     else:
         scalars = np.array(self.atoms.get_array(self.colormode),
                            dtype=float)
         return np.ma.array(scalars, mask=np.isnan(scalars))
Esempio n. 36
0
def exafs_first_shell(S02, energy_shift, absorber,
    ignore_elements, edge, neighbor_cutoff, trajectory):
    feff_options = {
            'RMAX':str(neighbor_cutoff),
            'HOLE':'%i %.4f' % (feff_edge_number(edge), S02),
            'CORRECTIONS':'%.4f %.4f' % (energy_shift, 0.0),
    }

    #get the bulk reference state
    path = exafs_reference_path(absorber, feff_options)

    k = None
    chi_total = None

    counter = -1
    interactions = 0
    nl = None

    for step, atoms in enumerate(trajectory):
        if COMM_WORLD.rank == 0:
            time_stamp = strftime("%F %T")
            print '[%s] step %i/%i' % (time_stamp, step+1, len(trajectory))
        atoms = atoms.copy()
        if ignore_elements:
            ignore_indicies = [atom.index for atom in atoms
                               if atom.symbol in ignore_elements]
            del atoms[ignore_indicies]
        if nl is None:
            nl = NeighborList(len(atoms)*[neighbor_cutoff], skin=0.3,
                    self_interaction=False)
        nl.update(atoms)

        for i in xrange(len(atoms)):
            if atoms[i].symbol != absorber:
                continue
            indicies, offsets = nl.get_neighbors(i)
            for j, offset in zip(indicies, offsets):
                counter += 1
                if counter % COMM_WORLD.size != COMM_WORLD.rank:
                    continue

                r = atoms.get_distance(i,j,True)
                if r >= neighbor_cutoff: continue
                interactions += 1
                k, chi = chi_path(path, r, 0.0, energy_shift, S02, 1)

                if chi_total is not None:
                    chi_total += chi
                else:
                    chi_total = chi
    chi_total = COMM_WORLD.allreduce(chi_total)
    chi_total /= atoms.get_chemical_symbols().count(absorber)
    chi_total /= len(trajectory)
    chi_total *= 2
    return k, chi_total
Esempio n. 37
0
def hop_shuffle(atoms, A, B, count=10, R=3.0):
    """
    Shuffle atoms in given structure
    by swapping atom types within first coordination shell

    Parameters
    ----------
    atoms: ase.Atoms
        ase Atoms object, containing atomic cluster.
    A, B: string
        symbols of atoms to swap
    count: integer
        number of shuffles
    R: float
        radius of coordination shell, were atoms will be swapped

    Returns
    -------
        Function returns ASE atoms object whith
        shuffled atoms
    """
    n_atoms = len(atoms)
    nswaps = 0
    neiblist = NeighborList( [ R ] * n_atoms,
                             self_interaction=False,
                             bothways=True )
    neiblist.build( atoms )
    rnd = random.Random()
    while nswaps < count:
        i = rnd.randint(0, n_atoms-1)
        indeces, offsets = neiblist.get_neighbors( i )
        if (atoms[i].symbol == B):
            candidates = []
            for ii in indeces:
                if atoms[ii].symbol == A:
                    candidates.append( ii )
            if len(candidates) > 0:
                j = random.choice(candidates)
                atoms[i].symbol = A
                atoms[j].symbol = B
                nswaps += 1
                neiblist.build( atoms )
        elif (atoms[i].symbol == B):
            candidates = []
            for ii in indeces:
                if atoms[ii].symbol == A:
                    candidates.append( ii )
            if len(candidates) > 0:
                j = random.choice(candidates)
                atoms[i].symbol = B
                atoms[j].symbol = A
                nswaps += 1
                neiblist.build( atoms )
    return atoms
Esempio n. 38
0
                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)])
nl = NeighborList([0.5, 0.5], skin=0.1, sorted=True, self_interaction=False)
assert nl.update(h2)
assert not nl.update(h2)
assert (nl.get_neighbors(0)[0] == [1]).all()

h2[1].z += 0.09
assert not nl.update(h2)
assert (nl.get_neighbors(0)[0] == [1]).all()

h2[1].z += 0.09
assert nl.update(h2)
assert (nl.get_neighbors(0)[0] == []).all()
assert nl.nupdates == 2

h2 = Atoms('H2', positions=[(0, 0, 0), (0, 0, 1)])
nl = NeighborList([0.1, 0.1], skin=0.1, bothways=True, self_interaction=False)
assert nl.update(h2)
assert nl.get_neighbors(0)[1].shape == (0, 3)
assert nl.get_neighbors(0)[1].dtype == int
Esempio n. 39
0
class LennardJones(Calculator):
    implemented_properties = ['energy', 'forces', 'stress']
    default_parameters = {'epsilon': 1.0,
                          'sigma': 1.0,
                          'rc': None}
    nolabel = True

    def __init__(self, **kwargs):
        Calculator.__init__(self, **kwargs)

    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
            forces[a1] -= f.sum(axis=0)
            for a2, f2 in zip(neighbors, f):
                forces[a2] += f2
            stress += np.dot(f.T, d)

        if 'stress' in properties:
            if self.atoms.number_of_lattice_vectors == 3:
                stress += stress.T.copy()
                stress *= -0.5 / self.atoms.get_volume()
                self.results['stress'] = stress.flat[[0, 4, 8, 5, 2, 1]]
            else:
                raise PropertyNotImplementedError

        self.results['energy'] = energy
        self.results['free_energy'] = energy
        self.results['forces'] = forces
Esempio n. 40
0
def CoreShellFCC(atoms, type_a, type_b, ratio, a_cell, n_depth=-1):
    r"""This routine generates cluster with ideal core-shell architecture,
    so that atoms of type_a are placed on the surface
    and atoms of type_b are forming the core of nanoparticle.
    The 'surface' of nanoparticle is defined as atoms
    with unfinished coordination shell.

    Parameters
    ----------
    atoms: ase.Atoms
        ase Atoms object, containing atomic cluster.
    type_a: string
        Symbol of chemical element to be placed on the shell.
    type_b: string
        Symbol of chemical element to be placed in the core.
    ratio: float
        Guards the number of shell atoms, type_a:type_b = ratio:(1-ratio)
    a_cell: float
        Parameter of FCC cell, in Angstrom.
        Required for calculation of neighbor distances in for infinite
        crystal.
    n_depth: int
        Number of layers of the shell formed by atoms ratio.
        Default value -1 is ignored and n_depth is calculated according
        ratio. If n_depth is set then value of ratio is ignored.

    Returns
    -------
        Function returns ASE atoms object which
        contains bimetallic core-shell cluster

    Notes
    -----
        The criterion of the atom beeing on the surface is incompletnes
        of it's coordination shell. For the most outer atoms the first
        coordination shell will be incomplete (coordination number
        is less then 12 for FCC), for the second layer --
        second coordination shell( CN1 + CN2 < 12 + 6) and so on.
        In this algorithm each layer is tagged by the number
        ('depth'), take care if used with other routines
        dealing with tags (add_adsorbate etc).

        First, atoms with unfinished first shell are replaced
        by atoms type_a, then second, and so on.
        The last depth surface layer is replaced by random
        to maintain given ratio value.

    Example
    --------
    >>> atoms = FaceCenteredCubic('Ag',
      [(1, 0, 0), (1, 1, 0), (1, 1, 1)], [7,8,7], 4.09)
    >>> atoms = CoreShellFCC(atoms, 'Pt', 'Ag', 0.6, 4.09)
    >>> view(atoms)
    """
    # 0 < ratio < 1
    target_x = ratio
    if n_depth != -1:
        target_x = 1  # needed to label all needed layeres

    def fill_by_tag(atoms, chems, tag):
        """Replaces all atoms within selected layer"""
        for i in xrange(0, len(atoms)):
            if atoms[i].tag == tag:
                chems[i] = type_a
        return
    # coord numbers for FCC:
    coord_nums = [1, 12, 6, 24, 12, 24, 8, 48, 6, 36, 24, 24, 24, 72, 48,
    12, 48, 30, 72, 24]
    # coordination radii obtained from this array as R = sqrt(coord_radii)*a/2
    coord_radii = [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 30,
    32, 34, 36, 38, 40]

    ## generate FCC cluster ##
    #atoms = FaceCenteredCubic(type_b, surfaces, layers, a_cell)
    n_atoms = len(atoms)
    ## tag layers ##
    positions = [0]  # number of positions in layer
    n_tag = 0    # number of tags to check if there is enought layers
    n_shell = 0  # depth of the shell
    while (n_tag < n_atoms * target_x):
        n_shell += 1
        if (n_depth != -1)and(n_shell > n_depth):
            break
        neiblist = NeighborList(
          [
            a_cell / 2.0 * sqrt(coord_radii[n_shell]) / 2.0 + 0.0001
          ] * n_atoms,
          self_interaction=False, bothways=True
        )
        neiblist.build(atoms)
        for i in xrange(0, n_atoms):
            indeces, offsets = neiblist.get_neighbors(i)
            if (atoms[i].tag == 0):
                if (len(indeces) < sum(coord_nums[1:n_shell + 1])):
                    # coord shell is not full -> atom is on surface!
                    atoms[i].tag = n_shell
                    n_tag += 1
        # save the count of positions at each layer:
        positions.append(n_tag - sum(positions[0:n_shell]))
    ## populate layers ##
    chems = atoms.get_chemical_symbols()
    n_type_a = 0  # number of changes B -> A
    if (n_tag < n_atoms * target_x)and(n_depth == -1):
        # raise exception?
        return None
    else:
        n_filled = n_shell - 1  # number of totally filled layers
        ilayer = 1
        while (ilayer < n_filled + 1):
            fill_by_tag(atoms, chems, ilayer)
            n_type_a += positions[ilayer]
            ilayer += 1
        while (n_type_a < n_atoms * target_x)and(n_depth == -1):
            i = random.randint(0, n_atoms - 1)
            if (atoms[i].tag == n_shell):
                if (chems[i] == type_b):
                    chems[i] = type_a
                    n_type_a += 1
    atoms.set_chemical_symbols(chems)
    ## check number of atoms ##
    checkn_a = 0
    for element in chems:
        if element == type_a:
            checkn_a += 1
    assert n_type_a == checkn_a
    return atoms
Esempio n. 41
0
class EMT(Calculator):
    """Python implementation of the Effective Medium Potential.

    Supports the following standard EMT metals:
    Al, Cu, Ag, Au, Ni, Pd and Pt.

    In addition, the following elements are supported.
    They are NOT well described by EMT, and the parameters
    are not for any serious use:
    H, C, N, O

    The potential takes a single argument, ``asap_cutoff``
    (default: False).  If set to True, the cutoff mimics
    how Asap does it; most importantly the global cutoff
    is chosen from the largest atom present in the simulation,
    if False it is chosen from the largest atom in the parameter
    table.  True gives the behaviour of the Asap code and
    older EMT implementations, although the results are not
    bitwise identical.
    """
    implemented_properties = ['energy', 'forces']

    nolabel = True

    default_parameters = {'asap_cutoff': False}

    def __init__(self, **kwargs):
        Calculator.__init__(self, **kwargs)

    def initialize(self, atoms):
        self.par = {}
        self.rc = 0.0
        self.numbers = atoms.get_atomic_numbers()
        if self.parameters.asap_cutoff:
            relevant_pars = {}
            for symb, p in parameters.items():
                if atomic_numbers[symb] in self.numbers:
                    relevant_pars[symb] = p
        else:
            relevant_pars = parameters
        maxseq = max(par[1] for par in relevant_pars.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)
        if self.parameters.asap_cutoff:
            self.rc_list = self.rc * 1.045
        else:
            self.rc_list = self.rc + 0.5
        for Z in self.numbers:
            if Z not in self.par:
                sym = chemical_symbols[Z]
                if sym not in parameters:
                    raise NotImplementedError('No EMT-potential for {0}'
                                              .format(sym))
                p = parameters[sym]
                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}

        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']

        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_list] * 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_list:
                    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_list:
                    Z2 = numbers[a2]
                    p2 = self.par[Z2]
                    self.interact2(a1, a2, d, r, p1, p2, ksi[Z2])

        self.results['energy'] = self.energy
        self.results['free_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
Esempio n. 42
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_\text{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_\text{tot} = \sum_i F(\bar\rho_i) + \frac{1}{2}\sum_{i\ne j} \phi(r_{ij})
   + \frac{1}{2} \sum_{i,\alpha} (\mu_i^\alpha)^2
   + \frac{1}{2} \sum_{i,\alpha,\beta} (\lambda_i^{\alpha\beta})^2
   - \frac{1}{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=[b'EAM/ADP potential file\n',
                b'Generated from eam.py\n',
                b'blank\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.items():
            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'
        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, basestring):
            f = open(fileobj)
            self.set_form(fileobj)
        else:
            f = fileobj
            
        def lines_to_list(lines):
            """Make the data one long line so as not to care how its formatted
            """
            data = []
            for line in lines:
                data.extend(line.split())
            return data

        lines = f.readlines()
        if self.form == 'eam':        # single element eam file (aka funcfl)
                self.header = lines[:1]
                
                data = lines_to_list(lines[1:])
                
                # eam form is just like an alloy form for one element
                
                self.Nelements = 1
                self.Z = np.array([data[0]], dtype=int)
                self.mass = np.array([data[1]])
                self.a = np.array([data[2]])
                self.lattice = [data[3]]
                 
                self.nrho = int(data[4])
                self.drho = float(data[5])
                self.nr = int(data[6])
                self.dr = float(data[7])
                self.cutoff = float(data[8])
                
                n = 9 + self.nrho
                self.embedded_data = np.array([np.float_(data[9:n])])
                
                self.rphi_data = np.zeros([self.Nelements, self.Nelements,
                                           self.nr])
                                
                effective_charge = np.float_(data[n:n + self.nr])
                # convert effective charges to rphi according to
                # http://lammps.sandia.gov/doc/pair_eam.html
                self.rphi_data[0, 0] = Bohr * Hartree * (effective_charge**2)
                
                self.density_data = np.array(
                    [np.float_(data[n + self.nr:n + 2 * self.nr])])
                
        else:
                self.header = lines[:3]
                i = 3

                data = lines_to_list(lines[i:])

                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):
                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 symmetrical 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 symmetrical 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, 'wb')

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

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

        f.write('{0} '.format(self.Nelements).encode())
        f.write(' '.join(self.elements).encode() + b'\n')

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

        # 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]))).encode())
            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()
Esempio n. 43
0
def get_neighbours(atoms, r_cut, self_interaction=False):
    """Return a list of pairs of atoms within a given distance of each other.

    If matscipy can be imported, then this will directly call matscipy's
    neighbourlist function. Otherwise it will use ASE's NeighborList object.

    Args:
        atoms: ase.atoms object to calculate neighbours for
        r_cut: cutoff radius (float). Pairs of atoms are considered neighbours
            if they are within a distance r_cut of each other (note that this
            is double the parameter used in the ASE's neighborlist module)

    Returns: a tuple (i_list, j_list, d_list, fixed_atoms):
        i_list, j_list: i and j indices of each neighbour pair
        d_list: absolute distance between the corresponding pair
        fixed_atoms: indices of any fixed atoms
    """

    if isinstance(atoms, Filter):
        atoms = atoms.atoms

    if have_matscipy:
        i_list, j_list, d_list = neighbour_list('ijd', atoms, r_cut)
    else:

        radii = [r_cut / 2 for i in range(len(atoms))]
        nl = NeighborList(radii,
                          sorted=False,
                          self_interaction=False,
                          bothways=True)
        nl.update(atoms)
        i_list = []
        j_list = []
        d_list = []

        for i, atom in enumerate(atoms):
            posn_i = atom.position
            indices, offsets = nl.get_neighbors(i)
            assert len(indices) == len(offsets)
            for j, offset in zip(indices, offsets):
                # Offsets represent how far away an atom is from its pair in terms
                # of the repeating cell - for instance, an atom i might be in cell
                # (0, 0, 0) while the neighbouring atom j is in cell (0, 1, 1). To
                # get the true position we have to correct for the offset:
                posn_j = atoms.positions[j] + np.dot(offset, atoms.get_cell())
                distance = np.sqrt(((posn_j - posn_i)**2).sum())
                i_list.append(i)
                j_list.append(j)
                d_list.append(distance)

        i_list = np.array(i_list)
        j_list = np.array(j_list)
        d_list = np.array(d_list)

    # filter out self-interactions (across PBC)
    if not self_interaction:
        mask = i_list != j_list
        i_list = i_list[mask]
        j_list = j_list[mask]
        d_list = d_list[mask]

    # filter out bonds where 1st atom (i) in pair is fixed
    fixed_atoms = []
    for constraint in atoms.constraints:
        if isinstance(constraint, FixAtoms):
            fixed_atoms.extend(list(constraint.index))
        else:
            raise TypeError(
                'only FixAtoms constraints are supported by Precon class')

    return i_list, j_list, d_list, fixed_atoms
Esempio n. 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')
Esempio n. 45
0
def CoreShellCN(atoms, type_a, type_b, ratio, R_min = 1.5, CN_max=12, n_depth=-1):
    r"""This routine generates cluster with ideal core-shell architecture,
    so that atoms of type_a are placed on the surface
    and atoms of type_b are forming the core of nanoparticle.
    The 'surface' of nanoparticle is defined as atoms
    with unfinished first coordination shell.
      Used algorithm without need for explicit knowledge of
    far coordination shells parameters, as it was required in CoreShellFCC(..)

    Parameters
    ----------
    atoms: ase.Atoms
        ase Atoms object, containing atomic cluster.
    type_a: string
        Symbol of chemical element to be placed on the shell.
    type_b: string
        Symbol of chemical element to be placed in the core.
    ratio: float
        Guards the number of shell atoms, type_a:type_b = ratio:(1-ratio)
    R_min: float
        Typical bond length. Neighboring atoms within this value are counted
        as coordination numbers.
        Default is 3.0.
    CN_max: float
        Maximum possible coordination number (bulk coordination number).
        Default is 12.
    n_depth: int
        Number of layers of the shell formed by atoms ratio.
        Default value -1 is ignored and n_depth is calculated according
        ratio. If n_depth is set then value of ratio is ignored.

    Returns
    -------
        Function returns ASE atoms object which
        contains bimetallic core-shell cluster

    Example
    --------
    >>> atoms = FaceCenteredCubic('Ag',
      [(1, 0, 0), (1, 1, 0), (1, 1, 1)], [7,8,7], 4.09)
    >>> atoms = CoreShellCN(atoms, 'Pt', 'Ag', 0.5)
    >>> view(atoms)
    """
    # 0 < ratio < 1
    target_x = ratio
    if n_depth != -1:
        target_x = 1

    n_atoms = len(atoms)
    n_a = (np.array(atoms.get_chemical_symbols()) == type_a).sum()
    #n_b = (atoms.get_chemical_symbols() == type_b).sum()
    #print n_a

    n_shell = 0  # depth of the shell
    while (n_a < n_atoms * target_x):
        n_shell += 1
        print ("shell: ", n_shell)

        if (n_depth != -1)and(n_shell > n_depth):
            break

        neiblist = NeighborList( [ R_min ] * n_atoms,
          self_interaction=False, bothways=True )
        neiblist.build( atoms )

        for i in xrange( n_atoms ):
            indeces, offsets = neiblist.get_neighbors(i)
            if (atoms[i].symbol == type_b):
                CN_temp = 0
                for ii in indeces:
                    if atoms[ii].symbol == type_b:
                        CN_temp += 1
                #print "CN_temp: ", CN_temp
                if (CN_temp < CN_max):
                    # coord shell is not full, swap type to type_a!
                    atoms[i].tag = n_shell # not swap yet, but mark

        # swap atom types now. Stop if target ratio achieved
        for atom in atoms:
            if (atom.tag > 0)&(atom.symbol == type_b):
                if n_a < n_atoms * target_x:
                    atom.symbol = type_a
                    n_a += 1
                    #print "n_A: ", n_a
    # end while

    # check number of atoms
    checkn_a = 0
    for element in atoms.get_chemical_symbols():
        if element == type_a:
            checkn_a += 1
    #print "Should be equal: ", n_a, checkn_a
    assert n_a == checkn_a
    return atoms
Esempio n. 46
0
    def monoatomic(self, R1=3, calc_energy=False):
        r"""This routine analyzes atomic structure
        by the calculation of coordination numbers
        in cluster with only one type of atom.

        Parameters
        ----------
        R1: float
            First coordination shell will icnlude all atoms
            with distance less then R1 [Angstrom].
            Default value is 3.
        calc_energy: bool
            Flag used for calculation of potential energy with EMT
            calculator.  The default value is False, so that
            energy is not calculated.

        Returns
        -------
        N: int
            number of atoms in cluster
        R: float
            radius of the cluster
        CN: float
            average coord number
        E: float
            potential energy, -1 if calc_energy is False
        Ncore:
            number of atoms in core region (number of atoms with
            all 12 neighbors)
        CNshell:
            average coordination number for surface atoms only

        Notes
        -----
            The radius of the cluster is roughly determined as
            maximum the distance from the center to most distant atom
            in the cluster.

        Example
        --------
        >>> atoms = FaceCenteredCubic('Ag',
          [(1, 0, 0), (1, 1, 0), (1, 1, 1)], [7,8,7], 4.09)
        >>> qsar = QSAR(atoms)
        >>> qsar.monoatomic(R1=3.0)
        >>> print "average CN is ", qsar.CN
        """
        self.chems = ['*'] # any element. For now used for report only

        N = len(self.atoms)
        nl = NeighborList( [0.5 * R1] * N, self_interaction=False, bothways=True )
        nl.build(self.atoms)
        CN = 0
        Ncore = 0
        Nshell = 0
        CNshell = 0  # average CN of surface atoms
        for i in xrange(0, N):
            indeces, offsets = nl.get_neighbors(i)
            CN += len(indeces)
            if len(indeces) < 12:
                Nshell += 1
                CNshell += len(indeces)
            else:
                Ncore += 1
        CN = CN * 1.0 / N
        CNshell = CNshell * 1.0 / Nshell
        #atoms.center()
        R = self.atoms.positions.max() / 2.0
        if calc_energy:
            #from asap3 import EMT
            from ase.calculators.emt import EMT
            atoms.set_calculator(EMT())
            E = atoms.get_potential_energy()
        else:
            E = -1
        #return N, R, CN, E, Ncore, CNshell
        self.N = N #TODO: use array property CNs
        self.R = R
        self.CN = CN
        self.CNs = np.array([[CN]])
        self.E = E
        self.Ncore = Ncore
        self.CNshell = CNshell
Esempio n. 47
0
    def biatomic(self, A, B, R1=3.0, calc_energy=False):
        r"""This routine analyzes atomic structure
        by the calculation of coordination numbers
        in cluster with atoms of two types (A and B).

        Parameters
        ----------
        A: string
            atom type, like 'Ag', 'Pt', etc.
        B: string
            atom type, like 'Ag', 'Pt', etc.
        R1: float
            First coordination shell will icnlude all atoms
            with distance less then R1 [Angstrom].
            Default value is 3.
        calc_energy: bool
            Flag used for calculation of potential energy with EMT
            calculator.  The default value is False, so that
            energy is not calculated.

        Returns
        -------
        N: int
            number of atoms in cluster
        nA:
            number of atoms of type A
        R: float
            radius of the cluster
        CN_AA: float
            average number of atoms A around atom A
        CN_AB: float
            average number of atoms A around atom B
        CN_BB: float
            average number of atoms B around atom B
        CN_BA: float
            average number of atoms B around atom A
        etha: float
            parameter of local ordering, -1 < etha < 1.
            Returns 999 if concentration of one of the
            component is too low.
        E: float
            potential energy
        NAcore:
            number of A atoms in core
        NBcore:
            number of B atoms in core
        CNshellAA:
            average CN of A-A for surface atoms only
        CNshellAB:
            average CN of A-B for surface atoms only
        CNshellBB:
            average CN of B-B for surface atoms only
        CNshellBA:
            average CN of B-A for surface atoms only

        Notes
        -----
            The radius of the cluster is roughly determined as
            maximum the distance from the center to most distant atom
            in the cluster.

        Example
        --------
        >>> atoms = FaceCenteredCubic('Ag',
          [(1, 0, 0), (1, 1, 0), (1, 1, 1)], [7,8,7], 4.09)
        >>> atoms = CoreShellFCC(atoms, 'Pt', 'Ag', 0.6, 4.09)
        >>> [N, nA, R, CN_AA, CN_AB, CN_BB, CN_BA, etha] =
          biatomic(atoms, 'Pt', 'Ag')
        >>> print "Short range order parameter: ", etha
        """
        self.chems = [A, B] # for now used for report only

        N = len(self.atoms)
        nA = 0
        nB = 0
        for element in self.atoms.get_chemical_symbols():
            if element == A:
                nA += 1
            elif element == B:
                nB += 1
            else:
                raise Exception('Extra element ' + element)
        if (nA + nB != N):
            raise Exception('Number of A (' + str(nA) + ') ' +
              'and B (' + str(nB) + ') artoms mismatch!')
        nl = NeighborList([0.5 * R1] * N, self_interaction=False, bothways=True)
        nl.build(self.atoms)

        # initialize counters:
        CN_AA = 0    # averaged total coord. numbers
        CN_AB = 0
        CN_BB = 0
        CN_BA = 0
        NAcore = 0  # number of atoms in core region
        NBcore = 0
        CNshellAA = 0  # average coord. numbers for surface atoms
        CNshellAB = 0
        CNshellBB = 0
        CNshellBA = 0
        for iatom in xrange(0, N):
            #print "central atom index:", iatom, " kind: ", self.atoms[iatom].symbol
            indeces, offsets = nl.get_neighbors(iatom)
            if self.atoms[iatom].symbol == B:
                CN_BB_temp = 0
                CN_BA_temp = 0
                for ii in indeces:
                    #print "neighbor atom index:", ii, " kind: ", self.atoms[ii].symbol
                    if self.atoms[ii].symbol == B:
                        CN_BB_temp += 1
                    elif  self.atoms[ii].symbol == A:
                        CN_BA_temp += 1
                    else:
                        print("Warning: unknown atom type %s. It will not be counted!"%self.atoms[ii].symbol)
                CN_BB += CN_BB_temp
                CN_BA += CN_BA_temp
                if len(indeces) < 12:
                    # SHELL
                    CNshellBB += CN_BB_temp
                    CNshellBA += CN_BA_temp
                else:
                    # CORE
                    NBcore += 1
            elif self.atoms[iatom].symbol == A:
                CN_AA_temp = 0
                CN_AB_temp = 0
                for i in indeces:
                    #print "neighbor atom index:", i, " kind: ", self.atoms[i].symbol
                    if self.atoms[i].symbol == A:
                        CN_AA_temp += 1
                    elif self.atoms[i].symbol == B:
                        CN_AB_temp += 1
                    else:
                        print("Warning: unknown atom type %s. It will not be counted!"%self.atoms[i].symbol)
                CN_AA += CN_AA_temp
                CN_AB += CN_AB_temp
                if len(indeces) < 12:
                    # SHELL
                    CNshellAA += CN_AA_temp
                    CNshellAB += CN_AB_temp
                else:
                    # CORE
                    NAcore += 1
            else:
                #raise Exception("Un")
                print("Warning: unknown atom type %s. It will not be counted!"%self.atoms[iatom].symbol)
        # averaging:
        CN_AA = CN_AA * 1.0 / nA
        CN_AB = CN_AB * 1.0 / nA
        CN_BB = CN_BB * 1.0 / nB
        CN_BA = CN_BA * 1.0 / nB
        znam = (nA - NAcore)
        if znam > 0.0001:
            CNshellAA = CNshellAA * 1.0 / znam
            CNshellAB = CNshellAB * 1.0 / znam
        else:
            CNshellAA = 0
            CNshellAB = 0
        znam = (nB - NBcore)
        if znam > 0.0001:
            CNshellBB = CNshellBB * 1.0 / znam
            CNshellBA = CNshellBA * 1.0 / znam
        else:
            CNshellBB = 0
            CNshellBA = 0

        # calc concentrations:
        concB = nB * 1.0 / N
        znam = concB * (CN_AA + CN_AB)
        if znam < 0.0001:
            #print "WARNING! Too low B concentration: ",concB
            etha = 999
        else:
            etha = 1 - CN_AB / znam
        R = self.atoms.positions.max() / 2.0
        if calc_energy:
            #from asap3 import EMT
            from ase.calculators.emt import EMT
            self.atoms.set_calculator(EMT())
            E = self.atoms.get_potential_energy()
        else:
            E = -1
        #return N, nA, R, CN_AA, CN_AB, CN_BB, CN_BA, etha, E, NAcore, \
        #  NBcore, CNshellAA, CNshellAB, CNshellBB, CNshellBA
        self.N = N
        self.nA = nA
        self.R = R
        self.CN_AA = CN_AA  #TODO: use only arrays of CNs
        self.CN_AB = CN_AB
        self.CN_BB = CN_BB
        self.CN_BA = CN_BA
        self.CNs = np.array([ [CN_AA, CN_AB], [CN_BA, CN_BB] ])
        self.etha = etha
        self.E = E
        self.NAcore = NAcore
        self.NBcore = NBcore
        self.CNshellAA = CNshellAA
        self.CNshellAB = CNshellAB
        self.CNshellBB = CNshellBB
        self.CNshellBA = CNshellBA