コード例 #1
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
コード例 #2
0
ファイル: forcefield.py プロジェクト: kul-group/MAZE-sim
def get_bonds(cluster, mult=1, excluded_index=None, excluded_pair=None):
    """
    Using ase.geometry.analysis.Analysis to get all bonds, then remove the repeated ones.
    Function also allows removing certain bonding pair defined by user (excluded_pair).
    Or removing pairs including certain atomic indices (excluded_index).
    :param cluster:
    :param mult:
    :param excluded_index: list of integers
    :param excluded_pair: list of lists
    :return: full bonding list, shortened list.
    If both excluded_index and excluded_pair are None, bonding list == shortened list
    """
    if excluded_index is None:
        excluded_index = []
    if excluded_pair is None:
        excluded_pair = []

    nl = NeighborList(natural_cutoffs(cluster, mult=mult),
                      bothways=True,
                      self_interaction=False)
    nl.update(cluster)

    bond_list, shortened_list = [], []
    for count, indices in enumerate(Analysis(cluster, nl=nl).all_bonds[0]):
        for index in indices:
            if [count, index] not in bond_list and [index, count
                                                    ] not in bond_list:
                bond_list.append([count, index])

    for bond in bond_list:
        if all(single_index not in bond for single_index in excluded_index) and \
                all(tuple(bond) not in list(permutations(pair)) for pair in excluded_pair):
            shortened_list.append(bond)

    return bond_list, shortened_list
コード例 #3
0
ファイル: pov.py プロジェクト: rchiechi/QuantumParse
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
コード例 #4
0
ファイル: utilities.py プロジェクト: ggandus/QTransport
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)
コード例 #5
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)
コード例 #6
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()))
コード例 #7
0
ファイル: ase_utils.py プロジェクト: devonwa/twodee
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)
コード例 #8
0
ファイル: forcefield.py プロジェクト: kul-group/MAZE-sim
def get_angles(cluster, mult=1, excluded_index=None, excluded_pair=None):
    """
    #TODO: consider combining get_bonds and get_angles function
    ase.geometry.analysis.Analysis.unique_angles function does not work, return all angles.
    three-body interactions.
    :param excluded_pair: excluding all [particle1, particle2, particle3] lists involving the excluded pair
    """
    if excluded_index is None:
        excluded_index = []
    if excluded_pair is None:
        excluded_pair = []

    nl = NeighborList(natural_cutoffs(cluster, mult=mult),
                      bothways=True,
                      self_interaction=False)
    nl.update(cluster)

    angle_list, shortened_list = [], []
    for count, indices in enumerate(Analysis(cluster, nl=nl).all_angles[0]):
        for index in indices:
            if all(
                    list(val) not in angle_list for val in list(
                        permutations([count, index[0], index[1]]))):
                angle_list.append([count, index[0], index[1]])

    for angle in angle_list:
        if all(single_index not in angle for single_index in excluded_index) and \
                all(list(value) not in excluded_pair for value in list(permutations(angle, 2))):
            shortened_list.append(angle)

    return angle_list, shortened_list
コード例 #9
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
コード例 #10
0
ファイル: neighborlist.py プロジェクト: chuanxun/CatLearn
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
コード例 #11
0
  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)
コード例 #12
0
ファイル: cap_clust.py プロジェクト: kul-group/MAZE-sim
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)
コード例 #13
0
ファイル: classification.py プロジェクト: yfyh2013/CatKit
    def _build_neighborlist(self, cutoff=None):
        """Construct a nearest-neighbor list using ASE:
        https://wiki.fysik.dtu.dk/ase/ase/neighborlist.html

        This function is intended for adaptation with machine learning
        in the future.

        TODO: DEPRECATED. Needs to be replaced with those in utils.

        Parameters:
        -----------
        cutoff : ndarray (96,) or None
            cutoff radius for each of the first 96 elements.

        Returns:
        --------
        nl : ASE nearest-neighbor list
            nearest-neighbor lists.
        """
        if cutoff is None:
            cutoff = r[self.atoms.get_atomic_numbers()]

        # Build a nearest neighbor list
        nl = NL(cutoff, skin=0.16, self_interaction=False, bothways=False)
        nl.update(self.atoms)

        self.neighbor_list = nl

        return nl
コード例 #14
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
コード例 #15
0
ファイル: pull_atomscloser.py プロジェクト: lileist/myScripts
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)
コード例 #16
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
コード例 #17
0
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
コード例 #18
0
def ase_connectivity(atoms, cutoffs=None, count_bonds=True):
    """Return a connectivity matrix calculated of an atoms object.

    If no neighborlist or connectivity matrix is attached to the atoms object,
    a new one will be generated. Multiple connections are counted.

    Parameters
    ----------
    atoms : object
        An ase atoms object.
    cutoffs : list
        A list of cutoff radii for the atoms, ordered by atom index.

    Returns
    -------
    conn : array
        An n by n, where n is len(atoms).
    """
    if hasattr(atoms, 'neighborlist'):
        nl = atoms.neighborlist
    else:
        nl = NeighborList(cutoffs=cutoffs, bothways=True)
        nl.update(atoms)
        conn_mat = nl.get_connectivity_matrix(sparse=False)
        np.fill_diagonal(conn_mat, 0)

    return np.asarray(conn_mat, dtype=int)
