Chapter 4
Syntax
4.1 Data Types
In essence FreeFem++ is a compiler: its language is typed, polymorphic, with exception and reentrant.
Every variable must be declared of a certain type, in a declarative statement; each statement are separated
from the next by a semicolon ‘;’. The language allows the manipulation of basic types integers (int),
reals (real), strings (string), arrays (example: real[int]), bidimensional (2D) finite element
meshes (mesh), 2D finite element spaces (fespace) , analytical functions (func), arrays of finite
element functions (func[basic_type]), linear and bilinear operators, sparse matrices, vectors , etc. For
instance
int i,n=20; // i,n are
integer.
real[int] xx(n),yy(n); // two array of size
n
for (i=0;i<=20;i++) // which can be used in statements such
as
{ xx[i]= cos(i⋆pi/10); yy[i]= sin(i⋆pi/10); }
The life of a variable is the current block {…}, except the fespace variable, and the variables local to a
block are destroyed at the end of the block as follows.
Example 4.1
real r= 0.01;
mesh Th=square(10,10); // unit square
mesh
fespace Vh(Th,P1); // P1 lagrange finite element
space
Vh u = x+ exp(y);
func f = z ⋆ x + r ⋆ log(y);
plot(u,wait=true);
{ // new
block
real r = 2; // not the same
r
fespace Vh(Th,P1); // error because Vh is a global
name
} // end of
block
// here r back to
0.01
The type declarations are compulsory in FreeFem++ because it is easy to make bugs in a language with
many types. The variable name is just an alphanumeric string, the underscore character “_” is not allowed,
because it will be used as an operator in the future.
4.2 List of major types
-
bool
- is used for logical expression and flow-control. The result of a comparison is a boolean type
as in
bool fool=(1<2);
which makes fool to be true. Similar examples can be built with ==,<=,>=,<,>,! =.
-
int
- declares an integer.
-
string
- declare the variable to store a text enclosed within double quotes, such as:
"This is a string in double quotes."
-
real
- declares the variable to store a number such as “12.345”.
-
complex
- Complex numbers, such as 1 + 2i, FreeFem++ understand that i =
.
complex a = 1i, b = 2 + 3i;
cout << "a + b = " << a + b << endl;
cout << "a - b = " << a + b << endl;
cout << "a ⋆ b = " << a ⋆ b << endl;
cout << "a / b = " << a / b << endl;
Here’s the output;
a + b = (2,4)
a - b = (-2,-2)
a ⋆ b = (-3,2)
a / b = (0.230769,0.153846)
-
ofstream
- to declare an output file .
-
ifstream
- to declare an input file .
-
real[int
- ] declares a variable that stores multiple real numbers with integer index.
real[int] a(5);
a[0] = 1; a[1] = 2; a[2] = 3.3333333; a[3] = 4; a[4] = 5;
cout << "a = " << a << endl;
This produces the output;
a = 5 :
1 2 3.33333 4 5
-
real[string
- ] declares a variable that store multiple real numbers with string index.
-
string[string
- ] declares a variable that store multiple strings with string index.
-
func
- defines a function without argument, if independent variables are x, y. For example
func f=cos(x)+sin(y) ;
Remark that the function’s type is given by the expression’s type. Raising functions to a
numerical power is done, for instance, by x^1, y^0.23.
-
mesh
- creates the triangulation, see Section 5.
-
fespace
- defines a new type of finite element space, see Section Section 6.
-
problem
- declares the weak form of a partial differential problem without solving it.
-
solve
- declares a problem and solves it.
-
varf
- defines a full variational form.
-
matrix
- defines a sparse matrix.
4.3 Global Variables
The names x,y,z,label,region,P,N,nu_triangle are reserved words used to link the
language to the finite element tools:
-
x
- is the x coordinate of the current point (real value)
-
y
- is the y coordinate of the current point (real value)
-
z
- is the z coordinate of the current point (real value) , but is reserved for future use.
-
label
- contains the label number of a boundary if the current point is on a boundary, 0 otherwise
(int value).
-
region
- returns the region number of the current point (x,y) (int value).
-
P
- gives the current point (
2 value. ). By P.x, P.y, we can get the x,y components of P . Also
P.z is reserved.
-
N
- gives the outward unit normal vector at the current point if it is on a curve define by border
(
3 value). N.x and N.y are x and y components of the normal vector. N.z is reserved. .
-
lenEdge
- gives the length of the current edge
-
hTriangle
- gives the size of the current triangle
-
nuTriangle
- gives the index of the current triangle (int value).
-
nuEdge
- gives the index of the current edge in the triangle (int value).
-
nTonEdge
- gives the number of adjacent triangle of the current edge (integer ).
-
area
- give the area of the current triangle (real value).
-
cout
- is the standard output device (default is console). On MS-Windows, the standard output is
only to console, in this time. ostream
-
cin
- is the standard input device (default is keyboard). (istreamvalue).
-
endl
- give the end of line in the input/output devices.
-
true
- means “true” in bool value.
-
false
- means “false” in bool value.
-
pi
- is the realvalue approximation value of π.
4.4 System Commands
Here is how to show all the types, and all the operator and functions of a FreeFem++ program:
dumptable(cout);
To execute a system command in the string (not implemented on Carbon MacOS)
exec("shell command");
On MS-Windows, we need the full path. For example, if there is the command “ls.exe” in the subdirectory
“c:\cygwin\bin\”, then we must write
exec("c:\\cygwin\\bin\\ls.exe");
Another useful system command is assert() to make sure something is true.
assert(version>=1.40);
4.5 Arithmetic
On integers, +,-,* express the usual arithmetic summation (plus), subtraction (minus) and multiplication
(times), respectively,The operators ∕ and % yield the quotient and the remainder from the division of the
first expression by the second. If the second number of ∕ or % is zero the behavior is undefined. The
maximum or minimum of two integers a,b are obtained by max(a,b) of min(a,b). The power ab of
two integers a,b is calculated by writing a^b. The classical C++ ”arithmetical if” expression a ? b :
c is equal to the value of expression b if the value of expression a is true otherwise is equal to value of
expression c.
Example 4.2 Calculations with the integers
int a = 12, b = 5;
cout <<"plus, minus of "<<a<<" and "<<b<<" are "<<a+b<<", "<<a-b<<endl;
cout <<"multiplication, quotient of them are "<<a⋆b<<", "<<a/b<<endl;
cout <<"remainder from division of "<<a<<" by "<<b<<" is "<<a%b<<endl;
cout <<"the minus of "<<a<<" is "<< -a << endl;
cout <<a<<" plus -"<<b<<" need bracket:"<<a<<"+(-"<<b<<")="<<a+(-b)<<endl;
cout <<"max and min of "<<a<<" and "<<b<<" is "<<max(a,b)<<","<<min(a,b)<< endl;
cout <<b<<"th power of "<<a<<" is "<<a^b<< endl;
cout << " min == (a < b ? a : b ) is " << (a < b ? a : b) << endl;
b=0;
cout <<a<<"/0"<<" is "<< a/b << endl;
cout <<a<<"%0"<<" is "<< a%b << endl;
produce the following result:
plus, minus of 12 and 5 are 17, 7
multiplication, quotient of them are 60, 2
remainder from division of 12 by 5 is 2
the minus of 12 is -12
12 plus -5 need bracket :12+(-5)=7
max and min of 12 and 5 is 12,5
5th power of 12 is 248832
min == (a < b ? a : b) is 5
12/0 : long long long
Fatal error : ExecError Div by 0 at exec line 9
Exec error : exit
By the relation integer ⊂ real, the operators “+,-,*,∕,%” and “ max, min,
” are also applicable
on real-typed variables. However, % calculates the remainder of the integer parts of two real
numbers.
The following are examples similar to Example 4.2
real a=sqrt(2.), b = pi;
cout <<"plus, minus of "<<a<<" and "<<pi<<" are "<< a+b <<", "<< a-b << endl;
cout <<"multiplication, quotient of them are "<<a⋆b<<", "<<a/b<< endl;
cout <<"remainder from division of "<<a<<" by "<<b<<" is "<< a%b << endl;
cout <<"the minus of "<<a<<" is "<< -a << endl;
cout <<a<<" plus -"<<b<<" need bracket :"<<a<<"+(-"<<b<<")="<<a + (-b) << endl;
It gives the following output:
plus, minus of 1.41421 and 3.14159 are 4.55581, -1.72738
multiplication, quotient of them are 4.44288, 0.450158
remainder from division of 1.41421 by 3.14159 is 1
the minus of 1.41421 is -1.41421
1.41421 plus -3.14159 need bracket :1.41421+(-3.14159)=-1.72738
By the relation
the
operators “+,-,*,∕” and “
” are also applicable on complex-typed variables, but “%, max, min”
fall into misuse. Complex numbers such as 5+9i, i=
, can be a little tricky. For real
variables a=2.45, b=5.33, we must write the complex numbers a + i b and a + i
as
complex z1 = a+b⋆1i, z2=a+sqrt(2.0)⋆1i;
The imaginary and real parts of complex number z is obtained by imag and real. The conjugate of
a + bi (a,b are real) is defined by a -bi, which is denoted by conj(a+b⋆1i) in FreeFem++
.
The complex number z = a+ ib is considered as the pair (a,b) of real numbers a,b. We can attach to it the
point (a,b) in the Cartesian plane where the x-axis is for the real part and the y-axis for the imaginary part.
The same point (a,b) has a representation with polar coordinate (r,ϕ), So z his also z = r(cos ϕ + isin ϕ),
r =
and ϕ = tan -1(b∕a); r is called the modulus and ϕ the argument of z. In the following
example, we shall show them using FreeFem++ programming, and de Moivre’s formula
zn = rn(cos nϕ + isin nϕ).
Example 4.3
real a=2.45, b=5.33;
complex z1=a+b⋆1i, z2 = a+sqrt(2.)⋆1i;
func string pc(complex z) // printout complex to
(real)+i(imaginary)
{
string r = "("+real(z);
if (imag(z)>=0) r = r+"+";
return r+imag(z)+"i)";
}
// printout complex to
|z|⋆(cos(arg(z))+i⋆sin(arg(z)))
func string toPolar(complex z)
{
return abs(z)+"⋆(cos("+arg(z)+")+i⋆sin("+arg(z)+"))";
}
cout <<"Standard output of the complex "<<pc(z1)<<" is the pair "
<<z1<<endl;
cout <<"Plus, minus of "<<pc(z1)<<" and "<<pc(z2)<<" are "<< pc(z1+z2)
<<", "<< pc(z1-z2) << endl;
cout <<"Multiplication, quotient of them are "<<pc(z1⋆z2)<<", "
<<pc(z1/z2)<< endl;
cout <<"Real/imaginary part of "<<pc(z1)<<" is "<<real(z1)<<", "
<<imag(z1)<<endl;
cout <<"Absolute of "<<pc(z1)<<" is "<<abs(z1)<<endl;
cout <<pc(z2)<<" = "<<toPolar(z2)<<endl;
cout <<" and polar("<<abs(z2)<<","<<arg(z2)<<") = "
<< pc(polar(abs(z2),arg(z2)))<<endl;
cout <<"de Moivre's formula: "<<pc(z2)<<"^3 = "<<toPolar(z2^3)<<endl;
cout <<"conjugate of "<<pc(z2)<<" is "<<pc(conj(z2))<<endl;
cout <<pc(z1)<<"^"<<pc(z2)<<" is "<< pc(z1^z2) << endl;
Here’s the output from Example 4.3
Standard output of the complex (2.45+5.33i) is the pair (2.45,5.33)
Plus, minus of (2.45+5.33i) and (2.45+1.41421i) are (4.9+6.74421i), (0+3.91579i)
Multiplication, quotient of them are (-1.53526+16.5233i), (1.692+1.19883i)
Real/imaginary part of (2.45+5.33i) is 2.45, 5.33
Absolute of (2.45+5.33i) is 5.86612
(2.45+1.41421i) = 2.82887⋆(cos(0.523509)+i⋆sin(0.523509))
and polar(2.82887,0.523509) = (2.45+1.41421i)
de Moivre's formula: (2.45+1.41421i)^3
= 22.638⋆(cos(1.57053)+i⋆sin(1.57053))
conjugate of (2.45+1.41421i) is (2.45-1.41421i)
(2.45+5.33i)^(2.45+1.41421i) is (8.37072-12.7078i)
4.6 One Variable Functions
-
Fundamental functions
- are built into FreeFem++ .
The power function x
y = pow(x,y)= xy; the exponent function exp(x)
(= ex); the logarithmic function log(x)(= ln x) or log10(x) (= log
10x); the
trigonometric functions sin(x), cos(x), tan(x) depending on angles measured
by radian; the inverse of sin x,cos x,tan x called circular function or also call inverse
trigonometric function asin(x)(=arcsin x), acos(x)(=arccos x), atan(x)(=arctan x);
the atan2(x,y) function computes the principal value of the arc tangent of y∕x, using the
signs of both arguments to determine the quadrant of the return value;
the hyperbolic function,
and tanh x = sinh x∕cosh x written by sinh(x), cosh(x), tanh(x), asinh(x),
acosh(x) and atanh(x).
The real function to round to integer are floor(x) round to largest integral value not greater
than x, ceil(x) round to smallest integral value not less than x, rint(x) functions return
the integral value nearest to x (according to the prevailing rounding mode) in floating-point
format).
-
Elementary Functions
- is the class of functions consisting of the functions in this section
(polynomials, exponential, logarithmic, trigonometric, circular) and the functions obtained
from those listed by the four arithmetic operations
and by superposition f(g(x)), in which four arithmetic operarions and superpositions are permitted
finitely many times. In FreeFem++ , we can create all elementary functions. The
derivative of an elementary function is also elementary. However, the indefinite integral of
an elementary function cannot always be expressed in terms of elementary functions.
Example 4.4 The following s an example where an elementary function is used to build the
border of a domain. Cardioid
real b = 1.;
real a = b;
func real phix(real t)
{
return (a+b)⋆cos(t)-b⋆cos(t⋆(a+b)/b);
}
func real phiy(real t)
{
return (a+b)⋆sin(t)-b⋆sin(t⋆(a+b)/b);
}
border C(t=0,2⋆pi) { x=phix(t); y=phiy(t); }
mesh Th = buildmesh(C(50));
Taking the principal value, we can define log z for z ⇔ 0 by
Using FreeFem++ , we calculated exp(1+4i), sin(pi+1i), cos(pi/2-1i) and
log(1+2i), we then have
-
Random Functions
- from Mersenne Twister (see page
http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html for full
detail). It is A very fast random number generator Of period 2219937 -1, and the functions
are:
- randint32() generates unsigned 32-bit integers.
- randint31() generates unsigned 31-bit integers.
- randreal1() generates uniform real in [0,1] (32-bit resolution).
- randreal2() generates uniform real in [0,1) (32-bit resolution).
- randreal3() generates uniform real in (0,1) (32-bit resolution).
- randres53() generates uniform real in [0,1) with 53-bit resolution.
- randinit(seed ) initializes the state vector by using one 32-bit integer ”seed”,
which may be zero.
-
Library Functions
- form the mathematical library (version 2.17).
- the functions j0(x), j1(x), jn(n,x), y0(x), y1(x), yn(n,x) are
the Bessel functions of first and second kind.
The functions j0(x) and j1(x) compute the Bessel function of the first kind of the
order 0 and the order 1, respectively; the function jn(n, x) computes the Bessel
function of the first kind of the integer order n.
The functions y0(x) and y1(x) compute the linearly independent Bessel function
of the second kind of the order 0 and the order 1, respectively, for the positive integer
value x (expressed as a real); the function yn(n, x) computes the Bessel function of
the second kind for the integer order n for the positive integer value x (expressed as a
real).
- the function tgamma(x) calculates the Γ function of x. lgamma(x) calculates the
natural logorithm of the absolute value of the Γ function of x.
- The erf(x) function
calculates the error function, where erf(x) = 2 ____
pi) ∫
0xexp(-t2)dt. The erfc(x) =
function calculates the complementary error function of x, i.e. erfc(x) = 1 -erf(x).
4.7 Functions of Two Variables
4.7.1 Formula
The general form of real functions with two independent variables x,y is usually written as z = f(x,y). In
FreeFem++ , x and y are reserved word in Section 4.3. When two independent variables are x and y,
we can define a function without argument, for example
func f=cos(x)+sin(y) ;
Remark that the function’s type is given by the expression’s type. The power of functions are given in
FreeFem++ such as x^1, y^0.23. In func, we can write an elementary function as follows
func f = sin(x)⋆cos(y);
func g = (x^2+3⋆y^2)⋆exp(1-x^2-y^2);
func h = max(-0.5,0.1⋆log(f^2+g^2));
Complex valued function create functions with 2 variables x, y as follows,
mesh Th=square(20,20,[-pi+2⋆pi⋆x,-pi+2⋆pi⋆y]); //
] - π,π[2
fespace Vh(Th,P2);
func z=x+y⋆1i; //
z = x + iy
func f=imag(sqrt(z)); //
f = ℑ
func g=abs( sin(z/10)⋆exp(z^2/10) ); //
g = | sin z∕10 exp z2∕10|
Vh fh = f; plot(fh); // contour lines of
f
Vh gh = g; plot(gh); // contour lines of
g
We call also by two variable elementary function functions obtained from elementary functions f(x) or
g(y) by the four arithmetic operations and by superposition in finite times.
4.7.2 FE-function
Arithmetic built-in functions are able to construct a new function by the four arithmetic operations and
superposition of them (see elementary functions), which are called formulas to distinguish from
FE-functions. We can add new formulas easily, if we want. Here, FE-function is an element of finite
element space (real or complex) (see Section Section 6). Or to put it another way: formulas are the
mathematical expressions combining its numerical analogs, but it is independent of meshes
(triangulations).
Also, in FreeFem++, we can give an arbitrary symbol to FE-function combining numerical
calculation by FEM. The interpolation of a formula-function f in a FE-space is done as in
func f=x^2⋆(1+y)^3+y^2;
mesh Th = square(20,20,[-2+2⋆x,-2+2⋆y]); // square
] - 2, 2[2
fespace Vh(Th,P1);
Vh fh=f; // fh is the projection of f to Vh (real
value)
func zf=(x^2⋆(1+y)^3+y^2)⋆exp(x+1i⋆y);
Vh<complex> zh = zf; // zh is the projection of
zf
// to complex value Vh space
The construction of fh (=fh) is explained in Section 6.
Note 4.1 The command plotis valid only for real FE-functions.
Complex valued functions create functions with 2 variables x, y as follows,
mesh Th=square(20,20,[-pi+2⋆pi⋆x,-pi+2⋆pi⋆y]); //
] - π,π[2
fespace Vh(Th,P2);
func z=x+y⋆1i; //
z = x + iy
func f=imag(sqrt(z)); //
f = ℑ
func g=abs( sin(z/10)⋆exp(z^2/10) ); //
g = | sin z∕10 exp z2∕10|
Vh fh = f; plot(fh); // Fig. 4.1 isovalue of
f
Vh gh = g; plot(gh); // Fig. 4.2 isovalue of
g
4.8 Arrays
An array stores multiple objects, and there are 2 kinds of arrays: The first is the vector that is arrays with
integer indices and arrays with string indices.
In the first case, the size of this array must be know at the execution time, and the implementation is
done with the KN<> class so all the vector operator of KN<> are implemented. The sample
real [int] tab(10), tab1(10); // 2 array of 10
real
real [int] tab2; // bug array with no
size
tab = 1.03; // set all the array to
1.03
tab[1]=2.15;
cout << tab[1] << " " << tab[9] << " size of tab = "
<< tab.n << " min: " << tab.min << " max:" << tab.max
<< " sum : " << tab.sum << endl; //
tab.resize(12); // change the size of array
tab
// to 12 with preserving first
value
tab(10:11)=3.14; // set unset
value
cout <<" resize tab: " << tab << endl;
real [string] tt;
tt["+"]=1.5;
cout<<tt["a"]<<" "<<tt["+"]<<endl;
real[int] a(5),b(5),c(5),d(5);
a = 1;
b = 2;
c = 3;
a[2]=0;
d = ( a ? b : c ); // for i = 0, n-1 : d[i] = a[i] ? b[i] : c[i]
,
cout << " d = ( a ? b : c ) is " << d << endl;
d = ( a ? 1 : c ); // for i = 0, n-1: d[i] = a[i] ? 1 : c[i] ,
(v2.23-1)
d = ( a ? b : 0 ); // for i = 0, n-1: d[i] = a[i] ? b[i] : 0 ,
(v2.23-1)
d = ( a ? 1 : 0 ); // for i = 0, n-1: d[i] = a[i] ? 0 : 1 ,
(v2.23-1)
tab.sort ; // sort the array tab (version
2.18)
cout << " tab (after sort) " << tab << endl;
int[int] ii(0:d.n-1); // set array ii to 0,1, ..., d.n-1
(v3.2)
d=-1:-5; // set d to -1,-2, .. -5
(v3.2)
sort(d,ii); // sort array d and ii in
parallele
cout << " d " << d << "\n ii = " << ii << endl;
produce the output
2.15 1.03 size of tab = 10 min: 1.03 max:2.15 sum : 11.42
resize tab: 12
1.03 2.15 1.03 1.03 1.03
1.03 1.03 1.03 1.03 1.03
3.14 3.14
0 1.5
d = ( a ? b : c ) is 5
3 3 2 3 3
tab (after sort) 12
1.03 1.03 1.03 1.03 1.03
1.03 1.03 1.03 1.03 2.15
3.14 3.14
d 5
-5 -4 -3 -2 -1
ii = 5
4 3 2 1 0
You can set array like in matlab or scilab with operator ::, the array generator of a:c is equivalent to
a:1:c, and the array set by a:b:c is set to size ⌊|(b -a)∕c|+ 1⌋ and the value i is set by
a + i(b -a)∕c.
So you have of int,real, complex array:
// version 3.2 mai
2009
// like matlab. and
scilab
{
int[int] tt(2:10); //
2,3,4,5,6,7,8,9,10
int[int] t1(2:3:10); //
2,5,8,
cout << " tt(2:10)= " << tt << endl;
cout << " t1(2:3:10)= " << t1 << endl;
tt=1:2:5;
cout << " 1.:2:5 => " << tt << endl;
}
{
real[int] tt(2:10); //
2,3,4,5,6,7,8,9,10
real[int] t1(2.:3:10.); //
2,5,8,
cout << " tt(2:10)= " << tt << endl;
cout << " t1(2:3:10)= " << t1 << endl;
tt=1.:0.5:3.999;
cout << " 1.:0.5:3.999 => " << tt << endl;
}
{
complex[int] tt(2.+0i:10.+0i); //
2,3,4,5,6,7,8,9,10
complex[int] t1(2.:3.:10.); //
2,5,8,
cout << " tt(2.+0i:10.+0i)= " << tt << endl;
cout << " t1(2.:3.:10.)= " << t1 << endl;
}
The output is :
tt(2:10)= 9
2 3 4 5 6
7 8 9 10
t1(2:3:10)= 3
2 5 8
1.:2:5 => 3
1 3 5
tt(2:10) = = 9
2 3 4 5 6
7 8 9 10
t1(2.:3:10.)= 3
2 5 8
1.:0.5:3.999 => 6
1 1.5 2 2.5 3
3.5
tt(2.+0i:10.+0i)= 9
(2,0) (3,0) (4,0) (5,0) (6,0)
(7,0) (8,0) (9,0) (10,0)
t1(2.:3.:10.);= 3
(2,0) (5,0) (8,0)
the all integer array operator are :
{
int N=5;
real[int] a(N),b(N),c(N);
a =1;
a(0:4:2) = 2;
a(3:4) = 4;
cout <<" a = " << a << endl;
b = a+ a;
cout <<" b = a+a : " << b << endl;
b += a;
cout <<" b += a : " << b << endl;
b += 2⋆a;
cout <<" b += 2⋆a : " << b << endl;
b /= 2;
cout <<" b /= 2 : " << b << endl;
b ⋆= a; // same b = b .⋆
a
cout << "b⋆=a; b =" << b << endl;
b /= a; // same b = b ./
a
cout << "b/=a; b =" << b << endl;
c = a+b;
cout << " c =a+b : c=" << c << endl;
c = 2⋆a+4⋆b;
cout << " c =2⋆a+4b : c= " << c << endl;
c = a+4⋆b;
cout << " c =a+4b : c= " << c << endl;
c = -a+4⋆b;
cout << " c =-a+4b : c= " << c << endl;
c = -a-4⋆b;
cout << " c =-a-4b : c= " << c << endl;
c = -a-b;
cout << " c =-a-b : c= " << c << endl;
c = a .⋆ b;
cout << " c =a.⋆b : c= " << c << endl;
c = a ./ b;
cout << " c =a./b : c= " << c << endl;
c = 2 ⋆ b;
cout << " c =2⋆b : c= " << c << endl;
c = b⋆2 ;
cout << " c =b⋆2 : c= " << c << endl;
/⋆ this operator do not exist
c = b/2 ;
cout << " c =b/2 : c= " << c << endl;
⋆/
// ---- the methods
--
cout << " ||a||_1 = " << a.l1 << endl; //
cout << " ||a||_2 = " << a.l2 << endl; //
cout << " ||a||_infty = " << a.linfty << endl; //
cout << " sum a_i = " << a.sum << endl; //
cout << " max a_i = " << a.max << endl; //
cout << " min a_i = " << a.min << endl; //
cout << " a'⋆a = " << (a'⋆a) << endl; //
cout << " a quantile 0.2 = " << a.quantile(0.2) << endl; //
}
produce the output
5
3 3 2 3 3
== 3 3 2 3 3
a = 5
2 1 2 4 4
b = a+a : 5
4 2 4 8 8
b += a : 5
6 3 6 12 12
b += 2⋆a : 5
10 5 10 20 20
b /= 2 : 5
5 2.5 5 10 10
b⋆=a; b =5
10 2.5 10 40 40
b/=a; b =5
5 2.5 5 10 10
c =a+b : c=5
7 3.5 7 14 14
c =2⋆a+4b : c= 5
24 12 24 48 48
c =a+4b : c= 5
22 11 22 44 44
c =-a+4b : c= 5
18 9 18 36 36
c =-a-4b : c= 5
-22 -11 -22 -44 -44
c =-a-b : c= 5
-7 -3.5 -7 -14 -14
c =a.⋆b : c= 5
10 2.5 10 40 40
c =a./b : c= 5
0.4 0.4 0.4 0.4 0.4
c =2⋆b : c= 5
10 5 10 20 20
c =b⋆2 : c= 5
10 5 10 20 20
||a||_1 = 13
||a||_2 = 6.403124237
||a||_infty = 4
sum a_i = 13
max a_i = 4
min a_i = 1
a'⋆a = 41
a quantile 0.2 = 2
Note 4.2 Quantiles are points taken at regular intervals from the cumulative distribution function
of a random variable. Here the random is the array value.
This statisticial function a.quantile(q) and commute v of an array a of size n for a given
number q
]0,1[ such than
which is equivalent to v = a[q *n] when the array a is sorted.
Example of array with renumbering (version 2.3 or better) . The renumbering is always given by an
integer array, and if a value in the array is negative, the mining is not image, so we do not set the value.
int[int] I=[2,3,4,-1,0]; // the integer mapping to set the
renumbering
b=c=-3;
b= a(I); // for( i=0;i<b.n;i++) if(I[i] >=0)
b[i]=a[I[i]];
c(I)= a; // for( i=0;i<I.n;i++) if(I[i] >=0)
C(I[i])=a[i];
cout << " b = a(I) : " << b << "\n c(I) = a " << c << endl;
The output is
b = a(I) : 5
2 4 4 -3 2
c(I) = a 5
4 -3 2 1 2
4.8.1 Arrays with two integer indices versus matrix
Some example transform full matrices in sparse matrices.
int N=3,M=4;
real[int,int] A(N,M);
real[int] b(N),c(M);
b=[1,2,3];
c=[4,5,6,7];
complex[int,int] C(N,M);
complex[int] cb=[1,2,3],cc=[10i,20i,30i,40i];
b=[1,2,3];
int [int] I=[2,0,1];
int [int] J=[2,0,1,3];
A=1; // set the all
matrix
A(2,:) = 4; // the full line
2
A(:,1) = 5; // the full column
1
A(0:N-1,2) = 2; // set the column
2
A(1,0:2) = 3; // set the line 1 from 0 to
2
cout << " A = " << A << endl;
// outer
product
C = cb⋆cc';
C += 3⋆cb⋆cc';
C -= 5i⋆cb⋆cc';
cout << " C = " << C << endl;
// the way to transform a array to a sparse
matrix
matrix B;
B = A;
B=A(I,J); // B(i,j)=
A(I(i),J(j))
B=A(I^-1,J^-1); // B(I(i),J(j))=
A(i,j)
A = 2.⋆b⋆c'; // outer
product
cout << " A = " << A << endl;
B = b⋆c'; // outer product B(i,j) =
b(i)⋆c(j)
B = b⋆c'; // outer product B(i,j) =
b(i)⋆c(j)
B = (2⋆b⋆c')(I,J); // outer product B(i,j) =
b(I(i))⋆c(J(j))
B = (3.⋆b⋆c')(I^-1,J^-1); // outer product B(I(i),J(j)) =
b(i)⋆c(j)
cout << "B = (3.⋆b⋆c')(I^-1,J^-1) = " << B << endl;
the output is
b = a(I) : 5
2 4 4 -3 2
c(I) = a 5
4 -3 2 1 2
A = 3 4
1 5 2 1
3 3 3 1
4 5 2 4
C = 3 4
(-50,-40) (-100,-80) (-150,-120) (-200,-160)
(-100,-80) (-200,-160) (-300,-240) (-400,-320)
(-150,-120) (-300,-240) (-450,-360) (-600,-480)
A = 3 4
8 10 12 14
16 20 24 28
24 30 36 42
4.8.2 Matrix construction and setting
- To change the solver associated to a matrix do
set(M,solver=sparsesolver);
The default solver is GMRES.
- from a variationnal form: (see section 6.10 page 282 for details)
varf vDD(u,v) = int2d(Thm)(u⋆v⋆1e-10);
matrix DD=vDD(Lh,Lh);
- To set from a constant matrix
matrix A =
[[ 0, 1, 0, 10],
[ 0, 0, 2, 0],
[ 0, 0, 0, 3],
[ 4,0 , 0, 0]];
- To set from a block matrix
matrix M=[
[ Asd[0] ,0 ,0 ,0 ,Csd[0] ],
[ 0 ,Asd[1] ,0 ,0 ,Csd[1] ],
[ 0 ,0 ,Asd[2] ,0 ,Csd[2] ],
[ 0 ,0 ,0 ,Asd[3] ,Csd[3] ],
[ Csd[0]',Csd[1]',Csd[2]',Csd[3]',DD ]
];
// to now to pack the right hand
side
real[int] bb =[rhssd[0][], rhssd[1][],rhssd[2][],rhssd[3][],rhsl[] ];
set(M,solver=sparsesolver);
xx = M^-1 ⋆ bb;
[usd[0][],usd[1][],usd[2][],usd[3][],lh[]] = xx; // to
dispatch
// the solution on each
part.
where here Asd and Csd are array of matrix (from example mortar-DN-4.edp of
examples++-tuturial).
- To set or get all the indices and coef of the sparse matrix A, let I,J,C respectively two
int[int] array and a real[int] array. The three array defined the matrix as follow
and you have: Mab is a basic matrix with the only non zero term mab = 1.
You can write [I,J,C]=A ; to get all the term of the matrix A (the arrays are
automaticaly resize), and A=[I,J,C] ; to change all the term matrix. remark the size of
the matrix is with n= I.max and m=J.max. Remark you forget I,J when the build
a diagonal matrix, and n,m of the matrix.
4.8.3 Matrix Operations
The multiplicative operators *, /, and % group left to right.
- ' is unary right transposition of array, matrix in real case or Hermitian conjugation in
complex case.
- .⋆ is the term to term multiply operator.
- ./ is the term to term divide operator.
there are some compound operator:
- ^-1 is for solving the linear system (example: b = A^-1 x)
- ' ⋆ is the compound of transposition and matrix product, so it is the dot product
(example real DotProduct=a'⋆b) , in complex case you get the Hermitian product,
so mathematically we have a'⋆b= aTb .
- a⋆b' is the outer product (example matrix B=a'⋆b)
Example 4.5
mesh Th = square(2,1);
fespace Vh(Th,P1);
Vh f,g;
f = x⋆y;
g = sin(pi⋆x);
Vh<complex> ff,gg; // a complex valued finite element function
ff= x⋆(y+1i);
gg = exp(pi⋆x⋆1i);
varf mat(u,v) =
int2d(Th)(1⋆dx(u)⋆dx(v)+2⋆dx(u)⋆dy(v)+3⋆dy(u)⋆dx(v)+4⋆dy(u)⋆dy(v))
+ on(1,2,3,4,u=1);
varf mati(u,v) =
int2d(Th)(1⋆dx(u)⋆dx(v)+2i⋆dx(u)⋆dy(v)+3⋆dy(u)⋆dx(v)+4⋆dy(u)⋆dy(v))
+ on(1,2,3,4,u=1);
matrix A = mat(Vh,Vh); matrix<complex> AA = mati(Vh,Vh); // a complex
sparse matrix
Vh m0; m0[] = A⋆f[];
Vh m01; m01[] = A'⋆f[];
Vh m1; m1[] = f[].⋆g[];
Vh m2; m2[] = f[]./g[];
cout << "f = " << f[] << endl;
cout << "g = " << g[] << endl;
cout << "A = " << A << endl;
cout << "m0 = " << m0[] << endl;
cout << "m01 = " << m01[] << endl;
cout << "m1 = "<< m1[] << endl;
cout << "m2 = "<< m2[] << endl;
cout << "dot Product = "<< f[]'⋆g[] << endl;
cout << "hermitien Product = "<< ff[]'⋆gg[] << endl;
cout << "outer Product = "<< (A=ff[]⋆gg[]') << endl;
cout << "hermitien outer Product = "<< (AA=ff[]⋆gg[]') << endl;
real[int] diagofA(A.n);
diagofA = A.diag; // get the diagonal of the
matrix
A.diag = diagofA ; // set the diagonal of the matrix
// version 2.17 or better
---
int[int] I(1),J(1); real[int] C(1);
[I,J,C]=A; // get of the sparse term of the matrix A (the array are
resized)
cout << " I= " << I << endl;
cout << " J= " << J << endl;
cout << " C= " << C << endl;
A=[I,J,C]; // set a new
matrix
matrix D=[diagofA] ; // set a diagonal matrix D from the array diagofA.
cout << " D = " << D << endl;
You can also resize a sparse matrix A
A.resize(10,100);
remark, the new size can be greater or lesser than the previous size, all new term are put to zero.
On the triangulation of Figure 2.4 this produce the following:
The output of the I,J,C array:
I= 18
0 0 0 1 1
1 1 2 2 3
3 4 4 4 4
5 5 5
J= 18
0 1 4 1 2
4 5 2 5 0
3 0 1 3 4
1 4 5
C= 18
1e+30 0.5 -2.5 1e+30 0.5
0.5 -2.5 1e+30 0.5 0.5
1e+30 -2.5 0.5 0.5 1e+30
-2.5 0.5 1e+30
The output of a diagonal sparse matrix D (Warning du to fortran interface the indices start on the output
at one, but in FreeFem++ in index as in Cbegin at zero);
D = # Sparce Matrix (Morse)
# first line: n m (is symmetic) nbcoef
# after for each nonzero coefficient: i j a_ij where (i,j) \in {1,...,n}x{1,...,m}
6 6 1 6
1 1 1.0000000000000000199e+30
2 2 1.0000000000000000199e+30
3 3 1.0000000000000000199e+30
4 4 1.0000000000000000199e+30
5 5 1.0000000000000000199e+30
6 6 1.0000000000000000199e+30
Note 4.3 The operators ^-1cannot be used to create a matrix; the following gives an error
matrix AAA = A^-1;
But in examples++-load/lapack.edp you can inverse full matrix using lapack labrary and this small
dynamic link interface (see for more detail section C page 499).
load "lapack"
load "fflapack"
int n=5;
real[int,int] A(n,n),A1(n,n),B(n,n);
for(int i=0;i<n;++i)
for(int j=0;j<n;++j)
A(i,j)= (i==j) ? n+1 : 1;
cout << A << endl;
A1=A^-1; // def in
load "lapack"
cout << A1 << endl;
B=0;
for(int i=0;i<n;++i)
for(int j=0;j<n;++j)
for(int k=0;k<n;++k)
B(i,j) +=A(i,k)⋆A1(k,j);
cout << B << endl;
// A1+A^-1; attention ne marche
pas
inv(A1); // def in
load "fflapack"
cout << A1 << endl;
and the output is:
5 5
6 1 1 1 1
1 6 1 1 1
1 1 6 1 1
1 1 1 6 1
1 1 1 1 6
error: dgesv_ 0
5 5
0.18 -0.02 -0.02 -0.02 -0.02
-0.02 0.18 -0.02 -0.02 -0.02
-0.02 -0.02 0.18 -0.02 -0.02
-0.02 -0.02 -0.02 0.18 -0.02
-0.02 -0.02 -0.02 -0.02 0.18
5 5
1 -1.387778781e-17 -1.040834086e-17 3.469446952e-17 0
-1.040834086e-17 1 -1.040834086e-17 -2.081668171e-17 0
3.469446952e-18 -5.551115123e-17 1 -2.081668171e-17 -2.775557562e-17
1.387778781e-17 -4.510281038e-17 -4.857225733e-17 1 -2.775557562e-17
-1.387778781e-17 -9.714451465e-17 -5.551115123e-17 -4.163336342e-17 1
5 5
6 1 1 1 1
1 6 1 1 1
1 1 6 1 1
1 1 1 6 1
1 1 1 1 6
to compile lapack.cpp or fflapack.cpp you must have the library lapack on you system
and try in directory examples++-load
ff-c++ lapack.cpp -llapack
ff-c++ fflapack.cpp -llapack
4.8.4 Other arrays
It is also possible to make an array of FE functions, with the same syntax, and we can treat them as vector
valued function if we need them.
Example 4.6 In the following example, Poisson’s equation is solved for 3 different given functions
f = 1,sin(πx) cos(πy),|x -1||y -1|, whose solutions are stored in an array of FE function.
mesh Th=square(20,20,[2⋆x,2⋆y]);
fespace Vh(Th,P1);
Vh u, v, f;
problem Poisson(u,v) =
int2d(Th)( dx(u)⋆dx(v) + dy(u)⋆dy(v))
+ int2d(Th)( -f⋆v ) + on(1,2,3,4,u=0) ;
Vh[int] uu(3); // an array of FE
function
f=1; //
problem1
Poisson; uu[0] = u;
f=sin(pi⋆x)⋆cos(pi⋆y); //
problem2
Poisson; uu[1] = u;
f=abs(x-1)⋆abs(y-1); //
problem3
Poisson; uu[2] = u;
for (int i=0; i<3; i++) // plots all
solutions
plot(uu[i], wait=true);
For the second case, it is just a map of the
STL [26]
so no operations on vector are allowed, except the selection of an item .
The transpose or Hermitian conjugation operator is ' like Matlab or Scilab, so the way to compute the dot
product of two array a,b is real ab= a'⋆b.
int i;
real [int] tab(10), tab1(10); // 2 array of 10
real
real [int] tab2; // Error: array with no
size
tab = 1; // set all the array to
1
tab[1]=2;
cout << tab[1] << " " << tab[9] << " size of tab = "
<< tab.n << " " << tab.min << " " << tab.max << " " << endl;
tab1=tab; tab=tab+tab1; tab=2⋆tab+tab1⋆5; tab1=2⋆tab-tab1⋆5;
tab+=tab; cout << " dot product " << tab'⋆tab << endl; //
t
tabtab
cout << tab << endl; cout << tab[1] << " "
<< tab[9] << endl; real[string] map; // a dynamic
array
for (i=0;i<10;i=i+1)
{
tab[i] = i⋆i;
cout << i << " " << tab[i] << "\n";
};
map["1"]=2.0;
map[2]=3.0; // 2 is automatically cast to the string
"2"
cout << " map[\"1\"] = " << map["1"] << "; "<< endl;
cout << " map[2] = " << map[2] << "; "<< endl;
4.9 Loops
The for and while loops are implemented with break and continue keywords.
In for-loop, there are three parameters; the INITIALIZATION of a control variable, the CONDITION to
continue, the CHANGE of the control variable. While CONDITION is true, for-loop continue.
for (INITIALIZATION; CONDITION; CHANGE)
{ BLOCK of calculations }
Below, the sum from 1 to 10 is calculated by (the result is in sum),
int sum=0;
for (int i=1; i<=10; i++)
sum += i;
The while-loop
while (CONDITION) {
BLOCK of calculations or change of control variables
}
is executed repeatedly until CONDITION become false. The sum from 1 to 10 is also written by while as
follows,
int i=1, sum=0;
while (i<=10) {
sum += i; i++;
}
We can exit from a loop in midstream by break. The continue statement will pass the part from
continue to the end of the loop.
Example 4.7
for (int i=0;i<10;i=i+1)
cout << i << "\n";
real eps=1;
while (eps>1e-5)
{ eps = eps/2;
if( i++ <100) break;
cout << eps << endl;}
for (int j=0; j<20; j++) {
if (j<10) continue;
cout << "j = " << j << endl;
}
4.10 Input/Output
The syntax of input/output statements is similar to C++ syntax. It uses cout, cin, endl,
<<,>>.
To write to (resp. read from) a file, declare a new variable ofstream ofile("filename"); or
ofstream ofile("filename",append); (resp. ifstream ifile("filename"); ) and
use ofile (resp. ifile) as cout (resp. cin).
The word append in ofstream ofile("filename",append); means openning a file in append
mode.
Note 4.4 The file is closed at the exit of the enclosing block,
Example 4.8
int i;
cout << " std-out" << endl;
cout << " enter i= ? ";
cin >> i ;
{
ofstream f("toto.txt");
f << i << "coucou'\n";
}; // close the file f because the variable f is
delete
{
ifstream f("toto.txt");
f >> i;
}
{
ofstream f("toto.txt",append);
// to append to the existing file
"toto.txt"
f << i << "coucou'\n";
}; // close the file f because the variable f is
delete
cout << i << endl;
We add function to format the output.
- int nold=f.precision(n) Sets the number of digits printed to the right of the
decimal point. This applies to all subsequent floating point numbers written to that output
stream. However, this won’t make floating-point ”integers” print with a decimal point. It’s
necessary to use fixed for that effect.
- f.scientific Formats floating-point numbers in scientific notation ( d.dddEdd )
- f.fixed Used fixed point notation ( d.ddd ) for floating-point numbers. Opposite of
scientific.
- f.showbase Converts insertions to an external form that can be read according to the C++
lexical conventions for integral constants. By default, showbase is not set.
- f.noshowbase unset showbase flags
- f.showpos inserts a plus sign (+) into a decimal conversion of a positive integral value.
- f.noshowpos unset showpos flags
- f.default reset all the previous flags (fmtflags) to the default expect precision.
Where f is output stream descriptor, for example cout.
Remark, all this method exept the first return the stream f, so you can write
cout.scientific.showpos << 3 << endl;
4.11 Exception handling
In the version 2.3 of FreeFem++, we add exception handing like in C++. But to day we only catch all
the C++ exception, and in the C++ all the errors like ExecError, assert, exit, ... are
exception so you can have some surprise with the memory management.
The exception handle all ExecError:
Example 4.9 A simple example first to catch a division by zero:
real a;
try {
a=1./0.;
}
catch (...) // today only all exceptions are
permitted
{
cout << " Catch an ExecError " << endl;
a =0;
}
The output is
1/0 : d d d
current line = 3
Exec error : Div by 0
-- number :1
Try:: catch (...) exception
Catch an ExecError
Add a more realistic example:
Example 4.10 A case with none invertible matrix:
int nn=5 ;
mesh Th=square(nn,nn);
verbosity=5;
fespace Vh(Th,P1); // P1 FE
space
Vh uh,vh; // unkown and test
function.
func f=1; // right hand side
function
func g=0; // boundary condition
function
real cpu=clock();
problem laplace(uh,vh,solver=Cholesky,tolpivot=1e-6) = //
definion of the problem
int2d(Th)( dx(uh)⋆dx(vh) + dy(uh)⋆dy(vh) ) // bilinear
form
+ int2d(Th)( -f⋆vh ) // linear
form
;
try {
cout << " Try Cholesky \n";
laplace; // solve the problem plot(uh); // to see the
result
cout << "-- lap Cholesky " << nn << "x" << nn << " : " << -cpu+clock()
<< " s, max =" << uh[].max << endl;
}
catch(...) { // catch
all
cout << " Catch cholesky PB " << endl;
}
The output is
-- square mesh : nb vertices =36 , nb triangles = 50 ...
Nb of edges on Mortars = 0
Nb of edges on Boundary = 20, neb = 20
Nb Mortars 0
number of real boundary edges 20
Number of Edges = 85
Number of Boundary Edges = 20 neb = 20
Number of Mortars Edges = 0
Nb Of Mortars with Paper Def = 0 Nb Of Mortars = 0 ...
Nb Of Nodes = 36
Nb of DF = 36
Try Cholesky
-- Change of Mesh 0 0x312e9e8
Problem(): initmat 1 VF (discontinuous Galerkin) = 0
-- SizeOfSkyline =210
-- size of Matrix 196 Bytes skyline =1
-- discontinous Galerkin =0 size of Mat =196 Bytes
-- int in Optimized = 1, ...
all
-- boundary int Optimized = 1, all
ERREUR choleskypivot (35)= -1.23124e-13 < 1e-06
current line = 28
Exec error : FATAL ERREUR dans ../femlib/MatriceCreuse_tpl.hpp
cholesky line:
-- number :545
catch an erreur in solve => set sol = 0 !!!!!!!
Try:: catch (...) exception
Catch cholesky PB