From 9fd8dd807272cc8946f86bf8e3af5473c0720f0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Tue, 30 Mar 2021 20:45:16 +0200 Subject: [PATCH 01/19] Documentation: removed Communicators from the core concepts, MPI will be documented separately --- Documentation/Pages/core-concepts.md | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/Documentation/Pages/core-concepts.md b/Documentation/Pages/core-concepts.md index 3770602b8..de99eff9e 100644 --- a/Documentation/Pages/core-concepts.md +++ b/Documentation/Pages/core-concepts.md @@ -11,24 +11,15 @@ TNL is based on the following core concepts: (TODO: rename to `Executor` or something like that) - Device is responsible for the execution of algorithms in a specific way. - Algorithms can be specialized by the `Device` template parameter. -3. \ref TNL::Communicators "Communicators" - - Communicators represent the main abstraction for distributed computations, - where multiple programs (or instances of the same program) have to - communicate with each other. - - At present, there are only two communicators: - \ref TNL::Communicators::MpiCommunicator "MpiCommunicator" - (uses [MPI](https://en.wikipedia.org/wiki/Message_Passing_Interface)) and - \ref TNL::Communicators::NoDistrCommunicator "NoDistrCommunicator" - (dummy communicator without any distribution support). -4. \ref TNL::Containers::Algorithms "Algorithms" +3. \ref TNL::Containers::Algorithms "Algorithms" - Basic (container-free) algorithms specialized by `Device`/`Executor`. - `ParallelFor`, `Reduction`, `MultiReduction`, `ArrayOperations`, ... -5. \ref TNL::Containers "Containers" +4. \ref TNL::Containers "Containers" - Classes for general data structures. (TODO: alternatively use "Dense" and "Sparse", because a dense matrix can be an extended alias for 2D array) - `Array`, `Vector` (also `VectorOperations`), `NDArray`, ... -6. Views +5. Views - Views wrap only a raw pointer to data and some metadata (such as the array size), they do not do allocation and deallocation of the data. Hence, views have a fixed size which cannot be changed. @@ -38,7 +29,7 @@ TNL is based on the following core concepts: - Views have a copy-assignment operator which does a deep copy. - Views have all other methods present in the relevant container (data structure). -7. Smart pointers +6. Smart pointers - TODO TODO: formalize the concepts involving lambda functions (e.g. in `Reduction`) -- GitLab From 8ce38da0938aa0f1105ac309b8809e3a387f048e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Sun, 28 Mar 2021 22:43:35 +0200 Subject: [PATCH 02/19] Documentation: writing mesh tutorial --- Documentation/Tutorials/CMakeLists.txt | 1 + Documentation/Tutorials/Meshes/CMakeLists.txt | 51 ++++++++++++++ .../Tutorials/Meshes/ReadMeshExample.cpp | 35 ++++++++++ .../Tutorials/Meshes/example-triangles.vtu | 23 +++++++ .../Tutorials/Meshes/tutorial_Meshes.md | 67 +++++++++++++++++++ Documentation/Tutorials/index.md | 1 + src/TNL/Meshes/BuildConfigTags.h | 15 +++-- src/TNL/Meshes/TypeResolver/TypeResolver.h | 46 +++++++------ 8 files changed, 212 insertions(+), 27 deletions(-) create mode 100644 Documentation/Tutorials/Meshes/CMakeLists.txt create mode 100644 Documentation/Tutorials/Meshes/ReadMeshExample.cpp create mode 100644 Documentation/Tutorials/Meshes/example-triangles.vtu create mode 100644 Documentation/Tutorials/Meshes/tutorial_Meshes.md diff --git a/Documentation/Tutorials/CMakeLists.txt b/Documentation/Tutorials/CMakeLists.txt index 53ce4df62..5511d0633 100644 --- a/Documentation/Tutorials/CMakeLists.txt +++ b/Documentation/Tutorials/CMakeLists.txt @@ -5,3 +5,4 @@ add_subdirectory( ReductionAndScan ) add_subdirectory( ForLoops ) add_subdirectory( Pointers ) add_subdirectory( Matrices ) +add_subdirectory( Meshes ) diff --git a/Documentation/Tutorials/Meshes/CMakeLists.txt b/Documentation/Tutorials/Meshes/CMakeLists.txt new file mode 100644 index 000000000..3af12b95c --- /dev/null +++ b/Documentation/Tutorials/Meshes/CMakeLists.txt @@ -0,0 +1,51 @@ +set( COMMON_EXAMPLES +) +set( CPP_EXAMPLES + ReadMeshExample + ${COMMON_EXAMPLES} +) +set( CUDA_EXAMPLES + ${COMMON_EXAMPLES} +) + +foreach( target IN ITEMS ${CPP_EXAMPLES} ) + add_executable( ${target} ${target}.cpp ) + add_custom_command( COMMAND ${target} > ${TNL_DOCUMENTATION_OUTPUT_SNIPPETS_PATH}/${target}.out OUTPUT ${target}.out ) + set( CPP_OUTPUTS ${CPP_OUTPUTS} ${target}.out ) +endforeach() +add_custom_target( RunTutorialsMeshesExamples ALL DEPENDS ${CPP_OUTPUTS} ) + +if( BUILD_CUDA ) + foreach( target IN ITEMS ${CUDA_EXAMPLES} ) + cuda_add_executable( ${target}-cuda ${target}.cu OPTIONS ) + add_custom_command( COMMAND ${target}-cuda > ${TNL_DOCUMENTATION_OUTPUT_SNIPPETS_PATH}/${target}.out OUTPUT ${target}.out ) + set( CUDA_OUTPUTS ${CUDA_OUTPUTS} ${target}.out ) + endforeach() + add_custom_target( RunTutorialsMeshesCudaExamples ALL DEPENDS ${CUDA_OUTPUTS} ) +endif() + +find_package( ZLIB ) +if( ZLIB_FOUND ) + foreach( target IN ITEMS ${CPP_EXAMPLES} ${CUDA_EXAMPLES} ) + target_compile_definitions(${target} PUBLIC "-DHAVE_ZLIB") + target_include_directories(${target} PUBLIC ${ZLIB_INCLUDE_DIRS}) + target_link_libraries(${target} ${ZLIB_LIBRARIES}) + endforeach() +endif() + +find_package( tinyxml2 QUIET ) +if( tinyxml2_FOUND ) + foreach( target IN ITEMS ${CPP_EXAMPLES} ${CUDA_EXAMPLES} ) + target_compile_definitions(${target} PUBLIC "-DHAVE_TINYXML2") + target_link_libraries(${target} tinyxml2::tinyxml2) + endforeach() +endif() + + +set( DATA_FILES + example-triangles.vtu +) + +foreach( file IN ITEMS ${DATA_FILES} ) + configure_file(${file} ${CMAKE_CURRENT_BINARY_DIR}/${file} COPYONLY) +endforeach() diff --git a/Documentation/Tutorials/Meshes/ReadMeshExample.cpp b/Documentation/Tutorials/Meshes/ReadMeshExample.cpp new file mode 100644 index 000000000..ff2f41222 --- /dev/null +++ b/Documentation/Tutorials/Meshes/ReadMeshExample.cpp @@ -0,0 +1,35 @@ +#include + +// Define the tag for the MeshTypeResolver configuration +struct MeshConfigTag {}; + +namespace TNL { +namespace Meshes { +namespace BuildConfigTags { + +template<> struct MeshCellTopologyTag< MeshConfigTag, Topologies::Triangle > { enum { enabled = true }; }; +template<> struct MeshCellTopologyTag< MeshConfigTag, Topologies::Quadrangle > { enum { enabled = true }; }; + +} // namespace BuildConfigTags +} // namespace Meshes +} // namespace TNL + +// Define the main task/function of the program +template< typename Mesh > +bool task( const Mesh& mesh, const std::string& inputFileName ) +{ + std::cout << "The file '" << inputFileName << "' contains the following mesh: " + << TNL::getType() << std::endl; + return true; +} + +int main( int argc, char* argv[] ) +{ + const std::string inputFileName = "example-triangles.vtu"; + + auto wrapper = [&] ( auto& reader, auto&& mesh ) -> bool + { + return task( mesh, inputFileName ); + }; + return ! TNL::Meshes::resolveAndLoadMesh< MeshConfigTag, TNL::Devices::Host >( wrapper, inputFileName, "auto" ); +} diff --git a/Documentation/Tutorials/Meshes/example-triangles.vtu b/Documentation/Tutorials/Meshes/example-triangles.vtu new file mode 100644 index 000000000..a66f8b787 --- /dev/null +++ b/Documentation/Tutorials/Meshes/example-triangles.vtu @@ -0,0 +1,23 @@ + + + + + + +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eJxjZR1pAAA/VwS7 + + + + + diff --git a/Documentation/Tutorials/Meshes/tutorial_Meshes.md b/Documentation/Tutorials/Meshes/tutorial_Meshes.md new file mode 100644 index 000000000..aaacd8c6f --- /dev/null +++ b/Documentation/Tutorials/Meshes/tutorial_Meshes.md @@ -0,0 +1,67 @@ +\page tutorial_Meshes Unstructured meshes tutorial + +[TOC] + +## Introduction + +The [Mesh](@ref TNL::Meshes::Mesh) class template is a data structure for _conforming unstructured homogeneous_ meshes, which can be used as the fundamental data structure for numerical schemes based on finite volume or finite element methods. +The abstract representation supports almost any cell shape which can be described by an [entity topology](@ref TNL::Meshes::Topologies). +Currently there are common 2D quadrilateral, 3D hexahedron and arbitrarily dimensional simplex topologies built in the library. +The implementation is highly configurable via templates of the C++ language, which allows to avoid the storage of unnecessary dynamic data. +The internal memory layout is based on state--of--the--art [sparse matrix formats](@ref TNL::Matrices), which are optimized for different hardware architectures in order to provide high performance computations. +The [DistributedMesh](@ref TNL::Meshes::DistributedMeshes::DistributedMesh) class template is an extended data structure based on `Mesh`, which allows to represent meshes decomposed into several subdomains for distributed computing using the Message Passing Interface (MPI). + +## Reading a mesh from a file + +The most common way of mesh initialization is by reading a prepared input file created by an external program. +TNL provides classes and functions for reading the common VTK, VTU and Netgen file formats. + +\dontinclude ReadMeshExample.cpp + +The main difficulty is mapping the mesh included in the file to the correct C++ type, which can represent the mesh stored in the file. +This can be done with the [MeshTypeResolver](@ref TNL::Meshes::MeshTypeResolver) class, which needs to be configured to enable the processing of the specific cell topologies, which we want our program to handle. +For example, in the following code we enable loading of 2D triangular and quadrangular meshes: + +\skip #include +\until // namespace TNL + +There are other build config tags which can be used to enable or disable specific types used in the mesh: `RealType`, `GlobalIndexType` and `LocalIndexType`. +See the [BuildConfigTags](@ref TNL::Meshes::BuildConfigTags) namespace for an overview of these tags. + +Next, we can define the main task of our program as a templated function, which will be ultimately launched with the correct mesh type based on the input file. +We can also use any number of additional parameters, such as the input file name: + +\skip Define the main task +\until } + +Of course in practice, the function would be much more complex than this example, where we just print the file name and some textual representation of the mesh to the standard output. + +Finally, we define the `main` function, which sets the input parameters (hard-coded in this example) and calls the [resolveAndLoadMesh](@ref TNL::Meshes::resolveAndLoadMesh) function to resolve the mesh type and load the mesh from the file into the created object: + +\skip int main +\until } +\until } + +We need to specify two template parameters when calling `resolveAndLoadMesh`: + +1. our build config tag (`MeshConfigTag` in this example), +2. and the [device](@ref TNL::Devices) where the mesh should be allocated. + +Then we pass the the function which should be called with the initialized mesh, the input file name, and the input file format (`"auto"` means auto-detection based on the file name). +In order to show the flexibility of passing other parameters to our main `task` function as defined above, we suggest to implement a wrapper lambda function (called `wrapper` in the example), which captures the relevant variables and forwards them to the `task`. + +The return value of the `resolveAndLoadMesh` function is a boolean value representing the success (`true`) or failure (`false`) of the whole function call chain. +Hence, the return type of both `wrapper` and `task` needs to be `bool` as well. + +For completeness, the full example follows: +\includelineno ReadMeshExample.cpp + +## Mesh configuration + +## Public interface and basic usage + +## Parallel iteration over mesh entities + +## Writing a mesh and data to a file + +## Example: Game of Life diff --git a/Documentation/Tutorials/index.md b/Documentation/Tutorials/index.md index 55b92ad81..e35e674c8 100644 --- a/Documentation/Tutorials/index.md +++ b/Documentation/Tutorials/index.md @@ -9,3 +9,4 @@ 5. [For loops](tutorial_ForLoops.html) 6. [Cross-device pointers](tutorial_Pointers.html) 7. [Matrices](tutorial_Matrices.html) +8. [Unstructured meshes](tutorial_Meshes.html) diff --git a/src/TNL/Meshes/BuildConfigTags.h b/src/TNL/Meshes/BuildConfigTags.h index bc8d903c0..b2c138e9e 100644 --- a/src/TNL/Meshes/BuildConfigTags.h +++ b/src/TNL/Meshes/BuildConfigTags.h @@ -22,11 +22,14 @@ namespace TNL { namespace Meshes { +/** + * \brief Namespace for the configuration of the \ref GridTypeResolver and + * \ref MeshTypeResolver using so-called build config tags and partial class + * template specializations. + */ namespace BuildConfigTags { -/**** - * Configuration for structured grids - */ +// Configuration for structured grids // 1, 2, and 3 dimensions are enabled by default template< typename ConfigTag, int Dimension > struct GridDimensionTag { enum { enabled = ( Dimension > 0 && Dimension <= 3 ) }; }; @@ -48,7 +51,7 @@ template< typename ConfigTag > struct GridIndexTag< ConfigTag, int > { enum { en template< typename ConfigTag > struct GridIndexTag< ConfigTag, long int > { enum { enabled = true }; }; // The Grid is enabled for allowed dimensions and Real, Device and Index types. -// +// // By specializing this tag you can enable or disable custom combinations of // the grid template parameters. The default configuration is identical to the // individual per-type tags. @@ -65,9 +68,7 @@ struct GridTag< ConfigTag, Grid< Dimension, Real, Device, Index > > }; -/**** - * Configuration for unstructured meshes - */ +// Configuration for unstructured meshes // Meshes are enabled on all available devices by default. template< typename ConfigTag, typename Device > struct MeshDeviceTag { enum { enabled = false }; }; diff --git a/src/TNL/Meshes/TypeResolver/TypeResolver.h b/src/TNL/Meshes/TypeResolver/TypeResolver.h index a4002eaae..55e151bd5 100644 --- a/src/TNL/Meshes/TypeResolver/TypeResolver.h +++ b/src/TNL/Meshes/TypeResolver/TypeResolver.h @@ -16,19 +16,23 @@ namespace TNL { namespace Meshes { -/* +/** * This function does the following (in pseudo-code): * - * using Reader = [based on file type detection] - * Reader reader; - * using MeshType = [black magic] - * MeshType mesh; - * return functor( reader, mesh ); + * \code + * using Reader = [based on file type detection] + * Reader reader; + * using MeshType = [black magic] + * MeshType mesh; + * return functor( reader, mesh ); + * \endcode * * The functor should be a generic lambda expression with the following * signature (or an equivalent functor): * - * auto functor = [] ( auto& reader, auto&& mesh ) -> bool {}; + * \code + * auto functor = [] ( auto& reader, auto&& mesh ) -> bool {}; + * \endcode */ template< typename ConfigTag, typename Device, @@ -38,18 +42,20 @@ resolveMeshType( Functor&& functor, const std::string& fileName, const std::string& fileFormat = "auto" ); -/* - * This function dues the same as `resolveMeshType`, but also reuses the mesh +/** + * This function dues the same as \ref resolveMeshType, but also reuses the mesh * reader instance to load the mesh before passing it to the functor. * In pseudo-code: * - * using Reader = [based on file type detection] - * Reader reader; - * using MeshType = [black magic] - * MeshType mesh; - * if( ! reader.readMesh( mesh ) ) - * return false; - * return functor( reader, mesh ); + * \code + * using Reader = [based on file type detection] + * Reader reader; + * using MeshType = [black magic] + * MeshType mesh; + * if( ! reader.readMesh( mesh ) ) + * return false; + * return functor( reader, mesh ); + * \endcode */ template< typename ConfigTag, typename Device, @@ -59,13 +65,13 @@ resolveAndLoadMesh( Functor&& functor, const std::string& fileName, const std::string& fileFormat = "auto" ); -/* +/** * This function takes a file name and a mesh instance and attempts to load the * mesh from the file into the object. * - * NOTE: The use of this function in combination with `resolveMeshType` should - * be avoided. Use `resolveAndLoadMesh` instead to reuse the mesh reader - * instance created in `resolveMeshType`. + * \remark The use of this function in combination with \ref resolveMeshType + * should be avoided. Use \ref resolveAndLoadMesh instead to reuse the mesh + * reader instance created in \ref resolveMeshType. */ template< typename MeshConfig, typename Device > -- GitLab From d1dab83860b03a317446394179f6bdf0f6800108 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Sun, 28 Mar 2021 22:44:37 +0200 Subject: [PATCH 03/19] Updated .gitignore and removed accidental file --- .gitignore | 7 + .../Tutorials/Arrays/main-page.md.bak | 138 ------------------ 2 files changed, 7 insertions(+), 138 deletions(-) delete mode 100644 Documentation/Tutorials/Arrays/main-page.md.bak diff --git a/.gitignore b/.gitignore index d22aa829e..4ec8d3ef7 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,11 @@ # VSCode /.vscode + +# Editors +*.orig +*.bak +*.backup + +# GDB .gdb_history diff --git a/Documentation/Tutorials/Arrays/main-page.md.bak b/Documentation/Tutorials/Arrays/main-page.md.bak deleted file mode 100644 index d7edc433d..000000000 --- a/Documentation/Tutorials/Arrays/main-page.md.bak +++ /dev/null @@ -1,138 +0,0 @@ -# Arrays tutorial - -## Introduction - -This tutorial introduces arrays in TNL. Array is one of the most important structure for memory management. Methods implemented in arrays are particularly usefull for GPU programming. From this point of view, the reader will learn how to easily allocate memory on GPU, transfer data between GPU and CPU but also, how to initialise data allocated on GPU. In addition, the resulting code is hardware platform independent, so it can be ran on CPU without any changes. - -## Arrays - -Array is templated class defined in namespace ```TNL::Containers``` having three template parameters: - -* ```Value``` is type of data to be stored in the array -* ```Device``` is the device wheer the array is allocated. Currently it can be either ```Devices::Host``` for CPU or ```Devices::Cuda``` for GPU supporting CUDA. -* ```Index``` is the type to be used for indexing the array elements. - -The following example shows how to allocate arrays on CPU and GPU and how to manipulate the data. - -\include ArrayAllocation.cpp - -The result looks as follows: - -\include ArrayAllocation.out - - -## Arrays binding - -Arrays can share data with each other or data allocated elsewhere. It is called binding and it can be done using method ```bind```. The following example shows how to bind data allocated on host using the ```new``` operator. In this case, the TNL array do not free this data at the and of its life cycle. - -\include ArrayBinding-1.cpp - -It generates output like this: - -\include ArrayBinding-1.out - -One may also bind another TNL array. In this case, the data is shared and can be shared between multiple arrays. Reference counter ensures that the data is freed after the last array sharing the data ends its life cycle. - -\include ArrayBinding-2.cpp - -The result is: - -\include ArrayBinding-2.out - -Binding may also serve for data partitioning. Both CPU and GPU prefere data allocated in large contiguous blocks instead of many fragmented pieces of allocated memory. Another reason why one might want to partition the allocated data is demonstrated in the following example. Consider a situation of solving incompressible flow in 2D. The degrees of freedom consist of density and two components of velocity. Mostly, we want to manipulate either density or velocity. But some numerical solvers may need to have all degrees of freedom in one array. It can be managed like this: - -\include ArrayBinding-3.cpp - -The result is: - -\include ArrayBinding-3.out - - -## Array views - -Because of the data sharing, TNL Array is relatively complicated structure. In many situations, we prefer lightweight structure which only encapsulates the data pointer and keeps information about the data size. Passing array structure to GPU kernel can be one example. For this purpose there is ```ArrayView``` in TNL. It templated structure having the same template parameters as ```Array``` (it means ```Value```, ```Device``` and ```Index```). In fact, it is recommended to use ```Array``` only for the data allocation and to use ```ArrayView``` for most of the operations with the data since array view offer better functionality (for example ```ArrayView``` can be captured by lambda functions in CUDA while ```Array``` cannot). The following code snippet shows how to create an array view. - -\include ArrayView-1.cpp - -Its output is: - -\include ArrayView-1.out - -Of course, one may bind his own data into array view: - -\include ArrayView-2.cpp - -Output: - -\include ArrayView-2.out - -Array view never allocated or deallocate the memory managed by it. Therefore it can be created even in CUDA kernels which is not true for ```Array```. - -## Accessing the array elements - -There are two ways how to work with the array (or array view) elements - using the indexing operator (```operator[]```) which is more efficient or methods ```setElement``` and ```getElement``` which is more flexible. - -### Accessing the array elements with ```operator[]``` - -Indexing operator ```operator[]``` is implemented in both ```Array``` and ```ArrayView``` and it is defined as ```__cuda_callable__```. It means that it can be called even in CUDA kernels if the data is allocated on GPU, i.e. the ```Device``` parameter is ```Devicess::Cuda```. This operator returns a reference to given array element and so it is very efficient. However, calling this operator from host for data allocated in device (or vice versa) leads to segmentation fault (on the host system) or broken state of the device. It means: - -* You may call the ```operator[]``` on the **host** only for data allocated on the **host** (with device ```Devices::Host```). -* You may call the ```operator[]``` on the **device** only for data allocated on the **device** (with device ```Devices::Cuda```). - -The following example shows use of ```operator[]```. - -\include ElementsAccessing-1.cpp - -Output: - -\include ElementsAccessing-1.out - -In general in TNL, each method defined as ```__cuda_callable__``` can be called from the CUDA kernels. The method ```ArrayView::getSize``` is another example. We also would like to point the reader to better ways of arrays initiation for example with method ```ArrayView::evaluate``` or with ```ParalleFor```. - -### Accessing the array element with ```setElement``` and ```getElement``` - -On the other hand, the methods ```setElement``` and ```getElement``` can be called **from the host only** no matter where the array is allocated. None of the methods can be used in CUDA kernels. ```getElement``` returns copy of an element rather than a reference. Therefore it is slightly slower. If the array is on GPU, the array element is copied from the device on the host (or vice versa) which is significantly slower. In those parts of code where the perfomance matters, these methods shall not be called. Their use is, however, much easier and they allow to write one simple code for both CPU and GPU. Both methods are good candidates for: - -* reading/wiriting of only few elements in the array -* arrays inititation which is done only once and it is not time critical part of a code -* debugging purposes - -The following example shows the use of ```getElement``` and ```setElement```: - -\include ElementsAccessing-2.cpp - -Output: - -\include ElementsAccessing-2.out - -## Arrays initiation with lambdas - -More eifficient and still quite simple method for the arrays initiation is with the use of C++ lambda functions and method ```evaluate```. This method is implemented in ```ArrayView``` only. As an argument a lambda function is passed which is then evaluated for all elemeents. Optionaly one may define only subinterval of element indexes where the lambda shall be evaluated. If the underlaying array is allocated on GPU, the lambda function is called from CUDA kernel. This is why it is more efficient than use of ```setElement```. On the other hand, one must be carefull to use only ```__cuda_callable__``` methods inside the lambda. The use of the method ```evaluate``` demonstrates the following example. - -\include ArrayViewEvaluate.cpp - -Output: - -\include ArrayViewEvaluate.out - -## Checking the array contents - -Methods ```containsValue``` and ```containsOnlyValue``` serve for testing the contents of the arrays. ```containsValue``` returns ```true``` of there is at least one element in the array with given value. ```containsOnlyValue``` returnd ```true``` only if all elements of the array equal given value. The test can be restricted to subinterval of array elements. Both methods are implemented in ```Array``` as well as in ```ArrayView```. See the following code snippet for example of use. - -\include ContainsValue.cpp - -Output: - -\include ContainsValue.out - -## IO operations with Arrays - -Methods ```save``` and ```load``` serve for storing/restoring the array to/from a file in binary form. In case of ```Array```, loading of an array from a file causes data reallocation. ```ArrayView``` cannot do reallocatation, therefore the data loaded from a file is copied to the memory managed by the ```ArrayView```. The number of elements managed by the array view and those loaded from the file must be equal. See the following example. - -\include ArrayIO.cpp - -Output: - -\include ArrayIO.out - - -- GitLab From 33e561c3a881088447e13fbb5450b23ba328001d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Mon, 29 Mar 2021 13:43:26 +0200 Subject: [PATCH 04/19] Removed the code for entity orientations in the unstructured mesh It was unused, untested and unnecessarily complicated. We will deal with entity orientations when it is actually needed. I think that the implementation based on storing the whole permutations of subentity indices is not appropriate anyway - it induces huge memory consumption, while e.g. for faces we are interested only in the orientation of the outward normal vector with respect to the two adjacent cells, which can be stored as one bit per face-cell pair, or it can be deduced from the entity indices if we enforce a suitable convention (e.g. all normal vectors could be outward with respect to the adjacent cell with the smallest index). I think OpenFOAM uses a similar convention for normal vectors. As for the orientation of edges in 3D, I have no idea where it could be useful... --- src/TNL/Meshes/DefaultConfig.h | 10 - src/TNL/Meshes/MeshDetails/ConfigValidator.h | 10 +- .../MeshDetails/MeshEntityOrientation.h | 51 ------ .../MeshEntityReferenceOrientation.h | 67 ------- .../initializer/EntityInitializer.h | 171 +---------------- .../MeshDetails/initializer/Initializer.h | 146 +-------------- .../Meshes/MeshDetails/layers/StorageLayer.h | 30 --- .../layers/SubentityOrientationsLayer.h | 172 ------------------ .../MeshDetails/traits/MeshEntityTraits.h | 15 -- .../MeshDetails/traits/MeshSubentityTraits.h | 10 - src/TNL/Meshes/MeshEntity.h | 5 - src/TNL/Meshes/MeshEntity.hpp | 14 -- src/Tools/tnl-decompose-mesh.cpp | 6 - src/Tools/tnl-game-of-life.cpp | 6 - src/Tools/tnl-mesh-converter.cpp | 6 - src/Tools/tnl-test-distributed-mesh.h | 6 - src/UnitTests/Meshes/EntityTagsTest.h | 1 - src/UnitTests/Meshes/MeshOrderingTest.h | 1 - src/UnitTests/Meshes/MeshTest.h | 4 - src/UnitTests/Meshes/MeshTraverserTest.h | 2 - 20 files changed, 14 insertions(+), 719 deletions(-) delete mode 100644 src/TNL/Meshes/MeshDetails/MeshEntityOrientation.h delete mode 100644 src/TNL/Meshes/MeshDetails/MeshEntityReferenceOrientation.h delete mode 100644 src/TNL/Meshes/MeshDetails/layers/SubentityOrientationsLayer.h diff --git a/src/TNL/Meshes/DefaultConfig.h b/src/TNL/Meshes/DefaultConfig.h index e4eaba905..a07ef511b 100644 --- a/src/TNL/Meshes/DefaultConfig.h +++ b/src/TNL/Meshes/DefaultConfig.h @@ -51,16 +51,6 @@ struct DefaultConfig //return SubentityDimension == 0; } - /**** - * Storage of subentity orientations of mesh entities. - * It must be false for vertices and cells. - */ - template< typename EntityTopology > - static constexpr bool subentityOrientationStorage( EntityTopology, int SubentityDimension ) - { - return SubentityDimension > 0 && SubentityDimension < meshDimension; - } - /**** * Storage of superentities of mesh entities. */ diff --git a/src/TNL/Meshes/MeshDetails/ConfigValidator.h b/src/TNL/Meshes/MeshDetails/ConfigValidator.h index fa35ba4d0..0680026f0 100644 --- a/src/TNL/Meshes/MeshDetails/ConfigValidator.h +++ b/src/TNL/Meshes/MeshDetails/ConfigValidator.h @@ -34,18 +34,12 @@ class ConfigValidatorSubtopologyLayer static_assert( ! MeshConfig::subentityStorage( EntityTopology(), DimensionTag::value ) || MeshConfig::subentityStorage( EntityTopology(), 0 ), "entities that are stored as subentities must store their subvertices" ); - static_assert( ! MeshConfig::subentityOrientationStorage( EntityTopology(), DimensionTag::value ) || - MeshConfig::subentityStorage( EntityTopology(), DimensionTag::value ), - "orientation can be stored only for subentities that are stored"); }; template< typename MeshConfig, typename EntityTopology > class ConfigValidatorSubtopologyLayer< MeshConfig, EntityTopology, DimensionTag< 0 > > -{ - static_assert( ! MeshConfig::subentityOrientationStorage( EntityTopology(), 0 ), - "storage of vertex orientation does not make sense" ); -}; +{}; template< typename MeshConfig, @@ -95,8 +89,6 @@ class ConfigValidatorLayerCell static_assert( MeshConfig::subentityStorage( CellTopology(), 0 ), "subvertices of cells must be stored" ); - static_assert( ! MeshConfig::subentityOrientationStorage( CellTopology(), 0 ), - "storage of cell orientation does not make sense" ); }; template< typename MeshConfig > diff --git a/src/TNL/Meshes/MeshDetails/MeshEntityOrientation.h b/src/TNL/Meshes/MeshDetails/MeshEntityOrientation.h deleted file mode 100644 index 10132513a..000000000 --- a/src/TNL/Meshes/MeshDetails/MeshEntityOrientation.h +++ /dev/null @@ -1,51 +0,0 @@ -/*************************************************************************** - MeshEntityOrientation.h - description - ------------------- - begin : Aug 25, 2015 - copyright : (C) 2015 by Tomas Oberhuber et al. - email : tomas.oberhuber@fjfi.cvut.cz - ***************************************************************************/ - -/* See Copyright Notice in tnl/Copyright */ - -/*** - * Authors: - * Oberhuber Tomas, tomas.oberhuber@fjfi.cvut.cz - * Zabka Vitezslav, zabkav@gmail.com - */ - -#pragma once - -#include - -namespace TNL { -namespace Meshes { - -template< typename MeshConfig, - typename EntityTopology> -class MeshEntityOrientation -{ - template< typename, typename> - friend class MeshEntityReferenceOrientation; - - public: - using LocalIndexType = typename MeshTraits< MeshConfig >::LocalIndexType; - using IdPermutationArrayType = typename MeshTraits< MeshConfig >::template SubentityTraits< EntityTopology, 0 >::IdPermutationArrayType; - - const IdPermutationArrayType& getSubvertexPermutation() const - { - return subvertexPermutation; - } - - private: - void setPermutationValue( LocalIndexType index, LocalIndexType value ) - { - this->subvertexPermutation[ index ] = value; - } - - IdPermutationArrayType subvertexPermutation; -}; - -} // namespace Meshes -} // namespace TNL - diff --git a/src/TNL/Meshes/MeshDetails/MeshEntityReferenceOrientation.h b/src/TNL/Meshes/MeshDetails/MeshEntityReferenceOrientation.h deleted file mode 100644 index 80340c62c..000000000 --- a/src/TNL/Meshes/MeshDetails/MeshEntityReferenceOrientation.h +++ /dev/null @@ -1,67 +0,0 @@ -/*************************************************************************** - MeshEntityReferenceOrientation.h - description - ------------------- - begin : Aug 25, 2015 - copyright : (C) 2015 by Tomas Oberhuber et al. - email : tomas.oberhuber@fjfi.cvut.cz - ***************************************************************************/ - -/* See Copyright Notice in tnl/Copyright */ - -/*** - * Authors: - * Oberhuber Tomas, tomas.oberhuber@fjfi.cvut.cz - * Zabka Vitezslav, zabkav@gmail.com - */ - -#pragma once - -#include - -#include - -namespace TNL { -namespace Meshes { - -template< typename MeshConfig, typename EntityTopology > -class MeshEntityReferenceOrientation -{ - typedef typename MeshTraits< MeshConfig >::LocalIndexType LocalIndexType; - typedef typename MeshTraits< MeshConfig >::GlobalIndexType GlobalIndexType; - - public: - typedef EntitySeed< MeshConfig, EntityTopology > SeedType; - typedef MeshEntityOrientation< MeshConfig, EntityTopology > EntityOrientation; - - MeshEntityReferenceOrientation() = default; - - explicit MeshEntityReferenceOrientation( const SeedType& referenceSeed ) - { - auto referenceCornerIds = referenceSeed.getCornerIds(); - for( LocalIndexType i = 0; i < referenceCornerIds.getSize(); i++ ) - { - TNL_ASSERT_TRUE( this->cornerIdsMap.find( referenceCornerIds[ i ] ) == this->cornerIdsMap.end(), - "detected duplicate index in the reference seed" ); - this->cornerIdsMap.insert( std::make_pair( referenceCornerIds[i], i ) ); - } - } - - EntityOrientation createOrientation( const SeedType& seed ) const - { - EntityOrientation result; - auto cornerIds = seed.getCornerIds(); - for( LocalIndexType i = 0; i < cornerIds.getSize(); i++ ) - { - TNL_ASSERT_TRUE( this->cornerIdsMap.find( cornerIds[ i ] ) != this->cornerIdsMap.end(), - "unable to find index for entity orientation" ); - result.setPermutationValue( i, this->cornerIdsMap.find( cornerIds[ i ])->second ); - } - return result; - } - - private: - std::map< GlobalIndexType, LocalIndexType > cornerIdsMap; -}; - -} // namespace Meshes -} // namespace TNL diff --git a/src/TNL/Meshes/MeshDetails/initializer/EntityInitializer.h b/src/TNL/Meshes/MeshDetails/initializer/EntityInitializer.h index 0ee9f2e36..498dc27fe 100644 --- a/src/TNL/Meshes/MeshDetails/initializer/EntityInitializer.h +++ b/src/TNL/Meshes/MeshDetails/initializer/EntityInitializer.h @@ -32,10 +32,6 @@ template< typename MeshConfig, bool SubentityStorage = MeshConfig::subentityStorage( typename MeshTraits< MeshConfig >::template EntityTraits< SuperdimensionTag::value >::EntityTopology(), SubdimensionTag::value ), - bool SubentityOrientationStorage = - MeshConfig::subentityOrientationStorage( typename MeshTraits< MeshConfig >::template EntityTraits< SuperdimensionTag::value >::EntityTopology(), - SubdimensionTag::value ) && - MeshTraits< MeshConfig >::template EntityTraits< SubdimensionTag::value >::orientationNeeded, // storage in the subentity bool SuperentityStorage = MeshConfig::superentityStorage( typename MeshTraits< MeshConfig >::template EntityTraits< SubdimensionTag::value >::EntityTopology(), @@ -99,8 +95,8 @@ public: /**** * Mesh entity initializer layer with specializations * - * SUBENTITY STORAGE SUBENTITY ORIENTATION SUPERENTITY STORAGE - * TRUE FALSE TRUE + * SUBENTITY STORAGE SUPERENTITY STORAGE + * TRUE TRUE */ template< typename MeshConfig, typename SubdimensionTag, @@ -109,7 +105,6 @@ class EntityInitializerLayer< MeshConfig, SubdimensionTag, SuperdimensionTag, true, - false, true, true > : public EntityInitializerLayer< MeshConfig, @@ -183,8 +178,8 @@ public: /**** * Mesh entity initializer layer with specializations * - * SUBENTITY STORAGE SUBENTITY ORIENTATION SUPERENTITY STORAGE - * TRUE TRUE TRUE + * SUBENTITY STORAGE SUPERENTITY STORAGE + * TRUE FALSE */ template< typename MeshConfig, typename SubdimensionTag, @@ -193,156 +188,6 @@ class EntityInitializerLayer< MeshConfig, SubdimensionTag, SuperdimensionTag, true, - true, - true, - true > - : public EntityInitializerLayer< MeshConfig, - SubdimensionTag, - typename SuperdimensionTag::Decrement > -{ - using BaseType = EntityInitializerLayer< MeshConfig, - SubdimensionTag, - typename SuperdimensionTag::Decrement >; - using InitializerType = Initializer< MeshConfig >; - using MeshType = typename InitializerType::MeshType; - - using GlobalIndexType = typename MeshTraits< MeshConfig >::GlobalIndexType; - using LocalIndexType = typename MeshTraits< MeshConfig >::LocalIndexType; - using SubentityTraitsType = typename MeshTraits< MeshConfig >::template EntityTraits< SubdimensionTag::value >; - using SubentityTopology = typename SubentityTraitsType::EntityTopology; - using SuperentityTraitsType = typename MeshTraits< MeshConfig >::template EntityTraits< SuperdimensionTag::value >; - using SuperentityTopology = typename SuperentityTraitsType::EntityTopology; - using SubentitySeedsCreatorType = SubentitySeedsCreator< MeshConfig, SuperdimensionTag, SubdimensionTag >; - using SuperentityMatrixType = typename MeshTraits< MeshConfig >::SuperentityMatrixType; - -public: - static void initSuperentities( InitializerType& meshInitializer, MeshType& mesh ) - { - //std::cout << " Initiating superentities with dimension " << SuperdimensionTag::value << " for subentities with dimension " << SubdimensionTag::value << " ... " << std::endl; - - const GlobalIndexType subentitiesCount = mesh.template getEntitiesCount< SubdimensionTag::value >(); - const GlobalIndexType superentitiesCount = mesh.template getEntitiesCount< SuperdimensionTag::value >(); - if( SubdimensionTag::value > 0 ) - meshInitializer.template initSubentityMatrix< SuperdimensionTag::value, SubdimensionTag::value >( superentitiesCount, subentitiesCount ); - - // counter for superentities of each subentity - auto& superentitiesCounts = meshInitializer.template getSuperentitiesCountsArray< SubdimensionTag::value, SuperdimensionTag::value >(); - superentitiesCounts.setSize( subentitiesCount ); - superentitiesCounts.setValue( 0 ); - - for( GlobalIndexType superentityIndex = 0; superentityIndex < superentitiesCount; superentityIndex++ ) - { - auto subentitySeeds = SubentitySeedsCreatorType::create( meshInitializer.template getSubvertices< SuperdimensionTag::value >( superentityIndex ) ); - auto& subentityOrientationsArray = meshInitializer.template subentityOrientationsArray< SuperdimensionTag::value, SubdimensionTag::value >( superentityIndex ); - for( LocalIndexType i = 0; i < subentitySeeds.getSize(); i++ ) - { - const GlobalIndexType subentityIndex = meshInitializer.findEntitySeedIndex( subentitySeeds[ i ] ); - meshInitializer.template setSubentityIndex< SuperdimensionTag::value, SubdimensionTag::value >( superentityIndex, i, subentityIndex ); - superentitiesCounts[ subentityIndex ]++; - subentityOrientationsArray[ i ] = meshInitializer.template getReferenceOrientation< SubdimensionTag >( subentityIndex ).createOrientation( subentitySeeds[ i ] ); - } - } - - // allocate superentities storage - SuperentityMatrixType& matrix = meshInitializer.template getSuperentitiesMatrix< SubdimensionTag::value, SuperdimensionTag::value >(); - matrix.setDimensions( subentitiesCount, superentitiesCount ); - matrix.setRowCapacities( superentitiesCounts ); - superentitiesCounts.setValue( 0 ); - - // initialize superentities storage - for( GlobalIndexType superentityIndex = 0; superentityIndex < superentitiesCount; superentityIndex++ ) - { - for( LocalIndexType i = 0; - i < mesh.template getSubentitiesCount< SuperdimensionTag::value, SubdimensionTag::value >( superentityIndex ); - i++ ) - { - const GlobalIndexType subentityIndex = mesh.template getSubentityIndex< SuperdimensionTag::value, SubdimensionTag::value >( superentityIndex, i ); - auto row = matrix.getRow( subentityIndex ); - row.setElement( superentitiesCounts[ subentityIndex ]++, superentityIndex, true ); - } - } - - BaseType::initSuperentities( meshInitializer, mesh ); - } -}; - -/**** - * Mesh entity initializer layer with specializations - * - * SUBENTITY STORAGE SUBENTITY ORIENTATION SUPERENTITY STORAGE - * TRUE TRUE FALSE - */ -template< typename MeshConfig, - typename SubdimensionTag, - typename SuperdimensionTag > -class EntityInitializerLayer< MeshConfig, - SubdimensionTag, - SuperdimensionTag, - true, - true, - false, - true > - : public EntityInitializerLayer< MeshConfig, - SubdimensionTag, - typename SuperdimensionTag::Decrement > -{ - using BaseType = EntityInitializerLayer< MeshConfig, - SubdimensionTag, - typename SuperdimensionTag::Decrement >; - using InitializerType = Initializer< MeshConfig >; - using MeshType = typename InitializerType::MeshType; - - using GlobalIndexType = typename MeshTraits< MeshConfig >::GlobalIndexType; - using LocalIndexType = typename MeshTraits< MeshConfig >::LocalIndexType; - using SuperentityTraitsType = typename MeshTraits< MeshConfig >::template EntityTraits< SuperdimensionTag::value >; - using SuperentityTopology = typename SuperentityTraitsType::EntityTopology; - using SubentitySeedsCreatorType = SubentitySeedsCreator< MeshConfig, SuperdimensionTag, SubdimensionTag >; - -public: - static void initSuperentities( InitializerType& meshInitializer, MeshType& mesh ) - { - //std::cout << " Initiating superentities with dimension " << SuperdimensionTag::value << " for subentities with dimension " << SubdimensionTag::value << " ... " << std::endl; - - const GlobalIndexType subentitiesCount = mesh.template getEntitiesCount< SubdimensionTag::value >(); - const GlobalIndexType superentitiesCount = mesh.template getEntitiesCount< SuperdimensionTag::value >(); - if( SubdimensionTag::value > 0 ) - meshInitializer.template initSubentityMatrix< SuperdimensionTag::value, SubdimensionTag::value >( superentitiesCount, subentitiesCount ); - - for( GlobalIndexType superentityIndex = 0; - superentityIndex < mesh.template getEntitiesCount< SuperdimensionTag::value >(); - superentityIndex++ ) - { - auto subentitySeeds = SubentitySeedsCreatorType::create( meshInitializer.template getSubvertices< SuperdimensionTag::value >( superentityIndex ) ); - - auto& subentityOrientationsArray = meshInitializer.template subentityOrientationsArray< SuperdimensionTag::value, SubdimensionTag::value >( superentityIndex ); - - for( LocalIndexType i = 0; i < subentitySeeds.getSize(); i++ ) - { - const GlobalIndexType subentityIndex = meshInitializer.findEntitySeedIndex( subentitySeeds[ i ] ); - meshInitializer.template setSubentityIndex< SuperdimensionTag::value, SubdimensionTag::value >( superentityIndex, i, subentityIndex ); - - subentityOrientationsArray[ i ] = meshInitializer.template getReferenceOrientation< SubdimensionTag >( subentityIndex ).createOrientation( subentitySeeds[ i ] ); - } - } - - BaseType::initSuperentities( meshInitializer, mesh ); - } -}; - -/**** - * Mesh entity initializer layer with specializations - * - * SUBENTITY STORAGE SUBENTITY ORIENTATION SUPERENTITY STORAGE - * TRUE FALSE FALSE - */ -template< typename MeshConfig, - typename SubdimensionTag, - typename SuperdimensionTag > -class EntityInitializerLayer< MeshConfig, - SubdimensionTag, - SuperdimensionTag, - true, - false, false, true > : public EntityInitializerLayer< MeshConfig, @@ -391,8 +236,8 @@ public: /**** * Mesh entity initializer layer with specializations * - * SUBENTITY STORAGE SUBENTITY ORIENTATION SUPERENTITY STORAGE - * FALSE FALSE TRUE + * SUBENTITY STORAGE SUPERENTITY STORAGE + * FALSE TRUE */ template< typename MeshConfig, typename SubdimensionTag, @@ -401,7 +246,6 @@ class EntityInitializerLayer< MeshConfig, SubdimensionTag, SuperdimensionTag, false, - false, true, true > : public EntityInitializerLayer< MeshConfig, @@ -476,7 +320,6 @@ class EntityInitializerLayer< MeshConfig, SuperdimensionTag, false, false, - false, true > : public EntityInitializerLayer< MeshConfig, SubdimensionTag, @@ -486,13 +329,11 @@ class EntityInitializerLayer< MeshConfig, template< typename MeshConfig, typename SubdimensionTag, bool SubentityStorage, - bool SubentityOrientationStorage, bool SuperentityStorage > class EntityInitializerLayer< MeshConfig, SubdimensionTag, SubdimensionTag, SubentityStorage, - SubentityOrientationStorage, SuperentityStorage, false > { diff --git a/src/TNL/Meshes/MeshDetails/initializer/Initializer.h b/src/TNL/Meshes/MeshDetails/initializer/Initializer.h index 79c2cc5b9..f8d5775ca 100644 --- a/src/TNL/Meshes/MeshDetails/initializer/Initializer.h +++ b/src/TNL/Meshes/MeshDetails/initializer/Initializer.h @@ -19,7 +19,6 @@ #include #include #include -#include #include /* @@ -37,8 +36,7 @@ * - For all dimensions D from (cell dimension - 1) to 1: * - Create intermediate entity seeds, count the number of entities with * current dimension. - * - Set their subvertex indices. Create an indexed set of entity seeds and - * reference orientations (if applicable). + * - Set their subvertex indices. Create an indexed set of entity seeds. * - For all superdimensions S > D: * - Iterate over entities with dimension S and initialize their * subentity indices with dimension D. Inverse mapping (D->S) is @@ -61,16 +59,13 @@ namespace TNL { namespace Meshes { template< typename MeshConfig, - typename DimensionTag, - bool EntityReferenceOrientationStorage = - MeshTraits< MeshConfig >::template EntityTraits< DimensionTag::value >::orientationNeeded > + typename DimensionTag > class InitializerLayer; template< typename MeshConfig > class Initializer - : public InitializerLayer< MeshConfig, - typename MeshTraits< MeshConfig >::DimensionTag > + : public InitializerLayer< MeshConfig, typename MeshTraits< MeshConfig >::DimensionTag > { protected: // must be declared before its use in expression with decltype() @@ -153,32 +148,13 @@ class Initializer { return mesh->template getSuperentitiesMatrix< Dimension, Superdimension >(); } - - - template< int Dimension, int Subdimension > - auto - subentityOrientationsArray( const GlobalIndexType entityIndex ) - -> decltype( this->mesh->template subentityOrientationsArray< Dimension, Subdimension >( entityIndex ) ) - { - return mesh->template subentityOrientationsArray< Dimension, Subdimension >( entityIndex ); - } - - template< typename DimensionTag > - const MeshEntityReferenceOrientation< MeshConfig, typename MeshTraitsType::template EntityTraits< DimensionTag::value >::EntityTopology >& - getReferenceOrientation( const GlobalIndexType index ) const - { - return BaseType::getReferenceOrientation( DimensionTag(), index ); - } }; /**** * Mesh initializer layer for cells - * - entities orientation does not make sense for cells => it is turned off */ template< typename MeshConfig > -class InitializerLayer< MeshConfig, - typename MeshTraits< MeshConfig >::DimensionTag, - false > +class InitializerLayer< MeshConfig, typename MeshTraits< MeshConfig >::DimensionTag > : public InitializerLayer< MeshConfig, typename MeshTraits< MeshConfig >::DimensionTag::Decrement > { @@ -214,14 +190,11 @@ class InitializerLayer< MeshConfig, }; /**** - * Mesh initializer layer for other mesh entities than cells - * - entities orientation storage is turned off + * Mesh initializer layer for mesh entities other than cells */ template< typename MeshConfig, typename DimensionTag > -class InitializerLayer< MeshConfig, - DimensionTag, - false > +class InitializerLayer : public InitializerLayer< MeshConfig, typename DimensionTag::Decrement > { @@ -292,117 +265,15 @@ class InitializerLayer< MeshConfig, BaseType::initEntities( initializer, mesh ); } - using BaseType::getReferenceOrientation; - void getReferenceOrientation( DimensionTag, GlobalIndexType index ) const {} - - private: - SeedIndexedSet seedsIndexedSet; -}; - -/**** - * Mesh initializer layer for other mesh entities than cells - * - entities orientation storage is turned on - */ -template< typename MeshConfig, - typename DimensionTag > -class InitializerLayer< MeshConfig, - DimensionTag, - true > - : public InitializerLayer< MeshConfig, - typename DimensionTag::Decrement > -{ - using BaseType = InitializerLayer< MeshConfig, typename DimensionTag::Decrement >; - using MeshType = Mesh< MeshConfig >; - using MeshTraitsType = typename MeshType::MeshTraitsType; - - using EntityTraitsType = typename MeshType::template EntityTraits< DimensionTag::value >; - using EntityTopology = typename EntityTraitsType::EntityTopology; - using GlobalIndexType = typename MeshTraitsType::GlobalIndexType; - using LocalIndexType = typename MeshTraitsType::LocalIndexType; - - using InitializerType = Initializer< MeshConfig >; - using EntityInitializerType = EntityInitializer< MeshConfig, EntityTopology >; - using SeedType = EntitySeed< MeshConfig, EntityTopology >; - using SeedIndexedSet = typename MeshTraits< MeshConfig >::template EntityTraits< DimensionTag::value >::SeedIndexedSetType; - using SeedSet = typename MeshTraits< MeshConfig >::template EntityTraits< DimensionTag::value >::SeedSetType; - using ReferenceOrientationType = typename EntityTraitsType::ReferenceOrientationType; - using ReferenceOrientationArrayType = typename EntityTraitsType::ReferenceOrientationArrayType; - - public: - - GlobalIndexType getEntitiesCount( InitializerType& initializer, MeshType& mesh ) - { - using SubentitySeedsCreator = SubentitySeedsCreator< MeshConfig, Meshes::DimensionTag< MeshType::getMeshDimension() >, DimensionTag >; - SeedSet seedSet; - - for( GlobalIndexType i = 0; i < mesh.template getEntitiesCount< MeshType::getMeshDimension() >(); i++ ) - { - auto subentitySeeds = SubentitySeedsCreator::create( initializer.template getSubvertices< MeshType::getMeshDimension() >( i ) ); - for( LocalIndexType j = 0; j < subentitySeeds.getSize(); j++ ) - seedSet.insert( subentitySeeds[ j ] ); - } - - return seedSet.size(); - } - - using BaseType::findEntitySeedIndex; - GlobalIndexType findEntitySeedIndex( const SeedType& seed ) - { - return this->seedsIndexedSet.insert( seed ); - } - - void initEntities( InitializerType& initializer, MeshType& mesh ) - { - //std::cout << " Initiating entities with dimension " << DimensionTag::value << " ... " << std::endl; - const GlobalIndexType numberOfEntities = getEntitiesCount( initializer, mesh ); - initializer.template setEntitiesCount< DimensionTag::value >( numberOfEntities ); - EntityInitializerType::initSubvertexMatrix( numberOfEntities, initializer ); - this->referenceOrientations.resize( numberOfEntities ); - - using SubentitySeedsCreator = SubentitySeedsCreator< MeshConfig, Meshes::DimensionTag< MeshType::getMeshDimension() >, DimensionTag >; - for( GlobalIndexType i = 0; i < mesh.template getEntitiesCount< MeshType::getMeshDimension() >(); i++ ) - { - auto subentitySeeds = SubentitySeedsCreator::create( initializer.template getSubvertices< MeshType::getMeshDimension() >( i ) ); - for( LocalIndexType j = 0; j < subentitySeeds.getSize(); j++ ) - { - auto& seed = subentitySeeds[ j ]; - const auto pair = this->seedsIndexedSet.try_insert( seed ); - const GlobalIndexType& entityIndex = pair.first; - if( pair.second ) { - // insertion took place, initialize the entity - EntityInitializerType::initEntity( entityIndex, seed, initializer ); - this->referenceOrientations[ entityIndex ] = ReferenceOrientationType( seed ); - } - } - } - - EntityInitializerType::initSuperentities( initializer, mesh ); - this->seedsIndexedSet.clear(); - this->referenceOrientations.clear(); - this->referenceOrientations.shrink_to_fit(); - - BaseType::initEntities( initializer, mesh ); - } - - using BaseType::getReferenceOrientation; - const ReferenceOrientationType& getReferenceOrientation( DimensionTag, GlobalIndexType index ) const - { - return this->referenceOrientations[ index ]; - } - private: SeedIndexedSet seedsIndexedSet; - ReferenceOrientationArrayType referenceOrientations; }; /**** * Mesh initializer layer for vertices - * - their orientation does not make sense */ template< typename MeshConfig > -class InitializerLayer< MeshConfig, - DimensionTag< 0 >, - false > +class InitializerLayer< MeshConfig, DimensionTag< 0 > > { using MeshType = Mesh< MeshConfig >; using MeshTraitsType = typename MeshType::MeshTraitsType; @@ -429,9 +300,6 @@ class InitializerLayer< MeshConfig, //std::cout << " Initiating entities with dimension " << DimensionTag::value << " ... " << std::endl; EntityInitializerType::initSuperentities( initializer, mesh ); } - - // This method is due to 'using BaseType::getReferenceOrientation;' in the derived class. - void getReferenceOrientation( DimensionTag, GlobalIndexType index ) const {} }; } // namespace Meshes diff --git a/src/TNL/Meshes/MeshDetails/layers/StorageLayer.h b/src/TNL/Meshes/MeshDetails/layers/StorageLayer.h index f92729532..b6404eb68 100644 --- a/src/TNL/Meshes/MeshDetails/layers/StorageLayer.h +++ b/src/TNL/Meshes/MeshDetails/layers/StorageLayer.h @@ -19,7 +19,6 @@ #include #include #include -#include #include #include @@ -223,26 +222,6 @@ public: typename EntityTraits< Dimension >::EntityTopology >; return BaseType::template getSuperentitiesMatrix< Superdimension >(); } - - template< int Dimension, int Subdimension > - __cuda_callable__ - typename SubentityTraits< Dimension, Subdimension >::IdPermutationArrayType - getSubentityOrientation( typename MeshTraitsType::GlobalIndexType entityIndex, typename MeshTraitsType::LocalIndexType localIndex ) const - { - static_assert( SubentityTraits< Dimension, Subdimension >::orientationEnabled, - "You try to get subentity orientation which is not configured for storage." ); - return BaseType::getSubentityOrientation( DimensionTag< Dimension >(), DimensionTag< Subdimension >(), entityIndex, localIndex ); - } - - template< int Dimension, int Subdimension > - __cuda_callable__ - typename SubentityTraits< Dimension, Subdimension >::OrientationArrayType& - subentityOrientationsArray( typename MeshTraitsType::GlobalIndexType entityIndex ) - { - static_assert( SubentityTraits< Dimension, Subdimension >::orientationEnabled, - "You try to get subentity orientation which is not configured for storage." ); - return BaseType::subentityOrientationsArray( DimensionTag< Dimension >(), DimensionTag< Subdimension >(), entityIndex ); - } }; @@ -256,9 +235,6 @@ class StorageLayer< MeshConfig, : public SubentityStorageLayerFamily< MeshConfig, Device, typename MeshTraits< MeshConfig, Device >::template EntityTraits< DimensionTag::value >::EntityTopology >, - public SubentityOrientationsLayerFamily< MeshConfig, - Device, - typename MeshTraits< MeshConfig, Device >::template EntityTraits< DimensionTag::value >::EntityTopology >, public SuperentityStorageLayerFamily< MeshConfig, Device, typename MeshTraits< MeshConfig, Device >::template EntityTraits< DimensionTag::value >::EntityTopology >, @@ -272,12 +248,8 @@ public: using EntityType = typename EntityTraitsType::EntityType; using EntityTopology = typename EntityTraitsType::EntityTopology; using SubentityStorageBaseType = SubentityStorageLayerFamily< MeshConfig, Device, EntityTopology >; - using SubentityOrientationsBaseType = SubentityOrientationsLayerFamily< MeshConfig, Device, EntityTopology >; using SuperentityStorageBaseType = SuperentityStorageLayerFamily< MeshConfig, Device, EntityTopology >; - using BaseType::subentityOrientationsArray; - using SubentityOrientationsBaseType::subentityOrientationsArray; - StorageLayer() = default; explicit StorageLayer( const StorageLayer& other ) @@ -356,7 +328,6 @@ protected: void setEntitiesCount( DimensionTag, const GlobalIndexType& entitiesCount ) { this->entitiesCount = entitiesCount; - SubentityOrientationsBaseType::setEntitiesCount( entitiesCount ); } GlobalIndexType entitiesCount = 0; @@ -393,7 +364,6 @@ protected: } - void subentityOrientationsArray() {} void setEntitiesCount() {} void getEntitiesCount() const {} diff --git a/src/TNL/Meshes/MeshDetails/layers/SubentityOrientationsLayer.h b/src/TNL/Meshes/MeshDetails/layers/SubentityOrientationsLayer.h deleted file mode 100644 index 18a137547..000000000 --- a/src/TNL/Meshes/MeshDetails/layers/SubentityOrientationsLayer.h +++ /dev/null @@ -1,172 +0,0 @@ -/*************************************************************************** - SubentityOrientationsLayer.h - description - ------------------- - begin : Mar 24, 2020 - copyright : (C) 2020 by Tomas Oberhuber et al. - email : tomas.oberhuber@fjfi.cvut.cz - ***************************************************************************/ - -/* See Copyright Notice in tnl/Copyright */ - -#pragma once - -#include -#include -#include -#include - -namespace TNL { -namespace Meshes { - -template< typename MeshConfig, - typename Device, - typename EntityTopology, - typename SubdimensionTag, - bool SubentityOrientationStorage = - WeakSubentityStorageTrait< MeshConfig, Device, EntityTopology, SubdimensionTag >::storageEnabled && - MeshConfig::subentityOrientationStorage( EntityTopology(), SubdimensionTag::value ) > -class SubentityOrientationsLayer; - - -template< typename MeshConfig, - typename Device, - typename EntityTopology > -class SubentityOrientationsLayerFamily - : public SubentityOrientationsLayer< MeshConfig, - Device, - EntityTopology, - Meshes::DimensionTag< 0 > > -{ - using BaseType = SubentityOrientationsLayer< MeshConfig, - Device, - EntityTopology, - Meshes::DimensionTag< 0 > >; -public: - using BaseType::subentityOrientationsArray; - using BaseType::getSubentityOrientation; -}; - - -template< typename MeshConfig, - typename Device, - typename EntityTopology, - typename SubdimensionTag > -class SubentityOrientationsLayer< MeshConfig, - Device, - EntityTopology, - SubdimensionTag, - true > - : public SubentityOrientationsLayer< MeshConfig, - Device, - EntityTopology, - typename SubdimensionTag::Increment > -{ - using BaseType = SubentityOrientationsLayer< MeshConfig, - Device, - EntityTopology, - typename SubdimensionTag::Increment >; - - using MeshTraitsType = MeshTraits< MeshConfig, Device >; - using SubentityTraitsType = typename MeshTraitsType::template SubentityTraits< EntityTopology, SubdimensionTag::value >; - using DimensionTag = Meshes::DimensionTag< EntityTopology::dimension >; - -protected: - using GlobalIndexType = typename MeshTraitsType::GlobalIndexType; - using LocalIndexType = typename MeshTraitsType::LocalIndexType; - using OrientationArrayType = typename SubentityTraitsType::OrientationArrayType; - using IdPermutationArrayType = typename SubentityTraitsType::IdPermutationArrayType; - using OrientationsStorageArrayType = typename SubentityTraitsType::OrientationsStorageArrayType; - - void setEntitiesCount( const GlobalIndexType entitiesCount ) - { - orientations.setSize( entitiesCount ); - BaseType::setEntitiesCount( entitiesCount ); - } - - using BaseType::getSubentityOrientation; - __cuda_callable__ - const IdPermutationArrayType& getSubentityOrientation( DimensionTag, SubdimensionTag, GlobalIndexType entityIndex, LocalIndexType localIndex ) const - { - return orientations[ entityIndex ][ localIndex ].getSubvertexPermutation(); - } - - using BaseType::subentityOrientationsArray; - __cuda_callable__ - OrientationArrayType& subentityOrientationsArray( DimensionTag, SubdimensionTag, GlobalIndexType entityIndex ) - { - return orientations[ entityIndex ]; - } - -private: - OrientationsStorageArrayType orientations; -}; - -template< typename MeshConfig, - typename Device, - typename EntityTopology, - typename SubdimensionTag > -class SubentityOrientationsLayer< MeshConfig, - Device, - EntityTopology, - SubdimensionTag, - false > - : public SubentityOrientationsLayer< MeshConfig, - Device, - EntityTopology, - typename SubdimensionTag::Increment > -{ - using BaseType = SubentityOrientationsLayer< MeshConfig, - Device, - EntityTopology, - typename SubdimensionTag::Increment >; - - using MeshTraitsType = MeshTraits< MeshConfig, Device >; - using DimensionTag = Meshes::DimensionTag< EntityTopology::dimension >; - -protected: - using GlobalIndexType = typename MeshTraitsType::GlobalIndexType; - using LocalIndexType = typename MeshTraitsType::LocalIndexType; - - void setEntitiesCount( const GlobalIndexType entitiesCount ) - { - BaseType::setEntitiesCount( entitiesCount ); - } - - using BaseType::getSubentityOrientation; - using BaseType::subentityOrientationsArray; - - __cuda_callable__ - void getSubentityOrientation( DimensionTag, SubdimensionTag, GlobalIndexType entityIndex, LocalIndexType localIndex ) const {} - - __cuda_callable__ - void subentityOrientationsArray( DimensionTag, SubdimensionTag, GlobalIndexType entityIndex ) {} -}; - -// termination of recursive inheritance (everything is reduced to EntityStorage == false thanks to the WeakSubentityStorageTrait) -template< typename MeshConfig, - typename Device, - typename EntityTopology > -class SubentityOrientationsLayer< MeshConfig, - Device, - EntityTopology, - DimensionTag< EntityTopology::dimension >, - false > -{ -protected: - using GlobalIndexType = typename MeshConfig::GlobalIndexType; - using LocalIndexType = typename MeshConfig::LocalIndexType; - using DimensionTag = Meshes::DimensionTag< EntityTopology::dimension >; - using SubdimensionTag = Meshes::DimensionTag< EntityTopology::dimension >; - - /*** - * Necessary because of 'using BaseType::...;' in the derived classes - */ - void setEntitiesCount( GlobalIndexType ) {} - __cuda_callable__ - void getSubentityOrientation( DimensionTag, SubdimensionTag, GlobalIndexType, LocalIndexType ) const {} - __cuda_callable__ - void subentityOrientationsArray( DimensionTag, SubdimensionTag, GlobalIndexType ) {} -}; - -} // namespace Meshes -} // namespace TNL diff --git a/src/TNL/Meshes/MeshDetails/traits/MeshEntityTraits.h b/src/TNL/Meshes/MeshDetails/traits/MeshEntityTraits.h index 4e1115daf..2bf7f856f 100644 --- a/src/TNL/Meshes/MeshDetails/traits/MeshEntityTraits.h +++ b/src/TNL/Meshes/MeshDetails/traits/MeshEntityTraits.h @@ -28,7 +28,6 @@ namespace TNL { namespace Meshes { template< typename MeshConfig, typename Device, typename EntityTopology > class MeshEntity; -template< typename MeshConfig, typename EntityTopology > class MeshEntityReferenceOrientation; template< typename MeshConfig, typename DimensionTag > @@ -52,29 +51,15 @@ class MeshEntityTraits { using GlobalIndexType = typename MeshConfig::GlobalIndexType; - static constexpr bool checkOrientationNeeded() - { - if( Dimension == 0 || Dimension == MeshConfig::meshDimension ) - return false; - for( int d = 1; d < Dimension; d++ ) - if( MeshConfig::subentityOrientationStorage( EntityTopology(), d ) ) - return true; - return false; - } - public: static_assert( 0 <= Dimension && Dimension <= MeshConfig::meshDimension, "invalid dimension" ); using EntityTopology = typename EntityTopologyGetter< MeshConfig, DimensionTag< Dimension > >::Topology; using EntityType = MeshEntity< MeshConfig, Device, EntityTopology >; using SeedType = EntitySeed< MeshConfig, EntityTopology >; - using ReferenceOrientationType = MeshEntityReferenceOrientation< MeshConfig, EntityTopology >; using SeedIndexedSetType = Containers::UnorderedIndexedSet< SeedType, GlobalIndexType, typename SeedType::HashType, typename SeedType::KeyEqual >; using SeedSetType = std::unordered_set< typename SeedIndexedSetType::key_type, typename SeedIndexedSetType::hasher, typename SeedIndexedSetType::key_equal >; - using ReferenceOrientationArrayType = std::vector< ReferenceOrientationType >; - - static constexpr bool orientationNeeded = checkOrientationNeeded(); }; } // namespace Meshes diff --git a/src/TNL/Meshes/MeshDetails/traits/MeshSubentityTraits.h b/src/TNL/Meshes/MeshDetails/traits/MeshSubentityTraits.h index 570671931..b5d3df4d5 100644 --- a/src/TNL/Meshes/MeshDetails/traits/MeshSubentityTraits.h +++ b/src/TNL/Meshes/MeshDetails/traits/MeshSubentityTraits.h @@ -23,9 +23,6 @@ namespace TNL { namespace Meshes { -template< typename MeshConfig, typename EntityTopology > -class MeshEntityOrientation; - template< typename MeshConfig, typename Device, typename EntityTopology, @@ -40,17 +37,10 @@ public: static_assert( EntityTopology::dimension > Dimension, "Subentity dimension must be smaller than the entity dimension." ); static constexpr bool storageEnabled = MeshConfig::subentityStorage( EntityTopology(), Dimension ); - static constexpr bool orientationEnabled = MeshConfig::subentityOrientationStorage( EntityTopology(), Dimension ); static constexpr int count = Topologies::Subtopology< EntityTopology, Dimension >::count; using SubentityTopology = typename MeshEntityTraits< MeshConfig, Device, Dimension >::EntityTopology; using SubentityType = typename MeshEntityTraits< MeshConfig, Device, Dimension >::EntityType; - using Orientation = MeshEntityOrientation< MeshConfig, SubentityTopology >; - - // orientation and its accessor - using OrientationArrayType = Containers::StaticArray< count, Orientation >; - using IdPermutationArrayType = Containers::StaticArray< count, LocalIndexType >; - using OrientationsStorageArrayType = Containers::Array< OrientationArrayType, Device, GlobalIndexType >; template< LocalIndexType subentityIndex, LocalIndexType subentityVertexIndex > diff --git a/src/TNL/Meshes/MeshEntity.h b/src/TNL/Meshes/MeshEntity.h index ba91bc170..ab4410304 100644 --- a/src/TNL/Meshes/MeshEntity.h +++ b/src/TNL/Meshes/MeshEntity.h @@ -87,11 +87,6 @@ class MeshEntity __cuda_callable__ GlobalIndexType getSubentityIndex( const LocalIndexType localIndex ) const; - template< int Subdimension > - __cuda_callable__ - auto getSubentityOrientation( const LocalIndexType localIndex ) const - -> const typename SubentityTraits< Subdimension >::OrientationArrayType&; - /**** * Superentities */ diff --git a/src/TNL/Meshes/MeshEntity.hpp b/src/TNL/Meshes/MeshEntity.hpp index 67ee08377..03f811e48 100644 --- a/src/TNL/Meshes/MeshEntity.hpp +++ b/src/TNL/Meshes/MeshEntity.hpp @@ -124,20 +124,6 @@ getSubentityIndex( const LocalIndexType localIndex ) const return meshPointer->template getSubentityIndex< getEntityDimension(), Subdimension >( this->getIndex(), localIndex ); } -template< typename MeshConfig, - typename Device, - typename EntityTopology > - template< int Subdimension > -__cuda_callable__ -auto -MeshEntity< MeshConfig, Device, EntityTopology >:: -getSubentityOrientation( const LocalIndexType localIndex ) const - -> const typename SubentityTraits< Subdimension >::OrientationArrayType& -{ - TNL_ASSERT_TRUE( meshPointer, "meshPointer was not set" ); - return meshPointer->template getSubentityOrientation< getEntityDimension(), Subdimension >( this->getIndex(), localIndex ); -} - template< typename MeshConfig, typename Device, typename EntityTopology > diff --git a/src/Tools/tnl-decompose-mesh.cpp b/src/Tools/tnl-decompose-mesh.cpp index 53ca27b33..358ef92d7 100644 --- a/src/Tools/tnl-decompose-mesh.cpp +++ b/src/Tools/tnl-decompose-mesh.cpp @@ -87,12 +87,6 @@ struct MeshConfigTemplateTag< DecomposeMeshConfigTag > return SubentityDimension == 0 && EntityTopology::dimension >= meshDimension - 1; } - template< typename EntityTopology > - static constexpr bool subentityOrientationStorage( EntityTopology, int SubentityDimension ) - { - return false; - } - template< typename EntityTopology > static constexpr bool superentityStorage( EntityTopology, int SuperentityDimension ) { diff --git a/src/Tools/tnl-game-of-life.cpp b/src/Tools/tnl-game-of-life.cpp index 7003489ab..b825af4d5 100644 --- a/src/Tools/tnl-game-of-life.cpp +++ b/src/Tools/tnl-game-of-life.cpp @@ -86,12 +86,6 @@ struct MeshConfigTemplateTag< MyConfigTag > return SubentityDimension == 0 && EntityTopology::dimension >= meshDimension - 1; } - template< typename EntityTopology > - static constexpr bool subentityOrientationStorage( EntityTopology, int SubentityDimension ) - { - return false; - } - template< typename EntityTopology > static constexpr bool superentityStorage( EntityTopology, int SuperentityDimension ) { diff --git a/src/Tools/tnl-mesh-converter.cpp b/src/Tools/tnl-mesh-converter.cpp index 3d82d8552..f1a845984 100644 --- a/src/Tools/tnl-mesh-converter.cpp +++ b/src/Tools/tnl-mesh-converter.cpp @@ -80,12 +80,6 @@ struct MeshConfigTemplateTag< MeshConverterConfigTag > return SubentityDimension == 0 && EntityTopology::dimension == meshDimension; } - template< typename EntityTopology > - static constexpr bool subentityOrientationStorage( EntityTopology, int SubentityDimension ) - { - return false; - } - template< typename EntityTopology > static constexpr bool superentityStorage( EntityTopology, int SuperentityDimension ) { diff --git a/src/Tools/tnl-test-distributed-mesh.h b/src/Tools/tnl-test-distributed-mesh.h index 6b748d993..5ecaf52c2 100644 --- a/src/Tools/tnl-test-distributed-mesh.h +++ b/src/Tools/tnl-test-distributed-mesh.h @@ -88,12 +88,6 @@ struct MeshConfigTemplateTag< MyConfigTag > || SubentityDimension == meshDimension - 1; } - template< typename EntityTopology > - static constexpr bool subentityOrientationStorage( EntityTopology, int SubentityDimension ) - { - return false; - } - template< typename EntityTopology > static constexpr bool superentityStorage( EntityTopology, int SuperentityDimension ) { diff --git a/src/UnitTests/Meshes/EntityTagsTest.h b/src/UnitTests/Meshes/EntityTagsTest.h index d2f3546bb..855e29249 100644 --- a/src/UnitTests/Meshes/EntityTagsTest.h +++ b/src/UnitTests/Meshes/EntityTagsTest.h @@ -25,7 +25,6 @@ class TestQuadrangleMeshConfig : public DefaultConfig< Topologies::Quadrangle > public: static constexpr bool entityStorage( int dimensions ) { return true; } template< typename EntityTopology > static constexpr bool subentityStorage( EntityTopology, int SubentityDimensions ) { return true; } - template< typename EntityTopology > static constexpr bool subentityOrientationStorage( EntityTopology, int SubentityDimensions ) { return ( SubentityDimensions % 2 != 0 ); } template< typename EntityTopology > static constexpr bool superentityStorage( EntityTopology, int SuperentityDimensions ) { return true; } template< typename EntityTopology > static constexpr bool boundaryTagsStorage( EntityTopology ) { return true; } }; diff --git a/src/UnitTests/Meshes/MeshOrderingTest.h b/src/UnitTests/Meshes/MeshOrderingTest.h index 634f94454..4d5d2e512 100644 --- a/src/UnitTests/Meshes/MeshOrderingTest.h +++ b/src/UnitTests/Meshes/MeshOrderingTest.h @@ -22,7 +22,6 @@ class TestTriangleMeshConfig public: static constexpr bool entityStorage( int dimensions ) { return true; } template< typename EntityTopology > static constexpr bool subentityStorage( EntityTopology, int SubentityDimensions ) { return true; } - //template< typename EntityTopology > static constexpr bool subentityOrientationStorage( EntityTopology, int SubentityDimensions ) { return true; } template< typename EntityTopology > static constexpr bool superentityStorage( EntityTopology, int SuperentityDimensions ) { return true; } }; diff --git a/src/UnitTests/Meshes/MeshTest.h b/src/UnitTests/Meshes/MeshTest.h index 9d132e296..38ab992ce 100644 --- a/src/UnitTests/Meshes/MeshTest.h +++ b/src/UnitTests/Meshes/MeshTest.h @@ -32,7 +32,6 @@ class TestTriangleMeshConfig : public DefaultConfig< Topologies::Triangle > public: static constexpr bool entityStorage( int dimensions ) { return true; } template< typename EntityTopology > static constexpr bool subentityStorage( EntityTopology, int SubentityDimensions ) { return true; } - //template< typename EntityTopology > static constexpr bool subentityOrientationStorage( EntityTopology, int SubentityDimensions ) { return true; } template< typename EntityTopology > static constexpr bool superentityStorage( EntityTopology, int SuperentityDimensions ) { return true; } }; @@ -41,7 +40,6 @@ class TestQuadrangleMeshConfig : public DefaultConfig< Topologies::Quadrangle > public: static constexpr bool entityStorage( int dimensions ) { return true; } template< typename EntityTopology > static constexpr bool subentityStorage( EntityTopology, int SubentityDimensions ) { return true; } - template< typename EntityTopology > static constexpr bool subentityOrientationStorage( EntityTopology, int SubentityDimensions ) { return ( SubentityDimensions % 2 != 0 ); } template< typename EntityTopology > static constexpr bool superentityStorage( EntityTopology, int SuperentityDimensions ) { return true; } }; @@ -50,7 +48,6 @@ class TestTetrahedronMeshConfig : public DefaultConfig< Topologies::Tetrahedron public: static constexpr bool entityStorage( int dimensions ) { return true; } template< typename EntityTopology > static constexpr bool subentityStorage( EntityTopology, int SubentityDimensions ) { return true; } - template< typename EntityTopology > static constexpr bool subentityOrientationStorage( EntityTopology, int SubentityDimensions ) { return ( SubentityDimensions % 2 != 0 ); } template< typename EntityTopology > static constexpr bool superentityStorage( EntityTopology, int SuperentityDimensions ) { return true; } }; @@ -59,7 +56,6 @@ class TestHexahedronMeshConfig : public DefaultConfig< Topologies::Hexahedron > public: static constexpr bool entityStorage( int dimensions ) { return true; } template< typename EntityTopology > static constexpr bool subentityStorage( EntityTopology, int SubentityDimensions ) { return true; } - template< typename EntityTopology > static constexpr bool subentityOrientationStorage( EntityTopology, int SubentityDimensions ) { return ( SubentityDimensions % 2 != 0 ); } template< typename EntityTopology > static constexpr bool superentityStorage( EntityTopology, int SuperentityDimensions ) { return true; } }; diff --git a/src/UnitTests/Meshes/MeshTraverserTest.h b/src/UnitTests/Meshes/MeshTraverserTest.h index 0af13e5dc..2a0a9ce9a 100644 --- a/src/UnitTests/Meshes/MeshTraverserTest.h +++ b/src/UnitTests/Meshes/MeshTraverserTest.h @@ -25,7 +25,6 @@ class TestQuadrangleMeshConfig : public DefaultConfig< Topologies::Quadrangle > public: static constexpr bool entityStorage( int dimensions ) { return true; } template< typename EntityTopology > static constexpr bool subentityStorage( EntityTopology, int SubentityDimensions ) { return true; } - template< typename EntityTopology > static constexpr bool subentityOrientationStorage( EntityTopology, int SubentityDimensions ) { return ( SubentityDimensions % 2 != 0 ); } template< typename EntityTopology > static constexpr bool superentityStorage( EntityTopology, int SuperentityDimensions ) { return true; } }; @@ -34,7 +33,6 @@ class TestHexahedronMeshConfig : public DefaultConfig< Topologies::Hexahedron > public: static constexpr bool entityStorage( int dimensions ) { return true; } template< typename EntityTopology > static constexpr bool subentityStorage( EntityTopology, int SubentityDimensions ) { return true; } - template< typename EntityTopology > static constexpr bool subentityOrientationStorage( EntityTopology, int SubentityDimensions ) { return ( SubentityDimensions % 2 != 0 ); } template< typename EntityTopology > static constexpr bool superentityStorage( EntityTopology, int SuperentityDimensions ) { return true; } }; -- GitLab From 53521a322a4563f6be1025d89ae4673a606c8e43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Mon, 29 Mar 2021 22:28:32 +0200 Subject: [PATCH 05/19] Documentation: writing mesh tutorial --- .../Tutorials/Meshes/tutorial_Meshes.md | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/Documentation/Tutorials/Meshes/tutorial_Meshes.md b/Documentation/Tutorials/Meshes/tutorial_Meshes.md index aaacd8c6f..480dfee0a 100644 --- a/Documentation/Tutorials/Meshes/tutorial_Meshes.md +++ b/Documentation/Tutorials/Meshes/tutorial_Meshes.md @@ -58,8 +58,50 @@ For completeness, the full example follows: ## Mesh configuration +The [Mesh](@ref TNL::Meshes::Mesh) class template is configurable via its first template parameter, `Config`. +By default, the \ref TNL::Meshes::DefaultConfig template is used. +Alternative, user-specified configuration templates can be specified by defining the mesh configuration as the `MeshConfig` template in the [MeshConfigTemplateTag](@ref TNL::Meshes::BuildConfigTags::MeshConfigTemplateTag) build config tag specialization. +For example, here we derive the `MeshConfig` template from the [DefaultConfig](@ref TNL::Meshes::DefaultConfig) template and override the `subentityStorage` member function to store only those subentity incidence matrices, where the subentity dimension is 0 and the other dimension is at least $D-1$. +Hence, only faces and cells will be able to access their subvertices and there will be no other links from entities to their subentities. + + +```cpp +// Define the tag for the MeshTypeResolver configuration +struct MyConfigTag {}; + +namespace TNL { +namespace Meshes { +namespace BuildConfigTags { + +// Create a template specialization of the tag specifying the MeshConfig template to use as the Config parameter for the mesh. +template<> +struct MeshConfigTemplateTag< MyConfigTag > +{ + template< typename Cell, + int WorldDimension = Cell::dimension, + typename Real = double, + typename GlobalIndex = int, + typename LocalIndex = GlobalIndex > + struct MeshConfig + : public DefaultConfig< Cell, WorldDimension, Real, GlobalIndex, LocalIndex > + { + template< typename EntityTopology > + static constexpr bool subentityStorage( EntityTopology, int SubentityDimension ) + { + return SubentityDimension == 0 && EntityTopology::dimension >= Cell::dimension - 1; + } + }; +}; + +} // namespace BuildConfigTags +} // namespace Meshes +} // namespace TNL +``` + ## Public interface and basic usage +The whole public interface of the unstructured mesh and its mesh entity class can be found in the reference manual: \ref TNL::Meshes::Mesh, \ref TNL::Meshes::MeshEntity. + ## Parallel iteration over mesh entities ## Writing a mesh and data to a file -- GitLab From 46b43a4985fbe28df0d3e5cb8fa8f4eb28d400e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Mon, 29 Mar 2021 22:30:57 +0200 Subject: [PATCH 06/19] tnl-game-of-life: simplified mesh configuration --- src/Tools/tnl-game-of-life.cpp | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/Tools/tnl-game-of-life.cpp b/src/Tools/tnl-game-of-life.cpp index b825af4d5..792271eb7 100644 --- a/src/Tools/tnl-game-of-life.cpp +++ b/src/Tools/tnl-game-of-life.cpp @@ -71,33 +71,26 @@ struct MeshConfigTemplateTag< MyConfigTag > typename GlobalIndex = int, typename LocalIndex = GlobalIndex > struct MeshConfig + : public DefaultConfig< Cell, WorldDimension, Real, GlobalIndex, LocalIndex > { - using CellTopology = Cell; - using RealType = Real; - using GlobalIndexType = GlobalIndex; - using LocalIndexType = LocalIndex; - - static constexpr int worldDimension = WorldDimension; - static constexpr int meshDimension = Cell::dimension; - template< typename EntityTopology > static constexpr bool subentityStorage( EntityTopology, int SubentityDimension ) { - return SubentityDimension == 0 && EntityTopology::dimension >= meshDimension - 1; + return SubentityDimension == 0 && EntityTopology::dimension >= Cell::dimension - 1; } template< typename EntityTopology > static constexpr bool superentityStorage( EntityTopology, int SuperentityDimension ) { // return false; - return (EntityTopology::dimension == 0 || EntityTopology::dimension == meshDimension - 1) && SuperentityDimension == meshDimension; + return (EntityTopology::dimension == 0 || EntityTopology::dimension == Cell::dimension - 1) && SuperentityDimension == Cell::dimension; } template< typename EntityTopology > static constexpr bool entityTagsStorage( EntityTopology ) { // return false; - return EntityTopology::dimension == 0 || EntityTopology::dimension >= meshDimension - 1; + return EntityTopology::dimension == 0 || EntityTopology::dimension >= Cell::dimension - 1; } static constexpr bool dualGraphStorage() -- GitLab From 3c09f614056e980d69646b9d3c3ff4e90f21e7e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Mon, 29 Mar 2021 23:26:56 +0200 Subject: [PATCH 07/19] tnl-game-of-life: removed unnecessary MeshFunction --- src/Tools/tnl-game-of-life.cpp | 86 ++++++++++------------------------ 1 file changed, 25 insertions(+), 61 deletions(-) diff --git a/src/Tools/tnl-game-of-life.cpp b/src/Tools/tnl-game-of-life.cpp index 792271eb7..f62a20f19 100644 --- a/src/Tools/tnl-game-of-life.cpp +++ b/src/Tools/tnl-game-of-life.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -123,53 +122,20 @@ bool runGameOfLife( const Mesh& mesh ) Synchronizer sync; sync.initialize( mesh ); - // TODO - // simple mesh function without SharedPointer for the mesh - using Real = std::uint8_t; - struct MyMeshFunction - { - using MeshType = LocalMesh; - using RealType = Real; - using DeviceType = typename LocalMesh::DeviceType; - using IndexType = typename LocalMesh::GlobalIndexType; - using VectorType = Containers::Vector< RealType, DeviceType, IndexType >; - - static constexpr int getEntitiesDimension() { return Mesh::getMeshDimension(); } - - static constexpr int getMeshDimension() { return Mesh::getMeshDimension(); } - - MyMeshFunction( const LocalMesh& localMesh ) - { - data.setSize( localMesh.template getEntitiesCount< getEntitiesDimension() >() ); - } - - const VectorType& getData() const - { - return data; - } - - VectorType& getData() - { - return data; - } - - private: - VectorType data; - }; - - MyMeshFunction f_in( localMesh ), f_out( localMesh ); - f_in.getData().setValue( 0 ); - const Index pointsCount = localMesh.template getEntitiesCount< 0 >(); const Index cellsCount = localMesh.template getEntitiesCount< Mesh::getMeshDimension() >(); + using VectorType = Containers::Vector< std::uint8_t, typename LocalMesh::DeviceType, Index >; + VectorType f_in( cellsCount ), f_out( cellsCount ); + f_in.setValue( 0 ); + /* // random initial condition std::random_device dev; std::mt19937 rng(dev()); std::uniform_int_distribution<> dist(0, 1); for( Index i = 0; i < cellsCount; i++ ) - f_in.getData()[ i ] = dist(rng); + f_in.[ i ] = dist(rng); sync.synchronize( f_in ); */ // find the rank which contains most points in the box between (0.45, 0.45) and (0.55, 0.55) @@ -204,35 +170,33 @@ bool runGameOfLife( const Mesh& mesh ) // R-pentomino (stabilizes after 1103 iterations) const Index max_iter = 1103; if( count == max_count ) { - auto& v = f_in.getData(); - v[reference_cell] = 1; + f_in[reference_cell] = 1; Index n1 = localMesh.getCellNeighborIndex(reference_cell,1); // bottom Index n2 = localMesh.getCellNeighborIndex(reference_cell,2); // left Index n3 = localMesh.getCellNeighborIndex(reference_cell,5); // top Index n4 = localMesh.getCellNeighborIndex(reference_cell,6); // top-right - v[n1] = 1; - v[n2] = 1; - v[n3] = 1; - v[n4] = 1; + f_in[n1] = 1; + f_in[n2] = 1; + f_in[n3] = 1; + f_in[n4] = 1; } /* // Acorn (stabilizes after 5206 iterations) const Index max_iter = 5206; if( count == max_count ) { - auto& v = f_in.getData(); - v[reference_cell] = 1; + f_in[reference_cell] = 1; Index n1 = localMesh.getCellNeighborIndex(reference_cell,4); - v[n1] = 1; + f_in[n1] = 1; Index s1 = localMesh.getCellNeighborIndex(n1,4); Index s2 = localMesh.getCellNeighborIndex(s1,4); Index n2 = localMesh.getCellNeighborIndex(s2,4); - v[n2] = 1; + f_in[n2] = 1; Index n3 = localMesh.getCellNeighborIndex(n2,4); - v[n3] = 1; + f_in[n3] = 1; Index n4 = localMesh.getCellNeighborIndex(n3,4); - v[n4] = 1; - v[localMesh.getCellNeighborIndex(s2,5)] = 1; - v[localMesh.getCellNeighborIndex(localMesh.getCellNeighborIndex(n1,5),5)] = 1; + f_in[n4] = 1; + f_in[localMesh.getCellNeighborIndex(s2,5)] = 1; + f_in[localMesh.getCellNeighborIndex(localMesh.getCellNeighborIndex(n1,5),5)] = 1; } */ @@ -250,7 +214,7 @@ bool runGameOfLife( const Mesh& mesh ) // the PointData and CellData from the individual files should be added here if( mesh.getGhostLevels() > 0 ) pvtu.template writePCellData< std::uint8_t >( Meshes::VTK::ghostArrayName() ); - pvtu.template writePCellData< Real >( "function values" ); + pvtu.template writePCellData< typename VectorType::RealType >( "function values" ); const std::string subfilePath = pvtu.addPiece( mainFilePath, mesh.getCommunicationGroup() ); // create a .vtu file for local data @@ -261,15 +225,15 @@ bool runGameOfLife( const Mesh& mesh ) writer.template writeEntities< LocalMesh::getMeshDimension() >( localMesh ); if( mesh.getGhostLevels() > 0 ) writer.writeCellData( mesh.vtkCellGhostTypes(), Meshes::VTK::ghostArrayName() ); - writer.writeCellData( f_in.getData(), "function values" ); + writer.writeCellData( f_in, "function values" ); }; // write initial state make_snapshot( 0 ); // captures for the iteration kernel - const auto f_in_view = f_in.getData().getConstView(); - auto f_out_view = f_out.getData().getView(); + const auto f_in_view = f_in.getConstView(); + auto f_out_view = f_out.getView(); Pointers::DevicePointer< const LocalMesh > localMeshDevicePointer( localMesh ); const LocalMesh* localMeshPointer = &localMeshDevicePointer.template getData< typename LocalMesh::DeviceType >(); @@ -284,7 +248,7 @@ bool runGameOfLife( const Mesh& mesh ) auto kernel = [f_in_view, f_out_view, localMeshPointer] __cuda_callable__ ( Index i ) mutable { // sum values of the function on the neighbor entities - Real sum = 0; + typename VectorType::RealType sum = 0; for( Index n = 0; n < localMeshPointer->getCellNeighborsCount( i ); n++ ) { const Index neighbor = localMeshPointer->getCellNeighborIndex( i, n ); sum += f_in_view[ neighbor ]; @@ -312,17 +276,17 @@ bool runGameOfLife( const Mesh& mesh ) f_out_view[ i ] = 0; } }; - localMesh.template forLocal< MyMeshFunction::getEntitiesDimension() >( kernel ); + localMesh.template forLocal< Mesh::getMeshDimension() >( kernel ); // synchronize sync.synchronize( f_out ); // swap input and output arrays - f_in.getData().swap( f_out.getData() ); + f_in.swap( f_out ); // write output make_snapshot( iteration ); // check if finished - const bool done = max( f_in.getData() ) == 0 || iteration > max_iter || f_in.getData() == f_out.getData(); + const bool done = max( f_in ) == 0 || iteration > max_iter || f_in == f_out; TNL::MPI::Allreduce( &done, &all_done, 1, MPI_LAND, mesh.getCommunicationGroup() ); } while( all_done == false ); -- GitLab From 0eac3aaf1137482c739ec519a416eee39c8352ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Tue, 30 Mar 2021 19:54:47 +0200 Subject: [PATCH 08/19] Refactoring: renamed TypeResolver.h to resolveMeshType.h and loadDistributedMesh.h to resolveDistributedMeshType.h --- .../Tutorials/Meshes/ReadMeshExample.cpp | 2 +- .../Meshes/TypeResolver/GridTypeResolver.h | 2 +- ...peResolver_impl.h => GridTypeResolver.hpp} | 2 +- .../Meshes/TypeResolver/MeshTypeResolver.h | 2 +- ...peResolver_impl.h => MeshTypeResolver.hpp} | 2 +- .../TypeResolver/resolveDistributedMeshType.h | 80 +++++++++++++++++++ .../resolveDistributedMeshType.hpp} | 13 ++- .../{TypeResolver.h => resolveMeshType.h} | 4 +- ...ypeResolver_impl.h => resolveMeshType.hpp} | 4 +- .../Solvers/PDE/TimeDependentPDESolver_impl.h | 4 +- .../PDE/TimeIndependentPDESolver_impl.h | 4 +- src/TNL/Solvers/SolverInitiator_impl.h | 4 +- src/Tools/tnl-decompose-mesh.cpp | 2 +- src/Tools/tnl-game-of-life.cpp | 3 +- src/Tools/tnl-grid-to-mesh.cpp | 2 +- src/Tools/tnl-mesh-converter.cpp | 2 +- src/Tools/tnl-test-distributed-mesh.h | 3 +- src/Tools/tnl-view.cpp | 2 +- src/Tools/tnl-view.h | 7 +- 19 files changed, 110 insertions(+), 34 deletions(-) rename src/TNL/Meshes/TypeResolver/{GridTypeResolver_impl.h => GridTypeResolver.hpp} (99%) rename src/TNL/Meshes/TypeResolver/{MeshTypeResolver_impl.h => MeshTypeResolver.hpp} (99%) create mode 100644 src/TNL/Meshes/TypeResolver/resolveDistributedMeshType.h rename src/TNL/Meshes/{DistributedMeshes/loadDistributedMesh.h => TypeResolver/resolveDistributedMeshType.hpp} (94%) rename src/TNL/Meshes/TypeResolver/{TypeResolver.h => resolveMeshType.h} (96%) rename src/TNL/Meshes/TypeResolver/{TypeResolver_impl.h => resolveMeshType.hpp} (98%) diff --git a/Documentation/Tutorials/Meshes/ReadMeshExample.cpp b/Documentation/Tutorials/Meshes/ReadMeshExample.cpp index ff2f41222..9508ee1f5 100644 --- a/Documentation/Tutorials/Meshes/ReadMeshExample.cpp +++ b/Documentation/Tutorials/Meshes/ReadMeshExample.cpp @@ -1,4 +1,4 @@ -#include +#include // Define the tag for the MeshTypeResolver configuration struct MeshConfigTag {}; diff --git a/src/TNL/Meshes/TypeResolver/GridTypeResolver.h b/src/TNL/Meshes/TypeResolver/GridTypeResolver.h index 3a23a8028..79682788a 100644 --- a/src/TNL/Meshes/TypeResolver/GridTypeResolver.h +++ b/src/TNL/Meshes/TypeResolver/GridTypeResolver.h @@ -91,4 +91,4 @@ protected: } // namespace Meshes } // namespace TNL -#include +#include diff --git a/src/TNL/Meshes/TypeResolver/GridTypeResolver_impl.h b/src/TNL/Meshes/TypeResolver/GridTypeResolver.hpp similarity index 99% rename from src/TNL/Meshes/TypeResolver/GridTypeResolver_impl.h rename to src/TNL/Meshes/TypeResolver/GridTypeResolver.hpp index 817d2b23f..69fe4e03f 100644 --- a/src/TNL/Meshes/TypeResolver/GridTypeResolver_impl.h +++ b/src/TNL/Meshes/TypeResolver/GridTypeResolver.hpp @@ -1,5 +1,5 @@ /*************************************************************************** - GridTypeResolver_impl.h - description + GridTypeResolver.hpp - description ------------------- begin : Nov 22, 2016 copyright : (C) 2016 by Tomas Oberhuber et al. diff --git a/src/TNL/Meshes/TypeResolver/MeshTypeResolver.h b/src/TNL/Meshes/TypeResolver/MeshTypeResolver.h index a8b1ecb4a..00a949f5e 100644 --- a/src/TNL/Meshes/TypeResolver/MeshTypeResolver.h +++ b/src/TNL/Meshes/TypeResolver/MeshTypeResolver.h @@ -143,4 +143,4 @@ protected: } // namespace Meshes } // namespace TNL -#include +#include diff --git a/src/TNL/Meshes/TypeResolver/MeshTypeResolver_impl.h b/src/TNL/Meshes/TypeResolver/MeshTypeResolver.hpp similarity index 99% rename from src/TNL/Meshes/TypeResolver/MeshTypeResolver_impl.h rename to src/TNL/Meshes/TypeResolver/MeshTypeResolver.hpp index 2afb9eb7b..803cf84e9 100644 --- a/src/TNL/Meshes/TypeResolver/MeshTypeResolver_impl.h +++ b/src/TNL/Meshes/TypeResolver/MeshTypeResolver.hpp @@ -1,5 +1,5 @@ /*************************************************************************** - MeshTypeResolver_impl.h - description + MeshTypeResolver.hpp - description ------------------- begin : Nov 22, 2016 copyright : (C) 2016 by Tomas Oberhuber et al. diff --git a/src/TNL/Meshes/TypeResolver/resolveDistributedMeshType.h b/src/TNL/Meshes/TypeResolver/resolveDistributedMeshType.h new file mode 100644 index 000000000..a62424ef3 --- /dev/null +++ b/src/TNL/Meshes/TypeResolver/resolveDistributedMeshType.h @@ -0,0 +1,80 @@ +/*************************************************************************** + resolveDistributedMesh.h - description + ------------------- + begin : Apr 9, 2020 + copyright : (C) 2020 by Tomas Oberhuber et al. + email : tomas.oberhuber@fjfi.cvut.cz + ***************************************************************************/ + +/* See Copyright Notice in tnl/Copyright */ + +#pragma once + +#include +#include +#include + +namespace TNL { +namespace Meshes { + +template< typename ConfigTag, + typename Device, + typename Functor > +bool +resolveDistributedMeshType( Functor&& functor, + const std::string& fileName, + const std::string& fileFormat = "auto" ); + +template< typename ConfigTag, + typename Device, + typename Functor > +bool +resolveAndLoadDistributedMesh( Functor&& functor, + const std::string& fileName, + const std::string& fileFormat = "auto" ); + +template< typename MeshConfig, + typename Device > +bool +loadDistributedMesh( Mesh< MeshConfig, Device >& mesh, + DistributedMeshes::DistributedMesh< Mesh< MeshConfig, Device > >& distributedMesh, + const std::string& fileName, + const std::string& fileFormat = "auto" ); + +template< typename Problem, + typename MeshConfig, + typename Device > +bool +decomposeMesh( const Config::ParameterContainer& parameters, + const std::string& prefix, + Mesh< MeshConfig, Device >& mesh, + DistributedMeshes::DistributedMesh< Mesh< MeshConfig, Device > >& distributedMesh, + Problem& problem ); + +// overloads for grids +template< int Dimension, + typename Real, + typename Device, + typename Index > +bool +loadDistributedMesh( Grid< Dimension, Real, Device, Index >& mesh, + DistributedMeshes::DistributedMesh< Grid< Dimension, Real, Device, Index > > &distributedMesh, + const std::string& fileName, + const std::string& fileFormat = "auto" ); + +template< typename Problem, + int Dimension, + typename Real, + typename Device, + typename Index > +bool +decomposeMesh( const Config::ParameterContainer& parameters, + const std::string& prefix, + Grid< Dimension, Real, Device, Index >& mesh, + DistributedMeshes::DistributedMesh< Grid< Dimension, Real, Device, Index > > &distributedMesh, + Problem& problem ); + +} // namespace Meshes +} // namespace TNL + +#include diff --git a/src/TNL/Meshes/DistributedMeshes/loadDistributedMesh.h b/src/TNL/Meshes/TypeResolver/resolveDistributedMeshType.hpp similarity index 94% rename from src/TNL/Meshes/DistributedMeshes/loadDistributedMesh.h rename to src/TNL/Meshes/TypeResolver/resolveDistributedMeshType.hpp index 52c0b543b..1cd1ec76e 100644 --- a/src/TNL/Meshes/DistributedMeshes/loadDistributedMesh.h +++ b/src/TNL/Meshes/TypeResolver/resolveDistributedMeshType.hpp @@ -1,5 +1,5 @@ /*************************************************************************** - loadDistributedMesh.h - description + resolveDistributedMesh.hpp - description ------------------- begin : Apr 9, 2020 copyright : (C) 2020 by Tomas Oberhuber et al. @@ -12,10 +12,9 @@ #include -#include +#include #include #include -#include namespace TNL { namespace Meshes { @@ -26,7 +25,7 @@ template< typename ConfigTag, bool resolveDistributedMeshType( Functor&& functor, const std::string& fileName, - const std::string& fileFormat = "auto" ) + const std::string& fileFormat ) { std::cout << "Detecting distributed mesh from file " << fileName << " ..." << std::endl; @@ -76,7 +75,7 @@ template< typename ConfigTag, bool resolveAndLoadDistributedMesh( Functor&& functor, const std::string& fileName, - const std::string& fileFormat = "auto" ) + const std::string& fileFormat ) { auto wrapper = [&]( Readers::MeshReader& reader, auto&& mesh ) -> bool { @@ -100,7 +99,7 @@ bool loadDistributedMesh( Mesh< MeshConfig, Device >& mesh, DistributedMeshes::DistributedMesh< Mesh< MeshConfig, Device > >& distributedMesh, const std::string& fileName, - const std::string& fileFormat = "auto" ) + const std::string& fileFormat ) { // TODO: simplify interface, pass only the distributed mesh TNL_ASSERT_EQ( &mesh, &distributedMesh.getLocalMesh(), "mesh is not local mesh of the distributed mesh" ); @@ -152,7 +151,7 @@ bool loadDistributedMesh( Grid< Dimension, Real, Device, Index >& mesh, DistributedMeshes::DistributedMesh< Grid< Dimension, Real, Device, Index > > &distributedMesh, const std::string& fileName, - const std::string& fileFormat = "auto" ) + const std::string& fileFormat ) { std::cout << "Loading a global mesh from the file " << fileName << "..."; Grid< Dimension, Real, Device, Index > globalGrid; diff --git a/src/TNL/Meshes/TypeResolver/TypeResolver.h b/src/TNL/Meshes/TypeResolver/resolveMeshType.h similarity index 96% rename from src/TNL/Meshes/TypeResolver/TypeResolver.h rename to src/TNL/Meshes/TypeResolver/resolveMeshType.h index 55e151bd5..6386a511e 100644 --- a/src/TNL/Meshes/TypeResolver/TypeResolver.h +++ b/src/TNL/Meshes/TypeResolver/resolveMeshType.h @@ -1,5 +1,5 @@ /*************************************************************************** - MeshResolver.h - description + resolveMeshType.h - description ------------------- begin : Nov 22, 2016 copyright : (C) 2016 by Tomas Oberhuber et al. @@ -98,4 +98,4 @@ loadMesh( Grid< Dimension, Real, Device, Index >& grid, } // namespace Meshes } // namespace TNL -#include +#include diff --git a/src/TNL/Meshes/TypeResolver/TypeResolver_impl.h b/src/TNL/Meshes/TypeResolver/resolveMeshType.hpp similarity index 98% rename from src/TNL/Meshes/TypeResolver/TypeResolver_impl.h rename to src/TNL/Meshes/TypeResolver/resolveMeshType.hpp index 45ba4ca18..397e469dd 100644 --- a/src/TNL/Meshes/TypeResolver/TypeResolver_impl.h +++ b/src/TNL/Meshes/TypeResolver/resolveMeshType.hpp @@ -1,5 +1,5 @@ /*************************************************************************** - MeshResolver_impl.h - description + resolveMeshType.hpp - description ------------------- begin : Nov 22, 2016 copyright : (C) 2016 by Tomas Oberhuber et al. @@ -12,7 +12,7 @@ #include -#include +#include #include #include #include diff --git a/src/TNL/Solvers/PDE/TimeDependentPDESolver_impl.h b/src/TNL/Solvers/PDE/TimeDependentPDESolver_impl.h index 34f2798f8..63ba71c8f 100644 --- a/src/TNL/Solvers/PDE/TimeDependentPDESolver_impl.h +++ b/src/TNL/Solvers/PDE/TimeDependentPDESolver_impl.h @@ -11,8 +11,8 @@ #pragma once #include "TimeDependentPDESolver.h" -#include -#include +#include +#include namespace TNL { namespace Solvers { diff --git a/src/TNL/Solvers/PDE/TimeIndependentPDESolver_impl.h b/src/TNL/Solvers/PDE/TimeIndependentPDESolver_impl.h index 880d0ab31..b6e6c10c8 100644 --- a/src/TNL/Solvers/PDE/TimeIndependentPDESolver_impl.h +++ b/src/TNL/Solvers/PDE/TimeIndependentPDESolver_impl.h @@ -18,8 +18,8 @@ #pragma once #include -#include -#include +#include +#include namespace TNL { namespace Solvers { diff --git a/src/TNL/Solvers/SolverInitiator_impl.h b/src/TNL/Solvers/SolverInitiator_impl.h index 3d704426d..debac52af 100644 --- a/src/TNL/Solvers/SolverInitiator_impl.h +++ b/src/TNL/Solvers/SolverInitiator_impl.h @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include @@ -21,7 +21,7 @@ #include namespace TNL { -namespace Solvers { +namespace Solvers { template< template< typename Real, typename Device, typename Index, typename MeshType, typename ConfigTag, typename SolverStarter, typename CommunicatorType > class ProblemSetter, typename Real, diff --git a/src/Tools/tnl-decompose-mesh.cpp b/src/Tools/tnl-decompose-mesh.cpp index 358ef92d7..ac16df11b 100644 --- a/src/Tools/tnl-decompose-mesh.cpp +++ b/src/Tools/tnl-decompose-mesh.cpp @@ -11,7 +11,7 @@ // Implemented by: Jakub Klinkovský #include -#include +#include #include #include #include diff --git a/src/Tools/tnl-game-of-life.cpp b/src/Tools/tnl-game-of-life.cpp index f62a20f19..9bfb08033 100644 --- a/src/Tools/tnl-game-of-life.cpp +++ b/src/Tools/tnl-game-of-life.cpp @@ -11,8 +11,7 @@ #include #include -#include -#include +#include #include #include #include diff --git a/src/Tools/tnl-grid-to-mesh.cpp b/src/Tools/tnl-grid-to-mesh.cpp index 003a59f5c..fc0bb4248 100644 --- a/src/Tools/tnl-grid-to-mesh.cpp +++ b/src/Tools/tnl-grid-to-mesh.cpp @@ -9,7 +9,7 @@ /* See Copyright Notice in tnl/Copyright */ #include -#include +#include #include #include #include diff --git a/src/Tools/tnl-mesh-converter.cpp b/src/Tools/tnl-mesh-converter.cpp index f1a845984..fbcfd9263 100644 --- a/src/Tools/tnl-mesh-converter.cpp +++ b/src/Tools/tnl-mesh-converter.cpp @@ -9,7 +9,7 @@ /* See Copyright Notice in tnl/Copyright */ #include -#include +#include #include #include #include diff --git a/src/Tools/tnl-test-distributed-mesh.h b/src/Tools/tnl-test-distributed-mesh.h index 5ecaf52c2..9f2165a98 100644 --- a/src/Tools/tnl-test-distributed-mesh.h +++ b/src/Tools/tnl-test-distributed-mesh.h @@ -11,8 +11,7 @@ #include #include -#include -#include +#include #include #include #include diff --git a/src/Tools/tnl-view.cpp b/src/Tools/tnl-view.cpp index 97cfa7a97..3d9eb4c80 100644 --- a/src/Tools/tnl-view.cpp +++ b/src/Tools/tnl-view.cpp @@ -13,7 +13,7 @@ #include #include #include -#include +#include struct TNLViewBuildConfigTag {}; diff --git a/src/Tools/tnl-view.h b/src/Tools/tnl-view.h index bfda4f960..7be2cd4b1 100644 --- a/src/Tools/tnl-view.h +++ b/src/Tools/tnl-view.h @@ -16,12 +16,11 @@ #include #include #include +#include #include #include #include -#include - using namespace TNL; String getOutputFileName( const String& inputFileName, @@ -155,7 +154,7 @@ bool setMeshEntityDimension( const MeshPointer& meshPointer, { case 0: return setMeshEntityType< MeshPointer, 0, VectorFieldSize >( meshPointer, inputFileName, parsedObjectType, parameters ); - break; + break; case 1: return setMeshEntityType< MeshPointer, 1, VectorFieldSize >( meshPointer, inputFileName, parsedObjectType, parameters ); break; @@ -353,7 +352,7 @@ bool processFiles( const Config::ParameterContainer& parameters ) std::cerr << "unknown object ... SKIPPING!" << std::endl; continue; } - + if( verbose ) std::cout << objectType << " detected ... "; -- GitLab From b3fc7f0e0abf2e0310c261504baf9ec346ba7947 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Tue, 30 Mar 2021 20:13:38 +0200 Subject: [PATCH 09/19] Refactoring: moved BuildConfigTags.h for meshes into the TypeResolver directory This is more suitable location because the build config tags are used only by the GridTypeResolver and MeshTypeResolver classes. --- .../HeatEquation/HeatEquationBenchmarkBuildConfigTag.h | 2 +- src/Examples/heat-equation/HeatEquationBuildConfigTag.h | 4 ++-- src/Examples/inviscid-flow/eulerBuildConfigTag.h | 2 +- .../transport-equation/transportEquationBuildConfigTag.h | 2 +- .../Hamilton-Jacobi/Solvers/hamilton-jacobi/MainBuildConfig.h | 4 ++-- src/TNL/Meshes/{ => TypeResolver}/BuildConfigTags.h | 0 src/TNL/Meshes/TypeResolver/GridTypeResolver.h | 2 +- src/TNL/Meshes/TypeResolver/MeshTypeResolver.h | 2 +- src/TNL/Solvers/BuildConfigTags.h | 2 +- src/TNL/Solvers/FastBuildConfigTag.h | 2 +- src/Tools/tnl-quickstart/build-config-tag.h.in | 4 ++-- 11 files changed, 13 insertions(+), 13 deletions(-) rename src/TNL/Meshes/{ => TypeResolver}/BuildConfigTags.h (100%) diff --git a/src/Benchmarks/HeatEquation/HeatEquationBenchmarkBuildConfigTag.h b/src/Benchmarks/HeatEquation/HeatEquationBenchmarkBuildConfigTag.h index 8bdf9634e..dfecf655a 100644 --- a/src/Benchmarks/HeatEquation/HeatEquationBenchmarkBuildConfigTag.h +++ b/src/Benchmarks/HeatEquation/HeatEquationBenchmarkBuildConfigTag.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include namespace TNL { diff --git a/src/Examples/heat-equation/HeatEquationBuildConfigTag.h b/src/Examples/heat-equation/HeatEquationBuildConfigTag.h index 0e0389ca7..1e33ab2e1 100644 --- a/src/Examples/heat-equation/HeatEquationBuildConfigTag.h +++ b/src/Examples/heat-equation/HeatEquationBuildConfigTag.h @@ -11,14 +11,14 @@ #pragma once #include -#include +#include namespace TNL { class HeatEquationBuildConfigTag{}; namespace Solvers { - + /**** * Turn off support for float and long double. */ diff --git a/src/Examples/inviscid-flow/eulerBuildConfigTag.h b/src/Examples/inviscid-flow/eulerBuildConfigTag.h index fec6d0fe7..d396533a5 100644 --- a/src/Examples/inviscid-flow/eulerBuildConfigTag.h +++ b/src/Examples/inviscid-flow/eulerBuildConfigTag.h @@ -2,7 +2,7 @@ #define eulerBUILDCONFIGTAG_H_ #include -#include +#include namespace TNL { diff --git a/src/Examples/transport-equation/transportEquationBuildConfigTag.h b/src/Examples/transport-equation/transportEquationBuildConfigTag.h index 0121ab2c0..ac8035515 100644 --- a/src/Examples/transport-equation/transportEquationBuildConfigTag.h +++ b/src/Examples/transport-equation/transportEquationBuildConfigTag.h @@ -12,7 +12,7 @@ #pragma once #include -#include +#include namespace TNL { diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/MainBuildConfig.h b/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/MainBuildConfig.h index a2a1d7372..e37cb80b3 100644 --- a/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/MainBuildConfig.h +++ b/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/MainBuildConfig.h @@ -11,13 +11,13 @@ #pragma once #include -#include +#include namespace TNL { class HamiltonJacobiBuildConfig {}; -namespace Solvers { +namespace Solvers { /**** diff --git a/src/TNL/Meshes/BuildConfigTags.h b/src/TNL/Meshes/TypeResolver/BuildConfigTags.h similarity index 100% rename from src/TNL/Meshes/BuildConfigTags.h rename to src/TNL/Meshes/TypeResolver/BuildConfigTags.h diff --git a/src/TNL/Meshes/TypeResolver/GridTypeResolver.h b/src/TNL/Meshes/TypeResolver/GridTypeResolver.h index 79682788a..8297bb58c 100644 --- a/src/TNL/Meshes/TypeResolver/GridTypeResolver.h +++ b/src/TNL/Meshes/TypeResolver/GridTypeResolver.h @@ -12,7 +12,7 @@ #include -#include +#include namespace TNL { namespace Meshes { diff --git a/src/TNL/Meshes/TypeResolver/MeshTypeResolver.h b/src/TNL/Meshes/TypeResolver/MeshTypeResolver.h index 00a949f5e..46248e98c 100644 --- a/src/TNL/Meshes/TypeResolver/MeshTypeResolver.h +++ b/src/TNL/Meshes/TypeResolver/MeshTypeResolver.h @@ -12,7 +12,7 @@ #include -#include +#include namespace TNL { namespace Meshes { diff --git a/src/TNL/Solvers/BuildConfigTags.h b/src/TNL/Solvers/BuildConfigTags.h index bcd4cdafc..e75dcfddb 100644 --- a/src/TNL/Solvers/BuildConfigTags.h +++ b/src/TNL/Solvers/BuildConfigTags.h @@ -39,7 +39,7 @@ template< typename ConfigTag, typename Index > struct ConfigTagIndex{ enum { ena /**** * The mesh type will be resolved by the Solver by default. - * (The detailed mesh configuration is in TNL/Meshes/BuildConfigTags.h) + * (The detailed mesh configuration is in TNL/Meshes/TypeResolver/BuildConfigTags.h) */ template< typename ConfigTag > struct ConfigTagMeshResolve{ enum { enabled = true }; }; diff --git a/src/TNL/Solvers/FastBuildConfigTag.h b/src/TNL/Solvers/FastBuildConfigTag.h index e0f1cc174..d4a43df06 100644 --- a/src/TNL/Solvers/FastBuildConfigTag.h +++ b/src/TNL/Solvers/FastBuildConfigTag.h @@ -11,7 +11,7 @@ #pragma once #include -#include +#include namespace TNL { namespace Solvers { diff --git a/src/Tools/tnl-quickstart/build-config-tag.h.in b/src/Tools/tnl-quickstart/build-config-tag.h.in index f507baf06..febd7482a 100644 --- a/src/Tools/tnl-quickstart/build-config-tag.h.in +++ b/src/Tools/tnl-quickstart/build-config-tag.h.in @@ -18,10 +18,10 @@ template<> struct ConfigTagReal< {problemBaseName}BuildConfigTag, long double > */ template<> struct ConfigTagIndex< {problemBaseName}BuildConfigTag, short int >{{ enum {{ enabled = false }}; }}; template<> struct ConfigTagIndex< {problemBaseName}BuildConfigTag, long int >{{ enum {{ enabled = false }}; }}; - + /**** * The mesh type will be resolved by the Solver by default. - * (The detailed mesh configuration is in TNL/Meshes/BuildConfigTags.h) + * (The detailed mesh configuration is in TNL/Meshes/TypeResolver/BuildConfigTags.h) */ template<> struct ConfigTagMeshResolve< {problemBaseName}BuildConfigTag >{{ enum {{ enabled = true }}; }}; -- GitLab From ef5b6bedb7b3a2da9ce3d3ccbbb5750e70231aa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Tue, 30 Mar 2021 20:21:53 +0200 Subject: [PATCH 10/19] Refactoring: moved Hamilton-Jacobi from Experimental to Examples It is an implementation of a specific solver, not part of the general (header-only) library. If there are some useful generic templates (like operators etc.), they need to be refactored into the generic interface. --- src/Examples/CMakeLists.txt | 1 + .../Experimental => Examples}/Hamilton-Jacobi/CMakeLists.txt | 0 .../Operators/Hamilton-Jacobi/Eikonal/godunovEikonal.h | 0 .../Operators/Hamilton-Jacobi/Godunov-Eikonal/godunovEikonal.h | 0 .../Hamilton-Jacobi/Godunov-Eikonal/godunovEikonal1D_impl.h | 0 .../Hamilton-Jacobi/Godunov-Eikonal/godunovEikonal2D_impl.h | 0 .../Hamilton-Jacobi/Godunov-Eikonal/godunovEikonal3D_impl.h | 0 .../Hamilton-Jacobi/Godunov-Eikonal/parallelGodunovEikonal.h | 0 .../Godunov-Eikonal/parallelGodunovEikonal1D_impl.h | 0 .../Godunov-Eikonal/parallelGodunovEikonal2D_impl.h | 0 .../Godunov-Eikonal/parallelGodunovEikonal3D_impl.h | 0 .../Hamilton-Jacobi/Godunov-Eikonal/parallelGodunovMap.h | 0 .../Godunov-Eikonal/parallelGodunovMap2D_impl.h | 0 .../Operators/Hamilton-Jacobi/Godunov/godunov1D_impl.h | 0 .../Operators/Hamilton-Jacobi/Godunov/godunov2D_impl.h | 0 .../Operators/Hamilton-Jacobi/Godunov/godunov3D_impl.h | 0 .../Operators/Hamilton-Jacobi/tnlEikonalOperator.h | 0 .../Hamilton-Jacobi/Operators/Hamilton-Jacobi/upwindEikonal.h | 0 .../Hamilton-Jacobi/Solvers/CMakeLists.txt | 0 .../Hamilton-Jacobi/Solvers/hamilton-jacobi/CMakeLists.txt | 0 .../Solvers/hamilton-jacobi/HamiltonJacobiProblem.h | 0 .../Solvers/hamilton-jacobi/HamiltonJacobiProblemConfig.h | 0 .../Solvers/hamilton-jacobi/HamiltonJacobiProblemSetter.h | 0 .../Solvers/hamilton-jacobi/HamiltonJacobiProblemSetter_impl.h | 0 .../Solvers/hamilton-jacobi/HamiltonJacobiProblem_impl.h | 0 .../Hamilton-Jacobi/Solvers/hamilton-jacobi/MainBuildConfig.h | 0 .../Hamilton-Jacobi/Solvers/hamilton-jacobi/Makefile | 0 .../Hamilton-Jacobi/Solvers/hamilton-jacobi/main.cpp | 0 .../Solvers/hamilton-jacobi/tnl-direct-eikonal-solver.cpp | 0 .../Solvers/hamilton-jacobi/tnl-direct-eikonal-solver.cu | 0 .../Solvers/hamilton-jacobi/tnl-direct-eikonal-solver.h | 0 .../Solvers/hamilton-jacobi/tnl-run-eikonal-equation-eoc-test | 0 .../Solvers/hamilton-jacobi/tnl-run-fsm-eoc-test | 0 .../hamilton-jacobi/tnlDirectEikonalMethodBase1D_impl.h | 0 .../hamilton-jacobi/tnlDirectEikonalMethodBase2D_impl.h | 0 .../hamilton-jacobi/tnlDirectEikonalMethodBase3D_impl.h | 0 .../Solvers/hamilton-jacobi/tnlDirectEikonalMethodsBase.h | 0 .../Solvers/hamilton-jacobi/tnlDirectEikonalProblem.h | 0 .../Solvers/hamilton-jacobi/tnlDirectEikonalProblem_impl.h | 0 .../Solvers/hamilton-jacobi/tnlFastSweepingMethod.h | 0 .../Solvers/hamilton-jacobi/tnlFastSweepingMethod1D_impl.h | 0 .../Solvers/hamilton-jacobi/tnlFastSweepingMethod2D_impl.h | 0 .../Solvers/hamilton-jacobi/tnlFastSweepingMethod3D_impl.h | 0 src/TNL/Experimental/CMakeLists.txt | 3 --- 44 files changed, 1 insertion(+), 3 deletions(-) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/CMakeLists.txt (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Eikonal/godunovEikonal.h (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/godunovEikonal.h (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/godunovEikonal1D_impl.h (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/godunovEikonal2D_impl.h (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/godunovEikonal3D_impl.h (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/parallelGodunovEikonal.h (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/parallelGodunovEikonal1D_impl.h (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/parallelGodunovEikonal2D_impl.h (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/parallelGodunovEikonal3D_impl.h (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/parallelGodunovMap.h (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/parallelGodunovMap2D_impl.h (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov/godunov1D_impl.h (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov/godunov2D_impl.h (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov/godunov3D_impl.h (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Operators/Hamilton-Jacobi/tnlEikonalOperator.h (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Operators/Hamilton-Jacobi/upwindEikonal.h (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Solvers/CMakeLists.txt (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Solvers/hamilton-jacobi/CMakeLists.txt (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Solvers/hamilton-jacobi/HamiltonJacobiProblem.h (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Solvers/hamilton-jacobi/HamiltonJacobiProblemConfig.h (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Solvers/hamilton-jacobi/HamiltonJacobiProblemSetter.h (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Solvers/hamilton-jacobi/HamiltonJacobiProblemSetter_impl.h (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Solvers/hamilton-jacobi/HamiltonJacobiProblem_impl.h (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Solvers/hamilton-jacobi/MainBuildConfig.h (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Solvers/hamilton-jacobi/Makefile (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Solvers/hamilton-jacobi/main.cpp (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnl-direct-eikonal-solver.cpp (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnl-direct-eikonal-solver.cu (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnl-direct-eikonal-solver.h (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnl-run-eikonal-equation-eoc-test (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnl-run-fsm-eoc-test (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalMethodBase1D_impl.h (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalMethodBase2D_impl.h (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalMethodBase3D_impl.h (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalMethodsBase.h (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalProblem.h (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalProblem_impl.h (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlFastSweepingMethod.h (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlFastSweepingMethod1D_impl.h (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlFastSweepingMethod2D_impl.h (100%) rename src/{TNL/Experimental => Examples}/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlFastSweepingMethod3D_impl.h (100%) diff --git a/src/Examples/CMakeLists.txt b/src/Examples/CMakeLists.txt index 493f537d1..652815874 100644 --- a/src/Examples/CMakeLists.txt +++ b/src/Examples/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory( simple-examples ) +add_subdirectory( Hamilton-Jacobi ) add_subdirectory( heat-equation ) add_subdirectory( transport-equation ) add_subdirectory( navier-stokes ) diff --git a/src/TNL/Experimental/Hamilton-Jacobi/CMakeLists.txt b/src/Examples/Hamilton-Jacobi/CMakeLists.txt similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/CMakeLists.txt rename to src/Examples/Hamilton-Jacobi/CMakeLists.txt diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Eikonal/godunovEikonal.h b/src/Examples/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Eikonal/godunovEikonal.h similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Eikonal/godunovEikonal.h rename to src/Examples/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Eikonal/godunovEikonal.h diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/godunovEikonal.h b/src/Examples/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/godunovEikonal.h similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/godunovEikonal.h rename to src/Examples/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/godunovEikonal.h diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/godunovEikonal1D_impl.h b/src/Examples/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/godunovEikonal1D_impl.h similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/godunovEikonal1D_impl.h rename to src/Examples/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/godunovEikonal1D_impl.h diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/godunovEikonal2D_impl.h b/src/Examples/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/godunovEikonal2D_impl.h similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/godunovEikonal2D_impl.h rename to src/Examples/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/godunovEikonal2D_impl.h diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/godunovEikonal3D_impl.h b/src/Examples/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/godunovEikonal3D_impl.h similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/godunovEikonal3D_impl.h rename to src/Examples/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/godunovEikonal3D_impl.h diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/parallelGodunovEikonal.h b/src/Examples/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/parallelGodunovEikonal.h similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/parallelGodunovEikonal.h rename to src/Examples/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/parallelGodunovEikonal.h diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/parallelGodunovEikonal1D_impl.h b/src/Examples/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/parallelGodunovEikonal1D_impl.h similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/parallelGodunovEikonal1D_impl.h rename to src/Examples/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/parallelGodunovEikonal1D_impl.h diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/parallelGodunovEikonal2D_impl.h b/src/Examples/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/parallelGodunovEikonal2D_impl.h similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/parallelGodunovEikonal2D_impl.h rename to src/Examples/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/parallelGodunovEikonal2D_impl.h diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/parallelGodunovEikonal3D_impl.h b/src/Examples/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/parallelGodunovEikonal3D_impl.h similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/parallelGodunovEikonal3D_impl.h rename to src/Examples/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/parallelGodunovEikonal3D_impl.h diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/parallelGodunovMap.h b/src/Examples/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/parallelGodunovMap.h similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/parallelGodunovMap.h rename to src/Examples/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/parallelGodunovMap.h diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/parallelGodunovMap2D_impl.h b/src/Examples/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/parallelGodunovMap2D_impl.h similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/parallelGodunovMap2D_impl.h rename to src/Examples/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov-Eikonal/parallelGodunovMap2D_impl.h diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov/godunov1D_impl.h b/src/Examples/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov/godunov1D_impl.h similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov/godunov1D_impl.h rename to src/Examples/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov/godunov1D_impl.h diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov/godunov2D_impl.h b/src/Examples/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov/godunov2D_impl.h similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov/godunov2D_impl.h rename to src/Examples/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov/godunov2D_impl.h diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov/godunov3D_impl.h b/src/Examples/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov/godunov3D_impl.h similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov/godunov3D_impl.h rename to src/Examples/Hamilton-Jacobi/Operators/Hamilton-Jacobi/Godunov/godunov3D_impl.h diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Operators/Hamilton-Jacobi/tnlEikonalOperator.h b/src/Examples/Hamilton-Jacobi/Operators/Hamilton-Jacobi/tnlEikonalOperator.h similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Operators/Hamilton-Jacobi/tnlEikonalOperator.h rename to src/Examples/Hamilton-Jacobi/Operators/Hamilton-Jacobi/tnlEikonalOperator.h diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Operators/Hamilton-Jacobi/upwindEikonal.h b/src/Examples/Hamilton-Jacobi/Operators/Hamilton-Jacobi/upwindEikonal.h similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Operators/Hamilton-Jacobi/upwindEikonal.h rename to src/Examples/Hamilton-Jacobi/Operators/Hamilton-Jacobi/upwindEikonal.h diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Solvers/CMakeLists.txt b/src/Examples/Hamilton-Jacobi/Solvers/CMakeLists.txt similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Solvers/CMakeLists.txt rename to src/Examples/Hamilton-Jacobi/Solvers/CMakeLists.txt diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/CMakeLists.txt b/src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/CMakeLists.txt similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/CMakeLists.txt rename to src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/CMakeLists.txt diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/HamiltonJacobiProblem.h b/src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/HamiltonJacobiProblem.h similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/HamiltonJacobiProblem.h rename to src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/HamiltonJacobiProblem.h diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/HamiltonJacobiProblemConfig.h b/src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/HamiltonJacobiProblemConfig.h similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/HamiltonJacobiProblemConfig.h rename to src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/HamiltonJacobiProblemConfig.h diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/HamiltonJacobiProblemSetter.h b/src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/HamiltonJacobiProblemSetter.h similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/HamiltonJacobiProblemSetter.h rename to src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/HamiltonJacobiProblemSetter.h diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/HamiltonJacobiProblemSetter_impl.h b/src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/HamiltonJacobiProblemSetter_impl.h similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/HamiltonJacobiProblemSetter_impl.h rename to src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/HamiltonJacobiProblemSetter_impl.h diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/HamiltonJacobiProblem_impl.h b/src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/HamiltonJacobiProblem_impl.h similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/HamiltonJacobiProblem_impl.h rename to src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/HamiltonJacobiProblem_impl.h diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/MainBuildConfig.h b/src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/MainBuildConfig.h similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/MainBuildConfig.h rename to src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/MainBuildConfig.h diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/Makefile b/src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/Makefile similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/Makefile rename to src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/Makefile diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/main.cpp b/src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/main.cpp similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/main.cpp rename to src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/main.cpp diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnl-direct-eikonal-solver.cpp b/src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnl-direct-eikonal-solver.cpp similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnl-direct-eikonal-solver.cpp rename to src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnl-direct-eikonal-solver.cpp diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnl-direct-eikonal-solver.cu b/src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnl-direct-eikonal-solver.cu similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnl-direct-eikonal-solver.cu rename to src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnl-direct-eikonal-solver.cu diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnl-direct-eikonal-solver.h b/src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnl-direct-eikonal-solver.h similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnl-direct-eikonal-solver.h rename to src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnl-direct-eikonal-solver.h diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnl-run-eikonal-equation-eoc-test b/src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnl-run-eikonal-equation-eoc-test similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnl-run-eikonal-equation-eoc-test rename to src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnl-run-eikonal-equation-eoc-test diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnl-run-fsm-eoc-test b/src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnl-run-fsm-eoc-test similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnl-run-fsm-eoc-test rename to src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnl-run-fsm-eoc-test diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalMethodBase1D_impl.h b/src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalMethodBase1D_impl.h similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalMethodBase1D_impl.h rename to src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalMethodBase1D_impl.h diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalMethodBase2D_impl.h b/src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalMethodBase2D_impl.h similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalMethodBase2D_impl.h rename to src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalMethodBase2D_impl.h diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalMethodBase3D_impl.h b/src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalMethodBase3D_impl.h similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalMethodBase3D_impl.h rename to src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalMethodBase3D_impl.h diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalMethodsBase.h b/src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalMethodsBase.h similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalMethodsBase.h rename to src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalMethodsBase.h diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalProblem.h b/src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalProblem.h similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalProblem.h rename to src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalProblem.h diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalProblem_impl.h b/src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalProblem_impl.h similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalProblem_impl.h rename to src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlDirectEikonalProblem_impl.h diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlFastSweepingMethod.h b/src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlFastSweepingMethod.h similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlFastSweepingMethod.h rename to src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlFastSweepingMethod.h diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlFastSweepingMethod1D_impl.h b/src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlFastSweepingMethod1D_impl.h similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlFastSweepingMethod1D_impl.h rename to src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlFastSweepingMethod1D_impl.h diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlFastSweepingMethod2D_impl.h b/src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlFastSweepingMethod2D_impl.h similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlFastSweepingMethod2D_impl.h rename to src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlFastSweepingMethod2D_impl.h diff --git a/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlFastSweepingMethod3D_impl.h b/src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlFastSweepingMethod3D_impl.h similarity index 100% rename from src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlFastSweepingMethod3D_impl.h rename to src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/tnlFastSweepingMethod3D_impl.h diff --git a/src/TNL/Experimental/CMakeLists.txt b/src/TNL/Experimental/CMakeLists.txt index fc636e695..3c13f0ffb 100644 --- a/src/TNL/Experimental/CMakeLists.txt +++ b/src/TNL/Experimental/CMakeLists.txt @@ -1,4 +1 @@ add_subdirectory( Arithmetics ) -if( ${BUILD_EXAMPLES} ) - add_subdirectory( Hamilton-Jacobi ) -endif() -- GitLab From 3b7eaff12594aa719eb2337e2ef9f54d2c8de7e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Tue, 30 Mar 2021 20:35:15 +0200 Subject: [PATCH 11/19] Refactoring: moved Arithmetics to the main TNL directory The Experimental directory does not make much sense - there is a lot of experimental or just unused code in TNL, e.g. Images or Operators. - tests moved to the main UnitTests directory - added missing inclusion of the Algorithms tests directory to CMakeLists.txt --- src/CMakeLists.txt | 4 -- .../{Experimental => }/Arithmetics/Double.h | 0 .../Arithmetics/Double_impl.h | 0 .../Arithmetics/FlopsCounter.cpp | 0 .../Arithmetics/FlopsCounter.h | 0 .../Arithmetics/MultiPrecision.cpp | 0 .../Arithmetics/MultiPrecision.h | 0 src/TNL/{Experimental => }/Arithmetics/Quad.h | 0 .../Arithmetics/Quad_impl.h | 0 src/TNL/{Experimental => }/Arithmetics/Real.h | 0 .../Experimental/Arithmetics/CMakeLists.txt | 1 - .../Arithmetics/UnitTests/GtestMissingError.h | 21 ------ src/TNL/Experimental/CMakeLists.txt | 1 - src/TNL/Solvers/ODE/ExplicitSolver.h | 11 ++- .../Arithmetics}/CMakeLists.txt | 0 .../Arithmetics}/MultiPrecisionTest.cpp | 52 +++++++------- .../Arithmetics}/QuadTest.cpp | 70 +++++++++---------- src/UnitTests/CMakeLists.txt | 2 + .../Containers/VectorEvaluateAndReduceTest.h | 1 - src/UnitTests/Containers/VectorTestSetup.h | 2 +- 20 files changed, 69 insertions(+), 96 deletions(-) rename src/TNL/{Experimental => }/Arithmetics/Double.h (100%) rename src/TNL/{Experimental => }/Arithmetics/Double_impl.h (100%) rename src/TNL/{Experimental => }/Arithmetics/FlopsCounter.cpp (100%) rename src/TNL/{Experimental => }/Arithmetics/FlopsCounter.h (100%) rename src/TNL/{Experimental => }/Arithmetics/MultiPrecision.cpp (100%) rename src/TNL/{Experimental => }/Arithmetics/MultiPrecision.h (100%) rename src/TNL/{Experimental => }/Arithmetics/Quad.h (100%) rename src/TNL/{Experimental => }/Arithmetics/Quad_impl.h (100%) rename src/TNL/{Experimental => }/Arithmetics/Real.h (100%) delete mode 100644 src/TNL/Experimental/Arithmetics/CMakeLists.txt delete mode 100644 src/TNL/Experimental/Arithmetics/UnitTests/GtestMissingError.h delete mode 100644 src/TNL/Experimental/CMakeLists.txt rename src/{TNL/Experimental/Arithmetics/UnitTests => UnitTests/Arithmetics}/CMakeLists.txt (100%) rename src/{TNL/Experimental/Arithmetics/UnitTests => UnitTests/Arithmetics}/MultiPrecisionTest.cpp (95%) rename src/{TNL/Experimental/Arithmetics/UnitTests => UnitTests/Arithmetics}/QuadTest.cpp (93%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4dcf601c0..1da8d78ec 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,10 +4,6 @@ install( DIRECTORY TNL/ DESTINATION ${TNL_TARGET_INCLUDE_DIRECTORY} MESSAGE_NEVER FILES_MATCHING PATTERN "*.h" PATTERN "*.hpp" ) -# TODO: TNL/Experimental/ should be moved somewhere else, the TNL subdirectory -# should contain only header files -add_subdirectory( TNL/Experimental ) - # Note that it is important to start building examples as soon as possible, # because they take the longest time and other stuff can be pipelined before # they are finished (at least with Ninja). diff --git a/src/TNL/Experimental/Arithmetics/Double.h b/src/TNL/Arithmetics/Double.h similarity index 100% rename from src/TNL/Experimental/Arithmetics/Double.h rename to src/TNL/Arithmetics/Double.h diff --git a/src/TNL/Experimental/Arithmetics/Double_impl.h b/src/TNL/Arithmetics/Double_impl.h similarity index 100% rename from src/TNL/Experimental/Arithmetics/Double_impl.h rename to src/TNL/Arithmetics/Double_impl.h diff --git a/src/TNL/Experimental/Arithmetics/FlopsCounter.cpp b/src/TNL/Arithmetics/FlopsCounter.cpp similarity index 100% rename from src/TNL/Experimental/Arithmetics/FlopsCounter.cpp rename to src/TNL/Arithmetics/FlopsCounter.cpp diff --git a/src/TNL/Experimental/Arithmetics/FlopsCounter.h b/src/TNL/Arithmetics/FlopsCounter.h similarity index 100% rename from src/TNL/Experimental/Arithmetics/FlopsCounter.h rename to src/TNL/Arithmetics/FlopsCounter.h diff --git a/src/TNL/Experimental/Arithmetics/MultiPrecision.cpp b/src/TNL/Arithmetics/MultiPrecision.cpp similarity index 100% rename from src/TNL/Experimental/Arithmetics/MultiPrecision.cpp rename to src/TNL/Arithmetics/MultiPrecision.cpp diff --git a/src/TNL/Experimental/Arithmetics/MultiPrecision.h b/src/TNL/Arithmetics/MultiPrecision.h similarity index 100% rename from src/TNL/Experimental/Arithmetics/MultiPrecision.h rename to src/TNL/Arithmetics/MultiPrecision.h diff --git a/src/TNL/Experimental/Arithmetics/Quad.h b/src/TNL/Arithmetics/Quad.h similarity index 100% rename from src/TNL/Experimental/Arithmetics/Quad.h rename to src/TNL/Arithmetics/Quad.h diff --git a/src/TNL/Experimental/Arithmetics/Quad_impl.h b/src/TNL/Arithmetics/Quad_impl.h similarity index 100% rename from src/TNL/Experimental/Arithmetics/Quad_impl.h rename to src/TNL/Arithmetics/Quad_impl.h diff --git a/src/TNL/Experimental/Arithmetics/Real.h b/src/TNL/Arithmetics/Real.h similarity index 100% rename from src/TNL/Experimental/Arithmetics/Real.h rename to src/TNL/Arithmetics/Real.h diff --git a/src/TNL/Experimental/Arithmetics/CMakeLists.txt b/src/TNL/Experimental/Arithmetics/CMakeLists.txt deleted file mode 100644 index 60295b76b..000000000 --- a/src/TNL/Experimental/Arithmetics/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -ADD_SUBDIRECTORY( UnitTests ) diff --git a/src/TNL/Experimental/Arithmetics/UnitTests/GtestMissingError.h b/src/TNL/Experimental/Arithmetics/UnitTests/GtestMissingError.h deleted file mode 100644 index b308a16c8..000000000 --- a/src/TNL/Experimental/Arithmetics/UnitTests/GtestMissingError.h +++ /dev/null @@ -1,21 +0,0 @@ -/*************************************************************************** - GtestMissingError.h - description - ------------------- - begin : Jul 2, 2017 - copyright : (C) 2017 by Tomas Oberhuber et al. - email : tomas.oberhuber@fjfi.cvut.cz - ***************************************************************************/ - -/* See Copyright Notice in tnl/Copyright */ - -#pragma once - -#include - -struct GtestMissingError - : public std::runtime_error -{ - GtestMissingError() - : std::runtime_error( "The GTest library is needed to run the tests." ) - {} -}; diff --git a/src/TNL/Experimental/CMakeLists.txt b/src/TNL/Experimental/CMakeLists.txt deleted file mode 100644 index 3c13f0ffb..000000000 --- a/src/TNL/Experimental/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -add_subdirectory( Arithmetics ) diff --git a/src/TNL/Solvers/ODE/ExplicitSolver.h b/src/TNL/Solvers/ODE/ExplicitSolver.h index 72299eace..bade4ded7 100644 --- a/src/TNL/Solvers/ODE/ExplicitSolver.h +++ b/src/TNL/Solvers/ODE/ExplicitSolver.h @@ -11,7 +11,6 @@ #pragma once #include -#include #include #include #include @@ -30,7 +29,7 @@ class ExplicitSolver : public IterativeSolver< typename Problem::RealType, SolverMonitor > { public: - + using ProblemType = Problem; using DofVectorType = typename Problem::DofVectorType; using RealType = typename Problem::RealType; @@ -52,19 +51,19 @@ class ExplicitSolver : public IterativeSolver< typename Problem::RealType, void setTime( const RealType& t ); const RealType& getTime() const; - + void setStopTime( const RealType& stopTime ); RealType getStopTime() const; void setTau( const RealType& tau ); - + const RealType& getTau() const; void setMaxTau( const RealType& maxTau ); const RealType& getMaxTau() const; - + void setVerbose( IndexType v ); virtual bool solve( DofVectorPointer& u ) = 0; @@ -76,7 +75,7 @@ class ExplicitSolver : public IterativeSolver< typename Problem::RealType, void refreshSolverMonitor( bool force = false ); protected: - + /**** * Current time of the parabolic problem. */ diff --git a/src/TNL/Experimental/Arithmetics/UnitTests/CMakeLists.txt b/src/UnitTests/Arithmetics/CMakeLists.txt similarity index 100% rename from src/TNL/Experimental/Arithmetics/UnitTests/CMakeLists.txt rename to src/UnitTests/Arithmetics/CMakeLists.txt diff --git a/src/TNL/Experimental/Arithmetics/UnitTests/MultiPrecisionTest.cpp b/src/UnitTests/Arithmetics/MultiPrecisionTest.cpp similarity index 95% rename from src/TNL/Experimental/Arithmetics/UnitTests/MultiPrecisionTest.cpp rename to src/UnitTests/Arithmetics/MultiPrecisionTest.cpp index 164da9d21..04277185d 100644 --- a/src/TNL/Experimental/Arithmetics/UnitTests/MultiPrecisionTest.cpp +++ b/src/UnitTests/Arithmetics/MultiPrecisionTest.cpp @@ -14,7 +14,7 @@ * Daniel Simon, dansimon93@gmail.com */ -#ifdef HAVE_GTEST +#ifdef HAVE_GTEST #include #endif @@ -22,7 +22,7 @@ #include #endif -#include +#include /*NUMBERS*/ #define PREC 128 @@ -32,25 +32,25 @@ /* INFO: This test compares values from the GMP Library with values from our wrapped GMP Library in MultiPrecision. - MP_res -> result from MultiPrecision + MP_res -> result from MultiPrecision GMP_res -> result from GMP Library */ using namespace TNL; using namespace TNL::Arithmetics; -#ifdef HAVE_GTEST +#ifdef HAVE_GTEST TEST (MultiPrecisionTest, number_assignment) { /* GMPLIB */ mpf_t mpf1; mpf_set_default_prec (PREC); mpf_init_set_d (mpf1 , num_1); - + /* MultiPrecision */ MultiPrecision::setPrecision(PREC); MultiPrecision mp1 (num_1); - + EXPECT_EQ (mp1 , mpf1); } @@ -63,12 +63,12 @@ TEST (MultiPrecisionTest, number_negation) mpf_init_set_d (mpf1 , num_1); mpf_init (GMP_res); mpf_neg (GMP_res , mpf1); - + /* MultiPrecision */ MultiPrecision::setPrecision(PREC); MultiPrecision mp1 (num_1); MultiPrecision MP_res (-mp1); - + EXPECT_EQ (MP_res , GMP_res); } @@ -82,13 +82,13 @@ TEST (MultiPrecisionTest, op_plus_equals) mpf_init_set_d (mpf2 , num_1); mpf_init (GMP_res); mpf_add (GMP_res , mpf1 , mpf2); - + /* MultiPrecision */ MultiPrecision::setPrecision(PREC); MultiPrecision mp1 (num_2); MultiPrecision mp2 (num_1); MultiPrecision MP_res (mp1 += mp2); - + EXPECT_EQ (MP_res , GMP_res); } @@ -102,13 +102,13 @@ TEST (MultiPrecisionTest, op_minus_equals) mpf_init_set_d (mpf2 , num_1); mpf_init (GMP_res); mpf_sub (GMP_res , mpf1 , mpf2); - + /* MultiPrecision */ MultiPrecision::setPrecision(PREC); MultiPrecision mp1 (num_2); MultiPrecision mp2 (num_1); MultiPrecision MP_res (mp1 -= mp2); - + EXPECT_EQ (MP_res , GMP_res); } @@ -122,13 +122,13 @@ TEST (MultiPrecisionTest, op_mul_equals) mpf_init_set_d (mpf2 , num_1); mpf_init (GMP_res); mpf_mul (GMP_res , mpf1 , mpf2); - + /* MultiPrecision */ MultiPrecision::setPrecision(PREC); MultiPrecision mp1 (num_2); MultiPrecision mp2 (num_1); MultiPrecision MP_res (mp1 *= mp2); - + EXPECT_EQ (MP_res , GMP_res); } @@ -142,13 +142,13 @@ TEST (MultiPrecisionTest, op_div_equals) mpf_init_set_d (mpf2 , num_1); mpf_init (GMP_res); mpf_div (GMP_res , mpf1 , mpf2); - + /* MultiPrecision */ MultiPrecision::setPrecision(PREC); MultiPrecision mp1 (num_2); MultiPrecision mp2 (num_1); MultiPrecision MP_res (mp1 /= mp2); - + EXPECT_EQ (MP_res , GMP_res); } @@ -162,13 +162,13 @@ TEST (MultiPrecisionTest, op_plus) mpf_init_set_d (mpf2 , num_1); mpf_init (GMP_res); mpf_add (GMP_res , mpf1 , mpf2); - + /* MultiPrecision */ MultiPrecision::setPrecision(PREC); MultiPrecision mp1 (num_2); MultiPrecision mp2 (num_1); MultiPrecision MP_res (mp1 + mp2); - + EXPECT_EQ (MP_res , GMP_res); } @@ -182,13 +182,13 @@ TEST (MultiPrecisionTest, op_minus) mpf_init_set_d (mpf2 , num_1); mpf_init (GMP_res); mpf_sub (GMP_res , mpf1 , mpf2); - + /* MultiPrecision */ MultiPrecision::setPrecision(PREC); MultiPrecision mp1 (num_2); MultiPrecision mp2 (num_1); MultiPrecision MP_res (mp1 - mp2); - + EXPECT_EQ (MP_res , GMP_res); } @@ -202,13 +202,13 @@ TEST (MultiPrecisionTest, op_div) mpf_init_set_d (mpf2 , num_1); mpf_init (GMP_res); mpf_div (GMP_res , mpf1 , mpf2); - + /* MultiPrecision */ MultiPrecision::setPrecision(PREC); MultiPrecision mp1 (num_2); MultiPrecision mp2 (num_1); MultiPrecision MP_res (mp1 / mp2); - + EXPECT_EQ (MP_res , GMP_res); } @@ -222,19 +222,19 @@ TEST (MultiPrecisionTest, op_mul) mpf_init_set_d (mpf2 , num_1); mpf_init (GMP_res); mpf_mul (GMP_res , mpf1 , mpf2); - + /* MultiPrecision */ MultiPrecision::setPrecision(PREC); MultiPrecision mp1 (num_2); MultiPrecision mp2 (num_1); MultiPrecision MP_res (mp1 * mp2); - + EXPECT_EQ (MP_res , GMP_res); } #endif -#include "GtestMissingError.h" +#include "../GtestMissingError.h" int main( int argc, char* argv[] ) { #ifdef HAVE_GTEST @@ -243,4 +243,4 @@ int main( int argc, char* argv[] ) #else throw GtestMissingError(); #endif -} \ No newline at end of file +} diff --git a/src/TNL/Experimental/Arithmetics/UnitTests/QuadTest.cpp b/src/UnitTests/Arithmetics/QuadTest.cpp similarity index 93% rename from src/TNL/Experimental/Arithmetics/UnitTests/QuadTest.cpp rename to src/UnitTests/Arithmetics/QuadTest.cpp index 4f922292d..1e8c6942f 100644 --- a/src/TNL/Experimental/Arithmetics/UnitTests/QuadTest.cpp +++ b/src/UnitTests/Arithmetics/QuadTest.cpp @@ -14,15 +14,15 @@ * Daniel Simon, dansimon93@gmail.com */ -#ifdef HAVE_GTEST +#ifdef HAVE_GTEST #include #endif -#include -#include +#include +#include /*NUMBERS*/ -#define num_1 2.1230405067890102030405060708096352410708 +#define num_1 2.1230405067890102030405060708096352410708 #define num_2 1.2080706050401236549873571590082467951301 /* @@ -40,10 +40,10 @@ TEST (QuadTest, number_assignment) { /* Quad */ Quad qd1 (num_1); - + /* MultiPrecision */ MultiPrecision mp1 (num_1); - + EXPECT_EQ (mp1 , qd1); } @@ -54,12 +54,12 @@ TEST (QuadTest, op_plus_equals) Quad qd1 (num_1); Quad qd2 (num_2); Quad QD_res (qd1 += qd2); - + /* MultiPrecision */ MultiPrecision mp1 (num_1); MultiPrecision mp2 (num_2); MultiPrecision MP_res (mp1 += mp2); - + EXPECT_EQ (MP_res , QD_res); } @@ -70,12 +70,12 @@ TEST (QuadTest, op_minus_equals) Quad qd1 (num_1); Quad qd2 (num_2); Quad QD_res (qd1 -= qd2); - + /* MultiPrecision */ MultiPrecision mp1 (num_1); MultiPrecision mp2 (num_2); MultiPrecision MP_res (mp1 -= mp2); - + EXPECT_EQ (MP_res , QD_res); } @@ -86,12 +86,12 @@ TEST (QuadTest, op_mul_equals) Quad qd1 (num_1); Quad qd2 (num_2); Quad QD_res (qd1 *= qd2); - + /* MultiPrecision */ MultiPrecision mp1 (num_1); MultiPrecision mp2 (num_2); MultiPrecision MP_res (mp1 *= mp2); - + EXPECT_EQ (MP_res , QD_res); } @@ -102,12 +102,12 @@ TEST (QuadTest, op_div_equals) Quad qd1 (num_1); Quad qd2 (num_2); Quad QD_res (qd1 /= qd2); - + /* MultiPrecision */ MultiPrecision mp1 (num_1); MultiPrecision mp2 (num_2); MultiPrecision MP_res (mp1 /= mp2); - + EXPECT_EQ (MP_res , QD_res); } @@ -118,12 +118,12 @@ TEST (QuadTest, op_plus) Quad qd1 (num_1); Quad qd2 (num_2); Quad QD_res (qd1 + qd2); - + /* MultiPrecision */ MultiPrecision mp1 (num_1); MultiPrecision mp2 (num_2); MultiPrecision MP_res (mp1 + mp2); - + EXPECT_EQ (MP_res , QD_res); } @@ -134,12 +134,12 @@ TEST (QuadTest, op_minus) Quad qd1 (num_1); Quad qd2 (num_2); Quad QD_res (qd1 - qd2); - + /* MultiPrecision */ MultiPrecision mp1 (num_1); MultiPrecision mp2 (num_2); MultiPrecision MP_res (mp1 - mp2); - + EXPECT_EQ (MP_res , QD_res); } @@ -150,12 +150,12 @@ TEST (QuadTest, op_mul) Quad qd1 (num_1); Quad qd2 (num_2); Quad QD_res (qd1 * qd2); - + /* MultiPrecision */ MultiPrecision mp1 (num_1); MultiPrecision mp2 (num_2); MultiPrecision MP_res (mp1 * mp2); - + EXPECT_EQ (MP_res , QD_res); } @@ -166,12 +166,12 @@ TEST (QuadTest, op_div) Quad qd1 (num_1); Quad qd2 (num_2); Quad QD_res (qd1 / qd2); - + /* MultiPrecision */ MultiPrecision mp1 (num_1); MultiPrecision mp2 (num_2); MultiPrecision MP_res (mp1 / mp2); - + EXPECT_EQ (MP_res , QD_res); } @@ -182,12 +182,12 @@ TEST (QuadTest, cmp_equal) Quad qd1 (num_1); Quad qd2 (num_2); bool QD_res (qd1 == qd2); - + /* MultiPrecision */ MultiPrecision mp1 (num_1); MultiPrecision mp2 (num_2); bool MP_res (mp1 == mp2); - + EXPECT_EQ (MP_res , QD_res); } @@ -198,12 +198,12 @@ TEST (QuadTest, cmp_not_equal) Quad qd1 (num_1); Quad qd2 (num_2); bool QD_res (qd1 != qd2); - + /* MultiPrecision */ MultiPrecision mp1 (num_1); MultiPrecision mp2 (num_2); bool MP_res (mp1 != mp2); - + EXPECT_EQ (MP_res , QD_res); } @@ -214,12 +214,12 @@ TEST (QuadTest, cmp_less) Quad qd1 (num_1); Quad qd2 (num_2); bool QD_res (qd1 < qd2); - + /* MultiPrecision */ MultiPrecision mp1 (num_1); MultiPrecision mp2 (num_2); bool MP_res (mp1 < mp2); - + EXPECT_EQ (MP_res , QD_res); } @@ -230,12 +230,12 @@ TEST (QuadTest, cmp_greater) Quad qd1 (num_1); Quad qd2 (num_2); bool QD_res (qd1 > qd2); - + /* MultiPrecision */ MultiPrecision mp1 (num_1); MultiPrecision mp2 (num_2); bool MP_res (mp1 > mp2); - + EXPECT_EQ (MP_res , QD_res); } @@ -246,12 +246,12 @@ TEST (QuadTest, cmp_greater_equal) Quad qd1 (num_1); Quad qd2 (num_2); bool QD_res (qd1 >= qd2); - + /* MultiPrecision */ MultiPrecision mp1 (num_1); MultiPrecision mp2 (num_2); bool MP_res (mp1 >= mp2); - + EXPECT_EQ (MP_res , QD_res); } @@ -262,18 +262,18 @@ TEST (QuadTest, cmp_less_equal) Quad qd1 (num_1); Quad qd2 (num_2); bool QD_res (qd1 <= qd2); - + /* MultiPrecision */ MultiPrecision mp1 (num_1); MultiPrecision mp2 (num_2); bool MP_res (mp1 <= mp2); - + EXPECT_EQ (MP_res , QD_res); } #endif -#include "GtestMissingError.h" +#include "../GtestMissingError.h" int main( int argc, char* argv[] ) { #ifdef HAVE_GTEST diff --git a/src/UnitTests/CMakeLists.txt b/src/UnitTests/CMakeLists.txt index 2c0ba8650..80d2d61a0 100644 --- a/src/UnitTests/CMakeLists.txt +++ b/src/UnitTests/CMakeLists.txt @@ -1,3 +1,5 @@ +ADD_SUBDIRECTORY( Algorithms ) +ADD_SUBDIRECTORY( Arithmetics ) ADD_SUBDIRECTORY( Containers ) ADD_SUBDIRECTORY( Functions ) # Matrices are included from src/CMakeLists.txt diff --git a/src/UnitTests/Containers/VectorEvaluateAndReduceTest.h b/src/UnitTests/Containers/VectorEvaluateAndReduceTest.h index 8c0cd7f90..b32e39788 100644 --- a/src/UnitTests/Containers/VectorEvaluateAndReduceTest.h +++ b/src/UnitTests/Containers/VectorEvaluateAndReduceTest.h @@ -13,7 +13,6 @@ #ifdef HAVE_GTEST #include -#include #include #include #include "VectorTestSetup.h" diff --git a/src/UnitTests/Containers/VectorTestSetup.h b/src/UnitTests/Containers/VectorTestSetup.h index c8ec42bea..c9863009b 100644 --- a/src/UnitTests/Containers/VectorTestSetup.h +++ b/src/UnitTests/Containers/VectorTestSetup.h @@ -13,7 +13,7 @@ #ifdef HAVE_GTEST #include -#include +#include #include #include #include "VectorHelperFunctions.h" -- GitLab From c4c46d1f5dc3cb3136984503faec57420a0d5ccc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Tue, 30 Mar 2021 21:51:30 +0200 Subject: [PATCH 12/19] Fixed broken MultireductionTest --- src/UnitTests/Algorithms/MultireductionTest.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/UnitTests/Algorithms/MultireductionTest.h b/src/UnitTests/Algorithms/MultireductionTest.h index ec674d935..50286ca15 100644 --- a/src/UnitTests/Algorithms/MultireductionTest.h +++ b/src/UnitTests/Algorithms/MultireductionTest.h @@ -71,7 +71,7 @@ protected: result.setSize( n ); for( int i = 0; i < n; i++ ) { - DeviceView v( &V[ i * size ], size ); + DeviceView v( V.getData() + i * size, size ); if( i % 2 == 0 ) setLinearSequence( v ); else -- GitLab From 6e7a9fe0e5ce3aa25b0b2063be3059d3a087666b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Tue, 30 Mar 2021 21:34:47 +0200 Subject: [PATCH 13/19] Revert the value returned from getMaxGridSize The new value is not correct, it breaks e.g. ParallelFor2D and ParallelFor3D (for which we had disabled tests by mistake). We need to separate the max values for x, y and z axes, see the linked Gitlab issue. This reverts the commit 37632fd98a4450803d9077bf03e3f3c7b620c116 --- src/TNL/Cuda/LaunchHelpers.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/TNL/Cuda/LaunchHelpers.h b/src/TNL/Cuda/LaunchHelpers.h index 2b7113f43..a9e8bc168 100644 --- a/src/TNL/Cuda/LaunchHelpers.h +++ b/src/TNL/Cuda/LaunchHelpers.h @@ -15,9 +15,11 @@ namespace TNL { namespace Cuda { -inline constexpr size_t getMaxGridSize() +// TODO: the max grid size depends on the axis: (x,y,z): (2147483647, 65535, 65535) +// see https://mmg-gitlab.fjfi.cvut.cz/gitlab/tnl/tnl-dev/-/issues/4 +inline constexpr std::size_t getMaxGridSize() { - return 2147483647;//65535; + return 65535; } inline constexpr int getMaxBlockSize() -- GitLab From 477085972e84c48c22df40c26bf624fc29c4f8d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Wed, 31 Mar 2021 12:41:03 +0200 Subject: [PATCH 14/19] Documentation: writing mesh tutorial --- Documentation/Tutorials/Meshes/CMakeLists.txt | 2 + .../Meshes/MeshConfigurationExample.cpp | 60 +++++++++ .../Tutorials/Meshes/MeshIterationExample.cpp | 71 +++++++++++ .../Tutorials/Meshes/ReadMeshExample.cpp | 14 ++- .../Tutorials/Meshes/tutorial_Meshes.md | 116 +++++++++++------- src/TNL/Meshes/Mesh.h | 35 +++++- src/TNL/Meshes/MeshEntity.h | 24 ++-- 7 files changed, 261 insertions(+), 61 deletions(-) create mode 100644 Documentation/Tutorials/Meshes/MeshConfigurationExample.cpp create mode 100644 Documentation/Tutorials/Meshes/MeshIterationExample.cpp diff --git a/Documentation/Tutorials/Meshes/CMakeLists.txt b/Documentation/Tutorials/Meshes/CMakeLists.txt index 3af12b95c..e5d59c9e8 100644 --- a/Documentation/Tutorials/Meshes/CMakeLists.txt +++ b/Documentation/Tutorials/Meshes/CMakeLists.txt @@ -2,6 +2,8 @@ set( COMMON_EXAMPLES ) set( CPP_EXAMPLES ReadMeshExample + MeshConfigurationExample + MeshIterationExample ${COMMON_EXAMPLES} ) set( CUDA_EXAMPLES diff --git a/Documentation/Tutorials/Meshes/MeshConfigurationExample.cpp b/Documentation/Tutorials/Meshes/MeshConfigurationExample.cpp new file mode 100644 index 000000000..b8b71a223 --- /dev/null +++ b/Documentation/Tutorials/Meshes/MeshConfigurationExample.cpp @@ -0,0 +1,60 @@ +#include + +// Define the tag for the MeshTypeResolver configuration +struct MyConfigTag {}; + +//! [Configuration example] +namespace TNL { +namespace Meshes { +namespace BuildConfigTags { + +// Create a template specialization of the tag specifying the MeshConfig template to use as the Config parameter for the mesh. +template<> +struct MeshConfigTemplateTag< MyConfigTag > +{ + template< typename Cell, + int WorldDimension = Cell::dimension, + typename Real = double, + typename GlobalIndex = int, + typename LocalIndex = GlobalIndex > + struct MeshConfig + : public DefaultConfig< Cell, WorldDimension, Real, GlobalIndex, LocalIndex > + { + template< typename EntityTopology > + static constexpr bool subentityStorage( EntityTopology, int SubentityDimension ) + { + return SubentityDimension == 0 && EntityTopology::dimension >= Cell::dimension - 1; + } + }; +}; + +} // namespace BuildConfigTags +} // namespace Meshes +} // namespace TNL +//! [Configuration example] + +namespace TNL { +namespace Meshes { +namespace BuildConfigTags { + +// disable all grids +template< int Dimension, typename Real, typename Device, typename Index > +struct GridTag< MyConfigTag, Grid< Dimension, Real, Device, Index > > +{ enum { enabled = false }; }; + +template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Triangle > { enum { enabled = true }; }; + +} // namespace BuildConfigTags +} // namespace Meshes +} // namespace TNL + +int main( int argc, char* argv[] ) +{ + const std::string inputFileName = "example-triangles.vtu"; + + auto wrapper = [] ( auto& reader, auto&& mesh ) -> bool + { + return true; + }; + return ! TNL::Meshes::resolveAndLoadMesh< MyConfigTag, TNL::Devices::Host >( wrapper, inputFileName, "auto" ); +} diff --git a/Documentation/Tutorials/Meshes/MeshIterationExample.cpp b/Documentation/Tutorials/Meshes/MeshIterationExample.cpp new file mode 100644 index 000000000..fa375da43 --- /dev/null +++ b/Documentation/Tutorials/Meshes/MeshIterationExample.cpp @@ -0,0 +1,71 @@ +#include + +// Define the tag for the MeshTypeResolver configuration +struct MyConfigTag {}; + +namespace TNL { +namespace Meshes { +namespace BuildConfigTags { + +// disable all grids +template< int Dimension, typename Real, typename Device, typename Index > +struct GridTag< MyConfigTag, Grid< Dimension, Real, Device, Index > > +{ enum { enabled = false }; }; + +template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Triangle > { enum { enabled = true }; }; + +} // namespace BuildConfigTags +} // namespace Meshes +} // namespace TNL + +// Define the main task/function of the program +template< typename Mesh > +bool task( const Mesh& mesh ) +{ + //! [getEntitiesCount] + const int num_vertices = mesh.template getEntitiesCount< 0 >(); + const int num_cells = mesh.template getEntitiesCount< Mesh::getMeshDimension() >(); + //! [getEntitiesCount] + + // shut up warnings about unused variables + (void) num_vertices; + (void) num_cells; + + const int idx = num_vertices - 1; + const int idx2 = num_cells - 1; + + //! [getEntity] + typename Mesh::Vertex vert = mesh.template getEntity< 0 >( idx ); + typename Mesh::Cell elem = mesh.template getEntity< Mesh::getMeshDimension() >( idx2 ); + //! [getEntity] + + // shut up warnings about unused variables + (void) vert; + (void) elem; + +{ + //! [Iteration over subentities] + typename Mesh::Cell elem = mesh.template getEntity< Mesh::getMeshDimension() >( idx2 ); + const int n_subvert = elem.template getSubentitiesCount< 0 >(); + for( int v = 0; v < n_subvert; v++ ) { + const int v_idx = elem.template getSubentityIndex< 0 >( v ); + typename Mesh::Vertex vert = mesh.template getEntity< 0 >( v_idx ); + // [Do some work...] + (void) vert; + } + //! [Iteration over subentities] +} + + return true; +} + +int main( int argc, char* argv[] ) +{ + const std::string inputFileName = "example-triangles.vtu"; + + auto wrapper = [] ( auto& reader, auto&& mesh ) -> bool + { + return task( mesh ); + }; + return ! TNL::Meshes::resolveAndLoadMesh< MyConfigTag, TNL::Devices::Host >( wrapper, inputFileName, "auto" ); +} diff --git a/Documentation/Tutorials/Meshes/ReadMeshExample.cpp b/Documentation/Tutorials/Meshes/ReadMeshExample.cpp index 9508ee1f5..bebf2e030 100644 --- a/Documentation/Tutorials/Meshes/ReadMeshExample.cpp +++ b/Documentation/Tutorials/Meshes/ReadMeshExample.cpp @@ -1,19 +1,22 @@ +//! [config] #include // Define the tag for the MeshTypeResolver configuration -struct MeshConfigTag {}; +struct MyConfigTag {}; namespace TNL { namespace Meshes { namespace BuildConfigTags { -template<> struct MeshCellTopologyTag< MeshConfigTag, Topologies::Triangle > { enum { enabled = true }; }; -template<> struct MeshCellTopologyTag< MeshConfigTag, Topologies::Quadrangle > { enum { enabled = true }; }; +template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Triangle > { enum { enabled = true }; }; +template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Quadrangle > { enum { enabled = true }; }; } // namespace BuildConfigTags } // namespace Meshes } // namespace TNL +//! [config] +//! [task] // Define the main task/function of the program template< typename Mesh > bool task( const Mesh& mesh, const std::string& inputFileName ) @@ -22,7 +25,9 @@ bool task( const Mesh& mesh, const std::string& inputFileName ) << TNL::getType() << std::endl; return true; } +//! [task] +//! [main] int main( int argc, char* argv[] ) { const std::string inputFileName = "example-triangles.vtu"; @@ -31,5 +36,6 @@ int main( int argc, char* argv[] ) { return task( mesh, inputFileName ); }; - return ! TNL::Meshes::resolveAndLoadMesh< MeshConfigTag, TNL::Devices::Host >( wrapper, inputFileName, "auto" ); + return ! TNL::Meshes::resolveAndLoadMesh< MyConfigTag, TNL::Devices::Host >( wrapper, inputFileName, "auto" ); } +//! [main] diff --git a/Documentation/Tutorials/Meshes/tutorial_Meshes.md b/Documentation/Tutorials/Meshes/tutorial_Meshes.md index 480dfee0a..e75a42880 100644 --- a/Documentation/Tutorials/Meshes/tutorial_Meshes.md +++ b/Documentation/Tutorials/Meshes/tutorial_Meshes.md @@ -16,14 +16,11 @@ The [DistributedMesh](@ref TNL::Meshes::DistributedMeshes::DistributedMesh) clas The most common way of mesh initialization is by reading a prepared input file created by an external program. TNL provides classes and functions for reading the common VTK, VTU and Netgen file formats. -\dontinclude ReadMeshExample.cpp - The main difficulty is mapping the mesh included in the file to the correct C++ type, which can represent the mesh stored in the file. This can be done with the [MeshTypeResolver](@ref TNL::Meshes::MeshTypeResolver) class, which needs to be configured to enable the processing of the specific cell topologies, which we want our program to handle. For example, in the following code we enable loading of 2D triangular and quadrangular meshes: -\skip #include -\until // namespace TNL +\snippet ReadMeshExample.cpp config There are other build config tags which can be used to enable or disable specific types used in the mesh: `RealType`, `GlobalIndexType` and `LocalIndexType`. See the [BuildConfigTags](@ref TNL::Meshes::BuildConfigTags) namespace for an overview of these tags. @@ -31,16 +28,13 @@ See the [BuildConfigTags](@ref TNL::Meshes::BuildConfigTags) namespace for an ov Next, we can define the main task of our program as a templated function, which will be ultimately launched with the correct mesh type based on the input file. We can also use any number of additional parameters, such as the input file name: -\skip Define the main task -\until } +\snippet ReadMeshExample.cpp task Of course in practice, the function would be much more complex than this example, where we just print the file name and some textual representation of the mesh to the standard output. Finally, we define the `main` function, which sets the input parameters (hard-coded in this example) and calls the [resolveAndLoadMesh](@ref TNL::Meshes::resolveAndLoadMesh) function to resolve the mesh type and load the mesh from the file into the created object: -\skip int main -\until } -\until } +\snippet ReadMeshExample.cpp main We need to specify two template parameters when calling `resolveAndLoadMesh`: @@ -56,7 +50,7 @@ Hence, the return type of both `wrapper` and `task` needs to be `bool` as well. For completeness, the full example follows: \includelineno ReadMeshExample.cpp -## Mesh configuration +## Mesh configuration {#configuration} The [Mesh](@ref TNL::Meshes::Mesh) class template is configurable via its first template parameter, `Config`. By default, the \ref TNL::Meshes::DefaultConfig template is used. @@ -64,46 +58,82 @@ Alternative, user-specified configuration templates can be specified by defining For example, here we derive the `MeshConfig` template from the [DefaultConfig](@ref TNL::Meshes::DefaultConfig) template and override the `subentityStorage` member function to store only those subentity incidence matrices, where the subentity dimension is 0 and the other dimension is at least $D-1$. Hence, only faces and cells will be able to access their subvertices and there will be no other links from entities to their subentities. - -```cpp -// Define the tag for the MeshTypeResolver configuration -struct MyConfigTag {}; - -namespace TNL { -namespace Meshes { -namespace BuildConfigTags { - -// Create a template specialization of the tag specifying the MeshConfig template to use as the Config parameter for the mesh. -template<> -struct MeshConfigTemplateTag< MyConfigTag > -{ - template< typename Cell, - int WorldDimension = Cell::dimension, - typename Real = double, - typename GlobalIndex = int, - typename LocalIndex = GlobalIndex > - struct MeshConfig - : public DefaultConfig< Cell, WorldDimension, Real, GlobalIndex, LocalIndex > - { - template< typename EntityTopology > - static constexpr bool subentityStorage( EntityTopology, int SubentityDimension ) - { - return SubentityDimension == 0 && EntityTopology::dimension >= Cell::dimension - 1; - } - }; -}; - -} // namespace BuildConfigTags -} // namespace Meshes -} // namespace TNL -``` +\snippet MeshConfigurationExample.cpp Configuration example ## Public interface and basic usage The whole public interface of the unstructured mesh and its mesh entity class can be found in the reference manual: \ref TNL::Meshes::Mesh, \ref TNL::Meshes::MeshEntity. +Here we describe only the basic member functions. + +The main purpose of the [Mesh](@ref TNL::Meshes::Mesh) class template is to provide access to the mesh entities. +Firstly, there is a member function template called [getEntitiesCount](@ref TNL::Meshes::Mesh::getEntitiesCount) which returns the number of entities of the dimension specified as the template argument. +Given a mesh instance denoted as `mesh`, it can be used like this: + +\snippet MeshIterationExample.cpp getEntitiesCount + +Note that this member function and all other member functions presented below are marked as [\_\_cuda\_callable\_\_](tutorial_GeneralConcepts.html), so they can be called from usual host functions as well as CUDA kernels. + +The entity of given dimension and index can be accessed via a member function template called [getEntity](@ref TNL::Meshes::Mesh::getEntity). +Again, the entity dimension is specified as a template argument and the index is specified as a method argument. +The `getEntity` member function does not provide a _reference_ access to an entity stored in the mesh, but each entity is created _on demand_ and contains only a pointer to the mesh and the supplied entity index. +Hence, the mesh entity is kind of a _proxy object_ where all member functions call just an appropriate member function via the mesh pointer. +The `getEntity` member function can be used like this: + +\snippet MeshIterationExample.cpp getEntity + +Here we assume that `idx < num_vertices` and `idx2 < num_cells`. +Note that both `Mesh::Vertex` and `Mesh::Cell` are specific instances of the [MeshEntity](@ref TNL::Meshes::MeshEntity) class template. + +The information about the subentities and superentities can be accessed via the following member functions: + +- [getSubentitiesCount](@ref TNL::Meshes::MeshEntity::getSubentitiesCount) +- [getSubentityIndex](@ref TNL::Meshes::MeshEntity::getSubentityIndex) +- [getSuperentitiesCount](@ref TNL::Meshes::MeshEntity::getSuperentitiesCount) +- [getSuperentityIndex](@ref TNL::Meshes::MeshEntity::getSuperentityIndex) + +For example, they can be combined with the `getEntity` member function to iterate over all subvertices of a specific cell: + +\snippet MeshIterationExample.cpp Iteration over subentities + +The iteration over superentities adjacent to an entity is very similar and left as an exercise for the reader. + +Note that the implementations of all templated member functions providing access to subentities and superentities contain a `static_assert` expression which checks if the requested subentities or superentities are stored in the mesh according to its [configuration](#configuration). ## Parallel iteration over mesh entities +The [Mesh](@ref TNL::Meshes::Mesh) class template provides a simple interface for the parallel iteration over mesh entities of a specific dimension. +There are several member functions: + +- [forAll](@ref TNL::Meshes::Mesh::forAll) -- iterates over all mesh entities of a specific dimension +- [forBoundary](@ref TNL::Meshes::Mesh::forBoundary) -- iterates over boundary mesh entities of a specific dimension +- [forInterior](@ref TNL::Meshes::Mesh::forInterior) -- iterates over interior (i.e., not boundary) mesh entities of a specific dimension + +For distributed meshes there are two additional member functions: + +- [forGhost](@ref TNL::Meshes::Mesh::forGhost) -- iterates over ghost mesh entities of a specific dimension +- [forLocal](@ref TNL::Meshes::Mesh::forLocal) -- iterates over local (i.e., not ghost) mesh entities of a specific dimension + +All of these member functions have the same interface: they take one parameter, which should be a functor, such as a _lambda expression_ $f$ that is called as $f(i)$, where $i$ is the mesh entity index in the current iteration. +Remember that the iteration is performed _in parallel_, so all calls to the functor must be independent since they can be executed in any order. + +Note that only the mesh entity index is passed to the functor, it does not get the mesh entity object or even (a reference to) the mesh. +All additional information needed by the functor must be handled manually, e.g. via a _lambda capture_. + +For example, the iteration over cells on a mesh allocated on the host can be done as follows: + +```cpp +TODO +``` + +The parallel iteration is more complicated for meshes allocated on a GPU, since the lambda expression needs to capture a pointer to the copy of the mesh, which is allocated on the right device. +This can be achieved with a [smart pointer](tutorial_Pointers.html) as follows: + +```cpp +TODO +``` + +Alternatively, you can use a [SharedPointer](@ref TNL::Pointers::SharedPointer) instead of a [DevicePointer](@ref TNL::Pointers::DevicePointer) to allocate the mesh, but it does not allow to bind to an object which has already been created outside of the `SharedPointer`. + ## Writing a mesh and data to a file ## Example: Game of Life diff --git a/src/TNL/Meshes/Mesh.h b/src/TNL/Meshes/Mesh.h index e432d8ea6..fa5cfeb58 100644 --- a/src/TNL/Meshes/Mesh.h +++ b/src/TNL/Meshes/Mesh.h @@ -115,12 +115,19 @@ class Mesh virtual String getSerializationTypeVirtual() const; /** - * Entities + * \brief Returns the count of mesh entities of the given dimension. */ template< int Dimension > __cuda_callable__ GlobalIndexType getEntitiesCount() const; + /** + * \brief Returns the mesh entity of the given dimension and index. + * + * Note that objects representing mesh entities are not stored in the mesh, + * but created on demand. Since the \ref MeshEntity contains just a pointer + * to the mesh and the supplied entity index, the creation should be fast. + */ template< int Dimension > __cuda_callable__ EntityType< Dimension > getEntity( const GlobalIndexType entityIndex ) const; @@ -135,42 +142,56 @@ class Mesh EntityType getEntity( const GlobalIndexType entityIndex ) const; /** - * Points + * \brief Returns the spatial coordinates of the vertex with given index. */ __cuda_callable__ const PointType& getPoint( const GlobalIndexType vertexIndex ) const; + /** + * \brief Returns the spatial coordinates of the vertex with given index. + */ __cuda_callable__ PointType& getPoint( const GlobalIndexType vertexIndex ); /** - * Subentities + * \brief Returns the count of subentities of the entity with given index. */ template< int EntityDimension, int SubentityDimension > __cuda_callable__ constexpr LocalIndexType getSubentitiesCount( const GlobalIndexType entityIndex ) const; + /** + * \brief Returns the global index of the subentity specified by its local index. + */ template< int EntityDimension, int SubentityDimension > __cuda_callable__ GlobalIndexType getSubentityIndex( const GlobalIndexType entityIndex, const LocalIndexType subentityIndex ) const; /** - * Superentities + * \brief Returns the count of superentities of the entity with given index. */ template< int EntityDimension, int SuperentityDimension > __cuda_callable__ LocalIndexType getSuperentitiesCount( const GlobalIndexType entityIndex ) const; + /** + * \brief Returns the global index of the superentity specified by its local index. + */ template< int EntityDimension, int SuperentityDimension > __cuda_callable__ GlobalIndexType getSuperentityIndex( const GlobalIndexType entityIndex, const LocalIndexType superentityIndex ) const; /** - * Cell neighbors - access the dual graph + * \brief Returns the count of neighbor cells of the cell with given index, + * based on the information stored in the dual graph. */ __cuda_callable__ LocalIndexType getCellNeighborsCount( const GlobalIndexType cellIndex ) const; + /** + * \brief Returns the global index of the cell's specific neighbor cell wigh given local index, + * based on the information stored in the dual graph. + */ __cuda_callable__ GlobalIndexType getCellNeighborIndex( const GlobalIndexType cellIndex, const LocalIndexType neighborIndex ) const; @@ -231,7 +252,9 @@ class Mesh void forGhost( Func f ) const; - /* + /** + * \brief Reorders the entities of the given dimension. + * * The permutations follow the definition used in the Metis library: Let M * be the original mesh and M' the permuted mesh. Then entity with index i * in M' is the entity with index perm[i] in M and entity with index j in diff --git a/src/TNL/Meshes/MeshEntity.h b/src/TNL/Meshes/MeshEntity.h index ab4410304..b077ed045 100644 --- a/src/TNL/Meshes/MeshEntity.h +++ b/src/TNL/Meshes/MeshEntity.h @@ -70,36 +70,44 @@ class MeshEntity __cuda_callable__ GlobalIndexType getIndex() const; - /**** - * Points + /** + * \brief Returns the spatial coordinates of this vertex. + * + * Can be used only when \ref getEntityDimension returns 0. */ __cuda_callable__ PointType getPoint() const; - /**** - * Subentities + /** + * \brief Returns the count of subentities of this entity. */ template< int Subdimension > __cuda_callable__ LocalIndexType getSubentitiesCount() const; + /** + * \brief Returns the global index of the subentity specified by its local index. + */ template< int Subdimension > __cuda_callable__ GlobalIndexType getSubentityIndex( const LocalIndexType localIndex ) const; - /**** - * Superentities + /** + * \brief Returns the count of superentities of this entity. */ template< int Superdimension > __cuda_callable__ LocalIndexType getSuperentitiesCount() const; + /** + * \brief Returns the global index of the superentity specified by its local index. + */ template< int Superdimension > __cuda_callable__ GlobalIndexType getSuperentityIndex( const LocalIndexType localIndex ) const; - /**** - * Tags + /** + * \brief Returns the tag associated with this entity. */ __cuda_callable__ TagType getTag() const; -- GitLab From e4e2d37e91ecc03b2b1a8e61fc061fc8217d352f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Sat, 3 Apr 2021 17:19:48 +0200 Subject: [PATCH 15/19] Documentation: finished the mesh tutorial --- Documentation/Tutorials/Meshes/CMakeLists.txt | 24 +- Documentation/Tutorials/Meshes/GameOfLife.cpp | 246 ++++++++++++++++++ .../Tutorials/Meshes/GameOfLifeCuda.cu | 1 + .../Meshes/MeshConfigurationExample.cpp | 2 +- .../Tutorials/Meshes/MeshIterationExample.cpp | 12 + .../Tutorials/Meshes/ParallelIterationCuda.cu | 1 + .../Tutorials/Meshes/ParallelIterationCuda.h | 60 +++++ .../Tutorials/Meshes/grid-100x100.vtu | 23 ++ .../Tutorials/Meshes/tutorial_Meshes.md | 56 +++- 9 files changed, 410 insertions(+), 15 deletions(-) create mode 100644 Documentation/Tutorials/Meshes/GameOfLife.cpp create mode 120000 Documentation/Tutorials/Meshes/GameOfLifeCuda.cu create mode 120000 Documentation/Tutorials/Meshes/ParallelIterationCuda.cu create mode 100644 Documentation/Tutorials/Meshes/ParallelIterationCuda.h create mode 100644 Documentation/Tutorials/Meshes/grid-100x100.vtu diff --git a/Documentation/Tutorials/Meshes/CMakeLists.txt b/Documentation/Tutorials/Meshes/CMakeLists.txt index e5d59c9e8..9aa61e2e5 100644 --- a/Documentation/Tutorials/Meshes/CMakeLists.txt +++ b/Documentation/Tutorials/Meshes/CMakeLists.txt @@ -1,13 +1,12 @@ -set( COMMON_EXAMPLES -) set( CPP_EXAMPLES ReadMeshExample MeshConfigurationExample MeshIterationExample - ${COMMON_EXAMPLES} + GameOfLife ) set( CUDA_EXAMPLES - ${COMMON_EXAMPLES} + ParallelIterationCuda + GameOfLifeCuda ) foreach( target IN ITEMS ${CPP_EXAMPLES} ) @@ -19,8 +18,12 @@ add_custom_target( RunTutorialsMeshesExamples ALL DEPENDS ${CPP_OUTPUTS} ) if( BUILD_CUDA ) foreach( target IN ITEMS ${CUDA_EXAMPLES} ) - cuda_add_executable( ${target}-cuda ${target}.cu OPTIONS ) - add_custom_command( COMMAND ${target}-cuda > ${TNL_DOCUMENTATION_OUTPUT_SNIPPETS_PATH}/${target}.out OUTPUT ${target}.out ) + # FIXME: zlib and tinyxml2 should be conditional, but the custom command fails without them anyway... +# cuda_add_executable( ${target} ${target}.cu OPTIONS ) + cuda_add_executable( ${target} ${target}.cu OPTIONS "-DHAVE_ZLIB -DHAVE_TINYXML2" ) + target_link_libraries(${target} ${ZLIB_LIBRARIES} tinyxml2::tinyxml2) + + add_custom_command( COMMAND ${target} > ${TNL_DOCUMENTATION_OUTPUT_SNIPPETS_PATH}/${target}.out OUTPUT ${target}.out ) set( CUDA_OUTPUTS ${CUDA_OUTPUTS} ${target}.out ) endforeach() add_custom_target( RunTutorialsMeshesCudaExamples ALL DEPENDS ${CUDA_OUTPUTS} ) @@ -28,7 +31,9 @@ endif() find_package( ZLIB ) if( ZLIB_FOUND ) - foreach( target IN ITEMS ${CPP_EXAMPLES} ${CUDA_EXAMPLES} ) + # FIXME +# foreach( target IN ITEMS ${CPP_EXAMPLES} ${CUDA_EXAMPLES} ) + foreach( target IN ITEMS ${CPP_EXAMPLES} ) target_compile_definitions(${target} PUBLIC "-DHAVE_ZLIB") target_include_directories(${target} PUBLIC ${ZLIB_INCLUDE_DIRS}) target_link_libraries(${target} ${ZLIB_LIBRARIES}) @@ -37,7 +42,9 @@ endif() find_package( tinyxml2 QUIET ) if( tinyxml2_FOUND ) - foreach( target IN ITEMS ${CPP_EXAMPLES} ${CUDA_EXAMPLES} ) + # FIXME +# foreach( target IN ITEMS ${CPP_EXAMPLES} ${CUDA_EXAMPLES} ) + foreach( target IN ITEMS ${CPP_EXAMPLES} ) target_compile_definitions(${target} PUBLIC "-DHAVE_TINYXML2") target_link_libraries(${target} tinyxml2::tinyxml2) endforeach() @@ -46,6 +53,7 @@ endif() set( DATA_FILES example-triangles.vtu + grid-100x100.vtu ) foreach( file IN ITEMS ${DATA_FILES} ) diff --git a/Documentation/Tutorials/Meshes/GameOfLife.cpp b/Documentation/Tutorials/Meshes/GameOfLife.cpp new file mode 100644 index 000000000..9d3fc251b --- /dev/null +++ b/Documentation/Tutorials/Meshes/GameOfLife.cpp @@ -0,0 +1,246 @@ +#include + +#include +#include +#include + +using namespace TNL; + +struct MyConfigTag {}; + +namespace TNL { +namespace Meshes { +namespace BuildConfigTags { + +// disable all grids +template< int Dimension, typename Real, typename Device, typename Index > +struct GridTag< MyConfigTag, Grid< Dimension, Real, Device, Index > > +{ enum { enabled = false }; }; + +// Meshes are enabled only for topologies explicitly listed below. +//template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Edge > { enum { enabled = true }; }; +template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Triangle > { enum { enabled = true }; }; +template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Quadrangle > { enum { enabled = true }; }; +//template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Tetrahedron > { enum { enabled = true }; }; +//template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Hexahedron > { enum { enabled = true }; }; + +// Meshes are enabled only for the world dimension equal to the cell dimension. +template< typename CellTopology, int WorldDimension > +struct MeshWorldDimensionTag< MyConfigTag, CellTopology, WorldDimension > +{ enum { enabled = ( WorldDimension == CellTopology::dimension ) }; }; + +// Meshes are enabled only for types explicitly listed below. +template<> struct MeshRealTag< MyConfigTag, float > { enum { enabled = false }; }; +template<> struct MeshRealTag< MyConfigTag, double > { enum { enabled = true }; }; +template<> struct MeshGlobalIndexTag< MyConfigTag, int > { enum { enabled = true }; }; +template<> struct MeshGlobalIndexTag< MyConfigTag, long int > { enum { enabled = false }; }; +template<> struct MeshLocalIndexTag< MyConfigTag, short int > { enum { enabled = true }; }; + +// Config tag specifying the MeshConfig template to use. +template<> +struct MeshConfigTemplateTag< MyConfigTag > +{ + template< typename Cell, + int WorldDimension = Cell::dimension, + typename Real = double, + typename GlobalIndex = int, + typename LocalIndex = short int > + struct MeshConfig + : public DefaultConfig< Cell, WorldDimension, Real, GlobalIndex, LocalIndex > + { + template< typename EntityTopology > + static constexpr bool subentityStorage( EntityTopology, int SubentityDimension ) + { + return SubentityDimension == 0 && EntityTopology::dimension >= Cell::dimension - 1; + } + + template< typename EntityTopology > + static constexpr bool superentityStorage( EntityTopology, int SuperentityDimension ) + { +// return false; + return (EntityTopology::dimension == 0 || EntityTopology::dimension == Cell::dimension - 1) && SuperentityDimension == Cell::dimension; + } + + template< typename EntityTopology > + static constexpr bool entityTagsStorage( EntityTopology ) + { +// return false; + return EntityTopology::dimension == 0 || EntityTopology::dimension >= Cell::dimension - 1; + } + + static constexpr bool dualGraphStorage() + { + return true; + } + + static constexpr int dualGraphMinCommonVertices = 1; + }; +}; + +} // namespace BuildConfigTags +} // namespace Meshes +} // namespace TNL + + +template< typename Mesh > +bool runGameOfLife( const Mesh& mesh ) +{ + //! [Data vectors] + using Index = typename Mesh::GlobalIndexType; + const Index cellsCount = mesh.template getEntitiesCount< Mesh::getMeshDimension() >(); + + using VectorType = Containers::Vector< std::uint8_t, typename Mesh::DeviceType, Index >; + VectorType f_in( cellsCount ), f_out( cellsCount ); + f_in.setValue( 0 ); + //! [Data vectors] + +#if 1 + // generate random initial condition + std::random_device dev; + std::mt19937 rng(dev()); + std::uniform_int_distribution<> dist(0, 1); + for( Index i = 0; i < cellsCount; i++ ) + f_in[ i ] = dist(rng); + const Index max_iter = 100; +#else + // find a reference cell - the one closest to the point + typename Mesh::PointType p = {0.5, 0.5}; + typename Mesh::RealType dist = 1e5; + Index reference_cell = 0; + for( Index i = 0; i < cellsCount; i++ ) { + const auto cell = mesh.template getEntity< Mesh::getMeshDimension() >( i ); + const auto c = getEntityCenter( mesh, cell ); + const auto d = TNL::l2Norm( c - p ); + if( d < dist ) { + reference_cell = i; + dist = d; + } + } + // R-pentomino (stabilizes after 1103 iterations) + const Index max_iter = 1103; + f_in[reference_cell] = 1; + Index n1 = mesh.getCellNeighborIndex(reference_cell,1); // bottom + Index n2 = mesh.getCellNeighborIndex(reference_cell,2); // left + Index n3 = mesh.getCellNeighborIndex(reference_cell,5); // top + Index n4 = mesh.getCellNeighborIndex(reference_cell,6); // top-right + f_in[n1] = 1; + f_in[n2] = 1; + f_in[n3] = 1; + f_in[n4] = 1; +/* + // Acorn (stabilizes after 5206 iterations) + const Index max_iter = 5206; + f_in[reference_cell] = 1; + Index n1 = mesh.getCellNeighborIndex(reference_cell,4); + f_in[n1] = 1; + Index s1 = mesh.getCellNeighborIndex(n1,4); + Index s2 = mesh.getCellNeighborIndex(s1,4); + Index n2 = mesh.getCellNeighborIndex(s2,4); + f_in[n2] = 1; + Index n3 = mesh.getCellNeighborIndex(n2,4); + f_in[n3] = 1; + Index n4 = mesh.getCellNeighborIndex(n3,4); + f_in[n4] = 1; + f_in[mesh.getCellNeighborIndex(s2,5)] = 1; + f_in[mesh.getCellNeighborIndex(mesh.getCellNeighborIndex(n1,5),5)] = 1; +*/ +#endif + + //! [make_snapshot] + auto make_snapshot = [&] ( Index iteration ) + { + const std::string filePath = "GoL." + std::to_string(iteration) + ".vtu"; + std::ofstream file( filePath ); + using Writer = Meshes::Writers::VTUWriter< Mesh >; + Writer writer( file ); + writer.writeMetadata( iteration, iteration ); + writer.template writeEntities< Mesh::getMeshDimension() >( mesh ); + writer.writeCellData( f_in, "function values" ); + }; + //! [make_snapshot] + + //! [make initial snapshot] + // write initial state + make_snapshot( 0 ); + //! [make initial snapshot] + + // captures for the iteration kernel + auto f_in_view = f_in.getConstView(); + auto f_out_view = f_out.getView(); + Pointers::DevicePointer< const Mesh > meshDevicePointer( mesh ); + const Mesh* meshPointer = &meshDevicePointer.template getData< typename Mesh::DeviceType >(); + + bool all_done = false; + Index iteration = 0; + do { + iteration++; + std::cout << "Computing iteration " << iteration << "..." << std::endl; + + //! [Game of Life kernel] + auto kernel = [f_in_view, f_out_view, meshPointer] __cuda_callable__ ( Index i ) mutable + { + // sum values of the function on the neighbor cells + typename VectorType::RealType sum = 0; + for( Index n = 0; n < meshPointer->getCellNeighborsCount( i ); n++ ) { + const Index neighbor = meshPointer->getCellNeighborIndex( i, n ); + sum += f_in_view[ neighbor ]; + } + const bool live = f_in_view[ i ]; + + // Conway's rules for square grid + if( live ) { + // any live cell with less than two live neighbors dies + if( sum < 2 ) + f_out_view[ i ] = 0; + // any live cell with two or three live neighbors survives + else if( sum < 4 ) + f_out_view[ i ] = 1; + // any live cell with more than three live neighbors dies + else + f_out_view[ i ] = 0; + } + else { + // any dead cell with exactly three live neighbors becomes a live cell + if( sum == 3 ) + f_out_view[ i ] = 1; + // any other dead cell remains dead + else + f_out_view[ i ] = 0; + } + }; + //! [Game of Life kernel] + + //! [Game of Life iteration] + // iterate over all cells + mesh.template forAll< Mesh::getMeshDimension() >( kernel ); + + // swap input and output arrays + f_in.swap( f_out ); + // remember to update the views! + f_in_view.bind( f_in.getView() ); + f_out_view.bind( f_out.getView() ); + + // write output + make_snapshot( iteration ); + + // check if finished + all_done = max( f_in ) == 0 || iteration > max_iter || f_in == f_out; + //! [Game of Life iteration] + } + while( all_done == false ); + + return true; +} + +int main( int argc, char* argv[] ) +{ + const std::string inputFileName = "grid-100x100.vtu"; + const std::string inputFileFormat = "auto"; + + auto wrapper = [&] ( auto& reader, auto&& mesh ) -> bool + { + using MeshType = std::decay_t< decltype(mesh) >; + return runGameOfLife( std::forward(mesh) ); + }; + return ! Meshes::resolveAndLoadMesh< MyConfigTag, Devices::Host >( wrapper, inputFileName, inputFileFormat ); +} diff --git a/Documentation/Tutorials/Meshes/GameOfLifeCuda.cu b/Documentation/Tutorials/Meshes/GameOfLifeCuda.cu new file mode 120000 index 000000000..be4d635c0 --- /dev/null +++ b/Documentation/Tutorials/Meshes/GameOfLifeCuda.cu @@ -0,0 +1 @@ +GameOfLife.cpp \ No newline at end of file diff --git a/Documentation/Tutorials/Meshes/MeshConfigurationExample.cpp b/Documentation/Tutorials/Meshes/MeshConfigurationExample.cpp index b8b71a223..174748ad3 100644 --- a/Documentation/Tutorials/Meshes/MeshConfigurationExample.cpp +++ b/Documentation/Tutorials/Meshes/MeshConfigurationExample.cpp @@ -16,7 +16,7 @@ struct MeshConfigTemplateTag< MyConfigTag > int WorldDimension = Cell::dimension, typename Real = double, typename GlobalIndex = int, - typename LocalIndex = GlobalIndex > + typename LocalIndex = short int > struct MeshConfig : public DefaultConfig< Cell, WorldDimension, Real, GlobalIndex, LocalIndex > { diff --git a/Documentation/Tutorials/Meshes/MeshIterationExample.cpp b/Documentation/Tutorials/Meshes/MeshIterationExample.cpp index fa375da43..5f27f7dc3 100644 --- a/Documentation/Tutorials/Meshes/MeshIterationExample.cpp +++ b/Documentation/Tutorials/Meshes/MeshIterationExample.cpp @@ -56,6 +56,18 @@ bool task( const Mesh& mesh ) //! [Iteration over subentities] } +{ + //! [Parallel iteration host] + auto kernel = [&mesh] ( typename Mesh::GlobalIndexType i ) mutable + { + typename Mesh::Cell elem = mesh.template getEntity< Mesh::getMeshDimension() >( i ); + // [Do some work with the current cell `elem`...] + (void) elem; + }; + mesh.template forAll< Mesh::getMeshDimension() >( kernel ); + //! [Parallel iteration host] +} + return true; } diff --git a/Documentation/Tutorials/Meshes/ParallelIterationCuda.cu b/Documentation/Tutorials/Meshes/ParallelIterationCuda.cu new file mode 120000 index 000000000..a871a672a --- /dev/null +++ b/Documentation/Tutorials/Meshes/ParallelIterationCuda.cu @@ -0,0 +1 @@ +ParallelIterationCuda.h \ No newline at end of file diff --git a/Documentation/Tutorials/Meshes/ParallelIterationCuda.h b/Documentation/Tutorials/Meshes/ParallelIterationCuda.h new file mode 100644 index 000000000..5aaf6eb9a --- /dev/null +++ b/Documentation/Tutorials/Meshes/ParallelIterationCuda.h @@ -0,0 +1,60 @@ +// NOTE: this file must be a *.h file, because Doxygen does not highlight the syntax of +// snippets included from a *.cu file + +#include +#include + +// Define the tag for the MeshTypeResolver configuration +struct MyConfigTag {}; + +namespace TNL { +namespace Meshes { +namespace BuildConfigTags { + +// disable all grids +template< int Dimension, typename Real, typename Device, typename Index > +struct GridTag< MyConfigTag, Grid< Dimension, Real, Device, Index > > +{ enum { enabled = false }; }; + +template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Triangle > { enum { enabled = true }; }; + +} // namespace BuildConfigTags +} // namespace Meshes +} // namespace TNL + +// Define the main task/function of the program +template< typename HostMesh > +bool task( const HostMesh& hostMesh ) +{ + //! [Parallel iteration CUDA] + // Copy the mesh from host to the device + using DeviceMesh = TNL::Meshes::Mesh< typename HostMesh::Config, TNL::Devices::Cuda >; + DeviceMesh deviceMesh = hostMesh; + + // Create a device pointer to the device mesh + TNL::Pointers::DevicePointer< const DeviceMesh > meshDevicePointer( deviceMesh ); + const DeviceMesh* meshPointer = &meshDevicePointer.template getData< typename DeviceMesh::DeviceType >(); + + // Define and execute the kernel on the device + auto kernel = [meshPointer] __cuda_callable__ ( typename DeviceMesh::GlobalIndexType i ) mutable + { + typename DeviceMesh::Cell elem = meshPointer->template getEntity< DeviceMesh::getMeshDimension() >( i ); + // [Do some work with the current cell `elem`...] + (void) elem; + }; + deviceMesh.template forAll< DeviceMesh::getMeshDimension() >( kernel ); + //! [Parallel iteration CUDA] + + return true; +} + +int main( int argc, char* argv[] ) +{ + const std::string inputFileName = "example-triangles.vtu"; + + auto wrapper = [] ( auto& reader, auto&& mesh ) -> bool + { + return task( mesh ); + }; + return ! TNL::Meshes::resolveAndLoadMesh< MyConfigTag, TNL::Devices::Host >( wrapper, inputFileName, "auto" ); +} diff --git a/Documentation/Tutorials/Meshes/grid-100x100.vtu b/Documentation/Tutorials/Meshes/grid-100x100.vtu new file mode 100644 index 000000000..d1414a00b --- /dev/null +++ b/Documentation/Tutorials/Meshes/grid-100x100.vtu @@ -0,0 +1,23 @@ + + + + + + +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+nwOxemuPs8jcbqrz/9YnO7q6zoRp7v6ek/F6a6ew5k43vR8zsV/HhfD3My3f59xnac53dVzvhSnu3r+V+Lbv8+47sV8+/cZ131di9Ndvccb8e3fZ1z3eyu+/fuM697N6a724U58+/cZV0/uxbd/n3H1x5zuaq8ehrmZ0109T/Pt32dc52xOd/X8Hwdvzemu9vlp2Jc53dV7fB78N9/+fcb1vvg17N2c7mofXsS3f59x9cSc7mp/zOmu9up18Mqc7mqvzLd/n3H1ypzuaq/exLd/n3H1ypzuaq/M6a726n3wynz79xlXrz4Gr8zprvbqc/DKnO5qr74Gr8zprvbqe+A8LtRdzXfE6a5+/q443dXH2ROnu/r4++J0V7/ugTjd1edzKL79e0vreR6J0119/sfi28/7WK/rRHz7+xLr9Z6Kb3/evM7hTHzb6+t8zsW3X69zM6e7Vk53rZzuWjndtXK6a+V018rprpXTXSunu1ZOd62c7lo53bVyumvldNfK6a6V010rp7tWTnetnO5aOd21crpr5XTXyumulf88Hoa5mdNdK6e7Vk53rZzuWjndtXK6a+V018rprpXTXSunu1ZOd62c7lo53bVyumvldNfK6a6V010rp7tWTnetnO5aOd21crpr5XTXyn8er4NX5nTXyumuldNdK6e7Vk53rZzuWjndtXK6a+V018rprpXTXSunu1ZOd62c7lo53bVyumvldNfK6a6V010rp7tWTnetnO5aOd218p/H98DX/x6ufxfb3bVyuqufvytOd/Vx9sTprj7+vjjd1a97IE539fkcitNdfZ5H4nRXn/+xON3V13UiTnf19Z6K0109hzNxuqvncy7+87gY5ma+/bvY7q6V010950txuqvnfyW+/bvY7q6Vb/8utrtr5XRX7/FGfPt3sd1dK9/+XWx318rprvbhTnz7d7HdXSvf/l1sd9fK6a726mGYmznd1fM03/5dbHdX+2m+/bvY7q7eiznd1fsyp7t6j8+D/+bbv4vt7uq9m9Nd7cOL+PbvYru7+v76PfhjTne1V6+DV+Z0V3tlvv272O6u9sp8+3ex3V3tlTnd1V6Z013t1fvglfn272K7u9orc7qrvfocvDKnu9qrr8Erc7qrvfoe+Pr9hj/d1XxHnO7q5++K0119nD1xuquPvy9Od/XrHojTXX0+h+J0V5/nkTjd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J0539fH3xemuft0Dcbqrz+dQnO7q8zwSp7v6/I/F6a6+rhNxuquv91Sc7uo5nInTXT2fc/Gfx8UwN3O6q+dpTnf1nC/F6a6e/5U43dV7Mae7el/X4nRX7/FGnO7q/d6K0129d3O6q324E6e72pN7cbqr/TGnu9qrh2Fu5nRXz9Oc7uo5m9NdPf/HwVtzuqt9fhr2ZU539R6fB//N6a6+L34Nezenu9qHl+E+Mqe7+v76Pfhjztft1evglTnd1V6Z013tlTnd1V69idNd7ZU53dVemdNd7dX74JU53dVefQxemdNd7dXn4JU53dVefQ1emdNd7dX3wHm8qrua74jTXf38XXG6q4+zJ0539fH3xemuft0Dcbqrz+dQnO7q8zwSp7v6/I/F6a6+rhNxuquv91Sc7uo5nInTXT2fc/Gfx8UwN3O6a+V018rprpXTXSunu1ZOd62c7lo53bVyumvldNfK6a6V010rp7tWTnetnO5aOd21crpr5XTXyumuldNdK6e7Vk53rZzuWvnP42GYmzndtXK6a+V018rprpXTXSunu1ZOd62c7lo53bVyumvldNfKt38fbvXffPv52ut98Ut8+/lE6/1ivv39rvU+Mt++P2a9v36Lb7+/sN535tuvV6/M6a6V010rp7tWTnetnO5aOd21crpr5XTXyumuldNdK6e7Vk53rZzuWjndtXK6a+V018rprpXTXSunu1ZOd62c7lo53bVyumvlP4/vga9+/emu5jvidFc/f1ec7urj7InTXX38fXG6q1/3QJzu6vM5FKe7+jyPxOmuPv9jcbqrr+tEnO7q6z0Vp7t6DmfidFfP51z853ExzM2c7up5mtNdPedLcbqr538lTnf1Xszprt7XtTjd1Xu8Eae7er+34nRX792c7mof7sTprvbkXpzuan/M6a726mGYmznd1fM0p7t6zuZ0V8//cfDWnO5qn5+GfZnTXb3H58F/c7qr74tfw97N6a724WW4j8zprr6/fg/+mNNd7dXr4JU53dVemdNd7ZU53dVevYnTXe2VOd3VXpnTXe3V++CVOd3VXn0MXpnTXe3V5+CVOd3VXn0NXpnTXe3V98DXfv/TXc13xOmufv6uON3Vx9kTp7v6+PvidFe/7oE43dXncyhOd/V5HonTXX3+x+J0V1/XiTjd1dd7Kk539RzOxOmuns+5+M/jYpib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 + + + + + diff --git a/Documentation/Tutorials/Meshes/tutorial_Meshes.md b/Documentation/Tutorials/Meshes/tutorial_Meshes.md index e75a42880..21e982668 100644 --- a/Documentation/Tutorials/Meshes/tutorial_Meshes.md +++ b/Documentation/Tutorials/Meshes/tutorial_Meshes.md @@ -121,19 +121,63 @@ All additional information needed by the functor must be handled manually, e.g. For example, the iteration over cells on a mesh allocated on the host can be done as follows: -```cpp -TODO -``` +\snippet MeshIterationExample.cpp Parallel iteration host The parallel iteration is more complicated for meshes allocated on a GPU, since the lambda expression needs to capture a pointer to the copy of the mesh, which is allocated on the right device. This can be achieved with a [smart pointer](tutorial_Pointers.html) as follows: -```cpp -TODO -``` +\snippet ParallelIterationCuda.h Parallel iteration CUDA Alternatively, you can use a [SharedPointer](@ref TNL::Pointers::SharedPointer) instead of a [DevicePointer](@ref TNL::Pointers::DevicePointer) to allocate the mesh, but it does not allow to bind to an object which has already been created outside of the `SharedPointer`. ## Writing a mesh and data to a file +Numerical simulations typically produce results which can be interpreted as _mesh functions_ or _fields_. +In C++ they can be stored simply as arrays or vectors with the appropriate size. +For example, here we create two arrays `f_in` and `f_out`, which represent the input and output state of an iterative algorithm (`f_in` and `f_out` will be swapped after each iteration): + +\snippet GameOfLife.cpp Data vectors + +Note that here we used `std::uint8_t` as the value type. +The following value types are supported for the output into VTK file formats: `std::int8_t`, `std::uint8_t`, `std::int16_t`, `std::uint16_t`, `std::int32_t`, `std::uint32_t`, `std::int64_t`, `std::uint64_t`, `float`, `double`. + +The output into a specific file format can be done with an appropriate _writer_ class, see \ref TNL::Meshes::Writers. +For example, using [VTUWriter](@ref TNL::Meshes::Writers::VTUWriter) for the `.vtu` file format: + +\snippet GameOfLife.cpp make_snapshot + +Note that this writer supports writing metadata (iteration index and time level value), then we call `writeEntities` to write the mesh cells and `writeCellData` to write the mesh function values. +The `writeCellData` call can be repeated multiple times for different mesh functions that should be included in the snapshot. + +Then we can take the snapshot of the initial state, + +\snippet GameOfLife.cpp make initial snapshot + +and similarly use `make_snapshot` in the iteration loop. + ## Example: Game of Life + +In this example we will show how to implement the [Conway's Game of Life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life) using the [Mesh](@ref TNL::Meshes::Mesh) class template. +Although the game is usually implemented on structured grids rather than unstructured meshes, it will nicely illustrate how the building blocks for a numerical simulation are connected together. + +The kernel of the Game of Life can be implemented as follows: + +\snippet GameOfLife.cpp Game of Life kernel + +The `kernel` function takes `f_in_view` (the input state of the current iteration) and for the $i$-th cell sums the values of the neighbor cells, which are accessed using the dual graph -- see \ref TNL::Meshes::Mesh::getCellNeighborsCount and \ref TNL::Meshes::Mesh::getCellNeighborIndex. +Then it writes the resulting state of the $i$-th cell into `f_out_view` according to Conway's rules for a square grid: + +- any live cell with less than two live neighbors dies, +- any live cell with two or three live neighbors survives, +- any live cell with more than three live neighbors dies, +- any dead cell with exactly three live neighbors becomes a live cell, +- and any other dead cell remains dead. + +The kernel function is evaluated for all cells in the mesh, followed by swapping `f_in` and `f_out` (including their views), writing the output into a VTU file and checking if this was the last iteration: + +\snippet GameOfLife.cpp Game of Life iteration + +The remaining pieces needed for the implementation have either been already presented on this page, or they are left as an exercise to the reader. +For the sake of completeness, we include the full example below. + +\include GameOfLife.cpp -- GitLab From a58a590221d05b9cdb2c4628f6fcea2885b2b345 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Sat, 3 Apr 2021 17:20:33 +0200 Subject: [PATCH 16/19] Documentation: add *.cu to FILE_PATTERNS in Doxyfile and *.swp to .gitignore --- .gitignore | 1 + Documentation/Doxyfile | 1 + 2 files changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 4ec8d3ef7..6687c528d 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ *.orig *.bak *.backup +*.swp # GDB .gdb_history diff --git a/Documentation/Doxyfile b/Documentation/Doxyfile index 419bf3055..803278a7a 100644 --- a/Documentation/Doxyfile +++ b/Documentation/Doxyfile @@ -826,6 +826,7 @@ FILE_PATTERNS = *.c \ *.cxx \ *.cpp \ *.c++ \ + *.cu \ *.java \ *.ii \ *.ixx \ -- GitLab From 10a161ee3586c096e3b4fb72182fc70dff210c36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Sat, 3 Apr 2021 17:21:38 +0200 Subject: [PATCH 17/19] Fixed the MPI version of the Game of Life --- src/Tools/tnl-game-of-life.cpp | 41 ++++++++++++++++------------------ 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/src/Tools/tnl-game-of-life.cpp b/src/Tools/tnl-game-of-life.cpp index 9bfb08033..b2a1e8f84 100644 --- a/src/Tools/tnl-game-of-life.cpp +++ b/src/Tools/tnl-game-of-life.cpp @@ -26,21 +26,12 @@ namespace TNL { namespace Meshes { namespace BuildConfigTags { -/**** - * Turn off support for float and long double. - */ -template<> struct GridRealTag< MyConfigTag, float > { enum { enabled = false }; }; -template<> struct GridRealTag< MyConfigTag, long double > { enum { enabled = false }; }; - -/**** - * Turn off support for short int and long int indexing. - */ -template<> struct GridIndexTag< MyConfigTag, short int >{ enum { enabled = false }; }; -template<> struct GridIndexTag< MyConfigTag, long int >{ enum { enabled = false }; }; - -/**** - * Unstructured meshes. - */ +// disable all grids +template< int Dimension, typename Real, typename Device, typename Index > +struct GridTag< MyConfigTag, Grid< Dimension, Real, Device, Index > > +{ enum { enabled = false }; }; + +// Meshes are enabled only for topologies explicitly listed below. //template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Edge > { enum { enabled = true }; }; template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Triangle > { enum { enabled = true }; }; template<> struct MeshCellTopologyTag< MyConfigTag, Topologies::Quadrangle > { enum { enabled = true }; }; @@ -116,7 +107,7 @@ bool runGameOfLife( const Mesh& mesh ) // print basic mesh info mesh.printInfo( std::cout ); - // test synchronizer + // initialize the synchronizer using Synchronizer = Meshes::DistributedMeshes::DistributedMeshSynchronizer< Mesh >; Synchronizer sync; sync.initialize( mesh ); @@ -134,7 +125,7 @@ bool runGameOfLife( const Mesh& mesh ) std::mt19937 rng(dev()); std::uniform_int_distribution<> dist(0, 1); for( Index i = 0; i < cellsCount; i++ ) - f_in.[ i ] = dist(rng); + f_in[ i ] = dist(rng); sync.synchronize( f_in ); */ // find the rank which contains most points in the box between (0.45, 0.45) and (0.55, 0.55) @@ -150,6 +141,7 @@ bool runGameOfLife( const Mesh& mesh ) Index max_count; TNL::MPI::Allreduce( &count, &max_count, 1, MPI_MAX, mesh.getCommunicationGroup() ); std::cout << "Rank " << TNL::MPI::GetRank() << ": count=" << count << ", max_count=" << max_count << std::endl; + // FIXME: this is not reliable Index reference_cell = 0; if( count == max_count ) { // find cell which has all points in the central box @@ -168,7 +160,7 @@ bool runGameOfLife( const Mesh& mesh ) } // R-pentomino (stabilizes after 1103 iterations) const Index max_iter = 1103; - if( count == max_count ) { + if( count == max_count && localMesh.getCellNeighborsCount(reference_cell) > 6 ) { f_in[reference_cell] = 1; Index n1 = localMesh.getCellNeighborIndex(reference_cell,1); // bottom Index n2 = localMesh.getCellNeighborIndex(reference_cell,2); // left @@ -217,8 +209,8 @@ bool runGameOfLife( const Mesh& mesh ) const std::string subfilePath = pvtu.addPiece( mainFilePath, mesh.getCommunicationGroup() ); // create a .vtu file for local data - using Writer = Meshes::Writers::VTUWriter< LocalMesh >; std::ofstream subfile( subfilePath ); + using Writer = Meshes::Writers::VTUWriter< LocalMesh >; Writer writer( subfile ); writer.writeMetadata( iteration, iteration ); writer.template writeEntities< LocalMesh::getMeshDimension() >( localMesh ); @@ -231,7 +223,7 @@ bool runGameOfLife( const Mesh& mesh ) make_snapshot( 0 ); // captures for the iteration kernel - const auto f_in_view = f_in.getConstView(); + auto f_in_view = f_in.getConstView(); auto f_out_view = f_out.getView(); Pointers::DevicePointer< const LocalMesh > localMeshDevicePointer( localMesh ); const LocalMesh* localMeshPointer = &localMeshDevicePointer.template getData< typename LocalMesh::DeviceType >(); @@ -243,10 +235,10 @@ bool runGameOfLife( const Mesh& mesh ) if( TNL::MPI::GetRank() == 0 ) std::cout << "Computing iteration " << iteration << "..." << std::endl; - // iterate over all local entities + // iterate over all local cells auto kernel = [f_in_view, f_out_view, localMeshPointer] __cuda_callable__ ( Index i ) mutable { - // sum values of the function on the neighbor entities + // sum values of the function on the neighbor cells typename VectorType::RealType sum = 0; for( Index n = 0; n < localMeshPointer->getCellNeighborsCount( i ); n++ ) { const Index neighbor = localMeshPointer->getCellNeighborIndex( i, n ); @@ -279,8 +271,13 @@ bool runGameOfLife( const Mesh& mesh ) // synchronize sync.synchronize( f_out ); + // swap input and output arrays f_in.swap( f_out ); + // remember to update the views! + f_in_view.bind( f_in.getView() ); + f_out_view.bind( f_out.getView() ); + // write output make_snapshot( iteration ); -- GitLab From 1da7c1ae5892129a7e5c06787a12909d6e5dbab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Sat, 3 Apr 2021 21:58:54 +0200 Subject: [PATCH 18/19] MeshTest: removed tautological function which does not test anything --- src/UnitTests/Meshes/MeshTest.h | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/src/UnitTests/Meshes/MeshTest.h b/src/UnitTests/Meshes/MeshTest.h index 38ab992ce..e90161b96 100644 --- a/src/UnitTests/Meshes/MeshTest.h +++ b/src/UnitTests/Meshes/MeshTest.h @@ -119,36 +119,6 @@ void testMeshOnCuda( const Mesh& mesh ) #endif } -template< typename Mesh > -void testEntities( const Mesh& mesh ) -{ - using IndexType = typename Mesh::GlobalIndexType; - - // test that superentity accessors have been correctly bound - for( IndexType i = 0; i < mesh.template getEntitiesCount< 0 >(); i++ ) { - const auto v1 = mesh.template getEntity< 0 >( i ); - const auto v2 = mesh.template getEntity< 0 >( i ); - EXPECT_EQ( v1, v2 ); - EXPECT_EQ( v1.template getSuperentitiesCount< Mesh::getMeshDimension() >(), - v2.template getSuperentitiesCount< Mesh::getMeshDimension() >() ); - for( IndexType s = 0; s < v1.template getSuperentitiesCount< Mesh::getMeshDimension() >(); s++ ) - EXPECT_EQ( v1.template getSuperentityIndex< Mesh::getMeshDimension() >( s ), - v2.template getSuperentityIndex< Mesh::getMeshDimension() >( s ) ); - } - - // test that subentity accessors have been correctly bound - for( IndexType i = 0; i < mesh.template getEntitiesCount< Mesh::getMeshDimension() >(); i++ ) { - const auto c1 = mesh.template getEntity< Mesh::getMeshDimension() >( i ); - const auto c2 = mesh.template getEntity< Mesh::getMeshDimension() >( i ); - EXPECT_EQ( c1, c2 ); - EXPECT_EQ( c1.template getSubentitiesCount< 0 >(), - c2.template getSubentitiesCount< 0 >() ); - for( IndexType s = 0; s < c1.template getSubentitiesCount< 0 >(); s++ ) - EXPECT_EQ( c1.template getSubentityIndex< 0 >( s ), - c2.template getSubentityIndex< 0 >( s ) ); - } -} - template< typename Mesh > void testFinishedMesh( const Mesh& mesh ) { @@ -160,7 +130,6 @@ void testFinishedMesh( const Mesh& mesh ) compareStringRepresentation( mesh, mesh2 ); testCopyAssignment( mesh ); testMeshOnCuda( mesh ); - testEntities( mesh ); } TEST( MeshTest, TwoTrianglesTest ) -- GitLab From d57aa3b3dff07607446cd6386eb3d277d95f7235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= Date: Sat, 3 Apr 2021 22:01:27 +0200 Subject: [PATCH 19/19] Removed obsolete MeshEntityTest --- src/UnitTests/Meshes/CMakeLists.txt | 9 - src/UnitTests/Meshes/MeshEntityTest.cpp | 2 - src/UnitTests/Meshes/MeshEntityTest.h | 800 ------------------------ 3 files changed, 811 deletions(-) delete mode 100644 src/UnitTests/Meshes/MeshEntityTest.cpp delete mode 100644 src/UnitTests/Meshes/MeshEntityTest.h diff --git a/src/UnitTests/Meshes/CMakeLists.txt b/src/UnitTests/Meshes/CMakeLists.txt index d4b9d19f3..90763f547 100644 --- a/src/UnitTests/Meshes/CMakeLists.txt +++ b/src/UnitTests/Meshes/CMakeLists.txt @@ -35,12 +35,3 @@ ADD_TEST( EntityTagsTest ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/EntityTagsTest${CMAKE ADD_TEST( MeshTest ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/MeshTest${CMAKE_EXECUTABLE_SUFFIX} ) ADD_TEST( MeshTraverserTest ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/MeshTraverserTest${CMAKE_EXECUTABLE_SUFFIX} ) ADD_TEST( MeshOrderingTest ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/MeshOrderingTest${CMAKE_EXECUTABLE_SUFFIX} ) - - -## NOTE: MeshEntityTest does not make sense anymore, since mesh entities are -## created on the fly and all information is stored directly in the mesh -## TODO: check if something should be mereged into MeshTest and remove MeshEntityTest -#ADD_EXECUTABLE( MeshEntityTest MeshEntityTest.cpp ) -#TARGET_COMPILE_OPTIONS( MeshEntityTest PRIVATE ${CXX_TESTS_FLAGS} ) -#TARGET_LINK_LIBRARIES( MeshEntityTest ${GTEST_BOTH_LIBRARIES} ) -#ADD_TEST( MeshEntityTest ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/MeshEntityTest${CMAKE_EXECUTABLE_SUFFIX} ) diff --git a/src/UnitTests/Meshes/MeshEntityTest.cpp b/src/UnitTests/Meshes/MeshEntityTest.cpp deleted file mode 100644 index 66b6da128..000000000 --- a/src/UnitTests/Meshes/MeshEntityTest.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "MeshEntityTest.h" -#include "../main.h" diff --git a/src/UnitTests/Meshes/MeshEntityTest.h b/src/UnitTests/Meshes/MeshEntityTest.h deleted file mode 100644 index 235150d9d..000000000 --- a/src/UnitTests/Meshes/MeshEntityTest.h +++ /dev/null @@ -1,800 +0,0 @@ -#pragma once - -#ifdef HAVE_GTEST -#include - -#include -#include -#include -#include -#include -#include - -namespace MeshEntityTest { - -using namespace TNL; -using namespace TNL::Meshes; - -using RealType = double; -using Device = Devices::Host; -using IndexType = int; - -using TestEdgeMeshConfig = DefaultConfig< Topologies::Edge, 2, RealType, IndexType, IndexType, void >; - -class TestTriangleMeshConfig : public DefaultConfig< Topologies::Triangle > -{ -public: - template< typename EntityTopology > - static constexpr bool subentityStorage( EntityTopology entity, int subentityDimensions ) - { - return true; - } - - template< typename EntityTopology > - static constexpr bool superentityStorage( EntityTopology entity, int superentityDimensions ) - { - return true; - } -}; - -class TestTetrahedronMeshConfig : public DefaultConfig< Topologies::Tetrahedron > -{ -public: - template< typename EntityTopology > - static constexpr bool subentityStorage( EntityTopology entity, int subentityDimensions ) - { - return true; - } - - template< typename EntityTopology > - static constexpr bool superentityStorage( EntityTopology entity, int superentityDimensions ) - { - return true; - } -}; - -template< typename MeshConfig, typename EntityTopology, int Dimensions > -using SubentityStorage = typename MeshSubentityTraits< MeshConfig, Devices::Host, EntityTopology, Dimensions >::StorageNetworkType; - -template< typename MeshConfig, typename EntityTopology, int Dimensions > -using SuperentityStorage = typename MeshSuperentityTraits< MeshConfig, Devices::Host, EntityTopology, Dimensions >::StorageNetworkType; - -// stupid wrapper around MeshEntity to expose protected members needed for tests -template< typename MeshConfig, typename EntityTopology > -class TestMeshEntity - : public MeshEntity< MeshConfig, Devices::Host, EntityTopology > -{ - using BaseType = MeshEntity< MeshConfig, Devices::Host, EntityTopology >; - -public: - template< int Subdimensions, typename Storage > - void bindSubentitiesStorageNetwork( const Storage& storage ) - { - BaseType::template bindSubentitiesStorageNetwork< Subdimensions >( storage ); - } - - template< int Subdimensions > - void setSubentityIndex( const typename BaseType::LocalIndexType& localIndex, - const typename BaseType::GlobalIndexType& globalIndex ) - { - BaseType::template setSubentityIndex< Subdimensions >( localIndex, globalIndex ); - } - - using BaseType::bindSuperentitiesStorageNetwork; - using BaseType::setNumberOfSuperentities; - using BaseType::setSuperentityIndex; - using BaseType::setIndex; -}; - -template< typename Entity > -void generalTestSubentities( const Entity& entity ) -{ - Entity copy1( entity ); - Entity copy2 = entity; - - // check that subentity accessors have been rebound, at least for the 0th subvertex - EXPECT_EQ( copy1.template getSubentityIndex< 0 >( 0 ), entity.template getSubentityIndex< 0 >( 0 ) ); - EXPECT_EQ( copy2.template getSubentityIndex< 0 >( 0 ), entity.template getSubentityIndex< 0 >( 0 ) ); -} - -template< typename Entity > -void generalTestSuperentities( const Entity& entity ) -{ - Entity copy1( entity ); - Entity copy2 = entity; - - // check that subentity accessors have been rebound, at least for the 0th superentity - EXPECT_EQ( copy1.template getSuperentityIndex< Entity::getEntityDimension() + 1 >( 0 ), entity.template getSuperentityIndex< Entity::getEntityDimension() + 1 >( 0 ) ); - EXPECT_EQ( copy2.template getSuperentityIndex< Entity::getEntityDimension() + 1 >( 0 ), entity.template getSuperentityIndex< Entity::getEntityDimension() + 1 >( 0 ) ); -} - -TEST( MeshEntityTest, VertexMeshEntityTest ) -{ - using EdgeMeshEntityType = TestMeshEntity< TestEdgeMeshConfig, Topologies::Edge >; - using VertexMeshEntityType = TestMeshEntity< TestEdgeMeshConfig, typename EdgeMeshEntityType::SubentityTraits< 0 >::SubentityTopology >; - - using PointType = typename VertexMeshEntityType::PointType; - static_assert( std::is_same< PointType, Containers::StaticVector< 2, RealType > >::value, - "unexpected PointType" ); - - VertexMeshEntityType vertexEntity; - PointType point; - point.x() = 1.0; - point.y() = 2.0; - vertexEntity.setPoint( point ); - EXPECT_EQ( vertexEntity.getPoint(), point ); -} - -TEST( MeshEntityTest, EdgeMeshEntityTest ) -{ - using EdgeMeshEntityType = TestMeshEntity< TestEdgeMeshConfig, Topologies::Edge >; - using VertexMeshEntityType = TestMeshEntity< TestEdgeMeshConfig, typename EdgeMeshEntityType::SubentityTraits< 0 >::SubentityTopology >; - static_assert( EdgeMeshEntityType::SubentityTraits< 0 >::storageEnabled, "Testing edge entity does not store vertices as required." ); - - using PointType = typename VertexMeshEntityType::PointType; - static_assert( std::is_same< PointType, Containers::StaticVector< 2, RealType > >::value, - "unexpected PointType" ); - - /**** - * - * Here we test the following simple example: - * - - point2 - |\ - | \ - | \ - | \ - - .... - edge1 edge0 - .... - - - | \ - | \ - --------------------- - point0 edge2 point1 - - */ - - PointType point0( 0.0, 0.0 ), - point1( 1.0, 0.0 ), - point2( 0.0, 1.0 ); - - Containers::StaticArray< 3, VertexMeshEntityType > vertexEntities; - vertexEntities[ 0 ].setPoint( point0 ); - vertexEntities[ 1 ].setPoint( point1 ); - vertexEntities[ 2 ].setPoint( point2 ); - - EXPECT_EQ( vertexEntities[ 0 ].getPoint(), point0 ); - EXPECT_EQ( vertexEntities[ 1 ].getPoint(), point1 ); - EXPECT_EQ( vertexEntities[ 2 ].getPoint(), point2 ); - - Containers::StaticArray< 3, EdgeMeshEntityType > edgeEntities; - SubentityStorage< TestTriangleMeshConfig, Topologies::Edge, 0 > edgeVertexSubentities; - edgeVertexSubentities.setKeysRange( 3 ); - edgeVertexSubentities.allocate(); - - edgeEntities[ 0 ].template bindSubentitiesStorageNetwork< 0 >( edgeVertexSubentities.getValues( 0 ) ); - edgeEntities[ 0 ].template setSubentityIndex< 0 >( 0, 0 ); - edgeEntities[ 0 ].template setSubentityIndex< 0 >( 1, 1 ); - edgeEntities[ 1 ].template bindSubentitiesStorageNetwork< 0 >( edgeVertexSubentities.getValues( 1 ) ); - edgeEntities[ 1 ].template setSubentityIndex< 0 >( 0, 1 ); - edgeEntities[ 1 ].template setSubentityIndex< 0 >( 1, 2 ); - edgeEntities[ 2 ].template bindSubentitiesStorageNetwork< 0 >( edgeVertexSubentities.getValues( 2 ) ); - edgeEntities[ 2 ].template setSubentityIndex< 0 >( 0, 2 ); - edgeEntities[ 2 ].template setSubentityIndex< 0 >( 1, 0 ); - edgeEntities[ 0 ].setIndex( 0 ); - edgeEntities[ 1 ].setIndex( 1 ); - edgeEntities[ 2 ].setIndex( 2 ); - - EXPECT_EQ( vertexEntities[ edgeEntities[ 0 ].getVertexIndex( 0 ) ].getPoint(), point0 ); - EXPECT_EQ( vertexEntities[ edgeEntities[ 0 ].getVertexIndex( 1 ) ].getPoint(), point1 ); - EXPECT_EQ( vertexEntities[ edgeEntities[ 1 ].getVertexIndex( 0 ) ].getPoint(), point1 ); - EXPECT_EQ( vertexEntities[ edgeEntities[ 1 ].getVertexIndex( 1 ) ].getPoint(), point2 ); - EXPECT_EQ( vertexEntities[ edgeEntities[ 2 ].getVertexIndex( 0 ) ].getPoint(), point2 ); - EXPECT_EQ( vertexEntities[ edgeEntities[ 2 ].getVertexIndex( 1 ) ].getPoint(), point0 ); - - - generalTestSubentities( edgeEntities[ 0 ] ); - generalTestSubentities( edgeEntities[ 1 ] ); - generalTestSubentities( edgeEntities[ 2 ] ); -} - -TEST( MeshEntityTest, TriangleMeshEntityTest ) -{ - using TriangleMeshEntityType = TestMeshEntity< TestTriangleMeshConfig, Topologies::Triangle >; - using EdgeMeshEntityType = TestMeshEntity< TestTriangleMeshConfig, typename TriangleMeshEntityType::SubentityTraits< 1 >::SubentityTopology >; - using VertexMeshEntityType = TestMeshEntity< TestTriangleMeshConfig, typename TriangleMeshEntityType::SubentityTraits< 0 >::SubentityTopology >; - - static_assert( TriangleMeshEntityType::SubentityTraits< 1 >::storageEnabled, "Testing triangle entity does not store edges as required." ); - static_assert( TriangleMeshEntityType::SubentityTraits< 0 >::storageEnabled, "Testing triangle entity does not store vertices as required." ); - static_assert( EdgeMeshEntityType::SubentityTraits< 0 >::storageEnabled, "Testing edge entity does not store vertices as required." ); - - using PointType = typename VertexMeshEntityType::PointType; - static_assert( std::is_same< PointType, Containers::StaticVector< 2, RealType > >::value, - "unexpected PointType" ); - - /**** - * We set-up the same situation as in the test above - */ - PointType point0( 0.0, 0.0 ), - point1( 1.0, 0.0 ), - point2( 0.0, 1.0 ); - - Containers::StaticArray< 3, VertexMeshEntityType > vertexEntities; - vertexEntities[ 0 ].setPoint( point0 ); - vertexEntities[ 1 ].setPoint( point1 ); - vertexEntities[ 2 ].setPoint( point2 ); - - EXPECT_EQ( vertexEntities[ 0 ].getPoint(), point0 ); - EXPECT_EQ( vertexEntities[ 1 ].getPoint(), point1 ); - EXPECT_EQ( vertexEntities[ 2 ].getPoint(), point2 ); - - Containers::StaticArray< 3, EdgeMeshEntityType > edgeEntities; - SubentityStorage< TestTriangleMeshConfig, Topologies::Edge, 0 > edgeVertexSubentities; - edgeVertexSubentities.setKeysRange( 3 ); - edgeVertexSubentities.allocate(); - - edgeEntities[ 0 ].template bindSubentitiesStorageNetwork< 0 >( edgeVertexSubentities.getValues( 0 ) ); - edgeEntities[ 0 ].template setSubentityIndex< 0 >( 0, Topologies::SubentityVertexMap< Topologies::Triangle, Topologies::Edge, 0, 0 >::index ); - edgeEntities[ 0 ].template setSubentityIndex< 0 >( 1, Topologies::SubentityVertexMap< Topologies::Triangle, Topologies::Edge, 0, 1 >::index ); - edgeEntities[ 1 ].template bindSubentitiesStorageNetwork< 0 >( edgeVertexSubentities.getValues( 1 ) ); - edgeEntities[ 1 ].template setSubentityIndex< 0 >( 0, Topologies::SubentityVertexMap< Topologies::Triangle, Topologies::Edge, 1, 0 >::index ); - edgeEntities[ 1 ].template setSubentityIndex< 0 >( 1, Topologies::SubentityVertexMap< Topologies::Triangle, Topologies::Edge, 1, 1 >::index ); - edgeEntities[ 2 ].template bindSubentitiesStorageNetwork< 0 >( edgeVertexSubentities.getValues( 2 ) ); - edgeEntities[ 2 ].template setSubentityIndex< 0 >( 0, Topologies::SubentityVertexMap< Topologies::Triangle, Topologies::Edge, 2, 0 >::index ); - edgeEntities[ 2 ].template setSubentityIndex< 0 >( 1, Topologies::SubentityVertexMap< Topologies::Triangle, Topologies::Edge, 2, 1 >::index ); - - EXPECT_EQ( edgeEntities[ 0 ].getVertexIndex( 0 ), ( Topologies::SubentityVertexMap< Topologies::Triangle, Topologies::Edge, 0, 0 >::index ) ); - EXPECT_EQ( edgeEntities[ 0 ].getVertexIndex( 1 ), ( Topologies::SubentityVertexMap< Topologies::Triangle, Topologies::Edge, 0, 1 >::index ) ); - EXPECT_EQ( edgeEntities[ 1 ].getVertexIndex( 0 ), ( Topologies::SubentityVertexMap< Topologies::Triangle, Topologies::Edge, 1, 0 >::index ) ); - EXPECT_EQ( edgeEntities[ 1 ].getVertexIndex( 1 ), ( Topologies::SubentityVertexMap< Topologies::Triangle, Topologies::Edge, 1, 1 >::index ) ); - EXPECT_EQ( edgeEntities[ 2 ].getVertexIndex( 0 ), ( Topologies::SubentityVertexMap< Topologies::Triangle, Topologies::Edge, 2, 0 >::index ) ); - EXPECT_EQ( edgeEntities[ 2 ].getVertexIndex( 1 ), ( Topologies::SubentityVertexMap< Topologies::Triangle, Topologies::Edge, 2, 1 >::index ) ); - - TriangleMeshEntityType triangleEntity; - SubentityStorage< TestTriangleMeshConfig, Topologies::Triangle, 0 > triangleVertexSubentities; - SubentityStorage< TestTriangleMeshConfig, Topologies::Triangle, 1 > triangleEdgeSubentities; - triangleVertexSubentities.setKeysRange( 1 ); - triangleEdgeSubentities.setKeysRange( 1 ); - triangleVertexSubentities.allocate(); - triangleEdgeSubentities.allocate(); - - triangleEntity.template bindSubentitiesStorageNetwork< 0 >( triangleVertexSubentities.getValues( 0 ) ); - triangleEntity.template setSubentityIndex< 0 >( 0 , 0 ); - triangleEntity.template setSubentityIndex< 0 >( 1 , 1 ); - triangleEntity.template setSubentityIndex< 0 >( 2 , 2 ); - - EXPECT_EQ( triangleEntity.template getSubentityIndex< 0 >( 0 ), 0 ); - EXPECT_EQ( triangleEntity.template getSubentityIndex< 0 >( 1 ), 1 ); - EXPECT_EQ( triangleEntity.template getSubentityIndex< 0 >( 2 ), 2 ); - - triangleEntity.template bindSubentitiesStorageNetwork< 1 >( triangleEdgeSubentities.getValues( 0 ) ); - triangleEntity.template setSubentityIndex< 1 >( 0 , 0 ); - triangleEntity.template setSubentityIndex< 1 >( 1 , 1 ); - triangleEntity.template setSubentityIndex< 1 >( 2 , 2 ); - - EXPECT_EQ( triangleEntity.template getSubentityIndex< 1 >( 0 ), 0 ); - EXPECT_EQ( triangleEntity.template getSubentityIndex< 1 >( 1 ), 1 ); - EXPECT_EQ( triangleEntity.template getSubentityIndex< 1 >( 2 ), 2 ); -} - -TEST( MeshEntityTest, TetrahedronMeshEntityTest ) -{ - using TetrahedronMeshEntityType = TestMeshEntity< TestTetrahedronMeshConfig, Topologies::Tetrahedron >; - using TriangleMeshEntityType = TestMeshEntity< TestTetrahedronMeshConfig, typename TetrahedronMeshEntityType::SubentityTraits< 2 >::SubentityTopology >; - using EdgeMeshEntityType = TestMeshEntity< TestTetrahedronMeshConfig, typename TetrahedronMeshEntityType::SubentityTraits< 1 >::SubentityTopology >; - using VertexMeshEntityType = TestMeshEntity< TestTetrahedronMeshConfig, typename TetrahedronMeshEntityType::SubentityTraits< 0 >::SubentityTopology >; - - static_assert( TetrahedronMeshEntityType::SubentityTraits< 2 >::storageEnabled, "Testing tetrahedron entity does not store triangles as required." ); - static_assert( TetrahedronMeshEntityType::SubentityTraits< 1 >::storageEnabled, "Testing tetrahedron entity does not store edges as required." ); - static_assert( TetrahedronMeshEntityType::SubentityTraits< 0 >::storageEnabled, "Testing tetrahedron entity does not store vertices as required." ); - static_assert( TriangleMeshEntityType::SubentityTraits< 1 >::storageEnabled, "Testing triangle entity does not store edges as required." ); - static_assert( TriangleMeshEntityType::SubentityTraits< 0 >::storageEnabled, "Testing triangle entity does not store vertices as required." ); - static_assert( EdgeMeshEntityType::SubentityTraits< 0 >::storageEnabled, "Testing edge entity does not store vertices as required." ); - - using PointType = typename VertexMeshEntityType::PointType; - static_assert( std::is_same< PointType, Containers::StaticVector< 3, RealType > >::value, - "unexpected PointType" ); - - /**** - * We set-up similar situation as above but with - * tetrahedron. - */ - PointType point0( 0.0, 0.0, 0.0), - point1( 1.0, 0.0, 0.0 ), - point2( 0.0, 1.0, 0.0 ), - point3( 0.0, 0.0, 1.0 ); - - Containers::StaticArray< Topologies::Subtopology< Topologies::Tetrahedron, 0 >::count, - VertexMeshEntityType > vertexEntities; - - vertexEntities[ 0 ].setPoint( point0 ); - vertexEntities[ 1 ].setPoint( point1 ); - vertexEntities[ 2 ].setPoint( point2 ); - vertexEntities[ 3 ].setPoint( point3 ); - - EXPECT_EQ( vertexEntities[ 0 ].getPoint(), point0 ); - EXPECT_EQ( vertexEntities[ 1 ].getPoint(), point1 ); - EXPECT_EQ( vertexEntities[ 2 ].getPoint(), point2 ); - EXPECT_EQ( vertexEntities[ 3 ].getPoint(), point3 ); - - Containers::StaticArray< Topologies::Subtopology< Topologies::Tetrahedron, 1 >::count, - EdgeMeshEntityType > edgeEntities; - SubentityStorage< TestTriangleMeshConfig, Topologies::Edge, 0 > edgeVertexSubentities; - edgeVertexSubentities.setKeysRange( 6 ); - edgeVertexSubentities.allocate(); - - edgeEntities[ 0 ].template bindSubentitiesStorageNetwork< 0 >( edgeVertexSubentities.getValues( 0 ) ); - edgeEntities[ 0 ].template setSubentityIndex< 0 >( 0, Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Edge, 0, 0 >::index ); - edgeEntities[ 0 ].template setSubentityIndex< 0 >( 1, Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Edge, 0, 1 >::index ); - edgeEntities[ 1 ].template bindSubentitiesStorageNetwork< 0 >( edgeVertexSubentities.getValues( 1 ) ); - edgeEntities[ 1 ].template setSubentityIndex< 0 >( 0, Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Edge, 1, 0 >::index ); - edgeEntities[ 1 ].template setSubentityIndex< 0 >( 1, Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Edge, 1, 1 >::index ); - edgeEntities[ 2 ].template bindSubentitiesStorageNetwork< 0 >( edgeVertexSubentities.getValues( 2 ) ); - edgeEntities[ 2 ].template setSubentityIndex< 0 >( 0, Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Edge, 2, 0 >::index ); - edgeEntities[ 2 ].template setSubentityIndex< 0 >( 1, Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Edge, 2, 1 >::index ); - edgeEntities[ 3 ].template bindSubentitiesStorageNetwork< 0 >( edgeVertexSubentities.getValues( 3 ) ); - edgeEntities[ 3 ].template setSubentityIndex< 0 >( 0, Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Edge, 3, 0 >::index ); - edgeEntities[ 3 ].template setSubentityIndex< 0 >( 1, Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Edge, 3, 1 >::index ); - edgeEntities[ 4 ].template bindSubentitiesStorageNetwork< 0 >( edgeVertexSubentities.getValues( 4 ) ); - edgeEntities[ 4 ].template setSubentityIndex< 0 >( 0, Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Edge, 4, 0 >::index ); - edgeEntities[ 4 ].template setSubentityIndex< 0 >( 1, Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Edge, 4, 1 >::index ); - edgeEntities[ 5 ].template bindSubentitiesStorageNetwork< 0 >( edgeVertexSubentities.getValues( 5 ) ); - edgeEntities[ 5 ].template setSubentityIndex< 0 >( 0, Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Edge, 5, 0 >::index ); - edgeEntities[ 5 ].template setSubentityIndex< 0 >( 1, Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Edge, 5, 1 >::index ); - - EXPECT_EQ( edgeEntities[ 0 ].getVertexIndex( 0 ), ( Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Edge, 0, 0 >::index ) ); - EXPECT_EQ( edgeEntities[ 0 ].getVertexIndex( 1 ), ( Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Edge, 0, 1 >::index ) ); - EXPECT_EQ( edgeEntities[ 1 ].getVertexIndex( 0 ), ( Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Edge, 1, 0 >::index ) ); - EXPECT_EQ( edgeEntities[ 1 ].getVertexIndex( 1 ), ( Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Edge, 1, 1 >::index ) ); - EXPECT_EQ( edgeEntities[ 2 ].getVertexIndex( 0 ), ( Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Edge, 2, 0 >::index ) ); - EXPECT_EQ( edgeEntities[ 2 ].getVertexIndex( 1 ), ( Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Edge, 2, 1 >::index ) ); - EXPECT_EQ( edgeEntities[ 3 ].getVertexIndex( 0 ), ( Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Edge, 3, 0 >::index ) ); - EXPECT_EQ( edgeEntities[ 3 ].getVertexIndex( 1 ), ( Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Edge, 3, 1 >::index ) ); - EXPECT_EQ( edgeEntities[ 4 ].getVertexIndex( 0 ), ( Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Edge, 4, 0 >::index ) ); - EXPECT_EQ( edgeEntities[ 4 ].getVertexIndex( 1 ), ( Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Edge, 4, 1 >::index ) ); - EXPECT_EQ( edgeEntities[ 5 ].getVertexIndex( 0 ), ( Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Edge, 5, 0 >::index ) ); - EXPECT_EQ( edgeEntities[ 5 ].getVertexIndex( 1 ), ( Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Edge, 5, 1 >::index ) ); - - Containers::StaticArray< Topologies::Subtopology< Topologies::Tetrahedron, 2 >::count, - TriangleMeshEntityType > triangleEntities; - SubentityStorage< TestTriangleMeshConfig, Topologies::Triangle, 0 > triangleVertexSubentities; - triangleVertexSubentities.setKeysRange( 4 ); - triangleVertexSubentities.allocate(); - - triangleEntities[ 0 ].template bindSubentitiesStorageNetwork< 0 >( triangleVertexSubentities.getValues( 0 ) ); - triangleEntities[ 0 ].template setSubentityIndex< 0 >( 0, Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Triangle, 0, 0 >::index ); - triangleEntities[ 0 ].template setSubentityIndex< 0 >( 1, Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Triangle, 0, 1 >::index ); - triangleEntities[ 0 ].template setSubentityIndex< 0 >( 2, Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Triangle, 0, 2 >::index ); - triangleEntities[ 1 ].template bindSubentitiesStorageNetwork< 0 >( triangleVertexSubentities.getValues( 1 ) ); - triangleEntities[ 1 ].template setSubentityIndex< 0 >( 0, Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Triangle, 1, 0 >::index ); - triangleEntities[ 1 ].template setSubentityIndex< 0 >( 1, Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Triangle, 1, 1 >::index ); - triangleEntities[ 1 ].template setSubentityIndex< 0 >( 2, Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Triangle, 1, 2 >::index ); - triangleEntities[ 2 ].template bindSubentitiesStorageNetwork< 0 >( triangleVertexSubentities.getValues( 2 ) ); - triangleEntities[ 2 ].template setSubentityIndex< 0 >( 0, Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Triangle, 2, 0 >::index ); - triangleEntities[ 2 ].template setSubentityIndex< 0 >( 1, Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Triangle, 2, 1 >::index ); - triangleEntities[ 2 ].template setSubentityIndex< 0 >( 2, Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Triangle, 2, 2 >::index ); - triangleEntities[ 3 ].template bindSubentitiesStorageNetwork< 0 >( triangleVertexSubentities.getValues( 3 ) ); - triangleEntities[ 3 ].template setSubentityIndex< 0 >( 0, Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Triangle, 3, 0 >::index ); - triangleEntities[ 3 ].template setSubentityIndex< 0 >( 1, Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Triangle, 3, 1 >::index ); - triangleEntities[ 3 ].template setSubentityIndex< 0 >( 2, Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Triangle, 3, 2 >::index ); - - EXPECT_EQ( triangleEntities[ 0 ].getVertexIndex( 0 ), ( Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Triangle, 0, 0 >::index ) ); - EXPECT_EQ( triangleEntities[ 0 ].getVertexIndex( 1 ), ( Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Triangle, 0, 1 >::index ) ); - EXPECT_EQ( triangleEntities[ 0 ].getVertexIndex( 2 ), ( Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Triangle, 0, 2 >::index ) ); - EXPECT_EQ( triangleEntities[ 1 ].getVertexIndex( 0 ), ( Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Triangle, 1, 0 >::index ) ); - EXPECT_EQ( triangleEntities[ 1 ].getVertexIndex( 1 ), ( Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Triangle, 1, 1 >::index ) ); - EXPECT_EQ( triangleEntities[ 1 ].getVertexIndex( 2 ), ( Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Triangle, 1, 2 >::index ) ); - EXPECT_EQ( triangleEntities[ 2 ].getVertexIndex( 0 ), ( Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Triangle, 2, 0 >::index ) ); - EXPECT_EQ( triangleEntities[ 2 ].getVertexIndex( 1 ), ( Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Triangle, 2, 1 >::index ) ); - EXPECT_EQ( triangleEntities[ 2 ].getVertexIndex( 2 ), ( Topologies::SubentityVertexMap< Topologies::Tetrahedron, Topologies::Triangle, 2, 2 >::index ) ); - - TetrahedronMeshEntityType tetrahedronEntity; - SubentityStorage< TestTriangleMeshConfig, Topologies::Tetrahedron, 0 > tetrahedronVertexSubentities; - SubentityStorage< TestTriangleMeshConfig, Topologies::Tetrahedron, 1 > tetrahedronEdgeSubentities; - SubentityStorage< TestTriangleMeshConfig, Topologies::Tetrahedron, 2 > tetrahedronTriangleSubentities; - tetrahedronVertexSubentities.setKeysRange( 1 ); - tetrahedronEdgeSubentities.setKeysRange( 1 ); - tetrahedronTriangleSubentities.setKeysRange( 1 ); - tetrahedronVertexSubentities.allocate(); - tetrahedronEdgeSubentities.allocate(); - tetrahedronTriangleSubentities.allocate(); - - tetrahedronEntity.template bindSubentitiesStorageNetwork< 0 >( tetrahedronVertexSubentities.getValues( 0 ) ); - tetrahedronEntity.template setSubentityIndex< 0 >( 0, 0 ); - tetrahedronEntity.template setSubentityIndex< 0 >( 1, 1 ); - tetrahedronEntity.template setSubentityIndex< 0 >( 2, 2 ); - tetrahedronEntity.template setSubentityIndex< 0 >( 3, 3 ); - - EXPECT_EQ( tetrahedronEntity.getVertexIndex( 0 ), 0 ); - EXPECT_EQ( tetrahedronEntity.getVertexIndex( 1 ), 1 ); - EXPECT_EQ( tetrahedronEntity.getVertexIndex( 2 ), 2 ); - EXPECT_EQ( tetrahedronEntity.getVertexIndex( 3 ), 3 ); - - tetrahedronEntity.template bindSubentitiesStorageNetwork< 2 >( tetrahedronTriangleSubentities.getValues( 0 ) ); - tetrahedronEntity.template setSubentityIndex< 2 >( 0, 0 ); - tetrahedronEntity.template setSubentityIndex< 2 >( 1, 1 ); - tetrahedronEntity.template setSubentityIndex< 2 >( 2, 2 ); - tetrahedronEntity.template setSubentityIndex< 2 >( 3, 3 ); - - EXPECT_EQ( tetrahedronEntity.template getSubentityIndex< 2 >( 0 ), 0 ); - EXPECT_EQ( tetrahedronEntity.template getSubentityIndex< 2 >( 1 ), 1 ); - EXPECT_EQ( tetrahedronEntity.template getSubentityIndex< 2 >( 2 ), 2 ); - EXPECT_EQ( tetrahedronEntity.template getSubentityIndex< 2 >( 3 ), 3 ); - - tetrahedronEntity.template bindSubentitiesStorageNetwork< 1 >( tetrahedronEdgeSubentities.getValues( 0 ) ); - tetrahedronEntity.template setSubentityIndex< 1 >( 0, 0 ); - tetrahedronEntity.template setSubentityIndex< 1 >( 1, 1 ); - tetrahedronEntity.template setSubentityIndex< 1 >( 2, 2 ); - tetrahedronEntity.template setSubentityIndex< 1 >( 3, 3 ); - tetrahedronEntity.template setSubentityIndex< 1 >( 4, 4 ); - tetrahedronEntity.template setSubentityIndex< 1 >( 5, 5 ); - - EXPECT_EQ( tetrahedronEntity.template getSubentityIndex< 1 >( 0 ), 0 ); - EXPECT_EQ( tetrahedronEntity.template getSubentityIndex< 1 >( 1 ), 1 ); - EXPECT_EQ( tetrahedronEntity.template getSubentityIndex< 1 >( 2 ), 2 ); - EXPECT_EQ( tetrahedronEntity.template getSubentityIndex< 1 >( 3 ), 3 ); - EXPECT_EQ( tetrahedronEntity.template getSubentityIndex< 1 >( 4 ), 4 ); - EXPECT_EQ( tetrahedronEntity.template getSubentityIndex< 1 >( 5 ), 5 ); - - - generalTestSubentities( edgeEntities[ 0 ] ); - generalTestSubentities( edgeEntities[ 1 ] ); - generalTestSubentities( edgeEntities[ 2 ] ); - generalTestSubentities( tetrahedronEntity ); -} - -TEST( MeshEntityTest, TwoTrianglesMeshEntityTest ) -{ - using TriangleMeshEntityType = TestMeshEntity< TestTriangleMeshConfig, Topologies::Triangle >; - using EdgeMeshEntityType = TestMeshEntity< TestTriangleMeshConfig, typename TriangleMeshEntityType::SubentityTraits< 1 >::SubentityTopology >; - using VertexMeshEntityType = TestMeshEntity< TestTriangleMeshConfig, typename TriangleMeshEntityType::SubentityTraits< 0 >::SubentityTopology >; - - static_assert( TriangleMeshEntityType::SubentityTraits< 1 >::storageEnabled, "Testing triangle entity does not store edges as required." ); - static_assert( TriangleMeshEntityType::SubentityTraits< 0 >::storageEnabled, "Testing triangle entity does not store vertices as required." ); - static_assert( EdgeMeshEntityType::SubentityTraits< 0 >::storageEnabled, "Testing edge entity does not store vertices as required." ); - static_assert( EdgeMeshEntityType::SuperentityTraits< 2 >::storageEnabled, "Testing edge entity does not store triangles as required." ); - static_assert( VertexMeshEntityType::SuperentityTraits< 2 >::storageEnabled, "Testing vertex entity does not store triangles as required." ); - static_assert( VertexMeshEntityType::SuperentityTraits< 1 >::storageEnabled, "Testing vertex entity does not store edges as required." ); - - using PointType = typename VertexMeshEntityType::PointType; - static_assert( std::is_same< PointType, Containers::StaticVector< 2, RealType > >::value, - "unexpected PointType" ); - - /**** - * We set-up the following situation - point2 edge3 point3 - |\-------------------| - | \ | - | \ triangle1 | - | \ | - - .... - edge1 edge0 edge4 - .... - - - | triangle0 \ | - | \ | - ---------------------| - point0 edge2 point1 - */ - - PointType point0( 0.0, 0.0 ), - point1( 1.0, 0.0 ), - point2( 0.0, 1.0 ), - point3( 1.0, 1.0 ); - - Containers::StaticArray< 4, VertexMeshEntityType > vertexEntities; - vertexEntities[ 0 ].setPoint( point0 ); - vertexEntities[ 1 ].setPoint( point1 ); - vertexEntities[ 2 ].setPoint( point2 ); - vertexEntities[ 3 ].setPoint( point3 ); - - EXPECT_EQ( vertexEntities[ 0 ].getPoint(), point0 ); - EXPECT_EQ( vertexEntities[ 1 ].getPoint(), point1 ); - EXPECT_EQ( vertexEntities[ 2 ].getPoint(), point2 ); - EXPECT_EQ( vertexEntities[ 3 ].getPoint(), point3 ); - - Containers::StaticArray< 5, EdgeMeshEntityType > edgeEntities; - SubentityStorage< TestTriangleMeshConfig, Topologies::Edge, 0 > edgeVertexSubentities; - edgeVertexSubentities.setKeysRange( 5 ); - edgeVertexSubentities.allocate(); - - edgeEntities[ 0 ].template bindSubentitiesStorageNetwork< 0 >( edgeVertexSubentities.getValues( 0 ) ); - edgeEntities[ 0 ].template setSubentityIndex< 0 >( 0, 1 ); - edgeEntities[ 0 ].template setSubentityIndex< 0 >( 1, 2 ); - edgeEntities[ 1 ].template bindSubentitiesStorageNetwork< 0 >( edgeVertexSubentities.getValues( 1 ) ); - edgeEntities[ 1 ].template setSubentityIndex< 0 >( 0, 2 ); - edgeEntities[ 1 ].template setSubentityIndex< 0 >( 1, 0 ); - edgeEntities[ 2 ].template bindSubentitiesStorageNetwork< 0 >( edgeVertexSubentities.getValues( 2 ) ); - edgeEntities[ 2 ].template setSubentityIndex< 0 >( 0, 0 ); - edgeEntities[ 2 ].template setSubentityIndex< 0 >( 1, 1 ); - edgeEntities[ 3 ].template bindSubentitiesStorageNetwork< 0 >( edgeVertexSubentities.getValues( 3 ) ); - edgeEntities[ 3 ].template setSubentityIndex< 0 >( 0, 2 ); - edgeEntities[ 3 ].template setSubentityIndex< 0 >( 1, 3 ); - edgeEntities[ 4 ].template bindSubentitiesStorageNetwork< 0 >( edgeVertexSubentities.getValues( 4 ) ); - edgeEntities[ 4 ].template setSubentityIndex< 0 >( 0, 3 ); - edgeEntities[ 4 ].template setSubentityIndex< 0 >( 1, 1 ); - - EXPECT_EQ( edgeEntities[ 0 ].getVertexIndex( 0 ), 1 ); - EXPECT_EQ( edgeEntities[ 0 ].getVertexIndex( 1 ), 2 ); - EXPECT_EQ( edgeEntities[ 1 ].getVertexIndex( 0 ), 2 ); - EXPECT_EQ( edgeEntities[ 1 ].getVertexIndex( 1 ), 0 ); - EXPECT_EQ( edgeEntities[ 2 ].getVertexIndex( 0 ), 0 ); - EXPECT_EQ( edgeEntities[ 2 ].getVertexIndex( 1 ), 1 ); - EXPECT_EQ( edgeEntities[ 3 ].getVertexIndex( 0 ), 2 ); - EXPECT_EQ( edgeEntities[ 3 ].getVertexIndex( 1 ), 3 ); - EXPECT_EQ( edgeEntities[ 4 ].getVertexIndex( 0 ), 3 ); - EXPECT_EQ( edgeEntities[ 4 ].getVertexIndex( 1 ), 1 ); - - Containers::StaticArray< 2, TriangleMeshEntityType > triangleEntities; - SubentityStorage< TestTriangleMeshConfig, Topologies::Triangle, 0 > triangleVertexSubentities; - SubentityStorage< TestTriangleMeshConfig, Topologies::Triangle, 1 > triangleEdgeSubentities; - triangleVertexSubentities.setKeysRange( 2 ); - triangleVertexSubentities.allocate(); - triangleEdgeSubentities.setKeysRange( 2 ); - triangleEdgeSubentities.allocate(); - - triangleEntities[ 0 ].template bindSubentitiesStorageNetwork< 0 >( triangleVertexSubentities.getValues( 0 ) ); - triangleEntities[ 0 ].template setSubentityIndex< 0 >( 0 , 0 ); - triangleEntities[ 0 ].template setSubentityIndex< 0 >( 1 , 1 ); - triangleEntities[ 0 ].template setSubentityIndex< 0 >( 2 , 2 ); - triangleEntities[ 0 ].template bindSubentitiesStorageNetwork< 1 >( triangleEdgeSubentities.getValues( 0 ) ); - triangleEntities[ 0 ].template setSubentityIndex< 1 >( 0 , 0 ); - triangleEntities[ 0 ].template setSubentityIndex< 1 >( 1 , 1 ); - triangleEntities[ 0 ].template setSubentityIndex< 1 >( 2 , 2 ); - triangleEntities[ 1 ].template bindSubentitiesStorageNetwork< 0 >( triangleVertexSubentities.getValues( 1 ) ); - triangleEntities[ 1 ].template setSubentityIndex< 0 >( 0 , 1 ); - triangleEntities[ 1 ].template setSubentityIndex< 0 >( 1 , 2 ); - triangleEntities[ 1 ].template setSubentityIndex< 0 >( 2 , 3 ); - triangleEntities[ 1 ].template bindSubentitiesStorageNetwork< 1 >( triangleEdgeSubentities.getValues( 1 ) ); - triangleEntities[ 1 ].template setSubentityIndex< 1 >( 0 , 3 ); - triangleEntities[ 1 ].template setSubentityIndex< 1 >( 1 , 4 ); - triangleEntities[ 1 ].template setSubentityIndex< 1 >( 2 , 0 ); - - EXPECT_EQ( triangleEntities[ 0 ].template getSubentityIndex< 0 >( 0 ), 0 ); - EXPECT_EQ( triangleEntities[ 0 ].template getSubentityIndex< 0 >( 1 ), 1 ); - EXPECT_EQ( triangleEntities[ 0 ].template getSubentityIndex< 0 >( 2 ), 2 ); - EXPECT_EQ( triangleEntities[ 0 ].template getSubentityIndex< 1 >( 0 ), 0 ); - EXPECT_EQ( triangleEntities[ 0 ].template getSubentityIndex< 1 >( 1 ), 1 ); - EXPECT_EQ( triangleEntities[ 0 ].template getSubentityIndex< 1 >( 2 ), 2 ); - EXPECT_EQ( triangleEntities[ 1 ].template getSubentityIndex< 0 >( 0 ), 1 ); - EXPECT_EQ( triangleEntities[ 1 ].template getSubentityIndex< 0 >( 1 ), 2 ); - EXPECT_EQ( triangleEntities[ 1 ].template getSubentityIndex< 0 >( 2 ), 3 ); - EXPECT_EQ( triangleEntities[ 1 ].template getSubentityIndex< 1 >( 0 ), 3 ); - EXPECT_EQ( triangleEntities[ 1 ].template getSubentityIndex< 1 >( 1 ), 4 ); - EXPECT_EQ( triangleEntities[ 1 ].template getSubentityIndex< 1 >( 2 ), 0 ); - - - /* - * Tests for the superentities layer. - */ - SuperentityStorage< TestTriangleMeshConfig, Topologies::Vertex, 1 > vertexEdgeSuperentities; - vertexEdgeSuperentities.setKeysRange( 4 ); - vertexEdgeSuperentities.allocate( 3 ); - - vertexEntities[ 0 ].template bindSuperentitiesStorageNetwork< 1 >( vertexEdgeSuperentities.getValues( 0 ) ); - vertexEntities[ 0 ].template setNumberOfSuperentities< 1 >( 2 ); - vertexEntities[ 0 ].template setSuperentityIndex< 1 >( 0, 2 ); - vertexEntities[ 0 ].template setSuperentityIndex< 1 >( 1, 1 ); - - EXPECT_EQ( vertexEntities[ 0 ].template getSuperentitiesCount< 1 >(), 2 ); - EXPECT_EQ( vertexEntities[ 0 ].template getSuperentityIndex< 1 >( 0 ), 2 ); - EXPECT_EQ( vertexEntities[ 0 ].template getSuperentityIndex< 1 >( 1 ), 1 ); - - vertexEntities[ 1 ].template bindSuperentitiesStorageNetwork< 1 >( vertexEdgeSuperentities.getValues( 1 ) ); - vertexEntities[ 1 ].template setNumberOfSuperentities< 1 >( 3 ); - vertexEntities[ 1 ].template setSuperentityIndex< 1 >( 0, 0 ); - vertexEntities[ 1 ].template setSuperentityIndex< 1 >( 1, 2 ); - vertexEntities[ 1 ].template setSuperentityIndex< 1 >( 2, 4 ); - - EXPECT_EQ( vertexEntities[ 1 ].template getSuperentitiesCount< 1 >(), 3 ); - EXPECT_EQ( vertexEntities[ 1 ].template getSuperentityIndex< 1 >( 0 ), 0 ); - EXPECT_EQ( vertexEntities[ 1 ].template getSuperentityIndex< 1 >( 1 ), 2 ); - EXPECT_EQ( vertexEntities[ 1 ].template getSuperentityIndex< 1 >( 2 ), 4 ); - - - SuperentityStorage< TestTriangleMeshConfig, Topologies::Vertex, 2 > vertexCellSuperentities; - vertexCellSuperentities.setKeysRange( 4 ); - vertexCellSuperentities.allocate( 2 ); - - vertexEntities[ 1 ].template bindSuperentitiesStorageNetwork< 2 >( vertexCellSuperentities.getValues( 1 ) ); - vertexEntities[ 1 ].template setNumberOfSuperentities< 2 >( 2 ); - vertexEntities[ 1 ].template setSuperentityIndex< 2 >( 0, 0 ); - vertexEntities[ 1 ].template setSuperentityIndex< 2 >( 1, 1 ); - - EXPECT_EQ( vertexEntities[ 1 ].template getSuperentitiesCount< 2 >(), 2 ); - EXPECT_EQ( vertexEntities[ 1 ].template getSuperentityIndex< 2 >( 0 ), 0 ); - EXPECT_EQ( vertexEntities[ 1 ].template getSuperentityIndex< 2 >( 1 ), 1 ); - - - SuperentityStorage< TestTriangleMeshConfig, Topologies::Edge, 2 > edgeCellSuperentities; - edgeCellSuperentities.setKeysRange( 5 ); - edgeCellSuperentities.allocate( 2 ); - - edgeEntities[ 0 ].template bindSuperentitiesStorageNetwork< 2 >( edgeCellSuperentities.getValues( 0 ) ); - edgeEntities[ 0 ].template setNumberOfSuperentities< 2 >( 2 ); - edgeEntities[ 0 ].template setSuperentityIndex< 2 >( 0, 0 ); - edgeEntities[ 0 ].template setSuperentityIndex< 2 >( 1, 1 ); - - EXPECT_EQ( edgeEntities[ 0 ].template getSuperentitiesCount< 2 >(), 2 ); - EXPECT_EQ( edgeEntities[ 0 ].template getSuperentityIndex< 2 >( 0 ), 0 ); - EXPECT_EQ( edgeEntities[ 0 ].template getSuperentityIndex< 2 >( 1 ), 1 ); - - - generalTestSuperentities( vertexEntities[ 0 ] ); - generalTestSuperentities( vertexEntities[ 1 ] ); - generalTestSuperentities( edgeEntities[ 0 ] ); - generalTestSubentities( edgeEntities[ 0 ] ); - generalTestSubentities( edgeEntities[ 1 ] ); - generalTestSubentities( edgeEntities[ 2 ] ); - generalTestSubentities( edgeEntities[ 3 ] ); - generalTestSubentities( edgeEntities[ 4 ] ); - generalTestSubentities( triangleEntities[ 0 ] ); - generalTestSubentities( triangleEntities[ 1 ] ); -} - -TEST( MeshEntityTest, OneTriangleComparisonTest ) -{ - using TriangleMeshEntityType = TestMeshEntity< TestTriangleMeshConfig, Topologies::Triangle >; - using EdgeMeshEntityType = TestMeshEntity< TestTriangleMeshConfig, typename TriangleMeshEntityType::SubentityTraits< 1 >::SubentityTopology >; - using VertexMeshEntityType = TestMeshEntity< TestTriangleMeshConfig, typename TriangleMeshEntityType::SubentityTraits< 0 >::SubentityTopology >; - - static_assert( TriangleMeshEntityType::SubentityTraits< 1 >::storageEnabled, "Testing triangle entity does not store edges as required." ); - static_assert( TriangleMeshEntityType::SubentityTraits< 0 >::storageEnabled, "Testing triangle entity does not store vertices as required." ); - static_assert( EdgeMeshEntityType::SubentityTraits< 0 >::storageEnabled, "Testing edge entity does not store vertices as required." ); - static_assert( EdgeMeshEntityType::SuperentityTraits< 2 >::storageEnabled, "Testing edge entity does not store triangles as required." ); - static_assert( VertexMeshEntityType::SuperentityTraits< 2 >::storageEnabled, "Testing vertex entity does not store triangles as required." ); - static_assert( VertexMeshEntityType::SuperentityTraits< 1 >::storageEnabled, "Testing vertex entity does not store edges as required." ); - - using PointType = typename VertexMeshEntityType::PointType; - static_assert( std::is_same< PointType, Containers::StaticVector< 2, RealType > >::value, - "unexpected PointType" ); - - PointType point0( 0.0, 0.0 ), - point1( 1.0, 0.0 ), - point2( 0.0, 1.0 ); - - Containers::StaticArray< 3, VertexMeshEntityType > vertices; - vertices[ 0 ].setPoint( point0 ); - vertices[ 1 ].setPoint( point1 ); - vertices[ 2 ].setPoint( point2 ); - - Containers::StaticArray< 3, EdgeMeshEntityType > edges; - SubentityStorage< TestTriangleMeshConfig, Topologies::Edge, 0 > edgeVertexSubentities; - edgeVertexSubentities.setKeysRange( 3 ); - edgeVertexSubentities.allocate(); - - edges[ 0 ].template bindSubentitiesStorageNetwork< 0 >( edgeVertexSubentities.getValues( 0 ) ); - edges[ 0 ].template setSubentityIndex< 0 >( 0, 1 ); - edges[ 0 ].template setSubentityIndex< 0 >( 1, 2 ); - edges[ 1 ].template bindSubentitiesStorageNetwork< 0 >( edgeVertexSubentities.getValues( 1 ) ); - edges[ 1 ].template setSubentityIndex< 0 >( 0, 2 ); - edges[ 1 ].template setSubentityIndex< 0 >( 1, 0 ); - edges[ 2 ].template bindSubentitiesStorageNetwork< 0 >( edgeVertexSubentities.getValues( 2 ) ); - edges[ 2 ].template setSubentityIndex< 0 >( 0, 0 ); - edges[ 2 ].template setSubentityIndex< 0 >( 1, 1 ); - - TriangleMeshEntityType triangle; - SubentityStorage< TestTriangleMeshConfig, Topologies::Triangle, 0 > triangleVertexSubentities; - SubentityStorage< TestTriangleMeshConfig, Topologies::Triangle, 1 > triangleEdgeSubentities; - triangleVertexSubentities.setKeysRange( 1 ); - triangleEdgeSubentities.setKeysRange( 1 ); - triangleVertexSubentities.allocate(); - triangleEdgeSubentities.allocate(); - - triangle.template bindSubentitiesStorageNetwork< 0 >( triangleVertexSubentities.getValues( 0 ) ); - triangle.template setSubentityIndex< 0 >( 0 , 0 ); - triangle.template setSubentityIndex< 0 >( 1 , 1 ); - triangle.template setSubentityIndex< 0 >( 2 , 2 ); - triangle.template bindSubentitiesStorageNetwork< 1 >( triangleVertexSubentities.getValues( 0 ) ); - triangle.template setSubentityIndex< 1 >( 0 , 0 ); - triangle.template setSubentityIndex< 1 >( 1 , 1 ); - triangle.template setSubentityIndex< 1 >( 2 , 2 ); - - - SuperentityStorage< TestTriangleMeshConfig, Topologies::Vertex, 1 > vertexEdgeSuperentities; - vertexEdgeSuperentities.setKeysRange( 3 ); - vertexEdgeSuperentities.allocate( 2 ); - - vertices[ 0 ].template bindSuperentitiesStorageNetwork< 1 >( vertexEdgeSuperentities.getValues( 0 ) ); - vertices[ 0 ].template setNumberOfSuperentities< 1 >( 2 ); - vertices[ 0 ].template setSuperentityIndex< 1 >( 0, 2 ); - vertices[ 0 ].template setSuperentityIndex< 1 >( 1, 1 ); - - vertices[ 1 ].template bindSuperentitiesStorageNetwork< 1 >( vertexEdgeSuperentities.getValues( 1 ) ); - vertices[ 1 ].template setNumberOfSuperentities< 1 >( 2 ); - vertices[ 1 ].template setSuperentityIndex< 1 >( 0, 0 ); - vertices[ 1 ].template setSuperentityIndex< 1 >( 1, 2 ); - - vertices[ 2 ].template bindSuperentitiesStorageNetwork< 1 >( vertexEdgeSuperentities.getValues( 2 ) ); - vertices[ 2 ].template setNumberOfSuperentities< 1 >( 2 ); - vertices[ 2 ].template setSuperentityIndex< 1 >( 0, 0 ); - vertices[ 2 ].template setSuperentityIndex< 1 >( 1, 1 ); - - - SuperentityStorage< TestTriangleMeshConfig, Topologies::Vertex, 2 > vertexCellSuperentities; - vertexCellSuperentities.setKeysRange( 3 ); - vertexCellSuperentities.allocate( 1 ); - - vertices[ 0 ].template bindSuperentitiesStorageNetwork< 2 >( vertexCellSuperentities.getValues( 0 ) ); - vertices[ 0 ].template setNumberOfSuperentities< 2 >( 1 ); - vertices[ 0 ].template setSuperentityIndex< 2 >( 0, 0 ); - - vertices[ 1 ].template bindSuperentitiesStorageNetwork< 2 >( vertexCellSuperentities.getValues( 1 ) ); - vertices[ 1 ].template setNumberOfSuperentities< 2 >( 1 ); - vertices[ 1 ].template setSuperentityIndex< 2 >( 0, 0 ); - - vertices[ 2 ].template bindSuperentitiesStorageNetwork< 2 >( vertexCellSuperentities.getValues( 2 ) ); - vertices[ 2 ].template setNumberOfSuperentities< 2 >( 1 ); - vertices[ 2 ].template setSuperentityIndex< 2 >( 0, 0 ); - - - SuperentityStorage< TestTriangleMeshConfig, Topologies::Edge, 2 > edgeCellSuperentities; - edgeCellSuperentities.setKeysRange( 3 ); - edgeCellSuperentities.allocate( 1 ); - - edges[ 0 ].template bindSuperentitiesStorageNetwork< 2 >( edgeCellSuperentities.getValues( 0 ) ); - edges[ 0 ].template setNumberOfSuperentities< 2 >( 1 ); - edges[ 0 ].template setSuperentityIndex< 2 >( 0, 0 ); - - edges[ 1 ].template bindSuperentitiesStorageNetwork< 2 >( edgeCellSuperentities.getValues( 1 ) ); - edges[ 1 ].template setNumberOfSuperentities< 2 >( 1 ); - edges[ 1 ].template setSuperentityIndex< 2 >( 0, 0 ); - - edges[ 2 ].template bindSuperentitiesStorageNetwork< 2 >( edgeCellSuperentities.getValues( 2 ) ); - edges[ 2 ].template setNumberOfSuperentities< 2 >( 1 ); - edges[ 2 ].template setSuperentityIndex< 2 >( 0, 0 ); - - - /* - * Tests for MeshEntity::operator== - */ - EXPECT_EQ( vertices[ 0 ], vertices[ 0 ] ); - EXPECT_NE( vertices[ 0 ], vertices[ 1 ] ); - vertices[ 0 ].setPoint( point1 ); - vertices[ 0 ].template setSuperentityIndex< 1 >( 0, 0 ); - vertices[ 0 ].template setSuperentityIndex< 1 >( 1, 2 ); - EXPECT_EQ( vertices[ 0 ], vertices[ 1 ] ); - vertices[ 0 ].template setSuperentityIndex< 2 >( 0, 1 ); - EXPECT_NE( vertices[ 0 ], vertices[ 1 ] ); - vertices[ 1 ].template setSuperentityIndex< 2 >( 0, 1 ); - EXPECT_EQ( vertices[ 0 ], vertices[ 1 ] ); - - EXPECT_EQ( edges[ 0 ], edges[ 0 ] ); - EXPECT_NE( edges[ 0 ], edges[ 1 ] ); - edges[ 0 ].template setSubentityIndex< 0 >( 0, 2 ); - edges[ 0 ].template setSubentityIndex< 0 >( 1, 0 ); - EXPECT_EQ( edges[ 0 ], edges[ 1 ] ); - edges[ 0 ].template setSuperentityIndex< 2 >( 0, 1 ); - EXPECT_NE( edges[ 0 ], edges[ 1 ] ); - edges[ 1 ].template setSuperentityIndex< 2 >( 0, 1 ); - EXPECT_EQ( edges[ 0 ], edges[ 1 ] ); - - - /* - * Tests for copy-assignment - */ - VertexMeshEntityType v1( vertices[ 0 ] ); - EXPECT_EQ( v1, vertices[ 0 ] ); - VertexMeshEntityType v2 = vertices[ 0 ]; - EXPECT_EQ( v2, vertices[ 0 ] ); - - EdgeMeshEntityType e1( edges[ 0 ] ); - EXPECT_EQ( e1, edges[ 0 ] ); - EdgeMeshEntityType e2 = edges[ 0 ]; - EXPECT_EQ( e2, edges[ 0 ] ); - - TriangleMeshEntityType t1( triangle ); - EXPECT_EQ( t1, triangle ); - TriangleMeshEntityType t2 = triangle; - EXPECT_EQ( t2, triangle ); -} - -} // namespace MeshEntityTest - -#endif -- GitLab