surface-construct 0.8.4__tar.gz → 0.9.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.8.4/surface_construct.egg-info → surface_construct-0.9.1}/PKG-INFO +3 -3
- {surface_construct-0.8.4 → surface_construct-0.9.1}/setup.py +3 -4
- surface_construct-0.9.1/surface_construct/__init__.py +12 -0
- {surface_construct-0.8.4 → surface_construct-0.9.1}/surface_construct/db.py +1 -1
- surface_construct-0.8.4/surface_construct/sampling.py → surface_construct-0.9.1/surface_construct/sg_sampler.py +81 -79
- surface_construct-0.9.1/surface_construct/structures/__init__.py +50 -0
- surface_construct-0.9.1/surface_construct/structures/adsorbate.py +38 -0
- {surface_construct-0.8.4/surface_construct → surface_construct-0.9.1/surface_construct/structures}/surface.py +738 -738
- {surface_construct-0.8.4/surface_construct → surface_construct-0.9.1/surface_construct/structures}/surface_grid.py +261 -245
- surface_construct-0.9.1/surface_construct/tasks/__init__.py +14 -0
- surface_construct-0.9.1/surface_construct/tasks/sitesampling.py +191 -0
- surface_construct-0.9.1/surface_construct/tasks/taskbase.py +43 -0
- surface_construct-0.9.1/surface_construct/tasks/terminations.py +0 -0
- surface_construct-0.8.4/surface_construct/utils.py → surface_construct-0.9.1/surface_construct/utils/__init__.py +23 -4
- {surface_construct-0.8.4/surface_construct → surface_construct-0.9.1/surface_construct/utils}/atoms.py +3 -0
- {surface_construct-0.8.4 → surface_construct-0.9.1/surface_construct.egg-info}/PKG-INFO +3 -3
- surface_construct-0.9.1/surface_construct.egg-info/SOURCES.txt +27 -0
- {surface_construct-0.8.4 → surface_construct-0.9.1}/tests/test_sampling1.py +6 -5
- {surface_construct-0.8.4 → surface_construct-0.9.1}/tests/test_sampling2.py +6 -6
- {surface_construct-0.8.4 → surface_construct-0.9.1}/tests/test_surface_grid.py +2 -2
- surface_construct-0.9.1/tests/test_task.py +83 -0
- surface_construct-0.8.4/surface_construct/__init__.py +0 -4
- surface_construct-0.8.4/surface_construct/structure.py +0 -536
- surface_construct-0.8.4/surface_construct.egg-info/SOURCES.txt +0 -21
- {surface_construct-0.8.4 → surface_construct-0.9.1}/LICENSE +0 -0
- {surface_construct-0.8.4 → surface_construct-0.9.1}/README.md +0 -0
- {surface_construct-0.8.4 → surface_construct-0.9.1}/setup.cfg +0 -0
- {surface_construct-0.8.4 → surface_construct-0.9.1}/surface_construct/default_parameter.py +0 -0
- {surface_construct-0.8.4/surface_construct → surface_construct-0.9.1/surface_construct/utils}/weight_functions.py +0 -0
- {surface_construct-0.8.4 → surface_construct-0.9.1}/surface_construct.egg-info/dependency_links.txt +0 -0
- {surface_construct-0.8.4 → surface_construct-0.9.1}/surface_construct.egg-info/requires.txt +0 -0
- {surface_construct-0.8.4 → surface_construct-0.9.1}/surface_construct.egg-info/top_level.txt +0 -0
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: surface_construct
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.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
|
|
7
7
|
Author-email: 0403114076@163.com
|
|
8
8
|
License: GPL
|
|
9
9
|
Classifier: Programming Language :: Python :: 3
|
|
10
|
-
Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)
|
|
11
10
|
Classifier: Operating System :: OS Independent
|
|
12
11
|
Description-Content-Type: text/markdown
|
|
13
12
|
License-File: LICENSE
|
|
@@ -25,6 +24,7 @@ Dynamic: description
|
|
|
25
24
|
Dynamic: description-content-type
|
|
26
25
|
Dynamic: home-page
|
|
27
26
|
Dynamic: license
|
|
27
|
+
Dynamic: license-file
|
|
28
28
|
Dynamic: requires-dist
|
|
29
29
|
Dynamic: summary
|
|
30
30
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from setuptools import setup
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
2
|
|
|
3
3
|
with open("README.md", "r", encoding='utf-8') as f:
|
|
4
4
|
long_description = f.read()
|
|
@@ -15,8 +15,8 @@ install_requires = [
|
|
|
15
15
|
|
|
16
16
|
setup(
|
|
17
17
|
name='surface_construct',
|
|
18
|
-
version='0.
|
|
19
|
-
packages=
|
|
18
|
+
version='0.9.1',
|
|
19
|
+
packages=find_packages(),
|
|
20
20
|
url='https://gitee.com/pjren/surface_construct/',
|
|
21
21
|
license='GPL',
|
|
22
22
|
author='ren',
|
|
@@ -27,7 +27,6 @@ setup(
|
|
|
27
27
|
install_requires=install_requires,
|
|
28
28
|
classifiers=[
|
|
29
29
|
"Programming Language :: Python :: 3",
|
|
30
|
-
"License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
|
|
31
30
|
"Operating System :: OS Independent",
|
|
32
31
|
],
|
|
33
32
|
)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from surface_construct.structures.surface import Crystal, Surface, Slab, Termination
|
|
2
|
+
from surface_construct.structures.surface import get_terminations_score
|
|
3
|
+
from surface_construct.structures.surface_grid import SurfaceGrid, GridGenerator
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
__all__ = ['SurfaceGrid',
|
|
7
|
+
'GridGenerator',
|
|
8
|
+
'Crystal',
|
|
9
|
+
'Surface',
|
|
10
|
+
'Slab',
|
|
11
|
+
'Termination',
|
|
12
|
+
]
|
|
@@ -6,7 +6,7 @@ from ase.db.core import now
|
|
|
6
6
|
from ase.io.jsonio import write_json, read_json
|
|
7
7
|
import os
|
|
8
8
|
from ase.db.row import AtomsRow, row2dct
|
|
9
|
-
from surface_construct.atoms import get_atoms_topo_id
|
|
9
|
+
from surface_construct.utils.atoms import get_atoms_topo_id
|
|
10
10
|
from surface_construct.utils import calc_hull_vertices, get_calc_info
|
|
11
11
|
|
|
12
12
|
"""
|
|
@@ -1,36 +1,12 @@
|
|
|
1
|
-
"""
|
|
2
|
-
TODO: 关键点采样:top 位置、hollow位,bridge 位等等。
|
|
3
|
-
"""
|
|
4
|
-
import itertools
|
|
5
1
|
import numpy as np
|
|
6
|
-
from
|
|
7
|
-
from scipy.spatial import ConvexHull, cKDTree
|
|
2
|
+
from scipy.spatial import cKDTree
|
|
8
3
|
from scipy.spatial.distance import cdist
|
|
9
|
-
from scipy.special import comb
|
|
10
4
|
from sklearn.cluster import KMeans as Cluster
|
|
11
5
|
import random
|
|
12
6
|
|
|
13
|
-
from surface_construct.utils import furthest_sites
|
|
14
7
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def hull_vertices(hull):
|
|
18
|
-
hsimplices = hull.simplices
|
|
19
|
-
hvertices = hull.vertices
|
|
20
|
-
hnorms = hull.equations[:,0:-1]
|
|
21
|
-
ndim = hsimplices.shape[1]
|
|
22
|
-
vertices = []
|
|
23
|
-
# 去掉 hull 的 simplices 的角度较大的点
|
|
24
|
-
for i in hvertices:
|
|
25
|
-
p0_facets_idx = np.argwhere(hsimplices == i)[:,0]
|
|
26
|
-
p0_norms = hnorms[p0_facets_idx]
|
|
27
|
-
cosangle = lambda a,b: a.dot(b) / (np.linalg.norm(a) * np.linalg.norm(b))
|
|
28
|
-
# 求 i 凸点相邻的超平面的法向向量之间的夹角。如果存在夹角小于30度,即平面之间的夹角大于150度,则排除该点。反之,保留该点。
|
|
29
|
-
norm_angle_cos = np.absolute([cosangle(a,b) for a,b in itertools.combinations(p0_norms, 2)])
|
|
30
|
-
if np.sum(norm_angle_cos < MIN_HULL_ANGLE_COS) >= comb(ndim,2):
|
|
31
|
-
vertices.append(i)
|
|
32
|
-
|
|
33
|
-
return vertices
|
|
8
|
+
def name2sampler(name):
|
|
9
|
+
return globals()[name]
|
|
34
10
|
|
|
35
11
|
|
|
36
12
|
def addition_samples(sg_obj, size=None, probability=None, **kwargs):
|
|
@@ -58,9 +34,9 @@ def addition_samples(sg_obj, size=None, probability=None, **kwargs):
|
|
|
58
34
|
for method in method_list:
|
|
59
35
|
method_lower = method.lower()
|
|
60
36
|
if method_lower == 'max_sigma':
|
|
61
|
-
sampling_obj =
|
|
37
|
+
sampling_obj = MaxSigmaSGSampler(sg_obj)
|
|
62
38
|
elif method_lower == 'max_diversity':
|
|
63
|
-
sampling_obj =
|
|
39
|
+
sampling_obj = MaxDiversitySGSampler(sg_obj)
|
|
64
40
|
else:
|
|
65
41
|
raise NotImplementedError
|
|
66
42
|
point_idx = np.concatenate([point_idx, sampling_obj.samples(size=1, **kwargs)]) # 每种方法只采一个
|
|
@@ -68,10 +44,12 @@ def addition_samples(sg_obj, size=None, probability=None, **kwargs):
|
|
|
68
44
|
return point_idx
|
|
69
45
|
|
|
70
46
|
|
|
71
|
-
class
|
|
47
|
+
class SGSamplerBase:
|
|
72
48
|
def __init__(self, sg_obj, **kwargs):
|
|
73
49
|
self.sg_obj = sg_obj
|
|
74
50
|
self.threshold = kwargs.get('threshold', 0.37) # 0.37 is half of H-H bond
|
|
51
|
+
self.weight = kwargs.get('weight', 1.0) # 采样的几率,最后进行归一化处理
|
|
52
|
+
self.kwargs = kwargs
|
|
75
53
|
|
|
76
54
|
@property
|
|
77
55
|
def _pop_size(self):
|
|
@@ -91,29 +69,34 @@ class SamplingBase:
|
|
|
91
69
|
|
|
92
70
|
:return:
|
|
93
71
|
"""
|
|
94
|
-
if point_idx is
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
self.sg_obj._sample_vector = self.sg_obj.vector[point_idx]
|
|
102
|
-
self.sg_obj.sample_points = self.sg_obj.points[point_idx]
|
|
72
|
+
if point_idx is None:
|
|
73
|
+
point_idx = []
|
|
74
|
+
elif type(point_idx) is int:
|
|
75
|
+
point_idx = [point_idx]
|
|
76
|
+
|
|
77
|
+
for p in point_idx:
|
|
78
|
+
self.sg_obj.sample_idx = p
|
|
103
79
|
|
|
104
80
|
def _samples(self, size, **kwargs):
|
|
105
81
|
raise NotImplementedError
|
|
106
82
|
|
|
107
83
|
def samples(self, size=1, **kwargs):
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
84
|
+
result = []
|
|
85
|
+
curr_size = size
|
|
86
|
+
loop = 0
|
|
87
|
+
while len(result) < size and loop < 10:
|
|
88
|
+
point_idx = self._samples(size=curr_size, **kwargs)
|
|
89
|
+
filtered_idx = self.exclude_too_close_sample(point_idx)
|
|
90
|
+
self._append_sample_to_sg(point_idx=filtered_idx)
|
|
91
|
+
result += filtered_idx
|
|
92
|
+
curr_size = size - len(filtered_idx)
|
|
93
|
+
loop += 1
|
|
94
|
+
return result
|
|
112
95
|
|
|
113
96
|
def exclude_too_close_sample(self, idx_list, threshold=None):
|
|
114
97
|
if threshold is None:
|
|
115
98
|
threshold = self.threshold
|
|
116
|
-
if self.sg_obj.sample_idx:
|
|
99
|
+
if self.sg_obj.sample_idx is not None:
|
|
117
100
|
unique_idx_list = [i for i in idx_list if i not in self.sg_obj.sample_idx]
|
|
118
101
|
points = list(self.sg_obj.sample_points)
|
|
119
102
|
else:
|
|
@@ -131,11 +114,11 @@ class SamplingBase:
|
|
|
131
114
|
points.append(p)
|
|
132
115
|
new_idx_list.append(idx)
|
|
133
116
|
|
|
134
|
-
if len(new_idx_list) != idx_list:
|
|
117
|
+
if len(new_idx_list) != len(idx_list):
|
|
135
118
|
print(f"Exclude too close sample {set(idx_list)-set(new_idx_list)}")
|
|
136
119
|
return new_idx_list
|
|
137
120
|
|
|
138
|
-
class
|
|
121
|
+
class KeyPointSGSampler(SGSamplerBase):
|
|
139
122
|
"""
|
|
140
123
|
关键点采样,使用 vip_id
|
|
141
124
|
"""
|
|
@@ -145,8 +128,13 @@ class KeyPointSampling(SamplingBase):
|
|
|
145
128
|
self.sg_obj._clusters = clusters
|
|
146
129
|
return sample_idx
|
|
147
130
|
|
|
131
|
+
def samples(self, size=None, **kwargs):
|
|
132
|
+
point_idx = self._samples(**kwargs)
|
|
133
|
+
filtered_idx = self.exclude_too_close_sample(point_idx)
|
|
134
|
+
self._append_sample_to_sg(point_idx=filtered_idx)
|
|
135
|
+
return filtered_idx
|
|
148
136
|
|
|
149
|
-
class
|
|
137
|
+
class RandomSGSampler(SGSamplerBase):
|
|
150
138
|
"""
|
|
151
139
|
完全随机的选择点,仅用于测试,效率太低。
|
|
152
140
|
"""
|
|
@@ -158,54 +146,71 @@ class RandomSampling(SamplingBase):
|
|
|
158
146
|
self.seed = None
|
|
159
147
|
|
|
160
148
|
def _samples(self, size, **kwargs):
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
return pop_idx
|
|
149
|
+
idx = random.sample(self._population, size)
|
|
150
|
+
return idx
|
|
164
151
|
|
|
165
152
|
|
|
166
|
-
class
|
|
153
|
+
class MaxSigmaSGSampler(SGSamplerBase):
|
|
167
154
|
"""
|
|
168
155
|
对最大误差的点进行采样
|
|
169
156
|
"""
|
|
170
157
|
def _samples(self, size, **kwargs):
|
|
171
158
|
if 'energy' in self.sg_obj.grid_property:
|
|
172
159
|
# 如果已经读入了一些能量,则返回误差最大的点
|
|
173
|
-
|
|
174
|
-
|
|
160
|
+
sigma_array = self.sg_obj.grid_property['energy']
|
|
161
|
+
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
|
|
175
165
|
else:
|
|
176
166
|
raise "No energy for all population, pls do initial sampling first!"
|
|
177
167
|
|
|
178
168
|
|
|
179
|
-
class
|
|
169
|
+
class MinEnergySGSampler(SGSamplerBase):
|
|
180
170
|
"""
|
|
181
|
-
|
|
171
|
+
对最大误差的点进行采样
|
|
182
172
|
"""
|
|
183
|
-
|
|
184
173
|
def _samples(self, size, **kwargs):
|
|
174
|
+
if 'energy' in self.sg_obj.grid_property:
|
|
175
|
+
E_array = self.sg_obj.grid_property['energy']
|
|
176
|
+
# 如果已经读入了一些能量,则返回能量最低的点 (<0.1eV 以内,然后随机选一个)
|
|
177
|
+
E0 = E_array.min()
|
|
178
|
+
idx_list = np.argwhere(E_array <= E0+0.1).flatten().tolist()
|
|
179
|
+
idx = random.sample(idx_list, size)
|
|
180
|
+
return idx
|
|
181
|
+
else:
|
|
182
|
+
raise "No energy for all population, pls do initial sampling first!"
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
class InitialSGSampler(SGSamplerBase):
|
|
186
|
+
"""
|
|
187
|
+
结合使用 KeyPointSampling 和 MaxDiversitySampling
|
|
188
|
+
"""
|
|
189
|
+
def _samples(self, size=None, **kwargs):
|
|
190
|
+
# 先进行 KeyPoint sampling,数量不够再进行 Max diversity sampling
|
|
185
191
|
vip_idx = self.sg_obj.unique_vip_id
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
192
|
+
if size is None:
|
|
193
|
+
size = len(vip_idx)
|
|
194
|
+
|
|
189
195
|
if size == len(vip_idx):
|
|
190
|
-
|
|
191
|
-
self.
|
|
196
|
+
# 已经排除了距离过近的点,而且已经加入到了sg_obj
|
|
197
|
+
sample_idx = KeyPointSGSampler(self.sg_obj, **self.kwargs).samples(**kwargs)
|
|
192
198
|
elif size < len(vip_idx):
|
|
193
|
-
print("
|
|
194
|
-
|
|
195
|
-
comb_vip = list(itertools.combinations(vip_idx, size))
|
|
196
|
-
sample_idx = rng.choice(comb_vip)
|
|
199
|
+
print(f"The initial sampling size {size} is smaller than the number of key points {len(vip_idx)}.")
|
|
200
|
+
sample_idx = random.sample(vip_idx, size)
|
|
197
201
|
self._append_sample_to_sg(point_idx=sample_idx)
|
|
198
|
-
else:
|
|
199
|
-
self.
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
202
|
+
else:
|
|
203
|
+
sample_idx = KeyPointSGSampler(self.sg_obj, **self.kwargs).samples(**kwargs)
|
|
204
|
+
while len(sample_idx) < size:
|
|
205
|
+
adding_sample = MaxDiversitySGSampler(self.sg_obj).samples(size=size-len(sample_idx),**kwargs)
|
|
206
|
+
sample_idx = np.concatenate([sample_idx, adding_sample])
|
|
203
207
|
return sample_idx
|
|
204
208
|
|
|
205
209
|
def samples(self, size=1, **kwargs):
|
|
206
210
|
return self._samples(size=size, **kwargs)
|
|
207
211
|
|
|
208
|
-
|
|
212
|
+
|
|
213
|
+
class MaxDiversitySGSampler(SGSamplerBase):
|
|
209
214
|
"""
|
|
210
215
|
对当前采样结构差异最大的点进行采样
|
|
211
216
|
基本思路是这样的:
|
|
@@ -215,14 +220,13 @@ class MaxDiversitySampling(SamplingBase):
|
|
|
215
220
|
"""
|
|
216
221
|
def _samples(self, size, center=True, **kwargs):
|
|
217
222
|
"""
|
|
218
|
-
|
|
219
223
|
:param size:
|
|
220
224
|
:param center: 是否取中心。如果不是,则取能量最小值的点。如果没有能量则报错。
|
|
221
225
|
:param kwargs:
|
|
222
226
|
:return:
|
|
223
227
|
"""
|
|
224
228
|
# 判断是否有过往的采样点,如果没有,调用 InitialSampling
|
|
225
|
-
if self.sg_obj.sample_idx
|
|
229
|
+
if len(self.sg_obj.sample_idx) == 0:
|
|
226
230
|
clusters = Cluster(n_clusters=size).fit(self.sg_obj.vector)
|
|
227
231
|
virgin = list(set(clusters.labels_))
|
|
228
232
|
else:
|
|
@@ -254,9 +258,7 @@ class MaxDiversitySampling(SamplingBase):
|
|
|
254
258
|
virgin = larger_virgin
|
|
255
259
|
break
|
|
256
260
|
# 从 virgin 里面选取 size 个点
|
|
257
|
-
|
|
258
|
-
comb_vip = list(itertools.combinations(list(virgin), size))
|
|
259
|
-
cluster_idx = rng.choice(comb_vip)
|
|
261
|
+
cluster_idx = random.sample(list(virgin), size)
|
|
260
262
|
if (not center) and 'energy' not in self.sg_obj.grid_property:
|
|
261
263
|
center = True
|
|
262
264
|
print("Warning: Can't get cluster minimum energy, use cluster center instead!")
|
|
@@ -278,7 +280,7 @@ class MaxDiversitySampling(SamplingBase):
|
|
|
278
280
|
return point_idx
|
|
279
281
|
|
|
280
282
|
|
|
281
|
-
class
|
|
283
|
+
class NewtonSGSampler(SGSamplerBase):
|
|
282
284
|
"""
|
|
283
285
|
沿着受力方向进行采样
|
|
284
286
|
"""
|
|
@@ -287,7 +289,7 @@ class NewtonSampling(SamplingBase):
|
|
|
287
289
|
raise NotImplementedError
|
|
288
290
|
|
|
289
291
|
|
|
290
|
-
class RandomWalk(
|
|
292
|
+
class RandomWalk(SGSamplerBase):
|
|
291
293
|
"""
|
|
292
294
|
从给定点出发随机行走进行采样
|
|
293
295
|
"""
|
|
@@ -295,7 +297,7 @@ class RandomWalk(SamplingBase):
|
|
|
295
297
|
raise NotImplementedError
|
|
296
298
|
|
|
297
299
|
|
|
298
|
-
class
|
|
300
|
+
class SystematicSGSampler(SGSamplerBase):
|
|
299
301
|
"""
|
|
300
302
|
等距采样。主要用于测试。
|
|
301
303
|
"""
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
class AdsGridCombiner:
|
|
2
|
+
def __init__(self, sg_obj, ads_obj,
|
|
3
|
+
**kwargs):
|
|
4
|
+
"""
|
|
5
|
+
:param sg_obj: 表面格点
|
|
6
|
+
:param ads_obj: 吸附分子,包含Atoms,主轴,内坐标列表,根据这些参数可以得到分子坐标
|
|
7
|
+
:param kwargs:
|
|
8
|
+
"""
|
|
9
|
+
self.atoms = None
|
|
10
|
+
self.sg_obj = sg_obj
|
|
11
|
+
self.ads_obj = ads_obj
|
|
12
|
+
self.kwargs = kwargs
|
|
13
|
+
if self.sg_obj.atoms.calc is not None:
|
|
14
|
+
self.calc = self.sg_obj.atoms.calc
|
|
15
|
+
else:
|
|
16
|
+
if self.ads_obj.atoms.calc is not None:
|
|
17
|
+
self.calc = self.ads_obj.atoms.calc
|
|
18
|
+
else:
|
|
19
|
+
self.calc = None
|
|
20
|
+
|
|
21
|
+
def get_atoms(self, grid_idx=None,**kwargs):
|
|
22
|
+
"""
|
|
23
|
+
将分子放置于 grid_idx 格点上
|
|
24
|
+
:param grid_idx: 格点序号。默认是随机idx。
|
|
25
|
+
:return: 组合后的 Atoms
|
|
26
|
+
"""
|
|
27
|
+
attitude = kwargs.get('attitude') # 分子姿态,分子主轴的夹角
|
|
28
|
+
internal_coord = kwargs.get('internal_coord') # 分子内坐标
|
|
29
|
+
ads_atoms = self.ads_obj.atoms.copy()
|
|
30
|
+
# TODO: 先进性 rotate and 改变分子构象
|
|
31
|
+
site = self.sg_obj.points[grid_idx]
|
|
32
|
+
# TODO: get_z 去更新 site 的坐标
|
|
33
|
+
site = self._get_z(site, ads_atoms)
|
|
34
|
+
ads_atoms.set_positions(ads_atoms.get_positions() +
|
|
35
|
+
(site - ads_atoms.get_center_of_mass()))
|
|
36
|
+
|
|
37
|
+
atoms = self.sg_obj.atoms.copy()
|
|
38
|
+
atoms += ads_atoms
|
|
39
|
+
atoms.calc = self.calc
|
|
40
|
+
self.atoms = atoms
|
|
41
|
+
return atoms
|
|
42
|
+
|
|
43
|
+
def _get_z(self, xyz0, ads_atoms):
|
|
44
|
+
"""
|
|
45
|
+
由于分子可能会与表面冲突,调整分子的高度可以避免
|
|
46
|
+
事实上调整的是沿着格点法向向量的距离 xyz = xyz0 + r×d
|
|
47
|
+
:return:
|
|
48
|
+
"""
|
|
49
|
+
xyz = xyz0
|
|
50
|
+
return xyz
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from ase import Atoms
|
|
3
|
+
|
|
4
|
+
class Adsorbate:
|
|
5
|
+
"""
|
|
6
|
+
吸附分子的类,包含质心,内坐标,主轴
|
|
7
|
+
"""
|
|
8
|
+
def __init__(self, atoms, **kwargs):
|
|
9
|
+
# TODO:加上半径的参数,计算分子半径
|
|
10
|
+
self.atoms = atoms
|
|
11
|
+
self.internal_coords = dict()
|
|
12
|
+
self.kwargs = kwargs
|
|
13
|
+
self._rads = 0.76 # 默认是碳原子的共价半径
|
|
14
|
+
|
|
15
|
+
@property
|
|
16
|
+
def com(self):
|
|
17
|
+
return self.atoms.center_of_mass()
|
|
18
|
+
|
|
19
|
+
@property
|
|
20
|
+
def principal_axis(self):
|
|
21
|
+
# 分子主轴向量
|
|
22
|
+
evals, evecs = self.atoms.get_moments_of_inertia(vectors=True)
|
|
23
|
+
return evecs[np.argmin(np.abs(evals))]
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def rads(self):
|
|
27
|
+
# 分子的半径,sg_obj 构造时作为参考
|
|
28
|
+
# TODO: 计算分子半径
|
|
29
|
+
return self._rads
|
|
30
|
+
|
|
31
|
+
@rads.setter
|
|
32
|
+
def rads(self, value):
|
|
33
|
+
self._rads = value
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def natoms(self):
|
|
37
|
+
return len(self.atoms)
|
|
38
|
+
|