Function Spaces

Function spaces support is provided by the FunctionSpace class

The FunctionSpace class

  • constructs the table of degrees of freedom which maps local (elementwise) degrees of freedom to the global ones with respect to the geometrical entities,

  • embeds the definition of the elements of the function space allowing for a tight coupling between the elements and their function spaces,

  • stores an interpolation data structure (e.g. region tree) for rapid localisation of point sets (determining in which element they reside).

C++ Function

C++ Type

Function Space [1]

Pch<N>(mesh)

Pch_type<MeshType,N>

\(P^N_{c,h}\)

Pchv<N>(mesh)

Pchv_type<MeshType,N>

\([P^N_{c,h}\)^d]

Pdh<N>(mesh)

Pdh_type<MeshType,N>

\(P^N_{d,h}\)

Pdhv<N>(mesh)

Pdhv_type<MeshType,N>

\([P^N_{d,h}\)^d]

THch<N>(mesh)

THch_type<MeshType,N>

\([P^{N+1}_{c,h}\)^d \times P^N_{c,h}]

Dh<N>(mesh)

Dh_type<MeshType,N>

\(\mathbb{R}\mathbb{T}_h\)

Ned1h<N>(mesh)

Ned1h_type<MeshType,N>

\(\mathbb{N}_h\)

[[[1]]]: see Notations for the function spaces definitions.

Here are some examples how to define function spaces with Lagrange basis functions.

#include <feel/feeldiscr/pch.hpp>

// Mesh with triangles
using MeshType = Mesh<Simplex<2>>;
// Space spanned by P3 Lagrange finite element
FunctionSpace<MeshType,bases<Lagrange<3>>> Xh;
// is equivalent to (they are the same type)
Pch_type<MeshType,3> Xh;

// using the auto keyword
MeshType mesh = loadMesh( _mesh=new MeshType );
auto Xh = Pch<3>( mesh );
// is equivalent to
auto Xh = FunctionSpace<MeshType,bases<Lagrange<3>>>::New( mesh );
auto Xh = Pch_type<MeshType,3>::New( mesh );

1. Functions

One important feature in FunctionSpace is that it embeds the definition of element which allows for the strict definition of an Element of a FunctionSpace and thus ensures the correctness of the code.

An element has its representation as a vector, also in the case of product of multiple spaces.

#include <feel/feeldiscr/pch.hpp>

// Mesh with triangles
using MeshType = Mesh<Simplex<2>>;
auto mesh = loadMesh( _mesh=new MeshType );

// define P3 Lagrange finite element space
auto P3ch = Pch<3>(mesh);

// definie an element from P3ch, initialized to 0
auto u = P3ch.element();
// definie an element from P3ch, initialized to x^2+y^2
auto v = P3ch.element(Px()*Px()+Py()*Py());

1.1. Components

Components of vectorial or matrix function spaces can be accessed and manipulated as functions.

Components of a function space
// FunctionSpace<Mesh<Simplex<2> >, bases<Lagrange<2,Vectorial>>>
auto Xh = Pchv<2>(mesh);
auto U=Xh->element();
auto ux = u.comp(X);
auto uy = u.comp(Y);
the components of a vectorial or matrix function space are interleaved.

2. Product of function spaces

Products of function spaces are supported. This is very powerful to describe complex multiphysics problems when coupled with operators, functionals and forms described in the next sections. Extracting subspaces or component spaces are part of the interface of product of function spaces.

There are two ways to manipulate product of function spaces:

  • embed the product in the FunctionSpace class

  • define a product of function spaces which is not itself a FunctionSpace

2.1. Embed the product

To embed the product into the FunctionSpace, we use bases<> to collect the basis function that span the component spaces. We can pass an arbitrary number of function spaces at compile time using variadic templates. The resulting function space is called a composite function space.

Define a composite FunctionSpace
using P2P1P1_t = FunctionSpace<Mesh<Simplex<2> >, (1)
 bases<Lagrange<2,Vectorial>,    (2)
       Lagrange<1,Scalar>,       (3)
       Lagrange<1,Scalar>        (4)
      >
