KADATH SPECTRAL SOLVER

Tutorial 7 : Tensors

This tutorial concerns vectors and higher order tensors.

Tensorial basis

Before defining some tensors, one needs to describe the tensorial basis used. This object is different from the spectral basis and the two notions should not be confused. In each domain, the tensorial basis describes the basis on which the coordinates of a given tensor are known. In Kadath, there are two mains possibilities :

  • Cartesian basis denoted by the reserved word CARTESIAN_BASIS
  • Orthonormal spherical basis denoted by the reserved word SPHERICAL_BASIS

It is not because one is working in spherical coordinates than the Cartesian basis cannot be used. It just means that a vector V, will be described by its components Vx, Vy and Vz, each of them being given as a function of r, theta and phi.

Different tensorial basis can also be used in different domains. Some of them are not usable for some spaces (for instance one cannot use a spherical basis in a bispherical space).

// Assume a spherical space is known and ndom its number of domains
Base_tensor basis_cart (space, CARTESIAN_BASIS) ;
// Constructor with the same tensorial basis everywhere

// Sets an orthonormal spherical basis in the first shell
Base_tensor basis_mixed (space, CARTESIAN_BASIS) ;
basis_mixed.set_basis(1) = SPHERICAL_BASIS ;
cout << "The mixed tensorial basis is " << endl ;
cout << basis_mixed << endl ;

Constructing a vector field

After the scalar fields, vectors are the most simple tensorial fields. They are constructed from a Space. One must specify if it is in a covariant representation (reserved word COV) or a contravariant one (reserved word CON). So it can describe both true vectors or 1-forms. The tensorial basis must also be specified.

// Construction of a vector
Vector vecU (space, CON, basis_cart) ;

Vector affectation and spectral basis.

The components of a vector are affected by the function set that returns a given component. Beware that the indices of the components range from 1 to the dimension (and so do not start at 0).

As for the scalar fields, the spectral basis must be provided. It depends on the space used, the tensorial basis and the component considered.

For instance, for a vector in Cartesian coordinates, the function std_base assumes than the vector is the gradient of some scalar field. It follows that the x and y components are symmetric with respect to the plane z=0 and the z one is anti-symmetric, thus resulting in different basis.

// Sets (x,y,z) in the nucleus :
for (int cmp=1 ; cmp<=3 ; cmp++)
    vecU.set(cmp).set_domain(0) = space.get_domain(0)->get_cart(cmp) ;

// Sets (x/r, y/r, z/r) for the other domains
for (int d=1 ; d<ndom ; d++)
    for (int cmp=1 ; cmp<=3 ; cmp++)
        vecU.set(cmp).set_domain(d) = space.get_domain(d)->get_cart_surr(cmp) ;

// Sets the appropriate spectral basis (here the standard one)
vecU.std_base() ;

// Prints the spectral basis in the nucleus
for (int cmp=1 ; cmp<=3 ; cmp++) {
    cout << "Basis of cmp " << cmp << " in the nucleus: "<< endl ;
    cout << vecU(cmp)(0).get_base() << endl ;
}

Changing the tensorial basis

In some cases it is possible to change the tensorial basis by invoking :

  • change_basis_spher_to_cart to go from the orthonormal spherical basis to the Cartesian one.
  • change_basis_cart_to_spher to do the reverse.

The spectral bases are also changed in a consistent manner.

// Change the tensorial basis
vecU.change_basis_cart_to_spher() ;

// Prints the new tensorial basis
cout << vecU.get_basis() << endl ;

// Check that the spectral basis has changed
cout << "Spectral basis of component 3 in the nucleus: " << endl ;
cout << vecU(3)(0).get_base() << endl ;

General tensors

What is true for vectors, remains essentially true for higher order tensors. The only complication is that the valence must be passed to the constructor along with the type of all the indices.

// Construct a rank-two tensor, both indices being contravariant
Tensor Tone (space, 2, CON, basis_cart) ;
cout << Tone << endl ; /// The values of the components are not defined at this point

// A rank 3 tensor with COV, CON, CON indices
int valence = 3 ;
Array<int> type_indices (valence) ;
type_indices.set(0) = COV ; 
type_indices.set(1) = CON ;
type_indices.set(2) = CON ;
Tensor Ttwo (space, valence, type_indices, basis_cart) ;

Accessors

For low rank tensors (i.e. below a valence 4), accessors work by giving the indices one after the other.

For higher ranks, one needs to use the class Index (see tutorial 1). Beware that in this case, the indices should go from 0 to dim-1 (as one is using the Array description with Index).

// Direct accessor
Ttwo.set(1,2,2) = 1. ; // Sets the component (x,y,y) to one

// Accessor using an Index
Index indices (Ttwo) ;
indices.set(0) = 2 ; indices.set(1) = 2 ; indices.set(2) = 0 ; // Corresponds to (z,z,x)
Ttwo.set(indices) = 2. ;

cout << Ttwo << endl ; //Only two components are defined.