surface-construct 0.12.4__tar.gz → 0.12.6__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 (39) hide show
  1. {surface_construct-0.12.4/surface_construct.egg-info → surface_construct-0.12.6}/PKG-INFO +1 -1
  2. {surface_construct-0.12.4 → surface_construct-0.12.6}/setup.py +1 -1
  3. {surface_construct-0.12.4 → surface_construct-0.12.6}/surface_construct/sg_sampler.py +25 -4
  4. {surface_construct-0.12.4 → surface_construct-0.12.6}/surface_construct/structures/surface_grid.py +24 -11
  5. {surface_construct-0.12.4 → surface_construct-0.12.6}/surface_construct/tasks/__init__.py +2 -1
  6. {surface_construct-0.12.4 → surface_construct-0.12.6}/surface_construct/tasks/sitesampling.py +3 -5
  7. surface_construct-0.12.6/surface_construct/tasks/vipsitetask.py +74 -0
  8. {surface_construct-0.12.4 → surface_construct-0.12.6/surface_construct.egg-info}/PKG-INFO +1 -1
  9. {surface_construct-0.12.4 → surface_construct-0.12.6}/surface_construct.egg-info/SOURCES.txt +1 -0
  10. {surface_construct-0.12.4 → surface_construct-0.12.6}/tests/test_task.py +46 -6
  11. {surface_construct-0.12.4 → surface_construct-0.12.6}/LICENSE +0 -0
  12. {surface_construct-0.12.4 → surface_construct-0.12.6}/README.md +0 -0
  13. {surface_construct-0.12.4 → surface_construct-0.12.6}/setup.cfg +0 -0
  14. {surface_construct-0.12.4 → surface_construct-0.12.6}/surface_construct/__init__.py +0 -0
  15. {surface_construct-0.12.4 → surface_construct-0.12.6}/surface_construct/db.py +0 -0
  16. {surface_construct-0.12.4 → surface_construct-0.12.6}/surface_construct/default_parameter.py +0 -0
  17. {surface_construct-0.12.4 → surface_construct-0.12.6}/surface_construct/structures/__init__.py +0 -0
  18. {surface_construct-0.12.4 → surface_construct-0.12.6}/surface_construct/structures/adsorbate.py +0 -0
  19. {surface_construct-0.12.4 → surface_construct-0.12.6}/surface_construct/structures/combiner.py +0 -0
  20. {surface_construct-0.12.4 → surface_construct-0.12.6}/surface_construct/structures/pymsym_test.py +0 -0
  21. {surface_construct-0.12.4 → surface_construct-0.12.6}/surface_construct/structures/surface.py +0 -0
  22. {surface_construct-0.12.4 → surface_construct-0.12.6}/surface_construct/tasks/afm.py +0 -0
  23. {surface_construct-0.12.4 → surface_construct-0.12.6}/surface_construct/tasks/taskbase.py +0 -0
  24. {surface_construct-0.12.4 → surface_construct-0.12.6}/surface_construct/tasks/terminations.py +0 -0
  25. {surface_construct-0.12.4 → surface_construct-0.12.6}/surface_construct/utils/__init__.py +0 -0
  26. {surface_construct-0.12.4 → surface_construct-0.12.6}/surface_construct/utils/atoms.py +0 -0
  27. {surface_construct-0.12.4 → surface_construct-0.12.6}/surface_construct/utils/geometry.py +0 -0
  28. {surface_construct-0.12.4 → surface_construct-0.12.6}/surface_construct/utils/pymsym_wrapper.py +0 -0
  29. {surface_construct-0.12.4 → surface_construct-0.12.6}/surface_construct/utils/spglib_wrapper.py +0 -0
  30. {surface_construct-0.12.4 → surface_construct-0.12.6}/surface_construct/utils/weight_functions.py +0 -0
  31. {surface_construct-0.12.4 → surface_construct-0.12.6}/surface_construct.egg-info/dependency_links.txt +0 -0
  32. {surface_construct-0.12.4 → surface_construct-0.12.6}/surface_construct.egg-info/requires.txt +0 -0
  33. {surface_construct-0.12.4 → surface_construct-0.12.6}/surface_construct.egg-info/top_level.txt +0 -0
  34. {surface_construct-0.12.4 → surface_construct-0.12.6}/tests/test_adsorbate.py +0 -0
  35. {surface_construct-0.12.4 → surface_construct-0.12.6}/tests/test_combiner.py +0 -0
  36. {surface_construct-0.12.4 → surface_construct-0.12.6}/tests/test_sampling1.py +0 -0
  37. {surface_construct-0.12.4 → surface_construct-0.12.6}/tests/test_sampling2.py +0 -0
  38. {surface_construct-0.12.4 → surface_construct-0.12.6}/tests/test_simple_surface.py +0 -0
  39. {surface_construct-0.12.4 → surface_construct-0.12.6}/tests/test_surface_grid.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: surface_construct
