Chapter 3
Learning by Examples
This chapter is for those, like us, who don’t like manuals. A number of
simple examples cover a good deal of the capacity of FreeFem++ and are self-explanatory. For the
modelling part this chapter continues at Chapter 9 where some PDEes of physics, engineering and finance
are studied in greater depth.
3.1 Membranes
Summary
Here we shall learn how to solve a Dirichlet and/or mixed Dirichlet Neumann problem for the Laplace
operator with application to the equilibrium of a membrane under load. We shall also check the accuracy
of the method and interface with other graphics packages.
An elastic membrane Ω is attached to a planar rigid support Γ, and a force f(x)dx is exerted on each
surface element dx = dx1dx2. The vertical membrane displacement, φ(x), is obtained by solving Laplace’s
equation:
As
the membrane is fixed to its planar support, one has:
If the
support wasn’t planar but at an elevation z(x1,x2) then the boundary conditions would be of
non-homogeneous Dirichlet type.
If a part Γ2 of the membrane border Γ is not fixed to the support but is left hanging, then due to the
membrane’s rigidity the angle with the normal vector n is zero; thus the boundary conditions
are
where Γ1 = Γ -Γ2; recall that
= ∇φ ⋅n. Let us recall also that the Laplace operator Δ is defined
by:
With
such ”mixed boundary conditions” the problem has a unique solution (see (1987), Dautray-Lions (1988),
Strang (1986) and Raviart-Thomas (1983)); the easiest proof is to notice that φ is the state of least energy,
i.e.
and
where V is the subspace of the Sobolev space H1(Ω) of functions which have zero trace on Γ1. Recall that
(x
d, d = 2 here)
Calculus of variation shows that the minimum must satisfy, what is known as the weak form of the PDE or
its variational formulation (also known here as the theorem of virtual work)
Next
an integration by parts (Green’s formula) will show that this is equivalent to the PDE when second
derivatives exist.
WARNING
Unlike freefem+ which had both weak and strong forms, FreeFem++ implements only weak
formulations. It is not possible to go further in using this software if you don’t know the weak form (i.e.
variational formulation) of your problem: either you read a book, or ask help form a colleague or drop the
matter. Now if you want to solve a system of PDE like A(u,v) = 0, B(u,v) = 0 don’t close this manual,
because in weak form it is
Example
Let an ellipse have the length of the semimajor axis a = 2, and unitary the semiminor axis Let the surface
force be f = 1. Programming this case with FreeFem++ gives:
Example 3.1 (membrane.edp)
// file
membrane.edp
real theta=4.⋆pi/3.;
real a=2.,b=1.; // the length of the semimajor axis and semiminor
axis
func z=x;
border Gamma1(t=0,theta) { x = a ⋆ cos(t); y = b⋆sin(t); }
border Gamma2(t=theta,2⋆pi) { x = a ⋆ cos(t); y = b⋆sin(t); }
mesh Th=buildmesh(Gamma1(100)+Gamma2(50));
fespace Vh(Th,P2); // P2 conforming triangular
FEM
Vh phi,w, f=1;
solve Laplace(phi,w)=int2d(Th)(dx(phi)⋆dx(w) + dy(phi)⋆dy(w))
- int2d(Th)(f⋆w) + on(Gamma1,phi=z);
plot(phi,wait=true, ps="membrane.eps"); // Plot
phi
plot(Th,wait=true, ps="membraneTh.eps"); // Plot
Th
savemesh(Th,"Th.msh");
A triangulation is built by the keyword buildmesh. This keyword calls a triangulation subroutine based
on the Delaunay test, which first triangulates with only the boundary points, then adds internal points by
subdividing the edges. How fine the triangulation becomes is controlled by the size of the closest
boundary edges.
The PDE is then discretized using the triangular second order finite element method on the triangulation;
as was briefly indicated in the previous chapter, a linear system is derived from the discrete formulation
whose size is the number of vertices plus the number of mid-edges in the triangulation. The system is
solved by a multi-frontal Gauss LU factorization implemented in the package UMFPACK. The keyword
plot will display both Th and φ (remove Th if φ only is desired) and the qualifier fill=true
replaces the default option (colored level lines) by a full color display. Results are on figure
3.1.
plot(phi,wait=true,fill=true); // Plot phi with full color
display
Next we would like to check the results!
One simple way is to adjust the parameters so as to know the solutions. For instance on the unit circle a=1
, φe = sin(x2 + y2 -1) solves the problem when
except that on Γ2 ∂nφ = 2 instead of zero. So we will consider a non-homogeneous Neumann condition
and solve
We
will do that with two triangulations, compute the L2 error:
and print the error in both cases as well as the log of their ratio an indication of the rate of convergence.
Example 3.2 (membranerror.edp)
// file
membranerror.edp
verbosity =0; // to remove all default
output
real theta=4.⋆pi/3.;
real a=1.,b=1.; // the length of the semimajor axis and semiminor
axis
border Gamma1(t=0,theta) { x = a ⋆ cos(t); y = b⋆sin(t); }
border Gamma2(t=theta,2⋆pi) { x = a ⋆ cos(t); y = b⋆sin(t); }
func f=-4⋆(cos(x^2+y^2-1) -(x^2+y^2)⋆sin(x^2+y^2-1));
func phiexact=sin(x^2+y^2-1);
real[int] L2error(2); // an array two
values
for(int n=0;n<2;n++)
{
mesh Th=buildmesh(Gamma1(20⋆(n+1))+Gamma2(10⋆(n+1)));
fespace Vh(Th,P2);
Vh phi,w;
solve laplace(phi,w)=int2d(Th)(dx(phi)⋆dx(w) + dy(phi)⋆dy(w))
- int2d(Th)(f⋆w) - int1d(Th,Gamma2)(2⋆w)+ on(Gamma1,phi=0);
plot(Th,phi,wait=true,ps="membrane.eps"); // Plot Th and
phi
L2error[n]= sqrt(int2d(Th)((phi-phiexact)^2));
}
for(int n=0;n<2;n++)
cout << " L2error " << n << " = "<< L2error[n] <<endl;
cout <<" convergence rate = "<< log(L2error[0]/L2error[1])/log(2.) <<endl;
the output is
L2error 0 = 0.00462991
L2error 1 = 0.00117128
convergence rate = 1.9829
times: compile 0.02s, execution 6.94s
We find a rate of 1.93591, which is not close enough to the 3 predicted by the theory. The
Geometry is always a polygon so we lose one order due to the geometry approximation in
O(h2)
Now if you are not satisfied with the .eps plot generated by FreeFem++ and you want
to use other graphic facilities, then you must store the solution in a file very much like in
C++. It will be useless if you don’t save the triangulation as well, consequently you must do
{
ofstream ff("phi.txt");
ff << phi[];
}
savemesh(Th,"Th.msh");
For the triangulation the name is important: it is the extension that determines the format.
Still that may not take you where you want. Here is an interface with gnuplot to produce the right part of
figure 3.2.
// to build a gnuplot data
file
{ ofstream ff("graph.txt");
for (int i=0;i<Th.nt;i++)
{ for (int j=0; j <3; j++)
ff<<Th[i][j].x << " "<< Th[i][j].y<< " "<<phi[][Vh(i,j)]<<endl;
ff<<Th[i][0].x << " "<< Th[i][0].y<< " "<<phi[][Vh(i,0)]<<"\n\n\n"
}
}
We use the finite element numbering, where Wh(i,j) is the global index of jTh degrees of freedom of
triangle number i.
Then open gnuplot and do
set palette rgbformulae 30,31,32
splot "graph.txt" w l pal
This works with P2 and P1, but not with P1nc because the 3 first degrees of freedom of P2 or P2 are
on vertices and not with P1nc.
3.2 Heat Exchanger
Summary
Here we shall learn more about geometry input and triangulation files, as well as read and write
operations.
The problem
Let {Ci}1,2, be 2 thermal conductors within an enclosure C0. The first one is held at a constant temperature
u1 the other one has a given thermal conductivity κ2 5 times larger than the one of C0. We assume that the
border of enclosure C0 is held at temperature 20∘C and that we have waited long enough for thermal
equilibrium.
In order to know u(x) at any point x of the domain Ω, we must solve
where Ω is the interior of C0 minus the conductors C1 and Γ is the boundary of Ω, that is
C0 ∪C1 Here g is any function of x equal to ui on Ci. The second equation is a reduced form for:
The
variational formulation for this problem is in the subspace H01(Ω) ⊂ H1(Ω) of functions which have zero
traces on Γ.
Let us assume that C0 is a circle of radius 5 centered at the origin, Ci are rectangles, C1 being at the
constant temperature u1 = 60∘C.
Example 3.3 (heatex.edp)
// file
heatex.edp
int C1=99, C2=98; // could be anything such that ⇔ 0 and
C1 ⇔ C2
border C0(t=0,2⋆pi){x=5⋆cos(t); y=5⋆sin(t);}
border C11(t=0,1){ x=1+t; y=3; label=C1;}
border C12(t=0,1){ x=2; y=3-6⋆t; label=C1;}
border C13(t=0,1){ x=2-t; y=-3; label=C1;}
border C14(t=0,1){ x=1; y=-3+6⋆t; label=C1;}
border C21(t=0,1){ x=-2+t; y=3; label=C2;}
border C22(t=0,1){ x=-1; y=3-6⋆t; label=C2;}
border C23(t=0,1){ x=-1-t; y=-3; label=C2;}
border C24(t=0,1){ x=-2; y=-3+6⋆t; label=C2;}
plot( C0(50) // to see the border of the
domain
+ C11(5)+C12(20)+C13(5)+C14(20)
+ C21(-5)+C22(-20)+C23(-5)+C24(-20),
wait=true, ps="heatexb.eps");
mesh Th=buildmesh( C0(50)
+ C11(5)+C12(20)+C13(5)+C14(20)
+ C21(-5)+C22(-20)+C23(-5)+C24(-20));
plot(Th,wait=1);
fespace Vh(Th,P1); Vh u,v;
Vh kappa=1+2⋆(x<-1)⋆(x>-2)⋆(y<3)⋆(y>-3);
solve a(u,v)= int2d(Th)(kappa⋆(dx(u)⋆dx(v)+dy(u)⋆dy(v)))
+on(C0,u=20)+on(C1,u=60);
plot(u,wait=true, value=true, fill=true, ps="heatex.eps");
Note the following:
- C0 is oriented counterclockwise by t, while C1 is oriented clockwise and C2 is oriented
counterclockwise. This is why C1 is viewed as a hole by buildmesh.
- C1 and C2 are built by joining pieces of straight lines. To group them in the same logical unit
to input the boundary conditions in a readable way we assigned a label on the boundaries.
As said earlier, borders have an internal number corresponding to their order in the program
(check it by adding a cout<<C22; above). This is essential to understand how a mesh can
be output to a file and re-read (see below).
- As usual the mesh density is controlled by the number of vertices assigned to each boundary.
It is not possible to change the (uniform) distribution of vertices but a piece of boundary can
always be cut in two or more parts, for instance C12 could be replaced by C121+C122:
// border C12(t=0,1) x=2; y=3-6⋆t;
label=C1;
border C121(t=0,0.7){ x=2; y=3-6⋆t; label=C1;}
border C122(t=0.7,1){ x=2; y=3-6⋆t; label=C1;}
... buildmesh(.../⋆ C12(20) ⋆/ + C121(12)+C122(8)+...);
Exercise
Use the symmetry of the problem with respect to the axes; triangulate only one half of the domain,
and set Dirichlet conditions on the vertical axis, and Neumann conditions on the horizontal
axis.
Writing and reading triangulation files
Suppose that at the end of the previous program we added the line
savemesh(Th,"condensor.msh");
and then later on we write a similar program but we wish to read the mesh from that file. Then this is how
the condenser should be computed:
mesh Sh=readmesh("condensor.msh");
fespace Wh(Sh,P1); Wh us,vs;
solve b(us,vs)= int2d(Sh)(dx(us)⋆dx(vs)+dy(us)⋆dy(vs))
+on(1,us=0)+on(99,us=1)+on(98,us=-1);
plot(us);
Note that the names of the boundaries are lost but either their internal number (in the case of C0) or their
label number (for C1 and C2) are kept.
3.3 Acoustics
Summary
Here we go to grip with ill posed problems and eigenvalue problems
Pressure variations in air at rest are governed by the wave equation:
When the solution wave is monochromatic (and that depend on the boundary and initial conditions), u is
of the form u(x,t) = Re(v(x)eikt) where v is a solution of Helmholtz’s equation:
where g is the source. Note the “+” sign in front of the Laplace operator and that k > 0 is
real. This sign may make the problem ill posed for some values of
, a phenomenon called
“resonance”.
At resonance there are non-zero solutions even when g = 0. So the following program may or may not
work:
Example 3.4 (sound.edp)
// file
sound.edp
real kc2=1;
func g=y⋆(1-y);
border a0(t=0,1) { x= 5; y= 1+2⋆t ;}
border a1(t=0,1) { x=5-2⋆t; y= 3 ;}
border a2(t=0,1) { x= 3-2⋆t; y=3-2⋆t ;}
border a3(t=0,1) { x= 1-t; y= 1 ;}
border a4(t=0,1) { x= 0; y= 1-t ;}
border a5(t=0,1) { x= t; y= 0 ;}
border a6(t=0,1) { x= 1+4⋆t; y= t ;}
mesh Th=buildmesh( a0(20) + a1(20) + a2(20)
+ a3(20) + a4(20) + a5(20) + a6(20));
fespace Vh(Th,P1);
Vh u,v;
solve sound(u,v)=int2d(Th)(u⋆v ⋆ kc2 - dx(u)⋆dx(v) - dy(u)⋆dy(v))
- int1d(Th,a4)(g⋆v);
plot(u, wait=1, ps="sound.eps");
Results are on Figure 3.3. But when kc2 is an eigenvalue of the problem, then the solution is not unique: if
ue ↙ 0 is an eigen state, then for any given solution u + ue is another a solution. To find all the ue one can
do the following
real sigma = 20; // value of the
shift
// OP = A - sigma B ; // the shifted
matrix
varf op(u1,u2)= int2d(Th)( dx(u1)⋆dx(u2) + dy(u1)⋆dy(u2) - sigma⋆ u1⋆u2 );
varf b([u1],[u2]) = int2d(Th)( u1⋆u2 ) ; // no Boundary condition see note
9.1
matrix OP= op(Vh,Vh,solver=Crout,factorize=1);
matrix B= b(Vh,Vh,solver=CG,eps=1e-20);
int nev=2; // number of requested eigenvalues near
sigma
real[int] ev(nev); // to store the nev
eigenvalue
Vh[int] eV(nev); // to store the nev
eigenvector
int k=EigenValue(OP,B,sym=true,sigma=sigma,value=ev,vector=eV,
tol=1e-10,maxit=0,ncv=0);
cout<<ev(0)<<" 2 eigen values "<<ev(1)<<endl;
v=eV[0];
plot(v,wait=1,ps="eigen.eps");
3.4 Thermal Conduction
Summary
Here we shall learn how to deal with a time dependent parabolic problem. We shall also show how to
treat an axisymmetric problem and show also how to deal with a nonlinear problem.
How air cools a plate
We seek the temperature distribution in a plate (0,Lx) ×(0,Ly) ×(0,Lz) of rectangular cross section
Ω = (0,6) ×(0,1); the plate is surrounded by air at temperature ue and initially at temperature
u = u0 +
u1. In the plane perpendicular to the plate at z = Lz∕2, the temperature varies little with the
coordinate z; as a first approximation the problem is 2D.
We must solve the temperature equation in Ω in a time interval (0,T).
Here the diffusion κ will take two values, one below the middle horizontal line and ten times less above,
so as to simulate a thermostat. The term α(u -ue) accounts for the loss of temperature by
convection in air. Mathematically this boundary condition is of Fourier (or Robin, or mixed) type.
The variational formulation is in L2(0,T; H1(Ω)); in loose terms and after applying an implicit Euler finite
difference approximation in time; we shall seek un(x,y) satisfying for all w
H1(Ω):
func u0 =10+90⋆x/6;
func k = 1.8⋆(y<0.5)+0.2;
real ue = 25, alpha=0.25, T=5, dt=0.1 ;
mesh Th=square(30,5,[6⋆x,y]);
fespace Vh(Th,P1);
Vh u=u0,v,uold;
problem thermic(u,v)= int2d(Th)(u⋆v/dt + k⋆(dx(u) ⋆ dx(v) + dy(u) ⋆ dy(v)))
+ int1d(Th,1,3)(alpha⋆u⋆v)
- int1d(Th,1,3)(alpha⋆ue⋆v)
- int2d(Th)(uold⋆v/dt) + on(2,4,u=u0);
ofstream ff("thermic.dat");
for(real t=0;t<T;t+=dt){
uold=u; // uold ≡ un-1 = un ≡u
thermic; // here solve the thermic problem
ff<<u(3,0.5)<<endl;
plot(u);
}
Notice that we must separate by hand the bilinear part from the linear one.
Notice also that the way we store the temperature at point (3,0.5) for all times in file thermic.dat.
Should a one dimensional plot be required, the same procedure can be used. For instance to print
x
(x,0.9) one would do
for(int i=0;i<20;i++) cout<<dy(u)(6.0⋆i/20.0,0.9)<<endl;
Results are shown on Figure 3.4.
3.4.1 Axisymmetry: 3D Rod with circular section
Let us now deal with a cylindrical rod instead of a flat plate. For simplicity we take κ = 1. In cylindrical
coordinates, the Laplace operator becomes (r is the distance to the axis, z is the distance along the axis, θ
polar angle in a fixed plane perpendicular to the axis):
Symmetry implies that we loose the dependence with respect to θ; so the domain Ω is again a rectangle
]0,R[×]0,|[ . We take the convention of numbering of the edges as in square() (1 for the bottom
horizontal ...); the problem is now:
Note that the PDE has been multiplied by r.
After discretization in time with an implicit scheme, with time steps dt, in the FreeFem++ syntax r
becomes x and z becomes y and the problem is:
problem thermaxi(u,v)=int2d(Th)((u⋆v/dt + dx(u)⋆dx(v) + dy(u)⋆dy(v))⋆x)
+ int1d(Th,3)(alpha⋆x⋆u⋆v) - int1d(Th,3)(alpha⋆x⋆ue⋆v)
- int2d(Th)(uold⋆v⋆x/dt) + on(2,4,u=u0);
Notice that the bilinear form degenerates at x = 0. Still one can prove existence and uniqueness for u and
because of this degeneracy no boundary conditions need to be imposed on Γ1.
3.4.2 A Nonlinear Problem : Radiation
Heat loss through radiation is a loss proportional to the absolute temperature to the fourth
power (Stefan’s Law). This adds to the loss by convection and gives the following boundary
condition:
The problem is nonlinear, and must be solved iteratively. If m denotes the iteration index, a
semi-linearization of the radiation condition gives
because we have the identity a4 -b4 = (a -b)(a + b)(a2 + b2). The iterative process will work with
v = u -ue.
...
fespace Vh(Th,P1); // finite element
space
real rad=1e-8, uek=ue+273; // def of the physical
constants
Vh vold,w,v=u0-ue,b;
problem thermradia(v,w)
= int2d(Th)(v⋆w/dt + k⋆(dx(v) ⋆ dx(w) + dy(v) ⋆ dy(w)))
+ int1d(Th,1,3)(b⋆v⋆w)
- int2d(Th)(vold⋆w/dt) + on(2,4,v=u0-ue);
for(real t=0;t<T;t+=dt){
vold=v;
for(int m=0;m<5;m++){
b= alpha + rad ⋆ (v + 2⋆uek) ⋆ ((v+uek)^2 + uek^2);
thermradia;
}
}
vold=v+ue; plot(vold);
3.5 Irrotational Fan Blade Flow and Thermal effects
Summary
Here we will learn how to deal with a multi-physics system of PDEs on a Complex geometry, with multiple
meshes within one problem. We also learn how to manipulate the region indicator and see how smooth is
the projection operator from one mesh to another.
Incompressible flow
Without viscosity and vorticity incompressible flows have a velocity given by:
This
equation expresses both incompressibility (∇⋅u = 0) and absence of vortex (∇×u = 0).
As the fluid slips along the walls, normal velocity is zero, which means that ψ satisfies:
One
can also prescribe the normal velocity at an artificial boundary, and this translates into non constant
Dirichlet data for ψ.
Airfoil
Let us consider a wing profile S in a uniform flow. Infinity will be represented by a large circle C
where the flow is assumed to be of uniform velocity; one way to model this problem is to write
where ∂Ω = C ∪S
The NACA0012 Airfoil
An equation for the upper surface of a NACA0012 (this is a classical wing profile in aerodynamics)
is:
Example 3.5 (potential.edp)
// file
potential.edp
real S=99;
border C(t=0,2⋆pi) { x=5⋆cos(t); y=5⋆sin(t);}
border Splus(t=0,1){ x = t; y = 0.17735⋆sqrt(t)-0.075597⋆t
- 0.212836⋆(t^2)+0.17363⋆(t^3)-0.06254⋆(t^4); label=S;}
border Sminus(t=1,0){ x =t; y= -(0.17735⋆sqrt(t)-0.075597⋆t
-0.212836⋆(t^2)+0.17363⋆(t^3)-0.06254⋆(t^4)); label=S;}
mesh Th= buildmesh(C(50)+Splus(70)+Sminus(70));
fespace Vh(Th,P2); Vh psi,w;
solve potential(psi,w)=int2d(Th)(dx(psi)⋆dx(w)+dy(psi)⋆dy(w))+
on(C,psi = y) + on(S,psi=0);
plot(psi,wait=1);
A zoom of the streamlines are shown on Figure 3.5.
3.5.1 Heat Convection around the airfoil
Now let us assume that the airfoil is hot and that air is there to cool it. Much like in the previous section
the heat equation for the temperature u is
But
now the domain is outside AND inside S and κ takes a different value in air and in steel. Furthermore
there is convection of heat by the flow, hence the term u⋅∇v above. Consider the following, to be plugged
at the end of the previous program:
...
border D(t=0,2){x=1+t;y=0;} // added to have a fine mesh at
trail
mesh Sh = buildmesh(C(25)+Splus(-90)+Sminus(-90)+D(200));
fespace Wh(Sh,P1); Wh v,vv;
int steel=Sh(0.5,0).region, air=Sh(-1,0).region;
fespace W0(Sh,P0);
W0 k=0.01⋆(region==air)+0.1⋆(region==steel);
W0 u1=dy(psi)⋆(region==air), u2=-dx(psi)⋆(region==air);
Wh vold = 120⋆(region==steel);
real dt=0.05, nbT=50;
int i;
problem thermic(v,vv,init=i,solver=LU)= int2d(Sh)(v⋆vv/dt
+ k⋆(dx(v) ⋆ dx(vv) + dy(v) ⋆ dy(vv))
+ 10⋆(u1⋆dx(v)+u2⋆dy(v))⋆vv)- int2d(Sh)(vold⋆vv/dt);
for(i=0;i<nbT;i++){
v=vold; thermic;
plot(v);
}
Notice here
- how steel and air are identified by the mesh parameter region which is defined when
buildmesh is called and takes an integer value corresponding to each connected component
of Ω;
- how the convection terms are added without upwinding. Upwinding is necessary when the
Pecley number |u|L∕κ is large (here is a typical length scale), The factor 10 in front of the
convection terms is a quick way of multiplying the velocity by 10 (else it is too slow to see
something).
- The solver is Gauss’ LU factorization and when init⇔ 0 the LU decomposition is reused
so it is much faster after the first iteration.
3.6 Pure Convection : The Rotating Hill
Summary
Here we will present two methods for upwinding for the simplest convection problem. We will learn about
Characteristics-Galerkin and Discontinuous-Galerkin Finite Element Methods.
Let Ω be the unit disk centered at 0; consider the rotation vector field
Pure
convection by u is
The
exact solution c(xt,t) at time t en point xt is given by
where xt is the particle path in the flow starting at point x at time 0. So xt are solutions of
The ODE are reversible and we want the solution at point x at time t ( not at point xt) the initial point is
x-t, and we have
The
game consists in solving the equation until T = 2π, that is for a full revolution and to compare the final
solution with the initial one; they should be equal.
Solution by a Characteristics-Galerkin Method
In FreeFem++ there is an operator called convect([u1,u2],dt,c) which compute c ∘X with X
is the convect field defined by X(x) = xdt and where xτ is particule path in the steady state
velocity field u = [u1,u2] starting at point x at time τ = 0, so xτ is solution of the following
ODE:
When u is piecewise constant; this is possible because xτ is then a polygonal curve which can be
computed exactly and the solution exists always when u is divergence free; convect returns c(xdf ) = C ∘X.
Example 3.6 (convects.edp)
// file
convects.edp
border C(t=0, 2⋆pi) { x=cos(t); y=sin(t); };
mesh Th = buildmesh(C(100));
fespace Uh(Th,P1);
Uh cold, c = exp(-10⋆((x-0.3)^2 +(y-0.3)^2));
real dt = 0.17,t=0;
Uh u1 = y, u2 = -x;
for (int m=0; m<2⋆pi/dt ; m++) {
t += dt; cold=c;
c=convect([u1,u2],-dt,cold);
plot(c,cmm=" t="+t + ", min=" + c[].min + ", max=" + c[].max);
}
The method is very powerful but has two limitations: a/ it is not conservative, b/ it may diverge in rare
cases when |u| is too small due to quadrature error.
Solution by Discontinuous-Galerkin FEM
Discontinuous Galerkin methods take advantage of the discontinuities of c at the edges to build
upwinding. There are may formulations possible. We shall implement here the so-called dual-P1DC
formulation (see Ern[11]):
where E is the set of inner edges and EΓ- is the set of boundary edges where u⋅n < 0 (in our case there is
no such edges). Finally [c] is the jump of c across an edge with the convention that c+ refers to the value
on the right of the oriented edge.
Example 3.7 (convects_end.edp)
// file
convects.edp
...
fespace Vh(Th,P1dc);
Vh w, ccold, v1 = y, v2 = -x, cc = exp(-10⋆((x-0.3)^2 +(y-0.3)^2));
real u, al=0.5; dt = 0.05;
macro n(N.x⋆v1+N.y⋆v2) // problem Adual(cc,w)
=
int2d(Th)((cc/dt+(v1⋆dx(cc)+v2⋆dy(cc)))⋆w)
+ intalledges(Th)((1-nTonEdge)⋆w⋆(al⋆abs(n)-n/2)⋆jump(cc))
// - int1d(Th,C)((n<0)⋆abs(n)⋆cc⋆w) // unused because cc=0 on
∂Ω-
- int2d(Th)(ccold⋆w/dt);
for ( t=0; t< 2⋆pi ; t+=dt)
{
ccold=cc; Adual;
plot(cc,fill=1,cmm="t="+t + ", min=" + cc[].min + ", max=" + cc[].max);
};
real [int] viso=[-0.2,-0.1,0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1,1.1];
plot(c,wait=1,fill=1,ps="convectCG.eps",viso=viso);
plot(c,wait=1,fill=1,ps="convectDG.eps",viso=viso);
Notice the new keywords, intalledges to integrate on all edges of all triangles
 | (3.5) |
(so all internal edges are see two times ), nTonEdge which is one if the triangle has a boundary edge and
zero otherwise, jump to implement [c]. Results of both methods are shown on Figure 3.6 with identical
levels for the level line; this is done with the plot-modifier viso.
Notice also the macro where the parameter u is not used (but the syntax needs one) and which ends with a
//; it simply replaces the name n by (N.x⋆v1+N.y⋆v2). As easily guessed N.x,N.y is the normal to
the edge.
Now if you think that DG is too slow try this
// the same DG very much
faster
varf aadual(cc,w) = int2d(Th)((cc/dt+(v1⋆dx(cc)+v2⋆dy(cc)))⋆w)
+ intalledges(Th)((1-nTonEdge)⋆w⋆(al⋆abs(n)-n/2)⋆jump(cc));
varf bbdual(ccold,w) = - int2d(Th)(ccold⋆w/dt);
matrix AA= aadual(Vh,Vh);
matrix BB = bbdual(Vh,Vh);
set (AA,init=t,solver=sparsesolver);
Vh rhs=0;
for ( t=0; t< 2⋆pi ; t+=dt)
{
ccold=cc;
rhs[] = BB⋆ ccold[];
cc[] = AA^-1⋆rhs[];
plot(cc,fill=0,cmm="t="+t + ", min=" + cc[].min + ", max=" + cc[].max);
};
Notice the new keyword set to specify a solver in this framework; the modifier init is used to tel the solver
that the matrix has not changed (init=true), and the name parameter are the same that in problem
definition (see. 6.7) .
Finite Volume Methods
can also be handled with FreeFem++ but it requires programming. For instance the P0 -P1 Finite
Volume Method of Dervieux et al associates to each P0 function c1 a P0 function c0 with
constant value around each vertex qi equal to c1(qi) on the cell σ
i made by all the medians of all
triangles having qi as vertex. Then upwinding is done by taking left or right values at the
median:
It can be programmed as
load "mat_dervieux"; // external module in C++ must be
loaded
border a(t=0, 2⋆pi){ x = cos(t); y = sin(t); }
mesh th = buildmesh(a(100));
fespace Vh(th,P1);
Vh vh,vold,u1 = y, u2 = -x;
Vh v = exp(-10⋆((x-0.3)^2 +(y-0.3)^2)), vWall=0, rhs =0;
real dt = 0.025;
// qf1pTlump means mass lumping is
used
problem FVM(v,vh) = int2d(th,qft=qf1pTlump)(v⋆vh/dt)
- int2d(th,qft=qf1pTlump)(vold⋆vh/dt)
+ int1d(th,a)(((u1⋆N.x+u2⋆N.y)<0)⋆(u1⋆N.x+u2⋆N.y)⋆vWall⋆vh)
+ rhs[] ;
matrix A;
MatUpWind0(A,th,vold,[u1,u2]);
for ( int t=0; t< 2⋆pi ; t+=dt){
vold=v;
rhs[] = A ⋆ vold[] ; FVM;
plot(v,wait=0);
};
the mass lumping parameter forces a quadrature formula with Gauss points at the vertices so as to make
the mass matrix diagonal; the linear system solved by a conjugate gradient method for instance will then
converge in one or two iterations.
The right hand side rhs is computed by an external C++ function MatUpWind0(...) which is
programmed as
// computes matrix a on a triangle for the Dervieux
FVM
int fvmP1P0(double q[3][2], // the 3 vertices of a triangle
T
double u[2], // convection velocity on
T
double c[3], // the P1 function on
T
double a[3][3], // output
matrix
double where[3] ) // where>0 means we're on the
boundary
{
for(int i=0;i<3;i++) for(int j=0;j<3;j++) a[i][j]=0;
for(int i=0;i<3;i++){
int ip = (i+1)%3, ipp =(ip+1)%3;
double unL =-((q[ip][1]+q[i][1]-2⋆q[ipp][1])⋆u[0]
-(q[ip][0]+q[i][0]-2⋆q[ipp][0])⋆u[1])/6;
if(unL>0) { a[i][i] += unL; a[ip][i]-=unL;}
else{ a[i][ip] += unL; a[ip][ip]-=unL;}
if(where[i]&&where[ip]){ // this is a boundary
edge
unL=((q[ip][1]-q[i][1])⋆u[0] -(q[ip][0]-q[i][0])⋆u[1])/2;
if(unL>0) { a[i][i]+=unL; a[ip][ip]+=unL;}
}
}
return 1;
}
It must be inserted into a larger .cpp file, shown in Appendix A, which is the load module linked to
FreeFem++ .
3.7 A Projection Algorithm for the Navier-Stokes equations
Summary
Fluid flows require good algorithms and good triangultions. We show here an example of a complex
algorithm and or first example of mesh adaptation.
An incompressible viscous fluid satisfies:
A
possible algorithm, proposed by Chorin, is
where uoX(x) = u(x -u(x)δt) since ∂tu+ u⋅∇u is approximated by the method of characteristics, as in the
previous section.
An improvement over Chorin’s algorithm, given by Rannacher, is to compute a correction, q, to the
pressure (the overline denotes the mean over Ω)
and define
where ũ is the (um+1,vm+1) of Chorin’s algorithm.
The backward facing step
The geometry is that of a channel with a backward facing step so that the inflow section is smaller than
the outflow section. This geometry produces a fluid recirculation zone that must be captured
correctly.
This can only be done if the triangulation is sufficiently fine, or well adapted to the flow.
Example 3.8 (NSprojection.edp)
// file
NSprojection.edp
border a0(t=1,0){ x=0; y=t; label=1;}
border a1(t=0,1){ x=2⋆t; y=0; label=2;}
border a2(t=0,1){ x=2; y=-t/2; label=2;}
border a3(t=0,1){ x=2+18⋆t^1.2; y=-0.5; label=2;}
border a4(t=0,1){ x=20; y=-0.5+1.5⋆t; label=3;}
border a5(t=1,0){ x=20⋆t; y=1; label=4;}
int n=1;
mesh Th= buildmesh(a0(3⋆n)+a1(20⋆n)+a2(10⋆n)+a3(150⋆n)+a4(5⋆n)+a5(100⋆n));
plot(Th);
fespace Vh(Th,P1);
real nu = 0.0025, dt = 0.2; //
Reynolds=400
Vh w,u = 4⋆y⋆(1-y)⋆(y>0)⋆(x<2), v =0, p = 0, q=0;
real area= int2d(Th)(1.);
for(int n=0;n<100;n++){
Vh uold = u, vold = v, pold=p;
Vh f=convect([u,v],-dt,uold), g=convect([u,v],-dt,vold);
solve pb4u(u,w,init=n,solver=LU)
=int2d(Th)(u⋆w/dt +nu⋆(dx(u)⋆dx(w)+dy(u)⋆dy(w)))
-int2d(Th)((f/dt-dx(p))⋆w)
+ on(1,u = 4⋆y⋆(1-y)) + on(2,4,u = 0)+ on(3,u=f);
plot(u);
solve pb4v(v,w,init=n,solver=LU)
= int2d(Th)(v⋆w/dt +nu⋆(dx(v)⋆dx(w)+dy(v)⋆dy(w)))
-int2d(Th)((g/dt-dy(p))⋆w)
+on(1,2,3,4,v = 0);
real meandiv = int2d(Th)(dx(u)+dy(v))/area;
solve pb4p(q,w,init=n,solver=LU)= int2d(Th)(dx(q)⋆dx(w)+dy(q)⋆dy(w))
- int2d(Th)((dx(u)+ dy(v)-meandiv)⋆w/dt)+ on(3,q=0);
real meanpq = int2d(Th)(pold - q)/area;
if(n==50){
Th = adaptmesh(Th,u,v,q); plot(Th, wait=true);
}
p = pold-q-meanpq;
u = u + dx(q)⋆dt;
v = v + dy(q)⋆dt;
}
We show in figure 3.7 the numerical results obtained for a Reynolds number of 400 where mesh
adaptation is done after 50 iterations on the first mesh.
3.8 The System of elasticity
Elasticity
Solid objects deform under the action of applied forces: a point in the solid, originally at (x,y,z) will come
to (X,Y,Z) after some time; the vector u = (u1,u2,u3) = (X -x,Y -y,Z -z) is called the displacement.
When the displacement is small and the solid is elastic, Hooke’s law gives a relationship between the
stress tensor σ(u) = (σij(u)) and the strain tensor ϵ(u) = ϵij(u)
where the Kronecker symbol δij = 1 if i = j, 0 otherwise, with
and where λ,μ are two constants that describe the mechanical properties of the solid, and
are themselves related to the better known constants E, Young’s modulus, and ν, Poisson’s
ratio:
Lamé’s system
Let us consider a beam with axis Oz and with perpendicular section Ω. The components along x and y of
the strain u(x) in a section Ω subject to forces f perpendicular to the axis are governed by
where λ,μ are the Lamé coefficients introduced above.
Remark, we do not used this equation because the associated variationnal form does not give the right
boundary condition, we simply use
where the corresponding variationnal form is:
where : denote the tensor scalar product, i.e. a : b = ∑
i,jaijbij.
So the variationnal form can be written as :
Example
Consider elastic plate with the undeformed rectangle shape [0,20] ×[-1,1]. The body force is the gravity
force
and the boundary force
is zero on lower, upper and right sides. The left vertical sides of the
beam is fixed. The boundary conditions are
Here u = (u,v) has two components.
The above two equations are strongly coupled by their mixed derivatives, and thus any iterative solution
on each of the components is risky. One should rather use FreeFem++ ’s system approach and
write:
Example 3.9 (lame.edp)
// file
lame.edp
mesh Th=square(10,10,[20⋆x,2⋆y-1]);
fespace Vh(Th,P2);
Vh u,v,uu,vv;
real sqrt2=sqrt(2.);
macro epsilon(u1,u2) [dx(u1),dy(u2),(dy(u1)+dx(u2))/sqrt2] //
EOM
// the sqrt2 is because we want: epsilon(u1,u2)'⋆ epsilon(v1,v2)
== ϵ(u) : ϵ(v)
macro div(u,v) ( dx(u)+dy(v) ) //
EOM
real E = 21e5, nu = 0.28, mu= E/(2⋆(1+nu));
real lambda = E⋆nu/((1+nu)⋆(1-2⋆nu)), f = -1; //
solve lame([u,v],[uu,vv])= int2d(Th)(
lambda⋆div(u,v)⋆div(uu,vv)
+2.⋆mu⋆( epsilon(u,v)'⋆epsilon(uu,vv) ) )
- int2d(Th)(f⋆vv)
+ on(4,u=0,v=0);
real coef=100;
plot([u,v],wait=1,ps="lamevect.eps",coef=coef);
mesh th1 = movemesh(Th, [x+u⋆coef, y+v⋆coef]);
plot(th1,wait=1,ps="lamedeform.eps");
real dxmin = u[].min;
real dymin = v[].min;
cout << " - dep. max x = "<< dxmin<< " y=" << dymin << endl;
cout << " dep. (20,0) = " << u(20,0) << " " << v(20,0) << endl;
The numerical results are shown on figure 3.8 and the output is:
-- square mesh : nb vertices =121 , nb triangles = 200 , nb boundary edges 40
-- Solve : min -0.00174137 max 0.00174105
min -0.0263154 max 1.47016e-29
- dep. max x = -0.00174137 y=-0.0263154
dep. (20,0) = -1.8096e-07 -0.0263154
times: compile 0.010219s, execution 1.5827s
3.9 The System of Stokes for Fluids
In the case of a flow invariant with respect to the third coordinate (two-dimensional flow), flows at low
Reynolds number (for instance micro-organisms) satisfy,
where
= (u1,u2) is the fluid velocity and p its pressure.
The driven cavity is a standard test. It is a box full of liquid with its lid moving horizontally at speed one.
The pressure and the velocity must be discretized in compatible fintie element spaces for the LBB
conditions to be satisfied:
// file
stokes.edp
int n=3;
mesh Th=square(10⋆n,10⋆n);
fespace Uh(Th,P1b); Uh u,v,uu,vv;
fespace Ph(Th,P1); Ph p,pp;
solve stokes([u,v,p],[uu,vv,pp]) =
int2d(Th)(dx(u)⋆dx(uu)+dy(u)⋆dy(uu) + dx(v)⋆dx(vv)+ dy(v)⋆dy(vv)
+ dx(p)⋆uu + dy(p)⋆vv + pp⋆(dx(u)+dy(v)))
+ on(1,2,4,u=0,v=0) + on(3,u=1,v=0);
plot([u,v],p,wait=1);
Results are shown on figure 3.9
3.10 A Large Fluid Problem
A friend of one of us in Auroville-India was building a ramp to access an air conditioned room. As I was
visiting the construction site he told me that he expected to cool air escaping by the door to
the room to slide down the ramp and refrigerate the feet of the coming visitors. I told him
”no way” and decided to check numerically. The results are on the front page of this book.
The fluid velocity and pressure are solution of the Navier-Stokes equations with varying density function
of the temperature.
The geometry is trapezoidal with prescribed inflow made of cool air at the bottom and warm air
above and so are the initial conditions; there is free outflow, slip velocity at the top (artificial)
boundary and no-slip at the bottom. However the Navier-Stokes cum temperature equations
have a RANS k -ϵ model and a Boussinesq approximation for the buoyancy. This comes to
We use a time discretization which preserves positivity and uses the method of characteristics
(Xm(x) ≈ x -um(x)δt)
In variational form and with appropriated boundary conditions the problem is:
real L=6;
border aa(t=0,1){x=t; y=0 ;}
border bb(t=0,14){x=1+t; y= - 0.1⋆t ;}
border cc(t=-1.4,L){x=15; y=t ;}
border dd(t=15,0){x= t ; y = L;}
border ee(t=L,0.5){ x=0; y=t ;}
border ff(t=0.5,0){ x=0; y=t ;}
int n=8;
mesh Th=buildmesh(aa(n)+bb(9⋆n) + cc(4⋆n) + dd(10⋆n)+ee(6⋆n) + ff(n));
real s0=clock();
fespace Vh2(Th,P1b); // velocity
space
fespace Vh(Th,P1); // pressure
space
fespace V0h(Th,P0); // for
gradients
Vh2 u2,v2,up1=0,up2=0;
Vh2 u1,v1;
Vh u1x=0,u1y,u2x,u2y, vv;
real reylnods=500;
// cout << " Enter the reynolds number :"; cin >>
reylnods;
assert(reylnods>1 && reylnods < 100000);
up1=0;
up2=0;
func g=(x)⋆(1-x)⋆4; //
inflow
Vh p=0,q, temp1,temp=35, k=0.001,k1,ep=0.0001,ep1;
V0h muT=1,prodk,prode, kappa=0.25e-4, stress;
real alpha=0, eee=9.81/303, c1m = 1.3/0.09 ;
real nu=1, numu=nu/sqrt( 0.09), nuep=pow(nu,1.5)/4.1;
int i=0,iter=0;
real dt=0;
problem TEMPER(temp,q) = // temperature
equation
int2d(Th)(
alpha⋆temp⋆q + kappa ⋆ ( dx(temp)⋆dx(q) + dy(temp)⋆dy(q) ))
// + int1d(Th,aa,bb)(temp⋆q⋆
0.1)
+ int2d(Th) ( -alpha⋆convect([up1,up2],-dt,temp1)⋆q )
+ on(ff,temp=25)
+ on(aa,bb,temp=35) ;
problem kine(k,q)= // get the kinetic turbulent
energy
int2d(Th)(
(ep1/k1+alpha)⋆k⋆q + muT ⋆ ( dx(k)⋆dx(q) + dy(k)⋆dy(q) ))
// +
int1d(Th,aa,bb)(temp⋆q⋆0.1)
+ int2d(Th) ( prodk⋆q-alpha⋆convect([up1,up2],-dt,k1)⋆q )
+ on(ff,k=0.0001) + on(aa,bb,k=numu⋆stress) ;
problem viscturb(ep,q)= // get the rate of turbulent viscous
energy
int2d(Th)(
(1.92⋆ep1/k1+alpha)⋆ep⋆q + c1m⋆muT ⋆ ( dx(ep)⋆dx(q) + dy(ep)⋆dy(q) ))
// +
int1d(Th,aa,bb)(temp⋆q⋆0.1)
+ int2d(Th) ( prode⋆q-alpha⋆convect([up1,up2],-dt,ep1)⋆q )
+ on(ff,ep= 0.0001) + on(aa,bb,ep=nuep⋆pow(stress,1.5)) ;
solve NS ([u1,u2,p],[v1,v2,q]) = // Navier-Stokes k-epsilon and
Boussinesq
int2d(Th)(
alpha⋆( u1⋆v1 + u2⋆v2)
+ muT ⋆ (dx(u1)⋆dx(v1)+dy(u1)⋆dy(v1)+dx(u2)⋆dx(v2)+dy(u2)⋆dy(v2))
// ( 2⋆dx(u1)⋆dx(v1) +
2⋆dy(u2)⋆dy(v2)+(dy(u1)+dx(u2))⋆(dy(v1)+dx(v2)))
+ p⋆q⋆(0.000001)
- p⋆dx(v1) - p⋆dy(v2)
- dx(u1)⋆q - dy(u2)⋆q
)
+ int1d(Th,aa,bb,dd)(u1⋆v1⋆ 0.1)
+ int2d(Th) (eee⋆(temp-35)⋆v1 -alpha⋆convect([up1,up2],-dt,up1)⋆v1
-alpha⋆convect([up1,up2],-dt,up2)⋆v2 )
+ on(ff,u1=3,u2=0)
+ on(ee,u1=0,u2=0)
+ on(aa,dd,u2=0)
+ on(bb,u2= -up1⋆N.x/N.y)
+ on(cc,u2=0) ;
plot(coef=0.2,cmm=" [u1,u2] et p ",p,[u1,u2],ps="StokesP2P1.eps",value=1,wait=1);
{
real[int] xx(21),yy(21),pp(21);
for (int i=0;i<21;i++)
{
yy[i]=i/20.;
xx[i]=u1(0.5,i/20.);
pp[i]=p(i/20.,0.999);
}
cout << " " << yy << endl;
// plot([xx,yy],wait=1,cmm="u1 x=0.5
cup");
// plot([yy,pp],wait=1,cmm="pressure y=0.999
cup");
}
dt = 0.05;
int nbiter = 3;
real coefdt = 0.25^(1./nbiter);
real coefcut = 0.25^(1./nbiter) , cut=0.01;
real tol=0.5,coeftol = 0.5^(1./nbiter);
nu=1./reylnods;
for (iter=1;iter<=nbiter;iter++)
{
cout << " dt = " << dt << " ------------------------ " << endl;
alpha=1/dt;
for (i=0;i<=500;i++)
{
up1=u1;
up2=u2;
temp1=max(temp,25);
temp1=min(temp1,35);
k1=k; ep1=ep;
muT=0.09⋆k⋆k/ep;
NS; plot([u1,u2],wait=1); // Solves
Navier-Stokes
prode =0.126⋆k⋆(pow(2⋆dx(u1),2)+pow(2⋆dy(u2),2)+2⋆pow(dx(u2)+dy(u1),2))/2;
prodk= prode⋆k/ep⋆0.09/0.126;
kappa=muT/0.41;
stress=abs(dy(u1));
kine; plot(k,wait=1);
viscturb; plot(ep,wait=1);
TEMPER; // solves temperature
equation
if ( !(i % 5)){
plot(temp,value=1,fill=true,ps="temp_"+iter+"_"+i+".ps");
plot(coef=0.2,cmm=" [u1,u2] et p ",p,[u1,u2],ps="plotNS_"+iter+"_"+i+".ps");
}
cout << "CPU " << clock()-s0 << "s " << endl;
}
if (iter>= nbiter) break;
Th=adaptmesh(Th,[dx(u1),dy(u1),dx(u1),dy(u2)],splitpbedge=1,
abserror=0,cutoff=cut,err=tol, inquire=0,ratio=1.5,hmin=1./1000);
plot(Th,ps="ThNS.eps");
dt = dt⋆coefdt;
tol = tol ⋆coeftol;
cut = cut ⋆coefcut;
}
cout << "CPU " <<clock()-s0 << "s " << endl;
3.11 An Example with Complex Numbers
In a microwave oven heat comes from molecular excitation by an electromagnetic field. For a plane
monochromatic wave, amplitude is given by Helmholtz’s equation:
We
consider a rectangular oven where the wave is emitted by part of the upper wall. So the boundary of the
domain is made up of a part Γ1 where v = 0 and of another part Γ2 = [c,d] where for instance
v = sin(πy-c
c-d).
Within an object to be cooked, denoted by B, the heat source is proportional to v2. At equilibrium, one
has
where IB is 1 in the object and 0 elsewhere.
Results are shown on figure 3.10
In the program below β = 1∕(1 -I∕2) in the air and 2∕(1 -I∕2) in the object (i =
):
Example 3.10 (muwave.edp)
// file
muwave.edp
real a=20, b=20, c=15, d=8, e=2, l=12, f=2, g=2;
border a0(t=0,1) {x=a⋆t; y=0;label=1;}
border a1(t=1,2) {x=a; y= b⋆(t-1);label=1;}
border a2(t=2,3) { x=a⋆(3-t);y=b;label=1;}
border a3(t=3,4){x=0;y=b-(b-c)⋆(t-3);label=1;}
border a4(t=4,5){x=0;y=c-(c-d)⋆(t-4);label=2;}
border a5(t=5,6){ x=0; y= d⋆(6-t);label=1;}
border b0(t=0,1) {x=a-f+e⋆(t-1);y=g; label=3;}
border b1(t=1,4) {x=a-f; y=g+l⋆(t-1)/3; label=3;}
border b2(t=4,5) {x=a-f-e⋆(t-4); y=l+g; label=3;}
border b3(t=5,8) {x=a-e-f; y= l+g-l⋆(t-5)/3; label=3;}
int n=2;
mesh Th = buildmesh(a0(10⋆n)+a1(10⋆n)+a2(10⋆n)+a3(10⋆n)
+a4(10⋆n)+a5(10⋆n)+b0(5⋆n)+b1(10⋆n)+b2(5⋆n)+b3(10⋆n));
plot(Th,wait=1);
fespace Vh(Th,P1);
real meat = Th(a-f-e/2,g+l/2).region, air= Th(0.01,0.01).region;
Vh R=(region-air)/(meat-air);
Vh<complex> v,w;
solve muwave(v,w) = int2d(Th)(v⋆w⋆(1+R)
-(dx(v)⋆dx(w)+dy(v)⋆dy(w))⋆(1-0.5i))
+ on(1,v=0) + on(2, v=sin(pi⋆(y-c)/(c-d)));
Vh vr=real(v), vi=imag(v);
plot(vr,wait=1,ps="rmuonde.ps", fill=true);
plot(vi,wait=1,ps="imuonde.ps", fill=true);
fespace Uh(Th,P1); Uh u,uu, ff=1e5⋆(vr^2 + vi^2)⋆R;
solve temperature(u,uu)= int2d(Th)(dx(u)⋆ dx(uu)+ dy(u)⋆ dy(uu))
- int2d(Th)(ff⋆uu) + on(1,2,u=0);
plot(u,wait=1,ps="tempmuonde.ps", fill=true);
3.12 Optimal Control
Thanks to the function BFGS it is possible to solve complex nonlinear optimization problem within
FreeFem++. For example consider the following inverse problem
where the desired state ud, the boundary data uΓ and the observation set E ⊂ Ω are all given. Furthermore
let us assume that
where B,C,D are separated subsets of Ω.
To solve this problem by the quasi-Newton BFGS method we need the derivatives of J with respect to
b,c,d. We self explanatory notations, if δb,δc,δd are variations of b,c,d we have
Obviously J′b is equal to δJ when δb = 1,δc = 0,δd = 0, and so on for J′c and J′d.
All this is implemented in the following program
// file
optimcontrol.edp
border aa(t=0, 2⋆pi) { x = 5⋆cos(t); y = 5⋆sin(t); };
border bb(t=0, 2⋆pi) { x = cos(t); y = sin(t); };
border cc(t=0, 2⋆pi) { x = -3+cos(t); y = sin(t); };
border dd(t=0, 2⋆pi) { x = cos(t); y = -3+sin(t); };
mesh th = buildmesh(aa(70)+bb(35)+cc(35)+dd(35));
fespace Vh(th,P1);
Vh Ib=((x^2+y^2)<1.0001),
Ic=(((x+3)^2+ y^2)<1.0001),
Id=((x^2+(y+3)^2)<1.0001),
Ie=(((x-1)^2+ y^2)<=4),
ud,u,uh,du;
real[int] z(3);
problem A(u,uh) =int2d(th)((1+z[0]⋆Ib+z[1]⋆Ic+z[2]⋆Id)⋆(dx(u)⋆dx(uh)
+dy(u)⋆dy(uh))) + on(aa,u=x^3-y^3);
z[0]=2; z[1]=3; z[2]=4;
A; ud=u;
ofstream f("J.txt");
func real J(real[int] & Z)
{
for (int i=0;i<z.n;i++)z[i]=Z[i];
A; real s= int2d(th)(Ie⋆(u-ud)^2);
f<<s<<" "; return s;
}
real[int] dz(3), dJdz(3);
problem B(du,uh)
=int2d(th)((1+z[0]⋆Ib+z[1]⋆Ic+z[2]⋆Id)⋆(dx(du)⋆dx(uh)+dy(du)⋆dy(uh)))
+int2d(th)((dz[0]⋆Ib+dz[1]⋆Ic+dz[2]⋆Id)⋆(dx(u)⋆dx(uh)+dy(u)⋆dy(uh)))
+on(aa,du=0);
func real[int] DJ(real[int] &Z)
{
for(int i=0;i<z.n;i++)
{ for(int j=0;j<dz.n;j++) dz[j]=0;
dz[i]=1; B;
dJdz[i]= 2⋆int2d(th)(Ie⋆(u-ud)⋆du);
}
return dJdz;
}
real[int] Z(3);
for(int j=0;j<z.n;j++) Z[j]=1;
BFGS(J,DJ,Z,eps=1.e-6,nbiter=15,nbiterline=20);
cout << "BFGS: J(z) = " << J(Z) << endl;
for(int j=0;j<z.n;j++) cout<<z[j]<<endl;
plot(ud,value=1,ps="u.eps");
In this example the sets B,C,D,E are circles of boundaries bb,cc,dd,ee are the domain Ω is the circle of
boundary aa. The desired state ud is the solution of the PDE for b = 2,c = 3,d = 4. The unknowns are
packed into array z. Notice that it is necessary to recopy Z into z because one is a local variable while the
other one is global. The program found b = 2.00125,c = 3.00109,d = 4.00551. Figure 3.11 shows u at
convergence and the successive function evaluations of J.
Note that an adjoint state could have been used. Define p by
Consequently
Then the derivatives are found by setting δb = 1,δc = δd = 0 and so on:
Remark
As BFGS stores an M ×M matrix where M is the number of unknowns, it is dangerously expensive to use
this method when the unknown x is a Finite Element Function. One should use another optimizer such as
the NonLinear Conjugate Gradient NLCG (also a key word of FreeFem++). See the file algo.edp in the
examples folder.
3.13 A Flow with Shocks
Compressible Euler equations should be discretized with Finite Volumes or FEM with flux upwinding
scheme but these are not implemented in FreeFem++ . Nevertheless acceptable results can be obtained
with the method of characteristiscs provided that the mean values
=
(f+ + f-) are used at shocks in the
scheme.
One possibility is to couple u,p and then update ρ, i.e. A numerical result is given on Figure 3.12 and the FreeFem++ script is
verbosity=1;
int anew=1;
real x0=0.5,y0=0, rr=0.2;
border ccc(t=0,2){x=2-t;y=1;};
border ddd(t=0,1){x=0;y=1-t;};
border aaa1(t=0,x0-rr){x=t;y=0;};
border cercle(t=pi,0){ x=x0+rr⋆cos(t);y=y0+rr⋆sin(t);}
border aaa2(t=x0+rr,2){x=t;y=0;};
border bbb(t=0,1){x=2;y=t;};
int m=5; mesh Th;
if(anew) Th = buildmesh (ccc(5⋆m) +ddd(3⋆m) + aaa1(2⋆m) + cercle(5⋆m)
+ aaa2(5⋆m) + bbb(2⋆m) );
else Th = readmesh("Th_circle.mesh"); plot(Th,wait=0);
real dt=0.01, u0=2, err0=0.00625, pena=2;
fespace Wh(Th,P1);
fespace Vh(Th,P1);
Wh u,v,u1,v1,uh,vh;
Vh r,rh,r1;
macro dn(u) (N.x⋆dx(u)+N.y⋆dy(u) ) // def the normal
derivative
if(anew){ u1= u0; v1= 0; r1 = 1;}
else {
ifstream g("u.txt");g>>u1[];
ifstream gg("v.txt");gg>>v1[];
ifstream ggg("r.txt");ggg>>r1[];
plot(u1,ps="eta.eps", value=1,wait=1);
err0=err0/10; dt = dt/10;
}
problem eul(u,v,r,uh,vh,rh)
= int2d(Th)( (u⋆uh+v⋆vh+r⋆rh)/dt
+ ((dx(r)⋆uh+ dy(r)⋆vh) - (dx(rh)⋆u + dy(rh)⋆v))
)
+ int2d(Th)(-(rh⋆convect([u1,v1],-dt,r1) + uh⋆convect([u1,v1],-dt,u1)
+ vh⋆convect([u1,v1],-dt,v1))/dt)
+int1d(Th,6)(rh⋆u) //
+int1d(Th,1)(rh⋆v)
+ on(2,r=0) + on(2,u=u0) + on(2,v=0);
int j=80;
for(int k=0;k<3;k++)
{
if(k==20){ err0=err0/10; dt = dt/10; j=5;}
for(int i=0;i<j;i++){
eul; u1=u; v1=v; r1=abs(r);
cout<<"k="<<k<<" E="<<int2d(Th)(u^2+v^2+r)<<endl;
plot(r,wait=0,value=1);
}
Th = adaptmesh (Th,r, nbvx=40000,err=err0,
abserror=1,nbjacoby=2, omega=1.8,ratio=1.8, nbsmooth=3,
splitpbedge=1, maxsubdiv=5,rescaling=1) ;
plot(Th,wait=0);
u=u;v=v;r=r;
savemesh(Th,"Th_circle.mesh");
ofstream f("u.txt");f<<u[];
ofstream ff("v.txt");ff<<v[];
ofstream fff("r.txt");fff<<r[];
r1 = sqrt(u⋆u+v⋆v);
plot(r1,ps="mach.eps", value=1);
r1=r;
}
3.14 Classification of the equations
Summary
It is usually not easy to determine the type of a system. Yet the approximations and algorithms suited to
the problem depend on its type:
- Finite Elements compatible (LBB conditions) for elliptic systems
- Finite difference on the parabolic variable and a time loop on each elliptic subsystem of
parabolic systems; better stability diagrams when the schemes are implicit in time.
- Upwinding, Petrov-Galerkin, Characteristics-Galerkin, Discontinuous-Galerkin, Finite
Volumes for hyperbolic systems plus, possibly, a time loop.
When the system changes type, then expect difficulties (like shock discontinuities)!
Elliptic, parabolic and hyperbolic equations
A partial differential equation (PDE) is a relation between a function of several variables and its
derivatives.
The
range of x over which the equation is taken, here Ω, is called the domain of the PDE. The highest
derivation index, here m, is called the order. If F and φ are vector valued functions, then the PDE is
actually a system of PDEs.
Unless indicated otherwise, here by convention one PDE corresponds to one scalar valued F and φ. If F is
linear with respect to its arguments, then the PDE is said to be linear.
The general form of a second order, linear scalar PDE is ∂2φ __
∂xi∂xj and A : B means ∑
i,j=1da
ijbij.
where f(x),α(x)
,a(x)
d,B(x)
d×d are the PDE coefficients. If the coefficients are independent
of x, the PDE is said to have constant coefficients.
To a PDE we associate a quadratic form, by replacing φ by 1, ∂φ∕∂xi by zi and ∂2φ∕∂xi∂xj by zizj, where z
is a vector in
d :
If it is
the equation of an ellipse (ellipsoid if d ≥ 2), the PDE is said to be elliptic; if it is the equation of a
parabola or a hyperbola, the PDE is said to be parabolic or hyperbolic. If A ≡ 0, the degree is no longer 2
but 1, and for reasons that will appear more clearly later, the PDE is still said to be hyperbolic.
These concepts can be generalized to systems, by studying whether or not the polynomial system P(z)
associated with the PDE system has branches at infinity (ellipsoids have no branches at infinity,
paraboloids have one, and hyperboloids have several).
If the PDE is not linear, it is said to be non linear. Those are said to be locally elliptic, parabolic, or
hyperbolic according to the type of the linearized equation.
For example, for the non linear equation
we
have d = 2,x1 = t,x2 = x and its linearized form is:
which for the unknown u is locally elliptic if ∂φ
∂x < 0 and locally hyperbolic if ∂φ
∂x > 0.
Examples
Laplace’s equation is elliptic:
The
heat equation is parabolic in Q = Ω×]0,T[⊂
d+1 :
If
μ > 0, the wave equation is hyperbolic:
The
convection diffusion equation is parabolic if μ ⇔ 0 and hyperbolic otherwise:
The
biharmonic equation is elliptic:
Boundary conditions
A relation between a function and its derivatives is not sufficient to define the function. Additional
information on the boundary Γ = ∂Ω of Ω, or on part of Γ is necessary.
Such information is called a boundary condition. For example,
is
called a Dirichlet boundary condition. The Neumann condition is
where n is the normal at x
Γ directed towards the exterior of Ω (by definition ∂φ
∂n = ∇φ ⋅n).
Another classical condition, called a Robin (or Fourier) condition is written as:
Finding a set of boundary conditions that defines a unique φ is a difficult art.
In general, an elliptic equation is well posed (i.e. φ is unique) with one Dirichlet, Neumann or Robin
conditions on the whole boundary.
Thus, Laplace’s equations is well posed with a Dirichlet or Neumann condition but also with
Parabolic and hyperbolic equations rarely require boundary conditions on all of Γ×]0,T[. For instance, the
heat equation is well posed with
Here
t is time so the first condition is called an initial condition. The whole set of conditions are also called
Cauchy conditions.
The wave equation is well posed with