From efcdacffd060b6acf8c34da951771d8e32e5a76e Mon Sep 17 00:00:00 2001 From: koko <1429659362@qq.com> Date: Tue, 23 Sep 2025 19:39:54 +0800 Subject: [PATCH] Corner-sharing --- corner-sharing/0923_CS.py | 92 +++++++++++ corner-sharing/utils/CS_analyse.py | 244 +++++++++++++++++++++++++++-- 2 files changed, 325 insertions(+), 11 deletions(-) create mode 100644 corner-sharing/0923_CS.py diff --git a/corner-sharing/0923_CS.py b/corner-sharing/0923_CS.py new file mode 100644 index 0000000..7ec61d3 --- /dev/null +++ b/corner-sharing/0923_CS.py @@ -0,0 +1,92 @@ +import os +import csv +from pymatgen.core import Structure +from tqdm import tqdm # 引入tqdm来显示进度条,如果未安装请运行 pip install tqdm + +# --- 导入您自定义的分析函数 --- +# 假设您的函数存放在 'utils/CS_analyse.py' 文件中 +# 并且您已经将它们重命名 +# from calculate_polyhedra_sharing import calculate_polyhedra_sharing as CS_catulate +# from check_only_corner_sharing import check_only_corner_sharing +# 注意:根据您的描述,您会将函数放在 utils 文件夹中,因此导入方式如下: +from utils.CS_analyse import CS_catulate, check_only_corner_sharing + + +def process_cif_folder(cif_folder_path: str, output_csv_path: str): + """ + 遍历指定文件夹中的所有CIF文件,计算其角共享特性,并将结果输出到CSV文件。 + + 参数: + cif_folder_path (str): 存放CIF文件的文件夹路径。 + output_csv_path (str): 输出的CSV文件的路径。 + """ + # 检查输入文件夹是否存在 + if not os.path.isdir(cif_folder_path): + print(f"错误: 文件夹 '{cif_folder_path}' 不存在。") + return + + # 准备存储结果的列表 + results = [] + + # 获取所有CIF文件的列表 + try: + cif_files = [f for f in os.listdir(cif_folder_path) if f.endswith('.cif')] + if not cif_files: + print(f"警告: 在文件夹 '{cif_folder_path}' 中没有找到任何 .cif 文件。") + return + except FileNotFoundError: + print(f"错误: 无法访问文件夹 '{cif_folder_path}'。") + return + + print(f"开始处理 {len(cif_files)} 个CIF文件...") + + # 使用tqdm创建进度条,遍历所有CIF文件 + for filename in tqdm(cif_files, desc="Processing CIFs"): + file_path = os.path.join(cif_folder_path, filename) + + try: + # 1. 从CIF文件加载结构 + struct = Structure.from_file(file_path) + + # 2. 调用您的 CS_catulate 函数计算详细的共享关系 + # 这里使用默认参数 sp='Li', anion=['O'] + sharing_details = CS_catulate(struct, sp='Li', anion=['O','S','Cl','F','Br']) + + # 3. 调用 check_only_corner_sharing 函数进行最终判断 + is_only_corner = check_only_corner_sharing(sharing_details) + + # 4. 将文件名和结果存入列表 + results.append([filename, is_only_corner]) + + except Exception as e: + # 如果处理某个文件时出错,打印错误信息并继续处理下一个文件 + print(f"\n处理文件 '{filename}' 时发生错误: {e}") + results.append([filename, 'Error']) # 在CSV中标记错误 + + # 5. 将结果写入CSV文件 + try: + with open(output_csv_path, 'w', newline='', encoding='utf-8') as csvfile: + writer = csv.writer(csvfile) + # 写入表头 + writer.writerow(['CIF_File', 'Is_Only_Corner_Sharing']) + # 写入所有结果 + writer.writerows(results) + + print(f"\n处理完成!结果已保存到 '{output_csv_path}'") + + except IOError as e: + print(f"\n错误: 无法写入CSV文件 '{output_csv_path}': {e}") + + +# --- 主程序入口 --- +if __name__ == "__main__": + # ----- 参数配置 ----- + # 请将此路径修改为您存放CIF文件的文件夹的实际路径 + CIF_DIRECTORY = "data/0921" + + # 输出的CSV文件名 + OUTPUT_CSV = "corner_sharing_results.csv" + # ------------------- + + # 调用主函数开始处理 + process_cif_folder(CIF_DIRECTORY, OUTPUT_CSV) diff --git a/corner-sharing/utils/CS_analyse.py b/corner-sharing/utils/CS_analyse.py index 0177b24..587edd1 100644 --- a/corner-sharing/utils/CS_analyse.py +++ b/corner-sharing/utils/CS_analyse.py @@ -1,3 +1,5 @@ +from typing import List, Dict + from pymatgen.core.structure import Structure from pymatgen.analysis.local_env import VoronoiNN import numpy as np @@ -24,7 +26,132 @@ def special_check_for_3(site, nearest): return real_nearest -def CS_catulate(struct, sp='Li', anion=['O'], tol=0, cutoff=3.0,notice=False,ID=None): +def CS_catulate( + struct, + sp: str = 'Li', + anion: List[str] = ['O'], + tol: float = 0, + cutoff: float = 3.0, + notice: bool = False +) -> Dict[str, Dict[str, int]]: + """ + 计算结构中不同类型阳离子多面体之间的共享关系(角、边、面共享)。 + + 该函数会分别计算以下三种情况的共享数量: + 1. 目标原子 vs 目标原子 (e.g., Li-Li) + 2. 目标原子 vs 其他阳离子 (e.g., Li-X) + 3. 其他阳离子 vs 其他阳离子 (e.g., X-Y) + + 参数: + struct (Structure): 输入的pymatgen结构对象。 + sp (str): 目标元素符号,默认为 'Li'。 + anion (list): 阴离子元素符号列表,默认为 ['O']。 + tol (float): VoronoiNN 的容差。对于Li,通常设为0。 + cutoff (float): VoronoiNN 的截断距离。对于Li,通常设为3.0。 + notice (bool): 是否打印详细的共享信息。 + + 返回: + dict: 一个字典,包含三类共享关系的统计结果。 + 键 "sp_vs_sp", "sp_vs_other", "other_vs_other" 分别对应上述三种情况。 + 每个键的值是另一个字典,统计了共享2个(边)、3个(面)等情况的数量。 + 例如: {'sp_vs_sp': {'1': 10, '2': 4}, 'sp_vs_other': ...} + 共享1个阴离子为角共享,2个为边共享,3个为面共享。 + """ + # 初始化 VoronoiNN 对象 + voro_nn = VoronoiNN(tol=tol, cutoff=cutoff) + + # 1. 分类存储所有阳离子的近邻阴离子信息 + target_sites_info = [] + other_cation_sites_info = [] + + for index, site in enumerate(struct.sites): + # 跳过阴离子本身 + if site.species.chemical_system in anion: + continue + + # 获取当前位点的近邻阴离子 + try: + # 使用 get_nn_info 更直接 + nn_info = voro_nn.get_nn_info(struct, index) + nearest_anions = [ + nn["site"] for nn in nn_info + if nn["site"].species.chemical_system in anion + ] + except Exception as e: + print(f"Warning: Could not get neighbors for site {index} ({site.species_string}): {e}") + continue + + if not nearest_anions: + continue + + # 整理信息 + site_info = { + 'index': index, + 'element': site.species.chemical_system, + 'nearest_anion_indices': {nn.index for nn in nearest_anions} + } + + # 根据是否为目标原子进行分类 + if site.species.chemical_system == sp: + target_sites_info.append(site_info) + else: + other_cation_sites_info.append(site_info) + + # 2. 初始化结果字典 + # 共享数量key: 1-角, 2-边, 3-面 + results = { + "sp_vs_sp": {"1": 0, "2": 0, "3": 0, "4": 0}, + "sp_vs_other": {"1": 0, "2": 0, "3": 0, "4": 0}, + "other_vs_other": {"1": 0, "2": 0, "3": 0, "4": 0}, + } + + # 3. 计算不同类别之间的共享关系 + + # 3.1 目标原子 vs 目标原子 (sp_vs_sp) + for i in range(len(target_sites_info)): + for j in range(i + 1, len(target_sites_info)): + atom_i = target_sites_info[i] + atom_j = target_sites_info[j] + + shared_anions = atom_i['nearest_anion_indices'].intersection(atom_j['nearest_anion_indices']) + shared_count = len(shared_anions) + + if shared_count > 0 and str(shared_count) in results["sp_vs_sp"]: + results["sp_vs_sp"][str(shared_count)] += 1 + if notice: + print( + f"[Li-Li] Atom {atom_i['index']} and {atom_j['index']} share {shared_count} anions: {shared_anions}") + + # 3.2 目标原子 vs 其他阳离子 (sp_vs_other) + for atom_sp in target_sites_info: + for atom_other in other_cation_sites_info: + shared_anions = atom_sp['nearest_anion_indices'].intersection(atom_other['nearest_anion_indices']) + shared_count = len(shared_anions) + + if shared_count > 0 and str(shared_count) in results["sp_vs_other"]: + results["sp_vs_other"][str(shared_count)] += 1 + if notice: + print( + f"[Li-Other] Atom {atom_sp['index']} and {atom_other['index']} share {shared_count} anions: {shared_anions}") + + # 3.3 其他阳离子 vs 其他阳离子 (other_vs_other) + for i in range(len(other_cation_sites_info)): + for j in range(i + 1, len(other_cation_sites_info)): + atom_i = other_cation_sites_info[i] + atom_j = other_cation_sites_info[j] + + shared_anions = atom_i['nearest_anion_indices'].intersection(atom_j['nearest_anion_indices']) + shared_count = len(shared_anions) + + if shared_count > 0 and str(shared_count) in results["other_vs_other"]: + results["other_vs_other"][str(shared_count)] += 1 + if notice: + print( + f"[Other-Other] Atom {atom_i['index']} and {atom_j['index']} share {shared_count} anions: {shared_anions}") + + return results + +def CS_catulate_old(struct, sp='Li', anion=['O'], tol=0, cutoff=3.0,notice=False,ID=None): """ 计算结构中目标元素与最近阴离子的共享关系。 @@ -51,10 +178,10 @@ def CS_catulate(struct, sp='Li', anion=['O'], tol=0, cutoff=3.0,notice=False,ID= # 遍历结构中的每个位点 for index,site in enumerate(struct.sites): # 跳过阴离子位点 - if site.specie.symbol in anion: + if site.species.chemical_system in anion: continue # 跳过Li原子 - if site.specie.symbol == sp: + if site.species.chemical_system == sp: continue # 获取 Voronoi 多面体信息 voro_info = voro_nn.get_voronoi_polyhedra(struct, index) @@ -62,14 +189,14 @@ def CS_catulate(struct, sp='Li', anion=['O'], tol=0, cutoff=3.0,notice=False,ID= # 找到最近的阴离子位点 nearest_anions = [ nn_info["site"] for nn_info in voro_info.values() - if nn_info["site"].specie.symbol in anion + if nn_info["site"].species.chemical_system in anion ] # 如果没有找到最近的阴离子,跳过 if not nearest_anions: print(f"No nearest anions found for {ID} site {index}.") continue - if site.specie.symbol == 'B' or site.specie.symbol == 'N': + if site.species.chemical_system == 'B' or site.species.chemical_system == 'N': nearest_anions = special_check_for_3(site,nearest_anions) nearest_anions = check_real(nearest_anions) # 将结果添加到 atom_dice 列表中 @@ -110,10 +237,62 @@ def CS_catulate(struct, sp='Li', anion=['O'], tol=0, cutoff=3.0,notice=False,ID= return shared_count -def CS_count(struct, shared_count, sp='Li'): +def CS_count(struct, sharing_results: Dict[str, Dict[str, int]], sp: str = 'Li') -> float: + """ + 分析多面体共享结果,计算平均每个目标原子参与的共享阴离子数。 + + 这个函数是 calculate_polyhedra_sharing 的配套函数。 + + 参数: + struct (Structure): 输入的pymatgen结构对象,用于统计目标原子总数。 + sharing_results (dict): 来自 calculate_polyhedra_sharing 函数的输出结果。 + sp (str): 目标元素符号,默认为 'Li'。 + + 返回: + float: 平均每个目标原子sp参与的共享阴离子数量。 + 例如,结果为2.5意味着平均每个Li原子通过共享与其他阳离子 + (包括Li和其他阳离子)连接了2.5个阴离子。 + """ + # 1. 统计结构中目标原子的总数 + target_atom_count = 0 + for site in struct.sites: + if site.species.chemical_system == sp: + target_atom_count += 1 + + # 如果结构中没有目标原子,直接返回0,避免除以零错误 + if target_atom_count == 0: + return 0.0 + + # 2. 计算加权的共享阴离子总数 + total_shared_anions = 0 + + # 处理 sp_vs_sp (例如 Li-Li) 的共享 + # 每个共享关系涉及两个目标原子,所以权重需要乘以 2 + if "sp_vs_sp" in sharing_results: + sp_vs_sp_counts = sharing_results["sp_vs_sp"] + for num_shared_str, count in sp_vs_sp_counts.items(): + num_shared = int(num_shared_str) + # 权重 = 共享阴离子数 * 涉及的目标原子数 (2) * 出现次数 + total_shared_anions += num_shared * 2 * count + + # 处理 sp_vs_other (例如 Li-X) 的共享 + # 每个共享关系涉及一个目标原子,所以权重乘以 1 + if "sp_vs_other" in sharing_results: + sp_vs_other_counts = sharing_results["sp_vs_other"] + for num_shared_str, count in sp_vs_other_counts.items(): + num_shared = int(num_shared_str) + # 权重 = 共享阴离子数 * 涉及的目标原子数 (1) * 出现次数 + total_shared_anions += num_shared * 1 * count + + # 3. 计算平均值 + # 平均每个目标原子参与的共享阴离子数 = 总的加权共享数 / 目标原子总数 + average_sharing_per_atom = total_shared_anions / target_atom_count + + return average_sharing_per_atom +def CS_count_old(struct, shared_count, sp='Li'): count = 0 for site in struct.sites: - if site.specie.symbol == sp: + if site.species.chemical_system == sp: count += 1 # 累加符合条件的原子数量 CS_count = 0 @@ -128,7 +307,50 @@ def CS_count(struct, shared_count, sp='Li'): return CS_count -structure = Structure.from_file("../data/0921/wjy_001.cif") -a = CS_catulate(structure,notice=True) -b = CS_count(structure,a) -print(f"{a}\n{b}") \ No newline at end of file + +def check_only_corner_sharing(sharing_results: Dict[str, Dict[str, int]]) -> int: + """ + 检查目标原子(sp)是否只参与了角共享(共享1个阴离子)。 + + 该函数是 calculate_polyhedra_sharing 的配套函数。 + + 参数: + sharing_results (dict): 来自 calculate_polyhedra_sharing 函数的输出结果。 + + 返回: + int: + - 1: 如果 sp 的共享关系中,边共享(2)、面共享(3)等数量均为0, + 并且至少存在一个角共享(1)。 + - 0: 如果 sp 存在任何边、面等共享,或者没有任何共享关系。 + """ + # 提取与目标原子 sp 相关的共享数据 + sp_vs_sp_counts = sharing_results.get("sp_vs_sp", {}) + sp_vs_other_counts = sharing_results.get("sp_vs_other", {}) + + # 1. 检查是否存在任何边共享、面共享等 (共享数 > 1) + # 检查 sp-sp 的共享 + for num_shared_str, count in sp_vs_sp_counts.items(): + if int(num_shared_str) > 1 and count > 0: + return 0 # 发现了边/面共享,立即返回 0 + + # 检查 sp-other 的共享 + for num_shared_str, count in sp_vs_other_counts.items(): + if int(num_shared_str) > 1 and count > 0: + return 0 # 发现了边/面共享,立即返回 0 + + # 2. 检查是否存在至少一个角共享 (共享数 == 1) + # 运行到这里,说明已经没有任何边/面共享了。 + # 现在需要确认是否真的存在角共享,而不是完全没有共享。 + corner_share_sp_sp = sp_vs_sp_counts.get("1", 0) > 0 + corner_share_sp_other = sp_vs_other_counts.get("1", 0) > 0 + + if corner_share_sp_sp or corner_share_sp_other: + return 1 # 确认只存在角共享 + else: + return 0 # 没有任何共享关系,也返回 0 + +# structure = Structure.from_file("../data/0921/wjy_001.cif") +# a = CS_catulate(structure,notice=True) +# b = CS_count(structure,a) +# print(f"{a}\n{b}") +# print(check_only_corner_sharing(a)) \ No newline at end of file