>;
auto Xh = P2P1P1_t::New( mesh );
1 define the support of the composite function space and its components
2 the first component space is spanned by Lagrange<2,Vectorial>
3 the second component space is spanned by Lagrange<1,Scalar>
4 the third component space is spanned by Lagrange<1,Scalar>
in order to identify the function spaces in the preceding example, Feel++ adds a tag to the resulting function space to identify it uniquely as well as its functions.

A composite function space has no explicit DOF table as it corresponds to the collection of the DOF tables of the component spaces shifted by the index of the component space.

The component spaces and elements of a composite function space can be accessed using respectively

  • functionSpace<index>() method where index give the index of the space starting from 0

  • element<index>() where index give the index of the space starting from 0

Example of composite space illustrating functionSpace<> and element<>
using P2P1P1_t = FunctionSpace<Mesh<Simplex<2> >,
 bases<Lagrange<2,Vectorial>, Lagrange<1,Scalar>,
       Lagrange<1,Scalar> > > ;
auto Xh = P2P1P1_t::New( mesh );
auto Xh1 = Xh->functionSpace<0>();
auto Xh2 = Xh->functionSpace<1>();
auto Xh3 = Xh->functionSpace<2>();
auto U = Xh->element();
// Views: changing a view changes U and vice versa
// view on element associated to P2
auto u = U.element<0>();
// extract view of first component
auto ux = u.comp(X);
// view on element associated to 1st P1
auto p = U.element<1>();
// view on element associated to 2nd P1
auto q = U.element<2>();

2.2. Product of Function Spaces

There are two types of products:

  • compile time: the number of function spaces is known at compile time and the type of the function spaces can be different

  • runtime: the number of function spaces is dynamic, the type must the same but the support of the spaces may be different

In both cases, the data structures allow to

  • iterate over the underlying space

  • define elements of the product and access its components

Both forms of the product can be used by blockform. It allows to define bilinear and linear over these products and manipulate the corresponding blocks either at compile time or run time.

2.2.1. Runtime product

The runtime product of function spaces allows to create a dynamic product of spaces of the same type but not necessarily of the same support. if the mesh support is the same for all components then the doftable is built only for one component, the others are obtained by shifting the first doftable.

Example of dynamic product of function space
#include <feel/feeldiscr/product.hpp >
// ...
// dynamic function space
auto Wh = dynProduct<decltype(Pch<1>( mesh ))>( 10, mesh ); (1)
auto Vh = Pch<1>( mesh ); (2)
auto Xh = dynProduct( 10, Vh ); (3)
auto U = Xh.element(); (4)
auto u = U[0]; (5)
1 define a product of 10 function space with support mesh
2 defines a P1 piecewise continuous function space
3 defines a product of 10 function space based on Vh.
4 create an element of the product of function space
5 create a view on the first component of the function space
there is also a shared_ptr<> version, dynProductPtr, which returns a shared pointer product of function space.

2.2.2. Compile time product

// define mesh
auto Xh=Pch<1>(mesh);
auto Wh=Pchv<2>(mesh);
auto Zh = productPtr(Xh,Wh); (1)
auto U = Zh->element(); (2)
auto cXh = Zh->functionSpace(0_c);
auto cWh = Zh->functionSpace(1_c);
auto ex = exporter(_mesh=mesh);
ex->add("u",U(0_c)); (3)
ex->add("v",U(1_c)); (4)
ex->save();
1 create the compile time product Zh of Xh and Wh
2 create an element of Zh
3 export the first component, element of

2.2.3. Mixing Runtime and Compile time product

It is possible to mix compile time and one runtime product of function spaces provided that the dynamic function space is

auto Xh = Pch<1>(mesh);
auto Zh = Pch<1>(mesh);
auto Yh = Pchv<3>(mesh);
auto Vh = dynProductPtr<decltype(Pch<2>(mesh))>( n, mesh );
auto p = product2( Xh, Yh, Zh, Vh );

cout << "number of spaces " << p.numberOfSpaces() << std::endl;
auto U = p.element();
auto u = U(0_c); (1)
auto y = U(1_c); (2)
auto z = U(2_c); (3)
for( int i = 0; i < Vh->numberOfSpaces(); ++i )
  auto v = U( 3_c, i ); (4)
1 view on component 0, element of Xh
2 view on component 1, element of Yh
3 view on component 2, element of Zh
4 view on component 3, i-th element of Vh