vasp虚频计算-python脚本

写在前面:为了减少自己的工作量,也为了防止遗忘,便写了这个脚本

脚本使用方法:把vib.py文件复制到TS路径下,记事本打开vib.py脚本,修改TS的路径和需要计算虚频的插点文件,如02,保存关闭。在该TS路径下,命令行输入python3 vib.py 回车,根据程序提示,输入需要固定的原子种类和数量,固定规则为,由指定原子从后往前固定,输入完毕回车。脚本会自行在当前文件夹下创建VIB文件夹,并准备vasp计算所需要的其他文件。

脚本如下,自行取用:

python 复制代码
import os
import shutil
import subprocess

first_path = ["/fs2/home/niexiaowa/wl/FeC-CO-ML/Fe5C2/f10-1/CO2=CO+O/TS/"]
for i in first_path:

    # ====================== 用户配置区 ======================
    contcar_dir = i + "02"
    # 2. 外部文件路径:sub.sh KPOINTS POTCAR
    path_subsh = i + "sub.sh"
    path_kpoints = i + "KPOINTS"
    path_potcar = i + "POTCAR"

    incar_content = """
    SYSTEM = vib
    
    Initial Setting :
    ISTART = 0        
    ICHARG = 2     
    NCORE  = 8
    LWAVE  = .FALSE. 
    LCHARG = .FALSE.
    LVTOT  = .FALSE.
    LMAXMIX = 4
    
    Electronic Relaxation :
    ENCUT  = 500                 
    EDIFF  = 1E-05  
    ISMEAR = 0        
    SIGMA  = 0.05
    GGA    = PE       
    PREC   = Normal  
    ALGO   = Fast 
    LREAL  = Auto 
    
    Ionic Relaxation :
    EDIFFG = -0.01 
    NSW    = 1
    POTIM  = 0.015
    NFREE  = 2
    IBRION = 5
    ISYM   = 0  
    ISIF   = 2
    IVDW   = 11
    ISPIN  = 2
    #MAGMOM = 10*0 26*0 36*0 4*0 1*5 1*0
    """

    # 集群提交命令:slurm填sbatch;PBS填qsub
    submit_cmd = "sbatch"
    #submit_cmd = "qsub"
    # =======================================================


    def parse_contcar(filepath: str):
        """解析CONTCAR,原样读取所有行,区分是否带Selective Dynamics"""
        with open(filepath, "r", encoding="utf-8") as f:
            lines = [line.rstrip("\n") for line in f]
        ptr = 0
        title = lines[ptr].strip()
        ptr += 1
        scale = float(lines[ptr])
        ptr += 1
        cell = []
        for _ in range(3):
            cell.append([float(x) for x in lines[ptr].split()])
            ptr += 1
        elem_list = lines[ptr].split()
        ptr += 1
        natom_list = [int(x) for x in lines[ptr].split()]
        ptr += 1

        selective_flag = False
        if lines[ptr].strip().lower().startswith("s"):
            selective_flag = True
            ptr += 1
        coord_mode = lines[ptr].strip()
        ptr += 1

        natom_total = sum(natom_list)
        coord_raw_lines = []
        for _ in range(natom_total):
            coord_raw_lines.append(lines[ptr])
            ptr += 1

        return {
            "title": title,
            "scale": scale,
            "cell": cell,
            "elem": elem_list,
            "natoms": natom_list,
            "has_selective": selective_flag,
            "coord_mode": coord_mode,
            "coord_raw": coord_raw_lines
        }


    def parse_interactive_input(input_str: str):
        """交互输入解析,示例 "Fe:3 O:0 C:0" """
        free_spec = {}
        parts = input_str.strip().split()
        for p in parts:
            elem, num = p.split(":")
            free_spec[elem.strip()] = int(num.strip())
        return free_spec


    def generate_poscar_selective(contcar_data, free_spec: dict):
        """
        has_selective=True:原始每行 x y z T1 T2 T3,直接覆盖后三列,不增加字段,保持总共6列
        has_selective=False:追加T/F三列,同时写入Selective Dynamics标记
        同一元素:末尾N个原子 T T T;其余 F F F
        """
        elem_names = contcar_data["elem"]
        elem_counts = contcar_data["natoms"]
        raw_coord_lines = contcar_data["coord_raw"]

        atom_elem_list = []
        for elem, cnt in zip(elem_names, elem_counts):
            atom_elem_list.extend([elem] * cnt)

        sd_target = []
        log_info = []
        idx_global = 0
        for elem, cnt in zip(elem_names, elem_counts):
            n_free = free_spec.get(elem, 0)
            if n_free > cnt:
                raise ValueError(f"元素 {elem}: 设置自由数目{n_free} > 该元素总原子数 {cnt}")
            for i_in_type in range(cnt):
                idx_global += 1
                is_free = i_in_type >= (cnt - n_free)
                if is_free:
                    sd_target.append("T T T")
                    log_info.append(f"Atom{idx_global:4d}  {elem:4s}  -> Free(T T T)")
                else:
                    sd_target.append("F F F")
                    log_info.append(f"Atom{idx_global:4d}  {elem:4s}  -> Fix(F F F)")

        out_lines = []
        out_lines.append(contcar_data["title"])
        out_lines.append(f"{contcar_data['scale']:.10f}")
        for row in contcar_data["cell"]:
            out_lines.append(f"{row[0]:12.8f} {row[1]:12.8f} {row[2]:12.8f}")
        out_lines.append(" ".join(elem_names))
        out_lines.append(" ".join(map(str, elem_counts)))

        if contcar_data["has_selective"]:
            out_lines.append("Selective Dynamics")
        out_lines.append(contcar_data["coord_mode"])

        for raw_line, new_sd in zip(raw_coord_lines, sd_target):
            tokens = raw_line.strip().split()
            xyz = tokens[0:3]
            final_tokens = xyz + new_sd.split()
            out_lines.append(" ".join(final_tokens))

        return "\n".join(out_lines), log_info


    def main():
        contcar_path = os.path.join(contcar_dir, "CONTCAR")
        if not os.path.exists(contcar_path):
            raise FileNotFoundError(f"找不到CONTCAR: {contcar_path}")

        for fpath, fname in zip([path_subsh, path_kpoints, path_potcar], ["sub.sh", "KPOINTS", "POTCAR"]):
            if not os.path.exists(fpath):
                raise FileNotFoundError(f"缺失文件 {fname}: {fpath}")

        print("=" * 60)
        print("VASP IBRION=5 振动输入生成工具")
        print("示例输入: Fe:3 O:0 C:0")
        print("含义:Fe最后3个原子自由,O、C全部固定;未写元素默认全部固定")
        user_input = input("请输入自由原子设置:")
        free_spec = parse_interactive_input(user_input)
        print(f"解析得到设置:{free_spec}")
        print("=" * 60)

        contcar_abs = os.path.abspath(contcar_dir)
        parent_dir = os.path.dirname(contcar_abs)
        vib_dir = os.path.join(parent_dir, "VIB")

        if os.path.exists(vib_dir):
            shutil.rmtree(vib_dir)
        os.makedirs(vib_dir, exist_ok=True)
        print(f"[INFO] 创建振动计算目录:{vib_dir}")

        contcar_data = parse_contcar(contcar_path)
        poscar_text, atom_log = generate_poscar_selective(contcar_data, free_spec)

        print("\n========== 原子冻结状态 ==========")
        for line in atom_log:
            print(line)
        print("==================================\n")

        #保存原子状态日志
        log_file = os.path.join(vib_dir,"atom_status.log")
        with open(log_file,"w",encoding="utf-8") as f:
            f.write("\n".join(atom_log))

        poscar_out = os.path.join(vib_dir, "POSCAR")
        with open(poscar_out, "w", encoding="utf-8") as f:
            f.write(poscar_text)

        incar_out = os.path.join(vib_dir, "INCAR")
        with open(incar_out, "w", encoding="utf-8") as f:
            f.write(incar_content.lstrip("\n"))

        shutil.copy(path_subsh, os.path.join(vib_dir, "sub.sh"))
        shutil.copy(path_kpoints, os.path.join(vib_dir, "KPOINTS"))
        shutil.copy(path_potcar, os.path.join(vib_dir, "POTCAR"))
        print("[INFO] POSCAR INCAR KPOINTS POTCAR sub.sh atom_status.log 全部写入完成")

        print(f"[INFO] 使用 {submit_cmd} 提交作业")
        ret = subprocess.run(
            [submit_cmd, "sub.sh"],
            cwd=vib_dir,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            universal_newlines=True
        )
        if ret.returncode == 0:
            print(f"[SUCCESS]任务提交成功:\n{ret.stdout}")
        else:
            print(f"[ERROR] 提交失败\nstdout:\n{ret.stdout}\nstderr:\n{ret.stderr}")
            raise subprocess.CalledProcessError(ret.returncode, cmd=submit_cmd)


if __name__ == "__main__":
    main()
相关推荐
数字化转型分享点滴33 分钟前
富士康、蓝思科技为何选择四化信息?MES制造执行系统的实践
python·科技
魔镜前的帅比36 分钟前
(开源项目)x-claw(总)
python·ai·rust·开源
郝学胜-神的一滴1 小时前
Qt 高级编程 040:按钮悬浮弹出滑块弹窗的完整攻略
开发语言·c++·qt·软件工程·用户界面
xrandzj2 小时前
Python面向对象编程入门:类、实例、初始化与封装实践
开发语言·python
DLYSB_3 小时前
API 网关流量洪峰与突发 CC 攻击:我用 Go 写了个“现场物理防御哨兵”,把故障响应压缩到秒级
开发语言·后端·golang·报警灯
matlabgoodboy4 小时前
计算机毕设代做|Java Python Matlab APP 全套开发设计
java·python·课程设计
雨田言炎4 小时前
四、关于Qt项目需要知道的
开发语言·笔记·qt
用户8356290780514 小时前
Python Word 转 PDF 和 PDF 转 Word 指南
后端·python
用户8356290780515 小时前
如何使用 Python 加密和保护 Word 文档
后端·python