This commit is contained in:
2025-12-07 13:56:33 +08:00
parent 49f54b04cd
commit c83985cd02
22 changed files with 2732 additions and 0 deletions

363
py/crystal_2.py Normal file
View File

@@ -0,0 +1,363 @@
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