surface-construct 0.11__tar.gz → 0.12__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 (38) hide show
  1. {surface_construct-0.11/surface_construct.egg-info → surface_construct-0.12}/PKG-INFO +2 -2
  2. {surface_construct-0.11 → surface_construct-0.12}/setup.py +2 -2
  3. {surface_construct-0.11 → surface_construct-0.12}/surface_construct/structures/adsorbate.py +17 -5
  4. {surface_construct-0.11 → surface_construct-0.12}/surface_construct/structures/combiner.py +5 -0
  5. {surface_construct-0.11 → surface_construct-0.12}/surface_construct/tasks/__init__.py +2 -1
  6. surface_construct-0.12/surface_construct/tasks/afm.py +120 -0
  7. {surface_construct-0.11 → surface_construct-0.12}/surface_construct/tasks/sitesampling.py +1 -4
  8. {surface_construct-0.11 → surface_construct-0.12/surface_construct.egg-info}/PKG-INFO +2 -2
  9. {surface_construct-0.11 → surface_construct-0.12}/surface_construct.egg-info/SOURCES.txt +1 -0
  10. {surface_construct-0.11 → surface_construct-0.12}/tests/test_task.py +39 -3
  11. {surface_construct-0.11 → surface_construct-0.12}/LICENSE +0 -0
  12. {surface_construct-0.11 → surface_construct-0.12}/README.md +0 -0
  13. {surface_construct-0.11 → surface_construct-0.12}/setup.cfg +0 -0
  14. {surface_construct-0.11 → surface_construct-0.12}/surface_construct/__init__.py +0 -0
  15. {surface_construct-0.11 → surface_construct-0.12}/surface_construct/db.py +0 -0
  16. {surface_construct-0.11 → surface_construct-0.12}/surface_construct/default_parameter.py +0 -0
  17. {surface_construct-0.11 → surface_construct-0.12}/surface_construct/sg_sampler.py +0 -0
  18. {surface_construct-0.11 → surface_construct-0.12}/surface_construct/structures/__init__.py +0 -0
  19. {surface_construct-0.11 → surface_construct-0.12}/surface_construct/structures/pymsym_test.py +0 -0
  20. {surface_construct-0.11 → surface_construct-0.12}/surface_construct/structures/surface.py +0 -0
  21. {surface_construct-0.11 → surface_construct-0.12}/surface_construct/structures/surface_grid.py +0 -0
  22. {surface_construct-0.11 → surface_construct-0.12}/surface_construct/tasks/taskbase.py +0 -0
  23. {surface_construct-0.11 → surface_construct-0.12}/surface_construct/tasks/terminations.py +0 -0
  24. {surface_construct-0.11 → surface_construct-0.12}/surface_construct/utils/__init__.py +0 -0
  25. {surface_construct-0.11 → surface_construct-0.12}/surface_construct/utils/atoms.py +0 -0
  26. {surface_construct-0.11 → surface_construct-0.12}/surface_construct/utils/geometry.py +0 -0
  27. {surface_construct-0.11 → surface_construct-0.12}/surface_construct/utils/pymsym_wrapper.py +0 -0
  28. {surface_construct-0.11 → surface_construct-0.12}/surface_construct/utils/spglib_wrapper.py +0 -0
  29. {surface_construct-0.11 → surface_construct-0.12}/surface_construct/utils/weight_functions.py +0 -0
  30. {surface_construct-0.11 → surface_construct-0.12}/surface_construct.egg-info/dependency_links.txt +0 -0
  31. {surface_construct-0.11 → surface_construct-0.12}/surface_construct.egg-info/requires.txt +0 -0
  32. {surface_construct-0.11 → surface_construct-0.12}/surface_construct.egg-info/top_level.txt +0 -0
  33. {surface_construct-0.11 → surface_construct-0.12}/tests/test_adsorbate.py +0 -0
  34. {surface_construct-0.11 → surface_construct-0.12}/tests/test_combiner.py +0 -0
  35. {surface_construct-0.11 → surface_construct-0.12}/tests/test_sampling1.py +0 -0
  36. {surface_construct-0.11 → surface_construct-0.12}/tests/test_sampling2.py +0 -0
  37. {surface_construct-0.11 → surface_construct-0.12}/tests/test_simple_surface.py +0 -0
  38. {surface_construct-0.11 → surface_construct-0.12}/tests/test_surface_grid.py +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: surface_construct