3
- Version: 0.12.4
3
+ Version: 0.12.6
4
4
  Summary: Surface construction and surface reaction sampling tools.
5
5
  Home-page: https://gitee.com/pjren/surface_construct/
6
6
  Author: ren
@@ -15,7 +15,7 @@ install_requires = [
15
15
 
16
16
  setup(
17
17
  name='surface_construct',
18
- version='0.12.4',
18
+ version='0.12.6',
19
19
  packages=find_packages(),
20
20
  url='https://gitee.com/pjren/surface_construct/',
21
21
  license='GPL',
@@ -145,6 +145,14 @@ class RandomSGSampler(SGSamplerBase):
145
145
  else:
146
146
  self.seed = None
147
147
 
148
+ @property
149
+ def _population(self):
150
+ """
151
+ 排除已经采过的 index
152
+ :return:
153
+ """
154
+ return [i for i in range(self._pop_size) if i not in self.sg_obj.sample_idx]
155
+
148
156
  def _samples(self, size, **kwargs):
149
157
  idx = random.sample(self._population, size)
150
158
  return idx
@@ -157,11 +165,20 @@ class MaxSigmaSGSampler(SGSamplerBase):
157
165
  def _samples(self, size, **kwargs):
158
166
  if 'energy' in self.sg_obj.grid_property:
159
167
  # 如果已经读入了一些能量,则返回误差最大的点
160
- sigma_array = self.sg_obj.grid_property['energy']
168
+ sigma_array = self.sg_obj.grid_property_sigma['energy']
161
169
  sigma0 = sigma_array.max()
162
- idx_list = np.argwhere(sigma_array <= sigma0-0.1).flatten().tolist()
163
- idx = random.sample(idx_list, size)
164
- return idx
170
+ if len(sigma_array) < size:
171
+ raise ValueError("Number of points should greater than sample!")
172
+ elif len(sigma_array) == size:
173
+ return np.asarray(range(len(sigma_array)))
174
+ else:
175
+ delta = 0.01
176
+ idx_list = []
177
+ while len(idx_list) >= size:
178
+ idx_list = np.argwhere(sigma_array > sigma0-delta).flatten().tolist()
179
+ delta += 0.01
180
+ idx = random.sample(idx_list, size)
181
+ return idx
165
182
  else:
166
183
  raise "No energy for all population, pls do initial sampling first!"
167
184
 
@@ -173,6 +190,10 @@ class MinEnergySGSampler(SGSamplerBase):
173
190
  def _samples(self, size, **kwargs):
174
191
  if 'energy' in self.sg_obj.grid_property:
175
192
  E_array = self.sg_obj.grid_property['energy']
193
+ if len(E_array) < size:
194
+ raise ValueError("Number of points should greater than sample!")
195
+ elif len(E_array) == size:
196
+ return np.asarray(range(len(E_array)))
176
197
  # 如果已经读入了一些能量,则返回能量最低的点 (<0.1eV 以内,然后随机选一个)
177
198
  E0 = E_array.min()
178
199
  idx_list = np.argwhere(E_array <= E0+0.1).flatten().tolist()
@@ -770,13 +770,19 @@ class SurfaceGrid:
770
770
  :return:
771
771
  """
772
772
  if self._vector_interval is None:
773
- nsample = 100
773
+ nsample = min(100, len(self.points)-1) # 如果 points 被过滤了,就需要修改了。
774
774
  rng = np.random.default_rng()
775
- idx_0 = rng.choice(range(len(self.points)-1), size=nsample)
775
+ if nsample < 10:
776
+ comb = itertools.combinations(range(len(self.points)-1), nsample)
777
+ idx_0 = rng.choice(list(comb))
778
+ else:
779
+ idx_0 = rng.choice(range(len(self.points)-1), size=nsample)
776
780
  idx_1 = idx_0+1
777
- idx = np.asarray([[i,j] for i,j in zip(idx_0, idx_1) if (i not in idx_1 and j not in idx_0)])
781
+ idx = np.asarray([[i,j] for i,j in zip(idx_0, idx_1)])
778
782
  d_grid = np.linalg.norm(self.points[idx[:, 0]] - self.points[idx[:, 1]], axis=1)
779
- idx = idx[d_grid<1.2 * self.interval]
783
+ tol = 1.2 * self.interval
784
+ if np.any(d_grid<tol):
785
+ idx = idx[d_grid<tol]
780
786
  d_vector = np.linalg.norm(self.vector[idx[:,0]]-self.vector[idx[:,1]], axis=1).mean()
781
787
  k = d_vector / self.interval
782
788
  self._vector_interval = np.min(k)
@@ -899,6 +905,11 @@ class SurfaceGrid:
899
905
  self.sample_dct[idx]['calculated'] = True
900
906
 
901
907
  def fit(self, key='energy'):
908
+ if len(self.points) == len(self.calculated_sample):
909
+ self.grid_property[key] = [self.sample_dct[idx][key] for idx in range(len(self.points))]
910
+ self.grid_property_sigma[key] = [0.] * len(self.points)
911
+ return
912
+
902
913
  from sklearn.gaussian_process import GaussianProcessRegressor
903
914
  from sklearn.gaussian_process.kernels import RBF, ConstantKernel, WhiteKernel
904
915
  from sklearn.preprocessing import StandardScaler
@@ -909,12 +920,14 @@ class SurfaceGrid:
909
920
  scaler.fit(y)
910
921
  y_scaled = scaler.transform(y).T[0]
911
922
  # RBF Kernel
912
- length_scale_grid = 1.0 # 实空间的length scale, 1 angstrom
913
- length_scale_vector = length_scale_grid * self.vector_interval
914
- length_scale_bounds = (length_scale_vector/2.0, length_scale_vector*2.0)
915
- # length_scale_bounds = 'fixed'
916
- # length_scale = [length_scale_vector] * self._vector_dim # 向量空间的 length_scale, anisotropic
917
- length_scale = length_scale_vector # 向量空间的 length_scale, isotropic
923
+ try:
924
+ length_scale_grid = 1.0 # 实空间的length scale, 1 angstrom
925
+ length_scale_vector = length_scale_grid * self.vector_interval
926
+ length_scale_bounds = (length_scale_vector/10.0, length_scale_vector*10.0)
927
+ length_scale = length_scale_vector # 向量空间的 length_scale, isotropic
928
+ except IndexError:
929
+ length_scale = 1.0
930
+ length_scale_bounds = (1e-3, 1e3)
918
931
  rbf_kernel = RBF(length_scale=length_scale, length_scale_bounds=length_scale_bounds)
919
932
  # noise kernel
920
933
  noise_level_dict = {
@@ -928,7 +941,7 @@ class SurfaceGrid:
928
941
  noise_level_norm = np.abs(noise_level / scaler.scale_[0]) # 标准化缩放
929
942
  white_kernel = WhiteKernel(noise_level=noise_level_norm, noise_level_bounds='fixed')
930
943
  # 总的 kernel
931
- constant_kernel = ConstantKernel(constant_value=1.0, constant_value_bounds='fixed')
944
+ constant_kernel = ConstantKernel(constant_value=1.0, constant_value_bounds=(0.1,10))
932
945
  kernel = constant_kernel * rbf_kernel + white_kernel
933
946
  gp = GaussianProcessRegressor(kernel=kernel, n_restarts_optimizer=9, alpha=noise_level_norm*0.01)
934
947
  print(f"Kernel parameters before fit:{kernel})")
@@ -12,4 +12,5 @@
12
12
  4. 分析和作图
13
13
  """
14
14
  from .sitesampling import SurfaceSiteSampleTask
15
- from .afm import AFMTask
15
+ from .afm import AFMTask
16
+ from .vipsitetask import VIPSiteTask
@@ -53,7 +53,6 @@ class SurfaceSiteSampleTask(TaskBase):
53
53
  self.sg_obj = combiner.sg_obj
54
54
  if self.sg_obj.points is None:
55
55
  self.sg_obj.initialize()
56
- self.to_pkl() # 保存当前状态
57
56
 
58
57
  if self.sampler is None:
59
58
  self.sampler = [{'surface':'KeyPointSGSampler'}] # 默认关键点采样
@@ -63,8 +62,6 @@ class SurfaceSiteSampleTask(TaskBase):
63
62
  self.grid_idx = None
64
63
  self.stime = datetime.datetime.now()
65
64
 
66
- self.print_task_info()
67
-
68
65
  def print_task_info(self):
69
66
  msg = [f"Starting task {self.__class__.__name__} at "
70
67
  f"{datetime.datetime.ctime(self.stime)}.\n",
@@ -192,8 +189,9 @@ class SurfaceSiteSampleTask(TaskBase):
192
189
  self.irun(grid_idx, fit=False)
193
190
  else:
194
191
  for i in range(size):
195
- grid_idx = sg_sampler_obj(self.sg_obj).samples(size=1)
196
- self.irun(grid_idx, fit=True)
192
+ grid_idx = sg_sampler_obj(self.sg_obj).samples(size=1)[0]
193
+ self.irun(grid_idx)
194
+ self.sg_obj.fit()
197
195
  elif type(sg_sampler) in (tuple, list):
198
196
  msg = f" Appending sampling with {size} points."
199
197
  self.log(msg+'\n')
@@ -0,0 +1,74 @@
1
+ from matplotlib import pyplot as plt
2
+ from matplotlib.patches import Circle
3
+
4
+ from .sitesampling import SurfaceSiteSampleTask
5
+
6
+ class VIPSiteTask(SurfaceSiteSampleTask):
7
+ def __init__(self, exclude=None, include=None, **kwargs):
8
+ super().__init__(**kwargs)
9
+
10
+ vip_id = self.sg_obj.vip_id
11
+ grid_site_type, site_type_dict = self.sg_obj.get_grid_site_type()
12
+ if exclude is None and include is None:
13
+ msg = [f"Either exclude or include must be specified"]
14
+ msg += [f"Possible site types are {len(site_type_dict)} in total:"]
15
+ msg += [f"{k}: {v}" for k,v in site_type_dict.items() if type(k) in (int,)]
16
+ raise ValueError('\n'.join(msg))
17
+ elif exclude is not None and include is not None:
18
+ raise ValueError("Can not specified exclude and include at the same time")
19
+ elif exclude is not None and include is None:
20
+ filtered = [i for i in vip_id if site_type_dict[grid_site_type[i]] not in exclude]
21
+ else:
22
+ filtered = [i for i in vip_id if site_type_dict[grid_site_type[i]] in include]
23
+
24
+ points = self.sg_obj.points[filtered]
25
+ vector = self.sg_obj.vector[filtered]
26
+ raw_vector = self.sg_obj._raw_vector[filtered]
27
+ _ = self.sg_obj.vector_interval # 此处call 一下,用于后面的 fit 中的参数
28
+ self.sg_obj.points = points
29
+ self.sg_obj.vector = vector
30
+ self.sg_obj._raw_vector = raw_vector
31
+ self.sg_obj.vectorgen = None
32
+
33
+ sampler_size = 0
34
+ new_sampler = []
35
+ for isampler in self.sampler: # 不需要初始采样和key 点采样
36
+ if isampler['surface'] not in ('InitialSGSampler', 'KeyPointSGSampler'):
37
+ sampler_size += isampler.get('size', 0)
38
+ new_sampler.append(isampler)
39
+
40
+ # 如果 filter 之后的点小于等于 self.sampler['size'], 重新定义sampler
41
+ if sampler_size >= len(points):
42
+ self.sampler = [{
43
+ 'size': len(points), # 采样大小
44
+ 'surface': "RandomSGSampler", # 表面采样方法
45
+ }]
46
+ else:
47
+ self.sampler = new_sampler
48
+
49
+ def plot(self, figname='vip_site_heatmap.png'):
50
+ fig, ax = plt.subplots()
51
+ ax.set_aspect('equal')
52
+ # 画晶格
53
+ fpts = [[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0], [0, 0, 0]] # 得到max_z处表面的分数坐标
54
+ pts = self.sg_obj.atoms.cell.cartesian_positions(fpts)[:, :2] # 转为直角坐标,并取xy 两列
55
+ ax.plot(pts[:, 0], pts[:, 1], 'k-', linewidth=2)
56
+ # 画原子
57
+ cmap = plt.cm.jet
58
+ energies = self.sg_obj.grid_property['energy']
59
+ norm = plt.Normalize(vmin=min(energies), vmax=max(energies))
60
+ radius = 0.5
61
+ cell = self.sg_obj.atoms.cell
62
+ for point,e in zip(self.sg_obj.points,energies):
63
+ color = cmap(norm(e))
64
+ fx, fy = cell.scaled_positions(point)[:2]
65
+ px, py = cell.cartesian_positions([fx, fy, 0.])[:2] # 适用于非正交盒子,将其平移到 xy 平面
66
+ circle = Circle((px, py), radius, facecolor=color,edgecolor='k', linewidth=1, alpha=0.8)
67
+ ax.add_patch(circle)
68
+ # 添加 colorbar
69
+ sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm)
70
+ sm.set_array([]) # 需要调用 set_array 但不传数据
71
+ cbar = plt.colorbar(sm, ax=ax)
72
+ cbar.set_label('E/eV', fontsize=12)
73
+ plt.tight_layout()
74
+ fig.savefig(figname, bbox_inches='tight')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: surface_construct
3
- Version: 0.12.4
3
+ Version: 0.12.6
4
4
  Summary: Surface construction and surface reaction sampling tools.
5
5
  Home-page: https://gitee.com/pjren/surface_construct/
6
6
  Author: ren
@@ -21,6 +21,7 @@ surface_construct/tasks/afm.py
21
21
  surface_construct/tasks/sitesampling.py
22
22
  surface_construct/tasks/taskbase.py
23
23
  surface_construct/tasks/terminations.py
24
+ surface_construct/tasks/vipsitetask.py
24
25
  surface_construct/utils/__init__.py
25
26
  surface_construct/utils/atoms.py
26
27
  surface_construct/utils/geometry.py
@@ -3,6 +3,7 @@ import shutil
3
3
  from random import randint
4
4
 
5
5
  import ase.io
6
+ import numpy as np
6
7
  import pytest
7
8
  from ase import Atom, Atoms
8
9
  from ase.constraints import FixAtoms
@@ -11,7 +12,7 @@ from lasp_ase.lasp import Lasp
11
12
  from surface_construct import SurfaceGrid
12
13
  from surface_construct import AdsGridCombiner
13
14
  from surface_construct import Adsorbate
14
- from surface_construct.tasks import SurfaceSiteSampleTask, AFMTask
15
+ from surface_construct.tasks import SurfaceSiteSampleTask, AFMTask, VIPSiteTask
15
16
  from ase.optimize import LBFGS, BFGS
16
17
 
17
18
 
@@ -54,7 +55,8 @@ class TestTask:
54
55
  'weight': (0.1, 0.45, 0.45), # 表面采样方法的权重
55
56
  } # 第二步采样
56
57
  ]
