mgtoolbox-kernel 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,219 @@
1
+ from typing import Dict, Union, List
2
+ import math
3
+ import numpy as np
4
+ from .mgclass import MGobject
5
+ from .atom import Atom
6
+
7
+
8
+ class Site(MGobject):
9
+ """
10
+ Site 站点类
11
+ 用于描述材料结构中的站点
12
+ Parameters
13
+ ----------
14
+ object : [type]
15
+ [description]
16
+ """
17
+
18
+ def __init__(
19
+ self,
20
+ coord: np.ndarray,
21
+ occupier: Dict[Atom, float] = None,
22
+ label: str = "",
23
+ site_type: str = "",
24
+ attributes: Dict = None,
25
+ ):
26
+ """
27
+ __init__ 站点类初始化
28
+ 站点类初始化函数
29
+ Parameters
30
+ ----------
31
+ coords : np.ndarray
32
+ 站点位置分数坐标
33
+ occupier : Dict, optional
34
+ 站点包含的原子及占据率信息, by default None
35
+ label: str, optional
36
+ 站点标签信息
37
+ label: str, optional
38
+ 站点类型信息
39
+ attributes : Dict, optional
40
+ 站点类的其它可自定义属性, by default None。 如{"radius":10}
41
+ """
42
+ if attributes is None:
43
+ super().__init__(attributes={})
44
+ else:
45
+ super().__init__(attributes)
46
+ self.label: str = label
47
+ self.type: str = site_type
48
+ self.coord: np.ndarray = np.array(coord)
49
+ if occupier is None:
50
+ self.occupier: Dict[Atom, float] = {}
51
+ else:
52
+ self.occupier: Dict[Atom, float] = occupier
53
+
54
+
55
+ def __eq__(self, other: "Site") -> bool:
56
+ # 判断是否同一站点仅比较站点坐标
57
+ return np.all(np.isclose(self.coord, other.coord, rtol=0.001))
58
+
59
+ def __str__(self) -> str:
60
+ site_string = (
61
+ "\n{"
62
+ + "label:"
63
+ + self.label
64
+ + ", type:"
65
+ + str(self.type)
66
+ + ", coord:"
67
+ + str(self.coord)
68
+ + ", occupier:"
69
+ + str(self.occupier)
70
+ + "}"
71
+ )
72
+ return site_string
73
+
74
+ def __repr__(self) -> str:
75
+ return self.__str__()
76
+
77
+ @property
78
+ def atoms(self) -> list:
79
+ return list(self.occupier.keys())
80
+
81
+ @property
82
+ def atom_symbols(self) -> list:
83
+ return [atom.symbol for atom in self.occupier.keys()]
84
+
85
+ @property
86
+ def atom_valences(self) -> list:
87
+ return [atom.valence_state for atom in self.occupier.keys()]
88
+
89
+ @property
90
+ def atom_occupancies(self) -> list:
91
+ return list(self.occupier.values())
92
+
93
+ @property
94
+ def x(self) -> float:
95
+ return self.coord[0]
96
+
97
+ @property
98
+ def y(self) -> float:
99
+ return self.coord[1]
100
+
101
+ @property
102
+ def z(self) -> float:
103
+ return self.coord[2]
104
+
105
+ @x.setter
106
+ def x(self, x: float):
107
+ self.coord[0] = x
108
+
109
+ @y.setter
110
+ def y(self, y: float):
111
+ self.coord[1] = y
112
+
113
+ @z.setter
114
+ def z(self, z: float):
115
+ self.coord[2] = z
116
+
117
+ @property
118
+ def is_ordered(self):
119
+ """站点是否有序(满足站点上只包含一个原子且占据率为1.0)
120
+
121
+ Returns
122
+ -------
123
+ bool
124
+ 是否有序
125
+ """
126
+ return len(self.atoms) == 1 and math.isclose(self.atom_occupancies[0], 1.0)
127
+
128
+ def get_element_occupy(self, symbol: str):
129
+ """_summary_
130
+
131
+ Parameters
132
+ ----------
133
+ symbol : str
134
+ _description_
135
+
136
+ Returns
137
+ -------
138
+ _type_
139
+ _description_
140
+ """
141
+ for atom, occupy in self.occupier.items():
142
+ if atom.symbol == symbol:
143
+ return occupy
144
+
145
+ def assign_occupier(self, atom: Atom, occupy: float = 1.0):
146
+ """
147
+ assign_occupier 设置站点包含的原子信息
148
+
149
+ 设置站点包含的原子,如原子类型,占有率。
150
+
151
+ Parameters
152
+ ----------
153
+ atom : Atom
154
+ [添加的原子类型]
155
+ occupy : float, optional
156
+ [占据率], by default 1.0
157
+ """
158
+ if atom in self.atoms: # 若站点上已存在该原子类型,则将占据率求和
159
+ self.occupier[atom] += occupy
160
+ else:
161
+ self.occupier[atom] = occupy
162
+
163
+ def assign_occupier_by_dict(self, occupiper_dict):
164
+ """_summary_
165
+
166
+ Parameters
167
+ ----------
168
+ occupiper_dict : _type_
169
+ _description_
170
+ """
171
+ for k, v in occupiper_dict.items():
172
+ self.assign_occupier(k, v)
173
+
174
+ def assign_occupier_by_symbol(
175
+ self, symbol: str = "", valence_state: float = 0.0, occupy: float = 1.0
176
+ ):
177
+ """_summary_
178
+
179
+ Parameters
180
+ ----------
181
+ symbol : str, optional
182
+ _description_, by default ""
183
+ valence_state : float, optional
184
+ _description_, by default 0.0
185
+ occupy : float, optional
186
+ _description_, by default 1.0
187
+
188
+ Raises
189
+ ------
190
+ ValueError
191
+ _description_
192
+ """
193
+ if len(symbol) > 0:
194
+ atom = Atom(symbol, valence_state)
195
+ if atom not in self.atoms:
196
+ self.assign_occupier(atom, occupy)
197
+ else:
198
+ raise ValueError("Symbol cannot be empty!!!")
199
+
200
+ def get_occupy_sum(self):
201
+ """_summary_
202
+
203
+ Returns
204
+ -------
205
+ _type_
206
+ _description_
207
+
208
+ Raises
209
+ ------
210
+ ValueError
211
+ _description_
212
+ """
213
+ occu_sum = 0
214
+ for occupy in self.occupier.values():
215
+ occu_sum = occu_sum + occupy
216
+ if occu_sum > 1.0:
217
+ raise ValueError("occupy > 1.0")
218
+ return occu_sum
219
+
@@ -0,0 +1,194 @@
1
+ # -*- encoding: utf-8 -*-
2
+ """
3
+ @File : structure.py
4
+ @Time : 2021/04/30 15:49:14
5
+ @Author : 何冰
6
+ @Email : shhebing@qq.com
7
+ @WebSite : https://mgtoolbox.cn
8
+ @Desc : 材料结构相关类。
9
+ """
10
+ import re
11
+ from typing import Dict, List, Union
12
+ from pathlib import Path
13
+
14
+ import numpy as np
15
+ import scipy.spatial
16
+ from mgtoolbox_kernel.io import read_structure_file, read_structure_data
17
+ from .mgclass import MGobject
18
+ from .atom import Atom
19
+ from .cell import Cell
20
+ from .site import Site
21
+
22
+
23
+ class Structure(MGobject):
24
+ def __init__(self, sites: List[Site], cell: Cell, attributes=None):
25
+ if attributes is None:
26
+ attributes = {}
27
+ super().__init__(attributes)
28
+ self.sites: List[Site] = sites
29
+ self.cell: Cell = cell
30
+
31
+ def __repr__(self) -> str:
32
+ sstring = str({"cell": self.cell, "sites": self.sites})
33
+ return sstring
34
+
35
+ def add_site(self, site: Site):
36
+ self.sites.append(site)
37
+
38
+ def add_sites(self, sites: List[Site]):
39
+ self.sites += sites
40
+
41
+ def remove_sites(self, sites: List[Site]):
42
+ for site in sites:
43
+ self.sites.remove(site)
44
+
45
+ def set_cell(self, cell: Cell):
46
+ self.cell = cell
47
+
48
+ @property
49
+ def is_ordered(self):
50
+ """是否为有序结构
51
+
52
+ Returns
53
+ -------
54
+ bool
55
+ 是否有序
56
+ """
57
+ return all((site.is_ordered for site in self.sites))
58
+
59
+ @staticmethod
60
+ def from_file(filename: Union[str, Path]) -> Union["Structure", List["Structure"]]:
61
+ structs_mode = read_structure_file(filename)
62
+ structs = []
63
+ for struct_mode in structs_mode:
64
+ struct = Structure.from_struct_model(struct_mode)
65
+ structs.append(struct)
66
+ if len(structs) == 1:
67
+ return structs[0]
68
+ else:
69
+ return structs
70
+
71
+ @staticmethod
72
+ def from_data(file_data: str):
73
+ # 默认cif格式文件数据
74
+ structs_mode = read_structure_data(file_data)
75
+ structs = []
76
+ for struct_mode in structs_mode:
77
+ struct = Structure.from_struct_model(struct_mode)
78
+ structs.append(struct)
79
+ if len(structs) == 1:
80
+ return structs[0]
81
+ else:
82
+ return structs
83
+
84
+ @staticmethod
85
+ def merge_eq_coords(coords):
86
+ Structure.adjust_coords(coords)
87
+ dm = scipy.spatial.distance_matrix(coords, coords)
88
+ type_list = list(range(dm.shape[0]))
89
+ for row in range(dm.shape[0]):
90
+ for col in range(row + 1, dm.shape[1]):
91
+ if dm[row][col] < 0.003:
92
+ type_list[col] = type_list[row]
93
+ unique_ids = set(type_list)
94
+ ucoords = coords[list(unique_ids)]
95
+ return ucoords
96
+
97
+ @staticmethod
98
+ def adjust_coords(coords):
99
+ """调整分数坐标值
100
+ 使得分数坐标值在0.999-1.001范围的坐标更改为0.0
101
+ Parameters
102
+ ----------
103
+ coords : _type_
104
+ _description_
105
+ """
106
+ abs_value = np.abs(coords - 1.0)
107
+ coords[abs_value < 0.001] = 0.0
108
+
109
+ @staticmethod
110
+ def from_struct_model(struct_mode: Dict):
111
+ lattice_paramteters = struct_mode["lattice_parameters"]
112
+ cell = Cell(
113
+ lattice_paramteters[0],
114
+ lattice_paramteters[1],
115
+ lattice_paramteters[2],
116
+ lattice_paramteters[3],
117
+ lattice_paramteters[4],
118
+ lattice_paramteters[5],
119
+ )
120
+ attributes = struct_mode['attributes']
121
+ sites = []
122
+ for struct_site in struct_mode["sites"]:
123
+ coords = np.mod(
124
+ np.dot(struct_mode["symm_ops"][0], struct_site["coord"])
125
+ + struct_mode["symm_ops"][1],
126
+ 1.0,
127
+ )
128
+ # 合并等价位点中坐标相同位点
129
+ # Merge the points with the same coordinates in the equivalent points
130
+ ucoords = Structure.merge_eq_coords(coords)
131
+ for i, coord in enumerate(ucoords):
132
+ occupier = {}
133
+ for symbol, occupy in struct_site["occupancy"].items():
134
+ if occupy < 0.0:
135
+ raise ValueError("Structure file site occupy value error")
136
+ sym = re.findall(r"[A-Z][a-z]?", symbol)
137
+ if struct_mode["oxidation_state"]:
138
+ atom = Atom(sym[0], struct_mode["oxidation_state"][symbol])
139
+ occupier[atom] = occupy
140
+ else:
141
+ # -16 表示该离子化合价无法确定。
142
+ atom = Atom(sym[0], -16)
143
+ occupier[atom] = occupy
144
+ site = Site(coord, occupier)
145
+ site.label = struct_site["label"] + "_" + str(i)
146
+ site.type = struct_site["site_type"]
147
+ sites.append(site)
148
+ unique_sites = Structure.remove_duplicates_sites(sites)
149
+ return Structure(unique_sites, cell, attributes)
150
+
151
+ @staticmethod
152
+ def remove_duplicates_sites(sites: List[Site]):
153
+ result = []
154
+ for item in sites:
155
+ if item not in result:
156
+ result.append(item)
157
+ else:
158
+ result[result.index(item)].assign_occupier_by_dict(item.occupier)
159
+ return result
160
+
161
+ def write_to_cif(self, filename: str):
162
+ with Path(filename).open("+w") as f:
163
+ f.write(f"data_{Path(filename).stem}\n")
164
+
165
+ f.write("_cell_length_a {}\n".format(self.cell.abc[0]))
166
+ f.write("_cell_length_b {}\n".format(self.cell.abc[1]))
167
+ f.write("_cell_length_c {}\n".format(self.cell.abc[2]))
168
+
169
+ f.write("_cell_angle_alpha {}\n".format(self.cell.angles[0]))
170
+ f.write("_cell_angle_beta {}\n".format(self.cell.angles[1]))
171
+ f.write("_cell_angle_gamma {}\n".format(self.cell.angles[2]))
172
+
173
+ f.write("_symmetry_space_group_name_H-M 'P 1'\n")
174
+ f.write("_symmetry_Int_Tables_number 1\n")
175
+
176
+ f.write("loop_\n")
177
+ f.write("_symmetry_equiv_pos_site_id\n")
178
+ f.write("_symmetry_equiv_pos_as_xyz\n")
179
+ f.write("1 'x, y, z'\n")
180
+
181
+ f.write("loop_\n")
182
+ f.write("_atom_site_label\n")
183
+ f.write("_atom_site_type_symbol\n")
184
+ f.write("_atom_site_fract_x\n")
185
+ f.write("_atom_site_fract_y\n")
186
+ f.write("_atom_site_fract_z\n")
187
+ f.write("_atom_site_occupancy\n")
188
+ for site in self.sites:
189
+ for occupier, occupy in site.occupier.items():
190
+ f.write(
191
+ f"{site.label} {occupier.symbol} {site.x} {site.y} {site.z} {occupy}\n"
192
+ )
193
+
194
+ f.write(f"#End of data_{Path(filename).stem}")
File without changes
@@ -0,0 +1,141 @@
1
+ # -*- encoding: utf-8 -*-
2
+ import math
3
+ import re
4
+ import numpy as np
5
+ import periodictable as pt
6
+ from typing import Tuple
7
+
8
+ def parse_sitesym(symlist, sep=','):
9
+ """Parses a sequence of site symmetries in the form used by
10
+ International Tables and returns corresponding rotation and
11
+ translation arrays.
12
+
13
+ Example:
14
+
15
+ >>> symlist = [
16
+ ... 'x,y,z',
17
+ ... '-y+1/2,x+1/2,z',
18
+ ... '-y,-x,-z',
19
+ ... ]
20
+ >>> rot, trans = parse_sitesym(symlist)
21
+ >>> rot
22
+ array([[[ 1, 0, 0],
23
+ [ 0, 1, 0],
24
+ [ 0, 0, 1]],
25
+ <BLANKLINE>
26
+ [[ 0, -1, 0],
27
+ [ 1, 0, 0],
28
+ [ 0, 0, 1]],
29
+ <BLANKLINE>
30
+ [[ 0, -1, 0],
31
+ [-1, 0, 0],
32
+ [ 0, 0, -1]]])
33
+ >>> trans
34
+ array([[ 0. , 0. , 0. ],
35
+ [ 0.5, 0.5, 0. ],
36
+ [ 0. , 0. , 0. ]])
37
+ """
38
+ nsym = len(symlist)
39
+ rot = np.zeros((nsym, 3, 3), dtype='int')
40
+ trans = np.zeros((nsym, 3))
41
+
42
+ trans_pattern = r"([-+]?\d*\.\d*|[-+]?\d+)"
43
+ re_trans = re.compile(trans_pattern)
44
+ rot_pattern=r"[+-]?[xyz]"
45
+ re_rot = re.compile(rot_pattern)
46
+
47
+ for i, sym in enumerate(symlist):
48
+ for j, s in enumerate(sym.split(sep)):
49
+ s = s.lower().strip()
50
+ #得到旋转字符
51
+ strs_matches = re_rot.findall(s)
52
+ #得到偏移数字
53
+ numbers_matches = re_trans.findall(s)
54
+ if len(numbers_matches)==2:
55
+ trans[i, j] = float(numbers_matches[0]) / float(numbers_matches[1])
56
+ elif len(numbers_matches)==1:
57
+ trans[i, j] = float(numbers_matches[0])
58
+ elif len(numbers_matches)==0:
59
+ trans[i, j] = 0.0
60
+ else:
61
+ raise Exception("Read symmetry trans string error")
62
+ if len(strs_matches)<1:
63
+ raise Exception(
64
+ 'Error parsing %r. Invalid site symmetry: %s' %
65
+ (s, sym))
66
+ for sxyz in strs_matches:
67
+ sign = 1.0
68
+ if len(sxyz)==2:
69
+ if sxyz[0] in '+-':
70
+ if sxyz[0] == '-':
71
+ sign = -1.0
72
+ xyz=sxyz[1]
73
+ elif len(sxyz)==1:
74
+ xyz=sxyz[0]
75
+ sign = 1.0
76
+ else:
77
+ raise Exception(
78
+ 'Error parsing %r. Invalid site symmetry: %s' %
79
+ (s, sym))
80
+ if xyz in 'xyz':
81
+ k = ord(xyz) - ord('x')
82
+ rot[i, j, k] = sign
83
+ return rot, trans
84
+ def get_lattice_vectors(a, b, c, alpha, beta, gamma) -> np.ndarray:
85
+ """Get the cell vectors matrix(fix a-axis) from the cell parameters
86
+ Parameters
87
+ ----------
88
+ a : float
89
+ 晶格参数:a
90
+ b : float
91
+ 晶格参数:b
92
+ c : float
93
+ 晶格参数:c
94
+ alpha : float
95
+ 晶格参数:alpha
96
+ beta : float
97
+ 晶格参数:beta
98
+ gamma : float
99
+ 晶格参数:gamma
100
+
101
+ Returns
102
+ -------
103
+ numpy.ndarray
104
+ 晶格矢量矩阵
105
+ """
106
+ angles_r = np.radians([alpha, beta, gamma])
107
+ cos_alpha, cos_beta, cos_gamma = np.cos(angles_r)
108
+ sin_alpha, sin_beta, sin_gamma = np.sin(angles_r)
109
+
110
+ # fix a-axis
111
+ vector_a = a * np.array([1, 0, 0])
112
+ vector_b = b * np.array([cos_gamma, sin_gamma, 0])
113
+ cx = cos_beta
114
+ cy = (cos_alpha - cos_beta * cos_gamma) / sin_gamma
115
+ cz_sqr = 1. - cx * cx - cy * cy
116
+ cz = np.sqrt(cz_sqr)
117
+ vector_c = c * np.array([cx, cy, cz])
118
+ return np.array([vector_a, vector_b, vector_c])
119
+
120
+
121
+ def get_lattice_parameters(lattice_vectors: np.ndarray) -> Tuple:
122
+ """get lattice parameters from the lattice vectors matrix
123
+
124
+ Parameters
125
+ ----------
126
+ lattice_vectors : np.ndarray
127
+ 晶格矢量
128
+
129
+ Returns
130
+ -------
131
+ 元组类型
132
+ 返回晶格常数:a,b,c,alpha,beta,gamma
133
+ """
134
+ abc = np.linalg.norm(lattice_vectors, axis=1)
135
+ arc_alpha = np.arccos(np.dot(lattice_vectors[1], lattice_vectors[2]) / (abc[1] * abc[2]))
136
+ arc_beta = np.arccos(np.dot(lattice_vectors[0], lattice_vectors[2]) / (abc[0] * abc[2]))
137
+ arc_gamma = np.arccos(np.dot(lattice_vectors[1], lattice_vectors[0]) / (abc[1] * abc[0]))
138
+ arc_values = np.array([arc_alpha, arc_beta, arc_gamma])
139
+ alpha, beta, gamma = arc_values * 180 / math.pi
140
+ return tuple([abc[0], abc[1], abc[2], alpha, beta, gamma])
141
+
@@ -0,0 +1,15 @@
1
+ Metadata-Version: 2.1
2
+ Name: mgtoolbox-kernel
3
+ Version: 0.1.0
4
+ Home-page: https://gitee.com/shuhebing/mgtoolbox_kernel
5
+ Author: Bing He
6
+ Author-email: shhebing@qq.com
7
+ Requires-Python: >=3.7,<=3.12
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: pycifrw
10
+ Requires-Dist: numpy
11
+ Requires-Dist: scipy
12
+ Requires-Dist: pandas
13
+ Requires-Dist: spglib
14
+
15
+ # MGToolBox Materials Genome Toolbox
@@ -0,0 +1,18 @@
1
+ mgtoolbox_kernel/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ mgtoolbox_kernel/io/__init__.py,sha256=c7NUxkUR7IzAvZKHEqa_81DaH-kZACXY748xWrXd1aA,868
3
+ mgtoolbox_kernel/io/cif.py,sha256=vTqlmr7ONMPrhm3ytooqMNcpVm2_AA6o8bt3IAubZD4,6763
4
+ mgtoolbox_kernel/io/vasp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ mgtoolbox_kernel/io/vasp/poscar.py,sha256=ISmldKyDYVK50IySxvOByEqGHnP-bbjuQkD39JVNjqI,4559
6
+ mgtoolbox_kernel/kernel/__init__.py,sha256=yzBH8rszXZTN8zjbpCG1iraBEsv7uMk3GlUEWoxOytM,32
7
+ mgtoolbox_kernel/kernel/atom.py,sha256=mGV-IC7-WjuEe7ItEh3pHx0wi87uAtWRyOFhgKTGG9s,1430
8
+ mgtoolbox_kernel/kernel/cell.py,sha256=n0NzltcoQiEy3fuFMP0cLc1UwjnslTnuuVgFpa1AviY,15296
9
+ mgtoolbox_kernel/kernel/mgclass.py,sha256=7g_IdostJg7MgQLq-9geIzYI9ccouSDB-vI77xttKXw,179
10
+ mgtoolbox_kernel/kernel/potential.py,sha256=zM50evBCTiYhFEeOzKdPsyvmqpBeClOqra3s0fHnm20,3329
11
+ mgtoolbox_kernel/kernel/site.py,sha256=93_f1-np31o46ouXha-3SpNknymY4ng8QXQoGwmrYDs,5529
12
+ mgtoolbox_kernel/kernel/structure.py,sha256=GtLd38cSo1tWjcVk-bywPn0qC2umCXX2T1981cHJl2Y,6805
13
+ mgtoolbox_kernel/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ mgtoolbox_kernel/util/base.py,sha256=n1YnhXpqhB9eFyTyxMhRff8QU9egtQ3wMxMMyGNqw_0,4431
15
+ mgtoolbox_kernel-0.1.0.dist-info/METADATA,sha256=uVzqlJiFhGAE45fC_mHqFC85lzeceQy6Iw2EpKgzz_E,394
16
+ mgtoolbox_kernel-0.1.0.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
17
+ mgtoolbox_kernel-0.1.0.dist-info/top_level.txt,sha256=Od6EGc_Q9VZLH9OaHl7lcjfJBBZQ-rjhZujgsw8-tRs,17
18
+ mgtoolbox_kernel-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: bdist_wheel (0.40.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ mgtoolbox_kernel