Machine Learning

Building a Navier-Stokes Solver in Python from Scratch: Airflow Simulation

(CFD) is often seen as a black box of complex trading software. However, using the solver “from scratch” is one of the most powerful ways to learn the physics of fluid motion. I started this as a personal project, and as part of a Biophysics course, I took it as an opportunity to finally understand how these beautiful analogies work.

This guide is designed for data scientists and engineers who want to go beyond high-level libraries and understand the basic mechanics of numerical simulation by translating discrete mathematical components into logical Python code. We will also explore basic programming concepts such as vectorized functions with NumPy and stochastic convergence, which are essential skills for anyone interested in the scope of scientific computing and machine learning architecture.

We will walk through the derivation and Python implementation of a simple incompressible Navier-Stokes (NS) solver. Then, we will use this solver to simulate the airflow along the airfoil of a bird.

Physics: Incompressible Navier-Stokes

The fundamental equations of CFD are the Navier-Stokes equations, which describe how velocity and pressure develop in a fluid. In steady flight (like a diving bird), we assume that the air is incompressible (constant density) and laminar. It can be understood as Newton's law of motion, but as an infinite part of a fluid, which has a force that affects it. That's usually pressure and viscosity, but depending on the context you can add in gravity, mechanical pressure, or even electromagnetism if you feel like it. The author can prove this is not recommended for the first project.

The equations in vector form are:

[
frac{partial mathbf{v}}{partial t} + (mathbf{v} cdot nabla)mathbf{v} = -frac{1}{rho}nabla p + nu nabla^2 mathbf{v} \
nabla cdot mathbf{v} = 0
]

Where:

  • v: Velocity field (u,v)
  • p: Pressure
  • ρ: Density of liquid
  • ν: The kinematic viscosity

The first equation (Momentum) balances inertia against gradients and viscous diffusion. The second equation (Continuity) emphasizes that the density of the fluid remains constant.

Pressure Consolidation Problem

A major challenge in CFD is that pressure and velocity are coupled: the pressure field must be constantly adjusting to ensure that the fluid remains incompressible.

To solve this, we get a Pressure-Poisson equation by taking the divergence of the momentum equation. In an intelligent solver, we solve this Poisson equation each time to update the pressure, ensuring that the velocity field remains invariant.

Discretization: From Math to Grid

To solve these equations on a computer, we use The Complete Difference schemes on the same grid.

  • Time: First difference (explicit Euler).
  • Advection (Indirect terms): Rear/Top Differential (for stability).
  • Distribution and Compression: Average difference.

For example, the update formula for the u (x-velocity) component looks like this in finite difference form.

[
u_{i,j}^n frac{u_{i,j}^n - u_{i-1,j}^n}{Delta x}
]

In code, the adword u∂x∂u​ uses the reverse difference:

[
u_{i,j}^n frac{u_{i,j}^n - u_{i-1,j}^n}{Delta x}
]

Python Implementation

The implementation proceeds in four separate steps using NumPy arrays.

1. Implementation

We define the grid size (nx, ny), time step (dt), and visual parameters (rho, nu). We initialize the velocity (u,v) and pressure (p) fields to the egg or similar flow.

2. Wing Geometry (Dipped Edge)

To simulate a wing on a Cartesian grid, we need to mark which grid points inside strong wing.

  • We load the wing mesh (eg, from an STL file).
  • We create a Boolean mask group there True indicates a point within the wing.
  • During the simulation, we force the velocity to zero at these masked points (no-slip/no-penetration condition).

3. Main Solver Loop

The main loop repeats until the solution reaches a steady state. The steps are:

  1. Form Source Term (b): Calculate the difference in speed terms.
  2. Relieve Stress: Solve Poisson's equation for p using Jacobi iteration.
  3. Update speed: Use new stress to revise iu and v.
  4. Apply Boundary Conditions: Force entry velocity and zero velocity inside the wing.

The code

Here's how key math updates look like in Python (vectorized for performance).

Step A: Constructing a Pressure Source Timeline This represents the Right Hand Side (RHS) of the Poisson equation based on the current velocity.

