Page 1 of 1

VASP 6.6.1 on H100 GPUs for MLFF training

Posted: Mon Sep 14, 2026 12:20 pm
by rika_windisch

Dear VASP Team,

I am currently trying to continue the training of a MLFF with the gpu version of vasp 6.6.1.
I am using 1 MPI-rank per GPU on a node with 4 NVIDIA H100 GPUs. I want to use
multiple OpenMP threads per MPI rank, but I am not sure if this works well. The initialization of the Force Field
takes over 15 hours rigth now and I am just wondering if there is a better way to set this up?
This is my Slurm script I am using rigth now:

Code: Select all

#!/bin/bash
#SBATCH --job-name=train_PbS
#SBATCH -N 1
#SBATCH --gres=gpu:4
#SBATCH --ntasks=4
#SBATCH --ntasks-per-node=4
#SBATCH --cpus-per-task=16
#SBATCH --threads-per-core=1
#SBATCH -p zen4_0768_h100x4
#SBATCH --qos zen4_0768_h100x4
#SBATCH --time=71:59:00

set -euo pipefail

module --force purge
module load EESSI/2025.06
module load ASC/2025.06
module load NVHPC/25.9
module load FFTW/3.3.10-NVHPC-25.9
module load HDF5/1.14.5-NVHPC-25.9
module load CUDA/13.0

ulimit -s unlimited
export OMP_STACKSIZE=512m
export VASP_BIN=/home/rw74596/vasp.6.6.1/bin/vasp_std
export OMP_NUM_THREADS=${SLURM_CPUS_PER_TASK}
export OMP_PROC_BIND=spread
export OMP_PLACES=cores

echo "Job started: $(date)"
mpirun --bind-to none -np ${SLURM_NTASKS} "$VASP_BIN"
echo "Job finished: $(date)"

Thank you!

Rika


Re: VASP 6.6.1 on H100 GPUs for MLFF training

Posted: Tue Sep 15, 2026 11:52 am
by andreas.singraber

Dear Rika,

welcome to the VASP Forum! Thank you for the detailed description of the issue. However, may I ask you to also upload a minimal reproducible example (see also the forum posting guidelines) with the input and output files for your problematic run? With this information it will be much easier to dissect from where this hangup originates. With 15 hours of initialization of the ML force field I am already pretty sure that there is some hangup involved.

Am I correct to assume that you are working on the MUSICA HPC cluster? My colleague Alexander Hampel already did some testing on this system and gave me some hints. The issue may be that the line

Code: Select all

mpirun --bind-to none -np ${SLURM_NTASKS} "$VASP_BIN"

basically disables processor pinning (i.e. MPI ranks are not assigned to fixed cores but are free to be placed and even moved by the OS). This is generally not recommended on HPC systems because performance may suffer. In this specific case it may even lead to the hangups, Alexander noted that he always had to do explicit pinning to get these multi-GPU jobs to work on MUSICA. From his notes I tried to put together a recipe you can try out. First, I suggest to change the job script as follows:

Code: Select all

#!/bin/bash
#SBATCH --job-name=train_PbS
#SBATCH -N 1
#SBATCH --gres=gpu:4
#SBATCH --ntasks=4
#SBATCH --ntasks-per-node=4
#SBATCH --cpus-per-task=16
#SBATCH --threads-per-core=1
#SBATCH -p zen4_0768_h100x4
#SBATCH --qos zen4_0768_h100x4
#SBATCH --time=71:59:00

set -euo pipefail

module --force purge
module load EESSI/2025.06
module load ASC/2025.06
module load NVHPC/25.9
module load FFTW/3.3.10-NVHPC-25.9
module load HDF5/1.14.5-NVHPC-25.9
module load CUDA/13.0

