diff --git a/.gitignore b/.gitignore index d22aa829ed3edc29a907851f5c4dcc0da2dd9374..6687c528d90b009fd86b21bfce570d9b98f10d54 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,12 @@ # VSCode /.vscode + +# Editors +*.orig +*.bak +*.backup +*.swp + +# GDB .gdb_history diff --git a/Documentation/Doxyfile b/Documentation/Doxyfile index 419bf30559ed76e4bf0a2fee258ed4fccc2bca48..803278a7a4c88b7b58f761314ffecee4fc8c639b 100644 --- a/Documentation/Doxyfile +++ b/Documentation/Doxyfile @@ -826,6 +826,7 @@ FILE_PATTERNS = *.c \ *.cxx \ *.cpp \ *.c++ \ + *.cu \ *.java \ *.ii \ *.ixx \ diff --git a/Documentation/Pages/core-concepts.md b/Documentation/Pages/core-concepts.md index 3770602b8c50635481a8b9afe59d6cfeb1077cb9..de99eff9e60a053daf065c2558024a502b2e6700 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`) diff --git a/Documentation/Tutorials/Arrays/main-page.md.bak b/Documentation/Tutorials/Arrays/main-page.md.bak deleted file mode 100644 index d7edc433d26fc61b56f55d53a51a1c2badf0f3e9..0000000000000000000000000000000000000000 --- 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 - - diff --git a/Documentation/Tutorials/CMakeLists.txt b/Documentation/Tutorials/CMakeLists.txt index 53ce4df627f319ae6015464407bceb4410c5bb36..5511d063369ab83aebcb680e50e09d402673d75b 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 0000000000000000000000000000000000000000..9aa61e2e556ce91bff2f18999d91d428ce97b06a --- /dev/null +++ b/Documentation/Tutorials/Meshes/CMakeLists.txt @@ -0,0 +1,61 @@ +set( CPP_EXAMPLES + ReadMeshExample + MeshConfigurationExample + MeshIterationExample + GameOfLife +) +set( CUDA_EXAMPLES + ParallelIterationCuda + GameOfLifeCuda +) + +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} ) + # 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} ) +endif() + +find_package( ZLIB ) +if( ZLIB_FOUND ) + # 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}) + endforeach() +endif() + +find_package( tinyxml2 QUIET ) +if( tinyxml2_FOUND ) + # 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() +endif() + + +set( DATA_FILES + example-triangles.vtu + grid-100x100.vtu +) + +foreach( file IN ITEMS ${DATA_FILES} ) + configure_file(${file} ${CMAKE_CURRENT_BINARY_DIR}/${file} COPYONLY) +endforeach() diff --git a/Documentation/Tutorials/Meshes/GameOfLife.cpp b/Documentation/Tutorials/Meshes/GameOfLife.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9d3fc251b73e0b0ab0039908dfcb6bb5290ef94e --- /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 0000000000000000000000000000000000000000..be4d635c0f3e320f29e7d9f9f4b6f173c9458e1c --- /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 new file mode 100644 index 0000000000000000000000000000000000000000..174748ad310efdcb55878383a168de6d2d548205 --- /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 = 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; + } + }; +}; + +} // 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 0000000000000000000000000000000000000000..5f27f7dc3998b71f7bf812ac20548de691e7f0c1 --- /dev/null +++ b/Documentation/Tutorials/Meshes/MeshIterationExample.cpp @@ -0,0 +1,83 @@ +#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] +} + +{ + //! [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; +} + +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/ParallelIterationCuda.cu b/Documentation/Tutorials/Meshes/ParallelIterationCuda.cu new file mode 120000 index 0000000000000000000000000000000000000000..a871a672af2a7069e4076316289c858c2ba285f5 --- /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 0000000000000000000000000000000000000000..5aaf6eb9a38ad1b652c163675fcfa634dee22b70 --- /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/ReadMeshExample.cpp b/Documentation/Tutorials/Meshes/ReadMeshExample.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bebf2e030b7426cf9d12b1d60710c1c359347c47 --- /dev/null +++ b/Documentation/Tutorials/Meshes/ReadMeshExample.cpp @@ -0,0 +1,41 @@ +//! [config] +#include + +// Define the tag for the MeshTypeResolver configuration +struct MyConfigTag {}; + +namespace TNL { +namespace Meshes { +namespace BuildConfigTags { + +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 ) +{ + std::cout << "The file '" << inputFileName << "' contains the following mesh: " + << TNL::getType() << std::endl; + return true; +} +//! [task] + +//! [main] +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< MyConfigTag, TNL::Devices::Host >( wrapper, inputFileName, "auto" ); +} +//! [main] diff --git a/Documentation/Tutorials/Meshes/example-triangles.vtu b/Documentation/Tutorials/Meshes/example-triangles.vtu new file mode 100644 index 0000000000000000000000000000000000000000..a66f8b7877fbc5f18d0ff04bc3428d2e26992d03 --- /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/grid-100x100.vtu b/Documentation/Tutorials/Meshes/grid-100x100.vtu new file mode 100644 index 0000000000000000000000000000000000000000..d1414a00bcaf045ba6638d1d162d9876b54861ae --- /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 new file mode 100644 index 0000000000000000000000000000000000000000..21e982668ec7c6d5d5bc37f9b9c7302334167891 --- /dev/null +++ b/Documentation/Tutorials/Meshes/tutorial_Meshes.md @@ -0,0 +1,183 @@ +\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. + +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: + +\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. + +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: + +\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: + +\snippet ReadMeshExample.cpp main + +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 {#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. + +\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: + +\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: + +\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 diff --git a/Documentation/Tutorials/index.md b/Documentation/Tutorials/index.md index 55b92ad81a27a198dcc6cb71d534b84664ed3d78..e35e674c8fb877aa86fbedae4aeae94d1efa974b 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/Benchmarks/HeatEquation/HeatEquationBenchmarkBuildConfigTag.h b/src/Benchmarks/HeatEquation/HeatEquationBenchmarkBuildConfigTag.h index 8bdf9634ebea6a9951b4bcfea2adcdbbc070d2a9..dfecf655a733f6fb99e8366af5577069ea6b52ae 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/CMakeLists.txt b/src/CMakeLists.txt index 4dcf601c0f86c68fac656f75449519195d69b113..1da8d78ecb8a074295572a90e421c3fa4267e0bf 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/Examples/CMakeLists.txt b/src/Examples/CMakeLists.txt index 493f537d11ef6b6c54f42d476aca4d08cedf17cb..65281587449127e9d16787bcda8af234e562bcd5 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 97% rename from src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/MainBuildConfig.h rename to src/Examples/Hamilton-Jacobi/Solvers/hamilton-jacobi/MainBuildConfig.h index a2a1d7372caadb2ec725a1051e3b9975d0fa82df..e37cb80b3fd9809696e4dc89e8cf9a4d52876adc 100644 --- a/src/TNL/Experimental/Hamilton-Jacobi/Solvers/hamilton-jacobi/MainBuildConfig.h +++ b/src/Examples/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/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/Examples/heat-equation/HeatEquationBuildConfigTag.h b/src/Examples/heat-equation/HeatEquationBuildConfigTag.h index 0e0389ca74fa62367ec6d1facd07ac18c528ad9a..1e33ab2e12cd3905fea555384c853ab3585bc403 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 fec6d0fe721550c828cc475fafcd11d4c3496817..d396533a58a7676c49ff4f8b07faffb6bf817782 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 0121ab2c00582a4b8f47a96d81b30d31b35f51bf..ac8035515650dc7d6ae7dac17ae98c71ca286463 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/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/Cuda/LaunchHelpers.h b/src/TNL/Cuda/LaunchHelpers.h index 2b7113f43268864e8470ef3596a68b290c89cbf0..a9e8bc1683111b75312e5e334391a0192be22b2f 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() diff --git a/src/TNL/Experimental/Arithmetics/CMakeLists.txt b/src/TNL/Experimental/Arithmetics/CMakeLists.txt deleted file mode 100644 index 60295b76b53f3cc1411c2e032ee00b869fd65dfb..0000000000000000000000000000000000000000 --- 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 b308a16c8bb02d6afa38f097c48a9242c0512e08..0000000000000000000000000000000000000000 --- 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 fc636e69550a0d6b9565a72b5844d37bf44f116c..0000000000000000000000000000000000000000 --- a/src/TNL/Experimental/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -add_subdirectory( Arithmetics ) -if( ${BUILD_EXAMPLES} ) - add_subdirectory( Hamilton-Jacobi ) -endif() diff --git a/src/TNL/Meshes/DefaultConfig.h b/src/TNL/Meshes/DefaultConfig.h index e4eaba905d4fc9adbbb61223c487eac68be83092..a07ef511b0fac9a0635a22ed81a4fb4be14a58c1 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/Mesh.h b/src/TNL/Meshes/Mesh.h index e432d8ea6638e7f9d9b8603e9899c1d3b647a161..fa5cfeb58e028276c3e730b6684bae5bba50e1d4 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/MeshDetails/ConfigValidator.h b/src/TNL/Meshes/MeshDetails/ConfigValidator.h index fa35ba4d03e76bfa298150b351d0fca415af0701..0680026f02a4864d6e1f6b890122dbf90d8206da 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 10132513a0013dbe8e5ababfcde46621aeb715b3..0000000000000000000000000000000000000000 --- 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 80340c62cc0ff15104843b0ce7f3b1e6df7424e0..0000000000000000000000000000000000000000 --- 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 0ee9f2e3676cccb07419319d249327edcb59df46..498dc27fe532df8c6cfe62b50e5810205c6c25b9 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 79c2cc5b9ab41636df20d9c76b63d0c3ced77a51..f8d5775ca91649370cf7e406dd09134e47ec897f 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 f92729532ca01c960dc20ffb8cd1b18b99b976e4..b6404eb683cc14a992ae69028cbf586dffa5b0fd 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 18a1375474c09e1d087d1f580dbe63fd6e1ec952..0000000000000000000000000000000000000000 --- 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 4e1115dafc93e7b672f3c6541a672442a8964cc7..2bf7f856fc6cc8c7ad0a2963b308f8b4c9fb2421 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 5706719310b3782871c2d2ddac6727ac298d9c85..b5d3df4d54188c28213e67ae5d146335eb35765f 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 ba91bc1703a550c0428712b67ae1d3ca6301b5d9..b077ed04544f2d49b8b042680d698d9b92b64c09 100644 --- a/src/TNL/Meshes/MeshEntity.h +++ b/src/TNL/Meshes/MeshEntity.h @@ -70,41 +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; - template< int Subdimension > - __cuda_callable__ - auto getSubentityOrientation( const LocalIndexType localIndex ) const - -> const typename SubentityTraits< Subdimension >::OrientationArrayType&; - - /**** - * 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; diff --git a/src/TNL/Meshes/MeshEntity.hpp b/src/TNL/Meshes/MeshEntity.hpp index 67ee0837745364484f0af1de7ca61ed5fa5bee11..03f811e4812375a419a6f85832ce5a71b45c873e 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/TNL/Meshes/BuildConfigTags.h b/src/TNL/Meshes/TypeResolver/BuildConfigTags.h similarity index 96% rename from src/TNL/Meshes/BuildConfigTags.h rename to src/TNL/Meshes/TypeResolver/BuildConfigTags.h index bc8d903c06a2cb5c05b7c3bdf4c4c5e7a7684933..b2c138e9ef824ac8b7f7addeb0fd4103298b0b6f 100644 --- a/src/TNL/Meshes/BuildConfigTags.h +++ b/src/TNL/Meshes/TypeResolver/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/GridTypeResolver.h b/src/TNL/Meshes/TypeResolver/GridTypeResolver.h index 3a23a80288a96940b01f0955d27c893d5e0e71ac..8297bb58cda1b57462508dcbe5a35a2a105b125c 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 { @@ -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 817d2b23fc85baccbed3e708b1ca82636f441423..69fe4e03fd4fc8a0874aa59f4d4f165eb1e6d4db 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 a8b1ecb4ac707ab99bb0f8b9a290eb5b2c75c5cd..46248e98cb7347c727b09fca2521bdb017d6272b 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 { @@ -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 2afb9eb7b59c963efcf2262be6ee171df21372d5..803cf84e9e07d3fc2fac796856ec20b5a4a655ce 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 0000000000000000000000000000000000000000..a62424ef3946982fcfd61e65e8badf227e985145 --- /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 52c0b543b641a6a9a433109c7f380063d16ac66e..1cd1ec76e162195b9e340aac12e0ad8022741593 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 69% rename from src/TNL/Meshes/TypeResolver/TypeResolver.h rename to src/TNL/Meshes/TypeResolver/resolveMeshType.h index a4002eaaeb083acd2897a9ce270ea4e47a4fb613..6386a511e1e77511a724e829c6daa3629d8fff52 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. @@ -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 > @@ -92,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 45ba4ca1864ba36d63b8c62132ef3ce38826e150..397e469dde018e705fb430c905fa01a74fb4bcd3 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/BuildConfigTags.h b/src/TNL/Solvers/BuildConfigTags.h index bcd4cdafcacff729b51b827348bcd7703f4bec21..e75dcfddb0b37df8cdfd8e1eba38ae2e49e46cc4 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 e0f1cc174f93b995a37dd1fb66260c1a647b3be5..d4a43df06a2d6e9fb9b685381d421c70419c4ff5 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/TNL/Solvers/ODE/ExplicitSolver.h b/src/TNL/Solvers/ODE/ExplicitSolver.h index 72299eace872e7ed652865950aff5aac469c6549..bade4ded756ae428601bc2fb9e23cb3d810c6d34 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/Solvers/PDE/TimeDependentPDESolver_impl.h b/src/TNL/Solvers/PDE/TimeDependentPDESolver_impl.h index 34f2798f8d6ff70196ed0c7e375eab63f371eb19..63ba71c8f94719cd73293bdab46a311e4cdf962d 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 880d0ab31de1fcf6557ab40a5d2bbb1fbe3f0cd3..b6e6c10c89f6bf2427b15247702e491f63a404dc 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 3d704426dd2715d18355b6a1329c6333efaedf73..debac52af92bfe30793eebcb6fa064198904c534 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 53ca27b332973c966fc07edb383fdfed94d5f95b..ac16df11bf8f5441af6d96c0a2b55d890f3e3e02 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 @@ -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 7003489ab287dd733fa71761c0bc44302eb478d3..b2a1e8f84ba25517eafd0c17ab45a947fd7900ed 100644 --- a/src/Tools/tnl-game-of-life.cpp +++ b/src/Tools/tnl-game-of-life.cpp @@ -11,10 +11,8 @@ #include #include -#include -#include +#include #include -#include #include #include #include @@ -28,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 }; }; @@ -71,39 +60,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; - } - - template< typename EntityTopology > - static constexpr bool subentityOrientationStorage( EntityTopology, int SubentityDimension ) - { - return false; + 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() @@ -131,58 +107,25 @@ 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 ); - // 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) @@ -198,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 @@ -216,36 +160,34 @@ 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; + 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 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; } */ @@ -263,26 +205,26 @@ 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 - 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 ); 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(); + 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 >(); @@ -293,11 +235,11 @@ 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 - Real sum = 0; + // 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 ); sum += f_in_view[ neighbor ]; @@ -325,17 +267,22 @@ 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 ); + // 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 - 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 ); diff --git a/src/Tools/tnl-grid-to-mesh.cpp b/src/Tools/tnl-grid-to-mesh.cpp index 003a59f5ce071e902342d6cbfe2b5e6702b255b9..fc0bb42484a4cedf2dfb4609b4cc82d6141f8732 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 3d82d85525de46c9777110a45c8dadd856432f40..fbcfd926301a5532e48e84c1292f240586590b3a 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 @@ -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-quickstart/build-config-tag.h.in b/src/Tools/tnl-quickstart/build-config-tag.h.in index f507baf061cbd1d76c187cba42d54dcc3c765cdb..febd7482a06aa26b991782ae1794b49937cbcf1c 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 }}; }}; diff --git a/src/Tools/tnl-test-distributed-mesh.h b/src/Tools/tnl-test-distributed-mesh.h index 6b748d99355375e0f0bb4ac20eaf54129a07ad2f..9f2165a986f085dd9bd551cd023e3e6749130991 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 @@ -88,12 +87,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/Tools/tnl-view.cpp b/src/Tools/tnl-view.cpp index 97cfa7a97ec454279ee1ff2b8b279df8d2ea575a..3d9eb4c80b6d1dd29f448ecf01f9992a21f006e4 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 bfda4f960092553dc58ce596d4718d2d9a86ee72..7be2cd4b135e425f243bcbae28408b35c8a8c2a9 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 ... "; diff --git a/src/UnitTests/Algorithms/MultireductionTest.h b/src/UnitTests/Algorithms/MultireductionTest.h index ec674d935d579ed76b6ba4afc30b1343ca017c51..50286ca15d3044c9de0e78e1fc012ed31a07b723 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 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 164da9d21835971edb730393339e84a408565e77..04277185d9d84b2c56c436c9bcbf2198636914f0 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 4f922292d9261ae78a5e5d719249906538ba75d7..1e8c6942f4670ffc338ea968940d6afb16878683 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 2c0ba865069600a497932227759c3de5d9a3e9f6..80d2d61a0b91a7cc47990ed5ccda1b70a381fd82 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 8c0cd7f90bf20821043c46f4c6d7a9c74fbb511c..b32e39788349f0e09de51dd543639989d1e7e0f9 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 c8ec42bea482a1691fca97efecb8342985f8207d..c9863009b372e8c52151b89e42e3edd7d268dd86 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" diff --git a/src/UnitTests/Meshes/CMakeLists.txt b/src/UnitTests/Meshes/CMakeLists.txt index d4b9d19f33507757dc94746d338478e563ac2329..90763f547d953ebded72f9599e7b617b5106da2c 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/EntityTagsTest.h b/src/UnitTests/Meshes/EntityTagsTest.h index d2f3546bb1a7307ea809476835b56b1ee019f217..855e29249d53251545b6190531f785a5c675c855 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/MeshEntityTest.cpp b/src/UnitTests/Meshes/MeshEntityTest.cpp deleted file mode 100644 index 66b6da128f164951b07c6feff55f740dd9c3b988..0000000000000000000000000000000000000000 --- 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 235150d9d26748a7b95af60fb79471ee8e3986da..0000000000000000000000000000000000000000 --- 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 diff --git a/src/UnitTests/Meshes/MeshOrderingTest.h b/src/UnitTests/Meshes/MeshOrderingTest.h index 634f9445411e35cf45debd60f9c23d361c532207..4d5d2e51215fc72c7855f75b549c5efe91d9adc0 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 9d132e2969e6b1b04f0585844009c48a992ce747..e90161b9617ae7b279095dd047fbced833406555 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; } }; @@ -123,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 ) { @@ -164,7 +130,6 @@ void testFinishedMesh( const Mesh& mesh ) compareStringRepresentation( mesh, mesh2 ); testCopyAssignment( mesh ); testMeshOnCuda( mesh ); - testEntities( mesh ); } TEST( MeshTest, TwoTrianglesTest ) diff --git a/src/UnitTests/Meshes/MeshTraverserTest.h b/src/UnitTests/Meshes/MeshTraverserTest.h index 0af13e5dcb4b0b53221ea7b4693788fd1c273ea4..2a0a9ce9a9dfd9d4f422bb3cb7ce555efd3046c4 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; } };