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.
File without changes
@@ -0,0 +1,27 @@
1
+ from pathlib import Path
2
+ from typing import Dict, List, Union
3
+ from mgtoolbox_kernel.io.cif import CifFileParse
4
+ from mgtoolbox_kernel.io.vasp.poscar import PoscarParse
5
+
6
+
7
+
8
+ def read_structure_file(
9
+ filename:Union[str,Path]
10
+ ) -> Union[Dict[str, Union[str, float]], List[Dict[str, Union[str, float]]]]:
11
+ path = Path(filename)
12
+ if path.suffix == '.cif':
13
+ cifparse = CifFileParse()
14
+ cifparse.read_file(str(path))
15
+ return cifparse.get_structures()
16
+ elif path.suffix == '.vasp' or path.name == 'POSCAR':
17
+ structs = []
18
+ structs.append(PoscarParse(str(path)).get_structure())
19
+ return structs
20
+ structs = []
21
+ structs.append(PoscarParse(filename).get_structure())
22
+ return structs
23
+
24
+ def read_structure_data(file_data):
25
+ cifparse = CifFileParse()
26
+ cifparse.read_file(file_data)
27
+ return cifparse.get_structures()
@@ -0,0 +1,158 @@
1
+ # -*- encoding: utf-8 -*-
2
+ '''
3
+ @File : cif.py
4
+ @Time : 2021/12/1
5
+ @Author : 何冰
6
+ @Email : shhebing@qq.com
7
+ @WebSite : https://mtoolbox.cn
8
+ @Desc : cif文件处理相关类。
9
+ '''
10
+ import sys
11
+ from typing import List, Union, Dict
12
+ import numpy as np
13
+ from scipy.spatial import distance_matrix
14
+ import spglib as spg
15
+ from CifFile import ReadCif, get_number_with_esd
16
+ from mgtoolbox_kernel.util.base import parse_sitesym
17
+
18
+ spacegroup_to_hall_number = [
19
+ 0, 1, 2, 3, 6, 9, 18, 21, 30, 39, 57, 60, 63, 72, 81, 90, 108, 109, 112,
20
+ 115, 116, 119, 122, 123, 124, 125, 128, 134, 137, 143, 149, 155, 161, 164,
21
+ 170, 173, 176, 182, 185, 191, 197, 203, 209, 212, 215, 218, 221, 227, 228,
22
+ 230, 233, 239, 245, 251, 257, 263, 266, 269, 275, 278, 284, 290, 292, 298,
23
+ 304, 310, 313, 316, 322, 334, 335, 337, 338, 341, 343, 349, 350, 351, 352,
24
+ 353, 354, 355, 356, 357, 358, 359, 361, 363, 364, 366, 367, 368, 369, 370,
25
+ 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385,
26
+ 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400,
27
+ 401, 402, 404, 406, 407, 408, 410, 412, 413, 414, 416, 418, 419, 420, 422,
28
+ 424, 425, 426, 428, 430, 431, 432, 433, 435, 436, 438, 439, 440, 441, 442,
29
+ 443, 444, 446, 447, 448, 449, 450, 452, 454, 455, 456, 457, 458, 460, 462,
30
+ 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477,
31
+ 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492,
32
+ 493, 494, 495, 497, 498, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509,
33
+ 510, 511, 512, 513, 514, 515, 516, 517, 518, 520, 521, 523, 524, 525, 527,
34
+ 529, 530
35
+ ]
36
+
37
+
38
+ class CifFileParse(object):
39
+
40
+ def __init__(self):
41
+ self._structures = []
42
+
43
+ def read_file(self, filename):
44
+ cif_file = ReadCif(filename, scantype='flex')
45
+ for cif_struct in cif_file:
46
+ structure = self.__get_structure(cif_struct)
47
+ if structure:
48
+ self._structures.append(structure)
49
+
50
+ def get_structures(self) -> List[Dict[str, Union[str, float]]]:
51
+ return self._structures
52
+
53
+ def __get_lattice_parameter(self, cif_struc):
54
+ cell = np.zeros(6, )
55
+ cell[0] = get_number_with_esd(cif_struc['_cell_length_a'])[0]
56
+ cell[1] = get_number_with_esd(cif_struc['_cell_length_b'])[0]
57
+ cell[2] = get_number_with_esd(cif_struc['_cell_length_c'])[0]
58
+ cell[3] = get_number_with_esd(cif_struc['_cell_angle_alpha'])[0]
59
+ cell[4] = get_number_with_esd(cif_struc['_cell_angle_beta'])[0]
60
+ cell[5] = get_number_with_esd(cif_struc['_cell_angle_gamma'])[0]
61
+ return cell
62
+
63
+ def __get_symm_ops(self, cif_struc):
64
+ if '_symmetry_equiv_pos_as_xyz' in cif_struc:
65
+ return self.__parse_sitesym(
66
+ cif_struc['_symmetry_equiv_pos_as_xyz'])
67
+ elif '_symmetry_Int_Tables_number' in cif_struc:
68
+ spgnumber = int(cif_struc['_symmetry_Int_Tables_number'])
69
+ spgroup = spg.get_symmetry_from_database(
70
+ spacegroup_to_hall_number[spgnumber])
71
+ return spgroup['rotations'], spgroup['translations']
72
+ elif '_space_group_IT_number' in cif_struc:
73
+ spgnumber = int(cif_struc['_space_group_IT_number'])
74
+ spgroup = spg.get_symmetry_from_database(
75
+ spacegroup_to_hall_number[spgnumber])
76
+ return spgroup['rotations'], spgroup['translations']
77
+ else:
78
+ # 若以上参数结构文件中都不包含,默认为'x,y,z'
79
+ return self.__parse_sitesym(['x,y,z'])
80
+
81
+ def __get_oxidation(self, cif_struc):
82
+ oxi = {}
83
+ if '_atom_type_oxidation_number' not in cif_struc:
84
+ return None
85
+ for i, key in enumerate(cif_struc['_atom_type_symbol']):
86
+ oxi[key] = float(cif_struc['_atom_type_oxidation_number'][i])
87
+ return oxi
88
+
89
+ def __get_sites(self, cif_struc):
90
+ sites = []
91
+ for i, item in enumerate(cif_struc['_atom_site_label']):
92
+ site = {}
93
+ site['label'] = item
94
+ site['site_type'] = cif_struc['_atom_site_type_symbol'][i]
95
+ x = get_number_with_esd(cif_struc['_atom_site_fract_x'][i])[0]
96
+ y = get_number_with_esd(cif_struc['_atom_site_fract_y'][i])[0]
97
+ z = get_number_with_esd(cif_struc['_atom_site_fract_z'][i])[0]
98
+ site['coord'] = np.array([x, y, z])
99
+ site['occupancy'] = {
100
+ cif_struc['_atom_site_type_symbol'][i]:
101
+ get_number_with_esd(cif_struc['_atom_site_occupancy'][i])[0]
102
+ }
103
+ sites.append(site)
104
+
105
+ return self.__merge_sites(sites)
106
+
107
+ def __merge_sites(self, sites):
108
+ site_no = self.__get_coord_eq_sites(sites)
109
+ unique_no = set(site_no)
110
+ unique_sites = []
111
+ for uno in unique_no:
112
+ unique_sites.append(sites[uno])
113
+ unique_sites[-1]['site_type'] = uno
114
+ for i in range(len(site_no)):
115
+ if (uno == site_no[i]) and uno != i:
116
+ for k, v in sites[i]['occupancy'].items():
117
+ unique_sites[-1]['occupancy'][k] = v
118
+ return unique_sites
119
+
120
+ def __get_coord_eq_sites(self, sites: list):
121
+ sites_coords = np.array([coord['coord'] for coord in sites])
122
+ dm = distance_matrix(sites_coords, sites_coords)
123
+ type = list(range(dm.shape[0]))
124
+ for row in range(dm.shape[0]):
125
+ for col in range(row + 1, dm.shape[1]):
126
+ if dm[row][col] < 0.0001:
127
+ type[col] = type[row]
128
+ return type
129
+
130
+ def __parse_sitesym(self, symlist, sep=','):
131
+ parse_sitesym(symlist,sep)
132
+
133
+ def __get_attributes(self, cif_struc):
134
+ attr = {}
135
+ if '_chemical_formula_sum' in cif_struc:
136
+ formula = cif_struc['_chemical_formula_sum']
137
+ elif '_chemical_formula_structural' in cif_struc:
138
+ formula = cif_struc['_chemical_formula_structural']
139
+ elif '_chemical_formula_moiety' in cif_struc:
140
+ formula = cif_struc['_chemical_formula_moiety']
141
+ else:
142
+ formula = None
143
+ attr['chemical_formula'] = formula
144
+ return attr
145
+
146
+ def __get_structure(self, cif_struc):
147
+ structure = {}
148
+ try:
149
+ structure['lattice_parameters'] = self.__get_lattice_parameter(
150
+ cif_struc)
151
+ structure['sites'] = self.__get_sites(cif_struc)
152
+ structure['symm_ops'] = self.__get_symm_ops(cif_struc)
153
+ structure['oxidation_state'] = self.__get_oxidation(cif_struc)
154
+ structure['attributes'] = self.__get_attributes(cif_struc)
155
+ except (KeyError) as e:
156
+ sys.stderr.write(str(e) + '\n')
157
+ return None
158
+ return structure
File without changes
@@ -0,0 +1,132 @@
1
+ # -*- encoding: utf-8 -*-
2
+ """
3
+ @File : poscar.py
4
+ @Time : 2022/01/12 08:56:01
5
+ @Author : 何冰
6
+ @Version : 0.1
7
+ @Email : shhebing@qq.com
8
+ @WebSite : https://mtoolbox.cn
9
+ @Desc : Read vasp,poscar file convert to structure model
10
+ """
11
+ import sys
12
+ from pathlib import Path
13
+ from typing import List
14
+
15
+ import numpy as np
16
+ import spglib as spg
17
+
18
+ from mgtoolbox_kernel.util.base import get_lattice_parameters
19
+
20
+
21
+ class PoscarParse(object):
22
+ def __init__(self, filename=None):
23
+ self.structure: dict = {}
24
+ self.poscar_string: str = ""
25
+ self.lattice_vectors: np.ndarray = np.zeros((3, 3))
26
+ self.sites: List = []
27
+ if filename:
28
+ self.read_file(filename)
29
+
30
+ def read_file(self, filename: str):
31
+ vaspfile = Path(filename)
32
+ with vaspfile.open() as f:
33
+ self.__poscar_lines_list = f.readlines()
34
+ self.__parse(self.__poscar_lines_list)
35
+
36
+ def __parse(self, poscar_lines: List[str]):
37
+ lines = poscar_lines
38
+ self.comment = lines[0].strip()
39
+ scale = float(lines[1].split()[0])
40
+ if scale < 0:
41
+ scale = 1.0
42
+
43
+ self.lattice_vectors[0] = scale * np.array(
44
+ [float(x) for x in lines[2].split()[0:3]]
45
+ )
46
+ self.lattice_vectors[1] = scale * np.array(
47
+ [float(x) for x in lines[3].split()[0:3]]
48
+ )
49
+ self.lattice_vectors[2] = scale * np.array(
50
+ [float(x) for x in lines[4].split()[0:3]]
51
+ )
52
+ self.natoms_list = np.array([int(x) for x in lines[5].split() if x.isdigit()])
53
+ self.species_list = None
54
+ self.natom_of_species = {}
55
+ self.site_species_list = []
56
+ if self.natoms_list.size == 0:
57
+ self.species_list = [x for x in lines[5].split()]
58
+ self.natoms_list = np.array(
59
+ [int(x) for x in lines[6].split() if x.isdigit()]
60
+ )
61
+ for i, key in enumerate(self.species_list):
62
+ self.natom_of_species[key] = self.natoms_list[i]
63
+ self.site_species_list += [key] * self.natoms_list[i]
64
+ natoms = np.sum(self.natoms_list)
65
+ if self.species_list is None:
66
+ raise ValueError("Not supported the file format")
67
+ coord_mode = lines[7]
68
+ current_line = 7
69
+ if coord_mode[0].lower() in ["s"]:
70
+ current_line += 1
71
+ coord_mode = lines[current_line]
72
+ self.coord_type = None
73
+ if coord_mode[0].lower() in ["c", "k"]:
74
+ self.coord_type = "Cartesian"
75
+ elif coord_mode[0].lower() in ["d"]:
76
+ self.corrd_type = "Direct"
77
+ self.coords = np.zeros((natoms, 3))
78
+ for i in range(natoms):
79
+ current_line += 1
80
+ self.coords[i] = [float(x) for x in lines[current_line].split()[0:3]]
81
+
82
+ def get_structure(self):
83
+ structure = {}
84
+ try:
85
+ structure["lattice_parameters"] = self.__get_lattice_parameter()
86
+ structure["sites"] = self.__get_sites()
87
+ structure["symm_ops"] = self.__get_symm_ops()
88
+ structure["oxidation_state"] = None
89
+ structure['attributes'] = None
90
+ except KeyError as e:
91
+ sys.stderr.write(str(e) + "\n")
92
+ return None
93
+ return structure
94
+
95
+ def __get_lattice_parameter(self):
96
+ return get_lattice_parameters(self.lattice_vectors)
97
+
98
+ def __get_sites(self):
99
+ sites = []
100
+ label_id = 0
101
+ current_species = ""
102
+ for i, item in enumerate(self.coords):
103
+ site = {}
104
+ if current_species != self.site_species_list[i]:
105
+ label_id = 1
106
+ site["label"] = self.site_species_list[i] + str(label_id)
107
+ site["site_type"] = self.site_species_list[i]
108
+ site["coord"] = item
109
+ site["occupancy"] = {self.site_species_list[i]: 1.0}
110
+ sites.append(site)
111
+ label_id += 1
112
+ current_species = self.site_species_list[i]
113
+ return sites
114
+
115
+ def __get_symm_ops(self):
116
+ spgroup = spg.get_symmetry_from_database(1)
117
+ return spgroup["rotations"], spgroup["translations"]
118
+
119
+
120
+ if __name__ == "__main__":
121
+ ciff = PoscarParse()
122
+ ciff.read_file("./devtest/vasptest.vasp")
123
+ structs = [ciff.get_structure()]
124
+ print(len(structs))
125
+ for struct in structs:
126
+ print(struct["symm_ops"][0])
127
+ print(struct["symm_ops"][1])
128
+ print(
129
+ spg.get_hall_number_from_symmetry(
130
+ struct["symm_ops"][0], struct["symm_ops"][1], symprec=1e-5
131
+ )
132
+ )
@@ -0,0 +1 @@
1
+ from .structure import Structure
@@ -0,0 +1,53 @@
1
+ import math
2
+ from .mgclass import MGobject
3
+
4
+
5
+ class Atom(MGobject):
6
+ """Atom 离子类型
7
+ 用于表示结构中离子类型
8
+ Parameters
9
+ ----------
10
+ object : [type]
11
+ [description]
12
+ """
13
+
14
+ def __init__(self, symbol: str, valence_state: float):
15
+ """__init__ 初始化函数
16
+
17
+ Parameters
18
+ ----------
19
+ symbol : str 离子类型
20
+ 离子类型 如O,Fe,Al3+,Li1+ 等
21
+ valence_state : [float]
22
+ 离子化合价
23
+ attributes: dict
24
+ 其他属性
25
+ """
26
+ super().__init__()
27
+ # 离子元素类型 O,Fe etc
28
+ self.symbol: str = symbol
29
+ # 离子化合价 -1,+2,...等。
30
+ self.valence_state: float = valence_state
31
+ self.hash_value = hash(self.symbol + str(self.valence_state))
32
+
33
+ def __eq__(self, other: 'Atom') -> bool:
34
+ return self.symbol == other.symbol and (
35
+ math.fabs(self.valence_state - other.valence_state) < 0.001)
36
+
37
+ def __str__(self) -> str:
38
+ return '[symbol_type:' + self.symbol + ', valence:' + str(
39
+ self.valence_state) + ']'
40
+
41
+ def __hash__(self):
42
+ return self.hash_value
43
+
44
+ def __repr__(self):
45
+ return self.__str__()
46
+
47
+ def __lt__(self, other):
48
+ # 定义 < 操作符
49
+ return self.symbol < other.symbol
50
+
51
+ def __gt__(self, other):
52
+ # 定义 > 操作符
53
+ return self.symbol > other.symbol