Skip to content

Draft: Add geometry mapped by an element-function

Simon Praetorius requested to merge feature/mapped-geometry into master

Summary

A geometry that is defined as the mapping of a reference geometry

Details

A geometry can be seen as a class interface representing a reference domain R plus a mapping f, such that f(R) is of the same topology as R and f a bijective (diffeomorphic) mapping between R and f(R). The MappedGeometry provides this interface for a given ReferenceElement R and mapping f.

Additionally, the MappedGeometry allows to be defined on a different reference domain than associated to the mapping f. This could be, for example, the domain of a subEntity or element-intersection. In this case, we build a composition of two mappings f and g, where g represents a local-geometry transform between the two reference domains. Let I be the reference domain of the subEntity and g the associated geometry mapping (that is bijective between I and g(I)), then the MappedGeometry represents the composition f o g associated to the reference domain I.

We require for the local function f to fulfill some interface to be allowed in this wrapper class. Let lf be an object of local function, x be a local coordinate in the reference element of the geometry, then (formulated in c++-20 concepts)

template <class LF, class Range, class Domain, class LocalContext,
  class DLF = std::decay_t<decltype(derivative(std::declval<const LF&>())> >
concept LocalFunction = require (const LF& lf, const Domain& x)
{
  { lf(x) }             -> std::convertible_to<Range>;
  { lf.localContext() } -> std::convertible_to<LocalContext>;
  { derivative(lf) }    -> std::regular_invocable<Domain>;

  require (const DLF& dlf, const Domain& x)
  {
    { dlf(x) }          -> std::convertible_to<typename DerivativeTraits<Range(Domain)>::Range>;
  };
};

In this concept we make explicit notion of a "LocalContext" the local-function is associated to. This context refers to an entity or intersection the local-function was bound. This means that the local-function can be evaluated in the local-coordinates of that LocalContext. We thus require, that the local-context provides also a geometry.

Example 1

This example uses dune-functions, just to make the example short. One can easily define a local function following the test-mappedgeometry.cc

// construct a base grid e.g. with piecewise flat elements
YaspGrid<2> grid({1.0,1.0}, {10,10});

using Domain = FieldVector<double,2>;
using Range = FieldVector<double,3>;

auto sig = Functions::SignatureTag<Range(Domain)>{};
auto f = Functions::makeDifferentiableFunctionFromCallables(sig, 
  [](const Domain& x) -> FieldVector<double,3> {
    return {x[0], x[1], std::sin(x[0] * x[1])};
  },
  [](const Domain& x) -> FieldMatrix<double,3,2> {
    return {
      {1.0, 0.0},
      {0.0, 1.0},
      {x[1] * std::cos(x[0] * x[1]), x[0] * std::cos(x[0] * x[1])}
    };
  });

// create a differentiable analytic grid function
auto fGridFunction = Functions::makeAnalyticGridViewFunction(f, grid.leafGridView());

// build the corresponding local function
auto fLocalFunction = localFunction(fGridFunction);

for (const auto& e : elements(grid.leafGridView()))
{
  // bind the local function to the grid element
  fLocalFunction.bind(e);

  // construct the mapped geometry
  MappedGeometry geometry{referenceElement(e), fLocalFunction};

  // unbind the local function from the element
  fLocalFunction.unbind();
}

Example 2

In this example also intersection geometries are transformed. Since the local function must be bound to a grid element, we need an additional geometry transformation from the intersection geometry to the element geometry. The local-geometry transform is passed as additional argument. It can also be used to define a geometry on sub-entities of the grid element, by using a reference sub-element geometry mapping.

Let grid, f, fGridFunction, fLocalFunction be defined as in Example 1.

for (const auto& e : elements(grid.leafGridView()))
{
  // bind the local function to the grid element
  fLocalFunction.bind(e);

  for (const auto& is : intersections(element, grid.leafGridView())
  {
    // construct the mapped geometry for the intersection
    MappedGeometry isGeometry{referenceElement(is), fLocalFunction, is.geometryInInside()};
  }

  auto refElem = referenceElement(e);
  for (int i = 0; i < refElem.size(1); ++i) 
  {
    auto edgeRefElem = referenceElement<double,grid.dimension-1>(refElem.type(i,1));

    // construct a mapped geometry for the i'th subentity of codim 1
    // NOTE: this required a well-oriented (twist-free) grid
    MappedGeometry edgeGeometry{edgeRefElem, fLocalFunction, refElem.geometry<1>(i)};
  }

  // unbind the local function from the element
  fLocalFunction.unbind();
}
Edited by Simon Praetorius

Merge request reports