57
- task_obj = SurfaceSiteSampleTask(combiner=ads_grid_comb, sampler=sampler, optimizer=BFGS)
58
+ task_obj = SurfaceSiteSampleTask(combiner=ads_grid_comb, sampler=sampler, optimizer=None)
59
+ task_obj.print_task_info()
58
60
  task_obj.run()
59
61
  print('Done')
60
62
 
@@ -81,7 +83,9 @@ class TestTask:
81
83
  'weight': (0.4, 0.3, 0.3), # 表面采样方法的权重
82
84
  } # 第二步采样
83
85
  ]
84
- task_obj = SurfaceSiteSampleTask(combiner=ads_grid_comb, sampler=sampler, optimizer=LBFGS)
86
+ task_obj = SurfaceSiteSampleTask(combiner=ads_grid_comb, sampler=sampler, optimizer=None)
87
+
88
+ task_obj.print_task_info()
85
89
  task_obj.run()
86
90
  print('Done')
87
91
 
@@ -108,7 +112,8 @@ class TestTask:
108
112
  'weight': (0.4, 0.3, 0.3), # 表面采样方法的权重
109
113
  } # 第二步采样
110
114
  ]
111
- task_obj = SurfaceSiteSampleTask(combiner=ads_grid_comb, sampler=sampler, optimizer=LBFGS)
115
+ task_obj = SurfaceSiteSampleTask(combiner=ads_grid_comb, sampler=sampler, optimizer=None)
116
+ task_obj.print_task_info()
112
117
  task_obj.run()
