Statistiques
| Révision :

root / ase / calculators / neighborlist.py

Historique | Voir | Annoter | Télécharger (5 ko)

1 1 tkerber
from math import sqrt
2 1 tkerber
3 1 tkerber
import numpy as np
4 1 tkerber
5 1 tkerber
6 1 tkerber
class NeighborList:
7 1 tkerber
    """Neighbor list object.
8 1 tkerber

9 1 tkerber
    cutoffs: list of float
10 1 tkerber
        List of cutoff radii - one for each atom.
11 1 tkerber
    skin: float
12 1 tkerber
        If no atom has moved more than the skin-distance since the
13 1 tkerber
        last call to the ``update()`` method, then the neighbor list
14 1 tkerber
        can be reused.  This will save some expensive rebuilds of
15 1 tkerber
        the list, but extra neighbors outside the cutoff will be
16 1 tkerber
        returned.
17 1 tkerber
    self_interaction: bool
18 1 tkerber
        Should an atom return itself as a neighbor?
19 1 tkerber

20 1 tkerber
    Example::
21 1 tkerber

22 1 tkerber
      nl = NeighborList([2.3, 1.7])
23 1 tkerber
      nl.update(atoms)
24 1 tkerber
      indices, offsets = nl.get_neighbors(0)
25 1 tkerber

26 1 tkerber
    """
27 1 tkerber
28 1 tkerber
    def __init__(self, cutoffs, skin=0.3, sorted=False, self_interaction=True):
29 1 tkerber
        self.cutoffs = np.asarray(cutoffs) + skin
30 1 tkerber
        self.skin = skin
31 1 tkerber
        self.sorted = sorted
32 1 tkerber
        self.self_interaction = self_interaction
33 1 tkerber
        self.nupdates = 0
34 1 tkerber
35 1 tkerber
    def update(self, atoms):
36 1 tkerber
        """Make sure the list is up to date."""
37 1 tkerber
        if self.nupdates == 0:
38 1 tkerber
            self.build(atoms)
39 1 tkerber
            return True
40 1 tkerber
41 1 tkerber
        if ((self.pbc != atoms.get_pbc()).any() or
42 1 tkerber
            (self.cell != atoms.get_cell()).any() or
43 1 tkerber
            ((self.positions - atoms.get_positions())**2).sum(1).max() >
44 1 tkerber
            self.skin**2):
45 1 tkerber
            self.build(atoms)
46 1 tkerber
            return True
47 1 tkerber
48 1 tkerber
        return False
49 1 tkerber
50 1 tkerber
    def build(self, atoms):
51 1 tkerber
        """Build the list."""
52 1 tkerber
        self.positions = atoms.get_positions()
53 1 tkerber
        self.pbc = atoms.get_pbc()
54 1 tkerber
        self.cell = atoms.get_cell()
55 1 tkerber
        rcmax = self.cutoffs.max()
56 1 tkerber
57 1 tkerber
        icell = np.linalg.inv(self.cell)
58 1 tkerber
        scaled = np.dot(self.positions, icell)
59 1 tkerber
        scaled0 = scaled.copy()
60 1 tkerber
61 1 tkerber
        N = []
62 1 tkerber
        for i in range(3):
63 1 tkerber
            if self.pbc[i]:
64 1 tkerber
                scaled0[:, i] %= 1.0
65 1 tkerber
                v = icell[:, i]
66 1 tkerber
                h = 1 / sqrt(np.dot(v, v))
67 1 tkerber
                n =  int(2 * rcmax / h) + 1
68 1 tkerber
            else:
69 1 tkerber
                n = 0
70 1 tkerber
            N.append(n)
71 1 tkerber
72 1 tkerber
        offsets = np.empty((len(atoms), 3), int)
73 1 tkerber
        (scaled0 - scaled).round(out=offsets)
74 1 tkerber
        positions0 = np.dot(scaled0, self.cell)
75 1 tkerber
        natoms = len(atoms)
