363 lines
14 KiB
Python
363 lines
14 KiB
Python
from pymatgen.core import Structure
|
||
from pymatgen.core.periodic_table import Element, Specie
|
||
from pymatgen.analysis.local_env import CrystalNN
|
||
from pymatgen.analysis.structure_matcher import StructureMatcher
|
||
from pymatgen.io.cif import CifParser
|
||
import numpy as np
|
||
class crystal:
|
||
def __init__(self, file_path, element_positive='Na', mixed_anions=None):
|
||
# self.parse = CifParser(file_path)
|
||
# self.structure = self.parse.get_structures()[0]
|
||
if mixed_anions is None:
|
||
mixed_anions = {frozenset({'S', 'O'}), frozenset({'Cl', 'Br'}),frozenset({'Cl', 'O'}),frozenset({'Cl', 'Br'}),frozenset({'S', 'Cl'})}
|
||
self.structure = Structure.from_file(file_path)
|
||
self.file_path = file_path
|
||
self.element_positive = element_positive
|
||
self.check_all = False
|
||
self.check_basic_result = False
|
||
self.check_high_cn_and_face_sharing_result = False
|
||
self.check_percolation_radius_result = False
|
||
self.check_practical_result = False
|
||
self.anion = ""
|
||
self.anions = ""
|
||
self.mixed_anions = mixed_anions
|
||
#self.initialize()
|
||
|
||
def initialize(self):
|
||
print("e")
|
||
# self.check_basic_result=self.check_basic()
|
||
# self.check_high_cn_and_face_sharing_result = self.check_high_cn_and_face_sharing()
|
||
# self.check_percolation_radius_result = self.check_percolation_radius()
|
||
# self.check_all = self.check_basic_result and self.check_high_cn_and_face_sharing_result and self.check_percolation_radius_result
|
||
# print(f"{self.file_path}done")
|
||
|
||
def check_practical(self):
|
||
structure = self.structure
|
||
|
||
# 检查是否为Li-X-O,Li-P-S
|
||
excluded_X_elements = {'S', 'I', 'Si', 'C', 'P', 'Al', 'Ge', 'Se', 'B', 'Cl'}
|
||
chemical_system_set = structure.chemical_system_set
|
||
|
||
try:
|
||
if len(chemical_system_set) == 3:
|
||
if "Li" in chemical_system_set and "O" in chemical_system_set:
|
||
for element in excluded_X_elements:
|
||
if element in chemical_system_set:
|
||
return False
|
||
if "Li" in chemical_system_set and "P" in chemical_system_set and "S" in chemical_system_set:
|
||
return False
|
||
except Exception as e:
|
||
print(f"Error during Li-X-O check: {e}")
|
||
return False
|
||
|
||
# 排除过渡金属元素
|
||
excluded_transition_metals = {'Fe', 'Mn', 'Ni', 'Ti', 'Mo', 'V', 'Co'}
|
||
try:
|
||
if "Li" in chemical_system_set and "O" in chemical_system_set:
|
||
for element in excluded_transition_metals:
|
||
if element in chemical_system_set:
|
||
return False
|
||
except Exception as e:
|
||
print(f"Error during transition metal check: {e}")
|
||
return False
|
||
|
||
# 检查是否包含N, Re, Ho, Hf, Ru, Eu, Lu
|
||
excluded_elements = {'N', 'Re', 'Ho', 'Hf', 'Ru', 'Lu'}
|
||
try:
|
||
for element in excluded_elements:
|
||
if element in chemical_system_set:
|
||
return False
|
||
except Exception as e:
|
||
print(f"Error during excluded elements check: {e}")
|
||
return False
|
||
|
||
# 检查是否共享位点
|
||
try:
|
||
for site in structure.sites:
|
||
if 'Li' in site.species_string and len(site.species) > 1:
|
||
return False
|
||
except Exception as e:
|
||
print(f"Error during site sharing check: {e}")
|
||
return False
|
||
|
||
self.check_practical_result = True
|
||
return True
|
||
|
||
def check_basic(self):
|
||
structure = self.structure
|
||
#判断是否为二元化合物
|
||
if len(structure.types_of_specie) == 2:
|
||
return False
|
||
#判断阴离子是否为多种阴离子
|
||
# anions = {'O', 'S', 'Se', 'Te', 'F', 'Cl', 'Br', 'I'}
|
||
anions = {'O', 'S','Br','Cl'}
|
||
try:
|
||
for site in self.structure.sites:
|
||
try:
|
||
#if site.specie.symbol in anions:
|
||
if site.species.chemical_system in anions:
|
||
self.anion = site.specie.symbol
|
||
break
|
||
except AttributeError as e:
|
||
a=1
|
||
try:
|
||
if site.species.chemical_system in anions:
|
||
self.anion = site.specie.symbol
|
||
break
|
||
except AttributeError as e:
|
||
print(e)
|
||
if self.anion in anions:
|
||
a=1
|
||
else:
|
||
if not self.mixed_anions:
|
||
print("不是所选阴离子化合物")
|
||
return False
|
||
except Exception as e:
|
||
print(e)
|
||
return False
|
||
#这里添加对多种阴离子的支持
|
||
try:
|
||
# 创建一个集合来收集所有发现的阴离子
|
||
found_anions = set()
|
||
|
||
# 遍历structure以收集所有阴离子
|
||
for site in self.structure.sites:
|
||
try:
|
||
if site.species.chemical_system in anions:
|
||
found_anions.add(site.specie.symbol)
|
||
except AttributeError:
|
||
try:
|
||
if site.specie.symbol in anions:
|
||
found_anions.add(site.specie.symbol)
|
||
except AttributeError:
|
||
continue
|
||
|
||
# 检查找到的阴离子情况
|
||
if len(found_anions) == 0:
|
||
print("未找到任何预定义的阴离子")
|
||
return False
|
||
elif len(found_anions) == 1:
|
||
# 只有一种阴离子
|
||
self.anion = list(found_anions)[0]
|
||
print(f"发现单一阴离子: {self.anion}")
|
||
else:
|
||
# 有多种阴离子,检查是否匹配预定义的混合阴离子组合
|
||
found_anions_frozen = frozenset(found_anions)
|
||
if found_anions_frozen in self.mixed_anions:
|
||
self.anions = found_anions
|
||
self.anion = "+".join(sorted(found_anions)) # 例如: "Cl+S"
|
||
print(f"发现匹配的混合阴离子组合: {self.anion}")
|
||
else:
|
||
# 如果找到的阴离子组合不在预定义列表中
|
||
print(f"发现的阴离子组合 {found_anions} 不在预定义的混合阴离子列表中")
|
||
return False
|
||
except Exception as e:
|
||
print(f"处理阴离子时出错: {e}")
|
||
return False
|
||
|
||
#这里还要调试
|
||
# try:
|
||
# # 初始化总电荷
|
||
# total_charge = 0
|
||
#
|
||
# # 检查是否所有元素都有氧化态
|
||
# for site in structure:
|
||
# try:
|
||
# oxi_state = site.species.charge # 检查是否有氧化态
|
||
# total_charge += oxi_state # 累加氧化态
|
||
# except AttributeError:
|
||
# print(f"元素 {site.specie.symbol} 缺少氧化态定义")
|
||
# return False
|
||
# # 检查是否电荷平衡
|
||
# if total_charge == 0:
|
||
# print("所有元素的价态之和为 0,结构电荷平衡")
|
||
# else:
|
||
# print(f"所有元素的价态之和为 {total_charge},结构不平衡")
|
||
# return False
|
||
# except Exception as e:
|
||
# print(f"发生错误: {e}")
|
||
# return False
|
||
|
||
#判断原子个数
|
||
try:
|
||
if not self.mixed_anions:
|
||
if structure.num_sites>300:
|
||
return False
|
||
else:
|
||
if structure.num_sites>900:
|
||
return False
|
||
except Exception:
|
||
print("原子个数判断失败")
|
||
return False
|
||
|
||
#判断有几个阴离子
|
||
# anions = {'O', 'S', 'Se', 'Te', 'F', 'Cl', 'Br', 'I'}
|
||
# try:
|
||
# anion_elements = {site.species.chemical_system for site in structure if site.species.chemical_system in anions}
|
||
# if len(anion_elements) > 1:
|
||
# return False
|
||
# except Exception:
|
||
# print("阴离子个数判断失败")
|
||
# return False
|
||
|
||
#判断是否有放射性元素
|
||
radioactive_elements = {'U', 'Th', 'Pu', 'Ra', 'Rn', 'Po', 'Np', 'Am', 'Cm', 'Bk', 'Cf', 'Es', 'Fm', 'Md', 'No',
|
||
'Lr'}
|
||
|
||
try:
|
||
# 遍历结构中的元素
|
||
for site in structure:
|
||
if site.species.chemical_system in radioactive_elements:
|
||
return False # 存在放射性元素
|
||
except Exception:
|
||
print("放射性元素判断失败")
|
||
return False
|
||
|
||
|
||
#判断是否存在共占位
|
||
try:
|
||
for site in structure.sites:
|
||
if self.element_positive in [specie.symbol for specie in site.species.keys()] and len(site.species) > 1:
|
||
return False
|
||
except Exception:
|
||
print("共占位判断失败")
|
||
return False
|
||
|
||
#判读是否有无序或部分占位的阴离子
|
||
try:
|
||
for site in structure.sites:
|
||
for specie, occupancy in site.species.items():
|
||
if specie.symbol in anions and occupancy < 1:
|
||
return False
|
||
except Exception:
|
||
print("无序或部分占位的阴离子判断失败")
|
||
return False
|
||
|
||
#判断是否有水分子
|
||
try:
|
||
oxygen_sites = [site for site in structure.sites if site.species.chemical_system == "O"]
|
||
hydrogen_sites = [site for site in structure.sites if site.species.chemical_system == "H"]
|
||
|
||
for o_site in oxygen_sites:
|
||
nearby_hydrogens = [h_site for h_site in hydrogen_sites if o_site.distance(h_site) < 1.2]
|
||
|
||
if len(nearby_hydrogens) == 2:
|
||
return False
|
||
except Exception:
|
||
print("水分子判断失败")
|
||
return False
|
||
#接下来判断是否有标准信息
|
||
try:
|
||
for site in structure.sites:
|
||
for specie in site.species.keys():
|
||
element = Element(specie.symbol)
|
||
|
||
if not element.common_oxidation_states:
|
||
return False
|
||
|
||
try:
|
||
_ = Specie(element.symbol, max(element.common_oxidation_states)).ionic_radius
|
||
except:
|
||
return False
|
||
except Exception:
|
||
print("标准信息判断失败")
|
||
return False
|
||
#暂时不判断是否为电中性
|
||
#可能需要通过ovito等库来做判断
|
||
#存在一些文件不提供各元素的电负性
|
||
|
||
#判断电中性是否存在
|
||
|
||
self.check_basic_result = True
|
||
return True
|
||
|
||
def check_high_cn_and_face_sharing(self,cut_distance = 3.1):
|
||
structure = self.structure
|
||
#基于固角权重的计算
|
||
nn_finder = CrystalNN()
|
||
|
||
#遍历结构中的所有Na位点,检查配位数
|
||
#是所有Na位点都需要还是只检查高配位数的位点?
|
||
high_cn_ep_sites = []
|
||
try:
|
||
for i,site in enumerate(structure):
|
||
if site.specie == Element(self.element_positive):
|
||
cn = nn_finder.get_cn(structure,i)
|
||
if cn>=5:
|
||
high_cn_ep_sites.append(i)
|
||
if len(high_cn_ep_sites)==0:
|
||
return False
|
||
except Exception:
|
||
print("高配位Na离子判断失败")
|
||
return False
|
||
#检查共面
|
||
try:
|
||
for i in high_cn_ep_sites:
|
||
neighbors = nn_finder.get_nn_info(structure,i)
|
||
x_neighbors = []
|
||
x_neighbors = [
|
||
neighbor["site_index"]
|
||
for neighbor in neighbors
|
||
if structure[neighbor["site_index"]].specie.symbol == self.element_positive
|
||
and neighbor["site"].distance(structure[i]) <= cut_distance
|
||
]
|
||
|
||
if not self._check_face_sharing(i, x_neighbors):
|
||
print(f"Na site {i} does not share a face with other high-CN Na sites.")
|
||
return False
|
||
|
||
print("All high-CN Na sites are face-sharing.")
|
||
except Exception:
|
||
print("共面判断失败")
|
||
return True
|
||
|
||
|
||
def _check_face_sharing(self,site_index,neighbor_indices):
|
||
# 获取当前 Na 位点的坐标
|
||
site_coords = self.structure[site_index].coords
|
||
|
||
# 遍历邻居
|
||
for neighbor_index in neighbor_indices:
|
||
# 获取邻居的坐标
|
||
neighbor_coords = self.structure[neighbor_index].coords
|
||
|
||
# 获取两个原子之间共享的面(使用简单的距离或角度计算)
|
||
# 假设共享面的法向量计算可以从 Voronoi 构造
|
||
shared_face_normal = self._calculate_face_normal(site_coords, neighbor_coords)
|
||
|
||
# 判断是否共面(如果法向量的绝对值接近 0,可以认为共面)
|
||
if shared_face_normal is not None:
|
||
return True
|
||
|
||
return False
|
||
def _calculate_face_normal(self, coords1, coords2):
|
||
|
||
# 示例计算:用两个原子之间的向量生成法向量
|
||
vector = coords2 - coords1
|
||
norm = np.linalg.norm(vector)
|
||
|
||
# 如果向量接近零,返回 None
|
||
if norm < 1e-6:
|
||
return None
|
||
|
||
# 正则化向量作为法向量
|
||
return vector / norm
|
||
|
||
def check_percolation_radius(self):
|
||
return True
|
||
def group_structures_by_framework(structures):
|
||
matcher = StructureMatcher()
|
||
grouped_structures = []
|
||
|
||
for structure in structures:
|
||
matched = False
|
||
for group in grouped_structures:
|
||
|
||
if matcher.fit(structure, group[0]): # 比较结构是否匹配
|
||
group.append(structure)
|
||
matched = True
|
||
break
|
||
if not matched:
|
||
grouped_structures.append([structure])
|
||
|
||
return grouped_structures |