Interactive 2D fluid simulation lab using Fortran, MPI, CUDA, FastAPI, Docker, and a web UI.
This project was built for my Parallel Programming project. The goal is to show how the same CFD-style workload behaves on different execution models:
The app runs a 2D incompressible fluid simulation and visualizes a moving aerospace-style body with a vorticity wake and streamlines. It is not meant to be a full industrial CFD solver, but it is more than a basic classroom example because it includes numerical methods, MPI decomposition, CUDA acceleration, benchmarking, and a live browser interface.
Fluid Lab CFD allows the user to:
The interface is kept simple on purpose. The main controls are grid size, iteration count, execution mode, and MPI process count.
The simulation is based on the 2D incompressible Navier-Stokes equations using the vorticity-streamfunction formulation.
Instead of solving directly for pressure, the solver uses:
∂ω/∂t + u ∂ω/∂x + v ∂ω/∂y = ν ∇²ω
∇²ψ = -ω
u = ∂ψ/∂y
v = -∂ψ/∂x
Where:
ω = vorticity
ψ = streamfunction
u, v = velocity components
ν = viscosity
This formulation is useful for a 2D fluid demo because the pressure term is avoided and the velocity field can be recovered from the streamfunction.
The solver uses a finite difference grid.
Main numerical parts:
The Poisson equation is solved approximately using Jacobi iterations:
ψ(i,j) = 1/4 * (ψ(i+1,j) + ψ(i-1,j) + ψ(i,j+1) + ψ(i,j-1) + h²ω(i,j))
Velocity is calculated from the streamfunction:
u = (ψ(i,j+1) - ψ(i,j-1)) / 2h
v = -(ψ(i+1,j) - ψ(i-1,j)) / 2h
The vorticity update combines advection, diffusion, and a moving body wake term.
The aerospace body is handled with an immersed-boundary-inspired mask. This is a simplified way to represent a moving object inside the flow without making a full body-fitted mesh.
The CPU version is the baseline solver.
It is written in Fortran and compiled with optimization flags. This gives a clean baseline for comparing MPI and CUDA.
The MPI version uses row-wise domain decomposition.
Each MPI process owns part of the grid. Neighboring ranks exchange halo rows so the finite difference stencil can use boundary data from adjacent partitions.
The important MPI idea is:
each rank computes its own rows
halo rows are exchanged between neighbouring ranks
rank 0 gathers the final result
The Poisson solver needs repeated halo exchange, so MPI speedup is not perfectly linear. This is expected and important for the project.
In practice:
more MPI processes does not always mean perfect speedup
At higher process counts, communication overhead becomes more visible.
The web interface also detects the available CPU core count and warns the user if the selected MPI process count oversubscribes the machine.
Oversubscription is allowed, but it may reduce performance because multiple MPI ranks share the same CPU cores.
The CUDA version runs the stencil-style CFD workload on the GPU.
This is useful because many grid cells can be updated in parallel. For larger grids, CUDA can give much higher speedup compared with the CPU baseline.
CUDA mode requires:
The CUDA preview uses real CUDA runtime, but the visual preview is cleaned so the browser shows a readable wake visualization. Benchmark mode still reports real solver timing.
There are two Docker images.
This image supports CPU Fortran and MPI.
It is multi-architecture:
linux/amd64
linux/arm64
Run:
docker run --rm --shm-size=2g -p 8000:8000 irfanuruchi/fluid-lab-cfd:cpu
This image is useful for normal machines, including x64 and ARM64 systems.
This image supports CPU, MPI, and CUDA.
It is for NVIDIA GPU machines:
linux/amd64
Run:
docker run --rm --gpus all --shm-size=2g -p 8000:8000 irfanuruchi/fluid-lab-cfd:cuda
The latest tag points to the CUDA image:
docker run --rm --gpus all --shm-size=2g -p 8000:8000 irfanuruchi/fluid-lab-cfd:latest
After starting the container, open:
http://localhost:8000
docker build -t fluid-lab .
docker run --rm --gpus all --shm-size=2g -p 8000:8000 fluid-lab
docker build -f Dockerfile.cpu -t fluid-lab-cpu .
docker run --rm --shm-size=2g -p 8000:8000 fluid-lab-cpu
For preview:
Grid: 480 - 1000
Iterations: 1200
MPI processes: 4 - 12
For benchmark:
Grid: 720 - 1800
Iterations: 3000+
MPI processes: depends on CPU cores
For large MPI runs inside Docker, this is recommended:
--shm-size=2g
Without larger shared memory, large MPI workloads can fail inside Docker.
Example result from testing:
CPU baseline: 10.35 sec
CUDA GPU runtime: 0.2045 sec
CUDA speedup: 50.62x
MPI 24 processes runtime: 0.8776 sec
MPI speedup: 11.79x
MPI efficiency: 49%
This result shows the main idea of the project:
MPI efficiency decreases because the solver needs communication during halo exchange, especially during the Poisson iterations.
fluid-project/
├── backend/
│ └── main.py
├── solvers/
│ ├── fortran_cpu/
│ │ └── fluid_cpu.f90
│ ├── fortran_mpi/
│ │ └── fluid_mpi.f90
│ └── cuda_gpu/
│ └── fluid_cuda.cu
├── web/
│ ├── index.html
│ ├── app.js
│ └── style.css
├── Dockerfile
├── Dockerfile.cpu
├── requirements.txt
└── README.md
This is a student HPC/CFD project, not a production CFD solver.
The goal is to demonstrate the connection between numerical methods and parallel execution:
The project also shows that parallel programming is not only about using more cores. Memory layout, communication cost, workload size, synchronization, and the execution model all matter.
Built by Irfan Uruchi as a Parallel Programming project.
Content type
Image
Digest
sha256:16af100b1…
Size
242 MB
Last updated
4 months ago
docker pull irfanuruchi/fluid-lab-cfd