Home > Exercises Chapter 2 > Exercise 3

Exercise 3


         
In part (a) of exercise 3 we will construct the element mass matrix M. To do this we can simply take the code of exercise 1a, add three functions and make a minor change to the integration function. These are: the familliar dmatrix, a function called basis to collect the basis functions and a function called assem which calls the different fuctions to construct M. the new variables (as compared with 1a) are: P for the order of the polynomial expansion, Mdim for the size of the matrix (which here is equal to P+1), phi1 and phi2 for the basis functions and of course M for the mass matrix. Also the function func is not necessary. The change to the integration function to make it capable of integrating the product of two functions is rather easy to implement. To collect the basis functions as given in equation (2.38) in section 2.3.3.3 of the book the routine jacobfd is called from the function basis. jacobfd is documented here. Make sure that the cases p=0 and p=P are taken care of properly. Here is my solution. Because of the many 'near-zero-numbers' it is probably difficult to see the structure as shown in figure 2.6a in the book so it can be advantageous to build a function which puts near-zero's to zero.

When using Lagrangian interpolants, as seen in equation (2.41) of the book, the code looks like this. The only change is in the basis function. To collect the right polynomials use the polylib function hgll which is the 'alpha=beta=0 version' of hglj which is documented here. It can be seen that when running the code with np=P+1 we get a diagonal M (again neglecting the almost-zeros).

We now find ourselves at a crucial point, exercise 3c will be the first exercise where a matrix will be inverted. This cost me quite a lot of trouble but with my explanation it will not be difficult. First however, the right hand side vector f must be constructed. It is not difficult to do this, it only takes an extra command outside the inner loop in the M assembly function (which I called assem). At this point for me the trouble started. That's why I will now take some time to discuss several features of the Lapack library. If you are already familliar with Lapack it might be faster to skip this introduction.


Lapack introduction


The Lapack library is a library that provides a lot of matrix/vector operation routines. It is assumed that Lapack is installed on your workstation and you have access to it (which, as I am told, is quite common on a Linux computer). Lapack is written in Fortran77 so you must remember to put an underscore _ behind the function in the call. For the Lapack functions there exists an entire naming system which can be found in the Lapack user's guide. To invert the matrix in exercise 3c I will, unlike the suggestion in the exercise, use the LU-decomposition and solve routines dgetrf_ and dgetrs_. In these functions the first letter, thus d, means that the routines are of double precision. The next two letters, ge, mean that the routine can be used for general matrices (i.e. unsymmetric). Finally the letters trf mean factorisation and trs means use the factorisation and solve. To use a routine from Lapack this routine must be declared in a special way, I did this like this:

    extern "C" {extern void dgetrf_(int *, int *, double (*), int *, int [], int *);}

Thus without a semicolon in the end! Now the function dgetrf can be used just like a normal function (only with the extra underscore). Because in Fortran functionarguments must be adresses we must take care that in our code this is also the case. Arguments must thus be pointers or normal variables preceded by the adress operator &. To find an explanation of the Lapack functions I just used Google and typed (for example) "dgetrf arguments", now the first (with dgetrs the third) reference is a page with a good description. To use dgetrf we can enter in our code:

    dgetrf_(&Mdim, &Mdim, M[0], &Mdim, ipiv, &INFO);

The first, second and fourth argument are the adresses of the dimension of the matrix. The third argument is the matrix itself. ipiv is an integer pointer the lenght of the matrix which will store the pivot indices and INFO is an integer which will get the value 0 if the factorisation is completed succesfully. To solve I used:

    dgetrs_(&TRANS, &Mdim, &NRHS, M[0], &Mdim, ipiv, f, &Mdim, &INFO);

TRANS is an unsigned character precision variable that is set to the capital letter T, thus:

    TRANS = 'T';

which basically states that Lapack must use the transpose of A because in Fortran matrices are stored in a transpose way as compared to C and C++. NRHS is an integer that gives the number of columns of the right hand side vector (in our case 1) and f is the right hand side vector which after the solve is filled with our solution vector u. When we now compile the code we must not forget to link lapack and g2c (g2c translates fortran to c), the compiling commands now look like this:

    g++ -c polylib.c
    g++ -c exercise3c.cpp
    g++ -c exercise3c exercise3c.o polylib.o -llapack -lg2c


If everything goes as planned we should now have the solution vector u! This now ends the Lapack introduction section and we will continue with exercise 3c again.


We now have the weigths of the different polynomials in the vector f, so to construct the solution u_delta we only have to multiply them by their different basis functions and sum. For this I made a function called sol. For the allocation of the integervector ipiv I've made a function ivector. The output of my code is organised in such a way that when copy-pasting it to Matlab and running it a plot will be generated of the solution.

Imposing Dirichlet boundary conditions (bc's) is the aim of exercise 3d. To do this we decompose the solution into an unknown homogeneous solution u_H and a known solution which satisfies the Dirichlet bc's u_D. The weights for the solution u_H can be found by solving the matrix inversion where the basis functions for the mass matrix M run from p=1 to p=P-1 (thus the size of the matrix will become Mdim=P-1). Construction of u_D will be done by a new function called rhbc. Don't forget to change the right-hand-side vector to the right form and the change in the summation in the construction of u_H. The two-element-vector bc contains the bc's, bc[0] is the left bc and bc[1] the right bc. Here is my solution.

The only change that has to be made for part (e) is the inclusion of the mapping function chi as defined in exercise 1b and the insertion of the Jacobian Jac at the right places. don't forget to enter the right bc's! Oh, my code.