My DiffOpΒΆ
DiffOps are the static polymorphism classes implementing Operators for GridFunctions and Test- and TrialFunctions. They only have static member functions and are never created but only used as a template argument for DifferentialOperators. They use CRTP for "static virtual" methods. DifferentialOperator is the runtime polymorphism class wrapping them. Use T_DifferentialOperator<DiffOp> as a coupling mechanism.
We will need the identity and the gradient operator. These operators have to define the following constexpr values:
DIM |
Dimension of input |
DIM_SPACE |
Dimension of FESpace |
DIM_ELEMENT |
Dimension of Element |
DIM_DMAT |
Dimension of range |
DIFFORDER |
Order of differentiation |
The order of differentiation is used to choose the correct integration rules for integrating the polynomials. Further we need to implement a function that generates the result of the Diffop for one mapped integration point. This function gets a matrix because the result could be matrix valued. A minimal implementation of the identity would be this:
class MyDiffOpId : public DiffOp<MyDiffOpId>
{
public:
// ******************** Necessary stuff *************************
static constexpr int DIM = 1; // dimension of the input
static constexpr int DIM_SPACE = 2; // dimension of the space
static constexpr int DIM_ELEMENT = 2; // dimension of the element
static constexpr int DIM_DMAT = 1; // dimension of the output
static constexpr int DIFFORDER = 0; // order of differentiation
template<typename MIP, typename MAT>
static void GenerateMatrix(const FiniteElement& fel, const MIP& mip,
MAT& mat, LocalHeap& lh)
{
HeapReset hr(lh);
Cast(fel).CalcShape(mip.IP(), mat.Row(0));
}
// ******************** Helper function ***************************
static const MyBaseElement& Cast(const FiniteElement& fel)
{ return static_cast<const MyBaseElement&> (fel); }
};
}
Again some more functions can be implemented to improve performance, for example evaluation in the whole integration rule and SIMD implementations.
The gradient operator looks similar:
class MyDiffOpGradient : public DiffOp<MyDiffOpGradient>
{
public:
// ******************** Necessary stuff *************************
static constexpr int DIM = 1; // dimension of the input
static constexpr int DIM_SPACE = 2; // dimension of the space
static constexpr int DIM_ELEMENT = 2; // dimension of the element
static constexpr int DIM_DMAT = 2; // dimension of the output
static constexpr int DIFFORDER = 1; // order of differentiation
// so that you can call grad(u)
static string Name() { return "grad"; }
template<typename MIP, typename MAT>
static void GenerateMatrix(const FiniteElement& fel, const MIP& mip,
MAT& mat, LocalHeap& lh)
{
// We need to compute the gradient on the reference element and
// then transform it to the using the jacobian of the mapped
// integration point
HeapReset hr(lh);
// create a temporary matrix on the local heap
FlatMatrixFixWidth<2> dshape(fel.GetNDof(), lh);
Cast(fel).CalcDShape(mip.IP(), dshape);
mat = Trans(dshape * mip.GetJacobianInverse());
}
// ******************** Helper function ***************************
static const MyBaseElement& Cast(const FiniteElement& fel)
{ return static_cast<const MyBaseElement&> (fel); }
};
}
Again the implementation of a boundary evaluation operator will be left as an exercise.
To make the DiffOp available at runtime we have to put it into a T_DifferentialOperator class as template argument. This happens next in the FESpace implementation.