LehrFEM++ 1.0.0
A simple Finite Element Library for teaching
Loading...
Searching...
No Matches
Quick Reference - Degrees of Freedom (DOF) Handlers and Indexing

Edit on GitHub

Attention
The contents of this page is discussed in Lecture Document Section 2.8. Please read this section before using the quick reference.

Overview

Local computations are essential for efficient finite element methods (FEM). LehrFEM++ uses a local indexing scheme to map local shape functions to global degrees of freedom (DOFs). In LehrFEM++, this is done through objects of the type lf::assemble::DofHandler. This quick reference provides an overview of DOF handlers and indexing conventions in LehrFEM++. The assembly quick reference page provides more information on (local) assembly using DOF handlers.

Local to Global Index Mapping

The local shape functions of every cell (co-dimension-0 entity) are arranged according to the increasing dimension of the geometric entities they are associated with:

  1. Points (Vertices)
  2. Edges
  3. Cells (Triangles or Quadrilaterals)

DOFs associated with lower-dimensional entities (e.g., points and edges) are numbered first, followed by higher-dimensional ones (cells). Within each category, entities are numbered according to their intrinsic indexing as returned by the Index() function. This is also illustrated in Lecture Document Figure 140 and Lecture Document Figure 141.

DOF Handlers in LehrFEM++

LehrFEM++ provides two implementations of DOF handlers:

  1. lf::assemble::DynamicFEDofHandler: Allows for a variable number of local DOFs for each entity (e.g. for hp-FEM, see Lecture Document Paragraph 2.7.4.16)
  2. lf::assemble::UniformFEDofHandler: Assigns the same number of DOFs to each entity of a given type (uniform FE space).

DynamicFEDofHandler

A DynamicFEDofHandler can be initialized with a function that returns the number of DOFs associated with a given entity.

const std::shared_ptr<const lf::mesh::Mesh> mesh_p =
auto func = [](const lf::mesh::Entity& e) {
// return number of dofs associated with e
return 1;
};

UniformFEDofHandler

// Generate a simple test mesh
const std::shared_ptr<const lf::mesh::Mesh> mesh_p =
// Initialize a DofHandler for p=2 Lagrange FE

For example, in a second-order Lagrange FE space (as shown in the code above):

  • Each point carries 1 DOF.
  • Each edge carries 1 DOF.
  • Each triangle has 0 DOFs.
  • Each quadrilateral has 1 DOF.

Global Indexing convention

LehrFEM++ follows the convention of numbering global DOFs according to the following rule:

  1. DOFs associated with lower-dimensional entities are numbered first:

    \[ \text{POINT} \rightarrow \text{SEGMENT} \rightarrow \text{TRIA/QUAD} \]

  2. The indices of DOFs belonging to entities of the same co-dimension increase with increasing entity indices as returned by the Index() function.

Key Methods

The key methods provided by the DofHandler interface are:

unsigned num_dofs = dofh.NumDofs();
const lf::mesh::Entity* e = mesh_p->EntityByIndex(0, 0);
unsigned num_local_dofs = dofh.NumLocalDofs(*e);
const lf::mesh::Entity* e = mesh_p->EntityByIndex(0, 0);
std::span<const lf::assemble::gdof_idx_t> idx = dofh.GlobalDofIndices(*e);
for (auto i : idx) {
std::cout << i << " ";
} // -> prints the global indices of DOFs associated with the entity
const lf::mesh::Entity* e = mesh_p->EntityByIndex(0, 0);
unsigned num_int_dofs = dofh.NumInteriorDofs(*e);

Example Code

An example of using a DofHandler to print information about local-to-global mappings in LehrFEM++:

void printDofInfo(const lf::assemble::DofHandler &dofh) {
// Obtain pointer to the underlying mesh
const lf::mesh::Mesh &mesh{*dofh.Mesh()};
// Number of degrees of freedom managed by the DofHandler object
const lf::assemble::size_type N_dofs(dofh.NumDofs());
// Output information about dofs for entities of all co-dimensions
for (lf::base::dim_t codim = 0; codim <= mesh.DimMesh(); codim++) {
// Visit all entities of a codimension codim
for (const lf::mesh::Entity *e : mesh.Entities(codim)) {
// Fetch unique index of current entity supplied by mesh object
const lf::base::glb_idx_t e_idx = mesh.Index(*e);
// Number of shape functions covering current entity
const lf::assemble::size_type no_dofs(dofh.NumLocalDofs(*e));
// Obtain global indices of those shape functions ...
std::span<const lf::assemble::gdof_idx_t> dofarray{
dofh.GlobalDofIndices(*e)};
// and print them
std::cout << *e << ' ' << e_idx << ": " << no_dofs << " dofs = [";
for (int loc_dof_idx = 0; loc_dof_idx < no_dofs; ++loc_dof_idx) {
std::cout << dofarray[loc_dof_idx] << ' ';
}
std::cout << ']';
// Also output indices of interior shape functions
std::span<const lf::assemble::gdof_idx_t> intdofarray{
std::cout << " int = [";
for (lf::assemble::gdof_idx_t int_dof : intdofarray) {
std::cout << int_dof << ' ';
}
std::cout << ']' << std::endl;
}
}
// List entities associated with the dofs managed by the current
// DofHandler object
for (lf::assemble::gdof_idx_t dof_idx = 0; dof_idx < N_dofs; dof_idx++) {
const lf::mesh::Entity &e(dofh.Entity(dof_idx));
std::cout << "dof " << dof_idx << " -> " << e << " " << mesh.Index(e)
<< std::endl;
}
} // end function printDofInfo