本脚本能够从单个 POSCAR 出发,自动完成常压优化、压力点序列生成、结构迭代、能带与声子谱后处理的全流程。实现了从结构对称性分析到计算结果后处理的完整闭环,适用于需要批量处理高压计算任务的体系。

需提起配置好VASPKIT与PHONOPY环境。

本文将详细介绍该脚本的设计思路、使用方法与注意事项,

 

 

 

 

准备文件:仅需准备POSCAR

操作流程

 

$ sh db-4.sh

[INFO] 2026-03-18 01:03:25 ===== 基础文件检查 =====

[INFO] 2026-03-18 01:03:25 校验K点设置...

[INFO] 2026-03-18 01:03:25 ✅ K点设置合理:444 0 0

[INFO] 2026-03-18 01:03:25 ===== INCAR参数配置 =====

是否设置磁性?(y/n,默认n):n

是否取消对称性?(y/n,默认n):n

是否增加截断能?(y/n,默认n):n

自定义优化精度?(y/n,默认n,格式:1E-08;-2E-02):y

输入EDIFF;EDIFFG:1E-05;-2E-01

添加NCORE?(y/n,默认n):y

NCORE值:4

添加范德华修正?(y/n,默认n):n

VASP运行命令(示例:mpirun -np 32 vasp_std,默认:mpirun -np 16 vasp_std):mpirun -np 32 vasp_std-w

[INFO] 2026-03-18 01:04:05 ✅ VASP命令:mpirun -np 32 vasp_std-w

[INFO] 2026-03-18 01:04:05 ===== 初始结构设置 =====

是否跳过常压(0GPa)优化?(y/n,默认n):n

[INFO] 2026-03-18 01:04:18 ===== 常压(0GPa)结构优化 =====

[INFO] 2026-03-18 01:04:18 ✅ 初始空间群:International: Fm-3m

[INFO] 2026-03-18 01:04:18 第1次常压优化...

 

 

图片
图片
图片

完成初次常压结构优化(或可指定初始结构)后,可设置压力梯度与计算要求。

 

[INFO] 2026-03-18 00:51:07 ✅ 已选择已有CONTCAR作为初始输入:CONTCAR

[INFO] 2026-03-18 00:51:07 ✅ 初始空间群(基于已有CONTCAR):International: Fm-3m

[INFO] 2026-03-18 00:51:07 ===== 压力梯度设置 =====

压力输入类型:

1) 特殊压力点(如:1 5 10 GPa)

2) 常压至最大值(如:0-80GPa,自动步长)

3) 分段压力(如:0-30,70-200GPa)

选择类型(1/2/3,默认1):1

输入压力点(空格分隔):1 55

[INFO] 2026-03-18 00:51:32 最终压力列表(从小到大):1 55 GPa

[INFO] 2026-03-18 00:51:32 ===== 物性计算设置 =====

是否计算能带?(y/n,默认n):y

指定能带INCAR?(y/n,默认n):

是否使用原胞进行能带计算?(y/n,默认n):y

[INFO] 2026-03-18 00:51:40 ✅ 启用原胞进行能带计算

[INFO] 2026-03-18 00:51:40 生成KPATH.in(vaspkit -task 303)

 +---------------------------------------------------------------+

 |         VASPKIT Standard Edition 1.5.1 (27 Jan. 2024)         |

 |            Running VASPKIT Under Command-Line Mode            |

 +---------------------------------------------------------------+

 +-------------------------- Summary ----------------------------+

                           Prototype: AB

           Total Atoms in Input Cell:   8

     Lattice Constants in Input Cell:   6.411   6.411   6.411

        Lattice Angles in Input Cell:  90.000  90.000  90.000

       Total Atoms in Primitive Cell:   2

 Lattice Constants in Primitive Cell:   4.534   4.534   4.534

    Lattice Angles in Primitive Cell:  60.000  60.000  60.000

                      Crystal System: Cubic

                       Crystal Class: m-3m

                     Bravais Lattice: cF

            Extended Bravais Lattice: cF2

                  Space Group Number: 225

                         Point Group: 32 [ Oh ]

                       International: Fm-3m

                 Symmetry Operations: 192

                    Suggested K-Path: (shown in the next line)

 [ GAMMA-X-U|K-GAMMA-L-W-X ]

 +---------------------------------------------------------------+

 -->> (01) Written HIGH_SYMMETRY_POINTS File for Reference.

 -->> (02) Written PRIMCELL.vasp file.

 -->> (03) Written KPATH.in File for Band-Structure Calculation.

 o----------------------------WARNING----------------------------o

 | Do NOT forget to copy PRIMCELL.vasp to POSCAR unless you know |

 |   what you are doing. Otherwise you might get wrong results!  |

 o---------------------------------------------------------------o

 +---------------------------- Tip ------------------------------+

 | Make sure you have installed python & matplotlib successfully.|

 | The plot utility is under development in the preview release. |

 |  You CAN custom plot parameters in PLOT.in file if it exists, |

 |   or in ~/.vaspkit file if the PLOT.in file does NOT found.   |

 +---------------------------------------------------------------+

 -->> (04) Reading Plot Setting from the PLOT.in File.

findfont: Font family 'arial' not found.

fin

 -->> (05) Written Brillouin_Zone_3D.jpg File.

 |---------------------------------------------------------------|

 |                          * CITATIONS *                        |

 | When using VASPKIT in your research PLEASE cite the paper:    |

 | [1] V. WANG, N. XU, J.-C. LIU, G. TANG, W.-T. GENG, VASPKIT: A|

 | User-Friendly Interface Facilitating High-Throughput Computing|

 | and Analysis Using VASP Code, Computer Physics Communications |

 | 267, 108033, (2021), DOI: 10.1016/j.cpc.2021.108033           |

 o---------------------------------------------------------------o

[INFO] 2026-03-18 00:51:43 当前KPATH.in内容:

K-Path Generated by VASPKIT.

   20

Line-Mode

Reciprocal

   0.0000000000   0.0000000000   0.0000000000     GAMMA

   0.5000000000   0.0000000000   0.5000000000     X

 

   0.5000000000   0.0000000000   0.5000000000     X

   0.6250000000   0.2500000000   0.6250000000     U

 

   0.3750000000   0.3750000000   0.7500000000     K

   0.0000000000   0.0000000000   0.0000000000     GAMMA

 

   0.0000000000   0.0000000000   0.0000000000     GAMMA

   0.5000000000   0.5000000000   0.5000000000     L

 

   0.5000000000   0.5000000000   0.5000000000     L

   0.5000000000   0.2500000000   0.7500000000     W

 

   0.5000000000   0.2500000000   0.7500000000     W

   0.5000000000   0.0000000000   0.5000000000     X

 

确认KPATH.in是否合适?(y/n,默认y):y

是否计算声子谱?(y/n,默认n):y

指定声子INCAR?(y/n,默认n):

[INFO] 2026-03-18 00:51:47 ===== 开始压力循环计算 =====

[INFO] 2026-03-18 00:51:47 ===== 处理压力点:1 GPa(文件夹:P1GPa) =====

[INFO] 2026-03-18 00:51:47 ✅ 设置PSTRESS=10 kB(1 GPa)

[INFO] 2026-03-18 00:51:47 第1次优化1 GPa结构...

 

 

 

 

 

 

 

 

完整脚本附:

 

#!/bin/bash
set -uo pipefail

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'

# 日志函数
log() { echo -e "${BLUE}[INFO] $(date +%Y-%m-%d\ %H:%M:%S) $1${NC}"; }
error() { echo -e "${RED}[ERROR] $(date +%Y-%m-%d\ %H:%M:%S) $1${NC}"; exit 1; }
warning() { echo -e "${YELLOW}[WARNING] $(date +%Y-%m-%d\ %H:%M:%S) $1${NC}"; }

# 检查命令是否存在
check_command() {
    if ! command -v "$1" &> /dev/null; then
        error "命令$1未找到,请先安装并配置环境变量!"
    fi
}

# 检查文件是否为非空白
check_non_empty_file() {
    local file=$1
    if [ ! -f "$file" ] || [ ! -s "$file" ]; then
        return 1
    fi
    return 0
}

# 核心函数:提取空间群
get_space_group() {
    local poscar_file=$1
    if ! check_non_empty_file "$poscar_file"; then
        echo "UNKNOWN"
        return
    fi
    # 临时复制POSCAR到当前目录执行601
    cp "$poscar_file" TMP_POSCAR
    mv POSCAR POSCAR_BAK 2>/dev/null
    mv TMP_POSCAR POSCAR
    # 执行601并提取International空间群
    space_group=$(vaspkit -task 601 2>&1 | grep "International:" | awk '{print $2}')
    # 恢复原POSCAR
    mv POSCAR_BAK POSCAR 2>/dev/null
    rm -f TMP_POSCAR
    [ -z "$space_group" ] && space_group="UNKNOWN"
    echo "$space_group"
}

# ===================== 第一步:基础文件检查与修复 =====================
log "===== 基础文件检查 ====="



# 2. 自动生成KPOINTS(缺失/过小)
if ! check_non_empty_file "KPOINTS"; then
    warning "未找到有效KPOINTS,自动生成..."
    (echo 102; echo 2; echo 0.04) | vaspkit
fi

# 3. 修正K点校验(核心:提取第4行)
log "校验K点设置..."
kmesh_line=$(sed -n '4p' KPOINTS | awk '{print $1, $2, $3}' | tr -d ' ' | awk '{for(i=1;i<=3;i++) printf "%.0f ", $i}')
k1=$(echo $kmesh_line | awk '{print $1}')
k2=$(echo $kmesh_line | awk '{print $2}')
k3=$(echo $kmesh_line | awk '{print $3}')

if [ "$k1" -eq 1 ] && [ "$k2" -eq 1 ] && [ "$k3" -eq 1 ]; then
    warning "K点过小(1x1x1),自动更新..."
    (echo 102; echo 2; echo 0.04) | vaspkit
    kmesh_line=$(sed -n '4p' KPOINTS | awk '{print $1, $2, $3}')
fi
log "✅ K点设置合理:$kmesh_line"
# 1. 检查核心文件(仅检查INCAR/POTCAR,POSCAR可选,因为可能用已有CONTCAR)
if ! check_non_empty_file "INCAR"; then error "INCAR文件不存在或为空!"; fi
if ! check_non_empty_file "POTCAR"; then error "POTCAR文件不存在或为空(需手动从VASP官网获取)!"; fi
# ===================== 第二步:INCAR参数配置 =====================
log "===== INCAR参数配置 ====="
cp INCAR INCAR.bak

# 1. 磁性设置
read -p "是否设置磁性?(y/n,默认n):" is_mag; is_mag=${is_mag:-n}
sed -i '/ISPIN/d' INCAR
echo "ISPIN  =  $( [ "$is_mag" = "y" ] && echo 2 || echo 1 )" >> INCAR
[ "$is_mag" = "y" ] && warning "已设ISPIN=2,请手动添加MAGMOM!"

# 2. 对称性设置
read -p "是否取消对称性?(y/n,默认n):" is_sym_off; is_sym_off=${is_sym_off:-n}
sed -i '/ISYM/d' INCAR
echo "ISYM =  $( [ "$is_sym_off" = "y" ] && echo 0 || echo 2 )" >> INCAR

# 3. 截断能
read -p "是否增加截断能?(y/n,默认n):" is_encut; is_encut=${is_encut:-n}
sed -i '/ENCUT/d' INCAR
if [ "$is_encut" = "y" ]; then
    while true; do
        read -p "输入截断能(>400整数):" encut;
        if [[ "$encut" =~ ^[0-9]+$ ]] && [ "$encut" -gt 400 ]; then break; fi
        warning "输入无效!"
    done
else
    encut=400
fi
echo "ENCUT  =  $encut" >> INCAR

# 4. 优化精度
read -p "自定义优化精度?(y/n,默认n,格式:1E-08;-2E-02):" is_ediff; is_ediff=${is_ediff:-n}
sed -i '/EDIFF/d' INCAR; sed -i '/EDIFFG/d' INCAR
if [ "$is_ediff" = "y" ]; then
    read -p "输入EDIFF;EDIFFG:" ediff_vals
    ediff=$(echo $ediff_vals | cut -d';' -f1)
    ediffg=$(echo $ediff_vals | cut -d';' -f2)
else
    ediff=1E-05; ediffg=-2E-01
fi
echo "EDIFF  =  $ediff" >> INCAR; echo "EDIFFG =  $ediffg" >> INCAR

# 5. NCORE/范德华修正(可选跳过)
read -p "添加NCORE?(y/n,默认n):" is_ncore; is_ncore=${is_ncore:-n}
[ "$is_ncore" = "y" ] && read -p "NCORE值:" ncore && sed -i '/NCORE/d' INCAR && echo "NCORE  =  $ncore" >> INCAR

read -p "添加范德华修正?(y/n,默认n):" is_vdw; is_vdw=${is_vdw:-n}
[ "$is_vdw" = "y" ] && read -p "IVDW值(1/2/10等):" ivdw && sed -i '/IVDW/d' INCAR && echo "IVDW  =  $ivdw" >> INCAR

# 6. 强制参数
sed -i '/ISTART/s/^/#/' INCAR; sed -i '/ICHARG/s/^/#/' INCAR
sed -i '/LWAVE/d' INCAR; sed -i '/LCHARG/d' INCAR
echo "LWAVE  = .FALSE." >> INCAR; echo "LCHARG = .FALSE." >> INCAR
sed -i '/NSW/d' INCAR; sed -i '/IBRION/d' INCAR; sed -i '/ISIF/d' INCAR
echo "NSW    =  100" >> INCAR; echo "IBRION =  2" >> INCAR; echo "ISIF   =  3" >> INCAR

# ===================== 第三步:VASP运行命令 =====================
read -p "VASP运行命令(示例:mpirun -np 32 vasp_std,默认:mpirun -np 16 vasp_std):" vasp_cmd
vasp_cmd=${vasp_cmd:-"mpirun -np 16 vasp_std"}
log "✅ VASP命令:$vasp_cmd"

# ===================== 第四步:初始CONTCAR设置(核心:支持跳过常压优化) =====================
log "===== 初始结构设置 ====="
current_contcar=""
read -p "是否跳过常压(0GPa)优化?(y/n,默认n):" skip_0gpa; skip_0gpa=${skip_0gpa:-n}

if [ "$skip_0gpa" = "y" ]; then
    # 跳过常压优化,询问已有CONTCAR路径
    while true; do
        read -p "请输入已有优化结果的CONTCAR文件路径(如:./CONTCAR.0GPa 或 ./P5GPa/CONTCAR.5GPa):" contcar_path
        if check_non_empty_file "$contcar_path"; then
            current_contcar="$contcar_path"
            # 备份初始CONTCAR
            cp "$current_contcar" "CONTCAR.initial"
            current_contcar="CONTCAR.initial"
            log "✅ 已选择已有CONTCAR作为初始输入:$contcar_path"
            # 提取初始空间群
            initial_space_group=$(get_space_group "$current_contcar")
            log "✅ 初始空间群(基于已有CONTCAR):International: $initial_space_group"
            break
        else
            warning "指定的CONTCAR文件不存在或为空,请重新输入!"
        fi
    done
else
    # 执行常压(0GPa)优化
    log "===== 常压(0GPa)结构优化 ====="
    # 检查POSCAR(常压优化必须有)
    if ! check_non_empty_file "POSCAR"; then
        error "常压优化需要有效POSCAR文件!"
    fi
    # 提取初始空间群
    initial_space_group=$(get_space_group "POSCAR")
    log "✅ 初始空间群:International: $initial_space_group"
    
    # 压力设置(0GPa)
    sed -i '/PSTRESS/d' INCAR; echo "PSTRESS =  0" >> INCAR
    
    optimization_done=false
    max_attempts=5; attempt=0

    while [ $attempt -lt $max_attempts ] && [ "$optimization_done" = false ]; do
        attempt=$((attempt + 1))
        log "第$attempt次常压优化..."
        log_file="vasp_0GPa_$attempt.log"
        
        if ! $vasp_cmd > "$log_file" 2>&1; then
            warning "VASP运行失败,日志:$log_file"
            read -p "重试?(y/n,默认n):" retry; retry=${retry:-n}
            [ "$retry" = "n" ] && error "用户终止!"
            continue
        fi

        # 检查CONTCAR是否有效(核心:不管收敛与否,优先用CONTCAR)
        if check_non_empty_file "CONTCAR"; then
            cp CONTCAR "CONTCAR.0GPa"  # 备份0GPa的CONTCAR
            current_contcar="CONTCAR.0GPa"
            log "✅ 生成有效CONTCAR(0GPa),强制使用该文件作为后续输入"
            optimization_done=true
        else
            warning "未生成有效CONTCAR,重试..."
            continue
        fi

        # 可选:检查收敛条件(仅日志提示,不影响CONTCAR使用)
        ionic_steps=$(grep -E '^ [0-9]+ F=' "$log_file" | wc -l)
        reached=$(grep -c "reached required accuracy" "$log_file")
        if [ "$ionic_steps" -lt 3 ] && [ "$reached" -ge 1 ]; then
            log "✅ 常压优化收敛(离子步数:$ionic_steps)"
        else
            warning "常压优化未收敛,但仍使用CONTCAR作为后续输入"
        fi
    done

    [ "$optimization_done" = false ] && error "常压优化未生成有效CONTCAR!"
fi

# ===================== 第五步:压力梯度设置(排序+校验) =====================
log "===== 压力梯度设置 ====="
echo "压力输入类型:"
echo "1) 特殊压力点(如:1 5 10 GPa)"
echo "2) 常压至最大值(如:0-80GPa,自动步长)"
echo "3) 分段压力(如:0-30,70-200GPa)"
read -p "选择类型(1/2/3,默认1):" p_type; p_type=${p_type:-1}

pressure_list=()
case $p_type in
    1)
        read -p "输入压力点(空格分隔):" p_points
        pressure_list=($(echo "$p_points" | tr ' ' '\n' | sort -n | uniq))
        ;;
    2)
        read -p "压力最大值(GPa):" max_p
        # 自动步长:0-10=1, 10-20=2, 20-50=5, 50+=10
        for p in $(seq 0 1 10); do [ "$p" -le "$max_p" ] && pressure_list+=($p); done
        for p in $(seq 12 2 20); do [ "$p" -le "$max_p" ] && pressure_list+=($p); done
        for p in $(seq 25 5 50); do [ "$p" -le "$max_p" ] && pressure_list+=($p); done
        p=60; while [ "$p" -le "$max_p" ]; do pressure_list+=($p); p=$((p+10)); done
        pressure_list=($(echo "${pressure_list[@]}" | tr ' ' '\n' | sort -n | uniq))
        ;;
    3)
        read -p "分段压力(逗号分隔,如0-30,70-200):" p_seg
        read -p "步长(auto/数字,默认auto):" step; step=${step:-auto}
        IFS=',' read -ra segs <<< "$p_seg"
        for seg in "${segs[@]}"; do
            start=$(echo $seg | cut -d'-' -f1 | awk '{print int($1)}')
            end=$(echo $seg | cut -d'-' -f2 | awk '{print int($1)}')
            [ "$step" = "auto" ] && step=$( [ $end -le 10 ] && echo 1 || [ $end -le 20 ] && echo 2 || [ $end -le 50 ] && echo 5 || echo 10 )
            for p in $(seq $start $step $end); do pressure_list+=($p); done
        done
        pressure_list=($(echo "${pressure_list[@]}" | tr ' ' '\n' | sort -n | uniq))
        ;;