コード例 #19
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)
コード例 #20
0
ファイル: ase_utils.py プロジェクト: devonwa/twodee
def candidates_combos(atoms, edge=None, pore_size=None):
    """Return candidate pore indices combinations."""
    from itertools import combinations

    cans = []
    indices = [a.index for a in atoms if a.index not in edge]

    nblist = NeighborList([graphene_cutoff for i in range(len(atoms))],
                           bothways=True,
                           self_interaction=False)
    nblist.update(atoms)

    def constraint_check(pores):
        for pore in pores:
            remains = [a.index for a in atoms if a.index not in pore]
            if is_connected(nblist, remains) and is_connected(nblist, pore):
                cans.append(pore)

    if pore_size is not None:
        pores = combinations(indices, pore_size)
        print(sum([1 for p in pores]))
        pores = combinations(indices, pore_size)
        constraint_check(pores)
    else:
        for i in range(1, len(indices)):
            pores = combinations(indices, i)
            constraint_check(pores)

    return cans
コード例 #21
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
コード例 #22
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!")
コード例 #23
0
def test_supercell():
    atoms = Atoms(numbers=range(10),
                  cell=[(0.2, 1.2, 1.4), (1.4, 0.1, 1.6), (1.3, 2.0, -0.1)])
    atoms.set_scaled_positions(3 * random.random((10, 3)) - 1)

    for sorted in [False, True]:
        for p1 in range(2):
            for p2 in range(2):
                for p3 in range(2):
                    # print(p1, p2, p3)
                    atoms.set_pbc((p1, p2, p3))
                    nl = NeighborList(atoms.numbers * 0.2 + 0.5,
                                      skin=0.0,
                                      sorted=sorted)
                    nl.update(atoms)
                    d, c = count(nl, atoms)
                    atoms2 = atoms.repeat((p1 + 1, p2 + 1, p3 + 1))
                    nl2 = NeighborList(atoms2.numbers * 0.2 + 0.5,
                                       skin=0.0,
                                       sorted=sorted)
                    nl2.update(atoms2)
                    d2, c2 = count(nl2, atoms2)
                    c2.shape = (-1, 10)
                    dd = d * (p1 + 1) * (p2 + 1) * (p3 + 1) - d2
                    assert abs(dd) < 1e-10
                    assert not (c2 - c).any()
コード例 #24
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
コード例 #25
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
コード例 #26
0
ファイル: shrink_bond.py プロジェクト: lileist/myScripts
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
コード例 #27
0
ファイル: view.py プロジェクト: rchiechi/QuantumParse
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
コード例 #28
0
ファイル: shrink_bond.py プロジェクト: lileist/myScripts
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
コード例 #29
0
ファイル: tk_analysis.py プロジェクト: ggandus/QTransport
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
コード例 #30
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:]
コード例 #31
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))
コード例 #32
0
ファイル: view.py プロジェクト: rosswhitfield/ase
    def bind(self, frame):
        if not self.ui.get_widget('/MenuBar/ViewMenu/ShowBonds'
                                  ).get_active():
            self.bonds = np.empty((0, 5), int)
            return
        
        from ase.atoms import Atoms
        from ase.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:]
コード例 #33
0
def get_rdf(atoms, a, rs, dr, rmax, nl=None):
    """number of atoms in a shell between r and r+dr
       centered around atom a and normalized by the shell volume.
       The number of atoms are devided into types according to
       their atomic number.
    """
    V_r = get_shell_volume(rs, dr)
    if nl is None:
        cutoffs = [
            rmax / 2 + 0.1,
        ] * len(atoms)
        nl = NeighborList(cutoffs=cutoffs,
                          skin=0,
                          self_interaction=False,
                          bothways=True)
        nl.update(atoms)

    cluster = get_cluster(atoms, a=a, nl=nl)  # first atom in the cluser -> a=0
    zs = set(atoms.get_chemical_symbols())
    p = dict([[z, np.zeros(len(rs))] for z in zs])
    for ir, r in enumerate(rs):
        shell = get_shell(atoms=cluster, r=r, dr=dr, a=0)
        zs_shell, count = np.unique(shell.get_chemical_symbols(),
                                    return_counts=True)
        zc = dict([(z, c) for z, c in zip(zs_shell, count)])
        for z in zs:
            if z in zc:
                p[z][ir] = zc[z]
            else:
                p[z][ir] = 0
    for z in zs:
        p[z] /= V_r

    return p
コード例 #34
0
ファイル: view.py プロジェクト: 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
コード例 #35
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
コード例 #36
0
ファイル: exafs.py プロジェクト: SamChill/expectra
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
コード例 #37
0
ファイル: opls.py プロジェクト: rosswhitfield/ase
class OPLSff:
    def __init__(self, fileobj=None, warnings=0):
        self.warnings = warnings
        self.data = {}
        if fileobj is not None:
            self.read(fileobj)

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

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

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

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

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

            while add_line():
                pass

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

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

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

        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')
コード例 #38
0
ファイル: eam.py プロジェクト: rchiechi/QuantumParse
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()
コード例 #39
0
ファイル: emt.py プロジェクト: rchiechi/QuantumParse
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
コード例 #40
0
ファイル: neighbor.py プロジェクト: rchiechi/QuantumParse
        i, offsets = nl.get_neighbors(a)
        for j in i:
            c[j] += 1
        c[a] += len(i)
        d += (((R[i] + np.dot(offsets, cell) - R[a])**2).sum(1)**0.5).sum()
    return d, c

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

h2 = Atoms('H2', positions=[(0, 0, 0), (0, 0, 1)])
nl = NeighborList([0.5, 0.5], skin=0.1, sorted=True, self_interaction=False)
コード例 #41
0
ファイル: neighbors.py プロジェクト: rchiechi/QuantumParse
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
コード例 #42
0
ファイル: lj.py プロジェクト: rchiechi/QuantumParse
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