surface-construct 0.9.3__tar.gz → 0.10.1__tar.gz
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.
- {surface_construct-0.9.3/surface_construct.egg-info → surface_construct-0.10.1}/PKG-INFO +4 -1
- {surface_construct-0.9.3 → surface_construct-0.10.1}/README.md +3 -0
- {surface_construct-0.9.3 → surface_construct-0.10.1}/setup.py +1 -1
- {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/structures/surface.py +119 -72
- {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/structures/surface_grid.py +8 -5
- {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/utils/atoms.py +14 -18
- {surface_construct-0.9.3 → surface_construct-0.10.1/surface_construct.egg-info}/PKG-INFO +4 -1
- {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct.egg-info/SOURCES.txt +1 -0
- surface_construct-0.10.1/tests/test_simple_surface.py +39 -0
- {surface_construct-0.9.3 → surface_construct-0.10.1}/tests/test_surface_grid.py +3 -1
- {surface_construct-0.9.3 → surface_construct-0.10.1}/LICENSE +0 -0
- {surface_construct-0.9.3 → surface_construct-0.10.1}/setup.cfg +0 -0
- {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/__init__.py +0 -0
- {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/db.py +0 -0
- {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/default_parameter.py +0 -0
- {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/sg_sampler.py +0 -0
- {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/structures/__init__.py +0 -0
- {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/structures/adsorbate.py +0 -0
- {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/tasks/__init__.py +0 -0
- {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/tasks/sitesampling.py +0 -0
- {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/tasks/taskbase.py +0 -0
- {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/tasks/terminations.py +0 -0
- {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/utils/__init__.py +0 -0
- {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/utils/weight_functions.py +0 -0
- {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct.egg-info/dependency_links.txt +0 -0
- {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct.egg-info/requires.txt +0 -0
- {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct.egg-info/top_level.txt +0 -0
- {surface_construct-0.9.3 → surface_construct-0.10.1}/tests/test_sampling1.py +0 -0
- {surface_construct-0.9.3 → surface_construct-0.10.1}/tests/test_sampling2.py +0 -0
- {surface_construct-0.9.3 → surface_construct-0.10.1}/tests/test_task.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: surface_construct
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.1
|
|
4
4
|
Summary: Surface termination construction especially for complex model, such as oxides or carbides.
|
|
5
5
|
Home-page: https://gitee.com/pjren/surface_construct/
|
|
6
6
|
Author: ren
|
|
@@ -158,6 +158,8 @@ $$
|
|
|
158
158
|
|
|
159
159
|
**TODO**
|
|
160
160
|
|
|
161
|
+
* 计算位点 SALI (Structure-Activity Landscape Index),量化 activity cliffs,SALI = |ΔActivity| / (1 − Similarity)
|
|
162
|
+
* 新增 PathSampler,用于在相邻的关键格点之间的扩散路径进行采样,使用 ase.neb.idpp 方法进行采样,使用 Delaunay 剖分找到相邻位点的路径。
|
|
161
163
|
* 表面位点数据库
|
|
162
164
|
* 多原子体系(内坐标受限体系)
|
|
163
165
|
* 完善用户界面、例子、教程
|
|
@@ -167,5 +169,6 @@ $$
|
|
|
167
169
|
|
|
168
170
|
[^Duvenaud]: [The Kernel Cookbook: Advice on Covariance functions](https://www.cs.toronto.edu/~duvenaud/)]
|
|
169
171
|
[^BASC]: Shane Carr, Roman Garnett, Cynthia LoBASC: Applying Bayesian Optimization to the Search for Global Minima on Potential Energy Surfaces.
|
|
172
|
+
|
|
170
173
|
[^向量空间转化]: 计算实空间和向量空间的相邻格点距离的映射系数,根据此系数将实空间的距离转化为向量空间距离。
|
|
171
174
|
|
|
@@ -128,6 +128,8 @@ $$
|
|
|
128
128
|
|
|
129
129
|
**TODO**
|
|
130
130
|
|
|
131
|
+
* 计算位点 SALI (Structure-Activity Landscape Index),量化 activity cliffs,SALI = |ΔActivity| / (1 − Similarity)
|
|
132
|
+
* 新增 PathSampler,用于在相邻的关键格点之间的扩散路径进行采样,使用 ase.neb.idpp 方法进行采样,使用 Delaunay 剖分找到相邻位点的路径。
|
|
131
133
|
* 表面位点数据库
|
|
132
134
|
* 多原子体系(内坐标受限体系)
|
|
133
135
|
* 完善用户界面、例子、教程
|
|
@@ -137,5 +139,6 @@ $$
|
|
|
137
139
|
|
|
138
140
|
[^Duvenaud]: [The Kernel Cookbook: Advice on Covariance functions](https://www.cs.toronto.edu/~duvenaud/)]
|
|
139
141
|
[^BASC]: Shane Carr, Roman Garnett, Cynthia LoBASC: Applying Bayesian Optimization to the Search for Global Minima on Potential Energy Surfaces.
|
|
142
|
+
|
|
140
143
|
[^向量空间转化]: 计算实空间和向量空间的相邻格点距离的映射系数,根据此系数将实空间的距离转化为向量空间距离。
|
|
141
144
|
|
{surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/structures/surface.py
RENAMED
|
@@ -7,7 +7,7 @@ import pandas as pd
|
|
|
7
7
|
import spglib
|
|
8
8
|
from tqdm import tqdm
|
|
9
9
|
|
|
10
|
-
from surface_construct.utils.atoms import topo_identity, bondvalence, \
|
|
10
|
+
from surface_construct.utils.atoms import topo_identity,dist_identity, get_atoms_topo_id, bondvalence, \
|
|
11
11
|
refined_atoms, atoms2spg, coprime_factor, adj_matrix, topodist_matrix, atoms2graph, coordnum_adj
|
|
12
12
|
|
|
13
13
|
|
|
@@ -103,43 +103,31 @@ class Crystal(object):
|
|
|
103
103
|
|
|
104
104
|
|
|
105
105
|
class Surface:
|
|
106
|
-
def __init__(self, crystal, hkl
|
|
106
|
+
def __init__(self, crystal, hkl):
|
|
107
107
|
self.crystal = crystal
|
|
108
108
|
self.hkl = hkl
|
|
109
109
|
self.reduced_hkl = reduce_miller_indices(hkl)
|
|
110
110
|
self.primary_hkl, self.peer_hkl = get_symmetry_miller(crystal.atoms,
|
|
111
111
|
self.reduced_hkl)
|
|
112
|
-
self._refine =
|
|
112
|
+
self._refine = None
|
|
113
113
|
self._area = None
|
|
114
114
|
self._surface_parameter = None
|
|
115
|
+
self.original_surface_parameter = None
|
|
115
116
|
|
|
116
117
|
@property
|
|
117
|
-
def surface_parameter(self):
|
|
118
|
-
if self._surface_parameter is None:
|
|
119
|
-
slab = self.create_slab()
|
|
118
|
+
def surface_parameter(self, refine=False):
|
|
119
|
+
if refine != self._refine or self._surface_parameter is None:
|
|
120
|
+
slab = self.create_slab(refine=refine)
|
|
120
121
|
len_ang = slab.atoms.cell.cellpar()
|
|
121
122
|
self._surface_parameter = len_ang
|
|
122
123
|
return self._surface_parameter
|
|
123
124
|
|
|
124
|
-
@
|
|
125
|
-
def
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
@refine.setter
|
|
129
|
-
def refine(self, v):
|
|
130
|
-
v = bool(v)
|
|
131
|
-
if v != self._refine:
|
|
132
|
-
self._refine = v
|
|
133
|
-
# 重设surface_parameter,重新计算
|
|
134
|
-
self._surface_parameter = None
|
|
135
|
-
self._area = None
|
|
136
|
-
else: # do nothing
|
|
137
|
-
pass
|
|
125
|
+
@surface_parameter.setter
|
|
126
|
+
def surface_parameter(self, v):
|
|
127
|
+
self._surface_parameter = v
|
|
138
128
|
|
|
139
|
-
def create_slab(self, layers=3, vacuum=15.0, atoms=None, refine=None):
|
|
140
|
-
|
|
141
|
-
refine = self.refine
|
|
142
|
-
slab = Slab(self, layers, vacuum, atoms=atoms, refine=refine)
|
|
129
|
+
def create_slab(self, layers=3, vacuum=15.0, atoms=None, refine=None, from_last=False):
|
|
130
|
+
slab = Slab(self, layers, vacuum, atoms=atoms, refine=refine, from_last=from_last)
|
|
143
131
|
return slab
|
|
144
132
|
|
|
145
133
|
@property
|
|
@@ -151,15 +139,25 @@ class Surface:
|
|
|
151
139
|
|
|
152
140
|
return self._area
|
|
153
141
|
|
|
142
|
+
@area.setter
|
|
143
|
+
def area(self,v):
|
|
144
|
+
self._area = v
|
|
154
145
|
|
|
155
146
|
class Slab(object):
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
147
|
+
def __init__(self, surface, layers=3, vacuum=15.0, atoms=None, refine=False, from_last=False):
|
|
148
|
+
"""
|
|
149
|
+
TODO: construct from atoms without surface
|
|
150
|
+
:param surface:
|
|
151
|
+
:param layers:
|
|
152
|
+
:param vacuum:
|
|
153
|
+
:param atoms:
|
|
154
|
+
:param refine:
|
|
155
|
+
:param from_last: peel atoms from last index, default is from first.
|
|
156
|
+
"""
|
|
160
157
|
self.surface = surface
|
|
161
158
|
self.layers = layers
|
|
162
159
|
self.vacuum = vacuum
|
|
160
|
+
self.from_last = from_last
|
|
163
161
|
if not atoms:
|
|
164
162
|
atoms = ase.build.surface(surface.crystal.atoms,
|
|
165
163
|
surface.primary_hkl,
|
|
@@ -169,7 +167,7 @@ class Slab(object):
|
|
|
169
167
|
if refine:
|
|
170
168
|
atoms = refined_atoms(atoms)
|
|
171
169
|
|
|
172
|
-
self.refine =
|
|
170
|
+
self.refine = refine # 构造好了就要再更改了
|
|
173
171
|
self.atoms = atoms
|
|
174
172
|
self.natoms = len(self.atoms)
|
|
175
173
|
self.natoms_per_layer = int(self.natoms / layers) # Dirty: atoms 变量必须包含整数倍的层
|
|
@@ -213,14 +211,19 @@ class Slab(object):
|
|
|
213
211
|
self._adj_matrix = adj_matrix(self.atoms)
|
|
214
212
|
return self._adj_matrix
|
|
215
213
|
|
|
216
|
-
def supercell(self, size=(1, 1
|
|
217
|
-
|
|
214
|
+
def supercell(self, size=(1, 1)):
|
|
215
|
+
"""
|
|
216
|
+
TODO: super size the surface too.
|
|
217
|
+
:param size:
|
|
218
|
+
:return:
|
|
219
|
+
"""
|
|
220
|
+
multiple = size[0] * size[1]
|
|
218
221
|
if multiple == 1:
|
|
219
222
|
return self
|
|
220
223
|
# make supercell
|
|
221
224
|
P = [[size[0], 0, 0],
|
|
222
225
|
[0, size[1], 0],
|
|
223
|
-
[0, 0,
|
|
226
|
+
[0, 0, 1]]
|
|
224
227
|
extended_atoms = ase.build.make_supercell(self.atoms, P, wrap=False) # Note: wrap may change the atoms order.
|
|
225
228
|
# reorder atoms
|
|
226
229
|
new_atoms = ase.Atoms(cell=extended_atoms.cell, pbc=extended_atoms.pbc)
|
|
@@ -230,8 +233,10 @@ class Slab(object):
|
|
|
230
233
|
atoms_id = [i + self.natoms_per_layer * ilayer + self.natoms * icell
|
|
231
234
|
for i in range(self.natoms_per_layer)]
|
|
232
235
|
new_atoms.extend(extended_atoms[atoms_id])
|
|
233
|
-
|
|
234
|
-
super_slab = Slab(self.surface, self.layers, vacuum=self.vacuum, atoms=new_atoms)
|
|
236
|
+
# TODO: create new super surface
|
|
237
|
+
super_slab = Slab(self.surface, self.layers, vacuum=self.vacuum, atoms=new_atoms, from_last=self.from_last)
|
|
238
|
+
super_slab.surface.surface_parameter = new_atoms.cell.cellpar()
|
|
239
|
+
super_slab.surface.area = None
|
|
235
240
|
super_slab.refine = True # 不能在初始化时定义,否则会重构成单胞。但是这里为什么要定义为 True 呢?
|
|
236
241
|
return super_slab
|
|
237
242
|
|
|
@@ -259,6 +264,10 @@ class Slab(object):
|
|
|
259
264
|
|
|
260
265
|
@property
|
|
261
266
|
def all_cn(self):
|
|
267
|
+
"""
|
|
268
|
+
Slab 中所有原子的配位数
|
|
269
|
+
:return: 配位数列表,[{(原子序号,原子序号): 数量}]
|
|
270
|
+
"""
|
|
262
271
|
if self._all_cn is None:
|
|
263
272
|
self._all_cn = coordnum_adj(self.adj_matrix)
|
|
264
273
|
|
|
@@ -266,19 +275,34 @@ class Slab(object):
|
|
|
266
275
|
|
|
267
276
|
@property
|
|
268
277
|
def bulk_cn(self):
|
|
278
|
+
"""
|
|
279
|
+
Slab 中所有体相位置原子的配位数
|
|
280
|
+
:return: 配位数列表,[{(原子序号,原子序号): 数量}]
|
|
281
|
+
"""
|
|
269
282
|
if self._bulk_cn is None:
|
|
270
283
|
self._bulk_cn = [self.all_cn[i] for i in self.bulk_id]
|
|
271
284
|
return self._bulk_cn
|
|
272
285
|
|
|
273
286
|
@property
|
|
274
287
|
def skin_id(self): # Dirty trick
|
|
288
|
+
"""
|
|
289
|
+
表层 skin 的原子序号。假设体系有三层,以第一层为表层。
|
|
290
|
+
:return:
|
|
291
|
+
"""
|
|
275
292
|
if self._skin_id is None:
|
|
276
|
-
|
|
277
|
-
|
|
293
|
+
if self.from_last:
|
|
294
|
+
self._skin_id = list(range(self.natoms_per_layer * (self.layers - 1),
|
|
295
|
+
self.natoms_per_layer * self.layers))
|
|
296
|
+
else:
|
|
297
|
+
self._skin_id = list(range(0, self.natoms_per_layer))
|
|
278
298
|
return self._skin_id
|
|
279
299
|
|
|
280
300
|
@property
|
|
281
301
|
def bulk_id(self): # Dirty trick
|
|
302
|
+
"""
|
|
303
|
+
体相原子的原子序号。假设体系有三层,以中间层为表层。
|
|
304
|
+
:return:
|
|
305
|
+
"""
|
|
282
306
|
if self._bulk_id is None:
|
|
283
307
|
self._bulk_id = list(range(self.natoms_per_layer,
|
|
284
308
|
self.natoms_per_layer * (self.layers - 1)))
|
|
@@ -286,9 +310,16 @@ class Slab(object):
|
|
|
286
310
|
|
|
287
311
|
@property
|
|
288
312
|
def bottom_id(self): # Dirty trick
|
|
313
|
+
"""
|
|
314
|
+
底层原子的原子序号。假设体系有三层,以最下层为底层。
|
|
315
|
+
:return:
|
|
316
|
+
"""
|
|
289
317
|
if self._bottom_id is None:
|
|
290
|
-
|
|
291
|
-
|
|
318
|
+
if not self.from_last:
|
|
319
|
+
self._bottom_id = list(range(self.natoms_per_layer * (self.layers - 1),
|
|
320
|
+
self.natoms_per_layer * self.layers))
|
|
321
|
+
else:
|
|
322
|
+
self._bottom_id = list(range(0, self.natoms_per_layer))
|
|
292
323
|
return self._bottom_id
|
|
293
324
|
|
|
294
325
|
def estimate_terminations(self, sheet_window=0.5):
|
|
@@ -324,18 +355,19 @@ class Slab(object):
|
|
|
324
355
|
return self._sheet_list
|
|
325
356
|
|
|
326
357
|
pos = self.atoms.positions
|
|
327
|
-
|
|
358
|
+
skin_id = self.skin_id
|
|
359
|
+
posz = pos[skin_id, 2]
|
|
328
360
|
zmin, zmax = pos[:, 2].min(), pos[:, 2].max()
|
|
329
361
|
posz = posz - zmin if posz[0] - zmin < zmax - posz[0] else zmax - posz
|
|
330
|
-
|
|
331
|
-
posz = [i[1] for i in
|
|
332
|
-
posz_idx = [i[0] for i in
|
|
362
|
+
sorted_posz = sorted(zip(skin_id,posz), key=lambda lst: lst[1])
|
|
363
|
+
posz = [i[1] for i in sorted_posz]
|
|
364
|
+
posz_idx = [i[0] for i in sorted_posz] # 由小到大排列,从外表面到内部
|
|
333
365
|
posz_mx, posz_my = np.meshgrid(posz, posz)
|
|
334
366
|
posz_dist = np.abs(posz_mx - posz_my)
|
|
335
367
|
idx_row, idx_col = np.argwhere(posz_dist < self.sheet_window).T
|
|
336
|
-
idx_row_set = set(idx_row)
|
|
368
|
+
idx_row_set = sorted([[i, posz[i]] for i in set(idx_row)], key=lambda lst: lst[1]) # 从外表面到內表面排序,否则在切除的时候会混乱
|
|
337
369
|
sheet_list = []
|
|
338
|
-
for ir in idx_row_set:
|
|
370
|
+
for ir,_ in idx_row_set:
|
|
339
371
|
idx_col_set = set([posz_idx[i] for i in idx_col[idx_row == ir]])
|
|
340
372
|
if idx_col_set in sheet_list:
|
|
341
373
|
continue
|
|
@@ -357,6 +389,7 @@ class Slab(object):
|
|
|
357
389
|
peel_list = []
|
|
358
390
|
base_peel = set()
|
|
359
391
|
for sheet in self.sheet_list:
|
|
392
|
+
base_peel.difference_update(sheet) # 需要把本层相关的去掉
|
|
360
393
|
peel_list += [base_peel.union(set(idx_list)) for n in range(1, len(sheet) + 1)
|
|
361
394
|
for idx_list in itertools.combinations(sheet, n)]
|
|
362
395
|
base_peel.update(sheet)
|
|
@@ -365,7 +398,7 @@ class Slab(object):
|
|
|
365
398
|
|
|
366
399
|
return self._peel_set
|
|
367
400
|
|
|
368
|
-
def get_all_terminations(self, sheet_window=0.5, unique=False):
|
|
401
|
+
def get_all_terminations(self, sheet_window=0.5, unique=False, id_type='topo'):
|
|
369
402
|
"""
|
|
370
403
|
Extract all the termination for given miller indices
|
|
371
404
|
|
|
@@ -379,7 +412,7 @@ class Slab(object):
|
|
|
379
412
|
terminations = [self.create_terminations(peel) for peel in peel_set]
|
|
380
413
|
|
|
381
414
|
if unique:
|
|
382
|
-
terminations_dict = {termination.get_identity(): termination
|
|
415
|
+
terminations_dict = {termination.get_identity(id_type=id_type): termination
|
|
383
416
|
for termination in tqdm(terminations)}
|
|
384
417
|
print("Unique terminations count, ", len(terminations_dict))
|
|
385
418
|
return terminations_dict
|
|
@@ -422,18 +455,22 @@ class Termination(object):
|
|
|
422
455
|
self.bulk_id = slab.bulk_id
|
|
423
456
|
self.bottom_id = slab.bottom_id
|
|
424
457
|
self.dermis_id = sorted(list(set(self.skin_id) - set(peel_id))) # 真皮层,为什么要排序?
|
|
425
|
-
self.termination_id =
|
|
458
|
+
self.termination_id = [idx for idx in range(self.slab.natoms) if idx not in peel_id]
|
|
426
459
|
|
|
427
460
|
self.atoms = atoms
|
|
428
461
|
if atoms is None:
|
|
429
462
|
self.atoms = self.slab.atoms[self.termination_id]
|
|
430
463
|
|
|
431
464
|
# 根据 termination.atoms 重新对原子编号
|
|
432
|
-
self.
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
465
|
+
if self.slab.from_last:
|
|
466
|
+
# TODO: dirty, add the case for both side, or disordered the index
|
|
467
|
+
self.atoms_dermis_id = np.arange(len(self.atoms)-len(self.dermis_id), len(self.atoms))
|
|
468
|
+
self.atoms_bulk_id = self.bulk_id[:]
|
|
469
|
+
self.atoms_bottom_id = self.bottom_id[:]
|
|
470
|
+
else:
|
|
471
|
+
self.atoms_dermis_id = np.arange(len(self.dermis_id))
|
|
472
|
+
self.atoms_bulk_id = np.arange(len(self.dermis_id), len(self.dermis_id + self.bulk_id))
|
|
473
|
+
self.atoms_bottom_id = np.arange(len(self.dermis_id + self.bulk_id), len(self.atoms))
|
|
437
474
|
|
|
438
475
|
self._adj_matrix = None
|
|
439
476
|
self._all_cn = None
|
|
@@ -445,6 +482,7 @@ class Termination(object):
|
|
|
445
482
|
|
|
446
483
|
self._thickness = None
|
|
447
484
|
self.identity = None
|
|
485
|
+
self.identity_type = None
|
|
448
486
|
self.dsuc = None
|
|
449
487
|
|
|
450
488
|
@property
|
|
@@ -488,7 +526,9 @@ class Termination(object):
|
|
|
488
526
|
:return:
|
|
489
527
|
"""
|
|
490
528
|
if self._atoms_cus_id is None:
|
|
491
|
-
|
|
529
|
+
skin_idx = list(self.atoms_dermis_id) + list(self.atoms_bulk_id)
|
|
530
|
+
self._atoms_cus_id = [idx for idx, item in zip(skin_idx, self.cus_cn)
|
|
531
|
+
if any([i != 0 for i in item.values()])]
|
|
492
532
|
return self._atoms_cus_id
|
|
493
533
|
|
|
494
534
|
@property
|
|
@@ -500,7 +540,7 @@ class Termination(object):
|
|
|
500
540
|
"""
|
|
501
541
|
if self._cus_id is None:
|
|
502
542
|
slab_idx = self.dermis_id + self.bulk_id
|
|
503
|
-
self._cus_id = [idx for idx, item in zip(slab_idx, self.
|
|
543
|
+
self._cus_id = [idx for idx, item in zip(slab_idx, self.cus_cn)
|
|
504
544
|
if any([i != 0 for i in item.values()])]
|
|
505
545
|
|
|
506
546
|
return self._cus_id
|
|
@@ -516,9 +556,13 @@ class Termination(object):
|
|
|
516
556
|
slab_bulk_cn = self.slab.bulk_cn
|
|
517
557
|
result = [] # idx is the id of self.atoms
|
|
518
558
|
for idx, item in zip(self.dermis_id, self.dermis_cn): # Must take all surface layer into account.
|
|
559
|
+
if self.slab.from_last:
|
|
560
|
+
i = idx - (self.layers - 1) * self.natoms_per_layer
|
|
561
|
+
else:
|
|
562
|
+
i = idx
|
|
519
563
|
cus_item = {}
|
|
520
564
|
for k in item:
|
|
521
|
-
cus_item[k] = slab_bulk_cn[
|
|
565
|
+
cus_item[k] = slab_bulk_cn[i][k] - item[k]
|
|
522
566
|
result.append(cus_item)
|
|
523
567
|
for idx, item in zip(self.bulk_id, terminal_bulk_cn):
|
|
524
568
|
cus_item = {}
|
|
@@ -528,16 +572,10 @@ class Termination(object):
|
|
|
528
572
|
|
|
529
573
|
self._cus_cn = result # _cus_cn 中不仅有cus的cn,还有其他的cn,只是都是0而已
|
|
530
574
|
|
|
531
|
-
# self._atoms_cus_id 也在此处定义
|
|
532
|
-
if self._atoms_cus_id is None:
|
|
533
|
-
self._atoms_cus_id = [idx for idx, item in enumerate(self._cus_cn)
|
|
534
|
-
if any([i != 0 for i in item.values()])]
|
|
535
|
-
|
|
536
575
|
# 接下来,我们构造新的 cus_cn
|
|
537
|
-
# cus_cn 是一个dict:{
|
|
538
|
-
cus_cn = {idx: self._cus_cn[idx] for idx in self.
|
|
539
|
-
|
|
540
|
-
return cus_cn
|
|
576
|
+
# cus_cn 是一个dict:{cus_id: cn_dict}
|
|
577
|
+
# cus_cn = {idx: self._cus_cn[idx] for idx in self.cus_id}
|
|
578
|
+
return self._cus_cn
|
|
541
579
|
|
|
542
580
|
def pseudo_layer(self, fromslab=False):
|
|
543
581
|
"""
|
|
@@ -555,19 +593,28 @@ class Termination(object):
|
|
|
555
593
|
|
|
556
594
|
return np.concatenate([dermis, complement])
|
|
557
595
|
|
|
558
|
-
def get_identity(self):
|
|
559
|
-
if not self.identity:
|
|
596
|
+
def get_identity(self, id_type='topo'):
|
|
597
|
+
if (not self.identity) or (self.identity_type!=id_type):
|
|
560
598
|
pseudo_skin_atoms = self.atoms[self.atoms_cus_id]
|
|
561
599
|
# add ghost at the top of the surface, in order to distinguish the surface
|
|
562
600
|
pseudo_skin_z = pseudo_skin_atoms.positions[:, 2]
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
601
|
+
if self.slab.from_last: # dirty: index 0 at bottom and max index at top
|
|
602
|
+
tips = np.concatenate(np.argwhere(pseudo_skin_z == pseudo_skin_z.max())) # get the tip atoms id
|
|
603
|
+
ghost_xyz = pseudo_skin_atoms.positions[tips]
|
|
604
|
+
ghost_xyz[:, 2] = ghost_xyz[:, 2] + 0.5
|
|
605
|
+
else:
|
|
606
|
+
tips = np.concatenate(np.argwhere(pseudo_skin_z == pseudo_skin_z.min())) # get the tip atoms id
|
|
607
|
+
ghost_xyz = pseudo_skin_atoms.positions[tips]
|
|
608
|
+
ghost_xyz[:, 2] = ghost_xyz[:, 2] - 0.5
|
|
566
609
|
pseudo_skin_atoms.extend(ase.Atoms('X' + str(len(ghost_xyz)), ghost_xyz))
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
610
|
+
if id_type == 'topo':
|
|
611
|
+
identity = get_atoms_topo_id(pseudo_skin_atoms)
|
|
612
|
+
elif id_type == 'dist':
|
|
613
|
+
identity = dist_identity(pseudo_skin_atoms)
|
|
614
|
+
else:
|
|
615
|
+
raise NotImplementedError
|
|
570
616
|
self.identity = identity
|
|
617
|
+
self.identity_type = id_type
|
|
571
618
|
return self.identity
|
|
572
619
|
|
|
573
620
|
def get_identity_fast(self):
|
{surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/structures/surface_grid.py
RENAMED
|
@@ -424,10 +424,13 @@ class SurfaceGrid:
|
|
|
424
424
|
self.rcutoff = rcutoff
|
|
425
425
|
|
|
426
426
|
if type(rsub) in (list, tuple, np.ndarray):
|
|
427
|
-
|
|
427
|
+
if not len(rsub) == len(atoms):
|
|
428
|
+
raise ValueError("Not all atoms have radius!")
|
|
428
429
|
self.rsub = rsub
|
|
429
430
|
elif type(rsub) == dict:
|
|
430
431
|
self.rsub = [rsub.get(n) or rsub.get(chemical_symbols(n)) for n in atoms.numbers]
|
|
432
|
+
if None in self.rsub:
|
|
433
|
+
raise ValueError("Not all elements have radius!")
|
|
431
434
|
elif type(rsub) == str:
|
|
432
435
|
if rsub == 'covalent_radii' or rsub == 'natural_cutoff':
|
|
433
436
|
self.rsub = natural_cutoffs(atoms)
|
|
@@ -726,9 +729,8 @@ class SurfaceGrid:
|
|
|
726
729
|
:return:
|
|
727
730
|
"""
|
|
728
731
|
kwargs['pbc'] = kwargs.get('pbc',self.pbc)
|
|
729
|
-
kwargs['r0_dict'] = kwargs.get('r0_dict',
|
|
730
|
-
|
|
731
|
-
for atomnum,_ in self.species})
|
|
732
|
+
kwargs['r0_dict'] = kwargs.get('r0_dict', {atomnum:self.rads+r
|
|
733
|
+
for atomnum,r in zip(self.atoms.numbers, self.rsub)})
|
|
732
734
|
vgen = MLATVectorGen(self.atoms, vlen=self.vlen, weight_func=wf,
|
|
733
735
|
lpca=self.lpca, pca_ratio=pca_ratio,
|
|
734
736
|
rcutoff=self.rcutoff, **kwargs)
|
|
@@ -765,7 +767,8 @@ class SurfaceGrid:
|
|
|
765
767
|
sc = [min(vlen,c) for s,c in self.species]
|
|
766
768
|
v0 = np.concatenate(np.array([[np.inf]*c for c in sc]))
|
|
767
769
|
for idx, s in enumerate(self.species):
|
|
768
|
-
|
|
770
|
+
num = list(self.atoms.numbers).index(s[0])
|
|
771
|
+
r = self.rsub[num] + self.rads
|
|
769
772
|
v1 = v0.copy() # top
|
|
770
773
|
idx0 = sum(sc[:idx])
|
|
771
774
|
v1[idx0] = r
|
|
@@ -10,21 +10,6 @@ from hashlib import md5
|
|
|
10
10
|
import spglib
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
ATOM_COORDNUM = {
|
|
14
|
-
0: 1,
|
|
15
|
-
1: 1,
|
|
16
|
-
6: 4,
|
|
17
|
-
7: 3,
|
|
18
|
-
8: 2,
|
|
19
|
-
26: 12,
|
|
20
|
-
} # Maximum coordinate number for organic neutral mols.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
INITIAL_MAGMOM = {
|
|
24
|
-
26: 3,
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
28
13
|
def default_bv_parameter(atomnums=None):
|
|
29
14
|
# 使用共价键长之和来生成
|
|
30
15
|
radii = ase.data.covalent_radii.copy()
|
|
@@ -37,9 +22,6 @@ def default_bv_parameter(atomnums=None):
|
|
|
37
22
|
return bv_parameter
|
|
38
23
|
|
|
39
24
|
|
|
40
|
-
BONDVALENCE_PARAMETERS = default_bv_parameter()
|
|
41
|
-
|
|
42
|
-
|
|
43
25
|
def round10(x):
|
|
44
26
|
return round(x, 10)
|
|
45
27
|
|
|
@@ -209,6 +191,8 @@ def get_sub_matrix(mat, lst):
|
|
|
209
191
|
def topo_identity(dm):
|
|
210
192
|
"""
|
|
211
193
|
计算 topo 结构的标识。
|
|
194
|
+
If consider chirality of molecules, need add three non-collinear
|
|
195
|
+
points of cartesian coordinates. 通过主轴和二轴添加
|
|
212
196
|
:param dm: topodist_matrix
|
|
213
197
|
:return:
|
|
214
198
|
"""
|
|
@@ -231,6 +215,18 @@ def get_atoms_topo_id(atoms, *args, **kwargs):
|
|
|
231
215
|
return ids
|
|
232
216
|
|
|
233
217
|
|
|
218
|
+
def dist_identity(atoms, mic=True):
|
|
219
|
+
dm = atoms.get_all_distances(mic=mic)
|
|
220
|
+
n, m = dm.shape
|
|
221
|
+
I = np.identity(n)
|
|
222
|
+
dm = dm + atoms.get_atomic_numbers() * I # set diagonal elements as atomic number
|
|
223
|
+
eigval = np.linalg.eigvalsh(dm).round(5)
|
|
224
|
+
content = ' '.join(map(str, np.sort(eigval)))
|
|
225
|
+
identity = md5(content.encode('utf-8')).hexdigest()
|
|
226
|
+
|
|
227
|
+
return identity
|
|
228
|
+
|
|
229
|
+
|
|
234
230
|
def getghost(atoms):
|
|
235
231
|
# get X index
|
|
236
232
|
x = [idx for idx, i in enumerate(atoms.get_chemical_symbols()) if i == 'X']
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: surface_construct
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.1
|
|
4
4
|
Summary: Surface termination construction especially for complex model, such as oxides or carbides.
|
|
5
5
|
Home-page: https://gitee.com/pjren/surface_construct/
|
|
6
6
|
Author: ren
|
|
@@ -158,6 +158,8 @@ $$
|
|
|
158
158
|
|
|
159
159
|
**TODO**
|
|
160
160
|
|
|
161
|
+
* 计算位点 SALI (Structure-Activity Landscape Index),量化 activity cliffs,SALI = |ΔActivity| / (1 − Similarity)
|
|
162
|
+
* 新增 PathSampler,用于在相邻的关键格点之间的扩散路径进行采样,使用 ase.neb.idpp 方法进行采样,使用 Delaunay 剖分找到相邻位点的路径。
|
|
161
163
|
* 表面位点数据库
|
|
162
164
|
* 多原子体系(内坐标受限体系)
|
|
163
165
|
* 完善用户界面、例子、教程
|
|
@@ -167,5 +169,6 @@ $$
|
|
|
167
169
|
|
|
168
170
|
[^Duvenaud]: [The Kernel Cookbook: Advice on Covariance functions](https://www.cs.toronto.edu/~duvenaud/)]
|
|
169
171
|
[^BASC]: Shane Carr, Roman Garnett, Cynthia LoBASC: Applying Bayesian Optimization to the Search for Global Minima on Potential Energy Surfaces.
|
|
172
|
+
|
|
170
173
|
[^向量空间转化]: 计算实空间和向量空间的相邻格点距离的映射系数,根据此系数将实空间的距离转化为向量空间距离。
|
|
171
174
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
import ase.build
|
|
3
|
+
from surface_construct.structures.surface import Surface, Crystal, Slab
|
|
4
|
+
|
|
5
|
+
class TestSimpleSurface:
|
|
6
|
+
def test_Cu111_termination(self):
|
|
7
|
+
bulk = ase.build.bulk('Cu')
|
|
8
|
+
c = Crystal(bulk)
|
|
9
|
+
s = Surface(c, (1,1,1))
|
|
10
|
+
|
|
11
|
+
slab1 = Slab(s)
|
|
12
|
+
superslab1 = slab1.supercell((3,3))
|
|
13
|
+
terms = superslab1.get_all_terminations(unique=True)
|
|
14
|
+
atoms_list = [t.atoms for t in terms]
|
|
15
|
+
|
|
16
|
+
slab2 = Slab(s, from_last=True)
|
|
17
|
+
superslab2 = slab2.supercell((3,3))
|
|
18
|
+
terms_last = superslab2.get_all_terminations(unique=True)
|
|
19
|
+
assert len(terms) == len(terms_last) == 23
|
|
20
|
+
assert set(terms) == set(terms_last)
|
|
21
|
+
|
|
22
|
+
def test_Cu211_termination(self):
|
|
23
|
+
bulk = ase.build.bulk('Cu')
|
|
24
|
+
c = Crystal(bulk)
|
|
25
|
+
s = Surface(c, (2,1,1))
|
|
26
|
+
|
|
27
|
+
slab1 = Slab(s, layers=6)
|
|
28
|
+
slab1.set_layers(3)
|
|
29
|
+
superslab1 = slab1.supercell((2,3))
|
|
30
|
+
terms = superslab1.get_all_terminations(unique=True)
|
|
31
|
+
|
|
32
|
+
slab2 = Slab(s, from_last=True, layers=6)
|
|
33
|
+
slab2.set_layers(3)
|
|
34
|
+
superslab2 = slab2.supercell((2,3))
|
|
35
|
+
terms_last = superslab2.get_all_terminations(unique=True)
|
|
36
|
+
assert len(terms) == len(terms_last)
|
|
37
|
+
assert set(terms) == set(terms_last)
|
|
38
|
+
|
|
39
|
+
|
|
@@ -53,6 +53,8 @@ class TestSurfaceGrid:
|
|
|
53
53
|
sg_obj.vectorize()
|
|
54
54
|
graph = sg_obj.grid_graph
|
|
55
55
|
vip = sg_obj.vip_id
|
|
56
|
+
uvip = sg_obj.unique_vip_id
|
|
57
|
+
uvip_points = sg_obj.points[uvip] # [[x,y,z],....] Nx3
|
|
56
58
|
print(len(vip))
|
|
57
59
|
print(sg_obj.vector_unit)
|
|
58
60
|
print(len(sg_obj.vector))
|
|
@@ -92,7 +94,7 @@ class TestSurfaceGrid:
|
|
|
92
94
|
|
|
93
95
|
def test_ZSM5(self):
|
|
94
96
|
atoms = ase.io.read('atoms_files/zsm5.cif')
|
|
95
|
-
gridgen = GridGenerator(atoms, interval=0.
|
|
97
|
+
gridgen = GridGenerator(atoms, interval=0.5, subtype='bulk', rsub='vdw_radii', scale=1.0)
|
|
96
98
|
grid = gridgen.grid
|
|
97
99
|
print(len(grid))
|
|
98
100
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/structures/__init__.py
RENAMED
|
File without changes
|
{surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/structures/adsorbate.py
RENAMED
|
File without changes
|
|
File without changes
|
{surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/tasks/sitesampling.py
RENAMED
|
File without changes
|
|
File without changes
|
{surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/tasks/terminations.py
RENAMED
|
File without changes
|
|
File without changes
|
{surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/utils/weight_functions.py
RENAMED
|
File without changes
|
{surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct.egg-info/requires.txt
RENAMED
|
File without changes
|
{surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|