Download demo/run.sh from OneScience-Group/NequIP: direct link, hf CLI and curl.
- Browser
- Download file 5.77 kB
-
https://huggingface.co/OneScience-Group/NequIP/resolve/main/demo/run.sh
- Command line
-
hf download hf://OneScience-Group/NequIP/demo/run.sh
-
curl -L -o run.sh https://huggingface.co/OneScience-Group/NequIP/resolve/main/demo/run.sh
5.77 kB
| # Run NequIP training locally or submit it to Slurm from one YAML file. | |
| set -euo pipefail | |
| DEMO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | |
| NEQUIP_DIR="$(cd "$DEMO_DIR/.." && pwd)" | |
| PARSER="$DEMO_DIR/_parse_config.py" | |
| CONFIG="" | |
| SUBMIT=false | |
| while [[ $# -gt 0 ]]; do | |
| case "$1" in | |
| --config) CONFIG="$2"; shift 2 ;; | |
| --config=*) CONFIG="${1#*=}"; shift ;; | |
| --submit) SUBMIT=true; shift ;; | |
| -h|--help) | |
| echo "Usage: bash demo/run.sh --config configs/<name>.yaml [--submit]" | |
| echo "launch.mode: auto uses matching resources or submits when needed." | |
| echo "launch.mode: local runs directly; submit always submits to Slurm." | |
| exit 0 | |
| ;; | |
| *) echo "Unknown argument: $1" >&2; exit 2 ;; | |
| esac | |
| done | |
| [[ -n "$CONFIG" ]] || { echo "Please specify --config configs/<name>.yaml" >&2; exit 2; } | |
| [[ "$CONFIG" = /* ]] || CONFIG="$DEMO_DIR/$CONFIG" | |
| [[ -f "$CONFIG" ]] || { echo "Config not found: $CONFIG" >&2; exit 2; } | |
| if [[ -z "${CONDA_PREFIX:-}" ]]; then | |
| echo "Activate a OneScience MatChem conda environment before running this script." >&2 | |
| exit 2 | |
| fi | |
| if [[ -z "${ONESCIENCE_MODELS_DIR:-}" || -z "${ONESCIENCE_DATASETS_DIR:-}" ]]; then | |
| echo "Set ONESCIENCE_MODELS_DIR and ONESCIENCE_DATASETS_DIR before running this script." >&2 | |
| exit 2 | |
| fi | |
| export MATCHEM_CONDA_NAME="${MATCHEM_CONDA_NAME:-$(basename "$CONDA_PREFIX")}" | |
| NAME="$(python3 "$PARSER" "$CONFIG" name)" | |
| eval "$(python3 "$PARSER" "$CONFIG" launch)" | |
| eval "$(python3 "$PARSER" "$CONFIG" slurm)" | |
| ENV_EXPORTS="$(python3 "$PARSER" "$CONFIG" env)" | |
| if [[ "$RUN_MODE" == "submit" ]]; then | |
| SUBMIT=true | |
| fi | |
| if [[ "$RUN_MODE" == "auto" ]] && ! $SUBMIT; then | |
| IN_SLURM_ALLOCATION=false | |
| AVAILABLE_NODES=1 | |
| if [[ -n "${SLURM_JOB_ID:-}" ]]; then | |
| IN_SLURM_ALLOCATION=true | |
| AVAILABLE_NODES="${SLURM_NNODES:-${SLURM_JOB_NUM_NODES:-1}}" | |
| if ! [[ "$AVAILABLE_NODES" =~ ^[1-9][0-9]*$ ]]; then | |
| echo "Cannot determine allocated nodes from Slurm: $AVAILABLE_NODES" >&2 | |
| exit 2 | |
| fi | |
| fi | |
| AVAILABLE_GPUS="$( | |
| python3 -c 'import torch; print(torch.cuda.device_count() if torch.cuda.is_available() else 0)' \ | |
| 2>/dev/null || true | |
| )" | |
| if ! [[ "$AVAILABLE_GPUS" =~ ^[0-9]+$ ]]; then | |
| AVAILABLE_GPUS=0 | |
| fi | |
| RESOURCE_MISMATCH="" | |
| if (( AVAILABLE_NODES < NODES )); then | |
| RESOURCE_MISMATCH="the config requests $NODES nodes but only $AVAILABLE_NODES are available" | |
| elif (( AVAILABLE_GPUS < GPUS_PER_NODE )); then | |
| RESOURCE_MISMATCH="the config requests $GPUS_PER_NODE DCUs per node but only $AVAILABLE_GPUS are visible" | |
| fi | |
| if [[ -n "$RESOURCE_MISMATCH" ]]; then | |
| if ! command -v sbatch >/dev/null 2>&1; then | |
| echo "Current resources are insufficient: $RESOURCE_MISMATCH, and sbatch is unavailable." >&2 | |
| exit 2 | |
| fi | |
| if $IN_SLURM_ALLOCATION; then | |
| echo "Current Slurm allocation is insufficient: $RESOURCE_MISMATCH. Submitting a new Slurm job." | |
| else | |
| echo "Current resources are insufficient: $RESOURCE_MISMATCH. Submitting to Slurm." | |
| fi | |
| SUBMIT=true | |
| else | |
| echo "Current resources satisfy the config: nodes=$NODES, DCUs/node=$GPUS_PER_NODE." | |
| fi | |
| fi | |
| TIMESTAMP="$(date +%Y%m%d_%H%M%S)" | |
| OUTPUT_ROOT="${ONESCIENCE_NEQUIP_OUTPUT_ROOT:-$NEQUIP_DIR/outputs}" | |
| OUTPUT_DIR="$OUTPUT_ROOT/${NAME}_${TIMESTAMP}" | |
| mkdir -p "$OUTPUT_DIR/checkpoints" | |
| cp "$CONFIG" "$OUTPUT_DIR/source_config.yaml" | |
| python3 "$PARSER" "$CONFIG" training-config > "$OUTPUT_DIR/config.yaml" | |
| if $SUBMIT; then | |
| SLURM_SCRIPT="$OUTPUT_DIR/submit.sh" | |
| cat > "$SLURM_SCRIPT" <<EOF | |
| #!/bin/bash | |
| #SBATCH --job-name=$NAME | |
| #SBATCH --partition=$PARTITION | |
| #SBATCH --nodes=$NODES | |
| #SBATCH --ntasks-per-node=$GPUS_PER_NODE | |
| #SBATCH --cpus-per-task=$CPUS_PER_TASK | |
| #SBATCH --gres=dcu:$GPUS_PER_NODE | |
| #SBATCH --time=$TIME | |
| #SBATCH --output=$OUTPUT_DIR/slurm_%j.out | |
| #SBATCH --error=$OUTPUT_DIR/slurm_%j.err | |
| EOF | |
| if [[ -n "$NODELIST" ]]; then | |
| printf '#SBATCH --nodelist=%s\n' "$NODELIST" >> "$SLURM_SCRIPT" | |
| fi | |
| cat >> "$SLURM_SCRIPT" <<EOF | |
| set -euo pipefail | |
| export MATCHEM_CONDA_NAME="$MATCHEM_CONDA_NAME" | |
| export ONESCIENCE_MODELS_DIR="$ONESCIENCE_MODELS_DIR" | |
| export ONESCIENCE_DATASETS_DIR="$ONESCIENCE_DATASETS_DIR" | |
| export HSA_FORCE_FINE_GRAIN_PCIE=1 | |
| if (( $GPUS_PER_NODE > 1 )); then | |
| unset CUDA_VISIBLE_DEVICES HIP_VISIBLE_DEVICES ROCR_VISIBLE_DEVICES | |
| fi | |
| $ENV_EXPORTS | |
| cd "$OUTPUT_DIR" | |
| EOF | |
| if (( WORLD_SIZE > 1 )); then | |
| cat >> "$SLURM_SCRIPT" <<EOF | |
| exec srun --kill-on-bad-exit=1 \ | |
| --nodes=$NODES \ | |
| --ntasks=$WORLD_SIZE \ | |
| --ntasks-per-node=$GPUS_PER_NODE \ | |
| python "$NEQUIP_DIR/train.py" "hydra.run.dir=$OUTPUT_DIR" | |
| EOF | |
| else | |
| echo "exec python \"$NEQUIP_DIR/train.py\" \"hydra.run.dir=$OUTPUT_DIR\"" >> "$SLURM_SCRIPT" | |
| fi | |
| chmod u+x "$SLURM_SCRIPT" | |
| echo "Submitting NequIP job: $SLURM_SCRIPT" | |
| sbatch "$SLURM_SCRIPT" | |
| exit 0 | |
| fi | |
| eval "$ENV_EXPORTS" | |
| cd "$OUTPUT_DIR" | |
| if (( WORLD_SIZE > 1 )); then | |
| if (( NODES > 1 )); then | |
| if [[ "$RUN_MODE" == "auto" && -n "${SLURM_JOB_ID:-}" ]]; then | |
| exec srun --kill-on-bad-exit=1 \ | |
| --nodes="$NODES" \ | |
| --ntasks="$WORLD_SIZE" \ | |
| --ntasks-per-node="$GPUS_PER_NODE" \ | |
| python "$NEQUIP_DIR/train.py" "hydra.run.dir=$OUTPUT_DIR" | |
| fi | |
| echo "Multi-node NequIP training must be launched through Slurm (--submit)." >&2 | |
| exit 2 | |
| fi | |
| exec torchrun --standalone --nproc_per_node="$GPUS_PER_NODE" \ | |
| "$NEQUIP_DIR/train.py" "hydra.run.dir=$OUTPUT_DIR" | |
| fi | |
| exec python "$NEQUIP_DIR/train.py" "hydra.run.dir=$OUTPUT_DIR" | |