diff --git a/main.sh b/main.sh index 5ab2fe8..33a65f0 100644 --- a/main.sh +++ b/main.sh @@ -1,30 +1,64 @@ #!/bin/bash -#修改权限 + +# ===================== +# 项目自动化筛选脚本 +# ===================== + +# 1. 初始化设置 +# 修改上一级目录权限 chmod -R u+w ../Screen -#启用screen环境 + +# 启用 screen 环境 (Python 3.11) +source $(conda info --base)/etc/profile.d/conda.sh conda activate screen + # 设置当前目录为 PYTHONPATH cd py/ export PYTHONPATH=$(pwd):$PYTHONPATH -#调用预筛选 + +echo "============ Stage 1: Pre-process & Basic Screening ============" +# 调用预筛选 (处理 input_pre 到 input) python pre_process.py -#调用第一步筛选 + +# 调用第一步筛选 +# 功能:读取 CIF,进行基础检查,按阴离子分类,创建独立文件夹 (Anion/ID/ID.cif) python step1.py -#为第一步筛出的所有材料制作脚本 +# 为所有材料生成 Zeo++ 分析脚本 +# 功能:生成 analyze.sh,输出重定向至 log.txt python make_sh.py -#调整环境 + +# 2. 切换环境运行 Zeo++ +echo "============ Stage 2: Zeo++ Calculations ============" conda deactivate conda activate zeo -#运行脚本 + +# 进入数据目录执行所有生成的 shell 脚本 cd ../data/after_step1 -source sh_all.sh -rm sh_all.sh -#调整conda回到screen +if [ -f "sh_all.sh" ]; then + source sh_all.sh + rm sh_all.sh +else + echo "Error: sh_all.sh not found!" +fi + +# 3. 后处理与筛选 (Step 2-4) +echo "============ Stage 3: Data Extraction & Advanced Screening ============" +# 切回 screen 环境 conda deactivate conda activate screen - -#启用不同的python文件做分析 cd ../../py -python step2-5-file_process.py -#python step6.py + +# 提取日志数据 +# 功能:遍历所有 log.txt,提取孔径、距离等参数,生成汇总 CSV 到 ../output +python extract_data.py + +# 联合筛选 (Step 2, 3, 4) +# 功能:读取 CSV,根据阈值筛选,将符合条件的材料软链接到 ../data/after_screening +python step2_4_combined.py + +# Step 5 (扩胞与实际检查) +# 注意:这一步目前尚未更新适配新的软链接结构,待后续处理 +# python step5.py + +echo "Done! Check results in ../data/after_screening" \ No newline at end of file diff --git a/py/extract_data.py b/py/extract_data.py new file mode 100644 index 0000000..ca2d628 --- /dev/null +++ b/py/extract_data.py @@ -0,0 +1,154 @@ +import os +import re +import pandas as pd + + +def extract_parameters_from_log(log_path): + """ + 从 log.txt 中提取三个关键参数。 + 如果未找到,返回 None。 + """ + if not os.path.exists(log_path): + return None, None, None + + with open(log_path, 'r', encoding='utf-8') as f: + content = f.read() + + # 正则表达式定义 + # 1. Percolation diameter (原来的 Step 2) + # 匹配模式: # Percolation diameter (A): 1.06 + re_percolation = r"Percolation diameter \(A\):\s*([\d\.]+)" + + # 2. Minimum of d (原来的 Step 3) + # 匹配模式: the minium of d \n 3.862140561244235 + re_min_d = r"the minium of d\s*\n\s*([\d\.]+)" + + # 3. Maximum node length (原来的 Step 4) + # 匹配模式: # Maximum node length detected: 1.332 A + re_max_node = r"Maximum node length detected:\s*([\d\.]+)\s*A" + + # 提取数据 + match_perc = re.search(re_percolation, content) + match_d = re.search(re_min_d, content) + match_node = re.search(re_max_node, content) + + # 获取值,如果没匹配到则为空字符串或None + val_perc = match_perc.group(1) if match_perc else None + val_d = match_d.group(1) if match_d else None + val_node = match_node.group(1) if match_node else None + + return val_perc, val_d, val_node + + +def process_folder_recursively(base_input_folder, base_output_folder): + """ + 递归遍历文件夹,提取数据并生成 CSV。 + 逻辑: + 1. 遍历 base_input_folder 下的第一层子文件夹(通常是阴离子类别,如 O, S, O+S 等)。 + 2. 如果是单阴离子(如 O),直接处理其下的材料文件夹。 + 3. 如果是混合阴离子(如 O+S),需要进入下一层(如 O+S/O 和 O+S/S),分别处理。 + 4. 结果保存在 base_output_folder 下保持相同的目录结构。 + """ + + # 获取 after_step1 下的所有顶层目录 (例如 O, S, Cl, S+O ...) + if not os.path.exists(base_input_folder): + print(f"输入目录 {base_input_folder} 不存在") + return + + top_dirs = [d for d in os.listdir(base_input_folder) if os.path.isdir(os.path.join(base_input_folder, d))] + + for top_dir in top_dirs: + top_path = os.path.join(base_input_folder, top_dir) + + # 判断是否是混合阴离子目录(名字包含 +) + if "+" in top_dir: + # 混合阴离子情况:例如 S+O + # 需要遍历其子目录:S+O/S 和 S+O/O + sub_anions = [d for d in os.listdir(top_path) if os.path.isdir(os.path.join(top_path, d))] + for sub_anion in sub_anions: + # 构建路径:../data/after_step1/S+O/S + current_process_path = os.path.join(top_path, sub_anion) + # 构建输出 CSV 路径:../output/S+O/S/S.csv (或者 S+O_S.csv,这里按你要求的 O+S/O/O.csv 格式) + # 输出目录: ../output/S+O/S + output_dir = os.path.join(base_output_folder, top_dir, sub_anion) + csv_filename = f"{sub_anion}.csv" + + extract_and_save(current_process_path, output_dir, csv_filename) + else: + # 单一阴离子情况:例如 O + # 路径:../data/after_step1/O + current_process_path = top_path + # 输出目录: ../output/O + output_dir = os.path.join(base_output_folder, top_dir) + csv_filename = f"{top_dir}.csv" + + extract_and_save(current_process_path, output_dir, csv_filename) + + +def extract_and_save(input_dir, output_dir, csv_name): + """ + 实际执行提取和保存的函数。 + input_dir: 包含各个材料文件夹的目录 (例如 .../O/) + output_dir: CSV 保存目录 + csv_name: CSV 文件名 + """ + data_list = [] + + # input_dir 下面应该是各个材料的文件夹,例如 141, 142 ... + if not os.path.exists(input_dir): + return + + # 遍历下面的所有材料文件夹 + material_folders = [f for f in os.listdir(input_dir) if os.path.isdir(os.path.join(input_dir, f))] + + print(f"正在处理目录: {input_dir}, 发现 {len(material_folders)} 个材料文件夹") + + for material_id in material_folders: + material_path = os.path.join(input_dir, material_id) + # 根据新的 step1 逻辑,log 文件名为 log.txt + log_path = os.path.join(material_path, "log.txt") + + # 提取数据 + perc, min_d, max_node = extract_parameters_from_log(log_path) + + # 只要有一个数据存在,就记录(或者你可以改为必须全部存在) + # 这里设置为只要有记录就加入,方便排查错误 + if perc or min_d or max_node: + data_list.append({ + "Filename": material_id, + "Percolation Diameter (A)": perc, + "Minimum of d": min_d, + "Maximum Node Length (A)": max_node + }) + else: + # 如果 log.txt 不存在或者提取不到数据,可以选择记录空值 + data_list.append({ + "Filename": material_id, + "Percolation Diameter (A)": None, + "Minimum of d": None, + "Maximum Node Length (A)": None + }) + + # 如果有数据,保存为 CSV + if data_list: + if not os.path.exists(output_dir): + os.makedirs(output_dir) + + csv_path = os.path.join(output_dir, csv_name) + df = pd.DataFrame(data_list) + # 调整列顺序 + df = df[["Filename", "Percolation Diameter (A)", "Minimum of d", "Maximum Node Length (A)"]] + + df.to_csv(csv_path, index=False) + print(f"数据已保存至: {csv_path}") + else: + print(f"目录 {input_dir} 未提取到有效数据") + + +if __name__ == "__main__": + # 输入基础路径 (假设数据在 step1 处理后) + input_base = "../data/after_step1" + # 输出基础路径 (你提到的 output 文件夹) + output_base = "../output" + + process_folder_recursively(input_base, output_base) \ No newline at end of file diff --git a/py/make_sh.py b/py/make_sh.py index 6226c35..dd1861c 100644 --- a/py/make_sh.py +++ b/py/make_sh.py @@ -1,63 +1,13 @@ import os -def creat_sh(input_folder, anion, sh_file_path='analyze.sh'): - """ - 创建shell脚本,只处理两类CIF文件: - 1. 纯数字命名的CIF文件 (例如: 123.cif) - 2. 数字-坐标格式的CIF文件 (例如: 123-x1y2z3.cif) - - 参数: - input_folder: 输入文件夹路径 - anion: 阴离子类型 - sh_file_path: 生成的shell脚本路径 - """ - # 文件夹路径 - folder_path = input_folder - - import re - - # 定义两种文件名模式的正则表达式 - pattern1 = re.compile(r'^\d+\.cif$') # 纯数字.cif - pattern2 = re.compile(r'^\d+-x\d+y\d+z\d+\.cif$') # 数字-x数字y数字z数字.cif - - # 打开SH脚本文件用于写入 - with open(sh_file_path, 'w') as sh_file: - # 写入脚本头部 - sh_file.write('#!/bin/bash\n') - - # 遍历文件夹中的所有文件 - for filename in os.listdir(folder_path): - file_path = os.path.join(folder_path, filename) - - # 只处理文件(不处理文件夹) - if os.path.isfile(file_path): - # 检查文件名是否匹配两种模式之一 - if pattern1.match(filename) or pattern2.match(filename): - # 生成对应的命令 - command = f"python ../../../tool/analyze_voronoi_nodes.py {filename} -i ../../../tool/{anion}.yaml > {filename}.txt\n" - # 将命令写入SH脚本文件 - sh_file.write(command) - - print(f"SH脚本已生成:{sh_file_path}") - - -import os - - def create_sh_recursive(base_folder, tool_path="tool", relative_depth=2): """ - 递归遍历文件夹,为每个包含.cif文件的文件夹生成analyze.sh脚本, + 递归遍历文件夹,为每个包含.cif文件的文件夹生成analyze.sh脚本, 并在基础文件夹下创建一个sh_all.sh来执行所有脚本。 - - 参数: - base_folder: 起始文件夹路径 - tool_path: 工具目录的基本路径 - relative_depth: 基础相对深度,用于计算正确的相对路径 """ # 用于收集所有生成的analyze.sh脚本的相对路径 analyze_sh_paths = [] - base_folder_name = os.path.basename(base_folder) def process_folder(folder_path, current_depth=0): print(f"处理文件夹: {folder_path}") @@ -66,17 +16,31 @@ def create_sh_recursive(base_folder, tool_path="tool", relative_depth=2): folder_name = os.path.basename(folder_path) # 检查当前文件夹是否包含.cif文件 - has_cif_files = any( - f.endswith('.cif') for f in os.listdir(folder_path) if os.path.isfile(os.path.join(folder_path, f))) + # 注意:这里我们只关心当前层级下的cif文件 + cif_files = [f for f in os.listdir(folder_path) if + f.endswith('.cif') and os.path.isfile(os.path.join(folder_path, f))] + has_cif_files = len(cif_files) > 0 - # 如果当前文件夹包含.cif文件,生成脚本 + # 如果当前文件夹包含.cif文件,生成脚本 if has_cif_files: - # 计算正确的工具路径(根据深度增加../) + # 计算正确的工具路径(根据深度增加../) dots = "../" * (relative_depth + current_depth) tool_relative_path = f"{dots}{tool_path}" - # 确定anion参数(使用文件夹名) - anion = folder_name + # --- 修改开始: 修正Anion识别逻辑 --- + # 如果结构是 Anion/MaterialID/file.cif,此时folder_name是MaterialID + # 我们需要上一级目录的名字作为Anion (例如 'O', 'S', 'Cl') 来寻找对应的 .yaml 文件 + # 简单的判断逻辑:如果当前文件夹名字主要由数字组成(或者是ID格式),且包含cif文件,我们假设其父目录是Anion类型 + # 或者更直接的逻辑:在你的新结构中,包含cif的文件夹必定是底层文件夹,其父目录必定是Anion + parent_dir_name = os.path.basename(os.path.dirname(folder_path)) + + # 这里做一个简单的保护,如果是在第一层(比如直接在O文件夹下有cif),保持原状,否则使用父目录名 + # 在新结构下,cif总是在 '.../O/141/141.cif',所以anion应该是 parent_dir_name ('O') + if parent_dir_name in ['O', 'S', 'Cl', 'Br'] or folder_name not in ['O', 'S', 'Cl', 'Br']: + anion = parent_dir_name + else: + anion = folder_name + # --- 修改结束 --- # 生成脚本文件路径 sh_file_path = os.path.join(folder_path, "analyze.sh") @@ -84,18 +48,17 @@ def create_sh_recursive(base_folder, tool_path="tool", relative_depth=2): # 创建脚本 with open(sh_file_path, 'w') as sh_file: sh_file.write('#!/bin/bash\n') - for filename in os.listdir(folder_path): - file_path = os.path.join(folder_path, filename) - if os.path.isfile(file_path) and filename.endswith('.cif'): - command = f"python {tool_relative_path}/analyze_voronoi_nodes.py {filename} -i {tool_relative_path}/{anion}.yaml > {filename}.txt\n" - sh_file.write(command) + for filename in cif_files: + # --- 修改开始: 输出重定向到 log.txt --- + command = f"python {tool_relative_path}/analyze_voronoi_nodes.py {filename} -i {tool_relative_path}/{anion}.yaml > log.txt\n" + # --- 修改结束 --- + sh_file.write(command) # 将此脚本添加到收集器中 - # 计算相对于基础文件夹的路径 rel_path = os.path.relpath(folder_path, base_folder) analyze_sh_paths.append(rel_path) - print(f"生成脚本: {sh_file_path} (工具路径: {tool_relative_path})") + print(f"生成脚本: {sh_file_path} (工具路径: {tool_relative_path}, Anion: {anion})") # 获取子文件夹列表 subdirs = [d for d in os.listdir(folder_path) if os.path.isdir(os.path.join(folder_path, d))] @@ -105,7 +68,7 @@ def create_sh_recursive(base_folder, tool_path="tool", relative_depth=2): elements = folder_name.split("+") for element in elements: element_dir = os.path.join(folder_path, element) - # 如果对应元素的子文件夹不存在,创建它 + # 如果对应元素的子文件夹不存在,创建它 if not os.path.exists(element_dir): os.makedirs(element_dir) print(f"创建子文件夹: {element_dir}") @@ -144,13 +107,8 @@ def create_sh_recursive(base_folder, tool_path="tool", relative_depth=2): # 修改权限使脚本可执行 os.chmod(sh_all_path, 0o755) print(f"生成总执行脚本: {sh_all_path}") - print("所有脚本生成完成!") -# 示例调用 -# create_sh_recursive("../data/after_step1") + print("所有脚本生成完成!") + if __name__ == '__main__': - # creat_sh("../data/after_step1/O","O","../data/after_step1/O/analyze.sh") - # creat_sh("../data/after_step1/S","S","../data/after_step1/S/analyze.sh") - # creat_sh("../data/after_step1/Cl","Cl","../data/after_step1/Cl/analyze.sh") - # creat_sh("../data/after_step1/Br","Br","../data/after_step1/Br/analyze.sh") create_sh_recursive("../data/after_step1") \ No newline at end of file diff --git a/py/step1.py b/py/step1.py index 32ca068..f78ad87 100644 --- a/py/step1.py +++ b/py/step1.py @@ -1,12 +1,11 @@ from pymatgen.core import Structure from pymatgen.core.periodic_table import Element, Specie from pymatgen.io.cif import CifWriter - from crystal_2 import crystal import crystal_2 import os import shutil -from pymatgen.io.cif import CifWriter + def read_files_check_basic(folder_path): file_contents = [] @@ -24,25 +23,38 @@ def read_files_check_basic(folder_path): file_contents.append(temp) except Exception as e: print(e) + continue # 如果出错跳过当前循环,避免temp未定义报错 + print(f"正在处理{filename}") temp.check_basic() + if temp.check_basic_result: + # 获取不带后缀的文件名,用于创建同名文件夹 + file_base_name = os.path.splitext(filename)[0] + if not "+" in temp.anion: - target_folder = os.path.join("../data/after_step1",f"{temp.anion}") + # 单一阴离子情况 + # 路径变为: ../data/after_step1/Anion/FileBaseName/ + base_anion_folder = os.path.join("../data/after_step1", f"{temp.anion}") + target_folder = os.path.join(base_anion_folder, file_base_name) + if not os.path.exists(target_folder): os.makedirs(target_folder) # 目标文件路径 target_file_path = os.path.join(target_folder, filename) - # 复制文件到目标文件夹 shutil.copy(file_path, target_file_path) - print(f"文件 {filename}通过基本筛选,已复制到 {target_folder}") + print(f"文件 {filename}通过基本筛选,已复制到 {target_folder}") else: + # 混合阴离子情况 anions = temp.anion.split("+") for anion in anions: - target_folder = os.path.join("../data/after_step1", f"{temp.anion}") - target_folder = os.path.join(target_folder, anion) + # 路径变为: ../data/after_step1/AnionCombination/Anion/FileBaseName/ + base_group_folder = os.path.join("../data/after_step1", f"{temp.anion}") + base_anion_folder = os.path.join(base_group_folder, anion) + target_folder = os.path.join(base_anion_folder, file_base_name) + if not os.path.exists(target_folder): os.makedirs(target_folder) @@ -50,5 +62,8 @@ def read_files_check_basic(folder_path): target_file_path = os.path.join(target_folder, filename) # 复制文件到目标文件夹 shutil.copy(file_path, target_file_path) - print(f"文件 {filename}通过基本筛选,已复制到 {target_folder}") -read_files_check_basic("../data/input") \ No newline at end of file + print(f"文件 {filename}通过基本筛选,已复制到 {target_folder}") + + +if __name__ == "__main__": + read_files_check_basic("../data/input") \ No newline at end of file diff --git a/py/step2_4_combined.py b/py/step2_4_combined.py new file mode 100644 index 0000000..b8a5ebd --- /dev/null +++ b/py/step2_4_combined.py @@ -0,0 +1,147 @@ +import os +import pandas as pd +import math + +# ================= 配置区域 ================= +# 定义各阴离子的筛选阈值 +# perc: Percolation diameter (对应 Step 2, 大于此值) +# min_d: Minimum of d (对应 Step 3, 小于此值) +# node: Maximum node length (对应 Step 4, 大于此值) +THRESHOLDS = { + "O": {"perc": 0.50, "min_d": 3.0, "node": 2.2}, + "S": {"perc": 0.55, "min_d": 3.0, "node": 2.2}, + "Cl": {"perc": 0.45, "min_d": 3.0, "node": 2.0}, + "Br": {"perc": 0.45, "min_d": 3.0, "node": 2.0} +} + +# 路径配置 +CSV_ROOT_DIR = "../output" # CSV 所在的根目录 +DATA_SOURCE_DIR = "../data/after_step1" # 原始 CIF 文件所在的根目录 (用于创建链接源) +TARGET_DIR = "../data/after_screening" # 筛选后放置软链接的目标目录 + + +# =========================================== + +def check_requirements(row, anion_type): + """ + 检查单行数据是否符合要求 + """ + # 获取该阴离子类型的阈值配置 + config = THRESHOLDS.get(anion_type) + if not config: + print(f"Warning: 未知的阴离子类型 {anion_type},跳过筛选。") + return False + + try: + # 获取数值 (处理可能的空值或非数字情况) + perc = float(row["Percolation Diameter (A)"]) + min_d = float(row["Minimum of d"]) + node = float(row["Maximum Node Length (A)"]) + + # 检查是否为 NaN + if math.isnan(perc) or math.isnan(min_d) or math.isnan(node): + return False + + # --- 筛选逻辑 --- + # Step 2: 连通孔径 > 阈值 + c1 = perc > config["perc"] + # Step 3: 最短距离 < 3.0 (所有元素目前都是3.0) + c2 = min_d < config["min_d"] + # Step 4: 扩大锂离子节点 > 阈值 + c3 = node > config["node"] + + return c1 and c2 and c3 + + except (ValueError, TypeError): + return False + + +def create_symlink(group_name, anion_name, material_id): + """ + 创建软链接 + 源: ../data/after_step1/Group/Anion/ID/ID.cif + 目: ../data/after_screening/Group/Anion/ID.cif + """ + # 1. 构建源文件路径 (必须使用绝对路径以确保软链接在任何地方都有效) + # 注意:根据你修改后的 step1,文件在 ID 文件夹内,如 141/141.cif + rel_source_path = os.path.join(DATA_SOURCE_DIR, group_name, anion_name, material_id, f"{material_id}.cif") + abs_source_path = os.path.abspath(rel_source_path) + + if not os.path.exists(abs_source_path): + print(f"源文件不存在: {abs_source_path}") + return + + # 2. 构建目标文件夹路径 + target_subdir = os.path.join(TARGET_DIR, group_name, anion_name) + if not os.path.exists(target_subdir): + os.makedirs(target_subdir) + + # 3. 构建目标链接路径 + target_link_path = os.path.join(target_subdir, f"{material_id}.cif") + + # 4. 创建链接 + try: + # 如果目标已经存在(可能是旧的链接),先删除 + if os.path.exists(target_link_path) or os.path.islink(target_link_path): + os.remove(target_link_path) + + os.symlink(abs_source_path, target_link_path) + # print(f"Link: {material_id} -> Passed") + except OSError as e: + print(f"创建软链接失败 {material_id}: {e}") + + +def process_all_csvs(): + """ + 遍历 output 文件夹下的所有 CSV 并处理 + """ + if not os.path.exists(CSV_ROOT_DIR): + print(f"CSV 目录不存在: {CSV_ROOT_DIR}") + return + + print("开始执行 Step 2-4 联合筛选...") + + # 遍历 output 目录 + # 结构预期: ../output/Group/Anion/Anion.csv (例如 ../output/O+S/O/O.csv 或 ../output/O/O.csv) + for root, dirs, files in os.walk(CSV_ROOT_DIR): + for file in files: + if file.endswith(".csv"): + csv_path = os.path.join(root, file) + + # 推断 Group 和 Anion + # root 的末尾应该是 .../Group/Anion + # 例如 root = ../output/O+S/O + + path_parts = os.path.normpath(root).split(os.sep) + # 倒数第一级是 Anion (O), 倒数第二级是 Group (O+S) + if len(path_parts) >= 2: + anion_name = path_parts[-1] + group_name = path_parts[-2] + else: + print(f"跳过路径结构异常的 CSV: {csv_path}") + continue + + # 确保这是一个有效的阴离子类型 + if anion_name not in THRESHOLDS: + continue + + print(f"正在处理: Group={group_name}, Anion={anion_name} ({file})") + + # 读取 CSV + df = pd.read_csv(csv_path) + + pass_count = 0 + total_count = len(df) + + for index, row in df.iterrows(): + material_id = str(row['Filename']) + + if check_requirements(row, anion_name): + create_symlink(group_name, anion_name, material_id) + pass_count += 1 + + print(f" - 完成: {pass_count}/{total_count} 个材料通过筛选并建立链接。") + + +if __name__ == "__main__": + process_all_csvs() \ No newline at end of file diff --git a/readme.md b/readme.md index 04c08b8..2f177ff 100644 --- a/readme.md +++ b/readme.md @@ -1,71 +1,74 @@ -# 高通量筛选 +# 高通量筛选与扩胞项目 -## 版本 -calc +## 环境配置需求 -## 配置需求 +项目需要配置两个 Conda 环境,名称分别为 **screen** 和 **zeo**。 -需要两个conda环境,名字分别为**screen**,**zeo** +### 1. zeo 环境 (用于几何结构分析) +* **Python**: 2 +* **核心库**: `zeo++` (需编译), `pymatgen==2018.12.12`, `numpy==1.16.6` +* **其他**: `os`, `argparse`, `PrettyTable`, `monty`, `future` -### zeo +### 2. screen 环境 (用于逻辑筛选与数据处理) +* **Python**: 3.11.4 +* **核心库**: `pymatgen==2024.11.13`, `pandas` (新增,用于处理CSV) -#### 运行库需求 +## 快速开始 -``` 2018.12.12 -python == 2 -pymatgen == 2018.12.12 -Numpy = 1.16.6 -os -argparse = 1.4.0 -PrettyTable = 1.01 -monty = 1.0.0 -future = 1.0.0 -``` +1. **数据准备**: + * 如果数据来源为 **Materials Project (MP)**,请将 CIF 文件放入 `data/input_pre`。 + * 如果数据来源为 **ICSD**,请直接将 CIF 文件放入 `data/input`。 +2. **运行**: + * 确保已创建上述两个 Conda 环境。 + * 在根目录下运行自动化脚本: + ```bash + bash main.sh + ``` -#### zeo++软件需求 +## 处理流程详解 -需要编译后放入python库中 +### Stage 1: 预处理与基础筛选 (Step 1) +* **Pre-process**: 清洗数据,统一放入 `input` 文件夹。 +* **Step 1**: + * 读取 CIF 文件,利用 `crystal_2` 库检查电荷平衡与化学式。 + * **文件重组**: 将通过筛选的文件按阴离子类型分类。 + * **新结构**: 每个材料拥有独立的文件夹(例如 `after_step1/O/141/141.cif`),便于管理后续的计算日志。 +* **Make SH**: 自动生成用于调用 Zeo++ 的 `analyze.sh` 脚本。 -### screen +### Stage 2: Zeo++ 计算 +* 切换至 `zeo` 环境。 +* 计算材料的孔径 (Percolation diameter)、比表面积等几何参数。 +* 结果输出为每个文件夹下的 **`log.txt`**。 -``` -python == 3.11.4 -pymatgen == 2024.11.13 -``` +### Stage 3: 数据提取与联合筛选 (Step 2-4) +* **数据提取 (`extract_data.py`)**: + * 自动遍历所有文件夹中的 `log.txt`。 + * 提取关键参数:`Percolation Diameter` (Step 2), `Minimum of d` (Step 3), `Maximum Node Length` (Step 4)。 + * 结果汇总为 CSV 文件保存在 `output/` 目录下(例如 `output/O/O.csv`)。 +* **联合筛选 (`step2_4_combined.py`)**: + * 读取 CSV 文件,根据预设的阈值(如 O: Perc>0.5, Min_d<3.0, Node>2.2)进行过滤。 + * **结果**: 将符合所有条件的材料,以**软链接**的形式汇聚到 `data/after_screening` 文件夹中。 -## 使用说明 +--- -如果配置的conda环境同名,运行**main.sh**即可 +## 扩胞逻辑 (Step 5 - 待后续执行) -当数据来源为MP时,需要将数据放在input_pre中 +目前扩胞逻辑维持原状,基于筛选后的结构进行处理。 -如果数据来源为ICSD,仅需将数据放在input中即可 +### 算法分解 +1. **读取结构**: 解析 CIF 文件。 +2. **统计 Occupation**: + * 将具有相同 Occupation 值的原子归为一类。 + * 生成 `Occupation_list` 字典。 +3. **计算扩大倍数**: + * 根据 Occupation 的分子分母情况(如 0.5 对应 1/2),计算公约数。 +4. **生成结构列表**: + * 根据分子与分母生成 `structure_list`。 +5. **对称性处理与扩胞**: + * 根据材料结构的对称性,生成三个方向的扩胞列表 (如 `{"x":1, "y":2, "z":1}`)。 +6. **生成新文件**: + * 结合 `structure_list` 与扩胞倍数生成最终的超胞 CIF。 - - -# 扩胞 -## 以下为每一步的分解 -### Step1 -读取cif文件 -### Step2 -统计Occupation情况,将具有相同Occupation值的记为一类,用Occupation值作为Key创建字典,该字典的一个项为atom_serial,是一个列表,记录相同Ocupation值的原子序号 -将上述字典输入一个列表Occupation_list,字典预留分子与分母两个参数 -需要函数为 -```angular2html -def process_cif_file(struct) - return Occupation_list -``` -### step3 -根据Occupation_list来计算扩大倍数\\ -首先逐一计算每个字典的分子与分母,根据key来计算,例如第一个key值为0.5,此时其对应分子为1,分母为2 -合并没一个字典,探索每一个分数的情况并求出公约数与对应的分子,更新每一个字典的值 -### step4 -根据分子与分母情况,生成structure_list,其中Occupation_list中的元素的number处的和为分子,总共个数为分母 -### step5 -根据材料结构决定对称性,对不同对称性得到不同等效情况 -根据对称性与最终扩胞生成三个方向扩胞列表,其中每个元素是字典,遵循格式为{["x":1,"y":2,"z":1]} -### step5 -根据structure_list与Occupation_list生成新的cif并保存 -### 一些假设 -只考虑两个原子在同一位置上,暂不考虑三个原子以上的情况 -不考虑Li原子的共占位情况,对Li原子不做处理 +### 假设条件 +* 只考虑两个原子在同一位置上的共占位情况。 +* 不考虑 Li 原子的共占位情况,对 Li 原子不做处理。 \ No newline at end of file