esac

# 移除初始压力(如果跳过常压优化,初始压力可能不是0,需手动确认)
if [ "$skip_0gpa" = "n" ]; then
    # 跳过常压优化时,移除0GPa(已计算)
    pressure_list=($(echo "${pressure_list[@]}" | tr ' ' '\n' | grep -v '^0$' | sort -n))
fi
log "最终压力列表(从小到大):${pressure_list[*]} GPa"

# ===================== 第六步:能带/声子谱设置(原胞选项) =====================
log "===== 物性计算设置 ====="
# 能带计算(原胞选项)
read -p "是否计算能带?(y/n,默认n):" do_band; do_band=${do_band:-n}
band_incar=""
use_prim_cell=false
kpath_updated=false
if [ "$do_band" = "y" ]; then
    read -p "指定能带INCAR?(y/n,默认n):" has_band_incar; has_band_incar=${has_band_incar:-n}
    [ "$has_band_incar" = "y" ] && read -p "能带INCAR路径:" band_incar && [ ! -f "$band_incar" ] && error "能带INCAR不存在!"
    
    # 询问是否用原胞计算能带
    read -p "是否使用原胞进行能带计算?(y/n,默认n):" use_prim; use_prim=${use_prim:-n}
    [ "$use_prim" = "y" ] && use_prim_cell=true && log "✅ 启用原胞进行能带计算"
    
    # 生成并输出KPATH.in到终端
    log "生成KPATH.in(vaspkit -task 303)"
    # 临时使用初始CONTCAR生成KPATH.in
    cp "$current_contcar" TMP_POSCAR
    mv POSCAR POSCAR_BAK 2>/dev/null
    mv TMP_POSCAR POSCAR
    vaspkit -task 303
    mv POSCAR_BAK POSCAR 2>/dev/null
    rm -f TMP_POSCAR
    
    log "当前KPATH.in内容:"
    cat KPATH.in
    read -p "确认KPATH.in是否合适?(y/n,默认y):" confirm_kpath; confirm_kpath=${confirm_kpath:-y}
    [ "$confirm_kpath" = "n" ] && read -p "请输入自定义KPATH.in路径:" kpath_file && cp "$kpath_file" KPATH.in
fi

# 声子谱计算
read -p "是否计算声子谱?(y/n,默认n):" do_phono; do_phono=${do_phono:-n}
phono_incar=""
if [ "$do_phono" = "y" ]; then
    check_command "phonopy"
    read -p "指定声子INCAR?(y/n,默认n):" has_phono_incar; has_phono_incar=${has_phono_incar:-n}
    [ "$has_phono_incar" = "y" ] && read -p "声子INCAR路径:" phono_incar && [ ! -f "$phono_incar" ] && error "声子INCAR不存在!"
fi

# ===================== 第七步:压力循环计算(核心:强制用CONTCAR) =====================
log "===== 开始压力循环计算 ====="

