151 lines
5.7 KiB
Python
151 lines
5.7 KiB
Python
import random
|
||
from typing import List
|
||
from pymatgen.core import Structure
|
||
from pymatgen.io.vasp import Poscar
|
||
|
||
def _is_close_frac(z, target, tol=2e-2):
|
||
t = target % 1.0
|
||
return min(abs(z - t), abs(z - (t + 1)), abs(z - (t - 1))) < tol
|
||
|
||
def make_model3_poscar_from_cif(cif_path: str,
|
||
out_poscar: str = "POSCAR_model3_supercell",
|
||
seed: int = 42,
|
||
tol: float = 2e-2):
|
||
"""
|
||
将 model3.cif 扩胞为 [[3,0,0],[2,4,0],[0,0,6]] 的2160原子超胞,并把部分占据位点(Y2=0.75, Y3=0.25, Li2=0.5)
|
||
显式有序化后写出 POSCAR。
|
||
"""
|
||
random.seed(seed)
|
||
|
||
# 1) 读取 CIF
|
||
s = Structure.from_file(cif_path)
|
||
|
||
# 2) 扩胞(a_s=3a0, b_s=2a0+4b0, c_s=6c0)[1]
|
||
T = [[3, 0, 0],
|
||
[2, 4, 0],
|
||
[0, 0, 6]]
|
||
s.make_supercell(T)
|
||
|
||
# 3) 识别三类需取整的位点:Y2、Y3、Li2
|
||
y2_idx: List[int] = []
|
||
y3_idx: List[int] = []
|
||
li2_idx: List[int] = []
|
||
|
||
for i, site in enumerate(s.sites):
|
||
# 兼容不同版本pymatgen
|
||
try:
|
||
el = site.species.elements[0].symbol
|
||
except Exception:
|
||
ss = site.species_string
|
||
el = "Li" if ss.startswith("Li") else ("Y" if ss.startswith("Y") else ("Cl" if ss.startswith("Cl") else ss))
|
||
z = site.frac_coords[2]
|
||
if el == "Y":
|
||
if _is_close_frac(z, 0.488, tol):
|
||
y2_idx.append(i)
|
||
elif _is_close_frac(z, -0.065, tol) or _is_close_frac(z, 0.935, tol):
|
||
y3_idx.append(i)
|
||
elif el == "Li":
|
||
if _is_close_frac(z, 0.5, tol):
|
||
li2_idx.append(i)
|
||
|
||
def choose_keep(idxs, frac_keep):
|
||
n = len(idxs)
|
||
k = int(round(n * frac_keep))
|
||
if k < 0: k = 0
|
||
if k > n: k = n
|
||
keep = set(random.sample(idxs, k)) if 0 < k < n else set(idxs if k == n else [])
|
||
drop = [i for i in idxs if i not in keep]
|
||
return keep, drop
|
||
|
||
keep_y2, drop_y2 = choose_keep(y2_idx, 0.75)
|
||
keep_y3, drop_y3 = choose_keep(y3_idx, 0.25)
|
||
keep_li2, drop_li2 = choose_keep(li2_idx, 0.50)
|
||
|
||
# 4) 保留者占据设为1,其余删除
|
||
for i in keep_y2 | keep_y3:
|
||
s.replace(i, "Y")
|
||
for i in keep_li2:
|
||
s.replace(i, "Li")
|
||
to_remove = sorted(drop_y2 + drop_y3 + drop_li2, reverse=True)
|
||
for i in to_remove:
|
||
s.remove_sites([i])
|
||
|
||
# 5) 最终清理:消除任何残留的部分占据(防止 POSCAR 写出报错)
|
||
# 若有 site.is_ordered==False,则取该站位的“主要元素”替换为占据=1
|
||
for i, site in enumerate(s.sites):
|
||
if not site.is_ordered:
|
||
d = site.species.as_dict() # {'Li': 0.5} 或 {'Li':0.5,'Y':0.5}
|
||
elem = max(d.items(), key=lambda kv: kv[1])[0]
|
||
s.replace(i, elem)
|
||
|
||
# 6) 排序并写出 POSCAR
|
||
order = {"Li": 0, "Y": 1, "Cl": 2}
|
||
s = s.get_sorted_structure(key=lambda site: order.get(site.species.elements[0].symbol, 99))
|
||
Poscar(s).write_file(out_poscar)
|
||
|
||
# 报告
|
||
comp = {k: int(v) for k, v in s.composition.as_dict().items()}
|
||
print(f"写出 {out_poscar};总原子数 = {len(s)}")
|
||
print(f"Y2识别={len(y2_idx)},Y3识别={len(y3_idx)},Li2识别={len(li2_idx)};组成={comp}")
|
||
|
||
import random
|
||
from typing import List
|
||
from pymatgen.core import Structure
|
||
from pymatgen.io.vasp import Poscar
|
||
|
||
def make_pnma_poscar_from_cif(cif_path: str,
|
||
out_poscar: str = "POSCAR_pnma_supercell",
|
||
seed: int = 42,
|
||
supercell=(3,3,6),
|
||
tol: float = 1e-6):
|
||
"""
|
||
读取 Pnma 的 CIF(如 origin.cif),扩胞到 2160 原子,并把部分占据的 Li 位点(0.75)显式取整后写出 POSCAR。
|
||
默认超胞尺度为(3,3,6),体积放大因子=54,40原子/原胞×54=2160 [1][3]。
|
||
"""
|
||
random.seed(seed)
|
||
|
||
s = Structure.from_file(cif_path)
|
||
|
||
# 扩胞;Pnma原胞已是正交,直接用对角放缩
|
||
s.make_supercell(supercell)
|
||
|
||
# 找出所有“部分占据的 Li”位点
|
||
partial_li_idx: List[int] = []
|
||
for i, site in enumerate(s.sites):
|
||
if not site.is_ordered:
|
||
d = site.species.as_dict() # 例如 {'Li': 0.75}
|
||
# 只处理主要元素是Li且占据<1的位点
|
||
m_elem, m_occ = max(d.items(), key=lambda kv: kv[1])
|
||
if m_elem == "Li" and m_occ < 1 - tol:
|
||
partial_li_idx.append(i)
|
||
|
||
# 以占据0.75进行随机取整:保留75%,其余删除为“空位”
|
||
n = len(partial_li_idx)
|
||
k = int(round(n * 0.75))
|
||
keep = set(random.sample(partial_li_idx, k)) if 0 < k < n else set(partial_li_idx if k == n else [])
|
||
drop = sorted([i for i in partial_li_idx if i not in keep], reverse=True)
|
||
|
||
# 保留者设为占据=1;删除其余
|
||
for i in keep:
|
||
s.replace(i, "Li")
|
||
for i in drop:
|
||
s.remove_sites([i])
|
||
|
||
# 兜底:若仍有部分占据,强制取主要元素
|
||
for i, site in enumerate(s.sites):
|
||
if not site.is_ordered:
|
||
d = site.species.as_dict()
|
||
elem = max(d.items(), key=lambda kv: kv[1])[0]
|
||
s.replace(i, elem)
|
||
|
||
# 排序并写POSCAR
|
||
order = {"Li": 0, "Y": 1, "Cl": 2}
|
||
s = s.get_sorted_structure(key=lambda site: order.get(site.species.elements[0].symbol, 99))
|
||
Poscar(s).write_file(out_poscar)
|
||
|
||
comp = {k: int(v) for k, v in s.composition.as_dict().items()}
|
||
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) |