mgtoolbox-kernel 0.1.0__py3-none-any.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.
- mgtoolbox_kernel/__init__.py +0 -0
- mgtoolbox_kernel/io/__init__.py +27 -0
- mgtoolbox_kernel/io/cif.py +158 -0
- mgtoolbox_kernel/io/vasp/__init__.py +0 -0
- mgtoolbox_kernel/io/vasp/poscar.py +132 -0
- mgtoolbox_kernel/kernel/__init__.py +1 -0
- mgtoolbox_kernel/kernel/atom.py +53 -0
- mgtoolbox_kernel/kernel/cell.py +452 -0
- mgtoolbox_kernel/kernel/mgclass.py +8 -0
- mgtoolbox_kernel/kernel/potential.py +81 -0
- mgtoolbox_kernel/kernel/site.py +219 -0
- mgtoolbox_kernel/kernel/structure.py +194 -0
- mgtoolbox_kernel/util/__init__.py +0 -0
- mgtoolbox_kernel/util/base.py +141 -0
- mgtoolbox_kernel-0.1.0.dist-info/METADATA +15 -0
- mgtoolbox_kernel-0.1.0.dist-info/RECORD +18 -0
- mgtoolbox_kernel-0.1.0.dist-info/WHEEL +5 -0
- mgtoolbox_kernel-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
|
|
2
|
+
from itertools import product
|
|
3
|
+
from typing import List, Union
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
from spglib import niggli_reduce
|
|
7
|
+
|
|
8
|
+
from mgtoolbox_kernel.util.base import (get_lattice_parameters,
|
|
9
|
+
get_lattice_vectors)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Cell(object):
|
|
13
|
+
"""
|
|
14
|
+
表示晶体晶格的类。
|
|
15
|
+
|
|
16
|
+
该类用于存储和操作晶体晶格的参数和基矢。
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, a: float, b: float, c: float, alpha: float, beta: float, gamma: float, cell_basis_vectors=None):
|
|
20
|
+
"""
|
|
21
|
+
初始化晶体晶格。
|
|
22
|
+
|
|
23
|
+
:param a: 晶格参数 a
|
|
24
|
+
:param b: 晶格参数 b
|
|
25
|
+
:param c: 晶格参数 c
|
|
26
|
+
:param alpha: 晶格角度 alpha
|
|
27
|
+
:param beta: 晶格角度 beta
|
|
28
|
+
:param gamma: 晶格角度 gamma
|
|
29
|
+
:param cell_basis_vectors: 晶格基矢,默认为 None
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
self._cell_param: np.ndarray = np.zeros((2, 3))
|
|
33
|
+
self._cell_param[0] = [a, b, c]
|
|
34
|
+
self._cell_param[1] = [alpha, beta, gamma]
|
|
35
|
+
# self._cell_basis_vectors = None
|
|
36
|
+
if cell_basis_vectors is None:
|
|
37
|
+
self.__set_cell_basis_vectors(a, b, c, alpha, beta, gamma)
|
|
38
|
+
else:
|
|
39
|
+
self._cell_basis_vectors = cell_basis_vectors
|
|
40
|
+
|
|
41
|
+
def __eq__(self, other: 'Cell') -> bool:
|
|
42
|
+
return self is other or np.allclose(self._cell_basis_vectors, other._cell_basis_vectors)
|
|
43
|
+
|
|
44
|
+
def __repr__(self) -> str:
|
|
45
|
+
cstring = {
|
|
46
|
+
'lattice': self._cell_basis_vectors,
|
|
47
|
+
'parameters': self._cell_param.reshape(6, )
|
|
48
|
+
}
|
|
49
|
+
return str(cstring)
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def abc(self) -> np.ndarray:
|
|
53
|
+
"""Get the cell parameters:a,b,c
|
|
54
|
+
|
|
55
|
+
Returns
|
|
56
|
+
-------
|
|
57
|
+
np.ndarray
|
|
58
|
+
cell parameters:a,b,c
|
|
59
|
+
"""
|
|
60
|
+
return self._cell_param[0]
|
|
61
|
+
|
|
62
|
+
@abc.setter
|
|
63
|
+
def abc(self, value):
|
|
64
|
+
self._cell_param[0] = value
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def angles(self) -> np.ndarray:
|
|
68
|
+
"""Get the cell parameters:alpha,beta,gamma
|
|
69
|
+
|
|
70
|
+
Returns
|
|
71
|
+
-------
|
|
72
|
+
numpy.ndarray
|
|
73
|
+
cell parameters:alpha,beta,gamma
|
|
74
|
+
"""
|
|
75
|
+
return self._cell_param[1]
|
|
76
|
+
|
|
77
|
+
@angles.setter
|
|
78
|
+
def angles(self, value: Union[np.ndarray, List]):
|
|
79
|
+
self._cell_param[1] = value
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def lattice_parameters(self):
|
|
83
|
+
return np.hstack((self.abc, self.angles))
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def cell_basis_vectors(self):
|
|
87
|
+
return self._cell_basis_vectors
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def volume(self):
|
|
91
|
+
return np.fabs(np.linalg.det(np.transpose(self.cell_basis_vectors)))
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def reciprocal_cell_vectors(self):
|
|
95
|
+
'''获取倒易晶格向量,不包括2pi的系数
|
|
96
|
+
|
|
97
|
+
Returns
|
|
98
|
+
-------
|
|
99
|
+
倒易晶格向量
|
|
100
|
+
'''
|
|
101
|
+
return np.transpose(np.linalg.pinv(self.cell_basis_vectors))
|
|
102
|
+
|
|
103
|
+
def __set_cell_basis_vectors(self, a, b, c, alpha, beta, gamma):
|
|
104
|
+
self._cell_basis_vectors = get_lattice_vectors(a, b, c, alpha, beta, gamma)
|
|
105
|
+
|
|
106
|
+
@staticmethod
|
|
107
|
+
def from_lattice_parameters(a, b, c, alpha, beta, gamma):
|
|
108
|
+
return Cell(a, b, c, alpha, beta, gamma)
|
|
109
|
+
|
|
110
|
+
@staticmethod
|
|
111
|
+
def from_cell_vectors(cell_vectors: np.ndarray, fix_cell_vectors: bool = False):
|
|
112
|
+
(a, b, c, alpha, beta, gamma) = get_lattice_parameters(cell_vectors)
|
|
113
|
+
if not fix_cell_vectors:
|
|
114
|
+
return Cell(a, b, c, alpha, beta, gamma)
|
|
115
|
+
else:
|
|
116
|
+
return Cell(a, b, c, alpha, beta, gamma, cell_vectors)
|
|
117
|
+
|
|
118
|
+
def get_distances(
|
|
119
|
+
self,
|
|
120
|
+
cart_coords1,
|
|
121
|
+
cart_coords2=None,
|
|
122
|
+
mic=True
|
|
123
|
+
):
|
|
124
|
+
"""Get the minimum distance of to list of cartesian coordinates
|
|
125
|
+
|
|
126
|
+
Parameters
|
|
127
|
+
----------
|
|
128
|
+
list | numpy.ndarray
|
|
129
|
+
cartesian coordinates
|
|
130
|
+
list | numpy.ndarray, optional
|
|
131
|
+
cartesian coordinates, by default None
|
|
132
|
+
|
|
133
|
+
Returns
|
|
134
|
+
-------
|
|
135
|
+
numpy.ndarray, numpy.ndarray
|
|
136
|
+
minimum image distance vectors and its distances
|
|
137
|
+
"""
|
|
138
|
+
pbc = [True, True, True]
|
|
139
|
+
cart_coords1 = np.array(cart_coords1)
|
|
140
|
+
if cart_coords2 is None:
|
|
141
|
+
n1 = len(cart_coords1)
|
|
142
|
+
# upper triangular index
|
|
143
|
+
i1, i2 = np.triu_indices(n1, k=1)
|
|
144
|
+
distances_vecters = cart_coords1[i2] - cart_coords1[i1]
|
|
145
|
+
else:
|
|
146
|
+
cart_coords2 = np.array(cart_coords2)
|
|
147
|
+
distances_vecters = (cart_coords2[np.newaxis, :, :] - cart_coords1[:, np.newaxis, :]).reshape((-1, 3))
|
|
148
|
+
|
|
149
|
+
if np.sum(pbc) == 0 or mic == False:
|
|
150
|
+
minimum_vecters = np.asarray(distances_vecters)
|
|
151
|
+
vector_lengths = np.linalg.norm(distances_vecters, axis=1)
|
|
152
|
+
else:
|
|
153
|
+
minimum_vecters, vector_lengths = self.__find_mic_distances(distances_vecters)
|
|
154
|
+
|
|
155
|
+
if cart_coords2 is None:
|
|
156
|
+
Dout = np.zeros((n1, n1, 3))
|
|
157
|
+
Dout[(i1, i2)] = minimum_vecters
|
|
158
|
+
Dout -= np.transpose(Dout, axes=(1, 0, 2))
|
|
159
|
+
|
|
160
|
+
Dout_len = np.zeros((n1, n1))
|
|
161
|
+
Dout_len[(i1, i2)] = vector_lengths
|
|
162
|
+
Dout_len += Dout_len.T
|
|
163
|
+
return Dout, Dout_len
|
|
164
|
+
|
|
165
|
+
minimum_vecters.shape = (-1, len(cart_coords2), 3)
|
|
166
|
+
vector_lengths.shape = (-1, len(cart_coords2))
|
|
167
|
+
|
|
168
|
+
return minimum_vecters, vector_lengths
|
|
169
|
+
|
|
170
|
+
def __find_mic_distances(self, vecters):
|
|
171
|
+
"""Get the minimum image distance of each atoms
|
|
172
|
+
|
|
173
|
+
Parameters
|
|
174
|
+
----------
|
|
175
|
+
numpy.ndarray
|
|
176
|
+
distance vectors
|
|
177
|
+
|
|
178
|
+
Returns
|
|
179
|
+
-------
|
|
180
|
+
numpy.ndarray, numpy.ndarray
|
|
181
|
+
minimum image distance vectors and its distances
|
|
182
|
+
"""
|
|
183
|
+
pbc = [True, True, True]
|
|
184
|
+
n = np.sum(pbc)
|
|
185
|
+
minimum_vecters = []
|
|
186
|
+
vector_lengths = []
|
|
187
|
+
for v in vecters:
|
|
188
|
+
mic_flag = False
|
|
189
|
+
if n == 3:
|
|
190
|
+
minimum_vecter, vector_length = self.__direct_find_mic(v)
|
|
191
|
+
if (vector_length < 0.5 * min(self.abc)):
|
|
192
|
+
mic_flag = True
|
|
193
|
+
minimum_vecters.append(minimum_vecter)
|
|
194
|
+
vector_lengths.append(vector_length)
|
|
195
|
+
continue
|
|
196
|
+
else:
|
|
197
|
+
mic_flag = False
|
|
198
|
+
minimum_vecters = []
|
|
199
|
+
vector_lengths = []
|
|
200
|
+
break
|
|
201
|
+
minimum_vecters = np.array(minimum_vecters)
|
|
202
|
+
vector_lengths = np.array(vector_lengths)
|
|
203
|
+
if not mic_flag:
|
|
204
|
+
minimum_vecters, vector_lengths = self.__reduce_find_mic(vecters)
|
|
205
|
+
|
|
206
|
+
return minimum_vecters, vector_lengths
|
|
207
|
+
|
|
208
|
+
def __direct_find_mic(self, vecter):
|
|
209
|
+
"""Calculate the minimum image distances,and use the result when the minimum image convention is satisfied.The minimum image convention i.e distance < min(a,b,c)/2
|
|
210
|
+
|
|
211
|
+
Parameters
|
|
212
|
+
----------
|
|
213
|
+
numpy.ndarray
|
|
214
|
+
distance vector
|
|
215
|
+
|
|
216
|
+
Returns
|
|
217
|
+
-------
|
|
218
|
+
numpy.ndarray, numpy.ndarray
|
|
219
|
+
minimum image distance vector and its distance
|
|
220
|
+
"""
|
|
221
|
+
# convert to fractional coordinates
|
|
222
|
+
frac_coord = self.get_fractional_coordinates(vecter)
|
|
223
|
+
# Control the fractional coordinate range to (-0.5,0.5)
|
|
224
|
+
frac_coord -= np.floor(frac_coord + 0.5)
|
|
225
|
+
minimum_vecter = self.get_cartesian_coords(frac_coord)
|
|
226
|
+
vector_length = np.linalg.norm(minimum_vecter)
|
|
227
|
+
# Returns the shortest vector and its length
|
|
228
|
+
return minimum_vecter, vector_length
|
|
229
|
+
|
|
230
|
+
def __reduce_find_mic(self, vecters):
|
|
231
|
+
|
|
232
|
+
"""If the minimum image convention is not satisfied, reduce the cell to re-calculate the minimum image distances
|
|
233
|
+
|
|
234
|
+
Parameters
|
|
235
|
+
----------
|
|
236
|
+
vecters : numpy.ndarray
|
|
237
|
+
distance vectors
|
|
238
|
+
|
|
239
|
+
Returns
|
|
240
|
+
-------
|
|
241
|
+
numpy.ndarray, numpy.ndarray
|
|
242
|
+
minimum image distance vectors and its distances
|
|
243
|
+
"""
|
|
244
|
+
pbc = [True, True, True]
|
|
245
|
+
cart_coords = self.__wrap_atoms(vecters)
|
|
246
|
+
# set the periodic range of three vector directions according to the periodic boundary conditions. (-1,1)
|
|
247
|
+
periodic_range = [np.arange(-1 * p, p + 1) for p in pbc]
|
|
248
|
+
hkl_range = list(product(*periodic_range))
|
|
249
|
+
vecter_range = np.dot(hkl_range, self.cell_basis_vectors)
|
|
250
|
+
# get all atoms' coordinates in the cell and its mirror cells
|
|
251
|
+
expanded_cart_coords = cart_coords + vecter_range[:, None]
|
|
252
|
+
# calculate the minimum image distance and get its index
|
|
253
|
+
lengths = np.linalg.norm(expanded_cart_coords, axis=2)
|
|
254
|
+
indices = np.argmin(lengths, axis=0)
|
|
255
|
+
minimum_vecters = expanded_cart_coords[indices, np.arange(len(cart_coords)), :]
|
|
256
|
+
vector_lengths = lengths[indices, np.arange(len(cart_coords))]
|
|
257
|
+
return minimum_vecters, vector_lengths
|
|
258
|
+
|
|
259
|
+
def __wrap_atoms(self, cart_coord):
|
|
260
|
+
"""If there is periodicity in this direction, wrap the specified component of the coordinate into the cell
|
|
261
|
+
Parameters
|
|
262
|
+
----------
|
|
263
|
+
numpy.ndarray
|
|
264
|
+
cartesian coordinate
|
|
265
|
+
|
|
266
|
+
Returns
|
|
267
|
+
-------
|
|
268
|
+
numpy.ndarray
|
|
269
|
+
cartesian coordinate
|
|
270
|
+
"""
|
|
271
|
+
pbc = [True, True, True]
|
|
272
|
+
frac_coord = self.get_fractional_coordinates(cart_coord)
|
|
273
|
+
for i, periodic in enumerate(pbc):
|
|
274
|
+
if periodic:
|
|
275
|
+
frac_coord[:, i] %= 1.0
|
|
276
|
+
return self.get_cartesian_coords(frac_coord)
|
|
277
|
+
|
|
278
|
+
def distance(self, coord1, coord2=None, mic: bool = True):
|
|
279
|
+
"""Get the minimum distance of to list of cartesian coordinates
|
|
280
|
+
|
|
281
|
+
Parameters
|
|
282
|
+
----------
|
|
283
|
+
coord1 : list | numpy.ndarray
|
|
284
|
+
笛卡尔坐标
|
|
285
|
+
coord2 : list | numpy.ndarray
|
|
286
|
+
笛卡尔坐标,by default None,即coord1为距离向量
|
|
287
|
+
mic : bool
|
|
288
|
+
最小像距离,by default True
|
|
289
|
+
|
|
290
|
+
Returns
|
|
291
|
+
-------
|
|
292
|
+
numpy.ndarray, numpy.ndarray
|
|
293
|
+
最小像距离向量,向量长度
|
|
294
|
+
"""
|
|
295
|
+
if coord2 is None:
|
|
296
|
+
distance_vector = np.array(coord1)
|
|
297
|
+
else:
|
|
298
|
+
cart_coord1 = np.array(coord1)
|
|
299
|
+
cart_coord2 = np.array(coord2)
|
|
300
|
+
distance_vector = cart_coord2 - cart_coord1
|
|
301
|
+
if not mic:
|
|
302
|
+
minimum_vector = distance_vector
|
|
303
|
+
vector_length = np.linalg.norm(minimum_vector)
|
|
304
|
+
else:
|
|
305
|
+
minimum_vector, vector_length = self.find_mic_distance(distance_vector)
|
|
306
|
+
return minimum_vector, vector_length
|
|
307
|
+
|
|
308
|
+
def find_mic_distance(self, vector: np.ndarray):
|
|
309
|
+
"""Get the minimum image distance of specific two sites
|
|
310
|
+
|
|
311
|
+
Parameters
|
|
312
|
+
----------
|
|
313
|
+
vector : numpy.ndarray
|
|
314
|
+
距离向量
|
|
315
|
+
|
|
316
|
+
Returns
|
|
317
|
+
-------
|
|
318
|
+
numpy.ndarray, numpy.ndarray
|
|
319
|
+
minimum image distance vector and its distance
|
|
320
|
+
"""
|
|
321
|
+
minimum_vector, vector_length = self.direct_find_mic(vector)
|
|
322
|
+
if vector_length < 0.5 * min(self.abc):
|
|
323
|
+
mic_flag = True
|
|
324
|
+
else:
|
|
325
|
+
mic_flag = False
|
|
326
|
+
minimum_vector = np.array(minimum_vector)
|
|
327
|
+
vector_length = np.array(vector_length)
|
|
328
|
+
if not mic_flag:
|
|
329
|
+
minimum_vector, vector_length = self.reduce_find_mic(vector)
|
|
330
|
+
return minimum_vector, vector_length
|
|
331
|
+
|
|
332
|
+
def direct_find_mic(self, vector: np.ndarray):
|
|
333
|
+
"""Calculate the minimum image distances,and use the result when the minimum image convention is satisfied.The minimum image convention i.e distance < min(a,b,c)/2
|
|
334
|
+
|
|
335
|
+
Parameters
|
|
336
|
+
----------
|
|
337
|
+
vector : numpy.ndarray
|
|
338
|
+
距离向量
|
|
339
|
+
|
|
340
|
+
Returns
|
|
341
|
+
-------
|
|
342
|
+
numpy.ndarray, numpy.ndarray
|
|
343
|
+
minimum image distance vector and its distance
|
|
344
|
+
"""
|
|
345
|
+
|
|
346
|
+
# convert to fractional coordinates
|
|
347
|
+
frac_coord = self.get_fractional_coordinates(vector)
|
|
348
|
+
# Control the fractional coordinate range to (-0.5,0.5)
|
|
349
|
+
frac_coord -= np.floor(frac_coord + 0.5)
|
|
350
|
+
minimum_vector = self.get_cartesian_coords(frac_coord)
|
|
351
|
+
vector_length = np.linalg.norm(minimum_vector)
|
|
352
|
+
# Returns the shortest vector and its length
|
|
353
|
+
return minimum_vector, vector_length
|
|
354
|
+
|
|
355
|
+
def reduce_find_mic(self, vector: np.ndarray):
|
|
356
|
+
"""If the minimum image convention is not satisfied, reduce the cell to re-calculate the minimum image distance
|
|
357
|
+
|
|
358
|
+
Parameters
|
|
359
|
+
----------
|
|
360
|
+
vector : numpy.ndarray
|
|
361
|
+
距离向量
|
|
362
|
+
|
|
363
|
+
Returns
|
|
364
|
+
-------
|
|
365
|
+
numpy.ndarray, numpy.ndarray
|
|
366
|
+
minimum image distance vectors and its distances
|
|
367
|
+
"""
|
|
368
|
+
pbc = [True, True, True]
|
|
369
|
+
cart_coord = self.wrap_atoms(vector)
|
|
370
|
+
# set the periodic range of three vector directions according to the periodic boundary conditions. (-1,1)
|
|
371
|
+
periodic_range = [np.arange(-1 * p, p + 1) for p in pbc]
|
|
372
|
+
hkl_range = list(product(*periodic_range))
|
|
373
|
+
vector_range = np.dot(hkl_range, self.cell_basis_vectors)
|
|
374
|
+
# get all atoms' coordinates in the cell and its mirror cells
|
|
375
|
+
expanded_cart_coords = []
|
|
376
|
+
for v in vector_range:
|
|
377
|
+
expanded_cart_coords.append(cart_coord + v)
|
|
378
|
+
# calculate the minimum image distance and get its index
|
|
379
|
+
lengths = np.linalg.norm(expanded_cart_coords, axis=1)
|
|
380
|
+
index = np.argmin(lengths, axis=0)
|
|
381
|
+
minimum_vector = expanded_cart_coords[index]
|
|
382
|
+
vector_length = lengths[index]
|
|
383
|
+
return np.array(minimum_vector), np.array(vector_length)
|
|
384
|
+
|
|
385
|
+
def wrap_atoms(self, cart_coord):
|
|
386
|
+
"""If there is periodicity in this direction, wrap the specified component of the coordinate into the cell
|
|
387
|
+
|
|
388
|
+
Parameters
|
|
389
|
+
----------
|
|
390
|
+
cart_coord : numpy.ndarray
|
|
391
|
+
cartesian coordinate
|
|
392
|
+
|
|
393
|
+
Returns
|
|
394
|
+
-------
|
|
395
|
+
numpy.ndarray
|
|
396
|
+
cartesian coordinate
|
|
397
|
+
"""
|
|
398
|
+
pbc = [True, True, True]
|
|
399
|
+
frac_coord = self.get_fractional_coordinates(cart_coord)
|
|
400
|
+
for i, periodic in enumerate(pbc):
|
|
401
|
+
if periodic:
|
|
402
|
+
frac_coord[i] %= 1.0
|
|
403
|
+
return self.get_cartesian_coords(frac_coord)
|
|
404
|
+
|
|
405
|
+
def get_reduced_cell(self, algorithm: str = 'niggli'):
|
|
406
|
+
"""Select the reduce algorithm to reduce the cell,i.e,1.niggli 2.minkowski
|
|
407
|
+
|
|
408
|
+
Parameters
|
|
409
|
+
----------
|
|
410
|
+
algorithm : str, optional
|
|
411
|
+
choose, by default 'niggli'
|
|
412
|
+
|
|
413
|
+
Returns
|
|
414
|
+
-------
|
|
415
|
+
Cell
|
|
416
|
+
the Cell object after reduced
|
|
417
|
+
"""
|
|
418
|
+
if algorithm == 'niggli':
|
|
419
|
+
new_cell_vectors = niggli_reduce(self._cell_basis_vectors)
|
|
420
|
+
else:
|
|
421
|
+
new_cell_vectors = self._cell_basis_vectors
|
|
422
|
+
return self.from_cell_vectors(new_cell_vectors)
|
|
423
|
+
|
|
424
|
+
def get_cartesian_coords(self, frac_coords: np.ndarray) -> np.ndarray:
|
|
425
|
+
"""get_cartesian_coords 从分数坐标得到笛卡尔坐标
|
|
426
|
+
|
|
427
|
+
Parameters
|
|
428
|
+
----------
|
|
429
|
+
frac_coords : np.ndarray
|
|
430
|
+
分数坐标
|
|
431
|
+
|
|
432
|
+
Returns
|
|
433
|
+
-------
|
|
434
|
+
np.ndarray
|
|
435
|
+
笛卡尔坐标
|
|
436
|
+
"""
|
|
437
|
+
return np.dot(frac_coords, self.cell_basis_vectors)
|
|
438
|
+
|
|
439
|
+
def get_fractional_coordinates(self, cart_coords: np.ndarray) -> np.ndarray:
|
|
440
|
+
"""get_Fractional_coordinates 从笛卡尔坐标得到分数坐标
|
|
441
|
+
|
|
442
|
+
Parameters
|
|
443
|
+
----------
|
|
444
|
+
cart_coords : np.ndarray
|
|
445
|
+
笛卡尔坐标
|
|
446
|
+
|
|
447
|
+
Returns
|
|
448
|
+
-------
|
|
449
|
+
np.ndarray
|
|
450
|
+
分数坐标
|
|
451
|
+
"""
|
|
452
|
+
return np.dot(cart_coords, np.linalg.inv(self.cell_basis_vectors))
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from scipy.interpolate import RegularGridInterpolator
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class PotentialField(object):
|
|
6
|
+
|
|
7
|
+
def __init__(
|
|
8
|
+
self,
|
|
9
|
+
Potential: np.ndarray,
|
|
10
|
+
basis_vector: np.ndarray = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
|
|
11
|
+
) -> None:
|
|
12
|
+
self.potential = Potential
|
|
13
|
+
self.compute_gradients()
|
|
14
|
+
self.create_interpolat_function()
|
|
15
|
+
self.basis_vector: np.ndarray = np.array([[1, 0, 0], [0, 1, 0],
|
|
16
|
+
[0, 0, 1]])
|
|
17
|
+
|
|
18
|
+
def create_interpolat_function(self):
|
|
19
|
+
data_shape = self.potential.shape
|
|
20
|
+
x = np.linspace(0, 1, data_shape[0])
|
|
21
|
+
y = np.linspace(0, 1, data_shape[1])
|
|
22
|
+
z = np.linspace(0, 1, data_shape[2])
|
|
23
|
+
self.potential_interp_function = RegularGridInterpolator(
|
|
24
|
+
(x, y, z), self.potential)
|
|
25
|
+
self.potential_gradients_interp_function_x = RegularGridInterpolator(
|
|
26
|
+
(x, y, z), self.potential_gradients[0][:][:][:])
|
|
27
|
+
self.potential_gradients_interp_function_y = RegularGridInterpolator(
|
|
28
|
+
(x, y, z), self.potential_gradients[1][:][:][:])
|
|
29
|
+
self.potential_gradients_interp_function_z = RegularGridInterpolator(
|
|
30
|
+
(x, y, z), self.potential_gradients[2][:][:][:])
|
|
31
|
+
|
|
32
|
+
def compute_gradients(self):
|
|
33
|
+
self.potential_gradients = np.gradient(self.potential)
|
|
34
|
+
|
|
35
|
+
def get_gradients(self, coords: np.ndarray):
|
|
36
|
+
return np.array([
|
|
37
|
+
self.potential_gradients_interp_function_x(coords),
|
|
38
|
+
self.potential_gradients_interp_function_y(coords),
|
|
39
|
+
self.potential_gradients_interp_function_z(coords)
|
|
40
|
+
])
|
|
41
|
+
|
|
42
|
+
def get_potentials(self, coords: np.ndarray):
|
|
43
|
+
return self.potential_interp_function(coords)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class PeriodPotentialField(PotentialField):
|
|
47
|
+
|
|
48
|
+
def __init__(
|
|
49
|
+
self,
|
|
50
|
+
Potential: np.ndarray,
|
|
51
|
+
basis_vector: np.ndarray = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
|
|
52
|
+
) -> None:
|
|
53
|
+
super().__init__(Potential, basis_vector)
|
|
54
|
+
|
|
55
|
+
def compute_gradients(self):
|
|
56
|
+
super().compute_gradients()
|
|
57
|
+
# period boundary condtion
|
|
58
|
+
self.potential_gradients[0][ 0][ :][ :] \
|
|
59
|
+
= (-self.potential[-2][ :][ :] + self.potential[1][ :][ :]) * 0.5
|
|
60
|
+
self.potential_gradients[0][ -1][ :][ :] \
|
|
61
|
+
= (-self.potential[-2][ :][ :] + self.potential[1][ :][ :]) * 0.5
|
|
62
|
+
self.potential_gradients[1][ :][ 0][ :] \
|
|
63
|
+
= (-self.potential[:][ -2][ :] + self.potential[:][ 1][ :]) * 0.5
|
|
64
|
+
self.potential_gradients[1][ :][ -1][ :] \
|
|
65
|
+
= (-self.potential[:][ -2][ :] + self.potential[:][ 1][ :]) * 0.5
|
|
66
|
+
self.potential_gradients[2][ :][ :][ 0] \
|
|
67
|
+
= (-self.potential[:][ :][ -2] + self.potential[:][ :][ 1]) * 0.5
|
|
68
|
+
self.potential_gradients[2][ :][ :][ -1] \
|
|
69
|
+
= (-self.potential[:][ :][ -2] + self.potential[:][ :][ 1]) * 0.5
|
|
70
|
+
|
|
71
|
+
def get_gradients(self, coords: np.ndarray):
|
|
72
|
+
fcoords = np.mod(coords, 1.0)
|
|
73
|
+
result = np.array([
|
|
74
|
+
self.potential_gradients_interp_function_x(fcoords)[0],
|
|
75
|
+
self.potential_gradients_interp_function_y(fcoords)[0],
|
|
76
|
+
self.potential_gradients_interp_function_z(fcoords)[0]
|
|
77
|
+
])
|
|
78
|
+
return result
|
|
79
|
+
|
|
80
|
+
def get_potentials(self, coords: np.ndarray):
|
|
81
|
+
return self.potential_interp_function(np.mod(coords, 1.0))
|