@@ -113,3 +113,67 @@ For example, the faces of a simplex are numbered such that each face has the sam
\caption{Mesh entity topologies in the TNL library and the ordering of subvertices and faces.}
\label{fig:entity topologies}
\end{figure}
\chapter{Mesh Storage Layers}
\label{appendix:mesh layers}
The inheritance diagram for the \ic{Mesh} class template is shown in \cref{fig:inheritance_1}.
The template parameters \ic{Config}, which represents the mesh configuration (see \subsubsecref{sec:meshes:configuration}), and \ic{Device}, which can be either \ic{TNL::Devices::Host} or \ic{TNL::Devices::Cuda}\todo{only reference the section in \cref{chapter:programming and architectures} listing all devices in TNL} and represents the \emph{device} where the mesh will be allocated, are propagated to all base class templates in the hierarchy.
The first base class template is \ic{StorageLayerFamily} which does not have any data members and its purpose is to provide access via tag dispatching to all individual storage layers.
The recursive inheritance is realized in the \ic{StorageLayer} class template which is parametrized by the entity dimension tag, \ic{DimTag<d>}, and comprises all data related to $d$-dimensional mesh entities.
The \ic{StorageLayerFamily} class template starts the inheritance with the dimension 0 and each storage layer inherits from the same class template, but with an incremented dimension tag.
Finally, recursion is terminated by the empty $(D+1)$-dimensional partial class template specialization.
\caption{Inheritance scheme of the $D$-dimensional \ic{Mesh} class template.
The $(D+1)$-dimensional partial specialization of the \ic{StorageLayer} class template is an empty class template used to terminate the recursive inheritance.
}
\label{fig:inheritance_1}
\end{figure}
The inheritance diagram for the general $d$-dimensional \ic{StorageLayer} class template is shown in \cref{fig:inheritance_2} (the inheritance between the instances of \ic{StorageLayer} with the parameters \ic{DimTag<d>} and \ic{DimTag<d+1>} has been explained in \cref{fig:inheritance_1}).
The inheritance scheme is similar to \cref{fig:inheritance_1}, now using two separate storage layer families for the data related to subentities and superentities of $d$-dimensional entities.
We introduce the \ic{SubentityStorageLayer} and \ic{SuperentityStorageLayer} class templates which are parametrized by the entity dimensions $d$ and $d'$ and store one specific incidence matrix each: \ic{SubentityStorageLayer} stores $I_{d,d'}$, where $d > d'$, and \ic{SuperentityStorageLayer} stores $I_{d,d'}$, where $d < d'$.
By default, both \ic{SubentityStorageLayer} and \ic{SuperentityStorageLayer} also store the array containing the numbers of non-zero elements per row (see \subsubsecref{sec:meshes:internal data structures}).
The \ic{SubentityStorageLayer} template has a partial class template specialization for static entity topologies, which omits the array with numbers of non-zero elements per row and uses a compile-time constant instead.
Note that the inheritance for \ic{SubentityStorageLayer} is started by $d' =0$ and continues by incrementing $d'$, but for the \ic{SuperentityStorageLayer} it is started by $d' = D$ and continues by decrementing $d'$.
In both cases, the recursive inheritance which is terminated by an empty partial class template specialization when the entity dimension is the same as the subentity or superentity dimension (i.e., $d = d'$).
\caption{Inheritance scheme of the $d$-dimensional specialization of the \ic{StorageLayer} class template for a $D$-dimensional mesh.
The $d$-dimensional partial specializations of the \ic{SubentityStorageLayer} and \ic{SuperentityStorageLayer} class templates are empty class templates used to terminate the recursive inheritance.
}
\label{fig:inheritance_2}
\end{figure}
In the following code examples, we outline the implementation of the scheme from \cref{fig:inheritance_1} using recursive inheritance and partial class template specialization.
The recursive inheritance can be written in a simplified form as follows:
: public StorageLayer< Config, Device, typename DimTag::Increment >
{
// implementation of member functions is omitted
};
\end{cppcode}
It is assumed that the \ic{DimTag} parameter is initially supplied a 0-dimensional tag which is incremented by one to inherit the next storage layer.
Note that due to a \C++ language limitation, we have to use the \ic{DimTag} type template parameter instead of a non-type template parameter such as \ic{int d} to represent the dimension, because expressions involving other type template parameters cannot be used to partially specialize a non-type template parameter.
After all storage layers for the given mesh have been used, an empty partial class template specialization is supplied in order to terminate the recursive inheritance.
The empty specialization is identified by \ic{Config::meshDimension + 1}, where \ic{Config::meshDimension} stands for the mesh dimension $D$:
\begin{cppcode}
template< typename Config, typename Device >
class StorageLayer< Config, Device, DimTag< Config::meshDimension + 1 > >
{};
\end{cppcode}
In practice, the complete inheritance is more complicated than the scheme illustrated in \cref{fig:inheritance_1,fig:inheritance_2}, since the \ic{Mesh} class template has additional base classes which are omitted here for clarity.
Individual storage layers also store more data in addition to the incidence matrices.
For example, there are arrays storing \emph{tags} (such as \ic{BoundaryEntity}, \ic{GhostEntity} or arbitrary user attributes) for each mesh entity.
On the other hand, some data members which do not have to be parametrized by the entity dimension (e.g. the array of vertex coordinates) are placed directly in the \ic{Mesh} class template.
Finally, note that all data members in the hierarchy of storage layers are always present, even if they are unused according to the configuration.
Disabling storage in the configuration skips only dynamic allocation and \ic{static_assert} declarations guard access to the disabled data members.
@@ -396,73 +396,15 @@ If the $d$-dimensional entities have a static topology (e.g., a quadrangle or a
\subsubsection{Internal storage layers}
\label{sec:meshes:storage layers}
The data structure needs to contain several incidence matrices and arrays for the mesh representation depending on the configuration.
The data structure needs to contain several matrices and arrays for the mesh representation depending on the configuration.
The internal data structures can be organized in several layers for each dimension of entities in the mesh.
Individual storage layers can be combined using \C++ features such as \emph{recursive inheritance} and \emph{partial class template specializations} in order to provide an efficient and generic $D$-dimensional data structure.
Recursive inheritance is a \C++ design pattern in which an instance of a class template inherits from another instance of the same class template but with different template arguments.
Hence, in this case inheritance is not used to express a relationship between objects, but to generically include any finite number of differently typed attributes into a single object.
\inline{The rest of this subsection could be removed -- it is not particularly interesting for a thesis. Just say that the inheritance diagrams and additional comments related to the implementation of the storage layers can be found in \cite{klinkovsky:2022meshes,bobot:2022}.}
The inheritance diagram for the \ic{Mesh} class template is shown in \cref{fig:inheritance_1}.
The template parameters \ic{Config}, which represents the mesh configuration (see \subsubsecref{sec:meshes:configuration}), and \ic{Device}, which can be either \ic{TNL::Devices::Host} or \ic{TNL::Devices::Cuda}\todo{only reference the section in \cref{chapter:programming and architectures} listing all devices in TNL} and represents the \emph{device} where the mesh will be allocated, are propagated to all base class templates in the hierarchy.
The first base class template is \ic{StorageLayerFamily} which does not have any data members and its purpose is to provide access via tag dispatching to all individual storage layers.
The recursive inheritance is realized in the \ic{StorageLayer} class template which is parametrized by the entity dimension tag, \ic{DimTag<d>}, and comprises all data related to $d$-dimensional mesh entities.
The \ic{StorageLayerFamily} class template starts the inheritance with the dimension 0 and each storage layer inherits from the same class template, but with an incremented dimension tag.
Finally, recursion is terminated by the empty $(D+1)$-dimensional partial class template specialization.
\caption{Inheritance scheme of the $D$-dimensional \ic{Mesh} class template.
The $(D+1)$-dimensional partial specialization of the \ic{StorageLayer} class template is an empty class template used to terminate the recursive inheritance.
}
\label{fig:inheritance_1}
\end{figure}
The inheritance diagram for the general $d$-dimensional \ic{StorageLayer} class template is shown in \cref{fig:inheritance_2} (the inheritance between the instances of \ic{StorageLayer} with the parameters \ic{DimTag<d>} and \ic{DimTag<d+1>} has been explained in \cref{fig:inheritance_1}).
The inheritance scheme is similar to \cref{fig:inheritance_1}, now using two separate storage layer families for the data related to subentities and superentities of $d$-dimensional entities.
We introduce the \ic{SubentityStorageLayer} and \ic{SuperentityStorageLayer} class templates which are parametrized by the entity dimensions $d$ and $d'$ and store one specific incidence matrix each: \ic{SubentityStorageLayer} stores $I_{d,d'}$, where $d > d'$, and \ic{SuperentityStorageLayer} stores $I_{d,d'}$, where $d < d'$.
By default, both \ic{SubentityStorageLayer} and \ic{SuperentityStorageLayer} also store the array containing the numbers of non-zero elements per row (see \subsubsecref{sec:meshes:internal data structures}).
The \ic{SubentityStorageLayer} template has a partial class template specialization for static entity topologies, which omits the array with numbers of non-zero elements per row and uses a compile-time constant instead.
Note that the inheritance for \ic{SubentityStorageLayer} is started by $d' =0$ and continues by incrementing $d'$, but for the \ic{SuperentityStorageLayer} it is started by $d' = D$ and continues by decrementing $d'$.
In both cases, the recursive inheritance which is terminated by an empty partial class template specialization when the entity dimension is the same as the subentity or superentity dimension (i.e., $d = d'$).
\caption{Inheritance scheme of the $d$-dimensional specialization of the \ic{StorageLayer} class template for a $D$-dimensional mesh.
The $d$-dimensional partial specializations of the \ic{SubentityStorageLayer} and \ic{SuperentityStorageLayer} class templates are empty class templates used to terminate the recursive inheritance.
}
\label{fig:inheritance_2}
\end{figure}
In the following code examples, we outline the implementation of the scheme from \cref{fig:inheritance_1} using recursive inheritance and partial class template specialization.
The recursive inheritance can be written in a simplified form as follows:
: public StorageLayer< Config, Device, typename DimTag::Increment >
{
// implementation of member functions is omitted
};
\end{cppcode}
It is assumed that the \ic{DimTag} parameter is initially supplied a 0-dimensional tag which is incremented by one to inherit the next storage layer.
Note that due to a \C++ language limitation, we have to use the \ic{DimTag} type template parameter instead of a non-type template parameter such as \ic{int d} to represent the dimension, because expressions involving other type template parameters cannot be used to partially specialize a non-type template parameter.
After all storage layers for the given mesh have been used, an empty partial class template specialization is supplied in order to terminate the recursive inheritance.
The empty specialization is identified by \ic{Config::meshDimension + 1}, where \ic{Config::meshDimension} stands for the mesh dimension $D$:
\begin{cppcode}
template< typename Config, typename Device >
class StorageLayer< Config, Device, DimTag< Config::meshDimension + 1 > >
{};
\end{cppcode}
In practice, the complete inheritance is more complicated than the scheme illustrated in \cref{fig:inheritance_1,fig:inheritance_2}, since the \ic{Mesh} class template has additional base classes which are omitted in this paper for clarity.
Individual storage layers also store more data in addition to the incidence matrices.
For example, there are arrays storing \emph{tags} (such as \ic{BoundaryEntity}, \ic{GhostEntity} or arbitrary user attributes) for each mesh entity.
On the other hand, some data members which do not have to be parametrized by the entity dimension (e.g. the array of vertex coordinates) are placed directly in the \ic{Mesh} class template.
Finally, note that all data members in the hierarchy of storage layers are always present, even if they are unused according to the configuration.
Disabling storage in the configuration skips only dynamic allocation and \ic{static_assert} declarations guard access to the disabled data members.
The exact hierarchy of class templates comprising the \ic{Mesh} class template is quite complicated and technical.
A brief description of the main layers in the hierarchy is included in \cref{appendix:mesh layers}, which is based on the paper \cite{klinkovsky:2022meshes}.
An in-depth explanation and additional comments related to the implementation of the storage layers can be found in the thesis \cite{bobot:2022}.
\subsubsection{Mapping user data to mesh entities}