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.
Files changed (30) hide show
  1. {surface_construct-0.9.3/surface_construct.egg-info → surface_construct-0.10.1}/PKG-INFO +4 -1
  2. {surface_construct-0.9.3 → surface_construct-0.10.1}/README.md +3 -0
  3. {surface_construct-0.9.3 → surface_construct-0.10.1}/setup.py +1 -1
  4. {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/structures/surface.py +119 -72
  5. {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/structures/surface_grid.py +8 -5
  6. {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/utils/atoms.py +14 -18
  7. {surface_construct-0.9.3 → surface_construct-0.10.1/surface_construct.egg-info}/PKG-INFO +4 -1
  8. {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct.egg-info/SOURCES.txt +1 -0
  9. surface_construct-0.10.1/tests/test_simple_surface.py +39 -0
  10. {surface_construct-0.9.3 → surface_construct-0.10.1}/tests/test_surface_grid.py +3 -1
  11. {surface_construct-0.9.3 → surface_construct-0.10.1}/LICENSE +0 -0
  12. {surface_construct-0.9.3 → surface_construct-0.10.1}/setup.cfg +0 -0
  13. {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/__init__.py +0 -0
  14. {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/db.py +0 -0
  15. {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/default_parameter.py +0 -0
  16. {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/sg_sampler.py +0 -0
  17. {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/structures/__init__.py +0 -0
  18. {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/structures/adsorbate.py +0 -0
  19. {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/tasks/__init__.py +0 -0
  20. {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/tasks/sitesampling.py +0 -0
  21. {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/tasks/taskbase.py +0 -0
  22. {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/tasks/terminations.py +0 -0
  23. {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/utils/__init__.py +0 -0
  24. {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct/utils/weight_functions.py +0 -0
  25. {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct.egg-info/dependency_links.txt +0 -0
  26. {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct.egg-info/requires.txt +0 -0
  27. {surface_construct-0.9.3 → surface_construct-0.10.1}/surface_construct.egg-info/top_level.txt +0 -0
  28. {surface_construct-0.9.3 → surface_construct-0.10.1}/tests/test_sampling1.py +0 -0
  29. {surface_construct-0.9.3 → surface_construct-0.10.1}/tests/test_sampling2.py +0 -0
  30. {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.9.3
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
 
@@ -15,7 +15,7 @@ install_requires = [
15
15
 
16
16
  setup(
17
17
  name='surface_construct',
18
- version='0.9.3',
18
+ version='0.10.1',
19
19
  packages=find_packages(),
20
20
  url='https://gitee.com/pjren/surface_construct/',
21
21
  license='GPL',
@@ -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, refine=None):
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 = bool(refine) # 是否进行 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
- @property
125
- def refine(self):
126
- return self._refine
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
- if refine is None:
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
- TODO: 判断是否 refine 起到作用了?
158
- """
159
- def __init__(self, surface, layers=3, vacuum=15.0, atoms=None, refine=None):
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 = bool(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, 1)):
217
- multiple = size[0] * size[1] * size[2]
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, size[2]]]
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
- self._skin_id = list(range(0, self.natoms_per_layer))
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
- self._bottom_id = list(range(self.natoms_per_layer * (self.layers - 1),
291
- self.natoms_per_layer * self.layers))
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
- posz = pos[0:self.natoms_per_layer, 2]
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
- enumerate_posz = sorted(enumerate(posz), key=lambda lst: lst[1])
331
- posz = [i[1] for i in enumerate_posz]
332
- posz_idx = [i[0] for i in enumerate_posz] # 由小到大排列,从外表面到内部
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 = self.dermis_id + self.bulk_id + self.bottom_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.atoms_dermis_id = np.arange(len(self.dermis_id))
433
- self.atoms_bulk_id = np.arange(len(self.dermis_id),
434
- len(self.dermis_id + self.bulk_id))
435
- self.atoms_bottom_id = np.arange(len(self.dermis_id + self.bulk_id),
436
- len(self.atoms))
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
- _ = self.cus_cn # cus_cn 的过程中,顺带求 atoms_cus_id
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._cus_cn)
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[idx][k] - item[k]
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:{atoms_cus_id: cn_dict}
538
- cus_cn = {idx: self._cus_cn[idx] for idx in self._atoms_cus_id}
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
- tips = np.concatenate(np.argwhere(pseudo_skin_z == pseudo_skin_z.min())) # get the tip atoms id
564
- ghost_xyz = pseudo_skin_atoms.positions[tips]
565
- ghost_xyz[:, 2] = ghost_xyz[:, 2] - 0.5
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
- identity = topo_identity(topodist_matrix(adj_matrix(pseudo_skin_atoms)))
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):
@@ -424,10 +424,13 @@ class SurfaceGrid:
424
424
  self.rcutoff = rcutoff
425
425
 
426
426
  if type(rsub) in (list, tuple, np.ndarray):
427
- assert len(rsub) == len(atoms)
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
- {atomnum:self.rads + self.rsub[atomnum]
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
- r = self.rsub[s[0]] + self.rads
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.9.3
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
 
@@ -23,5 +23,6 @@ surface_construct/utils/atoms.py
23
23
  surface_construct/utils/weight_functions.py
24
24
  tests/test_sampling1.py
25
25
  tests/test_sampling2.py
26
+ tests/test_simple_surface.py
26
27
  tests/test_surface_grid.py
27
28
  tests/test_task.py
@@ -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.2, subtype='bulk', rsub='vdw_radii', scale=1.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