32. Stokes Equation#

The Stokes equation is the basic model for incompressible fluids. While several terms of the advanced Navier-Stokes equations are skipped, the Stokes equation allows to study the difficulty of incompressibility constraints.

The unknown fields are

velocityu:ΩRdpressurep:ΩR

The Stokes equation is a system of equations

Δu+p=fdivu=0

with a given force density f:ΩR2. The first equation models balance of momentum, the second one is the incompressibility constraint. In the simplest case, we set boundary conditions u=0 on Ω

32.1. Variational Formulation#

The weak form is : Find uV:=[H01(Ω)]d and pQ:=L20(Ω) such that

uv+divvp=fvvVdivuq=0qQ

The pressure is defined up to a constant, and the divergence equation is satisfied for constant test-functions, anyway. Thus, we need the space L20, which are L2-functions with zero mean value. For the beauty of the equation, we have substituted p by p.

32.2. Finite Element Spaces#

We discretize the Stokes equation by different finite element spaces. For the velocity we use continuous, H1-conforming elements. For the pressure, which is a field in L2, discontinuous finite elements are natural. But, alternatively, one may also use continuous elements.

We try different combinations of finite element spaces for V and Q.

from ngsolve import *
from ngsolve.webgui import Draw
mesh = Mesh(unit_square.GenerateMesh(maxh=0.05))

Use continuous elements of order k=2 for each velocity component, and piece-wise constants for pressure:

V = VectorH1(mesh, order=2, dirichlet=".*")
Q = L2(mesh, order=0)
X = V * Q
def SolveStokes(X):
    u,p = X.TrialFunction()
    v,q = X.TestFunction()
    a = BilinearForm(X)
    a += (InnerProduct(grad(u),grad(v))+div(u)*q+div(v)*p)*dx
    # a += -1e-10*p*q*dx
    a.Assemble()
    a.mat[X.components[0].ndof,X.components[0].ndof]+=-1  # fixing pressure in one point

    f = LinearForm( (x-0.5)*v[1]*dx).Assemble()

    gfu = GridFunction(X)
    gfu.vec.data = a.mat.Inverse(X.FreeDofs()) * f.vec
    return gfu
gfu = SolveStokes(X)
Draw (gfu.components[0], mesh, "velocity")
Draw (gfu.components[1], mesh, "pressure");

Try a P2×P1,dc pairing:

V = VectorH1(mesh, order=2, dirichlet=".*")
Q = L2(mesh, order=1)
X = V*Q
gfu = SolveStokes(X)
Draw (gfu.components[0], mesh, "velocity")
Draw (gfu.components[1], mesh, "pressure");   
---------------------------------------------------------------------------
NgException                               Traceback (most recent call last)
Cell In[5], line 4
      2 Q = L2(mesh, order=1)
      3 X = V*Q
----> 4 gfu = SolveStokes(X)
      5 Draw (gfu.components[0], mesh, "velocity")
      6 Draw (gfu.components[1], mesh, "pressure");   

Cell In[3], line 13, in SolveStokes(X)
     10 f = LinearForm( (x-0.5)*v[1]*dx).Assemble()
     12 gfu = GridFunction(X)
---> 13 gfu.vec.data = a.mat.Inverse(X.FreeDofs()) * f.vec
     14 return gfu

NgException: UmfpackInverse: Numeric factorization failed.

P2+×P1,dc, i.e. adding cubic bubbles:

V = VectorH1(mesh, order=2, dirichlet=".*")
V.SetOrder(TRIG,3)
V.Update()
Q = L2(mesh, order=1)
X = V*Q

gfu = SolveStokes(X)
Draw (gfu.components[0], mesh, "velocity")
Draw (gfu.components[1], mesh, "pressure");

The Taylor Hood element P2×P1 with continuous pressure:

V = VectorH1(mesh, order=2, dirichlet=".*")
Q = H1(mesh, order=1)
X = V*Q

gfu = SolveStokes(X)
Draw (gfu.components[0], mesh, "velocity")
Draw (gfu.components[1], mesh, "pressure");

We see that the pressure space must not be too rich, otherwise we obtain a singular system (or a bad solutions, in particular for the pressure).