In this tutorial, we will learn
The goal of this last tutorial is to solve a nonlinear multi-field PDE. As a model problem, we consider a well known benchmark in computational fluid dynamics, the lid-driven cavity for the incompressible Navier-Stokes equations. Formally, the PDE we want to solve is: find the velocity vector u and the pressure p such that
{−Δu+Re (u⋅∇) u+∇p=0 in Ω,∇⋅u=0 in Ω,u=g on ∂Ω,where the computational domain is the unit square Ω≐(0,1)d, d=2, Re is the Reynolds number (here, we take Re=10), and (w⋅∇) u=(∇u)tw is the well known convection operator. In this example, the driving force is the Dirichlet boundary velocity g, which is a non-zero horizontal velocity with a value of g=(1,0)t on the top side of the cavity, namely the boundary (0,1)×{1}, and g=0 elsewhere on ∂Ω. Since we impose Dirichlet boundary conditions on the entire boundary ∂Ω, the mean value of the pressure is constrained to zero in order have a well posed problem,
∫Ωq dΩ=0.In order to approximate this problem we chose a formulation based on inf-sub stable Qk/Pk−1 elements with continuous velocities and discontinuous pressures (see, e.g., [1] for specific details). The interpolation spaces are defined as follows. The velocity interpolation space is
V≐{v∈[C0(Ω)]d: v|T∈[Qk(T)]d for all T∈T},where T denotes an arbitrary cell of the FE mesh T, and Qk(T) is the local polynomial space in cell T defined as the multi-variate polynomials in T of order less or equal to k in each spatial coordinate. Note that, this is the usual continuous vector-valued Lagrangian FE space of order k defined on a mesh of quadrilaterals or hexahedra. On the other hand, the space for the pressure is
Q0≐{q∈Q: ∫Ωq dΩ=0}, withQ≐{q∈L2(Ω): q|T∈Pk−1(T) for all T∈T},where Pk−1(T) is the polynomial space of multi-variate polynomials in T of degree less or equal to k−1. Note that functions in Q0 are strongly constrained to have zero mean value. This is achieved in the code by removing one degree of freedom from the (unconstrained) interpolation space Q and adding a constant to the computed pressure so that the resulting function has zero mean value.
The weak form associated to these interpolation spaces reads: find (u,p)∈Ug×Q0 such that [r(u,p)](v,q)=0 for all (v,q)∈V0×Q0 where Ug and V0 are the set of functions in V fulfilling the Dirichlet boundary condition g and 0 on ∂Ω respectively. The weak residual r evaluated at a given pair (u,p) is the linear form defined as
[r(u,p)](v,q)≐a((u,p),(v,q))+[c(u)](v),with a((u,p),(v,q))≐∫Ω∇v⋅∇u dΩ−∫Ω(∇⋅v) p dΩ+∫Ωq (∇⋅u) dΩ,[c(u)](v)≐∫Ωv⋅((u⋅∇) u) dΩ.
In order to solve this nonlinear weak equation with a Newton-Raphson method, one needs to compute the Jacobian associated with the residual r. In this case, the Jacobian j evaluated at a pair (u,p) is the bilinear form defined as
[j(u,p)]((δu,δp),(v,q))≐a((δu,δp),(v,q))+[dc(u)](δu,v),where dc results from the linearization of the convective term, namely [dc(u)](δu,v)≐∫Ωv⋅((u⋅∇) δu) dΩ+∫Ωv⋅((δu⋅∇) u) dΩ.
We start with the discretization of the computational domain. We consider a 100×100 Cartesian mesh of the unit square.
using Gridap
n = 100
domain = (0,1,0,1)
partition = (n,n)
model = CartesianDiscreteModel(domain,partition)
For convenience, we create two new boundary tags, namely "diri1"
and "diri0"
, one for the top side of the square (where the velocity is non-zero), and another for the rest of the boundary (where the velocity is zero).
labels = get_face_labeling(model)
add_tag_from_tags!(labels,"diri1",[6,])
add_tag_from_tags!(labels,"diri0",[1,2,3,4,5,7,8])
For the velocities, we need to create a conventional vector-valued continuous Lagrangian FE space. In this example, we select a second order interpolation.
D = 2
order = 2
reffeᵤ = ReferenceFE(lagrangian,VectorValue{2,Float64},order)
V = TestFESpace(model,reffeᵤ,conformity=:H1,labels=labels,dirichlet_tags=["diri0","diri1"])
The interpolation space for the pressure is built as follows
reffeₚ = ReferenceFE(lagrangian,Float64,order-1;space=:P)
Q = TestFESpace(model,reffeₚ,conformity=:L2,constraint=:zeromean)
With the options :Lagrangian
, space=:P
, valuetype=Float64
, and order=order-1
, we select the local polynomial space Pk−1(T) on the cells T∈T. With the symbol space=:P
we specifically chose a local Lagrangian interpolation of type "P". Without using space=:P
, would lead to a local Lagrangian of type "Q" since this is the default for quadrilateral or hexahedral elements. On the other hand, constraint=:zeromean
leads to a FE space, whose functions are constrained to have mean value equal to zero, which is just what we need for the pressure space. With these objects, we build the trial multi-field FE spaces
uD0 = VectorValue(0,0)
uD1 = VectorValue(1,0)
U = TrialFESpace(V,[uD0,uD1])
P = TrialFESpace(Q)
Y = MultiFieldFESpace([V, Q])
X = MultiFieldFESpace([U, P])
From the discrete model we can define the triangulation and integration measure
degree = order
Ωₕ = Triangulation(model)
dΩ = Measure(Ωₕ,degree)
The different terms of the nonlinear weak form for this example are defined following an approach similar to the one discussed for the p-Laplacian equation, but this time using the notation for multi-field problems.
const Re = 10.0
conv(u,∇u) = Re*(∇u')⋅u
dconv(du,∇du,u,∇u) = conv(u,∇du)+conv(du,∇u)
The bilinear form reads
a((u,p),(v,q)) = ∫( ∇(v)⊙∇(u) - (∇⋅v)*p + q*(∇⋅u) )dΩ
The nonlinear term and its Jacobian are given by
c(u,v) = ∫( v⊙(conv∘(u,∇(u))) )dΩ
dc(u,du,v) = ∫( v⊙(dconv∘(du,∇(du),u,∇(u))) )dΩ
Finally, the Navier-Stokes weak form residual and Jacobian can be defined as
res((u,p),(v,q)) = a((u,p),(v,q)) + c(u,v)
jac((u,p),(du,dp),(v,q)) = a((du,dp),(v,q)) + dc(u,du,v)
With the functions res
, and jac
representing the weak residual and the Jacobian, we build the nonlinear FE problem:
op = FEOperator(res,jac,X,Y)
To finally solve the problem, we consider the same nonlinear solver as previously considered for the p-Laplacian equation.
using LineSearches: BackTracking
nls = NLSolver(
show_trace=true, method=:newton, linesearch=BackTracking())
solver = FESolver(nls)
In this example, we solve the problem without providing an initial guess (a default one equal to zero will be generated internally)
uh, ph = solve(solver,op)
Finally, we write the results for visualization (see next figure).
writevtk(Ωₕ,"ins-results",cellfields=["uh"=>uh,"ph"=>ph])
[1] H. C. Elman, D. J. Silvester, and A. J. Wathen. Finite elements and fast iterative solvers: with applications in incompressible fluid dynamics. Oxford University Press, 2005.
This notebook was generated using Literate.jl.