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