# b is the source term
# u and v are current velocity arrays
b[1:-1, 1:-1] = (rho * (
    1 / dt * ((u[1:-1, 2:] - u[1:-1, 0:-2]) / (2 * dx) +
              (v[2:, 1:-1] - v[0:-2, 1:-1]) / (2 * dy)) -
    ((u[1:-1, 2:] - u[1:-1, 0:-2]) / (2 * dx))**2 -
    2 * ((u[2:, 1:-1] - u[0:-2, 1:-1]) / (2 * dy) *
         (v[1:-1, 2:] - v[1:-1, 0:-2]) / (2 * dx)) -
    ((v[2:, 1:-1] - v[0:-2, 1:-1]) / (2 * dy))**2
))

Step B: Stress Solving (Jacobi Iteration) We repeat the smoothing of the pressure field until it approximates the source name.

for _ in range(nit):
    pn = p.copy()
    p[1:-1, 1:-1] = (
        (pn[1:-1, 2:] + pn[1:-1, 0:-2]) * dy**2 +
        (pn[2:, 1:-1] + pn[0:-2, 1:-1]) * dx**2 -
        b[1:-1, 1:-1] * dx**2 * dy**2
    ) / (2 * (dx**2 + dy**2))
# Boundary conditions: p=0 at edges (gauge pressure)
    p[:, -1] = 0; p[:, 0] = 0; p[-1, :] = 0; p[0, :] = 0

Step C: Speed ​​Review Finally, we update the velocity using the implicit momentum equation.

un = u.copy()
vn = v.copy() 
# Update u (x-velocity)
u[1:-1, 1:-1] = (un[1:-1, 1:-1] -
                 un[1:-1, 1:-1] * dt / dx * (un[1:-1, 1:-1] - un[1:-1, 0:-2]) -
                 vn[1:-1, 1:-1] * dt / dy * (un[1:-1, 1:-1] - un[0:-2, 1:-1]) -
                 dt / (2 * rho * dx) * (p[1:-1, 2:] - p[1:-1, 0:-2]) +
                 nu * (dt / dx**2 * (un[1:-1, 2:] - 2 * un[1:-1, 1:-1] + un[1:-1, 0:-2]) +
                       dt / dy**2 * (un[2:, 1:-1] - 2 * un[1:-1, 1:-1] + un[0:-2, 1:-1])))
# Update v (y-velocity)
v[1:-1, 1:-1] = (vn[1:-1, 1:-1] -
                 un[1:-1, 1:-1] * dt / dx * (vn[1:-1, 1:-1] - vn[1:-1, 0:-2]) -
                 vn[1:-1, 1:-1] * dt / dy * (vn[1:-1, 1:-1] - vn[0:-2, 1:-1]) -
                 dt / (2 * rho * dy) * (p[2:, 1:-1] - p[0:-2, 1:-1]) +
                 nu * (dt / dx**2 * (vn[1:-1, 2:] - 2 * vn[1:-1, 1:-1] + vn[1:-1, 0:-2]) +
                       dt / dy**2 * (vn[2:, 1:-1] - 2 * vn[1:-1, 1:-1] + vn[0:-2, 1:-1])))

Results: Does It Fly?

We applied this solver to a rigid wing profile with constant remote field penetration.

Appropriate Observation The results are consistent with physical expectations. The simulation shows high pressure below the wing and low pressure above it, which is what produces lift. Velocity vectors show air flow increasing in speed over a surface (Bernoulli's principle).

Strength: Lift vs. Drag By integrating the pressure field over the surface of the wing, we can calculate the altitude.

  • The referee shows that the pressure force dominates The force of viscous friction is about a factor of 1000x that of air.
  • As the angle of attack increases (from 0∘ to −20∘), the lift-to-drag ratio increases, the same trends observed for wind tunnels and professional CFD packages such as OpenFOAM.

Limitations and Next Steps

While implementing this solution was great for learning, the tool itself has its limitations:

  • Solution: 3D simulations on a Cartesian grid are computationally expensive and require coarser grids, making quantitative results less reliable.
  • Chaos: The solution is laminar; it lacks the turbulence model (such as k−ϵ) required for high-speed or complex flows.
  • Distribution: Different Upwind schemes are stable but numerically different, which may “smear” the details of positive flow.

Where can you go from here? This project serves as a starting point. Future improvements may include using higher-order advection schemes (like WENO), adding turbulence modeling, or moving to Finite Volume methods (like OpenFOAM) for better mesh handling in complex geometries. There are many clever tricks to get around a lot of situations that you might want to use. This is the first step towards really understanding CFD!

Source link

Related Articles

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button