Fluid Flow Simulation with the Navier-Stokes Equation


Click and drag to add velocity to the simulation. The color is the pressure.

The Navier-Stokes equation is below

ρDuDt=p+𝛕+ρg

ρ is density.
u is the velocity vector.
p is the pressure.
𝛕 is the stress tensor.
g is any body acceleration on the particles.

For this simulation the only body acceleration 𝐠 is the mouse dragging. We can just ignore it.

ρDuDt=p+𝛕

The material derivative for a velocity field is defined below.

DDt=t+u·

So original equation expanded is below.

ρ(ut+uu)=p+𝛕

The stress tensor is defined as

τ=μ(u+(u))+λ(u)I

where I is the identity matrix, and μ,λ are viscosity constants. Simple algebreic manipulation yields a solution for the change in time.

ut=1ρp+1ρ𝛕u·u

Incompressable constraint

But we still need to define what p is. To do this we add the additional constraint that the fluid is incompressable, meaning the density ρ is constant over space and time.

ρt=0
ρ=0

The change in density follows from the continuity equation in a velocity field.

ρt+(ρu)=0

The time derivative is zero from our constraint, and we apply the chain rule on the right.

ρu+uρ=0

And the gradient is zero also zero from our constraint, and we can divide out the density (assuming it isn't zero).

u=0

Thus our incompressability condition implies that the velocity field has zero divergence.

Pressure

Now we can try to find solve for pressure given the constraint that divergence of velocity is zero everywhere. First start with the time evolution equation with the stress tensor expanded.

ut=1ρp+1ρμ(u+(u))+1ρλ(u)Iu·u

The λ term is equal to zero since the velocity divergence is zero by the constriant.

ut=1ρp+1ρμ(u+(u))u·u

Distribute the divergence operator.

ut=1ρp+1ρμ(u+(u))+1ρλ(u)Iu·u

And (u)=(u)=0 because once again the divergence is defined to be zero.

ut=1ρp+1ρμuu·u

Then take the divergence of both sides

ut=1ρp+1ρμu(u·u)

And derivatives usually commute unless it's some really weird function. So we can move the divergence inside on the left hand side. Also the middle term is zero because u=(u)=0.

ut=1ρp(u·u)

And once again the gradient is always zero so the left is zero.

0=1ρp(u·u)

So we get a constraint on the pressure.

p=ρ(u·u)

Uniqueness of the solution for pressure

We need to show that the formula above should give p uniquelly up to a constant. Consider two solutions

p1=ρ(u·u)
p2=ρ(u·u)

One can see that

(p1p2)=0

But we can add any function ϕ=0 to a solution and it's still valid. So the solution's aren't unique up to a constant. The fix is we apply a boundary conditions.

With viscosity

If μ0 we enforce the boundary condition by setting u=0 so that it doesn't slip on the wall.

ut=1ρp+1ρμuu·u

Becomes

0=1ρp+1ρμu

Then

p=μu

No viscosity

If μ=0 we have the boundary condition by setting un=0 so that the fluid doesn't flow into or out of the wall. Then dotting both sides by n gives.

utn=1ρpnn(uu)

And the left is zero so

pn=ρn(uu)

Let the difference between two solutions be the below. We want to show that it's a constant everywhere.

ϕ=(p1p2)

And we know

ϕ=0
. And because the two solutions have the same gradient on the boundary ϕ=0 on the wall. Thus dotting it with the normal vector it's also zero.

nϕ=0

And there's also the vector calculus identity below.

(ϕϕ)=ϕϕ+ϕϕ

But the right most term is zero.

(ϕϕ)=ϕϕ

Then if we integrate over the volume

ΩdV(ϕϕ)=ΩdVϕϕ

But we can also apply the divergence rule

ΩdV(ϕϕ)=ΩdS(ϕϕ)n=ΩdSϕ(ϕn)

And we know ϕn=0 on the boundary.

ΩdVϕϕ=0

And since magnitude is always positive it must be that:

ϕ=0

Which means it's a constant.


Discreetizing

We have the proof that it works on a vector field but in reality we'll need to discreetize it for the simulation. We will have our velocity field u live on a N×N grid with spacing Δs. We then update it with two steps. We first advect it without accounting for pressure into an intermediate velocity u.

u(x,y)=u(x,y)Δt(uu)

Here we define the divergence with a symmetric finite differences

f(x,y)=f(x+Δs,y)f(xΔs,y)2Δs,f(x,y+Δs)f(x,yΔs)2Δs

We define the next velocity to be

unext(x,y)=u(x,y)Δt1ρp(x,y)

As usual we want the divergence of it to be zero so

0=unext(x,y)=unext(x+Δs,y)unext(xΔs,y)2Δs+unext(x,y+Δs)unext(x,yΔs)2Δs

And if we substitute it in

0=u(x+Δs,y)Δt1ρp(x+Δs,y)(u(xΔs,y)Δt1ρp(xΔs,y))2Δs+u(x,y+Δs)Δt1ρp(x,y+Δs)(u(x,yΔs)Δt1ρp(x,yΔs))2Δs

Then rearrange

Δtρp(x+Δs,y)p(xΔs,y)+p(x,y+Δs)p(x,yΔs)2Δs=u(x+Δs,y)u(xΔs,y)2Δs+u(x,y+Δs)u(x,yΔs)2Δs

And the denominators don't matter.

Δtρp(x+Δs,y)p(xΔs,y)+p(x,y+Δs)p(x,yΔs)=u(x+Δs,y)u(xΔs,y)+u(x,y+Δs)u(x,yΔs)

Then expand even more

p(x+Δs,y)2p(x,u)+p(xΔs,y)2Δsp(x,y+Δs)2p(x,u)+p(x,yΔs)2Δs=ρΔt(u(x+Δs,y)u(xΔs,y)+u(x,y+Δs)u(x,yΔs))

Note that this is like a discrete version of the p=ρ(uu) from earlier, because u=u+(uu)=(uu). Now if you solve for pressure of the current cell it gives the below.

p(x,y)=14(p(x+Δs,y)+p(xΔs,y)+p(x,y+Δs)+p(x,yΔs)+2ρΔsΔt(u(x+Δs,y)u(xΔs,y)+u(x,y+Δs)u(x,yΔs)))

And this is just a linear system of equations you can solve with whatever method. Its technically just a really big matrix.

Boundary

We also have to define how it handles the boundary conditions. We skipped over how the gradient operator should be handled when it goes off the grid. We focus on the case of no viscosity. So we want

unextn=0

Then try and figure it out

unext,xnx+unext,yny=0

Then substitue

(ux(x,y)Δt1ρ(p(x,y))x)nx+(uy(x,y)Δt1ρ(p(x,y))y)ny=0

And expand again

(ux(x,y)Δt1ρp(x+Δs,y)p(xΔs,y)2Δs)nx+(uy(x,y)Δt1ρp(x,y+Δs)p(x,yΔs)2Δs)ny=0

In the simulation we have 4 possible normal vectors for each of the four sides. On the left and right sides

ux(x,y)Δt1ρp(x+Δs,y)p(xΔs,y)2Δs=0

Then

p(x+Δs,y)p(xΔs,y)=2ρΔsΔtux(x,y)

And similarily for the top and bottom sides

p(x,y+Δs)p(x,yΔs)=2ρΔsΔtuy(x,y)

Thus on the boundary you can solve for the pressure that's outside grid. For example p(xΔx,y) when N=0. And for velocity you can just assume its zero outside the grid because it would be divergence free anyways since the boundary cells are constrained to be orthogonal to the normal vector.


sources

VortexShedding github for a lot of the webgl shader code