Skip to content

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

wc -l file       # lines
wc -w file       # words
wc -c file       # bytes

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

qa /[<Enter> v /]<Enter> d 0 /><Enter> p 0 q
@a    or    4@a

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)

#!/bin/bash
echo "Hello $USER"
echo "FIRST  = ${1}"
echo "SECOND = ${2}"
exit 0

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)

PATTERN='^#[[:digit:]]{4}$'
if [[ ${1} =~ ${PATTERN} ]]; then
    echo "matches"
else
    echo "no"
fi

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)

A=(23 45 56 76)
echo ${A[0]}
echo ${A[@]}
echo ${#A[@]}

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

plot 'data.txt' using 1:2:3 with yerrorbars title 'Data ± σ'

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)

#include <iostream>
int main(){ std::cout << "Hello World" << std::endl; return 0; }
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)

int b=23; int *a=&b;
std::cout<<a<<"\n"<<*a<<"\n";

Pointer arithmetic (example07.cpp)

int a[5]={34,76,48,55,7}; int *b=&a[0];
for(int i=0;i<5;++i) std::cout<<(b+i)<<" : "<<*(b+i)<<"\n";

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)

void swap(int* a,int* b){int t=*a; *a=*b; *b=t;}
swap(&a,&b);

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

g++ -E main.cpp -o main.i
g++ -S main.i  -o main.s
g++ -c main.s  -o main.o
g++       main.o -o main

Header guard

#ifndef COMPLEX_H
#define COMPLEX_H
class Complex { ... };
#endif

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

g++ -O0 -g debug03.cpp -o d3
gdb ./d3
(gdb) run     # SIGSEGV
(gdb) bt

valgrind for leak

g++ -O0 -g debug06.cpp -o d6
valgrind --leak-check=full --show-leak-kinds=all ./d6

Sanitizer

g++ -O1 -g -fsanitize=address,undefined prog.cpp -o prog
./prog

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.