From: David Wells Date: Sun, 2 Mar 2025 22:56:40 +0000 (-0500) Subject: Clean up and expand the orientation module. X-Git-Url: https://gitweb.dealii.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6147b12a84f4a28c2c39d716f73c1b9e3ba8177d;p=dealii.git Clean up and expand the orientation module. Part of #14667. --- diff --git a/doc/doxygen/headers/reordering.h b/doc/doxygen/headers/reordering.h index 50c538183a..64eb888ac3 100644 --- a/doc/doxygen/headers/reordering.h +++ b/doc/doxygen/headers/reordering.h @@ -20,37 +20,101 @@ * * @warning The implementation of orientation should be considered an internal * detail of the library. Normal users should not need to use the features - * describd in this group: instead, classes like QProjector use orientation + * described in this group: instead, classes like QProjector use orientation * information to consistently compute values on faces and lines. * + * The orientation of a line, triangle, or quadrilateral is a permutation of its + * vertices which does not result in a twisted cell: i.e., orientations are a + * subset of all possible vertex permutations. Orientations are encoded with + * types::geometric_orientation. + * + * A Triangulation contains both cells and also lower-dimensional objects, such + * as faces and vertices. While CellAccessor and TriaAccessor objects provide + * access to all the topological and geometric information of some entity (such + * as its neighbors, manifold ids, etc), these objects only store indices and a + * pointer to a Triangulation and look up the data they use from arrays managed + * by that Triangulation (typically in an + * internal::TriangulationImplementation::TriaLevel). This is an example of the + * flyweight + * pattern. For brevity, we typically write "the line stores" or "the cell + * stores" but the underlying implementation contains at least one level of + * indirection, as this data is stored in some way by the Triangulation and is + * indexed by TriaAccessorBase::index() and TriaAccessorBase::level(). + * + * In general, each geometric entity only stores the indices of the lower-level + * entities which bound it (e.g., a cell stores the indices of its faces, but in + * 3d must query the faces to get the indices of its lines). One exception to + * this rule is the vertex index cache: for performance reasons cells directly + * store their vertex indices. + * + * Each line and face has a unique index and exists exactly once in a + * Triangulation. Orientations are defined as the permutation which makes the + * vertices, defined in the context of the line or face, have the same order as + * the ones defined in the context of the cell. + * *

Orientation of Lines

* - * A Triangulation is built not just of cells but also of lower-dimensional - * objects. In particular, a line in 2D is a face which may be shared by two - * cells, whereas in 3D a line may be shared by an arbitrary number of cells. - * - * Lines (i.e., faces in 2D) are implicitly defined by the vertex numbering of - * the cell on which they are defined (i.e., there is no separate line or face - * object). This information is encoded in various ReferenceCell functions. For - * example, the first face of a triangle with nodes {0, 1, 2} is {0, 1}. - * Similarly, the first face of a triangle with nodes {1, 0, 3} is {1, 0}. By - * itself, this would be inconsistent, since the same line would be defined in - * twice: once by each of the two adjacent cells, and they might not agree - * whether the line should be {0, 1} or {1, 0}. To solve this problem deal.II - * also stores an unsigned char on each cell for each of the cell's - * faces; this value encodes the orientation of the line *as seen from this - * cell*. In this particular case the orientation of the {1, 0} line from the - * {0, 1, 2} cell is ReferenceCell::default_combined_face_orientation() whereas - * the orientation of the {0, 1} line from the {1, 0, 3} cell is - * ReferenceCell::reversed_combined_line_orientation(). + * In 1D, lines are cells and, since each line appears in the Triangulation + * exactly once, they do not store any orientation information. + * + * A line in 2D is a face which may be shared by two cells, whereas in 3D a line + * may be shared by an arbitrary number of cells. + * + * In 2D, each cell stores the indices of its bounding lines: i.e., each cell + * stores either 3 (for triangles) or 4 (for quadrilaterals) integers which + * enumerate those lines. Put another way: while there is no distinct `Line` + * class in deal.II, each line is uniquely identified by an index (accessed via + * CellAccessor::line_index()) and lines are, like other geometric entities, + * implemented with the flyweight pattern and represented by a TriaAccessor. + * Each line stores and is defined by the vertex indices which bound it and also + * stores other auxiliary information (such as boundary and manifold ids). + * + * In 3D, the line indices of a cell are stored by the faces which bound it: + * i.e., each cell stores its own face indices (which represent triangles or + * quadrilaterals) and each face stores its own line indices. Hence, when + * accessing a cell's lines, after identifying the face which owns that line + * (via ReferenceCell::standard_line_to_face_and_line_index()) all data lookups + * proceed in exactly the same way as the 2D case. + * + * Each line has both a unique index and a canonical vertex ordering. For + * example, consider two triangles with vertices `{15, 20, 25}` and `{20, 15, + * 3}`. The order of the vertices on each cell is defined by the CellData + * objects passed to Triangulation::create_triangulation(): typically, the order + * of the vertices is arbitrary aside from the constraint that they form a cell + * whose mapping to the reference cell has a positive Jacobian (see + * Triangulation::Triangulation() for more information on whether or not this + * should be checked). In this example, the first line of the first cell is + * `{15, 20}` whereas the first line of the second cell is `{20, 15}`. In + * deal.II, the canonical order of a line's vertices is set by the first cell + * with that line: i.e., line `0` will have vertices `{15, 20}` since it first + * appears in the first cell and those are the first two vertices of that cell. + * Similarly, line `1` is `{20, 25}` and line `2` is `{25, 15}`. This order + * (i.e., first and second vertex, then second and third, then third and first) + * is defined by ReferenceCell::line_to_cell_vertices() for each reference cell + * type. + * + * Canonicalization creates an inconsistency because the vertices of the first + * line on the second cell are reversed. To resolve this inconsistency, each 2D + * structure (either cells in 2D or faces in 3D) also stores the relative + * orientations of its bounding lines. In this example, for the first line, the + * first cell will store numbers::default_geometric_orientation and the second + * cell will store numbers::reverse_line_orientation. In each case this + * orientation value encodes the transformation necessary to make the canonical + * ordering match the cell-local ordering: i.e., the first cell does nothing and + * the second cell must invert the order. * *