for p in "${pressure_list[@]}"; do
    dir="P${p}GPa"
    mkdir -p "$dir"
    log "===== 处理压力点:$p GPa(文件夹:$dir) ====="
    
    # 1. 复制初始/前一个压力点的CONTCAR(核心:强制用CONTCAR)
    if ! check_non_empty_file "$current_contcar"; then
        warning "初始/前一个CONTCAR($current_contcar)无效,请检查!"
        read -p "是否终止计算?(y/n,默认y):" stop; stop=${stop:-y}
        [ "$stop" = "y" ] && error "用户终止计算!" || continue
    fi
    cp "$current_contcar" "$dir/POSCAR"  # 作为当前压力点的输入POSCAR
    cp KPOINTS POTCAR INCAR "$dir/"
    
    # 2. 修改当前压力的PSTRESS
    p_kb=$(echo "$p * 10" | bc)
    sed -i "s/PSTRESS = .*/PSTRESS =  $p_kb/" "$dir/INCAR"
    log "✅ 设置PSTRESS=$p_kb kB($p GPa)"
    
    # 3. 进入文件夹优化结构
    cd "$dir" || { warning "无法进入$dir,跳过"; continue; }
    
    # 结构优化循环(强制用CONTCAR)
    opt_done=false; attempt=0; max_attempts=3
    while [ $attempt -lt $max_attempts ] && [ "$opt_done" = false ]; do
        attempt=$((attempt + 1))
        log "第$attempt次优化$p GPa结构..."
        log_file="vasp_${p}GPa_$attempt.log"
        
        if ! $vasp_cmd > "$log_file" 2>&1; then
            warning "$p GPa第$attempt次VASP运行失败,日志:$log_file"
            continue
        fi
        
        # 核心:检查CONTCAR是否有效(不管收敛与否)
        if check_non_empty_file "CONTCAR"; then
            cp CONTCAR "CONTCAR.${p}GPa"  # 备份当前压力点的CONTCAR
            current_contcar="../$dir/CONTCAR.${p}GPa"  # 更新下一个压力点的输入
            log "✅ $p GPa生成有效CONTCAR,强制使用该文件作为后续输入"
            opt_done=true
            
            # 可选:检查收敛条件(仅日志提示)
            ionic_steps=$(grep -E '^ [0-9]+ F=' "$log_file" | wc -l)
            reached=$(grep -c "reached required accuracy" "$log_file")
            if [ "$ionic_steps" -lt 3 ] && [ "$reached" -ge 1 ]; then
                log "✅ $p GPa优化收敛(离子步数:$ionic_steps)"
            else
                warning "$p GPa优化未收敛,但仍使用CONTCAR作为后续输入"
            fi
            
            # 检查空间群是否变化(仅能带计算时)
            if [ "$do_band" = "y" ] && [ "$kpath_updated" = false ]; then
                current_sg=$(get_space_group "CONTCAR")
                if [ "$current_sg" != "$initial_space_group" ] && [ "$current_sg" != "UNKNOWN" ]; then
                    warning "空间群变化!初始:$initial_space_group → 当前:$current_sg"
                    read -p "是否更新KPATH.in?(y/n,默认n):" update_kpath; update_kpath=${update_kpath:-n}
                    if [ "$update_kpath" = "y" ]; then
                        # 基于当前CONTCAR生成新的KPATH.in
                        vaspkit -task 303
                        cp KPATH.in ../../KPATH.in
                        kpath_updated=true
                        log "✅ 已基于当前结构更新KPATH.in"
                    fi
                fi
            fi
        else
            warning "$p GPa第$attempt次优化未生成有效CONTCAR,重试..."
            continue
        fi
    done
    
    [ "$opt_done" = false ] && { warning "$p GPa优化失败,跳过"; cd ..; continue; }

    # ===================== 能带计算(原胞选项) =====================
    if [ "$do_band" = "y" ]; then
        log "开始$p GPa能带计算..."
        mkdir -p band
        cd band || { warning "无法进入band,跳过"; cd ..; continue; }
        
        # 复制当前压力点的CONTCAR到band目录
        cp ../CONTCAR POSCAR
        
        # 如果使用原胞,先生成PRIMCELL.vasp并替换POSCAR,再生成K点
        if [ "$use_prim_cell" = true ]; then
            log "生成原胞PRIMCELL.vasp(vaspkit -task 602)"
            vaspkit -task 602
            if check_non_empty_file "PRIMCELL.vasp"; then
                mv POSCAR POSCAR_ori  # 备份原结构
                cp PRIMCELL.vasp POSCAR
                log "✅ 已替换为原胞PRIMCELL.vasp作为能带计算输入"
                
                # 重新生成K点(基于原胞)
                log "基于原胞生成新的K点"
                (echo 102; echo 2; echo 0.04) | vaspkit
            else
                warning "生成原胞失败,使用原CONTCAR继续"
            fi
        fi
        
        # 复制其他文件
        cp ../../POTCAR ../../INCAR .
        
        # 处理能带INCAR
        if [ -n "$band_incar" ]; then
            cp "$band_incar" INCAR
        else
            sed -i '/NSW/s/^/#/' INCAR; sed -i '/IBRION/s/^/#/' INCAR
            sed -i '/LWAVE/d' INCAR; sed -i '/LCHARG/d' INCAR
            echo "LWAVE  = .TRUE." >> INCAR; echo "LCHARG = .TRUE." >> INCAR
            echo "LORBIT = 10" >> INCAR; echo "NEDOS  = 5001" >> INCAR
        fi
        
        # 第一步SCF计算
        log "执行第一步SCF自洽计算"
        $vasp_cmd > band_calc1.log 2>&1
        
        # 第二步能带计算(替换KPATH.in)
        cp ../../KPATH.in KPOINTS
        log "执行第二步能带计算"
        $vasp_cmd > band_calc2.log 2>&1
        
        # 数据处理
        log "处理能带数据"
        (echo 211; echo 1)|vaspkit
        (echo 215; echo 1)|vaspkit
        (echo 111; echo 2)|vaspkit
        (echo 211; echo 2)|vaspkit
        (echo 113; echo all;echo 2)|vaspkit
        (echo 213; echo 2;echo all)|vaspkit
        
        cd ..
    fi

    # ===================== 声子谱计算(强制用CONTCAR) =====================
    if [ "$do_phono" = "y" ]; then
        log "开始$p GPa声子谱计算..."
        mkdir -p phono
        cd phono || { warning "无法进入phono,跳过"; cd ..; continue; }
        
        # 复制当前压力点的CONTCAR作为输入
        cp ../CONTCAR POSCAR
        cp ../../POTCAR ../../KPOINTS ../../INCAR .
        
        # 处理声子INCAR
        if [ -n "$phono_incar" ]; then
            cp "$phono_incar" INCAR
        else
            sed -i '/NCORE/s/^/#/' INCAR
            sed -i '/NSW/d' INCAR; sed -i '/IBRION/d' INCAR
            echo "NSW    =  1" >> INCAR
            grep -q "IVDW" INCAR && echo "IBRION =  6" >> INCAR || echo "IBRION =  8" >> INCAR
        fi
        
        # 运行VASP+phonopy
        log "执行声子谱VASP计算"
        $vasp_cmd > phono_vasp.log 2>&1
        
        if check_non_empty_file "vasprun.xml"; then
            # 生成KPATH.phonopy
            cat > KPATH.phonopy << EOF
