From: Ansys Fluent Rust Computational fluid dynamics
Code: https://github.com/A1i98/rust-cfd-lid-driven-cavity
Bind
[[meta bind projects]]Tasks
Sessions BUTTON[add-session] BUTTON[open-latest-session]
Simulation in ANSYS
A two-dimensional square cavity with dimensions of 1 m × 1 m was created in ANSYS DesignModeler to represent the computational domain for the lid-driven cavity simulation.

After the sketch was converted into a surface body, the resulting geometry was assigned the Fluid body type. This step defines the computational domain as a fluid region, allowing ANSYS Fluent to generate the control volumes required for solving the governing flow equations.
A structured quadrilateral mesh was generated with a global element size of 0.01 m. Local mesh refinement was then applied to improve spatial resolution in regions where higher velocity gradients are expected. Finally, appropriate Named Selections were created for all boundaries, including the moving wall (lid) and stationary walls, to facilitate the assignment of boundary conditions in Fluent.


Simulation in Rust
PurposeThis note documents a complete 2D lid-driven cavity flow solver written in Rust. The solver uses a finite-difference staggered layout and an artificial-compressibility pseudo-time iteration to obtain the steady incompressible solution. It also writes CSV, Tecplot, and SVG plot files automatically.
Problem Definition
The classical lid-driven cavity is a square 2D domain of side length . The top wall moves horizontally with velocity , while the other three walls are stationary. The flow is assumed incompressible, laminar, Newtonian, and two-dimensional.
The default case solved by the Rust program is:
| Parameter | Symbol | Default value |
|---|---|---|
| Domain length | ||
| Lid velocity | ||
| Reynolds number | ||
| Grid size | ||
| Pseudo-time step | ||
| Artificial-compressibility parameter |
Physical meaningThe final result is the steady-state incompressible cavity flow. The artificial-compressibility time variable is a pseudo-time used for convergence, not necessarily the physical transient time.
Governing Equations
For a 2D incompressible Newtonian flow, the dimensional governing equations are the continuity equation and the Navier-Stokes momentum equations:
Non-Dimensional Form
Using , , and as reference scales, the equations become:
where:
The Rust code solves the steady incompressible problem through pseudo-time marching. For this reason, the continuity equation is replaced during the iterative process by the artificial-compressibility equation:
At convergence, , so the incompressibility constraint is recovered:
Boundary Conditions
The cavity walls use no-penetration and no-slip boundary conditions.
| Boundary | Velocity condition |
|---|---|
| Top wall / moving lid | , |
| Bottom wall | , |
| Left wall | , |
| Right wall | , |
The pressure uses a zero-normal-gradient condition at the walls:
Because incompressible pressure is defined up to an arbitrary constant, the code subtracts the mean pressure after every pressure update.
Numerical Method
The code uses a staggered finite-difference arrangement similar to a MAC grid:
| Quantity | Storage idea | Purpose |
|---|---|---|
| horizontal velocity stored on vertical control-volume faces | reduces pressure-velocity decoupling | |
| vertical velocity stored on horizontal control-volume faces | improves discrete continuity coupling | |
| pressure-like field at pressure nodes | enforces incompressibility through pseudo-time correction | |
| , , | cell-centered post-processed values | exported for plotting and comparison |
Why staggered storage is usefulOn a collocated grid, pressure and velocity can decouple if the interpolation is not handled carefully. The staggered layout naturally couples pressure gradients and mass fluxes.
Staggered Grid Arrangement
For a grid size n, the Rust solver stores the fields as follows:
| Field | Rust vector size | Logical index range |
|---|---|---|
u | n * (n + 1) | , |
v | (n + 1) * n | , |
p | (n + 1) * (n + 1) | , |
The grid spacing is:
The exported cell-centered values are computed as:
Discrete Equations Used in the Code
X-Momentum Equation
The continuous non-dimensional x-momentum equation is:
The pseudo-time finite-difference update used in the code is:
where:
Y-Momentum Equation
The y-momentum equation is:
The pseudo-time update used in the code is:
where:
Artificial-Compressibility Pressure Update
The discrete pressure update is:
The convergence criterion is based mainly on the mean absolute divergence:
The default stopping criterion is:
Rust Code
Configuration and CLI Arguments
The solver can be controlled from the terminal without editing the source file.
#[derive(Debug, Clone)]struct Config { n: usize, re: f64, lid_velocity: f64, dt: f64, delta: f64, max_steps: usize, min_steps: usize, tolerance: f64, report_every: usize, out_dir: PathBuf, stream_sor_omega: f64, stream_max_iter: usize, stream_tol: f64,}The most important parameters are n, re, dt, delta, and tolerance.
Staggered Indexing
The arrays are one-dimensional Vec<f64> buffers, but the code maps them to 2D staggered indices.
fn iu(&self, i: usize, j: usize) -> usize { // u: i = 0..n-1, j = 0..n j * self.cfg.n + i}
fn iv(&self, i: usize, j: usize) -> usize { // v: i = 0..n, j = 0..n-1 j * (self.cfg.n + 1) + i}
fn ip(&self, i: usize, j: usize) -> usize { // p: i = 0..n, j = 0..n j * (self.cfg.n + 1) + i}X-Momentum Update
This section applies convection, pressure gradient, and viscous diffusion for the horizontal velocity.
let u_ij = self.u[self.iu(i, j)];
let duu_dx = (self.u[self.iu(i + 1, j)].powi(2) - self.u[self.iu(i - 1, j)].powi(2)) / (2.0 * dx);
let uv_n = 0.25 * (self.u[self.iu(i, j)] + self.u[self.iu(i, j + 1)]) * (self.v[self.iv(i, j)] + self.v[self.iv(i + 1, j)]);let uv_s = 0.25 * (self.u[self.iu(i, j)] + self.u[self.iu(i, j - 1)]) * (self.v[self.iv(i, j - 1)] + self.v[self.iv(i + 1, j - 1)]);let duv_dy = (uv_n - uv_s) / dy;
let dp_dx = (self.p[self.ip(i + 1, j)] - self.p[self.ip(i, j)]) / dx;
let lap_u = (self.u[self.iu(i + 1, j)] - 2.0 * u_ij + self.u[self.iu(i - 1, j)]) / dx2 + (self.u[self.iu(i, j + 1)] - 2.0 * u_ij + self.u[self.iu(i, j - 1)]) / dy2;
let new_u = u_ij - dt * (duu_dx + duv_dy + dp_dx) + dt * inv_re * lap_u;Y-Momentum Update
This section is the analogous update for vertical velocity.
let uv_e = 0.25 * (self.u[self.iu(i, j)] + self.u[self.iu(i, j + 1)]) * (self.v[self.iv(i, j)] + self.v[self.iv(i + 1, j)]);let uv_w = 0.25 * (self.u[self.iu(i - 1, j)] + self.u[self.iu(i - 1, j + 1)]) * (self.v[self.iv(i, j)] + self.v[self.iv(i - 1, j)]);let duv_dx = (uv_e - uv_w) / dx;
let dvv_dy = (self.v[self.iv(i, j + 1)].powi(2) - self.v[self.iv(i, j - 1)].powi(2)) / (2.0 * dy);
let dp_dy = (self.p[self.ip(i, j + 1)] - self.p[self.ip(i, j)]) / dy;
let lap_v = (self.v[self.iv(i + 1, j)] - 2.0 * v_ij + self.v[self.iv(i - 1, j)]) / dx2 + (self.v[self.iv(i, j + 1)] - 2.0 * v_ij + self.v[self.iv(i, j - 1)]) / dy2;
let new_v = v_ij - dt * (duv_dx + dvv_dy + dp_dy) + dt * inv_re * lap_v;Pressure and Continuity Update
The pressure equation drives the divergence toward zero.
for i in 1..=(n - 1) { for j in 1..=(n - 1) { let divergence = (self.un[self.iu(i, j)] - self.un[self.iu(i - 1, j)]) / dx + (self.vn[self.iv(i, j)] - self.vn[self.iv(i, j - 1)]) / dy; let idx = self.ip(i, j); self.pn[idx] = self.p[idx] - dt * delta * divergence; }}Boundary Conditions
The top lid is imposed through a ghost-cell relation. For the moving lid:
therefore:
for i in 0..n { let bottom = self.iu(i, 0); let bottom_inside = self.iu(i, 1); let top = self.iu(i, n); let top_inside = self.iu(i, n - 1); self.un[bottom] = -self.un[bottom_inside]; self.un[top] = 2.0 * self.cfg.lid_velocity - self.un[top_inside];}Post-Processing Fields
The solver computes vorticity as:
It also computes a streamfunction from:
The post-processing step writes the final fields, centerline profiles, and plots.
How to Run
1. Build in Release Mode
cargo build --release2. Run the Default Case
cargo run --release -- --n 65 --re 100 --dt 0.001 --delta 4.5 --out results
3. Quick Test Case
For a fast check before the final run:
cargo run --release -- --n 33 --re 100 --max-steps 30000 --out quick_results4. Useful Parameter Changes
| Goal | Suggested command option |
|---|---|
| Finer grid | --n 129 |
| Lower Reynolds number | --re 10 |
| Higher Reynolds number | --re 400 |
| More strict convergence | --tol 1e-9 |
| Faster rough run | --tol 1e-6 --n 33 |
| Change output folder | --out my_results |
Stability warningFor larger Reynolds numbers or finer grids, reduce
--dt. A safe first adjustment is to halvedtwhen the residual oscillates or grows.
Output Files
After running, the solver creates an output folder such as results/.
| File | Description |
|---|---|
run_config.txt | Simulation parameters and final step |
convergence.csv | Iteration history: divergence and velocity change |
field.csv | Full field data: x,y,u,v,p,speed,vorticity,streamfunction |
field_tecplot.dat | Tecplot-compatible field file |
centerline_u_vertical.csv | profile along |
centerline_v_horizontal.csv | profile along |
images/u_velocity.svg | SVG heatmap for |
images/v_velocity.svg | SVG heatmap for |
images/speed.svg | SVG heatmap for velocity magnitude |
images/pressure.svg | SVG heatmap for pressure |
images/vorticity.svg | SVG heatmap for vorticity |
images/streamfunction.svg | SVG heatmap for streamfunction |
images/centerline_u_vertical.svg | Centerline profile plot |
images/centerline_v_horizontal.svg | Centerline profile plot |
images/convergence.svg | Convergence history plot |
Plot
Velocity Magnitude

Figure 1. Velocity magnitude field.
Horizontal Velocity Contour

Figure 2. Horizontal velocity field.
Vertical Velocity Contour

Figure 3. Vertical velocity field.
Pressure Field

Figure 4. Pressure field from artificial-compressibility iteration.
Vorticity Field

Figure 5. Vorticity field.
Streamfunction Field

Figure 6. Streamfunction field.
Vertical Centerline U Profile

Figure 7. velocity profile along .
Horizontal Centerline V Profile

Figure 8. velocity profile along .
Convergence History

Figure 9. Convergence history based on mean absolute divergence.
Verification and Comparison with ANSYS Fluent
Numerical Stability Notes
The explicit pseudo-time update is simple and transparent, but stability depends on grid size, Reynolds number, and pseudo-time step.
A conservative starting point is:
For the default case:
The default is therefore conservative for .
Higher-Reynolds-number warningAt higher Reynolds numbers, the cavity develops thinner boundary layers and stronger gradients. Use a finer grid and reduce
dt. For example, try--n 129 --dt 0.0005as a starting point for .
References
- Ghia, U., Ghia, K. N., & Shin, C. T. (1982). High-Re solutions for incompressible flow using the Navier-Stokes equations and a multigrid method. Journal of Computational Physics, 48(3), 387-411.
- Peyret, R., & Taylor, T. D. (1983). Computational Methods for Fluid Flow. Springer.
- Ferziger, J. H., Perić, M., & Street, R. L. (2020). Computational Methods for Fluid Dynamics. Springer.
- Patankar, S. V. (1980). Numerical Heat Transfer and Fluid Flow. Hemisphere Publishing.