ulimit -s unlimited
export OMP_STACKSIZE=2048m                              # increased from 512m
export VASP_BIN=/home/rw74596/vasp.6.6.1/bin/vasp_std
export OMP_NUM_THREADS=${SLURM_CPUS_PER_TASK}
export MKL_NUM_THREADS=${SLURM_CPUS_PER_TASK}           # added
export OMP_PROC_BIND=close                              # "close" instead of "spread"
export OMP_PLACES=cores
export OMP_WAIT_POLICY=PASSIVE                          # added

export NVSHMEM_DISABLE=1                                # added
export UCX_COLL_ENABLE=n                                # added
export UCX_WARN_UNUSED_ENV_VARS=n                       # added


echo "Job started: $(date)"
mpirun -n ${SLURM_NTASKS} ./gpu_wrapper.sh "$VASP_BIN"  # call gpu_wrapper.sh for explicit pinning
echo "Job finished: $(date)"

I appended a short comment in the lines I edited. The mpirun command now calls a separate GPU wrapper script gpu_wrapper.sh. Here are the contents of this script (thanks to Alexander for sharing):

Code: Select all

#!/usr/bin/env bash
#
# GPU + CPU pinning wrapper using taskset
#

set -euo pipefail

# --- identify local rank ---
if [[ -n "${OMPI_COMM_WORLD_LOCAL_RANK:-}" ]]; then
    LOCAL_RANK=${OMPI_COMM_WORLD_LOCAL_RANK}
elif [[ -n "${SLURM_LOCALID:-}" ]]; then
    LOCAL_RANK=${SLURM_LOCALID}
else
    echo "Error: cannot determine local rank" >&2
    exit 1
fi

if (( LOCAL_RANK < 0 || LOCAL_RANK > 3 )); then
    echo "Error: LOCAL_RANK=${LOCAL_RANK} not supported" >&2
    exit 1
fi

# --- affinity tables ---
CPU_RANGES=(
    "72-95,264-287"
    "48-71,240-263"
    "144-167,336-359"
    "96-119,288-311"
)

GPUS=(0 1 2 3)

CPU_RANGE=${CPU_RANGES[$LOCAL_RANK]}
GPU_ID=${GPUS[$LOCAL_RANK]}

# --- threads ---
OMP_NUM_THREADS=${OMP_NUM_THREADS:-1}