76 1 tkerber
        indices = np.arange(natoms)
77 1 tkerber
78 1 tkerber
        self.nneighbors = 0
79 1 tkerber
        self.npbcneighbors = 0
80 1 tkerber
        self.neighbors = [np.empty(0, int) for a in range(natoms)]
81 1 tkerber
        self.displacements = [np.empty((0, 3), int) for a in range(natoms)]
82 1 tkerber
        for n1 in range(0, N[0] + 1):
83 1 tkerber
            for n2 in range(-N[1], N[1] + 1):
84 1 tkerber
                for n3 in range(-N[2], N[2] + 1):
85 1 tkerber
                    if n1 == 0 and (n2 < 0 or n2 == 0 and n3 < 0):
86 1 tkerber
                        continue
87 1 tkerber
                    displacement = np.dot((n1, n2, n3), self.cell)
88 1 tkerber
                    for a in range(natoms):
89 1 tkerber
                        d = positions0 + displacement - positions0[a]
90 1 tkerber
                        i = indices[(d**2).sum(1) <
91 1 tkerber
                                    (self.cutoffs + self.cutoffs[a])**2]
92 1 tkerber
                        if n1 == 0 and n2 == 0 and n3 == 0:
93 1 tkerber
                            if self.self_interaction:
94 1 tkerber
                                i = i[i >= a]
95 1 tkerber
                            else:
96 1 tkerber
                                i = i[i > a]
97 1 tkerber
                        self.nneighbors += len(i)
98 1 tkerber
                        self.neighbors[a] = np.concatenate(
99 1 tkerber
                            (self.neighbors[a], i))
100 1 tkerber
                        disp = np.empty((len(i), 3), int)
101 1 tkerber
                        disp[:] = (n1, n2, n3)
102 1 tkerber
                        disp += offsets[i] - offsets[a]
103 1 tkerber
                        self.npbcneighbors += disp.any(1).sum()
104 1 tkerber
                        self.displacements[a] = np.concatenate(
105 1 tkerber
                            (self.displacements[a], disp))
106 1 tkerber
107 1 tkerber
        if self.sorted:
108 1 tkerber
            for a, i in enumerate(self.neighbors):
109 1 tkerber
                mask = (i < a)
110 1 tkerber
                if mask.any():
111 1 tkerber
                    j = i[mask]
112 1 tkerber
                    offsets = self.displacements[a][mask]
113 1 tkerber
                    for b, offset in zip(j, offsets):
114 1 tkerber
                        self.neighbors[b] = np.concatenate(
115 1 tkerber
                            (self.neighbors[b], [a]))
116 1 tkerber
                        self.displacements[b] = np.concatenate(
117 1 tkerber
                                (self.displacements[b], [-offset]))
118 1 tkerber
                    mask = np.logical_not(mask)
119 1 tkerber
                    self.neighbors[a] = self.neighbors[a][mask]
120 1 tkerber
                    self.displacements[a] = self.displacements[a][mask]
121 1 tkerber
122 1 tkerber
        self.nupdates += 1
123 1 tkerber
124 1 tkerber
    def get_neighbors(self, a):
125 1 tkerber
        """Return neighbors of atom number a.
126 1 tkerber

127 1 tkerber
        A list of indices and offsets to neighboring atoms is
128 1 tkerber
        returned.  The positions of the neighbor atoms can be
129 1 tkerber
        calculated like this::
130 1 tkerber

131 1 tkerber
          indices, offsets = nl.get_neighbors(42)
132 1 tkerber
          for i, offset in zip(indices, offsets):
133 1 tkerber
              print atoms.positions[i] + dot(offset, atoms.get_cell())
134 1 tkerber

135 1 tkerber
        Notice that if get_neighbors(a) gives atom b as a neighbor,
136 1 tkerber
        then get_neighbors(b) will not return a as a neighbor!"""
137 1 tkerber
138 1 tkerber
        return self.neighbors[a], self.displacements[a]