3
- Version: 0.11
4
- Summary: Surface termination construction especially for complex model, such as oxides or carbides.
3
+ Version: 0.12
4
+ Summary: Surface construction and surface reaction sampling tools.
5
5
  Home-page: https://gitee.com/pjren/surface_construct/
6
6
  Author: ren
7
7
  Author-email: 0403114076@163.com
@@ -15,13 +15,13 @@ install_requires = [
15
15
 
16
16
  setup(
17
17
  name='surface_construct',
18
- version='0.11',
18
+ version='0.12',
19
19
  packages=find_packages(),
20
20
  url='https://gitee.com/pjren/surface_construct/',
21
21
  license='GPL',
22
22
  author='ren',
23
23
  author_email='0403114076@163.com',
24
- description='Surface termination construction especially for complex model, such as oxides or carbides.',
24
+ description='Surface construction and surface reaction sampling tools.',
25
25
  long_description=long_description,
26
26
  long_description_content_type="text/markdown",
27
27
  install_requires=install_requires,
@@ -5,7 +5,7 @@ from ase.geometry.analysis import Analysis as ase_Analysis
5
5
  from ase.geometry.distance import distance as ase_distance
6
6
  from scipy.spatial import cKDTree
7
7
 
8
- from surface_construct.utils.atoms import atoms2graph, atoms_regulate, set_dihedrals
8
+ from surface_construct.utils.atoms import atoms2graph, atoms_regulate, set_dihedrals, topodist_matrix, topo_identity
9
9
  from surface_construct.utils.geometry import dih_grid, sample_rotations_with_symmetry, view_samples, \
10
10
  estimate_rotation_samples
11
11
  from surface_construct.utils.pymsym_wrapper import get_pure_rotations, get_point_group, get_Cn
@@ -64,9 +64,12 @@ class Adsorbate:
64
64
  f" Point Group: {self.point_group}",
65
65
  f" Radius: {self.rads}",
66
66
  f" Principal axis: [{px:8.3f}, {py:8.3f}, {pz:8.3f}] ",
67
- f" Number of dihedral: {len(self.all_dihedrals)}"]
67
+ ]
68
68
  if self.dihedral_delta:
69
- info.append(f" Number of dihedral grid: {len(self.dihedral_grid)}",)
69
+ info+=[
70
+ f" Number of dihedral grid: {len(self.dihedral_grid)}",
71
+ f" Number of dihedral: {len(self.all_dihedrals)}",
72
+ ]
70
73
  info += [f" Number of symmetry rotation operations: {len(self.symmetry_rotation_operations)}",
71
74
  f" Number of symmetry rotations: {len(self._symmetry_rotations)}",]
72
75
  if self.rotation_delta:
@@ -90,11 +93,14 @@ class Adsorbate:
90
93
  将分子旋转到主轴 = z, 次主轴=x, 第三轴 = y
91
94
  """
92
95
  # com to 0,0,0
96
+ constraints = self.atoms.constraints.copy() # 需要先消除限制,因为会影响set_com 和旋转
97
+ self.atoms.constraints = []
93
98
  if len(self.atoms) > 1:
94
99
  atoms_regulate(self.atoms)
95
100
  else:
96
101
  self.atoms.set_center_of_mass([0.,0.,0.])
97
102
  self.is_regulated = True
103
+ self.atoms.constraints = constraints
98
104
 
99
105
  @property
100
106
  def com(self):
@@ -106,8 +112,8 @@ class Adsorbate:
106
112
  @property
107
113
  def principal_axis(self):
108
114
  # 分子主轴向量
109
- if not self.is_regulated:
110
- self.regulate()
115
+ #if not self.is_regulated:
116
+ # self.regulate()
111
117
  evals, evecs = self.atoms.get_moments_of_inertia(vectors=True)
112
118
  return evecs[np.argmin(np.abs(evals))]
113
119
 
@@ -216,6 +222,12 @@ class Adsorbate:
216
222
 
217
223
  return self._atoms_graph
218
224
 
225
+ @property
226
+ def topo_id(self):
227
+ dm = topodist_matrix(self._adj_matrix)
228
+ ids = topo_identity(dm)
229
+ return ids
230
+
219
231
  def get_dihedral_mask(self, dihedral: Tuple[int, int, int, int]) -> set:
220
232
  """
