fastatomstruct 0.7.4__cp313-none-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,323 @@
1
+ from typing import Any, Callable, Dict, List, Optional
2
+ from time import time
3
+
4
+ import numpy as np
5
+ from ase import Atoms
6
+ from .fastatomstruct import *
7
+ from tidynamics import msd
8
+
9
+
10
+ def chunks(indices: List[int], n: int):
11
+ """Yield successive chunks from list of atoms."""
12
+ for i in range(0, len(indices), n):
13
+ yield indices[i : i + n]
14
+
15
+
16
+ def ipar(
17
+ func: Callable, atoms: List[Atoms], *args: List[Any], **kwargs: Dict[Any, Any]
18
+ ) -> List[Any]:
19
+ """Image-based parallelization.
20
+
21
+ The calculation of many structural quantities is parallelized over atoms.
22
+ However, image-based parallelization (or a mixture of both) can be more efficient
23
+ in some cases. This function makes using this parallelization layer quite easy.
24
+ You can just use it as a simple wrapper around other functions implemented in
25
+ `fastatomstruct` (see example below). **This function needs a working installation
26
+ of the MPI4Py package!** You can set the `RAYON_NUM_THREADS` enviroment variable
27
+ to control the number of threads.
28
+
29
+ Arguments:
30
+ func (Callable): Some function from the `fastatomstruct` package
31
+ atoms (List[Atoms]): List of ASE configurations
32
+
33
+ Returns:
34
+ List of results; type depends on the output of func
35
+
36
+ Examples:
37
+
38
+ The exemplary file "Sb-1.00-300-100.traj" `can be found here <https://zivgitlab.uni-muenster.de/ag-salinga/fastatomstruct/-/raw/master/regressiontesting/Structures/Sb-1.00-300-100.traj>`_.
39
+ The code below needs to be run e.g. with `mpirun -n 2 test.py`. In this case,
40
+ two processes will be used.
41
+
42
+ >>> import fastatomstruct as fs
43
+ >>> from ase import io
44
+ >>> atoms = io.read("Sb-1.00-300-100.traj", index=":2")
45
+ >>> print(fs.ipar(fs.q_tetrahedral, atoms, 3.2, 3))
46
+ [array([6, 3, 4, 2, 4, 3, 3, 2, 3, 5, 3, 3, 3, 3, 4, 4, 4, 5, 4, 3, 3, 3,
47
+ 3, 3, 4, 4, 4, 4, 3, 3, 3, 4, 3, 5, 3, 3, 4, 4, 2, 4, 4, 3, 4, 3,
48
+ 3, 4, 5, 3, 5, 3, 2, 3, 3, 4, 4, 4, 3, 3, 3, 3, 3, 2, 4, 4, 4, 4,
49
+ 4, 4, 4, 3, 3, 5, 3, 3, 3, 4, 3, 3, 4, 3, 3, 3, 4, 4, 3, 3, 3, 3,
50
+ 4, 5, 4, 4, 3, 4, 3, 3, 3, 4, 3, 4, 3, 2, 4, 4, 3, 3, 3, 3, 4, 4,
51
+ 4, 3, 3, 3, 3, 4, 4, 4, 2, 2, 4, 3, 5, 2, 2, 5, 3, 4, 3, 3, 4, 4,
52
+ 4, 4, 4, 4, 5, 3, 5, 5, 3, 3, 3, 4, 3, 4, 3, 5, 3, 4, 3, 3, 4, 4,
53
+ 2, 3, 2, 2, 3, 2, 4, 2, 3, 3, 4, 4, 4, 3, 4, 3, 4, 5, 3, 4, 2, 4,
54
+ 3, 5, 3, 4, 3, 4, 3, 3, 2, 4, 3, 3, 3, 3, 2, 4, 4, 3, 3, 3, 3, 4,
55
+ 3, 3, 5, 4, 3, 3, 4, 3, 2, 4, 3, 2, 3, 3, 3, 4, 4, 3, 3, 3, 4, 4,
56
+ 5, 6, 3, 3, 3, 4, 4, 2, 3, 4, 3, 3, 3, 3, 4, 2, 3, 3, 2, 3, 4, 3,
57
+ 3, 4, 3, 4, 3, 4, 2, 3, 4, 3, 3, 3, 3, 5, 3, 4, 3, 4, 4, 3, 4, 3,
58
+ 4, 4, 4, 3, 4, 5, 3, 3, 4, 5, 4, 4, 3, 2, 3, 3, 3, 5, 3, 4, 3, 3,
59
+ 4, 2, 1, 2, 3, 4, 3, 4, 4, 3, 4, 3, 4, 3, 4, 5, 3, 2, 3, 3, 3, 3,
60
+ 3, 3, 3, 4, 4, 4, 2, 4, 4, 3, 4, 4, 3, 4, 4, 3, 3, 3, 3, 5, 3, 4,
61
+ 3, 4, 3, 3, 5, 3, 3, 2, 5, 3, 4, 3, 3, 4, 3, 4, 5, 3, 5, 4, 2, 4,
62
+ 3, 2, 3, 4, 4, 4, 3, 3, 4, 4, 3, 4, 5, 3, 3, 4, 2, 4, 3, 4, 3, 5,
63
+ 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 5, 3, 3, 3, 4, 4, 3, 4, 3, 4, 4, 2,
64
+ 4, 4, 2, 3, 3, 3, 3, 4, 4, 4, 3, 3, 4, 5, 4, 3, 3, 4, 4, 4, 2, 4,
65
+ 3, 4, 4, 3, 3, 4, 3, 3, 3, 4, 5, 2, 3, 4, 3, 3, 4, 2, 3, 2, 4, 4,
66
+ 3, 3, 3, 4, 4, 5, 5, 3, 4, 6, 3, 2, 3, 3, 4, 5, 3, 4, 3, 3, 3, 3,
67
+ 5, 3, 4, 3, 2, 4, 4, 3, 3, 4, 3, 4, 3, 3, 3, 2, 5, 4, 4, 3, 3, 2,
68
+ 4, 4, 3, 3, 3, 3, 3, 3, 3, 5, 3, 2, 3, 4, 3, 4, 3, 4, 4, 3, 4, 4,
69
+ 5, 3, 4, 3, 3, 4, 3, 3, 3, 5, 4, 4, 3, 4, 2, 4, 4, 3, 3, 2, 3, 3,
70
+ 3, 4, 3, 4, 3, 3, 4, 4, 3, 3, 3, 4, 3, 3, 4, 4, 2, 3, 3, 4, 3, 4,
71
+ 3, 4, 3, 3, 2, 4, 5, 2, 4, 3, 5, 3, 3, 3, 3, 3, 3, 4, 3, 2, 2, 4,
72
+ 3, 5, 3, 3, 4, 3, 3, 3, 3, 3, 4, 3, 4, 5, 3, 4, 4, 4, 2, 2, 5, 5,
73
+ 5, 3, 1, 2, 3, 2, 4, 3, 4, 3, 3, 4, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3,
74
+ 4, 4, 4, 3, 3, 3, 4, 3, 3, 5, 4, 2, 5, 2, 3, 5, 3, 3, 3, 3, 4, 3,
75
+ 2, 3, 5, 4, 4, 2, 3, 3, 3, 4, 4, 3, 5, 2, 4, 3, 3, 3, 4, 4, 4, 3,
76
+ 2, 4, 3, 3, 3, 3, 6, 3, 4, 4, 3, 4, 3, 3, 4, 3, 2, 4, 3, 3, 3, 2,
77
+ 5, 2, 2, 4, 3, 4, 5, 2, 4, 4, 4, 3, 3, 4, 2, 4, 2, 3, 3, 4, 4, 4,
78
+ 3, 3, 4, 2, 4, 3, 5, 3, 4, 4, 4, 3, 3, 5, 4, 4, 3, 3, 5, 3, 3, 3,
79
+ 3, 3], dtype=uint64), array([5, 3, 4, 3, 3, 2, 4, 4, 3, 4, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 5,
80
+ 3, 3, 3, 3, 4, 4, 5, 5, 4, 3, 4, 5, 3, 3, 3, 4, 2, 3, 2, 3, 4, 3,
81
+ 3, 4, 4, 3, 4, 3, 3, 3, 3, 3, 4, 4, 3, 2, 3, 2, 3, 4, 4, 3, 4, 3,
82
+ 5, 4, 4, 4, 3, 3, 3, 5, 3, 3, 3, 4, 3, 3, 3, 4, 3, 4, 3, 4, 2, 3,
83
+ 4, 3, 3, 2, 3, 3, 3, 3, 4, 4, 4, 4, 3, 4, 4, 3, 3, 3, 3, 3, 4, 3,
84
+ 3, 3, 4, 3, 4, 4, 3, 3, 3, 3, 3, 2, 3, 1, 4, 3, 3, 4, 3, 3, 4, 3,
85
+ 4, 3, 4, 2, 3, 3, 4, 3, 2, 2, 3, 2, 3, 4, 4, 4, 3, 5, 2, 3, 4, 4,
86
+ 4, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 4, 1, 3, 5, 2, 4, 4, 4, 4, 3, 3,
87
+ 2, 4, 2, 3, 3, 3, 4, 4, 2, 4, 3, 2, 3, 3, 2, 3, 3, 4, 5, 4, 4, 4,
88
+ 3, 3, 4, 3, 3, 3, 2, 3, 4, 2, 3, 2, 3, 4, 2, 3, 3, 3, 3, 3, 3, 3,
89
+ 5, 3, 3, 4, 3, 4, 3, 2, 3, 3, 3, 4, 3, 4, 5, 3, 5, 5, 3, 3, 4, 4,
90
+ 3, 4, 3, 4, 4, 4, 3, 3, 5, 3, 3, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3,
91
+ 4, 4, 5, 3, 3, 4, 5, 3, 4, 3, 3, 4, 2, 2, 3, 3, 3, 3, 5, 4, 3, 5,
92
+ 4, 3, 3, 3, 4, 4, 4, 2, 3, 3, 4, 2, 3, 3, 2, 2, 2, 2, 3, 2, 3, 4,
93
+ 3, 3, 4, 3, 3, 5, 3, 4, 4, 4, 3, 5, 2, 3, 4, 3, 3, 2, 4, 4, 5, 2,
94
+ 3, 4, 3, 3, 4, 5, 4, 3, 3, 4, 2, 4, 4, 3, 3, 3, 4, 3, 4, 4, 4, 3,
95
+ 3, 2, 3, 3, 3, 5, 2, 4, 4, 4, 4, 3, 4, 2, 3, 3, 3, 4, 3, 3, 5, 5,
96
+ 3, 3, 3, 3, 2, 4, 4, 3, 4, 3, 4, 3, 4, 3, 3, 4, 3, 3, 4, 4, 3, 2,
97
+ 2, 3, 2, 3, 2, 3, 3, 2, 4, 4, 3, 2, 4, 3, 4, 3, 3, 4, 2, 4, 4, 2,
98
+ 3, 5, 3, 2, 3, 4, 3, 4, 4, 4, 3, 4, 3, 2, 3, 3, 2, 4, 3, 4, 2, 4,
99
+ 3, 3, 4, 5, 3, 4, 4, 3, 4, 5, 4, 3, 2, 4, 4, 4, 3, 5, 3, 2, 3, 2,
100
+ 3, 2, 3, 3, 3, 2, 4, 3, 3, 3, 4, 4, 4, 5, 3, 3, 4, 4, 4, 3, 3, 4,
101
+ 3, 5, 3, 3, 4, 3, 4, 3, 3, 3, 4, 3, 4, 4, 3, 2, 4, 3, 4, 4, 4, 2,
102
+ 5, 4, 3, 3, 4, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3,
103
+ 2, 1, 3, 3, 3, 3, 4, 4, 3, 2, 2, 4, 4, 2, 4, 4, 3, 2, 4, 4, 3, 4,
104
+ 4, 5, 4, 4, 2, 3, 3, 4, 4, 1, 5, 3, 3, 3, 3, 2, 3, 3, 3, 3, 4, 3,
105
+ 4, 5, 3, 3, 4, 3, 5, 3, 3, 4, 4, 2, 4, 4, 3, 2, 4, 5, 3, 4, 4, 5,
106
+ 1, 3, 4, 3, 3, 3, 4, 4, 4, 3, 3, 2, 4, 3, 3, 4, 4, 3, 4, 3, 5, 3,
107
+ 3, 4, 3, 3, 3, 3, 2, 2, 5, 4, 4, 4, 4, 3, 3, 3, 2, 3, 3, 3, 3, 3,
108
+ 3, 3, 4, 4, 4, 3, 3, 5, 4, 3, 3, 4, 5, 3, 5, 4, 4, 4, 3, 4, 4, 3,
109
+ 4, 4, 3, 4, 4, 4, 5, 2, 3, 3, 5, 4, 3, 3, 3, 3, 4, 2, 3, 3, 4, 3,
110
+ 3, 5, 4, 5, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 5, 5,
111
+ 4, 4, 3, 3, 4, 4, 4, 3, 2, 3, 3, 3, 2, 4, 3, 4, 3, 4, 3, 3, 2, 4,
112
+ 4, 3], dtype=uint64)]
113
+ """
114
+ from mpi4py import MPI
115
+
116
+ comm = MPI.COMM_WORLD
117
+ rank = comm.Get_rank()
118
+
119
+ if rank == 0:
120
+ print("\nImage parallelization report\n" + "----------------------------")
121
+
122
+ if rank == 0:
123
+ res_full, t0_f = [], time()
124
+
125
+ for i, c in enumerate(chunks(list(range(len(atoms))), comm.size)):
126
+ while len(c) < comm.size:
127
+ c.append(None)
128
+
129
+ if rank == 0:
130
+ per = (len(c) - np.count_nonzero([ci is None for ci in c])) / len(c)
131
+ t0 = time()
132
+ if c[rank] is not None:
133
+ a = atoms[c[rank]]
134
+ res = func(a, *args, **kwargs)
135
+ else:
136
+ res = None
137
+
138
+ res = comm.gather(res, root=0)
139
+ if rank == 0:
140
+ t = time() - t0
141
+ print(f"Chunk {i + 1}: {per * 100:03.2f}% | {t:.2f} s")
142
+ res_full.extend(res)
143
+
144
+ if rank == 0:
145
+ for i in range(len(res_full) - 1, -1, -1):
146
+ if res_full[i] is None:
147
+ del res_full[i]
148
+ print("----------------------------")
149
+ print(f"Total: {(time() - t0_f):.2f} s\n")
150
+ else:
151
+ res_full = None
152
+
153
+ return res_full
154
+
155
+
156
+ def static_structure_factor(
157
+ atoms: List[Atoms],
158
+ q: np.ndarray,
159
+ r_max: float,
160
+ n_bins: int,
161
+ filter: Optional[Filter] = None,
162
+ ) -> np.ndarray:
163
+ """Static structure factor, as calculated from the RDF.
164
+
165
+ For isotropic systems, the static structure factor can be calculated using
166
+
167
+ .. math::
168
+
169
+ S(q) = q + 4 \pi \\rho \int_0^\infty r (g(r) - 1) \\frac{\sin{qr}}{q} dr,
170
+
171
+ with :math:`q` the absolute value of the reciprocal vector and :math:`g(r)`
172
+ the radial distribution function.
173
+
174
+ Arguments:
175
+ atoms (ase.Atoms or List[ase.Atoms]): Atoms object(s) from ASE
176
+ q (np.ndarray): Array with values of :math:`q`
177
+ r_max (float): Cutoff radius for calculating the radial distribution function
178
+ n_bins (int): Number of bins for calculating the radial distribution function
179
+ filter (fastatomstruct.Filter): Filter applied to the atoms
180
+
181
+ Returns:
182
+ np.ndarray of floats with values of :math:`S(q)`
183
+
184
+ Examples
185
+ --------
186
+
187
+ The exemplary file "Sb540.traj" `can be found here <https://zivgitlab.uni-muenster.de/ag-salinga/fastatomstruct/-/raw/master/regressiontesting/Structures/Sb540.traj>`_.
188
+
189
+ >>> import fastatomstruct as fs
190
+ >>> import numpy as np
191
+ >>> from ase import io
192
+ >>> atoms = io.read("Sb540.traj")
193
+ >>> q = np.linspace(0.1, 10, 100)
194
+ >>> fs.static_structure_factor(atoms, q, 10, 100)
195
+ array([ 2.38192150e+00, 1.52370665e+00, 6.46487735e-01, 1.56760937e-01,
196
+ 1.20323650e-01, 2.76121498e-01, 2.99045859e-01, 8.31513912e-02,
197
+ -1.90316248e-01, -2.47717736e-01, -2.64701388e-03, 3.32212401e-01,
198
+ 4.12935073e-01, 8.23095798e-02, -4.33279251e-01, -6.37901553e-01,
199
+ -1.34285685e-01, 1.02734748e+00, 2.30245828e+00, 2.97459962e+00,
200
+ 2.63495154e+00, 1.47795768e+00, 1.95200237e-01, -4.64900582e-01,
201
+ -1.67915414e-01, 8.23636781e-01, 1.86264373e+00, 2.35654147e+00,
202
+ 2.12370925e+00, 1.44199006e+00, 7.96081378e-01, 5.32944085e-01,
203
+ 6.68269224e-01, 9.48456321e-01, 1.08128433e+00, 9.46956728e-01,
204
+ 6.48174186e-01, 3.96442496e-01, 3.47315244e-01, 5.10293291e-01,
205
+ 7.78951127e-01, 1.03029307e+00, 1.20441057e+00, 1.31266361e+00,
206
+ 1.39040807e+00, 1.44917566e+00, 1.46855988e+00, 1.42365507e+00,
207
+ 1.31353477e+00, 1.16335792e+00, 1.00360668e+00, 8.51865026e-01,
208
+ 7.15775063e-01, 6.10344434e-01, 5.65892631e-01, 6.11851235e-01,
209
+ 7.47484590e-01, 9.27629136e-01, 1.08202234e+00, 1.15793322e+00,
210
+ 1.15344778e+00, 1.11384177e+00, 1.09289068e+00, 1.10994910e+00,
211
+ 1.13732027e+00, 1.12748028e+00, 1.05705663e+00, 9.51231920e-01,
212
+ 8.68843102e-01, 8.60366739e-01, 9.31803892e-01, 1.04118569e+00,
213
+ 1.12787974e+00, 1.15066369e+00, 1.10732762e+00, 1.02633983e+00,
214
+ 9.43038802e-01, 8.80934999e-01, 8.48915518e-01, 8.48906335e-01,
215
+ 8.81116102e-01, 9.40582699e-01, 1.01098283e+00, 1.06655300e+00,
216
+ 1.08547102e+00, 1.06556309e+00, 1.02825219e+00, 1.00491362e+00,
217
+ 1.01457151e+00, 1.05006526e+00, 1.08377161e+00, 1.08843837e+00,
218
+ 1.05674417e+00, 1.00482220e+00, 9.58470246e-01, 9.34461644e-01,
219
+ 9.31941120e-01, 9.38971544e-01, 9.46172717e-01, 9.54459745e-01])
220
+ """
221
+ if isinstance(atoms, list):
222
+ rdf = []
223
+ for a in atoms:
224
+ r, rdf_i = radial_distribution_function(a, r_max, n_bins, filter)
225
+ rdf.append(rdf_i)
226
+ rdf = np.mean(rdf, axis=0)
227
+ rho = len(atoms[0]) / atoms[0].get_volume()
228
+ else:
229
+ r, rdf = radial_distribution_function(atoms, r_max, n_bins)
230
+ rho = len(atoms) / atoms.get_volume()
231
+
232
+ integral = np.zeros(len(q))
233
+ for i, qi in enumerate(q):
234
+ integrand = r[1:] * np.sin(qi * r[1:]) * (rdf[1:] - 1)
235
+ integral[i] = np.trapz(integrand, r[1:])
236
+ return 1 + 4 * np.pi * rho / q * integral
237
+
238
+
239
+ def __convert_pos(atoms) -> List[np.ndarray]:
240
+ pos = []
241
+ for i in range(len(atoms[0])):
242
+ pos_temp = np.empty((len(atoms), 3))
243
+ for j, a in enumerate(atoms):
244
+ pos_temp[j, :] = a.positions[i, :]
245
+ pos.append(pos_temp)
246
+ return pos
247
+
248
+
249
+ def mean_squared_displacement(atoms: List[Atoms]) -> np.ndarray:
250
+ """Mean squared displacment of a trajectory.
251
+
252
+ The MSD is calculated using the `tidynamics` package. It is defined as
253
+
254
+ .. math::
255
+
256
+ \\mathrm{MSD} = \\frac{1}{N} \sum_{i = 1}^N |\\vec{x}_i(t) - \\vec{x}_i(0)|^2,
257
+
258
+ where the :math:`\\vec{x}_i(t)` are the atomic positions at time :math:`t`.
259
+
260
+ Arguments:
261
+ atoms (List[ase.Atoms]): Trajectory, atoms objects from ASE
262
+
263
+ Returns:
264
+ NumPy Array of floats containing the mean squared displacement
265
+
266
+ Examples
267
+ --------
268
+
269
+ The exemplary file "Sb-1.00-300-100.traj" `can be found here <https://zivgitlab.uni-muenster.de/ag-salinga/fastatomstruct/-/raw/master/regressiontesting/Structures/Sb-1.00-300-100.traj>`_.
270
+
271
+ >>> import fastatomstruct as fs
272
+ >>> from ase import io
273
+ >>> atoms = io.read("Sb-1.00-300-100.traj", index=":")
274
+ >>> fs.mean_squared_displacement(atoms)
275
+ array([-1.64608547e-11, 8.99267872e-02, 1.44127662e-01, ...,
276
+ 2.02885143e+00, 2.02005773e+00, 2.01818494e+00])
277
+ """
278
+ return squared_displacement(atoms).mean(axis=0)
279
+
280
+
281
+ def squared_displacement(atoms: List[Atoms]) -> np.ndarray:
282
+ """Squared displacment of a trajectory.
283
+
284
+ The SD is calculated using the `tidynamics` package. It is defined as
285
+
286
+ .. math::
287
+
288
+ \\mathrm{SD}_i = |\vec{x}_i(t) - \vec{x}_i(0)|^2,
289
+
290
+ where the :math:`\vec{x}_i(t)` are the atomic positions at time :math:`t`.
291
+
292
+ Arguments:
293
+ atoms (List[ase.Atoms]): Trajectory, atoms objects from ASE
294
+
295
+ Returns:
296
+ NumPy Array of floats containing the mean squared displacement
297
+
298
+ Examples
299
+ --------
300
+
301
+ The exemplary file "Sb-1.00-300-100.traj" `can be found here <https://zivgitlab.uni-muenster.de/ag-salinga/fastatomstruct/-/raw/master/regressiontesting/Structures/Sb-1.00-300-100.traj>`_.
302
+
303
+ >>> import fastatomstruct as fs
304
+ >>> from ase import io
305
+ >>> atoms = io.read("Sb-1.00-300-100.traj", index=":")
306
+ >>> fs.mean_squared_displacement(atoms)
307
+ array([[-2.91038305e-11, 8.38161177e-02, 1.25422533e-01, ...,
308
+ 6.15651363e-01, 5.27809637e-01, 6.19426924e-01],
309
+ [ 0.00000000e+00, 9.27498088e-02, 1.52887610e-01, ...,
310
+ 1.07754904e+00, 1.10512104e+00, 1.46645886e+00],
311
+ [-1.16415322e-10, 8.61089322e-02, 1.37081905e-01, ...,
312
+ 1.27085909e+00, 1.18840340e+00, 9.64727558e-01],
313
+ ...,
314
+ [-4.65661287e-10, 9.55223907e-02, 1.56593970e-01, ...,
315
+ 4.45991428e-01, 2.80684893e-01, 1.91919705e-01],
316
+ [ 0.00000000e+00, 8.69368854e-02, 1.34933834e-01, ...,
317
+ 4.86719506e-01, 3.47152810e-01, 5.66086433e-01],
318
+ [-9.31322575e-10, 8.86508557e-02, 1.46782934e-01, ...,
319
+ 6.20435820e-01, 7.15149704e-01, 1.23890466e+00]])
320
+ """
321
+ pos = __convert_pos(atoms)
322
+ m = [msd(p) for p in pos]
323
+ return np.array(m)