Deployed e4869e0 with MkDocs version: 1.6.1
This commit is contained in:
parent
fd3dbe898a
commit
26e0b9ee28
75 changed files with 13749 additions and 5 deletions
83
scripts/hpc/export_requirements.py
Normal file
83
scripts/hpc/export_requirements.py
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Export HPC pip requirements from pyproject.toml.
|
||||
|
||||
This is a LOCAL DEVELOPER UTILITY — run it on your own machine before pushing
|
||||
code whenever pyproject.toml dependencies change. It reads the modules from
|
||||
env/hpc/modules.txt and the full dependency list from pyproject.toml, then
|
||||
writes the remainder to env/hpc/requirements.txt.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
|
||||
|
||||
def normalise(name: str) -> str:
|
||||
"""Normalise a PyPI package name for comparison."""
|
||||
return re.sub(r"[-_.]+", "-", name).lower()
|
||||
|
||||
|
||||
def pkg_name(dep: str) -> str:
|
||||
"""Extract the bare package name from a PEP 508 dependency string."""
|
||||
return re.split(r"[\[=><~!;]", dep)[0].strip()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
import tomllib
|
||||
|
||||
modules_path = ROOT / "env" / "hpc" / "modules.txt"
|
||||
if not modules_path.exists():
|
||||
print(f"Error: {modules_path} not found.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Read normalized module names from base modules only
|
||||
# Library modules (like PyTorch) are kept in requirements for portability
|
||||
module_names = [
|
||||
normalise(line.split()[0].split("/")[0])
|
||||
for line in modules_path.read_text().splitlines()
|
||||
if line.strip() and not line.startswith("#")
|
||||
]
|
||||
|
||||
pyproject_path = ROOT / "pyproject.toml"
|
||||
with pyproject_path.open("rb") as f:
|
||||
data = tomllib.load(f)
|
||||
|
||||
# Collect all dependencies, merging 'cuda' extras into base dependencies
|
||||
dep_dict: dict[str, str] = {}
|
||||
for dep in data.get("project", {}).get("dependencies", []):
|
||||
dep_dict[normalise(pkg_name(dep))] = dep
|
||||
|
||||
# Add cuda extras (takes precedence for HPC)
|
||||
optional_deps = data.get("project", {}).get("optional-dependencies", {})
|
||||
for group in ["cuda"]:
|
||||
for dep in optional_deps.get(group, []):
|
||||
dep_dict[normalise(pkg_name(dep))] = dep
|
||||
|
||||
deps = list(dep_dict.values())
|
||||
|
||||
final_deps: list[str] = []
|
||||
print("Checking dependencies against HPC module list...", file=sys.stderr)
|
||||
for dep in deps:
|
||||
name = normalise(pkg_name(dep))
|
||||
# Smart check: if the package name is a substring of any loaded module name
|
||||
# (e.g. 'torch' in 'pytorch', 'scipy' in 'scipy-bundle')
|
||||
if any(name in mod for mod in module_names):
|
||||
print(f" [skip – module provider found] {dep}", file=sys.stderr)
|
||||
continue
|
||||
|
||||
final_deps.append(dep)
|
||||
print(f" [pip] {dep}", file=sys.stderr)
|
||||
|
||||
hpc_dir = ROOT / "env" / "hpc"
|
||||
output_path = hpc_dir / "requirements.txt"
|
||||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
output_path.write_text("\n".join(final_deps) + "\n")
|
||||
print(f"\nWrote {len(final_deps)} requirement(s) to {output_path}", file=sys.stderr)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
54
scripts/hpc/install.sh
Normal file
54
scripts/hpc/install.sh
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#!/bin/bash -l
|
||||
# scripts/hpc/install.sh
|
||||
#
|
||||
# Usage (on any compute node):
|
||||
# bash scripts/hpc/install.sh
|
||||
#
|
||||
# Batch usage:
|
||||
# qsub scripts/hpc/install.sh
|
||||
|
||||
#PBS -N brittlestar-install
|
||||
#PBS -l walltime=00:15:00
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Preliminary status echo
|
||||
echo ">>> Starting installation job $PBS_JOBID on $(hostname)..."
|
||||
|
||||
if [ -n "$PBS_O_WORKDIR" ]; then
|
||||
cd "$PBS_O_WORKDIR"
|
||||
fi
|
||||
|
||||
mkdir -p "${PBS_O_WORKDIR}/runs"
|
||||
|
||||
# Mirror configs to $VSC_DATA to avoid home quota limits (3GB)
|
||||
# vsc-venv manages environments relative to the requirements file
|
||||
PROJ_NAME=$(basename "$PWD")
|
||||
HPC_CONFIG_DIR="$VSC_DATA/$PROJ_NAME/env/hpc"
|
||||
mkdir -p "$HPC_CONFIG_DIR"
|
||||
cp env/hpc/*.txt "$HPC_CONFIG_DIR/"
|
||||
|
||||
# Keep caches off $VSC_HOME (quota ~3 GB).
|
||||
export PIP_CACHE_DIR="$VSC_SCRATCH/.cache/pip"
|
||||
export UV_CACHE_DIR="$VSC_SCRATCH/.cache/uv"
|
||||
mkdir -p "$PIP_CACHE_DIR" "$UV_CACHE_DIR"
|
||||
|
||||
module load vsc-venv
|
||||
|
||||
echo ">>> Synchronizing and activating environment (vsc-venv)..."
|
||||
# cd to $VSC_DATA so vsc-venv creates its venvs/ directory there, not in $HOME.
|
||||
mkdir -p "$VSC_DATA/$PROJ_NAME"
|
||||
cd "$VSC_DATA/$PROJ_NAME"
|
||||
set +euo pipefail
|
||||
source vsc-venv --activate \
|
||||
--modules "$HPC_CONFIG_DIR/modules.txt" \
|
||||
--requirements "$HPC_CONFIG_DIR/requirements.txt"
|
||||
set -euo pipefail
|
||||
cd "$PBS_O_WORKDIR"
|
||||
|
||||
echo '>>> Installing ipykernel...'
|
||||
CLUSTER_ID="${VSC_INSTITUTE_CLUSTER:-generic}"
|
||||
python -m ipykernel install --user --name="sel3_${CLUSTER_ID}" \
|
||||
--display-name "SEL3 (${CLUSTER_ID})"
|
||||
|
||||
echo '>>> Done'
|
||||
75
scripts/hpc/train.pbs
Normal file
75
scripts/hpc/train.pbs
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
# Production training (requires GPU at runtime):
|
||||
# qsub -l gpus=1 scripts/hpc/train.pbs
|
||||
# Debug/CPU training:
|
||||
# qsub scripts/hpc/train.pbs
|
||||
|
||||
#PBS -N brittlestar-ppo
|
||||
#PBS -l nodes=1:ppn=8
|
||||
#PBS -l walltime=24:00:00
|
||||
#PBS -o runs/brittlestar-ppo.o$PBS_JOBID
|
||||
#PBS -e runs/brittlestar-ppo.e$PBS_JOBID
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Preliminary status echo
|
||||
echo ">>> Starting training job $PBS_JOBID on $(hostname)..."
|
||||
|
||||
if [ -n "$PBS_O_WORKDIR" ]; then
|
||||
cd "$PBS_O_WORKDIR"
|
||||
fi
|
||||
|
||||
# Set up storage paths dynamically
|
||||
PROJ_NAME=$(basename "$PWD")
|
||||
RUN_ID="brittlestar_${PBS_JOBID}"
|
||||
SCRATCH_RUNDIR="$VSC_SCRATCH/runs/$RUN_ID"
|
||||
DATA_RUNDIR="$VSC_DATA/runs/$RUN_ID"
|
||||
mkdir -p "$SCRATCH_RUNDIR" "$DATA_RUNDIR" runs/
|
||||
|
||||
# Keep caches off $VSC_HOME (quota ~3 GB).
|
||||
export PIP_CACHE_DIR="$VSC_SCRATCH/.cache/pip"
|
||||
export UV_CACHE_DIR="$VSC_SCRATCH/.cache/uv"
|
||||
mkdir -p "$PIP_CACHE_DIR" "$UV_CACHE_DIR"
|
||||
|
||||
module load vsc-venv
|
||||
|
||||
echo ">>> Synchronizing and activating environment (vsc-venv)..."
|
||||
HPC_CONFIG_DIR="$VSC_DATA/$PROJ_NAME/env/hpc"
|
||||
if [ ! -d "$HPC_CONFIG_DIR" ]; then
|
||||
echo "ERROR: HPC_CONFIG_DIR ($HPC_CONFIG_DIR) does not exist. Run install.sh first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# cd to $VSC_DATA so vsc-venv finds its venvs/ directory there, not in $HOME.
|
||||
cd "$VSC_DATA/$PROJ_NAME"
|
||||
set +euo pipefail
|
||||
source vsc-venv --activate \
|
||||
--modules "$HPC_CONFIG_DIR/modules.txt" \
|
||||
--requirements "$HPC_CONFIG_DIR/requirements.txt"
|
||||
set -euo pipefail
|
||||
cd "$PBS_O_WORKDIR"
|
||||
|
||||
|
||||
echo ">>> Starting BrittleStar training..."
|
||||
export MUJOCO_GL=egl
|
||||
export WANDB_DIR="$SCRATCH_RUNDIR"
|
||||
|
||||
export PYTHONPATH="$PBS_O_WORKDIR/src:${PYTHONPATH:-}"
|
||||
|
||||
if [ -f "$VSC_DATA/$PROJ_NAME/.env" ]; then
|
||||
echo ">>> Sourcing API keys from .env..."
|
||||
export $(grep -v '^#' "$VSC_DATA/$PROJ_NAME/.env" | xargs)
|
||||
elif [ -f "$PBS_O_WORKDIR/.env" ]; then
|
||||
echo ">>> Sourcing API keys from .env..."
|
||||
export $(grep -v '^#' "$PBS_O_WORKDIR/.env" | xargs)
|
||||
fi
|
||||
|
||||
# Run training using Hydra overrides
|
||||
python scripts/train.py \
|
||||
hydra.run.dir="$SCRATCH_RUNDIR" \
|
||||
ppo=stable \
|
||||
logging=hpc
|
||||
|
||||
echo ">>> Staging out results to $DATA_RUNDIR..."
|
||||
cp -r "$SCRATCH_RUNDIR/." "$DATA_RUNDIR/"
|
||||
|
||||
echo ">>> Done"
|
||||
Reference in a new issue