221
233
  为二面角生成移动的 mask indices
@@ -120,6 +120,10 @@ class AdsGridCombiner:
120
120
  rotation:Sequence=None,
121
121
  dock_point:int=None) -> ase.Atoms:
122
122
  from scipy.spatial.transform import Rotation as R
123
+
124
+ atoms = self.ads_obj.atoms.copy()
125
+ constraints = atoms.constraints.copy() # 需要先消除限制,因为会影响set_com 和旋转
126
+ atoms.constraints = []
123
127
  if dih_value is not None:
124
128
  dih_dict = self.ads_obj.get_dihedrals(dih_value=dih_value)
125
129
  atoms = set_dihedrals(self.ads_obj.atoms,dih_dict)
@@ -134,6 +138,7 @@ class AdsGridCombiner:
134
138
  atoms.positions = R.from_quat(rotation, scalar_first=True).apply(atoms.positions)
135
139
 
136
140
  atoms.positions += xyz # move atoms
141
+ atoms.constraints = constraints # 重新找回 constraints
137
142
  return atoms
138
143
 
139
144
  def get_vip_rot(self)->Sequence:
@@ -11,4 +11,5 @@
11
11
  3. obj.run 运行任务
12
12
  4. 分析和作图
13
13
  """
14
- from .sitesampling import SurfaceSiteSampleTask
14
+ from .sitesampling import SurfaceSiteSampleTask
15
+ from .afm import AFMTask
@@ -0,0 +1,120 @@
1
+ import datetime
2
+
3
+ import ase.io
4
+ import numpy as np
5
+ from ase import Atoms
6
+ from ase.constraints import FixAtoms, dict2constraint
7
+ from .sitesampling import SurfaceSiteSampleTask
8
+
9
+ def update_fixatoms_index(atoms:Atoms, idx:int):
10
+ c_list = []
11
+ for c in atoms.constraints:
12
+ if isinstance(c, FixAtoms):
13
+ dct = c.todict()
14
+ dct['kwargs']['indices'] = [i+idx for i in dct['kwargs']['indices']]
15
+ new_c = dict2constraint(dct)
16
+ c_list.append(new_c)
17
+ return c_list
18
+
19
+ def get_topo_id(A,nl=None): # 替代老的
20
+ from ase.geometry.analysis import Analysis as ase_Analysis
21
+ from surface_construct.utils.atoms import topodist_matrix, topo_identity
22
+ analysis = ase_Analysis(A, nl=nl)
23
+ adj_matrix = analysis.adjacency_matrix[0].toarray()
24
+ dm = topodist_matrix(adj_matrix)
25
+ ids = topo_identity(dm)
26
+ return ids
27
+
28
+ class AFMTask(SurfaceSiteSampleTask):
29
+
30
+ def irun(self,grid_idx=None, **kwargs):
31
+ # 采样单元运行
32
+ self.grid_idx = grid_idx
33
+ x,y,z = self.sg_obj.points[grid_idx]
34
+ dock_point = self.combiner.ads_obj.dock_points[0] # tip must define the dock_point
35
+ dz_range = (np.linspace(2.0,0.75,self.kwargs.get('nz',10)) *
36
+ (max(self.sg_obj.rsub)+self.sg_obj.rads)) # from far to near
37
+ z0 = max(self.sg_obj.atoms.positions[:,2])
38
+ e0_list = []
39
+ dz_list = []
40
+ atoms_list = []
41
+ ads_topo_id0 = self.combiner.ads_obj.topo_id
42
+ for dz in dz_range:
43
+ ads_atoms = self.combiner.docking(
44
+ xyz=[x,y,z0+dz],
45
+ dock_point=dock_point,
46
+ )
47
+ atoms = self.sg_obj.atoms.copy()
48
+ atoms += ads_atoms
49
+ atoms.calc = self.combiner.calc
50
+
51
+ if self.optimizer is not None:
52
+ # 设置 constraint
53
+ idx0 = len(self.sg_obj.atoms)
54
+ new_constraint = (update_fixatoms_index(self.combiner.ads_obj.atoms, idx0)
55
+ + atoms.constraints)
56
+ atoms.constraints = new_constraint
57
+ opt_kwargs = self.kwargs.get('optimizer',dict())
58
+ output_name = f"opt_{grid_idx}-{dz:.2f}"
59
+ if 'logfile' not in opt_kwargs:
60
+ opt_kwargs['logfile'] = f"{output_name}.log"
61
+ if 'trajectory' not in opt_kwargs:
62
+ opt_kwargs['trajectory'] = f"{output_name}.traj"
63
+ if 'fmax' in opt_kwargs:
64
+ fmax = opt_kwargs.pop('fmax')
65
+ else:
66
+ fmax = 0.1
67
+ if 'steps' in opt_kwargs:
68
+ steps = opt_kwargs.pop('steps')
69
+ else:
70
+ steps = 100
71
+ opt = self.optimizer(atoms, **opt_kwargs)
72
+ converged = opt.run(fmax=fmax, steps=steps)
73
+ if converged:
74
+ new_ads_atoms = atoms[len(self.sg_obj.atoms):]
75
+ topo_id = get_topo_id(new_ads_atoms,nl=self.combiner.ads_obj.nl)
76
+ if topo_id != ads_topo_id0:
77
+ msg=f"Warning: The connectivity of tip is broken for dz={dz}"
78
+ self.log(msg+'\n')
79
+ converged = False
80
+ else:
81
+ converged = True
82
+
83
+ if converged:
84
+ e0=atoms.get_potential_energy()
85
+ e0_list.append(e0)
86
+ dz_list.append(dz)
87
+ atoms_list.append(atoms.copy())
88
+
89
+ if len(e0_list) == 0:
90
+ e0 = np.nan
91
+ converged = False # re-define converged
92
+ else:
93
+ converged = True
94
+ e0_min_idx = np.argmin(e0_list)
95
+ e0 = e0_list[e0_min_idx]
96
+ dz = dz_list[e0_min_idx]
97
+ z = z0 + dz
98
+
99
+ # 打印信息到 log file:
100
+ used_time = (datetime.datetime.now() - self.stime).seconds
101
+ h = used_time // 3600
102
+ m = (used_time % 3600) // 60
103
+ s = (used_time % 60)
104
+ msg = (f"SAMPLE {h:02}:{m:02}:{s:02} {grid_idx:7} "
105
+ f"{x:8.3f} {y:8.3f} {z:8.3f} {e0:.4f} {str(converged)[0]}")
106
+ self.log(msg+'\n')
107
+ # 输出 优化完的结构
108
+ if converged:
109
+ ase.io.write(f"atoms_{grid_idx}.traj", atoms_list)
110
+ # 更新 sg_obj
111
+ self.sg_obj.set_energy(grid_idx, e0)
112
+ for iz, ie in zip(dz_list, e0_list):
113
+ self.sg_obj.set_property(grid_idx, ie, key=iz)
114
+ self.to_pkl()
115
+ # 是否画图?
116
+ if kwargs.get('fit', False):
117
+ self.sg_obj.fit()
118
+ self.sg_obj.plot_energy()
119
+ else:
120
+ self.sg_obj.del_sample(grid_idx)
@@ -81,7 +81,6 @@ class SurfaceSiteSampleTask(TaskBase):
81
81
  msg.append(f" Size: {s.get('size')}")
82
82
  msg.append(f" Surface Sampler: {s.get('surface')}")
83
83
  msg.append(f" Surface Sampler weight: {s.get('weight',1.0)}")
84
- msg.append(f" Conformation Sampler: {s.get('conformation')}")
85
84
  msg.append("Other kwargs:")
86
85
  for k,v in self.kwargs.items():
87
86
  msg.append(f' {k} : {v}')
@@ -113,8 +112,7 @@ class SurfaceSiteSampleTask(TaskBase):
113
112
  cidx=cidx,
114
113
  ridx=ridx,
115
114
  dp_idx=dp_idx,
116
- opt_z=True,
117
- **kwargs)
115
+ opt_z=True)
118
116
  if atoms is None:
119
117
  continue
120
118
  if self.optimizer is not None:
@@ -181,7 +179,6 @@ class SurfaceSiteSampleTask(TaskBase):
181
179
  sg_sampler = isampler.get("surface")
182
180
  if type(sg_sampler) in (tuple, list) and len(sg_sampler) == 1:
183
181
  sg_sampler = sg_sampler[0]
184
- conf_sampler = isampler.get("conformation", None) # TODO: 这个需要重新设计和调试
185
182
  if type(sg_sampler) == str:
186
183
  sg_sampler_obj = name2sampler(sg_sampler)
187
184
  if sg_sampler in ('InitialSGSampler', 'KeyPointSGSampler'): # 一次性采N样,其余都是一次一个点
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: surface_construct
3
- Version: 0.11
4
- Summary: Surface termination construction especially for complex model, such as oxides or carbides.
3
+ Version: 0.12
4
+ Summary: Surface construction and surface reaction sampling tools.
5
5
  Home-page: https://gitee.com/pjren/surface_construct/
6
6
  Author: ren
7
7
  Author-email: 0403114076@163.com
@@ -17,6 +17,7 @@ surface_construct/structures/pymsym_test.py
17
17
  surface_construct/structures/surface.py
18
18
  surface_construct/structures/surface_grid.py
19
19
  surface_construct/tasks/__init__.py
20
+ surface_construct/tasks/afm.py
20
21
  surface_construct/tasks/sitesampling.py
21
22
  surface_construct/tasks/taskbase.py
22
23
  surface_construct/tasks/terminations.py
@@ -4,17 +4,18 @@ from random import randint
4
4
 
5
5
  import ase.io
6
6
  import pytest
7
- from ase import Atom
7
+ from ase import Atom, Atoms
8
+ from ase.constraints import FixAtoms
8
9
  from ase.visualize import view
9
10
  from lasp_ase.lasp import Lasp
10
11
  from surface_construct import SurfaceGrid
11
12
  from surface_construct import AdsGridCombiner
12
13
  from surface_construct import Adsorbate
13
- from surface_construct.tasks import SurfaceSiteSampleTask
14
+ from surface_construct.tasks import SurfaceSiteSampleTask, AFMTask
14
15
  from ase.optimize import LBFGS, BFGS
15
16
 
16
17
 
17
- class TestSurfaceSiteSampling:
18
+ class TestTask:
18
19
  """