113
118
  print('Done')
114
119
 
@@ -171,6 +176,41 @@ class TestTask:
171
176
  'weight': (0.5, 0.5), # 表面采样方法的权重
172
177
  } # 第二步采样
173
178
  ]
174
- task_obj = AFMTask(combiner=com_obj, sampler=sampler, optimizer=LBFGS, nz=10) # nz 定义 z方向采多少样
179
+ task_obj = AFMTask(combiner=com_obj, sampler=sampler, optimizer=None, nz=3) # nz 定义 z方向采多少样
180
+ task_obj.print_task_info()
175
181
  task_obj.run()
176
- print('Done')
182
+ print('Done')
183
+
184
+ def test_vipsite(self):
185
+ """
186
+ Water on CuO/Cu surface, only O top site
187
+ :return:
188
+ """
189
+ shutil.copyfile('../atoms_files/CuCHO_lasp.in', 'lasp.in')
190
+ shutil.copyfile('../atoms_files/CuCHO.pot', 'CuCHO.pot')
191
+ atoms = ase.io.read('../atoms_files/CuOx-Cu100-CONTCAR')
192
+ atoms.calc = Lasp()
193
+ ads_atoms = ase.Atoms('H2O',[[0.,0.,-1.],[0.,0.86,0.5],[0.,0.,0.]])
194
+ ads_obj = Adsorbate(ads_atoms,rtype='vdw_radii')
195
+ sg_obj = SurfaceGrid(atoms,rads=ads_obj.rads)
196
+ ads_grid_comb = AdsGridCombiner(sg_obj, ads_obj)
197
+ sampler =[
198
+ {
199
+ 'surface': "RandomSGSampler", # 第一步,随机表面采样方法。
200
+ 'size': 2, # 采样大小
201
+ },
202
+ {
203
+ 'surface': ("MaxDiversitySGSampler", "MinEnergySGSampler", "MaxSigmaSGSampler"), # 表面采样方法
204
+ 'size': 4, # 采样大小
205
+ 'weight': (0.4, 0.3, 0.3), # 表面采样方法的权重
206
+ } # 第二步采样
207
+ ]
208
+ task_obj = VIPSiteTask(combiner=ads_grid_comb, sampler=sampler, optimizer=None, include=[(8,)])
209
+ task_obj.print_task_info()
210
+ task_obj.run()
211
+ task_obj.plot()
212
+ energies = task_obj.sg_obj.grid_property['energy']
213
+ np.savetxt('energies.csv', np.column_stack((sg_obj.points, energies)),
214
+ header='x, y ,z , energy/eV',
215
+ delimiter=',', fmt='%f')
216
+ print('Done')