CSM及TET,CS
This commit is contained in:
2
.idea/Screen.iml
generated
2
.idea/Screen.iml
generated
@@ -2,7 +2,7 @@
|
|||||||
<module type="PYTHON_MODULE" version="4">
|
<module type="PYTHON_MODULE" version="4">
|
||||||
<component name="NewModuleRootManager">
|
<component name="NewModuleRootManager">
|
||||||
<content url="file://$MODULE_DIR$" />
|
<content url="file://$MODULE_DIR$" />
|
||||||
<orderEntry type="jdk" jdkName="C:\Users\PC20230606\Miniconda3" jdkType="Python SDK" />
|
<orderEntry type="jdk" jdkName="Python 3.11" jdkType="Python SDK" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
||||||
224
py/CSM_reconstruct.py
Normal file
224
py/CSM_reconstruct.py
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import numpy as np
|
||||||
|
import argparse
|
||||||
|
from tqdm import tqdm
|
||||||
|
from scipy.spatial import ConvexHull
|
||||||
|
from pymatgen.core import Structure
|
||||||
|
from pymatgen.core.periodic_table import Element
|
||||||
|
from pymatgen.analysis.chemenv.coordination_environments.coordination_geometry_finder import LocalGeometryFinder
|
||||||
|
|
||||||
|
# ================= 配置区域 =================
|
||||||
|
# 建议使用绝对路径,避免找不到文件夹
|
||||||
|
INPUT_DIR = "../../solidstate-tools/corner-sharing/data/1209/input" # 请确保这里有你的 .cif 文件
|
||||||
|
OUTPUT_DIR = "../output/CSM"
|
||||||
|
TARGET_ELEMENT = 'Li'
|
||||||
|
ENV_TYPE = 'both'
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
|
||||||
|
class HiddenPrints:
|
||||||
|
'''用于隐藏 pymatgen 繁杂的输出'''
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
self._original_stdout = sys.stdout
|
||||||
|
sys.stdout = open(os.devnull, 'w')
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
sys.stdout.close()
|
||||||
|
sys.stdout = self._original_stdout
|
||||||
|
|
||||||
|
|
||||||
|
def non_elements(struct):
|
||||||
|
"""
|
||||||
|
【关键修复】保留卤素(F, Cl, Br, I) 和其他阴离子,防止氯化物结构被清空。
|
||||||
|
"""
|
||||||
|
# 这里加入了 F, Cl, Br, I, P, Se, Te 等
|
||||||
|
anions_to_keep = {"O", "S", "N", "F", "Cl", "Br", "I", "P", "Se", "Te", "As", "Sb", "C"}
|
||||||
|
stripped = struct.copy()
|
||||||
|
species_to_remove = [el.symbol for el in stripped.composition.elements
|
||||||
|
if el.symbol not in anions_to_keep]
|
||||||
|
if species_to_remove:
|
||||||
|
stripped.remove_species(species_to_remove)
|
||||||
|
return stripped
|
||||||
|
|
||||||
|
|
||||||
|
def site_env(coord, struct, sp="Li", envtype='both'):
|
||||||
|
stripped = non_elements(struct)
|
||||||
|
|
||||||
|
# 如果剥离后结构为空(例如纯金属锂),直接返回
|
||||||
|
if len(stripped) == 0:
|
||||||
|
return {'csm': np.nan, 'vol': np.nan, 'type': 'Error_NoAnions'}
|
||||||
|
|
||||||
|
with_li = stripped.copy()
|
||||||
|
# 插入一个探测用的 Li 原子
|
||||||
|
with_li.append(sp, coord, coords_are_cartesian=False, validate_proximity=False)
|
||||||
|
|
||||||
|
# 尝试排序,如果因为部分占据导致排序失败,则使用原始顺序
|
||||||
|
try:
|
||||||
|
with_li = with_li.get_sorted_structure()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
tet_oct_competition = []
|
||||||
|
|
||||||
|
# ---------------- 四面体 (Tet) 检测 ----------------
|
||||||
|
if envtype == 'both' or envtype == 'tet':
|
||||||
|
for dist in np.linspace(1, 4, 601): # 扫描距离 1A 到 4A
|
||||||
|
neigh = with_li.get_neighbors(with_li.sites[0], dist)
|
||||||
|
if len(neigh) < 4:
|
||||||
|
continue
|
||||||
|
elif len(neigh) > 4:
|
||||||
|
break
|
||||||
|
|
||||||
|
neigh_coords = [i.coords for i in neigh]
|
||||||
|
try:
|
||||||
|
with HiddenPrints():
|
||||||
|
lgf = LocalGeometryFinder(only_symbols=["T:4"])
|
||||||
|
lgf.setup_structure(structure=with_li)
|
||||||
|
lgf.setup_local_geometry(isite=0, coords=neigh_coords)
|
||||||
|
|
||||||
|
site_volume = ConvexHull(neigh_coords).volume
|
||||||
|
# 获取 CSM
|
||||||
|
csm_val = lgf.get_coordination_symmetry_measures()['T:4']['csm']
|
||||||
|
|
||||||
|
tet_env = {'csm': csm_val, 'vol': site_volume, 'type': 'tet'}
|
||||||
|
tet_oct_competition.append(tet_env)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
if len(neigh) == 4: break
|
||||||
|
|
||||||
|
# ---------------- 八面体 (Oct) 检测 ----------------
|
||||||
|
if envtype == 'both' or envtype == 'oct':
|
||||||
|
for dist in np.linspace(1, 4, 601):
|
||||||
|
neigh = with_li.get_neighbors(with_li.sites[0], dist)
|
||||||
|
if len(neigh) < 6:
|
||||||
|
continue
|
||||||
|
elif len(neigh) > 6:
|
||||||
|
break
|
||||||
|
|
||||||
|
neigh_coords = [i.coords for i in neigh]
|
||||||
|
try:
|
||||||
|
with HiddenPrints():
|
||||||
|
lgf = LocalGeometryFinder(only_symbols=["O:6"], permutations_safe_override=False)
|
||||||
|
lgf.setup_structure(structure=with_li)
|
||||||
|
lgf.setup_local_geometry(isite=0, coords=neigh_coords)
|
||||||
|
|
||||||
|
site_volume = ConvexHull(neigh_coords).volume
|
||||||
|
csm_val = lgf.get_coordination_symmetry_measures()['O:6']['csm']
|
||||||
|
|
||||||
|
oct_env = {'csm': csm_val, 'vol': site_volume, 'type': 'oct'}
|
||||||
|
tet_oct_competition.append(oct_env)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
if len(neigh) == 6: break
|
||||||
|
|
||||||
|
# ---------------- 结果判定 ----------------
|
||||||
|
if len(tet_oct_competition) == 0:
|
||||||
|
return {'csm': np.nan, 'vol': np.nan, 'type': 'Non_' + envtype}
|
||||||
|
elif len(tet_oct_competition) == 1:
|
||||||
|
return tet_oct_competition[0]
|
||||||
|
elif len(tet_oct_competition) >= 2:
|
||||||
|
return min(tet_oct_competition, key=lambda x: x['csm'])
|
||||||
|
|
||||||
|
|
||||||
|
def extract_sites(struct, sp="Li", envtype='both'):
|
||||||
|
envlist = []
|
||||||
|
# 遍历所有位点寻找 Li
|
||||||
|
for i, site in enumerate(struct):
|
||||||
|
site_elements = [el.symbol for el in site.species.elements]
|
||||||
|
if sp in site_elements:
|
||||||
|
try:
|
||||||
|
# 传入结构副本以防修改原结构
|
||||||
|
singleenv = site_env(site.frac_coords, struct.copy(), sp, envtype)
|
||||||
|
envlist.append({
|
||||||
|
'site_index': i,
|
||||||
|
'frac_coords': site.frac_coords,
|
||||||
|
'type': singleenv.get('type', 'unknown'),
|
||||||
|
'csm': singleenv.get('csm', np.nan),
|
||||||
|
'volume': singleenv.get('vol', np.nan)
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
# 捕捉单个位点计算错误,不中断程序
|
||||||
|
# print(f" [Warn] Site {i} calculation failed: {e}")
|
||||||
|
pass
|
||||||
|
return envlist
|
||||||
|
|
||||||
|
|
||||||
|
def export_envs(envlist, sp, envtype, fname):
|
||||||
|
with open(fname, 'w') as f:
|
||||||
|
f.write('List of environment information\n')
|
||||||
|
f.write(f'Species : {sp}\n')
|
||||||
|
f.write(f'Envtype : {envtype}\n')
|
||||||
|
for item in envlist:
|
||||||
|
# 格式化输出,确保没有数据也能看懂
|
||||||
|
f.write(f"Site index {item['site_index']}: {item}\n")
|
||||||
|
|
||||||
|
|
||||||
|
# ================= 主程序 =================
|
||||||
|
def run_csm_analysis():
|
||||||
|
# 1. 检查目录
|
||||||
|
if not os.path.exists(INPUT_DIR):
|
||||||
|
print(f"错误: 输入目录不存在 -> {os.path.abspath(INPUT_DIR)}")
|
||||||
|
return
|
||||||
|
|
||||||
|
cif_files = []
|
||||||
|
for root, dirs, files in os.walk(INPUT_DIR):
|
||||||
|
for file in files:
|
||||||
|
if file.endswith(".cif"):
|
||||||
|
cif_files.append(os.path.join(root, file))
|
||||||
|
|
||||||
|
if not cif_files:
|
||||||
|
print(f"在 {INPUT_DIR} 中未找到 .cif 文件。")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"开始分析 {len(cif_files)} 个文件 (目标元素: {TARGET_ELEMENT}, 包含阴离子: F,Cl,Br,I,O,S,N...)")
|
||||||
|
|
||||||
|
success_count = 0
|
||||||
|
|
||||||
|
for cif_path in tqdm(cif_files, desc="Calculating CSM"):
|
||||||
|
try:
|
||||||
|
# 准备路径
|
||||||
|
rel_path = os.path.relpath(cif_path, INPUT_DIR)
|
||||||
|
rel_dir = os.path.dirname(rel_path)
|
||||||
|
file_base = os.path.splitext(os.path.basename(cif_path))[0]
|
||||||
|
|
||||||
|
target_dir = os.path.join(OUTPUT_DIR, rel_dir)
|
||||||
|
if not os.path.exists(target_dir):
|
||||||
|
os.makedirs(target_dir)
|
||||||
|
|
||||||
|
target_dat_path = os.path.join(target_dir, f"{file_base}.dat")
|
||||||
|
|
||||||
|
# 如果文件已存在且不为空,可选择跳过
|
||||||
|
# if os.path.exists(target_dat_path) and os.path.getsize(target_dat_path) > 0:
|
||||||
|
# continue
|
||||||
|
|
||||||
|
# 读取结构
|
||||||
|
struct = Structure.from_file(cif_path)
|
||||||
|
|
||||||
|
# 检查是否含 Li
|
||||||
|
if Element(TARGET_ELEMENT) not in struct.composition.elements:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 计算环境
|
||||||
|
env_list = extract_sites(struct, sp=TARGET_ELEMENT, envtype=ENV_TYPE)
|
||||||
|
|
||||||
|
# 写入结果 (即使 env_list 为空也写入一个标记文件,方便debug)
|
||||||
|
if env_list:
|
||||||
|
export_envs(env_list, sp=TARGET_ELEMENT, envtype=ENV_TYPE, fname=target_dat_path)
|
||||||
|
success_count += 1
|
||||||
|
else:
|
||||||
|
with open(target_dat_path, 'w') as f:
|
||||||
|
f.write(f"No {TARGET_ELEMENT} environments found (Check connectivity or anion types).")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n[Error] File: {os.path.basename(cif_path)} -> {e}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
print(f"\n分析完成!成功生成 {success_count} 个文件。")
|
||||||
|
print(f"输出目录: {os.path.abspath(OUTPUT_DIR)}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
run_csm_analysis()
|
||||||
@@ -12,11 +12,11 @@ from tqdm import tqdm
|
|||||||
|
|
||||||
# ================= 配置区域 =================
|
# ================= 配置区域 =================
|
||||||
# 输入目录:使用筛选后的目录,只计算符合要求的材料
|
# 输入目录:使用筛选后的目录,只计算符合要求的材料
|
||||||
INPUT_DIR = "../data/after_screening"
|
INPUT_DIR = "../../solidstate-tools/corner-sharing/data/1209/input"
|
||||||
# 输出目录
|
# 输出目录
|
||||||
OUTPUT_DIR = "../output/CSM"
|
OUTPUT_DIR = "../output/CSM"
|
||||||
# 分析参数
|
# 分析参数
|
||||||
TARGET_ELEMENT = 'Li'
|
TARGET_ELEMENT = 'Na'
|
||||||
ENV_TYPE = 'both' # 可选 'tet', 'oct', 'both'
|
ENV_TYPE = 'both' # 可选 'tet', 'oct', 'both'
|
||||||
# ===========================================
|
# ===========================================
|
||||||
|
|
||||||
|
|||||||
@@ -110,4 +110,4 @@ def read_files_check_basic(folder_path):
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# 根据你的 readme,MP数据在 input_pre,ICSD在 input
|
# 根据你的 readme,MP数据在 input_pre,ICSD在 input
|
||||||
# 这里默认读取 input,你可以根据实际情况修改
|
# 这里默认读取 input,你可以根据实际情况修改
|
||||||
read_files_check_basic("../data/input")
|
read_files_check_basic("../../solidstate-tools/corner-sharing/data/1209/input")
|
||||||
@@ -96,7 +96,7 @@ def organize_files_direct(input_folder, output_base):
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# 输入路径
|
# 输入路径
|
||||||
input_dir = "../data/input" # 如果是MP数据请改为 ../data/input_pre
|
input_dir = "../../solidstate-tools/corner-sharing/data/1209/input" # 如果是MP数据请改为 ../data/input_pre
|
||||||
# 输出路径
|
# 输出路径
|
||||||
output_dir = "../data/after_step1"
|
output_dir = "../data/after_step1"
|
||||||
|
|
||||||
|
|||||||
0
py/utils/__init__.py
Normal file
0
py/utils/__init__.py
Normal file
@@ -52,7 +52,7 @@ def non_elements(struct, sp='Li'):
|
|||||||
sp : the mobile specie
|
sp : the mobile specie
|
||||||
returns a new structure containing only the framework anions (O, S, N).
|
returns a new structure containing only the framework anions (O, S, N).
|
||||||
"""
|
"""
|
||||||
anions_to_keep = {"O", "S", "N"}
|
anions_to_keep = {"O", "S", "N","Br","Cl"}
|
||||||
stripped = struct.copy()
|
stripped = struct.copy()
|
||||||
species_to_remove = [el.symbol for el in stripped.composition.elements
|
species_to_remove = [el.symbol for el in stripped.composition.elements
|
||||||
if el.symbol not in anions_to_keep]
|
if el.symbol not in anions_to_keep]
|
||||||
|
|||||||
Reference in New Issue
Block a user