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.
- mgtoolbox_kernel/__init__.py +0 -0
- mgtoolbox_kernel/io/__init__.py +27 -0
- mgtoolbox_kernel/io/cif.py +158 -0
- mgtoolbox_kernel/io/vasp/__init__.py +0 -0
- mgtoolbox_kernel/io/vasp/poscar.py +132 -0
- mgtoolbox_kernel/kernel/__init__.py +1 -0
- mgtoolbox_kernel/kernel/atom.py +53 -0
- mgtoolbox_kernel/kernel/cell.py +452 -0
- mgtoolbox_kernel/kernel/mgclass.py +8 -0
- mgtoolbox_kernel/kernel/potential.py +81 -0
- mgtoolbox_kernel/kernel/site.py +219 -0
- mgtoolbox_kernel/kernel/structure.py +194 -0
- mgtoolbox_kernel/util/__init__.py +0 -0
- mgtoolbox_kernel/util/base.py +141 -0
- mgtoolbox_kernel-0.1.0.dist-info/METADATA +15 -0
- mgtoolbox_kernel-0.1.0.dist-info/RECORD +18 -0
- mgtoolbox_kernel-0.1.0.dist-info/WHEEL +5 -0
- mgtoolbox_kernel-0.1.0.dist-info/top_level.txt +1 -0
|
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
|