Add ReferenceElement::subEntities() method returning a range
For a reference element re the call
re.subEntity(i,codim,j,c);
returns the index (wrt re) of the j-th sub-subentity of codim (wrt re) c of the subentity (i,codim). This allows e.g. to access all vertices of a face. However, there's a big problem with this method:
It is often misinterpreted by users that the passed index j is
the index of one grid-subentity within another grid-subentity.
To be more precise it is assumed that the subEntity() methods
of the reference element and a grid element are consistent with
each other. Unfortunately this is in general not true because
it is impossible to construct such an consistent subentity
embedding for any grid. As a consequence both may provide a
different embedding and the index j is essentially useless
despite allowing to iterate over all such subentities using
// Don't use j directly in such a loop, it's very likely that
// your assumptions on the meaning of j don't hold true!
for(std::size_t j=0; j<re.size(i,codim,c); ++j)
foo(re.subEntity(i,codim,j,c));
While the behaviour is documented, users repeatedly fall into this trap and the issue is discussed on the list once in a while.
As a remedy this introduces a new method ReferenceElement::subEntities()
returning all the subentities as an iterable range. The actual type of this
range is implementation defined. Using this the above loop turns into:
// No j here!
for(auto k : re.subEntities(i,codim,c))
foo(k);
This is not only more readable, but it especially avoids misinterpreting
the 'index' j. You can still run into the trap on purpose by assuming a
special ordering of the range. But this is very unlikely and you have to take
special action to construct the index j.
This MR is based on !93 (merged) (contains) because it consistently tests for all codim/c
combinations. But one could also restrict the allowed cases to c>=codim here.
In a later stage we may want to deprecate the error prone subEntity() method.
Side note: Thanks to the implementer (@martin.nolte?) of the reference elements for
caching those numbers in a consecutive way. Implementing this feature just amounts
into wrapping pointers into the cache in a Dune::IteratorRange. However, I'd even more like to put them into gsl:span.