19
20
  Simple Ru 0001 suface
20
21
  """
@@ -110,4 +111,39 @@ class TestSurfaceSiteSampling:
110
111
  ]
111
112
  task_obj = SurfaceSiteSampleTask(combiner=com_obj, sampler=sampler, optimizer=None)
112
113
  task_obj.run()
114
+ print('Done')
115
+
116
+ def test_afm(self):
117
+ tip_atoms = Atoms('Ru4CO',positions=[
118
+ [4.96420026, 5.33360004, 5.96179962],
119
+ [2.98259974, 6.51570022, 6.69340014],
120
+ [6.93529963, 6.55210018, 6.65879965],
121
+ [4.97309983, 3.62199992, 6.84219956],
122
+ [4.93830025, 5.30839980, 3.98790002],
123
+ [4.91949975, 5.28810024, 2.48350024],
124
+ ])
125
+
126
+ tip_atoms.constraints = FixAtoms(mask=tip_atoms.symbols == 'Ru')
127
+ tip_obj = Adsorbate(tip_atoms)
128
+ tip_obj.dock_point_indices = [[5]] # O atom as docker_point
129
+ shutil.copyfile('../atoms_files/RuCHO_lasp.in', 'lasp.in')
130
+ shutil.copyfile('../atoms_files/RuCHO_pf2.pot', 'RuCHO.pot')
131
+ satoms = ase.io.read('../atoms_files/ru_0001_POSCAR')
132
+ satoms.calc = Lasp()
133
+ sg_obj = SurfaceGrid(satoms, interval=0.1, rads=tip_obj.rads, lpca=False, subtype='slab')
134
+ sg_obj.gridize()
135
+ com_obj = AdsGridCombiner(sg_obj, tip_obj)
136
+
137
+ sampler =[
138
+ {
139
+ 'surface': "KeyPointSGSampler", # 表面采样方法
140
+ }, # 第一步采样
141
+ {
142
+ 'size': 2, # 采样大小
143
+ 'surface': ("MaxDiversitySGSampler", "MaxSigmaSGSampler"), # 表面采样方法
144
+ 'weight': (0.5, 0.5), # 表面采样方法的权重
145
+ } # 第二步采样
146
+ ]
147
+ task_obj = AFMTask(combiner=com_obj, sampler=sampler, optimizer=LBFGS, nz=10) # nz 定义 z方向采多少样
148
+ task_obj.run()
113
149
  print('Done')