The module command loads and unloads coordinated
software stacks.
Everyday commands:
$ module avail # list available modules
$ module avail gcc # search for modules matching "gcc"
$ module load rocm # load a module
$ module list # show currently loaded modulesLoading a module adjusts PATH,
LD_LIBRARY_PATH, etc. so the right binaries and libraries
are found. Load several to combine them; conflicting ones fail or
auto-swap.
Finding out about a module:
$ module whatis <name> # short description
$ module help <name> # detailed help
$ module display <name> # env vars set
$ module spider # ALL modules
$ module spider <name> # how to load
$ module key <keyword> # search by keywordDocs: module command · module environment
A software stack bundles a CPE version with HLRS
additions, versioned YYYY.D. Two flavours:
HLRS/APU: MI300A nodes (default)HLRS/CPU: CPU-only nodes$ module list HLRS # which stack is loaded
$ module avail HLRS # all available stacks
$ echo $HLRS_SOFTWARE_STACK_RELEASE_VERSION # e.g. 2026.1
$ echo $HLRS_SOFTWARE_STACK_CPE_VERSION # bundled CPE, e.g. 25.09
$ module load HLRS/CPU # switch to CPU stackLoading the right flavour (on login and in the job script) is your responsibility.
Login default: two stacks auto-load
(don't `module purge`):
HLRS/APU/2026.1
pins CPE 25.09 (cce 20.0.0)
adds rocm 6.4.1, GPU target,
GPU/MPI env, HLRS software
PrgEnv-cray
cc / CC / ftn wrappers
cray-mpich, cray-libsci
(the CPE libs, next slide)
Can't pick CPE directly:
switch the HLRS stack to change it.
See the HLRS software stacks docs.
PrgEnv modules coordinate the compiler, MPI, and libraries.
PrgEnv-cray loads by default at login and provides the
compiler wrappers.
PrgEnv-cray: Cray compilers (default)PrgEnv-gnu: GNU compilersPrgEnv-amd: AMD compilersSwapping auto-reloads the matching cray-mpich and
cray-libsci. Do not change the base compiler
module directly; always go through PrgEnv-*.
Only PrgEnv-cray and PrgEnv-amd can compile
for the MI300A GPUs. PrgEnv-gnu is CPU-only.
Always build with the wrappers, never the compiler directly. They add the right include paths, libraries, and MPI setup:
To see what a wrapper invokes:
See the programming environments docs.
The job starts from a fresh environment. The script must set up everything itself:
module load the stack
you built against (rocm, cray-mpich) again.
Login-session modules do not carry over.MAMBA_ROOT_PREFIX,
then micromamba activate. Conda/mamba environments do not
carry over either.python see the
right interpreter and libraries.Hunter has 188 APU nodes shared by hundreds of users.
You cannot just SSH into a compute node and run your simulation. Instead, you submit a job to the batch system, which:
Hunter uses PBSPro as its batch system.
qsub jobscript.pbsYou get an email (if requested) at begin/abort/end.
jobscript.pbs
│
│ qsub
▼
┌─────────┐
│ queue │ ◄── qstat
└────┬────┘ qdel
│
│ scheduled
▼
┌─────────┐
│ running │
└────┬────┘
│
▼
log files
What works in your interactive shell does not automatically work in a job. A PBS job starts from a fresh login with the site defaults (not your session):
~/.bashrc is sourcedHLRS/APU
+ PrgEnv-cray)load,
swap, or export) is
discardedRule of thumb: load any non-default stack, PrgEnv, or module inside the script. The defaults are already there; your interactive changes are not.
Login session Job (compute node)
┌────────────┐ ┌──────────────┐
│ ~/.bashrc │ ── ✓ ──▶│ ~/.bashrc │
│ modules │ ── x ── │ default stack│
│ PATH=… │ ── ? ──▶│ PATH=default │
└────────────┘ └──────────────┘
re-run `module load` and 'export' in the script
Example jobscript.pbs:
#!/bin/bash
#PBS -N hello_jobstep
#PBS -m bae
#PBS -M your.name@example.com
#PBS -l select=2:node_type=mi300a:mpiprocs=4:ompthreads=22
#PBS -l walltime=00:10:00
#PBS -q test
cd $PBS_O_WORKDIR
export OMP_NUM_THREADS=22
export OMP_PROC_BIND=close
### not necessary here since it's the default
module load HLRS/APU
mpiexec -ppn 4 -np 8 \
--cpu-bind list:2-23:26-47:50-71:74-95 \
--gpu-bind list:0:1:2:3 \
./hello_jobstep | sort -k2n -k5nSubmit with qsub jobscript.pbs
See the PBSPro docs: batch mode · node types · examples
Three parts:
#PBS directives: comments to bash, but
the scheduler reads them.cd $PBS_O_WORKDIR (else
runs in $HOME), then module load (login
modules do not carry over).mpiexec runs the program;
--cpu-bind/--gpu-bind pin each rank to a chip
(affinity section, later).Resource request (-l select=...):
select=2 → 2 nodesnode_type=mi300a → APU nodes (4× MI300A each)mpiprocs=4 → 4 ranks/node (one per chip)ompthreads=22 → 22 threads/rank (24 cores, 2 for
OS)walltime=HH:MM:SS → max runtime; job
killed if exceeded. Try to minimize it, since longer
walltime → longer queue wait.Two queues you will use today:
| Queue | Walltime | Use for |
|---|---|---|
test |
max 25 min | quick tests, debugging, the hands-on |
| (default) | longer | production runs |
If you do not specify -q, PBS chooses a default queue
based on your resource request.
See all available queues at Batch_System_PBSPro_(Hunter)#Batch_Queue_Policies_and_Limitations

