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 =   ---
√ -1.


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
              i   j                       i  j
len Ed ge = |q  - q|  if the current edge is [q ,q ]

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

bool ⊂ int ⊂ real ⊂ complex,
the operators “+,-,*,” and “ ^  ” are also applicable on complex-typed variables, but “%, max, min” fall into misuse. Complex numbers such as 5+9i, i= √---
 - 1, 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√ ---
  2.0 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 =   -------
√ a2 + b 2 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 + isin ).

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,

        (x    -x)                ( x   -x)
sinh x = e  - e   ∕2,    co sh x =  e + e   ∕2.

and tanh x = sinh x∕cosh x written by sinh(x), cosh(x), tanh(x), asinh(x), acosh(x) and atanh(x).

                  ------                         ------
sinh -1 x = ln [x + √x2 + 1],    co sh -1x = ln [x + √x2 - 1].

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
f(x) + g(x),f (x ) - g(x),f(x)g(x ),f (x )∕g(x)

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

lnz = ln|z| + iarg z.

Using FreeFem++ , we calculated exp(1+4i), sin(pi+1i), cos(pi/2-1i) and log(1+2i), we then have

   - 1.77 679 - 2.05 72i,  1.889671 0-16 - 1.1 752i,
9.4 483310-17 + 1.17 52i,   0.804 719 + 1.107 15i.
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:
Library Functions
form the mathematical library (version 2.17).

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 =   -
√ z
func g=abs( sin(z/10)⋆exp(z^2/10) );  // g = | sin z∕10 exp z210|
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 = √ -
  z
func g=abs( sin(z/10)⋆exp(z^2/10) );  // g = | sin z∕10 exp z210|
Vh fh = f; plot(fh);   // Fig. 4.1 isovalue of f
Vh gh = g; plot(gh);   // Fig. 4.2 isovalue of g


PIC

Figure 4.1: √-
 z has branch

PIC

Figure 4.2: |sin(z∕10) exp(z210)|


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|+ 1and 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

#{i∕a[i] < v} ~ q * n
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

4.8.3 Matrix Operations

The multiplicative operators *, /, and % group left to right.

there are some compound operator:

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:

                ⌊                                    ⌋
                ||| 1030   0.5    0.    30.  -2.5   0.  |||
                ||||  0.   1030   0.5    0.    0.5   -2.5 ||||
                ||||  0.    0.   1030   0.    0.    0.5  ||||
         A   =  |||||                     30             |||||
                |||| 0.5    0.    0.   10     0.30   0.  ||||
                |||⌈ -2.5   0.5    0.    0.5   10     0.  |||⌉
                   0.   -2.5   0.    0.    0.5   10 30
                (                    )T
 {v} = f[]   =    0  0  0  0  0.5  1
                (              -16                -16 )
 {w} = g[]   =    0  1  1.2 × 10    0  1  1.2 × 10
                (                             29    30 )T
    A⋆ f[]   =    -1.25  - 2.25  0.5  0  5 × 10    10       (= A{v})
                (                              29    30 )T        T
  A '⋆ f[]   =    -1.25  - 2.25  0  0.25  5 × 10    10       (= A  {v})
                ( 0  0  0  0  0.5  1.2 × 10-16 )T                        T
f[] .⋆ g[]   =                                     = (v1w 1  ⋅⋅⋅   vMwM )
f[] ./ g[]   =  ( -Na N  0  0  -Na N  0.5   8.1 × 1015 )T   = (v ∕w  ⋅⋅⋅v ∕w  )T
                                                             1   1    M   M
f[] '⋆ g[]   =  0.5  (= {v}T {w } = {v} ⋅ {w })
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 STL1 [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.

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