KADATH SPECTRAL SOLVER

Tutorial 6 : Using Definitions

This tutorial is devoted to the use of definitions in the Kadath PDE solver.

Test problem

We will use the same physical problem as in tutorial 5, except we will work with the logarithm of the previous field. In order to do so one needs to do the following changes :

  • The bulk equation becomes Delta P + partial_i P partial^i P = 0
  • The inner boundary condition is dr(P) + 1/(2a) = 0
  • The outer boundary condition is P=0

Doing so we have transformed a linear problem into a non-linear one.

Definitions in Kadath

Very often an expression appears several times in a system. Instead of rewritting it each time in terms of the variables and constants, one can pass it to the solver as a definition. This is done via the function add_def

// Assume a system of equation syst and a scalar called field have been defined (as in tutorial 5).
// Pass the term partial_i P partial^i P as a definition
syst.add_var ("P", field) ;
syst.add_def ("dP2 = scal(grad(P), grad(P))") ;

// scal is a reserved word that denotes the flat scalar product
//grad is a reserved word that denotes the flat gradient

After having passed a definition to the system, one can use its name in all Kadath equations

// The bulk equations
for (int d=1 ; d<ndom ; d++)
     syst.add_eq_inside (d, "Lap(P) + dP2 = 0") ;
// Inner BC
syst.add_eq_bc (1, INNER_BC, "dn(P) + 0.5/a = 0") ;
// Outer BC 
syst.add_eq_bc (ndom-1, OUTER_BC, "P=0") ;

Once the matching conditions on the field have been written, one can solve the system and check the resolution, as in Tutorial 5. You will notice that the system being non-linear the Newton-Raphson solver needs several steps to converge.

Why use definitions ?

There are two main reasons :

  • First it makes the writing of the system clearer because one does not need to write everything explicitly in terms of the variables and constants.
  • Second, the value of the definition is computed only once per iteration and not each time it is encountered in the equations. This can lead to significant improvement in terms of computational time.

    For instance, assume the radial derivative of a field appears many times in the equations. If you only write dr(F), Kadath will compute this derivative each time it is encountered. However, if you pass a definition that contains this derivative, it will be computed only once (per iteration), its value will be stored in the definition and just copied each time its value is needed.

Using definitions to do computations

Kadath allows the user to recover the value of a definition using the function give_val_def. It returns the current value of the definition, in the form of a Tensor (see Tutorial 7). Zeros will be put in the domains where the definition is not known.

// Let us compute P^2 in the first shell, after the system resolution
syst.add_def (1, "Psquare = P^2") ; 
// The definition is only defined in the first shell.

// Recover its value as a Tensor, that one will convert to a Scalar field
Scalar P2 (syst.give_val_def("Psquare")) ;
// Print P2 which is not zero only in the first shell
cout << P2 << endl ;

// Compare with the square computed directly
Scalar P2direct (field*field) ;
cout <<  P2direct(1) << endl ;

Of course in this example, it is easier to do a direct computation outside the System_of_eqs but this is not always the case.