$ qsub jobscript.pbs # submit
12345.hunter-batch
$ qstat # show your jobs
$ qstat -u $USER # same, explicit
$ qstat -f 12345 # full info on a running job
$ qstat -xf 12345 # full info on a finished job
$ qdel 12345 # cancel a jobWhen a job finishes unexpectedly:
qstat -xf <jobid> → exit status & resources
used.o / .e filesBefore installing anything yourself, check whether it is already on Hunter as a module. Most common HPC libraries are.
Numerical & math
cray-libsci (BLAS, LAPACK, ScaLAPACK) (loaded by
default)cray-libsci_acc: GPU-accelerated BLAS/LAPACKcray-fftw: FFTspetsc, hypre, trilinos:
sparse solverskokkos: performance-portable parallelismMesh partitioning
metis, parmetis, scotch,
kahipParallel I/O
cray-hdf5-parallel, cray-netcdfcray-parallel-netcdfadios2Build & development
cmake, meson, ninjaboost, python/3.11Profiling (covered later today)
perftools-lite, scorep,
scalasca, extrae, mpip,
vampir, rocprof-computeHunter has several commercial CAE/CFD codes, all for non-commercial use only. Most also require Uni Stuttgart membership; check the terms per code:
| Code | Use case | Hunter partition |
|---|---|---|
| ANSYS¹ (Fluent, CFX, Mechanical) | CFD, structural | Genoa CPU; Fluent also on MI300A APU |
| STAR-CCM+ | CFD, multiphysics | Genoa CPU |
| ABAQUS | Structural mechanics | Genoa CPU |
| LS-Dyna | Explicit dynamics, crash | Genoa CPU |
| VASP² | Computational chemistry | check wiki |
¹ Fluent/CFX: any HLRS user (non-commercial). Mechanical: U Stuttgart only. ² VASP: per research-group license.
These codes do not work the same way as your own MPI programs:
siemens/lic/...)select line and mpiprocs
differ per codeFull overview of available application software packages: kb.hlrs.de/platforms/index.php/Application_software_packages
cae moduleThe cae module bundles small HLRS-written tools that
provides utility commands and provides job script templates:
| Command | What it does |
|---|---|
qgen.sh |
Generate a PBS job script from a template; use
qgen.sh -l to list them |
qwtime |
Show remaining walltime of a running job |
qssh |
SSH into the first node of one of your running jobs |
ws_share |
Share a workspace with another user (replaces
ws_exchange) |
jobnodesinfo.py |
List the nodes a job is using and their layout |
Example how to generate and submit a job from a template:
Hunter’s nodes draw substantial power and every allocated node counts:
| Node type | CPUs / APUs | Node power (TDP) | Node power (peak) |
|---|---|---|---|
| MI300A APU (EX255a) | 4× MI300A | ~2,200 W | ~3,040 W |
| Genoa CPU (EX4252) | 2× EPYC 9374F | ~640 W (CPU only) | ~800–1,100 W (est.) |
System and data centre infrastructure add further overhead:
| Component | What it covers | MI300A overhead | Genoa overhead |
|---|---|---|---|
| System | + Network, storage, CDUs, PSU losses | × 1.43 | × 1.51 |
| Data centre | + Chillers, UPS, transformers, pumps | × 1.05 | × 1.05 |
Each MI300A node your code cannot keep busy consumes ~3,300 W of total facility power
Equivalent to 8 average German households (≈ 400W each) running continuously.
What you can do:
NodeSystemInfraPower in the Project
ManagerYou only know where a code spends its time by measuring it.
Intuition about hotspots is wrong more often than not; small functions called millions of times beat big functions called once.
Profiling answers:
The first two questions tell you what to optimize. The last two tell you how many nodes to request, which, as the previous slide showed, is also a power and budget question.
Hunter ships with several established profiling tools, so you do not need to install anything:
| Tool | Strength | Module |
|---|---|---|
| Perftools-lite (HPE Cray) | Default on Hunter; minimal setup, automatic hotspot summary | perftools-lite |
| mpiP | Lightweight MPI-only profiling, low overhead | mpip |
| Score-P + Scalasca | Full-featured profiling and tracing, MPI + OpenMP | scorep scalasca |
| Extrae + Paraver | Detailed traces with GUI visualization | extrae bsc_tools |
ROCm tools (rocprof,
omnitrace) |
GPU kernel profiling on MI300A (self-study material) | shipped with rocm |
| Darshan | I/O profiling: drop-in via LD_PRELOAD, generates I/O
behavior reports |
darshan-runtime |
| HLRS user support | Performance advice and help interpreting profiling results; write a ticket | - |
Start with Perftools-lite. It is the path of least
resistance on Cray systems: load the module, recompile, run normally,
then pat_report. Most users will not need to look
further.
You don’t have to interpret profiles alone. HLRS user support helps with performance questions too, write a trouble ticket or an Email to rt-platform-hunter@hlrs.de with your job ID, what you measured, and what surprised you.
This was just a mention, not a tutorial. When you’re ready:
HLRS knowledge base workflow guides written for HLRS systems:
Upstream training material Score-P, Scalasca, and Cube are developed at JSC and presented regularly at VI-HPS workshops:
HLRS courses on performance optimization and parallel programming hlrs.de/training/hpc-training.
If you get stuck on a real code, open a support ticket. We help our users optimize their applications on HLRS systems.
An example of a CFD workflow on a cluster, demonstrated with with OpenFOAM®:
Plot generated
with ParaView
pitzDailySteady turbulent flow over a backward-facing step.
simpleFoam: steady-state,
incompressible, RANSThe flow detaches at the step, forms a recirculation bubble, and reattaches downstream. The reattachment length is a classic validation quantity and was measured experimentally by Pitz & Daily (1983).
pitzDaily-hlrs/ ← static, in version control
├── job.pbs
├── vis.pbs
├── plot_streamlines.py
├── src/ custom dicts (copied into system at runtime)
│ ├── controlDict
│ ├── decomposeParDict
│ └── fvSolution
└── run./ ← created by the jobscript
├── 0/
├── constant/
└── system/
└── ... (tutorial dicts + your src/ overrides)
pitzDaily is a test setup shipped with
OpenFOAM®,
after loading the openfoam module, you can copy it from:
$WM_PROJECT_DIR/tutorials/incompressible/simpleFoam/pitzDaily/
Project root holds everything around the case:
job.pbs, vis.pbs: what the cluster
runsplot_streamlines.py: postprocessingsrc/: replacement dicts copied into
system/ at job startThe OpenFOAM® case itself:
0/: initial conditions + boundary
conditions, one file per field (U, p,
k, epsilon, …)constant/: mesh in
polyMesh/, transport + turbulence propertiessystem/: what the solver does:
controlDict: time stepping, outputfvSchemes: discretizationfvSolution: linear solvers, tolerancesdecomposeParDict: how to split for MPI#!/bin/bash
#PBS -N openfoam_pitzDaily
#PBS -j oe
#PBS -l walltime=00:10:00
# 2 APU nodes × 96 cores = 192 ranks
#PBS -l select=2:node_type=mi300a:mpiprocs=96
module load HLRS/CPU
module load openfoam
cd $PBS_O_WORKDIR
RUN_DIR="$PBS_O_WORKDIR/run.${PBS_JOBID%%.*}"
mkdir -p $RUN_DIR
# Stage the case
CASEPATH="$WM_PROJECT_DIR/tutorials/incompressible/simpleFoam/pitzDaily/"
cp -r $CASEPATH/* $RUN_DIR/
# Drop in our custom dicts
cp $PBS_O_WORKDIR/src/controlDict $RUN_DIR/system/
cp $PBS_O_WORKDIR/src/decomposeParDict $RUN_DIR/system/
cp $PBS_O_WORKDIR/src/fvSolution $RUN_DIR/system/
...select=2:mpiprocs=96 Two MI300A nodes,
fully populated. Must equal numberOfSubdomains in
decomposeParDict or the solver errors out.
module load Compute nodes start with a
clean environment, load modules inside the script, not just in your
login shell.
Custom dicts in src/ Tutorial defaults
aren’t suited for a parallel HPC run. Three replacements:
controlDict: run length, outputdecomposeParDict: method metis, subdomain
countfvSolution: solver tolerancesOne source of truth: change a value once, resubmit.
blockMesh + refineMesh
Generate ~12k cells, the two refinement passes bring it to ~780k. The
original is too small to show meaningful parallel scaling.
decomposePar -fileHandler collated
Splits mesh + fields into per-rank chunks. collated writes
one file per field instead of one per rank per timestep —> Lustre
metadata server stays happy.
mpirun -n $CORES
$PBS_NODEFILE has one line per allocated rank;
wc -l counts them.
-parallel -fileHandler collated The
solver needs both flags to read the decomposed case correctly.
Output appears in openfoam_pitzDaily.o<jobid> in
the submit directory.
Stitches the per-rank chunks inside processors*/
subdomains back into single time files at the top level
(3000/ for our final iteration).
Now we have a regular, undivided OpenFOAM® case ready to visualise.
The reconstructed directory contains all you need:
run.123456/
├── 0/ initial conditions
├── 3000/ final result
├── constant/ mesh + physics
├── system/ solver settings
└── processors*/ ← no longer needed
compute on Hunter → reconstruct → render on Vulcan
Hunter has no X-server for security reasons. It is recommended to use the visualisation nodes on Vulcan instead.
The Hunter workspace is mounted on Vulcan’s vis nodes, so no copy step is needed. Same path, same files, viewed from a different cluster.
Two paths:
qsub vis.pbs on Vulcan. If you
have a working plotting script.#!/bin/bash
#PBS -N pv_streamlines
#PBS -q vis
#PBS -l select=1:node_type=genoa-a30
#PBS -l walltime=00:30:00
#PBS -j oe
# Point at the case on the (mounted) Hunter workspace
# Copy the path from the Hunter login node, you won't see it on Vulcan's login nodes
RUN_DIR="/lustre/hpe/ws13/.../run.<jobid>"
cd "$RUN_DIR"
module load openfoam
reconstructPar -newTimes
module load paraview
pvbatch --opengl-window-backend 'EGL' \
"$PBS_O_WORKDIR/plot_streamlines.py" .If you visualised multiple timesteps, you can download the images and turn them into a video:

Submit on Vulcan: qsub vis.pbs
The genoa-a30 node has one NVIDIA A30 GPU. The
EGL backend uses it for rendering without needing an X
server.
For tuning the render, the GUI is essential. HLRS provides a helper script that handles VNC session setup:
On Vulcan login:
The script prints a ssh ... command and a
vncviewer localhost:10000 command once the job starts.
On your local machine:
Install one of TurboVNC, tightvncviewer, or TigerVNC. Then run the printed command, a desktop will appear, see next slide.

Inside the VNC desktop:
HLRS-startX # GPU-accessible X server
module load paraview
module load VirtualGL
vglrun paraview # hardware-accelerated GUIvglrun routes OpenGL calls to the actual GPU via
VirtualGL. Otherwise, the X server inside VNC would do software
rendering.
Open case.foam from the Hunter workspace mount, set up
Stream Tracer, save screenshot.
End the session via the VNC Helper app on the desktop.

High-Performance
Computing Center
Stuttgart
Lucas Jordan
Introduction to the HLRS Systems