Orientation of Faces

* - * In deal.II, we express the orientation of an object with three booleans: - * orientation, rotate, and flip. The default values for these are true, false, - * and false. These values are typically encoded or decoded from or to a single - * unsigned char by the internal::combined_face_orientation() and - * internal::split_face_orientation() functions. + * Unlike lines, which only have two possible orientations, a quadrilateral + * (i.e., a face of a pyramid, wedge, or hexahedron in 3D) has 8 possible + * orientations and a triangle (i.e., a face of a tetrahedron, pyramid, or wedge + * in 3D) has 6. In deal.II, we encode the orientation of a quadrilateral or + * triangle with three booleans: orientation, rotation, and flip. The default + * values for these are true, false, and false. These values are typically + * encoded or decoded from or to a single types::geometric_orientation value + * (whose exact binary representation is an internal library detail) by the + * internal::combined_face_orientation() and internal::split_face_orientation() + * functions. * * For a quadrilateral, these values correspond to * - *orientation* : `true` is the default orientation and `false` means @@ -73,16 +137,34 @@ * consider flip-rotate or flip-orient-rotate as those cases are equivalent, * respectively, to the identity operation or the orientation = `true` case as * flip-rotate is equal to the identity operation. As a consequence, there are - * only six valid orientations for triangles as faces of tetrahedra. + * only six valid orientations for triangles as faces of tetrahedra, pyramids, + * or wedges. + * + * Like the line case, the stored orientation value defines the way that the + * vertices of the face must be permuted to match the cell-local ordering. A + * consequence of this choice is that QProjector uses the inverse orientation + * (via ReferenceCell::get_inverse_combined_orientation()) to compute the + * locations of quadrature points, since exactly one of the following + * possibilities must happen: + * + * 1. If we are on the face which defines the canonical ordering of the face + * vertices then that face's orientation must be + * numbers::default_geometric_orientation, whose inverse is itself (as it is + * the identity permutation). Hence, in this case, applying the permutation + * to the positions of the vertices will not change the positions of the + * quadrature points. + * + * 2. If we are on the neighbor's face then, to make vertices match, we must + * transform the cell-local vertices so that they match the first cell's + * vertex ordering: i.e., the inverse orientation. * *

Orientation of Quadrilateral Meshes

* * Purely quadrilateral meshes are a special case, since deal.II will (with the * exception of faces which are neighbors across periodic boundaries) * consistently orient purely quadrilateral meshes. Hence, in this case, the - * orientation of all lines will be - * ReferenceCell::default_combined_face_orientation(). See @cite AABB17 for more - * information on this algorithm. + * orientation of all lines will be numbers::default_geometric_orientation. See + * @cite AABB17 for more information on this algorithm. * * For example, in two dimensions, a quad consists of four lines which have a * direction, which is by definition as follows: