Discontinuous Galerkin Library
#include "dg/algorithm.h"
Collaboration diagram for The tag dispatch system:

Modules

 All TensorTraits specialisations
 

Classes

struct  dg::AnyPolicyTag
 Execution Policy base class. More...
 
struct  dg::NoPolicyTag
 Indicate that a type does not have an execution policy. More...
 
struct  dg::AnyMatrixTag
 tensor_category base class More...
 
struct  dg::NotATensorTag
 Indicate that a type is not a tensor. More...
 

Detailed Description

The dg dispatch system

At the heart of the dispatch system lies the dg::TensorTraits class that specifies how a type T behaves in the respective functions though a tag system defined by the tensor_category tag and the execution_policy tag. Per default it looks like

template< class T, class Enable=void>
{
using value_type = void;
};
Indicate that a type does not have an execution policy.
Definition: execution_policy.h:20
Indicate that a type is not a tensor.
Definition: matrix_categories.h:13
The vector traits.
Definition: tensor_traits.h:31
NoPolicyTag execution_policy
Definition: tensor_traits.h:34
NotATensorTag tensor_category
Definition: tensor_traits.h:33
void value_type
Definition: tensor_traits.h:32

The values for tensor_category are either dg::NotATensorTag or (a class derived from) dg::AnyMatrixTag, while the values for execution_policy are either dg::NoPolicyTag or (a class derived from) dg::AnyPolicyTag. Any type T assumes the above default values for value_type, tensor_category and execution_policy unless a specialisation exists for that type (see All TensorTraits specialisations for a full list). We define the following nomenclature

When dispatching level 1 functions we distinguish between three classes of functions: trivially parallel (dg::blas1::subroutine and related dg::blas1 functions), global communication (dg::blas1::dot and dg::blas2::dot) and local communication (dg::blas2::symv).

The subroutine function

The execution of dg::blas1::subroutine with a Functor called routine (and all related dg::blas1 functions) is equivalent to the following:

  1. Assert the following prerequisites:
    1. All template parameter types must be either Scalars or Vectors and have an execution policy and a value type
    2. All Vectors and all execution policies must be mutually compatible
    3. All Vectors must contain the same number of elements (equal sizes)
    4. The number of template parameters must be equal to the number of parameters in the routine
    5. The value type of every template parameter must be convertible to the respective parameter type in the routine
  2. If all types are Scalars, apply the routine and return
  3. If at least one type is a Vector, then all Scalars are promoted to this type with the same size, communicator and execution policy
  4. Check the base class of the tensor_category:
    1. If dg::SharedVectorTag, check execution policy and dispatch to respective implementation (The implementation just loops over all elements in the vectors and applies the routine)
    2. If dg::MPIVectorTag, access the underlying data and recursively call dg::blas1::subroutine (and start again at 1)
    3. If dg::RecursiveVectorTag, loop over all elements and recursively call dg::blas1::subroutine for all elements (and start again at 1)

The dot function

The execution of dg::blas1::dot and dg::blas2::dot is equivalent to the following:

  1. Assert the following prerequisites:
    1. All template parameter types must be either Scalars or Vectors and have an execution policy and a value type
    2. All Vectors and all execution policies must be mutually compatible
    3. All Vectors must contain the same number of elements (equal sizes)
  2. If all types are Scalars, multiply and return
  3. If at least one type is a Vector, then all Scalars are promoted to this type with the same size, communicator and execution policy
  4. Check the base class of the tensor_category:
    1. If dg::SharedVectorTag, check execution policy and dispatch to respective implementation (The implementation multiplies and accumulates all elements in the vectors). Return the result.
    2. If dg::MPIVectorTag, assert that the vector MPI-communicators are congruent (same process group, different context) or ident (same process group, same context). Then, access the underlying data and recursively call dot (and start again at 1). Accumulate the result among participating processes and return.
    3. If dg::RecursiveVectorTag, loop over all elements and recursively call dot for all elements (and start again at 1). Accumulate the results and return.

The symv function

In the execution of the dg::blas2::symv (and its aliases dg::blas2::gemv and dg::apply) each matrix type has individual prerequisites and execution paths depending on its tensor_category. We can identify some general rules:

  1. If the tensor category is dg::NotATensorTag (any type has this tag by default unless it is overwritten by a specialization of dg::TensorTraits ) then the compiler assumes that the type is a functor type and immediately forwards all parameters to the operator() member and none of the following applies
  2. If the Matrix type has the dg::SelfMadeMatrixTag tensor category, then all parameters are immediately forwarded to the symv member function. No asserts are performed and none of the following applies.
  3. If the Matrix type is either a Scalar or a Vector and the remaining types do not have the dg::RecursiveVectorTag tensor category, then dg::blas2::symv is equivalent to dg::blas1::pointwiseDot
  4. The container template parameters must be Vectors or Scalars and must have compatible execution policies. Vectors must be compatible.
  5. If the tensor category of the Vectors is dg::RecursiveVectorTag and the tensor category of the Matrix is not, then the dg::blas2::symv is recursively called with the Matrix on all elements of the Vectors.