Sunteți pe pagina 1din 49

FreeFem++ Applied to the Driven Cavity

(Part III Essay)

Andrew McRae
Supervisor: Prof E. J. Hinch

May 2012
Contents
1 Introduction 2

2 Analytic Formulation 2
2.1 Primitive-Variable Formulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2.2 Streamfunction-Vorticity Formulation . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.3 Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

3 Numerical Methods 4
3.1 Finite Element Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
3.2 Finite Difference Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

4 Literature Review 6

5 FreeFem++ 9

6 The Driven Cavity in FreeFem++ 11


6.1 Mesh & Function Spaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
6.2 Time evolution method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
6.3 Newton iteration method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
6.4 Comments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

7 Results 14
7.1 Runtime of individual functions; variation with n, Re, ∆t . . . . . . . . . . . . . . . . . 14
7.2 ψ as a proxy for convergence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
7.3 Convergence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
7.3.1 Dependence on ∆t . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
7.3.2 Dependence on Re . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
7.3.3 Dependence on n . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
7.4 Elementary failures of convergence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
7.5 Steady solutions with the time-evolution method . . . . . . . . . . . . . . . . . . . . . . 24
7.5.1 Dependence on ∆t . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
7.5.2 Dependence on n . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
7.5.3 Repeated extrapolation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
7.6 Comparison with literature . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
7.7 Non-uniform meshes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
7.8 The Newton method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
7.9 A New(ton) Hope? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

8 Conclusion 40

A Directory Structure and Code Listings 40

B References 48

1
1 Introduction
The driven cavity problem has long been a standard test case for new solution methods in Computational
Fluid Dynamics, since both the geometry and boundary conditions are easy to implement, yet the flow
retains complex features. The standard problem is to resolve the steady two-dimensional flow in a square
domain with sides and bottom at rest, and a lid moving at a uniform velocity - this is sometimes referred
to specifically as the ’lid-driven cavity’. The boundary conditions are no-slip on the walls, and the initial
condition normally corresponds to a fluid at rest. As mentioned by Shankar [10], this may be used as a
model of a ‘short-dwell coater, used to produce high-grade paper and photographic film’.
There are several natural extensions: in the shear-driven cavity problem, one replaces the solid lid
by another fluid. This may be used to model wind-driven circulation in a lake. There is an analogous
three dimensional lid-driven cavity problem: a cube with a translating lid, although it is the axisymmetric
problem of a cylinder with a rotating lid that more closely resembles the two dimensional problem. One
may also change the velocity profile of the lid to be non-uniform, although this is less likely to correspond
to a physical problem.
In this essay, I will firstly give the analytic formulation of the driven cavity problem. I will then
summarise relevant numerical methods, and give a chronological review of the literature on numerical
solutions to the lid-driven cavity. I will introduce FreeFem++ with relevant code samples, eventually
presenting two distinct methods for finding steady solutions of the lid-driven cavity. Results and analysis
will then be presented, with comparisons made to the available literature.

2 Analytic Formulation
2.1 Primitive-Variable Formulation
We define the velocity u, the pressure p, the density ρ, and kinematic viscosity ν. Then the unsteady flow
evolves according to the Navier-Stokes equations

