/*
This file is part of t8code.
t8code is a C library to manage a collection (a forest) of multiple
connected adaptive space-trees of general element classes in parallel.
Copyright (C) 2024 the developers
t8code is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
t8code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with t8code; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file t8_scheme.hxx
* This file defines the interface of eclass scheme implementations. Further, it defines the t8_scheme class, which
* holds one or more element schemes. It also relays the function calls to the specific schemes.
*/
#ifndef T8_SCHEME_HXX
#define T8_SCHEME_HXX
#include <variant>
#include <vector>
#include <t8_refcount.h>
#include <t8_eclass.h>
#include <t8_schemes/t8_default/t8_default.hxx>
#include <t8_schemes/t8_default/t8_default_vertex/t8_default_vertex.hxx>
#include <t8_schemes/t8_default/t8_default_line/t8_default_line.hxx>
#include <t8_schemes/t8_default/t8_default_quad/t8_default_quad.hxx>
#include <t8_schemes/t8_default/t8_default_hex/t8_default_hex.hxx>
#include <t8_schemes/t8_default/t8_default_tri/t8_default_tri.hxx>
#include <t8_schemes/t8_default/t8_default_tet/t8_default_tet.hxx>
#include <t8_schemes/t8_default/t8_default_prism/t8_default_prism.hxx>
#include <t8_schemes/t8_default/t8_default_pyramid/t8_default_pyramid.hxx>
#include <t8_schemes/t8_standalone/t8_standalone.hxx>
#include <t8_schemes/t8_standalone/t8_standalone_implementation.hxx>
#include <string>
#if T8_ENABLE_DEBUG
// Only needed for t8_debug_print_type
#include <typeinfo>
/**
* Get the type of the template parameter as a string.
* \tparam TType The template parameter to get the type of.
* \return std::string& The type of the template parameter as a string.
*/
template <typename TType>
inline std::string &
t8_debug_print_type ()
{
static std::string type_name = typeid (TType).name ();
return type_name;
}
#endif // T8_ENABLE_DEBUG
/** This class holds one or more element schemes.
* It also relays the function calls to the specific schemes. */
struct t8_scheme
{
friend struct t8_scheme_builder;
public:
t8_scheme ()
{
t8_refcount_init (&rc);
};
~t8_scheme ()
{
if (sc_refcount_is_active (&rc)) {
T8_ASSERT (t8_refcount_is_last (&rc));
t8_refcount_unref (&rc);
}
t8_debugf ("Deleted the scheme.\n");
};
/* clang-format off */
/** Variant to hold an eclass scheme. */
using scheme_var = std::variant<
/* Default schemes */
t8_default_scheme_vertex,
t8_default_scheme_line,
t8_default_scheme_quad,
t8_default_scheme_tri,
t8_default_scheme_hex,
t8_default_scheme_tet,
t8_default_scheme_prism,
t8_default_scheme_pyramid,
t8_standalone_scheme<T8_ECLASS_VERTEX>,
t8_standalone_scheme<T8_ECLASS_LINE>,
t8_standalone_scheme<T8_ECLASS_QUAD>,
t8_standalone_scheme<T8_ECLASS_HEX>
>;
/* clang-format on */
using scheme_container = std::vector<scheme_var>; /**< Container type for holding eclass schemes. */
private:
scheme_container eclass_schemes; /**< The container holding the eclass schemes. */
mutable t8_refcount_t
rc; /**< The reference count of the scheme. Mutable so that the class can be const and the ref counter is still mutable. TODO: Replace by shared_ptr when forest becomes a class. */
public:
/**
* Increase the reference count of the scheme.
*/
inline void
ref () const
{
t8_refcount_ref (&rc);
}
/**
* Decrease the reference count of the scheme.
* If the reference count reaches zero, the scheme is deleted.
* \return The remaining reference count. If 0 the scheme was deleted.
*/
inline int
unref () const
{
const int remaining = rc.refcount - 1;
if (t8_refcount_unref (&rc)) {
t8_debugf ("Deleting the scheme.\n");
delete this;
}
return remaining;
}
/** Get the number of eclass schemes inside the scheme.
* \return The number of eclass schemes.
*/
inline size_t
get_num_eclass_schemes () const
{
return eclass_schemes.size ();
}
/** Check if the scheme is of a specific type.
* \tparam TEclass_Scheme The type of the scheme to check for.
* \param [in] tree_class The eclass of the current tree.
* \return True if the scheme is of type \a TEclassScheme, false otherwise.
*/
template <class TEclassScheme>
inline bool
check_eclass_scheme_type (const t8_eclass_t tree_class) const
{
return std::holds_alternative<TEclassScheme> (eclass_schemes[tree_class]);
}
/** Get the eclass an eclass scheme is valid for.
* \param [in] tree_class The eclass of the current tree.
* \return The valid tree class for the eclass scheme.
* \note This function should return the input value as long as the
* eclass schemes are sorted correctly. In the future, the trees will access the schemes by a key and then this
* function will make more sense.
*/
inline t8_eclass_t
get_eclass_scheme_eclass (const t8_eclass_t tree_class) const
{
return std::visit ([&] (auto &&scheme) { return scheme.get_eclass (); }, eclass_schemes[tree_class]);
}
/** Get the dimension of the eclass scheme.
* \param [in] tree_class The eclass of the current tree.
* \return The dimension of the eclass scheme.
*/
inline size_t
get_eclass_scheme_dimension (const t8_eclass_t tree_class) const
{
return std::visit ([&] (auto &&scheme) { return scheme.get_dimension (); }, eclass_schemes[tree_class]);
}
/** Return the size of any element of a given class.
* \param [in] tree_class The eclass of the current tree.
* \return The size of an element of class \a tree_class.
* We provide a default implementation of this routine that should suffice
* for most use cases.
*/
inline size_t
get_element_size (const t8_eclass_t tree_class) const
{
return std::visit ([&] (auto &&scheme) { return scheme.get_element_size (); }, eclass_schemes[tree_class]);
};
/** Returns true, if there is one element in the tree, that does not refine into 2^dim children.
* Returns false otherwise.
* \param [in] tree_class The eclass of the current tree.
* \return true if there is one element in the tree that does not refine into 2^dim children.
*/
inline bool
refines_irregular (const t8_eclass_t tree_class) const
{
return std::visit ([&] (auto &&scheme) { return scheme.refines_irregular (); }, eclass_schemes[tree_class]);
};
/** Return the maximum allowed level for any element of a given class.
* \param [in] tree_class The eclass of the current tree.
* \return The maximum allowed level for elements of class \a tree_class.
*/
inline int
get_maxlevel (const t8_eclass_t tree_class) const
{
return std::visit ([&] (auto &&scheme) { return scheme.get_maxlevel (); }, eclass_schemes[tree_class]);
};
/** Return the level of a particular element.
* \param [in] tree_class The eclass of the current tree.
* \param [in] element The element whose level should be returned.
* \return The level of \a element.
*/
inline int
element_get_level (const t8_eclass_t tree_class, const t8_element_t *element) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_get_level (element); }, eclass_schemes[tree_class]);
};
/** Copy all entries of \a source to \a dest. \a dest must be an existing
* element. No memory is allocated by this function.
* \param [in] tree_class The eclass of the current tree.
* \param [in] source The element whose entries will be copied to \a dest.
* \param [in,out] dest This element's entries will be overwritten with the
* entries of \a source.
* \note \a source and \a dest may point to the same element.
*/
inline void
element_copy (const t8_eclass_t tree_class, const t8_element_t *source, t8_element_t *dest) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_copy (source, dest); }, eclass_schemes[tree_class]);
};
/** Compare two elements.
* \param [in] tree_class The eclass of the current tree.
* \param [in] elem1 The first element.
* \param [in] elem2 The second element.
* \return negative if elem1 < elem2, zero if elem1 equals elem2
* and positive if elem1 > elem2.
* If elem2 is a copy of elem1 then the elements are equal.
*/
inline int
element_compare (const t8_eclass_t tree_class, const t8_element_t *elem1, const t8_element_t *elem2) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_compare (elem1, elem2); },
eclass_schemes[tree_class]);
};
/** Check if two elements are equal.
* \param [in] tree_class The eclass of the current tree.
* \param [in] elem1 The first element.
* \param [in] elem2 The second element.
* \return true if the elements are equal, false if they are not equal
*/
inline bool
element_is_equal (const t8_eclass_t tree_class, const t8_element_t *elem1, const t8_element_t *elem2) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_is_equal (elem1, elem2); },
eclass_schemes[tree_class]);
};
/**
* Indicates if an element is refinable. Possible reasons for being not refinable could be
* that the element has reached its max level.
* \param [in] tree_class The eclass of the current tree.
* \param [in] element The element to check.
* \return True if the element is refinable.
*/
inline bool
element_is_refinable (const t8_eclass_t tree_class, const t8_element_t *element) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_is_refinable (element); },
eclass_schemes[tree_class]);
};
/** Compute the parent of a given element \a element and store it in \a parent.
* \a parent needs to be an existing element. No memory is allocated by this function.
* \a element and \a parent can point to the same element, then the entries of
* \a element are overwritten by the ones of its parent.
* \param [in] tree_class The eclass of the current tree.
* \param [in] element The element whose parent will be computed.
* \param [in,out] parent This element's entries will be overwritten by those
* of \a element's parent.
* The storage for this element must exist
* and match the element class of the parent.
* For a pyramid, for example, it may be either a
* tetrahedron or a pyramid depending on \a element's childid.
*/
inline void
element_get_parent (const t8_eclass_t tree_class, const t8_element_t *element, t8_element_t *parent) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_get_parent (element, parent); },
eclass_schemes[tree_class]);
};
/** Compute the number of siblings of an element. That is the number of
* Children of its parent.
* \param [in] tree_class The eclass of the current tree.
* \param [in] element The element.
* \return The number of siblings of \a element.
* Note that this number is >= 1, since we count the element itself as a sibling.
*/
inline int
element_get_num_siblings (const t8_eclass_t tree_class, const t8_element_t *element) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_get_num_siblings (element); },
eclass_schemes[tree_class]);
};
/** Compute a specific sibling of a given element \a element and store it in \a sibling.
* \a sibling needs to be an existing element. No memory is allocated by this function.
* \a element and \a sibling can point to the same element, then the entries of
* \a element are overwritten by the ones of its sibid-th sibling.
* \param [in] tree_class The eclass of the current tree.
* \param [in] element The element whose sibling will be computed.
* \param [in] sibid The id of the sibling computed.
* \param [in,out] sibling This element's entries will be overwritten by those
* of \a element's sibid-th sibling.
* The storage for this element must exist
* and match the element class of the sibling.
*/
inline void
element_get_sibling (const t8_eclass_t tree_class, const t8_element_t *element, const int sibid,
t8_element_t *sibling) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_get_sibling (element, sibid, sibling); },
eclass_schemes[tree_class]);
};
/** Compute the number of corners of a given element.
* \param [in] tree_class The eclass of the current tree.
* \param [in] element The element.
* \return The number of corners of \a element.
*/
inline int
element_get_num_corners (const t8_eclass_t tree_class, const t8_element_t *element) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_get_num_corners (element); },
eclass_schemes[tree_class]);
};
/** Compute the number of faces of a given element.
* \param [in] tree_class The eclass of the current tree.
* \param [in] element The element.
* \return The number of faces of \a element.
*/
inline int
element_get_num_faces (const t8_eclass_t tree_class, const t8_element_t *element) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_get_num_faces (element); },
eclass_schemes[tree_class]);
};
/** Compute the maximum number of faces of a given element and all of its
* descendants.
* \param [in] tree_class The eclass of the current tree.
* \param [in] element The element.
* \return The maximum number of faces of \a element and its descendants.
*/
inline int
element_get_max_num_faces (const t8_eclass_t tree_class, const t8_element_t *element) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_get_max_num_faces (element); },
eclass_schemes[tree_class]);
};
/** Return the number of children of an element when it is refined.
* \param [in] tree_class The eclass of the current tree.
* \param [in] element The element whose number of children is returned.
* \return The number of children of \a element if it is to be refined.
*/
inline int
element_get_num_children (const t8_eclass_t tree_class, const t8_element_t *element) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_get_num_children (element); },
eclass_schemes[tree_class]);
};
/** Return the max number of children of an eclass.
* \param [in] tree_class The eclass of tree the elements are part of.
* \return The max number of children of \a element.
*/
inline int
get_max_num_children (const t8_eclass_t tree_class) const
{
return std::visit ([&] (auto &&scheme) { return scheme.get_max_num_children (); }, eclass_schemes[tree_class]);
};
/** Return the number of children of an element's face when the element is refined.
* \param [in] tree_class The eclass of the current tree.
* \param [in] element The element whose face is considered.
* \param [in] face A face of \a element.
* \return The number of children of \a face if \a element is to be refined.
*/
inline int
element_get_num_face_children (const t8_eclass_t tree_class, const t8_element_t *element, const int face) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_get_num_face_children (element, face); },
eclass_schemes[tree_class]);
};
/** Return the corner number of an element's face corner.
* Example quad: 2 x --- x 3
* | |
* | | face 1
* 0 x --- x 1
* Thus for face = 1 the output is: corner=0 : 1, corner=1: 3
*
* \param [in] tree_class The eclass of the current tree.
* \param [in] element The element.
* \param [in] face A face index for \a element.
* \param [in] corner A corner index for the face 0 <= \a corner < num_face_corners.
* \return The corner number of the \a corner-th vertex of \a face.
*/
inline int
element_get_face_corner (const t8_eclass_t tree_class, const t8_element_t *element, const int face,
const int corner) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_get_face_corner (element, face, corner); },
eclass_schemes[tree_class]);
};
/** Return the face numbers of the faces sharing an element's corner.
* Example quad: 2 x --- x 3
* | |
* | | face 1
* 0 x --- x 1
* face 2
* Thus for corner = 1 the output is: face=0 : 2, face=1: 1
* \param [in] tree_class The eclass of the current tree.
* \param [in] element The element.
* \param [in] corner A corner index for the face.
* \param [in] face A face index for \a corner.
* \return The face number of the \a face-th face at \a corner.
*/
inline int
element_get_corner_face (const t8_eclass_t tree_class, const t8_element_t *element, const int corner,
const int face) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_get_corner_face (element, corner, face); },
eclass_schemes[tree_class]);
};
/** Construct the child element of a given number.
* \param [in] tree_class The eclass of the current tree.
* \param [in] element This must be a valid element, bigger than maxlevel.
* \param [in] childid The number of the child to construct.
* \param [in,out] child The storage for this element must exist.
* On output, a valid element.
* It is valid to call this function with element = child.
*/
inline void
element_get_child (const t8_eclass_t tree_class, const t8_element_t *element, const int childid,
t8_element_t *child) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_get_child (element, childid, child); },
eclass_schemes[tree_class]);
};
/** Construct all children of a given element.
* \param [in] tree_class The eclass of the current tree.
* \param [in] element This must be a valid element, bigger than maxlevel.
* \param [in] length The length of the output array \a c must match
* the number of children.
* \param [in,out] c The storage for these \a length elements must exist.
* On output, all children are valid.
* It is valid to call this function with element = c[0].
* \see element_get_num_children
*/
inline void
element_get_children (const t8_eclass_t tree_class, const t8_element_t *element, const int length,
t8_element_t *c[]) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_get_children (element, length, c); },
eclass_schemes[tree_class]);
};
/** Compute the child id of an element.
* \param [in] tree_class The eclass of the current tree.
* \param [in] element This must be a valid element.
* \return The child id of element.
*/
inline int
element_get_child_id (const t8_eclass_t tree_class, const t8_element_t *element) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_get_child_id (element); },
eclass_schemes[tree_class]);
};
/** Compute the ancestor id of an element, that is the child id
* at a given level.
* \param [in] tree_class The eclass of the current tree.
* \param [in] element This must be a valid element.
* \param [in] level A refinement level. Must satisfy \a level <= element.level
* \return The child_id of \a element in regard to its \a level ancestor.
* \note The ancestor id at element.level is the same as the child id.
*/
inline int
element_get_ancestor_id (const t8_eclass_t tree_class, const t8_element_t *element, const int level) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_get_ancestor_id (element, level); },
eclass_schemes[tree_class]);
};
/** Query whether a given set of elements is a family or not.
* \param [in] tree_class The eclass of the current tree.
* \param [in] fam An array of as many elements as an element of class
* \a tree_class has siblings.
* \return Zero if \a fam is not a family, nonzero if it is.
* \note level 0 elements do not form a family.
*/
inline bool
elements_are_family (const t8_eclass_t tree_class, t8_element_t *const *fam) const
{
return std::visit ([&] (auto &&scheme) { return scheme.elements_are_family (fam); }, eclass_schemes[tree_class]);
};
/** Compute the nearest common ancestor of two elements. That is,
* the element with highest level that still has both given elements as
* descendants.
* \param [in] tree_class The eclass of the current tree.
* \param [in] elem1 The first of the two input elements.
* \param [in] elem2 The second of the two input elements.
* \param [in,out] nca The storage for this element must exist
* and match the element class of the child.
* On output the unique nearest common ancestor of
* \a elem1 and \a elem2.
*/
inline void
element_get_nca (const t8_eclass_t tree_class, const t8_element_t *elem1, const t8_element_t *elem2,
t8_element_t *const nca) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_get_nca (elem1, elem2, nca); },
eclass_schemes[tree_class]);
};
/** Compute the shape of the face of an element.
* \param [in] tree_class The eclass of the current tree.
* \param [in] element The element.
* \param [in] face A face of \a element.
* \return The element shape of the face.
* I.e. T8_ECLASS_LINE for quads, T8_ECLASS_TRIANGLE for tets
* and depending on the face number either T8_ECLASS_QUAD or
* T8_ECLASS_TRIANGLE for prisms.
*/
inline t8_element_shape_t
element_get_face_shape (const t8_eclass_t tree_class, const t8_element_t *element, const int face) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_get_face_shape (element, face); },
eclass_schemes[tree_class]);
};
/** Given an element and a face of the element, compute all children of
* the element that touch the face.
* \param [in] tree_class The eclass of the current tree.
* \param [in] element The element.
* \param [in] face A face of \a element.
* \param [in,out] children Allocated elements, in which the children of \a element
* that share a face with \a face are stored.
* They will be stored in order of their linear id.
* \param [in] num_children The number of elements in \a children. Must match
* the number of children that touch \a face.
* \ref element_get_num_face_children
* \param [in,out] child_indices If not NULL, an array of num_children integers must be given,
* on output its i-th entry is the child_id of the i-th face_child.
* It is valid to call this function with element = children[0].
*/
inline void
element_get_children_at_face (const t8_eclass_t tree_class, const t8_element_t *element, const int face,
t8_element_t *children[], int num_children, int *child_indices) const
{
return std::visit (
[&] (auto &&scheme) {
return scheme.element_get_children_at_face (element, face, children, num_children, child_indices);
},
eclass_schemes[tree_class]);
};
/** Given a face of an element and a child number of a child of that face, return the face number
* of the child of the element that matches the child face.
* \verbatim
x ---- x x x x ---- x
| | | | | | | <-- f
| | | x | x--x
| | | | |
x ---- x x x ---- x
element face face_child Returns the face number f
\endverbatim
* \param [in] tree_class The eclass of the current tree.
* \param [in] element The element.
* \param [in] face Then number of the face.
* \param [in] face_child A number 0 <= \a face_child < num_face_children,
* specifying a child of \a element that shares a face with \a face.
* These children are counted in linear order. This coincides with
* the order of children from a call to \ref element_get_children_at_face.
* \return The face number of the face of a child of \a element
* that coincides with \a face_child.
*/
inline int
element_face_get_child_face (const t8_eclass_t tree_class, const t8_element_t *element, const int face,
const int face_child) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_face_get_child_face (element, face, face_child); },
eclass_schemes[tree_class]);
};
/** Given a face of an element return the face number
* of the parent of the element that matches the element's face. Or return -1 if
* no face of the parent matches the face.
* \param [in] tree_class The eclass of the current tree.
* \param [in] element The element.
* \param [in] face Then number of the face.
* \return If \a face of \a element is also a face of \a element's parent,
* the face number of this face. Otherwise -1.
* \note For the root element this function always returns \a face.
*/
inline int
element_face_get_parent_face (const t8_eclass_t tree_class, const t8_element_t *element, const int face) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_face_get_parent_face (element, face); },
eclass_schemes[tree_class]);
};
/** Given a face of an element and a level coarser than (or equal to)
* the element's level, return the face number
* of the ancestor of the element that matches the element's face. Or return -1 if
* no face of the ancestor matches the face.
* \param [in] tree_class The eclass of the current tree.
* \param [in] element The element.
* \param [in] ancestor_level A refinement level smaller than (or equal to) \a element's level.
* \param [in] face Then number of a face of \a element.
* \return If \a face of \a element is a subface of a face of \a element's ancestor at level \a ancestor_level,
* the face number of this face. Otherwise -1.
* \note For the root element this function always returns \a face.
*/
int
element_face_get_ancestor_face (const t8_eclass_t tree_class, const t8_element_t *element, const int ancestor_level,
const int face) const
{
return std::visit (
[&] (auto &&scheme) { return scheme.element_face_get_ancestor_face (element, ancestor_level, face); },
eclass_schemes[tree_class]);
}
/** Given an element and a face of this element. If the face lies on the
* tree boundary, return the face number of the tree face.
* If not the return value is arbitrary.
* You can call \ref element_is_root_boundary to query whether the face is
* at the tree boundary.
* \param [in] tree_class The eclass of the current tree.
* \param [in] element The element.
* \param [in] face The index of a face of \a element.
* \return The index of the tree face that \a face is a subface of, if
* \a face is on a tree boundary.
* Any arbitrary integer if \a is not at a tree boundary.
* \warning The return value may look like a valid face of the tree even if
* the element does not lie on the root boundary.
*/
inline int
element_get_tree_face (const t8_eclass_t tree_class, const t8_element_t *element, const int face) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_get_tree_face (element, face); },
eclass_schemes[tree_class]);
};
/** Suppose we have two trees that share a common face f.
* Given an element e that is a subface of f in one of the trees
* and given the orientation of the tree connection, construct the face
* element of the respective tree neighbor that logically coincides with e
* but lies in the coordinate system of the neighbor tree.
* \param [in] tree_class The eclass of the current tree.
* \param [in] elem1 The face element.
* \param [in,out] elem2 On return the face element \a elem1 with respect
* to the coordinate system of the other tree.
* \param [in] orientation The orientation of the tree-tree connection.
* \see t8_cmesh_set_join
* \param [in] sign Depending on the topological orientation of the two tree faces,
* either 0 (both faces have opposite orientation)
* or 1 (both faces have the same top. orientation).
* \ref t8_eclass_face_orientation
* \param [in] is_smaller_face Flag to declare whether \a elem1 belongs to
* the smaller face. A face f of tree T is smaller than
* f' of T' if either the eclass of T is smaller or if
* the classes are equal and f<f'. The orientation is
* defined in relation to the smaller face.
* \note \a elem1 and \a elem2 may point to the same element.
*/
inline void
element_transform_face (const t8_eclass_t tree_class, const t8_element_t *elem1, t8_element_t *elem2,
const int orientation, const int sign, const int is_smaller_face) const
{
return std::visit (
[&] (auto &&scheme) { return scheme.element_transform_face (elem1, elem2, orientation, sign, is_smaller_face); },
eclass_schemes[tree_class]);
};
/** Given a boundary face inside a root tree's face construct
* the element inside the root tree that has the given face as a
* face.
* \param [in] tree_class The eclass of the current tree.
* \param [in] face A face element.
* \param [in,out] element An allocated element. The entries will be filled with
* the data of the element that has \a face as a face and
* lies within the root tree.
* \param [in] root_face The index of the face of the root tree in which \a face
* lies.
* \return The face number of the face of \a element that coincides
* with \a face.
*/
inline int
element_extrude_face (const t8_eclass_t tree_class, const t8_element_t *face, t8_element_t *element,
const int root_face) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_extrude_face (face, element, root_face, this); },
eclass_schemes[tree_class]);
};
/** Construct the boundary element at a specific face.
* \param [in] tree_class The eclass of the current tree.
* \param [in] element The input element.
* \param [in] face The index of the face of which to construct the
* boundary element.
* \param [in,out] boundary An allocated element of dimension of \a element
* minus 1. The entries will be filled with the entries
* of the face of \a element.
* If \a element is of class T8_ECLASS_VERTEX, then \a boundary must be NULL
* and will not be modified.
*/
inline void
element_get_boundary_face (const t8_eclass_t tree_class, const t8_element_t *element, const int face,
t8_element_t *boundary) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_get_boundary_face (element, face, boundary, this); },
eclass_schemes[tree_class]);
};
/** Construct the first descendant of an element at a given level that touches a given face.
* \param [in] tree_class The eclass of the current tree.
* \param [in] element The input element.
* \param [in] face A face of \a element.
* \param [in, out] first_desc An allocated element. This element's data will be
* filled with the data of the first descendant of \a element
* that shares a face with \a face.
* \param [in] level The level, at which the first descendant is constructed
*/
inline void
element_get_first_descendant_face (const t8_eclass_t tree_class, const t8_element_t *element, const int face,
t8_element_t *first_desc, const int level) const
{
return std::visit (
[&] (auto &&scheme) { return scheme.element_get_first_descendant_face (element, face, first_desc, level); },
eclass_schemes[tree_class]);
};
/** Construct the last descendant of an element at a given level that touches a given face.
* \param [in] tree_class The eclass of the current tree.
* \param [in] element The input element.
* \param [in] face A face of \a element.
* \param [in, out] last_desc An allocated element. This element's data will be
* filled with the data of the last descendant of \a element
* that shares a face with \a face.
* \param [in] level The level, at which the last descendant is constructed
*/
inline void
element_get_last_descendant_face (const t8_eclass_t tree_class, const t8_element_t *element, const int face,
t8_element_t *last_desc, const int level) const
{
return std::visit (
[&] (auto &&scheme) { return scheme.element_get_last_descendant_face (element, face, last_desc, level); },
eclass_schemes[tree_class]);
};
/** Compute whether a given element shares a given face with its root tree.
* \param [in] tree_class The eclass of the current tree.
* \param [in] element The input element.
* \param [in] face A face of \a element.
* \return True if \a face is a subface of the element's root element.
* \note You can compute the corresponding face number of the tree via \ref element_get_tree_face.
*/
inline bool
element_is_root_boundary (const t8_eclass_t tree_class, const t8_element_t *element, const int face) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_is_root_boundary (element, face); },
eclass_schemes[tree_class]);
};
/** Construct the face neighbor of a given element if this face neighbor
* is inside the root tree. Return 0 otherwise.
* \param [in] tree_class The eclass of the current tree.
* \param [in] element The element to be considered.
* \param [in,out] neigh If the face neighbor of \a element along \a face is inside
* the root tree, this element's data is filled with the
* data of the face neighbor. Otherwise the data can be modified
* arbitrarily.
* \param [in] face The number of the face along which the neighbor should be
* constructed.
* \param [out] neigh_face The number of \a face as viewed from \a neigh.
* An arbitrary value, if the neighbor is not inside the root tree.
* \return True if \a neigh is inside the root tree.
* False if not. In this case \a neigh's data can be arbitrary
* on output.
*/
inline int
element_get_face_neighbor_inside (const t8_eclass_t tree_class, const t8_element_t *element, t8_element_t *neigh,
const int face, int *neigh_face) const
{
return std::visit (
[&] (auto &&scheme) { return scheme.element_get_face_neighbor_inside (element, neigh, face, neigh_face); },
eclass_schemes[tree_class]);
};
/** Return the shape of an allocated element according its type.
* For example, a child of an element can be an element of a different shape
* and has to be handled differently - according to its shape.
* \param [in] tree_class The eclass of the current tree.
* \param [in] element The element to be considered
* \return The shape of the element as an eclass
*/
inline t8_element_shape_t
element_get_shape (const t8_eclass_t tree_class, const t8_element_t *element) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_get_shape (element); }, eclass_schemes[tree_class]);
};
/** Initialize the entries of an allocated element according to a
* given linear id in a uniform refinement.
* \param [in] tree_class The eclass of the current tree.
* \param [in,out] element The element whose entries will be set.
* \param [in] level The level of the uniform refinement to consider.
* \param [in] id The linear id.
* id must fulfil 0 <= id < 'number of leaves in the uniform refinement'
*/
inline void
element_set_linear_id (const t8_eclass_t tree_class, t8_element_t *element, const int level,
const t8_linearidx_t id) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_set_linear_id (element, level, id); },
eclass_schemes[tree_class]);
};
/** Compute the linear id of a given element in a hypothetical uniform
* refinement of a given level.
* \param [in] tree_class The eclass of the current tree.
* \param [in] element The element whose id we compute.
* \param [in] level The level of the uniform refinement to consider.
* \return The linear id of the element.
*/
inline t8_linearidx_t
element_get_linear_id (const t8_eclass_t tree_class, const t8_element_t *element, const int level) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_get_linear_id (element, level); },
eclass_schemes[tree_class]);
};
/** Compute the first descendant of a given element.
* \param [in] tree_class The eclass of the current tree.
* \param [in] element The element whose descendant is computed.
* \param [out] desc The first element in a uniform refinement of \a element
* of the given level.
* \param [in] level The level, at which the descendant is computed.
*/
inline void
element_get_first_descendant (const t8_eclass_t tree_class, const t8_element_t *element, t8_element_t *desc,
const int level) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_get_first_descendant (element, desc, level); },
eclass_schemes[tree_class]);
};
/** Compute the last descendant of a given element.
* \param [in] tree_class The eclass of the current tree.
* \param [in] element The element whose descendant is computed.
* \param [out] desc The last element in a uniform refinement of \a element
* of the given level.
* \param [in] level The level, at which the descendant is computed.
*/
inline void
element_get_last_descendant (const t8_eclass_t tree_class, const t8_element_t *element, t8_element_t *desc,
const int level) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_get_last_descendant (element, desc, level); },
eclass_schemes[tree_class]);
};
/** Construct the successor in a uniform refinement of a given element.
* \param [in] tree_class The eclass of the current tree.
* \param [in] element The element whose successor should be constructed.
* \param [in,out] successor The element whose entries will be set to the successor of \a element.
*/
inline void
element_construct_successor (const t8_eclass_t tree_class, const t8_element_t *element, t8_element_t *successor) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_construct_successor (element, successor); },
eclass_schemes[tree_class]);
};
/** Compute the coordinates of a given element vertex inside a reference tree
* that is embedded into [0,1]^d (d = dimension).
* \param [in] tree_class The eclass of the current tree.
* \param [in] element The element to be considered.
* \param [in] vertex The id of the vertex whose coordinates shall be computed.
* \param [out] coords An array of at least as many doubles as the element's dimension
* whose entries will be filled with the coordinates of \a vertex.
* \warning coords should be zero-initialized, as only the first d coords will be set, but when used elsewhere
* all coords might be used.
*/
inline void
element_get_vertex_reference_coords (const t8_eclass_t tree_class, const t8_element_t *element, const int vertex,
double coords[]) const
{
return std::visit (
[&] (auto &&scheme) { return scheme.element_get_vertex_reference_coords (element, vertex, coords); },
eclass_schemes[tree_class]);
};
/** Convert points in the reference space of an element to points in the
* reference space of the tree.
* \param [in] tree_class The eclass of the current tree.
* \param [in] element The element.
* \param [in] ref_coords The coordinates \f$ [0,1]^\mathrm{dim} \f$ of the point
* in the reference space of the element.
* \param [in] num_coords Number of \f$ dim\f$-sized coordinates to evaluate.
* \param [out] out_coords The coordinates of the points in the
* reference space of the tree.
*/
inline void
element_get_reference_coords (const t8_eclass_t tree_class, const t8_element_t *element, const double *ref_coords,
const size_t num_coords, double *out_coords) const
{
return std::visit (
[&] (auto &&scheme) { return scheme.element_get_reference_coords (element, ref_coords, num_coords, out_coords); },
eclass_schemes[tree_class]);
};
/** Count how many leaf descendants of a given uniform level an element would produce.
* \param [in] tree_class The eclass of the current tree.
* \param [in] t The element to be checked.
* \param [in] level A refinement level.
* \return Suppose \a t is uniformly refined up to level \a level. The return value
* is the resulting number of elements (of the given level).
* If \a level < element_get_level(t), the return value should be 0.
*
* Example: If \a t is a line element that refines into 2 line elements on each level,
* then the return value is max(0, 2^{\a level - level(\a t)}).
* Thus, if \a t's level is 0, and \a level = 3, the return value is 2^3 = 8.
*/
inline t8_gloidx_t
element_count_leaves (const t8_eclass_t tree_class, const t8_element_t *t, const int level) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_count_leaves (t, level); },
eclass_schemes[tree_class]);
};
/** Count how many leaf descendants of a given uniform level the root element will produce.
* \param [in] tree_class The eclass of the current tree.
* \param [in] level A refinement level.
* \return The value of \ref t8_element_count_leaves if the input element
* is the root (level 0) element.
*
* This is a convenience function, and can be implemented via
* \ref t8_element_count_leaves.
*/
inline t8_gloidx_t
count_leaves_from_root (const t8_eclass_t tree_class, const int level) const
{
return std::visit ([&] (auto &&scheme) { return scheme.count_leaves_from_root (level); },
eclass_schemes[tree_class]);
};
#if T8_ENABLE_DEBUG
/** Query whether a given element can be considered as 'valid' and it is
* safe to perform any of the above algorithms on it.
* For example this could mean that all coordinates are in valid ranges
* and other membervariables do have meaningful values.
* \param [in] tree_class The eclass of the current tree.
* \param [in] element The element to be checked.
* \return True if \a element is safe to use. False otherwise.
* \note An element that is constructed with \ref element_new
* must pass this test.
* \note An element for which \ref element_init was called must pass
* this test.
* \note This function is used for debugging to catch certain errors.
* These can for example occur when an element points to a region
* of memory which should not be interpreted as an element.
* \note We recommend to use the assertion T8_ASSERT (element_is_valid (element))
* in the implementation of each of the functions in this file.
*/
inline int
element_is_valid (const t8_eclass_t tree_class, const t8_element_t *element) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_is_valid (element); }, eclass_schemes[tree_class]);
};
/**
* Print a given element. For a example for a triangle print the coordinates
* and the level of the triangle. This function is only available in the
* debugging configuration.
* \param [in] tree_class The eclass of the current tree.
* \param [in] element The element to print
*/
inline void
element_debug_print (const t8_eclass_t tree_class, const t8_element_t *element) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_debug_print (element); },
eclass_schemes[tree_class]);
};
#endif
/**
* Fill a string with readable information about the element
* \param [in] tree_class The eclass of the current tree.
* \param[in] element The element to translate into human-readable information.
* \param[in, out] debug_string The string to fill.
* \param[in] string_size The length of \a debug_string.
*/
inline void
element_to_string (const t8_eclass_t tree_class, const t8_element_t *element, char *debug_string,
const int string_size) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_to_string (element, debug_string, string_size); },
eclass_schemes[tree_class]);
};
/** Allocate memory for \a length many elements of a given class and initialize them,
* and put pointers to the elements in the provided array.
* \param [in] tree_class The eclass of the current tree.
* \param [in] length The number of elements to be allocated.
* \param [in,out] elements On input an array of \a length many element pointers.
* On output all these pointers will point to an allocated
* and initialized element.
* \note There are two ways to create multiple elements of the same type. Create an
* array of element pointers and fill it with \ref element_new, or allocate memory
* for \a length times \a element_size many bytes, and fill them with element_init.
* To access a specific element, offset calculation needs to be done manually, as
* \ref t8_element_t is incomplete.
* \note In debugging mode, an element that was created with \ref element_new
* must pass \ref element_is_valid (for example the root element).
* \note If an element was created by \ref element_new then \ref element_init
* may not be called for it. Thus, \ref element_new should initialize an element
* in the same way as a call to \ref element_init would.
* \note Every call to \ref element_new must be matched by a call to \ref element_destroy
* \see element_destroy
* \see element_init
* \see element_is_valid
*/
inline void
element_new (const t8_eclass_t tree_class, const int length, t8_element_t **elements) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_new (length, elements); },
eclass_schemes[tree_class]);
};
/** Initialize an array of allocated elements.
* \param [in] tree_class The eclass of the current tree.
* \param [in] length The number of elements to be initialized.
* \param [in,out] elements On input an array of \a length many allocated
* elements.
* \note In debugging mode, an element that was passed to \ref element_init
* must pass \ref element_is_valid.
* \note If an element was created by \ref element_new then \ref element_init
* may not be called for it. Thus, \ref element_init should initialize an element
* in the same way as a call to \ref element_new would.
* \note Every call to \ref element_init must be matched by a call to \ref element_deinit
* \see element_deinit
* \see element_new
* \see element_is_valid
*/
inline void
element_init (const t8_eclass_t tree_class, const int length, t8_element_t *elements) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_init (length, elements); },
eclass_schemes[tree_class]);
};
/** Deinitialize an array of allocated elements.
* \param [in] tree_class The eclass of the current tree.
* \param [in] length The number of elements to be deinitialized.
* \param [in,out] elements On input an array of \a length many allocated
* and initialized elements, on output an array of
* \a length many allocated, but not initialized elements.
* \note Call this function if you called \ref element_init on the element pointers.
* \see element_init
*/
inline void
element_deinit (const t8_eclass_t tree_class, const int length, t8_element_t *elements) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_deinit (length, elements); },
eclass_schemes[tree_class]);
};
/** Deallocate an array of elements.
* \param [in] tree_class The eclass of the current tree.
* \param [in] length The number of elements in the array.
* \param [in,out] elements On input an array of \a length many allocated
* element pointers.
* On output all these pointers will be freed.
* \a element itself will not be freed by this function.
* \see element_new
*/
inline void
element_destroy (const t8_eclass_t tree_class, const int length, t8_element_t **elements) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_destroy (length, elements); },
eclass_schemes[tree_class]);
};
/** create the root element
* \param [in] tree_class The eclass of the current tree.
* \param [in,out] element The element that is filled with the root
*/
inline void
set_to_root (const t8_eclass_t tree_class, t8_element_t *element) const
{
return std::visit ([&] (auto &&scheme) { return scheme.set_to_root (element); }, eclass_schemes[tree_class]);
};
/** Pack multiple elements into contiguous memory, so they can be sent via MPI.
* \param [in] tree_class The eclass of the current tree.
* \param [in] elements Array of elements that are to be packed
* \param [in] count Number of elements to pack
* \param [in,out] send_buffer Buffer in which to pack the elements
* \param [in] buffer_size size of the buffer (in order to check that we don't access out of range)
* \param [in, out] position the position of the first byte that is not already packed
* \param [in] comm MPI Communicator
*/
inline void
element_MPI_Pack (const t8_eclass_t tree_class, t8_element_t **const elements, const unsigned int count,
void *send_buffer, int buffer_size, int *position, sc_MPI_Comm comm) const
{
return std::visit (
[&] (auto &&scheme) {
return scheme.element_MPI_Pack (elements, count, send_buffer, buffer_size, position, comm);
},
eclass_schemes[tree_class]);
};
/** Determine an upper bound for the size of the packed message of \a count elements
* \param [in] tree_class The eclass of the current tree.
* \param [in] count Number of elements to pack
* \param [in] comm MPI Communicator
* \param [out] pack_size upper bound on the message size
*/
inline void
element_MPI_Pack_size (const t8_eclass_t tree_class, const unsigned int count, sc_MPI_Comm comm, int *pack_size) const
{
return std::visit ([&] (auto &&scheme) { return scheme.element_MPI_Pack_size (count, comm, pack_size); },
eclass_schemes[tree_class]);
};
/** Unpack multiple elements from contiguous memory that was received via MPI.
* \param [in] tree_class The eclass of the current tree.
* \param [in] recvbuf Buffer from which to unpack the elements
* \param [in] buffer_size size of the buffer (in order to check that we don't access out of range)
* \param [in, out] position the position of the first byte that is not already packed
* \param [in] elements Array of initialised elements that is to be filled from the message
* \param [in] count Number of elements to unpack
* \param [in] comm MPI Communicator
*/
inline void
element_MPI_Unpack (t8_eclass_t tree_class, void *recvbuf, const int buffer_size, int *position,
t8_element_t **elements, const unsigned int count, sc_MPI_Comm comm) const
{
return std::visit (
[&] (auto &&scheme) { return scheme.element_MPI_Unpack (recvbuf, buffer_size, position, elements, count, comm); },
eclass_schemes[tree_class]);
};
};
#endif /* !T8_SCHEME_HXX */