NPOINTS = 501
DIM =       1 1 1
BAND = AUTO
BAND_LABELS = AUTO
TETRAHEDRON = .TRUE.
PDOS = AUTO
BAND_CONNECTION = .TRUE.
FORCE_CONSTANTS = READ
EOF
            # 运行phonopy
            log "执行phonopy计算"
            phonopy --fc vasprun.xml
            phonopy -s -p KPATH.phonopy --pa=auto
            phonopy-bandplot --gnuplot | tee phono.txt
            rm -f *.yaml
            log "✅ 声子谱计算完成"
        else
            warning "未生成有效vasprun.xml,跳过phonopy计算"
        fi
        
        cd ..
    fi

    cd ..  # 返回主目录
done

# ===================== 计算完成 =====================
log "${GREEN}===== 所有计算完成! =====${NC}"
log "✅ 初始CONTCAR:$current_contcar(跳过常压优化)/ CONTCAR.0GPa(常压优化)"
log "✅ 压力点CONTCAR:P*GPa/CONTCAR.*GPa(每个压力点的核心输出)"
log "✅ 关键文件:"
log "  - 能带计算:P*GPa/band/(使用CONTCAR/原胞PRIMCELL.vasp作为输入)"
log "  - 声子谱计算:P*GPa/phono/(使用CONTCAR作为输入)"
log "✅ 注意:所有后续计算均强制使用CONTCAR(无论是否收敛)"