# Optional sanity check
CORES_AVAILABLE=$(( ${CPU_RANGE#*-} - ${CPU_RANGE%-*} + 1 ))
if (( OMP_NUM_THREADS > CORES_AVAILABLE )); then
    echo "Error: OMP_NUM_THREADS=${OMP_NUM_THREADS} > available cores (${CORES_AVAILABLE})" >&2
    exit 1
fi

# --- environment ---
export CUDA_VISIBLE_DEVICES=${GPU_ID}
export OMP_PLACES=cores
export OMP_PROC_BIND=close

echo "[Rank ${LOCAL_RANK}] GPU=${GPU_ID} CPUs=${CPU_RANGE} OMP_NUM_THREADS=${OMP_NUM_THREADS}" >&2

# --- exec with pinning ---
exec taskset -c ${CPU_RANGE} "$@"

Just put these commands in a file called gpu_wrapper.sh, put it into your run directory and make it executable with

Code: Select all

chmod u+x ./gpu_wrapper.sh

Now you can try to resubmit your job and see if the hangups persist. Hope this helps, please report back if you find more issues!

All the best,
Andreas Singraber


Re: VASP 6.6.1 on H100 GPUs for MLFF training

Posted: Wed Sep 16, 2026 4:14 pm
by rika_windisch

Hello Andreas Singraber,

thanks for the detailed reply!
Okay I suspected that that was maybe an issue but I tried a few things and it did not change anything before.
I tried it with the wrapper script, but now I got the following error:

Code: Select all

Job started: Wed Sep 16 16:19:43 CEST 2026
Node: n3012-020
MPI ranks: 4
CPUs per rank: 16
Total requested CPUs: 64
GPUs: 0,1,2,3
========================================
--------------------------------------------------------------------------
Open MPI tried to bind a new process, but something went wrong.  The
process was killed without launching the target application.  Your job
will now abort.

  Local host:        n3012-020
  Application name:  ./gpu_wrapper.sh
  Error message:     hwloc_set_cpubind returned "Error" for bitmap "0,192"
  Location:          ../../../../../orte/mca/rtc/hwloc/rtc_hwloc.c:382
--------------------------------------------------------------------------
--------------------------------------------------------------------------
mpirun was unable to start the specified application as it encountered an
error:

Error code: -125
Error name: The specified application failed to start
Node: n3012-020

when attempting to start process rank 0.
--------------------------------------------------------------------------
[n3012-020:3533542] 1 more process has sent help message help-orte-odls-default.txt / binding generic error
[n3012-020:3533542] Set MCA parameter "orte_base_help_aggregate" to 0 to see all help / error messages
2 total processes failed to start

I also attached my input files. Besides this I have a larger Force Field.

Best,

Rika


Re: VASP 6.6.1 on H100 GPUs for MLFF training

Posted: Mon Sep 21, 2026 5:21 pm
by andreas.singraber

Hello Rika,

I am sorry the suggested scripts did not work properly... following the error message you received I made some changes to both scripts (actually reintroducing the --bind-to none):

Code: Select all

#!/bin/bash
#SBATCH --job-name=train_PbS
#SBATCH -N 1
#SBATCH --exclusive                                     # own all 192 cores; required for the fixed CPU ranges in gpu_wrapper.sh
#SBATCH --gres=gpu:4
#SBATCH --ntasks=4
#SBATCH --ntasks-per-node=4
#SBATCH --cpus-per-task=48                              # 192 cores / 4 ranks (was 16 -> clashed with the wrapper's ranges)
#SBATCH --threads-per-core=1
#SBATCH -p zen4_0768_h100x4
#SBATCH --qos zen4_0768_h100x4
#SBATCH --time=71:59:00

set -uo pipefail                                        # dropped -e: it interacts badly with the module functions

module --force purge
module load EESSI/2025.06
module load ASC/2025.06
module load NVHPC/25.9
module load FFTW/3.3.10-NVHPC-25.9
module load HDF5/1.14.5-NVHPC-25.9
module load CUDA/13.0

ulimit -s unlimited
export OMP_STACKSIZE=2048m
export VASP_BIN=/home/rw74596/vasp.6.6.1/bin/vasp_std

export OMP_NUM_THREADS=16                               # explicit: must NOT inherit cpus-per-task=48
export OMP_PROC_BIND=close
export OMP_PLACES=cores
export OMP_WAIT_POLICY=PASSIVE

export NVSHMEM_DISABLE=1
export UCX_COLL_ENABLE=n
export UCX_WARN_UNUSED_ENV_VARS=n

echo "Job started: $(date)"
echo "Node: $(hostname -s)"
echo "MPI ranks: ${SLURM_NTASKS}"
echo "OMP threads per rank: ${OMP_NUM_THREADS}"
echo "========================================"

# --bind-to none: let gpu_wrapper.sh do the pinning via taskset.
# Without it Open MPI binds first, by core (= CPU + its SMT sibling), and with
# --threads-per-core=1 the sibling is outside the Slurm cpuset -> hwloc_set_cpubind fails.
mpirun -n "${SLURM_NTASKS}" --bind-to none "${SLURM_SUBMIT_DIR}/gpu_wrapper.sh" "$VASP_BIN"

echo "Job finished: $(date)"

and

Code: Select all

#!/usr/bin/env bash
#
# GPU + CPU pinning wrapper using taskset
#
# Requires mpirun --bind-to none, otherwise Open MPI binds the rank first and
# this wrapper never gets a say (and fails on the SMT sibling CPUs).
#

set -euo pipefail

# --- identify local rank ---
if [[ -n "${OMPI_COMM_WORLD_LOCAL_RANK:-}" ]]; then
    LOCAL_RANK=${OMPI_COMM_WORLD_LOCAL_RANK}
elif [[ -n "${SLURM_LOCALID:-}" ]]; then
    LOCAL_RANK=${SLURM_LOCALID}
else
    echo "Error: cannot determine local rank" >&2
    exit 1
fi

# --- affinity tables ---
# 16 cores per rank, NUMA-local to the matching GPU.
# No SMT siblings (192-383): --threads-per-core=1 keeps them out of the cpuset.
# Sockets: cores 0-95 = socket 0, cores 96-191 = socket 1.
# VERIFY the GPU <-> socket mapping on the node with: nvidia-smi topo -m
CPU_RANGES=(
    "0-15"          # rank 0 -> GPU 0, socket 0
    "48-63"         # rank 1 -> GPU 1, socket 0
    "96-111"        # rank 2 -> GPU 2, socket 1
    "144-159"       # rank 3 -> GPU 3, socket 1
)

GPUS=(0 1 2 3)

if (( LOCAL_RANK < 0 || LOCAL_RANK >= ${#CPU_RANGES[@]} )); then
    echo "Error: LOCAL_RANK=${LOCAL_RANK} not supported" >&2
    exit 1
fi

CPU_RANGE=${CPU_RANGES[$LOCAL_RANK]}
GPU_ID=${GPUS[$LOCAL_RANK]}

# --- helpers ---
# Expand "a-b,c-d,e" into one CPU id per line.
expand_cpus() {
    local spec=$1 part lo hi c out=()
    local -a parts
    IFS=',' read -ra parts <<< "$spec"
    for part in "${parts[@]}"; do
        if [[ $part == *-* ]]; then
            lo=${part%-*}
            hi=${part#*-}
            for (( c = lo; c <= hi; c++ )); do out+=("$c"); done
        else
            out+=("$part")
        fi
    done
    printf '%s\n' "${out[@]}"
}

# --- sanity checks ---
mapfile -t WANT    < <(expand_cpus "$CPU_RANGE")
mapfile -t ALLOWED < <(expand_cpus "$(taskset -cp $$ | awk -F': ' '{print $2}')")

for c in "${WANT[@]}"; do
    if ! printf '%s\n' "${ALLOWED[@]}" | grep -qx -- "$c"; then
        echo "Error: rank ${LOCAL_RANK} wants CPU ${c}, which is not in this job's cpuset." >&2
        echo "       Allowed: $(taskset -cp $$ | awk -F': ' '{print $2}')" >&2
        echo "       Request the full node (--exclusive) or adjust CPU_RANGES." >&2
        exit 1
    fi
done

OMP_NUM_THREADS=${OMP_NUM_THREADS:-1}
if (( OMP_NUM_THREADS > ${#WANT[@]} )); then
    echo "Error: OMP_NUM_THREADS=${OMP_NUM_THREADS} > cores in mask (${#WANT[@]})" >&2
    exit 1
fi

# --- environment ---
export CUDA_VISIBLE_DEVICES=${GPU_ID}
export OMP_PLACES=cores
export OMP_PROC_BIND=close

echo "[Rank ${LOCAL_RANK}] GPU=${GPU_ID} CPUs=${CPU_RANGE} (${#WANT[@]} cores) OMP_NUM_THREADS=${OMP_NUM_THREADS}" >&2

# --- exec with pinning ---
exec taskset -c "${CPU_RANGE}" "$@"

However, since I have no access to the HPC machines it is really just guessing whether this will work any better.

Maybe it's best to look at this issue from another perspective: can you actually run the job without the machine-learning feature enabled? Can you maybe provide the full set of input files (including the ML_AB data set for continuation via some sharing platform if too large to attach here)? Did you compile VASP yourself on this cluster or is it a binary provided by the system administrators? If it's your own build, can you share the makefile.include and the toolchain information (which modules were loaded, etc.) here? Thank you!

All the best,
Andreas