∂u 1
+ (u · ∇)u = − ∇p + ν∇2 u (2.1.1)
∂t ρ
∇·u=0, (2.1.2)
with (
(U, 0) on the lid,
u=
0 on the sides and bottom.
We can non-dimensionalise by the lid velocity U , the side length L, and the density ρ to give

∂u 1 2
+ (u · ∇)u = −∇p + ∇ u (2.1.3)
∂t Re
∇·u=0, (2.1.4)
where the equations now depend on a single nondimensional parameter: the Reynolds number
UL
Re = .
ν
It is hence normal to assume that the domain is of unit width and height, and that the lid moves at unit
speed. Then the boundary conditions become
(
(1, 0) on the lid,
u=
0 on the sides and bottom.

2
2.2 Streamfunction-Vorticity Formulation
While the above equations can be used directly, it is more common to take the curl of the momentum
equation. The z-component then gives
∂ω 1 2
+ (u · ∇)ω = ∇ ω, (2.2.1)
∂t Re
where ω is the z-component of vorticity:
∂uy ∂ux
ω= − .
∂x ∂y
By introducing a streamfunction ψ satisfying
∂ψ ∂ψ
ux = , uy = − ,
∂y ∂x
such that
∇2 ψ = −ω ,
the incompressibility constraint is automatically satisfied. Equation (2.2.1) describing the vorticity evo-
lution can now be written
∂ω ∂(ω, ψ) 1 2
+ = ∇ ω, (2.2.2)
∂t ∂(x, y) Re
with boundary conditions

ψ = 0 on the sides, lid and bottom,


∂ψ
= 0 on the sides,
∂x (
∂ψ 1 on the lid,
=
∂y 0 on the bottom.

Note that, unlike the primitive variable formulation, this cannot be extended to three dimensions.

2.3 Discussion
In the primitive-variable formulation, the steady solution satisfies
1 2
(u · ∇)u = −∇p + ∇ u (2.3.1)
Re
∇·u=0, (2.3.2)
with boundary conditions as above.
In the streamfunction-vorticity formulation, the steady solution satisfies

∂(ω, ψ) 1 2
= ∇ ω (2.3.3)
∂(x, y) Re

∇2 ψ = −ω , (2.3.4)
with boundary conditions as above.
Both formulations require us to find a solution to coupled, non-linear PDEs in two variables. In both
formulations, boundary conditions exist for only one of the variables. The pressure p in the primitive-
variable formulation is non-local, and not easily linked to the incompressibility constraint ∇ · u = 0,
although physically they are connected. Chorin introduced an artificial compressibility scheme [13],
in which the fluid density was allowed to vary, and a projection method [14] in order to make this

3
formulation more amenable to numerical techniques. The streamfunction-vorticity approach generally
requires the boundary conditions on ∂ψ ∂ψ
∂x , ∂y to be converted into boundary conditions on ω.
It should be noted that this problem has non-matching boundary conditions at the upper corners.
Analytically, this leads to singularities in the pressure and vorticity fields in the upper corners [10].
One may be tempted to enforce a (non-uniform) velocity profile on the lid that vanishes at the corners.
However, since an experimental setup would usually have a translating lid, this would be sidestepping a
genuine issue. A proper formulation would include a small gap h between the lid and the side wall, and
look at the limit as h → 0. According to Shankar [10], this was done by Srinivasan [15] and Meleshko
[16]; they found that the effects of the singularities are indeed confined to the neighbourhood of the upper
corners.

3 Numerical Methods
The equations above are not in a suitable format to be solved on a computer, since they require infinites-
imal spatial resolution, with the velocity and pressure (or vorticity and streamfunction) calculated to
arbitrary precision at each point. Since we want solutions to be obtained in finite time(!), we must be
content with finding an approximate solution. This requires us to discretise space and work with finite
precision arithmetic. Although it is not the case here, if our problem involved an infinite domain, we
would additonally have to restrict to a large-but-finite domain. Two common numerical methods are
described below, and a technique for solving the Poisson problem −∇2 u = f is described for each one.
FreeFem++ uses the Finite Element method, but the Finite Difference method is more intuitive and dom-
inates the literature, hence a brief overview of this is included. The following material is influenced by
Iserles’s book [12].

3.1 Finite Element Method


Let L be a linear differential operator, and f a known function on some finite domain A. We seek to
find an approximate solution to Lu = f in A, with Dirichlet boundary conditions. (It should be noted
immediately that the Navier-Stokes momentum equation is not linear, and hence cannot be written in this
form. We will cross this bridge later.)
We seek to express the approximate solution as a linear combination of basis functions in a finite-
dimensional space. Let the dimension of the space be m, and label the approximate solution um . Then
m
X
um = φ0 (x) + γl φl (x) , (3.1.1)
l=1

where φ0 (x) will satisfy the boundary conditions, and {φ1 , φ2 , . . . , φm } are linearly independent func-
tions that vanish on the boundary of the domain. Consider the ’defect’ Lum − f . If um were an exact
solution, the defect would be the zero function. Instead we demand that, for some inner product h·, ·i,
hLum − f, φi i = 0 , for i = 1, 2, . . . , m . (3.1.2)
The inner product used is the standard L2 inner product
Z
hv, wi = v(x0 )w(x0 ) dA ,
A

If we now look at the specific problem L = −∇2 , with zero boundary conditions so that φ0 vanishes,
then Equation (3.1.2) becomes
Z m
! Z
X
0
− φi (x )∇ 2
γl φl (x ) dA = f (x0 )φi (x0 ) dA , for i = 1, 2, . . . , m .
0
(3.1.3)
A l=1 A

A crucial step is to now integrate by parts. This reduces the differentiability requirements on the φi . We
no longer require each φi to be twice-differentiable, and indeed piecewise differentiability is enough.

4
Thus, this equation can be satisfied, even though the resulting answer no longer satisfies Lu = f in the
true sense. Since φ1 , . . . , φm vanish on the boundary, integrating the LHS of Equation (3.1.3) by parts
gives
m
X Z Z
0 0
γl ∇φi (x ) · ∇φl (x ) dA = f (x0 )φi (x0 ) dA , for i = 1, 2, . . . , m , (3.1.4)
l=1 A A

which is the form we shall use. This can clearly be written as a matrix equation: define
Z
Lij = ∇φi (x0 ) · ∇φj (x0 ) dA ,
A
Z
fi = f (x0 )φi (x0 ) dA ,
A

L = (Lij ) , f = (fi ) , u = (γi ) ,


then
Lu = f .

Of course, this is only useful if the finite-dimensional space approximates the (infinite-dimensional)
space of candidate solutions to Lu = f ‘well’. One choice of basis functions corresponds to the initial
terms in a Fourier expansion, and leads to the spectral method. This has the advantage of needing
relatively few terms to approximate the solution well, i.e. the matrix L above would be relatively small.
However, the spectral method isn’t suitable for domains of arbitrary shape, and requires appropriate
boundary conditions (typically periodic).
For the finite element method, the basis functions are chosen to have small support, i.e. each basis
function is zero in most of the domain. A 2D domain is subdivided into a mesh of many elements,
typically triangles, with no triangle vertex lying on the edge of another triangle. A common choice of
basis functions is piecewise linear functions {φi } such that φj = 1 on a single interior vertex j, and 0
on all other vertices. Assuming sufficiently fine triangulation, hφi , φj i will be 0 for most choices of i
and j, except where i and j correspond to neighbouring vertices. One may also choose basis functions
that are polynomials of higher degree, or something more exotic. This leads to L being large, but sparse,
hence L can be stored efficiently, and operations can be carried out in much less time than on a dense
matrix of similar size. In general, rather than inverting L directly, an iterative scheme will be used to
get an approximate solution, to some desired accuracy. Further discussion on sparse matrix algorithms is
somewhat beyond the scope of the essay.
In practice, rather than talking about basis functions explicitly, one usually proceeds as follows: if
-∇2 u = f , then for any “test-function” v,
Z Z
− v∇2 u dA = f v dA .
A A

We then carry out integration by parts. Assuming that the surface term disappears as a result of boundary
conditions, this gives Z Z
∇u · ∇v dA = f v dA .
A A
If the surface term does not vanish, a line integral will also be present in the equation. We now restrict u
and v to be in the finite-dimensional space above. Once these are expressed as sums of basis functions,
the same algebraic equations are obtained.
A major advantage of the finite element method is that the mesh density can be highly nonuniform.
One may want a high density of elements in a region of interest (for example, where the boundary
condition changes rapidly), but fewer (and hence larger) elements in a region of less interest such as the
far-field. This is an easy way to increase accuracy and/or decrease computation time.

5
3.2 Finite Difference Method
This section is not designed to be comprehensive, but is merely provided to aid understanding of the
literature below. In the finite difference method, one replaces a function over a continuous domain by a
function that only takes values at a finite number of discrete points. By using Taylor expansions, deriva-
tives of a function can be approximated by linear combinations of values taken at nearby points. One can
obtain approximations correct to higher-order by including more terms. While this is computationally
more expensive, it may lead to higher accuracy and/or faster convergence. On the other hand, it may
be a very poor representation if a function varies significantly over a small enough spatial region, as in
common in shocks.
Given the Poisson problem ∇2 u = −f , in a square domain with Dirichlet BCs, we may proceed as
follows: assume the grid has uniform spacing in x and y, and that the spatial steps ∆x and ∆y are both
h. Let n be the number of interior points in both x and y. The simplest representation of ∇2 u i,j is

1
∇2 u i,j ≈ 2 (ui−1,j + ui+1,j + ui,j−1 + ui,j+1 − 4ui,j ) + O h2 ,
 
(3.2.1)
h
which rearranges to
1  
ui,j ≈ ui−1,j + ui+1,j + ui,j−1 + ui,j+1 − h2 ∇2 u i,j . (3.2.2)
4
Assuming that f = −∇2 u is known in the interior, this is a single equation in 5 unknowns (fewer if the
point is next to the boundary). However, we can in theory write down the corresponding equation for
every interior point, giving a system of n2 equations in n2 unknowns. We can again switch into matrix
language. Assuming that there is a unique solution, this could be found by inverting an n2 by n2 matrix.
However, this is an O(n6 ) operation, which is too large for comfort. But again, the matrix is sparse -
each row will have at most 5 entries, and methods such as Gauss-Seidel or Successive Over Relaxation
may be used to find a solution to the required accuracy.
One major disadvantage of the finite difference method is that domains of irregular shape are not
easily handled, and a lot of resources may be required to interpolate boundary values onto nearby grid
points. A uniform grid may represent regions of the domain with unnecessarily high accuracy, wasting
computation time. However, using a nonuniform grid often lowers the order of the method used, due to
higher-order terms in the Taylor expansion no longer cancelling.

4 Literature Review
Note that, in the literature, the steady solution is rarely found by evolving the initial flow forward in time
until it converges. Usually, an interative scheme is used on both variables simulataneously.
Kawaguti (1961) [1] appears to have the earliest numerical solutions for the lid-driven cavity problem.
This used the streamfunction-vorticity formulation with a finite difference method on an 11×11 uniform
grid. The governing equations (2.3.3) and (2.3.4) can be read as Poisson problems in the variables ω and
ψ respectively. One can use the boundary condition for ∂ψ
∂n to get an estimate for ω on the boundary:
(
2 2ψP − 2h on the lid,
−h ω0 = (4.0.3)
2ψP on the bottom,

where ω0 is the value on the boundary, and ψP is the value at the nearest interior point. Each iteration,
the vorticity at the boundary was updated to satisfy Equation (4.0.3), then each interior point wi,j , ψi,j
was updated using Equation (3.2.2) in terms of its neighbours and the LHS. This was repeated until the
values converged to five decimal places. Steady solutions were obtained for Re = 0 to Re = 64, but the
iterative scheme diverged at Re = 128, likely due to the coarse mesh.
Burggraf (1966) [2] is in 2 parts: in the first part, an analytic model is developed for the core of
the driven-cavity flow. In the second part, he modifies Kawaguti’s iterative procedure so that convergent

6
solutions at any Reynolds number can be attained. This is done by using underrelaxation: essentially,
during the ‘interior update’ step of the procedure described above, ψi,j is not updated according to the
RHS of Equation (3.2.2), instead
 
n+1 1 n n n n
− h2 ∇2 ψi,j
n n

ψi,j =µ ψi−1,j + ψi+1,j + ψi,j−1 + ψi,j+1 + (1 − µ) ψi,j , (4.0.4)
4

for some 0 < µ < 1, chosen by trial-and-error. This gives increased stability, though the computational
time required may make this unfeasible for large enough Re. He obtained results up to Re = 400, using
a 40×40 uniform grid, although he questioned the accuracy of the Re = 400 solution. He identified
secondary eddies in the lower corners, in agreement with Moffatt ’64 for Stokes flow. Using a analytic
high-Re model suggested by Batchelor, of an inviscid rotational core with thin boundary layers, Burggraf
suggested that the vorticity of the core should approach 1.886 as Re → ∞. His Re = 400 solution was
in qualitative agreement, with a central vorticity of 2.15 (decreasing from 3.14 at Re = 100). He also
observed secondary, counter-rotating eddies in the lower corners.
Benjamin & Denny (1979) [3] again uses the streamfunction-vorticity formulation with a finite dif-
ference method, but on a non-uniform grid. Their grid spacing was motivated by the tan function, and
concentrated points near the walls and corners of the domain, with an order-of-magnitude difference in
the node density from middle to edge. A method of ’false transients’ was used, with governing equations
∂ψ
= ω + ∇2 ψ (4.0.5)
∂t
   
∂ω ∂ ∂ψ ∂ ∂ψ 1 2
=− ω + ω + ∇ ω, (4.0.6)
∂t ∂x ∂y ∂y ∂x Re
so that both ω and ψ converge to their final values simultaneously. Spatial derivatives were approximated
in the usual way, and ADI iteration was used to relax the resulting algebraic problem. In particular,
the speed of convergence was increased by using a non-uniform relaxation parameter. The boundary
conditions for ω (derived from the conditions on ∂ψ ∂n ) were correct to high order, so as not to reduce
the order of convergence for the global solution. Convergence was deemed to have occured when the
root-mean-square residuals
 1/2
2
RMSRψ = (ω + ∇2 ψ)ij (4.0.7)

 2 !1/2
1 2
RMSRω = ∇ ω − ∇ · (uω) , (4.0.8)
Re ij

(with the average taken over interior points) were sufficiently small. They obtained solutions at Re up to
10,000, on grids of up to 151×151. Extrapolations were made of various flow indicators such as the value
of ψ and ω at the centre of the main vortex, using an a + bhn model. In particular, a central vorticity of
1.885 at Re = 10,000 was obtained by extrapolation, in comparison to the 1.886 predicted by Burggraf in
the limit Re → ∞. At Re = 10,000, a tertiary and fourth-order vertex were observed in the bottom-right
corner (with lid moving from left to right), a tertiary vortex in the bottom-left corner, and a secondary
vortex in the top-left (existing for Re & 1,200).
Ghia, Ghia & Shin (1982) [4] uses the streamfunction-vorticity formulation with finite differences,
but with a multigrid method. The rationale behind this is the following: iterative procedures to solve the
equations tend to dampen errors on the scale of the grid spacing in just a few iterations, while errors on a
scale much larger than the grid spacing are smoothed far more slowly (this can be shown rigourously by
considering a Fourier decomposition). A multigrid method involves running iterations on many different
grids, with varying mesh sizes. One might begin by running a small number of iterations on the full grid,
with mesh size h. One then generates a grid with a larger mesh size, say 2h, and maps the function onto
the coarser grid. Needless to say, this restriction can be carried out many way. The simplest method is,
for each point on the coarse mesh, to copy a single value from a corresponding point in the fine mesh,
but in general, a weighted average of several values from nearby points on the fine mesh will be used.
A full discussion is well beyond the scope of this essay. The iteration procedure is then carried out
on the coarser mesh, and the corrections are mapped back onto the fine grid (again, this can be done

7
in a multitude of ways). By carrying out this procedure on a sensible sequence of grids, one obtains
convergence in far less time than if the finest grid had been used throughout. Again, results for up to Re
= 10,000 on a 257×257 grid were generated, in good agreement with results obtained in [3].
Schreiber & Keller (1983) [5] uses the streamfunction-vorticity formulation with finite differences
on a uniform 180×180 grid. However, after discretisation of the derivatives, a Newton-like iterative
method was used to obtain convergence to a steady solution. In addition, for the central differencing
scheme used, multiple numerical solutions were found at high Reynolds numbers. Spurious solutions
were identified, and a modified scheme was produced that seemed to give a unique solution. Results
were in good agreement with [3] and [4] at matching Re. In another paper published in the same year
[6], Schreiber + Keller investigate spurious solutions. They comment that a basic result from algebraic
geometry (Bezout’s theorem) leads to the algebraic equations resulting from the finite difference method
2
on an n × n grid having some 2n solutions, hence nearly all solutions to the equations are therefore
spurious! While most of these will be complex, and hence easily avoidable, real spurious solutions do
exist, and may even be reached when using continuation from a known physical state. They note that
spurious solutions have been published in at least 4 papers before that date, and in only one case were
they identified as being unphysical. Typically these correspond to an enlongated vortex near the lid, and
a counter-rotating vortex occupying the majority of the cavity.
Barragy & Carey (1997) [7] use the streamfunction-vorticity formulation, but with finite elements.
A nonuniform mesh is used, with the ratio between the size of the smallest and largest elements being
about 30. In particular, while their mesh was relatively small (between 5 and 32 elements in the x and
y direction), they used high-order polynomials (p = 6 to 8) for their basis functions. Their results were
in agreement with the previous literature. One new feature was seen: a tertiary vortex in the upper-left
corner was observed in their Re = 16,000 solution, however their solution was underresolved elsewhere
in the cavity. A fifth-order vortex was possibly observed in the lower-right corner. However, the size of
this was of the order of the element spacing, and hence the authors were cautious of affirming its validity.
Erturk, Corke & Gokcol (2005) [8] present results up to Re = 21,000. They use the streamfunction-
vorticity formulation with finite differences, but on a 601×601 uniform mesh. This appears to be the
highest Re done to date, excluding Nallasame and Prasad (1977) who presented steady solutions up to
Re = 50,000 (but their results are likely to be highly inaccurate - see the conclusion section of this essay
for further details). Erturk et. al. again used a false-transient method, but cleverly factorised the spatial
derivative operator to give tridiagonal matrices; this gave a significant increase in computation speed.
They extrapolated flow indicators to get values accurate to O(∆h6 ), which were in good agreement with
other results in the literature.
All the above papers have sought a steady solution of the driven cavity problem. Note that several
of the above papers contained remarks saying that they were unable to obtain steady solutions at a larger
Re, and suggested that this may be due to genuine instability, or simply due to an overly coarse mesh.
One recurring theme has been that a finer mesh has allowed solutions at larger Re to be found. In [9], a
discussion of the driven cavity problem, Erturk concludes,

“in order to obtain a steady solutions at high Reynolds numbers (Re > 10,000), a grid mesh
larger than 257×257 have to be used. In this study, first, we used a grid mesh with 257×257
grid points and solved the equations. With these many grid points we could not obtain a
solution for Reynolds numbers above 10,000. Above Re = 10,000 we observed that the
solution was oscillating....
We then increased the number of grids to 513×513. This time we were able to obtain steady
solutions up to Re ≤ 15,000. The important thing to note is that while using this many
number of grids, above Re = 15,000 the solution was not converging but it was oscillating.
Finally, we increased the number of grids to 1025×1025. This time we were able to obtain
converged steady solutions of driven cavity flow up to Reynolds number of 20,000. Above
Re = 20,000 our solution was oscillating again. This suggests that most probably, steady
computations are possible when a larger grid mesh is used.”

8
When fine grids are used, the ‘cell Reynolds number’ or ‘Peclet number’ defined as

u∆h
ReC =
ν
decreases, and this has been shown to improve the numerical stability, which may explain this behaviour.
In addition, features such as higher-order vortices in the corners may not be resolved if the mesh is too
small. Numerical studies to explicitly determine the transition from stability to instability have also been
done. In [9], Erturk states that the literature in this area suggested that instability occurs beyond a critical
Re between 7,500 and 12,500. However, none of these studies used a finer mesh than 257×257, on
which Erturk et. al. also had problems obtaining a steady solution. Indeed, they did obtain results at Re
> 12,500, but required a finer mesh than this. It is therefore plausible that the results from these stability
studies depend largely on the mesh size used, and are hence not useful.
Experimentally, the two-dimensional flow cannot be realised. In a thin cell, there are substantial edge
effects. According to Shankar & Deshpande [10], Bogatyrev & Gorin [17] and Koseff & Street [18]
showed that the flow is three-dimensional even when the spanwise aspect ratio (SAR) is large, perhaps
contrary to intuition. Erturk [9] mentions experiments performed with an SAR of up to 3, in which local
3D features were observed, not just global features due to the end walls. Furthermore, the flow becomes
turbulent for a sufficiently high Reynolds number. That is, the physical flow in a cavity is neither two-
dimensional, nor steady at high Reynolds numbers. Numerical studies have also demonstrated that the
steady two-dimensional flow is unstable to a three-dimensional disturbance, possibly even at Re = 600.
However, although the flow is fundamentally ficticious, the mathematical and numerical nature of the
flow is still of interest.

5 FreeFem++
FreeFem++ is a language for solving PDEs using the finite element method. The syntax bears a resem-
blence to C/C++, though with far fewer keywords. Importantly, the weak formulation of a PDE must be
used in the problem specification. A brief summary is given below:
Firstly, a mesh should be declared:

mesh Th; // initialise mesh

Next, the mesh must be initialised. This may be done using an inbuilt mesh as follows:

Th = square(8,5); // uniform 8x5 grid in the unit square, each


// rectangle divided into two triangles,
// diagonals running bottom-left to top-right

More commonly, a border is firstly defined in terms of a parameter:

// define a circle of radius 3


border C(t=-1,1){x=3*cos(pi*t);y=3*sin(pi*t)};

A mesh can then be constructed:

Th = buildmesh(C(20)); // builds a mesh, from 20


// points on the border

FreeFem++ uses a modified Delaunay triangulation algorithm here. Note that the boundary points corre-
spond to uniformly-spaced values of the parameter t. If we had instead written

border C(t=-1,1){x=3*cos(pi*tˆ3);y=3*sin(pi*tˆ3)};

9
then we would have a non-uniform distribution of points on the boundary: they would now be concen-
trated towards the right-hand side of the circle. The triangulation algorithm would produce many small
elements to the right of the interior. This may be desirable in certain situations, as described previously.
Finally, a mesh can be automatically adapted:

Th = adaptmesh(Th,f); // adapts Th, using a function f

This will concentrate elements in regions where f changes rapidly.


Finite Element function spaces are then defined over the mesh:

fespace Mh(Th,P1); // piecewise linear fns over Th


fespace Xh(Th,P2); // piecewise quadratic fns over Th

Many different types of finite element are available for use, not just piecewise polynomial elements.
Next, functions within these spaces are declared:

Mh u, v; // declares u and v to be functions in the space Mh

Next, the PDE is declared, in the weak formulation. For example, consider the Poisson problem described
previously: given f , find u such that: u = 0 on the boundary, and, ∀v,
Z Z
∇u · ∇v dA − f v dA = 0 .
A A

This is represented in FreeFem++ by

solve Poisson(u,v) = int2d(Th)(dx(u)*dx(v) + dy(u)*dy(v)


- int2d(Th)(f*v)
+ on(C,u=0);

The problem may also be specified and solved separately, by

problem Poisson(u,v) = ...


/* some more commands */
Poisson;

The bilinear term ∇u · ∇v and the linear term f v must not be put in the same integral. A Dirichlet
boundary condition is represented in the third line of the Poisson problem above. A Neumann boundary
condition would give a boundary integral term

int1d(Th)(f*w)

The output can then be displayed to screen, or saved to file.

plot(u); // displays isovalues of u


ofstream file1("output.txt"); // opens output.txt for writing
file1 << u[]; // saves u to output.txt

It is also possible to save and load the mesh, using

savemesh(Th,"mesh.msh");
Th = readmesh(Th,"mesh.msh");

FreeFem++ can save the mesh in a variety of formats for compatibility with other programs.

10
6 The Driven Cavity in FreeFem++
6.1 Mesh & Function Spaces
Three different meshes were used. The first mesh was the uniform mesh

square(n,n)

for various values of n, giving 2n2 triangles. The second and third mesh were nonuniform, designed to
concentrate elements near the edges and in the corners, and reduce the element density in the core. The
bottom side of the meshes were parameterised as follows:

border bottom(t=0,1){x=3*tˆ2-2*tˆ3;y=0;};
border bottom(t=0,1){x=10*tˆ3-15*tˆ4+6*tˆ5;y=0;};
2
These were chosen so that dx dt ∝ t (1 − t) and [t (1 − t)] respectively. The other sides had identical
parameterisations, up to rotation and translation. These meshes had n points on each side, but fewer than
2n2 triangles. As can be seen, the meshes indeed become more non-uniform:

Figure 6.1.1: Meshes 1-3 for n = 32.

Next, the function spaces were defined. Following an example in the FreeFem++ manual [11], all
functions were defined using P2 (piecewise-quadratic) elements, apart from pressure, which used P1
(piecewise-linear) elements.

6.2 Time evolution method


The manual suggests several methods of implementing the Navier-Stokes equations (2.1.3) & (2.1.4),
suitable for solving the driven cavity problem. The first method I will use is a simple time evolution
method, declaring the steady solution to be reached when the flow changes by less than some small
amount per time step. Note that the (u · ∇) u term in the momentum equation is not linear in u. After
multiplying by a test-function, this would be third order, and hence not suitable for the finite element
method. Instead, we identify the LHS of Equation (2.1.3) as the material derivative
Du ∂u
= + (u · ∇)u ,
Dt ∂t
and use the inbuilt convect operator. In discretised time, using superscripts for timesteps, we then have
1 n+1 1 2 n+1
(u − un ◦ X n ) − ∇ u + ∇pn+1 = 0 (6.2.1)
∆t Re
∇ · un+1 = 0 , (6.2.2)

11
where un ◦ X n represents the convected velocity field,

un ◦ X n ≈ un |x−∆t·un |x .

We take the scalar product of the first equation with a test function v, multiply the second by a test
function q, and combine the equations. After integration by parts, our analytic problem is: given un , pn ,
we seek un+1 , pn+1 such that ∀v, q,
Z  
1 1
un+1 · v − (un ◦ X n ) .v + ∇un+1 : ∇v − pn+1 ∇ · v − q∇ · un+1 dA = 0 .
 
A ∆t Re
(6.2.3)
The boundary conditions are simply the velocity boundary conditions on u.
After defining the following macros:

macro Grad(u1,u2) [dx(u1),dy(u1) , dx(u2),dy(u2) ] //


macro div(u1,u2) (dx(u1)+dy(u2)) //

to aid readability, as suggested by [11], the time evolution problem is represented by the following code,
also taken from [11]:

problem navierst(u1,u2,p,v1,v2,q,init=nsi) =
int2d(Th)(alpha*(u1*v1 + u2*v2)
+ nu*(Grad(u1,u2)’*Grad(v1,v2))
- 1e-8*p*q
- div(u1,u2)*q - div(v1,v2)*p)
+ int2d(Th) (-alpha*convect([up1,up2],-dt,up1)*v1
-alpha*convect([up1,up2],-dt,up2)*v2)
+ on(1,2,4,u1=0,u2=0) + on(3,u1=1,u2=0);

1 1
where alpha is ∆t , nu is Re , and u and v have been broken into components u1, u2, v1, v2. The
init parameter allows the matrices to be reused rather than regenerated each iteration, saving com-
putation time. [up1, up2] is the velocity field from the previous timestep, to be used in the convect
operator. A term p · q · 10−8 has been inserted, to aid numerical stability. Finally, the boundary condition
is implemented in the final line, where the labels 1-4 represent the sides of the square.

6.3 Newton iteration method


The second method is a Newton-style iterative procedure. The steady solution has u, p satisfying

(u · ∇)u − ν∇2 u + ∇p = 0 (6.3.1)

∇·u=0. (6.3.2)
The weak formulation is to find u, p such that ∀v, p,
Z  
1
((u · ∇) u) · v + ∇u : ∇v − p∇ · v − q∇ · u dA = 0 . (6.3.3)
A Re

Newton’s algorithm to solve the generic problem ‘find x in Rn such that F (x) = 0’ is:

1. evaluate the Jacobian DF that satisfies F (x + δx) = F (x) + DF (x)(δx) + O(δx2 )


2. choose some initial x0 ∈ Rn

3. find δxi such that DF (xi )δxi = F (xi ), and set xi+1 = xi − δxi
4. repeat 3 until |δxi | is sufficiently small

12
For the steady Navier-Stokes problem, we have
Z  
1
F (u, p) = ((u · ∇) u) · v + (∇u) : (∇v) − p∇ · v − q∇ · u dA (6.3.4)
A Re

DF (u, p) (δu, δp) =


Z  
1
((δu · ∇) u) · v + ((u · ∇) δu) · v + ∇δu : ∇v − δp∇ · v − q∇ · δu dA (6.3.5)
A Re

Note that the variables to be solved for are δu and v; u is taken as given in each iteration, therefore we
only have linear and bilinear terms. In FreeFem++ language, this becomes the following, also taken from
[11]:

problem NR([du1,du2,dp],[v1,v2,q],init=nri) =
int2d(Th)(nu*(Grad(du1,du2)’*Grad(v1,v2))
+ UgradV(du1,du2,u1,u2)’*[v1,v2]
+ UgradV(u1,u2,du1,du2)’*[v1,v2]
- 1e-8*dp*q
- div(du1,du2)*q - div(v1,v2)*dp)
- int2d(Th)(nu*(Grad(u1,u2)’*Grad(v1,v2))
+ UgradV(u1,u2,u1,u2)’*[v1,v2]
- div(u1,u2)*q - div(v1,v2)*p)
+ on(1,2,3,4,du1=0,du2=0);

where the additional macro

macro UgradV(u1,u2,v1,v2) [ [u1,u2]’*[dx(v1),dy(v1)] ,


[u1,u2]’*[dx(v2),dy(v2)] ] //

was used.
Again, a stabilisation term has been used. In addition, the boundary condition imposes δu = 0 on the
boundary, i.e. it requires the initial guess to satisfy the correct boundary conditions. While it is plausible
that if δu 6= 0 is not exactly satisfied, the end solution may no longer satisfy the boundary condition
exactly, in practice this is not an issue, due to the small number of Newton iterations performed. I later
verified this:
 in the steady solutions obtained, the velocities on the no-slip boundaries were typically
O 10−20 , as was the vertical velocity on the top plate; the horizontal velocity on the top plate was
exactly 1, since double-precision floating point numbers have some 15 or 16 decimal digits of accuracy.

6.4 Comments
Both the time evolution and Newton iteration methods require some initial velocity and pressure distri-
bution. While the flow in the former method may start from rest, this state cannot be used for the latter
method, since this does not satisfy the boundary conditions of the Driven Cavity problem. In practice, I
will use parameter continuation extensively, using solutions for similar Re, possibly interpolated from a
different mesh. Initially, I will use the solution to the Stokes problem with Re = 0, which is identical to
the time-evolution problem with the convect operator removed. Again, the following code is taken from
[11]:

problem stokes([u1,u2,p],[v1,v2,q]) =
int2d(Th)(Grad(u1,u2)’*Grad(v1,v2)
- 1e-8*p*q
- div(u1,u2)*q - div(v1,v2)*p)
+ on(1,2,4,u1=0,u2=0) + on(3,u1=1,u2=0);

13
Finally, it will later be shown that the streamfunction ψ can be used as a proxy for convergence of
other variables, and gives visually intuitive plots. The code used to generate this is again taken from [11]:

problem createsf(psi,phi,init=sfi) =
int2d(Th)(dx(psi)*dx(phi) + dy(psi)*dy(phi))
+ int2d(Th)(-phi*(dy(u1)-dx(u2)))
+ on(1,2,3,4,psi=0);

where we have used a test-function phi, and the relationship

∇2 ψ = −ω ,

where
∂uy ∂ux
ω= − ,
∂x ∂y
setting ψ = 0 on the boundary.
Much of the code used is to facilitate file I/O, solution caching, and other things that are less relevent
to the discussion here, but of great help when running simulations. A full code listing will be given in
the appendix.

7 Results
The following subsections relate to the time evolution method, unless specifically stated otherwise.

7.1 Runtime of individual functions; variation with n, Re, ∆t


The following tests were all carried out using FreeFem++-cs 12.3 running on Windows Vista SP2 (32-
bit), with negligible background activity, on a laptop containing an Intel Core 2 Duo T8300 processor.
Each test was repeated 3 times, with the median time taken. The maximum deviations were at most 1-2%
from the median.

Iterations Time (s)


0 0.055
100 0.826
200 1.556
300 2.270

Table 7.1.1: Re = 10, ∆t = 0.1, mesh 1 (uniform), n = 8. stokes and createsf were executed a
single time, then navierst was executed each iteration.

Iterations Time (s)


0 0.053
100 1.365
200 2.643
300 3.917

Table 7.1.2: Re = 10, ∆t = 0.1, mesh 1 (uniform), n = 8. stokes and createsf were executed a
single time, then navierst and createsf were executed each iteration.

As expected, the results in Tables 7.1.1 and 7.1.2 suggest that execution time scales linearly with
the number of iterations, with some extra time required for the stokes problem and the navierst
initialisation (recall that the internal matrices for navierst are stored to avoid repeated recalculation).

14
In addition, navierst and createsf take comparable time to run, which strongly suggests avoiding
making calls to createsf every time step.

mesh type n vertices interior vertices triangles Time (s)


1 8 81 49 128 3.015
1 10 121 81 200 4.516
1 12 169 121 288 6.452
3 20 224 144 366 8.468
1 14 225 169 392 8.731
1 16 289 225 512 11.627

Table 7.1.3: Re = 10, ∆t = 0.1, 400 iterations. stokes and createsf were executed a single time,
then navierst was executed each iteration.

In Table 7.1.3, we see that for the uniform grids, as expected, execution speed is approximately
quadratic in n. A non-uniform mesh result has been inserted for comparison. Despite having n = 20,
this has almost the same number of total vertices as the uniform mesh with n = 14. It has 14.8% fewer
interior vertices, and 6.6% fewer triangles, but execution time is (only) reduced by 3%, hence execution
time isn’t simply linear in one of these measures.

Re Time (s)
10 14.484
100 14.326
1000 14.011

Table 7.1.4: ∆t = 0.1, mesh 1 (uniform), n = 32, 100 iterations. stokes and createsf were executed
a single time, then navierst was executed each iteration.

In Table 7.1.4, we see that the time taken varies slightly (but consistently) with Re. Note that this
is unrelated to convergence towards a steady flow; we are running a fixed number of iterations. This is
somewhat unexpected, but is perhaps related to the iterative methods used in the internal sparse matrix
routines.

∆t Time (s)
0.200 17.368
0.100 14.304
0.050 12.739
0.025 12.015

Table 7.1.5: Re = 100, mesh 1 (uniform), n = 32, 100 iterations. stokes and createsf were executed
a single time, then navierst was executed each iteration.

In Table 7.1.5, we see that the execution time varies significantly with ∆t, in particular, it seems that
the execution time goes like t0 + a∆t. To the author, it seems most likely that this behaviour is due to the
inbuilt convect function: it is plausible that the timestep ∆t is being broken down into smaller units,
and the flow is being convected through each of these smaller time steps (where the size of the small
time unit is chosen to meet some error tolerance in each small time step). Later results on solution errors
will lend support to this theory.

7.2 ψ as a proxy for convergence


Here, we aim to show that ψ, u and p converge simultaneously, so that it is sufficient to check for
convergence of ψ. The following simulations were done, to cover a range of Re and mesh size, all on a
uniform mesh:

15
1. n = 8, ∆t = 0.2, Re = 10, 50 steps
2. n = 16, ∆t = 0.1, Re = 100, 200 steps
3. n = 32, ∆t = 0.05, Re = 1000, 800 steps

The main time-step loop was modified by to calculate the differences ∆pn = pn −pn−1 , sim. ∆unx , ∆uny ,
∆ψ n , and output the maximum values k∆pn k∞ , etc., to file every iteration. These were then plotted in
Excel.
As can be seen in figures 7.2.1-7.2.3, ψ converges at the same rate as u and p. The logs are in fact to
base 10, and in the first picture, we reach the limits of machine precision before step 50 (about 16 decimal
digits; kux k∞ and kuy k∞ ≈ 1, kψk∞ ≈ 0.1, kpk∞ is a few orders of magnitude larger, reflecting the
theoretical singularities in the upper corners.)

Figure 7.2.1: n = 8, ∆t = 0.2, Re = 10

Figure 7.2.2: n = 16, ∆t = 0.1, Re = 100

16
Figure 7.2.3: n = 32, ∆t = 0.05, Re = 1000

7.3 Convergence
Here, we shall investigate how quickly the flow converges to a steady solution, and how this changes with
∆t, Re and n.

7.3.1 Dependence on ∆t

The following simulations were all done with n = 32, which is a reasonable mesh to use for obtaining
results (unlike n = 8!) Firstly it was noticed that there were occasions on which ψ would not converge
to an accuracy of 10−16 , only to 10−11 or so (for example, the combination Re = 150, ∆t = 0.2 or
0.15). Thus, convergence will be deemed to have been reached when k∆ψ n k∞ < 10−10 . The following
simulations were done, starting from the solution to the Stokes problem:

1. Re = 25: ∆t = 0.0125, 0.025, 0.05, 0.1, 0.2


2. Re = 100: ∆t = 0.0125, 0.025, 0.05, 0.1, 0.2
3. Re = 400: ∆t = 0.05, 0.07, 0.1, 0.14, 0.2

In all simulations, after initial transients decay, there is a clear regime of exponential decay towards
a steady state. A typical example is shown in figures 7.3.1 and 7.3.2.

17
Figure 7.3.1: k∆ψ n k∞ /k∆ψ n−1 k∞ against n, for Re = 100; ∆t = 0.0125 (top) to 0.2 (bottom)

Figure 7.3.2: Inside the exponential decay regime: ln(k∆ψ n k∞ ) against n, for Re = 100; ∆t = 0.0125
(right) to 0.2 (left)

It is natural to plot a log-log graph of the slopes in Figure 7.3.2 (and similar graphs for Re = 25 and
400) against ∆t, in this is done in Figures 7.3.3 to 7.3.5.

18
Figure 7.3.3: Re = 25 (∆t = 0.2 omitted due to insufficient time in the exponential decay regime)

Figure 7.3.4: Re = 100

Figure 7.3.5: Re = 400

19
1
These indeed show that the number of steps needed to converge ∝ ∆t - but this was clearly to be
expected! This is, of course, consistent with the program modelling the physical flow accurately.

7.3.2 Dependence on Re

The following simulations were done:

1. ∆t = 0.05: Re = 25, 50, 100, 200, 400


2. ∆t = 0.1: Re = 25, 50, 100, 200, 400

3. ∆t = 0.2: Re = 25, 50, 100, 200, 400

Again, log-log graphs were plotted of slope against Re.

Figure 7.3.6: ∆t = 0.05

Figure 7.3.7: ∆t = 0.1

20
Figure 7.3.8: ∆t = 0.2

The relationship in Figures 7.3.6-7.3.8 is less clear-cut. The number of steps needed to converge
unsurprisingly increases with Re, but doesn’t seem to follow a simple power law. In the Re = 25 to Re
= 400 regime, the number of steps ∝ Re0.775 or so, as a first approximation, but the deviations from a
straight line on the log-log plots are consistent for the different ∆t’s.

7.3.3 Dependence on n

The following simulations were done:

1. Re = 10, ∆t = 0.1: n = 16, 23, 32, 45, 64


2. Re = 100, ∆t = 0.1: n = 16, 23, 32, 45, 64

n steps
16 29
23 27
32 26
45 27
64 28

Table 7.3.1: Re = 10, ∆t = 0.1: steps taken to converge, starting from the Stokes solution

n steps
16 267
23 268
32 268
45 268
64 268

Table 7.3.2: Re = 100, ∆t = 0.1: steps taken to converge, starting from the Stokes solution

As can be seen from tables 7.3.1 and 7.3.2, the number of steps needed to converge is (essentially)
independent of the mesh used. This increases our confidence that we are indeed modelling the physical
flow somewhat accurately.

7.4 Elementary failures of convergence


We expect that our problem will fail to converge at sufficiently large Re. We will investigate this briefly.

21
Re Result
400 converges to steady
800 converges to steady
1500 converges to steady
1600 oscillates, fails to converge
3200 converges to a spurious double-loop solution
6400 spurious double loop (seemingly unsteady)
10000 spurious double loop (unsteady)
20000 spurious double loop (almost nonsense)
40000 nonsense

Table 7.4.1: n = 8, ∆t = 0.2

Re Result
1500 converges to steady
1600 converges to steady
1800 converges to steady
1850 converges to steady
1875 spurious double loop
1900 spurious double loop
2000 spurious double loop

Table 7.4.2: n = 8, ∆t = 0.1

Re Result
1600 converges to steady
3200 converges to steady
6400 converges to steady
8000 converges to steady
9000 spurious double loop

Table 7.4.3: n = 16, ∆t = 0.2

The above results show that convergence can be achieved at higher Re by decreasing ∆t, but mainly
by increasing n.
Typical streamfunction plots of the steady, double-loop and nonsense states are shown in figures
7.4.1-7.4.3. The isovalues plotted are not evenly spaced, and are instead chosen to emphasise the non-
primary vortices. It is interesting to see that we obtain the same spurious double-loop solution as those
seen in the literature, as mentioned previously, and alluded to in [6].

22
Figure 7.4.1: n = 8, ∆t = 0.2, Re = 400, steady solution

Figure 7.4.2: n = 8, ∆t = 0.2, Re = 3200, steady spurious double loop

Figure 7.4.3: n = 8, ∆t = 0.2, Re = 40000, very unsteady solution (though slightly coherent)

23
7.5 Steady solutions with the time-evolution method
As a side-effect of running the previous simulations, it became clear that the results were not independent
of ∆t and n, even though the streamline plots were superficially similar. From this point onwards, we
shall restrict our attention to the flow variable ψmax , which corresponds to the value of ψ at the centre of
the primary vortex, typically ≈ 0.1. This is used throughout the literature in comparisons between papers
(in papers where the lid moves from right to left, this becomes ψmin - needless to say the conversion is
trivial).

7.5.1 Dependence on ∆t

The following simulations were run, for n = 16:

1. Re = 25: ∆t = 0.4, 0.28, 0.2, 0.14, 0.1, 0.07, 0.05, 0.035, 0.025, 0.0175, 0.0125, 0.01

2. Re = 100: ∆t = 0.4, 0.28, 0.2, 0.14, 0.1, 0.07, 0.05, 0.035, 0.025, 0.0175, 0.0125, 0.01
3. Re = 400: ∆t = 0.4, 0.28, 0.2, 0.14, 0.1, 0.07, 0.05, 0.035, 0.025, 0.0175, 0.0125, 0.01

Results are in Table 7.5.1; this is plotted in Figure 7.5.1.

ψmax
∆t Re = 25 Re = 100 Re = 400
0.4 0.0938137 0.0853667 0.07012
0.28 0.0951156 0.0883954 0.074631
0.2 0.0960474 0.0908261 0.078839
0.14 0.0967591 0.0928685 0.0829783
0.1 0.0972329 0.0943222 0.0861688
0.07 0.0975783 0.0955438 0.089281
0.05 0.0977932 0.0963963 0.0916376
0.035 0.097945 0.0969928 0.0935377
0.025 0.0980452 0.0974017 0.0948252
0.0175 0.0981256 0.0977299 0.0956418
0.0125 0.0981776 0.0979501 0.0962699
0.01 0.0982043 0.0980684 0.0966181

Table 7.5.1: Dependence on ∆t: Re = 25, 100, 400; n = 16

24
Figure 7.5.1: ψmax against ∆t, n = 16

The above plot strongly suggests that the leading-order error term is O (∆t). This is in agreement
with my earlier hypothesis about the convect operator: that the timestep ∆t is broken down into
smaller timesteps, such that the error during each small timestep is smaller than some tolerance - leading
to the total error being linear in ∆t.

7.5.2 Dependence on n

The following simulations were run, for Re = 100:

1. ∆t = 0.1: n = 8, 11, 16, 23, 32, 45, 64


2. ∆t = 0.05: n = 8, 11, 16, 23, 32, 45, 64
3. ∆t = 0.025: n = 8, 11, 16, 23, 32, 45, 64

Results are in Table 7.5.2; this is plotted in Figure 7.5.2.

ψmax
n ∆t = 0.1 ∆t = 0.05 ∆t = 0.025
8 0.0899634 0.0914896 0.0923374
11 0.0922175 0.093955 0.0948482
16 0.0943222 0.0963963 0.0974017
23 0.0954286 0.0977388 0.0988729
32 0.0960135 0.0984644 0.0997386
45 0.0963704 0.0990315 0.100403
64 0.0965907 0.0993701 0.100855

Table 7.5.2: Dependence on n: ∆t = 0.1, 0.05, 0.025; Re = 100

25
Figure 7.5.2: ψmax against n−1 , Re = 100

1

The above plot suggests that the leading-order error term is O n , but this is not completely clear,
especially for ∆t = 0.1.

7.5.3 Repeated extrapolation

We have seen that naive extrapolation in n may be of questionable use. It seems to reasonable to attempt
extrapolation in ∆t, then extrapolation in n: the former giving a ‘perfect’ solution for a given mesh, the
latter removing the mesh dependence.
For Re = 10, 100, we will linearly extrapolate the results for ∆t = 0.05 and 0.025 to give a result for
∆t = 0, and see how this varies with n, for n = 16, 19, 23, 27, 32, 38, 45, 54, 64, 76, 91.
Results are given in Tables 7.5.3 and 7.5.4, and the extrapolated values are plotted in Figure 7.5.3.

ψmax
n ∆t = 0.05 ∆t = 0.025 ∆t = 0 (Extr)
16 0.0980122 0.0981159 0.0982196
19 0.0985264 0.0986324 0.0987384
23 0.0987878 0.0988976 0.0990074
27 0.0989469 0.0990607 0.0991745
32 0.0991109 0.0992281 0.0993453
38 0.0992271 0.0993484 0.0994697
45 0.0993098 0.0994345 0.0995592
54 0.099394 0.099523 0.099652
64 0.0994797 0.0996122 0.0997447
76 0.09953 0.0996659 0.0998018
91 0.0995841 0.099723 0.0998619

Table 7.5.3: Re = 10: dependence of extrapolated result on n

26
ψmax
n ∆t = 0.05 ∆t = 0.025 ∆t = 0 (Extr)
16 0.0963963 0.0974017 0.0984071
19 0.097095 0.0981454 0.0991958
23 0.0977388 0.0988729 0.100007
27 0.0981572 0.0993668 0.1005764
32 0.0984644 0.0997386 0.1010128
38 0.0987623 0.100107 0.1014517
45 0.0990315 0.100403 0.1017745
54 0.0992281 0.100669 0.1021099
64 0.0993701 0.100855 0.1023399
76 0.0994811 0.101009 0.1025369
91 0.0995733 0.101145 0.1027167

Table 7.5.4: Re = 100: dependence of extrapolated result on n

Figure 7.5.3: ψmax against n−1 , Re = 10 and 100

Linear extrapolation in n now seems more justified. This gives a modus operandi: for a given Re,

1. For a given n, obtain a converged solution at two small values of ∆t


2. For this n, use the above results to extrapolate a solution for ∆t = 0

3. Use the results at several values of n to extrapolate a result as n−1 → 0

7.6 Comparison with literature


We use ∆t = 0.035 and 0.025, n = 45 and 64.

27
ψmax
n ∆t = 0.035 ∆t = 0.025 ∆t = 0 (Extr)
45 0.0998591 0.100403 0.101763
64 0.100258 0.100855 0.102348
Result: 0.103732
Ghia, Ghia & Shin ’82 [4]: 0.103423

Table 7.6.1: Re = 100

ψmax
n ∆t = 0.035 ∆t = 0.025 ∆t = 0 (Extr)
45 0.101282 0.10317 0.10789
64 0.102364 0.104456 0.109686
Result: 0.11394
Ghia, Ghia & Shin ’82 [4]: 0.113909

Table 7.6.2: Re = 400

ψmax
n ∆t = 0.035 ∆t = 0.025 ∆t = 0 (Extr)
45 0.0974772 0.100418 0.10777
64 0.0989792 0.102308 0.11063
Result: 0.117404
Erturk, Corke & Gokcol ’05 [8]: 0.118942
Barragy & Carey ’97 [7]: 0.118930
Schreiber & Keller ’83 [5]: 0.11894
Benjamin & Denny ’79 [3]: 0.1193
(Ghia, Ghia & Shin ’82 [4]:) (0.117929)

Table 7.6.3: Re = 1000 - note the result from Ghia et. al. is somewhat different to the others

ψmax
n ∆t = 0.035 ∆t = 0.025 ∆t = 0 (Extr)
45 0.0890742 0.0929585 0.102669
64 0.0909806 0.0954064 0.106471
Result: 0.115475
Erturk, Corke & Gokcol ’05 [8]: 0.121470

Table 7.6.4: Re = 2500 - the extrapolated result is now way off

The results for Re = 100 and 400 seem promising, although the results available in the literature are
limited. By Re = 1000, while results in the literature cluster around 0.1189, the extrapolated result is only
0.1174, and the gap widens even more for Re = 2500. We must be able to do better...

7.7 Non-uniform meshes


Everything up to now has used the uniform mesh within a square. We now introduce the custom non-
uniform meshes. One downside is that we cannot expect to get useful results by extrapolation in n, due
to the non-uniformity of the meshes, but we can still extrapolate in ∆t. Results are given below, in tables
7.7.1-7.7.4.

28
ψmax
n ∆t Mesh 1 Mesh 2 Mesh 3
32 0.035 0.099233 0.101296 0.101554
0.025 0.0997386 0.101824 0.102077
0 (Extrap) 0.101002 0.103144 0.103385
45 0.035 0.0998591 0.101393 0.101558
0.025 0.100403 0.101951 0.102092
0 (Extrap) 0.101763 0.103346 0.103427
64 0.035 0.100258 0.101409 0.101521
0.025 0.100855 0.101996 0.102083
0 (Extrap) 0.102348 0.103464 0.103488
Previous result: 0.103732
Ghia, Ghia & Shin ’82 [4]: 0.103423

Table 7.7.1: Re = 100

ψmax
n ∆t Mesh 1 Mesh 2 Mesh 3
32 0.035 0.099616 0.105359 0.106320
0.025 0.101374 0.107347 0.108314
0 (Extrap) 0.105770 0.112317 0.113299
45 0.035 0.101282 0.105701 0.106256
0.025 0.103170 0.107802 0.108320
0 (Extrap) 0.107890 0.113055 0.113480
64 0.035 0.102364 0.105720 0.106096
0.025 0.104456 0.107862 0.108210
0 (Extrap) 0.109686 0.113217 0.113495
Previous result: 0.113940
Ghia, Ghia & Shin ’82 [4]: 0.113909

Table 7.7.2: Re = 400

ψmax
n ∆t Mesh 1 Mesh 2 Mesh 3
32 0.035 0.095061 0.103895 0.105330
0.025 0.097759 0.106935 0.108692
0 (Extrap) 0.104504 0.114535 0.117097
45 0.035 0.097477 0.104151 0.105194
0.025 0.100418 0.107607 0.108740
0 (Extrap) 0.107770 0.116247 0.117605
64 0.035 0.098979 0.104064 0.104619
0.025 0.102308 0.107584 0.108130
0 (Extrap) 0.110630 0.116384 0.116908
Previous result: 0.117404
Erturk, Corke & Gokcol ’05 [8]: 0.118942

Table 7.7.3: Re = 1000

29
ψmax
n ∆t Mesh 1 Mesh 2 Mesh 3
32 0.035 0.086160 0.097610 0.101349
0.025 0.089171 0.101193 0.106556
0 (Extrap) 0.096699 0.110150 0.119574
45 0.035 0.089074 0.098326 0.099634
0.025 0.092959 0.103111 0.104757
0 (Extrap) 0.102669 0.115074 0.117565
64 0.035 0.090981 0.098019 0.098997
0.025 0.095406 0.102901 0.103961
0 (Extrap) 0.106471 0.115107 0.116371
Previous result: 0.115475
Erturk, Corke & Gokcol ’05 [8]: 0.121470

Table 7.7.4: Re = 2500

Clearly the individual results are far more accurate on the non-uniform meshes. The results on Mesh
3, with n = 32 and ∆t = 0.035 are more accurate than the results on the uniform mesh with n = 64
and ∆t = 0.025 for all four values of Re (now is a good time to recall the past finding that simulations
on Meshes 2 and 3 are faster than on Mesh 1 at the same n). Unfortunately, we give up a lot by not
being able to extrapolate in n−1 (indeed, some of the results above aren’t even monotonic in n), and the
doubly-extrapolated Mesh 1 results are generally better than the singly-extrapolated Mesh 2 and Mesh 3
results.

7.8 The Newton method


As has been shown, the IVP formulation could be made to give reasonably accurate results. However, the
execution speed was far from optimal, and the simulations at Re = 2500 on the finest meshes were taking
several hours (parameter continuation did not reduce this much, since the solution changed by 10−3 to
10−2 when ∆t, n or Re were changed, while we demanded convergence to 10−10 ).
The Newton method executes much more quickly. However, it is unlikely to converge unless the
initial flow is somewhat close to the steady flow, so parameter continuation will be used extensively. A
typical set of results is given below, in Table 7.8.1. These are plotted in Figure 7.8.1.

ψmax
n Mesh 1 Mesh 2 Mesh 3 Comment
32 0.105417 0.116718 0.118166 initialised with time evolution solution with ∆t = 0.025
45 0.109077 0.117812 0.118725 initialised with time evolution solution with ∆t = 0.025
64 0.111891 0.118365 0.118819 initialised with time evolution solution with ∆t = 0.025
91 0.111372 0.118350 0.118864 initialised with Newton-64 solution
128 0.111377 0.118377 0.118881 initialised with Newton-91 solution
Previous result: 0.117404
Erturk, Corke & Gokcol ’05 [8]: 0.118942

Table 7.8.1: Re = 1000

30
Figure 7.8.1: ψmax against n−1 , Re = 1000, Newton Method

A typical result is shown in Table 7.8.1. Although it is not immediately obvious that something is
wrong, Figure 7.8.1 suggests that there is a problem. It seems that the solutions obtained at n = 91,
128 are spurious. This was not obvious, since the streamline plots were almost identical to the genuine
solution. Note that these were continued from other Newton results, while the ones continued from the
IVP were fine. The accuracy of the genuine solutions should be emphasised, though. At n = 64, on Mesh
3, we are a mere 10−4 away from the answers given in the literature, and the Newton convergence took
well under a minute. However, we did begin with the result of an IVP simulation that took several hours.

7.9 A New(ton) Hope?


The findings from the previous section suggest a unified method: one that uses the incredibly fast conver-
gence of the Newton method, with occasional time evolution steps to ensure we are on the right solution
branch. While the method I settled on is far from perfect, it seemed to work: Given an initial guess for
the steady flow,

1. Carry out a small number of time evolution steps.


2. Carry out Newton iterations. If convergence does not occur, reload time evolution results, reduce
time step but increase number of steps.
3. Repeat 1 and 2 until two consecutive Newton procedures converge, and to the same result.

It is certainly feasible that we may converge to the same spurious solution twice. However, it was found
that we indeed converged to the same flow as the solutions above that were initialised with a time evolu-
tion steady state, but to a slightly different flow to the solutions generated above that were initialised by
a Newton method solution.
Using this method, steady solutions were generated on Mesh 3 with n = 128, 160, 192. Steady
solutions were generated for Re = 100 to 5000 in steps of 100, and beyond Re = 5000 in steps of 500.
Each attempt was initialised with the steady solution of the previous Re, with Re = 100 initialised with
the Stokes solution. At the largest few values of Re (for each n), steps of 500 were insufficient to prevent
blowup, and steps of 100 were used. In addition, the timestep ∆t was decreased from 0.1 to as little as
0.001. Results will be listed below, with comparisons made to extrapolated results in Erturk et. al. [8] and
Barragy & Carey [7] where appropriate. Later simulations with n = 193 and 200 were also done, these
results are also included below.

31
ψmax
Re n = 128 n = 160 n = 192 n = 193 n = 200 Erturk [8] Barragy [7]
500 0.115411 0.115414 0.115420 0.115419 0.115419
1000 0.118923 0.118929 0.118932 0.118933 0.118934 0.118942 0.118930
1500 0.120320 0.120321 0.120327 0.120329 0.120329
2000 0.121036 0.121042 0.121043 0.121046 0.121045
2500 0.121460 0.121461 0.121464 0.121463 0.121463 0.121470 0.121462
3000 0.121726 0.121732 0.121734 0.121735 0.121735
3500 0.121919 0.121917 0.121919 0.121919 0.121921
4000 0.122056 0.122055 0.122050 0.122054 0.122051
4500 0.122163 0.122156 0.122149 0.122151 0.122148
5000 0.122244 0.122231 0.122222 0.122224 0.122221 0.122233 0.122219
7500 0.122425 0.122408 0.122388 0.122390 0.122384 0.122386 0.122380
10000 0.122474 0.122446 0.122408 0.122418 0.122404 0.122390 0.122393
12500 0.122497 0.122446 0.122384 0.122403 0.122379 0.122326 0.122358
15000 0.122499 0.122436 0.122343 0.122374 0.122338 0.122245
17500 0.122477 0.122426 0.122299 0.122335 0.122291 0.122167 -
20000 0.122445 0.122416 0.122257 0.122292 0.122242 0.122084 -
(21000) 0.122434 0.122411 0.122242 0.122275 0.122223 0.122056 -
22500 0.122423 0.122404 0.122223 0.122249 0.122196 - -
25000 0.122420 0.122391 0.122198 0.122210 0.122155 - -
27500 0.122441 0.122374 0.122175 0.122173 0.122121 - -
30000 0.122474 0.122356 0.122143 0.122139 0.122092 - -
32500 0.122470 0.122343 0.122094 0.122105 0.122069 - -
35000 0.122312 0.122346 0.122016 0.122069 0.122048 - -
(37200) 0.122392 - -
37500 - 0.122381 0.121907 0.122031 0.122030 - -
40000 - 0.122458 0.121773 0.121994 0.122013 - -
42500 - 0.122578 0.121625 0.121958 0.121995 - -
45000 - 0.122746 0.121469 0.121922 0.121976 - -
(47300) - 0.121961 - -
47500 - 0.122977 0.121303 0.121896 - - -
(48600) - 0.121887 - - -
50000 - 0.123296 0.121146 - - - -
52500 - 0.123915 0.120986 - - - -
(53900) - 0.124354 - - - -
55000 - - 0.120840 - - - -
57500 - - 0.120712 - - - -
60000 - - 0.120609 - - - -
62500 - - 0.120555 - - - -
(63600) - - 0.120542 - - - -

Table 7.9.1: Results with unified method (‘-’ indicates a failure to reach a steady solution; a blank cell
indicates that a steady solution was reached for some higher Re, but no simulation was done at this Re.
In particular, Barragy and Carey obtained a solution at Re = 16,000, but did not publish their ψmax )

Several plots of streamfunctions of steady flows generated by the above method follow. Isovalues will
be plotted at -0.03, -0.02, -0.01, -3×10−3 , -10−3 , ..., -3×10−8 , -10−8 , -10−9 , 0, 10−9 , 10−8 , 3×10−8 ,
..., 10−3 , 3×10−3 , 0.01, 0.02, ..., 0.10, 0.11, 0.12 so that the weak corner vortices may be clearly seen.
Finally, two figures will be shown of the computed ux and uy velocities along lines through the
geometric centre of the cavity.

32
Figure 7.9.1: Mesh 3, n = 192. To be used in the plots below.

Figure 7.9.2: Re = 0, solution of the Stokes problem. Note the left-right symmetry. Secondary vortices
in the bottom corners are visible.

33
Figure 7.9.3: Unified method, Re = 100. The primary vortex centre has been shifted downstream and is
slightly lower. The bottom-right secondary vortex has grown, the bottom-left vortex has shrunk slightly.

Figure 7.9.4: Unified method, Re = 400. The primary vortex centre approaches the centre of the cavity.
The secondary vortices have grown, and a tertiary vortex in the bottom-right is just visible.

34
Figure 7.9.5: Unified method, Re = 1000. The secondary vortices have grown further. We now have a
largely inviscid primary vortex.

Figure 7.9.6: Unified method, Re = 1500. Note the formation of a secondary vortex in the top-left. The
appearance of this between Re = 1000 and 1500 is consistent with the literature.

35
Figure 7.9.7: Unified method, Re = 2500. The secondary vortex in the top-left and its impact on the flow
are now obvious. Tertiary vortices in both bottom corners are just visible.

Figure 7.9.8: Unified method, Re = 10000. Tertiary vortices in the bottom corners are clearly formed.

36
Figure 7.9.9: Unified method, Re = 15000. A tertiary vortex in the top-left can be identified.

Figure 7.9.10: Unified method, Re = 30000. A fourth-order vortex can be clearly seen in the bottom-right
corner; a fourth-order vortex in the bottom-left may also be visible.

37
Figure 7.9.11: Unified method, Re = 50000. The fourth-order vortex in the bottom-left is more visible.

Figure 7.9.12: Unified method, Re = 63600. Both fourth-order vortices are clearly visible. In the up-
per left corner, a second tertiary vortex may have formed below the first tertiary vortex (compare the
secondary vortex at c. 1500, and the first tertiary vortex at c. 10000).

38
Figure 7.9.13: Unified method, ux velocities along the line x = 0.5, for several values of Re. These are
in agreement with results in the literature, specifically those in [8]. The non-monotinicity near the lid is
not an artifact.

Figure 7.9.14: Unified method, uy velocities along the line y = 0.5, for several values of Re. Note the
largely inviscid core in the above diagrams, for Re ≥ 1000.

39
8 Conclusion
The results with the unified method show that FreeFem++ is very capable. The author believes that these
are the first qualitatively correct, converged, steady solutions that have been found for Re > 21,000.
In particular, the author believes that the second tertiary vortex seen forming in the Re = 63,600 plot
has not been seen in the literature before. While Nallasamy and Prasad [19] presented results up to Re
= 50,000, their ψmax for Re = 1,000 was some 20% below the 0.1189 common in the literature, and
their streamfunction contours for Re = 30,000 resemble ‘rounded squares’ and give no indication of the
rich counter-rotating vortex structure seen here and elsewhere in the literature, thus their results can be
discounted. Erturk et. al. write “Nallasamy and Prasad have presented steady solutions for Reynolds
numbers up to Re ≤ 50000, however, their solutions are believed to be inaccurate as a result of excessive
numerical dissipation caused by their first order upwind difference scheme.”
The n = 192, 193, 200 results tabulated above are in perfect agreement with the existing literature
until at least Re = 7,500. However, the variation with n is very interesting. While there is a general trend
between n = 128, 160 and 192, the difference between results at the same Re for n = 192 and n = 193
suggest that there is some ‘randomness’ that depends on the exact triangulation used. In addition, I was
able to obtain converged results beyond Re = 50,000 for n = 160 and 192, but not for n = 193 and 200!
There is a sufficiently large difference between the results obtained for different n (and sufficiently little
existing literature) that I only claim the solutions at very high Re are qualitatively correct. Beyond Re =
10,000 or so, the difference between the n = 192, 193, 200 solutions suggest that these are fundamen-
tally different (although visually similar) solutions. Though one might be tempted to put this down to
bifurcations, repeated attempts to reach a different solution ‘branch’ for Re ≤ 30,000 for n = 128 were
completely unsuccessful. Vorticity plots at high Re (not shown) suggest the solutions are underresolved
for Re & 5,000.
One thing that should be emphasised is the simplicity of the algorithms used here. In particular, both
algorithms were taken from the FreeFem++ manual, and once the time-evolution convergence results had
been seen, it became an obvious to use the Newton method to speed up the ‘slow’ transient decay. This
was even more of a no-brainer when the lack of independence of the steady solution on the timestep ∆t
was noticed. An obvious thing that was not investigated in this essay was the use of the adaptmesh
function to generate a non-uniform mesh. This was briefly tested: due to the singularities, the upper
corners were given an incredibly high density of elements, while the lower corners were much more
sparesly populated. It is unlikely that the fourth-order vortices would have been identified, due to the low
mesh density in these corners. In addition, it was relatively hard to control the number of triangles in
the mesh produced: this had to be done through trial-and-error by setting a tolerance parameter. There
is a command to limit the number of triangles produced, but this seemed to simply halt the triangulation
algorithm prematurely, rather than giving an optimal mesh. Due to time constraints, this was therefore
dropped.
The non-uniform meshes lacked the desireable property of being able to meaningfully extrapolate
results in n, and in addition, the lack of structure in the mesh led to some degree of ‘randomness’ as seen
above. Barragy and Carey [7] used a ‘stretched grid’ with predictable structure; perhaps that would have
led to better results. Finally, FreeFem++ refused to run the algorithms on overly dense meshes: I had
planned to use the unified method on larger n, say n = 256, but I was unable to go beyond n = 200. This
was perhaps due to numerical tolerance issues. Nevertheless, this was slightly disappointing. Execution
time was barely an issue, even for n = 200, each solution took a minute or two, and solutions in intervals
of 500 up to Re = 50,000 could be generated in a few hours.

A Directory Structure and Code Listings


The files should all be placed in the same directory. The subdirectory /cache/ is used to store both
incomplete and complete calculations, described in further detail below. Filenames are of the form
mesh-n-Re-dt-variable.txt, where mesh is the mesh type 1, 2 or 3, and the parameter dt
is ∆t for an IVP solution, 0 for a Newton solution, and -1 for a Unified solution. variable is velou,

40
velov, sfunc, vorti or press, for the velocity components, pressure, streamfunction and vorticity
respectively. The subdirectory /mesh/ is used to store the meshes. The subdirectory /output/ is used
to store log-style output, including variables like ψmax . The main file is CavFunc.idp. This contains
a single function Cavity, which aims to find a steady solution using a particular method, for a given
Re, for a given timestep, on a given mesh, starting from a specified initial state. It is intended that this
is called via CavWrap.edp, either using the parameters listed within that file, or as a loop, to facilitate
batch runs.
The caching system should be briefly explained: the function in CavFunc.idp takes boolean pa-
rameters toload and tosave, and a set of loading parameters, corresponding to a particular solution
to load. If toload is set to false, the loading parameters will be ignored and a Stokes solution will be
generated before timesteps or Newton iterations are carried out. If toload is set to true, the /cache/
subdirectory will firstly be examined for velocity and pressure files corresponding to the current simu-
lation. If velocity and pressure files corresponding to the current simulation exist, these will be loaded,
and timesteps or Newton iterations will begin (if the loaded solution corresponds to the converged steady
state then the timesteps/Newton iterations will soon terminate). If the files corresponding to the current
simulation do not exist, velocity and pressure files corresponding to the loading parameters will be used
(if these do not exist, an exception will be thrown, and FreeFem++ will crash). If tosave is set to
true, velocity, pressure, vorticity and streamfunction files will be saved when a converged solution is
reached (all methods), and for the time evolution method only, every 100 timesteps. The streamfunction
and vorticity variables are considered derived quantities, and hence only the velocity and pressure files
are used when loading. The streamfunction and vorticity may of course be plotted.
Code listings follow:

meshgen.edp

/* This program is used to generate the meshes */


for (int n=8;n<=32;n++) // or whatever
{
mesh Ah, Bh, Ch;

Ah=square(n,n);

border C1(t=0,1){x=3*tˆ2-2*tˆ3;y=0;label=1;};
border C2(t=0,1){x=1;y=3*tˆ2-2*tˆ3;label=2;};
border C3(t=1,0){x=3*tˆ2-2*tˆ3;y=1;label=3;};
border C4(t=1,0){x=0;y=3*tˆ2-2*tˆ3;label=4;};
Bh = buildmesh(C1(n)+C2(n)+C3(n)+C4(n));

border D1(t=0,1){x=10*tˆ3-15*tˆ4+6*tˆ5;y=0;label=1;};
border D2(t=0,1){x=1;y=10*tˆ3-15*tˆ4+6*tˆ5;label=2;};
border D3(t=1,0){x=10*tˆ3-15*tˆ4+6*tˆ5;y=1;label=3;};
border D4(t=1,0){x=0;y=10*tˆ3-15*tˆ4+6*tˆ5;label=4;};
Ch = buildmesh(D1(n)+D2(n)+D3(n)+D4(n));

//plot(Ah,grey=1);
//plot(Bh,grey=1);
//plot(Ch,grey=1);
savemesh(Ah,"mesh\\1-"+n+"-mesh.msh");
savemesh(Bh,"mesh\\2-"+n+"-mesh.msh");
savemesh(Ch,"mesh\\3-"+n+"-mesh.msh");
}

41
load.idp

/* Loads velocity components and pressure from files */

// Should really put { } around the whole thing, but only called once
int fileexist = 1;
// If file doesn’t exist, exception is thrown; fileexist set to 0
try {
ifstream f1("cache\\"+meshtype+"-"+n+"-"+re+"-"+qdt+"-velou.txt");
} catch (...) fileexist = 0;

if (fileexist) {
ifstream f1("cache\\"+meshtype+"-"+n+"-"+re+"-"+qdt+"-velou.txt");
ifstream f2("cache\\"+meshtype+"-"+n+"-"+re+"-"+qdt+"-velov.txt");
ifstream f3("cache\\"+meshtype+"-"+n+"-"+re+"-"+qdt+"-press.txt");
f1 >> u1[]; f2 >> u2[]; f3 >> p[];
} else {
ifstream f1("cache\\"+ltype+"-"+ln+"-"+lre+"-"+ldt+"-velou.txt");
ifstream f2("cache\\"+ltype+"-"+ln+"-"+lre+"-"+ldt+"-velov.txt");
ifstream f3("cache\\"+ltype+"-"+ln+"-"+lre+"-"+ldt+"-press.txt");
// if we are on a different mesh, convert to new mesh
if ((meshtype != ltype) || (n != ln)) {
mesh ThA = readmesh("mesh\\"+ltype+"-"+ln+"-mesh.msh");
fespace XhA(ThA,P2);
fespace MhA(ThA,P1);
XhA u1A, u2A;
MhA pA;
f1 >> u1A[]; f2 >> u2A[]; f3 >> pA[];
u1 = u1A; u2 = u2A; p = pA; // convert
} else {
f1 >> u1[]; f2 >> u2[]; f3 >> p[];
}
}

save.idp

/* Saves velocity, pressure and streamfunction to file */

ofstream f1("cache\\"+meshtype+"-"+n+"-"+re+"-"+qdt+"-velou.txt");
ofstream f2("cache\\"+meshtype+"-"+n+"-"+re+"-"+qdt+"-velov.txt");
ofstream f3("cache\\"+meshtype+"-"+n+"-"+re+"-"+qdt+"-press.txt");
ofstream f4("cache\\"+meshtype+"-"+n+"-"+re+"-"+qdt+"-sfunc.txt");
ofstream f5("cache\\"+meshtype+"-"+n+"-"+re+"-"+qdt+"-vorti.txt");

f1 << u1[]; f2 << u2[]; f3 << p[]; f4 << psi[]; f5 << w[];

42
problems.idp

macro Grad(u1,u2) [dx(u1),dy(u1) , dx(u2),dy(u2) ] //


macro UgradV(u1,u2,v1,v2) [ [u1,u2]’*[dx(v1),dy(v1)] ,
[u1,u2]’*[dx(v2),dy(v2)] ] //
macro div(u1,u2) (dx(u1)+dy(u2)) //

problem createsf(psi,phi,init=sfi) =
int2d(Th)(dx(psi)*dx(phi) + dy(psi)*dy(phi))
+ int2d(Th)(-phi*(dy(u1)-dx(u2)))
+ on(1,2,3,4,psi=0);

problem stokes([u1,u2,p],[v1,v2,q]) =
int2d(Th)(Grad(u1,u2)’*Grad(v1,v2)
- 1e-8*p*q
- div(u1,u2)*q - div(v1,v2)*p)
+ on(1,2,4,u1=0,u2=0) + on(3,u1=1,u2=0);

problem navierst(u1,u2,p,v1,v2,q,init=nsi) =
int2d(Th)(alpha*(u1*v1 + u2*v2)
+ nu*(Grad(u1,u2)’*Grad(v1,v2))
- 1e-8*p*q
- div(u1,u2)*q - div(v1,v2)*p)
+ int2d(Th) (-alpha*convect([up1,up2],-dt,up1)*v1
-alpha*convect([up1,up2],-dt,up2)*v2)
+ on(1,2,4,u1=0,u2=0) + on(3,u1=1,u2=0);

problem NR([du1,du2,dp],[v1,v2,q],init=nri) =
int2d(Th)(nu*(Grad(du1,du2)’*Grad(v1,v2))
+ UgradV(du1,du2,u1,u2)’*[v1,v2]
+ UgradV(u1,u2,du1,du2)’*[v1,v2]
- 1e-8*dp*q
- div(du1,du2)*q - div(v1,v2)*dp)
- int2d(Th)(nu*(Grad(u1,u2)’*Grad(v1,v2))
+ UgradV(u1,u2,u1,u2)’*[v1,v2]
- div(u1,u2)*q - div(v1,v2)*p)
+ on(1,2,3,4,du1=0,du2=0);

43
CavFunc.idp

func int Cavity(int method, int n, int meshtype, real re, real delt,
real tol, int IVPminsteps, int IVPmaxsteps, bool toload,int ln,
int ltype, real lre, real ldt, bool tosave, bool intplot, bool ep)

/* Method 1 is IVP, Method 2 is Newton, Method 3 is combined */


{
/* DECLARE MESH */
mesh Th;

/* INITIALISE MESH */
Th = readmesh("mesh\\"+meshtype+"-"+n+"-mesh.msh");
if (intplot) plot(Th,cmm="Mesh "+meshtype+", n="+n);

/* DECLARE FUNCTION SPACES ON MESH */


fespace Xh(Th,P2);
fespace Mh(Th,P1);

/* DECLARE FUNCTIONS */
Xh u1, u2, psi, w; // velocity, streamfunction, vorticity
Xh v1, v2, phi; // test-functions for Finite Elements
Xh psip, psid; // previous value psi_prev, psi_diff
Mh p, q; // pressure and pressure test-function
Xh up1, up2; // previous values, for convect
Xh du1, du2; // du for Newton methods
Mh dp; // dp for Newton methods
Xh psipp; // store a Newton-converged psi
Mh pp; // store p, in case Newton blows up

/* CONSTANT DECLARATION */
real dt = delt; // gets changed in method 3
real nu = 1/re;
real alpha;
int sfi=0, nsi=0, nri=0; // init variables
int jj = 0; // related to init for method 3
real qdt = delt; // for loading file purposes
if (method==2) qdt = 0; // Newton files have dt = 0 (hack)
if (method==3) qdt = -1; // Unified files have dt = -1 (hack)

/* PROBLEM DEFINITIONS */
include "problems.idp"

/* INITIALISE FUNCTIONS (VELOCITY, PRESSURE) */


if (toload) {
include "load.idp" // load stored velocity/pressure
} else stokes; // otherwise start from Stokes solution
createsf; sfi=1; // create streamfunction
if (intplot) plot(psi,cmm="Re="+re+" start");

/* OPEN OUTPUT FILE FOR WRITING */


ofstream ofile("output\\output.txt",append);
ofile << "method="<<method<<", mesh="<<meshtype<<
", n="<<n<<", Re="<<re<<", dt="<<dt<<endl;

44
if (method==1) {
alpha = 1/dt;
/* MAIN TIME-STEP LOOP */
for(nsi=0;nsi<IVPmaxsteps;nsi++) {
up1 = u1; up2 = u2;
navierst;

// every IVPminsteps, check for convergence


if(!(nsi%IVPminsteps) && nsi) {
psip = psi;
createsf;
if (intplot) plot(psi,cmm="Re="+re+", "+nsi+" steps");
psid = psi-psip;
ofile<<nsi<<" "<<psid[].linfty<<" "<<psi[].linfty<<endl;
if (psid[].linfty < tol*IVPminsteps) break;

// in addition, every 5th time, save


if(tosave && !(nsi%(IVPminsteps*5)) && nsi) {
w=dy(u1)-dx(u2);
include "save.idp"
}
}
};
if (ep) plot(psi,cmm="Re="+re+",dt="+qdt);
if(tosave) {
w=dy(u1)-dx(u2);
include "save.idp"
}
}

if (method==2) {
for(nri=0;nri<30;nri++) {
NR;
u1[] -= du1[]; u2[] -= du2[]; p[] -= dp[];
psip = psi;
createsf;
if (intplot) plot(psi,cmm="Re="+re+", "+nri+" steps");
psid = psi-psip;
ofile<<nri<<" "<<psid[].linfty<<" "<<psi[].linfty<<endl;
if (psid[].linfty<tol) break;
if (nri>3 && psid[].linfty>1) break; // Newton failed
}
if (psid[].linfty<tol) {
if (ep) plot(psi,cmm="Re="+re);
if(tosave) {
w=dy(u1)-dx(u2);
include "save.idp"
}
} else if (ep) plot(psi,cmm="Re="+re);
}

if (method==3) {
int steps = IVPminsteps;
psipp = psi;
while (1) {

45
alpha = 1/dt;
for(nsi=0;nsi<steps;nsi++) {
up1 = u1; up2 = u2;
navierst;
}
createsf;

up1 = u1; up2 = u2; pp = p; // in case Newton blows up


for(nri=jj;nri<30;nri++) {
NR;
u1[] -= du1[]; u2[] -= du2[]; p[] -= dp[];
psip = psi;
createsf;
psid = psi-psip;
if (psid[].linfty<tol) break;
if (n>3 && psid[].linfty>1.) break; // blowup
}
jj=1; // only init the first time
ofile<<"Newt "<<nri<<" "<<psi[].linfty<<endl;

if (psid[].linfty>tol) { // Newton didn’t converge


if (ep) plot(psi,cmm="Re="+re+" failed, dt="+dt);
u1=up1; u2=up2; p=pp; // load solution
steps = min(IVPmaxsteps,(steps*3)/2);
dt = dt*0.75; // do more steps at smaller dt
} else {
if (intplot) plot(psi,cmm="Re="+re+" worked");
steps = (steps*3)/4;
dt = dt*0.75;
psid = psi - psipp;
// last two Newtons have given the same answer - stop
if (psid[].linfty < 2*tol) break;
psipp = psi;
}
}
if (ep) plot(psi,cmm="Re="+re);
if(tosave) {
w=dy(u1)-dx(u2);
include "save.idp"
}
}
ofile << endl; // insert a line break
return 0;
}

46
CavWrap.edp

int method = 3; // 1 = IVP, 2 = Newton, 3 = Unified

int meshtype = 3; // 1 = uniform mesh, 2 and 3 = nonuniform


int n = 128; // points on mesh edge
real re = 40000; // Reynolds number
real dt = 0.001; // timestep

int IVPminsteps = 20; // min IVP steps to take


int IVPmaxsteps = 10000; // max IVP steps to take

real tol = 1e-9; // psi tolerance

bool toload = true; // whether to load a previous solution


bool tosave = true; // whether to save this solution

int lmeshtype = 3; // mesh type of solution to load


int ln = 128; // points on mesh edge of solution to load
real lre = 1000; // Re of solution to load
real ldt = -1; // dt of solution to load (0 = Newton, -1 = Unif)

bool intplot = false; // whether to do intermediate plots


bool endplot = false; // whether to plot the final streamlines

include "CavFunc.idp"

// Feel free to wrap the following in a loop

Cavity(method, n, meshtype, re, dt, tol, IVPminsteps, IVPmaxsteps,


toload, ln, lmeshtype, lre, ldt, tosave, intplot, endplot);

plot.edp

/* Loads mesh and streamfunction for plotting */

mesh Th = readmesh("mesh\\3-192-mesh.msh");
fespace Xh(Th,P2);
Xh psi;

real[int] viso(100);

// array of isovalues
viso = [-3e-2,-2e-2,-1e-2, 0
1e-2, 2e-2, 3e-2, 4e-2,
5e-2, 6e-2, 7e-2, 8e-2,
9e-2, 1e-1, 1.1e-1, 1.2e-1];

plot(Th,grey=1);
{ifstream f("cache\\3-192-1000--1-sfunc.txt");f >> psi[];
plot(psi, viso=viso(0:viso.n-1));}

47
B References

References
[1] Kawaguti, M. 1961 Numerical Solution of the Navier-Stokes Equations for the Flow in a Two-
Dimensional Cavity. Journal of the Physical Society of Japan. 16, 2307-2315.
[2] Burggraf, O. R. 1966 Analytical and numerical studies of the structure of steady separated flows.
Journal of Fluid Mechanics. 24, 113-151.
[3] Benjamin, A. S. & Denny, V. E. 1979 On the Convergence of Numerical Solutions for 2-D Flows
in a Cavity at Large Re. Journal of Computational Physics. 33, 340-358.
[4] 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, 387-411.
[5] Schreiber, R. & Keller, H. B. 1983 Driven Cavity Flows by Efficient Numerical Techniques. Journal
of Computational Physics. 49, 310-333.
[6] Schreiber, R. & Keller, H. B. 1983 Spurious Solutions in Driven Cavity Calculations. Journal of
Computational Physics. 49, 165-172.
[7] Barragy, E. & Carey, G. F. 1997 Stream Function-Vorticity Driven Cavity Solution using p Finite
Elements. Computers & Fluids. 26, 453-468.
[8] Erturk, E., Corke, T. C. & Gokcol, C. 2005 Numerical solutions of 2-D steady incompressible driven
cavity flow at high Reynolds numbers. International Journal for Numerical Methods in Fluids. 48,
747-774.
[9] Erturk, E. 2009 Discussions on driven cavity flow. International Journal for Numerical Methods in
Fluids. 60, 275-294.
[10] Shankar, P. N. & Deshpande, M. D. 2000 Fluid Mechanics in the Driven Cavity. Annual Review
of Fluid Mechanics. 32, 93-136.
[11] Hecht, F. 2012 FreeFem++ Manual. http://www.freefem.org/ff++/ftp/freefem++doc.pdf.
[12] Iserles, A. 2009 A First Course in the Numerical Analysis of Differential Equations (2nd ed.).
Cambridge, UK: Cambridge University Press.
[13] Chorin, A. J. 1967 A Numerical Method for Solving Incompressible Viscous Flow Problems.
Journal of Computational Physics. 2, 12-26.
[14] Chorin, A. J. 1968 Numerical Solution of the Navier-Stokes Equations. Mathematics of Compu-
tation. 22, 745-762.
[15] Srinivasan, R. 1995 Accurate solutions for steady plane flow in the driven cavity. Zeitschrift fur
Angewandte Mathematik und Physik. 46, 524-545.
[16] Meleshko, V. V. 1996 Steady Stokes flow in a rectangular cavity. Proceedings of the Royal Society
A. 452, 1999-2022.
[17] Bogatyrev, V. Y. A. & Gorin, A. V. 1978 End effects in rectangular cavities. Fluid Mechanics-
Soviet Research. 7, 101-106.
[18] Koseff, J. R. & Street, R. L. 1984 On the endwall effects in a lid-driven cavity flow. Journal of
Fluids Engineering. 106, 385-389.
[19] Nallasamy, M. & Krishna Prasad, K. 1977 On cavity flow at high Reynolds numbers. Journal of
Fluid Mechanics. 79, 391-414.

48

S-ar putea să vă placă și