一些小修改
This commit is contained in:
79
dpgen/create_supercell_poscar.py
Normal file
79
dpgen/create_supercell_poscar.py
Normal file
@@ -0,0 +1,79 @@
|
||||
from pymatgen.core import Structure
|
||||
from pymatgen.io.vasp import Poscar
|
||||
|
||||
def create_supercell_poscar(cif_path, supercell_matrix, output_filename="POSCAR_supercell"):
|
||||
"""
|
||||
从CIF文件读取晶体结构,根据指定的矩阵进行扩胞,并生成VASP POSCAR文件。
|
||||
|
||||
Args:
|
||||
cif_path (str): 输入的CIF文件路径。
|
||||
supercell_matrix (list or tuple): 3x3的扩胞矩阵。
|
||||
- 对于简单的对角扩胞 (例如 2x2x4),使用: [[2, 0, 0], [0, 2, 0], [0, 0, 4]]
|
||||
- 对于非对角扩胞 (例如 a_s=3a, b_s=2a+4b, c_s=6c),使用: [[3, 0, 0], [2, 4, 0], [0, 0, 6]]
|
||||
output_filename (str): 输出的POSCAR文件名。默认为 "POSCAR_supercell"。
|
||||
|
||||
Returns:
|
||||
bool: 如果成功生成文件则返回 True,否则返回 False。
|
||||
"""
|
||||
try:
|
||||
# 1. 从CIF文件加载结构
|
||||
# 使用 from_file 静态方法直接读取
|
||||
# primitive=False 确保我们使用CIF中定义的晶胞,而不是其原胞
|
||||
original_structure = Structure.from_file(cif_path, primitive=False)
|
||||
|
||||
print("--- 原始晶胞信息 ---")
|
||||
print(f" 原子数: {original_structure.num_sites}")
|
||||
print(f" 化学式: {original_structure.composition.reduced_formula}")
|
||||
print(f" 晶格参数 (a, b, c, α, β, γ):")
|
||||
lat = original_structure.lattice
|
||||
print(f" {lat.a:.4f}, {lat.b:.4f}, {lat.c:.4f}, {lat.alpha:.2f}, {lat.beta:.2f}, {lat.gamma:.2f}")
|
||||
|
||||
# 2. 进行扩胞操作
|
||||
# 注意:pymatgen 会自动处理原子坐标的映射
|
||||
supercell_structure = original_structure * supercell_matrix
|
||||
|
||||
print("\n--- 扩胞后信息 ---")
|
||||
print(f" 扩胞矩阵: {supercell_matrix}")
|
||||
print(f" 新原子数: {supercell_structure.num_sites}")
|
||||
print(f" 新化学式: {supercell_structure.composition.reduced_formula}")
|
||||
print(f" 新晶格参数 (a, b, c, α, β, γ):")
|
||||
super_lat = supercell_structure.lattice
|
||||
print(f" {super_lat.a:.4f}, {super_lat.b:.4f}, {super_lat.c:.4f}, {super_lat.alpha:.2f}, {super_lat.beta:.2f}, {super_lat.gamma:.2f}")
|
||||
|
||||
# 3. 创建Poscar对象并写入文件
|
||||
# comment 参数可以设置POSCAR文件的第一行注释
|
||||
poscar = Poscar(supercell_structure, comment=f"Supercell from {cif_path}")
|
||||
poscar.write_file(output_filename)
|
||||
|
||||
print(f"\n成功!已将扩胞结构写入文件: {output_filename}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"发生错误: {e}")
|
||||
return False
|
||||
|
||||
# --- 使用示例 ---
|
||||
|
||||
# 假设您的CIF文件名为 "origin.cif",并且与此脚本在同一目录下。
|
||||
# 如果您在复现 Wang Shuo 的工作,他们可能使用了不同的扩胞方案。
|
||||
# 例如,用于MD模拟的大超胞是 2x2x4 [source_id: 3]。
|
||||
# 而用于DP-GEN探索的小超胞是 1x1x2 [source_id: 3]。
|
||||
|
||||
# 示例1:生成用于DP-GEN探索的 1x1x2 小超胞 (60个原子)
|
||||
print("="*40)
|
||||
print("正在生成 1x1x2 超胞 (用于 DP-GEN 探索)...")
|
||||
matrix_1x1x2 = [[1, 0, 0], [0, 1, 0], [0, 0, 2]]
|
||||
create_supercell_poscar("data/P3ma/origin.cif", matrix_1x1x2, "data/P3ma/output/POSCAR_1x1x2_60atoms")
|
||||
|
||||
# 示例2:生成用于LAMMPS MD模拟的 2x2x4 大超胞 (480个原子)
|
||||
# print("\n" + "="*40)
|
||||
# print("正在生成 2x2x4 超胞 (用于 LAMMPS MD 模拟)...")
|
||||
# matrix_2x2x4 = [[2, 0, 0], [0, 2, 0], [0, 0, 4]]
|
||||
# create_supercell_poscar("origin.cif", matrix_2x2x4, "POSCAR_2x2x4_480atoms")
|
||||
#
|
||||
# # 示例3:生成 Geng 等人研究中使用的大超胞 (2160个原子) [source_id: 1]
|
||||
# # 这个扩胞矩阵 a_s = 3a_0, b_s = 2a_0 + 4b_0, c_s = 6c_0 [source_id: 1]
|
||||
# print("\n" + "="*40)
|
||||
# print("正在生成非对角扩胞超胞 (Geng et al.)...")
|
||||
# matrix_geng = [[3, 0, 0], [2, 4, 0], [0, 0, 6]]
|
||||
# create_supercell_poscar("origin.cif", matrix_geng, "POSCAR_Geng_2160atoms")
|
||||
@@ -147,5 +147,5 @@ def make_pnma_poscar_from_cif(cif_path: str,
|
||||
print(f"写出 {out_poscar};总原子数 = {len(s)};组成 = {comp}")
|
||||
|
||||
if __name__=="__main__":
|
||||
# make_model3_poscar_from_cif("data/P3ma/model3.cif","data/P3ma/supercell_model4.poscar")
|
||||
make_pnma_poscar_from_cif("data/Pnma/origin.cif","data/Pnma/supercell_pnma.poscar",seed=42)
|
||||
# make_model3_poscar_from_cif("raw/P3ma/model3.cif","raw/P3ma/supercell_model4.poscar")
|
||||
make_pnma_poscar_from_cif("data/Pnma/origin.cif","raw/Pnma/supercell_pnma.poscar",seed=42)
|
||||
@@ -0,0 +1,240 @@
|
||||
import pymatgen.core as mg
|
||||
from pymatgen.io.cif import CifParser
|
||||
from pymatgen.transformations.standard_transformations import SupercellTransformation
|
||||
import random
|
||||
import os
|
||||
|
||||
|
||||
def create_ordered_structure_from_disordered(disordered_structure):
|
||||
"""
|
||||
手动将包含部分占位的无序结构转换为有序结构,借鉴plus.py的思路。
|
||||
"""
|
||||
s = disordered_structure.copy()
|
||||
|
||||
# 识别需要处理的部分占位
|
||||
# 根据 model3.cif, Y2(z≈0.488, occ=0.75), Y3(z≈-0.065, occ=0.25), Li2(z≈0.5, occ=0.5) [model3.cif]
|
||||
y2_indices, y3_indices, li2_indices = [], [], []
|
||||
|
||||
for i, site in enumerate(s.sites):
|
||||
# 使用z坐标来识别特定的部分占位
|
||||
z = site.frac_coords[2]
|
||||
if site.species_string == "Y":
|
||||
if abs(z - 0.488) < 0.05:
|
||||
y2_indices.append(i)
|
||||
elif abs(z - (-0.065)) < 0.05 or abs(z - (1 - 0.065)) < 0.05:
|
||||
y3_indices.append(i)
|
||||
elif site.species_string == "Li":
|
||||
if abs(z - 0.5) < 0.05:
|
||||
li2_indices.append(i)
|
||||
|
||||
# 根据占位率随机选择要保留的原子
|
||||
def choose_keep(indices, keep_fraction):
|
||||
num_to_keep = int(round(len(indices) * keep_fraction))
|
||||
return set(random.sample(indices, num_to_keep))
|
||||
|
||||
keep_y2 = choose_keep(y2_indices, 0.75)
|
||||
keep_y3 = choose_keep(y3_indices, 0.25)
|
||||
keep_li2 = choose_keep(li2_indices, 0.50)
|
||||
|
||||
# 找出所有需要删除的原子索引
|
||||
to_remove_indices = [i for i in y2_indices if i not in keep_y2]
|
||||
to_remove_indices.extend([i for i in y3_indices if i not in keep_y3])
|
||||
to_remove_indices.extend([i for i in li2_indices if i not in keep_li2])
|
||||
|
||||
# 从后往前删除,避免索引错位
|
||||
s.remove_sites(sorted(to_remove_indices, reverse=True))
|
||||
|
||||
# --- 关键修复步骤 ---
|
||||
# 最终清理,确保所有位点都是有序的
|
||||
for i, site in enumerate(s.sites):
|
||||
if not site.is_ordered:
|
||||
# 将Composition对象转换为字典,然后找到占位率最高的元素 [plus.py]
|
||||
species_dict = site.species.as_dict()
|
||||
main_specie = max(species_dict.items(), key=lambda item: item[1])[0]
|
||||
s.replace(i, main_specie)
|
||||
|
||||
return s
|
||||
|
||||
|
||||
def create_supercells_from_file(cif_path, output_path="."):
|
||||
"""
|
||||
根据给定的CIF文件路径,生成三种不同尺寸和缺陷的超胞,并保存为POSCAR文件。
|
||||
"""
|
||||
if not os.path.exists(cif_path):
|
||||
print(f"错误: 文件 '{cif_path}' 不存在。")
|
||||
return
|
||||
|
||||
print(f"正在从 {cif_path} 读取结构...")
|
||||
parser = CifParser(cif_path)
|
||||
disordered_structure = parser.parse_structures(primitive=False)[0]
|
||||
|
||||
structure = create_ordered_structure_from_disordered(disordered_structure)
|
||||
print(f"成功将无序结构转换为一个包含 {len(structure)} 个原子的有序单胞。")
|
||||
|
||||
os.makedirs(output_path, exist_ok=True)
|
||||
|
||||
# 任务一:生成60原子超胞 (无缺陷)
|
||||
print("\n--- 正在生成 60原子无缺陷超胞 (1x1x2) ---")
|
||||
tf_60 = SupercellTransformation([[1, 0, 0], [0, 1, 0], [0, 0, 2]])
|
||||
sc_60_no_defect = tf_60.apply_transformation(structure)
|
||||
print(f"原子总数: {len(sc_60_no_defect)}, 化学式: {sc_60_no_defect.composition.reduced_formula}")
|
||||
sc_60_no_defect.to(fmt="poscar", filename=os.path.join(output_path, "POSCAR_60_no_defect"))
|
||||
print(f"已保存文件: {os.path.join(output_path, 'POSCAR_60_no_defect')}")
|
||||
|
||||
# 任务二:生成60原子超胞 (含一对反位缺陷)
|
||||
print("\n--- 正在生成 60原子含一对反位缺陷超胞 ---")
|
||||
sc_60_defect = sc_60_no_defect.copy()
|
||||
li_indices = [i for i, site in enumerate(sc_60_defect.sites) if site.species_string == 'Li']
|
||||
y_indices = [i for i, site in enumerate(sc_60_defect.sites) if site.species_string == 'Y']
|
||||
|
||||
if li_indices and y_indices:
|
||||
li_swap_idx, y_swap_idx = random.choice(li_indices), random.choice(y_indices)
|
||||
sc_60_defect.replace(li_swap_idx, "Y")
|
||||
sc_60_defect.replace(y_swap_idx, "Li")
|
||||
print(f"成功引入一对反位缺陷。浓度: {2 / (len(li_indices) + len(y_indices)) * 100:.2f}%")
|
||||
sc_60_defect.to(fmt="poscar", filename=os.path.join(output_path, "POSCAR_60_antisite_defect"))
|
||||
print(f"已保存文件: {os.path.join(output_path, 'POSCAR_60_antisite_defect')}")
|
||||
|
||||
# 任务三:生成90原子超胞 (含一对反位缺陷)
|
||||
print("\n--- 正在生成 90原子含一对反位缺陷超胞 ---")
|
||||
tf_90 = SupercellTransformation([[1, 0, 0], [0, 1, 0], [0, 0, 3]])
|
||||
sc_90_no_defect = tf_90.apply_transformation(structure)
|
||||
sc_90_defect = sc_90_no_defect.copy()
|
||||
li_indices = [i for i, site in enumerate(sc_90_defect.sites) if site.species_string == 'Li']
|
||||
y_indices = [i for i, site in enumerate(sc_90_defect.sites) if site.species_string == 'Y']
|
||||
|
||||
if li_indices and y_indices:
|
||||
li_swap_idx, y_swap_idx = random.choice(li_indices), random.choice(y_indices)
|
||||
sc_90_defect.replace(li_swap_idx, "Y")
|
||||
sc_90_defect.replace(y_swap_idx, "Li")
|
||||
print(f"原子总数: {len(sc_90_defect)}, 浓度: {2 / (len(li_indices) + len(y_indices)) * 100:.2f}%")
|
||||
sc_90_defect.to(fmt="poscar", filename=os.path.join(output_path, "POSCAR_90_antisite_defect"))
|
||||
print(f"已保存文件: {os.path.join(output_path, 'POSCAR_90_antisite_defect')}")
|
||||
|
||||
|
||||
def create_ordered_p3ma_structure(disordered_structure):
|
||||
"""
|
||||
手动将P3ma相的无序结构(包含Y2, Y3, Li2的部分占位)转换为有序结构。
|
||||
"""
|
||||
s = disordered_structure.copy()
|
||||
|
||||
# 根据 model3.cif, 识别Y2(z≈0.488, occ=0.75), Y3(z≈-0.065, occ=0.25), Li2(z≈0.5, occ=0.5) [model3.cif]
|
||||
y2_indices, y3_indices, li2_indices = [], [], []
|
||||
|
||||
for i, site in enumerate(s.sites):
|
||||
z = site.frac_coords[2]
|
||||
if site.species_string == "Y":
|
||||
if abs(z - 0.488) < 0.05:
|
||||
y2_indices.append(i)
|
||||
elif abs(z - (-0.065)) < 0.05 or abs(z - (1 - 0.065)) < 0.05:
|
||||
y3_indices.append(i)
|
||||
elif site.species_string == "Li":
|
||||
if abs(z - 0.5) < 0.05:
|
||||
li2_indices.append(i)
|
||||
|
||||
# 根据占位率随机选择要保留的原子
|
||||
def choose_keep(indices, keep_fraction):
|
||||
num_to_keep = int(round(len(indices) * keep_fraction))
|
||||
return set(random.sample(indices, num_to_keep))
|
||||
|
||||
keep_y2 = choose_keep(y2_indices, 0.75)
|
||||
keep_y3 = choose_keep(y3_indices, 0.25)
|
||||
keep_li2 = choose_keep(li2_indices, 0.50)
|
||||
|
||||
# 找出所有需要删除的原子索引
|
||||
to_remove_indices = [i for i in y2_indices if i not in keep_y2]
|
||||
to_remove_indices.extend([i for i in y3_indices if i not in keep_y3])
|
||||
to_remove_indices.extend([i for i in li2_indices if i not in keep_li2])
|
||||
|
||||
s.remove_sites(sorted(to_remove_indices, reverse=True))
|
||||
|
||||
# 最终清理,确保所有位点都是有序的
|
||||
for i, site in enumerate(s.sites):
|
||||
if not site.is_ordered:
|
||||
species_dict = site.species.as_dict()
|
||||
main_specie = max(species_dict.items(), key=lambda item: item[1])[0]
|
||||
s.replace(i, main_specie)
|
||||
|
||||
return s
|
||||
|
||||
|
||||
def create_multiple_p3ma_supercells(cif_path, num_configs=5, output_path="."):
|
||||
"""
|
||||
读取P3ma相CIF,为不同尺寸的超胞生成多个具有不同反位缺陷位置的构型。
|
||||
"""
|
||||
if not os.path.exists(cif_path):
|
||||
print(f"错误: 文件 '{cif_path}' 不存在。")
|
||||
return
|
||||
|
||||
print(f"正在从 {cif_path} 读取P3ma结构...")
|
||||
parser = CifParser(cif_path)
|
||||
disordered_structure = parser.parse_structures(primitive=False)[0]
|
||||
|
||||
structure = create_ordered_p3ma_structure(disordered_structure)
|
||||
print(f"成功将无序P3ma结构转换为一个包含 {len(structure)} 个原子的有序单胞。")
|
||||
|
||||
os.makedirs(output_path, exist_ok=True)
|
||||
|
||||
target_sizes = [60, 90]
|
||||
for size in target_sizes:
|
||||
print(f"\n--- 正在为约 {size} 原子的版本生成 {num_configs} 个不同构型 ---")
|
||||
|
||||
# 1. 构建基准超胞
|
||||
if size == 60:
|
||||
tf = SupercellTransformation([[1, 0, 0], [0, 1, 0], [0, 0, 2]])
|
||||
filename_suffix = "60_approx"
|
||||
else: # size == 90
|
||||
tf = SupercellTransformation([[1, 0, 0], [0, 1, 0], [0, 0, 3]])
|
||||
filename_suffix = "90_approx"
|
||||
|
||||
base_supercell = tf.apply_transformation(structure)
|
||||
print(f"已生成基准超胞,实际原子数: {len(base_supercell)}")
|
||||
|
||||
li_indices = [i for i, site in enumerate(base_supercell.sites) if site.species_string == 'Li']
|
||||
y_indices = [i for i, site in enumerate(base_supercell.sites) if site.species_string == 'Y']
|
||||
|
||||
if not li_indices or not y_indices:
|
||||
print("错误:在超胞中未找到足够的Li或Y原子来引入缺陷。")
|
||||
continue
|
||||
|
||||
# 2. 循环生成多个独特的缺陷构型
|
||||
used_pairs = set()
|
||||
for i in range(num_configs):
|
||||
defect_supercell = base_supercell.copy()
|
||||
|
||||
# 确保随机选择的交换对是全新的
|
||||
# 增加一个尝试次数上限,防止在原子数很少时陷入死循环
|
||||
max_tries = len(li_indices) * len(y_indices)
|
||||
for _ in range(max_tries):
|
||||
li_swap_idx = random.choice(li_indices)
|
||||
y_swap_idx = random.choice(y_indices)
|
||||
pair = tuple(sorted((li_swap_idx, y_swap_idx)))
|
||||
if pair not in used_pairs:
|
||||
used_pairs.add(pair)
|
||||
break
|
||||
else:
|
||||
print(f" 警告: 未能找到更多独特的交换对,已停止在第 {i} 个构型。")
|
||||
break
|
||||
|
||||
# 引入缺陷
|
||||
defect_supercell.replace(li_swap_idx, "Y")
|
||||
defect_supercell.replace(y_swap_idx, "Li")
|
||||
|
||||
print(f" 配置 {i}: 成功引入一对反位缺陷 (Li at index {li_swap_idx} <-> Y at index {y_swap_idx})。")
|
||||
|
||||
# 3. 保存为带编号的POSCAR文件
|
||||
poscar_filename = f"POSCAR_P3ma_{filename_suffix}_antisite_defect_{i}"
|
||||
poscar_path = os.path.join(output_path, poscar_filename)
|
||||
defect_supercell.to(fmt="poscar", filename=poscar_path)
|
||||
print(f" 已保存文件: {poscar_path}")
|
||||
|
||||
if __name__ == '__main__':
|
||||
# --- 使用方法 ---
|
||||
# 1. 将您的CIF文件保存,例如命名为 "Li3YCl6.cif"
|
||||
# 2. 将文件名作为参数传递给函数
|
||||
cif_file_path = "data/P3ma/model3.cif" # 修改为您的CIF文件名
|
||||
output_directory = "raw/P3ma/output" # 可以指定一个输出目录
|
||||
|
||||
# create_supercells_from_file(cif_file_path, output_directory)
|
||||
create_multiple_p3ma_supercells(cif_file_path,output_path=output_directory)
|
||||
print("所有任务完成!")
|
||||
115
dpgen/supercell_make_pnma.py
Normal file
115
dpgen/supercell_make_pnma.py
Normal file
@@ -0,0 +1,115 @@
|
||||
import pymatgen.core as mg
|
||||
from pymatgen.io.cif import CifParser
|
||||
from pymatgen.transformations.standard_transformations import SupercellTransformation
|
||||
import random
|
||||
import os
|
||||
|
||||
|
||||
def create_ordered_pnma_structure(disordered_structure):
|
||||
"""
|
||||
手动将Pnma相的无序结构(主要为Li的部分占位)转换为有序结构。
|
||||
"""
|
||||
s = disordered_structure.copy()
|
||||
|
||||
# 根据origin.cif, Li位点的占位率为0.75 [5]
|
||||
partial_li_indices = [i for i, site in enumerate(s.sites) if "Li" in site.species and not site.is_ordered]
|
||||
|
||||
# 根据0.75的占位率随机选择要保留的Li原子
|
||||
num_to_keep = int(round(len(partial_li_indices) * 0.75))
|
||||
keep_indices = set(random.sample(partial_li_indices, num_to_keep))
|
||||
|
||||
# 找出需要删除的原子索引
|
||||
to_remove_indices = [i for i in partial_li_indices if i not in keep_indices]
|
||||
|
||||
s.remove_sites(sorted(to_remove_indices, reverse=True))
|
||||
|
||||
# 重新创建一个新的、完全有序的结构,避免任何副作用
|
||||
ordered_species = []
|
||||
ordered_coords = []
|
||||
for site in s.sites:
|
||||
# 只取每个位点的主要元素
|
||||
main_specie = site.species.elements[0]
|
||||
ordered_species.append(main_specie)
|
||||
ordered_coords.append(site.frac_coords)
|
||||
|
||||
final_structure = mg.Structure(s.lattice, ordered_species, ordered_coords)
|
||||
|
||||
return final_structure
|
||||
|
||||
|
||||
def create_multiple_pnma_supercells(cif_path, num_configs=3, output_path="."):
|
||||
"""
|
||||
读取Pnma相CIF,为不同尺寸的超胞生成多个具有不同反位缺陷位置的构型。
|
||||
"""
|
||||
if not os.path.exists(cif_path):
|
||||
print(f"错误: 文件 '{cif_path}' 不存在。")
|
||||
return
|
||||
|
||||
print(f"正在从 {cif_path} 读取Pnma结构...")
|
||||
parser = CifParser(cif_path)
|
||||
disordered_structure = parser.parse_structures(primitive=False)[0]
|
||||
|
||||
structure = create_ordered_pnma_structure(disordered_structure)
|
||||
print(f"成功将无序Pnma结构转换为一个包含 {len(structure)} 个原子的有序单胞。")
|
||||
|
||||
os.makedirs(output_path, exist_ok=True)
|
||||
|
||||
target_sizes = [60, 90]
|
||||
for size in target_sizes:
|
||||
print(f"\n--- 正在为约 {size} 原子的版本生成 {num_configs} 个不同构型 ---")
|
||||
|
||||
# 1. 构建基准超胞
|
||||
if size == 60:
|
||||
tf = SupercellTransformation([[1, 0, 0], [0, 1, 0], [0, 0, 2]])
|
||||
filename_suffix = "60_approx"
|
||||
else: # size == 90
|
||||
tf = SupercellTransformation([[1, 0, 0], [0, 1, 0], [0, 0, 3]])
|
||||
filename_suffix = "90_approx"
|
||||
|
||||
base_supercell = tf.apply_transformation(structure)
|
||||
print(f"已生成基准超胞,实际原子数: {len(base_supercell)}")
|
||||
|
||||
li_indices = [i for i, site in enumerate(base_supercell.sites) if site.species_string == 'Li']
|
||||
y_indices = [i for i, site in enumerate(base_supercell.sites) if site.species_string == 'Y']
|
||||
|
||||
if not li_indices or not y_indices:
|
||||
print("错误:在超胞中未找到足够的Li或Y原子来引入缺陷。")
|
||||
continue
|
||||
|
||||
# 2. 循环生成多个独特的缺陷构型
|
||||
used_pairs = set()
|
||||
for i in range(num_configs):
|
||||
defect_supercell = base_supercell.copy()
|
||||
|
||||
# 确保随机选择的交换对是全新的
|
||||
while True:
|
||||
li_swap_idx = random.choice(li_indices)
|
||||
y_swap_idx = random.choice(y_indices)
|
||||
# 使用排序后的元组作为键,确保(a,b)和(b,a)被视为相同
|
||||
pair = tuple(sorted((li_swap_idx, y_swap_idx)))
|
||||
if pair not in used_pairs:
|
||||
used_pairs.add(pair)
|
||||
break
|
||||
|
||||
# 引入缺陷
|
||||
defect_supercell.replace(li_swap_idx, "Y")
|
||||
defect_supercell.replace(y_swap_idx, "Li")
|
||||
|
||||
print(f" 配置 {i}: 成功引入一对反位缺陷 (Li at index {li_swap_idx} <-> Y at index {y_swap_idx})。")
|
||||
|
||||
# 3. 保存为带编号的POSCAR文件
|
||||
poscar_filename = f"POSCAR_Pnma_{filename_suffix}_antisite_defect_{i}"
|
||||
poscar_path = os.path.join(output_path, poscar_filename)
|
||||
defect_supercell.to(fmt="poscar", filename=poscar_path)
|
||||
print(f" 已保存文件: {poscar_path}")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 请将您的Pnma相CIF文件保存,并修改此路径
|
||||
# 这里我们使用您提供的参考文件名 'origin.cif'
|
||||
cif_file_path = "data/Pnma/origin.cif"
|
||||
output_directory = "raw/Pnma/output"
|
||||
|
||||
create_multiple_pnma_supercells(cif_file_path, num_configs=3, output_path=output_directory)
|
||||
|
||||
print("\nPnma相处理完成!")
|
||||
197
dpgen/supercell_make_wangshuo.py
Normal file
197
dpgen/supercell_make_wangshuo.py
Normal file
@@ -0,0 +1,197 @@
|
||||
import random
|
||||
from collections import defaultdict, Counter
|
||||
|
||||
from pymatgen.core import Structure, Element
|
||||
from pymatgen.io.lammps.data import LammpsData
|
||||
|
||||
# ASE 兜底(可选)
|
||||
try:
|
||||
from ase.io import write as ase_write
|
||||
from ase import Atoms as ASEAtoms
|
||||
HAS_ASE = True
|
||||
except Exception:
|
||||
HAS_ASE = False
|
||||
|
||||
# ===== 用户参数 =====
|
||||
cif_filename = "data/P3ma/origin.cif" # 你的输入 CIF(含部分占位)[5]
|
||||
supercell_matrix = [[1, 0, 0], [0, 1, 0], [0, 0, 2]] # 2×2×4 超胞(总复制数=16)[3]
|
||||
out_lammps = "lyc_P3m1_1x1x2_from_cif_ordered.vasp" # 输出 LAMMPS raw
|
||||
seed = 2025
|
||||
strict_count = True # 严格配额:每个父位点在超胞内按占位概率分配整数个原子/空位
|
||||
# ====================
|
||||
|
||||
random.seed(seed)
|
||||
|
||||
def species_to_probs(site):
|
||||
"""
|
||||
将站点的物种占位转换为 [(species or None(vac), prob)] 列表,prob 归一化为和=1。
|
||||
若总占位 < 1,补一个 vacancy(None)。
|
||||
去掉氧化态,仅保留元素。
|
||||
"""
|
||||
sp_items = site.species.items()
|
||||
total = 0.0
|
||||
pairs = []
|
||||
for spc, occ in sp_items:
|
||||
# 转成 Element(剔除氧化态)
|
||||
try:
|
||||
e = Element(spc.symbol) if hasattr(spc, "symbol") else Element(str(spc))
|
||||
except Exception:
|
||||
e = Element(str(spc))
|
||||
pairs.append((e, float(occ)))
|
||||
total += float(occ)
|
||||
if total < 1.0 - 1e-10:
|
||||
pairs.append((None, 1.0 - total)) # vacancy
|
||||
total = 1.0
|
||||
# 归一化
|
||||
if abs(total - 1.0) > 1e-10:
|
||||
pairs = [(e, p / total) for (e, p) in pairs]
|
||||
return pairs
|
||||
|
||||
def draw_counts_from_probs(n, probs):
|
||||
"""
|
||||
给定复制数 n 和概率 probs,返回 {species/None: count},使计数和为 n。
|
||||
先按四舍五入,再用残差修正到总和= n。
|
||||
"""
|
||||
# 初分配
|
||||
counts = {sp: int(round(p * n)) for sp, p in probs}
|
||||
s = sum(counts.values())
|
||||
if s == n:
|
||||
return counts
|
||||
|
||||
# 残差排序:需要增加则按概率大的优先加;需要减少则按概率小的优先减
|
||||
if n > s:
|
||||
need = n - s
|
||||
probs_sorted = sorted(probs, key=lambda x: x[1], reverse=True)
|
||||
for i in range(need):
|
||||
sp = probs_sorted[i % len(probs_sorted)][0]
|
||||
counts[sp] = counts.get(sp, 0) + 1
|
||||
else:
|
||||
need = s - n
|
||||
probs_sorted = sorted(probs, key=lambda x: x[1]) # 先减概率小的
|
||||
idx = 0
|
||||
while need > 0 and idx < 50 * len(probs_sorted):
|
||||
sp = probs_sorted[idx % len(probs_sorted)][0]
|
||||
if counts.get(sp, 0) > 0:
|
||||
counts[sp] -= 1
|
||||
need -= 1
|
||||
idx += 1
|
||||
return counts
|
||||
|
||||
def collapse_disorder_to_ordered_supercell(struct, M, strict=True):
|
||||
"""
|
||||
处理步骤:
|
||||
1) 给原胞每个位点打 parent_id
|
||||
2) 扩胞到 M
|
||||
3) 以父位点为组,在组内(复制数 n)按占位概率分配整数个 species/空位到每个复制位点
|
||||
- 有序位点:所有复制直接保留
|
||||
- 无序位点/部分占位:严格配额或独立抽样
|
||||
4) 返回完全有序的超胞 Structure
|
||||
"""
|
||||
s0 = struct.copy()
|
||||
s0.add_site_property("parent_id", list(range(s0.num_sites)))
|
||||
|
||||
sc = s0.copy()
|
||||
sc.make_supercell(M)
|
||||
|
||||
# 按父位点分组
|
||||
groups = defaultdict(list)
|
||||
for i, site in enumerate(sc.sites):
|
||||
pid = sc.site_properties["parent_id"][i]
|
||||
groups[pid].append(i)
|
||||
|
||||
new_species = []
|
||||
new_fracs = []
|
||||
new_lat = sc.lattice
|
||||
|
||||
for pid, idx_list in groups.items():
|
||||
# 用该组第一个复制的站点定义占位
|
||||
site0 = sc[idx_list[0]]
|
||||
# 有序站点:直接全部保留(只有一种元素,且占位为1)
|
||||
if site0.is_ordered:
|
||||
species_elem = list(site0.species.keys())[0]
|
||||
for i in idx_list:
|
||||
new_species.append(species_elem)
|
||||
new_fracs.append(sc[i].frac_coords)
|
||||
continue
|
||||
|
||||
# 无序/部分占位:概率分配
|
||||
probs = species_to_probs(site0)
|
||||
n = len(idx_list)
|
||||
|
||||
if strict:
|
||||
counts = draw_counts_from_probs(n, probs)
|
||||
# 构造分配池并打乱
|
||||
pool = []
|
||||
for sp, c in counts.items():
|
||||
pool += [sp] * c
|
||||
random.shuffle(pool)
|
||||
# 分配到每个复制位点
|
||||
for i, sp in zip(idx_list, pool):
|
||||
if sp is None:
|
||||
continue # vacancy -> 删除该位点
|
||||
new_species.append(sp)
|
||||
new_fracs.append(sc[i].frac_coords)
|
||||
else:
|
||||
# 独立抽样
|
||||
import bisect
|
||||
species_list = [sp for sp, p in probs]
|
||||
cum = []
|
||||
ssum = 0.0
|
||||
for _, p in probs:
|
||||
ssum += p
|
||||
cum.append(ssum)
|
||||
for i in idx_list:
|
||||
r = random.random()
|
||||
j = bisect.bisect_left(cum, r)
|
||||
sp = species_list[j]
|
||||
if sp is None:
|
||||
continue
|
||||
new_species.append(sp)
|
||||
new_fracs.append(sc[i].frac_coords)
|
||||
|
||||
ordered_sc = Structure(new_lat, new_species, new_fracs, to_unit_cell=True, coords_are_cartesian=False)
|
||||
# 去除可能残留的氧化态(LAMMPS atomic 不需要)
|
||||
try:
|
||||
ordered_sc.remove_oxidation_states()
|
||||
except Exception:
|
||||
pass
|
||||
return ordered_sc
|
||||
|
||||
# 1) 读取 CIF(含部分占位)
|
||||
s_in = Structure.from_file(cif_filename, primitive=False)
|
||||
print(f"读入: {cif_filename}, 原胞位点: {s_in.num_sites}, 有序?: {s_in.is_ordered}")
|
||||
|
||||
# 2) 在 2×2×4 超胞上固化部分占位 -> 完全有序超胞
|
||||
ordered_sc = collapse_disorder_to_ordered_supercell(s_in, supercell_matrix, strict=strict_count)
|
||||
print(f"生成有序超胞: 位点数={ordered_sc.num_sites}, 有序?: {ordered_sc.is_ordered}")
|
||||
|
||||
# 3) 打印元素计数,核对化学计量
|
||||
elem_count = Counter([sp.symbol for sp in ordered_sc.species])
|
||||
print("元素计数:", dict(elem_count))
|
||||
|
||||
# 4) 写 LAMMPS raw(pymatgen,失败则 ASE 兜底)
|
||||
wrote = False
|
||||
try:
|
||||
ldata = LammpsData.from_structure(ordered_sc, atom_style="atomic")
|
||||
ldata.write_file(out_lammps)
|
||||
wrote = True
|
||||
print(f"已写出 LAMMPS raw: {out_lammps} (pymatgen)")
|
||||
except Exception as e:
|
||||
print("pymatgen 写 LAMMPS raw 失败:", e)
|
||||
|
||||
if not wrote and HAS_ASE:
|
||||
try:
|
||||
ase_atoms = ASEAtoms(
|
||||
symbols=[sp.symbol for sp in ordered_sc.species],
|
||||
positions=ordered_sc.cart_coords,
|
||||
cell=ordered_sc.lattice.matrix,
|
||||
pbc=True
|
||||
)
|
||||
ase_write(out_lammps, ase_atoms, format="lammps-raw", atom_style="atomic")
|
||||
wrote = True
|
||||
print(f"已写出 LAMMPS raw: {out_lammps} (ASE)")
|
||||
except Exception as e:
|
||||
print("ASE 写 LAMMPS raw 也失败:", e)
|
||||
|
||||
if not wrote:
|
||||
print("写 LAMMPS raw 失败,请把错误信息发我。")
|
||||
Reference in New Issue
Block a user