HPC / CFD — Coding & Command Practice¶
All commands, scripts and code fragments needed for the exam, with expected I/O.
1. Linux Commands¶
Permissions¶
chmod 755 script.sh # rwxr-xr-x
chmod 600 secret.key # rw-------
chmod 4755 prog # setuid; runs as owner
chmod 1777 /shared # sticky
chmod -R g+w project/ # recursively give group write
chown alice:students file
ls -lh
Redirection / pipes¶
ls -l > listing.txt
echo more >> listing.txt
./run.sh > out.log 2>&1 &
ls /missing 2> err.log
ls -l ~ | grep "^d" | wc -l
Word count¶
Files¶
mkdir -p a/b/c
cp -rpv src dst
mv old new
ln -s target link
find . -type f -name "*.cpp" -size +1k -mtime -7
2. SSH / Remote¶
# Generate key
ssh-keygen -t ed25519 -b 4096 -f ~/.ssh/cluster_key
# Push to server
ssh-copy-id -i ~/.ssh/cluster_key.pub user@cluster
# Config block
cat >> ~/.ssh/config <<'EOF'
Host hpc
HostName cluster.example.com
User myuser
IdentityFile ~/.ssh/cluster_key
EOF
chmod 600 ~/.ssh/{cluster_key,config}
chmod 700 ~/.ssh
# Connect
ssh hpc
# File transfer
scp -r local/ hpc:~/remote/
rsync -avzP --delete src/ hpc:~/dst/
sshfs hpc:/remote ~/mnt -o reconnect
fusermount -u ~/mnt
# Port forwarding
ssh -L 8888:cn01:8888 hpc
ssh -R 9000:localhost:22 hpc
ssh -D 1080 hpc
# Persistent shell
tmux new -s sim # detach: Ctrl-b d
tmux a -t sim
nohup ./run > log 2>&1 &
disown
3. Vim¶
Common keystrokes¶
i a o I A O insert variants
Esc back to Normal
:w :wq :q :q! save/quit
yy 5yy y2j p P yank/paste
dd 5dd dw d$ d0 delete
u Ctrl-r undo/redo
. repeat
/pat n N * search
:%s/old/new/gc global replace
:%s/\<Re\>/Reynolds/g whole-word
qa…q @a 5@a macro
:reg a inspect macro
:e file buffer
:bnext :bprev :ls buffers
:sp file :vsp file splits
Ctrl-w h/j/k/l move
:tabnew gt gT tabs
Macro from E03 Task 3¶
Useful .vimrc¶
set nocompatible
syntax on
filetype plugin indent on
set number relativenumber
set tabstop=4 shiftwidth=4 expandtab
set hlsearch incsearch ignorecase smartcase
set autoindent smartindent
set wildmenu showcmd ruler
set clipboard=unnamedplus
set undofile undodir=~/.vim/undo
let mapleader = ","
nnoremap <leader>w :w<CR>
nnoremap <leader>h :nohlsearch<CR>
inoremap jk <Esc>
nnoremap <F5> :w<CR>:!g++ -O2 -std=c++17 % -o %<<CR>:!./%<<CR>
4. Regex / Glob¶
Glob¶
ls *.txt
ls dir-?/*.h
ls {dir-1,dir-2}/*.{cpp,h,txt}
ls dir-{1,3}/*[0-9].h
ls dir-3/*[A-Z]*.h
shopt -s globstar; ls **/*.cpp
Regex (lecture solutions)¶
\+[0-9]+ [0-9]{3}-[0-9]{3}-[0-9]{4} # phone
^[[:alnum:]_.]+@[[:alpha:]]+\.[[:alpha:]]{2,}$ # email
^[^:]+:[^:]+:[^:]+:[1-9][0-9]{2,}:[^:]*:[^:]+:.*$ # passwd GID>=100
^[^:]+:[^:]+:[^:]+:[^:]+:[^:]*:\/home.+:.*$ # /home dir
^[^:]+:[^:]+:[^:]+:[^:]+:[^:]*:[^:]+:\/bin\/bash # bash users
5. find / grep / sed / awk / column¶
# find
find . -regex './.*/[Ss]ource.+\.cpp$' -exec chmod +w {} \;
find . -mtime -1 -type f -name '*.log' -exec grep -l ERROR {} +
# grep
grep -E "(diverg|nan|inf)" run.log
grep -nA 2 -B 1 ERROR run.log
grep -lriE "TODO" --include='*.cpp' src/
# sed
sed -E -i.bak 's/^Re *= *[0-9]+/Re = 5000/' in.dat
sed -n '/^ERROR/p' run.log
sed -E 's/([A-Za-z]+)_([A-Za-z]+)/\2_\1/' names.txt
# awk
awk -F, 'NR>1 && $4=="Engineering" && $6>60000 {print $1,$2,$3}' employees.csv
awk 'BEGIN{FS=",";OFS="\t"} /Marketing/ {print $1,$2,$3}' employees.csv
awk '{s+=$2} END {print "sum =", s}' file
awk '{a[$1]++} END {for (k in a) print k, a[k]}' words.txt
# column
column -t -s ":" /etc/passwd > pretty.txt
column -t -s , file.csv | head
# diff
diff -u a.txt b.txt
diff -q dirA dirB
6. Bash Scripting¶
Hello & arguments (example02.sh + example03.sh)¶
Conditional (example06.sh)¶
if [ ${1} -lt 2006 ]; then
echo "above 18"
elif [ ${1} -eq 2006 ]; then
echo "exactly 18"
else
echo "below 18"
fi
Regex test (example07.sh)¶
Case (example08.sh)¶
echo "1.start 2.stop 3.list"
read -p "choice: " c
case $c in
1) echo "started" ;;
2) echo "stopped" ;;
3) ls ;;
*) echo "invalid" ;;
esac
Array (example09.sh)¶
Loops (example10–13.sh)¶
count=1
while [ $count -le 5 ]; do echo $count; ((count++)); done
for ((i=0;i<5;i++)); do echo $i; done
for x in "${A[@]}"; do echo $x; done
count=1
until [ $count -ge 5 ]; do echo $count; ((count++)); done
getopts (example15.sh)¶
OPTSTRING=":a:b:"
while getopts "$OPTSTRING" opt; do
case "$opt" in
a) echo "a=$OPTARG" ;;
b) echo "b=$OPTARG" ;;
:) echo "-$OPTARG needs arg"; exit 1 ;;
?) echo "invalid -$OPTARG"; exit 1 ;;
esac
done
shift $((OPTIND-1))
Function (example18.sh)¶
is_even(){ if (($1 % 2 == 0)); then return 1; else return 0; fi; }
is_even 4
RES=$?
[ $RES -eq 1 ] && echo "even" || echo "odd"
Prime (E06 Task 1, fixed regex)¶
read -p "Enter a number: " N
if ! [[ "$N" =~ ^[0-9]+$ ]]; then echo "positive int only"; exit 1; fi
PRIME=1
(( N<2 )) && PRIME=0
for ((i=2; i*i<=N; i++)); do
(( N % i == 0 )) && { PRIME=0; break; }
done
(( PRIME )) && echo "$N is prime" || echo "$N is not prime"
Backup-data (full)¶
#!/bin/bash
OPTSTRING=":vs"; VERBOSE=0; SYNC=0
USAGE(){ echo "Error: ${1}"; echo "Usage: ./backup-data [-v] [-s] SRC DST"; }
while getopts "$OPTSTRING" opt; do
case "$opt" in
v) VERBOSE=1 ;;
s) SYNC=1 ;;
?) USAGE "Invalid option"; exit 1 ;;
esac
done
shift $((OPTIND-1))
SRC=${1}; DST=${2}
[[ -d "$SRC" && -d "$DST" ]] || { USAGE "Bad dirs"; exit 1; }
if (( SYNC )); then
if (( VERBOSE )); then rsync -aPv "$SRC" "$DST"; else rsync -aP "$SRC" "$DST"; fi
else
if (( VERBOSE )); then cp -vr "$SRC" "$DST"; else cp -r "$SRC" "$DST"; fi
fi
Parameter sweep skeleton¶
#!/bin/bash
set -euo pipefail
TEMPLATE="${1:-case_template}"
RE_LIST=(500 1000 5000 10000)
for RE in "${RE_LIST[@]}"; do
DIR="case_Re${RE}"
rm -rf "$DIR"; cp -r "$TEMPLATE" "$DIR"
sed -i -E "s/^(Re *= *).*/\1${RE}/" "$DIR/in.dat"
( cd "$DIR"; ./solver > run.log 2>&1 ) &
done
wait
7. Gnuplot¶
Basic plot¶
plot 'linear_data.txt' using 1:2 with linespoints title 'data'
set title "Linear Plot"
set xlabel "x"; set ylabel "y"
set xrange [0:12]; set yrange [0:22]
set grid; set key left top
set style line 1 lt 1 lw 2 pt 7 ps 1.5 lc rgb 'red'
plot 'linear_data.txt' using 1:2 with linespoints linestyle 1
set terminal png; set output 'linear_plot.png'; replot
Multi-curve (E07 Task 2.1)¶
set style line 1 lt 1 lw 2 lc rgb "red" pt 7
set style line 2 lt 2 lw 2 lc rgb "blue" pt 5
plot 'quadratic.txt' u 1:2 w lp ls 1 t 'q', \
'exponential.txt' u 1:2 w lp ls 2 t 'e'
Multiplot 2x2¶
set multiplot layout 2,2 title "Sweep"
plot 'q.txt' u 1:2 w lp t 'quad'
plot 'e.txt' u 1:2 w lp t 'exp'
plot 's.txt' u 1:2 w lp t 'sin'
plot 'l.txt' u 1:2 w lp t 'lin'
unset multiplot
set terminal png; set output 'multi.png'; replot
Curve fit¶
f(x) = a*x**2 + b*x + c
fit f(x) 'noisy.txt' using 1:2 via a,b,c
plot 'noisy.txt' u 1:2 t 'data' w p, \
f(x) t sprintf('Fit: %.2fx²+%.2fx+%.2f',a,b,c) w l
Error bars¶
Bash automation¶
for f in datasets/*.txt; do
name=$(basename "$f" .txt)
gnuplot <<EOF
set terminal png; set output "datasets/$name.png"
set title "$name"
plot '$f' using 1:2 with linespoints title '$name'
EOF
done
8. Git¶
git --version
git config --global user.name "Debwashis"
git config --global user.email "debwa.web@gmail.com"
mkdir my_project && cd my_project
git init
echo "# My Project" > README.md
git add README.md
git commit -m "Initial commit with README.md"
git log --oneline --graph --all
# Branch
git switch -c feature
echo "## Feature" >> README.md
git add README.md
git commit -m "Add feature"
git switch main
git merge feature
git branch -d feature
# Remote
git remote add origin git@git.rz.tu-bs.de:user/proj.git
git push -u origin main
# Tag
git tag -a v1.0 -m "Release"
git push --tags
# Undo
git restore file
git reset --soft HEAD~1
git revert <hash>
# Conflict
git pull origin main # → conflict
# edit markers, then:
git add file
git commit -m "Resolve conflict"
git push
9. C++ Examples (line-by-line)¶
Hello (example01.cpp)¶
Compile: g++ example01.cpp -o ex01 && ./ex01
Sizes (example02.cpp)¶
#include <iostream>
int main(){
int a=34; char b='k'; float c=32.56f;
std::cout<<"size int="<<sizeof(int)<<"\n";
std::cout<<"size char="<<sizeof(char)<<"\n";
std::cout<<"size float="<<sizeof(float)<<"\n";
}
Pointer (example05.cpp)¶
Pointer arithmetic (example07.cpp)¶
Function (example09.cpp)¶
bool is_prime(int n){
if(n<2) return false;
for(int i=2;i<n;++i) if(n%i==0) return false;
return true;
}
Call by value vs reference (example10.cpp)¶
void byVal(int a){a++;}
void byRef(int& a){a++;}
int main(){
int a=34; byVal(a); std::cout<<a<<"\n"; // 34
byRef(a); std::cout<<a<<"\n"; // 35
}
Swap with pointers (E09 Task 1)¶
Complex (E09 Task 2)¶
class Complex {
private:
float a,b;
public:
Complex(float x,float y):a(x),b(y){}
void display(){ std::cout<<a<<"+"<<b<<"i\n"; }
float real() const { return a; }
float img() const { return b; }
Complex add(const Complex& c1, const Complex& c2){
return Complex(c1.real()+c2.real(), c1.img()+c2.img());
}
Complex multiply(const Complex& c1, const Complex& c2){
return Complex(c1.real()*c2.real() - c1.img()*c2.img(),
c1.real()*c2.img() + c1.img()*c2.real());
}
float modulus(){ return std::sqrt(a*a+b*b); }
};
10. Build / Make / CMake¶
Stages¶
Header guard¶
Makefile (E10)¶
CXX=g++
CXXFLAGS=-O2 -Wall -std=c++17 -Iinclude
OBJ=build/main.o build/add.o build/sub.o build/mult.o build/mod.o
bin/prog: $(OBJ) | bin
$(CXX) $(CXXFLAGS) -o $@ $^
build/%.o: src/%.cpp include/Complex.h | build
$(CXX) $(CXXFLAGS) -c $< -o $@
bin build: ; mkdir -p $@
.PHONY: clean run
clean: ; rm -rf build bin
run: bin/prog ; ./bin/prog
Lecture's Makefile (demo02 — shared libs)¶
LIBPATH=./lib
INCLUDE=./include
./bin/main: ./src/main.cpp lib/libadd.so lib/libsub.so lib/libmult.so
g++ -I$(INCLUDE) -L$(LIBPATH) -o ./bin/main ./src/main.cpp -ladd -lsub -lmult
lib/libadd.so: ./src/add.cpp; g++ -fPIC -shared -o $@ $<
lib/libsub.so: ./src/sub.cpp; g++ -fPIC -shared -o $@ $<
lib/libmult.so: ./src/mult.cpp; g++ -fPIC -shared -o $@ $<
clean: ; rm -f lib/*.so ./bin/*
CMake (E10 alt)¶
cmake_minimum_required(VERSION 3.16)
project(complex_demo CXX)
set(CMAKE_CXX_STANDARD 17)
add_library(complex STATIC src/add.cpp src/sub.cpp src/mult.cpp src/mod.cpp)
target_include_directories(complex PUBLIC include)
add_executable(prog src/main.cpp)
target_link_libraries(prog PRIVATE complex)
Build: cmake -B build && cmake --build build && ./build/prog.
11. Debugging¶
gdb session for off-by-one¶
g++ -O0 -g debug01.cpp -o d1
gdb ./d1
(gdb) break 8
(gdb) run
(gdb) display sum
(gdb) display i
(gdb) n
... (until i==5 reads OOB)
gdb session for NULL deref¶
valgrind for leak¶
Sanitizer¶
Mapper2D (E11) — fix¶
int xyToPos(int x, int y, int sx, int sy){ return x + sx*y; }
int posToX (int p, int sx, int sy){ return p % sx; }
int posToY (int p, int sx, int sy){ return p / sx; }
12. MPI / OpenMP¶
MPI hello (example01.cpp)¶
#include <mpi.h>
#include <iostream>
int main(int argc,char**argv){
int rank;
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD,&rank);
std::cout<<"Rank "<<rank<<"\n";
MPI_Finalize();
}
mpic++ -O2 example01.cpp -o ex01 && mpirun -n 4 ./ex01
MPI loop split (example02.cpp)¶
int per=floor((double)numbers/n_ranks);
if (numbers % n_ranks > 0) per++;
int first=rank*per, last=first+per;
for (int i=first;i<last;++i) if(i<numbers) ...
MPI Send/Recv int (example03_int.cpp)¶
if (rank==0){ int m=42; MPI_Send(&m,1,MPI_INT,1,0,MPI_COMM_WORLD);}
if (rank==1){ int m; MPI_Status s; MPI_Recv(&m,1,MPI_INT,0,0,MPI_COMM_WORLD,&s); std::cout<<m;}
MPI sum¶
long sum, local = rank;
MPI_Reduce(&local,&sum,1,MPI_LONG,MPI_SUM,0,MPI_COMM_WORLD);
if (rank==0) std::cout<<"sum = "<<sum<<"\n";
OpenMP reduction¶
#include <omp.h>
double sum=0;
#pragma omp parallel for reduction(+:sum)
for(int i=0;i<N;++i) sum += a[i];
g++ -O3 -fopenmp prog.cpp -o prog && OMP_NUM_THREADS=8 ./prog
Race fix three ways¶
#pragma omp parallel for reduction(+:sum) // best
#pragma omp atomic // good
#pragma omp critical // worst (slow)
Hybrid SBATCH¶
#SBATCH --nodes=4
#SBATCH --ntasks-per-node=2
#SBATCH --cpus-per-task=8
export OMP_NUM_THREADS=$SLURM_CPUS_PER_TASK
mpirun -n $SLURM_NTASKS ./solver
13. SLURM¶
sinfo
squeue -u $USER
sbatch run.sbatch
scancel 12345
salloc --nodes=1 --time=01:00:00
srun --pty bash
scontrol show job 12345
sacct -j 12345 --format=JobID,State,Elapsed,MaxRSS
Job script¶
#!/bin/bash
#SBATCH --job-name=cfd
#SBATCH --partition=standard
#SBATCH --nodes=4
#SBATCH --ntasks-per-node=16
#SBATCH --cpus-per-task=2
#SBATCH --time=04:00:00
#SBATCH --output=cfd_%j.log
module purge
module load gcc/12 openmpi/4.1
export OMP_NUM_THREADS=$SLURM_CPUS_PER_TASK
cd $SLURM_SUBMIT_DIR
mpirun -n $SLURM_NTASKS ./solver in.dat
Array job¶
#SBATCH --array=0-9
RE=(500 1000 2000 5000 10000 20000 50000 100000 200000 500000)
mkdir -p case_Re${RE[$SLURM_ARRAY_TASK_ID]} && cd $_
cp ../template/* . && sed -i -E "s/^Re=.*/Re=${RE[$SLURM_ARRAY_TASK_ID]}/" in.dat
mpirun -n $SLURM_NTASKS ../solver in.dat
Modules¶
module avail
module list
module spider gcc
module load gcc/12 openmpi/4.1 cmake/3.27
module switch openmpi/4.1 openmpi/5.0
module purge
14. Mini Projects¶
Permission playground (Ch. 3)¶
mkdir perm_demo && cd perm_demo
echo hi > public.txt && chmod 644 public.txt
echo secret > secret.txt && chmod 600 secret.txt
echo '#!/bin/bash\necho "Hello $USER"' > greet.sh && chmod 755 greet.sh
mkdir shared && chmod 2775 shared # setgid
ls -l
./greet.sh
Department salary report (Ch. 7)¶
awk -F, 'NR>1' employee_database.csv > clean.csv
awk -F, '{sum[$4]+=$6; cnt[$4]++} END{for(d in sum) printf "%-15s %.2f\n", d, sum[d]/cnt[d]}' clean.csv \
| sort -k2 -nr | column -t > avg_salary.txt
Re-sweep with sbatch (Ch. 15) — see §13.¶
Mini Monte-Carlo π (Ch. 14)¶
See Chapter 14 §12.
End of practice file.